Commit 2c834b4d authored by Barry Song's avatar Barry Song Committed by Greg Kroah-Hartman

staging: iio: adis16240 driver

Signed-off-by: default avatarBarry Song <Barry.Song@analog.com>
Signed-off-by: default avatarJonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 671ece14
...@@ -12,6 +12,15 @@ config ADIS16209 ...@@ -12,6 +12,15 @@ config ADIS16209
Say yes here to build support for Analog Devices adis16209 dual-axis digital inclinometer Say yes here to build support for Analog Devices adis16209 dual-axis digital inclinometer
and accelerometer. and accelerometer.
config ADIS16240
tristate "Analog Devices ADIS16240 Programmable Impact Sensor and Recorder"
depends on SPI
select IIO_TRIGGER if IIO_RING_BUFFER
select IIO_SW_RING if IIO_RING_BUFFER
help
Say yes here to build support for Analog Devices adis16240 programmable
impact Sensor and recorder.
config KXSD9 config KXSD9
tristate "Kionix KXSD9 Accelerometer Driver" tristate "Kionix KXSD9 Accelerometer Driver"
depends on SPI depends on SPI
......
...@@ -5,6 +5,10 @@ adis16209-y := adis16209_core.o ...@@ -5,6 +5,10 @@ adis16209-y := adis16209_core.o
adis16209-$(CONFIG_IIO_RING_BUFFER) += adis16209_ring.o adis16209_trigger.o adis16209-$(CONFIG_IIO_RING_BUFFER) += adis16209_ring.o adis16209_trigger.o
obj-$(CONFIG_ADIS16209) += adis16209.o obj-$(CONFIG_ADIS16209) += adis16209.o
adis16240-y := adis16240_core.o
adis16240-$(CONFIG_IIO_RING_BUFFER) += adis16240_ring.o adis16240_trigger.o
obj-$(CONFIG_ADIS16240) += adis16240.o
obj-$(CONFIG_KXSD9) += kxsd9.o obj-$(CONFIG_KXSD9) += kxsd9.o
lis3l02dq-y := lis3l02dq_core.o lis3l02dq-y := lis3l02dq_core.o
......
#ifndef SPI_ADIS16240_H_
#define SPI_ADIS16240_H_
#define ADIS16240_STARTUP_DELAY 220 /* ms */
#define ADIS16240_READ_REG(a) a
#define ADIS16240_WRITE_REG(a) ((a) | 0x80)
/* 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
#define ADIS16240_OUTPUTS 6
/* MSC_CTRL */
/* Enables sum-of-squares output (XYZPEAK_OUT) */
#define ADIS16240_MSC_CTRL_XYZPEAK_OUT_EN (1 << 15)
/* Enables peak tracking output (XPEAK_OUT, YPEAK_OUT, and ZPEAK_OUT) */
#define ADIS16240_MSC_CTRL_X_Y_ZPEAK_OUT_EN (1 << 14)
/* Self-test enable: 1 = apply electrostatic force, 0 = disabled */
#define ADIS16240_MSC_CTRL_SELF_TEST_EN (1 << 8)
/* Data-ready enable: 1 = enabled, 0 = disabled */
#define ADIS16240_MSC_CTRL_DATA_RDY_EN (1 << 2)
/* Data-ready polarity: 1 = active high, 0 = active low */
#define ADIS16240_MSC_CTRL_ACTIVE_HIGH (1 << 1)
/* Data-ready line selection: 1 = DIO2, 0 = DIO1 */
#define ADIS16240_MSC_CTRL_DATA_RDY_DIO2 (1 << 0)
/* DIAG_STAT */
/* Alarm 2 status: 1 = alarm active, 0 = alarm inactive */
#define ADIS16240_DIAG_STAT_ALARM2 (1<<9)
/* Alarm 1 status: 1 = alarm active, 0 = alarm inactive */
#define ADIS16240_DIAG_STAT_ALARM1 (1<<8)
/* Capture buffer full: 1 = capture buffer is full */
#define ADIS16240_DIAG_STAT_CPT_BUF_FUL (1<<7)
/* Flash test, checksum flag: 1 = mismatch, 0 = match */
#define ADIS16240_DIAG_STAT_CHKSUM (1<<6)
/* Power-on, self-test flag: 1 = failure, 0 = pass */
#define ADIS16240_DIAG_STAT_PWRON_FAIL (1<<5)
/* Power-on self-test: 1 = in-progress, 0 = complete */
#define ADIS16240_DIAG_STAT_PWRON_BUSY (1<<4)
/* SPI communications failure */
#define ADIS16240_DIAG_STAT_SPI_FAIL (1<<3)
/* Flash update failure */
#define ADIS16240_DIAG_STAT_FLASH_UPT (1<<2)
/* Power supply above 3.625 V */
#define ADIS16240_DIAG_STAT_POWER_HIGH (1<<1)
/* Power supply below 3.15 V */
#define ADIS16240_DIAG_STAT_POWER_LOW (1<<0)
/* GLOB_CMD */
#define ADIS16240_GLOB_CMD_RESUME (1<<8)
#define ADIS16240_GLOB_CMD_SW_RESET (1<<7)
#define ADIS16240_GLOB_CMD_STANDBY (1<<2)
#define ADIS16240_ERROR_ACTIVE (1<<14)
#define ADIS16240_MAX_TX 24
#define ADIS16240_MAX_RX 24
/**
* struct adis16240_state - device instance specific data
* @us: actual spi_device
* @work_trigger_to_ring: bh for triggered event handling
* @work_cont_thresh: CLEAN
* @inter: used to check if new interrupt has been triggered
* @last_timestamp: passing timestamp from th to bh of interrupt handler
* @indio_dev: industrial I/O device structure
* @trig: data ready trigger registered with iio
* @tx: transmit buffer
* @rx: recieve buffer
* @buf_lock: mutex to protect tx and rx
**/
struct adis16240_state {
struct spi_device *us;
struct work_struct work_trigger_to_ring;
struct iio_work_cont work_cont_thresh;
s64 last_timestamp;
struct iio_dev *indio_dev;
struct iio_trigger *trig;
u8 *tx;
u8 *rx;
struct mutex buf_lock;
};
int adis16240_set_irq(struct device *dev, bool enable);
#ifdef CONFIG_IIO_RING_BUFFER
/* At the moment triggers are only used for ring buffer
* filling. This may change!
*/
enum adis16240_scan {
ADIS16240_SCAN_SUPPLY,
ADIS16240_SCAN_ACC_X,
ADIS16240_SCAN_ACC_Y,
ADIS16240_SCAN_ACC_Z,
ADIS16240_SCAN_AUX_ADC,
ADIS16240_SCAN_TEMP,
};
void adis16240_remove_trigger(struct iio_dev *indio_dev);
int adis16240_probe_trigger(struct iio_dev *indio_dev);
ssize_t adis16240_read_data_from_ring(struct device *dev,
struct device_attribute *attr,
char *buf);
int adis16240_configure_ring(struct iio_dev *indio_dev);
void adis16240_unconfigure_ring(struct iio_dev *indio_dev);
int adis16240_initialize_ring(struct iio_ring_buffer *ring);
void adis16240_uninitialize_ring(struct iio_ring_buffer *ring);
#else /* CONFIG_IIO_RING_BUFFER */
static inline void adis16240_remove_trigger(struct iio_dev *indio_dev)
{
}
static inline int adis16240_probe_trigger(struct iio_dev *indio_dev)
{
return 0;
}
static inline ssize_t
adis16240_read_data_from_ring(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return 0;
}
static int adis16240_configure_ring(struct iio_dev *indio_dev)
{
return 0;
}
static inline void adis16240_unconfigure_ring(struct iio_dev *indio_dev)
{
}
static inline int adis16240_initialize_ring(struct iio_ring_buffer *ring)
{
return 0;
}
static inline void adis16240_uninitialize_ring(struct iio_ring_buffer *ring)
{
}
#endif /* CONFIG_IIO_RING_BUFFER */
#endif /* SPI_ADIS16240_H_ */
This diff is collapsed.
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/workqueue.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/spi/spi.h>
#include <linux/sysfs.h>
#include <linux/list.h>
#include "../iio.h"
#include "../sysfs.h"
#include "../ring_sw.h"
#include "accel.h"
#include "../trigger.h"
#include "adis16240.h"
/**
* combine_8_to_16() utility function to munge to u8s into u16
**/
static inline u16 combine_8_to_16(u8 lower, u8 upper)
{
u16 _lower = lower;
u16 _upper = upper;
return _lower | (_upper << 8);
}
static IIO_SCAN_EL_C(supply, ADIS16240_SCAN_SUPPLY, IIO_UNSIGNED(10),
ADIS16240_SUPPLY_OUT, NULL);
static IIO_SCAN_EL_C(accel_x, ADIS16240_SCAN_ACC_X, IIO_SIGNED(10),
ADIS16240_XACCL_OUT, NULL);
static IIO_SCAN_EL_C(accel_y, ADIS16240_SCAN_ACC_Y, IIO_SIGNED(10),
ADIS16240_YACCL_OUT, NULL);
static IIO_SCAN_EL_C(accel_z, ADIS16240_SCAN_ACC_Z, IIO_SIGNED(10),
ADIS16240_ZACCL_OUT, NULL);
static IIO_SCAN_EL_C(aux_adc, ADIS16240_SCAN_AUX_ADC, IIO_UNSIGNED(10),
ADIS16240_AUX_ADC, NULL);
static IIO_SCAN_EL_C(temp, ADIS16240_SCAN_TEMP, IIO_UNSIGNED(10),
ADIS16240_TEMP_OUT, NULL);
static IIO_SCAN_EL_TIMESTAMP(6);
static struct attribute *adis16240_scan_el_attrs[] = {
&iio_scan_el_supply.dev_attr.attr,
&iio_scan_el_accel_x.dev_attr.attr,
&iio_scan_el_accel_y.dev_attr.attr,
&iio_scan_el_accel_z.dev_attr.attr,
&iio_scan_el_aux_adc.dev_attr.attr,
&iio_scan_el_temp.dev_attr.attr,
&iio_scan_el_timestamp.dev_attr.attr,
NULL,
};
static struct attribute_group adis16240_scan_el_group = {
.attrs = adis16240_scan_el_attrs,
.name = "scan_elements",
};
/**
* adis16240_poll_func_th() top half interrupt handler called by trigger
* @private_data: iio_dev
**/
static void adis16240_poll_func_th(struct iio_dev *indio_dev)
{
struct adis16240_state *st = iio_dev_get_devdata(indio_dev);
st->last_timestamp = indio_dev->trig->timestamp;
schedule_work(&st->work_trigger_to_ring);
}
/**
* adis16240_read_ring_data() read data registers which will be placed into ring
* @dev: device associated with child of actual device (iio_dev or iio_trig)
* @rx: somewhere to pass back the value read
**/
static int adis16240_read_ring_data(struct device *dev, u8 *rx)
{
struct spi_message msg;
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct adis16240_state *st = iio_dev_get_devdata(indio_dev);
struct spi_transfer xfers[ADIS16240_OUTPUTS + 1];
int ret;
int i;
mutex_lock(&st->buf_lock);
spi_message_init(&msg);
memset(xfers, 0, sizeof(xfers));
for (i = 0; i <= ADIS16240_OUTPUTS; i++) {
xfers[i].bits_per_word = 8;
xfers[i].cs_change = 1;
xfers[i].len = 2;
xfers[i].delay_usecs = 30;
xfers[i].tx_buf = st->tx + 2 * i;
st->tx[2 * i]
= ADIS16240_READ_REG(ADIS16240_SUPPLY_OUT + 2 * i);
st->tx[2 * i + 1] = 0;
if (i >= 1)
xfers[i].rx_buf = rx + 2 * (i - 1);
spi_message_add_tail(&xfers[i], &msg);
}
ret = spi_sync(st->us, &msg);
if (ret)
dev_err(&st->us->dev, "problem when burst reading");
mutex_unlock(&st->buf_lock);
return ret;
}
static void adis16240_trigger_bh_to_ring(struct work_struct *work_s)
{
struct adis16240_state *st
= container_of(work_s, struct adis16240_state,
work_trigger_to_ring);
int i = 0;
s16 *data;
size_t datasize = st->indio_dev
->ring->access.get_bpd(st->indio_dev->ring);
data = kmalloc(datasize , GFP_KERNEL);
if (data == NULL) {
dev_err(&st->us->dev, "memory alloc failed in ring bh");
return;
}
if (st->indio_dev->scan_count)
if (adis16240_read_ring_data(&st->indio_dev->dev, st->rx) >= 0)
for (; i < st->indio_dev->scan_count; i++) {
data[i] = combine_8_to_16(st->rx[i*2+1],
st->rx[i*2]);
}
/* Guaranteed to be aligned with 8 byte boundary */
if (st->indio_dev->scan_timestamp)
*((s64 *)(data + ((i + 3)/4)*4)) = st->last_timestamp;
st->indio_dev->ring->access.store_to(st->indio_dev->ring,
(u8 *)data,
st->last_timestamp);
iio_trigger_notify_done(st->indio_dev->trig);
kfree(data);
return;
}
static int adis16240_data_rdy_ring_preenable(struct iio_dev *indio_dev)
{
size_t size;
dev_dbg(&indio_dev->dev, "%s\n", __func__);
/* Check if there are any scan elements enabled, if not fail*/
if (!(indio_dev->scan_count || indio_dev->scan_timestamp))
return -EINVAL;
if (indio_dev->ring->access.set_bpd) {
if (indio_dev->scan_timestamp)
if (indio_dev->scan_count)
/* Timestamp (aligned sizeof(s64) and data */
size = (((indio_dev->scan_count * sizeof(s16))
+ sizeof(s64) - 1)
& ~(sizeof(s64) - 1))
+ sizeof(s64);
else /* Timestamp only */
size = sizeof(s64);
else /* Data only */
size = indio_dev->scan_count*sizeof(s16);
indio_dev->ring->access.set_bpd(indio_dev->ring, size);
}
return 0;
}
static int adis16240_data_rdy_ring_postenable(struct iio_dev *indio_dev)
{
return indio_dev->trig
? iio_trigger_attach_poll_func(indio_dev->trig,
indio_dev->pollfunc)
: 0;
}
static int adis16240_data_rdy_ring_predisable(struct iio_dev *indio_dev)
{
return indio_dev->trig
? iio_trigger_dettach_poll_func(indio_dev->trig,
indio_dev->pollfunc)
: 0;
}
void adis16240_unconfigure_ring(struct iio_dev *indio_dev)
{
kfree(indio_dev->pollfunc);
iio_sw_rb_free(indio_dev->ring);
}
int adis16240_configure_ring(struct iio_dev *indio_dev)
{
int ret = 0;
struct adis16240_state *st = indio_dev->dev_data;
struct iio_ring_buffer *ring;
INIT_WORK(&st->work_trigger_to_ring, adis16240_trigger_bh_to_ring);
/* Set default scan mode */
iio_scan_mask_set(indio_dev, iio_scan_el_supply.number);
iio_scan_mask_set(indio_dev, iio_scan_el_accel_x.number);
iio_scan_mask_set(indio_dev, iio_scan_el_accel_y.number);
iio_scan_mask_set(indio_dev, iio_scan_el_accel_z.number);
iio_scan_mask_set(indio_dev, iio_scan_el_temp.number);
iio_scan_mask_set(indio_dev, iio_scan_el_aux_adc.number);
indio_dev->scan_timestamp = true;
indio_dev->scan_el_attrs = &adis16240_scan_el_group;
ring = iio_sw_rb_allocate(indio_dev);
if (!ring) {
ret = -ENOMEM;
return ret;
}
indio_dev->ring = ring;
/* Effectively select the ring buffer implementation */
iio_ring_sw_register_funcs(&ring->access);
ring->preenable = &adis16240_data_rdy_ring_preenable;
ring->postenable = &adis16240_data_rdy_ring_postenable;
ring->predisable = &adis16240_data_rdy_ring_predisable;
ring->owner = THIS_MODULE;
indio_dev->pollfunc = kzalloc(sizeof(*indio_dev->pollfunc), GFP_KERNEL);
if (indio_dev->pollfunc == NULL) {
ret = -ENOMEM;
goto error_iio_sw_rb_free;;
}
indio_dev->pollfunc->poll_func_main = &adis16240_poll_func_th;
indio_dev->pollfunc->private_data = indio_dev;
indio_dev->modes |= INDIO_RING_TRIGGERED;
return 0;
error_iio_sw_rb_free:
iio_sw_rb_free(indio_dev->ring);
return ret;
}
int adis16240_initialize_ring(struct iio_ring_buffer *ring)
{
return iio_ring_buffer_register(ring, 0);
}
void adis16240_uninitialize_ring(struct iio_ring_buffer *ring)
{
iio_ring_buffer_unregister(ring);
}
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/sysfs.h>
#include <linux/list.h>
#include <linux/spi/spi.h>
#include "../iio.h"
#include "../sysfs.h"
#include "../trigger.h"
#include "adis16240.h"
/**
* adis16240_data_rdy_trig_poll() the event handler for the data rdy trig
**/
static int adis16240_data_rdy_trig_poll(struct iio_dev *dev_info,
int index,
s64 timestamp,
int no_test)
{
struct adis16240_state *st = iio_dev_get_devdata(dev_info);
struct iio_trigger *trig = st->trig;
trig->timestamp = timestamp;
iio_trigger_poll(trig);
return IRQ_HANDLED;
}
IIO_EVENT_SH(data_rdy_trig, &adis16240_data_rdy_trig_poll);
static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL);
static struct attribute *adis16240_trigger_attrs[] = {
&dev_attr_name.attr,
NULL,
};
static const struct attribute_group adis16240_trigger_attr_group = {
.attrs = adis16240_trigger_attrs,
};
/**
* adis16240_data_rdy_trigger_set_state() set datardy interrupt state
**/
static int adis16240_data_rdy_trigger_set_state(struct iio_trigger *trig,
bool state)
{
struct adis16240_state *st = trig->private_data;
struct iio_dev *indio_dev = st->indio_dev;
int ret = 0;
dev_dbg(&indio_dev->dev, "%s (%d)\n", __func__, state);
ret = adis16240_set_irq(&st->indio_dev->dev, state);
if (state == false) {
iio_remove_event_from_list(&iio_event_data_rdy_trig,
&indio_dev->interrupts[0]
->ev_list);
flush_scheduled_work();
} else {
iio_add_event_to_list(&iio_event_data_rdy_trig,
&indio_dev->interrupts[0]->ev_list);
}
return ret;
}
/**
* adis16240_trig_try_reen() try renabling irq for data rdy trigger
* @trig: the datardy trigger
**/
static int adis16240_trig_try_reen(struct iio_trigger *trig)
{
struct adis16240_state *st = trig->private_data;
enable_irq(st->us->irq);
return 0;
}
int adis16240_probe_trigger(struct iio_dev *indio_dev)
{
int ret;
struct adis16240_state *st = indio_dev->dev_data;
st->trig = iio_allocate_trigger();
st->trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL);
if (!st->trig->name) {
ret = -ENOMEM;
goto error_free_trig;
}
snprintf((char *)st->trig->name,
IIO_TRIGGER_NAME_LENGTH,
"adis16240-dev%d", indio_dev->id);
st->trig->dev.parent = &st->us->dev;
st->trig->owner = THIS_MODULE;
st->trig->private_data = st;
st->trig->set_trigger_state = &adis16240_data_rdy_trigger_set_state;
st->trig->try_reenable = &adis16240_trig_try_reen;
st->trig->control_attrs = &adis16240_trigger_attr_group;
ret = iio_trigger_register(st->trig);
/* select default trigger */
indio_dev->trig = st->trig;
if (ret)
goto error_free_trig_name;
return 0;
error_free_trig_name:
kfree(st->trig->name);
error_free_trig:
iio_free_trigger(st->trig);
return ret;
}
void adis16240_remove_trigger(struct iio_dev *indio_dev)
{
struct adis16240_state *state = indio_dev->dev_data;
iio_trigger_unregister(state->trig);
kfree(state->trig->name);
iio_free_trigger(state->trig);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment