1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * UCSI ACPI driver
4 *
5 * Copyright (C) 2017, Intel Corporation
6 * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
7 */
8
9 #include <linux/platform_device.h>
10 #include <linux/module.h>
11 #include <linux/acpi.h>
12 #include <linux/dmi.h>
13
14 #include "ucsi.h"
15
16 #define UCSI_DSM_UUID "6f8398c2-7ca4-11e4-ad36-631042b5008f"
17 #define UCSI_DSM_FUNC_WRITE 1
18 #define UCSI_DSM_FUNC_READ 2
19
20 struct ucsi_acpi {
21 struct device *dev;
22 struct ucsi *ucsi;
23 void *base;
24 bool check_bogus_event;
25 guid_t guid;
26 u64 cmd;
27 };
28
ucsi_acpi_dsm(struct ucsi_acpi * ua,int func)29 static int ucsi_acpi_dsm(struct ucsi_acpi *ua, int func)
30 {
31 union acpi_object *obj;
32
33 obj = acpi_evaluate_dsm(ACPI_HANDLE(ua->dev), &ua->guid, 1, func,
34 NULL);
35 if (!obj) {
36 dev_err(ua->dev, "%s: failed to evaluate _DSM %d\n",
37 __func__, func);
38 return -EIO;
39 }
40
41 ACPI_FREE(obj);
42 return 0;
43 }
44
ucsi_acpi_read_version(struct ucsi * ucsi,u16 * version)45 static int ucsi_acpi_read_version(struct ucsi *ucsi, u16 *version)
46 {
47 struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
48 int ret;
49
50 ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ);
51 if (ret)
52 return ret;
53
54 memcpy(version, ua->base + UCSI_VERSION, sizeof(*version));
55
56 return 0;
57 }
58
ucsi_acpi_read_cci(struct ucsi * ucsi,u32 * cci)59 static int ucsi_acpi_read_cci(struct ucsi *ucsi, u32 *cci)
60 {
61 struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
62 int ret;
63
64 ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ);
65 if (ret)
66 return ret;
67
68 memcpy(cci, ua->base + UCSI_CCI, sizeof(*cci));
69
70 return 0;
71 }
72
ucsi_acpi_read_message_in(struct ucsi * ucsi,void * val,size_t val_len)73 static int ucsi_acpi_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
74 {
75 struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
76 int ret;
77
78 ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ);
79 if (ret)
80 return ret;
81
82 memcpy(val, ua->base + UCSI_MESSAGE_IN, val_len);
83
84 return 0;
85 }
86
ucsi_acpi_async_control(struct ucsi * ucsi,u64 command)87 static int ucsi_acpi_async_control(struct ucsi *ucsi, u64 command)
88 {
89 struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
90
91 memcpy(ua->base + UCSI_CONTROL, &command, sizeof(command));
92 ua->cmd = command;
93
94 return ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_WRITE);
95 }
96
97 static const struct ucsi_operations ucsi_acpi_ops = {
98 .read_version = ucsi_acpi_read_version,
99 .read_cci = ucsi_acpi_read_cci,
100 .read_message_in = ucsi_acpi_read_message_in,
101 .sync_control = ucsi_sync_control_common,
102 .async_control = ucsi_acpi_async_control
103 };
104
105 static int
ucsi_zenbook_read_cci(struct ucsi * ucsi,u32 * cci)106 ucsi_zenbook_read_cci(struct ucsi *ucsi, u32 *cci)
107 {
108 struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
109 int ret;
110
111 if (UCSI_COMMAND(ua->cmd) == UCSI_PPM_RESET) {
112 ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ);
113 if (ret)
114 return ret;
115 }
116
117 memcpy(cci, ua->base + UCSI_CCI, sizeof(*cci));
118
119 return 0;
120 }
121
122 static int
ucsi_zenbook_read_message_in(struct ucsi * ucsi,void * val,size_t val_len)123 ucsi_zenbook_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
124 {
125 struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
126
127 /* UCSI_MESSAGE_IN is never read for PPM_RESET, return stored data */
128 memcpy(val, ua->base + UCSI_MESSAGE_IN, val_len);
129
130 return 0;
131 }
132
133 static const struct ucsi_operations ucsi_zenbook_ops = {
134 .read_version = ucsi_acpi_read_version,
135 .read_cci = ucsi_zenbook_read_cci,
136 .read_message_in = ucsi_zenbook_read_message_in,
137 .sync_control = ucsi_sync_control_common,
138 .async_control = ucsi_acpi_async_control
139 };
140
ucsi_gram_read_message_in(struct ucsi * ucsi,void * val,size_t val_len)141 static int ucsi_gram_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
142 {
143 u16 bogus_change = UCSI_CONSTAT_POWER_LEVEL_CHANGE |
144 UCSI_CONSTAT_PDOS_CHANGE;
145 struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
146 struct ucsi_connector_status *status;
147 int ret;
148
149 ret = ucsi_acpi_read_message_in(ucsi, val, val_len);
150 if (ret < 0)
151 return ret;
152
153 if (UCSI_COMMAND(ua->cmd) == UCSI_GET_CONNECTOR_STATUS &&
154 ua->check_bogus_event) {
155 status = (struct ucsi_connector_status *)val;
156
157 /* Clear the bogus change */
158 if (status->change == bogus_change)
159 status->change = 0;
160
161 ua->check_bogus_event = false;
162 }
163
164 return ret;
165 }
166
ucsi_gram_sync_control(struct ucsi * ucsi,u64 command)167 static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command)
168 {
169 struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
170 int ret;
171
172 ret = ucsi_sync_control_common(ucsi, command);
173 if (ret < 0)
174 return ret;
175
176 if (UCSI_COMMAND(ua->cmd) == UCSI_GET_PDOS &&
177 ua->cmd & UCSI_GET_PDOS_PARTNER_PDO(1) &&
178 ua->cmd & UCSI_GET_PDOS_SRC_PDOS)
179 ua->check_bogus_event = true;
180
181 return ret;
182 }
183
184 static const struct ucsi_operations ucsi_gram_ops = {
185 .read_version = ucsi_acpi_read_version,
186 .read_cci = ucsi_acpi_read_cci,
187 .read_message_in = ucsi_gram_read_message_in,
188 .sync_control = ucsi_gram_sync_control,
189 .async_control = ucsi_acpi_async_control
190 };
191
192 static const struct dmi_system_id ucsi_acpi_quirks[] = {
193 {
194 .matches = {
195 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
196 DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UA_UM325UA"),
197 },
198 .driver_data = (void *)&ucsi_zenbook_ops,
199 },
200 {
201 .matches = {
202 DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"),
203 DMI_MATCH(DMI_PRODUCT_FAMILY, "LG gram PC"),
204 DMI_MATCH(DMI_PRODUCT_NAME, "90Q"),
205 },
206 .driver_data = (void *)&ucsi_gram_ops,
207 },
208 { }
209 };
210
ucsi_acpi_notify(acpi_handle handle,u32 event,void * data)211 static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data)
212 {
213 struct ucsi_acpi *ua = data;
214 u32 cci;
215 int ret;
216
217 ret = ua->ucsi->ops->read_cci(ua->ucsi, &cci);
218 if (ret)
219 return;
220
221 ucsi_notify_common(ua->ucsi, cci);
222 }
223
ucsi_acpi_probe(struct platform_device * pdev)224 static int ucsi_acpi_probe(struct platform_device *pdev)
225 {
226 struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
227 const struct ucsi_operations *ops = &ucsi_acpi_ops;
228 const struct dmi_system_id *id;
229 struct ucsi_acpi *ua;
230 struct resource *res;
231 acpi_status status;
232 int ret;
233
234 if (adev->dep_unmet)
235 return -EPROBE_DEFER;
236
237 ua = devm_kzalloc(&pdev->dev, sizeof(*ua), GFP_KERNEL);
238 if (!ua)
239 return -ENOMEM;
240
241 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
242 if (!res) {
243 dev_err(&pdev->dev, "missing memory resource\n");
244 return -ENODEV;
245 }
246
247 ua->base = devm_memremap(&pdev->dev, res->start, resource_size(res), MEMREMAP_WB);
248 if (IS_ERR(ua->base))
249 return PTR_ERR(ua->base);
250
251 ret = guid_parse(UCSI_DSM_UUID, &ua->guid);
252 if (ret)
253 return ret;
254
255 ua->dev = &pdev->dev;
256
257 id = dmi_first_match(ucsi_acpi_quirks);
258 if (id)
259 ops = id->driver_data;
260
261 ua->ucsi = ucsi_create(&pdev->dev, ops);
262 if (IS_ERR(ua->ucsi))
263 return PTR_ERR(ua->ucsi);
264
265 ucsi_set_drvdata(ua->ucsi, ua);
266
267 status = acpi_install_notify_handler(ACPI_HANDLE(&pdev->dev),
268 ACPI_DEVICE_NOTIFY,
269 ucsi_acpi_notify, ua);
270 if (ACPI_FAILURE(status)) {
271 dev_err(&pdev->dev, "failed to install notify handler\n");
272 ucsi_destroy(ua->ucsi);
273 return -ENODEV;
274 }
275
276 ret = ucsi_register(ua->ucsi);
277 if (ret) {
278 acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev),
279 ACPI_DEVICE_NOTIFY,
280 ucsi_acpi_notify);
281 ucsi_destroy(ua->ucsi);
282 return ret;
283 }
284
285 platform_set_drvdata(pdev, ua);
286
287 return 0;
288 }
289
ucsi_acpi_remove(struct platform_device * pdev)290 static void ucsi_acpi_remove(struct platform_device *pdev)
291 {
292 struct ucsi_acpi *ua = platform_get_drvdata(pdev);
293
294 ucsi_unregister(ua->ucsi);
295 ucsi_destroy(ua->ucsi);
296
297 acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev), ACPI_DEVICE_NOTIFY,
298 ucsi_acpi_notify);
299 }
300
ucsi_acpi_resume(struct device * dev)301 static int ucsi_acpi_resume(struct device *dev)
302 {
303 struct ucsi_acpi *ua = dev_get_drvdata(dev);
304
305 return ucsi_resume(ua->ucsi);
306 }
307
308 static DEFINE_SIMPLE_DEV_PM_OPS(ucsi_acpi_pm_ops, NULL, ucsi_acpi_resume);
309
310 static const struct acpi_device_id ucsi_acpi_match[] = {
311 { "PNP0CA0", 0 },
312 { },
313 };
314 MODULE_DEVICE_TABLE(acpi, ucsi_acpi_match);
315
316 static struct platform_driver ucsi_acpi_platform_driver = {
317 .driver = {
318 .name = "ucsi_acpi",
319 .pm = pm_ptr(&ucsi_acpi_pm_ops),
320 .acpi_match_table = ACPI_PTR(ucsi_acpi_match),
321 },
322 .probe = ucsi_acpi_probe,
323 .remove_new = ucsi_acpi_remove,
324 };
325
326 module_platform_driver(ucsi_acpi_platform_driver);
327
328 MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
329 MODULE_LICENSE("GPL v2");
330 MODULE_DESCRIPTION("UCSI ACPI driver");
331