diff options
Diffstat (limited to 'drivers/iio')
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 *) ®, 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, ®); + 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, ®); + 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, ®, 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, ®, 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, ®, 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, ®); + 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, ®); + 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, ®); + 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, ®); + 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, ®); + 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, ®); + 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, ®); + 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, ®); + 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"); |