linux/drivers/iio/accel/cros_ec_accel_legacy.c
<<
>>
Prefs
   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
  37static 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
  70static 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
 110static 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
 124static 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
 156static 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
 163static 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
 208static struct platform_driver cros_ec_accel_platform_driver = {
 209        .driver = {
 210                .name   = DRV_NAME,
 211        },
 212        .probe          = cros_ec_accel_legacy_probe,
 213};
 214module_platform_driver(cros_ec_accel_platform_driver);
 215
 216MODULE_DESCRIPTION("ChromeOS EC legacy accelerometer driver");
 217MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>");
 218MODULE_LICENSE("GPL v2");
 219MODULE_ALIAS("platform:" DRV_NAME);
 220