1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright 2010 Analog Devices Inc.
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include <linux/device.h>
9*4882a593Smuzhiyun #include <linux/kernel.h>
10*4882a593Smuzhiyun #include <linux/list.h>
11*4882a593Smuzhiyun #include <linux/module.h>
12*4882a593Smuzhiyun #include <linux/spi/spi.h>
13*4882a593Smuzhiyun #include <linux/slab.h>
14*4882a593Smuzhiyun #include <linux/sysfs.h>
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun #include <linux/iio/iio.h>
17*4882a593Smuzhiyun #include <linux/iio/imu/adis.h>
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun #define ADIS16209_STARTUP_DELAY_MS 220
20*4882a593Smuzhiyun #define ADIS16209_FLASH_CNT_REG 0x00
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun /* Data Output Register Definitions */
23*4882a593Smuzhiyun #define ADIS16209_SUPPLY_OUT_REG 0x02
24*4882a593Smuzhiyun #define ADIS16209_XACCL_OUT_REG 0x04
25*4882a593Smuzhiyun #define ADIS16209_YACCL_OUT_REG 0x06
26*4882a593Smuzhiyun /* Output, auxiliary ADC input */
27*4882a593Smuzhiyun #define ADIS16209_AUX_ADC_REG 0x08
28*4882a593Smuzhiyun /* Output, temperature */
29*4882a593Smuzhiyun #define ADIS16209_TEMP_OUT_REG 0x0A
30*4882a593Smuzhiyun /* Output, +/- 90 degrees X-axis inclination */
31*4882a593Smuzhiyun #define ADIS16209_XINCL_OUT_REG 0x0C
32*4882a593Smuzhiyun #define ADIS16209_YINCL_OUT_REG 0x0E
33*4882a593Smuzhiyun /* Output, +/-180 vertical rotational position */
34*4882a593Smuzhiyun #define ADIS16209_ROT_OUT_REG 0x10
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun /*
37*4882a593Smuzhiyun * Calibration Register Definitions.
38*4882a593Smuzhiyun * Acceleration, inclination or rotation offset null.
39*4882a593Smuzhiyun */
40*4882a593Smuzhiyun #define ADIS16209_XACCL_NULL_REG 0x12
41*4882a593Smuzhiyun #define ADIS16209_YACCL_NULL_REG 0x14
42*4882a593Smuzhiyun #define ADIS16209_XINCL_NULL_REG 0x16
43*4882a593Smuzhiyun #define ADIS16209_YINCL_NULL_REG 0x18
44*4882a593Smuzhiyun #define ADIS16209_ROT_NULL_REG 0x1A
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun /* Alarm Register Definitions */
47*4882a593Smuzhiyun #define ADIS16209_ALM_MAG1_REG 0x20
48*4882a593Smuzhiyun #define ADIS16209_ALM_MAG2_REG 0x22
49*4882a593Smuzhiyun #define ADIS16209_ALM_SMPL1_REG 0x24
50*4882a593Smuzhiyun #define ADIS16209_ALM_SMPL2_REG 0x26
51*4882a593Smuzhiyun #define ADIS16209_ALM_CTRL_REG 0x28
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun #define ADIS16209_AUX_DAC_REG 0x30
54*4882a593Smuzhiyun #define ADIS16209_GPIO_CTRL_REG 0x32
55*4882a593Smuzhiyun #define ADIS16209_SMPL_PRD_REG 0x36
56*4882a593Smuzhiyun #define ADIS16209_AVG_CNT_REG 0x38
57*4882a593Smuzhiyun #define ADIS16209_SLP_CNT_REG 0x3A
58*4882a593Smuzhiyun
59*4882a593Smuzhiyun #define ADIS16209_MSC_CTRL_REG 0x34
60*4882a593Smuzhiyun #define ADIS16209_MSC_CTRL_PWRUP_SELF_TEST BIT(10)
61*4882a593Smuzhiyun #define ADIS16209_MSC_CTRL_SELF_TEST_EN BIT(8)
62*4882a593Smuzhiyun #define ADIS16209_MSC_CTRL_DATA_RDY_EN BIT(2)
63*4882a593Smuzhiyun /* Data-ready polarity: 1 = active high, 0 = active low */
64*4882a593Smuzhiyun #define ADIS16209_MSC_CTRL_ACTIVE_HIGH BIT(1)
65*4882a593Smuzhiyun #define ADIS16209_MSC_CTRL_DATA_RDY_DIO2 BIT(0)
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun #define ADIS16209_STAT_REG 0x3C
68*4882a593Smuzhiyun #define ADIS16209_STAT_ALARM2 BIT(9)
69*4882a593Smuzhiyun #define ADIS16209_STAT_ALARM1 BIT(8)
70*4882a593Smuzhiyun #define ADIS16209_STAT_SELFTEST_FAIL_BIT 5
71*4882a593Smuzhiyun #define ADIS16209_STAT_SPI_FAIL_BIT 3
72*4882a593Smuzhiyun #define ADIS16209_STAT_FLASH_UPT_FAIL_BIT 2
73*4882a593Smuzhiyun /* Power supply above 3.625 V */
74*4882a593Smuzhiyun #define ADIS16209_STAT_POWER_HIGH_BIT 1
75*4882a593Smuzhiyun /* Power supply below 2.975 V */
76*4882a593Smuzhiyun #define ADIS16209_STAT_POWER_LOW_BIT 0
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun #define ADIS16209_CMD_REG 0x3E
79*4882a593Smuzhiyun #define ADIS16209_CMD_SW_RESET BIT(7)
80*4882a593Smuzhiyun #define ADIS16209_CMD_CLEAR_STAT BIT(4)
81*4882a593Smuzhiyun #define ADIS16209_CMD_FACTORY_CAL BIT(1)
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun #define ADIS16209_ERROR_ACTIVE BIT(14)
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun enum adis16209_scan {
86*4882a593Smuzhiyun ADIS16209_SCAN_SUPPLY,
87*4882a593Smuzhiyun ADIS16209_SCAN_ACC_X,
88*4882a593Smuzhiyun ADIS16209_SCAN_ACC_Y,
89*4882a593Smuzhiyun ADIS16209_SCAN_AUX_ADC,
90*4882a593Smuzhiyun ADIS16209_SCAN_TEMP,
91*4882a593Smuzhiyun ADIS16209_SCAN_INCLI_X,
92*4882a593Smuzhiyun ADIS16209_SCAN_INCLI_Y,
93*4882a593Smuzhiyun ADIS16209_SCAN_ROT,
94*4882a593Smuzhiyun };
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun static const u8 adis16209_addresses[8][1] = {
97*4882a593Smuzhiyun [ADIS16209_SCAN_SUPPLY] = { },
98*4882a593Smuzhiyun [ADIS16209_SCAN_AUX_ADC] = { },
99*4882a593Smuzhiyun [ADIS16209_SCAN_ACC_X] = { ADIS16209_XACCL_NULL_REG },
100*4882a593Smuzhiyun [ADIS16209_SCAN_ACC_Y] = { ADIS16209_YACCL_NULL_REG },
101*4882a593Smuzhiyun [ADIS16209_SCAN_INCLI_X] = { ADIS16209_XINCL_NULL_REG },
102*4882a593Smuzhiyun [ADIS16209_SCAN_INCLI_Y] = { ADIS16209_YINCL_NULL_REG },
103*4882a593Smuzhiyun [ADIS16209_SCAN_ROT] = { },
104*4882a593Smuzhiyun [ADIS16209_SCAN_TEMP] = { },
105*4882a593Smuzhiyun };
106*4882a593Smuzhiyun
adis16209_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)107*4882a593Smuzhiyun static int adis16209_write_raw(struct iio_dev *indio_dev,
108*4882a593Smuzhiyun struct iio_chan_spec const *chan,
109*4882a593Smuzhiyun int val,
110*4882a593Smuzhiyun int val2,
111*4882a593Smuzhiyun long mask)
112*4882a593Smuzhiyun {
113*4882a593Smuzhiyun struct adis *st = iio_priv(indio_dev);
114*4882a593Smuzhiyun int m;
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun if (mask != IIO_CHAN_INFO_CALIBBIAS)
117*4882a593Smuzhiyun return -EINVAL;
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun switch (chan->type) {
120*4882a593Smuzhiyun case IIO_ACCEL:
121*4882a593Smuzhiyun case IIO_INCLI:
122*4882a593Smuzhiyun m = GENMASK(13, 0);
123*4882a593Smuzhiyun break;
124*4882a593Smuzhiyun default:
125*4882a593Smuzhiyun return -EINVAL;
126*4882a593Smuzhiyun }
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun return adis_write_reg_16(st, adis16209_addresses[chan->scan_index][0],
129*4882a593Smuzhiyun val & m);
130*4882a593Smuzhiyun }
131*4882a593Smuzhiyun
adis16209_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)132*4882a593Smuzhiyun static int adis16209_read_raw(struct iio_dev *indio_dev,
133*4882a593Smuzhiyun struct iio_chan_spec const *chan,
134*4882a593Smuzhiyun int *val, int *val2,
135*4882a593Smuzhiyun long mask)
136*4882a593Smuzhiyun {
137*4882a593Smuzhiyun struct adis *st = iio_priv(indio_dev);
138*4882a593Smuzhiyun int ret;
139*4882a593Smuzhiyun int bits;
140*4882a593Smuzhiyun u8 addr;
141*4882a593Smuzhiyun s16 val16;
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun switch (mask) {
144*4882a593Smuzhiyun case IIO_CHAN_INFO_RAW:
145*4882a593Smuzhiyun return adis_single_conversion(indio_dev, chan,
146*4882a593Smuzhiyun ADIS16209_ERROR_ACTIVE, val);
147*4882a593Smuzhiyun case IIO_CHAN_INFO_SCALE:
148*4882a593Smuzhiyun switch (chan->type) {
149*4882a593Smuzhiyun case IIO_VOLTAGE:
150*4882a593Smuzhiyun *val = 0;
151*4882a593Smuzhiyun switch (chan->channel) {
152*4882a593Smuzhiyun case 0:
153*4882a593Smuzhiyun *val2 = 305180; /* 0.30518 mV */
154*4882a593Smuzhiyun break;
155*4882a593Smuzhiyun case 1:
156*4882a593Smuzhiyun *val2 = 610500; /* 0.6105 mV */
157*4882a593Smuzhiyun break;
158*4882a593Smuzhiyun default:
159*4882a593Smuzhiyun return -EINVAL;
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun return IIO_VAL_INT_PLUS_MICRO;
162*4882a593Smuzhiyun case IIO_TEMP:
163*4882a593Smuzhiyun *val = -470;
164*4882a593Smuzhiyun *val2 = 0;
165*4882a593Smuzhiyun return IIO_VAL_INT_PLUS_MICRO;
166*4882a593Smuzhiyun case IIO_ACCEL:
167*4882a593Smuzhiyun /*
168*4882a593Smuzhiyun * IIO base unit for sensitivity of accelerometer
169*4882a593Smuzhiyun * is milli g.
170*4882a593Smuzhiyun * 1 LSB represents 0.244 mg.
171*4882a593Smuzhiyun */
172*4882a593Smuzhiyun *val = 0;
173*4882a593Smuzhiyun *val2 = IIO_G_TO_M_S_2(244140);
174*4882a593Smuzhiyun return IIO_VAL_INT_PLUS_NANO;
175*4882a593Smuzhiyun case IIO_INCLI:
176*4882a593Smuzhiyun case IIO_ROT:
177*4882a593Smuzhiyun /*
178*4882a593Smuzhiyun * IIO base units for rotation are degrees.
179*4882a593Smuzhiyun * 1 LSB represents 0.025 milli degrees.
180*4882a593Smuzhiyun */
181*4882a593Smuzhiyun *val = 0;
182*4882a593Smuzhiyun *val2 = 25000;
183*4882a593Smuzhiyun return IIO_VAL_INT_PLUS_MICRO;
184*4882a593Smuzhiyun default:
185*4882a593Smuzhiyun return -EINVAL;
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun break;
188*4882a593Smuzhiyun case IIO_CHAN_INFO_OFFSET:
189*4882a593Smuzhiyun /*
190*4882a593Smuzhiyun * The raw ADC value is 0x4FE when the temperature
191*4882a593Smuzhiyun * is 45 degrees and the scale factor per milli
192*4882a593Smuzhiyun * degree celcius is -470.
193*4882a593Smuzhiyun */
194*4882a593Smuzhiyun *val = 25000 / -470 - 0x4FE;
195*4882a593Smuzhiyun return IIO_VAL_INT;
196*4882a593Smuzhiyun case IIO_CHAN_INFO_CALIBBIAS:
197*4882a593Smuzhiyun switch (chan->type) {
198*4882a593Smuzhiyun case IIO_ACCEL:
199*4882a593Smuzhiyun bits = 14;
200*4882a593Smuzhiyun break;
201*4882a593Smuzhiyun default:
202*4882a593Smuzhiyun return -EINVAL;
203*4882a593Smuzhiyun }
204*4882a593Smuzhiyun addr = adis16209_addresses[chan->scan_index][0];
205*4882a593Smuzhiyun ret = adis_read_reg_16(st, addr, &val16);
206*4882a593Smuzhiyun if (ret)
207*4882a593Smuzhiyun return ret;
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun *val = sign_extend32(val16, bits - 1);
210*4882a593Smuzhiyun return IIO_VAL_INT;
211*4882a593Smuzhiyun }
212*4882a593Smuzhiyun return -EINVAL;
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun static const struct iio_chan_spec adis16209_channels[] = {
216*4882a593Smuzhiyun ADIS_SUPPLY_CHAN(ADIS16209_SUPPLY_OUT_REG, ADIS16209_SCAN_SUPPLY,
217*4882a593Smuzhiyun 0, 14),
218*4882a593Smuzhiyun ADIS_TEMP_CHAN(ADIS16209_TEMP_OUT_REG, ADIS16209_SCAN_TEMP, 0, 12),
219*4882a593Smuzhiyun ADIS_ACCEL_CHAN(X, ADIS16209_XACCL_OUT_REG, ADIS16209_SCAN_ACC_X,
220*4882a593Smuzhiyun BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),
221*4882a593Smuzhiyun ADIS_ACCEL_CHAN(Y, ADIS16209_YACCL_OUT_REG, ADIS16209_SCAN_ACC_Y,
222*4882a593Smuzhiyun BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14),
223*4882a593Smuzhiyun ADIS_AUX_ADC_CHAN(ADIS16209_AUX_ADC_REG, ADIS16209_SCAN_AUX_ADC, 0, 12),
224*4882a593Smuzhiyun ADIS_INCLI_CHAN(X, ADIS16209_XINCL_OUT_REG, ADIS16209_SCAN_INCLI_X,
225*4882a593Smuzhiyun 0, 0, 14),
226*4882a593Smuzhiyun ADIS_INCLI_CHAN(Y, ADIS16209_YINCL_OUT_REG, ADIS16209_SCAN_INCLI_Y,
227*4882a593Smuzhiyun 0, 0, 14),
228*4882a593Smuzhiyun ADIS_ROT_CHAN(X, ADIS16209_ROT_OUT_REG, ADIS16209_SCAN_ROT, 0, 0, 14),
229*4882a593Smuzhiyun IIO_CHAN_SOFT_TIMESTAMP(8)
230*4882a593Smuzhiyun };
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun static const struct iio_info adis16209_info = {
233*4882a593Smuzhiyun .read_raw = adis16209_read_raw,
234*4882a593Smuzhiyun .write_raw = adis16209_write_raw,
235*4882a593Smuzhiyun .update_scan_mode = adis_update_scan_mode,
236*4882a593Smuzhiyun };
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun static const char * const adis16209_status_error_msgs[] = {
239*4882a593Smuzhiyun [ADIS16209_STAT_SELFTEST_FAIL_BIT] = "Self test failure",
240*4882a593Smuzhiyun [ADIS16209_STAT_SPI_FAIL_BIT] = "SPI failure",
241*4882a593Smuzhiyun [ADIS16209_STAT_FLASH_UPT_FAIL_BIT] = "Flash update failed",
242*4882a593Smuzhiyun [ADIS16209_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
243*4882a593Smuzhiyun [ADIS16209_STAT_POWER_LOW_BIT] = "Power supply below 2.975V",
244*4882a593Smuzhiyun };
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun static const struct adis_timeout adis16209_timeouts = {
247*4882a593Smuzhiyun .reset_ms = ADIS16209_STARTUP_DELAY_MS,
248*4882a593Smuzhiyun .self_test_ms = ADIS16209_STARTUP_DELAY_MS,
249*4882a593Smuzhiyun .sw_reset_ms = ADIS16209_STARTUP_DELAY_MS,
250*4882a593Smuzhiyun };
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun static const struct adis_data adis16209_data = {
253*4882a593Smuzhiyun .read_delay = 30,
254*4882a593Smuzhiyun .msc_ctrl_reg = ADIS16209_MSC_CTRL_REG,
255*4882a593Smuzhiyun .glob_cmd_reg = ADIS16209_CMD_REG,
256*4882a593Smuzhiyun .diag_stat_reg = ADIS16209_STAT_REG,
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun .self_test_mask = ADIS16209_MSC_CTRL_SELF_TEST_EN,
259*4882a593Smuzhiyun .self_test_reg = ADIS16209_MSC_CTRL_REG,
260*4882a593Smuzhiyun .self_test_no_autoclear = true,
261*4882a593Smuzhiyun .timeouts = &adis16209_timeouts,
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun .status_error_msgs = adis16209_status_error_msgs,
264*4882a593Smuzhiyun .status_error_mask = BIT(ADIS16209_STAT_SELFTEST_FAIL_BIT) |
265*4882a593Smuzhiyun BIT(ADIS16209_STAT_SPI_FAIL_BIT) |
266*4882a593Smuzhiyun BIT(ADIS16209_STAT_FLASH_UPT_FAIL_BIT) |
267*4882a593Smuzhiyun BIT(ADIS16209_STAT_POWER_HIGH_BIT) |
268*4882a593Smuzhiyun BIT(ADIS16209_STAT_POWER_LOW_BIT),
269*4882a593Smuzhiyun };
270*4882a593Smuzhiyun
adis16209_probe(struct spi_device * spi)271*4882a593Smuzhiyun static int adis16209_probe(struct spi_device *spi)
272*4882a593Smuzhiyun {
273*4882a593Smuzhiyun struct iio_dev *indio_dev;
274*4882a593Smuzhiyun struct adis *st;
275*4882a593Smuzhiyun int ret;
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
278*4882a593Smuzhiyun if (!indio_dev)
279*4882a593Smuzhiyun return -ENOMEM;
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun st = iio_priv(indio_dev);
282*4882a593Smuzhiyun spi_set_drvdata(spi, indio_dev);
283*4882a593Smuzhiyun
284*4882a593Smuzhiyun indio_dev->name = spi->dev.driver->name;
285*4882a593Smuzhiyun indio_dev->info = &adis16209_info;
286*4882a593Smuzhiyun indio_dev->channels = adis16209_channels;
287*4882a593Smuzhiyun indio_dev->num_channels = ARRAY_SIZE(adis16209_channels);
288*4882a593Smuzhiyun indio_dev->modes = INDIO_DIRECT_MODE;
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun ret = adis_init(st, indio_dev, spi, &adis16209_data);
291*4882a593Smuzhiyun if (ret)
292*4882a593Smuzhiyun return ret;
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun ret = devm_adis_setup_buffer_and_trigger(st, indio_dev, NULL);
295*4882a593Smuzhiyun if (ret)
296*4882a593Smuzhiyun return ret;
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun ret = adis_initial_startup(st);
299*4882a593Smuzhiyun if (ret)
300*4882a593Smuzhiyun return ret;
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun return devm_iio_device_register(&spi->dev, indio_dev);
303*4882a593Smuzhiyun }
304*4882a593Smuzhiyun
305*4882a593Smuzhiyun static struct spi_driver adis16209_driver = {
306*4882a593Smuzhiyun .driver = {
307*4882a593Smuzhiyun .name = "adis16209",
308*4882a593Smuzhiyun },
309*4882a593Smuzhiyun .probe = adis16209_probe,
310*4882a593Smuzhiyun };
311*4882a593Smuzhiyun module_spi_driver(adis16209_driver);
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
314*4882a593Smuzhiyun MODULE_DESCRIPTION("Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer");
315*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
316*4882a593Smuzhiyun MODULE_ALIAS("spi:adis16209");
317