Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * cros_ec_dev - expose the Chrome OS Embedded Controller to user-space
0004  *
0005  * Copyright (C) 2014 Google, Inc.
0006  */
0007 
0008 #include <linux/dmi.h>
0009 #include <linux/kconfig.h>
0010 #include <linux/mfd/core.h>
0011 #include <linux/module.h>
0012 #include <linux/mod_devicetable.h>
0013 #include <linux/of_platform.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/platform_data/cros_ec_chardev.h>
0016 #include <linux/platform_data/cros_ec_commands.h>
0017 #include <linux/platform_data/cros_ec_proto.h>
0018 #include <linux/slab.h>
0019 
0020 #define DRV_NAME "cros-ec-dev"
0021 
0022 static struct class cros_class = {
0023     .owner          = THIS_MODULE,
0024     .name           = "chromeos",
0025 };
0026 
0027 /**
0028  * struct cros_feature_to_name - CrOS feature id to name/short description.
0029  * @id: The feature identifier.
0030  * @name: Device name associated with the feature id.
0031  * @desc: Short name that will be displayed.
0032  */
0033 struct cros_feature_to_name {
0034     unsigned int id;
0035     const char *name;
0036     const char *desc;
0037 };
0038 
0039 /**
0040  * struct cros_feature_to_cells - CrOS feature id to mfd cells association.
0041  * @id: The feature identifier.
0042  * @mfd_cells: Pointer to the array of mfd cells that needs to be added.
0043  * @num_cells: Number of mfd cells into the array.
0044  */
0045 struct cros_feature_to_cells {
0046     unsigned int id;
0047     const struct mfd_cell *mfd_cells;
0048     unsigned int num_cells;
0049 };
0050 
0051 static const struct cros_feature_to_name cros_mcu_devices[] = {
0052     {
0053         .id = EC_FEATURE_FINGERPRINT,
0054         .name   = CROS_EC_DEV_FP_NAME,
0055         .desc   = "Fingerprint",
0056     },
0057     {
0058         .id = EC_FEATURE_ISH,
0059         .name   = CROS_EC_DEV_ISH_NAME,
0060         .desc   = "Integrated Sensor Hub",
0061     },
0062     {
0063         .id = EC_FEATURE_SCP,
0064         .name   = CROS_EC_DEV_SCP_NAME,
0065         .desc   = "System Control Processor",
0066     },
0067     {
0068         .id = EC_FEATURE_SCP_C1,
0069         .name   = CROS_EC_DEV_SCP_C1_NAME,
0070         .desc   = "System Control Processor 2nd Core",
0071     },
0072     {
0073         .id = EC_FEATURE_TOUCHPAD,
0074         .name   = CROS_EC_DEV_TP_NAME,
0075         .desc   = "Touchpad",
0076     },
0077 };
0078 
0079 static const struct mfd_cell cros_ec_cec_cells[] = {
0080     { .name = "cros-ec-cec", },
0081 };
0082 
0083 static const struct mfd_cell cros_ec_rtc_cells[] = {
0084     { .name = "cros-ec-rtc", },
0085 };
0086 
0087 static const struct mfd_cell cros_ec_sensorhub_cells[] = {
0088     { .name = "cros-ec-sensorhub", },
0089 };
0090 
0091 static const struct mfd_cell cros_usbpd_charger_cells[] = {
0092     { .name = "cros-usbpd-charger", },
0093     { .name = "cros-usbpd-logger", },
0094 };
0095 
0096 static const struct mfd_cell cros_usbpd_notify_cells[] = {
0097     { .name = "cros-usbpd-notify", },
0098 };
0099 
0100 static const struct cros_feature_to_cells cros_subdevices[] = {
0101     {
0102         .id     = EC_FEATURE_CEC,
0103         .mfd_cells  = cros_ec_cec_cells,
0104         .num_cells  = ARRAY_SIZE(cros_ec_cec_cells),
0105     },
0106     {
0107         .id     = EC_FEATURE_RTC,
0108         .mfd_cells  = cros_ec_rtc_cells,
0109         .num_cells  = ARRAY_SIZE(cros_ec_rtc_cells),
0110     },
0111     {
0112         .id     = EC_FEATURE_USB_PD,
0113         .mfd_cells  = cros_usbpd_charger_cells,
0114         .num_cells  = ARRAY_SIZE(cros_usbpd_charger_cells),
0115     },
0116 };
0117 
0118 static const struct mfd_cell cros_ec_platform_cells[] = {
0119     { .name = "cros-ec-chardev", },
0120     { .name = "cros-ec-debugfs", },
0121     { .name = "cros-ec-sysfs", },
0122 };
0123 
0124 static const struct mfd_cell cros_ec_pchg_cells[] = {
0125     { .name = "cros-ec-pchg", },
0126 };
0127 
0128 static const struct mfd_cell cros_ec_lightbar_cells[] = {
0129     { .name = "cros-ec-lightbar", }
0130 };
0131 
0132 static const struct mfd_cell cros_ec_vbc_cells[] = {
0133     { .name = "cros-ec-vbc", }
0134 };
0135 
0136 static void cros_ec_class_release(struct device *dev)
0137 {
0138     kfree(to_cros_ec_dev(dev));
0139 }
0140 
0141 static int ec_device_probe(struct platform_device *pdev)
0142 {
0143     int retval = -ENOMEM;
0144     struct device_node *node;
0145     struct device *dev = &pdev->dev;
0146     struct cros_ec_platform *ec_platform = dev_get_platdata(dev);
0147     struct cros_ec_dev *ec = kzalloc(sizeof(*ec), GFP_KERNEL);
0148     struct ec_response_pchg_count pchg_count;
0149     int i;
0150 
0151     if (!ec)
0152         return retval;
0153 
0154     dev_set_drvdata(dev, ec);
0155     ec->ec_dev = dev_get_drvdata(dev->parent);
0156     ec->dev = dev;
0157     ec->cmd_offset = ec_platform->cmd_offset;
0158     ec->features.flags[0] = -1U; /* Not cached yet */
0159     ec->features.flags[1] = -1U; /* Not cached yet */
0160     device_initialize(&ec->class_dev);
0161 
0162     for (i = 0; i < ARRAY_SIZE(cros_mcu_devices); i++) {
0163         /*
0164          * Check whether this is actually a dedicated MCU rather
0165          * than an standard EC.
0166          */
0167         if (cros_ec_check_features(ec, cros_mcu_devices[i].id)) {
0168             dev_info(dev, "CrOS %s MCU detected\n",
0169                  cros_mcu_devices[i].desc);
0170             /*
0171              * Help userspace differentiating ECs from other MCU,
0172              * regardless of the probing order.
0173              */
0174             ec_platform->ec_name = cros_mcu_devices[i].name;
0175             break;
0176         }
0177     }
0178 
0179     /*
0180      * Add the class device
0181      */
0182     ec->class_dev.class = &cros_class;
0183     ec->class_dev.parent = dev;
0184     ec->class_dev.release = cros_ec_class_release;
0185 
0186     retval = dev_set_name(&ec->class_dev, "%s", ec_platform->ec_name);
0187     if (retval) {
0188         dev_err(dev, "dev_set_name failed => %d\n", retval);
0189         goto failed;
0190     }
0191 
0192     retval = device_add(&ec->class_dev);
0193     if (retval)
0194         goto failed;
0195 
0196     /* check whether this EC is a sensor hub. */
0197     if (cros_ec_get_sensor_count(ec) > 0) {
0198         retval = mfd_add_hotplug_devices(ec->dev,
0199                 cros_ec_sensorhub_cells,
0200                 ARRAY_SIZE(cros_ec_sensorhub_cells));
0201         if (retval)
0202             dev_err(ec->dev, "failed to add %s subdevice: %d\n",
0203                 cros_ec_sensorhub_cells->name, retval);
0204     }
0205 
0206     /*
0207      * The following subdevices can be detected by sending the
0208      * EC_FEATURE_GET_CMD Embedded Controller device.
0209      */
0210     for (i = 0; i < ARRAY_SIZE(cros_subdevices); i++) {
0211         if (cros_ec_check_features(ec, cros_subdevices[i].id)) {
0212             retval = mfd_add_hotplug_devices(ec->dev,
0213                         cros_subdevices[i].mfd_cells,
0214                         cros_subdevices[i].num_cells);
0215             if (retval)
0216                 dev_err(ec->dev,
0217                     "failed to add %s subdevice: %d\n",
0218                     cros_subdevices[i].mfd_cells->name,
0219                     retval);
0220         }
0221     }
0222 
0223     /*
0224      * Lightbar is a special case. Newer devices support autodetection,
0225      * but older ones do not.
0226      */
0227     if (cros_ec_check_features(ec, EC_FEATURE_LIGHTBAR) ||
0228         dmi_match(DMI_PRODUCT_NAME, "Link")) {
0229         retval = mfd_add_hotplug_devices(ec->dev,
0230                     cros_ec_lightbar_cells,
0231                     ARRAY_SIZE(cros_ec_lightbar_cells));
0232         if (retval)
0233             dev_warn(ec->dev, "failed to add lightbar: %d\n",
0234                  retval);
0235     }
0236 
0237     /*
0238      * The PD notifier driver cell is separate since it only needs to be
0239      * explicitly added on platforms that don't have the PD notifier ACPI
0240      * device entry defined.
0241      */
0242     if (IS_ENABLED(CONFIG_OF) && ec->ec_dev->dev->of_node) {
0243         if (cros_ec_check_features(ec, EC_FEATURE_USB_PD)) {
0244             retval = mfd_add_hotplug_devices(ec->dev,
0245                     cros_usbpd_notify_cells,
0246                     ARRAY_SIZE(cros_usbpd_notify_cells));
0247             if (retval)
0248                 dev_err(ec->dev,
0249                     "failed to add PD notify devices: %d\n",
0250                     retval);
0251         }
0252     }
0253 
0254     /*
0255      * The PCHG device cannot be detected by sending EC_FEATURE_GET_CMD, but
0256      * it can be detected by querying the number of peripheral chargers.
0257      */
0258     retval = cros_ec_cmd(ec->ec_dev, 0, EC_CMD_PCHG_COUNT, NULL, 0,
0259                  &pchg_count, sizeof(pchg_count));
0260     if (retval >= 0 && pchg_count.port_count) {
0261         retval = mfd_add_hotplug_devices(ec->dev,
0262                     cros_ec_pchg_cells,
0263                     ARRAY_SIZE(cros_ec_pchg_cells));
0264         if (retval)
0265             dev_warn(ec->dev, "failed to add pchg: %d\n",
0266                  retval);
0267     }
0268 
0269     /*
0270      * The following subdevices cannot be detected by sending the
0271      * EC_FEATURE_GET_CMD to the Embedded Controller device.
0272      */
0273     retval = mfd_add_hotplug_devices(ec->dev, cros_ec_platform_cells,
0274                      ARRAY_SIZE(cros_ec_platform_cells));
0275     if (retval)
0276         dev_warn(ec->dev,
0277              "failed to add cros-ec platform devices: %d\n",
0278              retval);
0279 
0280     /* Check whether this EC instance has a VBC NVRAM */
0281     node = ec->ec_dev->dev->of_node;
0282     if (of_property_read_bool(node, "google,has-vbc-nvram")) {
0283         retval = mfd_add_hotplug_devices(ec->dev, cros_ec_vbc_cells,
0284                         ARRAY_SIZE(cros_ec_vbc_cells));
0285         if (retval)
0286             dev_warn(ec->dev, "failed to add VBC devices: %d\n",
0287                  retval);
0288     }
0289 
0290     return 0;
0291 
0292 failed:
0293     put_device(&ec->class_dev);
0294     return retval;
0295 }
0296 
0297 static int ec_device_remove(struct platform_device *pdev)
0298 {
0299     struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev);
0300 
0301     mfd_remove_devices(ec->dev);
0302     device_unregister(&ec->class_dev);
0303     return 0;
0304 }
0305 
0306 static const struct platform_device_id cros_ec_id[] = {
0307     { DRV_NAME, 0 },
0308     { /* sentinel */ }
0309 };
0310 MODULE_DEVICE_TABLE(platform, cros_ec_id);
0311 
0312 static struct platform_driver cros_ec_dev_driver = {
0313     .driver = {
0314         .name = DRV_NAME,
0315     },
0316     .id_table = cros_ec_id,
0317     .probe = ec_device_probe,
0318     .remove = ec_device_remove,
0319 };
0320 
0321 static int __init cros_ec_dev_init(void)
0322 {
0323     int ret;
0324 
0325     ret  = class_register(&cros_class);
0326     if (ret) {
0327         pr_err(CROS_EC_DEV_NAME ": failed to register device class\n");
0328         return ret;
0329     }
0330 
0331     /* Register the driver */
0332     ret = platform_driver_register(&cros_ec_dev_driver);
0333     if (ret < 0) {
0334         pr_warn(CROS_EC_DEV_NAME ": can't register driver: %d\n", ret);
0335         goto failed_devreg;
0336     }
0337     return 0;
0338 
0339 failed_devreg:
0340     class_unregister(&cros_class);
0341     return ret;
0342 }
0343 
0344 static void __exit cros_ec_dev_exit(void)
0345 {
0346     platform_driver_unregister(&cros_ec_dev_driver);
0347     class_unregister(&cros_class);
0348 }
0349 
0350 module_init(cros_ec_dev_init);
0351 module_exit(cros_ec_dev_exit);
0352 
0353 MODULE_AUTHOR("Bill Richardson <wfrichar@chromium.org>");
0354 MODULE_DESCRIPTION("Userspace interface to the Chrome OS Embedded Controller");
0355 MODULE_VERSION("1.0");
0356 MODULE_LICENSE("GPL");