linux/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * cros_ec_sensors - Driver for Chrome OS Embedded Controller sensors.
   4 *
   5 * Copyright (C) 2016 Google, Inc
   6 *
   7 * This driver uses the cros-ec interface to communicate with the Chrome OS
   8 * EC about sensors data. Data access is presented through iio sysfs.
   9 */
  10
  11#include <linux/device.h>
  12#include <linux/iio/buffer.h>
  13#include <linux/iio/common/cros_ec_sensors_core.h>
  14#include <linux/iio/iio.h>
  15#include <linux/iio/kfifo_buf.h>
  16#include <linux/iio/trigger_consumer.h>
  17#include <linux/iio/triggered_buffer.h>
  18#include <linux/kernel.h>
  19#include <linux/mfd/cros_ec.h>
  20#include <linux/mfd/cros_ec_commands.h>
  21#include <linux/module.h>
  22#include <linux/platform_device.h>
  23#include <linux/slab.h>
  24
  25#define CROS_EC_SENSORS_MAX_CHANNELS 4
  26
  27/* State data for ec_sensors iio driver. */
  28struct cros_ec_sensors_state {
  29        /* Shared by all sensors */
  30        struct cros_ec_sensors_core_state core;
  31
  32        struct iio_chan_spec channels[CROS_EC_SENSORS_MAX_CHANNELS];
  33};
  34
  35static int cros_ec_sensors_read(struct iio_dev *indio_dev,
  36                          struct iio_chan_spec const *chan,
  37                          int *val, int *val2, long mask)
  38{
  39        struct cros_ec_sensors_state *st = iio_priv(indio_dev);
  40        s16 data = 0;
  41        s64 val64;
  42        int i;
  43        int ret;
  44        int idx = chan->scan_index;
  45
  46        mutex_lock(&st->core.cmd_lock);
  47
  48        switch (mask) {
  49        case IIO_CHAN_INFO_RAW:
  50                ret = st->core.read_ec_sensors_data(indio_dev, 1 << idx, &data);
  51                if (ret < 0)
  52                        break;
  53                ret = IIO_VAL_INT;
  54                *val = data;
  55                break;
  56        case IIO_CHAN_INFO_CALIBBIAS:
  57                st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
  58                st->core.param.sensor_offset.flags = 0;
  59
  60                ret = cros_ec_motion_send_host_cmd(&st->core, 0);
  61                if (ret < 0)
  62                        break;
  63
  64                /* Save values */
  65                for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
  66                        st->core.calib[i] =
  67                                st->core.resp->sensor_offset.offset[i];
  68                ret = IIO_VAL_INT;
  69                *val = st->core.calib[idx];
  70                break;
  71        case IIO_CHAN_INFO_SCALE:
  72                st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
  73                st->core.param.sensor_range.data = EC_MOTION_SENSE_NO_VALUE;
  74
  75                ret = cros_ec_motion_send_host_cmd(&st->core, 0);
  76                if (ret < 0)
  77                        break;
  78
  79                val64 = st->core.resp->sensor_range.ret;
  80                switch (st->core.type) {
  81                case MOTIONSENSE_TYPE_ACCEL:
  82                        /*
  83                         * EC returns data in g, iio exepects m/s^2.
  84                         * Do not use IIO_G_TO_M_S_2 to avoid precision loss.
  85                         */
  86                        *val = div_s64(val64 * 980665, 10);
  87                        *val2 = 10000 << (CROS_EC_SENSOR_BITS - 1);
  88                        ret = IIO_VAL_FRACTIONAL;
  89                        break;
  90                case MOTIONSENSE_TYPE_GYRO:
  91                        /*
  92                         * EC returns data in dps, iio expects rad/s.
  93                         * Do not use IIO_DEGREE_TO_RAD to avoid precision
  94                         * loss. Round to the nearest integer.
  95                         */
  96                        *val = 0;
  97                        *val2 = div_s64(val64 * 3141592653ULL,
  98                                        180 << (CROS_EC_SENSOR_BITS - 1));
  99                        ret = IIO_VAL_INT_PLUS_NANO;
 100                        break;
 101                case MOTIONSENSE_TYPE_MAG:
 102                        /*
 103                         * EC returns data in 16LSB / uT,
 104                         * iio expects Gauss
 105                         */
 106                        *val = val64;
 107                        *val2 = 100 << (CROS_EC_SENSOR_BITS - 1);
 108                        ret = IIO_VAL_FRACTIONAL;
 109                        break;
 110                default:
 111                        ret = -EINVAL;
 112                }
 113                break;
 114        default:
 115                ret = cros_ec_sensors_core_read(&st->core, chan, val, val2,
 116                                                mask);
 117                break;
 118        }
 119        mutex_unlock(&st->core.cmd_lock);
 120
 121        return ret;
 122}
 123
 124static int cros_ec_sensors_write(struct iio_dev *indio_dev,
 125                               struct iio_chan_spec const *chan,
 126                               int val, int val2, long mask)
 127{
 128        struct cros_ec_sensors_state *st = iio_priv(indio_dev);
 129        int i;
 130        int ret;
 131        int idx = chan->scan_index;
 132
 133        mutex_lock(&st->core.cmd_lock);
 134
 135        switch (mask) {
 136        case IIO_CHAN_INFO_CALIBBIAS:
 137                st->core.calib[idx] = val;
 138
 139                /* Send to EC for each axis, even if not complete */
 140                st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
 141                st->core.param.sensor_offset.flags =
 142                        MOTION_SENSE_SET_OFFSET;
 143                for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
 144                        st->core.param.sensor_offset.offset[i] =
 145                                st->core.calib[i];
 146                st->core.param.sensor_offset.temp =
 147                        EC_MOTION_SENSE_INVALID_CALIB_TEMP;
 148
 149                ret = cros_ec_motion_send_host_cmd(&st->core, 0);
 150                break;
 151        case IIO_CHAN_INFO_SCALE:
 152                if (st->core.type == MOTIONSENSE_TYPE_MAG) {
 153                        ret = -EINVAL;
 154                        break;
 155                }
 156                st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
 157                st->core.param.sensor_range.data = val;
 158
 159                /* Always roundup, so caller gets at least what it asks for. */
 160                st->core.param.sensor_range.roundup = 1;
 161
 162                ret = cros_ec_motion_send_host_cmd(&st->core, 0);
 163                break;
 164        default:
 165                ret = cros_ec_sensors_core_write(
 166                                &st->core, chan, val, val2, mask);
 167                break;
 168        }
 169
 170        mutex_unlock(&st->core.cmd_lock);
 171
 172        return ret;
 173}
 174
 175static const struct iio_info ec_sensors_info = {
 176        .read_raw = &cros_ec_sensors_read,
 177        .write_raw = &cros_ec_sensors_write,
 178};
 179
 180static int cros_ec_sensors_probe(struct platform_device *pdev)
 181{
 182        struct device *dev = &pdev->dev;
 183        struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
 184        struct iio_dev *indio_dev;
 185        struct cros_ec_sensors_state *state;
 186        struct iio_chan_spec *channel;
 187        int ret, i;
 188
 189        if (!ec_dev || !ec_dev->ec_dev) {
 190                dev_warn(&pdev->dev, "No CROS EC device found.\n");
 191                return -EINVAL;
 192        }
 193
 194        indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
 195        if (!indio_dev)
 196                return -ENOMEM;
 197
 198        ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
 199        if (ret)
 200                return ret;
 201
 202        indio_dev->info = &ec_sensors_info;
 203        state = iio_priv(indio_dev);
 204        for (channel = state->channels, i = CROS_EC_SENSOR_X;
 205             i < CROS_EC_SENSOR_MAX_AXIS; i++, channel++) {
 206                /* Common part */
 207                channel->info_mask_separate =
 208                        BIT(IIO_CHAN_INFO_RAW) |
 209                        BIT(IIO_CHAN_INFO_CALIBBIAS);
 210                channel->info_mask_shared_by_all =
 211                        BIT(IIO_CHAN_INFO_SCALE) |
 212                        BIT(IIO_CHAN_INFO_FREQUENCY) |
 213                        BIT(IIO_CHAN_INFO_SAMP_FREQ);
 214                channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
 215                channel->scan_type.storagebits = CROS_EC_SENSOR_BITS;
 216                channel->scan_index = i;
 217                channel->ext_info = cros_ec_sensors_ext_info;
 218                channel->modified = 1;
 219                channel->channel2 = IIO_MOD_X + i;
 220                channel->scan_type.sign = 's';
 221
 222                /* Sensor specific */
 223                switch (state->core.type) {
 224                case MOTIONSENSE_TYPE_ACCEL:
 225                        channel->type = IIO_ACCEL;
 226                        break;
 227                case MOTIONSENSE_TYPE_GYRO:
 228                        channel->type = IIO_ANGL_VEL;
 229                        break;
 230                case MOTIONSENSE_TYPE_MAG:
 231                        channel->type = IIO_MAGN;
 232                        break;
 233                default:
 234                        dev_err(&pdev->dev, "Unknown motion sensor\n");
 235                        return -EINVAL;
 236                }
 237        }
 238
 239        /* Timestamp */
 240        channel->type = IIO_TIMESTAMP;
 241        channel->channel = -1;
 242        channel->scan_index = CROS_EC_SENSOR_MAX_AXIS;
 243        channel->scan_type.sign = 's';
 244        channel->scan_type.realbits = 64;
 245        channel->scan_type.storagebits = 64;
 246
 247        indio_dev->channels = state->channels;
 248        indio_dev->num_channels = CROS_EC_SENSORS_MAX_CHANNELS;
 249
 250        /* There is only enough room for accel and gyro in the io space */
 251        if ((state->core.ec->cmd_readmem != NULL) &&
 252            (state->core.type != MOTIONSENSE_TYPE_MAG))
 253                state->core.read_ec_sensors_data = cros_ec_sensors_read_lpc;
 254        else
 255                state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
 256
 257        ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
 258                        cros_ec_sensors_capture, NULL);
 259        if (ret)
 260                return ret;
 261
 262        return devm_iio_device_register(dev, indio_dev);
 263}
 264
 265static const struct platform_device_id cros_ec_sensors_ids[] = {
 266        {
 267                .name = "cros-ec-accel",
 268        },
 269        {
 270                .name = "cros-ec-gyro",
 271        },
 272        {
 273                .name = "cros-ec-mag",
 274        },
 275        { /* sentinel */ }
 276};
 277MODULE_DEVICE_TABLE(platform, cros_ec_sensors_ids);
 278
 279static struct platform_driver cros_ec_sensors_platform_driver = {
 280        .driver = {
 281                .name   = "cros-ec-sensors",
 282                .pm     = &cros_ec_sensors_pm_ops,
 283        },
 284        .probe          = cros_ec_sensors_probe,
 285        .id_table       = cros_ec_sensors_ids,
 286};
 287module_platform_driver(cros_ec_sensors_platform_driver);
 288
 289MODULE_DESCRIPTION("ChromeOS EC 3-axis sensors driver");
 290MODULE_LICENSE("GPL v2");
 291