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