aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/iio/accel
diff options
context:
space:
mode:
authorJonathan Cameron <jic23@kernel.org>2013-01-06 10:10:00 -0500
committerJonathan Cameron <jic23@kernel.org>2013-01-26 05:07:53 -0500
commit81ca486fe54c799033fac1eb6d93baf64df37ba6 (patch)
tree4818c0f5b884504aa9b485f537f35590c1123dbd /drivers/iio/accel
parentd526e513c7dd1bb3f9696c7d38634e5ebf5f0919 (diff)
iio:accel:kxsd9 move out of staging
This is a very simple driver giving basic access to this part over an spi bus. Signed-off-by: Jonathan Cameron <jic23@kernel.org>
Diffstat (limited to 'drivers/iio/accel')
-rw-r--r--drivers/iio/accel/Kconfig7
-rw-r--r--drivers/iio/accel/Makefile1
-rw-r--r--drivers/iio/accel/kxsd9.c291
3 files changed, 299 insertions, 0 deletions
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index 05e996fafc9d..d6003531bd63 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -14,4 +14,11 @@ config HID_SENSOR_ACCEL_3D
14 Say yes here to build support for the HID SENSOR 14 Say yes here to build support for the HID SENSOR
15 accelerometers 3D. 15 accelerometers 3D.
16 16
17config KXSD9
18 tristate "Kionix KXSD9 Accelerometer Driver"
19 depends on SPI
20 help
21 Say yes here to build support for the Kionix KXSD9 accelerometer.
22 Currently this only supports the device via an SPI interface.
23
17endmenu 24endmenu
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index 5bc6855a973e..4e1c85987d13 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -3,3 +3,4 @@
3# 3#
4 4
5obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o 5obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
6obj-$(CONFIG_KXSD9) += kxsd9.o
diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c
new file mode 100644
index 000000000000..4a24c2ee81a9
--- /dev/null
+++ b/drivers/iio/accel/kxsd9.c
@@ -0,0 +1,291 @@
1/*
2 * kxsd9.c simple support for the Kionix KXSD9 3D
3 * accelerometer.
4 *
5 * Copyright (c) 2008-2009 Jonathan Cameron <jic23@kernel.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * The i2c interface is very similar, so shouldn't be a problem once
12 * I have a suitable wire made up.
13 *
14 * TODO: Support the motion detector
15 * Uses register address incrementing so could have a
16 * heavily optimized ring buffer access function.
17 */
18
19#include <linux/device.h>
20#include <linux/kernel.h>
21#include <linux/spi/spi.h>
22#include <linux/sysfs.h>
23#include <linux/slab.h>
24#include <linux/module.h>
25
26#include <linux/iio/iio.h>
27#include <linux/iio/sysfs.h>
28
29#define KXSD9_REG_X 0x00
30#define KXSD9_REG_Y 0x02
31#define KXSD9_REG_Z 0x04
32#define KXSD9_REG_AUX 0x06
33#define KXSD9_REG_RESET 0x0a
34#define KXSD9_REG_CTRL_C 0x0c
35
36#define KXSD9_FS_MASK 0x03
37
38#define KXSD9_REG_CTRL_B 0x0d
39#define KXSD9_REG_CTRL_A 0x0e
40
41#define KXSD9_READ(a) (0x80 | (a))
42#define KXSD9_WRITE(a) (a)
43
44#define KXSD9_STATE_RX_SIZE 2
45#define KXSD9_STATE_TX_SIZE 2
46/**
47 * struct kxsd9_state - device related storage
48 * @buf_lock: protect the rx and tx buffers.
49 * @us: spi device
50 * @rx: single rx buffer storage
51 * @tx: single tx buffer storage
52 **/
53struct kxsd9_state {
54 struct mutex buf_lock;
55 struct spi_device *us;
56 u8 rx[KXSD9_STATE_RX_SIZE] ____cacheline_aligned;
57 u8 tx[KXSD9_STATE_TX_SIZE];
58};
59
60#define KXSD9_SCALE_2G "0.011978"
61#define KXSD9_SCALE_4G "0.023927"
62#define KXSD9_SCALE_6G "0.035934"
63#define KXSD9_SCALE_8G "0.047853"
64
65/* reverse order */
66static const int kxsd9_micro_scales[4] = { 47853, 35934, 23927, 11978 };
67
68static int kxsd9_write_scale(struct iio_dev *indio_dev, int micro)
69{
70 int ret, i;
71 struct kxsd9_state *st = iio_priv(indio_dev);
72 bool foundit = false;
73
74 for (i = 0; i < 4; i++)
75 if (micro == kxsd9_micro_scales[i]) {
76 foundit = true;
77 break;
78 }
79 if (!foundit)
80 return -EINVAL;
81
82 mutex_lock(&st->buf_lock);
83 ret = spi_w8r8(st->us, KXSD9_READ(KXSD9_REG_CTRL_C));
84 if (ret)
85 goto error_ret;
86 st->tx[0] = KXSD9_WRITE(KXSD9_REG_CTRL_C);
87 st->tx[1] = (ret & ~KXSD9_FS_MASK) | i;
88
89 ret = spi_write(st->us, st->tx, 2);
90error_ret:
91 mutex_unlock(&st->buf_lock);
92 return ret;
93}
94
95static int kxsd9_read(struct iio_dev *indio_dev, u8 address)
96{
97 struct spi_message msg;
98 int ret;
99 struct kxsd9_state *st = iio_priv(indio_dev);
100 struct spi_transfer xfers[] = {
101 {
102 .bits_per_word = 8,
103 .len = 1,
104 .delay_usecs = 200,
105 .tx_buf = st->tx,
106 }, {
107 .bits_per_word = 8,
108 .len = 2,
109 .rx_buf = st->rx,
110 },
111 };
112
113 mutex_lock(&st->buf_lock);
114 st->tx[0] = KXSD9_READ(address);
115 spi_message_init(&msg);
116 spi_message_add_tail(&xfers[0], &msg);
117 spi_message_add_tail(&xfers[1], &msg);
118 ret = spi_sync(st->us, &msg);
119 if (ret)
120 return ret;
121 return (((u16)(st->rx[0])) << 8) | (st->rx[1] & 0xF0);
122}
123
124static IIO_CONST_ATTR(accel_scale_available,
125 KXSD9_SCALE_2G " "
126 KXSD9_SCALE_4G " "
127 KXSD9_SCALE_6G " "
128 KXSD9_SCALE_8G);
129
130static struct attribute *kxsd9_attributes[] = {
131 &iio_const_attr_accel_scale_available.dev_attr.attr,
132 NULL,
133};
134
135static int kxsd9_write_raw(struct iio_dev *indio_dev,
136 struct iio_chan_spec const *chan,
137 int val,
138 int val2,
139 long mask)
140{
141 int ret = -EINVAL;
142
143 if (mask == IIO_CHAN_INFO_SCALE) {
144 /* Check no integer component */
145 if (val)
146 return -EINVAL;
147 ret = kxsd9_write_scale(indio_dev, val2);
148 }
149
150 return ret;
151}
152
153static int kxsd9_read_raw(struct iio_dev *indio_dev,
154 struct iio_chan_spec const *chan,
155 int *val, int *val2, long mask)
156{
157 int ret = -EINVAL;
158 struct kxsd9_state *st = iio_priv(indio_dev);
159
160 switch (mask) {
161 case IIO_CHAN_INFO_RAW:
162 ret = kxsd9_read(indio_dev, chan->address);
163 if (ret < 0)
164 goto error_ret;
165 *val = ret;
166 break;
167 case IIO_CHAN_INFO_SCALE:
168 ret = spi_w8r8(st->us, KXSD9_READ(KXSD9_REG_CTRL_C));
169 if (ret)
170 goto error_ret;
171 *val2 = kxsd9_micro_scales[ret & KXSD9_FS_MASK];
172 ret = IIO_VAL_INT_PLUS_MICRO;
173 break;
174 }
175
176error_ret:
177 return ret;
178};
179#define KXSD9_ACCEL_CHAN(axis) \
180 { \
181 .type = IIO_ACCEL, \
182 .modified = 1, \
183 .channel2 = IIO_MOD_##axis, \
184 .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
185 IIO_CHAN_INFO_SCALE_SHARED_BIT, \
186 .address = KXSD9_REG_##axis, \
187 }
188
189static const struct iio_chan_spec kxsd9_channels[] = {
190 KXSD9_ACCEL_CHAN(X), KXSD9_ACCEL_CHAN(Y), KXSD9_ACCEL_CHAN(Z),
191 {
192 .type = IIO_VOLTAGE,
193 .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT,
194 .indexed = 1,
195 .address = KXSD9_REG_AUX,
196 }
197};
198
199static const struct attribute_group kxsd9_attribute_group = {
200 .attrs = kxsd9_attributes,
201};
202
203static int kxsd9_power_up(struct kxsd9_state *st)
204{
205 int ret;
206
207 st->tx[0] = 0x0d;
208 st->tx[1] = 0x40;
209 ret = spi_write(st->us, st->tx, 2);
210 if (ret)
211 return ret;
212
213 st->tx[0] = 0x0c;
214 st->tx[1] = 0x9b;
215 return spi_write(st->us, st->tx, 2);
216};
217
218static const struct iio_info kxsd9_info = {
219 .read_raw = &kxsd9_read_raw,
220 .write_raw = &kxsd9_write_raw,
221 .attrs = &kxsd9_attribute_group,
222 .driver_module = THIS_MODULE,
223};
224
225static int kxsd9_probe(struct spi_device *spi)
226{
227 struct iio_dev *indio_dev;
228 struct kxsd9_state *st;
229 int ret;
230
231 indio_dev = iio_device_alloc(sizeof(*st));
232 if (indio_dev == NULL) {
233 ret = -ENOMEM;
234 goto error_ret;
235 }
236 st = iio_priv(indio_dev);
237 spi_set_drvdata(spi, indio_dev);
238
239 st->us = spi;
240 mutex_init(&st->buf_lock);
241 indio_dev->channels = kxsd9_channels;
242 indio_dev->num_channels = ARRAY_SIZE(kxsd9_channels);
243 indio_dev->name = spi_get_device_id(spi)->name;
244 indio_dev->dev.parent = &spi->dev;
245 indio_dev->info = &kxsd9_info;
246 indio_dev->modes = INDIO_DIRECT_MODE;
247
248 spi->mode = SPI_MODE_0;
249 spi_setup(spi);
250 kxsd9_power_up(st);
251
252 ret = iio_device_register(indio_dev);
253 if (ret)
254 goto error_free_dev;
255
256 return 0;
257
258error_free_dev:
259 iio_device_free(indio_dev);
260error_ret:
261 return ret;
262}
263
264static int kxsd9_remove(struct spi_device *spi)
265{
266 iio_device_unregister(spi_get_drvdata(spi));
267 iio_device_free(spi_get_drvdata(spi));
268
269 return 0;
270}
271
272static const struct spi_device_id kxsd9_id[] = {
273 {"kxsd9", 0},
274 { },
275};
276MODULE_DEVICE_TABLE(spi, kxsd9_id);
277
278static struct spi_driver kxsd9_driver = {
279 .driver = {
280 .name = "kxsd9",
281 .owner = THIS_MODULE,
282 },
283 .probe = kxsd9_probe,
284 .remove = kxsd9_remove,
285 .id_table = kxsd9_id,
286};
287module_spi_driver(kxsd9_driver);
288
289MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
290MODULE_DESCRIPTION("Kionix KXSD9 SPI driver");
291MODULE_LICENSE("GPL v2");