]>
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 */ | |
cdd6dee0 | 70 | #define AD2S1210_DEF_TCK (1000000000 / AD2S1210_DEF_CLKIN) |
817e5c65 GY |
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 | }; | |
8d80ccbb | 101 | |
b19e9ad5 JC |
102 | static inline void ad2s1210_set_mode(enum ad2s1210_mode mode, |
103 | struct ad2s1210_state *st) | |
817e5c65 | 104 | { |
b19e9ad5 JC |
105 | gpio_set_value(st->pdata->a[0], ad2s1210_mode_vals[mode][0]); |
106 | gpio_set_value(st->pdata->a[1], ad2s1210_mode_vals[mode][1]); | |
817e5c65 GY |
107 | st->mode = mode; |
108 | } | |
109 | ||
110 | /* write 1 bytes (address or data) to the chip */ | |
b19e9ad5 | 111 | static int ad2s1210_config_write(struct ad2s1210_state *st, u8 data) |
817e5c65 | 112 | { |
b19e9ad5 | 113 | int ret; |
817e5c65 | 114 | |
b19e9ad5 | 115 | ad2s1210_set_mode(MOD_CONFIG, st); |
817e5c65 | 116 | st->tx[0] = data; |
b19e9ad5 JC |
117 | ret = spi_write(st->sdev, st->tx, 1); |
118 | if (ret < 0) | |
817e5c65 | 119 | return ret; |
b19e9ad5 JC |
120 | st->old_data = true; |
121 | ||
122 | return 0; | |
817e5c65 GY |
123 | } |
124 | ||
125 | /* read value from one of the registers */ | |
b19e9ad5 | 126 | static int ad2s1210_config_read(struct ad2s1210_state *st, |
ae6394aa | 127 | unsigned char address) |
b19e9ad5 JC |
128 | { |
129 | struct spi_transfer xfer = { | |
130 | .len = 2, | |
131 | .rx_buf = st->rx, | |
132 | .tx_buf = st->tx, | |
133 | }; | |
817e5c65 GY |
134 | int ret = 0; |
135 | ||
b19e9ad5 | 136 | ad2s1210_set_mode(MOD_CONFIG, st); |
b19e9ad5 JC |
137 | st->tx[0] = address | AD2S1210_MSB_IS_HIGH; |
138 | st->tx[1] = AD2S1210_REG_FAULT; | |
ad6c46b0 | 139 | ret = spi_sync_transfer(st->sdev, &xfer, 1); |
b19e9ad5 | 140 | if (ret < 0) |
817e5c65 | 141 | return ret; |
b19e9ad5 JC |
142 | st->old_data = true; |
143 | ||
144 | return st->rx[1]; | |
817e5c65 GY |
145 | } |
146 | ||
b19e9ad5 JC |
147 | static inline |
148 | int ad2s1210_update_frequency_control_word(struct ad2s1210_state *st) | |
817e5c65 | 149 | { |
b19e9ad5 | 150 | int ret; |
817e5c65 | 151 | unsigned char fcw; |
b19e9ad5 | 152 | |
817e5c65 | 153 | fcw = (unsigned char)(st->fexcit * (1 << 15) / st->fclkin); |
b19e9ad5 | 154 | if (fcw < AD2S1210_MIN_FCW || fcw > AD2S1210_MAX_FCW) { |
8ac7f9b3 | 155 | dev_err(&st->sdev->dev, "ad2s1210: FCW out of range\n"); |
b19e9ad5 JC |
156 | return -ERANGE; |
157 | } | |
158 | ||
159 | ret = ad2s1210_config_write(st, AD2S1210_REG_EXCIT_FREQ); | |
160 | if (ret < 0) | |
161 | return ret; | |
162 | ||
163 | return ad2s1210_config_write(st, fcw); | |
817e5c65 GY |
164 | } |
165 | ||
b19e9ad5 | 166 | static unsigned char ad2s1210_read_resolution_pin(struct ad2s1210_state *st) |
817e5c65 | 167 | { |
b19e9ad5 JC |
168 | return ad2s1210_resolution_value[ |
169 | (gpio_get_value(st->pdata->res[0]) << 1) | | |
170 | gpio_get_value(st->pdata->res[1])]; | |
817e5c65 | 171 | } |
b19e9ad5 JC |
172 | |
173 | static const int ad2s1210_res_pins[4][2] = { | |
174 | { 0, 0 }, {0, 1}, {1, 0}, {1, 1} | |
175 | }; | |
176 | ||
177 | static inline void ad2s1210_set_resolution_pin(struct ad2s1210_state *st) | |
817e5c65 | 178 | { |
b19e9ad5 | 179 | gpio_set_value(st->pdata->res[0], |
cdd6dee0 | 180 | ad2s1210_res_pins[(st->resolution - 10) / 2][0]); |
b19e9ad5 | 181 | gpio_set_value(st->pdata->res[1], |
cdd6dee0 | 182 | ad2s1210_res_pins[(st->resolution - 10) / 2][1]); |
817e5c65 | 183 | } |
817e5c65 | 184 | |
b19e9ad5 | 185 | static inline int ad2s1210_soft_reset(struct ad2s1210_state *st) |
817e5c65 | 186 | { |
b19e9ad5 JC |
187 | int ret; |
188 | ||
189 | ret = ad2s1210_config_write(st, AD2S1210_REG_SOFT_RESET); | |
190 | if (ret < 0) | |
191 | return ret; | |
192 | ||
193 | return ad2s1210_config_write(st, 0x0); | |
817e5c65 GY |
194 | } |
195 | ||
817e5c65 | 196 | static ssize_t ad2s1210_show_fclkin(struct device *dev, |
b19e9ad5 JC |
197 | struct device_attribute *attr, |
198 | char *buf) | |
817e5c65 | 199 | { |
a651cf9b | 200 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
8a689c11 | 201 | |
31a3dda6 | 202 | return sprintf(buf, "%u\n", st->fclkin); |
817e5c65 GY |
203 | } |
204 | ||
205 | static ssize_t ad2s1210_store_fclkin(struct device *dev, | |
b19e9ad5 JC |
206 | struct device_attribute *attr, |
207 | const char *buf, | |
208 | size_t len) | |
817e5c65 | 209 | { |
a651cf9b | 210 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
e5e26dd5 | 211 | unsigned int fclkin; |
817e5c65 GY |
212 | int ret; |
213 | ||
e5e26dd5 | 214 | ret = kstrtouint(buf, 10, &fclkin); |
b19e9ad5 JC |
215 | if (ret) |
216 | return ret; | |
217 | if (fclkin < AD2S1210_MIN_CLKIN || fclkin > AD2S1210_MAX_CLKIN) { | |
92371df8 | 218 | dev_err(dev, "ad2s1210: fclkin out of range\n"); |
817e5c65 GY |
219 | return -EINVAL; |
220 | } | |
b19e9ad5 JC |
221 | |
222 | mutex_lock(&st->lock); | |
223 | st->fclkin = fclkin; | |
224 | ||
225 | ret = ad2s1210_update_frequency_control_word(st); | |
226 | if (ret < 0) | |
227 | goto error_ret; | |
228 | ret = ad2s1210_soft_reset(st); | |
229 | error_ret: | |
817e5c65 | 230 | mutex_unlock(&st->lock); |
b19e9ad5 JC |
231 | |
232 | return ret < 0 ? ret : len; | |
817e5c65 GY |
233 | } |
234 | ||
235 | static ssize_t ad2s1210_show_fexcit(struct device *dev, | |
b19e9ad5 JC |
236 | struct device_attribute *attr, |
237 | char *buf) | |
817e5c65 | 238 | { |
a651cf9b | 239 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
8a689c11 | 240 | |
31a3dda6 | 241 | return sprintf(buf, "%u\n", st->fexcit); |
817e5c65 GY |
242 | } |
243 | ||
244 | static ssize_t ad2s1210_store_fexcit(struct device *dev, | |
b19e9ad5 JC |
245 | struct device_attribute *attr, |
246 | const char *buf, size_t len) | |
817e5c65 | 247 | { |
a651cf9b | 248 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
e5e26dd5 | 249 | unsigned int fexcit; |
817e5c65 GY |
250 | int ret; |
251 | ||
e5e26dd5 | 252 | ret = kstrtouint(buf, 10, &fexcit); |
b19e9ad5 JC |
253 | if (ret < 0) |
254 | return ret; | |
255 | if (fexcit < AD2S1210_MIN_EXCIT || fexcit > AD2S1210_MAX_EXCIT) { | |
92371df8 HM |
256 | dev_err(dev, |
257 | "ad2s1210: excitation frequency out of range\n"); | |
817e5c65 GY |
258 | return -EINVAL; |
259 | } | |
b19e9ad5 JC |
260 | mutex_lock(&st->lock); |
261 | st->fexcit = fexcit; | |
262 | ret = ad2s1210_update_frequency_control_word(st); | |
263 | if (ret < 0) | |
264 | goto error_ret; | |
265 | ret = ad2s1210_soft_reset(st); | |
266 | error_ret: | |
817e5c65 | 267 | mutex_unlock(&st->lock); |
b19e9ad5 JC |
268 | |
269 | return ret < 0 ? ret : len; | |
817e5c65 GY |
270 | } |
271 | ||
272 | static ssize_t ad2s1210_show_control(struct device *dev, | |
b19e9ad5 JC |
273 | struct device_attribute *attr, |
274 | char *buf) | |
817e5c65 | 275 | { |
a651cf9b | 276 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
b19e9ad5 | 277 | int ret; |
8a689c11 | 278 | |
817e5c65 | 279 | mutex_lock(&st->lock); |
b19e9ad5 | 280 | ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); |
817e5c65 | 281 | mutex_unlock(&st->lock); |
b19e9ad5 | 282 | return ret < 0 ? ret : sprintf(buf, "0x%x\n", ret); |
817e5c65 GY |
283 | } |
284 | ||
285 | static ssize_t ad2s1210_store_control(struct device *dev, | |
ae6394aa ERR |
286 | struct device_attribute *attr, |
287 | const char *buf, size_t len) | |
817e5c65 | 288 | { |
a651cf9b | 289 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
e5e26dd5 | 290 | unsigned char udata; |
817e5c65 GY |
291 | unsigned char data; |
292 | int ret; | |
293 | ||
e5e26dd5 | 294 | ret = kstrtou8(buf, 16, &udata); |
b19e9ad5 JC |
295 | if (ret) |
296 | return -EINVAL; | |
297 | ||
817e5c65 | 298 | mutex_lock(&st->lock); |
b19e9ad5 JC |
299 | ret = ad2s1210_config_write(st, AD2S1210_REG_CONTROL); |
300 | if (ret < 0) | |
301 | goto error_ret; | |
302 | data = udata & AD2S1210_MSB_IS_LOW; | |
303 | ret = ad2s1210_config_write(st, data); | |
304 | if (ret < 0) | |
305 | goto error_ret; | |
306 | ||
307 | ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); | |
308 | if (ret < 0) | |
309 | goto error_ret; | |
310 | if (ret & AD2S1210_MSB_IS_HIGH) { | |
817e5c65 | 311 | ret = -EIO; |
92371df8 HM |
312 | dev_err(dev, |
313 | "ad2s1210: write control register fail\n"); | |
817e5c65 GY |
314 | goto error_ret; |
315 | } | |
b19e9ad5 JC |
316 | st->resolution |
317 | = ad2s1210_resolution_value[data & AD2S1210_SET_RESOLUTION]; | |
318 | if (st->pdata->gpioin) { | |
319 | data = ad2s1210_read_resolution_pin(st); | |
320 | if (data != st->resolution) | |
8ac7f9b3 | 321 | dev_warn(dev, "ad2s1210: resolution settings not match\n"); |
84681c7c | 322 | } else { |
b19e9ad5 | 323 | ad2s1210_set_resolution_pin(st); |
84681c7c | 324 | } |
817e5c65 | 325 | ret = len; |
b19e9ad5 JC |
326 | st->hysteresis = !!(data & AD2S1210_ENABLE_HYSTERESIS); |
327 | ||
817e5c65 GY |
328 | error_ret: |
329 | mutex_unlock(&st->lock); | |
330 | return ret; | |
331 | } | |
332 | ||
333 | static ssize_t ad2s1210_show_resolution(struct device *dev, | |
ae6394aa ERR |
334 | struct device_attribute *attr, |
335 | char *buf) | |
817e5c65 | 336 | { |
a651cf9b | 337 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
8a689c11 | 338 | |
817e5c65 GY |
339 | return sprintf(buf, "%d\n", st->resolution); |
340 | } | |
341 | ||
342 | static ssize_t ad2s1210_store_resolution(struct device *dev, | |
ae6394aa ERR |
343 | struct device_attribute *attr, |
344 | const char *buf, size_t len) | |
817e5c65 | 345 | { |
a651cf9b | 346 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
817e5c65 | 347 | unsigned char data; |
e5e26dd5 | 348 | unsigned char udata; |
817e5c65 GY |
349 | int ret; |
350 | ||
e5e26dd5 | 351 | ret = kstrtou8(buf, 10, &udata); |
b19e9ad5 | 352 | if (ret || udata < 10 || udata > 16) { |
92371df8 | 353 | dev_err(dev, "ad2s1210: resolution out of range\n"); |
817e5c65 GY |
354 | return -EINVAL; |
355 | } | |
356 | mutex_lock(&st->lock); | |
b19e9ad5 JC |
357 | ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); |
358 | if (ret < 0) | |
359 | goto error_ret; | |
360 | data = ret; | |
361 | data &= ~AD2S1210_SET_RESOLUTION; | |
362 | data |= (udata - 10) >> 1; | |
363 | ret = ad2s1210_config_write(st, AD2S1210_REG_CONTROL); | |
364 | if (ret < 0) | |
365 | goto error_ret; | |
366 | ret = ad2s1210_config_write(st, data & AD2S1210_MSB_IS_LOW); | |
367 | if (ret < 0) | |
368 | goto error_ret; | |
369 | ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); | |
370 | if (ret < 0) | |
371 | goto error_ret; | |
372 | data = ret; | |
373 | if (data & AD2S1210_MSB_IS_HIGH) { | |
817e5c65 | 374 | ret = -EIO; |
92371df8 | 375 | dev_err(dev, "ad2s1210: setting resolution fail\n"); |
817e5c65 GY |
376 | goto error_ret; |
377 | } | |
b19e9ad5 JC |
378 | st->resolution |
379 | = ad2s1210_resolution_value[data & AD2S1210_SET_RESOLUTION]; | |
380 | if (st->pdata->gpioin) { | |
381 | data = ad2s1210_read_resolution_pin(st); | |
382 | if (data != st->resolution) | |
8ac7f9b3 | 383 | dev_warn(dev, "ad2s1210: resolution settings not match\n"); |
84681c7c | 384 | } else { |
b19e9ad5 | 385 | ad2s1210_set_resolution_pin(st); |
84681c7c | 386 | } |
817e5c65 GY |
387 | ret = len; |
388 | error_ret: | |
389 | mutex_unlock(&st->lock); | |
390 | return ret; | |
391 | } | |
b19e9ad5 | 392 | |
817e5c65 GY |
393 | /* read the fault register since last sample */ |
394 | static ssize_t ad2s1210_show_fault(struct device *dev, | |
ae6394aa | 395 | struct device_attribute *attr, char *buf) |
817e5c65 | 396 | { |
a651cf9b | 397 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
b19e9ad5 | 398 | int ret; |
817e5c65 GY |
399 | |
400 | mutex_lock(&st->lock); | |
b19e9ad5 | 401 | ret = ad2s1210_config_read(st, AD2S1210_REG_FAULT); |
817e5c65 | 402 | mutex_unlock(&st->lock); |
b19e9ad5 JC |
403 | |
404 | return ret ? ret : sprintf(buf, "0x%x\n", ret); | |
817e5c65 GY |
405 | } |
406 | ||
407 | static ssize_t ad2s1210_clear_fault(struct device *dev, | |
b19e9ad5 JC |
408 | struct device_attribute *attr, |
409 | const char *buf, | |
410 | size_t len) | |
817e5c65 | 411 | { |
a651cf9b | 412 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
b19e9ad5 | 413 | int ret; |
817e5c65 GY |
414 | |
415 | mutex_lock(&st->lock); | |
b19e9ad5 | 416 | gpio_set_value(st->pdata->sample, 0); |
817e5c65 GY |
417 | /* delay (2 * tck + 20) nano seconds */ |
418 | udelay(1); | |
b19e9ad5 JC |
419 | gpio_set_value(st->pdata->sample, 1); |
420 | ret = ad2s1210_config_read(st, AD2S1210_REG_FAULT); | |
421 | if (ret < 0) | |
422 | goto error_ret; | |
423 | gpio_set_value(st->pdata->sample, 0); | |
424 | gpio_set_value(st->pdata->sample, 1); | |
425 | error_ret: | |
817e5c65 GY |
426 | mutex_unlock(&st->lock); |
427 | ||
b19e9ad5 | 428 | return ret < 0 ? ret : len; |
817e5c65 GY |
429 | } |
430 | ||
431 | static ssize_t ad2s1210_show_reg(struct device *dev, | |
b19e9ad5 JC |
432 | struct device_attribute *attr, |
433 | char *buf) | |
817e5c65 | 434 | { |
a651cf9b | 435 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
817e5c65 | 436 | struct iio_dev_attr *iattr = to_iio_dev_attr(attr); |
b19e9ad5 | 437 | int ret; |
817e5c65 GY |
438 | |
439 | mutex_lock(&st->lock); | |
b19e9ad5 | 440 | ret = ad2s1210_config_read(st, iattr->address); |
817e5c65 | 441 | mutex_unlock(&st->lock); |
b19e9ad5 JC |
442 | |
443 | return ret < 0 ? ret : sprintf(buf, "%d\n", ret); | |
817e5c65 GY |
444 | } |
445 | ||
446 | static ssize_t ad2s1210_store_reg(struct device *dev, | |
ae6394aa ERR |
447 | struct device_attribute *attr, |
448 | const char *buf, size_t len) | |
817e5c65 | 449 | { |
a651cf9b | 450 | struct ad2s1210_state *st = iio_priv(dev_to_iio_dev(dev)); |
e5e26dd5 | 451 | unsigned char data; |
817e5c65 GY |
452 | int ret; |
453 | struct iio_dev_attr *iattr = to_iio_dev_attr(attr); | |
454 | ||
e5e26dd5 | 455 | ret = kstrtou8(buf, 10, &data); |
817e5c65 GY |
456 | if (ret) |
457 | return -EINVAL; | |
458 | mutex_lock(&st->lock); | |
b19e9ad5 JC |
459 | ret = ad2s1210_config_write(st, iattr->address); |
460 | if (ret < 0) | |
461 | goto error_ret; | |
462 | ret = ad2s1210_config_write(st, data & AD2S1210_MSB_IS_LOW); | |
463 | error_ret: | |
817e5c65 | 464 | mutex_unlock(&st->lock); |
b19e9ad5 | 465 | return ret < 0 ? ret : len; |
817e5c65 GY |
466 | } |
467 | ||
29148543 JC |
468 | static int ad2s1210_read_raw(struct iio_dev *indio_dev, |
469 | struct iio_chan_spec const *chan, | |
470 | int *val, | |
471 | int *val2, | |
472 | long m) | |
817e5c65 | 473 | { |
29148543 | 474 | struct ad2s1210_state *st = iio_priv(indio_dev); |
105967ad | 475 | u16 negative; |
817e5c65 | 476 | int ret = 0; |
817e5c65 | 477 | u16 pos; |
817e5c65 | 478 | s16 vel; |
817e5c65 | 479 | |
817e5c65 | 480 | mutex_lock(&st->lock); |
b19e9ad5 | 481 | gpio_set_value(st->pdata->sample, 0); |
817e5c65 GY |
482 | /* delay (6 * tck + 20) nano seconds */ |
483 | udelay(1); | |
484 | ||
29148543 JC |
485 | switch (chan->type) { |
486 | case IIO_ANGL: | |
487 | ad2s1210_set_mode(MOD_POS, st); | |
488 | break; | |
489 | case IIO_ANGL_VEL: | |
490 | ad2s1210_set_mode(MOD_VEL, st); | |
491 | break; | |
492 | default: | |
2b7cb7be MR |
493 | ret = -EINVAL; |
494 | break; | |
29148543 JC |
495 | } |
496 | if (ret < 0) | |
497 | goto error_ret; | |
b19e9ad5 | 498 | ret = spi_read(st->sdev, st->rx, 2); |
29148543 | 499 | if (ret < 0) |
817e5c65 | 500 | goto error_ret; |
29148543 JC |
501 | |
502 | switch (chan->type) { | |
503 | case IIO_ANGL: | |
c3f63dd6 | 504 | pos = be16_to_cpup((__be16 *)st->rx); |
29148543 JC |
505 | if (st->hysteresis) |
506 | pos >>= 16 - st->resolution; | |
507 | *val = pos; | |
508 | ret = IIO_VAL_INT; | |
509 | break; | |
510 | case IIO_ANGL_VEL: | |
511 | negative = st->rx[0] & 0x80; | |
c3f63dd6 | 512 | vel = be16_to_cpup((__be16 *)st->rx); |
29148543 JC |
513 | vel >>= 16 - st->resolution; |
514 | if (vel & 0x8000) { | |
515 | negative = (0xffff >> st->resolution) << st->resolution; | |
516 | vel |= negative; | |
517 | } | |
518 | *val = vel; | |
519 | ret = IIO_VAL_INT; | |
520 | break; | |
521 | default: | |
522 | mutex_unlock(&st->lock); | |
523 | return -EINVAL; | |
817e5c65 | 524 | } |
29148543 | 525 | |
817e5c65 | 526 | error_ret: |
b19e9ad5 | 527 | gpio_set_value(st->pdata->sample, 1); |
817e5c65 GY |
528 | /* delay (2 * tck + 20) nano seconds */ |
529 | udelay(1); | |
530 | mutex_unlock(&st->lock); | |
29148543 | 531 | return ret; |
817e5c65 GY |
532 | } |
533 | ||
6144f61a | 534 | static IIO_DEVICE_ATTR(fclkin, 0644, |
b19e9ad5 | 535 | ad2s1210_show_fclkin, ad2s1210_store_fclkin, 0); |
6144f61a | 536 | static IIO_DEVICE_ATTR(fexcit, 0644, |
b19e9ad5 | 537 | ad2s1210_show_fexcit, ad2s1210_store_fexcit, 0); |
6144f61a | 538 | static IIO_DEVICE_ATTR(control, 0644, |
b19e9ad5 | 539 | ad2s1210_show_control, ad2s1210_store_control, 0); |
6144f61a | 540 | static IIO_DEVICE_ATTR(bits, 0644, |
b19e9ad5 | 541 | ad2s1210_show_resolution, ad2s1210_store_resolution, 0); |
6144f61a | 542 | static IIO_DEVICE_ATTR(fault, 0644, |
b19e9ad5 | 543 | ad2s1210_show_fault, ad2s1210_clear_fault, 0); |
29148543 | 544 | |
6144f61a | 545 | static IIO_DEVICE_ATTR(los_thrd, 0644, |
b19e9ad5 JC |
546 | ad2s1210_show_reg, ad2s1210_store_reg, |
547 | AD2S1210_REG_LOS_THRD); | |
6144f61a | 548 | static IIO_DEVICE_ATTR(dos_ovr_thrd, 0644, |
b19e9ad5 JC |
549 | ad2s1210_show_reg, ad2s1210_store_reg, |
550 | AD2S1210_REG_DOS_OVR_THRD); | |
6144f61a | 551 | static IIO_DEVICE_ATTR(dos_mis_thrd, 0644, |
b19e9ad5 JC |
552 | ad2s1210_show_reg, ad2s1210_store_reg, |
553 | AD2S1210_REG_DOS_MIS_THRD); | |
6144f61a | 554 | static IIO_DEVICE_ATTR(dos_rst_max_thrd, 0644, |
b19e9ad5 JC |
555 | ad2s1210_show_reg, ad2s1210_store_reg, |
556 | AD2S1210_REG_DOS_RST_MAX_THRD); | |
6144f61a | 557 | static IIO_DEVICE_ATTR(dos_rst_min_thrd, 0644, |
b19e9ad5 JC |
558 | ad2s1210_show_reg, ad2s1210_store_reg, |
559 | AD2S1210_REG_DOS_RST_MIN_THRD); | |
6144f61a | 560 | static IIO_DEVICE_ATTR(lot_high_thrd, 0644, |
b19e9ad5 JC |
561 | ad2s1210_show_reg, ad2s1210_store_reg, |
562 | AD2S1210_REG_LOT_HIGH_THRD); | |
6144f61a | 563 | static IIO_DEVICE_ATTR(lot_low_thrd, 0644, |
b19e9ad5 JC |
564 | ad2s1210_show_reg, ad2s1210_store_reg, |
565 | AD2S1210_REG_LOT_LOW_THRD); | |
817e5c65 | 566 | |
f4e4b955 | 567 | static const struct iio_chan_spec ad2s1210_channels[] = { |
29148543 JC |
568 | { |
569 | .type = IIO_ANGL, | |
570 | .indexed = 1, | |
571 | .channel = 0, | |
07d0f654 | 572 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), |
29148543 JC |
573 | }, { |
574 | .type = IIO_ANGL_VEL, | |
575 | .indexed = 1, | |
576 | .channel = 0, | |
07d0f654 | 577 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), |
29148543 JC |
578 | } |
579 | }; | |
580 | ||
817e5c65 | 581 | static struct attribute *ad2s1210_attributes[] = { |
817e5c65 GY |
582 | &iio_dev_attr_fclkin.dev_attr.attr, |
583 | &iio_dev_attr_fexcit.dev_attr.attr, | |
584 | &iio_dev_attr_control.dev_attr.attr, | |
585 | &iio_dev_attr_bits.dev_attr.attr, | |
586 | &iio_dev_attr_fault.dev_attr.attr, | |
817e5c65 GY |
587 | &iio_dev_attr_los_thrd.dev_attr.attr, |
588 | &iio_dev_attr_dos_ovr_thrd.dev_attr.attr, | |
589 | &iio_dev_attr_dos_mis_thrd.dev_attr.attr, | |
590 | &iio_dev_attr_dos_rst_max_thrd.dev_attr.attr, | |
591 | &iio_dev_attr_dos_rst_min_thrd.dev_attr.attr, | |
592 | &iio_dev_attr_lot_high_thrd.dev_attr.attr, | |
593 | &iio_dev_attr_lot_low_thrd.dev_attr.attr, | |
594 | NULL, | |
595 | }; | |
596 | ||
597 | static const struct attribute_group ad2s1210_attribute_group = { | |
817e5c65 GY |
598 | .attrs = ad2s1210_attributes, |
599 | }; | |
600 | ||
4ae1c61f | 601 | static int ad2s1210_initial(struct ad2s1210_state *st) |
817e5c65 GY |
602 | { |
603 | unsigned char data; | |
604 | int ret; | |
605 | ||
606 | mutex_lock(&st->lock); | |
b19e9ad5 JC |
607 | if (st->pdata->gpioin) |
608 | st->resolution = ad2s1210_read_resolution_pin(st); | |
609 | else | |
610 | ad2s1210_set_resolution_pin(st); | |
611 | ||
612 | ret = ad2s1210_config_write(st, AD2S1210_REG_CONTROL); | |
613 | if (ret < 0) | |
614 | goto error_ret; | |
615 | data = AD2S1210_DEF_CONTROL & ~(AD2S1210_SET_RESOLUTION); | |
616 | data |= (st->resolution - 10) >> 1; | |
617 | ret = ad2s1210_config_write(st, data); | |
618 | if (ret < 0) | |
619 | goto error_ret; | |
620 | ret = ad2s1210_config_read(st, AD2S1210_REG_CONTROL); | |
621 | if (ret < 0) | |
817e5c65 GY |
622 | goto error_ret; |
623 | ||
b19e9ad5 | 624 | if (ret & AD2S1210_MSB_IS_HIGH) { |
817e5c65 GY |
625 | ret = -EIO; |
626 | goto error_ret; | |
627 | } | |
628 | ||
b19e9ad5 JC |
629 | ret = ad2s1210_update_frequency_control_word(st); |
630 | if (ret < 0) | |
631 | goto error_ret; | |
632 | ret = ad2s1210_soft_reset(st); | |
817e5c65 GY |
633 | error_ret: |
634 | mutex_unlock(&st->lock); | |
635 | return ret; | |
636 | } | |
637 | ||
6fe8135f | 638 | static const struct iio_info ad2s1210_info = { |
3a1d9489 | 639 | .read_raw = ad2s1210_read_raw, |
6fe8135f JC |
640 | .attrs = &ad2s1210_attribute_group, |
641 | .driver_module = THIS_MODULE, | |
642 | }; | |
643 | ||
b19e9ad5 JC |
644 | static int ad2s1210_setup_gpios(struct ad2s1210_state *st) |
645 | { | |
b19e9ad5 | 646 | unsigned long flags = st->pdata->gpioin ? GPIOF_DIR_IN : GPIOF_DIR_OUT; |
93decf36 JC |
647 | struct gpio ad2s1210_gpios[] = { |
648 | { st->pdata->sample, GPIOF_DIR_IN, "sample" }, | |
649 | { st->pdata->a[0], flags, "a0" }, | |
650 | { st->pdata->a[1], flags, "a1" }, | |
651 | { st->pdata->res[0], flags, "res0" }, | |
652 | { st->pdata->res[0], flags, "res1" }, | |
653 | }; | |
b19e9ad5 | 654 | |
93decf36 | 655 | return gpio_request_array(ad2s1210_gpios, ARRAY_SIZE(ad2s1210_gpios)); |
b19e9ad5 JC |
656 | } |
657 | ||
658 | static void ad2s1210_free_gpios(struct ad2s1210_state *st) | |
659 | { | |
93decf36 JC |
660 | unsigned long flags = st->pdata->gpioin ? GPIOF_DIR_IN : GPIOF_DIR_OUT; |
661 | struct gpio ad2s1210_gpios[] = { | |
662 | { st->pdata->sample, GPIOF_DIR_IN, "sample" }, | |
663 | { st->pdata->a[0], flags, "a0" }, | |
664 | { st->pdata->a[1], flags, "a1" }, | |
665 | { st->pdata->res[0], flags, "res0" }, | |
666 | { st->pdata->res[0], flags, "res1" }, | |
667 | }; | |
668 | ||
669 | gpio_free_array(ad2s1210_gpios, ARRAY_SIZE(ad2s1210_gpios)); | |
b19e9ad5 JC |
670 | } |
671 | ||
4ae1c61f | 672 | static int ad2s1210_probe(struct spi_device *spi) |
817e5c65 | 673 | { |
b19e9ad5 | 674 | struct iio_dev *indio_dev; |
817e5c65 | 675 | struct ad2s1210_state *st; |
b19e9ad5 JC |
676 | int ret; |
677 | ||
b5b6e7be | 678 | if (!spi->dev.platform_data) |
b19e9ad5 | 679 | return -EINVAL; |
817e5c65 | 680 | |
50711572 SK |
681 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); |
682 | if (!indio_dev) | |
683 | return -ENOMEM; | |
b19e9ad5 JC |
684 | st = iio_priv(indio_dev); |
685 | st->pdata = spi->dev.platform_data; | |
686 | ret = ad2s1210_setup_gpios(st); | |
687 | if (ret < 0) | |
50711572 | 688 | return ret; |
b19e9ad5 JC |
689 | |
690 | spi_set_drvdata(spi, indio_dev); | |
817e5c65 GY |
691 | |
692 | mutex_init(&st->lock); | |
693 | st->sdev = spi; | |
b19e9ad5 | 694 | st->hysteresis = true; |
817e5c65 | 695 | st->mode = MOD_CONFIG; |
b19e9ad5 | 696 | st->resolution = 12; |
817e5c65 | 697 | st->fexcit = AD2S1210_DEF_EXCIT; |
817e5c65 | 698 | |
b19e9ad5 JC |
699 | indio_dev->dev.parent = &spi->dev; |
700 | indio_dev->info = &ad2s1210_info; | |
701 | indio_dev->modes = INDIO_DIRECT_MODE; | |
29148543 JC |
702 | indio_dev->channels = ad2s1210_channels; |
703 | indio_dev->num_channels = ARRAY_SIZE(ad2s1210_channels); | |
bf52f059 | 704 | indio_dev->name = spi_get_device_id(spi)->name; |
817e5c65 | 705 | |
b19e9ad5 | 706 | ret = iio_device_register(indio_dev); |
817e5c65 | 707 | if (ret) |
b19e9ad5 | 708 | goto error_free_gpios; |
817e5c65 | 709 | |
b19e9ad5 | 710 | st->fclkin = spi->max_speed_hz; |
817e5c65 GY |
711 | spi->mode = SPI_MODE_3; |
712 | spi_setup(spi); | |
817e5c65 | 713 | ad2s1210_initial(st); |
b19e9ad5 | 714 | |
817e5c65 GY |
715 | return 0; |
716 | ||
b19e9ad5 JC |
717 | error_free_gpios: |
718 | ad2s1210_free_gpios(st); | |
817e5c65 GY |
719 | return ret; |
720 | } | |
721 | ||
447d4f29 | 722 | static int ad2s1210_remove(struct spi_device *spi) |
817e5c65 | 723 | { |
b19e9ad5 | 724 | struct iio_dev *indio_dev = spi_get_drvdata(spi); |
26d25ae3 | 725 | |
3e394407 | 726 | iio_device_unregister(indio_dev); |
d2fffd6c | 727 | ad2s1210_free_gpios(iio_priv(indio_dev)); |
817e5c65 GY |
728 | |
729 | return 0; | |
730 | } | |
731 | ||
bf52f059 JC |
732 | static const struct spi_device_id ad2s1210_id[] = { |
733 | { "ad2s1210" }, | |
734 | {} | |
735 | }; | |
55e4390c | 736 | MODULE_DEVICE_TABLE(spi, ad2s1210_id); |
bf52f059 | 737 | |
817e5c65 GY |
738 | static struct spi_driver ad2s1210_driver = { |
739 | .driver = { | |
740 | .name = DRV_NAME, | |
817e5c65 GY |
741 | }, |
742 | .probe = ad2s1210_probe, | |
e543acf0 | 743 | .remove = ad2s1210_remove, |
bf52f059 | 744 | .id_table = ad2s1210_id, |
817e5c65 | 745 | }; |
ae6ae6fe | 746 | module_spi_driver(ad2s1210_driver); |
817e5c65 GY |
747 | |
748 | MODULE_AUTHOR("Graff Yang <graff.yang@gmail.com>"); | |
749 | MODULE_DESCRIPTION("Analog Devices AD2S1210 Resolver to Digital SPI driver"); | |
750 | MODULE_LICENSE("GPL v2"); |