linux/drivers/iio/accel/mc3230.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * mCube MC3230 3-Axis Accelerometer
   4 *
   5 * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
   6 *
   7 * IIO driver for mCube MC3230; 7-bit I2C address: 0x4c.
   8 */
   9
  10#include <linux/i2c.h>
  11#include <linux/module.h>
  12#include <linux/iio/iio.h>
  13#include <linux/iio/sysfs.h>
  14
  15#define MC3230_REG_XOUT                 0x00
  16#define MC3230_REG_YOUT                 0x01
  17#define MC3230_REG_ZOUT                 0x02
  18
  19#define MC3230_REG_MODE                 0x07
  20#define MC3230_MODE_OPCON_MASK          0x03
  21#define MC3230_MODE_OPCON_WAKE          0x01
  22#define MC3230_MODE_OPCON_STANDBY       0x03
  23
  24#define MC3230_REG_CHIP_ID              0x18
  25#define MC3230_CHIP_ID                  0x01
  26
  27#define MC3230_REG_PRODUCT_CODE         0x3b
  28#define MC3230_PRODUCT_CODE             0x19
  29
  30/*
  31 * The accelerometer has one measurement range:
  32 *
  33 * -1.5g - +1.5g (8-bit, signed)
  34 *
  35 * scale = (1.5 + 1.5) * 9.81 / (2^8 - 1)       = 0.115411765
  36 */
  37
  38static const int mc3230_nscale = 115411765;
  39
  40#define MC3230_CHANNEL(reg, axis) {     \
  41        .type = IIO_ACCEL,      \
  42        .address = reg, \
  43        .modified = 1,  \
  44        .channel2 = IIO_MOD_##axis,     \
  45        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),   \
  46        .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),   \
  47}
  48
  49static const struct iio_chan_spec mc3230_channels[] = {
  50        MC3230_CHANNEL(MC3230_REG_XOUT, X),
  51        MC3230_CHANNEL(MC3230_REG_YOUT, Y),
  52        MC3230_CHANNEL(MC3230_REG_ZOUT, Z),
  53};
  54
  55struct mc3230_data {
  56        struct i2c_client *client;
  57};
  58
  59static int mc3230_set_opcon(struct mc3230_data *data, int opcon)
  60{
  61        int ret;
  62        struct i2c_client *client = data->client;
  63
  64        ret = i2c_smbus_read_byte_data(client, MC3230_REG_MODE);
  65        if (ret < 0) {
  66                dev_err(&client->dev, "failed to read mode reg: %d\n", ret);
  67                return ret;
  68        }
  69
  70        ret &= ~MC3230_MODE_OPCON_MASK;
  71        ret |= opcon;
  72
  73        ret = i2c_smbus_write_byte_data(client, MC3230_REG_MODE, ret);
  74        if (ret < 0) {
  75                dev_err(&client->dev, "failed to write mode reg: %d\n", ret);
  76                return ret;
  77        }
  78
  79        return 0;
  80}
  81
  82static int mc3230_read_raw(struct iio_dev *indio_dev,
  83                                struct iio_chan_spec const *chan,
  84                                int *val, int *val2, long mask)
  85{
  86        struct mc3230_data *data = iio_priv(indio_dev);
  87        int ret;
  88
  89        switch (mask) {
  90        case IIO_CHAN_INFO_RAW:
  91                ret = i2c_smbus_read_byte_data(data->client, chan->address);
  92                if (ret < 0)
  93                        return ret;
  94                *val = sign_extend32(ret, 7);
  95                return IIO_VAL_INT;
  96        case IIO_CHAN_INFO_SCALE:
  97                *val = 0;
  98                *val2 = mc3230_nscale;
  99                return IIO_VAL_INT_PLUS_NANO;
 100        default:
 101                return -EINVAL;
 102        }
 103}
 104
 105static const struct iio_info mc3230_info = {
 106        .read_raw       = mc3230_read_raw,
 107};
 108
 109static int mc3230_probe(struct i2c_client *client,
 110                        const struct i2c_device_id *id)
 111{
 112        int ret;
 113        struct iio_dev *indio_dev;
 114        struct mc3230_data *data;
 115
 116        /* First check chip-id and product-id */
 117        ret = i2c_smbus_read_byte_data(client, MC3230_REG_CHIP_ID);
 118        if (ret != MC3230_CHIP_ID)
 119                return (ret < 0) ? ret : -ENODEV;
 120
 121        ret = i2c_smbus_read_byte_data(client, MC3230_REG_PRODUCT_CODE);
 122        if (ret != MC3230_PRODUCT_CODE)
 123                return (ret < 0) ? ret : -ENODEV;
 124
 125        indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
 126        if (!indio_dev) {
 127                dev_err(&client->dev, "iio allocation failed!\n");
 128                return -ENOMEM;
 129        }
 130
 131        data = iio_priv(indio_dev);
 132        data->client = client;
 133        i2c_set_clientdata(client, indio_dev);
 134
 135        indio_dev->info = &mc3230_info;
 136        indio_dev->name = "mc3230";
 137        indio_dev->modes = INDIO_DIRECT_MODE;
 138        indio_dev->channels = mc3230_channels;
 139        indio_dev->num_channels = ARRAY_SIZE(mc3230_channels);
 140
 141        ret = mc3230_set_opcon(data, MC3230_MODE_OPCON_WAKE);
 142        if (ret < 0)
 143                return ret;
 144
 145        ret = iio_device_register(indio_dev);
 146        if (ret < 0) {
 147                dev_err(&client->dev, "device_register failed\n");
 148                mc3230_set_opcon(data, MC3230_MODE_OPCON_STANDBY);
 149        }
 150
 151        return ret;
 152}
 153
 154static int mc3230_remove(struct i2c_client *client)
 155{
 156        struct iio_dev *indio_dev = i2c_get_clientdata(client);
 157
 158        iio_device_unregister(indio_dev);
 159
 160        return mc3230_set_opcon(iio_priv(indio_dev), MC3230_MODE_OPCON_STANDBY);
 161}
 162
 163#ifdef CONFIG_PM_SLEEP
 164static int mc3230_suspend(struct device *dev)
 165{
 166        struct mc3230_data *data;
 167
 168        data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
 169
 170        return mc3230_set_opcon(data, MC3230_MODE_OPCON_STANDBY);
 171}
 172
 173static int mc3230_resume(struct device *dev)
 174{
 175        struct mc3230_data *data;
 176
 177        data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
 178
 179        return mc3230_set_opcon(data, MC3230_MODE_OPCON_WAKE);
 180}
 181#endif
 182
 183static SIMPLE_DEV_PM_OPS(mc3230_pm_ops, mc3230_suspend, mc3230_resume);
 184
 185static const struct i2c_device_id mc3230_i2c_id[] = {
 186        {"mc3230", 0},
 187        {}
 188};
 189MODULE_DEVICE_TABLE(i2c, mc3230_i2c_id);
 190
 191static struct i2c_driver mc3230_driver = {
 192        .driver = {
 193                .name = "mc3230",
 194                .pm = &mc3230_pm_ops,
 195        },
 196        .probe          = mc3230_probe,
 197        .remove         = mc3230_remove,
 198        .id_table       = mc3230_i2c_id,
 199};
 200
 201module_i2c_driver(mc3230_driver);
 202
 203MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
 204MODULE_DESCRIPTION("mCube MC3230 3-Axis Accelerometer driver");
 205MODULE_LICENSE("GPL v2");
 206