[PATCH 4/4] misc: Add SAR100 angular rate driver.

Stefan Schmidt stefan at datenfreihafen.org
Mon Apr 26 09:25:06 CEST 2010


Signed-off-by: Stefan Schmidt <stefan at datenfreihafen.org>
---
 drivers/misc/Kconfig  |    7 +
 drivers/misc/Makefile |    1 +
 drivers/misc/sar100.c |  296 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 304 insertions(+), 0 deletions(-)
 create mode 100644 drivers/misc/sar100.c

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index aed9c8c..235ec60 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -505,4 +505,11 @@ config MLX90609
 	  This option enabled the driver for the MLX90609 Angular Rate Sensor
 	  (gyroscope).
 
+config SAR100
+	tristate "SAR100 Angular Rate Sensor (gyroscope)"
+	depends on SPI
+	help
+	  This option enabled the driver for the SAR100 Angular Rate Sensor
+	  (gyroscope).
+
 endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index d211639..56106af 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -37,4 +37,5 @@ obj-$(CONFIG_MACH_NEO1973)      += neo1973_version.o \
                                    neo1973_pm_host.o \
                                    neo1973_pm_resume_reason.o
 obj-$(CONFIG_MLX90609)		+= mlx90609.o
+obj-$(CONFIG_SAR100)		+= sar100.o
 
diff --git a/drivers/misc/sar100.c b/drivers/misc/sar100.c
new file mode 100644
index 0000000..ba5a531
--- /dev/null
+++ b/drivers/misc/sar100.c
@@ -0,0 +1,296 @@
+/* Linux kernel driver for the Sensonor SAR100 Angular Rate Sensor
+ *
+ * Copyright (C) 2010 DLR
+ * Author: Stefan Schmidt <stefan at datenfreihafen.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+
+/* Read commands */
+#define RARH	0x80	/* Read angular rate, high byte */
+#define RARLX	0x8E	/* Read angular rate, low byte extended */
+#define RTMP	0xB0	/* Read internal temperature */
+#define RSR	0xB4	/* Read status register */
+
+/* SafeGuard commands, address goes before command */
+#define SGDIS1	0xD74E	/* SafeGuard disable command 1 */
+#define SGDIS2	0x5063	/* SafeGuard disable command 2 */
+#define SGDIS3	0xA812	/* SafeGuard disable command 3 */
+#define SGEN	0x55	/* SafeGuard enable command */
+
+/* Replies */
+#define SGR1	0x80
+#define SGR2	0x00
+#define ERR_MASK	0xF0
+#define RARLX_MASK	0x0F
+#define ERROR	0x80
+
+/* Error handling commands */
+#define PRCEN	0xAA	/* Re-enable signal processing after error condition */
+
+struct sar100_info {
+	struct spi_device *spi_dev;
+	struct device *dev;
+	u8 tx_buf[2];
+	u8 rx_buf[1];
+	struct mutex lock;
+};
+
+static int sar100_spi(struct sar100_info *sar, u8 cmd, u8 *data)
+{
+	struct spi_message message;
+        struct spi_transfer xfer;
+	int status;
+
+	 /* Build our spi message */
+	spi_message_init(&message);
+	memset(&xfer, 0, sizeof(xfer));
+	xfer.len = 1;
+        xfer.tx_buf = sar->tx_buf;
+        xfer.rx_buf = sar->rx_buf;
+
+        /* Put our command in the output buffer */
+	sar->tx_buf[0] = cmd;
+
+        spi_message_add_tail(&xfer, &message);
+
+	mutex_lock(&sar->lock);
+        status = spi_sync(sar->spi_dev, &message);
+	mutex_unlock(&sar->lock);
+
+        if (status == 0)
+                *data = sar->rx_buf[0];
+	return status;
+}
+
+static int sar100_spi16(struct sar100_info *sar, u16 cmd)
+{
+	struct spi_message message;
+        struct spi_transfer xfer;
+	int status;
+
+	 /* Build our spi message */
+	spi_message_init(&message);
+	memset(&xfer, 0, sizeof(xfer));
+	xfer.len = 2;
+        xfer.tx_buf = sar->tx_buf;
+        xfer.rx_buf = sar->rx_buf;
+
+        /* Put our command in the output buffer */
+	sar->tx_buf[0] = cmd >> 8;
+	sar->tx_buf[1] = cmd;
+
+        spi_message_add_tail(&xfer, &message);
+
+	mutex_lock(&sar->lock);
+        status = spi_sync(sar->spi_dev, &message);
+	mutex_unlock(&sar->lock);
+
+	return status;
+}
+
+static int read_status(struct sar100_info *sar)
+{
+	u8 rsr;
+
+	sar100_spi(sar, RSR, &rsr);
+	sar100_spi(sar, 0x0F, &rsr); /* Dummy command */
+
+	return rsr;
+}
+
+static int handle_safeguard(struct sar100_info *sar)
+{
+	u8 status, ret;
+
+	/* Test for ADC_OK or EXC_OK problems */
+	status = read_status(sar);
+	if (!(status >> 7)) {
+		dev_info(&sar->spi_dev->dev, "ADC overflow, recovering\n");
+		goto recover;
+	}
+	status &= 0x03;
+	status >>= 1;
+	if (!status) {
+		dev_info(&sar->spi_dev->dev, "Control loop failed, recovering\n");
+		goto recover;
+	}
+	else {
+		/* Not recoverable error found: power ccle needed */
+		dev_info(&sar->spi_dev->dev, "Unrecoverable error condition. Power clycle needed.\n");
+		return -EIO;
+	}
+
+recover:
+	/* Execute safegurad sequence */
+	msleep(100);
+	sar100_spi16(sar, SGDIS1);
+	sar100_spi16(sar, SGDIS2);
+	sar100_spi16(sar, SGDIS3);
+	sar100_spi(sar, PRCEN, &ret);
+	sar100_spi(sar, SGEN, &ret);
+	return 0;
+}
+
+static int read_rate(struct sar100_info *sar)
+{
+	int ret;
+	u8 rarh, rarlx;
+
+	sar100_spi(sar, RARH, &rarh);
+	sar100_spi(sar, 0x0F, &rarh); /* Dummy command */
+	sar100_spi(sar, RARLX, &rarlx);
+	sar100_spi(sar, 0x0F, &rarlx); /* Dummy command */
+
+	/* If any monitored error is found the answer will be 0x80 */
+	if (rarh == ERROR && rarlx == ERROR) {
+		handle_safeguard(sar);
+		dev_info(&sar->spi_dev->dev, "Error detected\n");
+		return -EIO;
+	}
+
+	rarlx &= RARLX_MASK;
+	ret = ((s8)rarh) << 8 | ((s8)rarlx) << 4;
+
+	return ret >> 4;
+}
+
+static int read_temp(struct sar100_info *sar)
+{
+	u8 rtmp;
+
+	sar100_spi(sar, RTMP, &rtmp);
+	sar100_spi(sar, 0x0F, &rtmp); /* Dummy command */
+
+	return rtmp;
+}
+
+/* Sysfs interface for temperature and angular rate */
+static ssize_t show_temperature(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct sar100_info *sar = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%i\n", read_temp(sar));
+}
+
+static DEVICE_ATTR(temperature, S_IRUGO, show_temperature, NULL);
+
+static ssize_t show_angular_rate(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	struct sar100_info *sar = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%i\n", read_rate(sar));
+}
+
+static DEVICE_ATTR(angular_rate, S_IRUGO, show_angular_rate, NULL);
+
+static struct attribute *sar100_sysfs_entries[] = {
+	&dev_attr_angular_rate.attr,
+	&dev_attr_temperature.attr,
+	NULL
+};
+
+static struct attribute_group sar100_attr_group = {
+	.name	= NULL,
+	.attrs	= sar100_sysfs_entries,
+};
+
+/* Device driver infrastructure */
+static int __devinit sar100_probe(struct spi_device *spi)
+{
+	int rc, status;
+	struct sar100_info *sar;
+
+	sar = kzalloc(sizeof(*sar), GFP_KERNEL);
+	if (!sar)
+		return -ENOMEM;
+
+	sar->spi_dev = spi;
+	dev_set_drvdata(&spi->dev, sar);
+	mutex_init(&sar->lock);
+
+	rc = sysfs_create_group(&spi->dev.kobj, &sar100_attr_group);
+	if (rc) {
+		dev_err(&spi->dev, "Error creating sysfs group\n");
+		goto err_out;
+	}
+
+	msleep(200); /* Start Up time ?? */
+
+	dev_info(&spi->dev, "SAR100 driver loaded\n");
+	status = read_status(sar);
+	if (status == 0xFF)
+		dev_info(&spi->dev, "Power and status is ok\n");
+	else {
+		dev_info(&spi->dev, "Power or another problem\n");
+		goto err_out;
+	}
+
+	return 0;
+
+err_out:
+	dev_set_drvdata(&spi->dev, NULL);
+	kfree(sar);
+	return rc;
+}
+
+static int __devexit sar100_remove(struct spi_device *spi)
+{
+	struct sar100_info *sar = dev_get_drvdata(&spi->dev);
+
+	sysfs_remove_group(&spi->dev.kobj, &sar100_attr_group);
+	dev_set_drvdata(&spi->dev, NULL);
+	kfree(sar);
+
+	return 0;
+}
+
+static struct spi_driver sar100_driver = {
+	.driver = {
+		.name	= "sar100",
+		.owner	= THIS_MODULE,
+	},
+	.probe	 = sar100_probe,
+	.remove	 = __devexit_p(sar100_remove),
+};
+
+static int __init sar100_init(void)
+{
+	return spi_register_driver(&sar100_driver);
+}
+
+static void __exit sar100_exit(void)
+{
+	spi_unregister_driver(&sar100_driver);
+}
+
+MODULE_DESCRIPTION("SPI driver for the SAR100 angular rate sensor");
+MODULE_AUTHOR("Stefan Schmidt <stefan at datenfreihafen.org>");
+MODULE_LICENSE("GPL");
+
+module_init(sar100_init);
+module_exit(sar100_exit);
-- 
1.7.0.5




More information about the openmoko-kernel mailing list