diff options
Diffstat (limited to 'drivers/iio/adc')
-rw-r--r-- | drivers/iio/adc/Kconfig | 40 | ||||
-rw-r--r-- | drivers/iio/adc/Makefile | 3 | ||||
-rw-r--r-- | drivers/iio/adc/aspeed_adc.c | 25 | ||||
-rw-r--r-- | drivers/iio/adc/at91-sama5d2_adc.c | 456 | ||||
-rw-r--r-- | drivers/iio/adc/at91_adc.c | 4 | ||||
-rw-r--r-- | drivers/iio/adc/axp288_adc.c | 20 | ||||
-rw-r--r-- | drivers/iio/adc/hx711.c | 134 | ||||
-rw-r--r-- | drivers/iio/adc/ina2xx-adc.c | 317 | ||||
-rw-r--r-- | drivers/iio/adc/meson_saradc.c | 60 | ||||
-rw-r--r-- | drivers/iio/adc/qcom-vadc-common.c | 4 | ||||
-rw-r--r-- | drivers/iio/adc/sd_adc_modulator.c | 68 | ||||
-rw-r--r-- | drivers/iio/adc/stm32-adc-core.c | 14 | ||||
-rw-r--r-- | drivers/iio/adc/stm32-adc-core.h | 14 | ||||
-rw-r--r-- | drivers/iio/adc/stm32-adc.c | 199 | ||||
-rw-r--r-- | drivers/iio/adc/stm32-dfsdm-adc.c | 1205 | ||||
-rw-r--r-- | drivers/iio/adc/stm32-dfsdm-core.c | 302 | ||||
-rw-r--r-- | drivers/iio/adc/stm32-dfsdm.h | 310 | ||||
-rw-r--r-- | drivers/iio/adc/ti_am335x_adc.c | 2 |
18 files changed, 2880 insertions, 297 deletions
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index ef86296b8b0d..72bc2b71765a 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 @@ -318,6 +319,8 @@ config HI8435 config HX711 tristate "AVIA HX711 ADC for weight cells" depends on GPIOLIB + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER help If you say yes here you get support for AVIA HX711 ADC which is used for weigh cells @@ -629,6 +632,18 @@ config SPEAR_ADC To compile this driver as a module, choose M here: the module will be called spear_adc. +config SD_ADC_MODULATOR + tristate "Generic sigma delta modulator" + depends on OF + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Select this option to enables sigma delta modulator. This driver can + support generic sigma delta modulators. + + This driver can also be built as a module. If so, the module + will be called sd_adc_modulator. + config STM32_ADC_CORE tristate "STMicroelectronics STM32 adc core" depends on ARCH_STM32 || COMPILE_TEST @@ -656,6 +671,31 @@ config STM32_ADC This driver can also be built as a module. If so, the module will be called stm32-adc. +config STM32_DFSDM_CORE + tristate "STMicroelectronics STM32 DFSDM core" + depends on (ARCH_STM32 && OF) || COMPILE_TEST + select REGMAP + select REGMAP_MMIO + help + Select this option to enable the driver for STMicroelectronics + STM32 digital filter for sigma delta converter. + + This driver can also be built as a module. If so, the module + will be called stm32-dfsdm-core. + +config STM32_DFSDM_ADC + tristate "STMicroelectronics STM32 dfsdm adc" + depends on (ARCH_STM32 && OF) || COMPILE_TEST + select STM32_DFSDM_CORE + select REGMAP_MMIO + select IIO_BUFFER_HW_CONSUMER + help + Select this option to support ADCSigma delta modulator for + STMicroelectronics STM32 digital filter for sigma delta converter. + + This driver can also be built as a module. If so, the module + will be called stm32-dfsdm-adc. + config STX104 tristate "Apex Embedded Systems STX104 driver" depends on PC104 && X86 && ISA_BUS_API diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 9572c1090f35..28a9423997f3 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -64,6 +64,8 @@ obj-$(CONFIG_STX104) += stx104.o obj-$(CONFIG_SUN4I_GPADC) += sun4i-gpadc-iio.o obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o obj-$(CONFIG_STM32_ADC) += stm32-adc.o +obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o +obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o obj-$(CONFIG_TI_ADC084S021) += ti-adc084s021.o @@ -82,3 +84,4 @@ obj-$(CONFIG_VF610_ADC) += vf610_adc.o obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o xilinx-xadc-y := xilinx-xadc-core.o xilinx-xadc-events.o obj-$(CONFIG_XILINX_XADC) += xilinx-xadc.o +obj-$(CONFIG_SD_ADC_MODULATOR) += sd_adc_modulator.o 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 <linux/module.h> #include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/reset.h> #include <linux/spinlock.h> #include <linux/types.h> @@ -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); diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 755a493c2a2c..4eff8351ce29 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -16,6 +16,8 @@ #include <linux/bitops.h> #include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/module.h> @@ -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; + + 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); - iio_trigger_notify_done(indio->trig); + 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; @@ -541,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); @@ -580,9 +828,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 +961,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 +1072,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 +1138,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 +1164,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 +1182,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); diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 3836d4222a3e..71a5ee652b79 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -1177,9 +1177,9 @@ static int at91_adc_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); st->reg_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(st->reg_base)) { + if (IS_ERR(st->reg_base)) return PTR_ERR(st->reg_base); - } + /* * Disable all IRQs before setting up the handler 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/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c index d10b9f13d557..9430b54121e0 100644 --- a/drivers/iio/adc/hx711.c +++ b/drivers/iio/adc/hx711.c @@ -24,6 +24,9 @@ #include <linux/delay.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> #include <linux/gpio/consumer.h> #include <linux/regulator/consumer.h> @@ -89,6 +92,11 @@ struct hx711_data { int gain_set; /* gain set on device */ int gain_chan_a; /* gain for channel A */ struct mutex lock; + /* + * triggered buffer + * 2x32-bit channel + 64-bit timestamp + */ + u32 buffer[4]; }; static int hx711_cycle(struct hx711_data *hx711_data) @@ -145,15 +153,16 @@ static int hx711_wait_for_ready(struct hx711_data *hx711_data) int i, val; /* - * a maximum reset cycle time of 56 ms was measured. - * we round it up to 100 ms + * in some rare cases the reset takes quite a long time + * especially when the channel is changed. + * Allow up to one second for it */ for (i = 0; i < 100; i++) { val = gpiod_get_value(hx711_data->gpiod_dout); if (!val) break; - /* sleep at least 1 ms */ - msleep(1); + /* sleep at least 10 ms */ + msleep(10); } if (val) return -EIO; @@ -195,9 +204,7 @@ static int hx711_reset(struct hx711_data *hx711_data) * after a dummy read we need to wait vor readiness * for not mixing gain pulses with the clock */ - ret = hx711_wait_for_ready(hx711_data); - if (ret) - return ret; + val = hx711_wait_for_ready(hx711_data); } return val; @@ -236,34 +243,40 @@ static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan) return 0; } +static int hx711_reset_read(struct hx711_data *hx711_data, int chan) +{ + int ret; + int val; + + /* + * hx711_reset() must be called from here + * because it could be calling hx711_read() by itself + */ + if (hx711_reset(hx711_data)) { + dev_err(hx711_data->dev, "reset failed!"); + return -EIO; + } + + ret = hx711_set_gain_for_channel(hx711_data, chan); + if (ret < 0) + return ret; + + val = hx711_read(hx711_data); + + return val; +} + static int hx711_read_raw(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, int *val, int *val2, long mask) { struct hx711_data *hx711_data = iio_priv(indio_dev); - int ret; switch (mask) { case IIO_CHAN_INFO_RAW: mutex_lock(&hx711_data->lock); - /* - * hx711_reset() must be called from here - * because it could be calling hx711_read() by itself - */ - if (hx711_reset(hx711_data)) { - mutex_unlock(&hx711_data->lock); - dev_err(hx711_data->dev, "reset failed!"); - return -EIO; - } - - ret = hx711_set_gain_for_channel(hx711_data, chan->channel); - if (ret < 0) { - mutex_unlock(&hx711_data->lock); - return ret; - } - - *val = hx711_read(hx711_data); + *val = hx711_reset_read(hx711_data, chan->channel); mutex_unlock(&hx711_data->lock); @@ -339,6 +352,36 @@ static int hx711_write_raw_get_fmt(struct iio_dev *indio_dev, return IIO_VAL_INT_PLUS_NANO; } +static irqreturn_t hx711_trigger(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct hx711_data *hx711_data = iio_priv(indio_dev); + int i, j = 0; + + mutex_lock(&hx711_data->lock); + + memset(hx711_data->buffer, 0, sizeof(hx711_data->buffer)); + + for (i = 0; i < indio_dev->masklength; i++) { + if (!test_bit(i, indio_dev->active_scan_mask)) + continue; + + hx711_data->buffer[j] = hx711_reset_read(hx711_data, + indio_dev->channels[i].channel); + j++; + } + + iio_push_to_buffers_with_timestamp(indio_dev, hx711_data->buffer, + pf->timestamp); + + mutex_unlock(&hx711_data->lock); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + static ssize_t hx711_scale_available_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -387,6 +430,13 @@ static const struct iio_chan_spec hx711_chan_spec[] = { .indexed = 1, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_CPU, + }, }, { .type = IIO_VOLTAGE, @@ -394,7 +444,15 @@ static const struct iio_chan_spec hx711_chan_spec[] = { .indexed = 1, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 1, + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_CPU, + }, }, + IIO_CHAN_SOFT_TIMESTAMP(2), }; static int hx711_probe(struct platform_device *pdev) @@ -459,10 +517,9 @@ static int hx711_probe(struct platform_device *pdev) * 1 LSB = (AVDD * 100) / GAIN / 1678 [10^-9 mV] */ ret = regulator_get_voltage(hx711_data->reg_avdd); - if (ret < 0) { - regulator_disable(hx711_data->reg_avdd); - return ret; - } + if (ret < 0) + goto error_regulator; + /* we need 10^-9 mV */ ret *= 100; @@ -482,12 +539,27 @@ static int hx711_probe(struct platform_device *pdev) indio_dev->channels = hx711_chan_spec; indio_dev->num_channels = ARRAY_SIZE(hx711_chan_spec); + ret = iio_triggered_buffer_setup(indio_dev, iio_pollfunc_store_time, + hx711_trigger, NULL); + if (ret < 0) { + dev_err(dev, "setup of iio triggered buffer failed\n"); + goto error_regulator; + } + ret = iio_device_register(indio_dev); if (ret < 0) { dev_err(dev, "Couldn't register the device\n"); - regulator_disable(hx711_data->reg_avdd); + goto error_buffer; } + return 0; + +error_buffer: + iio_triggered_buffer_cleanup(indio_dev); + +error_regulator: + regulator_disable(hx711_data->reg_avdd); + return ret; } @@ -501,6 +573,8 @@ static int hx711_remove(struct platform_device *pdev) iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + regulator_disable(hx711_data->reg_avdd); return 0; diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 84a43871f7dc..0635a79864bf 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -44,13 +44,14 @@ #define INA226_MASK_ENABLE 0x06 #define INA226_CVRF BIT(3) -#define INA219_CNVR BIT(1) #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 @@ -63,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) @@ -79,6 +88,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) @@ -110,11 +124,12 @@ enum ina2xx_ids { ina219, ina226 }; struct ina2xx_config { u16 config_default; - int calibration_factor; - int shunt_div; - int bus_voltage_shift; + int calibration_value; + int shunt_voltage_lsb; /* nV */ + int bus_voltage_shift; /* position of lsb */ int bus_voltage_lsb; /* uV */ - int power_lsb; /* uW */ + /* fixed relation between current and power lsb, uW/uA */ + int power_lsb_factor; enum ina2xx_ids chip_id; }; @@ -127,26 +142,28 @@ 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; }; static const struct ina2xx_config ina2xx_config[] = { [ina219] = { .config_default = INA219_CONFIG_DEFAULT, - .calibration_factor = 40960000, - .shunt_div = 100, - .bus_voltage_shift = 3, + .calibration_value = 4096, + .shunt_voltage_lsb = 10000, + .bus_voltage_shift = INA219_BUS_VOLTAGE_SHIFT, .bus_voltage_lsb = 4000, - .power_lsb = 20000, + .power_lsb_factor = 20, .chip_id = ina219, }, [ina226] = { .config_default = INA226_CONFIG_DEFAULT, - .calibration_factor = 5120000, - .shunt_div = 400, + .calibration_value = 2048, + .shunt_voltage_lsb = 2500, .bus_voltage_shift = 0, .bus_voltage_lsb = 1250, - .power_lsb = 25000, + .power_lsb_factor = 25, .chip_id = ina226, }, }; @@ -170,6 +187,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: @@ -197,26 +217,48 @@ 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: - /* 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_CURRENT: + /* + * processed (mA) = raw * current_lsb (mA) + * current_lsb (mA) = shunt_voltage_lsb (nV) / + * shunt_resistor (uOhm) + */ + *val = chip->config->shunt_voltage_lsb; + *val2 = chip->shunt_resistor_uohm; return IIO_VAL_FRACTIONAL; case INA2XX_POWER: - /* processed (mW) = raw*lsb (uW) / 1000 */ - *val = chip->config->power_lsb; + /* + * processed (mW) = raw * power_lsb (mW) + * power_lsb (mW) = power_lsb_factor (mW/mA) * + * current_lsb (mA) + */ + *val = chip->config->power_lsb_factor * + chip->config->shunt_voltage_lsb; + *val2 = chip->shunt_resistor_uohm; + return IIO_VAL_FRACTIONAL; + } + + 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_CURRENT: - /* processed (mA) = raw (mA) */ - *val = 1; + case INA2XX_BUS_VOLTAGE: + *val = chip->range_vbus == 32 ? 1 : 2; return IIO_VAL_INT; } } @@ -353,6 +395,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) @@ -395,6 +505,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; } @@ -434,25 +552,21 @@ static ssize_t ina2xx_allow_async_readout_store(struct device *dev, } /* - * Set current LSB to 1mA, shunt is in uOhms - * (equation 13 in datasheet). We hardcode a Current_LSB - * of 1.0 x10-3. The only remaining parameter is RShunt. - * There is no need to expose the CALIBRATION register - * to the user for now. But we need to reset this register - * if the user updates RShunt after driver init, e.g upon - * reading an EEPROM/Probe-type value. + * Calibration register is set to the best value, which eliminates + * truncation errors on calculating current register in hardware. + * According to datasheet (INA 226: eq. 3, INA219: eq. 4) the best values + * are 2048 for ina226 and 4096 for ina219. They are hardcoded as + * calibration_value. */ static int ina2xx_set_calibration(struct ina2xx_chip_info *chip) { - u16 regval = DIV_ROUND_CLOSEST(chip->config->calibration_factor, - chip->shunt_resistor_uohm); - - return regmap_write(chip->regmap, INA2XX_CALIBRATION, regval); + return regmap_write(chip->regmap, INA2XX_CALIBRATION, + chip->config->calibration_value); } static int set_shunt_resistor(struct ina2xx_chip_info *chip, unsigned int val) { - if (val <= 0 || val > chip->config->calibration_factor) + if (val == 0 || val > INT_MAX) return -EINVAL; chip->shunt_resistor_uohm = val; @@ -485,11 +599,6 @@ static ssize_t ina2xx_shunt_resistor_store(struct device *dev, if (ret) return ret; - /* Update the Calibration register */ - ret = ina2xx_set_calibration(chip); - if (ret) - return ret; - return len; } @@ -532,19 +641,23 @@ 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, \ .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 = { \ .sign = 'u', \ - .realbits = 16, \ + .shift = _shift, \ + .realbits = 16 - _shift, \ .storagebits = 16, \ .endianness = IIO_LE, \ } \ @@ -579,23 +692,18 @@ 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), }; -static int ina2xx_work_buffer(struct iio_dev *indio_dev) +static int ina2xx_conversion_ready(struct iio_dev *indio_dev) { struct ina2xx_chip_info *chip = iio_priv(indio_dev); - unsigned short data[8]; - int bit, ret, i = 0; - s64 time_a, time_b; + int ret; unsigned int alert; - int cnvr_need_clear = 0; - - time_a = iio_get_time_ns(indio_dev); /* * Because the timer thread and the chip conversion clock @@ -608,23 +716,31 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev) * For now, we do an extra read of the MASK_ENABLE register (INA226) * resp. the BUS_VOLTAGE register (INA219). */ - if (!chip->allow_async_readout) - do { - if (chip->config->chip_id == ina226) { - ret = regmap_read(chip->regmap, - INA226_MASK_ENABLE, &alert); - alert &= INA226_CVRF; - } else { - ret = regmap_read(chip->regmap, - INA2XX_BUS_VOLTAGE, &alert); - alert &= INA219_CNVR; - cnvr_need_clear = alert; - } + if (chip->config->chip_id == ina226) { + ret = regmap_read(chip->regmap, + INA226_MASK_ENABLE, &alert); + alert &= INA226_CVRF; + } else { + ret = regmap_read(chip->regmap, + INA2XX_BUS_VOLTAGE, &alert); + alert &= INA219_CNVR; + } - if (ret < 0) - return ret; + if (ret < 0) + return ret; + + return !!alert; +} + +static int ina2xx_work_buffer(struct iio_dev *indio_dev) +{ + struct ina2xx_chip_info *chip = iio_priv(indio_dev); + /* data buffer needs space for channel data and timestap */ + unsigned short data[4 + sizeof(s64)/sizeof(short)]; + int bit, ret, i = 0; + s64 time; - } while (!alert); + time = iio_get_time_ns(indio_dev); /* * Single register reads: bulk_read will not work with ina226/219 @@ -640,26 +756,11 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev) return ret; data[i++] = val; - - if (INA2XX_SHUNT_VOLTAGE + bit == INA2XX_POWER) - cnvr_need_clear = 0; - } - - /* Dummy read on INA219 power register to clear CNVR flag */ - if (cnvr_need_clear && chip->config->chip_id == ina219) { - unsigned int val; - - ret = regmap_read(chip->regmap, INA2XX_POWER, &val); - if (ret < 0) - return ret; } - time_b = iio_get_time_ns(indio_dev); + iio_push_to_buffers_with_timestamp(indio_dev, data, time); - iio_push_to_buffers_with_timestamp(indio_dev, - (unsigned int *)data, time_a); - - return (unsigned long)(time_b - time_a) / 1000; + return 0; }; static int ina2xx_capture_thread(void *data) @@ -667,7 +768,9 @@ static int ina2xx_capture_thread(void *data) struct iio_dev *indio_dev = data; struct ina2xx_chip_info *chip = iio_priv(indio_dev); int sampling_us = SAMPLING_PERIOD(chip); - int buffer_us; + int ret; + struct timespec64 next, now, delta; + s64 delay_us; /* * Poll a bit faster than the chip internal Fs, in case @@ -676,13 +779,43 @@ static int ina2xx_capture_thread(void *data) if (!chip->allow_async_readout) sampling_us -= 200; + ktime_get_ts64(&next); + do { - buffer_us = ina2xx_work_buffer(indio_dev); - if (buffer_us < 0) - return buffer_us; + while (!chip->allow_async_readout) { + ret = ina2xx_conversion_ready(indio_dev); + if (ret < 0) + return ret; - if (sampling_us > buffer_us) - udelay(sampling_us - buffer_us); + /* + * If the conversion was not yet finished, + * reset the reference timestamp. + */ + if (ret == 0) + ktime_get_ts64(&next); + else + break; + } + + ret = ina2xx_work_buffer(indio_dev); + if (ret < 0) + return ret; + + ktime_get_ts64(&now); + + /* + * Advance the timestamp for the next poll by one sampling + * interval, and sleep for the remainder (next - now) + * In case "next" has already passed, the interval is added + * multiple times, i.e. samples are dropped. + */ + do { + timespec64_add_ns(&next, 1000 * sampling_us); + delta = timespec64_sub(next, now); + delay_us = div_s64(timespec64_to_ns(&delta), 1000); + } while (delay_us <= 0); + + usleep_range(delay_us, (delay_us * 3) >> 1); } while (!kthread_should_stop()); @@ -746,7 +879,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); @@ -780,6 +912,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, }; @@ -860,6 +993,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); diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index 36047147ce7c..29fa7736d80c 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -96,8 +96,8 @@ #define MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK GENMASK(11, 0) #define MESON_SAR_ADC_AUX_SW 0x1c - #define MESON_SAR_ADC_AUX_SW_MUX_SEL_CHAN_MASK(_chan) \ - (GENMASK(10, 8) << (((_chan) - 2) * 2)) + #define MESON_SAR_ADC_AUX_SW_MUX_SEL_CHAN_SHIFT(_chan) \ + (8 + (((_chan) - 2) * 3)) #define MESON_SAR_ADC_AUX_SW_VREF_P_MUX BIT(6) #define MESON_SAR_ADC_AUX_SW_VREF_N_MUX BIT(5) #define MESON_SAR_ADC_AUX_SW_MODE_SEL BIT(4) @@ -221,6 +221,7 @@ enum meson_sar_adc_chan7_mux_sel { struct meson_sar_adc_data { bool has_bl30_integration; + unsigned long clock_rate; u32 bandgap_reg; unsigned int resolution; const char *name; @@ -233,7 +234,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; @@ -622,7 +622,7 @@ static int meson_sar_adc_clk_init(struct iio_dev *indio_dev, static int meson_sar_adc_init(struct iio_dev *indio_dev) { struct meson_sar_adc_priv *priv = iio_priv(indio_dev); - int regval, ret; + int regval, i, ret; /* * make sure we start at CH7 input since the other muxes are only used @@ -677,6 +677,32 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) FIELD_PREP(MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK, 1)); + /* + * set up the input channel muxes in MESON_SAR_ADC_CHAN_10_SW + * (0 = SAR_ADC_CH0, 1 = SAR_ADC_CH1) + */ + regval = FIELD_PREP(MESON_SAR_ADC_CHAN_10_SW_CHAN0_MUX_SEL_MASK, 0); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW, + MESON_SAR_ADC_CHAN_10_SW_CHAN0_MUX_SEL_MASK, + regval); + regval = FIELD_PREP(MESON_SAR_ADC_CHAN_10_SW_CHAN1_MUX_SEL_MASK, 1); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW, + MESON_SAR_ADC_CHAN_10_SW_CHAN1_MUX_SEL_MASK, + regval); + + /* + * set up the input channel muxes in MESON_SAR_ADC_AUX_SW + * (2 = SAR_ADC_CH2, 3 = SAR_ADC_CH3, ...) and enable + * MESON_SAR_ADC_AUX_SW_YP_DRIVE_SW and + * MESON_SAR_ADC_AUX_SW_XP_DRIVE_SW like the vendor driver. + */ + regval = 0; + for (i = 2; i <= 7; i++) + regval |= i << MESON_SAR_ADC_AUX_SW_MUX_SEL_CHAN_SHIFT(i); + regval |= MESON_SAR_ADC_AUX_SW_YP_DRIVE_SW; + regval |= MESON_SAR_ADC_AUX_SW_XP_DRIVE_SW; + regmap_write(priv->regmap, MESON_SAR_ADC_AUX_SW, regval); + ret = clk_set_parent(priv->adc_sel_clk, priv->clkin); if (ret) { dev_err(indio_dev->dev.parent, @@ -684,7 +710,7 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) return ret; } - ret = clk_set_rate(priv->adc_clk, 1200000); + ret = clk_set_rate(priv->adc_clk, priv->data->clock_rate); if (ret) { dev_err(indio_dev->dev.parent, "failed to set adc clock rate\n"); @@ -731,12 +757,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); @@ -763,8 +783,6 @@ err_adc_clk: regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, MESON_SAR_ADC_REG3_ADC_EN, 0); meson_sar_adc_set_bandgap(indio_dev, false); - clk_disable_unprepare(priv->sana_clk); -err_sana_clk: clk_disable_unprepare(priv->core_clk); err_core_clk: regulator_disable(priv->vref); @@ -790,7 +808,6 @@ static int meson_sar_adc_hw_disable(struct iio_dev *indio_dev) meson_sar_adc_set_bandgap(indio_dev, false); - clk_disable_unprepare(priv->sana_clk); clk_disable_unprepare(priv->core_clk); regulator_disable(priv->vref); @@ -866,6 +883,7 @@ static const struct iio_info meson_sar_adc_iio_info = { static const struct meson_sar_adc_data meson_sar_adc_meson8_data = { .has_bl30_integration = false, + .clock_rate = 1150000, .bandgap_reg = MESON_SAR_ADC_DELTA_10, .regmap_config = &meson_sar_adc_regmap_config_meson8, .resolution = 10, @@ -874,6 +892,7 @@ static const struct meson_sar_adc_data meson_sar_adc_meson8_data = { static const struct meson_sar_adc_data meson_sar_adc_meson8b_data = { .has_bl30_integration = false, + .clock_rate = 1150000, .bandgap_reg = MESON_SAR_ADC_DELTA_10, .regmap_config = &meson_sar_adc_regmap_config_meson8, .resolution = 10, @@ -882,6 +901,7 @@ static const struct meson_sar_adc_data meson_sar_adc_meson8b_data = { static const struct meson_sar_adc_data meson_sar_adc_gxbb_data = { .has_bl30_integration = true, + .clock_rate = 1200000, .bandgap_reg = MESON_SAR_ADC_REG11, .regmap_config = &meson_sar_adc_regmap_config_gxbb, .resolution = 10, @@ -890,6 +910,7 @@ static const struct meson_sar_adc_data meson_sar_adc_gxbb_data = { static const struct meson_sar_adc_data meson_sar_adc_gxl_data = { .has_bl30_integration = true, + .clock_rate = 1200000, .bandgap_reg = MESON_SAR_ADC_REG11, .regmap_config = &meson_sar_adc_regmap_config_gxbb, .resolution = 12, @@ -898,6 +919,7 @@ static const struct meson_sar_adc_data meson_sar_adc_gxl_data = { static const struct meson_sar_adc_data meson_sar_adc_gxm_data = { .has_bl30_integration = true, + .clock_rate = 1200000, .bandgap_reg = MESON_SAR_ADC_REG11, .regmap_config = &meson_sar_adc_regmap_config_gxbb, .resolution = 12, @@ -993,16 +1015,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) { 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 <linux/math64.h> #include <linux/log2.h> #include <linux/err.h> +#include <linux/module.h> #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"); diff --git a/drivers/iio/adc/sd_adc_modulator.c b/drivers/iio/adc/sd_adc_modulator.c new file mode 100644 index 000000000000..560d8c7d9d86 --- /dev/null +++ b/drivers/iio/adc/sd_adc_modulator.c @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Generic sigma delta modulator driver + * + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>. + */ + +#include <linux/iio/iio.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/module.h> +#include <linux/of_device.h> + +static const struct iio_info iio_sd_mod_iio_info; + +static const struct iio_chan_spec iio_sd_mod_ch = { + .type = IIO_VOLTAGE, + .indexed = 1, + .scan_type = { + .sign = 'u', + .realbits = 1, + .shift = 0, + }, +}; + +static int iio_sd_mod_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *iio; + + iio = devm_iio_device_alloc(dev, 0); + if (!iio) + return -ENOMEM; + + iio->dev.parent = dev; + iio->dev.of_node = dev->of_node; + iio->name = dev_name(dev); + iio->info = &iio_sd_mod_iio_info; + iio->modes = INDIO_BUFFER_HARDWARE; + + iio->num_channels = 1; + iio->channels = &iio_sd_mod_ch; + + platform_set_drvdata(pdev, iio); + + return devm_iio_device_register(&pdev->dev, iio); +} + +static const struct of_device_id sd_adc_of_match[] = { + { .compatible = "sd-modulator" }, + { .compatible = "ads1201" }, + { } +}; +MODULE_DEVICE_TABLE(of, sd_adc_of_match); + +static struct platform_driver iio_sd_mod_adc = { + .driver = { + .name = "iio_sd_adc_mod", + .of_match_table = of_match_ptr(sd_adc_of_match), + }, + .probe = iio_sd_mod_probe, +}; + +module_platform_driver(iio_sd_mod_adc); + +MODULE_DESCRIPTION("Basic sigma delta modulator"); +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index 6aefef99f935..40be7d9fadbf 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * This file is part of STM32 ADC driver * @@ -6,19 +7,6 @@ * * Inspired from: fsl-imx25-tsadc * - * License type: GPLv2 - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ #include <linux/clk.h> diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h index 250ee958a669..8af507b3f32d 100644 --- a/drivers/iio/adc/stm32-adc-core.h +++ b/drivers/iio/adc/stm32-adc-core.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * This file is part of STM32 ADC driver * * Copyright (C) 2016, STMicroelectronics - All Rights Reserved * Author: Fabrice Gasnier <fabrice.gasnier@st.com>. * - * License type: GPLv2 - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ #ifndef __STM32_ADC_H diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index c9d96f935dba..7f5def465340 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -1,22 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * This file is part of STM32 ADC driver * * Copyright (C) 2016, STMicroelectronics - All Rights Reserved * Author: Fabrice Gasnier <fabrice.gasnier@st.com>. - * - * License type: GPLv2 - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ #include <linux/clk.h> @@ -92,6 +79,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 @@ -153,6 +141,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 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 @@ -297,9 +287,11 @@ 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 + * @chan_name: channel name array */ struct stm32_adc { struct stm32_adc_common *common; @@ -318,72 +310,37 @@ 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_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; +struct stm32_adc_diff_channel { + u32 vinp; + u32 vinn; }; /** * 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 +351,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), }; @@ -983,15 +940,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) @@ -1263,10 +1224,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; } @@ -1315,6 +1289,7 @@ static int stm32_adc_set_watermark(struct iio_dev *indio_dev, unsigned int val) { struct stm32_adc *adc = iio_priv(indio_dev); unsigned int watermark = STM32_DMA_BUFFER_SIZE / 2; + unsigned int rx_buf_sz = STM32_DMA_BUFFER_SIZE; /* * dma cyclic transfers are used, buffer is split into two periods. @@ -1323,7 +1298,7 @@ static int stm32_adc_set_watermark(struct iio_dev *indio_dev, unsigned int val) * - one buffer (period) driver can push with iio_trigger_poll(). */ watermark = min(watermark, val * (unsigned)(sizeof(u16))); - adc->rx_buf_sz = watermark * 2; + adc->rx_buf_sz = min(rx_buf_sz, watermark * 2 * adc->num_conv); return 0; } @@ -1628,29 +1603,40 @@ 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, - int scan_index, u32 smp) + struct iio_chan_spec *chan, u32 vinp, + u32 vinn, int scan_index, bool differential) { struct stm32_adc *adc = iio_priv(indio_dev); - - chan->type = channel->type; - chan->channel = channel->channel; - chan->datasheet_name = channel->name; + char *name = adc->chan_name[vinp]; + + chan->type = IIO_VOLTAGE; + chan->channel = 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) @@ -1658,17 +1644,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 */ @@ -1689,6 +1698,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 @@ -1696,12 +1732,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], - &adc_info->channels[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; diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c new file mode 100644 index 000000000000..daa026d6a94f --- /dev/null +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -0,0 +1,1205 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is the ADC part of the STM32 DFSDM driver + * + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>. + */ + +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/iio/buffer.h> +#include <linux/iio/hw-consumer.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#include "stm32-dfsdm.h" + +#define DFSDM_DMA_BUFFER_SIZE (4 * PAGE_SIZE) + +/* Conversion timeout */ +#define DFSDM_TIMEOUT_US 100000 +#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000)) + +/* Oversampling attribute default */ +#define DFSDM_DEFAULT_OVERSAMPLING 100 + +/* Oversampling max values */ +#define DFSDM_MAX_INT_OVERSAMPLING 256 +#define DFSDM_MAX_FL_OVERSAMPLING 1024 + +/* Max sample resolutions */ +#define DFSDM_MAX_RES BIT(31) +#define DFSDM_DATA_RES BIT(23) + +enum sd_converter_type { + DFSDM_AUDIO, + DFSDM_IIO, +}; + +struct stm32_dfsdm_dev_data { + int type; + int (*init)(struct iio_dev *indio_dev); + unsigned int num_channels; + const struct regmap_config *regmap_cfg; +}; + +struct stm32_dfsdm_adc { + struct stm32_dfsdm *dfsdm; + const struct stm32_dfsdm_dev_data *dev_data; + unsigned int fl_id; + unsigned int ch_id; + + /* ADC specific */ + unsigned int oversamp; + struct iio_hw_consumer *hwc; + struct completion completion; + u32 *buffer; + + /* Audio specific */ + unsigned int spi_freq; /* SPI bus clock frequency */ + unsigned int sample_freq; /* Sample frequency after filter decimation */ + int (*cb)(const void *data, size_t size, void *cb_priv); + void *cb_priv; + + /* DMA */ + u8 *rx_buf; + unsigned int bufi; /* Buffer current position */ + unsigned int buf_sz; /* Buffer size */ + struct dma_chan *dma_chan; + dma_addr_t dma_buf; +}; + +struct stm32_dfsdm_str2field { + const char *name; + unsigned int val; +}; + +/* DFSDM channel serial interface type */ +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_type[] = { + { "SPI_R", 0 }, /* SPI with data on rising edge */ + { "SPI_F", 1 }, /* SPI with data on falling edge */ + { "MANCH_R", 2 }, /* Manchester codec, rising edge = logic 0 */ + { "MANCH_F", 3 }, /* Manchester codec, falling edge = logic 1 */ + {}, +}; + +/* DFSDM channel clock source */ +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_src[] = { + /* External SPI clock (CLKIN x) */ + { "CLKIN", DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL }, + /* Internal SPI clock (CLKOUT) */ + { "CLKOUT", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL }, + /* Internal SPI clock divided by 2 (falling edge) */ + { "CLKOUT_F", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING }, + /* Internal SPI clock divided by 2 (falling edge) */ + { "CLKOUT_R", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING }, + {}, +}; + +static int stm32_dfsdm_str2val(const char *str, + const struct stm32_dfsdm_str2field *list) +{ + const struct stm32_dfsdm_str2field *p = list; + + for (p = list; p && p->name; p++) + if (!strcmp(p->name, str)) + return p->val; + + return -EINVAL; +} + +static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, + unsigned int fast, unsigned int oversamp) +{ + unsigned int i, d, fosr, iosr; + u64 res; + s64 delta; + unsigned int m = 1; /* multiplication factor */ + unsigned int p = fl->ford; /* filter order (ford) */ + + pr_debug("%s: Requested oversampling: %d\n", __func__, oversamp); + /* + * This function tries to compute filter oversampling and integrator + * oversampling, base on oversampling ratio requested by user. + * + * Decimation d depends on the filter order and the oversampling ratios. + * ford: filter order + * fosr: filter over sampling ratio + * iosr: integrator over sampling ratio + */ + if (fl->ford == DFSDM_FASTSINC_ORDER) { + m = 2; + p = 2; + } + + /* + * Look for filter and integrator oversampling ratios which allows + * to reach 24 bits data output resolution. + * Leave as soon as if exact resolution if reached. + * Otherwise the higher resolution below 32 bits is kept. + */ + for (fosr = 1; fosr <= DFSDM_MAX_FL_OVERSAMPLING; fosr++) { + for (iosr = 1; iosr <= DFSDM_MAX_INT_OVERSAMPLING; iosr++) { + if (fast) + d = fosr * iosr; + else if (fl->ford == DFSDM_FASTSINC_ORDER) + d = fosr * (iosr + 3) + 2; + else + d = fosr * (iosr - 1 + p) + p; + + if (d > oversamp) + break; + else if (d != oversamp) + continue; + /* + * Check resolution (limited to signed 32 bits) + * res <= 2^31 + * Sincx filters: + * res = m * fosr^p x iosr (with m=1, p=ford) + * FastSinc filter + * res = m * fosr^p x iosr (with m=2, p=2) + */ + res = fosr; + for (i = p - 1; i > 0; i--) { + res = res * (u64)fosr; + if (res > DFSDM_MAX_RES) + break; + } + if (res > DFSDM_MAX_RES) + continue; + res = res * (u64)m * (u64)iosr; + if (res > DFSDM_MAX_RES) + continue; + + delta = res - DFSDM_DATA_RES; + + if (res >= fl->res) { + fl->res = res; + fl->fosr = fosr; + fl->iosr = iosr; + fl->fast = fast; + pr_debug("%s: fosr = %d, iosr = %d\n", + __func__, fl->fosr, fl->iosr); + } + + if (!delta) + return 0; + } + } + + if (!fl->fosr) + return -EINVAL; + + return 0; +} + +static int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, + unsigned int ch_id) +{ + return regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id), + DFSDM_CHCFGR1_CHEN_MASK, + DFSDM_CHCFGR1_CHEN(1)); +} + +static void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, + unsigned int ch_id) +{ + regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id), + DFSDM_CHCFGR1_CHEN_MASK, DFSDM_CHCFGR1_CHEN(0)); +} + +static int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm, + struct stm32_dfsdm_channel *ch) +{ + unsigned int id = ch->id; + struct regmap *regmap = dfsdm->regmap; + int ret; + + ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id), + DFSDM_CHCFGR1_SITP_MASK, + DFSDM_CHCFGR1_SITP(ch->type)); + if (ret < 0) + return ret; + ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id), + DFSDM_CHCFGR1_SPICKSEL_MASK, + DFSDM_CHCFGR1_SPICKSEL(ch->src)); + if (ret < 0) + return ret; + return regmap_update_bits(regmap, DFSDM_CHCFGR1(id), + DFSDM_CHCFGR1_CHINSEL_MASK, + DFSDM_CHCFGR1_CHINSEL(ch->alt_si)); +} + +static int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, + unsigned int fl_id) +{ + int ret; + + /* Enable filter */ + ret = regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id), + DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(1)); + if (ret < 0) + return ret; + + /* Start conversion */ + return regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id), + DFSDM_CR1_RSWSTART_MASK, + DFSDM_CR1_RSWSTART(1)); +} + +static void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id) +{ + /* Disable conversion */ + regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id), + DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0)); +} + +static int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, + unsigned int fl_id, unsigned int ch_id) +{ + struct regmap *regmap = dfsdm->regmap; + struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[fl_id]; + int ret; + + /* Average integrator oversampling */ + ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK, + DFSDM_FCR_IOSR(fl->iosr - 1)); + if (ret) + return ret; + + /* Filter order and Oversampling */ + ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FOSR_MASK, + DFSDM_FCR_FOSR(fl->fosr - 1)); + if (ret) + return ret; + + ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FORD_MASK, + DFSDM_FCR_FORD(fl->ford)); + if (ret) + return ret; + + /* No scan mode supported for the moment */ + ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RCH_MASK, + DFSDM_CR1_RCH(ch_id)); + if (ret) + return ret; + + return regmap_update_bits(regmap, DFSDM_CR1(fl_id), + DFSDM_CR1_RSYNC_MASK, + DFSDM_CR1_RSYNC(fl->sync_mode)); +} + +static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm, + struct iio_dev *indio_dev, + struct iio_chan_spec *ch) +{ + struct stm32_dfsdm_channel *df_ch; + const char *of_str; + int chan_idx = ch->scan_index; + int ret, val; + + ret = of_property_read_u32_index(indio_dev->dev.of_node, + "st,adc-channels", chan_idx, + &ch->channel); + if (ret < 0) { + dev_err(&indio_dev->dev, + " Error parsing 'st,adc-channels' for idx %d\n", + chan_idx); + return ret; + } + if (ch->channel >= dfsdm->num_chs) { + dev_err(&indio_dev->dev, + " Error bad channel number %d (max = %d)\n", + ch->channel, dfsdm->num_chs); + return -EINVAL; + } + + ret = of_property_read_string_index(indio_dev->dev.of_node, + "st,adc-channel-names", chan_idx, + &ch->datasheet_name); + if (ret < 0) { + dev_err(&indio_dev->dev, + " Error parsing 'st,adc-channel-names' for idx %d\n", + chan_idx); + return ret; + } + + df_ch = &dfsdm->ch_list[ch->channel]; + df_ch->id = ch->channel; + + ret = of_property_read_string_index(indio_dev->dev.of_node, + "st,adc-channel-types", chan_idx, + &of_str); + if (!ret) { + val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type); + if (val < 0) + return val; + } else { + val = 0; + } + df_ch->type = val; + + ret = of_property_read_string_index(indio_dev->dev.of_node, + "st,adc-channel-clk-src", chan_idx, + &of_str); + if (!ret) { + val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src); + if (val < 0) + return val; + } else { + val = 0; + } + df_ch->src = val; + + ret = of_property_read_u32_index(indio_dev->dev.of_node, + "st,adc-alt-channel", chan_idx, + &df_ch->alt_si); + if (ret < 0) + df_ch->alt_si = 0; + + return 0; +} + +static ssize_t dfsdm_adc_audio_get_spiclk(struct iio_dev *indio_dev, + uintptr_t priv, + const struct iio_chan_spec *chan, + char *buf) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", adc->spi_freq); +} + +static ssize_t dfsdm_adc_audio_set_spiclk(struct iio_dev *indio_dev, + uintptr_t priv, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id]; + struct stm32_dfsdm_channel *ch = &adc->dfsdm->ch_list[adc->ch_id]; + unsigned int sample_freq = adc->sample_freq; + unsigned int spi_freq; + int ret; + + dev_err(&indio_dev->dev, "enter %s\n", __func__); + /* If DFSDM is master on SPI, SPI freq can not be updated */ + if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL) + return -EPERM; + + ret = kstrtoint(buf, 0, &spi_freq); + if (ret) + return ret; + + if (!spi_freq) + return -EINVAL; + + if (sample_freq) { + if (spi_freq % sample_freq) + dev_warn(&indio_dev->dev, + "Sampling rate not accurate (%d)\n", + spi_freq / (spi_freq / sample_freq)); + + ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq)); + if (ret < 0) { + dev_err(&indio_dev->dev, + "No filter parameters that match!\n"); + return ret; + } + } + adc->spi_freq = spi_freq; + + return len; +} + +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc, bool dma) +{ + struct regmap *regmap = adc->dfsdm->regmap; + int ret; + unsigned int dma_en = 0, cont_en = 0; + + ret = stm32_dfsdm_start_channel(adc->dfsdm, adc->ch_id); + if (ret < 0) + return ret; + + ret = stm32_dfsdm_filter_configure(adc->dfsdm, adc->fl_id, + adc->ch_id); + if (ret < 0) + goto stop_channels; + + if (dma) { + /* Enable DMA transfer*/ + dma_en = DFSDM_CR1_RDMAEN(1); + /* Enable conversion triggered by SPI clock*/ + cont_en = DFSDM_CR1_RCONT(1); + } + /* Enable DMA transfer*/ + ret = regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id), + DFSDM_CR1_RDMAEN_MASK, dma_en); + if (ret < 0) + goto stop_channels; + + /* Enable conversion triggered by SPI clock*/ + ret = regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id), + DFSDM_CR1_RCONT_MASK, cont_en); + if (ret < 0) + goto stop_channels; + + ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id); + if (ret < 0) + goto stop_channels; + + return 0; + +stop_channels: + regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id), + DFSDM_CR1_RDMAEN_MASK, 0); + + regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id), + DFSDM_CR1_RCONT_MASK, 0); + stm32_dfsdm_stop_channel(adc->dfsdm, adc->fl_id); + + return ret; +} + +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc) +{ + struct regmap *regmap = adc->dfsdm->regmap; + + stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id); + + /* Clean conversion options */ + regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id), + DFSDM_CR1_RDMAEN_MASK, 0); + + regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id), + DFSDM_CR1_RCONT_MASK, 0); + + stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id); +} + +static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev, + unsigned int val) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2; + + /* + * DMA cyclic transfers are used, buffer is split into two periods. + * There should be : + * - always one buffer (period) DMA is working on + * - one buffer (period) driver pushed to ASoC side. + */ + watermark = min(watermark, val * (unsigned int)(sizeof(u32))); + adc->buf_sz = watermark * 2; + + return 0; +} + +static unsigned int stm32_dfsdm_adc_dma_residue(struct stm32_dfsdm_adc *adc) +{ + struct dma_tx_state state; + enum dma_status status; + + status = dmaengine_tx_status(adc->dma_chan, + adc->dma_chan->cookie, + &state); + if (status == DMA_IN_PROGRESS) { + /* Residue is size in bytes from end of buffer */ + unsigned int i = adc->buf_sz - state.residue; + unsigned int size; + + /* Return available bytes */ + if (i >= adc->bufi) + size = i - adc->bufi; + else + size = adc->buf_sz + i - adc->bufi; + + return size; + } + + return 0; +} + +static void stm32_dfsdm_audio_dma_buffer_done(void *data) +{ + struct iio_dev *indio_dev = data; + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + int available = stm32_dfsdm_adc_dma_residue(adc); + size_t old_pos; + + /* + * FIXME: In Kernel interface does not support cyclic DMA buffer,and + * offers only an interface to push data samples per samples. + * For this reason IIO buffer interface is not used and interface is + * bypassed using a private callback registered by ASoC. + * This should be a temporary solution waiting a cyclic DMA engine + * support in IIO. + */ + + dev_dbg(&indio_dev->dev, "%s: pos = %d, available = %d\n", __func__, + adc->bufi, available); + old_pos = adc->bufi; + + while (available >= indio_dev->scan_bytes) { + u32 *buffer = (u32 *)&adc->rx_buf[adc->bufi]; + + /* Mask 8 LSB that contains the channel ID */ + *buffer = (*buffer & 0xFFFFFF00) << 8; + available -= indio_dev->scan_bytes; + adc->bufi += indio_dev->scan_bytes; + if (adc->bufi >= adc->buf_sz) { + if (adc->cb) + adc->cb(&adc->rx_buf[old_pos], + adc->buf_sz - old_pos, adc->cb_priv); + adc->bufi = 0; + old_pos = 0; + } + } + if (adc->cb) + adc->cb(&adc->rx_buf[old_pos], adc->bufi - old_pos, + adc->cb_priv); +} + +static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct dma_async_tx_descriptor *desc; + dma_cookie_t cookie; + int ret; + + if (!adc->dma_chan) + return -EINVAL; + + dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__, + adc->buf_sz, adc->buf_sz / 2); + + /* Prepare a DMA cyclic transaction */ + desc = dmaengine_prep_dma_cyclic(adc->dma_chan, + adc->dma_buf, + adc->buf_sz, adc->buf_sz / 2, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!desc) + return -EBUSY; + + desc->callback = stm32_dfsdm_audio_dma_buffer_done; + desc->callback_param = indio_dev; + + cookie = dmaengine_submit(desc); + ret = dma_submit_error(cookie); + if (ret) { + dmaengine_terminate_all(adc->dma_chan); + return ret; + } + + /* Issue pending DMA requests */ + dma_async_issue_pending(adc->dma_chan); + + return 0; +} + +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + int ret; + + /* Reset adc buffer index */ + adc->bufi = 0; + + ret = stm32_dfsdm_start_dfsdm(adc->dfsdm); + if (ret < 0) + return ret; + + ret = stm32_dfsdm_start_conv(adc, true); + if (ret) { + dev_err(&indio_dev->dev, "Can't start conversion\n"); + goto stop_dfsdm; + } + + if (adc->dma_chan) { + ret = stm32_dfsdm_adc_dma_start(indio_dev); + if (ret) { + dev_err(&indio_dev->dev, "Can't start DMA\n"); + goto err_stop_conv; + } + } + + return 0; + +err_stop_conv: + stm32_dfsdm_stop_conv(adc); +stop_dfsdm: + stm32_dfsdm_stop_dfsdm(adc->dfsdm); + + return ret; +} + +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + + if (adc->dma_chan) + dmaengine_terminate_all(adc->dma_chan); + + stm32_dfsdm_stop_conv(adc); + + stm32_dfsdm_stop_dfsdm(adc->dfsdm); + + return 0; +} + +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = { + .postenable = &stm32_dfsdm_postenable, + .predisable = &stm32_dfsdm_predisable, +}; + +/** + * stm32_dfsdm_get_buff_cb() - register a callback that will be called when + * DMA transfer period is achieved. + * + * @iio_dev: Handle to IIO device. + * @cb: Pointer to callback function: + * - data: pointer to data buffer + * - size: size in byte of the data buffer + * - private: pointer to consumer private structure. + * @private: Pointer to consumer private structure. + */ +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev, + int (*cb)(const void *data, size_t size, + void *private), + void *private) +{ + struct stm32_dfsdm_adc *adc; + + if (!iio_dev) + return -EINVAL; + adc = iio_priv(iio_dev); + + adc->cb = cb; + adc->cb_priv = private; + + return 0; +} +EXPORT_SYMBOL_GPL(stm32_dfsdm_get_buff_cb); + +/** + * stm32_dfsdm_release_buff_cb - unregister buffer callback + * + * @iio_dev: Handle to IIO device. + */ +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev) +{ + struct stm32_dfsdm_adc *adc; + + if (!iio_dev) + return -EINVAL; + adc = iio_priv(iio_dev); + + adc->cb = NULL; + adc->cb_priv = NULL; + + return 0; +} +EXPORT_SYMBOL_GPL(stm32_dfsdm_release_buff_cb); + +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *res) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + long timeout; + int ret; + + reinit_completion(&adc->completion); + + adc->buffer = res; + + ret = stm32_dfsdm_start_dfsdm(adc->dfsdm); + if (ret < 0) + return ret; + + ret = regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id), + DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(1)); + if (ret < 0) + goto stop_dfsdm; + + ret = stm32_dfsdm_start_conv(adc, false); + if (ret < 0) { + regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id), + DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0)); + goto stop_dfsdm; + } + + timeout = wait_for_completion_interruptible_timeout(&adc->completion, + DFSDM_TIMEOUT); + + /* Mask IRQ for regular conversion achievement*/ + regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id), + DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0)); + + if (timeout == 0) + ret = -ETIMEDOUT; + else if (timeout < 0) + ret = timeout; + else + ret = IIO_VAL_INT; + + stm32_dfsdm_stop_conv(adc); + +stop_dfsdm: + stm32_dfsdm_stop_dfsdm(adc->dfsdm); + + return ret; +} + +static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id]; + struct stm32_dfsdm_channel *ch = &adc->dfsdm->ch_list[adc->ch_id]; + unsigned int spi_freq = adc->spi_freq; + int ret = -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + ret = stm32_dfsdm_set_osrs(fl, 0, val); + if (!ret) + adc->oversamp = val; + + return ret; + + case IIO_CHAN_INFO_SAMP_FREQ: + if (!val) + return -EINVAL; + if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL) + spi_freq = adc->dfsdm->spi_master_freq; + + if (spi_freq % val) + dev_warn(&indio_dev->dev, + "Sampling rate not accurate (%d)\n", + spi_freq / (spi_freq / val)); + + ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / val)); + if (ret < 0) { + dev_err(&indio_dev->dev, + "Not able to find parameter that match!\n"); + return ret; + } + adc->sample_freq = val; + + return 0; + } + + return -EINVAL; +} + +static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_hw_consumer_enable(adc->hwc); + if (ret < 0) { + dev_err(&indio_dev->dev, + "%s: IIO enable failed (channel %d)\n", + __func__, chan->channel); + return ret; + } + ret = stm32_dfsdm_single_conv(indio_dev, chan, val); + iio_hw_consumer_disable(adc->hwc); + if (ret < 0) { + dev_err(&indio_dev->dev, + "%s: Conversion failed (channel %d)\n", + __func__, chan->channel); + return ret; + } + return IIO_VAL_INT; + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = adc->oversamp; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = adc->sample_freq; + + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static const struct iio_info stm32_dfsdm_info_audio = { + .hwfifo_set_watermark = stm32_dfsdm_set_watermark, + .read_raw = stm32_dfsdm_read_raw, + .write_raw = stm32_dfsdm_write_raw, +}; + +static const struct iio_info stm32_dfsdm_info_adc = { + .read_raw = stm32_dfsdm_read_raw, + .write_raw = stm32_dfsdm_write_raw, +}; + +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg) +{ + struct stm32_dfsdm_adc *adc = arg; + struct iio_dev *indio_dev = iio_priv_to_dev(adc); + struct regmap *regmap = adc->dfsdm->regmap; + unsigned int status, int_en; + + regmap_read(regmap, DFSDM_ISR(adc->fl_id), &status); + regmap_read(regmap, DFSDM_CR2(adc->fl_id), &int_en); + + if (status & DFSDM_ISR_REOCF_MASK) { + /* Read the data register clean the IRQ status */ + regmap_read(regmap, DFSDM_RDATAR(adc->fl_id), adc->buffer); + complete(&adc->completion); + } + + if (status & DFSDM_ISR_ROVRF_MASK) { + if (int_en & DFSDM_CR2_ROVRIE_MASK) + dev_warn(&indio_dev->dev, "Overrun detected\n"); + regmap_update_bits(regmap, DFSDM_ICR(adc->fl_id), + DFSDM_ICR_CLRROVRF_MASK, + DFSDM_ICR_CLRROVRF_MASK); + } + + return IRQ_HANDLED; +} + +/* + * Define external info for SPI Frequency and audio sampling rate that can be + * configured by ASoC driver through consumer.h API + */ +static const struct iio_chan_spec_ext_info dfsdm_adc_audio_ext_info[] = { + /* spi_clk_freq : clock freq on SPI/manchester bus used by channel */ + { + .name = "spi_clk_freq", + .shared = IIO_SHARED_BY_TYPE, + .read = dfsdm_adc_audio_get_spiclk, + .write = dfsdm_adc_audio_set_spiclk, + }, + {}, +}; + +static void stm32_dfsdm_dma_release(struct iio_dev *indio_dev) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + + if (adc->dma_chan) { + dma_free_coherent(adc->dma_chan->device->dev, + DFSDM_DMA_BUFFER_SIZE, + adc->rx_buf, adc->dma_buf); + dma_release_channel(adc->dma_chan); + } +} + +static int stm32_dfsdm_dma_request(struct iio_dev *indio_dev) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct dma_slave_config config = { + .src_addr = (dma_addr_t)adc->dfsdm->phys_base + + DFSDM_RDATAR(adc->fl_id), + .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + }; + int ret; + + adc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx"); + if (!adc->dma_chan) + return -EINVAL; + + adc->rx_buf = dma_alloc_coherent(adc->dma_chan->device->dev, + DFSDM_DMA_BUFFER_SIZE, + &adc->dma_buf, GFP_KERNEL); + if (!adc->rx_buf) { + ret = -ENOMEM; + goto err_release; + } + + ret = dmaengine_slave_config(adc->dma_chan, &config); + if (ret) + goto err_free; + + return 0; + +err_free: + dma_free_coherent(adc->dma_chan->device->dev, DFSDM_DMA_BUFFER_SIZE, + adc->rx_buf, adc->dma_buf); +err_release: + dma_release_channel(adc->dma_chan); + + return ret; +} + +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev, + struct iio_chan_spec *ch) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + int ret; + + ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, ch); + if (ret < 0) + return ret; + + ch->type = IIO_VOLTAGE; + ch->indexed = 1; + + /* + * IIO_CHAN_INFO_RAW: used to compute regular conversion + * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling + */ + ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + ch->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO); + + if (adc->dev_data->type == DFSDM_AUDIO) { + ch->scan_type.sign = 's'; + ch->ext_info = dfsdm_adc_audio_ext_info; + } else { + ch->scan_type.sign = 'u'; + } + ch->scan_type.realbits = 24; + ch->scan_type.storagebits = 32; + adc->ch_id = ch->channel; + + return stm32_dfsdm_chan_configure(adc->dfsdm, + &adc->dfsdm->ch_list[ch->channel]); +} + +static int stm32_dfsdm_audio_init(struct iio_dev *indio_dev) +{ + struct iio_chan_spec *ch; + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct stm32_dfsdm_channel *d_ch; + int ret; + + indio_dev->modes |= INDIO_BUFFER_SOFTWARE; + indio_dev->setup_ops = &stm32_dfsdm_buffer_setup_ops; + + ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL); + if (!ch) + return -ENOMEM; + + ch->scan_index = 0; + + ret = stm32_dfsdm_adc_chan_init_one(indio_dev, ch); + if (ret < 0) { + dev_err(&indio_dev->dev, "Channels init failed\n"); + return ret; + } + ch->info_mask_separate = BIT(IIO_CHAN_INFO_SAMP_FREQ); + + d_ch = &adc->dfsdm->ch_list[adc->ch_id]; + if (d_ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL) + adc->spi_freq = adc->dfsdm->spi_master_freq; + + indio_dev->num_channels = 1; + indio_dev->channels = ch; + + return stm32_dfsdm_dma_request(indio_dev); +} + +static int stm32_dfsdm_adc_init(struct iio_dev *indio_dev) +{ + struct iio_chan_spec *ch; + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + int num_ch; + int ret, chan_idx; + + adc->oversamp = DFSDM_DEFAULT_OVERSAMPLING; + ret = stm32_dfsdm_set_osrs(&adc->dfsdm->fl_list[adc->fl_id], 0, + adc->oversamp); + if (ret < 0) + return ret; + + num_ch = of_property_count_u32_elems(indio_dev->dev.of_node, + "st,adc-channels"); + if (num_ch < 0 || num_ch > adc->dfsdm->num_chs) { + dev_err(&indio_dev->dev, "Bad st,adc-channels\n"); + return num_ch < 0 ? num_ch : -EINVAL; + } + + /* Bind to SD modulator IIO device */ + adc->hwc = devm_iio_hw_consumer_alloc(&indio_dev->dev); + if (IS_ERR(adc->hwc)) + return -EPROBE_DEFER; + + ch = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*ch), + GFP_KERNEL); + if (!ch) + return -ENOMEM; + + for (chan_idx = 0; chan_idx < num_ch; chan_idx++) { + ch->scan_index = chan_idx; + ret = stm32_dfsdm_adc_chan_init_one(indio_dev, ch); + if (ret < 0) { + dev_err(&indio_dev->dev, "Channels init failed\n"); + return ret; + } + } + + indio_dev->num_channels = num_ch; + indio_dev->channels = ch; + + init_completion(&adc->completion); + + return 0; +} + +static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_adc_data = { + .type = DFSDM_IIO, + .init = stm32_dfsdm_adc_init, +}; + +static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_audio_data = { + .type = DFSDM_AUDIO, + .init = stm32_dfsdm_audio_init, +}; + +static const struct of_device_id stm32_dfsdm_adc_match[] = { + { + .compatible = "st,stm32-dfsdm-adc", + .data = &stm32h7_dfsdm_adc_data, + }, + { + .compatible = "st,stm32-dfsdm-dmic", + .data = &stm32h7_dfsdm_audio_data, + }, + {} +}; + +static int stm32_dfsdm_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct stm32_dfsdm_adc *adc; + struct device_node *np = dev->of_node; + const struct stm32_dfsdm_dev_data *dev_data; + struct iio_dev *iio; + char *name; + int ret, irq, val; + + + dev_data = of_device_get_match_data(dev); + iio = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!iio) { + dev_err(dev, "%s: Failed to allocate IIO\n", __func__); + return -ENOMEM; + } + + adc = iio_priv(iio); + adc->dfsdm = dev_get_drvdata(dev->parent); + + iio->dev.parent = dev; + iio->dev.of_node = np; + iio->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + + platform_set_drvdata(pdev, adc); + + ret = of_property_read_u32(dev->of_node, "reg", &adc->fl_id); + if (ret != 0) { + dev_err(dev, "Missing reg property\n"); + return -EINVAL; + } + + name = devm_kzalloc(dev, sizeof("dfsdm-adc0"), GFP_KERNEL); + if (!name) + return -ENOMEM; + if (dev_data->type == DFSDM_AUDIO) { + iio->info = &stm32_dfsdm_info_audio; + snprintf(name, sizeof("dfsdm-pdm0"), "dfsdm-pdm%d", adc->fl_id); + } else { + iio->info = &stm32_dfsdm_info_adc; + snprintf(name, sizeof("dfsdm-adc0"), "dfsdm-adc%d", adc->fl_id); + } + iio->name = name; + + /* + * In a first step IRQs generated for channels are not treated. + * So IRQ associated to filter instance 0 is dedicated to the Filter 0. + */ + irq = platform_get_irq(pdev, 0); + ret = devm_request_irq(dev, irq, stm32_dfsdm_irq, + 0, pdev->name, adc); + if (ret < 0) { + dev_err(dev, "Failed to request IRQ\n"); + return ret; + } + + ret = of_property_read_u32(dev->of_node, "st,filter-order", &val); + if (ret < 0) { + dev_err(dev, "Failed to set filter order\n"); + return ret; + } + + adc->dfsdm->fl_list[adc->fl_id].ford = val; + + ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val); + if (!ret) + adc->dfsdm->fl_list[adc->fl_id].sync_mode = val; + + adc->dev_data = dev_data; + ret = dev_data->init(iio); + if (ret < 0) + return ret; + + ret = iio_device_register(iio); + if (ret < 0) + goto err_cleanup; + + dev_err(dev, "of_platform_populate\n"); + if (dev_data->type == DFSDM_AUDIO) { + ret = of_platform_populate(np, NULL, NULL, dev); + if (ret < 0) { + dev_err(dev, "Failed to find an audio DAI\n"); + goto err_unregister; + } + } + + return 0; + +err_unregister: + iio_device_unregister(iio); +err_cleanup: + stm32_dfsdm_dma_release(iio); + + return ret; +} + +static int stm32_dfsdm_adc_remove(struct platform_device *pdev) +{ + struct stm32_dfsdm_adc *adc = platform_get_drvdata(pdev); + struct iio_dev *indio_dev = iio_priv_to_dev(adc); + + if (adc->dev_data->type == DFSDM_AUDIO) + of_platform_depopulate(&pdev->dev); + iio_device_unregister(indio_dev); + stm32_dfsdm_dma_release(indio_dev); + + return 0; +} + +static struct platform_driver stm32_dfsdm_adc_driver = { + .driver = { + .name = "stm32-dfsdm-adc", + .of_match_table = stm32_dfsdm_adc_match, + }, + .probe = stm32_dfsdm_adc_probe, + .remove = stm32_dfsdm_adc_remove, +}; +module_platform_driver(stm32_dfsdm_adc_driver); + +MODULE_DESCRIPTION("STM32 sigma delta ADC"); +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c new file mode 100644 index 000000000000..6290332cfd3f --- /dev/null +++ b/drivers/iio/adc/stm32-dfsdm-core.c @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is part the core part STM32 DFSDM driver + * + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com> for STMicroelectronics. + */ + +#include <linux/clk.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#include "stm32-dfsdm.h" + +struct stm32_dfsdm_dev_data { + unsigned int num_filters; + unsigned int num_channels; + const struct regmap_config *regmap_cfg; +}; + +#define STM32H7_DFSDM_NUM_FILTERS 4 +#define STM32H7_DFSDM_NUM_CHANNELS 8 + +static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg) +{ + if (reg < DFSDM_FILTER_BASE_ADR) + return false; + + /* + * Mask is done on register to avoid to list registers of all + * filter instances. + */ + switch (reg & DFSDM_FILTER_REG_MASK) { + case DFSDM_CR1(0) & DFSDM_FILTER_REG_MASK: + case DFSDM_ISR(0) & DFSDM_FILTER_REG_MASK: + case DFSDM_JDATAR(0) & DFSDM_FILTER_REG_MASK: + case DFSDM_RDATAR(0) & DFSDM_FILTER_REG_MASK: + return true; + } + + return false; +} + +static const struct regmap_config stm32h7_dfsdm_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = sizeof(u32), + .max_register = 0x2B8, + .volatile_reg = stm32_dfsdm_volatile_reg, + .fast_io = true, +}; + +static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_data = { + .num_filters = STM32H7_DFSDM_NUM_FILTERS, + .num_channels = STM32H7_DFSDM_NUM_CHANNELS, + .regmap_cfg = &stm32h7_dfsdm_regmap_cfg, +}; + +struct dfsdm_priv { + struct platform_device *pdev; /* platform device */ + + struct stm32_dfsdm dfsdm; /* common data exported for all instances */ + + unsigned int spi_clk_out_div; /* SPI clkout divider value */ + atomic_t n_active_ch; /* number of current active channels */ + + struct clk *clk; /* DFSDM clock */ + struct clk *aclk; /* audio clock */ +}; + +/** + * stm32_dfsdm_start_dfsdm - start global dfsdm interface. + * + * Enable interface if n_active_ch is not null. + * @dfsdm: Handle used to retrieve dfsdm context. + */ +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm) +{ + struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm); + struct device *dev = &priv->pdev->dev; + unsigned int clk_div = priv->spi_clk_out_div; + int ret; + + if (atomic_inc_return(&priv->n_active_ch) == 1) { + ret = clk_prepare_enable(priv->clk); + if (ret < 0) { + dev_err(dev, "Failed to start clock\n"); + goto error_ret; + } + if (priv->aclk) { + ret = clk_prepare_enable(priv->aclk); + if (ret < 0) { + dev_err(dev, "Failed to start audio clock\n"); + goto disable_clk; + } + } + + /* Output the SPI CLKOUT (if clk_div == 0 clock if OFF) */ + ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0), + DFSDM_CHCFGR1_CKOUTDIV_MASK, + DFSDM_CHCFGR1_CKOUTDIV(clk_div)); + if (ret < 0) + goto disable_aclk; + + /* Global enable of DFSDM interface */ + ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0), + DFSDM_CHCFGR1_DFSDMEN_MASK, + DFSDM_CHCFGR1_DFSDMEN(1)); + if (ret < 0) + goto disable_aclk; + } + + dev_dbg(dev, "%s: n_active_ch %d\n", __func__, + atomic_read(&priv->n_active_ch)); + + return 0; + +disable_aclk: + clk_disable_unprepare(priv->aclk); +disable_clk: + clk_disable_unprepare(priv->clk); + +error_ret: + atomic_dec(&priv->n_active_ch); + + return ret; +} +EXPORT_SYMBOL_GPL(stm32_dfsdm_start_dfsdm); + +/** + * stm32_dfsdm_stop_dfsdm - stop global DFSDM interface. + * + * Disable interface if n_active_ch is null + * @dfsdm: Handle used to retrieve dfsdm context. + */ +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm) +{ + struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm); + int ret; + + if (atomic_dec_and_test(&priv->n_active_ch)) { + /* Global disable of DFSDM interface */ + ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0), + DFSDM_CHCFGR1_DFSDMEN_MASK, + DFSDM_CHCFGR1_DFSDMEN(0)); + if (ret < 0) + return ret; + + /* Stop SPI CLKOUT */ + ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0), + DFSDM_CHCFGR1_CKOUTDIV_MASK, + DFSDM_CHCFGR1_CKOUTDIV(0)); + if (ret < 0) + return ret; + + clk_disable_unprepare(priv->clk); + if (priv->aclk) + clk_disable_unprepare(priv->aclk); + } + dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__, + atomic_read(&priv->n_active_ch)); + + return 0; +} +EXPORT_SYMBOL_GPL(stm32_dfsdm_stop_dfsdm); + +static int stm32_dfsdm_parse_of(struct platform_device *pdev, + struct dfsdm_priv *priv) +{ + struct device_node *node = pdev->dev.of_node; + struct resource *res; + unsigned long clk_freq; + unsigned int spi_freq, rem; + int ret; + + if (!node) + return -EINVAL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Failed to get memory resource\n"); + return -ENODEV; + } + priv->dfsdm.phys_base = res->start; + priv->dfsdm.base = devm_ioremap_resource(&pdev->dev, res); + + /* + * "dfsdm" clock is mandatory for DFSDM peripheral clocking. + * "dfsdm" or "audio" clocks can be used as source clock for + * the SPI clock out signal and internal processing, depending + * on use case. + */ + priv->clk = devm_clk_get(&pdev->dev, "dfsdm"); + if (IS_ERR(priv->clk)) { + dev_err(&pdev->dev, "No stm32_dfsdm_clk clock found\n"); + return -EINVAL; + } + + priv->aclk = devm_clk_get(&pdev->dev, "audio"); + if (IS_ERR(priv->aclk)) + priv->aclk = NULL; + + if (priv->aclk) + clk_freq = clk_get_rate(priv->aclk); + else + clk_freq = clk_get_rate(priv->clk); + + /* SPI clock out frequency */ + ret = of_property_read_u32(pdev->dev.of_node, "spi-max-frequency", + &spi_freq); + if (ret < 0) { + /* No SPI master mode */ + return 0; + } + + priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1; + priv->dfsdm.spi_master_freq = spi_freq; + + if (rem) { + dev_warn(&pdev->dev, "SPI clock not accurate\n"); + dev_warn(&pdev->dev, "%ld = %d * %d + %d\n", + clk_freq, spi_freq, priv->spi_clk_out_div + 1, rem); + } + + return 0; +}; + +static const struct of_device_id stm32_dfsdm_of_match[] = { + { + .compatible = "st,stm32h7-dfsdm", + .data = &stm32h7_dfsdm_data, + }, + {} +}; +MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match); + +static int stm32_dfsdm_probe(struct platform_device *pdev) +{ + struct dfsdm_priv *priv; + const struct stm32_dfsdm_dev_data *dev_data; + struct stm32_dfsdm *dfsdm; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->pdev = pdev; + + dev_data = of_device_get_match_data(&pdev->dev); + + dfsdm = &priv->dfsdm; + dfsdm->fl_list = devm_kcalloc(&pdev->dev, dev_data->num_filters, + sizeof(*dfsdm->fl_list), GFP_KERNEL); + if (!dfsdm->fl_list) + return -ENOMEM; + + dfsdm->num_fls = dev_data->num_filters; + dfsdm->ch_list = devm_kcalloc(&pdev->dev, dev_data->num_channels, + sizeof(*dfsdm->ch_list), + GFP_KERNEL); + if (!dfsdm->ch_list) + return -ENOMEM; + dfsdm->num_chs = dev_data->num_channels; + + ret = stm32_dfsdm_parse_of(pdev, priv); + if (ret < 0) + return ret; + + dfsdm->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "dfsdm", + dfsdm->base, + &stm32h7_dfsdm_regmap_cfg); + if (IS_ERR(dfsdm->regmap)) { + ret = PTR_ERR(dfsdm->regmap); + dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n", + __func__, ret); + return ret; + } + + platform_set_drvdata(pdev, dfsdm); + + return devm_of_platform_populate(&pdev->dev); +} + +static struct platform_driver stm32_dfsdm_driver = { + .probe = stm32_dfsdm_probe, + .driver = { + .name = "stm32-dfsdm", + .of_match_table = stm32_dfsdm_of_match, + }, +}; + +module_platform_driver(stm32_dfsdm_driver); + +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics STM32 dfsdm driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h new file mode 100644 index 000000000000..8708394b0725 --- /dev/null +++ b/drivers/iio/adc/stm32-dfsdm.h @@ -0,0 +1,310 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This file is part of STM32 DFSDM driver + * + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com>. + */ + +#ifndef MDF_STM32_DFSDM__H +#define MDF_STM32_DFSDM__H + +#include <linux/bitfield.h> + +/* + * STM32 DFSDM - global register map + * ________________________________________________________ + * | Offset | Registers block | + * -------------------------------------------------------- + * | 0x000 | CHANNEL 0 + COMMON CHANNEL FIELDS | + * -------------------------------------------------------- + * | 0x020 | CHANNEL 1 | + * -------------------------------------------------------- + * | ... | ..... | + * -------------------------------------------------------- + * | 0x0E0 | CHANNEL 7 | + * -------------------------------------------------------- + * | 0x100 | FILTER 0 + COMMON FILTER FIELDs | + * -------------------------------------------------------- + * | 0x200 | FILTER 1 | + * -------------------------------------------------------- + * | 0x300 | FILTER 2 | + * -------------------------------------------------------- + * | 0x400 | FILTER 3 | + * -------------------------------------------------------- + */ + +/* + * Channels register definitions + */ +#define DFSDM_CHCFGR1(y) ((y) * 0x20 + 0x00) +#define DFSDM_CHCFGR2(y) ((y) * 0x20 + 0x04) +#define DFSDM_AWSCDR(y) ((y) * 0x20 + 0x08) +#define DFSDM_CHWDATR(y) ((y) * 0x20 + 0x0C) +#define DFSDM_CHDATINR(y) ((y) * 0x20 + 0x10) + +/* CHCFGR1: Channel configuration register 1 */ +#define DFSDM_CHCFGR1_SITP_MASK GENMASK(1, 0) +#define DFSDM_CHCFGR1_SITP(v) FIELD_PREP(DFSDM_CHCFGR1_SITP_MASK, v) +#define DFSDM_CHCFGR1_SPICKSEL_MASK GENMASK(3, 2) +#define DFSDM_CHCFGR1_SPICKSEL(v) FIELD_PREP(DFSDM_CHCFGR1_SPICKSEL_MASK, v) +#define DFSDM_CHCFGR1_SCDEN_MASK BIT(5) +#define DFSDM_CHCFGR1_SCDEN(v) FIELD_PREP(DFSDM_CHCFGR1_SCDEN_MASK, v) +#define DFSDM_CHCFGR1_CKABEN_MASK BIT(6) +#define DFSDM_CHCFGR1_CKABEN(v) FIELD_PREP(DFSDM_CHCFGR1_CKABEN_MASK, v) +#define DFSDM_CHCFGR1_CHEN_MASK BIT(7) +#define DFSDM_CHCFGR1_CHEN(v) FIELD_PREP(DFSDM_CHCFGR1_CHEN_MASK, v) +#define DFSDM_CHCFGR1_CHINSEL_MASK BIT(8) +#define DFSDM_CHCFGR1_CHINSEL(v) FIELD_PREP(DFSDM_CHCFGR1_CHINSEL_MASK, v) +#define DFSDM_CHCFGR1_DATMPX_MASK GENMASK(13, 12) +#define DFSDM_CHCFGR1_DATMPX(v) FIELD_PREP(DFSDM_CHCFGR1_DATMPX_MASK, v) +#define DFSDM_CHCFGR1_DATPACK_MASK GENMASK(15, 14) +#define DFSDM_CHCFGR1_DATPACK(v) FIELD_PREP(DFSDM_CHCFGR1_DATPACK_MASK, v) +#define DFSDM_CHCFGR1_CKOUTDIV_MASK GENMASK(23, 16) +#define DFSDM_CHCFGR1_CKOUTDIV(v) FIELD_PREP(DFSDM_CHCFGR1_CKOUTDIV_MASK, v) +#define DFSDM_CHCFGR1_CKOUTSRC_MASK BIT(30) +#define DFSDM_CHCFGR1_CKOUTSRC(v) FIELD_PREP(DFSDM_CHCFGR1_CKOUTSRC_MASK, v) +#define DFSDM_CHCFGR1_DFSDMEN_MASK BIT(31) +#define DFSDM_CHCFGR1_DFSDMEN(v) FIELD_PREP(DFSDM_CHCFGR1_DFSDMEN_MASK, v) + +/* CHCFGR2: Channel configuration register 2 */ +#define DFSDM_CHCFGR2_DTRBS_MASK GENMASK(7, 3) +#define DFSDM_CHCFGR2_DTRBS(v) FIELD_PREP(DFSDM_CHCFGR2_DTRBS_MASK, v) +#define DFSDM_CHCFGR2_OFFSET_MASK GENMASK(31, 8) +#define DFSDM_CHCFGR2_OFFSET(v) FIELD_PREP(DFSDM_CHCFGR2_OFFSET_MASK, v) + +/* AWSCDR: Channel analog watchdog and short circuit detector */ +#define DFSDM_AWSCDR_SCDT_MASK GENMASK(7, 0) +#define DFSDM_AWSCDR_SCDT(v) FIELD_PREP(DFSDM_AWSCDR_SCDT_MASK, v) +#define DFSDM_AWSCDR_BKSCD_MASK GENMASK(15, 12) +#define DFSDM_AWSCDR_BKSCD(v) FIELD_PREP(DFSDM_AWSCDR_BKSCD_MASK, v) +#define DFSDM_AWSCDR_AWFOSR_MASK GENMASK(20, 16) +#define DFSDM_AWSCDR_AWFOSR(v) FIELD_PREP(DFSDM_AWSCDR_AWFOSR_MASK, v) +#define DFSDM_AWSCDR_AWFORD_MASK GENMASK(23, 22) +#define DFSDM_AWSCDR_AWFORD(v) FIELD_PREP(DFSDM_AWSCDR_AWFORD_MASK, v) + +/* + * Filters register definitions + */ +#define DFSDM_FILTER_BASE_ADR 0x100 +#define DFSDM_FILTER_REG_MASK 0x7F +#define DFSDM_FILTER_X_BASE_ADR(x) ((x) * 0x80 + DFSDM_FILTER_BASE_ADR) + +#define DFSDM_CR1(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x00) +#define DFSDM_CR2(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x04) +#define DFSDM_ISR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x08) +#define DFSDM_ICR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x0C) +#define DFSDM_JCHGR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x10) +#define DFSDM_FCR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x14) +#define DFSDM_JDATAR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x18) +#define DFSDM_RDATAR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x1C) +#define DFSDM_AWHTR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x20) +#define DFSDM_AWLTR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x24) +#define DFSDM_AWSR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x28) +#define DFSDM_AWCFR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x2C) +#define DFSDM_EXMAX(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x30) +#define DFSDM_EXMIN(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x34) +#define DFSDM_CNVTIMR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x38) + +/* CR1 Control register 1 */ +#define DFSDM_CR1_DFEN_MASK BIT(0) +#define DFSDM_CR1_DFEN(v) FIELD_PREP(DFSDM_CR1_DFEN_MASK, v) +#define DFSDM_CR1_JSWSTART_MASK BIT(1) +#define DFSDM_CR1_JSWSTART(v) FIELD_PREP(DFSDM_CR1_JSWSTART_MASK, v) +#define DFSDM_CR1_JSYNC_MASK BIT(3) +#define DFSDM_CR1_JSYNC(v) FIELD_PREP(DFSDM_CR1_JSYNC_MASK, v) +#define DFSDM_CR1_JSCAN_MASK BIT(4) +#define DFSDM_CR1_JSCAN(v) FIELD_PREP(DFSDM_CR1_JSCAN_MASK, v) +#define DFSDM_CR1_JDMAEN_MASK BIT(5) +#define DFSDM_CR1_JDMAEN(v) FIELD_PREP(DFSDM_CR1_JDMAEN_MASK, v) +#define DFSDM_CR1_JEXTSEL_MASK GENMASK(12, 8) +#define DFSDM_CR1_JEXTSEL(v) FIELD_PREP(DFSDM_CR1_JEXTSEL_MASK, v) +#define DFSDM_CR1_JEXTEN_MASK GENMASK(14, 13) +#define DFSDM_CR1_JEXTEN(v) FIELD_PREP(DFSDM_CR1_JEXTEN_MASK, v) +#define DFSDM_CR1_RSWSTART_MASK BIT(17) +#define DFSDM_CR1_RSWSTART(v) FIELD_PREP(DFSDM_CR1_RSWSTART_MASK, v) +#define DFSDM_CR1_RCONT_MASK BIT(18) +#define DFSDM_CR1_RCONT(v) FIELD_PREP(DFSDM_CR1_RCONT_MASK, v) +#define DFSDM_CR1_RSYNC_MASK BIT(19) +#define DFSDM_CR1_RSYNC(v) FIELD_PREP(DFSDM_CR1_RSYNC_MASK, v) +#define DFSDM_CR1_RDMAEN_MASK BIT(21) +#define DFSDM_CR1_RDMAEN(v) FIELD_PREP(DFSDM_CR1_RDMAEN_MASK, v) +#define DFSDM_CR1_RCH_MASK GENMASK(26, 24) +#define DFSDM_CR1_RCH(v) FIELD_PREP(DFSDM_CR1_RCH_MASK, v) +#define DFSDM_CR1_FAST_MASK BIT(29) +#define DFSDM_CR1_FAST(v) FIELD_PREP(DFSDM_CR1_FAST_MASK, v) +#define DFSDM_CR1_AWFSEL_MASK BIT(30) +#define DFSDM_CR1_AWFSEL(v) FIELD_PREP(DFSDM_CR1_AWFSEL_MASK, v) + +/* CR2: Control register 2 */ +#define DFSDM_CR2_IE_MASK GENMASK(6, 0) +#define DFSDM_CR2_IE(v) FIELD_PREP(DFSDM_CR2_IE_MASK, v) +#define DFSDM_CR2_JEOCIE_MASK BIT(0) +#define DFSDM_CR2_JEOCIE(v) FIELD_PREP(DFSDM_CR2_JEOCIE_MASK, v) +#define DFSDM_CR2_REOCIE_MASK BIT(1) +#define DFSDM_CR2_REOCIE(v) FIELD_PREP(DFSDM_CR2_REOCIE_MASK, v) +#define DFSDM_CR2_JOVRIE_MASK BIT(2) +#define DFSDM_CR2_JOVRIE(v) FIELD_PREP(DFSDM_CR2_JOVRIE_MASK, v) +#define DFSDM_CR2_ROVRIE_MASK BIT(3) +#define DFSDM_CR2_ROVRIE(v) FIELD_PREP(DFSDM_CR2_ROVRIE_MASK, v) +#define DFSDM_CR2_AWDIE_MASK BIT(4) +#define DFSDM_CR2_AWDIE(v) FIELD_PREP(DFSDM_CR2_AWDIE_MASK, v) +#define DFSDM_CR2_SCDIE_MASK BIT(5) +#define DFSDM_CR2_SCDIE(v) FIELD_PREP(DFSDM_CR2_SCDIE_MASK, v) +#define DFSDM_CR2_CKABIE_MASK BIT(6) +#define DFSDM_CR2_CKABIE(v) FIELD_PREP(DFSDM_CR2_CKABIE_MASK, v) +#define DFSDM_CR2_EXCH_MASK GENMASK(15, 8) +#define DFSDM_CR2_EXCH(v) FIELD_PREP(DFSDM_CR2_EXCH_MASK, v) +#define DFSDM_CR2_AWDCH_MASK GENMASK(23, 16) +#define DFSDM_CR2_AWDCH(v) FIELD_PREP(DFSDM_CR2_AWDCH_MASK, v) + +/* ISR: Interrupt status register */ +#define DFSDM_ISR_JEOCF_MASK BIT(0) +#define DFSDM_ISR_JEOCF(v) FIELD_PREP(DFSDM_ISR_JEOCF_MASK, v) +#define DFSDM_ISR_REOCF_MASK BIT(1) +#define DFSDM_ISR_REOCF(v) FIELD_PREP(DFSDM_ISR_REOCF_MASK, v) +#define DFSDM_ISR_JOVRF_MASK BIT(2) +#define DFSDM_ISR_JOVRF(v) FIELD_PREP(DFSDM_ISR_JOVRF_MASK, v) +#define DFSDM_ISR_ROVRF_MASK BIT(3) +#define DFSDM_ISR_ROVRF(v) FIELD_PREP(DFSDM_ISR_ROVRF_MASK, v) +#define DFSDM_ISR_AWDF_MASK BIT(4) +#define DFSDM_ISR_AWDF(v) FIELD_PREP(DFSDM_ISR_AWDF_MASK, v) +#define DFSDM_ISR_JCIP_MASK BIT(13) +#define DFSDM_ISR_JCIP(v) FIELD_PREP(DFSDM_ISR_JCIP_MASK, v) +#define DFSDM_ISR_RCIP_MASK BIT(14) +#define DFSDM_ISR_RCIP(v) FIELD_PREP(DFSDM_ISR_RCIP, v) +#define DFSDM_ISR_CKABF_MASK GENMASK(23, 16) +#define DFSDM_ISR_CKABF(v) FIELD_PREP(DFSDM_ISR_CKABF_MASK, v) +#define DFSDM_ISR_SCDF_MASK GENMASK(31, 24) +#define DFSDM_ISR_SCDF(v) FIELD_PREP(DFSDM_ISR_SCDF_MASK, v) + +/* ICR: Interrupt flag clear register */ +#define DFSDM_ICR_CLRJOVRF_MASK BIT(2) +#define DFSDM_ICR_CLRJOVRF(v) FIELD_PREP(DFSDM_ICR_CLRJOVRF_MASK, v) +#define DFSDM_ICR_CLRROVRF_MASK BIT(3) +#define DFSDM_ICR_CLRROVRF(v) FIELD_PREP(DFSDM_ICR_CLRROVRF_MASK, v) +#define DFSDM_ICR_CLRCKABF_MASK GENMASK(23, 16) +#define DFSDM_ICR_CLRCKABF(v) FIELD_PREP(DFSDM_ICR_CLRCKABF_MASK, v) +#define DFSDM_ICR_CLRCKABF_CH_MASK(y) BIT(16 + (y)) +#define DFSDM_ICR_CLRCKABF_CH(v, y) \ + (((v) << (16 + (y))) & DFSDM_ICR_CLRCKABF_CH_MASK(y)) +#define DFSDM_ICR_CLRSCDF_MASK GENMASK(31, 24) +#define DFSDM_ICR_CLRSCDF(v) FIELD_PREP(DFSDM_ICR_CLRSCDF_MASK, v) +#define DFSDM_ICR_CLRSCDF_CH_MASK(y) BIT(24 + (y)) +#define DFSDM_ICR_CLRSCDF_CH(v, y) \ + (((v) << (24 + (y))) & DFSDM_ICR_CLRSCDF_MASK(y)) + +/* FCR: Filter control register */ +#define DFSDM_FCR_IOSR_MASK GENMASK(7, 0) +#define DFSDM_FCR_IOSR(v) FIELD_PREP(DFSDM_FCR_IOSR_MASK, v) +#define DFSDM_FCR_FOSR_MASK GENMASK(25, 16) +#define DFSDM_FCR_FOSR(v) FIELD_PREP(DFSDM_FCR_FOSR_MASK, v) +#define DFSDM_FCR_FORD_MASK GENMASK(31, 29) +#define DFSDM_FCR_FORD(v) FIELD_PREP(DFSDM_FCR_FORD_MASK, v) + +/* RDATAR: Filter data register for regular channel */ +#define DFSDM_DATAR_CH_MASK GENMASK(2, 0) +#define DFSDM_DATAR_DATA_OFFSET 8 +#define DFSDM_DATAR_DATA_MASK GENMASK(31, DFSDM_DATAR_DATA_OFFSET) + +/* AWLTR: Filter analog watchdog low threshold register */ +#define DFSDM_AWLTR_BKAWL_MASK GENMASK(3, 0) +#define DFSDM_AWLTR_BKAWL(v) FIELD_PREP(DFSDM_AWLTR_BKAWL_MASK, v) +#define DFSDM_AWLTR_AWLT_MASK GENMASK(31, 8) +#define DFSDM_AWLTR_AWLT(v) FIELD_PREP(DFSDM_AWLTR_AWLT_MASK, v) + +/* AWHTR: Filter analog watchdog low threshold register */ +#define DFSDM_AWHTR_BKAWH_MASK GENMASK(3, 0) +#define DFSDM_AWHTR_BKAWH(v) FIELD_PREP(DFSDM_AWHTR_BKAWH_MASK, v) +#define DFSDM_AWHTR_AWHT_MASK GENMASK(31, 8) +#define DFSDM_AWHTR_AWHT(v) FIELD_PREP(DFSDM_AWHTR_AWHT_MASK, v) + +/* AWSR: Filter watchdog status register */ +#define DFSDM_AWSR_AWLTF_MASK GENMASK(7, 0) +#define DFSDM_AWSR_AWLTF(v) FIELD_PREP(DFSDM_AWSR_AWLTF_MASK, v) +#define DFSDM_AWSR_AWHTF_MASK GENMASK(15, 8) +#define DFSDM_AWSR_AWHTF(v) FIELD_PREP(DFSDM_AWSR_AWHTF_MASK, v) + +/* AWCFR: Filter watchdog status register */ +#define DFSDM_AWCFR_AWLTF_MASK GENMASK(7, 0) +#define DFSDM_AWCFR_AWLTF(v) FIELD_PREP(DFSDM_AWCFR_AWLTF_MASK, v) +#define DFSDM_AWCFR_AWHTF_MASK GENMASK(15, 8) +#define DFSDM_AWCFR_AWHTF(v) FIELD_PREP(DFSDM_AWCFR_AWHTF_MASK, v) + +/* DFSDM filter order */ +enum stm32_dfsdm_sinc_order { + DFSDM_FASTSINC_ORDER, /* FastSinc filter type */ + DFSDM_SINC1_ORDER, /* Sinc 1 filter type */ + DFSDM_SINC2_ORDER, /* Sinc 2 filter type */ + DFSDM_SINC3_ORDER, /* Sinc 3 filter type */ + DFSDM_SINC4_ORDER, /* Sinc 4 filter type (N.A. for watchdog) */ + DFSDM_SINC5_ORDER, /* Sinc 5 filter type (N.A. for watchdog) */ + DFSDM_NB_SINC_ORDER, +}; + +/** + * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter + * @iosr: integrator oversampling + * @fosr: filter oversampling + * @ford: filter order + * @res: output sample resolution + * @sync_mode: filter synchronized with filter 0 + * @fast: filter fast mode + */ +struct stm32_dfsdm_filter { + unsigned int iosr; + unsigned int fosr; + enum stm32_dfsdm_sinc_order ford; + u64 res; + unsigned int sync_mode; + unsigned int fast; +}; + +/** + * struct stm32_dfsdm_channel - structure relative to stm32 FDSDM channel + * @id: id of the channel + * @type: interface type linked to stm32_dfsdm_chan_type + * @src: interface type linked to stm32_dfsdm_chan_src + * @alt_si: alternative serial input interface + */ +struct stm32_dfsdm_channel { + unsigned int id; + unsigned int type; + unsigned int src; + unsigned int alt_si; +}; + +/** + * struct stm32_dfsdm - stm32 FDSDM driver common data (for all instances) + * @base: control registers base cpu addr + * @phys_base: DFSDM IP register physical address + * @regmap: regmap for register read/write + * @fl_list: filter resources list + * @num_fls: number of filter resources available + * @ch_list: channel resources list + * @num_chs: number of channel resources available + * @spi_master_freq: SPI clock out frequency + */ +struct stm32_dfsdm { + void __iomem *base; + phys_addr_t phys_base; + struct regmap *regmap; + struct stm32_dfsdm_filter *fl_list; + unsigned int num_fls; + struct stm32_dfsdm_channel *ch_list; + unsigned int num_chs; + unsigned int spi_master_freq; +}; + +/* DFSDM channel serial spi clock source */ +enum stm32_dfsdm_spi_clk_src { + DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL, + DFSDM_CHANNEL_SPI_CLOCK_INTERNAL, + DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING, + DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING +}; + +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm); +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm); + +#endif diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index b3e573cc6f5f..80df5a377d30 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -523,7 +523,7 @@ static int tiadc_read_raw(struct iio_dev *indio_dev, } am335x_tsc_se_adc_done(adc_dev->mfd_tscadc); - if (found == false) + if (!found) ret = -EBUSY; err_unlock: |