]> bbs.cooldavid.org Git - net-next-2.6.git/blame - drivers/staging/adis16255/adis16255.c
Staging: adis16255: add proper section markings to hotplug funcs
[net-next-2.6.git] / drivers / staging / adis16255 / adis16255.c
CommitLineData
3c034cce 1/*
2 * Analog Devices ADIS16250/ADIS16255 Low Power Gyroscope
3 *
4 * Written by: Matthias Brugger <m_brugger@web.de>
5 *
6 * Copyright (C) 2010 Fraunhofer Institute for Integrated Circuits
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the
20 * Free Software Foundation, Inc.,
21 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 */
23
e390b07b
MB
24/*
25 * The driver just has a bare interface to the sysfs (sample rate in Hz,
26 * orientation (x, y, z) and gyroscope data in °/sec.
27 *
28 * It should be added to iio subsystem when this has left staging.
29 *
30 */
31
3c034cce 32#include <linux/init.h>
33#include <linux/module.h>
34#include <linux/device.h>
35#include <linux/list.h>
36#include <linux/errno.h>
37#include <linux/mutex.h>
38#include <linux/slab.h>
39
40#include <linux/interrupt.h>
41#include <linux/sysfs.h>
42#include <linux/stat.h>
43#include <linux/delay.h>
44
45#include <linux/gpio.h>
46
47#include <linux/spi/spi.h>
48#include <linux/workqueue.h>
49
50#include "adis16255.h"
51
52#define ADIS_STATUS 0x3d
53#define ADIS_SMPL_PRD_MSB 0x37
54#define ADIS_SMPL_PRD_LSB 0x36
55#define ADIS_MSC_CTRL_MSB 0x35
56#define ADIS_MSC_CTRL_LSB 0x34
57#define ADIS_GPIO_CTRL 0x33
58#define ADIS_ALM_SMPL1 0x25
59#define ADIS_ALM_MAG1 0x21
60#define ADIS_GYRO_SCALE 0x17
61#define ADIS_GYRO_OUT 0x05
62#define ADIS_SUPPLY_OUT 0x03
63#define ADIS_ENDURANCE 0x01
64
65/*
66 * data structure for every sensor
67 *
68 * @dev: Driver model representation of the device.
69 * @spi: Pointer to the spi device which will manage i/o to spi bus.
70 * @data: Last read data from device.
71 * @irq_adis: GPIO Number of IRQ signal
72 * @irq: irq line manage by kernel
e390b07b 73 * @negative: indicates if sensor is upside down (negative == 1)
3c034cce 74 * @direction: indicates axis (x, y, z) the sensor is meassuring
75 */
76struct spi_adis16255_data {
77 struct device dev;
78 struct spi_device *spi;
79 s16 data;
3c034cce 80 int irq;
81 u8 negative;
82 char direction;
83};
84
85/*-------------------------------------------------------------------------*/
86
87static int spi_adis16255_read_data(struct spi_adis16255_data *spiadis,
88 u8 adr,
89 u8 *rbuf)
90{
e390b07b 91 struct spi_device *spi = spiadis->spi;
3c034cce 92 struct spi_message msg;
93 struct spi_transfer xfer1, xfer2;
94 u8 *buf, *rx;
95 int ret;
96
e390b07b
MB
97 buf = kzalloc(4, GFP_KERNEL);
98 if (buf == NULL)
3c034cce 99 return -ENOMEM;
100
e390b07b
MB
101 rx = kzalloc(4, GFP_KERNEL);
102 if (rx == NULL) {
103 ret = -ENOMEM;
3c034cce 104 goto err_buf;
105 }
106
e390b07b 107 buf[0] = adr;
3c034cce 108
109 spi_message_init(&msg);
110 memset(&xfer1, 0, sizeof(xfer1));
111 memset(&xfer2, 0, sizeof(xfer2));
112
e390b07b
MB
113 xfer1.tx_buf = buf;
114 xfer1.rx_buf = buf + 2;
115 xfer1.len = 2;
116 xfer1.delay_usecs = 9;
3c034cce 117
e390b07b
MB
118 xfer2.tx_buf = rx + 2;
119 xfer2.rx_buf = rx;
120 xfer2.len = 2;
3c034cce 121
122 spi_message_add_tail(&xfer1, &msg);
123 spi_message_add_tail(&xfer2, &msg);
124
e390b07b
MB
125 ret = spi_sync(spi, &msg);
126 if (ret == 0) {
127 rbuf[0] = rx[0];
128 rbuf[1] = rx[1];
3c034cce 129 }
130
131 kfree(rx);
132err_buf:
133 kfree(buf);
134
135 return ret;
136}
137
138static int spi_adis16255_write_data(struct spi_adis16255_data *spiadis,
139 u8 adr1,
140 u8 adr2,
141 u8 *wbuf)
142{
e390b07b 143 struct spi_device *spi = spiadis->spi;
3c034cce 144 struct spi_message msg;
145 struct spi_transfer xfer1, xfer2;
146 u8 *buf, *rx;
147 int ret;
148
e390b07b
MB
149 buf = kmalloc(4, GFP_KERNEL);
150 if (buf == NULL)
3c034cce 151 return -ENOMEM;
152
e390b07b
MB
153 rx = kzalloc(4, GFP_KERNEL);
154 if (rx == NULL) {
155 ret = -ENOMEM;
3c034cce 156 goto err_buf;
157 }
158
159 spi_message_init(&msg);
160 memset(&xfer1, 0, sizeof(xfer1));
161 memset(&xfer2, 0, sizeof(xfer2));
162
e390b07b
MB
163 buf[0] = adr1 | 0x80;
164 buf[1] = *wbuf;
3c034cce 165
e390b07b
MB
166 buf[2] = adr2 | 0x80;
167 buf[3] = *(wbuf + 1);
3c034cce 168
e390b07b
MB
169 xfer1.tx_buf = buf;
170 xfer1.rx_buf = rx;
171 xfer1.len = 2;
172 xfer1.delay_usecs = 9;
3c034cce 173
e390b07b
MB
174 xfer2.tx_buf = buf+2;
175 xfer2.rx_buf = rx+2;
176 xfer2.len = 2;
3c034cce 177
178 spi_message_add_tail(&xfer1, &msg);
179 spi_message_add_tail(&xfer2, &msg);
180
e390b07b
MB
181 ret = spi_sync(spi, &msg);
182 if (ret != 0)
183 dev_warn(&spi->dev, "write data to %#x %#x failed\n",
3c034cce 184 buf[0], buf[2]);
185
186 kfree(rx);
187err_buf:
188 kfree(buf);
189 return ret;
190}
191
192/*-------------------------------------------------------------------------*/
193
194static irqreturn_t adis_irq_thread(int irq, void *dev_id)
195{
e390b07b 196 struct spi_adis16255_data *spiadis = dev_id;
3c034cce 197 int status;
e390b07b
MB
198 u16 value = 0;
199
200 status = spi_adis16255_read_data(spiadis, ADIS_GYRO_OUT, (u8 *)&value);
201 if (status != 0) {
3c034cce 202 dev_warn(&spiadis->spi->dev, "SPI FAILED\n");
e390b07b 203 goto exit;
3c034cce 204 }
205
e390b07b
MB
206 /* perform on new data only... */
207 if (value & 0x8000) {
208 /* delete error and new data bit */
209 value = value & 0x3fff;
210 /* set negative value */
211 if (value & 0x2000)
212 value = value | 0xe000;
213
214 if (likely(spiadis->negative))
215 value = -value;
216
217 spiadis->data = (s16) value;
218 }
219
220exit:
3c034cce 221 return IRQ_HANDLED;
222}
223
224/*-------------------------------------------------------------------------*/
225
226ssize_t adis16255_show_data(struct device *device,
227 struct device_attribute *da,
228 char *buf)
229{
e390b07b 230 struct spi_adis16255_data *spiadis = dev_get_drvdata(device);
3c034cce 231 return snprintf(buf, PAGE_SIZE, "%d\n", spiadis->data);
232}
233DEVICE_ATTR(data, S_IRUGO , adis16255_show_data, NULL);
234
235ssize_t adis16255_show_direction(struct device *device,
236 struct device_attribute *da,
237 char *buf)
238{
e390b07b 239 struct spi_adis16255_data *spiadis = dev_get_drvdata(device);
3c034cce 240 return snprintf(buf, PAGE_SIZE, "%c\n", spiadis->direction);
241}
242DEVICE_ATTR(direction, S_IRUGO , adis16255_show_direction, NULL);
243
e390b07b
MB
244ssize_t adis16255_show_sample_rate(struct device *device,
245 struct device_attribute *da,
246 char *buf)
247{
248 struct spi_adis16255_data *spiadis = dev_get_drvdata(device);
249 int status = 0;
250 u16 value = 0;
251 int ts = 0;
252
253 status = spi_adis16255_read_data(spiadis, ADIS_SMPL_PRD_MSB,
254 (u8 *)&value);
255 if (status != 0)
256 return -EINVAL;
257
258 if (value & 0x80) {
259 /* timebase = 60.54 ms */
260 ts = 60540 * ((0x7f & value) + 1);
261 } else {
262 /* timebase = 1.953 ms */
263 ts = 1953 * ((0x7f & value) + 1);
264 }
265
266 return snprintf(buf, PAGE_SIZE, "%d\n", (1000*1000)/ts);
267}
268DEVICE_ATTR(sample_rate, S_IRUGO , adis16255_show_sample_rate, NULL);
269
270static struct attribute *adis16255_attributes[] = {
3c034cce 271 &dev_attr_data.attr,
272 &dev_attr_direction.attr,
e390b07b 273 &dev_attr_sample_rate.attr,
3c034cce 274 NULL
275};
276
e390b07b
MB
277static const struct attribute_group adis16255_attr_group = {
278 .attrs = adis16255_attributes,
3c034cce 279};
280
281/*-------------------------------------------------------------------------*/
282
e390b07b
MB
283static int spi_adis16255_shutdown(struct spi_adis16255_data *spiadis)
284{
285 u16 value = 0;
286 /* turn sensor off */
287 spi_adis16255_write_data(spiadis,
288 ADIS_SMPL_PRD_MSB, ADIS_SMPL_PRD_LSB,
289 (u8 *)&value);
290 spi_adis16255_write_data(spiadis,
291 ADIS_MSC_CTRL_MSB, ADIS_MSC_CTRL_LSB,
292 (u8 *)&value);
293 return 0;
294}
295
296static int spi_adis16255_bringup(struct spi_adis16255_data *spiadis)
3c034cce 297{
e390b07b
MB
298 int status = 0;
299 u16 value = 0;
300
301 status = spi_adis16255_read_data(spiadis, ADIS_GYRO_SCALE,
302 (u8 *)&value);
303 if (status != 0)
304 goto err;
305 if (value != 0x0800) {
306 dev_warn(&spiadis->spi->dev, "Scale factor is none default"
307 "value (%.4x)\n", value);
308 }
309
310 /* timebase = 1.953 ms, Ns = 0 -> 512 Hz sample rate */
311 value = 0x0001;
312 status = spi_adis16255_write_data(spiadis,
313 ADIS_SMPL_PRD_MSB, ADIS_SMPL_PRD_LSB,
314 (u8 *)&value);
315 if (status != 0)
316 goto err;
317
318 /* start internal self-test */
319 value = 0x0400;
320 status = spi_adis16255_write_data(spiadis,
321 ADIS_MSC_CTRL_MSB, ADIS_MSC_CTRL_LSB,
322 (u8 *)&value);
323 if (status != 0)
324 goto err;
325
326 /* wait 35 ms to finish self-test */
327 msleep(35);
328
329 value = 0x0000;
330 status = spi_adis16255_read_data(spiadis, ADIS_STATUS,
331 (u8 *)&value);
332 if (status != 0)
333 goto err;
334
335 if (value & 0x23) {
336 if (value & 0x20) {
337 dev_warn(&spiadis->spi->dev, "self-test error\n");
338 status = -ENODEV;
339 goto err;
340 } else if (value & 0x3) {
341 dev_warn(&spiadis->spi->dev, "Sensor voltage"
342 "out of range.\n");
343 status = -ENODEV;
344 goto err;
345 }
346 }
3c034cce 347
e390b07b
MB
348 /* set interrupt to active high on DIO0 when data ready */
349 value = 0x0006;
350 status = spi_adis16255_write_data(spiadis,
351 ADIS_MSC_CTRL_MSB, ADIS_MSC_CTRL_LSB,
352 (u8 *)&value);
353 if (status != 0)
354 goto err;
355 return status;
356
357err:
358 spi_adis16255_shutdown(spiadis);
359 return status;
360}
361
362/*-------------------------------------------------------------------------*/
3c034cce 363
c3dee74f 364static int __devinit spi_adis16255_probe(struct spi_device *spi)
e390b07b
MB
365{
366
367 struct adis16255_init_data *init_data = spi->dev.platform_data;
3c034cce 368 struct spi_adis16255_data *spiadis;
e390b07b 369 int status = 0;
3c034cce 370
e390b07b 371 spiadis = kzalloc(sizeof(*spiadis), GFP_KERNEL);
3c034cce 372 if (!spiadis)
373 return -ENOMEM;
374
e390b07b
MB
375 spiadis->spi = spi;
376 spiadis->direction = init_data->direction;
3c034cce 377
378 if (init_data->negative)
e390b07b 379 spiadis->negative = 1;
3c034cce 380
e390b07b
MB
381 status = gpio_request(init_data->irq, "adis16255");
382 if (status != 0)
3c034cce 383 goto err;
384
e390b07b
MB
385 status = gpio_direction_input(init_data->irq);
386 if (status != 0)
3c034cce 387 goto gpio_err;
388
e390b07b 389 spiadis->irq = gpio_to_irq(init_data->irq);
3c034cce 390
e390b07b 391 status = request_threaded_irq(spiadis->irq,
3c034cce 392 NULL, adis_irq_thread,
393 IRQF_DISABLED, "adis-driver", spiadis);
394
e390b07b 395 if (status != 0) {
3c034cce 396 dev_err(&spi->dev, "IRQ request failed\n");
397 goto gpio_err;
398 }
399
e390b07b 400 dev_dbg(&spi->dev, "GPIO %d IRQ %d\n", init_data->irq, spiadis->irq);
3c034cce 401
402 dev_set_drvdata(&spi->dev, spiadis);
e390b07b
MB
403 status = sysfs_create_group(&spi->dev.kobj, &adis16255_attr_group);
404 if (status != 0)
405 goto irq_err;
3c034cce 406
e390b07b
MB
407 status = spi_adis16255_bringup(spiadis);
408 if (status != 0)
409 goto irq_err;
3c034cce 410
e390b07b 411 dev_info(&spi->dev, "spi_adis16255 driver added!\n");
3c034cce 412
413 return status;
414
415irq_err:
416 free_irq(spiadis->irq, spiadis);
417gpio_err:
e390b07b 418 gpio_free(init_data->irq);
3c034cce 419err:
420 kfree(spiadis);
421 return status;
422}
423
c3dee74f 424static int __devexit spi_adis16255_remove(struct spi_device *spi)
3c034cce 425{
e390b07b 426 struct spi_adis16255_data *spiadis = dev_get_drvdata(&spi->dev);
3c034cce 427
e390b07b 428 spi_adis16255_shutdown(spiadis);
3c034cce 429
430 free_irq(spiadis->irq, spiadis);
e390b07b 431 gpio_free(irq_to_gpio(spiadis->irq));
3c034cce 432
433 sysfs_remove_group(&spiadis->spi->dev.kobj, &adis16255_attr_group);
434
435 kfree(spiadis);
436
437 dev_info(&spi->dev, "spi_adis16255 driver removed!\n");
438 return 0;
439}
440
e390b07b
MB
441static struct spi_driver spi_adis16255_drv = {
442 .driver = {
443 .name = "spi_adis16255",
444 .owner = THIS_MODULE,
3c034cce 445 },
e390b07b
MB
446 .probe = spi_adis16255_probe,
447 .remove = __devexit_p(spi_adis16255_remove),
3c034cce 448};
449
450/*-------------------------------------------------------------------------*/
451
452static int __init spi_adis16255_init(void)
453{
454 return spi_register_driver(&spi_adis16255_drv);
455}
456module_init(spi_adis16255_init);
457
458static void __exit spi_adis16255_exit(void)
459{
460 spi_unregister_driver(&spi_adis16255_drv);
461}
462module_exit(spi_adis16255_exit);
463
464MODULE_AUTHOR("Matthias Brugger");
465MODULE_DESCRIPTION("SPI device driver for ADIS16255 sensor");
466MODULE_LICENSE("GPL");