summaryrefslogtreecommitdiff
path: root/drivers/iio
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-09-30 03:37:48 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-09-30 03:37:48 +0200
commit9f827d8099e76a4a2fb99faa88ee7859459f2360 (patch)
treef08d4db69c53bb441d759747c59a68ae28e82825 /drivers/iio
parent99207b80bbf602f1674485c3f9653b7c527785d9 (diff)
parent1d2f1e084b7386b4082ebc2490ce0ddc8efe5667 (diff)
Merge tag 'iio-for-4.4a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next
Jonathan writes: First round of new driver, new functionality and cleanups for IIO in the 4.4 cycle New device support * APDS9960 ALS + proximity driver * bmg160 SPI devices. * HDC100x humidity sensors * Holt HI-8435 threshold detector * mma8453Q accelerometer added to the mma8452 driver * mma86452FC and mma8653FC accelerometers added to the mma8452 driver * mxc4005 accelerometer * PulsedLight LIDAR * SensorTech VZ89x volatile organic compound sensor * UPISEMI uS5182d ALS and proximity sensors New core functionality * triggered events - use triggers to check for changes in threshold type detectors on devices with out interrupt support. First user is the holt comparator. * chemical concentration and resistance channel types. New driver functionality * vf610 - buffer support. - followup coccinelle warning fix. Core rework * buffers - break out callback buffer to own module. - move buffer implementations to a new subdirectory * percolate the error code form iio_event_getfd out to userspace rather than giving a missleading error later on. Cleanups * adddac drivers - use BIT macro where appropriate. * meter drivers - use BIT macro where appropriate. * ad7303 - add an OF match table to line up with the binding docs. * adc128s052 - add an OF match table to line up with the binding docs. * adf4350 - add an OF match table to line up with the binding docs * as3935 - add an OF match table to line up with the binding docs. * berlin2-adc - use GENMASK and BIT for masks - prevent attempting to sample multiple channels at once by moving a mutex scop - coding style cleanups * bmg150_magn - kconfig sort order was wrong - fix it. * bmg160 - use i2c regmap and drop all uses of i2c_client - separate i2c and core driver * cc10001_adc - kconfig sort order was wrong - fix it. * evgen (dummy driver helper module) - move interrupt generation to irq_work to reduce differences between the dummy driver and real hardware drivers. * hmc5843 - set the name dynamically rather than to a fixed value for one of the suported parts. - export module alias information to allow autoprobing of module. * lpc32xx - on failure to get resource or irq return -ENXIO as uppose to -EBUSY * max1027 - set .of_match_table to actually allow OF style matching. * max5821 - add MODULE_DEVICE_TABLE for OF table. * mma8452 - refactor to separate out chip specific data. - add freefall / motion interrupt source for devices that do their interrupts slightly differently. - update copywrite notice. - leave naming of events directory in sysfs to the core * mcp320x - set .of_match_table so that it can be use for OF style matching. * mlx90614 - Implement filter configuration (note the datasheet changed as a result of the driver reviews to include the values we needed ;) * opt3001 - drop .owner field as assigned by platform driver core. * si7020 - replace a bitmask on the humidity values with a more correct range check. * stk310 - improved error handling. - use BIT macro where appropriate and use the resulting defines instead of magic numbers in the code. - fix indentation * st-sensors - add debugfs register read hook * tsl4531 - fix error handling in check_id * twl6030 - fix module autoload for OF * iio-trig-sysfs - document add and remove attribute * trigger in staging - code alignment fixes. - braces on both branches of if statement if needed for one. * xilinx-xadc - push interrupts into hardirq context as there isn't much in them any more and it avoids breaking PREEMPT_RT builds due to the use of a spinlock between the hardirq and the thread. Tools * event-monitor - report unsupported events. We keep expanding what can come from drivers so give a helpful error if one turns up in an out of date userspace program. * generic-buffer - helpful message about needing to enable a channel to start the buffer.
Diffstat (limited to 'drivers/iio')
-rw-r--r--drivers/iio/Kconfig29
-rw-r--r--drivers/iio/Makefile6
-rw-r--r--drivers/iio/accel/Kconfig19
-rw-r--r--drivers/iio/accel/Makefile2
-rw-r--r--drivers/iio/accel/mma8452.c356
-rw-r--r--drivers/iio/accel/mxc4005.c567
-rw-r--r--drivers/iio/accel/st_accel_core.c1
-rw-r--r--drivers/iio/adc/Kconfig35
-rw-r--r--drivers/iio/adc/Makefile3
-rw-r--r--drivers/iio/adc/berlin2-adc.c91
-rw-r--r--drivers/iio/adc/hi8435.c534
-rw-r--r--drivers/iio/adc/max1027.c1
-rw-r--r--drivers/iio/adc/mcp320x.c1
-rw-r--r--drivers/iio/adc/ti-adc128s052.c8
-rw-r--r--drivers/iio/adc/twl6030-gpadc.c1
-rw-r--r--drivers/iio/adc/vf610_adc.c101
-rw-r--r--drivers/iio/adc/xilinx-xadc-core.c37
-rw-r--r--drivers/iio/adc/xilinx-xadc.h2
-rw-r--r--drivers/iio/amplifiers/ad8366.c1
-rw-r--r--drivers/iio/buffer/Kconfig24
-rw-r--r--drivers/iio/buffer/Makefile8
-rw-r--r--drivers/iio/buffer/industrialio-buffer-cb.c (renamed from drivers/iio/buffer_cb.c)12
-rw-r--r--drivers/iio/buffer/industrialio-triggered-buffer.c (renamed from drivers/iio/industrialio-triggered-buffer.c)0
-rw-r--r--drivers/iio/buffer/kfifo_buf.c (renamed from drivers/iio/kfifo_buf.c)0
-rw-r--r--drivers/iio/chemical/Kconfig15
-rw-r--r--drivers/iio/chemical/Makefile6
-rw-r--r--drivers/iio/chemical/vz89x.c237
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_core.c22
-rw-r--r--drivers/iio/dac/ad7303.c7
-rw-r--r--drivers/iio/dac/max5821.c1
-rw-r--r--drivers/iio/frequency/adf4350.c9
-rw-r--r--drivers/iio/gyro/Kconfig19
-rw-r--r--drivers/iio/gyro/Makefile4
-rw-r--r--drivers/iio/gyro/bmg160.h10
-rw-r--r--drivers/iio/gyro/bmg160_core.c (renamed from drivers/iio/gyro/bmg160.c)361
-rw-r--r--drivers/iio/gyro/bmg160_i2c.c71
-rw-r--r--drivers/iio/gyro/bmg160_spi.c57
-rw-r--r--drivers/iio/gyro/st_gyro_core.c1
-rw-r--r--drivers/iio/humidity/Kconfig10
-rw-r--r--drivers/iio/humidity/Makefile1
-rw-r--r--drivers/iio/humidity/hdc100x.c319
-rw-r--r--drivers/iio/humidity/si7020.c6
-rw-r--r--drivers/iio/industrialio-core.c10
-rw-r--r--drivers/iio/industrialio-trigger.c12
-rw-r--r--drivers/iio/industrialio-triggered-event.c68
-rw-r--r--drivers/iio/light/Kconfig23
-rw-r--r--drivers/iio/light/Makefile2
-rw-r--r--drivers/iio/light/apds9960.c1130
-rw-r--r--drivers/iio/light/opt3001.c1
-rw-r--r--drivers/iio/light/stk3310.c75
-rw-r--r--drivers/iio/light/tsl4531.c9
-rw-r--r--drivers/iio/light/us5182d.c507
-rw-r--r--drivers/iio/magnetometer/Kconfig33
-rw-r--r--drivers/iio/magnetometer/Makefile3
-rw-r--r--drivers/iio/magnetometer/st_magn_core.c1
-rw-r--r--drivers/iio/pressure/st_pressure_core.c1
-rw-r--r--drivers/iio/proximity/Kconfig12
-rw-r--r--drivers/iio/proximity/Makefile1
-rw-r--r--drivers/iio/proximity/as3935.c7
-rw-r--r--drivers/iio/proximity/pulsedlight-lidar-lite-v2.c288
-rw-r--r--drivers/iio/temperature/mlx90614.c90
61 files changed, 4813 insertions, 455 deletions
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 4011effe4c05..119c94df2b9e 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -19,27 +19,7 @@ config IIO_BUFFER
acquisition methods.
if IIO_BUFFER
-
-config IIO_BUFFER_CB
- bool "IIO callback buffer used for push in-kernel interfaces"
- help
- Should be selected by any drivers that do in-kernel push
- usage. That is, those where the data is pushed to the consumer.
-
-config IIO_KFIFO_BUF
- tristate "Industrial I/O buffering based on kfifo"
- help
- A simple fifo based on kfifo. Note that this currently provides
- no buffer events so it is up to userspace to work out how
- often to read from the buffer.
-
-config IIO_TRIGGERED_BUFFER
- tristate
- select IIO_TRIGGER
- select IIO_KFIFO_BUF
- help
- Provides helper functions for setting up triggered buffers.
-
+ source "drivers/iio/buffer/Kconfig"
endif # IIO_BUFFER
config IIO_TRIGGER
@@ -58,9 +38,16 @@ config IIO_CONSUMERS_PER_TRIGGER
This value controls the maximum number of consumers that a
given trigger may handle. Default is 2.
+config IIO_TRIGGERED_EVENT
+ tristate
+ select IIO_TRIGGER
+ help
+ Provides helper functions for setting up triggered events.
+
source "drivers/iio/accel/Kconfig"
source "drivers/iio/adc/Kconfig"
source "drivers/iio/amplifiers/Kconfig"
+source "drivers/iio/chemical/Kconfig"
source "drivers/iio/common/Kconfig"
source "drivers/iio/dac/Kconfig"
source "drivers/iio/frequency/Kconfig"
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 698afc2d17ce..e2100554e3b4 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -6,14 +6,14 @@ obj-$(CONFIG_IIO) += industrialio.o
industrialio-y := industrialio-core.o industrialio-event.o inkern.o
industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
-industrialio-$(CONFIG_IIO_BUFFER_CB) += buffer_cb.o
-obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o
-obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
+obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o
obj-y += accel/
obj-y += adc/
obj-y += amplifiers/
+obj-y += buffer/
+obj-y += chemical/
obj-y += common/
obj-y += dac/
obj-y += gyro/
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index a59047d7657e..8172ae5c3d0d 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -100,13 +100,13 @@ config KXCJK1013
be called kxcjk-1013.
config MMA8452
- tristate "Freescale MMA8452Q Accelerometer Driver"
+ tristate "Freescale MMA8452Q and similar Accelerometers Driver"
depends on I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
- Say yes here to build support for the Freescale MMA8452Q 3-axis
- accelerometer.
+ Say yes here to build support for the following Freescale 3-axis
+ accelerometers: MMA8452Q, MMA8453Q, MMA8652FC, MMA8653FC.
To compile this driver as a module, choose M here: the module
will be called mma8452.
@@ -137,6 +137,19 @@ config MMA9553
To compile this driver as a module, choose M here: the module
will be called mma9553.
+config MXC4005
+ tristate "Memsic MXC4005XC 3-Axis Accelerometer Driver"
+ depends on I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ select REGMAP_I2C
+ help
+ Say yes here to build support for the Memsic MXC4005XC 3-axis
+ accelerometer.
+
+ To compile this driver as a module, choose M. The module will be
+ called mxc4005.
+
config STK8312
tristate "Sensortek STK8312 3-Axis Accelerometer Driver"
depends on I2C
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index ebd2675b2a02..020dda0ae508 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -14,6 +14,8 @@ obj-$(CONFIG_MMA9551_CORE) += mma9551_core.o
obj-$(CONFIG_MMA9551) += mma9551.o
obj-$(CONFIG_MMA9553) += mma9553.o
+obj-$(CONFIG_MXC4005) += mxc4005.o
+
obj-$(CONFIG_STK8312) += stk8312.o
obj-$(CONFIG_STK8BA50) += stk8ba50.o
diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c
index b921d84c1be6..1eccc2dcf14c 100644
--- a/drivers/iio/accel/mma8452.c
+++ b/drivers/iio/accel/mma8452.c
@@ -1,6 +1,12 @@
/*
- * mma8452.c - Support for Freescale MMA8452Q 3-axis 12-bit accelerometer
+ * mma8452.c - Support for following Freescale 3-axis accelerometers:
*
+ * MMA8452Q (12 bit)
+ * MMA8453Q (10 bit)
+ * MMA8652FC (12 bit)
+ * MMA8653FC (10 bit)
+ *
+ * Copyright 2015 Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
* Copyright 2014 Peter Meerwald <pmeerw@pmeerw.net>
*
* This file is subject to the terms and conditions of version 2 of
@@ -22,10 +28,11 @@
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/events.h>
#include <linux/delay.h>
+#include <linux/of_device.h>
#define MMA8452_STATUS 0x00
#define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0))
-#define MMA8452_OUT_X 0x01 /* MSB first, 12-bit */
+#define MMA8452_OUT_X 0x01 /* MSB first */
#define MMA8452_OUT_Y 0x03
#define MMA8452_OUT_Z 0x05
#define MMA8452_INT_SRC 0x0c
@@ -38,6 +45,16 @@
#define MMA8452_DATA_CFG_HPF_MASK BIT(4)
#define MMA8452_HP_FILTER_CUTOFF 0x0f
#define MMA8452_HP_FILTER_CUTOFF_SEL_MASK GENMASK(1, 0)
+#define MMA8452_FF_MT_CFG 0x15
+#define MMA8452_FF_MT_CFG_OAE BIT(6)
+#define MMA8452_FF_MT_CFG_ELE BIT(7)
+#define MMA8452_FF_MT_SRC 0x16
+#define MMA8452_FF_MT_SRC_XHE BIT(1)
+#define MMA8452_FF_MT_SRC_YHE BIT(3)
+#define MMA8452_FF_MT_SRC_ZHE BIT(5)
+#define MMA8452_FF_MT_THS 0x17
+#define MMA8452_FF_MT_THS_MASK 0x7f
+#define MMA8452_FF_MT_COUNT 0x18
#define MMA8452_TRANSIENT_CFG 0x1d
#define MMA8452_TRANSIENT_CFG_HPF_BYP BIT(0)
#define MMA8452_TRANSIENT_CFG_CHAN(chan) BIT(chan + 1)
@@ -65,15 +82,65 @@
#define MMA8452_MAX_REG 0x31
#define MMA8452_INT_DRDY BIT(0)
+#define MMA8452_INT_FF_MT BIT(2)
#define MMA8452_INT_TRANS BIT(5)
#define MMA8452_DEVICE_ID 0x2a
+#define MMA8453_DEVICE_ID 0x3a
+#define MMA8652_DEVICE_ID 0x4a
+#define MMA8653_DEVICE_ID 0x5a
struct mma8452_data {
struct i2c_client *client;
struct mutex lock;
u8 ctrl_reg1;
u8 data_cfg;
+ const struct mma_chip_info *chip_info;
+};
+
+/**
+ * struct mma_chip_info - chip specific data for Freescale's accelerometers
+ * @chip_id: WHO_AM_I register's value
+ * @channels: struct iio_chan_spec matching the device's
+ * capabilities
+ * @num_channels: number of channels
+ * @mma_scales: scale factors for converting register values
+ * to m/s^2; 3 modes: 2g, 4g, 8g; 2 integers
+ * per mode: m/s^2 and micro m/s^2
+ * @ev_cfg: event config register address
+ * @ev_cfg_ele: latch bit in event config register
+ * @ev_cfg_chan_shift: number of the bit to enable events in X
+ * direction; in event config register
+ * @ev_src: event source register address
+ * @ev_src_xe: bit in event source register that indicates
+ * an event in X direction
+ * @ev_src_ye: bit in event source register that indicates
+ * an event in Y direction
+ * @ev_src_ze: bit in event source register that indicates
+ * an event in Z direction
+ * @ev_ths: event threshold register address
+ * @ev_ths_mask: mask for the threshold value
+ * @ev_count: event count (period) register address
+ *
+ * Since not all chips supported by the driver support comparing high pass
+ * filtered data for events (interrupts), different interrupt sources are
+ * used for different chips and the relevant registers are included here.
+ */
+struct mma_chip_info {
+ u8 chip_id;
+ const struct iio_chan_spec *channels;
+ int num_channels;
+ const int mma_scales[3][2];
+ u8 ev_cfg;
+ u8 ev_cfg_ele;
+ u8 ev_cfg_chan_shift;
+ u8 ev_src;
+ u8 ev_src_xe;
+ u8 ev_src_ye;
+ u8 ev_src_ze;
+ u8 ev_ths;
+ u8 ev_ths_mask;
+ u8 ev_count;
};
static int mma8452_drdy(struct mma8452_data *data)
@@ -143,16 +210,6 @@ static const int mma8452_samp_freq[8][2] = {
{6, 250000}, {1, 560000}
};
-/*
- * Hardware has fullscale of -2G, -4G, -8G corresponding to raw value -2048
- * The userspace interface uses m/s^2 and we declare micro units
- * So scale factor is given by:
- * g * N * 1000000 / 2048 for N = 2, 4, 8 and g = 9.80665
- */
-static const int mma8452_scales[3][2] = {
- {0, 9577}, {0, 19154}, {0, 38307}
-};
-
/* Datasheet table 35 (step time vs sample frequency) */
static const int mma8452_transient_time_step_us[8] = {
1250,
@@ -189,8 +246,11 @@ static ssize_t mma8452_show_scale_avail(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- return mma8452_show_int_plus_micros(buf, mma8452_scales,
- ARRAY_SIZE(mma8452_scales));
+ struct mma8452_data *data = iio_priv(i2c_get_clientdata(
+ to_i2c_client(dev)));
+
+ return mma8452_show_int_plus_micros(buf, data->chip_info->mma_scales,
+ ARRAY_SIZE(data->chip_info->mma_scales));
}
static ssize_t mma8452_show_hp_cutoff_avail(struct device *dev,
@@ -221,9 +281,8 @@ static int mma8452_get_samp_freq_index(struct mma8452_data *data,
static int mma8452_get_scale_index(struct mma8452_data *data, int val, int val2)
{
- return mma8452_get_int_plus_micros_index(mma8452_scales,
- ARRAY_SIZE(mma8452_scales),
- val, val2);
+ return mma8452_get_int_plus_micros_index(data->chip_info->mma_scales,
+ ARRAY_SIZE(data->chip_info->mma_scales), val, val2);
}
static int mma8452_get_hp_filter_index(struct mma8452_data *data,
@@ -270,14 +329,15 @@ static int mma8452_read_raw(struct iio_dev *indio_dev,
if (ret < 0)
return ret;
- *val = sign_extend32(be16_to_cpu(buffer[chan->scan_index]) >> 4,
- 11);
+ *val = sign_extend32(be16_to_cpu(
+ buffer[chan->scan_index]) >> chan->scan_type.shift,
+ chan->scan_type.realbits - 1);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
i = data->data_cfg & MMA8452_DATA_CFG_FS_MASK;
- *val = mma8452_scales[i][0];
- *val2 = mma8452_scales[i][1];
+ *val = data->chip_info->mma_scales[i][0];
+ *val2 = data->chip_info->mma_scales[i][1];
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SAMP_FREQ:
@@ -439,17 +499,17 @@ static int mma8452_read_thresh(struct iio_dev *indio_dev,
switch (info) {
case IIO_EV_INFO_VALUE:
ret = i2c_smbus_read_byte_data(data->client,
- MMA8452_TRANSIENT_THS);
+ data->chip_info->ev_ths);
if (ret < 0)
return ret;
- *val = ret & MMA8452_TRANSIENT_THS_MASK;
+ *val = ret & data->chip_info->ev_ths_mask;
return IIO_VAL_INT;
case IIO_EV_INFO_PERIOD:
ret = i2c_smbus_read_byte_data(data->client,
- MMA8452_TRANSIENT_COUNT);
+ data->chip_info->ev_count);
if (ret < 0)
return ret;
@@ -497,7 +557,8 @@ static int mma8452_write_thresh(struct iio_dev *indio_dev,
if (val < 0 || val > MMA8452_TRANSIENT_THS_MASK)
return -EINVAL;
- return mma8452_change_config(data, MMA8452_TRANSIENT_THS, val);
+ return mma8452_change_config(data, data->chip_info->ev_ths,
+ val);
case IIO_EV_INFO_PERIOD:
steps = (val * USEC_PER_SEC + val2) /
@@ -507,7 +568,7 @@ static int mma8452_write_thresh(struct iio_dev *indio_dev,
if (steps < 0 || steps > 0xff)
return -EINVAL;
- return mma8452_change_config(data, MMA8452_TRANSIENT_COUNT,
+ return mma8452_change_config(data, data->chip_info->ev_count,
steps);
case IIO_EV_INFO_HIGH_PASS_FILTER_3DB:
@@ -538,13 +599,15 @@ static int mma8452_read_event_config(struct iio_dev *indio_dev,
enum iio_event_direction dir)
{
struct mma8452_data *data = iio_priv(indio_dev);
+ const struct mma_chip_info *chip = data->chip_info;
int ret;
- ret = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_CFG);
+ ret = i2c_smbus_read_byte_data(data->client,
+ data->chip_info->ev_cfg);
if (ret < 0)
return ret;
- return ret & MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index) ? 1 : 0;
+ return !!(ret & BIT(chan->scan_index + chip->ev_cfg_chan_shift));
}
static int mma8452_write_event_config(struct iio_dev *indio_dev,
@@ -554,20 +617,22 @@ static int mma8452_write_event_config(struct iio_dev *indio_dev,
int state)
{
struct mma8452_data *data = iio_priv(indio_dev);
+ const struct mma_chip_info *chip = data->chip_info;
int val;
- val = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_CFG);
+ val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg);
if (val < 0)
return val;
if (state)
- val |= MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index);
+ val |= BIT(chan->scan_index + chip->ev_cfg_chan_shift);
else
- val &= ~MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index);
+ val &= ~BIT(chan->scan_index + chip->ev_cfg_chan_shift);
- val |= MMA8452_TRANSIENT_CFG_ELE;
+ val |= chip->ev_cfg_ele;
+ val |= MMA8452_FF_MT_CFG_OAE;
- return mma8452_change_config(data, MMA8452_TRANSIENT_CFG, val);
+ return mma8452_change_config(data, chip->ev_cfg, val);
}
static void mma8452_transient_interrupt(struct iio_dev *indio_dev)
@@ -576,25 +641,25 @@ static void mma8452_transient_interrupt(struct iio_dev *indio_dev)
s64 ts = iio_get_time_ns();
int src;
- src = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_SRC);
+ src = i2c_smbus_read_byte_data(data->client, data->chip_info->ev_src);
if (src < 0)
return;
- if (src & MMA8452_TRANSIENT_SRC_XTRANSE)
+ if (src & data->chip_info->ev_src_xe)
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X,
IIO_EV_TYPE_MAG,
IIO_EV_DIR_RISING),
ts);
- if (src & MMA8452_TRANSIENT_SRC_YTRANSE)
+ if (src & data->chip_info->ev_src_ye)
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Y,
IIO_EV_TYPE_MAG,
IIO_EV_DIR_RISING),
ts);
- if (src & MMA8452_TRANSIENT_SRC_ZTRANSE)
+ if (src & data->chip_info->ev_src_ze)
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Z,
IIO_EV_TYPE_MAG,
@@ -606,6 +671,7 @@ static irqreturn_t mma8452_interrupt(int irq, void *p)
{
struct iio_dev *indio_dev = p;
struct mma8452_data *data = iio_priv(indio_dev);
+ const struct mma_chip_info *chip = data->chip_info;
int ret = IRQ_NONE;
int src;
@@ -618,7 +684,10 @@ static irqreturn_t mma8452_interrupt(int irq, void *p)
ret = IRQ_HANDLED;
}
- if (src & MMA8452_INT_TRANS) {
+ if ((src & MMA8452_INT_TRANS &&
+ chip->ev_src == MMA8452_TRANSIENT_SRC) ||
+ (src & MMA8452_INT_FF_MT &&
+ chip->ev_src == MMA8452_FF_MT_SRC)) {
mma8452_transient_interrupt(indio_dev);
ret = IRQ_HANDLED;
}
@@ -680,6 +749,16 @@ static const struct iio_event_spec mma8452_transient_event[] = {
},
};
+static const struct iio_event_spec mma8452_motion_event[] = {
+ {
+ .type = IIO_EV_TYPE_MAG,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_PERIOD)
+ },
+};
+
/*
* Threshold is configured in fixed 8G/127 steps regardless of
* currently selected scale for measurement.
@@ -693,10 +772,9 @@ static struct attribute *mma8452_event_attributes[] = {
static struct attribute_group mma8452_event_attribute_group = {
.attrs = mma8452_event_attributes,
- .name = "events",
};
-#define MMA8452_CHANNEL(axis, idx) { \
+#define MMA8452_CHANNEL(axis, idx, bits) { \
.type = IIO_ACCEL, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
@@ -708,22 +786,144 @@ static struct attribute_group mma8452_event_attribute_group = {
.scan_index = idx, \
.scan_type = { \
.sign = 's', \
- .realbits = 12, \
+ .realbits = (bits), \
.storagebits = 16, \
- .shift = 4, \
+ .shift = 16 - (bits), \
.endianness = IIO_BE, \
}, \
.event_spec = mma8452_transient_event, \
.num_event_specs = ARRAY_SIZE(mma8452_transient_event), \
}
+#define MMA8652_CHANNEL(axis, idx, bits) { \
+ .type = IIO_ACCEL, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_index = idx, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = (bits), \
+ .storagebits = 16, \
+ .shift = 16 - (bits), \
+ .endianness = IIO_BE, \
+ }, \
+ .event_spec = mma8452_motion_event, \
+ .num_event_specs = ARRAY_SIZE(mma8452_motion_event), \
+}
+
static const struct iio_chan_spec mma8452_channels[] = {
- MMA8452_CHANNEL(X, 0),
- MMA8452_CHANNEL(Y, 1),
- MMA8452_CHANNEL(Z, 2),
+ MMA8452_CHANNEL(X, 0, 12),
+ MMA8452_CHANNEL(Y, 1, 12),
+ MMA8452_CHANNEL(Z, 2, 12),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
+static const struct iio_chan_spec mma8453_channels[] = {
+ MMA8452_CHANNEL(X, 0, 10),
+ MMA8452_CHANNEL(Y, 1, 10),
+ MMA8452_CHANNEL(Z, 2, 10),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const struct iio_chan_spec mma8652_channels[] = {
+ MMA8652_CHANNEL(X, 0, 12),
+ MMA8652_CHANNEL(Y, 1, 12),
+ MMA8652_CHANNEL(Z, 2, 12),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const struct iio_chan_spec mma8653_channels[] = {
+ MMA8652_CHANNEL(X, 0, 10),
+ MMA8652_CHANNEL(Y, 1, 10),
+ MMA8652_CHANNEL(Z, 2, 10),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+enum {
+ mma8452,
+ mma8453,
+ mma8652,
+ mma8653,
+};
+
+static const struct mma_chip_info mma_chip_info_table[] = {
+ [mma8452] = {
+ .chip_id = MMA8452_DEVICE_ID,
+ .channels = mma8452_channels,
+ .num_channels = ARRAY_SIZE(mma8452_channels),
+ /*
+ * Hardware has fullscale of -2G, -4G, -8G corresponding to
+ * raw value -2048 for 12 bit or -512 for 10 bit.
+ * The userspace interface uses m/s^2 and we declare micro units
+ * So scale factor for 12 bit here is given by:
+ * g * N * 1000000 / 2048 for N = 2, 4, 8 and g=9.80665
+ */
+ .mma_scales = { {0, 9577}, {0, 19154}, {0, 38307} },
+ .ev_cfg = MMA8452_TRANSIENT_CFG,
+ .ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE,
+ .ev_cfg_chan_shift = 1,
+ .ev_src = MMA8452_TRANSIENT_SRC,
+ .ev_src_xe = MMA8452_TRANSIENT_SRC_XTRANSE,
+ .ev_src_ye = MMA8452_TRANSIENT_SRC_YTRANSE,
+ .ev_src_ze = MMA8452_TRANSIENT_SRC_ZTRANSE,
+ .ev_ths = MMA8452_TRANSIENT_THS,
+ .ev_ths_mask = MMA8452_TRANSIENT_THS_MASK,
+ .ev_count = MMA8452_TRANSIENT_COUNT,
+ },
+ [mma8453] = {
+ .chip_id = MMA8453_DEVICE_ID,
+ .channels = mma8453_channels,
+ .num_channels = ARRAY_SIZE(mma8453_channels),
+ .mma_scales = { {0, 38307}, {0, 76614}, {0, 153228} },
+ .ev_cfg = MMA8452_TRANSIENT_CFG,
+ .ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE,
+ .ev_cfg_chan_shift = 1,
+ .ev_src = MMA8452_TRANSIENT_SRC,
+ .ev_src_xe = MMA8452_TRANSIENT_SRC_XTRANSE,
+ .ev_src_ye = MMA8452_TRANSIENT_SRC_YTRANSE,
+ .ev_src_ze = MMA8452_TRANSIENT_SRC_ZTRANSE,
+ .ev_ths = MMA8452_TRANSIENT_THS,
+ .ev_ths_mask = MMA8452_TRANSIENT_THS_MASK,
+ .ev_count = MMA8452_TRANSIENT_COUNT,
+ },
+ [mma8652] = {
+ .chip_id = MMA8652_DEVICE_ID,
+ .channels = mma8652_channels,
+ .num_channels = ARRAY_SIZE(mma8652_channels),
+ .mma_scales = { {0, 9577}, {0, 19154}, {0, 38307} },
+ .ev_cfg = MMA8452_FF_MT_CFG,
+ .ev_cfg_ele = MMA8452_FF_MT_CFG_ELE,
+ .ev_cfg_chan_shift = 3,
+ .ev_src = MMA8452_FF_MT_SRC,
+ .ev_src_xe = MMA8452_FF_MT_SRC_XHE,
+ .ev_src_ye = MMA8452_FF_MT_SRC_YHE,
+ .ev_src_ze = MMA8452_FF_MT_SRC_ZHE,
+ .ev_ths = MMA8452_FF_MT_THS,
+ .ev_ths_mask = MMA8452_FF_MT_THS_MASK,
+ .ev_count = MMA8452_FF_MT_COUNT,
+ },
+ [mma8653] = {
+ .chip_id = MMA8653_DEVICE_ID,
+ .channels = mma8653_channels,
+ .num_channels = ARRAY_SIZE(mma8653_channels),
+ .mma_scales = { {0, 38307}, {0, 76614}, {0, 153228} },
+ .ev_cfg = MMA8452_FF_MT_CFG,
+ .ev_cfg_ele = MMA8452_FF_MT_CFG_ELE,
+ .ev_cfg_chan_shift = 3,
+ .ev_src = MMA8452_FF_MT_SRC,
+ .ev_src_xe = MMA8452_FF_MT_SRC_XHE,
+ .ev_src_ye = MMA8452_FF_MT_SRC_YHE,
+ .ev_src_ze = MMA8452_FF_MT_SRC_ZHE,
+ .ev_ths = MMA8452_FF_MT_THS,
+ .ev_ths_mask = MMA8452_FF_MT_THS_MASK,
+ .ev_count = MMA8452_FF_MT_COUNT,
+ },
+};
+
static struct attribute *mma8452_attributes[] = {
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
&iio_dev_attr_in_accel_scale_available.dev_attr.attr,
@@ -841,18 +1041,28 @@ static int mma8452_reset(struct i2c_client *client)
return -ETIMEDOUT;
}
+static const struct of_device_id mma8452_dt_ids[] = {
+ { .compatible = "fsl,mma8452", .data = &mma_chip_info_table[mma8452] },
+ { .compatible = "fsl,mma8453", .data = &mma_chip_info_table[mma8453] },
+ { .compatible = "fsl,mma8652", .data = &mma_chip_info_table[mma8652] },
+ { .compatible = "fsl,mma8653", .data = &mma_chip_info_table[mma8653] },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mma8452_dt_ids);
+
static int mma8452_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct mma8452_data *data;
struct iio_dev *indio_dev;
int ret;
+ const struct of_device_id *match;
- ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I);
- if (ret < 0)
- return ret;
- if (ret != MMA8452_DEVICE_ID)
+ match = of_match_device(mma8452_dt_ids, &client->dev);
+ if (!match) {
+ dev_err(&client->dev, "unknown device model\n");
return -ENODEV;
+ }
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
@@ -861,14 +1071,33 @@ static int mma8452_probe(struct i2c_client *client,
data = iio_priv(indio_dev);
data->client = client;
mutex_init(&data->lock);
+ data->chip_info = match->data;
+
+ ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I);
+ if (ret < 0)
+ return ret;
+
+ switch (ret) {
+ case MMA8452_DEVICE_ID:
+ case MMA8453_DEVICE_ID:
+ case MMA8652_DEVICE_ID:
+ case MMA8653_DEVICE_ID:
+ if (ret == data->chip_info->chip_id)
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ dev_info(&client->dev, "registering %s accelerometer; ID 0x%x\n",
+ match->compatible, data->chip_info->chip_id);
i2c_set_clientdata(client, indio_dev);
indio_dev->info = &mma8452_info;
indio_dev->name = id->name;
indio_dev->dev.parent = &client->dev;
indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->channels = mma8452_channels;
- indio_dev->num_channels = ARRAY_SIZE(mma8452_channels);
+ indio_dev->channels = data->chip_info->channels;
+ indio_dev->num_channels = data->chip_info->num_channels;
indio_dev->available_scan_masks = mma8452_scan_masks;
ret = mma8452_reset(client);
@@ -892,13 +1121,15 @@ static int mma8452_probe(struct i2c_client *client,
if (client->irq) {
/*
- * Although we enable the transient interrupt source once and
- * for all here the transient event detection itself is not
- * enabled until userspace asks for it by
- * mma8452_write_event_config()
+ * Although we enable the interrupt sources once and for
+ * all here the event detection itself is not enabled until
+ * userspace asks for it by mma8452_write_event_config()
*/
- int supported_interrupts = MMA8452_INT_DRDY | MMA8452_INT_TRANS;
- int enabled_interrupts = MMA8452_INT_TRANS;
+ int supported_interrupts = MMA8452_INT_DRDY |
+ MMA8452_INT_TRANS |
+ MMA8452_INT_FF_MT;
+ int enabled_interrupts = MMA8452_INT_TRANS |
+ MMA8452_INT_FF_MT;
/* Assume wired to INT1 pin */
ret = i2c_smbus_write_byte_data(client,
@@ -987,17 +1218,14 @@ static SIMPLE_DEV_PM_OPS(mma8452_pm_ops, mma8452_suspend, mma8452_resume);
#endif
static const struct i2c_device_id mma8452_id[] = {
- { "mma8452", 0 },
+ { "mma8452", mma8452 },
+ { "mma8453", mma8453 },
+ { "mma8652", mma8652 },
+ { "mma8653", mma8653 },
{ }
};
MODULE_DEVICE_TABLE(i2c, mma8452_id);
-static const struct of_device_id mma8452_dt_ids[] = {
- { .compatible = "fsl,mma8452" },
- { }
-};
-MODULE_DEVICE_TABLE(of, mma8452_dt_ids);
-
static struct i2c_driver mma8452_driver = {
.driver = {
.name = "mma8452",
diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c
new file mode 100644
index 000000000000..e72e218c2696
--- /dev/null
+++ b/drivers/iio/accel/mxc4005.c
@@ -0,0 +1,567 @@
+/*
+ * 3-axis accelerometer driver for MXC4005XC Memsic sensor
+ *
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regmap.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#define MXC4005_DRV_NAME "mxc4005"
+#define MXC4005_IRQ_NAME "mxc4005_event"
+#define MXC4005_REGMAP_NAME "mxc4005_regmap"
+
+#define MXC4005_REG_XOUT_UPPER 0x03
+#define MXC4005_REG_XOUT_LOWER 0x04
+#define MXC4005_REG_YOUT_UPPER 0x05
+#define MXC4005_REG_YOUT_LOWER 0x06
+#define MXC4005_REG_ZOUT_UPPER 0x07
+#define MXC4005_REG_ZOUT_LOWER 0x08
+
+#define MXC4005_REG_INT_MASK1 0x0B
+#define MXC4005_REG_INT_MASK1_BIT_DRDYE 0x01
+
+#define MXC4005_REG_INT_CLR1 0x01
+#define MXC4005_REG_INT_CLR1_BIT_DRDYC 0x01
+
+#define MXC4005_REG_CONTROL 0x0D
+#define MXC4005_REG_CONTROL_MASK_FSR GENMASK(6, 5)
+#define MXC4005_CONTROL_FSR_SHIFT 5
+
+#define MXC4005_REG_DEVICE_ID 0x0E
+
+enum mxc4005_axis {
+ AXIS_X,
+ AXIS_Y,
+ AXIS_Z,
+};
+
+enum mxc4005_range {
+ MXC4005_RANGE_2G,
+ MXC4005_RANGE_4G,
+ MXC4005_RANGE_8G,
+};
+
+struct mxc4005_data {
+ struct device *dev;
+ struct mutex mutex;
+ struct regmap *regmap;
+ struct iio_trigger *dready_trig;
+ __be16 buffer[8];
+ bool trigger_enabled;
+};
+
+/*
+ * MXC4005 can operate in the following ranges:
+ * +/- 2G, 4G, 8G (the default +/-2G)
+ *
+ * (2 + 2) * 9.81 / (2^12 - 1) = 0.009582
+ * (4 + 4) * 9.81 / (2^12 - 1) = 0.019164
+ * (8 + 8) * 9.81 / (2^12 - 1) = 0.038329
+ */
+static const struct {
+ u8 range;
+ int scale;
+} mxc4005_scale_table[] = {
+ {MXC4005_RANGE_2G, 9582},
+ {MXC4005_RANGE_4G, 19164},
+ {MXC4005_RANGE_8G, 38329},
+};
+
+
+static IIO_CONST_ATTR(in_accel_scale_available, "0.009582 0.019164 0.038329");
+
+static struct attribute *mxc4005_attributes[] = {
+ &iio_const_attr_in_accel_scale_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group mxc4005_attrs_group = {
+ .attrs = mxc4005_attributes,
+};
+
+static bool mxc4005_is_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MXC4005_REG_XOUT_UPPER:
+ case MXC4005_REG_XOUT_LOWER:
+ case MXC4005_REG_YOUT_UPPER:
+ case MXC4005_REG_YOUT_LOWER:
+ case MXC4005_REG_ZOUT_UPPER:
+ case MXC4005_REG_ZOUT_LOWER:
+ case MXC4005_REG_DEVICE_ID:
+ case MXC4005_REG_CONTROL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mxc4005_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MXC4005_REG_INT_CLR1:
+ case MXC4005_REG_INT_MASK1:
+ case MXC4005_REG_CONTROL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config mxc4005_regmap_config = {
+ .name = MXC4005_REGMAP_NAME,
+
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = MXC4005_REG_DEVICE_ID,
+
+ .readable_reg = mxc4005_is_readable_reg,
+ .writeable_reg = mxc4005_is_writeable_reg,
+};
+
+static int mxc4005_read_xyz(struct mxc4005_data *data)
+{
+ int ret;
+
+ ret = regmap_bulk_read(data->regmap, MXC4005_REG_XOUT_UPPER,
+ (u8 *) data->buffer, sizeof(data->buffer));
+ if (ret < 0) {
+ dev_err(data->dev, "failed to read axes\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mxc4005_read_axis(struct mxc4005_data *data,
+ unsigned int addr)
+{
+ __be16 reg;
+ int ret;
+
+ ret = regmap_bulk_read(data->regmap, addr, (u8 *) &reg, sizeof(reg));
+ if (ret < 0) {
+ dev_err(data->dev, "failed to read reg %02x\n", addr);
+ return ret;
+ }
+
+ return be16_to_cpu(reg);
+}
+
+static int mxc4005_read_scale(struct mxc4005_data *data)
+{
+ unsigned int reg;
+ int ret;
+ int i;
+
+ ret = regmap_read(data->regmap, MXC4005_REG_CONTROL, &reg);
+ if (ret < 0) {
+ dev_err(data->dev, "failed to read reg_control\n");
+ return ret;
+ }
+
+ i = reg >> MXC4005_CONTROL_FSR_SHIFT;
+
+ if (i < 0 || i >= ARRAY_SIZE(mxc4005_scale_table))
+ return -EINVAL;
+
+ return mxc4005_scale_table[i].scale;
+}
+
+static int mxc4005_set_scale(struct mxc4005_data *data, int val)
+{
+ unsigned int reg;
+ int i;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(mxc4005_scale_table); i++) {
+ if (mxc4005_scale_table[i].scale == val) {
+ reg = i << MXC4005_CONTROL_FSR_SHIFT;
+ ret = regmap_update_bits(data->regmap,
+ MXC4005_REG_CONTROL,
+ MXC4005_REG_CONTROL_MASK_FSR,
+ reg);
+ if (ret < 0)
+ dev_err(data->dev,
+ "failed to write reg_control\n");
+ return ret;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int mxc4005_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct mxc4005_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_ACCEL:
+ if (iio_buffer_enabled(indio_dev))
+ return -EBUSY;
+
+ ret = mxc4005_read_axis(data, chan->address);
+ if (ret < 0)
+ return ret;
+ *val = sign_extend32(ret >> chan->scan_type.shift,
+ chan->scan_type.realbits - 1);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ ret = mxc4005_read_scale(data);
+ if (ret < 0)
+ return ret;
+
+ *val = 0;
+ *val2 = ret;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mxc4005_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct mxc4005_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (val != 0)
+ return -EINVAL;
+
+ return mxc4005_set_scale(data, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info mxc4005_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = mxc4005_read_raw,
+ .write_raw = mxc4005_write_raw,
+ .attrs = &mxc4005_attrs_group,
+};
+
+static const unsigned long mxc4005_scan_masks[] = {
+ BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z),
+ 0
+};
+
+#define MXC4005_CHANNEL(_axis, _addr) { \
+ .type = IIO_ACCEL, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##_axis, \
+ .address = _addr, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_index = AXIS_##_axis, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ .shift = 4, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+static const struct iio_chan_spec mxc4005_channels[] = {
+ MXC4005_CHANNEL(X, MXC4005_REG_XOUT_UPPER),
+ MXC4005_CHANNEL(Y, MXC4005_REG_YOUT_UPPER),
+ MXC4005_CHANNEL(Z, MXC4005_REG_ZOUT_UPPER),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static irqreturn_t mxc4005_trigger_handler(int irq, void *private)
+{
+ struct iio_poll_func *pf = private;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct mxc4005_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = mxc4005_read_xyz(data);
+ if (ret < 0)
+ goto err;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+ pf->timestamp);
+
+err:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int mxc4005_clr_intr(struct mxc4005_data *data)
+{
+ int ret;
+
+ /* clear interrupt */
+ ret = regmap_write(data->regmap, MXC4005_REG_INT_CLR1,
+ MXC4005_REG_INT_CLR1_BIT_DRDYC);
+ if (ret < 0) {
+ dev_err(data->dev, "failed to write to reg_int_clr1\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mxc4005_set_trigger_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct mxc4005_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->mutex);
+ if (state) {
+ ret = regmap_write(data->regmap, MXC4005_REG_INT_MASK1,
+ MXC4005_REG_INT_MASK1_BIT_DRDYE);
+ } else {
+ ret = regmap_write(data->regmap, MXC4005_REG_INT_MASK1,
+ ~MXC4005_REG_INT_MASK1_BIT_DRDYE);
+ }
+
+ if (ret < 0) {
+ mutex_unlock(&data->mutex);
+ dev_err(data->dev, "failed to update reg_int_mask1");
+ return ret;
+ }
+
+ data->trigger_enabled = state;
+ mutex_unlock(&data->mutex);
+
+ return 0;
+}
+
+static int mxc4005_trigger_try_reen(struct iio_trigger *trig)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct mxc4005_data *data = iio_priv(indio_dev);
+
+ if (!data->dready_trig)
+ return 0;
+
+ return mxc4005_clr_intr(data);
+}
+
+static const struct iio_trigger_ops mxc4005_trigger_ops = {
+ .set_trigger_state = mxc4005_set_trigger_state,
+ .try_reenable = mxc4005_trigger_try_reen,
+ .owner = THIS_MODULE,
+};
+
+static int mxc4005_gpio_probe(struct i2c_client *client,
+ struct mxc4005_data *data)
+{
+ struct device *dev;
+ struct gpio_desc *gpio;
+ int ret;
+
+ if (!client)
+ return -EINVAL;
+
+ dev = &client->dev;
+
+ gpio = devm_gpiod_get_index(dev, "mxc4005_int", 0, GPIOD_IN);
+ if (IS_ERR(gpio)) {
+ dev_err(dev, "failed to get acpi gpio index\n");
+ return PTR_ERR(gpio);
+ }
+
+ ret = gpiod_to_irq(gpio);
+
+ dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
+
+ return ret;
+}
+
+static int mxc4005_chip_init(struct mxc4005_data *data)
+{
+ int ret;
+ unsigned int reg;
+
+ ret = regmap_read(data->regmap, MXC4005_REG_DEVICE_ID, &reg);
+ if (ret < 0) {
+ dev_err(data->dev, "failed to read chip id\n");
+ return ret;
+ }
+
+ dev_dbg(data->dev, "MXC4005 chip id %02x\n", reg);
+
+ return 0;
+}
+
+static int mxc4005_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mxc4005_data *data;
+ struct iio_dev *indio_dev;
+ struct regmap *regmap;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ regmap = devm_regmap_init_i2c(client, &mxc4005_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&client->dev, "failed to initialize regmap\n");
+ return PTR_ERR(regmap);
+ }
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->dev = &client->dev;
+ data->regmap = regmap;
+
+ ret = mxc4005_chip_init(data);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to initialize chip\n");
+ return ret;
+ }
+
+ mutex_init(&data->mutex);
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->channels = mxc4005_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mxc4005_channels);
+ indio_dev->available_scan_masks = mxc4005_scan_masks;
+ indio_dev->name = MXC4005_DRV_NAME;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &mxc4005_info;
+
+ ret = iio_triggered_buffer_setup(indio_dev,
+ iio_pollfunc_store_time,
+ mxc4005_trigger_handler,
+ NULL);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "failed to setup iio triggered buffer\n");
+ return ret;
+ }
+
+ if (client->irq < 0)
+ client->irq = mxc4005_gpio_probe(client, data);
+
+ if (client->irq > 0) {
+ data->dready_trig = devm_iio_trigger_alloc(&client->dev,
+ "%s-dev%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (!data->dready_trig)
+ return -ENOMEM;
+
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ iio_trigger_generic_data_rdy_poll,
+ NULL,
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ MXC4005_IRQ_NAME,
+ data->dready_trig);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to init threaded irq\n");
+ goto err_buffer_cleanup;
+ }
+
+ data->dready_trig->dev.parent = &client->dev;
+ data->dready_trig->ops = &mxc4005_trigger_ops;
+ iio_trigger_set_drvdata(data->dready_trig, indio_dev);
+ indio_dev->trig = data->dready_trig;
+ iio_trigger_get(indio_dev->trig);
+ ret = iio_trigger_register(data->dready_trig);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to register trigger\n");
+ goto err_trigger_unregister;
+ }
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "unable to register iio device %d\n", ret);
+ goto err_buffer_cleanup;
+ }
+
+ return 0;
+
+err_trigger_unregister:
+ iio_trigger_unregister(data->dready_trig);
+err_buffer_cleanup:
+ iio_triggered_buffer_cleanup(indio_dev);
+
+ return ret;
+}
+
+static int mxc4005_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct mxc4005_data *data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ iio_triggered_buffer_cleanup(indio_dev);
+ if (data->dready_trig)
+ iio_trigger_unregister(data->dready_trig);
+
+ return 0;
+}
+
+static const struct acpi_device_id mxc4005_acpi_match[] = {
+ {"MXC4005", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, mxc4005_acpi_match);
+
+static const struct i2c_device_id mxc4005_id[] = {
+ {"mxc4005", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, mxc4005_id);
+
+static struct i2c_driver mxc4005_driver = {
+ .driver = {
+ .name = MXC4005_DRV_NAME,
+ .acpi_match_table = ACPI_PTR(mxc4005_acpi_match),
+ },
+ .probe = mxc4005_probe,
+ .remove = mxc4005_remove,
+ .id_table = mxc4005_id,
+};
+
+module_i2c_driver(mxc4005_driver);
+
+MODULE_AUTHOR("Teodora Baluta <teodora.baluta@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MXC4005 3-axis accelerometer driver");
diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c
index ff30f8806880..dab8b76c1427 100644
--- a/drivers/iio/accel/st_accel_core.c
+++ b/drivers/iio/accel/st_accel_core.c
@@ -618,6 +618,7 @@ static const struct iio_info accel_info = {
.attrs = &st_accel_attribute_group,
.read_raw = &st_accel_read_raw,
.write_raw = &st_accel_write_raw,
+ .debugfs_reg_access = &st_sensors_debugfs_reg_access,
};
#ifdef CONFIG_IIO_TRIGGER
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 50c103d75af9..7868c744fd4b 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -149,6 +149,17 @@ config BERLIN2_ADC
Marvell Berlin2 ADC driver. This ADC has 8 channels, with one used for
temperature measurement.
+config CC10001_ADC
+ tristate "Cosmic Circuits 10001 ADC driver"
+ depends on HAS_IOMEM && HAVE_CLK && REGULATOR
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Cosmic Circuits 10001 ADC.
+
+ This driver can also be built as a module. If so, the module will be
+ called cc10001_adc.
+
config DA9150_GPADC
tristate "Dialog DA9150 GPADC driver support"
depends on MFD_DA9150
@@ -161,17 +172,6 @@ config DA9150_GPADC
To compile this driver as a module, choose M here: the module will be
called berlin2-adc.
-config CC10001_ADC
- tristate "Cosmic Circuits 10001 ADC driver"
- depends on HAS_IOMEM && HAVE_CLK && REGULATOR
- select IIO_BUFFER
- select IIO_TRIGGERED_BUFFER
- help
- Say yes here to build support for Cosmic Circuits 10001 ADC.
-
- This driver can also be built as a module. If so, the module will be
- called cc10001_adc.
-
config EXYNOS_ADC
tristate "Exynos ADC driver support"
depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST)
@@ -183,6 +183,17 @@ config EXYNOS_ADC
To compile this driver as a module, choose M here: the module will be
called exynos_adc.
+config HI8435
+ tristate "Holt Integrated Circuits HI-8435 threshold detector"
+ select IIO_TRIGGERED_EVENT
+ depends on SPI
+ help
+ If you say yes here you get support for Holt Integrated Circuits
+ HI-8435 chip.
+
+ This driver can also be built as a module. If so, the module will be
+ called hi8435.
+
config LP8788_ADC
tristate "LP8788 ADC driver"
depends on MFD_LP8788
@@ -361,6 +372,8 @@ config TWL6030_GPADC
config VF610_ADC
tristate "Freescale vf610 ADC driver"
depends on OF
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
help
Say yes here to support for Vybrid board analog-to-digital converter.
Since the IP is used for i.MX6SLX, the driver also support i.MX6SLX.
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index a0962103e866..99b37a963a1e 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -16,9 +16,10 @@ obj-$(CONFIG_AD799X) += ad799x.o
obj-$(CONFIG_AT91_ADC) += at91_adc.o
obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
-obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
+obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
+obj-$(CONFIG_HI8435) += hi8435.o
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
obj-$(CONFIG_MAX1027) += max1027.o
obj-$(CONFIG_MAX1363) += max1363.o
diff --git a/drivers/iio/adc/berlin2-adc.c b/drivers/iio/adc/berlin2-adc.c
index 4946d9bf1764..71c806ecc722 100644
--- a/drivers/iio/adc/berlin2-adc.c
+++ b/drivers/iio/adc/berlin2-adc.c
@@ -27,13 +27,13 @@
#define BERLIN2_SM_CTRL_SM_SOC_INT BIT(1)
#define BERLIN2_SM_CTRL_SOC_SM_INT BIT(2)
#define BERLIN2_SM_CTRL_ADC_SEL(x) ((x) << 5) /* 0-15 */
-#define BERLIN2_SM_CTRL_ADC_SEL_MASK (0xf << 5)
+#define BERLIN2_SM_CTRL_ADC_SEL_MASK GENMASK(8, 5)
#define BERLIN2_SM_CTRL_ADC_POWER BIT(9)
#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV2 (0x0 << 10)
#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV3 (0x1 << 10)
#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV4 (0x2 << 10)
#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV8 (0x3 << 10)
-#define BERLIN2_SM_CTRL_ADC_CLKSEL_MASK (0x3 << 10)
+#define BERLIN2_SM_CTRL_ADC_CLKSEL_MASK GENMASK(11, 10)
#define BERLIN2_SM_CTRL_ADC_START BIT(12)
#define BERLIN2_SM_CTRL_ADC_RESET BIT(13)
#define BERLIN2_SM_CTRL_ADC_BANDGAP_RDY BIT(14)
@@ -50,7 +50,7 @@
#define BERLIN2_SM_CTRL_TSEN_MODE_10_50 (0x1 << 22) /* 10-50 C */
#define BERLIN2_SM_CTRL_TSEN_RESET BIT(29)
#define BERLIN2_SM_ADC_DATA 0x20
-#define BERLIN2_SM_ADC_MASK 0x3ff
+#define BERLIN2_SM_ADC_MASK GENMASK(9, 0)
#define BERLIN2_SM_ADC_STATUS 0x1c
#define BERLIN2_SM_ADC_STATUS_DATA_RDY(x) BIT(x) /* 0-15 */
#define BERLIN2_SM_ADC_STATUS_DATA_RDY_MASK GENMASK(15, 0)
@@ -65,9 +65,9 @@
#define BERLIN2_SM_TSEN_CTRL_START BIT(8)
#define BERLIN2_SM_TSEN_CTRL_SETTLING_4 (0x0 << 21) /* 4 us */
#define BERLIN2_SM_TSEN_CTRL_SETTLING_12 (0x1 << 21) /* 12 us */
-#define BERLIN2_SM_TSEN_CTRL_SETTLING_MASK (0x1 << 21)
+#define BERLIN2_SM_TSEN_CTRL_SETTLING_MASK BIT(21)
#define BERLIN2_SM_TSEN_CTRL_TRIM(x) ((x) << 22)
-#define BERLIN2_SM_TSEN_CTRL_TRIM_MASK (0xf << 22)
+#define BERLIN2_SM_TSEN_CTRL_TRIM_MASK GENMASK(25, 22)
struct berlin2_adc_priv {
struct regmap *regmap;
@@ -78,13 +78,13 @@ struct berlin2_adc_priv {
};
#define BERLIN2_ADC_CHANNEL(n, t) \
- { \
- .channel = n, \
- .datasheet_name = "channel"#n, \
- .type = t, \
- .indexed = 1, \
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
- }
+ { \
+ .channel = n, \
+ .datasheet_name = "channel"#n, \
+ .type = t, \
+ .indexed = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ }
static const struct iio_chan_spec berlin2_adc_channels[] = {
BERLIN2_ADC_CHANNEL(0, IIO_VOLTAGE), /* external input */
@@ -111,18 +111,24 @@ static int berlin2_adc_read(struct iio_dev *indio_dev, int channel)
mutex_lock(&priv->lock);
+ /* Enable the interrupts */
+ regmap_write(priv->regmap, BERLIN2_SM_ADC_STATUS,
+ BERLIN2_SM_ADC_STATUS_INT_EN(channel));
+
/* Configure the ADC */
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
- BERLIN2_SM_CTRL_ADC_RESET | BERLIN2_SM_CTRL_ADC_SEL_MASK
- | BERLIN2_SM_CTRL_ADC_START,
- BERLIN2_SM_CTRL_ADC_SEL(channel) | BERLIN2_SM_CTRL_ADC_START);
+ BERLIN2_SM_CTRL_ADC_RESET |
+ BERLIN2_SM_CTRL_ADC_SEL_MASK |
+ BERLIN2_SM_CTRL_ADC_START,
+ BERLIN2_SM_CTRL_ADC_SEL(channel) |
+ BERLIN2_SM_CTRL_ADC_START);
ret = wait_event_interruptible_timeout(priv->wq, priv->data_available,
- msecs_to_jiffies(1000));
+ msecs_to_jiffies(1000));
/* Disable the interrupts */
regmap_update_bits(priv->regmap, BERLIN2_SM_ADC_STATUS,
- BERLIN2_SM_ADC_STATUS_INT_EN(channel), 0);
+ BERLIN2_SM_ADC_STATUS_INT_EN(channel), 0);
if (ret == 0)
ret = -ETIMEDOUT;
@@ -132,7 +138,7 @@ static int berlin2_adc_read(struct iio_dev *indio_dev, int channel)
}
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
- BERLIN2_SM_CTRL_ADC_START, 0);
+ BERLIN2_SM_CTRL_ADC_START, 0);
data = priv->data;
priv->data_available = false;
@@ -149,24 +155,31 @@ static int berlin2_adc_tsen_read(struct iio_dev *indio_dev)
mutex_lock(&priv->lock);
+ /* Enable interrupts */
+ regmap_write(priv->regmap, BERLIN2_SM_TSEN_STATUS,
+ BERLIN2_SM_TSEN_STATUS_INT_EN);
+
/* Configure the ADC */
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
- BERLIN2_SM_CTRL_TSEN_RESET | BERLIN2_SM_CTRL_ADC_ROTATE,
- BERLIN2_SM_CTRL_ADC_ROTATE);
+ BERLIN2_SM_CTRL_TSEN_RESET |
+ BERLIN2_SM_CTRL_ADC_ROTATE,
+ BERLIN2_SM_CTRL_ADC_ROTATE);
/* Configure the temperature sensor */
regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_CTRL,
- BERLIN2_SM_TSEN_CTRL_TRIM_MASK | BERLIN2_SM_TSEN_CTRL_SETTLING_MASK
- | BERLIN2_SM_TSEN_CTRL_START,
- BERLIN2_SM_TSEN_CTRL_TRIM(3) | BERLIN2_SM_TSEN_CTRL_SETTLING_12
- | BERLIN2_SM_TSEN_CTRL_START);
+ BERLIN2_SM_TSEN_CTRL_TRIM_MASK |
+ BERLIN2_SM_TSEN_CTRL_SETTLING_MASK |
+ BERLIN2_SM_TSEN_CTRL_START,
+ BERLIN2_SM_TSEN_CTRL_TRIM(3) |
+ BERLIN2_SM_TSEN_CTRL_SETTLING_12 |
+ BERLIN2_SM_TSEN_CTRL_START);
ret = wait_event_interruptible_timeout(priv->wq, priv->data_available,
- msecs_to_jiffies(1000));
+ msecs_to_jiffies(1000));
/* Disable interrupts */
regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_STATUS,
- BERLIN2_SM_TSEN_STATUS_INT_EN, 0);
+ BERLIN2_SM_TSEN_STATUS_INT_EN, 0);
if (ret == 0)
ret = -ETIMEDOUT;
@@ -176,7 +189,7 @@ static int berlin2_adc_tsen_read(struct iio_dev *indio_dev)
}
regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_CTRL,
- BERLIN2_SM_TSEN_CTRL_START, 0);
+ BERLIN2_SM_TSEN_CTRL_START, 0);
data = priv->data;
priv->data_available = false;
@@ -187,10 +200,9 @@ static int berlin2_adc_tsen_read(struct iio_dev *indio_dev)
}
static int berlin2_adc_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan, int *val, int *val2,
- long mask)
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
{
- struct berlin2_adc_priv *priv = iio_priv(indio_dev);
int temp;
switch (mask) {
@@ -198,10 +210,6 @@ static int berlin2_adc_read_raw(struct iio_dev *indio_dev,
if (chan->type != IIO_VOLTAGE)
return -EINVAL;
- /* Enable the interrupts */
- regmap_write(priv->regmap, BERLIN2_SM_ADC_STATUS,
- BERLIN2_SM_ADC_STATUS_INT_EN(chan->channel));
-
*val = berlin2_adc_read(indio_dev, chan->channel);
if (*val < 0)
return *val;
@@ -211,10 +219,6 @@ static int berlin2_adc_read_raw(struct iio_dev *indio_dev,
if (chan->type != IIO_TEMP)
return -EINVAL;
- /* Enable interrupts */
- regmap_write(priv->regmap, BERLIN2_SM_TSEN_STATUS,
- BERLIN2_SM_TSEN_STATUS_INT_EN);
-
temp = berlin2_adc_tsen_read(indio_dev);
if (temp < 0)
return temp;
@@ -306,12 +310,12 @@ static int berlin2_adc_probe(struct platform_device *pdev)
return tsen_irq;
ret = devm_request_irq(&pdev->dev, irq, berlin2_adc_irq, 0,
- pdev->dev.driver->name, indio_dev);
+ pdev->dev.driver->name, indio_dev);
if (ret)
return ret;
ret = devm_request_irq(&pdev->dev, tsen_irq, berlin2_adc_tsen_irq,
- 0, pdev->dev.driver->name, indio_dev);
+ 0, pdev->dev.driver->name, indio_dev);
if (ret)
return ret;
@@ -328,13 +332,14 @@ static int berlin2_adc_probe(struct platform_device *pdev)
/* Power up the ADC */
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
- BERLIN2_SM_CTRL_ADC_POWER, BERLIN2_SM_CTRL_ADC_POWER);
+ BERLIN2_SM_CTRL_ADC_POWER,
+ BERLIN2_SM_CTRL_ADC_POWER);
ret = iio_device_register(indio_dev);
if (ret) {
/* Power down the ADC */
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
- BERLIN2_SM_CTRL_ADC_POWER, 0);
+ BERLIN2_SM_CTRL_ADC_POWER, 0);
return ret;
}
@@ -350,7 +355,7 @@ static int berlin2_adc_remove(struct platform_device *pdev)
/* Power down the ADC */
regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
- BERLIN2_SM_CTRL_ADC_POWER, 0);
+ BERLIN2_SM_CTRL_ADC_POWER, 0);
return 0;
}
diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c
new file mode 100644
index 000000000000..c73c6c62a6ac
--- /dev/null
+++ b/drivers/iio/adc/hi8435.c
@@ -0,0 +1,534 @@
+/*
+ * Holt Integrated Circuits HI-8435 threshold detector driver
+ *
+ * Copyright (C) 2015 Zodiac Inflight Innovations
+ * Copyright (C) 2015 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_event.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio/consumer.h>
+
+#define DRV_NAME "hi8435"
+
+/* Register offsets for HI-8435 */
+#define HI8435_CTRL_REG 0x02
+#define HI8435_PSEN_REG 0x04
+#define HI8435_TMDATA_REG 0x1E
+#define HI8435_GOCENHYS_REG 0x3A
+#define HI8435_SOCENHYS_REG 0x3C
+#define HI8435_SO7_0_REG 0x10
+#define HI8435_SO15_8_REG 0x12
+#define HI8435_SO23_16_REG 0x14
+#define HI8435_SO31_24_REG 0x16
+#define HI8435_SO31_0_REG 0x78
+
+#define HI8435_WRITE_OPCODE 0x00
+#define HI8435_READ_OPCODE 0x80
+
+/* CTRL register bits */
+#define HI8435_CTRL_TEST 0x01
+#define HI8435_CTRL_SRST 0x02
+
+struct hi8435_priv {
+ struct spi_device *spi;
+ struct mutex lock;
+
+ unsigned long event_scan_mask; /* soft mask/unmask channels events */
+ unsigned int event_prev_val;
+
+ unsigned threshold_lo[2]; /* GND-Open and Supply-Open thresholds */
+ unsigned threshold_hi[2]; /* GND-Open and Supply-Open thresholds */
+ u8 reg_buffer[3] ____cacheline_aligned;
+};
+
+static int hi8435_readb(struct hi8435_priv *priv, u8 reg, u8 *val)
+{
+ reg |= HI8435_READ_OPCODE;
+ return spi_write_then_read(priv->spi, &reg, 1, val, 1);
+}
+
+static int hi8435_readw(struct hi8435_priv *priv, u8 reg, u16 *val)
+{
+ int ret;
+ __be16 be_val;
+
+ reg |= HI8435_READ_OPCODE;
+ ret = spi_write_then_read(priv->spi, &reg, 1, &be_val, 2);
+ *val = be16_to_cpu(be_val);
+
+ return ret;
+}
+
+static int hi8435_readl(struct hi8435_priv *priv, u8 reg, u32 *val)
+{
+ int ret;
+ __be32 be_val;
+
+ reg |= HI8435_READ_OPCODE;
+ ret = spi_write_then_read(priv->spi, &reg, 1, &be_val, 4);
+ *val = be32_to_cpu(be_val);
+
+ return ret;
+}
+
+static int hi8435_writeb(struct hi8435_priv *priv, u8 reg, u8 val)
+{
+ priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
+ priv->reg_buffer[1] = val;
+
+ return spi_write(priv->spi, priv->reg_buffer, 2);
+}
+
+static int hi8435_writew(struct hi8435_priv *priv, u8 reg, u16 val)
+{
+ priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE;
+ priv->reg_buffer[1] = (val >> 8) & 0xff;
+ priv->reg_buffer[2] = val & 0xff;
+
+ return spi_write(priv->spi, priv->reg_buffer, 3);
+}
+
+static int hi8435_read_event_config(struct iio_dev *idev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct hi8435_priv *priv = iio_priv(idev);
+
+ return !!(priv->event_scan_mask & BIT(chan->channel));
+}
+
+static int hi8435_write_event_config(struct iio_dev *idev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir, int state)
+{
+ struct hi8435_priv *priv = iio_priv(idev);
+
+ priv->event_scan_mask &= ~BIT(chan->channel);
+ if (state)
+ priv->event_scan_mask |= BIT(chan->channel);
+
+ return 0;
+}
+
+static int hi8435_read_event_value(struct iio_dev *idev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int *val, int *val2)
+{
+ struct hi8435_priv *priv = iio_priv(idev);
+ int ret;
+ u8 mode, psen;
+ u16 reg;
+
+ ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
+ if (ret < 0)
+ return ret;
+
+ /* Supply-Open or GND-Open sensing mode */
+ mode = !!(psen & BIT(chan->channel / 8));
+
+ ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
+ HI8435_GOCENHYS_REG, &reg);
+ if (ret < 0)
+ return ret;
+
+ if (dir == IIO_EV_DIR_FALLING)
+ *val = ((reg & 0xff) - (reg >> 8)) / 2;
+ else if (dir == IIO_EV_DIR_RISING)
+ *val = ((reg & 0xff) + (reg >> 8)) / 2;
+
+ return IIO_VAL_INT;
+}
+
+static int hi8435_write_event_value(struct iio_dev *idev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int val, int val2)
+{
+ struct hi8435_priv *priv = iio_priv(idev);
+ int ret;
+ u8 mode, psen;
+ u16 reg;
+
+ ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen);
+ if (ret < 0)
+ return ret;
+
+ /* Supply-Open or GND-Open sensing mode */
+ mode = !!(psen & BIT(chan->channel / 8));
+
+ ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
+ HI8435_GOCENHYS_REG, &reg);
+ if (ret < 0)
+ return ret;
+
+ if (dir == IIO_EV_DIR_FALLING) {
+ /* falling threshold range 2..21V, hysteresis minimum 2V */
+ if (val < 2 || val > 21 || (val + 2) > priv->threshold_hi[mode])
+ return -EINVAL;
+
+ if (val == priv->threshold_lo[mode])
+ return 0;
+
+ priv->threshold_lo[mode] = val;
+
+ /* hysteresis must not be odd */
+ if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
+ priv->threshold_hi[mode]--;
+ } else if (dir == IIO_EV_DIR_RISING) {
+ /* rising threshold range 3..22V, hysteresis minimum 2V */
+ if (val < 3 || val > 22 || val < (priv->threshold_lo[mode] + 2))
+ return -EINVAL;
+
+ if (val == priv->threshold_hi[mode])
+ return 0;
+
+ priv->threshold_hi[mode] = val;
+
+ /* hysteresis must not be odd */
+ if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2)
+ priv->threshold_lo[mode]++;
+ }
+
+ /* program thresholds */
+ mutex_lock(&priv->lock);
+
+ ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG :
+ HI8435_GOCENHYS_REG, &reg);
+ if (ret < 0) {
+ mutex_unlock(&priv->lock);
+ return ret;
+ }
+
+ /* hysteresis */
+ reg = priv->threshold_hi[mode] - priv->threshold_lo[mode];
+ reg <<= 8;
+ /* threshold center */
+ reg |= (priv->threshold_hi[mode] + priv->threshold_lo[mode]);
+
+ ret = hi8435_writew(priv, mode ? HI8435_SOCENHYS_REG :
+ HI8435_GOCENHYS_REG, reg);
+
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+static int hi8435_debugfs_reg_access(struct iio_dev *idev,
+ unsigned reg, unsigned writeval,
+ unsigned *readval)
+{
+ struct hi8435_priv *priv = iio_priv(idev);
+ int ret;
+ u8 val;
+
+ if (readval != NULL) {
+ ret = hi8435_readb(priv, reg, &val);
+ *readval = val;
+ } else {
+ val = (u8)writeval;
+ ret = hi8435_writeb(priv, reg, val);
+ }
+
+ return ret;
+}
+
+static const struct iio_event_spec hi8435_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+static int hi8435_get_sensing_mode(struct iio_dev *idev,
+ const struct iio_chan_spec *chan)
+{
+ struct hi8435_priv *priv = iio_priv(idev);
+ int ret;
+ u8 reg;
+
+ ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
+ if (ret < 0)
+ return ret;
+
+ return !!(reg & BIT(chan->channel / 8));
+}
+
+static int hi8435_set_sensing_mode(struct iio_dev *idev,
+ const struct iio_chan_spec *chan,
+ unsigned int mode)
+{
+ struct hi8435_priv *priv = iio_priv(idev);
+ int ret;
+ u8 reg;
+
+ mutex_lock(&priv->lock);
+
+ ret = hi8435_readb(priv, HI8435_PSEN_REG, &reg);
+ if (ret < 0) {
+ mutex_unlock(&priv->lock);
+ return ret;
+ }
+
+ reg &= ~BIT(chan->channel / 8);
+ if (mode)
+ reg |= BIT(chan->channel / 8);
+
+ ret = hi8435_writeb(priv, HI8435_PSEN_REG, reg);
+
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+static const char * const hi8435_sensing_modes[] = { "GND-Open",
+ "Supply-Open" };
+
+static const struct iio_enum hi8435_sensing_mode = {
+ .items = hi8435_sensing_modes,
+ .num_items = ARRAY_SIZE(hi8435_sensing_modes),
+ .get = hi8435_get_sensing_mode,
+ .set = hi8435_set_sensing_mode,
+};
+
+static const struct iio_chan_spec_ext_info hi8435_ext_info[] = {
+ IIO_ENUM("sensing_mode", IIO_SEPARATE, &hi8435_sensing_mode),
+ {},
+};
+
+#define HI8435_VOLTAGE_CHANNEL(num) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = num, \
+ .event_spec = hi8435_events, \
+ .num_event_specs = ARRAY_SIZE(hi8435_events), \
+ .ext_info = hi8435_ext_info, \
+}
+
+static const struct iio_chan_spec hi8435_channels[] = {
+ HI8435_VOLTAGE_CHANNEL(0),
+ HI8435_VOLTAGE_CHANNEL(1),
+ HI8435_VOLTAGE_CHANNEL(2),
+ HI8435_VOLTAGE_CHANNEL(3),
+ HI8435_VOLTAGE_CHANNEL(4),
+ HI8435_VOLTAGE_CHANNEL(5),
+ HI8435_VOLTAGE_CHANNEL(6),
+ HI8435_VOLTAGE_CHANNEL(7),
+ HI8435_VOLTAGE_CHANNEL(8),
+ HI8435_VOLTAGE_CHANNEL(9),
+ HI8435_VOLTAGE_CHANNEL(10),
+ HI8435_VOLTAGE_CHANNEL(11),
+ HI8435_VOLTAGE_CHANNEL(12),
+ HI8435_VOLTAGE_CHANNEL(13),
+ HI8435_VOLTAGE_CHANNEL(14),
+ HI8435_VOLTAGE_CHANNEL(15),
+ HI8435_VOLTAGE_CHANNEL(16),
+ HI8435_VOLTAGE_CHANNEL(17),
+ HI8435_VOLTAGE_CHANNEL(18),
+ HI8435_VOLTAGE_CHANNEL(19),
+ HI8435_VOLTAGE_CHANNEL(20),
+ HI8435_VOLTAGE_CHANNEL(21),
+ HI8435_VOLTAGE_CHANNEL(22),
+ HI8435_VOLTAGE_CHANNEL(23),
+ HI8435_VOLTAGE_CHANNEL(24),
+ HI8435_VOLTAGE_CHANNEL(25),
+ HI8435_VOLTAGE_CHANNEL(26),
+ HI8435_VOLTAGE_CHANNEL(27),
+ HI8435_VOLTAGE_CHANNEL(28),
+ HI8435_VOLTAGE_CHANNEL(29),
+ HI8435_VOLTAGE_CHANNEL(30),
+ HI8435_VOLTAGE_CHANNEL(31),
+ IIO_CHAN_SOFT_TIMESTAMP(32),
+};
+
+static const struct iio_info hi8435_info = {
+ .driver_module = THIS_MODULE,
+ .read_event_config = &hi8435_read_event_config,
+ .write_event_config = hi8435_write_event_config,
+ .read_event_value = &hi8435_read_event_value,
+ .write_event_value = &hi8435_write_event_value,
+ .debugfs_reg_access = &hi8435_debugfs_reg_access,
+};
+
+static void hi8435_iio_push_event(struct iio_dev *idev, unsigned int val)
+{
+ struct hi8435_priv *priv = iio_priv(idev);
+ enum iio_event_direction dir;
+ unsigned int i;
+ unsigned int status = priv->event_prev_val ^ val;
+
+ if (!status)
+ return;
+
+ for_each_set_bit(i, &priv->event_scan_mask, 32) {
+ if (status & BIT(i)) {
+ dir = val & BIT(i) ? IIO_EV_DIR_RISING :
+ IIO_EV_DIR_FALLING;
+ iio_push_event(idev,
+ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
+ IIO_EV_TYPE_THRESH, dir),
+ iio_get_time_ns());
+ }
+ }
+
+ priv->event_prev_val = val;
+}
+
+static irqreturn_t hi8435_trigger_handler(int irq, void *private)
+{
+ struct iio_poll_func *pf = private;
+ struct iio_dev *idev = pf->indio_dev;
+ struct hi8435_priv *priv = iio_priv(idev);
+ u32 val;
+ int ret;
+
+ ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val);
+ if (ret < 0)
+ goto err_read;
+
+ hi8435_iio_push_event(idev, val);
+
+err_read:
+ iio_trigger_notify_done(idev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int hi8435_probe(struct spi_device *spi)
+{
+ struct iio_dev *idev;
+ struct hi8435_priv *priv;
+ struct gpio_desc *reset_gpio;
+ int ret;
+
+ idev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
+ if (!idev)
+ return -ENOMEM;
+
+ priv = iio_priv(idev);
+ priv->spi = spi;
+
+ reset_gpio = devm_gpiod_get(&spi->dev, NULL, GPIOD_OUT_LOW);
+ if (IS_ERR(reset_gpio)) {
+ /* chip s/w reset if h/w reset failed */
+ hi8435_writeb(priv, HI8435_CTRL_REG, HI8435_CTRL_SRST);
+ hi8435_writeb(priv, HI8435_CTRL_REG, 0);
+ } else {
+ udelay(5);
+ gpiod_set_value(reset_gpio, 1);
+ }
+
+ spi_set_drvdata(spi, idev);
+ mutex_init(&priv->lock);
+
+ idev->dev.parent = &spi->dev;
+ idev->name = spi_get_device_id(spi)->name;
+ idev->modes = INDIO_DIRECT_MODE;
+ idev->info = &hi8435_info;
+ idev->channels = hi8435_channels;
+ idev->num_channels = ARRAY_SIZE(hi8435_channels);
+
+ /* unmask all events */
+ priv->event_scan_mask = ~(0);
+ /*
+ * There is a restriction in the chip - the hysteresis can not be odd.
+ * If the hysteresis is set to odd value then chip gets into lock state
+ * and not functional anymore.
+ * After chip reset the thresholds are in undefined state, so we need to
+ * initialize thresholds to some initial values and then prevent
+ * userspace setting odd hysteresis.
+ *
+ * Set threshold low voltage to 2V, threshold high voltage to 4V
+ * for both GND-Open and Supply-Open sensing modes.
+ */
+ priv->threshold_lo[0] = priv->threshold_lo[1] = 2;
+ priv->threshold_hi[0] = priv->threshold_hi[1] = 4;
+ hi8435_writew(priv, HI8435_GOCENHYS_REG, 0x206);
+ hi8435_writew(priv, HI8435_SOCENHYS_REG, 0x206);
+
+ ret = iio_triggered_event_setup(idev, NULL, hi8435_trigger_handler);
+ if (ret)
+ return ret;
+
+ ret = iio_device_register(idev);
+ if (ret < 0) {
+ dev_err(&spi->dev, "unable to register device\n");
+ goto unregister_triggered_event;
+ }
+
+ return 0;
+
+unregister_triggered_event:
+ iio_triggered_event_cleanup(idev);
+ return ret;
+}
+
+static int hi8435_remove(struct spi_device *spi)
+{
+ struct iio_dev *idev = spi_get_drvdata(spi);
+
+ iio_device_unregister(idev);
+ iio_triggered_event_cleanup(idev);
+
+ return 0;
+}
+
+static const struct of_device_id hi8435_dt_ids[] = {
+ { .compatible = "holt,hi8435" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, hi8435_dt_ids);
+
+static const struct spi_device_id hi8435_id[] = {
+ { "hi8435", 0},
+ { }
+};
+MODULE_DEVICE_TABLE(spi, hi8435_id);
+
+static struct spi_driver hi8435_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(hi8435_dt_ids),
+ },
+ .probe = hi8435_probe,
+ .remove = hi8435_remove,
+ .id_table = hi8435_id,
+};
+module_spi_driver(hi8435_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_DESCRIPTION("HI-8435 threshold detector");
diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c
index 44bf815adb6c..54a8302aaace 100644
--- a/drivers/iio/adc/max1027.c
+++ b/drivers/iio/adc/max1027.c
@@ -508,6 +508,7 @@ static int max1027_remove(struct spi_device *spi)
static struct spi_driver max1027_driver = {
.driver = {
.name = "max1027",
+ .of_match_table = of_match_ptr(max1027_adc_dt_ids),
.owner = THIS_MODULE,
},
.probe = max1027_probe,
diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c
index b19e4f9d16e0..41a21e986c1a 100644
--- a/drivers/iio/adc/mcp320x.c
+++ b/drivers/iio/adc/mcp320x.c
@@ -404,6 +404,7 @@ MODULE_DEVICE_TABLE(spi, mcp320x_id);
static struct spi_driver mcp320x_driver = {
.driver = {
.name = "mcp320x",
+ .of_match_table = of_match_ptr(mcp320x_dt_ids),
.owner = THIS_MODULE,
},
.probe = mcp320x_probe,
diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c
index 915be6b60097..98c0d2b444bf 100644
--- a/drivers/iio/adc/ti-adc128s052.c
+++ b/drivers/iio/adc/ti-adc128s052.c
@@ -174,6 +174,13 @@ static int adc128_remove(struct spi_device *spi)
return 0;
}
+static const struct of_device_id adc128_of_match[] = {
+ { .compatible = "ti,adc128s052", },
+ { .compatible = "ti,adc122s021", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, adc128_of_match);
+
static const struct spi_device_id adc128_id[] = {
{ "adc128s052", 0}, /* index into adc128_config */
{ "adc122s021", 1},
@@ -184,6 +191,7 @@ MODULE_DEVICE_TABLE(spi, adc128_id);
static struct spi_driver adc128_driver = {
.driver = {
.name = "adc128s052",
+ .of_match_table = of_match_ptr(adc128_of_match),
.owner = THIS_MODULE,
},
.probe = adc128_probe,
diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c
index df12c57e6ce0..becbb0aef232 100644
--- a/drivers/iio/adc/twl6030-gpadc.c
+++ b/drivers/iio/adc/twl6030-gpadc.c
@@ -875,6 +875,7 @@ static const struct of_device_id of_twl6030_match_tbl[] = {
},
{ /* end */ }
};
+MODULE_DEVICE_TABLE(of, of_twl6030_match_tbl);
static int twl6030_gpadc_probe(struct platform_device *pdev)
{
diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c
index 6bf4c20eb231..599cde3d03a1 100644
--- a/drivers/iio/adc/vf610_adc.c
+++ b/drivers/iio/adc/vf610_adc.c
@@ -34,8 +34,11 @@
#include <linux/err.h>
#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
#include <linux/iio/sysfs.h>
-#include <linux/iio/driver.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
/* This will be the driver name the kernel reports */
#define DRIVER_NAME "vf610-adc"
@@ -170,6 +173,7 @@ struct vf610_adc {
u32 sample_freq_avail[5];
struct completion completion;
+ u16 buffer[8];
};
static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 };
@@ -505,12 +509,24 @@ static const struct iio_chan_spec_ext_info vf610_ext_info[] = {
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.ext_info = vf610_ext_info, \
+ .scan_index = (_idx), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ }, \
}
#define VF610_ADC_TEMPERATURE_CHAN(_idx, _chan_type) { \
.type = (_chan_type), \
.channel = (_idx), \
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
+ .scan_index = (_idx), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ }, \
}
static const struct iio_chan_spec vf610_adc_iio_channels[] = {
@@ -531,6 +547,7 @@ static const struct iio_chan_spec vf610_adc_iio_channels[] = {
VF610_ADC_CHAN(14, IIO_VOLTAGE),
VF610_ADC_CHAN(15, IIO_VOLTAGE),
VF610_ADC_TEMPERATURE_CHAN(26, IIO_TEMP),
+ IIO_CHAN_SOFT_TIMESTAMP(32),
/* sentinel */
};
@@ -559,13 +576,20 @@ static int vf610_adc_read_data(struct vf610_adc *info)
static irqreturn_t vf610_adc_isr(int irq, void *dev_id)
{
- struct vf610_adc *info = (struct vf610_adc *)dev_id;
+ struct iio_dev *indio_dev = (struct iio_dev *)dev_id;
+ struct vf610_adc *info = iio_priv(indio_dev);
int coco;
coco = readl(info->regs + VF610_REG_ADC_HS);
if (coco & VF610_ADC_HS_COCO0) {
info->value = vf610_adc_read_data(info);
- complete(&info->completion);
+ if (iio_buffer_enabled(indio_dev)) {
+ info->buffer[0] = info->value;
+ iio_push_to_buffers_with_timestamp(indio_dev,
+ info->buffer, iio_get_time_ns());
+ iio_trigger_notify_done(indio_dev->trig);
+ } else
+ complete(&info->completion);
}
return IRQ_HANDLED;
@@ -613,8 +637,12 @@ static int vf610_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_RAW:
case IIO_CHAN_INFO_PROCESSED:
mutex_lock(&indio_dev->mlock);
- reinit_completion(&info->completion);
+ if (iio_buffer_enabled(indio_dev)) {
+ mutex_unlock(&indio_dev->mlock);
+ return -EBUSY;
+ }
+ reinit_completion(&info->completion);
hc_cfg = VF610_ADC_ADCHC(chan->channel);
hc_cfg |= VF610_ADC_AIEN;
writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
@@ -694,6 +722,56 @@ static int vf610_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
+static int vf610_adc_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct vf610_adc *info = iio_priv(indio_dev);
+ unsigned int channel;
+ int ret;
+ int val;
+
+ ret = iio_triggered_buffer_postenable(indio_dev);
+ if (ret)
+ return ret;
+
+ val = readl(info->regs + VF610_REG_ADC_GC);
+ val |= VF610_ADC_ADCON;
+ writel(val, info->regs + VF610_REG_ADC_GC);
+
+ channel = find_first_bit(indio_dev->active_scan_mask,
+ indio_dev->masklength);
+
+ val = VF610_ADC_ADCHC(channel);
+ val |= VF610_ADC_AIEN;
+
+ writel(val, info->regs + VF610_REG_ADC_HC0);
+
+ return 0;
+}
+
+static int vf610_adc_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct vf610_adc *info = iio_priv(indio_dev);
+ unsigned int hc_cfg = 0;
+ int val;
+
+ val = readl(info->regs + VF610_REG_ADC_GC);
+ val &= ~VF610_ADC_ADCON;
+ writel(val, info->regs + VF610_REG_ADC_GC);
+
+ hc_cfg |= VF610_ADC_CONV_DISABLE;
+ hc_cfg &= ~VF610_ADC_AIEN;
+
+ writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
+
+ return iio_triggered_buffer_predisable(indio_dev);
+}
+
+static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
+ .postenable = &vf610_adc_buffer_postenable,
+ .predisable = &vf610_adc_buffer_predisable,
+ .validate_scan_mask = &iio_validate_scan_mask_onehot,
+};
+
static int vf610_adc_reg_access(struct iio_dev *indio_dev,
unsigned reg, unsigned writeval,
unsigned *readval)
@@ -753,7 +831,7 @@ static int vf610_adc_probe(struct platform_device *pdev)
ret = devm_request_irq(info->dev, irq,
vf610_adc_isr, 0,
- dev_name(&pdev->dev), info);
+ dev_name(&pdev->dev), indio_dev);
if (ret < 0) {
dev_err(&pdev->dev, "failed requesting irq, irq = %d\n", irq);
return ret;
@@ -806,15 +884,23 @@ static int vf610_adc_probe(struct platform_device *pdev)
vf610_adc_cfg_init(info);
vf610_adc_hw_init(info);
+ ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+ NULL, &iio_triggered_buffer_setup_ops);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Couldn't initialise the buffer\n");
+ goto error_iio_device_register;
+ }
+
ret = iio_device_register(indio_dev);
if (ret) {
dev_err(&pdev->dev, "Couldn't register the device.\n");
- goto error_iio_device_register;
+ goto error_adc_buffer_init;
}
return 0;
-
+error_adc_buffer_init:
+ iio_triggered_buffer_cleanup(indio_dev);
error_iio_device_register:
clk_disable_unprepare(info->clk);
error_adc_clk_enable:
@@ -829,6 +915,7 @@ static int vf610_adc_remove(struct platform_device *pdev)
struct vf610_adc *info = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
regulator_disable(info->vref);
clk_disable_unprepare(info->clk);
diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c
index ce93bd8e3f68..0370624a35db 100644
--- a/drivers/iio/adc/xilinx-xadc-core.c
+++ b/drivers/iio/adc/xilinx-xadc-core.c
@@ -273,33 +273,13 @@ static void xadc_zynq_unmask_worker(struct work_struct *work)
schedule_delayed_work(&xadc->zynq_unmask_work,
msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT));
}
-}
-
-static irqreturn_t xadc_zynq_threaded_interrupt_handler(int irq, void *devid)
-{
- struct iio_dev *indio_dev = devid;
- struct xadc *xadc = iio_priv(indio_dev);
- unsigned int alarm;
-
- spin_lock_irq(&xadc->lock);
- alarm = xadc->zynq_alarm;
- xadc->zynq_alarm = 0;
- spin_unlock_irq(&xadc->lock);
-
- xadc_handle_events(indio_dev, xadc_zynq_transform_alarm(alarm));
- /* unmask the required interrupts in timer. */
- schedule_delayed_work(&xadc->zynq_unmask_work,
- msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT));
-
- return IRQ_HANDLED;
}
static irqreturn_t xadc_zynq_interrupt_handler(int irq, void *devid)
{
struct iio_dev *indio_dev = devid;
struct xadc *xadc = iio_priv(indio_dev);
- irqreturn_t ret = IRQ_HANDLED;
uint32_t status;
xadc_read_reg(xadc, XADC_ZYNQ_REG_INTSTS, &status);
@@ -321,18 +301,23 @@ static irqreturn_t xadc_zynq_interrupt_handler(int irq, void *devid)
status &= XADC_ZYNQ_INT_ALARM_MASK;
if (status) {
- xadc->zynq_alarm |= status;
xadc->zynq_masked_alarm |= status;
/*
* mask the current event interrupt,
* unmask it when the interrupt is no more active.
*/
xadc_zynq_update_intmsk(xadc, 0, 0);
- ret = IRQ_WAKE_THREAD;
+
+ xadc_handle_events(indio_dev,
+ xadc_zynq_transform_alarm(status));
+
+ /* unmask the required interrupts in timer. */
+ schedule_delayed_work(&xadc->zynq_unmask_work,
+ msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT));
}
spin_unlock(&xadc->lock);
- return ret;
+ return IRQ_HANDLED;
}
#define XADC_ZYNQ_TCK_RATE_MAX 50000000
@@ -437,7 +422,6 @@ static const struct xadc_ops xadc_zynq_ops = {
.setup = xadc_zynq_setup,
.get_dclk_rate = xadc_zynq_get_dclk_rate,
.interrupt_handler = xadc_zynq_interrupt_handler,
- .threaded_interrupt_handler = xadc_zynq_threaded_interrupt_handler,
.update_alarm = xadc_zynq_update_alarm,
};
@@ -1225,9 +1209,8 @@ static int xadc_probe(struct platform_device *pdev)
if (ret)
goto err_free_samplerate_trigger;
- ret = request_threaded_irq(irq, xadc->ops->interrupt_handler,
- xadc->ops->threaded_interrupt_handler,
- 0, dev_name(&pdev->dev), indio_dev);
+ ret = request_irq(irq, xadc->ops->interrupt_handler, 0,
+ dev_name(&pdev->dev), indio_dev);
if (ret)
goto err_clk_disable_unprepare;
diff --git a/drivers/iio/adc/xilinx-xadc.h b/drivers/iio/adc/xilinx-xadc.h
index 54adc5087210..f6f081965647 100644
--- a/drivers/iio/adc/xilinx-xadc.h
+++ b/drivers/iio/adc/xilinx-xadc.h
@@ -60,7 +60,6 @@ struct xadc {
enum xadc_external_mux_mode external_mux_mode;
- unsigned int zynq_alarm;
unsigned int zynq_masked_alarm;
unsigned int zynq_intmask;
struct delayed_work zynq_unmask_work;
@@ -79,7 +78,6 @@ struct xadc_ops {
void (*update_alarm)(struct xadc *, unsigned int);
unsigned long (*get_dclk_rate)(struct xadc *);
irqreturn_t (*interrupt_handler)(int, void *);
- irqreturn_t (*threaded_interrupt_handler)(int, void *);
unsigned int flags;
};
diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c
index c0d364ebaea8..32b82a2dc894 100644
--- a/drivers/iio/amplifiers/ad8366.c
+++ b/drivers/iio/amplifiers/ad8366.c
@@ -195,6 +195,7 @@ static const struct spi_device_id ad8366_id[] = {
{"ad8366", 0},
{}
};
+MODULE_DEVICE_TABLE(spi, ad8366_id);
static struct spi_driver ad8366_driver = {
.driver = {
diff --git a/drivers/iio/buffer/Kconfig b/drivers/iio/buffer/Kconfig
new file mode 100644
index 000000000000..0a7b2fd3699b
--- /dev/null
+++ b/drivers/iio/buffer/Kconfig
@@ -0,0 +1,24 @@
+#
+# Industrial I/O generic buffer implementations
+#
+# When adding new entries keep the list in alphabetical order
+
+config IIO_BUFFER_CB
+ tristate "IIO callback buffer used for push in-kernel interfaces"
+ help
+ Should be selected by any drivers that do in-kernel push
+ usage. That is, those where the data is pushed to the consumer.
+
+config IIO_KFIFO_BUF
+ tristate "Industrial I/O buffering based on kfifo"
+ help
+ A simple fifo based on kfifo. Note that this currently provides
+ no buffer events so it is up to userspace to work out how
+ often to read from the buffer.
+
+config IIO_TRIGGERED_BUFFER
+ tristate
+ select IIO_TRIGGER
+ select IIO_KFIFO_BUF
+ help
+ Provides helper functions for setting up triggered buffers.
diff --git a/drivers/iio/buffer/Makefile b/drivers/iio/buffer/Makefile
new file mode 100644
index 000000000000..4d193b9a9123
--- /dev/null
+++ b/drivers/iio/buffer/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the industrial I/O buffer implementations
+#
+
+# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_IIO_BUFFER_CB) += industrialio-buffer-cb.o
+obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o
+obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
diff --git a/drivers/iio/buffer_cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c
index 1648e6e5a848..323079c3ccce 100644
--- a/drivers/iio/buffer_cb.c
+++ b/drivers/iio/buffer/industrialio-buffer-cb.c
@@ -1,4 +1,12 @@
+/* The industrial I/O callback buffer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/export.h>
@@ -124,3 +132,7 @@ struct iio_channel
return cb_buffer->channels;
}
EXPORT_SYMBOL_GPL(iio_channel_cb_get_channels);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
+MODULE_DESCRIPTION("Industrial I/O callback buffer");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/industrialio-triggered-buffer.c b/drivers/iio/buffer/industrialio-triggered-buffer.c
index 4b2858ba1fd6..4b2858ba1fd6 100644
--- a/drivers/iio/industrialio-triggered-buffer.c
+++ b/drivers/iio/buffer/industrialio-triggered-buffer.c
diff --git a/drivers/iio/kfifo_buf.c b/drivers/iio/buffer/kfifo_buf.c
index c5b999f0c519..c5b999f0c519 100644
--- a/drivers/iio/kfifo_buf.c
+++ b/drivers/iio/buffer/kfifo_buf.c
diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig
new file mode 100644
index 000000000000..3061b7299f0f
--- /dev/null
+++ b/drivers/iio/chemical/Kconfig
@@ -0,0 +1,15 @@
+#
+# Chemical sensors
+#
+
+menu "Chemical Sensors"
+
+config VZ89X
+ tristate "SGX Sensortech MiCS VZ89X VOC sensor"
+ depends on I2C
+ help
+ Say Y here to build I2C interface support for the SGX
+ Sensortech MiCS VZ89X VOC (Volatile Organic Compounds)
+ sensors
+
+endmenu
diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile
new file mode 100644
index 000000000000..7292f2ded587
--- /dev/null
+++ b/drivers/iio/chemical/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for IIO chemical sensors
+#
+
+# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_VZ89X) += vz89x.o
diff --git a/drivers/iio/chemical/vz89x.c b/drivers/iio/chemical/vz89x.c
new file mode 100644
index 000000000000..b45420039ca2
--- /dev/null
+++ b/drivers/iio/chemical/vz89x.c
@@ -0,0 +1,237 @@
+/*
+ * vz89x.c - Support for SGX Sensortech MiCS VZ89X VOC sensors
+ *
+ * Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define VZ89X_REG_MEASUREMENT 0x09
+#define VZ89X_REG_MEASUREMENT_SIZE 6
+
+#define VZ89X_VOC_CO2_IDX 0
+#define VZ89X_VOC_SHORT_IDX 1
+#define VZ89X_VOC_TVOC_IDX 2
+#define VZ89X_VOC_RESISTANCE_IDX 3
+
+struct vz89x_data {
+ struct i2c_client *client;
+ struct mutex lock;
+ unsigned long last_update;
+
+ u8 buffer[VZ89X_REG_MEASUREMENT_SIZE];
+};
+
+static const struct iio_chan_spec vz89x_channels[] = {
+ {
+ .type = IIO_CONCENTRATION,
+ .channel2 = IIO_MOD_CO2,
+ .modified = 1,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW),
+ .address = VZ89X_VOC_CO2_IDX,
+ },
+ {
+ .type = IIO_CONCENTRATION,
+ .channel2 = IIO_MOD_VOC,
+ .modified = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .address = VZ89X_VOC_SHORT_IDX,
+ .extend_name = "short",
+ },
+ {
+ .type = IIO_CONCENTRATION,
+ .channel2 = IIO_MOD_VOC,
+ .modified = 1,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW),
+ .address = VZ89X_VOC_TVOC_IDX,
+ },
+ {
+ .type = IIO_RESISTANCE,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ .address = VZ89X_VOC_RESISTANCE_IDX,
+ },
+};
+
+static IIO_CONST_ATTR(in_concentration_co2_scale, "0.00000698689");
+static IIO_CONST_ATTR(in_concentration_voc_scale, "0.00000000436681223");
+
+static struct attribute *vz89x_attributes[] = {
+ &iio_const_attr_in_concentration_co2_scale.dev_attr.attr,
+ &iio_const_attr_in_concentration_voc_scale.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group vz89x_attrs_group = {
+ .attrs = vz89x_attributes,
+};
+
+static int vz89x_get_measurement(struct vz89x_data *data)
+{
+ int ret;
+ int i;
+
+ /* sensor can only be polled once a second max per datasheet */
+ if (!time_after(jiffies, data->last_update + HZ))
+ return 0;
+
+ ret = i2c_smbus_write_word_data(data->client,
+ VZ89X_REG_MEASUREMENT, 0);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < VZ89X_REG_MEASUREMENT_SIZE; i++) {
+ ret = i2c_smbus_read_byte(data->client);
+ if (ret < 0)
+ return ret;
+ data->buffer[i] = ret;
+ }
+
+ data->last_update = jiffies;
+
+ return 0;
+}
+
+static int vz89x_get_resistance_reading(struct vz89x_data *data)
+{
+ u8 *buf = &data->buffer[VZ89X_VOC_TVOC_IDX];
+
+ return buf[0] | (buf[1] << 8) | (buf[2] << 16);
+}
+
+static int vz89x_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct vz89x_data *data = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&data->lock);
+ ret = vz89x_get_measurement(data);
+ mutex_unlock(&data->lock);
+
+ if (ret)
+ return ret;
+
+ switch (chan->address) {
+ case VZ89X_VOC_CO2_IDX:
+ case VZ89X_VOC_SHORT_IDX:
+ case VZ89X_VOC_TVOC_IDX:
+ *val = data->buffer[chan->address];
+ return IIO_VAL_INT;
+ case VZ89X_VOC_RESISTANCE_IDX:
+ *val = vz89x_get_resistance_reading(data);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_RESISTANCE:
+ *val = 10;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ switch (chan->address) {
+ case VZ89X_VOC_CO2_IDX:
+ *val = 44;
+ *val2 = 250000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case VZ89X_VOC_TVOC_IDX:
+ *val = -13;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return ret;
+}
+
+static const struct iio_info vz89x_info = {
+ .attrs = &vz89x_attrs_group,
+ .read_raw = vz89x_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int vz89x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct iio_dev *indio_dev;
+ struct vz89x_data *data;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_BYTE))
+ return -ENODEV;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+ data->last_update = jiffies - HZ;
+ mutex_init(&data->lock);
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &vz89x_info,
+ indio_dev->name = dev_name(&client->dev);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ indio_dev->channels = vz89x_channels;
+ indio_dev->num_channels = ARRAY_SIZE(vz89x_channels);
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id vz89x_id[] = {
+ { "vz89x", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, vz89x_id);
+
+static const struct of_device_id vz89x_dt_ids[] = {
+ { .compatible = "sgx,vz89x" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, vz89x_dt_ids);
+
+static struct i2c_driver vz89x_driver = {
+ .driver = {
+ .name = "vz89x",
+ .of_match_table = of_match_ptr(vz89x_dt_ids),
+ },
+ .probe = vz89x_probe,
+ .id_table = vz89x_id,
+};
+module_i2c_driver(vz89x_driver);
+
+MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_DESCRIPTION("SGX Sensortech MiCS VZ89X VOC sensors");
+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
index 2e7fdb502645..25258e2c1a82 100644
--- a/drivers/iio/common/st_sensors/st_sensors_core.c
+++ b/drivers/iio/common/st_sensors/st_sensors_core.c
@@ -44,6 +44,28 @@ st_sensors_write_data_with_mask_error:
return err;
}
+int st_sensors_debugfs_reg_access(struct iio_dev *indio_dev,
+ unsigned reg, unsigned writeval,
+ unsigned *readval)
+{
+ struct st_sensor_data *sdata = iio_priv(indio_dev);
+ u8 readdata;
+ int err;
+
+ if (!readval)
+ return sdata->tf->write_byte(&sdata->tb, sdata->dev,
+ (u8)reg, (u8)writeval);
+
+ err = sdata->tf->read_byte(&sdata->tb, sdata->dev, (u8)reg, &readdata);
+ if (err < 0)
+ return err;
+
+ *readval = (unsigned)readdata;
+
+ return 0;
+}
+EXPORT_SYMBOL(st_sensors_debugfs_reg_access);
+
static int st_sensors_match_odr(struct st_sensor_settings *sensor_settings,
unsigned int odr, struct st_sensor_odr_avl *odr_out)
{
diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c
index fa2810032968..18a4ad5ff8c5 100644
--- a/drivers/iio/dac/ad7303.c
+++ b/drivers/iio/dac/ad7303.c
@@ -281,6 +281,12 @@ static int ad7303_remove(struct spi_device *spi)
return 0;
}
+static const struct of_device_id ad7303_spi_of_match[] = {
+ { .compatible = "adi,ad7303", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, ad7303_spi_of_match);
+
static const struct spi_device_id ad7303_spi_ids[] = {
{ "ad7303", 0 },
{}
@@ -290,6 +296,7 @@ MODULE_DEVICE_TABLE(spi, ad7303_spi_ids);
static struct spi_driver ad7303_driver = {
.driver = {
.name = "ad7303",
+ .of_match_table = of_match_ptr(ad7303_spi_of_match),
.owner = THIS_MODULE,
},
.probe = ad7303_probe,
diff --git a/drivers/iio/dac/max5821.c b/drivers/iio/dac/max5821.c
index 28b8748ea824..86e9e112f554 100644
--- a/drivers/iio/dac/max5821.c
+++ b/drivers/iio/dac/max5821.c
@@ -387,6 +387,7 @@ static const struct of_device_id max5821_of_match[] = {
{ .compatible = "maxim,max5821" },
{ }
};
+MODULE_DEVICE_TABLE(of, max5821_of_match);
static struct i2c_driver max5821_driver = {
.driver = {
diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c
index 9890c81c027d..73f27e0a08dd 100644
--- a/drivers/iio/frequency/adf4350.c
+++ b/drivers/iio/frequency/adf4350.c
@@ -616,15 +616,24 @@ static int adf4350_remove(struct spi_device *spi)
return 0;
}
+static const struct of_device_id adf4350_of_match[] = {
+ { .compatible = "adi,adf4350", },
+ { .compatible = "adi,adf4351", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, adf4350_of_match);
+
static const struct spi_device_id adf4350_id[] = {
{"adf4350", 4350},
{"adf4351", 4351},
{}
};
+MODULE_DEVICE_TABLE(spi, adf4350_id);
static struct spi_driver adf4350_driver = {
.driver = {
.name = "adf4350",
+ .of_match_table = of_match_ptr(adf4350_of_match),
.owner = THIS_MODULE,
},
.probe = adf4350_probe,
diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig
index 8d2439345673..e816d29d6a62 100644
--- a/drivers/iio/gyro/Kconfig
+++ b/drivers/iio/gyro/Kconfig
@@ -52,15 +52,26 @@ config ADXRS450
config BMG160
tristate "BOSCH BMG160 Gyro Sensor"
- depends on I2C
+ depends on (I2C || SPI_MASTER)
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
+ select BMG160_I2C if (I2C)
+ select BMG160_SPI if (SPI)
help
- Say yes here to build support for Bosch BMG160 Tri-axis Gyro Sensor
- driver. This driver also supports BMI055 gyroscope.
+ Say yes here to build support for BOSCH BMG160 Tri-axis Gyro Sensor
+ driver connected via I2C or SPI. This driver also supports BMI055
+ gyroscope.
This driver can also be built as a module. If so, the module
- will be called bmg160.
+ will be called bmg160_i2c or bmg160_spi.
+
+config BMG160_I2C
+ tristate
+ select REGMAP_I2C
+
+config BMG160_SPI
+ tristate
+ select REGMAP_SPI
config HID_SENSOR_GYRO_3D
depends on HID_SENSOR_HUB
diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile
index f46341b39139..f866a4be0667 100644
--- a/drivers/iio/gyro/Makefile
+++ b/drivers/iio/gyro/Makefile
@@ -8,7 +8,9 @@ obj-$(CONFIG_ADIS16130) += adis16130.o
obj-$(CONFIG_ADIS16136) += adis16136.o
obj-$(CONFIG_ADIS16260) += adis16260.o
obj-$(CONFIG_ADXRS450) += adxrs450.o
-obj-$(CONFIG_BMG160) += bmg160.o
+obj-$(CONFIG_BMG160) += bmg160_core.o
+obj-$(CONFIG_BMG160_I2C) += bmg160_i2c.o
+obj-$(CONFIG_BMG160_SPI) += bmg160_spi.o
obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
diff --git a/drivers/iio/gyro/bmg160.h b/drivers/iio/gyro/bmg160.h
new file mode 100644
index 000000000000..72db723c8fb6
--- /dev/null
+++ b/drivers/iio/gyro/bmg160.h
@@ -0,0 +1,10 @@
+#ifndef BMG160_H_
+#define BMG160_H_
+
+extern const struct dev_pm_ops bmg160_pm_ops;
+
+int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq,
+ const char *name);
+void bmg160_core_remove(struct device *dev);
+
+#endif /* BMG160_H_ */
diff --git a/drivers/iio/gyro/bmg160.c b/drivers/iio/gyro/bmg160_core.c
index 460bf715d541..02ff789852a0 100644
--- a/drivers/iio/gyro/bmg160.c
+++ b/drivers/iio/gyro/bmg160_core.c
@@ -13,7 +13,6 @@
*/
#include <linux/module.h>
-#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/slab.h>
@@ -28,8 +27,9 @@
#include <linux/iio/events.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
+#include <linux/regmap.h>
+#include "bmg160.h"
-#define BMG160_DRV_NAME "bmg160"
#define BMG160_IRQ_NAME "bmg160_event"
#define BMG160_GPIO_NAME "gpio_int"
@@ -97,7 +97,8 @@
#define BMG160_AUTO_SUSPEND_DELAY_MS 2000
struct bmg160_data {
- struct i2c_client *client;
+ struct device *dev;
+ struct regmap *regmap;
struct iio_trigger *dready_trig;
struct iio_trigger *motion_trig;
struct mutex mutex;
@@ -108,6 +109,7 @@ struct bmg160_data {
int slope_thres;
bool dready_trigger_on;
bool motion_trigger_on;
+ int irq;
};
enum bmg160_axis {
@@ -138,10 +140,9 @@ static int bmg160_set_mode(struct bmg160_data *data, u8 mode)
{
int ret;
- ret = i2c_smbus_write_byte_data(data->client,
- BMG160_REG_PMU_LPW, mode);
+ ret = regmap_write(data->regmap, BMG160_REG_PMU_LPW, mode);
if (ret < 0) {
- dev_err(&data->client->dev, "Error writing reg_pmu_lpw\n");
+ dev_err(data->dev, "Error writing reg_pmu_lpw\n");
return ret;
}
@@ -169,10 +170,9 @@ static int bmg160_set_bw(struct bmg160_data *data, int val)
if (bw_bits < 0)
return bw_bits;
- ret = i2c_smbus_write_byte_data(data->client, BMG160_REG_PMU_BW,
- bw_bits);
+ ret = regmap_write(data->regmap, BMG160_REG_PMU_BW, bw_bits);
if (ret < 0) {
- dev_err(&data->client->dev, "Error writing reg_pmu_bw\n");
+ dev_err(data->dev, "Error writing reg_pmu_bw\n");
return ret;
}
@@ -184,16 +184,17 @@ static int bmg160_set_bw(struct bmg160_data *data, int val)
static int bmg160_chip_init(struct bmg160_data *data)
{
int ret;
+ unsigned int val;
- ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_CHIP_ID);
+ ret = regmap_read(data->regmap, BMG160_REG_CHIP_ID, &val);
if (ret < 0) {
- dev_err(&data->client->dev, "Error reading reg_chip_id\n");
+ dev_err(data->dev, "Error reading reg_chip_id\n");
return ret;
}
- dev_dbg(&data->client->dev, "Chip Id %x\n", ret);
- if (ret != BMG160_CHIP_ID_VAL) {
- dev_err(&data->client->dev, "invalid chip %x\n", ret);
+ dev_dbg(data->dev, "Chip Id %x\n", val);
+ if (val != BMG160_CHIP_ID_VAL) {
+ dev_err(data->dev, "invalid chip %x\n", val);
return -ENODEV;
}
@@ -210,42 +211,33 @@ static int bmg160_chip_init(struct bmg160_data *data)
return ret;
/* Set Default Range */
- ret = i2c_smbus_write_byte_data(data->client,
- BMG160_REG_RANGE,
- BMG160_RANGE_500DPS);
+ ret = regmap_write(data->regmap, BMG160_REG_RANGE, BMG160_RANGE_500DPS);
if (ret < 0) {
- dev_err(&data->client->dev, "Error writing reg_range\n");
+ dev_err(data->dev, "Error writing reg_range\n");
return ret;
}
data->dps_range = BMG160_RANGE_500DPS;
- ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_SLOPE_THRES);
+ ret = regmap_read(data->regmap, BMG160_REG_SLOPE_THRES, &val);
if (ret < 0) {
- dev_err(&data->client->dev, "Error reading reg_slope_thres\n");
+ dev_err(data->dev, "Error reading reg_slope_thres\n");
return ret;
}
- data->slope_thres = ret;
+ data->slope_thres = val;
/* Set default interrupt mode */
- ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_EN_1);
+ ret = regmap_update_bits(data->regmap, BMG160_REG_INT_EN_1,
+ BMG160_INT1_BIT_OD, 0);
if (ret < 0) {
- dev_err(&data->client->dev, "Error reading reg_int_en_1\n");
- return ret;
- }
- ret &= ~BMG160_INT1_BIT_OD;
- ret = i2c_smbus_write_byte_data(data->client,
- BMG160_REG_INT_EN_1, ret);
- if (ret < 0) {
- dev_err(&data->client->dev, "Error writing reg_int_en_1\n");
+ dev_err(data->dev, "Error updating bits in reg_int_en_1\n");
return ret;
}
- ret = i2c_smbus_write_byte_data(data->client,
- BMG160_REG_INT_RST_LATCH,
- BMG160_INT_MODE_LATCH_INT |
- BMG160_INT_MODE_LATCH_RESET);
+ ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH,
+ BMG160_INT_MODE_LATCH_INT |
+ BMG160_INT_MODE_LATCH_RESET);
if (ret < 0) {
- dev_err(&data->client->dev,
+ dev_err(data->dev,
"Error writing reg_motion_intr\n");
return ret;
}
@@ -259,17 +251,17 @@ static int bmg160_set_power_state(struct bmg160_data *data, bool on)
int ret;
if (on)
- ret = pm_runtime_get_sync(&data->client->dev);
+ ret = pm_runtime_get_sync(data->dev);
else {
- pm_runtime_mark_last_busy(&data->client->dev);
- ret = pm_runtime_put_autosuspend(&data->client->dev);
+ pm_runtime_mark_last_busy(data->dev);
+ ret = pm_runtime_put_autosuspend(data->dev);
}
if (ret < 0) {
- dev_err(&data->client->dev,
+ dev_err(data->dev,
"Failed: bmg160_set_power_state for %d\n", on);
if (on)
- pm_runtime_put_noidle(&data->client->dev);
+ pm_runtime_put_noidle(data->dev);
return ret;
}
@@ -284,43 +276,30 @@ static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data,
int ret;
/* Enable/Disable INT_MAP0 mapping */
- ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_MAP_0);
- if (ret < 0) {
- dev_err(&data->client->dev, "Error reading reg_int_map0\n");
- return ret;
- }
- if (status)
- ret |= BMG160_INT_MAP_0_BIT_ANY;
- else
- ret &= ~BMG160_INT_MAP_0_BIT_ANY;
-
- ret = i2c_smbus_write_byte_data(data->client,
- BMG160_REG_INT_MAP_0,
- ret);
+ ret = regmap_update_bits(data->regmap, BMG160_REG_INT_MAP_0,
+ BMG160_INT_MAP_0_BIT_ANY,
+ (status ? BMG160_INT_MAP_0_BIT_ANY : 0));
if (ret < 0) {
- dev_err(&data->client->dev, "Error writing reg_int_map0\n");
+ dev_err(data->dev, "Error updating bits reg_int_map0\n");
return ret;
}
/* Enable/Disable slope interrupts */
if (status) {
/* Update slope thres */
- ret = i2c_smbus_write_byte_data(data->client,
- BMG160_REG_SLOPE_THRES,
- data->slope_thres);
+ ret = regmap_write(data->regmap, BMG160_REG_SLOPE_THRES,
+ data->slope_thres);
if (ret < 0) {
- dev_err(&data->client->dev,
+ dev_err(data->dev,
"Error writing reg_slope_thres\n");
return ret;
}
- ret = i2c_smbus_write_byte_data(data->client,
- BMG160_REG_MOTION_INTR,
- BMG160_INT_MOTION_X |
- BMG160_INT_MOTION_Y |
- BMG160_INT_MOTION_Z);
+ ret = regmap_write(data->regmap, BMG160_REG_MOTION_INTR,
+ BMG160_INT_MOTION_X | BMG160_INT_MOTION_Y |
+ BMG160_INT_MOTION_Z);
if (ret < 0) {
- dev_err(&data->client->dev,
+ dev_err(data->dev,
"Error writing reg_motion_intr\n");
return ret;
}
@@ -331,28 +310,26 @@ static int bmg160_setup_any_motion_interrupt(struct bmg160_data *data,
* to set latched mode, we will be flooded anyway with INTR
*/
if (!data->dready_trigger_on) {
- ret = i2c_smbus_write_byte_data(data->client,
- BMG160_REG_INT_RST_LATCH,
- BMG160_INT_MODE_LATCH_INT |
- BMG160_INT_MODE_LATCH_RESET);
+ ret = regmap_write(data->regmap,
+ BMG160_REG_INT_RST_LATCH,
+ BMG160_INT_MODE_LATCH_INT |
+ BMG160_INT_MODE_LATCH_RESET);
if (ret < 0) {
- dev_err(&data->client->dev,
+ dev_err(data->dev,
"Error writing reg_rst_latch\n");
return ret;
}
}
- ret = i2c_smbus_write_byte_data(data->client,
- BMG160_REG_INT_EN_0,
- BMG160_DATA_ENABLE_INT);
+ ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0,
+ BMG160_DATA_ENABLE_INT);
- } else
- ret = i2c_smbus_write_byte_data(data->client,
- BMG160_REG_INT_EN_0,
- 0);
+ } else {
+ ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0, 0);
+ }
if (ret < 0) {
- dev_err(&data->client->dev, "Error writing reg_int_en0\n");
+ dev_err(data->dev, "Error writing reg_int_en0\n");
return ret;
}
@@ -365,59 +342,43 @@ static int bmg160_setup_new_data_interrupt(struct bmg160_data *data,
int ret;
/* Enable/Disable INT_MAP1 mapping */
- ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_MAP_1);
- if (ret < 0) {
- dev_err(&data->client->dev, "Error reading reg_int_map1\n");
- return ret;
- }
-
- if (status)
- ret |= BMG160_INT_MAP_1_BIT_NEW_DATA;
- else
- ret &= ~BMG160_INT_MAP_1_BIT_NEW_DATA;
-
- ret = i2c_smbus_write_byte_data(data->client,
- BMG160_REG_INT_MAP_1,
- ret);
+ ret = regmap_update_bits(data->regmap, BMG160_REG_INT_MAP_1,
+ BMG160_INT_MAP_1_BIT_NEW_DATA,
+ (status ? BMG160_INT_MAP_1_BIT_NEW_DATA : 0));
if (ret < 0) {
- dev_err(&data->client->dev, "Error writing reg_int_map1\n");
+ dev_err(data->dev, "Error updating bits in reg_int_map1\n");
return ret;
}
if (status) {
- ret = i2c_smbus_write_byte_data(data->client,
- BMG160_REG_INT_RST_LATCH,
- BMG160_INT_MODE_NON_LATCH_INT |
- BMG160_INT_MODE_LATCH_RESET);
+ ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH,
+ BMG160_INT_MODE_NON_LATCH_INT |
+ BMG160_INT_MODE_LATCH_RESET);
if (ret < 0) {
- dev_err(&data->client->dev,
+ dev_err(data->dev,
"Error writing reg_rst_latch\n");
return ret;
}
- ret = i2c_smbus_write_byte_data(data->client,
- BMG160_REG_INT_EN_0,
- BMG160_DATA_ENABLE_INT);
+ ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0,
+ BMG160_DATA_ENABLE_INT);
} else {
/* Restore interrupt mode */
- ret = i2c_smbus_write_byte_data(data->client,
- BMG160_REG_INT_RST_LATCH,
- BMG160_INT_MODE_LATCH_INT |
- BMG160_INT_MODE_LATCH_RESET);
+ ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH,
+ BMG160_INT_MODE_LATCH_INT |
+ BMG160_INT_MODE_LATCH_RESET);
if (ret < 0) {
- dev_err(&data->client->dev,
+ dev_err(data->dev,
"Error writing reg_rst_latch\n");
return ret;
}
- ret = i2c_smbus_write_byte_data(data->client,
- BMG160_REG_INT_EN_0,
- 0);
+ ret = regmap_write(data->regmap, BMG160_REG_INT_EN_0, 0);
}
if (ret < 0) {
- dev_err(&data->client->dev, "Error writing reg_int_en0\n");
+ dev_err(data->dev, "Error writing reg_int_en0\n");
return ret;
}
@@ -444,12 +405,10 @@ static int bmg160_set_scale(struct bmg160_data *data, int val)
for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) {
if (bmg160_scale_table[i].scale == val) {
- ret = i2c_smbus_write_byte_data(
- data->client,
- BMG160_REG_RANGE,
- bmg160_scale_table[i].dps_range);
+ ret = regmap_write(data->regmap, BMG160_REG_RANGE,
+ bmg160_scale_table[i].dps_range);
if (ret < 0) {
- dev_err(&data->client->dev,
+ dev_err(data->dev,
"Error writing reg_range\n");
return ret;
}
@@ -464,6 +423,7 @@ static int bmg160_set_scale(struct bmg160_data *data, int val)
static int bmg160_get_temp(struct bmg160_data *data, int *val)
{
int ret;
+ unsigned int raw_val;
mutex_lock(&data->mutex);
ret = bmg160_set_power_state(data, true);
@@ -472,15 +432,15 @@ static int bmg160_get_temp(struct bmg160_data *data, int *val)
return ret;
}
- ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_TEMP);
+ ret = regmap_read(data->regmap, BMG160_REG_TEMP, &raw_val);
if (ret < 0) {
- dev_err(&data->client->dev, "Error reading reg_temp\n");
+ dev_err(data->dev, "Error reading reg_temp\n");
bmg160_set_power_state(data, false);
mutex_unlock(&data->mutex);
return ret;
}
- *val = sign_extend32(ret, 7);
+ *val = sign_extend32(raw_val, 7);
ret = bmg160_set_power_state(data, false);
mutex_unlock(&data->mutex);
if (ret < 0)
@@ -492,6 +452,7 @@ static int bmg160_get_temp(struct bmg160_data *data, int *val)
static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val)
{
int ret;
+ unsigned int raw_val;
mutex_lock(&data->mutex);
ret = bmg160_set_power_state(data, true);
@@ -500,15 +461,16 @@ static int bmg160_get_axis(struct bmg160_data *data, int axis, int *val)
return ret;
}
- ret = i2c_smbus_read_word_data(data->client, BMG160_AXIS_TO_REG(axis));
+ ret = regmap_bulk_read(data->regmap, BMG160_AXIS_TO_REG(axis), &raw_val,
+ 2);
if (ret < 0) {
- dev_err(&data->client->dev, "Error reading axis %d\n", axis);
+ dev_err(data->dev, "Error reading axis %d\n", axis);
bmg160_set_power_state(data, false);
mutex_unlock(&data->mutex);
return ret;
}
- *val = sign_extend32(ret, 15);
+ *val = sign_extend32(raw_val, 15);
ret = bmg160_set_power_state(data, false);
mutex_unlock(&data->mutex);
if (ret < 0)
@@ -807,12 +769,13 @@ static irqreturn_t bmg160_trigger_handler(int irq, void *p)
struct iio_dev *indio_dev = pf->indio_dev;
struct bmg160_data *data = iio_priv(indio_dev);
int bit, ret, i = 0;
+ unsigned int val;
mutex_lock(&data->mutex);
for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
- ret = i2c_smbus_read_word_data(data->client,
- BMG160_AXIS_TO_REG(bit));
+ ret = regmap_bulk_read(data->regmap, BMG160_AXIS_TO_REG(bit),
+ &val, 2);
if (ret < 0) {
mutex_unlock(&data->mutex);
goto err;
@@ -840,12 +803,11 @@ static int bmg160_trig_try_reen(struct iio_trigger *trig)
return 0;
/* Set latched mode interrupt and clear any latched interrupt */
- ret = i2c_smbus_write_byte_data(data->client,
- BMG160_REG_INT_RST_LATCH,
- BMG160_INT_MODE_LATCH_INT |
- BMG160_INT_MODE_LATCH_RESET);
+ ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH,
+ BMG160_INT_MODE_LATCH_INT |
+ BMG160_INT_MODE_LATCH_RESET);
if (ret < 0) {
- dev_err(&data->client->dev, "Error writing reg_rst_latch\n");
+ dev_err(data->dev, "Error writing reg_rst_latch\n");
return ret;
}
@@ -907,33 +869,34 @@ static irqreturn_t bmg160_event_handler(int irq, void *private)
struct bmg160_data *data = iio_priv(indio_dev);
int ret;
int dir;
+ unsigned int val;
- ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_STATUS_2);
+ ret = regmap_read(data->regmap, BMG160_REG_INT_STATUS_2, &val);
if (ret < 0) {
- dev_err(&data->client->dev, "Error reading reg_int_status2\n");
+ dev_err(data->dev, "Error reading reg_int_status2\n");
goto ack_intr_status;
}
- if (ret & 0x08)
+ if (val & 0x08)
dir = IIO_EV_DIR_RISING;
else
dir = IIO_EV_DIR_FALLING;
- if (ret & BMG160_ANY_MOTION_BIT_X)
+ if (val & BMG160_ANY_MOTION_BIT_X)
iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
0,
IIO_MOD_X,
IIO_EV_TYPE_ROC,
dir),
iio_get_time_ns());
- if (ret & BMG160_ANY_MOTION_BIT_Y)
+ if (val & BMG160_ANY_MOTION_BIT_Y)
iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
0,
IIO_MOD_Y,
IIO_EV_TYPE_ROC,
dir),
iio_get_time_ns());
- if (ret & BMG160_ANY_MOTION_BIT_Z)
+ if (val & BMG160_ANY_MOTION_BIT_Z)
iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
0,
IIO_MOD_Z,
@@ -943,12 +906,11 @@ static irqreturn_t bmg160_event_handler(int irq, void *private)
ack_intr_status:
if (!data->dready_trigger_on) {
- ret = i2c_smbus_write_byte_data(data->client,
- BMG160_REG_INT_RST_LATCH,
- BMG160_INT_MODE_LATCH_INT |
- BMG160_INT_MODE_LATCH_RESET);
+ ret = regmap_write(data->regmap, BMG160_REG_INT_RST_LATCH,
+ BMG160_INT_MODE_LATCH_INT |
+ BMG160_INT_MODE_LATCH_RESET);
if (ret < 0)
- dev_err(&data->client->dev,
+ dev_err(data->dev,
"Error writing reg_rst_latch\n");
}
@@ -993,18 +955,13 @@ static const struct iio_buffer_setup_ops bmg160_buffer_setup_ops = {
.postdisable = bmg160_buffer_postdisable,
};
-static int bmg160_gpio_probe(struct i2c_client *client,
- struct bmg160_data *data)
+static int bmg160_gpio_probe(struct bmg160_data *data)
{
struct device *dev;
struct gpio_desc *gpio;
- int ret;
-
- if (!client)
- return -EINVAL;
- dev = &client->dev;
+ dev = data->dev;
/* data ready gpio interrupt pin */
gpio = devm_gpiod_get_index(dev, BMG160_GPIO_NAME, 0, GPIOD_IN);
@@ -1013,11 +970,12 @@ static int bmg160_gpio_probe(struct i2c_client *client,
return PTR_ERR(gpio);
}
- ret = gpiod_to_irq(gpio);
+ data->irq = gpiod_to_irq(gpio);
- dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
+ dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio),
+ data->irq);
- return ret;
+ return 0;
}
static const char *bmg160_match_acpi_device(struct device *dev)
@@ -1031,21 +989,22 @@ static const char *bmg160_match_acpi_device(struct device *dev)
return dev_name(dev);
}
-static int bmg160_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq,
+ const char *name)
{
struct bmg160_data *data;
struct iio_dev *indio_dev;
int ret;
- const char *name = NULL;
- indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
- i2c_set_clientdata(client, indio_dev);
- data->client = client;
+ dev_set_drvdata(dev, indio_dev);
+ data->dev = dev;
+ data->irq = irq;
+ data->regmap = regmap;
ret = bmg160_chip_init(data);
if (ret < 0)
@@ -1053,25 +1012,22 @@ static int bmg160_probe(struct i2c_client *client,
mutex_init(&data->mutex);
- if (id)
- name = id->name;
+ if (ACPI_HANDLE(dev))
+ name = bmg160_match_acpi_device(dev);
- if (ACPI_HANDLE(&client->dev))
- name = bmg160_match_acpi_device(&client->dev);
-
- indio_dev->dev.parent = &client->dev;
+ indio_dev->dev.parent = dev;
indio_dev->channels = bmg160_channels;
indio_dev->num_channels = ARRAY_SIZE(bmg160_channels);
indio_dev->name = name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &bmg160_info;
- if (client->irq <= 0)
- client->irq = bmg160_gpio_probe(client, data);
+ if (data->irq <= 0)
+ bmg160_gpio_probe(data);
- if (client->irq > 0) {
- ret = devm_request_threaded_irq(&client->dev,
- client->irq,
+ if (data->irq > 0) {
+ ret = devm_request_threaded_irq(dev,
+ data->irq,
bmg160_data_rdy_trig_poll,
bmg160_event_handler,
IRQF_TRIGGER_RISING,
@@ -1080,28 +1036,28 @@ static int bmg160_probe(struct i2c_client *client,
if (ret)
return ret;
- data->dready_trig = devm_iio_trigger_alloc(&client->dev,
+ data->dready_trig = devm_iio_trigger_alloc(dev,
"%s-dev%d",
indio_dev->name,
indio_dev->id);
if (!data->dready_trig)
return -ENOMEM;
- data->motion_trig = devm_iio_trigger_alloc(&client->dev,
+ data->motion_trig = devm_iio_trigger_alloc(dev,
"%s-any-motion-dev%d",
indio_dev->name,
indio_dev->id);
if (!data->motion_trig)
return -ENOMEM;
- data->dready_trig->dev.parent = &client->dev;
+ data->dready_trig->dev.parent = dev;
data->dready_trig->ops = &bmg160_trigger_ops;
iio_trigger_set_drvdata(data->dready_trig, indio_dev);
ret = iio_trigger_register(data->dready_trig);
if (ret)
return ret;
- data->motion_trig->dev.parent = &client->dev;
+ data->motion_trig->dev.parent = dev;
data->motion_trig->ops = &bmg160_trigger_ops;
iio_trigger_set_drvdata(data->motion_trig, indio_dev);
ret = iio_trigger_register(data->motion_trig);
@@ -1116,25 +1072,25 @@ static int bmg160_probe(struct i2c_client *client,
bmg160_trigger_handler,
&bmg160_buffer_setup_ops);
if (ret < 0) {
- dev_err(&client->dev,
+ dev_err(dev,
"iio triggered buffer setup failed\n");
goto err_trigger_unregister;
}
ret = iio_device_register(indio_dev);
if (ret < 0) {
- dev_err(&client->dev, "unable to register iio device\n");
+ dev_err(dev, "unable to register iio device\n");
goto err_buffer_cleanup;
}
- ret = pm_runtime_set_active(&client->dev);
+ ret = pm_runtime_set_active(dev);
if (ret)
goto err_iio_unregister;
- pm_runtime_enable(&client->dev);
- pm_runtime_set_autosuspend_delay(&client->dev,
+ pm_runtime_enable(dev);
+ pm_runtime_set_autosuspend_delay(dev,
BMG160_AUTO_SUSPEND_DELAY_MS);
- pm_runtime_use_autosuspend(&client->dev);
+ pm_runtime_use_autosuspend(dev);
return 0;
@@ -1150,15 +1106,16 @@ err_trigger_unregister:
return ret;
}
+EXPORT_SYMBOL_GPL(bmg160_core_probe);
-static int bmg160_remove(struct i2c_client *client)
+void bmg160_core_remove(struct device *dev)
{
- struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct bmg160_data *data = iio_priv(indio_dev);
- pm_runtime_disable(&client->dev);
- pm_runtime_set_suspended(&client->dev);
- pm_runtime_put_noidle(&client->dev);
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(dev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
@@ -1171,14 +1128,13 @@ static int bmg160_remove(struct i2c_client *client)
mutex_lock(&data->mutex);
bmg160_set_mode(data, BMG160_MODE_DEEP_SUSPEND);
mutex_unlock(&data->mutex);
-
- return 0;
}
+EXPORT_SYMBOL_GPL(bmg160_core_remove);
#ifdef CONFIG_PM_SLEEP
static int bmg160_suspend(struct device *dev)
{
- struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct bmg160_data *data = iio_priv(indio_dev);
mutex_lock(&data->mutex);
@@ -1190,7 +1146,7 @@ static int bmg160_suspend(struct device *dev)
static int bmg160_resume(struct device *dev)
{
- struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct bmg160_data *data = iio_priv(indio_dev);
mutex_lock(&data->mutex);
@@ -1206,13 +1162,13 @@ static int bmg160_resume(struct device *dev)
#ifdef CONFIG_PM
static int bmg160_runtime_suspend(struct device *dev)
{
- struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct bmg160_data *data = iio_priv(indio_dev);
int ret;
ret = bmg160_set_mode(data, BMG160_MODE_SUSPEND);
if (ret < 0) {
- dev_err(&data->client->dev, "set mode failed\n");
+ dev_err(data->dev, "set mode failed\n");
return -EAGAIN;
}
@@ -1221,7 +1177,7 @@ static int bmg160_runtime_suspend(struct device *dev)
static int bmg160_runtime_resume(struct device *dev)
{
- struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct bmg160_data *data = iio_priv(indio_dev);
int ret;
@@ -1235,39 +1191,12 @@ static int bmg160_runtime_resume(struct device *dev)
}
#endif
-static const struct dev_pm_ops bmg160_pm_ops = {
+const struct dev_pm_ops bmg160_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(bmg160_suspend, bmg160_resume)
SET_RUNTIME_PM_OPS(bmg160_runtime_suspend,
bmg160_runtime_resume, NULL)
};
-
-static const struct acpi_device_id bmg160_acpi_match[] = {
- {"BMG0160", 0},
- {"BMI055B", 0},
- {},
-};
-
-MODULE_DEVICE_TABLE(acpi, bmg160_acpi_match);
-
-static const struct i2c_device_id bmg160_id[] = {
- {"bmg160", 0},
- {"bmi055_gyro", 0},
- {}
-};
-
-MODULE_DEVICE_TABLE(i2c, bmg160_id);
-
-static struct i2c_driver bmg160_driver = {
- .driver = {
- .name = BMG160_DRV_NAME,
- .acpi_match_table = ACPI_PTR(bmg160_acpi_match),
- .pm = &bmg160_pm_ops,
- },
- .probe = bmg160_probe,
- .remove = bmg160_remove,
- .id_table = bmg160_id,
-};
-module_i2c_driver(bmg160_driver);
+EXPORT_SYMBOL_GPL(bmg160_pm_ops);
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/gyro/bmg160_i2c.c b/drivers/iio/gyro/bmg160_i2c.c
new file mode 100644
index 000000000000..90126a5a7663
--- /dev/null
+++ b/drivers/iio/gyro/bmg160_i2c.c
@@ -0,0 +1,71 @@
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
+
+#include "bmg160.h"
+
+static const struct regmap_config bmg160_regmap_i2c_conf = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x3f
+};
+
+static int bmg160_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct regmap *regmap;
+ const char *name = NULL;
+
+ regmap = devm_regmap_init_i2c(client, &bmg160_regmap_i2c_conf);
+ if (IS_ERR(regmap)) {
+ dev_err(&client->dev, "Failed to register i2c regmap %d\n",
+ (int)PTR_ERR(regmap));
+ return PTR_ERR(regmap);
+ }
+
+ if (id)
+ name = id->name;
+
+ return bmg160_core_probe(&client->dev, regmap, client->irq, name);
+}
+
+static int bmg160_i2c_remove(struct i2c_client *client)
+{
+ bmg160_core_remove(&client->dev);
+
+ return 0;
+}
+
+static const struct acpi_device_id bmg160_acpi_match[] = {
+ {"BMG0160", 0},
+ {"BMI055B", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(acpi, bmg160_acpi_match);
+
+static const struct i2c_device_id bmg160_i2c_id[] = {
+ {"bmg160", 0},
+ {"bmi055_gyro", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, bmg160_i2c_id);
+
+static struct i2c_driver bmg160_i2c_driver = {
+ .driver = {
+ .name = "bmg160_i2c",
+ .acpi_match_table = ACPI_PTR(bmg160_acpi_match),
+ .pm = &bmg160_pm_ops,
+ },
+ .probe = bmg160_i2c_probe,
+ .remove = bmg160_i2c_remove,
+ .id_table = bmg160_i2c_id,
+};
+module_i2c_driver(bmg160_i2c_driver);
+
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("BMG160 I2C Gyro driver");
diff --git a/drivers/iio/gyro/bmg160_spi.c b/drivers/iio/gyro/bmg160_spi.c
new file mode 100644
index 000000000000..021ea5fe6a37
--- /dev/null
+++ b/drivers/iio/gyro/bmg160_spi.c
@@ -0,0 +1,57 @@
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+
+#include "bmg160.h"
+
+static const struct regmap_config bmg160_regmap_spi_conf = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x3f,
+};
+
+static int bmg160_spi_probe(struct spi_device *spi)
+{
+ struct regmap *regmap;
+ const struct spi_device_id *id = spi_get_device_id(spi);
+
+ regmap = devm_regmap_init_spi(spi, &bmg160_regmap_spi_conf);
+ if (IS_ERR(regmap)) {
+ dev_err(&spi->dev, "Failed to register spi regmap %d\n",
+ (int)PTR_ERR(regmap));
+ return PTR_ERR(regmap);
+ }
+
+ return bmg160_core_probe(&spi->dev, regmap, spi->irq, id->name);
+}
+
+static int bmg160_spi_remove(struct spi_device *spi)
+{
+ bmg160_core_remove(&spi->dev);
+
+ return 0;
+}
+
+static const struct spi_device_id bmg160_spi_id[] = {
+ {"bmg160", 0},
+ {"bmi055_gyro", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(spi, bmg160_spi_id);
+
+static struct spi_driver bmg160_spi_driver = {
+ .driver = {
+ .name = "bmg160_spi",
+ .pm = &bmg160_pm_ops,
+ },
+ .probe = bmg160_spi_probe,
+ .remove = bmg160_spi_remove,
+ .id_table = bmg160_spi_id,
+};
+module_spi_driver(bmg160_spi_driver);
+
+MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("BMG160 SPI Gyro driver");
diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c
index 4b993a5bc9a1..02eddcebeea3 100644
--- a/drivers/iio/gyro/st_gyro_core.c
+++ b/drivers/iio/gyro/st_gyro_core.c
@@ -383,6 +383,7 @@ static const struct iio_info gyro_info = {
.attrs = &st_gyro_attribute_group,
.read_raw = &st_gyro_read_raw,
.write_raw = &st_gyro_write_raw,
+ .debugfs_reg_access = &st_sensors_debugfs_reg_access,
};
#ifdef CONFIG_IIO_TRIGGER
diff --git a/drivers/iio/humidity/Kconfig b/drivers/iio/humidity/Kconfig
index 688c0d1cb47d..353ee9ade95d 100644
--- a/drivers/iio/humidity/Kconfig
+++ b/drivers/iio/humidity/Kconfig
@@ -12,6 +12,16 @@ config DHT11
Other sensors should work as well as long as they speak the
same protocol.
+config HDC100X
+ tristate "TI HDC100x relative humidity and temperature sensor"
+ depends on I2C
+ help
+ Say yes here to build support for the TI HDC100x series of
+ relative humidity and temperature sensors.
+
+ To compile this driver as a module, choose M here: the module
+ will be called hdc100x.
+
config SI7005
tristate "SI7005 relative humidity and temperature sensor"
depends on I2C
diff --git a/drivers/iio/humidity/Makefile b/drivers/iio/humidity/Makefile
index 86e2d26e9f4d..3e62c0a12d5e 100644
--- a/drivers/iio/humidity/Makefile
+++ b/drivers/iio/humidity/Makefile
@@ -3,5 +3,6 @@
#
obj-$(CONFIG_DHT11) += dht11.o
+obj-$(CONFIG_HDC100X) += hdc100x.o
obj-$(CONFIG_SI7005) += si7005.o
obj-$(CONFIG_SI7020) += si7020.o
diff --git a/drivers/iio/humidity/hdc100x.c b/drivers/iio/humidity/hdc100x.c
new file mode 100644
index 000000000000..28245782ecfb
--- /dev/null
+++ b/drivers/iio/humidity/hdc100x.c
@@ -0,0 +1,319 @@
+/*
+ * hdc100x.c - Support for the TI HDC100x temperature + humidity sensors
+ *
+ * Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define HDC100X_REG_TEMP 0x00
+#define HDC100X_REG_HUMIDITY 0x01
+
+#define HDC100X_REG_CONFIG 0x02
+#define HDC100X_REG_CONFIG_HEATER_EN BIT(13)
+
+struct hdc100x_data {
+ struct i2c_client *client;
+ struct mutex lock;
+ u16 config;
+
+ /* integration time of the sensor */
+ int adc_int_us[2];
+};
+
+/* integration time in us */
+static const int hdc100x_int_time[][3] = {
+ { 6350, 3650, 0 }, /* IIO_TEMP channel*/
+ { 6500, 3850, 2500 }, /* IIO_HUMIDITYRELATIVE channel */
+};
+
+/* HDC100X_REG_CONFIG shift and mask values */
+static const struct {
+ int shift;
+ int mask;
+} hdc100x_resolution_shift[2] = {
+ { /* IIO_TEMP channel */
+ .shift = 10,
+ .mask = 1
+ },
+ { /* IIO_HUMIDITYRELATIVE channel */
+ .shift = 8,
+ .mask = 2,
+ },
+};
+
+static IIO_CONST_ATTR(temp_integration_time_available,
+ "0.00365 0.00635");
+
+static IIO_CONST_ATTR(humidityrelative_integration_time_available,
+ "0.0025 0.00385 0.0065");
+
+static IIO_CONST_ATTR(out_current_heater_raw_available,
+ "0 1");
+
+static struct attribute *hdc100x_attributes[] = {
+ &iio_const_attr_temp_integration_time_available.dev_attr.attr,
+ &iio_const_attr_humidityrelative_integration_time_available.dev_attr.attr,
+ &iio_const_attr_out_current_heater_raw_available.dev_attr.attr,
+ NULL
+};
+
+static struct attribute_group hdc100x_attribute_group = {
+ .attrs = hdc100x_attributes,
+};
+
+static const struct iio_chan_spec hdc100x_channels[] = {
+ {
+ .type = IIO_TEMP,
+ .address = HDC100X_REG_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ },
+ {
+ .type = IIO_HUMIDITYRELATIVE,
+ .address = HDC100X_REG_HUMIDITY,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_INT_TIME)
+ },
+ {
+ .type = IIO_CURRENT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .extend_name = "heater",
+ .output = 1,
+ },
+};
+
+static int hdc100x_update_config(struct hdc100x_data *data, int mask, int val)
+{
+ int tmp = (~mask & data->config) | val;
+ int ret;
+
+ ret = i2c_smbus_write_word_swapped(data->client,
+ HDC100X_REG_CONFIG, tmp);
+ if (!ret)
+ data->config = tmp;
+
+ return ret;
+}
+
+static int hdc100x_set_it_time(struct hdc100x_data *data, int chan, int val2)
+{
+ int shift = hdc100x_resolution_shift[chan].shift;
+ int ret = -EINVAL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hdc100x_int_time[chan]); i++) {
+ if (val2 && val2 == hdc100x_int_time[chan][i]) {
+ ret = hdc100x_update_config(data,
+ hdc100x_resolution_shift[chan].mask << shift,
+ i << shift);
+ if (!ret)
+ data->adc_int_us[chan] = val2;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int hdc100x_get_measurement(struct hdc100x_data *data,
+ struct iio_chan_spec const *chan)
+{
+ struct i2c_client *client = data->client;
+ int delay = data->adc_int_us[chan->address];
+ int ret;
+ int val;
+
+ /* start measurement */
+ ret = i2c_smbus_write_byte(client, chan->address);
+ if (ret < 0) {
+ dev_err(&client->dev, "cannot start measurement");
+ return ret;
+ }
+
+ /* wait for integration time to pass */
+ usleep_range(delay, delay + 1000);
+
+ /*
+ * i2c_smbus_read_word_data cannot() be used here due to the command
+ * value not being understood and causes NAKs preventing any reading
+ * from being accessed.
+ */
+ ret = i2c_smbus_read_byte(client);
+ if (ret < 0) {
+ dev_err(&client->dev, "cannot read high byte measurement");
+ return ret;
+ }
+ val = ret << 6;
+
+ ret = i2c_smbus_read_byte(client);
+ if (ret < 0) {
+ dev_err(&client->dev, "cannot read low byte measurement");
+ return ret;
+ }
+ val |= ret >> 2;
+
+ return val;
+}
+
+static int hdc100x_get_heater_status(struct hdc100x_data *data)
+{
+ return !!(data->config & HDC100X_REG_CONFIG_HEATER_EN);
+}
+
+static int hdc100x_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct hdc100x_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW: {
+ int ret;
+
+ mutex_lock(&data->lock);
+ if (chan->type == IIO_CURRENT) {
+ *val = hdc100x_get_heater_status(data);
+ ret = IIO_VAL_INT;
+ } else {
+ ret = hdc100x_get_measurement(data, chan);
+ if (ret >= 0) {
+ *val = ret;
+ ret = IIO_VAL_INT;
+ }
+ }
+ mutex_unlock(&data->lock);
+ return ret;
+ }
+ case IIO_CHAN_INFO_INT_TIME:
+ *val = 0;
+ *val2 = data->adc_int_us[chan->address];
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_TEMP) {
+ *val = 165;
+ *val2 = 65536 >> 2;
+ return IIO_VAL_FRACTIONAL;
+ } else {
+ *val = 0;
+ *val2 = 10000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = -40;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int hdc100x_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct hdc100x_data *data = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ if (val != 0)
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ ret = hdc100x_set_it_time(data, chan->address, val2);
+ mutex_unlock(&data->lock);
+ return ret;
+ case IIO_CHAN_INFO_RAW:
+ if (chan->type != IIO_CURRENT || val2 != 0)
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_HEATER_EN,
+ val ? HDC100X_REG_CONFIG_HEATER_EN : 0);
+ mutex_unlock(&data->lock);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info hdc100x_info = {
+ .read_raw = hdc100x_read_raw,
+ .write_raw = hdc100x_write_raw,
+ .attrs = &hdc100x_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
+static int hdc100x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct iio_dev *indio_dev;
+ struct hdc100x_data *data;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE))
+ return -ENODEV;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+ mutex_init(&data->lock);
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->name = dev_name(&client->dev);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &hdc100x_info;
+
+ indio_dev->channels = hdc100x_channels;
+ indio_dev->num_channels = ARRAY_SIZE(hdc100x_channels);
+
+ /* be sure we are in a known state */
+ hdc100x_set_it_time(data, 0, hdc100x_int_time[0][0]);
+ hdc100x_set_it_time(data, 1, hdc100x_int_time[1][0]);
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id hdc100x_id[] = {
+ { "hdc100x", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, hdc100x_id);
+
+static struct i2c_driver hdc100x_driver = {
+ .driver = {
+ .name = "hdc100x",
+ },
+ .probe = hdc100x_probe,
+ .id_table = hdc100x_id,
+};
+module_i2c_driver(hdc100x_driver);
+
+MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_DESCRIPTION("TI HDC100x humidity and temperature sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/humidity/si7020.c b/drivers/iio/humidity/si7020.c
index fa3b809aff5e..12128d1ca570 100644
--- a/drivers/iio/humidity/si7020.c
+++ b/drivers/iio/humidity/si7020.c
@@ -57,8 +57,12 @@ static int si7020_read_raw(struct iio_dev *indio_dev,
if (ret < 0)
return ret;
*val = ret >> 2;
+ /*
+ * Humidity values can slightly exceed the 0-100%RH
+ * range and should be corrected by software
+ */
if (chan->type == IIO_HUMIDITYRELATIVE)
- *val &= GENMASK(11, 0);
+ *val = clamp_val(*val, 786, 13893);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
if (chan->type == IIO_TEMP)
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index b3fcc2c449d8..208358f9e7e3 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -75,6 +75,8 @@ static const char * const iio_chan_type_name_spec[] = {
[IIO_ENERGY] = "energy",
[IIO_DISTANCE] = "distance",
[IIO_VELOCITY] = "velocity",
+ [IIO_CONCENTRATION] = "concentration",
+ [IIO_RESISTANCE] = "resistance",
};
static const char * const iio_modifier_names[] = {
@@ -111,6 +113,8 @@ static const char * const iio_modifier_names[] = {
[IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z] = "sqrt(x^2+y^2+z^2)",
[IIO_MOD_I] = "i",
[IIO_MOD_Q] = "q",
+ [IIO_MOD_CO2] = "co2",
+ [IIO_MOD_VOC] = "voc",
};
/* relies on pairs of these shared then separate */
@@ -962,7 +966,7 @@ static void iio_device_unregister_sysfs(struct iio_dev *indio_dev)
static void iio_dev_release(struct device *device)
{
struct iio_dev *indio_dev = dev_to_iio_dev(device);
- if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+ if (indio_dev->modes & (INDIO_BUFFER_TRIGGERED | INDIO_EVENT_TRIGGERED))
iio_device_unregister_trigger_consumer(indio_dev);
iio_device_unregister_eventset(indio_dev);
iio_device_unregister_sysfs(indio_dev);
@@ -1153,6 +1157,8 @@ static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (cmd == IIO_GET_EVENT_FD_IOCTL) {
fd = iio_event_getfd(indio_dev);
+ if (fd < 0)
+ return fd;
if (copy_to_user(ip, &fd, sizeof(fd)))
return -EFAULT;
return 0;
@@ -1241,7 +1247,7 @@ int iio_device_register(struct iio_dev *indio_dev)
"Failed to register event set\n");
goto error_free_sysfs;
}
- if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+ if (indio_dev->modes & (INDIO_BUFFER_TRIGGERED | INDIO_EVENT_TRIGGERED))
iio_device_register_trigger_consumer(indio_dev);
if ((indio_dev->modes & INDIO_ALL_BUFFER_MODES) &&
diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c
index 570606c2adbd..ae2806aafb72 100644
--- a/drivers/iio/industrialio-trigger.c
+++ b/drivers/iio/industrialio-trigger.c
@@ -366,10 +366,18 @@ static ssize_t iio_trigger_write_current(struct device *dev,
indio_dev->trig = trig;
- if (oldtrig)
+ if (oldtrig) {
+ if (indio_dev->modes & INDIO_EVENT_TRIGGERED)
+ iio_trigger_detach_poll_func(oldtrig,
+ indio_dev->pollfunc_event);
iio_trigger_put(oldtrig);
- if (indio_dev->trig)
+ }
+ if (indio_dev->trig) {
iio_trigger_get(indio_dev->trig);
+ if (indio_dev->modes & INDIO_EVENT_TRIGGERED)
+ iio_trigger_attach_poll_func(indio_dev->trig,
+ indio_dev->pollfunc_event);
+ }
return len;
}
diff --git a/drivers/iio/industrialio-triggered-event.c b/drivers/iio/industrialio-triggered-event.c
new file mode 100644
index 000000000000..8cc254fac16a
--- /dev/null
+++ b/drivers/iio/industrialio-triggered-event.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 Cogent Embedded, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/triggered_event.h>
+#include <linux/iio/trigger_consumer.h>
+
+/**
+ * iio_triggered_event_setup() - Setup pollfunc_event for triggered event
+ * @indio_dev: IIO device structure
+ * @h: Function which will be used as pollfunc_event top half
+ * @thread: Function which will be used as pollfunc_event bottom half
+ *
+ * This function combines some common tasks which will normally be performed
+ * when setting up a triggered event. It will allocate the pollfunc_event and
+ * set mode to use it for triggered event.
+ *
+ * Before calling this function the indio_dev structure should already be
+ * completely initialized, but not yet registered. In practice this means that
+ * this function should be called right before iio_device_register().
+ *
+ * To free the resources allocated by this function call
+ * iio_triggered_event_cleanup().
+ */
+int iio_triggered_event_setup(struct iio_dev *indio_dev,
+ irqreturn_t (*h)(int irq, void *p),
+ irqreturn_t (*thread)(int irq, void *p))
+{
+ indio_dev->pollfunc_event = iio_alloc_pollfunc(h,
+ thread,
+ IRQF_ONESHOT,
+ indio_dev,
+ "%s_consumer%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (indio_dev->pollfunc_event == NULL)
+ return -ENOMEM;
+
+ /* Flag that events polling is possible */
+ indio_dev->modes |= INDIO_EVENT_TRIGGERED;
+
+ return 0;
+}
+EXPORT_SYMBOL(iio_triggered_event_setup);
+
+/**
+ * iio_triggered_event_cleanup() - Free resources allocated by iio_triggered_event_setup()
+ * @indio_dev: IIO device structure
+ */
+void iio_triggered_event_cleanup(struct iio_dev *indio_dev)
+{
+ indio_dev->modes &= ~INDIO_EVENT_TRIGGERED;
+ iio_dealloc_pollfunc(indio_dev->pollfunc_event);
+}
+EXPORT_SYMBOL(iio_triggered_event_cleanup);
+
+MODULE_AUTHOR("Vladimir Barinov");
+MODULE_DESCRIPTION("IIO helper functions for setting up triggered events");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index 7ed859a700c4..cfd3df8416bb 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -50,6 +50,19 @@ config APDS9300
To compile this driver as a module, choose M here: the
module will be called apds9300.
+config APDS9960
+ tristate "Avago APDS9960 gesture/RGB/ALS/proximity sensor"
+ select REGMAP_I2C
+ select IIO_BUFFER
+ select IIO_KFIFO_BUF
+ depends on I2C
+ help
+ Say Y here to build I2C interface support for the Avago
+ APDS9960 gesture/RGB/ALS/proximity sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called apds9960
+
config BH1750
tristate "ROHM BH1750 ambient light sensor"
depends on I2C
@@ -287,6 +300,16 @@ config TSL4531
To compile this driver as a module, choose M here: the
module will be called tsl4531.
+config US5182D
+ tristate "UPISEMI light and proximity sensor"
+ depends on I2C
+ help
+ If you say yes here you get support for the UPISEMI US5182D
+ ambient light and proximity sensor.
+
+ This driver can also be built as a module. If so, the module
+ will be called us5182d.
+
config VCNL4000
tristate "VCNL4000 combined ALS and proximity sensor"
depends on I2C
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index 91c74c014b6f..b2c31053db0c 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_ACPI_ALS) += acpi-als.o
obj-$(CONFIG_ADJD_S311) += adjd_s311.o
obj-$(CONFIG_AL3320A) += al3320a.o
obj-$(CONFIG_APDS9300) += apds9300.o
+obj-$(CONFIG_APDS9960) += apds9960.o
obj-$(CONFIG_BH1750) += bh1750.o
obj-$(CONFIG_CM32181) += cm32181.o
obj-$(CONFIG_CM3232) += cm3232.o
@@ -27,4 +28,5 @@ obj-$(CONFIG_STK3310) += stk3310.o
obj-$(CONFIG_TCS3414) += tcs3414.o
obj-$(CONFIG_TCS3472) += tcs3472.o
obj-$(CONFIG_TSL4531) += tsl4531.o
+obj-$(CONFIG_US5182D) += us5182d.o
obj-$(CONFIG_VCNL4000) += vcnl4000.o
diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c
new file mode 100644
index 000000000000..bf80ce47926b
--- /dev/null
+++ b/drivers/iio/light/apds9960.c
@@ -0,0 +1,1130 @@
+/*
+ * apds9960.c - Support for Avago APDS9960 gesture/RGB/ALS/proximity sensor
+ *
+ * Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * TODO: gesture + proximity calib offsets
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/sysfs.h>
+#include <linux/of_gpio.h>
+
+#define APDS9960_REGMAP_NAME "apds9960_regmap"
+#define APDS9960_DRV_NAME "apds9960"
+
+#define APDS9960_REG_RAM_START 0x00
+#define APDS9960_REG_RAM_END 0x7f
+
+#define APDS9960_REG_ENABLE 0x80
+#define APDS9960_REG_ATIME 0x81
+#define APDS9960_REG_WTIME 0x83
+
+#define APDS9960_REG_AILTL 0x84
+#define APDS9960_REG_AILTH 0x85
+#define APDS9960_REG_AIHTL 0x86
+#define APDS9960_REG_AIHTH 0x87
+
+#define APDS9960_REG_PILT 0x89
+#define APDS9960_REG_PIHT 0x8b
+#define APDS9960_REG_PERS 0x8c
+
+#define APDS9960_REG_CONFIG_1 0x8d
+#define APDS9960_REG_PPULSE 0x8e
+
+#define APDS9960_REG_CONTROL 0x8f
+#define APDS9960_REG_CONTROL_AGAIN_MASK 0x03
+#define APDS9960_REG_CONTROL_PGAIN_MASK 0x0c
+#define APDS9960_REG_CONTROL_AGAIN_MASK_SHIFT 0
+#define APDS9960_REG_CONTROL_PGAIN_MASK_SHIFT 2
+
+#define APDS9960_REG_CONFIG_2 0x90
+#define APDS9960_REG_CONFIG_2_GGAIN_MASK 0x60
+#define APDS9960_REG_CONFIG_2_GGAIN_MASK_SHIFT 5
+
+#define APDS9960_REG_ID 0x92
+
+#define APDS9960_REG_STATUS 0x93
+#define APDS9960_REG_STATUS_PS_INT BIT(5)
+#define APDS9960_REG_STATUS_ALS_INT BIT(4)
+#define APDS9960_REG_STATUS_GINT BIT(2)
+
+#define APDS9960_REG_PDATA 0x9c
+#define APDS9960_REG_POFFSET_UR 0x9d
+#define APDS9960_REG_POFFSET_DL 0x9e
+#define APDS9960_REG_CONFIG_3 0x9f
+
+#define APDS9960_REG_GPENTH 0xa0
+#define APDS9960_REG_GEXTH 0xa1
+
+#define APDS9960_REG_GCONF_1 0xa2
+#define APDS9960_REG_GCONF_1_GFIFO_THRES_MASK 0xc0
+#define APDS9960_REG_GCONF_1_GFIFO_THRES_MASK_SHIFT 6
+
+#define APDS9960_REG_GCONF_2 0xa3
+#define APDS9960_REG_GOFFSET_U 0xa4
+#define APDS9960_REG_GOFFSET_D 0xa5
+#define APDS9960_REG_GPULSE 0xa6
+#define APDS9960_REG_GOFFSET_L 0xa7
+#define APDS9960_REG_GOFFSET_R 0xa9
+#define APDS9960_REG_GCONF_3 0xaa
+
+#define APDS9960_REG_GCONF_4 0xab
+#define APDS9960_REG_GFLVL 0xae
+#define APDS9960_REG_GSTATUS 0xaf
+
+#define APDS9960_REG_IFORCE 0xe4
+#define APDS9960_REG_PICLEAR 0xe5
+#define APDS9960_REG_CICLEAR 0xe6
+#define APDS9960_REG_AICLEAR 0xe7
+
+#define APDS9960_DEFAULT_PERS 0x33
+#define APDS9960_DEFAULT_GPENTH 0x50
+#define APDS9960_DEFAULT_GEXTH 0x40
+
+#define APDS9960_MAX_PXS_THRES_VAL 255
+#define APDS9960_MAX_ALS_THRES_VAL 0xffff
+#define APDS9960_MAX_INT_TIME_IN_US 1000000
+
+enum apds9960_als_channel_idx {
+ IDX_ALS_CLEAR, IDX_ALS_RED, IDX_ALS_GREEN, IDX_ALS_BLUE,
+};
+
+#define APDS9960_REG_ALS_BASE 0x94
+#define APDS9960_REG_ALS_CHANNEL(_colour) \
+ (APDS9960_REG_ALS_BASE + (IDX_ALS_##_colour * 2))
+
+enum apds9960_gesture_channel_idx {
+ IDX_DIR_UP, IDX_DIR_DOWN, IDX_DIR_LEFT, IDX_DIR_RIGHT,
+};
+
+#define APDS9960_REG_GFIFO_BASE 0xfc
+#define APDS9960_REG_GFIFO_DIR(_dir) \
+ (APDS9960_REG_GFIFO_BASE + IDX_DIR_##_dir)
+
+struct apds9960_data {
+ struct i2c_client *client;
+ struct iio_dev *indio_dev;
+ struct mutex lock;
+
+ /* regmap fields */
+ struct regmap *regmap;
+ struct regmap_field *reg_int_als;
+ struct regmap_field *reg_int_ges;
+ struct regmap_field *reg_int_pxs;
+
+ struct regmap_field *reg_enable_als;
+ struct regmap_field *reg_enable_ges;
+ struct regmap_field *reg_enable_pxs;
+
+ /* state */
+ int als_int;
+ int pxs_int;
+ int gesture_mode_running;
+
+ /* gain values */
+ int als_gain;
+ int pxs_gain;
+
+ /* integration time value in us */
+ int als_adc_int_us;
+
+ /* gesture buffer */
+ u8 buffer[4]; /* 4 8-bit channels */
+};
+
+static const struct reg_default apds9960_reg_defaults[] = {
+ /* Default ALS integration time = 2.48ms */
+ { APDS9960_REG_ATIME, 0xff },
+};
+
+static const struct regmap_range apds9960_volatile_ranges[] = {
+ regmap_reg_range(APDS9960_REG_STATUS,
+ APDS9960_REG_PDATA),
+ regmap_reg_range(APDS9960_REG_GFLVL,
+ APDS9960_REG_GSTATUS),
+ regmap_reg_range(APDS9960_REG_GFIFO_DIR(UP),
+ APDS9960_REG_GFIFO_DIR(RIGHT)),
+ regmap_reg_range(APDS9960_REG_IFORCE,
+ APDS9960_REG_AICLEAR),
+};
+
+static const struct regmap_access_table apds9960_volatile_table = {
+ .yes_ranges = apds9960_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(apds9960_volatile_ranges),
+};
+
+static const struct regmap_range apds9960_precious_ranges[] = {
+ regmap_reg_range(APDS9960_REG_RAM_START, APDS9960_REG_RAM_END),
+};
+
+static const struct regmap_access_table apds9960_precious_table = {
+ .yes_ranges = apds9960_precious_ranges,
+ .n_yes_ranges = ARRAY_SIZE(apds9960_precious_ranges),
+};
+
+static const struct regmap_range apds9960_readable_ranges[] = {
+ regmap_reg_range(APDS9960_REG_ENABLE,
+ APDS9960_REG_GSTATUS),
+ regmap_reg_range(APDS9960_REG_GFIFO_DIR(UP),
+ APDS9960_REG_GFIFO_DIR(RIGHT)),
+};
+
+static const struct regmap_access_table apds9960_readable_table = {
+ .yes_ranges = apds9960_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(apds9960_readable_ranges),
+};
+
+static const struct regmap_range apds9960_writeable_ranges[] = {
+ regmap_reg_range(APDS9960_REG_ENABLE, APDS9960_REG_CONFIG_2),
+ regmap_reg_range(APDS9960_REG_POFFSET_UR, APDS9960_REG_GCONF_4),
+ regmap_reg_range(APDS9960_REG_IFORCE, APDS9960_REG_AICLEAR),
+};
+
+static const struct regmap_access_table apds9960_writeable_table = {
+ .yes_ranges = apds9960_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(apds9960_writeable_ranges),
+};
+
+static const struct regmap_config apds9960_regmap_config = {
+ .name = APDS9960_REGMAP_NAME,
+ .reg_bits = 8,
+ .val_bits = 8,
+ .use_single_rw = 1,
+
+ .volatile_table = &apds9960_volatile_table,
+ .precious_table = &apds9960_precious_table,
+ .rd_table = &apds9960_readable_table,
+ .wr_table = &apds9960_writeable_table,
+
+ .reg_defaults = apds9960_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(apds9960_reg_defaults),
+ .max_register = APDS9960_REG_GFIFO_DIR(RIGHT),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct iio_event_spec apds9960_pxs_event_spec[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+static const struct iio_event_spec apds9960_als_event_spec[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+#define APDS9960_GESTURE_CHANNEL(_dir, _si) { \
+ .type = IIO_PROXIMITY, \
+ .channel = _si + 1, \
+ .scan_index = _si, \
+ .indexed = 1, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 8, \
+ .storagebits = 8, \
+ }, \
+}
+
+#define APDS9960_INTENSITY_CHANNEL(_colour) { \
+ .type = IIO_INTENSITY, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_INT_TIME), \
+ .channel2 = IIO_MOD_LIGHT_##_colour, \
+ .address = APDS9960_REG_ALS_CHANNEL(_colour), \
+ .modified = 1, \
+ .scan_index = -1, \
+}
+
+static const unsigned long apds9960_scan_masks[] = {0xf, 0};
+
+static const struct iio_chan_spec apds9960_channels[] = {
+ {
+ .type = IIO_PROXIMITY,
+ .address = APDS9960_REG_PDATA,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .channel = 0,
+ .indexed = 0,
+ .scan_index = -1,
+
+ .event_spec = apds9960_pxs_event_spec,
+ .num_event_specs = ARRAY_SIZE(apds9960_pxs_event_spec),
+ },
+ /* Gesture Sensor */
+ APDS9960_GESTURE_CHANNEL(UP, 0),
+ APDS9960_GESTURE_CHANNEL(DOWN, 1),
+ APDS9960_GESTURE_CHANNEL(LEFT, 2),
+ APDS9960_GESTURE_CHANNEL(RIGHT, 3),
+ /* ALS */
+ {
+ .type = IIO_INTENSITY,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_INT_TIME),
+ .channel2 = IIO_MOD_LIGHT_CLEAR,
+ .address = APDS9960_REG_ALS_CHANNEL(CLEAR),
+ .modified = 1,
+ .scan_index = -1,
+
+ .event_spec = apds9960_als_event_spec,
+ .num_event_specs = ARRAY_SIZE(apds9960_als_event_spec),
+ },
+ /* RGB Sensor */
+ APDS9960_INTENSITY_CHANNEL(RED),
+ APDS9960_INTENSITY_CHANNEL(GREEN),
+ APDS9960_INTENSITY_CHANNEL(BLUE),
+};
+
+/* integration time in us */
+static const int apds9960_int_time[][2] =
+ { {28000, 246}, {100000, 219}, {200000, 182}, {700000, 0} };
+
+/* gain mapping */
+static const int apds9960_pxs_gain_map[] = {1, 2, 4, 8};
+static const int apds9960_als_gain_map[] = {1, 4, 16, 64};
+
+static IIO_CONST_ATTR(proximity_scale_available, "1 2 4 8");
+static IIO_CONST_ATTR(intensity_scale_available, "1 4 16 64");
+static IIO_CONST_ATTR_INT_TIME_AVAIL("0.028 0.1 0.2 0.7");
+
+static struct attribute *apds9960_attributes[] = {
+ &iio_const_attr_proximity_scale_available.dev_attr.attr,
+ &iio_const_attr_intensity_scale_available.dev_attr.attr,
+ &iio_const_attr_integration_time_available.dev_attr.attr,
+ NULL,
+};
+
+static struct attribute_group apds9960_attribute_group = {
+ .attrs = apds9960_attributes,
+};
+
+static const struct reg_field apds9960_reg_field_int_als =
+ REG_FIELD(APDS9960_REG_ENABLE, 4, 4);
+
+static const struct reg_field apds9960_reg_field_int_ges =
+ REG_FIELD(APDS9960_REG_GCONF_4, 1, 1);
+
+static const struct reg_field apds9960_reg_field_int_pxs =
+ REG_FIELD(APDS9960_REG_ENABLE, 5, 5);
+
+static const struct reg_field apds9960_reg_field_enable_als =
+ REG_FIELD(APDS9960_REG_ENABLE, 1, 1);
+
+static const struct reg_field apds9960_reg_field_enable_ges =
+ REG_FIELD(APDS9960_REG_ENABLE, 6, 6);
+
+static const struct reg_field apds9960_reg_field_enable_pxs =
+ REG_FIELD(APDS9960_REG_ENABLE, 2, 2);
+
+static int apds9960_set_it_time(struct apds9960_data *data, int val2)
+{
+ int ret = -EINVAL;
+ int idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(apds9960_int_time); idx++) {
+ if (apds9960_int_time[idx][0] == val2) {
+ mutex_lock(&data->lock);
+ ret = regmap_write(data->regmap, APDS9960_REG_ATIME,
+ apds9960_int_time[idx][1]);
+ if (!ret)
+ data->als_adc_int_us = val2;
+ mutex_unlock(&data->lock);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int apds9960_set_pxs_gain(struct apds9960_data *data, int val)
+{
+ int ret = -EINVAL;
+ int idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(apds9960_pxs_gain_map); idx++) {
+ if (apds9960_pxs_gain_map[idx] == val) {
+ /* pxs + gesture gains are mirrored */
+ mutex_lock(&data->lock);
+ ret = regmap_update_bits(data->regmap,
+ APDS9960_REG_CONTROL,
+ APDS9960_REG_CONTROL_PGAIN_MASK,
+ idx << APDS9960_REG_CONTROL_PGAIN_MASK_SHIFT);
+ if (ret) {
+ mutex_unlock(&data->lock);
+ break;
+ }
+
+ ret = regmap_update_bits(data->regmap,
+ APDS9960_REG_CONFIG_2,
+ APDS9960_REG_CONFIG_2_GGAIN_MASK,
+ idx << APDS9960_REG_CONFIG_2_GGAIN_MASK_SHIFT);
+ if (!ret)
+ data->pxs_gain = idx;
+ mutex_unlock(&data->lock);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int apds9960_set_als_gain(struct apds9960_data *data, int val)
+{
+ int ret = -EINVAL;
+ int idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(apds9960_als_gain_map); idx++) {
+ if (apds9960_als_gain_map[idx] == val) {
+ mutex_lock(&data->lock);
+ ret = regmap_update_bits(data->regmap,
+ APDS9960_REG_CONTROL,
+ APDS9960_REG_CONTROL_AGAIN_MASK, idx);
+ if (!ret)
+ data->als_gain = idx;
+ mutex_unlock(&data->lock);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int apds9960_set_power_state(struct apds9960_data *data, bool on)
+{
+ struct device *dev = &data->client->dev;
+ int ret = 0;
+
+ mutex_lock(&data->lock);
+
+ if (on) {
+ int suspended;
+
+ suspended = pm_runtime_suspended(dev);
+ ret = pm_runtime_get_sync(dev);
+
+ /* Allow one integration cycle before allowing a reading */
+ if (suspended)
+ usleep_range(data->als_adc_int_us,
+ APDS9960_MAX_INT_TIME_IN_US);
+ } else {
+ ret = pm_runtime_put_autosuspend(dev);
+ }
+
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+#else
+static int apds9960_set_power_state(struct apds9960_data *data, bool on)
+{
+ return 0;
+}
+#endif
+
+static int apds9960_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct apds9960_data *data = iio_priv(indio_dev);
+ u16 buf;
+ int ret = -EINVAL;
+
+ if (data->gesture_mode_running)
+ return -EBUSY;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ apds9960_set_power_state(data, true);
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ ret = regmap_read(data->regmap, chan->address, val);
+ if (!ret)
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_INTENSITY:
+ ret = regmap_bulk_read(data->regmap, chan->address,
+ &buf, 2);
+ if (!ret)
+ ret = IIO_VAL_INT;
+ *val = le16_to_cpu(buf);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ apds9960_set_power_state(data, false);
+ break;
+ case IIO_CHAN_INFO_INT_TIME:
+ /* RGB + ALS sensors only have integration time */
+ mutex_lock(&data->lock);
+ switch (chan->type) {
+ case IIO_INTENSITY:
+ *val = 0;
+ *val2 = data->als_adc_int_us;
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ mutex_unlock(&data->lock);
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ mutex_lock(&data->lock);
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ *val = apds9960_pxs_gain_map[data->pxs_gain];
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_INTENSITY:
+ *val = apds9960_als_gain_map[data->als_gain];
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ mutex_unlock(&data->lock);
+ break;
+ }
+
+ return ret;
+};
+
+static int apds9960_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct apds9960_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ /* RGB + ALS sensors only have int time */
+ switch (chan->type) {
+ case IIO_INTENSITY:
+ if (val != 0)
+ return -EINVAL;
+ return apds9960_set_it_time(data, val2);
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ if (val2 != 0)
+ return -EINVAL;
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ return apds9960_set_pxs_gain(data, val);
+ case IIO_INTENSITY:
+ return apds9960_set_als_gain(data, val);
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ };
+
+ return 0;
+}
+
+static inline int apds9960_get_thres_reg(const struct iio_chan_spec *chan,
+ enum iio_event_direction dir,
+ u8 *reg)
+{
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ *reg = APDS9960_REG_PIHT;
+ break;
+ case IIO_INTENSITY:
+ *reg = APDS9960_REG_AIHTL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IIO_EV_DIR_FALLING:
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ *reg = APDS9960_REG_PILT;
+ break;
+ case IIO_INTENSITY:
+ *reg = APDS9960_REG_AILTL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int apds9960_read_event(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int *val, int *val2)
+{
+ u8 reg;
+ u16 buf;
+ int ret = 0;
+ struct apds9960_data *data = iio_priv(indio_dev);
+
+ if (info != IIO_EV_INFO_VALUE)
+ return -EINVAL;
+
+ ret = apds9960_get_thres_reg(chan, dir, &reg);
+ if (ret < 0)
+ return ret;
+
+ if (chan->type == IIO_PROXIMITY) {
+ ret = regmap_read(data->regmap, reg, val);
+ if (ret < 0)
+ return ret;
+ } else if (chan->type == IIO_INTENSITY) {
+ ret = regmap_bulk_read(data->regmap, reg, &buf, 2);
+ if (ret < 0)
+ return ret;
+ *val = le16_to_cpu(buf);
+ } else
+ return -EINVAL;
+
+ *val2 = 0;
+
+ return IIO_VAL_INT;
+}
+
+static int apds9960_write_event(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int val, int val2)
+{
+ u8 reg;
+ u16 buf;
+ int ret = 0;
+ struct apds9960_data *data = iio_priv(indio_dev);
+
+ if (info != IIO_EV_INFO_VALUE)
+ return -EINVAL;
+
+ ret = apds9960_get_thres_reg(chan, dir, &reg);
+ if (ret < 0)
+ return ret;
+
+ if (chan->type == IIO_PROXIMITY) {
+ if (val < 0 || val > APDS9960_MAX_PXS_THRES_VAL)
+ return -EINVAL;
+ ret = regmap_write(data->regmap, reg, val);
+ if (ret < 0)
+ return ret;
+ } else if (chan->type == IIO_INTENSITY) {
+ if (val < 0 || val > APDS9960_MAX_ALS_THRES_VAL)
+ return -EINVAL;
+ buf = cpu_to_le16(val);
+ ret = regmap_bulk_write(data->regmap, reg, &buf, 2);
+ if (ret < 0)
+ return ret;
+ } else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int apds9960_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct apds9960_data *data = iio_priv(indio_dev);
+
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ return data->pxs_int;
+ case IIO_INTENSITY:
+ return data->als_int;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int apds9960_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ int state)
+{
+ struct apds9960_data *data = iio_priv(indio_dev);
+ int ret;
+
+ state = !!state;
+
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ if (data->pxs_int == state)
+ return -EINVAL;
+
+ ret = regmap_field_write(data->reg_int_pxs, state);
+ if (ret)
+ return ret;
+ data->pxs_int = state;
+ apds9960_set_power_state(data, state);
+ break;
+ case IIO_INTENSITY:
+ if (data->als_int == state)
+ return -EINVAL;
+
+ ret = regmap_field_write(data->reg_int_als, state);
+ if (ret)
+ return ret;
+ data->als_int = state;
+ apds9960_set_power_state(data, state);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct iio_info apds9960_info = {
+ .driver_module = THIS_MODULE,
+ .attrs = &apds9960_attribute_group,
+ .read_raw = apds9960_read_raw,
+ .write_raw = apds9960_write_raw,
+ .read_event_value = apds9960_read_event,
+ .write_event_value = apds9960_write_event,
+ .read_event_config = apds9960_read_event_config,
+ .write_event_config = apds9960_write_event_config,
+
+};
+
+static inline int apds9660_fifo_is_empty(struct apds9960_data *data)
+{
+ int cnt;
+ int ret;
+
+ ret = regmap_read(data->regmap, APDS9960_REG_GFLVL, &cnt);
+ if (ret)
+ return ret;
+
+ return cnt;
+}
+
+static void apds9960_read_gesture_fifo(struct apds9960_data *data)
+{
+ int ret, cnt = 0;
+
+ mutex_lock(&data->lock);
+ data->gesture_mode_running = 1;
+
+ while (cnt-- || (cnt = apds9660_fifo_is_empty(data) > 0)) {
+ ret = regmap_bulk_read(data->regmap, APDS9960_REG_GFIFO_BASE,
+ &data->buffer, 4);
+
+ if (ret)
+ goto err_read;
+
+ iio_push_to_buffers(data->indio_dev, data->buffer);
+ }
+
+err_read:
+ data->gesture_mode_running = 0;
+ mutex_unlock(&data->lock);
+}
+
+static irqreturn_t apds9960_interrupt_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct apds9960_data *data = iio_priv(indio_dev);
+ int ret, status;
+
+ ret = regmap_read(data->regmap, APDS9960_REG_STATUS, &status);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "irq status reg read failed\n");
+ return IRQ_HANDLED;
+ }
+
+ if ((status & APDS9960_REG_STATUS_ALS_INT) && data->als_int) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ iio_get_time_ns());
+ regmap_write(data->regmap, APDS9960_REG_CICLEAR, 1);
+ }
+
+ if ((status & APDS9960_REG_STATUS_PS_INT) && data->pxs_int) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ iio_get_time_ns());
+ regmap_write(data->regmap, APDS9960_REG_PICLEAR, 1);
+ }
+
+ if (status & APDS9960_REG_STATUS_GINT)
+ apds9960_read_gesture_fifo(data);
+
+ return IRQ_HANDLED;
+}
+
+static int apds9960_set_powermode(struct apds9960_data *data, bool state)
+{
+ return regmap_update_bits(data->regmap, APDS9960_REG_ENABLE, 1, state);
+}
+
+static int apds9960_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct apds9960_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = regmap_field_write(data->reg_int_ges, 1);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(data->reg_enable_ges, 1);
+ if (ret)
+ return ret;
+
+ pm_runtime_get_sync(&data->client->dev);
+
+ return 0;
+}
+
+static int apds9960_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct apds9960_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = regmap_field_write(data->reg_enable_ges, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(data->reg_int_ges, 0);
+ if (ret)
+ return ret;
+
+ pm_runtime_put_autosuspend(&data->client->dev);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops apds9960_buffer_setup_ops = {
+ .postenable = apds9960_buffer_postenable,
+ .predisable = apds9960_buffer_predisable,
+};
+
+static int apds9960_regfield_init(struct apds9960_data *data)
+{
+ struct device *dev = &data->client->dev;
+ struct regmap *regmap = data->regmap;
+
+ data->reg_int_als = devm_regmap_field_alloc(dev, regmap,
+ apds9960_reg_field_int_als);
+ if (IS_ERR(data->reg_int_als)) {
+ dev_err(dev, "INT ALS reg field init failed\n");
+ return PTR_ERR(data->reg_int_als);
+ }
+
+ data->reg_int_ges = devm_regmap_field_alloc(dev, regmap,
+ apds9960_reg_field_int_ges);
+ if (IS_ERR(data->reg_int_ges)) {
+ dev_err(dev, "INT gesture reg field init failed\n");
+ return PTR_ERR(data->reg_int_ges);
+ }
+
+ data->reg_int_pxs = devm_regmap_field_alloc(dev, regmap,
+ apds9960_reg_field_int_pxs);
+ if (IS_ERR(data->reg_int_pxs)) {
+ dev_err(dev, "INT pxs reg field init failed\n");
+ return PTR_ERR(data->reg_int_pxs);
+ }
+
+ data->reg_enable_als = devm_regmap_field_alloc(dev, regmap,
+ apds9960_reg_field_enable_als);
+ if (IS_ERR(data->reg_enable_als)) {
+ dev_err(dev, "Enable ALS reg field init failed\n");
+ return PTR_ERR(data->reg_enable_als);
+ }
+
+ data->reg_enable_ges = devm_regmap_field_alloc(dev, regmap,
+ apds9960_reg_field_enable_ges);
+ if (IS_ERR(data->reg_enable_ges)) {
+ dev_err(dev, "Enable gesture reg field init failed\n");
+ return PTR_ERR(data->reg_enable_ges);
+ }
+
+ data->reg_enable_pxs = devm_regmap_field_alloc(dev, regmap,
+ apds9960_reg_field_enable_pxs);
+ if (IS_ERR(data->reg_enable_pxs)) {
+ dev_err(dev, "Enable PXS reg field init failed\n");
+ return PTR_ERR(data->reg_enable_pxs);
+ }
+
+ return 0;
+}
+
+static int apds9960_chip_init(struct apds9960_data *data)
+{
+ int ret;
+
+ /* Default IT for ALS of 28 ms */
+ ret = apds9960_set_it_time(data, 28000);
+ if (ret)
+ return ret;
+
+ /* Ensure gesture interrupt is OFF */
+ ret = regmap_field_write(data->reg_int_ges, 0);
+ if (ret)
+ return ret;
+
+ /* Disable gesture sensor, since polling is useless from user-space */
+ ret = regmap_field_write(data->reg_enable_ges, 0);
+ if (ret)
+ return ret;
+
+ /* Ensure proximity interrupt is OFF */
+ ret = regmap_field_write(data->reg_int_pxs, 0);
+ if (ret)
+ return ret;
+
+ /* Enable proximity sensor for polling */
+ ret = regmap_field_write(data->reg_enable_pxs, 1);
+ if (ret)
+ return ret;
+
+ /* Ensure ALS interrupt is OFF */
+ ret = regmap_field_write(data->reg_int_als, 0);
+ if (ret)
+ return ret;
+
+ /* Enable ALS sensor for polling */
+ ret = regmap_field_write(data->reg_enable_als, 1);
+ if (ret)
+ return ret;
+ /*
+ * When enabled trigger an interrupt after 3 readings
+ * outside threshold for ALS + PXS
+ */
+ ret = regmap_write(data->regmap, APDS9960_REG_PERS,
+ APDS9960_DEFAULT_PERS);
+ if (ret)
+ return ret;
+
+ /*
+ * Wait for 4 event outside gesture threshold to prevent interrupt
+ * flooding.
+ */
+ ret = regmap_update_bits(data->regmap, APDS9960_REG_GCONF_1,
+ APDS9960_REG_GCONF_1_GFIFO_THRES_MASK,
+ BIT(0) << APDS9960_REG_GCONF_1_GFIFO_THRES_MASK_SHIFT);
+ if (ret)
+ return ret;
+
+ /* Default ENTER and EXIT thresholds for the GESTURE engine. */
+ ret = regmap_write(data->regmap, APDS9960_REG_GPENTH,
+ APDS9960_DEFAULT_GPENTH);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(data->regmap, APDS9960_REG_GEXTH,
+ APDS9960_DEFAULT_GEXTH);
+ if (ret)
+ return ret;
+
+ return apds9960_set_powermode(data, 1);
+}
+
+static int apds9960_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct apds9960_data *data;
+ struct iio_buffer *buffer;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ buffer = devm_iio_kfifo_allocate(&client->dev);
+ if (!buffer)
+ return -ENOMEM;
+
+ iio_device_attach_buffer(indio_dev, buffer);
+
+ indio_dev->info = &apds9960_info;
+ indio_dev->name = APDS9960_DRV_NAME;
+ indio_dev->channels = apds9960_channels;
+ indio_dev->num_channels = ARRAY_SIZE(apds9960_channels);
+ indio_dev->available_scan_masks = apds9960_scan_masks;
+ indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
+ indio_dev->setup_ops = &apds9960_buffer_setup_ops;
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+
+ data->regmap = devm_regmap_init_i2c(client, &apds9960_regmap_config);
+ if (IS_ERR(data->regmap)) {
+ dev_err(&client->dev, "regmap initialization failed.\n");
+ return PTR_ERR(data->regmap);
+ }
+
+ data->client = client;
+ data->indio_dev = indio_dev;
+ mutex_init(&data->lock);
+
+ ret = pm_runtime_set_active(&client->dev);
+ if (ret)
+ goto error_power_down;
+
+ pm_runtime_enable(&client->dev);
+ pm_runtime_set_autosuspend_delay(&client->dev, 5000);
+ pm_runtime_use_autosuspend(&client->dev);
+
+ apds9960_set_power_state(data, true);
+
+ ret = apds9960_regfield_init(data);
+ if (ret)
+ goto error_power_down;
+
+ ret = apds9960_chip_init(data);
+ if (ret)
+ goto error_power_down;
+
+ if (client->irq <= 0) {
+ dev_err(&client->dev, "no valid irq defined\n");
+ ret = -EINVAL;
+ goto error_power_down;
+ }
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, apds9960_interrupt_handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "apds9960_event",
+ indio_dev);
+ if (ret) {
+ dev_err(&client->dev, "request irq (%d) failed\n", client->irq);
+ goto error_power_down;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_power_down;
+
+ apds9960_set_power_state(data, false);
+
+ return 0;
+
+error_power_down:
+ apds9960_set_power_state(data, false);
+
+ return ret;
+}
+
+static int apds9960_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct apds9960_data *data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ apds9960_set_powermode(data, 0);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int apds9960_runtime_suspend(struct device *dev)
+{
+ struct apds9960_data *data =
+ iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+ return apds9960_set_powermode(data, 0);
+}
+
+static int apds9960_runtime_resume(struct device *dev)
+{
+ struct apds9960_data *data =
+ iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+ return apds9960_set_powermode(data, 1);
+}
+#endif
+
+static const struct dev_pm_ops apds9960_pm_ops = {
+ SET_RUNTIME_PM_OPS(apds9960_runtime_suspend,
+ apds9960_runtime_resume, NULL)
+};
+
+static const struct i2c_device_id apds9960_id[] = {
+ { "apds9960", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, apds9960_id);
+
+static struct i2c_driver apds9960_driver = {
+ .driver = {
+ .name = APDS9960_DRV_NAME,
+ .pm = &apds9960_pm_ops,
+ },
+ .probe = apds9960_probe,
+ .remove = apds9960_remove,
+ .id_table = apds9960_id,
+};
+module_i2c_driver(apds9960_driver);
+
+MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_DESCRIPTION("ADPS9960 Gesture/RGB/ALS/Proximity sensor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c
index 923aa6aef0ed..01e111e72d4b 100644
--- a/drivers/iio/light/opt3001.c
+++ b/drivers/iio/light/opt3001.c
@@ -793,7 +793,6 @@ static struct i2c_driver opt3001_driver = {
.driver = {
.name = "opt3001",
.of_match_table = of_match_ptr(opt3001_of_match),
- .owner = THIS_MODULE,
},
};
diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c
index 993eb201148e..013b21779db9 100644
--- a/drivers/iio/light/stk3310.c
+++ b/drivers/iio/light/stk3310.c
@@ -35,8 +35,8 @@
#define STK3310_REG_ID 0x3E
#define STK3310_MAX_REG 0x80
-#define STK3310_STATE_EN_PS 0x01
-#define STK3310_STATE_EN_ALS 0x02
+#define STK3310_STATE_EN_PS BIT(0)
+#define STK3310_STATE_EN_ALS BIT(1)
#define STK3310_STATE_STANDBY 0x00
#define STK3310_CHIP_ID_VAL 0x13
@@ -241,8 +241,11 @@ static int stk3310_write_event(struct iio_dev *indio_dev,
struct stk3310_data *data = iio_priv(indio_dev);
struct i2c_client *client = data->client;
- regmap_field_read(data->reg_ps_gain, &index);
- if (val > stk3310_ps_max[index])
+ ret = regmap_field_read(data->reg_ps_gain, &index);
+ if (ret < 0)
+ return ret;
+
+ if (val < 0 || val > stk3310_ps_max[index])
return -EINVAL;
if (dir == IIO_EV_DIR_RISING)
@@ -266,9 +269,12 @@ static int stk3310_read_event_config(struct iio_dev *indio_dev,
enum iio_event_direction dir)
{
unsigned int event_val;
+ int ret;
struct stk3310_data *data = iio_priv(indio_dev);
- regmap_field_read(data->reg_int_ps, &event_val);
+ ret = regmap_field_read(data->reg_int_ps, &event_val);
+ if (ret < 0)
+ return ret;
return event_val;
}
@@ -307,14 +313,16 @@ static int stk3310_read_raw(struct iio_dev *indio_dev,
struct stk3310_data *data = iio_priv(indio_dev);
struct i2c_client *client = data->client;
+ if (chan->type != IIO_LIGHT && chan->type != IIO_PROXIMITY)
+ return -EINVAL;
+
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (chan->type == IIO_LIGHT)
reg = STK3310_REG_ALS_DATA_MSB;
- else if (chan->type == IIO_PROXIMITY)
- reg = STK3310_REG_PS_DATA_MSB;
else
- return -EINVAL;
+ reg = STK3310_REG_PS_DATA_MSB;
+
mutex_lock(&data->lock);
ret = regmap_bulk_read(data->regmap, reg, &buf, 2);
if (ret < 0) {
@@ -327,17 +335,23 @@ static int stk3310_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
case IIO_CHAN_INFO_INT_TIME:
if (chan->type == IIO_LIGHT)
- regmap_field_read(data->reg_als_it, &index);
+ ret = regmap_field_read(data->reg_als_it, &index);
else
- regmap_field_read(data->reg_ps_it, &index);
+ ret = regmap_field_read(data->reg_ps_it, &index);
+ if (ret < 0)
+ return ret;
+
*val = stk3310_it_table[index][0];
*val2 = stk3310_it_table[index][1];
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SCALE:
if (chan->type == IIO_LIGHT)
- regmap_field_read(data->reg_als_gain, &index);
+ ret = regmap_field_read(data->reg_als_gain, &index);
else
- regmap_field_read(data->reg_ps_gain, &index);
+ ret = regmap_field_read(data->reg_ps_gain, &index);
+ if (ret < 0)
+ return ret;
+
*val = stk3310_scale_table[index][0];
*val2 = stk3310_scale_table[index][1];
return IIO_VAL_INT_PLUS_MICRO;
@@ -354,6 +368,9 @@ static int stk3310_write_raw(struct iio_dev *indio_dev,
int index;
struct stk3310_data *data = iio_priv(indio_dev);
+ if (chan->type != IIO_LIGHT && chan->type != IIO_PROXIMITY)
+ return -EINVAL;
+
switch (mask) {
case IIO_CHAN_INFO_INT_TIME:
index = stk3310_get_index(stk3310_it_table,
@@ -368,7 +385,7 @@ static int stk3310_write_raw(struct iio_dev *indio_dev,
ret = regmap_field_write(data->reg_ps_it, index);
if (ret < 0)
dev_err(&data->client->dev,
- "sensor configuration failed\n");
+ "sensor configuration failed\n");
mutex_unlock(&data->lock);
return ret;
@@ -385,7 +402,7 @@ static int stk3310_write_raw(struct iio_dev *indio_dev,
ret = regmap_field_write(data->reg_ps_gain, index);
if (ret < 0)
dev_err(&data->client->dev,
- "sensor configuration failed\n");
+ "sensor configuration failed\n");
mutex_unlock(&data->lock);
return ret;
}
@@ -419,8 +436,8 @@ static int stk3310_set_state(struct stk3310_data *data, u8 state)
dev_err(&client->dev, "failed to change sensor state\n");
} else if (state != STK3310_STATE_STANDBY) {
/* Don't reset the 'enabled' flags if we're going in standby */
- data->ps_enabled = !!(state & 0x01);
- data->als_enabled = !!(state & 0x02);
+ data->ps_enabled = !!(state & STK3310_STATE_EN_PS);
+ data->als_enabled = !!(state & STK3310_STATE_EN_ALS);
}
mutex_unlock(&data->lock);
@@ -435,7 +452,10 @@ static int stk3310_init(struct iio_dev *indio_dev)
struct stk3310_data *data = iio_priv(indio_dev);
struct i2c_client *client = data->client;
- regmap_read(data->regmap, STK3310_REG_ID, &chipid);
+ ret = regmap_read(data->regmap, STK3310_REG_ID, &chipid);
+ if (ret < 0)
+ return ret;
+
if (chipid != STK3310_CHIP_ID_VAL &&
chipid != STK3311_CHIP_ID_VAL) {
dev_err(&client->dev, "invalid chip id: 0x%x\n", chipid);
@@ -604,8 +624,13 @@ static int stk3310_probe(struct i2c_client *client,
if (ret < 0)
return ret;
- if (client->irq < 0)
+ if (client->irq < 0) {
client->irq = stk3310_gpio_probe(client);
+ if (client->irq < 0) {
+ ret = client->irq;
+ goto err_standby;
+ }
+ }
if (client->irq >= 0) {
ret = devm_request_threaded_irq(&client->dev, client->irq,
@@ -614,17 +639,23 @@ static int stk3310_probe(struct i2c_client *client,
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
STK3310_EVENT, indio_dev);
- if (ret < 0)
+ if (ret < 0) {
dev_err(&client->dev, "request irq %d failed\n",
- client->irq);
+ client->irq);
+ goto err_standby;
+ }
}
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev, "device_register failed\n");
- stk3310_set_state(data, STK3310_STATE_STANDBY);
+ goto err_standby;
}
+ return 0;
+
+err_standby:
+ stk3310_set_state(data, STK3310_STATE_STANDBY);
return ret;
}
@@ -648,7 +679,7 @@ static int stk3310_suspend(struct device *dev)
static int stk3310_resume(struct device *dev)
{
- int state = 0;
+ u8 state = 0;
struct stk3310_data *data;
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
diff --git a/drivers/iio/light/tsl4531.c b/drivers/iio/light/tsl4531.c
index 26979183d27c..cf94ec72b181 100644
--- a/drivers/iio/light/tsl4531.c
+++ b/drivers/iio/light/tsl4531.c
@@ -158,9 +158,9 @@ static int tsl4531_check_id(struct i2c_client *client)
case TSL45313_ID:
case TSL45315_ID:
case TSL45317_ID:
- return 1;
- default:
return 0;
+ default:
+ return -ENODEV;
}
}
@@ -180,9 +180,10 @@ static int tsl4531_probe(struct i2c_client *client,
data->client = client;
mutex_init(&data->lock);
- if (!tsl4531_check_id(client)) {
+ ret = tsl4531_check_id(client);
+ if (ret) {
dev_err(&client->dev, "no TSL4531 sensor\n");
- return -ENODEV;
+ return ret;
}
ret = i2c_smbus_write_byte_data(data->client, TSL4531_CONTROL,
diff --git a/drivers/iio/light/us5182d.c b/drivers/iio/light/us5182d.c
new file mode 100644
index 000000000000..49dab3cb3e23
--- /dev/null
+++ b/drivers/iio/light/us5182d.c
@@ -0,0 +1,507 @@
+/*
+ * Copyright (c) 2015 Intel Corporation
+ *
+ * Driver for UPISEMI us5182d Proximity and Ambient Light Sensor.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * To do: Interrupt support.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/mutex.h>
+
+#define US5182D_REG_CFG0 0x00
+#define US5182D_CFG0_ONESHOT_EN BIT(6)
+#define US5182D_CFG0_SHUTDOWN_EN BIT(7)
+#define US5182D_CFG0_WORD_ENABLE BIT(0)
+
+#define US5182D_REG_CFG1 0x01
+#define US5182D_CFG1_ALS_RES16 BIT(4)
+#define US5182D_CFG1_AGAIN_DEFAULT 0x00
+
+#define US5182D_REG_CFG2 0x02
+#define US5182D_CFG2_PX_RES16 BIT(4)
+#define US5182D_CFG2_PXGAIN_DEFAULT BIT(2)
+
+#define US5182D_REG_CFG3 0x03
+#define US5182D_CFG3_LED_CURRENT100 (BIT(4) | BIT(5))
+
+#define US5182D_REG_CFG4 0x10
+
+/*
+ * Registers for tuning the auto dark current cancelling feature.
+ * DARK_TH(reg 0x27,0x28) - threshold (counts) for auto dark cancelling.
+ * when ALS > DARK_TH --> ALS_Code = ALS - Upper(0x2A) * Dark
+ * when ALS < DARK_TH --> ALS_Code = ALS - Lower(0x29) * Dark
+ */
+#define US5182D_REG_UDARK_TH 0x27
+#define US5182D_REG_DARK_AUTO_EN 0x2b
+#define US5182D_REG_AUTO_LDARK_GAIN 0x29
+#define US5182D_REG_AUTO_HDARK_GAIN 0x2a
+
+#define US5182D_OPMODE_ALS 0x01
+#define US5182D_OPMODE_PX 0x02
+#define US5182D_OPMODE_SHIFT 4
+
+#define US5182D_REG_DARK_AUTO_EN_DEFAULT 0x80
+#define US5182D_REG_AUTO_LDARK_GAIN_DEFAULT 0x16
+#define US5182D_REG_AUTO_HDARK_GAIN_DEFAULT 0x00
+
+#define US5182D_REG_ADL 0x0c
+#define US5182D_REG_PDL 0x0e
+
+#define US5182D_REG_MODE_STORE 0x21
+#define US5182D_STORE_MODE 0x01
+
+#define US5182D_REG_CHIPID 0xb2
+
+#define US5182D_OPMODE_MASK GENMASK(5, 4)
+#define US5182D_AGAIN_MASK 0x07
+#define US5182D_RESET_CHIP 0x01
+
+#define US5182D_CHIPID 0x26
+#define US5182D_DRV_NAME "us5182d"
+
+#define US5182D_GA_RESOLUTION 1000
+
+#define US5182D_READ_BYTE 1
+#define US5182D_READ_WORD 2
+#define US5182D_OPSTORE_SLEEP_TIME 20 /* ms */
+
+/* Available ranges: [12354, 7065, 3998, 2202, 1285, 498, 256, 138] lux */
+static const int us5182d_scales[] = {188500, 107800, 61000, 33600, 19600, 7600,
+ 3900, 2100};
+
+/*
+ * Experimental thresholds that work with US5182D sensor on evaluation board
+ * roughly between 12-32 lux
+ */
+static u16 us5182d_dark_ths_vals[] = {170, 200, 512, 512, 800, 2000, 4000,
+ 8000};
+
+enum mode {
+ US5182D_ALS_PX,
+ US5182D_ALS_ONLY,
+ US5182D_PX_ONLY
+};
+
+struct us5182d_data {
+ struct i2c_client *client;
+ struct mutex lock;
+
+ /* Glass attenuation factor */
+ u32 ga;
+
+ /* Dark gain tuning */
+ u8 lower_dark_gain;
+ u8 upper_dark_gain;
+ u16 *us5182d_dark_ths;
+
+ u8 opmode;
+};
+
+static IIO_CONST_ATTR(in_illuminance_scale_available,
+ "0.0021 0.0039 0.0076 0.0196 0.0336 0.061 0.1078 0.1885");
+
+static struct attribute *us5182d_attrs[] = {
+ &iio_const_attr_in_illuminance_scale_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group us5182d_attr_group = {
+ .attrs = us5182d_attrs,
+};
+
+static const struct {
+ u8 reg;
+ u8 val;
+} us5182d_regvals[] = {
+ {US5182D_REG_CFG0, (US5182D_CFG0_SHUTDOWN_EN |
+ US5182D_CFG0_WORD_ENABLE)},
+ {US5182D_REG_CFG1, US5182D_CFG1_ALS_RES16},
+ {US5182D_REG_CFG2, (US5182D_CFG2_PX_RES16 |
+ US5182D_CFG2_PXGAIN_DEFAULT)},
+ {US5182D_REG_CFG3, US5182D_CFG3_LED_CURRENT100},
+ {US5182D_REG_MODE_STORE, US5182D_STORE_MODE},
+ {US5182D_REG_CFG4, 0x00},
+};
+
+static const struct iio_chan_spec us5182d_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ },
+ {
+ .type = IIO_PROXIMITY,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ }
+};
+
+static int us5182d_get_als(struct us5182d_data *data)
+{
+ int ret;
+ unsigned long result;
+
+ ret = i2c_smbus_read_word_data(data->client,
+ US5182D_REG_ADL);
+ if (ret < 0)
+ return ret;
+
+ result = ret * data->ga / US5182D_GA_RESOLUTION;
+ if (result > 0xffff)
+ result = 0xffff;
+
+ return result;
+}
+
+static int us5182d_set_opmode(struct us5182d_data *data, u8 mode)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG0);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * In oneshot mode the chip will power itself down after taking the
+ * required measurement.
+ */
+ ret = ret | US5182D_CFG0_ONESHOT_EN;
+
+ /* update mode */
+ ret = ret & ~US5182D_OPMODE_MASK;
+ ret = ret | (mode << US5182D_OPMODE_SHIFT);
+
+ /*
+ * After updating the operating mode, the chip requires that
+ * the operation is stored, by writing 1 in the STORE_MODE
+ * register (auto-clearing).
+ */
+ ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG0, ret);
+ if (ret < 0)
+ return ret;
+
+ if (mode == data->opmode)
+ return 0;
+
+ ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_MODE_STORE,
+ US5182D_STORE_MODE);
+ if (ret < 0)
+ return ret;
+
+ data->opmode = mode;
+ msleep(US5182D_OPSTORE_SLEEP_TIME);
+
+ return 0;
+}
+
+static int us5182d_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct us5182d_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_LIGHT:
+ mutex_lock(&data->lock);
+ ret = us5182d_set_opmode(data, US5182D_OPMODE_ALS);
+ if (ret < 0)
+ goto out_err;
+
+ ret = us5182d_get_als(data);
+ if (ret < 0)
+ goto out_err;
+ mutex_unlock(&data->lock);
+ *val = ret;
+ return IIO_VAL_INT;
+ case IIO_PROXIMITY:
+ mutex_lock(&data->lock);
+ ret = us5182d_set_opmode(data, US5182D_OPMODE_PX);
+ if (ret < 0)
+ goto out_err;
+
+ ret = i2c_smbus_read_word_data(data->client,
+ US5182D_REG_PDL);
+ if (ret < 0)
+ goto out_err;
+ mutex_unlock(&data->lock);
+ *val = ret;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_CHAN_INFO_SCALE:
+ ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG1);
+ if (ret < 0)
+ return ret;
+
+ *val = 0;
+ *val2 = us5182d_scales[ret & US5182D_AGAIN_MASK];
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+out_err:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+/**
+ * us5182d_update_dark_th - update Darh_Th registers
+ * @data us5182d_data structure
+ * @index index in us5182d_dark_ths array to use for the updated value
+ *
+ * Function needs to be called with a lock held because it needs two i2c write
+ * byte operations as these registers (0x27 0x28) don't work in word mode
+ * accessing.
+ */
+static int us5182d_update_dark_th(struct us5182d_data *data, int index)
+{
+ __be16 dark_th = cpu_to_be16(data->us5182d_dark_ths[index]);
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_UDARK_TH,
+ ((u8 *)&dark_th)[0]);
+ if (ret < 0)
+ return ret;
+
+ return i2c_smbus_write_byte_data(data->client, US5182D_REG_UDARK_TH + 1,
+ ((u8 *)&dark_th)[1]);
+}
+
+/**
+ * us5182d_apply_scale - update the ALS scale
+ * @data us5182d_data structure
+ * @index index in us5182d_scales array to use for the updated value
+ *
+ * Function needs to be called with a lock held as we're having more than one
+ * i2c operation.
+ */
+static int us5182d_apply_scale(struct us5182d_data *data, int index)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG1);
+ if (ret < 0)
+ return ret;
+
+ ret = ret & (~US5182D_AGAIN_MASK);
+ ret |= index;
+
+ ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG1, ret);
+ if (ret < 0)
+ return ret;
+
+ return us5182d_update_dark_th(data, index);
+}
+
+static int us5182d_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct us5182d_data *data = iio_priv(indio_dev);
+ int ret, i;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (val != 0)
+ return -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(us5182d_scales); i++)
+ if (val2 == us5182d_scales[i]) {
+ mutex_lock(&data->lock);
+ ret = us5182d_apply_scale(data, i);
+ mutex_unlock(&data->lock);
+ return ret;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info us5182d_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = us5182d_read_raw,
+ .write_raw = us5182d_write_raw,
+ .attrs = &us5182d_attr_group,
+};
+
+static int us5182d_reset(struct iio_dev *indio_dev)
+{
+ struct us5182d_data *data = iio_priv(indio_dev);
+
+ return i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG3,
+ US5182D_RESET_CHIP);
+}
+
+static int us5182d_init(struct iio_dev *indio_dev)
+{
+ struct us5182d_data *data = iio_priv(indio_dev);
+ int i, ret;
+
+ ret = us5182d_reset(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ data->opmode = 0;
+ for (i = 0; i < ARRAY_SIZE(us5182d_regvals); i++) {
+ ret = i2c_smbus_write_byte_data(data->client,
+ us5182d_regvals[i].reg,
+ us5182d_regvals[i].val);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void us5182d_get_platform_data(struct iio_dev *indio_dev)
+{
+ struct us5182d_data *data = iio_priv(indio_dev);
+
+ if (device_property_read_u32(&data->client->dev, "upisemi,glass-coef",
+ &data->ga))
+ data->ga = US5182D_GA_RESOLUTION;
+ if (device_property_read_u16_array(&data->client->dev,
+ "upisemi,dark-ths",
+ data->us5182d_dark_ths,
+ ARRAY_SIZE(us5182d_dark_ths_vals)))
+ data->us5182d_dark_ths = us5182d_dark_ths_vals;
+ if (device_property_read_u8(&data->client->dev,
+ "upisemi,upper-dark-gain",
+ &data->upper_dark_gain))
+ data->upper_dark_gain = US5182D_REG_AUTO_HDARK_GAIN_DEFAULT;
+ if (device_property_read_u8(&data->client->dev,
+ "upisemi,lower-dark-gain",
+ &data->lower_dark_gain))
+ data->lower_dark_gain = US5182D_REG_AUTO_LDARK_GAIN_DEFAULT;
+}
+
+static int us5182d_dark_gain_config(struct iio_dev *indio_dev)
+{
+ struct us5182d_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = us5182d_update_dark_th(data, US5182D_CFG1_AGAIN_DEFAULT);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(data->client,
+ US5182D_REG_AUTO_LDARK_GAIN,
+ data->lower_dark_gain);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(data->client,
+ US5182D_REG_AUTO_HDARK_GAIN,
+ data->upper_dark_gain);
+ if (ret < 0)
+ return ret;
+
+ return i2c_smbus_write_byte_data(data->client, US5182D_REG_DARK_AUTO_EN,
+ US5182D_REG_DARK_AUTO_EN_DEFAULT);
+}
+
+static int us5182d_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct us5182d_data *data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+
+ mutex_init(&data->lock);
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &us5182d_info;
+ indio_dev->name = US5182D_DRV_NAME;
+ indio_dev->channels = us5182d_channels;
+ indio_dev->num_channels = ARRAY_SIZE(us5182d_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CHIPID);
+ if (ret != US5182D_CHIPID) {
+ dev_err(&data->client->dev,
+ "Failed to detect US5182 light chip\n");
+ return (ret < 0) ? ret : -ENODEV;
+ }
+
+ us5182d_get_platform_data(indio_dev);
+ ret = us5182d_init(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ ret = us5182d_dark_gain_config(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ return iio_device_register(indio_dev);
+}
+
+static int us5182d_remove(struct i2c_client *client)
+{
+ iio_device_unregister(i2c_get_clientdata(client));
+ return i2c_smbus_write_byte_data(client, US5182D_REG_CFG0,
+ US5182D_CFG0_SHUTDOWN_EN);
+}
+
+static const struct acpi_device_id us5182d_acpi_match[] = {
+ { "USD5182", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(acpi, us5182d_acpi_match);
+
+static const struct i2c_device_id us5182d_id[] = {
+ {"usd5182", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, us5182d_id);
+
+static struct i2c_driver us5182d_driver = {
+ .driver = {
+ .name = US5182D_DRV_NAME,
+ .acpi_match_table = ACPI_PTR(us5182d_acpi_match),
+ },
+ .probe = us5182d_probe,
+ .remove = us5182d_remove,
+ .id_table = us5182d_id,
+
+};
+module_i2c_driver(us5182d_driver);
+
+MODULE_AUTHOR("Adriana Reus <adriana.reus@intel.com>");
+MODULE_DESCRIPTION("Driver for us5182d Proximity and Light Sensor");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
index efb9350b0d76..868abada3409 100644
--- a/drivers/iio/magnetometer/Kconfig
+++ b/drivers/iio/magnetometer/Kconfig
@@ -24,6 +24,24 @@ config AK09911
help
Deprecated: AK09911 is now supported by AK8975 driver.
+config BMC150_MAGN
+ tristate "Bosch BMC150 Magnetometer Driver"
+ depends on I2C
+ select REGMAP_I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for the BMC150 magnetometer.
+
+ Currently this only supports the device via an i2c interface.
+
+ This is a combo module with both accelerometer and magnetometer.
+ This driver is only implementing magnetometer part, which has
+ its own address and register map.
+
+ To compile this driver as a module, choose M here: the module will be
+ called bmc150_magn.
+
config MAG3110
tristate "Freescale MAG3110 3-Axis Magnetometer"
depends on I2C
@@ -87,19 +105,4 @@ config IIO_ST_MAGN_SPI_3AXIS
depends on IIO_ST_MAGN_3AXIS
depends on IIO_ST_SENSORS_SPI
-config BMC150_MAGN
- tristate "Bosch BMC150 Magnetometer Driver"
- depends on I2C
- select REGMAP_I2C
- select IIO_BUFFER
- select IIO_TRIGGERED_BUFFER
- help
- Say yes here to build support for the BMC150 magnetometer.
-
- Currently this only supports the device via an i2c interface.
-
- This is a combo module with both accelerometer and magnetometer.
- This driver is only implementing magnetometer part, which has
- its own address and register map.
-
endmenu
diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
index 33b1d4d54ee7..2c72df458ec2 100644
--- a/drivers/iio/magnetometer/Makefile
+++ b/drivers/iio/magnetometer/Makefile
@@ -4,6 +4,7 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AK8975) += ak8975.o
+obj-$(CONFIG_BMC150_MAGN) += bmc150_magn.o
obj-$(CONFIG_MAG3110) += mag3110.o
obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o
obj-$(CONFIG_MMC35240) += mmc35240.o
@@ -14,5 +15,3 @@ st_magn-$(CONFIG_IIO_BUFFER) += st_magn_buffer.o
obj-$(CONFIG_IIO_ST_MAGN_I2C_3AXIS) += st_magn_i2c.o
obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o
-
-obj-$(CONFIG_BMC150_MAGN) += bmc150_magn.o
diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c
index f8dc4b85d70c..b27f0146647b 100644
--- a/drivers/iio/magnetometer/st_magn_core.c
+++ b/drivers/iio/magnetometer/st_magn_core.c
@@ -560,6 +560,7 @@ static const struct iio_info magn_info = {
.attrs = &st_magn_attribute_group,
.read_raw = &st_magn_read_raw,
.write_raw = &st_magn_write_raw,
+ .debugfs_reg_access = &st_sensors_debugfs_reg_access,
};
#ifdef CONFIG_IIO_TRIGGER
diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c
index eb41d2b92c24..b39a2fb0671c 100644
--- a/drivers/iio/pressure/st_pressure_core.c
+++ b/drivers/iio/pressure/st_pressure_core.c
@@ -400,6 +400,7 @@ static const struct iio_info press_info = {
.attrs = &st_press_attribute_group,
.read_raw = &st_press_read_raw,
.write_raw = &st_press_write_raw,
+ .debugfs_reg_access = &st_sensors_debugfs_reg_access,
};
#ifdef CONFIG_IIO_TRIGGER
diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig
index 41a8d8ffa0de..ef4c73db5b53 100644
--- a/drivers/iio/proximity/Kconfig
+++ b/drivers/iio/proximity/Kconfig
@@ -20,6 +20,18 @@ endmenu
menu "Proximity sensors"
+config LIDAR_LITE_V2
+ tristate "PulsedLight LIDAR sensor"
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ depends on I2C
+ help
+ Say Y to build a driver for PulsedLight LIDAR range finding
+ sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pulsedlight-lite-v2
+
config SX9500
tristate "SX9500 Semtech proximity sensor"
select IIO_BUFFER
diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile
index 9818dc562abd..9aadd9a8ee99 100644
--- a/drivers/iio/proximity/Makefile
+++ b/drivers/iio/proximity/Makefile
@@ -4,4 +4,5 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AS3935) += as3935.o
+obj-$(CONFIG_LIDAR_LITE_V2) += pulsedlight-lidar-lite-v2.o
obj-$(CONFIG_SX9500) += sx9500.o
diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c
index bc0d68efd455..e95035136889 100644
--- a/drivers/iio/proximity/as3935.c
+++ b/drivers/iio/proximity/as3935.c
@@ -434,6 +434,12 @@ static int as3935_remove(struct spi_device *spi)
return 0;
}
+static const struct of_device_id as3935_of_match[] = {
+ { .compatible = "ams,as3935", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, as3935_of_match);
+
static const struct spi_device_id as3935_id[] = {
{"as3935", 0},
{},
@@ -443,6 +449,7 @@ MODULE_DEVICE_TABLE(spi, as3935_id);
static struct spi_driver as3935_driver = {
.driver = {
.name = "as3935",
+ .of_match_table = of_match_ptr(as3935_of_match),
.owner = THIS_MODULE,
.pm = AS3935_PM_OPS,
},
diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
new file mode 100644
index 000000000000..185a7ab4f31b
--- /dev/null
+++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
@@ -0,0 +1,288 @@
+/*
+ * pulsedlight-lidar-lite-v2.c - Support for PulsedLight LIDAR sensor
+ *
+ * Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * TODO: runtime pm, interrupt mode, and signal strength reporting
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#define LIDAR_REG_CONTROL 0x00
+#define LIDAR_REG_CONTROL_ACQUIRE BIT(2)
+
+#define LIDAR_REG_STATUS 0x01
+#define LIDAR_REG_STATUS_INVALID BIT(3)
+#define LIDAR_REG_STATUS_READY BIT(0)
+
+#define LIDAR_REG_DATA_HBYTE 0x0f
+#define LIDAR_REG_DATA_LBYTE 0x10
+
+#define LIDAR_DRV_NAME "lidar"
+
+struct lidar_data {
+ struct iio_dev *indio_dev;
+ struct i2c_client *client;
+
+ u16 buffer[8]; /* 2 byte distance + 8 byte timestamp */
+};
+
+static const struct iio_chan_spec lidar_channels[] = {
+ {
+ .type = IIO_DISTANCE,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static int lidar_read_byte(struct lidar_data *data, int reg)
+{
+ struct i2c_client *client = data->client;
+ int ret;
+
+ /*
+ * Device needs a STOP condition between address write, and data read
+ * so in turn i2c_smbus_read_byte_data cannot be used
+ */
+
+ ret = i2c_smbus_write_byte(client, reg);
+ if (ret < 0) {
+ dev_err(&client->dev, "cannot write addr value");
+ return ret;
+ }
+
+ ret = i2c_smbus_read_byte(client);
+ if (ret < 0)
+ dev_err(&client->dev, "cannot read data value");
+
+ return ret;
+}
+
+static inline int lidar_write_control(struct lidar_data *data, int val)
+{
+ return i2c_smbus_write_byte_data(data->client, LIDAR_REG_CONTROL, val);
+}
+
+static int lidar_read_measurement(struct lidar_data *data, u16 *reg)
+{
+ int ret;
+ int val;
+
+ ret = lidar_read_byte(data, LIDAR_REG_DATA_HBYTE);
+ if (ret < 0)
+ return ret;
+ val = ret << 8;
+
+ ret = lidar_read_byte(data, LIDAR_REG_DATA_LBYTE);
+ if (ret < 0)
+ return ret;
+
+ val |= ret;
+ *reg = val;
+
+ return 0;
+}
+
+static int lidar_get_measurement(struct lidar_data *data, u16 *reg)
+{
+ struct i2c_client *client = data->client;
+ int tries = 10;
+ int ret;
+
+ /* start sample */
+ ret = lidar_write_control(data, LIDAR_REG_CONTROL_ACQUIRE);
+ if (ret < 0) {
+ dev_err(&client->dev, "cannot send start measurement command");
+ return ret;
+ }
+
+ while (tries--) {
+ usleep_range(1000, 2000);
+
+ ret = lidar_read_byte(data, LIDAR_REG_STATUS);
+ if (ret < 0)
+ break;
+
+ /* return 0 since laser is likely pointed out of range */
+ if (ret & LIDAR_REG_STATUS_INVALID) {
+ *reg = 0;
+ ret = 0;
+ break;
+ }
+
+ /* sample ready to read */
+ if (!(ret & LIDAR_REG_STATUS_READY)) {
+ ret = lidar_read_measurement(data, reg);
+ break;
+ }
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int lidar_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct lidar_data *data = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ mutex_lock(&indio_dev->mlock);
+
+ if (iio_buffer_enabled(indio_dev) && mask == IIO_CHAN_INFO_RAW) {
+ ret = -EBUSY;
+ goto error_busy;
+ }
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW: {
+ u16 reg;
+
+ ret = lidar_get_measurement(data, &reg);
+ if (!ret) {
+ *val = reg;
+ ret = IIO_VAL_INT;
+ }
+ break;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = 10000;
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ }
+
+error_busy:
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static irqreturn_t lidar_trigger_handler(int irq, void *private)
+{
+ struct iio_poll_func *pf = private;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct lidar_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = lidar_get_measurement(data, data->buffer);
+ if (!ret) {
+ iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+ iio_get_time_ns());
+ } else {
+ dev_err(&data->client->dev, "cannot read LIDAR measurement");
+ }
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_info lidar_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = lidar_read_raw,
+};
+
+static int lidar_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct lidar_data *data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ indio_dev->info = &lidar_info;
+ indio_dev->name = LIDAR_DRV_NAME;
+ indio_dev->channels = lidar_channels;
+ indio_dev->num_channels = ARRAY_SIZE(lidar_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+
+ data->client = client;
+ data->indio_dev = indio_dev;
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ lidar_trigger_handler, NULL);
+ if (ret)
+ return ret;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_unreg_buffer;
+
+ return 0;
+
+error_unreg_buffer:
+ iio_triggered_buffer_cleanup(indio_dev);
+
+ return ret;
+}
+
+static int lidar_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id lidar_id[] = {
+ {"lidar-lite-v2", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, lidar_id);
+
+static const struct of_device_id lidar_dt_ids[] = {
+ { .compatible = "pulsedlight,lidar-lite-v2" },
+ { }
+};
+
+static struct i2c_driver lidar_driver = {
+ .driver = {
+ .name = LIDAR_DRV_NAME,
+ .of_match_table = of_match_ptr(lidar_dt_ids),
+ },
+ .probe = lidar_probe,
+ .remove = lidar_remove,
+ .id_table = lidar_id,
+};
+module_i2c_driver(lidar_driver);
+
+MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_DESCRIPTION("PulsedLight LIDAR sensor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c
index 5d033a5af615..3fd3ba426a84 100644
--- a/drivers/iio/temperature/mlx90614.c
+++ b/drivers/iio/temperature/mlx90614.c
@@ -3,6 +3,7 @@
*
* Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
* Copyright (c) 2015 Essensium NV
+ * Copyright (c) 2015 Melexis
*
* This file is subject to the terms and conditions of version 2 of
* the GNU General Public License. See the file COPYING in the main
@@ -20,7 +21,6 @@
* always has a pull-up so we do not need an extra GPIO to drive it high. If
* the "wakeup" GPIO is not given, power management will be disabled.
*
- * TODO: filter configuration
*/
#include <linux/err.h>
@@ -32,6 +32,7 @@
#include <linux/pm_runtime.h>
#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
#define MLX90614_OP_RAM 0x00
#define MLX90614_OP_EEPROM 0x20
@@ -79,6 +80,20 @@ struct mlx90614_data {
unsigned long ready_timestamp; /* in jiffies */
};
+/* Bandwidth values for IIR filtering */
+static const int mlx90614_iir_values[] = {77, 31, 20, 15, 723, 153, 110, 86};
+static IIO_CONST_ATTR(in_temp_object_filter_low_pass_3db_frequency_available,
+ "0.15 0.20 0.31 0.77 0.86 1.10 1.53 7.23");
+
+static struct attribute *mlx90614_attributes[] = {
+ &iio_const_attr_in_temp_object_filter_low_pass_3db_frequency_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group mlx90614_attr_group = {
+ .attrs = mlx90614_attributes,
+};
+
/*
* Erase an address and write word.
* The mutex must be locked before calling.
@@ -117,6 +132,42 @@ static s32 mlx90614_write_word(const struct i2c_client *client, u8 command,
return ret;
}
+/*
+ * Find the IIR value inside mlx90614_iir_values array and return its position
+ * which is equivalent to the bit value in sensor register
+ */
+static inline s32 mlx90614_iir_search(const struct i2c_client *client,
+ int value)
+{
+ int i;
+ s32 ret;
+
+ for (i = 0; i < ARRAY_SIZE(mlx90614_iir_values); ++i) {
+ if (value == mlx90614_iir_values[i])
+ break;
+ }
+
+ if (i == ARRAY_SIZE(mlx90614_iir_values))
+ return -EINVAL;
+
+ /*
+ * CONFIG register values must not be changed so
+ * we must read them before we actually write
+ * changes
+ */
+ ret = i2c_smbus_read_word_data(client, MLX90614_CONFIG);
+ if (ret > 0)
+ return ret;
+
+ /* Write changed values */
+ ret = mlx90614_write_word(client, MLX90614_CONFIG,
+ (i << MLX90614_CONFIG_IIR_SHIFT) |
+ (((u16) ((0x7 << MLX90614_CONFIG_FIR_SHIFT) |
+ ((u16) ret & (~((u16) MLX90614_CONFIG_FIR_MASK))))) &
+ (~(u16) MLX90614_CONFIG_IIR_MASK)));
+ return ret;
+}
+
#ifdef CONFIG_PM
/*
* If @startup is true, make sure MLX90614_TIMING_STARTUP ms have elapsed since
@@ -236,6 +287,21 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev,
*val2 = ret * MLX90614_CONST_EMISSIVITY_RESOLUTION;
}
return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: /* IIR setting with
+ FIR = 1024 */
+ mlx90614_power_get(data, false);
+ mutex_lock(&data->lock);
+ ret = i2c_smbus_read_word_data(data->client, MLX90614_CONFIG);
+ mutex_unlock(&data->lock);
+ mlx90614_power_put(data);
+
+ if (ret < 0)
+ return ret;
+
+ *val = mlx90614_iir_values[ret & MLX90614_CONFIG_IIR_MASK] / 100;
+ *val2 = (mlx90614_iir_values[ret & MLX90614_CONFIG_IIR_MASK] % 100) *
+ 10000;
+ return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
@@ -263,6 +329,18 @@ static int mlx90614_write_raw(struct iio_dev *indio_dev,
mlx90614_power_put(data);
return ret;
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: /* IIR Filter setting */
+ if (val < 0 || val2 < 0)
+ return -EINVAL;
+
+ mlx90614_power_get(data, false);
+ mutex_lock(&data->lock);
+ ret = mlx90614_iir_search(data->client,
+ val * 100 + val2 / 10000);
+ mutex_unlock(&data->lock);
+ mlx90614_power_put(data);
+
+ return ret;
default:
return -EINVAL;
}
@@ -275,6 +353,8 @@ static int mlx90614_write_raw_get_fmt(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_CALIBEMISSIVITY:
return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
@@ -294,7 +374,8 @@ static const struct iio_chan_spec mlx90614_channels[] = {
.modified = 1,
.channel2 = IIO_MOD_TEMP_OBJECT,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
- BIT(IIO_CHAN_INFO_CALIBEMISSIVITY),
+ BIT(IIO_CHAN_INFO_CALIBEMISSIVITY) |
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE),
},
@@ -305,7 +386,8 @@ static const struct iio_chan_spec mlx90614_channels[] = {
.channel = 1,
.channel2 = IIO_MOD_TEMP_OBJECT,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
- BIT(IIO_CHAN_INFO_CALIBEMISSIVITY),
+ BIT(IIO_CHAN_INFO_CALIBEMISSIVITY) |
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE),
},
@@ -315,6 +397,7 @@ static const struct iio_info mlx90614_info = {
.read_raw = mlx90614_read_raw,
.write_raw = mlx90614_write_raw,
.write_raw_get_fmt = mlx90614_write_raw_get_fmt,
+ .attrs = &mlx90614_attr_group,
.driver_module = THIS_MODULE,
};
@@ -569,5 +652,6 @@ module_i2c_driver(mlx90614_driver);
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
MODULE_AUTHOR("Vianney le Clément de Saint-Marcq <vianney.leclement@essensium.com>");
+MODULE_AUTHOR("Crt Mori <cmo@melexis.com>");
MODULE_DESCRIPTION("Melexis MLX90614 contactless IR temperature sensor driver");
MODULE_LICENSE("GPL");