1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * ADIS16080/100 Yaw Rate Gyroscope with SPI driver |
4 | * |
5 | * Copyright 2010 Analog Devices Inc. |
6 | */ |
7 | #include <linux/delay.h> |
8 | #include <linux/mutex.h> |
9 | #include <linux/device.h> |
10 | #include <linux/kernel.h> |
11 | #include <linux/spi/spi.h> |
12 | #include <linux/slab.h> |
13 | #include <linux/sysfs.h> |
14 | #include <linux/module.h> |
15 | |
16 | #include <linux/iio/iio.h> |
17 | #include <linux/iio/sysfs.h> |
18 | |
19 | #define ADIS16080_DIN_GYRO (0 << 10) /* Gyroscope output */ |
20 | #define ADIS16080_DIN_TEMP (1 << 10) /* Temperature output */ |
21 | #define ADIS16080_DIN_AIN1 (2 << 10) |
22 | #define ADIS16080_DIN_AIN2 (3 << 10) |
23 | |
24 | /* |
25 | * 1: Write contents on DIN to control register. |
26 | * 0: No changes to control register. |
27 | */ |
28 | |
29 | #define ADIS16080_DIN_WRITE (1 << 15) |
30 | |
31 | struct adis16080_chip_info { |
32 | int scale_val; |
33 | int scale_val2; |
34 | }; |
35 | |
36 | /** |
37 | * struct adis16080_state - device instance specific data |
38 | * @us: actual spi_device to write data |
39 | * @info: chip specific parameters |
40 | * @buf: transmit or receive buffer |
41 | * @lock: lock to protect buffer during reads |
42 | **/ |
43 | struct adis16080_state { |
44 | struct spi_device *us; |
45 | const struct adis16080_chip_info *info; |
46 | struct mutex lock; |
47 | |
48 | __be16 buf __aligned(IIO_DMA_MINALIGN); |
49 | }; |
50 | |
51 | static int adis16080_read_sample(struct iio_dev *indio_dev, |
52 | u16 addr, int *val) |
53 | { |
54 | struct adis16080_state *st = iio_priv(indio_dev); |
55 | int ret; |
56 | struct spi_transfer t[] = { |
57 | { |
58 | .tx_buf = &st->buf, |
59 | .len = 2, |
60 | .cs_change = 1, |
61 | }, { |
62 | .rx_buf = &st->buf, |
63 | .len = 2, |
64 | }, |
65 | }; |
66 | |
67 | st->buf = cpu_to_be16(addr | ADIS16080_DIN_WRITE); |
68 | |
69 | ret = spi_sync_transfer(spi: st->us, xfers: t, ARRAY_SIZE(t)); |
70 | if (ret == 0) |
71 | *val = sign_extend32(be16_to_cpu(st->buf), index: 11); |
72 | |
73 | return ret; |
74 | } |
75 | |
76 | static int adis16080_read_raw(struct iio_dev *indio_dev, |
77 | struct iio_chan_spec const *chan, |
78 | int *val, |
79 | int *val2, |
80 | long mask) |
81 | { |
82 | struct adis16080_state *st = iio_priv(indio_dev); |
83 | int ret; |
84 | |
85 | switch (mask) { |
86 | case IIO_CHAN_INFO_RAW: |
87 | mutex_lock(&st->lock); |
88 | ret = adis16080_read_sample(indio_dev, addr: chan->address, val); |
89 | mutex_unlock(lock: &st->lock); |
90 | return ret ? ret : IIO_VAL_INT; |
91 | case IIO_CHAN_INFO_SCALE: |
92 | switch (chan->type) { |
93 | case IIO_ANGL_VEL: |
94 | *val = st->info->scale_val; |
95 | *val2 = st->info->scale_val2; |
96 | return IIO_VAL_FRACTIONAL; |
97 | case IIO_VOLTAGE: |
98 | /* VREF = 5V, 12 bits */ |
99 | *val = 5000; |
100 | *val2 = 12; |
101 | return IIO_VAL_FRACTIONAL_LOG2; |
102 | case IIO_TEMP: |
103 | /* 85 C = 585, 25 C = 0 */ |
104 | *val = 85000 - 25000; |
105 | *val2 = 585; |
106 | return IIO_VAL_FRACTIONAL; |
107 | default: |
108 | return -EINVAL; |
109 | } |
110 | case IIO_CHAN_INFO_OFFSET: |
111 | switch (chan->type) { |
112 | case IIO_VOLTAGE: |
113 | /* 2.5 V = 0 */ |
114 | *val = 2048; |
115 | return IIO_VAL_INT; |
116 | case IIO_TEMP: |
117 | /* 85 C = 585, 25 C = 0 */ |
118 | *val = DIV_ROUND_CLOSEST(25 * 585, 85 - 25); |
119 | return IIO_VAL_INT; |
120 | default: |
121 | return -EINVAL; |
122 | } |
123 | default: |
124 | break; |
125 | } |
126 | |
127 | return -EINVAL; |
128 | } |
129 | |
130 | static const struct iio_chan_spec adis16080_channels[] = { |
131 | { |
132 | .type = IIO_ANGL_VEL, |
133 | .modified = 1, |
134 | .channel2 = IIO_MOD_Z, |
135 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
136 | BIT(IIO_CHAN_INFO_SCALE), |
137 | .address = ADIS16080_DIN_GYRO, |
138 | }, { |
139 | .type = IIO_VOLTAGE, |
140 | .indexed = 1, |
141 | .channel = 0, |
142 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
143 | BIT(IIO_CHAN_INFO_SCALE) | |
144 | BIT(IIO_CHAN_INFO_OFFSET), |
145 | .address = ADIS16080_DIN_AIN1, |
146 | }, { |
147 | .type = IIO_VOLTAGE, |
148 | .indexed = 1, |
149 | .channel = 1, |
150 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
151 | BIT(IIO_CHAN_INFO_SCALE) | |
152 | BIT(IIO_CHAN_INFO_OFFSET), |
153 | .address = ADIS16080_DIN_AIN2, |
154 | }, { |
155 | .type = IIO_TEMP, |
156 | .indexed = 1, |
157 | .channel = 0, |
158 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | |
159 | BIT(IIO_CHAN_INFO_SCALE) | |
160 | BIT(IIO_CHAN_INFO_OFFSET), |
161 | .address = ADIS16080_DIN_TEMP, |
162 | } |
163 | }; |
164 | |
165 | static const struct iio_info adis16080_info = { |
166 | .read_raw = &adis16080_read_raw, |
167 | }; |
168 | |
169 | enum { |
170 | ID_ADIS16080, |
171 | ID_ADIS16100, |
172 | }; |
173 | |
174 | static const struct adis16080_chip_info adis16080_chip_info[] = { |
175 | [ID_ADIS16080] = { |
176 | /* 80 degree = 819, 819 rad = 46925 degree */ |
177 | .scale_val = 80, |
178 | .scale_val2 = 46925, |
179 | }, |
180 | [ID_ADIS16100] = { |
181 | /* 300 degree = 1230, 1230 rad = 70474 degree */ |
182 | .scale_val = 300, |
183 | .scale_val2 = 70474, |
184 | }, |
185 | }; |
186 | |
187 | static int adis16080_probe(struct spi_device *spi) |
188 | { |
189 | const struct spi_device_id *id = spi_get_device_id(sdev: spi); |
190 | struct adis16080_state *st; |
191 | struct iio_dev *indio_dev; |
192 | |
193 | /* setup the industrialio driver allocated elements */ |
194 | indio_dev = devm_iio_device_alloc(parent: &spi->dev, sizeof_priv: sizeof(*st)); |
195 | if (!indio_dev) |
196 | return -ENOMEM; |
197 | st = iio_priv(indio_dev); |
198 | |
199 | mutex_init(&st->lock); |
200 | |
201 | /* Allocate the comms buffers */ |
202 | st->us = spi; |
203 | st->info = &adis16080_chip_info[id->driver_data]; |
204 | |
205 | indio_dev->name = spi->dev.driver->name; |
206 | indio_dev->channels = adis16080_channels; |
207 | indio_dev->num_channels = ARRAY_SIZE(adis16080_channels); |
208 | indio_dev->info = &adis16080_info; |
209 | indio_dev->modes = INDIO_DIRECT_MODE; |
210 | |
211 | return devm_iio_device_register(&spi->dev, indio_dev); |
212 | } |
213 | |
214 | static const struct spi_device_id adis16080_ids[] = { |
215 | { "adis16080" , ID_ADIS16080 }, |
216 | { "adis16100" , ID_ADIS16100 }, |
217 | {}, |
218 | }; |
219 | MODULE_DEVICE_TABLE(spi, adis16080_ids); |
220 | |
221 | static struct spi_driver adis16080_driver = { |
222 | .driver = { |
223 | .name = "adis16080" , |
224 | }, |
225 | .probe = adis16080_probe, |
226 | .id_table = adis16080_ids, |
227 | }; |
228 | module_spi_driver(adis16080_driver); |
229 | |
230 | MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>" ); |
231 | MODULE_DESCRIPTION("Analog Devices ADIS16080/100 Yaw Rate Gyroscope Driver" ); |
232 | MODULE_LICENSE("GPL v2" ); |
233 | |