Commit | Line | Data |
---|---|---|
bb6f19ea BS |
1 | /* |
2 | * ADIS16204 Programmable High-g Digital Impact Sensor and Recorder | |
3 | * | |
4 | * Copyright 2010 Analog Devices Inc. | |
5 | * | |
6 | * Licensed under the GPL-2 or later. | |
7 | */ | |
8 | ||
9 | #include <linux/interrupt.h> | |
10 | #include <linux/irq.h> | |
bb6f19ea BS |
11 | #include <linux/delay.h> |
12 | #include <linux/mutex.h> | |
13 | #include <linux/device.h> | |
14 | #include <linux/kernel.h> | |
15 | #include <linux/spi/spi.h> | |
16 | #include <linux/slab.h> | |
17 | #include <linux/sysfs.h> | |
18 | #include <linux/list.h> | |
99c97852 | 19 | #include <linux/module.h> |
bb6f19ea | 20 | |
06458e27 JC |
21 | #include <linux/iio/iio.h> |
22 | #include <linux/iio/sysfs.h> | |
23 | #include <linux/iio/buffer.h> | |
ec04cb04 | 24 | #include <linux/iio/imu/adis.h> |
bb6f19ea BS |
25 | |
26 | #include "adis16204.h" | |
c9b9e49e JC |
27 | |
28 | /* Unique to this driver currently */ | |
bb6f19ea | 29 | |
5bd22f51 LPC |
30 | static const u8 adis16204_addresses[][2] = { |
31 | [ADIS16204_SCAN_ACC_X] = { ADIS16204_XACCL_NULL, ADIS16204_X_PEAK_OUT }, | |
32 | [ADIS16204_SCAN_ACC_Y] = { ADIS16204_YACCL_NULL, ADIS16204_Y_PEAK_OUT }, | |
33 | [ADIS16204_SCAN_ACC_XY] = { 0, ADIS16204_XY_PEAK_OUT }, | |
ad3eb9ab | 34 | }; |
c9b9e49e | 35 | |
ad3eb9ab JC |
36 | static int adis16204_read_raw(struct iio_dev *indio_dev, |
37 | struct iio_chan_spec const *chan, | |
38 | int *val, int *val2, | |
39 | long mask) | |
40 | { | |
5bd22f51 | 41 | struct adis *st = iio_priv(indio_dev); |
ad3eb9ab JC |
42 | int ret; |
43 | int bits; | |
44 | u8 addr; | |
45 | s16 val16; | |
c9b9e49e | 46 | int addrind; |
ad3eb9ab JC |
47 | |
48 | switch (mask) { | |
31313fc6 | 49 | case IIO_CHAN_INFO_RAW: |
5bd22f51 LPC |
50 | return adis_single_conversion(indio_dev, chan, |
51 | ADIS16204_ERROR_ACTIVE, val); | |
c8a9f805 | 52 | case IIO_CHAN_INFO_SCALE: |
ad3eb9ab | 53 | switch (chan->type) { |
6835cb6b | 54 | case IIO_VOLTAGE: |
9cc19b81 LPC |
55 | if (chan->channel == 0) { |
56 | *val = 1; | |
57 | *val2 = 220000; /* 1.22 mV */ | |
58 | } else { | |
59 | *val = 0; | |
60 | *val2 = 610000; /* 0.61 mV */ | |
61 | } | |
ad3eb9ab JC |
62 | return IIO_VAL_INT_PLUS_MICRO; |
63 | case IIO_TEMP: | |
9cc19b81 LPC |
64 | *val = -470; /* 0.47 C */ |
65 | *val2 = 0; | |
ad3eb9ab JC |
66 | return IIO_VAL_INT_PLUS_MICRO; |
67 | case IIO_ACCEL: | |
68 | *val = 0; | |
f699d102 JC |
69 | switch (chan->channel2) { |
70 | case IIO_MOD_X: | |
71 | case IIO_MOD_ROOT_SUM_SQUARED_X_Y: | |
9cc19b81 | 72 | *val2 = IIO_G_TO_M_S_2(17125); /* 17.125 mg */ |
f699d102 JC |
73 | break; |
74 | case IIO_MOD_Y: | |
75 | case IIO_MOD_Z: | |
9cc19b81 | 76 | *val2 = IIO_G_TO_M_S_2(8407); /* 8.407 mg */ |
f699d102 JC |
77 | break; |
78 | } | |
ad3eb9ab JC |
79 | return IIO_VAL_INT_PLUS_MICRO; |
80 | default: | |
81 | return -EINVAL; | |
82 | } | |
83 | break; | |
c8a9f805 | 84 | case IIO_CHAN_INFO_OFFSET: |
9cc19b81 | 85 | *val = 25000 / -470 - 1278; /* 25 C = 1278 */ |
ad3eb9ab | 86 | return IIO_VAL_INT; |
c8a9f805 JC |
87 | case IIO_CHAN_INFO_CALIBBIAS: |
88 | case IIO_CHAN_INFO_PEAK: | |
89 | if (mask == IIO_CHAN_INFO_CALIBBIAS) { | |
ad3eb9ab | 90 | bits = 12; |
5bd22f51 | 91 | addrind = 0; |
c9b9e49e JC |
92 | } else { /* PEAK_SEPARATE */ |
93 | bits = 14; | |
5bd22f51 | 94 | addrind = 1; |
c9b9e49e | 95 | } |
ad3eb9ab | 96 | mutex_lock(&indio_dev->mlock); |
5bd22f51 LPC |
97 | addr = adis16204_addresses[chan->scan_index][addrind]; |
98 | ret = adis_read_reg_16(st, addr, &val16); | |
ad3eb9ab JC |
99 | if (ret) { |
100 | mutex_unlock(&indio_dev->mlock); | |
101 | return ret; | |
102 | } | |
103 | val16 &= (1 << bits) - 1; | |
104 | val16 = (s16)(val16 << (16 - bits)) >> (16 - bits); | |
105 | *val = val16; | |
106 | mutex_unlock(&indio_dev->mlock); | |
107 | return IIO_VAL_INT; | |
108 | } | |
109 | return -EINVAL; | |
110 | } | |
111 | ||
112 | static int adis16204_write_raw(struct iio_dev *indio_dev, | |
113 | struct iio_chan_spec const *chan, | |
114 | int val, | |
115 | int val2, | |
116 | long mask) | |
117 | { | |
5bd22f51 | 118 | struct adis *st = iio_priv(indio_dev); |
ad3eb9ab JC |
119 | int bits; |
120 | s16 val16; | |
121 | u8 addr; | |
d7b79519 | 122 | |
ad3eb9ab | 123 | switch (mask) { |
c8a9f805 | 124 | case IIO_CHAN_INFO_CALIBBIAS: |
ad3eb9ab JC |
125 | switch (chan->type) { |
126 | case IIO_ACCEL: | |
127 | bits = 12; | |
128 | break; | |
129 | default: | |
130 | return -EINVAL; | |
73327b4c | 131 | } |
ad3eb9ab | 132 | val16 = val & ((1 << bits) - 1); |
5bd22f51 LPC |
133 | addr = adis16204_addresses[chan->scan_index][1]; |
134 | return adis_write_reg_16(st, addr, val16); | |
ad3eb9ab JC |
135 | } |
136 | return -EINVAL; | |
137 | } | |
138 | ||
f4e4b955 | 139 | static const struct iio_chan_spec adis16204_channels[] = { |
82695ef5 JC |
140 | ADIS_SUPPLY_CHAN(ADIS16204_SUPPLY_OUT, ADIS16204_SCAN_SUPPLY, 0, 12), |
141 | ADIS_AUX_ADC_CHAN(ADIS16204_AUX_ADC, ADIS16204_SCAN_AUX_ADC, 0, 12), | |
142 | ADIS_TEMP_CHAN(ADIS16204_TEMP_OUT, ADIS16204_SCAN_TEMP, 0, 12), | |
5bd22f51 | 143 | ADIS_ACCEL_CHAN(X, ADIS16204_XACCL_OUT, ADIS16204_SCAN_ACC_X, |
9963bce7 HM |
144 | BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), |
145 | 0, 14), | |
5bd22f51 | 146 | ADIS_ACCEL_CHAN(Y, ADIS16204_YACCL_OUT, ADIS16204_SCAN_ACC_Y, |
9963bce7 HM |
147 | BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), |
148 | 0, 14), | |
5bd22f51 | 149 | ADIS_ACCEL_CHAN(ROOT_SUM_SQUARED_X_Y, ADIS16204_XY_RSS_OUT, |
9963bce7 | 150 | ADIS16204_SCAN_ACC_XY, BIT(IIO_CHAN_INFO_PEAK), 0, 14), |
ad3eb9ab | 151 | IIO_CHAN_SOFT_TIMESTAMP(5), |
bb6f19ea BS |
152 | }; |
153 | ||
6fe8135f | 154 | static const struct iio_info adis16204_info = { |
6fe8135f JC |
155 | .read_raw = &adis16204_read_raw, |
156 | .write_raw = &adis16204_write_raw, | |
aacff892 | 157 | .update_scan_mode = adis_update_scan_mode, |
6fe8135f JC |
158 | .driver_module = THIS_MODULE, |
159 | }; | |
160 | ||
5bd22f51 LPC |
161 | static const char * const adis16204_status_error_msgs[] = { |
162 | [ADIS16204_DIAG_STAT_SELFTEST_FAIL_BIT] = "Self test failure", | |
163 | [ADIS16204_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure", | |
164 | [ADIS16204_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed", | |
165 | [ADIS16204_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V", | |
166 | [ADIS16204_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 2.975V", | |
167 | }; | |
168 | ||
169 | static const struct adis_data adis16204_data = { | |
170 | .read_delay = 20, | |
171 | .msc_ctrl_reg = ADIS16204_MSC_CTRL, | |
172 | .glob_cmd_reg = ADIS16204_GLOB_CMD, | |
173 | .diag_stat_reg = ADIS16204_DIAG_STAT, | |
174 | ||
175 | .self_test_mask = ADIS16204_MSC_CTRL_SELF_TEST_EN, | |
176 | .startup_delay = ADIS16204_STARTUP_DELAY, | |
177 | ||
178 | .status_error_msgs = adis16204_status_error_msgs, | |
179 | .status_error_mask = BIT(ADIS16204_DIAG_STAT_SELFTEST_FAIL_BIT) | | |
180 | BIT(ADIS16204_DIAG_STAT_SPI_FAIL_BIT) | | |
181 | BIT(ADIS16204_DIAG_STAT_FLASH_UPT_BIT) | | |
182 | BIT(ADIS16204_DIAG_STAT_POWER_HIGH_BIT) | | |
183 | BIT(ADIS16204_DIAG_STAT_POWER_LOW_BIT), | |
184 | }; | |
185 | ||
4ae1c61f | 186 | static int adis16204_probe(struct spi_device *spi) |
bb6f19ea | 187 | { |
26d25ae3 | 188 | int ret; |
5bd22f51 | 189 | struct adis *st; |
4de66bbb | 190 | struct iio_dev *indio_dev; |
bb6f19ea | 191 | |
4de66bbb | 192 | /* setup the industrialio driver allocated elements */ |
cdfb09a4 SK |
193 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); |
194 | if (!indio_dev) | |
195 | return -ENOMEM; | |
4de66bbb JC |
196 | st = iio_priv(indio_dev); |
197 | /* this is only used for removal purposes */ | |
198 | spi_set_drvdata(spi, indio_dev); | |
bb6f19ea | 199 | |
4de66bbb JC |
200 | indio_dev->name = spi->dev.driver->name; |
201 | indio_dev->dev.parent = &spi->dev; | |
202 | indio_dev->info = &adis16204_info; | |
203 | indio_dev->channels = adis16204_channels; | |
204 | indio_dev->num_channels = ARRAY_SIZE(adis16204_channels); | |
205 | indio_dev->modes = INDIO_DIRECT_MODE; | |
bb6f19ea | 206 | |
5bd22f51 | 207 | ret = adis_init(st, indio_dev, spi, &adis16204_data); |
bb6f19ea | 208 | if (ret) |
cdfb09a4 | 209 | return ret; |
bb6f19ea | 210 | |
5bd22f51 LPC |
211 | ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL); |
212 | if (ret) | |
cdfb09a4 | 213 | return ret; |
bb6f19ea BS |
214 | |
215 | /* Get the device into a sane initial state */ | |
5bd22f51 | 216 | ret = adis_initial_startup(st); |
bb6f19ea | 217 | if (ret) |
5bd22f51 | 218 | goto error_cleanup_buffer_trigger; |
26d25ae3 JC |
219 | ret = iio_device_register(indio_dev); |
220 | if (ret) | |
5bd22f51 | 221 | goto error_cleanup_buffer_trigger; |
26d25ae3 | 222 | |
bb6f19ea BS |
223 | return 0; |
224 | ||
5bd22f51 LPC |
225 | error_cleanup_buffer_trigger: |
226 | adis_cleanup_buffer_and_trigger(st, indio_dev); | |
bb6f19ea BS |
227 | return ret; |
228 | } | |
229 | ||
447d4f29 | 230 | static int adis16204_remove(struct spi_device *spi) |
bb6f19ea | 231 | { |
4de66bbb | 232 | struct iio_dev *indio_dev = spi_get_drvdata(spi); |
5bd22f51 | 233 | struct adis *st = iio_priv(indio_dev); |
bb6f19ea | 234 | |
d2fffd6c | 235 | iio_device_unregister(indio_dev); |
5bd22f51 | 236 | adis_cleanup_buffer_and_trigger(st, indio_dev); |
bb6f19ea BS |
237 | |
238 | return 0; | |
239 | } | |
240 | ||
241 | static struct spi_driver adis16204_driver = { | |
242 | .driver = { | |
243 | .name = "adis16204", | |
244 | .owner = THIS_MODULE, | |
245 | }, | |
246 | .probe = adis16204_probe, | |
e543acf0 | 247 | .remove = adis16204_remove, |
bb6f19ea | 248 | }; |
ae6ae6fe | 249 | module_spi_driver(adis16204_driver); |
bb6f19ea BS |
250 | |
251 | MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); | |
ad3eb9ab | 252 | MODULE_DESCRIPTION("ADIS16204 High-g Digital Impact Sensor and Recorder"); |
bb6f19ea | 253 | MODULE_LICENSE("GPL v2"); |
55e4390c | 254 | MODULE_ALIAS("spi:adis16204"); |