15e011558SThierry Escande /* 25e011558SThierry Escande * cros_ec_dev - expose the Chrome OS Embedded Controller to user-space 35e011558SThierry Escande * 45e011558SThierry Escande * Copyright (C) 2014 Google, Inc. 55e011558SThierry Escande * 65e011558SThierry Escande * This program is free software; you can redistribute it and/or modify 75e011558SThierry Escande * it under the terms of the GNU General Public License as published by 85e011558SThierry Escande * the Free Software Foundation; either version 2 of the License, or 95e011558SThierry Escande * (at your option) any later version. 105e011558SThierry Escande * 115e011558SThierry Escande * This program is distributed in the hope that it will be useful, 125e011558SThierry Escande * but WITHOUT ANY WARRANTY; without even the implied warranty of 135e011558SThierry Escande * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 145e011558SThierry Escande * GNU General Public License for more details. 155e011558SThierry Escande * 165e011558SThierry Escande * You should have received a copy of the GNU General Public License 175e011558SThierry Escande * along with this program. If not, see <http://www.gnu.org/licenses/>. 185e011558SThierry Escande */ 195e011558SThierry Escande 205e011558SThierry Escande #include <linux/fs.h> 215e011558SThierry Escande #include <linux/mfd/core.h> 225e011558SThierry Escande #include <linux/module.h> 23ac316725SRandy Dunlap #include <linux/mod_devicetable.h> 240545625bSEnric Balletbo i Serra #include <linux/of_platform.h> 255e011558SThierry Escande #include <linux/platform_device.h> 265e011558SThierry Escande #include <linux/pm.h> 275e011558SThierry Escande #include <linux/slab.h> 285e011558SThierry Escande #include <linux/uaccess.h> 295e011558SThierry Escande 305e011558SThierry Escande #include "cros_ec_dev.h" 315e011558SThierry Escande 325e011558SThierry Escande #define DRV_NAME "cros-ec-dev" 335e011558SThierry Escande 345e011558SThierry Escande /* Device variables */ 355e011558SThierry Escande #define CROS_MAX_DEV 128 365e011558SThierry Escande static int ec_major; 375e011558SThierry Escande 385e011558SThierry Escande static struct class cros_class = { 395e011558SThierry Escande .owner = THIS_MODULE, 405e011558SThierry Escande .name = "chromeos", 415e011558SThierry Escande }; 425e011558SThierry Escande 435e011558SThierry Escande /* Basic communication */ 445e011558SThierry Escande static int ec_get_version(struct cros_ec_dev *ec, char *str, int maxlen) 455e011558SThierry Escande { 465e011558SThierry Escande struct ec_response_get_version *resp; 475e011558SThierry Escande static const char * const current_image_name[] = { 485e011558SThierry Escande "unknown", "read-only", "read-write", "invalid", 495e011558SThierry Escande }; 505e011558SThierry Escande struct cros_ec_command *msg; 515e011558SThierry Escande int ret; 525e011558SThierry Escande 535e011558SThierry Escande msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL); 545e011558SThierry Escande if (!msg) 555e011558SThierry Escande return -ENOMEM; 565e011558SThierry Escande 575e011558SThierry Escande msg->version = 0; 585e011558SThierry Escande msg->command = EC_CMD_GET_VERSION + ec->cmd_offset; 595e011558SThierry Escande msg->insize = sizeof(*resp); 605e011558SThierry Escande msg->outsize = 0; 615e011558SThierry Escande 625e011558SThierry Escande ret = cros_ec_cmd_xfer(ec->ec_dev, msg); 635e011558SThierry Escande if (ret < 0) 645e011558SThierry Escande goto exit; 655e011558SThierry Escande 665e011558SThierry Escande if (msg->result != EC_RES_SUCCESS) { 675e011558SThierry Escande snprintf(str, maxlen, 685e011558SThierry Escande "%s\nUnknown EC version: EC returned %d\n", 695e011558SThierry Escande CROS_EC_DEV_VERSION, msg->result); 705e011558SThierry Escande ret = -EINVAL; 715e011558SThierry Escande goto exit; 725e011558SThierry Escande } 735e011558SThierry Escande 745e011558SThierry Escande resp = (struct ec_response_get_version *)msg->data; 755e011558SThierry Escande if (resp->current_image >= ARRAY_SIZE(current_image_name)) 765e011558SThierry Escande resp->current_image = 3; /* invalid */ 775e011558SThierry Escande 785e011558SThierry Escande snprintf(str, maxlen, "%s\n%s\n%s\n%s\n", CROS_EC_DEV_VERSION, 795e011558SThierry Escande resp->version_string_ro, resp->version_string_rw, 805e011558SThierry Escande current_image_name[resp->current_image]); 815e011558SThierry Escande 825e011558SThierry Escande ret = 0; 835e011558SThierry Escande exit: 845e011558SThierry Escande kfree(msg); 855e011558SThierry Escande return ret; 865e011558SThierry Escande } 875e011558SThierry Escande 885e011558SThierry Escande static int cros_ec_check_features(struct cros_ec_dev *ec, int feature) 895e011558SThierry Escande { 905e011558SThierry Escande struct cros_ec_command *msg; 915e011558SThierry Escande int ret; 925e011558SThierry Escande 935e011558SThierry Escande if (ec->features[0] == -1U && ec->features[1] == -1U) { 945e011558SThierry Escande /* features bitmap not read yet */ 955e011558SThierry Escande 965e011558SThierry Escande msg = kmalloc(sizeof(*msg) + sizeof(ec->features), GFP_KERNEL); 975e011558SThierry Escande if (!msg) 985e011558SThierry Escande return -ENOMEM; 995e011558SThierry Escande 1005e011558SThierry Escande msg->version = 0; 1015e011558SThierry Escande msg->command = EC_CMD_GET_FEATURES + ec->cmd_offset; 1025e011558SThierry Escande msg->insize = sizeof(ec->features); 1035e011558SThierry Escande msg->outsize = 0; 1045e011558SThierry Escande 1055e011558SThierry Escande ret = cros_ec_cmd_xfer(ec->ec_dev, msg); 1065e011558SThierry Escande if (ret < 0 || msg->result != EC_RES_SUCCESS) { 1075e011558SThierry Escande dev_warn(ec->dev, "cannot get EC features: %d/%d\n", 1085e011558SThierry Escande ret, msg->result); 1095e011558SThierry Escande memset(ec->features, 0, sizeof(ec->features)); 110df7c3bf2SStephen Boyd } else { 1115e011558SThierry Escande memcpy(ec->features, msg->data, sizeof(ec->features)); 112df7c3bf2SStephen Boyd } 1135e011558SThierry Escande 1145e011558SThierry Escande dev_dbg(ec->dev, "EC features %08x %08x\n", 1155e011558SThierry Escande ec->features[0], ec->features[1]); 1165e011558SThierry Escande 1175e011558SThierry Escande kfree(msg); 1185e011558SThierry Escande } 1195e011558SThierry Escande 1205e011558SThierry Escande return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature); 1215e011558SThierry Escande } 1225e011558SThierry Escande 1235e011558SThierry Escande /* Device file ops */ 1245e011558SThierry Escande static int ec_device_open(struct inode *inode, struct file *filp) 1255e011558SThierry Escande { 1265e011558SThierry Escande struct cros_ec_dev *ec = container_of(inode->i_cdev, 1275e011558SThierry Escande struct cros_ec_dev, cdev); 1285e011558SThierry Escande filp->private_data = ec; 1295e011558SThierry Escande nonseekable_open(inode, filp); 1305e011558SThierry Escande return 0; 1315e011558SThierry Escande } 1325e011558SThierry Escande 1335e011558SThierry Escande static int ec_device_release(struct inode *inode, struct file *filp) 1345e011558SThierry Escande { 1355e011558SThierry Escande return 0; 1365e011558SThierry Escande } 1375e011558SThierry Escande 1385e011558SThierry Escande static ssize_t ec_device_read(struct file *filp, char __user *buffer, 1395e011558SThierry Escande size_t length, loff_t *offset) 1405e011558SThierry Escande { 1415e011558SThierry Escande struct cros_ec_dev *ec = filp->private_data; 1425e011558SThierry Escande char msg[sizeof(struct ec_response_get_version) + 1435e011558SThierry Escande sizeof(CROS_EC_DEV_VERSION)]; 1445e011558SThierry Escande size_t count; 1455e011558SThierry Escande int ret; 1465e011558SThierry Escande 1475e011558SThierry Escande if (*offset != 0) 1485e011558SThierry Escande return 0; 1495e011558SThierry Escande 1505e011558SThierry Escande ret = ec_get_version(ec, msg, sizeof(msg)); 1515e011558SThierry Escande if (ret) 1525e011558SThierry Escande return ret; 1535e011558SThierry Escande 1545e011558SThierry Escande count = min(length, strlen(msg)); 1555e011558SThierry Escande 1565e011558SThierry Escande if (copy_to_user(buffer, msg, count)) 1575e011558SThierry Escande return -EFAULT; 1585e011558SThierry Escande 1595e011558SThierry Escande *offset = count; 1605e011558SThierry Escande return count; 1615e011558SThierry Escande } 1625e011558SThierry Escande 1635e011558SThierry Escande /* Ioctls */ 1645e011558SThierry Escande static long ec_device_ioctl_xcmd(struct cros_ec_dev *ec, void __user *arg) 1655e011558SThierry Escande { 1665e011558SThierry Escande long ret; 1675e011558SThierry Escande struct cros_ec_command u_cmd; 1685e011558SThierry Escande struct cros_ec_command *s_cmd; 1695e011558SThierry Escande 1705e011558SThierry Escande if (copy_from_user(&u_cmd, arg, sizeof(u_cmd))) 1715e011558SThierry Escande return -EFAULT; 1725e011558SThierry Escande 1735e011558SThierry Escande if ((u_cmd.outsize > EC_MAX_MSG_BYTES) || 1745e011558SThierry Escande (u_cmd.insize > EC_MAX_MSG_BYTES)) 1755e011558SThierry Escande return -EINVAL; 1765e011558SThierry Escande 1775e011558SThierry Escande s_cmd = kmalloc(sizeof(*s_cmd) + max(u_cmd.outsize, u_cmd.insize), 1785e011558SThierry Escande GFP_KERNEL); 1795e011558SThierry Escande if (!s_cmd) 1805e011558SThierry Escande return -ENOMEM; 1815e011558SThierry Escande 1825e011558SThierry Escande if (copy_from_user(s_cmd, arg, sizeof(*s_cmd) + u_cmd.outsize)) { 1835e011558SThierry Escande ret = -EFAULT; 1845e011558SThierry Escande goto exit; 1855e011558SThierry Escande } 1865e011558SThierry Escande 1875e011558SThierry Escande if (u_cmd.outsize != s_cmd->outsize || 1885e011558SThierry Escande u_cmd.insize != s_cmd->insize) { 1895e011558SThierry Escande ret = -EINVAL; 1905e011558SThierry Escande goto exit; 1915e011558SThierry Escande } 1925e011558SThierry Escande 1935e011558SThierry Escande s_cmd->command += ec->cmd_offset; 1945e011558SThierry Escande ret = cros_ec_cmd_xfer(ec->ec_dev, s_cmd); 1955e011558SThierry Escande /* Only copy data to userland if data was received. */ 1965e011558SThierry Escande if (ret < 0) 1975e011558SThierry Escande goto exit; 1985e011558SThierry Escande 1995e011558SThierry Escande if (copy_to_user(arg, s_cmd, sizeof(*s_cmd) + s_cmd->insize)) 2005e011558SThierry Escande ret = -EFAULT; 2015e011558SThierry Escande exit: 2025e011558SThierry Escande kfree(s_cmd); 2035e011558SThierry Escande return ret; 2045e011558SThierry Escande } 2055e011558SThierry Escande 2065e011558SThierry Escande static long ec_device_ioctl_readmem(struct cros_ec_dev *ec, void __user *arg) 2075e011558SThierry Escande { 2085e011558SThierry Escande struct cros_ec_device *ec_dev = ec->ec_dev; 2095e011558SThierry Escande struct cros_ec_readmem s_mem = { }; 2105e011558SThierry Escande long num; 2115e011558SThierry Escande 2125e011558SThierry Escande /* Not every platform supports direct reads */ 2135e011558SThierry Escande if (!ec_dev->cmd_readmem) 2145e011558SThierry Escande return -ENOTTY; 2155e011558SThierry Escande 2165e011558SThierry Escande if (copy_from_user(&s_mem, arg, sizeof(s_mem))) 2175e011558SThierry Escande return -EFAULT; 2185e011558SThierry Escande 2195e011558SThierry Escande num = ec_dev->cmd_readmem(ec_dev, s_mem.offset, s_mem.bytes, 2205e011558SThierry Escande s_mem.buffer); 2215e011558SThierry Escande if (num <= 0) 2225e011558SThierry Escande return num; 2235e011558SThierry Escande 2245e011558SThierry Escande if (copy_to_user((void __user *)arg, &s_mem, sizeof(s_mem))) 2255e011558SThierry Escande return -EFAULT; 2265e011558SThierry Escande 2275e011558SThierry Escande return 0; 2285e011558SThierry Escande } 2295e011558SThierry Escande 2305e011558SThierry Escande static long ec_device_ioctl(struct file *filp, unsigned int cmd, 2315e011558SThierry Escande unsigned long arg) 2325e011558SThierry Escande { 2335e011558SThierry Escande struct cros_ec_dev *ec = filp->private_data; 2345e011558SThierry Escande 2355e011558SThierry Escande if (_IOC_TYPE(cmd) != CROS_EC_DEV_IOC) 2365e011558SThierry Escande return -ENOTTY; 2375e011558SThierry Escande 2385e011558SThierry Escande switch (cmd) { 2395e011558SThierry Escande case CROS_EC_DEV_IOCXCMD: 2405e011558SThierry Escande return ec_device_ioctl_xcmd(ec, (void __user *)arg); 2415e011558SThierry Escande case CROS_EC_DEV_IOCRDMEM: 2425e011558SThierry Escande return ec_device_ioctl_readmem(ec, (void __user *)arg); 2435e011558SThierry Escande } 2445e011558SThierry Escande 2455e011558SThierry Escande return -ENOTTY; 2465e011558SThierry Escande } 2475e011558SThierry Escande 2485e011558SThierry Escande /* Module initialization */ 2495e011558SThierry Escande static const struct file_operations fops = { 2505e011558SThierry Escande .open = ec_device_open, 2515e011558SThierry Escande .release = ec_device_release, 2525e011558SThierry Escande .read = ec_device_read, 2535e011558SThierry Escande .unlocked_ioctl = ec_device_ioctl, 2545e011558SThierry Escande #ifdef CONFIG_COMPAT 2555e011558SThierry Escande .compat_ioctl = ec_device_ioctl, 2565e011558SThierry Escande #endif 2575e011558SThierry Escande }; 2585e011558SThierry Escande 25948a2ca0eSEnric Balletbo i Serra static void cros_ec_class_release(struct device *dev) 26048a2ca0eSEnric Balletbo i Serra { 26148a2ca0eSEnric Balletbo i Serra kfree(to_cros_ec_dev(dev)); 26248a2ca0eSEnric Balletbo i Serra } 26348a2ca0eSEnric Balletbo i Serra 2645e011558SThierry Escande static void cros_ec_sensors_register(struct cros_ec_dev *ec) 2655e011558SThierry Escande { 2665e011558SThierry Escande /* 2675e011558SThierry Escande * Issue a command to get the number of sensor reported. 2685e011558SThierry Escande * Build an array of sensors driver and register them all. 2695e011558SThierry Escande */ 2705e011558SThierry Escande int ret, i, id, sensor_num; 2715e011558SThierry Escande struct mfd_cell *sensor_cells; 2725e011558SThierry Escande struct cros_ec_sensor_platform *sensor_platforms; 2735e011558SThierry Escande int sensor_type[MOTIONSENSE_TYPE_MAX]; 2745e011558SThierry Escande struct ec_params_motion_sense *params; 2755e011558SThierry Escande struct ec_response_motion_sense *resp; 2765e011558SThierry Escande struct cros_ec_command *msg; 2775e011558SThierry Escande 2785e011558SThierry Escande msg = kzalloc(sizeof(struct cros_ec_command) + 2795e011558SThierry Escande max(sizeof(*params), sizeof(*resp)), GFP_KERNEL); 2805e011558SThierry Escande if (msg == NULL) 2815e011558SThierry Escande return; 2825e011558SThierry Escande 2835e011558SThierry Escande msg->version = 2; 2845e011558SThierry Escande msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset; 2855e011558SThierry Escande msg->outsize = sizeof(*params); 2865e011558SThierry Escande msg->insize = sizeof(*resp); 2875e011558SThierry Escande 2885e011558SThierry Escande params = (struct ec_params_motion_sense *)msg->data; 2895e011558SThierry Escande params->cmd = MOTIONSENSE_CMD_DUMP; 2905e011558SThierry Escande 2915e011558SThierry Escande ret = cros_ec_cmd_xfer(ec->ec_dev, msg); 2925e011558SThierry Escande if (ret < 0 || msg->result != EC_RES_SUCCESS) { 2935e011558SThierry Escande dev_warn(ec->dev, "cannot get EC sensor information: %d/%d\n", 2945e011558SThierry Escande ret, msg->result); 2955e011558SThierry Escande goto error; 2965e011558SThierry Escande } 2975e011558SThierry Escande 2985e011558SThierry Escande resp = (struct ec_response_motion_sense *)msg->data; 2995e011558SThierry Escande sensor_num = resp->dump.sensor_count; 300c1d1e91aSGwendal Grignou /* Allocate 1 extra sensors in FIFO are needed */ 3016396bb22SKees Cook sensor_cells = kcalloc(sensor_num + 1, sizeof(struct mfd_cell), 3025e011558SThierry Escande GFP_KERNEL); 3035e011558SThierry Escande if (sensor_cells == NULL) 3045e011558SThierry Escande goto error; 3055e011558SThierry Escande 3066396bb22SKees Cook sensor_platforms = kcalloc(sensor_num + 1, 3076396bb22SKees Cook sizeof(struct cros_ec_sensor_platform), 3086396bb22SKees Cook GFP_KERNEL); 3095e011558SThierry Escande if (sensor_platforms == NULL) 3105e011558SThierry Escande goto error_platforms; 3115e011558SThierry Escande 3125e011558SThierry Escande memset(sensor_type, 0, sizeof(sensor_type)); 3135e011558SThierry Escande id = 0; 3145e011558SThierry Escande for (i = 0; i < sensor_num; i++) { 3155e011558SThierry Escande params->cmd = MOTIONSENSE_CMD_INFO; 3165e011558SThierry Escande params->info.sensor_num = i; 3175e011558SThierry Escande ret = cros_ec_cmd_xfer(ec->ec_dev, msg); 3185e011558SThierry Escande if (ret < 0 || msg->result != EC_RES_SUCCESS) { 3195e011558SThierry Escande dev_warn(ec->dev, "no info for EC sensor %d : %d/%d\n", 3205e011558SThierry Escande i, ret, msg->result); 3215e011558SThierry Escande continue; 3225e011558SThierry Escande } 3235e011558SThierry Escande switch (resp->info.type) { 3245e011558SThierry Escande case MOTIONSENSE_TYPE_ACCEL: 3255e011558SThierry Escande sensor_cells[id].name = "cros-ec-accel"; 3265e011558SThierry Escande break; 3275e011558SThierry Escande case MOTIONSENSE_TYPE_BARO: 3285e011558SThierry Escande sensor_cells[id].name = "cros-ec-baro"; 3295e011558SThierry Escande break; 3305e011558SThierry Escande case MOTIONSENSE_TYPE_GYRO: 3315e011558SThierry Escande sensor_cells[id].name = "cros-ec-gyro"; 3325e011558SThierry Escande break; 3335e011558SThierry Escande case MOTIONSENSE_TYPE_MAG: 3345e011558SThierry Escande sensor_cells[id].name = "cros-ec-mag"; 3355e011558SThierry Escande break; 3365e011558SThierry Escande case MOTIONSENSE_TYPE_PROX: 3375e011558SThierry Escande sensor_cells[id].name = "cros-ec-prox"; 3385e011558SThierry Escande break; 3395e011558SThierry Escande case MOTIONSENSE_TYPE_LIGHT: 3405e011558SThierry Escande sensor_cells[id].name = "cros-ec-light"; 3415e011558SThierry Escande break; 3425e011558SThierry Escande case MOTIONSENSE_TYPE_ACTIVITY: 3435e011558SThierry Escande sensor_cells[id].name = "cros-ec-activity"; 3445e011558SThierry Escande break; 3455e011558SThierry Escande default: 3465e011558SThierry Escande dev_warn(ec->dev, "unknown type %d\n", resp->info.type); 3475e011558SThierry Escande continue; 3485e011558SThierry Escande } 3495e011558SThierry Escande sensor_platforms[id].sensor_num = i; 3505e011558SThierry Escande sensor_cells[id].id = sensor_type[resp->info.type]; 3515e011558SThierry Escande sensor_cells[id].platform_data = &sensor_platforms[id]; 3525e011558SThierry Escande sensor_cells[id].pdata_size = 3535e011558SThierry Escande sizeof(struct cros_ec_sensor_platform); 3545e011558SThierry Escande 3555e011558SThierry Escande sensor_type[resp->info.type]++; 3565e011558SThierry Escande id++; 3575e011558SThierry Escande } 3585e011558SThierry Escande 359c1d1e91aSGwendal Grignou if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2) 360c1d1e91aSGwendal Grignou ec->has_kb_wake_angle = true; 361c1d1e91aSGwendal Grignou 3625e011558SThierry Escande if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) { 3635e011558SThierry Escande sensor_cells[id].name = "cros-ec-ring"; 3645e011558SThierry Escande id++; 3655e011558SThierry Escande } 3665e011558SThierry Escande 3675e011558SThierry Escande ret = mfd_add_devices(ec->dev, 0, sensor_cells, id, 3685e011558SThierry Escande NULL, 0, NULL); 3695e011558SThierry Escande if (ret) 3705e011558SThierry Escande dev_err(ec->dev, "failed to add EC sensors\n"); 3715e011558SThierry Escande 3725e011558SThierry Escande kfree(sensor_platforms); 3735e011558SThierry Escande error_platforms: 3745e011558SThierry Escande kfree(sensor_cells); 3755e011558SThierry Escande error: 3765e011558SThierry Escande kfree(msg); 3775e011558SThierry Escande } 3785e011558SThierry Escande 37903a5755cSNeil Armstrong static const struct mfd_cell cros_ec_cec_cells[] = { 38003a5755cSNeil Armstrong { .name = "cros-ec-cec" } 38103a5755cSNeil Armstrong }; 38203a5755cSNeil Armstrong 38395a4d07fSEnric Balletbo i Serra static const struct mfd_cell cros_ec_rtc_cells[] = { 38495a4d07fSEnric Balletbo i Serra { .name = "cros-ec-rtc" } 38595a4d07fSEnric Balletbo i Serra }; 38695a4d07fSEnric Balletbo i Serra 3873144dce7SEnric Balletbo i Serra static const struct mfd_cell cros_usbpd_charger_cells[] = { 3883144dce7SEnric Balletbo i Serra { .name = "cros-usbpd-charger" } 3893144dce7SEnric Balletbo i Serra }; 3903144dce7SEnric Balletbo i Serra 391ecf8a6cdSEnric Balletbo i Serra static const struct mfd_cell cros_ec_platform_cells[] = { 3926fce0a2cSEnric Balletbo i Serra { .name = "cros-ec-debugfs" }, 393ecf8a6cdSEnric Balletbo i Serra { .name = "cros-ec-lightbar" }, 3946fd7f2bbSEnric Balletbo i Serra { .name = "cros-ec-sysfs" }, 3950545625bSEnric Balletbo i Serra }; 3960545625bSEnric Balletbo i Serra 3970545625bSEnric Balletbo i Serra static const struct mfd_cell cros_ec_vbc_cells[] = { 3980545625bSEnric Balletbo i Serra { .name = "cros-ec-vbc" } 399ecf8a6cdSEnric Balletbo i Serra }; 400ecf8a6cdSEnric Balletbo i Serra 4015e011558SThierry Escande static int ec_device_probe(struct platform_device *pdev) 4025e011558SThierry Escande { 4035e011558SThierry Escande int retval = -ENOMEM; 4040545625bSEnric Balletbo i Serra struct device_node *node; 4055e011558SThierry Escande struct device *dev = &pdev->dev; 4065e011558SThierry Escande struct cros_ec_platform *ec_platform = dev_get_platdata(dev); 40748a2ca0eSEnric Balletbo i Serra struct cros_ec_dev *ec = kzalloc(sizeof(*ec), GFP_KERNEL); 4085e011558SThierry Escande 4095e011558SThierry Escande if (!ec) 4105e011558SThierry Escande return retval; 4115e011558SThierry Escande 4125e011558SThierry Escande dev_set_drvdata(dev, ec); 4135e011558SThierry Escande ec->ec_dev = dev_get_drvdata(dev->parent); 4145e011558SThierry Escande ec->dev = dev; 4155e011558SThierry Escande ec->cmd_offset = ec_platform->cmd_offset; 4165e011558SThierry Escande ec->features[0] = -1U; /* Not cached yet */ 4175e011558SThierry Escande ec->features[1] = -1U; /* Not cached yet */ 4185e011558SThierry Escande device_initialize(&ec->class_dev); 4195e011558SThierry Escande cdev_init(&ec->cdev, &fops); 4205e011558SThierry Escande 4215e011558SThierry Escande /* 4225e011558SThierry Escande * Add the class device 4235e011558SThierry Escande * Link to the character device for creating the /dev entry 4245e011558SThierry Escande * in devtmpfs. 4255e011558SThierry Escande */ 4265e011558SThierry Escande ec->class_dev.devt = MKDEV(ec_major, pdev->id); 4275e011558SThierry Escande ec->class_dev.class = &cros_class; 4285e011558SThierry Escande ec->class_dev.parent = dev; 42948a2ca0eSEnric Balletbo i Serra ec->class_dev.release = cros_ec_class_release; 4305e011558SThierry Escande 4315e011558SThierry Escande retval = dev_set_name(&ec->class_dev, "%s", ec_platform->ec_name); 4325e011558SThierry Escande if (retval) { 4335e011558SThierry Escande dev_err(dev, "dev_set_name failed => %d\n", retval); 4345e011558SThierry Escande goto failed; 4355e011558SThierry Escande } 4365e011558SThierry Escande 437c1d1e91aSGwendal Grignou /* check whether this EC is a sensor hub. */ 438c1d1e91aSGwendal Grignou if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE)) 439c1d1e91aSGwendal Grignou cros_ec_sensors_register(ec); 440c1d1e91aSGwendal Grignou 44103a5755cSNeil Armstrong /* Check whether this EC instance has CEC host command support */ 44203a5755cSNeil Armstrong if (cros_ec_check_features(ec, EC_FEATURE_CEC)) { 44303a5755cSNeil Armstrong retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO, 44403a5755cSNeil Armstrong cros_ec_cec_cells, 44503a5755cSNeil Armstrong ARRAY_SIZE(cros_ec_cec_cells), 44603a5755cSNeil Armstrong NULL, 0, NULL); 44703a5755cSNeil Armstrong if (retval) 44803a5755cSNeil Armstrong dev_err(ec->dev, 44903a5755cSNeil Armstrong "failed to add cros-ec-cec device: %d\n", 45003a5755cSNeil Armstrong retval); 45103a5755cSNeil Armstrong } 45203a5755cSNeil Armstrong 45395a4d07fSEnric Balletbo i Serra /* Check whether this EC instance has RTC host command support */ 45495a4d07fSEnric Balletbo i Serra if (cros_ec_check_features(ec, EC_FEATURE_RTC)) { 45595a4d07fSEnric Balletbo i Serra retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO, 45695a4d07fSEnric Balletbo i Serra cros_ec_rtc_cells, 45795a4d07fSEnric Balletbo i Serra ARRAY_SIZE(cros_ec_rtc_cells), 45895a4d07fSEnric Balletbo i Serra NULL, 0, NULL); 45995a4d07fSEnric Balletbo i Serra if (retval) 46095a4d07fSEnric Balletbo i Serra dev_err(ec->dev, 46195a4d07fSEnric Balletbo i Serra "failed to add cros-ec-rtc device: %d\n", 46295a4d07fSEnric Balletbo i Serra retval); 46395a4d07fSEnric Balletbo i Serra } 46495a4d07fSEnric Balletbo i Serra 4653144dce7SEnric Balletbo i Serra /* Check whether this EC instance has the PD charge manager */ 4663144dce7SEnric Balletbo i Serra if (cros_ec_check_features(ec, EC_FEATURE_USB_PD)) { 4673144dce7SEnric Balletbo i Serra retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO, 4683144dce7SEnric Balletbo i Serra cros_usbpd_charger_cells, 4693144dce7SEnric Balletbo i Serra ARRAY_SIZE(cros_usbpd_charger_cells), 4703144dce7SEnric Balletbo i Serra NULL, 0, NULL); 4713144dce7SEnric Balletbo i Serra if (retval) 4723144dce7SEnric Balletbo i Serra dev_err(ec->dev, 4733144dce7SEnric Balletbo i Serra "failed to add cros-usbpd-charger device: %d\n", 4743144dce7SEnric Balletbo i Serra retval); 4753144dce7SEnric Balletbo i Serra } 4763144dce7SEnric Balletbo i Serra 477c1d1e91aSGwendal Grignou /* We can now add the sysfs class, we know which parameter to show */ 4785e011558SThierry Escande retval = cdev_device_add(&ec->cdev, &ec->class_dev); 4795e011558SThierry Escande if (retval) { 4805e011558SThierry Escande dev_err(dev, "cdev_device_add failed => %d\n", retval); 4815e011558SThierry Escande goto failed; 4825e011558SThierry Escande } 4835e011558SThierry Escande 484ecf8a6cdSEnric Balletbo i Serra retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO, 485ecf8a6cdSEnric Balletbo i Serra cros_ec_platform_cells, 486ecf8a6cdSEnric Balletbo i Serra ARRAY_SIZE(cros_ec_platform_cells), 487ecf8a6cdSEnric Balletbo i Serra NULL, 0, NULL); 488ecf8a6cdSEnric Balletbo i Serra if (retval) 489ecf8a6cdSEnric Balletbo i Serra dev_warn(ec->dev, 490ecf8a6cdSEnric Balletbo i Serra "failed to add cros-ec platform devices: %d\n", 491ecf8a6cdSEnric Balletbo i Serra retval); 492ecf8a6cdSEnric Balletbo i Serra 4930545625bSEnric Balletbo i Serra /* Check whether this EC instance has a VBC NVRAM */ 4940545625bSEnric Balletbo i Serra node = ec->ec_dev->dev->of_node; 4950545625bSEnric Balletbo i Serra if (of_property_read_bool(node, "google,has-vbc-nvram")) { 4960545625bSEnric Balletbo i Serra retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO, 4970545625bSEnric Balletbo i Serra cros_ec_vbc_cells, 4980545625bSEnric Balletbo i Serra ARRAY_SIZE(cros_ec_vbc_cells), 4990545625bSEnric Balletbo i Serra NULL, 0, NULL); 5000545625bSEnric Balletbo i Serra if (retval) 5010545625bSEnric Balletbo i Serra dev_warn(ec->dev, "failed to add VBC devices: %d\n", 5020545625bSEnric Balletbo i Serra retval); 5030545625bSEnric Balletbo i Serra } 5040545625bSEnric Balletbo i Serra 5055e011558SThierry Escande return 0; 5065e011558SThierry Escande 5075e011558SThierry Escande failed: 5085e011558SThierry Escande put_device(&ec->class_dev); 5095e011558SThierry Escande return retval; 5105e011558SThierry Escande } 5115e011558SThierry Escande 5125e011558SThierry Escande static int ec_device_remove(struct platform_device *pdev) 5135e011558SThierry Escande { 5145e011558SThierry Escande struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev); 5155e011558SThierry Escande 51618e294ddSEnric Balletbo i Serra mfd_remove_devices(ec->dev); 5175e011558SThierry Escande cdev_del(&ec->cdev); 5185e011558SThierry Escande device_unregister(&ec->class_dev); 5195e011558SThierry Escande return 0; 5205e011558SThierry Escande } 5215e011558SThierry Escande 5225e011558SThierry Escande static const struct platform_device_id cros_ec_id[] = { 5235e011558SThierry Escande { DRV_NAME, 0 }, 524abeed71bSWei-Ning Huang { /* sentinel */ } 5255e011558SThierry Escande }; 5265e011558SThierry Escande MODULE_DEVICE_TABLE(platform, cros_ec_id); 5275e011558SThierry Escande 5285e011558SThierry Escande static struct platform_driver cros_ec_dev_driver = { 5295e011558SThierry Escande .driver = { 5305e011558SThierry Escande .name = DRV_NAME, 5315e011558SThierry Escande }, 5326eb35784SNathan Chancellor .id_table = cros_ec_id, 5335e011558SThierry Escande .probe = ec_device_probe, 5345e011558SThierry Escande .remove = ec_device_remove, 5355e011558SThierry Escande }; 5365e011558SThierry Escande 5375e011558SThierry Escande static int __init cros_ec_dev_init(void) 5385e011558SThierry Escande { 5395e011558SThierry Escande int ret; 5405e011558SThierry Escande dev_t dev = 0; 5415e011558SThierry Escande 5425e011558SThierry Escande ret = class_register(&cros_class); 5435e011558SThierry Escande if (ret) { 5445e011558SThierry Escande pr_err(CROS_EC_DEV_NAME ": failed to register device class\n"); 5455e011558SThierry Escande return ret; 5465e011558SThierry Escande } 5475e011558SThierry Escande 5485e011558SThierry Escande /* Get a range of minor numbers (starting with 0) to work with */ 5495e011558SThierry Escande ret = alloc_chrdev_region(&dev, 0, CROS_MAX_DEV, CROS_EC_DEV_NAME); 5505e011558SThierry Escande if (ret < 0) { 5515e011558SThierry Escande pr_err(CROS_EC_DEV_NAME ": alloc_chrdev_region() failed\n"); 5525e011558SThierry Escande goto failed_chrdevreg; 5535e011558SThierry Escande } 5545e011558SThierry Escande ec_major = MAJOR(dev); 5555e011558SThierry Escande 5565e011558SThierry Escande /* Register the driver */ 5575e011558SThierry Escande ret = platform_driver_register(&cros_ec_dev_driver); 5585e011558SThierry Escande if (ret < 0) { 5595e011558SThierry Escande pr_warn(CROS_EC_DEV_NAME ": can't register driver: %d\n", ret); 5605e011558SThierry Escande goto failed_devreg; 5615e011558SThierry Escande } 5625e011558SThierry Escande return 0; 5635e011558SThierry Escande 5645e011558SThierry Escande failed_devreg: 5655e011558SThierry Escande unregister_chrdev_region(MKDEV(ec_major, 0), CROS_MAX_DEV); 5665e011558SThierry Escande failed_chrdevreg: 5675e011558SThierry Escande class_unregister(&cros_class); 5685e011558SThierry Escande return ret; 5695e011558SThierry Escande } 5705e011558SThierry Escande 5715e011558SThierry Escande static void __exit cros_ec_dev_exit(void) 5725e011558SThierry Escande { 5735e011558SThierry Escande platform_driver_unregister(&cros_ec_dev_driver); 5745e011558SThierry Escande unregister_chrdev(ec_major, CROS_EC_DEV_NAME); 5755e011558SThierry Escande class_unregister(&cros_class); 5765e011558SThierry Escande } 5775e011558SThierry Escande 5785e011558SThierry Escande module_init(cros_ec_dev_init); 5795e011558SThierry Escande module_exit(cros_ec_dev_exit); 5805e011558SThierry Escande 5815e011558SThierry Escande MODULE_ALIAS("platform:" DRV_NAME); 5825e011558SThierry Escande MODULE_AUTHOR("Bill Richardson <wfrichar@chromium.org>"); 5835e011558SThierry Escande MODULE_DESCRIPTION("Userspace interface to the Chrome OS Embedded Controller"); 5845e011558SThierry Escande MODULE_VERSION("1.0"); 5855e011558SThierry Escande MODULE_LICENSE("GPL"); 586