Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v6.13 268 lines 7.1 kB view raw
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 19static BLOCKING_NOTIFIER_HEAD(cros_usbpd_notifier_list); 20 21struct 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 */ 37int cros_usbpd_register_notify(struct notifier_block *nb) 38{ 39 return blocking_notifier_chain_register(&cros_usbpd_notifier_list, 40 nb); 41} 42EXPORT_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 */ 51void cros_usbpd_unregister_notify(struct notifier_block *nb) 52{ 53 blocking_notifier_chain_unregister(&cros_usbpd_notifier_list, nb); 54} 55EXPORT_SYMBOL_GPL(cros_usbpd_unregister_notify); 56 57static 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 84send_notify: 85 blocking_notifier_call_chain(&cros_usbpd_notifier_list, event, NULL); 86} 87 88#ifdef CONFIG_ACPI 89 90static 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 97static 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 138static 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 147static const struct acpi_device_id cros_usbpd_notify_acpi_device_ids[] = { 148 { ACPI_DRV_NAME, 0 }, 149 { } 150}; 151MODULE_DEVICE_TABLE(acpi, cros_usbpd_notify_acpi_device_ids); 152 153static 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 164static 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 184static 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 211static 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 222static const struct platform_device_id cros_usbpd_notify_id[] = { 223 { DRV_NAME, 0 }, 224 {} 225}; 226MODULE_DEVICE_TABLE(platform, cros_usbpd_notify_id); 227 228static 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 237static 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 255static 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 263module_init(cros_usbpd_notify_init); 264module_exit(cros_usbpd_notify_exit); 265 266MODULE_LICENSE("GPL"); 267MODULE_DESCRIPTION("ChromeOS power delivery notifier device"); 268MODULE_AUTHOR("Jon Flatley <jflat@chromium.org>");