1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * UCSI ACPI driver
4  *
5  * Copyright (C) 2017, Intel Corporation
6  * Author: Heikki Krogerus <[email protected]>
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 	int ret;
114 
115 	ret = ucsi_acpi_read_message_in(ucsi, val, val_len);
116 	if (ret < 0)
117 		return ret;
118 
119 	if (UCSI_COMMAND(ua->cmd) == UCSI_GET_CONNECTOR_STATUS &&
120 	    ua->check_bogus_event) {
121 		/* Clear the bogus change */
122 		if (*(u16 *)val == bogus_change)
123 			*(u16 *)val = 0;
124 
125 		ua->check_bogus_event = false;
126 	}
127 
128 	return ret;
129 }
130 
ucsi_gram_sync_control(struct ucsi * ucsi,u64 command)131 static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command)
132 {
133 	struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
134 	int ret;
135 
136 	ret = ucsi_sync_control_common(ucsi, command);
137 	if (ret < 0)
138 		return ret;
139 
140 	if (UCSI_COMMAND(ua->cmd) == UCSI_GET_PDOS &&
141 	    ua->cmd & UCSI_GET_PDOS_PARTNER_PDO(1) &&
142 	    ua->cmd & UCSI_GET_PDOS_SRC_PDOS)
143 		ua->check_bogus_event = true;
144 
145 	return ret;
146 }
147 
148 static const struct ucsi_operations ucsi_gram_ops = {
149 	.read_version = ucsi_acpi_read_version,
150 	.read_cci = ucsi_acpi_read_cci,
151 	.poll_cci = ucsi_acpi_poll_cci,
152 	.read_message_in = ucsi_gram_read_message_in,
153 	.sync_control = ucsi_gram_sync_control,
154 	.async_control = ucsi_acpi_async_control
155 };
156 
157 static const struct dmi_system_id ucsi_acpi_quirks[] = {
158 	{
159 		.matches = {
160 			DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"),
161 			DMI_MATCH(DMI_PRODUCT_FAMILY, "LG gram PC"),
162 			DMI_MATCH(DMI_PRODUCT_NAME, "90Q"),
163 		},
164 		.driver_data = (void *)&ucsi_gram_ops,
165 	},
166 	{ }
167 };
168 
ucsi_acpi_notify(acpi_handle handle,u32 event,void * data)169 static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data)
170 {
171 	struct ucsi_acpi *ua = data;
172 	u32 cci;
173 	int ret;
174 
175 	ret = ua->ucsi->ops->read_cci(ua->ucsi, &cci);
176 	if (ret)
177 		return;
178 
179 	ucsi_notify_common(ua->ucsi, cci);
180 }
181 
ucsi_acpi_probe(struct platform_device * pdev)182 static int ucsi_acpi_probe(struct platform_device *pdev)
183 {
184 	struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
185 	const struct ucsi_operations *ops = &ucsi_acpi_ops;
186 	const struct dmi_system_id *id;
187 	struct ucsi_acpi *ua;
188 	struct resource *res;
189 	acpi_status status;
190 	int ret;
191 
192 	if (adev->dep_unmet)
193 		return -EPROBE_DEFER;
194 
195 	ua = devm_kzalloc(&pdev->dev, sizeof(*ua), GFP_KERNEL);
196 	if (!ua)
197 		return -ENOMEM;
198 
199 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
200 	if (!res) {
201 		dev_err(&pdev->dev, "missing memory resource\n");
202 		return -ENODEV;
203 	}
204 
205 	ua->base = devm_memremap(&pdev->dev, res->start, resource_size(res), MEMREMAP_WB);
206 	if (IS_ERR(ua->base))
207 		return PTR_ERR(ua->base);
208 
209 	ret = guid_parse(UCSI_DSM_UUID, &ua->guid);
210 	if (ret)
211 		return ret;
212 
213 	ua->dev = &pdev->dev;
214 
215 	id = dmi_first_match(ucsi_acpi_quirks);
216 	if (id)
217 		ops = id->driver_data;
218 
219 	ua->ucsi = ucsi_create(&pdev->dev, ops);
220 	if (IS_ERR(ua->ucsi))
221 		return PTR_ERR(ua->ucsi);
222 
223 	ucsi_set_drvdata(ua->ucsi, ua);
224 
225 	status = acpi_install_notify_handler(ACPI_HANDLE(&pdev->dev),
226 					     ACPI_DEVICE_NOTIFY,
227 					     ucsi_acpi_notify, ua);
228 	if (ACPI_FAILURE(status)) {
229 		dev_err(&pdev->dev, "failed to install notify handler\n");
230 		ucsi_destroy(ua->ucsi);
231 		return -ENODEV;
232 	}
233 
234 	ret = ucsi_register(ua->ucsi);
235 	if (ret) {
236 		acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev),
237 					   ACPI_DEVICE_NOTIFY,
238 					   ucsi_acpi_notify);
239 		ucsi_destroy(ua->ucsi);
240 		return ret;
241 	}
242 
243 	platform_set_drvdata(pdev, ua);
244 
245 	return 0;
246 }
247 
ucsi_acpi_remove(struct platform_device * pdev)248 static void ucsi_acpi_remove(struct platform_device *pdev)
249 {
250 	struct ucsi_acpi *ua = platform_get_drvdata(pdev);
251 
252 	ucsi_unregister(ua->ucsi);
253 	ucsi_destroy(ua->ucsi);
254 
255 	acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev), ACPI_DEVICE_NOTIFY,
256 				   ucsi_acpi_notify);
257 }
258 
ucsi_acpi_resume(struct device * dev)259 static int ucsi_acpi_resume(struct device *dev)
260 {
261 	struct ucsi_acpi *ua = dev_get_drvdata(dev);
262 
263 	return ucsi_resume(ua->ucsi);
264 }
265 
266 static DEFINE_SIMPLE_DEV_PM_OPS(ucsi_acpi_pm_ops, NULL, ucsi_acpi_resume);
267 
268 static const struct acpi_device_id ucsi_acpi_match[] = {
269 	{ "PNP0CA0", 0 },
270 	{ },
271 };
272 MODULE_DEVICE_TABLE(acpi, ucsi_acpi_match);
273 
274 static struct platform_driver ucsi_acpi_platform_driver = {
275 	.driver = {
276 		.name = "ucsi_acpi",
277 		.pm = pm_ptr(&ucsi_acpi_pm_ops),
278 		.acpi_match_table = ACPI_PTR(ucsi_acpi_match),
279 	},
280 	.probe = ucsi_acpi_probe,
281 	.remove = ucsi_acpi_remove,
282 };
283 
284 module_platform_driver(ucsi_acpi_platform_driver);
285 
286 MODULE_AUTHOR("Heikki Krogerus <[email protected]>");
287 MODULE_LICENSE("GPL v2");
288 MODULE_DESCRIPTION("UCSI ACPI driver");
289