1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * cros_ec_dev - expose the Chrome OS Embedded Controller to user-space 4 * 5 * Copyright (C) 2014 Google, Inc. 6 */ 7 8 #include <linux/mfd/core.h> 9 #include <linux/module.h> 10 #include <linux/mod_devicetable.h> 11 #include <linux/of_platform.h> 12 #include <linux/platform_device.h> 13 #include <linux/platform_data/cros_ec_chardev.h> 14 #include <linux/platform_data/cros_ec_commands.h> 15 #include <linux/platform_data/cros_ec_proto.h> 16 #include <linux/slab.h> 17 18 #define DRV_NAME "cros-ec-dev" 19 20 static struct class cros_class = { 21 .owner = THIS_MODULE, 22 .name = "chromeos", 23 }; 24 25 /** 26 * cros_feature_to_name - CrOS feature id to name/short description. 27 * @id: The feature identifier. 28 * @name: Device name associated with the feature id. 29 * @desc: Short name that will be displayed. 30 */ 31 struct cros_feature_to_name { 32 unsigned int id; 33 const char *name; 34 const char *desc; 35 }; 36 37 /** 38 * cros_feature_to_cells - CrOS feature id to mfd cells association. 39 * @id: The feature identifier. 40 * @mfd_cells: Pointer to the array of mfd cells that needs to be added. 41 * @num_cells: Number of mfd cells into the array. 42 */ 43 struct cros_feature_to_cells { 44 unsigned int id; 45 const struct mfd_cell *mfd_cells; 46 unsigned int num_cells; 47 }; 48 49 static const struct cros_feature_to_name cros_mcu_devices[] = { 50 { 51 .id = EC_FEATURE_FINGERPRINT, 52 .name = CROS_EC_DEV_FP_NAME, 53 .desc = "Fingerprint", 54 }, 55 { 56 .id = EC_FEATURE_ISH, 57 .name = CROS_EC_DEV_ISH_NAME, 58 .desc = "Integrated Sensor Hub", 59 }, 60 { 61 .id = EC_FEATURE_SCP, 62 .name = CROS_EC_DEV_SCP_NAME, 63 .desc = "System Control Processor", 64 }, 65 { 66 .id = EC_FEATURE_TOUCHPAD, 67 .name = CROS_EC_DEV_TP_NAME, 68 .desc = "Touchpad", 69 }, 70 }; 71 72 static const struct mfd_cell cros_ec_cec_cells[] = { 73 { .name = "cros-ec-cec", }, 74 }; 75 76 static const struct mfd_cell cros_ec_rtc_cells[] = { 77 { .name = "cros-ec-rtc", }, 78 }; 79 80 static const struct mfd_cell cros_ec_sensorhub_cells[] = { 81 { .name = "cros-ec-sensorhub", }, 82 }; 83 84 static const struct mfd_cell cros_usbpd_charger_cells[] = { 85 { .name = "cros-usbpd-charger", }, 86 { .name = "cros-usbpd-logger", }, 87 }; 88 89 static const struct cros_feature_to_cells cros_subdevices[] = { 90 { 91 .id = EC_FEATURE_CEC, 92 .mfd_cells = cros_ec_cec_cells, 93 .num_cells = ARRAY_SIZE(cros_ec_cec_cells), 94 }, 95 { 96 .id = EC_FEATURE_RTC, 97 .mfd_cells = cros_ec_rtc_cells, 98 .num_cells = ARRAY_SIZE(cros_ec_rtc_cells), 99 }, 100 { 101 .id = EC_FEATURE_USB_PD, 102 .mfd_cells = cros_usbpd_charger_cells, 103 .num_cells = ARRAY_SIZE(cros_usbpd_charger_cells), 104 }, 105 }; 106 107 static const struct mfd_cell cros_ec_platform_cells[] = { 108 { .name = "cros-ec-chardev", }, 109 { .name = "cros-ec-debugfs", }, 110 { .name = "cros-ec-lightbar", }, 111 { .name = "cros-ec-sysfs", }, 112 }; 113 114 static const struct mfd_cell cros_ec_vbc_cells[] = { 115 { .name = "cros-ec-vbc", } 116 }; 117 118 static void cros_ec_class_release(struct device *dev) 119 { 120 kfree(to_cros_ec_dev(dev)); 121 } 122 123 static int ec_device_probe(struct platform_device *pdev) 124 { 125 int retval = -ENOMEM; 126 struct device_node *node; 127 struct device *dev = &pdev->dev; 128 struct cros_ec_platform *ec_platform = dev_get_platdata(dev); 129 struct cros_ec_dev *ec = kzalloc(sizeof(*ec), GFP_KERNEL); 130 int i; 131 132 if (!ec) 133 return retval; 134 135 dev_set_drvdata(dev, ec); 136 ec->ec_dev = dev_get_drvdata(dev->parent); 137 ec->dev = dev; 138 ec->cmd_offset = ec_platform->cmd_offset; 139 ec->features[0] = -1U; /* Not cached yet */ 140 ec->features[1] = -1U; /* Not cached yet */ 141 device_initialize(&ec->class_dev); 142 143 for (i = 0; i < ARRAY_SIZE(cros_mcu_devices); i++) { 144 /* 145 * Check whether this is actually a dedicated MCU rather 146 * than an standard EC. 147 */ 148 if (cros_ec_check_features(ec, cros_mcu_devices[i].id)) { 149 dev_info(dev, "CrOS %s MCU detected\n", 150 cros_mcu_devices[i].desc); 151 /* 152 * Help userspace differentiating ECs from other MCU, 153 * regardless of the probing order. 154 */ 155 ec_platform->ec_name = cros_mcu_devices[i].name; 156 break; 157 } 158 } 159 160 /* 161 * Add the class device 162 */ 163 ec->class_dev.class = &cros_class; 164 ec->class_dev.parent = dev; 165 ec->class_dev.release = cros_ec_class_release; 166 167 retval = dev_set_name(&ec->class_dev, "%s", ec_platform->ec_name); 168 if (retval) { 169 dev_err(dev, "dev_set_name failed => %d\n", retval); 170 goto failed; 171 } 172 173 retval = device_add(&ec->class_dev); 174 if (retval) 175 goto failed; 176 177 /* check whether this EC is a sensor hub. */ 178 if (cros_ec_get_sensor_count(ec) > 0) { 179 retval = mfd_add_hotplug_devices(ec->dev, 180 cros_ec_sensorhub_cells, 181 ARRAY_SIZE(cros_ec_sensorhub_cells)); 182 if (retval) 183 dev_err(ec->dev, "failed to add %s subdevice: %d\n", 184 cros_ec_sensorhub_cells->name, retval); 185 } 186 187 /* 188 * The following subdevices can be detected by sending the 189 * EC_FEATURE_GET_CMD Embedded Controller device. 190 */ 191 for (i = 0; i < ARRAY_SIZE(cros_subdevices); i++) { 192 if (cros_ec_check_features(ec, cros_subdevices[i].id)) { 193 retval = mfd_add_hotplug_devices(ec->dev, 194 cros_subdevices[i].mfd_cells, 195 cros_subdevices[i].num_cells); 196 if (retval) 197 dev_err(ec->dev, 198 "failed to add %s subdevice: %d\n", 199 cros_subdevices[i].mfd_cells->name, 200 retval); 201 } 202 } 203 204 /* 205 * The following subdevices cannot be detected by sending the 206 * EC_FEATURE_GET_CMD to the Embedded Controller device. 207 */ 208 retval = mfd_add_hotplug_devices(ec->dev, cros_ec_platform_cells, 209 ARRAY_SIZE(cros_ec_platform_cells)); 210 if (retval) 211 dev_warn(ec->dev, 212 "failed to add cros-ec platform devices: %d\n", 213 retval); 214 215 /* Check whether this EC instance has a VBC NVRAM */ 216 node = ec->ec_dev->dev->of_node; 217 if (of_property_read_bool(node, "google,has-vbc-nvram")) { 218 retval = mfd_add_hotplug_devices(ec->dev, cros_ec_vbc_cells, 219 ARRAY_SIZE(cros_ec_vbc_cells)); 220 if (retval) 221 dev_warn(ec->dev, "failed to add VBC devices: %d\n", 222 retval); 223 } 224 225 return 0; 226 227 failed: 228 put_device(&ec->class_dev); 229 return retval; 230 } 231 232 static int ec_device_remove(struct platform_device *pdev) 233 { 234 struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev); 235 236 mfd_remove_devices(ec->dev); 237 device_unregister(&ec->class_dev); 238 return 0; 239 } 240 241 static const struct platform_device_id cros_ec_id[] = { 242 { DRV_NAME, 0 }, 243 { /* sentinel */ } 244 }; 245 MODULE_DEVICE_TABLE(platform, cros_ec_id); 246 247 static struct platform_driver cros_ec_dev_driver = { 248 .driver = { 249 .name = DRV_NAME, 250 }, 251 .id_table = cros_ec_id, 252 .probe = ec_device_probe, 253 .remove = ec_device_remove, 254 }; 255 256 static int __init cros_ec_dev_init(void) 257 { 258 int ret; 259 260 ret = class_register(&cros_class); 261 if (ret) { 262 pr_err(CROS_EC_DEV_NAME ": failed to register device class\n"); 263 return ret; 264 } 265 266 /* Register the driver */ 267 ret = platform_driver_register(&cros_ec_dev_driver); 268 if (ret < 0) { 269 pr_warn(CROS_EC_DEV_NAME ": can't register driver: %d\n", ret); 270 goto failed_devreg; 271 } 272 return 0; 273 274 failed_devreg: 275 class_unregister(&cros_class); 276 return ret; 277 } 278 279 static void __exit cros_ec_dev_exit(void) 280 { 281 platform_driver_unregister(&cros_ec_dev_driver); 282 class_unregister(&cros_class); 283 } 284 285 module_init(cros_ec_dev_init); 286 module_exit(cros_ec_dev_exit); 287 288 MODULE_ALIAS("platform:" DRV_NAME); 289 MODULE_AUTHOR("Bill Richardson <wfrichar@chromium.org>"); 290 MODULE_DESCRIPTION("Userspace interface to the Chrome OS Embedded Controller"); 291 MODULE_VERSION("1.0"); 292 MODULE_LICENSE("GPL"); 293