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