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