Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright 2020 Google LLC
0004  *
0005  * This driver serves as the receiver of cros_ec PD host events.
0006  */
0007 
0008 #include <linux/acpi.h>
0009 #include <linux/module.h>
0010 #include <linux/platform_data/cros_ec_proto.h>
0011 #include <linux/platform_data/cros_usbpd_notify.h>
0012 #include <linux/platform_device.h>
0013 
0014 #define DRV_NAME "cros-usbpd-notify"
0015 #define DRV_NAME_PLAT_ACPI "cros-usbpd-notify-acpi"
0016 #define ACPI_DRV_NAME "GOOG0003"
0017 
0018 static BLOCKING_NOTIFIER_HEAD(cros_usbpd_notifier_list);
0019 
0020 struct cros_usbpd_notify_data {
0021     struct device *dev;
0022     struct cros_ec_device *ec;
0023     struct notifier_block nb;
0024 };
0025 
0026 /**
0027  * cros_usbpd_register_notify - Register a notifier callback for PD events.
0028  * @nb: Notifier block pointer to register
0029  *
0030  * On ACPI platforms this corresponds to host events on the ECPD
0031  * "GOOG0003" ACPI device. On non-ACPI platforms this will filter mkbp events
0032  * for USB PD events.
0033  *
0034  * Return: 0 on success or negative error code.
0035  */
0036 int cros_usbpd_register_notify(struct notifier_block *nb)
0037 {
0038     return blocking_notifier_chain_register(&cros_usbpd_notifier_list,
0039                         nb);
0040 }
0041 EXPORT_SYMBOL_GPL(cros_usbpd_register_notify);
0042 
0043 /**
0044  * cros_usbpd_unregister_notify - Unregister notifier callback for PD events.
0045  * @nb: Notifier block pointer to unregister
0046  *
0047  * Unregister a notifier callback that was previously registered with
0048  * cros_usbpd_register_notify().
0049  */
0050 void cros_usbpd_unregister_notify(struct notifier_block *nb)
0051 {
0052     blocking_notifier_chain_unregister(&cros_usbpd_notifier_list, nb);
0053 }
0054 EXPORT_SYMBOL_GPL(cros_usbpd_unregister_notify);
0055 
0056 static void cros_usbpd_get_event_and_notify(struct device  *dev,
0057                         struct cros_ec_device *ec_dev)
0058 {
0059     struct ec_response_host_event_status host_event_status;
0060     u32 event = 0;
0061     int ret;
0062 
0063     /*
0064      * We still send a 0 event out to older devices which don't
0065      * have the updated device heirarchy.
0066      */
0067     if (!ec_dev) {
0068         dev_dbg(dev,
0069             "EC device inaccessible; sending 0 event status.\n");
0070         goto send_notify;
0071     }
0072 
0073     /* Check for PD host events on EC. */
0074     ret = cros_ec_cmd(ec_dev, 0, EC_CMD_PD_HOST_EVENT_STATUS,
0075               NULL, 0, &host_event_status, sizeof(host_event_status));
0076     if (ret < 0) {
0077         dev_warn(dev, "Can't get host event status (err: %d)\n", ret);
0078         goto send_notify;
0079     }
0080 
0081     event = host_event_status.status;
0082 
0083 send_notify:
0084     blocking_notifier_call_chain(&cros_usbpd_notifier_list, event, NULL);
0085 }
0086 
0087 #ifdef CONFIG_ACPI
0088 
0089 static void cros_usbpd_notify_acpi(acpi_handle device, u32 event, void *data)
0090 {
0091     struct cros_usbpd_notify_data *pdnotify = data;
0092 
0093     cros_usbpd_get_event_and_notify(pdnotify->dev, pdnotify->ec);
0094 }
0095 
0096 static int cros_usbpd_notify_probe_acpi(struct platform_device *pdev)
0097 {
0098     struct cros_usbpd_notify_data *pdnotify;
0099     struct device *dev = &pdev->dev;
0100     struct acpi_device *adev;
0101     struct cros_ec_device *ec_dev;
0102     acpi_status status;
0103 
0104     adev = ACPI_COMPANION(dev);
0105 
0106     pdnotify = devm_kzalloc(dev, sizeof(*pdnotify), GFP_KERNEL);
0107     if (!pdnotify)
0108         return -ENOMEM;
0109 
0110     /* Get the EC device pointer needed to talk to the EC. */
0111     ec_dev = dev_get_drvdata(dev->parent);
0112     if (!ec_dev) {
0113         /*
0114          * We continue even for older devices which don't have the
0115          * correct device heirarchy, namely, GOOG0003 is a child
0116          * of GOOG0004.
0117          */
0118         dev_warn(dev, "Couldn't get Chrome EC device pointer.\n");
0119     }
0120 
0121     pdnotify->dev = dev;
0122     pdnotify->ec = ec_dev;
0123 
0124     status = acpi_install_notify_handler(adev->handle,
0125                          ACPI_ALL_NOTIFY,
0126                          cros_usbpd_notify_acpi,
0127                          pdnotify);
0128     if (ACPI_FAILURE(status)) {
0129         dev_warn(dev, "Failed to register notify handler %08x\n",
0130              status);
0131         return -EINVAL;
0132     }
0133 
0134     return 0;
0135 }
0136 
0137 static int cros_usbpd_notify_remove_acpi(struct platform_device *pdev)
0138 {
0139     struct device *dev = &pdev->dev;
0140     struct acpi_device *adev = ACPI_COMPANION(dev);
0141 
0142     acpi_remove_notify_handler(adev->handle, ACPI_ALL_NOTIFY,
0143                    cros_usbpd_notify_acpi);
0144 
0145     return 0;
0146 }
0147 
0148 static const struct acpi_device_id cros_usbpd_notify_acpi_device_ids[] = {
0149     { ACPI_DRV_NAME, 0 },
0150     { }
0151 };
0152 MODULE_DEVICE_TABLE(acpi, cros_usbpd_notify_acpi_device_ids);
0153 
0154 static struct platform_driver cros_usbpd_notify_acpi_driver = {
0155     .driver = {
0156         .name = DRV_NAME_PLAT_ACPI,
0157         .acpi_match_table = cros_usbpd_notify_acpi_device_ids,
0158     },
0159     .probe = cros_usbpd_notify_probe_acpi,
0160     .remove = cros_usbpd_notify_remove_acpi,
0161 };
0162 
0163 #endif /* CONFIG_ACPI */
0164 
0165 static int cros_usbpd_notify_plat(struct notifier_block *nb,
0166                   unsigned long queued_during_suspend,
0167                   void *data)
0168 {
0169     struct cros_usbpd_notify_data *pdnotify = container_of(nb,
0170             struct cros_usbpd_notify_data, nb);
0171     struct cros_ec_device *ec_dev = (struct cros_ec_device *)data;
0172     u32 host_event = cros_ec_get_host_event(ec_dev);
0173 
0174     if (!host_event)
0175         return NOTIFY_DONE;
0176 
0177     if (host_event & (EC_HOST_EVENT_MASK(EC_HOST_EVENT_PD_MCU) |
0178               EC_HOST_EVENT_MASK(EC_HOST_EVENT_USB_MUX))) {
0179         cros_usbpd_get_event_and_notify(pdnotify->dev, ec_dev);
0180         return NOTIFY_OK;
0181     }
0182     return NOTIFY_DONE;
0183 }
0184 
0185 static int cros_usbpd_notify_probe_plat(struct platform_device *pdev)
0186 {
0187     struct device *dev = &pdev->dev;
0188     struct cros_ec_dev *ecdev = dev_get_drvdata(dev->parent);
0189     struct cros_usbpd_notify_data *pdnotify;
0190     int ret;
0191 
0192     pdnotify = devm_kzalloc(dev, sizeof(*pdnotify), GFP_KERNEL);
0193     if (!pdnotify)
0194         return -ENOMEM;
0195 
0196     pdnotify->dev = dev;
0197     pdnotify->ec = ecdev->ec_dev;
0198     pdnotify->nb.notifier_call = cros_usbpd_notify_plat;
0199 
0200     dev_set_drvdata(dev, pdnotify);
0201 
0202     ret = blocking_notifier_chain_register(&ecdev->ec_dev->event_notifier,
0203                            &pdnotify->nb);
0204     if (ret < 0) {
0205         dev_err(dev, "Failed to register notifier\n");
0206         return ret;
0207     }
0208 
0209     return 0;
0210 }
0211 
0212 static int cros_usbpd_notify_remove_plat(struct platform_device *pdev)
0213 {
0214     struct device *dev = &pdev->dev;
0215     struct cros_ec_dev *ecdev = dev_get_drvdata(dev->parent);
0216     struct cros_usbpd_notify_data *pdnotify =
0217         (struct cros_usbpd_notify_data *)dev_get_drvdata(dev);
0218 
0219     blocking_notifier_chain_unregister(&ecdev->ec_dev->event_notifier,
0220                        &pdnotify->nb);
0221 
0222     return 0;
0223 }
0224 
0225 static struct platform_driver cros_usbpd_notify_plat_driver = {
0226     .driver = {
0227         .name = DRV_NAME,
0228     },
0229     .probe = cros_usbpd_notify_probe_plat,
0230     .remove = cros_usbpd_notify_remove_plat,
0231 };
0232 
0233 static int __init cros_usbpd_notify_init(void)
0234 {
0235     int ret;
0236 
0237     ret = platform_driver_register(&cros_usbpd_notify_plat_driver);
0238     if (ret < 0)
0239         return ret;
0240 
0241 #ifdef CONFIG_ACPI
0242     platform_driver_register(&cros_usbpd_notify_acpi_driver);
0243 #endif
0244     return 0;
0245 }
0246 
0247 static void __exit cros_usbpd_notify_exit(void)
0248 {
0249 #ifdef CONFIG_ACPI
0250     platform_driver_unregister(&cros_usbpd_notify_acpi_driver);
0251 #endif
0252     platform_driver_unregister(&cros_usbpd_notify_plat_driver);
0253 }
0254 
0255 module_init(cros_usbpd_notify_init);
0256 module_exit(cros_usbpd_notify_exit);
0257 
0258 MODULE_LICENSE("GPL");
0259 MODULE_DESCRIPTION("ChromeOS power delivery notifier device");
0260 MODULE_AUTHOR("Jon Flatley <jflat@chromium.org>");
0261 MODULE_ALIAS("platform:" DRV_NAME);