]>
Commit | Line | Data |
---|---|---|
75a6faf6 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
b1b79f53 AM |
2 | /* |
3 | * IIO driver for Domintech DMARD06 accelerometer | |
4 | * | |
5 | * Copyright (C) 2016 Aleksei Mamlin <mamlinav@gmail.com> | |
b1b79f53 AM |
6 | */ |
7 | ||
8 | #include <linux/module.h> | |
9 | #include <linux/i2c.h> | |
10 | #include <linux/iio/iio.h> | |
11 | ||
12 | #define DMARD06_DRV_NAME "dmard06" | |
13 | ||
14 | /* Device data registers */ | |
15 | #define DMARD06_CHIP_ID_REG 0x0f | |
16 | #define DMARD06_TOUT_REG 0x40 | |
17 | #define DMARD06_XOUT_REG 0x41 | |
18 | #define DMARD06_YOUT_REG 0x42 | |
19 | #define DMARD06_ZOUT_REG 0x43 | |
20 | #define DMARD06_CTRL1_REG 0x44 | |
21 | ||
22 | /* Device ID value */ | |
23 | #define DMARD05_CHIP_ID 0x05 | |
24 | #define DMARD06_CHIP_ID 0x06 | |
25 | #define DMARD07_CHIP_ID 0x07 | |
26 | ||
27 | /* Device values */ | |
28 | #define DMARD05_AXIS_SCALE_VAL 15625 | |
29 | #define DMARD06_AXIS_SCALE_VAL 31250 | |
30 | #define DMARD06_TEMP_CENTER_VAL 25 | |
31 | #define DMARD06_SIGN_BIT 7 | |
32 | ||
33 | /* Device power modes */ | |
34 | #define DMARD06_MODE_NORMAL 0x27 | |
35 | #define DMARD06_MODE_POWERDOWN 0x00 | |
36 | ||
37 | /* Device channels */ | |
38 | #define DMARD06_ACCEL_CHANNEL(_axis, _reg) { \ | |
39 | .type = IIO_ACCEL, \ | |
40 | .address = _reg, \ | |
41 | .channel2 = IIO_MOD_##_axis, \ | |
42 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | |
43 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ | |
44 | .modified = 1, \ | |
45 | } | |
46 | ||
47 | #define DMARD06_TEMP_CHANNEL(_reg) { \ | |
48 | .type = IIO_TEMP, \ | |
49 | .address = _reg, \ | |
50 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ | |
51 | BIT(IIO_CHAN_INFO_OFFSET), \ | |
52 | } | |
53 | ||
54 | struct dmard06_data { | |
55 | struct i2c_client *client; | |
56 | u8 chip_id; | |
57 | }; | |
58 | ||
59 | static const struct iio_chan_spec dmard06_channels[] = { | |
60 | DMARD06_ACCEL_CHANNEL(X, DMARD06_XOUT_REG), | |
61 | DMARD06_ACCEL_CHANNEL(Y, DMARD06_YOUT_REG), | |
62 | DMARD06_ACCEL_CHANNEL(Z, DMARD06_ZOUT_REG), | |
63 | DMARD06_TEMP_CHANNEL(DMARD06_TOUT_REG), | |
64 | }; | |
65 | ||
66 | static int dmard06_read_raw(struct iio_dev *indio_dev, | |
67 | struct iio_chan_spec const *chan, | |
68 | int *val, int *val2, long mask) | |
69 | { | |
70 | struct dmard06_data *dmard06 = iio_priv(indio_dev); | |
71 | int ret; | |
72 | ||
73 | switch (mask) { | |
74 | case IIO_CHAN_INFO_RAW: | |
75 | ret = i2c_smbus_read_byte_data(dmard06->client, | |
76 | chan->address); | |
77 | if (ret < 0) { | |
78 | dev_err(&dmard06->client->dev, | |
79 | "Error reading data: %d\n", ret); | |
80 | return ret; | |
81 | } | |
82 | ||
83 | *val = sign_extend32(ret, DMARD06_SIGN_BIT); | |
84 | ||
85 | if (dmard06->chip_id == DMARD06_CHIP_ID) | |
86 | *val = *val >> 1; | |
87 | ||
88 | switch (chan->type) { | |
89 | case IIO_ACCEL: | |
90 | return IIO_VAL_INT; | |
91 | case IIO_TEMP: | |
92 | if (dmard06->chip_id != DMARD06_CHIP_ID) | |
93 | *val = *val / 2; | |
94 | return IIO_VAL_INT; | |
95 | default: | |
96 | return -EINVAL; | |
97 | } | |
98 | case IIO_CHAN_INFO_OFFSET: | |
99 | switch (chan->type) { | |
100 | case IIO_TEMP: | |
101 | *val = DMARD06_TEMP_CENTER_VAL; | |
102 | return IIO_VAL_INT; | |
103 | default: | |
104 | return -EINVAL; | |
105 | } | |
106 | case IIO_CHAN_INFO_SCALE: | |
107 | switch (chan->type) { | |
108 | case IIO_ACCEL: | |
109 | *val = 0; | |
110 | if (dmard06->chip_id == DMARD06_CHIP_ID) | |
111 | *val2 = DMARD06_AXIS_SCALE_VAL; | |
112 | else | |
113 | *val2 = DMARD05_AXIS_SCALE_VAL; | |
114 | return IIO_VAL_INT_PLUS_MICRO; | |
115 | default: | |
116 | return -EINVAL; | |
117 | } | |
118 | default: | |
119 | return -EINVAL; | |
120 | } | |
121 | } | |
122 | ||
123 | static const struct iio_info dmard06_info = { | |
b1b79f53 AM |
124 | .read_raw = dmard06_read_raw, |
125 | }; | |
126 | ||
127 | static int dmard06_probe(struct i2c_client *client, | |
128 | const struct i2c_device_id *id) | |
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->dev.parent = &client->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 = of_match_ptr(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"); |