1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * ADIS16203 Programmable 360 Degrees Inclinometer |
4 | * |
5 | * Copyright 2010 Analog Devices Inc. |
6 | */ |
7 | |
8 | #include <linux/device.h> |
9 | |
10 | #include <linux/iio/iio.h> |
11 | #include <linux/iio/imu/adis.h> |
12 | |
13 | #include <linux/kernel.h> |
14 | #include <linux/module.h> |
15 | #include <linux/spi/spi.h> |
16 | |
17 | #define ADIS16203_STARTUP_DELAY 220 /* ms */ |
18 | |
19 | /* Flash memory write count */ |
20 | #define ADIS16203_FLASH_CNT 0x00 |
21 | |
22 | /* Output, power supply */ |
23 | #define ADIS16203_SUPPLY_OUT 0x02 |
24 | |
25 | /* Output, auxiliary ADC input */ |
26 | #define ADIS16203_AUX_ADC 0x08 |
27 | |
28 | /* Output, temperature */ |
29 | #define ADIS16203_TEMP_OUT 0x0A |
30 | |
31 | /* Output, x-axis inclination */ |
32 | #define ADIS16203_XINCL_OUT 0x0C |
33 | |
34 | /* Output, y-axis inclination */ |
35 | #define ADIS16203_YINCL_OUT 0x0E |
36 | |
37 | /* Incline null calibration */ |
38 | #define ADIS16203_INCL_NULL 0x18 |
39 | |
40 | /* Alarm 1 amplitude threshold */ |
41 | #define ADIS16203_ALM_MAG1 0x20 |
42 | |
43 | /* Alarm 2 amplitude threshold */ |
44 | #define ADIS16203_ALM_MAG2 0x22 |
45 | |
46 | /* Alarm 1, sample period */ |
47 | #define ADIS16203_ALM_SMPL1 0x24 |
48 | |
49 | /* Alarm 2, sample period */ |
50 | #define ADIS16203_ALM_SMPL2 0x26 |
51 | |
52 | /* Alarm control */ |
53 | #define ADIS16203_ALM_CTRL 0x28 |
54 | |
55 | /* Auxiliary DAC data */ |
56 | #define ADIS16203_AUX_DAC 0x30 |
57 | |
58 | /* General-purpose digital input/output control */ |
59 | #define ADIS16203_GPIO_CTRL 0x32 |
60 | |
61 | /* Miscellaneous control */ |
62 | #define ADIS16203_MSC_CTRL 0x34 |
63 | |
64 | /* Internal sample period (rate) control */ |
65 | #define ADIS16203_SMPL_PRD 0x36 |
66 | |
67 | /* Operation, filter configuration */ |
68 | #define ADIS16203_AVG_CNT 0x38 |
69 | |
70 | /* Operation, sleep mode control */ |
71 | #define ADIS16203_SLP_CNT 0x3A |
72 | |
73 | /* Diagnostics, system status register */ |
74 | #define ADIS16203_DIAG_STAT 0x3C |
75 | |
76 | /* Operation, system command register */ |
77 | #define ADIS16203_GLOB_CMD 0x3E |
78 | |
79 | /* MSC_CTRL */ |
80 | |
81 | /* Self-test at power-on: 1 = disabled, 0 = enabled */ |
82 | #define ADIS16203_MSC_CTRL_PWRUP_SELF_TEST BIT(10) |
83 | |
84 | /* Reverses rotation of both inclination outputs */ |
85 | #define ADIS16203_MSC_CTRL_REVERSE_ROT_EN BIT(9) |
86 | |
87 | /* Self-test enable */ |
88 | #define ADIS16203_MSC_CTRL_SELF_TEST_EN BIT(8) |
89 | |
90 | /* Data-ready enable: 1 = enabled, 0 = disabled */ |
91 | #define ADIS16203_MSC_CTRL_DATA_RDY_EN BIT(2) |
92 | |
93 | /* Data-ready polarity: 1 = active high, 0 = active low */ |
94 | #define ADIS16203_MSC_CTRL_ACTIVE_HIGH BIT(1) |
95 | |
96 | /* Data-ready line selection: 1 = DIO1, 0 = DIO0 */ |
97 | #define ADIS16203_MSC_CTRL_DATA_RDY_DIO1 BIT(0) |
98 | |
99 | /* DIAG_STAT */ |
100 | |
101 | /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */ |
102 | #define ADIS16203_DIAG_STAT_ALARM2 BIT(9) |
103 | |
104 | /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */ |
105 | #define ADIS16203_DIAG_STAT_ALARM1 BIT(8) |
106 | |
107 | /* Self-test diagnostic error flag */ |
108 | #define ADIS16203_DIAG_STAT_SELFTEST_FAIL_BIT 5 |
109 | |
110 | /* SPI communications failure */ |
111 | #define ADIS16203_DIAG_STAT_SPI_FAIL_BIT 3 |
112 | |
113 | /* Flash update failure */ |
114 | #define ADIS16203_DIAG_STAT_FLASH_UPT_BIT 2 |
115 | |
116 | /* Power supply above 3.625 V */ |
117 | #define ADIS16203_DIAG_STAT_POWER_HIGH_BIT 1 |
118 | |
119 | /* Power supply below 2.975 V */ |
120 | #define ADIS16203_DIAG_STAT_POWER_LOW_BIT 0 |
121 | |
122 | /* GLOB_CMD */ |
123 | |
124 | #define ADIS16203_GLOB_CMD_SW_RESET BIT(7) |
125 | #define ADIS16203_GLOB_CMD_CLEAR_STAT BIT(4) |
126 | #define ADIS16203_GLOB_CMD_FACTORY_CAL BIT(1) |
127 | |
128 | #define ADIS16203_ERROR_ACTIVE BIT(14) |
129 | |
130 | enum adis16203_scan { |
131 | ADIS16203_SCAN_INCLI_X, |
132 | ADIS16203_SCAN_INCLI_Y, |
133 | ADIS16203_SCAN_SUPPLY, |
134 | ADIS16203_SCAN_AUX_ADC, |
135 | ADIS16203_SCAN_TEMP, |
136 | }; |
137 | |
138 | #define DRIVER_NAME "adis16203" |
139 | |
140 | static const u8 adis16203_addresses[] = { |
141 | [ADIS16203_SCAN_INCLI_X] = ADIS16203_INCL_NULL, |
142 | }; |
143 | |
144 | static int adis16203_write_raw(struct iio_dev *indio_dev, |
145 | struct iio_chan_spec const *chan, |
146 | int val, |
147 | int val2, |
148 | long mask) |
149 | { |
150 | struct adis *st = iio_priv(indio_dev); |
151 | /* currently only one writable parameter which keeps this simple */ |
152 | u8 addr = adis16203_addresses[chan->scan_index]; |
153 | |
154 | return adis_write_reg_16(adis: st, reg: addr, val: val & 0x3FFF); |
155 | } |
156 | |
157 | static int adis16203_read_raw(struct iio_dev *indio_dev, |
158 | struct iio_chan_spec const *chan, |
159 | int *val, int *val2, |
160 | long mask) |
161 | { |
162 | struct adis *st = iio_priv(indio_dev); |
163 | int ret; |
164 | u8 addr; |
165 | s16 val16; |
166 | |
167 | switch (mask) { |
168 | case IIO_CHAN_INFO_RAW: |
169 | return adis_single_conversion(indio_dev, chan, |
170 | ADIS16203_ERROR_ACTIVE, val); |
171 | case IIO_CHAN_INFO_SCALE: |
172 | switch (chan->type) { |
173 | case IIO_VOLTAGE: |
174 | if (chan->channel == 0) { |
175 | *val = 1; |
176 | *val2 = 220000; /* 1.22 mV */ |
177 | } else { |
178 | *val = 0; |
179 | *val2 = 610000; /* 0.61 mV */ |
180 | } |
181 | return IIO_VAL_INT_PLUS_MICRO; |
182 | case IIO_TEMP: |
183 | *val = -470; /* -0.47 C */ |
184 | *val2 = 0; |
185 | return IIO_VAL_INT_PLUS_MICRO; |
186 | case IIO_INCLI: |
187 | *val = 0; |
188 | *val2 = 25000; /* 0.025 degree */ |
189 | return IIO_VAL_INT_PLUS_MICRO; |
190 | default: |
191 | return -EINVAL; |
192 | } |
193 | case IIO_CHAN_INFO_OFFSET: |
194 | *val = 25000 / -470 - 1278; /* 25 C = 1278 */ |
195 | return IIO_VAL_INT; |
196 | case IIO_CHAN_INFO_CALIBBIAS: |
197 | addr = adis16203_addresses[chan->scan_index]; |
198 | ret = adis_read_reg_16(adis: st, reg: addr, val: &val16); |
199 | if (ret) |
200 | return ret; |
201 | *val = sign_extend32(value: val16, index: 13); |
202 | return IIO_VAL_INT; |
203 | default: |
204 | return -EINVAL; |
205 | } |
206 | } |
207 | |
208 | static const struct iio_chan_spec adis16203_channels[] = { |
209 | ADIS_SUPPLY_CHAN(ADIS16203_SUPPLY_OUT, ADIS16203_SCAN_SUPPLY, 0, 12), |
210 | ADIS_AUX_ADC_CHAN(ADIS16203_AUX_ADC, ADIS16203_SCAN_AUX_ADC, 0, 12), |
211 | ADIS_INCLI_CHAN(X, ADIS16203_XINCL_OUT, ADIS16203_SCAN_INCLI_X, |
212 | BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), |
213 | /* Fixme: Not what it appears to be - see data sheet */ |
214 | ADIS_INCLI_CHAN(Y, ADIS16203_YINCL_OUT, ADIS16203_SCAN_INCLI_Y, |
215 | 0, 0, 14), |
216 | ADIS_TEMP_CHAN(ADIS16203_TEMP_OUT, ADIS16203_SCAN_TEMP, 0, 12), |
217 | IIO_CHAN_SOFT_TIMESTAMP(5), |
218 | }; |
219 | |
220 | static const struct iio_info adis16203_info = { |
221 | .read_raw = adis16203_read_raw, |
222 | .write_raw = adis16203_write_raw, |
223 | .update_scan_mode = adis_update_scan_mode, |
224 | }; |
225 | |
226 | static const char * const adis16203_status_error_msgs[] = { |
227 | [ADIS16203_DIAG_STAT_SELFTEST_FAIL_BIT] = "Self test failure" , |
228 | [ADIS16203_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure" , |
229 | [ADIS16203_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed" , |
230 | [ADIS16203_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V" , |
231 | [ADIS16203_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 2.975V" , |
232 | }; |
233 | |
234 | static const struct adis_timeout adis16203_timeouts = { |
235 | .reset_ms = ADIS16203_STARTUP_DELAY, |
236 | .sw_reset_ms = ADIS16203_STARTUP_DELAY, |
237 | .self_test_ms = ADIS16203_STARTUP_DELAY |
238 | }; |
239 | |
240 | static const struct adis_data adis16203_data = { |
241 | .read_delay = 20, |
242 | .msc_ctrl_reg = ADIS16203_MSC_CTRL, |
243 | .glob_cmd_reg = ADIS16203_GLOB_CMD, |
244 | .diag_stat_reg = ADIS16203_DIAG_STAT, |
245 | |
246 | .self_test_mask = ADIS16203_MSC_CTRL_SELF_TEST_EN, |
247 | .self_test_reg = ADIS16203_MSC_CTRL, |
248 | .self_test_no_autoclear = true, |
249 | .timeouts = &adis16203_timeouts, |
250 | |
251 | .status_error_msgs = adis16203_status_error_msgs, |
252 | .status_error_mask = BIT(ADIS16203_DIAG_STAT_SELFTEST_FAIL_BIT) | |
253 | BIT(ADIS16203_DIAG_STAT_SPI_FAIL_BIT) | |
254 | BIT(ADIS16203_DIAG_STAT_FLASH_UPT_BIT) | |
255 | BIT(ADIS16203_DIAG_STAT_POWER_HIGH_BIT) | |
256 | BIT(ADIS16203_DIAG_STAT_POWER_LOW_BIT), |
257 | }; |
258 | |
259 | static int adis16203_probe(struct spi_device *spi) |
260 | { |
261 | int ret; |
262 | struct iio_dev *indio_dev; |
263 | struct adis *st; |
264 | |
265 | /* setup the industrialio driver allocated elements */ |
266 | indio_dev = devm_iio_device_alloc(parent: &spi->dev, sizeof_priv: sizeof(*st)); |
267 | if (!indio_dev) |
268 | return -ENOMEM; |
269 | st = iio_priv(indio_dev); |
270 | /* this is only used for removal purposes */ |
271 | spi_set_drvdata(spi, data: indio_dev); |
272 | |
273 | indio_dev->name = spi->dev.driver->name; |
274 | indio_dev->channels = adis16203_channels; |
275 | indio_dev->num_channels = ARRAY_SIZE(adis16203_channels); |
276 | indio_dev->info = &adis16203_info; |
277 | indio_dev->modes = INDIO_DIRECT_MODE; |
278 | |
279 | ret = adis_init(adis: st, indio_dev, spi, data: &adis16203_data); |
280 | if (ret) |
281 | return ret; |
282 | |
283 | ret = devm_adis_setup_buffer_and_trigger(adis: st, indio_dev, NULL); |
284 | if (ret) |
285 | return ret; |
286 | |
287 | /* Get the device into a sane initial state */ |
288 | ret = __adis_initial_startup(adis: st); |
289 | if (ret) |
290 | return ret; |
291 | |
292 | return devm_iio_device_register(&spi->dev, indio_dev); |
293 | } |
294 | |
295 | static const struct of_device_id adis16203_of_match[] = { |
296 | { .compatible = "adi,adis16203" }, |
297 | { }, |
298 | }; |
299 | |
300 | MODULE_DEVICE_TABLE(of, adis16203_of_match); |
301 | |
302 | static struct spi_driver adis16203_driver = { |
303 | .driver = { |
304 | .name = "adis16203" , |
305 | .of_match_table = adis16203_of_match, |
306 | }, |
307 | .probe = adis16203_probe, |
308 | }; |
309 | module_spi_driver(adis16203_driver); |
310 | |
311 | MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>" ); |
312 | MODULE_DESCRIPTION("Analog Devices ADIS16203 Programmable 360 Degrees Inclinometer" ); |
313 | MODULE_LICENSE("GPL v2" ); |
314 | MODULE_ALIAS("spi:adis16203" ); |
315 | MODULE_IMPORT_NS(IIO_ADISLIB); |
316 | |