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 .driver_module = THIS_MODULE, 128 .read_raw = dmard06_read_raw, 129 }; 130 131 static int dmard06_probe(struct i2c_client *client, 132 const struct i2c_device_id *id) 133 { 134 int ret; 135 struct iio_dev *indio_dev; 136 struct dmard06_data *dmard06; 137 138 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { 139 dev_err(&client->dev, "I2C check functionality failed\n"); 140 return -ENXIO; 141 } 142 143 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dmard06)); 144 if (!indio_dev) { 145 dev_err(&client->dev, "Failed to allocate iio device\n"); 146 return -ENOMEM; 147 } 148 149 dmard06 = iio_priv(indio_dev); 150 dmard06->client = client; 151 152 ret = i2c_smbus_read_byte_data(dmard06->client, DMARD06_CHIP_ID_REG); 153 if (ret < 0) { 154 dev_err(&client->dev, "Error reading chip id: %d\n", ret); 155 return ret; 156 } 157 158 if (ret != DMARD05_CHIP_ID && ret != DMARD06_CHIP_ID && 159 ret != DMARD07_CHIP_ID) { 160 dev_err(&client->dev, "Invalid chip id: %02d\n", ret); 161 return -ENODEV; 162 } 163 164 dmard06->chip_id = ret; 165 166 i2c_set_clientdata(client, indio_dev); 167 indio_dev->dev.parent = &client->dev; 168 indio_dev->name = DMARD06_DRV_NAME; 169 indio_dev->modes = INDIO_DIRECT_MODE; 170 indio_dev->channels = dmard06_channels; 171 indio_dev->num_channels = ARRAY_SIZE(dmard06_channels); 172 indio_dev->info = &dmard06_info; 173 174 return devm_iio_device_register(&client->dev, indio_dev); 175 } 176 177 #ifdef CONFIG_PM_SLEEP 178 static int dmard06_suspend(struct device *dev) 179 { 180 struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 181 struct dmard06_data *dmard06 = iio_priv(indio_dev); 182 int ret; 183 184 ret = i2c_smbus_write_byte_data(dmard06->client, DMARD06_CTRL1_REG, 185 DMARD06_MODE_POWERDOWN); 186 if (ret < 0) 187 return ret; 188 189 return 0; 190 } 191 192 static int dmard06_resume(struct device *dev) 193 { 194 struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 195 struct dmard06_data *dmard06 = iio_priv(indio_dev); 196 int ret; 197 198 ret = i2c_smbus_write_byte_data(dmard06->client, DMARD06_CTRL1_REG, 199 DMARD06_MODE_NORMAL); 200 if (ret < 0) 201 return ret; 202 203 return 0; 204 } 205 206 static SIMPLE_DEV_PM_OPS(dmard06_pm_ops, dmard06_suspend, dmard06_resume); 207 #define DMARD06_PM_OPS (&dmard06_pm_ops) 208 #else 209 #define DMARD06_PM_OPS NULL 210 #endif 211 212 static const struct i2c_device_id dmard06_id[] = { 213 { "dmard05", 0 }, 214 { "dmard06", 0 }, 215 { "dmard07", 0 }, 216 { } 217 }; 218 MODULE_DEVICE_TABLE(i2c, dmard06_id); 219 220 static const struct of_device_id dmard06_of_match[] = { 221 { .compatible = "domintech,dmard05" }, 222 { .compatible = "domintech,dmard06" }, 223 { .compatible = "domintech,dmard07" }, 224 { } 225 }; 226 MODULE_DEVICE_TABLE(of, dmard06_of_match); 227 228 static struct i2c_driver dmard06_driver = { 229 .probe = dmard06_probe, 230 .id_table = dmard06_id, 231 .driver = { 232 .name = DMARD06_DRV_NAME, 233 .of_match_table = of_match_ptr(dmard06_of_match), 234 .pm = DMARD06_PM_OPS, 235 }, 236 }; 237 module_i2c_driver(dmard06_driver); 238 239 MODULE_AUTHOR("Aleksei Mamlin <mamlinav@gmail.com>"); 240 MODULE_DESCRIPTION("Domintech DMARD06 accelerometer driver"); 241 MODULE_LICENSE("GPL v2"); 242