linux/drivers/iio/accel/cros_ec_accel_legacy.c
<<
>>
Prefs
   1/*
   2 * Driver for older Chrome OS EC accelerometer
   3 *
   4 * Copyright 2017 Google, Inc
   5 *
   6 * This software is licensed under the terms of the GNU General Public
   7 * License version 2, as published by the Free Software Foundation, and
   8 * may be copied, distributed, and modified under those terms.
   9 *
  10 * This program is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU General Public License for more details.
  14 *
  15 * This driver uses the memory mapper cros-ec interface to communicate
  16 * with the Chrome OS EC about accelerometer data.
  17 * Accelerometer access is presented through iio sysfs.
  18 */
  19
  20#include <linux/delay.h>
  21#include <linux/device.h>
  22#include <linux/iio/buffer.h>
  23#include <linux/iio/iio.h>
  24#include <linux/iio/kfifo_buf.h>
  25#include <linux/iio/trigger_consumer.h>
  26#include <linux/iio/triggered_buffer.h>
  27#include <linux/kernel.h>
  28#include <linux/mfd/cros_ec.h>
  29#include <linux/mfd/cros_ec_commands.h>
  30#include <linux/module.h>
  31#include <linux/slab.h>
  32#include <linux/sysfs.h>
  33#include <linux/platform_device.h>
  34
  35#define DRV_NAME        "cros-ec-accel-legacy"
  36
  37/*
  38 * Sensor scale hard coded at 10 bits per g, computed as:
  39 * g / (2^10 - 1) = 0.009586168; with g = 9.80665 m.s^-2
  40 */
  41#define ACCEL_LEGACY_NSCALE 9586168
  42
  43/* Indices for EC sensor values. */
  44enum {
  45        X,
  46        Y,
  47        Z,
  48        MAX_AXIS,
  49};
  50
  51/* State data for cros_ec_accel_legacy iio driver. */
  52struct cros_ec_accel_legacy_state {
  53        struct cros_ec_device *ec;
  54
  55        /*
  56         * Array holding data from a single capture. 2 bytes per channel
  57         * for the 3 channels plus the timestamp which is always last and
  58         * 8-bytes aligned.
  59         */
  60        s16 capture_data[8];
  61        s8 sign[MAX_AXIS];
  62        u8 sensor_num;
  63};
  64
  65static int ec_cmd_read_u8(struct cros_ec_device *ec, unsigned int offset,
  66                          u8 *dest)
  67{
  68        return ec->cmd_readmem(ec, offset, 1, dest);
  69}
  70
  71static int ec_cmd_read_u16(struct cros_ec_device *ec, unsigned int offset,
  72                           u16 *dest)
  73{
  74        __le16 tmp;
  75        int ret = ec->cmd_readmem(ec, offset, 2, &tmp);
  76
  77        *dest = le16_to_cpu(tmp);
  78
  79        return ret;
  80}
  81
  82/**
  83 * read_ec_until_not_busy() - Read from EC status byte until it reads not busy.
  84 * @st: Pointer to state information for device.
  85 *
  86 * This function reads EC status until its busy bit gets cleared. It does not
  87 * wait indefinitely and returns -EIO if the EC status is still busy after a
  88 * few hundreds milliseconds.
  89 *
  90 * Return: 8-bit status if ok, -EIO on error
  91 */
  92static int read_ec_until_not_busy(struct cros_ec_accel_legacy_state *st)
  93{
  94        struct cros_ec_device *ec = st->ec;
  95        u8 status;
  96        int attempts = 0;
  97
  98        ec_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status);
  99        while (status & EC_MEMMAP_ACC_STATUS_BUSY_BIT) {
 100                /* Give up after enough attempts, return error. */
 101                if (attempts++ >= 50)
 102                        return -EIO;
 103
 104                /* Small delay every so often. */
 105                if (attempts % 5 == 0)
 106                        msleep(25);
 107
 108                ec_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status);
 109        }
 110
 111        return status;
 112}
 113
 114/**
 115 * read_ec_accel_data_unsafe() - Read acceleration data from EC shared memory.
 116 * @st:        Pointer to state information for device.
 117 * @scan_mask: Bitmap of the sensor indices to scan.
 118 * @data:      Location to store data.
 119 *
 120 * This is the unsafe function for reading the EC data. It does not guarantee
 121 * that the EC will not modify the data as it is being read in.
 122 */
 123static void read_ec_accel_data_unsafe(struct cros_ec_accel_legacy_state *st,
 124                                      unsigned long scan_mask, s16 *data)
 125{
 126        int i = 0;
 127        int num_enabled = bitmap_weight(&scan_mask, MAX_AXIS);
 128
 129        /* Read all sensors enabled in scan_mask. Each value is 2 bytes. */
 130        while (num_enabled--) {
 131                i = find_next_bit(&scan_mask, MAX_AXIS, i);
 132                ec_cmd_read_u16(st->ec,
 133                                EC_MEMMAP_ACC_DATA +
 134                                sizeof(s16) *
 135                                (1 + i + st->sensor_num * MAX_AXIS),
 136                                data);
 137                *data *= st->sign[i];
 138                i++;
 139                data++;
 140        }
 141}
 142
 143/**
 144 * read_ec_accel_data() - Read acceleration data from EC shared memory.
 145 * @st:        Pointer to state information for device.
 146 * @scan_mask: Bitmap of the sensor indices to scan.
 147 * @data:      Location to store data.
 148 *
 149 * This is the safe function for reading the EC data. It guarantees that
 150 * the data sampled was not modified by the EC while being read.
 151 *
 152 * Return: 0 if ok, -ve on error
 153 */
 154static int read_ec_accel_data(struct cros_ec_accel_legacy_state *st,
 155                              unsigned long scan_mask, s16 *data)
 156{
 157        u8 samp_id = 0xff;
 158        u8 status = 0;
 159        int ret;
 160        int attempts = 0;
 161
 162        /*
 163         * Continually read all data from EC until the status byte after
 164         * all reads reflects that the EC is not busy and the sample id
 165         * matches the sample id from before all reads. This guarantees
 166         * that data read in was not modified by the EC while reading.
 167         */
 168        while ((status & (EC_MEMMAP_ACC_STATUS_BUSY_BIT |
 169                          EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK)) != samp_id) {
 170                /* If we have tried to read too many times, return error. */
 171                if (attempts++ >= 5)
 172                        return -EIO;
 173
 174                /* Read status byte until EC is not busy. */
 175                ret = read_ec_until_not_busy(st);
 176                if (ret < 0)
 177                        return ret;
 178                status = ret;
 179
 180                /*
 181                 * Store the current sample id so that we can compare to the
 182                 * sample id after reading the data.
 183                 */
 184                samp_id = status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK;
 185
 186                /* Read all EC data, format it, and store it into data. */
 187                read_ec_accel_data_unsafe(st, scan_mask, data);
 188
 189                /* Read status byte. */
 190                ec_cmd_read_u8(st->ec, EC_MEMMAP_ACC_STATUS, &status);
 191        }
 192
 193        return 0;
 194}
 195
 196static int cros_ec_accel_legacy_read(struct iio_dev *indio_dev,
 197                                     struct iio_chan_spec const *chan,
 198                                     int *val, int *val2, long mask)
 199{
 200        struct cros_ec_accel_legacy_state *st = iio_priv(indio_dev);
 201        s16 data = 0;
 202        int ret = IIO_VAL_INT;
 203
 204        switch (mask) {
 205        case IIO_CHAN_INFO_RAW:
 206                ret = read_ec_accel_data(st, (1 << chan->scan_index), &data);
 207                if (ret)
 208                        return ret;
 209                *val = data;
 210                return IIO_VAL_INT;
 211        case IIO_CHAN_INFO_SCALE:
 212                *val = 0;
 213                *val2 = ACCEL_LEGACY_NSCALE;
 214                return IIO_VAL_INT_PLUS_NANO;
 215        case IIO_CHAN_INFO_CALIBBIAS:
 216                /* Calibration not supported. */
 217                *val = 0;
 218                return IIO_VAL_INT;
 219        default:
 220                return -EINVAL;
 221        }
 222}
 223
 224static int cros_ec_accel_legacy_write(struct iio_dev *indio_dev,
 225                                      struct iio_chan_spec const *chan,
 226                                      int val, int val2, long mask)
 227{
 228        /*
 229         * Do nothing but don't return an error code to allow calibration
 230         * script to work.
 231         */
 232        if (mask == IIO_CHAN_INFO_CALIBBIAS)
 233                return 0;
 234
 235        return -EINVAL;
 236}
 237
 238static const struct iio_info cros_ec_accel_legacy_info = {
 239        .read_raw = &cros_ec_accel_legacy_read,
 240        .write_raw = &cros_ec_accel_legacy_write,
 241};
 242
 243/**
 244 * cros_ec_accel_legacy_capture() - The trigger handler function
 245 * @irq: The interrupt number.
 246 * @p:   Private data - always a pointer to the poll func.
 247 *
 248 * On a trigger event occurring, if the pollfunc is attached then this
 249 * handler is called as a threaded interrupt (and hence may sleep). It
 250 * is responsible for grabbing data from the device and pushing it into
 251 * the associated buffer.
 252 *
 253 * Return: IRQ_HANDLED
 254 */
 255static irqreturn_t cros_ec_accel_legacy_capture(int irq, void *p)
 256{
 257        struct iio_poll_func *pf = p;
 258        struct iio_dev *indio_dev = pf->indio_dev;
 259        struct cros_ec_accel_legacy_state *st = iio_priv(indio_dev);
 260
 261        /* Clear capture data. */
 262        memset(st->capture_data, 0, sizeof(st->capture_data));
 263
 264        /*
 265         * Read data based on which channels are enabled in scan mask. Note
 266         * that on a capture we are always reading the calibrated data.
 267         */
 268        read_ec_accel_data(st, *indio_dev->active_scan_mask, st->capture_data);
 269
 270        iio_push_to_buffers_with_timestamp(indio_dev, (void *)st->capture_data,
 271                                           iio_get_time_ns(indio_dev));
 272
 273        /*
 274         * Tell the core we are done with this trigger and ready for the
 275         * next one.
 276         */
 277        iio_trigger_notify_done(indio_dev->trig);
 278
 279        return IRQ_HANDLED;
 280}
 281
 282static char *cros_ec_accel_legacy_loc_strings[] = {
 283        [MOTIONSENSE_LOC_BASE] = "base",
 284        [MOTIONSENSE_LOC_LID] = "lid",
 285        [MOTIONSENSE_LOC_MAX] = "unknown",
 286};
 287
 288static ssize_t cros_ec_accel_legacy_loc(struct iio_dev *indio_dev,
 289                                        uintptr_t private,
 290                                        const struct iio_chan_spec *chan,
 291                                        char *buf)
 292{
 293        struct cros_ec_accel_legacy_state *st = iio_priv(indio_dev);
 294
 295        return sprintf(buf, "%s\n",
 296                       cros_ec_accel_legacy_loc_strings[st->sensor_num +
 297                                                        MOTIONSENSE_LOC_BASE]);
 298}
 299
 300static ssize_t cros_ec_accel_legacy_id(struct iio_dev *indio_dev,
 301                                       uintptr_t private,
 302                                       const struct iio_chan_spec *chan,
 303                                       char *buf)
 304{
 305        struct cros_ec_accel_legacy_state *st = iio_priv(indio_dev);
 306
 307        return sprintf(buf, "%d\n", st->sensor_num);
 308}
 309
 310static const struct iio_chan_spec_ext_info cros_ec_accel_legacy_ext_info[] = {
 311        {
 312                .name = "id",
 313                .shared = IIO_SHARED_BY_ALL,
 314                .read = cros_ec_accel_legacy_id,
 315        },
 316        {
 317                .name = "location",
 318                .shared = IIO_SHARED_BY_ALL,
 319                .read = cros_ec_accel_legacy_loc,
 320        },
 321        { }
 322};
 323
 324#define CROS_EC_ACCEL_LEGACY_CHAN(_axis)                                \
 325        {                                                               \
 326                .type = IIO_ACCEL,                                      \
 327                .channel2 = IIO_MOD_X + (_axis),                        \
 328                .modified = 1,                                          \
 329                .info_mask_separate =                                   \
 330                        BIT(IIO_CHAN_INFO_RAW) |                        \
 331                        BIT(IIO_CHAN_INFO_SCALE) |                      \
 332                        BIT(IIO_CHAN_INFO_CALIBBIAS),                   \
 333                .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE),    \
 334                .ext_info = cros_ec_accel_legacy_ext_info,              \
 335                .scan_type = {                                          \
 336                        .sign = 's',                                    \
 337                        .realbits = 16,                                 \
 338                        .storagebits = 16,                              \
 339                },                                                      \
 340        }                                                               \
 341
 342static struct iio_chan_spec ec_accel_channels[] = {
 343        CROS_EC_ACCEL_LEGACY_CHAN(X),
 344        CROS_EC_ACCEL_LEGACY_CHAN(Y),
 345        CROS_EC_ACCEL_LEGACY_CHAN(Z),
 346        IIO_CHAN_SOFT_TIMESTAMP(MAX_AXIS)
 347};
 348
 349static int cros_ec_accel_legacy_probe(struct platform_device *pdev)
 350{
 351        struct device *dev = &pdev->dev;
 352        struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
 353        struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
 354        struct iio_dev *indio_dev;
 355        struct cros_ec_accel_legacy_state *state;
 356        int ret, i;
 357
 358        if (!ec || !ec->ec_dev) {
 359                dev_warn(&pdev->dev, "No EC device found.\n");
 360                return -EINVAL;
 361        }
 362
 363        if (!ec->ec_dev->cmd_readmem) {
 364                dev_warn(&pdev->dev, "EC does not support direct reads.\n");
 365                return -EINVAL;
 366        }
 367
 368        indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
 369        if (!indio_dev)
 370                return -ENOMEM;
 371
 372        platform_set_drvdata(pdev, indio_dev);
 373        state = iio_priv(indio_dev);
 374        state->ec = ec->ec_dev;
 375        state->sensor_num = sensor_platform->sensor_num;
 376
 377        indio_dev->dev.parent = dev;
 378        indio_dev->name = pdev->name;
 379        indio_dev->channels = ec_accel_channels;
 380        /*
 381         * Present the channel using HTML5 standard:
 382         * need to invert X and Y and invert some lid axis.
 383         */
 384        for (i = X ; i < MAX_AXIS; i++) {
 385                switch (i) {
 386                case X:
 387                        ec_accel_channels[X].scan_index = Y;
 388                case Y:
 389                        ec_accel_channels[Y].scan_index = X;
 390                case Z:
 391                        ec_accel_channels[Z].scan_index = Z;
 392                }
 393                if (state->sensor_num == MOTIONSENSE_LOC_LID && i != Y)
 394                        state->sign[i] = -1;
 395                else
 396                        state->sign[i] = 1;
 397        }
 398        indio_dev->num_channels = ARRAY_SIZE(ec_accel_channels);
 399        indio_dev->dev.parent = &pdev->dev;
 400        indio_dev->info = &cros_ec_accel_legacy_info;
 401        indio_dev->modes = INDIO_DIRECT_MODE;
 402
 403        ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
 404                                              cros_ec_accel_legacy_capture,
 405                                              NULL);
 406        if (ret)
 407                return ret;
 408
 409        return devm_iio_device_register(dev, indio_dev);
 410}
 411
 412static struct platform_driver cros_ec_accel_platform_driver = {
 413        .driver = {
 414                .name   = DRV_NAME,
 415        },
 416        .probe          = cros_ec_accel_legacy_probe,
 417};
 418module_platform_driver(cros_ec_accel_platform_driver);
 419
 420MODULE_DESCRIPTION("ChromeOS EC legacy accelerometer driver");
 421MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>");
 422MODULE_LICENSE("GPL");
 423MODULE_ALIAS("platform:" DRV_NAME);
 424