mei: gsc: use polling instead of interrupts
[linux-2.6-block.git] / drivers / misc / mei / gsc-me.c
CommitLineData
a98c30fd
TW
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright(c) 2019-2022, Intel Corporation. All rights reserved.
4 *
5 * Intel Management Engine Interface (Intel MEI) Linux driver
6 */
7
8#include <linux/module.h>
9#include <linux/mei_aux.h>
10#include <linux/device.h>
11#include <linux/irqreturn.h>
12#include <linux/jiffies.h>
13#include <linux/ktime.h>
14#include <linux/delay.h>
15#include <linux/pm_runtime.h>
5b063995 16#include <linux/kthread.h>
a98c30fd
TW
17
18#include "mei_dev.h"
19#include "hw-me.h"
20#include "hw-me-regs.h"
21
22#include "mei-trace.h"
23
24#define MEI_GSC_RPM_TIMEOUT 500
25
26static int mei_gsc_read_hfs(const struct mei_device *dev, int where, u32 *val)
27{
28 struct mei_me_hw *hw = to_me_hw(dev);
29
30 *val = ioread32(hw->mem_addr + where + 0xC00);
31
32 return 0;
33}
34
35static int mei_gsc_probe(struct auxiliary_device *aux_dev,
36 const struct auxiliary_device_id *aux_dev_id)
37{
38 struct mei_aux_device *adev = auxiliary_dev_to_mei_aux_dev(aux_dev);
39 struct mei_device *dev;
40 struct mei_me_hw *hw;
41 struct device *device;
42 const struct mei_cfg *cfg;
43 int ret;
44
45 cfg = mei_me_get_cfg(aux_dev_id->driver_data);
46 if (!cfg)
47 return -ENODEV;
48
49 device = &aux_dev->dev;
50
51 dev = mei_me_dev_init(device, cfg);
52 if (!dev) {
53 ret = -ENOMEM;
54 goto err;
55 }
56
57 hw = to_me_hw(dev);
58 hw->mem_addr = devm_ioremap_resource(device, &adev->bar);
59 if (IS_ERR(hw->mem_addr)) {
60 dev_err(device, "mmio not mapped\n");
61 ret = PTR_ERR(hw->mem_addr);
62 goto err;
63 }
64
65 hw->irq = adev->irq;
66 hw->read_fws = mei_gsc_read_hfs;
67
68 dev_set_drvdata(device, dev);
69
5b063995
TW
70 /* use polling */
71 if (mei_me_hw_use_polling(hw)) {
72 mei_disable_interrupts(dev);
73 mei_clear_interrupts(dev);
74 init_waitqueue_head(&hw->wait_active);
75 hw->is_active = true; /* start in active mode for initialization */
76 hw->polling_thread = kthread_run(mei_me_polling_thread, dev,
77 "kmegscirqd/%s", dev_name(device));
78 if (IS_ERR(hw->polling_thread)) {
79 ret = PTR_ERR(hw->polling_thread);
80 dev_err(device, "unable to create kernel thread: %d\n", ret);
81 goto err;
82 }
83 } else {
84 ret = devm_request_threaded_irq(device, hw->irq,
85 mei_me_irq_quick_handler,
86 mei_me_irq_thread_handler,
87 IRQF_ONESHOT, KBUILD_MODNAME, dev);
88 if (ret) {
89 dev_err(device, "irq register failed %d\n", ret);
90 goto err;
91 }
a98c30fd
TW
92 }
93
94 pm_runtime_get_noresume(device);
95 pm_runtime_set_active(device);
96 pm_runtime_enable(device);
97
ce97126d
AU
98 /* Continue to char device setup in spite of firmware handshake failure.
99 * In order to provide access to the firmware status registers to the user
100 * space via sysfs.
101 */
102 if (mei_start(dev))
103 dev_warn(device, "init hw failure.\n");
a98c30fd
TW
104
105 pm_runtime_set_autosuspend_delay(device, MEI_GSC_RPM_TIMEOUT);
106 pm_runtime_use_autosuspend(device);
107
108 ret = mei_register(dev, device);
109 if (ret)
110 goto register_err;
111
112 pm_runtime_put_noidle(device);
113 return 0;
114
115register_err:
116 mei_stop(dev);
5b063995
TW
117 if (!mei_me_hw_use_polling(hw))
118 devm_free_irq(device, hw->irq, dev);
a98c30fd
TW
119
120err:
121 dev_err(device, "probe failed: %d\n", ret);
122 dev_set_drvdata(device, NULL);
123 return ret;
124}
125
126static void mei_gsc_remove(struct auxiliary_device *aux_dev)
127{
128 struct mei_device *dev;
129 struct mei_me_hw *hw;
130
131 dev = dev_get_drvdata(&aux_dev->dev);
132 if (!dev)
133 return;
134
135 hw = to_me_hw(dev);
136
137 mei_stop(dev);
138
5b063995
TW
139 hw = to_me_hw(dev);
140 if (mei_me_hw_use_polling(hw))
141 kthread_stop(hw->polling_thread);
142
a98c30fd
TW
143 mei_deregister(dev);
144
145 pm_runtime_disable(&aux_dev->dev);
146
147 mei_disable_interrupts(dev);
5b063995
TW
148 if (!mei_me_hw_use_polling(hw))
149 devm_free_irq(&aux_dev->dev, hw->irq, dev);
a98c30fd
TW
150}
151
152static int __maybe_unused mei_gsc_pm_suspend(struct device *device)
153{
154 struct mei_device *dev = dev_get_drvdata(device);
155
156 if (!dev)
157 return -ENODEV;
158
159 mei_stop(dev);
160
161 mei_disable_interrupts(dev);
162
163 return 0;
164}
165
166static int __maybe_unused mei_gsc_pm_resume(struct device *device)
167{
168 struct mei_device *dev = dev_get_drvdata(device);
169 int err;
170
171 if (!dev)
172 return -ENODEV;
173
174 err = mei_restart(dev);
175 if (err)
176 return err;
177
178 /* Start timer if stopped in suspend */
179 schedule_delayed_work(&dev->timer_work, HZ);
180
181 return 0;
182}
183
ad10a354
TW
184static int __maybe_unused mei_gsc_pm_runtime_idle(struct device *device)
185{
186 struct mei_device *dev = dev_get_drvdata(device);
187
188 if (!dev)
189 return -ENODEV;
190 if (mei_write_is_idle(dev))
191 pm_runtime_autosuspend(device);
192
193 return -EBUSY;
194}
195
196static int __maybe_unused mei_gsc_pm_runtime_suspend(struct device *device)
197{
198 struct mei_device *dev = dev_get_drvdata(device);
199 struct mei_me_hw *hw;
200 int ret;
201
202 if (!dev)
203 return -ENODEV;
204
205 mutex_lock(&dev->device_lock);
206
207 if (mei_write_is_idle(dev)) {
208 hw = to_me_hw(dev);
209 hw->pg_state = MEI_PG_ON;
5b063995
TW
210
211 if (mei_me_hw_use_polling(hw))
212 hw->is_active = false;
ad10a354
TW
213 ret = 0;
214 } else {
215 ret = -EAGAIN;
216 }
217
218 mutex_unlock(&dev->device_lock);
219
220 return ret;
221}
222
223static int __maybe_unused mei_gsc_pm_runtime_resume(struct device *device)
224{
225 struct mei_device *dev = dev_get_drvdata(device);
226 struct mei_me_hw *hw;
227 irqreturn_t irq_ret;
228
229 if (!dev)
230 return -ENODEV;
231
232 mutex_lock(&dev->device_lock);
233
234 hw = to_me_hw(dev);
235 hw->pg_state = MEI_PG_OFF;
236
5b063995
TW
237 if (mei_me_hw_use_polling(hw)) {
238 hw->is_active = true;
239 wake_up(&hw->wait_active);
240 }
241
ad10a354
TW
242 mutex_unlock(&dev->device_lock);
243
244 irq_ret = mei_me_irq_thread_handler(1, dev);
245 if (irq_ret != IRQ_HANDLED)
246 dev_err(dev->dev, "thread handler fail %d\n", irq_ret);
247
248 return 0;
249}
250
251static const struct dev_pm_ops mei_gsc_pm_ops = {
252 SET_SYSTEM_SLEEP_PM_OPS(mei_gsc_pm_suspend,
253 mei_gsc_pm_resume)
254 SET_RUNTIME_PM_OPS(mei_gsc_pm_runtime_suspend,
255 mei_gsc_pm_runtime_resume,
256 mei_gsc_pm_runtime_idle)
257};
a98c30fd
TW
258
259static const struct auxiliary_device_id mei_gsc_id_table[] = {
260 {
261 .name = "i915.mei-gsc",
262 .driver_data = MEI_ME_GSC_CFG,
263
264 },
265 {
266 .name = "i915.mei-gscfi",
267 .driver_data = MEI_ME_GSCFI_CFG,
268 },
269 {
270 /* sentinel */
271 }
272};
273MODULE_DEVICE_TABLE(auxiliary, mei_gsc_id_table);
274
275static struct auxiliary_driver mei_gsc_driver = {
276 .probe = mei_gsc_probe,
277 .remove = mei_gsc_remove,
278 .driver = {
279 /* auxiliary_driver_register() sets .name to be the modname */
280 .pm = &mei_gsc_pm_ops,
281 },
282 .id_table = mei_gsc_id_table
283};
284module_auxiliary_driver(mei_gsc_driver);
285
286MODULE_AUTHOR("Intel Corporation");
287MODULE_ALIAS("auxiliary:i915.mei-gsc");
288MODULE_ALIAS("auxiliary:i915.mei-gscfi");
289MODULE_LICENSE("GPL");