]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blob - drivers/misc/mei/spi-vsc.c
UBUNTU: SAUCE: mei_vsc: add ACPI HID for ADL
[mirror_ubuntu-jammy-kernel.git] / drivers / misc / mei / spi-vsc.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2021, Intel Corporation. All rights reserved.
4 * Intel Management Engine Interface (Intel MEI) Linux driver
5 */
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>
15
16 #include "client.h"
17 #include "hw-vsc.h"
18 #include "mei_dev.h"
19
20 /* gpio resources*/
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 },
30 {}
31 };
32
33 static int mei_vsc_probe(struct spi_device *spi)
34 {
35 struct mei_vsc_hw *hw;
36 struct mei_device *dev;
37 int ret;
38
39 dev = mei_vsc_dev_init(&spi->dev);
40 if (!dev)
41 return -ENOMEM;
42
43 hw = to_vsc_hw(dev);
44 mutex_init(&hw->mutex);
45 init_waitqueue_head(&hw->xfer_wait);
46 hw->spi = spi;
47 spi_set_drvdata(spi, dev);
48
49 ret = devm_acpi_dev_add_driver_gpios(&spi->dev, mei_vsc_acpi_gpios);
50 if (ret) {
51 dev_err(&spi->dev, "%s: fail to add gpio\n", __func__);
52 return -EBUSY;
53 }
54
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");
58 return -EINVAL;
59 }
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");
63 return -EINVAL;
64 }
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");
68 return -EINVAL;
69 }
70
71 ret = acpi_dev_gpio_irq_get_by(ACPI_COMPANION(&spi->dev),
72 "wakeuphostint-gpios", 0);
73 if (ret < 0)
74 return ret;
75
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,
81 KBUILD_MODNAME, dev);
82 if (mei_start(dev)) {
83 dev_err(&spi->dev, "init hw failure.\n");
84 ret = -ENODEV;
85 goto release_irq;
86 }
87
88 ret = mei_register(dev, &spi->dev);
89 if (ret)
90 goto stop;
91
92 pm_runtime_enable(dev->dev);
93 dev_dbg(&spi->dev, "initialization successful.\n");
94 return 0;
95
96 stop:
97 mei_stop(dev);
98 release_irq:
99 mei_cancel_work(dev);
100 mei_disable_interrupts(dev);
101 free_irq(hw->wakeuphostint, dev);
102 return ret;
103 }
104
105 static int __maybe_unused mei_vsc_suspend(struct device *device)
106 {
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);
110
111 if (!dev)
112 return -ENODEV;
113
114 dev_dbg(dev->dev, "%s\n", __func__);
115
116 hw->disconnect = true;
117 mei_stop(dev);
118 mei_disable_interrupts(dev);
119 free_irq(hw->wakeuphostint, dev);
120 return 0;
121 }
122
123 static int __maybe_unused mei_vsc_resume(struct device *device)
124 {
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);
128 int ret;
129
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);
136 if (ret) {
137 dev_err(device, "request_threaded_irq failed: irq = %d.\n",
138 hw->wakeuphostint);
139 return ret;
140 }
141
142 hw->disconnect = false;
143 ret = mei_restart(dev);
144 if (ret)
145 return ret;
146
147 /* Start timer if stopped in suspend */
148 schedule_delayed_work(&dev->timer_work, HZ);
149 return 0;
150 }
151
152 static int mei_vsc_remove(struct spi_device *spi)
153 {
154 struct mei_device *dev = spi_get_drvdata(spi);
155 struct mei_vsc_hw *hw = to_vsc_hw(dev);
156
157 dev_info(&spi->dev, "%s %d", __func__, hw->wakeuphostint);
158
159 pm_runtime_disable(dev->dev);
160 hw->disconnect = true;
161 mei_stop(dev);
162 mei_disable_interrupts(dev);
163 free_irq(hw->wakeuphostint, dev);
164 mei_deregister(dev);
165 mutex_destroy(&hw->mutex);
166 return 0;
167 }
168
169 /**
170 * mei_vsc_shutdown - Device Removal Routine
171 *
172 * @spi: SPI device structure
173 *
174 * mei_vsc_shutdown is called from the reboot notifier
175 * it's a simplified version of remove so we go down
176 * faster.
177 */
178 static void mei_vsc_shutdown(struct spi_device *spi)
179 {
180 struct mei_device *dev = spi_get_drvdata(spi);
181 struct mei_vsc_hw *hw = to_vsc_hw(dev);
182
183 dev_dbg(dev->dev, "shutdown\n");
184 hw->disconnect = true;
185 mei_stop(dev);
186
187 mei_disable_interrupts(dev);
188 free_irq(hw->wakeuphostint, dev);
189 }
190
191 static const struct dev_pm_ops mei_vsc_pm_ops = {
192
193 SET_SYSTEM_SLEEP_PM_OPS(mei_vsc_suspend, mei_vsc_resume)
194 };
195
196 static const struct acpi_device_id mei_vsc_acpi_ids[] = {
197 { "INTC1058", 1 },
198 { "INTC1094", 1 },
199 {},
200 };
201 MODULE_DEVICE_TABLE(acpi, mei_vsc_acpi_ids);
202
203 static struct spi_driver mei_vsc_driver = {
204 .driver = {
205 .name = KBUILD_MODNAME,
206 .acpi_match_table = ACPI_PTR(mei_vsc_acpi_ids),
207 .pm = &mei_vsc_pm_ops,
208 },
209 .probe = mei_vsc_probe,
210 .remove = mei_vsc_remove,
211 .shutdown = mei_vsc_shutdown,
212 .driver.probe_type = PROBE_PREFER_ASYNCHRONOUS,
213 };
214 module_spi_driver(mei_vsc_driver);
215
216 MODULE_AUTHOR("Ye Xiang <xiang.ye@intel.com>");
217 MODULE_DESCRIPTION("Intel MEI VSC driver");
218 MODULE_LICENSE("GPL v2");