linux/drivers/iio/accel/adxl345_core.c
<<
>>
Prefs
   1/*
   2 * ADXL345 3-Axis Digital Accelerometer IIO core driver
   3 *
   4 * Copyright (c) 2017 Eva Rachel Retuya <eraretuya@gmail.com>
   5 *
   6 * This file is subject to the terms and conditions of version 2 of
   7 * the GNU General Public License. See the file COPYING in the main
   8 * directory of this archive for more details.
   9 */
  10
  11#include <linux/module.h>
  12#include <linux/regmap.h>
  13
  14#include <linux/iio/iio.h>
  15
  16#include "adxl345.h"
  17
  18#define ADXL345_REG_DEVID               0x00
  19#define ADXL345_REG_POWER_CTL           0x2D
  20#define ADXL345_REG_DATA_FORMAT         0x31
  21#define ADXL345_REG_DATAX0              0x32
  22#define ADXL345_REG_DATAY0              0x34
  23#define ADXL345_REG_DATAZ0              0x36
  24
  25#define ADXL345_POWER_CTL_MEASURE       BIT(3)
  26#define ADXL345_POWER_CTL_STANDBY       0x00
  27
  28#define ADXL345_DATA_FORMAT_FULL_RES    BIT(3) /* Up to 13-bits resolution */
  29#define ADXL345_DATA_FORMAT_2G          0
  30#define ADXL345_DATA_FORMAT_4G          1
  31#define ADXL345_DATA_FORMAT_8G          2
  32#define ADXL345_DATA_FORMAT_16G         3
  33
  34#define ADXL345_DEVID                   0xE5
  35
  36/*
  37 * In full-resolution mode, scale factor is maintained at ~4 mg/LSB
  38 * in all g ranges.
  39 *
  40 * At +/- 16g with 13-bit resolution, scale is computed as:
  41 * (16 + 16) * 9.81 / (2^13 - 1) = 0.0383
  42 */
  43static const int adxl345_uscale = 38300;
  44
  45struct adxl345_data {
  46        struct regmap *regmap;
  47        u8 data_range;
  48};
  49
  50#define ADXL345_CHANNEL(reg, axis) {                                    \
  51        .type = IIO_ACCEL,                                              \
  52        .modified = 1,                                                  \
  53        .channel2 = IIO_MOD_##axis,                                     \
  54        .address = reg,                                                 \
  55        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),                   \
  56        .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),           \
  57}
  58
  59static const struct iio_chan_spec adxl345_channels[] = {
  60        ADXL345_CHANNEL(ADXL345_REG_DATAX0, X),
  61        ADXL345_CHANNEL(ADXL345_REG_DATAY0, Y),
  62        ADXL345_CHANNEL(ADXL345_REG_DATAZ0, Z),
  63};
  64
  65static int adxl345_read_raw(struct iio_dev *indio_dev,
  66                            struct iio_chan_spec const *chan,
  67                            int *val, int *val2, long mask)
  68{
  69        struct adxl345_data *data = iio_priv(indio_dev);
  70        __le16 regval;
  71        int ret;
  72
  73        switch (mask) {
  74        case IIO_CHAN_INFO_RAW:
  75                /*
  76                 * Data is stored in adjacent registers:
  77                 * ADXL345_REG_DATA(X0/Y0/Z0) contain the least significant byte
  78                 * and ADXL345_REG_DATA(X0/Y0/Z0) + 1 the most significant byte
  79                 */
  80                ret = regmap_bulk_read(data->regmap, chan->address, &regval,
  81                                       sizeof(regval));
  82                if (ret < 0)
  83                        return ret;
  84
  85                *val = sign_extend32(le16_to_cpu(regval), 12);
  86                return IIO_VAL_INT;
  87        case IIO_CHAN_INFO_SCALE:
  88                *val = 0;
  89                *val2 = adxl345_uscale;
  90
  91                return IIO_VAL_INT_PLUS_MICRO;
  92        }
  93
  94        return -EINVAL;
  95}
  96
  97static const struct iio_info adxl345_info = {
  98        .read_raw       = adxl345_read_raw,
  99};
 100
 101int adxl345_core_probe(struct device *dev, struct regmap *regmap,
 102                       const char *name)
 103{
 104        struct adxl345_data *data;
 105        struct iio_dev *indio_dev;
 106        u32 regval;
 107        int ret;
 108
 109        ret = regmap_read(regmap, ADXL345_REG_DEVID, &regval);
 110        if (ret < 0) {
 111                dev_err(dev, "Error reading device ID: %d\n", ret);
 112                return ret;
 113        }
 114
 115        if (regval != ADXL345_DEVID) {
 116                dev_err(dev, "Invalid device ID: %x, expected %x\n",
 117                        regval, ADXL345_DEVID);
 118                return -ENODEV;
 119        }
 120
 121        indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
 122        if (!indio_dev)
 123                return -ENOMEM;
 124
 125        data = iio_priv(indio_dev);
 126        dev_set_drvdata(dev, indio_dev);
 127        data->regmap = regmap;
 128        /* Enable full-resolution mode */
 129        data->data_range = ADXL345_DATA_FORMAT_FULL_RES;
 130
 131        ret = regmap_write(data->regmap, ADXL345_REG_DATA_FORMAT,
 132                           data->data_range);
 133        if (ret < 0) {
 134                dev_err(dev, "Failed to set data range: %d\n", ret);
 135                return ret;
 136        }
 137
 138        indio_dev->dev.parent = dev;
 139        indio_dev->name = name;
 140        indio_dev->info = &adxl345_info;
 141        indio_dev->modes = INDIO_DIRECT_MODE;
 142        indio_dev->channels = adxl345_channels;
 143        indio_dev->num_channels = ARRAY_SIZE(adxl345_channels);
 144
 145        /* Enable measurement mode */
 146        ret = regmap_write(data->regmap, ADXL345_REG_POWER_CTL,
 147                           ADXL345_POWER_CTL_MEASURE);
 148        if (ret < 0) {
 149                dev_err(dev, "Failed to enable measurement mode: %d\n", ret);
 150                return ret;
 151        }
 152
 153        ret = iio_device_register(indio_dev);
 154        if (ret < 0) {
 155                dev_err(dev, "iio_device_register failed: %d\n", ret);
 156                regmap_write(data->regmap, ADXL345_REG_POWER_CTL,
 157                             ADXL345_POWER_CTL_STANDBY);
 158        }
 159
 160        return ret;
 161}
 162EXPORT_SYMBOL_GPL(adxl345_core_probe);
 163
 164int adxl345_core_remove(struct device *dev)
 165{
 166        struct iio_dev *indio_dev = dev_get_drvdata(dev);
 167        struct adxl345_data *data = iio_priv(indio_dev);
 168
 169        iio_device_unregister(indio_dev);
 170
 171        return regmap_write(data->regmap, ADXL345_REG_POWER_CTL,
 172                            ADXL345_POWER_CTL_STANDBY);
 173}
 174EXPORT_SYMBOL_GPL(adxl345_core_remove);
 175
 176MODULE_AUTHOR("Eva Rachel Retuya <eraretuya@gmail.com>");
 177MODULE_DESCRIPTION("ADXL345 3-Axis Digital Accelerometer core driver");
 178MODULE_LICENSE("GPL v2");
 179