]>
Commit | Line | Data |
---|---|---|
671ece14 BS |
1 | /* |
2 | * ADIS16209 Programmable Digital Vibration Sensor driver | |
3 | * | |
4 | * Copyright 2010 Analog Devices Inc. | |
5 | * | |
6 | * Licensed under the GPL-2 or later. | |
7 | */ | |
8 | ||
671ece14 BS |
9 | #include <linux/delay.h> |
10 | #include <linux/mutex.h> | |
11 | #include <linux/device.h> | |
12 | #include <linux/kernel.h> | |
13 | #include <linux/spi/spi.h> | |
1cb6c1f5 | 14 | #include <linux/slab.h> |
671ece14 BS |
15 | #include <linux/sysfs.h> |
16 | #include <linux/list.h> | |
99c97852 | 17 | #include <linux/module.h> |
671ece14 | 18 | |
06458e27 JC |
19 | #include <linux/iio/iio.h> |
20 | #include <linux/iio/sysfs.h> | |
21 | #include <linux/iio/buffer.h> | |
ec04cb04 | 22 | #include <linux/iio/imu/adis.h> |
671ece14 BS |
23 | |
24 | #include "adis16209.h" | |
511fb29e LPC |
25 | |
26 | static const u8 adis16209_addresses[8][1] = { | |
27 | [ADIS16209_SCAN_SUPPLY] = { }, | |
28 | [ADIS16209_SCAN_AUX_ADC] = { }, | |
29 | [ADIS16209_SCAN_ACC_X] = { ADIS16209_XACCL_NULL }, | |
30 | [ADIS16209_SCAN_ACC_Y] = { ADIS16209_YACCL_NULL }, | |
31 | [ADIS16209_SCAN_INCLI_X] = { ADIS16209_XINCL_NULL }, | |
32 | [ADIS16209_SCAN_INCLI_Y] = { ADIS16209_YINCL_NULL }, | |
33 | [ADIS16209_SCAN_ROT] = { }, | |
34 | [ADIS16209_SCAN_TEMP] = { }, | |
a34c26dc JC |
35 | }; |
36 | ||
37 | static int adis16209_write_raw(struct iio_dev *indio_dev, | |
38 | struct iio_chan_spec const *chan, | |
39 | int val, | |
40 | int val2, | |
41 | long mask) | |
42 | { | |
511fb29e | 43 | struct adis *st = iio_priv(indio_dev); |
a34c26dc JC |
44 | int bits; |
45 | s16 val16; | |
46 | u8 addr; | |
d7b79519 | 47 | |
a34c26dc | 48 | switch (mask) { |
c8a9f805 | 49 | case IIO_CHAN_INFO_CALIBBIAS: |
a34c26dc JC |
50 | switch (chan->type) { |
51 | case IIO_ACCEL: | |
52 | case IIO_INCLI: | |
53 | bits = 14; | |
54 | break; | |
55 | default: | |
56 | return -EINVAL; | |
73327b4c | 57 | } |
a34c26dc | 58 | val16 = val & ((1 << bits) - 1); |
511fb29e LPC |
59 | addr = adis16209_addresses[chan->scan_index][0]; |
60 | return adis_write_reg_16(st, addr, val16); | |
a34c26dc JC |
61 | } |
62 | return -EINVAL; | |
63 | } | |
64 | ||
65 | static int adis16209_read_raw(struct iio_dev *indio_dev, | |
66 | struct iio_chan_spec const *chan, | |
67 | int *val, int *val2, | |
68 | long mask) | |
69 | { | |
511fb29e | 70 | struct adis *st = iio_priv(indio_dev); |
a34c26dc JC |
71 | int ret; |
72 | int bits; | |
73 | u8 addr; | |
74 | s16 val16; | |
75 | ||
76 | switch (mask) { | |
31313fc6 | 77 | case IIO_CHAN_INFO_RAW: |
511fb29e LPC |
78 | return adis_single_conversion(indio_dev, chan, |
79 | ADIS16209_ERROR_ACTIVE, val); | |
c8a9f805 | 80 | case IIO_CHAN_INFO_SCALE: |
a34c26dc | 81 | switch (chan->type) { |
6835cb6b | 82 | case IIO_VOLTAGE: |
a34c26dc JC |
83 | *val = 0; |
84 | if (chan->channel == 0) | |
d5304b77 | 85 | *val2 = 305180; /* 0.30518 mV */ |
a34c26dc | 86 | else |
d5304b77 | 87 | *val2 = 610500; /* 0.6105 mV */ |
a34c26dc JC |
88 | return IIO_VAL_INT_PLUS_MICRO; |
89 | case IIO_TEMP: | |
d5304b77 LPC |
90 | *val = -470; /* -0.47 C */ |
91 | *val2 = 0; | |
a34c26dc JC |
92 | return IIO_VAL_INT_PLUS_MICRO; |
93 | case IIO_ACCEL: | |
94 | *val = 0; | |
d5304b77 LPC |
95 | *val2 = IIO_G_TO_M_S_2(244140); /* 0.244140 mg */ |
96 | return IIO_VAL_INT_PLUS_NANO; | |
a34c26dc | 97 | case IIO_INCLI: |
d5304b77 | 98 | case IIO_ROT: |
a34c26dc | 99 | *val = 0; |
d5304b77 | 100 | *val2 = 25000; /* 0.025 degree */ |
a34c26dc JC |
101 | return IIO_VAL_INT_PLUS_MICRO; |
102 | default: | |
103 | return -EINVAL; | |
104 | } | |
105 | break; | |
c8a9f805 | 106 | case IIO_CHAN_INFO_OFFSET: |
d5304b77 | 107 | *val = 25000 / -470 - 0x4FE; /* 25 C = 0x4FE */ |
a34c26dc | 108 | return IIO_VAL_INT; |
c8a9f805 | 109 | case IIO_CHAN_INFO_CALIBBIAS: |
a34c26dc JC |
110 | switch (chan->type) { |
111 | case IIO_ACCEL: | |
112 | bits = 14; | |
113 | break; | |
114 | default: | |
115 | return -EINVAL; | |
73327b4c | 116 | } |
a34c26dc | 117 | mutex_lock(&indio_dev->mlock); |
511fb29e LPC |
118 | addr = adis16209_addresses[chan->scan_index][0]; |
119 | ret = adis_read_reg_16(st, addr, &val16); | |
a34c26dc JC |
120 | if (ret) { |
121 | mutex_unlock(&indio_dev->mlock); | |
122 | return ret; | |
123 | } | |
124 | val16 &= (1 << bits) - 1; | |
125 | val16 = (s16)(val16 << (16 - bits)) >> (16 - bits); | |
126 | *val = val16; | |
127 | mutex_unlock(&indio_dev->mlock); | |
128 | return IIO_VAL_INT; | |
129 | } | |
130 | return -EINVAL; | |
131 | } | |
132 | ||
f4e4b955 | 133 | static const struct iio_chan_spec adis16209_channels[] = { |
82695ef5 JC |
134 | ADIS_SUPPLY_CHAN(ADIS16209_SUPPLY_OUT, ADIS16209_SCAN_SUPPLY, 0, 14), |
135 | ADIS_TEMP_CHAN(ADIS16209_TEMP_OUT, ADIS16209_SCAN_TEMP, 0, 12), | |
511fb29e | 136 | ADIS_ACCEL_CHAN(X, ADIS16209_XACCL_OUT, ADIS16209_SCAN_ACC_X, |
9963bce7 | 137 | BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), |
511fb29e | 138 | ADIS_ACCEL_CHAN(Y, ADIS16209_YACCL_OUT, ADIS16209_SCAN_ACC_Y, |
9963bce7 | 139 | BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), |
82695ef5 JC |
140 | ADIS_AUX_ADC_CHAN(ADIS16209_AUX_ADC, ADIS16209_SCAN_AUX_ADC, 0, 12), |
141 | ADIS_INCLI_CHAN(X, ADIS16209_XINCL_OUT, ADIS16209_SCAN_INCLI_X, | |
9963bce7 | 142 | 0, 0, 14), |
82695ef5 | 143 | ADIS_INCLI_CHAN(Y, ADIS16209_YINCL_OUT, ADIS16209_SCAN_INCLI_Y, |
9963bce7 | 144 | 0, 0, 14), |
82695ef5 | 145 | ADIS_ROT_CHAN(X, ADIS16209_ROT_OUT, ADIS16209_SCAN_ROT, 0, 0, 14), |
a34c26dc JC |
146 | IIO_CHAN_SOFT_TIMESTAMP(8) |
147 | }; | |
671ece14 | 148 | |
6fe8135f | 149 | static const struct iio_info adis16209_info = { |
6fe8135f JC |
150 | .read_raw = &adis16209_read_raw, |
151 | .write_raw = &adis16209_write_raw, | |
aacff892 | 152 | .update_scan_mode = adis_update_scan_mode, |
6fe8135f JC |
153 | .driver_module = THIS_MODULE, |
154 | }; | |
155 | ||
511fb29e LPC |
156 | static const char * const adis16209_status_error_msgs[] = { |
157 | [ADIS16209_DIAG_STAT_SELFTEST_FAIL_BIT] = "Self test failure", | |
158 | [ADIS16209_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure", | |
159 | [ADIS16209_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed", | |
160 | [ADIS16209_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V", | |
161 | [ADIS16209_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V", | |
162 | }; | |
163 | ||
164 | static const struct adis_data adis16209_data = { | |
165 | .read_delay = 30, | |
166 | .msc_ctrl_reg = ADIS16209_MSC_CTRL, | |
167 | .glob_cmd_reg = ADIS16209_GLOB_CMD, | |
168 | .diag_stat_reg = ADIS16209_DIAG_STAT, | |
169 | ||
170 | .self_test_mask = ADIS16209_MSC_CTRL_SELF_TEST_EN, | |
5d116edd | 171 | .self_test_no_autoclear = true, |
511fb29e LPC |
172 | .startup_delay = ADIS16209_STARTUP_DELAY, |
173 | ||
174 | .status_error_msgs = adis16209_status_error_msgs, | |
175 | .status_error_mask = BIT(ADIS16209_DIAG_STAT_SELFTEST_FAIL_BIT) | | |
176 | BIT(ADIS16209_DIAG_STAT_SPI_FAIL_BIT) | | |
177 | BIT(ADIS16209_DIAG_STAT_FLASH_UPT_BIT) | | |
178 | BIT(ADIS16209_DIAG_STAT_POWER_HIGH_BIT) | | |
179 | BIT(ADIS16209_DIAG_STAT_POWER_LOW_BIT), | |
180 | }; | |
181 | ||
4ae1c61f | 182 | static int adis16209_probe(struct spi_device *spi) |
671ece14 | 183 | { |
26d25ae3 | 184 | int ret; |
511fb29e | 185 | struct adis *st; |
35212dfa | 186 | struct iio_dev *indio_dev; |
671ece14 | 187 | |
35212dfa | 188 | /* setup the industrialio driver allocated elements */ |
864bf5cd SK |
189 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); |
190 | if (!indio_dev) | |
191 | return -ENOMEM; | |
35212dfa JC |
192 | st = iio_priv(indio_dev); |
193 | /* this is only used for removal purposes */ | |
194 | spi_set_drvdata(spi, indio_dev); | |
671ece14 | 195 | |
35212dfa JC |
196 | indio_dev->name = spi->dev.driver->name; |
197 | indio_dev->dev.parent = &spi->dev; | |
198 | indio_dev->info = &adis16209_info; | |
199 | indio_dev->channels = adis16209_channels; | |
200 | indio_dev->num_channels = ARRAY_SIZE(adis16209_channels); | |
201 | indio_dev->modes = INDIO_DIRECT_MODE; | |
671ece14 | 202 | |
511fb29e LPC |
203 | ret = adis_init(st, indio_dev, spi, &adis16209_data); |
204 | if (ret) | |
864bf5cd | 205 | return ret; |
511fb29e | 206 | ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL); |
671ece14 | 207 | if (ret) |
864bf5cd | 208 | return ret; |
671ece14 BS |
209 | |
210 | /* Get the device into a sane initial state */ | |
511fb29e | 211 | ret = adis_initial_startup(st); |
671ece14 | 212 | if (ret) |
511fb29e | 213 | goto error_cleanup_buffer_trigger; |
26d25ae3 JC |
214 | ret = iio_device_register(indio_dev); |
215 | if (ret) | |
511fb29e | 216 | goto error_cleanup_buffer_trigger; |
26d25ae3 | 217 | |
671ece14 BS |
218 | return 0; |
219 | ||
511fb29e LPC |
220 | error_cleanup_buffer_trigger: |
221 | adis_cleanup_buffer_and_trigger(st, indio_dev); | |
671ece14 BS |
222 | return ret; |
223 | } | |
224 | ||
447d4f29 | 225 | static int adis16209_remove(struct spi_device *spi) |
671ece14 | 226 | { |
35212dfa | 227 | struct iio_dev *indio_dev = spi_get_drvdata(spi); |
511fb29e | 228 | struct adis *st = iio_priv(indio_dev); |
671ece14 | 229 | |
d2fffd6c | 230 | iio_device_unregister(indio_dev); |
511fb29e | 231 | adis_cleanup_buffer_and_trigger(st, indio_dev); |
671ece14 BS |
232 | |
233 | return 0; | |
234 | } | |
235 | ||
236 | static struct spi_driver adis16209_driver = { | |
237 | .driver = { | |
238 | .name = "adis16209", | |
671ece14 BS |
239 | }, |
240 | .probe = adis16209_probe, | |
e543acf0 | 241 | .remove = adis16209_remove, |
671ece14 | 242 | }; |
ae6ae6fe | 243 | module_spi_driver(adis16209_driver); |
671ece14 BS |
244 | |
245 | MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); | |
246 | MODULE_DESCRIPTION("Analog Devices ADIS16209 Digital Vibration Sensor driver"); | |
247 | MODULE_LICENSE("GPL v2"); | |
55e4390c | 248 | MODULE_ALIAS("spi:adis16209"); |