aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/iio/accel/kxsd9.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-02-21 15:11:44 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2013-02-21 15:11:44 -0500
commitb5c78e04dd061b776978dad61dd85357081147b0 (patch)
tree2416b2dc61c452c3aeb2a32bcedf15e6257be638 /drivers/iio/accel/kxsd9.c
parent06991c28f37ad68e5c03777f5c3b679b56e3dac1 (diff)
parent951348b377385475aa256c27e1c9e2564c9ec160 (diff)
Merge tag 'staging-3.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
Pull staging tree update from Greg Kroah-Hartman: "Here's the big staging tree merge for 3.9-rc1 Lots of cleanups and updates for drivers all through the staging tree. We are pretty much "code neutral" here, adding just about as many lines as we removed. All of these have been in linux-next for a while." * tag 'staging-3.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (804 commits) staging: comedi: vmk80xx: wait for URBs to complete staging: comedi: drivers: addi-data: hwdrv_apci3200.c: Add a missing semicolon staging: et131x: Update TODO list staging: et131x: Remove assignment of skb->dev staging: wlan-ng: hfa384x.h: fix for error reported by smatch staging/zache checkpatch ERROR: spaces prohibited around that staging/ozwpan: Mark read only parameters and structs as const staging/ozwpan: Remove empty and unused function oz_cdev_heartbeat staging/ozwpan: Mark local functions as static (fix sparse warnings) staging/ozwpan: Add missing header includes staging/usbip: Mark local functions as static (fix sparse warnings) staging/xgifb: Remove duplicated code in loops. staging/xgifb: Consolidate return paths staging/xgifb: Remove code without effect staging/xgifb: Remove unnecessary casts staging/xgifb: Consolidate if/else if with identical code branches staging: vt6656: replaced custom TRUE definition with true staging: vt6656: replaced custom FALSE definition with false staging: vt6656: replace custom BOOL definition with bool staging/rtl8187se: Mark functions as static to silence sparse ...
Diffstat (limited to 'drivers/iio/accel/kxsd9.c')
-rw-r--r--drivers/iio/accel/kxsd9.c287
1 files changed, 287 insertions, 0 deletions
diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c
new file mode 100644
index 000000000000..c2229a521ab9
--- /dev/null
+++ b/drivers/iio/accel/kxsd9.c
@@ -0,0 +1,287 @@
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 int ret;
98 struct kxsd9_state *st = iio_priv(indio_dev);
99 struct spi_transfer xfers[] = {
100 {
101 .bits_per_word = 8,
102 .len = 1,
103 .delay_usecs = 200,
104 .tx_buf = st->tx,
105 }, {
106 .bits_per_word = 8,
107 .len = 2,
108 .rx_buf = st->rx,
109 },
110 };
111
112 mutex_lock(&st->buf_lock);
113 st->tx[0] = KXSD9_READ(address);
114 ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
115 if (ret)
116 return ret;
117 return (((u16)(st->rx[0])) << 8) | (st->rx[1] & 0xF0);
118}
119
120static IIO_CONST_ATTR(accel_scale_available,
121 KXSD9_SCALE_2G " "
122 KXSD9_SCALE_4G " "
123 KXSD9_SCALE_6G " "
124 KXSD9_SCALE_8G);
125
126static struct attribute *kxsd9_attributes[] = {
127 &iio_const_attr_accel_scale_available.dev_attr.attr,
128 NULL,
129};
130
131static int kxsd9_write_raw(struct iio_dev *indio_dev,
132 struct iio_chan_spec const *chan,
133 int val,
134 int val2,
135 long mask)
136{
137 int ret = -EINVAL;
138
139 if (mask == IIO_CHAN_INFO_SCALE) {
140 /* Check no integer component */
141 if (val)
142 return -EINVAL;
143 ret = kxsd9_write_scale(indio_dev, val2);
144 }
145
146 return ret;
147}
148
149static int kxsd9_read_raw(struct iio_dev *indio_dev,
150 struct iio_chan_spec const *chan,
151 int *val, int *val2, long mask)
152{
153 int ret = -EINVAL;
154 struct kxsd9_state *st = iio_priv(indio_dev);
155
156 switch (mask) {
157 case IIO_CHAN_INFO_RAW:
158 ret = kxsd9_read(indio_dev, chan->address);
159 if (ret < 0)
160 goto error_ret;
161 *val = ret;
162 break;
163 case IIO_CHAN_INFO_SCALE:
164 ret = spi_w8r8(st->us, KXSD9_READ(KXSD9_REG_CTRL_C));
165 if (ret)
166 goto error_ret;
167 *val2 = kxsd9_micro_scales[ret & KXSD9_FS_MASK];
168 ret = IIO_VAL_INT_PLUS_MICRO;
169 break;
170 }
171
172error_ret:
173 return ret;
174};
175#define KXSD9_ACCEL_CHAN(axis) \
176 { \
177 .type = IIO_ACCEL, \
178 .modified = 1, \
179 .channel2 = IIO_MOD_##axis, \
180 .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
181 IIO_CHAN_INFO_SCALE_SHARED_BIT, \
182 .address = KXSD9_REG_##axis, \
183 }
184
185static const struct iio_chan_spec kxsd9_channels[] = {
186 KXSD9_ACCEL_CHAN(X), KXSD9_ACCEL_CHAN(Y), KXSD9_ACCEL_CHAN(Z),
187 {
188 .type = IIO_VOLTAGE,
189 .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT,
190 .indexed = 1,
191 .address = KXSD9_REG_AUX,
192 }
193};
194
195static const struct attribute_group kxsd9_attribute_group = {
196 .attrs = kxsd9_attributes,
197};
198
199static int kxsd9_power_up(struct kxsd9_state *st)
200{
201 int ret;
202
203 st->tx[0] = 0x0d;
204 st->tx[1] = 0x40;
205 ret = spi_write(st->us, st->tx, 2);
206 if (ret)
207 return ret;
208
209 st->tx[0] = 0x0c;
210 st->tx[1] = 0x9b;
211 return spi_write(st->us, st->tx, 2);
212};
213
214static const struct iio_info kxsd9_info = {
215 .read_raw = &kxsd9_read_raw,
216 .write_raw = &kxsd9_write_raw,
217 .attrs = &kxsd9_attribute_group,
218 .driver_module = THIS_MODULE,
219};
220
221static int kxsd9_probe(struct spi_device *spi)
222{
223 struct iio_dev *indio_dev;
224 struct kxsd9_state *st;
225 int ret;
226
227 indio_dev = iio_device_alloc(sizeof(*st));
228 if (indio_dev == NULL) {
229 ret = -ENOMEM;
230 goto error_ret;
231 }
232 st = iio_priv(indio_dev);
233 spi_set_drvdata(spi, indio_dev);
234
235 st->us = spi;
236 mutex_init(&st->buf_lock);
237 indio_dev->channels = kxsd9_channels;
238 indio_dev->num_channels = ARRAY_SIZE(kxsd9_channels);
239 indio_dev->name = spi_get_device_id(spi)->name;
240 indio_dev->dev.parent = &spi->dev;
241 indio_dev->info = &kxsd9_info;
242 indio_dev->modes = INDIO_DIRECT_MODE;
243
244 spi->mode = SPI_MODE_0;
245 spi_setup(spi);
246 kxsd9_power_up(st);
247
248 ret = iio_device_register(indio_dev);
249 if (ret)
250 goto error_free_dev;
251
252 return 0;
253
254error_free_dev:
255 iio_device_free(indio_dev);
256error_ret:
257 return ret;
258}
259
260static int kxsd9_remove(struct spi_device *spi)
261{
262 iio_device_unregister(spi_get_drvdata(spi));
263 iio_device_free(spi_get_drvdata(spi));
264
265 return 0;
266}
267
268static const struct spi_device_id kxsd9_id[] = {
269 {"kxsd9", 0},
270 { },
271};
272MODULE_DEVICE_TABLE(spi, kxsd9_id);
273
274static struct spi_driver kxsd9_driver = {
275 .driver = {
276 .name = "kxsd9",
277 .owner = THIS_MODULE,
278 },
279 .probe = kxsd9_probe,
280 .remove = kxsd9_remove,
281 .id_table = kxsd9_id,
282};
283module_spi_driver(kxsd9_driver);
284
285MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
286MODULE_DESCRIPTION("Kionix KXSD9 SPI driver");
287MODULE_LICENSE("GPL v2");