1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2021, Intel Corporation. All rights reserved.
4 * Intel Management Engine Interface (Intel MEI) Linux driver
6 #include <linux/acpi.h>
7 #include <linux/delay.h>
8 #include <linux/gpio.h>
9 #include <linux/interrupt.h>
10 #include <linux/irq.h>
11 #include <linux/mei.h>
12 #include <linux/module.h>
13 #include <linux/pm_runtime.h>
14 #include <linux/spi/spi.h>
21 static const struct acpi_gpio_params wakeuphost_gpio
= { 0, 0, false };
22 static const struct acpi_gpio_params wakeuphostint_gpio
= { 1, 0, false };
23 static const struct acpi_gpio_params resetfw_gpio
= { 2, 0, false };
24 static const struct acpi_gpio_params wakeupfw
= { 3, 0, false };
25 static const struct acpi_gpio_mapping mei_vsc_acpi_gpios
[] = {
26 { "wakeuphost-gpios", &wakeuphost_gpio
, 1 },
27 { "wakeuphostint-gpios", &wakeuphostint_gpio
, 1 },
28 { "resetfw-gpios", &resetfw_gpio
, 1 },
29 { "wakeupfw-gpios", &wakeupfw
, 1 },
33 static int mei_vsc_probe(struct spi_device
*spi
)
35 struct mei_vsc_hw
*hw
;
36 struct mei_device
*dev
;
39 dev
= mei_vsc_dev_init(&spi
->dev
);
44 mutex_init(&hw
->mutex
);
45 init_waitqueue_head(&hw
->xfer_wait
);
47 spi_set_drvdata(spi
, dev
);
49 ret
= devm_acpi_dev_add_driver_gpios(&spi
->dev
, mei_vsc_acpi_gpios
);
51 dev_err(&spi
->dev
, "%s: fail to add gpio\n", __func__
);
55 hw
->wakeuphost
= devm_gpiod_get(&spi
->dev
, "wakeuphost", GPIOD_IN
);
56 if (IS_ERR(hw
->wakeuphost
)) {
57 dev_err(&spi
->dev
, "gpio get irq failed\n");
60 hw
->resetfw
= devm_gpiod_get(&spi
->dev
, "resetfw", GPIOD_OUT_HIGH
);
61 if (IS_ERR(hw
->resetfw
)) {
62 dev_err(&spi
->dev
, "gpio get resetfw failed\n");
65 hw
->wakeupfw
= devm_gpiod_get(&spi
->dev
, "wakeupfw", GPIOD_OUT_HIGH
);
66 if (IS_ERR(hw
->wakeupfw
)) {
67 dev_err(&spi
->dev
, "gpio get wakeupfw failed\n");
71 ret
= acpi_dev_gpio_irq_get_by(ACPI_COMPANION(&spi
->dev
),
72 "wakeuphostint-gpios", 0);
76 hw
->wakeuphostint
= ret
;
77 irq_set_status_flags(hw
->wakeuphostint
, IRQ_DISABLE_UNLAZY
);
78 ret
= request_threaded_irq(hw
->wakeuphostint
, mei_vsc_irq_quick_handler
,
79 mei_vsc_irq_thread_handler
,
80 IRQF_TRIGGER_FALLING
| IRQF_ONESHOT
,
83 dev_err(&spi
->dev
, "init hw failure.\n");
88 ret
= mei_register(dev
, &spi
->dev
);
92 pm_runtime_enable(dev
->dev
);
93 dev_dbg(&spi
->dev
, "initialization successful.\n");
100 mei_disable_interrupts(dev
);
101 free_irq(hw
->wakeuphostint
, dev
);
105 static int __maybe_unused
mei_vsc_suspend(struct device
*device
)
107 struct spi_device
*spi
= to_spi_device(device
);
108 struct mei_device
*dev
= spi_get_drvdata(spi
);
109 struct mei_vsc_hw
*hw
= to_vsc_hw(dev
);
114 dev_dbg(dev
->dev
, "%s\n", __func__
);
116 hw
->disconnect
= true;
118 mei_disable_interrupts(dev
);
119 free_irq(hw
->wakeuphostint
, dev
);
123 static int __maybe_unused
mei_vsc_resume(struct device
*device
)
125 struct spi_device
*spi
= to_spi_device(device
);
126 struct mei_device
*dev
= spi_get_drvdata(spi
);
127 struct mei_vsc_hw
*hw
= to_vsc_hw(dev
);
130 dev_dbg(dev
->dev
, "%s\n", __func__
);
131 irq_set_status_flags(hw
->wakeuphostint
, IRQ_DISABLE_UNLAZY
);
132 ret
= request_threaded_irq(hw
->wakeuphostint
, mei_vsc_irq_quick_handler
,
133 mei_vsc_irq_thread_handler
,
134 IRQF_TRIGGER_FALLING
| IRQF_ONESHOT
,
135 KBUILD_MODNAME
, dev
);
137 dev_err(device
, "request_threaded_irq failed: irq = %d.\n",
142 hw
->disconnect
= false;
143 ret
= mei_restart(dev
);
147 /* Start timer if stopped in suspend */
148 schedule_delayed_work(&dev
->timer_work
, HZ
);
152 static int mei_vsc_remove(struct spi_device
*spi
)
154 struct mei_device
*dev
= spi_get_drvdata(spi
);
155 struct mei_vsc_hw
*hw
= to_vsc_hw(dev
);
157 dev_info(&spi
->dev
, "%s %d", __func__
, hw
->wakeuphostint
);
159 pm_runtime_disable(dev
->dev
);
160 hw
->disconnect
= true;
162 mei_disable_interrupts(dev
);
163 free_irq(hw
->wakeuphostint
, dev
);
165 mutex_destroy(&hw
->mutex
);
170 * mei_vsc_shutdown - Device Removal Routine
172 * @spi: SPI device structure
174 * mei_vsc_shutdown is called from the reboot notifier
175 * it's a simplified version of remove so we go down
178 static void mei_vsc_shutdown(struct spi_device
*spi
)
180 struct mei_device
*dev
= spi_get_drvdata(spi
);
181 struct mei_vsc_hw
*hw
= to_vsc_hw(dev
);
183 dev_dbg(dev
->dev
, "shutdown\n");
184 hw
->disconnect
= true;
187 mei_disable_interrupts(dev
);
188 free_irq(hw
->wakeuphostint
, dev
);
191 static const struct dev_pm_ops mei_vsc_pm_ops
= {
193 SET_SYSTEM_SLEEP_PM_OPS(mei_vsc_suspend
, mei_vsc_resume
)
196 static const struct acpi_device_id mei_vsc_acpi_ids
[] = {
201 MODULE_DEVICE_TABLE(acpi
, mei_vsc_acpi_ids
);
203 static struct spi_driver mei_vsc_driver
= {
205 .name
= KBUILD_MODNAME
,
206 .acpi_match_table
= ACPI_PTR(mei_vsc_acpi_ids
),
207 .pm
= &mei_vsc_pm_ops
,
209 .probe
= mei_vsc_probe
,
210 .remove
= mei_vsc_remove
,
211 .shutdown
= mei_vsc_shutdown
,
212 .driver
.probe_type
= PROBE_PREFER_ASYNCHRONOUS
,
214 module_spi_driver(mei_vsc_driver
);
216 MODULE_AUTHOR("Ye Xiang <xiang.ye@intel.com>");
217 MODULE_DESCRIPTION("Intel MEI VSC driver");
218 MODULE_LICENSE("GPL v2");