cros_ec_accel_legacy.c (7005B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Driver for older Chrome OS EC accelerometer 4 * 5 * Copyright 2017 Google, Inc 6 * 7 * This driver uses the memory mapper cros-ec interface to communicate 8 * with the Chrome OS EC about accelerometer data or older commands. 9 * Accelerometer access is presented through iio sysfs. 10 */ 11 12#include <linux/delay.h> 13#include <linux/device.h> 14#include <linux/iio/buffer.h> 15#include <linux/iio/common/cros_ec_sensors_core.h> 16#include <linux/iio/iio.h> 17#include <linux/iio/kfifo_buf.h> 18#include <linux/iio/trigger_consumer.h> 19#include <linux/iio/triggered_buffer.h> 20#include <linux/kernel.h> 21#include <linux/module.h> 22#include <linux/slab.h> 23#include <linux/platform_data/cros_ec_commands.h> 24#include <linux/platform_data/cros_ec_proto.h> 25#include <linux/platform_device.h> 26 27#define DRV_NAME "cros-ec-accel-legacy" 28 29#define CROS_EC_SENSOR_LEGACY_NUM 2 30/* 31 * Sensor scale hard coded at 10 bits per g, computed as: 32 * g / (2^10 - 1) = 0.009586168; with g = 9.80665 m.s^-2 33 */ 34#define ACCEL_LEGACY_NSCALE 9586168 35 36/* 37 * Sensor frequency is hard-coded to 10Hz. 38 */ 39static const int cros_ec_legacy_sample_freq[] = { 10, 0 }; 40 41static int cros_ec_accel_legacy_read_cmd(struct iio_dev *indio_dev, 42 unsigned long scan_mask, s16 *data) 43{ 44 struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); 45 int ret; 46 unsigned int i; 47 u8 sensor_num; 48 49 /* 50 * Read all sensor data through a command. 51 * Save sensor_num, it is assumed to stay. 52 */ 53 sensor_num = st->param.info.sensor_num; 54 st->param.cmd = MOTIONSENSE_CMD_DUMP; 55 st->param.dump.max_sensor_count = CROS_EC_SENSOR_LEGACY_NUM; 56 ret = cros_ec_motion_send_host_cmd(st, 57 sizeof(st->resp->dump) + CROS_EC_SENSOR_LEGACY_NUM * 58 sizeof(struct ec_response_motion_sensor_data)); 59 st->param.info.sensor_num = sensor_num; 60 if (ret != 0) { 61 dev_warn(&indio_dev->dev, "Unable to read sensor data\n"); 62 return ret; 63 } 64 65 for_each_set_bit(i, &scan_mask, indio_dev->masklength) { 66 *data = st->resp->dump.sensor[sensor_num].data[i] * 67 st->sign[i]; 68 data++; 69 } 70 71 return 0; 72} 73 74static int cros_ec_accel_legacy_read(struct iio_dev *indio_dev, 75 struct iio_chan_spec const *chan, 76 int *val, int *val2, long mask) 77{ 78 struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); 79 s16 data = 0; 80 int ret; 81 int idx = chan->scan_index; 82 83 mutex_lock(&st->cmd_lock); 84 85 switch (mask) { 86 case IIO_CHAN_INFO_RAW: 87 ret = st->read_ec_sensors_data(indio_dev, 1 << idx, &data); 88 if (ret < 0) 89 break; 90 ret = IIO_VAL_INT; 91 *val = data; 92 break; 93 case IIO_CHAN_INFO_SCALE: 94 WARN_ON(st->type != MOTIONSENSE_TYPE_ACCEL); 95 *val = 0; 96 *val2 = ACCEL_LEGACY_NSCALE; 97 ret = IIO_VAL_INT_PLUS_NANO; 98 break; 99 case IIO_CHAN_INFO_CALIBBIAS: 100 /* Calibration not supported. */ 101 *val = 0; 102 ret = IIO_VAL_INT; 103 break; 104 case IIO_CHAN_INFO_SAMP_FREQ: 105 *val = cros_ec_legacy_sample_freq[0]; 106 *val2 = cros_ec_legacy_sample_freq[1]; 107 ret = IIO_VAL_INT_PLUS_MICRO; 108 break; 109 default: 110 ret = cros_ec_sensors_core_read(st, chan, val, val2, 111 mask); 112 break; 113 } 114 mutex_unlock(&st->cmd_lock); 115 116 return ret; 117} 118 119static int cros_ec_accel_legacy_write(struct iio_dev *indio_dev, 120 struct iio_chan_spec const *chan, 121 int val, int val2, long mask) 122{ 123 /* 124 * Do nothing but don't return an error code to allow calibration 125 * script to work. 126 */ 127 if (mask == IIO_CHAN_INFO_CALIBBIAS) 128 return 0; 129 130 return -EINVAL; 131} 132 133/** 134 * cros_ec_accel_legacy_read_avail() - get available values 135 * @indio_dev: pointer to state information for device 136 * @chan: channel specification structure table 137 * @vals: list of available values 138 * @type: type of data returned 139 * @length: number of data returned in the array 140 * @mask: specifies which values to be requested 141 * 142 * Return: an error code or IIO_AVAIL_LIST 143 */ 144static int cros_ec_accel_legacy_read_avail(struct iio_dev *indio_dev, 145 struct iio_chan_spec const *chan, 146 const int **vals, 147 int *type, 148 int *length, 149 long mask) 150{ 151 switch (mask) { 152 case IIO_CHAN_INFO_SAMP_FREQ: 153 *length = ARRAY_SIZE(cros_ec_legacy_sample_freq); 154 *vals = cros_ec_legacy_sample_freq; 155 *type = IIO_VAL_INT_PLUS_MICRO; 156 return IIO_AVAIL_LIST; 157 } 158 159 return -EINVAL; 160} 161 162static const struct iio_info cros_ec_accel_legacy_info = { 163 .read_raw = &cros_ec_accel_legacy_read, 164 .write_raw = &cros_ec_accel_legacy_write, 165 .read_avail = &cros_ec_accel_legacy_read_avail, 166}; 167 168/* 169 * Present the channel using HTML5 standard: 170 * need to invert X and Y and invert some lid axis. 171 */ 172#define CROS_EC_ACCEL_ROTATE_AXIS(_axis) \ 173 ((_axis) == CROS_EC_SENSOR_Z ? CROS_EC_SENSOR_Z : \ 174 ((_axis) == CROS_EC_SENSOR_X ? CROS_EC_SENSOR_Y : \ 175 CROS_EC_SENSOR_X)) 176 177#define CROS_EC_ACCEL_LEGACY_CHAN(_axis) \ 178 { \ 179 .type = IIO_ACCEL, \ 180 .channel2 = IIO_MOD_X + (_axis), \ 181 .modified = 1, \ 182 .info_mask_separate = \ 183 BIT(IIO_CHAN_INFO_RAW) | \ 184 BIT(IIO_CHAN_INFO_CALIBBIAS), \ 185 .info_mask_shared_by_all = \ 186 BIT(IIO_CHAN_INFO_SCALE) | \ 187 BIT(IIO_CHAN_INFO_SAMP_FREQ), \ 188 .info_mask_shared_by_all_available = \ 189 BIT(IIO_CHAN_INFO_SAMP_FREQ), \ 190 .ext_info = cros_ec_sensors_ext_info, \ 191 .scan_type = { \ 192 .sign = 's', \ 193 .realbits = CROS_EC_SENSOR_BITS, \ 194 .storagebits = CROS_EC_SENSOR_BITS, \ 195 }, \ 196 .scan_index = CROS_EC_ACCEL_ROTATE_AXIS(_axis), \ 197 } \ 198 199static const struct iio_chan_spec cros_ec_accel_legacy_channels[] = { 200 CROS_EC_ACCEL_LEGACY_CHAN(CROS_EC_SENSOR_X), 201 CROS_EC_ACCEL_LEGACY_CHAN(CROS_EC_SENSOR_Y), 202 CROS_EC_ACCEL_LEGACY_CHAN(CROS_EC_SENSOR_Z), 203 IIO_CHAN_SOFT_TIMESTAMP(CROS_EC_SENSOR_MAX_AXIS) 204}; 205 206static int cros_ec_accel_legacy_probe(struct platform_device *pdev) 207{ 208 struct device *dev = &pdev->dev; 209 struct iio_dev *indio_dev; 210 struct cros_ec_sensors_core_state *state; 211 int ret; 212 213 indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state)); 214 if (!indio_dev) 215 return -ENOMEM; 216 217 ret = cros_ec_sensors_core_init(pdev, indio_dev, true, 218 cros_ec_sensors_capture, NULL); 219 if (ret) 220 return ret; 221 222 indio_dev->info = &cros_ec_accel_legacy_info; 223 state = iio_priv(indio_dev); 224 225 if (state->ec->cmd_readmem != NULL) 226 state->read_ec_sensors_data = cros_ec_sensors_read_lpc; 227 else 228 state->read_ec_sensors_data = cros_ec_accel_legacy_read_cmd; 229 230 indio_dev->channels = cros_ec_accel_legacy_channels; 231 indio_dev->num_channels = ARRAY_SIZE(cros_ec_accel_legacy_channels); 232 /* The lid sensor needs to be presented inverted. */ 233 if (state->loc == MOTIONSENSE_LOC_LID) { 234 state->sign[CROS_EC_SENSOR_X] = -1; 235 state->sign[CROS_EC_SENSOR_Z] = -1; 236 } 237 238 return devm_iio_device_register(dev, indio_dev); 239} 240 241static struct platform_driver cros_ec_accel_platform_driver = { 242 .driver = { 243 .name = DRV_NAME, 244 }, 245 .probe = cros_ec_accel_legacy_probe, 246}; 247module_platform_driver(cros_ec_accel_platform_driver); 248 249MODULE_DESCRIPTION("ChromeOS EC legacy accelerometer driver"); 250MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>"); 251MODULE_LICENSE("GPL v2"); 252MODULE_ALIAS("platform:" DRV_NAME);