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