11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 25e011558SThierry Escande /* 35e011558SThierry Escande * cros_ec_dev - expose the Chrome OS Embedded Controller to user-space 45e011558SThierry Escande * 55e011558SThierry Escande * Copyright (C) 2014 Google, Inc. 65e011558SThierry Escande */ 75e011558SThierry Escande 85e011558SThierry Escande #include <linux/mfd/core.h> 9eda2e30cSEnric Balletbo i Serra #include <linux/mfd/cros_ec.h> 105e011558SThierry Escande #include <linux/module.h> 11ac316725SRandy Dunlap #include <linux/mod_devicetable.h> 120545625bSEnric Balletbo i Serra #include <linux/of_platform.h> 135e011558SThierry Escande #include <linux/platform_device.h> 14459aedb9SEnric Balletbo i Serra #include <linux/platform_data/cros_ec_chardev.h> 15840d9f13SEnric Balletbo i Serra #include <linux/platform_data/cros_ec_commands.h> 16840d9f13SEnric Balletbo i Serra #include <linux/platform_data/cros_ec_proto.h> 175e011558SThierry Escande #include <linux/slab.h> 185e011558SThierry Escande 195e011558SThierry Escande #define DRV_NAME "cros-ec-dev" 205e011558SThierry Escande 215e011558SThierry Escande static struct class cros_class = { 225e011558SThierry Escande .owner = THIS_MODULE, 235e011558SThierry Escande .name = "chromeos", 245e011558SThierry Escande }; 255e011558SThierry Escande 26b027dcf7SEnric Balletbo i Serra /** 27b027dcf7SEnric Balletbo i Serra * cros_feature_to_name - CrOS feature id to name/short description. 28b027dcf7SEnric Balletbo i Serra * @id: The feature identifier. 29b027dcf7SEnric Balletbo i Serra * @name: Device name associated with the feature id. 30b027dcf7SEnric Balletbo i Serra * @desc: Short name that will be displayed. 31b027dcf7SEnric Balletbo i Serra */ 32b027dcf7SEnric Balletbo i Serra struct cros_feature_to_name { 33b027dcf7SEnric Balletbo i Serra unsigned int id; 34b027dcf7SEnric Balletbo i Serra const char *name; 35b027dcf7SEnric Balletbo i Serra const char *desc; 36b027dcf7SEnric Balletbo i Serra }; 37b027dcf7SEnric Balletbo i Serra 38832a636fSEnric Balletbo i Serra /** 39832a636fSEnric Balletbo i Serra * cros_feature_to_cells - CrOS feature id to mfd cells association. 40832a636fSEnric Balletbo i Serra * @id: The feature identifier. 41832a636fSEnric Balletbo i Serra * @mfd_cells: Pointer to the array of mfd cells that needs to be added. 42832a636fSEnric Balletbo i Serra * @num_cells: Number of mfd cells into the array. 43832a636fSEnric Balletbo i Serra */ 44832a636fSEnric Balletbo i Serra struct cros_feature_to_cells { 45832a636fSEnric Balletbo i Serra unsigned int id; 46832a636fSEnric Balletbo i Serra const struct mfd_cell *mfd_cells; 47832a636fSEnric Balletbo i Serra unsigned int num_cells; 48832a636fSEnric Balletbo i Serra }; 49832a636fSEnric Balletbo i Serra 50b027dcf7SEnric Balletbo i Serra static const struct cros_feature_to_name cros_mcu_devices[] = { 51b027dcf7SEnric Balletbo i Serra { 52b027dcf7SEnric Balletbo i Serra .id = EC_FEATURE_FINGERPRINT, 53b027dcf7SEnric Balletbo i Serra .name = CROS_EC_DEV_FP_NAME, 54b027dcf7SEnric Balletbo i Serra .desc = "Fingerprint", 55b027dcf7SEnric Balletbo i Serra }, 56b027dcf7SEnric Balletbo i Serra { 57b027dcf7SEnric Balletbo i Serra .id = EC_FEATURE_ISH, 58b027dcf7SEnric Balletbo i Serra .name = CROS_EC_DEV_ISH_NAME, 59b027dcf7SEnric Balletbo i Serra .desc = "Integrated Sensor Hub", 60b027dcf7SEnric Balletbo i Serra }, 61b027dcf7SEnric Balletbo i Serra { 62b027dcf7SEnric Balletbo i Serra .id = EC_FEATURE_SCP, 63b027dcf7SEnric Balletbo i Serra .name = CROS_EC_DEV_SCP_NAME, 64b027dcf7SEnric Balletbo i Serra .desc = "System Control Processor", 65b027dcf7SEnric Balletbo i Serra }, 66b027dcf7SEnric Balletbo i Serra { 67b027dcf7SEnric Balletbo i Serra .id = EC_FEATURE_TOUCHPAD, 68b027dcf7SEnric Balletbo i Serra .name = CROS_EC_DEV_TP_NAME, 69b027dcf7SEnric Balletbo i Serra .desc = "Touchpad", 70b027dcf7SEnric Balletbo i Serra }, 71b027dcf7SEnric Balletbo i Serra }; 72b027dcf7SEnric Balletbo i Serra 73832a636fSEnric Balletbo i Serra static const struct mfd_cell cros_ec_cec_cells[] = { 74832a636fSEnric Balletbo i Serra { .name = "cros-ec-cec", }, 75832a636fSEnric Balletbo i Serra }; 76832a636fSEnric Balletbo i Serra 77832a636fSEnric Balletbo i Serra static const struct mfd_cell cros_ec_rtc_cells[] = { 78832a636fSEnric Balletbo i Serra { .name = "cros-ec-rtc", }, 79832a636fSEnric Balletbo i Serra }; 80832a636fSEnric Balletbo i Serra 81832a636fSEnric Balletbo i Serra static const struct mfd_cell cros_usbpd_charger_cells[] = { 82832a636fSEnric Balletbo i Serra { .name = "cros-usbpd-charger", }, 83832a636fSEnric Balletbo i Serra { .name = "cros-usbpd-logger", }, 84832a636fSEnric Balletbo i Serra }; 85832a636fSEnric Balletbo i Serra 86832a636fSEnric Balletbo i Serra static const struct cros_feature_to_cells cros_subdevices[] = { 87832a636fSEnric Balletbo i Serra { 88832a636fSEnric Balletbo i Serra .id = EC_FEATURE_CEC, 89832a636fSEnric Balletbo i Serra .mfd_cells = cros_ec_cec_cells, 90832a636fSEnric Balletbo i Serra .num_cells = ARRAY_SIZE(cros_ec_cec_cells), 91832a636fSEnric Balletbo i Serra }, 92832a636fSEnric Balletbo i Serra { 93832a636fSEnric Balletbo i Serra .id = EC_FEATURE_RTC, 94832a636fSEnric Balletbo i Serra .mfd_cells = cros_ec_rtc_cells, 95832a636fSEnric Balletbo i Serra .num_cells = ARRAY_SIZE(cros_ec_rtc_cells), 96832a636fSEnric Balletbo i Serra }, 97832a636fSEnric Balletbo i Serra { 98832a636fSEnric Balletbo i Serra .id = EC_FEATURE_USB_PD, 99832a636fSEnric Balletbo i Serra .mfd_cells = cros_usbpd_charger_cells, 100832a636fSEnric Balletbo i Serra .num_cells = ARRAY_SIZE(cros_usbpd_charger_cells), 101832a636fSEnric Balletbo i Serra }, 102832a636fSEnric Balletbo i Serra }; 103832a636fSEnric Balletbo i Serra 104832a636fSEnric Balletbo i Serra static const struct mfd_cell cros_ec_platform_cells[] = { 105832a636fSEnric Balletbo i Serra { .name = "cros-ec-chardev", }, 106832a636fSEnric Balletbo i Serra { .name = "cros-ec-debugfs", }, 107832a636fSEnric Balletbo i Serra { .name = "cros-ec-lightbar", }, 108832a636fSEnric Balletbo i Serra { .name = "cros-ec-sysfs", }, 109832a636fSEnric Balletbo i Serra }; 110832a636fSEnric Balletbo i Serra 111832a636fSEnric Balletbo i Serra static const struct mfd_cell cros_ec_vbc_cells[] = { 112832a636fSEnric Balletbo i Serra { .name = "cros-ec-vbc", } 113832a636fSEnric Balletbo i Serra }; 114832a636fSEnric Balletbo i Serra 1155e011558SThierry Escande static int cros_ec_check_features(struct cros_ec_dev *ec, int feature) 1165e011558SThierry Escande { 1175e011558SThierry Escande struct cros_ec_command *msg; 1185e011558SThierry Escande int ret; 1195e011558SThierry Escande 1205e011558SThierry Escande if (ec->features[0] == -1U && ec->features[1] == -1U) { 1215e011558SThierry Escande /* features bitmap not read yet */ 1225156fb75SEnric Balletbo i Serra msg = kzalloc(sizeof(*msg) + sizeof(ec->features), GFP_KERNEL); 1235e011558SThierry Escande if (!msg) 1245e011558SThierry Escande return -ENOMEM; 1255e011558SThierry Escande 1265e011558SThierry Escande msg->command = EC_CMD_GET_FEATURES + ec->cmd_offset; 1275e011558SThierry Escande msg->insize = sizeof(ec->features); 1285e011558SThierry Escande 1295156fb75SEnric Balletbo i Serra ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); 1305156fb75SEnric Balletbo i Serra if (ret < 0) { 1315e011558SThierry Escande dev_warn(ec->dev, "cannot get EC features: %d/%d\n", 1325e011558SThierry Escande ret, msg->result); 1335e011558SThierry Escande memset(ec->features, 0, sizeof(ec->features)); 134df7c3bf2SStephen Boyd } else { 1355e011558SThierry Escande memcpy(ec->features, msg->data, sizeof(ec->features)); 136df7c3bf2SStephen Boyd } 1375e011558SThierry Escande 1385e011558SThierry Escande dev_dbg(ec->dev, "EC features %08x %08x\n", 1395e011558SThierry Escande ec->features[0], ec->features[1]); 1405e011558SThierry Escande 1415e011558SThierry Escande kfree(msg); 1425e011558SThierry Escande } 1435e011558SThierry Escande 1445e011558SThierry Escande return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature); 1455e011558SThierry Escande } 1465e011558SThierry Escande 14748a2ca0eSEnric Balletbo i Serra static void cros_ec_class_release(struct device *dev) 14848a2ca0eSEnric Balletbo i Serra { 14948a2ca0eSEnric Balletbo i Serra kfree(to_cros_ec_dev(dev)); 15048a2ca0eSEnric Balletbo i Serra } 15148a2ca0eSEnric Balletbo i Serra 1525e011558SThierry Escande static void cros_ec_sensors_register(struct cros_ec_dev *ec) 1535e011558SThierry Escande { 1545e011558SThierry Escande /* 1555e011558SThierry Escande * Issue a command to get the number of sensor reported. 1565e011558SThierry Escande * Build an array of sensors driver and register them all. 1575e011558SThierry Escande */ 1585e011558SThierry Escande int ret, i, id, sensor_num; 1595e011558SThierry Escande struct mfd_cell *sensor_cells; 1605e011558SThierry Escande struct cros_ec_sensor_platform *sensor_platforms; 1615e011558SThierry Escande int sensor_type[MOTIONSENSE_TYPE_MAX]; 1625e011558SThierry Escande struct ec_params_motion_sense *params; 1635e011558SThierry Escande struct ec_response_motion_sense *resp; 1645e011558SThierry Escande struct cros_ec_command *msg; 1655e011558SThierry Escande 1665e011558SThierry Escande msg = kzalloc(sizeof(struct cros_ec_command) + 1675e011558SThierry Escande max(sizeof(*params), sizeof(*resp)), GFP_KERNEL); 1685e011558SThierry Escande if (msg == NULL) 1695e011558SThierry Escande return; 1705e011558SThierry Escande 1715e011558SThierry Escande msg->version = 2; 1725e011558SThierry Escande msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset; 1735e011558SThierry Escande msg->outsize = sizeof(*params); 1745e011558SThierry Escande msg->insize = sizeof(*resp); 1755e011558SThierry Escande 1765e011558SThierry Escande params = (struct ec_params_motion_sense *)msg->data; 1775e011558SThierry Escande params->cmd = MOTIONSENSE_CMD_DUMP; 1785e011558SThierry Escande 1795156fb75SEnric Balletbo i Serra ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); 1805156fb75SEnric Balletbo i Serra if (ret < 0) { 1815e011558SThierry Escande dev_warn(ec->dev, "cannot get EC sensor information: %d/%d\n", 1825e011558SThierry Escande ret, msg->result); 1835e011558SThierry Escande goto error; 1845e011558SThierry Escande } 1855e011558SThierry Escande 1865e011558SThierry Escande resp = (struct ec_response_motion_sense *)msg->data; 1875e011558SThierry Escande sensor_num = resp->dump.sensor_count; 1881bb407f1SGwendal Grignou /* 1891bb407f1SGwendal Grignou * Allocate 2 extra sensors if lid angle sensor and/or FIFO are needed. 1901bb407f1SGwendal Grignou */ 1911bb407f1SGwendal Grignou sensor_cells = kcalloc(sensor_num + 2, sizeof(struct mfd_cell), 1925e011558SThierry Escande GFP_KERNEL); 1935e011558SThierry Escande if (sensor_cells == NULL) 1945e011558SThierry Escande goto error; 1955e011558SThierry Escande 1961bb407f1SGwendal Grignou sensor_platforms = kcalloc(sensor_num, 1976396bb22SKees Cook sizeof(struct cros_ec_sensor_platform), 1986396bb22SKees Cook GFP_KERNEL); 1995e011558SThierry Escande if (sensor_platforms == NULL) 2005e011558SThierry Escande goto error_platforms; 2015e011558SThierry Escande 2025e011558SThierry Escande memset(sensor_type, 0, sizeof(sensor_type)); 2035e011558SThierry Escande id = 0; 2045e011558SThierry Escande for (i = 0; i < sensor_num; i++) { 2055e011558SThierry Escande params->cmd = MOTIONSENSE_CMD_INFO; 2065e011558SThierry Escande params->info.sensor_num = i; 2075156fb75SEnric Balletbo i Serra ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); 2085156fb75SEnric Balletbo i Serra if (ret < 0) { 2095e011558SThierry Escande dev_warn(ec->dev, "no info for EC sensor %d : %d/%d\n", 2105e011558SThierry Escande i, ret, msg->result); 2115e011558SThierry Escande continue; 2125e011558SThierry Escande } 2135e011558SThierry Escande switch (resp->info.type) { 2145e011558SThierry Escande case MOTIONSENSE_TYPE_ACCEL: 2155e011558SThierry Escande sensor_cells[id].name = "cros-ec-accel"; 2165e011558SThierry Escande break; 2175e011558SThierry Escande case MOTIONSENSE_TYPE_BARO: 2185e011558SThierry Escande sensor_cells[id].name = "cros-ec-baro"; 2195e011558SThierry Escande break; 2205e011558SThierry Escande case MOTIONSENSE_TYPE_GYRO: 2215e011558SThierry Escande sensor_cells[id].name = "cros-ec-gyro"; 2225e011558SThierry Escande break; 2235e011558SThierry Escande case MOTIONSENSE_TYPE_MAG: 2245e011558SThierry Escande sensor_cells[id].name = "cros-ec-mag"; 2255e011558SThierry Escande break; 2265e011558SThierry Escande case MOTIONSENSE_TYPE_PROX: 2275e011558SThierry Escande sensor_cells[id].name = "cros-ec-prox"; 2285e011558SThierry Escande break; 2295e011558SThierry Escande case MOTIONSENSE_TYPE_LIGHT: 2305e011558SThierry Escande sensor_cells[id].name = "cros-ec-light"; 2315e011558SThierry Escande break; 2325e011558SThierry Escande case MOTIONSENSE_TYPE_ACTIVITY: 2335e011558SThierry Escande sensor_cells[id].name = "cros-ec-activity"; 2345e011558SThierry Escande break; 2355e011558SThierry Escande default: 2365e011558SThierry Escande dev_warn(ec->dev, "unknown type %d\n", resp->info.type); 2375e011558SThierry Escande continue; 2385e011558SThierry Escande } 2395e011558SThierry Escande sensor_platforms[id].sensor_num = i; 2405e011558SThierry Escande sensor_cells[id].id = sensor_type[resp->info.type]; 2415e011558SThierry Escande sensor_cells[id].platform_data = &sensor_platforms[id]; 2425e011558SThierry Escande sensor_cells[id].pdata_size = 2435e011558SThierry Escande sizeof(struct cros_ec_sensor_platform); 2445e011558SThierry Escande 2455e011558SThierry Escande sensor_type[resp->info.type]++; 2465e011558SThierry Escande id++; 2475e011558SThierry Escande } 2485e011558SThierry Escande 249c1d1e91aSGwendal Grignou if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2) 250c1d1e91aSGwendal Grignou ec->has_kb_wake_angle = true; 251c1d1e91aSGwendal Grignou 2525e011558SThierry Escande if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) { 2535e011558SThierry Escande sensor_cells[id].name = "cros-ec-ring"; 2545e011558SThierry Escande id++; 2555e011558SThierry Escande } 2561bb407f1SGwendal Grignou if (cros_ec_check_features(ec, 2571bb407f1SGwendal Grignou EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS)) { 2581bb407f1SGwendal Grignou sensor_cells[id].name = "cros-ec-lid-angle"; 2591bb407f1SGwendal Grignou id++; 2601bb407f1SGwendal Grignou } 2615e011558SThierry Escande 2625e011558SThierry Escande ret = mfd_add_devices(ec->dev, 0, sensor_cells, id, 2635e011558SThierry Escande NULL, 0, NULL); 2645e011558SThierry Escande if (ret) 2655e011558SThierry Escande dev_err(ec->dev, "failed to add EC sensors\n"); 2665e011558SThierry Escande 2675e011558SThierry Escande kfree(sensor_platforms); 2685e011558SThierry Escande error_platforms: 2695e011558SThierry Escande kfree(sensor_cells); 2705e011558SThierry Escande error: 2715e011558SThierry Escande kfree(msg); 2725e011558SThierry Escande } 2735e011558SThierry Escande 274ecc2ca47SGwendal Grignou static struct cros_ec_sensor_platform sensor_platforms[] = { 275ecc2ca47SGwendal Grignou { .sensor_num = 0 }, 276ecc2ca47SGwendal Grignou { .sensor_num = 1 } 277ecc2ca47SGwendal Grignou }; 278ecc2ca47SGwendal Grignou 279ecc2ca47SGwendal Grignou static const struct mfd_cell cros_ec_accel_legacy_cells[] = { 280ecc2ca47SGwendal Grignou { 281ecc2ca47SGwendal Grignou .name = "cros-ec-accel-legacy", 282ecc2ca47SGwendal Grignou .platform_data = &sensor_platforms[0], 283ecc2ca47SGwendal Grignou .pdata_size = sizeof(struct cros_ec_sensor_platform), 284ecc2ca47SGwendal Grignou }, 285ecc2ca47SGwendal Grignou { 286ecc2ca47SGwendal Grignou .name = "cros-ec-accel-legacy", 287ecc2ca47SGwendal Grignou .platform_data = &sensor_platforms[1], 288ecc2ca47SGwendal Grignou .pdata_size = sizeof(struct cros_ec_sensor_platform), 289ecc2ca47SGwendal Grignou } 290ecc2ca47SGwendal Grignou }; 291ecc2ca47SGwendal Grignou 292ecc2ca47SGwendal Grignou static void cros_ec_accel_legacy_register(struct cros_ec_dev *ec) 293ecc2ca47SGwendal Grignou { 294ecc2ca47SGwendal Grignou struct cros_ec_device *ec_dev = ec->ec_dev; 295ecc2ca47SGwendal Grignou u8 status; 296ecc2ca47SGwendal Grignou int ret; 297ecc2ca47SGwendal Grignou 298ecc2ca47SGwendal Grignou /* 299ecc2ca47SGwendal Grignou * ECs that need legacy support are the main EC, directly connected to 300ecc2ca47SGwendal Grignou * the AP. 301ecc2ca47SGwendal Grignou */ 302ecc2ca47SGwendal Grignou if (ec->cmd_offset != 0) 303ecc2ca47SGwendal Grignou return; 304ecc2ca47SGwendal Grignou 305ecc2ca47SGwendal Grignou /* 306ecc2ca47SGwendal Grignou * Check if EC supports direct memory reads and if EC has 307ecc2ca47SGwendal Grignou * accelerometers. 308ecc2ca47SGwendal Grignou */ 309ecc2ca47SGwendal Grignou if (ec_dev->cmd_readmem) { 310ecc2ca47SGwendal Grignou ret = ec_dev->cmd_readmem(ec_dev, EC_MEMMAP_ACC_STATUS, 1, 311ecc2ca47SGwendal Grignou &status); 312ecc2ca47SGwendal Grignou if (ret < 0) { 313ecc2ca47SGwendal Grignou dev_warn(ec->dev, "EC direct read error.\n"); 314ecc2ca47SGwendal Grignou return; 315ecc2ca47SGwendal Grignou } 316ecc2ca47SGwendal Grignou 317ecc2ca47SGwendal Grignou /* Check if EC has accelerometers. */ 318ecc2ca47SGwendal Grignou if (!(status & EC_MEMMAP_ACC_STATUS_PRESENCE_BIT)) { 319ecc2ca47SGwendal Grignou dev_info(ec->dev, "EC does not have accelerometers.\n"); 320ecc2ca47SGwendal Grignou return; 321ecc2ca47SGwendal Grignou } 322ecc2ca47SGwendal Grignou } 323ecc2ca47SGwendal Grignou 324ecc2ca47SGwendal Grignou /* 325ecc2ca47SGwendal Grignou * The device may still support accelerometers: 326ecc2ca47SGwendal Grignou * it would be an older ARM based device that do not suppor the 327ecc2ca47SGwendal Grignou * EC_CMD_GET_FEATURES command. 328ecc2ca47SGwendal Grignou * 329ecc2ca47SGwendal Grignou * Register 2 accelerometers, we will fail in the IIO driver if there 330ecc2ca47SGwendal Grignou * are no sensors. 331ecc2ca47SGwendal Grignou */ 332ecc2ca47SGwendal Grignou ret = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO, 333ecc2ca47SGwendal Grignou cros_ec_accel_legacy_cells, 334ecc2ca47SGwendal Grignou ARRAY_SIZE(cros_ec_accel_legacy_cells), 335ecc2ca47SGwendal Grignou NULL, 0, NULL); 336ecc2ca47SGwendal Grignou if (ret) 337ecc2ca47SGwendal Grignou dev_err(ec_dev->dev, "failed to add EC sensors\n"); 338ecc2ca47SGwendal Grignou } 339ecc2ca47SGwendal Grignou 3405e011558SThierry Escande static int ec_device_probe(struct platform_device *pdev) 3415e011558SThierry Escande { 3425e011558SThierry Escande int retval = -ENOMEM; 3430545625bSEnric Balletbo i Serra struct device_node *node; 3445e011558SThierry Escande struct device *dev = &pdev->dev; 3455e011558SThierry Escande struct cros_ec_platform *ec_platform = dev_get_platdata(dev); 34648a2ca0eSEnric Balletbo i Serra struct cros_ec_dev *ec = kzalloc(sizeof(*ec), GFP_KERNEL); 347b027dcf7SEnric Balletbo i Serra int i; 3485e011558SThierry Escande 3495e011558SThierry Escande if (!ec) 3505e011558SThierry Escande return retval; 3515e011558SThierry Escande 3525e011558SThierry Escande dev_set_drvdata(dev, ec); 3535e011558SThierry Escande ec->ec_dev = dev_get_drvdata(dev->parent); 3545e011558SThierry Escande ec->dev = dev; 3555e011558SThierry Escande ec->cmd_offset = ec_platform->cmd_offset; 3565e011558SThierry Escande ec->features[0] = -1U; /* Not cached yet */ 3575e011558SThierry Escande ec->features[1] = -1U; /* Not cached yet */ 3585e011558SThierry Escande device_initialize(&ec->class_dev); 3595e011558SThierry Escande 360b027dcf7SEnric Balletbo i Serra for (i = 0; i < ARRAY_SIZE(cros_mcu_devices); i++) { 36190486af5SEnric Balletbo i Serra /* 362b027dcf7SEnric Balletbo i Serra * Check whether this is actually a dedicated MCU rather 363b027dcf7SEnric Balletbo i Serra * than an standard EC. 364b027dcf7SEnric Balletbo i Serra */ 365b027dcf7SEnric Balletbo i Serra if (cros_ec_check_features(ec, cros_mcu_devices[i].id)) { 366b027dcf7SEnric Balletbo i Serra dev_info(dev, "CrOS %s MCU detected\n", 367b027dcf7SEnric Balletbo i Serra cros_mcu_devices[i].desc); 368b027dcf7SEnric Balletbo i Serra /* 369b027dcf7SEnric Balletbo i Serra * Help userspace differentiating ECs from other MCU, 37090486af5SEnric Balletbo i Serra * regardless of the probing order. 37190486af5SEnric Balletbo i Serra */ 372b027dcf7SEnric Balletbo i Serra ec_platform->ec_name = cros_mcu_devices[i].name; 373b027dcf7SEnric Balletbo i Serra break; 37490486af5SEnric Balletbo i Serra } 375554e937eSPi-Hsun Shih } 376554e937eSPi-Hsun Shih 377d4cee950SRushikesh S Kadam /* 3785e011558SThierry Escande * Add the class device 3795e011558SThierry Escande */ 3805e011558SThierry Escande ec->class_dev.class = &cros_class; 3815e011558SThierry Escande ec->class_dev.parent = dev; 38248a2ca0eSEnric Balletbo i Serra ec->class_dev.release = cros_ec_class_release; 3835e011558SThierry Escande 3845e011558SThierry Escande retval = dev_set_name(&ec->class_dev, "%s", ec_platform->ec_name); 3855e011558SThierry Escande if (retval) { 3865e011558SThierry Escande dev_err(dev, "dev_set_name failed => %d\n", retval); 3875e011558SThierry Escande goto failed; 3885e011558SThierry Escande } 3895e011558SThierry Escande 390459aedb9SEnric Balletbo i Serra retval = device_add(&ec->class_dev); 391459aedb9SEnric Balletbo i Serra if (retval) 392459aedb9SEnric Balletbo i Serra goto failed; 393459aedb9SEnric Balletbo i Serra 394c1d1e91aSGwendal Grignou /* check whether this EC is a sensor hub. */ 395c1d1e91aSGwendal Grignou if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE)) 396c1d1e91aSGwendal Grignou cros_ec_sensors_register(ec); 397ecc2ca47SGwendal Grignou else 398ecc2ca47SGwendal Grignou /* Workaroud for older EC firmware */ 399ecc2ca47SGwendal Grignou cros_ec_accel_legacy_register(ec); 400c1d1e91aSGwendal Grignou 401832a636fSEnric Balletbo i Serra /* 402832a636fSEnric Balletbo i Serra * The following subdevices can be detected by sending the 403832a636fSEnric Balletbo i Serra * EC_FEATURE_GET_CMD Embedded Controller device. 404832a636fSEnric Balletbo i Serra */ 405832a636fSEnric Balletbo i Serra for (i = 0; i < ARRAY_SIZE(cros_subdevices); i++) { 406832a636fSEnric Balletbo i Serra if (cros_ec_check_features(ec, cros_subdevices[i].id)) { 407832a636fSEnric Balletbo i Serra retval = mfd_add_hotplug_devices(ec->dev, 408832a636fSEnric Balletbo i Serra cros_subdevices[i].mfd_cells, 409832a636fSEnric Balletbo i Serra cros_subdevices[i].num_cells); 41003a5755cSNeil Armstrong if (retval) 41103a5755cSNeil Armstrong dev_err(ec->dev, 412832a636fSEnric Balletbo i Serra "failed to add %s subdevice: %d\n", 413832a636fSEnric Balletbo i Serra cros_subdevices[i].mfd_cells->name, 41403a5755cSNeil Armstrong retval); 41503a5755cSNeil Armstrong } 41695a4d07fSEnric Balletbo i Serra } 41795a4d07fSEnric Balletbo i Serra 418832a636fSEnric Balletbo i Serra /* 419832a636fSEnric Balletbo i Serra * The following subdevices cannot be detected by sending the 420832a636fSEnric Balletbo i Serra * EC_FEATURE_GET_CMD to the Embedded Controller device. 421832a636fSEnric Balletbo i Serra */ 422ecf8a6cdSEnric Balletbo i Serra retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO, 423ecf8a6cdSEnric Balletbo i Serra cros_ec_platform_cells, 424ecf8a6cdSEnric Balletbo i Serra ARRAY_SIZE(cros_ec_platform_cells), 425ecf8a6cdSEnric Balletbo i Serra NULL, 0, NULL); 426ecf8a6cdSEnric Balletbo i Serra if (retval) 427ecf8a6cdSEnric Balletbo i Serra dev_warn(ec->dev, 428ecf8a6cdSEnric Balletbo i Serra "failed to add cros-ec platform devices: %d\n", 429ecf8a6cdSEnric Balletbo i Serra retval); 430ecf8a6cdSEnric Balletbo i Serra 4310545625bSEnric Balletbo i Serra /* Check whether this EC instance has a VBC NVRAM */ 4320545625bSEnric Balletbo i Serra node = ec->ec_dev->dev->of_node; 4330545625bSEnric Balletbo i Serra if (of_property_read_bool(node, "google,has-vbc-nvram")) { 4340545625bSEnric Balletbo i Serra retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO, 4350545625bSEnric Balletbo i Serra cros_ec_vbc_cells, 4360545625bSEnric Balletbo i Serra ARRAY_SIZE(cros_ec_vbc_cells), 4370545625bSEnric Balletbo i Serra NULL, 0, NULL); 4380545625bSEnric Balletbo i Serra if (retval) 4390545625bSEnric Balletbo i Serra dev_warn(ec->dev, "failed to add VBC devices: %d\n", 4400545625bSEnric Balletbo i Serra retval); 4410545625bSEnric Balletbo i Serra } 4420545625bSEnric Balletbo i Serra 4435e011558SThierry Escande return 0; 4445e011558SThierry Escande 4455e011558SThierry Escande failed: 4465e011558SThierry Escande put_device(&ec->class_dev); 4475e011558SThierry Escande return retval; 4485e011558SThierry Escande } 4495e011558SThierry Escande 4505e011558SThierry Escande static int ec_device_remove(struct platform_device *pdev) 4515e011558SThierry Escande { 4525e011558SThierry Escande struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev); 4535e011558SThierry Escande 45418e294ddSEnric Balletbo i Serra mfd_remove_devices(ec->dev); 4555e011558SThierry Escande device_unregister(&ec->class_dev); 4565e011558SThierry Escande return 0; 4575e011558SThierry Escande } 4585e011558SThierry Escande 4595e011558SThierry Escande static const struct platform_device_id cros_ec_id[] = { 4605e011558SThierry Escande { DRV_NAME, 0 }, 461abeed71bSWei-Ning Huang { /* sentinel */ } 4625e011558SThierry Escande }; 4635e011558SThierry Escande MODULE_DEVICE_TABLE(platform, cros_ec_id); 4645e011558SThierry Escande 4655e011558SThierry Escande static struct platform_driver cros_ec_dev_driver = { 4665e011558SThierry Escande .driver = { 4675e011558SThierry Escande .name = DRV_NAME, 4685e011558SThierry Escande }, 4696eb35784SNathan Chancellor .id_table = cros_ec_id, 4705e011558SThierry Escande .probe = ec_device_probe, 4715e011558SThierry Escande .remove = ec_device_remove, 4725e011558SThierry Escande }; 4735e011558SThierry Escande 4745e011558SThierry Escande static int __init cros_ec_dev_init(void) 4755e011558SThierry Escande { 4765e011558SThierry Escande int ret; 4775e011558SThierry Escande 4785e011558SThierry Escande ret = class_register(&cros_class); 4795e011558SThierry Escande if (ret) { 4805e011558SThierry Escande pr_err(CROS_EC_DEV_NAME ": failed to register device class\n"); 4815e011558SThierry Escande return ret; 4825e011558SThierry Escande } 4835e011558SThierry Escande 4845e011558SThierry Escande /* Register the driver */ 4855e011558SThierry Escande ret = platform_driver_register(&cros_ec_dev_driver); 4865e011558SThierry Escande if (ret < 0) { 4875e011558SThierry Escande pr_warn(CROS_EC_DEV_NAME ": can't register driver: %d\n", ret); 4885e011558SThierry Escande goto failed_devreg; 4895e011558SThierry Escande } 4905e011558SThierry Escande return 0; 4915e011558SThierry Escande 4925e011558SThierry Escande failed_devreg: 4935e011558SThierry Escande class_unregister(&cros_class); 4945e011558SThierry Escande return ret; 4955e011558SThierry Escande } 4965e011558SThierry Escande 4975e011558SThierry Escande static void __exit cros_ec_dev_exit(void) 4985e011558SThierry Escande { 4995e011558SThierry Escande platform_driver_unregister(&cros_ec_dev_driver); 5005e011558SThierry Escande class_unregister(&cros_class); 5015e011558SThierry Escande } 5025e011558SThierry Escande 5035e011558SThierry Escande module_init(cros_ec_dev_init); 5045e011558SThierry Escande module_exit(cros_ec_dev_exit); 5055e011558SThierry Escande 5065e011558SThierry Escande MODULE_ALIAS("platform:" DRV_NAME); 5075e011558SThierry Escande MODULE_AUTHOR("Bill Richardson <wfrichar@chromium.org>"); 5085e011558SThierry Escande MODULE_DESCRIPTION("Userspace interface to the Chrome OS Embedded Controller"); 5095e011558SThierry Escande MODULE_VERSION("1.0"); 5105e011558SThierry Escande MODULE_LICENSE("GPL"); 511