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/fs.h> 95e011558SThierry Escande #include <linux/mfd/core.h> 10eda2e30cSEnric Balletbo i Serra #include <linux/mfd/cros_ec.h> 11eda2e30cSEnric Balletbo i Serra #include <linux/mfd/cros_ec_commands.h> 125e011558SThierry Escande #include <linux/module.h> 13ac316725SRandy Dunlap #include <linux/mod_devicetable.h> 14eda2e30cSEnric Balletbo i Serra #include <linux/platform_data/cros_ec_chardev.h> 150545625bSEnric Balletbo i Serra #include <linux/of_platform.h> 165e011558SThierry Escande #include <linux/platform_device.h> 175e011558SThierry Escande #include <linux/pm.h> 185e011558SThierry Escande #include <linux/slab.h> 195e011558SThierry Escande #include <linux/uaccess.h> 205e011558SThierry Escande 215e011558SThierry Escande 225e011558SThierry Escande #define DRV_NAME "cros-ec-dev" 235e011558SThierry Escande 245e011558SThierry Escande /* Device variables */ 255e011558SThierry Escande #define CROS_MAX_DEV 128 265e011558SThierry Escande static int ec_major; 275e011558SThierry Escande 285e011558SThierry Escande static struct class cros_class = { 295e011558SThierry Escande .owner = THIS_MODULE, 305e011558SThierry Escande .name = "chromeos", 315e011558SThierry Escande }; 325e011558SThierry Escande 335e011558SThierry Escande /* Basic communication */ 345e011558SThierry Escande static int ec_get_version(struct cros_ec_dev *ec, char *str, int maxlen) 355e011558SThierry Escande { 365e011558SThierry Escande struct ec_response_get_version *resp; 375e011558SThierry Escande static const char * const current_image_name[] = { 385e011558SThierry Escande "unknown", "read-only", "read-write", "invalid", 395e011558SThierry Escande }; 405e011558SThierry Escande struct cros_ec_command *msg; 415e011558SThierry Escande int ret; 425e011558SThierry Escande 435e011558SThierry Escande msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL); 445e011558SThierry Escande if (!msg) 455e011558SThierry Escande return -ENOMEM; 465e011558SThierry Escande 475e011558SThierry Escande msg->version = 0; 485e011558SThierry Escande msg->command = EC_CMD_GET_VERSION + ec->cmd_offset; 495e011558SThierry Escande msg->insize = sizeof(*resp); 505e011558SThierry Escande msg->outsize = 0; 515e011558SThierry Escande 525e011558SThierry Escande ret = cros_ec_cmd_xfer(ec->ec_dev, msg); 535e011558SThierry Escande if (ret < 0) 545e011558SThierry Escande goto exit; 555e011558SThierry Escande 565e011558SThierry Escande if (msg->result != EC_RES_SUCCESS) { 575e011558SThierry Escande snprintf(str, maxlen, 585e011558SThierry Escande "%s\nUnknown EC version: EC returned %d\n", 595e011558SThierry Escande CROS_EC_DEV_VERSION, msg->result); 605e011558SThierry Escande ret = -EINVAL; 615e011558SThierry Escande goto exit; 625e011558SThierry Escande } 635e011558SThierry Escande 645e011558SThierry Escande resp = (struct ec_response_get_version *)msg->data; 655e011558SThierry Escande if (resp->current_image >= ARRAY_SIZE(current_image_name)) 665e011558SThierry Escande resp->current_image = 3; /* invalid */ 675e011558SThierry Escande 685e011558SThierry Escande snprintf(str, maxlen, "%s\n%s\n%s\n%s\n", CROS_EC_DEV_VERSION, 695e011558SThierry Escande resp->version_string_ro, resp->version_string_rw, 705e011558SThierry Escande current_image_name[resp->current_image]); 715e011558SThierry Escande 725e011558SThierry Escande ret = 0; 735e011558SThierry Escande exit: 745e011558SThierry Escande kfree(msg); 755e011558SThierry Escande return ret; 765e011558SThierry Escande } 775e011558SThierry Escande 785e011558SThierry Escande static int cros_ec_check_features(struct cros_ec_dev *ec, int feature) 795e011558SThierry Escande { 805e011558SThierry Escande struct cros_ec_command *msg; 815e011558SThierry Escande int ret; 825e011558SThierry Escande 835e011558SThierry Escande if (ec->features[0] == -1U && ec->features[1] == -1U) { 845e011558SThierry Escande /* features bitmap not read yet */ 855e011558SThierry Escande 865e011558SThierry Escande msg = kmalloc(sizeof(*msg) + sizeof(ec->features), GFP_KERNEL); 875e011558SThierry Escande if (!msg) 885e011558SThierry Escande return -ENOMEM; 895e011558SThierry Escande 905e011558SThierry Escande msg->version = 0; 915e011558SThierry Escande msg->command = EC_CMD_GET_FEATURES + ec->cmd_offset; 925e011558SThierry Escande msg->insize = sizeof(ec->features); 935e011558SThierry Escande msg->outsize = 0; 945e011558SThierry Escande 955e011558SThierry Escande ret = cros_ec_cmd_xfer(ec->ec_dev, msg); 965e011558SThierry Escande if (ret < 0 || msg->result != EC_RES_SUCCESS) { 975e011558SThierry Escande dev_warn(ec->dev, "cannot get EC features: %d/%d\n", 985e011558SThierry Escande ret, msg->result); 995e011558SThierry Escande memset(ec->features, 0, sizeof(ec->features)); 100df7c3bf2SStephen Boyd } else { 1015e011558SThierry Escande memcpy(ec->features, msg->data, sizeof(ec->features)); 102df7c3bf2SStephen Boyd } 1035e011558SThierry Escande 1045e011558SThierry Escande dev_dbg(ec->dev, "EC features %08x %08x\n", 1055e011558SThierry Escande ec->features[0], ec->features[1]); 1065e011558SThierry Escande 1075e011558SThierry Escande kfree(msg); 1085e011558SThierry Escande } 1095e011558SThierry Escande 1105e011558SThierry Escande return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature); 1115e011558SThierry Escande } 1125e011558SThierry Escande 1135e011558SThierry Escande /* Device file ops */ 1145e011558SThierry Escande static int ec_device_open(struct inode *inode, struct file *filp) 1155e011558SThierry Escande { 1165e011558SThierry Escande struct cros_ec_dev *ec = container_of(inode->i_cdev, 1175e011558SThierry Escande struct cros_ec_dev, cdev); 1185e011558SThierry Escande filp->private_data = ec; 1195e011558SThierry Escande nonseekable_open(inode, filp); 1205e011558SThierry Escande return 0; 1215e011558SThierry Escande } 1225e011558SThierry Escande 1235e011558SThierry Escande static int ec_device_release(struct inode *inode, struct file *filp) 1245e011558SThierry Escande { 1255e011558SThierry Escande return 0; 1265e011558SThierry Escande } 1275e011558SThierry Escande 1285e011558SThierry Escande static ssize_t ec_device_read(struct file *filp, char __user *buffer, 1295e011558SThierry Escande size_t length, loff_t *offset) 1305e011558SThierry Escande { 1315e011558SThierry Escande struct cros_ec_dev *ec = filp->private_data; 1325e011558SThierry Escande char msg[sizeof(struct ec_response_get_version) + 1335e011558SThierry Escande sizeof(CROS_EC_DEV_VERSION)]; 1345e011558SThierry Escande size_t count; 1355e011558SThierry Escande int ret; 1365e011558SThierry Escande 1375e011558SThierry Escande if (*offset != 0) 1385e011558SThierry Escande return 0; 1395e011558SThierry Escande 1405e011558SThierry Escande ret = ec_get_version(ec, msg, sizeof(msg)); 1415e011558SThierry Escande if (ret) 1425e011558SThierry Escande return ret; 1435e011558SThierry Escande 1445e011558SThierry Escande count = min(length, strlen(msg)); 1455e011558SThierry Escande 1465e011558SThierry Escande if (copy_to_user(buffer, msg, count)) 1475e011558SThierry Escande return -EFAULT; 1485e011558SThierry Escande 1495e011558SThierry Escande *offset = count; 1505e011558SThierry Escande return count; 1515e011558SThierry Escande } 1525e011558SThierry Escande 1535e011558SThierry Escande /* Ioctls */ 1545e011558SThierry Escande static long ec_device_ioctl_xcmd(struct cros_ec_dev *ec, void __user *arg) 1555e011558SThierry Escande { 1565e011558SThierry Escande long ret; 1575e011558SThierry Escande struct cros_ec_command u_cmd; 1585e011558SThierry Escande struct cros_ec_command *s_cmd; 1595e011558SThierry Escande 1605e011558SThierry Escande if (copy_from_user(&u_cmd, arg, sizeof(u_cmd))) 1615e011558SThierry Escande return -EFAULT; 1625e011558SThierry Escande 1635e011558SThierry Escande if ((u_cmd.outsize > EC_MAX_MSG_BYTES) || 1645e011558SThierry Escande (u_cmd.insize > EC_MAX_MSG_BYTES)) 1655e011558SThierry Escande return -EINVAL; 1665e011558SThierry Escande 1675e011558SThierry Escande s_cmd = kmalloc(sizeof(*s_cmd) + max(u_cmd.outsize, u_cmd.insize), 1685e011558SThierry Escande GFP_KERNEL); 1695e011558SThierry Escande if (!s_cmd) 1705e011558SThierry Escande return -ENOMEM; 1715e011558SThierry Escande 1725e011558SThierry Escande if (copy_from_user(s_cmd, arg, sizeof(*s_cmd) + u_cmd.outsize)) { 1735e011558SThierry Escande ret = -EFAULT; 1745e011558SThierry Escande goto exit; 1755e011558SThierry Escande } 1765e011558SThierry Escande 1775e011558SThierry Escande if (u_cmd.outsize != s_cmd->outsize || 1785e011558SThierry Escande u_cmd.insize != s_cmd->insize) { 1795e011558SThierry Escande ret = -EINVAL; 1805e011558SThierry Escande goto exit; 1815e011558SThierry Escande } 1825e011558SThierry Escande 1835e011558SThierry Escande s_cmd->command += ec->cmd_offset; 1845e011558SThierry Escande ret = cros_ec_cmd_xfer(ec->ec_dev, s_cmd); 1855e011558SThierry Escande /* Only copy data to userland if data was received. */ 1865e011558SThierry Escande if (ret < 0) 1875e011558SThierry Escande goto exit; 1885e011558SThierry Escande 1895e011558SThierry Escande if (copy_to_user(arg, s_cmd, sizeof(*s_cmd) + s_cmd->insize)) 1905e011558SThierry Escande ret = -EFAULT; 1915e011558SThierry Escande exit: 1925e011558SThierry Escande kfree(s_cmd); 1935e011558SThierry Escande return ret; 1945e011558SThierry Escande } 1955e011558SThierry Escande 1965e011558SThierry Escande static long ec_device_ioctl_readmem(struct cros_ec_dev *ec, void __user *arg) 1975e011558SThierry Escande { 1985e011558SThierry Escande struct cros_ec_device *ec_dev = ec->ec_dev; 1995e011558SThierry Escande struct cros_ec_readmem s_mem = { }; 2005e011558SThierry Escande long num; 2015e011558SThierry Escande 2025e011558SThierry Escande /* Not every platform supports direct reads */ 2035e011558SThierry Escande if (!ec_dev->cmd_readmem) 2045e011558SThierry Escande return -ENOTTY; 2055e011558SThierry Escande 2065e011558SThierry Escande if (copy_from_user(&s_mem, arg, sizeof(s_mem))) 2075e011558SThierry Escande return -EFAULT; 2085e011558SThierry Escande 2095e011558SThierry Escande num = ec_dev->cmd_readmem(ec_dev, s_mem.offset, s_mem.bytes, 2105e011558SThierry Escande s_mem.buffer); 2115e011558SThierry Escande if (num <= 0) 2125e011558SThierry Escande return num; 2135e011558SThierry Escande 2145e011558SThierry Escande if (copy_to_user((void __user *)arg, &s_mem, sizeof(s_mem))) 2155e011558SThierry Escande return -EFAULT; 2165e011558SThierry Escande 217c1778e58SGuenter Roeck return num; 2185e011558SThierry Escande } 2195e011558SThierry Escande 2205e011558SThierry Escande static long ec_device_ioctl(struct file *filp, unsigned int cmd, 2215e011558SThierry Escande unsigned long arg) 2225e011558SThierry Escande { 2235e011558SThierry Escande struct cros_ec_dev *ec = filp->private_data; 2245e011558SThierry Escande 2255e011558SThierry Escande if (_IOC_TYPE(cmd) != CROS_EC_DEV_IOC) 2265e011558SThierry Escande return -ENOTTY; 2275e011558SThierry Escande 2285e011558SThierry Escande switch (cmd) { 2295e011558SThierry Escande case CROS_EC_DEV_IOCXCMD: 2305e011558SThierry Escande return ec_device_ioctl_xcmd(ec, (void __user *)arg); 2315e011558SThierry Escande case CROS_EC_DEV_IOCRDMEM: 2325e011558SThierry Escande return ec_device_ioctl_readmem(ec, (void __user *)arg); 2335e011558SThierry Escande } 2345e011558SThierry Escande 2355e011558SThierry Escande return -ENOTTY; 2365e011558SThierry Escande } 2375e011558SThierry Escande 2385e011558SThierry Escande /* Module initialization */ 2395e011558SThierry Escande static const struct file_operations fops = { 2405e011558SThierry Escande .open = ec_device_open, 2415e011558SThierry Escande .release = ec_device_release, 2425e011558SThierry Escande .read = ec_device_read, 2435e011558SThierry Escande .unlocked_ioctl = ec_device_ioctl, 2445e011558SThierry Escande #ifdef CONFIG_COMPAT 2455e011558SThierry Escande .compat_ioctl = ec_device_ioctl, 2465e011558SThierry Escande #endif 2475e011558SThierry Escande }; 2485e011558SThierry Escande 24948a2ca0eSEnric Balletbo i Serra static void cros_ec_class_release(struct device *dev) 25048a2ca0eSEnric Balletbo i Serra { 25148a2ca0eSEnric Balletbo i Serra kfree(to_cros_ec_dev(dev)); 25248a2ca0eSEnric Balletbo i Serra } 25348a2ca0eSEnric Balletbo i Serra 2545e011558SThierry Escande static void cros_ec_sensors_register(struct cros_ec_dev *ec) 2555e011558SThierry Escande { 2565e011558SThierry Escande /* 2575e011558SThierry Escande * Issue a command to get the number of sensor reported. 2585e011558SThierry Escande * Build an array of sensors driver and register them all. 2595e011558SThierry Escande */ 2605e011558SThierry Escande int ret, i, id, sensor_num; 2615e011558SThierry Escande struct mfd_cell *sensor_cells; 2625e011558SThierry Escande struct cros_ec_sensor_platform *sensor_platforms; 2635e011558SThierry Escande int sensor_type[MOTIONSENSE_TYPE_MAX]; 2645e011558SThierry Escande struct ec_params_motion_sense *params; 2655e011558SThierry Escande struct ec_response_motion_sense *resp; 2665e011558SThierry Escande struct cros_ec_command *msg; 2675e011558SThierry Escande 2685e011558SThierry Escande msg = kzalloc(sizeof(struct cros_ec_command) + 2695e011558SThierry Escande max(sizeof(*params), sizeof(*resp)), GFP_KERNEL); 2705e011558SThierry Escande if (msg == NULL) 2715e011558SThierry Escande return; 2725e011558SThierry Escande 2735e011558SThierry Escande msg->version = 2; 2745e011558SThierry Escande msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset; 2755e011558SThierry Escande msg->outsize = sizeof(*params); 2765e011558SThierry Escande msg->insize = sizeof(*resp); 2775e011558SThierry Escande 2785e011558SThierry Escande params = (struct ec_params_motion_sense *)msg->data; 2795e011558SThierry Escande params->cmd = MOTIONSENSE_CMD_DUMP; 2805e011558SThierry Escande 2815e011558SThierry Escande ret = cros_ec_cmd_xfer(ec->ec_dev, msg); 2825e011558SThierry Escande if (ret < 0 || msg->result != EC_RES_SUCCESS) { 2835e011558SThierry Escande dev_warn(ec->dev, "cannot get EC sensor information: %d/%d\n", 2845e011558SThierry Escande ret, msg->result); 2855e011558SThierry Escande goto error; 2865e011558SThierry Escande } 2875e011558SThierry Escande 2885e011558SThierry Escande resp = (struct ec_response_motion_sense *)msg->data; 2895e011558SThierry Escande sensor_num = resp->dump.sensor_count; 2901bb407f1SGwendal Grignou /* 2911bb407f1SGwendal Grignou * Allocate 2 extra sensors if lid angle sensor and/or FIFO are needed. 2921bb407f1SGwendal Grignou */ 2931bb407f1SGwendal Grignou sensor_cells = kcalloc(sensor_num + 2, sizeof(struct mfd_cell), 2945e011558SThierry Escande GFP_KERNEL); 2955e011558SThierry Escande if (sensor_cells == NULL) 2965e011558SThierry Escande goto error; 2975e011558SThierry Escande 2981bb407f1SGwendal Grignou sensor_platforms = kcalloc(sensor_num, 2996396bb22SKees Cook sizeof(struct cros_ec_sensor_platform), 3006396bb22SKees Cook GFP_KERNEL); 3015e011558SThierry Escande if (sensor_platforms == NULL) 3025e011558SThierry Escande goto error_platforms; 3035e011558SThierry Escande 3045e011558SThierry Escande memset(sensor_type, 0, sizeof(sensor_type)); 3055e011558SThierry Escande id = 0; 3065e011558SThierry Escande for (i = 0; i < sensor_num; i++) { 3075e011558SThierry Escande params->cmd = MOTIONSENSE_CMD_INFO; 3085e011558SThierry Escande params->info.sensor_num = i; 3095e011558SThierry Escande ret = cros_ec_cmd_xfer(ec->ec_dev, msg); 3105e011558SThierry Escande if (ret < 0 || msg->result != EC_RES_SUCCESS) { 3115e011558SThierry Escande dev_warn(ec->dev, "no info for EC sensor %d : %d/%d\n", 3125e011558SThierry Escande i, ret, msg->result); 3135e011558SThierry Escande continue; 3145e011558SThierry Escande } 3155e011558SThierry Escande switch (resp->info.type) { 3165e011558SThierry Escande case MOTIONSENSE_TYPE_ACCEL: 3175e011558SThierry Escande sensor_cells[id].name = "cros-ec-accel"; 3185e011558SThierry Escande break; 3195e011558SThierry Escande case MOTIONSENSE_TYPE_BARO: 3205e011558SThierry Escande sensor_cells[id].name = "cros-ec-baro"; 3215e011558SThierry Escande break; 3225e011558SThierry Escande case MOTIONSENSE_TYPE_GYRO: 3235e011558SThierry Escande sensor_cells[id].name = "cros-ec-gyro"; 3245e011558SThierry Escande break; 3255e011558SThierry Escande case MOTIONSENSE_TYPE_MAG: 3265e011558SThierry Escande sensor_cells[id].name = "cros-ec-mag"; 3275e011558SThierry Escande break; 3285e011558SThierry Escande case MOTIONSENSE_TYPE_PROX: 3295e011558SThierry Escande sensor_cells[id].name = "cros-ec-prox"; 3305e011558SThierry Escande break; 3315e011558SThierry Escande case MOTIONSENSE_TYPE_LIGHT: 3325e011558SThierry Escande sensor_cells[id].name = "cros-ec-light"; 3335e011558SThierry Escande break; 3345e011558SThierry Escande case MOTIONSENSE_TYPE_ACTIVITY: 3355e011558SThierry Escande sensor_cells[id].name = "cros-ec-activity"; 3365e011558SThierry Escande break; 3375e011558SThierry Escande default: 3385e011558SThierry Escande dev_warn(ec->dev, "unknown type %d\n", resp->info.type); 3395e011558SThierry Escande continue; 3405e011558SThierry Escande } 3415e011558SThierry Escande sensor_platforms[id].sensor_num = i; 3425e011558SThierry Escande sensor_cells[id].id = sensor_type[resp->info.type]; 3435e011558SThierry Escande sensor_cells[id].platform_data = &sensor_platforms[id]; 3445e011558SThierry Escande sensor_cells[id].pdata_size = 3455e011558SThierry Escande sizeof(struct cros_ec_sensor_platform); 3465e011558SThierry Escande 3475e011558SThierry Escande sensor_type[resp->info.type]++; 3485e011558SThierry Escande id++; 3495e011558SThierry Escande } 3505e011558SThierry Escande 351c1d1e91aSGwendal Grignou if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2) 352c1d1e91aSGwendal Grignou ec->has_kb_wake_angle = true; 353c1d1e91aSGwendal Grignou 3545e011558SThierry Escande if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) { 3555e011558SThierry Escande sensor_cells[id].name = "cros-ec-ring"; 3565e011558SThierry Escande id++; 3575e011558SThierry Escande } 3581bb407f1SGwendal Grignou if (cros_ec_check_features(ec, 3591bb407f1SGwendal Grignou EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS)) { 3601bb407f1SGwendal Grignou sensor_cells[id].name = "cros-ec-lid-angle"; 3611bb407f1SGwendal Grignou id++; 3621bb407f1SGwendal Grignou } 3635e011558SThierry Escande 3645e011558SThierry Escande ret = mfd_add_devices(ec->dev, 0, sensor_cells, id, 3655e011558SThierry Escande NULL, 0, NULL); 3665e011558SThierry Escande if (ret) 3675e011558SThierry Escande dev_err(ec->dev, "failed to add EC sensors\n"); 3685e011558SThierry Escande 3695e011558SThierry Escande kfree(sensor_platforms); 3705e011558SThierry Escande error_platforms: 3715e011558SThierry Escande kfree(sensor_cells); 3725e011558SThierry Escande error: 3735e011558SThierry Escande kfree(msg); 3745e011558SThierry Escande } 3755e011558SThierry Escande 376ecc2ca47SGwendal Grignou static struct cros_ec_sensor_platform sensor_platforms[] = { 377ecc2ca47SGwendal Grignou { .sensor_num = 0 }, 378ecc2ca47SGwendal Grignou { .sensor_num = 1 } 379ecc2ca47SGwendal Grignou }; 380ecc2ca47SGwendal Grignou 381ecc2ca47SGwendal Grignou static const struct mfd_cell cros_ec_accel_legacy_cells[] = { 382ecc2ca47SGwendal Grignou { 383ecc2ca47SGwendal Grignou .name = "cros-ec-accel-legacy", 384ecc2ca47SGwendal Grignou .platform_data = &sensor_platforms[0], 385ecc2ca47SGwendal Grignou .pdata_size = sizeof(struct cros_ec_sensor_platform), 386ecc2ca47SGwendal Grignou }, 387ecc2ca47SGwendal Grignou { 388ecc2ca47SGwendal Grignou .name = "cros-ec-accel-legacy", 389ecc2ca47SGwendal Grignou .platform_data = &sensor_platforms[1], 390ecc2ca47SGwendal Grignou .pdata_size = sizeof(struct cros_ec_sensor_platform), 391ecc2ca47SGwendal Grignou } 392ecc2ca47SGwendal Grignou }; 393ecc2ca47SGwendal Grignou 394ecc2ca47SGwendal Grignou static void cros_ec_accel_legacy_register(struct cros_ec_dev *ec) 395ecc2ca47SGwendal Grignou { 396ecc2ca47SGwendal Grignou struct cros_ec_device *ec_dev = ec->ec_dev; 397ecc2ca47SGwendal Grignou u8 status; 398ecc2ca47SGwendal Grignou int ret; 399ecc2ca47SGwendal Grignou 400ecc2ca47SGwendal Grignou /* 401ecc2ca47SGwendal Grignou * ECs that need legacy support are the main EC, directly connected to 402ecc2ca47SGwendal Grignou * the AP. 403ecc2ca47SGwendal Grignou */ 404ecc2ca47SGwendal Grignou if (ec->cmd_offset != 0) 405ecc2ca47SGwendal Grignou return; 406ecc2ca47SGwendal Grignou 407ecc2ca47SGwendal Grignou /* 408ecc2ca47SGwendal Grignou * Check if EC supports direct memory reads and if EC has 409ecc2ca47SGwendal Grignou * accelerometers. 410ecc2ca47SGwendal Grignou */ 411ecc2ca47SGwendal Grignou if (ec_dev->cmd_readmem) { 412ecc2ca47SGwendal Grignou ret = ec_dev->cmd_readmem(ec_dev, EC_MEMMAP_ACC_STATUS, 1, 413ecc2ca47SGwendal Grignou &status); 414ecc2ca47SGwendal Grignou if (ret < 0) { 415ecc2ca47SGwendal Grignou dev_warn(ec->dev, "EC direct read error.\n"); 416ecc2ca47SGwendal Grignou return; 417ecc2ca47SGwendal Grignou } 418ecc2ca47SGwendal Grignou 419ecc2ca47SGwendal Grignou /* Check if EC has accelerometers. */ 420ecc2ca47SGwendal Grignou if (!(status & EC_MEMMAP_ACC_STATUS_PRESENCE_BIT)) { 421ecc2ca47SGwendal Grignou dev_info(ec->dev, "EC does not have accelerometers.\n"); 422ecc2ca47SGwendal Grignou return; 423ecc2ca47SGwendal Grignou } 424ecc2ca47SGwendal Grignou } 425ecc2ca47SGwendal Grignou 426ecc2ca47SGwendal Grignou /* 427ecc2ca47SGwendal Grignou * The device may still support accelerometers: 428ecc2ca47SGwendal Grignou * it would be an older ARM based device that do not suppor the 429ecc2ca47SGwendal Grignou * EC_CMD_GET_FEATURES command. 430ecc2ca47SGwendal Grignou * 431ecc2ca47SGwendal Grignou * Register 2 accelerometers, we will fail in the IIO driver if there 432ecc2ca47SGwendal Grignou * are no sensors. 433ecc2ca47SGwendal Grignou */ 434ecc2ca47SGwendal Grignou ret = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO, 435ecc2ca47SGwendal Grignou cros_ec_accel_legacy_cells, 436ecc2ca47SGwendal Grignou ARRAY_SIZE(cros_ec_accel_legacy_cells), 437ecc2ca47SGwendal Grignou NULL, 0, NULL); 438ecc2ca47SGwendal Grignou if (ret) 439ecc2ca47SGwendal Grignou dev_err(ec_dev->dev, "failed to add EC sensors\n"); 440ecc2ca47SGwendal Grignou } 441ecc2ca47SGwendal Grignou 44203a5755cSNeil Armstrong static const struct mfd_cell cros_ec_cec_cells[] = { 44303a5755cSNeil Armstrong { .name = "cros-ec-cec" } 44403a5755cSNeil Armstrong }; 44503a5755cSNeil Armstrong 44695a4d07fSEnric Balletbo i Serra static const struct mfd_cell cros_ec_rtc_cells[] = { 44795a4d07fSEnric Balletbo i Serra { .name = "cros-ec-rtc" } 44895a4d07fSEnric Balletbo i Serra }; 44995a4d07fSEnric Balletbo i Serra 4503144dce7SEnric Balletbo i Serra static const struct mfd_cell cros_usbpd_charger_cells[] = { 45149a65e3cSEnric Balletbo i Serra { .name = "cros-usbpd-charger" }, 45249a65e3cSEnric Balletbo i Serra { .name = "cros-usbpd-logger" }, 4533144dce7SEnric Balletbo i Serra }; 4543144dce7SEnric Balletbo i Serra 455ecf8a6cdSEnric Balletbo i Serra static const struct mfd_cell cros_ec_platform_cells[] = { 4566fce0a2cSEnric Balletbo i Serra { .name = "cros-ec-debugfs" }, 457ecf8a6cdSEnric Balletbo i Serra { .name = "cros-ec-lightbar" }, 4586fd7f2bbSEnric Balletbo i Serra { .name = "cros-ec-sysfs" }, 4590545625bSEnric Balletbo i Serra }; 4600545625bSEnric Balletbo i Serra 4610545625bSEnric Balletbo i Serra static const struct mfd_cell cros_ec_vbc_cells[] = { 4620545625bSEnric Balletbo i Serra { .name = "cros-ec-vbc" } 463ecf8a6cdSEnric Balletbo i Serra }; 464ecf8a6cdSEnric Balletbo i Serra 4655e011558SThierry Escande static int ec_device_probe(struct platform_device *pdev) 4665e011558SThierry Escande { 4675e011558SThierry Escande int retval = -ENOMEM; 4680545625bSEnric Balletbo i Serra struct device_node *node; 4695e011558SThierry Escande struct device *dev = &pdev->dev; 4705e011558SThierry Escande struct cros_ec_platform *ec_platform = dev_get_platdata(dev); 47148a2ca0eSEnric Balletbo i Serra struct cros_ec_dev *ec = kzalloc(sizeof(*ec), GFP_KERNEL); 4725e011558SThierry Escande 4735e011558SThierry Escande if (!ec) 4745e011558SThierry Escande return retval; 4755e011558SThierry Escande 4765e011558SThierry Escande dev_set_drvdata(dev, ec); 4775e011558SThierry Escande ec->ec_dev = dev_get_drvdata(dev->parent); 4785e011558SThierry Escande ec->dev = dev; 4795e011558SThierry Escande ec->cmd_offset = ec_platform->cmd_offset; 4805e011558SThierry Escande ec->features[0] = -1U; /* Not cached yet */ 4815e011558SThierry Escande ec->features[1] = -1U; /* Not cached yet */ 4825e011558SThierry Escande device_initialize(&ec->class_dev); 4835e011558SThierry Escande cdev_init(&ec->cdev, &fops); 4845e011558SThierry Escande 48590486af5SEnric Balletbo i Serra /* Check whether this is actually a Fingerprint MCU rather than an EC */ 48690486af5SEnric Balletbo i Serra if (cros_ec_check_features(ec, EC_FEATURE_FINGERPRINT)) { 48790486af5SEnric Balletbo i Serra dev_info(dev, "CrOS Fingerprint MCU detected.\n"); 48890486af5SEnric Balletbo i Serra /* 48990486af5SEnric Balletbo i Serra * Help userspace differentiating ECs from FP MCU, 49090486af5SEnric Balletbo i Serra * regardless of the probing order. 49190486af5SEnric Balletbo i Serra */ 49290486af5SEnric Balletbo i Serra ec_platform->ec_name = CROS_EC_DEV_FP_NAME; 49390486af5SEnric Balletbo i Serra } 49490486af5SEnric Balletbo i Serra 4955e011558SThierry Escande /* 496d4cee950SRushikesh S Kadam * Check whether this is actually an Integrated Sensor Hub (ISH) 497d4cee950SRushikesh S Kadam * rather than an EC. 498d4cee950SRushikesh S Kadam */ 499d4cee950SRushikesh S Kadam if (cros_ec_check_features(ec, EC_FEATURE_ISH)) { 500d4cee950SRushikesh S Kadam dev_info(dev, "CrOS ISH MCU detected.\n"); 501d4cee950SRushikesh S Kadam /* 502d4cee950SRushikesh S Kadam * Help userspace differentiating ECs from ISH MCU, 503d4cee950SRushikesh S Kadam * regardless of the probing order. 504d4cee950SRushikesh S Kadam */ 505d4cee950SRushikesh S Kadam ec_platform->ec_name = CROS_EC_DEV_ISH_NAME; 506d4cee950SRushikesh S Kadam } 507d4cee950SRushikesh S Kadam 5084f8f2bb7SEnric Balletbo i Serra /* Check whether this is actually a Touchpad MCU rather than an EC */ 5094f8f2bb7SEnric Balletbo i Serra if (cros_ec_check_features(ec, EC_FEATURE_TOUCHPAD)) { 5104f8f2bb7SEnric Balletbo i Serra dev_info(dev, "CrOS Touchpad MCU detected.\n"); 5114f8f2bb7SEnric Balletbo i Serra /* 5124f8f2bb7SEnric Balletbo i Serra * Help userspace differentiating ECs from TP MCU, 5134f8f2bb7SEnric Balletbo i Serra * regardless of the probing order. 5144f8f2bb7SEnric Balletbo i Serra */ 5154f8f2bb7SEnric Balletbo i Serra ec_platform->ec_name = CROS_EC_DEV_TP_NAME; 5164f8f2bb7SEnric Balletbo i Serra } 5174f8f2bb7SEnric Balletbo i Serra 518554e937eSPi-Hsun Shih /* Check whether this is actually a SCP rather than an EC. */ 519554e937eSPi-Hsun Shih if (cros_ec_check_features(ec, EC_FEATURE_SCP)) { 520554e937eSPi-Hsun Shih dev_info(dev, "CrOS SCP MCU detected.\n"); 521554e937eSPi-Hsun Shih /* 522554e937eSPi-Hsun Shih * Help userspace differentiating ECs from SCP, 523554e937eSPi-Hsun Shih * regardless of the probing order. 524554e937eSPi-Hsun Shih */ 525554e937eSPi-Hsun Shih ec_platform->ec_name = CROS_EC_DEV_SCP_NAME; 526554e937eSPi-Hsun Shih } 527554e937eSPi-Hsun Shih 528d4cee950SRushikesh S Kadam /* 5295e011558SThierry Escande * Add the class device 5305e011558SThierry Escande * Link to the character device for creating the /dev entry 5315e011558SThierry Escande * in devtmpfs. 5325e011558SThierry Escande */ 5335e011558SThierry Escande ec->class_dev.devt = MKDEV(ec_major, pdev->id); 5345e011558SThierry Escande ec->class_dev.class = &cros_class; 5355e011558SThierry Escande ec->class_dev.parent = dev; 53648a2ca0eSEnric Balletbo i Serra ec->class_dev.release = cros_ec_class_release; 5375e011558SThierry Escande 5385e011558SThierry Escande retval = dev_set_name(&ec->class_dev, "%s", ec_platform->ec_name); 5395e011558SThierry Escande if (retval) { 5405e011558SThierry Escande dev_err(dev, "dev_set_name failed => %d\n", retval); 5415e011558SThierry Escande goto failed; 5425e011558SThierry Escande } 5435e011558SThierry Escande 544c1d1e91aSGwendal Grignou /* check whether this EC is a sensor hub. */ 545c1d1e91aSGwendal Grignou if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE)) 546c1d1e91aSGwendal Grignou cros_ec_sensors_register(ec); 547ecc2ca47SGwendal Grignou else 548ecc2ca47SGwendal Grignou /* Workaroud for older EC firmware */ 549ecc2ca47SGwendal Grignou cros_ec_accel_legacy_register(ec); 550c1d1e91aSGwendal Grignou 55103a5755cSNeil Armstrong /* Check whether this EC instance has CEC host command support */ 55203a5755cSNeil Armstrong if (cros_ec_check_features(ec, EC_FEATURE_CEC)) { 55303a5755cSNeil Armstrong retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO, 55403a5755cSNeil Armstrong cros_ec_cec_cells, 55503a5755cSNeil Armstrong ARRAY_SIZE(cros_ec_cec_cells), 55603a5755cSNeil Armstrong NULL, 0, NULL); 55703a5755cSNeil Armstrong if (retval) 55803a5755cSNeil Armstrong dev_err(ec->dev, 55903a5755cSNeil Armstrong "failed to add cros-ec-cec device: %d\n", 56003a5755cSNeil Armstrong retval); 56103a5755cSNeil Armstrong } 56203a5755cSNeil Armstrong 56395a4d07fSEnric Balletbo i Serra /* Check whether this EC instance has RTC host command support */ 56495a4d07fSEnric Balletbo i Serra if (cros_ec_check_features(ec, EC_FEATURE_RTC)) { 56595a4d07fSEnric Balletbo i Serra retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO, 56695a4d07fSEnric Balletbo i Serra cros_ec_rtc_cells, 56795a4d07fSEnric Balletbo i Serra ARRAY_SIZE(cros_ec_rtc_cells), 56895a4d07fSEnric Balletbo i Serra NULL, 0, NULL); 56995a4d07fSEnric Balletbo i Serra if (retval) 57095a4d07fSEnric Balletbo i Serra dev_err(ec->dev, 57195a4d07fSEnric Balletbo i Serra "failed to add cros-ec-rtc device: %d\n", 57295a4d07fSEnric Balletbo i Serra retval); 57395a4d07fSEnric Balletbo i Serra } 57495a4d07fSEnric Balletbo i Serra 5753144dce7SEnric Balletbo i Serra /* Check whether this EC instance has the PD charge manager */ 5763144dce7SEnric Balletbo i Serra if (cros_ec_check_features(ec, EC_FEATURE_USB_PD)) { 5773144dce7SEnric Balletbo i Serra retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO, 5783144dce7SEnric Balletbo i Serra cros_usbpd_charger_cells, 5793144dce7SEnric Balletbo i Serra ARRAY_SIZE(cros_usbpd_charger_cells), 5803144dce7SEnric Balletbo i Serra NULL, 0, NULL); 5813144dce7SEnric Balletbo i Serra if (retval) 5823144dce7SEnric Balletbo i Serra dev_err(ec->dev, 5833144dce7SEnric Balletbo i Serra "failed to add cros-usbpd-charger device: %d\n", 5843144dce7SEnric Balletbo i Serra retval); 5853144dce7SEnric Balletbo i Serra } 5863144dce7SEnric Balletbo i Serra 587c1d1e91aSGwendal Grignou /* We can now add the sysfs class, we know which parameter to show */ 5885e011558SThierry Escande retval = cdev_device_add(&ec->cdev, &ec->class_dev); 5895e011558SThierry Escande if (retval) { 5905e011558SThierry Escande dev_err(dev, "cdev_device_add failed => %d\n", retval); 5915e011558SThierry Escande goto failed; 5925e011558SThierry Escande } 5935e011558SThierry Escande 594ecf8a6cdSEnric Balletbo i Serra retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO, 595ecf8a6cdSEnric Balletbo i Serra cros_ec_platform_cells, 596ecf8a6cdSEnric Balletbo i Serra ARRAY_SIZE(cros_ec_platform_cells), 597ecf8a6cdSEnric Balletbo i Serra NULL, 0, NULL); 598ecf8a6cdSEnric Balletbo i Serra if (retval) 599ecf8a6cdSEnric Balletbo i Serra dev_warn(ec->dev, 600ecf8a6cdSEnric Balletbo i Serra "failed to add cros-ec platform devices: %d\n", 601ecf8a6cdSEnric Balletbo i Serra retval); 602ecf8a6cdSEnric Balletbo i Serra 6030545625bSEnric Balletbo i Serra /* Check whether this EC instance has a VBC NVRAM */ 6040545625bSEnric Balletbo i Serra node = ec->ec_dev->dev->of_node; 6050545625bSEnric Balletbo i Serra if (of_property_read_bool(node, "google,has-vbc-nvram")) { 6060545625bSEnric Balletbo i Serra retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO, 6070545625bSEnric Balletbo i Serra cros_ec_vbc_cells, 6080545625bSEnric Balletbo i Serra ARRAY_SIZE(cros_ec_vbc_cells), 6090545625bSEnric Balletbo i Serra NULL, 0, NULL); 6100545625bSEnric Balletbo i Serra if (retval) 6110545625bSEnric Balletbo i Serra dev_warn(ec->dev, "failed to add VBC devices: %d\n", 6120545625bSEnric Balletbo i Serra retval); 6130545625bSEnric Balletbo i Serra } 6140545625bSEnric Balletbo i Serra 6155e011558SThierry Escande return 0; 6165e011558SThierry Escande 6175e011558SThierry Escande failed: 6185e011558SThierry Escande put_device(&ec->class_dev); 6195e011558SThierry Escande return retval; 6205e011558SThierry Escande } 6215e011558SThierry Escande 6225e011558SThierry Escande static int ec_device_remove(struct platform_device *pdev) 6235e011558SThierry Escande { 6245e011558SThierry Escande struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev); 6255e011558SThierry Escande 62618e294ddSEnric Balletbo i Serra mfd_remove_devices(ec->dev); 6275e011558SThierry Escande cdev_del(&ec->cdev); 6285e011558SThierry Escande device_unregister(&ec->class_dev); 6295e011558SThierry Escande return 0; 6305e011558SThierry Escande } 6315e011558SThierry Escande 6325e011558SThierry Escande static const struct platform_device_id cros_ec_id[] = { 6335e011558SThierry Escande { DRV_NAME, 0 }, 634abeed71bSWei-Ning Huang { /* sentinel */ } 6355e011558SThierry Escande }; 6365e011558SThierry Escande MODULE_DEVICE_TABLE(platform, cros_ec_id); 6375e011558SThierry Escande 6385e011558SThierry Escande static struct platform_driver cros_ec_dev_driver = { 6395e011558SThierry Escande .driver = { 6405e011558SThierry Escande .name = DRV_NAME, 6415e011558SThierry Escande }, 6426eb35784SNathan Chancellor .id_table = cros_ec_id, 6435e011558SThierry Escande .probe = ec_device_probe, 6445e011558SThierry Escande .remove = ec_device_remove, 6455e011558SThierry Escande }; 6465e011558SThierry Escande 6475e011558SThierry Escande static int __init cros_ec_dev_init(void) 6485e011558SThierry Escande { 6495e011558SThierry Escande int ret; 6505e011558SThierry Escande dev_t dev = 0; 6515e011558SThierry Escande 6525e011558SThierry Escande ret = class_register(&cros_class); 6535e011558SThierry Escande if (ret) { 6545e011558SThierry Escande pr_err(CROS_EC_DEV_NAME ": failed to register device class\n"); 6555e011558SThierry Escande return ret; 6565e011558SThierry Escande } 6575e011558SThierry Escande 6585e011558SThierry Escande /* Get a range of minor numbers (starting with 0) to work with */ 6595e011558SThierry Escande ret = alloc_chrdev_region(&dev, 0, CROS_MAX_DEV, CROS_EC_DEV_NAME); 6605e011558SThierry Escande if (ret < 0) { 6615e011558SThierry Escande pr_err(CROS_EC_DEV_NAME ": alloc_chrdev_region() failed\n"); 6625e011558SThierry Escande goto failed_chrdevreg; 6635e011558SThierry Escande } 6645e011558SThierry Escande ec_major = MAJOR(dev); 6655e011558SThierry Escande 6665e011558SThierry Escande /* Register the driver */ 6675e011558SThierry Escande ret = platform_driver_register(&cros_ec_dev_driver); 6685e011558SThierry Escande if (ret < 0) { 6695e011558SThierry Escande pr_warn(CROS_EC_DEV_NAME ": can't register driver: %d\n", ret); 6705e011558SThierry Escande goto failed_devreg; 6715e011558SThierry Escande } 6725e011558SThierry Escande return 0; 6735e011558SThierry Escande 6745e011558SThierry Escande failed_devreg: 6755e011558SThierry Escande unregister_chrdev_region(MKDEV(ec_major, 0), CROS_MAX_DEV); 6765e011558SThierry Escande failed_chrdevreg: 6775e011558SThierry Escande class_unregister(&cros_class); 6785e011558SThierry Escande return ret; 6795e011558SThierry Escande } 6805e011558SThierry Escande 6815e011558SThierry Escande static void __exit cros_ec_dev_exit(void) 6825e011558SThierry Escande { 6835e011558SThierry Escande platform_driver_unregister(&cros_ec_dev_driver); 6845e011558SThierry Escande unregister_chrdev(ec_major, CROS_EC_DEV_NAME); 6855e011558SThierry Escande class_unregister(&cros_class); 6865e011558SThierry Escande } 6875e011558SThierry Escande 6885e011558SThierry Escande module_init(cros_ec_dev_init); 6895e011558SThierry Escande module_exit(cros_ec_dev_exit); 6905e011558SThierry Escande 6915e011558SThierry Escande MODULE_ALIAS("platform:" DRV_NAME); 6925e011558SThierry Escande MODULE_AUTHOR("Bill Richardson <wfrichar@chromium.org>"); 6935e011558SThierry Escande MODULE_DESCRIPTION("Userspace interface to the Chrome OS Embedded Controller"); 6945e011558SThierry Escande MODULE_VERSION("1.0"); 6955e011558SThierry Escande MODULE_LICENSE("GPL"); 696