Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d79df0b1 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging-2.6: (577 commits)
  Staging: ramzswap: Handler for swap slot free callback
  swap: Add swap slot free callback to block_device_operations
  swap: Add flag to identify block swap devices
  Staging: vt6655: use ETH_FRAME_LEN macro instead of custom one
  Staging: vt6655: use ETH_DATA_LEN macro instead of custom one
  Staging: vt6655: use ETH_FCS_LEN macro instead of custom one
  Staging: vt6656: use ETH_HLEN macro instead of custom one
  Staging: comedi: quatech_daqp_cs.c Replace eos semaphore with a completion.
  Staging: dt3155v4l: remove private memory allocator
  Staging: crystalhd: Remove typedefs from driver
  Staging: winbond: Fix for pointer name format issue in mds.c
  Staging: vt6656: removed custom UCHAR/USHORT/UINT/ULONG/ULONGLONG typedefs
  Staging: vt6656: removed custom CHAR/SHORT/INT/LONG typedefs
  Staging: comedi: Altered the way printk is used in 8255.c
  staging: iio: adis16350 and similar IMU driver
  Staging: iio: max1363 Fix two bugs in single_channel_from_ring
  Staging: iio: adis16220 extract bin_attribute structures from state
  Staging: iio: adis16220 vibration sensor driver
  Staging: comedi: Kconfig dependancy fixes
  Staging: comedi: fix up build error from last Kconfig changes
  ...
parents 6e80e8ed c8d1a126
Loading
Loading
Loading
Loading
+9 −9
Original line number Diff line number Diff line
@@ -59,8 +59,6 @@ source "drivers/staging/wlan-ng/Kconfig"

source "drivers/staging/echo/Kconfig"

source "drivers/staging/poch/Kconfig"

source "drivers/staging/otus/Kconfig"

source "drivers/staging/rt2860/Kconfig"
@@ -113,6 +111,8 @@ source "drivers/staging/vme/Kconfig"

source "drivers/staging/rar_register/Kconfig"

source "drivers/staging/memrar/Kconfig"

source "drivers/staging/sep/Kconfig"

source "drivers/staging/iio/Kconfig"
@@ -127,19 +127,19 @@ source "drivers/staging/batman-adv/Kconfig"

source "drivers/staging/samsung-laptop/Kconfig"

source "drivers/staging/strip/Kconfig"
source "drivers/staging/sm7xx/Kconfig"

source "drivers/staging/arlan/Kconfig"
source "drivers/staging/dt3155/Kconfig"

source "drivers/staging/wavelan/Kconfig"
source "drivers/staging/dt3155v4l/Kconfig"

source "drivers/staging/netwave/Kconfig"
source "drivers/staging/crystalhd/Kconfig"

source "drivers/staging/sm7xx/Kconfig"
source "drivers/staging/cxt1e1/Kconfig"

source "drivers/staging/dt3155/Kconfig"
source "drivers/staging/ti-st/Kconfig"

source "drivers/staging/crystalhd/Kconfig"
source "drivers/staging/adis16255/Kconfig"

endif # !STAGING_EXCLUDE_BUILD
endif # STAGING
+5 −6
Original line number Diff line number Diff line
@@ -12,7 +12,6 @@ obj-$(CONFIG_USB_IP_COMMON) += usbip/
obj-$(CONFIG_W35UND)		+= winbond/
obj-$(CONFIG_PRISM2_USB)	+= wlan-ng/
obj-$(CONFIG_ECHO)		+= echo/
obj-$(CONFIG_POCH)		+= poch/
obj-$(CONFIG_OTUS)		+= otus/
obj-$(CONFIG_RT2860)		+= rt2860/
obj-$(CONFIG_RT2870)		+= rt2870/
@@ -37,6 +36,7 @@ obj-$(CONFIG_FB_UDL) += udlfb/
obj-$(CONFIG_HYPERV)		+= hv/
obj-$(CONFIG_VME_BUS)		+= vme/
obj-$(CONFIG_RAR_REGISTER)	+= rar_register/
obj-$(CONFIG_MRST_RAR_HANDLER)	+= memrar/
obj-$(CONFIG_DX_SEP)		+= sep/
obj-$(CONFIG_IIO)		+= iio/
obj-$(CONFIG_RAMZSWAP)		+= ramzswap/
@@ -44,11 +44,10 @@ obj-$(CONFIG_WLAGS49_H2) += wlags49_h2/
obj-$(CONFIG_WLAGS49_H25)	+= wlags49_h25/
obj-$(CONFIG_BATMAN_ADV)	+= batman-adv/
obj-$(CONFIG_SAMSUNG_LAPTOP)	+= samsung-laptop/
obj-$(CONFIG_STRIP)		+= strip/
obj-$(CONFIG_ARLAN)		+= arlan/
obj-$(CONFIG_WAVELAN)		+= wavelan/
obj-$(CONFIG_PCMCIA_WAVELAN)	+= wavelan/
obj-$(CONFIG_PCMCIA_NETWAVE)	+= netwave/
obj-$(CONFIG_FB_SM7XX)		+= sm7xx/
obj-$(CONFIG_DT3155)		+= dt3155/
obj-$(CONFIG_VIDEO_DT3155)	+= dt3155v4l/
obj-$(CONFIG_CRYSTALHD)		+= crystalhd/
obj-$(CONFIG_CXT1E1)		+= cxt1e1/
obj-$(CONFIG_TI_ST)		+= ti-st/
obj-$(CONFIG_ADIS16255)		+= adis16255/
+11 −0
Original line number Diff line number Diff line
config ADIS16255
	tristate "Ananlog Devices ADIS16250/16255"
	depends on SPI && SYSFS
	---help---
	If you say yes here you get support for the Analog Devices
	ADIS16250/16255 Low Power Gyroscope. The driver exposes
	orientation and gyroscope value, as well as sample rate
	to the sysfs.

	This driver can also be built as a module. If so, the module
	will be called adis16255.
+1 −0
Original line number Diff line number Diff line
obj-$(CONFIG_ADIS16255)		+= adis16255.o
+466 −0
Original line number Diff line number Diff line
/*
 * Analog Devices ADIS16250/ADIS16255 Low Power Gyroscope
 *
 * Written by: Matthias Brugger <m_brugger@web.de>
 *
 * Copyright (C) 2010 Fraunhofer Institute for Integrated Circuits
 *
 * 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.
 */

/*
 * The driver just has a bare interface to the sysfs (sample rate in Hz,
 * orientation (x, y, z) and gyroscope data in °/sec.
 *
 * It should be added to iio subsystem when this has left staging.
 *
 */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/slab.h>

#include <linux/interrupt.h>
#include <linux/sysfs.h>
#include <linux/stat.h>
#include <linux/delay.h>

#include <linux/gpio.h>

#include <linux/spi/spi.h>
#include <linux/workqueue.h>

#include "adis16255.h"

#define ADIS_STATUS        0x3d
#define ADIS_SMPL_PRD_MSB  0x37
#define ADIS_SMPL_PRD_LSB  0x36
#define ADIS_MSC_CTRL_MSB  0x35
#define ADIS_MSC_CTRL_LSB  0x34
#define ADIS_GPIO_CTRL     0x33
#define ADIS_ALM_SMPL1     0x25
#define ADIS_ALM_MAG1      0x21
#define ADIS_GYRO_SCALE    0x17
#define ADIS_GYRO_OUT      0x05
#define ADIS_SUPPLY_OUT    0x03
#define ADIS_ENDURANCE     0x01

/*
 * data structure for every sensor
 *
 * @dev:       Driver model representation of the device.
 * @spi:       Pointer to the spi device which will manage i/o to spi bus.
 * @data:      Last read data from device.
 * @irq_adis:  GPIO Number of IRQ signal
 * @irq:       irq line manage by kernel
 * @negative:  indicates if sensor is upside down (negative == 1)
 * @direction: indicates axis (x, y, z) the sensor is meassuring
 */
struct spi_adis16255_data {
	struct device dev;
	struct spi_device *spi;
	s16 data;
	int irq;
	u8 negative;
	char direction;
};

/*-------------------------------------------------------------------------*/

static int spi_adis16255_read_data(struct spi_adis16255_data *spiadis,
					u8 adr,
					u8 *rbuf)
{
	struct spi_device *spi = spiadis->spi;
	struct spi_message msg;
	struct spi_transfer xfer1, xfer2;
	u8 *buf, *rx;
	int ret;

	buf = kzalloc(4, GFP_KERNEL);
	if (buf == NULL)
		return -ENOMEM;

	rx = kzalloc(4, GFP_KERNEL);
	if (rx == NULL) {
		ret = -ENOMEM;
		goto err_buf;
	}

	buf[0] = adr;

	spi_message_init(&msg);
	memset(&xfer1, 0, sizeof(xfer1));
	memset(&xfer2, 0, sizeof(xfer2));

	xfer1.tx_buf = buf;
	xfer1.rx_buf = buf + 2;
	xfer1.len = 2;
	xfer1.delay_usecs = 9;

	xfer2.tx_buf = rx + 2;
	xfer2.rx_buf = rx;
	xfer2.len = 2;

	spi_message_add_tail(&xfer1, &msg);
	spi_message_add_tail(&xfer2, &msg);

	ret = spi_sync(spi, &msg);
	if (ret == 0) {
		rbuf[0] = rx[0];
		rbuf[1] = rx[1];
	}

	kfree(rx);
err_buf:
	kfree(buf);

	return ret;
}

static int spi_adis16255_write_data(struct spi_adis16255_data *spiadis,
					u8 adr1,
					u8 adr2,
					u8 *wbuf)
{
	struct spi_device *spi = spiadis->spi;
	struct spi_message   msg;
	struct spi_transfer  xfer1, xfer2;
	u8       *buf, *rx;
	int         ret;

	buf = kmalloc(4, GFP_KERNEL);
	if (buf == NULL)
		return -ENOMEM;

	rx = kzalloc(4, GFP_KERNEL);
	if (rx == NULL) {
		ret = -ENOMEM;
		goto err_buf;
	}

	spi_message_init(&msg);
	memset(&xfer1, 0, sizeof(xfer1));
	memset(&xfer2, 0, sizeof(xfer2));

	buf[0] = adr1 | 0x80;
	buf[1] = *wbuf;

	buf[2] = adr2 | 0x80;
	buf[3] = *(wbuf + 1);

	xfer1.tx_buf = buf;
	xfer1.rx_buf = rx;
	xfer1.len = 2;
	xfer1.delay_usecs = 9;

	xfer2.tx_buf = buf+2;
	xfer2.rx_buf = rx+2;
	xfer2.len = 2;

	spi_message_add_tail(&xfer1, &msg);
	spi_message_add_tail(&xfer2, &msg);

	ret = spi_sync(spi, &msg);
	if (ret != 0)
		dev_warn(&spi->dev, "write data to %#x %#x failed\n",
				buf[0], buf[2]);

	kfree(rx);
err_buf:
	kfree(buf);
	return ret;
}

/*-------------------------------------------------------------------------*/

static irqreturn_t adis_irq_thread(int irq, void *dev_id)
{
	struct spi_adis16255_data *spiadis = dev_id;
	int status;
	u16 value = 0;

	status =  spi_adis16255_read_data(spiadis, ADIS_GYRO_OUT, (u8 *)&value);
	if (status != 0) {
		dev_warn(&spiadis->spi->dev, "SPI FAILED\n");
		goto exit;
	}

	/* perform on new data only... */
	if (value & 0x8000) {
		/* delete error and new data bit */
		value = value & 0x3fff;
		/* set negative value */
		if (value & 0x2000)
			value = value | 0xe000;

		if (likely(spiadis->negative))
			value = -value;

		spiadis->data = (s16) value;
	}

exit:
	return IRQ_HANDLED;
}

/*-------------------------------------------------------------------------*/

ssize_t adis16255_show_data(struct device *device,
		struct device_attribute *da,
		char *buf)
{
	struct spi_adis16255_data *spiadis = dev_get_drvdata(device);
	return snprintf(buf, PAGE_SIZE, "%d\n", spiadis->data);
}
DEVICE_ATTR(data, S_IRUGO , adis16255_show_data, NULL);

ssize_t adis16255_show_direction(struct device *device,
		struct device_attribute *da,
		char *buf)
{
	struct spi_adis16255_data *spiadis = dev_get_drvdata(device);
	return snprintf(buf, PAGE_SIZE, "%c\n", spiadis->direction);
}
DEVICE_ATTR(direction, S_IRUGO , adis16255_show_direction, NULL);

ssize_t adis16255_show_sample_rate(struct device *device,
		struct device_attribute *da,
		char *buf)
{
	struct spi_adis16255_data *spiadis = dev_get_drvdata(device);
	int status = 0;
	u16 value = 0;
	int ts = 0;

	status = spi_adis16255_read_data(spiadis, ADIS_SMPL_PRD_MSB,
				(u8 *)&value);
	if (status != 0)
		return -EINVAL;

	if (value & 0x80) {
		/* timebase = 60.54 ms */
		ts = 60540 * ((0x7f & value) + 1);
	} else {
		/* timebase = 1.953 ms */
		ts = 1953 * ((0x7f & value) + 1);
	}

	return snprintf(buf, PAGE_SIZE, "%d\n", (1000*1000)/ts);
}
DEVICE_ATTR(sample_rate, S_IRUGO , adis16255_show_sample_rate, NULL);

static struct attribute *adis16255_attributes[] = {
	&dev_attr_data.attr,
	&dev_attr_direction.attr,
	&dev_attr_sample_rate.attr,
	NULL
};

static const struct attribute_group adis16255_attr_group = {
	.attrs = adis16255_attributes,
};

/*-------------------------------------------------------------------------*/

static int spi_adis16255_shutdown(struct spi_adis16255_data *spiadis)
{
	u16 value = 0;
	/* turn sensor off */
	spi_adis16255_write_data(spiadis,
			ADIS_SMPL_PRD_MSB, ADIS_SMPL_PRD_LSB,
			(u8 *)&value);
	spi_adis16255_write_data(spiadis,
			ADIS_MSC_CTRL_MSB, ADIS_MSC_CTRL_LSB,
			(u8 *)&value);
	return 0;
}

static int spi_adis16255_bringup(struct spi_adis16255_data *spiadis)
{
	int status = 0;
	u16 value = 0;

	status = spi_adis16255_read_data(spiadis, ADIS_GYRO_SCALE,
				(u8 *)&value);
	if (status != 0)
		goto err;
	if (value != 0x0800) {
		dev_warn(&spiadis->spi->dev, "Scale factor is none default"
				"value (%.4x)\n", value);
	}

	/* timebase = 1.953 ms, Ns = 0 -> 512 Hz sample rate */
	value =  0x0001;
	status = spi_adis16255_write_data(spiadis,
				ADIS_SMPL_PRD_MSB, ADIS_SMPL_PRD_LSB,
				(u8 *)&value);
	if (status != 0)
		goto err;

	/* start internal self-test */
	value = 0x0400;
	status = spi_adis16255_write_data(spiadis,
				ADIS_MSC_CTRL_MSB, ADIS_MSC_CTRL_LSB,
				(u8 *)&value);
	if (status != 0)
		goto err;

	/* wait 35 ms to finish self-test */
	msleep(35);

	value = 0x0000;
	status = spi_adis16255_read_data(spiadis, ADIS_STATUS,
				(u8 *)&value);
	if (status != 0)
		goto err;

	if (value & 0x23) {
		if (value & 0x20) {
			dev_warn(&spiadis->spi->dev, "self-test error\n");
			status = -ENODEV;
			goto err;
		} else if (value & 0x3)	{
			dev_warn(&spiadis->spi->dev, "Sensor voltage"
						"out of range.\n");
			status = -ENODEV;
			goto err;
		}
	}

	/* set interrupt to active high on DIO0 when data ready */
	value = 0x0006;
	status = spi_adis16255_write_data(spiadis,
				ADIS_MSC_CTRL_MSB, ADIS_MSC_CTRL_LSB,
				(u8 *)&value);
	if (status != 0)
		goto err;
	return status;

err:
	spi_adis16255_shutdown(spiadis);
	return status;
}

/*-------------------------------------------------------------------------*/

static int spi_adis16255_probe(struct spi_device *spi)
{

	struct adis16255_init_data *init_data = spi->dev.platform_data;
	struct spi_adis16255_data  *spiadis;
	int status = 0;

	spiadis = kzalloc(sizeof(*spiadis), GFP_KERNEL);
	if (!spiadis)
		return -ENOMEM;

	spiadis->spi = spi;
	spiadis->direction = init_data->direction;

	if (init_data->negative)
		spiadis->negative = 1;

	status = gpio_request(init_data->irq, "adis16255");
	if (status != 0)
		goto err;

	status = gpio_direction_input(init_data->irq);
	if (status != 0)
		goto gpio_err;

	spiadis->irq = gpio_to_irq(init_data->irq);

	status = request_threaded_irq(spiadis->irq,
			NULL, adis_irq_thread,
			IRQF_DISABLED, "adis-driver", spiadis);

	if (status != 0) {
		dev_err(&spi->dev, "IRQ request failed\n");
		goto gpio_err;
	}

	dev_dbg(&spi->dev, "GPIO %d IRQ %d\n", init_data->irq, spiadis->irq);

	dev_set_drvdata(&spi->dev, spiadis);
	status = sysfs_create_group(&spi->dev.kobj, &adis16255_attr_group);
	if (status != 0)
		goto irq_err;

	status = spi_adis16255_bringup(spiadis);
	if (status != 0)
		goto irq_err;

	dev_info(&spi->dev, "spi_adis16255 driver added!\n");

	return status;

irq_err:
	free_irq(spiadis->irq, spiadis);
gpio_err:
	gpio_free(init_data->irq);
err:
	kfree(spiadis);
	return status;
}

static int spi_adis16255_remove(struct spi_device *spi)
{
	struct spi_adis16255_data  *spiadis    = dev_get_drvdata(&spi->dev);

	spi_adis16255_shutdown(spiadis);

	free_irq(spiadis->irq, spiadis);
	gpio_free(irq_to_gpio(spiadis->irq));

	sysfs_remove_group(&spiadis->spi->dev.kobj, &adis16255_attr_group);

	kfree(spiadis);

	dev_info(&spi->dev, "spi_adis16255 driver removed!\n");
	return 0;
}

static struct spi_driver spi_adis16255_drv = {
	.driver = {
		.name =  "spi_adis16255",
		.owner = THIS_MODULE,
	},
	.probe = spi_adis16255_probe,
	.remove =   __devexit_p(spi_adis16255_remove),
};

/*-------------------------------------------------------------------------*/

static int __init spi_adis16255_init(void)
{
	return spi_register_driver(&spi_adis16255_drv);
}
module_init(spi_adis16255_init);

static void __exit spi_adis16255_exit(void)
{
	spi_unregister_driver(&spi_adis16255_drv);
}
module_exit(spi_adis16255_exit);

MODULE_AUTHOR("Matthias Brugger");
MODULE_DESCRIPTION("SPI device driver for ADIS16255 sensor");
MODULE_LICENSE("GPL");
Loading