1 /* 2 * IIO driver for Domintech DMARD06 accelerometer 3 * 4 * Copyright (C) 2016 Aleksei Mamlin <mamlinav@gmail.com> 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 11 #include <linux/module.h> 12 #include <linux/i2c.h> 13 #include <linux/iio/iio.h> 14 15 #define DMARD06_DRV_NAME "dmard06" 16 17 /* Device data registers */ 18 #define DMARD06_CHIP_ID_REG 0x0f 19 #define DMARD06_TOUT_REG 0x40 20 #define DMARD06_XOUT_REG 0x41 21 #define DMARD06_YOUT_REG 0x42 22 #define DMARD06_ZOUT_REG 0x43 23 #define DMARD06_CTRL1_REG 0x44 24 25 /* Device ID value */ 26 #define DMARD05_CHIP_ID 0x05 27 #define DMARD06_CHIP_ID 0x06 28 #define DMARD07_CHIP_ID 0x07 29 30 /* Device values */ 31 #define DMARD05_AXIS_SCALE_VAL 15625 32 #define DMARD06_AXIS_SCALE_VAL 31250 33 #define DMARD06_TEMP_CENTER_VAL 25 34 #define DMARD06_SIGN_BIT 7 35 36 /* Device power modes */ 37 #define DMARD06_MODE_NORMAL 0x27 38 #define DMARD06_MODE_POWERDOWN 0x00 39 40 /* Device channels */ 41 #define DMARD06_ACCEL_CHANNEL(_axis, _reg) { \ 42 .type = IIO_ACCEL, \ 43 .address = _reg, \ 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 .modified = 1, \ 48 } 49 50 #define DMARD06_TEMP_CHANNEL(_reg) { \ 51 .type = IIO_TEMP, \ 52 .address = _reg, \ 53 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ 54 BIT(IIO_CHAN_INFO_OFFSET), \ 55 } 56 57 struct dmard06_data { 58 struct i2c_client *client; 59 u8 chip_id; 60 }; 61 62 static const struct iio_chan_spec dmard06_channels[] = { 63 DMARD06_ACCEL_CHANNEL(X, DMARD06_XOUT_REG), 64 DMARD06_ACCEL_CHANNEL(Y, DMARD06_YOUT_REG), 65 DMARD06_ACCEL_CHANNEL(Z, DMARD06_ZOUT_REG), 66 DMARD06_TEMP_CHANNEL(DMARD06_TOUT_REG), 67 }; 68 69 static int dmard06_read_raw(struct iio_dev *indio_dev, 70 struct iio_chan_spec const *chan, 71 int *val, int *val2, long mask) 72 { 73 struct dmard06_data *dmard06 = iio_priv(indio_dev); 74 int ret; 75 76 switch (mask) { 77 case IIO_CHAN_INFO_RAW: 78 ret = i2c_smbus_read_byte_data(dmard06->client, 79 chan->address); 80 if (ret < 0) { 81 dev_err(&dmard06->client->dev, 82 "Error reading data: %d\n", ret); 83 return ret; 84 } 85 86 *val = sign_extend32(ret, DMARD06_SIGN_BIT); 87 88 if (dmard06->chip_id == DMARD06_CHIP_ID) 89 *val = *val >> 1; 90 91 switch (chan->type) { 92 case IIO_ACCEL: 93 return IIO_VAL_INT; 94 case IIO_TEMP: 95 if (dmard06->chip_id != DMARD06_CHIP_ID) 96 *val = *val / 2; 97 return IIO_VAL_INT; 98 default: 99 return -EINVAL; 100 } 101 case IIO_CHAN_INFO_OFFSET: 102 switch (chan->type) { 103 case IIO_TEMP: 104 *val = DMARD06_TEMP_CENTER_VAL; 105 return IIO_VAL_INT; 106 default: 107 return -EINVAL; 108 } 109 case IIO_CHAN_INFO_SCALE: 110 switch (chan->type) { 111 case IIO_ACCEL: 112 *val = 0; 113 if (dmard06->chip_id == DMARD06_CHIP_ID) 114 *val2 = DMARD06_AXIS_SCALE_VAL; 115 else 116 *val2 = DMARD05_AXIS_SCALE_VAL; 117 return IIO_VAL_INT_PLUS_MICRO; 118 default: 119 return -EINVAL; 120 } 121 default: 122 return -EINVAL; 123 } 124 } 125 126 static const struct iio_info dmard06_info = { 127 .read_raw = dmard06_read_raw, 128 }; 129 130 static int dmard06_probe(struct i2c_client *client, 131 const struct i2c_device_id *id) 132 { 133 int ret; 134 struct iio_dev *indio_dev; 135 struct dmard06_data *dmard06; 136 137 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { 138 dev_err(&client->dev, "I2C check functionality failed\n"); 139 return -ENXIO; 140 } 141 142 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dmard06)); 143 if (!indio_dev) { 144 dev_err(&client->dev, "Failed to allocate iio device\n"); 145 return -ENOMEM; 146 } 147 148 dmard06 = iio_priv(indio_dev); 149 dmard06->client = client; 150 151 ret = i2c_smbus_read_byte_data(dmard06->client, DMARD06_CHIP_ID_REG); 152 if (ret < 0) { 153 dev_err(&client->dev, "Error reading chip id: %d\n", ret); 154 return ret; 155 } 156 157 if (ret != DMARD05_CHIP_ID && ret != DMARD06_CHIP_ID && 158 ret != DMARD07_CHIP_ID) { 159 dev_err(&client->dev, "Invalid chip id: %02d\n", ret); 160 return -ENODEV; 161 } 162 163 dmard06->chip_id = ret; 164 165 i2c_set_clientdata(client, indio_dev); 166 indio_dev->dev.parent = &client->dev; 167 indio_dev->name = DMARD06_DRV_NAME; 168 indio_dev->modes = INDIO_DIRECT_MODE; 169 indio_dev->channels = dmard06_channels; 170 indio_dev->num_channels = ARRAY_SIZE(dmard06_channels); 171 indio_dev->info = &dmard06_info; 172 173 return devm_iio_device_register(&client->dev, indio_dev); 174 } 175 176 #ifdef CONFIG_PM_SLEEP 177 static int dmard06_suspend(struct device *dev) 178 { 179 struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 180 struct dmard06_data *dmard06 = iio_priv(indio_dev); 181 int ret; 182 183 ret = i2c_smbus_write_byte_data(dmard06->client, DMARD06_CTRL1_REG, 184 DMARD06_MODE_POWERDOWN); 185 if (ret < 0) 186 return ret; 187 188 return 0; 189 } 190 191 static int dmard06_resume(struct device *dev) 192 { 193 struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 194 struct dmard06_data *dmard06 = iio_priv(indio_dev); 195 int ret; 196 197 ret = i2c_smbus_write_byte_data(dmard06->client, DMARD06_CTRL1_REG, 198 DMARD06_MODE_NORMAL); 199 if (ret < 0) 200 return ret; 201 202 return 0; 203 } 204 205 static SIMPLE_DEV_PM_OPS(dmard06_pm_ops, dmard06_suspend, dmard06_resume); 206 #define DMARD06_PM_OPS (&dmard06_pm_ops) 207 #else 208 #define DMARD06_PM_OPS NULL 209 #endif 210 211 static const struct i2c_device_id dmard06_id[] = { 212 { "dmard05", 0 }, 213 { "dmard06", 0 }, 214 { "dmard07", 0 }, 215 { } 216 }; 217 MODULE_DEVICE_TABLE(i2c, dmard06_id); 218 219 static const struct of_device_id dmard06_of_match[] = { 220 { .compatible = "domintech,dmard05" }, 221 { .compatible = "domintech,dmard06" }, 222 { .compatible = "domintech,dmard07" }, 223 { } 224 }; 225 MODULE_DEVICE_TABLE(of, dmard06_of_match); 226 227 static struct i2c_driver dmard06_driver = { 228 .probe = dmard06_probe, 229 .id_table = dmard06_id, 230 .driver = { 231 .name = DMARD06_DRV_NAME, 232 .of_match_table = of_match_ptr(dmard06_of_match), 233 .pm = DMARD06_PM_OPS, 234 }, 235 }; 236 module_i2c_driver(dmard06_driver); 237 238 MODULE_AUTHOR("Aleksei Mamlin <mamlinav@gmail.com>"); 239 MODULE_DESCRIPTION("Domintech DMARD06 accelerometer driver"); 240 MODULE_LICENSE("GPL v2"); 241