diff options
Diffstat (limited to 'recipes-kernel/linux/files/0018-Quark-sensors-quark.patch')
-rw-r--r-- | recipes-kernel/linux/files/0018-Quark-sensors-quark.patch | 2155 |
1 files changed, 2155 insertions, 0 deletions
diff --git a/recipes-kernel/linux/files/0018-Quark-sensors-quark.patch b/recipes-kernel/linux/files/0018-Quark-sensors-quark.patch new file mode 100644 index 0000000..e59a7de --- /dev/null +++ b/recipes-kernel/linux/files/0018-Quark-sensors-quark.patch @@ -0,0 +1,2155 @@ +From xxxx Mon Sep 17 00:00:00 2001 +From: Dan O'Donovan <dan.odonovan@emutex.com> +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 <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/errno.h> ++#include <linux/types.h> ++#include <linux/mutex.h> ++#include <linux/interrupt.h> ++#include <linux/i2c.h> ++#include <linux/gpio.h> ++#include <linux/iio/iio.h> ++#include <linux/iio/sysfs.h> ++#include <linux/iio/events.h> ++ ++#include <linux/iio/common/st_sensors.h> ++#include <linux/iio/common/st_sensors_i2c.h> ++ ++#include <linux/platform_data/lis331dlh_intel_qrk.h> ++ ++/* 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 <wojciech.ziemba@emutex.com>"); ++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 <denis.ciocca@st.com> ++ * ++ * Licensed under the GPL-2. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/iio/iio.h> ++#include <linux/iio/trigger.h> ++#include <linux/interrupt.h> ++#include <linux/iio/buffer.h> ++#include <linux/iio/trigger_consumer.h> ++#include <linux/iio/triggered_buffer.h> ++#include <linux/irqreturn.h> ++ ++#include <linux/iio/common/st_sensors.h> ++ ++ ++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 <denis.ciocca@st.com>"); ++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 <denis.ciocca@st.com> ++ * ++ * Licensed under the GPL-2. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/delay.h> ++#include <linux/iio/iio.h> ++#include <asm/unaligned.h> ++ ++#include <linux/iio/common/st_sensors.h> ++ ++ ++#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 <denis.ciocca@st.com>"); ++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 <denis.ciocca@st.com> ++ * ++ * Licensed under the GPL-2. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/iio/iio.h> ++ ++#include <linux/iio/common/st_sensors_i2c.h> ++ ++ ++#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 <denis.ciocca@st.com>"); ++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 <denis.ciocca@st.com> ++ * ++ * Licensed under the GPL-2. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/iio/iio.h> ++ ++#include <linux/iio/common/st_sensors_spi.h> ++ ++ ++#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 <denis.ciocca@st.com>"); ++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 <denis.ciocca@st.com> ++ * ++ * Licensed under the GPL-2. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/iio/iio.h> ++#include <linux/iio/trigger.h> ++#include <linux/interrupt.h> ++ ++#include <linux/iio/common/st_sensors.h> ++ ++ ++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 <denis.ciocca@st.com>"); ++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 <denis.ciocca@st.com> ++ * ++ * Licensed under the GPL-2. ++ */ ++ ++#ifndef ST_SENSORS_H ++#define ST_SENSORS_H ++ ++#include <linux/i2c.h> ++#include <linux/spi/spi.h> ++#include <linux/irqreturn.h> ++#include <linux/iio/trigger.h> ++ ++#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 <denis.ciocca@st.com> ++ * ++ * Licensed under the GPL-2. ++ */ ++ ++#ifndef ST_SENSORS_I2C_H ++#define ST_SENSORS_I2C_H ++ ++#include <linux/i2c.h> ++#include <linux/iio/common/st_sensors.h> ++ ++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 <denis.ciocca@st.com> ++ * ++ * Licensed under the GPL-2. ++ */ ++ ++#ifndef ST_SENSORS_SPI_H ++#define ST_SENSORS_SPI_H ++ ++#include <linux/spi/spi.h> ++#include <linux/iio/common/st_sensors.h> ++ ++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 + |