162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2003-2022, Intel Corporation. All rights reserved.
462306a36Sopenharmony_ci * Intel Management Engine Interface (Intel MEI) Linux driver
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/pci.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/kthread.h>
1062306a36Sopenharmony_ci#include <linux/interrupt.h>
1162306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1262306a36Sopenharmony_ci#include <linux/sizes.h>
1362306a36Sopenharmony_ci#include <linux/delay.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "mei_dev.h"
1662306a36Sopenharmony_ci#include "hbm.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "hw-me.h"
1962306a36Sopenharmony_ci#include "hw-me-regs.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include "mei-trace.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/**
2462306a36Sopenharmony_ci * mei_me_reg_read - Reads 32bit data from the mei device
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci * @hw: the me hardware structure
2762306a36Sopenharmony_ci * @offset: offset from which to read the data
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * Return: register value (u32)
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_cistatic inline u32 mei_me_reg_read(const struct mei_me_hw *hw,
3262306a36Sopenharmony_ci			       unsigned long offset)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	return ioread32(hw->mem_addr + offset);
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/**
3962306a36Sopenharmony_ci * mei_me_reg_write - Writes 32bit data to the mei device
4062306a36Sopenharmony_ci *
4162306a36Sopenharmony_ci * @hw: the me hardware structure
4262306a36Sopenharmony_ci * @offset: offset from which to write the data
4362306a36Sopenharmony_ci * @value: register value to write (u32)
4462306a36Sopenharmony_ci */
4562306a36Sopenharmony_cistatic inline void mei_me_reg_write(const struct mei_me_hw *hw,
4662306a36Sopenharmony_ci				 unsigned long offset, u32 value)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	iowrite32(value, hw->mem_addr + offset);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/**
5262306a36Sopenharmony_ci * mei_me_mecbrw_read - Reads 32bit data from ME circular buffer
5362306a36Sopenharmony_ci *  read window register
5462306a36Sopenharmony_ci *
5562306a36Sopenharmony_ci * @dev: the device structure
5662306a36Sopenharmony_ci *
5762306a36Sopenharmony_ci * Return: ME_CB_RW register value (u32)
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_cistatic inline u32 mei_me_mecbrw_read(const struct mei_device *dev)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	return mei_me_reg_read(to_me_hw(dev), ME_CB_RW);
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/**
6562306a36Sopenharmony_ci * mei_me_hcbww_write - write 32bit data to the host circular buffer
6662306a36Sopenharmony_ci *
6762306a36Sopenharmony_ci * @dev: the device structure
6862306a36Sopenharmony_ci * @data: 32bit data to be written to the host circular buffer
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_cistatic inline void mei_me_hcbww_write(struct mei_device *dev, u32 data)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	mei_me_reg_write(to_me_hw(dev), H_CB_WW, data);
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/**
7662306a36Sopenharmony_ci * mei_me_mecsr_read - Reads 32bit data from the ME CSR
7762306a36Sopenharmony_ci *
7862306a36Sopenharmony_ci * @dev: the device structure
7962306a36Sopenharmony_ci *
8062306a36Sopenharmony_ci * Return: ME_CSR_HA register value (u32)
8162306a36Sopenharmony_ci */
8262306a36Sopenharmony_cistatic inline u32 mei_me_mecsr_read(const struct mei_device *dev)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	u32 reg;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	reg = mei_me_reg_read(to_me_hw(dev), ME_CSR_HA);
8762306a36Sopenharmony_ci	trace_mei_reg_read(dev->dev, "ME_CSR_HA", ME_CSR_HA, reg);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	return reg;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/**
9362306a36Sopenharmony_ci * mei_hcsr_read - Reads 32bit data from the host CSR
9462306a36Sopenharmony_ci *
9562306a36Sopenharmony_ci * @dev: the device structure
9662306a36Sopenharmony_ci *
9762306a36Sopenharmony_ci * Return: H_CSR register value (u32)
9862306a36Sopenharmony_ci */
9962306a36Sopenharmony_cistatic inline u32 mei_hcsr_read(const struct mei_device *dev)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	u32 reg;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	reg = mei_me_reg_read(to_me_hw(dev), H_CSR);
10462306a36Sopenharmony_ci	trace_mei_reg_read(dev->dev, "H_CSR", H_CSR, reg);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return reg;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/**
11062306a36Sopenharmony_ci * mei_hcsr_write - writes H_CSR register to the mei device
11162306a36Sopenharmony_ci *
11262306a36Sopenharmony_ci * @dev: the device structure
11362306a36Sopenharmony_ci * @reg: new register value
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_cistatic inline void mei_hcsr_write(struct mei_device *dev, u32 reg)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	trace_mei_reg_write(dev->dev, "H_CSR", H_CSR, reg);
11862306a36Sopenharmony_ci	mei_me_reg_write(to_me_hw(dev), H_CSR, reg);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/**
12262306a36Sopenharmony_ci * mei_hcsr_set - writes H_CSR register to the mei device,
12362306a36Sopenharmony_ci * and ignores the H_IS bit for it is write-one-to-zero.
12462306a36Sopenharmony_ci *
12562306a36Sopenharmony_ci * @dev: the device structure
12662306a36Sopenharmony_ci * @reg: new register value
12762306a36Sopenharmony_ci */
12862306a36Sopenharmony_cistatic inline void mei_hcsr_set(struct mei_device *dev, u32 reg)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	reg &= ~H_CSR_IS_MASK;
13162306a36Sopenharmony_ci	mei_hcsr_write(dev, reg);
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci/**
13562306a36Sopenharmony_ci * mei_hcsr_set_hig - set host interrupt (set H_IG)
13662306a36Sopenharmony_ci *
13762306a36Sopenharmony_ci * @dev: the device structure
13862306a36Sopenharmony_ci */
13962306a36Sopenharmony_cistatic inline void mei_hcsr_set_hig(struct mei_device *dev)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	u32 hcsr;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	hcsr = mei_hcsr_read(dev) | H_IG;
14462306a36Sopenharmony_ci	mei_hcsr_set(dev, hcsr);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/**
14862306a36Sopenharmony_ci * mei_me_d0i3c_read - Reads 32bit data from the D0I3C register
14962306a36Sopenharmony_ci *
15062306a36Sopenharmony_ci * @dev: the device structure
15162306a36Sopenharmony_ci *
15262306a36Sopenharmony_ci * Return: H_D0I3C register value (u32)
15362306a36Sopenharmony_ci */
15462306a36Sopenharmony_cistatic inline u32 mei_me_d0i3c_read(const struct mei_device *dev)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	u32 reg;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	reg = mei_me_reg_read(to_me_hw(dev), H_D0I3C);
15962306a36Sopenharmony_ci	trace_mei_reg_read(dev->dev, "H_D0I3C", H_D0I3C, reg);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return reg;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci/**
16562306a36Sopenharmony_ci * mei_me_d0i3c_write - writes H_D0I3C register to device
16662306a36Sopenharmony_ci *
16762306a36Sopenharmony_ci * @dev: the device structure
16862306a36Sopenharmony_ci * @reg: new register value
16962306a36Sopenharmony_ci */
17062306a36Sopenharmony_cistatic inline void mei_me_d0i3c_write(struct mei_device *dev, u32 reg)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	trace_mei_reg_write(dev->dev, "H_D0I3C", H_D0I3C, reg);
17362306a36Sopenharmony_ci	mei_me_reg_write(to_me_hw(dev), H_D0I3C, reg);
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci/**
17762306a36Sopenharmony_ci * mei_me_trc_status - read trc status register
17862306a36Sopenharmony_ci *
17962306a36Sopenharmony_ci * @dev: mei device
18062306a36Sopenharmony_ci * @trc: trc status register value
18162306a36Sopenharmony_ci *
18262306a36Sopenharmony_ci * Return: 0 on success, error otherwise
18362306a36Sopenharmony_ci */
18462306a36Sopenharmony_cistatic int mei_me_trc_status(struct mei_device *dev, u32 *trc)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct mei_me_hw *hw = to_me_hw(dev);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (!hw->cfg->hw_trc_supported)
18962306a36Sopenharmony_ci		return -EOPNOTSUPP;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	*trc = mei_me_reg_read(hw, ME_TRC);
19262306a36Sopenharmony_ci	trace_mei_reg_read(dev->dev, "ME_TRC", ME_TRC, *trc);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	return 0;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci/**
19862306a36Sopenharmony_ci * mei_me_fw_status - read fw status register from pci config space
19962306a36Sopenharmony_ci *
20062306a36Sopenharmony_ci * @dev: mei device
20162306a36Sopenharmony_ci * @fw_status: fw status register values
20262306a36Sopenharmony_ci *
20362306a36Sopenharmony_ci * Return: 0 on success, error otherwise
20462306a36Sopenharmony_ci */
20562306a36Sopenharmony_cistatic int mei_me_fw_status(struct mei_device *dev,
20662306a36Sopenharmony_ci			    struct mei_fw_status *fw_status)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	struct mei_me_hw *hw = to_me_hw(dev);
20962306a36Sopenharmony_ci	const struct mei_fw_status *fw_src = &hw->cfg->fw_status;
21062306a36Sopenharmony_ci	int ret;
21162306a36Sopenharmony_ci	int i;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if (!fw_status || !hw->read_fws)
21462306a36Sopenharmony_ci		return -EINVAL;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	fw_status->count = fw_src->count;
21762306a36Sopenharmony_ci	for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) {
21862306a36Sopenharmony_ci		ret = hw->read_fws(dev, fw_src->status[i],
21962306a36Sopenharmony_ci				   &fw_status->status[i]);
22062306a36Sopenharmony_ci		trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HFS_X",
22162306a36Sopenharmony_ci				       fw_src->status[i],
22262306a36Sopenharmony_ci				       fw_status->status[i]);
22362306a36Sopenharmony_ci		if (ret)
22462306a36Sopenharmony_ci			return ret;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	return 0;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci/**
23162306a36Sopenharmony_ci * mei_me_hw_config - configure hw dependent settings
23262306a36Sopenharmony_ci *
23362306a36Sopenharmony_ci * @dev: mei device
23462306a36Sopenharmony_ci *
23562306a36Sopenharmony_ci * Return:
23662306a36Sopenharmony_ci *  * -EINVAL when read_fws is not set
23762306a36Sopenharmony_ci *  * 0 on success
23862306a36Sopenharmony_ci *
23962306a36Sopenharmony_ci */
24062306a36Sopenharmony_cistatic int mei_me_hw_config(struct mei_device *dev)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	struct mei_me_hw *hw = to_me_hw(dev);
24362306a36Sopenharmony_ci	u32 hcsr, reg;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	if (WARN_ON(!hw->read_fws))
24662306a36Sopenharmony_ci		return -EINVAL;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	/* Doesn't change in runtime */
24962306a36Sopenharmony_ci	hcsr = mei_hcsr_read(dev);
25062306a36Sopenharmony_ci	hw->hbuf_depth = (hcsr & H_CBD) >> 24;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	reg = 0;
25362306a36Sopenharmony_ci	hw->read_fws(dev, PCI_CFG_HFS_1, &reg);
25462306a36Sopenharmony_ci	trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HFS_1", PCI_CFG_HFS_1, reg);
25562306a36Sopenharmony_ci	hw->d0i3_supported =
25662306a36Sopenharmony_ci		((reg & PCI_CFG_HFS_1_D0I3_MSK) == PCI_CFG_HFS_1_D0I3_MSK);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	hw->pg_state = MEI_PG_OFF;
25962306a36Sopenharmony_ci	if (hw->d0i3_supported) {
26062306a36Sopenharmony_ci		reg = mei_me_d0i3c_read(dev);
26162306a36Sopenharmony_ci		if (reg & H_D0I3C_I3)
26262306a36Sopenharmony_ci			hw->pg_state = MEI_PG_ON;
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	return 0;
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci/**
26962306a36Sopenharmony_ci * mei_me_pg_state  - translate internal pg state
27062306a36Sopenharmony_ci *   to the mei power gating state
27162306a36Sopenharmony_ci *
27262306a36Sopenharmony_ci * @dev:  mei device
27362306a36Sopenharmony_ci *
27462306a36Sopenharmony_ci * Return: MEI_PG_OFF if aliveness is on and MEI_PG_ON otherwise
27562306a36Sopenharmony_ci */
27662306a36Sopenharmony_cistatic inline enum mei_pg_state mei_me_pg_state(struct mei_device *dev)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct mei_me_hw *hw = to_me_hw(dev);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	return hw->pg_state;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic inline u32 me_intr_src(u32 hcsr)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	return hcsr & H_CSR_IS_MASK;
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci/**
28962306a36Sopenharmony_ci * me_intr_disable - disables mei device interrupts
29062306a36Sopenharmony_ci *      using supplied hcsr register value.
29162306a36Sopenharmony_ci *
29262306a36Sopenharmony_ci * @dev: the device structure
29362306a36Sopenharmony_ci * @hcsr: supplied hcsr register value
29462306a36Sopenharmony_ci */
29562306a36Sopenharmony_cistatic inline void me_intr_disable(struct mei_device *dev, u32 hcsr)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	hcsr &= ~H_CSR_IE_MASK;
29862306a36Sopenharmony_ci	mei_hcsr_set(dev, hcsr);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci/**
30262306a36Sopenharmony_ci * me_intr_clear - clear and stop interrupts
30362306a36Sopenharmony_ci *
30462306a36Sopenharmony_ci * @dev: the device structure
30562306a36Sopenharmony_ci * @hcsr: supplied hcsr register value
30662306a36Sopenharmony_ci */
30762306a36Sopenharmony_cistatic inline void me_intr_clear(struct mei_device *dev, u32 hcsr)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	if (me_intr_src(hcsr))
31062306a36Sopenharmony_ci		mei_hcsr_write(dev, hcsr);
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci/**
31462306a36Sopenharmony_ci * mei_me_intr_clear - clear and stop interrupts
31562306a36Sopenharmony_ci *
31662306a36Sopenharmony_ci * @dev: the device structure
31762306a36Sopenharmony_ci */
31862306a36Sopenharmony_cistatic void mei_me_intr_clear(struct mei_device *dev)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	u32 hcsr = mei_hcsr_read(dev);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	me_intr_clear(dev, hcsr);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci/**
32562306a36Sopenharmony_ci * mei_me_intr_enable - enables mei device interrupts
32662306a36Sopenharmony_ci *
32762306a36Sopenharmony_ci * @dev: the device structure
32862306a36Sopenharmony_ci */
32962306a36Sopenharmony_cistatic void mei_me_intr_enable(struct mei_device *dev)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	u32 hcsr;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	if (mei_me_hw_use_polling(to_me_hw(dev)))
33462306a36Sopenharmony_ci		return;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	hcsr = mei_hcsr_read(dev) | H_CSR_IE_MASK;
33762306a36Sopenharmony_ci	mei_hcsr_set(dev, hcsr);
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci/**
34162306a36Sopenharmony_ci * mei_me_intr_disable - disables mei device interrupts
34262306a36Sopenharmony_ci *
34362306a36Sopenharmony_ci * @dev: the device structure
34462306a36Sopenharmony_ci */
34562306a36Sopenharmony_cistatic void mei_me_intr_disable(struct mei_device *dev)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	u32 hcsr = mei_hcsr_read(dev);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	me_intr_disable(dev, hcsr);
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci/**
35362306a36Sopenharmony_ci * mei_me_synchronize_irq - wait for pending IRQ handlers
35462306a36Sopenharmony_ci *
35562306a36Sopenharmony_ci * @dev: the device structure
35662306a36Sopenharmony_ci */
35762306a36Sopenharmony_cistatic void mei_me_synchronize_irq(struct mei_device *dev)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	struct mei_me_hw *hw = to_me_hw(dev);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	if (mei_me_hw_use_polling(hw))
36262306a36Sopenharmony_ci		return;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	synchronize_irq(hw->irq);
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci/**
36862306a36Sopenharmony_ci * mei_me_hw_reset_release - release device from the reset
36962306a36Sopenharmony_ci *
37062306a36Sopenharmony_ci * @dev: the device structure
37162306a36Sopenharmony_ci */
37262306a36Sopenharmony_cistatic void mei_me_hw_reset_release(struct mei_device *dev)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	u32 hcsr = mei_hcsr_read(dev);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	hcsr |= H_IG;
37762306a36Sopenharmony_ci	hcsr &= ~H_RST;
37862306a36Sopenharmony_ci	mei_hcsr_set(dev, hcsr);
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci/**
38262306a36Sopenharmony_ci * mei_me_host_set_ready - enable device
38362306a36Sopenharmony_ci *
38462306a36Sopenharmony_ci * @dev: mei device
38562306a36Sopenharmony_ci */
38662306a36Sopenharmony_cistatic void mei_me_host_set_ready(struct mei_device *dev)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	u32 hcsr = mei_hcsr_read(dev);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	if (!mei_me_hw_use_polling(to_me_hw(dev)))
39162306a36Sopenharmony_ci		hcsr |= H_CSR_IE_MASK;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	hcsr |=  H_IG | H_RDY;
39462306a36Sopenharmony_ci	mei_hcsr_set(dev, hcsr);
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci/**
39862306a36Sopenharmony_ci * mei_me_host_is_ready - check whether the host has turned ready
39962306a36Sopenharmony_ci *
40062306a36Sopenharmony_ci * @dev: mei device
40162306a36Sopenharmony_ci * Return: bool
40262306a36Sopenharmony_ci */
40362306a36Sopenharmony_cistatic bool mei_me_host_is_ready(struct mei_device *dev)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	u32 hcsr = mei_hcsr_read(dev);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	return (hcsr & H_RDY) == H_RDY;
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci/**
41162306a36Sopenharmony_ci * mei_me_hw_is_ready - check whether the me(hw) has turned ready
41262306a36Sopenharmony_ci *
41362306a36Sopenharmony_ci * @dev: mei device
41462306a36Sopenharmony_ci * Return: bool
41562306a36Sopenharmony_ci */
41662306a36Sopenharmony_cistatic bool mei_me_hw_is_ready(struct mei_device *dev)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	u32 mecsr = mei_me_mecsr_read(dev);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	return (mecsr & ME_RDY_HRA) == ME_RDY_HRA;
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci/**
42462306a36Sopenharmony_ci * mei_me_hw_is_resetting - check whether the me(hw) is in reset
42562306a36Sopenharmony_ci *
42662306a36Sopenharmony_ci * @dev: mei device
42762306a36Sopenharmony_ci * Return: bool
42862306a36Sopenharmony_ci */
42962306a36Sopenharmony_cistatic bool mei_me_hw_is_resetting(struct mei_device *dev)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	u32 mecsr = mei_me_mecsr_read(dev);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	return (mecsr & ME_RST_HRA) == ME_RST_HRA;
43462306a36Sopenharmony_ci}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci/**
43762306a36Sopenharmony_ci * mei_gsc_pxp_check - check for gsc firmware entering pxp mode
43862306a36Sopenharmony_ci *
43962306a36Sopenharmony_ci * @dev: the device structure
44062306a36Sopenharmony_ci */
44162306a36Sopenharmony_cistatic void mei_gsc_pxp_check(struct mei_device *dev)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	struct mei_me_hw *hw = to_me_hw(dev);
44462306a36Sopenharmony_ci	u32 fwsts5 = 0;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	if (dev->pxp_mode == MEI_DEV_PXP_DEFAULT)
44762306a36Sopenharmony_ci		return;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	hw->read_fws(dev, PCI_CFG_HFS_5, &fwsts5);
45062306a36Sopenharmony_ci	trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HFS_5", PCI_CFG_HFS_5, fwsts5);
45162306a36Sopenharmony_ci	if ((fwsts5 & GSC_CFG_HFS_5_BOOT_TYPE_MSK) == GSC_CFG_HFS_5_BOOT_TYPE_PXP) {
45262306a36Sopenharmony_ci		dev_dbg(dev->dev, "pxp mode is ready 0x%08x\n", fwsts5);
45362306a36Sopenharmony_ci		dev->pxp_mode = MEI_DEV_PXP_READY;
45462306a36Sopenharmony_ci	} else {
45562306a36Sopenharmony_ci		dev_dbg(dev->dev, "pxp mode is not ready 0x%08x\n", fwsts5);
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci/**
46062306a36Sopenharmony_ci * mei_me_hw_ready_wait - wait until the me(hw) has turned ready
46162306a36Sopenharmony_ci *  or timeout is reached
46262306a36Sopenharmony_ci *
46362306a36Sopenharmony_ci * @dev: mei device
46462306a36Sopenharmony_ci * Return: 0 on success, error otherwise
46562306a36Sopenharmony_ci */
46662306a36Sopenharmony_cistatic int mei_me_hw_ready_wait(struct mei_device *dev)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci	mutex_unlock(&dev->device_lock);
46962306a36Sopenharmony_ci	wait_event_timeout(dev->wait_hw_ready,
47062306a36Sopenharmony_ci			dev->recvd_hw_ready,
47162306a36Sopenharmony_ci			dev->timeouts.hw_ready);
47262306a36Sopenharmony_ci	mutex_lock(&dev->device_lock);
47362306a36Sopenharmony_ci	if (!dev->recvd_hw_ready) {
47462306a36Sopenharmony_ci		dev_err(dev->dev, "wait hw ready failed\n");
47562306a36Sopenharmony_ci		return -ETIME;
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	mei_gsc_pxp_check(dev);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	mei_me_hw_reset_release(dev);
48162306a36Sopenharmony_ci	dev->recvd_hw_ready = false;
48262306a36Sopenharmony_ci	return 0;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci/**
48662306a36Sopenharmony_ci * mei_me_hw_start - hw start routine
48762306a36Sopenharmony_ci *
48862306a36Sopenharmony_ci * @dev: mei device
48962306a36Sopenharmony_ci * Return: 0 on success, error otherwise
49062306a36Sopenharmony_ci */
49162306a36Sopenharmony_cistatic int mei_me_hw_start(struct mei_device *dev)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	int ret = mei_me_hw_ready_wait(dev);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	if (ret)
49662306a36Sopenharmony_ci		return ret;
49762306a36Sopenharmony_ci	dev_dbg(dev->dev, "hw is ready\n");
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	mei_me_host_set_ready(dev);
50062306a36Sopenharmony_ci	return ret;
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci/**
50562306a36Sopenharmony_ci * mei_hbuf_filled_slots - gets number of device filled buffer slots
50662306a36Sopenharmony_ci *
50762306a36Sopenharmony_ci * @dev: the device structure
50862306a36Sopenharmony_ci *
50962306a36Sopenharmony_ci * Return: number of filled slots
51062306a36Sopenharmony_ci */
51162306a36Sopenharmony_cistatic unsigned char mei_hbuf_filled_slots(struct mei_device *dev)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	u32 hcsr;
51462306a36Sopenharmony_ci	char read_ptr, write_ptr;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	hcsr = mei_hcsr_read(dev);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	read_ptr = (char) ((hcsr & H_CBRP) >> 8);
51962306a36Sopenharmony_ci	write_ptr = (char) ((hcsr & H_CBWP) >> 16);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	return (unsigned char) (write_ptr - read_ptr);
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci/**
52562306a36Sopenharmony_ci * mei_me_hbuf_is_empty - checks if host buffer is empty.
52662306a36Sopenharmony_ci *
52762306a36Sopenharmony_ci * @dev: the device structure
52862306a36Sopenharmony_ci *
52962306a36Sopenharmony_ci * Return: true if empty, false - otherwise.
53062306a36Sopenharmony_ci */
53162306a36Sopenharmony_cistatic bool mei_me_hbuf_is_empty(struct mei_device *dev)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	return mei_hbuf_filled_slots(dev) == 0;
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci/**
53762306a36Sopenharmony_ci * mei_me_hbuf_empty_slots - counts write empty slots.
53862306a36Sopenharmony_ci *
53962306a36Sopenharmony_ci * @dev: the device structure
54062306a36Sopenharmony_ci *
54162306a36Sopenharmony_ci * Return: -EOVERFLOW if overflow, otherwise empty slots count
54262306a36Sopenharmony_ci */
54362306a36Sopenharmony_cistatic int mei_me_hbuf_empty_slots(struct mei_device *dev)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci	struct mei_me_hw *hw = to_me_hw(dev);
54662306a36Sopenharmony_ci	unsigned char filled_slots, empty_slots;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	filled_slots = mei_hbuf_filled_slots(dev);
54962306a36Sopenharmony_ci	empty_slots = hw->hbuf_depth - filled_slots;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	/* check for overflow */
55262306a36Sopenharmony_ci	if (filled_slots > hw->hbuf_depth)
55362306a36Sopenharmony_ci		return -EOVERFLOW;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	return empty_slots;
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci/**
55962306a36Sopenharmony_ci * mei_me_hbuf_depth - returns depth of the hw buffer.
56062306a36Sopenharmony_ci *
56162306a36Sopenharmony_ci * @dev: the device structure
56262306a36Sopenharmony_ci *
56362306a36Sopenharmony_ci * Return: size of hw buffer in slots
56462306a36Sopenharmony_ci */
56562306a36Sopenharmony_cistatic u32 mei_me_hbuf_depth(const struct mei_device *dev)
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci	struct mei_me_hw *hw = to_me_hw(dev);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	return hw->hbuf_depth;
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci/**
57362306a36Sopenharmony_ci * mei_me_hbuf_write - writes a message to host hw buffer.
57462306a36Sopenharmony_ci *
57562306a36Sopenharmony_ci * @dev: the device structure
57662306a36Sopenharmony_ci * @hdr: header of message
57762306a36Sopenharmony_ci * @hdr_len: header length in bytes: must be multiplication of a slot (4bytes)
57862306a36Sopenharmony_ci * @data: payload
57962306a36Sopenharmony_ci * @data_len: payload length in bytes
58062306a36Sopenharmony_ci *
58162306a36Sopenharmony_ci * Return: 0 if success, < 0 - otherwise.
58262306a36Sopenharmony_ci */
58362306a36Sopenharmony_cistatic int mei_me_hbuf_write(struct mei_device *dev,
58462306a36Sopenharmony_ci			     const void *hdr, size_t hdr_len,
58562306a36Sopenharmony_ci			     const void *data, size_t data_len)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	unsigned long rem;
58862306a36Sopenharmony_ci	unsigned long i;
58962306a36Sopenharmony_ci	const u32 *reg_buf;
59062306a36Sopenharmony_ci	u32 dw_cnt;
59162306a36Sopenharmony_ci	int empty_slots;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	if (WARN_ON(!hdr || hdr_len & 0x3))
59462306a36Sopenharmony_ci		return -EINVAL;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	if (!data && data_len) {
59762306a36Sopenharmony_ci		dev_err(dev->dev, "wrong parameters null data with data_len = %zu\n", data_len);
59862306a36Sopenharmony_ci		return -EINVAL;
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM((struct mei_msg_hdr *)hdr));
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	empty_slots = mei_hbuf_empty_slots(dev);
60462306a36Sopenharmony_ci	dev_dbg(dev->dev, "empty slots = %d.\n", empty_slots);
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	if (empty_slots < 0)
60762306a36Sopenharmony_ci		return -EOVERFLOW;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	dw_cnt = mei_data2slots(hdr_len + data_len);
61062306a36Sopenharmony_ci	if (dw_cnt > (u32)empty_slots)
61162306a36Sopenharmony_ci		return -EMSGSIZE;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	reg_buf = hdr;
61462306a36Sopenharmony_ci	for (i = 0; i < hdr_len / MEI_SLOT_SIZE; i++)
61562306a36Sopenharmony_ci		mei_me_hcbww_write(dev, reg_buf[i]);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	reg_buf = data;
61862306a36Sopenharmony_ci	for (i = 0; i < data_len / MEI_SLOT_SIZE; i++)
61962306a36Sopenharmony_ci		mei_me_hcbww_write(dev, reg_buf[i]);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	rem = data_len & 0x3;
62262306a36Sopenharmony_ci	if (rem > 0) {
62362306a36Sopenharmony_ci		u32 reg = 0;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci		memcpy(&reg, (const u8 *)data + data_len - rem, rem);
62662306a36Sopenharmony_ci		mei_me_hcbww_write(dev, reg);
62762306a36Sopenharmony_ci	}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	mei_hcsr_set_hig(dev);
63062306a36Sopenharmony_ci	if (!mei_me_hw_is_ready(dev))
63162306a36Sopenharmony_ci		return -EIO;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	return 0;
63462306a36Sopenharmony_ci}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci/**
63762306a36Sopenharmony_ci * mei_me_count_full_read_slots - counts read full slots.
63862306a36Sopenharmony_ci *
63962306a36Sopenharmony_ci * @dev: the device structure
64062306a36Sopenharmony_ci *
64162306a36Sopenharmony_ci * Return: -EOVERFLOW if overflow, otherwise filled slots count
64262306a36Sopenharmony_ci */
64362306a36Sopenharmony_cistatic int mei_me_count_full_read_slots(struct mei_device *dev)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	u32 me_csr;
64662306a36Sopenharmony_ci	char read_ptr, write_ptr;
64762306a36Sopenharmony_ci	unsigned char buffer_depth, filled_slots;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	me_csr = mei_me_mecsr_read(dev);
65062306a36Sopenharmony_ci	buffer_depth = (unsigned char)((me_csr & ME_CBD_HRA) >> 24);
65162306a36Sopenharmony_ci	read_ptr = (char) ((me_csr & ME_CBRP_HRA) >> 8);
65262306a36Sopenharmony_ci	write_ptr = (char) ((me_csr & ME_CBWP_HRA) >> 16);
65362306a36Sopenharmony_ci	filled_slots = (unsigned char) (write_ptr - read_ptr);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	/* check for overflow */
65662306a36Sopenharmony_ci	if (filled_slots > buffer_depth)
65762306a36Sopenharmony_ci		return -EOVERFLOW;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	dev_dbg(dev->dev, "filled_slots =%08x\n", filled_slots);
66062306a36Sopenharmony_ci	return (int)filled_slots;
66162306a36Sopenharmony_ci}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci/**
66462306a36Sopenharmony_ci * mei_me_read_slots - reads a message from mei device.
66562306a36Sopenharmony_ci *
66662306a36Sopenharmony_ci * @dev: the device structure
66762306a36Sopenharmony_ci * @buffer: message buffer will be written
66862306a36Sopenharmony_ci * @buffer_length: message size will be read
66962306a36Sopenharmony_ci *
67062306a36Sopenharmony_ci * Return: always 0
67162306a36Sopenharmony_ci */
67262306a36Sopenharmony_cistatic int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer,
67362306a36Sopenharmony_ci			     unsigned long buffer_length)
67462306a36Sopenharmony_ci{
67562306a36Sopenharmony_ci	u32 *reg_buf = (u32 *)buffer;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	for (; buffer_length >= MEI_SLOT_SIZE; buffer_length -= MEI_SLOT_SIZE)
67862306a36Sopenharmony_ci		*reg_buf++ = mei_me_mecbrw_read(dev);
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	if (buffer_length > 0) {
68162306a36Sopenharmony_ci		u32 reg = mei_me_mecbrw_read(dev);
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci		memcpy(reg_buf, &reg, buffer_length);
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	mei_hcsr_set_hig(dev);
68762306a36Sopenharmony_ci	return 0;
68862306a36Sopenharmony_ci}
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci/**
69162306a36Sopenharmony_ci * mei_me_pg_set - write pg enter register
69262306a36Sopenharmony_ci *
69362306a36Sopenharmony_ci * @dev: the device structure
69462306a36Sopenharmony_ci */
69562306a36Sopenharmony_cistatic void mei_me_pg_set(struct mei_device *dev)
69662306a36Sopenharmony_ci{
69762306a36Sopenharmony_ci	struct mei_me_hw *hw = to_me_hw(dev);
69862306a36Sopenharmony_ci	u32 reg;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	reg = mei_me_reg_read(hw, H_HPG_CSR);
70162306a36Sopenharmony_ci	trace_mei_reg_read(dev->dev, "H_HPG_CSR", H_HPG_CSR, reg);
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	reg |= H_HPG_CSR_PGI;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	trace_mei_reg_write(dev->dev, "H_HPG_CSR", H_HPG_CSR, reg);
70662306a36Sopenharmony_ci	mei_me_reg_write(hw, H_HPG_CSR, reg);
70762306a36Sopenharmony_ci}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci/**
71062306a36Sopenharmony_ci * mei_me_pg_unset - write pg exit register
71162306a36Sopenharmony_ci *
71262306a36Sopenharmony_ci * @dev: the device structure
71362306a36Sopenharmony_ci */
71462306a36Sopenharmony_cistatic void mei_me_pg_unset(struct mei_device *dev)
71562306a36Sopenharmony_ci{
71662306a36Sopenharmony_ci	struct mei_me_hw *hw = to_me_hw(dev);
71762306a36Sopenharmony_ci	u32 reg;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	reg = mei_me_reg_read(hw, H_HPG_CSR);
72062306a36Sopenharmony_ci	trace_mei_reg_read(dev->dev, "H_HPG_CSR", H_HPG_CSR, reg);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	WARN(!(reg & H_HPG_CSR_PGI), "PGI is not set\n");
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	reg |= H_HPG_CSR_PGIHEXR;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	trace_mei_reg_write(dev->dev, "H_HPG_CSR", H_HPG_CSR, reg);
72762306a36Sopenharmony_ci	mei_me_reg_write(hw, H_HPG_CSR, reg);
72862306a36Sopenharmony_ci}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci/**
73162306a36Sopenharmony_ci * mei_me_pg_legacy_enter_sync - perform legacy pg entry procedure
73262306a36Sopenharmony_ci *
73362306a36Sopenharmony_ci * @dev: the device structure
73462306a36Sopenharmony_ci *
73562306a36Sopenharmony_ci * Return: 0 on success an error code otherwise
73662306a36Sopenharmony_ci */
73762306a36Sopenharmony_cistatic int mei_me_pg_legacy_enter_sync(struct mei_device *dev)
73862306a36Sopenharmony_ci{
73962306a36Sopenharmony_ci	struct mei_me_hw *hw = to_me_hw(dev);
74062306a36Sopenharmony_ci	int ret;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	dev->pg_event = MEI_PG_EVENT_WAIT;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_ENTRY_REQ_CMD);
74562306a36Sopenharmony_ci	if (ret)
74662306a36Sopenharmony_ci		return ret;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	mutex_unlock(&dev->device_lock);
74962306a36Sopenharmony_ci	wait_event_timeout(dev->wait_pg,
75062306a36Sopenharmony_ci		dev->pg_event == MEI_PG_EVENT_RECEIVED,
75162306a36Sopenharmony_ci		dev->timeouts.pgi);
75262306a36Sopenharmony_ci	mutex_lock(&dev->device_lock);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	if (dev->pg_event == MEI_PG_EVENT_RECEIVED) {
75562306a36Sopenharmony_ci		mei_me_pg_set(dev);
75662306a36Sopenharmony_ci		ret = 0;
75762306a36Sopenharmony_ci	} else {
75862306a36Sopenharmony_ci		ret = -ETIME;
75962306a36Sopenharmony_ci	}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	dev->pg_event = MEI_PG_EVENT_IDLE;
76262306a36Sopenharmony_ci	hw->pg_state = MEI_PG_ON;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	return ret;
76562306a36Sopenharmony_ci}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci/**
76862306a36Sopenharmony_ci * mei_me_pg_legacy_exit_sync - perform legacy pg exit procedure
76962306a36Sopenharmony_ci *
77062306a36Sopenharmony_ci * @dev: the device structure
77162306a36Sopenharmony_ci *
77262306a36Sopenharmony_ci * Return: 0 on success an error code otherwise
77362306a36Sopenharmony_ci */
77462306a36Sopenharmony_cistatic int mei_me_pg_legacy_exit_sync(struct mei_device *dev)
77562306a36Sopenharmony_ci{
77662306a36Sopenharmony_ci	struct mei_me_hw *hw = to_me_hw(dev);
77762306a36Sopenharmony_ci	int ret;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	if (dev->pg_event == MEI_PG_EVENT_RECEIVED)
78062306a36Sopenharmony_ci		goto reply;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	dev->pg_event = MEI_PG_EVENT_WAIT;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	mei_me_pg_unset(dev);
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	mutex_unlock(&dev->device_lock);
78762306a36Sopenharmony_ci	wait_event_timeout(dev->wait_pg,
78862306a36Sopenharmony_ci		dev->pg_event == MEI_PG_EVENT_RECEIVED,
78962306a36Sopenharmony_ci		dev->timeouts.pgi);
79062306a36Sopenharmony_ci	mutex_lock(&dev->device_lock);
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_cireply:
79362306a36Sopenharmony_ci	if (dev->pg_event != MEI_PG_EVENT_RECEIVED) {
79462306a36Sopenharmony_ci		ret = -ETIME;
79562306a36Sopenharmony_ci		goto out;
79662306a36Sopenharmony_ci	}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	dev->pg_event = MEI_PG_EVENT_INTR_WAIT;
79962306a36Sopenharmony_ci	ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_EXIT_RES_CMD);
80062306a36Sopenharmony_ci	if (ret)
80162306a36Sopenharmony_ci		return ret;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	mutex_unlock(&dev->device_lock);
80462306a36Sopenharmony_ci	wait_event_timeout(dev->wait_pg,
80562306a36Sopenharmony_ci		dev->pg_event == MEI_PG_EVENT_INTR_RECEIVED,
80662306a36Sopenharmony_ci		dev->timeouts.pgi);
80762306a36Sopenharmony_ci	mutex_lock(&dev->device_lock);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	if (dev->pg_event == MEI_PG_EVENT_INTR_RECEIVED)
81062306a36Sopenharmony_ci		ret = 0;
81162306a36Sopenharmony_ci	else
81262306a36Sopenharmony_ci		ret = -ETIME;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ciout:
81562306a36Sopenharmony_ci	dev->pg_event = MEI_PG_EVENT_IDLE;
81662306a36Sopenharmony_ci	hw->pg_state = MEI_PG_OFF;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	return ret;
81962306a36Sopenharmony_ci}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci/**
82262306a36Sopenharmony_ci * mei_me_pg_in_transition - is device now in pg transition
82362306a36Sopenharmony_ci *
82462306a36Sopenharmony_ci * @dev: the device structure
82562306a36Sopenharmony_ci *
82662306a36Sopenharmony_ci * Return: true if in pg transition, false otherwise
82762306a36Sopenharmony_ci */
82862306a36Sopenharmony_cistatic bool mei_me_pg_in_transition(struct mei_device *dev)
82962306a36Sopenharmony_ci{
83062306a36Sopenharmony_ci	return dev->pg_event >= MEI_PG_EVENT_WAIT &&
83162306a36Sopenharmony_ci	       dev->pg_event <= MEI_PG_EVENT_INTR_WAIT;
83262306a36Sopenharmony_ci}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci/**
83562306a36Sopenharmony_ci * mei_me_pg_is_enabled - detect if PG is supported by HW
83662306a36Sopenharmony_ci *
83762306a36Sopenharmony_ci * @dev: the device structure
83862306a36Sopenharmony_ci *
83962306a36Sopenharmony_ci * Return: true is pg supported, false otherwise
84062306a36Sopenharmony_ci */
84162306a36Sopenharmony_cistatic bool mei_me_pg_is_enabled(struct mei_device *dev)
84262306a36Sopenharmony_ci{
84362306a36Sopenharmony_ci	struct mei_me_hw *hw = to_me_hw(dev);
84462306a36Sopenharmony_ci	u32 reg = mei_me_mecsr_read(dev);
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	if (hw->d0i3_supported)
84762306a36Sopenharmony_ci		return true;
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	if ((reg & ME_PGIC_HRA) == 0)
85062306a36Sopenharmony_ci		goto notsupported;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	if (!dev->hbm_f_pg_supported)
85362306a36Sopenharmony_ci		goto notsupported;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	return true;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_cinotsupported:
85862306a36Sopenharmony_ci	dev_dbg(dev->dev, "pg: not supported: d0i3 = %d HGP = %d hbm version %d.%d ?= %d.%d\n",
85962306a36Sopenharmony_ci		hw->d0i3_supported,
86062306a36Sopenharmony_ci		!!(reg & ME_PGIC_HRA),
86162306a36Sopenharmony_ci		dev->version.major_version,
86262306a36Sopenharmony_ci		dev->version.minor_version,
86362306a36Sopenharmony_ci		HBM_MAJOR_VERSION_PGI,
86462306a36Sopenharmony_ci		HBM_MINOR_VERSION_PGI);
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	return false;
86762306a36Sopenharmony_ci}
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci/**
87062306a36Sopenharmony_ci * mei_me_d0i3_set - write d0i3 register bit on mei device.
87162306a36Sopenharmony_ci *
87262306a36Sopenharmony_ci * @dev: the device structure
87362306a36Sopenharmony_ci * @intr: ask for interrupt
87462306a36Sopenharmony_ci *
87562306a36Sopenharmony_ci * Return: D0I3C register value
87662306a36Sopenharmony_ci */
87762306a36Sopenharmony_cistatic u32 mei_me_d0i3_set(struct mei_device *dev, bool intr)
87862306a36Sopenharmony_ci{
87962306a36Sopenharmony_ci	u32 reg = mei_me_d0i3c_read(dev);
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	reg |= H_D0I3C_I3;
88262306a36Sopenharmony_ci	if (intr)
88362306a36Sopenharmony_ci		reg |= H_D0I3C_IR;
88462306a36Sopenharmony_ci	else
88562306a36Sopenharmony_ci		reg &= ~H_D0I3C_IR;
88662306a36Sopenharmony_ci	mei_me_d0i3c_write(dev, reg);
88762306a36Sopenharmony_ci	/* read it to ensure HW consistency */
88862306a36Sopenharmony_ci	reg = mei_me_d0i3c_read(dev);
88962306a36Sopenharmony_ci	return reg;
89062306a36Sopenharmony_ci}
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci/**
89362306a36Sopenharmony_ci * mei_me_d0i3_unset - clean d0i3 register bit on mei device.
89462306a36Sopenharmony_ci *
89562306a36Sopenharmony_ci * @dev: the device structure
89662306a36Sopenharmony_ci *
89762306a36Sopenharmony_ci * Return: D0I3C register value
89862306a36Sopenharmony_ci */
89962306a36Sopenharmony_cistatic u32 mei_me_d0i3_unset(struct mei_device *dev)
90062306a36Sopenharmony_ci{
90162306a36Sopenharmony_ci	u32 reg = mei_me_d0i3c_read(dev);
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	reg &= ~H_D0I3C_I3;
90462306a36Sopenharmony_ci	reg |= H_D0I3C_IR;
90562306a36Sopenharmony_ci	mei_me_d0i3c_write(dev, reg);
90662306a36Sopenharmony_ci	/* read it to ensure HW consistency */
90762306a36Sopenharmony_ci	reg = mei_me_d0i3c_read(dev);
90862306a36Sopenharmony_ci	return reg;
90962306a36Sopenharmony_ci}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci/**
91262306a36Sopenharmony_ci * mei_me_d0i3_enter_sync - perform d0i3 entry procedure
91362306a36Sopenharmony_ci *
91462306a36Sopenharmony_ci * @dev: the device structure
91562306a36Sopenharmony_ci *
91662306a36Sopenharmony_ci * Return: 0 on success an error code otherwise
91762306a36Sopenharmony_ci */
91862306a36Sopenharmony_cistatic int mei_me_d0i3_enter_sync(struct mei_device *dev)
91962306a36Sopenharmony_ci{
92062306a36Sopenharmony_ci	struct mei_me_hw *hw = to_me_hw(dev);
92162306a36Sopenharmony_ci	int ret;
92262306a36Sopenharmony_ci	u32 reg;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	reg = mei_me_d0i3c_read(dev);
92562306a36Sopenharmony_ci	if (reg & H_D0I3C_I3) {
92662306a36Sopenharmony_ci		/* we are in d0i3, nothing to do */
92762306a36Sopenharmony_ci		dev_dbg(dev->dev, "d0i3 set not needed\n");
92862306a36Sopenharmony_ci		ret = 0;
92962306a36Sopenharmony_ci		goto on;
93062306a36Sopenharmony_ci	}
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	/* PGI entry procedure */
93362306a36Sopenharmony_ci	dev->pg_event = MEI_PG_EVENT_WAIT;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_ENTRY_REQ_CMD);
93662306a36Sopenharmony_ci	if (ret)
93762306a36Sopenharmony_ci		/* FIXME: should we reset here? */
93862306a36Sopenharmony_ci		goto out;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	mutex_unlock(&dev->device_lock);
94162306a36Sopenharmony_ci	wait_event_timeout(dev->wait_pg,
94262306a36Sopenharmony_ci		dev->pg_event == MEI_PG_EVENT_RECEIVED,
94362306a36Sopenharmony_ci		dev->timeouts.pgi);
94462306a36Sopenharmony_ci	mutex_lock(&dev->device_lock);
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	if (dev->pg_event != MEI_PG_EVENT_RECEIVED) {
94762306a36Sopenharmony_ci		ret = -ETIME;
94862306a36Sopenharmony_ci		goto out;
94962306a36Sopenharmony_ci	}
95062306a36Sopenharmony_ci	/* end PGI entry procedure */
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	dev->pg_event = MEI_PG_EVENT_INTR_WAIT;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	reg = mei_me_d0i3_set(dev, true);
95562306a36Sopenharmony_ci	if (!(reg & H_D0I3C_CIP)) {
95662306a36Sopenharmony_ci		dev_dbg(dev->dev, "d0i3 enter wait not needed\n");
95762306a36Sopenharmony_ci		ret = 0;
95862306a36Sopenharmony_ci		goto on;
95962306a36Sopenharmony_ci	}
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	mutex_unlock(&dev->device_lock);
96262306a36Sopenharmony_ci	wait_event_timeout(dev->wait_pg,
96362306a36Sopenharmony_ci		dev->pg_event == MEI_PG_EVENT_INTR_RECEIVED,
96462306a36Sopenharmony_ci		dev->timeouts.d0i3);
96562306a36Sopenharmony_ci	mutex_lock(&dev->device_lock);
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	if (dev->pg_event != MEI_PG_EVENT_INTR_RECEIVED) {
96862306a36Sopenharmony_ci		reg = mei_me_d0i3c_read(dev);
96962306a36Sopenharmony_ci		if (!(reg & H_D0I3C_I3)) {
97062306a36Sopenharmony_ci			ret = -ETIME;
97162306a36Sopenharmony_ci			goto out;
97262306a36Sopenharmony_ci		}
97362306a36Sopenharmony_ci	}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	ret = 0;
97662306a36Sopenharmony_cion:
97762306a36Sopenharmony_ci	hw->pg_state = MEI_PG_ON;
97862306a36Sopenharmony_ciout:
97962306a36Sopenharmony_ci	dev->pg_event = MEI_PG_EVENT_IDLE;
98062306a36Sopenharmony_ci	dev_dbg(dev->dev, "d0i3 enter ret = %d\n", ret);
98162306a36Sopenharmony_ci	return ret;
98262306a36Sopenharmony_ci}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci/**
98562306a36Sopenharmony_ci * mei_me_d0i3_enter - perform d0i3 entry procedure
98662306a36Sopenharmony_ci *   no hbm PG handshake
98762306a36Sopenharmony_ci *   no waiting for confirmation; runs with interrupts
98862306a36Sopenharmony_ci *   disabled
98962306a36Sopenharmony_ci *
99062306a36Sopenharmony_ci * @dev: the device structure
99162306a36Sopenharmony_ci *
99262306a36Sopenharmony_ci * Return: 0 on success an error code otherwise
99362306a36Sopenharmony_ci */
99462306a36Sopenharmony_cistatic int mei_me_d0i3_enter(struct mei_device *dev)
99562306a36Sopenharmony_ci{
99662306a36Sopenharmony_ci	struct mei_me_hw *hw = to_me_hw(dev);
99762306a36Sopenharmony_ci	u32 reg;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	reg = mei_me_d0i3c_read(dev);
100062306a36Sopenharmony_ci	if (reg & H_D0I3C_I3) {
100162306a36Sopenharmony_ci		/* we are in d0i3, nothing to do */
100262306a36Sopenharmony_ci		dev_dbg(dev->dev, "already d0i3 : set not needed\n");
100362306a36Sopenharmony_ci		goto on;
100462306a36Sopenharmony_ci	}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	mei_me_d0i3_set(dev, false);
100762306a36Sopenharmony_cion:
100862306a36Sopenharmony_ci	hw->pg_state = MEI_PG_ON;
100962306a36Sopenharmony_ci	dev->pg_event = MEI_PG_EVENT_IDLE;
101062306a36Sopenharmony_ci	dev_dbg(dev->dev, "d0i3 enter\n");
101162306a36Sopenharmony_ci	return 0;
101262306a36Sopenharmony_ci}
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci/**
101562306a36Sopenharmony_ci * mei_me_d0i3_exit_sync - perform d0i3 exit procedure
101662306a36Sopenharmony_ci *
101762306a36Sopenharmony_ci * @dev: the device structure
101862306a36Sopenharmony_ci *
101962306a36Sopenharmony_ci * Return: 0 on success an error code otherwise
102062306a36Sopenharmony_ci */
102162306a36Sopenharmony_cistatic int mei_me_d0i3_exit_sync(struct mei_device *dev)
102262306a36Sopenharmony_ci{
102362306a36Sopenharmony_ci	struct mei_me_hw *hw = to_me_hw(dev);
102462306a36Sopenharmony_ci	int ret;
102562306a36Sopenharmony_ci	u32 reg;
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	dev->pg_event = MEI_PG_EVENT_INTR_WAIT;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	reg = mei_me_d0i3c_read(dev);
103062306a36Sopenharmony_ci	if (!(reg & H_D0I3C_I3)) {
103162306a36Sopenharmony_ci		/* we are not in d0i3, nothing to do */
103262306a36Sopenharmony_ci		dev_dbg(dev->dev, "d0i3 exit not needed\n");
103362306a36Sopenharmony_ci		ret = 0;
103462306a36Sopenharmony_ci		goto off;
103562306a36Sopenharmony_ci	}
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	reg = mei_me_d0i3_unset(dev);
103862306a36Sopenharmony_ci	if (!(reg & H_D0I3C_CIP)) {
103962306a36Sopenharmony_ci		dev_dbg(dev->dev, "d0i3 exit wait not needed\n");
104062306a36Sopenharmony_ci		ret = 0;
104162306a36Sopenharmony_ci		goto off;
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	mutex_unlock(&dev->device_lock);
104562306a36Sopenharmony_ci	wait_event_timeout(dev->wait_pg,
104662306a36Sopenharmony_ci		dev->pg_event == MEI_PG_EVENT_INTR_RECEIVED,
104762306a36Sopenharmony_ci		dev->timeouts.d0i3);
104862306a36Sopenharmony_ci	mutex_lock(&dev->device_lock);
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	if (dev->pg_event != MEI_PG_EVENT_INTR_RECEIVED) {
105162306a36Sopenharmony_ci		reg = mei_me_d0i3c_read(dev);
105262306a36Sopenharmony_ci		if (reg & H_D0I3C_I3) {
105362306a36Sopenharmony_ci			ret = -ETIME;
105462306a36Sopenharmony_ci			goto out;
105562306a36Sopenharmony_ci		}
105662306a36Sopenharmony_ci	}
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	ret = 0;
105962306a36Sopenharmony_cioff:
106062306a36Sopenharmony_ci	hw->pg_state = MEI_PG_OFF;
106162306a36Sopenharmony_ciout:
106262306a36Sopenharmony_ci	dev->pg_event = MEI_PG_EVENT_IDLE;
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	dev_dbg(dev->dev, "d0i3 exit ret = %d\n", ret);
106562306a36Sopenharmony_ci	return ret;
106662306a36Sopenharmony_ci}
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci/**
106962306a36Sopenharmony_ci * mei_me_pg_legacy_intr - perform legacy pg processing
107062306a36Sopenharmony_ci *			   in interrupt thread handler
107162306a36Sopenharmony_ci *
107262306a36Sopenharmony_ci * @dev: the device structure
107362306a36Sopenharmony_ci */
107462306a36Sopenharmony_cistatic void mei_me_pg_legacy_intr(struct mei_device *dev)
107562306a36Sopenharmony_ci{
107662306a36Sopenharmony_ci	struct mei_me_hw *hw = to_me_hw(dev);
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	if (dev->pg_event != MEI_PG_EVENT_INTR_WAIT)
107962306a36Sopenharmony_ci		return;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	dev->pg_event = MEI_PG_EVENT_INTR_RECEIVED;
108262306a36Sopenharmony_ci	hw->pg_state = MEI_PG_OFF;
108362306a36Sopenharmony_ci	if (waitqueue_active(&dev->wait_pg))
108462306a36Sopenharmony_ci		wake_up(&dev->wait_pg);
108562306a36Sopenharmony_ci}
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci/**
108862306a36Sopenharmony_ci * mei_me_d0i3_intr - perform d0i3 processing in interrupt thread handler
108962306a36Sopenharmony_ci *
109062306a36Sopenharmony_ci * @dev: the device structure
109162306a36Sopenharmony_ci * @intr_source: interrupt source
109262306a36Sopenharmony_ci */
109362306a36Sopenharmony_cistatic void mei_me_d0i3_intr(struct mei_device *dev, u32 intr_source)
109462306a36Sopenharmony_ci{
109562306a36Sopenharmony_ci	struct mei_me_hw *hw = to_me_hw(dev);
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	if (dev->pg_event == MEI_PG_EVENT_INTR_WAIT &&
109862306a36Sopenharmony_ci	    (intr_source & H_D0I3C_IS)) {
109962306a36Sopenharmony_ci		dev->pg_event = MEI_PG_EVENT_INTR_RECEIVED;
110062306a36Sopenharmony_ci		if (hw->pg_state == MEI_PG_ON) {
110162306a36Sopenharmony_ci			hw->pg_state = MEI_PG_OFF;
110262306a36Sopenharmony_ci			if (dev->hbm_state != MEI_HBM_IDLE) {
110362306a36Sopenharmony_ci				/*
110462306a36Sopenharmony_ci				 * force H_RDY because it could be
110562306a36Sopenharmony_ci				 * wiped off during PG
110662306a36Sopenharmony_ci				 */
110762306a36Sopenharmony_ci				dev_dbg(dev->dev, "d0i3 set host ready\n");
110862306a36Sopenharmony_ci				mei_me_host_set_ready(dev);
110962306a36Sopenharmony_ci			}
111062306a36Sopenharmony_ci		} else {
111162306a36Sopenharmony_ci			hw->pg_state = MEI_PG_ON;
111262306a36Sopenharmony_ci		}
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci		wake_up(&dev->wait_pg);
111562306a36Sopenharmony_ci	}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	if (hw->pg_state == MEI_PG_ON && (intr_source & H_IS)) {
111862306a36Sopenharmony_ci		/*
111962306a36Sopenharmony_ci		 * HW sent some data and we are in D0i3, so
112062306a36Sopenharmony_ci		 * we got here because of HW initiated exit from D0i3.
112162306a36Sopenharmony_ci		 * Start runtime pm resume sequence to exit low power state.
112262306a36Sopenharmony_ci		 */
112362306a36Sopenharmony_ci		dev_dbg(dev->dev, "d0i3 want resume\n");
112462306a36Sopenharmony_ci		mei_hbm_pg_resume(dev);
112562306a36Sopenharmony_ci	}
112662306a36Sopenharmony_ci}
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci/**
112962306a36Sopenharmony_ci * mei_me_pg_intr - perform pg processing in interrupt thread handler
113062306a36Sopenharmony_ci *
113162306a36Sopenharmony_ci * @dev: the device structure
113262306a36Sopenharmony_ci * @intr_source: interrupt source
113362306a36Sopenharmony_ci */
113462306a36Sopenharmony_cistatic void mei_me_pg_intr(struct mei_device *dev, u32 intr_source)
113562306a36Sopenharmony_ci{
113662306a36Sopenharmony_ci	struct mei_me_hw *hw = to_me_hw(dev);
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	if (hw->d0i3_supported)
113962306a36Sopenharmony_ci		mei_me_d0i3_intr(dev, intr_source);
114062306a36Sopenharmony_ci	else
114162306a36Sopenharmony_ci		mei_me_pg_legacy_intr(dev);
114262306a36Sopenharmony_ci}
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci/**
114562306a36Sopenharmony_ci * mei_me_pg_enter_sync - perform runtime pm entry procedure
114662306a36Sopenharmony_ci *
114762306a36Sopenharmony_ci * @dev: the device structure
114862306a36Sopenharmony_ci *
114962306a36Sopenharmony_ci * Return: 0 on success an error code otherwise
115062306a36Sopenharmony_ci */
115162306a36Sopenharmony_ciint mei_me_pg_enter_sync(struct mei_device *dev)
115262306a36Sopenharmony_ci{
115362306a36Sopenharmony_ci	struct mei_me_hw *hw = to_me_hw(dev);
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	if (hw->d0i3_supported)
115662306a36Sopenharmony_ci		return mei_me_d0i3_enter_sync(dev);
115762306a36Sopenharmony_ci	else
115862306a36Sopenharmony_ci		return mei_me_pg_legacy_enter_sync(dev);
115962306a36Sopenharmony_ci}
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci/**
116262306a36Sopenharmony_ci * mei_me_pg_exit_sync - perform runtime pm exit procedure
116362306a36Sopenharmony_ci *
116462306a36Sopenharmony_ci * @dev: the device structure
116562306a36Sopenharmony_ci *
116662306a36Sopenharmony_ci * Return: 0 on success an error code otherwise
116762306a36Sopenharmony_ci */
116862306a36Sopenharmony_ciint mei_me_pg_exit_sync(struct mei_device *dev)
116962306a36Sopenharmony_ci{
117062306a36Sopenharmony_ci	struct mei_me_hw *hw = to_me_hw(dev);
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	if (hw->d0i3_supported)
117362306a36Sopenharmony_ci		return mei_me_d0i3_exit_sync(dev);
117462306a36Sopenharmony_ci	else
117562306a36Sopenharmony_ci		return mei_me_pg_legacy_exit_sync(dev);
117662306a36Sopenharmony_ci}
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci/**
117962306a36Sopenharmony_ci * mei_me_hw_reset - resets fw via mei csr register.
118062306a36Sopenharmony_ci *
118162306a36Sopenharmony_ci * @dev: the device structure
118262306a36Sopenharmony_ci * @intr_enable: if interrupt should be enabled after reset.
118362306a36Sopenharmony_ci *
118462306a36Sopenharmony_ci * Return: 0 on success an error code otherwise
118562306a36Sopenharmony_ci */
118662306a36Sopenharmony_cistatic int mei_me_hw_reset(struct mei_device *dev, bool intr_enable)
118762306a36Sopenharmony_ci{
118862306a36Sopenharmony_ci	struct mei_me_hw *hw = to_me_hw(dev);
118962306a36Sopenharmony_ci	int ret;
119062306a36Sopenharmony_ci	u32 hcsr;
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	if (intr_enable) {
119362306a36Sopenharmony_ci		mei_me_intr_enable(dev);
119462306a36Sopenharmony_ci		if (hw->d0i3_supported) {
119562306a36Sopenharmony_ci			ret = mei_me_d0i3_exit_sync(dev);
119662306a36Sopenharmony_ci			if (ret)
119762306a36Sopenharmony_ci				return ret;
119862306a36Sopenharmony_ci		} else {
119962306a36Sopenharmony_ci			hw->pg_state = MEI_PG_OFF;
120062306a36Sopenharmony_ci		}
120162306a36Sopenharmony_ci	}
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	pm_runtime_set_active(dev->dev);
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	hcsr = mei_hcsr_read(dev);
120662306a36Sopenharmony_ci	/* H_RST may be found lit before reset is started,
120762306a36Sopenharmony_ci	 * for example if preceding reset flow hasn't completed.
120862306a36Sopenharmony_ci	 * In that case asserting H_RST will be ignored, therefore
120962306a36Sopenharmony_ci	 * we need to clean H_RST bit to start a successful reset sequence.
121062306a36Sopenharmony_ci	 */
121162306a36Sopenharmony_ci	if ((hcsr & H_RST) == H_RST) {
121262306a36Sopenharmony_ci		dev_warn(dev->dev, "H_RST is set = 0x%08X", hcsr);
121362306a36Sopenharmony_ci		hcsr &= ~H_RST;
121462306a36Sopenharmony_ci		mei_hcsr_set(dev, hcsr);
121562306a36Sopenharmony_ci		hcsr = mei_hcsr_read(dev);
121662306a36Sopenharmony_ci	}
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	hcsr |= H_RST | H_IG | H_CSR_IS_MASK;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	if (!intr_enable || mei_me_hw_use_polling(to_me_hw(dev)))
122162306a36Sopenharmony_ci		hcsr &= ~H_CSR_IE_MASK;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	dev->recvd_hw_ready = false;
122462306a36Sopenharmony_ci	mei_hcsr_write(dev, hcsr);
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	/*
122762306a36Sopenharmony_ci	 * Host reads the H_CSR once to ensure that the
122862306a36Sopenharmony_ci	 * posted write to H_CSR completes.
122962306a36Sopenharmony_ci	 */
123062306a36Sopenharmony_ci	hcsr = mei_hcsr_read(dev);
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	if ((hcsr & H_RST) == 0)
123362306a36Sopenharmony_ci		dev_warn(dev->dev, "H_RST is not set = 0x%08X", hcsr);
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	if ((hcsr & H_RDY) == H_RDY)
123662306a36Sopenharmony_ci		dev_warn(dev->dev, "H_RDY is not cleared 0x%08X", hcsr);
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	if (!intr_enable) {
123962306a36Sopenharmony_ci		mei_me_hw_reset_release(dev);
124062306a36Sopenharmony_ci		if (hw->d0i3_supported) {
124162306a36Sopenharmony_ci			ret = mei_me_d0i3_enter(dev);
124262306a36Sopenharmony_ci			if (ret)
124362306a36Sopenharmony_ci				return ret;
124462306a36Sopenharmony_ci		}
124562306a36Sopenharmony_ci	}
124662306a36Sopenharmony_ci	return 0;
124762306a36Sopenharmony_ci}
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci/**
125062306a36Sopenharmony_ci * mei_me_irq_quick_handler - The ISR of the MEI device
125162306a36Sopenharmony_ci *
125262306a36Sopenharmony_ci * @irq: The irq number
125362306a36Sopenharmony_ci * @dev_id: pointer to the device structure
125462306a36Sopenharmony_ci *
125562306a36Sopenharmony_ci * Return: irqreturn_t
125662306a36Sopenharmony_ci */
125762306a36Sopenharmony_ciirqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id)
125862306a36Sopenharmony_ci{
125962306a36Sopenharmony_ci	struct mei_device *dev = (struct mei_device *)dev_id;
126062306a36Sopenharmony_ci	u32 hcsr;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	hcsr = mei_hcsr_read(dev);
126362306a36Sopenharmony_ci	if (!me_intr_src(hcsr))
126462306a36Sopenharmony_ci		return IRQ_NONE;
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	dev_dbg(dev->dev, "interrupt source 0x%08X\n", me_intr_src(hcsr));
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	/* disable interrupts on device */
126962306a36Sopenharmony_ci	me_intr_disable(dev, hcsr);
127062306a36Sopenharmony_ci	return IRQ_WAKE_THREAD;
127162306a36Sopenharmony_ci}
127262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_me_irq_quick_handler);
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci/**
127562306a36Sopenharmony_ci * mei_me_irq_thread_handler - function called after ISR to handle the interrupt
127662306a36Sopenharmony_ci * processing.
127762306a36Sopenharmony_ci *
127862306a36Sopenharmony_ci * @irq: The irq number
127962306a36Sopenharmony_ci * @dev_id: pointer to the device structure
128062306a36Sopenharmony_ci *
128162306a36Sopenharmony_ci * Return: irqreturn_t
128262306a36Sopenharmony_ci *
128362306a36Sopenharmony_ci */
128462306a36Sopenharmony_ciirqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
128562306a36Sopenharmony_ci{
128662306a36Sopenharmony_ci	struct mei_device *dev = (struct mei_device *) dev_id;
128762306a36Sopenharmony_ci	struct list_head cmpl_list;
128862306a36Sopenharmony_ci	s32 slots;
128962306a36Sopenharmony_ci	u32 hcsr;
129062306a36Sopenharmony_ci	int rets = 0;
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	dev_dbg(dev->dev, "function called after ISR to handle the interrupt processing.\n");
129362306a36Sopenharmony_ci	/* initialize our complete list */
129462306a36Sopenharmony_ci	mutex_lock(&dev->device_lock);
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	hcsr = mei_hcsr_read(dev);
129762306a36Sopenharmony_ci	me_intr_clear(dev, hcsr);
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	INIT_LIST_HEAD(&cmpl_list);
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	/* check if ME wants a reset */
130262306a36Sopenharmony_ci	if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING) {
130362306a36Sopenharmony_ci		dev_warn(dev->dev, "FW not ready: resetting: dev_state = %d pxp = %d\n",
130462306a36Sopenharmony_ci			 dev->dev_state, dev->pxp_mode);
130562306a36Sopenharmony_ci		if (dev->dev_state == MEI_DEV_POWERING_DOWN ||
130662306a36Sopenharmony_ci		    dev->dev_state == MEI_DEV_POWER_DOWN)
130762306a36Sopenharmony_ci			mei_cl_all_disconnect(dev);
130862306a36Sopenharmony_ci		else if (dev->dev_state != MEI_DEV_DISABLED)
130962306a36Sopenharmony_ci			schedule_work(&dev->reset_work);
131062306a36Sopenharmony_ci		goto end;
131162306a36Sopenharmony_ci	}
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	if (mei_me_hw_is_resetting(dev))
131462306a36Sopenharmony_ci		mei_hcsr_set_hig(dev);
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	mei_me_pg_intr(dev, me_intr_src(hcsr));
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	/*  check if we need to start the dev */
131962306a36Sopenharmony_ci	if (!mei_host_is_ready(dev)) {
132062306a36Sopenharmony_ci		if (mei_hw_is_ready(dev)) {
132162306a36Sopenharmony_ci			dev_dbg(dev->dev, "we need to start the dev.\n");
132262306a36Sopenharmony_ci			dev->recvd_hw_ready = true;
132362306a36Sopenharmony_ci			wake_up(&dev->wait_hw_ready);
132462306a36Sopenharmony_ci		} else {
132562306a36Sopenharmony_ci			dev_dbg(dev->dev, "Spurious Interrupt\n");
132662306a36Sopenharmony_ci		}
132762306a36Sopenharmony_ci		goto end;
132862306a36Sopenharmony_ci	}
132962306a36Sopenharmony_ci	/* check slots available for reading */
133062306a36Sopenharmony_ci	slots = mei_count_full_read_slots(dev);
133162306a36Sopenharmony_ci	while (slots > 0) {
133262306a36Sopenharmony_ci		dev_dbg(dev->dev, "slots to read = %08x\n", slots);
133362306a36Sopenharmony_ci		rets = mei_irq_read_handler(dev, &cmpl_list, &slots);
133462306a36Sopenharmony_ci		/* There is a race between ME write and interrupt delivery:
133562306a36Sopenharmony_ci		 * Not all data is always available immediately after the
133662306a36Sopenharmony_ci		 * interrupt, so try to read again on the next interrupt.
133762306a36Sopenharmony_ci		 */
133862306a36Sopenharmony_ci		if (rets == -ENODATA)
133962306a36Sopenharmony_ci			break;
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci		if (rets) {
134262306a36Sopenharmony_ci			dev_err(dev->dev, "mei_irq_read_handler ret = %d, state = %d.\n",
134362306a36Sopenharmony_ci				rets, dev->dev_state);
134462306a36Sopenharmony_ci			if (dev->dev_state != MEI_DEV_RESETTING &&
134562306a36Sopenharmony_ci			    dev->dev_state != MEI_DEV_DISABLED &&
134662306a36Sopenharmony_ci			    dev->dev_state != MEI_DEV_POWERING_DOWN &&
134762306a36Sopenharmony_ci			    dev->dev_state != MEI_DEV_POWER_DOWN)
134862306a36Sopenharmony_ci				schedule_work(&dev->reset_work);
134962306a36Sopenharmony_ci			goto end;
135062306a36Sopenharmony_ci		}
135162306a36Sopenharmony_ci	}
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	/*
135662306a36Sopenharmony_ci	 * During PG handshake only allowed write is the replay to the
135762306a36Sopenharmony_ci	 * PG exit message, so block calling write function
135862306a36Sopenharmony_ci	 * if the pg event is in PG handshake
135962306a36Sopenharmony_ci	 */
136062306a36Sopenharmony_ci	if (dev->pg_event != MEI_PG_EVENT_WAIT &&
136162306a36Sopenharmony_ci	    dev->pg_event != MEI_PG_EVENT_RECEIVED) {
136262306a36Sopenharmony_ci		rets = mei_irq_write_handler(dev, &cmpl_list);
136362306a36Sopenharmony_ci		dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
136462306a36Sopenharmony_ci	}
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	mei_irq_compl_handler(dev, &cmpl_list);
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ciend:
136962306a36Sopenharmony_ci	dev_dbg(dev->dev, "interrupt thread end ret = %d\n", rets);
137062306a36Sopenharmony_ci	mei_me_intr_enable(dev);
137162306a36Sopenharmony_ci	mutex_unlock(&dev->device_lock);
137262306a36Sopenharmony_ci	return IRQ_HANDLED;
137362306a36Sopenharmony_ci}
137462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_me_irq_thread_handler);
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci#define MEI_POLLING_TIMEOUT_ACTIVE 100
137762306a36Sopenharmony_ci#define MEI_POLLING_TIMEOUT_IDLE   500
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci/**
138062306a36Sopenharmony_ci * mei_me_polling_thread - interrupt register polling thread
138162306a36Sopenharmony_ci *
138262306a36Sopenharmony_ci * The thread monitors the interrupt source register and calls
138362306a36Sopenharmony_ci * mei_me_irq_thread_handler() to handle the firmware
138462306a36Sopenharmony_ci * input.
138562306a36Sopenharmony_ci *
138662306a36Sopenharmony_ci * The function polls in MEI_POLLING_TIMEOUT_ACTIVE timeout
138762306a36Sopenharmony_ci * in case there was an event, in idle case the polling
138862306a36Sopenharmony_ci * time increases yet again by MEI_POLLING_TIMEOUT_ACTIVE
138962306a36Sopenharmony_ci * up to MEI_POLLING_TIMEOUT_IDLE.
139062306a36Sopenharmony_ci *
139162306a36Sopenharmony_ci * @_dev: mei device
139262306a36Sopenharmony_ci *
139362306a36Sopenharmony_ci * Return: always 0
139462306a36Sopenharmony_ci */
139562306a36Sopenharmony_ciint mei_me_polling_thread(void *_dev)
139662306a36Sopenharmony_ci{
139762306a36Sopenharmony_ci	struct mei_device *dev = _dev;
139862306a36Sopenharmony_ci	irqreturn_t irq_ret;
139962306a36Sopenharmony_ci	long polling_timeout = MEI_POLLING_TIMEOUT_ACTIVE;
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	dev_dbg(dev->dev, "kernel thread is running\n");
140262306a36Sopenharmony_ci	while (!kthread_should_stop()) {
140362306a36Sopenharmony_ci		struct mei_me_hw *hw = to_me_hw(dev);
140462306a36Sopenharmony_ci		u32 hcsr;
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci		wait_event_timeout(hw->wait_active,
140762306a36Sopenharmony_ci				   hw->is_active || kthread_should_stop(),
140862306a36Sopenharmony_ci				   msecs_to_jiffies(MEI_POLLING_TIMEOUT_IDLE));
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci		if (kthread_should_stop())
141162306a36Sopenharmony_ci			break;
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci		hcsr = mei_hcsr_read(dev);
141462306a36Sopenharmony_ci		if (me_intr_src(hcsr)) {
141562306a36Sopenharmony_ci			polling_timeout = MEI_POLLING_TIMEOUT_ACTIVE;
141662306a36Sopenharmony_ci			irq_ret = mei_me_irq_thread_handler(1, dev);
141762306a36Sopenharmony_ci			if (irq_ret != IRQ_HANDLED)
141862306a36Sopenharmony_ci				dev_err(dev->dev, "irq_ret %d\n", irq_ret);
141962306a36Sopenharmony_ci		} else {
142062306a36Sopenharmony_ci			/*
142162306a36Sopenharmony_ci			 * Increase timeout by MEI_POLLING_TIMEOUT_ACTIVE
142262306a36Sopenharmony_ci			 * up to MEI_POLLING_TIMEOUT_IDLE
142362306a36Sopenharmony_ci			 */
142462306a36Sopenharmony_ci			polling_timeout = clamp_val(polling_timeout + MEI_POLLING_TIMEOUT_ACTIVE,
142562306a36Sopenharmony_ci						    MEI_POLLING_TIMEOUT_ACTIVE,
142662306a36Sopenharmony_ci						    MEI_POLLING_TIMEOUT_IDLE);
142762306a36Sopenharmony_ci		}
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci		schedule_timeout_interruptible(msecs_to_jiffies(polling_timeout));
143062306a36Sopenharmony_ci	}
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	return 0;
143362306a36Sopenharmony_ci}
143462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_me_polling_thread);
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_cistatic const struct mei_hw_ops mei_me_hw_ops = {
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	.trc_status = mei_me_trc_status,
143962306a36Sopenharmony_ci	.fw_status = mei_me_fw_status,
144062306a36Sopenharmony_ci	.pg_state  = mei_me_pg_state,
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	.host_is_ready = mei_me_host_is_ready,
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	.hw_is_ready = mei_me_hw_is_ready,
144562306a36Sopenharmony_ci	.hw_reset = mei_me_hw_reset,
144662306a36Sopenharmony_ci	.hw_config = mei_me_hw_config,
144762306a36Sopenharmony_ci	.hw_start = mei_me_hw_start,
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	.pg_in_transition = mei_me_pg_in_transition,
145062306a36Sopenharmony_ci	.pg_is_enabled = mei_me_pg_is_enabled,
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	.intr_clear = mei_me_intr_clear,
145362306a36Sopenharmony_ci	.intr_enable = mei_me_intr_enable,
145462306a36Sopenharmony_ci	.intr_disable = mei_me_intr_disable,
145562306a36Sopenharmony_ci	.synchronize_irq = mei_me_synchronize_irq,
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	.hbuf_free_slots = mei_me_hbuf_empty_slots,
145862306a36Sopenharmony_ci	.hbuf_is_ready = mei_me_hbuf_is_empty,
145962306a36Sopenharmony_ci	.hbuf_depth = mei_me_hbuf_depth,
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	.write = mei_me_hbuf_write,
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	.rdbuf_full_slots = mei_me_count_full_read_slots,
146462306a36Sopenharmony_ci	.read_hdr = mei_me_mecbrw_read,
146562306a36Sopenharmony_ci	.read = mei_me_read_slots
146662306a36Sopenharmony_ci};
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci/**
146962306a36Sopenharmony_ci * mei_me_fw_type_nm() - check for nm sku
147062306a36Sopenharmony_ci *
147162306a36Sopenharmony_ci * Read ME FW Status register to check for the Node Manager (NM) Firmware.
147262306a36Sopenharmony_ci * The NM FW is only signaled in PCI function 0.
147362306a36Sopenharmony_ci * __Note__: Deprecated by PCH8 and newer.
147462306a36Sopenharmony_ci *
147562306a36Sopenharmony_ci * @pdev: pci device
147662306a36Sopenharmony_ci *
147762306a36Sopenharmony_ci * Return: true in case of NM firmware
147862306a36Sopenharmony_ci */
147962306a36Sopenharmony_cistatic bool mei_me_fw_type_nm(const struct pci_dev *pdev)
148062306a36Sopenharmony_ci{
148162306a36Sopenharmony_ci	u32 reg;
148262306a36Sopenharmony_ci	unsigned int devfn;
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0);
148562306a36Sopenharmony_ci	pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_2, &reg);
148662306a36Sopenharmony_ci	trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_2", PCI_CFG_HFS_2, reg);
148762306a36Sopenharmony_ci	/* make sure that bit 9 (NM) is up and bit 10 (DM) is down */
148862306a36Sopenharmony_ci	return (reg & 0x600) == 0x200;
148962306a36Sopenharmony_ci}
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci#define MEI_CFG_FW_NM                           \
149262306a36Sopenharmony_ci	.quirk_probe = mei_me_fw_type_nm
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci/**
149562306a36Sopenharmony_ci * mei_me_fw_type_sps_4() - check for sps 4.0 sku
149662306a36Sopenharmony_ci *
149762306a36Sopenharmony_ci * Read ME FW Status register to check for SPS Firmware.
149862306a36Sopenharmony_ci * The SPS FW is only signaled in the PCI function 0.
149962306a36Sopenharmony_ci * __Note__: Deprecated by SPS 5.0 and newer.
150062306a36Sopenharmony_ci *
150162306a36Sopenharmony_ci * @pdev: pci device
150262306a36Sopenharmony_ci *
150362306a36Sopenharmony_ci * Return: true in case of SPS firmware
150462306a36Sopenharmony_ci */
150562306a36Sopenharmony_cistatic bool mei_me_fw_type_sps_4(const struct pci_dev *pdev)
150662306a36Sopenharmony_ci{
150762306a36Sopenharmony_ci	u32 reg;
150862306a36Sopenharmony_ci	unsigned int devfn;
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0);
151162306a36Sopenharmony_ci	pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_1, &reg);
151262306a36Sopenharmony_ci	trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_1", PCI_CFG_HFS_1, reg);
151362306a36Sopenharmony_ci	return (reg & PCI_CFG_HFS_1_OPMODE_MSK) == PCI_CFG_HFS_1_OPMODE_SPS;
151462306a36Sopenharmony_ci}
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci#define MEI_CFG_FW_SPS_4                          \
151762306a36Sopenharmony_ci	.quirk_probe = mei_me_fw_type_sps_4
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci/**
152062306a36Sopenharmony_ci * mei_me_fw_type_sps_ign() - check for sps or ign sku
152162306a36Sopenharmony_ci *
152262306a36Sopenharmony_ci * Read ME FW Status register to check for SPS or IGN Firmware.
152362306a36Sopenharmony_ci * The SPS/IGN FW is only signaled in pci function 0
152462306a36Sopenharmony_ci *
152562306a36Sopenharmony_ci * @pdev: pci device
152662306a36Sopenharmony_ci *
152762306a36Sopenharmony_ci * Return: true in case of SPS/IGN firmware
152862306a36Sopenharmony_ci */
152962306a36Sopenharmony_cistatic bool mei_me_fw_type_sps_ign(const struct pci_dev *pdev)
153062306a36Sopenharmony_ci{
153162306a36Sopenharmony_ci	u32 reg;
153262306a36Sopenharmony_ci	u32 fw_type;
153362306a36Sopenharmony_ci	unsigned int devfn;
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0);
153662306a36Sopenharmony_ci	pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_3, &reg);
153762306a36Sopenharmony_ci	trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_3", PCI_CFG_HFS_3, reg);
153862306a36Sopenharmony_ci	fw_type = (reg & PCI_CFG_HFS_3_FW_SKU_MSK);
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "fw type is %d\n", fw_type);
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci	return fw_type == PCI_CFG_HFS_3_FW_SKU_IGN ||
154362306a36Sopenharmony_ci	       fw_type == PCI_CFG_HFS_3_FW_SKU_SPS;
154462306a36Sopenharmony_ci}
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci#define MEI_CFG_KIND_ITOUCH                     \
154762306a36Sopenharmony_ci	.kind = "itouch"
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci#define MEI_CFG_TYPE_GSC                        \
155062306a36Sopenharmony_ci	.kind = "gsc"
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci#define MEI_CFG_TYPE_GSCFI                      \
155362306a36Sopenharmony_ci	.kind = "gscfi"
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci#define MEI_CFG_FW_SPS_IGN                      \
155662306a36Sopenharmony_ci	.quirk_probe = mei_me_fw_type_sps_ign
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci#define MEI_CFG_FW_VER_SUPP                     \
155962306a36Sopenharmony_ci	.fw_ver_supported = 1
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci#define MEI_CFG_ICH_HFS                      \
156262306a36Sopenharmony_ci	.fw_status.count = 0
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci#define MEI_CFG_ICH10_HFS                        \
156562306a36Sopenharmony_ci	.fw_status.count = 1,                   \
156662306a36Sopenharmony_ci	.fw_status.status[0] = PCI_CFG_HFS_1
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci#define MEI_CFG_PCH_HFS                         \
156962306a36Sopenharmony_ci	.fw_status.count = 2,                   \
157062306a36Sopenharmony_ci	.fw_status.status[0] = PCI_CFG_HFS_1,   \
157162306a36Sopenharmony_ci	.fw_status.status[1] = PCI_CFG_HFS_2
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci#define MEI_CFG_PCH8_HFS                        \
157462306a36Sopenharmony_ci	.fw_status.count = 6,                   \
157562306a36Sopenharmony_ci	.fw_status.status[0] = PCI_CFG_HFS_1,   \
157662306a36Sopenharmony_ci	.fw_status.status[1] = PCI_CFG_HFS_2,   \
157762306a36Sopenharmony_ci	.fw_status.status[2] = PCI_CFG_HFS_3,   \
157862306a36Sopenharmony_ci	.fw_status.status[3] = PCI_CFG_HFS_4,   \
157962306a36Sopenharmony_ci	.fw_status.status[4] = PCI_CFG_HFS_5,   \
158062306a36Sopenharmony_ci	.fw_status.status[5] = PCI_CFG_HFS_6
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci#define MEI_CFG_DMA_128 \
158362306a36Sopenharmony_ci	.dma_size[DMA_DSCR_HOST] = SZ_128K, \
158462306a36Sopenharmony_ci	.dma_size[DMA_DSCR_DEVICE] = SZ_128K, \
158562306a36Sopenharmony_ci	.dma_size[DMA_DSCR_CTRL] = PAGE_SIZE
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci#define MEI_CFG_TRC \
158862306a36Sopenharmony_ci	.hw_trc_supported = 1
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci/* ICH Legacy devices */
159162306a36Sopenharmony_cistatic const struct mei_cfg mei_me_ich_cfg = {
159262306a36Sopenharmony_ci	MEI_CFG_ICH_HFS,
159362306a36Sopenharmony_ci};
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci/* ICH devices */
159662306a36Sopenharmony_cistatic const struct mei_cfg mei_me_ich10_cfg = {
159762306a36Sopenharmony_ci	MEI_CFG_ICH10_HFS,
159862306a36Sopenharmony_ci};
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci/* PCH6 devices */
160162306a36Sopenharmony_cistatic const struct mei_cfg mei_me_pch6_cfg = {
160262306a36Sopenharmony_ci	MEI_CFG_PCH_HFS,
160362306a36Sopenharmony_ci};
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci/* PCH7 devices */
160662306a36Sopenharmony_cistatic const struct mei_cfg mei_me_pch7_cfg = {
160762306a36Sopenharmony_ci	MEI_CFG_PCH_HFS,
160862306a36Sopenharmony_ci	MEI_CFG_FW_VER_SUPP,
160962306a36Sopenharmony_ci};
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci/* PCH Cougar Point and Patsburg with quirk for Node Manager exclusion */
161262306a36Sopenharmony_cistatic const struct mei_cfg mei_me_pch_cpt_pbg_cfg = {
161362306a36Sopenharmony_ci	MEI_CFG_PCH_HFS,
161462306a36Sopenharmony_ci	MEI_CFG_FW_VER_SUPP,
161562306a36Sopenharmony_ci	MEI_CFG_FW_NM,
161662306a36Sopenharmony_ci};
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci/* PCH8 Lynx Point and newer devices */
161962306a36Sopenharmony_cistatic const struct mei_cfg mei_me_pch8_cfg = {
162062306a36Sopenharmony_ci	MEI_CFG_PCH8_HFS,
162162306a36Sopenharmony_ci	MEI_CFG_FW_VER_SUPP,
162262306a36Sopenharmony_ci};
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci/* PCH8 Lynx Point and newer devices - iTouch */
162562306a36Sopenharmony_cistatic const struct mei_cfg mei_me_pch8_itouch_cfg = {
162662306a36Sopenharmony_ci	MEI_CFG_KIND_ITOUCH,
162762306a36Sopenharmony_ci	MEI_CFG_PCH8_HFS,
162862306a36Sopenharmony_ci	MEI_CFG_FW_VER_SUPP,
162962306a36Sopenharmony_ci};
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci/* PCH8 Lynx Point with quirk for SPS Firmware exclusion */
163262306a36Sopenharmony_cistatic const struct mei_cfg mei_me_pch8_sps_4_cfg = {
163362306a36Sopenharmony_ci	MEI_CFG_PCH8_HFS,
163462306a36Sopenharmony_ci	MEI_CFG_FW_VER_SUPP,
163562306a36Sopenharmony_ci	MEI_CFG_FW_SPS_4,
163662306a36Sopenharmony_ci};
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci/* LBG with quirk for SPS (4.0) Firmware exclusion */
163962306a36Sopenharmony_cistatic const struct mei_cfg mei_me_pch12_sps_4_cfg = {
164062306a36Sopenharmony_ci	MEI_CFG_PCH8_HFS,
164162306a36Sopenharmony_ci	MEI_CFG_FW_VER_SUPP,
164262306a36Sopenharmony_ci	MEI_CFG_FW_SPS_4,
164362306a36Sopenharmony_ci};
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci/* Cannon Lake and newer devices */
164662306a36Sopenharmony_cistatic const struct mei_cfg mei_me_pch12_cfg = {
164762306a36Sopenharmony_ci	MEI_CFG_PCH8_HFS,
164862306a36Sopenharmony_ci	MEI_CFG_FW_VER_SUPP,
164962306a36Sopenharmony_ci	MEI_CFG_DMA_128,
165062306a36Sopenharmony_ci};
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci/* Cannon Lake with quirk for SPS 5.0 and newer Firmware exclusion */
165362306a36Sopenharmony_cistatic const struct mei_cfg mei_me_pch12_sps_cfg = {
165462306a36Sopenharmony_ci	MEI_CFG_PCH8_HFS,
165562306a36Sopenharmony_ci	MEI_CFG_FW_VER_SUPP,
165662306a36Sopenharmony_ci	MEI_CFG_DMA_128,
165762306a36Sopenharmony_ci	MEI_CFG_FW_SPS_IGN,
165862306a36Sopenharmony_ci};
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci/* Cannon Lake itouch with quirk for SPS 5.0 and newer Firmware exclusion
166162306a36Sopenharmony_ci * w/o DMA support.
166262306a36Sopenharmony_ci */
166362306a36Sopenharmony_cistatic const struct mei_cfg mei_me_pch12_itouch_sps_cfg = {
166462306a36Sopenharmony_ci	MEI_CFG_KIND_ITOUCH,
166562306a36Sopenharmony_ci	MEI_CFG_PCH8_HFS,
166662306a36Sopenharmony_ci	MEI_CFG_FW_VER_SUPP,
166762306a36Sopenharmony_ci	MEI_CFG_FW_SPS_IGN,
166862306a36Sopenharmony_ci};
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci/* Tiger Lake and newer devices */
167162306a36Sopenharmony_cistatic const struct mei_cfg mei_me_pch15_cfg = {
167262306a36Sopenharmony_ci	MEI_CFG_PCH8_HFS,
167362306a36Sopenharmony_ci	MEI_CFG_FW_VER_SUPP,
167462306a36Sopenharmony_ci	MEI_CFG_DMA_128,
167562306a36Sopenharmony_ci	MEI_CFG_TRC,
167662306a36Sopenharmony_ci};
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci/* Tiger Lake with quirk for SPS 5.0 and newer Firmware exclusion */
167962306a36Sopenharmony_cistatic const struct mei_cfg mei_me_pch15_sps_cfg = {
168062306a36Sopenharmony_ci	MEI_CFG_PCH8_HFS,
168162306a36Sopenharmony_ci	MEI_CFG_FW_VER_SUPP,
168262306a36Sopenharmony_ci	MEI_CFG_DMA_128,
168362306a36Sopenharmony_ci	MEI_CFG_TRC,
168462306a36Sopenharmony_ci	MEI_CFG_FW_SPS_IGN,
168562306a36Sopenharmony_ci};
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci/* Graphics System Controller */
168862306a36Sopenharmony_cistatic const struct mei_cfg mei_me_gsc_cfg = {
168962306a36Sopenharmony_ci	MEI_CFG_TYPE_GSC,
169062306a36Sopenharmony_ci	MEI_CFG_PCH8_HFS,
169162306a36Sopenharmony_ci	MEI_CFG_FW_VER_SUPP,
169262306a36Sopenharmony_ci};
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci/* Graphics System Controller Firmware Interface */
169562306a36Sopenharmony_cistatic const struct mei_cfg mei_me_gscfi_cfg = {
169662306a36Sopenharmony_ci	MEI_CFG_TYPE_GSCFI,
169762306a36Sopenharmony_ci	MEI_CFG_PCH8_HFS,
169862306a36Sopenharmony_ci	MEI_CFG_FW_VER_SUPP,
169962306a36Sopenharmony_ci};
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci/*
170262306a36Sopenharmony_ci * mei_cfg_list - A list of platform platform specific configurations.
170362306a36Sopenharmony_ci * Note: has to be synchronized with  enum mei_cfg_idx.
170462306a36Sopenharmony_ci */
170562306a36Sopenharmony_cistatic const struct mei_cfg *const mei_cfg_list[] = {
170662306a36Sopenharmony_ci	[MEI_ME_UNDEF_CFG] = NULL,
170762306a36Sopenharmony_ci	[MEI_ME_ICH_CFG] = &mei_me_ich_cfg,
170862306a36Sopenharmony_ci	[MEI_ME_ICH10_CFG] = &mei_me_ich10_cfg,
170962306a36Sopenharmony_ci	[MEI_ME_PCH6_CFG] = &mei_me_pch6_cfg,
171062306a36Sopenharmony_ci	[MEI_ME_PCH7_CFG] = &mei_me_pch7_cfg,
171162306a36Sopenharmony_ci	[MEI_ME_PCH_CPT_PBG_CFG] = &mei_me_pch_cpt_pbg_cfg,
171262306a36Sopenharmony_ci	[MEI_ME_PCH8_CFG] = &mei_me_pch8_cfg,
171362306a36Sopenharmony_ci	[MEI_ME_PCH8_ITOUCH_CFG] = &mei_me_pch8_itouch_cfg,
171462306a36Sopenharmony_ci	[MEI_ME_PCH8_SPS_4_CFG] = &mei_me_pch8_sps_4_cfg,
171562306a36Sopenharmony_ci	[MEI_ME_PCH12_CFG] = &mei_me_pch12_cfg,
171662306a36Sopenharmony_ci	[MEI_ME_PCH12_SPS_4_CFG] = &mei_me_pch12_sps_4_cfg,
171762306a36Sopenharmony_ci	[MEI_ME_PCH12_SPS_CFG] = &mei_me_pch12_sps_cfg,
171862306a36Sopenharmony_ci	[MEI_ME_PCH12_SPS_ITOUCH_CFG] = &mei_me_pch12_itouch_sps_cfg,
171962306a36Sopenharmony_ci	[MEI_ME_PCH15_CFG] = &mei_me_pch15_cfg,
172062306a36Sopenharmony_ci	[MEI_ME_PCH15_SPS_CFG] = &mei_me_pch15_sps_cfg,
172162306a36Sopenharmony_ci	[MEI_ME_GSC_CFG] = &mei_me_gsc_cfg,
172262306a36Sopenharmony_ci	[MEI_ME_GSCFI_CFG] = &mei_me_gscfi_cfg,
172362306a36Sopenharmony_ci};
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ciconst struct mei_cfg *mei_me_get_cfg(kernel_ulong_t idx)
172662306a36Sopenharmony_ci{
172762306a36Sopenharmony_ci	BUILD_BUG_ON(ARRAY_SIZE(mei_cfg_list) != MEI_ME_NUM_CFG);
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	if (idx >= MEI_ME_NUM_CFG)
173062306a36Sopenharmony_ci		return NULL;
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci	return mei_cfg_list[idx];
173362306a36Sopenharmony_ci}
173462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_me_get_cfg);
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci/**
173762306a36Sopenharmony_ci * mei_me_dev_init - allocates and initializes the mei device structure
173862306a36Sopenharmony_ci *
173962306a36Sopenharmony_ci * @parent: device associated with physical device (pci/platform)
174062306a36Sopenharmony_ci * @cfg: per device generation config
174162306a36Sopenharmony_ci * @slow_fw: configure longer timeouts as FW is slow
174262306a36Sopenharmony_ci *
174362306a36Sopenharmony_ci * Return: The mei_device pointer on success, NULL on failure.
174462306a36Sopenharmony_ci */
174562306a36Sopenharmony_cistruct mei_device *mei_me_dev_init(struct device *parent,
174662306a36Sopenharmony_ci				   const struct mei_cfg *cfg, bool slow_fw)
174762306a36Sopenharmony_ci{
174862306a36Sopenharmony_ci	struct mei_device *dev;
174962306a36Sopenharmony_ci	struct mei_me_hw *hw;
175062306a36Sopenharmony_ci	int i;
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci	dev = devm_kzalloc(parent, sizeof(*dev) + sizeof(*hw), GFP_KERNEL);
175362306a36Sopenharmony_ci	if (!dev)
175462306a36Sopenharmony_ci		return NULL;
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	hw = to_me_hw(dev);
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	for (i = 0; i < DMA_DSCR_NUM; i++)
175962306a36Sopenharmony_ci		dev->dr_dscr[i].size = cfg->dma_size[i];
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_ci	mei_device_init(dev, parent, slow_fw, &mei_me_hw_ops);
176262306a36Sopenharmony_ci	hw->cfg = cfg;
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	dev->fw_f_fw_ver_supported = cfg->fw_ver_supported;
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci	dev->kind = cfg->kind;
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	return dev;
176962306a36Sopenharmony_ci}
177062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mei_me_dev_init);
1771