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