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
63 memcpy(cci, ua->base + UCSI_CCI, sizeof(*cci));
64
65 return 0;
66 }
67
ucsi_acpi_poll_cci(struct ucsi * ucsi,u32 * cci)68 static int ucsi_acpi_poll_cci(struct ucsi *ucsi, u32 *cci)
69 {
70 struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
71 int ret;
72
73 ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ);
74 if (ret)
75 return ret;
76
77 return ucsi_acpi_read_cci(ucsi, cci);
78 }
79
ucsi_acpi_read_message_in(struct ucsi * ucsi,void * val,size_t val_len)80 static int ucsi_acpi_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
81 {
82 struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
83
84 memcpy(val, ua->base + UCSI_MESSAGE_IN, val_len);
85
86 return 0;
87 }
88
ucsi_acpi_async_control(struct ucsi * ucsi,u64 command)89 static int ucsi_acpi_async_control(struct ucsi *ucsi, u64 command)
90 {
91 struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
92
93 memcpy(ua->base + UCSI_CONTROL, &command, sizeof(command));
94 ua->cmd = command;
95
96 return ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_WRITE);
97 }
98
99 static const struct ucsi_operations ucsi_acpi_ops = {
100 .read_version = ucsi_acpi_read_version,
101 .read_cci = ucsi_acpi_read_cci,
102 .poll_cci = ucsi_acpi_poll_cci,
103 .read_message_in = ucsi_acpi_read_message_in,
104 .sync_control = ucsi_sync_control_common,
105 .async_control = ucsi_acpi_async_control
106 };
107
ucsi_gram_read_message_in(struct ucsi * ucsi,void * val,size_t val_len)108 static int ucsi_gram_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
109 {
110 u16 bogus_change = UCSI_CONSTAT_POWER_LEVEL_CHANGE |
111 UCSI_CONSTAT_PDOS_CHANGE;
112 struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
113 struct ucsi_connector_status *status;
114 int ret;
115
116 ret = ucsi_acpi_read_message_in(ucsi, val, val_len);
117 if (ret < 0)
118 return ret;
119
120 if (UCSI_COMMAND(ua->cmd) == UCSI_GET_CONNECTOR_STATUS &&
121 ua->check_bogus_event) {
122 status = (struct ucsi_connector_status *)val;
123
124 /* Clear the bogus change */
125 if (status->change == bogus_change)
126 status->change = 0;
127
128 ua->check_bogus_event = false;
129 }
130
131 return ret;
132 }
133
ucsi_gram_sync_control(struct ucsi * ucsi,u64 command)134 static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command)
135 {
136 struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
137 int ret;
138
139 ret = ucsi_sync_control_common(ucsi, command);
140 if (ret < 0)
141 return ret;
142
143 if (UCSI_COMMAND(ua->cmd) == UCSI_GET_PDOS &&
144 ua->cmd & UCSI_GET_PDOS_PARTNER_PDO(1) &&
145 ua->cmd & UCSI_GET_PDOS_SRC_PDOS)
146 ua->check_bogus_event = true;
147
148 return ret;
149 }
150
151 static const struct ucsi_operations ucsi_gram_ops = {
152 .read_version = ucsi_acpi_read_version,
153 .read_cci = ucsi_acpi_read_cci,
154 .poll_cci = ucsi_acpi_poll_cci,
155 .read_message_in = ucsi_gram_read_message_in,
156 .sync_control = ucsi_gram_sync_control,
157 .async_control = ucsi_acpi_async_control
158 };
159
160 static const struct dmi_system_id ucsi_acpi_quirks[] = {
161 {
162 .matches = {
163 DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"),
164 DMI_MATCH(DMI_PRODUCT_FAMILY, "LG gram PC"),
165 DMI_MATCH(DMI_PRODUCT_NAME, "90Q"),
166 },
167 .driver_data = (void *)&ucsi_gram_ops,
168 },
169 { }
170 };
171
ucsi_acpi_notify(acpi_handle handle,u32 event,void * data)172 static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data)
173 {
174 struct ucsi_acpi *ua = data;
175 u32 cci;
176 int ret;
177
178 ret = ua->ucsi->ops->read_cci(ua->ucsi, &cci);
179 if (ret)
180 return;
181
182 ucsi_notify_common(ua->ucsi, cci);
183 }
184
ucsi_acpi_probe(struct platform_device * pdev)185 static int ucsi_acpi_probe(struct platform_device *pdev)
186 {
187 struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
188 const struct ucsi_operations *ops = &ucsi_acpi_ops;
189 const struct dmi_system_id *id;
190 struct ucsi_acpi *ua;
191 struct resource *res;
192 acpi_status status;
193 int ret;
194
195 if (adev->dep_unmet)
196 return -EPROBE_DEFER;
197
198 ua = devm_kzalloc(&pdev->dev, sizeof(*ua), GFP_KERNEL);
199 if (!ua)
200 return -ENOMEM;
201
202 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
203 if (!res) {
204 dev_err(&pdev->dev, "missing memory resource\n");
205 return -ENODEV;
206 }
207
208 ua->base = devm_memremap(&pdev->dev, res->start, resource_size(res), MEMREMAP_WB);
209 if (IS_ERR(ua->base))
210 return PTR_ERR(ua->base);
211
212 ret = guid_parse(UCSI_DSM_UUID, &ua->guid);
213 if (ret)
214 return ret;
215
216 ua->dev = &pdev->dev;
217
218 id = dmi_first_match(ucsi_acpi_quirks);
219 if (id)
220 ops = id->driver_data;
221
222 ua->ucsi = ucsi_create(&pdev->dev, ops);
223 if (IS_ERR(ua->ucsi))
224 return PTR_ERR(ua->ucsi);
225
226 ucsi_set_drvdata(ua->ucsi, ua);
227
228 status = acpi_install_notify_handler(ACPI_HANDLE(&pdev->dev),
229 ACPI_DEVICE_NOTIFY,
230 ucsi_acpi_notify, ua);
231 if (ACPI_FAILURE(status)) {
232 dev_err(&pdev->dev, "failed to install notify handler\n");
233 ucsi_destroy(ua->ucsi);
234 return -ENODEV;
235 }
236
237 ret = ucsi_register(ua->ucsi);
238 if (ret) {
239 acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev),
240 ACPI_DEVICE_NOTIFY,
241 ucsi_acpi_notify);
242 ucsi_destroy(ua->ucsi);
243 return ret;
244 }
245
246 platform_set_drvdata(pdev, ua);
247
248 return 0;
249 }
250
ucsi_acpi_remove(struct platform_device * pdev)251 static void ucsi_acpi_remove(struct platform_device *pdev)
252 {
253 struct ucsi_acpi *ua = platform_get_drvdata(pdev);
254
255 ucsi_unregister(ua->ucsi);
256 ucsi_destroy(ua->ucsi);
257
258 acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev), ACPI_DEVICE_NOTIFY,
259 ucsi_acpi_notify);
260 }
261
ucsi_acpi_resume(struct device * dev)262 static int ucsi_acpi_resume(struct device *dev)
263 {
264 struct ucsi_acpi *ua = dev_get_drvdata(dev);
265
266 return ucsi_resume(ua->ucsi);
267 }
268
269 static DEFINE_SIMPLE_DEV_PM_OPS(ucsi_acpi_pm_ops, NULL, ucsi_acpi_resume);
270
271 static const struct acpi_device_id ucsi_acpi_match[] = {
272 { "PNP0CA0", 0 },
273 { },
274 };
275 MODULE_DEVICE_TABLE(acpi, ucsi_acpi_match);
276
277 static struct platform_driver ucsi_acpi_platform_driver = {
278 .driver = {
279 .name = "ucsi_acpi",
280 .pm = pm_ptr(&ucsi_acpi_pm_ops),
281 .acpi_match_table = ACPI_PTR(ucsi_acpi_match),
282 },
283 .probe = ucsi_acpi_probe,
284 .remove_new = ucsi_acpi_remove,
285 };
286
287 module_platform_driver(ucsi_acpi_platform_driver);
288
289 MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
290 MODULE_LICENSE("GPL v2");
291 MODULE_DESCRIPTION("UCSI ACPI driver");
292