root/drivers/iio/accel/cros_ec_accel_legacy.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cros_ec_accel_legacy_read_cmd
  2. cros_ec_accel_legacy_read
  3. cros_ec_accel_legacy_write
  4. cros_ec_accel_legacy_probe

   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/mfd/cros_ec.h>
  22 #include <linux/module.h>
  23 #include <linux/slab.h>
  24 #include <linux/platform_data/cros_ec_commands.h>
  25 #include <linux/platform_data/cros_ec_proto.h>
  26 #include <linux/platform_device.h>
  27 
  28 #define DRV_NAME        "cros-ec-accel-legacy"
  29 
  30 #define CROS_EC_SENSOR_LEGACY_NUM 2
  31 /*
  32  * Sensor scale hard coded at 10 bits per g, computed as:
  33  * g / (2^10 - 1) = 0.009586168; with g = 9.80665 m.s^-2
  34  */
  35 #define ACCEL_LEGACY_NSCALE 9586168
  36 
  37 static int cros_ec_accel_legacy_read_cmd(struct iio_dev *indio_dev,
  38                                   unsigned long scan_mask, s16 *data)
  39 {
  40         struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
  41         int ret;
  42         unsigned int i;
  43         u8 sensor_num;
  44 
  45         /*
  46          * Read all sensor data through a command.
  47          * Save sensor_num, it is assumed to stay.
  48          */
  49         sensor_num = st->param.info.sensor_num;
  50         st->param.cmd = MOTIONSENSE_CMD_DUMP;
  51         st->param.dump.max_sensor_count = CROS_EC_SENSOR_LEGACY_NUM;
  52         ret = cros_ec_motion_send_host_cmd(st,
  53                         sizeof(st->resp->dump) + CROS_EC_SENSOR_LEGACY_NUM *
  54                         sizeof(struct ec_response_motion_sensor_data));
  55         st->param.info.sensor_num = sensor_num;
  56         if (ret != 0) {
  57                 dev_warn(&indio_dev->dev, "Unable to read sensor data\n");
  58                 return ret;
  59         }
  60 
  61         for_each_set_bit(i, &scan_mask, indio_dev->masklength) {
  62                 *data = st->resp->dump.sensor[sensor_num].data[i] *
  63                         st->sign[i];
  64                 data++;
  65         }
  66 
  67         return 0;
  68 }
  69 
  70 static int cros_ec_accel_legacy_read(struct iio_dev *indio_dev,
  71                                      struct iio_chan_spec const *chan,
  72                                      int *val, int *val2, long mask)
  73 {
  74         struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
  75         s16 data = 0;
  76         int ret;
  77         int idx = chan->scan_index;
  78 
  79         mutex_lock(&st->cmd_lock);
  80 
  81         switch (mask) {
  82         case IIO_CHAN_INFO_RAW:
  83                 ret = st->read_ec_sensors_data(indio_dev, 1 << idx, &data);
  84                 if (ret < 0)
  85                         break;
  86                 ret = IIO_VAL_INT;
  87                 *val = data;
  88                 break;
  89         case IIO_CHAN_INFO_SCALE:
  90                 WARN_ON(st->type != MOTIONSENSE_TYPE_ACCEL);
  91                 *val = 0;
  92                 *val2 = ACCEL_LEGACY_NSCALE;
  93                 ret = IIO_VAL_INT_PLUS_NANO;
  94                 break;
  95         case IIO_CHAN_INFO_CALIBBIAS:
  96                 /* Calibration not supported. */
  97                 *val = 0;
  98                 ret = IIO_VAL_INT;
  99                 break;
 100         default:
 101                 ret = cros_ec_sensors_core_read(st, chan, val, val2,
 102                                 mask);
 103                 break;
 104         }
 105         mutex_unlock(&st->cmd_lock);
 106 
 107         return ret;
 108 }
 109 
 110 static int cros_ec_accel_legacy_write(struct iio_dev *indio_dev,
 111                                       struct iio_chan_spec const *chan,
 112                                       int val, int val2, long mask)
 113 {
 114         /*
 115          * Do nothing but don't return an error code to allow calibration
 116          * script to work.
 117          */
 118         if (mask == IIO_CHAN_INFO_CALIBBIAS)
 119                 return 0;
 120 
 121         return -EINVAL;
 122 }
 123 
 124 static const struct iio_info cros_ec_accel_legacy_info = {
 125         .read_raw = &cros_ec_accel_legacy_read,
 126         .write_raw = &cros_ec_accel_legacy_write,
 127 };
 128 
 129 /*
 130  * Present the channel using HTML5 standard:
 131  * need to invert X and Y and invert some lid axis.
 132  */
 133 #define CROS_EC_ACCEL_ROTATE_AXIS(_axis)                                \
 134         ((_axis) == CROS_EC_SENSOR_Z ? CROS_EC_SENSOR_Z :               \
 135          ((_axis) == CROS_EC_SENSOR_X ? CROS_EC_SENSOR_Y :              \
 136           CROS_EC_SENSOR_X))
 137 
 138 #define CROS_EC_ACCEL_LEGACY_CHAN(_axis)                                \
 139         {                                                               \
 140                 .type = IIO_ACCEL,                                      \
 141                 .channel2 = IIO_MOD_X + (_axis),                        \
 142                 .modified = 1,                                          \
 143                 .info_mask_separate =                                   \
 144                         BIT(IIO_CHAN_INFO_RAW) |                        \
 145                         BIT(IIO_CHAN_INFO_CALIBBIAS),                   \
 146                 .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE),    \
 147                 .ext_info = cros_ec_sensors_ext_info,                   \
 148                 .scan_type = {                                          \
 149                         .sign = 's',                                    \
 150                         .realbits = CROS_EC_SENSOR_BITS,                \
 151                         .storagebits = CROS_EC_SENSOR_BITS,             \
 152                 },                                                      \
 153                 .scan_index = CROS_EC_ACCEL_ROTATE_AXIS(_axis),         \
 154         }                                                               \
 155 
 156 static const struct iio_chan_spec cros_ec_accel_legacy_channels[] = {
 157                 CROS_EC_ACCEL_LEGACY_CHAN(CROS_EC_SENSOR_X),
 158                 CROS_EC_ACCEL_LEGACY_CHAN(CROS_EC_SENSOR_Y),
 159                 CROS_EC_ACCEL_LEGACY_CHAN(CROS_EC_SENSOR_Z),
 160                 IIO_CHAN_SOFT_TIMESTAMP(CROS_EC_SENSOR_MAX_AXIS)
 161 };
 162 
 163 static int cros_ec_accel_legacy_probe(struct platform_device *pdev)
 164 {
 165         struct device *dev = &pdev->dev;
 166         struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
 167         struct iio_dev *indio_dev;
 168         struct cros_ec_sensors_core_state *state;
 169         int ret;
 170 
 171         if (!ec || !ec->ec_dev) {
 172                 dev_warn(&pdev->dev, "No EC device found.\n");
 173                 return -EINVAL;
 174         }
 175 
 176         indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
 177         if (!indio_dev)
 178                 return -ENOMEM;
 179 
 180         ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
 181         if (ret)
 182                 return ret;
 183 
 184         indio_dev->info = &cros_ec_accel_legacy_info;
 185         state = iio_priv(indio_dev);
 186 
 187         if (state->ec->cmd_readmem != NULL)
 188                 state->read_ec_sensors_data = cros_ec_sensors_read_lpc;
 189         else
 190                 state->read_ec_sensors_data = cros_ec_accel_legacy_read_cmd;
 191 
 192         indio_dev->channels = cros_ec_accel_legacy_channels;
 193         indio_dev->num_channels = ARRAY_SIZE(cros_ec_accel_legacy_channels);
 194         /* The lid sensor needs to be presented inverted. */
 195         if (state->loc == MOTIONSENSE_LOC_LID) {
 196                 state->sign[CROS_EC_SENSOR_X] = -1;
 197                 state->sign[CROS_EC_SENSOR_Z] = -1;
 198         }
 199 
 200         ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
 201                         cros_ec_sensors_capture, NULL);
 202         if (ret)
 203                 return ret;
 204 
 205         return devm_iio_device_register(dev, indio_dev);
 206 }
 207 
 208 static struct platform_driver cros_ec_accel_platform_driver = {
 209         .driver = {
 210                 .name   = DRV_NAME,
 211         },
 212         .probe          = cros_ec_accel_legacy_probe,
 213 };
 214 module_platform_driver(cros_ec_accel_platform_driver);
 215 
 216 MODULE_DESCRIPTION("ChromeOS EC legacy accelerometer driver");
 217 MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>");
 218 MODULE_LICENSE("GPL v2");
 219 MODULE_ALIAS("platform:" DRV_NAME);

/* [<][>][^][v][top][bottom][index][help] */