mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-09 01:28:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
112
drivers/iio/gyro/Kconfig
Normal file
112
drivers/iio/gyro/Kconfig
Normal file
|
@ -0,0 +1,112 @@
|
|||
#
|
||||
# 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 BMG160
|
||||
tristate "BOSCH BMG160 Gyro Sensor"
|
||||
depends on I2C
|
||||
select IIO_TRIGGERED_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say yes here to build support for Bosch BMG160 Tri-axis Gyro Sensor
|
||||
driver. This driver also supports BMI055 gyroscope.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called bmg160.
|
||||
|
||||
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
|
24
drivers/iio/gyro/Makefile
Normal file
24
drivers/iio/gyro/Makefile
Normal file
|
@ -0,0 +1,24 @@
|
|||
#
|
||||
# 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_BMG160) += bmg160.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
|
241
drivers/iio/gyro/adis16080.c
Normal file
241
drivers/iio/gyro/adis16080.c
Normal file
|
@ -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");
|
179
drivers/iio/gyro/adis16130.c
Normal file
179
drivers/iio/gyro/adis16130.c
Normal file
|
@ -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");
|
577
drivers/iio/gyro/adis16136.c
Normal file
577
drivers/iio/gyro/adis16136.c
Normal file
|
@ -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");
|
389
drivers/iio/gyro/adis16260.c
Normal file
389
drivers/iio/gyro/adis16260.c
Normal file
|
@ -0,0 +1,389 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
/* 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 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),
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), 14),
|
||||
ADIS_INCLI_CHAN(X, ADIS16260_ANGL_OUT, ADIS16260_SCAN_ANGL, 0,
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), 14),
|
||||
ADIS_TEMP_CHAN(ADIS16260_TEMP_OUT, ADIS16260_SCAN_TEMP,
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), 12),
|
||||
ADIS_SUPPLY_CHAN(ADIS16260_SUPPLY_OUT, ADIS16260_SCAN_SUPPLY,
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), 12),
|
||||
ADIS_AUX_ADC_CHAN(ADIS16260_AUX_ADC, ADIS16260_SCAN_AUX_ADC,
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), 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;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = adis_read_reg_16(adis, ADIS16260_SMPL_PRD, &val16);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (spi_get_device_id(adis->spi)->driver_data)
|
||||
/* If an adis16251 */
|
||||
*val = (val16 & ADIS16260_SMPL_PRD_TIME_BASE) ?
|
||||
8 : 256;
|
||||
else
|
||||
*val = (val16 & ADIS16260_SMPL_PRD_TIME_BASE) ?
|
||||
66 : 2048;
|
||||
*val /= (val16 & ADIS16260_SMPL_PRD_DIV_MASK) + 1;
|
||||
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);
|
||||
int ret;
|
||||
u8 addr;
|
||||
u8 t;
|
||||
|
||||
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);
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
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;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info adis16260_info = {
|
||||
.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");
|
468
drivers/iio/gyro/adxrs450.c
Normal file
468
drivers/iio/gyro/adxrs450.c
Normal file
|
@ -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");
|
1273
drivers/iio/gyro/bmg160.c
Normal file
1273
drivers/iio/gyro/bmg160.c
Normal file
File diff suppressed because it is too large
Load diff
427
drivers/iio/gyro/hid-sensor-gyro-3d.c
Normal file
427
drivers/iio/gyro/hid-sensor-gyro-3d.c
Normal file
|
@ -0,0 +1,427 @@
|
|||
/*
|
||||
* 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,
|
||||
},
|
||||
.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");
|
153
drivers/iio/gyro/itg3200_buffer.c
Normal file
153
drivers/iio/gyro/itg3200_buffer.c
Normal file
|
@ -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 = iio_trigger_get(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);
|
||||
}
|
374
drivers/iio/gyro/itg3200_core.c
Normal file
374
drivers/iio/gyro/itg3200_core.c
Normal file
|
@ -0,0 +1,374 @@
|
|||
/*
|
||||
* 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;
|
||||
u8 regval;
|
||||
|
||||
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;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_DLPF, ®val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = (regval & ITG3200_DLPF_CFG_MASK) ? 1000 : 8000;
|
||||
|
||||
ret = itg3200_read_reg_8(indio_dev,
|
||||
ITG3200_REG_SAMPLE_RATE_DIV,
|
||||
®val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val /= regval + 1;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int itg3200_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
int ret;
|
||||
u8 t;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
if (val == 0 || val2 != 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_DLPF, &t);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
t = ((t & ITG3200_DLPF_CFG_MASK) ? 1000u : 8000u) / val - 1;
|
||||
|
||||
ret = itg3200_write_reg_8(indio_dev,
|
||||
ITG3200_REG_SAMPLE_RATE_DIV,
|
||||
t);
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
.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),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.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),
|
||||
};
|
||||
|
||||
static const struct iio_info itg3200_info = {
|
||||
.read_raw = &itg3200_read_raw,
|
||||
.write_raw = &itg3200_write_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");
|
53
drivers/iio/gyro/st_gyro.h
Normal file
53
drivers/iio/gyro/st_gyro.h
Normal file
|
@ -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 */
|
105
drivers/iio/gyro/st_gyro_buffer.c
Normal file
105
drivers/iio/gyro/st_gyro_buffer.c
Normal file
|
@ -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");
|
388
drivers/iio/gyro/st_gyro_core.c
Normal file
388
drivers/iio/gyro/st_gyro_core.c
Normal file
|
@ -0,0 +1,388 @@
|
|||
/*
|
||||
* 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;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = gdata->odr;
|
||||
return IIO_VAL_INT;
|
||||
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;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
if (val2)
|
||||
return -EINVAL;
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
err = st_sensors_set_odr(indio_dev, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return err;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
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,
|
||||
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");
|
116
drivers/iio/gyro/st_gyro_i2c.c
Normal file
116
drivers/iio/gyro/st_gyro_i2c.c
Normal file
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* 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"
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id st_gyro_of_match[] = {
|
||||
{
|
||||
.compatible = "st,l3g4200d-gyro",
|
||||
.data = L3G4200D_GYRO_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm330d-gyro",
|
||||
.data = LSM330D_GYRO_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm330dl-gyro",
|
||||
.data = LSM330DL_GYRO_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm330dlc-gyro",
|
||||
.data = LSM330DLC_GYRO_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,l3gd20-gyro",
|
||||
.data = L3GD20_GYRO_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,l3g4is-gyro",
|
||||
.data = L3G4IS_GYRO_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm330-gyro",
|
||||
.data = LSM330_GYRO_DEV_NAME,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_gyro_of_match);
|
||||
#else
|
||||
#define st_gyro_of_match NULL
|
||||
#endif
|
||||
|
||||
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_of_i2c_probe(client, st_gyro_of_match);
|
||||
|
||||
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",
|
||||
.of_match_table = of_match_ptr(st_gyro_of_match),
|
||||
},
|
||||
.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");
|
76
drivers/iio/gyro/st_gyro_spi.c
Normal file
76
drivers/iio/gyro/st_gyro_spi.c
Normal file
|
@ -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");
|
Loading…
Add table
Add a link
Reference in a new issue