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