PageRenderTime 58ms CodeModel.GetById 40ms app.highlight 15ms RepoModel.GetById 1ms app.codeStats 0ms

/drivers/staging/iio/accel/kxsd9.c

https://bitbucket.org/cyanogenmod/android_kernel_asus_tf300t
C | 354 lines | 286 code | 43 blank | 25 comment | 14 complexity | 957fa180317575180c73b4609f94047b MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-1.0, GPL-2.0
  1/*
  2 * kxsd9.c	simple support for the Kionix KXSD9 3D
  3 *		accelerometer.
  4 *
  5 * Copyright (c) 2008-2009 Jonathan Cameron <jic23@cam.ac.uk>
  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
 25#include "../iio.h"
 26#include "../sysfs.h"
 27#include "../adc/adc.h"
 28#include "accel.h"
 29
 30#define KXSD9_REG_X		0x00
 31#define KXSD9_REG_Y		0x02
 32#define KXSD9_REG_Z		0x04
 33#define KXSD9_REG_AUX		0x06
 34#define KXSD9_REG_RESET		0x0a
 35#define KXSD9_REG_CTRL_C	0x0c
 36
 37#define KXSD9_FS_8		0x00
 38#define KXSD9_FS_6		0x01
 39#define KXSD9_FS_4		0x02
 40#define KXSD9_FS_2		0x03
 41#define KXSD9_FS_MASK		0x03
 42
 43#define KXSD9_REG_CTRL_B	0x0d
 44#define KXSD9_REG_CTRL_A	0x0e
 45
 46#define KXSD9_READ(a) (0x80 | (a))
 47#define KXSD9_WRITE(a) (a)
 48
 49#define KXSD9_SCALE_2G "0.011978"
 50#define KXSD9_SCALE_4G "0.023927"
 51#define KXSD9_SCALE_6G "0.035934"
 52#define KXSD9_SCALE_8G "0.047853"
 53
 54#define KXSD9_STATE_RX_SIZE 2
 55#define KXSD9_STATE_TX_SIZE 4
 56/**
 57 * struct kxsd9_state - device related storage
 58 * @buf_lock:	protect the rx and tx buffers.
 59 * @us:		spi device
 60 * @rx:		single rx buffer storage
 61 * @tx:		single tx buffer storage
 62 **/
 63struct kxsd9_state {
 64	struct mutex buf_lock;
 65	struct spi_device *us;
 66	u8 rx[KXSD9_STATE_RX_SIZE] ____cacheline_aligned;
 67	u8 tx[KXSD9_STATE_TX_SIZE];
 68};
 69
 70/* This may want to move to mili g to allow for non integer ranges */
 71static ssize_t kxsd9_read_scale(struct device *dev,
 72				struct device_attribute *attr,
 73				char *buf)
 74{
 75	int ret;
 76	ssize_t len = 0;
 77	struct iio_dev *indio_dev = dev_get_drvdata(dev);
 78	struct kxsd9_state *st = iio_priv(indio_dev);
 79	struct spi_transfer xfer = {
 80		.bits_per_word = 8,
 81		.len = 2,
 82		.cs_change = 1,
 83		.tx_buf = st->tx,
 84		.rx_buf = st->rx,
 85	};
 86	struct spi_message msg;
 87
 88	mutex_lock(&st->buf_lock);
 89	st->tx[0] = KXSD9_READ(KXSD9_REG_CTRL_C);
 90	st->tx[1] = 0;
 91	spi_message_init(&msg);
 92	spi_message_add_tail(&xfer, &msg);
 93	ret = spi_sync(st->us, &msg);
 94	if (ret)
 95		goto error_ret;
 96
 97	switch (st->rx[1] & KXSD9_FS_MASK) {
 98	case KXSD9_FS_8:
 99		len += sprintf(buf, "%s\n", KXSD9_SCALE_8G);
100		break;
101	case KXSD9_FS_6:
102		len += sprintf(buf, "%s\n", KXSD9_SCALE_6G);
103		break;
104	case KXSD9_FS_4:
105		len += sprintf(buf, "%s\n", KXSD9_SCALE_4G);
106		break;
107	case KXSD9_FS_2:
108		len += sprintf(buf, "%s\n", KXSD9_SCALE_2G);
109		break;
110	}
111
112error_ret:
113	mutex_unlock(&st->buf_lock);
114
115	return ret ? ret : len;
116}
117static ssize_t kxsd9_write_scale(struct device *dev,
118				 struct device_attribute *attr,
119				 const char *buf,
120				 size_t len)
121{
122
123	struct spi_message msg;
124	int ret;
125	struct iio_dev *indio_dev = dev_get_drvdata(dev);
126	struct kxsd9_state *st = iio_priv(indio_dev);
127	u8 val;
128	struct spi_transfer xfers[] = {
129		{
130			.bits_per_word = 8,
131			.len = 2,
132			.cs_change = 1,
133			.tx_buf = st->tx,
134			.rx_buf = st->rx,
135		}, {
136			.bits_per_word = 8,
137			.len = 2,
138			.cs_change = 1,
139			.tx_buf = st->tx,
140		},
141	};
142
143	if (!strncmp(buf, KXSD9_SCALE_8G,
144		     strlen(buf) < strlen(KXSD9_SCALE_8G)
145		     ? strlen(buf) : strlen(KXSD9_SCALE_8G)))
146		val = KXSD9_FS_8;
147	else if (!strncmp(buf, KXSD9_SCALE_6G,
148			  strlen(buf) < strlen(KXSD9_SCALE_6G)
149			  ? strlen(buf) : strlen(KXSD9_SCALE_6G)))
150		val = KXSD9_FS_6;
151	else if (!strncmp(buf, KXSD9_SCALE_4G,
152			  strlen(buf) < strlen(KXSD9_SCALE_4G)
153			  ? strlen(buf) : strlen(KXSD9_SCALE_4G)))
154		val = KXSD9_FS_4;
155	else if (!strncmp(buf, KXSD9_SCALE_2G,
156			  strlen(buf) < strlen(KXSD9_SCALE_2G)
157			  ? strlen(buf) : strlen(KXSD9_SCALE_2G)))
158		val = KXSD9_FS_2;
159	else
160		return -EINVAL;
161
162	mutex_lock(&st->buf_lock);
163	st->tx[0] = KXSD9_READ(KXSD9_REG_CTRL_C);
164	st->tx[1] = 0;
165	spi_message_init(&msg);
166	spi_message_add_tail(&xfers[0], &msg);
167	ret = spi_sync(st->us, &msg);
168	if (ret)
169		goto error_ret;
170	st->tx[0] = KXSD9_WRITE(KXSD9_REG_CTRL_C);
171	st->tx[1] = (st->rx[1] & ~KXSD9_FS_MASK) | val;
172
173	spi_message_init(&msg);
174	spi_message_add_tail(&xfers[1], &msg);
175	ret = spi_sync(st->us, &msg);
176error_ret:
177	mutex_unlock(&st->buf_lock);
178	return ret ? ret : len;
179}
180
181static ssize_t kxsd9_read_accel(struct device *dev,
182				struct device_attribute *attr,
183				char *buf)
184{
185	struct spi_message msg;
186	int ret;
187	ssize_t len = 0;
188	u16 val;
189	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
190	struct iio_dev *indio_dev = dev_get_drvdata(dev);
191	struct kxsd9_state *st = iio_priv(indio_dev);
192	struct spi_transfer xfers[] = {
193		{
194			.bits_per_word = 8,
195			.len = 1,
196			.cs_change = 0,
197			.delay_usecs = 200,
198			.tx_buf = st->tx,
199		}, {
200			.bits_per_word = 8,
201			.len = 2,
202			.cs_change = 1,
203			.rx_buf = st->rx,
204		},
205	};
206
207	mutex_lock(&st->buf_lock);
208	st->tx[0] = KXSD9_READ(this_attr->address);
209	spi_message_init(&msg);
210	spi_message_add_tail(&xfers[0], &msg);
211	spi_message_add_tail(&xfers[1], &msg);
212	ret = spi_sync(st->us, &msg);
213	if (ret)
214		goto error_ret;
215	val = (((u16)(st->rx[0])) << 8) | (st->rx[1] & 0xF0);
216	len = sprintf(buf, "%d\n", val);
217error_ret:
218	mutex_unlock(&st->buf_lock);
219
220	return ret ? ret : len;
221}
222
223static IIO_DEV_ATTR_ACCEL_X(kxsd9_read_accel, KXSD9_REG_X);
224static IIO_DEV_ATTR_ACCEL_Y(kxsd9_read_accel, KXSD9_REG_Y);
225static IIO_DEV_ATTR_ACCEL_Z(kxsd9_read_accel, KXSD9_REG_Z);
226static IIO_DEV_ATTR_IN_RAW(0, kxsd9_read_accel, KXSD9_REG_AUX);
227
228static IIO_DEVICE_ATTR(accel_scale,
229		S_IRUGO | S_IWUSR,
230		kxsd9_read_scale,
231		kxsd9_write_scale,
232		0);
233
234static IIO_CONST_ATTR(accel_scale_available,
235		KXSD9_SCALE_2G " "
236		KXSD9_SCALE_4G " "
237		KXSD9_SCALE_6G " "
238		KXSD9_SCALE_8G);
239
240static struct attribute *kxsd9_attributes[] = {
241	&iio_dev_attr_accel_x_raw.dev_attr.attr,
242	&iio_dev_attr_accel_y_raw.dev_attr.attr,
243	&iio_dev_attr_accel_z_raw.dev_attr.attr,
244	&iio_dev_attr_in0_raw.dev_attr.attr,
245	&iio_dev_attr_accel_scale.dev_attr.attr,
246	&iio_const_attr_accel_scale_available.dev_attr.attr,
247	NULL,
248};
249
250static const struct attribute_group kxsd9_attribute_group = {
251	.attrs = kxsd9_attributes,
252};
253
254static int __devinit kxsd9_power_up(struct kxsd9_state *st)
255{
256	struct spi_transfer xfers[2] = {
257		{
258			.bits_per_word = 8,
259			.len = 2,
260			.cs_change = 1,
261			.tx_buf = st->tx,
262		}, {
263			.bits_per_word = 8,
264			.len = 2,
265			.cs_change = 1,
266			.tx_buf = st->tx + 2,
267		},
268	};
269	struct spi_message msg;
270	st->tx[0] = 0x0d;
271	st->tx[1] = 0x40;
272	st->tx[2] = 0x0c;
273	st->tx[3] = 0x9b;
274
275	spi_message_init(&msg);
276	spi_message_add_tail(&xfers[0], &msg);
277	spi_message_add_tail(&xfers[1], &msg);
278
279	return spi_sync(st->us, &msg);
280};
281
282static const struct iio_info kxsd9_info = {
283	.attrs = &kxsd9_attribute_group,
284	.driver_module = THIS_MODULE,
285};
286
287static int __devinit kxsd9_probe(struct spi_device *spi)
288{
289	struct iio_dev *indio_dev;
290	struct kxsd9_state *st;
291	int ret = 0;
292
293	indio_dev = iio_allocate_device(sizeof(*st));
294	if (indio_dev == NULL) {
295		ret = -ENOMEM;
296		goto error_ret;
297	}
298	st = iio_priv(indio_dev);
299	spi_set_drvdata(spi, indio_dev);
300
301	st->us = spi;
302	mutex_init(&st->buf_lock);
303
304	indio_dev->dev.parent = &spi->dev;
305	indio_dev->info = &kxsd9_info;
306	indio_dev->modes = INDIO_DIRECT_MODE;
307
308	ret = iio_device_register(indio_dev);
309	if (ret)
310		goto error_free_dev;
311
312	spi->mode = SPI_MODE_0;
313	spi_setup(spi);
314	kxsd9_power_up(st);
315
316	return 0;
317
318error_free_dev:
319	iio_free_device(indio_dev);
320error_ret:
321	return ret;
322}
323
324static int __devexit kxsd9_remove(struct spi_device *spi)
325{
326	iio_device_unregister(spi_get_drvdata(spi));
327
328	return 0;
329}
330
331static struct spi_driver kxsd9_driver = {
332	.driver = {
333		.name = "kxsd9",
334		.owner = THIS_MODULE,
335	},
336	.probe = kxsd9_probe,
337	.remove = __devexit_p(kxsd9_remove),
338};
339
340static __init int kxsd9_spi_init(void)
341{
342	return spi_register_driver(&kxsd9_driver);
343}
344module_init(kxsd9_spi_init);
345
346static __exit void kxsd9_spi_exit(void)
347{
348	spi_unregister_driver(&kxsd9_driver);
349}
350module_exit(kxsd9_spi_exit);
351
352MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
353MODULE_DESCRIPTION("Kionix KXSD9 SPI driver");
354MODULE_LICENSE("GPL v2");