Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+182
View File
@@ -0,0 +1,182 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# 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, ADIS16137 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 ADXRS290
tristate "Analog Devices ADXRS290 Dual-Axis MEMS Gyroscope SPI driver"
depends on SPI
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for Analog Devices ADXRS290 programmable
digital output gyroscope.
This driver can also be built as a module. If so, the module will be
called adxrs290.
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 || SPI_MASTER)
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select BMG160_I2C if (I2C)
select BMG160_SPI if (SPI)
help
Say yes here to build support for BOSCH BMG160 Tri-axis Gyro Sensor
driver connected via I2C or SPI. This driver also supports BMI055
and BMI088 gyroscope.
This driver can also be built as a module. If so, the module
will be called bmg160_i2c or bmg160_spi.
config BMG160_I2C
tristate
select REGMAP_I2C
config BMG160_SPI
tristate
select REGMAP_SPI
config FXAS21002C
tristate "NXP FXAS21002C Gyro Sensor"
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select FXAS21002C_I2C if (I2C)
select FXAS21002C_SPI if (SPI)
depends on (I2C || SPI_MASTER)
help
Say yes here to build support for NXP FXAS21002C Tri-axis Gyro
Sensor driver connected via I2C or SPI.
This driver can also be built as a module. If so, the module
will be called fxas21002c_i2c or fxas21002c_spi.
config FXAS21002C_I2C
tristate
select REGMAP_I2C
config FXAS21002C_SPI
tristate
select REGMAP_SPI
config HID_SENSOR_GYRO_3D
depends on HID_SENSOR_HUB
select IIO_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 MPU3050
tristate
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select REGMAP
config MPU3050_I2C
tristate "Invensense MPU3050 devices on I2C"
depends on I2C
select MPU3050
select REGMAP_I2C
select I2C_MUX
help
This driver supports the Invensense MPU3050 gyroscope over I2C.
This driver can be built as a module. The module will be called
inv-mpu3050-i2c.
config IIO_ST_GYRO_3AXIS
tristate "STMicroelectronics gyroscopes 3-Axis Driver"
depends on (I2C || SPI_MASTER) && SYSFS
select IIO_ST_SENSORS_CORE
select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
help
Say yes here to build support for STMicroelectronics gyroscopes:
L3G4200D, LSM330DL, L3GD20, LSM330DLC, L3G4IS, LSM330, LSM9DS0.
Also need to enable at least one of I2C and SPI interface drivers
below.
config IIO_ST_GYRO_I2C_3AXIS
tristate "STMicroelectronics gyroscopes 3-Axis I2C Interface"
depends on I2C && IIO_ST_GYRO_3AXIS
default I2C && IIO_ST_GYRO_3AXIS
select IIO_ST_SENSORS_I2C
help
Build support for STMicroelectronics gyroscopes I2C interface.
To compile this driver as a module, choose M here. The module
will be called st_gyro_i2c.
config IIO_ST_GYRO_SPI_3AXIS
tristate "STMicroelectronics gyroscopes 3-Axis SPI Interface"
depends on SPI_MASTER && IIO_ST_GYRO_3AXIS
default SPI_MASTER && IIO_ST_GYRO_3AXIS
select IIO_ST_SENSORS_SPI
help
Build support for STMicroelectronics gyroscopes SPI interface.
To compile this driver as a module, choose M here. The module
will be called st_gyro_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
+38
View File
@@ -0,0 +1,38 @@
# SPDX-License-Identifier: GPL-2.0
#
# 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_ADXRS290) += adxrs290.o
obj-$(CONFIG_ADXRS450) += adxrs450.o
obj-$(CONFIG_BMG160) += bmg160_core.o
obj-$(CONFIG_BMG160_I2C) += bmg160_i2c.o
obj-$(CONFIG_BMG160_SPI) += bmg160_spi.o
obj-$(CONFIG_FXAS21002C) += fxas21002c_core.o
obj-$(CONFIG_FXAS21002C_I2C) += fxas21002c_i2c.o
obj-$(CONFIG_FXAS21002C_SPI) += fxas21002c_spi.o
obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
# Currently this is rolled into one module, split it if
# we ever create a separate SPI interface for MPU-3050
obj-$(CONFIG_MPU3050) += mpu3050.o
mpu3050-objs := mpu3050-core.o mpu3050-i2c.o
itg3200-y := itg3200_core.o
itg3200-$(CONFIG_IIO_BUFFER) += itg3200_buffer.o
obj-$(CONFIG_ITG3200) += itg3200.o
obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) += ssp_gyro_sensor.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
+232
View File
@@ -0,0 +1,232 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ADIS16080/100 Yaw Rate Gyroscope with SPI driver
*
* Copyright 2010 Analog Devices Inc.
*/
#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
* @lock: lock to protect buffer during reads
**/
struct adis16080_state {
struct spi_device *us;
const struct adis16080_chip_info *info;
struct mutex lock;
__be16 buf __aligned(IIO_DMA_MINALIGN);
};
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(&st->lock);
ret = adis16080_read_sample(indio_dev, chan->address, val);
mutex_unlock(&st->lock);
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,
};
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);
mutex_init(&st->lock);
/* 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->info = &adis16080_info;
indio_dev->modes = INDIO_DIRECT_MODE;
return devm_iio_device_register(&spi->dev, indio_dev);
}
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",
},
.probe = adis16080_probe,
.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");
+175
View File
@@ -0,0 +1,175 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ADIS16130 Digital Output, High Precision Angular Rate Sensor driver
*
* Copyright 2010 Analog Devices Inc.
*/
#include <linux/mutex.h>
#include <linux/kernel.h>
#include <linux/spi/spi.h>
#include <linux/module.h>
#include <linux/iio/iio.h>
#include <linux/unaligned.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] __aligned(IIO_DMA_MINALIGN);
};
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 = get_unaligned_be24(&st->buf[1]);
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 */
ret = adis16130_spi_read(indio_dev, chan->address, &temp);
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,
};
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->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",
},
.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");
+586
View File
@@ -0,0 +1,586 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* ADIS16133/ADIS16135/ADIS16136 gyroscope driver
*
* Copyright 2012 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*/
#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/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;
const struct adis_data adis_data;
};
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)
return ret;
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT1, &lot1);
if (ret)
return ret;
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT2, &lot2);
if (ret)
return ret;
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT3, &lot3);
if (ret)
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)
return ret;
*val = prod_id;
return 0;
}
DEFINE_DEBUGFS_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)
return ret;
*val = flash_count;
return 0;
}
DEFINE_DEBUGFS_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);
struct dentry *d = iio_get_debugfs_dentry(indio_dev);
debugfs_create_file_unsafe("serial_number", 0400,
d, adis16136, &adis16136_serial_fops);
debugfs_create_file_unsafe("product_id", 0400,
d, adis16136, &adis16136_product_id_fops);
debugfs_create_file_unsafe("flash_count", 0400,
d, 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)
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;
adis_dev_auto_lock(&adis16136->adis);
ret = __adis16136_get_freq(adis16136, &freq);
if (ret)
return ret;
return sysfs_emit(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;
adis_dev_auto_lock(&adis16136->adis);
ret = __adis16136_get_freq(adis16136, &freq);
if (ret)
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;
adis_dev_auto_lock(&adis16136->adis);
ret = __adis_read_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT,
&val16);
if (ret)
return ret;
ret = __adis16136_get_freq(adis16136, &freq);
if (ret)
return ret;
*val = freq / adis16136_3db_divisors[val16 & 0x07];
return 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)
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 = {
.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;
ret = sscanf(indio_dev->name, "adis%u\n", &device_id);
if (ret != 1)
return -EINVAL;
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",
};
#define ADIS16136_DATA(_timeouts) \
{ \
.diag_stat_reg = ADIS16136_REG_DIAG_STAT, \
.glob_cmd_reg = ADIS16136_REG_GLOB_CMD, \
.msc_ctrl_reg = ADIS16136_REG_MSC_CTRL, \
.self_test_reg = ADIS16136_REG_MSC_CTRL, \
.self_test_mask = ADIS16136_MSC_CTRL_SELF_TEST, \
.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), \
.timeouts = (_timeouts), \
}
enum adis16136_id {
ID_ADIS16133,
ID_ADIS16135,
ID_ADIS16136,
ID_ADIS16137,
};
static const struct adis_timeout adis16133_timeouts = {
.reset_ms = 75,
.sw_reset_ms = 75,
.self_test_ms = 50,
};
static const struct adis_timeout adis16136_timeouts = {
.reset_ms = 128,
.sw_reset_ms = 75,
.self_test_ms = 245,
};
static const struct adis16136_chip_info adis16136_chip_info[] = {
[ID_ADIS16133] = {
.precision = IIO_DEGREE_TO_RAD(1200),
.fullscale = 24000,
.adis_data = ADIS16136_DATA(&adis16133_timeouts),
},
[ID_ADIS16135] = {
.precision = IIO_DEGREE_TO_RAD(300),
.fullscale = 24000,
.adis_data = ADIS16136_DATA(&adis16133_timeouts),
},
[ID_ADIS16136] = {
.precision = IIO_DEGREE_TO_RAD(450),
.fullscale = 24623,
.adis_data = ADIS16136_DATA(&adis16136_timeouts),
},
[ID_ADIS16137] = {
.precision = IIO_DEGREE_TO_RAD(1000),
.fullscale = 24609,
.adis_data = ADIS16136_DATA(&adis16136_timeouts),
},
};
static void adis16136_stop(void *data)
{
adis16136_stop_device(data);
}
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;
const struct adis_data *adis16136_data;
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->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;
adis16136_data = &adis16136->chip_info->adis_data;
ret = adis_init(&adis16136->adis, indio_dev, spi, adis16136_data);
if (ret)
return ret;
ret = devm_adis_setup_buffer_and_trigger(&adis16136->adis, indio_dev, NULL);
if (ret)
return ret;
ret = adis16136_initial_setup(indio_dev);
if (ret)
return ret;
ret = devm_add_action_or_reset(&spi->dev, adis16136_stop, indio_dev);
if (ret)
return ret;
ret = devm_iio_device_register(&spi->dev, indio_dev);
if (ret)
return ret;
adis16136_debugfs_init(indio_dev);
return 0;
}
static const struct spi_device_id adis16136_ids[] = {
{ "adis16133", ID_ADIS16133 },
{ "adis16135", ID_ADIS16135 },
{ "adis16136", ID_ADIS16136 },
{ "adis16137", ID_ADIS16137 },
{ }
};
MODULE_DEVICE_TABLE(spi, adis16136_ids);
static struct spi_driver adis16136_driver = {
.driver = {
.name = "adis16136",
},
.id_table = adis16136_ids,
.probe = adis16136_probe,
};
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");
MODULE_IMPORT_NS("IIO_ADISLIB");
+436
View File
@@ -0,0 +1,436 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ADIS16260/ADIS16265 Programmable Digital Gyroscope Sensor Driver
*
* Copyright 2010 Analog Devices Inc.
*/
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/spi/spi.h>
#include <linux/module.h>
#include <linux/iio/iio.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
struct adis16260_chip_info {
unsigned int gyro_max_val;
unsigned int gyro_max_scale;
const struct iio_chan_spec *channels;
unsigned int num_channels;
};
struct adis16260 {
const struct adis16260_chip_info *info;
struct adis adis;
};
enum adis16260_type {
ADIS16251,
ADIS16260,
ADIS16266,
};
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 struct iio_chan_spec adis16266_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_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(4),
};
static const struct adis16260_chip_info adis16260_chip_info_table[] = {
[ADIS16251] = {
.gyro_max_scale = 80,
.gyro_max_val = IIO_RAD_TO_DEGREE(4368),
.channels = adis16260_channels,
.num_channels = ARRAY_SIZE(adis16260_channels),
},
[ADIS16260] = {
.gyro_max_scale = 320,
.gyro_max_val = IIO_RAD_TO_DEGREE(4368),
.channels = adis16260_channels,
.num_channels = ARRAY_SIZE(adis16260_channels),
},
[ADIS16266] = {
.gyro_max_scale = 14000,
.gyro_max_val = IIO_RAD_TO_DEGREE(3357),
.channels = adis16266_channels,
.num_channels = ARRAY_SIZE(adis16266_channels),
},
};
/* Power down the device */
static int adis16260_stop_device(struct iio_dev *indio_dev)
{
struct adis16260 *adis16260 = iio_priv(indio_dev);
int ret;
u16 val = ADIS16260_SLP_CNT_POWER_OFF;
ret = adis_write_reg_16(&adis16260->adis, ADIS16260_SLP_CNT, val);
if (ret)
dev_err(&indio_dev->dev, "problem with turning device off: SLP_CNT");
return ret;
}
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 adis16260 *adis16260 = iio_priv(indio_dev);
const struct adis16260_chip_info *info = adis16260->info;
struct adis *adis = &adis16260->adis;
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 = info->gyro_max_scale;
*val2 = info->gyro_max_val;
return IIO_VAL_FRACTIONAL;
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 adis16260 *adis16260 = iio_priv(indio_dev);
struct adis *adis = &adis16260->adis;
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:
if (val <= 0)
return -EINVAL;
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--;
adis_dev_auto_scoped_lock(adis) {
if (t >= 0x0A)
adis->spi->max_speed_hz = ADIS16260_SPI_SLOW;
else
adis->spi->max_speed_hz = ADIS16260_SPI_FAST;
return __adis_write_reg_8(adis, ADIS16260_SMPL_PRD, t);
}
unreachable();
}
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,
};
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_timeout adis16260_timeouts = {
.reset_ms = ADIS16260_STARTUP_DELAY,
.sw_reset_ms = ADIS16260_STARTUP_DELAY,
.self_test_ms = ADIS16260_STARTUP_DELAY,
};
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,
.self_test_reg = ADIS16260_MSC_CTRL,
.timeouts = &adis16260_timeouts,
.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 void adis16260_stop(void *data)
{
adis16260_stop_device(data);
}
static int adis16260_probe(struct spi_device *spi)
{
const struct spi_device_id *id;
struct adis16260 *adis16260;
struct iio_dev *indio_dev;
int ret;
id = spi_get_device_id(spi);
if (!id)
return -ENODEV;
/* setup the industrialio driver allocated elements */
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adis16260));
if (!indio_dev)
return -ENOMEM;
adis16260 = iio_priv(indio_dev);
/* this is only used for removal purposes */
spi_set_drvdata(spi, indio_dev);
adis16260->info = &adis16260_chip_info_table[id->driver_data];
indio_dev->name = id->name;
indio_dev->info = &adis16260_info;
indio_dev->channels = adis16260->info->channels;
indio_dev->num_channels = adis16260->info->num_channels;
indio_dev->modes = INDIO_DIRECT_MODE;
ret = adis_init(&adis16260->adis, indio_dev, spi, &adis16260_data);
if (ret)
return ret;
ret = devm_adis_setup_buffer_and_trigger(&adis16260->adis, indio_dev, NULL);
if (ret)
return ret;
/* Get the device into a sane initial state */
ret = __adis_initial_startup(&adis16260->adis);
if (ret)
return ret;
ret = devm_add_action_or_reset(&spi->dev, adis16260_stop, indio_dev);
if (ret)
return ret;
return devm_iio_device_register(&spi->dev, indio_dev);
}
/*
* 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", ADIS16260},
{"adis16265", ADIS16260},
{"adis16266", ADIS16266},
{"adis16250", ADIS16260},
{"adis16255", ADIS16260},
{"adis16251", ADIS16251},
{ }
};
MODULE_DEVICE_TABLE(spi, adis16260_id);
static struct spi_driver adis16260_driver = {
.driver = {
.name = "adis16260",
},
.probe = adis16260_probe,
.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");
MODULE_IMPORT_NS("IIO_ADISLIB");
+693
View File
@@ -0,0 +1,693 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ADXRS290 SPI Gyroscope Driver
*
* Copyright (C) 2020 Nishant Malpani <nish.malpani25@gmail.com>
* Copyright (C) 2020 Analog Devices, Inc.
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#define ADXRS290_ADI_ID 0xAD
#define ADXRS290_MEMS_ID 0x1D
#define ADXRS290_DEV_ID 0x92
#define ADXRS290_REG_ADI_ID 0x00
#define ADXRS290_REG_MEMS_ID 0x01
#define ADXRS290_REG_DEV_ID 0x02
#define ADXRS290_REG_REV_ID 0x03
#define ADXRS290_REG_SN0 0x04 /* Serial Number Registers, 4 bytes */
#define ADXRS290_REG_DATAX0 0x08 /* Roll Rate o/p Data Regs, 2 bytes */
#define ADXRS290_REG_DATAY0 0x0A /* Pitch Rate o/p Data Regs, 2 bytes */
#define ADXRS290_REG_TEMP0 0x0C
#define ADXRS290_REG_POWER_CTL 0x10
#define ADXRS290_REG_FILTER 0x11
#define ADXRS290_REG_DATA_RDY 0x12
#define ADXRS290_READ BIT(7)
#define ADXRS290_TSM BIT(0)
#define ADXRS290_MEASUREMENT BIT(1)
#define ADXRS290_DATA_RDY_OUT BIT(0)
#define ADXRS290_SYNC_MASK GENMASK(1, 0)
#define ADXRS290_SYNC(x) FIELD_PREP(ADXRS290_SYNC_MASK, x)
#define ADXRS290_LPF_MASK GENMASK(2, 0)
#define ADXRS290_LPF(x) FIELD_PREP(ADXRS290_LPF_MASK, x)
#define ADXRS290_HPF_MASK GENMASK(7, 4)
#define ADXRS290_HPF(x) FIELD_PREP(ADXRS290_HPF_MASK, x)
#define ADXRS290_READ_REG(reg) (ADXRS290_READ | (reg))
#define ADXRS290_MAX_TRANSITION_TIME_MS 100
enum adxrs290_mode {
ADXRS290_MODE_STANDBY,
ADXRS290_MODE_MEASUREMENT,
};
enum adxrs290_scan_index {
ADXRS290_IDX_X,
ADXRS290_IDX_Y,
ADXRS290_IDX_TEMP,
ADXRS290_IDX_TS,
};
struct adxrs290_state {
struct spi_device *spi;
/* Serialize reads and their subsequent processing */
struct mutex lock;
enum adxrs290_mode mode;
unsigned int lpf_3db_freq_idx;
unsigned int hpf_3db_freq_idx;
struct iio_trigger *dready_trig;
/* Ensure correct alignment of timestamp when present */
struct {
s16 channels[3];
aligned_s64 ts;
} buffer;
};
/*
* Available cut-off frequencies of the low pass filter in Hz.
* The integer part and fractional part are represented separately.
*/
static const int adxrs290_lpf_3db_freq_hz_table[][2] = {
[0] = {480, 0},
[1] = {320, 0},
[2] = {160, 0},
[3] = {80, 0},
[4] = {56, 600000},
[5] = {40, 0},
[6] = {28, 300000},
[7] = {20, 0},
};
/*
* Available cut-off frequencies of the high pass filter in Hz.
* The integer part and fractional part are represented separately.
*/
static const int adxrs290_hpf_3db_freq_hz_table[][2] = {
[0] = {0, 0},
[1] = {0, 11000},
[2] = {0, 22000},
[3] = {0, 44000},
[4] = {0, 87000},
[5] = {0, 175000},
[6] = {0, 350000},
[7] = {0, 700000},
[8] = {1, 400000},
[9] = {2, 800000},
[10] = {11, 300000},
};
static int adxrs290_get_rate_data(struct iio_dev *indio_dev, const u8 cmd, int *val)
{
struct adxrs290_state *st = iio_priv(indio_dev);
int temp;
guard(mutex)(&st->lock);
temp = spi_w8r16(st->spi, cmd);
if (temp < 0)
return temp;
*val = sign_extend32(temp, 15);
return 0;
}
static int adxrs290_get_temp_data(struct iio_dev *indio_dev, int *val)
{
const u8 cmd = ADXRS290_READ_REG(ADXRS290_REG_TEMP0);
struct adxrs290_state *st = iio_priv(indio_dev);
int temp;
guard(mutex)(&st->lock);
temp = spi_w8r16(st->spi, cmd);
if (temp < 0)
return temp;
/* extract lower 12 bits temperature reading */
*val = sign_extend32(temp, 11);
return 0;
}
static int adxrs290_get_3db_freq(struct iio_dev *indio_dev, u8 *val, u8 *val2)
{
const u8 cmd = ADXRS290_READ_REG(ADXRS290_REG_FILTER);
struct adxrs290_state *st = iio_priv(indio_dev);
short temp;
guard(mutex)(&st->lock);
temp = spi_w8r8(st->spi, cmd);
if (temp < 0)
return temp;
*val = FIELD_GET(ADXRS290_LPF_MASK, temp);
*val2 = FIELD_GET(ADXRS290_HPF_MASK, temp);
return 0;
}
static int adxrs290_spi_write_reg(struct spi_device *spi, const u8 reg,
const u8 val)
{
u8 buf[2];
buf[0] = reg;
buf[1] = val;
return spi_write_then_read(spi, buf, ARRAY_SIZE(buf), NULL, 0);
}
static int adxrs290_find_match(const int (*freq_tbl)[2], const int n,
const int val, const int val2)
{
int i;
for (i = 0; i < n; i++) {
if (freq_tbl[i][0] == val && freq_tbl[i][1] == val2)
return i;
}
return -EINVAL;
}
static int adxrs290_set_filter_freq(struct iio_dev *indio_dev,
const unsigned int lpf_idx,
const unsigned int hpf_idx)
{
struct adxrs290_state *st = iio_priv(indio_dev);
u8 val;
val = ADXRS290_HPF(hpf_idx) | ADXRS290_LPF(lpf_idx);
return adxrs290_spi_write_reg(st->spi, ADXRS290_REG_FILTER, val);
}
static int adxrs290_set_mode(struct iio_dev *indio_dev, enum adxrs290_mode mode)
{
struct adxrs290_state *st = iio_priv(indio_dev);
int val, ret;
if (st->mode == mode)
return 0;
guard(mutex)(&st->lock);
ret = spi_w8r8(st->spi, ADXRS290_READ_REG(ADXRS290_REG_POWER_CTL));
if (ret < 0)
return ret;
val = ret;
switch (mode) {
case ADXRS290_MODE_STANDBY:
val &= ~ADXRS290_MEASUREMENT;
break;
case ADXRS290_MODE_MEASUREMENT:
val |= ADXRS290_MEASUREMENT;
break;
default:
return -EINVAL;
}
ret = adxrs290_spi_write_reg(st->spi, ADXRS290_REG_POWER_CTL, val);
if (ret < 0) {
dev_err(&st->spi->dev, "unable to set mode: %d\n", ret);
return ret;
}
/* update cached mode */
st->mode = mode;
return ret;
}
static void adxrs290_chip_off_action(void *data)
{
struct iio_dev *indio_dev = data;
adxrs290_set_mode(indio_dev, ADXRS290_MODE_STANDBY);
}
static int adxrs290_initial_setup(struct iio_dev *indio_dev)
{
struct adxrs290_state *st = iio_priv(indio_dev);
struct spi_device *spi = st->spi;
int ret;
ret = adxrs290_spi_write_reg(spi, ADXRS290_REG_POWER_CTL,
ADXRS290_MEASUREMENT | ADXRS290_TSM);
if (ret < 0)
return ret;
st->mode = ADXRS290_MODE_MEASUREMENT;
return devm_add_action_or_reset(&spi->dev, adxrs290_chip_off_action,
indio_dev);
}
static int adxrs290_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val,
int *val2,
long mask)
{
struct adxrs290_state *st = iio_priv(indio_dev);
unsigned int t;
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
switch (chan->type) {
case IIO_ANGL_VEL:
ret = adxrs290_get_rate_data(indio_dev,
ADXRS290_READ_REG(chan->address),
val);
if (ret < 0)
break;
ret = IIO_VAL_INT;
break;
case IIO_TEMP:
ret = adxrs290_get_temp_data(indio_dev, val);
if (ret < 0)
break;
ret = IIO_VAL_INT;
break;
default:
ret = -EINVAL;
break;
}
iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_ANGL_VEL:
/* 1 LSB = 0.005 degrees/sec */
*val = 0;
*val2 = 87266;
return IIO_VAL_INT_PLUS_NANO;
case IIO_TEMP:
/* 1 LSB = 0.1 degrees Celsius */
*val = 100;
return IIO_VAL_INT;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
switch (chan->type) {
case IIO_ANGL_VEL:
t = st->lpf_3db_freq_idx;
*val = adxrs290_lpf_3db_freq_hz_table[t][0];
*val2 = adxrs290_lpf_3db_freq_hz_table[t][1];
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
switch (chan->type) {
case IIO_ANGL_VEL:
t = st->hpf_3db_freq_idx;
*val = adxrs290_hpf_3db_freq_hz_table[t][0];
*val2 = adxrs290_hpf_3db_freq_hz_table[t][1];
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
}
return -EINVAL;
}
static int adxrs290_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val,
int val2,
long mask)
{
struct adxrs290_state *st = iio_priv(indio_dev);
int ret, lpf_idx, hpf_idx;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
switch (mask) {
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
lpf_idx = adxrs290_find_match(adxrs290_lpf_3db_freq_hz_table,
ARRAY_SIZE(adxrs290_lpf_3db_freq_hz_table),
val, val2);
if (lpf_idx < 0) {
ret = -EINVAL;
break;
}
/* caching the updated state of the low-pass filter */
st->lpf_3db_freq_idx = lpf_idx;
/* retrieving the current state of the high-pass filter */
hpf_idx = st->hpf_3db_freq_idx;
ret = adxrs290_set_filter_freq(indio_dev, lpf_idx, hpf_idx);
break;
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
hpf_idx = adxrs290_find_match(adxrs290_hpf_3db_freq_hz_table,
ARRAY_SIZE(adxrs290_hpf_3db_freq_hz_table),
val, val2);
if (hpf_idx < 0) {
ret = -EINVAL;
break;
}
/* caching the updated state of the high-pass filter */
st->hpf_3db_freq_idx = hpf_idx;
/* retrieving the current state of the low-pass filter */
lpf_idx = st->lpf_3db_freq_idx;
ret = adxrs290_set_filter_freq(indio_dev, lpf_idx, hpf_idx);
break;
default:
ret = -EINVAL;
break;
}
iio_device_release_direct(indio_dev);
return ret;
}
static int adxrs290_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long mask)
{
switch (mask) {
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
*vals = (const int *)adxrs290_lpf_3db_freq_hz_table;
*type = IIO_VAL_INT_PLUS_MICRO;
/* Values are stored in a 2D matrix */
*length = ARRAY_SIZE(adxrs290_lpf_3db_freq_hz_table) * 2;
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
*vals = (const int *)adxrs290_hpf_3db_freq_hz_table;
*type = IIO_VAL_INT_PLUS_MICRO;
/* Values are stored in a 2D matrix */
*length = ARRAY_SIZE(adxrs290_hpf_3db_freq_hz_table) * 2;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
static int adxrs290_reg_access_rw(struct spi_device *spi, unsigned int reg,
unsigned int *readval)
{
int ret;
ret = spi_w8r8(spi, ADXRS290_READ_REG(reg));
if (ret < 0)
return ret;
*readval = ret;
return 0;
}
static int adxrs290_reg_access(struct iio_dev *indio_dev, unsigned int reg,
unsigned int writeval, unsigned int *readval)
{
struct adxrs290_state *st = iio_priv(indio_dev);
if (readval)
return adxrs290_reg_access_rw(st->spi, reg, readval);
else
return adxrs290_spi_write_reg(st->spi, reg, writeval);
}
static int adxrs290_data_rdy_trigger_set_state(struct iio_trigger *trig,
bool state)
{
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
struct adxrs290_state *st = iio_priv(indio_dev);
int ret;
u8 val;
val = state ? ADXRS290_SYNC(ADXRS290_DATA_RDY_OUT) : 0;
ret = adxrs290_spi_write_reg(st->spi, ADXRS290_REG_DATA_RDY, val);
if (ret < 0)
dev_err(&st->spi->dev, "failed to start data rdy interrupt\n");
return ret;
}
static void adxrs290_reset_trig(struct iio_trigger *trig)
{
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
int val;
/*
* Data ready interrupt is reset after a read of the data registers.
* Here, we only read the 16b DATAY registers as that marks the end of
* a read of the data registers and initiates a reset for the interrupt
* line.
*/
adxrs290_get_rate_data(indio_dev,
ADXRS290_READ_REG(ADXRS290_REG_DATAY0), &val);
}
static const struct iio_trigger_ops adxrs290_trigger_ops = {
.set_trigger_state = &adxrs290_data_rdy_trigger_set_state,
.validate_device = &iio_trigger_validate_own_device,
.reenable = &adxrs290_reset_trig,
};
static irqreturn_t adxrs290_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct adxrs290_state *st = iio_priv(indio_dev);
u8 tx = ADXRS290_READ_REG(ADXRS290_REG_DATAX0);
int ret;
do {
guard(mutex)(&st->lock);
/* exercise a bulk data capture starting from reg DATAX0... */
ret = spi_write_then_read(st->spi, &tx, sizeof(tx),
st->buffer.channels,
sizeof(st->buffer.channels));
if (ret < 0)
break;
iio_push_to_buffers_with_timestamp(indio_dev, &st->buffer,
pf->timestamp);
} while (0);
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
#define ADXRS290_ANGL_VEL_CHANNEL(reg, axis) { \
.type = IIO_ANGL_VEL, \
.address = reg, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \
BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \
.info_mask_shared_by_type_available = \
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \
BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \
.scan_index = ADXRS290_IDX_##axis, \
.scan_type = { \
.sign = 's', \
.realbits = 16, \
.storagebits = 16, \
.endianness = IIO_LE, \
}, \
}
static const struct iio_chan_spec adxrs290_channels[] = {
ADXRS290_ANGL_VEL_CHANNEL(ADXRS290_REG_DATAX0, X),
ADXRS290_ANGL_VEL_CHANNEL(ADXRS290_REG_DATAY0, Y),
{
.type = IIO_TEMP,
.address = ADXRS290_REG_TEMP0,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
.scan_index = ADXRS290_IDX_TEMP,
.scan_type = {
.sign = 's',
.realbits = 12,
.storagebits = 16,
.endianness = IIO_LE,
},
},
IIO_CHAN_SOFT_TIMESTAMP(ADXRS290_IDX_TS),
};
static const unsigned long adxrs290_avail_scan_masks[] = {
BIT(ADXRS290_IDX_X) | BIT(ADXRS290_IDX_Y) | BIT(ADXRS290_IDX_TEMP),
0
};
static const struct iio_info adxrs290_info = {
.read_raw = &adxrs290_read_raw,
.write_raw = &adxrs290_write_raw,
.read_avail = &adxrs290_read_avail,
.debugfs_reg_access = &adxrs290_reg_access,
};
static int adxrs290_probe_trigger(struct iio_dev *indio_dev)
{
struct adxrs290_state *st = iio_priv(indio_dev);
int ret;
if (!st->spi->irq) {
dev_info(&st->spi->dev, "no irq, using polling\n");
return 0;
}
st->dready_trig = devm_iio_trigger_alloc(&st->spi->dev, "%s-dev%d",
indio_dev->name,
iio_device_id(indio_dev));
if (!st->dready_trig)
return -ENOMEM;
st->dready_trig->ops = &adxrs290_trigger_ops;
iio_trigger_set_drvdata(st->dready_trig, indio_dev);
ret = devm_request_irq(&st->spi->dev, st->spi->irq,
&iio_trigger_generic_data_rdy_poll,
IRQF_NO_THREAD, "adxrs290_irq", st->dready_trig);
if (ret < 0)
return dev_err_probe(&st->spi->dev, ret,
"request irq %d failed\n", st->spi->irq);
ret = devm_iio_trigger_register(&st->spi->dev, st->dready_trig);
if (ret) {
dev_err(&st->spi->dev, "iio trigger register failed\n");
return ret;
}
indio_dev->trig = iio_trigger_get(st->dready_trig);
return 0;
}
static int adxrs290_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
struct adxrs290_state *st;
u8 val, val2;
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
st->spi = spi;
indio_dev->name = "adxrs290";
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = adxrs290_channels;
indio_dev->num_channels = ARRAY_SIZE(adxrs290_channels);
indio_dev->info = &adxrs290_info;
indio_dev->available_scan_masks = adxrs290_avail_scan_masks;
mutex_init(&st->lock);
val = spi_w8r8(spi, ADXRS290_READ_REG(ADXRS290_REG_ADI_ID));
if (val != ADXRS290_ADI_ID) {
dev_err(&spi->dev, "Wrong ADI ID 0x%02x\n", val);
return -ENODEV;
}
val = spi_w8r8(spi, ADXRS290_READ_REG(ADXRS290_REG_MEMS_ID));
if (val != ADXRS290_MEMS_ID) {
dev_err(&spi->dev, "Wrong MEMS ID 0x%02x\n", val);
return -ENODEV;
}
val = spi_w8r8(spi, ADXRS290_READ_REG(ADXRS290_REG_DEV_ID));
if (val != ADXRS290_DEV_ID) {
dev_err(&spi->dev, "Wrong DEV ID 0x%02x\n", val);
return -ENODEV;
}
/* default mode the gyroscope starts in */
st->mode = ADXRS290_MODE_STANDBY;
/* switch to measurement mode and switch on the temperature sensor */
ret = adxrs290_initial_setup(indio_dev);
if (ret < 0)
return ret;
/* max transition time to measurement mode */
msleep(ADXRS290_MAX_TRANSITION_TIME_MS);
ret = adxrs290_get_3db_freq(indio_dev, &val, &val2);
if (ret < 0)
return ret;
st->lpf_3db_freq_idx = val;
st->hpf_3db_freq_idx = val2;
ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
&iio_pollfunc_store_time,
&adxrs290_trigger_handler, NULL);
if (ret < 0)
return dev_err_probe(&spi->dev, ret,
"iio triggered buffer setup failed\n");
ret = adxrs290_probe_trigger(indio_dev);
if (ret < 0)
return ret;
return devm_iio_device_register(&spi->dev, indio_dev);
}
static const struct of_device_id adxrs290_of_match[] = {
{ .compatible = "adi,adxrs290" },
{ }
};
MODULE_DEVICE_TABLE(of, adxrs290_of_match);
static struct spi_driver adxrs290_driver = {
.driver = {
.name = "adxrs290",
.of_match_table = adxrs290_of_match,
},
.probe = adxrs290_probe,
};
module_spi_driver(adxrs290_driver);
MODULE_AUTHOR("Nishant Malpani <nish.malpani25@gmail.com>");
MODULE_DESCRIPTION("Analog Devices ADXRS290 Gyroscope SPI driver");
MODULE_LICENSE("GPL");
+459
View File
@@ -0,0 +1,459 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* ADXRS450/ADXRS453 Digital Output Gyroscope Driver
*
* Copyright 2011 Analog Devices Inc.
*/
#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 __aligned(IIO_DMA_MINALIGN);
__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,
.len = sizeof(st->tx),
.cs_change = 1,
}, {
.rx_buf = &st->rx,
.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,
.len = sizeof(st->tx),
.cs_change = 1,
}, {
.rx_buf = &st->rx,
.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,
.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 = {
.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->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",
},
.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");
+11
View File
@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef BMG160_H_
#define BMG160_H_
extern const struct dev_pm_ops bmg160_pm_ops;
int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq,
const char *name);
void bmg160_core_remove(struct device *dev);
#endif /* BMG160_H_ */
File diff suppressed because it is too large Load Diff
+81
View File
@@ -0,0 +1,81 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
#include "bmg160.h"
static const struct regmap_config bmg160_regmap_i2c_conf = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x3f
};
static int bmg160_i2c_probe(struct i2c_client *client)
{
const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct regmap *regmap;
const char *name;
regmap = devm_regmap_init_i2c(client, &bmg160_regmap_i2c_conf);
if (IS_ERR(regmap)) {
dev_err(&client->dev, "Failed to register i2c regmap: %pe\n",
regmap);
return PTR_ERR(regmap);
}
if (id)
name = id->name;
else
name = iio_get_acpi_device_name(&client->dev);
return bmg160_core_probe(&client->dev, regmap, client->irq, name);
}
static void bmg160_i2c_remove(struct i2c_client *client)
{
bmg160_core_remove(&client->dev);
}
static const struct acpi_device_id bmg160_acpi_match[] = {
{"BMG0160", 0},
{ }
};
MODULE_DEVICE_TABLE(acpi, bmg160_acpi_match);
static const struct i2c_device_id bmg160_i2c_id[] = {
{ .name = "bmg160" },
{ .name = "bmi055_gyro" },
{ .name = "bmi088_gyro" },
{ }
};
MODULE_DEVICE_TABLE(i2c, bmg160_i2c_id);
static const struct of_device_id bmg160_of_match[] = {
{ .compatible = "bosch,bmg160" },
{ .compatible = "bosch,bmi055_gyro" },
{ .compatible = "bosch,bmi088_gyro" },
{ }
};
MODULE_DEVICE_TABLE(of, bmg160_of_match);
static struct i2c_driver bmg160_i2c_driver = {
.driver = {
.name = "bmg160_i2c",
.acpi_match_table = bmg160_acpi_match,
.of_match_table = bmg160_of_match,
.pm = &bmg160_pm_ops,
},
.probe = bmg160_i2c_probe,
.remove = bmg160_i2c_remove,
.id_table = bmg160_i2c_id,
};
module_i2c_driver(bmg160_i2c_driver);
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("BMG160 I2C Gyro driver");
+67
View File
@@ -0,0 +1,67 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/spi/spi.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
#include "bmg160.h"
static const struct regmap_config bmg160_regmap_spi_conf = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x3f,
};
static int bmg160_spi_probe(struct spi_device *spi)
{
struct regmap *regmap;
const struct spi_device_id *id = spi_get_device_id(spi);
regmap = devm_regmap_init_spi(spi, &bmg160_regmap_spi_conf);
if (IS_ERR(regmap)) {
dev_err(&spi->dev, "Failed to register spi regmap: %pe\n",
regmap);
return PTR_ERR(regmap);
}
return bmg160_core_probe(&spi->dev, regmap, spi->irq, id->name);
}
static void bmg160_spi_remove(struct spi_device *spi)
{
bmg160_core_remove(&spi->dev);
}
static const struct spi_device_id bmg160_spi_id[] = {
{"bmg160", 0},
{"bmi055_gyro", 0},
{"bmi088_gyro", 0},
{ }
};
MODULE_DEVICE_TABLE(spi, bmg160_spi_id);
static const struct of_device_id bmg160_of_match[] = {
{ .compatible = "bosch,bmg160" },
{ .compatible = "bosch,bmi055_gyro" },
{ .compatible = "bosch,bmi088_gyro" },
{ }
};
MODULE_DEVICE_TABLE(of, bmg160_of_match);
static struct spi_driver bmg160_spi_driver = {
.driver = {
.name = "bmg160_spi",
.of_match_table = bmg160_of_match,
.pm = &bmg160_pm_ops,
},
.probe = bmg160_spi_probe,
.remove = bmg160_spi_remove,
.id_table = bmg160_spi_id,
};
module_spi_driver(bmg160_spi_driver);
MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("BMG160 SPI Gyro driver");
+84
View File
@@ -0,0 +1,84 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Driver for NXP FXAS21002C Gyroscope - Header
*
* Copyright (C) 2019 Linaro Ltd.
*/
#ifndef FXAS21002C_H_
#define FXAS21002C_H_
#include <linux/regmap.h>
#define FXAS21002C_REG_STATUS 0x00
#define FXAS21002C_REG_OUT_X_MSB 0x01
#define FXAS21002C_REG_OUT_X_LSB 0x02
#define FXAS21002C_REG_OUT_Y_MSB 0x03
#define FXAS21002C_REG_OUT_Y_LSB 0x04
#define FXAS21002C_REG_OUT_Z_MSB 0x05
#define FXAS21002C_REG_OUT_Z_LSB 0x06
#define FXAS21002C_REG_DR_STATUS 0x07
#define FXAS21002C_REG_F_STATUS 0x08
#define FXAS21002C_REG_F_SETUP 0x09
#define FXAS21002C_REG_F_EVENT 0x0A
#define FXAS21002C_REG_INT_SRC_FLAG 0x0B
#define FXAS21002C_REG_WHO_AM_I 0x0C
#define FXAS21002C_REG_CTRL0 0x0D
#define FXAS21002C_REG_RT_CFG 0x0E
#define FXAS21002C_REG_RT_SRC 0x0F
#define FXAS21002C_REG_RT_THS 0x10
#define FXAS21002C_REG_RT_COUNT 0x11
#define FXAS21002C_REG_TEMP 0x12
#define FXAS21002C_REG_CTRL1 0x13
#define FXAS21002C_REG_CTRL2 0x14
#define FXAS21002C_REG_CTRL3 0x15
enum fxas21002c_fields {
F_DR_STATUS,
F_OUT_X_MSB,
F_OUT_X_LSB,
F_OUT_Y_MSB,
F_OUT_Y_LSB,
F_OUT_Z_MSB,
F_OUT_Z_LSB,
/* DR_STATUS */
F_ZYX_OW, F_Z_OW, F_Y_OW, F_X_OW, F_ZYX_DR, F_Z_DR, F_Y_DR, F_X_DR,
/* F_STATUS */
F_OVF, F_WMKF, F_CNT,
/* F_SETUP */
F_MODE, F_WMRK,
/* F_EVENT */
F_EVENT, FE_TIME,
/* INT_SOURCE_FLAG */
F_BOOTEND, F_SRC_FIFO, F_SRC_RT, F_SRC_DRDY,
/* WHO_AM_I */
F_WHO_AM_I,
/* CTRL_REG0 */
F_BW, F_SPIW, F_SEL, F_HPF_EN, F_FS,
/* RT_CFG */
F_ELE, F_ZTEFE, F_YTEFE, F_XTEFE,
/* RT_SRC */
F_EA, F_ZRT, F_ZRT_POL, F_YRT, F_YRT_POL, F_XRT, F_XRT_POL,
/* RT_THS */
F_DBCNTM, F_THS,
/* RT_COUNT */
F_RT_COUNT,
/* TEMP */
F_TEMP,
/* CTRL_REG1 */
F_RST, F_ST, F_DR, F_ACTIVE, F_READY,
/* CTRL_REG2 */
F_INT_CFG_FIFO, F_INT_EN_FIFO, F_INT_CFG_RT, F_INT_EN_RT,
F_INT_CFG_DRDY, F_INT_EN_DRDY, F_IPOL, F_PP_OD,
/* CTRL_REG3 */
F_WRAPTOONE, F_EXTCTRLEN, F_FS_DOUBLE,
/* MAX FIELDS */
F_MAX_FIELDS,
};
extern const struct dev_pm_ops fxas21002c_pm_ops;
int fxas21002c_core_probe(struct device *dev, struct regmap *regmap, int irq,
const char *name);
void fxas21002c_core_remove(struct device *dev);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,67 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for NXP FXAS21002C Gyroscope - I2C
*
* Copyright (C) 2018 Linaro Ltd.
*/
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include "fxas21002c.h"
static const struct regmap_config fxas21002c_regmap_i2c_conf = {
.reg_bits = 8,
.val_bits = 8,
.max_register = FXAS21002C_REG_CTRL3,
};
static int fxas21002c_i2c_probe(struct i2c_client *i2c)
{
struct regmap *regmap;
regmap = devm_regmap_init_i2c(i2c, &fxas21002c_regmap_i2c_conf);
if (IS_ERR(regmap)) {
dev_err(&i2c->dev, "Failed to register i2c regmap: %ld\n",
PTR_ERR(regmap));
return PTR_ERR(regmap);
}
return fxas21002c_core_probe(&i2c->dev, regmap, i2c->irq, i2c->name);
}
static void fxas21002c_i2c_remove(struct i2c_client *i2c)
{
fxas21002c_core_remove(&i2c->dev);
}
static const struct i2c_device_id fxas21002c_i2c_id[] = {
{ .name = "fxas21002c" },
{ }
};
MODULE_DEVICE_TABLE(i2c, fxas21002c_i2c_id);
static const struct of_device_id fxas21002c_i2c_of_match[] = {
{ .compatible = "nxp,fxas21002c", },
{ }
};
MODULE_DEVICE_TABLE(of, fxas21002c_i2c_of_match);
static struct i2c_driver fxas21002c_i2c_driver = {
.driver = {
.name = "fxas21002c_i2c",
.pm = pm_ptr(&fxas21002c_pm_ops),
.of_match_table = fxas21002c_i2c_of_match,
},
.probe = fxas21002c_i2c_probe,
.remove = fxas21002c_i2c_remove,
.id_table = fxas21002c_i2c_id,
};
module_i2c_driver(fxas21002c_i2c_driver);
MODULE_AUTHOR("Rui Miguel Silva <rui.silva@linaro.org>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("FXAS21002C I2C Gyro driver");
MODULE_IMPORT_NS("IIO_FXAS21002C");
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for NXP Fxas21002c Gyroscope - SPI
*
* Copyright (C) 2019 Linaro Ltd.
*/
#include <linux/err.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include "fxas21002c.h"
static const struct regmap_config fxas21002c_regmap_spi_conf = {
.reg_bits = 8,
.val_bits = 8,
.max_register = FXAS21002C_REG_CTRL3,
};
static int fxas21002c_spi_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
struct regmap *regmap;
regmap = devm_regmap_init_spi(spi, &fxas21002c_regmap_spi_conf);
if (IS_ERR(regmap)) {
dev_err(&spi->dev, "Failed to register spi regmap: %ld\n",
PTR_ERR(regmap));
return PTR_ERR(regmap);
}
return fxas21002c_core_probe(&spi->dev, regmap, spi->irq, id->name);
}
static void fxas21002c_spi_remove(struct spi_device *spi)
{
fxas21002c_core_remove(&spi->dev);
}
static const struct spi_device_id fxas21002c_spi_id[] = {
{ "fxas21002c", 0 },
{ }
};
MODULE_DEVICE_TABLE(spi, fxas21002c_spi_id);
static const struct of_device_id fxas21002c_spi_of_match[] = {
{ .compatible = "nxp,fxas21002c", },
{ }
};
MODULE_DEVICE_TABLE(of, fxas21002c_spi_of_match);
static struct spi_driver fxas21002c_spi_driver = {
.driver = {
.name = "fxas21002c_spi",
.pm = pm_ptr(&fxas21002c_pm_ops),
.of_match_table = fxas21002c_spi_of_match,
},
.probe = fxas21002c_spi_probe,
.remove = fxas21002c_spi_remove,
.id_table = fxas21002c_spi_id,
};
module_spi_driver(fxas21002c_spi_driver);
MODULE_AUTHOR("Rui Miguel Silva <rui.silva@linaro.org>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("FXAS21002C SPI Gyro driver");
MODULE_IMPORT_NS("IIO_FXAS21002C");
@@ -0,0 +1,386 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* HID Sensors Driver
* Copyright (c) 2012, Intel Corporation.
*/
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/hid-sensor-hub.h>
#include <linux/iio/iio.h>
#include <linux/iio/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,
};
#define CHANNEL_SCAN_INDEX_TIMESTAMP 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];
struct {
u32 gyro_val[GYRO_3D_CHANNEL_MAX];
aligned_s64 timestamp;
} scan;
int scale_pre_decml;
int scale_post_decml;
int scale_precision;
int value_offset;
s64 timestamp;
};
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
};
static const u32 gyro_3d_sensitivity_addresses[] = {
HID_USAGE_SENSOR_DATA_ANGL_VELOCITY,
};
/* 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,
},
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP)
};
/* 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 min;
*val = 0;
*val2 = 0;
switch (mask) {
case IIO_CHAN_INFO_RAW:
hid_sensor_power_state(&gyro_state->common_attributes, true);
report_id = gyro_state->gyro[chan->scan_index].report_id;
min = gyro_state->gyro[chan->scan_index].logical_minimum;
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,
SENSOR_HUB_SYNC,
min < 0);
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 = {
.read_raw = &gyro_3d_read_raw,
.write_raw = &gyro_3d_write_raw,
};
/* 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)) {
if (!gyro_state->timestamp)
gyro_state->timestamp = iio_get_time_ns(indio_dev);
iio_push_to_buffers_with_timestamp(indio_dev, &gyro_state->scan,
gyro_state->timestamp);
gyro_state->timestamp = 0;
}
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->scan.gyro_val[CHANNEL_SCAN_INDEX_X + offset] =
*(u32 *)raw_data;
ret = 0;
break;
case HID_USAGE_SENSOR_TIME_TIMESTAMP:
gyro_state->timestamp =
hid_sensor_convert_timestamp(&gyro_state->common_attributes,
*(s64 *)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;
for (unsigned int ch = CHANNEL_SCAN_INDEX_X; ch <= CHANNEL_SCAN_INDEX_Z; ch++) {
ret = sensor_hub_input_get_attribute_info(hsdev,
HID_INPUT_REPORT,
usage_id,
HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS + ch,
&st->gyro[ch]);
if (ret < 0)
break;
channels[ch].scan_type = (struct iio_scan_type) {
.format = 's',
.realbits = BYTES_TO_BITS(st->gyro[ch].size),
.storagebits = BITS_PER_TYPE(u32),
};
}
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);
return ret;
}
/* Function to initialize the processing for usage id */
static int hid_gyro_3d_probe(struct platform_device *pdev)
{
struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev);
int ret = 0;
static const char *name = "gyro_3d";
struct iio_dev *indio_dev;
struct gyro_3d_state *gyro_state;
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,
gyro_3d_sensitivity_addresses,
ARRAY_SIZE(gyro_3d_sensitivity_addresses));
if (ret) {
dev_err(&pdev->dev, "failed to setup common attributes\n");
return ret;
}
indio_dev->channels = devm_kmemdup(&pdev->dev, gyro_3d_channels,
sizeof(gyro_3d_channels), GFP_KERNEL);
if (!indio_dev->channels) {
dev_err(&pdev->dev, "failed to duplicate channels\n");
return -ENOMEM;
}
ret = gyro_3d_parse_report(pdev, hsdev,
(struct iio_chan_spec *)indio_dev->channels,
HID_USAGE_SENSOR_GYRO_3D, gyro_state);
if (ret) {
dev_err(&pdev->dev, "failed to setup attributes\n");
return ret;
}
indio_dev->num_channels = ARRAY_SIZE(gyro_3d_channels);
indio_dev->info = &gyro_3d_info;
indio_dev->name = name;
indio_dev->modes = INDIO_DIRECT_MODE;
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");
return ret;
}
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(indio_dev, &gyro_state->common_attributes);
return ret;
}
/* Function to deinitialize the processing for usage id */
static void hid_gyro_3d_remove(struct platform_device *pdev)
{
struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev);
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(indio_dev, &gyro_state->common_attributes);
}
static const struct platform_device_id hid_gyro_3d_ids[] = {
{
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
.name = "HID-SENSOR-200076",
},
{ }
};
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,
.pm = &hid_sensor_pm_ops,
},
.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");
MODULE_IMPORT_NS("IIO_HID");
@@ -0,0 +1,153 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
*/
#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);
/*
* Ensure correct alignment and padding including for the
* timestamp that may be inserted.
*/
struct {
__be16 buf[ITG3200_SCAN_ELEMENTS];
aligned_s64 ts;
} scan;
int ret = itg3200_read_all_channels(st->i2c, scan.buf);
if (ret < 0)
goto error_ret;
iio_push_to_buffers_with_timestamp(indio_dev, &scan, pf->timestamp);
error_ret:
iio_trigger_notify_done(indio_dev->trig);
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 = {
.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(&st->i2c->dev, "%s-dev%d", indio_dev->name,
iio_device_id(indio_dev));
if (!st->trig)
return -ENOMEM;
ret = request_irq(st->i2c->irq, &iio_trigger_generic_data_rdy_poll,
IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
"itg3200_data_rdy", st->trig);
if (ret)
goto error_free_trig;
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);
}
+418
View File
@@ -0,0 +1,418 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
*
* TODO:
* - Support digital low pass filter
* - Support power management
*/
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/module.h>
#include <linux/mutex.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);
if (ret)
return ret;
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, &regval);
if (ret)
return ret;
*val = (regval & ITG3200_DLPF_CFG_MASK) ? 1000 : 8000;
ret = itg3200_read_reg_8(indio_dev,
ITG3200_REG_SAMPLE_RATE_DIV,
&regval);
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)
{
struct itg3200 *st = iio_priv(indio_dev);
int ret;
u8 t;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
if (val == 0 || val2 != 0)
return -EINVAL;
mutex_lock(&st->lock);
ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_DLPF, &t);
if (ret) {
mutex_unlock(&st->lock);
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(&st->lock);
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_reset(indio_dev);
if (ret)
goto err_ret;
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_enable_full_scale(indio_dev);
err_ret:
return ret;
}
static const struct iio_mount_matrix *
itg3200_get_mount_matrix(const struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct itg3200 *data = iio_priv(indio_dev);
return &data->orientation;
}
static const struct iio_chan_spec_ext_info itg3200_ext_info[] = {
IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, itg3200_get_mount_matrix),
{ }
};
#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, \
.ext_info = itg3200_ext_info, \
}
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,
};
static const unsigned long itg3200_available_scan_masks[] = { 0xffffffff, 0x0 };
static int itg3200_probe(struct i2c_client *client)
{
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);
ret = iio_read_mount_matrix(&client->dev, &st->orientation);
if (ret)
return ret;
i2c_set_clientdata(client, indio_dev);
st->i2c = client;
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;
mutex_init(&st->lock);
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 void 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);
}
static int itg3200_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct itg3200 *st = iio_priv(indio_dev);
dev_dbg(&st->i2c->dev, "suspend device");
return itg3200_write_reg_8(indio_dev, ITG3200_REG_POWER_MANAGEMENT,
ITG3200_SLEEP);
}
static int itg3200_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
return itg3200_initial_setup(indio_dev);
}
static DEFINE_SIMPLE_DEV_PM_OPS(itg3200_pm_ops, itg3200_suspend,
itg3200_resume);
static const struct i2c_device_id itg3200_id[] = {
{ .name = "itg3200" },
{ }
};
MODULE_DEVICE_TABLE(i2c, itg3200_id);
static const struct of_device_id itg3200_of_match[] = {
{ .compatible = "invensense,itg3200" },
{ }
};
MODULE_DEVICE_TABLE(of, itg3200_of_match);
static struct i2c_driver itg3200_driver = {
.driver = {
.name = "itg3200",
.of_match_table = itg3200_of_match,
.pm = pm_sleep_ptr(&itg3200_pm_ops),
},
.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");
File diff suppressed because it is too large Load Diff
+122
View File
@@ -0,0 +1,122 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/pm_runtime.h>
#include "mpu3050.h"
static const struct regmap_config mpu3050_i2c_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
static int mpu3050_i2c_bypass_select(struct i2c_mux_core *mux, u32 chan_id)
{
struct mpu3050 *mpu3050 = i2c_mux_priv(mux);
/* Just power up the device, that is all that is needed */
return pm_runtime_resume_and_get(mpu3050->dev);
}
static int mpu3050_i2c_bypass_deselect(struct i2c_mux_core *mux, u32 chan_id)
{
struct mpu3050 *mpu3050 = i2c_mux_priv(mux);
pm_runtime_put_autosuspend(mpu3050->dev);
return 0;
}
static int mpu3050_i2c_probe(struct i2c_client *client)
{
const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct regmap *regmap;
const char *name;
struct mpu3050 *mpu3050;
int ret;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_I2C_BLOCK))
return -EOPNOTSUPP;
if (id)
name = id->name;
else
return -ENODEV;
regmap = devm_regmap_init_i2c(client, &mpu3050_i2c_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&client->dev, "Failed to register i2c regmap: %pe\n",
regmap);
return PTR_ERR(regmap);
}
ret = mpu3050_common_probe(&client->dev, regmap, client->irq, name);
if (ret)
return ret;
/* The main driver is up, now register the I2C mux */
mpu3050 = iio_priv(dev_get_drvdata(&client->dev));
mpu3050->i2cmux = i2c_mux_alloc(client->adapter, &client->dev,
1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE,
mpu3050_i2c_bypass_select,
mpu3050_i2c_bypass_deselect);
/* Just fail the mux, there is no point in killing the driver */
if (!mpu3050->i2cmux)
dev_err(&client->dev, "failed to allocate I2C mux\n");
else {
mpu3050->i2cmux->priv = mpu3050;
/* Ignore failure, not critical */
i2c_mux_add_adapter(mpu3050->i2cmux, 0, 0);
}
return 0;
}
static void mpu3050_i2c_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = dev_get_drvdata(&client->dev);
struct mpu3050 *mpu3050 = iio_priv(indio_dev);
if (mpu3050->i2cmux)
i2c_mux_del_adapters(mpu3050->i2cmux);
mpu3050_common_remove(&client->dev);
}
/*
* device id table is used to identify what device can be
* supported by this driver
*/
static const struct i2c_device_id mpu3050_i2c_id[] = {
{ .name = "mpu3050" },
{ }
};
MODULE_DEVICE_TABLE(i2c, mpu3050_i2c_id);
static const struct of_device_id mpu3050_i2c_of_match[] = {
{ .compatible = "invensense,mpu3050", .data = "mpu3050" },
/* Deprecated vendor ID from the Input driver */
{ .compatible = "invn,mpu3050", .data = "mpu3050" },
{ }
};
MODULE_DEVICE_TABLE(of, mpu3050_i2c_of_match);
static struct i2c_driver mpu3050_i2c_driver = {
.probe = mpu3050_i2c_probe,
.remove = mpu3050_i2c_remove,
.id_table = mpu3050_i2c_id,
.driver = {
.of_match_table = mpu3050_i2c_of_match,
.name = "mpu3050-i2c",
.pm = pm_ptr(&mpu3050_dev_pm_ops),
},
};
module_i2c_driver(mpu3050_i2c_driver);
MODULE_AUTHOR("Linus Walleij");
MODULE_DESCRIPTION("Invensense MPU3050 gyroscope driver");
MODULE_LICENSE("GPL");
+97
View File
@@ -0,0 +1,97 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/iio/iio.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/i2c.h>
/**
* enum mpu3050_fullscale - indicates the full range of the sensor in deg/sec
*/
enum mpu3050_fullscale {
FS_250_DPS = 0,
FS_500_DPS,
FS_1000_DPS,
FS_2000_DPS,
};
/**
* enum mpu3050_lpf - indicates the low pass filter width
*/
enum mpu3050_lpf {
/* This implicity sets sample frequency to 8 kHz */
LPF_256_HZ_NOLPF = 0,
/* All others sets the sample frequency to 1 kHz */
LPF_188_HZ,
LPF_98_HZ,
LPF_42_HZ,
LPF_20_HZ,
LPF_10_HZ,
LPF_5_HZ,
LPF_2100_HZ_NOLPF,
};
enum mpu3050_axis {
AXIS_X = 0,
AXIS_Y,
AXIS_Z,
AXIS_MAX,
};
/**
* struct mpu3050 - instance state container for the device
* @dev: parent device for this instance
* @orientation: mounting matrix, flipped axis etc
* @map: regmap to reach the registers
* @lock: serialization lock to marshal all requests
* @irq: the IRQ used for this device
* @regs: the regulators to power this device
* @fullscale: the current fullscale setting for the device
* @lpf: digital low pass filter setting for the device
* @divisor: base frequency divider: divides 8 or 1 kHz
* @calibration: the three signed 16-bit calibration settings that
* get written into the offset registers for each axis to compensate
* for DC offsets
* @trig: trigger for the MPU-3050 interrupt, if present
* @hw_irq_trigger: hardware interrupt trigger is in use
* @irq_actl: interrupt is active low
* @irq_latch: latched IRQ, this means that it is a level IRQ
* @irq_opendrain: the interrupt line shall be configured open drain
* @pending_fifo_footer: tells us if there is a pending footer in the FIFO
* that we have to read out first when handling the FIFO
* @hw_timestamp: latest hardware timestamp from the trigger IRQ, when in
* use
* @i2cmux: an I2C mux reflecting the fact that this sensor is a hub with
* a pass-through I2C interface coming out of it: this device needs to be
* powered up in order to reach devices on the other side of this mux
*/
struct mpu3050 {
struct device *dev;
struct iio_mount_matrix orientation;
struct regmap *map;
struct mutex lock;
int irq;
struct regulator_bulk_data regs[2];
enum mpu3050_fullscale fullscale;
enum mpu3050_lpf lpf;
u8 divisor;
s16 calibration[3];
struct iio_trigger *trig;
bool hw_irq_trigger;
bool irq_actl;
bool irq_latch;
bool irq_opendrain;
bool pending_fifo_footer;
s64 hw_timestamp;
struct i2c_mux_core *i2cmux;
};
/* Probe called from different transports */
int mpu3050_common_probe(struct device *dev,
struct regmap *map,
int irq,
const char *name);
void mpu3050_common_remove(struct device *dev);
/* PM ops */
extern const struct dev_pm_ops mpu3050_dev_pm_ops;
@@ -0,0 +1,144 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
*/
#include <linux/iio/common/ssp_sensors.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include "../common/ssp_sensors/ssp_iio_sensor.h"
#define SSP_CHANNEL_COUNT 3
#define SSP_GYROSCOPE_NAME "ssp-gyroscope"
static const char ssp_gyro_name[] = SSP_GYROSCOPE_NAME;
enum ssp_gyro_3d_channel {
SSP_CHANNEL_SCAN_INDEX_X,
SSP_CHANNEL_SCAN_INDEX_Y,
SSP_CHANNEL_SCAN_INDEX_Z,
SSP_CHANNEL_SCAN_INDEX_TIME,
};
static int ssp_gyro_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
u32 t;
struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
t = ssp_get_sensor_delay(data, SSP_GYROSCOPE_SENSOR);
ssp_convert_to_freq(t, val, val2);
return IIO_VAL_INT_PLUS_MICRO;
default:
break;
}
return -EINVAL;
}
static int ssp_gyro_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long mask)
{
int ret;
struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
ret = ssp_convert_to_time(val, val2);
ret = ssp_change_delay(data, SSP_GYROSCOPE_SENSOR, ret);
if (ret < 0)
dev_err(&indio_dev->dev, "gyro sensor enable fail\n");
return ret;
default:
break;
}
return -EINVAL;
}
static const struct iio_info ssp_gyro_iio_info = {
.read_raw = &ssp_gyro_read_raw,
.write_raw = &ssp_gyro_write_raw,
};
static const unsigned long ssp_gyro_scan_mask[] = { 0x07, 0, };
static const struct iio_chan_spec ssp_gyro_channels[] = {
SSP_CHANNEL_AG(IIO_ANGL_VEL, IIO_MOD_X, SSP_CHANNEL_SCAN_INDEX_X),
SSP_CHANNEL_AG(IIO_ANGL_VEL, IIO_MOD_Y, SSP_CHANNEL_SCAN_INDEX_Y),
SSP_CHANNEL_AG(IIO_ANGL_VEL, IIO_MOD_Z, SSP_CHANNEL_SCAN_INDEX_Z),
IIO_CHAN_SOFT_TIMESTAMP(SSP_CHANNEL_SCAN_INDEX_TIME),
};
static int ssp_process_gyro_data(struct iio_dev *indio_dev, void *buf,
int64_t timestamp)
{
return ssp_common_process_data(indio_dev, buf, SSP_GYROSCOPE_SIZE,
timestamp);
}
static const struct iio_buffer_setup_ops ssp_gyro_buffer_ops = {
.postenable = &ssp_common_buffer_postenable,
.postdisable = &ssp_common_buffer_postdisable,
};
static int ssp_gyro_probe(struct platform_device *pdev)
{
int ret;
struct iio_dev *indio_dev;
struct ssp_sensor_data *spd;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*spd));
if (!indio_dev)
return -ENOMEM;
spd = iio_priv(indio_dev);
spd->process_data = ssp_process_gyro_data;
spd->type = SSP_GYROSCOPE_SENSOR;
indio_dev->name = ssp_gyro_name;
indio_dev->info = &ssp_gyro_iio_info;
indio_dev->channels = ssp_gyro_channels;
indio_dev->num_channels = ARRAY_SIZE(ssp_gyro_channels);
indio_dev->available_scan_masks = ssp_gyro_scan_mask;
ret = devm_iio_kfifo_buffer_setup(&pdev->dev, indio_dev,
&ssp_gyro_buffer_ops);
if (ret)
return ret;
platform_set_drvdata(pdev, indio_dev);
ret = devm_iio_device_register(&pdev->dev, indio_dev);
if (ret < 0)
return ret;
/* ssp registering should be done after all iio setup */
ssp_register_consumer(indio_dev, SSP_GYROSCOPE_SENSOR);
return 0;
}
static struct platform_driver ssp_gyro_driver = {
.driver = {
.name = SSP_GYROSCOPE_NAME,
},
.probe = ssp_gyro_probe,
};
module_platform_driver(ssp_gyro_driver);
MODULE_AUTHOR("Karol Wrona <k.wrona@samsung.com>");
MODULE_DESCRIPTION("Samsung sensorhub gyroscopes driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("IIO_SSP_SENSORS");
+39
View File
@@ -0,0 +1,39 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* STMicroelectronics gyroscopes driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
* v. 1.0.0
*/
#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 L3GD20H_GYRO_DEV_NAME "l3gd20h"
#define L3G4IS_GYRO_DEV_NAME "l3g4is_ui"
#define LSM330_GYRO_DEV_NAME "lsm330_gyro"
#define LSM9DS0_GYRO_DEV_NAME "lsm9ds0_gyro"
#ifdef CONFIG_IIO_BUFFER
int st_gyro_allocate_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;
}
#define ST_GYRO_TRIGGER_SET_STATE NULL
#endif /* CONFIG_IIO_BUFFER */
#endif /* ST_GYRO_H */
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics gyroscopes driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
*/
#include <linux/kernel.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.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_postenable(struct iio_dev *indio_dev)
{
int err;
err = st_sensors_set_axis_enable(indio_dev, indio_dev->active_scan_mask[0]);
if (err < 0)
return err;
err = st_sensors_set_enable(indio_dev, true);
if (err < 0)
goto st_gyro_buffer_enable_all_axis;
return 0;
st_gyro_buffer_enable_all_axis:
st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
return err;
}
static int st_gyro_buffer_predisable(struct iio_dev *indio_dev)
{
int err;
err = st_sensors_set_enable(indio_dev, false);
if (err < 0)
return err;
return st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
}
static const struct iio_buffer_setup_ops st_gyro_buffer_setup_ops = {
.postenable = &st_gyro_buffer_postenable,
.predisable = &st_gyro_buffer_predisable,
};
int st_gyro_allocate_ring(struct iio_dev *indio_dev)
{
return devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev,
NULL, &st_sensors_trigger_handler, &st_gyro_buffer_setup_ops);
}
+519
View File
@@ -0,0 +1,519 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics gyroscopes driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/interrupt.h>
#include <linux/sysfs.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.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_245DPS 245
#define ST_GYRO_FS_AVL_250DPS 250
#define ST_GYRO_FS_AVL_500DPS 500
#define ST_GYRO_FS_AVL_2000DPS 2000
static const struct iio_mount_matrix *
st_gyro_get_mount_matrix(const struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct st_sensor_data *gdata = iio_priv(indio_dev);
return &gdata->mount_matrix;
}
static const struct iio_chan_spec_ext_info st_gyro_mount_matrix_ext_info[] = {
IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, st_gyro_get_mount_matrix),
{ }
};
static const struct iio_chan_spec st_gyro_16bit_channels[] = {
ST_SENSORS_LSM_CHANNELS_EXT(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_gyro_mount_matrix_ext_info),
ST_SENSORS_LSM_CHANNELS_EXT(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_gyro_mount_matrix_ext_info),
ST_SENSORS_LSM_CHANNELS_EXT(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,
st_gyro_mount_matrix_ext_info),
IIO_CHAN_SOFT_TIMESTAMP(3)
};
static const struct st_sensor_settings st_gyro_sensors_settings[] = {
{
.wai = 0xd3,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = L3G4200D_GYRO_DEV_NAME,
[1] = LSM330DL_GYRO_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
.odr = {
.addr = 0x20,
.mask = 0xc0,
.odr_avl = {
{ .hz = 100, .value = 0x00, },
{ .hz = 200, .value = 0x01, },
{ .hz = 400, .value = 0x02, },
{ .hz = 800, .value = 0x03, },
},
},
.pw = {
.addr = 0x20,
.mask = 0x08,
.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 = 0x23,
.mask = 0x30,
.fs_avl = {
[0] = {
.num = ST_GYRO_FS_AVL_250DPS,
.value = 0x00,
.gain = IIO_DEGREE_TO_RAD(8750),
},
[1] = {
.num = ST_GYRO_FS_AVL_500DPS,
.value = 0x01,
.gain = IIO_DEGREE_TO_RAD(17500),
},
[2] = {
.num = ST_GYRO_FS_AVL_2000DPS,
.value = 0x02,
.gain = IIO_DEGREE_TO_RAD(70000),
},
},
},
.bdu = {
.addr = 0x23,
.mask = 0x80,
},
.drdy_irq = {
.int2 = {
.addr = 0x22,
.mask = 0x08,
},
/*
* The sensor has IHL (active low) and open
* drain settings, but only for INT1 and not
* for the DRDY line on INT2.
*/
.stat_drdy = {
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
.mask = 0x07,
},
},
.sim = {
.addr = 0x23,
.value = BIT(0),
},
.multi_read_bit = true,
.bootime = 2,
},
{
.wai = 0xd4,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.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 = 0x20,
.mask = 0xc0,
.odr_avl = {
{ .hz = 95, .value = 0x00, },
{ .hz = 190, .value = 0x01, },
{ .hz = 380, .value = 0x02, },
{ .hz = 760, .value = 0x03, },
},
},
.pw = {
.addr = 0x20,
.mask = 0x08,
.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 = 0x23,
.mask = 0x30,
.fs_avl = {
[0] = {
.num = ST_GYRO_FS_AVL_250DPS,
.value = 0x00,
.gain = IIO_DEGREE_TO_RAD(8750),
},
[1] = {
.num = ST_GYRO_FS_AVL_500DPS,
.value = 0x01,
.gain = IIO_DEGREE_TO_RAD(17500),
},
[2] = {
.num = ST_GYRO_FS_AVL_2000DPS,
.value = 0x02,
.gain = IIO_DEGREE_TO_RAD(70000),
},
},
},
.bdu = {
.addr = 0x23,
.mask = 0x80,
},
.drdy_irq = {
.int2 = {
.addr = 0x22,
.mask = 0x08,
},
/*
* The sensor has IHL (active low) and open
* drain settings, but only for INT1 and not
* for the DRDY line on INT2.
*/
.stat_drdy = {
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
.mask = 0x07,
},
},
.sim = {
.addr = 0x23,
.value = BIT(0),
},
.multi_read_bit = true,
.bootime = 2,
},
{
.wai = 0xd4,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = LSM9DS0_GYRO_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
.odr = {
.addr = 0x20,
.mask = GENMASK(7, 6),
.odr_avl = {
{ .hz = 95, .value = 0x00, },
{ .hz = 190, .value = 0x01, },
{ .hz = 380, .value = 0x02, },
{ .hz = 760, .value = 0x03, },
},
},
.pw = {
.addr = 0x20,
.mask = BIT(3),
.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 = 0x23,
.mask = GENMASK(5, 4),
.fs_avl = {
[0] = {
.num = ST_GYRO_FS_AVL_245DPS,
.value = 0x00,
.gain = IIO_DEGREE_TO_RAD(8750),
},
[1] = {
.num = ST_GYRO_FS_AVL_500DPS,
.value = 0x01,
.gain = IIO_DEGREE_TO_RAD(17500),
},
[2] = {
.num = ST_GYRO_FS_AVL_2000DPS,
.value = 0x02,
.gain = IIO_DEGREE_TO_RAD(70000),
},
},
},
.bdu = {
.addr = 0x23,
.mask = BIT(7),
},
.drdy_irq = {
.int2 = {
.addr = 0x22,
.mask = BIT(3),
},
/*
* The sensor has IHL (active low) and open
* drain settings, but only for INT1 and not
* for the DRDY line on INT2.
*/
.stat_drdy = {
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
.mask = GENMASK(2, 0),
},
},
.sim = {
.addr = 0x23,
.value = BIT(0),
},
.multi_read_bit = true,
.bootime = 2,
},
{
.wai = 0xd7,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = L3GD20H_GYRO_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
.odr = {
.addr = 0x20,
.mask = 0xc0,
.odr_avl = {
{ .hz = 100, .value = 0x00, },
{ .hz = 200, .value = 0x01, },
{ .hz = 400, .value = 0x02, },
{ .hz = 800, .value = 0x03, },
},
},
.pw = {
.addr = 0x20,
.mask = 0x08,
.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 = 0x23,
.mask = 0x30,
.fs_avl = {
[0] = {
.num = ST_GYRO_FS_AVL_245DPS,
.value = 0x00,
.gain = IIO_DEGREE_TO_RAD(8750),
},
[1] = {
.num = ST_GYRO_FS_AVL_500DPS,
.value = 0x01,
.gain = IIO_DEGREE_TO_RAD(17500),
},
[2] = {
.num = ST_GYRO_FS_AVL_2000DPS,
.value = 0x02,
.gain = IIO_DEGREE_TO_RAD(70000),
},
},
},
.bdu = {
.addr = 0x23,
.mask = 0x80,
},
.drdy_irq = {
.int2 = {
.addr = 0x22,
.mask = 0x08,
},
/*
* The sensor has IHL (active low) and open
* drain settings, but only for INT1 and not
* for the DRDY line on INT2.
*/
.stat_drdy = {
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
.mask = 0x07,
},
},
.sim = {
.addr = 0x23,
.value = BIT(0),
},
.multi_read_bit = true,
.bootime = 2,
},
};
/* DRDY on gyros is available only on INT2 pin */
static const struct st_sensors_platform_data gyro_pdata = {
.drdy_int_pin = 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)
{
switch (mask) {
case IIO_CHAN_INFO_SCALE:
return st_sensors_set_fullscale_by_gain(indio_dev, val2);
case IIO_CHAN_INFO_SAMP_FREQ:
if (val2)
return -EINVAL;
return st_sensors_set_odr(indio_dev, val);
default:
return -EINVAL;
}
}
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 = {
.attrs = &st_gyro_attribute_group,
.read_raw = &st_gyro_read_raw,
.write_raw = &st_gyro_write_raw,
.debugfs_reg_access = &st_sensors_debugfs_reg_access,
};
#ifdef CONFIG_IIO_TRIGGER
static const struct iio_trigger_ops st_gyro_trigger_ops = {
.set_trigger_state = ST_GYRO_TRIGGER_SET_STATE,
.validate_device = st_sensors_validate_device,
};
#define ST_GYRO_TRIGGER_OPS (&st_gyro_trigger_ops)
#else
#define ST_GYRO_TRIGGER_OPS NULL
#endif
/*
* st_gyro_get_settings() - get sensor settings from device name
* @name: device name buffer reference.
*
* Return: valid reference on success, NULL otherwise.
*/
const struct st_sensor_settings *st_gyro_get_settings(const char *name)
{
int index = st_sensors_get_settings_index(name,
st_gyro_sensors_settings,
ARRAY_SIZE(st_gyro_sensors_settings));
if (index < 0)
return NULL;
return &st_gyro_sensors_settings[index];
}
EXPORT_SYMBOL_NS(st_gyro_get_settings, "IIO_ST_SENSORS");
int st_gyro_common_probe(struct iio_dev *indio_dev)
{
struct st_sensor_data *gdata = iio_priv(indio_dev);
struct st_sensors_platform_data *pdata;
struct device *parent = indio_dev->dev.parent;
int err;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &gyro_info;
err = st_sensors_verify_id(indio_dev);
if (err < 0)
return err;
gdata->num_data_channels = ST_GYRO_NUMBER_DATA_CHANNELS;
indio_dev->channels = gdata->sensor_settings->ch;
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
err = iio_read_mount_matrix(parent, &gdata->mount_matrix);
if (err)
return err;
gdata->current_fullscale = &gdata->sensor_settings->fs.fs_avl[0];
gdata->odr = gdata->sensor_settings->odr.odr_avl[0].hz;
pdata = (struct st_sensors_platform_data *)&gyro_pdata;
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 (gdata->irq > 0) {
err = st_sensors_allocate_trigger(indio_dev,
ST_GYRO_TRIGGER_OPS);
if (err < 0)
return err;
}
return devm_iio_device_register(parent, indio_dev);
}
EXPORT_SYMBOL_NS(st_gyro_common_probe, "IIO_ST_SENSORS");
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
MODULE_DESCRIPTION("STMicroelectronics gyroscopes driver");
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS("IIO_ST_SENSORS");
+121
View File
@@ -0,0 +1,121 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics gyroscopes driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/common/st_sensors.h>
#include <linux/iio/common/st_sensors_i2c.h>
#include "st_gyro.h"
static 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,l3gd20h-gyro",
.data = L3GD20H_GYRO_DEV_NAME,
},
{
.compatible = "st,l3g4is-gyro",
.data = L3G4IS_GYRO_DEV_NAME,
},
{
.compatible = "st,lsm330-gyro",
.data = LSM330_GYRO_DEV_NAME,
},
{
.compatible = "st,lsm9ds0-gyro",
.data = LSM9DS0_GYRO_DEV_NAME,
},
{ }
};
MODULE_DEVICE_TABLE(of, st_gyro_of_match);
static int st_gyro_i2c_probe(struct i2c_client *client)
{
const struct st_sensor_settings *settings;
struct st_sensor_data *gdata;
struct iio_dev *indio_dev;
int err;
st_sensors_dev_name_probe(&client->dev, client->name, sizeof(client->name));
settings = st_gyro_get_settings(client->name);
if (!settings) {
dev_err(&client->dev, "device name %s not recognized.\n",
client->name);
return -ENODEV;
}
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*gdata));
if (!indio_dev)
return -ENOMEM;
gdata = iio_priv(indio_dev);
gdata->sensor_settings = (struct st_sensor_settings *)settings;
err = st_sensors_i2c_configure(indio_dev, client);
if (err < 0)
return err;
err = st_sensors_power_enable(indio_dev);
if (err)
return err;
return st_gyro_common_probe(indio_dev);
}
static const struct i2c_device_id st_gyro_id_table[] = {
{ .name = L3G4200D_GYRO_DEV_NAME },
{ .name = LSM330D_GYRO_DEV_NAME },
{ .name = LSM330DL_GYRO_DEV_NAME },
{ .name = LSM330DLC_GYRO_DEV_NAME },
{ .name = L3GD20_GYRO_DEV_NAME },
{ .name = L3GD20H_GYRO_DEV_NAME },
{ .name = L3G4IS_GYRO_DEV_NAME },
{ .name = LSM330_GYRO_DEV_NAME },
{ .name = LSM9DS0_GYRO_DEV_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, st_gyro_id_table);
static struct i2c_driver st_gyro_driver = {
.driver = {
.name = "st-gyro-i2c",
.of_match_table = st_gyro_of_match,
},
.probe = st_gyro_i2c_probe,
.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");
MODULE_IMPORT_NS("IIO_ST_SENSORS");
+126
View File
@@ -0,0 +1,126 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics gyroscopes driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
*/
#include <linux/kernel.h>
#include <linux/module.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"
/*
* For new single-chip sensors use <device_name> as compatible string.
* For old single-chip devices keep <device_name>-gyro to maintain
* compatibility
*/
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,l3gd20h-gyro",
.data = L3GD20H_GYRO_DEV_NAME,
},
{
.compatible = "st,l3g4is-gyro",
.data = L3G4IS_GYRO_DEV_NAME,
},
{
.compatible = "st,lsm330-gyro",
.data = LSM330_GYRO_DEV_NAME,
},
{
.compatible = "st,lsm9ds0-gyro",
.data = LSM9DS0_GYRO_DEV_NAME,
},
{ }
};
MODULE_DEVICE_TABLE(of, st_gyro_of_match);
static int st_gyro_spi_probe(struct spi_device *spi)
{
const struct st_sensor_settings *settings;
struct st_sensor_data *gdata;
struct iio_dev *indio_dev;
int err;
st_sensors_dev_name_probe(&spi->dev, spi->modalias, sizeof(spi->modalias));
settings = st_gyro_get_settings(spi->modalias);
if (!settings) {
dev_err(&spi->dev, "device name %s not recognized.\n",
spi->modalias);
return -ENODEV;
}
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*gdata));
if (!indio_dev)
return -ENOMEM;
gdata = iio_priv(indio_dev);
gdata->sensor_settings = (struct st_sensor_settings *)settings;
err = st_sensors_spi_configure(indio_dev, spi);
if (err < 0)
return err;
err = st_sensors_power_enable(indio_dev);
if (err)
return err;
return st_gyro_common_probe(indio_dev);
}
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 },
{ L3GD20H_GYRO_DEV_NAME },
{ L3G4IS_GYRO_DEV_NAME },
{ LSM330_GYRO_DEV_NAME },
{ LSM9DS0_GYRO_DEV_NAME },
{ }
};
MODULE_DEVICE_TABLE(spi, st_gyro_id_table);
static struct spi_driver st_gyro_driver = {
.driver = {
.name = "st-gyro-spi",
.of_match_table = st_gyro_of_match,
},
.probe = st_gyro_spi_probe,
.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");
MODULE_IMPORT_NS("IIO_ST_SENSORS");