1 /* 2 * IIO driver for the 3-axis accelerometer Domintech DMARD09. 3 * 4 * Copyright (c) 2016, Jelle van der Waa <jelle@vdwaa.nl> 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms and conditions of the GNU General Public License, 8 * version 2, as published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 * more details. 14 */ 15 16 #include <asm/unaligned.h> 17 #include <linux/module.h> 18 #include <linux/i2c.h> 19 #include <linux/iio/iio.h> 20 21 #define DMARD09_DRV_NAME "dmard09" 22 23 #define DMARD09_REG_CHIPID 0x18 24 #define DMARD09_REG_STAT 0x0A 25 #define DMARD09_REG_X 0x0C 26 #define DMARD09_REG_Y 0x0E 27 #define DMARD09_REG_Z 0x10 28 #define DMARD09_CHIPID 0x95 29 30 #define DMARD09_BUF_LEN 8 31 #define DMARD09_AXIS_X 0 32 #define DMARD09_AXIS_Y 1 33 #define DMARD09_AXIS_Z 2 34 #define DMARD09_AXIS_X_OFFSET ((DMARD09_AXIS_X + 1) * 2) 35 #define DMARD09_AXIS_Y_OFFSET ((DMARD09_AXIS_Y + 1 )* 2) 36 #define DMARD09_AXIS_Z_OFFSET ((DMARD09_AXIS_Z + 1) * 2) 37 38 struct dmard09_data { 39 struct i2c_client *client; 40 }; 41 42 #define DMARD09_CHANNEL(_axis, offset) { \ 43 .type = IIO_ACCEL, \ 44 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 45 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ 46 .modified = 1, \ 47 .address = offset, \ 48 .channel2 = IIO_MOD_##_axis, \ 49 } 50 51 static const struct iio_chan_spec dmard09_channels[] = { 52 DMARD09_CHANNEL(X, DMARD09_AXIS_X_OFFSET), 53 DMARD09_CHANNEL(Y, DMARD09_AXIS_Y_OFFSET), 54 DMARD09_CHANNEL(Z, DMARD09_AXIS_Z_OFFSET), 55 }; 56 57 static int dmard09_read_raw(struct iio_dev *indio_dev, 58 struct iio_chan_spec const *chan, 59 int *val, int *val2, long mask) 60 { 61 struct dmard09_data *data = iio_priv(indio_dev); 62 u8 buf[DMARD09_BUF_LEN]; 63 int ret; 64 s16 accel; 65 66 switch (mask) { 67 case IIO_CHAN_INFO_RAW: 68 /* 69 * Read from the DMAR09_REG_STAT register, since the chip 70 * caches reads from the individual X, Y, Z registers. 71 */ 72 ret = i2c_smbus_read_i2c_block_data(data->client, 73 DMARD09_REG_STAT, 74 DMARD09_BUF_LEN, buf); 75 if (ret < 0) { 76 dev_err(&data->client->dev, "Error reading reg %d\n", 77 DMARD09_REG_STAT); 78 return ret; 79 } 80 81 accel = get_unaligned_le16(&buf[chan->address]); 82 83 /* Remove lower 3 bits and sign extend */ 84 accel <<= 4; 85 accel >>= 7; 86 87 *val = accel; 88 89 return IIO_VAL_INT; 90 default: 91 return -EINVAL; 92 } 93 } 94 95 static const struct iio_info dmard09_info = { 96 .read_raw = dmard09_read_raw, 97 }; 98 99 static int dmard09_probe(struct i2c_client *client, 100 const struct i2c_device_id *id) 101 { 102 int ret; 103 struct iio_dev *indio_dev; 104 struct dmard09_data *data; 105 106 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 107 if (!indio_dev) { 108 dev_err(&client->dev, "iio allocation failed\n"); 109 return -ENOMEM; 110 } 111 112 data = iio_priv(indio_dev); 113 data->client = client; 114 115 ret = i2c_smbus_read_byte_data(data->client, DMARD09_REG_CHIPID); 116 if (ret < 0) { 117 dev_err(&client->dev, "Error reading chip id %d\n", ret); 118 return ret; 119 } 120 121 if (ret != DMARD09_CHIPID) { 122 dev_err(&client->dev, "Invalid chip id %d\n", ret); 123 return -ENODEV; 124 } 125 126 i2c_set_clientdata(client, indio_dev); 127 indio_dev->dev.parent = &client->dev; 128 indio_dev->name = DMARD09_DRV_NAME; 129 indio_dev->modes = INDIO_DIRECT_MODE; 130 indio_dev->channels = dmard09_channels; 131 indio_dev->num_channels = ARRAY_SIZE(dmard09_channels); 132 indio_dev->info = &dmard09_info; 133 134 return devm_iio_device_register(&client->dev, indio_dev); 135 } 136 137 static const struct i2c_device_id dmard09_id[] = { 138 { "dmard09", 0}, 139 { }, 140 }; 141 142 MODULE_DEVICE_TABLE(i2c, dmard09_id); 143 144 static struct i2c_driver dmard09_driver = { 145 .driver = { 146 .name = DMARD09_DRV_NAME 147 }, 148 .probe = dmard09_probe, 149 .id_table = dmard09_id, 150 }; 151 152 module_i2c_driver(dmard09_driver); 153 154 MODULE_AUTHOR("Jelle van der Waa <jelle@vdwaa.nl>"); 155 MODULE_DESCRIPTION("DMARD09 3-axis accelerometer driver"); 156 MODULE_LICENSE("GPL"); 157