1
2
3
4
5
6
7
8
9#include <linux/module.h>
10#include <linux/delay.h>
11#include <linux/mutex.h>
12#include <linux/device.h>
13#include <linux/kernel.h>
14#include <linux/spi/spi.h>
15#include <linux/slab.h>
16#include <linux/sysfs.h>
17
18#include <linux/iio/iio.h>
19#include <linux/iio/sysfs.h>
20
21#define ADIS16060_GYRO 0x20
22#define ADIS16060_TEMP_OUT 0x10
23#define ADIS16060_AIN2 0x80
24#define ADIS16060_AIN1 0x40
25
26
27
28
29
30
31
32
33struct adis16060_state {
34 struct spi_device *us_w;
35 struct spi_device *us_r;
36 struct mutex buf_lock;
37
38 u8 buf[3] ____cacheline_aligned;
39};
40
41static struct iio_dev *adis16060_iio_dev;
42
43static int adis16060_spi_write_then_read(struct iio_dev *indio_dev,
44 u8 conf, u16 *val)
45{
46 int ret;
47 struct adis16060_state *st = iio_priv(indio_dev);
48
49 mutex_lock(&st->buf_lock);
50 st->buf[2] = conf;
51 ret = spi_write(st->us_w, st->buf, 3);
52
53 if (ret < 0) {
54 mutex_unlock(&st->buf_lock);
55 return ret;
56 }
57
58 ret = spi_read(st->us_r, st->buf, 3);
59
60
61
62
63
64
65 if (!ret)
66 *val = ((st->buf[0] & 0x3) << 12) |
67 (st->buf[1] << 4) |
68 ((st->buf[2] >> 4) & 0xF);
69 mutex_unlock(&st->buf_lock);
70
71 return ret;
72}
73
74static int adis16060_read_raw(struct iio_dev *indio_dev,
75 struct iio_chan_spec const *chan,
76 int *val, int *val2,
77 long mask)
78{
79 u16 tval = 0;
80 int ret;
81
82 switch (mask) {
83 case IIO_CHAN_INFO_RAW:
84 ret = adis16060_spi_write_then_read(indio_dev,
85 chan->address, &tval);
86 if (ret < 0)
87 return ret;
88
89 *val = tval;
90 return IIO_VAL_INT;
91 case IIO_CHAN_INFO_OFFSET:
92 *val = -7;
93 *val2 = 461117;
94 return IIO_VAL_INT_PLUS_MICRO;
95 case IIO_CHAN_INFO_SCALE:
96 *val = 0;
97 *val2 = 34000;
98 return IIO_VAL_INT_PLUS_MICRO;
99 }
100
101 return -EINVAL;
102}
103
104static const struct iio_info adis16060_info = {
105 .read_raw = adis16060_read_raw,
106};
107
108static const struct iio_chan_spec adis16060_channels[] = {
109 {
110 .type = IIO_ANGL_VEL,
111 .modified = 1,
112 .channel2 = IIO_MOD_Z,
113 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
114 .address = ADIS16060_GYRO,
115 }, {
116 .type = IIO_VOLTAGE,
117 .indexed = 1,
118 .channel = 0,
119 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
120 .address = ADIS16060_AIN1,
121 }, {
122 .type = IIO_VOLTAGE,
123 .indexed = 1,
124 .channel = 1,
125 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
126 .address = ADIS16060_AIN2,
127 }, {
128 .type = IIO_TEMP,
129 .indexed = 1,
130 .channel = 0,
131 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
132 BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE),
133 .address = ADIS16060_TEMP_OUT,
134 }
135};
136
137static int adis16060_r_probe(struct spi_device *spi)
138{
139 int ret;
140 struct adis16060_state *st;
141 struct iio_dev *indio_dev;
142
143
144 indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
145 if (!indio_dev)
146 return -ENOMEM;
147
148 spi_set_drvdata(spi, indio_dev);
149 st = iio_priv(indio_dev);
150 st->us_r = spi;
151 mutex_init(&st->buf_lock);
152
153 indio_dev->name = spi->dev.driver->name;
154 indio_dev->dev.parent = &spi->dev;
155 indio_dev->info = &adis16060_info;
156 indio_dev->modes = INDIO_DIRECT_MODE;
157 indio_dev->channels = adis16060_channels;
158 indio_dev->num_channels = ARRAY_SIZE(adis16060_channels);
159
160 ret = devm_iio_device_register(&spi->dev, indio_dev);
161 if (ret)
162 return ret;
163
164 adis16060_iio_dev = indio_dev;
165 return 0;
166}
167
168static int adis16060_w_probe(struct spi_device *spi)
169{
170 int ret;
171 struct iio_dev *indio_dev = adis16060_iio_dev;
172 struct adis16060_state *st;
173
174 if (!indio_dev) {
175 ret = -ENODEV;
176 goto error_ret;
177 }
178 st = iio_priv(indio_dev);
179 spi_set_drvdata(spi, indio_dev);
180 st->us_w = spi;
181 return 0;
182
183error_ret:
184 return ret;
185}
186
187static int adis16060_w_remove(struct spi_device *spi)
188{
189 return 0;
190}
191
192static struct spi_driver adis16060_r_driver = {
193 .driver = {
194 .name = "adis16060_r",
195 },
196 .probe = adis16060_r_probe,
197};
198
199static struct spi_driver adis16060_w_driver = {
200 .driver = {
201 .name = "adis16060_w",
202 },
203 .probe = adis16060_w_probe,
204 .remove = adis16060_w_remove,
205};
206
207static __init int adis16060_init(void)
208{
209 int ret;
210
211 ret = spi_register_driver(&adis16060_r_driver);
212 if (ret < 0)
213 return ret;
214
215 ret = spi_register_driver(&adis16060_w_driver);
216 if (ret < 0) {
217 spi_unregister_driver(&adis16060_r_driver);
218 return ret;
219 }
220
221 return 0;
222}
223module_init(adis16060_init);
224
225static __exit void adis16060_exit(void)
226{
227 spi_unregister_driver(&adis16060_w_driver);
228 spi_unregister_driver(&adis16060_r_driver);
229}
230module_exit(adis16060_exit);
231
232MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
233MODULE_DESCRIPTION("Analog Devices ADIS16060 Yaw Rate Gyroscope Driver");
234MODULE_LICENSE("GPL v2");
235