From 57f0f512b273f60d52568b8c6b77e17f5636edc0 Mon Sep 17 00:00:00 2001 From: André Fabian Silva Delgado Date: Wed, 5 Aug 2015 17:04:01 -0300 Subject: Initial import --- drivers/staging/iio/accel/Kconfig | 101 +++ drivers/staging/iio/accel/Makefile | 28 + drivers/staging/iio/accel/adis16201.h | 66 ++ drivers/staging/iio/accel/adis16201_core.c | 248 ++++++ drivers/staging/iio/accel/adis16203.h | 59 ++ drivers/staging/iio/accel/adis16203_core.c | 216 +++++ drivers/staging/iio/accel/adis16204.h | 68 ++ drivers/staging/iio/accel/adis16204_core.c | 254 ++++++ drivers/staging/iio/accel/adis16209.h | 105 +++ drivers/staging/iio/accel/adis16209_core.c | 248 ++++++ drivers/staging/iio/accel/adis16220.h | 140 ++++ drivers/staging/iio/accel/adis16220_core.c | 495 ++++++++++++ drivers/staging/iio/accel/adis16240.h | 129 +++ drivers/staging/iio/accel/adis16240_core.c | 301 +++++++ drivers/staging/iio/accel/lis3l02dq.h | 212 +++++ drivers/staging/iio/accel/lis3l02dq_core.c | 813 +++++++++++++++++++ drivers/staging/iio/accel/lis3l02dq_ring.c | 426 ++++++++++ drivers/staging/iio/accel/sca3000.h | 278 +++++++ drivers/staging/iio/accel/sca3000_core.c | 1209 ++++++++++++++++++++++++++++ drivers/staging/iio/accel/sca3000_ring.c | 348 ++++++++ 20 files changed, 5744 insertions(+) create mode 100644 drivers/staging/iio/accel/Kconfig create mode 100644 drivers/staging/iio/accel/Makefile create mode 100644 drivers/staging/iio/accel/adis16201.h create mode 100644 drivers/staging/iio/accel/adis16201_core.c create mode 100644 drivers/staging/iio/accel/adis16203.h create mode 100644 drivers/staging/iio/accel/adis16203_core.c create mode 100644 drivers/staging/iio/accel/adis16204.h create mode 100644 drivers/staging/iio/accel/adis16204_core.c create mode 100644 drivers/staging/iio/accel/adis16209.h create mode 100644 drivers/staging/iio/accel/adis16209_core.c create mode 100644 drivers/staging/iio/accel/adis16220.h create mode 100644 drivers/staging/iio/accel/adis16220_core.c create mode 100644 drivers/staging/iio/accel/adis16240.h create mode 100644 drivers/staging/iio/accel/adis16240_core.c create mode 100644 drivers/staging/iio/accel/lis3l02dq.h create mode 100644 drivers/staging/iio/accel/lis3l02dq_core.c create mode 100644 drivers/staging/iio/accel/lis3l02dq_ring.c create mode 100644 drivers/staging/iio/accel/sca3000.h create mode 100644 drivers/staging/iio/accel/sca3000_core.c create mode 100644 drivers/staging/iio/accel/sca3000_ring.c (limited to 'drivers/staging/iio/accel') diff --git a/drivers/staging/iio/accel/Kconfig b/drivers/staging/iio/accel/Kconfig new file mode 100644 index 000000000..07b7ffa00 --- /dev/null +++ b/drivers/staging/iio/accel/Kconfig @@ -0,0 +1,101 @@ +# +# Accelerometer drivers +# +menu "Accelerometers" + +config ADIS16201 + tristate "Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer" + depends on SPI + select IIO_ADIS_LIB + select IIO_ADIS_LIB_BUFFER if IIO_BUFFER + help + Say Y here to build support for Analog Devices adis16201 dual-axis + digital inclinometer and accelerometer. + + To compile this driver as a module, say M here: the module will + be called adis16201. + +config ADIS16203 + tristate "Analog Devices ADIS16203 Programmable 360 Degrees Inclinometer" + depends on SPI + select IIO_ADIS_LIB + select IIO_ADIS_LIB_BUFFER if IIO_BUFFER + help + Say Y here to build support for Analog Devices adis16203 Programmable + 360 Degrees Inclinometer. + + To compile this driver as a module, say M here: the module will be + called adis16203. + +config ADIS16204 + tristate "Analog Devices ADIS16204 Programmable High-g Digital Impact Sensor and Recorder" + depends on SPI + select IIO_ADIS_LIB + select IIO_ADIS_LIB_BUFFER if IIO_BUFFER + help + Say Y here to build support for Analog Devices adis16204 Programmable + High-g Digital Impact Sensor and Recorder. + + To compile this driver as a module, say M here: the module will be + called adis16204. + +config ADIS16209 + tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer" + depends on SPI + select IIO_ADIS_LIB + select IIO_ADIS_LIB_BUFFER if IIO_BUFFER + help + Say Y here to build support for Analog Devices adis16209 dual-axis digital inclinometer + and accelerometer. + + To compile this driver as a module, say M here: the module will be + called adis16209. + +config ADIS16220 + tristate "Analog Devices ADIS16220 Programmable Digital Vibration Sensor" + depends on SPI + select IIO_ADIS_LIB + help + Say Y here to build support for Analog Devices adis16220 programmable + digital vibration sensor. + + To compile this driver as a module, say M here: the module will be + called adis16220. + +config ADIS16240 + tristate "Analog Devices ADIS16240 Programmable Impact Sensor and Recorder" + depends on SPI + select IIO_ADIS_LIB + select IIO_ADIS_LIB_BUFFER if IIO_BUFFER + help + Say Y here to build support for Analog Devices adis16240 programmable + impact Sensor and recorder. + + To compile this driver as a module, say M here: the module will be + called adis16240. + +config LIS3L02DQ + tristate "ST Microelectronics LIS3L02DQ Accelerometer Driver" + depends on SPI + select IIO_TRIGGER if IIO_BUFFER + depends on !IIO_BUFFER || IIO_KFIFO_BUF + depends on GPIOLIB + help + Say Y 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. + + To compile this driver as a module, say M here: the module will be + called lis3l02dq. + +config SCA3000 + depends on IIO_BUFFER + depends on SPI + tristate "VTI SCA3000 series accelerometers" + help + Say Y here to build support for the VTI SCA3000 series of SPI + accelerometers. These devices use a hardware ring buffer. + + To compile this driver as a module, say M here: the module will be + called sca3000. +endmenu diff --git a/drivers/staging/iio/accel/Makefile b/drivers/staging/iio/accel/Makefile new file mode 100644 index 000000000..1ed137f1a --- /dev/null +++ b/drivers/staging/iio/accel/Makefile @@ -0,0 +1,28 @@ +# +# Makefile for industrial I/O accelerometer drivers +# + +adis16201-y := adis16201_core.o +obj-$(CONFIG_ADIS16201) += adis16201.o + +adis16203-y := adis16203_core.o +obj-$(CONFIG_ADIS16203) += adis16203.o + +adis16204-y := adis16204_core.o +obj-$(CONFIG_ADIS16204) += adis16204.o + +adis16209-y := adis16209_core.o +obj-$(CONFIG_ADIS16209) += adis16209.o + +adis16220-y := adis16220_core.o +obj-$(CONFIG_ADIS16220) += adis16220.o + +adis16240-y := adis16240_core.o +obj-$(CONFIG_ADIS16240) += adis16240.o + +lis3l02dq-y := lis3l02dq_core.o +lis3l02dq-$(CONFIG_IIO_BUFFER) += lis3l02dq_ring.o +obj-$(CONFIG_LIS3L02DQ) += lis3l02dq.o + +sca3000-y := sca3000_core.o sca3000_ring.o +obj-$(CONFIG_SCA3000) += sca3000.o diff --git a/drivers/staging/iio/accel/adis16201.h b/drivers/staging/iio/accel/adis16201.h new file mode 100644 index 000000000..e6b8c9af6 --- /dev/null +++ b/drivers/staging/iio/accel/adis16201.h @@ -0,0 +1,66 @@ +#ifndef SPI_ADIS16201_H_ +#define SPI_ADIS16201_H_ + +#define ADIS16201_STARTUP_DELAY 220 /* ms */ + +#define ADIS16201_FLASH_CNT 0x00 /* Flash memory write count */ +#define ADIS16201_SUPPLY_OUT 0x02 /* Output, power supply */ +#define ADIS16201_XACCL_OUT 0x04 /* Output, x-axis accelerometer */ +#define ADIS16201_YACCL_OUT 0x06 /* Output, y-axis accelerometer */ +#define ADIS16201_AUX_ADC 0x08 /* Output, auxiliary ADC input */ +#define ADIS16201_TEMP_OUT 0x0A /* Output, temperature */ +#define ADIS16201_XINCL_OUT 0x0C /* Output, x-axis inclination */ +#define ADIS16201_YINCL_OUT 0x0E /* Output, y-axis inclination */ +#define ADIS16201_XACCL_OFFS 0x10 /* Calibration, x-axis acceleration offset */ +#define ADIS16201_YACCL_OFFS 0x12 /* Calibration, y-axis acceleration offset */ +#define ADIS16201_XACCL_SCALE 0x14 /* x-axis acceleration scale factor */ +#define ADIS16201_YACCL_SCALE 0x16 /* y-axis acceleration scale factor */ +#define ADIS16201_XINCL_OFFS 0x18 /* Calibration, x-axis inclination offset */ +#define ADIS16201_YINCL_OFFS 0x1A /* Calibration, y-axis inclination offset */ +#define ADIS16201_XINCL_SCALE 0x1C /* x-axis inclination scale factor */ +#define ADIS16201_YINCL_SCALE 0x1E /* y-axis inclination scale factor */ +#define ADIS16201_ALM_MAG1 0x20 /* Alarm 1 amplitude threshold */ +#define ADIS16201_ALM_MAG2 0x22 /* Alarm 2 amplitude threshold */ +#define ADIS16201_ALM_SMPL1 0x24 /* Alarm 1, sample period */ +#define ADIS16201_ALM_SMPL2 0x26 /* Alarm 2, sample period */ +#define ADIS16201_ALM_CTRL 0x28 /* Alarm control */ +#define ADIS16201_AUX_DAC 0x30 /* Auxiliary DAC data */ +#define ADIS16201_GPIO_CTRL 0x32 /* General-purpose digital input/output control */ +#define ADIS16201_MSC_CTRL 0x34 /* Miscellaneous control */ +#define ADIS16201_SMPL_PRD 0x36 /* Internal sample period (rate) control */ +#define ADIS16201_AVG_CNT 0x38 /* Operation, filter configuration */ +#define ADIS16201_SLP_CNT 0x3A /* Operation, sleep mode control */ +#define ADIS16201_DIAG_STAT 0x3C /* Diagnostics, system status register */ +#define ADIS16201_GLOB_CMD 0x3E /* Operation, system command register */ + +/* MSC_CTRL */ +#define ADIS16201_MSC_CTRL_SELF_TEST_EN BIT(8) /* Self-test enable */ +#define ADIS16201_MSC_CTRL_DATA_RDY_EN BIT(2) /* Data-ready enable: 1 = enabled, 0 = disabled */ +#define ADIS16201_MSC_CTRL_ACTIVE_HIGH BIT(1) /* Data-ready polarity: 1 = active high, 0 = active low */ +#define ADIS16201_MSC_CTRL_DATA_RDY_DIO1 BIT(0) /* Data-ready line selection: 1 = DIO1, 0 = DIO0 */ + +/* DIAG_STAT */ +#define ADIS16201_DIAG_STAT_ALARM2 BIT(9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */ +#define ADIS16201_DIAG_STAT_ALARM1 BIT(8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */ +#define ADIS16201_DIAG_STAT_SPI_FAIL_BIT 3 /* SPI communications failure */ +#define ADIS16201_DIAG_STAT_FLASH_UPT_BIT 2 /* Flash update failure */ +#define ADIS16201_DIAG_STAT_POWER_HIGH_BIT 1 /* Power supply above 3.625 V */ +#define ADIS16201_DIAG_STAT_POWER_LOW_BIT 0 /* Power supply below 3.15 V */ + +/* GLOB_CMD */ +#define ADIS16201_GLOB_CMD_SW_RESET BIT(7) +#define ADIS16201_GLOB_CMD_FACTORY_CAL BIT(1) + +#define ADIS16201_ERROR_ACTIVE BIT(14) + +enum adis16201_scan { + ADIS16201_SCAN_ACC_X, + ADIS16201_SCAN_ACC_Y, + ADIS16201_SCAN_INCLI_X, + ADIS16201_SCAN_INCLI_Y, + ADIS16201_SCAN_SUPPLY, + ADIS16201_SCAN_AUX_ADC, + ADIS16201_SCAN_TEMP, +}; + +#endif /* SPI_ADIS16201_H_ */ diff --git a/drivers/staging/iio/accel/adis16201_core.c b/drivers/staging/iio/accel/adis16201_core.c new file mode 100644 index 000000000..10db68581 --- /dev/null +++ b/drivers/staging/iio/accel/adis16201_core.c @@ -0,0 +1,248 @@ +/* + * ADIS16201 Programmable Digital Vibration Sensor driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "adis16201.h" + +static const u8 adis16201_addresses[] = { + [ADIS16201_SCAN_ACC_X] = ADIS16201_XACCL_OFFS, + [ADIS16201_SCAN_ACC_Y] = ADIS16201_YACCL_OFFS, + [ADIS16201_SCAN_INCLI_X] = ADIS16201_XINCL_OFFS, + [ADIS16201_SCAN_INCLI_Y] = ADIS16201_YINCL_OFFS, +}; + +static int adis16201_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, + long mask) +{ + struct adis *st = iio_priv(indio_dev); + int ret; + int bits; + u8 addr; + s16 val16; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return adis_single_conversion(indio_dev, chan, + ADIS16201_ERROR_ACTIVE, val); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->channel == 0) { + *val = 1; + *val2 = 220000; /* 1.22 mV */ + } else { + *val = 0; + *val2 = 610000; /* 0.610 mV */ + } + return IIO_VAL_INT_PLUS_MICRO; + case IIO_TEMP: + *val = -470; /* 0.47 C */ + *val2 = 0; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_ACCEL: + *val = 0; + *val2 = IIO_G_TO_M_S_2(462400); /* 0.4624 mg */ + return IIO_VAL_INT_PLUS_NANO; + case IIO_INCLI: + *val = 0; + *val2 = 100000; /* 0.1 degree */ + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + break; + case IIO_CHAN_INFO_OFFSET: + *val = 25000 / -470 - 1278; /* 25 C = 1278 */ + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) { + case IIO_ACCEL: + bits = 12; + break; + case IIO_INCLI: + bits = 9; + break; + default: + return -EINVAL; + } + mutex_lock(&indio_dev->mlock); + addr = adis16201_addresses[chan->scan_index]; + ret = adis_read_reg_16(st, addr, &val16); + if (ret) { + mutex_unlock(&indio_dev->mlock); + return ret; + } + val16 &= (1 << bits) - 1; + val16 = (s16)(val16 << (16 - bits)) >> (16 - bits); + *val = val16; + mutex_unlock(&indio_dev->mlock); + return IIO_VAL_INT; + } + return -EINVAL; +} + +static int adis16201_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct adis *st = iio_priv(indio_dev); + int bits; + s16 val16; + u8 addr; + + switch (mask) { + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) { + case IIO_ACCEL: + bits = 12; + break; + case IIO_INCLI: + bits = 9; + break; + default: + return -EINVAL; + } + val16 = val & ((1 << bits) - 1); + addr = adis16201_addresses[chan->scan_index]; + return adis_write_reg_16(st, addr, val16); + } + return -EINVAL; +} + +static const struct iio_chan_spec adis16201_channels[] = { + ADIS_SUPPLY_CHAN(ADIS16201_SUPPLY_OUT, ADIS16201_SCAN_SUPPLY, 0, 12), + ADIS_TEMP_CHAN(ADIS16201_TEMP_OUT, ADIS16201_SCAN_TEMP, 0, 12), + ADIS_ACCEL_CHAN(X, ADIS16201_XACCL_OUT, ADIS16201_SCAN_ACC_X, + BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), + ADIS_ACCEL_CHAN(Y, ADIS16201_YACCL_OUT, ADIS16201_SCAN_ACC_Y, + BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), + ADIS_AUX_ADC_CHAN(ADIS16201_AUX_ADC, ADIS16201_SCAN_AUX_ADC, 0, 12), + ADIS_INCLI_CHAN(X, ADIS16201_XINCL_OUT, ADIS16201_SCAN_INCLI_X, + BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), + ADIS_INCLI_CHAN(X, ADIS16201_YINCL_OUT, ADIS16201_SCAN_INCLI_Y, + BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), + IIO_CHAN_SOFT_TIMESTAMP(7) +}; + +static const struct iio_info adis16201_info = { + .read_raw = &adis16201_read_raw, + .write_raw = &adis16201_write_raw, + .update_scan_mode = adis_update_scan_mode, + .driver_module = THIS_MODULE, +}; + +static const char * const adis16201_status_error_msgs[] = { + [ADIS16201_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure", + [ADIS16201_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed", + [ADIS16201_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V", + [ADIS16201_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V", +}; + +static const struct adis_data adis16201_data = { + .read_delay = 20, + .msc_ctrl_reg = ADIS16201_MSC_CTRL, + .glob_cmd_reg = ADIS16201_GLOB_CMD, + .diag_stat_reg = ADIS16201_DIAG_STAT, + + .self_test_mask = ADIS16201_MSC_CTRL_SELF_TEST_EN, + .startup_delay = ADIS16201_STARTUP_DELAY, + + .status_error_msgs = adis16201_status_error_msgs, + .status_error_mask = BIT(ADIS16201_DIAG_STAT_SPI_FAIL_BIT) | + BIT(ADIS16201_DIAG_STAT_FLASH_UPT_BIT) | + BIT(ADIS16201_DIAG_STAT_POWER_HIGH_BIT) | + BIT(ADIS16201_DIAG_STAT_POWER_LOW_BIT), +}; + +static int adis16201_probe(struct spi_device *spi) +{ + int ret; + struct adis *st; + struct iio_dev *indio_dev; + + /* setup the industrialio driver allocated elements */ + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + /* this is only used for removal purposes */ + spi_set_drvdata(spi, indio_dev); + + indio_dev->name = spi->dev.driver->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->info = &adis16201_info; + + indio_dev->channels = adis16201_channels; + indio_dev->num_channels = ARRAY_SIZE(adis16201_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = adis_init(st, indio_dev, spi, &adis16201_data); + if (ret) + return ret; + ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL); + if (ret) + return ret; + + /* Get the device into a sane initial state */ + ret = adis_initial_startup(st); + if (ret) + goto error_cleanup_buffer_trigger; + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto error_cleanup_buffer_trigger; + return 0; + +error_cleanup_buffer_trigger: + adis_cleanup_buffer_and_trigger(st, indio_dev); + return ret; +} + +static int adis16201_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct adis *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + adis_cleanup_buffer_and_trigger(st, indio_dev); + + return 0; +} + +static struct spi_driver adis16201_driver = { + .driver = { + .name = "adis16201", + .owner = THIS_MODULE, + }, + .probe = adis16201_probe, + .remove = adis16201_remove, +}; +module_spi_driver(adis16201_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADIS16201 Programmable Digital Vibration Sensor driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:adis16201"); diff --git a/drivers/staging/iio/accel/adis16203.h b/drivers/staging/iio/accel/adis16203.h new file mode 100644 index 000000000..6426e38bf --- /dev/null +++ b/drivers/staging/iio/accel/adis16203.h @@ -0,0 +1,59 @@ +#ifndef SPI_ADIS16203_H_ +#define SPI_ADIS16203_H_ + +#define ADIS16203_STARTUP_DELAY 220 /* ms */ + +#define ADIS16203_FLASH_CNT 0x00 /* Flash memory write count */ +#define ADIS16203_SUPPLY_OUT 0x02 /* Output, power supply */ +#define ADIS16203_AUX_ADC 0x08 /* Output, auxiliary ADC input */ +#define ADIS16203_TEMP_OUT 0x0A /* Output, temperature */ +#define ADIS16203_XINCL_OUT 0x0C /* Output, x-axis inclination */ +#define ADIS16203_YINCL_OUT 0x0E /* Output, y-axis inclination */ +#define ADIS16203_INCL_NULL 0x18 /* Incline null calibration */ +#define ADIS16203_ALM_MAG1 0x20 /* Alarm 1 amplitude threshold */ +#define ADIS16203_ALM_MAG2 0x22 /* Alarm 2 amplitude threshold */ +#define ADIS16203_ALM_SMPL1 0x24 /* Alarm 1, sample period */ +#define ADIS16203_ALM_SMPL2 0x26 /* Alarm 2, sample period */ +#define ADIS16203_ALM_CTRL 0x28 /* Alarm control */ +#define ADIS16203_AUX_DAC 0x30 /* Auxiliary DAC data */ +#define ADIS16203_GPIO_CTRL 0x32 /* General-purpose digital input/output control */ +#define ADIS16203_MSC_CTRL 0x34 /* Miscellaneous control */ +#define ADIS16203_SMPL_PRD 0x36 /* Internal sample period (rate) control */ +#define ADIS16203_AVG_CNT 0x38 /* Operation, filter configuration */ +#define ADIS16203_SLP_CNT 0x3A /* Operation, sleep mode control */ +#define ADIS16203_DIAG_STAT 0x3C /* Diagnostics, system status register */ +#define ADIS16203_GLOB_CMD 0x3E /* Operation, system command register */ + +/* MSC_CTRL */ +#define ADIS16203_MSC_CTRL_PWRUP_SELF_TEST BIT(10) /* Self-test at power-on: 1 = disabled, 0 = enabled */ +#define ADIS16203_MSC_CTRL_REVERSE_ROT_EN BIT(9) /* Reverses rotation of both inclination outputs */ +#define ADIS16203_MSC_CTRL_SELF_TEST_EN BIT(8) /* Self-test enable */ +#define ADIS16203_MSC_CTRL_DATA_RDY_EN BIT(2) /* Data-ready enable: 1 = enabled, 0 = disabled */ +#define ADIS16203_MSC_CTRL_ACTIVE_HIGH BIT(1) /* Data-ready polarity: 1 = active high, 0 = active low */ +#define ADIS16203_MSC_CTRL_DATA_RDY_DIO1 BIT(0) /* Data-ready line selection: 1 = DIO1, 0 = DIO0 */ + +/* DIAG_STAT */ +#define ADIS16203_DIAG_STAT_ALARM2 BIT(9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */ +#define ADIS16203_DIAG_STAT_ALARM1 BIT(8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */ +#define ADIS16203_DIAG_STAT_SELFTEST_FAIL_BIT 5 /* Self-test diagnostic error flag */ +#define ADIS16203_DIAG_STAT_SPI_FAIL_BIT 3 /* SPI communications failure */ +#define ADIS16203_DIAG_STAT_FLASH_UPT_BIT 2 /* Flash update failure */ +#define ADIS16203_DIAG_STAT_POWER_HIGH_BIT 1 /* Power supply above 3.625 V */ +#define ADIS16203_DIAG_STAT_POWER_LOW_BIT 0 /* Power supply below 3.15 V */ + +/* GLOB_CMD */ +#define ADIS16203_GLOB_CMD_SW_RESET BIT(7) +#define ADIS16203_GLOB_CMD_CLEAR_STAT BIT(4) +#define ADIS16203_GLOB_CMD_FACTORY_CAL BIT(1) + +#define ADIS16203_ERROR_ACTIVE BIT(14) + +enum adis16203_scan { + ADIS16203_SCAN_INCLI_X, + ADIS16203_SCAN_INCLI_Y, + ADIS16203_SCAN_SUPPLY, + ADIS16203_SCAN_AUX_ADC, + ADIS16203_SCAN_TEMP, +}; + +#endif /* SPI_ADIS16203_H_ */ diff --git a/drivers/staging/iio/accel/adis16203_core.c b/drivers/staging/iio/accel/adis16203_core.c new file mode 100644 index 000000000..fb593d23d --- /dev/null +++ b/drivers/staging/iio/accel/adis16203_core.c @@ -0,0 +1,216 @@ +/* + * ADIS16203 Programmable Digital Vibration Sensor driver + * + * Copyright 2030 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "adis16203.h" + +#define DRIVER_NAME "adis16203" + +static const u8 adis16203_addresses[] = { + [ADIS16203_SCAN_INCLI_X] = ADIS16203_INCL_NULL, +}; + +static int adis16203_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct adis *st = iio_priv(indio_dev); + /* currently only one writable parameter which keeps this simple */ + u8 addr = adis16203_addresses[chan->scan_index]; + + return adis_write_reg_16(st, addr, val & 0x3FFF); +} + +static int adis16203_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, + long mask) +{ + struct adis *st = iio_priv(indio_dev); + int ret; + int bits; + u8 addr; + s16 val16; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return adis_single_conversion(indio_dev, chan, + ADIS16203_ERROR_ACTIVE, val); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->channel == 0) { + *val = 1; + *val2 = 220000; /* 1.22 mV */ + } else { + *val = 0; + *val2 = 610000; /* 0.61 mV */ + } + return IIO_VAL_INT_PLUS_MICRO; + case IIO_TEMP: + *val = -470; /* -0.47 C */ + *val2 = 0; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_INCLI: + *val = 0; + *val2 = 25000; /* 0.025 degree */ + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + *val = 25000 / -470 - 1278; /* 25 C = 1278 */ + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + bits = 14; + mutex_lock(&indio_dev->mlock); + addr = adis16203_addresses[chan->scan_index]; + ret = adis_read_reg_16(st, addr, &val16); + if (ret) { + mutex_unlock(&indio_dev->mlock); + return ret; + } + val16 &= (1 << bits) - 1; + val16 = (s16)(val16 << (16 - bits)) >> (16 - bits); + *val = val16; + mutex_unlock(&indio_dev->mlock); + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static const struct iio_chan_spec adis16203_channels[] = { + ADIS_SUPPLY_CHAN(ADIS16203_SUPPLY_OUT, ADIS16203_SCAN_SUPPLY, 0, 12), + ADIS_AUX_ADC_CHAN(ADIS16203_AUX_ADC, ADIS16203_SCAN_AUX_ADC, 0, 12), + ADIS_INCLI_CHAN(X, ADIS16203_XINCL_OUT, ADIS16203_SCAN_INCLI_X, + BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), + /* Fixme: Not what it appears to be - see data sheet */ + ADIS_INCLI_CHAN(Y, ADIS16203_YINCL_OUT, ADIS16203_SCAN_INCLI_Y, + 0, 0, 14), + ADIS_TEMP_CHAN(ADIS16203_TEMP_OUT, ADIS16203_SCAN_TEMP, 0, 12), + IIO_CHAN_SOFT_TIMESTAMP(5), +}; + +static const struct iio_info adis16203_info = { + .read_raw = &adis16203_read_raw, + .write_raw = &adis16203_write_raw, + .update_scan_mode = adis_update_scan_mode, + .driver_module = THIS_MODULE, +}; + +static const char * const adis16203_status_error_msgs[] = { + [ADIS16203_DIAG_STAT_SELFTEST_FAIL_BIT] = "Self test failure", + [ADIS16203_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure", + [ADIS16203_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed", + [ADIS16203_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V", + [ADIS16203_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V", +}; + +static const struct adis_data adis16203_data = { + .read_delay = 20, + .msc_ctrl_reg = ADIS16203_MSC_CTRL, + .glob_cmd_reg = ADIS16203_GLOB_CMD, + .diag_stat_reg = ADIS16203_DIAG_STAT, + + .self_test_mask = ADIS16203_MSC_CTRL_SELF_TEST_EN, + .startup_delay = ADIS16203_STARTUP_DELAY, + + .status_error_msgs = adis16203_status_error_msgs, + .status_error_mask = BIT(ADIS16203_DIAG_STAT_SELFTEST_FAIL_BIT) | + BIT(ADIS16203_DIAG_STAT_SPI_FAIL_BIT) | + BIT(ADIS16203_DIAG_STAT_FLASH_UPT_BIT) | + BIT(ADIS16203_DIAG_STAT_POWER_HIGH_BIT) | + BIT(ADIS16203_DIAG_STAT_POWER_LOW_BIT), +}; + +static int adis16203_probe(struct spi_device *spi) +{ + int ret; + struct iio_dev *indio_dev; + struct adis *st; + + /* setup the industrialio driver allocated elements */ + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + st = iio_priv(indio_dev); + /* this is only used for removal purposes */ + spi_set_drvdata(spi, indio_dev); + + indio_dev->name = spi->dev.driver->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->channels = adis16203_channels; + indio_dev->num_channels = ARRAY_SIZE(adis16203_channels); + indio_dev->info = &adis16203_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = adis_init(st, indio_dev, spi, &adis16203_data); + if (ret) + return ret; + + ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL); + if (ret) + return ret; + + /* Get the device into a sane initial state */ + ret = adis_initial_startup(st); + if (ret) + goto error_cleanup_buffer_trigger; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_cleanup_buffer_trigger; + + return 0; + +error_cleanup_buffer_trigger: + adis_cleanup_buffer_and_trigger(st, indio_dev); + return ret; +} + +static int adis16203_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct adis *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + adis_cleanup_buffer_and_trigger(st, indio_dev); + + return 0; +} + +static struct spi_driver adis16203_driver = { + .driver = { + .name = "adis16203", + .owner = THIS_MODULE, + }, + .probe = adis16203_probe, + .remove = adis16203_remove, +}; +module_spi_driver(adis16203_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADIS16203 Programmable Digital Vibration Sensor driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:adis16203"); diff --git a/drivers/staging/iio/accel/adis16204.h b/drivers/staging/iio/accel/adis16204.h new file mode 100644 index 000000000..0b23f0b5c --- /dev/null +++ b/drivers/staging/iio/accel/adis16204.h @@ -0,0 +1,68 @@ +#ifndef SPI_ADIS16204_H_ +#define SPI_ADIS16204_H_ + +#define ADIS16204_STARTUP_DELAY 220 /* ms */ + +#define ADIS16204_FLASH_CNT 0x00 /* Flash memory write count */ +#define ADIS16204_SUPPLY_OUT 0x02 /* Output, power supply */ +#define ADIS16204_XACCL_OUT 0x04 /* Output, x-axis accelerometer */ +#define ADIS16204_YACCL_OUT 0x06 /* Output, y-axis accelerometer */ +#define ADIS16204_AUX_ADC 0x08 /* Output, auxiliary ADC input */ +#define ADIS16204_TEMP_OUT 0x0A /* Output, temperature */ +#define ADIS16204_X_PEAK_OUT 0x0C /* Twos complement */ +#define ADIS16204_Y_PEAK_OUT 0x0E /* Twos complement */ +#define ADIS16204_XACCL_NULL 0x10 /* Calibration, x-axis acceleration offset null */ +#define ADIS16204_YACCL_NULL 0x12 /* Calibration, y-axis acceleration offset null */ +#define ADIS16204_XACCL_SCALE 0x14 /* X-axis scale factor calibration register */ +#define ADIS16204_YACCL_SCALE 0x16 /* Y-axis scale factor calibration register */ +#define ADIS16204_XY_RSS_OUT 0x18 /* XY combined acceleration (RSS) */ +#define ADIS16204_XY_PEAK_OUT 0x1A /* Peak, XY combined output (RSS) */ +#define ADIS16204_CAP_BUF_1 0x1C /* Capture buffer output register 1 */ +#define ADIS16204_CAP_BUF_2 0x1E /* Capture buffer output register 2 */ +#define ADIS16204_ALM_MAG1 0x20 /* Alarm 1 amplitude threshold */ +#define ADIS16204_ALM_MAG2 0x22 /* Alarm 2 amplitude threshold */ +#define ADIS16204_ALM_CTRL 0x28 /* Alarm control */ +#define ADIS16204_CAPT_PNTR 0x2A /* Capture register address pointer */ +#define ADIS16204_AUX_DAC 0x30 /* Auxiliary DAC data */ +#define ADIS16204_GPIO_CTRL 0x32 /* General-purpose digital input/output control */ +#define ADIS16204_MSC_CTRL 0x34 /* Miscellaneous control */ +#define ADIS16204_SMPL_PRD 0x36 /* Internal sample period (rate) control */ +#define ADIS16204_AVG_CNT 0x38 /* Operation, filter configuration */ +#define ADIS16204_SLP_CNT 0x3A /* Operation, sleep mode control */ +#define ADIS16204_DIAG_STAT 0x3C /* Diagnostics, system status register */ +#define ADIS16204_GLOB_CMD 0x3E /* Operation, system command register */ + +/* MSC_CTRL */ +#define ADIS16204_MSC_CTRL_PWRUP_SELF_TEST BIT(10) /* Self-test at power-on: 1 = disabled, 0 = enabled */ +#define ADIS16204_MSC_CTRL_SELF_TEST_EN BIT(8) /* Self-test enable */ +#define ADIS16204_MSC_CTRL_DATA_RDY_EN BIT(2) /* Data-ready enable: 1 = enabled, 0 = disabled */ +#define ADIS16204_MSC_CTRL_ACTIVE_HIGH BIT(1) /* Data-ready polarity: 1 = active high, 0 = active low */ +#define ADIS16204_MSC_CTRL_DATA_RDY_DIO2 BIT(0) /* Data-ready line selection: 1 = DIO2, 0 = DIO1 */ + +/* DIAG_STAT */ +#define ADIS16204_DIAG_STAT_ALARM2 BIT(9) /* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */ +#define ADIS16204_DIAG_STAT_ALARM1 BIT(8) /* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */ +#define ADIS16204_DIAG_STAT_SELFTEST_FAIL_BIT 5 /* Self-test diagnostic error flag: 1 = error condition, + 0 = normal operation */ +#define ADIS16204_DIAG_STAT_SPI_FAIL_BIT 3 /* SPI communications failure */ +#define ADIS16204_DIAG_STAT_FLASH_UPT_BIT 2 /* Flash update failure */ +#define ADIS16204_DIAG_STAT_POWER_HIGH_BIT 1 /* Power supply above 3.625 V */ +#define ADIS16204_DIAG_STAT_POWER_LOW_BIT 0 /* Power supply below 2.975 V */ + +/* GLOB_CMD */ +#define ADIS16204_GLOB_CMD_SW_RESET BIT(7) +#define ADIS16204_GLOB_CMD_CLEAR_STAT BIT(4) +#define ADIS16204_GLOB_CMD_FACTORY_CAL BIT(1) + +#define ADIS16204_ERROR_ACTIVE BIT(14) + +enum adis16204_scan { + ADIS16204_SCAN_ACC_X, + ADIS16204_SCAN_ACC_Y, + ADIS16204_SCAN_ACC_XY, + ADIS16204_SCAN_SUPPLY, + ADIS16204_SCAN_AUX_ADC, + ADIS16204_SCAN_TEMP, +}; + +#endif /* SPI_ADIS16204_H_ */ diff --git a/drivers/staging/iio/accel/adis16204_core.c b/drivers/staging/iio/accel/adis16204_core.c new file mode 100644 index 000000000..ea0ac2467 --- /dev/null +++ b/drivers/staging/iio/accel/adis16204_core.c @@ -0,0 +1,254 @@ +/* + * ADIS16204 Programmable High-g Digital Impact Sensor and Recorder + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "adis16204.h" + +/* Unique to this driver currently */ + +static const u8 adis16204_addresses[][2] = { + [ADIS16204_SCAN_ACC_X] = { ADIS16204_XACCL_NULL, ADIS16204_X_PEAK_OUT }, + [ADIS16204_SCAN_ACC_Y] = { ADIS16204_YACCL_NULL, ADIS16204_Y_PEAK_OUT }, + [ADIS16204_SCAN_ACC_XY] = { 0, ADIS16204_XY_PEAK_OUT }, +}; + +static int adis16204_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, + long mask) +{ + struct adis *st = iio_priv(indio_dev); + int ret; + int bits; + u8 addr; + s16 val16; + int addrind; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return adis_single_conversion(indio_dev, chan, + ADIS16204_ERROR_ACTIVE, val); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->channel == 0) { + *val = 1; + *val2 = 220000; /* 1.22 mV */ + } else { + *val = 0; + *val2 = 610000; /* 0.61 mV */ + } + return IIO_VAL_INT_PLUS_MICRO; + case IIO_TEMP: + *val = -470; /* 0.47 C */ + *val2 = 0; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_ACCEL: + *val = 0; + switch (chan->channel2) { + case IIO_MOD_X: + case IIO_MOD_ROOT_SUM_SQUARED_X_Y: + *val2 = IIO_G_TO_M_S_2(17125); /* 17.125 mg */ + break; + case IIO_MOD_Y: + case IIO_MOD_Z: + *val2 = IIO_G_TO_M_S_2(8407); /* 8.407 mg */ + break; + } + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + break; + case IIO_CHAN_INFO_OFFSET: + *val = 25000 / -470 - 1278; /* 25 C = 1278 */ + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + case IIO_CHAN_INFO_PEAK: + if (mask == IIO_CHAN_INFO_CALIBBIAS) { + bits = 12; + addrind = 0; + } else { /* PEAK_SEPARATE */ + bits = 14; + addrind = 1; + } + mutex_lock(&indio_dev->mlock); + addr = adis16204_addresses[chan->scan_index][addrind]; + ret = adis_read_reg_16(st, addr, &val16); + if (ret) { + mutex_unlock(&indio_dev->mlock); + return ret; + } + val16 &= (1 << bits) - 1; + val16 = (s16)(val16 << (16 - bits)) >> (16 - bits); + *val = val16; + mutex_unlock(&indio_dev->mlock); + return IIO_VAL_INT; + } + return -EINVAL; +} + +static int adis16204_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct adis *st = iio_priv(indio_dev); + int bits; + s16 val16; + u8 addr; + + switch (mask) { + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) { + case IIO_ACCEL: + bits = 12; + break; + default: + return -EINVAL; + } + val16 = val & ((1 << bits) - 1); + addr = adis16204_addresses[chan->scan_index][1]; + return adis_write_reg_16(st, addr, val16); + } + return -EINVAL; +} + +static const struct iio_chan_spec adis16204_channels[] = { + ADIS_SUPPLY_CHAN(ADIS16204_SUPPLY_OUT, ADIS16204_SCAN_SUPPLY, 0, 12), + ADIS_AUX_ADC_CHAN(ADIS16204_AUX_ADC, ADIS16204_SCAN_AUX_ADC, 0, 12), + ADIS_TEMP_CHAN(ADIS16204_TEMP_OUT, ADIS16204_SCAN_TEMP, 0, 12), + ADIS_ACCEL_CHAN(X, ADIS16204_XACCL_OUT, ADIS16204_SCAN_ACC_X, + BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), + 0, 14), + ADIS_ACCEL_CHAN(Y, ADIS16204_YACCL_OUT, ADIS16204_SCAN_ACC_Y, + BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), + 0, 14), + ADIS_ACCEL_CHAN(ROOT_SUM_SQUARED_X_Y, ADIS16204_XY_RSS_OUT, + ADIS16204_SCAN_ACC_XY, BIT(IIO_CHAN_INFO_PEAK), 0, 14), + IIO_CHAN_SOFT_TIMESTAMP(5), +}; + +static const struct iio_info adis16204_info = { + .read_raw = &adis16204_read_raw, + .write_raw = &adis16204_write_raw, + .update_scan_mode = adis_update_scan_mode, + .driver_module = THIS_MODULE, +}; + +static const char * const adis16204_status_error_msgs[] = { + [ADIS16204_DIAG_STAT_SELFTEST_FAIL_BIT] = "Self test failure", + [ADIS16204_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure", + [ADIS16204_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed", + [ADIS16204_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V", + [ADIS16204_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 2.975V", +}; + +static const struct adis_data adis16204_data = { + .read_delay = 20, + .msc_ctrl_reg = ADIS16204_MSC_CTRL, + .glob_cmd_reg = ADIS16204_GLOB_CMD, + .diag_stat_reg = ADIS16204_DIAG_STAT, + + .self_test_mask = ADIS16204_MSC_CTRL_SELF_TEST_EN, + .startup_delay = ADIS16204_STARTUP_DELAY, + + .status_error_msgs = adis16204_status_error_msgs, + .status_error_mask = BIT(ADIS16204_DIAG_STAT_SELFTEST_FAIL_BIT) | + BIT(ADIS16204_DIAG_STAT_SPI_FAIL_BIT) | + BIT(ADIS16204_DIAG_STAT_FLASH_UPT_BIT) | + BIT(ADIS16204_DIAG_STAT_POWER_HIGH_BIT) | + BIT(ADIS16204_DIAG_STAT_POWER_LOW_BIT), +}; + +static int adis16204_probe(struct spi_device *spi) +{ + int ret; + struct adis *st; + struct iio_dev *indio_dev; + + /* setup the industrialio driver allocated elements */ + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + st = iio_priv(indio_dev); + /* this is only used for removal purposes */ + spi_set_drvdata(spi, indio_dev); + + indio_dev->name = spi->dev.driver->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->info = &adis16204_info; + indio_dev->channels = adis16204_channels; + indio_dev->num_channels = ARRAY_SIZE(adis16204_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = adis_init(st, indio_dev, spi, &adis16204_data); + if (ret) + return ret; + + ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL); + if (ret) + return ret; + + /* Get the device into a sane initial state */ + ret = adis_initial_startup(st); + if (ret) + goto error_cleanup_buffer_trigger; + ret = iio_device_register(indio_dev); + if (ret) + goto error_cleanup_buffer_trigger; + + return 0; + +error_cleanup_buffer_trigger: + adis_cleanup_buffer_and_trigger(st, indio_dev); + return ret; +} + +static int adis16204_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct adis *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + adis_cleanup_buffer_and_trigger(st, indio_dev); + + return 0; +} + +static struct spi_driver adis16204_driver = { + .driver = { + .name = "adis16204", + .owner = THIS_MODULE, + }, + .probe = adis16204_probe, + .remove = adis16204_remove, +}; +module_spi_driver(adis16204_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("ADIS16204 High-g Digital Impact Sensor and Recorder"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:adis16204"); diff --git a/drivers/staging/iio/accel/adis16209.h b/drivers/staging/iio/accel/adis16209.h new file mode 100644 index 000000000..813698d18 --- /dev/null +++ b/drivers/staging/iio/accel/adis16209.h @@ -0,0 +1,105 @@ +#ifndef SPI_ADIS16209_H_ +#define SPI_ADIS16209_H_ + +#define ADIS16209_STARTUP_DELAY 220 /* ms */ + +/* Flash memory write count */ +#define ADIS16209_FLASH_CNT 0x00 +/* Output, power supply */ +#define ADIS16209_SUPPLY_OUT 0x02 +/* Output, x-axis accelerometer */ +#define ADIS16209_XACCL_OUT 0x04 +/* Output, y-axis accelerometer */ +#define ADIS16209_YACCL_OUT 0x06 +/* Output, auxiliary ADC input */ +#define ADIS16209_AUX_ADC 0x08 +/* Output, temperature */ +#define ADIS16209_TEMP_OUT 0x0A +/* Output, x-axis inclination */ +#define ADIS16209_XINCL_OUT 0x0C +/* Output, y-axis inclination */ +#define ADIS16209_YINCL_OUT 0x0E +/* Output, +/-180 vertical rotational position */ +#define ADIS16209_ROT_OUT 0x10 +/* Calibration, x-axis acceleration offset null */ +#define ADIS16209_XACCL_NULL 0x12 +/* Calibration, y-axis acceleration offset null */ +#define ADIS16209_YACCL_NULL 0x14 +/* Calibration, x-axis inclination offset null */ +#define ADIS16209_XINCL_NULL 0x16 +/* Calibration, y-axis inclination offset null */ +#define ADIS16209_YINCL_NULL 0x18 +/* Calibration, vertical rotation offset null */ +#define ADIS16209_ROT_NULL 0x1A +/* Alarm 1 amplitude threshold */ +#define ADIS16209_ALM_MAG1 0x20 +/* Alarm 2 amplitude threshold */ +#define ADIS16209_ALM_MAG2 0x22 +/* Alarm 1, sample period */ +#define ADIS16209_ALM_SMPL1 0x24 +/* Alarm 2, sample period */ +#define ADIS16209_ALM_SMPL2 0x26 +/* Alarm control */ +#define ADIS16209_ALM_CTRL 0x28 +/* Auxiliary DAC data */ +#define ADIS16209_AUX_DAC 0x30 +/* General-purpose digital input/output control */ +#define ADIS16209_GPIO_CTRL 0x32 +/* Miscellaneous control */ +#define ADIS16209_MSC_CTRL 0x34 +/* Internal sample period (rate) control */ +#define ADIS16209_SMPL_PRD 0x36 +/* Operation, filter configuration */ +#define ADIS16209_AVG_CNT 0x38 +/* Operation, sleep mode control */ +#define ADIS16209_SLP_CNT 0x3A +/* Diagnostics, system status register */ +#define ADIS16209_DIAG_STAT 0x3C +/* Operation, system command register */ +#define ADIS16209_GLOB_CMD 0x3E + +/* MSC_CTRL */ +/* Self-test at power-on: 1 = disabled, 0 = enabled */ +#define ADIS16209_MSC_CTRL_PWRUP_SELF_TEST BIT(10) +/* Self-test enable */ +#define ADIS16209_MSC_CTRL_SELF_TEST_EN BIT(8) +/* Data-ready enable: 1 = enabled, 0 = disabled */ +#define ADIS16209_MSC_CTRL_DATA_RDY_EN BIT(2) +/* Data-ready polarity: 1 = active high, 0 = active low */ +#define ADIS16209_MSC_CTRL_ACTIVE_HIGH BIT(1) +/* Data-ready line selection: 1 = DIO2, 0 = DIO1 */ +#define ADIS16209_MSC_CTRL_DATA_RDY_DIO2 BIT(0) + +/* DIAG_STAT */ +/* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */ +#define ADIS16209_DIAG_STAT_ALARM2 BIT(9) +/* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */ +#define ADIS16209_DIAG_STAT_ALARM1 BIT(8) +/* Self-test diagnostic error flag: 1 = error condition, 0 = normal operation */ +#define ADIS16209_DIAG_STAT_SELFTEST_FAIL_BIT 5 +/* SPI communications failure */ +#define ADIS16209_DIAG_STAT_SPI_FAIL_BIT 3 +/* Flash update failure */ +#define ADIS16209_DIAG_STAT_FLASH_UPT_BIT 2 +/* Power supply above 3.625 V */ +#define ADIS16209_DIAG_STAT_POWER_HIGH_BIT 1 +/* Power supply below 3.15 V */ +#define ADIS16209_DIAG_STAT_POWER_LOW_BIT 0 + +/* GLOB_CMD */ +#define ADIS16209_GLOB_CMD_SW_RESET BIT(7) +#define ADIS16209_GLOB_CMD_CLEAR_STAT BIT(4) +#define ADIS16209_GLOB_CMD_FACTORY_CAL BIT(1) + +#define ADIS16209_ERROR_ACTIVE BIT(14) + +#define ADIS16209_SCAN_SUPPLY 0 +#define ADIS16209_SCAN_ACC_X 1 +#define ADIS16209_SCAN_ACC_Y 2 +#define ADIS16209_SCAN_AUX_ADC 3 +#define ADIS16209_SCAN_TEMP 4 +#define ADIS16209_SCAN_INCLI_X 5 +#define ADIS16209_SCAN_INCLI_Y 6 +#define ADIS16209_SCAN_ROT 7 + +#endif /* SPI_ADIS16209_H_ */ diff --git a/drivers/staging/iio/accel/adis16209_core.c b/drivers/staging/iio/accel/adis16209_core.c new file mode 100644 index 000000000..d1dc1a3cb --- /dev/null +++ b/drivers/staging/iio/accel/adis16209_core.c @@ -0,0 +1,248 @@ +/* + * ADIS16209 Programmable Digital Vibration Sensor driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "adis16209.h" + +static const u8 adis16209_addresses[8][1] = { + [ADIS16209_SCAN_SUPPLY] = { }, + [ADIS16209_SCAN_AUX_ADC] = { }, + [ADIS16209_SCAN_ACC_X] = { ADIS16209_XACCL_NULL }, + [ADIS16209_SCAN_ACC_Y] = { ADIS16209_YACCL_NULL }, + [ADIS16209_SCAN_INCLI_X] = { ADIS16209_XINCL_NULL }, + [ADIS16209_SCAN_INCLI_Y] = { ADIS16209_YINCL_NULL }, + [ADIS16209_SCAN_ROT] = { }, + [ADIS16209_SCAN_TEMP] = { }, +}; + +static int adis16209_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct adis *st = iio_priv(indio_dev); + int bits; + s16 val16; + u8 addr; + + switch (mask) { + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) { + case IIO_ACCEL: + case IIO_INCLI: + bits = 14; + break; + default: + return -EINVAL; + } + val16 = val & ((1 << bits) - 1); + addr = adis16209_addresses[chan->scan_index][0]; + return adis_write_reg_16(st, addr, val16); + } + return -EINVAL; +} + +static int adis16209_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, + long mask) +{ + struct adis *st = iio_priv(indio_dev); + int ret; + int bits; + u8 addr; + s16 val16; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return adis_single_conversion(indio_dev, chan, + ADIS16209_ERROR_ACTIVE, val); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + *val = 0; + if (chan->channel == 0) + *val2 = 305180; /* 0.30518 mV */ + else + *val2 = 610500; /* 0.6105 mV */ + return IIO_VAL_INT_PLUS_MICRO; + case IIO_TEMP: + *val = -470; /* -0.47 C */ + *val2 = 0; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_ACCEL: + *val = 0; + *val2 = IIO_G_TO_M_S_2(244140); /* 0.244140 mg */ + return IIO_VAL_INT_PLUS_NANO; + case IIO_INCLI: + case IIO_ROT: + *val = 0; + *val2 = 25000; /* 0.025 degree */ + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + break; + case IIO_CHAN_INFO_OFFSET: + *val = 25000 / -470 - 0x4FE; /* 25 C = 0x4FE */ + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) { + case IIO_ACCEL: + bits = 14; + break; + default: + return -EINVAL; + } + mutex_lock(&indio_dev->mlock); + addr = adis16209_addresses[chan->scan_index][0]; + ret = adis_read_reg_16(st, addr, &val16); + if (ret) { + mutex_unlock(&indio_dev->mlock); + return ret; + } + val16 &= (1 << bits) - 1; + val16 = (s16)(val16 << (16 - bits)) >> (16 - bits); + *val = val16; + mutex_unlock(&indio_dev->mlock); + return IIO_VAL_INT; + } + return -EINVAL; +} + +static const struct iio_chan_spec adis16209_channels[] = { + ADIS_SUPPLY_CHAN(ADIS16209_SUPPLY_OUT, ADIS16209_SCAN_SUPPLY, 0, 14), + ADIS_TEMP_CHAN(ADIS16209_TEMP_OUT, ADIS16209_SCAN_TEMP, 0, 12), + ADIS_ACCEL_CHAN(X, ADIS16209_XACCL_OUT, ADIS16209_SCAN_ACC_X, + BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), + ADIS_ACCEL_CHAN(Y, ADIS16209_YACCL_OUT, ADIS16209_SCAN_ACC_Y, + BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), + ADIS_AUX_ADC_CHAN(ADIS16209_AUX_ADC, ADIS16209_SCAN_AUX_ADC, 0, 12), + ADIS_INCLI_CHAN(X, ADIS16209_XINCL_OUT, ADIS16209_SCAN_INCLI_X, + 0, 0, 14), + ADIS_INCLI_CHAN(Y, ADIS16209_YINCL_OUT, ADIS16209_SCAN_INCLI_Y, + 0, 0, 14), + ADIS_ROT_CHAN(X, ADIS16209_ROT_OUT, ADIS16209_SCAN_ROT, 0, 0, 14), + IIO_CHAN_SOFT_TIMESTAMP(8) +}; + +static const struct iio_info adis16209_info = { + .read_raw = &adis16209_read_raw, + .write_raw = &adis16209_write_raw, + .update_scan_mode = adis_update_scan_mode, + .driver_module = THIS_MODULE, +}; + +static const char * const adis16209_status_error_msgs[] = { + [ADIS16209_DIAG_STAT_SELFTEST_FAIL_BIT] = "Self test failure", + [ADIS16209_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure", + [ADIS16209_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed", + [ADIS16209_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V", + [ADIS16209_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V", +}; + +static const struct adis_data adis16209_data = { + .read_delay = 30, + .msc_ctrl_reg = ADIS16209_MSC_CTRL, + .glob_cmd_reg = ADIS16209_GLOB_CMD, + .diag_stat_reg = ADIS16209_DIAG_STAT, + + .self_test_mask = ADIS16209_MSC_CTRL_SELF_TEST_EN, + .startup_delay = ADIS16209_STARTUP_DELAY, + + .status_error_msgs = adis16209_status_error_msgs, + .status_error_mask = BIT(ADIS16209_DIAG_STAT_SELFTEST_FAIL_BIT) | + BIT(ADIS16209_DIAG_STAT_SPI_FAIL_BIT) | + BIT(ADIS16209_DIAG_STAT_FLASH_UPT_BIT) | + BIT(ADIS16209_DIAG_STAT_POWER_HIGH_BIT) | + BIT(ADIS16209_DIAG_STAT_POWER_LOW_BIT), +}; + +static int adis16209_probe(struct spi_device *spi) +{ + int ret; + struct adis *st; + struct iio_dev *indio_dev; + + /* setup the industrialio driver allocated elements */ + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + st = iio_priv(indio_dev); + /* this is only used for removal purposes */ + spi_set_drvdata(spi, indio_dev); + + indio_dev->name = spi->dev.driver->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->info = &adis16209_info; + indio_dev->channels = adis16209_channels; + indio_dev->num_channels = ARRAY_SIZE(adis16209_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = adis_init(st, indio_dev, spi, &adis16209_data); + if (ret) + return ret; + ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL); + if (ret) + return ret; + + /* Get the device into a sane initial state */ + ret = adis_initial_startup(st); + if (ret) + goto error_cleanup_buffer_trigger; + ret = iio_device_register(indio_dev); + if (ret) + goto error_cleanup_buffer_trigger; + + return 0; + +error_cleanup_buffer_trigger: + adis_cleanup_buffer_and_trigger(st, indio_dev); + return ret; +} + +static int adis16209_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct adis *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + adis_cleanup_buffer_and_trigger(st, indio_dev); + + return 0; +} + +static struct spi_driver adis16209_driver = { + .driver = { + .name = "adis16209", + .owner = THIS_MODULE, + }, + .probe = adis16209_probe, + .remove = adis16209_remove, +}; +module_spi_driver(adis16209_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADIS16209 Digital Vibration Sensor driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:adis16209"); diff --git a/drivers/staging/iio/accel/adis16220.h b/drivers/staging/iio/accel/adis16220.h new file mode 100644 index 000000000..eab863311 --- /dev/null +++ b/drivers/staging/iio/accel/adis16220.h @@ -0,0 +1,140 @@ +#ifndef SPI_ADIS16220_H_ +#define SPI_ADIS16220_H_ + +#include + +#define ADIS16220_STARTUP_DELAY 220 /* ms */ + +/* Flash memory write count */ +#define ADIS16220_FLASH_CNT 0x00 +/* Control, acceleration offset adjustment control */ +#define ADIS16220_ACCL_NULL 0x02 +/* Control, AIN1 offset adjustment control */ +#define ADIS16220_AIN1_NULL 0x04 +/* Control, AIN2 offset adjustment control */ +#define ADIS16220_AIN2_NULL 0x06 +/* Output, power supply during capture */ +#define ADIS16220_CAPT_SUPPLY 0x0A +/* Output, temperature during capture */ +#define ADIS16220_CAPT_TEMP 0x0C +/* Output, peak acceleration during capture */ +#define ADIS16220_CAPT_PEAKA 0x0E +/* Output, peak AIN1 level during capture */ +#define ADIS16220_CAPT_PEAK1 0x10 +/* Output, peak AIN2 level during capture */ +#define ADIS16220_CAPT_PEAK2 0x12 +/* Output, capture buffer for acceleration */ +#define ADIS16220_CAPT_BUFA 0x14 +/* Output, capture buffer for AIN1 */ +#define ADIS16220_CAPT_BUF1 0x16 +/* Output, capture buffer for AIN2 */ +#define ADIS16220_CAPT_BUF2 0x18 +/* Control, capture buffer address pointer */ +#define ADIS16220_CAPT_PNTR 0x1A +/* Control, capture control register */ +#define ADIS16220_CAPT_CTRL 0x1C +/* Control, capture period (automatic mode) */ +#define ADIS16220_CAPT_PRD 0x1E +/* Control, Alarm A, acceleration peak threshold */ +#define ADIS16220_ALM_MAGA 0x20 +/* Control, Alarm 1, AIN1 peak threshold */ +#define ADIS16220_ALM_MAG1 0x22 +/* Control, Alarm 2, AIN2 peak threshold */ +#define ADIS16220_ALM_MAG2 0x24 +/* Control, Alarm S, peak threshold */ +#define ADIS16220_ALM_MAGS 0x26 +/* Control, alarm configuration register */ +#define ADIS16220_ALM_CTRL 0x28 +/* Control, general I/O configuration */ +#define ADIS16220_GPIO_CTRL 0x32 +/* Control, self-test control, AIN configuration */ +#define ADIS16220_MSC_CTRL 0x34 +/* Control, digital I/O configuration */ +#define ADIS16220_DIO_CTRL 0x36 +/* Control, filter configuration */ +#define ADIS16220_AVG_CNT 0x38 +/* Status, system status */ +#define ADIS16220_DIAG_STAT 0x3C +/* Control, system commands */ +#define ADIS16220_GLOB_CMD 0x3E +/* Status, self-test response */ +#define ADIS16220_ST_DELTA 0x40 +/* Lot Identification Code 1 */ +#define ADIS16220_LOT_ID1 0x52 +/* Lot Identification Code 2 */ +#define ADIS16220_LOT_ID2 0x54 +/* Product identifier; convert to decimal = 16220 */ +#define ADIS16220_PROD_ID 0x56 +/* Serial number */ +#define ADIS16220_SERIAL_NUM 0x58 + +#define ADIS16220_CAPTURE_SIZE 2048 + +/* MSC_CTRL */ +#define ADIS16220_MSC_CTRL_SELF_TEST_EN BIT(8) +#define ADIS16220_MSC_CTRL_POWER_SUP_COM_AIN1 BIT(1) +#define ADIS16220_MSC_CTRL_POWER_SUP_COM_AIN2 BIT(0) + +/* DIO_CTRL */ +#define ADIS16220_MSC_CTRL_DIO2_BUSY_IND (BIT(5) | BIT(4)) +#define ADIS16220_MSC_CTRL_DIO1_BUSY_IND (BIT(3) | BIT(2)) +#define ADIS16220_MSC_CTRL_DIO2_ACT_HIGH BIT(1) +#define ADIS16220_MSC_CTRL_DIO1_ACT_HIGH BIT(0) + +/* DIAG_STAT */ +/* AIN2 sample > ALM_MAG2 */ +#define ADIS16220_DIAG_STAT_ALM_MAG2 BIT(14) +/* AIN1 sample > ALM_MAG1 */ +#define ADIS16220_DIAG_STAT_ALM_MAG1 BIT(13) +/* Acceleration sample > ALM_MAGA */ +#define ADIS16220_DIAG_STAT_ALM_MAGA BIT(12) +/* Error condition programmed into ALM_MAGS[11:0] and ALM_CTRL[5:4] is true */ +#define ADIS16220_DIAG_STAT_ALM_MAGS BIT(11) +/* |Peak value in AIN2 data capture| > ALM_MAG2 */ +#define ADIS16220_DIAG_STAT_PEAK_AIN2 BIT(10) +/* |Peak value in AIN1 data capture| > ALM_MAG1 */ +#define ADIS16220_DIAG_STAT_PEAK_AIN1 BIT(9) +/* |Peak value in acceleration data capture| > ALM_MAGA */ +#define ADIS16220_DIAG_STAT_PEAK_ACCEL BIT(8) +/* Data ready, capture complete */ +#define ADIS16220_DIAG_STAT_DATA_RDY BIT(7) +#define ADIS16220_DIAG_STAT_FLASH_CHK BIT(6) +#define ADIS16220_DIAG_STAT_SELF_TEST BIT(5) +/* Capture period violation/interruption */ +#define ADIS16220_DIAG_STAT_VIOLATION_BIT 4 +/* SPI communications failure */ +#define ADIS16220_DIAG_STAT_SPI_FAIL_BIT 3 +/* Flash update failure */ +#define ADIS16220_DIAG_STAT_FLASH_UPT_BIT 2 +/* Power supply above 3.625 V */ +#define ADIS16220_DIAG_STAT_POWER_HIGH_BIT 1 +/* Power supply below 3.15 V */ +#define ADIS16220_DIAG_STAT_POWER_LOW_BIT 0 + +/* GLOB_CMD */ +#define ADIS16220_GLOB_CMD_SW_RESET BIT(7) +#define ADIS16220_GLOB_CMD_SELF_TEST BIT(2) +#define ADIS16220_GLOB_CMD_PWR_DOWN BIT(1) + +#define ADIS16220_MAX_TX 2048 +#define ADIS16220_MAX_RX 2048 + +#define ADIS16220_SPI_BURST (u32)(1000 * 1000) +#define ADIS16220_SPI_FAST (u32)(2000 * 1000) + +/** + * struct adis16220_state - device instance specific data + * @adis: adis device + * @tx: transmit buffer + * @rx: receive buffer + * @buf_lock: mutex to protect tx and rx + **/ +struct adis16220_state { + struct adis adis; + + struct mutex buf_lock; + u8 tx[ADIS16220_MAX_TX] ____cacheline_aligned; + u8 rx[ADIS16220_MAX_RX]; +}; + +#endif /* SPI_ADIS16220_H_ */ diff --git a/drivers/staging/iio/accel/adis16220_core.c b/drivers/staging/iio/accel/adis16220_core.c new file mode 100644 index 000000000..e46a91c69 --- /dev/null +++ b/drivers/staging/iio/accel/adis16220_core.c @@ -0,0 +1,495 @@ +/* + * ADIS16220 Programmable Digital Vibration Sensor driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "adis16220.h" + +static ssize_t adis16220_read_16bit(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct adis16220_state *st = iio_priv(indio_dev); + ssize_t ret; + u16 val; + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis_read_reg_16(&st->adis, this_attr->address, &val); + mutex_unlock(&indio_dev->mlock); + if (ret) + return ret; + return sprintf(buf, "%u\n", val); +} + +static ssize_t adis16220_write_16bit(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + struct adis16220_state *st = iio_priv(indio_dev); + int ret; + u16 val; + + ret = kstrtou16(buf, 10, &val); + if (ret) + goto error_ret; + ret = adis_write_reg_16(&st->adis, this_attr->address, val); + +error_ret: + return ret ? ret : len; +} + +static int adis16220_capture(struct iio_dev *indio_dev) +{ + struct adis16220_state *st = iio_priv(indio_dev); + int ret; + + /* initiates a manual data capture */ + ret = adis_write_reg_16(&st->adis, ADIS16220_GLOB_CMD, 0xBF08); + if (ret) + dev_err(&indio_dev->dev, "problem beginning capture"); + + usleep_range(10000, 11000); /* delay for capture to finish */ + + return ret; +} + +static ssize_t adis16220_write_capture(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + bool val; + int ret; + + ret = strtobool(buf, &val); + if (ret) + return ret; + if (!val) + return -EINVAL; + ret = adis16220_capture(indio_dev); + if (ret) + return ret; + + return len; +} + +static ssize_t adis16220_capture_buffer_read(struct iio_dev *indio_dev, + char *buf, + loff_t off, + size_t count, + int addr) +{ + struct adis16220_state *st = iio_priv(indio_dev); + struct spi_transfer xfers[] = { + { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + .delay_usecs = 25, + }, { + .tx_buf = st->tx, + .rx_buf = st->rx, + .bits_per_word = 8, + .cs_change = 1, + .delay_usecs = 25, + }, + }; + int ret; + int i; + + if (unlikely(!count)) + return count; + + if ((off >= ADIS16220_CAPTURE_SIZE) || (count & 1) || (off & 1)) + return -EINVAL; + + if (off + count > ADIS16220_CAPTURE_SIZE) + count = ADIS16220_CAPTURE_SIZE - off; + + /* write the begin position of capture buffer */ + ret = adis_write_reg_16(&st->adis, + ADIS16220_CAPT_PNTR, + off > 1); + if (ret) + return -EIO; + + /* read count/2 values from capture buffer */ + mutex_lock(&st->buf_lock); + + for (i = 0; i < count; i += 2) { + st->tx[i] = ADIS_READ_REG(addr); + st->tx[i + 1] = 0; + } + xfers[1].len = count; + + ret = spi_sync_transfer(st->adis.spi, xfers, ARRAY_SIZE(xfers)); + if (ret) { + mutex_unlock(&st->buf_lock); + return -EIO; + } + + memcpy(buf, st->rx, count); + + mutex_unlock(&st->buf_lock); + return count; +} + +static ssize_t adis16220_accel_bin_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, + loff_t off, + size_t count) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(kobj_to_dev(kobj)); + + return adis16220_capture_buffer_read(indio_dev, buf, + off, count, + ADIS16220_CAPT_BUFA); +} + +static struct bin_attribute accel_bin = { + .attr = { + .name = "accel_bin", + .mode = S_IRUGO, + }, + .read = adis16220_accel_bin_read, + .size = ADIS16220_CAPTURE_SIZE, +}; + +static ssize_t adis16220_adc1_bin_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, + size_t count) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(kobj_to_dev(kobj)); + + return adis16220_capture_buffer_read(indio_dev, buf, + off, count, + ADIS16220_CAPT_BUF1); +} + +static struct bin_attribute adc1_bin = { + .attr = { + .name = "in0_bin", + .mode = S_IRUGO, + }, + .read = adis16220_adc1_bin_read, + .size = ADIS16220_CAPTURE_SIZE, +}; + +static ssize_t adis16220_adc2_bin_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, + size_t count) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(kobj_to_dev(kobj)); + + return adis16220_capture_buffer_read(indio_dev, buf, + off, count, + ADIS16220_CAPT_BUF2); +} + +static struct bin_attribute adc2_bin = { + .attr = { + .name = "in1_bin", + .mode = S_IRUGO, + }, + .read = adis16220_adc2_bin_read, + .size = ADIS16220_CAPTURE_SIZE, +}; + +#define IIO_DEV_ATTR_CAPTURE(_store) \ + IIO_DEVICE_ATTR(capture, S_IWUSR, NULL, _store, 0) + +static IIO_DEV_ATTR_CAPTURE(adis16220_write_capture); + +#define IIO_DEV_ATTR_CAPTURE_COUNT(_mode, _show, _store, _addr) \ + IIO_DEVICE_ATTR(capture_count, _mode, _show, _store, _addr) + +static IIO_DEV_ATTR_CAPTURE_COUNT(S_IWUSR | S_IRUGO, + adis16220_read_16bit, + adis16220_write_16bit, + ADIS16220_CAPT_PNTR); + +enum adis16220_channel { + in_supply, in_1, in_2, accel, temp +}; + +struct adis16220_address_spec { + u8 addr; + u8 bits; + bool sign; +}; + +/* Address / bits / signed */ +static const struct adis16220_address_spec adis16220_addresses[][3] = { + [in_supply] = { { ADIS16220_CAPT_SUPPLY, 12, 0 }, }, + [in_1] = { { ADIS16220_CAPT_BUF1, 16, 1 }, + { ADIS16220_AIN1_NULL, 16, 1 }, + { ADIS16220_CAPT_PEAK1, 16, 1 }, }, + [in_2] = { { ADIS16220_CAPT_BUF2, 16, 1 }, + { ADIS16220_AIN2_NULL, 16, 1 }, + { ADIS16220_CAPT_PEAK2, 16, 1 }, }, + [accel] = { { ADIS16220_CAPT_BUFA, 16, 1 }, + { ADIS16220_ACCL_NULL, 16, 1 }, + { ADIS16220_CAPT_PEAKA, 16, 1 }, }, + [temp] = { { ADIS16220_CAPT_TEMP, 12, 0 }, } +}; + +static int adis16220_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, + long mask) +{ + struct adis16220_state *st = iio_priv(indio_dev); + const struct adis16220_address_spec *addr; + int ret = -EINVAL; + int addrind = 0; + u16 uval; + s16 sval; + u8 bits; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + addrind = 0; + break; + case IIO_CHAN_INFO_OFFSET: + if (chan->type == IIO_TEMP) { + *val = 25000 / -470 - 1278; /* 25 C = 1278 */ + return IIO_VAL_INT; + } + addrind = 1; + break; + case IIO_CHAN_INFO_PEAK: + addrind = 2; + break; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_TEMP: + *val = -470; /* -0.47 C */ + *val2 = 0; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_ACCEL: + *val2 = IIO_G_TO_M_S_2(19073); /* 19.073 g */ + return IIO_VAL_INT_PLUS_MICRO; + case IIO_VOLTAGE: + if (chan->channel == 0) { + *val = 1; + *val2 = 220700; /* 1.2207 mV */ + } else { + /* Should really be dependent on VDD */ + *val2 = 305180; /* 305.18 uV */ + } + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + default: + return -EINVAL; + } + addr = &adis16220_addresses[chan->address][addrind]; + if (addr->sign) { + ret = adis_read_reg_16(&st->adis, addr->addr, &sval); + if (ret) + return ret; + bits = addr->bits; + sval &= (1 << bits) - 1; + sval = (s16)(sval << (16 - bits)) >> (16 - bits); + *val = sval; + return IIO_VAL_INT; + } + ret = adis_read_reg_16(&st->adis, addr->addr, &uval); + if (ret) + return ret; + bits = addr->bits; + uval &= (1 << bits) - 1; + *val = uval; + return IIO_VAL_INT; +} + +static const struct iio_chan_spec adis16220_channels[] = { + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = 0, + .extend_name = "supply", + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .address = in_supply, + }, { + .type = IIO_ACCEL, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_PEAK), + .address = accel, + }, { + .type = IIO_TEMP, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE), + .address = temp, + }, { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE), + .address = in_1, + }, { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = 2, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .address = in_2, + } +}; + +static struct attribute *adis16220_attributes[] = { + &iio_dev_attr_capture.dev_attr.attr, + &iio_dev_attr_capture_count.dev_attr.attr, + NULL +}; + +static const struct attribute_group adis16220_attribute_group = { + .attrs = adis16220_attributes, +}; + +static const struct iio_info adis16220_info = { + .attrs = &adis16220_attribute_group, + .driver_module = THIS_MODULE, + .read_raw = &adis16220_read_raw, +}; + +static const char * const adis16220_status_error_msgs[] = { + [ADIS16220_DIAG_STAT_VIOLATION_BIT] = "Capture period violation/interruption", + [ADIS16220_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure", + [ADIS16220_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed", + [ADIS16220_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V", + [ADIS16220_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V", +}; + +static const struct adis_data adis16220_data = { + .read_delay = 35, + .write_delay = 35, + .msc_ctrl_reg = ADIS16220_MSC_CTRL, + .glob_cmd_reg = ADIS16220_GLOB_CMD, + .diag_stat_reg = ADIS16220_DIAG_STAT, + + .self_test_mask = ADIS16220_MSC_CTRL_SELF_TEST_EN, + .startup_delay = ADIS16220_STARTUP_DELAY, + + .status_error_msgs = adis16220_status_error_msgs, + .status_error_mask = BIT(ADIS16220_DIAG_STAT_VIOLATION_BIT) | + BIT(ADIS16220_DIAG_STAT_SPI_FAIL_BIT) | + BIT(ADIS16220_DIAG_STAT_FLASH_UPT_BIT) | + BIT(ADIS16220_DIAG_STAT_POWER_HIGH_BIT) | + BIT(ADIS16220_DIAG_STAT_POWER_LOW_BIT), +}; + +static int adis16220_probe(struct spi_device *spi) +{ + int ret; + struct adis16220_state *st; + struct iio_dev *indio_dev; + + /* setup the industrialio driver allocated elements */ + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + /* this is only used for removal purposes */ + spi_set_drvdata(spi, indio_dev); + + indio_dev->name = spi->dev.driver->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->info = &adis16220_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = adis16220_channels; + indio_dev->num_channels = ARRAY_SIZE(adis16220_channels); + + ret = devm_iio_device_register(&spi->dev, indio_dev); + if (ret) + return ret; + + ret = sysfs_create_bin_file(&indio_dev->dev.kobj, &accel_bin); + if (ret) + return ret; + + ret = sysfs_create_bin_file(&indio_dev->dev.kobj, &adc1_bin); + if (ret) + goto error_rm_accel_bin; + + ret = sysfs_create_bin_file(&indio_dev->dev.kobj, &adc2_bin); + if (ret) + goto error_rm_adc1_bin; + + ret = adis_init(&st->adis, indio_dev, spi, &adis16220_data); + if (ret) + goto error_rm_adc2_bin; + /* Get the device into a sane initial state */ + ret = adis_initial_startup(&st->adis); + if (ret) + goto error_rm_adc2_bin; + return 0; + +error_rm_adc2_bin: + sysfs_remove_bin_file(&indio_dev->dev.kobj, &adc2_bin); +error_rm_adc1_bin: + sysfs_remove_bin_file(&indio_dev->dev.kobj, &adc1_bin); +error_rm_accel_bin: + sysfs_remove_bin_file(&indio_dev->dev.kobj, &accel_bin); + return ret; +} + +static int adis16220_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + + sysfs_remove_bin_file(&indio_dev->dev.kobj, &adc2_bin); + sysfs_remove_bin_file(&indio_dev->dev.kobj, &adc1_bin); + sysfs_remove_bin_file(&indio_dev->dev.kobj, &accel_bin); + + return 0; +} + +static struct spi_driver adis16220_driver = { + .driver = { + .name = "adis16220", + .owner = THIS_MODULE, + }, + .probe = adis16220_probe, + .remove = adis16220_remove, +}; +module_spi_driver(adis16220_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADIS16220 Digital Vibration Sensor"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:adis16220"); diff --git a/drivers/staging/iio/accel/adis16240.h b/drivers/staging/iio/accel/adis16240.h new file mode 100644 index 000000000..66b5ad2f4 --- /dev/null +++ b/drivers/staging/iio/accel/adis16240.h @@ -0,0 +1,129 @@ +#ifndef SPI_ADIS16240_H_ +#define SPI_ADIS16240_H_ + +#define ADIS16240_STARTUP_DELAY 220 /* ms */ + +/* Flash memory write count */ +#define ADIS16240_FLASH_CNT 0x00 +/* Output, power supply */ +#define ADIS16240_SUPPLY_OUT 0x02 +/* Output, x-axis accelerometer */ +#define ADIS16240_XACCL_OUT 0x04 +/* Output, y-axis accelerometer */ +#define ADIS16240_YACCL_OUT 0x06 +/* Output, z-axis accelerometer */ +#define ADIS16240_ZACCL_OUT 0x08 +/* Output, auxiliary ADC input */ +#define ADIS16240_AUX_ADC 0x0A +/* Output, temperature */ +#define ADIS16240_TEMP_OUT 0x0C +/* Output, x-axis acceleration peak */ +#define ADIS16240_XPEAK_OUT 0x0E +/* Output, y-axis acceleration peak */ +#define ADIS16240_YPEAK_OUT 0x10 +/* Output, z-axis acceleration peak */ +#define ADIS16240_ZPEAK_OUT 0x12 +/* Output, sum-of-squares acceleration peak */ +#define ADIS16240_XYZPEAK_OUT 0x14 +/* Output, Capture Buffer 1, X and Y acceleration */ +#define ADIS16240_CAPT_BUF1 0x16 +/* Output, Capture Buffer 2, Z acceleration */ +#define ADIS16240_CAPT_BUF2 0x18 +/* Diagnostic, error flags */ +#define ADIS16240_DIAG_STAT 0x1A +/* Diagnostic, event counter */ +#define ADIS16240_EVNT_CNTR 0x1C +/* Diagnostic, check sum value from firmware test */ +#define ADIS16240_CHK_SUM 0x1E +/* Calibration, x-axis acceleration offset adjustment */ +#define ADIS16240_XACCL_OFF 0x20 +/* Calibration, y-axis acceleration offset adjustment */ +#define ADIS16240_YACCL_OFF 0x22 +/* Calibration, z-axis acceleration offset adjustment */ +#define ADIS16240_ZACCL_OFF 0x24 +/* Clock, hour and minute */ +#define ADIS16240_CLK_TIME 0x2E +/* Clock, month and day */ +#define ADIS16240_CLK_DATE 0x30 +/* Clock, year */ +#define ADIS16240_CLK_YEAR 0x32 +/* Wake-up setting, hour and minute */ +#define ADIS16240_WAKE_TIME 0x34 +/* Wake-up setting, month and day */ +#define ADIS16240_WAKE_DATE 0x36 +/* Alarm 1 amplitude threshold */ +#define ADIS16240_ALM_MAG1 0x38 +/* Alarm 2 amplitude threshold */ +#define ADIS16240_ALM_MAG2 0x3A +/* Alarm control */ +#define ADIS16240_ALM_CTRL 0x3C +/* Capture, external trigger control */ +#define ADIS16240_XTRIG_CTRL 0x3E +/* Capture, address pointer */ +#define ADIS16240_CAPT_PNTR 0x40 +/* Capture, configuration and control */ +#define ADIS16240_CAPT_CTRL 0x42 +/* General-purpose digital input/output control */ +#define ADIS16240_GPIO_CTRL 0x44 +/* Miscellaneous control */ +#define ADIS16240_MSC_CTRL 0x46 +/* Internal sample period (rate) control */ +#define ADIS16240_SMPL_PRD 0x48 +/* System command */ +#define ADIS16240_GLOB_CMD 0x4A + +/* MSC_CTRL */ +/* Enables sum-of-squares output (XYZPEAK_OUT) */ +#define ADIS16240_MSC_CTRL_XYZPEAK_OUT_EN BIT(15) +/* Enables peak tracking output (XPEAK_OUT, YPEAK_OUT, and ZPEAK_OUT) */ +#define ADIS16240_MSC_CTRL_X_Y_ZPEAK_OUT_EN BIT(14) +/* Self-test enable: 1 = apply electrostatic force, 0 = disabled */ +#define ADIS16240_MSC_CTRL_SELF_TEST_EN BIT(8) +/* Data-ready enable: 1 = enabled, 0 = disabled */ +#define ADIS16240_MSC_CTRL_DATA_RDY_EN BIT(2) +/* Data-ready polarity: 1 = active high, 0 = active low */ +#define ADIS16240_MSC_CTRL_ACTIVE_HIGH BIT(1) +/* Data-ready line selection: 1 = DIO2, 0 = DIO1 */ +#define ADIS16240_MSC_CTRL_DATA_RDY_DIO2 BIT(0) + +/* DIAG_STAT */ +/* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */ +#define ADIS16240_DIAG_STAT_ALARM2 BIT(9) +/* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */ +#define ADIS16240_DIAG_STAT_ALARM1 BIT(8) +/* Capture buffer full: 1 = capture buffer is full */ +#define ADIS16240_DIAG_STAT_CPT_BUF_FUL BIT(7) +/* Flash test, checksum flag: 1 = mismatch, 0 = match */ +#define ADIS16240_DIAG_STAT_CHKSUM BIT(6) +/* Power-on, self-test flag: 1 = failure, 0 = pass */ +#define ADIS16240_DIAG_STAT_PWRON_FAIL_BIT 5 +/* Power-on self-test: 1 = in-progress, 0 = complete */ +#define ADIS16240_DIAG_STAT_PWRON_BUSY BIT(4) +/* SPI communications failure */ +#define ADIS16240_DIAG_STAT_SPI_FAIL_BIT 3 +/* Flash update failure */ +#define ADIS16240_DIAG_STAT_FLASH_UPT_BIT 2 +/* Power supply above 3.625 V */ +#define ADIS16240_DIAG_STAT_POWER_HIGH_BIT 1 + /* Power supply below 3.15 V */ +#define ADIS16240_DIAG_STAT_POWER_LOW_BIT 0 + +/* GLOB_CMD */ +#define ADIS16240_GLOB_CMD_RESUME BIT(8) +#define ADIS16240_GLOB_CMD_SW_RESET BIT(7) +#define ADIS16240_GLOB_CMD_STANDBY BIT(2) + +#define ADIS16240_ERROR_ACTIVE BIT(14) + +/* At the moment triggers are only used for ring buffer + * filling. This may change! + */ + +#define ADIS16240_SCAN_ACC_X 0 +#define ADIS16240_SCAN_ACC_Y 1 +#define ADIS16240_SCAN_ACC_Z 2 +#define ADIS16240_SCAN_SUPPLY 3 +#define ADIS16240_SCAN_AUX_ADC 4 +#define ADIS16240_SCAN_TEMP 5 + +#endif /* SPI_ADIS16240_H_ */ diff --git a/drivers/staging/iio/accel/adis16240_core.c b/drivers/staging/iio/accel/adis16240_core.c new file mode 100644 index 000000000..cb074e864 --- /dev/null +++ b/drivers/staging/iio/accel/adis16240_core.c @@ -0,0 +1,301 @@ +/* + * ADIS16240 Programmable Impact Sensor and Recorder driver + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "adis16240.h" + +static ssize_t adis16240_spi_read_signed(struct device *dev, + struct device_attribute *attr, + char *buf, + unsigned bits) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct adis *st = iio_priv(indio_dev); + int ret; + s16 val = 0; + unsigned shift = 16 - bits; + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + + ret = adis_read_reg_16(st, + this_attr->address, (u16 *)&val); + if (ret) + return ret; + + if (val & ADIS16240_ERROR_ACTIVE) + adis_check_status(st); + + val = (s16)(val << shift) >> shift; + return sprintf(buf, "%d\n", val); +} + +static ssize_t adis16240_read_12bit_signed(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t ret; + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + ret = adis16240_spi_read_signed(dev, attr, buf, 12); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static IIO_DEVICE_ATTR(in_accel_xyz_squared_peak_raw, S_IRUGO, + adis16240_read_12bit_signed, NULL, + ADIS16240_XYZPEAK_OUT); + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("4096"); + +static const u8 adis16240_addresses[][2] = { + [ADIS16240_SCAN_ACC_X] = { ADIS16240_XACCL_OFF, ADIS16240_XPEAK_OUT }, + [ADIS16240_SCAN_ACC_Y] = { ADIS16240_YACCL_OFF, ADIS16240_YPEAK_OUT }, + [ADIS16240_SCAN_ACC_Z] = { ADIS16240_ZACCL_OFF, ADIS16240_ZPEAK_OUT }, +}; + +static int adis16240_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, + long mask) +{ + struct adis *st = iio_priv(indio_dev); + int ret; + int bits; + u8 addr; + s16 val16; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return adis_single_conversion(indio_dev, chan, + ADIS16240_ERROR_ACTIVE, val); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->channel == 0) { + *val = 4; + *val2 = 880000; /* 4.88 mV */ + return IIO_VAL_INT_PLUS_MICRO; + } + return -EINVAL; + case IIO_TEMP: + *val = 244; /* 0.244 C */ + *val2 = 0; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_ACCEL: + *val = 0; + *val2 = IIO_G_TO_M_S_2(51400); /* 51.4 mg */ + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + break; + case IIO_CHAN_INFO_PEAK_SCALE: + *val = 0; + *val2 = IIO_G_TO_M_S_2(51400); /* 51.4 mg */ + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OFFSET: + *val = 25000 / 244 - 0x133; /* 25 C = 0x133 */ + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + bits = 10; + mutex_lock(&indio_dev->mlock); + addr = adis16240_addresses[chan->scan_index][0]; + ret = adis_read_reg_16(st, addr, &val16); + if (ret) { + mutex_unlock(&indio_dev->mlock); + return ret; + } + val16 &= (1 << bits) - 1; + val16 = (s16)(val16 << (16 - bits)) >> (16 - bits); + *val = val16; + mutex_unlock(&indio_dev->mlock); + return IIO_VAL_INT; + case IIO_CHAN_INFO_PEAK: + bits = 10; + mutex_lock(&indio_dev->mlock); + addr = adis16240_addresses[chan->scan_index][1]; + ret = adis_read_reg_16(st, addr, &val16); + if (ret) { + mutex_unlock(&indio_dev->mlock); + return ret; + } + val16 &= (1 << bits) - 1; + val16 = (s16)(val16 << (16 - bits)) >> (16 - bits); + *val = val16; + mutex_unlock(&indio_dev->mlock); + return IIO_VAL_INT; + } + return -EINVAL; +} + +static int adis16240_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct adis *st = iio_priv(indio_dev); + int bits = 10; + s16 val16; + u8 addr; + + switch (mask) { + case IIO_CHAN_INFO_CALIBBIAS: + val16 = val & ((1 << bits) - 1); + addr = adis16240_addresses[chan->scan_index][0]; + return adis_write_reg_16(st, addr, val16); + } + return -EINVAL; +} + +static const struct iio_chan_spec adis16240_channels[] = { + ADIS_SUPPLY_CHAN(ADIS16240_SUPPLY_OUT, ADIS16240_SCAN_SUPPLY, 0, 10), + ADIS_AUX_ADC_CHAN(ADIS16240_AUX_ADC, ADIS16240_SCAN_AUX_ADC, 0, 10), + ADIS_ACCEL_CHAN(X, ADIS16240_XACCL_OUT, ADIS16240_SCAN_ACC_X, + BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), + 0, 10), + ADIS_ACCEL_CHAN(Y, ADIS16240_YACCL_OUT, ADIS16240_SCAN_ACC_Y, + BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), + 0, 10), + ADIS_ACCEL_CHAN(Z, ADIS16240_ZACCL_OUT, ADIS16240_SCAN_ACC_Z, + BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_PEAK), + 0, 10), + ADIS_TEMP_CHAN(ADIS16240_TEMP_OUT, ADIS16240_SCAN_TEMP, 0, 10), + IIO_CHAN_SOFT_TIMESTAMP(6) +}; + +static struct attribute *adis16240_attributes[] = { + &iio_dev_attr_in_accel_xyz_squared_peak_raw.dev_attr.attr, + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group adis16240_attribute_group = { + .attrs = adis16240_attributes, +}; + +static const struct iio_info adis16240_info = { + .attrs = &adis16240_attribute_group, + .read_raw = &adis16240_read_raw, + .write_raw = &adis16240_write_raw, + .update_scan_mode = adis_update_scan_mode, + .driver_module = THIS_MODULE, +}; + +static const char * const adis16240_status_error_msgs[] = { + [ADIS16240_DIAG_STAT_PWRON_FAIL_BIT] = "Power on, self-test failed", + [ADIS16240_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure", + [ADIS16240_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed", + [ADIS16240_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V", + [ADIS16240_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 2.225V", +}; + +static const struct adis_data adis16240_data = { + .write_delay = 35, + .read_delay = 35, + .msc_ctrl_reg = ADIS16240_MSC_CTRL, + .glob_cmd_reg = ADIS16240_GLOB_CMD, + .diag_stat_reg = ADIS16240_DIAG_STAT, + + .self_test_mask = ADIS16240_MSC_CTRL_SELF_TEST_EN, + .startup_delay = ADIS16240_STARTUP_DELAY, + + .status_error_msgs = adis16240_status_error_msgs, + .status_error_mask = BIT(ADIS16240_DIAG_STAT_PWRON_FAIL_BIT) | + BIT(ADIS16240_DIAG_STAT_SPI_FAIL_BIT) | + BIT(ADIS16240_DIAG_STAT_FLASH_UPT_BIT) | + BIT(ADIS16240_DIAG_STAT_POWER_HIGH_BIT) | + BIT(ADIS16240_DIAG_STAT_POWER_LOW_BIT), +}; + +static int adis16240_probe(struct spi_device *spi) +{ + int ret; + struct adis *st; + struct iio_dev *indio_dev; + + /* setup the industrialio driver allocated elements */ + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + st = iio_priv(indio_dev); + /* this is only used for removal purposes */ + spi_set_drvdata(spi, indio_dev); + + indio_dev->name = spi->dev.driver->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->info = &adis16240_info; + indio_dev->channels = adis16240_channels; + indio_dev->num_channels = ARRAY_SIZE(adis16240_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = adis_init(st, indio_dev, spi, &adis16240_data); + if (ret) + return ret; + ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL); + if (ret) + return ret; + + /* Get the device into a sane initial state */ + ret = adis_initial_startup(st); + if (ret) + goto error_cleanup_buffer_trigger; + ret = iio_device_register(indio_dev); + if (ret) + goto error_cleanup_buffer_trigger; + return 0; + +error_cleanup_buffer_trigger: + adis_cleanup_buffer_and_trigger(st, indio_dev); + return ret; +} + +static int adis16240_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct adis *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + adis_cleanup_buffer_and_trigger(st, indio_dev); + + return 0; +} + +static struct spi_driver adis16240_driver = { + .driver = { + .name = "adis16240", + .owner = THIS_MODULE, + }, + .probe = adis16240_probe, + .remove = adis16240_remove, +}; +module_spi_driver(adis16240_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices Programmable Impact Sensor and Recorder"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:adis16240"); diff --git a/drivers/staging/iio/accel/lis3l02dq.h b/drivers/staging/iio/accel/lis3l02dq.h new file mode 100644 index 000000000..3f24c629b --- /dev/null +++ b/drivers/staging/iio/accel/lis3l02dq.h @@ -0,0 +1,212 @@ +/* + * LISL02DQ.h -- support STMicroelectronics LISD02DQ + * 3d 2g Linear Accelerometers via SPI + * + * Copyright (c) 2007 Jonathan Cameron + * + * Loosely based upon tle62x0.c + * + * 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. + */ + +#ifndef SPI_LIS3L02DQ_H_ +#define SPI_LIS3L02DQ_H_ +#define LIS3L02DQ_READ_REG(a) ((a) | 0x80) +#define LIS3L02DQ_WRITE_REG(a) a + +/* Calibration parameters */ +#define LIS3L02DQ_REG_OFFSET_X_ADDR 0x16 +#define LIS3L02DQ_REG_OFFSET_Y_ADDR 0x17 +#define LIS3L02DQ_REG_OFFSET_Z_ADDR 0x18 + +#define LIS3L02DQ_REG_GAIN_X_ADDR 0x19 +#define LIS3L02DQ_REG_GAIN_Y_ADDR 0x1A +#define LIS3L02DQ_REG_GAIN_Z_ADDR 0x1B + +/* Control Register (1 of 2) */ +#define LIS3L02DQ_REG_CTRL_1_ADDR 0x20 +/* Power ctrl - either bit set corresponds to on*/ +#define LIS3L02DQ_REG_CTRL_1_PD_ON 0xC0 + +/* Decimation Factor */ +#define LIS3L02DQ_DEC_MASK 0x30 +#define LIS3L02DQ_REG_CTRL_1_DF_128 0x00 +#define LIS3L02DQ_REG_CTRL_1_DF_64 0x10 +#define LIS3L02DQ_REG_CTRL_1_DF_32 0x20 +#define LIS3L02DQ_REG_CTRL_1_DF_8 (0x10 | 0x20) + +/* Self Test Enable */ +#define LIS3L02DQ_REG_CTRL_1_SELF_TEST_ON 0x08 + +/* Axes enable ctrls */ +#define LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE 0x04 +#define LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE 0x02 +#define LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE 0x01 + +/* Control Register (2 of 2) */ +#define LIS3L02DQ_REG_CTRL_2_ADDR 0x21 + +/* Block Data Update only after MSB and LSB read */ +#define LIS3L02DQ_REG_CTRL_2_BLOCK_UPDATE 0x40 + +/* Set to big endian output */ +#define LIS3L02DQ_REG_CTRL_2_BIG_ENDIAN 0x20 + +/* Reboot memory content */ +#define LIS3L02DQ_REG_CTRL_2_REBOOT_MEMORY 0x10 + +/* Interrupt Enable - applies data ready to the RDY pad */ +#define LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT 0x08 + +/* Enable Data Ready Generation - relationship with previous unclear in docs */ +#define LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION 0x04 + +/* SPI 3 wire mode */ +#define LIS3L02DQ_REG_CTRL_2_THREE_WIRE_SPI_MODE 0x02 + +/* Data alignment, default is 12 bit right justified + * - option for 16 bit left justified */ +#define LIS3L02DQ_REG_CTRL_2_DATA_ALIGNMENT_16_BIT_LEFT_JUSTIFIED 0x01 + +/* Interrupt related stuff */ +#define LIS3L02DQ_REG_WAKE_UP_CFG_ADDR 0x23 + +/* Switch from or combination of conditions to and */ +#define LIS3L02DQ_REG_WAKE_UP_CFG_BOOLEAN_AND 0x80 + +/* Latch interrupt request, + * if on ack must be given by reading the ack register */ +#define LIS3L02DQ_REG_WAKE_UP_CFG_LATCH_SRC 0x40 + +/* Z Interrupt on High (above threshold) */ +#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_HIGH 0x20 +/* Z Interrupt on Low */ +#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_LOW 0x10 +/* Y Interrupt on High */ +#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_HIGH 0x08 +/* Y Interrupt on Low */ +#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Y_LOW 0x04 +/* X Interrupt on High */ +#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_HIGH 0x02 +/* X Interrupt on Low */ +#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_X_LOW 0x01 + +/* Register that gives description of what caused interrupt + * - latched if set in CFG_ADDRES */ +#define LIS3L02DQ_REG_WAKE_UP_SRC_ADDR 0x24 +/* top bit ignored */ +/* Interrupt Active */ +#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_ACTIVATED 0x40 +/* Interupts that have been triggered */ +#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_HIGH 0x20 +#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_LOW 0x10 +#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_HIGH 0x08 +#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_LOW 0x04 +#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_HIGH 0x02 +#define LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_LOW 0x01 + +#define LIS3L02DQ_REG_WAKE_UP_ACK_ADDR 0x25 + +/* Status register */ +#define LIS3L02DQ_REG_STATUS_ADDR 0x27 +/* XYZ axis data overrun - first is all overrun? */ +#define LIS3L02DQ_REG_STATUS_XYZ_OVERRUN 0x80 +#define LIS3L02DQ_REG_STATUS_Z_OVERRUN 0x40 +#define LIS3L02DQ_REG_STATUS_Y_OVERRUN 0x20 +#define LIS3L02DQ_REG_STATUS_X_OVERRUN 0x10 +/* XYZ new data available - first is all 3 available? */ +#define LIS3L02DQ_REG_STATUS_XYZ_NEW_DATA 0x08 +#define LIS3L02DQ_REG_STATUS_Z_NEW_DATA 0x04 +#define LIS3L02DQ_REG_STATUS_Y_NEW_DATA 0x02 +#define LIS3L02DQ_REG_STATUS_X_NEW_DATA 0x01 + +/* The accelerometer readings - low and high bytes. + * Form of high byte dependent on justification set in ctrl reg */ +#define LIS3L02DQ_REG_OUT_X_L_ADDR 0x28 +#define LIS3L02DQ_REG_OUT_X_H_ADDR 0x29 +#define LIS3L02DQ_REG_OUT_Y_L_ADDR 0x2A +#define LIS3L02DQ_REG_OUT_Y_H_ADDR 0x2B +#define LIS3L02DQ_REG_OUT_Z_L_ADDR 0x2C +#define LIS3L02DQ_REG_OUT_Z_H_ADDR 0x2D + +/* Threshold values for all axes and both above and below thresholds + * - i.e. there is only one value */ +#define LIS3L02DQ_REG_THS_L_ADDR 0x2E +#define LIS3L02DQ_REG_THS_H_ADDR 0x2F + +#define LIS3L02DQ_DEFAULT_CTRL1 (LIS3L02DQ_REG_CTRL_1_PD_ON \ + | LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE \ + | LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE \ + | LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE \ + | LIS3L02DQ_REG_CTRL_1_DF_128) + +#define LIS3L02DQ_DEFAULT_CTRL2 0 + +#define LIS3L02DQ_MAX_TX 12 +#define LIS3L02DQ_MAX_RX 12 +/** + * struct lis3l02dq_state - device instance specific data + * @us: actual spi_device + * @trig: data ready trigger registered with iio + * @buf_lock: mutex to protect tx and rx + * @tx: transmit buffer + * @rx: receive buffer + **/ +struct lis3l02dq_state { + struct spi_device *us; + struct iio_trigger *trig; + struct mutex buf_lock; + int gpio; + bool trigger_on; + + u8 tx[LIS3L02DQ_MAX_RX] ____cacheline_aligned; + u8 rx[LIS3L02DQ_MAX_RX] ____cacheline_aligned; +}; + +int lis3l02dq_spi_read_reg_8(struct iio_dev *indio_dev, + u8 reg_address, + u8 *val); + +int lis3l02dq_spi_write_reg_8(struct iio_dev *indio_dev, + u8 reg_address, + u8 val); + +int lis3l02dq_disable_all_events(struct iio_dev *indio_dev); + +#ifdef CONFIG_IIO_BUFFER +/* At the moment triggers are only used for buffer + * filling. This may change! + */ +void lis3l02dq_remove_trigger(struct iio_dev *indio_dev); +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); + +irqreturn_t lis3l02dq_data_rdy_trig_poll(int irq, void *private); +#define lis3l02dq_th lis3l02dq_data_rdy_trig_poll + +#else /* CONFIG_IIO_BUFFER */ +#define lis3l02dq_th lis3l02dq_nobuffer + +static inline void lis3l02dq_remove_trigger(struct iio_dev *indio_dev) +{ +} + +static inline int lis3l02dq_probe_trigger(struct iio_dev *indio_dev) +{ + return 0; +} + +static int lis3l02dq_configure_buffer(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline void lis3l02dq_unconfigure_buffer(struct iio_dev *indio_dev) +{ +} +#endif /* CONFIG_IIO_BUFFER */ +#endif /* SPI_LIS3L02DQ_H_ */ diff --git a/drivers/staging/iio/accel/lis3l02dq_core.c b/drivers/staging/iio/accel/lis3l02dq_core.c new file mode 100644 index 000000000..ebcab56c8 --- /dev/null +++ b/drivers/staging/iio/accel/lis3l02dq_core.c @@ -0,0 +1,813 @@ +/* + * lis3l02dq.c support STMicroelectronics LISD02DQ + * 3d 2g Linear Accelerometers via SPI + * + * Copyright (c) 2007 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. + * + * Settings: + * 16 bit left justified mode used. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "lis3l02dq.h" + +/* At the moment the spi framework doesn't allow global setting of cs_change. + * It's in the likely to be added comment at the top of spi.h. + * This means that use cannot be made of spi_write etc. + */ +/* direct copy of the irq_default_primary_handler */ +#ifndef CONFIG_IIO_BUFFER +static irqreturn_t lis3l02dq_nobuffer(int irq, void *private) +{ + return IRQ_WAKE_THREAD; +} +#endif + +/** + * lis3l02dq_spi_read_reg_8() - read single byte from a single register + * @indio_dev: iio_dev for this actual device + * @reg_address: the address of the register to be read + * @val: pass back the resulting value + **/ +int lis3l02dq_spi_read_reg_8(struct iio_dev *indio_dev, + u8 reg_address, u8 *val) +{ + struct lis3l02dq_state *st = iio_priv(indio_dev); + int ret; + struct spi_transfer xfer = { + .tx_buf = st->tx, + .rx_buf = st->rx, + .bits_per_word = 8, + .len = 2, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = LIS3L02DQ_READ_REG(reg_address); + st->tx[1] = 0; + + ret = spi_sync_transfer(st->us, &xfer, 1); + *val = st->rx[1]; + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * lis3l02dq_spi_write_reg_8() - write single byte to a register + * @indio_dev: iio_dev for this device + * @reg_address: the address of the register to be written + * @val: the value to write + **/ +int lis3l02dq_spi_write_reg_8(struct iio_dev *indio_dev, + u8 reg_address, + u8 val) +{ + int ret; + struct lis3l02dq_state *st = iio_priv(indio_dev); + + mutex_lock(&st->buf_lock); + st->tx[0] = LIS3L02DQ_WRITE_REG(reg_address); + st->tx[1] = val; + ret = spi_write(st->us, st->tx, 2); + mutex_unlock(&st->buf_lock); + + return ret; +} + +/** + * lisl302dq_spi_write_reg_s16() - write 2 bytes to a pair of registers + * @indio_dev: iio_dev for this device + * @lower_reg_address: the address of the lower of the two registers. + * Second register is assumed to have address one greater. + * @value: value to be written + **/ +static int lis3l02dq_spi_write_reg_s16(struct iio_dev *indio_dev, + u8 lower_reg_address, + s16 value) +{ + int ret; + struct lis3l02dq_state *st = iio_priv(indio_dev); + struct spi_transfer xfers[] = { { + .tx_buf = st->tx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + }, { + .tx_buf = st->tx + 2, + .bits_per_word = 8, + .len = 2, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = LIS3L02DQ_WRITE_REG(lower_reg_address); + st->tx[1] = value & 0xFF; + st->tx[2] = LIS3L02DQ_WRITE_REG(lower_reg_address + 1); + st->tx[3] = (value >> 8) & 0xFF; + + ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers)); + mutex_unlock(&st->buf_lock); + + return ret; +} + +static int lis3l02dq_read_reg_s16(struct iio_dev *indio_dev, + u8 lower_reg_address, + int *val) +{ + struct lis3l02dq_state *st = iio_priv(indio_dev); + int ret; + s16 tempval; + struct spi_transfer xfers[] = { { + .tx_buf = st->tx, + .rx_buf = st->rx, + .bits_per_word = 8, + .len = 2, + .cs_change = 1, + }, { + .tx_buf = st->tx + 2, + .rx_buf = st->rx + 2, + .bits_per_word = 8, + .len = 2, + }, + }; + + mutex_lock(&st->buf_lock); + st->tx[0] = LIS3L02DQ_READ_REG(lower_reg_address); + st->tx[1] = 0; + st->tx[2] = LIS3L02DQ_READ_REG(lower_reg_address + 1); + st->tx[3] = 0; + + ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers)); + if (ret) { + dev_err(&st->us->dev, "problem when reading 16 bit register"); + goto error_ret; + } + tempval = (s16)(st->rx[1]) | ((s16)(st->rx[3]) << 8); + + *val = tempval; +error_ret: + mutex_unlock(&st->buf_lock); + return ret; +} + +enum lis3l02dq_rm_ind { + LIS3L02DQ_ACCEL, + LIS3L02DQ_GAIN, + LIS3L02DQ_BIAS, +}; + +static u8 lis3l02dq_axis_map[3][3] = { + [LIS3L02DQ_ACCEL] = { LIS3L02DQ_REG_OUT_X_L_ADDR, + LIS3L02DQ_REG_OUT_Y_L_ADDR, + LIS3L02DQ_REG_OUT_Z_L_ADDR }, + [LIS3L02DQ_GAIN] = { LIS3L02DQ_REG_GAIN_X_ADDR, + LIS3L02DQ_REG_GAIN_Y_ADDR, + LIS3L02DQ_REG_GAIN_Z_ADDR }, + [LIS3L02DQ_BIAS] = { LIS3L02DQ_REG_OFFSET_X_ADDR, + LIS3L02DQ_REG_OFFSET_Y_ADDR, + LIS3L02DQ_REG_OFFSET_Z_ADDR } +}; + +static int lis3l02dq_read_thresh(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + int ret; + + ret = lis3l02dq_read_reg_s16(indio_dev, LIS3L02DQ_REG_THS_L_ADDR, val); + if (ret) + return ret; + return IIO_VAL_INT; +} + +static int lis3l02dq_write_thresh(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + u16 value = val; + + return lis3l02dq_spi_write_reg_s16(indio_dev, + LIS3L02DQ_REG_THS_L_ADDR, + value); +} + +static int lis3l02dq_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + int ret = -EINVAL, reg; + u8 uval; + s8 sval; + + switch (mask) { + case IIO_CHAN_INFO_CALIBBIAS: + if (val > 255 || val < -256) + return -EINVAL; + sval = val; + reg = lis3l02dq_axis_map[LIS3L02DQ_BIAS][chan->address]; + ret = lis3l02dq_spi_write_reg_8(indio_dev, reg, sval); + break; + case IIO_CHAN_INFO_CALIBSCALE: + if (val & ~0xFF) + return -EINVAL; + uval = val; + reg = lis3l02dq_axis_map[LIS3L02DQ_GAIN][chan->address]; + ret = lis3l02dq_spi_write_reg_8(indio_dev, reg, uval); + break; + } + return ret; +} + +static int lis3l02dq_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + u8 utemp; + s8 stemp; + ssize_t ret = 0; + u8 reg; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + /* Take the iio_dev status lock */ + mutex_lock(&indio_dev->mlock); + if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) { + ret = -EBUSY; + } else { + reg = lis3l02dq_axis_map + [LIS3L02DQ_ACCEL][chan->address]; + ret = lis3l02dq_read_reg_s16(indio_dev, reg, val); + } + mutex_unlock(&indio_dev->mlock); + if (ret < 0) + goto error_ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = 9580; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_CALIBSCALE: + reg = lis3l02dq_axis_map[LIS3L02DQ_GAIN][chan->address]; + ret = lis3l02dq_spi_read_reg_8(indio_dev, reg, &utemp); + if (ret) + goto error_ret; + /* to match with what previous code does */ + *val = utemp; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_CALIBBIAS: + reg = lis3l02dq_axis_map[LIS3L02DQ_BIAS][chan->address]; + ret = lis3l02dq_spi_read_reg_8(indio_dev, reg, (u8 *)&stemp); + /* to match with what previous code does */ + *val = stemp; + return IIO_VAL_INT; + } +error_ret: + return ret; +} + +static ssize_t lis3l02dq_read_frequency(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + int ret, len = 0; + s8 t; + + ret = lis3l02dq_spi_read_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_1_ADDR, + (u8 *)&t); + if (ret) + return ret; + t &= LIS3L02DQ_DEC_MASK; + switch (t) { + case LIS3L02DQ_REG_CTRL_1_DF_128: + len = sprintf(buf, "280\n"); + break; + case LIS3L02DQ_REG_CTRL_1_DF_64: + len = sprintf(buf, "560\n"); + break; + case LIS3L02DQ_REG_CTRL_1_DF_32: + len = sprintf(buf, "1120\n"); + break; + case LIS3L02DQ_REG_CTRL_1_DF_8: + len = sprintf(buf, "4480\n"); + break; + } + return len; +} + +static ssize_t lis3l02dq_write_frequency(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + unsigned long val; + int ret; + u8 t; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + mutex_lock(&indio_dev->mlock); + ret = lis3l02dq_spi_read_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_1_ADDR, + &t); + if (ret) + goto error_ret_mutex; + /* Wipe the bits clean */ + t &= ~LIS3L02DQ_DEC_MASK; + switch (val) { + case 280: + t |= LIS3L02DQ_REG_CTRL_1_DF_128; + break; + case 560: + t |= LIS3L02DQ_REG_CTRL_1_DF_64; + break; + case 1120: + t |= LIS3L02DQ_REG_CTRL_1_DF_32; + break; + case 4480: + t |= LIS3L02DQ_REG_CTRL_1_DF_8; + break; + default: + ret = -EINVAL; + goto error_ret_mutex; + } + + ret = lis3l02dq_spi_write_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_1_ADDR, + t); + +error_ret_mutex: + mutex_unlock(&indio_dev->mlock); + + return ret ? ret : len; +} + +static int lis3l02dq_initial_setup(struct iio_dev *indio_dev) +{ + struct lis3l02dq_state *st = iio_priv(indio_dev); + int ret; + u8 val, valtest; + + st->us->mode = SPI_MODE_3; + + spi_setup(st->us); + + val = LIS3L02DQ_DEFAULT_CTRL1; + /* Write suitable defaults to ctrl1 */ + ret = lis3l02dq_spi_write_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_1_ADDR, + val); + if (ret) { + dev_err(&st->us->dev, "problem with setup control register 1"); + goto err_ret; + } + /* Repeat as sometimes doesn't work first time? */ + ret = lis3l02dq_spi_write_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_1_ADDR, + val); + if (ret) { + dev_err(&st->us->dev, "problem with setup control register 1"); + goto err_ret; + } + + /* Read back to check this has worked acts as loose test of correct + * chip */ + ret = lis3l02dq_spi_read_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_1_ADDR, + &valtest); + if (ret || (valtest != val)) { + dev_err(&indio_dev->dev, + "device not playing ball %d %d\n", valtest, val); + ret = -EINVAL; + goto err_ret; + } + + val = LIS3L02DQ_DEFAULT_CTRL2; + ret = lis3l02dq_spi_write_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_2_ADDR, + val); + if (ret) { + dev_err(&st->us->dev, "problem with setup control register 2"); + goto err_ret; + } + + val = LIS3L02DQ_REG_WAKE_UP_CFG_LATCH_SRC; + ret = lis3l02dq_spi_write_reg_8(indio_dev, + LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, + val); + if (ret) + dev_err(&st->us->dev, "problem with interrupt cfg register"); +err_ret: + + return ret; +} + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, + lis3l02dq_read_frequency, + lis3l02dq_write_frequency); + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("280 560 1120 4480"); + +static irqreturn_t lis3l02dq_event_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + u8 t; + + s64 timestamp = iio_get_time_ns(); + + lis3l02dq_spi_read_reg_8(indio_dev, + LIS3L02DQ_REG_WAKE_UP_SRC_ADDR, + &t); + + if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_HIGH) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_Z, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + timestamp); + + if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Z_LOW) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_Z, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + timestamp); + + if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_HIGH) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_Y, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + timestamp); + + if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_Y_LOW) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_Y, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + timestamp); + + if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_HIGH) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_X, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + timestamp); + + if (t & LIS3L02DQ_REG_WAKE_UP_SRC_INTERRUPT_X_LOW) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_X, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + timestamp); + + /* Ack and allow for new interrupts */ + lis3l02dq_spi_read_reg_8(indio_dev, + LIS3L02DQ_REG_WAKE_UP_ACK_ADDR, + &t); + + return IRQ_HANDLED; +} + +static const struct iio_event_spec lis3l02dq_event[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE), + } +}; + +#define LIS3L02DQ_CHAN(index, mod) \ + { \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel2 = mod, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_CALIBSCALE) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .address = index, \ + .scan_index = index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 12, \ + .storagebits = 16, \ + }, \ + .event_spec = lis3l02dq_event, \ + .num_event_specs = ARRAY_SIZE(lis3l02dq_event), \ + } + +static const struct iio_chan_spec lis3l02dq_channels[] = { + LIS3L02DQ_CHAN(0, IIO_MOD_X), + LIS3L02DQ_CHAN(1, IIO_MOD_Y), + LIS3L02DQ_CHAN(2, IIO_MOD_Z), + IIO_CHAN_SOFT_TIMESTAMP(3) +}; + +static int lis3l02dq_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + u8 val; + int ret; + u8 mask = (1 << (chan->channel2*2 + (dir == IIO_EV_DIR_RISING))); + + ret = lis3l02dq_spi_read_reg_8(indio_dev, + LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, + &val); + if (ret < 0) + return ret; + + return !!(val & mask); +} + +int lis3l02dq_disable_all_events(struct iio_dev *indio_dev) +{ + int ret; + u8 control, val; + + ret = lis3l02dq_spi_read_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_2_ADDR, + &control); + + control &= ~LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT; + ret = lis3l02dq_spi_write_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_2_ADDR, + control); + if (ret) + goto error_ret; + /* Also for consistency clear the mask */ + ret = lis3l02dq_spi_read_reg_8(indio_dev, + LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, + &val); + if (ret) + goto error_ret; + val &= ~0x3f; + + ret = lis3l02dq_spi_write_reg_8(indio_dev, + LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, + val); + if (ret) + goto error_ret; + + ret = control; +error_ret: + return ret; +} + +static int lis3l02dq_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + int ret = 0; + u8 val, control; + u8 currentlyset; + bool changed = false; + u8 mask = (1 << (chan->channel2*2 + (dir == IIO_EV_DIR_RISING))); + + mutex_lock(&indio_dev->mlock); + /* read current control */ + ret = lis3l02dq_spi_read_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_2_ADDR, + &control); + if (ret) + goto error_ret; + ret = lis3l02dq_spi_read_reg_8(indio_dev, + LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, + &val); + if (ret < 0) + goto error_ret; + currentlyset = val & mask; + + if (!currentlyset && state) { + changed = true; + val |= mask; + } else if (currentlyset && !state) { + changed = true; + val &= ~mask; + } + + if (changed) { + ret = lis3l02dq_spi_write_reg_8(indio_dev, + LIS3L02DQ_REG_WAKE_UP_CFG_ADDR, + val); + if (ret) + goto error_ret; + control = val & 0x3f ? + (control | LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT) : + (control & ~LIS3L02DQ_REG_CTRL_2_ENABLE_INTERRUPT); + ret = lis3l02dq_spi_write_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_2_ADDR, + control); + if (ret) + goto error_ret; + } + +error_ret: + mutex_unlock(&indio_dev->mlock); + return ret; +} + +static struct attribute *lis3l02dq_attributes[] = { + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group lis3l02dq_attribute_group = { + .attrs = lis3l02dq_attributes, +}; + +static const struct iio_info lis3l02dq_info = { + .read_raw = &lis3l02dq_read_raw, + .write_raw = &lis3l02dq_write_raw, + .read_event_value = &lis3l02dq_read_thresh, + .write_event_value = &lis3l02dq_write_thresh, + .write_event_config = &lis3l02dq_write_event_config, + .read_event_config = &lis3l02dq_read_event_config, + .driver_module = THIS_MODULE, + .attrs = &lis3l02dq_attribute_group, +}; + +static int lis3l02dq_probe(struct spi_device *spi) +{ + int ret; + struct lis3l02dq_state *st; + struct iio_dev *indio_dev; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + st = iio_priv(indio_dev); + /* this is only used for removal purposes */ + spi_set_drvdata(spi, indio_dev); + + st->us = spi; + st->gpio = of_get_gpio(spi->dev.of_node, 0); + mutex_init(&st->buf_lock); + indio_dev->name = spi->dev.driver->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->info = &lis3l02dq_info; + indio_dev->channels = lis3l02dq_channels; + indio_dev->num_channels = ARRAY_SIZE(lis3l02dq_channels); + + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = lis3l02dq_configure_buffer(indio_dev); + if (ret) + return ret; + + if (spi->irq) { + ret = request_threaded_irq(st->us->irq, + &lis3l02dq_th, + &lis3l02dq_event_handler, + IRQF_TRIGGER_RISING, + "lis3l02dq", + indio_dev); + if (ret) + goto error_unreg_buffer_funcs; + + ret = lis3l02dq_probe_trigger(indio_dev); + if (ret) + goto error_free_interrupt; + } + + /* Get the device into a sane initial state */ + ret = lis3l02dq_initial_setup(indio_dev); + if (ret) + goto error_remove_trigger; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_remove_trigger; + + return 0; + +error_remove_trigger: + if (spi->irq) + lis3l02dq_remove_trigger(indio_dev); +error_free_interrupt: + if (spi->irq) + free_irq(st->us->irq, indio_dev); +error_unreg_buffer_funcs: + lis3l02dq_unconfigure_buffer(indio_dev); + return ret; +} + +/* Power down the device */ +static int lis3l02dq_stop_device(struct iio_dev *indio_dev) +{ + int ret; + struct lis3l02dq_state *st = iio_priv(indio_dev); + u8 val = 0; + + mutex_lock(&indio_dev->mlock); + ret = lis3l02dq_spi_write_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_1_ADDR, + val); + if (ret) { + dev_err(&st->us->dev, "problem with turning device off: ctrl1"); + goto err_ret; + } + + ret = lis3l02dq_spi_write_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_2_ADDR, + val); + if (ret) + dev_err(&st->us->dev, "problem with turning device off: ctrl2"); +err_ret: + mutex_unlock(&indio_dev->mlock); + return ret; +} + +/* fixme, confirm ordering in this function */ +static int lis3l02dq_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct lis3l02dq_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + lis3l02dq_disable_all_events(indio_dev); + lis3l02dq_stop_device(indio_dev); + + if (spi->irq) + free_irq(st->us->irq, indio_dev); + + lis3l02dq_remove_trigger(indio_dev); + lis3l02dq_unconfigure_buffer(indio_dev); + + return 0; +} + +static struct spi_driver lis3l02dq_driver = { + .driver = { + .name = "lis3l02dq", + .owner = THIS_MODULE, + }, + .probe = lis3l02dq_probe, + .remove = lis3l02dq_remove, +}; +module_spi_driver(lis3l02dq_driver); + +MODULE_AUTHOR("Jonathan Cameron "); +MODULE_DESCRIPTION("ST LIS3L02DQ Accelerometer SPI driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:lis3l02dq"); diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c new file mode 100644 index 000000000..b892f2cf5 --- /dev/null +++ b/drivers/staging/iio/accel/lis3l02dq_ring.c @@ -0,0 +1,426 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "lis3l02dq.h" + +/** + * combine_8_to_16() utility function to munge two u8s into u16 + **/ +static inline u16 combine_8_to_16(u8 lower, u8 upper) +{ + u16 _lower = lower; + u16 _upper = upper; + + return _lower | (_upper << 8); +} + +/** + * lis3l02dq_data_rdy_trig_poll() the event handler for the data rdy trig + **/ +irqreturn_t lis3l02dq_data_rdy_trig_poll(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct lis3l02dq_state *st = iio_priv(indio_dev); + + if (st->trigger_on) { + iio_trigger_poll(st->trig); + return IRQ_HANDLED; + } + + return IRQ_WAKE_THREAD; +} + +static const u8 read_all_tx_array[] = { + LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_X_L_ADDR), 0, + LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_X_H_ADDR), 0, + LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Y_L_ADDR), 0, + LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Y_H_ADDR), 0, + LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Z_L_ADDR), 0, + LIS3L02DQ_READ_REG(LIS3L02DQ_REG_OUT_Z_H_ADDR), 0, +}; + +/** + * lis3l02dq_read_all() Reads all channels currently selected + * @indio_dev: IIO device state + * @rx_array: (dma capable) receive array, must be at least + * 4*number of channels + **/ +static int lis3l02dq_read_all(struct iio_dev *indio_dev, u8 *rx_array) +{ + struct lis3l02dq_state *st = iio_priv(indio_dev); + struct spi_transfer *xfers; + struct spi_message msg; + int ret, i, j = 0; + + xfers = kcalloc(bitmap_weight(indio_dev->active_scan_mask, + indio_dev->masklength) * 2, + sizeof(*xfers), GFP_KERNEL); + if (!xfers) + return -ENOMEM; + + mutex_lock(&st->buf_lock); + + for (i = 0; i < ARRAY_SIZE(read_all_tx_array)/4; i++) + if (test_bit(i, indio_dev->active_scan_mask)) { + /* lower byte */ + xfers[j].tx_buf = st->tx + 2*j; + st->tx[2*j] = read_all_tx_array[i*4]; + st->tx[2*j + 1] = 0; + if (rx_array) + xfers[j].rx_buf = rx_array + j*2; + xfers[j].bits_per_word = 8; + xfers[j].len = 2; + xfers[j].cs_change = 1; + j++; + + /* upper byte */ + xfers[j].tx_buf = st->tx + 2*j; + st->tx[2*j] = read_all_tx_array[i*4 + 2]; + st->tx[2*j + 1] = 0; + if (rx_array) + xfers[j].rx_buf = rx_array + j*2; + xfers[j].bits_per_word = 8; + xfers[j].len = 2; + xfers[j].cs_change = 1; + j++; + } + + /* After these are transmitted, the rx_buff should have + * values in alternate bytes + */ + spi_message_init(&msg); + for (j = 0; j < bitmap_weight(indio_dev->active_scan_mask, + indio_dev->masklength) * 2; j++) + spi_message_add_tail(&xfers[j], &msg); + + ret = spi_sync(st->us, &msg); + mutex_unlock(&st->buf_lock); + kfree(xfers); + + return ret; +} + +static int lis3l02dq_get_buffer_element(struct iio_dev *indio_dev, + u8 *buf) +{ + int ret, i; + u8 *rx_array; + s16 *data = (s16 *)buf; + int scan_count = bitmap_weight(indio_dev->active_scan_mask, + indio_dev->masklength); + + rx_array = kcalloc(4, scan_count, GFP_KERNEL); + if (!rx_array) + return -ENOMEM; + ret = lis3l02dq_read_all(indio_dev, rx_array); + if (ret < 0) { + kfree(rx_array); + return ret; + } + for (i = 0; i < scan_count; i++) + data[i] = combine_8_to_16(rx_array[i*4+1], + rx_array[i*4+3]); + kfree(rx_array); + + return i*sizeof(data[0]); +} + +static irqreturn_t lis3l02dq_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + int len = 0; + char *data; + + data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); + if (!data) + goto done; + + if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength)) + len = lis3l02dq_get_buffer_element(indio_dev, data); + + iio_push_to_buffers_with_timestamp(indio_dev, data, pf->timestamp); + + kfree(data); +done: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +/* Caller responsible for locking as necessary. */ +static int +__lis3l02dq_write_data_ready_config(struct iio_dev *indio_dev, bool state) +{ + int ret; + u8 valold; + bool currentlyset; + struct lis3l02dq_state *st = iio_priv(indio_dev); + + /* Get the current event mask register */ + ret = lis3l02dq_spi_read_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_2_ADDR, + &valold); + if (ret) + goto error_ret; + /* Find out if data ready is already on */ + currentlyset + = valold & LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION; + + /* Disable requested */ + if (!state && currentlyset) { + /* Disable the data ready signal */ + valold &= ~LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION; + + /* The double write is to overcome a hardware bug? */ + ret = lis3l02dq_spi_write_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_2_ADDR, + valold); + if (ret) + goto error_ret; + ret = lis3l02dq_spi_write_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_2_ADDR, + valold); + if (ret) + goto error_ret; + st->trigger_on = false; + /* Enable requested */ + } else if (state && !currentlyset) { + /* If not set, enable requested + * first disable all events */ + ret = lis3l02dq_disable_all_events(indio_dev); + if (ret < 0) + goto error_ret; + + valold = ret | + LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION; + + st->trigger_on = true; + ret = lis3l02dq_spi_write_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_2_ADDR, + valold); + if (ret) + goto error_ret; + } + + return 0; +error_ret: + return ret; +} + +/** + * lis3l02dq_data_rdy_trigger_set_state() set datardy interrupt state + * + * If disabling the interrupt also does a final read to ensure it is clear. + * This is only important in some cases where the scan enable elements are + * switched before the buffer is reenabled. + **/ +static int lis3l02dq_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + int ret = 0; + u8 t; + + __lis3l02dq_write_data_ready_config(indio_dev, state); + if (!state) { + /* + * A possible quirk with the handler is currently worked around + * by ensuring outstanding read events are cleared. + */ + ret = lis3l02dq_read_all(indio_dev, NULL); + } + lis3l02dq_spi_read_reg_8(indio_dev, + LIS3L02DQ_REG_WAKE_UP_SRC_ADDR, + &t); + return ret; +} + +/** + * lis3l02dq_trig_try_reen() try reenabling irq for data rdy trigger + * @trig: the datardy trigger + */ +static int lis3l02dq_trig_try_reen(struct iio_trigger *trig) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct lis3l02dq_state *st = iio_priv(indio_dev); + int i; + + /* If gpio still high (or high again) + * In theory possible we will need to do this several times */ + for (i = 0; i < 5; i++) + if (gpio_get_value(st->gpio)) + lis3l02dq_read_all(indio_dev, NULL); + else + break; + if (i == 5) + pr_info("Failed to clear the interrupt for lis3l02dq\n"); + + /* irq reenabled so success! */ + return 0; +} + +static const struct iio_trigger_ops lis3l02dq_trigger_ops = { + .owner = THIS_MODULE, + .set_trigger_state = &lis3l02dq_data_rdy_trigger_set_state, + .try_reenable = &lis3l02dq_trig_try_reen, +}; + +int lis3l02dq_probe_trigger(struct iio_dev *indio_dev) +{ + int ret; + struct lis3l02dq_state *st = iio_priv(indio_dev); + + st->trig = iio_trigger_alloc("lis3l02dq-dev%d", indio_dev->id); + if (!st->trig) { + ret = -ENOMEM; + goto error_ret; + } + + st->trig->dev.parent = &st->us->dev; + st->trig->ops = &lis3l02dq_trigger_ops; + iio_trigger_set_drvdata(st->trig, indio_dev); + ret = iio_trigger_register(st->trig); + if (ret) + goto error_free_trig; + + return 0; + +error_free_trig: + iio_trigger_free(st->trig); +error_ret: + return ret; +} + +void lis3l02dq_remove_trigger(struct iio_dev *indio_dev) +{ + struct lis3l02dq_state *st = iio_priv(indio_dev); + + iio_trigger_unregister(st->trig); + iio_trigger_free(st->trig); +} + +void lis3l02dq_unconfigure_buffer(struct iio_dev *indio_dev) +{ + iio_dealloc_pollfunc(indio_dev->pollfunc); + iio_kfifo_free(indio_dev->buffer); +} + +static int lis3l02dq_buffer_postenable(struct iio_dev *indio_dev) +{ + /* Disable unwanted channels otherwise the interrupt will not clear */ + u8 t; + int ret; + bool oneenabled = false; + + ret = lis3l02dq_spi_read_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_1_ADDR, + &t); + if (ret) + goto error_ret; + + if (test_bit(0, indio_dev->active_scan_mask)) { + t |= LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE; + oneenabled = true; + } else { + t &= ~LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE; + } + if (test_bit(1, indio_dev->active_scan_mask)) { + t |= LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE; + oneenabled = true; + } else { + t &= ~LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE; + } + if (test_bit(2, indio_dev->active_scan_mask)) { + t |= LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE; + oneenabled = true; + } else { + t &= ~LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE; + } + if (!oneenabled) /* what happens in this case is unknown */ + return -EINVAL; + ret = lis3l02dq_spi_write_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_1_ADDR, + t); + if (ret) + goto error_ret; + + return iio_triggered_buffer_postenable(indio_dev); +error_ret: + return ret; +} + +/* Turn all channels on again */ +static int lis3l02dq_buffer_predisable(struct iio_dev *indio_dev) +{ + u8 t; + int ret; + + ret = iio_triggered_buffer_predisable(indio_dev); + if (ret) + goto error_ret; + + ret = lis3l02dq_spi_read_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_1_ADDR, + &t); + if (ret) + goto error_ret; + t |= LIS3L02DQ_REG_CTRL_1_AXES_X_ENABLE | + LIS3L02DQ_REG_CTRL_1_AXES_Y_ENABLE | + LIS3L02DQ_REG_CTRL_1_AXES_Z_ENABLE; + + ret = lis3l02dq_spi_write_reg_8(indio_dev, + LIS3L02DQ_REG_CTRL_1_ADDR, + t); + +error_ret: + return ret; +} + +static const struct iio_buffer_setup_ops lis3l02dq_buffer_setup_ops = { + .postenable = &lis3l02dq_buffer_postenable, + .predisable = &lis3l02dq_buffer_predisable, +}; + +int lis3l02dq_configure_buffer(struct iio_dev *indio_dev) +{ + int ret; + struct iio_buffer *buffer; + + buffer = iio_kfifo_allocate(); + if (!buffer) + return -ENOMEM; + + iio_device_attach_buffer(indio_dev, buffer); + + buffer->scan_timestamp = true; + indio_dev->setup_ops = &lis3l02dq_buffer_setup_ops; + + /* Functions are NULL as we set handler below */ + indio_dev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time, + &lis3l02dq_trigger_handler, + 0, + indio_dev, + "lis3l02dq_consumer%d", + indio_dev->id); + + if (!indio_dev->pollfunc) { + ret = -ENOMEM; + goto error_iio_sw_rb_free; + } + + indio_dev->modes |= INDIO_BUFFER_TRIGGERED; + return 0; + +error_iio_sw_rb_free: + iio_kfifo_free(indio_dev->buffer); + return ret; +} diff --git a/drivers/staging/iio/accel/sca3000.h b/drivers/staging/iio/accel/sca3000.h new file mode 100644 index 000000000..9c8a9587d --- /dev/null +++ b/drivers/staging/iio/accel/sca3000.h @@ -0,0 +1,278 @@ +/* + * sca3000.c -- support VTI sca3000 series accelerometers + * via SPI + * + * Copyright (c) 2007 Jonathan Cameron + * + * Partly based upon tle62x0.c + * + * 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. + * + * Initial mode is direct measurement. + * + * Untested things + * + * Temperature reading (the e05 I'm testing with doesn't have a sensor) + * + * Free fall detection mode - supported but untested as I'm not droping my + * dubious wire rig far enough to test it. + * + * Unsupported as yet + * + * Time stamping of data from ring. Various ideas on how to do this but none + * are remotely simple. Suggestions welcome. + * + * Individual enabling disabling of channels going into ring buffer + * + * Overflow handling (this is signaled for all but 8 bit ring buffer mode.) + * + * Motion detector using AND combinations of signals. + * + * Note: Be very careful about not touching an register bytes marked + * as reserved on the data sheet. They really mean it as changing convents of + * some will cause the device to lock up. + * + * Known issues - on rare occasions the interrupts lock up. Not sure why as yet. + * Can probably alleviate this by reading the interrupt register on start, but + * that is really just brushing the problem under the carpet. + */ +#ifndef _SCA3000 +#define _SCA3000 + +#define SCA3000_WRITE_REG(a) (((a) << 2) | 0x02) +#define SCA3000_READ_REG(a) ((a) << 2) + +#define SCA3000_REG_ADDR_REVID 0x00 +#define SCA3000_REVID_MAJOR_MASK 0xf0 +#define SCA3000_REVID_MINOR_MASK 0x0f + +#define SCA3000_REG_ADDR_STATUS 0x02 +#define SCA3000_LOCKED 0x20 +#define SCA3000_EEPROM_CS_ERROR 0x02 +#define SCA3000_SPI_FRAME_ERROR 0x01 + +/* All reads done using register decrement so no need to directly access LSBs */ +#define SCA3000_REG_ADDR_X_MSB 0x05 +#define SCA3000_REG_ADDR_Y_MSB 0x07 +#define SCA3000_REG_ADDR_Z_MSB 0x09 + +#define SCA3000_REG_ADDR_RING_OUT 0x0f + +/* Temp read untested - the e05 doesn't have the sensor */ +#define SCA3000_REG_ADDR_TEMP_MSB 0x13 + +#define SCA3000_REG_ADDR_MODE 0x14 +#define SCA3000_MODE_PROT_MASK 0x28 + +#define SCA3000_RING_BUF_ENABLE 0x80 +#define SCA3000_RING_BUF_8BIT 0x40 +/* + * Free fall detection triggers an interrupt if the acceleration + * is below a threshold for equivalent of 25cm drop + */ +#define SCA3000_FREE_FALL_DETECT 0x10 +#define SCA3000_MEAS_MODE_NORMAL 0x00 +#define SCA3000_MEAS_MODE_OP_1 0x01 +#define SCA3000_MEAS_MODE_OP_2 0x02 + +/* + * In motion detection mode the accelerations are band pass filtered + * (approx 1 - 25Hz) and then a programmable threshold used to trigger + * and interrupt. + */ +#define SCA3000_MEAS_MODE_MOT_DET 0x03 + +#define SCA3000_REG_ADDR_BUF_COUNT 0x15 + +#define SCA3000_REG_ADDR_INT_STATUS 0x16 + +#define SCA3000_INT_STATUS_THREE_QUARTERS 0x80 +#define SCA3000_INT_STATUS_HALF 0x40 + +#define SCA3000_INT_STATUS_FREE_FALL 0x08 +#define SCA3000_INT_STATUS_Y_TRIGGER 0x04 +#define SCA3000_INT_STATUS_X_TRIGGER 0x02 +#define SCA3000_INT_STATUS_Z_TRIGGER 0x01 + +/* Used to allow access to multiplexed registers */ +#define SCA3000_REG_ADDR_CTRL_SEL 0x18 +/* Only available for SCA3000-D03 and SCA3000-D01 */ +#define SCA3000_REG_CTRL_SEL_I2C_DISABLE 0x01 +#define SCA3000_REG_CTRL_SEL_MD_CTRL 0x02 +#define SCA3000_REG_CTRL_SEL_MD_Y_TH 0x03 +#define SCA3000_REG_CTRL_SEL_MD_X_TH 0x04 +#define SCA3000_REG_CTRL_SEL_MD_Z_TH 0x05 +/* + * BE VERY CAREFUL WITH THIS, IF 3 BITS ARE NOT SET the device + * will not function + */ +#define SCA3000_REG_CTRL_SEL_OUT_CTRL 0x0B +#define SCA3000_OUT_CTRL_PROT_MASK 0xE0 +#define SCA3000_OUT_CTRL_BUF_X_EN 0x10 +#define SCA3000_OUT_CTRL_BUF_Y_EN 0x08 +#define SCA3000_OUT_CTRL_BUF_Z_EN 0x04 +#define SCA3000_OUT_CTRL_BUF_DIV_4 0x02 +#define SCA3000_OUT_CTRL_BUF_DIV_2 0x01 + +/* + * Control which motion detector interrupts are on. + * For now only OR combinations are supported. + */ +#define SCA3000_MD_CTRL_PROT_MASK 0xC0 +#define SCA3000_MD_CTRL_OR_Y 0x01 +#define SCA3000_MD_CTRL_OR_X 0x02 +#define SCA3000_MD_CTRL_OR_Z 0x04 +/* Currently unsupported */ +#define SCA3000_MD_CTRL_AND_Y 0x08 +#define SCA3000_MD_CTRL_AND_X 0x10 +#define SAC3000_MD_CTRL_AND_Z 0x20 + +/* + * Some control registers of complex access methods requiring this register to + * be used to remove a lock. + */ +#define SCA3000_REG_ADDR_UNLOCK 0x1e + +#define SCA3000_REG_ADDR_INT_MASK 0x21 +#define SCA3000_INT_MASK_PROT_MASK 0x1C + +#define SCA3000_INT_MASK_RING_THREE_QUARTER 0x80 +#define SCA3000_INT_MASK_RING_HALF 0x40 + +#define SCA3000_INT_MASK_ALL_INTS 0x02 +#define SCA3000_INT_MASK_ACTIVE_HIGH 0x01 +#define SCA3000_INT_MASK_ACTIVE_LOW 0x00 + +/* Values of multiplexed registers (write to ctrl_data after select) */ +#define SCA3000_REG_ADDR_CTRL_DATA 0x22 + +/* + * Measurement modes available on some sca3000 series chips. Code assumes others + * may become available in the future. + * + * Bypass - Bypass the low-pass filter in the signal channel so as to increase + * signal bandwidth. + * + * Narrow - Narrow low-pass filtering of the signal channel and half output + * data rate by decimation. + * + * Wide - Widen low-pass filtering of signal channel to increase bandwidth + */ +#define SCA3000_OP_MODE_BYPASS 0x01 +#define SCA3000_OP_MODE_NARROW 0x02 +#define SCA3000_OP_MODE_WIDE 0x04 +#define SCA3000_MAX_TX 6 +#define SCA3000_MAX_RX 2 + +/** + * struct sca3000_state - device instance state information + * @us: the associated spi device + * @info: chip variant information + * @interrupt_handler_ws: event interrupt handler for all events + * @last_timestamp: the timestamp of the last event + * @mo_det_use_count: reference counter for the motion detection unit + * @lock: lock used to protect elements of sca3000_state + * and the underlying device state. + * @bpse: number of bits per scan element + * @tx: dma-able transmit buffer + * @rx: dma-able receive buffer + **/ +struct sca3000_state { + struct spi_device *us; + const struct sca3000_chip_info *info; + struct work_struct interrupt_handler_ws; + s64 last_timestamp; + int mo_det_use_count; + struct mutex lock; + int bpse; + /* Can these share a cacheline ? */ + u8 rx[2] ____cacheline_aligned; + u8 tx[6] ____cacheline_aligned; +}; + +/** + * struct sca3000_chip_info - model dependent parameters + * @scale: scale * 10^-6 + * @temp_output: some devices have temperature sensors. + * @measurement_mode_freq: normal mode sampling frequency + * @option_mode_1: first optional mode. Not all models have one + * @option_mode_1_freq: option mode 1 sampling frequency + * @option_mode_2: second optional mode. Not all chips have one + * @option_mode_2_freq: option mode 2 sampling frequency + * + * This structure is used to hold information about the functionality of a given + * sca3000 variant. + **/ +struct sca3000_chip_info { + unsigned int scale; + bool temp_output; + int measurement_mode_freq; + int option_mode_1; + int option_mode_1_freq; + int option_mode_2; + int option_mode_2_freq; + int mot_det_mult_xz[6]; + int mot_det_mult_y[7]; +}; + +int sca3000_read_data_short(struct sca3000_state *st, + u8 reg_address_high, + int len); + +/** + * sca3000_write_reg() write a single register + * @address: address of register on chip + * @val: value to be written to register + * + * The main lock must be held. + **/ +int sca3000_write_reg(struct sca3000_state *st, u8 address, u8 val); + +#ifdef CONFIG_IIO_BUFFER +/** + * sca3000_register_ring_funcs() setup the ring state change functions + **/ +void sca3000_register_ring_funcs(struct iio_dev *indio_dev); + +/** + * sca3000_configure_ring() - allocate and configure ring buffer + * @indio_dev: iio-core device whose ring is to be configured + * + * The hardware ring buffer needs far fewer ring buffer functions than + * a software one as a lot of things are handled automatically. + * This function also tells the iio core that our device supports a + * hardware ring buffer mode. + **/ +int sca3000_configure_ring(struct iio_dev *indio_dev); + +/** + * sca3000_unconfigure_ring() - deallocate the ring buffer + * @indio_dev: iio-core device whose ring we are freeing + **/ +void sca3000_unconfigure_ring(struct iio_dev *indio_dev); + +/** + * sca3000_ring_int_process() handles ring related event pushing and escalation + * @val: the event code + **/ +void sca3000_ring_int_process(u8 val, struct iio_buffer *ring); + +#else +static inline void sca3000_register_ring_funcs(struct iio_dev *indio_dev) +{ +} + +static inline +int sca3000_register_ring_access_and_init(struct iio_dev *indio_dev) +{ + return 0; +} + +static inline void sca3000_ring_int_process(u8 val, void *ring) +{ +} + +#endif +#endif /* _SCA3000 */ diff --git a/drivers/staging/iio/accel/sca3000_core.c b/drivers/staging/iio/accel/sca3000_core.c new file mode 100644 index 000000000..b614f272b --- /dev/null +++ b/drivers/staging/iio/accel/sca3000_core.c @@ -0,0 +1,1209 @@ +/* + * sca3000_core.c -- support VTI sca3000 series accelerometers via SPI + * + * 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. + * + * Copyright (c) 2009 Jonathan Cameron + * + * See industrialio/accels/sca3000.h for comments. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sca3000.h" + +enum sca3000_variant { + d01, + e02, + e04, + e05, +}; + +/* + * Note where option modes are not defined, the chip simply does not + * support any. + * Other chips in the sca3000 series use i2c and are not included here. + * + * Some of these devices are only listed in the family data sheet and + * do not actually appear to be available. + */ +static const struct sca3000_chip_info sca3000_spi_chip_info_tbl[] = { + [d01] = { + .scale = 7357, + .temp_output = true, + .measurement_mode_freq = 250, + .option_mode_1 = SCA3000_OP_MODE_BYPASS, + .option_mode_1_freq = 250, + .mot_det_mult_xz = {50, 100, 200, 350, 650, 1300}, + .mot_det_mult_y = {50, 100, 150, 250, 450, 850, 1750}, + }, + [e02] = { + .scale = 9810, + .measurement_mode_freq = 125, + .option_mode_1 = SCA3000_OP_MODE_NARROW, + .option_mode_1_freq = 63, + .mot_det_mult_xz = {100, 150, 300, 550, 1050, 2050}, + .mot_det_mult_y = {50, 100, 200, 350, 700, 1350, 2700}, + }, + [e04] = { + .scale = 19620, + .measurement_mode_freq = 100, + .option_mode_1 = SCA3000_OP_MODE_NARROW, + .option_mode_1_freq = 50, + .option_mode_2 = SCA3000_OP_MODE_WIDE, + .option_mode_2_freq = 400, + .mot_det_mult_xz = {200, 300, 600, 1100, 2100, 4100}, + .mot_det_mult_y = {100, 200, 400, 7000, 1400, 2700, 54000}, + }, + [e05] = { + .scale = 61313, + .measurement_mode_freq = 200, + .option_mode_1 = SCA3000_OP_MODE_NARROW, + .option_mode_1_freq = 50, + .option_mode_2 = SCA3000_OP_MODE_WIDE, + .option_mode_2_freq = 400, + .mot_det_mult_xz = {600, 900, 1700, 3200, 6100, 11900}, + .mot_det_mult_y = {300, 600, 1200, 2000, 4100, 7800, 15600}, + }, +}; + +int sca3000_write_reg(struct sca3000_state *st, u8 address, u8 val) +{ + st->tx[0] = SCA3000_WRITE_REG(address); + st->tx[1] = val; + return spi_write(st->us, st->tx, 2); +} + +int sca3000_read_data_short(struct sca3000_state *st, + uint8_t reg_address_high, + int len) +{ + struct spi_transfer xfer[2] = { + { + .len = 1, + .tx_buf = st->tx, + }, { + .len = len, + .rx_buf = st->rx, + } + }; + st->tx[0] = SCA3000_READ_REG(reg_address_high); + + return spi_sync_transfer(st->us, xfer, ARRAY_SIZE(xfer)); +} + +/** + * sca3000_reg_lock_on() test if the ctrl register lock is on + * + * Lock must be held. + **/ +static int sca3000_reg_lock_on(struct sca3000_state *st) +{ + int ret; + + ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_STATUS, 1); + if (ret < 0) + return ret; + + return !(st->rx[0] & SCA3000_LOCKED); +} + +/** + * __sca3000_unlock_reg_lock() unlock the control registers + * + * Note the device does not appear to support doing this in a single transfer. + * This should only ever be used as part of ctrl reg read. + * Lock must be held before calling this + **/ +static int __sca3000_unlock_reg_lock(struct sca3000_state *st) +{ + struct spi_transfer xfer[3] = { + { + .len = 2, + .cs_change = 1, + .tx_buf = st->tx, + }, { + .len = 2, + .cs_change = 1, + .tx_buf = st->tx + 2, + }, { + .len = 2, + .tx_buf = st->tx + 4, + }, + }; + st->tx[0] = SCA3000_WRITE_REG(SCA3000_REG_ADDR_UNLOCK); + st->tx[1] = 0x00; + st->tx[2] = SCA3000_WRITE_REG(SCA3000_REG_ADDR_UNLOCK); + st->tx[3] = 0x50; + st->tx[4] = SCA3000_WRITE_REG(SCA3000_REG_ADDR_UNLOCK); + st->tx[5] = 0xA0; + + return spi_sync_transfer(st->us, xfer, ARRAY_SIZE(xfer)); +} + +/** + * sca3000_write_ctrl_reg() write to a lock protect ctrl register + * @sel: selects which registers we wish to write to + * @val: the value to be written + * + * Certain control registers are protected against overwriting by the lock + * register and use a shared write address. This function allows writing of + * these registers. + * Lock must be held. + **/ +static int sca3000_write_ctrl_reg(struct sca3000_state *st, + uint8_t sel, + uint8_t val) +{ + + int ret; + + ret = sca3000_reg_lock_on(st); + if (ret < 0) + goto error_ret; + if (ret) { + ret = __sca3000_unlock_reg_lock(st); + if (ret) + goto error_ret; + } + + /* Set the control select register */ + ret = sca3000_write_reg(st, SCA3000_REG_ADDR_CTRL_SEL, sel); + if (ret) + goto error_ret; + + /* Write the actual value into the register */ + ret = sca3000_write_reg(st, SCA3000_REG_ADDR_CTRL_DATA, val); + +error_ret: + return ret; +} + +/** + * sca3000_read_ctrl_reg() read from lock protected control register. + * + * Lock must be held. + **/ +static int sca3000_read_ctrl_reg(struct sca3000_state *st, + u8 ctrl_reg) +{ + int ret; + + ret = sca3000_reg_lock_on(st); + if (ret < 0) + goto error_ret; + if (ret) { + ret = __sca3000_unlock_reg_lock(st); + if (ret) + goto error_ret; + } + /* Set the control select register */ + ret = sca3000_write_reg(st, SCA3000_REG_ADDR_CTRL_SEL, ctrl_reg); + if (ret) + goto error_ret; + ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_CTRL_DATA, 1); + if (ret) + goto error_ret; + else + return st->rx[0]; +error_ret: + return ret; +} + +/** + * sca3000_show_rev() - sysfs interface to read the chip revision number + **/ +static ssize_t sca3000_show_rev(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int len = 0, ret; + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct sca3000_state *st = iio_priv(indio_dev); + + mutex_lock(&st->lock); + ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_REVID, 1); + if (ret < 0) + goto error_ret; + len += sprintf(buf + len, + "major=%d, minor=%d\n", + st->rx[0] & SCA3000_REVID_MAJOR_MASK, + st->rx[0] & SCA3000_REVID_MINOR_MASK); +error_ret: + mutex_unlock(&st->lock); + + return ret ? ret : len; +} + +/** + * sca3000_show_available_measurement_modes() display available modes + * + * This is all read from chip specific data in the driver. Not all + * of the sca3000 series support modes other than normal. + **/ +static ssize_t +sca3000_show_available_measurement_modes(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct sca3000_state *st = iio_priv(indio_dev); + int len = 0; + + len += sprintf(buf + len, "0 - normal mode"); + switch (st->info->option_mode_1) { + case SCA3000_OP_MODE_NARROW: + len += sprintf(buf + len, ", 1 - narrow mode"); + break; + case SCA3000_OP_MODE_BYPASS: + len += sprintf(buf + len, ", 1 - bypass mode"); + break; + } + switch (st->info->option_mode_2) { + case SCA3000_OP_MODE_WIDE: + len += sprintf(buf + len, ", 2 - wide mode"); + break; + } + /* always supported */ + len += sprintf(buf + len, " 3 - motion detection\n"); + + return len; +} + +/** + * sca3000_show_measurement_mode() sysfs read of current mode + **/ +static ssize_t +sca3000_show_measurement_mode(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct sca3000_state *st = iio_priv(indio_dev); + int len = 0, ret; + + mutex_lock(&st->lock); + ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1); + if (ret) + goto error_ret; + /* mask bottom 2 bits - only ones that are relevant */ + st->rx[0] &= 0x03; + switch (st->rx[0]) { + case SCA3000_MEAS_MODE_NORMAL: + len += sprintf(buf + len, "0 - normal mode\n"); + break; + case SCA3000_MEAS_MODE_MOT_DET: + len += sprintf(buf + len, "3 - motion detection\n"); + break; + case SCA3000_MEAS_MODE_OP_1: + switch (st->info->option_mode_1) { + case SCA3000_OP_MODE_NARROW: + len += sprintf(buf + len, "1 - narrow mode\n"); + break; + case SCA3000_OP_MODE_BYPASS: + len += sprintf(buf + len, "1 - bypass mode\n"); + break; + } + break; + case SCA3000_MEAS_MODE_OP_2: + switch (st->info->option_mode_2) { + case SCA3000_OP_MODE_WIDE: + len += sprintf(buf + len, "2 - wide mode\n"); + break; + } + break; + } + +error_ret: + mutex_unlock(&st->lock); + + return ret ? ret : len; +} + +/** + * sca3000_store_measurement_mode() set the current mode + **/ +static ssize_t +sca3000_store_measurement_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct sca3000_state *st = iio_priv(indio_dev); + int ret; + u8 mask = 0x03; + u8 val; + + mutex_lock(&st->lock); + ret = kstrtou8(buf, 10, &val); + if (ret) + goto error_ret; + if (val > 3) { + ret = -EINVAL; + goto error_ret; + } + ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1); + if (ret) + goto error_ret; + st->rx[0] &= ~mask; + st->rx[0] |= (val & mask); + ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE, st->rx[0]); + if (ret) + goto error_ret; + mutex_unlock(&st->lock); + + return len; + +error_ret: + mutex_unlock(&st->lock); + + return ret; +} + + +/* + * Not even vaguely standard attributes so defined here rather than + * in the relevant IIO core headers + */ +static IIO_DEVICE_ATTR(measurement_mode_available, S_IRUGO, + sca3000_show_available_measurement_modes, + NULL, 0); + +static IIO_DEVICE_ATTR(measurement_mode, S_IRUGO | S_IWUSR, + sca3000_show_measurement_mode, + sca3000_store_measurement_mode, + 0); + +/* More standard attributes */ + +static IIO_DEVICE_ATTR(revision, S_IRUGO, sca3000_show_rev, NULL, 0); + +static const struct iio_event_spec sca3000_event = { + .type = IIO_EV_TYPE_MAG, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), +}; + +#define SCA3000_CHAN(index, mod) \ + { \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel2 = mod, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\ + .address = index, \ + .scan_index = index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 11, \ + .storagebits = 16, \ + .shift = 5, \ + }, \ + .event_spec = &sca3000_event, \ + .num_event_specs = 1, \ + } + +static const struct iio_chan_spec sca3000_channels[] = { + SCA3000_CHAN(0, IIO_MOD_X), + SCA3000_CHAN(1, IIO_MOD_Y), + SCA3000_CHAN(2, IIO_MOD_Z), +}; + +static const struct iio_chan_spec sca3000_channels_with_temp[] = { + SCA3000_CHAN(0, IIO_MOD_X), + SCA3000_CHAN(1, IIO_MOD_Y), + SCA3000_CHAN(2, IIO_MOD_Z), + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + /* No buffer support */ + .scan_index = -1, + }, +}; + +static u8 sca3000_addresses[3][3] = { + [0] = {SCA3000_REG_ADDR_X_MSB, SCA3000_REG_CTRL_SEL_MD_X_TH, + SCA3000_MD_CTRL_OR_X}, + [1] = {SCA3000_REG_ADDR_Y_MSB, SCA3000_REG_CTRL_SEL_MD_Y_TH, + SCA3000_MD_CTRL_OR_Y}, + [2] = {SCA3000_REG_ADDR_Z_MSB, SCA3000_REG_CTRL_SEL_MD_Z_TH, + SCA3000_MD_CTRL_OR_Z}, +}; + +static int sca3000_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct sca3000_state *st = iio_priv(indio_dev); + int ret; + u8 address; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&st->lock); + if (chan->type == IIO_ACCEL) { + if (st->mo_det_use_count) { + mutex_unlock(&st->lock); + return -EBUSY; + } + address = sca3000_addresses[chan->address][0]; + ret = sca3000_read_data_short(st, address, 2); + if (ret < 0) { + mutex_unlock(&st->lock); + return ret; + } + *val = (be16_to_cpup((__be16 *)st->rx) >> 3) & 0x1FFF; + *val = ((*val) << (sizeof(*val)*8 - 13)) >> + (sizeof(*val)*8 - 13); + } else { + /* get the temperature when available */ + ret = sca3000_read_data_short(st, + SCA3000_REG_ADDR_TEMP_MSB, 2); + if (ret < 0) { + mutex_unlock(&st->lock); + return ret; + } + *val = ((st->rx[0] & 0x3F) << 3) | + ((st->rx[1] & 0xE0) >> 5); + } + mutex_unlock(&st->lock); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + if (chan->type == IIO_ACCEL) + *val2 = st->info->scale; + else /* temperature */ + *val2 = 555556; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OFFSET: + *val = -214; + *val2 = 600000; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +/** + * sca3000_read_av_freq() sysfs function to get available frequencies + * + * The later modes are only relevant to the ring buffer - and depend on current + * mode. Note that data sheet gives rather wide tolerances for these so integer + * division will give good enough answer and not all chips have them specified + * at all. + **/ +static ssize_t sca3000_read_av_freq(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct sca3000_state *st = iio_priv(indio_dev); + int len = 0, ret, val; + + mutex_lock(&st->lock); + ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1); + val = st->rx[0]; + mutex_unlock(&st->lock); + if (ret) + goto error_ret; + + switch (val & 0x03) { + case SCA3000_MEAS_MODE_NORMAL: + len += sprintf(buf + len, "%d %d %d\n", + st->info->measurement_mode_freq, + st->info->measurement_mode_freq/2, + st->info->measurement_mode_freq/4); + break; + case SCA3000_MEAS_MODE_OP_1: + len += sprintf(buf + len, "%d %d %d\n", + st->info->option_mode_1_freq, + st->info->option_mode_1_freq/2, + st->info->option_mode_1_freq/4); + break; + case SCA3000_MEAS_MODE_OP_2: + len += sprintf(buf + len, "%d %d %d\n", + st->info->option_mode_2_freq, + st->info->option_mode_2_freq/2, + st->info->option_mode_2_freq/4); + break; + } + return len; +error_ret: + return ret; +} +/** + * __sca3000_get_base_freq() obtain mode specific base frequency + * + * lock must be held + **/ +static inline int __sca3000_get_base_freq(struct sca3000_state *st, + const struct sca3000_chip_info *info, + int *base_freq) +{ + int ret; + + ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1); + if (ret) + goto error_ret; + switch (0x03 & st->rx[0]) { + case SCA3000_MEAS_MODE_NORMAL: + *base_freq = info->measurement_mode_freq; + break; + case SCA3000_MEAS_MODE_OP_1: + *base_freq = info->option_mode_1_freq; + break; + case SCA3000_MEAS_MODE_OP_2: + *base_freq = info->option_mode_2_freq; + break; + } +error_ret: + return ret; +} + +/** + * sca3000_read_frequency() sysfs interface to get the current frequency + **/ +static ssize_t sca3000_read_frequency(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct sca3000_state *st = iio_priv(indio_dev); + int ret, len = 0, base_freq = 0, val; + + mutex_lock(&st->lock); + ret = __sca3000_get_base_freq(st, st->info, &base_freq); + if (ret) + goto error_ret_mut; + ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL); + mutex_unlock(&st->lock); + if (ret) + goto error_ret; + val = ret; + if (base_freq > 0) + switch (val & 0x03) { + case 0x00: + case 0x03: + len = sprintf(buf, "%d\n", base_freq); + break; + case 0x01: + len = sprintf(buf, "%d\n", base_freq/2); + break; + case 0x02: + len = sprintf(buf, "%d\n", base_freq/4); + break; + } + + return len; +error_ret_mut: + mutex_unlock(&st->lock); +error_ret: + return ret; +} + +/** + * sca3000_set_frequency() sysfs interface to set the current frequency + **/ +static ssize_t sca3000_set_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 sca3000_state *st = iio_priv(indio_dev); + int ret, base_freq = 0; + int ctrlval; + int val; + + ret = kstrtoint(buf, 10, &val); + if (ret) + return ret; + + mutex_lock(&st->lock); + /* What mode are we in? */ + ret = __sca3000_get_base_freq(st, st->info, &base_freq); + if (ret) + goto error_free_lock; + + ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL); + if (ret < 0) + goto error_free_lock; + ctrlval = ret; + /* clear the bits */ + ctrlval &= ~0x03; + + if (val == base_freq/2) { + ctrlval |= SCA3000_OUT_CTRL_BUF_DIV_2; + } else if (val == base_freq/4) { + ctrlval |= SCA3000_OUT_CTRL_BUF_DIV_4; + } else if (val != base_freq) { + ret = -EINVAL; + goto error_free_lock; + } + ret = sca3000_write_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL, + ctrlval); +error_free_lock: + mutex_unlock(&st->lock); + + return ret ? ret : len; +} + +/* + * Should only really be registered if ring buffer support is compiled in. + * Does no harm however and doing it right would add a fair bit of complexity + */ +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(sca3000_read_av_freq); + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, + sca3000_read_frequency, + sca3000_set_frequency); + +/** + * sca3000_read_thresh() - query of a threshold + **/ +static int sca3000_read_thresh(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + int ret, i; + struct sca3000_state *st = iio_priv(indio_dev); + int num = chan->channel2; + + mutex_lock(&st->lock); + ret = sca3000_read_ctrl_reg(st, sca3000_addresses[num][1]); + mutex_unlock(&st->lock); + if (ret < 0) + return ret; + *val = 0; + if (num == 1) + for_each_set_bit(i, (unsigned long *)&ret, + ARRAY_SIZE(st->info->mot_det_mult_y)) + *val += st->info->mot_det_mult_y[i]; + else + for_each_set_bit(i, (unsigned long *)&ret, + ARRAY_SIZE(st->info->mot_det_mult_xz)) + *val += st->info->mot_det_mult_xz[i]; + + return IIO_VAL_INT; +} + +/** + * sca3000_write_thresh() control of threshold + **/ +static int sca3000_write_thresh(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct sca3000_state *st = iio_priv(indio_dev); + int num = chan->channel2; + int ret; + int i; + u8 nonlinear = 0; + + if (num == 1) { + i = ARRAY_SIZE(st->info->mot_det_mult_y); + while (i > 0) + if (val >= st->info->mot_det_mult_y[--i]) { + nonlinear |= (1 << i); + val -= st->info->mot_det_mult_y[i]; + } + } else { + i = ARRAY_SIZE(st->info->mot_det_mult_xz); + while (i > 0) + if (val >= st->info->mot_det_mult_xz[--i]) { + nonlinear |= (1 << i); + val -= st->info->mot_det_mult_xz[i]; + } + } + + mutex_lock(&st->lock); + ret = sca3000_write_ctrl_reg(st, sca3000_addresses[num][1], nonlinear); + mutex_unlock(&st->lock); + + return ret; +} + +static struct attribute *sca3000_attributes[] = { + &iio_dev_attr_revision.dev_attr.attr, + &iio_dev_attr_measurement_mode_available.dev_attr.attr, + &iio_dev_attr_measurement_mode.dev_attr.attr, + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_sampling_frequency.dev_attr.attr, + NULL, +}; + +static const struct attribute_group sca3000_attribute_group = { + .attrs = sca3000_attributes, +}; + +/** + * sca3000_event_handler() - handling ring and non ring events + * + * Ring related interrupt handler. Depending on event, push to + * the ring buffer event chrdev or the event one. + * + * This function is complicated by the fact that the devices can signify ring + * and non ring events via the same interrupt line and they can only + * be distinguished via a read of the relevant status register. + **/ +static irqreturn_t sca3000_event_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct sca3000_state *st = iio_priv(indio_dev); + int ret, val; + s64 last_timestamp = iio_get_time_ns(); + + /* + * Could lead if badly timed to an extra read of status reg, + * but ensures no interrupt is missed. + */ + mutex_lock(&st->lock); + ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_STATUS, 1); + val = st->rx[0]; + mutex_unlock(&st->lock); + if (ret) + goto done; + + sca3000_ring_int_process(val, indio_dev->buffer); + + if (val & SCA3000_INT_STATUS_FREE_FALL) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_X_AND_Y_AND_Z, + IIO_EV_TYPE_MAG, + IIO_EV_DIR_FALLING), + last_timestamp); + + if (val & SCA3000_INT_STATUS_Y_TRIGGER) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_Y, + IIO_EV_TYPE_MAG, + IIO_EV_DIR_RISING), + last_timestamp); + + if (val & SCA3000_INT_STATUS_X_TRIGGER) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_X, + IIO_EV_TYPE_MAG, + IIO_EV_DIR_RISING), + last_timestamp); + + if (val & SCA3000_INT_STATUS_Z_TRIGGER) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_Z, + IIO_EV_TYPE_MAG, + IIO_EV_DIR_RISING), + last_timestamp); + +done: + return IRQ_HANDLED; +} + +/** + * sca3000_read_event_config() what events are enabled + **/ +static int sca3000_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct sca3000_state *st = iio_priv(indio_dev); + int ret; + u8 protect_mask = 0x03; + int num = chan->channel2; + + /* read current value of mode register */ + mutex_lock(&st->lock); + ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1); + if (ret) + goto error_ret; + + if ((st->rx[0] & protect_mask) != SCA3000_MEAS_MODE_MOT_DET) + ret = 0; + else { + ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL); + if (ret < 0) + goto error_ret; + /* only supporting logical or's for now */ + ret = !!(ret & sca3000_addresses[num][2]); + } +error_ret: + mutex_unlock(&st->lock); + + return ret; +} +/** + * sca3000_query_free_fall_mode() is free fall mode enabled + **/ +static ssize_t sca3000_query_free_fall_mode(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct sca3000_state *st = iio_priv(indio_dev); + int val; + + mutex_lock(&st->lock); + ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1); + val = st->rx[0]; + mutex_unlock(&st->lock); + if (ret < 0) + return ret; + return sprintf(buf, "%d\n", !!(val & SCA3000_FREE_FALL_DETECT)); +} + +/** + * sca3000_set_free_fall_mode() simple on off control for free fall int + * + * In these chips the free fall detector should send an interrupt if + * the device falls more than 25cm. This has not been tested due + * to fragile wiring. + **/ +static ssize_t sca3000_set_free_fall_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct sca3000_state *st = iio_priv(indio_dev); + u8 val; + int ret; + u8 protect_mask = SCA3000_FREE_FALL_DETECT; + + mutex_lock(&st->lock); + ret = kstrtou8(buf, 10, &val); + if (ret) + goto error_ret; + + /* read current value of mode register */ + ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1); + if (ret) + goto error_ret; + + /* if off and should be on */ + if (val && !(st->rx[0] & protect_mask)) + ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE, + (st->rx[0] | SCA3000_FREE_FALL_DETECT)); + /* if on and should be off */ + else if (!val && (st->rx[0] & protect_mask)) + ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE, + (st->rx[0] & ~protect_mask)); +error_ret: + mutex_unlock(&st->lock); + + return ret ? ret : len; +} + +/** + * sca3000_write_event_config() simple on off control for motion detector + * + * This is a per axis control, but enabling any will result in the + * motion detector unit being enabled. + * N.B. enabling motion detector stops normal data acquisition. + * There is a complexity in knowing which mode to return to when + * this mode is disabled. Currently normal mode is assumed. + **/ +static int sca3000_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + struct sca3000_state *st = iio_priv(indio_dev); + int ret, ctrlval; + u8 protect_mask = 0x03; + int num = chan->channel2; + + mutex_lock(&st->lock); + /* + * First read the motion detector config to find out if + * this axis is on + */ + ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL); + if (ret < 0) + goto exit_point; + ctrlval = ret; + /* if off and should be on */ + if (state && !(ctrlval & sca3000_addresses[num][2])) { + ret = sca3000_write_ctrl_reg(st, + SCA3000_REG_CTRL_SEL_MD_CTRL, + ctrlval | + sca3000_addresses[num][2]); + if (ret) + goto exit_point; + st->mo_det_use_count++; + } else if (!state && (ctrlval & sca3000_addresses[num][2])) { + ret = sca3000_write_ctrl_reg(st, + SCA3000_REG_CTRL_SEL_MD_CTRL, + ctrlval & + ~(sca3000_addresses[num][2])); + if (ret) + goto exit_point; + st->mo_det_use_count--; + } + + /* read current value of mode register */ + ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1); + if (ret) + goto exit_point; + /* if off and should be on */ + if ((st->mo_det_use_count) + && ((st->rx[0] & protect_mask) != SCA3000_MEAS_MODE_MOT_DET)) + ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE, + (st->rx[0] & ~protect_mask) + | SCA3000_MEAS_MODE_MOT_DET); + /* if on and should be off */ + else if (!(st->mo_det_use_count) + && ((st->rx[0] & protect_mask) == SCA3000_MEAS_MODE_MOT_DET)) + ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE, + (st->rx[0] & ~protect_mask)); +exit_point: + mutex_unlock(&st->lock); + + return ret; +} + +/* Free fall detector related event attribute */ +static IIO_DEVICE_ATTR_NAMED(accel_xayaz_mag_falling_en, + in_accel_x&y&z_mag_falling_en, + S_IRUGO | S_IWUSR, + sca3000_query_free_fall_mode, + sca3000_set_free_fall_mode, + 0); + +static IIO_CONST_ATTR_NAMED(accel_xayaz_mag_falling_period, + in_accel_x&y&z_mag_falling_period, + "0.226"); + +static struct attribute *sca3000_event_attributes[] = { + &iio_dev_attr_accel_xayaz_mag_falling_en.dev_attr.attr, + &iio_const_attr_accel_xayaz_mag_falling_period.dev_attr.attr, + NULL, +}; + +static struct attribute_group sca3000_event_attribute_group = { + .attrs = sca3000_event_attributes, + .name = "events", +}; + +/** + * sca3000_clean_setup() get the device into a predictable state + * + * Devices use flash memory to store many of the register values + * and hence can come up in somewhat unpredictable states. + * Hence reset everything on driver load. + **/ +static int sca3000_clean_setup(struct sca3000_state *st) +{ + int ret; + + mutex_lock(&st->lock); + /* Ensure all interrupts have been acknowledged */ + ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_STATUS, 1); + if (ret) + goto error_ret; + + /* Turn off all motion detection channels */ + ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL); + if (ret < 0) + goto error_ret; + ret = sca3000_write_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL, + ret & SCA3000_MD_CTRL_PROT_MASK); + if (ret) + goto error_ret; + + /* Disable ring buffer */ + ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL); + ret = sca3000_write_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL, + (ret & SCA3000_OUT_CTRL_PROT_MASK) + | SCA3000_OUT_CTRL_BUF_X_EN + | SCA3000_OUT_CTRL_BUF_Y_EN + | SCA3000_OUT_CTRL_BUF_Z_EN + | SCA3000_OUT_CTRL_BUF_DIV_4); + if (ret) + goto error_ret; + /* Enable interrupts, relevant to mode and set up as active low */ + ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_MASK, 1); + if (ret) + goto error_ret; + ret = sca3000_write_reg(st, + SCA3000_REG_ADDR_INT_MASK, + (ret & SCA3000_INT_MASK_PROT_MASK) + | SCA3000_INT_MASK_ACTIVE_LOW); + if (ret) + goto error_ret; + /* + * Select normal measurement mode, free fall off, ring off + * Ring in 12 bit mode - it is fine to overwrite reserved bits 3,5 + * as that occurs in one of the example on the datasheet + */ + ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1); + if (ret) + goto error_ret; + ret = sca3000_write_reg(st, SCA3000_REG_ADDR_MODE, + (st->rx[0] & SCA3000_MODE_PROT_MASK)); + st->bpse = 11; + +error_ret: + mutex_unlock(&st->lock); + return ret; +} + +static const struct iio_info sca3000_info = { + .attrs = &sca3000_attribute_group, + .read_raw = &sca3000_read_raw, + .event_attrs = &sca3000_event_attribute_group, + .read_event_value = &sca3000_read_thresh, + .write_event_value = &sca3000_write_thresh, + .read_event_config = &sca3000_read_event_config, + .write_event_config = &sca3000_write_event_config, + .driver_module = THIS_MODULE, +}; + +static int sca3000_probe(struct spi_device *spi) +{ + int ret; + struct sca3000_state *st; + struct iio_dev *indio_dev; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + st->us = spi; + mutex_init(&st->lock); + st->info = &sca3000_spi_chip_info_tbl[spi_get_device_id(spi) + ->driver_data]; + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &sca3000_info; + if (st->info->temp_output) { + indio_dev->channels = sca3000_channels_with_temp; + indio_dev->num_channels = + ARRAY_SIZE(sca3000_channels_with_temp); + } else { + indio_dev->channels = sca3000_channels; + indio_dev->num_channels = ARRAY_SIZE(sca3000_channels); + } + indio_dev->modes = INDIO_DIRECT_MODE; + + sca3000_configure_ring(indio_dev); + ret = iio_device_register(indio_dev); + if (ret < 0) + return ret; + + if (spi->irq) { + ret = request_threaded_irq(spi->irq, + NULL, + &sca3000_event_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "sca3000", + indio_dev); + if (ret) + goto error_unregister_dev; + } + sca3000_register_ring_funcs(indio_dev); + ret = sca3000_clean_setup(st); + if (ret) + goto error_free_irq; + return 0; + +error_free_irq: + if (spi->irq) + free_irq(spi->irq, indio_dev); +error_unregister_dev: + iio_device_unregister(indio_dev); + return ret; +} + +static int sca3000_stop_all_interrupts(struct sca3000_state *st) +{ + int ret; + + mutex_lock(&st->lock); + ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_MASK, 1); + if (ret) + goto error_ret; + ret = sca3000_write_reg(st, SCA3000_REG_ADDR_INT_MASK, + (st->rx[0] & + ~(SCA3000_INT_MASK_RING_THREE_QUARTER | + SCA3000_INT_MASK_RING_HALF | + SCA3000_INT_MASK_ALL_INTS))); +error_ret: + mutex_unlock(&st->lock); + return ret; +} + +static int sca3000_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct sca3000_state *st = iio_priv(indio_dev); + + /* Must ensure no interrupts can be generated after this! */ + sca3000_stop_all_interrupts(st); + if (spi->irq) + free_irq(spi->irq, indio_dev); + iio_device_unregister(indio_dev); + sca3000_unconfigure_ring(indio_dev); + + return 0; +} + +static const struct spi_device_id sca3000_id[] = { + {"sca3000_d01", d01}, + {"sca3000_e02", e02}, + {"sca3000_e04", e04}, + {"sca3000_e05", e05}, + {} +}; +MODULE_DEVICE_TABLE(spi, sca3000_id); + +static struct spi_driver sca3000_driver = { + .driver = { + .name = "sca3000", + .owner = THIS_MODULE, + }, + .probe = sca3000_probe, + .remove = sca3000_remove, + .id_table = sca3000_id, +}; +module_spi_driver(sca3000_driver); + +MODULE_AUTHOR("Jonathan Cameron "); +MODULE_DESCRIPTION("VTI SCA3000 Series Accelerometers SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/accel/sca3000_ring.c b/drivers/staging/iio/accel/sca3000_ring.c new file mode 100644 index 000000000..8589eade1 --- /dev/null +++ b/drivers/staging/iio/accel/sca3000_ring.c @@ -0,0 +1,348 @@ +/* + * sca3000_ring.c -- support VTI sca3000 series accelerometers via SPI + * + * 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. + * + * Copyright (c) 2009 Jonathan Cameron + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "../ring_hw.h" +#include "sca3000.h" + +/* RFC / future work + * + * The internal ring buffer doesn't actually change what it holds depending + * on which signals are enabled etc, merely whether you can read them. + * As such the scan mode selection is somewhat different than for a software + * ring buffer and changing it actually covers any data already in the buffer. + * Currently scan elements aren't configured so it doesn't matter. + */ + +static int sca3000_read_data(struct sca3000_state *st, + uint8_t reg_address_high, + u8 **rx_p, + int len) +{ + int ret; + struct spi_transfer xfer[2] = { + { + .len = 1, + .tx_buf = st->tx, + }, { + .len = len, + } + }; + *rx_p = kmalloc(len, GFP_KERNEL); + if (*rx_p == NULL) { + ret = -ENOMEM; + goto error_ret; + } + xfer[1].rx_buf = *rx_p; + st->tx[0] = SCA3000_READ_REG(reg_address_high); + ret = spi_sync_transfer(st->us, xfer, ARRAY_SIZE(xfer)); + if (ret) { + dev_err(get_device(&st->us->dev), "problem reading register"); + goto error_free_rx; + } + + return 0; +error_free_rx: + kfree(*rx_p); +error_ret: + return ret; +} + +/** + * sca3000_read_first_n_hw_rb() - main ring access, pulls data from ring + * @r: the ring + * @count: number of samples to try and pull + * @data: output the actual samples pulled from the hw ring + * + * Currently does not provide timestamps. As the hardware doesn't add them they + * can only be inferred approximately from ring buffer events such as 50% full + * and knowledge of when buffer was last emptied. This is left to userspace. + **/ +static int sca3000_read_first_n_hw_rb(struct iio_buffer *r, + size_t count, char __user *buf) +{ + struct iio_hw_buffer *hw_ring = iio_to_hw_buf(r); + struct iio_dev *indio_dev = hw_ring->private; + struct sca3000_state *st = iio_priv(indio_dev); + u8 *rx; + int ret, i, num_available, num_read = 0; + int bytes_per_sample = 1; + + if (st->bpse == 11) + bytes_per_sample = 2; + + mutex_lock(&st->lock); + if (count % bytes_per_sample) { + ret = -EINVAL; + goto error_ret; + } + + ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_BUF_COUNT, 1); + if (ret) + goto error_ret; + else + num_available = st->rx[0]; + /* + * num_available is the total number of samples available + * i.e. number of time points * number of channels. + */ + if (count > num_available * bytes_per_sample) + num_read = num_available*bytes_per_sample; + else + num_read = count; + + ret = sca3000_read_data(st, + SCA3000_REG_ADDR_RING_OUT, + &rx, num_read); + if (ret) + goto error_ret; + + for (i = 0; i < num_read; i++) + *(((u16 *)rx) + i) = be16_to_cpup((__be16 *)rx + i); + + if (copy_to_user(buf, rx, num_read)) + ret = -EFAULT; + kfree(rx); + r->stufftoread = 0; +error_ret: + mutex_unlock(&st->lock); + + return ret ? ret : num_read; +} + +static size_t sca3000_ring_buf_data_available(struct iio_buffer *r) +{ + return r->stufftoread ? r->watermark : 0; +} + +/** + * sca3000_query_ring_int() is the hardware ring status interrupt enabled + **/ +static ssize_t sca3000_query_ring_int(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret, val; + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct sca3000_state *st = iio_priv(indio_dev); + + mutex_lock(&st->lock); + ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_MASK, 1); + val = st->rx[0]; + mutex_unlock(&st->lock); + if (ret) + return ret; + + return sprintf(buf, "%d\n", !!(val & this_attr->address)); +} + +/** + * sca3000_set_ring_int() set state of ring status interrupt + **/ +static ssize_t sca3000_set_ring_int(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct sca3000_state *st = iio_priv(indio_dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + u8 val; + int ret; + + mutex_lock(&st->lock); + ret = kstrtou8(buf, 10, &val); + if (ret) + goto error_ret; + ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_INT_MASK, 1); + if (ret) + goto error_ret; + if (val) + ret = sca3000_write_reg(st, + SCA3000_REG_ADDR_INT_MASK, + st->rx[0] | this_attr->address); + else + ret = sca3000_write_reg(st, + SCA3000_REG_ADDR_INT_MASK, + st->rx[0] & ~this_attr->address); +error_ret: + mutex_unlock(&st->lock); + + return ret ? ret : len; +} + +static IIO_DEVICE_ATTR(50_percent, S_IRUGO | S_IWUSR, + sca3000_query_ring_int, + sca3000_set_ring_int, + SCA3000_INT_MASK_RING_HALF); + +static IIO_DEVICE_ATTR(75_percent, S_IRUGO | S_IWUSR, + sca3000_query_ring_int, + sca3000_set_ring_int, + SCA3000_INT_MASK_RING_THREE_QUARTER); + +static ssize_t sca3000_show_buffer_scale(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct sca3000_state *st = iio_priv(indio_dev); + + return sprintf(buf, "0.%06d\n", 4*st->info->scale); +} + +static IIO_DEVICE_ATTR(in_accel_scale, + S_IRUGO, + sca3000_show_buffer_scale, + NULL, + 0); + +/* + * Ring buffer attributes + * This device is a bit unusual in that the sampling frequency and bpse + * only apply to the ring buffer. At all times full rate and accuracy + * is available via direct reading from registers. + */ +static const struct attribute *sca3000_ring_attributes[] = { + &iio_dev_attr_50_percent.dev_attr.attr, + &iio_dev_attr_75_percent.dev_attr.attr, + &iio_dev_attr_in_accel_scale.dev_attr.attr, + NULL, +}; + +static struct iio_buffer *sca3000_rb_allocate(struct iio_dev *indio_dev) +{ + struct iio_buffer *buf; + struct iio_hw_buffer *ring; + + ring = kzalloc(sizeof(*ring), GFP_KERNEL); + if (!ring) + return NULL; + + ring->private = indio_dev; + buf = &ring->buf; + buf->stufftoread = 0; + buf->length = 64; + buf->attrs = sca3000_ring_attributes; + iio_buffer_init(buf); + + return buf; +} + +static void sca3000_ring_release(struct iio_buffer *r) +{ + kfree(iio_to_hw_buf(r)); +} + +static const struct iio_buffer_access_funcs sca3000_ring_access_funcs = { + .read_first_n = &sca3000_read_first_n_hw_rb, + .data_available = sca3000_ring_buf_data_available, + .release = sca3000_ring_release, +}; + +int sca3000_configure_ring(struct iio_dev *indio_dev) +{ + struct iio_buffer *buffer; + + buffer = sca3000_rb_allocate(indio_dev); + if (buffer == NULL) + return -ENOMEM; + indio_dev->modes |= INDIO_BUFFER_HARDWARE; + + indio_dev->buffer->access = &sca3000_ring_access_funcs; + + iio_device_attach_buffer(indio_dev, buffer); + + return 0; +} + +void sca3000_unconfigure_ring(struct iio_dev *indio_dev) +{ + iio_buffer_put(indio_dev->buffer); +} + +static inline +int __sca3000_hw_ring_state_set(struct iio_dev *indio_dev, bool state) +{ + struct sca3000_state *st = iio_priv(indio_dev); + int ret; + + mutex_lock(&st->lock); + ret = sca3000_read_data_short(st, SCA3000_REG_ADDR_MODE, 1); + if (ret) + goto error_ret; + if (state) { + dev_info(&indio_dev->dev, "supposedly enabling ring buffer\n"); + ret = sca3000_write_reg(st, + SCA3000_REG_ADDR_MODE, + (st->rx[0] | SCA3000_RING_BUF_ENABLE)); + } else + ret = sca3000_write_reg(st, + SCA3000_REG_ADDR_MODE, + (st->rx[0] & ~SCA3000_RING_BUF_ENABLE)); +error_ret: + mutex_unlock(&st->lock); + + return ret; +} +/** + * sca3000_hw_ring_preenable() hw ring buffer preenable function + * + * Very simple enable function as the chip will allows normal reads + * during ring buffer operation so as long as it is indeed running + * before we notify the core, the precise ordering does not matter. + **/ +static int sca3000_hw_ring_preenable(struct iio_dev *indio_dev) +{ + return __sca3000_hw_ring_state_set(indio_dev, 1); +} + +static int sca3000_hw_ring_postdisable(struct iio_dev *indio_dev) +{ + return __sca3000_hw_ring_state_set(indio_dev, 0); +} + +static const struct iio_buffer_setup_ops sca3000_ring_setup_ops = { + .preenable = &sca3000_hw_ring_preenable, + .postdisable = &sca3000_hw_ring_postdisable, +}; + +void sca3000_register_ring_funcs(struct iio_dev *indio_dev) +{ + indio_dev->setup_ops = &sca3000_ring_setup_ops; +} + +/** + * sca3000_ring_int_process() ring specific interrupt handling. + * + * This is only split from the main interrupt handler so as to + * reduce the amount of code if the ring buffer is not enabled. + **/ +void sca3000_ring_int_process(u8 val, struct iio_buffer *ring) +{ + if (val & (SCA3000_INT_STATUS_THREE_QUARTERS | + SCA3000_INT_STATUS_HALF)) { + ring->stufftoread = true; + wake_up_interruptible(&ring->pollq); + } +} -- cgit v1.2.3-54-g00ecf