From 1911f48de0d90b30a424906b7343b20dac90e3d9 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 19 Jun 2017 08:22:25 +0200 Subject: iio: accel: bmc150: Add support for BOSC0200 ACPI device id Add support for the BOSC0200 ACPI device id used on some x86 tablets. note driver_data is not set to a specific model, driver_data is not used anyways (instead detection is done on the chip_id reg) and the 2 tablets with a BOSC0200 ACPI device id I've have 2 different chips, one has a BMA250E, the other a BMA222E. Signed-off-by: Hans de Goede Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bmc150-accel-i2c.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/bmc150-accel-i2c.c b/drivers/iio/accel/bmc150-accel-i2c.c index 8ca8041267ef..f85014fbaa12 100644 --- a/drivers/iio/accel/bmc150-accel-i2c.c +++ b/drivers/iio/accel/bmc150-accel-i2c.c @@ -64,6 +64,7 @@ static const struct acpi_device_id bmc150_accel_acpi_match[] = { {"BMA250E", bma250e}, {"BMA222E", bma222e}, {"BMA0280", bma280}, + {"BOSC0200"}, { }, }; MODULE_DEVICE_TABLE(acpi, bmc150_accel_acpi_match); -- cgit v1.2.3 From 45a4e4220bf4927e321e18750e47c576bf62b000 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 19 Jun 2017 22:04:30 +0200 Subject: iio: gyro: st_gyro: fix L3GD20H support Add proper support for L3GD20H gyroscope sensor. In particular: - use L3GD20H as device name instead of L3GD20 - fix available full scales - fix available sample frequencies Note that the original patch listed first below introduced broken support for this part. The second patch drops the support as it didn't work. This new patch brings in working support. Fixes: 9444a300c2be (IIO: Add support for L3GD20H gyroscope) Fixes: a0657716416f ("iio:gyro: bug on L3GD20H gyroscope support") Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/st_gyro.h | 1 + drivers/iio/gyro/st_gyro_core.c | 13 +++++++------ drivers/iio/gyro/st_gyro_i2c.c | 5 +++++ drivers/iio/gyro/st_gyro_spi.c | 1 + 4 files changed, 14 insertions(+), 6 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/gyro/st_gyro.h b/drivers/iio/gyro/st_gyro.h index a5c5c4e29add..48923ae6ac3b 100644 --- a/drivers/iio/gyro/st_gyro.h +++ b/drivers/iio/gyro/st_gyro.h @@ -19,6 +19,7 @@ #define LSM330DL_GYRO_DEV_NAME "lsm330dl_gyro" #define LSM330DLC_GYRO_DEV_NAME "lsm330dlc_gyro" #define L3GD20_GYRO_DEV_NAME "l3gd20" +#define L3GD20H_GYRO_DEV_NAME "l3gd20h" #define L3G4IS_GYRO_DEV_NAME "l3g4is_ui" #define LSM330_GYRO_DEV_NAME "lsm330_gyro" #define LSM9DS0_GYRO_DEV_NAME "lsm9ds0_gyro" diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index 2a42b3d583e8..e366422e8512 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -35,6 +35,7 @@ #define ST_GYRO_DEFAULT_OUT_Z_L_ADDR 0x2c /* FULLSCALE */ +#define ST_GYRO_FS_AVL_245DPS 245 #define ST_GYRO_FS_AVL_250DPS 250 #define ST_GYRO_FS_AVL_500DPS 500 #define ST_GYRO_FS_AVL_2000DPS 2000 @@ -196,17 +197,17 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { .wai = 0xd7, .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, .sensors_supported = { - [0] = L3GD20_GYRO_DEV_NAME, + [0] = L3GD20H_GYRO_DEV_NAME, }, .ch = (struct iio_chan_spec *)st_gyro_16bit_channels, .odr = { .addr = 0x20, .mask = 0xc0, .odr_avl = { - { .hz = 95, .value = 0x00, }, - { .hz = 190, .value = 0x01, }, - { .hz = 380, .value = 0x02, }, - { .hz = 760, .value = 0x03, }, + { .hz = 100, .value = 0x00, }, + { .hz = 200, .value = 0x01, }, + { .hz = 400, .value = 0x02, }, + { .hz = 800, .value = 0x03, }, }, }, .pw = { @@ -224,7 +225,7 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { .mask = 0x30, .fs_avl = { [0] = { - .num = ST_GYRO_FS_AVL_250DPS, + .num = ST_GYRO_FS_AVL_245DPS, .value = 0x00, .gain = IIO_DEGREE_TO_RAD(8750), }, diff --git a/drivers/iio/gyro/st_gyro_i2c.c b/drivers/iio/gyro/st_gyro_i2c.c index 40056b821036..3f628746cb93 100644 --- a/drivers/iio/gyro/st_gyro_i2c.c +++ b/drivers/iio/gyro/st_gyro_i2c.c @@ -40,6 +40,10 @@ static const struct of_device_id st_gyro_of_match[] = { .compatible = "st,l3gd20-gyro", .data = L3GD20_GYRO_DEV_NAME, }, + { + .compatible = "st,l3gd20h-gyro", + .data = L3GD20H_GYRO_DEV_NAME, + }, { .compatible = "st,l3g4is-gyro", .data = L3G4IS_GYRO_DEV_NAME, @@ -95,6 +99,7 @@ static const struct i2c_device_id st_gyro_id_table[] = { { LSM330DL_GYRO_DEV_NAME }, { LSM330DLC_GYRO_DEV_NAME }, { L3GD20_GYRO_DEV_NAME }, + { L3GD20H_GYRO_DEV_NAME }, { L3G4IS_GYRO_DEV_NAME }, { LSM330_GYRO_DEV_NAME }, { LSM9DS0_GYRO_DEV_NAME }, diff --git a/drivers/iio/gyro/st_gyro_spi.c b/drivers/iio/gyro/st_gyro_spi.c index fbf2faed501c..fa14d8f2170d 100644 --- a/drivers/iio/gyro/st_gyro_spi.c +++ b/drivers/iio/gyro/st_gyro_spi.c @@ -52,6 +52,7 @@ static const struct spi_device_id st_gyro_id_table[] = { { LSM330DL_GYRO_DEV_NAME }, { LSM330DLC_GYRO_DEV_NAME }, { L3GD20_GYRO_DEV_NAME }, + { L3GD20H_GYRO_DEV_NAME }, { L3G4IS_GYRO_DEV_NAME }, { LSM330_GYRO_DEV_NAME }, { LSM9DS0_GYRO_DEV_NAME }, -- cgit v1.2.3 From 8f4c9575a8e29f6ee0a45a1288eb4af9f215279d Mon Sep 17 00:00:00 2001 From: Michael Stecklein Date: Mon, 19 Jun 2017 11:01:05 -0500 Subject: iio: humidity: hdc100x: document compatible HDC10xx devices Include datasheet links, add i2c_device_id entries, and update kconfig help for compatible HDC10xx devices: HDC1000, HDC1008, HDC1010, HDC1050, and HDC1080. Signed-off-by: Michael Stecklein Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/Kconfig | 3 ++- drivers/iio/humidity/hdc100x.c | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/humidity/Kconfig b/drivers/iio/humidity/Kconfig index 14b9ce453d9d..2c0fc9a400b8 100644 --- a/drivers/iio/humidity/Kconfig +++ b/drivers/iio/humidity/Kconfig @@ -31,7 +31,8 @@ config HDC100X select IIO_TRIGGERED_BUFFER help Say yes here to build support for the Texas Instruments - HDC1000 and HDC1008 relative humidity and temperature sensors. + HDC1000, HDC1008, HDC1010, HDC1050, and HDC1080 relative + humidity and temperature sensors. To compile this driver as a module, choose M here: the module will be called hdc100x. diff --git a/drivers/iio/humidity/hdc100x.c b/drivers/iio/humidity/hdc100x.c index aa17115f54c9..6778bc73d5c4 100644 --- a/drivers/iio/humidity/hdc100x.c +++ b/drivers/iio/humidity/hdc100x.c @@ -13,6 +13,12 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * + * Datasheets: + * http://www.ti.com/product/HDC1000/datasheet + * http://www.ti.com/product/HDC1008/datasheet + * http://www.ti.com/product/HDC1010/datasheet + * http://www.ti.com/product/HDC1050/datasheet + * http://www.ti.com/product/HDC1080/datasheet */ #include @@ -414,6 +420,11 @@ static int hdc100x_remove(struct i2c_client *client) static const struct i2c_device_id hdc100x_id[] = { { "hdc100x", 0 }, + { "hdc1000", 0 }, + { "hdc1008", 0 }, + { "hdc1010", 0 }, + { "hdc1050", 0 }, + { "hdc1080", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, hdc100x_id); -- cgit v1.2.3 From a2d80de873de0a01c14aae4d85c5558be23c42d1 Mon Sep 17 00:00:00 2001 From: Michael Stecklein Date: Mon, 19 Jun 2017 11:01:06 -0500 Subject: iio: humidity: hdc100x: add match table and device id's Add of_match_table and point it to a list of compatible device tree device id's. Signed-off-by: Michael Stecklein Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/hdc100x.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/humidity/hdc100x.c b/drivers/iio/humidity/hdc100x.c index 6778bc73d5c4..7851bd90ef64 100644 --- a/drivers/iio/humidity/hdc100x.c +++ b/drivers/iio/humidity/hdc100x.c @@ -429,9 +429,20 @@ static const struct i2c_device_id hdc100x_id[] = { }; MODULE_DEVICE_TABLE(i2c, hdc100x_id); +static const struct of_device_id hdc100x_dt_ids[] = { + { .compatible = "ti,hdc1000" }, + { .compatible = "ti,hdc1008" }, + { .compatible = "ti,hdc1010" }, + { .compatible = "ti,hdc1050" }, + { .compatible = "ti,hdc1080" }, + { } +}; +MODULE_DEVICE_TABLE(of, hdc100x_dt_ids); + static struct i2c_driver hdc100x_driver = { .driver = { .name = "hdc100x", + .of_match_table = of_match_ptr(hdc100x_dt_ids), }, .probe = hdc100x_probe, .remove = hdc100x_remove, -- cgit v1.2.3 From edd80a5f511c5c9f01042a12588be5e5af4efab7 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 17 Jun 2017 18:00:16 -0300 Subject: iio: adc: Kconfig: Append vendor name for IMX7D_ADC To keep IMX7D_ADC text consistent with other iio/adc entries, add the vendor name to the driver menu text. Signed-off-by: Fabio Estevam Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 614fa41559b1..b56c4a1989a7 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -322,7 +322,7 @@ config INA2XX_ADC This driver is mutually exclusive with the HWMON version. config IMX7D_ADC - tristate "IMX7D ADC driver" + tristate "Freescale IMX7D ADC driver" depends on ARCH_MXC || COMPILE_TEST depends on HAS_IOMEM help -- cgit v1.2.3 From 5e1a1da0f8c9142902a97fe48b008860f5c7a305 Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Thu, 15 Jun 2017 16:24:57 +0300 Subject: iio: adc: at91-sama5d2_adc: add hw trigger and buffer support Added support for the external hardware trigger on pin ADTRG, integrated the three possible edges into the driver and created buffer management for data retrieval Signed-off-by: Eugen Hristev Acked-by: Ludovic Desroches Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 237 ++++++++++++++++++++++++++++++++++++- 1 file changed, 232 insertions(+), 5 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index e10dca3ed74b..1773a5dc06b2 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -25,6 +25,10 @@ #include #include #include +#include +#include +#include +#include #include /* Control Register */ @@ -132,6 +136,17 @@ #define AT91_SAMA5D2_PRESSR 0xbc /* Trigger Register */ #define AT91_SAMA5D2_TRGR 0xc0 +/* Mask for TRGMOD field of TRGR register */ +#define AT91_SAMA5D2_TRGR_TRGMOD_MASK GENMASK(2, 0) +/* No trigger, only software trigger can start conversions */ +#define AT91_SAMA5D2_TRGR_TRGMOD_NO_TRIGGER 0 +/* Trigger Mode external trigger rising edge */ +#define AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_RISE 1 +/* Trigger Mode external trigger falling edge */ +#define AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_FALL 2 +/* Trigger Mode external trigger any edge */ +#define AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_ANY 3 + /* Correction Select Register */ #define AT91_SAMA5D2_COSR 0xd0 /* Correction Value Register */ @@ -145,14 +160,29 @@ /* Version Register */ #define AT91_SAMA5D2_VERSION 0xfc +#define AT91_SAMA5D2_HW_TRIG_CNT 3 +#define AT91_SAMA5D2_SINGLE_CHAN_CNT 12 +#define AT91_SAMA5D2_DIFF_CHAN_CNT 6 + +/* + * Maximum number of bytes to hold conversion from all channels + * plus the timestamp + */ +#define AT91_BUFFER_MAX_BYTES ((AT91_SAMA5D2_SINGLE_CHAN_CNT + \ + AT91_SAMA5D2_DIFF_CHAN_CNT) * 2 + 8) + +#define AT91_BUFFER_MAX_HWORDS (AT91_BUFFER_MAX_BYTES / 2) + #define AT91_SAMA5D2_CHAN_SINGLE(num, addr) \ { \ .type = IIO_VOLTAGE, \ .channel = num, \ .address = addr, \ + .scan_index = num, \ .scan_type = { \ .sign = 'u', \ .realbits = 12, \ + .storagebits = 16, \ }, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ @@ -168,9 +198,11 @@ .channel = num, \ .channel2 = num2, \ .address = addr, \ + .scan_index = num + AT91_SAMA5D2_SINGLE_CHAN_CNT, \ .scan_type = { \ .sign = 's', \ .realbits = 12, \ + .storagebits = 16, \ }, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ @@ -188,6 +220,12 @@ struct at91_adc_soc_info { unsigned max_sample_rate; }; +struct at91_adc_trigger { + char *name; + unsigned int trgmod_value; + unsigned int edge_type; +}; + struct at91_adc_state { void __iomem *base; int irq; @@ -195,11 +233,14 @@ struct at91_adc_state { struct regulator *reg; struct regulator *vref; int vref_uv; + struct iio_trigger *trig; + const struct at91_adc_trigger *selected_trig; const struct iio_chan_spec *chan; bool conversion_done; u32 conversion_value; struct at91_adc_soc_info soc_info; wait_queue_head_t wq_data_available; + u16 buffer[AT91_BUFFER_MAX_HWORDS]; /* * lock to prevent concurrent 'single conversion' requests through * sysfs. @@ -207,6 +248,24 @@ struct at91_adc_state { struct mutex lock; }; +static const struct at91_adc_trigger at91_adc_trigger_list[] = { + { + .name = "external_rising", + .trgmod_value = AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_RISE, + .edge_type = IRQ_TYPE_EDGE_RISING, + }, + { + .name = "external_falling", + .trgmod_value = AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_FALL, + .edge_type = IRQ_TYPE_EDGE_FALLING, + }, + { + .name = "external_any", + .trgmod_value = AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_ANY, + .edge_type = IRQ_TYPE_EDGE_BOTH, + }, +}; + static const struct iio_chan_spec at91_adc_channels[] = { AT91_SAMA5D2_CHAN_SINGLE(0, 0x50), AT91_SAMA5D2_CHAN_SINGLE(1, 0x54), @@ -226,8 +285,128 @@ static const struct iio_chan_spec at91_adc_channels[] = { AT91_SAMA5D2_CHAN_DIFF(6, 7, 0x68), AT91_SAMA5D2_CHAN_DIFF(8, 9, 0x70), AT91_SAMA5D2_CHAN_DIFF(10, 11, 0x78), + IIO_CHAN_SOFT_TIMESTAMP(AT91_SAMA5D2_SINGLE_CHAN_CNT + + AT91_SAMA5D2_DIFF_CHAN_CNT + 1), +}; + +static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state) +{ + struct iio_dev *indio = iio_trigger_get_drvdata(trig); + struct at91_adc_state *st = iio_priv(indio); + u32 status = at91_adc_readl(st, AT91_SAMA5D2_TRGR); + u8 bit; + + /* clear TRGMOD */ + status &= ~AT91_SAMA5D2_TRGR_TRGMOD_MASK; + + if (state) + status |= st->selected_trig->trgmod_value; + + /* set/unset hw trigger */ + at91_adc_writel(st, AT91_SAMA5D2_TRGR, status); + + for_each_set_bit(bit, indio->active_scan_mask, indio->num_channels) { + struct iio_chan_spec const *chan = indio->channels + bit; + + if (state) { + at91_adc_writel(st, AT91_SAMA5D2_CHER, + BIT(chan->channel)); + at91_adc_writel(st, AT91_SAMA5D2_IER, + BIT(chan->channel)); + } else { + at91_adc_writel(st, AT91_SAMA5D2_IDR, + BIT(chan->channel)); + at91_adc_writel(st, AT91_SAMA5D2_CHDR, + BIT(chan->channel)); + } + } + + return 0; +} + +static int at91_adc_reenable_trigger(struct iio_trigger *trig) +{ + struct iio_dev *indio = iio_trigger_get_drvdata(trig); + struct at91_adc_state *st = iio_priv(indio); + + enable_irq(st->irq); + + /* Needed to ACK the DRDY interruption */ + at91_adc_readl(st, AT91_SAMA5D2_LCDR); + return 0; +} + +static const struct iio_trigger_ops at91_adc_trigger_ops = { + .owner = THIS_MODULE, + .set_trigger_state = &at91_adc_configure_trigger, + .try_reenable = &at91_adc_reenable_trigger, }; +static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *indio, + char *trigger_name) +{ + struct iio_trigger *trig; + int ret; + + trig = devm_iio_trigger_alloc(&indio->dev, "%s-dev%d-%s", indio->name, + indio->id, trigger_name); + if (!trig) + return NULL; + + trig->dev.parent = indio->dev.parent; + iio_trigger_set_drvdata(trig, indio); + trig->ops = &at91_adc_trigger_ops; + + ret = devm_iio_trigger_register(&indio->dev, trig); + if (ret) + return ERR_PTR(ret); + + return trig; +} + +static int at91_adc_trigger_init(struct iio_dev *indio) +{ + struct at91_adc_state *st = iio_priv(indio); + + st->trig = at91_adc_allocate_trigger(indio, st->selected_trig->name); + if (IS_ERR(st->trig)) { + dev_err(&indio->dev, + "could not allocate trigger\n"); + return PTR_ERR(st->trig); + } + + return 0; +} + +static irqreturn_t at91_adc_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio = pf->indio_dev; + struct at91_adc_state *st = iio_priv(indio); + int i = 0; + u8 bit; + + for_each_set_bit(bit, indio->active_scan_mask, indio->num_channels) { + struct iio_chan_spec const *chan = indio->channels + bit; + + st->buffer[i] = at91_adc_readl(st, chan->address); + i++; + } + + iio_push_to_buffers_with_timestamp(indio, st->buffer, pf->timestamp); + + iio_trigger_notify_done(indio->trig); + + return IRQ_HANDLED; +} + +static int at91_adc_buffer_init(struct iio_dev *indio) +{ + return devm_iio_triggered_buffer_setup(&indio->dev, indio, + &iio_pollfunc_store_time, + &at91_adc_trigger_handler, NULL); +} + static unsigned at91_adc_startup_time(unsigned startup_time_min, unsigned adc_clk_khz) { @@ -293,14 +472,18 @@ static irqreturn_t at91_adc_interrupt(int irq, void *private) u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR); u32 imr = at91_adc_readl(st, AT91_SAMA5D2_IMR); - if (status & imr) { + if (!(status & imr)) + return IRQ_NONE; + + if (iio_buffer_enabled(indio)) { + disable_irq_nosync(irq); + iio_trigger_poll(indio->trig); + } else { st->conversion_value = at91_adc_readl(st, st->chan->address); st->conversion_done = true; wake_up_interruptible(&st->wq_data_available); - return IRQ_HANDLED; } - - return IRQ_NONE; + return IRQ_HANDLED; } static int at91_adc_read_raw(struct iio_dev *indio_dev, @@ -313,6 +496,11 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: + /* we cannot use software trigger if hw trigger enabled */ + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + mutex_lock(&st->lock); st->chan = chan; @@ -344,6 +532,8 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev, at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel)); mutex_unlock(&st->lock); + + iio_device_release_direct_mode(indio_dev); return ret; case IIO_CHAN_INFO_SCALE: @@ -391,7 +581,8 @@ static int at91_adc_probe(struct platform_device *pdev) struct iio_dev *indio_dev; struct at91_adc_state *st; struct resource *res; - int ret; + int ret, i; + u32 edge_type; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); if (!indio_dev) @@ -432,6 +623,27 @@ static int at91_adc_probe(struct platform_device *pdev) return ret; } + ret = of_property_read_u32(pdev->dev.of_node, + "atmel,trigger-edge-type", &edge_type); + if (ret) { + dev_err(&pdev->dev, + "invalid or missing value for atmel,trigger-edge-type\n"); + return ret; + } + + st->selected_trig = NULL; + + for (i = 0; i < AT91_SAMA5D2_HW_TRIG_CNT; i++) + if (at91_adc_trigger_list[i].edge_type == edge_type) { + st->selected_trig = &at91_adc_trigger_list[i]; + break; + } + + if (!st->selected_trig) { + dev_err(&pdev->dev, "invalid external trigger edge value\n"); + return -EINVAL; + } + init_waitqueue_head(&st->wq_data_available); mutex_init(&st->lock); @@ -499,10 +711,25 @@ static int at91_adc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, indio_dev); + ret = at91_adc_buffer_init(indio_dev); + if (ret < 0) { + dev_err(&pdev->dev, "couldn't initialize the buffer.\n"); + goto per_clk_disable_unprepare; + } + + ret = at91_adc_trigger_init(indio_dev); + if (ret < 0) { + dev_err(&pdev->dev, "couldn't setup the triggers.\n"); + goto per_clk_disable_unprepare; + } + ret = iio_device_register(indio_dev); if (ret < 0) goto per_clk_disable_unprepare; + dev_info(&pdev->dev, "setting up trigger as %s\n", + st->selected_trig->name); + dev_info(&pdev->dev, "version: %x\n", readl_relaxed(st->base + AT91_SAMA5D2_VERSION)); -- cgit v1.2.3 From 55eaac2b9980a8a24f0f7ad6dde8a48e1795fbd9 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 13 Jun 2017 21:22:22 +0530 Subject: iio: temperature: tsys01: Add OF match table Add of_match_table for Measurement Specialties TSYS01 temperature sensor Signed-off-by: Manivannan Sadhasivam Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/tsys01.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/temperature/tsys01.c b/drivers/iio/temperature/tsys01.c index 3e60c6189d98..d8aa211d76e4 100644 --- a/drivers/iio/temperature/tsys01.c +++ b/drivers/iio/temperature/tsys01.c @@ -214,11 +214,18 @@ static const struct i2c_device_id tsys01_id[] = { }; MODULE_DEVICE_TABLE(i2c, tsys01_id); +static const struct of_device_id tsys01_of_match[] = { + { .compatible = "meas,tsys01", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tsys01_of_match); + static struct i2c_driver tsys01_driver = { .probe = tsys01_i2c_probe, .id_table = tsys01_id, .driver = { .name = "tsys01", + .of_match_table = of_match_ptr(tsys01_of_match), }, }; -- cgit v1.2.3 From e5c2ce6bacfa98fdfc6fe984833656fabaeecd2b Mon Sep 17 00:00:00 2001 From: Stefan Brüns Date: Wed, 24 May 2017 02:09:07 +0200 Subject: iio: adc: Fix polling of INA219 conversion ready flag While the INA226 has a conversion ready flag (CVRF) in the R/W Mask/Enable register with read-to-clear semantics, the corresponding bit of the INA219 (CNVR) is part of the bus voltage register. The flag is cleared by reading the power register. Signed-off-by: Stefan Brüns Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ina2xx-adc.c | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 232c0b80d658..68884d26b50c 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -44,6 +44,7 @@ #define INA226_MASK_ENABLE 0x06 #define INA226_CVRF BIT(3) +#define INA219_CNVR BIT(1) #define INA2XX_MAX_REGISTERS 8 @@ -592,6 +593,7 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev) int bit, ret, i = 0; s64 time_a, time_b; unsigned int alert; + int cnvr_need_clear = 0; time_a = iio_get_time_ns(indio_dev); @@ -603,22 +605,30 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev) * we check the ConVersionReadyFlag. * On hardware that supports using the ALERT pin to toggle a * GPIO a triggered buffer could be used instead. - * For now, we pay for that extra read of the ALERT register + * For now, we do an extra read of the MASK_ENABLE register (INA226) + * resp. the BUS_VOLTAGE register (INA219). */ if (!chip->allow_async_readout) do { - ret = regmap_read(chip->regmap, INA226_MASK_ENABLE, - &alert); + if (chip->config->chip_id == ina226) { + ret = regmap_read(chip->regmap, + INA226_MASK_ENABLE, &alert); + alert &= INA226_CVRF; + } else { + ret = regmap_read(chip->regmap, + INA2XX_BUS_VOLTAGE, &alert); + alert &= INA219_CNVR; + cnvr_need_clear = alert; + } + if (ret < 0) return ret; - alert &= INA226_CVRF; } while (!alert); /* - * Single register reads: bulk_read will not work with ina226 - * as there is no auto-increment of the address register for - * data length longer than 16bits. + * Single register reads: bulk_read will not work with ina226/219 + * as there is no auto-increment of the register pointer. */ for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { @@ -630,6 +640,18 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev) return ret; data[i++] = val; + + if (INA2XX_SHUNT_VOLTAGE + bit == INA2XX_POWER) + cnvr_need_clear = 0; + } + + /* Dummy read on INA219 power register to clear CNVR flag */ + if (cnvr_need_clear && chip->config->chip_id == ina219) { + unsigned int val; + + ret = regmap_read(chip->regmap, INA2XX_POWER, &val); + if (ret < 0) + return ret; } time_b = iio_get_time_ns(indio_dev); -- cgit v1.2.3 From 53d7d8139f652f87e1d9aa877007e757d08fc59e Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 13 Jun 2017 00:05:08 +0900 Subject: iio: light: tcs3472: fix ATIME register write The integration time is controlled by the ATIME register only. However, this register is written by i2c_smbus_write_word_data() in write_raw(). We actually don't need to write a subsequent register. So just use i2c_smbus_write_byte_data() instead. Cc: Peter Meerwald Cc: Jonathan Cameron Signed-off-by: Akinobu Mita Acked-by: Peter Meerwald-Stadler Signed-off-by: Jonathan Cameron --- drivers/iio/light/tcs3472.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index 3aa71e34ae28..a9e153b3e1f9 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -169,7 +169,7 @@ static int tcs3472_write_raw(struct iio_dev *indio_dev, for (i = 0; i < 256; i++) { if (val2 == (256 - i) * 2400) { data->atime = i; - return i2c_smbus_write_word_data( + return i2c_smbus_write_byte_data( data->client, TCS3472_ATIME, data->atime); } -- cgit v1.2.3 From 13718564167da80b6169809185b4c5a32497d0c1 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Tue, 20 Jun 2017 19:42:45 +0200 Subject: iio: magnetometer: st_magn_core: enable multiread by default for LIS3MDL Enable multiread by default for LIS3MDL since using I2C interface the subaddr Msb is used to automatically increase the slave address during multiple data read/write. In the same way, using SPI interface, bit 1 in register address is used to enable auto-increment of the slave address in multiple read/write. Fixes: 872e79add756 (iio:magn: Add STMicroelectronics magn driver) Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/st_magn_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index 8e1b0861fbe4..3573636bad8e 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -315,7 +315,7 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = { }, }, }, - .multi_read_bit = false, + .multi_read_bit = true, .bootime = 2, }, { -- cgit v1.2.3 From 250bbbdbed93ca27c9f4d445c960a87d9f7e2044 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Tue, 20 Jun 2017 21:52:08 +0200 Subject: iio: common: st_sensors: move st_sensors_of_i2c_probe() in common code Move st_sensors_of_i2c_probe() in st_sensors_core and rename it in st_sensors_of_name_probe(). That change is necessary to add device-tree support in spi code otherwise the rest of the autodetection will fail since spi->modalias (and indio_dev->name) will be set using compatible string value that differs from standard sensor name Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_i2c.c | 3 ++- drivers/iio/common/st_sensors/st_sensors_core.c | 31 +++++++++++++++++++++++++ drivers/iio/common/st_sensors/st_sensors_i2c.c | 29 ----------------------- drivers/iio/gyro/st_gyro_i2c.c | 3 ++- drivers/iio/magnetometer/st_magn_i2c.c | 3 ++- drivers/iio/pressure/st_pressure_i2c.c | 3 ++- include/linux/iio/common/st_sensors.h | 12 ++++++++++ include/linux/iio/common/st_sensors_i2c.h | 10 -------- 8 files changed, 51 insertions(+), 43 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index 543f0ad7fd7e..ac67826135be 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -144,7 +144,8 @@ static int st_accel_i2c_probe(struct i2c_client *client, adata = iio_priv(indio_dev); if (client->dev.of_node) { - st_sensors_of_i2c_probe(client, st_accel_of_match); + st_sensors_of_name_probe(&client->dev, st_accel_of_match, + client->name, sizeof(client->name)); } else if (ACPI_HANDLE(&client->dev)) { ret = st_sensors_match_acpi_device(&client->dev); if ((ret < 0) || (ret >= ST_ACCEL_MAX)) diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index 79c8c7cd70d5..274868100fd0 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -345,6 +346,36 @@ static struct st_sensors_platform_data *st_sensors_of_probe(struct device *dev, return pdata; } + +/** + * st_sensors_of_name_probe() - device tree probe for ST sensor name + * @dev: driver model representation of the device. + * @match: the OF match table for the device, containing compatible strings + * but also a .data field with the corresponding internal kernel name + * used by this sensor. + * @name: device name buffer reference. + * @len: device name buffer length. + * + * In effect this function matches a compatible string to an internal kernel + * name for a certain sensor device, so that the rest of the autodetection can + * rely on that name from this point on. I2C/SPI devices will be renamed + * to match the internal kernel convention. + */ +void st_sensors_of_name_probe(struct device *dev, + const struct of_device_id *match, + char *name, int len) +{ + const struct of_device_id *of_id; + + of_id = of_match_device(match, dev); + if (!of_id || !of_id->data) + return; + + /* The name from the OF match takes precedence if present */ + strncpy(name, of_id->data, len); + name[len - 1] = '\0'; +} +EXPORT_SYMBOL(st_sensors_of_name_probe); #else static struct st_sensors_platform_data *st_sensors_of_probe(struct device *dev, struct st_sensors_platform_data *defdata) diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c index c83df4dbfcd7..b81e48e9f27e 100644 --- a/drivers/iio/common/st_sensors/st_sensors_i2c.c +++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c @@ -79,35 +79,6 @@ void st_sensors_i2c_configure(struct iio_dev *indio_dev, } EXPORT_SYMBOL(st_sensors_i2c_configure); -#ifdef CONFIG_OF -/** - * st_sensors_of_i2c_probe() - device tree probe for ST I2C sensors - * @client: the I2C client device for the sensor - * @match: the OF match table for the device, containing compatible strings - * but also a .data field with the corresponding internal kernel name - * used by this sensor. - * - * In effect this function matches a compatible string to an internal kernel - * name for a certain sensor device, so that the rest of the autodetection can - * rely on that name from this point on. I2C client devices will be renamed - * to match the internal kernel convention. - */ -void st_sensors_of_i2c_probe(struct i2c_client *client, - const struct of_device_id *match) -{ - const struct of_device_id *of_id; - - of_id = of_match_device(match, &client->dev); - if (!of_id) - return; - - /* The name from the OF match takes precedence if present */ - strncpy(client->name, of_id->data, sizeof(client->name)); - client->name[sizeof(client->name) - 1] = '\0'; -} -EXPORT_SYMBOL(st_sensors_of_i2c_probe); -#endif - #ifdef CONFIG_ACPI int st_sensors_match_acpi_device(struct device *dev) { diff --git a/drivers/iio/gyro/st_gyro_i2c.c b/drivers/iio/gyro/st_gyro_i2c.c index 3f628746cb93..b405b82b9177 100644 --- a/drivers/iio/gyro/st_gyro_i2c.c +++ b/drivers/iio/gyro/st_gyro_i2c.c @@ -75,7 +75,8 @@ static int st_gyro_i2c_probe(struct i2c_client *client, return -ENOMEM; gdata = iio_priv(indio_dev); - st_sensors_of_i2c_probe(client, st_gyro_of_match); + st_sensors_of_name_probe(&client->dev, st_gyro_of_match, + client->name, sizeof(client->name)); st_sensors_i2c_configure(indio_dev, client, gdata); diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c index 8aa37af306ed..6a6c8121ac2c 100644 --- a/drivers/iio/magnetometer/st_magn_i2c.c +++ b/drivers/iio/magnetometer/st_magn_i2c.c @@ -59,7 +59,8 @@ static int st_magn_i2c_probe(struct i2c_client *client, return -ENOMEM; mdata = iio_priv(indio_dev); - st_sensors_of_i2c_probe(client, st_magn_of_match); + st_sensors_of_name_probe(&client->dev, st_magn_of_match, + client->name, sizeof(client->name)); st_sensors_i2c_configure(indio_dev, client, mdata); diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c index 17417a4d5a5f..7f15e927fa2b 100644 --- a/drivers/iio/pressure/st_pressure_i2c.c +++ b/drivers/iio/pressure/st_pressure_i2c.c @@ -77,7 +77,8 @@ static int st_press_i2c_probe(struct i2c_client *client, press_data = iio_priv(indio_dev); if (client->dev.of_node) { - st_sensors_of_i2c_probe(client, st_press_of_match); + st_sensors_of_name_probe(&client->dev, st_press_of_match, + client->name, sizeof(client->name)); } else if (ACPI_HANDLE(&client->dev)) { ret = st_sensors_match_acpi_device(&client->dev); if ((ret < 0) || (ret >= ST_PRESS_MAX)) diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h index 497f2b3a5a62..1f8211b6438b 100644 --- a/include/linux/iio/common/st_sensors.h +++ b/include/linux/iio/common/st_sensors.h @@ -325,4 +325,16 @@ ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *dev, ssize_t st_sensors_sysfs_scale_avail(struct device *dev, struct device_attribute *attr, char *buf); +#ifdef CONFIG_OF +void st_sensors_of_name_probe(struct device *dev, + const struct of_device_id *match, + char *name, int len); +#else +static inline void st_sensors_of_name_probe(struct device *dev, + const struct of_device_id *match, + char *name, int len) +{ +} +#endif + #endif /* ST_SENSORS_H */ diff --git a/include/linux/iio/common/st_sensors_i2c.h b/include/linux/iio/common/st_sensors_i2c.h index 254de3c7dde8..0a2c25e06d1f 100644 --- a/include/linux/iio/common/st_sensors_i2c.h +++ b/include/linux/iio/common/st_sensors_i2c.h @@ -18,16 +18,6 @@ void st_sensors_i2c_configure(struct iio_dev *indio_dev, struct i2c_client *client, struct st_sensor_data *sdata); -#ifdef CONFIG_OF -void st_sensors_of_i2c_probe(struct i2c_client *client, - const struct of_device_id *match); -#else -static inline void st_sensors_of_i2c_probe(struct i2c_client *client, - const struct of_device_id *match) -{ -} -#endif - #ifdef CONFIG_ACPI int st_sensors_match_acpi_device(struct device *dev); #else -- cgit v1.2.3 From c24f83bad9a4f1c8ea2f39771f6b2e3b2afd35ed Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Tue, 20 Jun 2017 21:52:09 +0200 Subject: iio: accel: st_accel_spi: rename of_device_id table in st_accel_of_match In order to add proper device tree support give a more general name to of_device_id table Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_spi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index 1a867f5563a4..d13742edee0e 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -62,17 +62,17 @@ static const struct spi_device_id st_accel_id_table[] = { MODULE_DEVICE_TABLE(spi, st_accel_id_table); #ifdef CONFIG_OF -static const struct of_device_id lis302dl_spi_dt_ids[] = { +static const struct of_device_id st_accel_of_match[] = { { .compatible = "st,lis302dl-spi" }, {} }; -MODULE_DEVICE_TABLE(of, lis302dl_spi_dt_ids); +MODULE_DEVICE_TABLE(of, st_accel_of_match); #endif static struct spi_driver st_accel_driver = { .driver = { .name = "st-accel-spi", - .of_match_table = of_match_ptr(lis302dl_spi_dt_ids), + .of_match_table = of_match_ptr(st_accel_of_match), }, .probe = st_accel_spi_probe, .remove = st_accel_spi_remove, -- cgit v1.2.3 From 500a2eefd6b16ba141a8fb777ea6962d2eb65e3b Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Fri, 23 Jun 2017 15:54:57 +0300 Subject: iio: adc: at91-sama5d2_adc: add support for suspend/resume functionality Added support for suspend/resume functionality for the ADC IP in sama5d2 SoC. In order to enter Suspend to ram mode (backup + self refresh mode for memory), in which the ADC IP is no longer powered, we need to reset the pins to default state, for the scenario when they are also used for I2C bus to communicate with the PMIC. On resume, we need to reconfigure the ADC IP registers and reconfigure the trigger registers in the case when the suspend procedure is done while sysfs has the buffer and trigger enabled. In the case the suspend happens exactly during a software triggered conversion, the request will time out, because we reset and power down the ADC. Signed-off-by: Eugen Hristev Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 90 +++++++++++++++++++++++++++++++++----- 1 file changed, 80 insertions(+), 10 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 1773a5dc06b2..57e1eb23c45d 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -29,6 +29,7 @@ #include #include #include +#include #include /* Control Register */ @@ -576,6 +577,20 @@ static const struct iio_info at91_adc_info = { .driver_module = THIS_MODULE, }; +static void at91_adc_hw_init(struct at91_adc_state *st) +{ + at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST); + at91_adc_writel(st, AT91_SAMA5D2_IDR, 0xffffffff); + /* + * Transfer field must be set to 2 according to the datasheet and + * allows different analog settings for each channel. + */ + at91_adc_writel(st, AT91_SAMA5D2_MR, + AT91_SAMA5D2_MR_TRANSFER(2) | AT91_SAMA5D2_MR_ANACH); + + at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate); +} + static int at91_adc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; @@ -694,16 +709,7 @@ static int at91_adc_probe(struct platform_device *pdev) goto vref_disable; } - at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST); - at91_adc_writel(st, AT91_SAMA5D2_IDR, 0xffffffff); - /* - * Transfer field must be set to 2 according to the datasheet and - * allows different analog settings for each channel. - */ - at91_adc_writel(st, AT91_SAMA5D2_MR, - AT91_SAMA5D2_MR_TRANSFER(2) | AT91_SAMA5D2_MR_ANACH); - - at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate); + at91_adc_hw_init(st); ret = clk_prepare_enable(st->per_clk); if (ret) @@ -759,6 +765,69 @@ static int at91_adc_remove(struct platform_device *pdev) return 0; } +static __maybe_unused int at91_adc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = + platform_get_drvdata(to_platform_device(dev)); + struct at91_adc_state *st = iio_priv(indio_dev); + + /* + * Do a sofware reset of the ADC before we go to suspend. + * this will ensure that all pins are free from being muxed by the ADC + * and can be used by for other devices. + * Otherwise, ADC will hog them and we can't go to suspend mode. + */ + at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST); + + clk_disable_unprepare(st->per_clk); + regulator_disable(st->vref); + regulator_disable(st->reg); + + return pinctrl_pm_select_sleep_state(dev); +} + +static __maybe_unused int at91_adc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = + platform_get_drvdata(to_platform_device(dev)); + struct at91_adc_state *st = iio_priv(indio_dev); + int ret; + + ret = pinctrl_pm_select_default_state(dev); + if (ret) + goto resume_failed; + + ret = regulator_enable(st->reg); + if (ret) + goto resume_failed; + + ret = regulator_enable(st->vref); + if (ret) + goto reg_disable_resume; + + ret = clk_prepare_enable(st->per_clk); + if (ret) + goto vref_disable_resume; + + at91_adc_hw_init(st); + + /* reconfiguring trigger hardware state */ + if (iio_buffer_enabled(indio_dev)) + at91_adc_configure_trigger(st->trig, true); + + return 0; + +vref_disable_resume: + regulator_disable(st->vref); +reg_disable_resume: + regulator_disable(st->reg); +resume_failed: + dev_err(&indio_dev->dev, "failed to resume\n"); + return ret; +} + +static SIMPLE_DEV_PM_OPS(at91_adc_pm_ops, at91_adc_suspend, at91_adc_resume); + static const struct of_device_id at91_adc_dt_match[] = { { .compatible = "atmel,sama5d2-adc", @@ -774,6 +843,7 @@ static struct platform_driver at91_adc_driver = { .driver = { .name = "at91-sama5d2_adc", .of_match_table = at91_adc_dt_match, + .pm = &at91_adc_pm_ops, }, }; module_platform_driver(at91_adc_driver) -- cgit v1.2.3 From c43aaa2772732a24a642afc26c030a2bee35aa0c Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 13 Jun 2017 22:50:05 +0530 Subject: iio: humidity: htu21: Add OF match table Add of_match_table for Measurement-Specialties htu21 temperature & humidity sensor and humidity part of MS8607 sensor Signed-off-by: Manivannan Sadhasivam Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/htu21.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/humidity/htu21.c b/drivers/iio/humidity/htu21.c index 0fbbd8c40894..2c4b9be85a05 100644 --- a/drivers/iio/humidity/htu21.c +++ b/drivers/iio/humidity/htu21.c @@ -238,11 +238,19 @@ static const struct i2c_device_id htu21_id[] = { }; MODULE_DEVICE_TABLE(i2c, htu21_id); +static const struct of_device_id htu21_of_match[] = { + { .compatible = "meas,htu21", }, + { .compatible = "meas,ms8607-humidity", }, + { }, +}; +MODULE_DEVICE_TABLE(of, htu21_of_match); + static struct i2c_driver htu21_driver = { .probe = htu21_probe, .id_table = htu21_id, .driver = { .name = "htu21", + .of_match_table = of_match_ptr(htu21_of_match), }, }; -- cgit v1.2.3 From 1813c71317df7a4b2578346967a3b3f6e6ab6c0e Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 13 Jun 2017 22:51:13 +0530 Subject: iio: pressure: ms5637: Add OF match table Add of_match_table for Measurement-Specialties MS5637, MS5805, MS5837 and MS8607 pressure & temperature sensor Signed-off-by: Manivannan Sadhasivam Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/ms5637.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/pressure/ms5637.c b/drivers/iio/pressure/ms5637.c index 953ffbc0ef96..c413f8a84a63 100644 --- a/drivers/iio/pressure/ms5637.c +++ b/drivers/iio/pressure/ms5637.c @@ -181,11 +181,21 @@ static const struct i2c_device_id ms5637_id[] = { }; MODULE_DEVICE_TABLE(i2c, ms5637_id); +static const struct of_device_id ms5637_of_match[] = { + { .compatible = "meas,ms5637", }, + { .compatible = "meas,ms5805", }, + { .compatible = "meas,ms5837", }, + { .compatible = "meas,ms8607-temppressure", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ms5637_of_match); + static struct i2c_driver ms5637_driver = { .probe = ms5637_probe, .id_table = ms5637_id, .driver = { - .name = "ms5637" + .name = "ms5637", + .of_match_table = of_match_ptr(ms5637_of_match), }, }; -- cgit v1.2.3 From ff5fff4af47970cdeb92cb8194c8933db4ae248b Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Wed, 21 Jun 2017 21:21:28 +0200 Subject: iio: imu: st_lsm6dsx: support open drain mode Add open drain support in order to share requested IRQ line between st_lsm6dsx device and other peripherals Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index 2a72acc6e049..e2737dc71b67 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -31,6 +31,8 @@ #include #include +#include + #include "st_lsm6dsx.h" #define ST_LSM6DSX_REG_FIFO_THL_ADDR 0x06 @@ -39,6 +41,8 @@ #define ST_LSM6DSX_REG_FIFO_DEC_GXL_ADDR 0x08 #define ST_LSM6DSX_REG_HLACTIVE_ADDR 0x12 #define ST_LSM6DSX_REG_HLACTIVE_MASK BIT(5) +#define ST_LSM6DSX_REG_PP_OD_ADDR 0x12 +#define ST_LSM6DSX_REG_PP_OD_MASK BIT(4) #define ST_LSM6DSX_REG_FIFO_MODE_ADDR 0x0a #define ST_LSM6DSX_FIFO_MODE_MASK GENMASK(2, 0) #define ST_LSM6DSX_FIFO_ODR_MASK GENMASK(6, 3) @@ -417,6 +421,8 @@ static const struct iio_buffer_setup_ops st_lsm6dsx_buffer_ops = { int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw) { + struct device_node *np = hw->dev->of_node; + struct st_sensors_platform_data *pdata; struct iio_buffer *buffer; unsigned long irq_type; bool irq_active_low; @@ -444,6 +450,17 @@ int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw) if (err < 0) return err; + pdata = (struct st_sensors_platform_data *)hw->dev->platform_data; + if ((np && of_property_read_bool(np, "drive-open-drain")) || + (pdata && pdata->open_drain)) { + err = st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_PP_OD_ADDR, + ST_LSM6DSX_REG_PP_OD_MASK, 1); + if (err < 0) + return err; + + irq_type |= IRQF_SHARED; + } + err = devm_request_threaded_irq(hw->dev, hw->irq, st_lsm6dsx_handler_irq, st_lsm6dsx_handler_thread, -- cgit v1.2.3 From 5236bbc6cd688c0065bf0fba59097b010dc22040 Mon Sep 17 00:00:00 2001 From: Zhiyong Tao Date: Mon, 26 Jun 2017 14:11:41 +0800 Subject: iio: adc: mt7622: add support for suspend/resume. This patch supports auxadc suspend/resume flow. Disable auxadc clk and power in suspend function. Enable axuadc clk and power in resume function. Signed-off-by: Zhiyong Tao Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mt6577_auxadc.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/mt6577_auxadc.c b/drivers/iio/adc/mt6577_auxadc.c index 2d104c828041..914314a4436b 100644 --- a/drivers/iio/adc/mt6577_auxadc.c +++ b/drivers/iio/adc/mt6577_auxadc.c @@ -184,6 +184,37 @@ static const struct iio_info mt6577_auxadc_info = { .read_raw = &mt6577_auxadc_read_raw, }; +static int __maybe_unused mt6577_auxadc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev); + int ret; + + ret = clk_prepare_enable(adc_dev->adc_clk); + if (ret) { + pr_err("failed to enable auxadc clock\n"); + return ret; + } + + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, + MT6577_AUXADC_PDN_EN, 0); + mdelay(MT6577_AUXADC_POWER_READY_MS); + + return 0; +} + +static int __maybe_unused mt6577_auxadc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev); + + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, + 0, MT6577_AUXADC_PDN_EN); + clk_disable_unprepare(adc_dev->adc_clk); + + return 0; +} + static int mt6577_auxadc_probe(struct platform_device *pdev) { struct mt6577_auxadc_device *adc_dev; @@ -269,6 +300,10 @@ static int mt6577_auxadc_remove(struct platform_device *pdev) return 0; } +static SIMPLE_DEV_PM_OPS(mt6577_auxadc_pm_ops, + mt6577_auxadc_suspend, + mt6577_auxadc_resume); + static const struct of_device_id mt6577_auxadc_of_match[] = { { .compatible = "mediatek,mt2701-auxadc", }, { .compatible = "mediatek,mt8173-auxadc", }, @@ -280,6 +315,7 @@ static struct platform_driver mt6577_auxadc_driver = { .driver = { .name = "mt6577-auxadc", .of_match_table = mt6577_auxadc_of_match, + .pm = &mt6577_auxadc_pm_ops, }, .probe = mt6577_auxadc_probe, .remove = mt6577_auxadc_remove, -- cgit v1.2.3 From 65746eb742f344f8d90a87910003c2e402a33c6f Mon Sep 17 00:00:00 2001 From: Zhiyong Tao Date: Mon, 26 Jun 2017 14:11:42 +0800 Subject: iio: adc: mt7622: Add compatible node for mt7622. This commit adds mt7622 compatible node. Signed-off-by: Zhiyong Tao Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mt6577_auxadc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/mt6577_auxadc.c b/drivers/iio/adc/mt6577_auxadc.c index 914314a4436b..414cf44bf19d 100644 --- a/drivers/iio/adc/mt6577_auxadc.c +++ b/drivers/iio/adc/mt6577_auxadc.c @@ -306,6 +306,7 @@ static SIMPLE_DEV_PM_OPS(mt6577_auxadc_pm_ops, static const struct of_device_id mt6577_auxadc_of_match[] = { { .compatible = "mediatek,mt2701-auxadc", }, + { .compatible = "mediatek,mt7622-auxadc", }, { .compatible = "mediatek,mt8173-auxadc", }, { } }; -- cgit v1.2.3 From ea5c6e262c8557731d76b2346ebfa6b25e2fa302 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Tue, 27 Jun 2017 00:44:54 +0900 Subject: iio: light: tcs3472: add link to datasheet Add a link to the TCS3472 datasheet in the AMS's website. Cc: Peter Meerwald Cc: Jonathan Cameron Signed-off-by: Akinobu Mita Signed-off-by: Jonathan Cameron --- drivers/iio/light/tcs3472.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index a9e153b3e1f9..09e6ca5e332e 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -11,6 +11,8 @@ * 7-bit I2C slave address 0x39 (TCS34721, TCS34723) or 0x29 (TCS34725, * TCS34727) * + * Datasheet: http://ams.com/eng/content/download/319364/1117183/file/TCS3472_Datasheet_EN_v2.pdf + * * TODO: interrupt support, thresholds, wait time */ -- cgit v1.2.3 From b64a097af4c86102866eb20ecc55058ca76fc2e8 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 29 Jun 2017 13:57:04 +0200 Subject: iio: gyro: mpu3050: Allow open drain with anything Open drain should work fine with rising or high level IRQs, this code was due to some misunderstanding on my part. Reported-by: Lorenzo Bianconi Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/mpu3050-core.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c index 2be2a5d287e6..e0d241a9aa30 100644 --- a/drivers/iio/gyro/mpu3050-core.c +++ b/drivers/iio/gyro/mpu3050-core.c @@ -1063,11 +1063,6 @@ static int mpu3050_trigger_probe(struct iio_dev *indio_dev, int irq) case IRQF_TRIGGER_RISING: dev_info(&indio_dev->dev, "pulse interrupts on the rising edge\n"); - if (mpu3050->irq_opendrain) { - dev_info(&indio_dev->dev, - "rising edge incompatible with open drain\n"); - mpu3050->irq_opendrain = false; - } break; case IRQF_TRIGGER_FALLING: mpu3050->irq_actl = true; @@ -1078,11 +1073,6 @@ static int mpu3050_trigger_probe(struct iio_dev *indio_dev, int irq) mpu3050->irq_latch = true; dev_info(&indio_dev->dev, "interrupts active high level\n"); - if (mpu3050->irq_opendrain) { - dev_info(&indio_dev->dev, - "active high incompatible with open drain\n"); - mpu3050->irq_opendrain = false; - } /* * With level IRQs, we mask the IRQ until it is processed, * but with edge IRQs (pulses) we can queue several interrupts -- cgit v1.2.3 From e3169994f6563f7e80a357ea68d5e9b040cb126c Mon Sep 17 00:00:00 2001 From: Angelo Compagnucci Date: Wed, 28 Jun 2017 23:53:09 +0200 Subject: iio: adc: mcp3422: Changing initial channel Initial channel should be the first available channel on all configurations, so changing to channel 0 available on all supported chips. Signed-off-by: Angelo Compagnucci Reported-by: Maarten Brock Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mcp3422.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/mcp3422.c b/drivers/iio/adc/mcp3422.c index 254135e07792..6737df8d9bdd 100644 --- a/drivers/iio/adc/mcp3422.c +++ b/drivers/iio/adc/mcp3422.c @@ -379,7 +379,7 @@ static int mcp3422_probe(struct i2c_client *client, /* meaningful default configuration */ config = (MCP3422_CONT_SAMPLING - | MCP3422_CHANNEL_VALUE(1) + | MCP3422_CHANNEL_VALUE(0) | MCP3422_PGA_VALUE(MCP3422_PGA_1) | MCP3422_SAMPLE_RATE_VALUE(MCP3422_SRATE_240)); mcp3422_update_config(adc, config); -- cgit v1.2.3 From 6be72b9067d443a44065541853edfb92ffca0401 Mon Sep 17 00:00:00 2001 From: Angelo Compagnucci Date: Wed, 28 Jun 2017 23:53:10 +0200 Subject: iio: adc: mcp3422: Checking for error on probe Some part of the configuration are not touched after the probe and if something goes wrong on writing the initial one, the chip will misbehave. Adding an error checking ensures that the inital configuration will be written correctly. Moreover ensures that a sensible configuration will be saved in driver data and used subsequently as intended. Signed-off-by: Angelo Compagnucci Reported-by: Maarten Brock Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mcp3422.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/mcp3422.c b/drivers/iio/adc/mcp3422.c index 6737df8d9bdd..63de705086ed 100644 --- a/drivers/iio/adc/mcp3422.c +++ b/drivers/iio/adc/mcp3422.c @@ -382,7 +382,9 @@ static int mcp3422_probe(struct i2c_client *client, | MCP3422_CHANNEL_VALUE(0) | MCP3422_PGA_VALUE(MCP3422_PGA_1) | MCP3422_SAMPLE_RATE_VALUE(MCP3422_SRATE_240)); - mcp3422_update_config(adc, config); + err = mcp3422_update_config(adc, config); + if (err < 0) + return err; err = devm_iio_device_register(&client->dev, indio_dev); if (err < 0) -- cgit v1.2.3 From 004123f3dfb8bef1458508c1b1f18e56cc1cd336 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 28 Jun 2017 14:06:50 +0100 Subject: iio: adc: stm32: make array stm32h7_adc_ckmodes_spec static The array stm32h7_adc_ckmodes_spec does not need to be in global scope, so make it static. Cleans up sparse warning: "symbol 'stm32h7_adc_ckmodes_spec' was not declared. Should it be static?" Signed-off-by: Colin Ian King Acked-by: Fabrice Gasnier Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index e09233b03c05..9d083c2338f9 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -172,7 +172,7 @@ struct stm32h7_adc_ck_spec { int div; }; -const struct stm32h7_adc_ck_spec stm32h7_adc_ckmodes_spec[] = { +static const struct stm32h7_adc_ck_spec stm32h7_adc_ckmodes_spec[] = { /* 00: CK_ADC[1..3]: Asynchronous clock modes */ { 0, 0, 1 }, { 0, 1, 2 }, -- cgit v1.2.3 From b995a732daabbcac2b8fdbba638c6e91ba95209a Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Tue, 27 Jun 2017 11:59:56 -0700 Subject: iio:adc:ltc2497: Add support for board file based iio consumer mapping. Add missing hooks in the LTC2497 IIO driver to allow hwmon mapping. Signed-off-by: Gaurav Gupta Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ltc2497.c | 54 ++++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 19 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c index 2691b10023f5..5bf8011dcde9 100644 --- a/drivers/iio/adc/ltc2497.c +++ b/drivers/iio/adc/ltc2497.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -127,13 +128,14 @@ static int ltc2497_read_raw(struct iio_dev *indio_dev, } } -#define LTC2497_CHAN(_chan, _addr) { \ +#define LTC2497_CHAN(_chan, _addr, _ds_name) { \ .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), \ + .datasheet_name = (_ds_name), \ } #define LTC2497_CHAN_DIFF(_chan, _addr) { \ @@ -148,22 +150,22 @@ static int ltc2497_read_raw(struct iio_dev *indio_dev, } 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(0, LTC2497_SGL, "CH0"), + LTC2497_CHAN(1, LTC2497_SGL, "CH1"), + LTC2497_CHAN(2, LTC2497_SGL, "CH2"), + LTC2497_CHAN(3, LTC2497_SGL, "CH3"), + LTC2497_CHAN(4, LTC2497_SGL, "CH4"), + LTC2497_CHAN(5, LTC2497_SGL, "CH5"), + LTC2497_CHAN(6, LTC2497_SGL, "CH6"), + LTC2497_CHAN(7, LTC2497_SGL, "CH7"), + LTC2497_CHAN(8, LTC2497_SGL, "CH8"), + LTC2497_CHAN(9, LTC2497_SGL, "CH9"), + LTC2497_CHAN(10, LTC2497_SGL, "CH10"), + LTC2497_CHAN(11, LTC2497_SGL, "CH11"), + LTC2497_CHAN(12, LTC2497_SGL, "CH12"), + LTC2497_CHAN(13, LTC2497_SGL, "CH13"), + LTC2497_CHAN(14, LTC2497_SGL, "CH14"), + LTC2497_CHAN(15, LTC2497_SGL, "CH15"), LTC2497_CHAN_DIFF(0, LTC2497_DIFF), LTC2497_CHAN_DIFF(1, LTC2497_DIFF), LTC2497_CHAN_DIFF(2, LTC2497_DIFF), @@ -192,6 +194,7 @@ static int ltc2497_probe(struct i2c_client *client, { struct iio_dev *indio_dev; struct ltc2497_st *st; + struct iio_map *plat_data; int ret; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | @@ -221,19 +224,31 @@ static int ltc2497_probe(struct i2c_client *client, if (ret < 0) return ret; + if (client->dev.platform_data) { + plat_data = ((struct iio_map *)client->dev.platform_data); + ret = iio_map_array_register(indio_dev, plat_data); + if (ret) { + dev_err(&indio_dev->dev, "iio map err: %d\n", ret); + goto err_regulator_disable; + } + } + ret = i2c_smbus_write_byte(st->client, LTC2497_CONFIG_DEFAULT); if (ret < 0) - goto err_regulator_disable; + goto err_array_unregister; st->addr_prev = LTC2497_CONFIG_DEFAULT; st->time_prev = ktime_get(); ret = iio_device_register(indio_dev); if (ret < 0) - goto err_regulator_disable; + goto err_array_unregister; return 0; +err_array_unregister: + iio_map_array_unregister(indio_dev); + err_regulator_disable: regulator_disable(st->ref); @@ -245,6 +260,7 @@ 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_map_array_unregister(indio_dev); iio_device_unregister(indio_dev); regulator_disable(st->ref); -- cgit v1.2.3 From 893bd0ebfa285bc869ae43484567758fce2f3204 Mon Sep 17 00:00:00 2001 From: Reno Farnesi Date: Mon, 26 Jun 2017 21:45:22 -0400 Subject: iio: sca3000: Remove trailing whitespace Removes trailing whitespace to fix checkpatch errors Signed-off-by: Reno Farnesi Signed-off-by: Jonathan Cameron --- drivers/iio/accel/sca3000.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index cb1d83fa19a0..39ab210c44f6 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -36,7 +36,7 @@ #define SCA3000_LOCKED BIT(5) #define SCA3000_EEPROM_CS_ERROR BIT(1) #define SCA3000_SPI_FRAME_ERROR BIT(0) - + /* All reads done using register decrement so no need to directly access LSBs */ #define SCA3000_REG_X_MSB_ADDR 0x05 #define SCA3000_REG_Y_MSB_ADDR 0x07 @@ -74,7 +74,7 @@ #define SCA3000_REG_INT_STATUS_ADDR 0x16 #define SCA3000_REG_INT_STATUS_THREE_QUARTERS BIT(7) #define SCA3000_REG_INT_STATUS_HALF BIT(6) - + #define SCA3000_INT_STATUS_FREE_FALL BIT(3) #define SCA3000_INT_STATUS_Y_TRIGGER BIT(2) #define SCA3000_INT_STATUS_X_TRIGGER BIT(1) @@ -124,7 +124,7 @@ #define SCA3000_REG_INT_MASK_ADDR 0x21 #define SCA3000_REG_INT_MASK_PROT_MASK 0x1C - + #define SCA3000_REG_INT_MASK_RING_THREE_QUARTER BIT(7) #define SCA3000_REG_INT_MASK_RING_HALF BIT(6) -- cgit v1.2.3 From 2aac3388f1682d6208f6759c338a7efa37b0deca Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Mon, 26 Jun 2017 17:25:33 -0700 Subject: iio: magnetometer: Only declare ACPI table when ACPI is enable Don't inflate the kernel size with data that isn't used. The conditional declaration also fixes the following warning when building with clang: drivers/iio/magnetometer/ak8975.c:704:36: error: variable 'ak_acpi_match' is not needed and will not be emitted [-Werror,-Wunneeded-internal-declaration] Signed-off-by: Matthias Kaehlcke Acked-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 825369fb1c57..4ff883942f7b 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -784,6 +784,7 @@ static const struct iio_info ak8975_info = { .driver_module = THIS_MODULE, }; +#ifdef CONFIG_ACPI static const struct acpi_device_id ak_acpi_match[] = { {"AK8975", AK8975}, {"AK8963", AK8963}, @@ -793,6 +794,7 @@ static const struct acpi_device_id ak_acpi_match[] = { { }, }; MODULE_DEVICE_TABLE(acpi, ak_acpi_match); +#endif static const char *ak8975_match_acpi_device(struct device *dev, enum asahi_compass_chipset *chipset) -- cgit v1.2.3 From ff73e8611e95ebcb9e88b533d9a666bc13c82a88 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Tue, 27 Jun 2017 00:53:43 +0200 Subject: iio: accel: st_accel_spi: add OF capability to st_accel_spi Add device tree support for LIS3DH, LSM330D, LSM330DL, LSM330DLC, LIS331DLH, LSM330, LSM303AGR, LIS2DH12, LIS3L02DQ, LNG2DM accel sensors. Fix LIS302DL support defining of_device_id data field in st_accel_of_match table Fixes: 762227721fe6 (iio: accel: st_accel: handle deprecated bindings) Signed-off-by: Lorenzo Bianconi Reviewed-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_spi.c | 69 +++++++++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 8 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index d13742edee0e..6b2eafa45cbb 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -18,6 +18,65 @@ #include #include "st_accel.h" +#ifdef CONFIG_OF +/* + * For new single-chip sensors use as compatible string. + * For old single-chip devices keep -accel to maintain + * compatibility + */ +static const struct of_device_id st_accel_of_match[] = { + { + /* An older compatible */ + .compatible = "st,lis302dl-spi", + .data = LIS3LV02DL_ACCEL_DEV_NAME, + }, + { + .compatible = "st,lis3dh-accel", + .data = LIS3DH_ACCEL_DEV_NAME, + }, + { + .compatible = "st,lsm330d-accel", + .data = LSM330D_ACCEL_DEV_NAME, + }, + { + .compatible = "st,lsm330dl-accel", + .data = LSM330DL_ACCEL_DEV_NAME, + }, + { + .compatible = "st,lsm330dlc-accel", + .data = LSM330DLC_ACCEL_DEV_NAME, + }, + { + .compatible = "st,lis331dlh-accel", + .data = LIS331DLH_ACCEL_DEV_NAME, + }, + { + .compatible = "st,lsm330-accel", + .data = LSM330_ACCEL_DEV_NAME, + }, + { + .compatible = "st,lsm303agr-accel", + .data = LSM303AGR_ACCEL_DEV_NAME, + }, + { + .compatible = "st,lis2dh12-accel", + .data = LIS2DH12_ACCEL_DEV_NAME, + }, + { + .compatible = "st,lis3l02dq", + .data = LIS3L02DQ_ACCEL_DEV_NAME, + }, + { + .compatible = "st,lng2dm-accel", + .data = LNG2DM_ACCEL_DEV_NAME, + }, + {} +}; +MODULE_DEVICE_TABLE(of, st_accel_of_match); +#else +#define st_accel_of_match NULL +#endif + static int st_accel_spi_probe(struct spi_device *spi) { struct iio_dev *indio_dev; @@ -30,6 +89,8 @@ static int st_accel_spi_probe(struct spi_device *spi) adata = iio_priv(indio_dev); + st_sensors_of_name_probe(&spi->dev, st_accel_of_match, + spi->modalias, sizeof(spi->modalias)); st_sensors_spi_configure(indio_dev, spi, adata); err = st_accel_common_probe(indio_dev); @@ -61,14 +122,6 @@ static const struct spi_device_id st_accel_id_table[] = { }; MODULE_DEVICE_TABLE(spi, st_accel_id_table); -#ifdef CONFIG_OF -static const struct of_device_id st_accel_of_match[] = { - { .compatible = "st,lis302dl-spi" }, - {} -}; -MODULE_DEVICE_TABLE(of, st_accel_of_match); -#endif - static struct spi_driver st_accel_driver = { .driver = { .name = "st-accel-spi", -- cgit v1.2.3 From 65a687a22dbcb97d4747fd4c8edc55d73b5a3907 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Tue, 27 Jun 2017 00:53:44 +0200 Subject: iio: magnetometer: st_magn_spi: add OF capability to st_magn_spi Add device tree support for LIS3MDL and LSM303AGR magnetometer sensors Signed-off-by: Lorenzo Bianconi Reviewed-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/st_magn_spi.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c index f3cb4dc05391..1ea64dd318aa 100644 --- a/drivers/iio/magnetometer/st_magn_spi.c +++ b/drivers/iio/magnetometer/st_magn_spi.c @@ -18,6 +18,28 @@ #include #include "st_magn.h" +#ifdef CONFIG_OF +/* + * For new single-chip sensors use as compatible string. + * For old single-chip devices keep -magn to maintain + * compatibility + */ +static const struct of_device_id st_magn_of_match[] = { + { + .compatible = "st,lis3mdl-magn", + .data = LIS3MDL_MAGN_DEV_NAME, + }, + { + .compatible = "st,lsm303agr-magn", + .data = LSM303AGR_MAGN_DEV_NAME, + }, + {} +}; +MODULE_DEVICE_TABLE(of, st_magn_of_match); +#else +#define st_magn_of_match NULL +#endif + static int st_magn_spi_probe(struct spi_device *spi) { struct iio_dev *indio_dev; @@ -30,6 +52,8 @@ static int st_magn_spi_probe(struct spi_device *spi) mdata = iio_priv(indio_dev); + st_sensors_of_name_probe(&spi->dev, st_magn_of_match, + spi->modalias, sizeof(spi->modalias)); st_sensors_spi_configure(indio_dev, spi, mdata); err = st_magn_common_probe(indio_dev); @@ -57,6 +81,7 @@ MODULE_DEVICE_TABLE(spi, st_magn_id_table); static struct spi_driver st_magn_driver = { .driver = { .name = "st-magn-spi", + .of_match_table = of_match_ptr(st_magn_of_match), }, .probe = st_magn_spi_probe, .remove = st_magn_spi_remove, -- cgit v1.2.3 From 794694ce233fa595e5125cd52475e76397d395e3 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Tue, 27 Jun 2017 00:53:45 +0200 Subject: iio: gyro: st_gyro_spi: add OF capability to st_gyro_spi Add device tree support for L3G4200D, LSM330D, LSM330DL, LSM330DLC, L3GD20, L3GD20H, L3G4IS, LSM330, LSM9DS0 gyroscope sensors Signed-off-by: Lorenzo Bianconi Reviewed-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/st_gyro_spi.c | 53 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/gyro/st_gyro_spi.c b/drivers/iio/gyro/st_gyro_spi.c index fa14d8f2170d..0b52ed577dc2 100644 --- a/drivers/iio/gyro/st_gyro_spi.c +++ b/drivers/iio/gyro/st_gyro_spi.c @@ -18,6 +18,56 @@ #include #include "st_gyro.h" +#ifdef CONFIG_OF +/* + * For new single-chip sensors use as compatible string. + * For old single-chip devices keep -gyro to maintain + * compatibility + */ +static const struct of_device_id st_gyro_of_match[] = { + { + .compatible = "st,l3g4200d-gyro", + .data = L3G4200D_GYRO_DEV_NAME, + }, + { + .compatible = "st,lsm330d-gyro", + .data = LSM330D_GYRO_DEV_NAME, + }, + { + .compatible = "st,lsm330dl-gyro", + .data = LSM330DL_GYRO_DEV_NAME, + }, + { + .compatible = "st,lsm330dlc-gyro", + .data = LSM330DLC_GYRO_DEV_NAME, + }, + { + .compatible = "st,l3gd20-gyro", + .data = L3GD20_GYRO_DEV_NAME, + }, + { + .compatible = "st,l3gd20h-gyro", + .data = L3GD20H_GYRO_DEV_NAME, + }, + { + .compatible = "st,l3g4is-gyro", + .data = L3G4IS_GYRO_DEV_NAME, + }, + { + .compatible = "st,lsm330-gyro", + .data = LSM330_GYRO_DEV_NAME, + }, + { + .compatible = "st,lsm9ds0-gyro", + .data = LSM9DS0_GYRO_DEV_NAME, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, st_gyro_of_match); +#else +#define st_gyro_of_match NULL +#endif + static int st_gyro_spi_probe(struct spi_device *spi) { struct iio_dev *indio_dev; @@ -30,6 +80,8 @@ static int st_gyro_spi_probe(struct spi_device *spi) gdata = iio_priv(indio_dev); + st_sensors_of_name_probe(&spi->dev, st_gyro_of_match, + spi->modalias, sizeof(spi->modalias)); st_sensors_spi_configure(indio_dev, spi, gdata); err = st_gyro_common_probe(indio_dev); @@ -63,6 +115,7 @@ MODULE_DEVICE_TABLE(spi, st_gyro_id_table); static struct spi_driver st_gyro_driver = { .driver = { .name = "st-gyro-spi", + .of_match_table = of_match_ptr(st_gyro_of_match), }, .probe = st_gyro_spi_probe, .remove = st_gyro_spi_remove, -- cgit v1.2.3 From 2ff59bf39bbdc3b187050f8bf3a486b4ac416e86 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Tue, 27 Jun 2017 00:53:46 +0200 Subject: iio: pressure: st_pressure_spi: add OF capability to st_pressure_spi Add device tree support for LPS22HB, LPS25H, LPS331AP, LPS001WP pressure sensors Signed-off-by: Lorenzo Bianconi Reviewed-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/st_pressure_spi.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c index 550508025af1..f5ebd36bb4bf 100644 --- a/drivers/iio/pressure/st_pressure_spi.c +++ b/drivers/iio/pressure/st_pressure_spi.c @@ -18,6 +18,36 @@ #include #include "st_pressure.h" +#ifdef CONFIG_OF +/* + * For new single-chip sensors use as compatible string. + * For old single-chip devices keep -press to maintain + * compatibility + */ +static const struct of_device_id st_press_of_match[] = { + { + .compatible = "st,lps001wp-press", + .data = LPS001WP_PRESS_DEV_NAME, + }, + { + .compatible = "st,lps25h-press", + .data = LPS25H_PRESS_DEV_NAME, + }, + { + .compatible = "st,lps331ap-press", + .data = LPS331AP_PRESS_DEV_NAME, + }, + { + .compatible = "st,lps22hb-press", + .data = LPS22HB_PRESS_DEV_NAME, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, st_press_of_match); +#else +#define st_press_of_match NULL +#endif + static int st_press_spi_probe(struct spi_device *spi) { struct iio_dev *indio_dev; @@ -30,6 +60,8 @@ static int st_press_spi_probe(struct spi_device *spi) press_data = iio_priv(indio_dev); + st_sensors_of_name_probe(&spi->dev, st_press_of_match, + spi->modalias, sizeof(spi->modalias)); st_sensors_spi_configure(indio_dev, spi, press_data); err = st_press_common_probe(indio_dev); @@ -58,6 +90,7 @@ MODULE_DEVICE_TABLE(spi, st_press_id_table); static struct spi_driver st_press_driver = { .driver = { .name = "st-press-spi", + .of_match_table = of_match_ptr(st_press_of_match), }, .probe = st_press_spi_probe, .remove = st_press_spi_remove, -- cgit v1.2.3 From aab498ba12172e81b2deffda9400ebcf41e3462f Mon Sep 17 00:00:00 2001 From: Reno Farnesi Date: Sat, 1 Jul 2017 17:54:08 -0400 Subject: iio: adis16400: Change unsigned to unsigned int This patch fixes the following checkpatch warnings: Prefer 'unsigned int' to bare use of 'unsigned Signed-off-by: Reno Farnesi Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis16400_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/imu/adis16400_core.c b/drivers/iio/imu/adis16400_core.c index fb7c0dbed51c..9b697d35dbef 100644 --- a/drivers/iio/imu/adis16400_core.c +++ b/drivers/iio/imu/adis16400_core.c @@ -217,7 +217,7 @@ static int adis16400_set_freq(struct adis16400_state *st, unsigned int freq) return adis_write_reg_8(&st->adis, ADIS16400_SMPL_PRD, val); } -static const unsigned adis16400_3db_divisors[] = { +static const unsigned int adis16400_3db_divisors[] = { [0] = 2, /* Special case */ [1] = 6, [2] = 12, @@ -890,7 +890,7 @@ static const struct adis_data adis16400_data = { static void adis16400_setup_chan_mask(struct adis16400_state *st) { const struct adis16400_chip_info *chip_info = st->variant; - unsigned i; + unsigned int i; for (i = 0; i < chip_info->num_channels; i++) { const struct iio_chan_spec *ch = &chip_info->channels[i]; -- cgit v1.2.3 From e12ffd241c00146665cc2355263888c0f9e2b7fd Mon Sep 17 00:00:00 2001 From: Mikko Koivunen Date: Mon, 3 Jul 2017 15:50:13 +0300 Subject: iio: light: rpr0521 triggered buffer Set up and use triggered buffer if there is irq defined for device in device tree. Trigger producer triggers from rpr0521 drdy interrupt line. Trigger consumer reads rpr0521 data to scan buffer. Depends on previous commits of _scale and _offset. Signed-off-by: Mikko Koivunen Signed-off-by: Jonathan Cameron --- drivers/iio/light/rpr0521.c | 336 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 329 insertions(+), 7 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c index 9d0c2e859bb2..a6efa12613a2 100644 --- a/drivers/iio/light/rpr0521.c +++ b/drivers/iio/light/rpr0521.c @@ -9,7 +9,7 @@ * * IIO driver for RPR-0521RS (7-bit I2C slave address 0x38). * - * TODO: illuminance channel, buffer + * TODO: illuminance channel */ #include @@ -20,6 +20,10 @@ #include #include +#include +#include +#include +#include #include #include @@ -30,6 +34,7 @@ #define RPR0521_REG_PXS_DATA 0x44 /* 16-bit, little endian */ #define RPR0521_REG_ALS_DATA0 0x46 /* 16-bit, little endian */ #define RPR0521_REG_ALS_DATA1 0x48 /* 16-bit, little endian */ +#define RPR0521_REG_INTERRUPT 0x4A #define RPR0521_REG_PS_OFFSET_LSB 0x53 #define RPR0521_REG_ID 0x92 @@ -42,16 +47,31 @@ #define RPR0521_ALS_DATA1_GAIN_SHIFT 2 #define RPR0521_PXS_GAIN_MASK GENMASK(5, 4) #define RPR0521_PXS_GAIN_SHIFT 4 +#define RPR0521_PXS_PERSISTENCE_MASK GENMASK(3, 0) +#define RPR0521_INTERRUPT_INT_TRIG_PS_MASK BIT(0) +#define RPR0521_INTERRUPT_INT_TRIG_ALS_MASK BIT(1) +#define RPR0521_INTERRUPT_INT_REASSERT_MASK BIT(3) +#define RPR0521_INTERRUPT_ALS_INT_STATUS_MASK BIT(6) +#define RPR0521_INTERRUPT_PS_INT_STATUS_MASK BIT(7) #define RPR0521_MODE_ALS_ENABLE BIT(7) #define RPR0521_MODE_ALS_DISABLE 0x00 #define RPR0521_MODE_PXS_ENABLE BIT(6) #define RPR0521_MODE_PXS_DISABLE 0x00 +#define RPR0521_PXS_PERSISTENCE_DRDY 0x00 + +#define RPR0521_INTERRUPT_INT_TRIG_PS_ENABLE BIT(0) +#define RPR0521_INTERRUPT_INT_TRIG_PS_DISABLE 0x00 +#define RPR0521_INTERRUPT_INT_TRIG_ALS_ENABLE BIT(1) +#define RPR0521_INTERRUPT_INT_TRIG_ALS_DISABLE 0x00 +#define RPR0521_INTERRUPT_INT_REASSERT_ENABLE BIT(3) +#define RPR0521_INTERRUPT_INT_REASSERT_DISABLE 0x00 #define RPR0521_MANUFACT_ID 0xE0 #define RPR0521_DEFAULT_MEAS_TIME 0x06 /* ALS - 100ms, PXS - 100ms */ #define RPR0521_DRV_NAME "RPR0521" +#define RPR0521_IRQ_NAME "rpr0521_event" #define RPR0521_REGMAP_NAME "rpr0521_regmap" #define RPR0521_SLEEP_DELAY_MS 2000 @@ -167,6 +187,9 @@ struct rpr0521_data { bool als_dev_en; bool pxs_dev_en; + struct iio_trigger *drdy_trigger0; + s64 irq_timestamp; + /* optimize runtime pm ops - enable/disable device only if needed */ bool als_ps_need_en; bool pxs_ps_need_en; @@ -196,6 +219,19 @@ static const struct attribute_group rpr0521_attribute_group = { .attrs = rpr0521_attributes, }; +/* Order of the channel data in buffer */ +enum rpr0521_scan_index_order { + RPR0521_CHAN_INDEX_PXS, + RPR0521_CHAN_INDEX_BOTH, + RPR0521_CHAN_INDEX_IR, +}; + +static const unsigned long rpr0521_available_scan_masks[] = { + BIT(RPR0521_CHAN_INDEX_PXS) | BIT(RPR0521_CHAN_INDEX_BOTH) | + BIT(RPR0521_CHAN_INDEX_IR), + 0 +}; + static const struct iio_chan_spec rpr0521_channels[] = { { .type = IIO_PROXIMITY, @@ -204,6 +240,13 @@ static const struct iio_chan_spec rpr0521_channels[] = { BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_index = RPR0521_CHAN_INDEX_PXS, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_LE, + }, }, { .type = IIO_INTENSITY, @@ -213,6 +256,13 @@ static const struct iio_chan_spec rpr0521_channels[] = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_index = RPR0521_CHAN_INDEX_BOTH, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_LE, + }, }, { .type = IIO_INTENSITY, @@ -222,6 +272,13 @@ static const struct iio_chan_spec rpr0521_channels[] = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_index = RPR0521_CHAN_INDEX_IR, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_LE, + }, }, }; @@ -330,6 +387,198 @@ static int rpr0521_set_power_state(struct rpr0521_data *data, bool on, return 0; } +/* Interrupt register tells if this sensor caused the interrupt or not. */ +static inline bool rpr0521_is_triggered(struct rpr0521_data *data) +{ + int ret; + int reg; + + ret = regmap_read(data->regmap, RPR0521_REG_INTERRUPT, ®); + if (ret < 0) + return false; /* Reg read failed. */ + if (reg & + (RPR0521_INTERRUPT_ALS_INT_STATUS_MASK | + RPR0521_INTERRUPT_PS_INT_STATUS_MASK)) + return true; + else + return false; /* Int not from this sensor. */ +} + +/* IRQ to trigger handler */ +static irqreturn_t rpr0521_drdy_irq_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct rpr0521_data *data = iio_priv(indio_dev); + + data->irq_timestamp = iio_get_time_ns(indio_dev); + /* + * We need to wake the thread to read the interrupt reg. It + * is not possible to do that here because regmap_read takes a + * mutex. + */ + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t rpr0521_drdy_irq_thread(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct rpr0521_data *data = iio_priv(indio_dev); + + if (rpr0521_is_triggered(data)) { + iio_trigger_poll_chained(data->drdy_trigger0); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static irqreturn_t rpr0521_trigger_consumer_store_time(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + + /* Other trigger polls store time here. */ + if (!iio_trigger_using_own(indio_dev)) + pf->timestamp = iio_get_time_ns(indio_dev); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t rpr0521_trigger_consumer_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct rpr0521_data *data = iio_priv(indio_dev); + int err; + + u8 buffer[16]; /* 3 16-bit channels + padding + ts */ + + /* Use irq timestamp when reasonable. */ + if (iio_trigger_using_own(indio_dev) && data->irq_timestamp) { + pf->timestamp = data->irq_timestamp; + data->irq_timestamp = 0; + } + /* Other chained trigger polls get timestamp only here. */ + if (!pf->timestamp) + pf->timestamp = iio_get_time_ns(indio_dev); + + err = regmap_bulk_read(data->regmap, RPR0521_REG_PXS_DATA, + &buffer, + (3 * 2) + 1); /* 3 * 16-bit + (discarded) int clear reg. */ + if (!err) + iio_push_to_buffers_with_timestamp(indio_dev, + buffer, pf->timestamp); + else + dev_err(&data->client->dev, + "Trigger consumer can't read from sensor.\n"); + pf->timestamp = 0; + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int rpr0521_write_int_enable(struct rpr0521_data *data) +{ + int err; + + /* Interrupt after each measurement */ + err = regmap_update_bits(data->regmap, RPR0521_REG_PXS_CTRL, + RPR0521_PXS_PERSISTENCE_MASK, + RPR0521_PXS_PERSISTENCE_DRDY); + if (err) { + dev_err(&data->client->dev, "PS control reg write fail.\n"); + return -EBUSY; + } + + /* Ignore latch and mode because of drdy */ + err = regmap_write(data->regmap, RPR0521_REG_INTERRUPT, + RPR0521_INTERRUPT_INT_REASSERT_DISABLE | + RPR0521_INTERRUPT_INT_TRIG_ALS_DISABLE | + RPR0521_INTERRUPT_INT_TRIG_PS_ENABLE + ); + if (err) { + dev_err(&data->client->dev, "Interrupt setup write fail.\n"); + return -EBUSY; + } + + return 0; +} + +static int rpr0521_write_int_disable(struct rpr0521_data *data) +{ + /* Don't care of clearing mode, assert and latch. */ + return regmap_write(data->regmap, RPR0521_REG_INTERRUPT, + RPR0521_INTERRUPT_INT_TRIG_ALS_DISABLE | + RPR0521_INTERRUPT_INT_TRIG_PS_DISABLE + ); +} + +/* + * Trigger producer enable / disable. Note that there will be trigs only when + * measurement data is ready to be read. + */ +static int rpr0521_pxs_drdy_set_state(struct iio_trigger *trigger, + bool enable_drdy) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trigger); + struct rpr0521_data *data = iio_priv(indio_dev); + int err; + + if (enable_drdy) + err = rpr0521_write_int_enable(data); + else + err = rpr0521_write_int_disable(data); + if (err) + dev_err(&data->client->dev, "rpr0521_pxs_drdy_set_state failed\n"); + + return err; +} + +static const struct iio_trigger_ops rpr0521_trigger_ops = { + .set_trigger_state = rpr0521_pxs_drdy_set_state, + .owner = THIS_MODULE, + }; + + +static int rpr0521_buffer_preenable(struct iio_dev *indio_dev) +{ + int err; + struct rpr0521_data *data = iio_priv(indio_dev); + + mutex_lock(&data->lock); + err = rpr0521_set_power_state(data, true, + (RPR0521_MODE_PXS_MASK | RPR0521_MODE_ALS_MASK)); + mutex_unlock(&data->lock); + if (err) + dev_err(&data->client->dev, "_buffer_preenable fail\n"); + + return err; +} + +static int rpr0521_buffer_postdisable(struct iio_dev *indio_dev) +{ + int err; + struct rpr0521_data *data = iio_priv(indio_dev); + + mutex_lock(&data->lock); + err = rpr0521_set_power_state(data, false, + (RPR0521_MODE_PXS_MASK | RPR0521_MODE_ALS_MASK)); + mutex_unlock(&data->lock); + if (err) + dev_err(&data->client->dev, "_buffer_postdisable fail\n"); + + return err; +} + +static const struct iio_buffer_setup_ops rpr0521_buffer_setup_ops = { + .preenable = rpr0521_buffer_preenable, + .postenable = iio_triggered_buffer_postenable, + .predisable = iio_triggered_buffer_predisable, + .postdisable = rpr0521_buffer_postdisable, +}; + static int rpr0521_get_gain(struct rpr0521_data *data, int chan, int *val, int *val2) { @@ -473,6 +722,7 @@ static int rpr0521_read_raw(struct iio_dev *indio_dev, { struct rpr0521_data *data = iio_priv(indio_dev); int ret; + int busy; u8 device_mask; __le16 raw_data; @@ -481,26 +731,30 @@ static int rpr0521_read_raw(struct iio_dev *indio_dev, if (chan->type != IIO_INTENSITY && chan->type != IIO_PROXIMITY) return -EINVAL; + busy = iio_device_claim_direct_mode(indio_dev); + if (busy) + return -EBUSY; + device_mask = rpr0521_data_reg[chan->address].device_mask; mutex_lock(&data->lock); ret = rpr0521_set_power_state(data, true, device_mask); - if (ret < 0) { - mutex_unlock(&data->lock); - return ret; - } + if (ret < 0) + goto rpr0521_read_raw_out; ret = regmap_bulk_read(data->regmap, rpr0521_data_reg[chan->address].address, &raw_data, sizeof(raw_data)); if (ret < 0) { rpr0521_set_power_state(data, false, device_mask); - mutex_unlock(&data->lock); - return ret; + goto rpr0521_read_raw_out; } ret = rpr0521_set_power_state(data, false, device_mask); + +rpr0521_read_raw_out: mutex_unlock(&data->lock); + iio_device_release_direct_mode(indio_dev); if (ret < 0) return ret; @@ -617,12 +871,15 @@ static int rpr0521_init(struct rpr0521_data *data) return ret; #endif + data->irq_timestamp = 0; + return 0; } static int rpr0521_poweroff(struct rpr0521_data *data) { int ret; + int tmp; ret = regmap_update_bits(data->regmap, RPR0521_REG_MODE_CTRL, RPR0521_MODE_ALS_MASK | @@ -635,6 +892,16 @@ static int rpr0521_poweroff(struct rpr0521_data *data) data->als_dev_en = false; data->pxs_dev_en = false; + /* + * Int pin keeps state after power off. Set pin to high impedance + * mode to prevent power drain. + */ + ret = regmap_read(data->regmap, RPR0521_REG_INTERRUPT, &tmp); + if (ret) { + dev_err(&data->client->dev, "Failed to reset int pin.\n"); + return ret; + } + return 0; } @@ -707,6 +974,61 @@ static int rpr0521_probe(struct i2c_client *client, pm_runtime_set_autosuspend_delay(&client->dev, RPR0521_SLEEP_DELAY_MS); pm_runtime_use_autosuspend(&client->dev); + /* + * If sensor write/read is needed in _probe after _use_autosuspend, + * sensor needs to be _resumed first using rpr0521_set_power_state(). + */ + + /* IRQ to trigger setup */ + if (client->irq) { + /* Trigger0 producer setup */ + data->drdy_trigger0 = devm_iio_trigger_alloc( + indio_dev->dev.parent, + "%s-dev%d", indio_dev->name, indio_dev->id); + if (!data->drdy_trigger0) { + ret = -ENOMEM; + goto err_pm_disable; + } + data->drdy_trigger0->dev.parent = indio_dev->dev.parent; + data->drdy_trigger0->ops = &rpr0521_trigger_ops; + indio_dev->available_scan_masks = rpr0521_available_scan_masks; + iio_trigger_set_drvdata(data->drdy_trigger0, indio_dev); + + /* Ties irq to trigger producer handler. */ + ret = devm_request_threaded_irq(&client->dev, client->irq, + rpr0521_drdy_irq_handler, rpr0521_drdy_irq_thread, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + RPR0521_IRQ_NAME, indio_dev); + if (ret < 0) { + dev_err(&client->dev, "request irq %d for trigger0 failed\n", + client->irq); + goto err_pm_disable; + } + + ret = devm_iio_trigger_register(indio_dev->dev.parent, + data->drdy_trigger0); + if (ret) { + dev_err(&client->dev, "iio trigger register failed\n"); + goto err_pm_disable; + } + + /* + * Now whole pipe from physical interrupt (irq defined by + * devicetree to device) to trigger0 output is set up. + */ + + /* Trigger consumer setup */ + ret = devm_iio_triggered_buffer_setup(indio_dev->dev.parent, + indio_dev, + rpr0521_trigger_consumer_store_time, + rpr0521_trigger_consumer_handler, + &rpr0521_buffer_setup_ops); + if (ret < 0) { + dev_err(&client->dev, "iio triggered buffer setup failed\n"); + goto err_pm_disable; + } + } + ret = iio_device_register(indio_dev); if (ret) goto err_pm_disable; -- cgit v1.2.3 From 1d3eacbfa64411465c568d58583be3b9fa26857b Mon Sep 17 00:00:00 2001 From: Mike Looijmans Date: Mon, 3 Jul 2017 08:25:57 +0200 Subject: iio: Add LTC2471/LTC2473 driver The LTC2741 and LTC2473 are single voltage ADC chips. The LTC2473 is similar to the LTC2471 but outputs a signed differential value. Datasheet: http://cds.linear.com/docs/en/datasheet/24713fb.pdf Signed-off-by: Mike Looijmans Reviewed-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 10 +++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ltc2471.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100644 drivers/iio/adc/ltc2471.c (limited to 'drivers/iio') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index b56c4a1989a7..8a6edf5bb1e5 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -362,6 +362,16 @@ config LPC32XX_ADC activate only one via device tree selection. Provides direct access via sysfs. +config LTC2471 + tristate "Linear Technology LTC2471 and LTC2473 ADC driver" + depends on I2C + help + Say yes here to build support for Linear Technology LTC2471 and + LTC2473 16-bit I2C ADC. + + This driver can also be built as a module. If so, the module will + be called ltc2471. + config LTC2485 tristate "Linear Technology LTC2485 ADC driver" depends on I2C diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index b546736a5541..73c206209f1f 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o +obj-$(CONFIG_LTC2471) += ltc2471.o obj-$(CONFIG_LTC2485) += ltc2485.o obj-$(CONFIG_LTC2497) += ltc2497.o obj-$(CONFIG_MAX1027) += max1027.o diff --git a/drivers/iio/adc/ltc2471.c b/drivers/iio/adc/ltc2471.c new file mode 100644 index 000000000000..29b7ed60cdb0 --- /dev/null +++ b/drivers/iio/adc/ltc2471.c @@ -0,0 +1,160 @@ +/* + * Driver for Linear Technology LTC2471 and LTC2473 voltage monitors + * The LTC2473 is identical to the 2471, but reports a differential signal. + * + * Copyright (C) 2017 Topic Embedded Products + * Author: Mike Looijmans + * + * License: GPLv2 + */ + +#include +#include +#include +#include +#include +#include + +enum ltc2471_chips { + ltc2471, + ltc2473, +}; + +struct ltc2471_data { + struct i2c_client *client; +}; + +/* Reference voltage is 1.25V */ +#define LTC2471_VREF 1250 + +/* Read two bytes from the I2C bus to obtain the ADC result */ +static int ltc2471_get_value(struct i2c_client *client) +{ + int ret; + __be16 buf; + + ret = i2c_master_recv(client, (char *)&buf, sizeof(buf)); + if (ret < 0) + return ret; + if (ret != sizeof(buf)) + return -EIO; + + /* MSB first */ + return be16_to_cpu(buf); +} + +static int ltc2471_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ltc2471_data *data = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = ltc2471_get_value(data->client); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + if (chan->differential) + /* Output ranges from -VREF to +VREF */ + *val = 2 * LTC2471_VREF; + else + /* Output ranges from 0 to VREF */ + *val = LTC2471_VREF; + *val2 = 16; /* 16 data bits */ + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_OFFSET: + /* Only differential chip has this property */ + *val = -LTC2471_VREF; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static const struct iio_chan_spec ltc2471_channel[] = { + { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static const struct iio_chan_spec ltc2473_channel[] = { + { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .differential = 1, + }, +}; + +static const struct iio_info ltc2471_info = { + .read_raw = ltc2471_read_raw, + .driver_module = THIS_MODULE, +}; + +static int ltc2471_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct ltc2471_data *data; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -EOPNOTSUPP; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->client = client; + + indio_dev->dev.parent = &client->dev; + indio_dev->name = id->name; + indio_dev->info = <c2471_info; + indio_dev->modes = INDIO_DIRECT_MODE; + if (id->driver_data == ltc2473) + indio_dev->channels = ltc2473_channel; + else + indio_dev->channels = ltc2471_channel; + indio_dev->num_channels = 1; + + /* Trigger once to start conversion and check if chip is there */ + ret = ltc2471_get_value(client); + if (ret < 0) { + dev_err(&client->dev, "Cannot read from device.\n"); + return ret; + } + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id ltc2471_i2c_id[] = { + { "ltc2471", ltc2471 }, + { "ltc2473", ltc2473 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ltc2471_i2c_id); + +static struct i2c_driver ltc2471_i2c_driver = { + .driver = { + .name = "ltc2471", + }, + .probe = ltc2471_i2c_probe, + .id_table = ltc2471_i2c_id, +}; + +module_i2c_driver(ltc2471_i2c_driver); + +MODULE_DESCRIPTION("LTC2471/LTC2473 ADC driver"); +MODULE_AUTHOR("Topic Embedded Products"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From eed3089fb28d9afc252821ca2913ca84b53e17e7 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 5 Jul 2017 03:25:58 -0700 Subject: iio: pressure: zpa2326: Add newlines to logging macros Add newlines to the macro definitions and use a more common style. Signed-off-by: Joe Perches Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/zpa2326.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c index c92a95f9f52c..ebfb1de7377f 100644 --- a/drivers/iio/pressure/zpa2326.c +++ b/drivers/iio/pressure/zpa2326.c @@ -141,14 +141,14 @@ struct zpa2326_private { struct regulator *vdd; }; -#define zpa2326_err(_idev, _format, _arg...) \ - dev_err(_idev->dev.parent, _format, ##_arg) +#define zpa2326_err(idev, fmt, ...) \ + dev_err(idev->dev.parent, fmt "\n", ##__VA_ARGS__) -#define zpa2326_warn(_idev, _format, _arg...) \ - dev_warn(_idev->dev.parent, _format, ##_arg) +#define zpa2326_warn(idev, fmt, ...) \ + dev_warn(idev->dev.parent, fmt "\n", ##__VA_ARGS__) -#define zpa2326_dbg(_idev, _format, _arg...) \ - dev_dbg(_idev->dev.parent, _format, ##_arg) +#define zpa2326_dbg(idev, fmt, ...) \ + dev_dbg(idev->dev.parent, fmt "\n", ##__VA_ARGS__) bool zpa2326_isreg_writeable(struct device *dev, unsigned int reg) { -- cgit v1.2.3 From 7c0299e879dd3c3fe0885fc07233b28f6007add3 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Thu, 6 Jul 2017 12:39:33 -1000 Subject: iio: adc: Add support for DLN2 ADC This patch adds support for Diolan DLN2 ADC via IIO's ADC interface. ADC is the fourth and final component of the DLN2 for the kernel. Signed-off-by: Jack Andersen Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 9 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/dln2-adc.c | 722 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 732 insertions(+) create mode 100644 drivers/iio/adc/dln2-adc.c (limited to 'drivers/iio') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 8a6edf5bb1e5..17e61c5e9ffd 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -239,6 +239,15 @@ config DA9150_GPADC To compile this driver as a module, choose M here: the module will be called berlin2-adc. +config DLN2_ADC + tristate "Diolan DLN-2 ADC driver support" + depends on MFD_DLN2 + help + Say yes here to build support for Diolan DLN-2 ADC. + + This driver can also be built as a module. If so, the module will be + called adc_dln2. + config ENVELOPE_DETECTOR tristate "Envelope detector using a DAC and a comparator" depends on OF diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 73c206209f1f..4abb7af9e5bb 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -24,6 +24,7 @@ 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_DLN2_ADC) += dln2-adc.o obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c new file mode 100644 index 000000000000..ab8d6aed5085 --- /dev/null +++ b/drivers/iio/adc/dln2-adc.c @@ -0,0 +1,722 @@ +/* + * Driver for the Diolan DLN-2 USB-ADC adapter + * + * Copyright (c) 2017 Jack Andersen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define DLN2_ADC_MOD_NAME "dln2-adc" + +#define DLN2_ADC_ID 0x06 + +#define DLN2_ADC_GET_CHANNEL_COUNT DLN2_CMD(0x01, DLN2_ADC_ID) +#define DLN2_ADC_ENABLE DLN2_CMD(0x02, DLN2_ADC_ID) +#define DLN2_ADC_DISABLE DLN2_CMD(0x03, DLN2_ADC_ID) +#define DLN2_ADC_CHANNEL_ENABLE DLN2_CMD(0x05, DLN2_ADC_ID) +#define DLN2_ADC_CHANNEL_DISABLE DLN2_CMD(0x06, DLN2_ADC_ID) +#define DLN2_ADC_SET_RESOLUTION DLN2_CMD(0x08, DLN2_ADC_ID) +#define DLN2_ADC_CHANNEL_GET_VAL DLN2_CMD(0x0A, DLN2_ADC_ID) +#define DLN2_ADC_CHANNEL_GET_ALL_VAL DLN2_CMD(0x0B, DLN2_ADC_ID) +#define DLN2_ADC_CHANNEL_SET_CFG DLN2_CMD(0x0C, DLN2_ADC_ID) +#define DLN2_ADC_CHANNEL_GET_CFG DLN2_CMD(0x0D, DLN2_ADC_ID) +#define DLN2_ADC_CONDITION_MET_EV DLN2_CMD(0x10, DLN2_ADC_ID) + +#define DLN2_ADC_EVENT_NONE 0 +#define DLN2_ADC_EVENT_BELOW 1 +#define DLN2_ADC_EVENT_LEVEL_ABOVE 2 +#define DLN2_ADC_EVENT_OUTSIDE 3 +#define DLN2_ADC_EVENT_INSIDE 4 +#define DLN2_ADC_EVENT_ALWAYS 5 + +#define DLN2_ADC_MAX_CHANNELS 8 +#define DLN2_ADC_DATA_BITS 10 + +/* + * Plays similar role to iio_demux_table in subsystem core; except allocated + * in a fixed 8-element array. + */ +struct dln2_adc_demux_table { + unsigned int from; + unsigned int to; + unsigned int length; +}; + +struct dln2_adc { + struct platform_device *pdev; + struct iio_chan_spec iio_channels[DLN2_ADC_MAX_CHANNELS + 1]; + int port, trigger_chan; + struct iio_trigger *trig; + struct mutex mutex; + /* Cached sample period in milliseconds */ + unsigned int sample_period; + /* Demux table */ + unsigned int demux_count; + struct dln2_adc_demux_table demux[DLN2_ADC_MAX_CHANNELS]; + /* Precomputed timestamp padding offset and length */ + unsigned int ts_pad_offset, ts_pad_length; +}; + +struct dln2_adc_port_chan { + u8 port; + u8 chan; +}; + +struct dln2_adc_get_all_vals { + __le16 channel_mask; + __le16 values[DLN2_ADC_MAX_CHANNELS]; +}; + +static void dln2_adc_add_demux(struct dln2_adc *dln2, + unsigned int in_loc, unsigned int out_loc, + unsigned int length) +{ + struct dln2_adc_demux_table *p = dln2->demux_count ? + &dln2->demux[dln2->demux_count - 1] : NULL; + + if (p && p->from + p->length == in_loc && + p->to + p->length == out_loc) { + p->length += length; + } else if (dln2->demux_count < DLN2_ADC_MAX_CHANNELS) { + p = &dln2->demux[dln2->demux_count++]; + p->from = in_loc; + p->to = out_loc; + p->length = length; + } +} + +static void dln2_adc_update_demux(struct dln2_adc *dln2) +{ + int in_ind = -1, out_ind; + unsigned int in_loc = 0, out_loc = 0; + struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev); + + /* Clear out any old demux */ + dln2->demux_count = 0; + + /* Optimize all 8-channels case */ + if (indio_dev->masklength && + (*indio_dev->active_scan_mask & 0xff) == 0xff) { + dln2_adc_add_demux(dln2, 0, 0, 16); + dln2->ts_pad_offset = 0; + dln2->ts_pad_length = 0; + return; + } + + /* Build demux table from fixed 8-channels to active_scan_mask */ + for_each_set_bit(out_ind, + indio_dev->active_scan_mask, + indio_dev->masklength) { + /* Handle timestamp separately */ + if (out_ind == DLN2_ADC_MAX_CHANNELS) + break; + for (++in_ind; in_ind != out_ind; ++in_ind) + in_loc += 2; + dln2_adc_add_demux(dln2, in_loc, out_loc, 2); + out_loc += 2; + in_loc += 2; + } + + if (indio_dev->scan_timestamp) { + size_t ts_offset = indio_dev->scan_bytes / sizeof(int64_t) - 1; + + dln2->ts_pad_offset = out_loc; + dln2->ts_pad_length = ts_offset * sizeof(int64_t) - out_loc; + } else { + dln2->ts_pad_offset = 0; + dln2->ts_pad_length = 0; + } +} + +static int dln2_adc_get_chan_count(struct dln2_adc *dln2) +{ + int ret; + u8 port = dln2->port; + u8 count; + int olen = sizeof(count); + + ret = dln2_transfer(dln2->pdev, DLN2_ADC_GET_CHANNEL_COUNT, + &port, sizeof(port), &count, &olen); + if (ret < 0) { + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + return ret; + } + if (olen < sizeof(count)) + return -EPROTO; + + return count; +} + +static int dln2_adc_set_port_resolution(struct dln2_adc *dln2) +{ + int ret; + struct dln2_adc_port_chan port_chan = { + .port = dln2->port, + .chan = DLN2_ADC_DATA_BITS, + }; + + ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_SET_RESOLUTION, + &port_chan, sizeof(port_chan)); + if (ret < 0) + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + + return ret; +} + +static int dln2_adc_set_chan_enabled(struct dln2_adc *dln2, + int channel, bool enable) +{ + int ret; + struct dln2_adc_port_chan port_chan = { + .port = dln2->port, + .chan = channel, + }; + u16 cmd = enable ? DLN2_ADC_CHANNEL_ENABLE : DLN2_ADC_CHANNEL_DISABLE; + + ret = dln2_transfer_tx(dln2->pdev, cmd, &port_chan, sizeof(port_chan)); + if (ret < 0) + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + + return ret; +} + +static int dln2_adc_set_port_enabled(struct dln2_adc *dln2, bool enable, + u16 *conflict_out) +{ + int ret; + u8 port = dln2->port; + __le16 conflict; + int olen = sizeof(conflict); + u16 cmd = enable ? DLN2_ADC_ENABLE : DLN2_ADC_DISABLE; + + if (conflict_out) + *conflict_out = 0; + + ret = dln2_transfer(dln2->pdev, cmd, &port, sizeof(port), + &conflict, &olen); + if (ret < 0) { + dev_dbg(&dln2->pdev->dev, "Problem in %s(%d)\n", + __func__, (int)enable); + if (conflict_out && enable && olen >= sizeof(conflict)) + *conflict_out = le16_to_cpu(conflict); + return ret; + } + if (enable && olen < sizeof(conflict)) + return -EPROTO; + + return ret; +} + +static int dln2_adc_set_chan_period(struct dln2_adc *dln2, + unsigned int channel, unsigned int period) +{ + int ret; + struct { + struct dln2_adc_port_chan port_chan; + __u8 type; + __le16 period; + __le16 low; + __le16 high; + } __packed set_cfg = { + .port_chan.port = dln2->port, + .port_chan.chan = channel, + .type = period ? DLN2_ADC_EVENT_ALWAYS : DLN2_ADC_EVENT_NONE, + .period = cpu_to_le16(period) + }; + + ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_CHANNEL_SET_CFG, + &set_cfg, sizeof(set_cfg)); + if (ret < 0) + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + + return ret; +} + +static int dln2_adc_read(struct dln2_adc *dln2, unsigned int channel) +{ + int ret, i; + struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev); + u16 conflict; + __le16 value; + int olen = sizeof(value); + struct dln2_adc_port_chan port_chan = { + .port = dln2->port, + .chan = channel, + }; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret < 0) + return ret; + + ret = dln2_adc_set_chan_enabled(dln2, channel, true); + if (ret < 0) + goto release_direct; + + ret = dln2_adc_set_port_enabled(dln2, true, &conflict); + if (ret < 0) { + if (conflict) { + dev_err(&dln2->pdev->dev, + "ADC pins conflict with mask %04X\n", + (int)conflict); + ret = -EBUSY; + } + goto disable_chan; + } + + /* + * Call GET_VAL twice due to initial zero-return immediately after + * enabling channel. + */ + for (i = 0; i < 2; ++i) { + ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_VAL, + &port_chan, sizeof(port_chan), + &value, &olen); + if (ret < 0) { + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + goto disable_port; + } + if (olen < sizeof(value)) { + ret = -EPROTO; + goto disable_port; + } + } + + ret = le16_to_cpu(value); + +disable_port: + dln2_adc_set_port_enabled(dln2, false, NULL); +disable_chan: + dln2_adc_set_chan_enabled(dln2, channel, false); +release_direct: + iio_device_release_direct_mode(indio_dev); + + return ret; +} + +static int dln2_adc_read_all(struct dln2_adc *dln2, + struct dln2_adc_get_all_vals *get_all_vals) +{ + int ret; + __u8 port = dln2->port; + int olen = sizeof(*get_all_vals); + + ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_ALL_VAL, + &port, sizeof(port), get_all_vals, &olen); + if (ret < 0) { + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + return ret; + } + if (olen < sizeof(*get_all_vals)) + return -EPROTO; + + return ret; +} + +static int dln2_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + int ret; + unsigned int microhertz; + struct dln2_adc *dln2 = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&dln2->mutex); + ret = dln2_adc_read(dln2, chan->channel); + mutex_unlock(&dln2->mutex); + + if (ret < 0) + return ret; + + *val = ret; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + /* + * Voltage reference is fixed at 3.3v + * 3.3 / (1 << 10) * 1000000000 + */ + *val = 0; + *val2 = 3222656; + return IIO_VAL_INT_PLUS_NANO; + + case IIO_CHAN_INFO_SAMP_FREQ: + if (dln2->sample_period) { + microhertz = 1000000000 / dln2->sample_period; + *val = microhertz / 1000000; + *val2 = microhertz % 1000000; + } else { + *val = 0; + *val2 = 0; + } + + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } +} + +static int dln2_adc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + int ret; + unsigned int microhertz; + struct dln2_adc *dln2 = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + microhertz = 1000000 * val + val2; + + mutex_lock(&dln2->mutex); + + dln2->sample_period = + microhertz ? 1000000000 / microhertz : UINT_MAX; + if (dln2->sample_period > 65535) { + dln2->sample_period = 65535; + dev_warn(&dln2->pdev->dev, + "clamping period to 65535ms\n"); + } + + /* + * The first requested channel is arbitrated as a shared + * trigger source, so only one event is registered with the + * DLN. The event handler will then read all enabled channel + * values using DLN2_ADC_CHANNEL_GET_ALL_VAL to maintain + * synchronization between ADC readings. + */ + if (dln2->trigger_chan != -1) + ret = dln2_adc_set_chan_period(dln2, + dln2->trigger_chan, dln2->sample_period); + else + ret = 0; + + mutex_unlock(&dln2->mutex); + + return ret; + + default: + return -EINVAL; + } +} + +static int dln2_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct dln2_adc *dln2 = iio_priv(indio_dev); + int chan_count = indio_dev->num_channels - 1; + int ret, i, j; + + mutex_lock(&dln2->mutex); + + for (i = 0; i < chan_count; ++i) { + ret = dln2_adc_set_chan_enabled(dln2, i, + test_bit(i, scan_mask)); + if (ret < 0) { + for (j = 0; j < i; ++j) + dln2_adc_set_chan_enabled(dln2, j, false); + mutex_unlock(&dln2->mutex); + dev_err(&dln2->pdev->dev, + "Unable to enable ADC channel %d\n", i); + return -EBUSY; + } + } + + dln2_adc_update_demux(dln2); + + mutex_unlock(&dln2->mutex); + + return 0; +} + +#define DLN2_ADC_CHAN(lval, idx) { \ + lval.type = IIO_VOLTAGE; \ + lval.channel = idx; \ + lval.indexed = 1; \ + lval.info_mask_separate = BIT(IIO_CHAN_INFO_RAW); \ + lval.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ); \ + lval.scan_index = idx; \ + lval.scan_type.sign = 'u'; \ + lval.scan_type.realbits = DLN2_ADC_DATA_BITS; \ + lval.scan_type.storagebits = 16; \ + lval.scan_type.endianness = IIO_LE; \ +} + +/* Assignment version of IIO_CHAN_SOFT_TIMESTAMP */ +#define IIO_CHAN_SOFT_TIMESTAMP_ASSIGN(lval, _si) { \ + lval.type = IIO_TIMESTAMP; \ + lval.channel = -1; \ + lval.scan_index = _si; \ + lval.scan_type.sign = 's'; \ + lval.scan_type.realbits = 64; \ + lval.scan_type.storagebits = 64; \ +} + +static const struct iio_info dln2_adc_info = { + .read_raw = dln2_adc_read_raw, + .write_raw = dln2_adc_write_raw, + .update_scan_mode = dln2_update_scan_mode, + .driver_module = THIS_MODULE, +}; + +static irqreturn_t dln2_adc_trigger_h(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct { + __le16 values[DLN2_ADC_MAX_CHANNELS]; + int64_t timestamp_space; + } data; + struct dln2_adc_get_all_vals dev_data; + struct dln2_adc *dln2 = iio_priv(indio_dev); + const struct dln2_adc_demux_table *t; + int ret, i; + + mutex_lock(&dln2->mutex); + ret = dln2_adc_read_all(dln2, &dev_data); + mutex_unlock(&dln2->mutex); + if (ret < 0) + goto done; + + /* Demux operation */ + for (i = 0; i < dln2->demux_count; ++i) { + t = &dln2->demux[i]; + memcpy((void *)data.values + t->to, + (void *)dev_data.values + t->from, t->length); + } + + /* Zero padding space between values and timestamp */ + if (dln2->ts_pad_length) + memset((void *)data.values + dln2->ts_pad_offset, + 0, dln2->ts_pad_length); + + iio_push_to_buffers_with_timestamp(indio_dev, &data, + iio_get_time_ns(indio_dev)); + +done: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev) +{ + int ret; + struct dln2_adc *dln2 = iio_priv(indio_dev); + u16 conflict; + unsigned int trigger_chan; + + mutex_lock(&dln2->mutex); + + /* Enable ADC */ + ret = dln2_adc_set_port_enabled(dln2, true, &conflict); + if (ret < 0) { + mutex_unlock(&dln2->mutex); + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + if (conflict) { + dev_err(&dln2->pdev->dev, + "ADC pins conflict with mask %04X\n", + (int)conflict); + ret = -EBUSY; + } + return ret; + } + + /* Assign trigger channel based on first enabled channel */ + trigger_chan = find_first_bit(indio_dev->active_scan_mask, + indio_dev->masklength); + if (trigger_chan < DLN2_ADC_MAX_CHANNELS) { + dln2->trigger_chan = trigger_chan; + ret = dln2_adc_set_chan_period(dln2, dln2->trigger_chan, + dln2->sample_period); + mutex_unlock(&dln2->mutex); + if (ret < 0) { + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + return ret; + } + } else { + dln2->trigger_chan = -1; + mutex_unlock(&dln2->mutex); + } + + return iio_triggered_buffer_postenable(indio_dev); +} + +static int dln2_adc_triggered_buffer_predisable(struct iio_dev *indio_dev) +{ + int ret; + struct dln2_adc *dln2 = iio_priv(indio_dev); + + mutex_lock(&dln2->mutex); + + /* Disable trigger channel */ + if (dln2->trigger_chan != -1) { + dln2_adc_set_chan_period(dln2, dln2->trigger_chan, 0); + dln2->trigger_chan = -1; + } + + /* Disable ADC */ + ret = dln2_adc_set_port_enabled(dln2, false, NULL); + + mutex_unlock(&dln2->mutex); + if (ret < 0) { + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + return ret; + } + + return iio_triggered_buffer_predisable(indio_dev); +} + +static const struct iio_buffer_setup_ops dln2_adc_buffer_setup_ops = { + .postenable = dln2_adc_triggered_buffer_postenable, + .predisable = dln2_adc_triggered_buffer_predisable, +}; + +static void dln2_adc_event(struct platform_device *pdev, u16 echo, + const void *data, int len) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct dln2_adc *dln2 = iio_priv(indio_dev); + + /* Called via URB completion handler */ + iio_trigger_poll(dln2->trig); +} + +static const struct iio_trigger_ops dln2_adc_trigger_ops = { + .owner = THIS_MODULE, +}; + +static int dln2_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dln2_adc *dln2; + struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct iio_dev *indio_dev; + int i, ret, chans; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*dln2)); + if (!indio_dev) { + dev_err(dev, "failed allocating iio device\n"); + return -ENOMEM; + } + + dln2 = iio_priv(indio_dev); + dln2->pdev = pdev; + dln2->port = pdata->port; + dln2->trigger_chan = -1; + mutex_init(&dln2->mutex); + + platform_set_drvdata(pdev, indio_dev); + + ret = dln2_adc_set_port_resolution(dln2); + if (ret < 0) { + dev_err(dev, "failed to set ADC resolution to 10 bits\n"); + return ret; + } + + chans = dln2_adc_get_chan_count(dln2); + if (chans < 0) { + dev_err(dev, "failed to get channel count: %d\n", chans); + return chans; + } + if (chans > DLN2_ADC_MAX_CHANNELS) { + chans = DLN2_ADC_MAX_CHANNELS; + dev_warn(dev, "clamping channels to %d\n", + DLN2_ADC_MAX_CHANNELS); + } + + for (i = 0; i < chans; ++i) + DLN2_ADC_CHAN(dln2->iio_channels[i], i) + IIO_CHAN_SOFT_TIMESTAMP_ASSIGN(dln2->iio_channels[i], i); + + indio_dev->name = DLN2_ADC_MOD_NAME; + indio_dev->dev.parent = dev; + indio_dev->info = &dln2_adc_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = dln2->iio_channels; + indio_dev->num_channels = chans + 1; + indio_dev->setup_ops = &dln2_adc_buffer_setup_ops; + + dln2->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", + indio_dev->name, indio_dev->id); + if (!dln2->trig) { + dev_err(dev, "failed to allocate trigger\n"); + return -ENOMEM; + } + dln2->trig->ops = &dln2_adc_trigger_ops; + iio_trigger_set_drvdata(dln2->trig, dln2); + devm_iio_trigger_register(dev, dln2->trig); + iio_trigger_set_immutable(indio_dev, dln2->trig); + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + dln2_adc_trigger_h, + &dln2_adc_buffer_setup_ops); + if (ret) { + dev_err(dev, "failed to allocate triggered buffer: %d\n", ret); + return ret; + } + + ret = dln2_register_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV, + dln2_adc_event); + if (ret) { + dev_err(dev, "failed to setup DLN2 periodic event: %d\n", ret); + return ret; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(dev, "failed to register iio device: %d\n", ret); + goto unregister_event; + } + + return ret; + +unregister_event: + dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV); + + return ret; +} + +static int dln2_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + iio_device_unregister(indio_dev); + dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV); + return 0; +} + +static struct platform_driver dln2_adc_driver = { + .driver.name = DLN2_ADC_MOD_NAME, + .probe = dln2_adc_probe, + .remove = dln2_adc_remove, +}; + +module_platform_driver(dln2_adc_driver); + +MODULE_AUTHOR("Jack Andersen Acked-by: Martin Blumenstingl Signed-off-by: Jonathan Cameron --- drivers/iio/adc/meson_saradc.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index 83da50ed73ab..c4e904bbf168 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -915,6 +915,11 @@ static int meson_sar_adc_probe(struct platform_device *pdev) init_completion(&priv->done); match = of_match_device(meson_sar_adc_of_match, &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "failed to match device\n"); + return -ENODEV; + } + priv->data = match->data; indio_dev->name = priv->data->name; -- cgit v1.2.3 From 36d311bc1663f004529d2f870c1fc939d00c49f0 Mon Sep 17 00:00:00 2001 From: Gustavo A. R. Silva Date: Fri, 7 Jul 2017 01:51:31 -0500 Subject: iio: adc: rockchip_saradc: add NULL check on of_match_device() return value Check return value from call to of_match_device() in order to prevent a NULL pointer dereference. In case of NULL print error message and return -ENODEV Signed-off-by: Gustavo A. R. Silva Signed-off-by: Jonathan Cameron --- drivers/iio/adc/rockchip_saradc.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c index ae6d3324f518..2bf2ed15a870 100644 --- a/drivers/iio/adc/rockchip_saradc.c +++ b/drivers/iio/adc/rockchip_saradc.c @@ -224,6 +224,11 @@ static int rockchip_saradc_probe(struct platform_device *pdev) info = iio_priv(indio_dev); match = of_match_device(rockchip_saradc_match, &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "failed to match device\n"); + return -ENODEV; + } + info->data = match->data; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); -- cgit v1.2.3 From 72e36017eee0ffcbcf1630074f122916ed104269 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 7 Jul 2017 16:55:19 +0100 Subject: iio: accel: make array init_data static to reduce code size Making the const array init_data static rather having it on the stack saves us a couple hundreds of bytes: Before: text data bss dec hex filename 3175 848 0 4023 fb7 drivers/iio/accel/da311.o After: text data bss dec hex filename 2860 936 0 3796 ed4 drivers/iio/accel/da311.o Signed-off-by: Colin Ian King Signed-off-by: Jonathan Cameron --- drivers/iio/accel/da311.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/da311.c b/drivers/iio/accel/da311.c index 537cfa8b6edf..c0c1620d2a2f 100644 --- a/drivers/iio/accel/da311.c +++ b/drivers/iio/accel/da311.c @@ -139,7 +139,7 @@ static int da311_register_mask_write(struct i2c_client *client, u16 addr, /* Init sequence taken from the android driver */ static int da311_reset(struct i2c_client *client) { - const struct { + static const struct { u16 addr; u8 mask; u8 data; -- cgit v1.2.3 From 2df331cfe38d8aac4e787110279e00d12d0b2b25 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 7 Jul 2017 17:08:35 +0100 Subject: iio:adc:at91-sama5d2: make array startup_lookup static to reduce code size Making the const array startup_lookup static rather having it on the stack saves 69 bytes. Add in missing int specifier to clean up a checkpatch warning. Before: text data bss dec hex filename 10297 2800 128 13225 33a9 drivers/iio/adc/at91-sama5d2_adc.o After: text data bss dec hex filename 10140 2888 128 13156 3364 drivers/iio/adc/at91-sama5d2_adc.o Signed-off-by: Colin Ian King Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 57e1eb23c45d..bc5b38e3a147 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -411,7 +411,7 @@ static int at91_adc_buffer_init(struct iio_dev *indio) static unsigned at91_adc_startup_time(unsigned startup_time_min, unsigned adc_clk_khz) { - const unsigned startup_lookup[] = { + static const unsigned int startup_lookup[] = { 0, 8, 16, 24, 64, 80, 96, 112, 512, 576, 640, 704, -- cgit v1.2.3 From 6011208d8ad794e9c30e9e4c95669c977b1e274b Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 8 Jul 2017 13:00:16 +0200 Subject: iio: accel: st_accel_i2c: fix i2c_device_id table Add H3LIS331DL, LIS331DL and LIS3LV02DL entries in st_accel_id_table Fixes: 1e52fefc9b0c (iio: accel: Add support for the h3lis331dl accel) Fixes: bbf5f037fad4 (iio: st_accel: support the LIS331DL sensor) Fixes: 3acddf74f807 (iio: st-sensors: add support for lis3lv02d accel) Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel.h | 3 +++ drivers/iio/accel/st_accel_i2c.c | 3 +++ 2 files changed, 6 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h index 3ad44ce7ae82..fe3864f0f146 100644 --- a/drivers/iio/accel/st_accel.h +++ b/drivers/iio/accel/st_accel.h @@ -29,6 +29,9 @@ enum st_accel_type { LIS2DH12, LIS3L02DQ, LNG2DM, + H3LIS331DL, + LIS331DL, + LIS3LV02DL, ST_ACCEL_MAX, }; diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index ac67826135be..1b22c5714e89 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -126,6 +126,9 @@ static const struct i2c_device_id st_accel_id_table[] = { { LIS2DH12_ACCEL_DEV_NAME, LIS2DH12 }, { LIS3L02DQ_ACCEL_DEV_NAME, LIS3L02DQ }, { LNG2DM_ACCEL_DEV_NAME, LNG2DM }, + { H3LIS331DL_DRIVER_NAME, H3LIS331DL }, + { LIS331DL_ACCEL_DEV_NAME, LIS331DL }, + { LIS3LV02DL_ACCEL_DEV_NAME, LIS3LV02DL }, {}, }; MODULE_DEVICE_TABLE(i2c, st_accel_id_table); -- cgit v1.2.3 From 2b96d66c205b23b55324118ff31905d7152cb1cc Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 8 Jul 2017 13:00:17 +0200 Subject: iio: accel: st_accel_spi: add support to H3LIS331DL, LIS331DL, LIS3LV02DL Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_spi.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index 6b2eafa45cbb..7a2a0384b42d 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -30,6 +30,10 @@ static const struct of_device_id st_accel_of_match[] = { .compatible = "st,lis302dl-spi", .data = LIS3LV02DL_ACCEL_DEV_NAME, }, + { + .compatible = "st,lis3lv02dl-accel", + .data = LIS3LV02DL_ACCEL_DEV_NAME, + }, { .compatible = "st,lis3dh-accel", .data = LIS3DH_ACCEL_DEV_NAME, @@ -70,6 +74,14 @@ static const struct of_device_id st_accel_of_match[] = { .compatible = "st,lng2dm-accel", .data = LNG2DM_ACCEL_DEV_NAME, }, + { + .compatible = "st,h3lis331dl-accel", + .data = H3LIS331DL_DRIVER_NAME, + }, + { + .compatible = "st,lis331dl-accel", + .data = LIS331DL_ACCEL_DEV_NAME, + }, {} }; MODULE_DEVICE_TABLE(of, st_accel_of_match); @@ -118,6 +130,9 @@ static const struct spi_device_id st_accel_id_table[] = { { LIS2DH12_ACCEL_DEV_NAME }, { LIS3L02DQ_ACCEL_DEV_NAME }, { LNG2DM_ACCEL_DEV_NAME }, + { H3LIS331DL_DRIVER_NAME }, + { LIS331DL_ACCEL_DEV_NAME }, + { LIS3LV02DL_ACCEL_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(spi, st_accel_id_table); -- cgit v1.2.3 From b30f1593678efe786234e12a86e321391ff9407c Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 8 Jul 2017 13:00:18 +0200 Subject: iio: accel: st_accel: rename H3LIS331DL_DRIVER_NAME in H3LIS331DL_ACCEL_DEV_NAME In order to use the standard name convention rename H3LIS331DL_DRIVER_NAME macro in H3LIS331DL_ACCEL_DEV_NAME Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel.h | 2 +- drivers/iio/accel/st_accel_core.c | 2 +- drivers/iio/accel/st_accel_i2c.c | 4 ++-- drivers/iio/accel/st_accel_spi.c | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h index fe3864f0f146..0fe521609a3a 100644 --- a/drivers/iio/accel/st_accel.h +++ b/drivers/iio/accel/st_accel.h @@ -35,7 +35,7 @@ enum st_accel_type { ST_ACCEL_MAX, }; -#define H3LIS331DL_DRIVER_NAME "h3lis331dl_accel" +#define H3LIS331DL_ACCEL_DEV_NAME "h3lis331dl_accel" #define LIS3LV02DL_ACCEL_DEV_NAME "lis3lv02dl_accel" #define LSM303DLHC_ACCEL_DEV_NAME "lsm303dlhc_accel" #define LIS3DH_ACCEL_DEV_NAME "lis3dh" diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 07d1489cd457..c7709494bed3 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -444,7 +444,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .wai = 0x32, .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, .sensors_supported = { - [0] = H3LIS331DL_DRIVER_NAME, + [0] = H3LIS331DL_ACCEL_DEV_NAME, }, .ch = (struct iio_chan_spec *)st_accel_12bit_channels, .odr = { diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index 1b22c5714e89..18cafb9f2468 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -84,7 +84,7 @@ static const struct of_device_id st_accel_of_match[] = { }, { .compatible = "st,h3lis331dl-accel", - .data = H3LIS331DL_DRIVER_NAME, + .data = H3LIS331DL_ACCEL_DEV_NAME, }, { .compatible = "st,lis3l02dq", @@ -126,7 +126,7 @@ static const struct i2c_device_id st_accel_id_table[] = { { LIS2DH12_ACCEL_DEV_NAME, LIS2DH12 }, { LIS3L02DQ_ACCEL_DEV_NAME, LIS3L02DQ }, { LNG2DM_ACCEL_DEV_NAME, LNG2DM }, - { H3LIS331DL_DRIVER_NAME, H3LIS331DL }, + { H3LIS331DL_ACCEL_DEV_NAME, H3LIS331DL }, { LIS331DL_ACCEL_DEV_NAME, LIS331DL }, { LIS3LV02DL_ACCEL_DEV_NAME, LIS3LV02DL }, {}, diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index 7a2a0384b42d..915fa49085f7 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -76,7 +76,7 @@ static const struct of_device_id st_accel_of_match[] = { }, { .compatible = "st,h3lis331dl-accel", - .data = H3LIS331DL_DRIVER_NAME, + .data = H3LIS331DL_ACCEL_DEV_NAME, }, { .compatible = "st,lis331dl-accel", @@ -130,7 +130,7 @@ static const struct spi_device_id st_accel_id_table[] = { { LIS2DH12_ACCEL_DEV_NAME }, { LIS3L02DQ_ACCEL_DEV_NAME }, { LNG2DM_ACCEL_DEV_NAME }, - { H3LIS331DL_DRIVER_NAME }, + { H3LIS331DL_ACCEL_DEV_NAME }, { LIS331DL_ACCEL_DEV_NAME }, { LIS3LV02DL_ACCEL_DEV_NAME }, {}, -- cgit v1.2.3 From 5b4e17c9c45a93bae226fdaa2985c8b521f45bd4 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 10 Jul 2017 18:46:54 +0100 Subject: iio: adc: at91: make array startup_lookup static Don't populate array startup_lookup on the stack but instead make it static. Makes the object code smaller. Before: text data bss dec hex filename 21754 5440 128 27322 6aba drivers/iio/adc/at91_adc.o After: text data bss dec hex filename 21613 5496 128 27237 6a65 drivers/iio/adc/at91_adc.o Signed-off-by: Colin Ian King Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91_adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 34b928cefeed..15109728cae7 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -799,7 +799,7 @@ static u32 calc_startup_ticks_9x5(u32 startup_time, u32 adc_clk_khz) * For sama5d3x and at91sam9x5, the formula changes to: * Startup Time = / ADC Clock */ - const int startup_lookup[] = { + static const int startup_lookup[] = { 0, 8, 16, 24, 64, 80, 96, 112, 512, 576, 640, 704, -- cgit v1.2.3 From 0c1a1b6cba85de405cd4c656f8bd65e54724a3cb Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Mon, 10 Jul 2017 15:23:58 +0200 Subject: iio: dac: stm32: fix error message Fix error message, there's no 'st,dac-channel' property, but 'reg' (see https://lkml.org/lkml/2017/4/3/567). Signed-off-by: Fabrice Gasnier Signed-off-by: Jonathan Cameron --- drivers/iio/dac/stm32-dac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/dac/stm32-dac.c b/drivers/iio/dac/stm32-dac.c index 50f8ec091058..c1864e8aa851 100644 --- a/drivers/iio/dac/stm32-dac.c +++ b/drivers/iio/dac/stm32-dac.c @@ -268,7 +268,7 @@ static int stm32_dac_chan_of_init(struct iio_dev *indio_dev) break; } if (i >= ARRAY_SIZE(stm32_dac_channels)) { - dev_err(&indio_dev->dev, "Invalid st,dac-channel\n"); + dev_err(&indio_dev->dev, "Invalid reg property\n"); return -EINVAL; } -- cgit v1.2.3 From 9d9ebe64dc2e1b3cb6411c817527409fe94227b1 Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Mon, 10 Jul 2017 15:23:59 +0200 Subject: iio: dac: stm32: add support for stm32f4 This adds support for STM32F4 Digital-To-Analog converter. Add compatible configuration data to handle hfsel (not present in stm32f4). Signed-off-by: Fabrice Gasnier Signed-off-by: Jonathan Cameron --- drivers/iio/dac/stm32-dac-core.c | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/dac/stm32-dac-core.c b/drivers/iio/dac/stm32-dac-core.c index 75e48788c7ea..32701be71cf7 100644 --- a/drivers/iio/dac/stm32-dac-core.c +++ b/drivers/iio/dac/stm32-dac-core.c @@ -42,6 +42,14 @@ struct stm32_dac_priv { struct stm32_dac_common common; }; +/** + * struct stm32_dac_cfg - DAC configuration + * @has_hfsel: DAC has high frequency control + */ +struct stm32_dac_cfg { + bool has_hfsel; +}; + static struct stm32_dac_priv *to_stm32_dac_priv(struct stm32_dac_common *com) { return container_of(com, struct stm32_dac_priv, common); @@ -57,6 +65,7 @@ static const struct regmap_config stm32_dac_regmap_cfg = { static int stm32_dac_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + const struct stm32_dac_cfg *cfg; struct stm32_dac_priv *priv; struct regmap *regmap; struct resource *res; @@ -69,6 +78,8 @@ static int stm32_dac_probe(struct platform_device *pdev) priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + cfg = (const struct stm32_dac_cfg *) + of_match_device(dev->driver->of_match_table, dev)->data; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); mmio = devm_ioremap_resource(dev, res); @@ -121,12 +132,16 @@ static int stm32_dac_probe(struct platform_device *pdev) reset_control_deassert(priv->rst); } - /* When clock speed is higher than 80MHz, set HFSEL */ - priv->common.hfsel = (clk_get_rate(priv->pclk) > 80000000UL); - ret = regmap_update_bits(regmap, STM32_DAC_CR, STM32H7_DAC_CR_HFSEL, - priv->common.hfsel ? STM32H7_DAC_CR_HFSEL : 0); - if (ret) - goto err_pclk; + if (cfg && cfg->has_hfsel) { + /* When clock speed is higher than 80MHz, set HFSEL */ + priv->common.hfsel = (clk_get_rate(priv->pclk) > 80000000UL); + ret = regmap_update_bits(regmap, STM32_DAC_CR, + STM32H7_DAC_CR_HFSEL, + priv->common.hfsel ? + STM32H7_DAC_CR_HFSEL : 0); + if (ret) + goto err_pclk; + } platform_set_drvdata(pdev, &priv->common); @@ -158,8 +173,17 @@ static int stm32_dac_remove(struct platform_device *pdev) return 0; } +static const struct stm32_dac_cfg stm32h7_dac_cfg = { + .has_hfsel = true, +}; + static const struct of_device_id stm32_dac_of_match[] = { - { .compatible = "st,stm32h7-dac-core", }, + { + .compatible = "st,stm32f4-dac-core", + }, { + .compatible = "st,stm32h7-dac-core", + .data = (void *)&stm32h7_dac_cfg, + }, {}, }; MODULE_DEVICE_TABLE(of, stm32_dac_of_match); -- cgit v1.2.3 From bc4b2a518f20508d866b55b5ea2739f3ced18cb6 Mon Sep 17 00:00:00 2001 From: Gaurav Gupta Date: Tue, 27 Jun 2017 09:46:01 -0700 Subject: iio: core: Fix mapping of iio channels to entry numbers When adding maps to the list, they were added using list_add, which adds them in LIFO order. When parsing using iio_channel_get_all(), these elements are hence returned in reverse order. As a result, the iio_hwmon mapping maps the first entry to the last channel and so on. Signed-off-by: Gaurav Gupta Signed-off-by: Jonathan Cameron --- drivers/iio/inkern.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index a3941bade6a7..487bf6b75172 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -44,7 +44,7 @@ int iio_map_array_register(struct iio_dev *indio_dev, struct iio_map *maps) } mapi->map = &maps[i]; mapi->indio_dev = indio_dev; - list_add(&mapi->l, &iio_map_list); + list_add_tail(&mapi->l, &iio_map_list); i++; } error_ret: -- cgit v1.2.3 From 50db2cd408a4a8883e738874208c84527e3c6a41 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 15 Jul 2017 22:07:44 +0200 Subject: iio: orientation: hid-sensor-rotation: Drop unnecessary static Drop static on a local variable, when the variable is initialized before any possible use. Thus, the static has no benefit. The semantic patch that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @bad exists@ position p; identifier x; type T; @@ static T x@p; ... x = <+...x...+> @@ identifier x; expression e; type T; position p != bad.p; @@ -static T x@p; ... when != x when strict ?x = e; // Signed-off-by: Julia Lawall Signed-off-by: Jonathan Cameron --- drivers/iio/orientation/hid-sensor-rotation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index e9fa86c87db5..98fe0c5df380 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -238,7 +238,7 @@ static int dev_rot_parse_report(struct platform_device *pdev, static int hid_dev_rot_probe(struct platform_device *pdev) { int ret; - static char *name; + char *name; struct iio_dev *indio_dev; struct dev_rot_state *rot_state; struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; -- cgit v1.2.3 From 8b298fb789aaf093897d2f2b905d0fbee8663c2e Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sun, 16 Jul 2017 18:26:03 -0300 Subject: iio: adc: ad7766: Remove unneeded gpiod NULL check The gpiod API checks for NULL descriptors, so there is no need to duplicate the check in the driver. Signed-off-by: Fabio Estevam Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7766.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ad7766.c b/drivers/iio/adc/ad7766.c index 75cca42b6e70..ce45037295d8 100644 --- a/drivers/iio/adc/ad7766.c +++ b/drivers/iio/adc/ad7766.c @@ -103,8 +103,7 @@ static int ad7766_preenable(struct iio_dev *indio_dev) return ret; } - if (ad7766->pd_gpio) - gpiod_set_value(ad7766->pd_gpio, 0); + gpiod_set_value(ad7766->pd_gpio, 0); return 0; } @@ -113,8 +112,7 @@ static int ad7766_postdisable(struct iio_dev *indio_dev) { struct ad7766 *ad7766 = iio_priv(indio_dev); - if (ad7766->pd_gpio) - gpiod_set_value(ad7766->pd_gpio, 1); + gpiod_set_value(ad7766->pd_gpio, 1); /* * The PD pin is synchronous to the clock, so give it some time to -- cgit v1.2.3 From a94c24a712acb26f3712ec7bf56e9b13e40a2a74 Mon Sep 17 00:00:00 2001 From: Narcisa Ana Maria Vasile Date: Mon, 17 Jul 2017 22:28:03 +0300 Subject: iio: chemical: ccs811: Add support for AMS CCS811 VOC sensor Add support for CCS811 VOC sensor. This patch adds support for reading current and voltage across the sensor and TVOC and equivalent CO2 values. Scale and offset values have been computed according to datasheet: - For current: raw value is in microamps => 0.001 scale to convert to milliamps - For voltage: 1.65V = 1023, therefore 1650mV = 1023 => 1650.0/1023 = 1.612903 scale to convert to millivolts - For eCO2: raw value range is from 400ppm to 8192ppm. => (val - 400) * (100 - 0) / (8192 - 400) + 0 = (val - 400) * 0.01283367 => offset: -400, scale = 0.012834 to get a percentage value -For TVOC: raw value range is from 0ppb to 1187ppb. => (val - 0) * 100 / (1187 - 0) + 0 = val * 0.0842459 => scale = 0.084246 for getting a percentage value Cc: Daniel Baluta Cc: Alison Schofield Signed-off-by: Narcisa Ana Maria Vasile Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/Kconfig | 7 + drivers/iio/chemical/Makefile | 1 + drivers/iio/chemical/ccs811.c | 339 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 347 insertions(+) create mode 100644 drivers/iio/chemical/ccs811.c (limited to 'drivers/iio') diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig index cea7f9857a1f..4d799b5cceac 100644 --- a/drivers/iio/chemical/Kconfig +++ b/drivers/iio/chemical/Kconfig @@ -21,6 +21,13 @@ config ATLAS_PH_SENSOR To compile this driver as module, choose M here: the module will be called atlas-ph-sensor. +config CCS811 + tristate "AMS CCS811 VOC sensor" + depends on I2C + help + Say Y here to build I2C interface support for the AMS + CCS811 VOC (Volatile Organic Compounds) sensor + config IAQCORE tristate "AMS iAQ-Core VOC sensors" depends on I2C diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile index b02202b41289..a629b29d1e0b 100644 --- a/drivers/iio/chemical/Makefile +++ b/drivers/iio/chemical/Makefile @@ -4,5 +4,6 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_ATLAS_PH_SENSOR) += atlas-ph-sensor.o +obj-$(CONFIG_CCS811) += ccs811.o obj-$(CONFIG_IAQCORE) += ams-iaq-core.o obj-$(CONFIG_VZ89X) += vz89x.o diff --git a/drivers/iio/chemical/ccs811.c b/drivers/iio/chemical/ccs811.c new file mode 100644 index 000000000000..8dbb5eddeb1f --- /dev/null +++ b/drivers/iio/chemical/ccs811.c @@ -0,0 +1,339 @@ +/* + * ccs811.c - Support for AMS CCS811 VOC Sensor + * + * Copyright (C) 2017 Narcisa Vasile + * + * Datasheet: ams.com/content/download/951091/2269479/CCS811_DS000459_3-00.pdf + * + * 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. + * + * IIO driver for AMS CCS811 (I2C address 0x5A/0x5B set by ADDR Low/High) + * + * TODO: + * 1. Make the drive mode selectable form userspace + * 2. Add support for interrupts + * 3. Adjust time to wait for data to be ready based on selected operation mode + * 4. Read error register and put the information in logs + */ + +#include +#include +#include +#include + +#define CCS811_STATUS 0x00 +#define CCS811_MEAS_MODE 0x01 +#define CCS811_ALG_RESULT_DATA 0x02 +#define CCS811_RAW_DATA 0x03 +#define CCS811_HW_ID 0x20 +#define CCS881_HW_ID_VALUE 0x81 +#define CCS811_HW_VERSION 0x21 +#define CCS811_HW_VERSION_VALUE 0x10 +#define CCS811_HW_VERSION_MASK 0xF0 +#define CCS811_ERR 0xE0 +/* Used to transition from boot to application mode */ +#define CCS811_APP_START 0xF4 + +/* Status register flags */ +#define CCS811_STATUS_ERROR BIT(0) +#define CCS811_STATUS_DATA_READY BIT(3) +#define CCS811_STATUS_APP_VALID_MASK BIT(4) +#define CCS811_STATUS_APP_VALID_LOADED BIT(4) +/* + * Value of FW_MODE bit of STATUS register describes the sensor's state: + * 0: Firmware is in boot mode, this allows new firmware to be loaded + * 1: Firmware is in application mode. CCS811 is ready to take ADC measurements + */ +#define CCS811_STATUS_FW_MODE_MASK BIT(7) +#define CCS811_STATUS_FW_MODE_APPLICATION BIT(7) + +/* Measurement modes */ +#define CCS811_MODE_IDLE 0x00 +#define CCS811_MODE_IAQ_1SEC 0x10 +#define CCS811_MODE_IAQ_10SEC 0x20 +#define CCS811_MODE_IAQ_60SEC 0x30 +#define CCS811_MODE_RAW_DATA 0x40 + +#define CCS811_VOLTAGE_MASK 0x3FF + +struct ccs811_reading { + __be16 co2; + __be16 voc; + u8 status; + u8 error; + __be16 resistance; +} __attribute__((__packed__)); + +struct ccs811_data { + struct i2c_client *client; + struct mutex lock; /* Protect readings */ + struct ccs811_reading buffer; +}; + +static const struct iio_chan_spec ccs811_channels[] = { + { + .type = IIO_CURRENT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) + }, { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) + }, { + .type = IIO_CONCENTRATION, + .channel2 = IIO_MOD_CO2, + .modified = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE) + }, { + .type = IIO_CONCENTRATION, + .channel2 = IIO_MOD_VOC, + .modified = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) + }, +}; + +/* + * The CCS811 powers-up in boot mode. A setup write to CCS811_APP_START will + * transition the sensor to application mode. + */ +static int ccs811_start_sensor_application(struct i2c_client *client) +{ + int ret; + + ret = i2c_smbus_read_byte_data(client, CCS811_STATUS); + if (ret < 0) + return ret; + + if ((ret & CCS811_STATUS_APP_VALID_MASK) != + CCS811_STATUS_APP_VALID_LOADED) + return -EIO; + + ret = i2c_smbus_write_byte(client, CCS811_APP_START); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_byte_data(client, CCS811_STATUS); + if (ret < 0) + return ret; + + if ((ret & CCS811_STATUS_FW_MODE_MASK) != + CCS811_STATUS_FW_MODE_APPLICATION) { + dev_err(&client->dev, "Application failed to start. Sensor is still in boot mode.\n"); + return -EIO; + } + + return 0; +} + +static int ccs811_setup(struct i2c_client *client) +{ + int ret; + + ret = ccs811_start_sensor_application(client); + if (ret < 0) + return ret; + + return i2c_smbus_write_byte_data(client, CCS811_MEAS_MODE, + CCS811_MODE_IAQ_1SEC); +} + +static int ccs811_get_measurement(struct ccs811_data *data) +{ + int ret, tries = 11; + + /* Maximum waiting time: 1s, as measurements are made every second */ + while (tries-- > 0) { + ret = i2c_smbus_read_byte_data(data->client, CCS811_STATUS); + if (ret < 0) + return ret; + + if ((ret & CCS811_STATUS_DATA_READY) || tries == 0) + break; + msleep(100); + } + if (!(ret & CCS811_STATUS_DATA_READY)) + return -EIO; + + return i2c_smbus_read_i2c_block_data(data->client, + CCS811_ALG_RESULT_DATA, 8, + (char *)&data->buffer); +} + +static int ccs811_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ccs811_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&data->lock); + ret = ccs811_get_measurement(data); + if (ret < 0) { + mutex_unlock(&data->lock); + return ret; + } + + switch (chan->type) { + case IIO_VOLTAGE: + *val = be16_to_cpu(data->buffer.resistance) & + CCS811_VOLTAGE_MASK; + ret = IIO_VAL_INT; + break; + case IIO_CURRENT: + *val = be16_to_cpu(data->buffer.resistance) >> 10; + ret = IIO_VAL_INT; + break; + case IIO_CONCENTRATION: + switch (chan->channel2) { + case IIO_MOD_CO2: + *val = be16_to_cpu(data->buffer.co2); + ret = IIO_VAL_INT; + break; + case IIO_MOD_VOC: + *val = be16_to_cpu(data->buffer.voc); + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + } + break; + default: + ret = -EINVAL; + } + mutex_unlock(&data->lock); + + return ret; + + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + *val = 1; + *val2 = 612903; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CURRENT: + *val = 0; + *val2 = 1000; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CONCENTRATION: + switch (chan->channel2) { + case IIO_MOD_CO2: + *val = 0; + *val2 = 12834; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_MOD_VOC: + *val = 0; + *val2 = 84246; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + if (!(chan->type == IIO_CONCENTRATION && + chan->channel2 == IIO_MOD_CO2)) + return -EINVAL; + *val = -400; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static const struct iio_info ccs811_info = { + .read_raw = ccs811_read_raw, + .driver_module = THIS_MODULE, +}; + +static int ccs811_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct ccs811_data *data; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE + | I2C_FUNC_SMBUS_BYTE_DATA + | I2C_FUNC_SMBUS_READ_I2C_BLOCK)) + return -EOPNOTSUPP; + + /* Check hardware id (should be 0x81 for this family of devices) */ + ret = i2c_smbus_read_byte_data(client, CCS811_HW_ID); + if (ret < 0) + return ret; + + if (ret != CCS881_HW_ID_VALUE) { + dev_err(&client->dev, "hardware id doesn't match CCS81x\n"); + return -ENODEV; + } + + ret = i2c_smbus_read_byte_data(client, CCS811_HW_VERSION); + if (ret < 0) + return ret; + + if ((ret & CCS811_HW_VERSION_MASK) != CCS811_HW_VERSION_VALUE) { + dev_err(&client->dev, "no CCS811 sensor\n"); + return -ENODEV; + } + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + ret = ccs811_setup(client); + if (ret < 0) + return ret; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + + mutex_init(&data->lock); + + indio_dev->dev.parent = &client->dev; + indio_dev->name = id->name; + indio_dev->info = &ccs811_info; + + indio_dev->channels = ccs811_channels; + indio_dev->num_channels = ARRAY_SIZE(ccs811_channels); + + return iio_device_register(indio_dev); +} + +static int ccs811_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + iio_device_unregister(indio_dev); + + return i2c_smbus_write_byte_data(client, CCS811_MEAS_MODE, + CCS811_MODE_IDLE); +} + +static const struct i2c_device_id ccs811_id[] = { + {"ccs811", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, ccs811_id); + +static struct i2c_driver ccs811_driver = { + .driver = { + .name = "ccs811", + }, + .probe = ccs811_probe, + .remove = ccs811_remove, + .id_table = ccs811_id, +}; +module_i2c_driver(ccs811_driver); + +MODULE_AUTHOR("Narcisa Vasile "); +MODULE_DESCRIPTION("CCS811 volatile organic compounds sensor"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From bd49302a73340cc3d064e5959524732ea0eea1ef Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 17 Jul 2017 19:38:56 +0200 Subject: iio: humidity: hts221: refactor write_with_mask code Move bit-shift in hts221_write_with_mask() instead of coding the shift depth in the configured value. That change will be necessary to fix an issue in device power-down procedure. Simplify hts221_avg_list table management Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/hts221.h | 6 --- drivers/iio/humidity/hts221_core.c | 94 +++++++++++++++++--------------------- 2 files changed, 42 insertions(+), 58 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/humidity/hts221.h b/drivers/iio/humidity/hts221.h index 94510266e0a5..bf33d8f6396c 100644 --- a/drivers/iio/humidity/hts221.h +++ b/drivers/iio/humidity/hts221.h @@ -30,12 +30,6 @@ struct hts221_transfer_function { int (*write)(struct device *dev, u8 addr, int len, u8 *data); }; -#define HTS221_AVG_DEPTH 8 -struct hts221_avg_avl { - u16 avg; - u8 val; -}; - enum hts221_sensor_type { HTS221_SENSOR_H, HTS221_SENSOR_T, diff --git a/drivers/iio/humidity/hts221_core.c b/drivers/iio/humidity/hts221_core.c index a56da3999e00..d2f59626b040 100644 --- a/drivers/iio/humidity/hts221_core.c +++ b/drivers/iio/humidity/hts221_core.c @@ -32,30 +32,12 @@ #define HTS221_HUMIDITY_AVG_MASK 0x07 #define HTS221_TEMP_AVG_MASK 0x38 -#define HTS221_ODR_MASK 0x87 +#define HTS221_ODR_MASK 0x03 #define HTS221_BDU_MASK BIT(2) +#define HTS221_ENABLE_MASK BIT(7) #define HTS221_DRDY_MASK BIT(2) -#define HTS221_ENABLE_SENSOR BIT(7) - -#define HTS221_HUMIDITY_AVG_4 0x00 /* 0.4 %RH */ -#define HTS221_HUMIDITY_AVG_8 0x01 /* 0.3 %RH */ -#define HTS221_HUMIDITY_AVG_16 0x02 /* 0.2 %RH */ -#define HTS221_HUMIDITY_AVG_32 0x03 /* 0.15 %RH */ -#define HTS221_HUMIDITY_AVG_64 0x04 /* 0.1 %RH */ -#define HTS221_HUMIDITY_AVG_128 0x05 /* 0.07 %RH */ -#define HTS221_HUMIDITY_AVG_256 0x06 /* 0.05 %RH */ -#define HTS221_HUMIDITY_AVG_512 0x07 /* 0.03 %RH */ - -#define HTS221_TEMP_AVG_2 0x00 /* 0.08 degC */ -#define HTS221_TEMP_AVG_4 0x08 /* 0.05 degC */ -#define HTS221_TEMP_AVG_8 0x10 /* 0.04 degC */ -#define HTS221_TEMP_AVG_16 0x18 /* 0.03 degC */ -#define HTS221_TEMP_AVG_32 0x20 /* 0.02 degC */ -#define HTS221_TEMP_AVG_64 0x28 /* 0.015 degC */ -#define HTS221_TEMP_AVG_128 0x30 /* 0.01 degC */ -#define HTS221_TEMP_AVG_256 0x38 /* 0.007 degC */ /* calibration registers */ #define HTS221_REG_0RH_CAL_X_H 0x36 @@ -73,10 +55,11 @@ struct hts221_odr { u8 val; }; +#define HTS221_AVG_DEPTH 8 struct hts221_avg { u8 addr; u8 mask; - struct hts221_avg_avl avg_avl[HTS221_AVG_DEPTH]; + u16 avg_avl[HTS221_AVG_DEPTH]; }; static const struct hts221_odr hts221_odr_table[] = { @@ -90,28 +73,28 @@ static const struct hts221_avg hts221_avg_list[] = { .addr = HTS221_REG_AVG_ADDR, .mask = HTS221_HUMIDITY_AVG_MASK, .avg_avl = { - { 4, HTS221_HUMIDITY_AVG_4 }, - { 8, HTS221_HUMIDITY_AVG_8 }, - { 16, HTS221_HUMIDITY_AVG_16 }, - { 32, HTS221_HUMIDITY_AVG_32 }, - { 64, HTS221_HUMIDITY_AVG_64 }, - { 128, HTS221_HUMIDITY_AVG_128 }, - { 256, HTS221_HUMIDITY_AVG_256 }, - { 512, HTS221_HUMIDITY_AVG_512 }, + 4, /* 0.4 %RH */ + 8, /* 0.3 %RH */ + 16, /* 0.2 %RH */ + 32, /* 0.15 %RH */ + 64, /* 0.1 %RH */ + 128, /* 0.07 %RH */ + 256, /* 0.05 %RH */ + 512, /* 0.03 %RH */ }, }, { .addr = HTS221_REG_AVG_ADDR, .mask = HTS221_TEMP_AVG_MASK, .avg_avl = { - { 2, HTS221_TEMP_AVG_2 }, - { 4, HTS221_TEMP_AVG_4 }, - { 8, HTS221_TEMP_AVG_8 }, - { 16, HTS221_TEMP_AVG_16 }, - { 32, HTS221_TEMP_AVG_32 }, - { 64, HTS221_TEMP_AVG_64 }, - { 128, HTS221_TEMP_AVG_128 }, - { 256, HTS221_TEMP_AVG_256 }, + 2, /* 0.08 degC */ + 4, /* 0.05 degC */ + 8, /* 0.04 degC */ + 16, /* 0.03 degC */ + 32, /* 0.02 degC */ + 64, /* 0.015 degC */ + 128, /* 0.01 degC */ + 256, /* 0.007 degC */ }, }, }; @@ -166,7 +149,7 @@ static int hts221_write_with_mask(struct hts221_hw *hw, u8 addr, u8 mask, goto unlock; } - data = (data & ~mask) | (val & mask); + data = (data & ~mask) | ((val << __ffs(mask)) & mask); err = hw->tf->write(hw->dev, addr, sizeof(data), &data); if (err < 0) @@ -201,11 +184,10 @@ static int hts221_check_whoami(struct hts221_hw *hw) int hts221_config_drdy(struct hts221_hw *hw, bool enable) { - u8 val = enable ? BIT(2) : 0; int err; err = hts221_write_with_mask(hw, HTS221_REG_CNTRL3_ADDR, - HTS221_DRDY_MASK, val); + HTS221_DRDY_MASK, enable); return err < 0 ? err : 0; } @@ -213,7 +195,6 @@ int hts221_config_drdy(struct hts221_hw *hw, bool enable) static int hts221_update_odr(struct hts221_hw *hw, u8 odr) { int i, err; - u8 val; for (i = 0; i < ARRAY_SIZE(hts221_odr_table); i++) if (hts221_odr_table[i].hz == odr) @@ -222,9 +203,19 @@ static int hts221_update_odr(struct hts221_hw *hw, u8 odr) if (i == ARRAY_SIZE(hts221_odr_table)) return -EINVAL; - val = HTS221_ENABLE_SENSOR | HTS221_BDU_MASK | hts221_odr_table[i].val; + /* enable Block Data Update */ + err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR, + HTS221_BDU_MASK, 1); + if (err < 0) + return err; + + err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR, + HTS221_ODR_MASK, hts221_odr_table[i].val); + if (err < 0) + return err; + err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR, - HTS221_ODR_MASK, val); + HTS221_ENABLE_MASK, 1); if (err < 0) return err; @@ -241,14 +232,13 @@ static int hts221_update_avg(struct hts221_hw *hw, const struct hts221_avg *avg = &hts221_avg_list[type]; for (i = 0; i < HTS221_AVG_DEPTH; i++) - if (avg->avg_avl[i].avg == val) + if (avg->avg_avl[i] == val) break; if (i == HTS221_AVG_DEPTH) return -EINVAL; - err = hts221_write_with_mask(hw, avg->addr, avg->mask, - avg->avg_avl[i].val); + err = hts221_write_with_mask(hw, avg->addr, avg->mask, i); if (err < 0) return err; @@ -283,7 +273,7 @@ hts221_sysfs_rh_oversampling_avail(struct device *dev, for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++) len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", - avg->avg_avl[i].avg); + avg->avg_avl[i]); buf[len - 1] = '\n'; return len; @@ -300,7 +290,7 @@ hts221_sysfs_temp_oversampling_avail(struct device *dev, for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++) len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", - avg->avg_avl[i].avg); + avg->avg_avl[i]); buf[len - 1] = '\n'; return len; @@ -534,13 +524,13 @@ static int hts221_read_raw(struct iio_dev *iio_dev, case IIO_HUMIDITYRELATIVE: avg = &hts221_avg_list[HTS221_SENSOR_H]; idx = hw->sensors[HTS221_SENSOR_H].cur_avg_idx; - *val = avg->avg_avl[idx].avg; + *val = avg->avg_avl[idx]; ret = IIO_VAL_INT; break; case IIO_TEMP: avg = &hts221_avg_list[HTS221_SENSOR_T]; idx = hw->sensors[HTS221_SENSOR_T].cur_avg_idx; - *val = avg->avg_avl[idx].avg; + *val = avg->avg_avl[idx]; ret = IIO_VAL_INT; break; default: @@ -661,7 +651,7 @@ int hts221_probe(struct iio_dev *iio_dev) return err; } - data = hts221_avg_list[HTS221_SENSOR_H].avg_avl[3].avg; + data = hts221_avg_list[HTS221_SENSOR_H].avg_avl[3]; err = hts221_update_avg(hw, HTS221_SENSOR_H, data); if (err < 0) { dev_err(hw->dev, "failed to set rh oversampling ratio\n"); @@ -676,7 +666,7 @@ int hts221_probe(struct iio_dev *iio_dev) return err; } - data = hts221_avg_list[HTS221_SENSOR_T].avg_avl[3].avg; + data = hts221_avg_list[HTS221_SENSOR_T].avg_avl[3]; err = hts221_update_avg(hw, HTS221_SENSOR_T, data); if (err < 0) { dev_err(hw->dev, -- cgit v1.2.3 From 2ede27402a66a896015491d9524f982c522a740a Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 17 Jul 2017 19:38:57 +0200 Subject: iio: humidity: hts221: move BDU configuration in probe routine Enable Block Data Update in hts221_probe() in order to avoid to reconfigure it every time the sensor is enabled Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/hts221_core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/humidity/hts221_core.c b/drivers/iio/humidity/hts221_core.c index d2f59626b040..47a29b2da1ce 100644 --- a/drivers/iio/humidity/hts221_core.c +++ b/drivers/iio/humidity/hts221_core.c @@ -203,12 +203,6 @@ static int hts221_update_odr(struct hts221_hw *hw, u8 odr) if (i == ARRAY_SIZE(hts221_odr_table)) return -EINVAL; - /* enable Block Data Update */ - err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR, - HTS221_BDU_MASK, 1); - if (err < 0) - return err; - err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR, HTS221_ODR_MASK, hts221_odr_table[i].val); if (err < 0) @@ -644,6 +638,12 @@ int hts221_probe(struct iio_dev *iio_dev) iio_dev->name = HTS221_DEV_NAME; iio_dev->info = &hts221_info; + /* enable Block Data Update */ + err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR, + HTS221_BDU_MASK, 1); + if (err < 0) + return err; + /* configure humidity sensor */ err = hts221_parse_rh_caldata(hw); if (err < 0) { -- cgit v1.2.3 From f6f58d9d93fdb53f24b0086ce1c51d3388aa6e53 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 17 Jul 2017 19:38:58 +0200 Subject: iio: humidity: hts221: do not overwrite reserved data during power-down GENMASK(6, 3) in CTRL_REG1 and GENMASK(6, 2) in CTRL_REG2 are marked reserved. Do not changed the original value (not declared in the datasheet) during power-down/suspend routines. Fixes: e4a70e3e7d84 (iio: humidity: add support to hts221 rh/temp device) Fixes: b7079eeac5da (iio: humidity: hts221: add power management support) Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/hts221_core.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/humidity/hts221_core.c b/drivers/iio/humidity/hts221_core.c index 47a29b2da1ce..6e5847386a45 100644 --- a/drivers/iio/humidity/hts221_core.c +++ b/drivers/iio/humidity/hts221_core.c @@ -305,11 +305,10 @@ int hts221_power_on(struct hts221_hw *hw) int hts221_power_off(struct hts221_hw *hw) { - __le16 data = 0; int err; - err = hw->tf->write(hw->dev, HTS221_REG_CNTRL1_ADDR, sizeof(data), - (u8 *)&data); + err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR, + HTS221_ENABLE_MASK, false); if (err < 0) return err; @@ -692,11 +691,10 @@ static int __maybe_unused hts221_suspend(struct device *dev) { struct iio_dev *iio_dev = dev_get_drvdata(dev); struct hts221_hw *hw = iio_priv(iio_dev); - __le16 data = 0; int err; - err = hw->tf->write(hw->dev, HTS221_REG_CNTRL1_ADDR, sizeof(data), - (u8 *)&data); + err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR, + HTS221_ENABLE_MASK, false); return err < 0 ? err : 0; } -- cgit v1.2.3 From ffebe74b7c95a41d2d0ac70a44d410e0efa37ad8 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 17 Jul 2017 19:38:59 +0200 Subject: iio: humidity: hts221: avoid useless ODR reconfiguration Configure sensor ODR just in hts221_write_raw() in order to avoid to set device sample rate multiple times. Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/hts221_core.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/humidity/hts221_core.c b/drivers/iio/humidity/hts221_core.c index 6e5847386a45..f99adc44139e 100644 --- a/drivers/iio/humidity/hts221_core.c +++ b/drivers/iio/humidity/hts221_core.c @@ -208,11 +208,6 @@ static int hts221_update_odr(struct hts221_hw *hw, u8 odr) if (err < 0) return err; - err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR, - HTS221_ENABLE_MASK, 1); - if (err < 0) - return err; - hw->odr = odr; return 0; @@ -294,7 +289,8 @@ int hts221_power_on(struct hts221_hw *hw) { int err; - err = hts221_update_odr(hw, hw->odr); + err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR, + HTS221_ENABLE_MASK, true); if (err < 0) return err; @@ -627,8 +623,6 @@ int hts221_probe(struct iio_dev *iio_dev) if (err < 0) return err; - hw->odr = hts221_odr_table[0].hz; - iio_dev->modes = INDIO_DIRECT_MODE; iio_dev->dev.parent = hw->dev; iio_dev->available_scan_masks = hts221_scan_masks; @@ -643,6 +637,10 @@ int hts221_probe(struct iio_dev *iio_dev) if (err < 0) return err; + err = hts221_update_odr(hw, hts221_odr_table[0].hz); + if (err < 0) + return err; + /* configure humidity sensor */ err = hts221_parse_rh_caldata(hw); if (err < 0) { @@ -706,7 +704,8 @@ static int __maybe_unused hts221_resume(struct device *dev) int err = 0; if (hw->enabled) - err = hts221_update_odr(hw, hw->odr); + err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR, + HTS221_ENABLE_MASK, true); return err; } -- cgit v1.2.3 From e3e25446a3d2d1beb2443b65a919e59a060182d5 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 17 Jul 2017 19:39:00 +0200 Subject: iio: humidity: hts221: squash hts221_power_on/off in hts221_set_enable Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/hts221.h | 3 +-- drivers/iio/humidity/hts221_buffer.c | 4 ++-- drivers/iio/humidity/hts221_core.c | 24 +++++------------------- 3 files changed, 8 insertions(+), 23 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/humidity/hts221.h b/drivers/iio/humidity/hts221.h index bf33d8f6396c..69dfa5c63ec2 100644 --- a/drivers/iio/humidity/hts221.h +++ b/drivers/iio/humidity/hts221.h @@ -62,8 +62,7 @@ extern const struct dev_pm_ops hts221_pm_ops; int hts221_config_drdy(struct hts221_hw *hw, bool enable); int hts221_probe(struct iio_dev *iio_dev); -int hts221_power_on(struct hts221_hw *hw); -int hts221_power_off(struct hts221_hw *hw); +int hts221_set_enable(struct hts221_hw *hw, bool enable); int hts221_allocate_buffers(struct hts221_hw *hw); int hts221_allocate_trigger(struct hts221_hw *hw); diff --git a/drivers/iio/humidity/hts221_buffer.c b/drivers/iio/humidity/hts221_buffer.c index 7d19a3da7ab7..c4ed153ad397 100644 --- a/drivers/iio/humidity/hts221_buffer.c +++ b/drivers/iio/humidity/hts221_buffer.c @@ -109,12 +109,12 @@ int hts221_allocate_trigger(struct hts221_hw *hw) static int hts221_buffer_preenable(struct iio_dev *iio_dev) { - return hts221_power_on(iio_priv(iio_dev)); + return hts221_set_enable(iio_priv(iio_dev), true); } static int hts221_buffer_postdisable(struct iio_dev *iio_dev) { - return hts221_power_off(iio_priv(iio_dev)); + return hts221_set_enable(iio_priv(iio_dev), false); } static const struct iio_buffer_setup_ops hts221_buffer_ops = { diff --git a/drivers/iio/humidity/hts221_core.c b/drivers/iio/humidity/hts221_core.c index f99adc44139e..dc55d9fbc702 100644 --- a/drivers/iio/humidity/hts221_core.c +++ b/drivers/iio/humidity/hts221_core.c @@ -285,30 +285,16 @@ hts221_sysfs_temp_oversampling_avail(struct device *dev, return len; } -int hts221_power_on(struct hts221_hw *hw) +int hts221_set_enable(struct hts221_hw *hw, bool enable) { int err; err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR, - HTS221_ENABLE_MASK, true); + HTS221_ENABLE_MASK, enable); if (err < 0) return err; - hw->enabled = true; - - return 0; -} - -int hts221_power_off(struct hts221_hw *hw) -{ - int err; - - err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR, - HTS221_ENABLE_MASK, false); - if (err < 0) - return err; - - hw->enabled = false; + hw->enabled = enable; return 0; } @@ -463,7 +449,7 @@ static int hts221_read_oneshot(struct hts221_hw *hw, u8 addr, int *val) u8 data[HTS221_DATA_SIZE]; int err; - err = hts221_power_on(hw); + err = hts221_set_enable(hw, true); if (err < 0) return err; @@ -473,7 +459,7 @@ static int hts221_read_oneshot(struct hts221_hw *hw, u8 addr, int *val) if (err < 0) return err; - hts221_power_off(hw); + hts221_set_enable(hw, false); *val = (s16)get_unaligned_le16(data); -- cgit v1.2.3 From e7b4b45e1e1f22585cfc986b1c5c99e124def10e Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 17 Jul 2017 19:39:01 +0200 Subject: iio: humidity: hts221: support active-low interrupts Add support for active low interrupts (IRQF_TRIGGER_LOW and IRQF_TRIGGER_FALLING). Configure the device as active high or low according to the requested irq line. Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/hts221.h | 1 + drivers/iio/humidity/hts221_buffer.c | 11 +++++++++++ drivers/iio/humidity/hts221_core.c | 3 +-- 3 files changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/humidity/hts221.h b/drivers/iio/humidity/hts221.h index 69dfa5c63ec2..c3de6e494f8c 100644 --- a/drivers/iio/humidity/hts221.h +++ b/drivers/iio/humidity/hts221.h @@ -61,6 +61,7 @@ struct hts221_hw { extern const struct dev_pm_ops hts221_pm_ops; int hts221_config_drdy(struct hts221_hw *hw, bool enable); +int hts221_write_with_mask(struct hts221_hw *hw, u8 addr, u8 mask, u8 val); int hts221_probe(struct iio_dev *iio_dev); int hts221_set_enable(struct hts221_hw *hw, bool enable); int hts221_allocate_buffers(struct hts221_hw *hw); diff --git a/drivers/iio/humidity/hts221_buffer.c b/drivers/iio/humidity/hts221_buffer.c index c4ed153ad397..ad5222295b2c 100644 --- a/drivers/iio/humidity/hts221_buffer.c +++ b/drivers/iio/humidity/hts221_buffer.c @@ -22,6 +22,8 @@ #include "hts221.h" +#define HTS221_REG_DRDY_HL_ADDR 0x22 +#define HTS221_REG_DRDY_HL_MASK BIT(7) #define HTS221_REG_STATUS_ADDR 0x27 #define HTS221_RH_DRDY_MASK BIT(1) #define HTS221_TEMP_DRDY_MASK BIT(0) @@ -67,6 +69,7 @@ static irqreturn_t hts221_trigger_handler_thread(int irq, void *private) int hts221_allocate_trigger(struct hts221_hw *hw) { struct iio_dev *iio_dev = iio_priv_to_dev(hw); + bool irq_active_low = false; unsigned long irq_type; int err; @@ -76,6 +79,10 @@ int hts221_allocate_trigger(struct hts221_hw *hw) case IRQF_TRIGGER_HIGH: case IRQF_TRIGGER_RISING: break; + case IRQF_TRIGGER_LOW: + case IRQF_TRIGGER_FALLING: + irq_active_low = true; + break; default: dev_info(hw->dev, "mode %lx unsupported, using IRQF_TRIGGER_RISING\n", @@ -84,6 +91,10 @@ int hts221_allocate_trigger(struct hts221_hw *hw) break; } + err = hts221_write_with_mask(hw, HTS221_REG_DRDY_HL_ADDR, + HTS221_REG_DRDY_HL_MASK, irq_active_low); + if (err < 0) + return err; err = devm_request_threaded_irq(hw->dev, hw->irq, NULL, hts221_trigger_handler_thread, irq_type | IRQF_ONESHOT, diff --git a/drivers/iio/humidity/hts221_core.c b/drivers/iio/humidity/hts221_core.c index dc55d9fbc702..25fcb9993998 100644 --- a/drivers/iio/humidity/hts221_core.c +++ b/drivers/iio/humidity/hts221_core.c @@ -135,8 +135,7 @@ static const struct iio_chan_spec hts221_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(2), }; -static int hts221_write_with_mask(struct hts221_hw *hw, u8 addr, u8 mask, - u8 val) +int hts221_write_with_mask(struct hts221_hw *hw, u8 addr, u8 mask, u8 val) { u8 data; int err; -- cgit v1.2.3 From 3921db46a8c5bc256c5956a04ad49cbbeac28e7d Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 18 Jul 2017 16:43:08 -0500 Subject: iio: Convert to using %pOF instead of full_name Now that we have a custom printf format specifier, convert users of full_name to use %pOF instead. This is preparation to remove storing of the full path string for each node. Signed-off-by: Rob Herring Cc: Jonathan Cameron Cc: Hartmut Knaack Cc: Lars-Peter Clausen Cc: Peter Meerwald-Stadler Cc: Carlo Caione Cc: Kevin Hilman Cc: linux-iio@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Cc: linux-amlogic@lists.infradead.org Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max9611.c | 4 ++-- drivers/iio/adc/meson_saradc.c | 8 ++++---- drivers/iio/adc/ti-ads1015.c | 16 ++++++++-------- drivers/iio/inkern.c | 4 ++-- 4 files changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/max9611.c b/drivers/iio/adc/max9611.c index b0526e4b9530..b1dd17cbce58 100644 --- a/drivers/iio/adc/max9611.c +++ b/drivers/iio/adc/max9611.c @@ -549,8 +549,8 @@ static int max9611_probe(struct i2c_client *client, ret = of_property_read_u32(of_node, shunt_res_prop, &of_shunt); if (ret) { dev_err(&client->dev, - "Missing %s property for %s node\n", - shunt_res_prop, of_node->full_name); + "Missing %s property for %pOF node\n", + shunt_res_prop, of_node); return ret; } max9611->shunt_resistor_uohm = of_shunt; diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index c4e904bbf168..2e8dbb89c8c9 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -572,8 +572,8 @@ static int meson_sar_adc_clk_init(struct iio_dev *indio_dev, struct clk_init_data init; const char *clk_parents[1]; - init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%s#adc_div", - of_node_full_name(indio_dev->dev.of_node)); + init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%pOF#adc_div", + indio_dev->dev.of_node); init.flags = 0; init.ops = &clk_divider_ops; clk_parents[0] = __clk_get_name(priv->clkin); @@ -591,8 +591,8 @@ static int meson_sar_adc_clk_init(struct iio_dev *indio_dev, if (WARN_ON(IS_ERR(priv->adc_div_clk))) return PTR_ERR(priv->adc_div_clk); - init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%s#adc_en", - of_node_full_name(indio_dev->dev.of_node)); + init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%pOF#adc_en", + indio_dev->dev.of_node); init.flags = CLK_SET_RATE_PARENT; init.ops = &clk_gate_ops; clk_parents[0] = __clk_get_name(priv->adc_div_clk); diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index f76d979fb7e8..5866118fafbf 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -505,24 +505,24 @@ static int ads1015_get_channels_config_of(struct i2c_client *client) unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE; if (of_property_read_u32(node, "reg", &pval)) { - dev_err(&client->dev, "invalid reg on %s\n", - node->full_name); + dev_err(&client->dev, "invalid reg on %pOF\n", + node); continue; } channel = pval; if (channel >= ADS1015_CHANNELS) { dev_err(&client->dev, - "invalid channel index %d on %s\n", - channel, node->full_name); + "invalid channel index %d on %pOF\n", + channel, node); continue; } if (!of_property_read_u32(node, "ti,gain", &pval)) { pga = pval; if (pga > 6) { - dev_err(&client->dev, "invalid gain on %s\n", - node->full_name); + dev_err(&client->dev, "invalid gain on %pOF\n", + node); of_node_put(node); return -EINVAL; } @@ -532,8 +532,8 @@ static int ads1015_get_channels_config_of(struct i2c_client *client) data_rate = pval; if (data_rate > 7) { dev_err(&client->dev, - "invalid data_rate on %s\n", - node->full_name); + "invalid data_rate on %pOF\n", + node); of_node_put(node); return -EINVAL; } diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 487bf6b75172..6c69da942321 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -205,8 +205,8 @@ static struct iio_channel *of_iio_channel_get_by_name(struct device_node *np, if (!IS_ERR(chan) || PTR_ERR(chan) == -EPROBE_DEFER) break; else if (name && index >= 0) { - pr_err("ERROR: could not get IIO channel %s:%s(%i)\n", - np->full_name, name ? name : "", index); + pr_err("ERROR: could not get IIO channel %pOF:%s(%i)\n", + np, name ? name : "", index); return NULL; } -- cgit v1.2.3 From 8614206a054a2b53237d5d5c47f8669477cf267f Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Mon, 24 Jul 2017 22:51:45 +0200 Subject: iio: adc: New driver for Cirrus Logic EP93xx ADC New driver adding support for ADC found on Cirrus Logic EP93xx series of SoCs. Board specific code must take care to create plaform device with all necessary resources. Signed-off-by: Alexander Sverdlin Acked-by: Jonathan Cameron Signed-off-by: Jonathan Cameron --- Documentation/iio/ep93xx_adc.txt | 29 +++++ drivers/iio/adc/Kconfig | 11 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ep93xx_adc.c | 255 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 296 insertions(+) create mode 100644 Documentation/iio/ep93xx_adc.txt create mode 100644 drivers/iio/adc/ep93xx_adc.c (limited to 'drivers/iio') diff --git a/Documentation/iio/ep93xx_adc.txt b/Documentation/iio/ep93xx_adc.txt new file mode 100644 index 000000000000..23053e7817bd --- /dev/null +++ b/Documentation/iio/ep93xx_adc.txt @@ -0,0 +1,29 @@ +Cirrus Logic EP93xx ADC driver. + +1. Overview + +The driver is intended to work on both low-end (EP9301, EP9302) devices with +5-channel ADC and high-end (EP9307, EP9312, EP9315) devices with 10-channel +touchscreen/ADC module. + +2. Channel numbering + +Numbering scheme for channels 0..4 is defined in EP9301 and EP9302 datasheets. +EP9307, EP9312 and EP9312 have 3 channels more (total 8), but the numbering is +not defined. So the last three are numbered randomly, let's say. + +Assuming ep93xx_adc is IIO device0, you'd find the following entries under +/sys/bus/iio/devices/iio:device0/: + + +-----------------+---------------+ + | sysfs entry | ball/pin name | + +-----------------+---------------+ + | in_voltage0_raw | YM | + | in_voltage1_raw | SXP | + | in_voltage2_raw | SXM | + | in_voltage3_raw | SYP | + | in_voltage4_raw | SYM | + | in_voltage5_raw | XP | + | in_voltage6_raw | XM | + | in_voltage7_raw | YP | + +-----------------+---------------+ diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 17e61c5e9ffd..e4eeebac5297 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -258,6 +258,17 @@ config ENVELOPE_DETECTOR To compile this driver as a module, choose M here: the module will be called envelope-detector. +config EP93XX_ADC + tristate "Cirrus Logic EP93XX ADC driver" + depends on ARCH_EP93XX + help + Driver for the ADC module on the EP93XX series of SoC from Cirrus Logic. + It's recommended to switch on CONFIG_HIGH_RES_TIMERS option, in this + case driver will reduce its CPU usage by 90% in some use cases. + + To compile this driver as a module, choose M here: the module will be + called ep93xx_adc. + config EXYNOS_ADC tristate "Exynos ADC driver support" depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST) diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 4abb7af9e5bb..9874e05f52d7 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o obj-$(CONFIG_DLN2_ADC) += dln2-adc.o obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o +obj-$(CONFIG_EP93XX_ADC) += ep93xx_adc.o obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o obj-$(CONFIG_HI8435) += hi8435.o diff --git a/drivers/iio/adc/ep93xx_adc.c b/drivers/iio/adc/ep93xx_adc.c new file mode 100644 index 000000000000..a179ac476c6d --- /dev/null +++ b/drivers/iio/adc/ep93xx_adc.c @@ -0,0 +1,255 @@ +/* + * Driver for ADC module on the Cirrus Logic EP93xx series of SoCs + * + * Copyright (C) 2015 Alexander Sverdlin + * + * 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. + * + * The driver uses polling to get the conversion status. According to EP93xx + * datasheets, reading ADCResult register starts the conversion, but user is also + * responsible for ensuring that delay between adjacent conversion triggers is + * long enough so that maximum allowed conversion rate is not exceeded. This + * basically renders IRQ mode unusable. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * This code could benefit from real HR Timers, but jiffy granularity would + * lower ADC conversion rate down to CONFIG_HZ, so we fallback to busy wait + * in such case. + * + * HR Timers-based version loads CPU only up to 10% during back to back ADC + * conversion, while busy wait-based version consumes whole CPU power. + */ +#ifdef CONFIG_HIGH_RES_TIMERS +#define ep93xx_adc_delay(usmin, usmax) usleep_range(usmin, usmax) +#else +#define ep93xx_adc_delay(usmin, usmax) udelay(usmin) +#endif + +#define EP93XX_ADC_RESULT 0x08 +#define EP93XX_ADC_SDR BIT(31) +#define EP93XX_ADC_SWITCH 0x18 +#define EP93XX_ADC_SW_LOCK 0x20 + +struct ep93xx_adc_priv { + struct clk *clk; + void __iomem *base; + int lastch; + struct mutex lock; +}; + +#define EP93XX_ADC_CH(index, dname, swcfg) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .address = swcfg, \ + .datasheet_name = dname, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ +} + +/* + * Numbering scheme for channels 0..4 is defined in EP9301 and EP9302 datasheets. + * EP9307, EP9312 and EP9312 have 3 channels more (total 8), but the numbering is + * not defined. So the last three are numbered randomly, let's say. + */ +static const struct iio_chan_spec ep93xx_adc_channels[8] = { + EP93XX_ADC_CH(0, "YM", 0x608), + EP93XX_ADC_CH(1, "SXP", 0x680), + EP93XX_ADC_CH(2, "SXM", 0x640), + EP93XX_ADC_CH(3, "SYP", 0x620), + EP93XX_ADC_CH(4, "SYM", 0x610), + EP93XX_ADC_CH(5, "XP", 0x601), + EP93XX_ADC_CH(6, "XM", 0x602), + EP93XX_ADC_CH(7, "YP", 0x604), +}; + +static int ep93xx_read_raw(struct iio_dev *iiodev, + struct iio_chan_spec const *channel, int *value, + int *shift, long mask) +{ + struct ep93xx_adc_priv *priv = iio_priv(iiodev); + unsigned long timeout; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&priv->lock); + if (priv->lastch != channel->channel) { + priv->lastch = channel->channel; + /* + * Switch register is software-locked, unlocking must be + * immediately followed by write + */ + local_irq_disable(); + writel_relaxed(0xAA, priv->base + EP93XX_ADC_SW_LOCK); + writel_relaxed(channel->address, + priv->base + EP93XX_ADC_SWITCH); + local_irq_enable(); + /* + * Settling delay depends on module clock and could be + * 2ms or 500us + */ + ep93xx_adc_delay(2000, 2000); + } + /* Start the conversion, eventually discarding old result */ + readl_relaxed(priv->base + EP93XX_ADC_RESULT); + /* Ensure maximum conversion rate is not exceeded */ + ep93xx_adc_delay(DIV_ROUND_UP(1000000, 925), + DIV_ROUND_UP(1000000, 925)); + /* At this point conversion must be completed, but anyway... */ + ret = IIO_VAL_INT; + timeout = jiffies + msecs_to_jiffies(1) + 1; + while (1) { + u32 t; + + t = readl_relaxed(priv->base + EP93XX_ADC_RESULT); + if (t & EP93XX_ADC_SDR) { + *value = sign_extend32(t, 15); + break; + } + + if (time_after(jiffies, timeout)) { + dev_err(&iiodev->dev, "Conversion timeout\n"); + ret = -ETIMEDOUT; + break; + } + + cpu_relax(); + } + mutex_unlock(&priv->lock); + return ret; + + case IIO_CHAN_INFO_OFFSET: + /* According to datasheet, range is -25000..25000 */ + *value = 25000; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + /* Typical supply voltage is 3.3v */ + *value = (1ULL << 32) * 3300 / 50000; + *shift = 32; + return IIO_VAL_FRACTIONAL_LOG2; + } + + return -EINVAL; +} + +static const struct iio_info ep93xx_adc_info = { + .driver_module = THIS_MODULE, + .read_raw = ep93xx_read_raw, +}; + +static int ep93xx_adc_probe(struct platform_device *pdev) +{ + int ret; + struct iio_dev *iiodev; + struct ep93xx_adc_priv *priv; + struct clk *pclk; + struct resource *res; + + iiodev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv)); + if (!iiodev) + return -ENOMEM; + priv = iio_priv(iiodev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Cannot obtain memory resource\n"); + return -ENXIO; + } + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) { + dev_err(&pdev->dev, "Cannot map memory resource\n"); + return PTR_ERR(priv->base); + } + + iiodev->dev.parent = &pdev->dev; + iiodev->name = dev_name(&pdev->dev); + iiodev->modes = INDIO_DIRECT_MODE; + iiodev->info = &ep93xx_adc_info; + iiodev->num_channels = ARRAY_SIZE(ep93xx_adc_channels); + iiodev->channels = ep93xx_adc_channels; + + priv->lastch = -1; + mutex_init(&priv->lock); + + platform_set_drvdata(pdev, iiodev); + + priv->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(&pdev->dev, "Cannot obtain clock\n"); + return PTR_ERR(priv->clk); + } + + pclk = clk_get_parent(priv->clk); + if (!pclk) { + dev_warn(&pdev->dev, "Cannot obtain parent clock\n"); + } else { + /* + * This is actually a place for improvement: + * EP93xx ADC supports two clock divisors -- 4 and 16, + * resulting in conversion rates 3750 and 925 samples per second + * with 500us or 2ms settling time respectively. + * One might find this interesting enough to be configurable. + */ + ret = clk_set_rate(priv->clk, clk_get_rate(pclk) / 16); + if (ret) + dev_warn(&pdev->dev, "Cannot set clock rate\n"); + /* + * We can tolerate rate setting failure because the module should + * work in any case. + */ + } + + ret = clk_enable(priv->clk); + if (ret) { + dev_err(&pdev->dev, "Cannot enable clock\n"); + return ret; + } + + ret = iio_device_register(iiodev); + if (ret) + clk_disable(priv->clk); + + return ret; +} + +static int ep93xx_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *iiodev = platform_get_drvdata(pdev); + struct ep93xx_adc_priv *priv = iio_priv(iiodev); + + iio_device_unregister(iiodev); + clk_disable(priv->clk); + + return 0; +} + +static struct platform_driver ep93xx_adc_driver = { + .driver = { + .name = "ep93xx-adc", + }, + .probe = ep93xx_adc_probe, + .remove = ep93xx_adc_remove, +}; +module_platform_driver(ep93xx_adc_driver); + +MODULE_AUTHOR("Alexander Sverdlin "); +MODULE_DESCRIPTION("Cirrus Logic EP93XX ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ep93xx-adc"); -- cgit v1.2.3 From 9251f7aaa53c0315c45ee000461002f971f01ea0 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 17 Jul 2017 19:39:03 +0200 Subject: iio: humidity: hts221: support open drain mode Add open drain support in order to share requested IRQ line between hts221 device and other peripherals Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/hts221_buffer.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/humidity/hts221_buffer.c b/drivers/iio/humidity/hts221_buffer.c index ad5222295b2c..f29f01a22375 100644 --- a/drivers/iio/humidity/hts221_buffer.c +++ b/drivers/iio/humidity/hts221_buffer.c @@ -20,10 +20,14 @@ #include #include +#include + #include "hts221.h" #define HTS221_REG_DRDY_HL_ADDR 0x22 #define HTS221_REG_DRDY_HL_MASK BIT(7) +#define HTS221_REG_DRDY_PP_OD_ADDR 0x22 +#define HTS221_REG_DRDY_PP_OD_MASK BIT(6) #define HTS221_REG_STATUS_ADDR 0x27 #define HTS221_RH_DRDY_MASK BIT(1) #define HTS221_TEMP_DRDY_MASK BIT(0) @@ -69,7 +73,9 @@ static irqreturn_t hts221_trigger_handler_thread(int irq, void *private) int hts221_allocate_trigger(struct hts221_hw *hw) { struct iio_dev *iio_dev = iio_priv_to_dev(hw); - bool irq_active_low = false; + bool irq_active_low = false, open_drain = false; + struct device_node *np = hw->dev->of_node; + struct st_sensors_platform_data *pdata; unsigned long irq_type; int err; @@ -95,6 +101,20 @@ int hts221_allocate_trigger(struct hts221_hw *hw) HTS221_REG_DRDY_HL_MASK, irq_active_low); if (err < 0) return err; + + pdata = (struct st_sensors_platform_data *)hw->dev->platform_data; + if ((np && of_property_read_bool(np, "drive-open-drain")) || + (pdata && pdata->open_drain)) { + irq_type |= IRQF_SHARED; + open_drain = true; + } + + err = hts221_write_with_mask(hw, HTS221_REG_DRDY_PP_OD_ADDR, + HTS221_REG_DRDY_PP_OD_MASK, + open_drain); + if (err < 0) + return err; + err = devm_request_threaded_irq(hw->dev, hw->irq, NULL, hts221_trigger_handler_thread, irq_type | IRQF_ONESHOT, -- cgit v1.2.3 From f3f0ae16f0ce2a11067b958c7cf2a48c32ea6d60 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 17 Jul 2017 19:39:05 +0200 Subject: iio: humidity: hts221: move drdy enable logic in hts221_trig_set_state() Move data-ready configuration in hts221_buffer.c since it is only related to trigger logic Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/hts221.h | 1 - drivers/iio/humidity/hts221_buffer.c | 8 +++++++- drivers/iio/humidity/hts221_core.c | 14 -------------- 3 files changed, 7 insertions(+), 16 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/humidity/hts221.h b/drivers/iio/humidity/hts221.h index c3de6e494f8c..51d021966222 100644 --- a/drivers/iio/humidity/hts221.h +++ b/drivers/iio/humidity/hts221.h @@ -60,7 +60,6 @@ struct hts221_hw { extern const struct dev_pm_ops hts221_pm_ops; -int hts221_config_drdy(struct hts221_hw *hw, bool enable); int hts221_write_with_mask(struct hts221_hw *hw, u8 addr, u8 mask, u8 val); int hts221_probe(struct iio_dev *iio_dev); int hts221_set_enable(struct hts221_hw *hw, bool enable); diff --git a/drivers/iio/humidity/hts221_buffer.c b/drivers/iio/humidity/hts221_buffer.c index f29f01a22375..9690dfe9a844 100644 --- a/drivers/iio/humidity/hts221_buffer.c +++ b/drivers/iio/humidity/hts221_buffer.c @@ -28,6 +28,8 @@ #define HTS221_REG_DRDY_HL_MASK BIT(7) #define HTS221_REG_DRDY_PP_OD_ADDR 0x22 #define HTS221_REG_DRDY_PP_OD_MASK BIT(6) +#define HTS221_REG_DRDY_EN_ADDR 0x22 +#define HTS221_REG_DRDY_EN_MASK BIT(2) #define HTS221_REG_STATUS_ADDR 0x27 #define HTS221_RH_DRDY_MASK BIT(1) #define HTS221_TEMP_DRDY_MASK BIT(0) @@ -36,8 +38,12 @@ static int hts221_trig_set_state(struct iio_trigger *trig, bool state) { struct iio_dev *iio_dev = iio_trigger_get_drvdata(trig); struct hts221_hw *hw = iio_priv(iio_dev); + int err; + + err = hts221_write_with_mask(hw, HTS221_REG_DRDY_EN_ADDR, + HTS221_REG_DRDY_EN_MASK, state); - return hts221_config_drdy(hw, state); + return err < 0 ? err : 0; } static const struct iio_trigger_ops hts221_trigger_ops = { diff --git a/drivers/iio/humidity/hts221_core.c b/drivers/iio/humidity/hts221_core.c index 25fcb9993998..32524a8dc66f 100644 --- a/drivers/iio/humidity/hts221_core.c +++ b/drivers/iio/humidity/hts221_core.c @@ -23,7 +23,6 @@ #define HTS221_REG_CNTRL1_ADDR 0x20 #define HTS221_REG_CNTRL2_ADDR 0x21 -#define HTS221_REG_CNTRL3_ADDR 0x22 #define HTS221_REG_AVG_ADDR 0x10 #define HTS221_REG_H_OUT_L 0x28 @@ -36,9 +35,6 @@ #define HTS221_BDU_MASK BIT(2) #define HTS221_ENABLE_MASK BIT(7) -#define HTS221_DRDY_MASK BIT(2) - - /* calibration registers */ #define HTS221_REG_0RH_CAL_X_H 0x36 #define HTS221_REG_1RH_CAL_X_H 0x3a @@ -181,16 +177,6 @@ static int hts221_check_whoami(struct hts221_hw *hw) return 0; } -int hts221_config_drdy(struct hts221_hw *hw, bool enable) -{ - int err; - - err = hts221_write_with_mask(hw, HTS221_REG_CNTRL3_ADDR, - HTS221_DRDY_MASK, enable); - - return err < 0 ? err : 0; -} - static int hts221_update_odr(struct hts221_hw *hw, u8 odr) { int i, err; -- cgit v1.2.3