Second set of IIO new drivers, cleanups and fixes for the 3.9 cycle.
This second version is due to a little fixup for an include path being added to the tsl2563 move out of staging after a report from Fengguang Wu and the 0-day kernel build testing backend. Minor bits: 1) A Kconfig dependency fix for the max1363 driver that has been causing some autobuilder fails in Linux Next. 2) Removal of a stale makefile entry 3) Fix an incorrect arguement for a sizeof call 4) Duplicate code removal in tsl2x7x driver 5) A missing spin lock init in hid-sensor-time New features: 1) mxs adc driver gains support for touchscreen special functions 2) mxs driver gainst supprot for the MX23 and dt entries added 3) adis16400 gains adis16448 support and some additional bells and whistles Moves out of staging. 1) adis16400 - a venerable driver gets a make over and moves out of staging. 2) Kxsd9 moved out fo staging 3) adis16080 gets cleaned up and moved out of staging 4) tsl2563 gets a little cleaned up and move out of staging Removals 1) sw_ring is killed off with all remaining drivers converted to kfifo. This has been scheduled for a long time since we switched to kfifo. There is demand for a high performance alternative, but this was never it and I'm glad to see this vestage of IIOs youth gone once and for all! -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.19 (GNU/Linux) iQIcBAABAgAGBQJRBWafAAoJEFSFNJnE9BaItLwP/2GaXlNcsN2rzlALMR13qffT cm8bWf5njuUCZ9RLxNMZ4I0hfuB3lZ3cV0jS8r748OnmRWCbS3Rxu+PmZcedQjfG L1puuJamQbSN+atussyHDZrOt7QGOQHEitfRKCMnw6aRDyi/l4UVjUQZN07QTD0a wN/FIpDHT9+LdY9S5zIoWRmBvyCQhiZqO6Pa0pgxOEuA87uKY2FzV/MCWoDOkpLj OdyrKHRBD5CtEGR0bIys3G1GW8HBYDHaS8Do3Kd7arBMvqg6CW1uSUAjz0XnDCnC 81+oAsCLeO2Bl2bAv4km44LAsgMHBSlbZTB91R1oq9SzfpQ1CdRXdK4KdhCz0Olp x/OC7QzmoYxhiDBbKcYREB3/AKQbEHcj3F9FnvOn+wOSaq31Q/Seq5GlXirZWrpg Frpbxenf5SW3IpZJCBvh1rfcjTw33r569T6avizhrX/uokEhz/3WEb9FBAtJYWag VwubRkjkoakmYLdvEHi/DCNXPNsXrNrU3w3O18+ulucvCTojPIx/+sXE829dTqcK BG944B8o9lIfwmKqdoFsGtTBxzx8204p8RS9TLJH40pmjZ4eW6zGZXbBJvNIChI8 1nOcksrcYVD9TOZ4GPm8jn2yCXiOHpfN2+p91rrgl5s4rfYNDZELnJMaOVup+ccR 1uDMgKEoPBX5wCms1r// =kkCb -----END PGP SIGNATURE----- Merge tag 'iio-for-3.9b-v2' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next Jonathan writes: Second set of IIO new drivers, cleanups and fixes for the 3.9 cycle. This second version is due to a little fixup for an include path being added to the tsl2563 move out of staging after a report from Fengguang Wu and the 0-day kernel build testing backend. Minor bits: 1) A Kconfig dependency fix for the max1363 driver that has been causing some autobuilder fails in Linux Next. 2) Removal of a stale makefile entry 3) Fix an incorrect arguement for a sizeof call 4) Duplicate code removal in tsl2x7x driver 5) A missing spin lock init in hid-sensor-time New features: 1) mxs adc driver gains support for touchscreen special functions 2) mxs driver gainst supprot for the MX23 and dt entries added 3) adis16400 gains adis16448 support and some additional bells and whistles Moves out of staging. 1) adis16400 - a venerable driver gets a make over and moves out of staging. 2) Kxsd9 moved out fo staging 3) adis16080 gets cleaned up and moved out of staging 4) tsl2563 gets a little cleaned up and move out of staging Removals 1) sw_ring is killed off with all remaining drivers converted to kfifo. This has been scheduled for a long time since we switched to kfifo. There is demand for a high performance alternative, but this was never it and I'm glad to see this vestage of IIOs youth gone once and for all!
This commit is contained in:
commit
83eaeec5b3
|
@ -5,6 +5,12 @@ Required properties:
|
|||
- reg: Address and length of the register set for the device
|
||||
- interrupts: Should contain the LRADC interrupts
|
||||
|
||||
Optional properties:
|
||||
- fsl,lradc-touchscreen-wires: Number of wires used to connect the touchscreen
|
||||
to LRADC. Valid value is either 4 or 5. If this
|
||||
property is not present, then the touchscreen is
|
||||
disabled.
|
||||
|
||||
Examples:
|
||||
|
||||
lradc@80050000 {
|
||||
|
|
|
@ -391,7 +391,9 @@
|
|||
};
|
||||
|
||||
lradc@80050000 {
|
||||
compatible = "fsl,imx23-lradc";
|
||||
reg = <0x80050000 0x2000>;
|
||||
interrupts = <36 37 38 39 40 41 42 43 44>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
#include <media/si4713.h>
|
||||
#include <linux/leds-lp5523.h>
|
||||
|
||||
#include <../drivers/staging/iio/light/tsl2563.h>
|
||||
#include <linux/platform_data/tsl2563.h>
|
||||
#include <linux/lis3lv02d.h>
|
||||
|
||||
#if defined(CONFIG_IR_RX51) || defined(CONFIG_IR_RX51_MODULE)
|
||||
|
|
|
@ -14,4 +14,11 @@ config HID_SENSOR_ACCEL_3D
|
|||
Say yes here to build support for the HID SENSOR
|
||||
accelerometers 3D.
|
||||
|
||||
config KXSD9
|
||||
tristate "Kionix KXSD9 Accelerometer Driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for the Kionix KXSD9 accelerometer.
|
||||
Currently this only supports the device via an SPI interface.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -3,3 +3,4 @@
|
|||
#
|
||||
|
||||
obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
|
||||
obj-$(CONFIG_KXSD9) += kxsd9.o
|
||||
|
|
|
@ -226,7 +226,7 @@ static int kxsd9_probe(struct spi_device *spi)
|
|||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct kxsd9_state *st;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
indio_dev = iio_device_alloc(sizeof(*st));
|
||||
if (indio_dev == NULL) {
|
||||
|
@ -245,14 +245,14 @@ static int kxsd9_probe(struct spi_device *spi)
|
|||
indio_dev->info = &kxsd9_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
|
||||
spi->mode = SPI_MODE_0;
|
||||
spi_setup(spi);
|
||||
kxsd9_power_up(st);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_dev:
|
|
@ -100,10 +100,8 @@ config LP8788_ADC
|
|||
config MAX1363
|
||||
tristate "Maxim max1363 ADC driver"
|
||||
depends on I2C
|
||||
select IIO_TRIGGER
|
||||
select MAX1363_RING_BUFFER
|
||||
select IIO_BUFFER
|
||||
select IIO_KFIFO_BUF
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for many Maxim i2c analog to digital
|
||||
converters (ADC). (max1361, max1362, max1363, max1364, max1036,
|
||||
|
|
|
@ -3,6 +3,13 @@
|
|||
#
|
||||
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 ADIS16136
|
||||
tristate "Analog devices ADIS16136 and similar gyroscopes driver"
|
||||
depends on SPI_MASTER
|
||||
|
|
|
@ -2,5 +2,6 @@
|
|||
# Makefile for industrial I/O gyroscope sensor drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_ADIS16080) += adis16080.o
|
||||
obj-$(CONFIG_ADIS16136) += adis16136.o
|
||||
obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
|
||||
|
|
|
@ -29,48 +29,50 @@
|
|||
|
||||
#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
|
||||
* @buf_lock: mutex to protect tx and rx
|
||||
**/
|
||||
struct adis16080_state {
|
||||
struct spi_device *us;
|
||||
struct mutex buf_lock;
|
||||
const struct adis16080_chip_info *info;
|
||||
|
||||
u8 buf[2] ____cacheline_aligned;
|
||||
__be16 buf ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int adis16080_spi_write(struct iio_dev *indio_dev,
|
||||
u16 val)
|
||||
static int adis16080_read_sample(struct iio_dev *indio_dev,
|
||||
u16 addr, int *val)
|
||||
{
|
||||
int ret;
|
||||
struct adis16080_state *st = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->buf[0] = val >> 8;
|
||||
st->buf[1] = val;
|
||||
|
||||
ret = spi_write(st->us, st->buf, 2);
|
||||
mutex_unlock(&st->buf_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16080_spi_read(struct iio_dev *indio_dev,
|
||||
u16 *val)
|
||||
{
|
||||
struct spi_message m;
|
||||
int ret;
|
||||
struct adis16080_state *st = iio_priv(indio_dev);
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
.tx_buf = &st->buf,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
}, {
|
||||
.rx_buf = &st->buf,
|
||||
.len = 2,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->buf = cpu_to_be16(addr | ADIS16080_DIN_WRITE);
|
||||
|
||||
ret = spi_read(st->us, st->buf, 2);
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&t[0], &m);
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
|
||||
ret = spi_sync(st->us, &m);
|
||||
if (ret == 0)
|
||||
*val = sign_extend32(((st->buf[0] & 0xF) << 8) | st->buf[1], 11);
|
||||
mutex_unlock(&st->buf_lock);
|
||||
*val = sign_extend32(be16_to_cpu(st->buf), 11);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -81,28 +83,52 @@ static int adis16080_read_raw(struct iio_dev *indio_dev,
|
|||
int *val2,
|
||||
long mask)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
u16 ut = 0;
|
||||
/* Take the iio_dev status lock */
|
||||
struct adis16080_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = adis16080_spi_write(indio_dev,
|
||||
chan->address |
|
||||
ADIS16080_DIN_WRITE);
|
||||
if (ret < 0)
|
||||
break;
|
||||
ret = adis16080_spi_read(indio_dev, &ut);
|
||||
if (ret < 0)
|
||||
break;
|
||||
*val = ut;
|
||||
ret = IIO_VAL_INT;
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = adis16080_read_sample(indio_dev, chan->address, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret ? ret : IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL_VEL:
|
||||
*val = st->info->scale_val;
|
||||
*val2 = st->info->scale_val2;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
case IIO_VOLTAGE:
|
||||
/* VREF = 5V, 12 bits */
|
||||
*val = 5000;
|
||||
*val2 = 12;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_TEMP:
|
||||
/* 85 C = 585, 25 C = 0 */
|
||||
*val = 85000 - 25000;
|
||||
*val2 = 585;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
/* 2.5 V = 0 */
|
||||
*val = 2048;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_TEMP:
|
||||
/* 85 C = 585, 25 C = 0 */
|
||||
*val = DIV_ROUND_CLOSEST(25 * 585, 85 - 25);
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adis16080_channels[] = {
|
||||
|
@ -110,25 +136,32 @@ static const struct iio_chan_spec adis16080_channels[] = {
|
|||
.type = IIO_ANGL_VEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Z,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
|
||||
.address = ADIS16080_DIN_GYRO,
|
||||
}, {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT,
|
||||
.address = ADIS16080_DIN_AIN1,
|
||||
}, {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 1,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT,
|
||||
.address = ADIS16080_DIN_AIN2,
|
||||
}, {
|
||||
.type = IIO_TEMP,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT,
|
||||
.address = ADIS16080_DIN_TEMP,
|
||||
}
|
||||
};
|
||||
|
@ -138,8 +171,27 @@ static const struct iio_info adis16080_info = {
|
|||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
enum {
|
||||
ID_ADIS16080,
|
||||
ID_ADIS16100,
|
||||
};
|
||||
|
||||
static const struct adis16080_chip_info adis16080_chip_info[] = {
|
||||
[ID_ADIS16080] = {
|
||||
/* 80 degree = 819, 819 rad = 46925 degree */
|
||||
.scale_val = 80,
|
||||
.scale_val2 = 46925,
|
||||
},
|
||||
[ID_ADIS16100] = {
|
||||
/* 300 degree = 1230, 1230 rad = 70474 degree */
|
||||
.scale_val = 300,
|
||||
.scale_val2 = 70474,
|
||||
},
|
||||
};
|
||||
|
||||
static int adis16080_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
int ret;
|
||||
struct adis16080_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
|
@ -156,7 +208,7 @@ static int adis16080_probe(struct spi_device *spi)
|
|||
|
||||
/* Allocate the comms buffers */
|
||||
st->us = spi;
|
||||
mutex_init(&st->buf_lock);
|
||||
st->info = &adis16080_chip_info[id->driver_data];
|
||||
|
||||
indio_dev->name = spi->dev.driver->name;
|
||||
indio_dev->channels = adis16080_channels;
|
||||
|
@ -176,7 +228,6 @@ error_ret:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* fixme, confirm ordering in this function */
|
||||
static int adis16080_remove(struct spi_device *spi)
|
||||
{
|
||||
iio_device_unregister(spi_get_drvdata(spi));
|
||||
|
@ -185,6 +236,13 @@ static int adis16080_remove(struct spi_device *spi)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id adis16080_ids[] = {
|
||||
{ "adis16080", ID_ADIS16080 },
|
||||
{ "adis16100", ID_ADIS16100 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, adis16080_ids);
|
||||
|
||||
static struct spi_driver adis16080_driver = {
|
||||
.driver = {
|
||||
.name = "adis16080",
|
||||
|
@ -192,10 +250,10 @@ static struct spi_driver adis16080_driver = {
|
|||
},
|
||||
.probe = adis16080_probe,
|
||||
.remove = adis16080_remove,
|
||||
.id_table = adis16080_ids,
|
||||
};
|
||||
module_spi_driver(adis16080_driver);
|
||||
|
||||
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices ADIS16080/100 Yaw Rate Gyroscope Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("spi:adis16080");
|
|
@ -3,6 +3,17 @@
|
|||
#
|
||||
menu "Inertial measurement units"
|
||||
|
||||
config ADIS16400
|
||||
tristate "Analog Devices ADIS16400 and similar IMU 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 adis16300, adis16344,
|
||||
adis16350, adis16354, adis16355, adis16360, adis16362, adis16364,
|
||||
adis16365, adis16400 and adis16405 triaxial inertial sensors
|
||||
(adis16400 series also have magnetometers).
|
||||
|
||||
config ADIS16480
|
||||
tristate "Analog Devices ADIS16480 and similar IMU driver"
|
||||
depends on SPI
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
# Makefile for Inertial Measurement Units
|
||||
#
|
||||
|
||||
adis16400-y := adis16400_core.o
|
||||
adis16400-$(CONFIG_IIO_BUFFER) += adis16400_buffer.o
|
||||
obj-$(CONFIG_ADIS16400) += adis16400.o
|
||||
obj-$(CONFIG_ADIS16480) += adis16480.o
|
||||
|
||||
adis_lib-y += adis.o
|
||||
|
|
|
@ -17,12 +17,11 @@
|
|||
#ifndef SPI_ADIS16400_H_
|
||||
#define SPI_ADIS16400_H_
|
||||
|
||||
#include <linux/iio/imu/adis.h>
|
||||
|
||||
#define ADIS16400_STARTUP_DELAY 290 /* ms */
|
||||
#define ADIS16400_MTEST_DELAY 90 /* ms */
|
||||
|
||||
#define ADIS16400_READ_REG(a) a
|
||||
#define ADIS16400_WRITE_REG(a) ((a) | 0x80)
|
||||
|
||||
#define ADIS16400_FLASH_CNT 0x00 /* Flash memory write count */
|
||||
#define ADIS16400_SUPPLY_OUT 0x02 /* Power supply measurement */
|
||||
#define ADIS16400_XGYRO_OUT 0x04 /* X-axis gyroscope output */
|
||||
|
@ -45,6 +44,9 @@
|
|||
#define ADIS16300_ROLL_OUT 0x14 /* Y axis inclinometer output measurement */
|
||||
#define ADIS16300_AUX_ADC 0x16 /* Auxiliary ADC measurement */
|
||||
|
||||
#define ADIS16448_BARO_OUT 0x16 /* Barometric pressure output */
|
||||
#define ADIS16448_TEMP_OUT 0x18 /* Temperature output */
|
||||
|
||||
/* Calibration parameters */
|
||||
#define ADIS16400_XGYRO_OFF 0x1A /* X-axis gyroscope bias offset factor */
|
||||
#define ADIS16400_YGYRO_OFF 0x1C /* Y-axis gyroscope bias offset factor */
|
||||
|
@ -75,7 +77,10 @@
|
|||
#define ADIS16400_ALM_CTRL 0x48 /* Alarm control */
|
||||
#define ADIS16400_AUX_DAC 0x4A /* Auxiliary DAC data */
|
||||
|
||||
#define ADIS16334_LOT_ID1 0x52 /* Lot identification code 1 */
|
||||
#define ADIS16334_LOT_ID2 0x54 /* Lot identification code 2 */
|
||||
#define ADIS16400_PRODUCT_ID 0x56 /* Product identifier */
|
||||
#define ADIS16334_SERIAL_NUMBER 0x58 /* Serial number, lot specific */
|
||||
|
||||
#define ADIS16400_ERROR_ACTIVE (1<<14)
|
||||
#define ADIS16400_NEW_DATA (1<<14)
|
||||
|
@ -96,21 +101,21 @@
|
|||
#define ADIS16400_SMPL_PRD_DIV_MASK 0x7F
|
||||
|
||||
/* DIAG_STAT */
|
||||
#define ADIS16400_DIAG_STAT_ZACCL_FAIL (1<<15)
|
||||
#define ADIS16400_DIAG_STAT_YACCL_FAIL (1<<14)
|
||||
#define ADIS16400_DIAG_STAT_XACCL_FAIL (1<<13)
|
||||
#define ADIS16400_DIAG_STAT_XGYRO_FAIL (1<<12)
|
||||
#define ADIS16400_DIAG_STAT_YGYRO_FAIL (1<<11)
|
||||
#define ADIS16400_DIAG_STAT_ZGYRO_FAIL (1<<10)
|
||||
#define ADIS16400_DIAG_STAT_ALARM2 (1<<9)
|
||||
#define ADIS16400_DIAG_STAT_ALARM1 (1<<8)
|
||||
#define ADIS16400_DIAG_STAT_FLASH_CHK (1<<6)
|
||||
#define ADIS16400_DIAG_STAT_SELF_TEST (1<<5)
|
||||
#define ADIS16400_DIAG_STAT_OVERFLOW (1<<4)
|
||||
#define ADIS16400_DIAG_STAT_SPI_FAIL (1<<3)
|
||||
#define ADIS16400_DIAG_STAT_FLASH_UPT (1<<2)
|
||||
#define ADIS16400_DIAG_STAT_POWER_HIGH (1<<1)
|
||||
#define ADIS16400_DIAG_STAT_POWER_LOW (1<<0)
|
||||
#define ADIS16400_DIAG_STAT_ZACCL_FAIL 15
|
||||
#define ADIS16400_DIAG_STAT_YACCL_FAIL 14
|
||||
#define ADIS16400_DIAG_STAT_XACCL_FAIL 13
|
||||
#define ADIS16400_DIAG_STAT_XGYRO_FAIL 12
|
||||
#define ADIS16400_DIAG_STAT_YGYRO_FAIL 11
|
||||
#define ADIS16400_DIAG_STAT_ZGYRO_FAIL 10
|
||||
#define ADIS16400_DIAG_STAT_ALARM2 9
|
||||
#define ADIS16400_DIAG_STAT_ALARM1 8
|
||||
#define ADIS16400_DIAG_STAT_FLASH_CHK 6
|
||||
#define ADIS16400_DIAG_STAT_SELF_TEST 5
|
||||
#define ADIS16400_DIAG_STAT_OVERFLOW 4
|
||||
#define ADIS16400_DIAG_STAT_SPI_FAIL 3
|
||||
#define ADIS16400_DIAG_STAT_FLASH_UPT 2
|
||||
#define ADIS16400_DIAG_STAT_POWER_HIGH 1
|
||||
#define ADIS16400_DIAG_STAT_POWER_LOW 0
|
||||
|
||||
/* GLOB_CMD */
|
||||
#define ADIS16400_GLOB_CMD_SW_RESET (1<<7)
|
||||
|
@ -126,9 +131,6 @@
|
|||
#define ADIS16334_RATE_DIV_SHIFT 8
|
||||
#define ADIS16334_RATE_INT_CLK BIT(0)
|
||||
|
||||
#define ADIS16400_MAX_TX 24
|
||||
#define ADIS16400_MAX_RX 24
|
||||
|
||||
#define ADIS16400_SPI_SLOW (u32)(300 * 1000)
|
||||
#define ADIS16400_SPI_BURST (u32)(1000 * 1000)
|
||||
#define ADIS16400_SPI_FAST (u32)(2000 * 1000)
|
||||
|
@ -136,6 +138,9 @@
|
|||
#define ADIS16400_HAS_PROD_ID BIT(0)
|
||||
#define ADIS16400_NO_BURST BIT(1)
|
||||
#define ADIS16400_HAS_SLOW_MODE BIT(2)
|
||||
#define ADIS16400_HAS_SERIAL_NUMBER BIT(3)
|
||||
|
||||
struct adis16400_state;
|
||||
|
||||
struct adis16400_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
|
@ -145,95 +150,63 @@ struct adis16400_chip_info {
|
|||
unsigned int accel_scale_micro;
|
||||
int temp_scale_nano;
|
||||
int temp_offset;
|
||||
unsigned long default_scan_mask;
|
||||
int (*set_freq)(struct iio_dev *indio_dev, unsigned int freq);
|
||||
int (*get_freq)(struct iio_dev *indio_dev);
|
||||
int (*set_freq)(struct adis16400_state *st, unsigned int freq);
|
||||
int (*get_freq)(struct adis16400_state *st);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct adis16400_state - device instance specific data
|
||||
* @us: actual spi_device
|
||||
* @trig: data ready trigger registered with iio
|
||||
* @tx: transmit buffer
|
||||
* @rx: receive buffer
|
||||
* @buf_lock: mutex to protect tx and rx
|
||||
* @filt_int: integer part of requested filter frequency
|
||||
* @variant: chip variant info
|
||||
* @filt_int: integer part of requested filter frequency
|
||||
* @adis: adis device
|
||||
**/
|
||||
struct adis16400_state {
|
||||
struct spi_device *us;
|
||||
struct iio_trigger *trig;
|
||||
struct mutex buf_lock;
|
||||
struct adis16400_chip_info *variant;
|
||||
int filt_int;
|
||||
|
||||
u8 tx[ADIS16400_MAX_TX] ____cacheline_aligned;
|
||||
u8 rx[ADIS16400_MAX_RX] ____cacheline_aligned;
|
||||
struct adis adis;
|
||||
};
|
||||
|
||||
int adis16400_set_irq(struct iio_dev *indio_dev, bool enable);
|
||||
|
||||
/* At the moment triggers are only used for ring buffer
|
||||
* filling. This may change!
|
||||
*/
|
||||
|
||||
#define ADIS16400_SCAN_SUPPLY 0
|
||||
#define ADIS16400_SCAN_GYRO_X 1
|
||||
#define ADIS16400_SCAN_GYRO_Y 2
|
||||
#define ADIS16400_SCAN_GYRO_Z 3
|
||||
#define ADIS16400_SCAN_ACC_X 4
|
||||
#define ADIS16400_SCAN_ACC_Y 5
|
||||
#define ADIS16400_SCAN_ACC_Z 6
|
||||
#define ADIS16400_SCAN_MAGN_X 7
|
||||
#define ADIS16350_SCAN_TEMP_X 7
|
||||
#define ADIS16400_SCAN_MAGN_Y 8
|
||||
#define ADIS16350_SCAN_TEMP_Y 8
|
||||
#define ADIS16400_SCAN_MAGN_Z 9
|
||||
#define ADIS16350_SCAN_TEMP_Z 9
|
||||
#define ADIS16400_SCAN_TEMP 10
|
||||
#define ADIS16350_SCAN_ADC_0 10
|
||||
#define ADIS16400_SCAN_ADC_0 11
|
||||
#define ADIS16300_SCAN_INCLI_X 12
|
||||
#define ADIS16300_SCAN_INCLI_Y 13
|
||||
enum {
|
||||
ADIS16400_SCAN_SUPPLY,
|
||||
ADIS16400_SCAN_GYRO_X,
|
||||
ADIS16400_SCAN_GYRO_Y,
|
||||
ADIS16400_SCAN_GYRO_Z,
|
||||
ADIS16400_SCAN_ACC_X,
|
||||
ADIS16400_SCAN_ACC_Y,
|
||||
ADIS16400_SCAN_ACC_Z,
|
||||
ADIS16400_SCAN_MAGN_X,
|
||||
ADIS16400_SCAN_MAGN_Y,
|
||||
ADIS16400_SCAN_MAGN_Z,
|
||||
ADIS16400_SCAN_BARO,
|
||||
ADIS16350_SCAN_TEMP_X,
|
||||
ADIS16350_SCAN_TEMP_Y,
|
||||
ADIS16350_SCAN_TEMP_Z,
|
||||
ADIS16300_SCAN_INCLI_X,
|
||||
ADIS16300_SCAN_INCLI_Y,
|
||||
ADIS16400_SCAN_ADC,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IIO_BUFFER
|
||||
void adis16400_remove_trigger(struct iio_dev *indio_dev);
|
||||
int adis16400_probe_trigger(struct iio_dev *indio_dev);
|
||||
|
||||
ssize_t adis16400_read_data_from_ring(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
|
||||
|
||||
int adis16400_configure_ring(struct iio_dev *indio_dev);
|
||||
void adis16400_unconfigure_ring(struct iio_dev *indio_dev);
|
||||
int adis16400_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *scan_mask);
|
||||
irqreturn_t adis16400_trigger_handler(int irq, void *p);
|
||||
|
||||
#else /* CONFIG_IIO_BUFFER */
|
||||
|
||||
static inline void adis16400_remove_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int adis16400_probe_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline ssize_t
|
||||
adis16400_read_data_from_ring(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adis16400_configure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void adis16400_unconfigure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
}
|
||||
#define adis16400_update_scan_mode NULL
|
||||
#define adis16400_trigger_handler NULL
|
||||
|
||||
#endif /* CONFIG_IIO_BUFFER */
|
||||
|
||||
#endif /* SPI_ADIS16400_H_ */
|
96
drivers/iio/imu/adis16400_buffer.c
Normal file
96
drivers/iio/imu/adis16400_buffer.c
Normal file
|
@ -0,0 +1,96 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
|
||||
#include "adis16400.h"
|
||||
|
||||
int adis16400_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *scan_mask)
|
||||
{
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
struct adis *adis = &st->adis;
|
||||
uint16_t *tx, *rx;
|
||||
|
||||
if (st->variant->flags & ADIS16400_NO_BURST)
|
||||
return adis_update_scan_mode(indio_dev, scan_mask);
|
||||
|
||||
kfree(adis->xfer);
|
||||
kfree(adis->buffer);
|
||||
|
||||
adis->xfer = kcalloc(2, sizeof(*adis->xfer), GFP_KERNEL);
|
||||
if (!adis->xfer)
|
||||
return -ENOMEM;
|
||||
|
||||
adis->buffer = kzalloc(indio_dev->scan_bytes + sizeof(u16),
|
||||
GFP_KERNEL);
|
||||
if (!adis->buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
rx = adis->buffer;
|
||||
tx = adis->buffer + indio_dev->scan_bytes;
|
||||
|
||||
tx[0] = ADIS_READ_REG(ADIS16400_GLOB_CMD);
|
||||
tx[1] = 0;
|
||||
|
||||
adis->xfer[0].tx_buf = tx;
|
||||
adis->xfer[0].bits_per_word = 8;
|
||||
adis->xfer[0].len = 2;
|
||||
adis->xfer[1].tx_buf = tx;
|
||||
adis->xfer[1].bits_per_word = 8;
|
||||
adis->xfer[1].len = indio_dev->scan_bytes;
|
||||
|
||||
spi_message_init(&adis->msg);
|
||||
spi_message_add_tail(&adis->xfer[0], &adis->msg);
|
||||
spi_message_add_tail(&adis->xfer[1], &adis->msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
irqreturn_t adis16400_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
struct adis *adis = &st->adis;
|
||||
u32 old_speed_hz = st->adis.spi->max_speed_hz;
|
||||
int ret;
|
||||
|
||||
if (!adis->buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!(st->variant->flags & ADIS16400_NO_BURST) &&
|
||||
st->adis.spi->max_speed_hz > ADIS16400_SPI_BURST) {
|
||||
st->adis.spi->max_speed_hz = ADIS16400_SPI_BURST;
|
||||
spi_setup(st->adis.spi);
|
||||
}
|
||||
|
||||
ret = spi_sync(adis->spi, &adis->msg);
|
||||
if (ret)
|
||||
dev_err(&adis->spi->dev, "Failed to read data: %d\n", ret);
|
||||
|
||||
if (!(st->variant->flags & ADIS16400_NO_BURST)) {
|
||||
st->adis.spi->max_speed_hz = old_speed_hz;
|
||||
spi_setup(st->adis.spi);
|
||||
}
|
||||
|
||||
/* Guaranteed to be aligned with 8 byte boundary */
|
||||
if (indio_dev->scan_timestamp) {
|
||||
void *b = adis->buffer + indio_dev->scan_bytes - sizeof(s64);
|
||||
*(s64 *)b = pf->timestamp;
|
||||
}
|
||||
|
||||
iio_push_to_buffers(indio_dev, adis->buffer);
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
965
drivers/iio/imu/adis16400_core.c
Normal file
965
drivers/iio/imu/adis16400_core.c
Normal file
|
@ -0,0 +1,965 @@
|
|||
/*
|
||||
* adis16400.c support Analog Devices ADIS16400/5
|
||||
* 3d 2g Linear Accelerometers,
|
||||
* 3d Gyroscopes,
|
||||
* 3d Magnetometers via SPI
|
||||
*
|
||||
* Copyright (c) 2009 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
|
||||
* Copyright (c) 2007 Jonathan Cameron <jic23@kernel.org>
|
||||
* Copyright (c) 2011 Analog Devices Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/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/debugfs.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
|
||||
#include "adis16400.h"
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
static ssize_t adis16400_show_serial_number(struct file *file,
|
||||
char __user *userbuf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct adis16400_state *st = file->private_data;
|
||||
u16 lot1, lot2, serial_number;
|
||||
char buf[16];
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16334_LOT_ID1, &lot1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16334_LOT_ID2, &lot2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16334_SERIAL_NUMBER,
|
||||
&serial_number);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
len = snprintf(buf, sizeof(buf), "%.4x-%.4x-%.4x\n", lot1, lot2,
|
||||
serial_number);
|
||||
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
|
||||
}
|
||||
|
||||
static const struct file_operations adis16400_serial_number_fops = {
|
||||
.open = simple_open,
|
||||
.read = adis16400_show_serial_number,
|
||||
.llseek = default_llseek,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int adis16400_show_product_id(void *arg, u64 *val)
|
||||
{
|
||||
struct adis16400_state *st = arg;
|
||||
uint16_t prod_id;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16400_PRODUCT_ID, &prod_id);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = prod_id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(adis16400_product_id_fops,
|
||||
adis16400_show_product_id, NULL, "%lld\n");
|
||||
|
||||
static int adis16400_show_flash_count(void *arg, u64 *val)
|
||||
{
|
||||
struct adis16400_state *st = arg;
|
||||
uint16_t flash_count;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16400_FLASH_CNT, &flash_count);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = flash_count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(adis16400_flash_count_fops,
|
||||
adis16400_show_flash_count, NULL, "%lld\n");
|
||||
|
||||
static int adis16400_debugfs_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (st->variant->flags & ADIS16400_HAS_SERIAL_NUMBER)
|
||||
debugfs_create_file("serial_number", 0400,
|
||||
indio_dev->debugfs_dentry, st,
|
||||
&adis16400_serial_number_fops);
|
||||
if (st->variant->flags & ADIS16400_HAS_PROD_ID)
|
||||
debugfs_create_file("product_id", 0400,
|
||||
indio_dev->debugfs_dentry, st,
|
||||
&adis16400_product_id_fops);
|
||||
debugfs_create_file("flash_count", 0400, indio_dev->debugfs_dentry,
|
||||
st, &adis16400_flash_count_fops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int adis16400_debugfs_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
enum adis16400_chip_variant {
|
||||
ADIS16300,
|
||||
ADIS16334,
|
||||
ADIS16350,
|
||||
ADIS16360,
|
||||
ADIS16362,
|
||||
ADIS16364,
|
||||
ADIS16400,
|
||||
ADIS16448,
|
||||
};
|
||||
|
||||
static int adis16334_get_freq(struct adis16400_state *st)
|
||||
{
|
||||
int ret;
|
||||
uint16_t t;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16400_SMPL_PRD, &t);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
t >>= ADIS16334_RATE_DIV_SHIFT;
|
||||
|
||||
return 819200 >> t;
|
||||
}
|
||||
|
||||
static int adis16334_set_freq(struct adis16400_state *st, unsigned int freq)
|
||||
{
|
||||
unsigned int t;
|
||||
|
||||
if (freq < 819200)
|
||||
t = ilog2(819200 / freq);
|
||||
else
|
||||
t = 0;
|
||||
|
||||
if (t > 0x31)
|
||||
t = 0x31;
|
||||
|
||||
t <<= ADIS16334_RATE_DIV_SHIFT;
|
||||
t |= ADIS16334_RATE_INT_CLK;
|
||||
|
||||
return adis_write_reg_16(&st->adis, ADIS16400_SMPL_PRD, t);
|
||||
}
|
||||
|
||||
static int adis16400_get_freq(struct adis16400_state *st)
|
||||
{
|
||||
int sps, ret;
|
||||
uint16_t t;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16400_SMPL_PRD, &t);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
sps = (t & ADIS16400_SMPL_PRD_TIME_BASE) ? 52851 : 1638404;
|
||||
sps /= (t & ADIS16400_SMPL_PRD_DIV_MASK) + 1;
|
||||
|
||||
return sps;
|
||||
}
|
||||
|
||||
static int adis16400_set_freq(struct adis16400_state *st, unsigned int freq)
|
||||
{
|
||||
unsigned int t;
|
||||
uint8_t val = 0;
|
||||
|
||||
t = 1638404 / freq;
|
||||
if (t >= 128) {
|
||||
val |= ADIS16400_SMPL_PRD_TIME_BASE;
|
||||
t = 52851 / freq;
|
||||
if (t >= 128)
|
||||
t = 127;
|
||||
} else if (t != 0) {
|
||||
t--;
|
||||
}
|
||||
|
||||
val |= t;
|
||||
|
||||
if (t >= 0x0A || (val & ADIS16400_SMPL_PRD_TIME_BASE))
|
||||
st->adis.spi->max_speed_hz = ADIS16400_SPI_SLOW;
|
||||
else
|
||||
st->adis.spi->max_speed_hz = ADIS16400_SPI_FAST;
|
||||
|
||||
return adis_write_reg_8(&st->adis, ADIS16400_SMPL_PRD, val);
|
||||
}
|
||||
|
||||
static ssize_t adis16400_read_frequency(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = st->variant->get_freq(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d.%.3d\n", ret / 1000, ret % 1000);
|
||||
}
|
||||
|
||||
static const unsigned adis16400_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 adis16400_set_filter(struct iio_dev *indio_dev, int sps, int val)
|
||||
{
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
uint16_t val16;
|
||||
int i, ret;
|
||||
|
||||
for (i = ARRAY_SIZE(adis16400_3db_divisors) - 1; i >= 1; i--) {
|
||||
if (sps / adis16400_3db_divisors[i] >= val)
|
||||
break;
|
||||
}
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16400_SENS_AVG, &val16);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = adis_write_reg_16(&st->adis, ADIS16400_SENS_AVG,
|
||||
(val16 & ~0x07) | i);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t adis16400_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 adis16400_state *st = iio_priv(indio_dev);
|
||||
int i, f, val;
|
||||
int ret;
|
||||
|
||||
ret = iio_str_to_fixpoint(buf, 100, &i, &f);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = i * 1000 + f;
|
||||
|
||||
if (val <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->variant->set_freq(st, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
/* Power down the device */
|
||||
static int adis16400_stop_device(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = adis_write_reg_16(&st->adis, ADIS16400_SLP_CNT,
|
||||
ADIS16400_SLP_CNT_POWER_OFF);
|
||||
if (ret)
|
||||
dev_err(&indio_dev->dev,
|
||||
"problem with turning device off: SLP_CNT");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16400_initial_setup(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
uint16_t prod_id, smp_prd;
|
||||
unsigned int device_id;
|
||||
int ret;
|
||||
|
||||
/* use low spi speed for init if the device has a slow mode */
|
||||
if (st->variant->flags & ADIS16400_HAS_SLOW_MODE)
|
||||
st->adis.spi->max_speed_hz = ADIS16400_SPI_SLOW;
|
||||
else
|
||||
st->adis.spi->max_speed_hz = ADIS16400_SPI_FAST;
|
||||
st->adis.spi->mode = SPI_MODE_3;
|
||||
spi_setup(st->adis.spi);
|
||||
|
||||
ret = adis_initial_startup(&st->adis);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (st->variant->flags & ADIS16400_HAS_PROD_ID) {
|
||||
ret = adis_read_reg_16(&st->adis,
|
||||
ADIS16400_PRODUCT_ID, &prod_id);
|
||||
if (ret)
|
||||
goto err_ret;
|
||||
|
||||
sscanf(indio_dev->name, "adis%u\n", &device_id);
|
||||
|
||||
if (prod_id != device_id)
|
||||
dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",
|
||||
device_id, prod_id);
|
||||
|
||||
dev_info(&indio_dev->dev, "%s: prod_id 0x%04x at CS%d (irq %d)\n",
|
||||
indio_dev->name, prod_id,
|
||||
st->adis.spi->chip_select, st->adis.spi->irq);
|
||||
}
|
||||
/* use high spi speed if possible */
|
||||
if (st->variant->flags & ADIS16400_HAS_SLOW_MODE) {
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16400_SMPL_PRD, &smp_prd);
|
||||
if (ret)
|
||||
goto err_ret;
|
||||
|
||||
if ((smp_prd & ADIS16400_SMPL_PRD_DIV_MASK) < 0x0A) {
|
||||
st->adis.spi->max_speed_hz = ADIS16400_SPI_FAST;
|
||||
spi_setup(st->adis.spi);
|
||||
}
|
||||
}
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
|
||||
adis16400_read_frequency,
|
||||
adis16400_write_frequency);
|
||||
|
||||
static const uint8_t adis16400_addresses[] = {
|
||||
[ADIS16400_SCAN_GYRO_X] = ADIS16400_XGYRO_OFF,
|
||||
[ADIS16400_SCAN_GYRO_Y] = ADIS16400_YGYRO_OFF,
|
||||
[ADIS16400_SCAN_GYRO_Z] = ADIS16400_ZGYRO_OFF,
|
||||
[ADIS16400_SCAN_ACC_X] = ADIS16400_XACCL_OFF,
|
||||
[ADIS16400_SCAN_ACC_Y] = ADIS16400_YACCL_OFF,
|
||||
[ADIS16400_SCAN_ACC_Z] = ADIS16400_ZACCL_OFF,
|
||||
};
|
||||
|
||||
static int adis16400_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long info)
|
||||
{
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
int ret, sps;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = adis_write_reg_16(&st->adis,
|
||||
adis16400_addresses[chan->scan_index], val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
/*
|
||||
* Need to cache values so we can update if the frequency
|
||||
* changes.
|
||||
*/
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->filt_int = val;
|
||||
/* Work out update to current value */
|
||||
sps = st->variant->get_freq(st);
|
||||
if (sps < 0) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return sps;
|
||||
}
|
||||
|
||||
ret = adis16400_set_filter(indio_dev, sps,
|
||||
val * 1000 + val2 / 1000);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int adis16400_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2, long info)
|
||||
{
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
int16_t val16;
|
||||
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 = 0;
|
||||
*val2 = st->variant->gyro_scale_micro;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_VOLTAGE:
|
||||
*val = 0;
|
||||
if (chan->channel == 0) {
|
||||
*val = 2;
|
||||
*val2 = 418000; /* 2.418 mV */
|
||||
} else {
|
||||
*val = 0;
|
||||
*val2 = 805800; /* 805.8 uV */
|
||||
}
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_ACCEL:
|
||||
*val = 0;
|
||||
*val2 = st->variant->accel_scale_micro;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_MAGN:
|
||||
*val = 0;
|
||||
*val2 = 500; /* 0.5 mgauss */
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_TEMP:
|
||||
*val = st->variant->temp_scale_nano / 1000000;
|
||||
*val2 = (st->variant->temp_scale_nano % 1000000);
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = adis_read_reg_16(&st->adis,
|
||||
adis16400_addresses[chan->scan_index], &val16);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
if (ret)
|
||||
return ret;
|
||||
val16 = ((val16 & 0xFFF) << 4) >> 4;
|
||||
*val = val16;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
/* currently only temperature */
|
||||
*val = st->variant->temp_offset;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
/* Need both the number of taps and the sampling frequency */
|
||||
ret = adis_read_reg_16(&st->adis,
|
||||
ADIS16400_SENS_AVG,
|
||||
&val16);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
ret = st->variant->get_freq(st);
|
||||
if (ret >= 0) {
|
||||
ret /= adis16400_3db_divisors[val16 & 0x07];
|
||||
*val = ret / 1000;
|
||||
*val2 = (ret % 1000) * 1000;
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#define ADIS16400_VOLTAGE_CHAN(addr, bits, name, si) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = 0, \
|
||||
.extend_name = name, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
|
||||
.address = (addr), \
|
||||
.scan_index = (si), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 0, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ADIS16400_SUPPLY_CHAN(addr, bits) \
|
||||
ADIS16400_VOLTAGE_CHAN(addr, bits, "supply", ADIS16400_SCAN_SUPPLY)
|
||||
|
||||
#define ADIS16400_AUX_ADC_CHAN(addr, bits) \
|
||||
ADIS16400_VOLTAGE_CHAN(addr, bits, NULL, ADIS16400_SCAN_ADC)
|
||||
|
||||
#define ADIS16400_GYRO_CHAN(mod, addr, bits) { \
|
||||
.type = IIO_ANGL_VEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_ ## mod, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT | \
|
||||
IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SHARED_BIT, \
|
||||
.address = addr, \
|
||||
.scan_index = ADIS16400_SCAN_GYRO_ ## mod, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 0, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ADIS16400_ACCEL_CHAN(mod, addr, bits) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_ ## mod, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT | \
|
||||
IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SHARED_BIT, \
|
||||
.address = (addr), \
|
||||
.scan_index = ADIS16400_SCAN_ACC_ ## mod, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 0, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ADIS16400_MAGN_CHAN(mod, addr, bits) { \
|
||||
.type = IIO_MAGN, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_ ## mod, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT | \
|
||||
IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SHARED_BIT, \
|
||||
.address = (addr), \
|
||||
.scan_index = ADIS16400_SCAN_MAGN_ ## mod, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 0, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ADIS16400_MOD_TEMP_NAME_X "x"
|
||||
#define ADIS16400_MOD_TEMP_NAME_Y "y"
|
||||
#define ADIS16400_MOD_TEMP_NAME_Z "z"
|
||||
|
||||
#define ADIS16400_MOD_TEMP_CHAN(mod, addr, bits) { \
|
||||
.type = IIO_TEMP, \
|
||||
.indexed = 1, \
|
||||
.channel = 0, \
|
||||
.extend_name = ADIS16400_MOD_TEMP_NAME_ ## mod, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY_SHARED_BIT, \
|
||||
.address = (addr), \
|
||||
.scan_index = ADIS16350_SCAN_TEMP_ ## mod, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 0, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ADIS16400_TEMP_CHAN(addr, bits) { \
|
||||
.type = IIO_TEMP, \
|
||||
.indexed = 1, \
|
||||
.channel = 0, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_OFFSET_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
|
||||
.address = (addr), \
|
||||
.scan_index = ADIS16350_SCAN_TEMP_X, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 0, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ADIS16400_INCLI_CHAN(mod, addr, bits) { \
|
||||
.type = IIO_INCLI, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_ ## mod, \
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT, \
|
||||
.address = (addr), \
|
||||
.scan_index = ADIS16300_SCAN_INCLI_ ## mod, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 0, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adis16400_channels[] = {
|
||||
ADIS16400_SUPPLY_CHAN(ADIS16400_SUPPLY_OUT, 14),
|
||||
ADIS16400_GYRO_CHAN(X, ADIS16400_XGYRO_OUT, 14),
|
||||
ADIS16400_GYRO_CHAN(Y, ADIS16400_YGYRO_OUT, 14),
|
||||
ADIS16400_GYRO_CHAN(Z, ADIS16400_ZGYRO_OUT, 14),
|
||||
ADIS16400_ACCEL_CHAN(X, ADIS16400_XACCL_OUT, 14),
|
||||
ADIS16400_ACCEL_CHAN(Y, ADIS16400_YACCL_OUT, 14),
|
||||
ADIS16400_ACCEL_CHAN(Z, ADIS16400_ZACCL_OUT, 14),
|
||||
ADIS16400_MAGN_CHAN(X, ADIS16400_XMAGN_OUT, 14),
|
||||
ADIS16400_MAGN_CHAN(Y, ADIS16400_YMAGN_OUT, 14),
|
||||
ADIS16400_MAGN_CHAN(Z, ADIS16400_ZMAGN_OUT, 14),
|
||||
ADIS16400_TEMP_CHAN(ADIS16400_TEMP_OUT, 12),
|
||||
ADIS16400_AUX_ADC_CHAN(ADIS16400_AUX_ADC, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(12)
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec adis16448_channels[] = {
|
||||
ADIS16400_GYRO_CHAN(X, ADIS16400_XGYRO_OUT, 16),
|
||||
ADIS16400_GYRO_CHAN(Y, ADIS16400_YGYRO_OUT, 16),
|
||||
ADIS16400_GYRO_CHAN(Z, ADIS16400_ZGYRO_OUT, 16),
|
||||
ADIS16400_ACCEL_CHAN(X, ADIS16400_XACCL_OUT, 16),
|
||||
ADIS16400_ACCEL_CHAN(Y, ADIS16400_YACCL_OUT, 16),
|
||||
ADIS16400_ACCEL_CHAN(Z, ADIS16400_ZACCL_OUT, 16),
|
||||
ADIS16400_MAGN_CHAN(X, ADIS16400_XMAGN_OUT, 16),
|
||||
ADIS16400_MAGN_CHAN(Y, ADIS16400_YMAGN_OUT, 16),
|
||||
ADIS16400_MAGN_CHAN(Z, ADIS16400_ZMAGN_OUT, 16),
|
||||
{
|
||||
.type = IIO_PRESSURE,
|
||||
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
|
||||
IIO_CHAN_INFO_SCALE_SHARED_BIT,
|
||||
.address = ADIS16448_BARO_OUT,
|
||||
.scan_index = ADIS16400_SCAN_BARO,
|
||||
.scan_type = IIO_ST('s', 16, 16, 0),
|
||||
},
|
||||
ADIS16400_TEMP_CHAN(ADIS16448_TEMP_OUT, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(11)
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec adis16350_channels[] = {
|
||||
ADIS16400_SUPPLY_CHAN(ADIS16400_SUPPLY_OUT, 12),
|
||||
ADIS16400_GYRO_CHAN(X, ADIS16400_XGYRO_OUT, 14),
|
||||
ADIS16400_GYRO_CHAN(Y, ADIS16400_YGYRO_OUT, 14),
|
||||
ADIS16400_GYRO_CHAN(Z, ADIS16400_ZGYRO_OUT, 14),
|
||||
ADIS16400_ACCEL_CHAN(X, ADIS16400_XACCL_OUT, 14),
|
||||
ADIS16400_ACCEL_CHAN(Y, ADIS16400_YACCL_OUT, 14),
|
||||
ADIS16400_ACCEL_CHAN(Z, ADIS16400_ZACCL_OUT, 14),
|
||||
ADIS16400_MAGN_CHAN(X, ADIS16400_XMAGN_OUT, 14),
|
||||
ADIS16400_MAGN_CHAN(Y, ADIS16400_YMAGN_OUT, 14),
|
||||
ADIS16400_MAGN_CHAN(Z, ADIS16400_ZMAGN_OUT, 14),
|
||||
ADIS16400_AUX_ADC_CHAN(ADIS16300_AUX_ADC, 12),
|
||||
ADIS16400_MOD_TEMP_CHAN(X, ADIS16350_XTEMP_OUT, 12),
|
||||
ADIS16400_MOD_TEMP_CHAN(Y, ADIS16350_YTEMP_OUT, 12),
|
||||
ADIS16400_MOD_TEMP_CHAN(Z, ADIS16350_ZTEMP_OUT, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(11)
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec adis16300_channels[] = {
|
||||
ADIS16400_SUPPLY_CHAN(ADIS16400_SUPPLY_OUT, 12),
|
||||
ADIS16400_GYRO_CHAN(X, ADIS16400_XGYRO_OUT, 14),
|
||||
ADIS16400_ACCEL_CHAN(X, ADIS16400_XACCL_OUT, 14),
|
||||
ADIS16400_ACCEL_CHAN(Y, ADIS16400_YACCL_OUT, 14),
|
||||
ADIS16400_ACCEL_CHAN(Z, ADIS16400_ZACCL_OUT, 14),
|
||||
ADIS16400_TEMP_CHAN(ADIS16350_XTEMP_OUT, 12),
|
||||
ADIS16400_AUX_ADC_CHAN(ADIS16300_AUX_ADC, 12),
|
||||
ADIS16400_INCLI_CHAN(X, ADIS16300_PITCH_OUT, 13),
|
||||
ADIS16400_INCLI_CHAN(Y, ADIS16300_ROLL_OUT, 13),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(14)
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec adis16334_channels[] = {
|
||||
ADIS16400_GYRO_CHAN(X, ADIS16400_XGYRO_OUT, 14),
|
||||
ADIS16400_GYRO_CHAN(Y, ADIS16400_YGYRO_OUT, 14),
|
||||
ADIS16400_GYRO_CHAN(Z, ADIS16400_ZGYRO_OUT, 14),
|
||||
ADIS16400_ACCEL_CHAN(X, ADIS16400_XACCL_OUT, 14),
|
||||
ADIS16400_ACCEL_CHAN(Y, ADIS16400_YACCL_OUT, 14),
|
||||
ADIS16400_ACCEL_CHAN(Z, ADIS16400_ZACCL_OUT, 14),
|
||||
ADIS16400_TEMP_CHAN(ADIS16350_XTEMP_OUT, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8)
|
||||
};
|
||||
|
||||
static struct attribute *adis16400_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group adis16400_attribute_group = {
|
||||
.attrs = adis16400_attributes,
|
||||
};
|
||||
|
||||
static struct adis16400_chip_info adis16400_chips[] = {
|
||||
[ADIS16300] = {
|
||||
.channels = adis16300_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16300_channels),
|
||||
.flags = ADIS16400_HAS_SLOW_MODE,
|
||||
.gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */
|
||||
.accel_scale_micro = 5884,
|
||||
.temp_scale_nano = 140000000, /* 0.14 C */
|
||||
.temp_offset = 25000000 / 140000, /* 25 C = 0x00 */
|
||||
.set_freq = adis16400_set_freq,
|
||||
.get_freq = adis16400_get_freq,
|
||||
},
|
||||
[ADIS16334] = {
|
||||
.channels = adis16334_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16334_channels),
|
||||
.flags = ADIS16400_HAS_PROD_ID | ADIS16400_NO_BURST |
|
||||
ADIS16400_HAS_SERIAL_NUMBER,
|
||||
.gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */
|
||||
.accel_scale_micro = IIO_G_TO_M_S_2(1000), /* 1 mg */
|
||||
.temp_scale_nano = 67850000, /* 0.06785 C */
|
||||
.temp_offset = 25000000 / 67850, /* 25 C = 0x00 */
|
||||
.set_freq = adis16334_set_freq,
|
||||
.get_freq = adis16334_get_freq,
|
||||
},
|
||||
[ADIS16350] = {
|
||||
.channels = adis16350_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16350_channels),
|
||||
.gyro_scale_micro = IIO_DEGREE_TO_RAD(73260), /* 0.07326 deg/s */
|
||||
.accel_scale_micro = IIO_G_TO_M_S_2(2522), /* 0.002522 g */
|
||||
.temp_scale_nano = 145300000, /* 0.1453 C */
|
||||
.temp_offset = 25000000 / 145300, /* 25 C = 0x00 */
|
||||
.flags = ADIS16400_NO_BURST | ADIS16400_HAS_SLOW_MODE,
|
||||
.set_freq = adis16400_set_freq,
|
||||
.get_freq = adis16400_get_freq,
|
||||
},
|
||||
[ADIS16360] = {
|
||||
.channels = adis16350_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16350_channels),
|
||||
.flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE |
|
||||
ADIS16400_HAS_SERIAL_NUMBER,
|
||||
.gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */
|
||||
.accel_scale_micro = IIO_G_TO_M_S_2(3333), /* 3.333 mg */
|
||||
.temp_scale_nano = 136000000, /* 0.136 C */
|
||||
.temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
|
||||
.set_freq = adis16400_set_freq,
|
||||
.get_freq = adis16400_get_freq,
|
||||
},
|
||||
[ADIS16362] = {
|
||||
.channels = adis16350_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16350_channels),
|
||||
.flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE |
|
||||
ADIS16400_HAS_SERIAL_NUMBER,
|
||||
.gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */
|
||||
.accel_scale_micro = IIO_G_TO_M_S_2(333), /* 0.333 mg */
|
||||
.temp_scale_nano = 136000000, /* 0.136 C */
|
||||
.temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
|
||||
.set_freq = adis16400_set_freq,
|
||||
.get_freq = adis16400_get_freq,
|
||||
},
|
||||
[ADIS16364] = {
|
||||
.channels = adis16350_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16350_channels),
|
||||
.flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE |
|
||||
ADIS16400_HAS_SERIAL_NUMBER,
|
||||
.gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */
|
||||
.accel_scale_micro = IIO_G_TO_M_S_2(1000), /* 1 mg */
|
||||
.temp_scale_nano = 136000000, /* 0.136 C */
|
||||
.temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
|
||||
.set_freq = adis16400_set_freq,
|
||||
.get_freq = adis16400_get_freq,
|
||||
},
|
||||
[ADIS16400] = {
|
||||
.channels = adis16400_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16400_channels),
|
||||
.flags = ADIS16400_HAS_PROD_ID | ADIS16400_HAS_SLOW_MODE,
|
||||
.gyro_scale_micro = IIO_DEGREE_TO_RAD(50000), /* 0.05 deg/s */
|
||||
.accel_scale_micro = IIO_G_TO_M_S_2(3333), /* 3.333 mg */
|
||||
.temp_scale_nano = 140000000, /* 0.14 C */
|
||||
.temp_offset = 25000000 / 140000, /* 25 C = 0x00 */
|
||||
.set_freq = adis16400_set_freq,
|
||||
.get_freq = adis16400_get_freq,
|
||||
},
|
||||
[ADIS16448] = {
|
||||
.channels = adis16448_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16448_channels),
|
||||
.flags = ADIS16400_HAS_PROD_ID |
|
||||
ADIS16400_HAS_SERIAL_NUMBER,
|
||||
.gyro_scale_micro = IIO_DEGREE_TO_RAD(10000), /* 0.01 deg/s */
|
||||
.accel_scale_micro = IIO_G_TO_M_S_2(833), /* 1/1200 g */
|
||||
.temp_scale_nano = 73860000, /* 0.07386 C */
|
||||
.temp_offset = 31000000 / 73860, /* 31 C = 0x00 */
|
||||
.set_freq = adis16334_set_freq,
|
||||
.get_freq = adis16334_get_freq,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct iio_info adis16400_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &adis16400_read_raw,
|
||||
.write_raw = &adis16400_write_raw,
|
||||
.attrs = &adis16400_attribute_group,
|
||||
.update_scan_mode = adis16400_update_scan_mode,
|
||||
.debugfs_reg_access = adis_debugfs_reg_access,
|
||||
};
|
||||
|
||||
static const unsigned long adis16400_burst_scan_mask[] = {
|
||||
~0UL,
|
||||
0,
|
||||
};
|
||||
|
||||
static const char * const adis16400_status_error_msgs[] = {
|
||||
[ADIS16400_DIAG_STAT_ZACCL_FAIL] = "Z-axis accelerometer self-test failure",
|
||||
[ADIS16400_DIAG_STAT_YACCL_FAIL] = "Y-axis accelerometer self-test failure",
|
||||
[ADIS16400_DIAG_STAT_XACCL_FAIL] = "X-axis accelerometer self-test failure",
|
||||
[ADIS16400_DIAG_STAT_XGYRO_FAIL] = "X-axis gyroscope self-test failure",
|
||||
[ADIS16400_DIAG_STAT_YGYRO_FAIL] = "Y-axis gyroscope self-test failure",
|
||||
[ADIS16400_DIAG_STAT_ZGYRO_FAIL] = "Z-axis gyroscope self-test failure",
|
||||
[ADIS16400_DIAG_STAT_ALARM2] = "Alarm 2 active",
|
||||
[ADIS16400_DIAG_STAT_ALARM1] = "Alarm 1 active",
|
||||
[ADIS16400_DIAG_STAT_FLASH_CHK] = "Flash checksum error",
|
||||
[ADIS16400_DIAG_STAT_SELF_TEST] = "Self test error",
|
||||
[ADIS16400_DIAG_STAT_OVERFLOW] = "Sensor overrange",
|
||||
[ADIS16400_DIAG_STAT_SPI_FAIL] = "SPI failure",
|
||||
[ADIS16400_DIAG_STAT_FLASH_UPT] = "Flash update failed",
|
||||
[ADIS16400_DIAG_STAT_POWER_HIGH] = "Power supply above 5.25V",
|
||||
[ADIS16400_DIAG_STAT_POWER_LOW] = "Power supply below 4.75V",
|
||||
};
|
||||
|
||||
static const struct adis_data adis16400_data = {
|
||||
.msc_ctrl_reg = ADIS16400_MSC_CTRL,
|
||||
.glob_cmd_reg = ADIS16400_GLOB_CMD,
|
||||
.diag_stat_reg = ADIS16400_DIAG_STAT,
|
||||
|
||||
.read_delay = 50,
|
||||
.write_delay = 50,
|
||||
|
||||
.self_test_mask = ADIS16400_MSC_CTRL_MEM_TEST,
|
||||
.startup_delay = ADIS16400_STARTUP_DELAY,
|
||||
|
||||
.status_error_msgs = adis16400_status_error_msgs,
|
||||
.status_error_mask = BIT(ADIS16400_DIAG_STAT_ZACCL_FAIL) |
|
||||
BIT(ADIS16400_DIAG_STAT_YACCL_FAIL) |
|
||||
BIT(ADIS16400_DIAG_STAT_XACCL_FAIL) |
|
||||
BIT(ADIS16400_DIAG_STAT_XGYRO_FAIL) |
|
||||
BIT(ADIS16400_DIAG_STAT_YGYRO_FAIL) |
|
||||
BIT(ADIS16400_DIAG_STAT_ZGYRO_FAIL) |
|
||||
BIT(ADIS16400_DIAG_STAT_ALARM2) |
|
||||
BIT(ADIS16400_DIAG_STAT_ALARM1) |
|
||||
BIT(ADIS16400_DIAG_STAT_FLASH_CHK) |
|
||||
BIT(ADIS16400_DIAG_STAT_SELF_TEST) |
|
||||
BIT(ADIS16400_DIAG_STAT_OVERFLOW) |
|
||||
BIT(ADIS16400_DIAG_STAT_SPI_FAIL) |
|
||||
BIT(ADIS16400_DIAG_STAT_FLASH_UPT) |
|
||||
BIT(ADIS16400_DIAG_STAT_POWER_HIGH) |
|
||||
BIT(ADIS16400_DIAG_STAT_POWER_LOW),
|
||||
};
|
||||
|
||||
static int adis16400_probe(struct spi_device *spi)
|
||||
{
|
||||
struct adis16400_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = iio_device_alloc(sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
/* this is only used for removal purposes */
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
/* setup the industrialio driver allocated elements */
|
||||
st->variant = &adis16400_chips[spi_get_device_id(spi)->driver_data];
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->channels = st->variant->channels;
|
||||
indio_dev->num_channels = st->variant->num_channels;
|
||||
indio_dev->info = &adis16400_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
if (!(st->variant->flags & ADIS16400_NO_BURST))
|
||||
indio_dev->available_scan_masks = adis16400_burst_scan_mask;
|
||||
|
||||
ret = adis_init(&st->adis, indio_dev, spi, &adis16400_data);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
|
||||
ret = adis_setup_buffer_and_trigger(&st->adis, indio_dev,
|
||||
adis16400_trigger_handler);
|
||||
if (ret)
|
||||
goto error_free_dev;
|
||||
|
||||
/* Get the device into a sane initial state */
|
||||
ret = adis16400_initial_setup(indio_dev);
|
||||
if (ret)
|
||||
goto error_cleanup_buffer;
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_cleanup_buffer;
|
||||
|
||||
adis16400_debugfs_init(indio_dev);
|
||||
return 0;
|
||||
|
||||
error_cleanup_buffer:
|
||||
adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
|
||||
error_free_dev:
|
||||
iio_device_free(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16400_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
adis16400_stop_device(indio_dev);
|
||||
|
||||
adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
|
||||
|
||||
iio_device_free(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id adis16400_id[] = {
|
||||
{"adis16300", ADIS16300},
|
||||
{"adis16334", ADIS16334},
|
||||
{"adis16350", ADIS16350},
|
||||
{"adis16354", ADIS16350},
|
||||
{"adis16355", ADIS16350},
|
||||
{"adis16360", ADIS16360},
|
||||
{"adis16362", ADIS16362},
|
||||
{"adis16364", ADIS16364},
|
||||
{"adis16365", ADIS16360},
|
||||
{"adis16400", ADIS16400},
|
||||
{"adis16405", ADIS16400},
|
||||
{"adis16448", ADIS16448},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, adis16400_id);
|
||||
|
||||
static struct spi_driver adis16400_driver = {
|
||||
.driver = {
|
||||
.name = "adis16400",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.id_table = adis16400_id,
|
||||
.probe = adis16400_probe,
|
||||
.remove = adis16400_remove,
|
||||
};
|
||||
module_spi_driver(adis16400_driver);
|
||||
|
||||
MODULE_AUTHOR("Manuel Stahl <manuel.stahl@iis.fraunhofer.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices ADIS16400/5 IMU SPI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -32,6 +32,16 @@ config SENSORS_LM3533
|
|||
changes. The ALS-control output values can be set per zone for the
|
||||
three current output channels.
|
||||
|
||||
config SENSORS_TSL2563
|
||||
tristate "TAOS TSL2560, TSL2561, TSL2562 and TSL2563 ambient light sensors"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for the Taos TSL2560,
|
||||
TSL2561, TSL2562 and TSL2563 ambient light sensors.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called tsl2563.
|
||||
|
||||
config VCNL4000
|
||||
tristate "VCNL4000 combined ALS and proximity sensor"
|
||||
depends on I2C
|
||||
|
|
|
@ -4,5 +4,6 @@
|
|||
|
||||
obj-$(CONFIG_ADJD_S311) += adjd_s311.o
|
||||
obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
|
||||
obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o
|
||||
obj-$(CONFIG_VCNL4000) += vcnl4000.o
|
||||
obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* drivers/i2c/chips/tsl2563.c
|
||||
* drivers/iio/light/tsl2563.c
|
||||
*
|
||||
* Copyright (C) 2008 Nokia Corporation
|
||||
*
|
||||
|
@ -38,52 +38,52 @@
|
|||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include "tsl2563.h"
|
||||
#include <linux/platform_data/tsl2563.h>
|
||||
|
||||
/* Use this many bits for fraction part. */
|
||||
#define ADC_FRAC_BITS (14)
|
||||
#define ADC_FRAC_BITS 14
|
||||
|
||||
/* Given number of 1/10000's in ADC_FRAC_BITS precision. */
|
||||
#define FRAC10K(f) (((f) * (1L << (ADC_FRAC_BITS))) / (10000))
|
||||
|
||||
/* Bits used for fraction in calibration coefficients.*/
|
||||
#define CALIB_FRAC_BITS (10)
|
||||
#define CALIB_FRAC_BITS 10
|
||||
/* 0.5 in CALIB_FRAC_BITS precision */
|
||||
#define CALIB_FRAC_HALF (1 << (CALIB_FRAC_BITS - 1))
|
||||
/* Make a fraction from a number n that was multiplied with b. */
|
||||
#define CALIB_FRAC(n, b) (((n) << CALIB_FRAC_BITS) / (b))
|
||||
/* Decimal 10^(digits in sysfs presentation) */
|
||||
#define CALIB_BASE_SYSFS (1000)
|
||||
#define CALIB_BASE_SYSFS 1000
|
||||
|
||||
#define TSL2563_CMD (0x80)
|
||||
#define TSL2563_CLEARINT (0x40)
|
||||
#define TSL2563_CMD 0x80
|
||||
#define TSL2563_CLEARINT 0x40
|
||||
|
||||
#define TSL2563_REG_CTRL (0x00)
|
||||
#define TSL2563_REG_TIMING (0x01)
|
||||
#define TSL2563_REG_LOWLOW (0x02) /* data0 low threshold, 2 bytes */
|
||||
#define TSL2563_REG_LOWHIGH (0x03)
|
||||
#define TSL2563_REG_HIGHLOW (0x04) /* data0 high threshold, 2 bytes */
|
||||
#define TSL2563_REG_HIGHHIGH (0x05)
|
||||
#define TSL2563_REG_INT (0x06)
|
||||
#define TSL2563_REG_ID (0x0a)
|
||||
#define TSL2563_REG_DATA0LOW (0x0c) /* broadband sensor value, 2 bytes */
|
||||
#define TSL2563_REG_DATA0HIGH (0x0d)
|
||||
#define TSL2563_REG_DATA1LOW (0x0e) /* infrared sensor value, 2 bytes */
|
||||
#define TSL2563_REG_DATA1HIGH (0x0f)
|
||||
#define TSL2563_REG_CTRL 0x00
|
||||
#define TSL2563_REG_TIMING 0x01
|
||||
#define TSL2563_REG_LOWLOW 0x02 /* data0 low threshold, 2 bytes */
|
||||
#define TSL2563_REG_LOWHIGH 0x03
|
||||
#define TSL2563_REG_HIGHLOW 0x04 /* data0 high threshold, 2 bytes */
|
||||
#define TSL2563_REG_HIGHHIGH 0x05
|
||||
#define TSL2563_REG_INT 0x06
|
||||
#define TSL2563_REG_ID 0x0a
|
||||
#define TSL2563_REG_DATA0LOW 0x0c /* broadband sensor value, 2 bytes */
|
||||
#define TSL2563_REG_DATA0HIGH 0x0d
|
||||
#define TSL2563_REG_DATA1LOW 0x0e /* infrared sensor value, 2 bytes */
|
||||
#define TSL2563_REG_DATA1HIGH 0x0f
|
||||
|
||||
#define TSL2563_CMD_POWER_ON (0x03)
|
||||
#define TSL2563_CMD_POWER_OFF (0x00)
|
||||
#define TSL2563_CTRL_POWER_MASK (0x03)
|
||||
#define TSL2563_CMD_POWER_ON 0x03
|
||||
#define TSL2563_CMD_POWER_OFF 0x00
|
||||
#define TSL2563_CTRL_POWER_MASK 0x03
|
||||
|
||||
#define TSL2563_TIMING_13MS (0x00)
|
||||
#define TSL2563_TIMING_100MS (0x01)
|
||||
#define TSL2563_TIMING_400MS (0x02)
|
||||
#define TSL2563_TIMING_MASK (0x03)
|
||||
#define TSL2563_TIMING_GAIN16 (0x10)
|
||||
#define TSL2563_TIMING_GAIN1 (0x00)
|
||||
#define TSL2563_TIMING_13MS 0x00
|
||||
#define TSL2563_TIMING_100MS 0x01
|
||||
#define TSL2563_TIMING_400MS 0x02
|
||||
#define TSL2563_TIMING_MASK 0x03
|
||||
#define TSL2563_TIMING_GAIN16 0x10
|
||||
#define TSL2563_TIMING_GAIN1 0x00
|
||||
|
||||
#define TSL2563_INT_DISBLED (0x00)
|
||||
#define TSL2563_INT_LEVEL (0x10)
|
||||
#define TSL2563_INT_DISBLED 0x00
|
||||
#define TSL2563_INT_LEVEL 0x10
|
||||
#define TSL2563_INT_PERSIST(n) ((n) & 0x0F)
|
||||
|
||||
struct tsl2563_gainlevel_coeff {
|
||||
|
@ -190,8 +190,10 @@ static int tsl2563_configure(struct tsl2563_chip *chip)
|
|||
ret = i2c_smbus_write_byte_data(chip->client,
|
||||
TSL2563_CMD | TSL2563_REG_LOWHIGH,
|
||||
(chip->low_thres >> 8) & 0xFF);
|
||||
/* Interrupt register is automatically written anyway if it is relevant
|
||||
so is not here */
|
||||
/*
|
||||
* Interrupt register is automatically written anyway if it is relevant
|
||||
* so is not here.
|
||||
*/
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
@ -423,9 +425,7 @@ static const struct tsl2563_lux_coeff lux_table[] = {
|
|||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Convert normalized, scaled ADC values to lux.
|
||||
*/
|
||||
/* Convert normalized, scaled ADC values to lux. */
|
||||
static unsigned int adc_to_lux(u32 adc0, u32 adc1)
|
||||
{
|
||||
const struct tsl2563_lux_coeff *lp = lux_table;
|
||||
|
@ -441,11 +441,6 @@ static unsigned int adc_to_lux(u32 adc0, u32 adc1)
|
|||
return (unsigned int) (lux >> ADC_FRAC_BITS);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Sysfs interface */
|
||||
/*--------------------------------------------------------------*/
|
||||
|
||||
|
||||
/* Apply calibration coefficient to ADC count. */
|
||||
static u32 calib_adc(u32 adc, u32 calib)
|
||||
{
|
||||
|
@ -677,18 +672,11 @@ static int tsl2563_read_interrupt_config(struct iio_dev *indio_dev,
|
|||
TSL2563_CMD | TSL2563_REG_INT);
|
||||
mutex_unlock(&chip->lock);
|
||||
if (ret < 0)
|
||||
goto error_ret;
|
||||
ret = !!(ret & 0x30);
|
||||
error_ret:
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
return !!(ret & 0x30);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Probe, Attach, Remove */
|
||||
/*--------------------------------------------------------------*/
|
||||
static struct i2c_driver tsl2563_i2c_driver;
|
||||
|
||||
static const struct iio_info tsl2563_info_no_irq = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &tsl2563_read_raw,
|
|
@ -225,6 +225,7 @@ static int hid_time_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, time_state);
|
||||
|
||||
spin_lock_init(&time_state->lock_last_time);
|
||||
init_completion(&time_state->comp_last_time);
|
||||
time_state->common_attributes.hsdev = hsdev;
|
||||
time_state->common_attributes.pdev = pdev;
|
||||
|
|
|
@ -12,19 +12,6 @@ config IIO_ST_HWMON
|
|||
map allows IIO devices to provide basic hwmon functionality
|
||||
for those channels specified in the map.
|
||||
|
||||
if IIO_BUFFER
|
||||
|
||||
config IIO_SW_RING
|
||||
select IIO_TRIGGER
|
||||
tristate "Industrial I/O lock free software ring"
|
||||
help
|
||||
Example software ring buffer implementation. The design aim
|
||||
of this particular realization was to minimize write locking
|
||||
with the intention that some devices would be able to write
|
||||
in interrupt context.
|
||||
|
||||
endif # IIO_BUFFER
|
||||
|
||||
source "drivers/staging/iio/accel/Kconfig"
|
||||
source "drivers/staging/iio/adc/Kconfig"
|
||||
source "drivers/staging/iio/addac/Kconfig"
|
||||
|
@ -32,7 +19,6 @@ source "drivers/staging/iio/cdc/Kconfig"
|
|||
source "drivers/staging/iio/frequency/Kconfig"
|
||||
source "drivers/staging/iio/gyro/Kconfig"
|
||||
source "drivers/staging/iio/impedance-analyzer/Kconfig"
|
||||
source "drivers/staging/iio/imu/Kconfig"
|
||||
source "drivers/staging/iio/light/Kconfig"
|
||||
source "drivers/staging/iio/magnetometer/Kconfig"
|
||||
source "drivers/staging/iio/meter/Kconfig"
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
# Makefile for the industrial I/O core.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_IIO_SW_RING) += ring_sw.o
|
||||
|
||||
obj-$(CONFIG_IIO_SIMPLE_DUMMY) += iio_dummy.o
|
||||
iio_dummy-y := iio_simple_dummy.o
|
||||
iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_EVENTS) += iio_simple_dummy_events.o
|
||||
|
@ -20,7 +18,6 @@ obj-y += cdc/
|
|||
obj-y += frequency/
|
||||
obj-y += gyro/
|
||||
obj-y += impedance-analyzer/
|
||||
obj-y += imu/
|
||||
obj-y += light/
|
||||
obj-y += magnetometer/
|
||||
obj-y += meter/
|
||||
|
|
|
@ -56,45 +56,17 @@ config ADIS16240
|
|||
Say yes here to build support for Analog Devices adis16240 programmable
|
||||
impact Sensor and recorder.
|
||||
|
||||
config KXSD9
|
||||
tristate "Kionix KXSD9 Accelerometer Driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for the Kionix KXSD9 accelerometer.
|
||||
Currently this only supports the device via an SPI interface.
|
||||
|
||||
config LIS3L02DQ
|
||||
tristate "ST Microelectronics LIS3L02DQ Accelerometer Driver"
|
||||
depends on SPI
|
||||
select IIO_TRIGGER if IIO_BUFFER
|
||||
depends on !IIO_BUFFER || IIO_KFIFO_BUF || IIO_SW_RING
|
||||
depends on !IIO_BUFFER || IIO_KFIFO_BUF
|
||||
depends on GENERIC_GPIO
|
||||
help
|
||||
Say yes here to build SPI support for the ST microelectronics
|
||||
accelerometer. The driver supplies direct access via sysfs files
|
||||
and an event interface via a character device.
|
||||
|
||||
choice
|
||||
prompt "Buffer type"
|
||||
depends on LIS3L02DQ && IIO_BUFFER
|
||||
|
||||
config LIS3L02DQ_BUF_KFIFO
|
||||
depends on IIO_KFIFO_BUF
|
||||
bool "Simple FIFO"
|
||||
help
|
||||
Kfifo based FIFO. Does not provide any events so it is up
|
||||
to userspace to ensure it reads often enough that data is not
|
||||
lost.
|
||||
|
||||
config LIS3L02DQ_BUF_RING_SW
|
||||
depends on IIO_SW_RING
|
||||
bool "IIO Software Ring"
|
||||
help
|
||||
Original IIO ring buffer implementation. Provides simple
|
||||
buffer events, half full etc.
|
||||
|
||||
endchoice
|
||||
|
||||
config SCA3000
|
||||
depends on IIO_BUFFER
|
||||
depends on SPI
|
||||
|
|
|
@ -20,8 +20,6 @@ obj-$(CONFIG_ADIS16220) += adis16220.o
|
|||
adis16240-y := adis16240_core.o
|
||||
obj-$(CONFIG_ADIS16240) += adis16240.o
|
||||
|
||||
obj-$(CONFIG_KXSD9) += kxsd9.o
|
||||
|
||||
lis3l02dq-y := lis3l02dq_core.o
|
||||
lis3l02dq-$(CONFIG_IIO_BUFFER) += lis3l02dq_ring.o
|
||||
obj-$(CONFIG_LIS3L02DQ) += lis3l02dq.o
|
||||
|
|
|
@ -185,14 +185,6 @@ int lis3l02dq_probe_trigger(struct iio_dev *indio_dev);
|
|||
int lis3l02dq_configure_buffer(struct iio_dev *indio_dev);
|
||||
void lis3l02dq_unconfigure_buffer(struct iio_dev *indio_dev);
|
||||
|
||||
#ifdef CONFIG_LIS3L02DQ_BUF_RING_SW
|
||||
#define lis3l02dq_free_buf iio_sw_rb_free
|
||||
#define lis3l02dq_alloc_buf iio_sw_rb_allocate
|
||||
#endif
|
||||
#ifdef CONFIG_LIS3L02DQ_BUF_KFIFO
|
||||
#define lis3l02dq_free_buf iio_kfifo_free
|
||||
#define lis3l02dq_alloc_buf iio_kfifo_allocate
|
||||
#endif
|
||||
irqreturn_t lis3l02dq_data_rdy_trig_poll(int irq, void *private);
|
||||
#define lis3l02dq_th lis3l02dq_data_rdy_trig_poll
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include <linux/export.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include "../ring_sw.h"
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
|
@ -318,7 +317,7 @@ void lis3l02dq_remove_trigger(struct iio_dev *indio_dev)
|
|||
void lis3l02dq_unconfigure_buffer(struct iio_dev *indio_dev)
|
||||
{
|
||||
iio_dealloc_pollfunc(indio_dev->pollfunc);
|
||||
lis3l02dq_free_buf(indio_dev->buffer);
|
||||
iio_kfifo_free(indio_dev->buffer);
|
||||
}
|
||||
|
||||
static int lis3l02dq_buffer_postenable(struct iio_dev *indio_dev)
|
||||
|
@ -401,7 +400,7 @@ int lis3l02dq_configure_buffer(struct iio_dev *indio_dev)
|
|||
int ret;
|
||||
struct iio_buffer *buffer;
|
||||
|
||||
buffer = lis3l02dq_alloc_buf(indio_dev);
|
||||
buffer = iio_kfifo_allocate(indio_dev);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -427,6 +426,6 @@ int lis3l02dq_configure_buffer(struct iio_dev *indio_dev)
|
|||
return 0;
|
||||
|
||||
error_iio_sw_rb_free:
|
||||
lis3l02dq_free_buf(indio_dev->buffer);
|
||||
iio_kfifo_free(indio_dev->buffer);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -119,12 +119,12 @@ config LPC32XX_ADC
|
|||
via sysfs.
|
||||
|
||||
config MXS_LRADC
|
||||
tristate "Freescale i.MX28 LRADC"
|
||||
tristate "Freescale i.MX23/i.MX28 LRADC"
|
||||
depends on ARCH_MXS
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for i.MX28 LRADC convertor
|
||||
Say yes here to build support for i.MX23/i.MX28 LRADC convertor
|
||||
built into these chips.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
#include <linux/stmp_device.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
#include <mach/mxs.h>
|
||||
#include <mach/common.h>
|
||||
|
@ -59,7 +61,39 @@
|
|||
#define LRADC_DELAY_TIMER_PER 200
|
||||
#define LRADC_DELAY_TIMER_LOOP 5
|
||||
|
||||
static const char * const mxs_lradc_irq_name[] = {
|
||||
/*
|
||||
* Once the pen touches the touchscreen, the touchscreen switches from
|
||||
* IRQ-driven mode to polling mode to prevent interrupt storm. The polling
|
||||
* is realized by worker thread, which is called every 20 or so milliseconds.
|
||||
* This gives the touchscreen enough fluence and does not strain the system
|
||||
* too much.
|
||||
*/
|
||||
#define LRADC_TS_SAMPLE_DELAY_MS 5
|
||||
|
||||
/*
|
||||
* The LRADC reads the following amount of samples from each touchscreen
|
||||
* channel and the driver then computes avarage of these.
|
||||
*/
|
||||
#define LRADC_TS_SAMPLE_AMOUNT 4
|
||||
|
||||
enum mxs_lradc_id {
|
||||
IMX23_LRADC,
|
||||
IMX28_LRADC,
|
||||
};
|
||||
|
||||
static const char * const mx23_lradc_irq_names[] = {
|
||||
"mxs-lradc-touchscreen",
|
||||
"mxs-lradc-channel0",
|
||||
"mxs-lradc-channel1",
|
||||
"mxs-lradc-channel2",
|
||||
"mxs-lradc-channel3",
|
||||
"mxs-lradc-channel4",
|
||||
"mxs-lradc-channel5",
|
||||
"mxs-lradc-channel6",
|
||||
"mxs-lradc-channel7",
|
||||
};
|
||||
|
||||
static const char * const mx28_lradc_irq_names[] = {
|
||||
"mxs-lradc-touchscreen",
|
||||
"mxs-lradc-thresh0",
|
||||
"mxs-lradc-thresh1",
|
||||
|
@ -75,6 +109,28 @@ static const char * const mxs_lradc_irq_name[] = {
|
|||
"mxs-lradc-button1",
|
||||
};
|
||||
|
||||
struct mxs_lradc_of_config {
|
||||
const int irq_count;
|
||||
const char * const *irq_name;
|
||||
};
|
||||
|
||||
static const struct mxs_lradc_of_config const mxs_lradc_of_config[] = {
|
||||
[IMX23_LRADC] = {
|
||||
.irq_count = ARRAY_SIZE(mx23_lradc_irq_names),
|
||||
.irq_name = mx23_lradc_irq_names,
|
||||
},
|
||||
[IMX28_LRADC] = {
|
||||
.irq_count = ARRAY_SIZE(mx28_lradc_irq_names),
|
||||
.irq_name = mx28_lradc_irq_names,
|
||||
},
|
||||
};
|
||||
|
||||
enum mxs_lradc_ts {
|
||||
MXS_LRADC_TOUCHSCREEN_NONE = 0,
|
||||
MXS_LRADC_TOUCHSCREEN_4WIRE,
|
||||
MXS_LRADC_TOUCHSCREEN_5WIRE,
|
||||
};
|
||||
|
||||
struct mxs_lradc {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
|
@ -86,21 +142,69 @@ struct mxs_lradc {
|
|||
struct mutex lock;
|
||||
|
||||
struct completion completion;
|
||||
|
||||
/*
|
||||
* Touchscreen LRADC channels receives a private slot in the CTRL4
|
||||
* register, the slot #7. Therefore only 7 slots instead of 8 in the
|
||||
* CTRL4 register can be mapped to LRADC channels when using the
|
||||
* touchscreen.
|
||||
*
|
||||
* Furthermore, certain LRADC channels are shared between touchscreen
|
||||
* and/or touch-buttons and generic LRADC block. Therefore when using
|
||||
* either of these, these channels are not available for the regular
|
||||
* sampling. The shared channels are as follows:
|
||||
*
|
||||
* CH0 -- Touch button #0
|
||||
* CH1 -- Touch button #1
|
||||
* CH2 -- Touch screen XPUL
|
||||
* CH3 -- Touch screen YPLL
|
||||
* CH4 -- Touch screen XNUL
|
||||
* CH5 -- Touch screen YNLR
|
||||
* CH6 -- Touch screen WIPER (5-wire only)
|
||||
*
|
||||
* The bitfields below represents which parts of the LRADC block are
|
||||
* switched into special mode of operation. These channels can not
|
||||
* be sampled as regular LRADC channels. The driver will refuse any
|
||||
* attempt to sample these channels.
|
||||
*/
|
||||
#define CHAN_MASK_TOUCHBUTTON (0x3 << 0)
|
||||
#define CHAN_MASK_TOUCHSCREEN_4WIRE (0xf << 2)
|
||||
#define CHAN_MASK_TOUCHSCREEN_5WIRE (0x1f << 2)
|
||||
enum mxs_lradc_ts use_touchscreen;
|
||||
bool stop_touchscreen;
|
||||
bool use_touchbutton;
|
||||
|
||||
struct input_dev *ts_input;
|
||||
struct work_struct ts_work;
|
||||
};
|
||||
|
||||
#define LRADC_CTRL0 0x00
|
||||
#define LRADC_CTRL0_TOUCH_DETECT_ENABLE (1 << 23)
|
||||
#define LRADC_CTRL0_TOUCH_SCREEN_TYPE (1 << 22)
|
||||
#define LRADC_CTRL0_TOUCH_DETECT_ENABLE (1 << 23)
|
||||
#define LRADC_CTRL0_TOUCH_SCREEN_TYPE (1 << 22)
|
||||
#define LRADC_CTRL0_YNNSW /* YM */ (1 << 21)
|
||||
#define LRADC_CTRL0_YPNSW /* YP */ (1 << 20)
|
||||
#define LRADC_CTRL0_YPPSW /* YP */ (1 << 19)
|
||||
#define LRADC_CTRL0_XNNSW /* XM */ (1 << 18)
|
||||
#define LRADC_CTRL0_XNPSW /* XM */ (1 << 17)
|
||||
#define LRADC_CTRL0_XPPSW /* XP */ (1 << 16)
|
||||
#define LRADC_CTRL0_PLATE_MASK (0x3f << 16)
|
||||
|
||||
#define LRADC_CTRL1 0x10
|
||||
#define LRADC_CTRL1_LRADC_IRQ(n) (1 << (n))
|
||||
#define LRADC_CTRL1_LRADC_IRQ_MASK 0x1fff
|
||||
#define LRADC_CTRL1_TOUCH_DETECT_IRQ_EN (1 << 24)
|
||||
#define LRADC_CTRL1_LRADC_IRQ_EN(n) (1 << ((n) + 16))
|
||||
#define LRADC_CTRL1_LRADC_IRQ_EN_MASK (0x1fff << 16)
|
||||
#define LRADC_CTRL1_LRADC_IRQ_EN_OFFSET 16
|
||||
#define LRADC_CTRL1_TOUCH_DETECT_IRQ (1 << 8)
|
||||
#define LRADC_CTRL1_LRADC_IRQ(n) (1 << (n))
|
||||
#define LRADC_CTRL1_LRADC_IRQ_MASK 0x1fff
|
||||
#define LRADC_CTRL1_LRADC_IRQ_OFFSET 0
|
||||
|
||||
#define LRADC_CTRL2 0x20
|
||||
#define LRADC_CTRL2_TEMPSENSE_PWD (1 << 15)
|
||||
|
||||
#define LRADC_STATUS 0x40
|
||||
#define LRADC_STATUS_TOUCH_DETECT_RAW (1 << 0)
|
||||
|
||||
#define LRADC_CH(n) (0x50 + (0x10 * (n)))
|
||||
#define LRADC_CH_ACCUMULATE (1 << 29)
|
||||
#define LRADC_CH_NUM_SAMPLES_MASK (0x1f << 24)
|
||||
|
@ -132,6 +236,7 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
|
|||
{
|
||||
struct mxs_lradc *lradc = iio_priv(iio_dev);
|
||||
int ret;
|
||||
unsigned long mask;
|
||||
|
||||
if (m != IIO_CHAN_INFO_RAW)
|
||||
return -EINVAL;
|
||||
|
@ -140,6 +245,12 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
|
|||
if (chan->channel > LRADC_MAX_TOTAL_CHANS)
|
||||
return -EINVAL;
|
||||
|
||||
/* Validate the channel if it doesn't intersect with reserved chans. */
|
||||
bitmap_set(&mask, chan->channel, 1);
|
||||
ret = iio_validate_scan_mask_onehot(iio_dev, &mask);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* See if there is no buffered operation in progess. If there is, simply
|
||||
* bail out. This can be improved to support both buffered and raw IO at
|
||||
|
@ -161,7 +272,11 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
|
|||
lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
writel(0xff, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
|
||||
writel(chan->channel, lradc->base + LRADC_CTRL4);
|
||||
/* Clean the slot's previous content, then set new one. */
|
||||
writel(LRADC_CTRL4_LRADCSELECT_MASK(0),
|
||||
lradc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR);
|
||||
writel(chan->channel, lradc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET);
|
||||
|
||||
writel(0, lradc->base + LRADC_CH(0));
|
||||
|
||||
/* Enable the IRQ and start sampling the channel. */
|
||||
|
@ -194,6 +309,269 @@ static const struct iio_info mxs_lradc_iio_info = {
|
|||
.read_raw = mxs_lradc_read_raw,
|
||||
};
|
||||
|
||||
/*
|
||||
* Touchscreen handling
|
||||
*/
|
||||
enum lradc_ts_plate {
|
||||
LRADC_SAMPLE_X,
|
||||
LRADC_SAMPLE_Y,
|
||||
LRADC_SAMPLE_PRESSURE,
|
||||
};
|
||||
|
||||
static int mxs_lradc_ts_touched(struct mxs_lradc *lradc)
|
||||
{
|
||||
uint32_t reg;
|
||||
|
||||
/* Enable touch detection. */
|
||||
writel(LRADC_CTRL0_PLATE_MASK,
|
||||
lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
writel(LRADC_CTRL0_TOUCH_DETECT_ENABLE,
|
||||
lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
|
||||
msleep(LRADC_TS_SAMPLE_DELAY_MS);
|
||||
|
||||
reg = readl(lradc->base + LRADC_STATUS);
|
||||
|
||||
return reg & LRADC_STATUS_TOUCH_DETECT_RAW;
|
||||
}
|
||||
|
||||
static int32_t mxs_lradc_ts_sample(struct mxs_lradc *lradc,
|
||||
enum lradc_ts_plate plate, int change)
|
||||
{
|
||||
unsigned long delay, jiff;
|
||||
uint32_t reg, ctrl0 = 0, chan = 0;
|
||||
/* The touchscreen always uses CTRL4 slot #7. */
|
||||
const uint8_t slot = 7;
|
||||
uint32_t val;
|
||||
|
||||
/*
|
||||
* There are three correct configurations of the controller sampling
|
||||
* the touchscreen, each of these configuration provides different
|
||||
* information from the touchscreen.
|
||||
*
|
||||
* The following table describes the sampling configurations:
|
||||
* +-------------+-------+-------+-------+
|
||||
* | Wire \ Axis | X | Y | Z |
|
||||
* +---------------------+-------+-------+
|
||||
* | X+ (CH2) | HI | TS | TS |
|
||||
* +-------------+-------+-------+-------+
|
||||
* | X- (CH4) | LO | SH | HI |
|
||||
* +-------------+-------+-------+-------+
|
||||
* | Y+ (CH3) | SH | HI | HI |
|
||||
* +-------------+-------+-------+-------+
|
||||
* | Y- (CH5) | TS | LO | SH |
|
||||
* +-------------+-------+-------+-------+
|
||||
*
|
||||
* HI ... strong '1' ; LO ... strong '0'
|
||||
* SH ... sample here ; TS ... tri-state
|
||||
*
|
||||
* There are a few other ways of obtaining the Z coordinate
|
||||
* (aka. pressure), but the one in the table seems to be the
|
||||
* most reliable one.
|
||||
*/
|
||||
switch (plate) {
|
||||
case LRADC_SAMPLE_X:
|
||||
ctrl0 = LRADC_CTRL0_XPPSW | LRADC_CTRL0_XNNSW;
|
||||
chan = 3;
|
||||
break;
|
||||
case LRADC_SAMPLE_Y:
|
||||
ctrl0 = LRADC_CTRL0_YPPSW | LRADC_CTRL0_YNNSW;
|
||||
chan = 4;
|
||||
break;
|
||||
case LRADC_SAMPLE_PRESSURE:
|
||||
ctrl0 = LRADC_CTRL0_YPPSW | LRADC_CTRL0_XNNSW;
|
||||
chan = 5;
|
||||
break;
|
||||
}
|
||||
|
||||
if (change) {
|
||||
writel(LRADC_CTRL0_PLATE_MASK,
|
||||
lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
writel(ctrl0, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
|
||||
writel(LRADC_CTRL4_LRADCSELECT_MASK(slot),
|
||||
lradc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR);
|
||||
writel(chan << LRADC_CTRL4_LRADCSELECT_OFFSET(slot),
|
||||
lradc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET);
|
||||
}
|
||||
|
||||
writel(0xffffffff, lradc->base + LRADC_CH(slot) + STMP_OFFSET_REG_CLR);
|
||||
writel(1 << slot, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
|
||||
delay = jiffies + msecs_to_jiffies(LRADC_TS_SAMPLE_DELAY_MS);
|
||||
do {
|
||||
jiff = jiffies;
|
||||
reg = readl_relaxed(lradc->base + LRADC_CTRL1);
|
||||
if (reg & LRADC_CTRL1_LRADC_IRQ(slot))
|
||||
break;
|
||||
} while (time_before(jiff, delay));
|
||||
|
||||
writel(LRADC_CTRL1_LRADC_IRQ(slot),
|
||||
lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
|
||||
if (time_after_eq(jiff, delay))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
val = readl(lradc->base + LRADC_CH(slot));
|
||||
val &= LRADC_CH_VALUE_MASK;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int32_t mxs_lradc_ts_sample_filter(struct mxs_lradc *lradc,
|
||||
enum lradc_ts_plate plate)
|
||||
{
|
||||
int32_t val, tot = 0;
|
||||
int i;
|
||||
|
||||
val = mxs_lradc_ts_sample(lradc, plate, 1);
|
||||
|
||||
/* Delay a bit so the touchscreen is stable. */
|
||||
mdelay(2);
|
||||
|
||||
for (i = 0; i < LRADC_TS_SAMPLE_AMOUNT; i++) {
|
||||
val = mxs_lradc_ts_sample(lradc, plate, 0);
|
||||
tot += val;
|
||||
}
|
||||
|
||||
return tot / LRADC_TS_SAMPLE_AMOUNT;
|
||||
}
|
||||
|
||||
static void mxs_lradc_ts_work(struct work_struct *ts_work)
|
||||
{
|
||||
struct mxs_lradc *lradc = container_of(ts_work,
|
||||
struct mxs_lradc, ts_work);
|
||||
int val_x, val_y, val_p;
|
||||
bool valid = false;
|
||||
|
||||
while (mxs_lradc_ts_touched(lradc)) {
|
||||
/* Disable touch detector so we can sample the touchscreen. */
|
||||
writel(LRADC_CTRL0_TOUCH_DETECT_ENABLE,
|
||||
lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
|
||||
if (likely(valid)) {
|
||||
input_report_abs(lradc->ts_input, ABS_X, val_x);
|
||||
input_report_abs(lradc->ts_input, ABS_Y, val_y);
|
||||
input_report_abs(lradc->ts_input, ABS_PRESSURE, val_p);
|
||||
input_report_key(lradc->ts_input, BTN_TOUCH, 1);
|
||||
input_sync(lradc->ts_input);
|
||||
}
|
||||
|
||||
valid = false;
|
||||
|
||||
val_x = mxs_lradc_ts_sample_filter(lradc, LRADC_SAMPLE_X);
|
||||
if (val_x < 0)
|
||||
continue;
|
||||
val_y = mxs_lradc_ts_sample_filter(lradc, LRADC_SAMPLE_Y);
|
||||
if (val_y < 0)
|
||||
continue;
|
||||
val_p = mxs_lradc_ts_sample_filter(lradc, LRADC_SAMPLE_PRESSURE);
|
||||
if (val_p < 0)
|
||||
continue;
|
||||
|
||||
valid = true;
|
||||
}
|
||||
|
||||
input_report_abs(lradc->ts_input, ABS_PRESSURE, 0);
|
||||
input_report_key(lradc->ts_input, BTN_TOUCH, 0);
|
||||
input_sync(lradc->ts_input);
|
||||
|
||||
/* Do not restart the TS IRQ if the driver is shutting down. */
|
||||
if (lradc->stop_touchscreen)
|
||||
return;
|
||||
|
||||
/* Restart the touchscreen interrupts. */
|
||||
writel(LRADC_CTRL1_TOUCH_DETECT_IRQ,
|
||||
lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
|
||||
lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
|
||||
}
|
||||
|
||||
static int mxs_lradc_ts_open(struct input_dev *dev)
|
||||
{
|
||||
struct mxs_lradc *lradc = input_get_drvdata(dev);
|
||||
|
||||
/* The touchscreen is starting. */
|
||||
lradc->stop_touchscreen = false;
|
||||
|
||||
/* Enable the touch-detect circuitry. */
|
||||
writel(LRADC_CTRL0_TOUCH_DETECT_ENABLE,
|
||||
lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
|
||||
/* Enable the touch-detect IRQ. */
|
||||
writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
|
||||
lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mxs_lradc_ts_close(struct input_dev *dev)
|
||||
{
|
||||
struct mxs_lradc *lradc = input_get_drvdata(dev);
|
||||
|
||||
/* Indicate the touchscreen is stopping. */
|
||||
lradc->stop_touchscreen = true;
|
||||
mb();
|
||||
|
||||
/* Wait until touchscreen thread finishes any possible remnants. */
|
||||
cancel_work_sync(&lradc->ts_work);
|
||||
|
||||
/* Disable touchscreen touch-detect IRQ. */
|
||||
writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
|
||||
lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
|
||||
/* Power-down touchscreen touch-detect circuitry. */
|
||||
writel(LRADC_CTRL0_TOUCH_DETECT_ENABLE,
|
||||
lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
}
|
||||
|
||||
static int mxs_lradc_ts_register(struct mxs_lradc *lradc)
|
||||
{
|
||||
struct input_dev *input;
|
||||
struct device *dev = lradc->dev;
|
||||
int ret;
|
||||
|
||||
if (!lradc->use_touchscreen)
|
||||
return 0;
|
||||
|
||||
input = input_allocate_device();
|
||||
if (!input) {
|
||||
dev_err(dev, "Failed to allocate TS device!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
input->name = DRIVER_NAME;
|
||||
input->id.bustype = BUS_HOST;
|
||||
input->dev.parent = dev;
|
||||
input->open = mxs_lradc_ts_open;
|
||||
input->close = mxs_lradc_ts_close;
|
||||
|
||||
__set_bit(EV_ABS, input->evbit);
|
||||
__set_bit(EV_KEY, input->evbit);
|
||||
__set_bit(BTN_TOUCH, input->keybit);
|
||||
input_set_abs_params(input, ABS_X, 0, LRADC_CH_VALUE_MASK, 0, 0);
|
||||
input_set_abs_params(input, ABS_Y, 0, LRADC_CH_VALUE_MASK, 0, 0);
|
||||
input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_CH_VALUE_MASK, 0, 0);
|
||||
|
||||
lradc->ts_input = input;
|
||||
input_set_drvdata(input, lradc);
|
||||
ret = input_register_device(input);
|
||||
if (ret)
|
||||
input_free_device(lradc->ts_input);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mxs_lradc_ts_unregister(struct mxs_lradc *lradc)
|
||||
{
|
||||
if (!lradc->use_touchscreen)
|
||||
return;
|
||||
|
||||
cancel_work_sync(&lradc->ts_work);
|
||||
|
||||
input_unregister_device(lradc->ts_input);
|
||||
}
|
||||
|
||||
/*
|
||||
* IRQ Handling
|
||||
*/
|
||||
|
@ -202,14 +580,24 @@ static irqreturn_t mxs_lradc_handle_irq(int irq, void *data)
|
|||
struct iio_dev *iio = data;
|
||||
struct mxs_lradc *lradc = iio_priv(iio);
|
||||
unsigned long reg = readl(lradc->base + LRADC_CTRL1);
|
||||
const uint32_t ts_irq_mask =
|
||||
LRADC_CTRL1_TOUCH_DETECT_IRQ_EN |
|
||||
LRADC_CTRL1_TOUCH_DETECT_IRQ;
|
||||
|
||||
if (!(reg & LRADC_CTRL1_LRADC_IRQ_MASK))
|
||||
return IRQ_NONE;
|
||||
|
||||
/*
|
||||
* Touchscreen IRQ handling code shall probably have priority
|
||||
* and therefore shall be placed here.
|
||||
* Touchscreen IRQ handling code has priority and therefore
|
||||
* is placed here. In case touchscreen IRQ arrives, disable
|
||||
* it ASAP
|
||||
*/
|
||||
if (reg & LRADC_CTRL1_TOUCH_DETECT_IRQ) {
|
||||
writel(ts_irq_mask,
|
||||
lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
|
||||
if (!lradc->stop_touchscreen)
|
||||
schedule_work(&lradc->ts_work);
|
||||
}
|
||||
|
||||
if (iio_buffer_enabled(iio))
|
||||
iio_trigger_poll(iio->trig, iio_get_time_ns());
|
||||
|
@ -305,8 +693,10 @@ static int mxs_lradc_buffer_preenable(struct iio_dev *iio)
|
|||
{
|
||||
struct mxs_lradc *lradc = iio_priv(iio);
|
||||
struct iio_buffer *buffer = iio->buffer;
|
||||
int ret = 0, chan, ofs = 0, enable = 0;
|
||||
uint32_t ctrl4 = 0;
|
||||
int ret = 0, chan, ofs = 0;
|
||||
unsigned long enable = 0;
|
||||
uint32_t ctrl4_set = 0;
|
||||
uint32_t ctrl4_clr = 0;
|
||||
uint32_t ctrl1_irq = 0;
|
||||
const uint32_t chan_value = LRADC_CH_ACCUMULATE |
|
||||
((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
|
||||
|
@ -338,17 +728,20 @@ static int mxs_lradc_buffer_preenable(struct iio_dev *iio)
|
|||
writel(0xff, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
|
||||
for_each_set_bit(chan, buffer->scan_mask, LRADC_MAX_TOTAL_CHANS) {
|
||||
ctrl4 |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs);
|
||||
ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs);
|
||||
ctrl4_clr |= LRADC_CTRL4_LRADCSELECT_MASK(ofs);
|
||||
ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs);
|
||||
writel(chan_value, lradc->base + LRADC_CH(ofs));
|
||||
enable |= 1 << ofs;
|
||||
bitmap_set(&enable, ofs, 1);
|
||||
ofs++;
|
||||
}
|
||||
|
||||
writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK,
|
||||
lradc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR);
|
||||
|
||||
writel(ctrl4, lradc->base + LRADC_CTRL4);
|
||||
writel(ctrl4_clr, lradc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR);
|
||||
writel(ctrl4_set, lradc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET);
|
||||
|
||||
writel(ctrl1_irq, lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
|
||||
|
||||
writel(enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET,
|
||||
|
@ -383,9 +776,33 @@ static int mxs_lradc_buffer_postdisable(struct iio_dev *iio)
|
|||
static bool mxs_lradc_validate_scan_mask(struct iio_dev *iio,
|
||||
const unsigned long *mask)
|
||||
{
|
||||
const int mw = bitmap_weight(mask, iio->masklength);
|
||||
struct mxs_lradc *lradc = iio_priv(iio);
|
||||
const int len = iio->masklength;
|
||||
const int map_chans = bitmap_weight(mask, len);
|
||||
int rsvd_chans = 0;
|
||||
unsigned long rsvd_mask = 0;
|
||||
|
||||
return mw <= LRADC_MAX_MAPPED_CHANS;
|
||||
if (lradc->use_touchbutton)
|
||||
rsvd_mask |= CHAN_MASK_TOUCHBUTTON;
|
||||
if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_4WIRE)
|
||||
rsvd_mask |= CHAN_MASK_TOUCHSCREEN_4WIRE;
|
||||
if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
|
||||
rsvd_mask |= CHAN_MASK_TOUCHSCREEN_5WIRE;
|
||||
|
||||
if (lradc->use_touchbutton)
|
||||
rsvd_chans++;
|
||||
if (lradc->use_touchscreen)
|
||||
rsvd_chans++;
|
||||
|
||||
/* Test for attempts to map channels with special mode of operation. */
|
||||
if (bitmap_intersects(mask, &rsvd_mask, len))
|
||||
return false;
|
||||
|
||||
/* Test for attempts to map more channels then available slots. */
|
||||
if (map_chans + rsvd_chans > LRADC_MAX_MAPPED_CHANS)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops mxs_lradc_buffer_ops = {
|
||||
|
@ -434,15 +851,29 @@ static const struct iio_chan_spec mxs_lradc_chan_spec[] = {
|
|||
|
||||
static void mxs_lradc_hw_init(struct mxs_lradc *lradc)
|
||||
{
|
||||
int i;
|
||||
const uint32_t cfg =
|
||||
/* The ADC always uses DELAY CHANNEL 0. */
|
||||
const uint32_t adc_cfg =
|
||||
(1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + 0)) |
|
||||
(LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET);
|
||||
|
||||
stmp_reset_block(lradc->base);
|
||||
|
||||
for (i = 0; i < LRADC_MAX_DELAY_CHANS; i++)
|
||||
writel(cfg | (1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + i)),
|
||||
lradc->base + LRADC_DELAY(i));
|
||||
/* Configure DELAY CHANNEL 0 for generic ADC sampling. */
|
||||
writel(adc_cfg, lradc->base + LRADC_DELAY(0));
|
||||
|
||||
/* Disable remaining DELAY CHANNELs */
|
||||
writel(0, lradc->base + LRADC_DELAY(1));
|
||||
writel(0, lradc->base + LRADC_DELAY(2));
|
||||
writel(0, lradc->base + LRADC_DELAY(3));
|
||||
|
||||
/* Configure the touchscreen type */
|
||||
writel(LRADC_CTRL0_TOUCH_SCREEN_TYPE,
|
||||
lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
|
||||
|
||||
if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE) {
|
||||
writel(LRADC_CTRL0_TOUCH_SCREEN_TYPE,
|
||||
lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
|
||||
}
|
||||
|
||||
/* Start internal temperature sensing. */
|
||||
writel(0, lradc->base + LRADC_CTRL2);
|
||||
|
@ -459,12 +890,25 @@ static void mxs_lradc_hw_stop(struct mxs_lradc *lradc)
|
|||
writel(0, lradc->base + LRADC_DELAY(i));
|
||||
}
|
||||
|
||||
static const struct of_device_id mxs_lradc_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx23-lradc", .data = (void *)IMX23_LRADC, },
|
||||
{ .compatible = "fsl,imx28-lradc", .data = (void *)IMX28_LRADC, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids);
|
||||
|
||||
static int mxs_lradc_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(mxs_lradc_dt_ids, &pdev->dev);
|
||||
const struct mxs_lradc_of_config *of_cfg =
|
||||
&mxs_lradc_of_config[(enum mxs_lradc_id)of_id->data];
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct mxs_lradc *lradc;
|
||||
struct iio_dev *iio;
|
||||
struct resource *iores;
|
||||
uint32_t ts_wires = 0;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
|
@ -486,8 +930,23 @@ static int mxs_lradc_probe(struct platform_device *pdev)
|
|||
goto err_addr;
|
||||
}
|
||||
|
||||
INIT_WORK(&lradc->ts_work, mxs_lradc_ts_work);
|
||||
|
||||
/* Check if touchscreen is enabled in DT. */
|
||||
ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
|
||||
&ts_wires);
|
||||
if (ret)
|
||||
dev_info(dev, "Touchscreen not enabled.\n");
|
||||
else if (ts_wires == 4)
|
||||
lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_4WIRE;
|
||||
else if (ts_wires == 5)
|
||||
lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_5WIRE;
|
||||
else
|
||||
dev_warn(dev, "Unsupported number of touchscreen wires (%d)\n",
|
||||
ts_wires);
|
||||
|
||||
/* Grab all IRQ sources */
|
||||
for (i = 0; i < 13; i++) {
|
||||
for (i = 0; i < of_cfg->irq_count; i++) {
|
||||
lradc->irq[i] = platform_get_irq(pdev, i);
|
||||
if (lradc->irq[i] < 0) {
|
||||
ret = -EINVAL;
|
||||
|
@ -496,7 +955,7 @@ static int mxs_lradc_probe(struct platform_device *pdev)
|
|||
|
||||
ret = devm_request_irq(dev, lradc->irq[i],
|
||||
mxs_lradc_handle_irq, 0,
|
||||
mxs_lradc_irq_name[i], iio);
|
||||
of_cfg->irq_name[i], iio);
|
||||
if (ret)
|
||||
goto err_addr;
|
||||
}
|
||||
|
@ -523,11 +982,16 @@ static int mxs_lradc_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto err_trig;
|
||||
|
||||
/* Register the touchscreen input device. */
|
||||
ret = mxs_lradc_ts_register(lradc);
|
||||
if (ret)
|
||||
goto err_dev;
|
||||
|
||||
/* Register IIO device. */
|
||||
ret = iio_device_register(iio);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register IIO device\n");
|
||||
goto err_dev;
|
||||
goto err_ts;
|
||||
}
|
||||
|
||||
/* Configure the hardware. */
|
||||
|
@ -535,6 +999,8 @@ static int mxs_lradc_probe(struct platform_device *pdev)
|
|||
|
||||
return 0;
|
||||
|
||||
err_ts:
|
||||
mxs_lradc_ts_unregister(lradc);
|
||||
err_dev:
|
||||
mxs_lradc_trigger_remove(iio);
|
||||
err_trig:
|
||||
|
@ -549,6 +1015,8 @@ static int mxs_lradc_remove(struct platform_device *pdev)
|
|||
struct iio_dev *iio = platform_get_drvdata(pdev);
|
||||
struct mxs_lradc *lradc = iio_priv(iio);
|
||||
|
||||
mxs_lradc_ts_unregister(lradc);
|
||||
|
||||
mxs_lradc_hw_stop(lradc);
|
||||
|
||||
iio_device_unregister(iio);
|
||||
|
@ -559,12 +1027,6 @@ static int mxs_lradc_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mxs_lradc_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx28-lradc", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids);
|
||||
|
||||
static struct platform_driver mxs_lradc_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
|
|
|
@ -10,13 +10,6 @@ config ADIS16060
|
|||
Say yes here to build support for Analog Devices adis16060 wide bandwidth
|
||||
yaw rate gyroscope with SPI.
|
||||
|
||||
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/100 Yaw Rate
|
||||
Gyroscope with SPI.
|
||||
|
||||
config ADIS16130
|
||||
tristate "Analog Devices ADIS16130 High Precision Angular Rate Sensor driver"
|
||||
depends on SPI
|
||||
|
|
|
@ -5,17 +5,11 @@
|
|||
adis16060-y := adis16060_core.o
|
||||
obj-$(CONFIG_ADIS16060) += adis16060.o
|
||||
|
||||
adis16080-y := adis16080_core.o
|
||||
obj-$(CONFIG_ADIS16080) += adis16080.o
|
||||
|
||||
adis16130-y := adis16130_core.o
|
||||
obj-$(CONFIG_ADIS16130) += adis16130.o
|
||||
|
||||
adis16260-y := adis16260_core.o
|
||||
obj-$(CONFIG_ADIS16260) += adis16260.o
|
||||
|
||||
adis16251-y := adis16251_core.o
|
||||
obj-$(CONFIG_ADIS16251) += adis16251.o
|
||||
|
||||
adxrs450-y := adxrs450_core.o
|
||||
obj-$(CONFIG_ADXRS450) += adxrs450.o
|
||||
|
|
|
@ -93,7 +93,7 @@ static int iio_hwmon_probe(struct platform_device *pdev)
|
|||
while (st->channels[st->num_channels].indio_dev)
|
||||
st->num_channels++;
|
||||
|
||||
st->attrs = kzalloc(sizeof(st->attrs) * (st->num_channels + 1),
|
||||
st->attrs = kzalloc(sizeof(*st->attrs) * (st->num_channels + 1),
|
||||
GFP_KERNEL);
|
||||
if (st->attrs == NULL) {
|
||||
ret = -ENOMEM;
|
||||
|
|
|
@ -7,7 +7,7 @@ config AD5933
|
|||
tristate "Analog Devices AD5933, AD5934 driver"
|
||||
depends on I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_SW_RING
|
||||
select IIO_KFIFO_BUF
|
||||
help
|
||||
Say yes here to build support for Analog Devices Impedance Converter,
|
||||
Network Analyzer, AD5933/4, provides direct access via sysfs.
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include "../ring_sw.h"
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
|
||||
#include "ad5933.h"
|
||||
|
||||
|
@ -630,7 +630,7 @@ static const struct iio_buffer_setup_ops ad5933_ring_setup_ops = {
|
|||
|
||||
static int ad5933_register_ring_funcs_and_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
indio_dev->buffer = iio_sw_rb_allocate(indio_dev);
|
||||
indio_dev->buffer = iio_kfifo_allocate(indio_dev);
|
||||
if (!indio_dev->buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -774,7 +774,7 @@ static int ad5933_probe(struct i2c_client *client,
|
|||
error_uninitialize_ring:
|
||||
iio_buffer_unregister(indio_dev);
|
||||
error_unreg_ring:
|
||||
iio_sw_rb_free(indio_dev->buffer);
|
||||
iio_kfifo_free(indio_dev->buffer);
|
||||
error_disable_reg:
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
@ -794,7 +794,7 @@ static int ad5933_remove(struct i2c_client *client)
|
|||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_buffer_unregister(indio_dev);
|
||||
iio_sw_rb_free(indio_dev->buffer);
|
||||
iio_kfifo_free(indio_dev->buffer);
|
||||
if (!IS_ERR(st->reg)) {
|
||||
regulator_disable(st->reg);
|
||||
regulator_put(st->reg);
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
#
|
||||
# IIO imu drivers configuration
|
||||
#
|
||||
menu "Inertial measurement units"
|
||||
|
||||
config ADIS16400
|
||||
tristate "Analog Devices ADIS16400 and similar IMU SPI driver"
|
||||
depends on SPI
|
||||
select IIO_SW_RING if IIO_BUFFER
|
||||
select IIO_TRIGGER if IIO_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices adis16300, adis16344,
|
||||
adis16350, adis16354, adis16355, adis16360, adis16362, adis16364,
|
||||
adis16365, adis16400 and adis16405 triaxial inertial sensors
|
||||
(adis16400 series also have magnetometers).
|
||||
|
||||
endmenu
|
|
@ -1,7 +0,0 @@
|
|||
#
|
||||
# Makefile for Inertial Measurement Units
|
||||
#
|
||||
|
||||
adis16400-y := adis16400_core.o
|
||||
adis16400-$(CONFIG_IIO_BUFFER) += adis16400_ring.o adis16400_trigger.o
|
||||
obj-$(CONFIG_ADIS16400) += adis16400.o
|
File diff suppressed because it is too large
Load diff
|
@ -1,204 +0,0 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include "../ring_sw.h"
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include "adis16400.h"
|
||||
|
||||
/**
|
||||
* adis16400_spi_read_burst() - read all data registers
|
||||
* @indio_dev: the IIO device
|
||||
* @rx: somewhere to pass back the value read (min size is 24 bytes)
|
||||
**/
|
||||
static int adis16400_spi_read_burst(struct iio_dev *indio_dev, u8 *rx)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
u32 old_speed_hz = st->us->max_speed_hz;
|
||||
int ret;
|
||||
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.tx_buf = st->tx,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
}, {
|
||||
.rx_buf = rx,
|
||||
.bits_per_word = 8,
|
||||
.len = 24,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = ADIS16400_READ_REG(ADIS16400_GLOB_CMD);
|
||||
st->tx[1] = 0;
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfers[0], &msg);
|
||||
spi_message_add_tail(&xfers[1], &msg);
|
||||
|
||||
st->us->max_speed_hz = min(ADIS16400_SPI_BURST, old_speed_hz);
|
||||
spi_setup(st->us);
|
||||
|
||||
ret = spi_sync(st->us, &msg);
|
||||
if (ret)
|
||||
dev_err(&st->us->dev, "problem when burst reading");
|
||||
|
||||
st->us->max_speed_hz = old_speed_hz;
|
||||
spi_setup(st->us);
|
||||
mutex_unlock(&st->buf_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const u16 read_all_tx_array[] = {
|
||||
cpu_to_be16(ADIS16400_READ_REG(ADIS16400_SUPPLY_OUT)),
|
||||
cpu_to_be16(ADIS16400_READ_REG(ADIS16400_XGYRO_OUT)),
|
||||
cpu_to_be16(ADIS16400_READ_REG(ADIS16400_YGYRO_OUT)),
|
||||
cpu_to_be16(ADIS16400_READ_REG(ADIS16400_ZGYRO_OUT)),
|
||||
cpu_to_be16(ADIS16400_READ_REG(ADIS16400_XACCL_OUT)),
|
||||
cpu_to_be16(ADIS16400_READ_REG(ADIS16400_YACCL_OUT)),
|
||||
cpu_to_be16(ADIS16400_READ_REG(ADIS16400_ZACCL_OUT)),
|
||||
cpu_to_be16(ADIS16400_READ_REG(ADIS16350_XTEMP_OUT)),
|
||||
cpu_to_be16(ADIS16400_READ_REG(ADIS16350_YTEMP_OUT)),
|
||||
cpu_to_be16(ADIS16400_READ_REG(ADIS16350_ZTEMP_OUT)),
|
||||
cpu_to_be16(ADIS16400_READ_REG(ADIS16400_AUX_ADC)),
|
||||
};
|
||||
|
||||
static int adis16350_spi_read_all(struct iio_dev *indio_dev, u8 *rx)
|
||||
{
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
|
||||
struct spi_message msg;
|
||||
int i, j = 0, ret;
|
||||
struct spi_transfer *xfers;
|
||||
int scan_count = bitmap_weight(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength);
|
||||
|
||||
xfers = kzalloc(sizeof(*xfers)*(scan_count + 1),
|
||||
GFP_KERNEL);
|
||||
if (xfers == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(read_all_tx_array); i++)
|
||||
if (test_bit(i, indio_dev->active_scan_mask)) {
|
||||
xfers[j].tx_buf = &read_all_tx_array[i];
|
||||
xfers[j].bits_per_word = 16;
|
||||
xfers[j].len = 2;
|
||||
xfers[j + 1].rx_buf = rx + j*2;
|
||||
j++;
|
||||
}
|
||||
xfers[j].bits_per_word = 16;
|
||||
xfers[j].len = 2;
|
||||
|
||||
spi_message_init(&msg);
|
||||
for (j = 0; j < scan_count + 1; j++)
|
||||
spi_message_add_tail(&xfers[j], &msg);
|
||||
|
||||
ret = spi_sync(st->us, &msg);
|
||||
kfree(xfers);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Whilst this makes a lot of calls to iio_sw_ring functions - it is to device
|
||||
* specific to be rolled into the core.
|
||||
*/
|
||||
static irqreturn_t adis16400_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
int i = 0, j, ret = 0;
|
||||
s16 *data;
|
||||
|
||||
/* Asumption that long is enough for maximum channels */
|
||||
unsigned long mask = *indio_dev->active_scan_mask;
|
||||
int scan_count = bitmap_weight(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength);
|
||||
data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
||||
if (data == NULL) {
|
||||
dev_err(&st->us->dev, "memory alloc failed in ring bh");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (scan_count) {
|
||||
if (st->variant->flags & ADIS16400_NO_BURST) {
|
||||
ret = adis16350_spi_read_all(indio_dev, st->rx);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
for (; i < scan_count; i++)
|
||||
data[i] = *(s16 *)(st->rx + i*2);
|
||||
} else {
|
||||
ret = adis16400_spi_read_burst(indio_dev, st->rx);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
for (; i < scan_count; i++) {
|
||||
j = __ffs(mask);
|
||||
mask &= ~(1 << j);
|
||||
data[i] = be16_to_cpup(
|
||||
(__be16 *)&(st->rx[j*2]));
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Guaranteed to be aligned with 8 byte boundary */
|
||||
if (indio_dev->scan_timestamp)
|
||||
*((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp;
|
||||
iio_push_to_buffers(indio_dev, (u8 *) data);
|
||||
|
||||
done:
|
||||
kfree(data);
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void adis16400_unconfigure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
iio_dealloc_pollfunc(indio_dev->pollfunc);
|
||||
iio_sw_rb_free(indio_dev->buffer);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops adis16400_ring_setup_ops = {
|
||||
.preenable = &iio_sw_buffer_preenable,
|
||||
.postenable = &iio_triggered_buffer_postenable,
|
||||
.predisable = &iio_triggered_buffer_predisable,
|
||||
};
|
||||
|
||||
int adis16400_configure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct iio_buffer *ring;
|
||||
|
||||
ring = iio_sw_rb_allocate(indio_dev);
|
||||
if (!ring) {
|
||||
ret = -ENOMEM;
|
||||
return ret;
|
||||
}
|
||||
indio_dev->buffer = ring;
|
||||
ring->scan_timestamp = true;
|
||||
indio_dev->setup_ops = &adis16400_ring_setup_ops;
|
||||
|
||||
indio_dev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time,
|
||||
&adis16400_trigger_handler,
|
||||
IRQF_ONESHOT,
|
||||
indio_dev,
|
||||
"%s_consumer%d",
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
if (indio_dev->pollfunc == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_iio_sw_rb_free;
|
||||
}
|
||||
|
||||
indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
|
||||
return 0;
|
||||
error_iio_sw_rb_free:
|
||||
iio_sw_rb_free(indio_dev->buffer);
|
||||
return ret;
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include "adis16400.h"
|
||||
|
||||
/**
|
||||
* adis16400_data_rdy_trigger_set_state() set datardy interrupt state
|
||||
**/
|
||||
static int adis16400_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
{
|
||||
struct iio_dev *indio_dev = trig->private_data;
|
||||
|
||||
dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
|
||||
return adis16400_set_irq(indio_dev, state);
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops adis16400_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_trigger_state = &adis16400_data_rdy_trigger_set_state,
|
||||
};
|
||||
|
||||
int adis16400_probe_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->trig = iio_trigger_alloc("%s-dev%d",
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
if (st->trig == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
ret = request_irq(st->us->irq,
|
||||
&iio_trigger_generic_data_rdy_poll,
|
||||
IRQF_TRIGGER_RISING,
|
||||
"adis16400",
|
||||
st->trig);
|
||||
if (ret)
|
||||
goto error_free_trig;
|
||||
st->trig->dev.parent = &st->us->dev;
|
||||
st->trig->private_data = indio_dev;
|
||||
st->trig->ops = &adis16400_trigger_ops;
|
||||
ret = iio_trigger_register(st->trig);
|
||||
|
||||
/* select default trigger */
|
||||
indio_dev->trig = st->trig;
|
||||
if (ret)
|
||||
goto error_free_irq;
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_irq:
|
||||
free_irq(st->us->irq, st->trig);
|
||||
error_free_trig:
|
||||
iio_trigger_free(st->trig);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void adis16400_remove_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16400_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_trigger_unregister(st->trig);
|
||||
free_irq(st->us->irq, st->trig);
|
||||
iio_trigger_free(st->trig);
|
||||
}
|
|
@ -25,16 +25,6 @@ config SENSORS_ISL29028
|
|||
Proximity value via iio. The ISL29028 provides the concurrent sensing
|
||||
of ambient light and proximity.
|
||||
|
||||
config SENSORS_TSL2563
|
||||
tristate "TAOS TSL2560, TSL2561, TSL2562 and TSL2563 ambient light sensors"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for the Taos TSL2560,
|
||||
TSL2561, TSL2562 and TSL2563 ambient light sensors.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called tsl2563.
|
||||
|
||||
config TSL2583
|
||||
tristate "TAOS TSL2580, TSL2581 and TSL2583 light-to-digital converters"
|
||||
depends on I2C
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
# Makefile for industrial I/O Light sensors
|
||||
#
|
||||
|
||||
obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o
|
||||
obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o
|
||||
obj-$(CONFIG_SENSORS_ISL29028) += isl29028.o
|
||||
obj-$(CONFIG_TSL2583) += tsl2583.o
|
||||
|
|
|
@ -291,59 +291,6 @@ static const u8 device_channel_config[] = {
|
|||
ALSPRX2
|
||||
};
|
||||
|
||||
/**
|
||||
* tsl2x7x_parse_buffer() - parse a decimal result from a buffer.
|
||||
* @*buf: pointer to char buffer to parse
|
||||
* @*result: pointer to buffer to contain
|
||||
* resulting interger / decimal as ints.
|
||||
*
|
||||
*/
|
||||
static int
|
||||
tsl2x7x_parse_buffer(const char *buf, struct tsl2x7x_parse_result *result)
|
||||
{
|
||||
int integer = 0, fract = 0, fract_mult = 100000;
|
||||
bool integer_part = true, negative = false;
|
||||
|
||||
if (buf[0] == '-') {
|
||||
negative = true;
|
||||
buf++;
|
||||
}
|
||||
|
||||
while (*buf) {
|
||||
if ('0' <= *buf && *buf <= '9') {
|
||||
if (integer_part)
|
||||
integer = integer*10 + *buf - '0';
|
||||
else {
|
||||
fract += fract_mult*(*buf - '0');
|
||||
if (fract_mult == 1)
|
||||
break;
|
||||
fract_mult /= 10;
|
||||
}
|
||||
} else if (*buf == '\n') {
|
||||
if (*(buf + 1) == '\0')
|
||||
break;
|
||||
else
|
||||
return -EINVAL;
|
||||
} else if (*buf == '.') {
|
||||
integer_part = false;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
buf++;
|
||||
}
|
||||
if (negative) {
|
||||
if (integer)
|
||||
integer = -integer;
|
||||
else
|
||||
fract = -fract;
|
||||
}
|
||||
|
||||
result->integer = integer;
|
||||
result->fract = fract;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tsl2x7x_i2c_read() - Read a byte from a register.
|
||||
* @client: i2c client
|
||||
|
@ -1036,13 +983,12 @@ static ssize_t tsl2x7x_als_time_store(struct device *dev,
|
|||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct tsl2X7X_chip *chip = iio_priv(indio_dev);
|
||||
struct tsl2x7x_parse_result result;
|
||||
int ret;
|
||||
|
||||
result.integer = 0;
|
||||
result.fract = 0;
|
||||
ret = iio_str_to_fixpoint(buf, 100, &result.integer, &result.fract);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tsl2x7x_parse_buffer(buf, &result);
|
||||
|
||||
result.fract /= 1000;
|
||||
result.fract /= 3;
|
||||
chip->tsl2x7x_settings.als_time =
|
||||
(TSL2X7X_MAX_TIMER_CNT - (u8)result.fract);
|
||||
|
@ -1109,12 +1055,12 @@ static ssize_t tsl2x7x_als_persistence_store(struct device *dev,
|
|||
struct tsl2X7X_chip *chip = iio_priv(indio_dev);
|
||||
struct tsl2x7x_parse_result result;
|
||||
int y, z, filter_delay;
|
||||
int ret;
|
||||
|
||||
result.integer = 0;
|
||||
result.fract = 0;
|
||||
tsl2x7x_parse_buffer(buf, &result);
|
||||
ret = iio_str_to_fixpoint(buf, 100, &result.integer, &result.fract);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
result.fract /= 1000;
|
||||
y = (TSL2X7X_MAX_TIMER_CNT - (u8)chip->tsl2x7x_settings.als_time) + 1;
|
||||
z = y * TSL2X7X_MIN_ITIME;
|
||||
|
||||
|
@ -1155,12 +1101,12 @@ static ssize_t tsl2x7x_prox_persistence_store(struct device *dev,
|
|||
struct tsl2X7X_chip *chip = iio_priv(indio_dev);
|
||||
struct tsl2x7x_parse_result result;
|
||||
int y, z, filter_delay;
|
||||
int ret;
|
||||
|
||||
result.integer = 0;
|
||||
result.fract = 0;
|
||||
tsl2x7x_parse_buffer(buf, &result);
|
||||
ret = iio_str_to_fixpoint(buf, 100, &result.integer, &result.fract);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
result.fract /= 1000;
|
||||
y = (TSL2X7X_MAX_TIMER_CNT - (u8)chip->tsl2x7x_settings.prx_time) + 1;
|
||||
z = y * TSL2X7X_MIN_ITIME;
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ config ADE7758
|
|||
tristate "Analog Devices ADE7758 Poly Phase Multifunction Energy Metering IC Driver"
|
||||
depends on SPI
|
||||
select IIO_TRIGGER if IIO_BUFFER
|
||||
select IIO_SW_RING if IIO_BUFFER
|
||||
select IIO_KFIFO_BUF if IIO_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices ADE7758 Polyphase
|
||||
Multifunction Energy Metering IC with Per Phase Information Driver.
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include <asm/unaligned.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include "../ring_sw.h"
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include "ade7758.h"
|
||||
|
||||
|
@ -119,7 +119,7 @@ static const struct iio_buffer_setup_ops ade7758_ring_setup_ops = {
|
|||
void ade7758_unconfigure_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
iio_dealloc_pollfunc(indio_dev->pollfunc);
|
||||
iio_sw_rb_free(indio_dev->buffer);
|
||||
iio_kfifo_free(indio_dev->buffer);
|
||||
}
|
||||
|
||||
int ade7758_configure_ring(struct iio_dev *indio_dev)
|
||||
|
@ -127,7 +127,7 @@ int ade7758_configure_ring(struct iio_dev *indio_dev)
|
|||
struct ade7758_state *st = iio_priv(indio_dev);
|
||||
int ret = 0;
|
||||
|
||||
indio_dev->buffer = iio_sw_rb_allocate(indio_dev);
|
||||
indio_dev->buffer = iio_kfifo_allocate(indio_dev);
|
||||
if (!indio_dev->buffer) {
|
||||
ret = -ENOMEM;
|
||||
return ret;
|
||||
|
@ -143,7 +143,7 @@ int ade7758_configure_ring(struct iio_dev *indio_dev)
|
|||
indio_dev->id);
|
||||
if (indio_dev->pollfunc == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_iio_sw_rb_free;
|
||||
goto error_iio_kfifo_free;
|
||||
}
|
||||
|
||||
indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
|
||||
|
@ -183,8 +183,8 @@ int ade7758_configure_ring(struct iio_dev *indio_dev)
|
|||
|
||||
return 0;
|
||||
|
||||
error_iio_sw_rb_free:
|
||||
iio_sw_rb_free(indio_dev->buffer);
|
||||
error_iio_kfifo_free:
|
||||
iio_kfifo_free(indio_dev->buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,366 +0,0 @@
|
|||
/* The industrial I/O simple minimally locked ring buffer.
|
||||
*
|
||||
* Copyright (c) 2008 Jonathan Cameron
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/poll.h>
|
||||
#include "ring_sw.h"
|
||||
#include <linux/iio/trigger.h>
|
||||
|
||||
/**
|
||||
* struct iio_sw_ring_buffer - software ring buffer
|
||||
* @buf: generic ring buffer elements
|
||||
* @data: the ring buffer memory
|
||||
* @read_p: read pointer (oldest available)
|
||||
* @write_p: write pointer
|
||||
* @half_p: half buffer length behind write_p (event generation)
|
||||
* @update_needed: flag to indicate change in size requested
|
||||
*
|
||||
* Note that the first element of all ring buffers must be a
|
||||
* struct iio_buffer.
|
||||
**/
|
||||
struct iio_sw_ring_buffer {
|
||||
struct iio_buffer buf;
|
||||
unsigned char *data;
|
||||
unsigned char *read_p;
|
||||
unsigned char *write_p;
|
||||
/* used to act as a point at which to signal an event */
|
||||
unsigned char *half_p;
|
||||
int update_needed;
|
||||
};
|
||||
|
||||
#define iio_to_sw_ring(r) container_of(r, struct iio_sw_ring_buffer, buf)
|
||||
|
||||
static inline int __iio_allocate_sw_ring_buffer(struct iio_sw_ring_buffer *ring,
|
||||
int bytes_per_datum, int length)
|
||||
{
|
||||
if ((length == 0) || (bytes_per_datum == 0))
|
||||
return -EINVAL;
|
||||
__iio_update_buffer(&ring->buf, bytes_per_datum, length);
|
||||
ring->data = kmalloc(length*ring->buf.bytes_per_datum, GFP_ATOMIC);
|
||||
ring->read_p = NULL;
|
||||
ring->write_p = NULL;
|
||||
ring->half_p = NULL;
|
||||
return ring->data ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
static inline void __iio_free_sw_ring_buffer(struct iio_sw_ring_buffer *ring)
|
||||
{
|
||||
kfree(ring->data);
|
||||
}
|
||||
|
||||
/* Ring buffer related functionality */
|
||||
/* Store to ring is typically called in the bh of a data ready interrupt handler
|
||||
* in the device driver */
|
||||
/* Lock always held if their is a chance this may be called */
|
||||
/* Only one of these per ring may run concurrently - enforced by drivers */
|
||||
static int iio_store_to_sw_ring(struct iio_sw_ring_buffer *ring,
|
||||
unsigned char *data)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned char *temp_ptr, *change_test_ptr;
|
||||
|
||||
/* initial store */
|
||||
if (unlikely(ring->write_p == NULL)) {
|
||||
ring->write_p = ring->data;
|
||||
/* Doesn't actually matter if this is out of the set
|
||||
* as long as the read pointer is valid before this
|
||||
* passes it - guaranteed as set later in this function.
|
||||
*/
|
||||
ring->half_p = ring->data - ring->buf.length*ring->buf.bytes_per_datum/2;
|
||||
}
|
||||
/* Copy data to where ever the current write pointer says */
|
||||
memcpy(ring->write_p, data, ring->buf.bytes_per_datum);
|
||||
barrier();
|
||||
/* Update the pointer used to get most recent value.
|
||||
* Always valid as either points to latest or second latest value.
|
||||
* Before this runs it is null and read attempts fail with -EAGAIN.
|
||||
*/
|
||||
barrier();
|
||||
/* temp_ptr used to ensure we never have an invalid pointer
|
||||
* it may be slightly lagging, but never invalid
|
||||
*/
|
||||
temp_ptr = ring->write_p + ring->buf.bytes_per_datum;
|
||||
/* End of ring, back to the beginning */
|
||||
if (temp_ptr == ring->data + ring->buf.length*ring->buf.bytes_per_datum)
|
||||
temp_ptr = ring->data;
|
||||
/* Update the write pointer
|
||||
* always valid as long as this is the only function able to write.
|
||||
* Care needed with smp systems to ensure more than one ring fill
|
||||
* is never scheduled.
|
||||
*/
|
||||
ring->write_p = temp_ptr;
|
||||
|
||||
if (ring->read_p == NULL)
|
||||
ring->read_p = ring->data;
|
||||
/* Buffer full - move the read pointer and create / escalate
|
||||
* ring event */
|
||||
/* Tricky case - if the read pointer moves before we adjust it.
|
||||
* Handle by not pushing if it has moved - may result in occasional
|
||||
* unnecessary buffer full events when it wasn't quite true.
|
||||
*/
|
||||
else if (ring->write_p == ring->read_p) {
|
||||
change_test_ptr = ring->read_p;
|
||||
temp_ptr = change_test_ptr + ring->buf.bytes_per_datum;
|
||||
if (temp_ptr
|
||||
== ring->data + ring->buf.length*ring->buf.bytes_per_datum) {
|
||||
temp_ptr = ring->data;
|
||||
}
|
||||
/* We are moving pointer on one because the ring is full. Any
|
||||
* change to the read pointer will be this or greater.
|
||||
*/
|
||||
if (change_test_ptr == ring->read_p)
|
||||
ring->read_p = temp_ptr;
|
||||
}
|
||||
/* investigate if our event barrier has been passed */
|
||||
/* There are definite 'issues' with this and chances of
|
||||
* simultaneous read */
|
||||
/* Also need to use loop count to ensure this only happens once */
|
||||
ring->half_p += ring->buf.bytes_per_datum;
|
||||
if (ring->half_p == ring->data + ring->buf.length*ring->buf.bytes_per_datum)
|
||||
ring->half_p = ring->data;
|
||||
if (ring->half_p == ring->read_p) {
|
||||
ring->buf.stufftoread = true;
|
||||
wake_up_interruptible(&ring->buf.pollq);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iio_read_first_n_sw_rb(struct iio_buffer *r,
|
||||
size_t n, char __user *buf)
|
||||
{
|
||||
struct iio_sw_ring_buffer *ring = iio_to_sw_ring(r);
|
||||
|
||||
u8 *initial_read_p, *initial_write_p, *current_read_p, *end_read_p;
|
||||
u8 *data;
|
||||
int ret, max_copied, bytes_to_rip, dead_offset;
|
||||
size_t data_available, buffer_size;
|
||||
|
||||
/* A userspace program has probably made an error if it tries to
|
||||
* read something that is not a whole number of bpds.
|
||||
* Return an error.
|
||||
*/
|
||||
if (n % ring->buf.bytes_per_datum) {
|
||||
ret = -EINVAL;
|
||||
printk(KERN_INFO "Ring buffer read request not whole number of"
|
||||
"samples: Request bytes %zd, Current bytes per datum %d\n",
|
||||
n, ring->buf.bytes_per_datum);
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
buffer_size = ring->buf.bytes_per_datum*ring->buf.length;
|
||||
|
||||
/* Limit size to whole of ring buffer */
|
||||
bytes_to_rip = min_t(size_t, buffer_size, n);
|
||||
|
||||
data = kmalloc(bytes_to_rip, GFP_KERNEL);
|
||||
if (data == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
/* build local copy */
|
||||
initial_read_p = ring->read_p;
|
||||
if (unlikely(initial_read_p == NULL)) { /* No data here as yet */
|
||||
ret = 0;
|
||||
goto error_free_data_cpy;
|
||||
}
|
||||
|
||||
initial_write_p = ring->write_p;
|
||||
|
||||
/* Need a consistent pair */
|
||||
while ((initial_read_p != ring->read_p)
|
||||
|| (initial_write_p != ring->write_p)) {
|
||||
initial_read_p = ring->read_p;
|
||||
initial_write_p = ring->write_p;
|
||||
}
|
||||
if (initial_write_p == initial_read_p) {
|
||||
/* No new data available.*/
|
||||
ret = 0;
|
||||
goto error_free_data_cpy;
|
||||
}
|
||||
|
||||
if (initial_write_p >= initial_read_p)
|
||||
data_available = initial_write_p - initial_read_p;
|
||||
else
|
||||
data_available = buffer_size - (initial_read_p - initial_write_p);
|
||||
|
||||
if (data_available < bytes_to_rip)
|
||||
bytes_to_rip = data_available;
|
||||
|
||||
if (initial_read_p + bytes_to_rip >= ring->data + buffer_size) {
|
||||
max_copied = ring->data + buffer_size - initial_read_p;
|
||||
memcpy(data, initial_read_p, max_copied);
|
||||
memcpy(data + max_copied, ring->data, bytes_to_rip - max_copied);
|
||||
end_read_p = ring->data + bytes_to_rip - max_copied;
|
||||
} else {
|
||||
memcpy(data, initial_read_p, bytes_to_rip);
|
||||
end_read_p = initial_read_p + bytes_to_rip;
|
||||
}
|
||||
|
||||
/* Now to verify which section was cleanly copied - i.e. how far
|
||||
* read pointer has been pushed */
|
||||
current_read_p = ring->read_p;
|
||||
|
||||
if (initial_read_p <= current_read_p)
|
||||
dead_offset = current_read_p - initial_read_p;
|
||||
else
|
||||
dead_offset = buffer_size - (initial_read_p - current_read_p);
|
||||
|
||||
/* possible issue if the initial write has been lapped or indeed
|
||||
* the point we were reading to has been passed */
|
||||
/* No valid data read.
|
||||
* In this case the read pointer is already correct having been
|
||||
* pushed further than we would look. */
|
||||
if (bytes_to_rip - dead_offset < 0) {
|
||||
ret = 0;
|
||||
goto error_free_data_cpy;
|
||||
}
|
||||
|
||||
/* setup the next read position */
|
||||
/* Beware, this may fail due to concurrency fun and games.
|
||||
* Possible that sufficient fill commands have run to push the read
|
||||
* pointer past where we would be after the rip. If this occurs, leave
|
||||
* it be.
|
||||
*/
|
||||
/* Tricky - deal with loops */
|
||||
|
||||
while (ring->read_p != end_read_p)
|
||||
ring->read_p = end_read_p;
|
||||
|
||||
ret = bytes_to_rip - dead_offset;
|
||||
|
||||
if (copy_to_user(buf, data + dead_offset, ret)) {
|
||||
ret = -EFAULT;
|
||||
goto error_free_data_cpy;
|
||||
}
|
||||
|
||||
if (bytes_to_rip >= ring->buf.length*ring->buf.bytes_per_datum/2)
|
||||
ring->buf.stufftoread = 0;
|
||||
|
||||
error_free_data_cpy:
|
||||
kfree(data);
|
||||
error_ret:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iio_store_to_sw_rb(struct iio_buffer *r,
|
||||
u8 *data)
|
||||
{
|
||||
struct iio_sw_ring_buffer *ring = iio_to_sw_ring(r);
|
||||
return iio_store_to_sw_ring(ring, data);
|
||||
}
|
||||
|
||||
static int iio_request_update_sw_rb(struct iio_buffer *r)
|
||||
{
|
||||
int ret = 0;
|
||||
struct iio_sw_ring_buffer *ring = iio_to_sw_ring(r);
|
||||
|
||||
r->stufftoread = false;
|
||||
if (!ring->update_needed)
|
||||
goto error_ret;
|
||||
__iio_free_sw_ring_buffer(ring);
|
||||
ret = __iio_allocate_sw_ring_buffer(ring, ring->buf.bytes_per_datum,
|
||||
ring->buf.length);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iio_get_bytes_per_datum_sw_rb(struct iio_buffer *r)
|
||||
{
|
||||
struct iio_sw_ring_buffer *ring = iio_to_sw_ring(r);
|
||||
return ring->buf.bytes_per_datum;
|
||||
}
|
||||
|
||||
static int iio_mark_update_needed_sw_rb(struct iio_buffer *r)
|
||||
{
|
||||
struct iio_sw_ring_buffer *ring = iio_to_sw_ring(r);
|
||||
ring->update_needed = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iio_set_bytes_per_datum_sw_rb(struct iio_buffer *r, size_t bpd)
|
||||
{
|
||||
if (r->bytes_per_datum != bpd) {
|
||||
r->bytes_per_datum = bpd;
|
||||
iio_mark_update_needed_sw_rb(r);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iio_get_length_sw_rb(struct iio_buffer *r)
|
||||
{
|
||||
return r->length;
|
||||
}
|
||||
|
||||
static int iio_set_length_sw_rb(struct iio_buffer *r, int length)
|
||||
{
|
||||
if (r->length != length) {
|
||||
r->length = length;
|
||||
iio_mark_update_needed_sw_rb(r);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static IIO_BUFFER_ENABLE_ATTR;
|
||||
static IIO_BUFFER_LENGTH_ATTR;
|
||||
|
||||
/* Standard set of ring buffer attributes */
|
||||
static struct attribute *iio_ring_attributes[] = {
|
||||
&dev_attr_length.attr,
|
||||
&dev_attr_enable.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group iio_ring_attribute_group = {
|
||||
.attrs = iio_ring_attributes,
|
||||
.name = "buffer",
|
||||
};
|
||||
|
||||
static const struct iio_buffer_access_funcs ring_sw_access_funcs = {
|
||||
.store_to = &iio_store_to_sw_rb,
|
||||
.read_first_n = &iio_read_first_n_sw_rb,
|
||||
.request_update = &iio_request_update_sw_rb,
|
||||
.get_bytes_per_datum = &iio_get_bytes_per_datum_sw_rb,
|
||||
.set_bytes_per_datum = &iio_set_bytes_per_datum_sw_rb,
|
||||
.get_length = &iio_get_length_sw_rb,
|
||||
.set_length = &iio_set_length_sw_rb,
|
||||
};
|
||||
|
||||
struct iio_buffer *iio_sw_rb_allocate(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct iio_buffer *buf;
|
||||
struct iio_sw_ring_buffer *ring;
|
||||
|
||||
ring = kzalloc(sizeof *ring, GFP_KERNEL);
|
||||
if (!ring)
|
||||
return NULL;
|
||||
ring->update_needed = true;
|
||||
buf = &ring->buf;
|
||||
iio_buffer_init(buf);
|
||||
buf->attrs = &iio_ring_attribute_group;
|
||||
buf->access = &ring_sw_access_funcs;
|
||||
|
||||
return buf;
|
||||
}
|
||||
EXPORT_SYMBOL(iio_sw_rb_allocate);
|
||||
|
||||
void iio_sw_rb_free(struct iio_buffer *r)
|
||||
{
|
||||
kfree(iio_to_sw_ring(r));
|
||||
}
|
||||
EXPORT_SYMBOL(iio_sw_rb_free);
|
||||
|
||||
MODULE_DESCRIPTION("Industrial I/O software ring buffer");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1,30 +0,0 @@
|
|||
/* The industrial I/O simple minimally locked ring buffer.
|
||||
*
|
||||
* Copyright (c) 2008 Jonathan Cameron
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This code is deliberately kept separate from the main industrialio I/O core
|
||||
* as it is intended that in the future a number of different software ring
|
||||
* buffer implementations will exist with different characteristics to suit
|
||||
* different applications.
|
||||
*
|
||||
* This particular one was designed for a data capture application where it was
|
||||
* particularly important that no userspace reads would interrupt the capture
|
||||
* process. To this end the ring is not locked during a read.
|
||||
*
|
||||
* Comments on this buffer design welcomed. It's far from efficient and some of
|
||||
* my understanding of the effects of scheduling on this are somewhat limited.
|
||||
* Frankly, to my mind, this is the current weak point in the industrial I/O
|
||||
* patch set.
|
||||
*/
|
||||
|
||||
#ifndef _IIO_RING_SW_H_
|
||||
#define _IIO_RING_SW_H_
|
||||
#include <linux/iio/buffer.h>
|
||||
|
||||
struct iio_buffer *iio_sw_rb_allocate(struct iio_dev *indio_dev);
|
||||
void iio_sw_rb_free(struct iio_buffer *ring);
|
||||
#endif /* _IIO_RING_SW_H_ */
|
|
@ -6,4 +6,3 @@ struct tsl2563_platform_data {
|
|||
};
|
||||
|
||||
#endif /* __LINUX_TSL2563_H */
|
||||
|
Loading…
Reference in a new issue