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