xref: /linux/drivers/misc/mei/hw-me.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
19fff0425STomas Winkler // SPDX-License-Identifier: GPL-2.0
29dc64d6aSTomas Winkler /*
35b063995STomas Winkler  * Copyright (c) 2003-2022, Intel Corporation. All rights reserved.
49dc64d6aSTomas Winkler  * Intel Management Engine Interface (Intel MEI) Linux driver
59dc64d6aSTomas Winkler  */
69dc64d6aSTomas Winkler 
79dc64d6aSTomas Winkler #include <linux/pci.h>
806ecd645STomas Winkler 
906ecd645STomas Winkler #include <linux/kthread.h>
1006ecd645STomas Winkler #include <linux/interrupt.h>
1177537ad2SAlexander Usyskin #include <linux/pm_runtime.h>
127026a5fdSAlexander Usyskin #include <linux/sizes.h>
135b063995STomas Winkler #include <linux/delay.h>
149dc64d6aSTomas Winkler 
159dc64d6aSTomas Winkler #include "mei_dev.h"
1606ecd645STomas Winkler #include "hbm.h"
1706ecd645STomas Winkler 
186e4cd27aSTomas Winkler #include "hw-me.h"
196e4cd27aSTomas Winkler #include "hw-me-regs.h"
2006ecd645STomas Winkler 
21a0a927d0STomas Winkler #include "mei-trace.h"
22a0a927d0STomas Winkler 
239dc64d6aSTomas Winkler /**
24b68301e9STomas Winkler  * mei_me_reg_read - Reads 32bit data from the mei device
259dc64d6aSTomas Winkler  *
26a8605ea2SAlexander Usyskin  * @hw: the me hardware structure
279dc64d6aSTomas Winkler  * @offset: offset from which to read the data
289dc64d6aSTomas Winkler  *
29a8605ea2SAlexander Usyskin  * Return: register value (u32)
309dc64d6aSTomas Winkler  */
mei_me_reg_read(const struct mei_me_hw * hw,unsigned long offset)31b68301e9STomas Winkler static inline u32 mei_me_reg_read(const struct mei_me_hw *hw,
329dc64d6aSTomas Winkler 			       unsigned long offset)
339dc64d6aSTomas Winkler {
3452c34561STomas Winkler 	return ioread32(hw->mem_addr + offset);
359dc64d6aSTomas Winkler }
369dc64d6aSTomas Winkler 
379dc64d6aSTomas Winkler 
389dc64d6aSTomas Winkler /**
39b68301e9STomas Winkler  * mei_me_reg_write - Writes 32bit data to the mei device
409dc64d6aSTomas Winkler  *
41a8605ea2SAlexander Usyskin  * @hw: the me hardware structure
429dc64d6aSTomas Winkler  * @offset: offset from which to write the data
439dc64d6aSTomas Winkler  * @value: register value to write (u32)
449dc64d6aSTomas Winkler  */
mei_me_reg_write(const struct mei_me_hw * hw,unsigned long offset,u32 value)45b68301e9STomas Winkler static inline void mei_me_reg_write(const struct mei_me_hw *hw,
469dc64d6aSTomas Winkler 				 unsigned long offset, u32 value)
479dc64d6aSTomas Winkler {
4852c34561STomas Winkler 	iowrite32(value, hw->mem_addr + offset);
499dc64d6aSTomas Winkler }
509dc64d6aSTomas Winkler 
519dc64d6aSTomas Winkler /**
52b68301e9STomas Winkler  * mei_me_mecbrw_read - Reads 32bit data from ME circular buffer
53d025284dSTomas Winkler  *  read window register
549dc64d6aSTomas Winkler  *
559dc64d6aSTomas Winkler  * @dev: the device structure
569dc64d6aSTomas Winkler  *
57a8605ea2SAlexander Usyskin  * Return: ME_CB_RW register value (u32)
589dc64d6aSTomas Winkler  */
mei_me_mecbrw_read(const struct mei_device * dev)59381a58c7STomas Winkler static inline u32 mei_me_mecbrw_read(const struct mei_device *dev)
609dc64d6aSTomas Winkler {
61b68301e9STomas Winkler 	return mei_me_reg_read(to_me_hw(dev), ME_CB_RW);
629dc64d6aSTomas Winkler }
63381a58c7STomas Winkler 
64381a58c7STomas Winkler /**
65381a58c7STomas Winkler  * mei_me_hcbww_write - write 32bit data to the host circular buffer
66381a58c7STomas Winkler  *
67381a58c7STomas Winkler  * @dev: the device structure
68381a58c7STomas Winkler  * @data: 32bit data to be written to the host circular buffer
69381a58c7STomas Winkler  */
mei_me_hcbww_write(struct mei_device * dev,u32 data)70381a58c7STomas Winkler static inline void mei_me_hcbww_write(struct mei_device *dev, u32 data)
71381a58c7STomas Winkler {
72381a58c7STomas Winkler 	mei_me_reg_write(to_me_hw(dev), H_CB_WW, data);
73381a58c7STomas Winkler }
74381a58c7STomas Winkler 
759dc64d6aSTomas Winkler /**
76b68301e9STomas Winkler  * mei_me_mecsr_read - Reads 32bit data from the ME CSR
779dc64d6aSTomas Winkler  *
78381a58c7STomas Winkler  * @dev: the device structure
799dc64d6aSTomas Winkler  *
80a8605ea2SAlexander Usyskin  * Return: ME_CSR_HA register value (u32)
819dc64d6aSTomas Winkler  */
mei_me_mecsr_read(const struct mei_device * dev)82381a58c7STomas Winkler static inline u32 mei_me_mecsr_read(const struct mei_device *dev)
839dc64d6aSTomas Winkler {
84a0a927d0STomas Winkler 	u32 reg;
85a0a927d0STomas Winkler 
86a0a927d0STomas Winkler 	reg = mei_me_reg_read(to_me_hw(dev), ME_CSR_HA);
87a0a927d0STomas Winkler 	trace_mei_reg_read(dev->dev, "ME_CSR_HA", ME_CSR_HA, reg);
88a0a927d0STomas Winkler 
89a0a927d0STomas Winkler 	return reg;
909dc64d6aSTomas Winkler }
919dc64d6aSTomas Winkler 
929dc64d6aSTomas Winkler /**
93d025284dSTomas Winkler  * mei_hcsr_read - Reads 32bit data from the host CSR
94d025284dSTomas Winkler  *
95381a58c7STomas Winkler  * @dev: the device structure
96d025284dSTomas Winkler  *
97a8605ea2SAlexander Usyskin  * Return: H_CSR register value (u32)
98d025284dSTomas Winkler  */
mei_hcsr_read(const struct mei_device * dev)99381a58c7STomas Winkler static inline u32 mei_hcsr_read(const struct mei_device *dev)
100d025284dSTomas Winkler {
101a0a927d0STomas Winkler 	u32 reg;
102a0a927d0STomas Winkler 
103a0a927d0STomas Winkler 	reg = mei_me_reg_read(to_me_hw(dev), H_CSR);
104a0a927d0STomas Winkler 	trace_mei_reg_read(dev->dev, "H_CSR", H_CSR, reg);
105a0a927d0STomas Winkler 
106a0a927d0STomas Winkler 	return reg;
107381a58c7STomas Winkler }
108381a58c7STomas Winkler 
109381a58c7STomas Winkler /**
110381a58c7STomas Winkler  * mei_hcsr_write - writes H_CSR register to the mei device
111381a58c7STomas Winkler  *
112381a58c7STomas Winkler  * @dev: the device structure
113381a58c7STomas Winkler  * @reg: new register value
114381a58c7STomas Winkler  */
mei_hcsr_write(struct mei_device * dev,u32 reg)115381a58c7STomas Winkler static inline void mei_hcsr_write(struct mei_device *dev, u32 reg)
116381a58c7STomas Winkler {
117a0a927d0STomas Winkler 	trace_mei_reg_write(dev->dev, "H_CSR", H_CSR, reg);
118381a58c7STomas Winkler 	mei_me_reg_write(to_me_hw(dev), H_CSR, reg);
119d025284dSTomas Winkler }
120d025284dSTomas Winkler 
121d025284dSTomas Winkler /**
122d025284dSTomas Winkler  * mei_hcsr_set - writes H_CSR register to the mei device,
1239dc64d6aSTomas Winkler  * and ignores the H_IS bit for it is write-one-to-zero.
1249dc64d6aSTomas Winkler  *
125381a58c7STomas Winkler  * @dev: the device structure
126381a58c7STomas Winkler  * @reg: new register value
1279dc64d6aSTomas Winkler  */
mei_hcsr_set(struct mei_device * dev,u32 reg)128381a58c7STomas Winkler static inline void mei_hcsr_set(struct mei_device *dev, u32 reg)
1299dc64d6aSTomas Winkler {
1301fa55b4eSAlexander Usyskin 	reg &= ~H_CSR_IS_MASK;
131381a58c7STomas Winkler 	mei_hcsr_write(dev, reg);
1329dc64d6aSTomas Winkler }
1339dc64d6aSTomas Winkler 
1341bd30b6aSTomas Winkler /**
1359c7daa61SAlexander Usyskin  * mei_hcsr_set_hig - set host interrupt (set H_IG)
1369c7daa61SAlexander Usyskin  *
1379c7daa61SAlexander Usyskin  * @dev: the device structure
1389c7daa61SAlexander Usyskin  */
mei_hcsr_set_hig(struct mei_device * dev)1399c7daa61SAlexander Usyskin static inline void mei_hcsr_set_hig(struct mei_device *dev)
1409c7daa61SAlexander Usyskin {
1419c7daa61SAlexander Usyskin 	u32 hcsr;
1429c7daa61SAlexander Usyskin 
1439c7daa61SAlexander Usyskin 	hcsr = mei_hcsr_read(dev) | H_IG;
1449c7daa61SAlexander Usyskin 	mei_hcsr_set(dev, hcsr);
1459c7daa61SAlexander Usyskin }
1469c7daa61SAlexander Usyskin 
1479c7daa61SAlexander Usyskin /**
148859ef2ffSAlexander Usyskin  * mei_me_d0i3c_read - Reads 32bit data from the D0I3C register
149859ef2ffSAlexander Usyskin  *
150859ef2ffSAlexander Usyskin  * @dev: the device structure
151859ef2ffSAlexander Usyskin  *
152859ef2ffSAlexander Usyskin  * Return: H_D0I3C register value (u32)
153859ef2ffSAlexander Usyskin  */
mei_me_d0i3c_read(const struct mei_device * dev)154859ef2ffSAlexander Usyskin static inline u32 mei_me_d0i3c_read(const struct mei_device *dev)
155859ef2ffSAlexander Usyskin {
156859ef2ffSAlexander Usyskin 	u32 reg;
157859ef2ffSAlexander Usyskin 
158859ef2ffSAlexander Usyskin 	reg = mei_me_reg_read(to_me_hw(dev), H_D0I3C);
159cf094ebeSAlexander Usyskin 	trace_mei_reg_read(dev->dev, "H_D0I3C", H_D0I3C, reg);
160859ef2ffSAlexander Usyskin 
161859ef2ffSAlexander Usyskin 	return reg;
162859ef2ffSAlexander Usyskin }
163859ef2ffSAlexander Usyskin 
164859ef2ffSAlexander Usyskin /**
165859ef2ffSAlexander Usyskin  * mei_me_d0i3c_write - writes H_D0I3C register to device
166859ef2ffSAlexander Usyskin  *
167859ef2ffSAlexander Usyskin  * @dev: the device structure
168859ef2ffSAlexander Usyskin  * @reg: new register value
169859ef2ffSAlexander Usyskin  */
mei_me_d0i3c_write(struct mei_device * dev,u32 reg)170859ef2ffSAlexander Usyskin static inline void mei_me_d0i3c_write(struct mei_device *dev, u32 reg)
171859ef2ffSAlexander Usyskin {
172cf094ebeSAlexander Usyskin 	trace_mei_reg_write(dev->dev, "H_D0I3C", H_D0I3C, reg);
173859ef2ffSAlexander Usyskin 	mei_me_reg_write(to_me_hw(dev), H_D0I3C, reg);
174859ef2ffSAlexander Usyskin }
175859ef2ffSAlexander Usyskin 
176859ef2ffSAlexander Usyskin /**
17752f6efdfSAlexander Usyskin  * mei_me_trc_status - read trc status register
17852f6efdfSAlexander Usyskin  *
17952f6efdfSAlexander Usyskin  * @dev: mei device
18052f6efdfSAlexander Usyskin  * @trc: trc status register value
18152f6efdfSAlexander Usyskin  *
18252f6efdfSAlexander Usyskin  * Return: 0 on success, error otherwise
18352f6efdfSAlexander Usyskin  */
mei_me_trc_status(struct mei_device * dev,u32 * trc)18452f6efdfSAlexander Usyskin static int mei_me_trc_status(struct mei_device *dev, u32 *trc)
18552f6efdfSAlexander Usyskin {
18652f6efdfSAlexander Usyskin 	struct mei_me_hw *hw = to_me_hw(dev);
18752f6efdfSAlexander Usyskin 
18852f6efdfSAlexander Usyskin 	if (!hw->cfg->hw_trc_supported)
18952f6efdfSAlexander Usyskin 		return -EOPNOTSUPP;
19052f6efdfSAlexander Usyskin 
19152f6efdfSAlexander Usyskin 	*trc = mei_me_reg_read(hw, ME_TRC);
19252f6efdfSAlexander Usyskin 	trace_mei_reg_read(dev->dev, "ME_TRC", ME_TRC, *trc);
19352f6efdfSAlexander Usyskin 
19452f6efdfSAlexander Usyskin 	return 0;
19552f6efdfSAlexander Usyskin }
19652f6efdfSAlexander Usyskin 
19752f6efdfSAlexander Usyskin /**
1981bd30b6aSTomas Winkler  * mei_me_fw_status - read fw status register from pci config space
1991bd30b6aSTomas Winkler  *
2001bd30b6aSTomas Winkler  * @dev: mei device
2011bd30b6aSTomas Winkler  * @fw_status: fw status register values
202ce23139cSAlexander Usyskin  *
203ce23139cSAlexander Usyskin  * Return: 0 on success, error otherwise
2041bd30b6aSTomas Winkler  */
mei_me_fw_status(struct mei_device * dev,struct mei_fw_status * fw_status)2051bd30b6aSTomas Winkler static int mei_me_fw_status(struct mei_device *dev,
2061bd30b6aSTomas Winkler 			    struct mei_fw_status *fw_status)
2071bd30b6aSTomas Winkler {
2084ad96db6STomas Winkler 	struct mei_me_hw *hw = to_me_hw(dev);
2094ad96db6STomas Winkler 	const struct mei_fw_status *fw_src = &hw->cfg->fw_status;
2101bd30b6aSTomas Winkler 	int ret;
2111bd30b6aSTomas Winkler 	int i;
2121bd30b6aSTomas Winkler 
213261e071aSTomas Winkler 	if (!fw_status || !hw->read_fws)
2141bd30b6aSTomas Winkler 		return -EINVAL;
2151bd30b6aSTomas Winkler 
2161bd30b6aSTomas Winkler 	fw_status->count = fw_src->count;
2171bd30b6aSTomas Winkler 	for (i = 0; i < fw_src->count && i < MEI_FW_STATUS_MAX; i++) {
218261e071aSTomas Winkler 		ret = hw->read_fws(dev, fw_src->status[i],
219a96c5482STomas Winkler 				   &fw_status->status[i]);
220261e071aSTomas Winkler 		trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HFS_X",
221a96c5482STomas Winkler 				       fw_src->status[i],
222a96c5482STomas Winkler 				       fw_status->status[i]);
2231bd30b6aSTomas Winkler 		if (ret)
2241bd30b6aSTomas Winkler 			return ret;
2251bd30b6aSTomas Winkler 	}
2261bd30b6aSTomas Winkler 
2271bd30b6aSTomas Winkler 	return 0;
2281bd30b6aSTomas Winkler }
229e7e0c231STomas Winkler 
230e7e0c231STomas Winkler /**
231393b148fSMasanari Iida  * mei_me_hw_config - configure hw dependent settings
232e7e0c231STomas Winkler  *
233e7e0c231STomas Winkler  * @dev: mei device
234261e071aSTomas Winkler  *
235261e071aSTomas Winkler  * Return:
236261e071aSTomas Winkler  *  * -EINVAL when read_fws is not set
237261e071aSTomas Winkler  *  * 0 on success
238261e071aSTomas Winkler  *
239e7e0c231STomas Winkler  */
mei_me_hw_config(struct mei_device * dev)240261e071aSTomas Winkler static int mei_me_hw_config(struct mei_device *dev)
241e7e0c231STomas Winkler {
242ba9cdd0eSTomas Winkler 	struct mei_me_hw *hw = to_me_hw(dev);
243bb9f4d26SAlexander Usyskin 	u32 hcsr, reg;
244bb9f4d26SAlexander Usyskin 
245261e071aSTomas Winkler 	if (WARN_ON(!hw->read_fws))
246261e071aSTomas Winkler 		return -EINVAL;
247261e071aSTomas Winkler 
248e7e0c231STomas Winkler 	/* Doesn't change in runtime */
249bb9f4d26SAlexander Usyskin 	hcsr = mei_hcsr_read(dev);
2508c8d964cSTomas Winkler 	hw->hbuf_depth = (hcsr & H_CBD) >> 24;
251ba9cdd0eSTomas Winkler 
252bb9f4d26SAlexander Usyskin 	reg = 0;
253261e071aSTomas Winkler 	hw->read_fws(dev, PCI_CFG_HFS_1, &reg);
254a96c5482STomas Winkler 	trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HFS_1", PCI_CFG_HFS_1, reg);
255bb9f4d26SAlexander Usyskin 	hw->d0i3_supported =
256bb9f4d26SAlexander Usyskin 		((reg & PCI_CFG_HFS_1_D0I3_MSK) == PCI_CFG_HFS_1_D0I3_MSK);
257b9a1fc99SAlexander Usyskin 
258b9a1fc99SAlexander Usyskin 	hw->pg_state = MEI_PG_OFF;
259b9a1fc99SAlexander Usyskin 	if (hw->d0i3_supported) {
260b9a1fc99SAlexander Usyskin 		reg = mei_me_d0i3c_read(dev);
261b9a1fc99SAlexander Usyskin 		if (reg & H_D0I3C_I3)
262b9a1fc99SAlexander Usyskin 			hw->pg_state = MEI_PG_ON;
263b9a1fc99SAlexander Usyskin 	}
264261e071aSTomas Winkler 
265261e071aSTomas Winkler 	return 0;
266e7e0c231STomas Winkler }
267964a2331STomas Winkler 
268964a2331STomas Winkler /**
269964a2331STomas Winkler  * mei_me_pg_state  - translate internal pg state
270964a2331STomas Winkler  *   to the mei power gating state
271964a2331STomas Winkler  *
272ce23139cSAlexander Usyskin  * @dev:  mei device
273ce23139cSAlexander Usyskin  *
274ce23139cSAlexander Usyskin  * Return: MEI_PG_OFF if aliveness is on and MEI_PG_ON otherwise
275964a2331STomas Winkler  */
mei_me_pg_state(struct mei_device * dev)276964a2331STomas Winkler static inline enum mei_pg_state mei_me_pg_state(struct mei_device *dev)
277964a2331STomas Winkler {
278ba9cdd0eSTomas Winkler 	struct mei_me_hw *hw = to_me_hw(dev);
27992db1555STomas Winkler 
280ba9cdd0eSTomas Winkler 	return hw->pg_state;
281964a2331STomas Winkler }
282964a2331STomas Winkler 
me_intr_src(u32 hcsr)283a2eb0fc0SAlexander Usyskin static inline u32 me_intr_src(u32 hcsr)
284a2eb0fc0SAlexander Usyskin {
285a2eb0fc0SAlexander Usyskin 	return hcsr & H_CSR_IS_MASK;
286a2eb0fc0SAlexander Usyskin }
287a2eb0fc0SAlexander Usyskin 
288a2eb0fc0SAlexander Usyskin /**
289a2eb0fc0SAlexander Usyskin  * me_intr_disable - disables mei device interrupts
290a2eb0fc0SAlexander Usyskin  *      using supplied hcsr register value.
291a2eb0fc0SAlexander Usyskin  *
292a2eb0fc0SAlexander Usyskin  * @dev: the device structure
293a2eb0fc0SAlexander Usyskin  * @hcsr: supplied hcsr register value
294a2eb0fc0SAlexander Usyskin  */
me_intr_disable(struct mei_device * dev,u32 hcsr)295a2eb0fc0SAlexander Usyskin static inline void me_intr_disable(struct mei_device *dev, u32 hcsr)
296a2eb0fc0SAlexander Usyskin {
297a2eb0fc0SAlexander Usyskin 	hcsr &= ~H_CSR_IE_MASK;
298a2eb0fc0SAlexander Usyskin 	mei_hcsr_set(dev, hcsr);
299a2eb0fc0SAlexander Usyskin }
300a2eb0fc0SAlexander Usyskin 
301a2eb0fc0SAlexander Usyskin /**
3023e917975SAlexander Usyskin  * me_intr_clear - clear and stop interrupts
303a2eb0fc0SAlexander Usyskin  *
304a2eb0fc0SAlexander Usyskin  * @dev: the device structure
305a2eb0fc0SAlexander Usyskin  * @hcsr: supplied hcsr register value
306a2eb0fc0SAlexander Usyskin  */
me_intr_clear(struct mei_device * dev,u32 hcsr)307a2eb0fc0SAlexander Usyskin static inline void me_intr_clear(struct mei_device *dev, u32 hcsr)
308a2eb0fc0SAlexander Usyskin {
309a2eb0fc0SAlexander Usyskin 	if (me_intr_src(hcsr))
310a2eb0fc0SAlexander Usyskin 		mei_hcsr_write(dev, hcsr);
311a2eb0fc0SAlexander Usyskin }
312a2eb0fc0SAlexander Usyskin 
3139dc64d6aSTomas Winkler /**
314ce23139cSAlexander Usyskin  * mei_me_intr_clear - clear and stop interrupts
3159dc64d6aSTomas Winkler  *
3169dc64d6aSTomas Winkler  * @dev: the device structure
3179dc64d6aSTomas Winkler  */
mei_me_intr_clear(struct mei_device * dev)318827eef51STomas Winkler static void mei_me_intr_clear(struct mei_device *dev)
3199dc64d6aSTomas Winkler {
320381a58c7STomas Winkler 	u32 hcsr = mei_hcsr_read(dev);
32192db1555STomas Winkler 
322a2eb0fc0SAlexander Usyskin 	me_intr_clear(dev, hcsr);
3239dc64d6aSTomas Winkler }
3249dc64d6aSTomas Winkler /**
325827eef51STomas Winkler  * mei_me_intr_enable - enables mei device interrupts
3269dc64d6aSTomas Winkler  *
3279dc64d6aSTomas Winkler  * @dev: the device structure
3289dc64d6aSTomas Winkler  */
mei_me_intr_enable(struct mei_device * dev)329827eef51STomas Winkler static void mei_me_intr_enable(struct mei_device *dev)
3309dc64d6aSTomas Winkler {
3315b063995STomas Winkler 	u32 hcsr;
33292db1555STomas Winkler 
3335b063995STomas Winkler 	if (mei_me_hw_use_polling(to_me_hw(dev)))
3345b063995STomas Winkler 		return;
3355b063995STomas Winkler 
3365b063995STomas Winkler 	hcsr = mei_hcsr_read(dev) | H_CSR_IE_MASK;
337381a58c7STomas Winkler 	mei_hcsr_set(dev, hcsr);
3389dc64d6aSTomas Winkler }
3399dc64d6aSTomas Winkler 
3409dc64d6aSTomas Winkler /**
341ce23139cSAlexander Usyskin  * mei_me_intr_disable - disables mei device interrupts
3429dc64d6aSTomas Winkler  *
3439dc64d6aSTomas Winkler  * @dev: the device structure
3449dc64d6aSTomas Winkler  */
mei_me_intr_disable(struct mei_device * dev)345827eef51STomas Winkler static void mei_me_intr_disable(struct mei_device *dev)
3469dc64d6aSTomas Winkler {
347381a58c7STomas Winkler 	u32 hcsr = mei_hcsr_read(dev);
34892db1555STomas Winkler 
349a2eb0fc0SAlexander Usyskin 	me_intr_disable(dev, hcsr);
3509dc64d6aSTomas Winkler }
3519dc64d6aSTomas Winkler 
352adfba322STomas Winkler /**
3534a8efd4aSTomas Winkler  * mei_me_synchronize_irq - wait for pending IRQ handlers
3544a8efd4aSTomas Winkler  *
3554a8efd4aSTomas Winkler  * @dev: the device structure
3564a8efd4aSTomas Winkler  */
mei_me_synchronize_irq(struct mei_device * dev)3574a8efd4aSTomas Winkler static void mei_me_synchronize_irq(struct mei_device *dev)
3584a8efd4aSTomas Winkler {
359261b3e1fSAlexander Usyskin 	struct mei_me_hw *hw = to_me_hw(dev);
3604a8efd4aSTomas Winkler 
3615b063995STomas Winkler 	if (mei_me_hw_use_polling(hw))
3625b063995STomas Winkler 		return;
3635b063995STomas Winkler 
364261b3e1fSAlexander Usyskin 	synchronize_irq(hw->irq);
3654a8efd4aSTomas Winkler }
3664a8efd4aSTomas Winkler 
3674a8efd4aSTomas Winkler /**
36868f8ea18STomas Winkler  * mei_me_hw_reset_release - release device from the reset
36968f8ea18STomas Winkler  *
37068f8ea18STomas Winkler  * @dev: the device structure
37168f8ea18STomas Winkler  */
mei_me_hw_reset_release(struct mei_device * dev)37268f8ea18STomas Winkler static void mei_me_hw_reset_release(struct mei_device *dev)
37368f8ea18STomas Winkler {
374381a58c7STomas Winkler 	u32 hcsr = mei_hcsr_read(dev);
37568f8ea18STomas Winkler 
37668f8ea18STomas Winkler 	hcsr |= H_IG;
37768f8ea18STomas Winkler 	hcsr &= ~H_RST;
378381a58c7STomas Winkler 	mei_hcsr_set(dev, hcsr);
37968f8ea18STomas Winkler }
380adfba322STomas Winkler 
381115ba28cSTomas Winkler /**
382827eef51STomas Winkler  * mei_me_host_set_ready - enable device
383115ba28cSTomas Winkler  *
384ce23139cSAlexander Usyskin  * @dev: mei device
385115ba28cSTomas Winkler  */
mei_me_host_set_ready(struct mei_device * dev)386827eef51STomas Winkler static void mei_me_host_set_ready(struct mei_device *dev)
387115ba28cSTomas Winkler {
388381a58c7STomas Winkler 	u32 hcsr = mei_hcsr_read(dev);
38992db1555STomas Winkler 
3905b063995STomas Winkler 	if (!mei_me_hw_use_polling(to_me_hw(dev)))
3915b063995STomas Winkler 		hcsr |= H_CSR_IE_MASK;
3925b063995STomas Winkler 
3935b063995STomas Winkler 	hcsr |=  H_IG | H_RDY;
394381a58c7STomas Winkler 	mei_hcsr_set(dev, hcsr);
395115ba28cSTomas Winkler }
396ce23139cSAlexander Usyskin 
397115ba28cSTomas Winkler /**
398827eef51STomas Winkler  * mei_me_host_is_ready - check whether the host has turned ready
399115ba28cSTomas Winkler  *
400a8605ea2SAlexander Usyskin  * @dev: mei device
401a8605ea2SAlexander Usyskin  * Return: bool
402115ba28cSTomas Winkler  */
mei_me_host_is_ready(struct mei_device * dev)403827eef51STomas Winkler static bool mei_me_host_is_ready(struct mei_device *dev)
404115ba28cSTomas Winkler {
405381a58c7STomas Winkler 	u32 hcsr = mei_hcsr_read(dev);
40692db1555STomas Winkler 
40718caeb70STomas Winkler 	return (hcsr & H_RDY) == H_RDY;
408115ba28cSTomas Winkler }
409115ba28cSTomas Winkler 
410115ba28cSTomas Winkler /**
411827eef51STomas Winkler  * mei_me_hw_is_ready - check whether the me(hw) has turned ready
412115ba28cSTomas Winkler  *
413a8605ea2SAlexander Usyskin  * @dev: mei device
414a8605ea2SAlexander Usyskin  * Return: bool
415115ba28cSTomas Winkler  */
mei_me_hw_is_ready(struct mei_device * dev)416827eef51STomas Winkler static bool mei_me_hw_is_ready(struct mei_device *dev)
417115ba28cSTomas Winkler {
418381a58c7STomas Winkler 	u32 mecsr = mei_me_mecsr_read(dev);
41992db1555STomas Winkler 
42018caeb70STomas Winkler 	return (mecsr & ME_RDY_HRA) == ME_RDY_HRA;
421115ba28cSTomas Winkler }
4229dc64d6aSTomas Winkler 
423ce23139cSAlexander Usyskin /**
42447f60a01SAlexander Usyskin  * mei_me_hw_is_resetting - check whether the me(hw) is in reset
42547f60a01SAlexander Usyskin  *
42647f60a01SAlexander Usyskin  * @dev: mei device
42747f60a01SAlexander Usyskin  * Return: bool
42847f60a01SAlexander Usyskin  */
mei_me_hw_is_resetting(struct mei_device * dev)42947f60a01SAlexander Usyskin static bool mei_me_hw_is_resetting(struct mei_device *dev)
43047f60a01SAlexander Usyskin {
43147f60a01SAlexander Usyskin 	u32 mecsr = mei_me_mecsr_read(dev);
43247f60a01SAlexander Usyskin 
43347f60a01SAlexander Usyskin 	return (mecsr & ME_RST_HRA) == ME_RST_HRA;
43447f60a01SAlexander Usyskin }
43547f60a01SAlexander Usyskin 
43647f60a01SAlexander Usyskin /**
437342e4c7eSTomas Winkler  * mei_gsc_pxp_check - check for gsc firmware entering pxp mode
438342e4c7eSTomas Winkler  *
439342e4c7eSTomas Winkler  * @dev: the device structure
440342e4c7eSTomas Winkler  */
mei_gsc_pxp_check(struct mei_device * dev)441342e4c7eSTomas Winkler static void mei_gsc_pxp_check(struct mei_device *dev)
442342e4c7eSTomas Winkler {
443342e4c7eSTomas Winkler 	struct mei_me_hw *hw = to_me_hw(dev);
444342e4c7eSTomas Winkler 	u32 fwsts5 = 0;
445342e4c7eSTomas Winkler 
446*34a674e9SVitaly Lubart 	if (!kind_is_gsc(dev) && !kind_is_gscfi(dev))
447342e4c7eSTomas Winkler 		return;
448342e4c7eSTomas Winkler 
449342e4c7eSTomas Winkler 	hw->read_fws(dev, PCI_CFG_HFS_5, &fwsts5);
450342e4c7eSTomas Winkler 	trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HFS_5", PCI_CFG_HFS_5, fwsts5);
451*34a674e9SVitaly Lubart 
452*34a674e9SVitaly Lubart 	if ((fwsts5 & GSC_CFG_HFS_5_BOOT_TYPE_MSK) == GSC_CFG_HFS_5_BOOT_TYPE_PXP) {
453*34a674e9SVitaly Lubart 		if (dev->gsc_reset_to_pxp == MEI_DEV_RESET_TO_PXP_DEFAULT)
454*34a674e9SVitaly Lubart 			dev->gsc_reset_to_pxp = MEI_DEV_RESET_TO_PXP_PERFORMED;
455*34a674e9SVitaly Lubart 	} else {
456*34a674e9SVitaly Lubart 		dev->gsc_reset_to_pxp = MEI_DEV_RESET_TO_PXP_DEFAULT;
457*34a674e9SVitaly Lubart 	}
458*34a674e9SVitaly Lubart 
459*34a674e9SVitaly Lubart 	if (dev->pxp_mode == MEI_DEV_PXP_DEFAULT)
460*34a674e9SVitaly Lubart 		return;
461*34a674e9SVitaly Lubart 
462342e4c7eSTomas Winkler 	if ((fwsts5 & GSC_CFG_HFS_5_BOOT_TYPE_MSK) == GSC_CFG_HFS_5_BOOT_TYPE_PXP) {
463342e4c7eSTomas Winkler 		dev_dbg(dev->dev, "pxp mode is ready 0x%08x\n", fwsts5);
464342e4c7eSTomas Winkler 		dev->pxp_mode = MEI_DEV_PXP_READY;
465342e4c7eSTomas Winkler 	} else {
466342e4c7eSTomas Winkler 		dev_dbg(dev->dev, "pxp mode is not ready 0x%08x\n", fwsts5);
467342e4c7eSTomas Winkler 	}
468342e4c7eSTomas Winkler }
469342e4c7eSTomas Winkler 
470342e4c7eSTomas Winkler /**
471ce23139cSAlexander Usyskin  * mei_me_hw_ready_wait - wait until the me(hw) has turned ready
472ce23139cSAlexander Usyskin  *  or timeout is reached
473ce23139cSAlexander Usyskin  *
474ce23139cSAlexander Usyskin  * @dev: mei device
475ce23139cSAlexander Usyskin  * Return: 0 on success, error otherwise
476ce23139cSAlexander Usyskin  */
mei_me_hw_ready_wait(struct mei_device * dev)477aafae7ecSTomas Winkler static int mei_me_hw_ready_wait(struct mei_device *dev)
478aafae7ecSTomas Winkler {
479aafae7ecSTomas Winkler 	mutex_unlock(&dev->device_lock);
4802c2b93ecSAlexander Usyskin 	wait_event_timeout(dev->wait_hw_ready,
481dab9bf41STomas Winkler 			dev->recvd_hw_ready,
48295953618SAlexander Usyskin 			dev->timeouts.hw_ready);
483aafae7ecSTomas Winkler 	mutex_lock(&dev->device_lock);
4842c2b93ecSAlexander Usyskin 	if (!dev->recvd_hw_ready) {
4852bf94cabSTomas Winkler 		dev_err(dev->dev, "wait hw ready failed\n");
4862c2b93ecSAlexander Usyskin 		return -ETIME;
487aafae7ecSTomas Winkler 	}
488aafae7ecSTomas Winkler 
489342e4c7eSTomas Winkler 	mei_gsc_pxp_check(dev);
490342e4c7eSTomas Winkler 
491663b7ee9SAlexander Usyskin 	mei_me_hw_reset_release(dev);
492aafae7ecSTomas Winkler 	dev->recvd_hw_ready = false;
493aafae7ecSTomas Winkler 	return 0;
494aafae7ecSTomas Winkler }
495aafae7ecSTomas Winkler 
496ce23139cSAlexander Usyskin /**
497*34a674e9SVitaly Lubart  * mei_me_check_fw_reset - check for the firmware reset error and exception conditions
498*34a674e9SVitaly Lubart  *
499*34a674e9SVitaly Lubart  * @dev: mei device
500*34a674e9SVitaly Lubart  */
mei_me_check_fw_reset(struct mei_device * dev)501*34a674e9SVitaly Lubart static void mei_me_check_fw_reset(struct mei_device *dev)
502*34a674e9SVitaly Lubart {
503*34a674e9SVitaly Lubart 	struct mei_fw_status fw_status;
504*34a674e9SVitaly Lubart 	char fw_sts_str[MEI_FW_STATUS_STR_SZ] = {0};
505*34a674e9SVitaly Lubart 	int ret;
506*34a674e9SVitaly Lubart 	u32 fw_pm_event = 0;
507*34a674e9SVitaly Lubart 
508*34a674e9SVitaly Lubart 	if (!dev->saved_fw_status_flag)
509*34a674e9SVitaly Lubart 		goto end;
510*34a674e9SVitaly Lubart 
511*34a674e9SVitaly Lubart 	if (dev->gsc_reset_to_pxp == MEI_DEV_RESET_TO_PXP_PERFORMED) {
512*34a674e9SVitaly Lubart 		ret = mei_fw_status(dev, &fw_status);
513*34a674e9SVitaly Lubart 		if (!ret) {
514*34a674e9SVitaly Lubart 			fw_pm_event = fw_status.status[1] & PCI_CFG_HFS_2_PM_EVENT_MASK;
515*34a674e9SVitaly Lubart 			if (fw_pm_event != PCI_CFG_HFS_2_PM_CMOFF_TO_CMX_ERROR &&
516*34a674e9SVitaly Lubart 			    fw_pm_event != PCI_CFG_HFS_2_PM_CM_RESET_ERROR)
517*34a674e9SVitaly Lubart 				goto end;
518*34a674e9SVitaly Lubart 		} else {
519*34a674e9SVitaly Lubart 			dev_err(dev->dev, "failed to read firmware status: %d\n", ret);
520*34a674e9SVitaly Lubart 		}
521*34a674e9SVitaly Lubart 	}
522*34a674e9SVitaly Lubart 
523*34a674e9SVitaly Lubart 	mei_fw_status2str(&dev->saved_fw_status, fw_sts_str, sizeof(fw_sts_str));
524*34a674e9SVitaly Lubart 	dev_warn(dev->dev, "unexpected reset: fw_pm_event = 0x%x, dev_state = %u fw status = %s\n",
525*34a674e9SVitaly Lubart 		 fw_pm_event, dev->saved_dev_state, fw_sts_str);
526*34a674e9SVitaly Lubart 
527*34a674e9SVitaly Lubart end:
528*34a674e9SVitaly Lubart 	if (dev->gsc_reset_to_pxp == MEI_DEV_RESET_TO_PXP_PERFORMED)
529*34a674e9SVitaly Lubart 		dev->gsc_reset_to_pxp = MEI_DEV_RESET_TO_PXP_DONE;
530*34a674e9SVitaly Lubart 	dev->saved_fw_status_flag = false;
531*34a674e9SVitaly Lubart }
532*34a674e9SVitaly Lubart 
533*34a674e9SVitaly Lubart /**
534ce23139cSAlexander Usyskin  * mei_me_hw_start - hw start routine
535ce23139cSAlexander Usyskin  *
536ce23139cSAlexander Usyskin  * @dev: mei device
537ce23139cSAlexander Usyskin  * Return: 0 on success, error otherwise
538ce23139cSAlexander Usyskin  */
mei_me_hw_start(struct mei_device * dev)539aafae7ecSTomas Winkler static int mei_me_hw_start(struct mei_device *dev)
540aafae7ecSTomas Winkler {
541aafae7ecSTomas Winkler 	int ret = mei_me_hw_ready_wait(dev);
54292db1555STomas Winkler 
543*34a674e9SVitaly Lubart 	if (kind_is_gsc(dev) || kind_is_gscfi(dev))
544*34a674e9SVitaly Lubart 		mei_me_check_fw_reset(dev);
545aafae7ecSTomas Winkler 	if (ret)
546aafae7ecSTomas Winkler 		return ret;
5472bf94cabSTomas Winkler 	dev_dbg(dev->dev, "hw is ready\n");
548aafae7ecSTomas Winkler 
549aafae7ecSTomas Winkler 	mei_me_host_set_ready(dev);
550aafae7ecSTomas Winkler 	return ret;
551aafae7ecSTomas Winkler }
552aafae7ecSTomas Winkler 
553aafae7ecSTomas Winkler 
5549dc64d6aSTomas Winkler /**
5559dc64d6aSTomas Winkler  * mei_hbuf_filled_slots - gets number of device filled buffer slots
5569dc64d6aSTomas Winkler  *
5577353f85cSSedat Dilek  * @dev: the device structure
5589dc64d6aSTomas Winkler  *
559a8605ea2SAlexander Usyskin  * Return: number of filled slots
5609dc64d6aSTomas Winkler  */
mei_hbuf_filled_slots(struct mei_device * dev)5619dc64d6aSTomas Winkler static unsigned char mei_hbuf_filled_slots(struct mei_device *dev)
5629dc64d6aSTomas Winkler {
56318caeb70STomas Winkler 	u32 hcsr;
5649dc64d6aSTomas Winkler 	char read_ptr, write_ptr;
5659dc64d6aSTomas Winkler 
566381a58c7STomas Winkler 	hcsr = mei_hcsr_read(dev);
5679dc64d6aSTomas Winkler 
56818caeb70STomas Winkler 	read_ptr = (char) ((hcsr & H_CBRP) >> 8);
56918caeb70STomas Winkler 	write_ptr = (char) ((hcsr & H_CBWP) >> 16);
5709dc64d6aSTomas Winkler 
5719dc64d6aSTomas Winkler 	return (unsigned char) (write_ptr - read_ptr);
5729dc64d6aSTomas Winkler }
5739dc64d6aSTomas Winkler 
5749dc64d6aSTomas Winkler /**
575393b148fSMasanari Iida  * mei_me_hbuf_is_empty - checks if host buffer is empty.
5769dc64d6aSTomas Winkler  *
5779dc64d6aSTomas Winkler  * @dev: the device structure
5789dc64d6aSTomas Winkler  *
579a8605ea2SAlexander Usyskin  * Return: true if empty, false - otherwise.
5809dc64d6aSTomas Winkler  */
mei_me_hbuf_is_empty(struct mei_device * dev)581827eef51STomas Winkler static bool mei_me_hbuf_is_empty(struct mei_device *dev)
5829dc64d6aSTomas Winkler {
5839dc64d6aSTomas Winkler 	return mei_hbuf_filled_slots(dev) == 0;
5849dc64d6aSTomas Winkler }
5859dc64d6aSTomas Winkler 
5869dc64d6aSTomas Winkler /**
587827eef51STomas Winkler  * mei_me_hbuf_empty_slots - counts write empty slots.
5889dc64d6aSTomas Winkler  *
5899dc64d6aSTomas Winkler  * @dev: the device structure
5909dc64d6aSTomas Winkler  *
591a8605ea2SAlexander Usyskin  * Return: -EOVERFLOW if overflow, otherwise empty slots count
5929dc64d6aSTomas Winkler  */
mei_me_hbuf_empty_slots(struct mei_device * dev)593827eef51STomas Winkler static int mei_me_hbuf_empty_slots(struct mei_device *dev)
5949dc64d6aSTomas Winkler {
5958c8d964cSTomas Winkler 	struct mei_me_hw *hw = to_me_hw(dev);
5969dc64d6aSTomas Winkler 	unsigned char filled_slots, empty_slots;
5979dc64d6aSTomas Winkler 
5989dc64d6aSTomas Winkler 	filled_slots = mei_hbuf_filled_slots(dev);
5998c8d964cSTomas Winkler 	empty_slots = hw->hbuf_depth - filled_slots;
6009dc64d6aSTomas Winkler 
6019dc64d6aSTomas Winkler 	/* check for overflow */
6028c8d964cSTomas Winkler 	if (filled_slots > hw->hbuf_depth)
6039dc64d6aSTomas Winkler 		return -EOVERFLOW;
6049dc64d6aSTomas Winkler 
6059dc64d6aSTomas Winkler 	return empty_slots;
6069dc64d6aSTomas Winkler }
6079dc64d6aSTomas Winkler 
608ce23139cSAlexander Usyskin /**
6098c8d964cSTomas Winkler  * mei_me_hbuf_depth - returns depth of the hw buffer.
610ce23139cSAlexander Usyskin  *
611ce23139cSAlexander Usyskin  * @dev: the device structure
612ce23139cSAlexander Usyskin  *
6138c8d964cSTomas Winkler  * Return: size of hw buffer in slots
614ce23139cSAlexander Usyskin  */
mei_me_hbuf_depth(const struct mei_device * dev)6158c8d964cSTomas Winkler static u32 mei_me_hbuf_depth(const struct mei_device *dev)
616827eef51STomas Winkler {
6178c8d964cSTomas Winkler 	struct mei_me_hw *hw = to_me_hw(dev);
6188c8d964cSTomas Winkler 
6198c8d964cSTomas Winkler 	return hw->hbuf_depth;
620827eef51STomas Winkler }
621827eef51STomas Winkler 
6229dc64d6aSTomas Winkler /**
6234b9960d0STomas Winkler  * mei_me_hbuf_write - writes a message to host hw buffer.
6249dc64d6aSTomas Winkler  *
6259dc64d6aSTomas Winkler  * @dev: the device structure
62698e70866STomas Winkler  * @hdr: header of message
62798e70866STomas Winkler  * @hdr_len: header length in bytes: must be multiplication of a slot (4bytes)
62898e70866STomas Winkler  * @data: payload
62998e70866STomas Winkler  * @data_len: payload length in bytes
6309dc64d6aSTomas Winkler  *
63198e70866STomas Winkler  * Return: 0 if success, < 0 - otherwise.
6329dc64d6aSTomas Winkler  */
mei_me_hbuf_write(struct mei_device * dev,const void * hdr,size_t hdr_len,const void * data,size_t data_len)6334b9960d0STomas Winkler static int mei_me_hbuf_write(struct mei_device *dev,
63498e70866STomas Winkler 			     const void *hdr, size_t hdr_len,
63598e70866STomas Winkler 			     const void *data, size_t data_len)
6369dc64d6aSTomas Winkler {
637c8c8d080STomas Winkler 	unsigned long rem;
63844c98df0STomas Winkler 	unsigned long i;
63998e70866STomas Winkler 	const u32 *reg_buf;
640c8c8d080STomas Winkler 	u32 dw_cnt;
6419dc64d6aSTomas Winkler 	int empty_slots;
6429dc64d6aSTomas Winkler 
6434ed1cc99STomas Winkler 	if (WARN_ON(!hdr || hdr_len & 0x3))
64498e70866STomas Winkler 		return -EINVAL;
64598e70866STomas Winkler 
6464ed1cc99STomas Winkler 	if (!data && data_len) {
6474ed1cc99STomas Winkler 		dev_err(dev->dev, "wrong parameters null data with data_len = %zu\n", data_len);
6484ed1cc99STomas Winkler 		return -EINVAL;
6494ed1cc99STomas Winkler 	}
6504ed1cc99STomas Winkler 
65198e70866STomas Winkler 	dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM((struct mei_msg_hdr *)hdr));
6529dc64d6aSTomas Winkler 
6539dc64d6aSTomas Winkler 	empty_slots = mei_hbuf_empty_slots(dev);
654d618072dSJustin Stitt 	dev_dbg(dev->dev, "empty slots = %d.\n", empty_slots);
6559dc64d6aSTomas Winkler 
656de877437STomas Winkler 	if (empty_slots < 0)
657de877437STomas Winkler 		return -EOVERFLOW;
658de877437STomas Winkler 
65998e70866STomas Winkler 	dw_cnt = mei_data2slots(hdr_len + data_len);
660de877437STomas Winkler 	if (dw_cnt > (u32)empty_slots)
6619d098192STomas Winkler 		return -EMSGSIZE;
6629dc64d6aSTomas Winkler 
66398e70866STomas Winkler 	reg_buf = hdr;
66498e70866STomas Winkler 	for (i = 0; i < hdr_len / MEI_SLOT_SIZE; i++)
665381a58c7STomas Winkler 		mei_me_hcbww_write(dev, reg_buf[i]);
6669dc64d6aSTomas Winkler 
66798e70866STomas Winkler 	reg_buf = data;
66898e70866STomas Winkler 	for (i = 0; i < data_len / MEI_SLOT_SIZE; i++)
66998e70866STomas Winkler 		mei_me_hcbww_write(dev, reg_buf[i]);
67098e70866STomas Winkler 
67198e70866STomas Winkler 	rem = data_len & 0x3;
6729dc64d6aSTomas Winkler 	if (rem > 0) {
6739dc64d6aSTomas Winkler 		u32 reg = 0;
67492db1555STomas Winkler 
67598e70866STomas Winkler 		memcpy(&reg, (const u8 *)data + data_len - rem, rem);
676381a58c7STomas Winkler 		mei_me_hcbww_write(dev, reg);
6779dc64d6aSTomas Winkler 	}
6789dc64d6aSTomas Winkler 
6799c7daa61SAlexander Usyskin 	mei_hcsr_set_hig(dev);
680827eef51STomas Winkler 	if (!mei_me_hw_is_ready(dev))
6819dc64d6aSTomas Winkler 		return -EIO;
6829dc64d6aSTomas Winkler 
6839dc64d6aSTomas Winkler 	return 0;
6849dc64d6aSTomas Winkler }
6859dc64d6aSTomas Winkler 
6869dc64d6aSTomas Winkler /**
687827eef51STomas Winkler  * mei_me_count_full_read_slots - counts read full slots.
6889dc64d6aSTomas Winkler  *
6899dc64d6aSTomas Winkler  * @dev: the device structure
6909dc64d6aSTomas Winkler  *
691a8605ea2SAlexander Usyskin  * Return: -EOVERFLOW if overflow, otherwise filled slots count
6929dc64d6aSTomas Winkler  */
mei_me_count_full_read_slots(struct mei_device * dev)693827eef51STomas Winkler static int mei_me_count_full_read_slots(struct mei_device *dev)
6949dc64d6aSTomas Winkler {
69518caeb70STomas Winkler 	u32 me_csr;
6969dc64d6aSTomas Winkler 	char read_ptr, write_ptr;
6979dc64d6aSTomas Winkler 	unsigned char buffer_depth, filled_slots;
6989dc64d6aSTomas Winkler 
699381a58c7STomas Winkler 	me_csr = mei_me_mecsr_read(dev);
70018caeb70STomas Winkler 	buffer_depth = (unsigned char)((me_csr & ME_CBD_HRA) >> 24);
70118caeb70STomas Winkler 	read_ptr = (char) ((me_csr & ME_CBRP_HRA) >> 8);
70218caeb70STomas Winkler 	write_ptr = (char) ((me_csr & ME_CBWP_HRA) >> 16);
7039dc64d6aSTomas Winkler 	filled_slots = (unsigned char) (write_ptr - read_ptr);
7049dc64d6aSTomas Winkler 
7059dc64d6aSTomas Winkler 	/* check for overflow */
7069dc64d6aSTomas Winkler 	if (filled_slots > buffer_depth)
7079dc64d6aSTomas Winkler 		return -EOVERFLOW;
7089dc64d6aSTomas Winkler 
7092bf94cabSTomas Winkler 	dev_dbg(dev->dev, "filled_slots =%08x\n", filled_slots);
7109dc64d6aSTomas Winkler 	return (int)filled_slots;
7119dc64d6aSTomas Winkler }
7129dc64d6aSTomas Winkler 
7139dc64d6aSTomas Winkler /**
714827eef51STomas Winkler  * mei_me_read_slots - reads a message from mei device.
7159dc64d6aSTomas Winkler  *
7169dc64d6aSTomas Winkler  * @dev: the device structure
7179dc64d6aSTomas Winkler  * @buffer: message buffer will be written
7189dc64d6aSTomas Winkler  * @buffer_length: message size will be read
719ce23139cSAlexander Usyskin  *
720ce23139cSAlexander Usyskin  * Return: always 0
7219dc64d6aSTomas Winkler  */
mei_me_read_slots(struct mei_device * dev,unsigned char * buffer,unsigned long buffer_length)722827eef51STomas Winkler static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer,
7239dc64d6aSTomas Winkler 			     unsigned long buffer_length)
7249dc64d6aSTomas Winkler {
7259dc64d6aSTomas Winkler 	u32 *reg_buf = (u32 *)buffer;
7269dc64d6aSTomas Winkler 
7279fc5f0f8STomas Winkler 	for (; buffer_length >= MEI_SLOT_SIZE; buffer_length -= MEI_SLOT_SIZE)
728827eef51STomas Winkler 		*reg_buf++ = mei_me_mecbrw_read(dev);
7299dc64d6aSTomas Winkler 
7309dc64d6aSTomas Winkler 	if (buffer_length > 0) {
731827eef51STomas Winkler 		u32 reg = mei_me_mecbrw_read(dev);
73292db1555STomas Winkler 
7339dc64d6aSTomas Winkler 		memcpy(reg_buf, &reg, buffer_length);
7349dc64d6aSTomas Winkler 	}
7359dc64d6aSTomas Winkler 
7369c7daa61SAlexander Usyskin 	mei_hcsr_set_hig(dev);
737827eef51STomas Winkler 	return 0;
7389dc64d6aSTomas Winkler }
7399dc64d6aSTomas Winkler 
74006ecd645STomas Winkler /**
7412d1995fcSAlexander Usyskin  * mei_me_pg_set - write pg enter register
742b16c3571STomas Winkler  *
743b16c3571STomas Winkler  * @dev: the device structure
744b16c3571STomas Winkler  */
mei_me_pg_set(struct mei_device * dev)7452d1995fcSAlexander Usyskin static void mei_me_pg_set(struct mei_device *dev)
746b16c3571STomas Winkler {
747b16c3571STomas Winkler 	struct mei_me_hw *hw = to_me_hw(dev);
748a0a927d0STomas Winkler 	u32 reg;
749a0a927d0STomas Winkler 
750a0a927d0STomas Winkler 	reg = mei_me_reg_read(hw, H_HPG_CSR);
751a0a927d0STomas Winkler 	trace_mei_reg_read(dev->dev, "H_HPG_CSR", H_HPG_CSR, reg);
75292db1555STomas Winkler 
753b16c3571STomas Winkler 	reg |= H_HPG_CSR_PGI;
754a0a927d0STomas Winkler 
755a0a927d0STomas Winkler 	trace_mei_reg_write(dev->dev, "H_HPG_CSR", H_HPG_CSR, reg);
756b16c3571STomas Winkler 	mei_me_reg_write(hw, H_HPG_CSR, reg);
757b16c3571STomas Winkler }
758b16c3571STomas Winkler 
759b16c3571STomas Winkler /**
7602d1995fcSAlexander Usyskin  * mei_me_pg_unset - write pg exit register
761b16c3571STomas Winkler  *
762b16c3571STomas Winkler  * @dev: the device structure
763b16c3571STomas Winkler  */
mei_me_pg_unset(struct mei_device * dev)7642d1995fcSAlexander Usyskin static void mei_me_pg_unset(struct mei_device *dev)
765b16c3571STomas Winkler {
766b16c3571STomas Winkler 	struct mei_me_hw *hw = to_me_hw(dev);
767a0a927d0STomas Winkler 	u32 reg;
768a0a927d0STomas Winkler 
769a0a927d0STomas Winkler 	reg = mei_me_reg_read(hw, H_HPG_CSR);
770a0a927d0STomas Winkler 	trace_mei_reg_read(dev->dev, "H_HPG_CSR", H_HPG_CSR, reg);
771b16c3571STomas Winkler 
772b16c3571STomas Winkler 	WARN(!(reg & H_HPG_CSR_PGI), "PGI is not set\n");
773b16c3571STomas Winkler 
774b16c3571STomas Winkler 	reg |= H_HPG_CSR_PGIHEXR;
775a0a927d0STomas Winkler 
776a0a927d0STomas Winkler 	trace_mei_reg_write(dev->dev, "H_HPG_CSR", H_HPG_CSR, reg);
777b16c3571STomas Winkler 	mei_me_reg_write(hw, H_HPG_CSR, reg);
778b16c3571STomas Winkler }
779b16c3571STomas Winkler 
780b16c3571STomas Winkler /**
781859ef2ffSAlexander Usyskin  * mei_me_pg_legacy_enter_sync - perform legacy pg entry procedure
782ba9cdd0eSTomas Winkler  *
783ba9cdd0eSTomas Winkler  * @dev: the device structure
784ba9cdd0eSTomas Winkler  *
785a8605ea2SAlexander Usyskin  * Return: 0 on success an error code otherwise
786ba9cdd0eSTomas Winkler  */
mei_me_pg_legacy_enter_sync(struct mei_device * dev)787859ef2ffSAlexander Usyskin static int mei_me_pg_legacy_enter_sync(struct mei_device *dev)
788ba9cdd0eSTomas Winkler {
789ba9cdd0eSTomas Winkler 	struct mei_me_hw *hw = to_me_hw(dev);
790ba9cdd0eSTomas Winkler 	int ret;
791ba9cdd0eSTomas Winkler 
792ba9cdd0eSTomas Winkler 	dev->pg_event = MEI_PG_EVENT_WAIT;
793ba9cdd0eSTomas Winkler 
794ba9cdd0eSTomas Winkler 	ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_ENTRY_REQ_CMD);
795ba9cdd0eSTomas Winkler 	if (ret)
796ba9cdd0eSTomas Winkler 		return ret;
797ba9cdd0eSTomas Winkler 
798ba9cdd0eSTomas Winkler 	mutex_unlock(&dev->device_lock);
799ba9cdd0eSTomas Winkler 	wait_event_timeout(dev->wait_pg,
80095953618SAlexander Usyskin 		dev->pg_event == MEI_PG_EVENT_RECEIVED,
80195953618SAlexander Usyskin 		dev->timeouts.pgi);
802ba9cdd0eSTomas Winkler 	mutex_lock(&dev->device_lock);
803ba9cdd0eSTomas Winkler 
804ba9cdd0eSTomas Winkler 	if (dev->pg_event == MEI_PG_EVENT_RECEIVED) {
8052d1995fcSAlexander Usyskin 		mei_me_pg_set(dev);
806ba9cdd0eSTomas Winkler 		ret = 0;
807ba9cdd0eSTomas Winkler 	} else {
808ba9cdd0eSTomas Winkler 		ret = -ETIME;
809ba9cdd0eSTomas Winkler 	}
810ba9cdd0eSTomas Winkler 
811ba9cdd0eSTomas Winkler 	dev->pg_event = MEI_PG_EVENT_IDLE;
812ba9cdd0eSTomas Winkler 	hw->pg_state = MEI_PG_ON;
813ba9cdd0eSTomas Winkler 
814ba9cdd0eSTomas Winkler 	return ret;
815ba9cdd0eSTomas Winkler }
816ba9cdd0eSTomas Winkler 
817ba9cdd0eSTomas Winkler /**
818859ef2ffSAlexander Usyskin  * mei_me_pg_legacy_exit_sync - perform legacy pg exit procedure
819ba9cdd0eSTomas Winkler  *
820ba9cdd0eSTomas Winkler  * @dev: the device structure
821ba9cdd0eSTomas Winkler  *
822a8605ea2SAlexander Usyskin  * Return: 0 on success an error code otherwise
823ba9cdd0eSTomas Winkler  */
mei_me_pg_legacy_exit_sync(struct mei_device * dev)824859ef2ffSAlexander Usyskin static int mei_me_pg_legacy_exit_sync(struct mei_device *dev)
825ba9cdd0eSTomas Winkler {
826ba9cdd0eSTomas Winkler 	struct mei_me_hw *hw = to_me_hw(dev);
827ba9cdd0eSTomas Winkler 	int ret;
828ba9cdd0eSTomas Winkler 
829ba9cdd0eSTomas Winkler 	if (dev->pg_event == MEI_PG_EVENT_RECEIVED)
830ba9cdd0eSTomas Winkler 		goto reply;
831ba9cdd0eSTomas Winkler 
832ba9cdd0eSTomas Winkler 	dev->pg_event = MEI_PG_EVENT_WAIT;
833ba9cdd0eSTomas Winkler 
8342d1995fcSAlexander Usyskin 	mei_me_pg_unset(dev);
835ba9cdd0eSTomas Winkler 
836ba9cdd0eSTomas Winkler 	mutex_unlock(&dev->device_lock);
837ba9cdd0eSTomas Winkler 	wait_event_timeout(dev->wait_pg,
83895953618SAlexander Usyskin 		dev->pg_event == MEI_PG_EVENT_RECEIVED,
83995953618SAlexander Usyskin 		dev->timeouts.pgi);
840ba9cdd0eSTomas Winkler 	mutex_lock(&dev->device_lock);
841ba9cdd0eSTomas Winkler 
842ba9cdd0eSTomas Winkler reply:
8433dc196eaSAlexander Usyskin 	if (dev->pg_event != MEI_PG_EVENT_RECEIVED) {
8443dc196eaSAlexander Usyskin 		ret = -ETIME;
8453dc196eaSAlexander Usyskin 		goto out;
8463dc196eaSAlexander Usyskin 	}
8473dc196eaSAlexander Usyskin 
8483dc196eaSAlexander Usyskin 	dev->pg_event = MEI_PG_EVENT_INTR_WAIT;
849ba9cdd0eSTomas Winkler 	ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_EXIT_RES_CMD);
8503dc196eaSAlexander Usyskin 	if (ret)
8513dc196eaSAlexander Usyskin 		return ret;
8523dc196eaSAlexander Usyskin 
8533dc196eaSAlexander Usyskin 	mutex_unlock(&dev->device_lock);
8543dc196eaSAlexander Usyskin 	wait_event_timeout(dev->wait_pg,
85595953618SAlexander Usyskin 		dev->pg_event == MEI_PG_EVENT_INTR_RECEIVED,
85695953618SAlexander Usyskin 		dev->timeouts.pgi);
8573dc196eaSAlexander Usyskin 	mutex_lock(&dev->device_lock);
8583dc196eaSAlexander Usyskin 
8593dc196eaSAlexander Usyskin 	if (dev->pg_event == MEI_PG_EVENT_INTR_RECEIVED)
8603dc196eaSAlexander Usyskin 		ret = 0;
861ba9cdd0eSTomas Winkler 	else
862ba9cdd0eSTomas Winkler 		ret = -ETIME;
863ba9cdd0eSTomas Winkler 
8643dc196eaSAlexander Usyskin out:
865ba9cdd0eSTomas Winkler 	dev->pg_event = MEI_PG_EVENT_IDLE;
866ba9cdd0eSTomas Winkler 	hw->pg_state = MEI_PG_OFF;
867ba9cdd0eSTomas Winkler 
868ba9cdd0eSTomas Winkler 	return ret;
869ba9cdd0eSTomas Winkler }
870ba9cdd0eSTomas Winkler 
871ba9cdd0eSTomas Winkler /**
8723dc196eaSAlexander Usyskin  * mei_me_pg_in_transition - is device now in pg transition
8733dc196eaSAlexander Usyskin  *
8743dc196eaSAlexander Usyskin  * @dev: the device structure
8753dc196eaSAlexander Usyskin  *
8763dc196eaSAlexander Usyskin  * Return: true if in pg transition, false otherwise
8773dc196eaSAlexander Usyskin  */
mei_me_pg_in_transition(struct mei_device * dev)8783dc196eaSAlexander Usyskin static bool mei_me_pg_in_transition(struct mei_device *dev)
8793dc196eaSAlexander Usyskin {
8803dc196eaSAlexander Usyskin 	return dev->pg_event >= MEI_PG_EVENT_WAIT &&
8813dc196eaSAlexander Usyskin 	       dev->pg_event <= MEI_PG_EVENT_INTR_WAIT;
8823dc196eaSAlexander Usyskin }
8833dc196eaSAlexander Usyskin 
8843dc196eaSAlexander Usyskin /**
885ee7e5afdSTomas Winkler  * mei_me_pg_is_enabled - detect if PG is supported by HW
886ee7e5afdSTomas Winkler  *
887ee7e5afdSTomas Winkler  * @dev: the device structure
888ee7e5afdSTomas Winkler  *
889a8605ea2SAlexander Usyskin  * Return: true is pg supported, false otherwise
890ee7e5afdSTomas Winkler  */
mei_me_pg_is_enabled(struct mei_device * dev)891ee7e5afdSTomas Winkler static bool mei_me_pg_is_enabled(struct mei_device *dev)
892ee7e5afdSTomas Winkler {
893859ef2ffSAlexander Usyskin 	struct mei_me_hw *hw = to_me_hw(dev);
894381a58c7STomas Winkler 	u32 reg = mei_me_mecsr_read(dev);
895ee7e5afdSTomas Winkler 
896859ef2ffSAlexander Usyskin 	if (hw->d0i3_supported)
897859ef2ffSAlexander Usyskin 		return true;
898859ef2ffSAlexander Usyskin 
899ee7e5afdSTomas Winkler 	if ((reg & ME_PGIC_HRA) == 0)
900ee7e5afdSTomas Winkler 		goto notsupported;
901ee7e5afdSTomas Winkler 
902bae1cc7dSTomas Winkler 	if (!dev->hbm_f_pg_supported)
903ee7e5afdSTomas Winkler 		goto notsupported;
904ee7e5afdSTomas Winkler 
905ee7e5afdSTomas Winkler 	return true;
906ee7e5afdSTomas Winkler 
907ee7e5afdSTomas Winkler notsupported:
908859ef2ffSAlexander Usyskin 	dev_dbg(dev->dev, "pg: not supported: d0i3 = %d HGP = %d hbm version %d.%d ?= %d.%d\n",
909859ef2ffSAlexander Usyskin 		hw->d0i3_supported,
910ee7e5afdSTomas Winkler 		!!(reg & ME_PGIC_HRA),
911ee7e5afdSTomas Winkler 		dev->version.major_version,
912ee7e5afdSTomas Winkler 		dev->version.minor_version,
913ee7e5afdSTomas Winkler 		HBM_MAJOR_VERSION_PGI,
914ee7e5afdSTomas Winkler 		HBM_MINOR_VERSION_PGI);
915ee7e5afdSTomas Winkler 
916ee7e5afdSTomas Winkler 	return false;
917ee7e5afdSTomas Winkler }
918ee7e5afdSTomas Winkler 
919ee7e5afdSTomas Winkler /**
920859ef2ffSAlexander Usyskin  * mei_me_d0i3_set - write d0i3 register bit on mei device.
921859ef2ffSAlexander Usyskin  *
922859ef2ffSAlexander Usyskin  * @dev: the device structure
923859ef2ffSAlexander Usyskin  * @intr: ask for interrupt
924859ef2ffSAlexander Usyskin  *
925859ef2ffSAlexander Usyskin  * Return: D0I3C register value
926859ef2ffSAlexander Usyskin  */
mei_me_d0i3_set(struct mei_device * dev,bool intr)927859ef2ffSAlexander Usyskin static u32 mei_me_d0i3_set(struct mei_device *dev, bool intr)
928859ef2ffSAlexander Usyskin {
929859ef2ffSAlexander Usyskin 	u32 reg = mei_me_d0i3c_read(dev);
930859ef2ffSAlexander Usyskin 
931859ef2ffSAlexander Usyskin 	reg |= H_D0I3C_I3;
932859ef2ffSAlexander Usyskin 	if (intr)
933859ef2ffSAlexander Usyskin 		reg |= H_D0I3C_IR;
934859ef2ffSAlexander Usyskin 	else
935859ef2ffSAlexander Usyskin 		reg &= ~H_D0I3C_IR;
936859ef2ffSAlexander Usyskin 	mei_me_d0i3c_write(dev, reg);
937859ef2ffSAlexander Usyskin 	/* read it to ensure HW consistency */
938859ef2ffSAlexander Usyskin 	reg = mei_me_d0i3c_read(dev);
939859ef2ffSAlexander Usyskin 	return reg;
940859ef2ffSAlexander Usyskin }
941859ef2ffSAlexander Usyskin 
942859ef2ffSAlexander Usyskin /**
943859ef2ffSAlexander Usyskin  * mei_me_d0i3_unset - clean d0i3 register bit on mei device.
944859ef2ffSAlexander Usyskin  *
945859ef2ffSAlexander Usyskin  * @dev: the device structure
946859ef2ffSAlexander Usyskin  *
947859ef2ffSAlexander Usyskin  * Return: D0I3C register value
948859ef2ffSAlexander Usyskin  */
mei_me_d0i3_unset(struct mei_device * dev)949859ef2ffSAlexander Usyskin static u32 mei_me_d0i3_unset(struct mei_device *dev)
950859ef2ffSAlexander Usyskin {
951859ef2ffSAlexander Usyskin 	u32 reg = mei_me_d0i3c_read(dev);
952859ef2ffSAlexander Usyskin 
953859ef2ffSAlexander Usyskin 	reg &= ~H_D0I3C_I3;
954859ef2ffSAlexander Usyskin 	reg |= H_D0I3C_IR;
955859ef2ffSAlexander Usyskin 	mei_me_d0i3c_write(dev, reg);
956859ef2ffSAlexander Usyskin 	/* read it to ensure HW consistency */
957859ef2ffSAlexander Usyskin 	reg = mei_me_d0i3c_read(dev);
958859ef2ffSAlexander Usyskin 	return reg;
959859ef2ffSAlexander Usyskin }
960859ef2ffSAlexander Usyskin 
961859ef2ffSAlexander Usyskin /**
962859ef2ffSAlexander Usyskin  * mei_me_d0i3_enter_sync - perform d0i3 entry procedure
963859ef2ffSAlexander Usyskin  *
964859ef2ffSAlexander Usyskin  * @dev: the device structure
965859ef2ffSAlexander Usyskin  *
966859ef2ffSAlexander Usyskin  * Return: 0 on success an error code otherwise
967859ef2ffSAlexander Usyskin  */
mei_me_d0i3_enter_sync(struct mei_device * dev)968859ef2ffSAlexander Usyskin static int mei_me_d0i3_enter_sync(struct mei_device *dev)
969859ef2ffSAlexander Usyskin {
970859ef2ffSAlexander Usyskin 	struct mei_me_hw *hw = to_me_hw(dev);
971859ef2ffSAlexander Usyskin 	int ret;
972859ef2ffSAlexander Usyskin 	u32 reg;
973859ef2ffSAlexander Usyskin 
974859ef2ffSAlexander Usyskin 	reg = mei_me_d0i3c_read(dev);
975859ef2ffSAlexander Usyskin 	if (reg & H_D0I3C_I3) {
976859ef2ffSAlexander Usyskin 		/* we are in d0i3, nothing to do */
977859ef2ffSAlexander Usyskin 		dev_dbg(dev->dev, "d0i3 set not needed\n");
978859ef2ffSAlexander Usyskin 		ret = 0;
979859ef2ffSAlexander Usyskin 		goto on;
980859ef2ffSAlexander Usyskin 	}
981859ef2ffSAlexander Usyskin 
982859ef2ffSAlexander Usyskin 	/* PGI entry procedure */
983859ef2ffSAlexander Usyskin 	dev->pg_event = MEI_PG_EVENT_WAIT;
984859ef2ffSAlexander Usyskin 
985859ef2ffSAlexander Usyskin 	ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_ENTRY_REQ_CMD);
986859ef2ffSAlexander Usyskin 	if (ret)
987859ef2ffSAlexander Usyskin 		/* FIXME: should we reset here? */
988859ef2ffSAlexander Usyskin 		goto out;
989859ef2ffSAlexander Usyskin 
990859ef2ffSAlexander Usyskin 	mutex_unlock(&dev->device_lock);
991859ef2ffSAlexander Usyskin 	wait_event_timeout(dev->wait_pg,
99295953618SAlexander Usyskin 		dev->pg_event == MEI_PG_EVENT_RECEIVED,
99395953618SAlexander Usyskin 		dev->timeouts.pgi);
994859ef2ffSAlexander Usyskin 	mutex_lock(&dev->device_lock);
995859ef2ffSAlexander Usyskin 
996859ef2ffSAlexander Usyskin 	if (dev->pg_event != MEI_PG_EVENT_RECEIVED) {
997859ef2ffSAlexander Usyskin 		ret = -ETIME;
998859ef2ffSAlexander Usyskin 		goto out;
999859ef2ffSAlexander Usyskin 	}
1000859ef2ffSAlexander Usyskin 	/* end PGI entry procedure */
1001859ef2ffSAlexander Usyskin 
1002859ef2ffSAlexander Usyskin 	dev->pg_event = MEI_PG_EVENT_INTR_WAIT;
1003859ef2ffSAlexander Usyskin 
1004859ef2ffSAlexander Usyskin 	reg = mei_me_d0i3_set(dev, true);
1005859ef2ffSAlexander Usyskin 	if (!(reg & H_D0I3C_CIP)) {
1006859ef2ffSAlexander Usyskin 		dev_dbg(dev->dev, "d0i3 enter wait not needed\n");
1007859ef2ffSAlexander Usyskin 		ret = 0;
1008859ef2ffSAlexander Usyskin 		goto on;
1009859ef2ffSAlexander Usyskin 	}
1010859ef2ffSAlexander Usyskin 
1011859ef2ffSAlexander Usyskin 	mutex_unlock(&dev->device_lock);
1012859ef2ffSAlexander Usyskin 	wait_event_timeout(dev->wait_pg,
101395953618SAlexander Usyskin 		dev->pg_event == MEI_PG_EVENT_INTR_RECEIVED,
101495953618SAlexander Usyskin 		dev->timeouts.d0i3);
1015859ef2ffSAlexander Usyskin 	mutex_lock(&dev->device_lock);
1016859ef2ffSAlexander Usyskin 
1017859ef2ffSAlexander Usyskin 	if (dev->pg_event != MEI_PG_EVENT_INTR_RECEIVED) {
1018859ef2ffSAlexander Usyskin 		reg = mei_me_d0i3c_read(dev);
1019859ef2ffSAlexander Usyskin 		if (!(reg & H_D0I3C_I3)) {
1020859ef2ffSAlexander Usyskin 			ret = -ETIME;
1021859ef2ffSAlexander Usyskin 			goto out;
1022859ef2ffSAlexander Usyskin 		}
1023859ef2ffSAlexander Usyskin 	}
1024859ef2ffSAlexander Usyskin 
1025859ef2ffSAlexander Usyskin 	ret = 0;
1026859ef2ffSAlexander Usyskin on:
1027859ef2ffSAlexander Usyskin 	hw->pg_state = MEI_PG_ON;
1028859ef2ffSAlexander Usyskin out:
1029859ef2ffSAlexander Usyskin 	dev->pg_event = MEI_PG_EVENT_IDLE;
1030859ef2ffSAlexander Usyskin 	dev_dbg(dev->dev, "d0i3 enter ret = %d\n", ret);
1031859ef2ffSAlexander Usyskin 	return ret;
1032859ef2ffSAlexander Usyskin }
1033859ef2ffSAlexander Usyskin 
1034859ef2ffSAlexander Usyskin /**
1035859ef2ffSAlexander Usyskin  * mei_me_d0i3_enter - perform d0i3 entry procedure
1036859ef2ffSAlexander Usyskin  *   no hbm PG handshake
1037859ef2ffSAlexander Usyskin  *   no waiting for confirmation; runs with interrupts
1038859ef2ffSAlexander Usyskin  *   disabled
1039859ef2ffSAlexander Usyskin  *
1040859ef2ffSAlexander Usyskin  * @dev: the device structure
1041859ef2ffSAlexander Usyskin  *
1042859ef2ffSAlexander Usyskin  * Return: 0 on success an error code otherwise
1043859ef2ffSAlexander Usyskin  */
mei_me_d0i3_enter(struct mei_device * dev)1044859ef2ffSAlexander Usyskin static int mei_me_d0i3_enter(struct mei_device *dev)
1045859ef2ffSAlexander Usyskin {
1046859ef2ffSAlexander Usyskin 	struct mei_me_hw *hw = to_me_hw(dev);
1047859ef2ffSAlexander Usyskin 	u32 reg;
1048859ef2ffSAlexander Usyskin 
1049859ef2ffSAlexander Usyskin 	reg = mei_me_d0i3c_read(dev);
1050859ef2ffSAlexander Usyskin 	if (reg & H_D0I3C_I3) {
1051859ef2ffSAlexander Usyskin 		/* we are in d0i3, nothing to do */
1052859ef2ffSAlexander Usyskin 		dev_dbg(dev->dev, "already d0i3 : set not needed\n");
1053859ef2ffSAlexander Usyskin 		goto on;
1054859ef2ffSAlexander Usyskin 	}
1055859ef2ffSAlexander Usyskin 
1056859ef2ffSAlexander Usyskin 	mei_me_d0i3_set(dev, false);
1057859ef2ffSAlexander Usyskin on:
1058859ef2ffSAlexander Usyskin 	hw->pg_state = MEI_PG_ON;
1059859ef2ffSAlexander Usyskin 	dev->pg_event = MEI_PG_EVENT_IDLE;
1060859ef2ffSAlexander Usyskin 	dev_dbg(dev->dev, "d0i3 enter\n");
1061859ef2ffSAlexander Usyskin 	return 0;
1062859ef2ffSAlexander Usyskin }
1063859ef2ffSAlexander Usyskin 
1064859ef2ffSAlexander Usyskin /**
1065859ef2ffSAlexander Usyskin  * mei_me_d0i3_exit_sync - perform d0i3 exit procedure
1066859ef2ffSAlexander Usyskin  *
1067859ef2ffSAlexander Usyskin  * @dev: the device structure
1068859ef2ffSAlexander Usyskin  *
1069859ef2ffSAlexander Usyskin  * Return: 0 on success an error code otherwise
1070859ef2ffSAlexander Usyskin  */
mei_me_d0i3_exit_sync(struct mei_device * dev)1071859ef2ffSAlexander Usyskin static int mei_me_d0i3_exit_sync(struct mei_device *dev)
1072859ef2ffSAlexander Usyskin {
1073859ef2ffSAlexander Usyskin 	struct mei_me_hw *hw = to_me_hw(dev);
1074859ef2ffSAlexander Usyskin 	int ret;
1075859ef2ffSAlexander Usyskin 	u32 reg;
1076859ef2ffSAlexander Usyskin 
1077859ef2ffSAlexander Usyskin 	dev->pg_event = MEI_PG_EVENT_INTR_WAIT;
1078859ef2ffSAlexander Usyskin 
1079859ef2ffSAlexander Usyskin 	reg = mei_me_d0i3c_read(dev);
1080859ef2ffSAlexander Usyskin 	if (!(reg & H_D0I3C_I3)) {
1081859ef2ffSAlexander Usyskin 		/* we are not in d0i3, nothing to do */
1082859ef2ffSAlexander Usyskin 		dev_dbg(dev->dev, "d0i3 exit not needed\n");
1083859ef2ffSAlexander Usyskin 		ret = 0;
1084859ef2ffSAlexander Usyskin 		goto off;
1085859ef2ffSAlexander Usyskin 	}
1086859ef2ffSAlexander Usyskin 
1087859ef2ffSAlexander Usyskin 	reg = mei_me_d0i3_unset(dev);
1088859ef2ffSAlexander Usyskin 	if (!(reg & H_D0I3C_CIP)) {
1089859ef2ffSAlexander Usyskin 		dev_dbg(dev->dev, "d0i3 exit wait not needed\n");
1090859ef2ffSAlexander Usyskin 		ret = 0;
1091859ef2ffSAlexander Usyskin 		goto off;
1092859ef2ffSAlexander Usyskin 	}
1093859ef2ffSAlexander Usyskin 
1094859ef2ffSAlexander Usyskin 	mutex_unlock(&dev->device_lock);
1095859ef2ffSAlexander Usyskin 	wait_event_timeout(dev->wait_pg,
109695953618SAlexander Usyskin 		dev->pg_event == MEI_PG_EVENT_INTR_RECEIVED,
109795953618SAlexander Usyskin 		dev->timeouts.d0i3);
1098859ef2ffSAlexander Usyskin 	mutex_lock(&dev->device_lock);
1099859ef2ffSAlexander Usyskin 
1100859ef2ffSAlexander Usyskin 	if (dev->pg_event != MEI_PG_EVENT_INTR_RECEIVED) {
1101859ef2ffSAlexander Usyskin 		reg = mei_me_d0i3c_read(dev);
1102859ef2ffSAlexander Usyskin 		if (reg & H_D0I3C_I3) {
1103859ef2ffSAlexander Usyskin 			ret = -ETIME;
1104859ef2ffSAlexander Usyskin 			goto out;
1105859ef2ffSAlexander Usyskin 		}
1106859ef2ffSAlexander Usyskin 	}
1107859ef2ffSAlexander Usyskin 
1108859ef2ffSAlexander Usyskin 	ret = 0;
1109859ef2ffSAlexander Usyskin off:
1110859ef2ffSAlexander Usyskin 	hw->pg_state = MEI_PG_OFF;
1111859ef2ffSAlexander Usyskin out:
1112859ef2ffSAlexander Usyskin 	dev->pg_event = MEI_PG_EVENT_IDLE;
1113859ef2ffSAlexander Usyskin 
1114859ef2ffSAlexander Usyskin 	dev_dbg(dev->dev, "d0i3 exit ret = %d\n", ret);
1115859ef2ffSAlexander Usyskin 	return ret;
1116859ef2ffSAlexander Usyskin }
1117859ef2ffSAlexander Usyskin 
1118859ef2ffSAlexander Usyskin /**
1119859ef2ffSAlexander Usyskin  * mei_me_pg_legacy_intr - perform legacy pg processing
1120859ef2ffSAlexander Usyskin  *			   in interrupt thread handler
1121859ef2ffSAlexander Usyskin  *
1122859ef2ffSAlexander Usyskin  * @dev: the device structure
1123859ef2ffSAlexander Usyskin  */
mei_me_pg_legacy_intr(struct mei_device * dev)1124859ef2ffSAlexander Usyskin static void mei_me_pg_legacy_intr(struct mei_device *dev)
1125859ef2ffSAlexander Usyskin {
1126859ef2ffSAlexander Usyskin 	struct mei_me_hw *hw = to_me_hw(dev);
1127859ef2ffSAlexander Usyskin 
1128859ef2ffSAlexander Usyskin 	if (dev->pg_event != MEI_PG_EVENT_INTR_WAIT)
1129859ef2ffSAlexander Usyskin 		return;
1130859ef2ffSAlexander Usyskin 
1131859ef2ffSAlexander Usyskin 	dev->pg_event = MEI_PG_EVENT_INTR_RECEIVED;
1132859ef2ffSAlexander Usyskin 	hw->pg_state = MEI_PG_OFF;
1133859ef2ffSAlexander Usyskin 	if (waitqueue_active(&dev->wait_pg))
1134859ef2ffSAlexander Usyskin 		wake_up(&dev->wait_pg);
1135859ef2ffSAlexander Usyskin }
1136859ef2ffSAlexander Usyskin 
1137859ef2ffSAlexander Usyskin /**
1138859ef2ffSAlexander Usyskin  * mei_me_d0i3_intr - perform d0i3 processing in interrupt thread handler
1139859ef2ffSAlexander Usyskin  *
1140859ef2ffSAlexander Usyskin  * @dev: the device structure
1141a2eb0fc0SAlexander Usyskin  * @intr_source: interrupt source
1142859ef2ffSAlexander Usyskin  */
mei_me_d0i3_intr(struct mei_device * dev,u32 intr_source)1143a2eb0fc0SAlexander Usyskin static void mei_me_d0i3_intr(struct mei_device *dev, u32 intr_source)
1144859ef2ffSAlexander Usyskin {
1145859ef2ffSAlexander Usyskin 	struct mei_me_hw *hw = to_me_hw(dev);
1146859ef2ffSAlexander Usyskin 
1147859ef2ffSAlexander Usyskin 	if (dev->pg_event == MEI_PG_EVENT_INTR_WAIT &&
1148a2eb0fc0SAlexander Usyskin 	    (intr_source & H_D0I3C_IS)) {
1149859ef2ffSAlexander Usyskin 		dev->pg_event = MEI_PG_EVENT_INTR_RECEIVED;
1150859ef2ffSAlexander Usyskin 		if (hw->pg_state == MEI_PG_ON) {
1151859ef2ffSAlexander Usyskin 			hw->pg_state = MEI_PG_OFF;
1152859ef2ffSAlexander Usyskin 			if (dev->hbm_state != MEI_HBM_IDLE) {
1153859ef2ffSAlexander Usyskin 				/*
1154859ef2ffSAlexander Usyskin 				 * force H_RDY because it could be
1155859ef2ffSAlexander Usyskin 				 * wiped off during PG
1156859ef2ffSAlexander Usyskin 				 */
1157859ef2ffSAlexander Usyskin 				dev_dbg(dev->dev, "d0i3 set host ready\n");
1158859ef2ffSAlexander Usyskin 				mei_me_host_set_ready(dev);
1159859ef2ffSAlexander Usyskin 			}
1160859ef2ffSAlexander Usyskin 		} else {
1161859ef2ffSAlexander Usyskin 			hw->pg_state = MEI_PG_ON;
1162859ef2ffSAlexander Usyskin 		}
1163859ef2ffSAlexander Usyskin 
1164859ef2ffSAlexander Usyskin 		wake_up(&dev->wait_pg);
1165859ef2ffSAlexander Usyskin 	}
1166859ef2ffSAlexander Usyskin 
1167a2eb0fc0SAlexander Usyskin 	if (hw->pg_state == MEI_PG_ON && (intr_source & H_IS)) {
1168859ef2ffSAlexander Usyskin 		/*
1169859ef2ffSAlexander Usyskin 		 * HW sent some data and we are in D0i3, so
1170859ef2ffSAlexander Usyskin 		 * we got here because of HW initiated exit from D0i3.
1171859ef2ffSAlexander Usyskin 		 * Start runtime pm resume sequence to exit low power state.
1172859ef2ffSAlexander Usyskin 		 */
1173859ef2ffSAlexander Usyskin 		dev_dbg(dev->dev, "d0i3 want resume\n");
1174859ef2ffSAlexander Usyskin 		mei_hbm_pg_resume(dev);
1175859ef2ffSAlexander Usyskin 	}
1176859ef2ffSAlexander Usyskin }
1177859ef2ffSAlexander Usyskin 
1178859ef2ffSAlexander Usyskin /**
11793dc196eaSAlexander Usyskin  * mei_me_pg_intr - perform pg processing in interrupt thread handler
11803dc196eaSAlexander Usyskin  *
11813dc196eaSAlexander Usyskin  * @dev: the device structure
1182a2eb0fc0SAlexander Usyskin  * @intr_source: interrupt source
11833dc196eaSAlexander Usyskin  */
mei_me_pg_intr(struct mei_device * dev,u32 intr_source)1184a2eb0fc0SAlexander Usyskin static void mei_me_pg_intr(struct mei_device *dev, u32 intr_source)
11853dc196eaSAlexander Usyskin {
11863dc196eaSAlexander Usyskin 	struct mei_me_hw *hw = to_me_hw(dev);
11873dc196eaSAlexander Usyskin 
1188859ef2ffSAlexander Usyskin 	if (hw->d0i3_supported)
1189a2eb0fc0SAlexander Usyskin 		mei_me_d0i3_intr(dev, intr_source);
1190859ef2ffSAlexander Usyskin 	else
1191859ef2ffSAlexander Usyskin 		mei_me_pg_legacy_intr(dev);
1192859ef2ffSAlexander Usyskin }
11933dc196eaSAlexander Usyskin 
1194859ef2ffSAlexander Usyskin /**
1195859ef2ffSAlexander Usyskin  * mei_me_pg_enter_sync - perform runtime pm entry procedure
1196859ef2ffSAlexander Usyskin  *
1197859ef2ffSAlexander Usyskin  * @dev: the device structure
1198859ef2ffSAlexander Usyskin  *
1199859ef2ffSAlexander Usyskin  * Return: 0 on success an error code otherwise
1200859ef2ffSAlexander Usyskin  */
mei_me_pg_enter_sync(struct mei_device * dev)1201859ef2ffSAlexander Usyskin int mei_me_pg_enter_sync(struct mei_device *dev)
1202859ef2ffSAlexander Usyskin {
1203859ef2ffSAlexander Usyskin 	struct mei_me_hw *hw = to_me_hw(dev);
1204859ef2ffSAlexander Usyskin 
1205859ef2ffSAlexander Usyskin 	if (hw->d0i3_supported)
1206859ef2ffSAlexander Usyskin 		return mei_me_d0i3_enter_sync(dev);
1207859ef2ffSAlexander Usyskin 	else
1208859ef2ffSAlexander Usyskin 		return mei_me_pg_legacy_enter_sync(dev);
1209859ef2ffSAlexander Usyskin }
1210859ef2ffSAlexander Usyskin 
1211859ef2ffSAlexander Usyskin /**
1212859ef2ffSAlexander Usyskin  * mei_me_pg_exit_sync - perform runtime pm exit procedure
1213859ef2ffSAlexander Usyskin  *
1214859ef2ffSAlexander Usyskin  * @dev: the device structure
1215859ef2ffSAlexander Usyskin  *
1216859ef2ffSAlexander Usyskin  * Return: 0 on success an error code otherwise
1217859ef2ffSAlexander Usyskin  */
mei_me_pg_exit_sync(struct mei_device * dev)1218859ef2ffSAlexander Usyskin int mei_me_pg_exit_sync(struct mei_device *dev)
1219859ef2ffSAlexander Usyskin {
1220859ef2ffSAlexander Usyskin 	struct mei_me_hw *hw = to_me_hw(dev);
1221859ef2ffSAlexander Usyskin 
1222859ef2ffSAlexander Usyskin 	if (hw->d0i3_supported)
1223859ef2ffSAlexander Usyskin 		return mei_me_d0i3_exit_sync(dev);
1224859ef2ffSAlexander Usyskin 	else
1225859ef2ffSAlexander Usyskin 		return mei_me_pg_legacy_exit_sync(dev);
12263dc196eaSAlexander Usyskin }
12273dc196eaSAlexander Usyskin 
12283dc196eaSAlexander Usyskin /**
1229ebad6b94SAlexander Usyskin  * mei_me_hw_reset - resets fw via mei csr register.
1230ebad6b94SAlexander Usyskin  *
1231ebad6b94SAlexander Usyskin  * @dev: the device structure
1232ebad6b94SAlexander Usyskin  * @intr_enable: if interrupt should be enabled after reset.
1233ebad6b94SAlexander Usyskin  *
1234b9a1fc99SAlexander Usyskin  * Return: 0 on success an error code otherwise
1235ebad6b94SAlexander Usyskin  */
mei_me_hw_reset(struct mei_device * dev,bool intr_enable)1236ebad6b94SAlexander Usyskin static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable)
1237ebad6b94SAlexander Usyskin {
1238b9a1fc99SAlexander Usyskin 	struct mei_me_hw *hw = to_me_hw(dev);
1239b9a1fc99SAlexander Usyskin 	int ret;
1240b9a1fc99SAlexander Usyskin 	u32 hcsr;
1241ebad6b94SAlexander Usyskin 
1242b9a1fc99SAlexander Usyskin 	if (intr_enable) {
1243b9a1fc99SAlexander Usyskin 		mei_me_intr_enable(dev);
1244b9a1fc99SAlexander Usyskin 		if (hw->d0i3_supported) {
1245b9a1fc99SAlexander Usyskin 			ret = mei_me_d0i3_exit_sync(dev);
1246b9a1fc99SAlexander Usyskin 			if (ret)
1247b9a1fc99SAlexander Usyskin 				return ret;
12489f463937SAlexander Usyskin 		} else {
12499f463937SAlexander Usyskin 			hw->pg_state = MEI_PG_OFF;
1250b9a1fc99SAlexander Usyskin 		}
1251b9a1fc99SAlexander Usyskin 	}
1252b9a1fc99SAlexander Usyskin 
125377537ad2SAlexander Usyskin 	pm_runtime_set_active(dev->dev);
125477537ad2SAlexander Usyskin 
1255b9a1fc99SAlexander Usyskin 	hcsr = mei_hcsr_read(dev);
1256ebad6b94SAlexander Usyskin 	/* H_RST may be found lit before reset is started,
1257ebad6b94SAlexander Usyskin 	 * for example if preceding reset flow hasn't completed.
1258ebad6b94SAlexander Usyskin 	 * In that case asserting H_RST will be ignored, therefore
1259ebad6b94SAlexander Usyskin 	 * we need to clean H_RST bit to start a successful reset sequence.
1260ebad6b94SAlexander Usyskin 	 */
1261ebad6b94SAlexander Usyskin 	if ((hcsr & H_RST) == H_RST) {
1262ebad6b94SAlexander Usyskin 		dev_warn(dev->dev, "H_RST is set = 0x%08X", hcsr);
1263ebad6b94SAlexander Usyskin 		hcsr &= ~H_RST;
1264ebad6b94SAlexander Usyskin 		mei_hcsr_set(dev, hcsr);
1265ebad6b94SAlexander Usyskin 		hcsr = mei_hcsr_read(dev);
1266ebad6b94SAlexander Usyskin 	}
1267ebad6b94SAlexander Usyskin 
1268ebad6b94SAlexander Usyskin 	hcsr |= H_RST | H_IG | H_CSR_IS_MASK;
1269ebad6b94SAlexander Usyskin 
12705b063995STomas Winkler 	if (!intr_enable || mei_me_hw_use_polling(to_me_hw(dev)))
1271ebad6b94SAlexander Usyskin 		hcsr &= ~H_CSR_IE_MASK;
1272ebad6b94SAlexander Usyskin 
1273ebad6b94SAlexander Usyskin 	dev->recvd_hw_ready = false;
1274ebad6b94SAlexander Usyskin 	mei_hcsr_write(dev, hcsr);
1275ebad6b94SAlexander Usyskin 
1276ebad6b94SAlexander Usyskin 	/*
1277ebad6b94SAlexander Usyskin 	 * Host reads the H_CSR once to ensure that the
1278ebad6b94SAlexander Usyskin 	 * posted write to H_CSR completes.
1279ebad6b94SAlexander Usyskin 	 */
1280ebad6b94SAlexander Usyskin 	hcsr = mei_hcsr_read(dev);
1281ebad6b94SAlexander Usyskin 
1282ebad6b94SAlexander Usyskin 	if ((hcsr & H_RST) == 0)
1283ebad6b94SAlexander Usyskin 		dev_warn(dev->dev, "H_RST is not set = 0x%08X", hcsr);
1284ebad6b94SAlexander Usyskin 
1285ebad6b94SAlexander Usyskin 	if ((hcsr & H_RDY) == H_RDY)
1286ebad6b94SAlexander Usyskin 		dev_warn(dev->dev, "H_RDY is not cleared 0x%08X", hcsr);
1287ebad6b94SAlexander Usyskin 
1288b9a1fc99SAlexander Usyskin 	if (!intr_enable) {
1289ebad6b94SAlexander Usyskin 		mei_me_hw_reset_release(dev);
1290b9a1fc99SAlexander Usyskin 		if (hw->d0i3_supported) {
1291b9a1fc99SAlexander Usyskin 			ret = mei_me_d0i3_enter(dev);
1292b9a1fc99SAlexander Usyskin 			if (ret)
1293b9a1fc99SAlexander Usyskin 				return ret;
1294b9a1fc99SAlexander Usyskin 		}
1295b9a1fc99SAlexander Usyskin 	}
1296ebad6b94SAlexander Usyskin 	return 0;
1297ebad6b94SAlexander Usyskin }
1298ebad6b94SAlexander Usyskin 
1299ebad6b94SAlexander Usyskin /**
130006ecd645STomas Winkler  * mei_me_irq_quick_handler - The ISR of the MEI device
130106ecd645STomas Winkler  *
130206ecd645STomas Winkler  * @irq: The irq number
130306ecd645STomas Winkler  * @dev_id: pointer to the device structure
130406ecd645STomas Winkler  *
1305a8605ea2SAlexander Usyskin  * Return: irqreturn_t
130606ecd645STomas Winkler  */
mei_me_irq_quick_handler(int irq,void * dev_id)130706ecd645STomas Winkler irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id)
130806ecd645STomas Winkler {
130906ecd645STomas Winkler 	struct mei_device *dev = (struct mei_device *)dev_id;
13101fa55b4eSAlexander Usyskin 	u32 hcsr;
131106ecd645STomas Winkler 
13121fa55b4eSAlexander Usyskin 	hcsr = mei_hcsr_read(dev);
1313a2eb0fc0SAlexander Usyskin 	if (!me_intr_src(hcsr))
131406ecd645STomas Winkler 		return IRQ_NONE;
131506ecd645STomas Winkler 
1316a2eb0fc0SAlexander Usyskin 	dev_dbg(dev->dev, "interrupt source 0x%08X\n", me_intr_src(hcsr));
13171fa55b4eSAlexander Usyskin 
1318a2eb0fc0SAlexander Usyskin 	/* disable interrupts on device */
1319a2eb0fc0SAlexander Usyskin 	me_intr_disable(dev, hcsr);
132006ecd645STomas Winkler 	return IRQ_WAKE_THREAD;
132106ecd645STomas Winkler }
1322a98c30fdSTomas Winkler EXPORT_SYMBOL_GPL(mei_me_irq_quick_handler);
132306ecd645STomas Winkler 
132406ecd645STomas Winkler /**
132506ecd645STomas Winkler  * mei_me_irq_thread_handler - function called after ISR to handle the interrupt
132606ecd645STomas Winkler  * processing.
132706ecd645STomas Winkler  *
132806ecd645STomas Winkler  * @irq: The irq number
132906ecd645STomas Winkler  * @dev_id: pointer to the device structure
133006ecd645STomas Winkler  *
1331a8605ea2SAlexander Usyskin  * Return: irqreturn_t
133206ecd645STomas Winkler  *
133306ecd645STomas Winkler  */
mei_me_irq_thread_handler(int irq,void * dev_id)133406ecd645STomas Winkler irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
133506ecd645STomas Winkler {
133606ecd645STomas Winkler 	struct mei_device *dev = (struct mei_device *) dev_id;
1337962ff7bcSAlexander Usyskin 	struct list_head cmpl_list;
133806ecd645STomas Winkler 	s32 slots;
1339a2eb0fc0SAlexander Usyskin 	u32 hcsr;
1340544f9460STomas Winkler 	int rets = 0;
134106ecd645STomas Winkler 
13422bf94cabSTomas Winkler 	dev_dbg(dev->dev, "function called after ISR to handle the interrupt processing.\n");
134306ecd645STomas Winkler 	/* initialize our complete list */
134406ecd645STomas Winkler 	mutex_lock(&dev->device_lock);
1345a2eb0fc0SAlexander Usyskin 
1346a2eb0fc0SAlexander Usyskin 	hcsr = mei_hcsr_read(dev);
1347a2eb0fc0SAlexander Usyskin 	me_intr_clear(dev, hcsr);
1348a2eb0fc0SAlexander Usyskin 
1349962ff7bcSAlexander Usyskin 	INIT_LIST_HEAD(&cmpl_list);
135006ecd645STomas Winkler 
135106ecd645STomas Winkler 	/* check if ME wants a reset */
135233ec0826STomas Winkler 	if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING) {
1353*34a674e9SVitaly Lubart 		if (kind_is_gsc(dev) || kind_is_gscfi(dev)) {
1354*34a674e9SVitaly Lubart 			dev_dbg(dev->dev, "FW not ready: resetting: dev_state = %d\n",
1355*34a674e9SVitaly Lubart 				dev->dev_state);
1356*34a674e9SVitaly Lubart 		} else {
1357*34a674e9SVitaly Lubart 			dev_warn(dev->dev, "FW not ready: resetting: dev_state = %d\n",
1358*34a674e9SVitaly Lubart 				 dev->dev_state);
1359*34a674e9SVitaly Lubart 		}
1360685867f4SAlexander Usyskin 		if (dev->dev_state == MEI_DEV_POWERING_DOWN ||
1361685867f4SAlexander Usyskin 		    dev->dev_state == MEI_DEV_POWER_DOWN)
1362685867f4SAlexander Usyskin 			mei_cl_all_disconnect(dev);
1363685867f4SAlexander Usyskin 		else if (dev->dev_state != MEI_DEV_DISABLED)
1364544f9460STomas Winkler 			schedule_work(&dev->reset_work);
1365544f9460STomas Winkler 		goto end;
136606ecd645STomas Winkler 	}
136706ecd645STomas Winkler 
136847f60a01SAlexander Usyskin 	if (mei_me_hw_is_resetting(dev))
136947f60a01SAlexander Usyskin 		mei_hcsr_set_hig(dev);
137047f60a01SAlexander Usyskin 
1371a2eb0fc0SAlexander Usyskin 	mei_me_pg_intr(dev, me_intr_src(hcsr));
13723dc196eaSAlexander Usyskin 
137306ecd645STomas Winkler 	/*  check if we need to start the dev */
137406ecd645STomas Winkler 	if (!mei_host_is_ready(dev)) {
137506ecd645STomas Winkler 		if (mei_hw_is_ready(dev)) {
13762bf94cabSTomas Winkler 			dev_dbg(dev->dev, "we need to start the dev.\n");
1377aafae7ecSTomas Winkler 			dev->recvd_hw_ready = true;
13782c2b93ecSAlexander Usyskin 			wake_up(&dev->wait_hw_ready);
137906ecd645STomas Winkler 		} else {
13802bf94cabSTomas Winkler 			dev_dbg(dev->dev, "Spurious Interrupt\n");
138106ecd645STomas Winkler 		}
1382544f9460STomas Winkler 		goto end;
138306ecd645STomas Winkler 	}
138406ecd645STomas Winkler 	/* check slots available for reading */
138506ecd645STomas Winkler 	slots = mei_count_full_read_slots(dev);
138606ecd645STomas Winkler 	while (slots > 0) {
13872bf94cabSTomas Winkler 		dev_dbg(dev->dev, "slots to read = %08x\n", slots);
1388962ff7bcSAlexander Usyskin 		rets = mei_irq_read_handler(dev, &cmpl_list, &slots);
1389b1b94b5dSTomas Winkler 		/* There is a race between ME write and interrupt delivery:
1390b1b94b5dSTomas Winkler 		 * Not all data is always available immediately after the
1391b1b94b5dSTomas Winkler 		 * interrupt, so try to read again on the next interrupt.
1392b1b94b5dSTomas Winkler 		 */
1393b1b94b5dSTomas Winkler 		if (rets == -ENODATA)
1394b1b94b5dSTomas Winkler 			break;
1395b1b94b5dSTomas Winkler 
139604af137cSAlexander Usyskin 		if (rets) {
139704af137cSAlexander Usyskin 			dev_err(dev->dev, "mei_irq_read_handler ret = %d, state = %d.\n",
139804af137cSAlexander Usyskin 				rets, dev->dev_state);
139904af137cSAlexander Usyskin 			if (dev->dev_state != MEI_DEV_RESETTING &&
140004af137cSAlexander Usyskin 			    dev->dev_state != MEI_DEV_DISABLED &&
140104af137cSAlexander Usyskin 			    dev->dev_state != MEI_DEV_POWERING_DOWN &&
140204af137cSAlexander Usyskin 			    dev->dev_state != MEI_DEV_POWER_DOWN)
1403544f9460STomas Winkler 				schedule_work(&dev->reset_work);
140406ecd645STomas Winkler 			goto end;
140506ecd645STomas Winkler 		}
1406544f9460STomas Winkler 	}
140706ecd645STomas Winkler 
14086aae48ffSTomas Winkler 	dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
14096aae48ffSTomas Winkler 
1410ba9cdd0eSTomas Winkler 	/*
1411ba9cdd0eSTomas Winkler 	 * During PG handshake only allowed write is the replay to the
1412ba9cdd0eSTomas Winkler 	 * PG exit message, so block calling write function
14133dc196eaSAlexander Usyskin 	 * if the pg event is in PG handshake
1414ba9cdd0eSTomas Winkler 	 */
14153dc196eaSAlexander Usyskin 	if (dev->pg_event != MEI_PG_EVENT_WAIT &&
14163dc196eaSAlexander Usyskin 	    dev->pg_event != MEI_PG_EVENT_RECEIVED) {
1417962ff7bcSAlexander Usyskin 		rets = mei_irq_write_handler(dev, &cmpl_list);
1418544f9460STomas Winkler 		dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
1419ba9cdd0eSTomas Winkler 	}
142006ecd645STomas Winkler 
1421962ff7bcSAlexander Usyskin 	mei_irq_compl_handler(dev, &cmpl_list);
142206ecd645STomas Winkler 
1423544f9460STomas Winkler end:
14242bf94cabSTomas Winkler 	dev_dbg(dev->dev, "interrupt thread end ret = %d\n", rets);
1425a2eb0fc0SAlexander Usyskin 	mei_me_intr_enable(dev);
1426544f9460STomas Winkler 	mutex_unlock(&dev->device_lock);
142706ecd645STomas Winkler 	return IRQ_HANDLED;
142806ecd645STomas Winkler }
1429a98c30fdSTomas Winkler EXPORT_SYMBOL_GPL(mei_me_irq_thread_handler);
143004dd3661SAlexander Usyskin 
14315b063995STomas Winkler #define MEI_POLLING_TIMEOUT_ACTIVE 100
14325b063995STomas Winkler #define MEI_POLLING_TIMEOUT_IDLE   500
14335b063995STomas Winkler 
14345b063995STomas Winkler /**
14355b063995STomas Winkler  * mei_me_polling_thread - interrupt register polling thread
14365b063995STomas Winkler  *
1437de735e7fSRandy Dunlap  * @_dev: mei device
1438de735e7fSRandy Dunlap  *
14395b063995STomas Winkler  * The thread monitors the interrupt source register and calls
14405b063995STomas Winkler  * mei_me_irq_thread_handler() to handle the firmware
14415b063995STomas Winkler  * input.
14425b063995STomas Winkler  *
14435b063995STomas Winkler  * The function polls in MEI_POLLING_TIMEOUT_ACTIVE timeout
14445b063995STomas Winkler  * in case there was an event, in idle case the polling
14455b063995STomas Winkler  * time increases yet again by MEI_POLLING_TIMEOUT_ACTIVE
14465b063995STomas Winkler  * up to MEI_POLLING_TIMEOUT_IDLE.
14475b063995STomas Winkler  *
14485b063995STomas Winkler  * Return: always 0
14495b063995STomas Winkler  */
mei_me_polling_thread(void * _dev)14505b063995STomas Winkler int mei_me_polling_thread(void *_dev)
14515b063995STomas Winkler {
14525b063995STomas Winkler 	struct mei_device *dev = _dev;
14535b063995STomas Winkler 	irqreturn_t irq_ret;
14545b063995STomas Winkler 	long polling_timeout = MEI_POLLING_TIMEOUT_ACTIVE;
14555b063995STomas Winkler 
14565b063995STomas Winkler 	dev_dbg(dev->dev, "kernel thread is running\n");
14575b063995STomas Winkler 	while (!kthread_should_stop()) {
14585b063995STomas Winkler 		struct mei_me_hw *hw = to_me_hw(dev);
14595b063995STomas Winkler 		u32 hcsr;
14605b063995STomas Winkler 
14615b063995STomas Winkler 		wait_event_timeout(hw->wait_active,
14625b063995STomas Winkler 				   hw->is_active || kthread_should_stop(),
14635b063995STomas Winkler 				   msecs_to_jiffies(MEI_POLLING_TIMEOUT_IDLE));
14645b063995STomas Winkler 
14655b063995STomas Winkler 		if (kthread_should_stop())
14665b063995STomas Winkler 			break;
14675b063995STomas Winkler 
14685b063995STomas Winkler 		hcsr = mei_hcsr_read(dev);
14695b063995STomas Winkler 		if (me_intr_src(hcsr)) {
14705b063995STomas Winkler 			polling_timeout = MEI_POLLING_TIMEOUT_ACTIVE;
14715b063995STomas Winkler 			irq_ret = mei_me_irq_thread_handler(1, dev);
14725b063995STomas Winkler 			if (irq_ret != IRQ_HANDLED)
14735b063995STomas Winkler 				dev_err(dev->dev, "irq_ret %d\n", irq_ret);
14745b063995STomas Winkler 		} else {
14755b063995STomas Winkler 			/*
14765b063995STomas Winkler 			 * Increase timeout by MEI_POLLING_TIMEOUT_ACTIVE
14775b063995STomas Winkler 			 * up to MEI_POLLING_TIMEOUT_IDLE
14785b063995STomas Winkler 			 */
14795b063995STomas Winkler 			polling_timeout = clamp_val(polling_timeout + MEI_POLLING_TIMEOUT_ACTIVE,
14805b063995STomas Winkler 						    MEI_POLLING_TIMEOUT_ACTIVE,
14815b063995STomas Winkler 						    MEI_POLLING_TIMEOUT_IDLE);
14825b063995STomas Winkler 		}
14835b063995STomas Winkler 
14845b063995STomas Winkler 		schedule_timeout_interruptible(msecs_to_jiffies(polling_timeout));
14855b063995STomas Winkler 	}
14865b063995STomas Winkler 
14875b063995STomas Winkler 	return 0;
14885b063995STomas Winkler }
14895b063995STomas Winkler EXPORT_SYMBOL_GPL(mei_me_polling_thread);
14905b063995STomas Winkler 
1491827eef51STomas Winkler static const struct mei_hw_ops mei_me_hw_ops = {
1492827eef51STomas Winkler 
149352f6efdfSAlexander Usyskin 	.trc_status = mei_me_trc_status,
14941bd30b6aSTomas Winkler 	.fw_status = mei_me_fw_status,
1495964a2331STomas Winkler 	.pg_state  = mei_me_pg_state,
1496964a2331STomas Winkler 
1497827eef51STomas Winkler 	.host_is_ready = mei_me_host_is_ready,
1498827eef51STomas Winkler 
1499827eef51STomas Winkler 	.hw_is_ready = mei_me_hw_is_ready,
1500827eef51STomas Winkler 	.hw_reset = mei_me_hw_reset,
1501827eef51STomas Winkler 	.hw_config = mei_me_hw_config,
1502aafae7ecSTomas Winkler 	.hw_start = mei_me_hw_start,
1503827eef51STomas Winkler 
15043dc196eaSAlexander Usyskin 	.pg_in_transition = mei_me_pg_in_transition,
1505ee7e5afdSTomas Winkler 	.pg_is_enabled = mei_me_pg_is_enabled,
1506ee7e5afdSTomas Winkler 
1507827eef51STomas Winkler 	.intr_clear = mei_me_intr_clear,
1508827eef51STomas Winkler 	.intr_enable = mei_me_intr_enable,
1509827eef51STomas Winkler 	.intr_disable = mei_me_intr_disable,
15104a8efd4aSTomas Winkler 	.synchronize_irq = mei_me_synchronize_irq,
1511827eef51STomas Winkler 
1512827eef51STomas Winkler 	.hbuf_free_slots = mei_me_hbuf_empty_slots,
1513827eef51STomas Winkler 	.hbuf_is_ready = mei_me_hbuf_is_empty,
15148c8d964cSTomas Winkler 	.hbuf_depth = mei_me_hbuf_depth,
1515827eef51STomas Winkler 
15164b9960d0STomas Winkler 	.write = mei_me_hbuf_write,
1517827eef51STomas Winkler 
1518827eef51STomas Winkler 	.rdbuf_full_slots = mei_me_count_full_read_slots,
1519827eef51STomas Winkler 	.read_hdr = mei_me_mecbrw_read,
1520827eef51STomas Winkler 	.read = mei_me_read_slots
1521827eef51STomas Winkler };
1522827eef51STomas Winkler 
15233fbd1dfeSTomas Winkler /**
15243fbd1dfeSTomas Winkler  * mei_me_fw_type_nm() - check for nm sku
15253fbd1dfeSTomas Winkler  *
1526de735e7fSRandy Dunlap  * @pdev: pci device
1527de735e7fSRandy Dunlap  *
15283fbd1dfeSTomas Winkler  * Read ME FW Status register to check for the Node Manager (NM) Firmware.
15293fbd1dfeSTomas Winkler  * The NM FW is only signaled in PCI function 0.
15303fbd1dfeSTomas Winkler  * __Note__: Deprecated by PCH8 and newer.
15313fbd1dfeSTomas Winkler  *
15323fbd1dfeSTomas Winkler  * Return: true in case of NM firmware
15333fbd1dfeSTomas Winkler  */
mei_me_fw_type_nm(const struct pci_dev * pdev)153445a2c762STomas Winkler static bool mei_me_fw_type_nm(const struct pci_dev *pdev)
1535c919951dSTomas Winkler {
1536c919951dSTomas Winkler 	u32 reg;
15373fbd1dfeSTomas Winkler 	unsigned int devfn;
153892db1555STomas Winkler 
15393fbd1dfeSTomas Winkler 	devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0);
15403fbd1dfeSTomas Winkler 	pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_2, &reg);
1541a96c5482STomas Winkler 	trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_2", PCI_CFG_HFS_2, reg);
1542c919951dSTomas Winkler 	/* make sure that bit 9 (NM) is up and bit 10 (DM) is down */
1543c919951dSTomas Winkler 	return (reg & 0x600) == 0x200;
1544c919951dSTomas Winkler }
1545c919951dSTomas Winkler 
1546c919951dSTomas Winkler #define MEI_CFG_FW_NM                           \
1547c919951dSTomas Winkler 	.quirk_probe = mei_me_fw_type_nm
1548c919951dSTomas Winkler 
15494d3c6c8eSTomas Winkler /**
155009f8c33aSTamar Mashiah  * mei_me_fw_type_sps_4() - check for sps 4.0 sku
15514d3c6c8eSTomas Winkler  *
1552de735e7fSRandy Dunlap  * @pdev: pci device
1553de735e7fSRandy Dunlap  *
15544d3c6c8eSTomas Winkler  * Read ME FW Status register to check for SPS Firmware.
15554d3c6c8eSTomas Winkler  * The SPS FW is only signaled in the PCI function 0.
15564d3c6c8eSTomas Winkler  * __Note__: Deprecated by SPS 5.0 and newer.
15574d3c6c8eSTomas Winkler  *
15584d3c6c8eSTomas Winkler  * Return: true in case of SPS firmware
15594d3c6c8eSTomas Winkler  */
mei_me_fw_type_sps_4(const struct pci_dev * pdev)156045a2c762STomas Winkler static bool mei_me_fw_type_sps_4(const struct pci_dev *pdev)
1561c919951dSTomas Winkler {
1562c919951dSTomas Winkler 	u32 reg;
15638c57cac1STomas Winkler 	unsigned int devfn;
15648c57cac1STomas Winkler 
15658c57cac1STomas Winkler 	devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0);
15668c57cac1STomas Winkler 	pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_1, &reg);
1567a96c5482STomas Winkler 	trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_1", PCI_CFG_HFS_1, reg);
15684d3c6c8eSTomas Winkler 	return (reg & PCI_CFG_HFS_1_OPMODE_MSK) == PCI_CFG_HFS_1_OPMODE_SPS;
1569c919951dSTomas Winkler }
1570c919951dSTomas Winkler 
1571f76d77f5STomas Winkler #define MEI_CFG_FW_SPS_4                          \
1572f76d77f5STomas Winkler 	.quirk_probe = mei_me_fw_type_sps_4
1573f76d77f5STomas Winkler 
1574f76d77f5STomas Winkler /**
1575ccdf6f80SAlexander Usyskin  * mei_me_fw_type_sps_ign() - check for sps or ign sku
1576f76d77f5STomas Winkler  *
1577de735e7fSRandy Dunlap  * @pdev: pci device
1578de735e7fSRandy Dunlap  *
1579ccdf6f80SAlexander Usyskin  * Read ME FW Status register to check for SPS or IGN Firmware.
1580ccdf6f80SAlexander Usyskin  * The SPS/IGN FW is only signaled in pci function 0
1581f76d77f5STomas Winkler  *
1582ccdf6f80SAlexander Usyskin  * Return: true in case of SPS/IGN firmware
1583f76d77f5STomas Winkler  */
mei_me_fw_type_sps_ign(const struct pci_dev * pdev)1584ccdf6f80SAlexander Usyskin static bool mei_me_fw_type_sps_ign(const struct pci_dev *pdev)
1585f76d77f5STomas Winkler {
1586f76d77f5STomas Winkler 	u32 reg;
1587f76d77f5STomas Winkler 	u32 fw_type;
1588f76d77f5STomas Winkler 	unsigned int devfn;
1589f76d77f5STomas Winkler 
1590f76d77f5STomas Winkler 	devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0);
1591f76d77f5STomas Winkler 	pci_bus_read_config_dword(pdev->bus, devfn, PCI_CFG_HFS_3, &reg);
1592f76d77f5STomas Winkler 	trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_3", PCI_CFG_HFS_3, reg);
1593f76d77f5STomas Winkler 	fw_type = (reg & PCI_CFG_HFS_3_FW_SKU_MSK);
1594f76d77f5STomas Winkler 
1595f76d77f5STomas Winkler 	dev_dbg(&pdev->dev, "fw type is %d\n", fw_type);
1596f76d77f5STomas Winkler 
1597ccdf6f80SAlexander Usyskin 	return fw_type == PCI_CFG_HFS_3_FW_SKU_IGN ||
1598ccdf6f80SAlexander Usyskin 	       fw_type == PCI_CFG_HFS_3_FW_SKU_SPS;
1599f76d77f5STomas Winkler }
1600f76d77f5STomas Winkler 
16012f79d3d1SAlexander Usyskin #define MEI_CFG_KIND_ITOUCH                     \
16022f79d3d1SAlexander Usyskin 	.kind = "itouch"
16032f79d3d1SAlexander Usyskin 
1604a98c30fdSTomas Winkler #define MEI_CFG_TYPE_GSC                        \
1605a98c30fdSTomas Winkler 	.kind = "gsc"
1606a98c30fdSTomas Winkler 
1607a98c30fdSTomas Winkler #define MEI_CFG_TYPE_GSCFI                      \
1608a98c30fdSTomas Winkler 	.kind = "gscfi"
1609a98c30fdSTomas Winkler 
1610ccdf6f80SAlexander Usyskin #define MEI_CFG_FW_SPS_IGN                      \
1611ccdf6f80SAlexander Usyskin 	.quirk_probe = mei_me_fw_type_sps_ign
1612c919951dSTomas Winkler 
1613f8204f0dSAlexander Usyskin #define MEI_CFG_FW_VER_SUPP                     \
1614f8204f0dSAlexander Usyskin 	.fw_ver_supported = 1
1615c919951dSTomas Winkler 
1616f5ac3c49STomas Winkler #define MEI_CFG_ICH_HFS                      \
16178d929d48SAlexander Usyskin 	.fw_status.count = 0
16188d929d48SAlexander Usyskin 
1619f5ac3c49STomas Winkler #define MEI_CFG_ICH10_HFS                        \
16208d929d48SAlexander Usyskin 	.fw_status.count = 1,                   \
16218d929d48SAlexander Usyskin 	.fw_status.status[0] = PCI_CFG_HFS_1
16228d929d48SAlexander Usyskin 
16238d929d48SAlexander Usyskin #define MEI_CFG_PCH_HFS                         \
16248d929d48SAlexander Usyskin 	.fw_status.count = 2,                   \
16258d929d48SAlexander Usyskin 	.fw_status.status[0] = PCI_CFG_HFS_1,   \
16268d929d48SAlexander Usyskin 	.fw_status.status[1] = PCI_CFG_HFS_2
16278d929d48SAlexander Usyskin 
1628edca5ea3SAlexander Usyskin #define MEI_CFG_PCH8_HFS                        \
1629edca5ea3SAlexander Usyskin 	.fw_status.count = 6,                   \
1630edca5ea3SAlexander Usyskin 	.fw_status.status[0] = PCI_CFG_HFS_1,   \
1631edca5ea3SAlexander Usyskin 	.fw_status.status[1] = PCI_CFG_HFS_2,   \
1632edca5ea3SAlexander Usyskin 	.fw_status.status[2] = PCI_CFG_HFS_3,   \
1633edca5ea3SAlexander Usyskin 	.fw_status.status[3] = PCI_CFG_HFS_4,   \
1634edca5ea3SAlexander Usyskin 	.fw_status.status[4] = PCI_CFG_HFS_5,   \
1635edca5ea3SAlexander Usyskin 	.fw_status.status[5] = PCI_CFG_HFS_6
16368d929d48SAlexander Usyskin 
16377026a5fdSAlexander Usyskin #define MEI_CFG_DMA_128 \
16387026a5fdSAlexander Usyskin 	.dma_size[DMA_DSCR_HOST] = SZ_128K, \
16397026a5fdSAlexander Usyskin 	.dma_size[DMA_DSCR_DEVICE] = SZ_128K, \
16407026a5fdSAlexander Usyskin 	.dma_size[DMA_DSCR_CTRL] = PAGE_SIZE
16417026a5fdSAlexander Usyskin 
164252f6efdfSAlexander Usyskin #define MEI_CFG_TRC \
164352f6efdfSAlexander Usyskin 	.hw_trc_supported = 1
164452f6efdfSAlexander Usyskin 
16458d929d48SAlexander Usyskin /* ICH Legacy devices */
1646f5ac3c49STomas Winkler static const struct mei_cfg mei_me_ich_cfg = {
16478d929d48SAlexander Usyskin 	MEI_CFG_ICH_HFS,
16488d929d48SAlexander Usyskin };
16498d929d48SAlexander Usyskin 
1650f5ac3c49STomas Winkler /* ICH devices */
1651f5ac3c49STomas Winkler static const struct mei_cfg mei_me_ich10_cfg = {
1652f5ac3c49STomas Winkler 	MEI_CFG_ICH10_HFS,
1653f5ac3c49STomas Winkler };
1654f5ac3c49STomas Winkler 
1655f8204f0dSAlexander Usyskin /* PCH6 devices */
1656f8204f0dSAlexander Usyskin static const struct mei_cfg mei_me_pch6_cfg = {
16578d929d48SAlexander Usyskin 	MEI_CFG_PCH_HFS,
16588d929d48SAlexander Usyskin };
16598d929d48SAlexander Usyskin 
1660f8204f0dSAlexander Usyskin /* PCH7 devices */
1661f8204f0dSAlexander Usyskin static const struct mei_cfg mei_me_pch7_cfg = {
1662f8204f0dSAlexander Usyskin 	MEI_CFG_PCH_HFS,
1663f8204f0dSAlexander Usyskin 	MEI_CFG_FW_VER_SUPP,
1664f8204f0dSAlexander Usyskin };
1665f8204f0dSAlexander Usyskin 
1666c919951dSTomas Winkler /* PCH Cougar Point and Patsburg with quirk for Node Manager exclusion */
1667f5ac3c49STomas Winkler static const struct mei_cfg mei_me_pch_cpt_pbg_cfg = {
1668c919951dSTomas Winkler 	MEI_CFG_PCH_HFS,
1669f8204f0dSAlexander Usyskin 	MEI_CFG_FW_VER_SUPP,
1670c919951dSTomas Winkler 	MEI_CFG_FW_NM,
1671c919951dSTomas Winkler };
1672c919951dSTomas Winkler 
1673edca5ea3SAlexander Usyskin /* PCH8 Lynx Point and newer devices */
1674f5ac3c49STomas Winkler static const struct mei_cfg mei_me_pch8_cfg = {
1675edca5ea3SAlexander Usyskin 	MEI_CFG_PCH8_HFS,
1676f8204f0dSAlexander Usyskin 	MEI_CFG_FW_VER_SUPP,
1677edca5ea3SAlexander Usyskin };
1678edca5ea3SAlexander Usyskin 
16792f79d3d1SAlexander Usyskin /* PCH8 Lynx Point and newer devices - iTouch */
16802f79d3d1SAlexander Usyskin static const struct mei_cfg mei_me_pch8_itouch_cfg = {
16812f79d3d1SAlexander Usyskin 	MEI_CFG_KIND_ITOUCH,
16822f79d3d1SAlexander Usyskin 	MEI_CFG_PCH8_HFS,
16832f79d3d1SAlexander Usyskin 	MEI_CFG_FW_VER_SUPP,
16842f79d3d1SAlexander Usyskin };
16852f79d3d1SAlexander Usyskin 
1686edca5ea3SAlexander Usyskin /* PCH8 Lynx Point with quirk for SPS Firmware exclusion */
1687f76d77f5STomas Winkler static const struct mei_cfg mei_me_pch8_sps_4_cfg = {
1688edca5ea3SAlexander Usyskin 	MEI_CFG_PCH8_HFS,
1689f8204f0dSAlexander Usyskin 	MEI_CFG_FW_VER_SUPP,
1690f76d77f5STomas Winkler 	MEI_CFG_FW_SPS_4,
1691f76d77f5STomas Winkler };
1692f76d77f5STomas Winkler 
1693f76d77f5STomas Winkler /* LBG with quirk for SPS (4.0) Firmware exclusion */
1694f76d77f5STomas Winkler static const struct mei_cfg mei_me_pch12_sps_4_cfg = {
1695f76d77f5STomas Winkler 	MEI_CFG_PCH8_HFS,
1696f76d77f5STomas Winkler 	MEI_CFG_FW_VER_SUPP,
1697f76d77f5STomas Winkler 	MEI_CFG_FW_SPS_4,
1698c919951dSTomas Winkler };
1699c919951dSTomas Winkler 
17007026a5fdSAlexander Usyskin /* Cannon Lake and newer devices */
17017026a5fdSAlexander Usyskin static const struct mei_cfg mei_me_pch12_cfg = {
17027026a5fdSAlexander Usyskin 	MEI_CFG_PCH8_HFS,
1703f8204f0dSAlexander Usyskin 	MEI_CFG_FW_VER_SUPP,
17047026a5fdSAlexander Usyskin 	MEI_CFG_DMA_128,
17057026a5fdSAlexander Usyskin };
17067026a5fdSAlexander Usyskin 
1707f76d77f5STomas Winkler /* Cannon Lake with quirk for SPS 5.0 and newer Firmware exclusion */
1708d76bc820STomas Winkler static const struct mei_cfg mei_me_pch12_sps_cfg = {
1709d76bc820STomas Winkler 	MEI_CFG_PCH8_HFS,
1710d76bc820STomas Winkler 	MEI_CFG_FW_VER_SUPP,
1711f76d77f5STomas Winkler 	MEI_CFG_DMA_128,
1712ccdf6f80SAlexander Usyskin 	MEI_CFG_FW_SPS_IGN,
1713f76d77f5STomas Winkler };
1714f76d77f5STomas Winkler 
17152f79d3d1SAlexander Usyskin /* Cannon Lake itouch with quirk for SPS 5.0 and newer Firmware exclusion
17162f79d3d1SAlexander Usyskin  * w/o DMA support.
1717f76d77f5STomas Winkler  */
17182f79d3d1SAlexander Usyskin static const struct mei_cfg mei_me_pch12_itouch_sps_cfg = {
17192f79d3d1SAlexander Usyskin 	MEI_CFG_KIND_ITOUCH,
1720f76d77f5STomas Winkler 	MEI_CFG_PCH8_HFS,
1721f76d77f5STomas Winkler 	MEI_CFG_FW_VER_SUPP,
1722ccdf6f80SAlexander Usyskin 	MEI_CFG_FW_SPS_IGN,
1723d76bc820STomas Winkler };
1724d76bc820STomas Winkler 
172552f6efdfSAlexander Usyskin /* Tiger Lake and newer devices */
172652f6efdfSAlexander Usyskin static const struct mei_cfg mei_me_pch15_cfg = {
172752f6efdfSAlexander Usyskin 	MEI_CFG_PCH8_HFS,
172852f6efdfSAlexander Usyskin 	MEI_CFG_FW_VER_SUPP,
172952f6efdfSAlexander Usyskin 	MEI_CFG_DMA_128,
173052f6efdfSAlexander Usyskin 	MEI_CFG_TRC,
173152f6efdfSAlexander Usyskin };
173252f6efdfSAlexander Usyskin 
17338c289ea0SAlexander Usyskin /* Tiger Lake with quirk for SPS 5.0 and newer Firmware exclusion */
17348c289ea0SAlexander Usyskin static const struct mei_cfg mei_me_pch15_sps_cfg = {
17358c289ea0SAlexander Usyskin 	MEI_CFG_PCH8_HFS,
17368c289ea0SAlexander Usyskin 	MEI_CFG_FW_VER_SUPP,
17378c289ea0SAlexander Usyskin 	MEI_CFG_DMA_128,
17388c289ea0SAlexander Usyskin 	MEI_CFG_TRC,
1739ccdf6f80SAlexander Usyskin 	MEI_CFG_FW_SPS_IGN,
17408c289ea0SAlexander Usyskin };
17418c289ea0SAlexander Usyskin 
1742a98c30fdSTomas Winkler /* Graphics System Controller */
1743a98c30fdSTomas Winkler static const struct mei_cfg mei_me_gsc_cfg = {
1744a98c30fdSTomas Winkler 	MEI_CFG_TYPE_GSC,
1745a98c30fdSTomas Winkler 	MEI_CFG_PCH8_HFS,
17461bc22fc5SAlexander Usyskin 	MEI_CFG_FW_VER_SUPP,
1747a98c30fdSTomas Winkler };
1748a98c30fdSTomas Winkler 
1749a98c30fdSTomas Winkler /* Graphics System Controller Firmware Interface */
1750a98c30fdSTomas Winkler static const struct mei_cfg mei_me_gscfi_cfg = {
1751a98c30fdSTomas Winkler 	MEI_CFG_TYPE_GSCFI,
1752a98c30fdSTomas Winkler 	MEI_CFG_PCH8_HFS,
17531bc22fc5SAlexander Usyskin 	MEI_CFG_FW_VER_SUPP,
1754a98c30fdSTomas Winkler };
1755a98c30fdSTomas Winkler 
1756f5ac3c49STomas Winkler /*
1757f5ac3c49STomas Winkler  * mei_cfg_list - A list of platform platform specific configurations.
1758f5ac3c49STomas Winkler  * Note: has to be synchronized with  enum mei_cfg_idx.
1759f5ac3c49STomas Winkler  */
1760f5ac3c49STomas Winkler static const struct mei_cfg *const mei_cfg_list[] = {
1761f5ac3c49STomas Winkler 	[MEI_ME_UNDEF_CFG] = NULL,
1762f5ac3c49STomas Winkler 	[MEI_ME_ICH_CFG] = &mei_me_ich_cfg,
1763f5ac3c49STomas Winkler 	[MEI_ME_ICH10_CFG] = &mei_me_ich10_cfg,
1764f8204f0dSAlexander Usyskin 	[MEI_ME_PCH6_CFG] = &mei_me_pch6_cfg,
1765f8204f0dSAlexander Usyskin 	[MEI_ME_PCH7_CFG] = &mei_me_pch7_cfg,
1766f5ac3c49STomas Winkler 	[MEI_ME_PCH_CPT_PBG_CFG] = &mei_me_pch_cpt_pbg_cfg,
1767f5ac3c49STomas Winkler 	[MEI_ME_PCH8_CFG] = &mei_me_pch8_cfg,
17682f79d3d1SAlexander Usyskin 	[MEI_ME_PCH8_ITOUCH_CFG] = &mei_me_pch8_itouch_cfg,
1769f76d77f5STomas Winkler 	[MEI_ME_PCH8_SPS_4_CFG] = &mei_me_pch8_sps_4_cfg,
17707026a5fdSAlexander Usyskin 	[MEI_ME_PCH12_CFG] = &mei_me_pch12_cfg,
1771f76d77f5STomas Winkler 	[MEI_ME_PCH12_SPS_4_CFG] = &mei_me_pch12_sps_4_cfg,
1772d76bc820STomas Winkler 	[MEI_ME_PCH12_SPS_CFG] = &mei_me_pch12_sps_cfg,
17732f79d3d1SAlexander Usyskin 	[MEI_ME_PCH12_SPS_ITOUCH_CFG] = &mei_me_pch12_itouch_sps_cfg,
177452f6efdfSAlexander Usyskin 	[MEI_ME_PCH15_CFG] = &mei_me_pch15_cfg,
17758c289ea0SAlexander Usyskin 	[MEI_ME_PCH15_SPS_CFG] = &mei_me_pch15_sps_cfg,
1776a98c30fdSTomas Winkler 	[MEI_ME_GSC_CFG] = &mei_me_gsc_cfg,
1777a98c30fdSTomas Winkler 	[MEI_ME_GSCFI_CFG] = &mei_me_gscfi_cfg,
1778f5ac3c49STomas Winkler };
1779f5ac3c49STomas Winkler 
mei_me_get_cfg(kernel_ulong_t idx)1780f5ac3c49STomas Winkler const struct mei_cfg *mei_me_get_cfg(kernel_ulong_t idx)
1781f5ac3c49STomas Winkler {
1782f5ac3c49STomas Winkler 	BUILD_BUG_ON(ARRAY_SIZE(mei_cfg_list) != MEI_ME_NUM_CFG);
1783f5ac3c49STomas Winkler 
1784f5ac3c49STomas Winkler 	if (idx >= MEI_ME_NUM_CFG)
1785f5ac3c49STomas Winkler 		return NULL;
1786f5ac3c49STomas Winkler 
1787f5ac3c49STomas Winkler 	return mei_cfg_list[idx];
1788a98c30fdSTomas Winkler }
1789a98c30fdSTomas Winkler EXPORT_SYMBOL_GPL(mei_me_get_cfg);
1790f5ac3c49STomas Winkler 
179152c34561STomas Winkler /**
1792393b148fSMasanari Iida  * mei_me_dev_init - allocates and initializes the mei device structure
179352c34561STomas Winkler  *
1794907b471cSTomas Winkler  * @parent: device associated with physical device (pci/platform)
17958d929d48SAlexander Usyskin  * @cfg: per device generation config
179695953618SAlexander Usyskin  * @slow_fw: configure longer timeouts as FW is slow
179752c34561STomas Winkler  *
1798f8a09605STomas Winkler  * Return: The mei_device pointer on success, NULL on failure.
179952c34561STomas Winkler  */
mei_me_dev_init(struct device * parent,const struct mei_cfg * cfg,bool slow_fw)1800907b471cSTomas Winkler struct mei_device *mei_me_dev_init(struct device *parent,
180195953618SAlexander Usyskin 				   const struct mei_cfg *cfg, bool slow_fw)
180252c34561STomas Winkler {
180352c34561STomas Winkler 	struct mei_device *dev;
18044ad96db6STomas Winkler 	struct mei_me_hw *hw;
1805ce0925e8STomas Winkler 	int i;
180652c34561STomas Winkler 
1807c614970eSTomas Winkler 	dev = devm_kzalloc(parent, sizeof(*dev) + sizeof(*hw), GFP_KERNEL);
180852c34561STomas Winkler 	if (!dev)
180952c34561STomas Winkler 		return NULL;
1810ce0925e8STomas Winkler 
18114ad96db6STomas Winkler 	hw = to_me_hw(dev);
181252c34561STomas Winkler 
1813ce0925e8STomas Winkler 	for (i = 0; i < DMA_DSCR_NUM; i++)
1814ce0925e8STomas Winkler 		dev->dr_dscr[i].size = cfg->dma_size[i];
1815ce0925e8STomas Winkler 
181695953618SAlexander Usyskin 	mei_device_init(dev, parent, slow_fw, &mei_me_hw_ops);
18174ad96db6STomas Winkler 	hw->cfg = cfg;
1818ce0925e8STomas Winkler 
1819f8204f0dSAlexander Usyskin 	dev->fw_f_fw_ver_supported = cfg->fw_ver_supported;
1820f8204f0dSAlexander Usyskin 
18212f79d3d1SAlexander Usyskin 	dev->kind = cfg->kind;
18222f79d3d1SAlexander Usyskin 
182352c34561STomas Winkler 	return dev;
182452c34561STomas Winkler }
1825a98c30fdSTomas Winkler EXPORT_SYMBOL_GPL(mei_me_dev_init);
1826