Commit | Line | Data |
---|---|---|
817e5c65 GY |
1 | /* |
2 | * ad2s1210.c support for the ADI Resolver to Digital Converters: AD2S1210 | |
3 | * | |
4 | * Copyright (c) 2010-2010 Analog Devices Inc. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | * | |
10 | */ | |
11 | #include <linux/types.h> | |
12 | #include <linux/mutex.h> | |
13 | #include <linux/device.h> | |
14 | #include <linux/spi/spi.h> | |
15 | #include <linux/slab.h> | |
16 | #include <linux/sysfs.h> | |
17 | #include <linux/delay.h> | |
18 | #include <linux/gpio.h> | |
99c97852 | 19 | #include <linux/module.h> |
817e5c65 | 20 | |
06458e27 JC |
21 | #include <linux/iio/iio.h> |
22 | #include <linux/iio/sysfs.h> | |
b19e9ad5 | 23 | #include "ad2s1210.h" |
817e5c65 GY |
24 | |
25 | #define DRV_NAME "ad2s1210" | |
26 | ||
b19e9ad5 JC |
27 | #define AD2S1210_DEF_CONTROL 0x7E |
28 | ||
29 | #define AD2S1210_MSB_IS_HIGH 0x80 | |
30 | #define AD2S1210_MSB_IS_LOW 0x7F | |
31 | #define AD2S1210_PHASE_LOCK_RANGE_44 0x20 | |
32 | #define AD2S1210_ENABLE_HYSTERESIS 0x10 | |
33 | #define AD2S1210_SET_ENRES1 0x08 | |
34 | #define AD2S1210_SET_ENRES0 0x04 | |
35 | #define AD2S1210_SET_RES1 0x02 | |
36 | #define AD2S1210_SET_RES0 0x01 | |
37 | ||
38 | #define AD2S1210_SET_ENRESOLUTION (AD2S1210_SET_ENRES1 | \ | |
39 | AD2S1210_SET_ENRES0) | |
40 | #define AD2S1210_SET_RESOLUTION (AD2S1210_SET_RES1 | AD2S1210_SET_RES0) | |
41 | ||
42 | #define AD2S1210_REG_POSITION 0x80 | |
43 | #define AD2S1210_REG_VELOCITY 0x82 | |
44 | #define AD2S1210_REG_LOS_THRD 0x88 | |
45 | #define AD2S1210_REG_DOS_OVR_THRD 0x89 | |
46 | #define AD2S1210_REG_DOS_MIS_THRD 0x8A | |
47 | #define AD2S1210_REG_DOS_RST_MAX_THRD 0x8B | |
48 | #define AD2S1210_REG_DOS_RST_MIN_THRD 0x8C | |
49 | #define AD2S1210_REG_LOT_HIGH_THRD 0x8D | |
50 | #define AD2S1210_REG_LOT_LOW_THRD 0x8E | |
51 | #define AD2S1210_REG_EXCIT_FREQ 0x91 | |
52 | #define AD2S1210_REG_CONTROL 0x92 | |
53 | #define AD2S1210_REG_SOFT_RESET 0xF0 | |
54 | #define AD2S1210_REG_FAULT 0xFF | |
817e5c65 GY |
55 | |
56 | /* pin SAMPLE, A0, A1, RES0, RES1, is controlled by driver */ | |
57 | #define AD2S1210_SAA 3 | |
817e5c65 GY |
58 | #define AD2S1210_PN (AD2S1210_SAA + AD2S1210_RES) |
59 | ||
60 | #define AD2S1210_MIN_CLKIN 6144000 | |
61 | #define AD2S1210_MAX_CLKIN 10240000 | |
62 | #define AD2S1210_MIN_EXCIT 2000 | |
63 | #define AD2S1210_MAX_EXCIT 20000 | |
64 | #define AD2S1210_MIN_FCW 0x4 | |
65 | #define AD2S1210_MAX_FCW 0x50 | |
66 | ||
67 | /* default input clock on serial interface */ | |
68 | #define AD2S1210_DEF_CLKIN 8192000 | |
69 | /* clock period in nano second */ | |
70 | #define AD2S1210_DEF_TCK (1000000000/AD2S1210_DEF_CLKIN) | |
71 | #define AD2S1210_DEF_EXCIT 10000 | |
72 | ||
73 | enum ad2s1210_mode { | |
74 | MOD_POS = 0, | |
75 | MOD_VEL, | |
817e5c65 | 76 | MOD_CONFIG, |
b19e9ad5 | 77 | MOD_RESERVED, |
817e5c65 GY |
78 | }; |
79 | ||
b19e9ad5 | 80 | static const unsigned int ad2s1210_resolution_value[] = { 10, 12, 14, 16 }; |
817e5c65 GY |
81 | |
82 | struct ad2s1210_state { | |
b19e9ad5 | 83 | const struct ad2s1210_platform_data *pdata; |
817e5c65 | 84 | struct mutex lock; |
817e5c65 | 85 | struct spi_device *sdev; |
817e5c65 GY |
86 | unsigned int fclkin; |
87 | unsigned int fexcit; | |
b19e9ad5 JC |
88 | bool hysteresis; |
89 | bool old_data; | |
90 | u8 resolution; | |
91 | enum ad2s1210_mode mode; | |
92 | u8 rx[2] ____cacheline_aligned; | |
93 | u8 tx[2] ____cacheline_aligned; | |
817e5c65 GY |
94 | }; |
95 | ||
b19e9ad5 JC |
96 | static const int ad2s1210_mode_vals[4][2] = { |
97 | [MOD_POS] = { 0, 0 }, | |
98 | [MOD_VEL] = { 0, 1 }, | |
99 | [MOD_CONFIG] = { 1, 0 }, | |
100 | }; | |
101 | static inline void ad2s1210_set_mode(enum ad2s1210_mode mode, | |
102 | struct ad2s1210_state *st) | |
817e5c65 | 103 | { |
b19e9ad5 JC |
104 | gpio_set_value(st->pdata->a[0], ad2s1210_mode_vals[mode][0]); |
105 | gpio_set_value(st->pdata->a[1], ad2s1210_mode_vals[mode][1]); | |
817e5c65 GY |
106 | st->mode = mode; |
107 | } | |
108 | ||
109 | /* write 1 bytes (address or data) to the chip */ | |
b19e9ad5 | 110 | static int ad2s1210_config_write(struct ad2s1210_state *st, u8 data) |
817e5c65 | 111 | { |
b19e9ad5 | 112 | int ret; |
817e5c65 | 113 | |
b19e9ad5 | 114 | ad2s1210_set_mode(MOD_CONFIG, st); |
817e5c65 | 115 | st->tx[0] = data; |
b19e9ad5 JC |
116 | ret = spi_write(st->sdev, st->tx, 1); |
117 | if (ret < 0) | |
817e5c65 | 118 | return ret; |
b19e9ad5 JC |
119 | st->old_data = true; |
120 | ||
121 | return 0; | |
817e5c65 GY |
122 | } |
123 | ||
124 | /* read value from one of the registers */ | |
b19e9ad5 JC |
125 | static int ad2s1210_config_read(struct ad2s1210_state *st, |
126 | unsigned char address) | |
127 | { | |
128 | struct spi_transfer xfer = { | |
129 | .len = 2, | |
130 | .rx_buf = st->rx, | |
131 | .tx_buf = st->tx, | |
132 | }; | |
817e5c65 GY |
133 | int ret = 0; |
134 | ||
b19e9ad5 | 135 | ad2s1210_set_mode(MOD_CONFIG, st); |
b19e9ad5 JC |
136 | st->tx[0] = address | AD2S1210_MSB_IS_HIGH; |
137 | st->tx[1] = AD2S1210_REG_FAULT; | |
ad6c46b0 | 138 | ret = spi_sync_transfer(st->sdev, &xfer, 1); |
b19e9ad5 | 139 | if (ret < 0) |
817e5c65 | 140 | return ret; |
b19e9ad5 JC |
141 | st->old_data = true; |
142 | ||
143 | return st->rx[1]; | |
817e5c65 GY |
144 | } |
145 | ||
b19e9ad5 JC |
146 | static inline |
147 | int ad2s1210_update_frequency_control_word(struct ad2s1210_state *st) | |
817e5c65 | 148 | { |
b19e9ad5 | 149 | int ret; |
817e5c65 | 150 | unsigned char fcw; |
b19e9ad5 | 151 | |
817e5c65 | 152 | fcw = (unsigned char)(st->fexcit * (1 << 15) / st->fclkin); |
b19e9ad5 | 153 | if (fcw < AD2S1210_MIN_FCW || fcw > AD2S1210_MAX_FCW) { |
817e5c65 | 154 | pr_err("ad2s1210: FCW out of range\n"); |
b19e9ad5 JC |
155 | return -ERANGE; |
156 | } | |
157 | ||
158 | ret = ad2s1210_config_write(st, AD2S1210_REG_EXCIT_FREQ); | |
159 | if (ret < 0) | |
160 | return ret; | |
161 | ||
162 | return ad2s1210_config_write(st, fcw); | |
817e5c65 GY |
163 | } |
164 | ||
b19e9ad5 | 165 | static unsigned char ad2s1210_read_resolution_pin(struct ad2s1210_state *st) |
817e5c65 | 166 | { |
b19e9ad5 JC |
167 | return ad2s1210_resolution_value[ |
168 | (gpio_get_value(st->pdata->res[0]) << 1) | | |
169 | gpio_get_value(st->pdata->res[1])]; | |
817e5c65 | 170 | } |
b19e9ad5 JC |
171 | |
172 | static const int ad2s1210_res_pins[4][2] = { | |
173 | { 0, 0 }, {0, 1}, {1, 0}, {1, 1} | |
174 | }; | |
175 | ||
176 | static inline void ad2s1210_set_resolution_pin(struct ad2s1210_state *st) | |
817e5c65 | 177 | { |
b19e9ad5 JC |
178 | gpio_set_value(st->pdata->res[0], |
179 | ad2s1210_res_pins[(st->resolution - 10)/2][0]); | |
180 | gpio_set_value(st->pdata->res[1], | |
181 | ad2s1210_res_pins[(st->resolution - 10)/2][1]); | |
817e5c65 | 182 | } |
817e5c65 | 183 | |
b19e9ad5 | 184 | static inline int ad2s1210_soft_reset(struct ad2s1210_state *st) |
817e5c65 | 185 | { |
b19e9ad5 JC |
186 | int ret; |
187 | ||
188 | ret = ad2s1210_config_write(st, AD2S1210_REG_SOFT_RESET); | |
189 | if (ret < 0) | |
190 | return ret; | |
191 | ||
192 | return ad2s1210_config_write(st, 0x0); | |
817e5c65 GY |
193 | } |
194 | ||
817e5c65 | 195 | static ssize_t ad2s1210_store_softreset(struct device *dev, |
b19e9ad5 JC |
196 | struct device_attribute *attr, |
197 | const char *buf, | |
198 | size_t len) | |
817e5c65 | 199 | { |
a651cf9b | 200 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
b19e9ad5 JC |
201 | int ret; |
202 | ||
817e5c65 | 203 | mutex_lock(&st->lock); |
b19e9ad5 | 204 | ret = ad2s1210_soft_reset(st); |
817e5c65 | 205 | mutex_unlock(&st->lock); |
b19e9ad5 JC |
206 | |
207 | return ret < 0 ? ret : len; | |
817e5c65 GY |
208 | } |
209 | ||
210 | static ssize_t ad2s1210_show_fclkin(struct device *dev, | |
b19e9ad5 JC |
211 | struct device_attribute *attr, |
212 | char *buf) | |
817e5c65 | 213 | { |
a651cf9b | 214 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
817e5c65 GY |
215 | return sprintf(buf, "%d\n", st->fclkin); |
216 | } | |
217 | ||
218 | static ssize_t ad2s1210_store_fclkin(struct device *dev, | |
b19e9ad5 JC |
219 | struct device_attribute *attr, |
220 | const char *buf, | |
221 | size_t len) | |
817e5c65 | 222 | { |
a651cf9b | 223 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
817e5c65 GY |
224 | unsigned long fclkin; |
225 | int ret; | |
226 | ||
227 | ret = strict_strtoul(buf, 10, &fclkin); | |
b19e9ad5 JC |
228 | if (ret) |
229 | return ret; | |
230 | if (fclkin < AD2S1210_MIN_CLKIN || fclkin > AD2S1210_MAX_CLKIN) { | |
817e5c65 GY |
231 | pr_err("ad2s1210: fclkin out of range\n"); |
232 | return -EINVAL; | |
233 | } | |
b19e9ad5 JC |
234 | |
235 | mutex_lock(&st->lock); | |
236 | st->fclkin = fclkin; | |
237 | ||
238 | ret = ad2s1210_update_frequency_control_word(st); | |
239 | if (ret < 0) | |
240 | goto error_ret; | |
241 | ret = ad2s1210_soft_reset(st); | |
242 | error_ret: | |
817e5c65 | 243 | mutex_unlock(&st->lock); |
b19e9ad5 JC |
244 | |
245 | return ret < 0 ? ret : len; | |
817e5c65 GY |
246 | } |
247 | ||
248 | static ssize_t ad2s1210_show_fexcit(struct device *dev, | |
b19e9ad5 JC |
249 | struct device_attribute *attr, |
250 | char *buf) | |
817e5c65 | 251 | { |
a651cf9b | 252 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
817e5c65 GY |
253 | return sprintf(buf, "%d\n", st->fexcit); |
254 | } | |
255 | ||
256 | static ssize_t ad2s1210_store_fexcit(struct device *dev, | |
b19e9ad5 JC |
257 | struct device_attribute *attr, |
258 | const char *buf, size_t len) | |
817e5c65 | 259 | { |
a651cf9b | 260 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
817e5c65 GY |
261 | unsigned long fexcit; |
262 | int ret; | |
263 | ||
264 | ret = strict_strtoul(buf, 10, &fexcit); | |
b19e9ad5 JC |
265 | if (ret < 0) |
266 | return ret; | |
267 | if (fexcit < AD2S1210_MIN_EXCIT || fexcit > AD2S1210_MAX_EXCIT) { | |
817e5c65 GY |
268 | pr_err("ad2s1210: excitation frequency out of range\n"); |
269 | return -EINVAL; | |
270 | } | |
b19e9ad5 JC |
271 | mutex_lock(&st->lock); |
272 | st->fexcit = fexcit; | |
273 | ret = ad2s1210_update_frequency_control_word(st); | |
274 | if (ret < 0) | |
275 | goto error_ret; | |
276 | ret = ad2s1210_soft_reset(st); | |
277 | error_ret: | |
817e5c65 | 278 | mutex_unlock(&st->lock); |
b19e9ad5 JC |
279 | |
280 | return ret < 0 ? ret : len; | |
817e5c65 GY |
281 | } |
282 | ||
283 | static ssize_t ad2s1210_show_control(struct device *dev, | |
b19e9ad5 JC |
284 | struct device_attribute *attr, |
285 | char *buf) | |
817e5c65 | 286 | { |
a651cf9b | 287 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
b19e9ad5 | 288 | int ret; |
817e5c65 | 289 | mutex_lock(&st->lock); |
b19e9ad5 | 290 | ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); |
817e5c65 | 291 | mutex_unlock(&st->lock); |
b19e9ad5 | 292 | return ret < 0 ? ret : sprintf(buf, "0x%x\n", ret); |
817e5c65 GY |
293 | } |
294 | ||
295 | static ssize_t ad2s1210_store_control(struct device *dev, | |
296 | struct device_attribute *attr, | |
297 | const char *buf, size_t len) | |
298 | { | |
a651cf9b | 299 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
817e5c65 GY |
300 | unsigned long udata; |
301 | unsigned char data; | |
302 | int ret; | |
303 | ||
304 | ret = strict_strtoul(buf, 16, &udata); | |
b19e9ad5 JC |
305 | if (ret) |
306 | return -EINVAL; | |
307 | ||
817e5c65 | 308 | mutex_lock(&st->lock); |
b19e9ad5 JC |
309 | ret = ad2s1210_config_write(st, AD2S1210_REG_CONTROL); |
310 | if (ret < 0) | |
311 | goto error_ret; | |
312 | data = udata & AD2S1210_MSB_IS_LOW; | |
313 | ret = ad2s1210_config_write(st, data); | |
314 | if (ret < 0) | |
315 | goto error_ret; | |
316 | ||
317 | ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); | |
318 | if (ret < 0) | |
319 | goto error_ret; | |
320 | if (ret & AD2S1210_MSB_IS_HIGH) { | |
817e5c65 GY |
321 | ret = -EIO; |
322 | pr_err("ad2s1210: write control register fail\n"); | |
323 | goto error_ret; | |
324 | } | |
b19e9ad5 JC |
325 | st->resolution |
326 | = ad2s1210_resolution_value[data & AD2S1210_SET_RESOLUTION]; | |
327 | if (st->pdata->gpioin) { | |
328 | data = ad2s1210_read_resolution_pin(st); | |
329 | if (data != st->resolution) | |
330 | pr_warning("ad2s1210: resolution settings not match\n"); | |
331 | } else | |
332 | ad2s1210_set_resolution_pin(st); | |
333 | ||
817e5c65 | 334 | ret = len; |
b19e9ad5 JC |
335 | st->hysteresis = !!(data & AD2S1210_ENABLE_HYSTERESIS); |
336 | ||
817e5c65 GY |
337 | error_ret: |
338 | mutex_unlock(&st->lock); | |
339 | return ret; | |
340 | } | |
341 | ||
342 | static ssize_t ad2s1210_show_resolution(struct device *dev, | |
343 | struct device_attribute *attr, char *buf) | |
344 | { | |
a651cf9b | 345 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
817e5c65 GY |
346 | return sprintf(buf, "%d\n", st->resolution); |
347 | } | |
348 | ||
349 | static ssize_t ad2s1210_store_resolution(struct device *dev, | |
350 | struct device_attribute *attr, | |
351 | const char *buf, size_t len) | |
352 | { | |
a651cf9b | 353 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
817e5c65 GY |
354 | unsigned char data; |
355 | unsigned long udata; | |
356 | int ret; | |
357 | ||
358 | ret = strict_strtoul(buf, 10, &udata); | |
b19e9ad5 | 359 | if (ret || udata < 10 || udata > 16) { |
817e5c65 GY |
360 | pr_err("ad2s1210: resolution out of range\n"); |
361 | return -EINVAL; | |
362 | } | |
363 | mutex_lock(&st->lock); | |
b19e9ad5 JC |
364 | ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); |
365 | if (ret < 0) | |
366 | goto error_ret; | |
367 | data = ret; | |
368 | data &= ~AD2S1210_SET_RESOLUTION; | |
369 | data |= (udata - 10) >> 1; | |
370 | ret = ad2s1210_config_write(st, AD2S1210_REG_CONTROL); | |
371 | if (ret < 0) | |
372 | goto error_ret; | |
373 | ret = ad2s1210_config_write(st, data & AD2S1210_MSB_IS_LOW); | |
374 | if (ret < 0) | |
375 | goto error_ret; | |
376 | ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); | |
377 | if (ret < 0) | |
378 | goto error_ret; | |
379 | data = ret; | |
380 | if (data & AD2S1210_MSB_IS_HIGH) { | |
817e5c65 GY |
381 | ret = -EIO; |
382 | pr_err("ad2s1210: setting resolution fail\n"); | |
383 | goto error_ret; | |
384 | } | |
b19e9ad5 JC |
385 | st->resolution |
386 | = ad2s1210_resolution_value[data & AD2S1210_SET_RESOLUTION]; | |
387 | if (st->pdata->gpioin) { | |
388 | data = ad2s1210_read_resolution_pin(st); | |
389 | if (data != st->resolution) | |
390 | pr_warning("ad2s1210: resolution settings not match\n"); | |
391 | } else | |
392 | ad2s1210_set_resolution_pin(st); | |
817e5c65 GY |
393 | ret = len; |
394 | error_ret: | |
395 | mutex_unlock(&st->lock); | |
396 | return ret; | |
397 | } | |
b19e9ad5 | 398 | |
817e5c65 GY |
399 | /* read the fault register since last sample */ |
400 | static ssize_t ad2s1210_show_fault(struct device *dev, | |
401 | struct device_attribute *attr, char *buf) | |
402 | { | |
a651cf9b | 403 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
b19e9ad5 | 404 | int ret; |
817e5c65 GY |
405 | |
406 | mutex_lock(&st->lock); | |
b19e9ad5 | 407 | ret = ad2s1210_config_read(st, AD2S1210_REG_FAULT); |
817e5c65 | 408 | mutex_unlock(&st->lock); |
b19e9ad5 JC |
409 | |
410 | return ret ? ret : sprintf(buf, "0x%x\n", ret); | |
817e5c65 GY |
411 | } |
412 | ||
413 | static ssize_t ad2s1210_clear_fault(struct device *dev, | |
b19e9ad5 JC |
414 | struct device_attribute *attr, |
415 | const char *buf, | |
416 | size_t len) | |
817e5c65 | 417 | { |
a651cf9b | 418 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
b19e9ad5 | 419 | int ret; |
817e5c65 GY |
420 | |
421 | mutex_lock(&st->lock); | |
b19e9ad5 | 422 | gpio_set_value(st->pdata->sample, 0); |
817e5c65 GY |
423 | /* delay (2 * tck + 20) nano seconds */ |
424 | udelay(1); | |
b19e9ad5 JC |
425 | gpio_set_value(st->pdata->sample, 1); |
426 | ret = ad2s1210_config_read(st, AD2S1210_REG_FAULT); | |
427 | if (ret < 0) | |
428 | goto error_ret; | |
429 | gpio_set_value(st->pdata->sample, 0); | |
430 | gpio_set_value(st->pdata->sample, 1); | |
431 | error_ret: | |
817e5c65 GY |
432 | mutex_unlock(&st->lock); |
433 | ||
b19e9ad5 | 434 | return ret < 0 ? ret : len; |
817e5c65 GY |
435 | } |
436 | ||
437 | static ssize_t ad2s1210_show_reg(struct device *dev, | |
b19e9ad5 JC |
438 | struct device_attribute *attr, |
439 | char *buf) | |
817e5c65 | 440 | { |
a651cf9b | 441 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
817e5c65 | 442 | struct iio_dev_attr *iattr = to_iio_dev_attr(attr); |
b19e9ad5 | 443 | int ret; |
817e5c65 GY |
444 | |
445 | mutex_lock(&st->lock); | |
b19e9ad5 | 446 | ret = ad2s1210_config_read(st, iattr->address); |
817e5c65 | 447 | mutex_unlock(&st->lock); |
b19e9ad5 JC |
448 | |
449 | return ret < 0 ? ret : sprintf(buf, "%d\n", ret); | |
817e5c65 GY |
450 | } |
451 | ||
452 | static ssize_t ad2s1210_store_reg(struct device *dev, | |
453 | struct device_attribute *attr, const char *buf, size_t len) | |
454 | { | |
a651cf9b | 455 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
817e5c65 GY |
456 | unsigned long data; |
457 | int ret; | |
458 | struct iio_dev_attr *iattr = to_iio_dev_attr(attr); | |
459 | ||
460 | ret = strict_strtoul(buf, 10, &data); | |
461 | if (ret) | |
462 | return -EINVAL; | |
463 | mutex_lock(&st->lock); | |
b19e9ad5 JC |
464 | ret = ad2s1210_config_write(st, iattr->address); |
465 | if (ret < 0) | |
466 | goto error_ret; | |
467 | ret = ad2s1210_config_write(st, data & AD2S1210_MSB_IS_LOW); | |
468 | error_ret: | |
817e5c65 | 469 | mutex_unlock(&st->lock); |
b19e9ad5 | 470 | return ret < 0 ? ret : len; |
817e5c65 GY |
471 | } |
472 | ||
29148543 JC |
473 | static int ad2s1210_read_raw(struct iio_dev *indio_dev, |
474 | struct iio_chan_spec const *chan, | |
475 | int *val, | |
476 | int *val2, | |
477 | long m) | |
817e5c65 | 478 | { |
29148543 | 479 | struct ad2s1210_state *st = iio_priv(indio_dev); |
b4824101 | 480 | u16 negative; |
817e5c65 | 481 | int ret = 0; |
817e5c65 | 482 | u16 pos; |
817e5c65 | 483 | s16 vel; |
817e5c65 | 484 | |
817e5c65 | 485 | mutex_lock(&st->lock); |
b19e9ad5 | 486 | gpio_set_value(st->pdata->sample, 0); |
817e5c65 GY |
487 | /* delay (6 * tck + 20) nano seconds */ |
488 | udelay(1); | |
489 | ||
29148543 JC |
490 | switch (chan->type) { |
491 | case IIO_ANGL: | |
492 | ad2s1210_set_mode(MOD_POS, st); | |
493 | break; | |
494 | case IIO_ANGL_VEL: | |
495 | ad2s1210_set_mode(MOD_VEL, st); | |
496 | break; | |
497 | default: | |
498 | ret = -EINVAL; | |
499 | break; | |
500 | } | |
501 | if (ret < 0) | |
502 | goto error_ret; | |
b19e9ad5 | 503 | ret = spi_read(st->sdev, st->rx, 2); |
29148543 | 504 | if (ret < 0) |
817e5c65 | 505 | goto error_ret; |
29148543 JC |
506 | |
507 | switch (chan->type) { | |
508 | case IIO_ANGL: | |
509 | pos = be16_to_cpup((u16 *)st->rx); | |
510 | if (st->hysteresis) | |
511 | pos >>= 16 - st->resolution; | |
512 | *val = pos; | |
513 | ret = IIO_VAL_INT; | |
514 | break; | |
515 | case IIO_ANGL_VEL: | |
516 | negative = st->rx[0] & 0x80; | |
517 | vel = be16_to_cpup((s16 *)st->rx); | |
518 | vel >>= 16 - st->resolution; | |
519 | if (vel & 0x8000) { | |
520 | negative = (0xffff >> st->resolution) << st->resolution; | |
521 | vel |= negative; | |
522 | } | |
523 | *val = vel; | |
524 | ret = IIO_VAL_INT; | |
525 | break; | |
526 | default: | |
527 | mutex_unlock(&st->lock); | |
528 | return -EINVAL; | |
817e5c65 | 529 | } |
29148543 | 530 | |
817e5c65 | 531 | error_ret: |
b19e9ad5 | 532 | gpio_set_value(st->pdata->sample, 1); |
817e5c65 GY |
533 | /* delay (2 * tck + 20) nano seconds */ |
534 | udelay(1); | |
535 | mutex_unlock(&st->lock); | |
29148543 | 536 | return ret; |
817e5c65 GY |
537 | } |
538 | ||
983bbfd0 | 539 | static IIO_DEVICE_ATTR(reset, S_IWUSR, |
b19e9ad5 | 540 | NULL, ad2s1210_store_softreset, 0); |
983bbfd0 | 541 | static IIO_DEVICE_ATTR(fclkin, S_IRUGO | S_IWUSR, |
b19e9ad5 | 542 | ad2s1210_show_fclkin, ad2s1210_store_fclkin, 0); |
983bbfd0 | 543 | static IIO_DEVICE_ATTR(fexcit, S_IRUGO | S_IWUSR, |
b19e9ad5 | 544 | ad2s1210_show_fexcit, ad2s1210_store_fexcit, 0); |
983bbfd0 | 545 | static IIO_DEVICE_ATTR(control, S_IRUGO | S_IWUSR, |
b19e9ad5 | 546 | ad2s1210_show_control, ad2s1210_store_control, 0); |
983bbfd0 | 547 | static IIO_DEVICE_ATTR(bits, S_IRUGO | S_IWUSR, |
b19e9ad5 | 548 | ad2s1210_show_resolution, ad2s1210_store_resolution, 0); |
983bbfd0 | 549 | static IIO_DEVICE_ATTR(fault, S_IRUGO | S_IWUSR, |
b19e9ad5 | 550 | ad2s1210_show_fault, ad2s1210_clear_fault, 0); |
29148543 | 551 | |
983bbfd0 | 552 | static IIO_DEVICE_ATTR(los_thrd, S_IRUGO | S_IWUSR, |
b19e9ad5 JC |
553 | ad2s1210_show_reg, ad2s1210_store_reg, |
554 | AD2S1210_REG_LOS_THRD); | |
983bbfd0 | 555 | static IIO_DEVICE_ATTR(dos_ovr_thrd, S_IRUGO | S_IWUSR, |
b19e9ad5 JC |
556 | ad2s1210_show_reg, ad2s1210_store_reg, |
557 | AD2S1210_REG_DOS_OVR_THRD); | |
983bbfd0 | 558 | static IIO_DEVICE_ATTR(dos_mis_thrd, S_IRUGO | S_IWUSR, |
b19e9ad5 JC |
559 | ad2s1210_show_reg, ad2s1210_store_reg, |
560 | AD2S1210_REG_DOS_MIS_THRD); | |
983bbfd0 | 561 | static IIO_DEVICE_ATTR(dos_rst_max_thrd, S_IRUGO | S_IWUSR, |
b19e9ad5 JC |
562 | ad2s1210_show_reg, ad2s1210_store_reg, |
563 | AD2S1210_REG_DOS_RST_MAX_THRD); | |
983bbfd0 | 564 | static IIO_DEVICE_ATTR(dos_rst_min_thrd, S_IRUGO | S_IWUSR, |
b19e9ad5 JC |
565 | ad2s1210_show_reg, ad2s1210_store_reg, |
566 | AD2S1210_REG_DOS_RST_MIN_THRD); | |
983bbfd0 | 567 | static IIO_DEVICE_ATTR(lot_high_thrd, S_IRUGO | S_IWUSR, |
b19e9ad5 JC |
568 | ad2s1210_show_reg, ad2s1210_store_reg, |
569 | AD2S1210_REG_LOT_HIGH_THRD); | |
983bbfd0 | 570 | static IIO_DEVICE_ATTR(lot_low_thrd, S_IRUGO | S_IWUSR, |
b19e9ad5 JC |
571 | ad2s1210_show_reg, ad2s1210_store_reg, |
572 | AD2S1210_REG_LOT_LOW_THRD); | |
817e5c65 | 573 | |
29148543 | 574 | |
f4e4b955 | 575 | static const struct iio_chan_spec ad2s1210_channels[] = { |
29148543 JC |
576 | { |
577 | .type = IIO_ANGL, | |
578 | .indexed = 1, | |
579 | .channel = 0, | |
07d0f654 | 580 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), |
29148543 JC |
581 | }, { |
582 | .type = IIO_ANGL_VEL, | |
583 | .indexed = 1, | |
584 | .channel = 0, | |
07d0f654 | 585 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), |
29148543 JC |
586 | } |
587 | }; | |
588 | ||
817e5c65 | 589 | static struct attribute *ad2s1210_attributes[] = { |
817e5c65 GY |
590 | &iio_dev_attr_reset.dev_attr.attr, |
591 | &iio_dev_attr_fclkin.dev_attr.attr, | |
592 | &iio_dev_attr_fexcit.dev_attr.attr, | |
593 | &iio_dev_attr_control.dev_attr.attr, | |
594 | &iio_dev_attr_bits.dev_attr.attr, | |
595 | &iio_dev_attr_fault.dev_attr.attr, | |
817e5c65 GY |
596 | &iio_dev_attr_los_thrd.dev_attr.attr, |
597 | &iio_dev_attr_dos_ovr_thrd.dev_attr.attr, | |
598 | &iio_dev_attr_dos_mis_thrd.dev_attr.attr, | |
599 | &iio_dev_attr_dos_rst_max_thrd.dev_attr.attr, | |
600 | &iio_dev_attr_dos_rst_min_thrd.dev_attr.attr, | |
601 | &iio_dev_attr_lot_high_thrd.dev_attr.attr, | |
602 | &iio_dev_attr_lot_low_thrd.dev_attr.attr, | |
603 | NULL, | |
604 | }; | |
605 | ||
606 | static const struct attribute_group ad2s1210_attribute_group = { | |
817e5c65 GY |
607 | .attrs = ad2s1210_attributes, |
608 | }; | |
609 | ||
4ae1c61f | 610 | static int ad2s1210_initial(struct ad2s1210_state *st) |
817e5c65 GY |
611 | { |
612 | unsigned char data; | |
613 | int ret; | |
614 | ||
615 | mutex_lock(&st->lock); | |
b19e9ad5 JC |
616 | if (st->pdata->gpioin) |
617 | st->resolution = ad2s1210_read_resolution_pin(st); | |
618 | else | |
619 | ad2s1210_set_resolution_pin(st); | |
620 | ||
621 | ret = ad2s1210_config_write(st, AD2S1210_REG_CONTROL); | |
622 | if (ret < 0) | |
623 | goto error_ret; | |
624 | data = AD2S1210_DEF_CONTROL & ~(AD2S1210_SET_RESOLUTION); | |
625 | data |= (st->resolution - 10) >> 1; | |
626 | ret = ad2s1210_config_write(st, data); | |
627 | if (ret < 0) | |
628 | goto error_ret; | |
629 | ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); | |
630 | if (ret < 0) | |
817e5c65 GY |
631 | goto error_ret; |
632 | ||
b19e9ad5 | 633 | if (ret & AD2S1210_MSB_IS_HIGH) { |
817e5c65 GY |
634 | ret = -EIO; |
635 | goto error_ret; | |
636 | } | |
637 | ||
b19e9ad5 JC |
638 | ret = ad2s1210_update_frequency_control_word(st); |
639 | if (ret < 0) | |
640 | goto error_ret; | |
641 | ret = ad2s1210_soft_reset(st); | |
817e5c65 GY |
642 | error_ret: |
643 | mutex_unlock(&st->lock); | |
644 | return ret; | |
645 | } | |
646 | ||
6fe8135f | 647 | static const struct iio_info ad2s1210_info = { |
29148543 | 648 | .read_raw = &ad2s1210_read_raw, |
6fe8135f JC |
649 | .attrs = &ad2s1210_attribute_group, |
650 | .driver_module = THIS_MODULE, | |
651 | }; | |
652 | ||
b19e9ad5 JC |
653 | static int ad2s1210_setup_gpios(struct ad2s1210_state *st) |
654 | { | |
b19e9ad5 | 655 | unsigned long flags = st->pdata->gpioin ? GPIOF_DIR_IN : GPIOF_DIR_OUT; |
93decf36 JC |
656 | struct gpio ad2s1210_gpios[] = { |
657 | { st->pdata->sample, GPIOF_DIR_IN, "sample" }, | |
658 | { st->pdata->a[0], flags, "a0" }, | |
659 | { st->pdata->a[1], flags, "a1" }, | |
660 | { st->pdata->res[0], flags, "res0" }, | |
661 | { st->pdata->res[0], flags, "res1" }, | |
662 | }; | |
b19e9ad5 | 663 | |
93decf36 | 664 | return gpio_request_array(ad2s1210_gpios, ARRAY_SIZE(ad2s1210_gpios)); |
b19e9ad5 JC |
665 | } |
666 | ||
667 | static void ad2s1210_free_gpios(struct ad2s1210_state *st) | |
668 | { | |
93decf36 JC |
669 | unsigned long flags = st->pdata->gpioin ? GPIOF_DIR_IN : GPIOF_DIR_OUT; |
670 | struct gpio ad2s1210_gpios[] = { | |
671 | { st->pdata->sample, GPIOF_DIR_IN, "sample" }, | |
672 | { st->pdata->a[0], flags, "a0" }, | |
673 | { st->pdata->a[1], flags, "a1" }, | |
674 | { st->pdata->res[0], flags, "res0" }, | |
675 | { st->pdata->res[0], flags, "res1" }, | |
676 | }; | |
677 | ||
678 | gpio_free_array(ad2s1210_gpios, ARRAY_SIZE(ad2s1210_gpios)); | |
b19e9ad5 JC |
679 | } |
680 | ||
4ae1c61f | 681 | static int ad2s1210_probe(struct spi_device *spi) |
817e5c65 | 682 | { |
b19e9ad5 | 683 | struct iio_dev *indio_dev; |
817e5c65 | 684 | struct ad2s1210_state *st; |
b19e9ad5 JC |
685 | int ret; |
686 | ||
687 | if (spi->dev.platform_data == NULL) | |
688 | return -EINVAL; | |
817e5c65 | 689 | |
7cbb7537 | 690 | indio_dev = iio_device_alloc(sizeof(*st)); |
b19e9ad5 | 691 | if (indio_dev == NULL) { |
817e5c65 GY |
692 | ret = -ENOMEM; |
693 | goto error_ret; | |
694 | } | |
b19e9ad5 JC |
695 | st = iio_priv(indio_dev); |
696 | st->pdata = spi->dev.platform_data; | |
697 | ret = ad2s1210_setup_gpios(st); | |
698 | if (ret < 0) | |
699 | goto error_free_dev; | |
700 | ||
701 | spi_set_drvdata(spi, indio_dev); | |
817e5c65 GY |
702 | |
703 | mutex_init(&st->lock); | |
704 | st->sdev = spi; | |
b19e9ad5 | 705 | st->hysteresis = true; |
817e5c65 | 706 | st->mode = MOD_CONFIG; |
b19e9ad5 | 707 | st->resolution = 12; |
817e5c65 | 708 | st->fexcit = AD2S1210_DEF_EXCIT; |
817e5c65 | 709 | |
b19e9ad5 JC |
710 | indio_dev->dev.parent = &spi->dev; |
711 | indio_dev->info = &ad2s1210_info; | |
712 | indio_dev->modes = INDIO_DIRECT_MODE; | |
29148543 JC |
713 | indio_dev->channels = ad2s1210_channels; |
714 | indio_dev->num_channels = ARRAY_SIZE(ad2s1210_channels); | |
bf52f059 | 715 | indio_dev->name = spi_get_device_id(spi)->name; |
817e5c65 | 716 | |
b19e9ad5 | 717 | ret = iio_device_register(indio_dev); |
817e5c65 | 718 | if (ret) |
b19e9ad5 | 719 | goto error_free_gpios; |
817e5c65 | 720 | |
b19e9ad5 | 721 | st->fclkin = spi->max_speed_hz; |
817e5c65 GY |
722 | spi->mode = SPI_MODE_3; |
723 | spi_setup(spi); | |
817e5c65 | 724 | ad2s1210_initial(st); |
b19e9ad5 | 725 | |
817e5c65 GY |
726 | return 0; |
727 | ||
b19e9ad5 JC |
728 | error_free_gpios: |
729 | ad2s1210_free_gpios(st); | |
817e5c65 | 730 | error_free_dev: |
7cbb7537 | 731 | iio_device_free(indio_dev); |
817e5c65 | 732 | error_ret: |
817e5c65 GY |
733 | return ret; |
734 | } | |
735 | ||
447d4f29 | 736 | static int ad2s1210_remove(struct spi_device *spi) |
817e5c65 | 737 | { |
b19e9ad5 | 738 | struct iio_dev *indio_dev = spi_get_drvdata(spi); |
26d25ae3 | 739 | |
3e394407 | 740 | iio_device_unregister(indio_dev); |
d2fffd6c | 741 | ad2s1210_free_gpios(iio_priv(indio_dev)); |
7cbb7537 | 742 | iio_device_free(indio_dev); |
817e5c65 GY |
743 | |
744 | return 0; | |
745 | } | |
746 | ||
bf52f059 JC |
747 | static const struct spi_device_id ad2s1210_id[] = { |
748 | { "ad2s1210" }, | |
749 | {} | |
750 | }; | |
55e4390c | 751 | MODULE_DEVICE_TABLE(spi, ad2s1210_id); |
bf52f059 | 752 | |
817e5c65 GY |
753 | static struct spi_driver ad2s1210_driver = { |
754 | .driver = { | |
755 | .name = DRV_NAME, | |
756 | .owner = THIS_MODULE, | |
757 | }, | |
758 | .probe = ad2s1210_probe, | |
e543acf0 | 759 | .remove = ad2s1210_remove, |
bf52f059 | 760 | .id_table = ad2s1210_id, |
817e5c65 | 761 | }; |
ae6ae6fe | 762 | module_spi_driver(ad2s1210_driver); |
817e5c65 GY |
763 | |
764 | MODULE_AUTHOR("Graff Yang <graff.yang@gmail.com>"); | |
765 | MODULE_DESCRIPTION("Analog Devices AD2S1210 Resolver to Digital SPI driver"); | |
766 | MODULE_LICENSE("GPL v2"); |