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