GNU Linux-libre 5.16.19-gnu
[releases.git] / drivers / iio / accel / dmard06.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * IIO driver for Domintech DMARD06 accelerometer
4  *
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             0x0f
17 #define DMARD06_TOUT_REG                0x40
18 #define DMARD06_XOUT_REG                0x41
19 #define DMARD06_YOUT_REG                0x42
20 #define DMARD06_ZOUT_REG                0x43
21 #define DMARD06_CTRL1_REG               0x44
22
23 /* Device ID value */
24 #define DMARD05_CHIP_ID                 0x05
25 #define DMARD06_CHIP_ID                 0x06
26 #define DMARD07_CHIP_ID                 0x07
27
28 /* Device values */
29 #define DMARD05_AXIS_SCALE_VAL          15625
30 #define DMARD06_AXIS_SCALE_VAL          31250
31 #define DMARD06_TEMP_CENTER_VAL         25
32 #define DMARD06_SIGN_BIT                7
33
34 /* Device power modes */
35 #define DMARD06_MODE_NORMAL             0x27
36 #define DMARD06_MODE_POWERDOWN          0x00
37
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
55 struct dmard06_data {
56         struct i2c_client *client;
57         u8 chip_id;
58 };
59
60 static 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
67 static 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                         else
114                                 *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
124 static const struct iio_info dmard06_info = {
125         .read_raw       = dmard06_read_raw,
126 };
127
128 static int dmard06_probe(struct i2c_client *client,
129                         const struct i2c_device_id *id)
130 {
131         int ret;
132         struct iio_dev *indio_dev;
133         struct dmard06_data *dmard06;
134
135         if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
136                 dev_err(&client->dev, "I2C check functionality failed\n");
137                 return -ENXIO;
138         }
139
140         indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dmard06));
141         if (!indio_dev) {
142                 dev_err(&client->dev, "Failed to allocate iio device\n");
143                 return -ENOMEM;
144         }
145
146         dmard06 = iio_priv(indio_dev);
147         dmard06->client = client;
148
149         ret = i2c_smbus_read_byte_data(dmard06->client, DMARD06_CHIP_ID_REG);
150         if (ret < 0) {
151                 dev_err(&client->dev, "Error reading chip id: %d\n", ret);
152                 return ret;
153         }
154
155         if (ret != DMARD05_CHIP_ID && ret != DMARD06_CHIP_ID &&
156             ret != DMARD07_CHIP_ID) {
157                 dev_err(&client->dev, "Invalid chip id: %02d\n", ret);
158                 return -ENODEV;
159         }
160
161         dmard06->chip_id = ret;
162
163         i2c_set_clientdata(client, indio_dev);
164         indio_dev->name = DMARD06_DRV_NAME;
165         indio_dev->modes = INDIO_DIRECT_MODE;
166         indio_dev->channels = dmard06_channels;
167         indio_dev->num_channels = ARRAY_SIZE(dmard06_channels);
168         indio_dev->info = &dmard06_info;
169
170         return devm_iio_device_register(&client->dev, indio_dev);
171 }
172
173 #ifdef CONFIG_PM_SLEEP
174 static int dmard06_suspend(struct device *dev)
175 {
176         struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
177         struct dmard06_data *dmard06 = iio_priv(indio_dev);
178         int ret;
179
180         ret = i2c_smbus_write_byte_data(dmard06->client, DMARD06_CTRL1_REG,
181                                         DMARD06_MODE_POWERDOWN);
182         if (ret < 0)
183                 return ret;
184
185         return 0;
186 }
187
188 static int dmard06_resume(struct device *dev)
189 {
190         struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
191         struct dmard06_data *dmard06 = iio_priv(indio_dev);
192         int ret;
193
194         ret = i2c_smbus_write_byte_data(dmard06->client, DMARD06_CTRL1_REG,
195                                         DMARD06_MODE_NORMAL);
196         if (ret < 0)
197                 return ret;
198
199         return 0;
200 }
201
202 static SIMPLE_DEV_PM_OPS(dmard06_pm_ops, dmard06_suspend, dmard06_resume);
203 #define DMARD06_PM_OPS (&dmard06_pm_ops)
204 #else
205 #define DMARD06_PM_OPS NULL
206 #endif
207
208 static const struct i2c_device_id dmard06_id[] = {
209         { "dmard05", 0 },
210         { "dmard06", 0 },
211         { "dmard07", 0 },
212         { }
213 };
214 MODULE_DEVICE_TABLE(i2c, dmard06_id);
215
216 static const struct of_device_id dmard06_of_match[] = {
217         { .compatible = "domintech,dmard05" },
218         { .compatible = "domintech,dmard06" },
219         { .compatible = "domintech,dmard07" },
220         { }
221 };
222 MODULE_DEVICE_TABLE(of, dmard06_of_match);
223
224 static struct i2c_driver dmard06_driver = {
225         .probe = dmard06_probe,
226         .id_table = dmard06_id,
227         .driver = {
228                 .name = DMARD06_DRV_NAME,
229                 .of_match_table = dmard06_of_match,
230                 .pm = DMARD06_PM_OPS,
231         },
232 };
233 module_i2c_driver(dmard06_driver);
234
235 MODULE_AUTHOR("Aleksei Mamlin <mamlinav@gmail.com>");
236 MODULE_DESCRIPTION("Domintech DMARD06 accelerometer driver");
237 MODULE_LICENSE("GPL v2");