linux/drivers/iio/accel/da280.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * IIO driver for the MiraMEMS DA280 3-axis accelerometer and
   4 * IIO driver for the MiraMEMS DA226 2-axis accelerometer
   5 *
   6 * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
   7 */
   8
   9#include <linux/module.h>
  10#include <linux/i2c.h>
  11#include <linux/acpi.h>
  12#include <linux/iio/iio.h>
  13#include <linux/iio/sysfs.h>
  14#include <linux/byteorder/generic.h>
  15
  16#define DA280_REG_CHIP_ID               0x01
  17#define DA280_REG_ACC_X_LSB             0x02
  18#define DA280_REG_ACC_Y_LSB             0x04
  19#define DA280_REG_ACC_Z_LSB             0x06
  20#define DA280_REG_MODE_BW               0x11
  21
  22#define DA280_CHIP_ID                   0x13
  23#define DA280_MODE_ENABLE               0x1e
  24#define DA280_MODE_DISABLE              0x9e
  25
  26enum da280_chipset { da226, da280 };
  27
  28/*
  29 * a value of + or -4096 corresponds to + or - 1G
  30 * scale = 9.81 / 4096 = 0.002395019
  31 */
  32
  33static const int da280_nscale = 2395019;
  34
  35#define DA280_CHANNEL(reg, axis) {      \
  36        .type = IIO_ACCEL,      \
  37        .address = reg, \
  38        .modified = 1,  \
  39        .channel2 = IIO_MOD_##axis,     \
  40        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),   \
  41        .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),   \
  42}
  43
  44static const struct iio_chan_spec da280_channels[] = {
  45        DA280_CHANNEL(DA280_REG_ACC_X_LSB, X),
  46        DA280_CHANNEL(DA280_REG_ACC_Y_LSB, Y),
  47        DA280_CHANNEL(DA280_REG_ACC_Z_LSB, Z),
  48};
  49
  50struct da280_data {
  51        struct i2c_client *client;
  52};
  53
  54static int da280_enable(struct i2c_client *client, bool enable)
  55{
  56        u8 data = enable ? DA280_MODE_ENABLE : DA280_MODE_DISABLE;
  57
  58        return i2c_smbus_write_byte_data(client, DA280_REG_MODE_BW, data);
  59}
  60
  61static int da280_read_raw(struct iio_dev *indio_dev,
  62                                struct iio_chan_spec const *chan,
  63                                int *val, int *val2, long mask)
  64{
  65        struct da280_data *data = iio_priv(indio_dev);
  66        int ret;
  67
  68        switch (mask) {
  69        case IIO_CHAN_INFO_RAW:
  70                ret = i2c_smbus_read_word_data(data->client, chan->address);
  71                if (ret < 0)
  72                        return ret;
  73                /*
  74                 * Values are 14 bits, stored as 16 bits with the 2
  75                 * least significant bits always 0.
  76                 */
  77                *val = (short)ret >> 2;
  78                return IIO_VAL_INT;
  79        case IIO_CHAN_INFO_SCALE:
  80                *val = 0;
  81                *val2 = da280_nscale;
  82                return IIO_VAL_INT_PLUS_NANO;
  83        default:
  84                return -EINVAL;
  85        }
  86}
  87
  88static const struct iio_info da280_info = {
  89        .read_raw       = da280_read_raw,
  90};
  91
  92static enum da280_chipset da280_match_acpi_device(struct device *dev)
  93{
  94        const struct acpi_device_id *id;
  95
  96        id = acpi_match_device(dev->driver->acpi_match_table, dev);
  97        if (!id)
  98                return -EINVAL;
  99
 100        return (enum da280_chipset) id->driver_data;
 101}
 102
 103static void da280_disable(void *client)
 104{
 105        da280_enable(client, false);
 106}
 107
 108static int da280_probe(struct i2c_client *client,
 109                        const struct i2c_device_id *id)
 110{
 111        int ret;
 112        struct iio_dev *indio_dev;
 113        struct da280_data *data;
 114        enum da280_chipset chip;
 115
 116        ret = i2c_smbus_read_byte_data(client, DA280_REG_CHIP_ID);
 117        if (ret != DA280_CHIP_ID)
 118                return (ret < 0) ? ret : -ENODEV;
 119
 120        indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
 121        if (!indio_dev)
 122                return -ENOMEM;
 123
 124        data = iio_priv(indio_dev);
 125        data->client = client;
 126
 127        indio_dev->info = &da280_info;
 128        indio_dev->modes = INDIO_DIRECT_MODE;
 129        indio_dev->channels = da280_channels;
 130
 131        if (ACPI_HANDLE(&client->dev)) {
 132                chip = da280_match_acpi_device(&client->dev);
 133        } else {
 134                chip = id->driver_data;
 135        }
 136
 137        if (chip == da226) {
 138                indio_dev->name = "da226";
 139                indio_dev->num_channels = 2;
 140        } else {
 141                indio_dev->name = "da280";
 142                indio_dev->num_channels = 3;
 143        }
 144
 145        ret = da280_enable(client, true);
 146        if (ret < 0)
 147                return ret;
 148
 149        ret = devm_add_action_or_reset(&client->dev, da280_disable, client);
 150        if (ret)
 151                return ret;
 152
 153        return devm_iio_device_register(&client->dev, indio_dev);
 154}
 155
 156#ifdef CONFIG_PM_SLEEP
 157static int da280_suspend(struct device *dev)
 158{
 159        return da280_enable(to_i2c_client(dev), false);
 160}
 161
 162static int da280_resume(struct device *dev)
 163{
 164        return da280_enable(to_i2c_client(dev), true);
 165}
 166#endif
 167
 168static SIMPLE_DEV_PM_OPS(da280_pm_ops, da280_suspend, da280_resume);
 169
 170static const struct acpi_device_id da280_acpi_match[] = {
 171        {"MIRAACC", da280},
 172        {},
 173};
 174MODULE_DEVICE_TABLE(acpi, da280_acpi_match);
 175
 176static const struct i2c_device_id da280_i2c_id[] = {
 177        { "da226", da226 },
 178        { "da280", da280 },
 179        {}
 180};
 181MODULE_DEVICE_TABLE(i2c, da280_i2c_id);
 182
 183static struct i2c_driver da280_driver = {
 184        .driver = {
 185                .name = "da280",
 186                .acpi_match_table = ACPI_PTR(da280_acpi_match),
 187                .pm = &da280_pm_ops,
 188        },
 189        .probe          = da280_probe,
 190        .id_table       = da280_i2c_id,
 191};
 192
 193module_i2c_driver(da280_driver);
 194
 195MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
 196MODULE_DESCRIPTION("MiraMEMS DA280 3-Axis Accelerometer driver");
 197MODULE_LICENSE("GPL v2");
 198