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> 245e011558SThierry Escande #include <linux/platform_device.h> 255e011558SThierry Escande #include <linux/pm.h> 265e011558SThierry Escande #include <linux/slab.h> 275e011558SThierry Escande #include <linux/uaccess.h> 285e011558SThierry Escande 295e011558SThierry Escande #include "cros_ec_dev.h" 305e011558SThierry Escande 315e011558SThierry Escande #define DRV_NAME "cros-ec-dev" 325e011558SThierry Escande 335e011558SThierry Escande /* Device variables */ 345e011558SThierry Escande #define CROS_MAX_DEV 128 355e011558SThierry Escande static int ec_major; 365e011558SThierry Escande 375e011558SThierry Escande static const struct attribute_group *cros_ec_groups[] = { 385e011558SThierry Escande &cros_ec_attr_group, 395e011558SThierry Escande &cros_ec_lightbar_attr_group, 405e011558SThierry Escande &cros_ec_vbc_attr_group, 415e011558SThierry Escande NULL, 425e011558SThierry Escande }; 435e011558SThierry Escande 445e011558SThierry Escande static struct class cros_class = { 455e011558SThierry Escande .owner = THIS_MODULE, 465e011558SThierry Escande .name = "chromeos", 475e011558SThierry Escande .dev_groups = cros_ec_groups, 485e011558SThierry Escande }; 495e011558SThierry Escande 505e011558SThierry Escande /* Basic communication */ 515e011558SThierry Escande static int ec_get_version(struct cros_ec_dev *ec, char *str, int maxlen) 525e011558SThierry Escande { 535e011558SThierry Escande struct ec_response_get_version *resp; 545e011558SThierry Escande static const char * const current_image_name[] = { 555e011558SThierry Escande "unknown", "read-only", "read-write", "invalid", 565e011558SThierry Escande }; 575e011558SThierry Escande struct cros_ec_command *msg; 585e011558SThierry Escande int ret; 595e011558SThierry Escande 605e011558SThierry Escande msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL); 615e011558SThierry Escande if (!msg) 625e011558SThierry Escande return -ENOMEM; 635e011558SThierry Escande 645e011558SThierry Escande msg->version = 0; 655e011558SThierry Escande msg->command = EC_CMD_GET_VERSION + ec->cmd_offset; 665e011558SThierry Escande msg->insize = sizeof(*resp); 675e011558SThierry Escande msg->outsize = 0; 685e011558SThierry Escande 695e011558SThierry Escande ret = cros_ec_cmd_xfer(ec->ec_dev, msg); 705e011558SThierry Escande if (ret < 0) 715e011558SThierry Escande goto exit; 725e011558SThierry Escande 735e011558SThierry Escande if (msg->result != EC_RES_SUCCESS) { 745e011558SThierry Escande snprintf(str, maxlen, 755e011558SThierry Escande "%s\nUnknown EC version: EC returned %d\n", 765e011558SThierry Escande CROS_EC_DEV_VERSION, msg->result); 775e011558SThierry Escande ret = -EINVAL; 785e011558SThierry Escande goto exit; 795e011558SThierry Escande } 805e011558SThierry Escande 815e011558SThierry Escande resp = (struct ec_response_get_version *)msg->data; 825e011558SThierry Escande if (resp->current_image >= ARRAY_SIZE(current_image_name)) 835e011558SThierry Escande resp->current_image = 3; /* invalid */ 845e011558SThierry Escande 855e011558SThierry Escande snprintf(str, maxlen, "%s\n%s\n%s\n%s\n", CROS_EC_DEV_VERSION, 865e011558SThierry Escande resp->version_string_ro, resp->version_string_rw, 875e011558SThierry Escande current_image_name[resp->current_image]); 885e011558SThierry Escande 895e011558SThierry Escande ret = 0; 905e011558SThierry Escande exit: 915e011558SThierry Escande kfree(msg); 925e011558SThierry Escande return ret; 935e011558SThierry Escande } 945e011558SThierry Escande 955e011558SThierry Escande static int cros_ec_check_features(struct cros_ec_dev *ec, int feature) 965e011558SThierry Escande { 975e011558SThierry Escande struct cros_ec_command *msg; 985e011558SThierry Escande int ret; 995e011558SThierry Escande 1005e011558SThierry Escande if (ec->features[0] == -1U && ec->features[1] == -1U) { 1015e011558SThierry Escande /* features bitmap not read yet */ 1025e011558SThierry Escande 1035e011558SThierry Escande msg = kmalloc(sizeof(*msg) + sizeof(ec->features), GFP_KERNEL); 1045e011558SThierry Escande if (!msg) 1055e011558SThierry Escande return -ENOMEM; 1065e011558SThierry Escande 1075e011558SThierry Escande msg->version = 0; 1085e011558SThierry Escande msg->command = EC_CMD_GET_FEATURES + ec->cmd_offset; 1095e011558SThierry Escande msg->insize = sizeof(ec->features); 1105e011558SThierry Escande msg->outsize = 0; 1115e011558SThierry Escande 1125e011558SThierry Escande ret = cros_ec_cmd_xfer(ec->ec_dev, msg); 1135e011558SThierry Escande if (ret < 0 || msg->result != EC_RES_SUCCESS) { 1145e011558SThierry Escande dev_warn(ec->dev, "cannot get EC features: %d/%d\n", 1155e011558SThierry Escande ret, msg->result); 1165e011558SThierry Escande memset(ec->features, 0, sizeof(ec->features)); 117df7c3bf2SStephen Boyd } else { 1185e011558SThierry Escande memcpy(ec->features, msg->data, sizeof(ec->features)); 119df7c3bf2SStephen Boyd } 1205e011558SThierry Escande 1215e011558SThierry Escande dev_dbg(ec->dev, "EC features %08x %08x\n", 1225e011558SThierry Escande ec->features[0], ec->features[1]); 1235e011558SThierry Escande 1245e011558SThierry Escande kfree(msg); 1255e011558SThierry Escande } 1265e011558SThierry Escande 1275e011558SThierry Escande return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature); 1285e011558SThierry Escande } 1295e011558SThierry Escande 1305e011558SThierry Escande /* Device file ops */ 1315e011558SThierry Escande static int ec_device_open(struct inode *inode, struct file *filp) 1325e011558SThierry Escande { 1335e011558SThierry Escande struct cros_ec_dev *ec = container_of(inode->i_cdev, 1345e011558SThierry Escande struct cros_ec_dev, cdev); 1355e011558SThierry Escande filp->private_data = ec; 1365e011558SThierry Escande nonseekable_open(inode, filp); 1375e011558SThierry Escande return 0; 1385e011558SThierry Escande } 1395e011558SThierry Escande 1405e011558SThierry Escande static int ec_device_release(struct inode *inode, struct file *filp) 1415e011558SThierry Escande { 1425e011558SThierry Escande return 0; 1435e011558SThierry Escande } 1445e011558SThierry Escande 1455e011558SThierry Escande static ssize_t ec_device_read(struct file *filp, char __user *buffer, 1465e011558SThierry Escande size_t length, loff_t *offset) 1475e011558SThierry Escande { 1485e011558SThierry Escande struct cros_ec_dev *ec = filp->private_data; 1495e011558SThierry Escande char msg[sizeof(struct ec_response_get_version) + 1505e011558SThierry Escande sizeof(CROS_EC_DEV_VERSION)]; 1515e011558SThierry Escande size_t count; 1525e011558SThierry Escande int ret; 1535e011558SThierry Escande 1545e011558SThierry Escande if (*offset != 0) 1555e011558SThierry Escande return 0; 1565e011558SThierry Escande 1575e011558SThierry Escande ret = ec_get_version(ec, msg, sizeof(msg)); 1585e011558SThierry Escande if (ret) 1595e011558SThierry Escande return ret; 1605e011558SThierry Escande 1615e011558SThierry Escande count = min(length, strlen(msg)); 1625e011558SThierry Escande 1635e011558SThierry Escande if (copy_to_user(buffer, msg, count)) 1645e011558SThierry Escande return -EFAULT; 1655e011558SThierry Escande 1665e011558SThierry Escande *offset = count; 1675e011558SThierry Escande return count; 1685e011558SThierry Escande } 1695e011558SThierry Escande 1705e011558SThierry Escande /* Ioctls */ 1715e011558SThierry Escande static long ec_device_ioctl_xcmd(struct cros_ec_dev *ec, void __user *arg) 1725e011558SThierry Escande { 1735e011558SThierry Escande long ret; 1745e011558SThierry Escande struct cros_ec_command u_cmd; 1755e011558SThierry Escande struct cros_ec_command *s_cmd; 1765e011558SThierry Escande 1775e011558SThierry Escande if (copy_from_user(&u_cmd, arg, sizeof(u_cmd))) 1785e011558SThierry Escande return -EFAULT; 1795e011558SThierry Escande 1805e011558SThierry Escande if ((u_cmd.outsize > EC_MAX_MSG_BYTES) || 1815e011558SThierry Escande (u_cmd.insize > EC_MAX_MSG_BYTES)) 1825e011558SThierry Escande return -EINVAL; 1835e011558SThierry Escande 1845e011558SThierry Escande s_cmd = kmalloc(sizeof(*s_cmd) + max(u_cmd.outsize, u_cmd.insize), 1855e011558SThierry Escande GFP_KERNEL); 1865e011558SThierry Escande if (!s_cmd) 1875e011558SThierry Escande return -ENOMEM; 1885e011558SThierry Escande 1895e011558SThierry Escande if (copy_from_user(s_cmd, arg, sizeof(*s_cmd) + u_cmd.outsize)) { 1905e011558SThierry Escande ret = -EFAULT; 1915e011558SThierry Escande goto exit; 1925e011558SThierry Escande } 1935e011558SThierry Escande 1945e011558SThierry Escande if (u_cmd.outsize != s_cmd->outsize || 1955e011558SThierry Escande u_cmd.insize != s_cmd->insize) { 1965e011558SThierry Escande ret = -EINVAL; 1975e011558SThierry Escande goto exit; 1985e011558SThierry Escande } 1995e011558SThierry Escande 2005e011558SThierry Escande s_cmd->command += ec->cmd_offset; 2015e011558SThierry Escande ret = cros_ec_cmd_xfer(ec->ec_dev, s_cmd); 2025e011558SThierry Escande /* Only copy data to userland if data was received. */ 2035e011558SThierry Escande if (ret < 0) 2045e011558SThierry Escande goto exit; 2055e011558SThierry Escande 2065e011558SThierry Escande if (copy_to_user(arg, s_cmd, sizeof(*s_cmd) + s_cmd->insize)) 2075e011558SThierry Escande ret = -EFAULT; 2085e011558SThierry Escande exit: 2095e011558SThierry Escande kfree(s_cmd); 2105e011558SThierry Escande return ret; 2115e011558SThierry Escande } 2125e011558SThierry Escande 2135e011558SThierry Escande static long ec_device_ioctl_readmem(struct cros_ec_dev *ec, void __user *arg) 2145e011558SThierry Escande { 2155e011558SThierry Escande struct cros_ec_device *ec_dev = ec->ec_dev; 2165e011558SThierry Escande struct cros_ec_readmem s_mem = { }; 2175e011558SThierry Escande long num; 2185e011558SThierry Escande 2195e011558SThierry Escande /* Not every platform supports direct reads */ 2205e011558SThierry Escande if (!ec_dev->cmd_readmem) 2215e011558SThierry Escande return -ENOTTY; 2225e011558SThierry Escande 2235e011558SThierry Escande if (copy_from_user(&s_mem, arg, sizeof(s_mem))) 2245e011558SThierry Escande return -EFAULT; 2255e011558SThierry Escande 2265e011558SThierry Escande num = ec_dev->cmd_readmem(ec_dev, s_mem.offset, s_mem.bytes, 2275e011558SThierry Escande s_mem.buffer); 2285e011558SThierry Escande if (num <= 0) 2295e011558SThierry Escande return num; 2305e011558SThierry Escande 2315e011558SThierry Escande if (copy_to_user((void __user *)arg, &s_mem, sizeof(s_mem))) 2325e011558SThierry Escande return -EFAULT; 2335e011558SThierry Escande 2345e011558SThierry Escande return 0; 2355e011558SThierry Escande } 2365e011558SThierry Escande 2375e011558SThierry Escande static long ec_device_ioctl(struct file *filp, unsigned int cmd, 2385e011558SThierry Escande unsigned long arg) 2395e011558SThierry Escande { 2405e011558SThierry Escande struct cros_ec_dev *ec = filp->private_data; 2415e011558SThierry Escande 2425e011558SThierry Escande if (_IOC_TYPE(cmd) != CROS_EC_DEV_IOC) 2435e011558SThierry Escande return -ENOTTY; 2445e011558SThierry Escande 2455e011558SThierry Escande switch (cmd) { 2465e011558SThierry Escande case CROS_EC_DEV_IOCXCMD: 2475e011558SThierry Escande return ec_device_ioctl_xcmd(ec, (void __user *)arg); 2485e011558SThierry Escande case CROS_EC_DEV_IOCRDMEM: 2495e011558SThierry Escande return ec_device_ioctl_readmem(ec, (void __user *)arg); 2505e011558SThierry Escande } 2515e011558SThierry Escande 2525e011558SThierry Escande return -ENOTTY; 2535e011558SThierry Escande } 2545e011558SThierry Escande 2555e011558SThierry Escande /* Module initialization */ 2565e011558SThierry Escande static const struct file_operations fops = { 2575e011558SThierry Escande .open = ec_device_open, 2585e011558SThierry Escande .release = ec_device_release, 2595e011558SThierry Escande .read = ec_device_read, 2605e011558SThierry Escande .unlocked_ioctl = ec_device_ioctl, 2615e011558SThierry Escande #ifdef CONFIG_COMPAT 2625e011558SThierry Escande .compat_ioctl = ec_device_ioctl, 2635e011558SThierry Escande #endif 2645e011558SThierry Escande }; 2655e011558SThierry Escande 2665e011558SThierry Escande static void cros_ec_sensors_register(struct cros_ec_dev *ec) 2675e011558SThierry Escande { 2685e011558SThierry Escande /* 2695e011558SThierry Escande * Issue a command to get the number of sensor reported. 2705e011558SThierry Escande * Build an array of sensors driver and register them all. 2715e011558SThierry Escande */ 2725e011558SThierry Escande int ret, i, id, sensor_num; 2735e011558SThierry Escande struct mfd_cell *sensor_cells; 2745e011558SThierry Escande struct cros_ec_sensor_platform *sensor_platforms; 2755e011558SThierry Escande int sensor_type[MOTIONSENSE_TYPE_MAX]; 2765e011558SThierry Escande struct ec_params_motion_sense *params; 2775e011558SThierry Escande struct ec_response_motion_sense *resp; 2785e011558SThierry Escande struct cros_ec_command *msg; 2795e011558SThierry Escande 2805e011558SThierry Escande msg = kzalloc(sizeof(struct cros_ec_command) + 2815e011558SThierry Escande max(sizeof(*params), sizeof(*resp)), GFP_KERNEL); 2825e011558SThierry Escande if (msg == NULL) 2835e011558SThierry Escande return; 2845e011558SThierry Escande 2855e011558SThierry Escande msg->version = 2; 2865e011558SThierry Escande msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset; 2875e011558SThierry Escande msg->outsize = sizeof(*params); 2885e011558SThierry Escande msg->insize = sizeof(*resp); 2895e011558SThierry Escande 2905e011558SThierry Escande params = (struct ec_params_motion_sense *)msg->data; 2915e011558SThierry Escande params->cmd = MOTIONSENSE_CMD_DUMP; 2925e011558SThierry Escande 2935e011558SThierry Escande ret = cros_ec_cmd_xfer(ec->ec_dev, msg); 2945e011558SThierry Escande if (ret < 0 || msg->result != EC_RES_SUCCESS) { 2955e011558SThierry Escande dev_warn(ec->dev, "cannot get EC sensor information: %d/%d\n", 2965e011558SThierry Escande ret, msg->result); 2975e011558SThierry Escande goto error; 2985e011558SThierry Escande } 2995e011558SThierry Escande 3005e011558SThierry Escande resp = (struct ec_response_motion_sense *)msg->data; 3015e011558SThierry Escande sensor_num = resp->dump.sensor_count; 302c1d1e91aSGwendal Grignou /* Allocate 1 extra sensors in FIFO are needed */ 3036396bb22SKees Cook sensor_cells = kcalloc(sensor_num + 1, sizeof(struct mfd_cell), 3045e011558SThierry Escande GFP_KERNEL); 3055e011558SThierry Escande if (sensor_cells == NULL) 3065e011558SThierry Escande goto error; 3075e011558SThierry Escande 3086396bb22SKees Cook sensor_platforms = kcalloc(sensor_num + 1, 3096396bb22SKees Cook sizeof(struct cros_ec_sensor_platform), 3106396bb22SKees Cook GFP_KERNEL); 3115e011558SThierry Escande if (sensor_platforms == NULL) 3125e011558SThierry Escande goto error_platforms; 3135e011558SThierry Escande 3145e011558SThierry Escande memset(sensor_type, 0, sizeof(sensor_type)); 3155e011558SThierry Escande id = 0; 3165e011558SThierry Escande for (i = 0; i < sensor_num; i++) { 3175e011558SThierry Escande params->cmd = MOTIONSENSE_CMD_INFO; 3185e011558SThierry Escande params->info.sensor_num = i; 3195e011558SThierry Escande ret = cros_ec_cmd_xfer(ec->ec_dev, msg); 3205e011558SThierry Escande if (ret < 0 || msg->result != EC_RES_SUCCESS) { 3215e011558SThierry Escande dev_warn(ec->dev, "no info for EC sensor %d : %d/%d\n", 3225e011558SThierry Escande i, ret, msg->result); 3235e011558SThierry Escande continue; 3245e011558SThierry Escande } 3255e011558SThierry Escande switch (resp->info.type) { 3265e011558SThierry Escande case MOTIONSENSE_TYPE_ACCEL: 3275e011558SThierry Escande sensor_cells[id].name = "cros-ec-accel"; 3285e011558SThierry Escande break; 3295e011558SThierry Escande case MOTIONSENSE_TYPE_BARO: 3305e011558SThierry Escande sensor_cells[id].name = "cros-ec-baro"; 3315e011558SThierry Escande break; 3325e011558SThierry Escande case MOTIONSENSE_TYPE_GYRO: 3335e011558SThierry Escande sensor_cells[id].name = "cros-ec-gyro"; 3345e011558SThierry Escande break; 3355e011558SThierry Escande case MOTIONSENSE_TYPE_MAG: 3365e011558SThierry Escande sensor_cells[id].name = "cros-ec-mag"; 3375e011558SThierry Escande break; 3385e011558SThierry Escande case MOTIONSENSE_TYPE_PROX: 3395e011558SThierry Escande sensor_cells[id].name = "cros-ec-prox"; 3405e011558SThierry Escande break; 3415e011558SThierry Escande case MOTIONSENSE_TYPE_LIGHT: 3425e011558SThierry Escande sensor_cells[id].name = "cros-ec-light"; 3435e011558SThierry Escande break; 3445e011558SThierry Escande case MOTIONSENSE_TYPE_ACTIVITY: 3455e011558SThierry Escande sensor_cells[id].name = "cros-ec-activity"; 3465e011558SThierry Escande break; 3475e011558SThierry Escande default: 3485e011558SThierry Escande dev_warn(ec->dev, "unknown type %d\n", resp->info.type); 3495e011558SThierry Escande continue; 3505e011558SThierry Escande } 3515e011558SThierry Escande sensor_platforms[id].sensor_num = i; 3525e011558SThierry Escande sensor_cells[id].id = sensor_type[resp->info.type]; 3535e011558SThierry Escande sensor_cells[id].platform_data = &sensor_platforms[id]; 3545e011558SThierry Escande sensor_cells[id].pdata_size = 3555e011558SThierry Escande sizeof(struct cros_ec_sensor_platform); 3565e011558SThierry Escande 3575e011558SThierry Escande sensor_type[resp->info.type]++; 3585e011558SThierry Escande id++; 3595e011558SThierry Escande } 3605e011558SThierry Escande 361c1d1e91aSGwendal Grignou if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2) 362c1d1e91aSGwendal Grignou ec->has_kb_wake_angle = true; 363c1d1e91aSGwendal Grignou 3645e011558SThierry Escande if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) { 3655e011558SThierry Escande sensor_cells[id].name = "cros-ec-ring"; 3665e011558SThierry Escande id++; 3675e011558SThierry Escande } 3685e011558SThierry Escande 3695e011558SThierry Escande ret = mfd_add_devices(ec->dev, 0, sensor_cells, id, 3705e011558SThierry Escande NULL, 0, NULL); 3715e011558SThierry Escande if (ret) 3725e011558SThierry Escande dev_err(ec->dev, "failed to add EC sensors\n"); 3735e011558SThierry Escande 3745e011558SThierry Escande kfree(sensor_platforms); 3755e011558SThierry Escande error_platforms: 3765e011558SThierry Escande kfree(sensor_cells); 3775e011558SThierry Escande error: 3785e011558SThierry Escande kfree(msg); 3795e011558SThierry Escande } 3805e011558SThierry Escande 38103a5755cSNeil Armstrong static const struct mfd_cell cros_ec_cec_cells[] = { 38203a5755cSNeil Armstrong { .name = "cros-ec-cec" } 38303a5755cSNeil Armstrong }; 38403a5755cSNeil Armstrong 38595a4d07fSEnric Balletbo i Serra static const struct mfd_cell cros_ec_rtc_cells[] = { 38695a4d07fSEnric Balletbo i Serra { .name = "cros-ec-rtc" } 38795a4d07fSEnric Balletbo i Serra }; 38895a4d07fSEnric Balletbo i Serra 3893144dce7SEnric Balletbo i Serra static const struct mfd_cell cros_usbpd_charger_cells[] = { 3903144dce7SEnric Balletbo i Serra { .name = "cros-usbpd-charger" } 3913144dce7SEnric Balletbo i Serra }; 3923144dce7SEnric Balletbo i Serra 3935e011558SThierry Escande static int ec_device_probe(struct platform_device *pdev) 3945e011558SThierry Escande { 3955e011558SThierry Escande int retval = -ENOMEM; 3965e011558SThierry Escande struct device *dev = &pdev->dev; 3975e011558SThierry Escande struct cros_ec_platform *ec_platform = dev_get_platdata(dev); 3983aa2177eSGwendal Grignou struct cros_ec_dev *ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL); 3995e011558SThierry Escande 4005e011558SThierry Escande if (!ec) 4015e011558SThierry Escande return retval; 4025e011558SThierry Escande 4035e011558SThierry Escande dev_set_drvdata(dev, ec); 4045e011558SThierry Escande ec->ec_dev = dev_get_drvdata(dev->parent); 4055e011558SThierry Escande ec->dev = dev; 4065e011558SThierry Escande ec->cmd_offset = ec_platform->cmd_offset; 4075e011558SThierry Escande ec->features[0] = -1U; /* Not cached yet */ 4085e011558SThierry Escande ec->features[1] = -1U; /* Not cached yet */ 4095e011558SThierry Escande device_initialize(&ec->class_dev); 4105e011558SThierry Escande cdev_init(&ec->cdev, &fops); 4115e011558SThierry Escande 4125e011558SThierry Escande /* 4135e011558SThierry Escande * Add the class device 4145e011558SThierry Escande * Link to the character device for creating the /dev entry 4155e011558SThierry Escande * in devtmpfs. 4165e011558SThierry Escande */ 4175e011558SThierry Escande ec->class_dev.devt = MKDEV(ec_major, pdev->id); 4185e011558SThierry Escande ec->class_dev.class = &cros_class; 4195e011558SThierry Escande ec->class_dev.parent = dev; 4205e011558SThierry Escande 4215e011558SThierry Escande retval = dev_set_name(&ec->class_dev, "%s", ec_platform->ec_name); 4225e011558SThierry Escande if (retval) { 4235e011558SThierry Escande dev_err(dev, "dev_set_name failed => %d\n", retval); 4245e011558SThierry Escande goto failed; 4255e011558SThierry Escande } 4265e011558SThierry Escande 427c1d1e91aSGwendal Grignou /* check whether this EC is a sensor hub. */ 428c1d1e91aSGwendal Grignou if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE)) 429c1d1e91aSGwendal Grignou cros_ec_sensors_register(ec); 430c1d1e91aSGwendal Grignou 43103a5755cSNeil Armstrong /* Check whether this EC instance has CEC host command support */ 43203a5755cSNeil Armstrong if (cros_ec_check_features(ec, EC_FEATURE_CEC)) { 43303a5755cSNeil Armstrong retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO, 43403a5755cSNeil Armstrong cros_ec_cec_cells, 43503a5755cSNeil Armstrong ARRAY_SIZE(cros_ec_cec_cells), 43603a5755cSNeil Armstrong NULL, 0, NULL); 43703a5755cSNeil Armstrong if (retval) 43803a5755cSNeil Armstrong dev_err(ec->dev, 43903a5755cSNeil Armstrong "failed to add cros-ec-cec device: %d\n", 44003a5755cSNeil Armstrong retval); 44103a5755cSNeil Armstrong } 44203a5755cSNeil Armstrong 44395a4d07fSEnric Balletbo i Serra /* Check whether this EC instance has RTC host command support */ 44495a4d07fSEnric Balletbo i Serra if (cros_ec_check_features(ec, EC_FEATURE_RTC)) { 44595a4d07fSEnric Balletbo i Serra retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO, 44695a4d07fSEnric Balletbo i Serra cros_ec_rtc_cells, 44795a4d07fSEnric Balletbo i Serra ARRAY_SIZE(cros_ec_rtc_cells), 44895a4d07fSEnric Balletbo i Serra NULL, 0, NULL); 44995a4d07fSEnric Balletbo i Serra if (retval) 45095a4d07fSEnric Balletbo i Serra dev_err(ec->dev, 45195a4d07fSEnric Balletbo i Serra "failed to add cros-ec-rtc device: %d\n", 45295a4d07fSEnric Balletbo i Serra retval); 45395a4d07fSEnric Balletbo i Serra } 45495a4d07fSEnric Balletbo i Serra 4553144dce7SEnric Balletbo i Serra /* Check whether this EC instance has the PD charge manager */ 4563144dce7SEnric Balletbo i Serra if (cros_ec_check_features(ec, EC_FEATURE_USB_PD)) { 4573144dce7SEnric Balletbo i Serra retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO, 4583144dce7SEnric Balletbo i Serra cros_usbpd_charger_cells, 4593144dce7SEnric Balletbo i Serra ARRAY_SIZE(cros_usbpd_charger_cells), 4603144dce7SEnric Balletbo i Serra NULL, 0, NULL); 4613144dce7SEnric Balletbo i Serra if (retval) 4623144dce7SEnric Balletbo i Serra dev_err(ec->dev, 4633144dce7SEnric Balletbo i Serra "failed to add cros-usbpd-charger device: %d\n", 4643144dce7SEnric Balletbo i Serra retval); 4653144dce7SEnric Balletbo i Serra } 4663144dce7SEnric Balletbo i Serra 467c1d1e91aSGwendal Grignou /* Take control of the lightbar from the EC. */ 468c1d1e91aSGwendal Grignou lb_manual_suspend_ctrl(ec, 1); 469c1d1e91aSGwendal Grignou 470c1d1e91aSGwendal Grignou /* We can now add the sysfs class, we know which parameter to show */ 4715e011558SThierry Escande retval = cdev_device_add(&ec->cdev, &ec->class_dev); 4725e011558SThierry Escande if (retval) { 4735e011558SThierry Escande dev_err(dev, "cdev_device_add failed => %d\n", retval); 4745e011558SThierry Escande goto failed; 4755e011558SThierry Escande } 4765e011558SThierry Escande 4775e011558SThierry Escande if (cros_ec_debugfs_init(ec)) 4785e011558SThierry Escande dev_warn(dev, "failed to create debugfs directory\n"); 4795e011558SThierry Escande 4805e011558SThierry Escande return 0; 4815e011558SThierry Escande 4825e011558SThierry Escande failed: 4835e011558SThierry Escande put_device(&ec->class_dev); 4845e011558SThierry Escande return retval; 4855e011558SThierry Escande } 4865e011558SThierry Escande 4875e011558SThierry Escande static int ec_device_remove(struct platform_device *pdev) 4885e011558SThierry Escande { 4895e011558SThierry Escande struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev); 4905e011558SThierry Escande 4915e011558SThierry Escande /* Let the EC take over the lightbar again. */ 4925e011558SThierry Escande lb_manual_suspend_ctrl(ec, 0); 4935e011558SThierry Escande 4945e011558SThierry Escande cros_ec_debugfs_remove(ec); 4955e011558SThierry Escande 4965e011558SThierry Escande cdev_del(&ec->cdev); 4975e011558SThierry Escande device_unregister(&ec->class_dev); 4985e011558SThierry Escande return 0; 4995e011558SThierry Escande } 5005e011558SThierry Escande 501e15b7f44SDaniel Hung-yu Wu static void ec_device_shutdown(struct platform_device *pdev) 502e15b7f44SDaniel Hung-yu Wu { 503e15b7f44SDaniel Hung-yu Wu struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev); 504e15b7f44SDaniel Hung-yu Wu 505e15b7f44SDaniel Hung-yu Wu /* Be sure to clear up debugfs delayed works */ 506e15b7f44SDaniel Hung-yu Wu cros_ec_debugfs_remove(ec); 507e15b7f44SDaniel Hung-yu Wu } 508e15b7f44SDaniel Hung-yu Wu 5095e011558SThierry Escande static const struct platform_device_id cros_ec_id[] = { 5105e011558SThierry Escande { DRV_NAME, 0 }, 511abeed71bSWei-Ning Huang { /* sentinel */ } 5125e011558SThierry Escande }; 5135e011558SThierry Escande MODULE_DEVICE_TABLE(platform, cros_ec_id); 5145e011558SThierry Escande 5155e011558SThierry Escande static __maybe_unused int ec_device_suspend(struct device *dev) 5165e011558SThierry Escande { 5175e011558SThierry Escande struct cros_ec_dev *ec = dev_get_drvdata(dev); 5185e011558SThierry Escande 51944d99d73SDouglas Anderson cros_ec_debugfs_suspend(ec); 52044d99d73SDouglas Anderson 5215e011558SThierry Escande lb_suspend(ec); 5225e011558SThierry Escande 5235e011558SThierry Escande return 0; 5245e011558SThierry Escande } 5255e011558SThierry Escande 5265e011558SThierry Escande static __maybe_unused int ec_device_resume(struct device *dev) 5275e011558SThierry Escande { 5285e011558SThierry Escande struct cros_ec_dev *ec = dev_get_drvdata(dev); 5295e011558SThierry Escande 53044d99d73SDouglas Anderson cros_ec_debugfs_resume(ec); 53144d99d73SDouglas Anderson 5325e011558SThierry Escande lb_resume(ec); 5335e011558SThierry Escande 5345e011558SThierry Escande return 0; 5355e011558SThierry Escande } 5365e011558SThierry Escande 5375e011558SThierry Escande static const struct dev_pm_ops cros_ec_dev_pm_ops = { 5385e011558SThierry Escande #ifdef CONFIG_PM_SLEEP 5395e011558SThierry Escande .suspend = ec_device_suspend, 5405e011558SThierry Escande .resume = ec_device_resume, 5415e011558SThierry Escande #endif 5425e011558SThierry Escande }; 5435e011558SThierry Escande 5445e011558SThierry Escande static struct platform_driver cros_ec_dev_driver = { 5455e011558SThierry Escande .driver = { 5465e011558SThierry Escande .name = DRV_NAME, 5475e011558SThierry Escande .pm = &cros_ec_dev_pm_ops, 5485e011558SThierry Escande }, 5496eb35784SNathan Chancellor .id_table = cros_ec_id, 5505e011558SThierry Escande .probe = ec_device_probe, 5515e011558SThierry Escande .remove = ec_device_remove, 552e15b7f44SDaniel Hung-yu Wu .shutdown = ec_device_shutdown, 5535e011558SThierry Escande }; 5545e011558SThierry Escande 5555e011558SThierry Escande static int __init cros_ec_dev_init(void) 5565e011558SThierry Escande { 5575e011558SThierry Escande int ret; 5585e011558SThierry Escande dev_t dev = 0; 5595e011558SThierry Escande 5605e011558SThierry Escande ret = class_register(&cros_class); 5615e011558SThierry Escande if (ret) { 5625e011558SThierry Escande pr_err(CROS_EC_DEV_NAME ": failed to register device class\n"); 5635e011558SThierry Escande return ret; 5645e011558SThierry Escande } 5655e011558SThierry Escande 5665e011558SThierry Escande /* Get a range of minor numbers (starting with 0) to work with */ 5675e011558SThierry Escande ret = alloc_chrdev_region(&dev, 0, CROS_MAX_DEV, CROS_EC_DEV_NAME); 5685e011558SThierry Escande if (ret < 0) { 5695e011558SThierry Escande pr_err(CROS_EC_DEV_NAME ": alloc_chrdev_region() failed\n"); 5705e011558SThierry Escande goto failed_chrdevreg; 5715e011558SThierry Escande } 5725e011558SThierry Escande ec_major = MAJOR(dev); 5735e011558SThierry Escande 5745e011558SThierry Escande /* Register the driver */ 5755e011558SThierry Escande ret = platform_driver_register(&cros_ec_dev_driver); 5765e011558SThierry Escande if (ret < 0) { 5775e011558SThierry Escande pr_warn(CROS_EC_DEV_NAME ": can't register driver: %d\n", ret); 5785e011558SThierry Escande goto failed_devreg; 5795e011558SThierry Escande } 5805e011558SThierry Escande return 0; 5815e011558SThierry Escande 5825e011558SThierry Escande failed_devreg: 5835e011558SThierry Escande unregister_chrdev_region(MKDEV(ec_major, 0), CROS_MAX_DEV); 5845e011558SThierry Escande failed_chrdevreg: 5855e011558SThierry Escande class_unregister(&cros_class); 5865e011558SThierry Escande return ret; 5875e011558SThierry Escande } 5885e011558SThierry Escande 5895e011558SThierry Escande static void __exit cros_ec_dev_exit(void) 5905e011558SThierry Escande { 5915e011558SThierry Escande platform_driver_unregister(&cros_ec_dev_driver); 5925e011558SThierry Escande unregister_chrdev(ec_major, CROS_EC_DEV_NAME); 5935e011558SThierry Escande class_unregister(&cros_class); 5945e011558SThierry Escande } 5955e011558SThierry Escande 5965e011558SThierry Escande module_init(cros_ec_dev_init); 5975e011558SThierry Escande module_exit(cros_ec_dev_exit); 5985e011558SThierry Escande 5995e011558SThierry Escande MODULE_ALIAS("platform:" DRV_NAME); 6005e011558SThierry Escande MODULE_AUTHOR("Bill Richardson <wfrichar@chromium.org>"); 6015e011558SThierry Escande MODULE_DESCRIPTION("Userspace interface to the Chrome OS Embedded Controller"); 6025e011558SThierry Escande MODULE_VERSION("1.0"); 6035e011558SThierry Escande MODULE_LICENSE("GPL"); 604