[PATCH 546/641] Staging: IIO: kxsd9 accelerometer minimal support

Greg Kroah-Hartman gregkh at suse.de
Tue Sep 15 19:12:57 UTC 2009


From: Jonathan Cameron <jic23 at cam.ac.uk>

This provides only very minimal support for this device.
Note that an alternate driver has been posted to the input
mailing list.

When the original LMKL discussion that led to the descision
to develop IIO occured, the question on whether the differing
requirements of IIO and input drivers made it a good idea
to have unified drivers was left as an open question.
It still is.  All opinions on this question welcome.

Signed-off-by: Jonathan Cameron <jic23 at cam.ac.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh at suse.de>
---
 drivers/staging/iio/accel/Kconfig  |    7 +
 drivers/staging/iio/accel/Makefile |    2 +
 drivers/staging/iio/accel/kxsd9.c  |  395 ++++++++++++++++++++++++++++++++++++
 drivers/staging/iio/adc/adc.h      |   13 ++
 4 files changed, 417 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/iio/accel/kxsd9.c
 create mode 100644 drivers/staging/iio/adc/adc.h

diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig
index d94da21..afe619d 100644
--- a/drivers/staging/iio/accel/Kconfig
+++ b/drivers/staging/iio/accel/Kconfig
@@ -3,6 +3,13 @@
 #
 comment "Accelerometers"
 
+config KXSD9
+	tristate "Kionix KXSD9 Accelerometer Driver"
+	depends on SPI
+	help
+	  Say yes here to build support for the Kionix KXSD9 accelerometer.
+	  Currently this only supports the device via an SPI interface.
+
 config LIS3L02DQ
 	tristate "ST Microelectronics LIS3L02DQ Accelerometer Driver"
 	depends on SPI
diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile
index 5a7ef9d..cbec688 100644
--- a/drivers/staging/iio/accel/Makefile
+++ b/drivers/staging/iio/accel/Makefile
@@ -1,5 +1,7 @@
 #
 # Makefile for industrial I/O accelerometer drivers
 #
+obj-$(CONFIG_KXSD9)	+= kxsd9.o
+
 lis3l02dq-y		:= lis3l02dq_core.o
 obj-$(CONFIG_LIS3L02DQ)	+= lis3l02dq.o
diff --git a/drivers/staging/iio/accel/kxsd9.c b/drivers/staging/iio/accel/kxsd9.c
new file mode 100644
index 0000000..33d16b6
--- /dev/null
+++ b/drivers/staging/iio/accel/kxsd9.c
@@ -0,0 +1,395 @@
+/*
+ * kxsd9.c	simple support for the Kionix KXSD9 3D
+ *		accelerometer.
+ *
+ * Copyright (c) 2008-2009 Jonathan Cameron <jic23 at cam.ac.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * The i2c interface is very similar, so shouldn't be a problem once
+ * I have a suitable wire made up.
+ *
+ * TODO:	Support the motion detector
+ *		Uses register address incrementing so could have a
+ *		heavily optimized ring buffer access function.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
+#include <linux/rtc.h>
+#include <linux/delay.h>
+
+#include "../iio.h"
+#include "../sysfs.h"
+#include "../adc/adc.h"
+#include "accel.h"
+
+#define KXSD9_REG_X		0x00
+#define KXSD9_REG_Y		0x02
+#define KXSD9_REG_Z		0x04
+#define KXSD9_REG_AUX		0x06
+#define KXSD9_REG_RESET		0x0a
+#define KXSD9_REG_CTRL_C	0x0c
+
+#define KXSD9_FS_8		0x00
+#define KXSD9_FS_6		0x01
+#define KXSD9_FS_4		0x02
+#define KXSD9_FS_2		0x03
+#define KXSD9_FS_MASK		0x03
+
+#define KXSD9_REG_CTRL_B	0x0d
+#define KXSD9_REG_CTRL_A	0x0e
+
+#define KXSD9_READ(a) (0x80 | (a))
+#define KXSD9_WRITE(a) (a)
+
+#define IIO_DEV_ATTR_ACCEL_SET_RANGE(_mode, _show, _store)	\
+	IIO_DEVICE_ATTR(accel_range, _mode, _show, _store, 0)
+
+#define KXSD9_STATE_RX_SIZE 2
+#define KXSD9_STATE_TX_SIZE 4
+/**
+ * struct kxsd9_state - device related storage
+ * @buf_lock:	protect the rx and tx buffers.
+ * @indio_dev:	associated industrial IO device
+ * @us:		spi device
+ * @rx:		single rx buffer storage
+ * @tx:		single tx buffer storage
+ **/
+struct kxsd9_state {
+	struct mutex buf_lock;
+	struct iio_dev *indio_dev;
+	struct spi_device *us;
+	u8 *rx;
+	u8 *tx;
+};
+
+/* This may want to move to mili g to allow for non integer ranges */
+static ssize_t kxsd9_read_accel_range(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	int ret;
+	ssize_t len = 0;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct kxsd9_state *st = indio_dev->dev_data;
+	struct spi_transfer xfer = {
+		.bits_per_word = 8,
+		.len = 2,
+		.cs_change = 1,
+		.tx_buf = st->tx,
+		.rx_buf = st->rx,
+	};
+	struct spi_message msg;
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = KXSD9_READ(KXSD9_REG_CTRL_C);
+	st->tx[1] = 0;
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret)
+		goto error_ret;
+
+	switch (st->rx[1] & KXSD9_FS_MASK) {
+	case KXSD9_FS_8:
+		len += sprintf(buf, "8\n");
+		break;
+	case KXSD9_FS_6:
+		len += sprintf(buf, "6\n");
+		break;
+	case KXSD9_FS_4:
+		len += sprintf(buf, "4\n");
+		break;
+	case KXSD9_FS_2:
+		len += sprintf(buf, "2\n");
+		break;
+	}
+
+error_ret:
+	mutex_unlock(&st->buf_lock);
+
+	return ret ? ret : len;
+}
+static ssize_t kxsd9_write_accel_range(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf,
+				      size_t len)
+{
+	long readin;
+	struct spi_message msg;
+	int ret;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct kxsd9_state *st = indio_dev->dev_data;
+	u8 val;
+	struct spi_transfer xfers[] = {
+		{
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.tx_buf = st->tx,
+			.rx_buf = st->rx,
+		}, {
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.tx_buf = st->tx,
+		},
+	};
+
+	ret = strict_strtol(buf, 10, &readin);
+	if (ret)
+		return ret;
+	switch (readin) {
+	case 8:
+		val = KXSD9_FS_8;
+		break;
+	case 6:
+		val = KXSD9_FS_6;
+		break;
+	case 4:
+		val = KXSD9_FS_4;
+		break;
+	case 2:
+		val = KXSD9_FS_2;
+		break;
+	default:
+		return -EINVAL;
+	}
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = KXSD9_READ(KXSD9_REG_CTRL_C);
+	st->tx[1] = 0;
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret)
+		goto error_ret;
+	st->tx[0] = KXSD9_WRITE(KXSD9_REG_CTRL_C);
+	st->tx[1] = (st->rx[1] & ~KXSD9_FS_MASK) | val;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	ret = spi_sync(st->us, &msg);
+error_ret:
+	mutex_unlock(&st->buf_lock);
+	return ret ? ret : len;
+}
+static ssize_t kxsd9_read_accel(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct spi_message msg;
+	int ret;
+	ssize_t len = 0;
+	u16 val;
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct kxsd9_state *st = indio_dev->dev_data;
+	struct spi_transfer xfers[] = {
+		{
+			.bits_per_word = 8,
+			.len = 1,
+			.cs_change = 0,
+			.delay_usecs = 200,
+			.tx_buf = st->tx,
+		}, {
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+			.rx_buf = st->rx,
+		},
+	};
+
+	mutex_lock(&st->buf_lock);
+	st->tx[0] = KXSD9_READ(this_attr->address);
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	ret = spi_sync(st->us, &msg);
+	if (ret)
+		goto error_ret;
+	val = (((u16)(st->rx[0])) << 8) | (st->rx[1] & 0xF0);
+	len = sprintf(buf, "%d\n", val);
+error_ret:
+	mutex_unlock(&st->buf_lock);
+
+	return ret ? ret : len;
+}
+
+static IIO_DEV_ATTR_ACCEL_X(kxsd9_read_accel, KXSD9_REG_X);
+static IIO_DEV_ATTR_ACCEL_Y(kxsd9_read_accel, KXSD9_REG_Y);
+static IIO_DEV_ATTR_ACCEL_Z(kxsd9_read_accel, KXSD9_REG_Z);
+static IIO_DEV_ATTR_ADC(0, kxsd9_read_accel, KXSD9_REG_AUX);
+static IIO_DEV_ATTR_ACCEL_SET_RANGE(S_IRUGO | S_IWUSR,
+				    kxsd9_read_accel_range,
+				    kxsd9_write_accel_range);
+
+static struct attribute *kxsd9_attributes[] = {
+	&iio_dev_attr_accel_x.dev_attr.attr,
+	&iio_dev_attr_accel_y.dev_attr.attr,
+	&iio_dev_attr_accel_z.dev_attr.attr,
+	&iio_dev_attr_adc_0.dev_attr.attr,
+	&iio_dev_attr_accel_range.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group kxsd9_attribute_group = {
+	.attrs = kxsd9_attributes,
+};
+
+static int __devinit kxsd9_power_up(struct spi_device *spi)
+{
+	int ret;
+	struct spi_transfer xfers[2] = {
+		{
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+		}, {
+			.bits_per_word = 8,
+			.len = 2,
+			.cs_change = 1,
+		},
+	};
+	struct spi_message msg;
+	u8 *tx2;
+	u8 *tx = kmalloc(2, GFP_KERNEL);
+
+	if (tx == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	tx2 = kmalloc(2, GFP_KERNEL);
+	if (tx2 == NULL) {
+		ret = -ENOMEM;
+		goto error_free_tx;
+	}
+	tx[0] = 0x0d;
+	tx[1] = 0x40;
+
+	tx2[0] = 0x0c;
+	tx2[1] = 0x9b;
+
+	xfers[0].tx_buf = tx;
+	xfers[1].tx_buf = tx2;
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfers[0], &msg);
+	spi_message_add_tail(&xfers[1], &msg);
+	ret = spi_sync(spi, &msg);
+
+	kfree(tx2);
+error_free_tx:
+	kfree(tx);
+error_ret:
+	return ret;
+
+};
+
+static int __devinit kxsd9_probe(struct spi_device *spi)
+{
+
+	struct kxsd9_state *st;
+	int ret = 0;
+
+	st = kzalloc(sizeof(*st), GFP_KERNEL);
+	if (st == NULL) {
+		ret = -ENOMEM;
+		goto error_ret;
+	}
+	spi_set_drvdata(spi, st);
+
+	st->rx = kmalloc(sizeof(*st->rx)*KXSD9_STATE_RX_SIZE,
+			 GFP_KERNEL);
+	if (st->rx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_st;
+	}
+	st->tx = kmalloc(sizeof(*st->tx)*KXSD9_STATE_TX_SIZE,
+			 GFP_KERNEL);
+	if (st->tx == NULL) {
+		ret = -ENOMEM;
+		goto error_free_rx;
+	}
+
+	st->us = spi;
+	mutex_init(&st->buf_lock);
+	st->indio_dev = iio_allocate_device();
+	if (st->indio_dev == NULL) {
+		ret = -ENOMEM;
+		goto error_free_tx;
+	}
+	st->indio_dev->dev.parent = &spi->dev;
+	/* for now */
+	st->indio_dev->num_interrupt_lines = 0;
+	st->indio_dev->event_attrs = NULL;
+
+	st->indio_dev->attrs = &kxsd9_attribute_group;
+	st->indio_dev->dev_data = (void *)(st);
+	st->indio_dev->driver_module = THIS_MODULE;
+	st->indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = iio_device_register(st->indio_dev);
+	if (ret)
+		goto error_free_dev;
+
+	spi->mode = SPI_MODE_0;
+	spi_setup(spi);
+	kxsd9_power_up(spi);
+
+	return 0;
+
+error_free_dev:
+	iio_free_device(st->indio_dev);
+error_free_tx:
+	kfree(st->tx);
+error_free_rx:
+	kfree(st->rx);
+error_free_st:
+	kfree(st);
+error_ret:
+	return ret;
+}
+
+static int __devexit kxsd9_remove(struct spi_device *spi)
+{
+	struct kxsd9_state *st = spi_get_drvdata(spi);
+
+	iio_device_unregister(st->indio_dev);
+	kfree(st->tx);
+	kfree(st->rx);
+	kfree(st);
+
+	return 0;
+}
+
+static struct spi_driver kxsd9_driver = {
+	.driver = {
+		.name = "kxsd9",
+		.owner = THIS_MODULE,
+	},
+	.probe = kxsd9_probe,
+	.remove = __devexit_p(kxsd9_remove),
+};
+
+static __init int kxsd9_spi_init(void)
+{
+	return spi_register_driver(&kxsd9_driver);
+}
+module_init(kxsd9_spi_init);
+
+static __exit void kxsd9_spi_exit(void)
+{
+	spi_unregister_driver(&kxsd9_driver);
+}
+module_exit(kxsd9_spi_exit);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23 at cam.ac.uk>");
+MODULE_DESCRIPTION("Kionix KXSD9 SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/adc/adc.h b/drivers/staging/iio/adc/adc.h
new file mode 100644
index 0000000..d925b2c
--- /dev/null
+++ b/drivers/staging/iio/adc/adc.h
@@ -0,0 +1,13 @@
+/*
+ * adc.h - sysfs attributes associated with ADCs
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * Copyright (c) 2008 Jonathan Cameron <jic23 at cam.ac.uk>
+ *
+ */
+
+#define IIO_DEV_ATTR_ADC(_num, _show, _addr)			\
+  IIO_DEVICE_ATTR(adc_##_num, S_IRUGO, _show, NULL, _addr)
-- 
1.6.4.2




More information about the devel mailing list