From xxxx Mon Sep 17 00:00:00 2001 From: Dan O'Donovan Date: Mon, 24 Feb 2014 18:41:59 +0000 Subject: [PATCH 18/21] Quark sensors --- drivers/iio/accel/Kconfig | 8 + drivers/iio/accel/Makefile | 2 + drivers/iio/accel/lis331dlh_intel_qrk.c | 735 ++++++++++++++++++++ drivers/iio/common/Kconfig | 1 + drivers/iio/common/Makefile | 1 + drivers/iio/common/st_sensors/Kconfig | 14 + drivers/iio/common/st_sensors/Makefile | 10 + drivers/iio/common/st_sensors/st_sensors_buffer.c | 115 +++ drivers/iio/common/st_sensors/st_sensors_core.c | 447 ++++++++++++ drivers/iio/common/st_sensors/st_sensors_i2c.c | 81 +++ drivers/iio/common/st_sensors/st_sensors_spi.c | 128 ++++ drivers/iio/common/st_sensors/st_sensors_trigger.c | 77 ++ drivers/iio/industrialio-buffer.c | 6 +- include/linux/iio/common/st_sensors.h | 289 ++++++++ include/linux/iio/common/st_sensors_i2c.h | 20 + include/linux/iio/common/st_sensors_spi.h | 20 + include/linux/platform_data/lis331dlh_intel_qrk.h | 36 + 17 files changed, 1987 insertions(+), 3 deletions(-) create mode 100644 drivers/iio/accel/lis331dlh_intel_qrk.c create mode 100644 drivers/iio/common/st_sensors/Kconfig create mode 100644 drivers/iio/common/st_sensors/Makefile create mode 100644 drivers/iio/common/st_sensors/st_sensors_buffer.c create mode 100644 drivers/iio/common/st_sensors/st_sensors_core.c create mode 100644 drivers/iio/common/st_sensors/st_sensors_i2c.c create mode 100644 drivers/iio/common/st_sensors/st_sensors_spi.c create mode 100644 drivers/iio/common/st_sensors/st_sensors_trigger.c create mode 100644 include/linux/iio/common/st_sensors.h create mode 100644 include/linux/iio/common/st_sensors_i2c.h create mode 100644 include/linux/iio/common/st_sensors_spi.h create mode 100644 include/linux/platform_data/lis331dlh_intel_qrk.h diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 05e996f..be6ded3 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -13,5 +13,13 @@ config HID_SENSOR_ACCEL_3D help Say yes here to build support for the HID SENSOR accelerometers 3D. + +config IIO_LIS331DLH_INTEL_QRK + tristate "STMicroelectronics LIS331DLH accelerometer i2c driver for Intel Quark platform" + depends on INTEL_QUARK_X1000_SOC + depends on I2C && SYSFS + select IIO_ST_SENSORS_CORE + help + Selects the LIS331DLH accelerometer driver for the Intel Clanton Hill platform endmenu diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index 5bc6855..81f8085 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -3,3 +3,5 @@ # obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o + +obj-$(CONFIG_IIO_LIS331DLH_INTEL_QRK) += lis331dlh_intel_qrk.o diff --git a/drivers/iio/accel/lis331dlh_intel_qrk.c b/drivers/iio/accel/lis331dlh_intel_qrk.c new file mode 100644 index 0000000..6b49c6f --- /dev/null +++ b/drivers/iio/accel/lis331dlh_intel_qrk.c @@ -0,0 +1,735 @@ +/* + * Intel Clanton Hill platform accelerometer driver + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Contact Information: + * Intel Corporation + * + * Derived from STMicroelectronics accelerometers driver by Denis Ciocca + * + * The Intel Clanton Hill platform hardware design includes an + * STMicroelectronics LIS331DLH accelerometer, intended to be used mainly for + * sensing orientation, movement and sudden impacts (e.g. vehicle collision) + * + * This driver plugs into the Linux Industrial-IO framework to provide a + * standardised user-space application interface for retreiving data and events + * from the accelerometer. + * + * The LIS331DLH is connected via I2C to the host CPU on the Clanton Hill + * platform and so this driver registers to the kernel as an I2C device driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +/* DEFAULT VALUE FOR SENSORS */ +#define ST_ACCEL_DEFAULT_OUT_X_L_ADDR 0x28 +#define ST_ACCEL_DEFAULT_OUT_Y_L_ADDR 0x2a +#define ST_ACCEL_DEFAULT_OUT_Z_L_ADDR 0x2c + +/* FULLSCALE */ +#define ST_ACCEL_FS_AVL_2G 2 +#define ST_ACCEL_FS_AVL_4G 4 +#define ST_ACCEL_FS_AVL_6G 6 +#define ST_ACCEL_FS_AVL_8G 8 +#define ST_ACCEL_FS_AVL_16G 16 + +/* CUSTOM VALUES FOR SENSOR 2 */ +#define ST_ACCEL_2_WAI_EXP 0x32 +#define ST_ACCEL_2_ODR_ADDR 0x20 +#define ST_ACCEL_2_ODR_MASK 0x18 +#define ST_ACCEL_2_ODR_AVL_50HZ_VAL 0x00 +#define ST_ACCEL_2_ODR_AVL_100HZ_VAL 0x01 +#define ST_ACCEL_2_ODR_AVL_400HZ_VAL 0x02 +#define ST_ACCEL_2_ODR_AVL_1000HZ_VAL 0x03 +#define ST_ACCEL_2_PW_ADDR 0x20 +#define ST_ACCEL_2_PW_MASK 0xe0 +#define ST_ACCEL_2_PW_DOWN 0x00 +#define ST_ACCEL_2_PW_NORMAL 0x20 +#define ST_ACCEL_2_CTRL_REG1_XEN 0x01 +#define ST_ACCEL_2_CTRL_REG1_YEN 0x02 +#define ST_ACCEL_2_CTRL_REG1_ZEN 0x04 +#define ST_ACCEL_2_FS_ADDR 0x23 +#define ST_ACCEL_2_FS_MASK 0x30 +#define ST_ACCEL_2_FS_AVL_2_VAL 0X00 +#define ST_ACCEL_2_FS_AVL_4_VAL 0X01 +#define ST_ACCEL_2_FS_AVL_8_VAL 0x03 +#define ST_ACCEL_2_FS_AVL_2_GAIN IIO_G_TO_M_S_2(1000) +#define ST_ACCEL_2_FS_AVL_4_GAIN IIO_G_TO_M_S_2(2000) +#define ST_ACCEL_2_FS_AVL_8_GAIN IIO_G_TO_M_S_2(3900) +#define ST_ACCEL_2_BDU_ADDR 0x23 +#define ST_ACCEL_2_BDU_MASK 0x80 +#define ST_ACCEL_2_DRDY_IRQ_ADDR 0x22 +#define ST_ACCEL_2_DRDY_IRQ_MASK 0x02 +#define ST_ACCEL_2_THRESH_IRQ_ADDR 0x30 +#define ST_ACCEL_2_THRESH_IRQ_MASK 0x7f +#define ST_ACCEL_2_INT1_CFG_ADDR 0x30 +#define ST_ACCEL_2_INT1_SRC_ADDR 0x31 +#define ST_ACCEL_2_INT1_THRESH_ADDR 0x32 +#define ST_ACCEL_2_INT1_DURATION_ADDR 0x33 +#define ST_ACCEL_2_INT2_CFG_ADDR 0x34 +#define ST_ACCEL_2_INT2_SRC_ADDR 0x35 +#define ST_ACCEL_2_INT2_THRESH_ADDR 0x36 +#define ST_ACCEL_2_INT2_DURATION_ADDR 0x37 +#define ST_ACCEL_2_INT_IA_MASK 0x40 +#define ST_ACCEL_2_INT_LIR_MASK 0x05 +#define ST_ACCEL_2_INT_SRC_HIGH_MASK 0x20 +#define ST_ACCEL_2_INT_CFG_XLIE_EN 0x01 +#define ST_ACCEL_2_INT_CFG_XHIE_EN 0x02 +#define ST_ACCEL_2_INT_CFG_YLIE_EN 0x04 +#define ST_ACCEL_2_INT_CFG_YHIE_EN 0x08 +#define ST_ACCEL_2_INT_CFG_ZLIE_EN 0x10 +#define ST_ACCEL_2_INT_CFG_ZHIE_EN 0x20 + +#define ST_ACCEL_2_MULTIREAD_BIT true +#define ST_ACCEL_2_THRESH_VAL_MIN 0x00 +#define ST_ACCEL_2_THRESH_VAL_MAX 0x7f +#define QRK_ACCEL_INT2_WAKEUP_THRESH_VAL 0x7f + +#define QRK_ACCEL_INT1_DISABLED 0 +#define QRK_ACCEL_INT1_ENABLED 1 + +#define QRK_ACCEL_LSM_CHANNELS(device_type, index, mod, endian, bits, addr) \ +{ \ + .type = device_type, \ + .modified = 1, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \ + .scan_index = index, \ + .channel = mod, \ + .channel2 = mod, \ + .address = addr, \ + .scan_type = { \ + .sign = 's', \ + .realbits = bits, \ + .shift = 16 - bits, \ + .storagebits = 16, \ + .endianness = endian, \ + }, \ + .event_mask = IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), \ +} + +static const u8 iio_modifier_map[] = { + IIO_NO_MOD, + IIO_MOD_X, + IIO_MOD_Y, + IIO_MOD_X_AND_Y, + IIO_MOD_Z, + IIO_MOD_X_AND_Z, + IIO_MOD_Y_AND_Z, + IIO_MOD_X_AND_Y_AND_Z, +}; + +/* Threshold event ISR bottom half. This function reads interrupt status + * registers for INT1 to reset any active interrupt conditions + * and pushes an IIO event if a threshold interrupt was active. + */ +static irqreturn_t lis331dlh_intel_qrk_threshold_event_handler( + int irq, + void *private) +{ + int err; + u8 data; + u8 mask; + int i; + u64 iio_modifier; + + struct st_sensor_data *sdata = iio_priv(private); + s64 timestamp = iio_get_time_ns(); + err = sdata->tf->read_byte(&sdata->tb, sdata->dev, + ST_ACCEL_2_INT1_SRC_ADDR, + &data); + + if (err < 0) + goto st_sensors_read_err; + + err = sdata->tf->read_byte(&sdata->tb, sdata->dev, + ST_ACCEL_2_INT1_CFG_ADDR, + &mask); + + if (err < 0) + goto st_sensors_read_err; + + if (data & ST_ACCEL_2_INT_IA_MASK) { + data &= mask; + + iio_modifier = 0; + for (i = 0; i < ST_SENSORS_NUMBER_DATA_CHANNELS; i++) { + iio_modifier <<= 1; + iio_modifier += !!(data & ST_ACCEL_2_INT_SRC_HIGH_MASK); + data <<= 2; + } + + iio_modifier = iio_modifier_map[iio_modifier]; + + iio_push_event(private, + IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + iio_modifier, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + timestamp); + } + +st_sensors_read_err: + return IRQ_HANDLED; +} + +static inline int lis331dlh_intel_qrk_read_info_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *ch, int *val) +{ + int err; + + mutex_lock(&indio_dev->mlock); + err = st_sensors_read_axis_data(indio_dev, ch->address, val); + + if (unlikely(err < 0)) + goto read_error; + + *val = *val >> ch->scan_type.shift; + mutex_unlock(&indio_dev->mlock); + + return err; + +read_error: + mutex_unlock(&indio_dev->mlock); + return err; +} + +static int lis331dlh_intel_qrk_read_raw( + struct iio_dev *indio_dev, + struct iio_chan_spec const *ch, + int *val, int *val2, long mask) +{ + int err; + struct st_sensor_data *adata = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + err = lis331dlh_intel_qrk_read_info_raw(indio_dev, ch, val); + if (unlikely(err < 0)) + goto read_error; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = adata->current_fullscale->gain; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + +read_error: + return err; +} + +static int lis331dlh_intel_qrk_write_raw( + struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int err; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + err = st_sensors_set_fullscale_by_gain(indio_dev, val2); + break; + default: + return -EINVAL; + } + + return err; +} + +static ST_SENSOR_DEV_ATTR_SAMP_FREQ(); +static ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL(); +static ST_SENSORS_DEV_ATTR_SCALE_AVAIL(in_accel_scale_available); + +static struct attribute *lis331dlh_intel_qrk_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_accel_scale_available.dev_attr.attr, + &iio_dev_attr_sampling_frequency.dev_attr.attr, + NULL, +}; + +static const struct attribute_group lis331dlh_intel_qrk_attribute_group = { + .attrs = lis331dlh_intel_qrk_attributes, +}; + +static int lis331dlh_intel_qrk_read_event_value( + struct iio_dev *indio_dev, + u64 event_code, + int *val) +{ + int err; + u8 data; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + err = sdata->tf->read_byte(&sdata->tb, sdata->dev, + ST_ACCEL_2_INT1_THRESH_ADDR, &data); + + *val = (int) data; + return err; +} + +static int lis331dlh_intel_qrk_write_event_value( + struct iio_dev *indio_dev, + u64 event_code, + int val) +{ + int err; + struct st_sensor_data *sdata; + + /* range check */ + if (unlikely((val < ST_ACCEL_2_THRESH_VAL_MIN) || + (val > ST_ACCEL_2_THRESH_VAL_MAX))) + return -EINVAL; + + sdata = iio_priv(indio_dev); + + err = sdata->tf->write_byte(&sdata->tb, sdata->dev, + ST_ACCEL_2_INT1_THRESH_ADDR, val); + + return err; +} + +/* Configure the INT1 pin to fire an interrupt on a high threshold event. + */ +static int lis331dlh_intel_qrk_configure_threshold_interrupt( + struct iio_dev *indio_dev, u8 state) +{ + int err = 0; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + if (sdata->sensor->drdy_irq.ig1.en_mask == state) + return 0; + + if (state == QRK_ACCEL_INT1_ENABLED) { + err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev), + NULL, + lis331dlh_intel_qrk_threshold_event_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "lis331dlh_intel_qrk_threshold", + indio_dev); + if (likely(err == 0)) { + sdata->sensor->drdy_irq.ig1.en_mask = + QRK_ACCEL_INT1_ENABLED; + err = sdata->tf->write_byte( + &sdata->tb, sdata->dev, + ST_ACCEL_2_INT1_DURATION_ADDR, 1); + } + } else { + free_irq(sdata->get_irq_data_ready(indio_dev), indio_dev); + sdata->sensor->drdy_irq.ig1.en_mask = QRK_ACCEL_INT1_DISABLED; + } + + return err; +} + +static int lis331dlh_intel_qrk_read_event_config( + struct iio_dev *indio_dev, + u64 event_code) +{ + int err = 0; + u8 data, mask; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + err = sdata->tf->read_byte(&sdata->tb, sdata->dev, + ST_ACCEL_2_INT1_CFG_ADDR, + &data); + + mask = 1 << ((IIO_EVENT_CODE_EXTRACT_MODIFIER(event_code) << 1) - 1); + + return !!(data & mask); +} + +static int lis331dlh_intel_qrk_write_event_config( + struct iio_dev *indio_dev, + u64 event_code, + int state) +{ + int err; + u8 data; + u8 mask; + + bool new_int_state; + + struct st_sensor_data *sdata = iio_priv(indio_dev); + mask = 1 << ((IIO_EVENT_CODE_EXTRACT_MODIFIER(event_code) << 1) - 1); + + err = st_sensors_write_data_with_mask(indio_dev, + ST_ACCEL_2_INT1_CFG_ADDR, + mask, state); + if (unlikely(err < 0)) + goto write_event_err; + + err = sdata->tf->read_byte(&sdata->tb, sdata->dev, + ST_ACCEL_2_INT1_CFG_ADDR, &data); + if (unlikely(err < 0)) + goto write_event_err; + + new_int_state = data & (ST_ACCEL_2_INT_CFG_XHIE_EN | + ST_ACCEL_2_INT_CFG_YHIE_EN | + ST_ACCEL_2_INT_CFG_ZHIE_EN); + err = lis331dlh_intel_qrk_configure_threshold_interrupt( + indio_dev, new_int_state); + +write_event_err: + return err; +} + +/* Configure the INT2 pin to fire an interrupt on a threshold high event. INT2 + * should be wired to a suspend well IRQ to wake up the host. + */ +static int lis331dlh_intel_qrk_enable_wakeup_interrupt( + struct iio_dev *indio_dev) +{ + int err = 0; + u8 data; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + err = sdata->tf->write_byte(&sdata->tb, sdata->dev, + ST_ACCEL_2_INT2_THRESH_ADDR, + QRK_ACCEL_INT2_WAKEUP_THRESH_VAL); + if (unlikely(err < 0)) + goto enable_wakeup_int_err; + + /* Latch interrupt request on INT2 */ + err = st_sensors_write_data_with_mask( + indio_dev, ST_ACCEL_2_DRDY_IRQ_ADDR, + ST_ACCEL_2_INT_LIR_MASK, 1); + if (unlikely(err < 0)) + goto enable_wakeup_int_err; + + err = sdata->tf->write_byte(&sdata->tb, sdata->dev, + ST_ACCEL_2_INT2_DURATION_ADDR, 0); + if (unlikely(err < 0)) + goto enable_wakeup_int_err; + + err = sdata->tf->write_byte(&sdata->tb, sdata->dev, + ST_ACCEL_2_INT2_CFG_ADDR, + ST_ACCEL_2_INT_CFG_XHIE_EN | + ST_ACCEL_2_INT_CFG_YHIE_EN); + if (unlikely(err < 0)) + goto enable_wakeup_int_err; + + /* Clean ST_ACCEL_2_INT2_SRC */ + + err = sdata->tf->read_byte(&sdata->tb, sdata->dev, + ST_ACCEL_2_INT2_SRC_ADDR, + &data); + +enable_wakeup_int_err: + return err; +} + +static int lis331dlh_intel_qrk_disable_wakeup_interrupt( + struct iio_dev *indio_dev) +{ + int err = 0; + u8 data; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + err = sdata->tf->write_byte(&sdata->tb, sdata->dev, + ST_ACCEL_2_INT2_CFG_ADDR, + 0); + if (unlikely(err < 0)) + goto disable_wakeup_int_err; + + /* Clean ST_ACCEL_2_INT2_SRC */ + err = sdata->tf->read_byte(&sdata->tb, sdata->dev, + ST_ACCEL_2_INT2_SRC_ADDR, + &data); + if (unlikely(err < 0)) + goto disable_wakeup_int_err; + +disable_wakeup_int_err: + return err; +} + +static int lis331dlh_intel_qrk_handle_wakeup_interrupt( + struct iio_dev *indio_dev) +{ + int err; + u8 data; + struct st_sensor_data *sdata = iio_priv(indio_dev); + s64 timestamp = iio_get_time_ns(); + + err = sdata->tf->read_byte(&sdata->tb, sdata->dev, + ST_ACCEL_2_INT2_SRC_ADDR, + &data); + if (unlikely(err < 0)) + goto handle_wakeup_int_err; + + if (data & ST_ACCEL_2_INT_IA_MASK) { + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_X_OR_Y_OR_Z, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + timestamp); + } + +handle_wakeup_int_err: + return err; +} + +static const struct iio_info accel_info = { + .driver_module = THIS_MODULE, + .attrs = &lis331dlh_intel_qrk_attribute_group, + .read_raw = &lis331dlh_intel_qrk_read_raw, + .write_raw = &lis331dlh_intel_qrk_write_raw, + .read_event_config = &lis331dlh_intel_qrk_read_event_config, + .write_event_config = &lis331dlh_intel_qrk_write_event_config, + .read_event_value = &lis331dlh_intel_qrk_read_event_value, + .write_event_value = &lis331dlh_intel_qrk_write_event_value, +}; + +static const struct iio_chan_spec st_accel_12bit_channels[] = { + QRK_ACCEL_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_X, IIO_MOD_X, IIO_LE, + ST_SENSORS_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_X_L_ADDR), + QRK_ACCEL_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Y, IIO_MOD_Y, IIO_LE, + ST_SENSORS_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Y_L_ADDR), + QRK_ACCEL_LSM_CHANNELS(IIO_ACCEL, ST_SENSORS_SCAN_Z, IIO_MOD_Z, IIO_LE, + ST_SENSORS_DEFAULT_12_REALBITS, ST_ACCEL_DEFAULT_OUT_Z_L_ADDR), + IIO_CHAN_SOFT_TIMESTAMP(3) +}; + +static struct st_sensors lis331dlh_intel_qrk_sensor = { + .wai = ST_ACCEL_2_WAI_EXP, + .sensors_supported = { + [0] = "lis331dlh_qrk", + }, + .ch = (struct iio_chan_spec *)st_accel_12bit_channels, + .odr = { + .addr = ST_ACCEL_2_ODR_ADDR, + .mask = ST_ACCEL_2_ODR_MASK, + .odr_avl = { + { 50, ST_ACCEL_2_ODR_AVL_50HZ_VAL, }, + { 100, ST_ACCEL_2_ODR_AVL_100HZ_VAL, }, + { 400, ST_ACCEL_2_ODR_AVL_400HZ_VAL, }, + { 1000, ST_ACCEL_2_ODR_AVL_1000HZ_VAL, }, + }, + }, + .pw = { + .addr = ST_ACCEL_2_PW_ADDR, + .mask = ST_ACCEL_2_PW_MASK, + .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE, + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .enable_axis = { + .addr = ST_SENSORS_DEFAULT_AXIS_ADDR, + .mask = ST_SENSORS_DEFAULT_AXIS_MASK, + }, + .fs = { + .addr = ST_ACCEL_2_FS_ADDR, + .mask = ST_ACCEL_2_FS_MASK, + .fs_avl = { + [0] = { + .num = ST_ACCEL_FS_AVL_2G, + .value = ST_ACCEL_2_FS_AVL_2_VAL, + .gain = ST_ACCEL_2_FS_AVL_2_GAIN, + }, + [1] = { + .num = ST_ACCEL_FS_AVL_4G, + .value = ST_ACCEL_2_FS_AVL_4_VAL, + .gain = ST_ACCEL_2_FS_AVL_4_GAIN, + }, + [2] = { + .num = ST_ACCEL_FS_AVL_8G, + .value = ST_ACCEL_2_FS_AVL_8_VAL, + .gain = ST_ACCEL_2_FS_AVL_8_GAIN, + }, + }, + }, + .bdu = { + .addr = ST_ACCEL_2_BDU_ADDR, + .mask = ST_ACCEL_2_BDU_MASK, + }, + .drdy_irq = { + .addr = ST_ACCEL_2_DRDY_IRQ_ADDR, + .mask = ST_ACCEL_2_DRDY_IRQ_MASK, + }, + .multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT, + .bootime = 2, +}; + +static int lis331dlh_intel_qrk_probe( + struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct st_sensor_data *adata; + struct lis331dlh_intel_qrk_platform_data *pdata; + int ret = 0; + + indio_dev = iio_device_alloc(sizeof(*adata)); + if (unlikely(indio_dev == NULL)) { + ret = -ENOMEM; + goto iio_device_alloc_error; + } + + i2c_set_clientdata(client, indio_dev); + indio_dev->dev.parent = &client->dev; + indio_dev->name = client->name; + + adata = iio_priv(indio_dev); + adata->dev = &client->dev; + + pdata = client->dev.platform_data; + if (unlikely(!pdata)) { + pr_err("No platform data provided\n"); + goto lis331dlh_intel_qrk_init_err; + } + + ret = gpio_to_irq(pdata->irq1_pin); + if (unlikely(ret < 0)) { + pr_err( + "Failed to obtain valid IRQ for GPIO %d, " + "gpio_to_irq returned %d\n", + pdata->irq1_pin, ret); + goto lis331dlh_intel_qrk_init_err; + } + to_i2c_client(adata->dev)->irq = ret; + + st_sensors_i2c_configure(indio_dev, client, adata); + + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &accel_info; + + ret = st_sensors_check_device_support(indio_dev, + 1, &lis331dlh_intel_qrk_sensor); + if (unlikely(ret < 0)) + goto lis331dlh_intel_qrk_init_err; + + indio_dev->channels = adata->sensor->ch; + indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS; + + adata->multiread_bit = adata->sensor->multi_read_bit; + adata->current_fullscale = (struct st_sensor_fullscale_avl *) + &adata->sensor->fs.fs_avl[0]; + adata->odr = adata->sensor->odr.odr_avl[0].hz; + + adata->sensor->drdy_irq.ig1.en_mask = QRK_ACCEL_INT1_DISABLED; + + ret = st_sensors_init_sensor(indio_dev); + if (unlikely(ret < 0)) + goto lis331dlh_intel_qrk_init_err; + + ret = st_sensors_set_enable(indio_dev, true); + if (unlikely(ret < 0)) + goto lis331dlh_intel_qrk_init_err; + + ret = iio_device_register(indio_dev); + if (unlikely(ret)) + goto lis331dlh_intel_qrk_init_err; + + return 0; + +lis331dlh_intel_qrk_init_err: + iio_device_free(indio_dev); +iio_device_alloc_error: + return ret; +} + +static int lis331dlh_intel_qrk_remove( + struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct st_sensor_data *adata = iio_priv(indio_dev); + + st_sensors_set_enable(indio_dev, false); + + if (adata->sensor->drdy_irq.ig1.en_mask == QRK_ACCEL_INT1_ENABLED) + free_irq(adata->get_irq_data_ready(indio_dev), indio_dev); + + iio_device_unregister(indio_dev); + + iio_device_free(indio_dev); + + return 0; +} + +#ifdef CONFIG_PM +static int lis331dlh_intel_qrk_suspend( + struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + lis331dlh_intel_qrk_enable_wakeup_interrupt(indio_dev); + + return 0; +} + +static int lis331dlh_intel_qrk_resume( + struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + lis331dlh_intel_qrk_handle_wakeup_interrupt(indio_dev); + lis331dlh_intel_qrk_disable_wakeup_interrupt(indio_dev); + + return 0; +} + +static const struct dev_pm_ops lis331dlh_intel_qrk_pm_ops = { + .suspend = lis331dlh_intel_qrk_suspend, + .resume = lis331dlh_intel_qrk_resume, +}; + +#define LIS331DLH_INTEL_QRK_PM_OPS (&lis331dlh_intel_qrk_pm_ops) +#else +#define LIS331DLH_INTEL_QRK_PM_OPS NULL +#endif + +static const struct i2c_device_id lis331dlh_intel_qrk_id_table[] = { + { "lis331dlh_qrk" }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, lis331dlh_intel_qrk_id_table); + +static struct i2c_driver lis331dlh_intel_qrk_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "lis331dlh_qrk", + .pm = LIS331DLH_INTEL_QRK_PM_OPS, + }, + .probe = lis331dlh_intel_qrk_probe, + .remove = lis331dlh_intel_qrk_remove, + .id_table = lis331dlh_intel_qrk_id_table, +}; + +module_i2c_driver(lis331dlh_intel_qrk_driver); + +MODULE_AUTHOR("Wojciech Ziemba "); +MODULE_DESCRIPTION("STMicroelectronics LIS331DLH accelerometer i2c driver for Intel Quark platform"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig index ed45ee5..64bcb14 100644 --- a/drivers/iio/common/Kconfig +++ b/drivers/iio/common/Kconfig @@ -3,3 +3,4 @@ # source "drivers/iio/common/hid-sensors/Kconfig" +source "drivers/iio/common/st_sensors/Kconfig" \ No newline at end of file diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile index 8158400..c2352be 100644 --- a/drivers/iio/common/Makefile +++ b/drivers/iio/common/Makefile @@ -7,3 +7,4 @@ # obj-y += hid-sensors/ +obj-y += st_sensors/ diff --git a/drivers/iio/common/st_sensors/Kconfig b/drivers/iio/common/st_sensors/Kconfig new file mode 100644 index 0000000..865f1ca --- /dev/null +++ b/drivers/iio/common/st_sensors/Kconfig @@ -0,0 +1,14 @@ +# +# STMicroelectronics sensors common library +# + +config IIO_ST_SENSORS_I2C + tristate + +config IIO_ST_SENSORS_SPI + tristate + +config IIO_ST_SENSORS_CORE + tristate + select IIO_ST_SENSORS_I2C if I2C + select IIO_ST_SENSORS_SPI if SPI_MASTER diff --git a/drivers/iio/common/st_sensors/Makefile b/drivers/iio/common/st_sensors/Makefile new file mode 100644 index 0000000..9f3e24f --- /dev/null +++ b/drivers/iio/common/st_sensors/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for the STMicroelectronics sensor common modules. +# + +obj-$(CONFIG_IIO_ST_SENSORS_I2C) += st_sensors_i2c.o +obj-$(CONFIG_IIO_ST_SENSORS_SPI) += st_sensors_spi.o +obj-$(CONFIG_IIO_ST_SENSORS_CORE) += st_sensors.o +st_sensors-y := st_sensors_core.o +st_sensors-$(CONFIG_IIO_BUFFER) += st_sensors_buffer.o +st_sensors-$(CONFIG_IIO_TRIGGER) += st_sensors_trigger.o diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c new file mode 100644 index 0000000..a269b7d --- /dev/null +++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c @@ -0,0 +1,115 @@ +/* + * STMicroelectronics sensors buffer library driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf) +{ + int i, n = 0, len; + u8 addr[ST_SENSORS_NUMBER_DATA_CHANNELS]; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + for (i = 0; i < ST_SENSORS_NUMBER_DATA_CHANNELS; i++) { + if (test_bit(i, indio_dev->active_scan_mask)) { + addr[n] = indio_dev->channels[i].address; + n++; + } + } + switch (n) { + case 1: + len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev, + addr[0], ST_SENSORS_BYTE_FOR_CHANNEL, buf, + sdata->multiread_bit); + break; + case 2: + if ((addr[1] - addr[0]) == ST_SENSORS_BYTE_FOR_CHANNEL) { + len = sdata->tf->read_multiple_byte(&sdata->tb, + sdata->dev, addr[0], + ST_SENSORS_BYTE_FOR_CHANNEL*n, + buf, sdata->multiread_bit); + } else { + u8 rx_array[ST_SENSORS_BYTE_FOR_CHANNEL* + ST_SENSORS_NUMBER_DATA_CHANNELS]; + len = sdata->tf->read_multiple_byte(&sdata->tb, + sdata->dev, addr[0], + ST_SENSORS_BYTE_FOR_CHANNEL* + ST_SENSORS_NUMBER_DATA_CHANNELS, + rx_array, sdata->multiread_bit); + if (len < 0) + goto read_data_channels_error; + + for (i = 0; n + i < sizeof(rx_array); i++){ + if (i < n) + buf[i] = rx_array[i]; + else + buf[i] = rx_array[n + i]; + } + len = ST_SENSORS_BYTE_FOR_CHANNEL*n; + } + break; + case 3: + len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev, + addr[0], ST_SENSORS_BYTE_FOR_CHANNEL* + ST_SENSORS_NUMBER_DATA_CHANNELS, + buf, sdata->multiread_bit); + break; + default: + len = -EINVAL; + goto read_data_channels_error; + } + if (len != ST_SENSORS_BYTE_FOR_CHANNEL*n) { + len = -EIO; + goto read_data_channels_error; + } + +read_data_channels_error: + return len; +} +EXPORT_SYMBOL(st_sensors_get_buffer_element); + +irqreturn_t st_sensors_trigger_handler(int irq, void *p) +{ + int len; + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + len = st_sensors_get_buffer_element(indio_dev, sdata->buffer_data); + if (len < 0) + goto st_sensors_get_buffer_element_error; + + if (indio_dev->scan_timestamp) + *(s64 *)((u8 *)sdata->buffer_data + + ALIGN(len, sizeof(s64))) = pf->timestamp; + + iio_push_to_buffers(indio_dev, sdata->buffer_data); + +st_sensors_get_buffer_element_error: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL(st_sensors_trigger_handler); + +MODULE_AUTHOR("Denis Ciocca "); +MODULE_DESCRIPTION("STMicroelectronics ST-sensors buffer"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c new file mode 100644 index 0000000..945a55b --- /dev/null +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -0,0 +1,447 @@ +/* + * STMicroelectronics sensors core library driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include + +#include + + +#define ST_SENSORS_WAI_ADDRESS 0x0f + +int st_sensors_write_data_with_mask(struct iio_dev *indio_dev, + u8 reg_addr, u8 mask, u8 data) +{ + int err; + u8 new_data; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + err = sdata->tf->read_byte(&sdata->tb, sdata->dev, reg_addr, &new_data); + if (err < 0) + goto st_sensors_write_data_with_mask_error; + + new_data = ((new_data & (~mask)) | ((data << __ffs(mask)) & mask)); + err = sdata->tf->write_byte(&sdata->tb, sdata->dev, reg_addr, new_data); + +st_sensors_write_data_with_mask_error: + return err; +} +EXPORT_SYMBOL(st_sensors_write_data_with_mask); + +static int st_sensors_match_odr(struct st_sensors *sensor, + unsigned int odr, struct st_sensor_odr_avl *odr_out) +{ + int i, ret = -EINVAL; + + for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) { + if (sensor->odr.odr_avl[i].hz == 0) + goto st_sensors_match_odr_error; + + if (sensor->odr.odr_avl[i].hz == odr) { + odr_out->hz = sensor->odr.odr_avl[i].hz; + odr_out->value = sensor->odr.odr_avl[i].value; + ret = 0; + break; + } + } + +st_sensors_match_odr_error: + return ret; +} + +int st_sensors_set_odr(struct iio_dev *indio_dev, unsigned int odr) +{ + int err; + struct st_sensor_odr_avl odr_out = {0, 0}; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + err = st_sensors_match_odr(sdata->sensor, odr, &odr_out); + if (err < 0) + goto st_sensors_match_odr_error; + + if ((sdata->sensor->odr.addr == sdata->sensor->pw.addr) && + (sdata->sensor->odr.mask == sdata->sensor->pw.mask)) { + if (sdata->enabled == true) { + err = st_sensors_write_data_with_mask(indio_dev, + sdata->sensor->odr.addr, + sdata->sensor->odr.mask, + odr_out.value); + } else { + err = 0; + } + } else { + err = st_sensors_write_data_with_mask(indio_dev, + sdata->sensor->odr.addr, sdata->sensor->odr.mask, + odr_out.value); + } + if (err >= 0) + sdata->odr = odr_out.hz; + +st_sensors_match_odr_error: + return err; +} +EXPORT_SYMBOL(st_sensors_set_odr); + +static int st_sensors_match_fs(struct st_sensors *sensor, + unsigned int fs, int *index_fs_avl) +{ + int i, ret = -EINVAL; + + for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) { + if (sensor->fs.fs_avl[i].num == 0) + goto st_sensors_match_odr_error; + + if (sensor->fs.fs_avl[i].num == fs) { + *index_fs_avl = i; + ret = 0; + break; + } + } + +st_sensors_match_odr_error: + return ret; +} + +static int st_sensors_set_fullscale(struct iio_dev *indio_dev, unsigned int fs) +{ + int err, i = 0; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + err = st_sensors_match_fs(sdata->sensor, fs, &i); + if (err < 0) + goto st_accel_set_fullscale_error; + + err = st_sensors_write_data_with_mask(indio_dev, + sdata->sensor->fs.addr, + sdata->sensor->fs.mask, + sdata->sensor->fs.fs_avl[i].value); + if (err < 0) + goto st_accel_set_fullscale_error; + + sdata->current_fullscale = (struct st_sensor_fullscale_avl *) + &sdata->sensor->fs.fs_avl[i]; + return err; + +st_accel_set_fullscale_error: + dev_err(&indio_dev->dev, "failed to set new fullscale.\n"); + return err; +} + +int st_sensors_set_enable(struct iio_dev *indio_dev, bool enable) +{ + u8 tmp_value; + int err = -EINVAL; + bool found = false; + struct st_sensor_odr_avl odr_out = {0, 0}; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + if (enable) { + tmp_value = sdata->sensor->pw.value_on; + if ((sdata->sensor->odr.addr == sdata->sensor->pw.addr) && + (sdata->sensor->odr.mask == sdata->sensor->pw.mask)) { + err = st_sensors_match_odr(sdata->sensor, + sdata->odr, &odr_out); + if (err < 0) + goto set_enable_error; + tmp_value = odr_out.value; + found = true; + } + err = st_sensors_write_data_with_mask(indio_dev, + sdata->sensor->pw.addr, + sdata->sensor->pw.mask, tmp_value); + if (err < 0) + goto set_enable_error; + + sdata->enabled = true; + + if (found) + sdata->odr = odr_out.hz; + } else { + err = st_sensors_write_data_with_mask(indio_dev, + sdata->sensor->pw.addr, + sdata->sensor->pw.mask, + sdata->sensor->pw.value_off); + if (err < 0) + goto set_enable_error; + + sdata->enabled = false; + } + +set_enable_error: + return err; +} +EXPORT_SYMBOL(st_sensors_set_enable); + +int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable) +{ + struct st_sensor_data *sdata = iio_priv(indio_dev); + + return st_sensors_write_data_with_mask(indio_dev, + sdata->sensor->enable_axis.addr, + sdata->sensor->enable_axis.mask, axis_enable); +} +EXPORT_SYMBOL(st_sensors_set_axis_enable); + +int st_sensors_init_sensor(struct iio_dev *indio_dev) +{ + int err; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + mutex_init(&sdata->tb.buf_lock); + + err = st_sensors_set_enable(indio_dev, false); + if (err < 0) + goto init_error; + + err = st_sensors_set_fullscale(indio_dev, + sdata->current_fullscale->num); + if (err < 0) + goto init_error; + + err = st_sensors_set_odr(indio_dev, sdata->odr); + if (err < 0) + goto init_error; + + /* set BDU */ + err = st_sensors_write_data_with_mask(indio_dev, + sdata->sensor->bdu.addr, sdata->sensor->bdu.mask, true); + if (err < 0) + goto init_error; + + err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS); + +init_error: + return err; +} +EXPORT_SYMBOL(st_sensors_init_sensor); + +int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable) +{ + int err; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + /* Enable/Disable the interrupt generator 1. */ + if (sdata->sensor->drdy_irq.ig1.en_addr > 0) { + err = st_sensors_write_data_with_mask(indio_dev, + sdata->sensor->drdy_irq.ig1.en_addr, + sdata->sensor->drdy_irq.ig1.en_mask, (int)enable); + if (err < 0) + goto st_accel_set_dataready_irq_error; + } + + /* Enable/Disable the interrupt generator for data ready. */ + err = st_sensors_write_data_with_mask(indio_dev, + sdata->sensor->drdy_irq.addr, + sdata->sensor->drdy_irq.mask, (int)enable); + +st_accel_set_dataready_irq_error: + return err; +} +EXPORT_SYMBOL(st_sensors_set_dataready_irq); + +int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev, int scale) +{ + int err = -EINVAL, i; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) { + if ((sdata->sensor->fs.fs_avl[i].gain == scale) && + (sdata->sensor->fs.fs_avl[i].gain != 0)) { + err = 0; + break; + } + } + if (err < 0) + goto st_sensors_match_scale_error; + + err = st_sensors_set_fullscale(indio_dev, + sdata->sensor->fs.fs_avl[i].num); + +st_sensors_match_scale_error: + return err; +} +EXPORT_SYMBOL(st_sensors_set_fullscale_by_gain); + +int st_sensors_read_axis_data(struct iio_dev *indio_dev, + u8 ch_addr, int *data) +{ + int err; + u8 outdata[ST_SENSORS_BYTE_FOR_CHANNEL]; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + err = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev, + ch_addr, ST_SENSORS_BYTE_FOR_CHANNEL, + outdata, sdata->multiread_bit); + if (err < 0) + goto read_error; + + *data = (s16)get_unaligned_le16(outdata); + +read_error: + return err; +} +EXPORT_SYMBOL(st_sensors_read_axis_data); + +int st_sensors_read_info_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *ch, int *val) +{ + int err; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + mutex_lock(&indio_dev->mlock); + if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) { + err = -EBUSY; + goto read_error; + } else { + err = st_sensors_set_enable(indio_dev, true); + if (err < 0) + goto read_error; + + msleep((sdata->sensor->bootime * 1000) / sdata->odr); + err = st_sensors_read_axis_data(indio_dev, ch->address, val); + if (err < 0) + goto read_error; + + *val = *val >> ch->scan_type.shift; + } + mutex_unlock(&indio_dev->mlock); + + return err; + +read_error: + mutex_unlock(&indio_dev->mlock); + return err; +} +EXPORT_SYMBOL(st_sensors_read_info_raw); + +int st_sensors_check_device_support(struct iio_dev *indio_dev, + int num_sensors_list, const struct st_sensors *sensors) +{ + u8 wai; + int i, n, err; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + err = sdata->tf->read_byte(&sdata->tb, sdata->dev, + ST_SENSORS_DEFAULT_WAI_ADDRESS, &wai); + if (err < 0) { + dev_err(&indio_dev->dev, "failed to read Who-Am-I register.\n"); + goto read_wai_error; + } + + for (i = 0; i < num_sensors_list; i++) { + if (sensors[i].wai == wai) + break; + } + if (i == num_sensors_list) + goto device_not_supported; + + for (n = 0; n < ARRAY_SIZE(sensors[i].sensors_supported); n++) { + if (strcmp(indio_dev->name, + &sensors[i].sensors_supported[n][0]) == 0) + break; + } + if (n == ARRAY_SIZE(sensors[i].sensors_supported)) { + dev_err(&indio_dev->dev, "device name and WhoAmI mismatch.\n"); + goto sensor_name_mismatch; + } + + sdata->sensor = (struct st_sensors *)&sensors[i]; + + return i; + +device_not_supported: + dev_err(&indio_dev->dev, "device not supported: WhoAmI (0x%x).\n", wai); +sensor_name_mismatch: + err = -ENODEV; +read_wai_error: + return err; +} +EXPORT_SYMBOL(st_sensors_check_device_support); + +ssize_t st_sensors_sysfs_get_sampling_frequency(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct st_sensor_data *adata = iio_priv(dev_get_drvdata(dev)); + + return sprintf(buf, "%d\n", adata->odr); +} +EXPORT_SYMBOL(st_sensors_sysfs_get_sampling_frequency); + +ssize_t st_sensors_sysfs_set_sampling_frequency(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int err; + unsigned int odr; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + err = kstrtoint(buf, 10, &odr); + if (err < 0) + goto conversion_error; + + mutex_lock(&indio_dev->mlock); + err = st_sensors_set_odr(indio_dev, odr); + mutex_unlock(&indio_dev->mlock); + +conversion_error: + return err < 0 ? err : size; +} +EXPORT_SYMBOL(st_sensors_sysfs_set_sampling_frequency); + +ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i, len = 0; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct st_sensor_data *sdata = iio_priv(indio_dev); + + mutex_lock(&indio_dev->mlock); + for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) { + if (sdata->sensor->odr.odr_avl[i].hz == 0) + break; + + len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", + sdata->sensor->odr.odr_avl[i].hz); + } + mutex_unlock(&indio_dev->mlock); + buf[len - 1] = '\n'; + + return len; +} +EXPORT_SYMBOL(st_sensors_sysfs_sampling_frequency_avail); + +ssize_t st_sensors_sysfs_scale_avail(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i, len = 0; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct st_sensor_data *sdata = iio_priv(indio_dev); + + mutex_lock(&indio_dev->mlock); + for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) { + if (sdata->sensor->fs.fs_avl[i].num == 0) + break; + + len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ", + sdata->sensor->fs.fs_avl[i].gain); + } + mutex_unlock(&indio_dev->mlock); + buf[len - 1] = '\n'; + + return len; +} +EXPORT_SYMBOL(st_sensors_sysfs_scale_avail); + +MODULE_AUTHOR("Denis Ciocca "); +MODULE_DESCRIPTION("STMicroelectronics ST-sensors core"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c new file mode 100644 index 0000000..38af944 --- /dev/null +++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c @@ -0,0 +1,81 @@ +/* + * STMicroelectronics sensors i2c library driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include + +#include + + +#define ST_SENSORS_I2C_MULTIREAD 0x80 + +static unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev) +{ + struct st_sensor_data *sdata = iio_priv(indio_dev); + + return to_i2c_client(sdata->dev)->irq; +} + +static int st_sensors_i2c_read_byte(struct st_sensor_transfer_buffer *tb, + struct device *dev, u8 reg_addr, u8 *res_byte) +{ + int err; + + err = i2c_smbus_read_byte_data(to_i2c_client(dev), reg_addr); + if (err < 0) + goto st_accel_i2c_read_byte_error; + + *res_byte = err & 0xff; + +st_accel_i2c_read_byte_error: + return err < 0 ? err : 0; +} + +static int st_sensors_i2c_read_multiple_byte( + struct st_sensor_transfer_buffer *tb, struct device *dev, + u8 reg_addr, int len, u8 *data, bool multiread_bit) +{ + if (multiread_bit) + reg_addr |= ST_SENSORS_I2C_MULTIREAD; + + return i2c_smbus_read_i2c_block_data(to_i2c_client(dev), + reg_addr, len, data); +} + +static int st_sensors_i2c_write_byte(struct st_sensor_transfer_buffer *tb, + struct device *dev, u8 reg_addr, u8 data) +{ + return i2c_smbus_write_byte_data(to_i2c_client(dev), reg_addr, data); +} + +static const struct st_sensor_transfer_function st_sensors_tf_i2c = { + .read_byte = st_sensors_i2c_read_byte, + .write_byte = st_sensors_i2c_write_byte, + .read_multiple_byte = st_sensors_i2c_read_multiple_byte, +}; + +void st_sensors_i2c_configure(struct iio_dev *indio_dev, + struct i2c_client *client, struct st_sensor_data *sdata) +{ + i2c_set_clientdata(client, indio_dev); + + indio_dev->dev.parent = &client->dev; + indio_dev->name = client->name; + + sdata->tf = &st_sensors_tf_i2c; + sdata->get_irq_data_ready = st_sensors_i2c_get_irq; +} +EXPORT_SYMBOL(st_sensors_i2c_configure); + +MODULE_AUTHOR("Denis Ciocca "); +MODULE_DESCRIPTION("STMicroelectronics ST-sensors i2c driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/common/st_sensors/st_sensors_spi.c b/drivers/iio/common/st_sensors/st_sensors_spi.c new file mode 100644 index 0000000..f0aa2f1 --- /dev/null +++ b/drivers/iio/common/st_sensors/st_sensors_spi.c @@ -0,0 +1,128 @@ +/* + * STMicroelectronics sensors spi library driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include + +#include + + +#define ST_SENSORS_SPI_MULTIREAD 0xc0 +#define ST_SENSORS_SPI_READ 0x80 + +static unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev) +{ + struct st_sensor_data *sdata = iio_priv(indio_dev); + + return to_spi_device(sdata->dev)->irq; +} + +static int st_sensors_spi_read(struct st_sensor_transfer_buffer *tb, + struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit) +{ + struct spi_message msg; + int err; + + struct spi_transfer xfers[] = { + { + .tx_buf = tb->tx_buf, + .bits_per_word = 8, + .len = 1, + }, + { + .rx_buf = tb->rx_buf, + .bits_per_word = 8, + .len = len, + } + }; + + mutex_lock(&tb->buf_lock); + if ((multiread_bit) && (len > 1)) + tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_MULTIREAD; + else + tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_READ; + + spi_message_init(&msg); + spi_message_add_tail(&xfers[0], &msg); + spi_message_add_tail(&xfers[1], &msg); + err = spi_sync(to_spi_device(dev), &msg); + if (err) + goto acc_spi_read_error; + + memcpy(data, tb->rx_buf, len*sizeof(u8)); + mutex_unlock(&tb->buf_lock); + return len; + +acc_spi_read_error: + mutex_unlock(&tb->buf_lock); + return err; +} + +static int st_sensors_spi_read_byte(struct st_sensor_transfer_buffer *tb, + struct device *dev, u8 reg_addr, u8 *res_byte) +{ + return st_sensors_spi_read(tb, dev, reg_addr, 1, res_byte, false); +} + +static int st_sensors_spi_read_multiple_byte( + struct st_sensor_transfer_buffer *tb, struct device *dev, + u8 reg_addr, int len, u8 *data, bool multiread_bit) +{ + return st_sensors_spi_read(tb, dev, reg_addr, len, data, multiread_bit); +} + +static int st_sensors_spi_write_byte(struct st_sensor_transfer_buffer *tb, + struct device *dev, u8 reg_addr, u8 data) +{ + struct spi_message msg; + int err; + + struct spi_transfer xfers = { + .tx_buf = tb->tx_buf, + .bits_per_word = 8, + .len = 2, + }; + + mutex_lock(&tb->buf_lock); + tb->tx_buf[0] = reg_addr; + tb->tx_buf[1] = data; + + spi_message_init(&msg); + spi_message_add_tail(&xfers, &msg); + err = spi_sync(to_spi_device(dev), &msg); + mutex_unlock(&tb->buf_lock); + + return err; +} + +static const struct st_sensor_transfer_function st_sensors_tf_spi = { + .read_byte = st_sensors_spi_read_byte, + .write_byte = st_sensors_spi_write_byte, + .read_multiple_byte = st_sensors_spi_read_multiple_byte, +}; + +void st_sensors_spi_configure(struct iio_dev *indio_dev, + struct spi_device *spi, struct st_sensor_data *sdata) +{ + spi_set_drvdata(spi, indio_dev); + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi->modalias; + + sdata->tf = &st_sensors_tf_spi; + sdata->get_irq_data_ready = st_sensors_spi_get_irq; +} +EXPORT_SYMBOL(st_sensors_spi_configure); + +MODULE_AUTHOR("Denis Ciocca "); +MODULE_DESCRIPTION("STMicroelectronics ST-sensors spi driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c new file mode 100644 index 0000000..139ed03 --- /dev/null +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -0,0 +1,77 @@ +/* + * STMicroelectronics sensors trigger library driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include + +#include + + +int st_sensors_allocate_trigger(struct iio_dev *indio_dev, + const struct iio_trigger_ops *trigger_ops) +{ + int err; + struct st_sensor_data *sdata = iio_priv(indio_dev); + + sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name); + if (sdata->trig == NULL) { + err = -ENOMEM; + dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n"); + goto iio_trigger_alloc_error; + } + + err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev), + iio_trigger_generic_data_rdy_poll, + NULL, + IRQF_TRIGGER_RISING, + sdata->trig->name, + sdata->trig); + if (err) + goto request_irq_error; + + sdata->trig->private_data = indio_dev; + sdata->trig->ops = trigger_ops; + sdata->trig->dev.parent = sdata->dev; + + err = iio_trigger_register(sdata->trig); + if (err < 0) { + dev_err(&indio_dev->dev, "failed to register iio trigger.\n"); + goto iio_trigger_register_error; + } + indio_dev->trig = sdata->trig; + + return 0; + +iio_trigger_register_error: + free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig); +request_irq_error: + iio_trigger_free(sdata->trig); +iio_trigger_alloc_error: + return err; +} +EXPORT_SYMBOL(st_sensors_allocate_trigger); + +void st_sensors_deallocate_trigger(struct iio_dev *indio_dev) +{ + struct st_sensor_data *sdata = iio_priv(indio_dev); + + iio_trigger_unregister(sdata->trig); + free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig); + iio_trigger_free(sdata->trig); +} +EXPORT_SYMBOL(st_sensors_deallocate_trigger); + +MODULE_AUTHOR("Denis Ciocca "); +MODULE_DESCRIPTION("STMicroelectronics ST-sensors trigger"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index aaadd32..7b8d510 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -119,8 +119,8 @@ static ssize_t iio_scan_el_show(struct device *dev, int ret; struct iio_dev *indio_dev = dev_to_iio_dev(dev); - ret = test_bit(to_iio_dev_attr(attr)->address, - indio_dev->buffer->scan_mask); + ret = abs(test_bit(to_iio_dev_attr(attr)->address, + indio_dev->buffer->scan_mask)); return sprintf(buf, "%d\n", ret); } @@ -762,7 +762,7 @@ int iio_scan_mask_query(struct iio_dev *indio_dev, if (!buffer->scan_mask) return 0; - return test_bit(bit, buffer->scan_mask); + return abs(test_bit(bit, buffer->scan_mask)); }; EXPORT_SYMBOL_GPL(iio_scan_mask_query); diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h new file mode 100644 index 0000000..56d0495 --- /dev/null +++ b/include/linux/iio/common/st_sensors.h @@ -0,0 +1,289 @@ +/* + * STMicroelectronics sensors library driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca + * + * Licensed under the GPL-2. + */ + +#ifndef ST_SENSORS_H +#define ST_SENSORS_H + +#include +#include +#include +#include + +#define ST_SENSORS_TX_MAX_LENGTH 2 +#define ST_SENSORS_RX_MAX_LENGTH 6 + +#define ST_SENSORS_ODR_LIST_MAX 10 +#define ST_SENSORS_FULLSCALE_AVL_MAX 10 + +#define ST_SENSORS_NUMBER_ALL_CHANNELS 4 +#define ST_SENSORS_NUMBER_DATA_CHANNELS 3 +#define ST_SENSORS_ENABLE_ALL_AXIS 0x07 +#define ST_SENSORS_BYTE_FOR_CHANNEL 2 +#define ST_SENSORS_SCAN_X 0 +#define ST_SENSORS_SCAN_Y 1 +#define ST_SENSORS_SCAN_Z 2 +#define ST_SENSORS_DEFAULT_12_REALBITS 12 +#define ST_SENSORS_DEFAULT_16_REALBITS 16 +#define ST_SENSORS_DEFAULT_POWER_ON_VALUE 0x01 +#define ST_SENSORS_DEFAULT_POWER_OFF_VALUE 0x00 +#define ST_SENSORS_DEFAULT_WAI_ADDRESS 0x0f +#define ST_SENSORS_DEFAULT_AXIS_ADDR 0x20 +#define ST_SENSORS_DEFAULT_AXIS_MASK 0x07 +#define ST_SENSORS_DEFAULT_AXIS_N_BIT 3 + +#define ST_SENSORS_MAX_NAME 17 +#define ST_SENSORS_MAX_4WAI 7 + +#define ST_SENSORS_LSM_CHANNELS(device_type, index, mod, endian, bits, addr) \ +{ \ + .type = device_type, \ + .modified = 1, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ + IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \ + .scan_index = index, \ + .channel2 = mod, \ + .address = addr, \ + .scan_type = { \ + .sign = 's', \ + .realbits = bits, \ + .shift = 16 - bits, \ + .storagebits = 16, \ + .endianness = endian, \ + }, \ +} + +#define ST_SENSOR_DEV_ATTR_SAMP_FREQ() \ + IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, \ + st_sensors_sysfs_get_sampling_frequency, \ + st_sensors_sysfs_set_sampling_frequency) + +#define ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL() \ + IIO_DEV_ATTR_SAMP_FREQ_AVAIL( \ + st_sensors_sysfs_sampling_frequency_avail) + +#define ST_SENSORS_DEV_ATTR_SCALE_AVAIL(name) \ + IIO_DEVICE_ATTR(name, S_IRUGO, \ + st_sensors_sysfs_scale_avail, NULL , 0); + +struct st_sensor_odr_avl { + unsigned int hz; + u8 value; +}; + +struct st_sensor_odr { + u8 addr; + u8 mask; + struct st_sensor_odr_avl odr_avl[ST_SENSORS_ODR_LIST_MAX]; +}; + +struct st_sensor_power { + u8 addr; + u8 mask; + u8 value_off; + u8 value_on; +}; + +struct st_sensor_axis { + u8 addr; + u8 mask; +}; + +struct st_sensor_fullscale_avl { + unsigned int num; + u8 value; + unsigned int gain; + unsigned int gain2; +}; + +struct st_sensor_fullscale { + u8 addr; + u8 mask; + struct st_sensor_fullscale_avl fs_avl[ST_SENSORS_FULLSCALE_AVL_MAX]; +}; + +/** + * struct st_sensor_bdu - ST sensor device block data update + * @addr: address of the register. + * @mask: mask to write the block data update flag. + */ +struct st_sensor_bdu { + u8 addr; + u8 mask; +}; + +/** + * struct st_sensor_data_ready_irq - ST sensor device data-ready interrupt + * @addr: address of the register. + * @mask: mask to write the on/off value. + * struct ig1 - represents the Interrupt Generator 1 of sensors. + * @en_addr: address of the enable ig1 register. + * @en_mask: mask to write the on/off value for enable. + */ +struct st_sensor_data_ready_irq { + u8 addr; + u8 mask; + struct { + u8 en_addr; + u8 en_mask; + } ig1; +}; + +/** + * struct st_sensor_transfer_buffer - ST sensor device I/O buffer + * @buf_lock: Mutex to protect rx and tx buffers. + * @tx_buf: Buffer used by SPI transfer function to send data to the sensors. + * This buffer is used to avoid DMA not-aligned issue. + * @rx_buf: Buffer used by SPI transfer to receive data from sensors. + * This buffer is used to avoid DMA not-aligned issue. + */ +struct st_sensor_transfer_buffer { + struct mutex buf_lock; + u8 rx_buf[ST_SENSORS_RX_MAX_LENGTH]; + u8 tx_buf[ST_SENSORS_TX_MAX_LENGTH] ____cacheline_aligned; +}; + +/** + * struct st_sensor_transfer_function - ST sensor device I/O function + * @read_byte: Function used to read one byte. + * @write_byte: Function used to write one byte. + * @read_multiple_byte: Function used to read multiple byte. + */ +struct st_sensor_transfer_function { + int (*read_byte) (struct st_sensor_transfer_buffer *tb, + struct device *dev, u8 reg_addr, u8 *res_byte); + int (*write_byte) (struct st_sensor_transfer_buffer *tb, + struct device *dev, u8 reg_addr, u8 data); + int (*read_multiple_byte) (struct st_sensor_transfer_buffer *tb, + struct device *dev, u8 reg_addr, int len, u8 *data, + bool multiread_bit); +}; + +/** + * struct st_sensors - ST sensors list + * @wai: Contents of WhoAmI register. + * @sensors_supported: List of supported sensors by struct itself. + * @ch: IIO channels for the sensor. + * @odr: Output data rate register and ODR list available. + * @pw: Power register of the sensor. + * @enable_axis: Enable one or more axis of the sensor. + * @fs: Full scale register and full scale list available. + * @bdu: Block data update register. + * @drdy_irq: Data ready register of the sensor. + * @multi_read_bit: Use or not particular bit for [I2C/SPI] multi-read. + * @bootime: samples to discard when sensor passing from power-down to power-up. + */ +struct st_sensors { + u8 wai; + char sensors_supported[ST_SENSORS_MAX_4WAI][ST_SENSORS_MAX_NAME]; + struct iio_chan_spec *ch; + struct st_sensor_odr odr; + struct st_sensor_power pw; + struct st_sensor_axis enable_axis; + struct st_sensor_fullscale fs; + struct st_sensor_bdu bdu; + struct st_sensor_data_ready_irq drdy_irq; + bool multi_read_bit; + unsigned int bootime; +}; + +/** + * struct st_sensor_data - ST sensor device status + * @dev: Pointer to instance of struct device (I2C or SPI). + * @trig: The trigger in use by the core driver. + * @sensor: Pointer to the current sensor struct in use. + * @current_fullscale: Maximum range of measure by the sensor. + * @enabled: Status of the sensor (false->off, true->on). + * @multiread_bit: Use or not particular bit for [I2C/SPI] multiread. + * @buffer_data: Data used by buffer part. + * @odr: Output data rate of the sensor [Hz]. + * @get_irq_data_ready: Function to get the IRQ used for data ready signal. + * @tf: Transfer function structure used by I/O operations. + * @tb: Transfer buffers and mutex used by I/O operations. + */ +struct st_sensor_data { + struct device *dev; + struct iio_trigger *trig; + struct st_sensors *sensor; + struct st_sensor_fullscale_avl *current_fullscale; + + bool enabled; + bool multiread_bit; + + char *buffer_data; + + unsigned int odr; + + unsigned int (*get_irq_data_ready) (struct iio_dev *indio_dev); + + const struct st_sensor_transfer_function *tf; + struct st_sensor_transfer_buffer tb; +}; + +#ifdef CONFIG_IIO_BUFFER +irqreturn_t st_sensors_trigger_handler(int irq, void *p); + +int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf); +#endif + +#ifdef CONFIG_IIO_TRIGGER +int st_sensors_allocate_trigger(struct iio_dev *indio_dev, + const struct iio_trigger_ops *trigger_ops); + +void st_sensors_deallocate_trigger(struct iio_dev *indio_dev); + +#else +static inline int st_sensors_allocate_trigger(struct iio_dev *indio_dev, + const struct iio_trigger_ops *trigger_ops) +{ + return 0; +} +static inline void st_sensors_deallocate_trigger(struct iio_dev *indio_dev) +{ + return; +} +#endif + +int st_sensors_init_sensor(struct iio_dev *indio_dev); + +int st_sensors_write_data_with_mask(struct iio_dev *indio_dev, + u8 reg_addr, u8 mask, u8 data); + +int st_sensors_set_enable(struct iio_dev *indio_dev, bool enable); + +int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable); + +int st_sensors_set_odr(struct iio_dev *indio_dev, unsigned int odr); + +int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable); + +int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev, int scale); + +int st_sensors_read_axis_data(struct iio_dev *indio_dev, + u8 ch_addr, int *data); + +int st_sensors_read_info_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *ch, int *val); + +int st_sensors_check_device_support(struct iio_dev *indio_dev, + int num_sensors_list, const struct st_sensors *sensors); + +ssize_t st_sensors_sysfs_get_sampling_frequency(struct device *dev, + struct device_attribute *attr, char *buf); + +ssize_t st_sensors_sysfs_set_sampling_frequency(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); + +ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *dev, + struct device_attribute *attr, char *buf); + +ssize_t st_sensors_sysfs_scale_avail(struct device *dev, + struct device_attribute *attr, char *buf); + +#endif /* ST_SENSORS_H */ diff --git a/include/linux/iio/common/st_sensors_i2c.h b/include/linux/iio/common/st_sensors_i2c.h new file mode 100644 index 0000000..67d8453 --- /dev/null +++ b/include/linux/iio/common/st_sensors_i2c.h @@ -0,0 +1,20 @@ +/* + * STMicroelectronics sensors i2c library driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca + * + * Licensed under the GPL-2. + */ + +#ifndef ST_SENSORS_I2C_H +#define ST_SENSORS_I2C_H + +#include +#include + +void st_sensors_i2c_configure(struct iio_dev *indio_dev, + struct i2c_client *client, struct st_sensor_data *sdata); + +#endif /* ST_SENSORS_I2C_H */ diff --git a/include/linux/iio/common/st_sensors_spi.h b/include/linux/iio/common/st_sensors_spi.h new file mode 100644 index 0000000..d964a35 --- /dev/null +++ b/include/linux/iio/common/st_sensors_spi.h @@ -0,0 +1,20 @@ +/* + * STMicroelectronics sensors spi library driver + * + * Copyright 2012-2013 STMicroelectronics Inc. + * + * Denis Ciocca + * + * Licensed under the GPL-2. + */ + +#ifndef ST_SENSORS_SPI_H +#define ST_SENSORS_SPI_H + +#include +#include + +void st_sensors_spi_configure(struct iio_dev *indio_dev, + struct spi_device *spi, struct st_sensor_data *sdata); + +#endif /* ST_SENSORS_SPI_H */ diff --git a/include/linux/platform_data/lis331dlh_intel_qrk.h b/include/linux/platform_data/lis331dlh_intel_qrk.h new file mode 100644 index 0000000..703c927 --- /dev/null +++ b/include/linux/platform_data/lis331dlh_intel_qrk.h @@ -0,0 +1,36 @@ +/* + * Platform data for Intel Clanton Hill platform accelerometer driver + * + * Copyright(c) 2013 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * Contact Information: + * Intel Corporation + * + */ + +#ifndef __LINUX_PLATFORM_DATA_LIS331DLH_INTEL_QRK_H__ +#define __LINUX_PLATFORM_DATA_LIS331DLH_INTEL_QRK_H__ + +/** + * struct lis331dlh_intel_qrk_platform_data - Platform data for the ST Micro + * accelerometer driver + * @irq1_pin: GPIO pin number for the threshold interrupt(INT1). + **/ +struct lis331dlh_intel_qrk_platform_data { + int irq1_pin; +}; + +#endif /* LINUX_PLATFORM_DATA_LIS331DLH_INTEL_QRK_H_ */ -- 1.7.4.1