Merge tag 'pci-v5.18-changes-2' of git://git.kernel.org/pub/scm/linux/kernel/git...
[sfrench/cifs-2.6.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 static int dmard06_suspend(struct device *dev)
174 {
175         struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
176         struct dmard06_data *dmard06 = iio_priv(indio_dev);
177         int ret;
178
179         ret = i2c_smbus_write_byte_data(dmard06->client, DMARD06_CTRL1_REG,
180                                         DMARD06_MODE_POWERDOWN);
181         if (ret < 0)
182                 return ret;
183
184         return 0;
185 }
186
187 static int dmard06_resume(struct device *dev)
188 {
189         struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
190         struct dmard06_data *dmard06 = iio_priv(indio_dev);
191         int ret;
192
193         ret = i2c_smbus_write_byte_data(dmard06->client, DMARD06_CTRL1_REG,
194                                         DMARD06_MODE_NORMAL);
195         if (ret < 0)
196                 return ret;
197
198         return 0;
199 }
200
201 static DEFINE_SIMPLE_DEV_PM_OPS(dmard06_pm_ops, dmard06_suspend,
202                                 dmard06_resume);
203
204 static const struct i2c_device_id dmard06_id[] = {
205         { "dmard05", 0 },
206         { "dmard06", 0 },
207         { "dmard07", 0 },
208         { }
209 };
210 MODULE_DEVICE_TABLE(i2c, dmard06_id);
211
212 static const struct of_device_id dmard06_of_match[] = {
213         { .compatible = "domintech,dmard05" },
214         { .compatible = "domintech,dmard06" },
215         { .compatible = "domintech,dmard07" },
216         { }
217 };
218 MODULE_DEVICE_TABLE(of, dmard06_of_match);
219
220 static struct i2c_driver dmard06_driver = {
221         .probe = dmard06_probe,
222         .id_table = dmard06_id,
223         .driver = {
224                 .name = DMARD06_DRV_NAME,
225                 .of_match_table = dmard06_of_match,
226                 .pm = pm_sleep_ptr(&dmard06_pm_ops),
227         },
228 };
229 module_i2c_driver(dmard06_driver);
230
231 MODULE_AUTHOR("Aleksei Mamlin <mamlinav@gmail.com>");
232 MODULE_DESCRIPTION("Domintech DMARD06 accelerometer driver");
233 MODULE_LICENSE("GPL v2");