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