From 9dde479e9f9e5b2eafa3db8b7ce919b74533beb8 Mon Sep 17 00:00:00 2001 From: simran singhal Date: Wed, 22 Mar 2017 21:50:43 +0530 Subject: staging: iio: adis16060: Remove iio_dev mlock and refactor code The IIO subsystem is redefining iio_dev->mlock to be used by the IIO core only for protecting device operating mode changes. ie. Changes between INDIO_DIRECT_MODE, INDIO_BUFFER_* modes. In this driver, mlock was being used to protect hardware state changes. In the driver, buf_lock protects both the adis16060_spi_write() and adis16060_spi_read() functions and both are always called in pair. First write, then read. Refactor the code to have one single function adis16060_spi_write_than_read() protected by the buf_lock. This removes the need for additional locking via mlock, so this locking is removed. Signed-off-by: simran singhal Signed-off-by: Jonathan Cameron --- drivers/staging/iio/gyro/adis16060_core.c | 35 +++++++++---------------------- 1 file changed, 10 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/iio/gyro/adis16060_core.c b/drivers/staging/iio/gyro/adis16060_core.c index c9d46e796f79..811596234823 100644 --- a/drivers/staging/iio/gyro/adis16060_core.c +++ b/drivers/staging/iio/gyro/adis16060_core.c @@ -40,25 +40,20 @@ struct adis16060_state { static struct iio_dev *adis16060_iio_dev; -static int adis16060_spi_write(struct iio_dev *indio_dev, u8 val) +static int adis16060_spi_write_than_read(struct iio_dev *indio_dev, + u8 conf, u16 *val) { int ret; struct adis16060_state *st = iio_priv(indio_dev); mutex_lock(&st->buf_lock); - st->buf[2] = val; /* The last 8 bits clocked in are latched */ + st->buf[2] = conf; /* The last 8 bits clocked in are latched */ ret = spi_write(st->us_w, st->buf, 3); - mutex_unlock(&st->buf_lock); - - return ret; -} - -static int adis16060_spi_read(struct iio_dev *indio_dev, u16 *val) -{ - int ret; - struct adis16060_state *st = iio_priv(indio_dev); - mutex_lock(&st->buf_lock); + if (ret < 0) { + mutex_unlock(&st->buf_lock); + return ret; + } ret = spi_read(st->us_r, st->buf, 3); @@ -86,17 +81,11 @@ static int adis16060_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - /* Take the iio_dev status lock */ - mutex_lock(&indio_dev->mlock); - ret = adis16060_spi_write(indio_dev, chan->address); + ret = adis16060_spi_write_than_read(indio_dev, + chan->address, &tval); if (ret < 0) - goto out_unlock; + return ret; - ret = adis16060_spi_read(indio_dev, &tval); - if (ret < 0) - goto out_unlock; - - mutex_unlock(&indio_dev->mlock); *val = tval; return IIO_VAL_INT; case IIO_CHAN_INFO_OFFSET: @@ -110,10 +99,6 @@ static int adis16060_read_raw(struct iio_dev *indio_dev, } return -EINVAL; - -out_unlock: - mutex_unlock(&indio_dev->mlock); - return ret; } static const struct iio_info adis16060_info = { -- cgit v1.2.3 From 63bb38c000a2c5817f643273c0596828fe7ce05c Mon Sep 17 00:00:00 2001 From: Arushi Singhal Date: Wed, 22 Mar 2017 22:12:34 +0530 Subject: staging: iio: Replace a bit shift by a use of BIT. This patch replaces bit shifting on 1 with the BIT(x) macro. This was done with coccinelle: @@ constant c; @@ -1 << c +BIT(c) Signed-off-by: Arushi Singhal Signed-off-by: Jonathan Cameron --- drivers/staging/iio/cdc/ad7150.c | 2 +- drivers/staging/iio/cdc/ad7746.c | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/iio/cdc/ad7150.c b/drivers/staging/iio/cdc/ad7150.c index ca72af3e9d4b..a6f249e9c1e1 100644 --- a/drivers/staging/iio/cdc/ad7150.c +++ b/drivers/staging/iio/cdc/ad7150.c @@ -232,7 +232,7 @@ static int ad7150_write_event_config(struct iio_dev *indio_dev, if (ret < 0) goto error_ret; - cfg = ret & ~((0x03 << 5) | (0x1 << 7)); + cfg = ret & ~((0x03 << 5) | BIT(7)); switch (type) { case IIO_EV_TYPE_MAG_ADAPTIVE: diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c index c2c8aa5585e4..2d8397b11b19 100644 --- a/drivers/staging/iio/cdc/ad7746.c +++ b/drivers/staging/iio/cdc/ad7746.c @@ -45,10 +45,10 @@ #define AD7746_STATUS_RDYCAP BIT(0) /* Capacitive Channel Setup Register Bit Designations (AD7746_REG_CAP_SETUP) */ -#define AD7746_CAPSETUP_CAPEN (1 << 7) -#define AD7746_CAPSETUP_CIN2 (1 << 6) /* AD7746 only */ -#define AD7746_CAPSETUP_CAPDIFF (1 << 5) -#define AD7746_CAPSETUP_CACHOP (1 << 0) +#define AD7746_CAPSETUP_CAPEN (BIT(7)) +#define AD7746_CAPSETUP_CIN2 (BIT(6)) /* AD7746 only */ +#define AD7746_CAPSETUP_CAPDIFF (BIT(5)) +#define AD7746_CAPSETUP_CACHOP (BIT(0)) /* Voltage/Temperature Setup Register Bit Designations (AD7746_REG_VT_SETUP) */ #define AD7746_VTSETUP_VTEN (1 << 7) @@ -56,9 +56,9 @@ #define AD7746_VTSETUP_VTMD_EXT_TEMP (1 << 5) #define AD7746_VTSETUP_VTMD_VDD_MON (2 << 5) #define AD7746_VTSETUP_VTMD_EXT_VIN (3 << 5) -#define AD7746_VTSETUP_EXTREF (1 << 4) -#define AD7746_VTSETUP_VTSHORT (1 << 1) -#define AD7746_VTSETUP_VTCHOP (1 << 0) +#define AD7746_VTSETUP_EXTREF (BIT(4)) +#define AD7746_VTSETUP_VTSHORT (BIT(1)) +#define AD7746_VTSETUP_VTCHOP (BIT(0)) /* Excitation Setup Register Bit Designations (AD7746_REG_EXC_SETUP) */ #define AD7746_EXCSETUP_CLKCTRL BIT(7) @@ -82,7 +82,7 @@ #define AD7746_CONF_MODE_GAIN_CAL (6 << 0) /* CAPDAC Register Bit Designations (AD7746_REG_CAPDACx) */ -#define AD7746_CAPDAC_DACEN (1 << 7) +#define AD7746_CAPDAC_DACEN (BIT(7)) #define AD7746_CAPDAC_DACP(x) ((x) & 0x7F) /* -- cgit v1.2.3 From d62bb1ad0532c8c31f79d83b91d4b06a512b8408 Mon Sep 17 00:00:00 2001 From: simran singhal Date: Mon, 20 Mar 2017 20:45:05 +0530 Subject: staging: ade7754: Clean up #includes Alphabetize and separate kernel and subsystem headers. Signed-off-by: simran singhal Signed-off-by: Jonathan Cameron --- drivers/staging/iio/meter/ade7754.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/iio/meter/ade7754.c b/drivers/staging/iio/meter/ade7754.c index c8d2d4c24e9d..6d8eb13f143d 100644 --- a/drivers/staging/iio/meter/ade7754.c +++ b/drivers/staging/iio/meter/ade7754.c @@ -6,18 +6,17 @@ * Licensed under the GPL-2 or later. */ -#include -#include #include -#include #include +#include +#include #include +#include +#include +#include #include #include #include -#include -#include - #include #include #include "meter.h" -- cgit v1.2.3 From 64822be434d69b226503f9d319f7a57bdbce2609 Mon Sep 17 00:00:00 2001 From: simran singhal Date: Tue, 21 Mar 2017 23:33:55 +0530 Subject: staging: iio: ade7753: Remove trailing whitespaces This patch removes trailing whitespaces in order to follow the Linux coding style. Signed-off-by: simran singhal Signed-off-by: Jonathan Cameron --- drivers/staging/iio/meter/ade7753.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/iio/meter/ade7753.c b/drivers/staging/iio/meter/ade7753.c index dfd8b71d6a41..b71fbd313778 100644 --- a/drivers/staging/iio/meter/ade7753.c +++ b/drivers/staging/iio/meter/ade7753.c @@ -83,10 +83,10 @@ * @buf_lock: mutex to protect tx and rx **/ struct ade7753_state { - struct spi_device *us; - struct mutex buf_lock; - u8 tx[ADE7753_MAX_TX] ____cacheline_aligned; - u8 rx[ADE7753_MAX_RX]; + struct spi_device *us; + struct mutex buf_lock; + u8 tx[ADE7753_MAX_TX] ____cacheline_aligned; + u8 rx[ADE7753_MAX_RX]; }; static int ade7753_spi_write_reg_8(struct device *dev, -- cgit v1.2.3 From 755d0da465fb870d2972ed19ef32c9da2498e3a1 Mon Sep 17 00:00:00 2001 From: Arushi Singhal Date: Tue, 21 Mar 2017 01:21:34 +0530 Subject: staging: ad7606: Replace mlock with driver private lock The IIO subsystem is redefining iio_dev->mlock to be used by the IIO core only for protecting device operating mode changes. ie. Changes between INDIO_DIRECT_MODE, INDIO_BUFFER_* modes. In this driver, mlock was being used to protect hardware state changes. Replace it with a lock in the devices global data. Signed-off-by: Arushi Singhal Signed-off-by: Jonathan Cameron --- drivers/staging/iio/adc/ad7606.c | 9 +++++---- drivers/staging/iio/adc/ad7606.h | 3 +++ 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/iio/adc/ad7606.c b/drivers/staging/iio/adc/ad7606.c index 9dbfa64b1e53..18f5f139117e 100644 --- a/drivers/staging/iio/adc/ad7606.c +++ b/drivers/staging/iio/adc/ad7606.c @@ -208,7 +208,7 @@ static int ad7606_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_SCALE: ret = -EINVAL; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); for (i = 0; i < ARRAY_SIZE(scale_avail); i++) if (val2 == scale_avail[i][1]) { gpiod_set_value(st->gpio_range, i); @@ -217,7 +217,7 @@ static int ad7606_write_raw(struct iio_dev *indio_dev, ret = 0; break; } - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return ret; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: @@ -231,11 +231,11 @@ static int ad7606_write_raw(struct iio_dev *indio_dev, values[1] = (ret >> 1) & 1; values[2] = (ret >> 2) & 1; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc, values); st->oversampling = val; - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return 0; default: @@ -413,6 +413,7 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, st = iio_priv(indio_dev); st->dev = dev; + mutex_init(&st->lock); st->bops = bops; st->base_address = base_address; /* tied to logic low, analog input range is +/- 5V */ diff --git a/drivers/staging/iio/adc/ad7606.h b/drivers/staging/iio/adc/ad7606.h index 746f9553d2ba..acaed8d5379c 100644 --- a/drivers/staging/iio/adc/ad7606.h +++ b/drivers/staging/iio/adc/ad7606.h @@ -14,6 +14,7 @@ * @name: identification string for chip * @channels: channel specification * @num_channels: number of channels + * @lock protect sensor state */ struct ad7606_chip_info { @@ -23,6 +24,7 @@ struct ad7606_chip_info { /** * struct ad7606_state - driver instance specific data + * @lock protect sensor state */ struct ad7606_state { @@ -37,6 +39,7 @@ struct ad7606_state { bool done; void __iomem *base_address; + struct mutex lock; /* protect sensor state */ struct gpio_desc *gpio_convst; struct gpio_desc *gpio_reset; struct gpio_desc *gpio_range; -- cgit v1.2.3 From fb4d75a9db652650c1813e989db2985f64dd8867 Mon Sep 17 00:00:00 2001 From: Narcisa Ana Maria Vasile Date: Mon, 20 Mar 2017 14:17:50 +0200 Subject: staging: iio: adt7136: Remove unnecessary blank lines This was reported by checkpatch.pl Signed-off-by: Narcisa Ana Maria Vasile Signed-off-by: Jonathan Cameron --- drivers/staging/iio/addac/adt7316.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/iio/addac/adt7316.c b/drivers/staging/iio/addac/adt7316.c index aa251c245981..872fac6f29e1 100644 --- a/drivers/staging/iio/addac/adt7316.c +++ b/drivers/staging/iio/addac/adt7316.c @@ -244,7 +244,6 @@ static ssize_t _adt7316_store_enabled(struct adt7316_chip_info *chip, chip->config1 = config1; return ret; - } static ssize_t adt7316_store_enabled(struct device *dev, @@ -434,7 +433,6 @@ static ssize_t adt7316_store_ad_channel(struct device *dev, config2 = chip->config2 & (~ADT7316_AD_SINGLE_CH_MASK); } - config2 |= data; ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG2, config2); @@ -725,7 +723,6 @@ static IIO_DEVICE_ATTR(AIN_internal_Vref, 0644, adt7316_store_AIN_internal_Vref, 0); - static ssize_t adt7316_show_enable_prop_DACA(struct device *dev, struct device_attribute *attr, char *buf) @@ -925,7 +922,6 @@ static ssize_t adt7316_show_all_DAC_update_modes(struct device *dev, static IIO_DEVICE_ATTR(all_DAC_update_modes, 0444, adt7316_show_all_DAC_update_modes, NULL, 0); - static ssize_t adt7316_store_update_DAC(struct device *dev, struct device_attribute *attr, const char *buf, -- cgit v1.2.3 From 6602292e1d30498981177fce6a24aff4a81bce39 Mon Sep 17 00:00:00 2001 From: Narcisa Ana Maria Vasile Date: Mon, 20 Mar 2017 14:18:03 +0200 Subject: staging: iio: Add blank lines after function declarations This was reported by checkpatch.pl: CHECK: Please use a blank line after function/struct/union/enum declarations Signed-off-by: Narcisa Ana Maria Vasile Signed-off-by: Jonathan Cameron --- drivers/staging/iio/addac/adt7316.c | 1 + drivers/staging/iio/cdc/ad7152.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/staging/iio/addac/adt7316.c b/drivers/staging/iio/addac/adt7316.c index 872fac6f29e1..b2bce26499f5 100644 --- a/drivers/staging/iio/addac/adt7316.c +++ b/drivers/staging/iio/addac/adt7316.c @@ -1863,6 +1863,7 @@ static ssize_t adt7316_set_int_mask(struct device *dev, return len; } + static inline ssize_t adt7316_show_ad_bound(struct device *dev, struct device_attribute *attr, char *buf) diff --git a/drivers/staging/iio/cdc/ad7152.c b/drivers/staging/iio/cdc/ad7152.c index ce0dce85929b..dc6ecd824365 100644 --- a/drivers/staging/iio/cdc/ad7152.c +++ b/drivers/staging/iio/cdc/ad7152.c @@ -244,6 +244,7 @@ static int ad7152_write_raw_samp_freq(struct device *dev, int val) return ret; } + static int ad7152_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, -- cgit v1.2.3 From 745c6dad490d9ec99f2f1cc3ec4b38afa4f5a8ed Mon Sep 17 00:00:00 2001 From: Narcisa Ana Maria Vasile Date: Mon, 20 Mar 2017 14:18:26 +0200 Subject: staging: iio: cdc: ad7746: Fix alignment with paranthesis This was reported by checkpatch.pl: CHECK: Alignment should match open parenthesis Signed-off-by: Narcisa Ana Maria Vasile Signed-off-by: Jonathan Cameron --- drivers/staging/iio/cdc/ad7746.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c index 2d8397b11b19..b42539e8fb55 100644 --- a/drivers/staging/iio/cdc/ad7746.c +++ b/drivers/staging/iio/cdc/ad7746.c @@ -547,7 +547,7 @@ static int ad7746_read_raw(struct iio_dev *indio_dev, regval = chip->config | AD7746_CONF_MODE_SINGLE_CONV; ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG, - regval); + regval); if (ret < 0) goto out; -- cgit v1.2.3 From 66893ea3757ceb2a95b57ac0fec6632003155f5b Mon Sep 17 00:00:00 2001 From: Gargi Sharma Date: Mon, 20 Mar 2017 14:00:42 +0530 Subject: staging: adis16203: Remove locking on raw reads of IIO_CHAN_INFO_CALIBBIAS adis16203_read_raw does not need lock for IIO_CHAN_INFO_CALIBBIAS since adis_read_reg_16 is already atomic and nothing else needs to be protected. Signed-off-by: Gargi Sharma Signed-off-by: Jonathan Cameron --- drivers/staging/iio/accel/adis16203.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/iio/accel/adis16203.c b/drivers/staging/iio/accel/adis16203.c index b59755aedd8b..faa2869b5431 100644 --- a/drivers/staging/iio/accel/adis16203.c +++ b/drivers/staging/iio/accel/adis16203.c @@ -203,17 +203,14 @@ static int adis16203_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_CALIBBIAS: bits = 14; - mutex_lock(&indio_dev->mlock); addr = adis16203_addresses[chan->scan_index]; ret = adis_read_reg_16(st, addr, &val16); if (ret) { - mutex_unlock(&indio_dev->mlock); return ret; } val16 &= (1 << bits) - 1; val16 = (s16)(val16 << (16 - bits)) >> (16 - bits); *val = val16; - mutex_unlock(&indio_dev->mlock); return IIO_VAL_INT; default: return -EINVAL; -- cgit v1.2.3 From 48ba7c3c0b37363d9ddca3354ca503ad721e14dc Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sat, 18 Mar 2017 19:38:19 +0100 Subject: iio: adc: meson-saradc: add calibration This patch adds calibration for the Meson SAR ADC. Points 25% vref and 75% vref are used for calibration. It uses a simple linear calibration function: SCALE * val + BIAS Successfully tested on a Odroid C2. Signed-off-by: Heiner Kallweit Tested-by: Martin Blumenstingl Reviewed-by: Martin Blumenstingl Signed-off-by: Jonathan Cameron --- drivers/iio/adc/meson_saradc.c | 77 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index cde9ca7a01b8..dd4190b50df6 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -166,6 +166,8 @@ #define MESON_SAR_ADC_MAX_FIFO_SIZE 32 #define MESON_SAR_ADC_TIMEOUT 100 /* ms */ +/* for use with IIO_VAL_INT_PLUS_MICRO */ +#define MILLION 1000000 #define MESON_SAR_ADC_CHAN(_chan) { \ .type = IIO_VOLTAGE, \ @@ -173,7 +175,9 @@ .channel = _chan, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_AVERAGE_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS) | \ + BIT(IIO_CHAN_INFO_CALIBSCALE), \ .datasheet_name = "SAR_ADC_CH"#_chan, \ } @@ -233,6 +237,8 @@ struct meson_sar_adc_priv { struct clk *adc_div_clk; struct clk_divider clk_div; struct completion done; + int calibbias; + int calibscale; }; static const struct regmap_config meson_sar_adc_regmap_config = { @@ -252,6 +258,17 @@ static unsigned int meson_sar_adc_get_fifo_count(struct iio_dev *indio_dev) return FIELD_GET(MESON_SAR_ADC_REG0_FIFO_COUNT_MASK, regval); } +static int meson_sar_adc_calib_val(struct iio_dev *indio_dev, int val) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int tmp; + + /* use val_calib = scale * val_raw + offset calibration function */ + tmp = div_s64((s64)val * priv->calibscale, MILLION) + priv->calibbias; + + return clamp(tmp, 0, (1 << priv->data->resolution) - 1); +} + static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev) { struct meson_sar_adc_priv *priv = iio_priv(indio_dev); @@ -302,7 +319,7 @@ static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev, fifo_val = FIELD_GET(MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK, regval); fifo_val &= GENMASK(priv->data->resolution - 1, 0); - *val = fifo_val; + *val = meson_sar_adc_calib_val(indio_dev, fifo_val); return 0; } @@ -527,6 +544,15 @@ static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev, *val2 = priv->data->resolution; return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_CALIBBIAS: + *val = priv->calibbias; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_CALIBSCALE: + *val = priv->calibscale / MILLION; + *val2 = priv->calibscale % MILLION; + return IIO_VAL_INT_PLUS_MICRO; + default: return -EINVAL; } @@ -762,6 +788,47 @@ static irqreturn_t meson_sar_adc_irq(int irq, void *data) return IRQ_HANDLED; } +static int meson_sar_adc_calib(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int ret, nominal0, nominal1, value0, value1; + + /* use points 25% and 75% for calibration */ + nominal0 = (1 << priv->data->resolution) / 4; + nominal1 = (1 << priv->data->resolution) * 3 / 4; + + meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_DIV4); + usleep_range(10, 20); + ret = meson_sar_adc_get_sample(indio_dev, + &meson_sar_adc_iio_channels[7], + MEAN_AVERAGING, EIGHT_SAMPLES, &value0); + if (ret < 0) + goto out; + + meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_MUL3_DIV4); + usleep_range(10, 20); + ret = meson_sar_adc_get_sample(indio_dev, + &meson_sar_adc_iio_channels[7], + MEAN_AVERAGING, EIGHT_SAMPLES, &value1); + if (ret < 0) + goto out; + + if (value1 <= value0) { + ret = -EINVAL; + goto out; + } + + priv->calibscale = div_s64((nominal1 - nominal0) * (s64)MILLION, + value1 - value0); + priv->calibbias = nominal0 - div_s64((s64)value0 * priv->calibscale, + MILLION); + ret = 0; +out: + meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_CH7_INPUT); + + return ret; +} + static const struct iio_info meson_sar_adc_iio_info = { .read_raw = meson_sar_adc_iio_info_read_raw, .driver_module = THIS_MODULE, @@ -901,6 +968,8 @@ static int meson_sar_adc_probe(struct platform_device *pdev) return PTR_ERR(priv->vref); } + priv->calibscale = MILLION; + ret = meson_sar_adc_init(indio_dev); if (ret) goto err; @@ -909,6 +978,10 @@ static int meson_sar_adc_probe(struct platform_device *pdev) if (ret) goto err; + ret = meson_sar_adc_calib(indio_dev); + if (ret) + dev_warn(&pdev->dev, "calibration failed\n"); + platform_set_drvdata(pdev, indio_dev); ret = iio_device_register(indio_dev); -- cgit v1.2.3 From 754434c445dafe79d55304a698c231ecbcc046d7 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Fri, 24 Mar 2017 18:44:03 +0100 Subject: iio: cros_ec_sensors: Use devm to setup the triggered buffer. Use resourced managed function devm_iio_triggered_buffer_setup to make error path simpler and be able to get rid of the remove function. Signed-off-by: Enric Balletbo i Serra Signed-off-by: Jonathan Cameron --- .../iio/common/cros_ec_sensors/cros_ec_sensors.c | 26 +++------------------- 1 file changed, 3 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c index d6c372bb433b..cbf6288ad174 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c @@ -267,31 +267,12 @@ static int cros_ec_sensors_probe(struct platform_device *pdev) else state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd; - ret = iio_triggered_buffer_setup(indio_dev, NULL, - cros_ec_sensors_capture, NULL); + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + cros_ec_sensors_capture, NULL); if (ret) return ret; - ret = iio_device_register(indio_dev); - if (ret) - goto error_uninit_buffer; - - return 0; - -error_uninit_buffer: - iio_triggered_buffer_cleanup(indio_dev); - - return ret; -} - -static int cros_ec_sensors_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - - iio_device_unregister(indio_dev); - iio_triggered_buffer_cleanup(indio_dev); - - return 0; + return devm_iio_device_register(dev, indio_dev); } static const struct platform_device_id cros_ec_sensors_ids[] = { @@ -313,7 +294,6 @@ static struct platform_driver cros_ec_sensors_platform_driver = { .name = "cros-ec-sensors", }, .probe = cros_ec_sensors_probe, - .remove = cros_ec_sensors_remove, .id_table = cros_ec_sensors_ids, }; module_platform_driver(cros_ec_sensors_platform_driver); -- cgit v1.2.3 From e29e7b3236672d25d693c23dcf6bc9f1afc25088 Mon Sep 17 00:00:00 2001 From: Arushi Singhal Date: Fri, 24 Mar 2017 19:25:33 +0530 Subject: staging: iio: Remove extra Parenthesis. Remove the extra parenthesis remove the checkpatch issue. Signed-off-by: Arushi Singhal Signed-off-by: Jonathan Cameron --- drivers/staging/iio/cdc/ad7746.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c index b42539e8fb55..cdcb4fccf3fe 100644 --- a/drivers/staging/iio/cdc/ad7746.c +++ b/drivers/staging/iio/cdc/ad7746.c @@ -45,10 +45,10 @@ #define AD7746_STATUS_RDYCAP BIT(0) /* Capacitive Channel Setup Register Bit Designations (AD7746_REG_CAP_SETUP) */ -#define AD7746_CAPSETUP_CAPEN (BIT(7)) -#define AD7746_CAPSETUP_CIN2 (BIT(6)) /* AD7746 only */ -#define AD7746_CAPSETUP_CAPDIFF (BIT(5)) -#define AD7746_CAPSETUP_CACHOP (BIT(0)) +#define AD7746_CAPSETUP_CAPEN BIT(7) +#define AD7746_CAPSETUP_CIN2 BIT(6) /* AD7746 only */ +#define AD7746_CAPSETUP_CAPDIFF BIT(5) +#define AD7746_CAPSETUP_CACHOP BIT(0) /* Voltage/Temperature Setup Register Bit Designations (AD7746_REG_VT_SETUP) */ #define AD7746_VTSETUP_VTEN (1 << 7) @@ -56,9 +56,9 @@ #define AD7746_VTSETUP_VTMD_EXT_TEMP (1 << 5) #define AD7746_VTSETUP_VTMD_VDD_MON (2 << 5) #define AD7746_VTSETUP_VTMD_EXT_VIN (3 << 5) -#define AD7746_VTSETUP_EXTREF (BIT(4)) -#define AD7746_VTSETUP_VTSHORT (BIT(1)) -#define AD7746_VTSETUP_VTCHOP (BIT(0)) +#define AD7746_VTSETUP_EXTREF BIT(4) +#define AD7746_VTSETUP_VTSHORT BIT(1) +#define AD7746_VTSETUP_VTCHOP BIT(0) /* Excitation Setup Register Bit Designations (AD7746_REG_EXC_SETUP) */ #define AD7746_EXCSETUP_CLKCTRL BIT(7) @@ -82,7 +82,7 @@ #define AD7746_CONF_MODE_GAIN_CAL (6 << 0) /* CAPDAC Register Bit Designations (AD7746_REG_CAPDACx) */ -#define AD7746_CAPDAC_DACEN (BIT(7)) +#define AD7746_CAPDAC_DACEN BIT(7) #define AD7746_CAPDAC_DACP(x) ((x) & 0x7F) /* -- cgit v1.2.3 From 5e7f47e495ad3657e1a7264bf20fb363607161e6 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Sun, 19 Mar 2017 20:23:12 +0530 Subject: iio:light: Add support for STMicro VL6180 sensor This patch adds support for STMicro VL6180 - ALS, range and proximity sensor. Sensor is capable of measuring the light intensity as well as object distance using TOF (Time of Flight) technology. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Peter Meerwald-Stadler Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/light/vl6180.txt | 15 + drivers/iio/light/Kconfig | 10 + drivers/iio/light/Makefile | 1 + drivers/iio/light/vl6180.c | 543 +++++++++++++++++++++ 4 files changed, 569 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/light/vl6180.txt create mode 100644 drivers/iio/light/vl6180.c (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/iio/light/vl6180.txt b/Documentation/devicetree/bindings/iio/light/vl6180.txt new file mode 100644 index 000000000000..2c52952715a0 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/vl6180.txt @@ -0,0 +1,15 @@ +STMicro VL6180 - ALS, range and proximity sensor + +Link to datasheet: http://www.st.com/resource/en/datasheet/vl6180x.pdf + +Required properties: + + -compatible: should be "st,vl6180" + -reg: the I2C address of the sensor + +Example: + +vl6180@29 { + compatible = "st,vl6180"; + reg = <0x29>; +}; diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 2afcbacc0f7e..33e755d8d825 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -405,4 +405,14 @@ config VEML6070 To compile this driver as a module, choose M here: the module will be called veml6070. +config VL6180 + tristate "VL6180 ALS, range and proximity sensor" + depends on I2C + help + Say Y here if you want to build a driver for the STMicroelectronics + VL6180 combined ambient light, range and proximity sensor. + + To compile this driver as a module, choose M here: the + module will be called vl6180. + endmenu diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index edfd69b6dc6d..681363c2b298 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -38,3 +38,4 @@ obj-$(CONFIG_TSL4531) += tsl4531.o obj-$(CONFIG_US5182D) += us5182d.o obj-$(CONFIG_VCNL4000) += vcnl4000.o obj-$(CONFIG_VEML6070) += veml6070.o +obj-$(CONFIG_VL6180) += vl6180.o diff --git a/drivers/iio/light/vl6180.c b/drivers/iio/light/vl6180.c new file mode 100644 index 000000000000..6e25b724d941 --- /dev/null +++ b/drivers/iio/light/vl6180.c @@ -0,0 +1,543 @@ +/* + * vl6180.c - Support for STMicroelectronics VL6180 ALS, range and proximity + * sensor + * + * Copyright 2017 Peter Meerwald-Stadler + * Copyright 2017 Manivannan Sadhasivam + * + * 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 + * directory of this archive for more details. + * + * IIO driver for VL6180 (7-bit I2C slave address 0x29) + * + * Range: 0 to 100mm + * ALS: < 1 Lux up to 100 kLux + * IR: 850nm + * + * TODO: irq, threshold events, continuous mode, hardware buffer + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define VL6180_DRV_NAME "vl6180" + +/* Device identification register and value */ +#define VL6180_MODEL_ID 0x000 +#define VL6180_MODEL_ID_VAL 0xb4 + +/* Configuration registers */ +#define VL6180_INTR_CONFIG 0x014 +#define VL6180_INTR_CLEAR 0x015 +#define VL6180_OUT_OF_RESET 0x016 +#define VL6180_HOLD 0x017 +#define VL6180_RANGE_START 0x018 +#define VL6180_ALS_START 0x038 +#define VL6180_ALS_GAIN 0x03f +#define VL6180_ALS_IT 0x040 + +/* Status registers */ +#define VL6180_RANGE_STATUS 0x04d +#define VL6180_ALS_STATUS 0x04e +#define VL6180_INTR_STATUS 0x04f + +/* Result value registers */ +#define VL6180_ALS_VALUE 0x050 +#define VL6180_RANGE_VALUE 0x062 +#define VL6180_RANGE_RATE 0x066 + +/* bits of the RANGE_START and ALS_START register */ +#define VL6180_MODE_CONT BIT(1) /* continuous mode */ +#define VL6180_STARTSTOP BIT(0) /* start measurement, auto-reset */ + +/* bits of the INTR_STATUS and INTR_CONFIG register */ +#define VL6180_ALS_READY BIT(5) +#define VL6180_RANGE_READY BIT(2) + +/* bits of the INTR_CLEAR register */ +#define VL6180_CLEAR_ERROR BIT(2) +#define VL6180_CLEAR_ALS BIT(1) +#define VL6180_CLEAR_RANGE BIT(0) + +/* bits of the HOLD register */ +#define VL6180_HOLD_ON BIT(0) + +/* default value for the ALS_IT register */ +#define VL6180_ALS_IT_100 0x63 /* 100 ms */ + +/* values for the ALS_GAIN register */ +#define VL6180_ALS_GAIN_1 0x46 +#define VL6180_ALS_GAIN_1_25 0x45 +#define VL6180_ALS_GAIN_1_67 0x44 +#define VL6180_ALS_GAIN_2_5 0x43 +#define VL6180_ALS_GAIN_5 0x42 +#define VL6180_ALS_GAIN_10 0x41 +#define VL6180_ALS_GAIN_20 0x40 +#define VL6180_ALS_GAIN_40 0x47 + +struct vl6180_data { + struct i2c_client *client; + struct mutex lock; +}; + +enum { VL6180_ALS, VL6180_RANGE, VL6180_PROX }; + +/** + * struct vl6180_chan_regs - Registers for accessing channels + * @drdy_mask: Data ready bit in status register + * @start_reg: Conversion start register + * @value_reg: Result value register + * @word: Register word length + */ +struct vl6180_chan_regs { + u8 drdy_mask; + u16 start_reg, value_reg; + bool word; +}; + +static const struct vl6180_chan_regs vl6180_chan_regs_table[] = { + [VL6180_ALS] = { + .drdy_mask = VL6180_ALS_READY, + .start_reg = VL6180_ALS_START, + .value_reg = VL6180_ALS_VALUE, + .word = true, + }, + [VL6180_RANGE] = { + .drdy_mask = VL6180_RANGE_READY, + .start_reg = VL6180_RANGE_START, + .value_reg = VL6180_RANGE_VALUE, + .word = false, + }, + [VL6180_PROX] = { + .drdy_mask = VL6180_RANGE_READY, + .start_reg = VL6180_RANGE_START, + .value_reg = VL6180_RANGE_RATE, + .word = true, + }, +}; + +static int vl6180_read(struct i2c_client *client, u16 cmd, void *databuf, + u8 len) +{ + __be16 cmdbuf = cpu_to_be16(cmd); + struct i2c_msg msgs[2] = { + { .addr = client->addr, .len = sizeof(cmdbuf), .buf = (u8 *) &cmdbuf }, + { .addr = client->addr, .len = len, .buf = databuf, + .flags = I2C_M_RD } }; + int ret; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + dev_err(&client->dev, "failed reading register 0x%04x\n", cmd); + + return ret; +} + +static int vl6180_read_byte(struct i2c_client *client, u16 cmd) +{ + u8 data; + int ret; + + ret = vl6180_read(client, cmd, &data, sizeof(data)); + if (ret < 0) + return ret; + + return data; +} + +static int vl6180_read_word(struct i2c_client *client, u16 cmd) +{ + __be16 data; + int ret; + + ret = vl6180_read(client, cmd, &data, sizeof(data)); + if (ret < 0) + return ret; + + return be16_to_cpu(data); +} + +static int vl6180_write_byte(struct i2c_client *client, u16 cmd, u8 val) +{ + u8 buf[3]; + struct i2c_msg msgs[1] = { + { .addr = client->addr, .len = sizeof(buf), .buf = (u8 *) &buf } }; + int ret; + + buf[0] = cmd >> 8; + buf[1] = cmd & 0xff; + buf[2] = val; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) { + dev_err(&client->dev, "failed writing register 0x%04x\n", cmd); + return ret; + } + + return 0; +} + +static int vl6180_write_word(struct i2c_client *client, u16 cmd, u16 val) +{ + __be16 buf[2]; + struct i2c_msg msgs[1] = { + { .addr = client->addr, .len = sizeof(buf), .buf = (u8 *) &buf } }; + int ret; + + buf[0] = cpu_to_be16(cmd); + buf[1] = cpu_to_be16(val); + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) { + dev_err(&client->dev, "failed writing register 0x%04x\n", cmd); + return ret; + } + + return 0; +} + +static int vl6180_measure(struct vl6180_data *data, int addr) +{ + struct i2c_client *client = data->client; + int tries = 20, ret; + u16 value; + + mutex_lock(&data->lock); + /* Start single shot measurement */ + ret = vl6180_write_byte(client, + vl6180_chan_regs_table[addr].start_reg, VL6180_STARTSTOP); + if (ret < 0) + goto fail; + + while (tries--) { + ret = vl6180_read_byte(client, VL6180_INTR_STATUS); + if (ret < 0) + goto fail; + + if (ret & vl6180_chan_regs_table[addr].drdy_mask) + break; + msleep(20); + } + + if (tries < 0) { + ret = -EIO; + goto fail; + } + + /* Read result value from appropriate registers */ + ret = vl6180_chan_regs_table[addr].word ? + vl6180_read_word(client, vl6180_chan_regs_table[addr].value_reg) : + vl6180_read_byte(client, vl6180_chan_regs_table[addr].value_reg); + if (ret < 0) + goto fail; + value = ret; + + /* Clear the interrupt flag after data read */ + ret = vl6180_write_byte(client, VL6180_INTR_CLEAR, + VL6180_CLEAR_ERROR | VL6180_CLEAR_ALS | VL6180_CLEAR_RANGE); + if (ret < 0) + goto fail; + + ret = value; + +fail: + mutex_unlock(&data->lock); + + return ret; +} + +static const struct iio_chan_spec vl6180_channels[] = { + { + .type = IIO_LIGHT, + .address = VL6180_ALS, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_HARDWAREGAIN), + }, { + .type = IIO_DISTANCE, + .address = VL6180_RANGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + }, { + .type = IIO_PROXIMITY, + .address = VL6180_PROX, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + } +}; + +/* + * Columns 3 & 4 represent the same value in decimal and hex notations. + * Kept in order to avoid the datatype conversion while reading the + * hardware_gain. + */ +static const int vl6180_als_gain[8][4] = { + { 1, 0, 70, VL6180_ALS_GAIN_1 }, + { 1, 250000, 69, VL6180_ALS_GAIN_1_25 }, + { 1, 670000, 68, VL6180_ALS_GAIN_1_67 }, + { 2, 500000, 67, VL6180_ALS_GAIN_2_5 }, + { 5, 0, 66, VL6180_ALS_GAIN_5 }, + { 10, 0, 65, VL6180_ALS_GAIN_10 }, + { 20, 0, 64, VL6180_ALS_GAIN_20 }, + { 40, 0, 71, VL6180_ALS_GAIN_40 } +}; + +static int vl6180_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct vl6180_data *data = iio_priv(indio_dev); + int ret, i; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = vl6180_measure(data, chan->address); + if (ret < 0) + return ret; + *val = ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_INT_TIME: + ret = vl6180_read_word(data->client, VL6180_ALS_IT); + if (ret < 0) + return ret; + *val = 0; /* 1 count = 1ms (0 = 1ms) */ + *val2 = (ret + 1) * 1000; /* convert to seconds */ + + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_LIGHT: + *val = 0; /* one ALS count is 0.32 Lux */ + *val2 = 320000; + break; + case IIO_DISTANCE: + *val = 0; /* sensor reports mm, scale to meter */ + *val2 = 1000; + break; + default: + return -EINVAL; + } + + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_HARDWAREGAIN: + ret = vl6180_read_byte(data->client, VL6180_ALS_GAIN); + if (ret < 0) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(vl6180_als_gain); i++) { + if (ret == vl6180_als_gain[i][2]) { + *val = vl6180_als_gain[i][0]; + *val2 = vl6180_als_gain[i][1]; + } + } + + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static IIO_CONST_ATTR(als_gain_available, "1 1.25 1.67 2.5 5 10 20 40"); + +static struct attribute *vl6180_attributes[] = { + &iio_const_attr_als_gain_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group vl6180_attribute_group = { + .attrs = vl6180_attributes, +}; + +/* HOLD is needed before updating any config registers */ +static int vl6180_hold(struct vl6180_data *data, bool hold) +{ + return vl6180_write_byte(data->client, VL6180_HOLD, + hold ? VL6180_HOLD_ON : 0); +} + +static int vl6180_set_als_gain(struct vl6180_data *data, int val, int val2) +{ + int i, ret; + + for (i = 0; i < ARRAY_SIZE(vl6180_als_gain); i++) { + if (val == vl6180_als_gain[i][0] && + val2 == vl6180_als_gain[i][1]) { + mutex_lock(&data->lock); + ret = vl6180_hold(data, true); + if (ret < 0) + goto fail; + ret = vl6180_write_byte(data->client, VL6180_ALS_GAIN, + vl6180_als_gain[i][3]); +fail: + vl6180_hold(data, false); + mutex_unlock(&data->lock); + return ret; + } + } + + return -EINVAL; +} + +static int vl6180_set_it(struct vl6180_data *data, int val2) +{ + int ret; + + mutex_lock(&data->lock); + ret = vl6180_hold(data, true); + if (ret < 0) + goto fail; + ret = vl6180_write_word(data->client, VL6180_ALS_IT, + (val2 - 500) / 1000); /* write value in ms */ +fail: + vl6180_hold(data, false); + mutex_unlock(&data->lock); + + return ret; +} + +static int vl6180_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct vl6180_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + if (val != 0 || val2 < 500 || val2 >= 512500) + return -EINVAL; + + return vl6180_set_it(data, val2); + case IIO_CHAN_INFO_HARDWAREGAIN: + if (chan->type != IIO_LIGHT) + return -EINVAL; + + return vl6180_set_als_gain(data, val, val2); + default: + return -EINVAL; + } +} + +static const struct iio_info vl6180_info = { + .read_raw = vl6180_read_raw, + .write_raw = vl6180_write_raw, + .attrs = &vl6180_attribute_group, + .driver_module = THIS_MODULE, +}; + +static int vl6180_init(struct vl6180_data *data) +{ + struct i2c_client *client = data->client; + int ret; + + ret = vl6180_read_byte(client, VL6180_MODEL_ID); + if (ret < 0) + return ret; + + if (ret != VL6180_MODEL_ID_VAL) { + dev_err(&client->dev, "invalid model ID %02x\n", ret); + return -ENODEV; + } + + ret = vl6180_hold(data, true); + if (ret < 0) + return ret; + + ret = vl6180_read_byte(client, VL6180_OUT_OF_RESET); + if (ret < 0) + return ret; + + /* + * Detect false reset condition here. This bit is always set when the + * system comes out of reset. + */ + if (ret != 0x01) + dev_info(&client->dev, "device is not fresh out of reset\n"); + + /* Enable ALS and Range ready interrupts */ + ret = vl6180_write_byte(client, VL6180_INTR_CONFIG, + VL6180_ALS_READY | VL6180_RANGE_READY); + if (ret < 0) + return ret; + + /* ALS integration time: 100ms */ + ret = vl6180_write_word(client, VL6180_ALS_IT, VL6180_ALS_IT_100); + if (ret < 0) + return ret; + + /* ALS gain: 1 */ + ret = vl6180_write_byte(client, VL6180_ALS_GAIN, VL6180_ALS_GAIN_1); + if (ret < 0) + return ret; + + ret = vl6180_write_byte(client, VL6180_OUT_OF_RESET, 0x00); + if (ret < 0) + return ret; + + return vl6180_hold(data, false); +} + +static int vl6180_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct vl6180_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 = &vl6180_info; + indio_dev->channels = vl6180_channels; + indio_dev->num_channels = ARRAY_SIZE(vl6180_channels); + indio_dev->name = VL6180_DRV_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = vl6180_init(data); + if (ret < 0) + return ret; + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct of_device_id vl6180_of_match[] = { + { .compatible = "st,vl6180", }, + { }, +}; +MODULE_DEVICE_TABLE(of, vl6180_of_match); + +static const struct i2c_device_id vl6180_id[] = { + { "vl6180", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, vl6180_id); + +static struct i2c_driver vl6180_driver = { + .driver = { + .name = VL6180_DRV_NAME, + .of_match_table = of_match_ptr(vl6180_of_match), + }, + .probe = vl6180_probe, + .id_table = vl6180_id, +}; + +module_i2c_driver(vl6180_driver); + +MODULE_AUTHOR("Peter Meerwald-Stadler "); +MODULE_AUTHOR("Manivannan Sadhasivam "); +MODULE_DESCRIPTION("STMicro VL6180 ALS, range and proximity sensor driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From e0ad9b73e81a2dfb8cf8b375fefbb0891a8187a1 Mon Sep 17 00:00:00 2001 From: Song Hongyan Date: Mon, 20 Mar 2017 17:12:54 +0800 Subject: iio: hid-sensor-attributes: Fix gravity sensor scale value not right issue Scale value include two parts: unit conversion and exponent conversion. Add gravity unit convert table to fix gravity sensor scale value not right issue. Signed-off-by: Song Hongyan Acked-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/common/hid-sensors/hid-sensor-attributes.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c index 9338f94ce1dc..cfb2fdc3177f 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c @@ -38,6 +38,12 @@ static struct { {HID_USAGE_SENSOR_ACCEL_3D, HID_USAGE_SENSOR_UNITS_G, 9, 806650000}, + {HID_USAGE_SENSOR_GRAVITY_VECTOR, 0, 9, 806650000}, + {HID_USAGE_SENSOR_GRAVITY_VECTOR, + HID_USAGE_SENSOR_UNITS_METERS_PER_SEC_SQRD, 1, 0}, + {HID_USAGE_SENSOR_GRAVITY_VECTOR, + HID_USAGE_SENSOR_UNITS_G, 9, 806650000}, + {HID_USAGE_SENSOR_GYRO_3D, 0, 0, 17453293}, {HID_USAGE_SENSOR_GYRO_3D, HID_USAGE_SENSOR_UNITS_RADIANS_PER_SECOND, 1, 0}, -- cgit v1.2.3 From d7ed89d5aadf09f1060cd3a9cf07df17447c7392 Mon Sep 17 00:00:00 2001 From: Song Hongyan Date: Mon, 20 Mar 2017 22:28:45 +0800 Subject: iio: hid: Add humidity sensor support Environmental humidity sensor is a hid defined sensor, it shows raw humidity measurement of air. More information can be found in: http://www.usb.org/developers/hidpage/HUTRR39b.pdf According to IIO ABI definition, humidityrelative data output unit is milli percent. Add the unit convert from percent to milli percent. Signed-off-by: Song Hongyan Signed-off-by: Jonathan Cameron --- .../iio/common/hid-sensors/hid-sensor-attributes.c | 2 + drivers/iio/humidity/Kconfig | 14 + drivers/iio/humidity/Makefile | 3 + drivers/iio/humidity/hid-sensor-humidity.c | 315 +++++++++++++++++++++ include/linux/hid-sensor-ids.h | 4 + 5 files changed, 338 insertions(+) create mode 100644 drivers/iio/humidity/hid-sensor-humidity.c (limited to 'drivers') diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c index cfb2fdc3177f..81647cc64a7b 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c @@ -71,6 +71,8 @@ static struct { {HID_USAGE_SENSOR_TEMPERATURE, 0, 1000, 0}, {HID_USAGE_SENSOR_TEMPERATURE, HID_USAGE_SENSOR_UNITS_DEGREES, 1000, 0}, + + {HID_USAGE_SENSOR_HUMIDITY, 0, 1000, 0}, }; static int pow_10(unsigned power) diff --git a/drivers/iio/humidity/Kconfig b/drivers/iio/humidity/Kconfig index 912477d54be2..14b9ce453d9d 100644 --- a/drivers/iio/humidity/Kconfig +++ b/drivers/iio/humidity/Kconfig @@ -36,6 +36,20 @@ config HDC100X To compile this driver as a module, choose M here: the module will be called hdc100x. +config HID_SENSOR_HUMIDITY + tristate "HID Environmental humidity sensor" + depends on HID_SENSOR_HUB + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select HID_SENSOR_IIO_COMMON + select HID_SENSOR_IIO_TRIGGER + help + Say yes here to build support for the HID SENSOR + humidity driver + + To compile this driver as a module, choose M here: the module + will be called hid-sensor-humidity. + config HTS221 tristate "STMicroelectronics HTS221 sensor Driver" depends on (I2C || SPI) diff --git a/drivers/iio/humidity/Makefile b/drivers/iio/humidity/Makefile index a6850e47c100..be0dedeb8f3c 100644 --- a/drivers/iio/humidity/Makefile +++ b/drivers/iio/humidity/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_AM2315) += am2315.o obj-$(CONFIG_DHT11) += dht11.o obj-$(CONFIG_HDC100X) += hdc100x.o +obj-$(CONFIG_HID_SENSOR_HUMIDITY) += hid-sensor-humidity.o hts221-y := hts221_core.o \ hts221_buffer.o @@ -15,3 +16,5 @@ obj-$(CONFIG_HTS221_SPI) += hts221_spi.o obj-$(CONFIG_HTU21) += htu21.o obj-$(CONFIG_SI7005) += si7005.o obj-$(CONFIG_SI7020) += si7020.o + +ccflags-y += -I$(srctree)/drivers/iio/common/hid-sensors diff --git a/drivers/iio/humidity/hid-sensor-humidity.c b/drivers/iio/humidity/hid-sensor-humidity.c new file mode 100644 index 000000000000..6e09c1acfe51 --- /dev/null +++ b/drivers/iio/humidity/hid-sensor-humidity.c @@ -0,0 +1,315 @@ +/* + * HID Sensors Driver + * Copyright (c) 2017, 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hid-sensor-trigger.h" + +struct hid_humidity_state { + struct hid_sensor_common common_attributes; + struct hid_sensor_hub_attribute_info humidity_attr; + s32 humidity_data; + int scale_pre_decml; + int scale_post_decml; + int scale_precision; + int value_offset; +}; + +/* Channel definitions */ +static const struct iio_chan_spec humidity_channels[] = { + { + .type = IIO_HUMIDITYRELATIVE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_HYSTERESIS), + }, + IIO_CHAN_SOFT_TIMESTAMP(1) +}; + +/* Adjust channel real bits based on report descriptor */ +static void humidity_adjust_channel_bit_mask(struct iio_chan_spec *channels, + int channel, int size) +{ + channels[channel].scan_type.sign = 's'; + /* Real storage bits will change based on the report desc. */ + channels[channel].scan_type.realbits = size * 8; + /* Maximum size of a sample to capture is s32 */ + channels[channel].scan_type.storagebits = sizeof(s32) * 8; +} + +static int humidity_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct hid_humidity_state *humid_st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (chan->type != IIO_HUMIDITYRELATIVE) + return -EINVAL; + hid_sensor_power_state(&humid_st->common_attributes, true); + *val = sensor_hub_input_attr_get_raw_value( + humid_st->common_attributes.hsdev, + HID_USAGE_SENSOR_HUMIDITY, + HID_USAGE_SENSOR_ATMOSPHERIC_HUMIDITY, + humid_st->humidity_attr.report_id, + SENSOR_HUB_SYNC); + hid_sensor_power_state(&humid_st->common_attributes, false); + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = humid_st->scale_pre_decml; + *val2 = humid_st->scale_post_decml; + + return humid_st->scale_precision; + + case IIO_CHAN_INFO_OFFSET: + *val = humid_st->value_offset; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SAMP_FREQ: + return hid_sensor_read_samp_freq_value( + &humid_st->common_attributes, val, val2); + + case IIO_CHAN_INFO_HYSTERESIS: + return hid_sensor_read_raw_hyst_value( + &humid_st->common_attributes, val, val2); + + default: + return -EINVAL; + } +} + +static int humidity_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct hid_humidity_state *humid_st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + return hid_sensor_write_samp_freq_value( + &humid_st->common_attributes, val, val2); + + case IIO_CHAN_INFO_HYSTERESIS: + return hid_sensor_write_raw_hyst_value( + &humid_st->common_attributes, val, val2); + + default: + return -EINVAL; + } +} + +static const struct iio_info humidity_info = { + .driver_module = THIS_MODULE, + .read_raw = &humidity_read_raw, + .write_raw = &humidity_write_raw, +}; + +/* Callback handler to send event after all samples are received and captured */ +static int humidity_proc_event(struct hid_sensor_hub_device *hsdev, + unsigned int usage_id, void *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct hid_humidity_state *humid_st = iio_priv(indio_dev); + + if (atomic_read(&humid_st->common_attributes.data_ready)) + iio_push_to_buffers_with_timestamp(indio_dev, + &humid_st->humidity_data, + iio_get_time_ns(indio_dev)); + + return 0; +} + +/* Capture samples in local storage */ +static int humidity_capture_sample(struct hid_sensor_hub_device *hsdev, + unsigned int usage_id, size_t raw_len, + char *raw_data, void *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct hid_humidity_state *humid_st = iio_priv(indio_dev); + + switch (usage_id) { + case HID_USAGE_SENSOR_ATMOSPHERIC_HUMIDITY: + humid_st->humidity_data = *(s32 *)raw_data; + + return 0; + default: + return -EINVAL; + } +} + +/* Parse report which is specific to an usage id */ +static int humidity_parse_report(struct platform_device *pdev, + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + unsigned int usage_id, + struct hid_humidity_state *st) +{ + int ret; + + ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT, + usage_id, + HID_USAGE_SENSOR_ATMOSPHERIC_HUMIDITY, + &st->humidity_attr); + if (ret < 0) + return ret; + + humidity_adjust_channel_bit_mask(channels, 0, st->humidity_attr.size); + + st->scale_precision = hid_sensor_format_scale( + HID_USAGE_SENSOR_HUMIDITY, + &st->humidity_attr, + &st->scale_pre_decml, + &st->scale_post_decml); + + /* Set Sensitivity field ids, when there is no individual modifier */ + if (st->common_attributes.sensitivity.index < 0) + sensor_hub_input_get_attribute_info(hsdev, + HID_FEATURE_REPORT, usage_id, + HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | + HID_USAGE_SENSOR_ATMOSPHERIC_HUMIDITY, + &st->common_attributes.sensitivity); + + return ret; +} + +static struct hid_sensor_hub_callbacks humidity_callbacks = { + .send_event = &humidity_proc_event, + .capture_sample = &humidity_capture_sample, +}; + +/* Function to initialize the processing for usage id */ +static int hid_humidity_probe(struct platform_device *pdev) +{ + static const char *name = "humidity"; + struct iio_dev *indio_dev; + struct hid_humidity_state *humid_st; + struct iio_chan_spec *humid_chans; + struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*humid_st)); + if (!indio_dev) + return -ENOMEM; + + humid_st = iio_priv(indio_dev); + humid_st->common_attributes.hsdev = hsdev; + humid_st->common_attributes.pdev = pdev; + + ret = hid_sensor_parse_common_attributes(hsdev, + HID_USAGE_SENSOR_HUMIDITY, + &humid_st->common_attributes); + if (ret) + return ret; + + humid_chans = devm_kmemdup(&indio_dev->dev, humidity_channels, + sizeof(humidity_channels), GFP_KERNEL); + if (!humid_chans) + return -ENOMEM; + + ret = humidity_parse_report(pdev, hsdev, humid_chans, + HID_USAGE_SENSOR_HUMIDITY, humid_st); + if (ret) + return ret; + + indio_dev->channels = humid_chans; + indio_dev->num_channels = ARRAY_SIZE(humidity_channels); + indio_dev->dev.parent = &pdev->dev; + indio_dev->info = &humidity_info; + indio_dev->name = name; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = devm_iio_triggered_buffer_setup(&pdev->dev, indio_dev, + &iio_pollfunc_store_time, NULL, NULL); + if (ret) + return ret; + + atomic_set(&humid_st->common_attributes.data_ready, 0); + ret = hid_sensor_setup_trigger(indio_dev, name, + &humid_st->common_attributes); + if (ret) + return ret; + + platform_set_drvdata(pdev, indio_dev); + + humidity_callbacks.pdev = pdev; + ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_HUMIDITY, + &humidity_callbacks); + if (ret) + goto error_remove_trigger; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_remove_callback; + + return ret; + +error_remove_callback: + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_HUMIDITY); +error_remove_trigger: + hid_sensor_remove_trigger(&humid_st->common_attributes); + return ret; +} + +/* Function to deinitialize the processing for usage id */ +static int hid_humidity_remove(struct platform_device *pdev) +{ + struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev); + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct hid_humidity_state *humid_st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_HUMIDITY); + hid_sensor_remove_trigger(&humid_st->common_attributes); + + return 0; +} + +static const struct platform_device_id hid_humidity_ids[] = { + { + /* Format: HID-SENSOR-usage_id_in_hex_lowercase */ + .name = "HID-SENSOR-200032", + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, hid_humidity_ids); + +static struct platform_driver hid_humidity_platform_driver = { + .id_table = hid_humidity_ids, + .driver = { + .name = KBUILD_MODNAME, + .pm = &hid_sensor_pm_ops, + }, + .probe = hid_humidity_probe, + .remove = hid_humidity_remove, +}; +module_platform_driver(hid_humidity_platform_driver); + +MODULE_DESCRIPTION("HID Environmental humidity sensor"); +MODULE_AUTHOR("Song Hongyan "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/hid-sensor-ids.h b/include/linux/hid-sensor-ids.h index 46dd1f27d2f2..761f86242473 100644 --- a/include/linux/hid-sensor-ids.h +++ b/include/linux/hid-sensor-ids.h @@ -49,6 +49,10 @@ #define HID_USAGE_SENSOR_TEMPERATURE 0x200033 #define HID_USAGE_SENSOR_DATA_ENVIRONMENTAL_TEMPERATURE 0x200434 +/* humidity */ +#define HID_USAGE_SENSOR_HUMIDITY 0x200032 +#define HID_USAGE_SENSOR_ATMOSPHERIC_HUMIDITY 0x200433 + /* Gyro 3D: (200076) */ #define HID_USAGE_SENSOR_GYRO_3D 0x200076 #define HID_USAGE_SENSOR_DATA_ANGL_VELOCITY 0x200456 -- cgit v1.2.3 From cf6c77323a96fc40309cc8a4921ef206cccdd961 Mon Sep 17 00:00:00 2001 From: Eva Rachel Retuya Date: Mon, 20 Mar 2017 19:27:05 +0800 Subject: staging: iio: tsl2x7x_core: Fix standard deviation calculation Standard deviation is calculated as the square root of the variance where variance is the mean of sample_sum and length. Correct the computation of statP->stddev in accordance to the proper calculation. Fixes: 3c97c08b5735 ("staging: iio: add TAOS tsl2x7x driver") Reported-by: Abhiram Balasubramanian Signed-off-by: Eva Rachel Retuya Signed-off-by: Jonathan Cameron --- drivers/staging/iio/light/tsl2x7x_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/staging/iio/light/tsl2x7x_core.c b/drivers/staging/iio/light/tsl2x7x_core.c index ea15bc1c300c..197201a70d59 100644 --- a/drivers/staging/iio/light/tsl2x7x_core.c +++ b/drivers/staging/iio/light/tsl2x7x_core.c @@ -854,7 +854,7 @@ void tsl2x7x_prox_calculate(int *data, int length, tmp = data[i] - statP->mean; sample_sum += tmp * tmp; } - statP->stddev = int_sqrt((long)sample_sum) / length; + statP->stddev = int_sqrt((long)sample_sum / length); } /** -- cgit v1.2.3 From baecc573d8aa916685057918bf86568ac6f28fa3 Mon Sep 17 00:00:00 2001 From: Mark Stenglein Date: Thu, 23 Mar 2017 02:18:12 -0400 Subject: staging: iio: accel: remove unneeded braces around single statements Fixes three checkpatch warnings due to braces used when single statements are sufficient. Signed-off-by: Mark Stenglein Signed-off-by: Jonathan Cameron --- drivers/staging/iio/accel/adis16209.c | 3 +-- drivers/staging/iio/accel/adis16240.c | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/iio/accel/adis16209.c b/drivers/staging/iio/accel/adis16209.c index 52fa2e0511be..8485c024e3f5 100644 --- a/drivers/staging/iio/accel/adis16209.c +++ b/drivers/staging/iio/accel/adis16209.c @@ -255,9 +255,8 @@ static int adis16209_read_raw(struct iio_dev *indio_dev, } addr = adis16209_addresses[chan->scan_index][0]; ret = adis_read_reg_16(st, addr, &val16); - if (ret) { + if (ret) return ret; - } val16 &= (1 << bits) - 1; val16 = (s16)(val16 << (16 - bits)) >> (16 - bits); *val = val16; diff --git a/drivers/staging/iio/accel/adis16240.c b/drivers/staging/iio/accel/adis16240.c index 6e3c95c9c3f6..109cd94b5ac3 100644 --- a/drivers/staging/iio/accel/adis16240.c +++ b/drivers/staging/iio/accel/adis16240.c @@ -290,9 +290,8 @@ static int adis16240_read_raw(struct iio_dev *indio_dev, bits = 10; addr = adis16240_addresses[chan->scan_index][0]; ret = adis_read_reg_16(st, addr, &val16); - if (ret) { + if (ret) return ret; - } val16 &= (1 << bits) - 1; val16 = (s16)(val16 << (16 - bits)) >> (16 - bits); *val = val16; @@ -301,9 +300,8 @@ static int adis16240_read_raw(struct iio_dev *indio_dev, bits = 10; addr = adis16240_addresses[chan->scan_index][1]; ret = adis_read_reg_16(st, addr, &val16); - if (ret) { + if (ret) return ret; - } val16 &= (1 << bits) - 1; val16 = (s16)(val16 << (16 - bits)) >> (16 - bits); *val = val16; -- cgit v1.2.3 From 40dffd9974b369be886d5fec7407087d7d9edfa8 Mon Sep 17 00:00:00 2001 From: Olivier Leveque Date: Thu, 23 Mar 2017 14:18:09 +0100 Subject: staging: iio: adc: ad7280a: fix permission coding style issue Symbolic permissions 'S_IWUSR | S_IRUGO' are not preferred. Consider using octal permissions '0644'. Found running checkpatch. Signed-off-by: Olivier Leveque Signed-off-by: Jonathan Cameron --- drivers/staging/iio/adc/ad7280a.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/iio/adc/ad7280a.c b/drivers/staging/iio/adc/ad7280a.c index 6a85e284c8a4..d5ab83f0236d 100644 --- a/drivers/staging/iio/adc/ad7280a.c +++ b/drivers/staging/iio/adc/ad7280a.c @@ -560,7 +560,7 @@ static int ad7280_attr_init(struct ad7280_state *st) st->iio_attr[cnt].address = AD7280A_DEVADDR(dev) << 8 | ch; st->iio_attr[cnt].dev_attr.attr.mode = - S_IWUSR | S_IRUGO; + 0644; st->iio_attr[cnt].dev_attr.show = ad7280_show_balance_sw; st->iio_attr[cnt].dev_attr.store = @@ -577,7 +577,7 @@ static int ad7280_attr_init(struct ad7280_state *st) AD7280A_DEVADDR(dev) << 8 | (AD7280A_CB1_TIMER + ch); st->iio_attr[cnt].dev_attr.attr.mode = - S_IWUSR | S_IRUGO; + 0644; st->iio_attr[cnt].dev_attr.show = ad7280_show_balance_timer; st->iio_attr[cnt].dev_attr.store = @@ -746,26 +746,26 @@ out: static IIO_DEVICE_ATTR_NAMED(in_thresh_low_value, in_voltage-voltage_thresh_low_value, - S_IRUGO | S_IWUSR, + 0644, ad7280_read_channel_config, ad7280_write_channel_config, AD7280A_CELL_UNDERVOLTAGE); static IIO_DEVICE_ATTR_NAMED(in_thresh_high_value, in_voltage-voltage_thresh_high_value, - S_IRUGO | S_IWUSR, + 0644, ad7280_read_channel_config, ad7280_write_channel_config, AD7280A_CELL_OVERVOLTAGE); static IIO_DEVICE_ATTR(in_temp_thresh_low_value, - S_IRUGO | S_IWUSR, + 0644, ad7280_read_channel_config, ad7280_write_channel_config, AD7280A_AUX_ADC_UNDERVOLTAGE); static IIO_DEVICE_ATTR(in_temp_thresh_high_value, - S_IRUGO | S_IWUSR, + 0644, ad7280_read_channel_config, ad7280_write_channel_config, AD7280A_AUX_ADC_OVERVOLTAGE); -- cgit v1.2.3 From e36020fd69952bf42554bbeb2c0e98927a0c656b Mon Sep 17 00:00:00 2001 From: simran singhal Date: Wed, 29 Mar 2017 01:51:48 +0530 Subject: iio: dac: ad5504: constify attribute_group structures Check for attribute_group structures that are only stored in the event_attrs filed of iio_info structure. As the event_attrs field of iio_info structures is constant, so these attribute_group structures can also be declared constant. Done using coccinelle: @r1 disable optional_qualifier @ identifier i; position p; @@ static struct attribute_group i@p = {...}; @ok1@ identifier r1.i; position p; struct iio_info x; @@ x.event_attrs=&i@p; @bad@ position p!={r1.p,ok1.p}; identifier r1.i; @@ i@p @depends on !bad disable optional_qualifier@ identifier r1.i; @@ static +const struct attribute_group i={...}; @depends on !bad disable optional_qualifier@ identifier r1.i; @@ +const struct attribute_group i; File size before: text data bss dec hex filename 3046 360 0 3406 d4e drivers/iio/dac/ad5504.o File size after: text data bss dec hex filename 3110 296 0 3406 d4e drivers/iio/dac/ad5504.o Signed-off-by: simran singhal Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5504.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c index 788b3d6fd1cc..22a027de498a 100644 --- a/drivers/iio/dac/ad5504.c +++ b/drivers/iio/dac/ad5504.c @@ -212,7 +212,7 @@ static struct attribute *ad5504_ev_attributes[] = { NULL, }; -static struct attribute_group ad5504_ev_attribute_group = { +static const struct attribute_group ad5504_ev_attribute_group = { .attrs = ad5504_ev_attributes, }; -- cgit v1.2.3 From c94645b1b153e4e46599de0181cd517cd6d207d9 Mon Sep 17 00:00:00 2001 From: simran singhal Date: Wed, 29 Mar 2017 01:45:28 +0530 Subject: iio: adc: max1363: constify attribute_group structures Check for attribute_group structures that are only stored in the event_attrs filed of iio_info structure. As the event_attrs field of iio_info structures is constant, so these attribute_group structures can also be declared constant. Done using coccinelle: @r1 disable optional_qualifier @ identifier i; position p; @@ static struct attribute_group i@p = {...}; @ok1@ identifier r1.i; position p; struct iio_info x; @@ x.event_attrs=&i@p; @bad@ position p!={r1.p,ok1.p}; identifier r1.i; @@ i@p @depends on !bad disable optional_qualifier@ identifier r1.i; @@ static +const struct attribute_group i={...}; @depends on !bad disable optional_qualifier@ identifier r1.i; @@ +const struct attribute_group i; File size before: text data bss dec hex filename 36951 448 0 37399 9217 drivers/iio/adc/max1363.o File size after: text data bss dec hex filename 37015 384 0 37399 9217 drivers/iio/adc/max1363.o Signed-off-by: simran singhal Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1363.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index c6c12feb4a08..80eada4886b3 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -1007,7 +1007,7 @@ static struct attribute *max1363_event_attributes[] = { NULL, }; -static struct attribute_group max1363_event_attribute_group = { +static const struct attribute_group max1363_event_attribute_group = { .attrs = max1363_event_attributes, }; -- cgit v1.2.3 From 612dc0e2e8fbc4b9e0ddbc279b77f5baf4b726d4 Mon Sep 17 00:00:00 2001 From: simran singhal Date: Wed, 29 Mar 2017 01:37:45 +0530 Subject: iio: adc: ad799x: constify attribute_group structures Check for attribute_group structures that are only stored in the event_attrs filed of iio_info structure. As the event_attrs field of iio_info structures is constant, so these attribute_group structures can also be declared constant. Done using coccinelle: @r1 disable optional_qualifier @ identifier i; position p; @@ static struct attribute_group i@p = {...}; @ok1@ identifier r1.i; position p; struct iio_info x; @@ x.event_attrs=&i@p; @bad@ position p!={r1.p,ok1.p}; identifier r1.i; @@ i@p @depends on !bad disable optional_qualifier@ identifier r1.i; @@ static +const struct attribute_group i={...}; @depends on !bad disable optional_qualifier@ identifier r1.i; @@ +const struct attribute_group i; File size before: text data bss dec hex filename 26051 464 0 26515 6793 drivers/iio/adc/ad799x.o File size after: text data bss dec hex filename 26115 400 0 26515 6793 drivers/iio/adc/ad799x.o Signed-off-by: simran singhal Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad799x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c index 9704090b7908..22426ae4af97 100644 --- a/drivers/iio/adc/ad799x.c +++ b/drivers/iio/adc/ad799x.c @@ -520,7 +520,7 @@ static struct attribute *ad799x_event_attributes[] = { NULL, }; -static struct attribute_group ad799x_event_attrs_group = { +static const struct attribute_group ad799x_event_attrs_group = { .attrs = ad799x_event_attributes, }; -- cgit v1.2.3 From bb4787f520f2497ea2455bb7cd795ac96a474191 Mon Sep 17 00:00:00 2001 From: Arushi Singhal Date: Thu, 30 Mar 2017 18:16:03 +0530 Subject: iio: adc: replace comma with a semicolon Replace a comma between expression statements by a semicolon. This changes the semantics of the code, but given the current indentation appears to be what is intended. A simplified version of the Coccinelle semantic patch that performs this transformation is as follows: // @r@ expression e1,e2; @@ e1 -, +; e2; // Signed-off-by: Arushi Singhal Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max11100.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/adc/max11100.c b/drivers/iio/adc/max11100.c index 23c060e1b663..1180bcc22ff1 100644 --- a/drivers/iio/adc/max11100.c +++ b/drivers/iio/adc/max11100.c @@ -124,8 +124,8 @@ static int max11100_probe(struct spi_device *spi) indio_dev->name = "max11100"; indio_dev->info = &max11100_info; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = max11100_channels, - indio_dev->num_channels = ARRAY_SIZE(max11100_channels), + indio_dev->channels = max11100_channels; + indio_dev->num_channels = ARRAY_SIZE(max11100_channels); state->vref_reg = devm_regulator_get(&spi->dev, "vref"); if (IS_ERR(state->vref_reg)) -- cgit v1.2.3 From 5e13b6f0e7cc7e53466d1b62a6d0ccbd7632f71f Mon Sep 17 00:00:00 2001 From: Arushi Singhal Date: Thu, 30 Mar 2017 18:11:22 +0530 Subject: drivers: iio: chemical: replace comma with a semicolon Replace a comma between expression statements by a semicolon. This changes the semantics of the code, but given the current indentation appears to be what is intended. A simplified version of the Coccinelle semantic patch that performs this transformation is as follows: // @r@ expression e1,e2; @@ e1 -, +; e2; // Signed-off-by: Arushi Singhal Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/ams-iaq-core.c | 2 +- drivers/iio/chemical/vz89x.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/chemical/ams-iaq-core.c b/drivers/iio/chemical/ams-iaq-core.c index 41a8e6f2e31d..c948ad2ee9ad 100644 --- a/drivers/iio/chemical/ams-iaq-core.c +++ b/drivers/iio/chemical/ams-iaq-core.c @@ -163,7 +163,7 @@ static int ams_iaqcore_probe(struct i2c_client *client, mutex_init(&data->lock); indio_dev->dev.parent = &client->dev; - indio_dev->info = &ams_iaqcore_info, + indio_dev->info = &ams_iaqcore_info; indio_dev->name = dev_name(&client->dev); indio_dev->modes = INDIO_DIRECT_MODE; diff --git a/drivers/iio/chemical/vz89x.c b/drivers/iio/chemical/vz89x.c index 8e0e4415c161..f75eea6822f2 100644 --- a/drivers/iio/chemical/vz89x.c +++ b/drivers/iio/chemical/vz89x.c @@ -393,7 +393,7 @@ static int vz89x_probe(struct i2c_client *client, mutex_init(&data->lock); indio_dev->dev.parent = &client->dev; - indio_dev->info = &vz89x_info, + indio_dev->info = &vz89x_info; indio_dev->name = dev_name(&client->dev); indio_dev->modes = INDIO_DIRECT_MODE; -- cgit v1.2.3 From b71244b62be470327a59fe940587ccb0811cf8d7 Mon Sep 17 00:00:00 2001 From: Gargi Sharma Date: Thu, 30 Mar 2017 15:03:46 +0530 Subject: staging: iio: update locking method during frequency writes The driver needs to insure atomicity during frequency changes of bus and device. The iiodev->mlock as used was not doing that. Replace it with the drivers existing buffer lock and introduce an auxiliary spi_write() that does not hold the lock. Signed-off-by: Gargi Sharma Signed-off-by: Jonathan Cameron --- drivers/staging/iio/meter/ade7754.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/iio/meter/ade7754.c b/drivers/staging/iio/meter/ade7754.c index 6d8eb13f143d..32dc50341746 100644 --- a/drivers/staging/iio/meter/ade7754.c +++ b/drivers/staging/iio/meter/ade7754.c @@ -96,7 +96,7 @@ /** * struct ade7754_state - device instance specific data * @us: actual spi_device - * @buf_lock: mutex to protect tx and rx + * @buf_lock: mutex to protect tx, rx and write frequency * @tx: transmit buffer * @rx: receive buffer **/ @@ -107,17 +107,25 @@ struct ade7754_state { u8 rx[ADE7754_MAX_RX]; }; -static int ade7754_spi_write_reg_8(struct device *dev, u8 reg_address, u8 val) +/* Unlocked version of ade7754_spi_write_reg_8 function */ +static int __ade7754_spi_write_reg_8(struct device *dev, u8 reg_address, u8 val) { - int ret; struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct ade7754_state *st = iio_priv(indio_dev); - mutex_lock(&st->buf_lock); st->tx[0] = ADE7754_WRITE_REG(reg_address); st->tx[1] = val; + return spi_write(st->us, st->tx, 2); +} - ret = spi_write(st->us, st->tx, 2); +static int ade7754_spi_write_reg_8(struct device *dev, u8 reg_address, u8 val) +{ + int ret; + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ade7754_state *st = iio_priv(indio_dev); + + mutex_lock(&st->buf_lock); + ret = __ade7754_spi_write_reg_8(dev, reg_address, val); mutex_unlock(&st->buf_lock); return ret; @@ -512,7 +520,7 @@ static ssize_t ade7754_write_frequency(struct device *dev, if (!val) return -EINVAL; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->buf_lock); t = 26000 / val; if (t > 0) @@ -530,10 +538,10 @@ static ssize_t ade7754_write_frequency(struct device *dev, reg &= ~(3 << 3); reg |= t << 3; - ret = ade7754_spi_write_reg_8(dev, ADE7754_WAVMODE, reg); + ret = __ade7754_spi_write_reg_8(dev, ADE7754_WAVMODE, reg); out: - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->buf_lock); return ret ? ret : len; } -- cgit v1.2.3 From 25ec249632d505192789e15dcafed5a9cdb1283e Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 23 Mar 2017 20:38:42 -0700 Subject: iio: adc: cpcap: Add minimal support for CPCAP PMIC ADC On Motorola phones like droid 4 there is a custom CPCAP PMIC. This PMIC has ADCs that are used for battery charging and USB PHY VBUS and ID pin detection. Unfortunately the only documentation for this ADC seems to be the Motorola mapphone Linux kernel tree. I have tested that reading raw and scaled values works, but I have not used the timed sampling that the ADC seems to support. Let's add a minimal support for it so we can eventually provide IIO channels for the related battery charging and USB PHY drivers. Cc: devicetree@vger.kernel.org Cc: Marcel Partap Cc: Michael Scott Cc: Sebastian Reichel Signed-off-by: Tony Lindgren Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/cpcap-adc.txt | 18 + drivers/iio/adc/Kconfig | 11 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/cpcap-adc.c | 1007 ++++++++++++++++++++ 4 files changed, 1037 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/cpcap-adc.txt create mode 100644 drivers/iio/adc/cpcap-adc.c (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/iio/adc/cpcap-adc.txt b/Documentation/devicetree/bindings/iio/adc/cpcap-adc.txt new file mode 100644 index 000000000000..487ea966858e --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/cpcap-adc.txt @@ -0,0 +1,18 @@ +Motorola CPCAP PMIC ADC binding + +Required properties: +- compatible: Should be "motorola,cpcap-adc" or "motorola,mapphone-cpcap-adc" +- interrupt-parent: The interrupt controller +- interrupts: The interrupt number for the ADC device +- interrupt-names: Should be "adcdone" +- #io-channel-cells: Number of cells in an IIO specifier + +Example: + +cpcap_adc: adc { + compatible = "motorola,mapphone-cpcap-adc"; + interrupt-parent = <&cpcap>; + interrupts = <8 IRQ_TYPE_NONE>; + interrupt-names = "adcdone"; + #io-channel-cells = <1>; +}; diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 2268a6fb9865..b369a4397d90 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -195,6 +195,17 @@ config CC10001_ADC This driver can also be built as a module. If so, the module will be called cc10001_adc. +config CPCAP_ADC + tristate "Motorola CPCAP PMIC ADC driver" + depends on MFD_CPCAP + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Motorola CPCAP PMIC ADC. + + This driver can also be built as a module. If so, the module will be + called cpcap-adc. + config DA9150_GPADC tristate "Dialog DA9150 GPADC driver support" depends on MFD_DA9150 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 73dbe399f894..26ce71bdeb67 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_AXP288_ADC) += axp288_adc.o obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o +obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o diff --git a/drivers/iio/adc/cpcap-adc.c b/drivers/iio/adc/cpcap-adc.c new file mode 100644 index 000000000000..62d37f8725b8 --- /dev/null +++ b/drivers/iio/adc/cpcap-adc.c @@ -0,0 +1,1007 @@ +/* + * Copyright (C) 2017 Tony Lindgren + * + * Rewritten for Linux IIO framework with some code based on + * earlier driver found in the Motorola Linux kernel: + * + * Copyright (C) 2009-2010 Motorola, Inc. + * + * 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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Register CPCAP_REG_ADCC1 bits */ +#define CPCAP_BIT_ADEN_AUTO_CLR BIT(15) /* Currently unused */ +#define CPCAP_BIT_CAL_MODE BIT(14) /* Set with BIT_RAND0 */ +#define CPCAP_BIT_ADC_CLK_SEL1 BIT(13) /* Currently unused */ +#define CPCAP_BIT_ADC_CLK_SEL0 BIT(12) /* Currently unused */ +#define CPCAP_BIT_ATOX BIT(11) +#define CPCAP_BIT_ATO3 BIT(10) +#define CPCAP_BIT_ATO2 BIT(9) +#define CPCAP_BIT_ATO1 BIT(8) +#define CPCAP_BIT_ATO0 BIT(7) +#define CPCAP_BIT_ADA2 BIT(6) +#define CPCAP_BIT_ADA1 BIT(5) +#define CPCAP_BIT_ADA0 BIT(4) +#define CPCAP_BIT_AD_SEL1 BIT(3) /* Set for bank1 */ +#define CPCAP_BIT_RAND1 BIT(2) /* Set for channel 16 & 17 */ +#define CPCAP_BIT_RAND0 BIT(1) /* Set with CAL_MODE */ +#define CPCAP_BIT_ADEN BIT(0) /* Currently unused */ + +/* Register CPCAP_REG_ADCC2 bits */ +#define CPCAP_BIT_CAL_FACTOR_ENABLE BIT(15) /* Currently unused */ +#define CPCAP_BIT_BATDETB_EN BIT(14) /* Currently unused */ +#define CPCAP_BIT_ADTRIG_ONESHOT BIT(13) /* Set for !TIMING_IMM */ +#define CPCAP_BIT_ASC BIT(12) /* Set for TIMING_IMM */ +#define CPCAP_BIT_ATOX_PS_FACTOR BIT(11) +#define CPCAP_BIT_ADC_PS_FACTOR1 BIT(10) +#define CPCAP_BIT_ADC_PS_FACTOR0 BIT(9) +#define CPCAP_BIT_AD4_SELECT BIT(8) /* Currently unused */ +#define CPCAP_BIT_ADC_BUSY BIT(7) /* Currently unused */ +#define CPCAP_BIT_THERMBIAS_EN BIT(6) /* Currently unused */ +#define CPCAP_BIT_ADTRIG_DIS BIT(5) /* Disable interrupt */ +#define CPCAP_BIT_LIADC BIT(4) /* Currently unused */ +#define CPCAP_BIT_TS_REFEN BIT(3) /* Currently unused */ +#define CPCAP_BIT_TS_M2 BIT(2) /* Currently unused */ +#define CPCAP_BIT_TS_M1 BIT(1) /* Currently unused */ +#define CPCAP_BIT_TS_M0 BIT(0) /* Currently unused */ + +#define CPCAP_MAX_TEMP_LVL 27 +#define CPCAP_FOUR_POINT_TWO_ADC 801 +#define ST_ADC_CAL_CHRGI_HIGH_THRESHOLD 530 +#define ST_ADC_CAL_CHRGI_LOW_THRESHOLD 494 +#define ST_ADC_CAL_BATTI_HIGH_THRESHOLD 530 +#define ST_ADC_CAL_BATTI_LOW_THRESHOLD 494 +#define ST_ADC_CALIBRATE_DIFF_THRESHOLD 3 + +#define CPCAP_ADC_MAX_RETRIES 5 /* Calibration and quirk */ + +/** + * struct cpcap_adc_ato - timing settings for cpcap adc + * + * Unfortunately no cpcap documentation available, please document when + * using these. + */ +struct cpcap_adc_ato { + unsigned short ato_in; + unsigned short atox_in; + unsigned short adc_ps_factor_in; + unsigned short atox_ps_factor_in; + unsigned short ato_out; + unsigned short atox_out; + unsigned short adc_ps_factor_out; + unsigned short atox_ps_factor_out; +}; + +/** + * struct cpcap-adc - cpcap adc device driver data + * @reg: cpcap regmap + * @dev: struct device + * @vendor: cpcap vendor + * @irq: interrupt + * @lock: mutex + * @ato: request timings + * @wq_data_avail: work queue + * @done: work done + */ +struct cpcap_adc { + struct regmap *reg; + struct device *dev; + u16 vendor; + int irq; + struct mutex lock; /* ADC register access lock */ + const struct cpcap_adc_ato *ato; + wait_queue_head_t wq_data_avail; + bool done; +}; + +/** + * enum cpcap_adc_channel - cpcap adc channels + */ +enum cpcap_adc_channel { + /* Bank0 channels */ + CPCAP_ADC_AD0_BATTDETB, /* Battery detection */ + CPCAP_ADC_BATTP, /* Battery voltage */ + CPCAP_ADC_VBUS, /* USB VBUS voltage */ + CPCAP_ADC_AD3, /* Battery temperature when charging */ + CPCAP_ADC_BPLUS_AD4, /* Another battery or system voltage */ + CPCAP_ADC_CHG_ISENSE, /* Calibrated charge current */ + CPCAP_ADC_BATTI, /* Calibrated system current */ + CPCAP_ADC_USB_ID, /* USB OTG ID, unused on droid 4? */ + + /* Bank1 channels */ + CPCAP_ADC_AD8, /* Seems unused */ + CPCAP_ADC_AD9, /* Seems unused */ + CPCAP_ADC_LICELL, /* Maybe system voltage? Always 3V */ + CPCAP_ADC_HV_BATTP, /* Another battery detection? */ + CPCAP_ADC_TSX1_AD12, /* Seems unused, for touchscreen? */ + CPCAP_ADC_TSX2_AD13, /* Seems unused, for touchscreen? */ + CPCAP_ADC_TSY1_AD14, /* Seems unused, for touchscreen? */ + CPCAP_ADC_TSY2_AD15, /* Seems unused, for touchscreen? */ + + /* Remuxed channels using bank0 entries */ + CPCAP_ADC_BATTP_PI16, /* Alternative mux mode for BATTP */ + CPCAP_ADC_BATTI_PI17, /* Alternative mux mode for BATTI */ + + CPCAP_ADC_CHANNEL_NUM, +}; + +/** + * enum cpcap_adc_timing - cpcap adc timing options + * + * CPCAP_ADC_TIMING_IMM seems to be immediate with no timings. + * Please document when using. + */ +enum cpcap_adc_timing { + CPCAP_ADC_TIMING_IMM, + CPCAP_ADC_TIMING_IN, + CPCAP_ADC_TIMING_OUT, +}; + +/** + * struct cpcap_adc_phasing_tbl - cpcap phasing table + * @offset: offset in the phasing table + * @multiplier: multiplier in the phasing table + * @divider: divider in the phasing table + * @min: minimum value + * @max: maximum value + */ +struct cpcap_adc_phasing_tbl { + short offset; + unsigned short multiplier; + unsigned short divider; + short min; + short max; +}; + +/** + * struct cpcap_adc_conversion_tbl - cpcap conversion table + * @conv_type: conversion type + * @align_offset: align offset + * @conv_offset: conversion offset + * @cal_offset: calibration offset + * @multiplier: conversion multiplier + * @divider: conversion divider + */ +struct cpcap_adc_conversion_tbl { + enum iio_chan_info_enum conv_type; + int align_offset; + int conv_offset; + int cal_offset; + int multiplier; + int divider; +}; + +/** + * struct cpcap_adc_request - cpcap adc request + * @channel: request channel + * @phase_tbl: channel phasing table + * @conv_tbl: channel conversion table + * @bank_index: channel index within the bank + * @timing: timing settings + * @result: result + */ +struct cpcap_adc_request { + int channel; + const struct cpcap_adc_phasing_tbl *phase_tbl; + const struct cpcap_adc_conversion_tbl *conv_tbl; + int bank_index; + enum cpcap_adc_timing timing; + int result; +}; + +/* Phasing table for channels. Note that channels 16 & 17 use BATTP and BATTI */ +static const struct cpcap_adc_phasing_tbl bank_phasing[] = { + /* Bank0 */ + [CPCAP_ADC_AD0_BATTDETB] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_BATTP] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_VBUS] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_AD3] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_BPLUS_AD4] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_CHG_ISENSE] = {0, 0x80, 0x80, -512, 511}, + [CPCAP_ADC_BATTI] = {0, 0x80, 0x80, -512, 511}, + [CPCAP_ADC_USB_ID] = {0, 0x80, 0x80, 0, 1023}, + + /* Bank1 */ + [CPCAP_ADC_AD8] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_AD9] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_LICELL] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_HV_BATTP] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_TSX1_AD12] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_TSX2_AD13] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_TSY1_AD14] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_TSY2_AD15] = {0, 0x80, 0x80, 0, 1023}, +}; + +/* + * Conversion table for channels. Updated during init based on calibration. + * Here too channels 16 & 17 use BATTP and BATTI. + */ +static struct cpcap_adc_conversion_tbl bank_conversion[] = { + /* Bank0 */ + [CPCAP_ADC_AD0_BATTDETB] = { + IIO_CHAN_INFO_PROCESSED, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_BATTP] = { + IIO_CHAN_INFO_PROCESSED, 0, 2400, 0, 2300, 1023, + }, + [CPCAP_ADC_VBUS] = { + IIO_CHAN_INFO_PROCESSED, 0, 0, 0, 10000, 1023, + }, + [CPCAP_ADC_AD3] = { + IIO_CHAN_INFO_PROCESSED, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_BPLUS_AD4] = { + IIO_CHAN_INFO_PROCESSED, 0, 2400, 0, 2300, 1023, + }, + [CPCAP_ADC_CHG_ISENSE] = { + IIO_CHAN_INFO_PROCESSED, -512, 2, 0, 5000, 1023, + }, + [CPCAP_ADC_BATTI] = { + IIO_CHAN_INFO_PROCESSED, -512, 2, 0, 5000, 1023, + }, + [CPCAP_ADC_USB_ID] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + + /* Bank1 */ + [CPCAP_ADC_AD8] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_AD9] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_LICELL] = { + IIO_CHAN_INFO_PROCESSED, 0, 0, 0, 3400, 1023, + }, + [CPCAP_ADC_HV_BATTP] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_TSX1_AD12] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_TSX2_AD13] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_TSY1_AD14] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_TSY2_AD15] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, +}; + +/* + * Temperature lookup table of register values to milliCelcius. + * REVISIT: Check the duplicate 0x3ff entry in a freezer + */ +static const int temp_map[CPCAP_MAX_TEMP_LVL][2] = { + { 0x03ff, -40000 }, + { 0x03ff, -35000 }, + { 0x03ef, -30000 }, + { 0x03b2, -25000 }, + { 0x036c, -20000 }, + { 0x0320, -15000 }, + { 0x02d0, -10000 }, + { 0x027f, -5000 }, + { 0x022f, 0 }, + { 0x01e4, 5000 }, + { 0x019f, 10000 }, + { 0x0161, 15000 }, + { 0x012b, 20000 }, + { 0x00fc, 25000 }, + { 0x00d4, 30000 }, + { 0x00b2, 35000 }, + { 0x0095, 40000 }, + { 0x007d, 45000 }, + { 0x0069, 50000 }, + { 0x0059, 55000 }, + { 0x004b, 60000 }, + { 0x003f, 65000 }, + { 0x0036, 70000 }, + { 0x002e, 75000 }, + { 0x0027, 80000 }, + { 0x0022, 85000 }, + { 0x001d, 90000 }, +}; + +#define CPCAP_CHAN(_type, _index, _address, _datasheet_name) { \ + .type = (_type), \ + .address = (_address), \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_PROCESSED), \ + .scan_index = (_index), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 10, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ + .datasheet_name = (_datasheet_name), \ +} + +/* + * The datasheet names are from Motorola mapphone Linux kernel except + * for the last two which might be uncalibrated charge voltage and + * current. + */ +static const struct iio_chan_spec cpcap_adc_channels[] = { + /* Bank0 */ + CPCAP_CHAN(IIO_TEMP, 0, CPCAP_REG_ADCD0, "battdetb"), + CPCAP_CHAN(IIO_VOLTAGE, 1, CPCAP_REG_ADCD1, "battp"), + CPCAP_CHAN(IIO_VOLTAGE, 2, CPCAP_REG_ADCD2, "vbus"), + CPCAP_CHAN(IIO_TEMP, 3, CPCAP_REG_ADCD3, "ad3"), + CPCAP_CHAN(IIO_VOLTAGE, 4, CPCAP_REG_ADCD4, "ad4"), + CPCAP_CHAN(IIO_CURRENT, 5, CPCAP_REG_ADCD5, "chg_isense"), + CPCAP_CHAN(IIO_CURRENT, 6, CPCAP_REG_ADCD6, "batti"), + CPCAP_CHAN(IIO_VOLTAGE, 7, CPCAP_REG_ADCD7, "usb_id"), + + /* Bank1 */ + CPCAP_CHAN(IIO_CURRENT, 8, CPCAP_REG_ADCD0, "ad8"), + CPCAP_CHAN(IIO_VOLTAGE, 9, CPCAP_REG_ADCD1, "ad9"), + CPCAP_CHAN(IIO_VOLTAGE, 10, CPCAP_REG_ADCD2, "licell"), + CPCAP_CHAN(IIO_VOLTAGE, 11, CPCAP_REG_ADCD3, "hv_battp"), + CPCAP_CHAN(IIO_VOLTAGE, 12, CPCAP_REG_ADCD4, "tsx1_ad12"), + CPCAP_CHAN(IIO_VOLTAGE, 13, CPCAP_REG_ADCD5, "tsx2_ad13"), + CPCAP_CHAN(IIO_VOLTAGE, 14, CPCAP_REG_ADCD6, "tsy1_ad14"), + CPCAP_CHAN(IIO_VOLTAGE, 15, CPCAP_REG_ADCD7, "tsy2_ad15"), + + /* There are two registers with multiplexed functionality */ + CPCAP_CHAN(IIO_VOLTAGE, 16, CPCAP_REG_ADCD0, "chg_vsense"), + CPCAP_CHAN(IIO_CURRENT, 17, CPCAP_REG_ADCD1, "batti2"), +}; + +static irqreturn_t cpcap_adc_irq_thread(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct cpcap_adc *ddata = iio_priv(indio_dev); + int error; + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_DIS, + CPCAP_BIT_ADTRIG_DIS); + if (error) + return IRQ_NONE; + + ddata->done = true; + wake_up_interruptible(&ddata->wq_data_avail); + + return IRQ_HANDLED; +} + +/* ADC calibration functions */ +static void cpcap_adc_setup_calibrate(struct cpcap_adc *ddata, + enum cpcap_adc_channel chan) +{ + unsigned int value = 0; + unsigned long timeout = jiffies + msecs_to_jiffies(3000); + int error; + + if ((chan != CPCAP_ADC_CHG_ISENSE) && + (chan != CPCAP_ADC_BATTI)) + return; + + value |= CPCAP_BIT_CAL_MODE | CPCAP_BIT_RAND0; + value |= ((chan << 4) & + (CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 | CPCAP_BIT_ADA0)); + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC1, + CPCAP_BIT_CAL_MODE | CPCAP_BIT_ATOX | + CPCAP_BIT_ATO3 | CPCAP_BIT_ATO2 | + CPCAP_BIT_ATO1 | CPCAP_BIT_ATO0 | + CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 | + CPCAP_BIT_ADA0 | CPCAP_BIT_AD_SEL1 | + CPCAP_BIT_RAND1 | CPCAP_BIT_RAND0, + value); + if (error) + return; + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ATOX_PS_FACTOR | + CPCAP_BIT_ADC_PS_FACTOR1 | + CPCAP_BIT_ADC_PS_FACTOR0, + 0); + if (error) + return; + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_DIS, + CPCAP_BIT_ADTRIG_DIS); + if (error) + return; + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ASC, + CPCAP_BIT_ASC); + if (error) + return; + + do { + schedule_timeout_uninterruptible(1); + error = regmap_read(ddata->reg, CPCAP_REG_ADCC2, &value); + if (error) + return; + } while ((value & CPCAP_BIT_ASC) && time_before(jiffies, timeout)); + + if (value & CPCAP_BIT_ASC) + dev_err(ddata->dev, + "Timeout waiting for calibration to complete\n"); + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC1, + CPCAP_BIT_CAL_MODE, 0); + if (error) + return; +} + +static int cpcap_adc_calibrate_one(struct cpcap_adc *ddata, + int channel, + u16 calibration_register, + int lower_threshold, + int upper_threshold) +{ + unsigned int calibration_data[2]; + unsigned short cal_data_diff; + int i, error; + + for (i = 0; i < CPCAP_ADC_MAX_RETRIES; i++) { + calibration_data[0] = 0; + calibration_data[1] = 0; + cal_data_diff = 0; + cpcap_adc_setup_calibrate(ddata, channel); + error = regmap_read(ddata->reg, calibration_register, + &calibration_data[0]); + if (error) + return error; + cpcap_adc_setup_calibrate(ddata, channel); + error = regmap_read(ddata->reg, calibration_register, + &calibration_data[1]); + if (error) + return error; + + if (calibration_data[0] > calibration_data[1]) + cal_data_diff = + calibration_data[0] - calibration_data[1]; + else + cal_data_diff = + calibration_data[1] - calibration_data[0]; + + if (((calibration_data[1] >= lower_threshold) && + (calibration_data[1] <= upper_threshold) && + (cal_data_diff <= ST_ADC_CALIBRATE_DIFF_THRESHOLD)) || + (ddata->vendor == CPCAP_VENDOR_TI)) { + bank_conversion[channel].cal_offset = + ((short)calibration_data[1] * -1) + 512; + dev_dbg(ddata->dev, "ch%i calibration complete: %i\n", + channel, bank_conversion[channel].cal_offset); + break; + } + usleep_range(5000, 10000); + } + + return 0; +} + +static int cpcap_adc_calibrate(struct cpcap_adc *ddata) +{ + int error; + + error = cpcap_adc_calibrate_one(ddata, CPCAP_ADC_CHG_ISENSE, + CPCAP_REG_ADCAL1, + ST_ADC_CAL_CHRGI_LOW_THRESHOLD, + ST_ADC_CAL_CHRGI_HIGH_THRESHOLD); + if (error) + return error; + + error = cpcap_adc_calibrate_one(ddata, CPCAP_ADC_BATTI, + CPCAP_REG_ADCAL2, + ST_ADC_CAL_BATTI_LOW_THRESHOLD, + ST_ADC_CAL_BATTI_HIGH_THRESHOLD); + if (error) + return error; + + return 0; +} + +/* ADC setup, read and scale functions */ +static void cpcap_adc_setup_bank(struct cpcap_adc *ddata, + struct cpcap_adc_request *req) +{ + const struct cpcap_adc_ato *ato = ddata->ato; + unsigned short value1 = 0; + unsigned short value2 = 0; + int error; + + if (!ato) + return; + + switch (req->channel) { + case CPCAP_ADC_AD8 ... CPCAP_ADC_TSY2_AD15: + value1 |= CPCAP_BIT_AD_SEL1; + break; + case CPCAP_ADC_BATTP_PI16 ... CPCAP_ADC_BATTI_PI17: + value1 |= CPCAP_BIT_RAND1; + default: + break; + } + + switch (req->timing) { + case CPCAP_ADC_TIMING_IN: + value1 |= ato->ato_in; + value1 |= ato->atox_in; + value2 |= ato->adc_ps_factor_in; + value2 |= ato->atox_ps_factor_in; + break; + case CPCAP_ADC_TIMING_OUT: + value1 |= ato->ato_out; + value1 |= ato->atox_out; + value2 |= ato->adc_ps_factor_out; + value2 |= ato->atox_ps_factor_out; + break; + + case CPCAP_ADC_TIMING_IMM: + default: + break; + } + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC1, + CPCAP_BIT_CAL_MODE | CPCAP_BIT_ATOX | + CPCAP_BIT_ATO3 | CPCAP_BIT_ATO2 | + CPCAP_BIT_ATO1 | CPCAP_BIT_ATO0 | + CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 | + CPCAP_BIT_ADA0 | CPCAP_BIT_AD_SEL1 | + CPCAP_BIT_RAND1 | CPCAP_BIT_RAND0, + value1); + if (error) + return; + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ATOX_PS_FACTOR | + CPCAP_BIT_ADC_PS_FACTOR1 | + CPCAP_BIT_ADC_PS_FACTOR0, + value2); + if (error) + return; + + if (req->timing == CPCAP_ADC_TIMING_IMM) { + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_DIS, + CPCAP_BIT_ADTRIG_DIS); + if (error) + return; + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ASC, + CPCAP_BIT_ASC); + if (error) + return; + } else { + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_ONESHOT, + CPCAP_BIT_ADTRIG_ONESHOT); + if (error) + return; + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_DIS, 0); + if (error) + return; + } +} + +/* + * Occasionally the ADC does not seem to start and there will be no + * interrupt. Let's re-init interrupt to prevent the ADC from hanging + * for the next request. It is unclear why this happens, but the next + * request will usually work after doing this. + */ +static void cpcap_adc_quirk_reset_lost_irq(struct cpcap_adc *ddata) +{ + int error; + + dev_info(ddata->dev, "lost ADC irq, attempting to reinit\n"); + disable_irq(ddata->irq); + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_DIS, + CPCAP_BIT_ADTRIG_DIS); + if (error) + dev_warn(ddata->dev, "%s reset failed: %i\n", + __func__, error); + enable_irq(ddata->irq); +} + +static int cpcap_adc_start_bank(struct cpcap_adc *ddata, + struct cpcap_adc_request *req) +{ + int i, error; + + req->timing = CPCAP_ADC_TIMING_IMM; + ddata->done = false; + + for (i = 0; i < CPCAP_ADC_MAX_RETRIES; i++) { + cpcap_adc_setup_bank(ddata, req); + error = wait_event_interruptible_timeout(ddata->wq_data_avail, + ddata->done, + msecs_to_jiffies(50)); + if (error > 0) + return 0; + + if (error == 0) { + cpcap_adc_quirk_reset_lost_irq(ddata); + error = -ETIMEDOUT; + continue; + } + + if (error < 0) + return error; + } + + return error; +} + +static void cpcap_adc_phase(struct cpcap_adc_request *req) +{ + const struct cpcap_adc_conversion_tbl *conv_tbl = req->conv_tbl; + const struct cpcap_adc_phasing_tbl *phase_tbl = req->phase_tbl; + int index = req->channel; + + /* Remuxed channels 16 and 17 use BATTP and BATTI entries */ + switch (req->channel) { + case CPCAP_ADC_BATTP: + case CPCAP_ADC_BATTP_PI16: + index = req->bank_index; + req->result -= phase_tbl[index].offset; + req->result -= CPCAP_FOUR_POINT_TWO_ADC; + req->result *= phase_tbl[index].multiplier; + if (phase_tbl[index].divider == 0) + return; + req->result /= phase_tbl[index].divider; + req->result += CPCAP_FOUR_POINT_TWO_ADC; + break; + case CPCAP_ADC_BATTI_PI17: + index = req->bank_index; + /* fallthrough */ + default: + req->result += conv_tbl[index].cal_offset; + req->result += conv_tbl[index].align_offset; + req->result *= phase_tbl[index].multiplier; + if (phase_tbl[index].divider == 0) + return; + req->result /= phase_tbl[index].divider; + req->result += phase_tbl[index].offset; + break; + } + + if (req->result < phase_tbl[index].min) + req->result = phase_tbl[index].min; + else if (req->result > phase_tbl[index].max) + req->result = phase_tbl[index].max; +} + +/* Looks up temperatures in a table and calculates averages if needed */ +static int cpcap_adc_table_to_millicelcius(unsigned short value) +{ + int i, result = 0, alpha; + + if (value <= temp_map[CPCAP_MAX_TEMP_LVL - 1][0]) + return temp_map[CPCAP_MAX_TEMP_LVL - 1][1]; + + if (value >= temp_map[0][0]) + return temp_map[0][1]; + + for (i = 0; i < CPCAP_MAX_TEMP_LVL - 1; i++) { + if ((value <= temp_map[i][0]) && + (value >= temp_map[i + 1][0])) { + if (value == temp_map[i][0]) { + result = temp_map[i][1]; + } else if (value == temp_map[i + 1][0]) { + result = temp_map[i + 1][1]; + } else { + alpha = ((value - temp_map[i][0]) * 1000) / + (temp_map[i + 1][0] - temp_map[i][0]); + + result = temp_map[i][1] + + ((alpha * (temp_map[i + 1][1] - + temp_map[i][1])) / 1000); + } + break; + } + } + + return result; +} + +static void cpcap_adc_convert(struct cpcap_adc_request *req) +{ + const struct cpcap_adc_conversion_tbl *conv_tbl = req->conv_tbl; + int index = req->channel; + + /* Remuxed channels 16 and 17 use BATTP and BATTI entries */ + switch (req->channel) { + case CPCAP_ADC_BATTP_PI16: + index = CPCAP_ADC_BATTP; + break; + case CPCAP_ADC_BATTI_PI17: + index = CPCAP_ADC_BATTI; + break; + default: + break; + } + + /* No conversion for raw channels */ + if (conv_tbl[index].conv_type == IIO_CHAN_INFO_RAW) + return; + + /* Temperatures use a lookup table instead of conversion table */ + if ((req->channel == CPCAP_ADC_AD0_BATTDETB) || + (req->channel == CPCAP_ADC_AD3)) { + req->result = + cpcap_adc_table_to_millicelcius(req->result); + + return; + } + + /* All processed channels use a conversion table */ + req->result *= conv_tbl[index].multiplier; + if (conv_tbl[index].divider == 0) + return; + req->result /= conv_tbl[index].divider; + req->result += conv_tbl[index].conv_offset; +} + +/* + * REVISIT: Check if timed sampling can use multiple channels at the + * same time. If not, replace channel_mask with just channel. + */ +static int cpcap_adc_read_bank_scaled(struct cpcap_adc *ddata, + struct cpcap_adc_request *req) +{ + int calibration_data, error, addr; + + if (ddata->vendor == CPCAP_VENDOR_TI) { + error = regmap_read(ddata->reg, CPCAP_REG_ADCAL1, + &calibration_data); + if (error) + return error; + bank_conversion[CPCAP_ADC_CHG_ISENSE].cal_offset = + ((short)calibration_data * -1) + 512; + + error = regmap_read(ddata->reg, CPCAP_REG_ADCAL2, + &calibration_data); + if (error) + return error; + bank_conversion[CPCAP_ADC_BATTI].cal_offset = + ((short)calibration_data * -1) + 512; + } + + addr = CPCAP_REG_ADCD0 + req->bank_index * 4; + + error = regmap_read(ddata->reg, addr, &req->result); + if (error) + return error; + + req->result &= 0x3ff; + cpcap_adc_phase(req); + cpcap_adc_convert(req); + + return 0; +} + +static int cpcap_adc_init_request(struct cpcap_adc_request *req, + int channel) +{ + req->channel = channel; + req->phase_tbl = bank_phasing; + req->conv_tbl = bank_conversion; + + switch (channel) { + case CPCAP_ADC_AD0_BATTDETB ... CPCAP_ADC_USB_ID: + req->bank_index = channel; + break; + case CPCAP_ADC_AD8 ... CPCAP_ADC_TSY2_AD15: + req->bank_index = channel - 8; + break; + case CPCAP_ADC_BATTP_PI16: + req->bank_index = CPCAP_ADC_BATTP; + break; + case CPCAP_ADC_BATTI_PI17: + req->bank_index = CPCAP_ADC_BATTI; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int cpcap_adc_read(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct cpcap_adc *ddata = iio_priv(indio_dev); + struct cpcap_adc_request req; + int error; + + error = cpcap_adc_init_request(&req, chan->channel); + if (error) + return error; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&ddata->lock); + error = cpcap_adc_start_bank(ddata, &req); + if (error) + goto err_unlock; + error = regmap_read(ddata->reg, chan->address, val); + if (error) + goto err_unlock; + mutex_unlock(&ddata->lock); + break; + case IIO_CHAN_INFO_PROCESSED: + mutex_lock(&ddata->lock); + error = cpcap_adc_start_bank(ddata, &req); + if (error) + goto err_unlock; + error = cpcap_adc_read_bank_scaled(ddata, &req); + if (error) + goto err_unlock; + mutex_unlock(&ddata->lock); + *val = req.result; + break; + default: + return -EINVAL; + } + + return IIO_VAL_INT; + +err_unlock: + mutex_unlock(&ddata->lock); + dev_err(ddata->dev, "error reading ADC: %i\n", error); + + return error; +} + +static const struct iio_info cpcap_adc_info = { + .read_raw = &cpcap_adc_read, + .driver_module = THIS_MODULE, +}; + +/* + * Configuration for Motorola mapphone series such as droid 4. + * Copied from the Motorola mapphone kernel tree. + */ +static const struct cpcap_adc_ato mapphone_adc = { + .ato_in = 0x0480, + .atox_in = 0, + .adc_ps_factor_in = 0x0200, + .atox_ps_factor_in = 0, + .ato_out = 0, + .atox_out = 0, + .adc_ps_factor_out = 0, + .atox_ps_factor_out = 0, +}; + +static const struct of_device_id cpcap_adc_id_table[] = { + { + .compatible = "motorola,cpcap-adc", + }, + { + .compatible = "motorola,mapphone-cpcap-adc", + .data = &mapphone_adc, + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, cpcap_adc_id_table); + +static int cpcap_adc_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + struct cpcap_adc *ddata; + struct iio_dev *indio_dev; + int error; + + match = of_match_device(of_match_ptr(cpcap_adc_id_table), + &pdev->dev); + if (!match) + return -EINVAL; + + if (!match->data) { + dev_err(&pdev->dev, "no configuration data found\n"); + + return -ENODEV; + } + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*ddata)); + if (!indio_dev) { + dev_err(&pdev->dev, "failed to allocate iio device\n"); + + return -ENOMEM; + } + ddata = iio_priv(indio_dev); + ddata->ato = match->data; + ddata->dev = &pdev->dev; + + mutex_init(&ddata->lock); + init_waitqueue_head(&ddata->wq_data_avail); + + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + indio_dev->dev.parent = &pdev->dev; + indio_dev->dev.of_node = pdev->dev.of_node; + indio_dev->channels = cpcap_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(cpcap_adc_channels); + indio_dev->name = dev_name(&pdev->dev); + indio_dev->info = &cpcap_adc_info; + + ddata->reg = dev_get_regmap(pdev->dev.parent, NULL); + if (!ddata->reg) + return -ENODEV; + + error = cpcap_get_vendor(ddata->dev, ddata->reg, &ddata->vendor); + if (error) + return error; + + platform_set_drvdata(pdev, indio_dev); + + ddata->irq = platform_get_irq_byname(pdev, "adcdone"); + if (!ddata->irq) + return -ENODEV; + + error = devm_request_threaded_irq(&pdev->dev, ddata->irq, NULL, + cpcap_adc_irq_thread, + IRQF_TRIGGER_NONE, + "cpcap-adc", indio_dev); + if (error) { + dev_err(&pdev->dev, "could not get irq: %i\n", + error); + + return error; + } + + error = cpcap_adc_calibrate(ddata); + if (error) + return error; + + dev_info(&pdev->dev, "CPCAP ADC device probed\n"); + + return devm_iio_device_register(&pdev->dev, indio_dev); +} + +static struct platform_driver cpcap_adc_driver = { + .driver = { + .name = "cpcap_adc", + .of_match_table = of_match_ptr(cpcap_adc_id_table), + }, + .probe = cpcap_adc_probe, +}; + +module_platform_driver(cpcap_adc_driver); + +MODULE_ALIAS("platform:cpcap_adc"); +MODULE_DESCRIPTION("CPCAP ADC driver"); +MODULE_AUTHOR("Tony Lindgren Date: Sat, 1 Apr 2017 14:09:55 +0530 Subject: iio: proximity: as3935: constify attribute_group structures Check for attribute_group structures that are only stored in the attrs filed of iio_info structure. As the attrs field of iio_info structures is constant, so these attribute_group structures can also be declared constant. Done using coccinelle: @r1 disable optional_qualifier @ identifier i; position p; @@ static struct attribute_group i@p = {...}; @ok1@ identifier r1.i; position p; struct iio_info x; @@ x.attrs=&i@p; @bad@ position p!={r1.p,ok1.p}; identifier r1.i; @@ i@p @depends on !bad disable optional_qualifier@ identifier r1.i; @@ static +const struct attribute_group i={...}; @depends on !bad disable optional_qualifier@ identifier r1.i; @@ +const struct attribute_group i; File size before: text data bss dec hex filename 4037 288 0 4325 10e5 drivers/iio/proximity/as3935.o File size after: text data bss dec hex filename 4101 256 0 4357 1105 drivers/iio/proximity/as3935.o Signed-off-by: simran singhal Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/as3935.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index 5656deb17261..1b8b4723bcca 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -155,7 +155,7 @@ static struct attribute *as3935_attributes[] = { NULL, }; -static struct attribute_group as3935_attribute_group = { +static const struct attribute_group as3935_attribute_group = { .attrs = as3935_attributes, }; -- cgit v1.2.3 From c2869498d0c0ef39f92bbd11c174d3ccd166826c Mon Sep 17 00:00:00 2001 From: simran singhal Date: Sat, 1 Apr 2017 14:03:30 +0530 Subject: iio: light: bh1750: constify attribute_group structures Check for attribute_group structures that are only stored in the attrs filed of iio_info structure. As the attrs field of iio_info structures is constant, so these attribute_group structures can also be declared constant. Done using coccinelle: @r1 disable optional_qualifier @ identifier i; position p; @@ static struct attribute_group i@p = {...}; @ok1@ identifier r1.i; position p; struct iio_info x; @@ x.attrs=&i@p; @bad@ position p!={r1.p,ok1.p}; identifier r1.i; @@ i@p @depends on !bad disable optional_qualifier@ identifier r1.i; @@ static +const struct attribute_group i={...}; @depends on !bad disable optional_qualifier@ identifier r1.i; @@ +const struct attribute_group i; File size before: text data bss dec hex filename 2276 352 0 2628 a44 drivers/iio/light/bh1750.o File size after: text data bss dec hex filename 2340 320 0 2660 a64 drivers/iio/light/bh1750.o Signed-off-by: simran singhal Signed-off-by: Jonathan Cameron --- drivers/iio/light/bh1750.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/light/bh1750.c b/drivers/iio/light/bh1750.c index b05946604f80..6c61187e630f 100644 --- a/drivers/iio/light/bh1750.c +++ b/drivers/iio/light/bh1750.c @@ -212,7 +212,7 @@ static struct attribute *bh1750_attributes[] = { NULL, }; -static struct attribute_group bh1750_attribute_group = { +static const struct attribute_group bh1750_attribute_group = { .attrs = bh1750_attributes, }; -- cgit v1.2.3 From 3ca437c052f083c625a2eed100f4e40bb9c7e2bd Mon Sep 17 00:00:00 2001 From: simran singhal Date: Sat, 1 Apr 2017 13:59:10 +0530 Subject: iio: light: apds9960: constify attribute_group structures Check for attribute_group structures that are only stored in the attrs filed of iio_info structure. As the attrs field of iio_info structures is constant, so these attribute_group structures can also be declared constant. Done using coccinelle: @r1 disable optional_qualifier @ identifier i; position p; @@ static struct attribute_group i@p = {...}; @ok1@ identifier r1.i; position p; struct iio_info x; @@ x.attrs=&i@p; @bad@ position p!={r1.p,ok1.p}; identifier r1.i; @@ i@p @depends on !bad disable optional_qualifier@ identifier r1.i; @@ static +const struct attribute_group i={...}; @depends on !bad disable optional_qualifier@ identifier r1.i; @@ +const struct attribute_group i; File size before: text data bss dec hex filename 8503 488 0 8991 231f drivers/iio/light/apds9960.o File size after: text data bss dec hex filename 8567 424 0 8991 231f drivers/iio/light/apds9960.o Signed-off-by: simran singhal Signed-off-by: Jonathan Cameron --- drivers/iio/light/apds9960.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c index 90bc98df362b..c7af36de29a7 100644 --- a/drivers/iio/light/apds9960.c +++ b/drivers/iio/light/apds9960.c @@ -343,7 +343,7 @@ static struct attribute *apds9960_attributes[] = { NULL, }; -static struct attribute_group apds9960_attribute_group = { +static const struct attribute_group apds9960_attribute_group = { .attrs = apds9960_attributes, }; -- cgit v1.2.3 From 757cff86a8942c50558bf91805f8e6bf0b6128b8 Mon Sep 17 00:00:00 2001 From: simran singhal Date: Sat, 1 Apr 2017 13:53:33 +0530 Subject: iio: humidity: hdc100x: constify attribute_group structures Check for attribute_group structures that are only stored in the attrs filed of iio_info structure. As the attrs field of iio_info structures is constant, so these attribute_group structures can also be declared constant. Done using coccinelle: @r1 disable optional_qualifier @ identifier i; position p; @@ static struct attribute_group i@p = {...}; @ok1@ identifier r1.i; position p; struct iio_info x; @@ x.attrs=&i@p; @bad@ position p!={r1.p,ok1.p}; identifier r1.i; @@ i@p @depends on !bad disable optional_qualifier@ identifier r1.i; @@ static +const struct attribute_group i={...}; @depends on !bad disable optional_qualifier@ identifier r1.i; @@ +const struct attribute_group i; File size before: text data bss dec hex filename 3459 488 0 3947 f6b drivers/iio/humidity/hdc100x.o File size after: text data bss dec hex filename 3507 424 0 3931 f5b drivers/iio/humidity/hdc100x.o Signed-off-by: simran singhal Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/hdc100x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/humidity/hdc100x.c b/drivers/iio/humidity/hdc100x.c index 265c34da52d1..aa17115f54c9 100644 --- a/drivers/iio/humidity/hdc100x.c +++ b/drivers/iio/humidity/hdc100x.c @@ -79,7 +79,7 @@ static struct attribute *hdc100x_attributes[] = { NULL }; -static struct attribute_group hdc100x_attribute_group = { +static const struct attribute_group hdc100x_attribute_group = { .attrs = hdc100x_attributes, }; -- cgit v1.2.3 From 4214dc1807afa0a9eb84f845c9c2772dcbeae0f8 Mon Sep 17 00:00:00 2001 From: simran singhal Date: Sat, 1 Apr 2017 13:47:51 +0530 Subject: iio: adc: hx711: constify attribute_group structures Check for attribute_group structures that are only stored in the attrs filed of iio_info structure. As the attrs field of iio_info structures is constant, so these attribute_group structures can also be declared constant. Done using coccinelle: @r1 disable optional_qualifier @ identifier i; position p; @@ static struct attribute_group i@p = {...}; @ok1@ identifier r1.i; position p; struct iio_info x; @@ x.attrs=&i@p; @bad@ position p!={r1.p,ok1.p}; identifier r1.i; @@ i@p @depends on !bad disable optional_qualifier@ identifier r1.i; @@ static +const struct attribute_group i={...}; @depends on !bad disable optional_qualifier@ identifier r1.i; @@ +const struct attribute_group i; File size before: text data bss dec hex filename 3042 480 0 3522 dc2 drivers/iio/adc/hx711.o File size after: text data bss dec hex filename 3098 416 0 3514 dba drivers/iio/adc/hx711.o Signed-off-by: simran singhal Signed-off-by: Jonathan Cameron --- drivers/iio/adc/hx711.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c index 139639f73769..27005d84ed73 100644 --- a/drivers/iio/adc/hx711.c +++ b/drivers/iio/adc/hx711.c @@ -369,7 +369,7 @@ static struct attribute *hx711_attributes[] = { NULL, }; -static struct attribute_group hx711_attribute_group = { +static const struct attribute_group hx711_attribute_group = { .attrs = hx711_attributes, }; -- cgit v1.2.3 From d952b4e6ad8f174577884b2a8ceb13832a7a6d21 Mon Sep 17 00:00:00 2001 From: simran singhal Date: Sat, 1 Apr 2017 07:58:56 +0530 Subject: staging: iio: light: tsl2x7x constify attribute_group structures As the event_attrs field of iio_info structures is constant, so these attribute_group structures can also be declared constant. File size before: text data bss dec hex filename 15064 1528 0 16592 40d0 drivers/staging/iio/light/tsl2x7x_core.o File size after: text data bss dec hex filename 15192 1400 0 16592 40d0 drivers/staging/iio/light/tsl2x7x_core.o Signed-off-by: simran singhal Signed-off-by: Jonathan Cameron --- drivers/staging/iio/light/tsl2x7x_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/staging/iio/light/tsl2x7x_core.c b/drivers/staging/iio/light/tsl2x7x_core.c index 197201a70d59..af3910bc1b4f 100644 --- a/drivers/staging/iio/light/tsl2x7x_core.c +++ b/drivers/staging/iio/light/tsl2x7x_core.c @@ -1676,7 +1676,7 @@ static const struct attribute_group tsl2X7X_device_attr_group_tbl[] = { }, }; -static struct attribute_group tsl2X7X_event_attr_group_tbl[] = { +static const struct attribute_group tsl2X7X_event_attr_group_tbl[] = { [ALS] = { .attrs = tsl2X7X_ALS_event_attrs, .name = "events", -- cgit v1.2.3 From 573803234e72d182ec39692770110e574cd5b222 Mon Sep 17 00:00:00 2001 From: Rick Altherr Date: Tue, 28 Mar 2017 14:52:59 -0700 Subject: iio: Aspeed ADC Aspeed BMC SoCs include a 16 channel, 10-bit ADC. Low and high threshold interrupts are supported by the hardware but are not currently implemented. Signed-off-by: Rick Altherr Tested-by: Xo Wang Reviewed-by: Joel Stanley Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 11 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/aspeed_adc.c | 295 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 307 insertions(+) create mode 100644 drivers/iio/adc/aspeed_adc.c (limited to 'drivers') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index b369a4397d90..38c76ea683ea 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -130,6 +130,17 @@ config AD799X To compile this driver as a module, choose M here: the module will be called ad799x. +config ASPEED_ADC + tristate "Aspeed ADC" + depends on ARCH_ASPEED || COMPILE_TEST + depends on COMMON_CLK + help + If you say yes here you get support for the ADC included in Aspeed + BMC SoCs. + + To compile this driver as a module, choose M here: the module will be + called aspeed_adc. + config AT91_ADC tristate "Atmel AT91 ADC" depends on ARCH_AT91 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 26ce71bdeb67..fd8e2a9f9e8b 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_AD7791) += ad7791.o obj-$(CONFIG_AD7793) += ad7793.o obj-$(CONFIG_AD7887) += ad7887.o obj-$(CONFIG_AD799X) += ad799x.o +obj-$(CONFIG_ASPEED_ADC) += aspeed_adc.o obj-$(CONFIG_AT91_ADC) += at91_adc.o obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o obj-$(CONFIG_AXP288_ADC) += axp288_adc.o diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c new file mode 100644 index 000000000000..62670cbfa2bb --- /dev/null +++ b/drivers/iio/adc/aspeed_adc.c @@ -0,0 +1,295 @@ +/* + * Aspeed AST2400/2500 ADC + * + * Copyright (C) 2017 Google, Inc. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define ASPEED_RESOLUTION_BITS 10 +#define ASPEED_CLOCKS_PER_SAMPLE 12 + +#define ASPEED_REG_ENGINE_CONTROL 0x00 +#define ASPEED_REG_INTERRUPT_CONTROL 0x04 +#define ASPEED_REG_VGA_DETECT_CONTROL 0x08 +#define ASPEED_REG_CLOCK_CONTROL 0x0C +#define ASPEED_REG_MAX 0xC0 + +#define ASPEED_OPERATION_MODE_POWER_DOWN (0x0 << 1) +#define ASPEED_OPERATION_MODE_STANDBY (0x1 << 1) +#define ASPEED_OPERATION_MODE_NORMAL (0x7 << 1) + +#define ASPEED_ENGINE_ENABLE BIT(0) + +struct aspeed_adc_model_data { + const char *model_name; + unsigned int min_sampling_rate; // Hz + unsigned int max_sampling_rate; // Hz + unsigned int vref_voltage; // mV +}; + +struct aspeed_adc_data { + struct device *dev; + void __iomem *base; + spinlock_t clk_lock; + struct clk_hw *clk_prescaler; + struct clk_hw *clk_scaler; +}; + +#define ASPEED_CHAN(_idx, _data_reg_addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_idx), \ + .address = (_data_reg_addr), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ +} + +static const struct iio_chan_spec aspeed_adc_iio_channels[] = { + ASPEED_CHAN(0, 0x10), + ASPEED_CHAN(1, 0x12), + ASPEED_CHAN(2, 0x14), + ASPEED_CHAN(3, 0x16), + ASPEED_CHAN(4, 0x18), + ASPEED_CHAN(5, 0x1A), + ASPEED_CHAN(6, 0x1C), + ASPEED_CHAN(7, 0x1E), + ASPEED_CHAN(8, 0x20), + ASPEED_CHAN(9, 0x22), + ASPEED_CHAN(10, 0x24), + ASPEED_CHAN(11, 0x26), + ASPEED_CHAN(12, 0x28), + ASPEED_CHAN(13, 0x2A), + ASPEED_CHAN(14, 0x2C), + ASPEED_CHAN(15, 0x2E), +}; + +static int aspeed_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + const struct aspeed_adc_model_data *model_data = + of_device_get_match_data(data->dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + *val = readw(data->base + chan->address); + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = model_data->vref_voltage; + *val2 = ASPEED_RESOLUTION_BITS; + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = clk_get_rate(data->clk_scaler->clk) / + ASPEED_CLOCKS_PER_SAMPLE; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int aspeed_adc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + const struct aspeed_adc_model_data *model_data = + of_device_get_match_data(data->dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (val < model_data->min_sampling_rate || + val > model_data->max_sampling_rate) + return -EINVAL; + + clk_set_rate(data->clk_scaler->clk, + val * ASPEED_CLOCKS_PER_SAMPLE); + return 0; + + case IIO_CHAN_INFO_SCALE: + case IIO_CHAN_INFO_RAW: + /* + * Technically, these could be written but the only reasons + * for doing so seem better handled in userspace. EPERM is + * returned to signal this is a policy choice rather than a + * hardware limitation. + */ + return -EPERM; + + default: + return -EINVAL; + } +} + +static int aspeed_adc_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + + if (!readval || reg % 4 || reg > ASPEED_REG_MAX) + return -EINVAL; + + *readval = readl(data->base + reg); + + return 0; +} + +static const struct iio_info aspeed_adc_iio_info = { + .driver_module = THIS_MODULE, + .read_raw = aspeed_adc_read_raw, + .write_raw = aspeed_adc_write_raw, + .debugfs_reg_access = aspeed_adc_reg_access, +}; + +static int aspeed_adc_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct aspeed_adc_data *data; + const struct aspeed_adc_model_data *model_data; + struct resource *res; + const char *clk_parent_name; + int ret; + u32 adc_engine_control_reg_val; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->base)) + return PTR_ERR(data->base); + + /* Register ADC clock prescaler with source specified by device tree. */ + spin_lock_init(&data->clk_lock); + clk_parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0); + + data->clk_prescaler = clk_hw_register_divider( + &pdev->dev, "prescaler", clk_parent_name, 0, + data->base + ASPEED_REG_CLOCK_CONTROL, + 17, 15, 0, &data->clk_lock); + if (IS_ERR(data->clk_prescaler)) + return PTR_ERR(data->clk_prescaler); + + /* + * Register ADC clock scaler downstream from the prescaler. Allow rate + * setting to adjust the prescaler as well. + */ + data->clk_scaler = clk_hw_register_divider( + &pdev->dev, "scaler", "prescaler", + CLK_SET_RATE_PARENT, + data->base + ASPEED_REG_CLOCK_CONTROL, + 0, 10, 0, &data->clk_lock); + if (IS_ERR(data->clk_scaler)) { + ret = PTR_ERR(data->clk_scaler); + goto scaler_error; + } + + /* Start all channels in normal mode. */ + clk_prepare_enable(data->clk_scaler->clk); + adc_engine_control_reg_val = GENMASK(31, 16) | + ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE; + writel(adc_engine_control_reg_val, + data->base + ASPEED_REG_ENGINE_CONTROL); + + model_data = of_device_get_match_data(&pdev->dev); + indio_dev->name = model_data->model_name; + indio_dev->dev.parent = &pdev->dev; + indio_dev->info = &aspeed_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = aspeed_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(aspeed_adc_iio_channels); + + ret = iio_device_register(indio_dev); + if (ret) + goto iio_register_error; + + return 0; + +iio_register_error: + writel(ASPEED_OPERATION_MODE_POWER_DOWN, + data->base + ASPEED_REG_ENGINE_CONTROL); + clk_disable_unprepare(data->clk_scaler->clk); + clk_hw_unregister_divider(data->clk_scaler); + +scaler_error: + clk_hw_unregister_divider(data->clk_prescaler); + return ret; +} + +static int aspeed_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct aspeed_adc_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + writel(ASPEED_OPERATION_MODE_POWER_DOWN, + data->base + ASPEED_REG_ENGINE_CONTROL); + clk_disable_unprepare(data->clk_scaler->clk); + clk_hw_unregister_divider(data->clk_scaler); + clk_hw_unregister_divider(data->clk_prescaler); + + return 0; +} + +static const struct aspeed_adc_model_data ast2400_model_data = { + .model_name = "ast2400-adc", + .vref_voltage = 2500, // mV + .min_sampling_rate = 10000, + .max_sampling_rate = 500000, +}; + +static const struct aspeed_adc_model_data ast2500_model_data = { + .model_name = "ast2500-adc", + .vref_voltage = 1800, // mV + .min_sampling_rate = 1, + .max_sampling_rate = 1000000, +}; + +static const struct of_device_id aspeed_adc_matches[] = { + { .compatible = "aspeed,ast2400-adc", .data = &ast2400_model_data }, + { .compatible = "aspeed,ast2500-adc", .data = &ast2500_model_data }, + {}, +}; +MODULE_DEVICE_TABLE(of, aspeed_adc_matches); + +static struct platform_driver aspeed_adc_driver = { + .probe = aspeed_adc_probe, + .remove = aspeed_adc_remove, + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = aspeed_adc_matches, + } +}; + +module_platform_driver(aspeed_adc_driver); + +MODULE_AUTHOR("Rick Altherr "); +MODULE_DESCRIPTION("Aspeed AST2400/2500 ADC Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 98b6d2b6764d75e386ce3236d771a7624a645e07 Mon Sep 17 00:00:00 2001 From: simran singhal Date: Fri, 31 Mar 2017 00:10:38 +0530 Subject: iio: light: lm3533-als: constify attribute_group structures Check for attribute_group structures that are only stored in the event_attrs filed of iio_info structure. As the event_attrs field of iio_info structures is constant, so these attribute_group structures can also be declared constant. Done using coccinelle: @r1 disable optional_qualifier @ identifier i; position p; @@ static struct attribute_group i@p = {...}; @ok1@ identifier r1.i; position p; struct iio_info x; @@ x.event_attrs=&i@p; @bad@ position p!={r1.p,ok1.p}; identifier r1.i; @@ i@p @depends on !bad disable optional_qualifier@ identifier r1.i; @@ static +const struct attribute_group i={...}; @depends on !bad disable optional_qualifier@ identifier r1.i; @@ +const struct attribute_group i; As the attrs field of iio_info structures is also constant, so these attribute_group structures can also be declared constant. Done manually. File size before: text data bss dec hex filename 5798 2376 0 8174 1fee drivers/iio/light/lm3533-als.o File size after: text data bss dec hex filename 5926 2248 0 8174 1fee drivers/iio/light/lm3533-als.o Signed-off-by: simran singhal Signed-off-by: Jonathan Cameron --- drivers/iio/light/lm3533-als.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c index f409c2047c05..0443fd2e8757 100644 --- a/drivers/iio/light/lm3533-als.c +++ b/drivers/iio/light/lm3533-als.c @@ -690,7 +690,7 @@ static struct attribute *lm3533_als_event_attributes[] = { NULL }; -static struct attribute_group lm3533_als_event_attribute_group = { +static const struct attribute_group lm3533_als_event_attribute_group = { .attrs = lm3533_als_event_attributes }; @@ -714,7 +714,7 @@ static struct attribute *lm3533_als_attributes[] = { NULL }; -static struct attribute_group lm3533_als_attribute_group = { +static const struct attribute_group lm3533_als_attribute_group = { .attrs = lm3533_als_attributes }; -- cgit v1.2.3 From 5a70925b16f2ad3d68dd6ecc469c9507d9a18e6a Mon Sep 17 00:00:00 2001 From: simran singhal Date: Fri, 31 Mar 2017 15:51:43 +0530 Subject: iio: gyro: adis16060: Change the name of function. Change the name of function from adis16060_spi_write_than_read() to adis16060_spi_write_then_read(). change "than" to "then" as its time depended. Signed-off-by: simran singhal Signed-off-by: Jonathan Cameron --- drivers/staging/iio/gyro/adis16060_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/iio/gyro/adis16060_core.c b/drivers/staging/iio/gyro/adis16060_core.c index 811596234823..967524583d8a 100644 --- a/drivers/staging/iio/gyro/adis16060_core.c +++ b/drivers/staging/iio/gyro/adis16060_core.c @@ -40,7 +40,7 @@ struct adis16060_state { static struct iio_dev *adis16060_iio_dev; -static int adis16060_spi_write_than_read(struct iio_dev *indio_dev, +static int adis16060_spi_write_then_read(struct iio_dev *indio_dev, u8 conf, u16 *val) { int ret; @@ -81,7 +81,7 @@ static int adis16060_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - ret = adis16060_spi_write_than_read(indio_dev, + ret = adis16060_spi_write_then_read(indio_dev, chan->address, &tval); if (ret < 0) return ret; -- cgit v1.2.3 From 0b568b3c4ae582dc8f20960dc02dc229957ea3d3 Mon Sep 17 00:00:00 2001 From: simran singhal Date: Sat, 1 Apr 2017 19:36:14 +0530 Subject: iio: adc: Remove unnecessary cast on void pointer The following Coccinelle script was used to detect this: @r@ expression x; void* e; type T; identifier f; @@ ( *((T *)e) | ((T *)x)[...] | ((T*)x)->f | - (T*) e ) Signed-off-by: simran singhal Signed-off-by: Jonathan Cameron --- drivers/iio/adc/exynos_adc.c | 2 +- drivers/iio/adc/imx7d_adc.c | 2 +- drivers/iio/adc/max1027.c | 2 +- drivers/iio/adc/rockchip_saradc.c | 2 +- drivers/iio/adc/sun4i-gpadc-iio.c | 2 +- drivers/iio/adc/vf610_adc.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c index ad1775b5f83c..6c5a7be9f8c1 100644 --- a/drivers/iio/adc/exynos_adc.c +++ b/drivers/iio/adc/exynos_adc.c @@ -579,7 +579,7 @@ static int exynos_read_s3c64xx_ts(struct iio_dev *indio_dev, int *x, int *y) static irqreturn_t exynos_adc_isr(int irq, void *dev_id) { - struct exynos_adc *info = (struct exynos_adc *)dev_id; + struct exynos_adc *info = dev_id; u32 mask = info->data->mask; /* Read value */ diff --git a/drivers/iio/adc/imx7d_adc.c b/drivers/iio/adc/imx7d_adc.c index e2241ee94783..254b29a68b9d 100644 --- a/drivers/iio/adc/imx7d_adc.c +++ b/drivers/iio/adc/imx7d_adc.c @@ -365,7 +365,7 @@ static int imx7d_adc_read_data(struct imx7d_adc *info) static irqreturn_t imx7d_adc_isr(int irq, void *dev_id) { - struct imx7d_adc *info = (struct imx7d_adc *)dev_id; + struct imx7d_adc *info = dev_id; int status; status = readl(info->regs + IMX7D_REG_ADC_INT_STATUS); diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index 3b7c4f78f37a..ebc715927e63 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -364,7 +364,7 @@ static int max1027_set_trigger_state(struct iio_trigger *trig, bool state) static irqreturn_t max1027_trigger_handler(int irq, void *private) { - struct iio_poll_func *pf = (struct iio_poll_func *)private; + struct iio_poll_func *pf = private; struct iio_dev *indio_dev = pf->indio_dev; struct max1027_state *st = iio_priv(indio_dev); diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c index 85d701291654..ae6d3324f518 100644 --- a/drivers/iio/adc/rockchip_saradc.c +++ b/drivers/iio/adc/rockchip_saradc.c @@ -109,7 +109,7 @@ static int rockchip_saradc_read_raw(struct iio_dev *indio_dev, static irqreturn_t rockchip_saradc_isr(int irq, void *dev_id) { - struct rockchip_saradc *info = (struct rockchip_saradc *)dev_id; + struct rockchip_saradc *info = dev_id; /* Read value */ info->last_val = readl_relaxed(info->regs + SARADC_DATA); diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c index a8e134fa190d..e53182510150 100644 --- a/drivers/iio/adc/sun4i-gpadc-iio.c +++ b/drivers/iio/adc/sun4i-gpadc-iio.c @@ -382,7 +382,7 @@ static int sun4i_gpadc_runtime_resume(struct device *dev) static int sun4i_gpadc_get_temp(void *data, int *temp) { - struct sun4i_gpadc_iio *info = (struct sun4i_gpadc_iio *)data; + struct sun4i_gpadc_iio *info = data; int val, scale, offset; if (sun4i_gpadc_temp_read(info->indio_dev, &val)) diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c index 228a003adeed..01fc76f7d660 100644 --- a/drivers/iio/adc/vf610_adc.c +++ b/drivers/iio/adc/vf610_adc.c @@ -584,7 +584,7 @@ static int vf610_adc_read_data(struct vf610_adc *info) static irqreturn_t vf610_adc_isr(int irq, void *dev_id) { - struct iio_dev *indio_dev = (struct iio_dev *)dev_id; + struct iio_dev *indio_dev = dev_id; struct vf610_adc *info = iio_priv(indio_dev); int coco; -- cgit v1.2.3 From 6c6c0987f94f9f5539d0f97e588383b0c5c6f458 Mon Sep 17 00:00:00 2001 From: simran singhal Date: Sat, 1 Apr 2017 19:39:21 +0530 Subject: iio: common: ms_sensors: Remove unnecessary cast on void pointer The following Coccinelle script was used to detect this: @r@ expression x; void* e; type T; identifier f; @@ ( *((T *)e) | ((T *)x)[...] | ((T*)x)->f | - (T*) e ) Signed-off-by: simran singhal Signed-off-by: Jonathan Cameron --- drivers/iio/common/ms_sensors/ms_sensors_i2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/common/ms_sensors/ms_sensors_i2c.c b/drivers/iio/common/ms_sensors/ms_sensors_i2c.c index ecf7721ecaf4..125b5ff61d19 100644 --- a/drivers/iio/common/ms_sensors/ms_sensors_i2c.c +++ b/drivers/iio/common/ms_sensors/ms_sensors_i2c.c @@ -74,7 +74,7 @@ EXPORT_SYMBOL(ms_sensors_reset); int ms_sensors_read_prom_word(void *cli, int cmd, u16 *word) { int ret; - struct i2c_client *client = (struct i2c_client *)cli; + struct i2c_client *client = cli; ret = i2c_smbus_read_word_swapped(client, cmd); if (ret < 0) { @@ -107,7 +107,7 @@ int ms_sensors_convert_and_read(void *cli, u8 conv, u8 rd, { int ret; __be32 buf = 0; - struct i2c_client *client = (struct i2c_client *)cli; + struct i2c_client *client = cli; /* Trigger conversion */ ret = i2c_smbus_write_byte(client, conv); -- cgit v1.2.3 From 4d925083e8f9f0c619fe212cfd0729c3dc1442b2 Mon Sep 17 00:00:00 2001 From: simran singhal Date: Sat, 1 Apr 2017 19:41:53 +0530 Subject: iio: dac: ad5504: Remove unnecessary cast on void pointer The following Coccinelle script was used to detect this: @r@ expression x; void* e; type T; identifier f; @@ ( *((T *)e) | ((T *)x)[...] | ((T*)x)->f | - (T*) e ) Signed-off-by: simran singhal Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5504.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c index 22a027de498a..712d86b4be09 100644 --- a/drivers/iio/dac/ad5504.c +++ b/drivers/iio/dac/ad5504.c @@ -223,7 +223,7 @@ static irqreturn_t ad5504_event_handler(int irq, void *private) 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), - iio_get_time_ns((struct iio_dev *)private)); + iio_get_time_ns(private)); return IRQ_HANDLED; } -- cgit v1.2.3 From 7ae6df68d68ed1d68d6bac11edc332da67108d8f Mon Sep 17 00:00:00 2001 From: simran singhal Date: Sat, 1 Apr 2017 19:44:15 +0530 Subject: iio: humidity: hts221: Remove unnecessary cast on void pointer The following Coccinelle script was used to detect this: @r@ expression x; void* e; type T; identifier f; @@ ( *((T *)e) | ((T *)x)[...] | ((T*)x)->f | - (T*) e ) Signed-off-by: simran singhal Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/hts221_buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/iio/humidity/hts221_buffer.c b/drivers/iio/humidity/hts221_buffer.c index 72ddcdac21a2..7d19a3da7ab7 100644 --- a/drivers/iio/humidity/hts221_buffer.c +++ b/drivers/iio/humidity/hts221_buffer.c @@ -41,7 +41,7 @@ static const struct iio_trigger_ops hts221_trigger_ops = { static irqreturn_t hts221_trigger_handler_thread(int irq, void *private) { - struct hts221_hw *hw = (struct hts221_hw *)private; + struct hts221_hw *hw = private; u8 status; int err; -- cgit v1.2.3 From 407e0b537eadc9f5e98da74b746ce84f42b89cd7 Mon Sep 17 00:00:00 2001 From: simran singhal Date: Sat, 1 Apr 2017 19:46:24 +0530 Subject: iio: imu: st_lsm6dsx: Remove unnecessary cast on void pointer The following Coccinelle script was used to detect this: @r@ expression x; void* e; type T; identifier f; @@ ( *((T *)e) | ((T *)x)[...] | ((T*)x)->f | - (T*) e ) Signed-off-by: simran singhal Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index e71eccedd340..5a748341a1d5 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -364,7 +364,7 @@ static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable) static irqreturn_t st_lsm6dsx_handler_irq(int irq, void *private) { - struct st_lsm6dsx_hw *hw = (struct st_lsm6dsx_hw *)private; + struct st_lsm6dsx_hw *hw = private; struct st_lsm6dsx_sensor *sensor; int i; @@ -388,7 +388,7 @@ static irqreturn_t st_lsm6dsx_handler_irq(int irq, void *private) static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private) { - struct st_lsm6dsx_hw *hw = (struct st_lsm6dsx_hw *)private; + struct st_lsm6dsx_hw *hw = private; int count; mutex_lock(&hw->fifo_lock); -- cgit v1.2.3 From af873b0d8f586c07ec329d6d91eaa58ba5d7c662 Mon Sep 17 00:00:00 2001 From: simran singhal Date: Sat, 1 Apr 2017 19:48:39 +0530 Subject: iio: pressure: zpa2326: Remove unnecessary cast on void pointer The following Coccinelle script was used to detect this: @r@ expression x; void* e; type T; identifier f; @@ ( *((T *)e) | ((T *)x)[...] | ((T*)x)->f | - (T*) e ) Signed-off-by: simran singhal Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/zpa2326.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c index c720c3ac0b9b..e58a0ad07477 100644 --- a/drivers/iio/pressure/zpa2326.c +++ b/drivers/iio/pressure/zpa2326.c @@ -751,7 +751,7 @@ static void zpa2326_suspend(struct iio_dev *indio_dev) */ static irqreturn_t zpa2326_handle_irq(int irq, void *data) { - struct iio_dev *indio_dev = (struct iio_dev *)data; + struct iio_dev *indio_dev = data; if (iio_buffer_enabled(indio_dev)) { /* Timestamping needed for buffered sampling only. */ @@ -790,7 +790,7 @@ static irqreturn_t zpa2326_handle_irq(int irq, void *data) */ static irqreturn_t zpa2326_handle_threaded_irq(int irq, void *data) { - struct iio_dev *indio_dev = (struct iio_dev *)data; + struct iio_dev *indio_dev = data; struct zpa2326_private *priv = iio_priv(indio_dev); unsigned int val; bool cont; -- cgit v1.2.3 From 0c8f492dd7ddf0dd381fa63b76066c0966d8dd73 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 26 Mar 2017 12:11:00 +0100 Subject: iio:imu:mpu6050 add explicit mpu9250 support The mpu9250 is a SIP containing an mpu6500 and an ak8975. If this was all there was too it there would be no need for explicit handling in the driver. Arguably the bindings would also only reflect the presence of an mpu6500 with the ak8975 hanging off it, as the kernel doesn't care that they are in one package. However, the WHOAMI value changes as well so best to add explicit support. Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt | 1 + drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 6 ++++++ drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c | 5 +++++ drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 2 ++ drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c | 1 + 5 files changed, 15 insertions(+) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt b/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt index e28cb3abb343..2b4514592f83 100644 --- a/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt +++ b/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt @@ -7,6 +7,7 @@ Required properties: "invensense,mpu6050" "invensense,mpu6500" "invensense,mpu9150" + "invensense,mpu9250" "invensense,icm20608" - reg : the I2C address of the sensor - interrupt-parent : should be the phandle for the interrupt controller diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index b9fcbf18aa99..96dabbd2f004 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -113,6 +113,12 @@ static const struct inv_mpu6050_hw hw_info[] = { .reg = ®_set_6050, .config = &chip_config_6050, }, + { + .whoami = INV_MPU9250_WHOAMI_VALUE, + .name = "MPU9250", + .reg = ®_set_6500, + .config = &chip_config_6050, + }, { .whoami = INV_ICM20608_WHOAMI_VALUE, .name = "ICM20608", diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index a8e6330cb906..64b5f5b92200 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -178,6 +178,7 @@ static const struct i2c_device_id inv_mpu_id[] = { {"mpu6050", INV_MPU6050}, {"mpu6500", INV_MPU6500}, {"mpu9150", INV_MPU9150}, + {"mpu9250", INV_MPU9250}, {"icm20608", INV_ICM20608}, {} }; @@ -197,6 +198,10 @@ static const struct of_device_id inv_of_match[] = { .compatible = "invensense,mpu9150", .data = (void *)INV_MPU9150 }, + { + .compatible = "invensense,mpu9250", + .data = (void *)INV_MPU9250 + }, { .compatible = "invensense,icm20608", .data = (void *)INV_ICM20608 diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index f0e8c5dd9fae..ef13de7a2c20 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -70,6 +70,7 @@ enum inv_devices { INV_MPU6500, INV_MPU6000, INV_MPU9150, + INV_MPU9250, INV_ICM20608, INV_NUM_PARTS }; @@ -226,6 +227,7 @@ struct inv_mpu6050_state { #define INV_MPU6050_WHOAMI_VALUE 0x68 #define INV_MPU6500_WHOAMI_VALUE 0x70 #define INV_MPU9150_WHOAMI_VALUE 0x68 +#define INV_MPU9250_WHOAMI_VALUE 0x71 #define INV_ICM20608_WHOAMI_VALUE 0xAF /* scan element definition */ diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index 6e6476dfa188..74506e5ac0db 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -82,6 +82,7 @@ static const struct spi_device_id inv_mpu_id[] = { {"mpu6000", INV_MPU6000}, {"mpu6500", INV_MPU6500}, {"mpu9150", INV_MPU9150}, + {"mpu9250", INV_MPU9250}, {"icm20608", INV_ICM20608}, {} }; -- cgit v1.2.3 From 13e283d6fc1b46d75da4076a18c419af0e05c6c7 Mon Sep 17 00:00:00 2001 From: Guru Das Srinagesh Date: Thu, 30 Mar 2017 02:55:39 -0700 Subject: staging: iio: ad9832: use 4-digit octal permissions This fixes the coding style issue of using S_IWUSR in place of 4-digit octal numbers. Issue detected by checkpatch. Signed-off-by: Guru Das Srinagesh Signed-off-by: Jonathan Cameron --- drivers/staging/iio/frequency/ad9832.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c index 425b8ab06fec..6da46ede7ee0 100644 --- a/drivers/staging/iio/frequency/ad9832.c +++ b/drivers/staging/iio/frequency/ad9832.c @@ -250,22 +250,22 @@ error_ret: * see dds.h for further information */ -static IIO_DEV_ATTR_FREQ(0, 0, S_IWUSR, NULL, ad9832_write, AD9832_FREQ0HM); -static IIO_DEV_ATTR_FREQ(0, 1, S_IWUSR, NULL, ad9832_write, AD9832_FREQ1HM); -static IIO_DEV_ATTR_FREQSYMBOL(0, S_IWUSR, NULL, ad9832_write, AD9832_FREQ_SYM); +static IIO_DEV_ATTR_FREQ(0, 0, 0200, NULL, ad9832_write, AD9832_FREQ0HM); +static IIO_DEV_ATTR_FREQ(0, 1, 0200, NULL, ad9832_write, AD9832_FREQ1HM); +static IIO_DEV_ATTR_FREQSYMBOL(0, 0200, NULL, ad9832_write, AD9832_FREQ_SYM); static IIO_CONST_ATTR_FREQ_SCALE(0, "1"); /* 1Hz */ -static IIO_DEV_ATTR_PHASE(0, 0, S_IWUSR, NULL, ad9832_write, AD9832_PHASE0H); -static IIO_DEV_ATTR_PHASE(0, 1, S_IWUSR, NULL, ad9832_write, AD9832_PHASE1H); -static IIO_DEV_ATTR_PHASE(0, 2, S_IWUSR, NULL, ad9832_write, AD9832_PHASE2H); -static IIO_DEV_ATTR_PHASE(0, 3, S_IWUSR, NULL, ad9832_write, AD9832_PHASE3H); -static IIO_DEV_ATTR_PHASESYMBOL(0, S_IWUSR, NULL, +static IIO_DEV_ATTR_PHASE(0, 0, 0200, NULL, ad9832_write, AD9832_PHASE0H); +static IIO_DEV_ATTR_PHASE(0, 1, 0200, NULL, ad9832_write, AD9832_PHASE1H); +static IIO_DEV_ATTR_PHASE(0, 2, 0200, NULL, ad9832_write, AD9832_PHASE2H); +static IIO_DEV_ATTR_PHASE(0, 3, 0200, NULL, ad9832_write, AD9832_PHASE3H); +static IIO_DEV_ATTR_PHASESYMBOL(0, 0200, NULL, ad9832_write, AD9832_PHASE_SYM); static IIO_CONST_ATTR_PHASE_SCALE(0, "0.0015339808"); /* 2PI/2^12 rad*/ -static IIO_DEV_ATTR_PINCONTROL_EN(0, S_IWUSR, NULL, +static IIO_DEV_ATTR_PINCONTROL_EN(0, 0200, NULL, ad9832_write, AD9832_PINCTRL_EN); -static IIO_DEV_ATTR_OUT_ENABLE(0, S_IWUSR, NULL, +static IIO_DEV_ATTR_OUT_ENABLE(0, 0200, NULL, ad9832_write, AD9832_OUTPUT_EN); static struct attribute *ad9832_attributes[] = { -- cgit v1.2.3 From bc82222fcca1ec3702caf98eb4a3ab0edfa6f789 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Wed, 29 Mar 2017 11:42:58 +0200 Subject: iio:adc: Driver for Linear Technology LTC2497 ADC This patch adds support for the Analog Devices / Linear Technology LTC2497 ADCs. The LTC2497 is a 16-channel (eight differential), 16-bit, high precision, delta-sigma ADC with an automatic, differential, input current cancellation front end and a 2-wire, I2C interface. Signed-off-by: Michael Hennerich Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/ltc2497.txt | 13 + MAINTAINERS | 1 + drivers/iio/adc/Kconfig | 10 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ltc2497.c | 279 +++++++++++++++++++++ 5 files changed, 304 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/ltc2497.txt create mode 100644 drivers/iio/adc/ltc2497.c (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/iio/adc/ltc2497.txt b/Documentation/devicetree/bindings/iio/adc/ltc2497.txt new file mode 100644 index 000000000000..a237ed99c0d8 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/ltc2497.txt @@ -0,0 +1,13 @@ +* Linear Technology / Analog Devices LTC2497 ADC + +Required properties: + - compatible: Must be "lltc,ltc2497" + - reg: Must contain the ADC I2C address + - vref-supply: The regulator supply for ADC reference voltage + +Example: + ltc2497: adc@76 { + compatible = "lltc,ltc2497"; + reg = <0x76>; + vref-supply = <<c2497_reg>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index a7d6f9a4c951..173043cc582f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -813,6 +813,7 @@ W: http://wiki.analog.com/ W: http://ez.analog.com/community/linux-device-drivers S: Supported F: drivers/iio/*/ad* +F: drivers/iio/adc/ltc2497* X: drivers/iio/*/adjd* F: drivers/staging/iio/*/ad* F: drivers/staging/iio/trigger/iio-trig-bfin-timer.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 38c76ea683ea..79b04693a5d1 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -348,6 +348,16 @@ config LTC2485 To compile this driver as a module, choose M here: the module will be called ltc2485. +config LTC2497 + tristate "Linear Technology LTC2497 ADC driver" + depends on I2C + help + Say yes here to build support for Linear Technology LTC2497 + 16-Bit 8-/16-Channel Delta Sigma ADC. + + To compile this driver as a module, choose M here: the module will be + called ltc2497. + config MAX1027 tristate "Maxim max1027 ADC driver" depends on SPI diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index fd8e2a9f9e8b..3d9174ab26c8 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o obj-$(CONFIG_LTC2485) += ltc2485.o +obj-$(CONFIG_LTC2497) += ltc2497.o obj-$(CONFIG_MAX1027) += max1027.o obj-$(CONFIG_MAX11100) += max11100.o obj-$(CONFIG_MAX1363) += max1363.o diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c new file mode 100644 index 000000000000..2691b10023f5 --- /dev/null +++ b/drivers/iio/adc/ltc2497.c @@ -0,0 +1,279 @@ +/* + * ltc2497.c - Driver for Analog Devices/Linear Technology LTC2497 ADC + * + * Copyright (C) 2017 Analog Devices Inc. + * + * Licensed under the GPL-2. + * + * Datasheet: http://cds.linear.com/docs/en/datasheet/2497fd.pdf + */ + +#include +#include +#include +#include +#include +#include +#include + +#define LTC2497_ENABLE 0xA0 +#define LTC2497_SGL BIT(4) +#define LTC2497_DIFF 0 +#define LTC2497_SIGN BIT(3) +#define LTC2497_CONFIG_DEFAULT LTC2497_ENABLE +#define LTC2497_CONVERSION_TIME_MS 150ULL + +struct ltc2497_st { + struct i2c_client *client; + struct regulator *ref; + ktime_t time_prev; + u8 addr_prev; + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + __be32 buf ____cacheline_aligned; +}; + +static int ltc2497_wait_conv(struct ltc2497_st *st) +{ + s64 time_elapsed; + + time_elapsed = ktime_ms_delta(ktime_get(), st->time_prev); + + if (time_elapsed < LTC2497_CONVERSION_TIME_MS) { + /* delay if conversion time not passed + * since last read or write + */ + if (msleep_interruptible( + LTC2497_CONVERSION_TIME_MS - time_elapsed)) + return -ERESTARTSYS; + + return 0; + } + + if (time_elapsed - LTC2497_CONVERSION_TIME_MS <= 0) { + /* We're in automatic mode - + * so the last reading is stil not outdated + */ + return 0; + } + + return 1; +} + +static int ltc2497_read(struct ltc2497_st *st, u8 address, int *val) +{ + struct i2c_client *client = st->client; + int ret; + + ret = ltc2497_wait_conv(st); + if (ret < 0) + return ret; + + if (ret || st->addr_prev != address) { + ret = i2c_smbus_write_byte(st->client, + LTC2497_ENABLE | address); + if (ret < 0) + return ret; + st->addr_prev = address; + if (msleep_interruptible(LTC2497_CONVERSION_TIME_MS)) + return -ERESTARTSYS; + } + ret = i2c_master_recv(client, (char *)&st->buf, 3); + if (ret < 0) { + dev_err(&client->dev, "i2c_master_recv failed\n"); + return ret; + } + st->time_prev = ktime_get(); + + /* convert and shift the result, + * and finally convert from offset binary to signed integer + */ + *val = (be32_to_cpu(st->buf) >> 14) - (1 << 17); + + return ret; +} + +static int ltc2497_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ltc2497_st *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&indio_dev->mlock); + ret = ltc2497_read(st, chan->address, val); + mutex_unlock(&indio_dev->mlock); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(st->ref); + if (ret < 0) + return ret; + + *val = ret / 1000; + *val2 = 17; + + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } +} + +#define LTC2497_CHAN(_chan, _addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_chan), \ + .address = (_addr | (_chan / 2) | ((_chan & 1) ? LTC2497_SIGN : 0)), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +#define LTC2497_CHAN_DIFF(_chan, _addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 1 : 0), \ + .channel2 = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 0 : 1),\ + .address = (_addr | _chan), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .differential = 1, \ +} + +static const struct iio_chan_spec ltc2497_channel[] = { + LTC2497_CHAN(0, LTC2497_SGL), + LTC2497_CHAN(1, LTC2497_SGL), + LTC2497_CHAN(2, LTC2497_SGL), + LTC2497_CHAN(3, LTC2497_SGL), + LTC2497_CHAN(4, LTC2497_SGL), + LTC2497_CHAN(5, LTC2497_SGL), + LTC2497_CHAN(6, LTC2497_SGL), + LTC2497_CHAN(7, LTC2497_SGL), + LTC2497_CHAN(8, LTC2497_SGL), + LTC2497_CHAN(9, LTC2497_SGL), + LTC2497_CHAN(10, LTC2497_SGL), + LTC2497_CHAN(11, LTC2497_SGL), + LTC2497_CHAN(12, LTC2497_SGL), + LTC2497_CHAN(13, LTC2497_SGL), + LTC2497_CHAN(14, LTC2497_SGL), + LTC2497_CHAN(15, LTC2497_SGL), + LTC2497_CHAN_DIFF(0, LTC2497_DIFF), + LTC2497_CHAN_DIFF(1, LTC2497_DIFF), + LTC2497_CHAN_DIFF(2, LTC2497_DIFF), + LTC2497_CHAN_DIFF(3, LTC2497_DIFF), + LTC2497_CHAN_DIFF(4, LTC2497_DIFF), + LTC2497_CHAN_DIFF(5, LTC2497_DIFF), + LTC2497_CHAN_DIFF(6, LTC2497_DIFF), + LTC2497_CHAN_DIFF(7, LTC2497_DIFF), + LTC2497_CHAN_DIFF(0, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(1, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(2, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(3, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(4, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(5, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(6, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(7, LTC2497_DIFF | LTC2497_SIGN), +}; + +static const struct iio_info ltc2497_info = { + .read_raw = ltc2497_read_raw, + .driver_module = THIS_MODULE, +}; + +static int ltc2497_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct ltc2497_st *st; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | + I2C_FUNC_SMBUS_WRITE_BYTE)) + return -EOPNOTSUPP; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + st->client = client; + + indio_dev->dev.parent = &client->dev; + indio_dev->name = id->name; + indio_dev->info = <c2497_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ltc2497_channel; + indio_dev->num_channels = ARRAY_SIZE(ltc2497_channel); + + st->ref = devm_regulator_get(&client->dev, "vref"); + if (IS_ERR(st->ref)) + return PTR_ERR(st->ref); + + ret = regulator_enable(st->ref); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte(st->client, LTC2497_CONFIG_DEFAULT); + if (ret < 0) + goto err_regulator_disable; + + st->addr_prev = LTC2497_CONFIG_DEFAULT; + st->time_prev = ktime_get(); + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto err_regulator_disable; + + return 0; + +err_regulator_disable: + regulator_disable(st->ref); + + return ret; +} + +static int ltc2497_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ltc2497_st *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + regulator_disable(st->ref); + + return 0; +} + +static const struct i2c_device_id ltc2497_id[] = { + { "ltc2497", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ltc2497_id); + +static const struct of_device_id ltc2497_of_match[] = { + { .compatible = "lltc,ltc2497", }, + {}, +}; +MODULE_DEVICE_TABLE(of, ltc2497_of_match); + +static struct i2c_driver ltc2497_driver = { + .driver = { + .name = "ltc2497", + .of_match_table = of_match_ptr(ltc2497_of_match), + }, + .probe = ltc2497_probe, + .remove = ltc2497_remove, + .id_table = ltc2497_id, +}; +module_i2c_driver(ltc2497_driver); + +MODULE_AUTHOR("Michael Hennerich "); +MODULE_DESCRIPTION("Linear Technology LTC2497 ADC driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From be2d73208606bc5d52e60dea223da77f49d58340 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 28 Mar 2017 12:07:23 +0200 Subject: iio: adc: sun4i: add THERMAL dependency With CONFIG_THERMAL=m and CONFIG_SUN4I_GPADC=y, we get a link error from calling devm_thermal_zone_of_sensor_register: drivers/iio/built-in.o: In function `sun4i_gpadc_probe': :(.text+0x1c284): undefined reference to `devm_thermal_zone_of_sensor_register' This adds a Kconfig dependency to ensure we can only have the ADC driver as built-in when this function is also built-in, or when we see the empty stub implementation. When the thermal code is a module, we can still build the adc driver as a module, too. Signed-off-by: Arnd Bergmann Fixes: d1caa99055382 ("iio: adc: add support for Allwinner SoCs ADC") Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 79b04693a5d1..b7b3a9a80043 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -595,6 +595,7 @@ config SUN4I_GPADC tristate "Support for the Allwinner SoCs GPADC" depends on IIO depends on MFD_SUN4I_GPADC + depends on THERMAL || !THERMAL_OF help Say yes here to build support for Allwinner (A10, A13 and A31) SoCs GPADC. This ADC provides 4 channels which can be used as an ADC or as -- cgit v1.2.3 From 2ccc15036d812e75f334c0fb813104ef765a2c77 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 2 Apr 2017 14:58:45 +0200 Subject: iio: imu: st_lsm6dsx: do not apply ODR configuration in write_raw handler This patch allows to avoid a transitory that occurs when a given sensor has been already enabled (e.g. gyroscope) and the user is configuring the sample frequency of the other one (e.g. accelerometer). The transitory lasts until the accelerometer is enabled. During that time slice the gyroscope ODR is incorrectly modified as well. At the end of the transitory both sensors work at the right frequency. Fix it introducing st_lsm6dsx_check_odr() routine to check ODR consistency in write_raw handler in order to apply frequency configuration just in st_lsm6dsx_set_odr() Fixes: 290a6ce11d93 (iio: imu: add support to lsm6dsx driver) Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 41 ++++++++++++++++++---------- 1 file changed, 26 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index c4332232ad71..f80a3d4ff977 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -308,32 +308,40 @@ static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor, return 0; } -static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr) +static int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, + u8 *val) { - enum st_lsm6dsx_sensor_id id = sensor->id; - int i, err; - u8 val; + int i; for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++) - if (st_lsm6dsx_odr_table[id].odr_avl[i].hz == odr) + if (st_lsm6dsx_odr_table[sensor->id].odr_avl[i].hz == odr) break; if (i == ST_LSM6DSX_ODR_LIST_SIZE) return -EINVAL; - val = st_lsm6dsx_odr_table[id].odr_avl[i].val; - err = st_lsm6dsx_write_with_mask(sensor->hw, - st_lsm6dsx_odr_table[id].reg.addr, - st_lsm6dsx_odr_table[id].reg.mask, - val); - if (err < 0) - return err; - + *val = st_lsm6dsx_odr_table[sensor->id].odr_avl[i].val; sensor->odr = odr; return 0; } +static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr) +{ + enum st_lsm6dsx_sensor_id id = sensor->id; + int err; + u8 val; + + err = st_lsm6dsx_check_odr(sensor, odr, &val); + if (err < 0) + return err; + + return st_lsm6dsx_write_with_mask(sensor->hw, + st_lsm6dsx_odr_table[id].reg.addr, + st_lsm6dsx_odr_table[id].reg.mask, + val); +} + int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor) { int err; @@ -436,9 +444,12 @@ static int st_lsm6dsx_write_raw(struct iio_dev *iio_dev, case IIO_CHAN_INFO_SCALE: err = st_lsm6dsx_set_full_scale(sensor, val2); break; - case IIO_CHAN_INFO_SAMP_FREQ: - err = st_lsm6dsx_set_odr(sensor, val); + case IIO_CHAN_INFO_SAMP_FREQ: { + u8 data; + + err = st_lsm6dsx_check_odr(sensor, val, &data); break; + } default: err = -EINVAL; break; -- cgit v1.2.3