1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright 2020 Google LLC
4  *
5  * This driver serves as the receiver of cros_ec PD host events.
6  */
7 
8 #include <linux/acpi.h>
9 #include <linux/module.h>
10 #include <linux/platform_data/cros_ec_proto.h>
11 #include <linux/platform_data/cros_usbpd_notify.h>
12 #include <linux/platform_device.h>
13 
14 #define DRV_NAME "cros-usbpd-notify"
15 #define DRV_NAME_PLAT_ACPI "cros-usbpd-notify-acpi"
16 #define ACPI_DRV_NAME "GOOG0003"
17 
18 static BLOCKING_NOTIFIER_HEAD(cros_usbpd_notifier_list);
19 
20 struct cros_usbpd_notify_data {
21 	struct device *dev;
22 	struct cros_ec_device *ec;
23 	struct notifier_block nb;
24 };
25 
26 /**
27  * cros_usbpd_register_notify - Register a notifier callback for PD events.
28  * @nb: Notifier block pointer to register
29  *
30  * On ACPI platforms this corresponds to host events on the ECPD
31  * "GOOG0003" ACPI device. On non-ACPI platforms this will filter mkbp events
32  * for USB PD events.
33  *
34  * Return: 0 on success or negative error code.
35  */
36 int cros_usbpd_register_notify(struct notifier_block *nb)
37 {
38 	return blocking_notifier_chain_register(&cros_usbpd_notifier_list,
39 						nb);
40 }
41 EXPORT_SYMBOL_GPL(cros_usbpd_register_notify);
42 
43 /**
44  * cros_usbpd_unregister_notify - Unregister notifier callback for PD events.
45  * @nb: Notifier block pointer to unregister
46  *
47  * Unregister a notifier callback that was previously registered with
48  * cros_usbpd_register_notify().
49  */
50 void cros_usbpd_unregister_notify(struct notifier_block *nb)
51 {
52 	blocking_notifier_chain_unregister(&cros_usbpd_notifier_list, nb);
53 }
54 EXPORT_SYMBOL_GPL(cros_usbpd_unregister_notify);
55 
56 /**
57  * cros_ec_command - Send a command to the EC.
58  *
59  * @ec_dev: EC device
60  * @command: EC command
61  * @outdata: EC command output data
62  * @outsize: Size of outdata
63  * @indata: EC command input data
64  * @insize: Size of indata
65  *
66  * Return: >= 0 on success, negative error number on failure.
67  */
68 static int cros_ec_command(struct cros_ec_device *ec_dev,
69 			   int command,
70 			   uint8_t *outdata,
71 			   int outsize,
72 			   uint8_t *indata,
73 			   int insize)
74 {
75 	struct cros_ec_command *msg;
76 	int ret;
77 
78 	msg = kzalloc(sizeof(*msg) + max(insize, outsize), GFP_KERNEL);
79 	if (!msg)
80 		return -ENOMEM;
81 
82 	msg->command = command;
83 	msg->outsize = outsize;
84 	msg->insize = insize;
85 
86 	if (outsize)
87 		memcpy(msg->data, outdata, outsize);
88 
89 	ret = cros_ec_cmd_xfer_status(ec_dev, msg);
90 	if (ret < 0)
91 		goto error;
92 
93 	if (insize)
94 		memcpy(indata, msg->data, insize);
95 error:
96 	kfree(msg);
97 	return ret;
98 }
99 
100 static void cros_usbpd_get_event_and_notify(struct device  *dev,
101 					    struct cros_ec_device *ec_dev)
102 {
103 	struct ec_response_host_event_status host_event_status;
104 	u32 event = 0;
105 	int ret;
106 
107 	/*
108 	 * We still send a 0 event out to older devices which don't
109 	 * have the updated device heirarchy.
110 	 */
111 	if (!ec_dev) {
112 		dev_dbg(dev,
113 			"EC device inaccessible; sending 0 event status.\n");
114 		goto send_notify;
115 	}
116 
117 	/* Check for PD host events on EC. */
118 	ret = cros_ec_command(ec_dev, EC_CMD_PD_HOST_EVENT_STATUS,
119 			      NULL, 0, (uint8_t *)&host_event_status, sizeof(host_event_status));
120 	if (ret < 0) {
121 		dev_warn(dev, "Can't get host event status (err: %d)\n", ret);
122 		goto send_notify;
123 	}
124 
125 	event = host_event_status.status;
126 
127 send_notify:
128 	blocking_notifier_call_chain(&cros_usbpd_notifier_list, event, NULL);
129 }
130 
131 #ifdef CONFIG_ACPI
132 
133 static void cros_usbpd_notify_acpi(acpi_handle device, u32 event, void *data)
134 {
135 	struct cros_usbpd_notify_data *pdnotify = data;
136 
137 	cros_usbpd_get_event_and_notify(pdnotify->dev, pdnotify->ec);
138 }
139 
140 static int cros_usbpd_notify_probe_acpi(struct platform_device *pdev)
141 {
142 	struct cros_usbpd_notify_data *pdnotify;
143 	struct device *dev = &pdev->dev;
144 	struct acpi_device *adev;
145 	struct cros_ec_device *ec_dev;
146 	acpi_status status;
147 
148 	adev = ACPI_COMPANION(dev);
149 
150 	pdnotify = devm_kzalloc(dev, sizeof(*pdnotify), GFP_KERNEL);
151 	if (!pdnotify)
152 		return -ENOMEM;
153 
154 	/* Get the EC device pointer needed to talk to the EC. */
155 	ec_dev = dev_get_drvdata(dev->parent);
156 	if (!ec_dev) {
157 		/*
158 		 * We continue even for older devices which don't have the
159 		 * correct device heirarchy, namely, GOOG0003 is a child
160 		 * of GOOG0004.
161 		 */
162 		dev_warn(dev, "Couldn't get Chrome EC device pointer.\n");
163 	}
164 
165 	pdnotify->dev = dev;
166 	pdnotify->ec = ec_dev;
167 
168 	status = acpi_install_notify_handler(adev->handle,
169 					     ACPI_ALL_NOTIFY,
170 					     cros_usbpd_notify_acpi,
171 					     pdnotify);
172 	if (ACPI_FAILURE(status)) {
173 		dev_warn(dev, "Failed to register notify handler %08x\n",
174 			 status);
175 		return -EINVAL;
176 	}
177 
178 	return 0;
179 }
180 
181 static int cros_usbpd_notify_remove_acpi(struct platform_device *pdev)
182 {
183 	struct device *dev = &pdev->dev;
184 	struct acpi_device *adev = ACPI_COMPANION(dev);
185 
186 	acpi_remove_notify_handler(adev->handle, ACPI_ALL_NOTIFY,
187 				   cros_usbpd_notify_acpi);
188 
189 	return 0;
190 }
191 
192 static const struct acpi_device_id cros_usbpd_notify_acpi_device_ids[] = {
193 	{ ACPI_DRV_NAME, 0 },
194 	{ }
195 };
196 MODULE_DEVICE_TABLE(acpi, cros_usbpd_notify_acpi_device_ids);
197 
198 static struct platform_driver cros_usbpd_notify_acpi_driver = {
199 	.driver = {
200 		.name = DRV_NAME_PLAT_ACPI,
201 		.acpi_match_table = cros_usbpd_notify_acpi_device_ids,
202 	},
203 	.probe = cros_usbpd_notify_probe_acpi,
204 	.remove = cros_usbpd_notify_remove_acpi,
205 };
206 
207 #endif /* CONFIG_ACPI */
208 
209 static int cros_usbpd_notify_plat(struct notifier_block *nb,
210 				  unsigned long queued_during_suspend,
211 				  void *data)
212 {
213 	struct cros_usbpd_notify_data *pdnotify = container_of(nb,
214 			struct cros_usbpd_notify_data, nb);
215 	struct cros_ec_device *ec_dev = (struct cros_ec_device *)data;
216 	u32 host_event = cros_ec_get_host_event(ec_dev);
217 
218 	if (!host_event)
219 		return NOTIFY_DONE;
220 
221 	if (host_event & (EC_HOST_EVENT_MASK(EC_HOST_EVENT_PD_MCU) |
222 			  EC_HOST_EVENT_MASK(EC_HOST_EVENT_USB_MUX))) {
223 		cros_usbpd_get_event_and_notify(pdnotify->dev, ec_dev);
224 		return NOTIFY_OK;
225 	}
226 	return NOTIFY_DONE;
227 }
228 
229 static int cros_usbpd_notify_probe_plat(struct platform_device *pdev)
230 {
231 	struct device *dev = &pdev->dev;
232 	struct cros_ec_dev *ecdev = dev_get_drvdata(dev->parent);
233 	struct cros_usbpd_notify_data *pdnotify;
234 	int ret;
235 
236 	pdnotify = devm_kzalloc(dev, sizeof(*pdnotify), GFP_KERNEL);
237 	if (!pdnotify)
238 		return -ENOMEM;
239 
240 	pdnotify->dev = dev;
241 	pdnotify->ec = ecdev->ec_dev;
242 	pdnotify->nb.notifier_call = cros_usbpd_notify_plat;
243 
244 	dev_set_drvdata(dev, pdnotify);
245 
246 	ret = blocking_notifier_chain_register(&ecdev->ec_dev->event_notifier,
247 					       &pdnotify->nb);
248 	if (ret < 0) {
249 		dev_err(dev, "Failed to register notifier\n");
250 		return ret;
251 	}
252 
253 	return 0;
254 }
255 
256 static int cros_usbpd_notify_remove_plat(struct platform_device *pdev)
257 {
258 	struct device *dev = &pdev->dev;
259 	struct cros_ec_dev *ecdev = dev_get_drvdata(dev->parent);
260 	struct cros_usbpd_notify_data *pdnotify =
261 		(struct cros_usbpd_notify_data *)dev_get_drvdata(dev);
262 
263 	blocking_notifier_chain_unregister(&ecdev->ec_dev->event_notifier,
264 					   &pdnotify->nb);
265 
266 	return 0;
267 }
268 
269 static struct platform_driver cros_usbpd_notify_plat_driver = {
270 	.driver = {
271 		.name = DRV_NAME,
272 	},
273 	.probe = cros_usbpd_notify_probe_plat,
274 	.remove = cros_usbpd_notify_remove_plat,
275 };
276 
277 static int __init cros_usbpd_notify_init(void)
278 {
279 	int ret;
280 
281 	ret = platform_driver_register(&cros_usbpd_notify_plat_driver);
282 	if (ret < 0)
283 		return ret;
284 
285 #ifdef CONFIG_ACPI
286 	platform_driver_register(&cros_usbpd_notify_acpi_driver);
287 #endif
288 	return 0;
289 }
290 
291 static void __exit cros_usbpd_notify_exit(void)
292 {
293 #ifdef CONFIG_ACPI
294 	platform_driver_unregister(&cros_usbpd_notify_acpi_driver);
295 #endif
296 	platform_driver_unregister(&cros_usbpd_notify_plat_driver);
297 }
298 
299 module_init(cros_usbpd_notify_init);
300 module_exit(cros_usbpd_notify_exit);
301 
302 MODULE_LICENSE("GPL");
303 MODULE_DESCRIPTION("ChromeOS power delivery notifier device");
304 MODULE_AUTHOR("Jon Flatley <jflat@chromium.org>");
305 MODULE_ALIAS("platform:" DRV_NAME);
306