0001
0002
0003
0004
0005
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
0029
0030
0031
0032
0033 struct cros_feature_to_name {
0034 unsigned int id;
0035 const char *name;
0036 const char *desc;
0037 };
0038
0039
0040
0041
0042
0043
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;
0159 ec->features.flags[1] = -1U;
0160 device_initialize(&ec->class_dev);
0161
0162 for (i = 0; i < ARRAY_SIZE(cros_mcu_devices); i++) {
0163
0164
0165
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
0172
0173
0174 ec_platform->ec_name = cros_mcu_devices[i].name;
0175 break;
0176 }
0177 }
0178
0179
0180
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
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
0208
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
0225
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
0239
0240
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
0256
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
0271
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
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 { }
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
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");