linux/drivers/iio/gyro/adis16080.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * ADIS16080/100 Yaw Rate Gyroscope with SPI driver
   4 *
   5 * Copyright 2010 Analog Devices Inc.
   6 */
   7#include <linux/delay.h>
   8#include <linux/mutex.h>
   9#include <linux/device.h>
  10#include <linux/kernel.h>
  11#include <linux/spi/spi.h>
  12#include <linux/slab.h>
  13#include <linux/sysfs.h>
  14#include <linux/module.h>
  15
  16#include <linux/iio/iio.h>
  17#include <linux/iio/sysfs.h>
  18
  19#define ADIS16080_DIN_GYRO   (0 << 10) /* Gyroscope output */
  20#define ADIS16080_DIN_TEMP   (1 << 10) /* Temperature output */
  21#define ADIS16080_DIN_AIN1   (2 << 10)
  22#define ADIS16080_DIN_AIN2   (3 << 10)
  23
  24/*
  25 * 1: Write contents on DIN to control register.
  26 * 0: No changes to control register.
  27 */
  28
  29#define ADIS16080_DIN_WRITE  (1 << 15)
  30
  31struct adis16080_chip_info {
  32        int scale_val;
  33        int scale_val2;
  34};
  35
  36/**
  37 * struct adis16080_state - device instance specific data
  38 * @us:                 actual spi_device to write data
  39 * @info:               chip specific parameters
  40 * @buf:                transmit or receive buffer
  41 * @lock:               lock to protect buffer during reads
  42 **/
  43struct adis16080_state {
  44        struct spi_device               *us;
  45        const struct adis16080_chip_info *info;
  46        struct mutex                    lock;
  47
  48        __be16 buf ____cacheline_aligned;
  49};
  50
  51static int adis16080_read_sample(struct iio_dev *indio_dev,
  52                u16 addr, int *val)
  53{
  54        struct adis16080_state *st = iio_priv(indio_dev);
  55        int ret;
  56        struct spi_transfer     t[] = {
  57                {
  58                        .tx_buf         = &st->buf,
  59                        .len            = 2,
  60                        .cs_change      = 1,
  61                }, {
  62                        .rx_buf         = &st->buf,
  63                        .len            = 2,
  64                },
  65        };
  66
  67        st->buf = cpu_to_be16(addr | ADIS16080_DIN_WRITE);
  68
  69        ret = spi_sync_transfer(st->us, t, ARRAY_SIZE(t));
  70        if (ret == 0)
  71                *val = sign_extend32(be16_to_cpu(st->buf), 11);
  72
  73        return ret;
  74}
  75
  76static int adis16080_read_raw(struct iio_dev *indio_dev,
  77                             struct iio_chan_spec const *chan,
  78                             int *val,
  79                             int *val2,
  80                             long mask)
  81{
  82        struct adis16080_state *st = iio_priv(indio_dev);
  83        int ret;
  84
  85        switch (mask) {
  86        case IIO_CHAN_INFO_RAW:
  87                mutex_lock(&st->lock);
  88                ret = adis16080_read_sample(indio_dev, chan->address, val);
  89                mutex_unlock(&st->lock);
  90                return ret ? ret : IIO_VAL_INT;
  91        case IIO_CHAN_INFO_SCALE:
  92                switch (chan->type) {
  93                case IIO_ANGL_VEL:
  94                        *val = st->info->scale_val;
  95                        *val2 = st->info->scale_val2;
  96                        return IIO_VAL_FRACTIONAL;
  97                case IIO_VOLTAGE:
  98                        /* VREF = 5V, 12 bits */
  99                        *val = 5000;
 100                        *val2 = 12;
 101                        return IIO_VAL_FRACTIONAL_LOG2;
 102                case IIO_TEMP:
 103                        /* 85 C = 585, 25 C = 0 */
 104                        *val = 85000 - 25000;
 105                        *val2 = 585;
 106                        return IIO_VAL_FRACTIONAL;
 107                default:
 108                        return -EINVAL;
 109                }
 110        case IIO_CHAN_INFO_OFFSET:
 111                switch (chan->type) {
 112                case IIO_VOLTAGE:
 113                        /* 2.5 V = 0 */
 114                        *val = 2048;
 115                        return IIO_VAL_INT;
 116                case IIO_TEMP:
 117                        /* 85 C = 585, 25 C = 0 */
 118                        *val = DIV_ROUND_CLOSEST(25 * 585, 85 - 25);
 119                        return IIO_VAL_INT;
 120                default:
 121                        return -EINVAL;
 122                }
 123        default:
 124                break;
 125        }
 126
 127        return -EINVAL;
 128}
 129
 130static const struct iio_chan_spec adis16080_channels[] = {
 131        {
 132                .type = IIO_ANGL_VEL,
 133                .modified = 1,
 134                .channel2 = IIO_MOD_Z,
 135                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 136                        BIT(IIO_CHAN_INFO_SCALE),
 137                .address = ADIS16080_DIN_GYRO,
 138        }, {
 139                .type = IIO_VOLTAGE,
 140                .indexed = 1,
 141                .channel = 0,
 142                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 143                        BIT(IIO_CHAN_INFO_SCALE) |
 144                        BIT(IIO_CHAN_INFO_OFFSET),
 145                .address = ADIS16080_DIN_AIN1,
 146        }, {
 147                .type = IIO_VOLTAGE,
 148                .indexed = 1,
 149                .channel = 1,
 150                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 151                        BIT(IIO_CHAN_INFO_SCALE) |
 152                        BIT(IIO_CHAN_INFO_OFFSET),
 153                .address = ADIS16080_DIN_AIN2,
 154        }, {
 155                .type = IIO_TEMP,
 156                .indexed = 1,
 157                .channel = 0,
 158                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 159                        BIT(IIO_CHAN_INFO_SCALE) |
 160                        BIT(IIO_CHAN_INFO_OFFSET),
 161                .address = ADIS16080_DIN_TEMP,
 162        }
 163};
 164
 165static const struct iio_info adis16080_info = {
 166        .read_raw = &adis16080_read_raw,
 167};
 168
 169enum {
 170        ID_ADIS16080,
 171        ID_ADIS16100,
 172};
 173
 174static const struct adis16080_chip_info adis16080_chip_info[] = {
 175        [ID_ADIS16080] = {
 176                /* 80 degree = 819, 819 rad = 46925 degree */
 177                .scale_val = 80,
 178                .scale_val2 = 46925,
 179        },
 180        [ID_ADIS16100] = {
 181                /* 300 degree = 1230, 1230 rad = 70474 degree */
 182                .scale_val = 300,
 183                .scale_val2 = 70474,
 184        },
 185};
 186
 187static int adis16080_probe(struct spi_device *spi)
 188{
 189        const struct spi_device_id *id = spi_get_device_id(spi);
 190        struct adis16080_state *st;
 191        struct iio_dev *indio_dev;
 192
 193        /* setup the industrialio driver allocated elements */
 194        indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
 195        if (!indio_dev)
 196                return -ENOMEM;
 197        st = iio_priv(indio_dev);
 198        /* this is only used for removal purposes */
 199        spi_set_drvdata(spi, indio_dev);
 200
 201        mutex_init(&st->lock);
 202
 203        /* Allocate the comms buffers */
 204        st->us = spi;
 205        st->info = &adis16080_chip_info[id->driver_data];
 206
 207        indio_dev->name = spi->dev.driver->name;
 208        indio_dev->channels = adis16080_channels;
 209        indio_dev->num_channels = ARRAY_SIZE(adis16080_channels);
 210        indio_dev->info = &adis16080_info;
 211        indio_dev->modes = INDIO_DIRECT_MODE;
 212
 213        return iio_device_register(indio_dev);
 214}
 215
 216static int adis16080_remove(struct spi_device *spi)
 217{
 218        iio_device_unregister(spi_get_drvdata(spi));
 219        return 0;
 220}
 221
 222static const struct spi_device_id adis16080_ids[] = {
 223        { "adis16080", ID_ADIS16080 },
 224        { "adis16100", ID_ADIS16100 },
 225        {},
 226};
 227MODULE_DEVICE_TABLE(spi, adis16080_ids);
 228
 229static struct spi_driver adis16080_driver = {
 230        .driver = {
 231                .name = "adis16080",
 232        },
 233        .probe = adis16080_probe,
 234        .remove = adis16080_remove,
 235        .id_table = adis16080_ids,
 236};
 237module_spi_driver(adis16080_driver);
 238
 239MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
 240MODULE_DESCRIPTION("Analog Devices ADIS16080/100 Yaw Rate Gyroscope Driver");
 241MODULE_LICENSE("GPL v2");
 242