1ec2daf6eSJon Flatley // SPDX-License-Identifier: GPL-2.0-only
2ec2daf6eSJon Flatley /*
3ec2daf6eSJon Flatley  * Copyright 2020 Google LLC
4ec2daf6eSJon Flatley  *
5ec2daf6eSJon Flatley  * This driver serves as the receiver of cros_ec PD host events.
6ec2daf6eSJon Flatley  */
7ec2daf6eSJon Flatley 
8ec2daf6eSJon Flatley #include <linux/acpi.h>
9ec2daf6eSJon Flatley #include <linux/module.h>
10ec2daf6eSJon Flatley #include <linux/platform_data/cros_ec_proto.h>
11ec2daf6eSJon Flatley #include <linux/platform_data/cros_usbpd_notify.h>
12ec2daf6eSJon Flatley #include <linux/platform_device.h>
13ec2daf6eSJon Flatley 
14ec2daf6eSJon Flatley #define DRV_NAME "cros-usbpd-notify"
15ec2daf6eSJon Flatley #define ACPI_DRV_NAME "GOOG0003"
16ec2daf6eSJon Flatley 
17ec2daf6eSJon Flatley static BLOCKING_NOTIFIER_HEAD(cros_usbpd_notifier_list);
18ec2daf6eSJon Flatley 
19f5d84a21SPrashant Malani struct cros_usbpd_notify_data {
20f5d84a21SPrashant Malani 	struct device *dev;
21f5d84a21SPrashant Malani 	struct cros_ec_device *ec;
22f5d84a21SPrashant Malani 	struct notifier_block nb;
23f5d84a21SPrashant Malani };
24f5d84a21SPrashant Malani 
25ec2daf6eSJon Flatley /**
26ec2daf6eSJon Flatley  * cros_usbpd_register_notify - Register a notifier callback for PD events.
27ec2daf6eSJon Flatley  * @nb: Notifier block pointer to register
28ec2daf6eSJon Flatley  *
29ec2daf6eSJon Flatley  * On ACPI platforms this corresponds to host events on the ECPD
30ec2daf6eSJon Flatley  * "GOOG0003" ACPI device. On non-ACPI platforms this will filter mkbp events
31ec2daf6eSJon Flatley  * for USB PD events.
32ec2daf6eSJon Flatley  *
33ec2daf6eSJon Flatley  * Return: 0 on success or negative error code.
34ec2daf6eSJon Flatley  */
35ec2daf6eSJon Flatley int cros_usbpd_register_notify(struct notifier_block *nb)
36ec2daf6eSJon Flatley {
37ec2daf6eSJon Flatley 	return blocking_notifier_chain_register(&cros_usbpd_notifier_list,
38ec2daf6eSJon Flatley 						nb);
39ec2daf6eSJon Flatley }
40ec2daf6eSJon Flatley EXPORT_SYMBOL_GPL(cros_usbpd_register_notify);
41ec2daf6eSJon Flatley 
42ec2daf6eSJon Flatley /**
43ec2daf6eSJon Flatley  * cros_usbpd_unregister_notify - Unregister notifier callback for PD events.
44ec2daf6eSJon Flatley  * @nb: Notifier block pointer to unregister
45ec2daf6eSJon Flatley  *
46ec2daf6eSJon Flatley  * Unregister a notifier callback that was previously registered with
47ec2daf6eSJon Flatley  * cros_usbpd_register_notify().
48ec2daf6eSJon Flatley  */
49ec2daf6eSJon Flatley void cros_usbpd_unregister_notify(struct notifier_block *nb)
50ec2daf6eSJon Flatley {
51ec2daf6eSJon Flatley 	blocking_notifier_chain_unregister(&cros_usbpd_notifier_list, nb);
52ec2daf6eSJon Flatley }
53ec2daf6eSJon Flatley EXPORT_SYMBOL_GPL(cros_usbpd_unregister_notify);
54ec2daf6eSJon Flatley 
55ec2daf6eSJon Flatley #ifdef CONFIG_ACPI
56ec2daf6eSJon Flatley 
57ec2daf6eSJon Flatley static int cros_usbpd_notify_add_acpi(struct acpi_device *adev)
58ec2daf6eSJon Flatley {
59ec2daf6eSJon Flatley 	return 0;
60ec2daf6eSJon Flatley }
61ec2daf6eSJon Flatley 
62ec2daf6eSJon Flatley static void cros_usbpd_notify_acpi(struct acpi_device *adev, u32 event)
63ec2daf6eSJon Flatley {
64ec2daf6eSJon Flatley 	blocking_notifier_call_chain(&cros_usbpd_notifier_list, event, NULL);
65ec2daf6eSJon Flatley }
66ec2daf6eSJon Flatley 
67ec2daf6eSJon Flatley static const struct acpi_device_id cros_usbpd_notify_acpi_device_ids[] = {
68ec2daf6eSJon Flatley 	{ ACPI_DRV_NAME, 0 },
69ec2daf6eSJon Flatley 	{ }
70ec2daf6eSJon Flatley };
71ec2daf6eSJon Flatley MODULE_DEVICE_TABLE(acpi, cros_usbpd_notify_acpi_device_ids);
72ec2daf6eSJon Flatley 
73ec2daf6eSJon Flatley static struct acpi_driver cros_usbpd_notify_acpi_driver = {
74ec2daf6eSJon Flatley 	.name = DRV_NAME,
75ec2daf6eSJon Flatley 	.class = DRV_NAME,
76ec2daf6eSJon Flatley 	.ids = cros_usbpd_notify_acpi_device_ids,
77ec2daf6eSJon Flatley 	.ops = {
78ec2daf6eSJon Flatley 		.add = cros_usbpd_notify_add_acpi,
79ec2daf6eSJon Flatley 		.notify = cros_usbpd_notify_acpi,
80ec2daf6eSJon Flatley 	},
81ec2daf6eSJon Flatley };
82ec2daf6eSJon Flatley 
83ec2daf6eSJon Flatley #endif /* CONFIG_ACPI */
84ec2daf6eSJon Flatley 
85ec2daf6eSJon Flatley static int cros_usbpd_notify_plat(struct notifier_block *nb,
86ec2daf6eSJon Flatley 				  unsigned long queued_during_suspend,
87ec2daf6eSJon Flatley 				  void *data)
88ec2daf6eSJon Flatley {
89ec2daf6eSJon Flatley 	struct cros_ec_device *ec_dev = (struct cros_ec_device *)data;
90ec2daf6eSJon Flatley 	u32 host_event = cros_ec_get_host_event(ec_dev);
91ec2daf6eSJon Flatley 
92ec2daf6eSJon Flatley 	if (!host_event)
9392e399c0SGwendal Grignou 		return NOTIFY_DONE;
94ec2daf6eSJon Flatley 
95ec2daf6eSJon Flatley 	if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_PD_MCU)) {
96ec2daf6eSJon Flatley 		blocking_notifier_call_chain(&cros_usbpd_notifier_list,
97ec2daf6eSJon Flatley 					     host_event, NULL);
98ec2daf6eSJon Flatley 		return NOTIFY_OK;
99ec2daf6eSJon Flatley 	}
100ec2daf6eSJon Flatley 	return NOTIFY_DONE;
101ec2daf6eSJon Flatley }
102ec2daf6eSJon Flatley 
103ec2daf6eSJon Flatley static int cros_usbpd_notify_probe_plat(struct platform_device *pdev)
104ec2daf6eSJon Flatley {
105ec2daf6eSJon Flatley 	struct device *dev = &pdev->dev;
106ec2daf6eSJon Flatley 	struct cros_ec_dev *ecdev = dev_get_drvdata(dev->parent);
107f5d84a21SPrashant Malani 	struct cros_usbpd_notify_data *pdnotify;
108ec2daf6eSJon Flatley 	int ret;
109ec2daf6eSJon Flatley 
110f5d84a21SPrashant Malani 	pdnotify = devm_kzalloc(dev, sizeof(*pdnotify), GFP_KERNEL);
111f5d84a21SPrashant Malani 	if (!pdnotify)
112ec2daf6eSJon Flatley 		return -ENOMEM;
113ec2daf6eSJon Flatley 
114f5d84a21SPrashant Malani 	pdnotify->dev = dev;
115f5d84a21SPrashant Malani 	pdnotify->ec = ecdev->ec_dev;
116f5d84a21SPrashant Malani 	pdnotify->nb.notifier_call = cros_usbpd_notify_plat;
117f5d84a21SPrashant Malani 
118f5d84a21SPrashant Malani 	dev_set_drvdata(dev, pdnotify);
119ec2daf6eSJon Flatley 
120ec2daf6eSJon Flatley 	ret = blocking_notifier_chain_register(&ecdev->ec_dev->event_notifier,
121f5d84a21SPrashant Malani 					       &pdnotify->nb);
122ec2daf6eSJon Flatley 	if (ret < 0) {
123ec2daf6eSJon Flatley 		dev_err(dev, "Failed to register notifier\n");
124ec2daf6eSJon Flatley 		return ret;
125ec2daf6eSJon Flatley 	}
126ec2daf6eSJon Flatley 
127ec2daf6eSJon Flatley 	return 0;
128ec2daf6eSJon Flatley }
129ec2daf6eSJon Flatley 
130ec2daf6eSJon Flatley static int cros_usbpd_notify_remove_plat(struct platform_device *pdev)
131ec2daf6eSJon Flatley {
132ec2daf6eSJon Flatley 	struct device *dev = &pdev->dev;
133ec2daf6eSJon Flatley 	struct cros_ec_dev *ecdev = dev_get_drvdata(dev->parent);
134f5d84a21SPrashant Malani 	struct cros_usbpd_notify_data *pdnotify =
135f5d84a21SPrashant Malani 		(struct cros_usbpd_notify_data *)dev_get_drvdata(dev);
136ec2daf6eSJon Flatley 
137f5d84a21SPrashant Malani 	blocking_notifier_chain_unregister(&ecdev->ec_dev->event_notifier,
138f5d84a21SPrashant Malani 					   &pdnotify->nb);
139ec2daf6eSJon Flatley 
140ec2daf6eSJon Flatley 	return 0;
141ec2daf6eSJon Flatley }
142ec2daf6eSJon Flatley 
143ec2daf6eSJon Flatley static struct platform_driver cros_usbpd_notify_plat_driver = {
144ec2daf6eSJon Flatley 	.driver = {
145ec2daf6eSJon Flatley 		.name = DRV_NAME,
146ec2daf6eSJon Flatley 	},
147ec2daf6eSJon Flatley 	.probe = cros_usbpd_notify_probe_plat,
148ec2daf6eSJon Flatley 	.remove = cros_usbpd_notify_remove_plat,
149ec2daf6eSJon Flatley };
150ec2daf6eSJon Flatley 
151ec2daf6eSJon Flatley static int __init cros_usbpd_notify_init(void)
152ec2daf6eSJon Flatley {
153ec2daf6eSJon Flatley 	int ret;
154ec2daf6eSJon Flatley 
155ec2daf6eSJon Flatley 	ret = platform_driver_register(&cros_usbpd_notify_plat_driver);
156ec2daf6eSJon Flatley 	if (ret < 0)
157ec2daf6eSJon Flatley 		return ret;
158ec2daf6eSJon Flatley 
159ec2daf6eSJon Flatley #ifdef CONFIG_ACPI
160ec2daf6eSJon Flatley 	acpi_bus_register_driver(&cros_usbpd_notify_acpi_driver);
161ec2daf6eSJon Flatley #endif
162ec2daf6eSJon Flatley 	return 0;
163ec2daf6eSJon Flatley }
164ec2daf6eSJon Flatley 
165ec2daf6eSJon Flatley static void __exit cros_usbpd_notify_exit(void)
166ec2daf6eSJon Flatley {
167ec2daf6eSJon Flatley #ifdef CONFIG_ACPI
168ec2daf6eSJon Flatley 	acpi_bus_unregister_driver(&cros_usbpd_notify_acpi_driver);
169ec2daf6eSJon Flatley #endif
170ec2daf6eSJon Flatley 	platform_driver_unregister(&cros_usbpd_notify_plat_driver);
171ec2daf6eSJon Flatley }
172ec2daf6eSJon Flatley 
173ec2daf6eSJon Flatley module_init(cros_usbpd_notify_init);
174ec2daf6eSJon Flatley module_exit(cros_usbpd_notify_exit);
175ec2daf6eSJon Flatley 
176ec2daf6eSJon Flatley MODULE_LICENSE("GPL");
177ec2daf6eSJon Flatley MODULE_DESCRIPTION("ChromeOS power delivery notifier device");
178ec2daf6eSJon Flatley MODULE_AUTHOR("Jon Flatley <jflat@chromium.org>");
179ec2daf6eSJon Flatley MODULE_ALIAS("platform:" DRV_NAME);
180