From 9a0ebbc93547d88f422905c34dcceebe928f3e9e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 13 Nov 2017 11:29:55 +0100 Subject: [PATCH 01/44] iio: adc/accel: Fix up module licenses The module license checker complains about these two so just fix it up. They are both GPLv2, both written by me or using code I extracted while refactoring from the GPLv2 drivers. Cc: Randy Dunlap Reported-by: Randy Dunlap Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/accel/kxsd9-i2c.c | 3 +++ drivers/iio/adc/qcom-vadc-common.c | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/drivers/iio/accel/kxsd9-i2c.c b/drivers/iio/accel/kxsd9-i2c.c index 98fbb628d5bd..38411e1c155b 100644 --- a/drivers/iio/accel/kxsd9-i2c.c +++ b/drivers/iio/accel/kxsd9-i2c.c @@ -63,3 +63,6 @@ static struct i2c_driver kxsd9_i2c_driver = { .id_table = kxsd9_i2c_id, }; module_i2c_driver(kxsd9_i2c_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("KXSD9 accelerometer I2C interface"); diff --git a/drivers/iio/adc/qcom-vadc-common.c b/drivers/iio/adc/qcom-vadc-common.c index 47d24ae5462f..fe3d7826783c 100644 --- a/drivers/iio/adc/qcom-vadc-common.c +++ b/drivers/iio/adc/qcom-vadc-common.c @@ -5,6 +5,7 @@ #include #include #include +#include #include "qcom-vadc-common.h" @@ -229,3 +230,6 @@ int qcom_vadc_decimation_from_dt(u32 value) return __ffs64(value / VADC_DECIMATION_MIN); } EXPORT_SYMBOL(qcom_vadc_decimation_from_dt); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Qualcomm ADC common functionality"); From 16ba0dc6eaf4d152126a855bc5c9a31f05abadb4 Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Wed, 15 Nov 2017 14:56:45 +0200 Subject: [PATCH 02/44] dt-bindings: iio: at91-sama5d2_adc: add optional dma property Added property for DMA configuration of the device. Signed-off-by: Eugen Hristev Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/at91-sama5d2_adc.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt index 552e7a83951d..6469a4cd2a6d 100644 --- a/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt +++ b/Documentation/devicetree/bindings/iio/adc/at91-sama5d2_adc.txt @@ -17,6 +17,11 @@ Required properties: This property uses the IRQ edge types values: IRQ_TYPE_EDGE_RISING , IRQ_TYPE_EDGE_FALLING or IRQ_TYPE_EDGE_BOTH +Optional properties: + - dmas: Phandle to dma channel for the ADC. + - dma-names: Must be "rx" when dmas property is being used. + See ../../dma/dma.txt for details. + Example: adc: adc@fc030000 { @@ -31,4 +36,6 @@ adc: adc@fc030000 { vddana-supply = <&vdd_3v3_lp_reg>; vref-supply = <&vdd_3v3_lp_reg>; atmel,trigger-edge-type = ; + dmas = <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(25))>; + dma-names = "rx"; } From 073c662017f2f3ea693bb31e4f5d2332265f1de0 Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Wed, 15 Nov 2017 14:56:47 +0200 Subject: [PATCH 03/44] iio: adc: at91-sama5d2_adc: add support for DMA Added support for DMA transfers. The implementation uses the user watermark to decide whether DMA will be used or not. For watermark 1, DMA will not be used. If watermark is bigger, DMA will be used. Sysfs attributes are created to indicate whether the DMA is used, with hwfifo_enabled, and the current DMA watermark is readable in hwfifo_watermark. Minimum and maximum values are in hwfifo_watermark_min and hwfifo_watermark_max. Signed-off-by: Eugen Hristev Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 1 + drivers/iio/adc/at91-sama5d2_adc.c | 453 +++++++++++++++++++++++++++-- 2 files changed, 434 insertions(+), 20 deletions(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index ef86296b8b0d..486793691de0 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -158,6 +158,7 @@ config AT91_SAMA5D2_ADC tristate "Atmel AT91 SAMA5D2 ADC" depends on ARCH_AT91 || COMPILE_TEST depends on HAS_IOMEM + depends on HAS_DMA select IIO_TRIGGERED_BUFFER help Say yes here to build support for Atmel SAMA5D2 ADC which is diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 755a493c2a2c..99f3c2c89768 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -16,6 +16,8 @@ #include #include +#include +#include #include #include #include @@ -100,6 +102,8 @@ #define AT91_SAMA5D2_LCDR 0x20 /* Interrupt Enable Register */ #define AT91_SAMA5D2_IER 0x24 +/* Interrupt Enable Register - general overrun error */ +#define AT91_SAMA5D2_IER_GOVRE BIT(25) /* Interrupt Disable Register */ #define AT91_SAMA5D2_IDR 0x28 /* Interrupt Mask Register */ @@ -167,13 +171,19 @@ /* * Maximum number of bytes to hold conversion from all channels - * plus the timestamp + * without the timestamp. */ -#define AT91_BUFFER_MAX_BYTES ((AT91_SAMA5D2_SINGLE_CHAN_CNT + \ - AT91_SAMA5D2_DIFF_CHAN_CNT) * 2 + 8) +#define AT91_BUFFER_MAX_CONVERSION_BYTES ((AT91_SAMA5D2_SINGLE_CHAN_CNT + \ + AT91_SAMA5D2_DIFF_CHAN_CNT) * 2) + +/* This total must also include the timestamp */ +#define AT91_BUFFER_MAX_BYTES (AT91_BUFFER_MAX_CONVERSION_BYTES + 8) #define AT91_BUFFER_MAX_HWORDS (AT91_BUFFER_MAX_BYTES / 2) +#define AT91_HWFIFO_MAX_SIZE_STR "128" +#define AT91_HWFIFO_MAX_SIZE 128 + #define AT91_SAMA5D2_CHAN_SINGLE(num, addr) \ { \ .type = IIO_VOLTAGE, \ @@ -228,6 +238,28 @@ struct at91_adc_trigger { bool hw_trig; }; +/** + * at91_adc_dma - at91-sama5d2 dma information struct + * @dma_chan: the dma channel acquired + * @rx_buf: dma coherent allocated area + * @rx_dma_buf: dma handler for the buffer + * @phys_addr: physical address of the ADC base register + * @buf_idx: index inside the dma buffer where reading was last done + * @rx_buf_sz: size of buffer used by DMA operation + * @watermark: number of conversions to copy before DMA triggers irq + * @dma_ts: hold the start timestamp of dma operation + */ +struct at91_adc_dma { + struct dma_chan *dma_chan; + u8 *rx_buf; + dma_addr_t rx_dma_buf; + phys_addr_t phys_addr; + int buf_idx; + int rx_buf_sz; + int watermark; + s64 dma_ts; +}; + struct at91_adc_state { void __iomem *base; int irq; @@ -242,6 +274,7 @@ struct at91_adc_state { u32 conversion_value; struct at91_adc_soc_info soc_info; wait_queue_head_t wq_data_available; + struct at91_adc_dma dma_st; u16 buffer[AT91_BUFFER_MAX_HWORDS]; /* * lock to prevent concurrent 'single conversion' requests through @@ -322,11 +355,17 @@ static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state) if (state) { at91_adc_writel(st, AT91_SAMA5D2_CHER, BIT(chan->channel)); - at91_adc_writel(st, AT91_SAMA5D2_IER, - BIT(chan->channel)); + /* enable irq only if not using DMA */ + if (!st->dma_st.dma_chan) { + at91_adc_writel(st, AT91_SAMA5D2_IER, + BIT(chan->channel)); + } } else { - at91_adc_writel(st, AT91_SAMA5D2_IDR, - BIT(chan->channel)); + /* disable irq only if not using DMA */ + if (!st->dma_st.dma_chan) { + at91_adc_writel(st, AT91_SAMA5D2_IDR, + BIT(chan->channel)); + } at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel)); } @@ -340,6 +379,10 @@ 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); + /* if we are using DMA, we must not reenable irq after each trigger */ + if (st->dma_st.dma_chan) + return 0; + enable_irq(st->irq); /* Needed to ACK the DRDY interruption */ @@ -350,6 +393,153 @@ static int at91_adc_reenable_trigger(struct iio_trigger *trig) static const struct iio_trigger_ops at91_adc_trigger_ops = { .set_trigger_state = &at91_adc_configure_trigger, .try_reenable = &at91_adc_reenable_trigger, + .validate_device = iio_trigger_validate_own_device, +}; + +static int at91_adc_dma_size_done(struct at91_adc_state *st) +{ + struct dma_tx_state state; + enum dma_status status; + int i, size; + + status = dmaengine_tx_status(st->dma_st.dma_chan, + st->dma_st.dma_chan->cookie, + &state); + if (status != DMA_IN_PROGRESS) + return 0; + + /* Transferred length is size in bytes from end of buffer */ + i = st->dma_st.rx_buf_sz - state.residue; + + /* Return available bytes */ + if (i >= st->dma_st.buf_idx) + size = i - st->dma_st.buf_idx; + else + size = st->dma_st.rx_buf_sz + i - st->dma_st.buf_idx; + return size; +} + +static void at91_dma_buffer_done(void *data) +{ + struct iio_dev *indio_dev = data; + + iio_trigger_poll_chained(indio_dev->trig); +} + +static int at91_adc_dma_start(struct iio_dev *indio_dev) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + struct dma_async_tx_descriptor *desc; + dma_cookie_t cookie; + int ret; + u8 bit; + + if (!st->dma_st.dma_chan) + return 0; + + /* we start a new DMA, so set buffer index to start */ + st->dma_st.buf_idx = 0; + + /* + * compute buffer size w.r.t. watermark and enabled channels. + * scan_bytes is aligned so we need an exact size for DMA + */ + st->dma_st.rx_buf_sz = 0; + + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->num_channels) { + struct iio_chan_spec const *chan = indio_dev->channels + bit; + + st->dma_st.rx_buf_sz += chan->scan_type.storagebits / 8; + } + st->dma_st.rx_buf_sz *= st->dma_st.watermark; + + /* Prepare a DMA cyclic transaction */ + desc = dmaengine_prep_dma_cyclic(st->dma_st.dma_chan, + st->dma_st.rx_dma_buf, + st->dma_st.rx_buf_sz, + st->dma_st.rx_buf_sz / 2, + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); + + if (!desc) { + dev_err(&indio_dev->dev, "cannot prepare DMA cyclic\n"); + return -EBUSY; + } + + desc->callback = at91_dma_buffer_done; + desc->callback_param = indio_dev; + + cookie = dmaengine_submit(desc); + ret = dma_submit_error(cookie); + if (ret) { + dev_err(&indio_dev->dev, "cannot submit DMA cyclic\n"); + dmaengine_terminate_async(st->dma_st.dma_chan); + return ret; + } + + /* enable general overrun error signaling */ + at91_adc_writel(st, AT91_SAMA5D2_IER, AT91_SAMA5D2_IER_GOVRE); + /* Issue pending DMA requests */ + dma_async_issue_pending(st->dma_st.dma_chan); + + /* consider current time as DMA start time for timestamps */ + st->dma_st.dma_ts = iio_get_time_ns(indio_dev); + + dev_dbg(&indio_dev->dev, "DMA cyclic started\n"); + + return 0; +} + +static int at91_adc_buffer_postenable(struct iio_dev *indio_dev) +{ + int ret; + + ret = at91_adc_dma_start(indio_dev); + if (ret) { + dev_err(&indio_dev->dev, "buffer postenable failed\n"); + return ret; + } + + return iio_triggered_buffer_postenable(indio_dev); +} + +static int at91_adc_buffer_predisable(struct iio_dev *indio_dev) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + int ret; + u8 bit; + + ret = iio_triggered_buffer_predisable(indio_dev); + if (ret < 0) + dev_err(&indio_dev->dev, "buffer predisable failed\n"); + + if (!st->dma_st.dma_chan) + return ret; + + /* if we are using DMA we must clear registers and end DMA */ + dmaengine_terminate_sync(st->dma_st.dma_chan); + + /* + * For each enabled channel we must read the last converted value + * to clear EOC status and not get a possible interrupt later. + * This value is being read by DMA from LCDR anyway + */ + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->num_channels) { + struct iio_chan_spec const *chan = indio_dev->channels + bit; + + if (st->dma_st.dma_chan) + at91_adc_readl(st, chan->address); + } + + /* read overflow register to clear possible overflow status */ + at91_adc_readl(st, AT91_SAMA5D2_OVER); + return ret; +} + +static const struct iio_buffer_setup_ops at91_buffer_setup_ops = { + .postenable = &at91_adc_buffer_postenable, + .predisable = &at91_adc_buffer_predisable, }; static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *indio, @@ -388,24 +578,77 @@ static int at91_adc_trigger_init(struct iio_dev *indio) return 0; } -static irqreturn_t at91_adc_trigger_handler(int irq, void *p) +static void at91_adc_trigger_handler_nodma(struct iio_dev *indio_dev, + struct iio_poll_func *pf) { - struct iio_poll_func *pf = p; - struct iio_dev *indio = pf->indio_dev; - struct at91_adc_state *st = iio_priv(indio); + struct at91_adc_state *st = iio_priv(indio_dev); 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; + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->num_channels) { + struct iio_chan_spec const *chan = indio_dev->channels + bit; st->buffer[i] = at91_adc_readl(st, chan->address); i++; } + iio_push_to_buffers_with_timestamp(indio_dev, st->buffer, + pf->timestamp); +} - iio_push_to_buffers_with_timestamp(indio, st->buffer, pf->timestamp); +static void at91_adc_trigger_handler_dma(struct iio_dev *indio_dev) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + int transferred_len = at91_adc_dma_size_done(st); + s64 ns = iio_get_time_ns(indio_dev); + s64 interval; + int sample_index = 0, sample_count, sample_size; - iio_trigger_notify_done(indio->trig); + u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR); + /* if we reached this point, we cannot sample faster */ + if (status & AT91_SAMA5D2_IER_GOVRE) + pr_info_ratelimited("%s: conversion overrun detected\n", + indio_dev->name); + + sample_size = div_s64(st->dma_st.rx_buf_sz, st->dma_st.watermark); + + sample_count = div_s64(transferred_len, sample_size); + + /* + * interval between samples is total time since last transfer handling + * divided by the number of samples (total size divided by sample size) + */ + interval = div_s64((ns - st->dma_st.dma_ts), sample_count); + + while (transferred_len >= sample_size) { + iio_push_to_buffers_with_timestamp(indio_dev, + (st->dma_st.rx_buf + st->dma_st.buf_idx), + (st->dma_st.dma_ts + interval * sample_index)); + /* adjust remaining length */ + transferred_len -= sample_size; + /* adjust buffer index */ + st->dma_st.buf_idx += sample_size; + /* in case of reaching end of buffer, reset index */ + if (st->dma_st.buf_idx >= st->dma_st.rx_buf_sz) + st->dma_st.buf_idx = 0; + sample_index++; + } + /* adjust saved time for next transfer handling */ + st->dma_st.dma_ts = iio_get_time_ns(indio_dev); +} + +static irqreturn_t at91_adc_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct at91_adc_state *st = iio_priv(indio_dev); + + if (st->dma_st.dma_chan) + at91_adc_trigger_handler_dma(indio_dev); + else + at91_adc_trigger_handler_nodma(indio_dev, pf); + + iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; } @@ -414,7 +657,7 @@ 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); + &at91_adc_trigger_handler, &at91_buffer_setup_ops); } static unsigned at91_adc_startup_time(unsigned startup_time_min, @@ -485,10 +728,13 @@ static irqreturn_t at91_adc_interrupt(int irq, void *private) if (!(status & imr)) return IRQ_NONE; - if (iio_buffer_enabled(indio)) { + if (iio_buffer_enabled(indio) && !st->dma_st.dma_chan) { disable_irq_nosync(irq); iio_trigger_poll(indio->trig); - } else { + } else if (iio_buffer_enabled(indio) && st->dma_st.dma_chan) { + disable_irq_nosync(irq); + WARN(true, "Unexpected irq occurred\n"); + } else if (!iio_buffer_enabled(indio)) { st->conversion_value = at91_adc_readl(st, st->chan->address); st->conversion_done = true; wake_up_interruptible(&st->wq_data_available); @@ -510,7 +756,6 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev, ret = iio_device_claim_direct_mode(indio_dev); if (ret) return ret; - mutex_lock(&st->lock); st->chan = chan; @@ -580,9 +825,123 @@ static int at91_adc_write_raw(struct iio_dev *indio_dev, return 0; } +static void at91_adc_dma_init(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct at91_adc_state *st = iio_priv(indio_dev); + struct dma_slave_config config = {0}; + /* + * We make the buffer double the size of the fifo, + * such that DMA uses one half of the buffer (full fifo size) + * and the software uses the other half to read/write. + */ + unsigned int pages = DIV_ROUND_UP(AT91_HWFIFO_MAX_SIZE * + AT91_BUFFER_MAX_CONVERSION_BYTES * 2, + PAGE_SIZE); + + if (st->dma_st.dma_chan) + return; + + st->dma_st.dma_chan = dma_request_slave_channel(&pdev->dev, "rx"); + + if (!st->dma_st.dma_chan) { + dev_info(&pdev->dev, "can't get DMA channel\n"); + goto dma_exit; + } + + st->dma_st.rx_buf = dma_alloc_coherent(st->dma_st.dma_chan->device->dev, + pages * PAGE_SIZE, + &st->dma_st.rx_dma_buf, + GFP_KERNEL); + if (!st->dma_st.rx_buf) { + dev_info(&pdev->dev, "can't allocate coherent DMA area\n"); + goto dma_chan_disable; + } + + /* Configure DMA channel to read data register */ + config.direction = DMA_DEV_TO_MEM; + config.src_addr = (phys_addr_t)(st->dma_st.phys_addr + + AT91_SAMA5D2_LCDR); + config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + config.src_maxburst = 1; + config.dst_maxburst = 1; + + if (dmaengine_slave_config(st->dma_st.dma_chan, &config)) { + dev_info(&pdev->dev, "can't configure DMA slave\n"); + goto dma_free_area; + } + + dev_info(&pdev->dev, "using %s for rx DMA transfers\n", + dma_chan_name(st->dma_st.dma_chan)); + + return; + +dma_free_area: + dma_free_coherent(st->dma_st.dma_chan->device->dev, pages * PAGE_SIZE, + st->dma_st.rx_buf, st->dma_st.rx_dma_buf); +dma_chan_disable: + dma_release_channel(st->dma_st.dma_chan); + st->dma_st.dma_chan = 0; +dma_exit: + dev_info(&pdev->dev, "continuing without DMA support\n"); +} + +static void at91_adc_dma_disable(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct at91_adc_state *st = iio_priv(indio_dev); + unsigned int pages = DIV_ROUND_UP(AT91_HWFIFO_MAX_SIZE * + AT91_BUFFER_MAX_CONVERSION_BYTES * 2, + PAGE_SIZE); + + /* if we are not using DMA, just return */ + if (!st->dma_st.dma_chan) + return; + + /* wait for all transactions to be terminated first*/ + dmaengine_terminate_sync(st->dma_st.dma_chan); + + dma_free_coherent(st->dma_st.dma_chan->device->dev, pages * PAGE_SIZE, + st->dma_st.rx_buf, st->dma_st.rx_dma_buf); + dma_release_channel(st->dma_st.dma_chan); + st->dma_st.dma_chan = 0; + + dev_info(&pdev->dev, "continuing without DMA support\n"); +} + +static int at91_adc_set_watermark(struct iio_dev *indio_dev, unsigned int val) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + + if (val > AT91_HWFIFO_MAX_SIZE) + return -EINVAL; + + if (!st->selected_trig->hw_trig) { + dev_dbg(&indio_dev->dev, "we need hw trigger for DMA\n"); + return 0; + } + + dev_dbg(&indio_dev->dev, "new watermark is %u\n", val); + st->dma_st.watermark = val; + + /* + * The logic here is: if we have watermark 1, it means we do + * each conversion with it's own IRQ, thus we don't need DMA. + * If the watermark is higher, we do DMA to do all the transfers in bulk + */ + + if (val == 1) + at91_adc_dma_disable(to_platform_device(&indio_dev->dev)); + else if (val > 1) + at91_adc_dma_init(to_platform_device(&indio_dev->dev)); + + return 0; +} + static const struct iio_info at91_adc_info = { .read_raw = &at91_adc_read_raw, .write_raw = &at91_adc_write_raw, + .hwfifo_set_watermark = &at91_adc_set_watermark, }; static void at91_adc_hw_init(struct at91_adc_state *st) @@ -599,6 +958,42 @@ static void at91_adc_hw_init(struct at91_adc_state *st) at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate); } +static ssize_t at91_adc_get_fifo_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = + platform_get_drvdata(to_platform_device(dev)); + struct at91_adc_state *st = iio_priv(indio_dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", !!st->dma_st.dma_chan); +} + +static ssize_t at91_adc_get_watermark(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = + platform_get_drvdata(to_platform_device(dev)); + struct at91_adc_state *st = iio_priv(indio_dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", st->dma_st.watermark); +} + +static IIO_DEVICE_ATTR(hwfifo_enabled, 0444, + at91_adc_get_fifo_state, NULL, 0); +static IIO_DEVICE_ATTR(hwfifo_watermark, 0444, + at91_adc_get_watermark, NULL, 0); + +static IIO_CONST_ATTR(hwfifo_watermark_min, "2"); +static IIO_CONST_ATTR(hwfifo_watermark_max, AT91_HWFIFO_MAX_SIZE_STR); + +static const struct attribute *at91_adc_fifo_attributes[] = { + &iio_const_attr_hwfifo_watermark_min.dev_attr.attr, + &iio_const_attr_hwfifo_watermark_max.dev_attr.attr, + &iio_dev_attr_hwfifo_watermark.dev_attr.attr, + &iio_dev_attr_hwfifo_enabled.dev_attr.attr, + NULL, +}; + static int at91_adc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; @@ -674,6 +1069,9 @@ static int at91_adc_probe(struct platform_device *pdev) if (!res) return -EINVAL; + /* if we plan to use DMA, we need the physical address of the regs */ + st->dma_st.phys_addr = res->start; + st->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(st->base)) return PTR_ERR(st->base); @@ -737,11 +1135,22 @@ static int at91_adc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "couldn't setup the triggers.\n"); goto per_clk_disable_unprepare; } + /* + * Initially the iio buffer has a length of 2 and + * a watermark of 1 + */ + st->dma_st.watermark = 1; + + iio_buffer_set_attrs(indio_dev->buffer, + at91_adc_fifo_attributes); } + if (dma_coerce_mask_and_coherent(&indio_dev->dev, DMA_BIT_MASK(32))) + dev_info(&pdev->dev, "cannot set DMA mask to 32-bit\n"); + ret = iio_device_register(indio_dev); if (ret < 0) - goto per_clk_disable_unprepare; + goto dma_disable; if (st->selected_trig->hw_trig) dev_info(&pdev->dev, "setting up trigger as %s\n", @@ -752,6 +1161,8 @@ static int at91_adc_probe(struct platform_device *pdev) return 0; +dma_disable: + at91_adc_dma_disable(pdev); per_clk_disable_unprepare: clk_disable_unprepare(st->per_clk); vref_disable: @@ -768,6 +1179,8 @@ static int at91_adc_remove(struct platform_device *pdev) iio_device_unregister(indio_dev); + at91_adc_dma_disable(pdev); + clk_disable_unprepare(st->per_clk); regulator_disable(st->vref); From 10c7a314c9fde2342407c1bd2d71d0c0f7ff9662 Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Wed, 15 Nov 2017 14:56:48 +0200 Subject: [PATCH 04/44] iio: adc: at91-sama5d2_adc: ack DRDY irq in direct mode Need to acknowledge DRDY irq in direct mode/ software triggered mode. Otherwise, on the next conversion, overrun flag will be raised, which is not a correct state. This doesn't affect the functionality, but will generate possible incorrect overrun reports. Signed-off-by: Eugen Hristev Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 99f3c2c89768..4eff8351ce29 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -786,6 +786,9 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev, at91_adc_writel(st, AT91_SAMA5D2_IDR, BIT(chan->channel)); at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel)); + /* Needed to ACK the DRDY interruption */ + at91_adc_readl(st, AT91_SAMA5D2_LCDR); + mutex_unlock(&st->lock); iio_device_release_direct_mode(indio_dev); From 5f045af0b5dcdeba2776f0123bb33aba57f7b265 Mon Sep 17 00:00:00 2001 From: Paolo Cretaro Date: Sun, 19 Nov 2017 01:57:45 +0100 Subject: [PATCH 05/44] iio: cros_ec: Remove unused variables Fix gcc warnings about variable 'ec_device' being set but not used in these files: common/cros_ec_sensors/cros_ec_sensors.c:194:25 light/cros_ec_light_prox.c:184:25 Signed-off-by: Paolo Cretaro Signed-off-by: Jonathan Cameron --- drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c | 2 -- drivers/iio/light/cros_ec_light_prox.c | 2 -- 2 files changed, 4 deletions(-) diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c index ed8063f2da99..7d30c59da3e2 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c @@ -191,7 +191,6 @@ static int cros_ec_sensors_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent); - struct cros_ec_device *ec_device; struct iio_dev *indio_dev; struct cros_ec_sensors_state *state; struct iio_chan_spec *channel; @@ -201,7 +200,6 @@ static int cros_ec_sensors_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "No CROS EC device found.\n"); return -EINVAL; } - ec_device = ec_dev->ec_dev; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state)); if (!indio_dev) diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c index b2a46b390d5c..acfad4aeb27a 100644 --- a/drivers/iio/light/cros_ec_light_prox.c +++ b/drivers/iio/light/cros_ec_light_prox.c @@ -181,7 +181,6 @@ static int cros_ec_light_prox_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent); - struct cros_ec_device *ec_device; struct iio_dev *indio_dev; struct cros_ec_light_prox_state *state; struct iio_chan_spec *channel; @@ -191,7 +190,6 @@ static int cros_ec_light_prox_probe(struct platform_device *pdev) dev_warn(dev, "No CROS EC device found.\n"); return -EINVAL; } - ec_device = ec_dev->ec_dev; indio_dev = devm_iio_device_alloc(dev, sizeof(*state)); if (!indio_dev) From 9d793c1a7f25d75d5947022368dfcfa9f75d4412 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 8 Nov 2017 12:08:26 -0600 Subject: [PATCH 06/44] iio: core: Mark expected switch fall-through In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Addresses-Coverity-ID: 1397962 Signed-off-by: Gustavo A. R. Silva Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 9c4cfd19b739..2e8e36f9da61 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -588,6 +588,7 @@ static ssize_t __iio_format_value(char *buf, size_t len, unsigned int type, return snprintf(buf, len, "%d", vals[0]); case IIO_VAL_INT_PLUS_MICRO_DB: scale_db = true; + /* fall through */ case IIO_VAL_INT_PLUS_MICRO: if (vals[1] < 0) return snprintf(buf, len, "-%d.%06u%s", abs(vals[0]), From 8556f5149819239a9dbcbc8d61b496a51f0c66a3 Mon Sep 17 00:00:00 2001 From: Peter Meerwald-Stadler Date: Mon, 6 Nov 2017 00:24:59 +0100 Subject: [PATCH 07/44] iio: light: Add driver for IDT ZOPT2201 ambient light and UVB sensor Driver for 20-bit ALS and UV B sensor with I2C interface exposing the following API: in_uvindex_input in_illuminance_raw in_illuminance_scale in_illuminance_scale_available in_intensity_uv_raw in_intensity_uv_scale in_intensity_uv_scale_available integration_time integration_time_available Signed-off-by: Peter Meerwald-Stadler Signed-off-by: Jonathan Cameron --- drivers/iio/light/Kconfig | 10 + drivers/iio/light/Makefile | 1 + drivers/iio/light/zopt2201.c | 568 +++++++++++++++++++++++++++++++++++ 3 files changed, 579 insertions(+) create mode 100644 drivers/iio/light/zopt2201.c diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 2356ed9285df..6a5835fab32e 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -425,4 +425,14 @@ config VL6180 To compile this driver as a module, choose M here: the module will be called vl6180. +config ZOPT2201 + tristate "ZOPT2201 ALS and UV B sensor" + depends on I2C + help + Say Y here if you want to build a driver for the IDT + ZOPT2201 ambient light and UV B sensor. + + To compile this driver as a module, choose M here: the + module will be called zopt2201. + endmenu diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index c263469b7ce9..f0176a800e14 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -41,3 +41,4 @@ obj-$(CONFIG_US5182D) += us5182d.o obj-$(CONFIG_VCNL4000) += vcnl4000.o obj-$(CONFIG_VEML6070) += veml6070.o obj-$(CONFIG_VL6180) += vl6180.o +obj-$(CONFIG_ZOPT2201) += zopt2201.o diff --git a/drivers/iio/light/zopt2201.c b/drivers/iio/light/zopt2201.c new file mode 100644 index 000000000000..041ac9effdb0 --- /dev/null +++ b/drivers/iio/light/zopt2201.c @@ -0,0 +1,568 @@ +/* + * zopt2201.c - Support for IDT ZOPT2201 ambient light and UV B sensor + * + * Copyright 2017 Peter Meerwald-Stadler + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * Datasheet: https://www.idt.com/document/dst/zopt2201-datasheet + * 7-bit I2C slave addresses 0x53 (default) or 0x52 (programmed) + * + * TODO: interrupt support, ALS/UVB raw mode + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define ZOPT2201_DRV_NAME "zopt2201" + +/* Registers */ +#define ZOPT2201_MAIN_CTRL 0x00 +#define ZOPT2201_LS_MEAS_RATE 0x04 +#define ZOPT2201_LS_GAIN 0x05 +#define ZOPT2201_PART_ID 0x06 +#define ZOPT2201_MAIN_STATUS 0x07 +#define ZOPT2201_ALS_DATA 0x0d /* LSB first, 13 to 20 bits */ +#define ZOPT2201_UVB_DATA 0x10 /* LSB first, 13 to 20 bits */ +#define ZOPT2201_UV_COMP_DATA 0x13 /* LSB first, 13 to 20 bits */ +#define ZOPT2201_COMP_DATA 0x16 /* LSB first, 13 to 20 bits */ +#define ZOPT2201_INT_CFG 0x19 +#define ZOPT2201_INT_PST 0x1a + +#define ZOPT2201_MAIN_CTRL_LS_MODE BIT(3) /* 0 .. ALS, 1 .. UV B */ +#define ZOPT2201_MAIN_CTRL_LS_EN BIT(1) + +/* Values for ZOPT2201_LS_MEAS_RATE resolution / bit width */ +#define ZOPT2201_MEAS_RES_20BIT 0 /* takes 400 ms */ +#define ZOPT2201_MEAS_RES_19BIT 1 /* takes 200 ms */ +#define ZOPT2201_MEAS_RES_18BIT 2 /* takes 100 ms, default */ +#define ZOPT2201_MEAS_RES_17BIT 3 /* takes 50 ms */ +#define ZOPT2201_MEAS_RES_16BIT 4 /* takes 25 ms */ +#define ZOPT2201_MEAS_RES_13BIT 5 /* takes 3.125 ms */ +#define ZOPT2201_MEAS_RES_SHIFT 4 + +/* Values for ZOPT2201_LS_MEAS_RATE measurement rate */ +#define ZOPT2201_MEAS_FREQ_25MS 0 +#define ZOPT2201_MEAS_FREQ_50MS 1 +#define ZOPT2201_MEAS_FREQ_100MS 2 /* default */ +#define ZOPT2201_MEAS_FREQ_200MS 3 +#define ZOPT2201_MEAS_FREQ_500MS 4 +#define ZOPT2201_MEAS_FREQ_1000MS 5 +#define ZOPT2201_MEAS_FREQ_2000MS 6 + +/* Values for ZOPT2201_LS_GAIN */ +#define ZOPT2201_LS_GAIN_1 0 +#define ZOPT2201_LS_GAIN_3 1 +#define ZOPT2201_LS_GAIN_6 2 +#define ZOPT2201_LS_GAIN_9 3 +#define ZOPT2201_LS_GAIN_18 4 + +/* Values for ZOPT2201_MAIN_STATUS */ +#define ZOPT2201_MAIN_STATUS_POWERON BIT(5) +#define ZOPT2201_MAIN_STATUS_INT BIT(4) +#define ZOPT2201_MAIN_STATUS_DRDY BIT(3) + +#define ZOPT2201_PART_NUMBER 0xb2 + +struct zopt2201_data { + struct i2c_client *client; + struct mutex lock; + u8 gain; + u8 res; + u8 rate; +}; + +static const struct { + unsigned int gain; /* gain factor */ + unsigned int scale; /* micro lux per count */ +} zopt2201_gain_als[] = { + { 1, 19200000 }, + { 3, 6400000 }, + { 6, 3200000 }, + { 9, 2133333 }, + { 18, 1066666 }, +}; + +static const struct { + unsigned int gain; /* gain factor */ + unsigned int scale; /* micro W/m2 per count */ +} zopt2201_gain_uvb[] = { + { 1, 460800 }, + { 3, 153600 }, + { 6, 76800 }, + { 9, 51200 }, + { 18, 25600 }, +}; + +static const struct { + unsigned int bits; /* sensor resolution in bits */ + unsigned long us; /* measurement time in micro seconds */ +} zopt2201_resolution[] = { + { 20, 400000 }, + { 19, 200000 }, + { 18, 100000 }, + { 17, 50000 }, + { 16, 25000 }, + { 13, 3125 }, +}; + +static const struct { + unsigned int scale, uscale; /* scale factor as integer + micro */ + u8 gain; /* gain register value */ + u8 res; /* resolution register value */ +} zopt2201_scale_als[] = { + { 19, 200000, 0, 5 }, + { 6, 400000, 1, 5 }, + { 3, 200000, 2, 5 }, + { 2, 400000, 0, 4 }, + { 2, 133333, 3, 5 }, + { 1, 200000, 0, 3 }, + { 1, 66666, 4, 5 }, + { 0, 800000, 1, 4 }, + { 0, 600000, 0, 2 }, + { 0, 400000, 2, 4 }, + { 0, 300000, 0, 1 }, + { 0, 266666, 3, 4 }, + { 0, 200000, 2, 3 }, + { 0, 150000, 0, 0 }, + { 0, 133333, 4, 4 }, + { 0, 100000, 2, 2 }, + { 0, 66666, 4, 3 }, + { 0, 50000, 2, 1 }, + { 0, 33333, 4, 2 }, + { 0, 25000, 2, 0 }, + { 0, 16666, 4, 1 }, + { 0, 8333, 4, 0 }, +}; + +static const struct { + unsigned int scale, uscale; /* scale factor as integer + micro */ + u8 gain; /* gain register value */ + u8 res; /* resolution register value */ +} zopt2201_scale_uvb[] = { + { 0, 460800, 0, 5 }, + { 0, 153600, 1, 5 }, + { 0, 76800, 2, 5 }, + { 0, 57600, 0, 4 }, + { 0, 51200, 3, 5 }, + { 0, 28800, 0, 3 }, + { 0, 25600, 4, 5 }, + { 0, 19200, 1, 4 }, + { 0, 14400, 0, 2 }, + { 0, 9600, 2, 4 }, + { 0, 7200, 0, 1 }, + { 0, 6400, 3, 4 }, + { 0, 4800, 2, 3 }, + { 0, 3600, 0, 0 }, + { 0, 3200, 4, 4 }, + { 0, 2400, 2, 2 }, + { 0, 1600, 4, 3 }, + { 0, 1200, 2, 1 }, + { 0, 800, 4, 2 }, + { 0, 600, 2, 0 }, + { 0, 400, 4, 1 }, + { 0, 200, 4, 0 }, +}; + +static int zopt2201_enable_mode(struct zopt2201_data *data, bool uvb_mode) +{ + u8 out = ZOPT2201_MAIN_CTRL_LS_EN; + + if (uvb_mode) + out |= ZOPT2201_MAIN_CTRL_LS_MODE; + + return i2c_smbus_write_byte_data(data->client, ZOPT2201_MAIN_CTRL, out); +} + +static int zopt2201_read(struct zopt2201_data *data, u8 reg) +{ + struct i2c_client *client = data->client; + int tries = 10; + u8 buf[3]; + int ret; + + mutex_lock(&data->lock); + ret = zopt2201_enable_mode(data, reg == ZOPT2201_UVB_DATA); + if (ret < 0) + goto fail; + + while (tries--) { + unsigned long t = zopt2201_resolution[data->res].us; + + if (t <= 20000) + usleep_range(t, t + 1000); + else + msleep(t / 1000); + ret = i2c_smbus_read_byte_data(client, ZOPT2201_MAIN_STATUS); + if (ret < 0) + goto fail; + if (ret & ZOPT2201_MAIN_STATUS_DRDY) + break; + } + + if (tries < 0) { + ret = -ETIMEDOUT; + goto fail; + } + + ret = i2c_smbus_read_i2c_block_data(client, reg, sizeof(buf), buf); + if (ret < 0) + goto fail; + + ret = i2c_smbus_write_byte_data(client, ZOPT2201_MAIN_CTRL, 0x00); + if (ret < 0) + goto fail; + mutex_unlock(&data->lock); + + return (buf[2] << 16) | (buf[1] << 8) | buf[0]; + +fail: + mutex_unlock(&data->lock); + return ret; +} + +static const struct iio_chan_spec zopt2201_channels[] = { + { + .type = IIO_LIGHT, + .address = ZOPT2201_ALS_DATA, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), + }, + { + .type = IIO_INTENSITY, + .modified = 1, + .channel2 = IIO_MOD_LIGHT_UV, + .address = ZOPT2201_UVB_DATA, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), + }, + { + .type = IIO_UVINDEX, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, +}; + +static int zopt2201_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct zopt2201_data *data = iio_priv(indio_dev); + u64 tmp; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = zopt2201_read(data, chan->address); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_PROCESSED: + ret = zopt2201_read(data, ZOPT2201_UVB_DATA); + if (ret < 0) + return ret; + *val = ret * 18 * + (1 << (20 - zopt2201_resolution[data->res].bits)) / + zopt2201_gain_uvb[data->gain].gain; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->address) { + case ZOPT2201_ALS_DATA: + *val = zopt2201_gain_als[data->gain].scale; + break; + case ZOPT2201_UVB_DATA: + *val = zopt2201_gain_uvb[data->gain].scale; + break; + default: + return -EINVAL; + } + + *val2 = 1000000; + *val2 *= (1 << (zopt2201_resolution[data->res].bits - 13)); + tmp = div_s64(*val * 1000000ULL, *val2); + *val = div_s64_rem(tmp, 1000000, val2); + + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_INT_TIME: + *val = 0; + *val2 = zopt2201_resolution[data->res].us; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int zopt2201_set_resolution(struct zopt2201_data *data, u8 res) +{ + int ret; + + ret = i2c_smbus_write_byte_data(data->client, ZOPT2201_LS_MEAS_RATE, + (res << ZOPT2201_MEAS_RES_SHIFT) | + data->rate); + if (ret < 0) + return ret; + + data->res = res; + + return 0; +} + +static int zopt2201_write_resolution(struct zopt2201_data *data, + int val, int val2) +{ + int i, ret; + + if (val != 0) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(zopt2201_resolution); i++) + if (val2 == zopt2201_resolution[i].us) { + mutex_lock(&data->lock); + ret = zopt2201_set_resolution(data, i); + mutex_unlock(&data->lock); + return ret; + } + + return -EINVAL; +} + +static int zopt2201_set_gain(struct zopt2201_data *data, u8 gain) +{ + int ret; + + ret = i2c_smbus_write_byte_data(data->client, ZOPT2201_LS_GAIN, gain); + if (ret < 0) + return ret; + + data->gain = gain; + + return 0; +} + +static int zopt2201_write_scale_als_by_idx(struct zopt2201_data *data, int idx) +{ + int ret; + + mutex_lock(&data->lock); + ret = zopt2201_set_resolution(data, zopt2201_scale_als[idx].res); + if (ret < 0) + goto unlock; + + ret = zopt2201_set_gain(data, zopt2201_scale_als[idx].gain); + +unlock: + mutex_unlock(&data->lock); + return ret; +} + +static int zopt2201_write_scale_als(struct zopt2201_data *data, + int val, int val2) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(zopt2201_scale_als); i++) + if (val == zopt2201_scale_als[i].scale && + val2 == zopt2201_scale_als[i].uscale) { + return zopt2201_write_scale_als_by_idx(data, i); + } + + return -EINVAL; +} + +static int zopt2201_write_scale_uvb_by_idx(struct zopt2201_data *data, int idx) +{ + int ret; + + mutex_lock(&data->lock); + ret = zopt2201_set_resolution(data, zopt2201_scale_als[idx].res); + if (ret < 0) + goto unlock; + + ret = zopt2201_set_gain(data, zopt2201_scale_als[idx].gain); + +unlock: + mutex_unlock(&data->lock); + return ret; +} + +static int zopt2201_write_scale_uvb(struct zopt2201_data *data, + int val, int val2) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(zopt2201_scale_uvb); i++) + if (val == zopt2201_scale_uvb[i].scale && + val2 == zopt2201_scale_uvb[i].uscale) + return zopt2201_write_scale_uvb_by_idx(data, i); + + return -EINVAL; +} + +static int zopt2201_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct zopt2201_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + return zopt2201_write_resolution(data, val, val2); + case IIO_CHAN_INFO_SCALE: + switch (chan->address) { + case ZOPT2201_ALS_DATA: + return zopt2201_write_scale_als(data, val, val2); + case ZOPT2201_UVB_DATA: + return zopt2201_write_scale_uvb(data, val, val2); + default: + return -EINVAL; + } + } + + return -EINVAL; +} + +static ssize_t zopt2201_show_int_time_available(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + size_t len = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(zopt2201_resolution); i++) + len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06lu ", + zopt2201_resolution[i].us); + buf[len - 1] = '\n'; + + return len; +} + +static IIO_DEV_ATTR_INT_TIME_AVAIL(zopt2201_show_int_time_available); + +static ssize_t zopt2201_show_als_scale_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(zopt2201_scale_als); i++) + len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06u ", + zopt2201_scale_als[i].scale, + zopt2201_scale_als[i].uscale); + buf[len - 1] = '\n'; + + return len; +} + +static ssize_t zopt2201_show_uvb_scale_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(zopt2201_scale_uvb); i++) + len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06u ", + zopt2201_scale_uvb[i].scale, + zopt2201_scale_uvb[i].uscale); + buf[len - 1] = '\n'; + + return len; +} + +static IIO_DEVICE_ATTR(in_illuminance_scale_available, 0444, + zopt2201_show_als_scale_avail, NULL, 0); +static IIO_DEVICE_ATTR(in_intensity_uv_scale_available, 0444, + zopt2201_show_uvb_scale_avail, NULL, 0); + +static struct attribute *zopt2201_attributes[] = { + &iio_dev_attr_integration_time_available.dev_attr.attr, + &iio_dev_attr_in_illuminance_scale_available.dev_attr.attr, + &iio_dev_attr_in_intensity_uv_scale_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group zopt2201_attribute_group = { + .attrs = zopt2201_attributes, +}; + +static const struct iio_info zopt2201_info = { + .read_raw = zopt2201_read_raw, + .write_raw = zopt2201_write_raw, + .attrs = &zopt2201_attribute_group, +}; + +static int zopt2201_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct zopt2201_data *data; + struct iio_dev *indio_dev; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) + return -EOPNOTSUPP; + + ret = i2c_smbus_read_byte_data(client, ZOPT2201_PART_ID); + if (ret < 0) + return ret; + if (ret != ZOPT2201_PART_NUMBER) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + mutex_init(&data->lock); + + indio_dev->dev.parent = &client->dev; + indio_dev->info = &zopt2201_info; + indio_dev->channels = zopt2201_channels; + indio_dev->num_channels = ARRAY_SIZE(zopt2201_channels); + indio_dev->name = ZOPT2201_DRV_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + + data->rate = ZOPT2201_MEAS_FREQ_100MS; + ret = zopt2201_set_resolution(data, ZOPT2201_MEAS_RES_18BIT); + if (ret < 0) + return ret; + + ret = zopt2201_set_gain(data, ZOPT2201_LS_GAIN_3); + if (ret < 0) + return ret; + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id zopt2201_id[] = { + { "zopt2201", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, zopt2201_id); + +static struct i2c_driver zopt2201_driver = { + .driver = { + .name = ZOPT2201_DRV_NAME, + }, + .probe = zopt2201_probe, + .id_table = zopt2201_id, +}; + +module_i2c_driver(zopt2201_driver); + +MODULE_AUTHOR("Peter Meerwald-Stadler "); +MODULE_DESCRIPTION("IDT ZOPT2201 ambient light and UV B sensor driver"); +MODULE_LICENSE("GPL"); From f44cd758121a341aa96ed89b88e3a08857b406ba Mon Sep 17 00:00:00 2001 From: Peter Meerwald-Stadler Date: Mon, 6 Nov 2017 00:25:00 +0100 Subject: [PATCH 08/44] Documentation: iio: Fix typo Signed-off-by: Peter Meerwald-Stadler Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 2e3f919485f4..a478740e2783 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -1290,7 +1290,7 @@ KernelVersion: 3.4 Contact: linux-iio@vger.kernel.org Description: Unit-less light intensity. Modifiers both and ir indicate - that measurements contains visible and infrared light + that measurements contain visible and infrared light components or just infrared light, respectively. Modifier uv indicates that measurements contain ultraviolet light components. From cc54a660a6332a55cee487b009c332200580b45b Mon Sep 17 00:00:00 2001 From: Harinath Nampally Date: Sun, 5 Nov 2017 13:00:02 -0500 Subject: [PATCH 09/44] iio: accel: mma8452: Rename a struct for code readibility Rename time step look up struct to generic name as the values in the look table are same for all the other events like pulse, transient etc. Signed-off-by: Harinath Nampally Acked-by: Martin Kepplinger Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index bfd4bc806fc2..16adf4710670 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -284,7 +284,7 @@ static const int mma8452_samp_freq[8][2] = { }; /* Datasheet table: step time "Relationship with the ODR" (sample frequency) */ -static const unsigned int mma8452_transient_time_step_us[4][8] = { +static const unsigned int mma8452_time_step_us[4][8] = { { 1250, 2500, 5000, 10000, 20000, 20000, 20000, 20000 }, /* normal */ { 1250, 2500, 5000, 10000, 20000, 80000, 80000, 80000 }, /* l p l n */ { 1250, 2500, 2500, 2500, 2500, 2500, 2500, 2500 }, /* high res*/ @@ -826,7 +826,7 @@ static int mma8452_read_event_value(struct iio_dev *indio_dev, if (power_mode < 0) return power_mode; - us = ret * mma8452_transient_time_step_us[power_mode][ + us = ret * mma8452_time_step_us[power_mode][ mma8452_get_odr_index(data)]; *val = us / USEC_PER_SEC; *val2 = us % USEC_PER_SEC; @@ -883,7 +883,7 @@ static int mma8452_write_event_value(struct iio_dev *indio_dev, return ret; steps = (val * USEC_PER_SEC + val2) / - mma8452_transient_time_step_us[ret][ + mma8452_time_step_us[ret][ mma8452_get_odr_index(data)]; if (steps < 0 || steps > 0xff) From a654c062dcdee14a48a1214a100aa17bc62b24df Mon Sep 17 00:00:00 2001 From: Harinath Nampally Date: Sun, 5 Nov 2017 13:00:03 -0500 Subject: [PATCH 10/44] iio: accel: mma8452: Rename config structs for readability Rename structs holding event configuration registers to more appropriate names. This naming is consistent with the event config register names given in the mma845x and fxls8471 datasheets. Signed-off-by: Harinath Nampally Acked-by: Martin Kepplinger Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 16adf4710670..43c3a6b00340 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -135,7 +135,7 @@ struct mma8452_event_regs { u8 ev_count; }; -static const struct mma8452_event_regs ev_regs_accel_falling = { +static const struct mma8452_event_regs ff_mt_ev_regs = { .ev_cfg = MMA8452_FF_MT_CFG, .ev_cfg_ele = MMA8452_FF_MT_CFG_ELE, .ev_cfg_chan_shift = MMA8452_FF_MT_CHAN_SHIFT, @@ -145,7 +145,7 @@ static const struct mma8452_event_regs ev_regs_accel_falling = { .ev_count = MMA8452_FF_MT_COUNT }; -static const struct mma8452_event_regs ev_regs_accel_rising = { +static const struct mma8452_event_regs trans_ev_regs = { .ev_cfg = MMA8452_TRANSIENT_CFG, .ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE, .ev_cfg_chan_shift = MMA8452_TRANSIENT_CHAN_SHIFT, @@ -777,12 +777,12 @@ static int mma8452_get_event_regs(struct mma8452_data *data, & MMA8452_INT_TRANS) && (data->chip_info->enabled_events & MMA8452_INT_TRANS)) - *ev_reg = &ev_regs_accel_rising; + *ev_reg = &trans_ev_regs; else - *ev_reg = &ev_regs_accel_falling; + *ev_reg = &ff_mt_ev_regs; return 0; case IIO_EV_DIR_FALLING: - *ev_reg = &ev_regs_accel_falling; + *ev_reg = &ff_mt_ev_regs; return 0; default: return -EINVAL; From b87935552efc14316429945568722348dc32c0a5 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 3 Nov 2017 15:03:39 +0200 Subject: [PATCH 11/44] iio: proximity: sx9500: Add another ACPI ID Add new ACPI ID for sx9500 as had been found on prototype board. Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/sx9500.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index 53c5d653e780..f878a9a9f304 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -1022,6 +1022,7 @@ static const struct dev_pm_ops sx9500_pm_ops = { static const struct acpi_device_id sx9500_acpi_match[] = { {"SSX9500", 0}, + {"SASX9500", 0}, { }, }; MODULE_DEVICE_TABLE(acpi, sx9500_acpi_match); From 9ed2484598001cdfbc8f85d349ab6b1e8fc9ee1f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 3 Nov 2017 15:03:40 +0200 Subject: [PATCH 12/44] iio: magnetometer: ak8975: Add another ACPI ID Add new ACPI ID for ak9911 as had been found on prototype board. Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index c09329069d0a..42a827a66512 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -788,6 +788,7 @@ static const struct acpi_device_id ak_acpi_match[] = { {"AK8975", AK8975}, {"AK8963", AK8963}, {"INVN6500", AK8963}, + {"AK009911", AK09911}, {"AK09911", AK09911}, {"AK09912", AK09912}, { }, From 71e083aaf716fffa91cf80e85e88f3c0674be81a Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 3 Nov 2017 09:00:25 +0100 Subject: [PATCH 13/44] staging: iio: ad7152: Improve unlocking of a mutex in ad7152_start_calib() * Add a jump target so that a call of the function "mutex_unlock" is stored only twice in this function implementation. * Replace two calls by goto statements. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Jonathan Cameron --- drivers/staging/iio/cdc/ad7152.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/staging/iio/cdc/ad7152.c b/drivers/staging/iio/cdc/ad7152.c index 61377ca444de..59d1b35f6a4e 100644 --- a/drivers/staging/iio/cdc/ad7152.c +++ b/drivers/staging/iio/cdc/ad7152.c @@ -118,22 +118,23 @@ static inline ssize_t ad7152_start_calib(struct device *dev, mutex_lock(&chip->state_lock); ret = i2c_smbus_write_byte_data(chip->client, AD7152_REG_CFG, regval); - if (ret < 0) { - mutex_unlock(&chip->state_lock); - return ret; - } + if (ret < 0) + goto unlock; do { mdelay(20); ret = i2c_smbus_read_byte_data(chip->client, AD7152_REG_CFG); - if (ret < 0) { - mutex_unlock(&chip->state_lock); - return ret; - } + if (ret < 0) + goto unlock; + } while ((ret == regval) && timeout--); mutex_unlock(&chip->state_lock); return len; + +unlock: + mutex_unlock(&chip->state_lock); + return ret; } static ssize_t ad7152_start_offset_calib(struct device *dev, From aa6b707c146d4d19e87af0b995fab95bec5990a3 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Fri, 3 Nov 2017 09:26:28 +0100 Subject: [PATCH 14/44] staging: iio: ad7746: Improve unlocking of a mutex in ad7746_start_calib() * Add a jump target so that a call of the function "mutex_unlock" is stored only twice in this function implementation. * Replace two calls by goto statements. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Jonathan Cameron --- drivers/staging/iio/cdc/ad7746.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c index a124853a05f0..c4a864725376 100644 --- a/drivers/staging/iio/cdc/ad7746.c +++ b/drivers/staging/iio/cdc/ad7746.c @@ -302,23 +302,24 @@ static inline ssize_t ad7746_start_calib(struct device *dev, mutex_lock(&chip->lock); regval |= chip->config; ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG, regval); - if (ret < 0) { - mutex_unlock(&chip->lock); - return ret; - } + if (ret < 0) + goto unlock; do { msleep(20); ret = i2c_smbus_read_byte_data(chip->client, AD7746_REG_CFG); - if (ret < 0) { - mutex_unlock(&chip->lock); - return ret; - } + if (ret < 0) + goto unlock; + } while ((ret == regval) && timeout--); mutex_unlock(&chip->lock); return len; + +unlock: + mutex_unlock(&chip->lock); + return ret; } static ssize_t ad7746_start_offset_calib(struct device *dev, From e36c8c161c3b7b63cdae88939bed150ff1560b78 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 31 Oct 2017 16:21:47 +0200 Subject: [PATCH 15/44] iio: imu: inv_mpu6050: Remove duplicate NULL check Since i2c_unregister_device() became NULL-aware we may remove duplicate NULL check. Cc: Jonathan Cameron Cc: Hartmut Knaack Cc: Lars-Peter Clausen Cc: Peter Meerwald-Stadler Cc: linux-iio@vger.kernel.org Signed-off-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c index dd6fc6d21f9d..d78a10403bac 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c @@ -196,8 +196,7 @@ void inv_mpu_acpi_delete_mux_client(struct i2c_client *client) { struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev)); - if (st->mux_client) - i2c_unregister_device(st->mux_client); + i2c_unregister_device(st->mux_client); } #else From 2e64438487697f3f099946edc8acd4ceea6b1ab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Br=C3=BCns?= Date: Sat, 28 Oct 2017 23:12:46 +0200 Subject: [PATCH 16/44] iio: adc: ina2xx: Shift bus voltage register to mask flag bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lower bits of the INA219/220 bus voltage register are conversion status flags, properly shift the value. When reading via IIO buffer, the value is passed on unaltered, shifting is the responsibility of the user. Signed-off-by: Stefan Brüns Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ina2xx-adc.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 84a43871f7dc..651dc9df2a7b 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -44,7 +44,6 @@ #define INA226_MASK_ENABLE 0x06 #define INA226_CVRF BIT(3) -#define INA219_CNVR BIT(1) #define INA2XX_MAX_REGISTERS 8 @@ -79,6 +78,11 @@ #define INA226_ITS_MASK GENMASK(5, 3) #define INA226_SHIFT_ITS(val) ((val) << 3) +/* INA219 Bus voltage register, low bits are flags */ +#define INA219_OVF BIT(0) +#define INA219_CNVR BIT(1) +#define INA219_BUS_VOLTAGE_SHIFT 3 + /* Cosmetic macro giving the sampling period for a full P=UxI cycle */ #define SAMPLING_PERIOD(c) ((c->int_time_vbus + c->int_time_vshunt) \ * c->avg) @@ -112,7 +116,7 @@ struct ina2xx_config { u16 config_default; int calibration_factor; int shunt_div; - int bus_voltage_shift; + int bus_voltage_shift; /* position of lsb */ int bus_voltage_lsb; /* uV */ int power_lsb; /* uW */ enum ina2xx_ids chip_id; @@ -135,7 +139,7 @@ static const struct ina2xx_config ina2xx_config[] = { .config_default = INA219_CONFIG_DEFAULT, .calibration_factor = 40960000, .shunt_div = 100, - .bus_voltage_shift = 3, + .bus_voltage_shift = INA219_BUS_VOLTAGE_SHIFT, .bus_voltage_lsb = 4000, .power_lsb = 20000, .chip_id = ina219, @@ -170,6 +174,9 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev, else *val = regval; + if (chan->address == INA2XX_BUS_VOLTAGE) + *val >>= chip->config->bus_voltage_shift; + return IIO_VAL_INT; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: @@ -203,9 +210,9 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev, return IIO_VAL_FRACTIONAL; case INA2XX_BUS_VOLTAGE: - /* processed (mV) = raw*lsb (uV) / (1000 << shift) */ + /* processed (mV) = raw * lsb (uV) / 1000 */ *val = chip->config->bus_voltage_lsb; - *val2 = 1000 << chip->config->bus_voltage_shift; + *val2 = 1000; return IIO_VAL_FRACTIONAL; case INA2XX_POWER: @@ -532,7 +539,7 @@ static ssize_t ina2xx_shunt_resistor_store(struct device *dev, * Sampling Freq is a consequence of the integration times of * the Voltage channels. */ -#define INA219_CHAN_VOLTAGE(_index, _address) { \ +#define INA219_CHAN_VOLTAGE(_index, _address, _shift) { \ .type = IIO_VOLTAGE, \ .address = (_address), \ .indexed = 1, \ @@ -544,7 +551,8 @@ static ssize_t ina2xx_shunt_resistor_store(struct device *dev, .scan_index = (_index), \ .scan_type = { \ .sign = 'u', \ - .realbits = 16, \ + .shift = _shift, \ + .realbits = 16 - _shift, \ .storagebits = 16, \ .endianness = IIO_LE, \ } \ @@ -579,8 +587,8 @@ static const struct iio_chan_spec ina226_channels[] = { }; static const struct iio_chan_spec ina219_channels[] = { - INA219_CHAN_VOLTAGE(0, INA2XX_SHUNT_VOLTAGE), - INA219_CHAN_VOLTAGE(1, INA2XX_BUS_VOLTAGE), + INA219_CHAN_VOLTAGE(0, INA2XX_SHUNT_VOLTAGE, 0), + INA219_CHAN_VOLTAGE(1, INA2XX_BUS_VOLTAGE, INA219_BUS_VOLTAGE_SHIFT), INA219_CHAN(IIO_POWER, 2, INA2XX_POWER), INA219_CHAN(IIO_CURRENT, 3, INA2XX_CURRENT), IIO_CHAN_SOFT_TIMESTAMP(4), From 2d8119d752d4fe794d768b0c42cdf6cf14b37bcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Br=C3=BCns?= Date: Sat, 28 Oct 2017 23:12:47 +0200 Subject: [PATCH 17/44] iio: adc: ina2xx: Use LSB specifier instead of divider in config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While the config uses the physical value corresponding to the LSB for both the power and the bus voltage register, the shunt voltage is specified as parts of 1 mV. Use the LSB physical value for all registers. No functional change. Signed-off-by: Stefan Brüns Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ina2xx-adc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 651dc9df2a7b..ada98ee4b80a 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -115,7 +115,7 @@ enum ina2xx_ids { ina219, ina226 }; struct ina2xx_config { u16 config_default; int calibration_factor; - int shunt_div; + int shunt_voltage_lsb; /* nV */ int bus_voltage_shift; /* position of lsb */ int bus_voltage_lsb; /* uV */ int power_lsb; /* uW */ @@ -138,7 +138,7 @@ static const struct ina2xx_config ina2xx_config[] = { [ina219] = { .config_default = INA219_CONFIG_DEFAULT, .calibration_factor = 40960000, - .shunt_div = 100, + .shunt_voltage_lsb = 10000, .bus_voltage_shift = INA219_BUS_VOLTAGE_SHIFT, .bus_voltage_lsb = 4000, .power_lsb = 20000, @@ -147,7 +147,7 @@ static const struct ina2xx_config ina2xx_config[] = { [ina226] = { .config_default = INA226_CONFIG_DEFAULT, .calibration_factor = 5120000, - .shunt_div = 400, + .shunt_voltage_lsb = 2500, .bus_voltage_shift = 0, .bus_voltage_lsb = 1250, .power_lsb = 25000, @@ -204,9 +204,9 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: switch (chan->address) { case INA2XX_SHUNT_VOLTAGE: - /* processed (mV) = raw/shunt_div */ - *val2 = chip->config->shunt_div; - *val = 1; + /* processed (mV) = raw * lsb(nV) / 1000000 */ + *val = chip->config->shunt_voltage_lsb; + *val2 = 1000000; return IIO_VAL_FRACTIONAL; case INA2XX_BUS_VOLTAGE: From ca6a2d86acaebd7a7e871fbb361a75101cf9f4cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Br=C3=BCns?= Date: Sat, 28 Oct 2017 23:12:48 +0200 Subject: [PATCH 18/44] iio: adc: ina2xx: Allow setting Shunt Voltage PGA gain and Bus Voltage range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reducing shunt and bus voltage range improves the accuracy, so allow altering the default settings. Both settings are exposed as gain values. While for the shunt voltage this is straightforward, the bus range settings of 32V (default) and 16V are mapped to gain values of 1 resp. 2, to provide a uniform API to userspace. As the gain settings are incorporated into the raw values by the sensor itself, adjusting of the scale attributes is not necessary. Signed-off-by: Stefan Brüns Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ina2xx-adc.c | 111 ++++++++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index ada98ee4b80a..ddf878163bf9 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -48,8 +48,10 @@ #define INA2XX_MAX_REGISTERS 8 /* settings - depend on use case */ -#define INA219_CONFIG_DEFAULT 0x399F /* PGA=8 */ +#define INA219_CONFIG_DEFAULT 0x399F /* PGA=1/8, BRNG=32V */ #define INA219_DEFAULT_IT 532 +#define INA219_DEFAULT_BRNG 1 /* 32V */ +#define INA219_DEFAULT_PGA 125 /* 1000/8 */ #define INA226_CONFIG_DEFAULT 0x4327 #define INA226_DEFAULT_AVG 4 #define INA226_DEFAULT_IT 1110 @@ -62,6 +64,14 @@ */ #define INA2XX_MODE_MASK GENMASK(3, 0) +/* Gain for VShunt: 1/8 (default), 1/4, 1/2, 1 */ +#define INA219_PGA_MASK GENMASK(12, 11) +#define INA219_SHIFT_PGA(val) ((val) << 11) + +/* VBus range: 32V (default), 16V */ +#define INA219_BRNG_MASK BIT(13) +#define INA219_SHIFT_BRNG(val) ((val) << 13) + /* Averaging for VBus/VShunt/Power */ #define INA226_AVG_MASK GENMASK(11, 9) #define INA226_SHIFT_AVG(val) ((val) << 9) @@ -131,6 +141,8 @@ struct ina2xx_chip_info { int avg; int int_time_vbus; /* Bus voltage integration time uS */ int int_time_vshunt; /* Shunt voltage integration time uS */ + int range_vbus; /* Bus voltage maximum in V */ + int pga_gain_vshunt; /* Shunt voltage PGA gain */ bool allow_async_readout; }; @@ -226,6 +238,18 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev, *val = 1; return IIO_VAL_INT; } + + case IIO_CHAN_INFO_HARDWAREGAIN: + switch (chan->address) { + case INA2XX_SHUNT_VOLTAGE: + *val = chip->pga_gain_vshunt; + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + + case INA2XX_BUS_VOLTAGE: + *val = chip->range_vbus == 32 ? 1 : 2; + return IIO_VAL_INT; + } } return -EINVAL; @@ -360,6 +384,74 @@ static int ina219_set_int_time_vshunt(struct ina2xx_chip_info *chip, return 0; } +static const int ina219_vbus_range_tab[] = { 1, 2 }; +static int ina219_set_vbus_range_denom(struct ina2xx_chip_info *chip, + unsigned int range, + unsigned int *config) +{ + if (range == 1) + chip->range_vbus = 32; + else if (range == 2) + chip->range_vbus = 16; + else + return -EINVAL; + + *config &= ~INA219_BRNG_MASK; + *config |= INA219_SHIFT_BRNG(range == 1 ? 1 : 0) & INA219_BRNG_MASK; + + return 0; +} + +static const int ina219_vshunt_gain_tab[] = { 125, 250, 500, 1000 }; +static const int ina219_vshunt_gain_frac[] = { + 125, 1000, 250, 1000, 500, 1000, 1000, 1000 }; + +static int ina219_set_vshunt_pga_gain(struct ina2xx_chip_info *chip, + unsigned int gain, + unsigned int *config) +{ + int bits; + + if (gain < 125 || gain > 1000) + return -EINVAL; + + bits = find_closest(gain, ina219_vshunt_gain_tab, + ARRAY_SIZE(ina219_vshunt_gain_tab)); + + chip->pga_gain_vshunt = ina219_vshunt_gain_tab[bits]; + bits = 3 - bits; + + *config &= ~INA219_PGA_MASK; + *config |= INA219_SHIFT_PGA(bits) & INA219_PGA_MASK; + + return 0; +} + +static int ina2xx_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_HARDWAREGAIN: + switch (chan->address) { + case INA2XX_SHUNT_VOLTAGE: + *type = IIO_VAL_FRACTIONAL; + *length = sizeof(ina219_vshunt_gain_frac) / sizeof(int); + *vals = ina219_vshunt_gain_frac; + return IIO_AVAIL_LIST; + + case INA2XX_BUS_VOLTAGE: + *type = IIO_VAL_INT; + *length = sizeof(ina219_vbus_range_tab) / sizeof(int); + *vals = ina219_vbus_range_tab; + return IIO_AVAIL_LIST; + } + } + + return -EINVAL; +} + static int ina2xx_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) @@ -402,6 +494,14 @@ static int ina2xx_write_raw(struct iio_dev *indio_dev, } break; + case IIO_CHAN_INFO_HARDWAREGAIN: + if (chan->address == INA2XX_SHUNT_VOLTAGE) + ret = ina219_set_vshunt_pga_gain(chip, val * 1000 + + val2 / 1000, &tmp); + else + ret = ina219_set_vbus_range_denom(chip, val, &tmp); + break; + default: ret = -EINVAL; } @@ -546,7 +646,10 @@ static ssize_t ina2xx_shunt_resistor_store(struct device *dev, .channel = (_index), \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_SCALE) | \ - BIT(IIO_CHAN_INFO_INT_TIME), \ + BIT(IIO_CHAN_INFO_INT_TIME) | \ + BIT(IIO_CHAN_INFO_HARDWAREGAIN), \ + .info_mask_separate_available = \ + BIT(IIO_CHAN_INFO_HARDWAREGAIN), \ .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ .scan_index = (_index), \ .scan_type = { \ @@ -754,7 +857,6 @@ static IIO_CONST_ATTR_NAMED(ina226_integration_time_available, integration_time_available, "0.000140 0.000204 0.000332 0.000588 0.001100 0.002116 0.004156 0.008244"); - static IIO_DEVICE_ATTR(in_allow_async_readout, S_IRUGO | S_IWUSR, ina2xx_allow_async_readout_show, ina2xx_allow_async_readout_store, 0); @@ -788,6 +890,7 @@ static const struct attribute_group ina226_attribute_group = { static const struct iio_info ina219_info = { .attrs = &ina219_attribute_group, .read_raw = ina2xx_read_raw, + .read_avail = ina2xx_read_avail, .write_raw = ina2xx_write_raw, .debugfs_reg_access = ina2xx_debug_reg, }; @@ -868,6 +971,8 @@ static int ina2xx_probe(struct i2c_client *client, chip->avg = 1; ina219_set_int_time_vbus(chip, INA219_DEFAULT_IT, &val); ina219_set_int_time_vshunt(chip, INA219_DEFAULT_IT, &val); + ina219_set_vbus_range_denom(chip, INA219_DEFAULT_BRNG, &val); + ina219_set_vshunt_pga_gain(chip, INA219_DEFAULT_PGA, &val); } ret = ina2xx_init(chip, val); From 098c5a6a14b7cb7727f370bcdcca693b3098e37f Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Wed, 25 Oct 2017 11:27:43 +0200 Subject: [PATCH 19/44] dt-bindings: iio: adc: stm32: add support for diff channels STM32H7 ADC channels may be configured either as single-ended or differential. Add 'st,adc-diff-channels' property to support differential channels. Differential channels are defined as a pair of positive and negative inputs: vinp & vinn. Signed-off-by: Fabrice Gasnier Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/st,stm32-adc.txt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt index 48bfcaa3ffcd..e8bb8243e92c 100644 --- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt @@ -62,6 +62,15 @@ Required properties: - st,adc-channels: List of single-ended channels muxed for this ADC. It can have up to 16 channels on stm32f4 or 20 channels on stm32h7, numbered from 0 to 15 or 19 (resp. for in0..in15 or in0..in19). +- st,adc-diff-channels: List of differential channels muxed for this ADC. + Depending on part used, some channels can be configured as differential + instead of single-ended (e.g. stm32h7). List here positive and negative + inputs pairs as , ,... vinp and vinn are numbered + from 0 to 19 on stm32h7) + Note: At least one of "st,adc-channels" or "st,adc-diff-channels" is required. + Both properties can be used together. Some channels can be used as + single-ended and some other ones as differential (mixed). But channels + can't be configured both as single-ended and differential (invalid). - #io-channel-cells = <1>: See the IIO bindings section "IIO consumers" in Documentation/devicetree/bindings/iio/iio-bindings.txt @@ -111,3 +120,18 @@ Example: ... other adc child nodes follow... }; + +Example to setup: +- channel 1 as single-ended +- channels 2 & 3 as differential (with resp. 6 & 7 negative inputs) + + adc: adc@40022000 { + compatible = "st,stm32h7-adc-core"; + ... + adc1: adc@0 { + compatible = "st,stm32h7-adc"; + ... + st,adc-channels = <1>; + st,adc-diff-channels = <2 6>, <3 7>; + }; + }; From 0bae72aa8a89fd2b6a8ae4b21ff9141fc1f3b496 Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Wed, 25 Oct 2017 11:27:44 +0200 Subject: [PATCH 20/44] iio: adc: stm32: remove const channel names definition Remove const array that defines channels. Build channels definition at probe time, when initializing channels (only for requested ones). This will ease adding differential channels support. Signed-off-by: Fabrice Gasnier Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc.c | 66 ++++++++----------------------------- 1 file changed, 14 insertions(+), 52 deletions(-) diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index c9d96f935dba..99981de41e0d 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -153,6 +153,8 @@ enum stm32h7_adc_dmngt { /* BOOST bit must be set on STM32H7 when ADC clock is above 20MHz */ #define STM32H7_BOOST_CLKRATE 20000000UL +#define STM32_ADC_CH_MAX 20 /* max number of channels */ +#define STM32_ADC_CH_SZ 5 /* max channel name size */ #define STM32_ADC_MAX_SQ 16 /* SQ1..SQ16 */ #define STM32_ADC_MAX_SMP 7 /* SMPx range is [0..7] */ #define STM32_ADC_TIMEOUT_US 100000 @@ -300,6 +302,7 @@ struct stm32_adc_cfg { * @pcsel bitmask to preselect channels on some devices * @smpr_val: sampling time settings (e.g. smpr1 / smpr2) * @cal: optional calibration data on some devices + * @chan_name: channel name array */ struct stm32_adc { struct stm32_adc_common *common; @@ -321,69 +324,28 @@ struct stm32_adc { u32 pcsel; u32 smpr_val[2]; struct stm32_adc_calib cal; -}; - -/** - * struct stm32_adc_chan_spec - specification of stm32 adc channel - * @type: IIO channel type - * @channel: channel number (single ended) - * @name: channel name (single ended) - */ -struct stm32_adc_chan_spec { - enum iio_chan_type type; - int channel; - const char *name; + char chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ]; }; /** * struct stm32_adc_info - stm32 ADC, per instance config data - * @channels: Reference to stm32 channels spec * @max_channels: Number of channels * @resolutions: available resolutions * @num_res: number of available resolutions */ struct stm32_adc_info { - const struct stm32_adc_chan_spec *channels; int max_channels; const unsigned int *resolutions; const unsigned int num_res; }; -/* - * Input definitions common for all instances: - * stm32f4 can have up to 16 channels - * stm32h7 can have up to 20 channels - */ -static const struct stm32_adc_chan_spec stm32_adc_channels[] = { - { IIO_VOLTAGE, 0, "in0" }, - { IIO_VOLTAGE, 1, "in1" }, - { IIO_VOLTAGE, 2, "in2" }, - { IIO_VOLTAGE, 3, "in3" }, - { IIO_VOLTAGE, 4, "in4" }, - { IIO_VOLTAGE, 5, "in5" }, - { IIO_VOLTAGE, 6, "in6" }, - { IIO_VOLTAGE, 7, "in7" }, - { IIO_VOLTAGE, 8, "in8" }, - { IIO_VOLTAGE, 9, "in9" }, - { IIO_VOLTAGE, 10, "in10" }, - { IIO_VOLTAGE, 11, "in11" }, - { IIO_VOLTAGE, 12, "in12" }, - { IIO_VOLTAGE, 13, "in13" }, - { IIO_VOLTAGE, 14, "in14" }, - { IIO_VOLTAGE, 15, "in15" }, - { IIO_VOLTAGE, 16, "in16" }, - { IIO_VOLTAGE, 17, "in17" }, - { IIO_VOLTAGE, 18, "in18" }, - { IIO_VOLTAGE, 19, "in19" }, -}; - static const unsigned int stm32f4_adc_resolutions[] = { /* sorted values so the index matches RES[1:0] in STM32F4_ADC_CR1 */ 12, 10, 8, 6, }; +/* stm32f4 can have up to 16 channels */ static const struct stm32_adc_info stm32f4_adc_info = { - .channels = stm32_adc_channels, .max_channels = 16, .resolutions = stm32f4_adc_resolutions, .num_res = ARRAY_SIZE(stm32f4_adc_resolutions), @@ -394,9 +356,9 @@ static const unsigned int stm32h7_adc_resolutions[] = { 16, 14, 12, 10, 8, }; +/* stm32h7 can have up to 20 channels */ static const struct stm32_adc_info stm32h7_adc_info = { - .channels = stm32_adc_channels, - .max_channels = 20, + .max_channels = STM32_ADC_CH_MAX, .resolutions = stm32h7_adc_resolutions, .num_res = ARRAY_SIZE(stm32h7_adc_resolutions), }; @@ -1628,15 +1590,16 @@ static void stm32_adc_smpr_init(struct stm32_adc *adc, int channel, u32 smp_ns) } static void stm32_adc_chan_init_one(struct iio_dev *indio_dev, - struct iio_chan_spec *chan, - const struct stm32_adc_chan_spec *channel, + struct iio_chan_spec *chan, u32 vinp, int scan_index, u32 smp) { struct stm32_adc *adc = iio_priv(indio_dev); + char *name = adc->chan_name[vinp]; - chan->type = channel->type; - chan->channel = channel->channel; - chan->datasheet_name = channel->name; + chan->type = IIO_VOLTAGE; + chan->channel = vinp; + snprintf(name, STM32_ADC_CH_SZ, "in%d", vinp); + chan->datasheet_name = name; chan->scan_index = scan_index; chan->indexed = 1; chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); @@ -1699,8 +1662,7 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev) scan_index, &smp); stm32_adc_chan_init_one(indio_dev, &channels[scan_index], - &adc_info->channels[val], - scan_index, smp); + val, scan_index, smp); scan_index++; } From 3fb2e24ed7b713bd6c1a03ab9f9849f76629f26f Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Wed, 25 Oct 2017 11:27:45 +0200 Subject: [PATCH 21/44] iio: adc: stm32: add support for differential channels STM32H7 ADC channels can be configured either as single ended or differential with 'st,adc-channels' or 'st,adc-diff-channels' (positive and negative input pair: , ...). Differential channels have different offset and scale, from spec: raw value = (full_scale / 2) * (1 + (vinp - vinn) / vref). Add offset attribute. Differential channels are selected by DIFSEL register. Negative inputs must be added to pre-selected channels as well (PCSEL). Signed-off-by: Fabrice Gasnier Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc.c | 123 ++++++++++++++++++++++++++++++------ 1 file changed, 103 insertions(+), 20 deletions(-) diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 99981de41e0d..da7fef376ed8 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -92,6 +92,7 @@ #define STM32H7_ADC_SQR3 0x38 #define STM32H7_ADC_SQR4 0x3C #define STM32H7_ADC_DR 0x40 +#define STM32H7_ADC_DIFSEL 0xC0 #define STM32H7_ADC_CALFACT 0xC4 #define STM32H7_ADC_CALFACT2 0xC8 @@ -154,7 +155,7 @@ enum stm32h7_adc_dmngt { #define STM32H7_BOOST_CLKRATE 20000000UL #define STM32_ADC_CH_MAX 20 /* max number of channels */ -#define STM32_ADC_CH_SZ 5 /* max channel name size */ +#define STM32_ADC_CH_SZ 10 /* max channel name size */ #define STM32_ADC_MAX_SQ 16 /* SQ1..SQ16 */ #define STM32_ADC_MAX_SMP 7 /* SMPx range is [0..7] */ #define STM32_ADC_TIMEOUT_US 100000 @@ -299,6 +300,7 @@ struct stm32_adc_cfg { * @rx_buf: dma rx buffer cpu address * @rx_dma_buf: dma rx buffer bus address * @rx_buf_sz: dma rx buffer size + * @difsel bitmask to set single-ended/differential channel * @pcsel bitmask to preselect channels on some devices * @smpr_val: sampling time settings (e.g. smpr1 / smpr2) * @cal: optional calibration data on some devices @@ -321,12 +323,18 @@ struct stm32_adc { u8 *rx_buf; dma_addr_t rx_dma_buf; unsigned int rx_buf_sz; + u32 difsel; u32 pcsel; u32 smpr_val[2]; struct stm32_adc_calib cal; char chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ]; }; +struct stm32_adc_diff_channel { + u32 vinp; + u32 vinn; +}; + /** * struct stm32_adc_info - stm32 ADC, per instance config data * @max_channels: Number of channels @@ -945,15 +953,19 @@ pwr_dwn: * stm32h7_adc_prepare() - Leave power down mode to enable ADC. * @adc: stm32 adc instance * Leave power down mode. + * Configure channels as single ended or differential before enabling ADC. * Enable ADC. * Restore calibration data. - * Pre-select channels that may be used in PCSEL (required by input MUX / IO). + * Pre-select channels that may be used in PCSEL (required by input MUX / IO): + * - Only one input is selected for single ended (e.g. 'vinp') + * - Two inputs are selected for differential channels (e.g. 'vinp' & 'vinn') */ static int stm32h7_adc_prepare(struct stm32_adc *adc) { int ret; stm32h7_adc_exit_pwr_down(adc); + stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel); ret = stm32h7_adc_enable(adc); if (ret) @@ -1225,10 +1237,23 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev, return ret; case IIO_CHAN_INFO_SCALE: - *val = adc->common->vref_mv; - *val2 = chan->scan_type.realbits; + if (chan->differential) { + *val = adc->common->vref_mv * 2; + *val2 = chan->scan_type.realbits; + } else { + *val = adc->common->vref_mv; + *val2 = chan->scan_type.realbits; + } return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OFFSET: + if (chan->differential) + /* ADC_full_scale / 2 */ + *val = -((1 << chan->scan_type.realbits) / 2); + else + *val = 0; + return IIO_VAL_INT; + default: return -EINVAL; } @@ -1591,29 +1616,39 @@ static void stm32_adc_smpr_init(struct stm32_adc *adc, int channel, u32 smp_ns) static void stm32_adc_chan_init_one(struct iio_dev *indio_dev, struct iio_chan_spec *chan, u32 vinp, - int scan_index, u32 smp) + u32 vinn, int scan_index, bool differential) { struct stm32_adc *adc = iio_priv(indio_dev); char *name = adc->chan_name[vinp]; chan->type = IIO_VOLTAGE; chan->channel = vinp; - snprintf(name, STM32_ADC_CH_SZ, "in%d", vinp); + if (differential) { + chan->differential = 1; + chan->channel2 = vinn; + snprintf(name, STM32_ADC_CH_SZ, "in%d-in%d", vinp, vinn); + } else { + snprintf(name, STM32_ADC_CH_SZ, "in%d", vinp); + } chan->datasheet_name = name; chan->scan_index = scan_index; chan->indexed = 1; chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); - chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); + chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET); chan->scan_type.sign = 'u'; chan->scan_type.realbits = adc->cfg->adc_info->resolutions[adc->res]; chan->scan_type.storagebits = 16; chan->ext_info = stm32_adc_ext_info; - /* Prepare sampling time settings */ - stm32_adc_smpr_init(adc, chan->channel, smp); - /* pre-build selected channels mask */ adc->pcsel |= BIT(chan->channel); + if (differential) { + /* pre-build diff channels mask */ + adc->difsel |= BIT(chan->channel); + /* Also add negative input to pre-selected channels */ + adc->pcsel |= BIT(chan->channel2); + } } static int stm32_adc_chan_of_init(struct iio_dev *indio_dev) @@ -1621,17 +1656,40 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev) struct device_node *node = indio_dev->dev.of_node; struct stm32_adc *adc = iio_priv(indio_dev); const struct stm32_adc_info *adc_info = adc->cfg->adc_info; + struct stm32_adc_diff_channel diff[STM32_ADC_CH_MAX]; struct property *prop; const __be32 *cur; struct iio_chan_spec *channels; - int scan_index = 0, num_channels, ret; + int scan_index = 0, num_channels = 0, num_diff = 0, ret, i; u32 val, smp = 0; - num_channels = of_property_count_u32_elems(node, "st,adc-channels"); - if (num_channels < 0 || - num_channels > adc_info->max_channels) { + ret = of_property_count_u32_elems(node, "st,adc-channels"); + if (ret > adc_info->max_channels) { dev_err(&indio_dev->dev, "Bad st,adc-channels?\n"); - return num_channels < 0 ? num_channels : -EINVAL; + return -EINVAL; + } else if (ret > 0) { + num_channels += ret; + } + + ret = of_property_count_elems_of_size(node, "st,adc-diff-channels", + sizeof(*diff)); + if (ret > adc_info->max_channels) { + dev_err(&indio_dev->dev, "Bad st,adc-diff-channels?\n"); + return -EINVAL; + } else if (ret > 0) { + int size = ret * sizeof(*diff) / sizeof(u32); + + num_diff = ret; + num_channels += ret; + ret = of_property_read_u32_array(node, "st,adc-diff-channels", + (u32 *)diff, size); + if (ret) + return ret; + } + + if (!num_channels) { + dev_err(&indio_dev->dev, "No channels configured\n"); + return -ENODATA; } /* Optional sample time is provided either for each, or all channels */ @@ -1652,6 +1710,33 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev) return -EINVAL; } + /* Channel can't be configured both as single-ended & diff */ + for (i = 0; i < num_diff; i++) { + if (val == diff[i].vinp) { + dev_err(&indio_dev->dev, + "channel %d miss-configured\n", val); + return -EINVAL; + } + } + stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val, + 0, scan_index, false); + scan_index++; + } + + for (i = 0; i < num_diff; i++) { + if (diff[i].vinp >= adc_info->max_channels || + diff[i].vinn >= adc_info->max_channels) { + dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n", + diff[i].vinp, diff[i].vinn); + return -EINVAL; + } + stm32_adc_chan_init_one(indio_dev, &channels[scan_index], + diff[i].vinp, diff[i].vinn, scan_index, + true); + scan_index++; + } + + for (i = 0; i < scan_index; i++) { /* * Using of_property_read_u32_index(), smp value will only be * modified if valid u32 value can be decoded. This allows to @@ -1659,11 +1744,9 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev) * value per channel. */ of_property_read_u32_index(node, "st,min-sample-time-nsecs", - scan_index, &smp); - - stm32_adc_chan_init_one(indio_dev, &channels[scan_index], - val, scan_index, smp); - scan_index++; + i, &smp); + /* Prepare sampling time settings */ + stm32_adc_smpr_init(adc, channels[i].channel, smp); } indio_dev->num_channels = scan_index; From dd86dbf94f93add95e153084e54b94deff7264b7 Mon Sep 17 00:00:00 2001 From: Peter Meerwald-Stadler Date: Fri, 27 Oct 2017 21:45:32 +0200 Subject: [PATCH 22/44] iio: health: max30102: Fix missing newline in dev_err Signed-off-by: Peter Meerwald-Stadler Acked-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/health/max30102.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c index 203ffb9cad6a..7dd9cd764af8 100644 --- a/drivers/iio/health/max30102.c +++ b/drivers/iio/health/max30102.c @@ -261,7 +261,7 @@ static int max30102_led_init(struct max30102_data *data) ret = max30102_get_current_idx(val, ®); if (ret) { - dev_err(dev, "invalid IR LED current setting %d", val); + dev_err(dev, "invalid IR LED current setting %d\n", val); return ret; } From c31c946af81c389febec8a09e627062a6ebd8c88 Mon Sep 17 00:00:00 2001 From: Peter Meerwald-Stadler Date: Fri, 27 Oct 2017 21:45:33 +0200 Subject: [PATCH 23/44] iio: health: max30102: Remove inconsistent full stop in error message Signed-off-by: Peter Meerwald-Stadler Acked-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/health/max30102.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c index 7dd9cd764af8..52733ddaf506 100644 --- a/drivers/iio/health/max30102.c +++ b/drivers/iio/health/max30102.c @@ -420,7 +420,7 @@ static int max30102_probe(struct i2c_client *client, data->regmap = devm_regmap_init_i2c(client, &max30102_regmap_config); if (IS_ERR(data->regmap)) { - dev_err(&client->dev, "regmap initialization failed.\n"); + dev_err(&client->dev, "regmap initialization failed\n"); return PTR_ERR(data->regmap); } max30102_set_powermode(data, false); From 7b0b0ec1576e36beb6915c81dec2db9c077270d8 Mon Sep 17 00:00:00 2001 From: Peter Meerwald-Stadler Date: Fri, 27 Oct 2017 21:45:34 +0200 Subject: [PATCH 24/44] iio: health: max30102: Fix mode config values Table 4 of the datasheet specifies the mode control, these are not individual bits; add multi LED mode Add multi-LED mode and fix MODE_MASK (3 bits wide, not 2) Signed-off-by: Peter Meerwald-Stadler Acked-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/health/max30102.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c index 52733ddaf506..bfda1f2f6907 100644 --- a/drivers/iio/health/max30102.c +++ b/drivers/iio/health/max30102.c @@ -60,9 +60,10 @@ #define MAX30102_REG_FIFO_CONFIG_AFULL BIT(0) #define MAX30102_REG_MODE_CONFIG 0x09 -#define MAX30102_REG_MODE_CONFIG_MODE_SPO2_EN BIT(0) -#define MAX30102_REG_MODE_CONFIG_MODE_HR_EN BIT(1) -#define MAX30102_REG_MODE_CONFIG_MODE_MASK 0x03 +#define MAX30102_REG_MODE_CONFIG_MODE_HR 0x02 /* red LED */ +#define MAX30102_REG_MODE_CONFIG_MODE_HR_SPO2 0x03 /* red + IR LED */ +#define MAX30102_REG_MODE_CONFIG_MODE_MULTI 0x07 /* multi-LED mode */ +#define MAX30102_REG_MODE_CONFIG_MODE_MASK GENMASK(2, 0) #define MAX30102_REG_MODE_CONFIG_PWR BIT(7) #define MAX30102_REG_SPO2_CONFIG 0x0a @@ -287,11 +288,10 @@ static int max30102_chip_init(struct max30102_data *data) if (ret) return ret; - /* enable SPO2 mode */ + /* enable HR + SPO2 mode */ ret = regmap_update_bits(data->regmap, MAX30102_REG_MODE_CONFIG, MAX30102_REG_MODE_CONFIG_MODE_MASK, - MAX30102_REG_MODE_CONFIG_MODE_HR_EN | - MAX30102_REG_MODE_CONFIG_MODE_SPO2_EN); + MAX30102_REG_MODE_CONFIG_MODE_HR_SPO2); if (ret) return ret; From d0b950c73dee67c2232482dbb6949b7a4f25a828 Mon Sep 17 00:00:00 2001 From: Peter Meerwald-Stadler Date: Fri, 27 Oct 2017 21:45:35 +0200 Subject: [PATCH 25/44] iio: health: max30102: Check retval of powermode function Signed-off-by: Peter Meerwald-Stadler Acked-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/health/max30102.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c index bfda1f2f6907..3b6213af79ea 100644 --- a/drivers/iio/health/max30102.c +++ b/drivers/iio/health/max30102.c @@ -423,7 +423,10 @@ static int max30102_probe(struct i2c_client *client, dev_err(&client->dev, "regmap initialization failed\n"); return PTR_ERR(data->regmap); } - max30102_set_powermode(data, false); + + ret = max30102_set_powermode(data, false); + if (ret) + return ret; ret = max30102_chip_init(data); if (ret) From 84b0ce05e4a5f1e17b2c74d7afbe72113b6f8a04 Mon Sep 17 00:00:00 2001 From: Peter Meerwald-Stadler Date: Fri, 27 Oct 2017 21:45:36 +0200 Subject: [PATCH 26/44] iio: health: max30102: Add check for part ID Signed-off-by: Peter Meerwald-Stadler Acked-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/health/max30102.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c index 3b6213af79ea..0123347bed58 100644 --- a/drivers/iio/health/max30102.c +++ b/drivers/iio/health/max30102.c @@ -32,6 +32,7 @@ #define MAX30102_REGMAP_NAME "max30102_regmap" #define MAX30102_DRV_NAME "max30102" +#define MAX30102_PART_NUMBER 0x15 #define MAX30102_REG_INT_STATUS 0x00 #define MAX30102_REG_INT_STATUS_PWR_RDY BIT(0) @@ -83,6 +84,9 @@ #define MAX30102_REG_TEMP_INTEGER 0x1f #define MAX30102_REG_TEMP_FRACTION 0x20 +#define MAX30102_REG_REV_ID 0xfe +#define MAX30102_REG_PART_ID 0xff + struct max30102_data { struct i2c_client *client; struct iio_dev *indio_dev; @@ -391,6 +395,7 @@ static int max30102_probe(struct i2c_client *client, struct iio_buffer *buffer; struct iio_dev *indio_dev; int ret; + unsigned int reg; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) @@ -424,6 +429,19 @@ static int max30102_probe(struct i2c_client *client, return PTR_ERR(data->regmap); } + /* check part ID */ + ret = regmap_read(data->regmap, MAX30102_REG_PART_ID, ®); + if (ret) + return ret; + if (reg != MAX30102_PART_NUMBER) + return -ENODEV; + + /* show revision ID */ + ret = regmap_read(data->regmap, MAX30102_REG_REV_ID, ®); + if (ret) + return ret; + dev_dbg(&client->dev, "max3010x revision %02x\n", reg); + ret = max30102_set_powermode(data, false); if (ret) return ret; From 839a74cde7ee6da20ee908a570c0e88970ac13c9 Mon Sep 17 00:00:00 2001 From: Peter Meerwald-Stadler Date: Fri, 27 Oct 2017 21:45:37 +0200 Subject: [PATCH 27/44] iio: health: max30102: Introduce intensity channel macro Signed-off-by: Peter Meerwald-Stalder Acked-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/health/max30102.c | 44 +++++++++++++---------------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c index 0123347bed58..6c395c8c2326 100644 --- a/drivers/iio/health/max30102.c +++ b/drivers/iio/health/max30102.c @@ -106,35 +106,23 @@ static const struct regmap_config max30102_regmap_config = { static const unsigned long max30102_scan_masks[] = {0x3, 0}; +#define MAX30102_INTENSITY_CHANNEL(_si, _mod) { \ + .type = IIO_INTENSITY, \ + .channel2 = _mod, \ + .modified = 1, \ + .scan_index = _si, \ + .scan_type = { \ + .sign = 'u', \ + .shift = 8, \ + .realbits = 18, \ + .storagebits = 32, \ + .endianness = IIO_BE, \ + }, \ + } + static const struct iio_chan_spec max30102_channels[] = { - { - .type = IIO_INTENSITY, - .channel2 = IIO_MOD_LIGHT_RED, - .modified = 1, - - .scan_index = 0, - .scan_type = { - .sign = 'u', - .shift = 8, - .realbits = 18, - .storagebits = 32, - .endianness = IIO_BE, - }, - }, - { - .type = IIO_INTENSITY, - .channel2 = IIO_MOD_LIGHT_IR, - .modified = 1, - - .scan_index = 1, - .scan_type = { - .sign = 'u', - .shift = 8, - .realbits = 18, - .storagebits = 32, - .endianness = IIO_BE, - }, - }, + MAX30102_INTENSITY_CHANNEL(0, IIO_MOD_LIGHT_RED), + MAX30102_INTENSITY_CHANNEL(1, IIO_MOD_LIGHT_IR), { .type = IIO_TEMP, .info_mask_separate = From a9c47abbdd71dceeaf1b923e5ce10e700e036905 Mon Sep 17 00:00:00 2001 From: Peter Meerwald-Stadler Date: Fri, 27 Oct 2017 21:45:38 +0200 Subject: [PATCH 28/44] iio: health: max30102: Add power enable parameter to get_temp function Chip must not be in shutdown for reading temperature, so briefly leave shutdown if buffer is not already running Signed-off-by: Peter Meerwald-Stadler Acked-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/health/max30102.c | 36 ++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c index 6c395c8c2326..dc4d04b5f291 100644 --- a/drivers/iio/health/max30102.c +++ b/drivers/iio/health/max30102.c @@ -321,20 +321,31 @@ static int max30102_read_temp(struct max30102_data *data, int *val) return 0; } -static int max30102_get_temp(struct max30102_data *data, int *val) +static int max30102_get_temp(struct max30102_data *data, int *val, bool en) { int ret; + if (en) { + ret = max30102_set_powermode(data, true); + if (ret) + return ret; + } + /* start acquisition */ ret = regmap_update_bits(data->regmap, MAX30102_REG_TEMP_CONFIG, MAX30102_REG_TEMP_CONFIG_TEMP_EN, MAX30102_REG_TEMP_CONFIG_TEMP_EN); if (ret) - return ret; + goto out; msleep(35); + ret = max30102_read_temp(data, val); - return max30102_read_temp(data, val); +out: + if (en) + max30102_set_powermode(data, false); + + return ret; } static int max30102_read_raw(struct iio_dev *indio_dev, @@ -347,20 +358,19 @@ static int max30102_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: /* - * Temperature reading can only be acquired while engine - * is running + * Temperature reading can only be acquired when not in + * shutdown; leave shutdown briefly when buffer not running */ mutex_lock(&indio_dev->mlock); - if (!iio_buffer_enabled(indio_dev)) - ret = -EBUSY; - else { - ret = max30102_get_temp(data, val); - if (!ret) - ret = IIO_VAL_INT; - } - + ret = max30102_get_temp(data, val, true); + else + ret = max30102_get_temp(data, val, false); mutex_unlock(&indio_dev->mlock); + if (ret) + return ret; + + ret = IIO_VAL_INT; break; case IIO_CHAN_INFO_SCALE: *val = 1; /* 0.0625 */ From ad90e570e2094537b8eb797fca65c492a7397e32 Mon Sep 17 00:00:00 2001 From: Peter Meerwald-Stadler Date: Fri, 27 Oct 2017 21:45:39 +0200 Subject: [PATCH 29/44] iio: health: max30102: Introduce indices for LED channels Signed-off-by: Peter Meerwald-Stadler Acked-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/health/max30102.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c index dc4d04b5f291..c224f0e7348d 100644 --- a/drivers/iio/health/max30102.c +++ b/drivers/iio/health/max30102.c @@ -34,6 +34,11 @@ #define MAX30102_DRV_NAME "max30102" #define MAX30102_PART_NUMBER 0x15 +enum max3012_led_idx { + MAX30102_LED_RED, + MAX30102_LED_IR, +}; + #define MAX30102_REG_INT_STATUS 0x00 #define MAX30102_REG_INT_STATUS_PWR_RDY BIT(0) #define MAX30102_REG_INT_STATUS_PROX_INT BIT(4) @@ -104,7 +109,10 @@ static const struct regmap_config max30102_regmap_config = { .val_bits = 8, }; -static const unsigned long max30102_scan_masks[] = {0x3, 0}; +static const unsigned long max30102_scan_masks[] = { + BIT(MAX30102_LED_RED) | BIT(MAX30102_LED_IR), + 0 +}; #define MAX30102_INTENSITY_CHANNEL(_si, _mod) { \ .type = IIO_INTENSITY, \ @@ -121,8 +129,8 @@ static const unsigned long max30102_scan_masks[] = {0x3, 0}; } static const struct iio_chan_spec max30102_channels[] = { - MAX30102_INTENSITY_CHANNEL(0, IIO_MOD_LIGHT_RED), - MAX30102_INTENSITY_CHANNEL(1, IIO_MOD_LIGHT_IR), + MAX30102_INTENSITY_CHANNEL(MAX30102_LED_RED, IIO_MOD_LIGHT_RED), + MAX30102_INTENSITY_CHANNEL(MAX30102_LED_IR, IIO_MOD_LIGHT_IR), { .type = IIO_TEMP, .info_mask_separate = From 83e6415d565f6ddfa12a58c3f854c4504bdaa7f9 Mon Sep 17 00:00:00 2001 From: Peter Meerwald-Stadler Date: Fri, 27 Oct 2017 21:45:40 +0200 Subject: [PATCH 30/44] iio: health: max30102: Move mode setting to buffer_postenable Move the programming of the mode setting from init() to buffer_postenable() Split out a separate function to only update the power/shutdown bit This changes permits to more easily implement different modes of measurements in further patches Signed-off-by: Peter Meerwald-Stadler Acked-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/health/max30102.c | 44 ++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c index c224f0e7348d..da3b100c9ca2 100644 --- a/drivers/iio/health/max30102.c +++ b/drivers/iio/health/max30102.c @@ -66,6 +66,7 @@ enum max3012_led_idx { #define MAX30102_REG_FIFO_CONFIG_AFULL BIT(0) #define MAX30102_REG_MODE_CONFIG 0x09 +#define MAX30102_REG_MODE_CONFIG_MODE_NONE 0x00 #define MAX30102_REG_MODE_CONFIG_MODE_HR 0x02 /* red LED */ #define MAX30102_REG_MODE_CONFIG_MODE_HR_SPO2 0x03 /* red + IR LED */ #define MAX30102_REG_MODE_CONFIG_MODE_MULTI 0x07 /* multi-LED mode */ @@ -139,25 +140,41 @@ static const struct iio_chan_spec max30102_channels[] = { }, }; -static int max30102_set_powermode(struct max30102_data *data, bool state) +static int max30102_set_power(struct max30102_data *data, bool en) { return regmap_update_bits(data->regmap, MAX30102_REG_MODE_CONFIG, MAX30102_REG_MODE_CONFIG_PWR, - state ? 0 : MAX30102_REG_MODE_CONFIG_PWR); + en ? 0 : MAX30102_REG_MODE_CONFIG_PWR); +} + +static int max30102_set_powermode(struct max30102_data *data, u8 mode, bool en) +{ + u8 reg = mode; + + if (!en) + reg |= MAX30102_REG_MODE_CONFIG_PWR; + + return regmap_update_bits(data->regmap, MAX30102_REG_MODE_CONFIG, + MAX30102_REG_MODE_CONFIG_PWR | + MAX30102_REG_MODE_CONFIG_MODE_MASK, reg); } static int max30102_buffer_postenable(struct iio_dev *indio_dev) { struct max30102_data *data = iio_priv(indio_dev); + u8 reg; - return max30102_set_powermode(data, true); + reg = MAX30102_REG_MODE_CONFIG_MODE_HR_SPO2; + + return max30102_set_powermode(data, reg, true); } static int max30102_buffer_predisable(struct iio_dev *indio_dev) { struct max30102_data *data = iio_priv(indio_dev); - return max30102_set_powermode(data, false); + return max30102_set_powermode(data, MAX30102_REG_MODE_CONFIG_MODE_NONE, + false); } static const struct iio_buffer_setup_ops max30102_buffer_setup_ops = { @@ -278,7 +295,7 @@ static int max30102_chip_init(struct max30102_data *data) if (ret) return ret; - /* enable 18-bit HR + SPO2 readings at 400Hz */ + /* configure 18-bit HR + SpO2 readings at 400Hz */ ret = regmap_write(data->regmap, MAX30102_REG_SPO2_CONFIG, (MAX30102_REG_SPO2_CONFIG_ADC_4096_STEPS << MAX30102_REG_SPO2_CONFIG_ADC_MASK_SHIFT) | @@ -288,13 +305,6 @@ static int max30102_chip_init(struct max30102_data *data) if (ret) return ret; - /* enable HR + SPO2 mode */ - ret = regmap_update_bits(data->regmap, MAX30102_REG_MODE_CONFIG, - MAX30102_REG_MODE_CONFIG_MODE_MASK, - MAX30102_REG_MODE_CONFIG_MODE_HR_SPO2); - if (ret) - return ret; - /* average 4 samples + generate FIFO interrupt */ ret = regmap_write(data->regmap, MAX30102_REG_FIFO_CONFIG, (MAX30102_REG_FIFO_CONFIG_AVG_4SAMPLES @@ -334,7 +344,7 @@ static int max30102_get_temp(struct max30102_data *data, int *val, bool en) int ret; if (en) { - ret = max30102_set_powermode(data, true); + ret = max30102_set_power(data, true); if (ret) return ret; } @@ -351,7 +361,7 @@ static int max30102_get_temp(struct max30102_data *data, int *val, bool en) out: if (en) - max30102_set_powermode(data, false); + max30102_set_power(data, false); return ret; } @@ -448,7 +458,9 @@ static int max30102_probe(struct i2c_client *client, return ret; dev_dbg(&client->dev, "max3010x revision %02x\n", reg); - ret = max30102_set_powermode(data, false); + /* clear mode setting, chip shutdown */ + ret = max30102_set_powermode(data, MAX30102_REG_MODE_CONFIG_MODE_NONE, + false); if (ret) return ret; @@ -479,7 +491,7 @@ static int max30102_remove(struct i2c_client *client) struct max30102_data *data = iio_priv(indio_dev); iio_device_unregister(indio_dev); - max30102_set_powermode(data, false); + max30102_set_power(data, false); return 0; } From fef79edc174dbe6002c5b46a005d0dc8f5e821ba Mon Sep 17 00:00:00 2001 From: Peter Meerwald-Stadler Date: Fri, 27 Oct 2017 21:45:41 +0200 Subject: [PATCH 31/44] iio: health: max30102: Prepare for copying varying number of measurements Current code assumes always 2 measurements (6 bytes) have to be copied, prepare for more flexibility Signed-off-by: Peter Meerwald-Stadler Acked-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/health/max30102.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c index da3b100c9ca2..37ddbada940d 100644 --- a/drivers/iio/health/max30102.c +++ b/drivers/iio/health/max30102.c @@ -58,7 +58,7 @@ enum max3012_led_idx { #define MAX30102_REG_FIFO_OVR_CTR 0x05 #define MAX30102_REG_FIFO_RD_PTR 0x06 #define MAX30102_REG_FIFO_DATA 0x07 -#define MAX30102_REG_FIFO_DATA_ENTRY_LEN 6 +#define MAX30102_REG_FIFO_DATA_BYTES 3 #define MAX30102_REG_FIFO_CONFIG 0x08 #define MAX30102_REG_FIFO_CONFIG_AVG_4SAMPLES BIT(1) @@ -198,6 +198,11 @@ static inline int max30102_fifo_count(struct max30102_data *data) return 0; } +#define MAX30102_COPY_DATA(i) \ + memcpy(&data->processed_buffer[(i)], \ + &buffer[(i) * MAX30102_REG_FIFO_DATA_BYTES], \ + MAX30102_REG_FIFO_DATA_BYTES) + static int max30102_read_measurement(struct max30102_data *data) { int ret; @@ -205,13 +210,13 @@ static int max30102_read_measurement(struct max30102_data *data) ret = i2c_smbus_read_i2c_block_data(data->client, MAX30102_REG_FIFO_DATA, - MAX30102_REG_FIFO_DATA_ENTRY_LEN, + 2 * MAX30102_REG_FIFO_DATA_BYTES, buffer); - memcpy(&data->processed_buffer[0], &buffer[0], 3); - memcpy(&data->processed_buffer[1], &buffer[3], 3); + MAX30102_COPY_DATA(0); + MAX30102_COPY_DATA(1); - return (ret == MAX30102_REG_FIFO_DATA_ENTRY_LEN) ? 0 : -EINVAL; + return (ret == 2 * MAX30102_REG_FIFO_DATA_BYTES) ? 0 : -EINVAL; } static irqreturn_t max30102_interrupt_handler(int irq, void *private) From 90579b69e94bf11ea0745ff003e82a6ce9725f96 Mon Sep 17 00:00:00 2001 From: Peter Meerwald-Stadler Date: Fri, 27 Oct 2017 21:45:42 +0200 Subject: [PATCH 32/44] iio: health: max30102: Add MAX30105 support The Maxim MAX30105 part adds a third LED (green) and uses a multi-LED measuring mode producing three measurements Signed-off-by: Peter Meerwald-Stadler Acked-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/health/max30102.c | 144 ++++++++++++++++++++++++++++++---- 1 file changed, 130 insertions(+), 14 deletions(-) diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c index 37ddbada940d..490dfbaf35ac 100644 --- a/drivers/iio/health/max30102.c +++ b/drivers/iio/health/max30102.c @@ -3,6 +3,9 @@ * * Copyright (C) 2017 Matt Ranostay * + * Support for MAX30105 optical particle sensor + * Copyright (C) 2017 Peter Meerwald-Stadler + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -13,6 +16,7 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * + * 7-bit I2C chip address: 0x57 * TODO: proximity power saving feature */ @@ -34,9 +38,15 @@ #define MAX30102_DRV_NAME "max30102" #define MAX30102_PART_NUMBER 0x15 +enum max30102_chip_id { + max30102, + max30105, +}; + enum max3012_led_idx { MAX30102_LED_RED, MAX30102_LED_IR, + MAX30105_LED_GREEN, }; #define MAX30102_REG_INT_STATUS 0x00 @@ -73,6 +83,11 @@ enum max3012_led_idx { #define MAX30102_REG_MODE_CONFIG_MODE_MASK GENMASK(2, 0) #define MAX30102_REG_MODE_CONFIG_PWR BIT(7) +#define MAX30102_REG_MODE_CONTROL_SLOT21 0x11 /* multi-LED control */ +#define MAX30102_REG_MODE_CONTROL_SLOT43 0x12 +#define MAX30102_REG_MODE_CONTROL_SLOT_MASK (GENMASK(6, 4) | GENMASK(2, 0)) +#define MAX30102_REG_MODE_CONTROL_SLOT_SHIFT 4 + #define MAX30102_REG_SPO2_CONFIG 0x0a #define MAX30102_REG_SPO2_CONFIG_PULSE_411_US 0x03 #define MAX30102_REG_SPO2_CONFIG_SR_400HZ 0x03 @@ -83,6 +98,7 @@ enum max3012_led_idx { #define MAX30102_REG_RED_LED_CONFIG 0x0c #define MAX30102_REG_IR_LED_CONFIG 0x0d +#define MAX30105_REG_GREEN_LED_CONFIG 0x0e #define MAX30102_REG_TEMP_CONFIG 0x21 #define MAX30102_REG_TEMP_CONFIG_TEMP_EN BIT(0) @@ -98,9 +114,10 @@ struct max30102_data { struct iio_dev *indio_dev; struct mutex lock; struct regmap *regmap; + enum max30102_chip_id chip_id; - u8 buffer[8]; - __be32 processed_buffer[2]; /* 2 x 18-bit (padded to 32-bits) */ + u8 buffer[12]; + __be32 processed_buffer[3]; /* 3 x 18-bit (padded to 32-bits) */ }; static const struct regmap_config max30102_regmap_config = { @@ -115,6 +132,13 @@ static const unsigned long max30102_scan_masks[] = { 0 }; +static const unsigned long max30105_scan_masks[] = { + BIT(MAX30102_LED_RED) | BIT(MAX30102_LED_IR), + BIT(MAX30102_LED_RED) | BIT(MAX30102_LED_IR) | + BIT(MAX30105_LED_GREEN), + 0 +}; + #define MAX30102_INTENSITY_CHANNEL(_si, _mod) { \ .type = IIO_INTENSITY, \ .channel2 = _mod, \ @@ -140,6 +164,18 @@ static const struct iio_chan_spec max30102_channels[] = { }, }; +static const struct iio_chan_spec max30105_channels[] = { + MAX30102_INTENSITY_CHANNEL(MAX30102_LED_RED, IIO_MOD_LIGHT_RED), + MAX30102_INTENSITY_CHANNEL(MAX30102_LED_IR, IIO_MOD_LIGHT_IR), + MAX30102_INTENSITY_CHANNEL(MAX30105_LED_GREEN, IIO_MOD_LIGHT_GREEN), + { + .type = IIO_TEMP, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = -1, + }, +}; + static int max30102_set_power(struct max30102_data *data, bool en) { return regmap_update_bits(data->regmap, MAX30102_REG_MODE_CONFIG, @@ -159,12 +195,40 @@ static int max30102_set_powermode(struct max30102_data *data, u8 mode, bool en) MAX30102_REG_MODE_CONFIG_MODE_MASK, reg); } +#define MAX30102_MODE_CONTROL_LED_SLOTS(slot2, slot1) \ + ((slot2 << MAX30102_REG_MODE_CONTROL_SLOT_SHIFT) | slot1) + static int max30102_buffer_postenable(struct iio_dev *indio_dev) { struct max30102_data *data = iio_priv(indio_dev); + int ret; u8 reg; - reg = MAX30102_REG_MODE_CONFIG_MODE_HR_SPO2; + switch (*indio_dev->active_scan_mask) { + case BIT(MAX30102_LED_RED) | BIT(MAX30102_LED_IR): + reg = MAX30102_REG_MODE_CONFIG_MODE_HR_SPO2; + break; + case BIT(MAX30102_LED_RED) | BIT(MAX30102_LED_IR) | + BIT(MAX30105_LED_GREEN): + ret = regmap_update_bits(data->regmap, + MAX30102_REG_MODE_CONTROL_SLOT21, + MAX30102_REG_MODE_CONTROL_SLOT_MASK, + MAX30102_MODE_CONTROL_LED_SLOTS(2, 1)); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, + MAX30102_REG_MODE_CONTROL_SLOT43, + MAX30102_REG_MODE_CONTROL_SLOT_MASK, + MAX30102_MODE_CONTROL_LED_SLOTS(0, 3)); + if (ret) + return ret; + + reg = MAX30102_REG_MODE_CONFIG_MODE_MULTI; + break; + default: + return -EINVAL; + } return max30102_set_powermode(data, reg, true); } @@ -203,32 +267,46 @@ static inline int max30102_fifo_count(struct max30102_data *data) &buffer[(i) * MAX30102_REG_FIFO_DATA_BYTES], \ MAX30102_REG_FIFO_DATA_BYTES) -static int max30102_read_measurement(struct max30102_data *data) +static int max30102_read_measurement(struct max30102_data *data, + unsigned int measurements) { int ret; u8 *buffer = (u8 *) &data->buffer; ret = i2c_smbus_read_i2c_block_data(data->client, MAX30102_REG_FIFO_DATA, - 2 * MAX30102_REG_FIFO_DATA_BYTES, + measurements * + MAX30102_REG_FIFO_DATA_BYTES, buffer); - MAX30102_COPY_DATA(0); - MAX30102_COPY_DATA(1); + switch (measurements) { + case 3: + MAX30102_COPY_DATA(2); + case 2: /* fall-through */ + MAX30102_COPY_DATA(1); + case 1: /* fall-through */ + MAX30102_COPY_DATA(0); + break; + default: + return -EINVAL; + } - return (ret == 2 * MAX30102_REG_FIFO_DATA_BYTES) ? 0 : -EINVAL; + return (ret == measurements * MAX30102_REG_FIFO_DATA_BYTES) ? + 0 : -EINVAL; } static irqreturn_t max30102_interrupt_handler(int irq, void *private) { struct iio_dev *indio_dev = private; struct max30102_data *data = iio_priv(indio_dev); + unsigned int measurements = bitmap_weight(indio_dev->active_scan_mask, + indio_dev->masklength); int ret, cnt = 0; mutex_lock(&data->lock); while (cnt || (cnt = max30102_fifo_count(data)) > 0) { - ret = max30102_read_measurement(data); + ret = max30102_read_measurement(data, measurements); if (ret) break; @@ -274,6 +352,29 @@ static int max30102_led_init(struct max30102_data *data) if (ret) return ret; + if (data->chip_id == max30105) { + ret = of_property_read_u32(np, + "maxim,green-led-current-microamp", &val); + if (ret) { + dev_info(dev, "no green-led-current-microamp set\n"); + + /* Default to 7 mA green LED */ + val = 7000; + } + + ret = max30102_get_current_idx(val, ®); + if (ret) { + dev_err(dev, "invalid green LED current setting %d\n", + val); + return ret; + } + + ret = regmap_write(data->regmap, MAX30105_REG_GREEN_LED_CONFIG, + reg); + if (ret) + return ret; + } + ret = of_property_read_u32(np, "maxim,ir-led-current-microamp", &val); if (ret) { dev_info(dev, "no ir-led-current-microamp set\n"); @@ -429,10 +530,7 @@ static int max30102_probe(struct i2c_client *client, iio_device_attach_buffer(indio_dev, buffer); indio_dev->name = MAX30102_DRV_NAME; - indio_dev->channels = max30102_channels; indio_dev->info = &max30102_info; - indio_dev->num_channels = ARRAY_SIZE(max30102_channels); - indio_dev->available_scan_masks = max30102_scan_masks; indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE); indio_dev->setup_ops = &max30102_buffer_setup_ops; indio_dev->dev.parent = &client->dev; @@ -440,10 +538,26 @@ static int max30102_probe(struct i2c_client *client, data = iio_priv(indio_dev); data->indio_dev = indio_dev; data->client = client; + data->chip_id = id->driver_data; mutex_init(&data->lock); i2c_set_clientdata(client, indio_dev); + switch (data->chip_id) { + case max30105: + indio_dev->channels = max30105_channels; + indio_dev->num_channels = ARRAY_SIZE(max30105_channels); + indio_dev->available_scan_masks = max30105_scan_masks; + break; + case max30102: + indio_dev->channels = max30102_channels; + indio_dev->num_channels = ARRAY_SIZE(max30102_channels); + indio_dev->available_scan_masks = max30102_scan_masks; + break; + default: + return -ENODEV; + } + data->regmap = devm_regmap_init_i2c(client, &max30102_regmap_config); if (IS_ERR(data->regmap)) { dev_err(&client->dev, "regmap initialization failed\n"); @@ -502,13 +616,15 @@ static int max30102_remove(struct i2c_client *client) } static const struct i2c_device_id max30102_id[] = { - { "max30102", 0 }, + { "max30102", max30102 }, + { "max30105", max30105 }, {} }; MODULE_DEVICE_TABLE(i2c, max30102_id); static const struct of_device_id max30102_dt_ids[] = { { .compatible = "maxim,max30102" }, + { .compatible = "maxim,max30105" }, { } }; MODULE_DEVICE_TABLE(of, max30102_dt_ids); @@ -525,5 +641,5 @@ static struct i2c_driver max30102_driver = { module_i2c_driver(max30102_driver); MODULE_AUTHOR("Matt Ranostay "); -MODULE_DESCRIPTION("MAX30102 heart rate and pulse oximeter sensor"); +MODULE_DESCRIPTION("MAX30102 heart rate/pulse oximeter and MAX30105 particle sensor driver"); MODULE_LICENSE("GPL"); From 02c5dff52b3e1eb38513699e17cdf973a5a40322 Mon Sep 17 00:00:00 2001 From: Peter Meerwald-Stadler Date: Fri, 27 Oct 2017 21:45:43 +0200 Subject: [PATCH 33/44] dt-bindings: iio: health: Add MAX30105 support to max30102.txt Signed-off-by: Peter Meerwald-Stadler Acked-by: Matt Ranostay Cc: Rob Herring Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/health/max30102.txt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/health/max30102.txt b/Documentation/devicetree/bindings/iio/health/max30102.txt index 8629c18b0e78..ef2ca0a0306f 100644 --- a/Documentation/devicetree/bindings/iio/health/max30102.txt +++ b/Documentation/devicetree/bindings/iio/health/max30102.txt @@ -1,9 +1,11 @@ Maxim MAX30102 heart rate and pulse oximeter sensor +Maxim MAX30105 optical particle-sensing module * https://datasheets.maximintegrated.com/en/ds/MAX30102.pdf +* https://datasheets.maximintegrated.com/en/ds/MAX30105.pdf Required properties: - - compatible: must be "maxim,max30102" + - compatible: must be "maxim,max30102" or "maxim,max30105" - reg: the I2C address of the sensor - interrupt-parent: should be the phandle for the interrupt controller - interrupts: the sole interrupt generated by the device @@ -12,8 +14,10 @@ Required properties: interrupt client node bindings. Optional properties: - - maxim,red-led-current-microamp: configuration for RED LED current + - maxim,red-led-current-microamp: configuration for red LED current - maxim,ir-led-current-microamp: configuration for IR LED current + - maxim,green-led-current-microamp: configuration for green LED current + (max30105 only) Note that each step is approximately 200 microamps, ranging from 0 uA to 50800 uA. From edf7550a1f93a88be2bf743b5d352b278d1b789c Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Tue, 31 Oct 2017 12:42:03 +1030 Subject: [PATCH 34/44] iio: adc: aspeed: Deassert reset in probe The ASPEED SoC must deassert a reset in order to use the ADC peripheral. The device tree bindings are updated to document the resets phandle, and the example is updated to match what is expected for both the reset and clock phandle. Note that the bindings should have always had the reset controller, as the hardware is unusable without it. Signed-off-by: Joel Stanley Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/aspeed_adc.txt | 4 ++- drivers/iio/adc/aspeed_adc.c | 25 ++++++++++++++----- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/adc/aspeed_adc.txt b/Documentation/devicetree/bindings/iio/adc/aspeed_adc.txt index 674e133b7cd7..034fc2ba100e 100644 --- a/Documentation/devicetree/bindings/iio/adc/aspeed_adc.txt +++ b/Documentation/devicetree/bindings/iio/adc/aspeed_adc.txt @@ -8,6 +8,7 @@ Required properties: - reg: memory window mapping address and length - clocks: Input clock used to derive the sample clock. Expected to be the SoC's APB clock. +- resets: Reset controller phandle - #io-channel-cells: Must be set to <1> to indicate channels are selected by index. @@ -15,6 +16,7 @@ Example: adc@1e6e9000 { compatible = "aspeed,ast2400-adc"; reg = <0x1e6e9000 0xb0>; - clocks = <&clk_apb>; + clocks = <&syscon ASPEED_CLK_APB>; + resets = <&syscon ASPEED_RESET_ADC>; #io-channel-cells = <1>; }; diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 8a958d5f1905..327a49ba1991 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -53,11 +54,12 @@ struct aspeed_adc_model_data { }; struct aspeed_adc_data { - struct device *dev; - void __iomem *base; - spinlock_t clk_lock; - struct clk_hw *clk_prescaler; - struct clk_hw *clk_scaler; + struct device *dev; + void __iomem *base; + spinlock_t clk_lock; + struct clk_hw *clk_prescaler; + struct clk_hw *clk_scaler; + struct reset_control *rst; }; #define ASPEED_CHAN(_idx, _data_reg_addr) { \ @@ -217,6 +219,15 @@ static int aspeed_adc_probe(struct platform_device *pdev) goto scaler_error; } + data->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(data->rst)) { + dev_err(&pdev->dev, + "invalid or missing reset controller device tree entry"); + ret = PTR_ERR(data->rst); + goto reset_error; + } + reset_control_deassert(data->rst); + model_data = of_device_get_match_data(&pdev->dev); if (model_data->wait_init_sequence) { @@ -263,9 +274,10 @@ iio_register_error: writel(ASPEED_OPERATION_MODE_POWER_DOWN, data->base + ASPEED_REG_ENGINE_CONTROL); clk_disable_unprepare(data->clk_scaler->clk); +reset_error: + reset_control_assert(data->rst); clk_enable_error: clk_hw_unregister_divider(data->clk_scaler); - scaler_error: clk_hw_unregister_divider(data->clk_prescaler); return ret; @@ -280,6 +292,7 @@ static int aspeed_adc_remove(struct platform_device *pdev) writel(ASPEED_OPERATION_MODE_POWER_DOWN, data->base + ASPEED_REG_ENGINE_CONTROL); clk_disable_unprepare(data->clk_scaler->clk); + reset_control_assert(data->rst); clk_hw_unregister_divider(data->clk_scaler); clk_hw_unregister_divider(data->clk_prescaler); From f4457f7b1747b754fb916ef3c02d82e913df8da5 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sat, 25 Nov 2017 10:27:19 +0100 Subject: [PATCH 35/44] MAINTAINERS: Add missing IIO ABI entries Cc: Lars-Peter Clausen Cc: Michael Hennerich Cc: Andreas Klinger Cc: Hartmut Knaack Cc: Peter Meerwald-Stadler Signed-off-by: Lukas Wunner Acked-by: William Breathitt Gray Signed-off-by: Jonathan Cameron --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 630a19e2b2b4..9cd4e4e801e4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -262,6 +262,7 @@ ACCES 104-QUAD-8 IIO DRIVER M: William Breathitt Gray L: linux-iio@vger.kernel.org S: Maintained +F: Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8 F: drivers/iio/counter/104-quad-8.c ACCES PCI-IDIO-16 GPIO DRIVER @@ -845,6 +846,8 @@ M: Michael Hennerich W: http://wiki.analog.com/ W: http://ez.analog.com/community/linux-device-drivers S: Supported +F: Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523 +F: Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 F: drivers/iio/*/ad* F: drivers/iio/adc/ltc2497* X: drivers/iio/*/adjd* @@ -4126,6 +4129,7 @@ DEVANTECH SRF ULTRASONIC RANGER IIO DRIVER M: Andreas Klinger L: linux-iio@vger.kernel.org S: Maintained +F: Documentation/ABI/testing/sysfs-bus-iio-distance-srf08 F: drivers/iio/proximity/srf*.c DEVICE COREDUMP (DEV_COREDUMP) @@ -6829,6 +6833,8 @@ R: Peter Meerwald-Stadler L: linux-iio@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git S: Maintained +F: Documentation/ABI/testing/configfs-iio* +F: Documentation/ABI/testing/sysfs-bus-iio* F: Documentation/devicetree/bindings/iio/ F: drivers/iio/ F: drivers/staging/iio/ From 3025c8688c1e9768fe272a021a039f5840012175 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 23 Nov 2017 23:59:05 +0100 Subject: [PATCH 36/44] iio: light: add support for UVIS25 sensor add support for STMicroelectronics UVIS25 uv sensor http://www.st.com/resource/en/datasheet/uvis25.pdf - continuos mode support - i2c support - spi support - trigger mode support - system PM support Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/light/Kconfig | 24 ++ drivers/iio/light/Makefile | 3 + drivers/iio/light/st_uvis25.h | 37 +++ drivers/iio/light/st_uvis25_core.c | 359 +++++++++++++++++++++++++++++ drivers/iio/light/st_uvis25_i2c.c | 69 ++++++ drivers/iio/light/st_uvis25_spi.c | 68 ++++++ 6 files changed, 560 insertions(+) create mode 100644 drivers/iio/light/st_uvis25.h create mode 100644 drivers/iio/light/st_uvis25_core.c create mode 100644 drivers/iio/light/st_uvis25_i2c.c create mode 100644 drivers/iio/light/st_uvis25_spi.c diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 6a5835fab32e..93fd421b10d7 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -334,6 +334,30 @@ config STK3310 Choosing M will build the driver as a module. If so, the module will be called stk3310. +config ST_UVIS25 + tristate "STMicroelectronics UVIS25 sensor driver" + depends on (I2C || SPI) + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select ST_UVIS25_I2C if (I2C) + select ST_UVIS25_SPI if (SPI_MASTER) + help + Say yes here to build support for STMicroelectronics UVIS25 + uv sensor + + To compile this driver as a module, choose M here: the module + will be called st_uvis25. + +config ST_UVIS25_I2C + tristate + depends on ST_UVIS25 + select REGMAP_I2C + +config ST_UVIS25_SPI + tristate + depends on ST_UVIS25 + select REGMAP_SPI + config TCS3414 tristate "TAOS TCS3414 digital color sensor" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index f0176a800e14..f714067a7816 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -33,6 +33,9 @@ obj-$(CONFIG_RPR0521) += rpr0521.o obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o obj-$(CONFIG_SI1145) += si1145.o obj-$(CONFIG_STK3310) += stk3310.o +obj-$(CONFIG_ST_UVIS25) += st_uvis25_core.o +obj-$(CONFIG_ST_UVIS25_I2C) += st_uvis25_i2c.o +obj-$(CONFIG_ST_UVIS25_SPI) += st_uvis25_spi.o obj-$(CONFIG_TCS3414) += tcs3414.o obj-$(CONFIG_TCS3472) += tcs3472.o obj-$(CONFIG_TSL2583) += tsl2583.o diff --git a/drivers/iio/light/st_uvis25.h b/drivers/iio/light/st_uvis25.h new file mode 100644 index 000000000000..5e970ab480cd --- /dev/null +++ b/drivers/iio/light/st_uvis25.h @@ -0,0 +1,37 @@ +/* + * STMicroelectronics uvis25 sensor driver + * + * Copyright 2017 STMicroelectronics Inc. + * + * Lorenzo Bianconi + * + * Licensed under the GPL-2. + */ + +#ifndef ST_UVIS25_H +#define ST_UVIS25_H + +#define ST_UVIS25_DEV_NAME "uvis25" + +#include + +/** + * struct st_uvis25_hw - ST UVIS25 sensor instance + * @regmap: Register map of the device. + * @trig: The trigger in use by the driver. + * @enabled: Status of the sensor (false->off, true->on). + * @irq: Device interrupt line (I2C or SPI). + */ +struct st_uvis25_hw { + struct regmap *regmap; + + struct iio_trigger *trig; + bool enabled; + int irq; +}; + +extern const struct dev_pm_ops st_uvis25_pm_ops; + +int st_uvis25_probe(struct device *dev, int irq, struct regmap *regmap); + +#endif /* ST_UVIS25_H */ diff --git a/drivers/iio/light/st_uvis25_core.c b/drivers/iio/light/st_uvis25_core.c new file mode 100644 index 000000000000..302635836e6b --- /dev/null +++ b/drivers/iio/light/st_uvis25_core.c @@ -0,0 +1,359 @@ +/* + * STMicroelectronics uvis25 sensor driver + * + * Copyright 2017 STMicroelectronics Inc. + * + * Lorenzo Bianconi + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "st_uvis25.h" + +#define ST_UVIS25_REG_WHOAMI_ADDR 0x0f +#define ST_UVIS25_REG_WHOAMI_VAL 0xca +#define ST_UVIS25_REG_CTRL1_ADDR 0x20 +#define ST_UVIS25_REG_ODR_MASK BIT(0) +#define ST_UVIS25_REG_BDU_MASK BIT(1) +#define ST_UVIS25_REG_CTRL2_ADDR 0x21 +#define ST_UVIS25_REG_BOOT_MASK BIT(7) +#define ST_UVIS25_REG_CTRL3_ADDR 0x22 +#define ST_UVIS25_REG_HL_MASK BIT(7) +#define ST_UVIS25_REG_STATUS_ADDR 0x27 +#define ST_UVIS25_REG_UV_DA_MASK BIT(0) +#define ST_UVIS25_REG_OUT_ADDR 0x28 + +static const struct iio_chan_spec st_uvis25_channels[] = { + { + .type = IIO_UVINDEX, + .address = ST_UVIS25_REG_OUT_ADDR, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 8, + .storagebits = 8, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static int st_uvis25_check_whoami(struct st_uvis25_hw *hw) +{ + int err, data; + + err = regmap_read(hw->regmap, ST_UVIS25_REG_WHOAMI_ADDR, &data); + if (err < 0) { + dev_err(regmap_get_device(hw->regmap), + "failed to read whoami register\n"); + return err; + } + + if (data != ST_UVIS25_REG_WHOAMI_VAL) { + dev_err(regmap_get_device(hw->regmap), + "wrong whoami {%02x vs %02x}\n", + data, ST_UVIS25_REG_WHOAMI_VAL); + return -ENODEV; + } + + return 0; +} + +static int st_uvis25_set_enable(struct st_uvis25_hw *hw, bool enable) +{ + int err; + + err = regmap_update_bits(hw->regmap, ST_UVIS25_REG_CTRL1_ADDR, + ST_UVIS25_REG_ODR_MASK, enable); + if (err < 0) + return err; + + hw->enabled = enable; + + return 0; +} + +static int st_uvis25_read_oneshot(struct st_uvis25_hw *hw, u8 addr, int *val) +{ + int err; + + err = st_uvis25_set_enable(hw, true); + if (err < 0) + return err; + + msleep(1500); + + /* + * in order to avoid possible race conditions with interrupt + * generation, disable the sensor first and then poll output + * register. That sequence guarantees the interrupt will be reset + * when irq line is unmasked + */ + err = st_uvis25_set_enable(hw, false); + if (err < 0) + return err; + + err = regmap_read(hw->regmap, addr, val); + + return err < 0 ? err : IIO_VAL_INT; +} + +static int st_uvis25_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *ch, + int *val, int *val2, long mask) +{ + int ret; + + ret = iio_device_claim_direct_mode(iio_dev); + if (ret) + return ret; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: { + struct st_uvis25_hw *hw = iio_priv(iio_dev); + + /* + * mask irq line during oneshot read since the sensor + * does not export the capability to disable data-ready line + * in the register map and it is enabled by default. + * If the line is unmasked during read_raw() it will be set + * active and never reset since the trigger is disabled + */ + if (hw->irq > 0) + disable_irq(hw->irq); + ret = st_uvis25_read_oneshot(hw, ch->address, val); + if (hw->irq > 0) + enable_irq(hw->irq); + break; + } + default: + ret = -EINVAL; + break; + } + + iio_device_release_direct_mode(iio_dev); + + return ret; +} + +static irqreturn_t st_uvis25_trigger_handler_thread(int irq, void *private) +{ + struct st_uvis25_hw *hw = private; + int err, status; + + err = regmap_read(hw->regmap, ST_UVIS25_REG_STATUS_ADDR, &status); + if (err < 0) + return IRQ_HANDLED; + + if (!(status & ST_UVIS25_REG_UV_DA_MASK)) + return IRQ_NONE; + + iio_trigger_poll_chained(hw->trig); + + return IRQ_HANDLED; +} + +static int st_uvis25_allocate_trigger(struct iio_dev *iio_dev) +{ + struct st_uvis25_hw *hw = iio_priv(iio_dev); + struct device *dev = regmap_get_device(hw->regmap); + bool irq_active_low = false; + unsigned long irq_type; + int err; + + irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq)); + + switch (irq_type) { + 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(dev, "mode %lx unsupported\n", irq_type); + return -EINVAL; + } + + err = regmap_update_bits(hw->regmap, ST_UVIS25_REG_CTRL3_ADDR, + ST_UVIS25_REG_HL_MASK, irq_active_low); + if (err < 0) + return err; + + err = devm_request_threaded_irq(dev, hw->irq, NULL, + st_uvis25_trigger_handler_thread, + irq_type | IRQF_ONESHOT, + iio_dev->name, hw); + if (err) { + dev_err(dev, "failed to request trigger irq %d\n", + hw->irq); + return err; + } + + hw->trig = devm_iio_trigger_alloc(dev, "%s-trigger", + iio_dev->name); + if (!hw->trig) + return -ENOMEM; + + iio_trigger_set_drvdata(hw->trig, iio_dev); + hw->trig->dev.parent = dev; + + return devm_iio_trigger_register(dev, hw->trig); +} + +static int st_uvis25_buffer_preenable(struct iio_dev *iio_dev) +{ + return st_uvis25_set_enable(iio_priv(iio_dev), true); +} + +static int st_uvis25_buffer_postdisable(struct iio_dev *iio_dev) +{ + return st_uvis25_set_enable(iio_priv(iio_dev), false); +} + +static const struct iio_buffer_setup_ops st_uvis25_buffer_ops = { + .preenable = st_uvis25_buffer_preenable, + .postenable = iio_triggered_buffer_postenable, + .predisable = iio_triggered_buffer_predisable, + .postdisable = st_uvis25_buffer_postdisable, +}; + +static irqreturn_t st_uvis25_buffer_handler_thread(int irq, void *p) +{ + u8 buffer[ALIGN(sizeof(u8), sizeof(s64)) + sizeof(s64)]; + struct iio_poll_func *pf = p; + struct iio_dev *iio_dev = pf->indio_dev; + struct st_uvis25_hw *hw = iio_priv(iio_dev); + int err; + + err = regmap_read(hw->regmap, ST_UVIS25_REG_OUT_ADDR, (int *)buffer); + if (err < 0) + goto out; + + iio_push_to_buffers_with_timestamp(iio_dev, buffer, + iio_get_time_ns(iio_dev)); + +out: + iio_trigger_notify_done(hw->trig); + + return IRQ_HANDLED; +} + +static int st_uvis25_allocate_buffer(struct iio_dev *iio_dev) +{ + struct st_uvis25_hw *hw = iio_priv(iio_dev); + + return devm_iio_triggered_buffer_setup(regmap_get_device(hw->regmap), + iio_dev, NULL, + st_uvis25_buffer_handler_thread, + &st_uvis25_buffer_ops); +} + +static const struct iio_info st_uvis25_info = { + .read_raw = st_uvis25_read_raw, +}; + +static int st_uvis25_init_sensor(struct st_uvis25_hw *hw) +{ + int err; + + err = regmap_update_bits(hw->regmap, ST_UVIS25_REG_CTRL2_ADDR, + ST_UVIS25_REG_BOOT_MASK, 1); + if (err < 0) + return err; + + msleep(2000); + + return regmap_update_bits(hw->regmap, ST_UVIS25_REG_CTRL1_ADDR, + ST_UVIS25_REG_BDU_MASK, 1); +} + +int st_uvis25_probe(struct device *dev, int irq, struct regmap *regmap) +{ + struct st_uvis25_hw *hw; + struct iio_dev *iio_dev; + int err; + + iio_dev = devm_iio_device_alloc(dev, sizeof(*hw)); + if (!iio_dev) + return -ENOMEM; + + dev_set_drvdata(dev, (void *)iio_dev); + + hw = iio_priv(iio_dev); + hw->irq = irq; + hw->regmap = regmap; + + err = st_uvis25_check_whoami(hw); + if (err < 0) + return err; + + iio_dev->modes = INDIO_DIRECT_MODE; + iio_dev->dev.parent = dev; + iio_dev->channels = st_uvis25_channels; + iio_dev->num_channels = ARRAY_SIZE(st_uvis25_channels); + iio_dev->name = ST_UVIS25_DEV_NAME; + iio_dev->info = &st_uvis25_info; + + err = st_uvis25_init_sensor(hw); + if (err < 0) + return err; + + if (hw->irq > 0) { + err = st_uvis25_allocate_buffer(iio_dev); + if (err < 0) + return err; + + err = st_uvis25_allocate_trigger(iio_dev); + if (err) + return err; + } + + return devm_iio_device_register(dev, iio_dev); +} +EXPORT_SYMBOL(st_uvis25_probe); + +static int __maybe_unused st_uvis25_suspend(struct device *dev) +{ + struct iio_dev *iio_dev = dev_get_drvdata(dev); + struct st_uvis25_hw *hw = iio_priv(iio_dev); + + return regmap_update_bits(hw->regmap, ST_UVIS25_REG_CTRL1_ADDR, + ST_UVIS25_REG_ODR_MASK, 0); +} + +static int __maybe_unused st_uvis25_resume(struct device *dev) +{ + struct iio_dev *iio_dev = dev_get_drvdata(dev); + struct st_uvis25_hw *hw = iio_priv(iio_dev); + + if (hw->enabled) + return regmap_update_bits(hw->regmap, ST_UVIS25_REG_CTRL1_ADDR, + ST_UVIS25_REG_ODR_MASK, 1); + + return 0; +} + +const struct dev_pm_ops st_uvis25_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(st_uvis25_suspend, st_uvis25_resume) +}; +EXPORT_SYMBOL(st_uvis25_pm_ops); + +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_DESCRIPTION("STMicroelectronics uvis25 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/light/st_uvis25_i2c.c b/drivers/iio/light/st_uvis25_i2c.c new file mode 100644 index 000000000000..c939c0b0ff10 --- /dev/null +++ b/drivers/iio/light/st_uvis25_i2c.c @@ -0,0 +1,69 @@ +/* + * STMicroelectronics uvis25 i2c driver + * + * Copyright 2017 STMicroelectronics Inc. + * + * Lorenzo Bianconi + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include + +#include "st_uvis25.h" + +#define UVIS25_I2C_AUTO_INCREMENT BIT(7) + +const struct regmap_config st_uvis25_i2c_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .write_flag_mask = UVIS25_I2C_AUTO_INCREMENT, + .read_flag_mask = UVIS25_I2C_AUTO_INCREMENT, +}; + +static int st_uvis25_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, &st_uvis25_i2c_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "Failed to register i2c regmap %d\n", + (int)PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + return st_uvis25_probe(&client->dev, client->irq, regmap); +} + +static const struct of_device_id st_uvis25_i2c_of_match[] = { + { .compatible = "st,uvis25", }, + {}, +}; +MODULE_DEVICE_TABLE(of, st_uvis25_i2c_of_match); + +static const struct i2c_device_id st_uvis25_i2c_id_table[] = { + { ST_UVIS25_DEV_NAME }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, st_uvis25_i2c_id_table); + +static struct i2c_driver st_uvis25_driver = { + .driver = { + .name = "st_uvis25_i2c", + .pm = &st_uvis25_pm_ops, + .of_match_table = of_match_ptr(st_uvis25_i2c_of_match), + }, + .probe = st_uvis25_i2c_probe, + .id_table = st_uvis25_i2c_id_table, +}; +module_i2c_driver(st_uvis25_driver); + +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_DESCRIPTION("STMicroelectronics uvis25 i2c driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/light/st_uvis25_spi.c b/drivers/iio/light/st_uvis25_spi.c new file mode 100644 index 000000000000..e697e14e7952 --- /dev/null +++ b/drivers/iio/light/st_uvis25_spi.c @@ -0,0 +1,68 @@ +/* + * STMicroelectronics uvis25 spi driver + * + * Copyright 2017 STMicroelectronics Inc. + * + * Lorenzo Bianconi + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include + +#include "st_uvis25.h" + +#define UVIS25_SENSORS_SPI_READ BIT(7) +#define UVIS25_SPI_AUTO_INCREMENT BIT(6) + +const struct regmap_config st_uvis25_spi_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .read_flag_mask = UVIS25_SENSORS_SPI_READ | UVIS25_SPI_AUTO_INCREMENT, + .write_flag_mask = UVIS25_SPI_AUTO_INCREMENT, +}; + +static int st_uvis25_spi_probe(struct spi_device *spi) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &st_uvis25_spi_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "Failed to register spi regmap %d\n", + (int)PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + return st_uvis25_probe(&spi->dev, spi->irq, regmap); +} + +static const struct of_device_id st_uvis25_spi_of_match[] = { + { .compatible = "st,uvis25", }, + {}, +}; +MODULE_DEVICE_TABLE(of, st_uvis25_spi_of_match); + +static const struct spi_device_id st_uvis25_spi_id_table[] = { + { ST_UVIS25_DEV_NAME }, + {}, +}; +MODULE_DEVICE_TABLE(spi, st_uvis25_spi_id_table); + +static struct spi_driver st_uvis25_driver = { + .driver = { + .name = "st_uvis25_spi", + .pm = &st_uvis25_pm_ops, + .of_match_table = of_match_ptr(st_uvis25_spi_of_match), + }, + .probe = st_uvis25_spi_probe, + .id_table = st_uvis25_spi_id_table, +}; +module_spi_driver(st_uvis25_driver); + +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_DESCRIPTION("STMicroelectronics uvis25 spi driver"); +MODULE_LICENSE("GPL v2"); From eed48a3beba0b1da725c77288091bfa6395d232d Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 23 Nov 2017 23:59:06 +0100 Subject: [PATCH 37/44] dt-bindings: iio: light: add UVIS25 device bindings Acked-by: Rob Herring Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/light/uvis25.txt | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/light/uvis25.txt diff --git a/Documentation/devicetree/bindings/iio/light/uvis25.txt b/Documentation/devicetree/bindings/iio/light/uvis25.txt new file mode 100644 index 000000000000..3041207e3f3c --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/uvis25.txt @@ -0,0 +1,23 @@ +* ST UVIS25 uv sensor + +Required properties: +- compatible: should be "st,uvis25" +- reg: i2c address of the sensor / spi cs line + +Optional properties: +- interrupt-parent: should be the phandle for the interrupt controller +- interrupts: interrupt mapping for IRQ. It should be configured with + flags IRQ_TYPE_LEVEL_HIGH, IRQ_TYPE_EDGE_RISING, IRQ_TYPE_LEVEL_LOW or + IRQ_TYPE_EDGE_FALLING. + + Refer to interrupt-controller/interrupts.txt for generic interrupt + client node bindings. + +Example: + +uvis25@47 { + compatible = "st,uvis25"; + reg = <0x47>; + interrupt-parent = <&gpio0>; + interrupts = <0 IRQ_TYPE_EDGE_RISING>; +}; From 4c42bef0d3a1dd1a04d1f42c079f461cc1b139bc Mon Sep 17 00:00:00 2001 From: Luke Ross Date: Thu, 23 Nov 2017 21:34:57 +0000 Subject: [PATCH 38/44] iio:accel:da280: Linx 820 Windows tablet has a da280 mapped via ACPI This adds an ACPI table to the driver and the ACPI ID of the sensor on the tablet. Signed-off-by: Luke Ross Signed-off-by: Jonathan Cameron --- drivers/iio/accel/da280.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/drivers/iio/accel/da280.c b/drivers/iio/accel/da280.c index 6c214783241c..d4b555203427 100644 --- a/drivers/iio/accel/da280.c +++ b/drivers/iio/accel/da280.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -25,7 +26,7 @@ #define DA280_MODE_ENABLE 0x1e #define DA280_MODE_DISABLE 0x9e -enum { da226, da280 }; +enum da280_chipset { da226, da280 }; /* * a value of + or -4096 corresponds to + or - 1G @@ -91,12 +92,24 @@ static const struct iio_info da280_info = { .read_raw = da280_read_raw, }; +static enum da280_chipset da280_match_acpi_device(struct device *dev) +{ + const struct acpi_device_id *id; + + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return -EINVAL; + + return (enum da280_chipset) id->driver_data; +} + static int da280_probe(struct i2c_client *client, const struct i2c_device_id *id) { int ret; struct iio_dev *indio_dev; struct da280_data *data; + enum da280_chipset chip; ret = i2c_smbus_read_byte_data(client, DA280_REG_CHIP_ID); if (ret != DA280_CHIP_ID) @@ -114,7 +127,14 @@ static int da280_probe(struct i2c_client *client, indio_dev->info = &da280_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = da280_channels; - if (id->driver_data == da226) { + + if (ACPI_HANDLE(&client->dev)) { + chip = da280_match_acpi_device(&client->dev); + } else { + chip = id->driver_data; + } + + if (chip == da226) { indio_dev->name = "da226"; indio_dev->num_channels = 2; } else { @@ -158,6 +178,12 @@ static int da280_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(da280_pm_ops, da280_suspend, da280_resume); +static const struct acpi_device_id da280_acpi_match[] = { + {"MIRAACC", da280}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, da280_acpi_match); + static const struct i2c_device_id da280_i2c_id[] = { { "da226", da226 }, { "da280", da280 }, @@ -168,6 +194,7 @@ MODULE_DEVICE_TABLE(i2c, da280_i2c_id); static struct i2c_driver da280_driver = { .driver = { .name = "da280", + .acpi_match_table = ACPI_PTR(da280_acpi_match), .pm = &da280_pm_ops, }, .probe = da280_probe, From 585ed27d06151f98e39238298f43ee261314ae74 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 22 Nov 2017 14:32:11 +0000 Subject: [PATCH 39/44] iio: accel: st_accel: remove redundant pointer pdata Pointer pdata is being assigned but it is never being used, hence it is redundant and can be removed. Cleans up clang warning: drivers/iio/accel/st_accel_core.c:952:3: warning: Value stored to 'pdata' is never read Signed-off-by: Colin Ian King Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_core.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 460aa58e0159..6fe995cf16a6 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -920,8 +920,6 @@ static const struct iio_trigger_ops st_accel_trigger_ops = { int st_accel_common_probe(struct iio_dev *indio_dev) { struct st_sensor_data *adata = iio_priv(indio_dev); - struct st_sensors_platform_data *pdata = - (struct st_sensors_platform_data *)adata->dev->platform_data; int irq = adata->get_irq_data_ready(indio_dev); int err; @@ -948,9 +946,6 @@ int st_accel_common_probe(struct iio_dev *indio_dev) &adata->sensor_settings->fs.fs_avl[0]; adata->odr = adata->sensor_settings->odr.odr_avl[0].hz; - if (!pdata) - pdata = (struct st_sensors_platform_data *)&default_accel_pdata; - err = st_sensors_init_sensor(indio_dev, adata->dev->platform_data); if (err < 0) goto st_accel_power_off; From 930df4d853a8493f6e203d14a944c226c64750c3 Mon Sep 17 00:00:00 2001 From: Xingyu Chen Date: Tue, 7 Nov 2017 22:10:06 +0800 Subject: [PATCH 40/44] iio: adc: meson-saradc: remove irrelevant clock "sana" The "sana" clock is not used at SAR ADC module in Amlogic Meson SoC, it is irrelevant for the SAR ADC. Signed-off-by: Xingyu Chen Signed-off-by: Yixun Lan Reviewed-by: Martin Blumenstingl Signed-off-by: Jonathan Cameron --- drivers/iio/adc/meson_saradc.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index 9c6932ffc0af..f0b6502a8904 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -231,7 +231,6 @@ struct meson_sar_adc_priv { const struct meson_sar_adc_data *data; struct clk *clkin; struct clk *core_clk; - struct clk *sana_clk; struct clk *adc_sel_clk; struct clk *adc_clk; struct clk_gate clk_gate; @@ -708,12 +707,6 @@ static int meson_sar_adc_hw_enable(struct iio_dev *indio_dev) goto err_core_clk; } - ret = clk_prepare_enable(priv->sana_clk); - if (ret) { - dev_err(indio_dev->dev.parent, "failed to enable sana clk\n"); - goto err_sana_clk; - } - regval = FIELD_PREP(MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, 1); regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, regval); @@ -741,8 +734,6 @@ err_adc_clk: MESON_SAR_ADC_REG3_ADC_EN, 0); regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, MESON_SAR_ADC_REG11_BANDGAP_EN, 0); - clk_disable_unprepare(priv->sana_clk); -err_sana_clk: clk_disable_unprepare(priv->core_clk); err_core_clk: regulator_disable(priv->vref); @@ -768,7 +759,6 @@ static int meson_sar_adc_hw_disable(struct iio_dev *indio_dev) regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, MESON_SAR_ADC_REG11_BANDGAP_EN, 0); - clk_disable_unprepare(priv->sana_clk); clk_disable_unprepare(priv->core_clk); regulator_disable(priv->vref); @@ -961,16 +951,6 @@ static int meson_sar_adc_probe(struct platform_device *pdev) return PTR_ERR(priv->core_clk); } - priv->sana_clk = devm_clk_get(&pdev->dev, "sana"); - if (IS_ERR(priv->sana_clk)) { - if (PTR_ERR(priv->sana_clk) == -ENOENT) { - priv->sana_clk = NULL; - } else { - dev_err(&pdev->dev, "failed to get sana clk\n"); - return PTR_ERR(priv->sana_clk); - } - } - priv->adc_clk = devm_clk_get(&pdev->dev, "adc_clk"); if (IS_ERR(priv->adc_clk)) { if (PTR_ERR(priv->adc_clk) == -ENOENT) { From 461774659add8b73d558c69d532a1a6ff32d627e Mon Sep 17 00:00:00 2001 From: Xingyu Chen Date: Tue, 7 Nov 2017 22:10:20 +0800 Subject: [PATCH 41/44] dt-bindings: iio: adc: update the doc for SAR ADC Update the doc as the SAR ADC modules doesn't require "sana" clock. Singed-off-by: Xingyu Chen Signed-off-by: Yixun Lan Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/amlogic,meson-saradc.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.txt b/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.txt index f413e82c8b83..1e6ee3deb4fa 100644 --- a/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.txt +++ b/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.txt @@ -15,7 +15,6 @@ Required properties: - "clkin" for the reference clock (typically XTAL) - "core" for the SAR ADC core clock optional clocks: - - "sana" for the analog clock - "adc_clk" for the ADC (sampling) clock - "adc_sel" for the ADC (sampling) clock mux - vref-supply: the regulator supply for the ADC reference voltage From 798c3c9b4ccad00fe2639770c4c86d247b9ee846 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Fri, 1 Dec 2017 12:10:58 +0100 Subject: [PATCH 42/44] iio: accel: bmc150: Add OF device ID table The driver doesn't have a struct of_device_id table but supported devices are registered via Device Trees. This is working on the assumption that a I2C device registered via OF will always match a legacy I2C device ID and that the MODALIAS reported will always be of the form i2c:. But this could change in the future so the correct approach is to have an OF device ID table if the devices are registered via OF. The I2C device ID table entries have the .driver_data field set, but they are not used in the driver so weren't set in the OF device table entries. Signed-off-by: Javier Martinez Canillas Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bmc150-accel-i2c.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/iio/accel/bmc150-accel-i2c.c b/drivers/iio/accel/bmc150-accel-i2c.c index f85014fbaa12..8ffc308d5fd0 100644 --- a/drivers/iio/accel/bmc150-accel-i2c.c +++ b/drivers/iio/accel/bmc150-accel-i2c.c @@ -81,9 +81,21 @@ static const struct i2c_device_id bmc150_accel_id[] = { MODULE_DEVICE_TABLE(i2c, bmc150_accel_id); +static const struct of_device_id bmc150_accel_of_match[] = { + { .compatible = "bosch,bmc150_accel" }, + { .compatible = "bosch,bmi055_accel" }, + { .compatible = "bosch,bma255" }, + { .compatible = "bosch,bma250e" }, + { .compatible = "bosch,bma222e" }, + { .compatible = "bosch,bma280" }, + { }, +}; +MODULE_DEVICE_TABLE(of, bmc150_accel_of_match); + static struct i2c_driver bmc150_accel_driver = { .driver = { .name = "bmc150_accel_i2c", + .of_match_table = bmc150_accel_of_match, .acpi_match_table = ACPI_PTR(bmc150_accel_acpi_match), .pm = &bmc150_accel_pm_ops, }, From cccae604868c821754883edee6ced81b22e6b75a Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Thu, 30 Nov 2017 21:13:34 +0530 Subject: [PATCH 43/44] iio: trigger: Fix platform_get_irq's error checking The platform_get_irq() function returns negative if an error occurs. zero or positive number on success. platform_get_irq() error checking for zero is not correct. Signed-off-by: Arvind Yadav Signed-off-by: Jonathan Cameron --- drivers/staging/iio/trigger/iio-trig-bfin-timer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/iio/trigger/iio-trig-bfin-timer.c b/drivers/staging/iio/trigger/iio-trig-bfin-timer.c index d80dcf82eba9..f389f5cca99d 100644 --- a/drivers/staging/iio/trigger/iio-trig-bfin-timer.c +++ b/drivers/staging/iio/trigger/iio-trig-bfin-timer.c @@ -187,9 +187,9 @@ static int iio_bfin_tmr_trigger_probe(struct platform_device *pdev) return -ENOMEM; st->irq = platform_get_irq(pdev, 0); - if (!st->irq) { + if (st->irq < 0) { dev_err(&pdev->dev, "No IRQs specified"); - return -ENODEV; + return st->irq; } ret = iio_bfin_tmr_get_number(st->irq); From 8d05ffd2b8a08668cfea9e64a2991ab8ec9b494e Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Sat, 25 Nov 2017 09:38:17 +0100 Subject: [PATCH 44/44] iio: Add macro to populate struct iio_map array The GPIO core provides a handy GPIO_LOOKUP() macro to populate a struct gpiod_lookup array without having to spell out attribute names (but still avoid breakage when attributes within the struct are rearranged or added). The axp288_adc.c driver uses a similar macro to populate a struct iio_map array. Make it available to others. Cc: Jacob Pan Signed-off-by: Lukas Wunner Acked-by: Chen-Yu Tsai Signed-off-by: Jonathan Cameron --- drivers/iio/adc/axp288_adc.c | 20 ++++++-------------- include/linux/iio/machine.h | 7 +++++++ 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c index 60c9e853dd81..031d568b4972 100644 --- a/drivers/iio/adc/axp288_adc.c +++ b/drivers/iio/adc/axp288_adc.c @@ -92,22 +92,14 @@ static const struct iio_chan_spec axp288_adc_channels[] = { }, }; -#define AXP288_ADC_MAP(_adc_channel_label, _consumer_dev_name, \ - _consumer_channel) \ - { \ - .adc_channel_label = _adc_channel_label, \ - .consumer_dev_name = _consumer_dev_name, \ - .consumer_channel = _consumer_channel, \ - } - /* for consumer drivers */ static struct iio_map axp288_adc_default_maps[] = { - AXP288_ADC_MAP("TS_PIN", "axp288-batt", "axp288-batt-temp"), - AXP288_ADC_MAP("PMIC_TEMP", "axp288-pmic", "axp288-pmic-temp"), - AXP288_ADC_MAP("GPADC", "axp288-gpadc", "axp288-system-temp"), - AXP288_ADC_MAP("BATT_CHG_I", "axp288-chrg", "axp288-chrg-curr"), - AXP288_ADC_MAP("BATT_DISCHRG_I", "axp288-chrg", "axp288-chrg-d-curr"), - AXP288_ADC_MAP("BATT_V", "axp288-batt", "axp288-batt-volt"), + IIO_MAP("TS_PIN", "axp288-batt", "axp288-batt-temp"), + IIO_MAP("PMIC_TEMP", "axp288-pmic", "axp288-pmic-temp"), + IIO_MAP("GPADC", "axp288-gpadc", "axp288-system-temp"), + IIO_MAP("BATT_CHG_I", "axp288-chrg", "axp288-chrg-curr"), + IIO_MAP("BATT_DISCHRG_I", "axp288-chrg", "axp288-chrg-d-curr"), + IIO_MAP("BATT_V", "axp288-batt", "axp288-batt-volt"), {}, }; diff --git a/include/linux/iio/machine.h b/include/linux/iio/machine.h index 1601a2a63a72..5e1cfa75f652 100644 --- a/include/linux/iio/machine.h +++ b/include/linux/iio/machine.h @@ -28,4 +28,11 @@ struct iio_map { void *consumer_data; }; +#define IIO_MAP(_provider_channel, _consumer_dev_name, _consumer_channel) \ +{ \ + .adc_channel_label = _provider_channel, \ + .consumer_dev_name = _consumer_dev_name, \ + .consumer_channel = _consumer_channel, \ +} + #endif