diff options
Diffstat (limited to 'drivers/iio/gyro')
| -rw-r--r-- | drivers/iio/gyro/Kconfig | 101 | ||||
| -rw-r--r-- | drivers/iio/gyro/Makefile | 23 | ||||
| -rw-r--r-- | drivers/iio/gyro/adis16080.c | 241 | ||||
| -rw-r--r-- | drivers/iio/gyro/adis16130.c | 179 | ||||
| -rw-r--r-- | drivers/iio/gyro/adis16136.c | 577 | ||||
| -rw-r--r-- | drivers/iio/gyro/adis16260.c | 421 | ||||
| -rw-r--r-- | drivers/iio/gyro/adxrs450.c | 468 | ||||
| -rw-r--r-- | drivers/iio/gyro/hid-sensor-gyro-3d.c | 428 | ||||
| -rw-r--r-- | drivers/iio/gyro/itg3200_buffer.c | 153 | ||||
| -rw-r--r-- | drivers/iio/gyro/itg3200_core.c | 389 | ||||
| -rw-r--r-- | drivers/iio/gyro/st_gyro.h | 53 | ||||
| -rw-r--r-- | drivers/iio/gyro/st_gyro_buffer.c | 105 | ||||
| -rw-r--r-- | drivers/iio/gyro/st_gyro_core.c | 380 | ||||
| -rw-r--r-- | drivers/iio/gyro/st_gyro_i2c.c | 77 | ||||
| -rw-r--r-- | drivers/iio/gyro/st_gyro_spi.c | 76 | 
15 files changed, 3671 insertions, 0 deletions
diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig new file mode 100644 index 00000000000..ac2d69e34c8 --- /dev/null +++ b/drivers/iio/gyro/Kconfig @@ -0,0 +1,101 @@ +# +# IIO Digital Gyroscope Sensor drivers configuration +# +# When adding new entries keep the list in alphabetical order + +menu "Digital gyroscope sensors" + +config ADIS16080 +	tristate "Analog Devices ADIS16080/100 Yaw Rate Gyroscope with SPI driver" +	depends on SPI +	help +	  Say yes here to build support for Analog Devices ADIS16080, ADIS16100 Yaw +	  Rate Gyroscope with SPI. + +config ADIS16130 +	tristate "Analog Devices ADIS16130 High Precision Angular Rate Sensor driver" +	depends on SPI +	help +	  Say yes here to build support for Analog Devices ADIS16130 High Precision +	  Angular Rate Sensor driver. + +config ADIS16136 +	tristate "Analog devices ADIS16136 and similar gyroscopes driver" +	depends on SPI_MASTER +	select IIO_ADIS_LIB +	select IIO_ADIS_LIB_BUFFER if IIO_BUFFER +	help +	  Say yes here to build support for the Analog Devices ADIS16133, ADIS16135, +	  ADIS16136 gyroscope devices. + +config ADIS16260 +	tristate "Analog Devices ADIS16260 Digital Gyroscope Sensor SPI driver" +	depends on SPI +	select IIO_ADIS_LIB +	select IIO_ADIS_LIB_BUFFER if IIO_BUFFER +	help +	  Say yes here to build support for Analog Devices ADIS16260 ADIS16265 +	  ADIS16250 ADIS16255 and ADIS16251 programmable digital gyroscope sensors. + +	  This driver can also be built as a module.  If so, the module +	  will be called adis16260. + +config ADXRS450 +	tristate "Analog Devices ADXRS450/3 Digital Output Gyroscope SPI driver" +	depends on SPI +	help +	  Say yes here to build support for Analog Devices ADXRS450 and ADXRS453 +	  programmable digital output gyroscope. + +	  This driver can also be built as a module.  If so, the module +	  will be called adxrs450. + +config HID_SENSOR_GYRO_3D +	depends on HID_SENSOR_HUB +	select IIO_BUFFER +	select IIO_TRIGGERED_BUFFER +	select HID_SENSOR_IIO_COMMON +	select HID_SENSOR_IIO_TRIGGER +	tristate "HID Gyroscope 3D" +	help +	  Say yes here to build support for the HID SENSOR +	  Gyroscope 3D. + +config IIO_ST_GYRO_3AXIS +	tristate "STMicroelectronics gyroscopes 3-Axis Driver" +	depends on (I2C || SPI_MASTER) && SYSFS +	select IIO_ST_SENSORS_CORE +	select IIO_ST_GYRO_I2C_3AXIS if (I2C) +	select IIO_ST_GYRO_SPI_3AXIS if (SPI_MASTER) +	select IIO_TRIGGERED_BUFFER if (IIO_BUFFER) +	help +	  Say yes here to build support for STMicroelectronics gyroscopes: +	  L3G4200D, LSM330DL, L3GD20, LSM330DLC, L3G4IS, LSM330. + +	  This driver can also be built as a module. If so, these modules +	  will be created: +	  - st_gyro (core functions for the driver [it is mandatory]); +	  - st_gyro_i2c (necessary for the I2C devices [optional*]); +	  - st_gyro_spi (necessary for the SPI devices [optional*]); + +	  (*) one of these is necessary to do something. + +config IIO_ST_GYRO_I2C_3AXIS +	tristate +	depends on IIO_ST_GYRO_3AXIS +	depends on IIO_ST_SENSORS_I2C + +config IIO_ST_GYRO_SPI_3AXIS +	tristate +	depends on IIO_ST_GYRO_3AXIS +	depends on IIO_ST_SENSORS_SPI + +config ITG3200 +	tristate "InvenSense ITG3200 Digital 3-Axis Gyroscope I2C driver" +	depends on I2C +	select IIO_TRIGGERED_BUFFER if IIO_BUFFER +	help +	  Say yes here to add support for the InvenSense ITG3200 digital +	  3-axis gyroscope sensor. + +endmenu diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile new file mode 100644 index 00000000000..2f2752a4ea8 --- /dev/null +++ b/drivers/iio/gyro/Makefile @@ -0,0 +1,23 @@ +# +# Makefile for industrial I/O gyroscope sensor drivers +# + +# When adding new entries keep the list in alphabetical order +obj-$(CONFIG_ADIS16080) += adis16080.o +obj-$(CONFIG_ADIS16130) += adis16130.o +obj-$(CONFIG_ADIS16136) += adis16136.o +obj-$(CONFIG_ADIS16260) += adis16260.o +obj-$(CONFIG_ADXRS450) += adxrs450.o + +obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o + +itg3200-y               := itg3200_core.o +itg3200-$(CONFIG_IIO_BUFFER) += itg3200_buffer.o +obj-$(CONFIG_ITG3200)   += itg3200.o + +obj-$(CONFIG_IIO_ST_GYRO_3AXIS) += st_gyro.o +st_gyro-y := st_gyro_core.o +st_gyro-$(CONFIG_IIO_BUFFER) += st_gyro_buffer.o + +obj-$(CONFIG_IIO_ST_GYRO_I2C_3AXIS) += st_gyro_i2c.o +obj-$(CONFIG_IIO_ST_GYRO_SPI_3AXIS) += st_gyro_spi.o diff --git a/drivers/iio/gyro/adis16080.c b/drivers/iio/gyro/adis16080.c new file mode 100644 index 00000000000..add50983726 --- /dev/null +++ b/drivers/iio/gyro/adis16080.c @@ -0,0 +1,241 @@ +/* + * ADIS16080/100 Yaw Rate Gyroscope with SPI driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#define ADIS16080_DIN_GYRO   (0 << 10) /* Gyroscope output */ +#define ADIS16080_DIN_TEMP   (1 << 10) /* Temperature output */ +#define ADIS16080_DIN_AIN1   (2 << 10) +#define ADIS16080_DIN_AIN2   (3 << 10) + +/* + * 1: Write contents on DIN to control register. + * 0: No changes to control register. + */ + +#define ADIS16080_DIN_WRITE  (1 << 15) + +struct adis16080_chip_info { +	int scale_val; +	int scale_val2; +}; + +/** + * struct adis16080_state - device instance specific data + * @us:			actual spi_device to write data + * @info:		chip specific parameters + * @buf:		transmit or receive buffer + **/ +struct adis16080_state { +	struct spi_device		*us; +	const struct adis16080_chip_info *info; + +	__be16 buf ____cacheline_aligned; +}; + +static int adis16080_read_sample(struct iio_dev *indio_dev, +		u16 addr, int *val) +{ +	struct adis16080_state *st = iio_priv(indio_dev); +	int ret; +	struct spi_transfer	t[] = { +		{ +			.tx_buf		= &st->buf, +			.len		= 2, +			.cs_change	= 1, +		}, { +			.rx_buf		= &st->buf, +			.len		= 2, +		}, +	}; + +	st->buf = cpu_to_be16(addr | ADIS16080_DIN_WRITE); + +	ret = spi_sync_transfer(st->us, t, ARRAY_SIZE(t)); +	if (ret == 0) +		*val = sign_extend32(be16_to_cpu(st->buf), 11); + +	return ret; +} + +static int adis16080_read_raw(struct iio_dev *indio_dev, +			     struct iio_chan_spec const *chan, +			     int *val, +			     int *val2, +			     long mask) +{ +	struct adis16080_state *st = iio_priv(indio_dev); +	int ret; + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		mutex_lock(&indio_dev->mlock); +		ret = adis16080_read_sample(indio_dev, chan->address, val); +		mutex_unlock(&indio_dev->mlock); +		return ret ? ret : IIO_VAL_INT; +	case IIO_CHAN_INFO_SCALE: +		switch (chan->type) { +		case IIO_ANGL_VEL: +			*val = st->info->scale_val; +			*val2 = st->info->scale_val2; +			return IIO_VAL_FRACTIONAL; +		case IIO_VOLTAGE: +			/* VREF = 5V, 12 bits */ +			*val = 5000; +			*val2 = 12; +			return IIO_VAL_FRACTIONAL_LOG2; +		case IIO_TEMP: +			/* 85 C = 585, 25 C = 0 */ +			*val = 85000 - 25000; +			*val2 = 585; +			return IIO_VAL_FRACTIONAL; +		default: +			return -EINVAL; +		} +	case IIO_CHAN_INFO_OFFSET: +		switch (chan->type) { +		case IIO_VOLTAGE: +			/* 2.5 V = 0 */ +			*val = 2048; +			return IIO_VAL_INT; +		case IIO_TEMP: +			/* 85 C = 585, 25 C = 0 */ +			*val = DIV_ROUND_CLOSEST(25 * 585, 85 - 25); +			return IIO_VAL_INT; +		default: +			return -EINVAL; +		} +	default: +		break; +	} + +	return -EINVAL; +} + +static const struct iio_chan_spec adis16080_channels[] = { +	{ +		.type = IIO_ANGL_VEL, +		.modified = 1, +		.channel2 = IIO_MOD_Z, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +			BIT(IIO_CHAN_INFO_SCALE), +		.address = ADIS16080_DIN_GYRO, +	}, { +		.type = IIO_VOLTAGE, +		.indexed = 1, +		.channel = 0, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +			BIT(IIO_CHAN_INFO_SCALE) | +			BIT(IIO_CHAN_INFO_OFFSET), +		.address = ADIS16080_DIN_AIN1, +	}, { +		.type = IIO_VOLTAGE, +		.indexed = 1, +		.channel = 1, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +			BIT(IIO_CHAN_INFO_SCALE) | +			BIT(IIO_CHAN_INFO_OFFSET), +		.address = ADIS16080_DIN_AIN2, +	}, { +		.type = IIO_TEMP, +		.indexed = 1, +		.channel = 0, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +			BIT(IIO_CHAN_INFO_SCALE) | +			BIT(IIO_CHAN_INFO_OFFSET), +		.address = ADIS16080_DIN_TEMP, +	} +}; + +static const struct iio_info adis16080_info = { +	.read_raw = &adis16080_read_raw, +	.driver_module = THIS_MODULE, +}; + +enum { +	ID_ADIS16080, +	ID_ADIS16100, +}; + +static const struct adis16080_chip_info adis16080_chip_info[] = { +	[ID_ADIS16080] = { +		/* 80 degree = 819, 819 rad = 46925 degree */ +		.scale_val = 80, +		.scale_val2 = 46925, +	}, +	[ID_ADIS16100] = { +		/* 300 degree = 1230, 1230 rad = 70474 degree */ +		.scale_val = 300, +		.scale_val2 = 70474, +	}, +}; + +static int adis16080_probe(struct spi_device *spi) +{ +	const struct spi_device_id *id = spi_get_device_id(spi); +	struct adis16080_state *st; +	struct iio_dev *indio_dev; + +	/* setup the industrialio driver allocated elements */ +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (!indio_dev) +		return -ENOMEM; +	st = iio_priv(indio_dev); +	/* this is only used for removal purposes */ +	spi_set_drvdata(spi, indio_dev); + +	/* Allocate the comms buffers */ +	st->us = spi; +	st->info = &adis16080_chip_info[id->driver_data]; + +	indio_dev->name = spi->dev.driver->name; +	indio_dev->channels = adis16080_channels; +	indio_dev->num_channels = ARRAY_SIZE(adis16080_channels); +	indio_dev->dev.parent = &spi->dev; +	indio_dev->info = &adis16080_info; +	indio_dev->modes = INDIO_DIRECT_MODE; + +	return iio_device_register(indio_dev); +} + +static int adis16080_remove(struct spi_device *spi) +{ +	iio_device_unregister(spi_get_drvdata(spi)); +	return 0; +} + +static const struct spi_device_id adis16080_ids[] = { +	{ "adis16080", ID_ADIS16080 }, +	{ "adis16100", ID_ADIS16100 }, +	{}, +}; +MODULE_DEVICE_TABLE(spi, adis16080_ids); + +static struct spi_driver adis16080_driver = { +	.driver = { +		.name = "adis16080", +		.owner = THIS_MODULE, +	}, +	.probe = adis16080_probe, +	.remove = adis16080_remove, +	.id_table = adis16080_ids, +}; +module_spi_driver(adis16080_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADIS16080/100 Yaw Rate Gyroscope Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/gyro/adis16130.c b/drivers/iio/gyro/adis16130.c new file mode 100644 index 00000000000..8d08c7ed1ea --- /dev/null +++ b/drivers/iio/gyro/adis16130.c @@ -0,0 +1,179 @@ +/* + * ADIS16130 Digital Output, High Precision Angular Rate Sensor driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/mutex.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> + +#define ADIS16130_CON         0x0 +#define ADIS16130_CON_RD      (1 << 6) +#define ADIS16130_IOP         0x1 + +/* 1 = data-ready signal low when unread data on all channels; */ +#define ADIS16130_IOP_ALL_RDY (1 << 3) +#define ADIS16130_IOP_SYNC    (1 << 0) /* 1 = synchronization enabled */ +#define ADIS16130_RATEDATA    0x8 /* Gyroscope output, rate of rotation */ +#define ADIS16130_TEMPDATA    0xA /* Temperature output */ +#define ADIS16130_RATECS      0x28 /* Gyroscope channel setup */ +#define ADIS16130_RATECS_EN   (1 << 3) /* 1 = channel enable; */ +#define ADIS16130_TEMPCS      0x2A /* Temperature channel setup */ +#define ADIS16130_TEMPCS_EN   (1 << 3) +#define ADIS16130_RATECONV    0x30 +#define ADIS16130_TEMPCONV    0x32 +#define ADIS16130_MODE        0x38 +#define ADIS16130_MODE_24BIT  (1 << 1) /* 1 = 24-bit resolution; */ + +/** + * struct adis16130_state - device instance specific data + * @us:			actual spi_device to write data + * @buf_lock:		mutex to protect tx and rx + * @buf:		unified tx/rx buffer + **/ +struct adis16130_state { +	struct spi_device		*us; +	struct mutex			buf_lock; +	u8				buf[4] ____cacheline_aligned; +}; + +static int adis16130_spi_read(struct iio_dev *indio_dev, u8 reg_addr, u32 *val) +{ +	int ret; +	struct adis16130_state *st = iio_priv(indio_dev); +	struct spi_transfer xfer = { +		.tx_buf = st->buf, +		.rx_buf = st->buf, +		.len = 4, +	}; + +	mutex_lock(&st->buf_lock); + +	st->buf[0] = ADIS16130_CON_RD | reg_addr; +	st->buf[1] = st->buf[2] = st->buf[3] = 0; + +	ret = spi_sync_transfer(st->us, &xfer, 1); +	if (ret == 0) +		*val = (st->buf[1] << 16) | (st->buf[2] << 8) | st->buf[3]; +	mutex_unlock(&st->buf_lock); + +	return ret; +} + +static int adis16130_read_raw(struct iio_dev *indio_dev, +			      struct iio_chan_spec const *chan, +			      int *val, int *val2, +			      long mask) +{ +	int ret; +	u32 temp; + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		/* Take the iio_dev status lock */ +		mutex_lock(&indio_dev->mlock); +		ret = adis16130_spi_read(indio_dev, chan->address, &temp); +		mutex_unlock(&indio_dev->mlock); +		if (ret) +			return ret; +		*val = temp; +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_SCALE: +		switch (chan->type) { +		case IIO_ANGL_VEL: +			/* 0 degree = 838860, 250 degree = 14260608 */ +			*val = 250; +			*val2 = 336440817; /* RAD_TO_DEGREE(14260608 - 8388608) */ +			return IIO_VAL_FRACTIONAL; +		case IIO_TEMP: +			/* 0C = 8036283, 105C = 9516048 */ +			*val = 105000; +			*val2 = 9516048 - 8036283; +			return IIO_VAL_FRACTIONAL; +		default: +			return -EINVAL; +		} +	case IIO_CHAN_INFO_OFFSET: +		switch (chan->type) { +		case IIO_ANGL_VEL: +			*val = -8388608; +			return IIO_VAL_INT; +		case IIO_TEMP: +			*val = -8036283; +			return IIO_VAL_INT; +		default: +			return -EINVAL; +		} +	} + +	return -EINVAL; +} + +static const struct iio_chan_spec adis16130_channels[] = { +	{ +		.type = IIO_ANGL_VEL, +		.modified = 1, +		.channel2 = IIO_MOD_Z, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +			BIT(IIO_CHAN_INFO_SCALE) | +			BIT(IIO_CHAN_INFO_OFFSET), +		.address = ADIS16130_RATEDATA, +	}, { +		.type = IIO_TEMP, +		.indexed = 1, +		.channel = 0, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +			BIT(IIO_CHAN_INFO_SCALE) | +			BIT(IIO_CHAN_INFO_OFFSET), +		.address = ADIS16130_TEMPDATA, +	} +}; + +static const struct iio_info adis16130_info = { +	.read_raw = &adis16130_read_raw, +	.driver_module = THIS_MODULE, +}; + +static int adis16130_probe(struct spi_device *spi) +{ +	struct adis16130_state *st; +	struct iio_dev *indio_dev; + +	/* setup the industrialio driver allocated elements */ +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (!indio_dev) +		return -ENOMEM; +	st = iio_priv(indio_dev); +	/* this is only used for removal purposes */ +	spi_set_drvdata(spi, indio_dev); +	st->us = spi; +	mutex_init(&st->buf_lock); +	indio_dev->name = spi->dev.driver->name; +	indio_dev->channels = adis16130_channels; +	indio_dev->num_channels = ARRAY_SIZE(adis16130_channels); +	indio_dev->dev.parent = &spi->dev; +	indio_dev->info = &adis16130_info; +	indio_dev->modes = INDIO_DIRECT_MODE; + +	return devm_iio_device_register(&spi->dev, indio_dev); +} + +static struct spi_driver adis16130_driver = { +	.driver = { +		.name = "adis16130", +		.owner = THIS_MODULE, +	}, +	.probe = adis16130_probe, +}; +module_spi_driver(adis16130_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADIS16130 High Precision Angular Rate"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:adis16130"); diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c new file mode 100644 index 00000000000..591bd555e1f --- /dev/null +++ b/drivers/iio/gyro/adis16136.c @@ -0,0 +1,577 @@ +/* + * ADIS16133/ADIS16135/ADIS16136 gyroscope driver + * + * Copyright 2012 Analog Devices Inc. + *   Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Licensed under the GPL-2. + */ + +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/imu/adis.h> + +#include <linux/debugfs.h> + +#define ADIS16136_REG_FLASH_CNT		0x00 +#define ADIS16136_REG_TEMP_OUT		0x02 +#define ADIS16136_REG_GYRO_OUT2		0x04 +#define ADIS16136_REG_GYRO_OUT		0x06 +#define ADIS16136_REG_GYRO_OFF2		0x08 +#define ADIS16136_REG_GYRO_OFF		0x0A +#define ADIS16136_REG_ALM_MAG1		0x10 +#define ADIS16136_REG_ALM_MAG2		0x12 +#define ADIS16136_REG_ALM_SAMPL1	0x14 +#define ADIS16136_REG_ALM_SAMPL2	0x16 +#define ADIS16136_REG_ALM_CTRL		0x18 +#define ADIS16136_REG_GPIO_CTRL		0x1A +#define ADIS16136_REG_MSC_CTRL		0x1C +#define ADIS16136_REG_SMPL_PRD		0x1E +#define ADIS16136_REG_AVG_CNT		0x20 +#define ADIS16136_REG_DEC_RATE		0x22 +#define ADIS16136_REG_SLP_CTRL		0x24 +#define ADIS16136_REG_DIAG_STAT		0x26 +#define ADIS16136_REG_GLOB_CMD		0x28 +#define ADIS16136_REG_LOT1		0x32 +#define ADIS16136_REG_LOT2		0x34 +#define ADIS16136_REG_LOT3		0x36 +#define ADIS16136_REG_PROD_ID		0x38 +#define ADIS16136_REG_SERIAL_NUM	0x3A + +#define ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL	2 +#define ADIS16136_DIAG_STAT_SPI_FAIL		3 +#define ADIS16136_DIAG_STAT_SELF_TEST_FAIL	5 +#define ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL	6 + +#define ADIS16136_MSC_CTRL_MEMORY_TEST BIT(11) +#define ADIS16136_MSC_CTRL_SELF_TEST BIT(10) + +struct adis16136_chip_info { +	unsigned int precision; +	unsigned int fullscale; +}; + +struct adis16136 { +	const struct adis16136_chip_info *chip_info; + +	struct adis adis; +}; + +#ifdef CONFIG_DEBUG_FS + +static ssize_t adis16136_show_serial(struct file *file, +		char __user *userbuf, size_t count, loff_t *ppos) +{ +	struct adis16136 *adis16136 = file->private_data; +	uint16_t lot1, lot2, lot3, serial; +	char buf[20]; +	size_t len; +	int ret; + +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SERIAL_NUM, +		&serial); +	if (ret < 0) +		return ret; + +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT1, &lot1); +	if (ret < 0) +		return ret; + +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT2, &lot2); +	if (ret < 0) +		return ret; + +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT3, &lot3); +	if (ret < 0) +		return ret; + +	len = snprintf(buf, sizeof(buf), "%.4x%.4x%.4x-%.4x\n", lot1, lot2, +		lot3, serial); + +	return simple_read_from_buffer(userbuf, count, ppos, buf, len); +} + +static const struct file_operations adis16136_serial_fops = { +	.open = simple_open, +	.read = adis16136_show_serial, +	.llseek = default_llseek, +	.owner = THIS_MODULE, +}; + +static int adis16136_show_product_id(void *arg, u64 *val) +{ +	struct adis16136 *adis16136 = arg; +	u16 prod_id; +	int ret; + +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_PROD_ID, +		&prod_id); +	if (ret < 0) +		return ret; + +	*val = prod_id; + +	return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(adis16136_product_id_fops, +	adis16136_show_product_id, NULL, "%llu\n"); + +static int adis16136_show_flash_count(void *arg, u64 *val) +{ +	struct adis16136 *adis16136 = arg; +	uint16_t flash_count; +	int ret; + +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_FLASH_CNT, +		&flash_count); +	if (ret < 0) +		return ret; + +	*val = flash_count; + +	return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(adis16136_flash_count_fops, +	adis16136_show_flash_count, NULL, "%lld\n"); + +static int adis16136_debugfs_init(struct iio_dev *indio_dev) +{ +	struct adis16136 *adis16136 = iio_priv(indio_dev); + +	debugfs_create_file("serial_number", 0400, indio_dev->debugfs_dentry, +		adis16136, &adis16136_serial_fops); +	debugfs_create_file("product_id", 0400, indio_dev->debugfs_dentry, +		adis16136, &adis16136_product_id_fops); +	debugfs_create_file("flash_count", 0400, indio_dev->debugfs_dentry, +		adis16136, &adis16136_flash_count_fops); + +	return 0; +} + +#else + +static int adis16136_debugfs_init(struct iio_dev *indio_dev) +{ +	return 0; +} + +#endif + +static int adis16136_set_freq(struct adis16136 *adis16136, unsigned int freq) +{ +	unsigned int t; + +	t = 32768 / freq; +	if (t < 0xf) +		t = 0xf; +	else if (t > 0xffff) +		t = 0xffff; +	else +		t--; + +	return adis_write_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, t); +} + +static int adis16136_get_freq(struct adis16136 *adis16136, unsigned int *freq) +{ +	uint16_t t; +	int ret; + +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, &t); +	if (ret < 0) +		return ret; + +	*freq = 32768 / (t + 1); + +	return 0; +} + +static ssize_t adis16136_write_frequency(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct adis16136 *adis16136 = iio_priv(indio_dev); +	unsigned int val; +	int ret; + +	ret = kstrtouint(buf, 10, &val); +	if (ret) +		return ret; + +	if (val == 0) +		return -EINVAL; + +	ret = adis16136_set_freq(adis16136, val); + +	return ret ? ret : len; +} + +static ssize_t adis16136_read_frequency(struct device *dev, +	struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct adis16136 *adis16136 = iio_priv(indio_dev); +	unsigned int freq; +	int ret; + +	ret = adis16136_get_freq(adis16136, &freq); +	if (ret < 0) +		return ret; + +	return sprintf(buf, "%d\n", freq); +} + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, +				  adis16136_read_frequency, +				  adis16136_write_frequency); + +static const unsigned adis16136_3db_divisors[] = { +	[0] = 2, /* Special case */ +	[1] = 6, +	[2] = 12, +	[3] = 25, +	[4] = 50, +	[5] = 100, +	[6] = 200, +	[7] = 200, /* Not a valid setting */ +}; + +static int adis16136_set_filter(struct iio_dev *indio_dev, int val) +{ +	struct adis16136 *adis16136 = iio_priv(indio_dev); +	unsigned int freq; +	int i, ret; + +	ret = adis16136_get_freq(adis16136, &freq); +	if (ret < 0) +		return ret; + +	for (i = ARRAY_SIZE(adis16136_3db_divisors) - 1; i >= 1; i--) { +		if (freq / adis16136_3db_divisors[i] >= val) +			break; +	} + +	return adis_write_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, i); +} + +static int adis16136_get_filter(struct iio_dev *indio_dev, int *val) +{ +	struct adis16136 *adis16136 = iio_priv(indio_dev); +	unsigned int freq; +	uint16_t val16; +	int ret; + +	mutex_lock(&indio_dev->mlock); + +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, &val16); +	if (ret < 0) +		goto err_unlock; + +	ret = adis16136_get_freq(adis16136, &freq); +	if (ret < 0) +		goto err_unlock; + +	*val = freq / adis16136_3db_divisors[val16 & 0x07]; + +err_unlock: +	mutex_unlock(&indio_dev->mlock); + +	return ret ? ret : IIO_VAL_INT; +} + +static int adis16136_read_raw(struct iio_dev *indio_dev, +	const struct iio_chan_spec *chan, int *val, int *val2, long info) +{ +	struct adis16136 *adis16136 = iio_priv(indio_dev); +	uint32_t val32; +	int ret; + +	switch (info) { +	case IIO_CHAN_INFO_RAW: +		return adis_single_conversion(indio_dev, chan, 0, val); +	case IIO_CHAN_INFO_SCALE: +		switch (chan->type) { +		case IIO_ANGL_VEL: +			*val = adis16136->chip_info->precision; +			*val2 = (adis16136->chip_info->fullscale << 16); +			return IIO_VAL_FRACTIONAL; +		case IIO_TEMP: +			*val = 10; +			*val2 = 697000; /* 0.010697 degree Celsius */ +			return IIO_VAL_INT_PLUS_MICRO; +		default: +			return -EINVAL; +		} +	case IIO_CHAN_INFO_CALIBBIAS: +		ret = adis_read_reg_32(&adis16136->adis, +			ADIS16136_REG_GYRO_OFF2, &val32); +		if (ret < 0) +			return ret; + +		*val = sign_extend32(val32, 31); + +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: +		return adis16136_get_filter(indio_dev, val); +	default: +		return -EINVAL; +	} +} + +static int adis16136_write_raw(struct iio_dev *indio_dev, +	const struct iio_chan_spec *chan, int val, int val2, long info) +{ +	struct adis16136 *adis16136 = iio_priv(indio_dev); + +	switch (info) { +	case IIO_CHAN_INFO_CALIBBIAS: +		return adis_write_reg_32(&adis16136->adis, +			ADIS16136_REG_GYRO_OFF2, val); +	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: +		return adis16136_set_filter(indio_dev, val); +	default: +		break; +	} + +	return -EINVAL; +} + +enum { +	ADIS16136_SCAN_GYRO, +	ADIS16136_SCAN_TEMP, +}; + +static const struct iio_chan_spec adis16136_channels[] = { +	{ +		.type = IIO_ANGL_VEL, +		.modified = 1, +		.channel2 = IIO_MOD_X, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +			BIT(IIO_CHAN_INFO_CALIBBIAS) | +			BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + +		.address = ADIS16136_REG_GYRO_OUT2, +		.scan_index = ADIS16136_SCAN_GYRO, +		.scan_type = { +			.sign = 's', +			.realbits = 32, +			.storagebits = 32, +			.endianness = IIO_BE, +		}, +	}, { +		.type = IIO_TEMP, +		.indexed = 1, +		.channel = 0, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +			BIT(IIO_CHAN_INFO_SCALE), +		.address = ADIS16136_REG_TEMP_OUT, +		.scan_index = ADIS16136_SCAN_TEMP, +		.scan_type = { +			.sign = 's', +			.realbits = 16, +			.storagebits = 16, +			.endianness = IIO_BE, +		}, +	}, +	IIO_CHAN_SOFT_TIMESTAMP(2), +}; + +static struct attribute *adis16136_attributes[] = { +	&iio_dev_attr_sampling_frequency.dev_attr.attr, +	NULL +}; + +static const struct attribute_group adis16136_attribute_group = { +	.attrs = adis16136_attributes, +}; + +static const struct iio_info adis16136_info = { +	.driver_module = THIS_MODULE, +	.attrs = &adis16136_attribute_group, +	.read_raw = &adis16136_read_raw, +	.write_raw = &adis16136_write_raw, +	.update_scan_mode = adis_update_scan_mode, +	.debugfs_reg_access = adis_debugfs_reg_access, +}; + +static int adis16136_stop_device(struct iio_dev *indio_dev) +{ +	struct adis16136 *adis16136 = iio_priv(indio_dev); +	int ret; + +	ret = adis_write_reg_16(&adis16136->adis, ADIS16136_REG_SLP_CTRL, 0xff); +	if (ret) +		dev_err(&indio_dev->dev, +			"Could not power down device: %d\n", ret); + +	return ret; +} + +static int adis16136_initial_setup(struct iio_dev *indio_dev) +{ +	struct adis16136 *adis16136 = iio_priv(indio_dev); +	unsigned int device_id; +	uint16_t prod_id; +	int ret; + +	ret = adis_initial_startup(&adis16136->adis); +	if (ret) +		return ret; + +	ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_PROD_ID, +		&prod_id); +	if (ret) +		return ret; + +	sscanf(indio_dev->name, "adis%u\n", &device_id); + +	if (prod_id != device_id) +		dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.", +				device_id, prod_id); + +	return 0; +} + +static const char * const adis16136_status_error_msgs[] = { +	[ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL] = "Flash update failed", +	[ADIS16136_DIAG_STAT_SPI_FAIL] = "SPI failure", +	[ADIS16136_DIAG_STAT_SELF_TEST_FAIL] = "Self test error", +	[ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL] = "Flash checksum error", +}; + +static const struct adis_data adis16136_data = { +	.diag_stat_reg = ADIS16136_REG_DIAG_STAT, +	.glob_cmd_reg = ADIS16136_REG_GLOB_CMD, +	.msc_ctrl_reg = ADIS16136_REG_MSC_CTRL, + +	.self_test_mask = ADIS16136_MSC_CTRL_SELF_TEST, +	.startup_delay = 80, + +	.read_delay = 10, +	.write_delay = 10, + +	.status_error_msgs = adis16136_status_error_msgs, +	.status_error_mask = BIT(ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL) | +		BIT(ADIS16136_DIAG_STAT_SPI_FAIL) | +		BIT(ADIS16136_DIAG_STAT_SELF_TEST_FAIL) | +		BIT(ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL), +}; + +enum adis16136_id { +	ID_ADIS16133, +	ID_ADIS16135, +	ID_ADIS16136, +}; + +static const struct adis16136_chip_info adis16136_chip_info[] = { +	[ID_ADIS16133] = { +		.precision = IIO_DEGREE_TO_RAD(1200), +		.fullscale = 24000, +	}, +	[ID_ADIS16135] = { +		.precision = IIO_DEGREE_TO_RAD(300), +		.fullscale = 24000, +	}, +	[ID_ADIS16136] = { +		.precision = IIO_DEGREE_TO_RAD(450), +		.fullscale = 24623, +	}, +}; + +static int adis16136_probe(struct spi_device *spi) +{ +	const struct spi_device_id *id = spi_get_device_id(spi); +	struct adis16136 *adis16136; +	struct iio_dev *indio_dev; +	int ret; + +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adis16136)); +	if (indio_dev == NULL) +		return -ENOMEM; + +	spi_set_drvdata(spi, indio_dev); + +	adis16136 = iio_priv(indio_dev); + +	adis16136->chip_info = &adis16136_chip_info[id->driver_data]; +	indio_dev->dev.parent = &spi->dev; +	indio_dev->name = spi_get_device_id(spi)->name; +	indio_dev->channels = adis16136_channels; +	indio_dev->num_channels = ARRAY_SIZE(adis16136_channels); +	indio_dev->info = &adis16136_info; +	indio_dev->modes = INDIO_DIRECT_MODE; + +	ret = adis_init(&adis16136->adis, indio_dev, spi, &adis16136_data); +	if (ret) +		return ret; + +	ret = adis_setup_buffer_and_trigger(&adis16136->adis, indio_dev, NULL); +	if (ret) +		return ret; + +	ret = adis16136_initial_setup(indio_dev); +	if (ret) +		goto error_cleanup_buffer; + +	ret = iio_device_register(indio_dev); +	if (ret) +		goto error_stop_device; + +	adis16136_debugfs_init(indio_dev); + +	return 0; + +error_stop_device: +	adis16136_stop_device(indio_dev); +error_cleanup_buffer: +	adis_cleanup_buffer_and_trigger(&adis16136->adis, indio_dev); +	return ret; +} + +static int adis16136_remove(struct spi_device *spi) +{ +	struct iio_dev *indio_dev = spi_get_drvdata(spi); +	struct adis16136 *adis16136 = iio_priv(indio_dev); + +	iio_device_unregister(indio_dev); +	adis16136_stop_device(indio_dev); + +	adis_cleanup_buffer_and_trigger(&adis16136->adis, indio_dev); + +	return 0; +} + +static const struct spi_device_id adis16136_ids[] = { +	{ "adis16133", ID_ADIS16133 }, +	{ "adis16135", ID_ADIS16135 }, +	{ "adis16136", ID_ADIS16136 }, +	{ } +}; +MODULE_DEVICE_TABLE(spi, adis16136_ids); + +static struct spi_driver adis16136_driver = { +	.driver = { +		.name = "adis16136", +		.owner = THIS_MODULE, +	}, +	.id_table = adis16136_ids, +	.probe = adis16136_probe, +	.remove = adis16136_remove, +}; +module_spi_driver(adis16136_driver); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("Analog Devices ADIS16133/ADIS16135/ADIS16136 gyroscope driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/gyro/adis16260.c b/drivers/iio/gyro/adis16260.c new file mode 100644 index 00000000000..22b6fb80fa1 --- /dev/null +++ b/drivers/iio/gyro/adis16260.c @@ -0,0 +1,421 @@ +/* + * ADIS16260/ADIS16265 Programmable Digital Gyroscope Sensor Driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/interrupt.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/sysfs.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/imu/adis.h> + +#define ADIS16260_STARTUP_DELAY	220 /* ms */ + +#define ADIS16260_FLASH_CNT  0x00 /* Flash memory write count */ +#define ADIS16260_SUPPLY_OUT 0x02 /* Power supply measurement */ +#define ADIS16260_GYRO_OUT   0x04 /* X-axis gyroscope output */ +#define ADIS16260_AUX_ADC    0x0A /* analog input channel measurement */ +#define ADIS16260_TEMP_OUT   0x0C /* internal temperature measurement */ +#define ADIS16260_ANGL_OUT   0x0E /* angle displacement */ +#define ADIS16260_GYRO_OFF   0x14 /* Calibration, offset/bias adjustment */ +#define ADIS16260_GYRO_SCALE 0x16 /* Calibration, scale adjustment */ +#define ADIS16260_ALM_MAG1   0x20 /* Alarm 1 magnitude/polarity setting */ +#define ADIS16260_ALM_MAG2   0x22 /* Alarm 2 magnitude/polarity setting */ +#define ADIS16260_ALM_SMPL1  0x24 /* Alarm 1 dynamic rate of change setting */ +#define ADIS16260_ALM_SMPL2  0x26 /* Alarm 2 dynamic rate of change setting */ +#define ADIS16260_ALM_CTRL   0x28 /* Alarm control */ +#define ADIS16260_AUX_DAC    0x30 /* Auxiliary DAC data */ +#define ADIS16260_GPIO_CTRL  0x32 /* Control, digital I/O line */ +#define ADIS16260_MSC_CTRL   0x34 /* Control, data ready, self-test settings */ +#define ADIS16260_SMPL_PRD   0x36 /* Control, internal sample rate */ +#define ADIS16260_SENS_AVG   0x38 /* Control, dynamic range, filtering */ +#define ADIS16260_SLP_CNT    0x3A /* Control, sleep mode initiation */ +#define ADIS16260_DIAG_STAT  0x3C /* Diagnostic, error flags */ +#define ADIS16260_GLOB_CMD   0x3E /* Control, global commands */ +#define ADIS16260_LOT_ID1    0x52 /* Lot Identification Code 1 */ +#define ADIS16260_LOT_ID2    0x54 /* Lot Identification Code 2 */ +#define ADIS16260_PROD_ID    0x56 /* Product identifier; +				   * convert to decimal = 16,265/16,260 */ +#define ADIS16260_SERIAL_NUM 0x58 /* Serial number */ + +#define ADIS16260_ERROR_ACTIVE			(1<<14) +#define ADIS16260_NEW_DATA			(1<<15) + +/* MSC_CTRL */ +#define ADIS16260_MSC_CTRL_MEM_TEST		(1<<11) +/* Internal self-test enable */ +#define ADIS16260_MSC_CTRL_INT_SELF_TEST	(1<<10) +#define ADIS16260_MSC_CTRL_NEG_SELF_TEST	(1<<9) +#define ADIS16260_MSC_CTRL_POS_SELF_TEST	(1<<8) +#define ADIS16260_MSC_CTRL_DATA_RDY_EN		(1<<2) +#define ADIS16260_MSC_CTRL_DATA_RDY_POL_HIGH	(1<<1) +#define ADIS16260_MSC_CTRL_DATA_RDY_DIO2	(1<<0) + +/* SMPL_PRD */ +/* Time base (tB): 0 = 1.953 ms, 1 = 60.54 ms */ +#define ADIS16260_SMPL_PRD_TIME_BASE	(1<<7) +#define ADIS16260_SMPL_PRD_DIV_MASK	0x7F + +/* SLP_CNT */ +#define ADIS16260_SLP_CNT_POWER_OFF     0x80 + +/* DIAG_STAT */ +#define ADIS16260_DIAG_STAT_ALARM2	(1<<9) +#define ADIS16260_DIAG_STAT_ALARM1	(1<<8) +#define ADIS16260_DIAG_STAT_FLASH_CHK_BIT	6 +#define ADIS16260_DIAG_STAT_SELF_TEST_BIT	5 +#define ADIS16260_DIAG_STAT_OVERFLOW_BIT	4 +#define ADIS16260_DIAG_STAT_SPI_FAIL_BIT	3 +#define ADIS16260_DIAG_STAT_FLASH_UPT_BIT	2 +#define ADIS16260_DIAG_STAT_POWER_HIGH_BIT	1 +#define ADIS16260_DIAG_STAT_POWER_LOW_BIT	0 + +/* GLOB_CMD */ +#define ADIS16260_GLOB_CMD_SW_RESET	(1<<7) +#define ADIS16260_GLOB_CMD_FLASH_UPD	(1<<3) +#define ADIS16260_GLOB_CMD_DAC_LATCH	(1<<2) +#define ADIS16260_GLOB_CMD_FAC_CALIB	(1<<1) +#define ADIS16260_GLOB_CMD_AUTO_NULL	(1<<0) + +#define ADIS16260_SPI_SLOW	(u32)(300 * 1000) +#define ADIS16260_SPI_BURST	(u32)(1000 * 1000) +#define ADIS16260_SPI_FAST	(u32)(2000 * 1000) + +/* At the moment triggers are only used for ring buffer + * filling. This may change! + */ + +#define ADIS16260_SCAN_GYRO	0 +#define ADIS16260_SCAN_SUPPLY	1 +#define ADIS16260_SCAN_AUX_ADC	2 +#define ADIS16260_SCAN_TEMP	3 +#define ADIS16260_SCAN_ANGL	4 + +static ssize_t adis16260_read_frequency(struct device *dev, +		struct device_attribute *attr, +		char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct adis *adis = iio_priv(indio_dev); +	int ret, len = 0; +	u16 t; +	int sps; +	ret = adis_read_reg_16(adis, ADIS16260_SMPL_PRD, &t); +	if (ret) +		return ret; + +	if (spi_get_device_id(adis->spi)->driver_data) /* If an adis16251 */ +		sps = (t & ADIS16260_SMPL_PRD_TIME_BASE) ? 8 : 256; +	else +		sps = (t & ADIS16260_SMPL_PRD_TIME_BASE) ? 66 : 2048; +	sps /= (t & ADIS16260_SMPL_PRD_DIV_MASK) + 1; +	len = sprintf(buf, "%d\n", sps); +	return len; +} + +static ssize_t adis16260_write_frequency(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct adis *adis = iio_priv(indio_dev); +	unsigned int val; +	int ret; +	u8 t; + +	ret = kstrtouint(buf, 10, &val); +	if (ret) +		return ret; + +	mutex_lock(&indio_dev->mlock); +	if (spi_get_device_id(adis->spi)->driver_data) +		t = 256 / val; +	else +		t = 2048 / val; + +	if (t > ADIS16260_SMPL_PRD_DIV_MASK) +		t = ADIS16260_SMPL_PRD_DIV_MASK; +	else if (t > 0) +		t--; + +	if (t >= 0x0A) +		adis->spi->max_speed_hz = ADIS16260_SPI_SLOW; +	else +		adis->spi->max_speed_hz = ADIS16260_SPI_FAST; +	ret = adis_write_reg_8(adis, ADIS16260_SMPL_PRD, t); + +	mutex_unlock(&indio_dev->mlock); + +	return ret ? ret : len; +} + +/* Power down the device */ +static int adis16260_stop_device(struct iio_dev *indio_dev) +{ +	struct adis *adis = iio_priv(indio_dev); +	int ret; +	u16 val = ADIS16260_SLP_CNT_POWER_OFF; + +	ret = adis_write_reg_16(adis, ADIS16260_SLP_CNT, val); +	if (ret) +		dev_err(&indio_dev->dev, "problem with turning device off: SLP_CNT"); + +	return ret; +} + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, +		adis16260_read_frequency, +		adis16260_write_frequency); + +static const struct iio_chan_spec adis16260_channels[] = { +	ADIS_GYRO_CHAN(X, ADIS16260_GYRO_OUT, ADIS16260_SCAN_GYRO, +		BIT(IIO_CHAN_INFO_CALIBBIAS) | +		BIT(IIO_CHAN_INFO_CALIBSCALE), 14), +	ADIS_INCLI_CHAN(X, ADIS16260_ANGL_OUT, ADIS16260_SCAN_ANGL, 0, 14), +	ADIS_TEMP_CHAN(ADIS16260_TEMP_OUT, ADIS16260_SCAN_TEMP, 12), +	ADIS_SUPPLY_CHAN(ADIS16260_SUPPLY_OUT, ADIS16260_SCAN_SUPPLY, 12), +	ADIS_AUX_ADC_CHAN(ADIS16260_AUX_ADC, ADIS16260_SCAN_AUX_ADC, 12), +	IIO_CHAN_SOFT_TIMESTAMP(5), +}; + +static const u8 adis16260_addresses[][2] = { +	[ADIS16260_SCAN_GYRO] = { ADIS16260_GYRO_OFF, ADIS16260_GYRO_SCALE }, +}; + +static int adis16260_read_raw(struct iio_dev *indio_dev, +			      struct iio_chan_spec const *chan, +			      int *val, int *val2, +			      long mask) +{ +	struct adis *adis = iio_priv(indio_dev); +	int ret; +	u8 addr; +	s16 val16; + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		return adis_single_conversion(indio_dev, chan, +				ADIS16260_ERROR_ACTIVE, val); +	case IIO_CHAN_INFO_SCALE: +		switch (chan->type) { +		case IIO_ANGL_VEL: +			*val = 0; +			if (spi_get_device_id(adis->spi)->driver_data) { +				/* 0.01832 degree / sec */ +				*val2 = IIO_DEGREE_TO_RAD(18320); +			} else { +				/* 0.07326 degree / sec */ +				*val2 = IIO_DEGREE_TO_RAD(73260); +			} +			return IIO_VAL_INT_PLUS_MICRO; +		case IIO_INCLI: +			*val = 0; +			*val2 = IIO_DEGREE_TO_RAD(36630); +			return IIO_VAL_INT_PLUS_MICRO; +		case IIO_VOLTAGE: +			if (chan->channel == 0) { +				*val = 1; +				*val2 = 831500; /* 1.8315 mV */ +			} else { +				*val = 0; +				*val2 = 610500; /* 610.5 uV */ +			} +			return IIO_VAL_INT_PLUS_MICRO; +		case IIO_TEMP: +			*val = 145; +			*val2 = 300000; /* 0.1453 C */ +			return IIO_VAL_INT_PLUS_MICRO; +		default: +			return -EINVAL; +		} +	case IIO_CHAN_INFO_OFFSET: +		*val = 250000 / 1453; /* 25 C = 0x00 */ +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_CALIBBIAS: +		addr = adis16260_addresses[chan->scan_index][0]; +		ret = adis_read_reg_16(adis, addr, &val16); +		if (ret) +			return ret; + +		*val = sign_extend32(val16, 11); +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_CALIBSCALE: +		addr = adis16260_addresses[chan->scan_index][1]; +		ret = adis_read_reg_16(adis, addr, &val16); +		if (ret) +			return ret; + +		*val = val16; +		return IIO_VAL_INT; +	} +	return -EINVAL; +} + +static int adis16260_write_raw(struct iio_dev *indio_dev, +			       struct iio_chan_spec const *chan, +			       int val, +			       int val2, +			       long mask) +{ +	struct adis *adis = iio_priv(indio_dev); +	u8 addr; + +	switch (mask) { +	case IIO_CHAN_INFO_CALIBBIAS: +		if (val < -2048 || val >= 2048) +			return -EINVAL; + +		addr = adis16260_addresses[chan->scan_index][0]; +		return adis_write_reg_16(adis, addr, val); +	case IIO_CHAN_INFO_CALIBSCALE: +		if (val < 0 || val >= 4096) +			return -EINVAL; + +		addr = adis16260_addresses[chan->scan_index][1]; +		return adis_write_reg_16(adis, addr, val); +	} +	return -EINVAL; +} + +static struct attribute *adis16260_attributes[] = { +	&iio_dev_attr_sampling_frequency.dev_attr.attr, +	NULL +}; + +static const struct attribute_group adis16260_attribute_group = { +	.attrs = adis16260_attributes, +}; + +static const struct iio_info adis16260_info = { +	.attrs = &adis16260_attribute_group, +	.read_raw = &adis16260_read_raw, +	.write_raw = &adis16260_write_raw, +	.update_scan_mode = adis_update_scan_mode, +	.driver_module = THIS_MODULE, +}; + +static const char * const adis1620_status_error_msgs[] = { +	[ADIS16260_DIAG_STAT_FLASH_CHK_BIT] = "Flash checksum error", +	[ADIS16260_DIAG_STAT_SELF_TEST_BIT] = "Self test error", +	[ADIS16260_DIAG_STAT_OVERFLOW_BIT] = "Sensor overrange", +	[ADIS16260_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure", +	[ADIS16260_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed", +	[ADIS16260_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 5.25", +	[ADIS16260_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 4.75", +}; + +static const struct adis_data adis16260_data = { +	.write_delay = 30, +	.read_delay = 30, +	.msc_ctrl_reg = ADIS16260_MSC_CTRL, +	.glob_cmd_reg = ADIS16260_GLOB_CMD, +	.diag_stat_reg = ADIS16260_DIAG_STAT, + +	.self_test_mask = ADIS16260_MSC_CTRL_MEM_TEST, +	.startup_delay = ADIS16260_STARTUP_DELAY, + +	.status_error_msgs = adis1620_status_error_msgs, +	.status_error_mask = BIT(ADIS16260_DIAG_STAT_FLASH_CHK_BIT) | +		BIT(ADIS16260_DIAG_STAT_SELF_TEST_BIT) | +		BIT(ADIS16260_DIAG_STAT_OVERFLOW_BIT) | +		BIT(ADIS16260_DIAG_STAT_SPI_FAIL_BIT) | +		BIT(ADIS16260_DIAG_STAT_FLASH_UPT_BIT) | +		BIT(ADIS16260_DIAG_STAT_POWER_HIGH_BIT) | +		BIT(ADIS16260_DIAG_STAT_POWER_LOW_BIT), +}; + +static int adis16260_probe(struct spi_device *spi) +{ +	struct iio_dev *indio_dev; +	struct adis *adis; +	int ret; + +	/* setup the industrialio driver allocated elements */ +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adis)); +	if (!indio_dev) +		return -ENOMEM; +	adis = iio_priv(indio_dev); +	/* this is only used for removal purposes */ +	spi_set_drvdata(spi, indio_dev); + +	indio_dev->name = spi_get_device_id(spi)->name; +	indio_dev->dev.parent = &spi->dev; +	indio_dev->info = &adis16260_info; +	indio_dev->channels = adis16260_channels; +	indio_dev->num_channels = ARRAY_SIZE(adis16260_channels); +	indio_dev->modes = INDIO_DIRECT_MODE; + +	ret = adis_init(adis, indio_dev, spi, &adis16260_data); +	if (ret) +		return ret; + +	ret = adis_setup_buffer_and_trigger(adis, indio_dev, NULL); +	if (ret) +		return ret; + +	/* Get the device into a sane initial state */ +	ret = adis_initial_startup(adis); +	if (ret) +		goto error_cleanup_buffer_trigger; +	ret = iio_device_register(indio_dev); +	if (ret) +		goto error_cleanup_buffer_trigger; + +	return 0; + +error_cleanup_buffer_trigger: +	adis_cleanup_buffer_and_trigger(adis, indio_dev); +	return ret; +} + +static int adis16260_remove(struct spi_device *spi) +{ +	struct iio_dev *indio_dev = spi_get_drvdata(spi); +	struct adis *adis = iio_priv(indio_dev); + +	iio_device_unregister(indio_dev); +	adis16260_stop_device(indio_dev); +	adis_cleanup_buffer_and_trigger(adis, indio_dev); + +	return 0; +} + +/* + * These parts do not need to be differentiated until someone adds + * support for the on chip filtering. + */ +static const struct spi_device_id adis16260_id[] = { +	{"adis16260", 0}, +	{"adis16265", 0}, +	{"adis16250", 0}, +	{"adis16255", 0}, +	{"adis16251", 1}, +	{} +}; +MODULE_DEVICE_TABLE(spi, adis16260_id); + +static struct spi_driver adis16260_driver = { +	.driver = { +		.name = "adis16260", +		.owner = THIS_MODULE, +	}, +	.probe = adis16260_probe, +	.remove = adis16260_remove, +	.id_table = adis16260_id, +}; +module_spi_driver(adis16260_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADIS16260/5 Digital Gyroscope Sensor"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/gyro/adxrs450.c b/drivers/iio/gyro/adxrs450.c new file mode 100644 index 00000000000..eb0e08ec9e2 --- /dev/null +++ b/drivers/iio/gyro/adxrs450.c @@ -0,0 +1,468 @@ +/* + * ADXRS450/ADXRS453 Digital Output Gyroscope Driver + * + * Copyright 2011 Analog Devices Inc. + * + * Licensed under the GPL-2. + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/list.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#define ADXRS450_STARTUP_DELAY	50 /* ms */ + +/* The MSB for the spi commands */ +#define ADXRS450_SENSOR_DATA    (0x20 << 24) +#define ADXRS450_WRITE_DATA	(0x40 << 24) +#define ADXRS450_READ_DATA	(0x80 << 24) + +#define ADXRS450_RATE1	0x00	/* Rate Registers */ +#define ADXRS450_TEMP1	0x02	/* Temperature Registers */ +#define ADXRS450_LOCST1	0x04	/* Low CST Memory Registers */ +#define ADXRS450_HICST1	0x06	/* High CST Memory Registers */ +#define ADXRS450_QUAD1	0x08	/* Quad Memory Registers */ +#define ADXRS450_FAULT1	0x0A	/* Fault Registers */ +#define ADXRS450_PID1	0x0C	/* Part ID Register 1 */ +#define ADXRS450_SNH	0x0E	/* Serial Number Registers, 4 bytes */ +#define ADXRS450_SNL	0x10 +#define ADXRS450_DNC1	0x12	/* Dynamic Null Correction Registers */ +/* Check bits */ +#define ADXRS450_P	0x01 +#define ADXRS450_CHK	0x02 +#define ADXRS450_CST	0x04 +#define ADXRS450_PWR	0x08 +#define ADXRS450_POR	0x10 +#define ADXRS450_NVM	0x20 +#define ADXRS450_Q	0x40 +#define ADXRS450_PLL	0x80 +#define ADXRS450_UV	0x100 +#define ADXRS450_OV	0x200 +#define ADXRS450_AMP	0x400 +#define ADXRS450_FAIL	0x800 + +#define ADXRS450_WRERR_MASK	(0x7 << 29) + +#define ADXRS450_MAX_RX 4 +#define ADXRS450_MAX_TX 4 + +#define ADXRS450_GET_ST(a)	((a >> 26) & 0x3) + +enum { +	ID_ADXRS450, +	ID_ADXRS453, +}; + +/** + * struct adxrs450_state - device instance specific data + * @us:			actual spi_device + * @buf_lock:		mutex to protect tx and rx + * @tx:			transmit buffer + * @rx:			receive buffer + **/ +struct adxrs450_state { +	struct spi_device	*us; +	struct mutex		buf_lock; +	__be32			tx ____cacheline_aligned; +	__be32			rx; + +}; + +/** + * adxrs450_spi_read_reg_16() - read 2 bytes from a register pair + * @indio_dev: device associated with child of actual iio_dev + * @reg_address: the address of the lower of the two registers, which should be + *	an even address, the second register's address is reg_address + 1. + * @val: somewhere to pass back the value read + **/ +static int adxrs450_spi_read_reg_16(struct iio_dev *indio_dev, +				    u8 reg_address, +				    u16 *val) +{ +	struct adxrs450_state *st = iio_priv(indio_dev); +	u32 tx; +	int ret; +	struct spi_transfer xfers[] = { +		{ +			.tx_buf = &st->tx, +			.bits_per_word = 8, +			.len = sizeof(st->tx), +			.cs_change = 1, +		}, { +			.rx_buf = &st->rx, +			.bits_per_word = 8, +			.len = sizeof(st->rx), +		}, +	}; + +	mutex_lock(&st->buf_lock); +	tx = ADXRS450_READ_DATA | (reg_address << 17); + +	if (!(hweight32(tx) & 1)) +		tx |= ADXRS450_P; + +	st->tx = cpu_to_be32(tx); +	ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers)); +	if (ret) { +		dev_err(&st->us->dev, "problem while reading 16 bit register 0x%02x\n", +				reg_address); +		goto error_ret; +	} + +	*val = (be32_to_cpu(st->rx) >> 5) & 0xFFFF; + +error_ret: +	mutex_unlock(&st->buf_lock); +	return ret; +} + +/** + * adxrs450_spi_write_reg_16() - write 2 bytes data to a register pair + * @indio_dev: device associated with child of actual actual iio_dev + * @reg_address: the address of the lower of the two registers,which should be + *	an even address, the second register's address is reg_address + 1. + * @val: value to be written. + **/ +static int adxrs450_spi_write_reg_16(struct iio_dev *indio_dev, +				     u8 reg_address, +				     u16 val) +{ +	struct adxrs450_state *st = iio_priv(indio_dev); +	u32 tx; +	int ret; + +	mutex_lock(&st->buf_lock); +	tx = ADXRS450_WRITE_DATA | (reg_address << 17) | (val << 1); + +	if (!(hweight32(tx) & 1)) +		tx |= ADXRS450_P; + +	st->tx = cpu_to_be32(tx); +	ret = spi_write(st->us, &st->tx, sizeof(st->tx)); +	if (ret) +		dev_err(&st->us->dev, "problem while writing 16 bit register 0x%02x\n", +			reg_address); +	usleep_range(100, 1000); /* enforce sequential transfer delay 0.1ms */ +	mutex_unlock(&st->buf_lock); +	return ret; +} + +/** + * adxrs450_spi_sensor_data() - read 2 bytes sensor data + * @indio_dev: device associated with child of actual iio_dev + * @val: somewhere to pass back the value read + **/ +static int adxrs450_spi_sensor_data(struct iio_dev *indio_dev, s16 *val) +{ +	struct adxrs450_state *st = iio_priv(indio_dev); +	int ret; +	struct spi_transfer xfers[] = { +		{ +			.tx_buf = &st->tx, +			.bits_per_word = 8, +			.len = sizeof(st->tx), +			.cs_change = 1, +		}, { +			.rx_buf = &st->rx, +			.bits_per_word = 8, +			.len = sizeof(st->rx), +		}, +	}; + +	mutex_lock(&st->buf_lock); +	st->tx = cpu_to_be32(ADXRS450_SENSOR_DATA); + +	ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers)); +	if (ret) { +		dev_err(&st->us->dev, "Problem while reading sensor data\n"); +		goto error_ret; +	} + +	*val = (be32_to_cpu(st->rx) >> 10) & 0xFFFF; + +error_ret: +	mutex_unlock(&st->buf_lock); +	return ret; +} + +/** + * adxrs450_spi_initial() - use for initializing procedure. + * @st: device instance specific data + * @val: somewhere to pass back the value read + * @chk: Whether to perform fault check + **/ +static int adxrs450_spi_initial(struct adxrs450_state *st, +		u32 *val, char chk) +{ +	int ret; +	u32 tx; +	struct spi_transfer xfers = { +		.tx_buf = &st->tx, +		.rx_buf = &st->rx, +		.bits_per_word = 8, +		.len = sizeof(st->tx), +	}; + +	mutex_lock(&st->buf_lock); +	tx = ADXRS450_SENSOR_DATA; +	if (chk) +		tx |= (ADXRS450_CHK | ADXRS450_P); +	st->tx = cpu_to_be32(tx); +	ret = spi_sync_transfer(st->us, &xfers, 1); +	if (ret) { +		dev_err(&st->us->dev, "Problem while reading initializing data\n"); +		goto error_ret; +	} + +	*val = be32_to_cpu(st->rx); + +error_ret: +	mutex_unlock(&st->buf_lock); +	return ret; +} + +/* Recommended Startup Sequence by spec */ +static int adxrs450_initial_setup(struct iio_dev *indio_dev) +{ +	u32 t; +	u16 data; +	int ret; +	struct adxrs450_state *st = iio_priv(indio_dev); + +	msleep(ADXRS450_STARTUP_DELAY*2); +	ret = adxrs450_spi_initial(st, &t, 1); +	if (ret) +		return ret; +	if (t != 0x01) +		dev_warn(&st->us->dev, "The initial power on response is not correct! Restart without reset?\n"); + +	msleep(ADXRS450_STARTUP_DELAY); +	ret = adxrs450_spi_initial(st, &t, 0); +	if (ret) +		return ret; + +	msleep(ADXRS450_STARTUP_DELAY); +	ret = adxrs450_spi_initial(st, &t, 0); +	if (ret) +		return ret; +	if (((t & 0xff) | 0x01) != 0xff || ADXRS450_GET_ST(t) != 2) { +		dev_err(&st->us->dev, "The second response is not correct!\n"); +		return -EIO; + +	} +	ret = adxrs450_spi_initial(st, &t, 0); +	if (ret) +		return ret; +	if (((t & 0xff) | 0x01) != 0xff || ADXRS450_GET_ST(t) != 2) { +		dev_err(&st->us->dev, "The third response is not correct!\n"); +		return -EIO; + +	} +	ret = adxrs450_spi_read_reg_16(indio_dev, ADXRS450_FAULT1, &data); +	if (ret) +		return ret; +	if (data & 0x0fff) { +		dev_err(&st->us->dev, "The device is not in normal status!\n"); +		return -EINVAL; +	} + +	return 0; +} + +static int adxrs450_write_raw(struct iio_dev *indio_dev, +			      struct iio_chan_spec const *chan, +			      int val, +			      int val2, +			      long mask) +{ +	int ret; +	switch (mask) { +	case IIO_CHAN_INFO_CALIBBIAS: +		if (val < -0x400 || val >= 0x400) +			return -EINVAL; +		ret = adxrs450_spi_write_reg_16(indio_dev, +						ADXRS450_DNC1, val); +		break; +	default: +		ret = -EINVAL; +		break; +	} +	return ret; +} + +static int adxrs450_read_raw(struct iio_dev *indio_dev, +			     struct iio_chan_spec const *chan, +			     int *val, +			     int *val2, +			     long mask) +{ +	int ret; +	s16 t; + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		switch (chan->type) { +		case IIO_ANGL_VEL: +			ret = adxrs450_spi_sensor_data(indio_dev, &t); +			if (ret) +				break; +			*val = t; +			ret = IIO_VAL_INT; +			break; +		case IIO_TEMP: +			ret = adxrs450_spi_read_reg_16(indio_dev, +						       ADXRS450_TEMP1, &t); +			if (ret) +				break; +			*val = (t >> 6) + 225; +			ret = IIO_VAL_INT; +			break; +		default: +			ret = -EINVAL; +			break; +		} +		break; +	case IIO_CHAN_INFO_SCALE: +		switch (chan->type) { +		case IIO_ANGL_VEL: +			*val = 0; +			*val2 = 218166; +			return IIO_VAL_INT_PLUS_NANO; +		case IIO_TEMP: +			*val = 200; +			*val2 = 0; +			return IIO_VAL_INT; +		default: +			return -EINVAL; +		} +	case IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW: +		ret = adxrs450_spi_read_reg_16(indio_dev, ADXRS450_QUAD1, &t); +		if (ret) +			break; +		*val = t; +		ret = IIO_VAL_INT; +		break; +	case IIO_CHAN_INFO_CALIBBIAS: +		ret = adxrs450_spi_read_reg_16(indio_dev, ADXRS450_DNC1, &t); +		if (ret) +			break; +		*val = sign_extend32(t, 9); +		ret = IIO_VAL_INT; +		break; +	default: +		ret = -EINVAL; +		break; +	} + +	return ret; +} + +static const struct iio_chan_spec adxrs450_channels[2][2] = { +	[ID_ADXRS450] = { +		{ +			.type = IIO_ANGL_VEL, +			.modified = 1, +			.channel2 = IIO_MOD_Z, +			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +			BIT(IIO_CHAN_INFO_CALIBBIAS) | +			BIT(IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW) | +			BIT(IIO_CHAN_INFO_SCALE), +		}, { +			.type = IIO_TEMP, +			.indexed = 1, +			.channel = 0, +			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +			BIT(IIO_CHAN_INFO_SCALE), +		} +	}, +	[ID_ADXRS453] = { +		{ +			.type = IIO_ANGL_VEL, +			.modified = 1, +			.channel2 = IIO_MOD_Z, +			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +			BIT(IIO_CHAN_INFO_SCALE) | +			BIT(IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW), +		}, { +			.type = IIO_TEMP, +			.indexed = 1, +			.channel = 0, +			.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +			BIT(IIO_CHAN_INFO_SCALE), +		} +	}, +}; + +static const struct iio_info adxrs450_info = { +	.driver_module = THIS_MODULE, +	.read_raw = &adxrs450_read_raw, +	.write_raw = &adxrs450_write_raw, +}; + +static int adxrs450_probe(struct spi_device *spi) +{ +	int ret; +	struct adxrs450_state *st; +	struct iio_dev *indio_dev; + +	/* setup the industrialio driver allocated elements */ +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); +	if (!indio_dev) +		return -ENOMEM; +	st = iio_priv(indio_dev); +	st->us = spi; +	mutex_init(&st->buf_lock); +	/* This is only used for removal purposes */ +	spi_set_drvdata(spi, indio_dev); + +	indio_dev->dev.parent = &spi->dev; +	indio_dev->info = &adxrs450_info; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->channels = +		adxrs450_channels[spi_get_device_id(spi)->driver_data]; +	indio_dev->num_channels = ARRAY_SIZE(adxrs450_channels); +	indio_dev->name = spi->dev.driver->name; + +	ret = devm_iio_device_register(&spi->dev, indio_dev); +	if (ret) +		return ret; + +	/* Get the device into a sane initial state */ +	ret = adxrs450_initial_setup(indio_dev); +	if (ret) +		return ret; + +	return 0; +} + +static const struct spi_device_id adxrs450_id[] = { +	{"adxrs450", ID_ADXRS450}, +	{"adxrs453", ID_ADXRS453}, +	{} +}; +MODULE_DEVICE_TABLE(spi, adxrs450_id); + +static struct spi_driver adxrs450_driver = { +	.driver = { +		.name = "adxrs450", +		.owner = THIS_MODULE, +	}, +	.probe = adxrs450_probe, +	.id_table	= adxrs450_id, +}; +module_spi_driver(adxrs450_driver); + +MODULE_AUTHOR("Cliff Cai <cliff.cai@xxxxxxxxxx>"); +MODULE_DESCRIPTION("Analog Devices ADXRS450/ADXRS453 Gyroscope SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c new file mode 100644 index 00000000000..fa034a3dad7 --- /dev/null +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -0,0 +1,428 @@ +/* + * HID Sensors Driver + * Copyright (c) 2012, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/hid-sensor-hub.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include "../common/hid-sensors/hid-sensor-trigger.h" + +enum gyro_3d_channel { +	CHANNEL_SCAN_INDEX_X, +	CHANNEL_SCAN_INDEX_Y, +	CHANNEL_SCAN_INDEX_Z, +	GYRO_3D_CHANNEL_MAX, +}; + +struct gyro_3d_state { +	struct hid_sensor_hub_callbacks callbacks; +	struct hid_sensor_common common_attributes; +	struct hid_sensor_hub_attribute_info gyro[GYRO_3D_CHANNEL_MAX]; +	u32 gyro_val[GYRO_3D_CHANNEL_MAX]; +	int scale_pre_decml; +	int scale_post_decml; +	int scale_precision; +	int value_offset; +}; + +static const u32 gyro_3d_addresses[GYRO_3D_CHANNEL_MAX] = { +	HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS, +	HID_USAGE_SENSOR_ANGL_VELOCITY_Y_AXIS, +	HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS +}; + +/* Channel definitions */ +static const struct iio_chan_spec gyro_3d_channels[] = { +	{ +		.type = IIO_ANGL_VEL, +		.modified = 1, +		.channel2 = IIO_MOD_X, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | +		BIT(IIO_CHAN_INFO_SCALE) | +		BIT(IIO_CHAN_INFO_SAMP_FREQ) | +		BIT(IIO_CHAN_INFO_HYSTERESIS), +		.scan_index = CHANNEL_SCAN_INDEX_X, +	}, { +		.type = IIO_ANGL_VEL, +		.modified = 1, +		.channel2 = IIO_MOD_Y, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | +		BIT(IIO_CHAN_INFO_SCALE) | +		BIT(IIO_CHAN_INFO_SAMP_FREQ) | +		BIT(IIO_CHAN_INFO_HYSTERESIS), +		.scan_index = CHANNEL_SCAN_INDEX_Y, +	}, { +		.type = IIO_ANGL_VEL, +		.modified = 1, +		.channel2 = IIO_MOD_Z, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | +		BIT(IIO_CHAN_INFO_SCALE) | +		BIT(IIO_CHAN_INFO_SAMP_FREQ) | +		BIT(IIO_CHAN_INFO_HYSTERESIS), +		.scan_index = CHANNEL_SCAN_INDEX_Z, +	} +}; + +/* Adjust channel real bits based on report descriptor */ +static void gyro_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels, +						int channel, int size) +{ +	channels[channel].scan_type.sign = 's'; +	/* Real storage bits will change based on the report desc. */ +	channels[channel].scan_type.realbits = size * 8; +	/* Maximum size of a sample to capture is u32 */ +	channels[channel].scan_type.storagebits = sizeof(u32) * 8; +} + +/* Channel read_raw handler */ +static int gyro_3d_read_raw(struct iio_dev *indio_dev, +			      struct iio_chan_spec const *chan, +			      int *val, int *val2, +			      long mask) +{ +	struct gyro_3d_state *gyro_state = iio_priv(indio_dev); +	int report_id = -1; +	u32 address; +	int ret_type; +	s32 poll_value; + +	*val = 0; +	*val2 = 0; +	switch (mask) { +	case 0: +		poll_value = hid_sensor_read_poll_value( +					&gyro_state->common_attributes); +		if (poll_value < 0) +			return -EINVAL; + +		hid_sensor_power_state(&gyro_state->common_attributes, true); +		msleep_interruptible(poll_value * 2); +		report_id = gyro_state->gyro[chan->scan_index].report_id; +		address = gyro_3d_addresses[chan->scan_index]; +		if (report_id >= 0) +			*val = sensor_hub_input_attr_get_raw_value( +					gyro_state->common_attributes.hsdev, +					HID_USAGE_SENSOR_GYRO_3D, address, +					report_id); +		else { +			*val = 0; +			hid_sensor_power_state(&gyro_state->common_attributes, +						false); +			return -EINVAL; +		} +		hid_sensor_power_state(&gyro_state->common_attributes, false); +		ret_type = IIO_VAL_INT; +		break; +	case IIO_CHAN_INFO_SCALE: +		*val = gyro_state->scale_pre_decml; +		*val2 = gyro_state->scale_post_decml; +		ret_type = gyro_state->scale_precision; +		break; +	case IIO_CHAN_INFO_OFFSET: +		*val = gyro_state->value_offset; +		ret_type = IIO_VAL_INT; +		break; +	case IIO_CHAN_INFO_SAMP_FREQ: +		ret_type = hid_sensor_read_samp_freq_value( +			&gyro_state->common_attributes, val, val2); +		break; +	case IIO_CHAN_INFO_HYSTERESIS: +		ret_type = hid_sensor_read_raw_hyst_value( +			&gyro_state->common_attributes, val, val2); +		break; +	default: +		ret_type = -EINVAL; +		break; +	} + +	return ret_type; +} + +/* Channel write_raw handler */ +static int gyro_3d_write_raw(struct iio_dev *indio_dev, +			       struct iio_chan_spec const *chan, +			       int val, +			       int val2, +			       long mask) +{ +	struct gyro_3d_state *gyro_state = iio_priv(indio_dev); +	int ret = 0; + +	switch (mask) { +	case IIO_CHAN_INFO_SAMP_FREQ: +		ret = hid_sensor_write_samp_freq_value( +				&gyro_state->common_attributes, val, val2); +		break; +	case IIO_CHAN_INFO_HYSTERESIS: +		ret = hid_sensor_write_raw_hyst_value( +				&gyro_state->common_attributes, val, val2); +		break; +	default: +		ret = -EINVAL; +	} + +	return ret; +} + +static const struct iio_info gyro_3d_info = { +	.driver_module = THIS_MODULE, +	.read_raw = &gyro_3d_read_raw, +	.write_raw = &gyro_3d_write_raw, +}; + +/* Function to push data to buffer */ +static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data, +	int len) +{ +	dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); +	iio_push_to_buffers(indio_dev, data); +} + +/* Callback handler to send event after all samples are received and captured */ +static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev, +				unsigned usage_id, +				void *priv) +{ +	struct iio_dev *indio_dev = platform_get_drvdata(priv); +	struct gyro_3d_state *gyro_state = iio_priv(indio_dev); + +	dev_dbg(&indio_dev->dev, "gyro_3d_proc_event\n"); +	if (atomic_read(&gyro_state->common_attributes.data_ready)) +		hid_sensor_push_data(indio_dev, +				gyro_state->gyro_val, +				sizeof(gyro_state->gyro_val)); + +	return 0; +} + +/* Capture samples in local storage */ +static int gyro_3d_capture_sample(struct hid_sensor_hub_device *hsdev, +				unsigned usage_id, +				size_t raw_len, char *raw_data, +				void *priv) +{ +	struct iio_dev *indio_dev = platform_get_drvdata(priv); +	struct gyro_3d_state *gyro_state = iio_priv(indio_dev); +	int offset; +	int ret = -EINVAL; + +	switch (usage_id) { +	case HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS: +	case HID_USAGE_SENSOR_ANGL_VELOCITY_Y_AXIS: +	case HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS: +		offset = usage_id - HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS; +		gyro_state->gyro_val[CHANNEL_SCAN_INDEX_X + offset] = +						*(u32 *)raw_data; +		ret = 0; +	break; +	default: +		break; +	} + +	return ret; +} + +/* Parse report which is specific to an usage id*/ +static int gyro_3d_parse_report(struct platform_device *pdev, +				struct hid_sensor_hub_device *hsdev, +				struct iio_chan_spec *channels, +				unsigned usage_id, +				struct gyro_3d_state *st) +{ +	int ret; +	int i; + +	for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) { +		ret = sensor_hub_input_get_attribute_info(hsdev, +				HID_INPUT_REPORT, +				usage_id, +				HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS + i, +				&st->gyro[CHANNEL_SCAN_INDEX_X + i]); +		if (ret < 0) +			break; +		gyro_3d_adjust_channel_bit_mask(channels, +				CHANNEL_SCAN_INDEX_X + i, +				st->gyro[CHANNEL_SCAN_INDEX_X + i].size); +	} +	dev_dbg(&pdev->dev, "gyro_3d %x:%x, %x:%x, %x:%x\n", +			st->gyro[0].index, +			st->gyro[0].report_id, +			st->gyro[1].index, st->gyro[1].report_id, +			st->gyro[2].index, st->gyro[2].report_id); + +	st->scale_precision = hid_sensor_format_scale( +				HID_USAGE_SENSOR_GYRO_3D, +				&st->gyro[CHANNEL_SCAN_INDEX_X], +				&st->scale_pre_decml, &st->scale_post_decml); + +	/* Set Sensitivity field ids, when there is no individual modifier */ +	if (st->common_attributes.sensitivity.index < 0) { +		sensor_hub_input_get_attribute_info(hsdev, +			HID_FEATURE_REPORT, usage_id, +			HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | +			HID_USAGE_SENSOR_DATA_ANGL_VELOCITY, +			&st->common_attributes.sensitivity); +		dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", +			st->common_attributes.sensitivity.index, +			st->common_attributes.sensitivity.report_id); +	} +	return ret; +} + +/* Function to initialize the processing for usage id */ +static int hid_gyro_3d_probe(struct platform_device *pdev) +{ +	int ret = 0; +	static const char *name = "gyro_3d"; +	struct iio_dev *indio_dev; +	struct gyro_3d_state *gyro_state; +	struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; +	struct iio_chan_spec *channels; + +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*gyro_state)); +	if (!indio_dev) +		return -ENOMEM; +	platform_set_drvdata(pdev, indio_dev); + +	gyro_state = iio_priv(indio_dev); +	gyro_state->common_attributes.hsdev = hsdev; +	gyro_state->common_attributes.pdev = pdev; + +	ret = hid_sensor_parse_common_attributes(hsdev, +						HID_USAGE_SENSOR_GYRO_3D, +						&gyro_state->common_attributes); +	if (ret) { +		dev_err(&pdev->dev, "failed to setup common attributes\n"); +		return ret; +	} + +	channels = kmemdup(gyro_3d_channels, sizeof(gyro_3d_channels), +			   GFP_KERNEL); +	if (!channels) { +		dev_err(&pdev->dev, "failed to duplicate channels\n"); +		return -ENOMEM; +	} + +	ret = gyro_3d_parse_report(pdev, hsdev, channels, +					HID_USAGE_SENSOR_GYRO_3D, gyro_state); +	if (ret) { +		dev_err(&pdev->dev, "failed to setup attributes\n"); +		goto error_free_dev_mem; +	} + +	indio_dev->channels = channels; +	indio_dev->num_channels = ARRAY_SIZE(gyro_3d_channels); +	indio_dev->dev.parent = &pdev->dev; +	indio_dev->info = &gyro_3d_info; +	indio_dev->name = name; +	indio_dev->modes = INDIO_DIRECT_MODE; + +	ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, +		NULL, NULL); +	if (ret) { +		dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); +		goto error_free_dev_mem; +	} +	atomic_set(&gyro_state->common_attributes.data_ready, 0); +	ret = hid_sensor_setup_trigger(indio_dev, name, +					&gyro_state->common_attributes); +	if (ret < 0) { +		dev_err(&pdev->dev, "trigger setup failed\n"); +		goto error_unreg_buffer_funcs; +	} + +	ret = iio_device_register(indio_dev); +	if (ret) { +		dev_err(&pdev->dev, "device register failed\n"); +		goto error_remove_trigger; +	} + +	gyro_state->callbacks.send_event = gyro_3d_proc_event; +	gyro_state->callbacks.capture_sample = gyro_3d_capture_sample; +	gyro_state->callbacks.pdev = pdev; +	ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D, +					&gyro_state->callbacks); +	if (ret < 0) { +		dev_err(&pdev->dev, "callback reg failed\n"); +		goto error_iio_unreg; +	} + +	return ret; + +error_iio_unreg: +	iio_device_unregister(indio_dev); +error_remove_trigger: +	hid_sensor_remove_trigger(&gyro_state->common_attributes); +error_unreg_buffer_funcs: +	iio_triggered_buffer_cleanup(indio_dev); +error_free_dev_mem: +	kfree(indio_dev->channels); +	return ret; +} + +/* Function to deinitialize the processing for usage id */ +static int hid_gyro_3d_remove(struct platform_device *pdev) +{ +	struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; +	struct iio_dev *indio_dev = platform_get_drvdata(pdev); +	struct gyro_3d_state *gyro_state = iio_priv(indio_dev); + +	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D); +	iio_device_unregister(indio_dev); +	hid_sensor_remove_trigger(&gyro_state->common_attributes); +	iio_triggered_buffer_cleanup(indio_dev); +	kfree(indio_dev->channels); + +	return 0; +} + +static struct platform_device_id hid_gyro_3d_ids[] = { +	{ +		/* Format: HID-SENSOR-usage_id_in_hex_lowercase */ +		.name = "HID-SENSOR-200076", +	}, +	{ /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, hid_gyro_3d_ids); + +static struct platform_driver hid_gyro_3d_platform_driver = { +	.id_table = hid_gyro_3d_ids, +	.driver = { +		.name	= KBUILD_MODNAME, +		.owner	= THIS_MODULE, +	}, +	.probe		= hid_gyro_3d_probe, +	.remove		= hid_gyro_3d_remove, +}; +module_platform_driver(hid_gyro_3d_platform_driver); + +MODULE_DESCRIPTION("HID Sensor Gyroscope 3D"); +MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/gyro/itg3200_buffer.c b/drivers/iio/gyro/itg3200_buffer.c new file mode 100644 index 00000000000..e3b3c508407 --- /dev/null +++ b/drivers/iio/gyro/itg3200_buffer.c @@ -0,0 +1,153 @@ +/* + * itg3200_buffer.c -- support InvenSense ITG3200 + *                     Digital 3-Axis Gyroscope driver + * + * Copyright (c) 2011 Christian Strobel <christian.strobel@iis.fraunhofer.de> + * Copyright (c) 2011 Manuel Stahl <manuel.stahl@iis.fraunhofer.de> + * Copyright (c) 2012 Thorsten Nowak <thorsten.nowak@iis.fraunhofer.de> + * + * 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. + */ + +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> + +#include <linux/iio/iio.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/gyro/itg3200.h> + + +static int itg3200_read_all_channels(struct i2c_client *i2c, __be16 *buf) +{ +	u8 tx = 0x80 | ITG3200_REG_TEMP_OUT_H; +	struct i2c_msg msg[2] = { +		{ +			.addr = i2c->addr, +			.flags = i2c->flags, +			.len = 1, +			.buf = &tx, +		}, +		{ +			.addr = i2c->addr, +			.flags = i2c->flags | I2C_M_RD, +			.len = ITG3200_SCAN_ELEMENTS * sizeof(s16), +			.buf = (char *)&buf, +		}, +	}; + +	return i2c_transfer(i2c->adapter, msg, 2); +} + +static irqreturn_t itg3200_trigger_handler(int irq, void *p) +{ +	struct iio_poll_func *pf = p; +	struct iio_dev *indio_dev = pf->indio_dev; +	struct itg3200 *st = iio_priv(indio_dev); +	__be16 buf[ITG3200_SCAN_ELEMENTS + sizeof(s64)/sizeof(u16)]; + +	int ret = itg3200_read_all_channels(st->i2c, buf); +	if (ret < 0) +		goto error_ret; + +	iio_push_to_buffers_with_timestamp(indio_dev, buf, pf->timestamp); + +	iio_trigger_notify_done(indio_dev->trig); + +error_ret: +	return IRQ_HANDLED; +} + +int itg3200_buffer_configure(struct iio_dev *indio_dev) +{ +	return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, +		itg3200_trigger_handler, NULL); +} + +void itg3200_buffer_unconfigure(struct iio_dev *indio_dev) +{ +	iio_triggered_buffer_cleanup(indio_dev); +} + + +static int itg3200_data_rdy_trigger_set_state(struct iio_trigger *trig, +		bool state) +{ +	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); +	int ret; +	u8 msc; + +	ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_IRQ_CONFIG, &msc); +	if (ret) +		goto error_ret; + +	if (state) +		msc |= ITG3200_IRQ_DATA_RDY_ENABLE; +	else +		msc &= ~ITG3200_IRQ_DATA_RDY_ENABLE; + +	ret = itg3200_write_reg_8(indio_dev, ITG3200_REG_IRQ_CONFIG, msc); +	if (ret) +		goto error_ret; + +error_ret: +	return ret; + +} + +static const struct iio_trigger_ops itg3200_trigger_ops = { +	.owner = THIS_MODULE, +	.set_trigger_state = &itg3200_data_rdy_trigger_set_state, +}; + +int itg3200_probe_trigger(struct iio_dev *indio_dev) +{ +	int ret; +	struct itg3200 *st = iio_priv(indio_dev); + +	st->trig = iio_trigger_alloc("%s-dev%d", indio_dev->name, +				     indio_dev->id); +	if (!st->trig) +		return -ENOMEM; + +	ret = request_irq(st->i2c->irq, +			  &iio_trigger_generic_data_rdy_poll, +			  IRQF_TRIGGER_RISING, +			  "itg3200_data_rdy", +			  st->trig); +	if (ret) +		goto error_free_trig; + + +	st->trig->dev.parent = &st->i2c->dev; +	st->trig->ops = &itg3200_trigger_ops; +	iio_trigger_set_drvdata(st->trig, indio_dev); +	ret = iio_trigger_register(st->trig); +	if (ret) +		goto error_free_irq; + +	/* select default trigger */ +	indio_dev->trig = st->trig; + +	return 0; + +error_free_irq: +	free_irq(st->i2c->irq, st->trig); +error_free_trig: +	iio_trigger_free(st->trig); +	return ret; +} + +void itg3200_remove_trigger(struct iio_dev *indio_dev) +{ +	struct itg3200 *st = iio_priv(indio_dev); + +	iio_trigger_unregister(st->trig); +	free_irq(st->i2c->irq, st->trig); +	iio_trigger_free(st->trig); +} diff --git a/drivers/iio/gyro/itg3200_core.c b/drivers/iio/gyro/itg3200_core.c new file mode 100644 index 00000000000..8295e318399 --- /dev/null +++ b/drivers/iio/gyro/itg3200_core.c @@ -0,0 +1,389 @@ +/* + * itg3200_core.c -- support InvenSense ITG3200 + *                   Digital 3-Axis Gyroscope driver + * + * Copyright (c) 2011 Christian Strobel <christian.strobel@iis.fraunhofer.de> + * Copyright (c) 2011 Manuel Stahl <manuel.stahl@iis.fraunhofer.de> + * Copyright (c) 2012 Thorsten Nowak <thorsten.nowak@iis.fraunhofer.de> + * + * 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. + * + * TODO: + * - Support digital low pass filter + * - Support power management + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/module.h> +#include <linux/delay.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> +#include <linux/iio/buffer.h> + +#include <linux/iio/gyro/itg3200.h> + + +int itg3200_write_reg_8(struct iio_dev *indio_dev, +		u8 reg_address, u8 val) +{ +	struct itg3200 *st = iio_priv(indio_dev); + +	return i2c_smbus_write_byte_data(st->i2c, 0x80 | reg_address, val); +} + +int itg3200_read_reg_8(struct iio_dev *indio_dev, +		u8 reg_address, u8 *val) +{ +	struct itg3200 *st = iio_priv(indio_dev); +	int ret; + +	ret = i2c_smbus_read_byte_data(st->i2c, reg_address); +	if (ret < 0) +		return ret; +	*val = ret; +	return 0; +} + +static int itg3200_read_reg_s16(struct iio_dev *indio_dev, u8 lower_reg_address, +		int *val) +{ +	struct itg3200 *st = iio_priv(indio_dev); +	struct i2c_client *client = st->i2c; +	int ret; +	s16 out; + +	struct i2c_msg msg[2] = { +		{ +			.addr = client->addr, +			.flags = client->flags, +			.len = 1, +			.buf = (char *)&lower_reg_address, +		}, +		{ +			.addr = client->addr, +			.flags = client->flags | I2C_M_RD, +			.len = 2, +			.buf = (char *)&out, +		}, +	}; + +	lower_reg_address |= 0x80; +	ret = i2c_transfer(client->adapter, msg, 2); +	be16_to_cpus(&out); +	*val = out; + +	return (ret == 2) ? 0 : ret; +} + +static int itg3200_read_raw(struct iio_dev *indio_dev, +		const struct iio_chan_spec *chan, +		int *val, int *val2, long info) +{ +	int ret = 0; +	u8 reg; + +	switch (info) { +	case IIO_CHAN_INFO_RAW: +		reg = (u8)chan->address; +		ret = itg3200_read_reg_s16(indio_dev, reg, val); +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_SCALE: +		*val = 0; +		if (chan->type == IIO_TEMP) +			*val2 = 1000000000/280; +		else +			*val2 = 1214142; /* (1 / 14,375) * (PI / 180) */ +		return IIO_VAL_INT_PLUS_NANO; +	case IIO_CHAN_INFO_OFFSET: +		/* Only the temperature channel has an offset */ +		*val = 23000; +		return IIO_VAL_INT; +	default: +		return -EINVAL; +	} +} + +static ssize_t itg3200_read_frequency(struct device *dev, +		struct device_attribute *attr, char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	int ret, sps; +	u8 val; + +	ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_DLPF, &val); +	if (ret) +		return ret; + +	sps = (val & ITG3200_DLPF_CFG_MASK) ? 1000 : 8000; + +	ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_SAMPLE_RATE_DIV, &val); +	if (ret) +		return ret; + +	sps /= val + 1; + +	return sprintf(buf, "%d\n", sps); +} + +static ssize_t itg3200_write_frequency(struct device *dev, +		struct device_attribute *attr, +		const char *buf, +		size_t len) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	unsigned val; +	int ret; +	u8 t; + +	ret = kstrtouint(buf, 10, &val); +	if (ret) +		return ret; + +	mutex_lock(&indio_dev->mlock); + +	ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_DLPF, &t); +	if (ret) +		goto err_ret; + +	if (val == 0) { +		ret = -EINVAL; +		goto err_ret; +	} +	t = ((t & ITG3200_DLPF_CFG_MASK) ? 1000u : 8000u) / val - 1; + +	ret = itg3200_write_reg_8(indio_dev, ITG3200_REG_SAMPLE_RATE_DIV, t); + +err_ret: +	mutex_unlock(&indio_dev->mlock); + +	return ret ? ret : len; +} + +/* + * Reset device and internal registers to the power-up-default settings + * Use the gyro clock as reference, as suggested by the datasheet + */ +static int itg3200_reset(struct iio_dev *indio_dev) +{ +	struct itg3200 *st = iio_priv(indio_dev); +	int ret; + +	dev_dbg(&st->i2c->dev, "reset device"); + +	ret = itg3200_write_reg_8(indio_dev, +			ITG3200_REG_POWER_MANAGEMENT, +			ITG3200_RESET); +	if (ret) { +		dev_err(&st->i2c->dev, "error resetting device"); +		goto error_ret; +	} + +	/* Wait for PLL (1ms according to datasheet) */ +	udelay(1500); + +	ret = itg3200_write_reg_8(indio_dev, +			ITG3200_REG_IRQ_CONFIG, +			ITG3200_IRQ_ACTIVE_HIGH | +			ITG3200_IRQ_PUSH_PULL | +			ITG3200_IRQ_LATCH_50US_PULSE | +			ITG3200_IRQ_LATCH_CLEAR_ANY); + +	if (ret) +		dev_err(&st->i2c->dev, "error init device"); + +error_ret: +	return ret; +} + +/* itg3200_enable_full_scale() - Disables the digital low pass filter */ +static int itg3200_enable_full_scale(struct iio_dev *indio_dev) +{ +	u8 val; +	int ret; + +	ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_DLPF, &val); +	if (ret) +		goto err_ret; + +	val |= ITG3200_DLPF_FS_SEL_2000; +	return itg3200_write_reg_8(indio_dev, ITG3200_REG_DLPF, val); + +err_ret: +	return ret; +} + +static int itg3200_initial_setup(struct iio_dev *indio_dev) +{ +	struct itg3200 *st = iio_priv(indio_dev); +	int ret; +	u8 val; + +	ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_ADDRESS, &val); +	if (ret) +		goto err_ret; + +	if (((val >> 1) & 0x3f) != 0x34) { +		dev_err(&st->i2c->dev, "invalid reg value 0x%02x", val); +		ret = -ENXIO; +		goto err_ret; +	} + +	ret = itg3200_reset(indio_dev); +	if (ret) +		goto err_ret; + +	ret = itg3200_enable_full_scale(indio_dev); +err_ret: +	return ret; +} + +#define ITG3200_ST						\ +	{ .sign = 's', .realbits = 16, .storagebits = 16, .endianness = IIO_BE } + +#define ITG3200_GYRO_CHAN(_mod) { \ +	.type = IIO_ANGL_VEL, \ +	.modified = 1, \ +	.channel2 = IIO_MOD_ ## _mod, \ +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ +	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +	.address = ITG3200_REG_GYRO_ ## _mod ## OUT_H, \ +	.scan_index = ITG3200_SCAN_GYRO_ ## _mod, \ +	.scan_type = ITG3200_ST, \ +} + +static const struct iio_chan_spec itg3200_channels[] = { +	{ +		.type = IIO_TEMP, +		.channel2 = IIO_NO_MOD, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | +		BIT(IIO_CHAN_INFO_SCALE), +		.address = ITG3200_REG_TEMP_OUT_H, +		.scan_index = ITG3200_SCAN_TEMP, +		.scan_type = ITG3200_ST, +	}, +	ITG3200_GYRO_CHAN(X), +	ITG3200_GYRO_CHAN(Y), +	ITG3200_GYRO_CHAN(Z), +	IIO_CHAN_SOFT_TIMESTAMP(ITG3200_SCAN_ELEMENTS), +}; + +/* IIO device attributes */ +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, itg3200_read_frequency, +		itg3200_write_frequency); + +static struct attribute *itg3200_attributes[] = { +	&iio_dev_attr_sampling_frequency.dev_attr.attr, +	NULL +}; + +static const struct attribute_group itg3200_attribute_group = { +	.attrs = itg3200_attributes, +}; + +static const struct iio_info itg3200_info = { +	.attrs = &itg3200_attribute_group, +	.read_raw = &itg3200_read_raw, +	.driver_module = THIS_MODULE, +}; + +static const unsigned long itg3200_available_scan_masks[] = { 0xffffffff, 0x0 }; + +static int itg3200_probe(struct i2c_client *client, +		const struct i2c_device_id *id) +{ +	int ret; +	struct itg3200 *st; +	struct iio_dev *indio_dev; + +	dev_dbg(&client->dev, "probe I2C dev with IRQ %i", client->irq); + +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st)); +	if (!indio_dev) +		return -ENOMEM; + +	st = iio_priv(indio_dev); + +	i2c_set_clientdata(client, indio_dev); +	st->i2c = client; + +	indio_dev->dev.parent = &client->dev; +	indio_dev->name = client->dev.driver->name; +	indio_dev->channels = itg3200_channels; +	indio_dev->num_channels = ARRAY_SIZE(itg3200_channels); +	indio_dev->available_scan_masks = itg3200_available_scan_masks; +	indio_dev->info = &itg3200_info; +	indio_dev->modes = INDIO_DIRECT_MODE; + +	ret = itg3200_buffer_configure(indio_dev); +	if (ret) +		return ret; + +	if (client->irq) { +		ret = itg3200_probe_trigger(indio_dev); +		if (ret) +			goto error_unconfigure_buffer; +	} + +	ret = itg3200_initial_setup(indio_dev); +	if (ret) +		goto error_remove_trigger; + +	ret = iio_device_register(indio_dev); +	if (ret) +		goto error_remove_trigger; + +	return 0; + +error_remove_trigger: +	if (client->irq) +		itg3200_remove_trigger(indio_dev); +error_unconfigure_buffer: +	itg3200_buffer_unconfigure(indio_dev); +	return ret; +} + +static int itg3200_remove(struct i2c_client *client) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(client); + +	iio_device_unregister(indio_dev); + +	if (client->irq) +		itg3200_remove_trigger(indio_dev); + +	itg3200_buffer_unconfigure(indio_dev); + +	return 0; +} + +static const struct i2c_device_id itg3200_id[] = { +	{ "itg3200", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, itg3200_id); + +static struct i2c_driver itg3200_driver = { +	.driver = { +		.owner  = THIS_MODULE, +		.name	= "itg3200", +	}, +	.id_table	= itg3200_id, +	.probe		= itg3200_probe, +	.remove		= itg3200_remove, +}; + +module_i2c_driver(itg3200_driver); + +MODULE_AUTHOR("Christian Strobel <christian.strobel@iis.fraunhofer.de>"); +MODULE_DESCRIPTION("ITG3200 Gyroscope I2C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/gyro/st_gyro.h b/drivers/iio/gyro/st_gyro.h new file mode 100644 index 00000000000..c197360c450 --- /dev/null +++ b/drivers/iio/gyro/st_gyro.h @@ -0,0 +1,53 @@ +/* + * STMicroelectronics gyroscopes driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * v. 1.0.0 + * Licensed under the GPL-2. + */ + +#ifndef ST_GYRO_H +#define ST_GYRO_H + +#include <linux/types.h> +#include <linux/iio/common/st_sensors.h> + +#define L3G4200D_GYRO_DEV_NAME		"l3g4200d" +#define LSM330D_GYRO_DEV_NAME		"lsm330d_gyro" +#define LSM330DL_GYRO_DEV_NAME		"lsm330dl_gyro" +#define LSM330DLC_GYRO_DEV_NAME		"lsm330dlc_gyro" +#define L3GD20_GYRO_DEV_NAME		"l3gd20" +#define L3G4IS_GYRO_DEV_NAME		"l3g4is_ui" +#define LSM330_GYRO_DEV_NAME		"lsm330_gyro" + +/** + * struct st_sensors_platform_data - gyro platform data + * @drdy_int_pin: DRDY on gyros is available only on INT2 pin. + */ +static const struct st_sensors_platform_data gyro_pdata = { +	.drdy_int_pin = 2, +}; + +int st_gyro_common_probe(struct iio_dev *indio_dev, +					struct st_sensors_platform_data *pdata); +void st_gyro_common_remove(struct iio_dev *indio_dev); + +#ifdef CONFIG_IIO_BUFFER +int st_gyro_allocate_ring(struct iio_dev *indio_dev); +void st_gyro_deallocate_ring(struct iio_dev *indio_dev); +int st_gyro_trig_set_state(struct iio_trigger *trig, bool state); +#define ST_GYRO_TRIGGER_SET_STATE (&st_gyro_trig_set_state) +#else /* CONFIG_IIO_BUFFER */ +static inline int st_gyro_allocate_ring(struct iio_dev *indio_dev) +{ +	return 0; +} +static inline void st_gyro_deallocate_ring(struct iio_dev *indio_dev) +{ +} +#define ST_GYRO_TRIGGER_SET_STATE NULL +#endif /* CONFIG_IIO_BUFFER */ + +#endif /* ST_GYRO_H */ diff --git a/drivers/iio/gyro/st_gyro_buffer.c b/drivers/iio/gyro/st_gyro_buffer.c new file mode 100644 index 00000000000..d67b17b6a7a --- /dev/null +++ b/drivers/iio/gyro/st_gyro_buffer.c @@ -0,0 +1,105 @@ +/* + * STMicroelectronics gyroscopes driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * Licensed under the GPL-2. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/iio/iio.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#include <linux/iio/common/st_sensors.h> +#include "st_gyro.h" + +int st_gyro_trig_set_state(struct iio_trigger *trig, bool state) +{ +	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + +	return st_sensors_set_dataready_irq(indio_dev, state); +} + +static int st_gyro_buffer_preenable(struct iio_dev *indio_dev) +{ +	return st_sensors_set_enable(indio_dev, true); +} + +static int st_gyro_buffer_postenable(struct iio_dev *indio_dev) +{ +	int err; +	struct st_sensor_data *gdata = iio_priv(indio_dev); + +	gdata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); +	if (gdata->buffer_data == NULL) { +		err = -ENOMEM; +		goto allocate_memory_error; +	} + +	err = st_sensors_set_axis_enable(indio_dev, +					(u8)indio_dev->active_scan_mask[0]); +	if (err < 0) +		goto st_gyro_buffer_postenable_error; + +	err = iio_triggered_buffer_postenable(indio_dev); +	if (err < 0) +		goto st_gyro_buffer_postenable_error; + +	return err; + +st_gyro_buffer_postenable_error: +	kfree(gdata->buffer_data); +allocate_memory_error: +	return err; +} + +static int st_gyro_buffer_predisable(struct iio_dev *indio_dev) +{ +	int err; +	struct st_sensor_data *gdata = iio_priv(indio_dev); + +	err = iio_triggered_buffer_predisable(indio_dev); +	if (err < 0) +		goto st_gyro_buffer_predisable_error; + +	err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS); +	if (err < 0) +		goto st_gyro_buffer_predisable_error; + +	err = st_sensors_set_enable(indio_dev, false); + +st_gyro_buffer_predisable_error: +	kfree(gdata->buffer_data); +	return err; +} + +static const struct iio_buffer_setup_ops st_gyro_buffer_setup_ops = { +	.preenable = &st_gyro_buffer_preenable, +	.postenable = &st_gyro_buffer_postenable, +	.predisable = &st_gyro_buffer_predisable, +}; + +int st_gyro_allocate_ring(struct iio_dev *indio_dev) +{ +	return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, +		&st_sensors_trigger_handler, &st_gyro_buffer_setup_ops); +} + +void st_gyro_deallocate_ring(struct iio_dev *indio_dev) +{ +	iio_triggered_buffer_cleanup(indio_dev); +} + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics gyroscopes buffer"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c new file mode 100644 index 00000000000..ed74a906998 --- /dev/null +++ b/drivers/iio/gyro/st_gyro_core.c @@ -0,0 +1,380 @@ +/* + * STMicroelectronics gyroscopes driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * Licensed under the GPL-2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/mutex.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/irq.h> +#include <linux/delay.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> +#include <linux/iio/buffer.h> + +#include <linux/iio/common/st_sensors.h> +#include "st_gyro.h" + +#define ST_GYRO_NUMBER_DATA_CHANNELS		3 + +/* DEFAULT VALUE FOR SENSORS */ +#define ST_GYRO_DEFAULT_OUT_X_L_ADDR		0x28 +#define ST_GYRO_DEFAULT_OUT_Y_L_ADDR		0x2a +#define ST_GYRO_DEFAULT_OUT_Z_L_ADDR		0x2c + +/* FULLSCALE */ +#define ST_GYRO_FS_AVL_250DPS			250 +#define ST_GYRO_FS_AVL_500DPS			500 +#define ST_GYRO_FS_AVL_2000DPS			2000 + +/* CUSTOM VALUES FOR SENSOR 1 */ +#define ST_GYRO_1_WAI_EXP			0xd3 +#define ST_GYRO_1_ODR_ADDR			0x20 +#define ST_GYRO_1_ODR_MASK			0xc0 +#define ST_GYRO_1_ODR_AVL_100HZ_VAL		0x00 +#define ST_GYRO_1_ODR_AVL_200HZ_VAL		0x01 +#define ST_GYRO_1_ODR_AVL_400HZ_VAL		0x02 +#define ST_GYRO_1_ODR_AVL_800HZ_VAL		0x03 +#define ST_GYRO_1_PW_ADDR			0x20 +#define ST_GYRO_1_PW_MASK			0x08 +#define ST_GYRO_1_FS_ADDR			0x23 +#define ST_GYRO_1_FS_MASK			0x30 +#define ST_GYRO_1_FS_AVL_250_VAL		0x00 +#define ST_GYRO_1_FS_AVL_500_VAL		0x01 +#define ST_GYRO_1_FS_AVL_2000_VAL		0x02 +#define ST_GYRO_1_FS_AVL_250_GAIN		IIO_DEGREE_TO_RAD(8750) +#define ST_GYRO_1_FS_AVL_500_GAIN		IIO_DEGREE_TO_RAD(17500) +#define ST_GYRO_1_FS_AVL_2000_GAIN		IIO_DEGREE_TO_RAD(70000) +#define ST_GYRO_1_BDU_ADDR			0x23 +#define ST_GYRO_1_BDU_MASK			0x80 +#define ST_GYRO_1_DRDY_IRQ_ADDR			0x22 +#define ST_GYRO_1_DRDY_IRQ_INT2_MASK		0x08 +#define ST_GYRO_1_MULTIREAD_BIT			true + +/* CUSTOM VALUES FOR SENSOR 2 */ +#define ST_GYRO_2_WAI_EXP			0xd4 +#define ST_GYRO_2_ODR_ADDR			0x20 +#define ST_GYRO_2_ODR_MASK			0xc0 +#define ST_GYRO_2_ODR_AVL_95HZ_VAL		0x00 +#define ST_GYRO_2_ODR_AVL_190HZ_VAL		0x01 +#define ST_GYRO_2_ODR_AVL_380HZ_VAL		0x02 +#define ST_GYRO_2_ODR_AVL_760HZ_VAL		0x03 +#define ST_GYRO_2_PW_ADDR			0x20 +#define ST_GYRO_2_PW_MASK			0x08 +#define ST_GYRO_2_FS_ADDR			0x23 +#define ST_GYRO_2_FS_MASK			0x30 +#define ST_GYRO_2_FS_AVL_250_VAL		0x00 +#define ST_GYRO_2_FS_AVL_500_VAL		0x01 +#define ST_GYRO_2_FS_AVL_2000_VAL		0x02 +#define ST_GYRO_2_FS_AVL_250_GAIN		IIO_DEGREE_TO_RAD(8750) +#define ST_GYRO_2_FS_AVL_500_GAIN		IIO_DEGREE_TO_RAD(17500) +#define ST_GYRO_2_FS_AVL_2000_GAIN		IIO_DEGREE_TO_RAD(70000) +#define ST_GYRO_2_BDU_ADDR			0x23 +#define ST_GYRO_2_BDU_MASK			0x80 +#define ST_GYRO_2_DRDY_IRQ_ADDR			0x22 +#define ST_GYRO_2_DRDY_IRQ_INT2_MASK		0x08 +#define ST_GYRO_2_MULTIREAD_BIT			true + +static const struct iio_chan_spec st_gyro_16bit_channels[] = { +	ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, +			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), +			ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16, +			ST_GYRO_DEFAULT_OUT_X_L_ADDR), +	ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, +			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), +			ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16, +			ST_GYRO_DEFAULT_OUT_Y_L_ADDR), +	ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, +			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), +			ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16, +			ST_GYRO_DEFAULT_OUT_Z_L_ADDR), +	IIO_CHAN_SOFT_TIMESTAMP(3) +}; + +static const struct st_sensors st_gyro_sensors[] = { +	{ +		.wai = ST_GYRO_1_WAI_EXP, +		.sensors_supported = { +			[0] = L3G4200D_GYRO_DEV_NAME, +			[1] = LSM330DL_GYRO_DEV_NAME, +		}, +		.ch = (struct iio_chan_spec *)st_gyro_16bit_channels, +		.odr = { +			.addr = ST_GYRO_1_ODR_ADDR, +			.mask = ST_GYRO_1_ODR_MASK, +			.odr_avl = { +				{ 100, ST_GYRO_1_ODR_AVL_100HZ_VAL, }, +				{ 200, ST_GYRO_1_ODR_AVL_200HZ_VAL, }, +				{ 400, ST_GYRO_1_ODR_AVL_400HZ_VAL, }, +				{ 800, ST_GYRO_1_ODR_AVL_800HZ_VAL, }, +			}, +		}, +		.pw = { +			.addr = ST_GYRO_1_PW_ADDR, +			.mask = ST_GYRO_1_PW_MASK, +			.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE, +			.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, +		}, +		.enable_axis = { +			.addr = ST_SENSORS_DEFAULT_AXIS_ADDR, +			.mask = ST_SENSORS_DEFAULT_AXIS_MASK, +		}, +		.fs = { +			.addr = ST_GYRO_1_FS_ADDR, +			.mask = ST_GYRO_1_FS_MASK, +			.fs_avl = { +				[0] = { +					.num = ST_GYRO_FS_AVL_250DPS, +					.value = ST_GYRO_1_FS_AVL_250_VAL, +					.gain = ST_GYRO_1_FS_AVL_250_GAIN, +				}, +				[1] = { +					.num = ST_GYRO_FS_AVL_500DPS, +					.value = ST_GYRO_1_FS_AVL_500_VAL, +					.gain = ST_GYRO_1_FS_AVL_500_GAIN, +				}, +				[2] = { +					.num = ST_GYRO_FS_AVL_2000DPS, +					.value = ST_GYRO_1_FS_AVL_2000_VAL, +					.gain = ST_GYRO_1_FS_AVL_2000_GAIN, +				}, +			}, +		}, +		.bdu = { +			.addr = ST_GYRO_1_BDU_ADDR, +			.mask = ST_GYRO_1_BDU_MASK, +		}, +		.drdy_irq = { +			.addr = ST_GYRO_1_DRDY_IRQ_ADDR, +			.mask_int2 = ST_GYRO_1_DRDY_IRQ_INT2_MASK, +		}, +		.multi_read_bit = ST_GYRO_1_MULTIREAD_BIT, +		.bootime = 2, +	}, +	{ +		.wai = ST_GYRO_2_WAI_EXP, +		.sensors_supported = { +			[0] = L3GD20_GYRO_DEV_NAME, +			[1] = LSM330D_GYRO_DEV_NAME, +			[2] = LSM330DLC_GYRO_DEV_NAME, +			[3] = L3G4IS_GYRO_DEV_NAME, +			[4] = LSM330_GYRO_DEV_NAME, +		}, +		.ch = (struct iio_chan_spec *)st_gyro_16bit_channels, +		.odr = { +			.addr = ST_GYRO_2_ODR_ADDR, +			.mask = ST_GYRO_2_ODR_MASK, +			.odr_avl = { +				{ 95, ST_GYRO_2_ODR_AVL_95HZ_VAL, }, +				{ 190, ST_GYRO_2_ODR_AVL_190HZ_VAL, }, +				{ 380, ST_GYRO_2_ODR_AVL_380HZ_VAL, }, +				{ 760, ST_GYRO_2_ODR_AVL_760HZ_VAL, }, +			}, +		}, +		.pw = { +			.addr = ST_GYRO_2_PW_ADDR, +			.mask = ST_GYRO_2_PW_MASK, +			.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE, +			.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, +		}, +		.enable_axis = { +			.addr = ST_SENSORS_DEFAULT_AXIS_ADDR, +			.mask = ST_SENSORS_DEFAULT_AXIS_MASK, +		}, +		.fs = { +			.addr = ST_GYRO_2_FS_ADDR, +			.mask = ST_GYRO_2_FS_MASK, +			.fs_avl = { +				[0] = { +					.num = ST_GYRO_FS_AVL_250DPS, +					.value = ST_GYRO_2_FS_AVL_250_VAL, +					.gain = ST_GYRO_2_FS_AVL_250_GAIN, +				}, +				[1] = { +					.num = ST_GYRO_FS_AVL_500DPS, +					.value = ST_GYRO_2_FS_AVL_500_VAL, +					.gain = ST_GYRO_2_FS_AVL_500_GAIN, +				}, +				[2] = { +					.num = ST_GYRO_FS_AVL_2000DPS, +					.value = ST_GYRO_2_FS_AVL_2000_VAL, +					.gain = ST_GYRO_2_FS_AVL_2000_GAIN, +				}, +			}, +		}, +		.bdu = { +			.addr = ST_GYRO_2_BDU_ADDR, +			.mask = ST_GYRO_2_BDU_MASK, +		}, +		.drdy_irq = { +			.addr = ST_GYRO_2_DRDY_IRQ_ADDR, +			.mask_int2 = ST_GYRO_2_DRDY_IRQ_INT2_MASK, +		}, +		.multi_read_bit = ST_GYRO_2_MULTIREAD_BIT, +		.bootime = 2, +	}, +}; + +static int st_gyro_read_raw(struct iio_dev *indio_dev, +			struct iio_chan_spec const *ch, int *val, +							int *val2, long mask) +{ +	int err; +	struct st_sensor_data *gdata = iio_priv(indio_dev); + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		err = st_sensors_read_info_raw(indio_dev, ch, val); +		if (err < 0) +			goto read_error; + +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_SCALE: +		*val = 0; +		*val2 = gdata->current_fullscale->gain; +		return IIO_VAL_INT_PLUS_MICRO; +	default: +		return -EINVAL; +	} + +read_error: +	return err; +} + +static int st_gyro_write_raw(struct iio_dev *indio_dev, +		struct iio_chan_spec const *chan, int val, int val2, long mask) +{ +	int err; + +	switch (mask) { +	case IIO_CHAN_INFO_SCALE: +		err = st_sensors_set_fullscale_by_gain(indio_dev, val2); +		break; +	default: +		err = -EINVAL; +	} + +	return err; +} + +static ST_SENSOR_DEV_ATTR_SAMP_FREQ(); +static ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL(); +static ST_SENSORS_DEV_ATTR_SCALE_AVAIL(in_anglvel_scale_available); + +static struct attribute *st_gyro_attributes[] = { +	&iio_dev_attr_sampling_frequency_available.dev_attr.attr, +	&iio_dev_attr_in_anglvel_scale_available.dev_attr.attr, +	&iio_dev_attr_sampling_frequency.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group st_gyro_attribute_group = { +	.attrs = st_gyro_attributes, +}; + +static const struct iio_info gyro_info = { +	.driver_module = THIS_MODULE, +	.attrs = &st_gyro_attribute_group, +	.read_raw = &st_gyro_read_raw, +	.write_raw = &st_gyro_write_raw, +}; + +#ifdef CONFIG_IIO_TRIGGER +static const struct iio_trigger_ops st_gyro_trigger_ops = { +	.owner = THIS_MODULE, +	.set_trigger_state = ST_GYRO_TRIGGER_SET_STATE, +}; +#define ST_GYRO_TRIGGER_OPS (&st_gyro_trigger_ops) +#else +#define ST_GYRO_TRIGGER_OPS NULL +#endif + +int st_gyro_common_probe(struct iio_dev *indio_dev, +					struct st_sensors_platform_data *pdata) +{ +	struct st_sensor_data *gdata = iio_priv(indio_dev); +	int irq = gdata->get_irq_data_ready(indio_dev); +	int err; + +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->info = &gyro_info; + +	st_sensors_power_enable(indio_dev); + +	err = st_sensors_check_device_support(indio_dev, +				ARRAY_SIZE(st_gyro_sensors), st_gyro_sensors); +	if (err < 0) +		return err; + +	gdata->num_data_channels = ST_GYRO_NUMBER_DATA_CHANNELS; +	gdata->multiread_bit = gdata->sensor->multi_read_bit; +	indio_dev->channels = gdata->sensor->ch; +	indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS; + +	gdata->current_fullscale = (struct st_sensor_fullscale_avl *) +						&gdata->sensor->fs.fs_avl[0]; +	gdata->odr = gdata->sensor->odr.odr_avl[0].hz; + +	err = st_sensors_init_sensor(indio_dev, pdata); +	if (err < 0) +		return err; + +	err = st_gyro_allocate_ring(indio_dev); +	if (err < 0) +		return err; + +	if (irq > 0) { +		err = st_sensors_allocate_trigger(indio_dev, +						  ST_GYRO_TRIGGER_OPS); +		if (err < 0) +			goto st_gyro_probe_trigger_error; +	} + +	err = iio_device_register(indio_dev); +	if (err) +		goto st_gyro_device_register_error; + +	dev_info(&indio_dev->dev, "registered gyroscope %s\n", +		 indio_dev->name); + +	return 0; + +st_gyro_device_register_error: +	if (irq > 0) +		st_sensors_deallocate_trigger(indio_dev); +st_gyro_probe_trigger_error: +	st_gyro_deallocate_ring(indio_dev); + +	return err; +} +EXPORT_SYMBOL(st_gyro_common_probe); + +void st_gyro_common_remove(struct iio_dev *indio_dev) +{ +	struct st_sensor_data *gdata = iio_priv(indio_dev); + +	st_sensors_power_disable(indio_dev); + +	iio_device_unregister(indio_dev); +	if (gdata->get_irq_data_ready(indio_dev) > 0) +		st_sensors_deallocate_trigger(indio_dev); + +	st_gyro_deallocate_ring(indio_dev); +} +EXPORT_SYMBOL(st_gyro_common_remove); + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics gyroscopes driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/gyro/st_gyro_i2c.c b/drivers/iio/gyro/st_gyro_i2c.c new file mode 100644 index 00000000000..23c12f361b0 --- /dev/null +++ b/drivers/iio/gyro/st_gyro_i2c.c @@ -0,0 +1,77 @@ +/* + * STMicroelectronics gyroscopes driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * Licensed under the GPL-2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/iio/iio.h> + +#include <linux/iio/common/st_sensors.h> +#include <linux/iio/common/st_sensors_i2c.h> +#include "st_gyro.h" + +static int st_gyro_i2c_probe(struct i2c_client *client, +						const struct i2c_device_id *id) +{ +	struct iio_dev *indio_dev; +	struct st_sensor_data *gdata; +	int err; + +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*gdata)); +	if (!indio_dev) +		return -ENOMEM; + +	gdata = iio_priv(indio_dev); +	gdata->dev = &client->dev; + +	st_sensors_i2c_configure(indio_dev, client, gdata); + +	err = st_gyro_common_probe(indio_dev, +				(struct st_sensors_platform_data *)&gyro_pdata); +	if (err < 0) +		return err; + +	return 0; +} + +static int st_gyro_i2c_remove(struct i2c_client *client) +{ +	st_gyro_common_remove(i2c_get_clientdata(client)); + +	return 0; +} + +static const struct i2c_device_id st_gyro_id_table[] = { +	{ L3G4200D_GYRO_DEV_NAME }, +	{ LSM330D_GYRO_DEV_NAME }, +	{ LSM330DL_GYRO_DEV_NAME }, +	{ LSM330DLC_GYRO_DEV_NAME }, +	{ L3GD20_GYRO_DEV_NAME }, +	{ L3G4IS_GYRO_DEV_NAME }, +	{ LSM330_GYRO_DEV_NAME }, +	{}, +}; +MODULE_DEVICE_TABLE(i2c, st_gyro_id_table); + +static struct i2c_driver st_gyro_driver = { +	.driver = { +		.owner = THIS_MODULE, +		.name = "st-gyro-i2c", +	}, +	.probe = st_gyro_i2c_probe, +	.remove = st_gyro_i2c_remove, +	.id_table = st_gyro_id_table, +}; +module_i2c_driver(st_gyro_driver); + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics gyroscopes i2c driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/gyro/st_gyro_spi.c b/drivers/iio/gyro/st_gyro_spi.c new file mode 100644 index 00000000000..b4ad3be2668 --- /dev/null +++ b/drivers/iio/gyro/st_gyro_spi.c @@ -0,0 +1,76 @@ +/* + * STMicroelectronics gyroscopes driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca <denis.ciocca@st.com> + * + * Licensed under the GPL-2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/iio/iio.h> + +#include <linux/iio/common/st_sensors.h> +#include <linux/iio/common/st_sensors_spi.h> +#include "st_gyro.h" + +static int st_gyro_spi_probe(struct spi_device *spi) +{ +	struct iio_dev *indio_dev; +	struct st_sensor_data *gdata; +	int err; + +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*gdata)); +	if (!indio_dev) +		return -ENOMEM; + +	gdata = iio_priv(indio_dev); +	gdata->dev = &spi->dev; + +	st_sensors_spi_configure(indio_dev, spi, gdata); + +	err = st_gyro_common_probe(indio_dev, +				(struct st_sensors_platform_data *)&gyro_pdata); +	if (err < 0) +		return err; + +	return 0; +} + +static int st_gyro_spi_remove(struct spi_device *spi) +{ +	st_gyro_common_remove(spi_get_drvdata(spi)); + +	return 0; +} + +static const struct spi_device_id st_gyro_id_table[] = { +	{ L3G4200D_GYRO_DEV_NAME }, +	{ LSM330D_GYRO_DEV_NAME }, +	{ LSM330DL_GYRO_DEV_NAME }, +	{ LSM330DLC_GYRO_DEV_NAME }, +	{ L3GD20_GYRO_DEV_NAME }, +	{ L3G4IS_GYRO_DEV_NAME }, +	{ LSM330_GYRO_DEV_NAME }, +	{}, +}; +MODULE_DEVICE_TABLE(spi, st_gyro_id_table); + +static struct spi_driver st_gyro_driver = { +	.driver = { +		.owner = THIS_MODULE, +		.name = "st-gyro-spi", +	}, +	.probe = st_gyro_spi_probe, +	.remove = st_gyro_spi_remove, +	.id_table = st_gyro_id_table, +}; +module_spi_driver(st_gyro_driver); + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics gyroscopes spi driver"); +MODULE_LICENSE("GPL v2");  | 
