1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Sensor HUB driver that discovers sensors behind a ChromeOS Embedded 4 * Controller. 5 * 6 * Copyright 2019 Google LLC 7 */ 8 9 #include <linux/init.h> 10 #include <linux/device.h> 11 #include <linux/delay.h> 12 #include <linux/module.h> 13 #include <linux/platform_data/cros_ec_commands.h> 14 #include <linux/platform_data/cros_ec_proto.h> 15 #include <linux/platform_data/cros_ec_sensorhub.h> 16 #include <linux/platform_device.h> 17 #include <linux/slab.h> 18 #include <linux/types.h> 19 20 #define DRV_NAME "cros-ec-sensorhub" 21 #define CROS_EC_CMD_INFO_RETRIES 50 22 23 static void cros_ec_sensorhub_free_sensor(void *arg) 24 { 25 struct platform_device *pdev = arg; 26 27 platform_device_unregister(pdev); 28 } 29 30 static int cros_ec_sensorhub_allocate_sensor(struct device *parent, 31 char *sensor_name, 32 int sensor_num) 33 { 34 struct cros_ec_sensor_platform sensor_platforms = { 35 .sensor_num = sensor_num, 36 }; 37 struct platform_device *pdev; 38 39 pdev = platform_device_register_data(parent, sensor_name, 40 PLATFORM_DEVID_AUTO, 41 &sensor_platforms, 42 sizeof(sensor_platforms)); 43 if (IS_ERR(pdev)) 44 return PTR_ERR(pdev); 45 46 return devm_add_action_or_reset(parent, 47 cros_ec_sensorhub_free_sensor, 48 pdev); 49 } 50 51 static int cros_ec_sensorhub_register(struct device *dev, 52 struct cros_ec_sensorhub *sensorhub) 53 { 54 int sensor_type[MOTIONSENSE_TYPE_MAX] = { 0 }; 55 struct cros_ec_command *msg = sensorhub->msg; 56 struct cros_ec_dev *ec = sensorhub->ec; 57 int ret, i, retries; 58 char *name; 59 60 61 msg->version = 1; 62 msg->insize = sizeof(struct ec_response_motion_sense); 63 msg->outsize = sizeof(struct ec_params_motion_sense); 64 65 for (i = 0; i < sensorhub->sensor_num; i++) { 66 sensorhub->params->cmd = MOTIONSENSE_CMD_INFO; 67 sensorhub->params->info.sensor_num = i; 68 69 retries = CROS_EC_CMD_INFO_RETRIES; 70 do { 71 ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); 72 if (ret == -EBUSY) { 73 /* The EC is still busy initializing sensors. */ 74 usleep_range(5000, 6000); 75 retries--; 76 } 77 } while (ret == -EBUSY && retries); 78 79 if (ret < 0) { 80 dev_err(dev, "no info for EC sensor %d : %d/%d\n", 81 i, ret, msg->result); 82 continue; 83 } 84 if (retries < CROS_EC_CMD_INFO_RETRIES) { 85 dev_warn(dev, "%d retries needed to bring up sensor %d\n", 86 CROS_EC_CMD_INFO_RETRIES - retries, i); 87 } 88 89 switch (sensorhub->resp->info.type) { 90 case MOTIONSENSE_TYPE_ACCEL: 91 name = "cros-ec-accel"; 92 break; 93 case MOTIONSENSE_TYPE_BARO: 94 name = "cros-ec-baro"; 95 break; 96 case MOTIONSENSE_TYPE_GYRO: 97 name = "cros-ec-gyro"; 98 break; 99 case MOTIONSENSE_TYPE_MAG: 100 name = "cros-ec-mag"; 101 break; 102 case MOTIONSENSE_TYPE_PROX: 103 name = "cros-ec-prox"; 104 break; 105 case MOTIONSENSE_TYPE_LIGHT: 106 name = "cros-ec-light"; 107 break; 108 case MOTIONSENSE_TYPE_ACTIVITY: 109 name = "cros-ec-activity"; 110 break; 111 default: 112 dev_warn(dev, "unknown type %d\n", 113 sensorhub->resp->info.type); 114 continue; 115 } 116 117 ret = cros_ec_sensorhub_allocate_sensor(dev, name, i); 118 if (ret) 119 return ret; 120 121 sensor_type[sensorhub->resp->info.type]++; 122 } 123 124 if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2) { 125 ec->has_kb_wake_angle = true; 126 if (ec->group && sysfs_update_group(&ec->class_dev.kobj, 127 ec->group)) 128 dev_warn(dev, "Unable to update sysfs"); 129 } 130 131 if (cros_ec_check_features(ec, 132 EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS)) { 133 ret = cros_ec_sensorhub_allocate_sensor(dev, 134 "cros-ec-lid-angle", 135 0); 136 if (ret) 137 return ret; 138 } 139 140 return 0; 141 } 142 143 static int cros_ec_sensorhub_probe(struct platform_device *pdev) 144 { 145 struct device *dev = &pdev->dev; 146 struct cros_ec_dev *ec = dev_get_drvdata(dev->parent); 147 struct cros_ec_sensorhub *data; 148 struct cros_ec_command *msg; 149 int ret, i, sensor_num; 150 151 msg = devm_kzalloc(dev, sizeof(struct cros_ec_command) + 152 max((u16)sizeof(struct ec_params_motion_sense), 153 ec->ec_dev->max_response), GFP_KERNEL); 154 if (!msg) 155 return -ENOMEM; 156 157 msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset; 158 159 data = devm_kzalloc(dev, sizeof(struct cros_ec_sensorhub), GFP_KERNEL); 160 if (!data) 161 return -ENOMEM; 162 163 mutex_init(&data->cmd_lock); 164 165 data->dev = dev; 166 data->ec = ec; 167 data->msg = msg; 168 data->params = (struct ec_params_motion_sense *)msg->data; 169 data->resp = (struct ec_response_motion_sense *)msg->data; 170 171 dev_set_drvdata(dev, data); 172 173 /* Check whether this EC is a sensor hub. */ 174 if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE)) { 175 sensor_num = cros_ec_get_sensor_count(ec); 176 if (sensor_num < 0) { 177 dev_err(dev, 178 "Unable to retrieve sensor information (err:%d)\n", 179 sensor_num); 180 return sensor_num; 181 } 182 if (sensor_num == 0) { 183 dev_err(dev, "Zero sensors reported.\n"); 184 return -EINVAL; 185 } 186 data->sensor_num = sensor_num; 187 188 /* 189 * Prepare the ring handler before enumering the 190 * sensors. 191 */ 192 if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) { 193 ret = cros_ec_sensorhub_ring_allocate(data); 194 if (ret) 195 return ret; 196 } 197 198 /* Enumerate the sensors.*/ 199 ret = cros_ec_sensorhub_register(dev, data); 200 if (ret) 201 return ret; 202 203 /* 204 * When the EC does not have a FIFO, the sensors will query 205 * their data themselves via sysfs or a software trigger. 206 */ 207 if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) { 208 ret = cros_ec_sensorhub_ring_add(data); 209 if (ret) 210 return ret; 211 /* 212 * The msg and its data is not under the control of the 213 * ring handler. 214 */ 215 return devm_add_action_or_reset(dev, 216 cros_ec_sensorhub_ring_remove, 217 data); 218 } 219 220 } else { 221 /* 222 * If the device has sensors but does not claim to 223 * be a sensor hub, we are in legacy mode. 224 */ 225 data->sensor_num = 2; 226 for (i = 0; i < data->sensor_num; i++) { 227 ret = cros_ec_sensorhub_allocate_sensor(dev, 228 "cros-ec-accel-legacy", i); 229 if (ret) 230 return ret; 231 } 232 } 233 234 235 return 0; 236 } 237 238 #ifdef CONFIG_PM_SLEEP 239 /* 240 * When the EC is suspending, we must stop sending interrupt, 241 * we may use the same interrupt line for waking up the device. 242 * Tell the EC to stop sending non-interrupt event on the iio ring. 243 */ 244 static int cros_ec_sensorhub_suspend(struct device *dev) 245 { 246 struct cros_ec_sensorhub *sensorhub = dev_get_drvdata(dev); 247 struct cros_ec_dev *ec = sensorhub->ec; 248 249 if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) 250 return cros_ec_sensorhub_ring_fifo_enable(sensorhub, false); 251 return 0; 252 } 253 254 static int cros_ec_sensorhub_resume(struct device *dev) 255 { 256 struct cros_ec_sensorhub *sensorhub = dev_get_drvdata(dev); 257 struct cros_ec_dev *ec = sensorhub->ec; 258 259 if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) 260 return cros_ec_sensorhub_ring_fifo_enable(sensorhub, true); 261 return 0; 262 } 263 #endif 264 265 static SIMPLE_DEV_PM_OPS(cros_ec_sensorhub_pm_ops, 266 cros_ec_sensorhub_suspend, 267 cros_ec_sensorhub_resume); 268 269 static const struct platform_device_id cros_ec_sensorhub_id[] = { 270 { DRV_NAME, 0 }, 271 {} 272 }; 273 MODULE_DEVICE_TABLE(platform, cros_ec_sensorhub_id); 274 275 static struct platform_driver cros_ec_sensorhub_driver = { 276 .driver = { 277 .name = DRV_NAME, 278 .pm = &cros_ec_sensorhub_pm_ops, 279 }, 280 .probe = cros_ec_sensorhub_probe, 281 .id_table = cros_ec_sensorhub_id, 282 }; 283 284 module_platform_driver(cros_ec_sensorhub_driver); 285 286 MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>"); 287 MODULE_DESCRIPTION("ChromeOS EC MEMS Sensor Hub Driver"); 288 MODULE_LICENSE("GPL"); 289