blob: 98aa1b92b9d4e78e2510815a1a14685eb36a2296 [file] [log] [blame]
Barry Song7a83f602010-10-27 21:44:06 -04001/*
2 * ADIS16130 Digital Output, High Precision Angular Rate Sensor driver
3 *
4 * Copyright 2010 Analog Devices Inc.
5 *
6 * Licensed under the GPL-2 or later.
7 */
8
Barry Song7a83f602010-10-27 21:44:06 -04009#include <linux/delay.h>
10#include <linux/mutex.h>
11#include <linux/device.h>
12#include <linux/kernel.h>
13#include <linux/spi/spi.h>
14#include <linux/slab.h>
15#include <linux/sysfs.h>
16#include <linux/list.h>
Paul Gortmaker99c97852011-07-03 15:49:50 -040017#include <linux/module.h>
Barry Song7a83f602010-10-27 21:44:06 -040018
Jonathan Cameron06458e22012-04-25 15:54:58 +010019#include <linux/iio/iio.h>
20#include <linux/iio/sysfs.h>
Barry Song7a83f602010-10-27 21:44:06 -040021
Jonathan Cameron8e678752011-02-26 17:30:18 +000022#define ADIS16130_CON 0x0
23#define ADIS16130_CON_RD (1 << 6)
24#define ADIS16130_IOP 0x1
25
26/* 1 = data-ready signal low when unread data on all channels; */
27#define ADIS16130_IOP_ALL_RDY (1 << 3)
28#define ADIS16130_IOP_SYNC (1 << 0) /* 1 = synchronization enabled */
29#define ADIS16130_RATEDATA 0x8 /* Gyroscope output, rate of rotation */
30#define ADIS16130_TEMPDATA 0xA /* Temperature output */
31#define ADIS16130_RATECS 0x28 /* Gyroscope channel setup */
32#define ADIS16130_RATECS_EN (1 << 3) /* 1 = channel enable; */
33#define ADIS16130_TEMPCS 0x2A /* Temperature channel setup */
34#define ADIS16130_TEMPCS_EN (1 << 3)
35#define ADIS16130_RATECONV 0x30
36#define ADIS16130_TEMPCONV 0x32
37#define ADIS16130_MODE 0x38
38#define ADIS16130_MODE_24BIT (1 << 1) /* 1 = 24-bit resolution; */
39
40/**
41 * struct adis16130_state - device instance specific data
42 * @us: actual spi_device to write data
Jonathan Cameron8e678752011-02-26 17:30:18 +000043 * @buf_lock: mutex to protect tx and rx
44 * @buf: unified tx/rx buffer
45 **/
46struct adis16130_state {
47 struct spi_device *us;
Jonathan Cameron8e678752011-02-26 17:30:18 +000048 struct mutex buf_lock;
49 u8 buf[4] ____cacheline_aligned;
50};
Barry Song7a83f602010-10-27 21:44:06 -040051
Jonathan Cameronae0b4bdd2011-08-12 17:47:59 +010052static int adis16130_spi_read(struct iio_dev *indio_dev, u8 reg_addr, u32 *val)
Barry Song7a83f602010-10-27 21:44:06 -040053{
54 int ret;
Jonathan Cameron9f8632d2011-06-27 13:07:45 +010055 struct adis16130_state *st = iio_priv(indio_dev);
Jonathan Cameroncdf6e812011-08-12 16:55:28 +010056 struct spi_message msg;
57 struct spi_transfer xfer = {
58 .tx_buf = st->buf,
59 .rx_buf = st->buf,
Jonathan Camerona5e73632011-08-12 17:08:41 +010060 .len = 4,
Jonathan Cameroncdf6e812011-08-12 16:55:28 +010061 };
Barry Song7a83f602010-10-27 21:44:06 -040062
63 mutex_lock(&st->buf_lock);
64
Jonathan Cameron8e678752011-02-26 17:30:18 +000065 st->buf[0] = ADIS16130_CON_RD | reg_addr;
Jonathan Cameroncdf6e812011-08-12 16:55:28 +010066 st->buf[1] = st->buf[2] = st->buf[3] = 0;
Barry Song7a83f602010-10-27 21:44:06 -040067
Jonathan Cameroncdf6e812011-08-12 16:55:28 +010068 spi_message_init(&msg);
69 spi_message_add_tail(&xfer, &msg);
70 ret = spi_sync(st->us, &msg);
Jonathan Camerona5e73632011-08-12 17:08:41 +010071 ret = spi_read(st->us, st->buf, 4);
Barry Song7a83f602010-10-27 21:44:06 -040072
Jonathan Camerona5e73632011-08-12 17:08:41 +010073 if (ret == 0)
74 *val = (st->buf[1] << 16) | (st->buf[2] << 8) | st->buf[3];
Barry Song7a83f602010-10-27 21:44:06 -040075 mutex_unlock(&st->buf_lock);
76
77 return ret;
78}
79
Jonathan Cameronae0b4bdd2011-08-12 17:47:59 +010080static int adis16130_read_raw(struct iio_dev *indio_dev,
81 struct iio_chan_spec const *chan,
82 int *val, int *val2,
83 long mask)
Barry Song7a83f602010-10-27 21:44:06 -040084{
Jonathan Cameronae0b4bdd2011-08-12 17:47:59 +010085 int ret;
86 u32 temp;
Barry Song7a83f602010-10-27 21:44:06 -040087
88 /* Take the iio_dev status lock */
89 mutex_lock(&indio_dev->mlock);
Jonathan Cameronae0b4bdd2011-08-12 17:47:59 +010090 ret = adis16130_spi_read(indio_dev, chan->address, &temp);
Barry Song7a83f602010-10-27 21:44:06 -040091 mutex_unlock(&indio_dev->mlock);
Jonathan Cameronae0b4bdd2011-08-12 17:47:59 +010092 if (ret)
Barry Song7a83f602010-10-27 21:44:06 -040093 return ret;
Jonathan Cameronae0b4bdd2011-08-12 17:47:59 +010094 *val = temp;
95 return IIO_VAL_INT;
Barry Song7a83f602010-10-27 21:44:06 -040096}
97
Jonathan Cameronae0b4bdd2011-08-12 17:47:59 +010098static const struct iio_chan_spec adis16130_channels[] = {
99 {
Jonathan Cameron41ea0402011-10-05 15:27:59 +0100100 .type = IIO_ANGL_VEL,
Jonathan Cameronae0b4bdd2011-08-12 17:47:59 +0100101 .modified = 1,
102 .channel2 = IIO_MOD_Z,
Jonathan Cameronfbaff212012-04-15 17:41:20 +0100103 .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT,
Jonathan Cameronae0b4bdd2011-08-12 17:47:59 +0100104 .address = ADIS16130_RATEDATA,
105 }, {
106 .type = IIO_TEMP,
107 .indexed = 1,
108 .channel = 0,
Jonathan Cameronfbaff212012-04-15 17:41:20 +0100109 .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT,
Jonathan Cameronae0b4bdd2011-08-12 17:47:59 +0100110 .address = ADIS16130_TEMPDATA,
111 }
Barry Song7a83f602010-10-27 21:44:06 -0400112};
113
Jonathan Cameron6fe81352011-05-18 14:42:37 +0100114static const struct iio_info adis16130_info = {
Jonathan Cameronae0b4bdd2011-08-12 17:47:59 +0100115 .read_raw = &adis16130_read_raw,
Jonathan Cameron6fe81352011-05-18 14:42:37 +0100116 .driver_module = THIS_MODULE,
117};
118
Barry Song7a83f602010-10-27 21:44:06 -0400119static int __devinit adis16130_probe(struct spi_device *spi)
120{
Jonathan Cameron7a9f4372011-02-11 14:20:02 +0000121 int ret;
Jonathan Cameron9f8632d2011-06-27 13:07:45 +0100122 struct adis16130_state *st;
123 struct iio_dev *indio_dev;
124
125 /* setup the industrialio driver allocated elements */
126 indio_dev = iio_allocate_device(sizeof(*st));
127 if (indio_dev == NULL) {
128 ret = -ENOMEM;
Barry Song7a83f602010-10-27 21:44:06 -0400129 goto error_ret;
130 }
Jonathan Cameron9f8632d2011-06-27 13:07:45 +0100131 st = iio_priv(indio_dev);
Barry Song7a83f602010-10-27 21:44:06 -0400132 /* this is only used for removal purposes */
Jonathan Cameron9f8632d2011-06-27 13:07:45 +0100133 spi_set_drvdata(spi, indio_dev);
Barry Song7a83f602010-10-27 21:44:06 -0400134 st->us = spi;
135 mutex_init(&st->buf_lock);
Jonathan Cameron9f8632d2011-06-27 13:07:45 +0100136 indio_dev->name = spi->dev.driver->name;
Jonathan Cameronae0b4bdd2011-08-12 17:47:59 +0100137 indio_dev->channels = adis16130_channels;
138 indio_dev->num_channels = ARRAY_SIZE(adis16130_channels);
Jonathan Cameron9f8632d2011-06-27 13:07:45 +0100139 indio_dev->dev.parent = &spi->dev;
140 indio_dev->info = &adis16130_info;
141 indio_dev->modes = INDIO_DIRECT_MODE;
Barry Song7a83f602010-10-27 21:44:06 -0400142
Jonathan Cameron9f8632d2011-06-27 13:07:45 +0100143 ret = iio_device_register(indio_dev);
Barry Song7a83f602010-10-27 21:44:06 -0400144 if (ret)
145 goto error_free_dev;
146
Barry Song7a83f602010-10-27 21:44:06 -0400147 return 0;
148
Barry Song7a83f602010-10-27 21:44:06 -0400149error_free_dev:
Jonathan Cameron9f8632d2011-06-27 13:07:45 +0100150 iio_free_device(indio_dev);
151
Barry Song7a83f602010-10-27 21:44:06 -0400152error_ret:
153 return ret;
154}
155
156/* fixme, confirm ordering in this function */
157static int adis16130_remove(struct spi_device *spi)
158{
Jonathan Cameron9f8632d2011-06-27 13:07:45 +0100159 iio_device_unregister(spi_get_drvdata(spi));
Jonathan Camerond2fffd62011-10-14 14:46:58 +0100160 iio_free_device(spi_get_drvdata(spi));
Barry Song7a83f602010-10-27 21:44:06 -0400161
162 return 0;
163}
164
165static struct spi_driver adis16130_driver = {
166 .driver = {
167 .name = "adis16130",
168 .owner = THIS_MODULE,
169 },
170 .probe = adis16130_probe,
171 .remove = __devexit_p(adis16130_remove),
172};
Lars-Peter Clausenae6ae6f2011-11-16 10:13:39 +0100173module_spi_driver(adis16130_driver);
Barry Song7a83f602010-10-27 21:44:06 -0400174
175MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
Jonathan Cameron8e678752011-02-26 17:30:18 +0000176MODULE_DESCRIPTION("Analog Devices ADIS16130 High Precision Angular Rate");
Barry Song7a83f602010-10-27 21:44:06 -0400177MODULE_LICENSE("GPL v2");
Lars-Peter Clausen55e43902011-11-16 08:53:31 +0100178MODULE_ALIAS("spi:adis16130");