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