xref: /linux/drivers/platform/chrome/cros_usbpd_notify.c (revision 0d3b051adbb72ed81956447d0d1e54d5943ee6f5)
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_pd_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_pd_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_pd_command(ec_dev, EC_CMD_PD_HOST_EVENT_STATUS,
119 				 NULL, 0,
120 				 (uint8_t *)&host_event_status,
121 				 sizeof(host_event_status));
122 	if (ret < 0) {
123 		dev_warn(dev, "Can't get host event status (err: %d)\n", ret);
124 		goto send_notify;
125 	}
126 
127 	event = host_event_status.status;
128 
129 send_notify:
130 	blocking_notifier_call_chain(&cros_usbpd_notifier_list, event, NULL);
131 }
132 
133 #ifdef CONFIG_ACPI
134 
135 static void cros_usbpd_notify_acpi(acpi_handle device, u32 event, void *data)
136 {
137 	struct cros_usbpd_notify_data *pdnotify = data;
138 
139 	cros_usbpd_get_event_and_notify(pdnotify->dev, pdnotify->ec);
140 }
141 
142 static int cros_usbpd_notify_probe_acpi(struct platform_device *pdev)
143 {
144 	struct cros_usbpd_notify_data *pdnotify;
145 	struct device *dev = &pdev->dev;
146 	struct acpi_device *adev;
147 	struct cros_ec_device *ec_dev;
148 	acpi_status status;
149 
150 	adev = ACPI_COMPANION(dev);
151 
152 	pdnotify = devm_kzalloc(dev, sizeof(*pdnotify), GFP_KERNEL);
153 	if (!pdnotify)
154 		return -ENOMEM;
155 
156 	/* Get the EC device pointer needed to talk to the EC. */
157 	ec_dev = dev_get_drvdata(dev->parent);
158 	if (!ec_dev) {
159 		/*
160 		 * We continue even for older devices which don't have the
161 		 * correct device heirarchy, namely, GOOG0003 is a child
162 		 * of GOOG0004.
163 		 */
164 		dev_warn(dev, "Couldn't get Chrome EC device pointer.\n");
165 	}
166 
167 	pdnotify->dev = dev;
168 	pdnotify->ec = ec_dev;
169 
170 	status = acpi_install_notify_handler(adev->handle,
171 					     ACPI_ALL_NOTIFY,
172 					     cros_usbpd_notify_acpi,
173 					     pdnotify);
174 	if (ACPI_FAILURE(status)) {
175 		dev_warn(dev, "Failed to register notify handler %08x\n",
176 			 status);
177 		return -EINVAL;
178 	}
179 
180 	return 0;
181 }
182 
183 static int cros_usbpd_notify_remove_acpi(struct platform_device *pdev)
184 {
185 	struct device *dev = &pdev->dev;
186 	struct acpi_device *adev = ACPI_COMPANION(dev);
187 
188 	acpi_remove_notify_handler(adev->handle, ACPI_ALL_NOTIFY,
189 				   cros_usbpd_notify_acpi);
190 
191 	return 0;
192 }
193 
194 static const struct acpi_device_id cros_usbpd_notify_acpi_device_ids[] = {
195 	{ ACPI_DRV_NAME, 0 },
196 	{ }
197 };
198 MODULE_DEVICE_TABLE(acpi, cros_usbpd_notify_acpi_device_ids);
199 
200 static struct platform_driver cros_usbpd_notify_acpi_driver = {
201 	.driver = {
202 		.name = DRV_NAME_PLAT_ACPI,
203 		.acpi_match_table = cros_usbpd_notify_acpi_device_ids,
204 	},
205 	.probe = cros_usbpd_notify_probe_acpi,
206 	.remove = cros_usbpd_notify_remove_acpi,
207 };
208 
209 #endif /* CONFIG_ACPI */
210 
211 static int cros_usbpd_notify_plat(struct notifier_block *nb,
212 				  unsigned long queued_during_suspend,
213 				  void *data)
214 {
215 	struct cros_usbpd_notify_data *pdnotify = container_of(nb,
216 			struct cros_usbpd_notify_data, nb);
217 	struct cros_ec_device *ec_dev = (struct cros_ec_device *)data;
218 	u32 host_event = cros_ec_get_host_event(ec_dev);
219 
220 	if (!host_event)
221 		return NOTIFY_DONE;
222 
223 	if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_PD_MCU)) {
224 		cros_usbpd_get_event_and_notify(pdnotify->dev, ec_dev);
225 		return NOTIFY_OK;
226 	}
227 	return NOTIFY_DONE;
228 }
229 
230 static int cros_usbpd_notify_probe_plat(struct platform_device *pdev)
231 {
232 	struct device *dev = &pdev->dev;
233 	struct cros_ec_dev *ecdev = dev_get_drvdata(dev->parent);
234 	struct cros_usbpd_notify_data *pdnotify;
235 	int ret;
236 
237 	pdnotify = devm_kzalloc(dev, sizeof(*pdnotify), GFP_KERNEL);
238 	if (!pdnotify)
239 		return -ENOMEM;
240 
241 	pdnotify->dev = dev;
242 	pdnotify->ec = ecdev->ec_dev;
243 	pdnotify->nb.notifier_call = cros_usbpd_notify_plat;
244 
245 	dev_set_drvdata(dev, pdnotify);
246 
247 	ret = blocking_notifier_chain_register(&ecdev->ec_dev->event_notifier,
248 					       &pdnotify->nb);
249 	if (ret < 0) {
250 		dev_err(dev, "Failed to register notifier\n");
251 		return ret;
252 	}
253 
254 	return 0;
255 }
256 
257 static int cros_usbpd_notify_remove_plat(struct platform_device *pdev)
258 {
259 	struct device *dev = &pdev->dev;
260 	struct cros_ec_dev *ecdev = dev_get_drvdata(dev->parent);
261 	struct cros_usbpd_notify_data *pdnotify =
262 		(struct cros_usbpd_notify_data *)dev_get_drvdata(dev);
263 
264 	blocking_notifier_chain_unregister(&ecdev->ec_dev->event_notifier,
265 					   &pdnotify->nb);
266 
267 	return 0;
268 }
269 
270 static struct platform_driver cros_usbpd_notify_plat_driver = {
271 	.driver = {
272 		.name = DRV_NAME,
273 	},
274 	.probe = cros_usbpd_notify_probe_plat,
275 	.remove = cros_usbpd_notify_remove_plat,
276 };
277 
278 static int __init cros_usbpd_notify_init(void)
279 {
280 	int ret;
281 
282 	ret = platform_driver_register(&cros_usbpd_notify_plat_driver);
283 	if (ret < 0)
284 		return ret;
285 
286 #ifdef CONFIG_ACPI
287 	platform_driver_register(&cros_usbpd_notify_acpi_driver);
288 #endif
289 	return 0;
290 }
291 
292 static void __exit cros_usbpd_notify_exit(void)
293 {
294 #ifdef CONFIG_ACPI
295 	platform_driver_unregister(&cros_usbpd_notify_acpi_driver);
296 #endif
297 	platform_driver_unregister(&cros_usbpd_notify_plat_driver);
298 }
299 
300 module_init(cros_usbpd_notify_init);
301 module_exit(cros_usbpd_notify_exit);
302 
303 MODULE_LICENSE("GPL");
304 MODULE_DESCRIPTION("ChromeOS power delivery notifier device");
305 MODULE_AUTHOR("Jon Flatley <jflat@chromium.org>");
306 MODULE_ALIAS("platform:" DRV_NAME);
307