meta-st-stm32mp/recipes-kernel/linux/linux-stm32mp/4.19/4.19.49/0010-ARM-stm32mp1-r2-IIO.patch

6333 lines
182 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From 6fddc08f91cc0eb406ced8e610aabac3ce7320de Mon Sep 17 00:00:00 2001
From: Lionel VITTE <lionel.vitte@st.com>
Date: Thu, 11 Jul 2019 14:12:01 +0200
Subject: [PATCH 10/30] ARM stm32mp1 r2 IIO
---
drivers/iio/adc/Kconfig | 11 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/stm32-adc-core.c | 720 ++++++++++++--
drivers/iio/adc/stm32-adc-core.h | 99 ++
drivers/iio/adc/stm32-adc-temp.c | 412 ++++++++
drivers/iio/adc/stm32-adc.c | 1471 ++++++++++++++++++++++++-----
drivers/iio/adc/stm32-dfsdm-adc.c | 853 +++++++++++++----
drivers/iio/adc/stm32-dfsdm-core.c | 184 +++-
drivers/iio/adc/stm32-dfsdm.h | 24 +-
drivers/iio/counter/stm32-lptimer-cnt.c | 55 ++
drivers/iio/dac/stm32-dac-core.c | 142 ++-
drivers/iio/dac/stm32-dac.c | 96 +-
drivers/iio/trigger/stm32-timer-trigger.c | 167 +++-
13 files changed, 3657 insertions(+), 578 deletions(-)
create mode 100644 drivers/iio/adc/stm32-adc-temp.c
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 9421c1e..66af479 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -662,6 +662,7 @@ config STM32_ADC_CORE
select MFD_STM32_TIMERS
select IIO_STM32_TIMER_TRIGGER
select IIO_TRIGGERED_BUFFER
+ select IRQ_WORK
help
Select this option to enable the core driver for STMicroelectronics
STM32 analog-to-digital converter (ADC).
@@ -679,6 +680,16 @@ config STM32_ADC
This driver can also be built as a module. If so, the module
will be called stm32-adc.
+config STM32_ADC_TEMP
+ tristate "STMicroelectronics STM32 ADC temperature sensor"
+ depends on STM32_ADC
+ help
+ Select this option to enable the driver for temperature sensor
+ connected internally to STM32 ADC.
+
+ This driver can also be built as a module. If so, the module
+ will be called stm32-adc-temp.
+
config STM32_DFSDM_CORE
tristate "STMicroelectronics STM32 DFSDM core"
depends on (ARCH_STM32 && OF) || COMPILE_TEST
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 03db7b5..527f9ef 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -65,6 +65,7 @@ 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_ADC_TEMP) += stm32-adc-temp.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
diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
index ca432e7..dc40c05 100644
--- a/drivers/iio/adc/stm32-adc-core.c
+++ b/drivers/iio/adc/stm32-adc-core.c
@@ -10,12 +10,18 @@
*/
#include <linux/clk.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
#include <linux/interrupt.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdesc.h>
#include <linux/irqdomain.h>
+#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
@@ -23,12 +29,29 @@
/* STM32F4 - common registers for all ADC instances: 1, 2 & 3 */
#define STM32F4_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
-#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04)
/* STM32F4_ADC_CSR - bit fields */
+#define STM32F4_OVR3 BIT(21)
+#define STM32F4_JEOC3 BIT(18)
#define STM32F4_EOC3 BIT(17)
+#define STM32F4_AWD3 BIT(16)
+#define STM32F4_OVR2 BIT(13)
+#define STM32F4_JEOC2 BIT(10)
#define STM32F4_EOC2 BIT(9)
+#define STM32F4_AWD2 BIT(8)
+#define STM32F4_OVR1 BIT(5)
+#define STM32F4_JEOC1 BIT(2)
#define STM32F4_EOC1 BIT(1)
+#define STM32F4_AWD1 BIT(0)
+#define STM32F4_EOC_MASK1 (STM32F4_EOC1 | STM32F4_AWD1 | \
+ STM32F4_OVR1)
+#define STM32F4_EOC_MASK2 (STM32F4_EOC2 | STM32F4_AWD2 | \
+ STM32F4_OVR2)
+#define STM32F4_EOC_MASK3 (STM32F4_EOC3 | STM32F4_AWD3 | \
+ STM32F4_OVR3)
+#define STM32F4_JEOC_MASK1 (STM32F4_JEOC1 | STM32F4_AWD1)
+#define STM32F4_JEOC_MASK2 (STM32F4_JEOC2 | STM32F4_AWD2)
+#define STM32F4_JEOC_MASK3 (STM32F4_JEOC3 | STM32F4_AWD3)
/* STM32F4_ADC_CCR - bit fields */
#define STM32F4_ADC_ADCPRE_SHIFT 16
@@ -36,11 +59,30 @@
/* STM32H7 - common registers for all ADC instances */
#define STM32H7_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
-#define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08)
/* STM32H7_ADC_CSR - bit fields */
+#define STM32H7_AWD3_SLV BIT(25)
+#define STM32H7_AWD2_SLV BIT(24)
+#define STM32H7_AWD1_SLV BIT(23)
+#define STM32H7_JEOS_SLV BIT(22)
+#define STM32H7_OVR_SLV BIT(20)
#define STM32H7_EOC_SLV BIT(18)
+#define STM32H7_AWD3_MST BIT(9)
+#define STM32H7_AWD2_MST BIT(8)
+#define STM32H7_AWD1_MST BIT(7)
+#define STM32H7_JEOS_MST BIT(6)
+#define STM32H7_OVR_MST BIT(4)
#define STM32H7_EOC_MST BIT(2)
+#define STM32H7_EOC_MASK1 (STM32H7_EOC_MST | STM32H7_AWD1_MST | \
+ STM32H7_AWD2_MST | STM32H7_AWD3_MST | \
+ STM32H7_OVR_MST)
+#define STM32H7_EOC_MASK2 (STM32H7_EOC_SLV | STM32H7_AWD1_SLV | \
+ STM32H7_AWD2_SLV | STM32H7_AWD3_SLV | \
+ STM32H7_OVR_SLV)
+#define STM32H7_JEOC_MASK1 (STM32H7_JEOS_MST | STM32H7_AWD1_MST | \
+ STM32H7_AWD2_MST | STM32H7_AWD3_MST)
+#define STM32H7_JEOC_MASK2 (STM32H7_JEOS_SLV | STM32H7_AWD1_SLV | \
+ STM32H7_AWD2_SLV | STM32H7_AWD3_SLV)
/* STM32H7_ADC_CCR - bit fields */
#define STM32H7_PRESC_SHIFT 18
@@ -48,18 +90,32 @@
#define STM32H7_CKMODE_SHIFT 16
#define STM32H7_CKMODE_MASK GENMASK(17, 16)
+#define STM32_ADC_CORE_SLEEP_DELAY_MS 2000
+
/**
* stm32_adc_common_regs - stm32 common registers, compatible dependent data
* @csr: common status register offset
+ * @ccr: common control register offset
* @eoc1: adc1 end of conversion flag in @csr
* @eoc2: adc2 end of conversion flag in @csr
* @eoc3: adc3 end of conversion flag in @csr
+ * @jeoc1: adc1 end of injected conversion flag in @csr
+ * @jeoc2: adc2 end of injected conversion flag in @csr
+ * @jeoc3: adc3 end of injected conversion flag in @csr
+ * @ier: interrupt enable register offset for each adc
+ * @eocie_msk: end of conversion interrupt enable mask in @ier
*/
struct stm32_adc_common_regs {
u32 csr;
+ u32 ccr;
u32 eoc1_msk;
u32 eoc2_msk;
u32 eoc3_msk;
+ u32 jeoc1_msk;
+ u32 jeoc2_msk;
+ u32 jeoc3_msk;
+ u32 ier;
+ u32 eocie_msk;
};
struct stm32_adc_priv;
@@ -69,11 +125,27 @@ struct stm32_adc_priv;
* @regs: common registers for all instances
* @clk_sel: clock selection routine
* @max_clk_rate_hz: maximum analog clock rate (Hz, from datasheet)
+ * @has_syscfg_clr: analog switch control use set and clear registers
+ * @exti_trigs EXTI triggers info
*/
struct stm32_adc_priv_cfg {
const struct stm32_adc_common_regs *regs;
int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *);
u32 max_clk_rate_hz;
+ int has_syscfg_clr;
+ struct stm32_adc_trig_info *exti_trigs;
+};
+
+/**
+ * stm32_adc_syscfg - stm32 ADC SYSCFG data
+ * @regmap: reference to syscon
+ * @reg: register offset within SYSCFG
+ * @mask: bitmask within SYSCFG register
+ */
+struct stm32_adc_syscfg {
+ struct regmap *regmap;
+ u32 reg;
+ u32 mask;
};
/**
@@ -82,18 +154,32 @@ struct stm32_adc_priv_cfg {
* @domain: irq domain reference
* @aclk: clock reference for the analog circuitry
* @bclk: bus clock common for all ADCs, depends on part used
+ * @max_clk_rate desired maximum clock rate
* @vref: regulator reference
* @cfg: compatible configuration data
* @common: common data for all ADC instances
+ * @ccr_bak: backup'ed CCR in low power mode
+ * @vbooster: BOOSTE syscfg / EN_BOOSTER syscfg set
+ * @vbooster_clr: EN_BOOSTER syscfg clear
+ * @anaswvdd: ANASWVDD syscfg set
+ * @anaswvdd_clr: ANASWVDD syscfg clear
*/
struct stm32_adc_priv {
int irq[STM32_ADC_MAX_ADCS];
struct irq_domain *domain;
struct clk *aclk;
struct clk *bclk;
+ u32 max_clk_rate;
+ struct regulator *vdd;
+ struct regulator *vdda;
struct regulator *vref;
const struct stm32_adc_priv_cfg *cfg;
struct stm32_adc_common common;
+ u32 ccr_bak;
+ struct stm32_adc_syscfg vbooster;
+ struct stm32_adc_syscfg vbooster_clr;
+ struct stm32_adc_syscfg anaswvdd;
+ struct stm32_adc_syscfg anaswvdd_clr;
};
static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com)
@@ -129,7 +215,7 @@ static int stm32f4_adc_clk_sel(struct platform_device *pdev,
}
for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
- if ((rate / stm32f4_pclk_div[i]) <= priv->cfg->max_clk_rate_hz)
+ if ((rate / stm32f4_pclk_div[i]) <= priv->max_clk_rate)
break;
}
if (i >= ARRAY_SIZE(stm32f4_pclk_div)) {
@@ -218,7 +304,7 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev,
if (ckmode)
continue;
- if ((rate / div) <= priv->cfg->max_clk_rate_hz)
+ if ((rate / div) <= priv->max_clk_rate)
goto out;
}
}
@@ -238,7 +324,7 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev,
if (!ckmode)
continue;
- if ((rate / div) <= priv->cfg->max_clk_rate_hz)
+ if ((rate / div) <= priv->max_clk_rate)
goto out;
}
@@ -265,18 +351,43 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev,
/* STM32F4 common registers definitions */
static const struct stm32_adc_common_regs stm32f4_adc_common_regs = {
.csr = STM32F4_ADC_CSR,
- .eoc1_msk = STM32F4_EOC1,
- .eoc2_msk = STM32F4_EOC2,
- .eoc3_msk = STM32F4_EOC3,
+ .ccr = STM32F4_ADC_CCR,
+ .eoc1_msk = STM32F4_EOC_MASK1,
+ .eoc2_msk = STM32F4_EOC_MASK2,
+ .eoc3_msk = STM32F4_EOC_MASK3,
+ .jeoc1_msk = STM32F4_JEOC_MASK1,
+ .jeoc2_msk = STM32F4_JEOC_MASK2,
+ .jeoc3_msk = STM32F4_JEOC_MASK3,
+ .ier = STM32F4_ADC_CR1,
+ .eocie_msk = STM32F4_EOCIE,
};
/* STM32H7 common registers definitions */
static const struct stm32_adc_common_regs stm32h7_adc_common_regs = {
.csr = STM32H7_ADC_CSR,
- .eoc1_msk = STM32H7_EOC_MST,
- .eoc2_msk = STM32H7_EOC_SLV,
+ .ccr = STM32H7_ADC_CCR,
+ .eoc1_msk = STM32H7_EOC_MASK1,
+ .eoc2_msk = STM32H7_EOC_MASK2,
+ .jeoc1_msk = STM32H7_JEOC_MASK1,
+ .jeoc2_msk = STM32H7_JEOC_MASK2,
+ .ier = STM32H7_ADC_IER,
+ .eocie_msk = STM32H7_EOCIE,
+};
+
+static const unsigned int stm32_adc_offset[STM32_ADC_MAX_ADCS] = {
+ 0, STM32_ADC_OFFSET, STM32_ADC_OFFSET * 2,
};
+static unsigned int stm32_adc_eoc_enabled(struct stm32_adc_priv *priv,
+ unsigned int adc)
+{
+ u32 ier, offset = stm32_adc_offset[adc];
+
+ ier = readl_relaxed(priv->common.base + offset + priv->cfg->regs->ier);
+
+ return ier & priv->cfg->regs->eocie_msk;
+}
+
/* ADC common interrupt for all instances */
static void stm32_adc_irq_handler(struct irq_desc *desc)
{
@@ -287,15 +398,39 @@ static void stm32_adc_irq_handler(struct irq_desc *desc)
chained_irq_enter(chip, desc);
status = readl_relaxed(priv->common.base + priv->cfg->regs->csr);
- if (status & priv->cfg->regs->eoc1_msk)
+ /*
+ * End of conversion may be handled by using IRQ or DMA. There may be a
+ * race here when two conversions complete at the same time on several
+ * ADCs. EOC may be read 'set' for several ADCs, with:
+ * - an ADC configured to use DMA (EOC triggers the DMA request, and
+ * is then automatically cleared by DR read in hardware)
+ * - an ADC configured to use IRQs (EOCIE bit is set. The handler must
+ * be called in this case)
+ * So both EOC status bit in CSR and EOCIE control bit must be checked
+ * before invoking the interrupt handler (e.g. call ISR only for
+ * IRQ-enabled ADCs).
+ */
+ if (status & priv->cfg->regs->eoc1_msk &&
+ stm32_adc_eoc_enabled(priv, 0))
generic_handle_irq(irq_find_mapping(priv->domain, 0));
- if (status & priv->cfg->regs->eoc2_msk)
+ if (status & priv->cfg->regs->eoc2_msk &&
+ stm32_adc_eoc_enabled(priv, 1))
generic_handle_irq(irq_find_mapping(priv->domain, 1));
- if (status & priv->cfg->regs->eoc3_msk)
+ if (status & priv->cfg->regs->eoc3_msk &&
+ stm32_adc_eoc_enabled(priv, 2))
generic_handle_irq(irq_find_mapping(priv->domain, 2));
+ if (status & priv->cfg->regs->jeoc1_msk)
+ generic_handle_irq(irq_find_mapping(priv->domain, 3));
+
+ if (status & priv->cfg->regs->jeoc2_msk)
+ generic_handle_irq(irq_find_mapping(priv->domain, 4));
+
+ if (status & priv->cfg->regs->jeoc3_msk)
+ generic_handle_irq(irq_find_mapping(priv->domain, 5));
+
chained_irq_exit(chip, desc);
};
@@ -344,7 +479,8 @@ static int stm32_adc_irq_probe(struct platform_device *pdev,
}
}
- priv->domain = irq_domain_add_simple(np, STM32_ADC_MAX_ADCS, 0,
+ /* 2 interrupt sources per ADC instance: regular & injected */
+ priv->domain = irq_domain_add_simple(np, STM32_ADC_MAX_ADCS * 2, 0,
&stm32_adc_domain_ops,
priv);
if (!priv->domain) {
@@ -368,7 +504,7 @@ static void stm32_adc_irq_remove(struct platform_device *pdev,
int hwirq;
unsigned int i;
- for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS; hwirq++)
+ for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS * 2; hwirq++)
irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq));
irq_domain_remove(priv->domain);
@@ -379,13 +515,415 @@ static void stm32_adc_irq_remove(struct platform_device *pdev,
}
}
+static struct stm32_adc_trig_info stm32f4_adc_exti_trigs[] = {
+ { "exti11", STM32_EXT15, 0, TRG_REGULAR },
+ { "exti15", 0, STM32_EXT15, TRG_INJECTED },
+ {},
+};
+
+static struct stm32_adc_trig_info stm32h7_adc_exti_trigs[] = {
+ { "exti11", STM32_EXT6, 0, TRG_REGULAR },
+ { "exti15", 0, STM32_EXT6, TRG_INJECTED },
+ {},
+};
+
+static int is_stm32_adc_child_dev(struct device *dev, void *data)
+{
+ return dev == data;
+}
+
+static int stm32_adc_validate_device(struct iio_trigger *trig,
+ struct iio_dev *indio_dev)
+{
+ /* Iterate over stm32 adc child devices, is indio_dev one of them ? */
+ if (device_for_each_child(trig->dev.parent, indio_dev->dev.parent,
+ is_stm32_adc_child_dev))
+ return 0;
+
+ return -EINVAL;
+}
+
+static const struct iio_trigger_ops stm32_adc_trigger_ops = {
+ .validate_device = stm32_adc_validate_device,
+};
+
+static irqreturn_t stm32_adc_trigger_isr(int irq, void *p)
+{
+ /* EXTI handler shouldn't be invoked, and isn't used */
+ return IRQ_HANDLED;
+}
+
+static struct iio_trigger *stm32_adc_trig_alloc_register(
+ struct platform_device *pdev,
+ struct stm32_adc_priv *priv,
+ struct stm32_adc_trig_info *trinfo)
+{
+ struct iio_trigger *trig;
+ int ret;
+
+ trig = devm_iio_trigger_alloc(&pdev->dev, "%s-%s", trinfo->name,
+ dev_name(&pdev->dev));
+ if (!trig)
+ return ERR_PTR(-ENOMEM);
+
+ trig->dev.parent = &pdev->dev;
+ trig->ops = &stm32_adc_trigger_ops;
+ iio_trigger_set_drvdata(trig, trinfo);
+
+ ret = devm_iio_trigger_register(&pdev->dev, trig);
+ if (ret) {
+ dev_err(&pdev->dev, "%s trig register failed\n", trinfo->name);
+ return ERR_PTR(ret);
+ }
+
+ list_add_tail(&trig->alloc_list, &priv->common.extrig_list);
+
+ return trig;
+}
+
+static int stm32_adc_triggers_probe(struct platform_device *pdev,
+ struct stm32_adc_priv *priv)
+{
+ struct device_node *child, *node = pdev->dev.of_node;
+ struct stm32_adc_trig_info *trinfo = priv->cfg->exti_trigs;
+ struct iio_trigger *trig;
+ int i, irq, ret;
+
+ INIT_LIST_HEAD(&priv->common.extrig_list);
+
+ for (i = 0; trinfo && trinfo[i].name; i++) {
+ for_each_available_child_of_node(node, child) {
+ if (of_property_match_string(child, "trigger-name",
+ trinfo[i].name) < 0)
+ continue;
+ trig = stm32_adc_trig_alloc_register(pdev, priv,
+ &trinfo[i]);
+ if (IS_ERR(trig))
+ return PTR_ERR(trig);
+
+ /*
+ * STM32 ADC can use EXTI GPIO (external interrupt line)
+ * as trigger source. EXTI line can generate IRQs and/or
+ * be used as trigger: EXTI line is hard wired as
+ * an input of ADC trigger selection MUX (muxed in with
+ * extsel on ADC controller side).
+ * Getting IRQs when trigger occurs is unused, rely on
+ * EOC interrupt instead. So, get EXTI IRQ, then mask it
+ * by default (on EXTI controller). After this, EXTI
+ * line HW path is configured (GPIO->EXTI->ADC),
+ */
+ irq = of_irq_get(child, 0);
+ if (irq <= 0) {
+ dev_err(&pdev->dev, "Can't get trigger irq\n");
+ return irq ? irq : -ENODEV;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq,
+ stm32_adc_trigger_isr, 0, NULL,
+ trig);
+ if (ret) {
+ dev_err(&pdev->dev, "Request IRQ failed\n");
+ return ret;
+ }
+ disable_irq(irq);
+ }
+ }
+
+ return 0;
+}
+
+static int stm32_adc_switches_supply_en(struct device *dev)
+{
+ struct stm32_adc_common *common = dev_get_drvdata(dev);
+ struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
+ int ret, vdda, vdd = 0;
+ u32 anaswvdd, en_booster;
+
+ /*
+ * On STM32H7 and STM32MP1, the ADC inputs are multiplexed with analog
+ * switches (e.g. PCSEL) which have reduced performances when their
+ * supply is below 2.7V (vdda by default):
+ * - Voltage booster can be used, to get full ADC performances
+ * (increases power consumption).
+ * - Vdd can be used if above 2.7V (STM32MP1 only).
+ *
+ * Make all this optional, since this is a trade-off between analog
+ * performance and power consumption.
+ */
+ if (IS_ERR(priv->vdda) || IS_ERR(priv->vbooster.regmap)) {
+ dev_dbg(dev, "%s: nothing to do\n", __func__);
+ return 0;
+ }
+
+ ret = regulator_enable(priv->vdda);
+ if (ret < 0) {
+ dev_err(dev, "vdda enable failed %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_get_voltage(priv->vdda);
+ if (ret < 0) {
+ dev_err(dev, "vdda get voltage failed %d\n", ret);
+ goto vdda_dis;
+ }
+ vdda = ret;
+
+ if (!IS_ERR(priv->vdd) && !IS_ERR(priv->anaswvdd.regmap)) {
+ ret = regulator_enable(priv->vdd);
+ if (ret < 0) {
+ dev_err(dev, "vdd enable failed %d\n", ret);
+ goto vdda_dis;
+ }
+
+ ret = regulator_get_voltage(priv->vdd);
+ if (ret < 0) {
+ dev_err(dev, "vdd get voltage failed %d\n", ret);
+ goto vdd_dis;
+ }
+ vdd = ret;
+ }
+
+ /*
+ * Recommended settings for ANASWVDD and EN_BOOSTER:
+ * - vdda > 2.7V: ANASWVDD = 0, EN_BOOSTER = 0
+ * - vdda < 2.7V and vdd < 2.7V: ANASWVDD = 0, EN_BOOSTER = 1
+ * - vdda < 2.7V but vdd > 2.7V: ANASWVDD = 1, EN_BOOSTER = 0 (stm32mp1)
+ */
+ if (vdda > 2700000) {
+ /* analog switches supplied by vdda (default) */
+ anaswvdd = 0;
+ en_booster = 0;
+ } else {
+ if (vdd < 2700000) {
+ /* Voltage booster enabled */
+ anaswvdd = 0;
+ en_booster = priv->vbooster.mask;
+ } else {
+ /* analog switches supplied by vdd */
+ anaswvdd = priv->anaswvdd.mask;
+ en_booster = 0;
+ }
+ }
+
+ dev_dbg(dev, "vdda=%d, vdd=%d, setting: en_booster=%x, anaswvdd=%x\n",
+ vdda, vdd, en_booster, anaswvdd);
+
+ /* direct write en_booster value (or use clear register) */
+ if (en_booster || IS_ERR(priv->vbooster_clr.regmap))
+ ret = regmap_update_bits(priv->vbooster.regmap,
+ priv->vbooster.reg,
+ priv->vbooster.mask, en_booster);
+ else
+ ret = regmap_write(priv->vbooster_clr.regmap,
+ priv->vbooster_clr.reg,
+ priv->vbooster_clr.mask);
+ if (ret) {
+ dev_err(dev, "can't access voltage booster, %d\n", ret);
+ goto vdd_dis;
+ }
+
+ /* Booster voltage can take up to 50 μs to stabilize */
+ if (en_booster)
+ usleep_range(50, 100);
+
+ if (!IS_ERR(priv->anaswvdd.regmap)) {
+ /* direct write anaswvdd value (or use clear register) */
+ if (anaswvdd || IS_ERR(priv->anaswvdd_clr.regmap))
+ ret = regmap_update_bits(priv->anaswvdd.regmap,
+ priv->anaswvdd.reg,
+ priv->anaswvdd.mask, anaswvdd);
+ else
+ ret = regmap_write(priv->anaswvdd_clr.regmap,
+ priv->anaswvdd_clr.reg,
+ priv->anaswvdd_clr.mask);
+ if (ret) {
+ dev_err(dev, "can't access anaswvdd, %d\n", ret);
+ goto booster_dis;
+ }
+ }
+
+ return ret;
+
+booster_dis:
+ if (IS_ERR(priv->vbooster_clr.regmap))
+ regmap_update_bits(priv->vbooster.regmap, priv->vbooster.reg,
+ priv->vbooster.mask, 0);
+ else
+ regmap_write(priv->vbooster_clr.regmap,
+ priv->vbooster_clr.reg,
+ priv->vbooster_clr.mask);
+vdd_dis:
+ if (!IS_ERR(priv->vdd) && !IS_ERR(priv->anaswvdd.regmap))
+ regulator_disable(priv->vdd);
+vdda_dis:
+ regulator_disable(priv->vdda);
+
+ return ret;
+}
+
+static void stm32_adc_switches_supply_dis(struct device *dev)
+{
+ struct stm32_adc_common *common = dev_get_drvdata(dev);
+ struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
+
+ if (IS_ERR(priv->vdda) || IS_ERR(priv->vbooster.regmap))
+ return;
+
+ if (!IS_ERR(priv->anaswvdd.regmap)) {
+ if (IS_ERR(priv->anaswvdd_clr.regmap))
+ regmap_update_bits(priv->anaswvdd.regmap,
+ priv->anaswvdd.reg,
+ priv->anaswvdd.mask, 0);
+ else
+ regmap_write(priv->anaswvdd_clr.regmap,
+ priv->anaswvdd_clr.reg,
+ priv->anaswvdd_clr.mask);
+ }
+
+ if (IS_ERR(priv->vbooster_clr.regmap))
+ regmap_update_bits(priv->vbooster.regmap, priv->vbooster.reg,
+ priv->vbooster.mask, 0);
+ else
+ regmap_write(priv->vbooster_clr.regmap,
+ priv->vbooster_clr.reg,
+ priv->vbooster_clr.mask);
+
+ if (!IS_ERR(priv->vdd) && !IS_ERR(priv->anaswvdd.regmap))
+ regulator_disable(priv->vdd);
+
+ regulator_disable(priv->vdda);
+}
+
+static int stm32_adc_core_hw_start(struct device *dev)
+{
+ struct stm32_adc_common *common = dev_get_drvdata(dev);
+ struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
+ int ret;
+
+ ret = stm32_adc_switches_supply_en(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = regulator_enable(priv->vref);
+ if (ret < 0) {
+ dev_err(dev, "vref enable failed\n");
+ goto err_switches_disable;
+ }
+
+ if (priv->bclk) {
+ ret = clk_prepare_enable(priv->bclk);
+ if (ret < 0) {
+ dev_err(dev, "bus clk enable failed\n");
+ goto err_regulator_disable;
+ }
+ }
+
+ if (priv->aclk) {
+ ret = clk_prepare_enable(priv->aclk);
+ if (ret < 0) {
+ dev_err(dev, "adc clk enable failed\n");
+ goto err_bclk_disable;
+ }
+ }
+
+ writel_relaxed(priv->ccr_bak, priv->common.base + priv->cfg->regs->ccr);
+
+ return 0;
+
+err_bclk_disable:
+ if (priv->bclk)
+ clk_disable_unprepare(priv->bclk);
+err_regulator_disable:
+ regulator_disable(priv->vref);
+err_switches_disable:
+ stm32_adc_switches_supply_dis(dev);
+
+ return ret;
+}
+
+static void stm32_adc_core_hw_stop(struct device *dev)
+{
+ struct stm32_adc_common *common = dev_get_drvdata(dev);
+ struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
+
+ /* Backup CCR that may be lost (depends on power state to achieve) */
+ priv->ccr_bak = readl_relaxed(priv->common.base + priv->cfg->regs->ccr);
+ if (priv->aclk)
+ clk_disable_unprepare(priv->aclk);
+ if (priv->bclk)
+ clk_disable_unprepare(priv->bclk);
+ regulator_disable(priv->vref);
+ stm32_adc_switches_supply_dis(dev);
+}
+
+static int stm32_adc_get_syscfg_cell(struct device_node *np,
+ struct stm32_adc_syscfg *syscfg,
+ const char * prop)
+{
+ int ret;
+
+ syscfg->regmap = syscon_regmap_lookup_by_phandle(np, prop);
+ if (IS_ERR(syscfg->regmap)) {
+ /* Optional */
+ if (PTR_ERR(syscfg->regmap) == -ENODEV)
+ return 0;
+ else
+ return PTR_ERR(syscfg->regmap);
+ }
+
+ ret = of_property_read_u32_index(np, prop, 1, &syscfg->reg);
+ if (ret)
+ return ret;
+
+ return of_property_read_u32_index(np, prop, 2, &syscfg->mask);
+}
+
+static int stm32_adc_syscfg_probe(struct platform_device *pdev,
+ struct stm32_adc_priv *priv)
+{
+ struct device_node *np = pdev->dev.of_node;
+ int ret;
+
+ /* Start to lookup BOOSTE/EN_BOOSTER first, for stm32h7/stm32mp1 */
+ ret = stm32_adc_get_syscfg_cell(np, &priv->vbooster,
+ "st,syscfg-vbooster");
+ if (ret)
+ return ret;
+
+ /* Continue with stm32mp1 EN_BOOSTER/ANASWVDD set and clear bits*/
+ ret = stm32_adc_get_syscfg_cell(np, &priv->vbooster_clr,
+ "st,syscfg-vbooster-clr");
+ if (ret)
+ return ret;
+
+ ret = stm32_adc_get_syscfg_cell(np, &priv->anaswvdd,
+ "st,syscfg-anaswvdd");
+ if (ret)
+ return ret;
+
+ ret = stm32_adc_get_syscfg_cell(np, &priv->anaswvdd_clr,
+ "st,syscfg-anaswvdd-clr");
+ if (ret)
+ return ret;
+
+ /* Sanity, check syscfg set/clear pairs are filled in */
+ if (priv->cfg->has_syscfg_clr && ((!IS_ERR(priv->vbooster.regmap) &&
+ IS_ERR(priv->vbooster_clr.regmap)) ||
+ (!IS_ERR(priv->anaswvdd.regmap) &&
+ IS_ERR(priv->anaswvdd_clr.regmap))))
+ return -EINVAL;
+
+ return ret;
+}
+
static int stm32_adc_probe(struct platform_device *pdev)
{
struct stm32_adc_priv *priv;
struct device *dev = &pdev->dev;
struct device_node *np = pdev->dev.of_node;
struct resource *res;
- int ret;
+ u32 max_rate;
+ int i, ret;
if (!pdev->dev.of_node)
return -ENODEV;
@@ -393,6 +931,7 @@ static int stm32_adc_probe(struct platform_device *pdev)
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
+ platform_set_drvdata(pdev, &priv->common);
priv->cfg = (const struct stm32_adc_priv_cfg *)
of_match_device(dev->driver->of_match_table, dev)->data;
@@ -402,6 +941,8 @@ static int stm32_adc_probe(struct platform_device *pdev)
if (IS_ERR(priv->common.base))
return PTR_ERR(priv->common.base);
priv->common.phys_base = res->start;
+ for (i = 0; i < STM32_ADC_MAX_ADCS; i++)
+ mutex_init(&priv->common.inj[i]);
priv->vref = devm_regulator_get(&pdev->dev, "vref");
if (IS_ERR(priv->vref)) {
@@ -410,67 +951,87 @@ static int stm32_adc_probe(struct platform_device *pdev)
return ret;
}
- ret = regulator_enable(priv->vref);
- if (ret < 0) {
- dev_err(&pdev->dev, "vref enable failed\n");
- return ret;
+ priv->vdda = devm_regulator_get_optional(&pdev->dev, "vdda");
+ if (IS_ERR(priv->vdda)) {
+ ret = PTR_ERR(priv->vdda);
+ if (ret != -ENODEV) {
+ dev_err(&pdev->dev, "vdda get failed, %d\n", ret);
+ return ret;
+ }
}
- ret = regulator_get_voltage(priv->vref);
- if (ret < 0) {
- dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
- goto err_regulator_disable;
+ priv->vdd = devm_regulator_get_optional(&pdev->dev, "vdd");
+ if (IS_ERR(priv->vdd)) {
+ ret = PTR_ERR(priv->vdd);
+ if (ret != -ENODEV) {
+ dev_err(&pdev->dev, "vdd get failed, %d\n", ret);
+ return ret;
+ }
}
- priv->common.vref_mv = ret / 1000;
- dev_dbg(&pdev->dev, "vref+=%dmV\n", priv->common.vref_mv);
priv->aclk = devm_clk_get(&pdev->dev, "adc");
if (IS_ERR(priv->aclk)) {
ret = PTR_ERR(priv->aclk);
- if (ret == -ENOENT) {
- priv->aclk = NULL;
- } else {
+ if (ret != -ENOENT) {
dev_err(&pdev->dev, "Can't get 'adc' clock\n");
- goto err_regulator_disable;
- }
- }
-
- if (priv->aclk) {
- ret = clk_prepare_enable(priv->aclk);
- if (ret < 0) {
- dev_err(&pdev->dev, "adc clk enable failed\n");
- goto err_regulator_disable;
+ return ret;
}
+ priv->aclk = NULL;
}
priv->bclk = devm_clk_get(&pdev->dev, "bus");
if (IS_ERR(priv->bclk)) {
ret = PTR_ERR(priv->bclk);
- if (ret == -ENOENT) {
- priv->bclk = NULL;
- } else {
+ if (ret != -ENOENT) {
dev_err(&pdev->dev, "Can't get 'bus' clock\n");
- goto err_aclk_disable;
+ return ret;
}
+ priv->bclk = NULL;
}
- if (priv->bclk) {
- ret = clk_prepare_enable(priv->bclk);
- if (ret < 0) {
- dev_err(&pdev->dev, "adc clk enable failed\n");
- goto err_aclk_disable;
- }
+ ret = stm32_adc_syscfg_probe(pdev, priv);
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Can't probe syscfg: %d\n", ret);
+ return ret;
}
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_set_autosuspend_delay(dev, STM32_ADC_CORE_SLEEP_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_enable(dev);
+
+ ret = stm32_adc_core_hw_start(dev);
+ if (ret)
+ goto err_pm_stop;
+
+ ret = regulator_get_voltage(priv->vref);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
+ goto err_hw_stop;
+ }
+ priv->common.vref_mv = ret / 1000;
+ dev_dbg(&pdev->dev, "vref+=%dmV\n", priv->common.vref_mv);
+
+ ret = of_property_read_u32(pdev->dev.of_node, "st,max-clk-rate-hz",
+ &max_rate);
+ if (!ret)
+ priv->max_clk_rate = min(max_rate, priv->cfg->max_clk_rate_hz);
+ else
+ priv->max_clk_rate = priv->cfg->max_clk_rate_hz;
+
ret = priv->cfg->clk_sel(pdev, priv);
if (ret < 0)
- goto err_bclk_disable;
+ goto err_hw_stop;
ret = stm32_adc_irq_probe(pdev, priv);
if (ret < 0)
- goto err_bclk_disable;
+ goto err_hw_stop;
- platform_set_drvdata(pdev, &priv->common);
+ ret = stm32_adc_triggers_probe(pdev, priv);
+ if (ret < 0)
+ goto err_irq_remove;
ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
if (ret < 0) {
@@ -478,21 +1039,19 @@ static int stm32_adc_probe(struct platform_device *pdev)
goto err_irq_remove;
}
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
return 0;
err_irq_remove:
stm32_adc_irq_remove(pdev, priv);
-
-err_bclk_disable:
- if (priv->bclk)
- clk_disable_unprepare(priv->bclk);
-
-err_aclk_disable:
- if (priv->aclk)
- clk_disable_unprepare(priv->aclk);
-
-err_regulator_disable:
- regulator_disable(priv->vref);
+err_hw_stop:
+ stm32_adc_core_hw_stop(dev);
+err_pm_stop:
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(dev);
return ret;
}
@@ -502,33 +1061,59 @@ static int stm32_adc_remove(struct platform_device *pdev)
struct stm32_adc_common *common = platform_get_drvdata(pdev);
struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
+ pm_runtime_get_sync(&pdev->dev);
of_platform_depopulate(&pdev->dev);
stm32_adc_irq_remove(pdev, priv);
- if (priv->bclk)
- clk_disable_unprepare(priv->bclk);
- if (priv->aclk)
- clk_disable_unprepare(priv->aclk);
- regulator_disable(priv->vref);
+ stm32_adc_core_hw_stop(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
return 0;
}
+#if defined(CONFIG_PM)
+static int stm32_adc_core_runtime_suspend(struct device *dev)
+{
+ stm32_adc_core_hw_stop(dev);
+
+ return 0;
+}
+
+static int stm32_adc_core_runtime_resume(struct device *dev)
+{
+ return stm32_adc_core_hw_start(dev);
+}
+#endif
+
+static const struct dev_pm_ops stm32_adc_core_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(stm32_adc_core_runtime_suspend,
+ stm32_adc_core_runtime_resume,
+ NULL)
+};
+
static const struct stm32_adc_priv_cfg stm32f4_adc_priv_cfg = {
.regs = &stm32f4_adc_common_regs,
.clk_sel = stm32f4_adc_clk_sel,
.max_clk_rate_hz = 36000000,
+ .exti_trigs = stm32f4_adc_exti_trigs,
};
static const struct stm32_adc_priv_cfg stm32h7_adc_priv_cfg = {
.regs = &stm32h7_adc_common_regs,
.clk_sel = stm32h7_adc_clk_sel,
.max_clk_rate_hz = 36000000,
+ .exti_trigs = stm32h7_adc_exti_trigs,
};
static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = {
.regs = &stm32h7_adc_common_regs,
.clk_sel = stm32h7_adc_clk_sel,
+ .has_syscfg_clr = true,
.max_clk_rate_hz = 40000000,
+ .exti_trigs = stm32h7_adc_exti_trigs,
};
static const struct of_device_id stm32_adc_of_match[] = {
@@ -552,6 +1137,7 @@ static struct platform_driver stm32_adc_driver = {
.driver = {
.name = "stm32-adc-core",
.of_match_table = stm32_adc_of_match,
+ .pm = &stm32_adc_core_pm_ops,
},
};
module_platform_driver(stm32_adc_driver);
diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h
index 8af507b..a7a4e54 100644
--- a/drivers/iio/adc/stm32-adc-core.h
+++ b/drivers/iio/adc/stm32-adc-core.h
@@ -25,20 +25,119 @@
* --------------------------------------------------------
*/
#define STM32_ADC_MAX_ADCS 3
+#define STM32_ADC_OFFSET 0x100
#define STM32_ADCX_COMN_OFFSET 0x300
+/* Number of linear calibration shadow registers / LINCALRDYW control bits */
+#define STM32H7_LINCALFACT_NUM 6
+
+/**
+ * struct stm32_adc_calib - optional adc calibration data
+ * @calfact_s: Calibration offset for single ended channels
+ * @calfact_d: Calibration offset in differential
+ * @lincalfact: Linearity calibration factor
+ * @calibrated: Indicates calibration status
+ */
+struct stm32_adc_calib {
+ u32 calfact_s;
+ u32 calfact_d;
+ u32 lincalfact[STM32H7_LINCALFACT_NUM];
+ bool calibrated;
+};
+
+/* STM32F4 - registers for each ADC instance */
+#define STM32F4_ADC_CR1 0x04
+
+/* STM32F4_ADC_CR1 - bit fields */
+#define STM32F4_EOCIE BIT(5)
+
+/* STM32F4 - common registers for all ADC instances: 1, 2 & 3 */
+#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04)
+
+/* STM32F4_ADC_CCR - bit fields */
+#define STM32F4_ADC_TSVREFE BIT(23)
+
+/* STM32H7 - registers for each instance */
+#define STM32H7_ADC_IER 0x04
+
+/* STM32H7_ADC_IER - bit fields */
+#define STM32H7_EOCIE BIT(2)
+
+/* STM32H7 - common registers for all ADC instances */
+#define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08)
+
+/* STM32H7_ADC_CCR - bit fields */
+#define STM32H7_VSENSEEN BIT(23)
+
/**
* struct stm32_adc_common - stm32 ADC driver common data (for all instances)
* @base: control registers base cpu addr
* @phys_base: control registers base physical addr
* @rate: clock rate used for analog circuitry
* @vref_mv: vref voltage (mv)
+ * @extrig_list: External trigger list registered by adc core
+ *
+ * Reserved variables for child devices, shared between regular/injected:
+ * @difsel bitmask array to set single-ended/differential channel
+ * @pcsel bitmask array to preselect channels on some devices
+ * @smpr_val: sampling time settings (e.g. smpr1 / smpr2)
*/
struct stm32_adc_common {
void __iomem *base;
phys_addr_t phys_base;
unsigned long rate;
int vref_mv;
+ struct list_head extrig_list;
+ u32 difsel[STM32_ADC_MAX_ADCS];
+ u32 pcsel[STM32_ADC_MAX_ADCS];
+ u32 smpr_val[STM32_ADC_MAX_ADCS][2];
+ int prepcnt[STM32_ADC_MAX_ADCS];
+ struct mutex inj[STM32_ADC_MAX_ADCS]; /* injected */
+ struct stm32_adc_calib cal[STM32_ADC_MAX_ADCS];
+};
+
+/* extsel - trigger mux selection value */
+enum stm32_adc_extsel {
+ STM32_EXT0,
+ STM32_EXT1,
+ STM32_EXT2,
+ STM32_EXT3,
+ STM32_EXT4,
+ STM32_EXT5,
+ STM32_EXT6,
+ STM32_EXT7,
+ STM32_EXT8,
+ STM32_EXT9,
+ STM32_EXT10,
+ STM32_EXT11,
+ STM32_EXT12,
+ STM32_EXT13,
+ STM32_EXT14,
+ STM32_EXT15,
+ STM32_EXT16,
+ STM32_EXT17,
+ STM32_EXT18,
+ STM32_EXT19,
+ STM32_EXT20,
+};
+
+/* trigger information flags */
+#define TRG_REGULAR BIT(0)
+#define TRG_INJECTED BIT(1)
+#define TRG_BOTH (TRG_REGULAR | TRG_INJECTED)
+
+/**
+ * struct stm32_adc_trig_info - ADC trigger info
+ * @name: name of the trigger, corresponding to its source
+ * @extsel: regular trigger selection
+ * @jextsel: injected trigger selection
+ * @flags: trigger flags: e.g. for regular, injected or both
+ */
+struct stm32_adc_trig_info {
+ const char *name;
+ u32 extsel;
+ u32 jextsel;
+ u32 flags;
};
#endif
diff --git a/drivers/iio/adc/stm32-adc-temp.c b/drivers/iio/adc/stm32-adc-temp.c
new file mode 100644
index 0000000..dd31916
--- /dev/null
+++ b/drivers/iio/adc/stm32-adc-temp.c
@@ -0,0 +1,412 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * STM32 ADC temperature sensor driver.
+ * This file is part of STM32 ADC driver.
+ *
+ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com> for STMicroelectronics.
+ */
+
+#include <linux/delay.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+
+#include "stm32-adc-core.h"
+
+#define STM32_ADC_TEMP_SUSPEND_DELAY_MS 2000
+
+/*
+ * stm32_adc_temp_cfg - stm32 temperature compatible configuration data
+ * @avg_slope: temp sensor average slope (uV/deg: from datasheet)
+ * @ts: temp sensor sample temperature (°C: from datasheet)
+ * @vts: temp sensor voltage @ts (mV: V25 or V30 in datasheet)
+ * @en_bit: temp sensor enable bits
+ * @en_reg: temp sensor enable register
+ * @ts_cal_bits: temp sensor ts_cal[1..2] raw resolution (bits)
+ * @t1: ts_cal1 sample temperature (°C: from datasheet)
+ * @t2: ts_cal2 sample temperature (°C: from datasheet)
+ */
+struct stm32_adc_temp_cfg {
+ unsigned int avg_slope;
+ unsigned int ts;
+ unsigned int vts;
+ unsigned int en_bit;
+ unsigned int en_reg;
+ unsigned int ts_cal_bits;
+ unsigned int t1;
+ unsigned int t2;
+};
+
+/*
+ * stm32_adc_temp - private data of STM32 ADC temperature sensor driver
+ * @base: control registers base cpu addr
+ * @cfg: compatible configuration data
+ * @temp_offset: temperature sensor offset
+ * @temp_scale: temperature sensor scale
+ * @realbits: adc resolution
+ * @ts_chan temperature sensor ADC channel (consumer)
+ * @tzd: thermal zone device
+ */
+struct stm32_adc_temp {
+ void __iomem *base;
+ const struct stm32_adc_temp_cfg *cfg;
+ int temp_offset;
+ int temp_scale;
+ int realbits;
+ struct iio_channel *ts_chan;
+ struct thermal_zone_device *tzd;
+};
+
+static const struct iio_chan_spec stm32_adc_temp_channel = {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .datasheet_name = "adc_temp",
+};
+
+static int stm32_adc_temp_get_temp(void *data, int *temp)
+{
+ struct stm32_adc_temp *priv = data;
+ struct iio_dev *indio_dev = iio_priv_to_dev(priv);
+ struct device *dev = indio_dev->dev.parent;
+ s64 val64;
+ int sense, ret;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(dev);
+ return ret;
+ }
+
+ ret = iio_read_channel_raw(priv->ts_chan, &sense);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ if (ret != IIO_VAL_INT)
+ return ret < 0 ? ret : -EINVAL;
+
+ val64 = (s64)(sense + priv->temp_offset) * priv->temp_scale;
+ *temp = val64 >> priv->realbits;
+
+ return 0;
+}
+
+static const struct thermal_zone_of_device_ops stm32_adc_tzd_ops = {
+ .get_temp = &stm32_adc_temp_get_temp,
+};
+
+static int stm32_adc_temp_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct stm32_adc_temp *priv = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(dev);
+ return ret;
+ }
+ ret = iio_read_channel_raw(priv->ts_chan, val);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ return ret;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = priv->temp_scale;
+ *val2 = priv->realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_OFFSET:
+ *val = priv->temp_offset;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info stm32_adc_temp_iio_info = {
+ .read_raw = stm32_adc_temp_read_raw,
+};
+
+static void stm32_adc_temp_set_enable_state(struct device *dev, bool en)
+{
+ struct stm32_adc_temp *priv = dev_get_drvdata(dev);
+ u32 val = readl_relaxed(priv->base + priv->cfg->en_reg);
+
+ if (en)
+ val |= priv->cfg->en_bit;
+ else
+ val &= ~priv->cfg->en_bit;
+ writel_relaxed(val, priv->base + priv->cfg->en_reg);
+
+ /*
+ * Temperature sensor "startup time" from datasheet, must be greater
+ * than slowest supported device.
+ */
+ if (en)
+ usleep_range(40, 50);
+}
+
+static int stm32_adc_temp_setup_offset_scale(struct platform_device *pdev,
+ struct stm32_adc_temp *priv)
+{
+ int scale_type, vref_mv, ret;
+ u16 ts_cal1 = 0, ts_cal2 = 0;
+ unsigned int slope, vts, ts;
+ u64 val64;
+
+ scale_type = iio_read_channel_scale(priv->ts_chan, &vref_mv,
+ &priv->realbits);
+ if (scale_type != IIO_VAL_FRACTIONAL_LOG2)
+ return scale_type < 0 ? scale_type : -EINVAL;
+
+ /* Optional read of temperature sensor calibration data */
+ ret = nvmem_cell_read_u16(&pdev->dev, "ts_cal1", &ts_cal1);
+ if (ret && ret != -ENOENT && ret != -ENOSYS)
+ return ret;
+ ret = nvmem_cell_read_u16(&pdev->dev, "ts_cal2", &ts_cal2);
+ if (ret && ret != -ENOENT && ret != -ENOSYS)
+ return ret;
+
+ if (!ts_cal1 || !ts_cal2) {
+ /* Use datasheet temperature sensor characteristics */
+ slope = priv->cfg->avg_slope;
+ ts = priv->cfg->ts;
+ vts = priv->cfg->vts;
+ } else {
+ /*
+ * Compute average slope (µV/°C) from calibration data:
+ * - ts_cal1: raw data @t1(°C), factory vref = 3.3V
+ * - ts_cal2: raw data @t2(°C), factory vref = 3.3V
+ */
+ slope = ts_cal2 - ts_cal1;
+ slope *= 3300000 / (priv->cfg->t2 - priv->cfg->t1);
+ slope >>= priv->cfg->ts_cal_bits;
+ ts = priv->cfg->t1;
+ vts = ts_cal1 * 3300;
+ vts >>= priv->cfg->ts_cal_bits;
+ }
+
+ dev_dbg(&pdev->dev, "ts_cal1=%x ts_cal2=%x slope=%d vts=%d\n",
+ ts_cal1, ts_cal2, slope, vts);
+
+ priv->temp_scale = vref_mv * 1000000 / slope;
+
+ /*
+ * T (°C) = (Vsense - V25) / AVG_slope + 25.
+ * raw offset to subtract @0°C: Vts0 = V25 - 25 * AVG_slope
+ */
+ val64 = ts << priv->realbits;
+ priv->temp_offset = div_u64(val64 * (u64)slope, 1000LL);
+ priv->temp_offset -= vts << priv->realbits;
+ priv->temp_offset /= vref_mv;
+
+ return 0;
+}
+
+static int stm32_adc_temp_probe(struct platform_device *pdev)
+{
+ struct stm32_adc_common *common = dev_get_drvdata(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
+ struct stm32_adc_temp *priv;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ indio_dev->name = dev_name(dev);
+ indio_dev->dev.parent = dev;
+ indio_dev->dev.of_node = pdev->dev.of_node;
+ indio_dev->info = &stm32_adc_temp_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = &stm32_adc_temp_channel;
+ indio_dev->num_channels = 1;
+
+ priv = iio_priv(indio_dev);
+ priv->base = common->base;
+ priv->cfg = (const struct stm32_adc_temp_cfg *)
+ of_match_device(dev->driver->of_match_table, dev)->data;
+
+ priv->ts_chan = devm_iio_channel_get(dev, NULL);
+ platform_set_drvdata(pdev, priv);
+ if (IS_ERR(priv->ts_chan)) {
+ ret = PTR_ERR(priv->ts_chan);
+ dev_err(&indio_dev->dev, "Can't get temp channel: %d\n", ret);
+ return ret;
+ }
+
+ ret = stm32_adc_temp_setup_offset_scale(pdev, priv);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Can't get offset/scale: %d\n", ret);
+ return ret;
+ }
+
+ /* Get stm32-adc-core PM online */
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_set_autosuspend_delay(dev, STM32_ADC_TEMP_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_enable(dev);
+
+ /* Power-on temperature sensor */
+ stm32_adc_temp_set_enable_state(dev, true);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Can't register iio dev: %d\n", ret);
+ goto fail;
+ }
+
+ priv->tzd = thermal_zone_of_sensor_register(dev, 0, priv,
+ &stm32_adc_tzd_ops);
+ /* Optional thermal zone device */
+ if (IS_ERR(priv->tzd)) {
+ ret = PTR_ERR(priv->tzd);
+ if (ret != -ENOENT && ret != -ENODEV) {
+ dev_err(&indio_dev->dev, "Can't register thermal: %d\n",
+ ret);
+ goto unreg;
+ }
+ dev_dbg(&indio_dev->dev, "Not using thermal: %d\n", ret);
+ priv->tzd = NULL;
+ }
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+
+unreg:
+ iio_device_unregister(indio_dev);
+
+fail:
+ stm32_adc_temp_set_enable_state(dev, false);
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(dev);
+
+ return ret;
+}
+
+static int stm32_adc_temp_remove(struct platform_device *pdev)
+{
+ struct stm32_adc_temp *priv = platform_get_drvdata(pdev);
+ struct iio_dev *indio_dev = iio_priv_to_dev(priv);
+
+ pm_runtime_get_sync(&pdev->dev);
+ if (priv->tzd)
+ thermal_zone_of_sensor_unregister(&pdev->dev, priv->tzd);
+ iio_device_unregister(indio_dev);
+ stm32_adc_temp_set_enable_state(&pdev->dev, false);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+
+ return 0;
+}
+
+#if defined(CONFIG_PM)
+static int stm32_adc_temp_runtime_suspend(struct device *dev)
+{
+ stm32_adc_temp_set_enable_state(dev, false);
+
+ return 0;
+}
+
+static int stm32_adc_temp_runtime_resume(struct device *dev)
+{
+ stm32_adc_temp_set_enable_state(dev, true);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops stm32_adc_temp_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(stm32_adc_temp_runtime_suspend,
+ stm32_adc_temp_runtime_resume,
+ NULL)
+};
+
+static const struct stm32_adc_temp_cfg stm32f4_adc_temp_cfg = {
+ .avg_slope = 2500,
+ .ts = 25,
+ .vts = 760, /* V25 from datasheet */
+ .en_reg = STM32F4_ADC_CCR,
+ .en_bit = STM32F4_ADC_TSVREFE,
+ .ts_cal_bits = 12,
+ .t1 = 30, /* ts_cal1 @30°C */
+ .t2 = 110, /* ts_cal2 @110°C */
+};
+
+static const struct stm32_adc_temp_cfg stm32h7_adc_temp_cfg = {
+ .avg_slope = 2000,
+ .ts = 30,
+ .vts = 620, /* V30 from datasheet */
+ .en_reg = STM32H7_ADC_CCR,
+ .en_bit = STM32H7_VSENSEEN,
+ .ts_cal_bits = 16,
+ .t1 = 30, /* ts_cal1 @30°C */
+ .t2 = 110, /* ts_cal2 @110°C */
+};
+
+/* TODO: update typical values when confirmed for stm32mp1 */
+static const struct stm32_adc_temp_cfg stm32mp1_adc_temp_cfg = {
+ .avg_slope = 2050,
+ .ts = 30,
+ .vts = 620, /* V30 from datasheet */
+ .en_reg = STM32H7_ADC_CCR,
+ .en_bit = STM32H7_VSENSEEN,
+ .ts_cal_bits = 16,
+ .t1 = 30, /* ts_cal1 @30°C */
+ .t2 = 130, /* ts_cal2 @130°C */
+};
+
+static const struct of_device_id stm32_adc_temp_of_match[] = {
+ {
+ .compatible = "st,stm32f4-adc-temp",
+ .data = (void *)&stm32f4_adc_temp_cfg,
+ },
+ {
+ .compatible = "st,stm32h7-adc-temp",
+ .data = (void *)&stm32h7_adc_temp_cfg,
+ },
+ {
+ .compatible = "st,stm32mp1-adc-temp",
+ .data = (void *)&stm32mp1_adc_temp_cfg,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32_adc_temp_of_match);
+
+static struct platform_driver stm32_adc_temp_driver = {
+ .probe = stm32_adc_temp_probe,
+ .remove = stm32_adc_temp_remove,
+ .driver = {
+ .name = "stm32-adc-temp",
+ .of_match_table = of_match_ptr(stm32_adc_temp_of_match),
+ .pm = &stm32_adc_temp_pm_ops,
+ },
+};
+module_platform_driver(stm32_adc_temp_driver);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 ADC Temperature IIO driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:stm32-adc-temp");
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 3784118..e82d4c5 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -12,6 +12,7 @@
#include <linux/dmaengine.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
#include <linux/iio/timer/stm32-lptim-trigger.h>
#include <linux/iio/timer/stm32-timer-trigger.h>
#include <linux/iio/trigger.h>
@@ -20,8 +21,10 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
+#include <linux/irq_work.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/of_device.h>
@@ -29,7 +32,6 @@
/* STM32F4 - Registers for each ADC instance */
#define STM32F4_ADC_SR 0x00
-#define STM32F4_ADC_CR1 0x04
#define STM32F4_ADC_CR2 0x08
#define STM32F4_ADC_SMPR1 0x0C
#define STM32F4_ADC_SMPR2 0x10
@@ -46,14 +48,25 @@
#define STM32F4_ADC_DR 0x4C
/* STM32F4_ADC_SR - bit fields */
+#define STM32F4_OVR BIT(5)
#define STM32F4_STRT BIT(4)
+#define STM32F4_JSTRT BIT(3)
+#define STM32F4_JEOC BIT(2)
#define STM32F4_EOC BIT(1)
+#define STM32F4_AWD BIT(0)
/* STM32F4_ADC_CR1 - bit fields */
#define STM32F4_RES_SHIFT 24
+#define STM32F4_OVRIE BIT(26)
#define STM32F4_RES_MASK GENMASK(25, 24)
+#define STM32F4_AWDEN BIT(23)
+#define STM32F4_JAWDEN BIT(22)
+#define STM32F4_AWDSGL BIT(9)
#define STM32F4_SCAN BIT(8)
-#define STM32F4_EOCIE BIT(5)
+#define STM32F4_JEOCIE BIT(7)
+#define STM32F4_AWDIE BIT(6)
+#define STM32F4_AWDCH_SHIFT 0
+#define STM32F4_AWDCH_MASK GENMASK(4, 0)
/* STM32F4_ADC_CR2 - bit fields */
#define STM32F4_SWSTART BIT(30)
@@ -61,6 +74,11 @@
#define STM32F4_EXTEN_MASK GENMASK(29, 28)
#define STM32F4_EXTSEL_SHIFT 24
#define STM32F4_EXTSEL_MASK GENMASK(27, 24)
+#define STM32F4_JSWSTART BIT(22)
+#define STM32F4_JEXTEN_SHIFT 20
+#define STM32F4_JEXTEN_MASK GENMASK(21, 20)
+#define STM32F4_JEXTSEL_SHIFT 16
+#define STM32F4_JEXTSEL_MASK GENMASK(19, 16)
#define STM32F4_EOCS BIT(10)
#define STM32F4_DDS BIT(9)
#define STM32F4_DMA BIT(8)
@@ -68,28 +86,49 @@
/* STM32H7 - Registers for each ADC instance */
#define STM32H7_ADC_ISR 0x00
-#define STM32H7_ADC_IER 0x04
#define STM32H7_ADC_CR 0x08
#define STM32H7_ADC_CFGR 0x0C
#define STM32H7_ADC_SMPR1 0x14
#define STM32H7_ADC_SMPR2 0x18
#define STM32H7_ADC_PCSEL 0x1C
+#define STM32H7_ADC_LTR1 0x20
+#define STM32H7_ADC_HTR1 0x24
#define STM32H7_ADC_SQR1 0x30
#define STM32H7_ADC_SQR2 0x34
#define STM32H7_ADC_SQR3 0x38
#define STM32H7_ADC_SQR4 0x3C
#define STM32H7_ADC_DR 0x40
+#define STM32H7_ADC_JSQR 0x4C
+#define STM32H7_ADC_JDR1 0x80
+#define STM32H7_ADC_JDR2 0x84
+#define STM32H7_ADC_JDR3 0x88
+#define STM32H7_ADC_JDR4 0x8C
+#define STM32H7_ADC_AWD2CR 0xA0
+#define STM32H7_ADC_AWD3CR 0xA4
+#define STM32H7_ADC_LTR2 0xB0
+#define STM32H7_ADC_HTR2 0xB4
+#define STM32H7_ADC_LTR3 0xB8
+#define STM32H7_ADC_HTR3 0xBC
#define STM32H7_ADC_DIFSEL 0xC0
#define STM32H7_ADC_CALFACT 0xC4
#define STM32H7_ADC_CALFACT2 0xC8
/* STM32H7_ADC_ISR - bit fields */
#define STM32MP1_VREGREADY BIT(12)
+#define STM32H7_AWD3 BIT(9)
+#define STM32H7_AWD2 BIT(8)
+#define STM32H7_AWD1 BIT(7)
+#define STM32H7_JEOS BIT(6)
+#define STM32H7_OVR BIT(4)
#define STM32H7_EOC BIT(2)
#define STM32H7_ADRDY BIT(0)
/* STM32H7_ADC_IER - bit fields */
-#define STM32H7_EOCIE STM32H7_EOC
+#define STM32H7_AWD3IE STM32H7_AWD3
+#define STM32H7_AWD2IE STM32H7_AWD2
+#define STM32H7_AWD1IE STM32H7_AWD1
+#define STM32H7_JEOSIE STM32H7_JEOS
+#define STM32H7_OVRIE STM32H7_OVR
/* STM32H7_ADC_CR - bit fields */
#define STM32H7_ADCAL BIT(31)
@@ -104,12 +143,19 @@
#define STM32H7_LINCALRDYW1 BIT(22)
#define STM32H7_ADCALLIN BIT(16)
#define STM32H7_BOOST BIT(8)
+#define STM32H7_JADSTP BIT(5)
#define STM32H7_ADSTP BIT(4)
+#define STM32H7_JADSTART BIT(3)
#define STM32H7_ADSTART BIT(2)
#define STM32H7_ADDIS BIT(1)
#define STM32H7_ADEN BIT(0)
/* STM32H7_ADC_CFGR bit fields */
+#define STM32H7_AWD1CH_SHIFT 26
+#define STM32H7_AWD1CH_MASK GENMASK(30, 26)
+#define STM32H7_JAWD1EN BIT(24)
+#define STM32H7_AWD1EN BIT(23)
+#define STM32H7_AWD1SGL BIT(22)
#define STM32H7_EXTEN_SHIFT 10
#define STM32H7_EXTEN_MASK GENMASK(11, 10)
#define STM32H7_EXTSEL_SHIFT 5
@@ -126,6 +172,12 @@ enum stm32h7_adc_dmngt {
STM32H7_DMNGT_DMA_CIRC, /* DMA circular mode */
};
+/* STM32H7_ADC_JSQR - bit fields */
+#define STM32H7_JEXTEN_SHIFT 7
+#define STM32H7_JEXTEN_MASK GENMASK(8, 7)
+#define STM32H7_JEXTSEL_SHIFT 2
+#define STM32H7_JEXTSEL_MASK GENMASK(6, 2)
+
/* STM32H7_ADC_CALFACT - bit fields */
#define STM32H7_CALFACT_D_SHIFT 16
#define STM32H7_CALFACT_D_MASK GENMASK(26, 16)
@@ -136,18 +188,17 @@ enum stm32h7_adc_dmngt {
#define STM32H7_LINCALFACT_SHIFT 0
#define STM32H7_LINCALFACT_MASK GENMASK(29, 0)
-/* Number of linear calibration shadow registers / LINCALRDYW control bits */
-#define STM32H7_LINCALFACT_NUM 6
-
/* 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_JSQ 4 /* JSQ1..JSQ4 */
#define STM32_ADC_MAX_SMP 7 /* SMPx range is [0..7] */
#define STM32_ADC_TIMEOUT_US 100000
#define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
+#define STM32_ADC_HW_STOP_DELAY_MS 100
#define STM32_DMA_BUFFER_SIZE PAGE_SIZE
@@ -159,53 +210,6 @@ enum stm32_adc_exten {
STM32_EXTEN_HWTRIG_BOTH_EDGES,
};
-/* extsel - trigger mux selection value */
-enum stm32_adc_extsel {
- STM32_EXT0,
- STM32_EXT1,
- STM32_EXT2,
- STM32_EXT3,
- STM32_EXT4,
- STM32_EXT5,
- STM32_EXT6,
- STM32_EXT7,
- STM32_EXT8,
- STM32_EXT9,
- STM32_EXT10,
- STM32_EXT11,
- STM32_EXT12,
- STM32_EXT13,
- STM32_EXT14,
- STM32_EXT15,
- STM32_EXT16,
- STM32_EXT17,
- STM32_EXT18,
- STM32_EXT19,
- STM32_EXT20,
-};
-
-/**
- * struct stm32_adc_trig_info - ADC trigger info
- * @name: name of the trigger, corresponding to its source
- * @extsel: trigger selection
- */
-struct stm32_adc_trig_info {
- const char *name;
- enum stm32_adc_extsel extsel;
-};
-
-/**
- * struct stm32_adc_calib - optional adc calibration data
- * @calfact_s: Calibration offset for single ended channels
- * @calfact_d: Calibration offset in differential
- * @lincalfact: Linearity calibration factor
- */
-struct stm32_adc_calib {
- u32 calfact_s;
- u32 calfact_d;
- u32 lincalfact[STM32H7_LINCALFACT_NUM];
-};
-
/**
* stm32_adc_regs - stm32 ADC misc registers & bitfield desc
* @reg: register offset
@@ -219,27 +223,73 @@ struct stm32_adc_regs {
};
/**
+ * struct stm32_adc_awd_reginfo - stm32 ADC analog watchdog regs desc
+ * @reg: awd control register offset
+ * @en_bits: ADW enable bits for regular conversions, in @reg
+ * @jen_bits: ADW enable bits for injected conversions, in @reg
+ * @awdch_mask: AWDCH bitfield mask, in @reg
+ * @awdch_shift: AWDCH shift, in @reg
+ * @htr: High threshold register offset
+ * @ltr: Low threshold register offset
+ * @ier_msk: interrupt enable bit mask in ier register
+ * @isr_msk: interrupt status bit mask in isr register
+ */
+struct stm32_adc_awd_reginfo {
+ u32 reg;
+ u32 en_bits;
+ u32 jen_bits;
+ u32 awdch_mask;
+ u32 awdch_shift;
+ u32 htr;
+ u32 ltr;
+ u32 ier_msk;
+ u32 isr_msk;
+};
+
+/**
* stm32_adc_regspec - stm32 registers definition, compatible dependent data
* @dr: data register offset
+ * @jdr: injected data registers offsets
* @ier_eoc: interrupt enable register & eocie bitfield
+ * @ier_jeoc: interrupt enable register & jeocie bitfield
+ * @ier_ovr: interrupt enable register & overrun bitfield
* @isr_eoc: interrupt status register & eoc bitfield
+ * @isr_jeoc: interrupt status register & jeoc bitfield
+ * @isr_ovr: interrupt status register & overrun bitfield
* @sqr: reference to sequence registers array
+ * @jsqr: reference to injected sequence registers array
* @exten: trigger control register & bitfield
* @extsel: trigger selection register & bitfield
+ * @jexten: injected trigger control register & bitfield
+ * @jextsel: injected trigger selection register & bitfield
* @res: resolution selection register & bitfield
* @smpr: smpr1 & smpr2 registers offset array
* @smp_bits: smpr1 & smpr2 index and bitfields
+ * @write_one_to_clear: clear isr flags by writing one to it
+ * @awd_reginfo: Analog watchdog description
+ * @num_awd: Number of Analog watchdog
*/
struct stm32_adc_regspec {
const u32 dr;
+ const u32 jdr[4];
const struct stm32_adc_regs ier_eoc;
+ const struct stm32_adc_regs ier_jeoc;
+ const struct stm32_adc_regs ier_ovr;
const struct stm32_adc_regs isr_eoc;
+ const struct stm32_adc_regs isr_jeoc;
+ const struct stm32_adc_regs isr_ovr;
const struct stm32_adc_regs *sqr;
+ const struct stm32_adc_regs *jsqr;
const struct stm32_adc_regs exten;
const struct stm32_adc_regs extsel;
+ const struct stm32_adc_regs jexten;
+ const struct stm32_adc_regs jextsel;
const struct stm32_adc_regs res;
const u32 smpr[2];
const struct stm32_adc_regs *smp_bits;
+ const bool write_one_to_clear;
+ const struct stm32_adc_awd_reginfo *awd_reginfo;
+ unsigned int num_awd;
};
struct stm32_adc;
@@ -251,12 +301,12 @@ struct stm32_adc;
* @trigs: external trigger sources
* @clk_required: clock is required
* @has_vregready: vregready status flag presence
- * @selfcalib: optional routine for self-calibration
* @prepare: optional prepare routine (power-up, enable)
* @start_conv: routine to start conversions
* @stop_conv: routine to stop conversions
* @unprepare: optional unprepare routine (disable, power-down)
* @smp_cycles: programmable sampling time (ADC clock cycles)
+ * @is_started: routine to get adc 'started' state
*/
struct stm32_adc_cfg {
const struct stm32_adc_regspec *regs;
@@ -264,18 +314,39 @@ struct stm32_adc_cfg {
struct stm32_adc_trig_info *trigs;
bool clk_required;
bool has_vregready;
- int (*selfcalib)(struct stm32_adc *);
int (*prepare)(struct stm32_adc *);
void (*start_conv)(struct stm32_adc *, bool dma);
void (*stop_conv)(struct stm32_adc *);
void (*unprepare)(struct stm32_adc *);
const unsigned int *smp_cycles;
+ bool (*is_started)(struct stm32_adc *adc);
+};
+
+/**
+ * struct stm32_adc_evt - Configuration data for Analog watchdog events
+ * @list: event configuration list
+ * @awd_id: assigned AWD index
+ * @chan: IIO chan spec reference for this event
+ * @hthresh: High threshold value
+ * @lthresh: Low threshold value
+ * @enabled: Event enabled state
+ * @set: Flag, event has been assigned an AWD and has been set
+ */
+struct stm32_adc_evt {
+ struct list_head list;
+ int awd_id;
+ const struct iio_chan_spec *chan;
+ u32 hthresh;
+ u32 lthresh;
+ bool enabled;
+ bool set;
};
/**
* struct stm32_adc - private data of each ADC IIO instance
* @common: reference to ADC block common data
* @offset: ADC instance register offset in ADC block
+ * @id: ADC instance id from offset
* @cfg: compatible configuration data
* @completion: end of single conversion completion
* @buffer: data buffer
@@ -290,15 +361,16 @@ 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
+ * @injected: use injected channels on this adc
+ * @evt_list: list of all events configured for this ADC block
+ * @awd_mask: analog watchdog bitmask for this adc
+ * @work: irq work used to call trigger poll routine
*/
struct stm32_adc {
struct stm32_adc_common *common;
u32 offset;
+ u32 id;
const struct stm32_adc_cfg *cfg;
struct completion completion;
u16 buffer[STM32_ADC_MAX_SQ];
@@ -313,11 +385,11 @@ 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];
+ bool injected;
+ struct list_head evt_list;
+ u32 awd_mask;
+ struct irq_work work;
};
struct stm32_adc_diff_channel {
@@ -342,9 +414,9 @@ static const unsigned int stm32f4_adc_resolutions[] = {
12, 10, 8, 6,
};
-/* stm32f4 can have up to 16 channels */
+/* stm32f4 can have up to 19 channels (incl. 16 external sources) */
static const struct stm32_adc_info stm32f4_adc_info = {
- .max_channels = 16,
+ .max_channels = 19,
.resolutions = stm32f4_adc_resolutions,
.num_res = ARRAY_SIZE(stm32f4_adc_resolutions),
};
@@ -390,25 +462,58 @@ static const struct stm32_adc_regs stm32f4_sq[STM32_ADC_MAX_SQ + 1] = {
/* STM32F4 external trigger sources for all instances */
static struct stm32_adc_trig_info stm32f4_adc_trigs[] = {
- { TIM1_CH1, STM32_EXT0 },
- { TIM1_CH2, STM32_EXT1 },
- { TIM1_CH3, STM32_EXT2 },
- { TIM2_CH2, STM32_EXT3 },
- { TIM2_CH3, STM32_EXT4 },
- { TIM2_CH4, STM32_EXT5 },
- { TIM2_TRGO, STM32_EXT6 },
- { TIM3_CH1, STM32_EXT7 },
- { TIM3_TRGO, STM32_EXT8 },
- { TIM4_CH4, STM32_EXT9 },
- { TIM5_CH1, STM32_EXT10 },
- { TIM5_CH2, STM32_EXT11 },
- { TIM5_CH3, STM32_EXT12 },
- { TIM8_CH1, STM32_EXT13 },
- { TIM8_TRGO, STM32_EXT14 },
+ { TIM1_CH1, STM32_EXT0, 0, TRG_REGULAR },
+ { TIM1_CH2, STM32_EXT1, 0, TRG_REGULAR },
+ { TIM1_CH3, STM32_EXT2, 0, TRG_REGULAR },
+ { TIM2_CH2, STM32_EXT3, 0, TRG_REGULAR },
+ { TIM2_CH3, STM32_EXT4, 0, TRG_REGULAR },
+ { TIM2_CH4, STM32_EXT5, 0, TRG_REGULAR },
+ { TIM2_TRGO, STM32_EXT6, STM32_EXT3, TRG_BOTH },
+ { TIM3_CH1, STM32_EXT7, 0, TRG_REGULAR },
+ { TIM3_TRGO, STM32_EXT8, 0, TRG_REGULAR },
+ { TIM4_CH4, STM32_EXT9, 0, TRG_REGULAR },
+ { TIM5_CH1, STM32_EXT10, 0, TRG_REGULAR },
+ { TIM5_CH2, STM32_EXT11, 0, TRG_REGULAR },
+ { TIM5_CH3, STM32_EXT12, 0, TRG_REGULAR },
+ { TIM8_CH1, STM32_EXT13, 0, TRG_REGULAR },
+ { TIM8_TRGO, STM32_EXT14, 0, TRG_REGULAR },
+ { TIM1_CH4, 0, STM32_EXT0, TRG_INJECTED },
+ { TIM1_TRGO, 0, STM32_EXT1, TRG_INJECTED },
+ { TIM2_CH1, 0, STM32_EXT2, TRG_INJECTED },
+ { TIM3_CH2, 0, STM32_EXT4, TRG_INJECTED },
+ { TIM3_CH4, 0, STM32_EXT5, TRG_INJECTED },
+ { TIM4_CH1, 0, STM32_EXT6, TRG_INJECTED },
+ { TIM4_CH2, 0, STM32_EXT7, TRG_INJECTED },
+ { TIM4_CH3, 0, STM32_EXT8, TRG_INJECTED },
+ { TIM4_TRGO, 0, STM32_EXT9, TRG_INJECTED },
+ { TIM5_CH4, 0, STM32_EXT10, TRG_INJECTED },
+ { TIM5_TRGO, 0, STM32_EXT11, TRG_INJECTED },
+ { TIM8_CH2, 0, STM32_EXT12, TRG_INJECTED },
+ { TIM8_CH3, 0, STM32_EXT13, TRG_INJECTED },
+ { TIM8_CH4, 0, STM32_EXT14, TRG_INJECTED },
{}, /* sentinel */
};
/**
+ * stm32f4_jsq - describe injected sequence register:
+ * - JL: injected sequence len
+ * - JSQ4..SQ1: sequence entries
+ * When JL == 3, ADC converts JSQ1, JSQ2, JSQ3, JSQ4
+ * When JL == 2, ADC converts JSQ2, JSQ3, JSQ4
+ * When JL == 1, ADC converts JSQ3, JSQ4
+ * When JL == 0, ADC converts JSQ4
+ */
+static const struct stm32_adc_regs stm32f4_jsq[STM32_ADC_MAX_JSQ + 1] = {
+ /* JL: len bit field description to be kept as first element */
+ { STM32F4_ADC_JSQR, GENMASK(21, 20), 20 },
+ /* JSQ4..JSQ1 registers & bit fields (reg, mask, shift) */
+ { STM32F4_ADC_JSQR, GENMASK(19, 15), 15 },
+ { STM32F4_ADC_JSQR, GENMASK(14, 10), 10 },
+ { STM32F4_ADC_JSQR, GENMASK(9, 5), 5 },
+ { STM32F4_ADC_JSQR, GENMASK(4, 0), 0 },
+};
+
+/**
* stm32f4_smp_bits[] - describe sampling time register index & bit fields
* Sorted so it can be indexed by channel number.
*/
@@ -441,17 +546,46 @@ static const unsigned int stm32f4_adc_smp_cycles[STM32_ADC_MAX_SMP + 1] = {
3, 15, 28, 56, 84, 112, 144, 480,
};
+static const struct stm32_adc_awd_reginfo stm32f4_awd_reginfo = {
+ .reg = STM32F4_ADC_CR1,
+ .en_bits = STM32F4_AWDSGL | STM32F4_AWDEN,
+ .jen_bits = STM32F4_AWDSGL | STM32F4_JAWDEN,
+ .awdch_mask = STM32F4_AWDCH_MASK,
+ .awdch_shift = STM32F4_AWDCH_SHIFT,
+ .htr = STM32F4_ADC_HTR,
+ .ltr = STM32F4_ADC_LTR,
+ .ier_msk = STM32F4_AWDIE,
+ .isr_msk = STM32F4_AWD,
+};
+
static const struct stm32_adc_regspec stm32f4_adc_regspec = {
.dr = STM32F4_ADC_DR,
+ .jdr = {
+ STM32F4_ADC_JDR1,
+ STM32F4_ADC_JDR2,
+ STM32F4_ADC_JDR3,
+ STM32F4_ADC_JDR4,
+ },
.ier_eoc = { STM32F4_ADC_CR1, STM32F4_EOCIE },
+ .ier_jeoc = { STM32F4_ADC_CR1, STM32F4_JEOCIE },
+ .ier_ovr = { STM32F4_ADC_CR1, STM32F4_OVRIE },
.isr_eoc = { STM32F4_ADC_SR, STM32F4_EOC },
+ .isr_jeoc = { STM32F4_ADC_SR, STM32F4_JEOC },
+ .isr_ovr = { STM32F4_ADC_SR, STM32F4_OVR },
.sqr = stm32f4_sq,
+ .jsqr = stm32f4_jsq,
.exten = { STM32F4_ADC_CR2, STM32F4_EXTEN_MASK, STM32F4_EXTEN_SHIFT },
.extsel = { STM32F4_ADC_CR2, STM32F4_EXTSEL_MASK,
STM32F4_EXTSEL_SHIFT },
+ .jexten = { STM32F4_ADC_CR2, STM32F4_JEXTEN_MASK,
+ STM32F4_JEXTEN_SHIFT },
+ .jextsel = { STM32F4_ADC_CR2, STM32F4_JEXTSEL_MASK,
+ STM32F4_JEXTSEL_SHIFT },
.res = { STM32F4_ADC_CR1, STM32F4_RES_MASK, STM32F4_RES_SHIFT },
.smpr = { STM32F4_ADC_SMPR1, STM32F4_ADC_SMPR2 },
.smp_bits = stm32f4_smp_bits,
+ .awd_reginfo = &stm32f4_awd_reginfo,
+ .num_awd = 1,
};
static const struct stm32_adc_regs stm32h7_sq[STM32_ADC_MAX_SQ + 1] = {
@@ -476,26 +610,41 @@ static const struct stm32_adc_regs stm32h7_sq[STM32_ADC_MAX_SQ + 1] = {
{ STM32H7_ADC_SQR4, GENMASK(10, 6), 6 },
};
+static const struct stm32_adc_regs stm32h7_jsq[STM32_ADC_MAX_JSQ + 1] = {
+ /* JL: len bit field description to be kept as first element */
+ { STM32H7_ADC_JSQR, GENMASK(1, 0), 0 },
+ /* JSQ1..JSQ4 registers & bit fields (reg, mask, shift) */
+ { STM32H7_ADC_JSQR, GENMASK(13, 9), 9 },
+ { STM32H7_ADC_JSQR, GENMASK(19, 15), 15 },
+ { STM32H7_ADC_JSQR, GENMASK(25, 21), 21 },
+ { STM32H7_ADC_JSQR, GENMASK(31, 27), 27 },
+};
+
/* STM32H7 external trigger sources for all instances */
static struct stm32_adc_trig_info stm32h7_adc_trigs[] = {
- { TIM1_CH1, STM32_EXT0 },
- { TIM1_CH2, STM32_EXT1 },
- { TIM1_CH3, STM32_EXT2 },
- { TIM2_CH2, STM32_EXT3 },
- { TIM3_TRGO, STM32_EXT4 },
- { TIM4_CH4, STM32_EXT5 },
- { TIM8_TRGO, STM32_EXT7 },
- { TIM8_TRGO2, STM32_EXT8 },
- { TIM1_TRGO, STM32_EXT9 },
- { TIM1_TRGO2, STM32_EXT10 },
- { TIM2_TRGO, STM32_EXT11 },
- { TIM4_TRGO, STM32_EXT12 },
- { TIM6_TRGO, STM32_EXT13 },
- { TIM15_TRGO, STM32_EXT14 },
- { TIM3_CH4, STM32_EXT15 },
- { LPTIM1_OUT, STM32_EXT18 },
- { LPTIM2_OUT, STM32_EXT19 },
- { LPTIM3_OUT, STM32_EXT20 },
+ { TIM1_CH1, STM32_EXT0, 0, TRG_REGULAR },
+ { TIM1_CH2, STM32_EXT1, 0, TRG_REGULAR },
+ { TIM1_CH3, STM32_EXT2, 0, TRG_REGULAR },
+ { TIM2_CH2, STM32_EXT3, 0, TRG_REGULAR },
+ { TIM3_TRGO, STM32_EXT4, STM32_EXT12, TRG_BOTH },
+ { TIM4_CH4, STM32_EXT5, 0, TRG_REGULAR },
+ { TIM8_TRGO, STM32_EXT7, STM32_EXT9, TRG_BOTH },
+ { TIM8_TRGO2, STM32_EXT8, STM32_EXT10, TRG_BOTH },
+ { TIM1_TRGO, STM32_EXT9, STM32_EXT0, TRG_BOTH },
+ { TIM1_TRGO2, STM32_EXT10, STM32_EXT8, TRG_BOTH },
+ { TIM2_TRGO, STM32_EXT11, STM32_EXT2, TRG_BOTH },
+ { TIM4_TRGO, STM32_EXT12, STM32_EXT5, TRG_BOTH },
+ { TIM6_TRGO, STM32_EXT13, STM32_EXT14, TRG_BOTH },
+ { TIM15_TRGO, STM32_EXT14, STM32_EXT15, TRG_BOTH },
+ { TIM3_CH4, STM32_EXT15, STM32_EXT4, TRG_BOTH },
+ { LPTIM1_OUT, STM32_EXT18, STM32_EXT18, TRG_BOTH },
+ { LPTIM2_OUT, STM32_EXT19, STM32_EXT19, TRG_BOTH },
+ { LPTIM3_OUT, STM32_EXT20, STM32_EXT20, TRG_BOTH },
+ { TIM1_CH4, 0, STM32_EXT1, TRG_INJECTED },
+ { TIM2_CH1, 0, STM32_EXT3, TRG_INJECTED },
+ { TIM8_CH4, 0, STM32_EXT7, TRG_INJECTED },
+ { TIM3_CH3, 0, STM32_EXT11, TRG_INJECTED },
+ { TIM3_CH1, 0, STM32_EXT13, TRG_INJECTED },
{},
};
@@ -533,17 +682,72 @@ static const unsigned int stm32h7_adc_smp_cycles[STM32_ADC_MAX_SMP + 1] = {
1, 2, 8, 16, 32, 64, 387, 810,
};
+/**
+ * stm32h7_awd_reginfo[] - Analog watchdog description.
+ *
+ * two watchdog types are found in stm32h7 ADC:
+ * - AWD1 has en_bits, and can select either a single or all channel(s)
+ * - AWD2 & AWD3 are enabled by channel mask (in AWDxCR)
+ * Remaining is similar (high/low threshold regs, ier/isr regs & mask)
+ */
+static const struct stm32_adc_awd_reginfo stm32h7_awd_reginfo[] = {
+ {
+ /* AWD1: has en_bits, configure it to guard one channel */
+ .reg = STM32H7_ADC_CFGR,
+ .en_bits = STM32H7_AWD1SGL | STM32H7_AWD1EN,
+ .jen_bits = STM32H7_AWD1SGL | STM32H7_JAWD1EN,
+ .awdch_mask = STM32H7_AWD1CH_MASK,
+ .awdch_shift = STM32H7_AWD1CH_SHIFT,
+ .htr = STM32H7_ADC_HTR1,
+ .ltr = STM32H7_ADC_LTR1,
+ .ier_msk = STM32H7_AWD1IE,
+ .isr_msk = STM32H7_AWD1,
+ }, {
+ /* AWD2 uses channel mask in AWD2CR register */
+ .reg = STM32H7_ADC_AWD2CR,
+ .htr = STM32H7_ADC_HTR2,
+ .ltr = STM32H7_ADC_LTR2,
+ .ier_msk = STM32H7_AWD2IE,
+ .isr_msk = STM32H7_AWD2,
+ }, {
+ /* AWD3 uses channel mask in AWD3CR register */
+ .reg = STM32H7_ADC_AWD3CR,
+ .htr = STM32H7_ADC_HTR3,
+ .ltr = STM32H7_ADC_LTR3,
+ .ier_msk = STM32H7_AWD3IE,
+ .isr_msk = STM32H7_AWD3,
+ },
+};
+
static const struct stm32_adc_regspec stm32h7_adc_regspec = {
.dr = STM32H7_ADC_DR,
+ .jdr = {
+ STM32H7_ADC_JDR1,
+ STM32H7_ADC_JDR2,
+ STM32H7_ADC_JDR3,
+ STM32H7_ADC_JDR4,
+ },
.ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE },
+ .ier_jeoc = { STM32H7_ADC_IER, STM32H7_JEOSIE },
+ .ier_ovr = { STM32H7_ADC_IER, STM32H7_OVRIE },
.isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC },
+ .isr_jeoc = { STM32H7_ADC_ISR, STM32H7_JEOS },
+ .isr_ovr = { STM32H7_ADC_ISR, STM32H7_OVR },
.sqr = stm32h7_sq,
+ .jsqr = stm32h7_jsq,
.exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT },
.extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK,
STM32H7_EXTSEL_SHIFT },
+ .jexten = { STM32H7_ADC_JSQR, STM32H7_JEXTEN_MASK,
+ STM32H7_JEXTEN_SHIFT },
+ .jextsel = { STM32H7_ADC_JSQR, STM32H7_JEXTSEL_MASK,
+ STM32H7_JEXTSEL_SHIFT },
.res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT },
.smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 },
.smp_bits = stm32h7_smp_bits,
+ .write_one_to_clear = true,
+ .awd_reginfo = stm32h7_awd_reginfo,
+ .num_awd = ARRAY_SIZE(stm32h7_awd_reginfo),
};
/**
@@ -599,8 +803,12 @@ static void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
*/
static void stm32_adc_conv_irq_enable(struct stm32_adc *adc)
{
- stm32_adc_set_bits(adc, adc->cfg->regs->ier_eoc.reg,
- adc->cfg->regs->ier_eoc.mask);
+ if (adc->injected)
+ stm32_adc_set_bits(adc, adc->cfg->regs->ier_jeoc.reg,
+ adc->cfg->regs->ier_jeoc.mask);
+ else
+ stm32_adc_set_bits(adc, adc->cfg->regs->ier_eoc.reg,
+ adc->cfg->regs->ier_eoc.mask);
};
/**
@@ -609,8 +817,30 @@ static void stm32_adc_conv_irq_enable(struct stm32_adc *adc)
*/
static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
{
- stm32_adc_clr_bits(adc, adc->cfg->regs->ier_eoc.reg,
- adc->cfg->regs->ier_eoc.mask);
+ if (adc->injected)
+ stm32_adc_clr_bits(adc, adc->cfg->regs->ier_jeoc.reg,
+ adc->cfg->regs->ier_jeoc.mask);
+ else
+ stm32_adc_clr_bits(adc, adc->cfg->regs->ier_eoc.reg,
+ adc->cfg->regs->ier_eoc.mask);
+}
+
+static void stm32_adc_ovr_irq_enable(struct stm32_adc *adc)
+{
+ if (adc->injected)
+ return;
+
+ stm32_adc_set_bits(adc, adc->cfg->regs->ier_ovr.reg,
+ adc->cfg->regs->ier_ovr.mask);
+}
+
+static void stm32_adc_ovr_irq_disable(struct stm32_adc *adc)
+{
+ if (adc->injected)
+ return;
+
+ stm32_adc_clr_bits(adc, adc->cfg->regs->ier_ovr.reg,
+ adc->cfg->regs->ier_ovr.mask);
}
static void stm32_adc_set_res(struct stm32_adc *adc)
@@ -623,6 +853,57 @@ static void stm32_adc_set_res(struct stm32_adc *adc)
stm32_adc_writel(adc, res->reg, val);
}
+static int stm32_adc_hw_stop(struct device *dev)
+{
+ struct stm32_adc *adc = dev_get_drvdata(dev);
+
+ if (adc->cfg->unprepare)
+ adc->cfg->unprepare(adc);
+
+ if (adc->clk)
+ clk_disable_unprepare(adc->clk);
+
+ return 0;
+}
+
+static int stm32_adc_hw_start(struct device *dev)
+{
+ struct stm32_adc *adc = dev_get_drvdata(dev);
+ int ret;
+
+ if (adc->clk) {
+ ret = clk_prepare_enable(adc->clk);
+ if (ret)
+ return ret;
+ }
+
+ stm32_adc_set_res(adc);
+
+ if (adc->cfg->prepare) {
+ ret = adc->cfg->prepare(adc);
+ if (ret)
+ goto err_clk_dis;
+ }
+
+ return 0;
+
+err_clk_dis:
+ if (adc->clk)
+ clk_disable_unprepare(adc->clk);
+
+ return ret;
+}
+
+static bool stm32f4_adc_is_started(struct stm32_adc *adc)
+{
+ u32 val = stm32_adc_readl(adc, STM32F4_ADC_SR);
+
+ if (adc->injected)
+ return !!(val & STM32F4_JSTRT);
+ else
+ return !!(val & STM32F4_STRT);
+}
+
/**
* stm32f4_adc_start_conv() - Start conversions for regular channels.
* @adc: stm32 adc instance
@@ -635,30 +916,80 @@ static void stm32_adc_set_res(struct stm32_adc *adc)
*/
static void stm32f4_adc_start_conv(struct stm32_adc *adc, bool dma)
{
+ u32 trig_msk, start_msk;
+
stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
- if (dma)
+ if (!adc->injected && dma)
stm32_adc_set_bits(adc, STM32F4_ADC_CR2,
STM32F4_DMA | STM32F4_DDS);
- stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_EOCS | STM32F4_ADON);
+ if (!(stm32_adc_readl(adc, STM32F4_ADC_CR2) & STM32F4_ADON)) {
+ stm32_adc_set_bits(adc, STM32F4_ADC_CR2,
+ STM32F4_EOCS | STM32F4_ADON);
- /* Wait for Power-up time (tSTAB from datasheet) */
- usleep_range(2, 3);
+ /* Wait for Power-up time (tSTAB from datasheet) */
+ usleep_range(2, 3);
+ }
+
+ if (adc->injected) {
+ trig_msk = STM32F4_JEXTEN_MASK;
+ start_msk = STM32F4_JSWSTART;
+ } else {
+ trig_msk = STM32F4_EXTEN_MASK;
+ start_msk = STM32F4_SWSTART;
+ }
/* Software start ? (e.g. trigger detection disabled ?) */
- if (!(stm32_adc_readl(adc, STM32F4_ADC_CR2) & STM32F4_EXTEN_MASK))
- stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_SWSTART);
+ if (!(stm32_adc_readl(adc, STM32F4_ADC_CR2) & trig_msk))
+ stm32_adc_set_bits(adc, STM32F4_ADC_CR2, start_msk);
}
static void stm32f4_adc_stop_conv(struct stm32_adc *adc)
{
- stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK);
- stm32_adc_clr_bits(adc, STM32F4_ADC_SR, STM32F4_STRT);
+ u32 val;
+
+ if (adc->injected) {
+ stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_JEXTEN_MASK);
+ stm32_adc_clr_bits(adc, STM32F4_ADC_SR, STM32F4_JSTRT);
+ } else {
+ stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK);
+ stm32_adc_clr_bits(adc, STM32F4_ADC_SR, STM32F4_STRT);
+ }
+
+ /* Disable adc when all triggered conversion have been disabled */
+ val = stm32_adc_readl(adc, STM32F4_ADC_CR2);
+ val &= STM32F4_EXTEN_MASK | STM32F4_JEXTEN_MASK;
+ if (!val) {
+ stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
+ stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_ADON);
+ }
+
+ if (!adc->injected)
+ stm32_adc_clr_bits(adc, STM32F4_ADC_CR2,
+ STM32F4_DMA | STM32F4_DDS);
+}
+
+static bool stm32h7_adc_is_enabled(struct stm32_adc *adc)
+{
+ return !!(stm32_adc_readl(adc, STM32H7_ADC_CR) & STM32H7_ADEN);
+}
+
+static bool stm32h7_adc_any_ongoing_conv(struct stm32_adc *adc)
+{
+ u32 val = stm32_adc_readl(adc, STM32H7_ADC_CR);
- stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
- stm32_adc_clr_bits(adc, STM32F4_ADC_CR2,
- STM32F4_ADON | STM32F4_DMA | STM32F4_DDS);
+ return !!(val & (STM32H7_ADSTART | STM32H7_JADSTART));
+}
+
+static bool stm32h7_adc_is_started(struct stm32_adc *adc)
+{
+ u32 val = stm32_adc_readl(adc, STM32H7_ADC_CR);
+
+ if (adc->injected)
+ return !!(val & STM32H7_JADSTART);
+ else
+ return !!(val & STM32H7_ADSTART);
}
static void stm32h7_adc_start_conv(struct stm32_adc *adc, bool dma)
@@ -667,6 +998,11 @@ static void stm32h7_adc_start_conv(struct stm32_adc *adc, bool dma)
unsigned long flags;
u32 val;
+ if (adc->injected) {
+ stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_JADSTART);
+ return;
+ }
+
if (dma)
dmngt = STM32H7_DMNGT_DMA_CIRC;
else
@@ -687,6 +1023,16 @@ static void stm32h7_adc_stop_conv(struct stm32_adc *adc)
int ret;
u32 val;
+ if (adc->injected) {
+ stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_JADSTP);
+ ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
+ !(val & (STM32H7_JADSTART)),
+ 100, STM32_ADC_TIMEOUT_US);
+ if (ret)
+ dev_warn(&indio_dev->dev, "stop failed\n");
+ return;
+ }
+
stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADSTP);
ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
@@ -704,6 +1050,10 @@ static int stm32h7_adc_exit_pwr_down(struct stm32_adc *adc)
int ret;
u32 val;
+ /* Is ADC already up ? */
+ if (stm32_adc_readl(adc, STM32H7_ADC_CR) & STM32H7_ADVREGEN)
+ return 0;
+
/* Exit deep power down, then enable ADC voltage regulator */
stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD);
stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADVREGEN);
@@ -730,6 +1080,10 @@ static int stm32h7_adc_exit_pwr_down(struct stm32_adc *adc)
static void stm32h7_adc_enter_pwr_down(struct stm32_adc *adc)
{
+ /* Check there is no regular or injected on-going conversions */
+ if (stm32h7_adc_any_ongoing_conv(adc))
+ return;
+
stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST);
/* Setting DEEPPWD disables ADC vreg and clears ADVREGEN */
@@ -742,6 +1096,9 @@ static int stm32h7_adc_enable(struct stm32_adc *adc)
int ret;
u32 val;
+ if (stm32h7_adc_is_enabled(adc))
+ return 0;
+
stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADEN);
/* Poll for ADRDY to be set (after adc startup time) */
@@ -765,6 +1122,10 @@ static void stm32h7_adc_disable(struct stm32_adc *adc)
int ret;
u32 val;
+ /* Check there is no regular or injected on-going conversions */
+ if (stm32h7_adc_any_ongoing_conv(adc))
+ return;
+
/* Disable ADC and wait until it's effectively disabled */
stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADDIS);
ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
@@ -777,18 +1138,15 @@ static void stm32h7_adc_disable(struct stm32_adc *adc)
/**
* stm32h7_adc_read_selfcalib() - read calibration shadow regs, save result
* @adc: stm32 adc instance
+ * Note: Must be called once ADC is enabled, so LINCALRDYW[1..6] are writable
*/
static int stm32h7_adc_read_selfcalib(struct stm32_adc *adc)
{
struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ struct stm32_adc_calib *cal = &adc->common->cal[adc->id];
int i, ret;
u32 lincalrdyw_mask, val;
- /* Enable adc so LINCALRDYW1..6 bits are writable */
- ret = stm32h7_adc_enable(adc);
- if (ret)
- return ret;
-
/* Read linearity calibration */
lincalrdyw_mask = STM32H7_LINCALRDYW6;
for (i = STM32H7_LINCALFACT_NUM - 1; i >= 0; i--) {
@@ -801,27 +1159,25 @@ static int stm32h7_adc_read_selfcalib(struct stm32_adc *adc)
100, STM32_ADC_TIMEOUT_US);
if (ret) {
dev_err(&indio_dev->dev, "Failed to read calfact\n");
- goto disable;
+ return ret;
}
val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT2);
- adc->cal.lincalfact[i] = (val & STM32H7_LINCALFACT_MASK);
- adc->cal.lincalfact[i] >>= STM32H7_LINCALFACT_SHIFT;
+ cal->lincalfact[i] = (val & STM32H7_LINCALFACT_MASK);
+ cal->lincalfact[i] >>= STM32H7_LINCALFACT_SHIFT;
lincalrdyw_mask >>= 1;
}
/* Read offset calibration */
val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT);
- adc->cal.calfact_s = (val & STM32H7_CALFACT_S_MASK);
- adc->cal.calfact_s >>= STM32H7_CALFACT_S_SHIFT;
- adc->cal.calfact_d = (val & STM32H7_CALFACT_D_MASK);
- adc->cal.calfact_d >>= STM32H7_CALFACT_D_SHIFT;
+ cal->calfact_s = (val & STM32H7_CALFACT_S_MASK);
+ cal->calfact_s >>= STM32H7_CALFACT_S_SHIFT;
+ cal->calfact_d = (val & STM32H7_CALFACT_D_MASK);
+ cal->calfact_d >>= STM32H7_CALFACT_D_SHIFT;
+ cal->calibrated = true;
-disable:
- stm32h7_adc_disable(adc);
-
- return ret;
+ return 0;
}
/**
@@ -832,11 +1188,16 @@ static int stm32h7_adc_read_selfcalib(struct stm32_adc *adc)
static int stm32h7_adc_restore_selfcalib(struct stm32_adc *adc)
{
struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ struct stm32_adc_calib *cal = &adc->common->cal[adc->id];
int i, ret;
u32 lincalrdyw_mask, val;
- val = (adc->cal.calfact_s << STM32H7_CALFACT_S_SHIFT) |
- (adc->cal.calfact_d << STM32H7_CALFACT_D_SHIFT);
+ /* Check there is no regular or injected on-going conversions */
+ if (stm32h7_adc_any_ongoing_conv(adc))
+ return 0;
+
+ val = (cal->calfact_s << STM32H7_CALFACT_S_SHIFT) |
+ (cal->calfact_d << STM32H7_CALFACT_D_SHIFT);
stm32_adc_writel(adc, STM32H7_ADC_CALFACT, val);
lincalrdyw_mask = STM32H7_LINCALRDYW6;
@@ -846,7 +1207,7 @@ static int stm32h7_adc_restore_selfcalib(struct stm32_adc *adc)
* Write CALFACT2, and set LINCALRDYW[6..1] bit to trigger
* data write. Then poll to wait for complete transfer.
*/
- val = adc->cal.lincalfact[i] << STM32H7_LINCALFACT_SHIFT;
+ val = cal->lincalfact[i] << STM32H7_LINCALFACT_SHIFT;
stm32_adc_writel(adc, STM32H7_ADC_CALFACT2, val);
stm32_adc_set_bits(adc, STM32H7_ADC_CR, lincalrdyw_mask);
ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
@@ -873,7 +1234,7 @@ static int stm32h7_adc_restore_selfcalib(struct stm32_adc *adc)
return ret;
}
val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT2);
- if (val != adc->cal.lincalfact[i] << STM32H7_LINCALFACT_SHIFT) {
+ if (val != cal->lincalfact[i] << STM32H7_LINCALFACT_SHIFT) {
dev_err(&indio_dev->dev, "calfact not consistent\n");
return -EIO;
}
@@ -898,19 +1259,19 @@ static int stm32h7_adc_restore_selfcalib(struct stm32_adc *adc)
#define STM32H7_ADC_CALIB_TIMEOUT_US 100000
/**
- * stm32h7_adc_selfcalib() - Procedure to calibrate ADC (from power down)
+ * stm32h7_adc_selfcalib() - Procedure to calibrate ADC
* @adc: stm32 adc instance
- * Exit from power down, calibrate ADC, then return to power down.
+ * Note: Must be called once ADC is out of power down.
*/
static int stm32h7_adc_selfcalib(struct stm32_adc *adc)
{
struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ struct stm32_adc_calib *cal = &adc->common->cal[adc->id];
int ret;
u32 val;
- ret = stm32h7_adc_exit_pwr_down(adc);
- if (ret)
- return ret;
+ if (cal->calibrated)
+ return cal->calibrated;
/*
* Select calibration mode:
@@ -927,7 +1288,7 @@ static int stm32h7_adc_selfcalib(struct stm32_adc *adc)
STM32H7_ADC_CALIB_TIMEOUT_US);
if (ret) {
dev_err(&indio_dev->dev, "calibration failed\n");
- goto pwr_dwn;
+ goto out;
}
/*
@@ -944,18 +1305,13 @@ static int stm32h7_adc_selfcalib(struct stm32_adc *adc)
STM32H7_ADC_CALIB_TIMEOUT_US);
if (ret) {
dev_err(&indio_dev->dev, "calibration failed\n");
- goto pwr_dwn;
+ goto out;
}
+out:
stm32_adc_clr_bits(adc, STM32H7_ADC_CR,
STM32H7_ADCALDIF | STM32H7_ADCALLIN);
- /* Read calibration result for future reference */
- ret = stm32h7_adc_read_selfcalib(adc);
-
-pwr_dwn:
- stm32h7_adc_enter_pwr_down(adc);
-
return ret;
}
@@ -972,23 +1328,43 @@ static int stm32h7_adc_selfcalib(struct stm32_adc *adc)
*/
static int stm32h7_adc_prepare(struct stm32_adc *adc)
{
- int ret;
+ u32 *difsel = &adc->common->difsel[adc->id];
+ u32 *pcsel = &adc->common->pcsel[adc->id];
+ int calib, ret;
+
+ /* protect race between regular/injected prepare, unprepare */
+ mutex_lock(&adc->common->inj[adc->id]);
+ adc->common->prepcnt[adc->id]++;
+ if (adc->common->prepcnt[adc->id] > 1) {
+ mutex_unlock(&adc->common->inj[adc->id]);
+ return 0;
+ }
ret = stm32h7_adc_exit_pwr_down(adc);
if (ret)
- return ret;
+ goto unlock;
+
+ ret = stm32h7_adc_selfcalib(adc);
+ if (ret < 0)
+ goto pwr_dwn;
+ calib = ret;
- stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel);
+ stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, *difsel);
ret = stm32h7_adc_enable(adc);
if (ret)
goto pwr_dwn;
- ret = stm32h7_adc_restore_selfcalib(adc);
+ /* Either restore or read calibration result for future reference */
+ if (calib)
+ ret = stm32h7_adc_restore_selfcalib(adc);
+ else
+ ret = stm32h7_adc_read_selfcalib(adc);
if (ret)
goto disable;
- stm32_adc_writel(adc, STM32H7_ADC_PCSEL, adc->pcsel);
+ stm32_adc_writel(adc, STM32H7_ADC_PCSEL, *pcsel);
+ mutex_unlock(&adc->common->inj[adc->id]);
return 0;
@@ -996,39 +1372,196 @@ static int stm32h7_adc_prepare(struct stm32_adc *adc)
stm32h7_adc_disable(adc);
pwr_dwn:
stm32h7_adc_enter_pwr_down(adc);
+unlock:
+ adc->common->prepcnt[adc->id]--;
+ mutex_unlock(&adc->common->inj[adc->id]);
return ret;
}
static void stm32h7_adc_unprepare(struct stm32_adc *adc)
{
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+
+ mutex_lock(&adc->common->inj[adc->id]);
+ adc->common->prepcnt[adc->id]--;
+ if (adc->common->prepcnt[adc->id] > 0) {
+ mutex_unlock(&adc->common->inj[adc->id]);
+ return;
+ }
+
+ if (adc->common->prepcnt[adc->id] < 0)
+ dev_err(&indio_dev->dev, "Unbalanced (un)prepare\n");
stm32h7_adc_disable(adc);
stm32h7_adc_enter_pwr_down(adc);
+ mutex_unlock(&adc->common->inj[adc->id]);
+}
+
+/**
+ * stm32_adc_find_unused_awd() - Find an unused analog watchdog
+ * @adc: stm32 adc instance
+ *
+ * Loop for all AWD to find a free AWD.
+ * Returns free AWD index or busy error.
+ */
+static int stm32_adc_find_unused_awd(struct stm32_adc *adc)
+{
+ const struct stm32_adc_awd_reginfo *awd_reginfo =
+ adc->cfg->regs->awd_reginfo;
+ u32 val, mask;
+ int i;
+
+ /* find unused AWD, either use en bits or channel mask */
+ for (i = 0; i < adc->cfg->regs->num_awd; i++) {
+ val = stm32_adc_readl(adc, awd_reginfo[i].reg);
+ mask = awd_reginfo[i].en_bits | awd_reginfo[i].jen_bits;
+ if (mask && !(val & mask))
+ break;
+ if (!mask && !val)
+ break;
+ }
+
+ if (i >= adc->cfg->regs->num_awd)
+ return -EBUSY;
+
+ return i;
}
/**
- * stm32_adc_conf_scan_seq() - Build regular channels scan sequence
+ * stm32_adc_awd_clear() - Disable analog watchdog for one adc
+ * @adc: stm32 adc instance
+ *
+ * Mask awd interrupts, disable awd.
+ */
+static void stm32_adc_awd_clear(struct stm32_adc *adc)
+{
+ int i;
+ u32 en_bits, ier = adc->cfg->regs->ier_eoc.reg;
+ struct stm32_adc_evt *evt;
+ const struct stm32_adc_awd_reginfo *awd_reginfo =
+ adc->cfg->regs->awd_reginfo;
+
+ list_for_each_entry(evt, &adc->evt_list, list) {
+ if (!evt->set)
+ continue;
+
+ i = evt->awd_id;
+
+ /* Disable AWD interrupt */
+ stm32_adc_clr_bits(adc, ier, awd_reginfo[i].ier_msk);
+
+ /* Disable AWD: either use en bits and channel num, or mask */
+ en_bits = awd_reginfo[i].en_bits | awd_reginfo[i].jen_bits;
+ if (en_bits)
+ stm32_adc_clr_bits(adc, awd_reginfo[i].reg, en_bits);
+ else
+ stm32_adc_writel(adc, awd_reginfo[i].reg, 0);
+
+ adc->awd_mask &= ~awd_reginfo[i].isr_msk;
+ evt->set = false;
+ }
+}
+
+/**
+ * stm32_adc_awd_set() - Set analog watchdog
+ * @adc: stm32 adc instance
+ *
+ * Set analog watchdog registers based on pre-built event list.
+ *
+ * Two watchdog types can be found in stm32 ADC:
+ * - 1st type can be used either on all channels, or on one channel. Choice
+ * is made to assing it to one channel only. It is enabled with enable bits
+ * and channel number.
+ * - 2nd type uses channel mask (choice to assign it to one channel only).
+ * In both case, set high & low threshold. Also unmask interrupt.
+ */
+static int stm32_adc_awd_set(struct stm32_adc *adc)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ int i;
+ struct stm32_adc_evt *evt;
+ const struct stm32_adc_awd_reginfo *awd_reginfo =
+ adc->cfg->regs->awd_reginfo;
+ u32 val, ier = adc->cfg->regs->ier_eoc.reg;
+
+ list_for_each_entry(evt, &adc->evt_list, list) {
+ if (!evt->enabled)
+ continue;
+
+ i = stm32_adc_find_unused_awd(adc);
+ if (i < 0) {
+ stm32_adc_awd_clear(adc);
+ return i;
+ }
+
+ evt->awd_id = i;
+ evt->set = true;
+ dev_dbg(&indio_dev->dev, "%s chan%d htr:%d ltr:%d\n",
+ __func__, evt->chan->channel, evt->hthresh,
+ evt->lthresh);
+
+ stm32_adc_writel(adc, awd_reginfo[i].htr, evt->hthresh);
+ stm32_adc_writel(adc, awd_reginfo[i].ltr, evt->lthresh);
+
+ /* Enable AWD: either use en bits and channel num, or mask */
+ if (awd_reginfo[i].en_bits | awd_reginfo[i].jen_bits) {
+ u32 mask = awd_reginfo[i].awdch_mask;
+ u32 shift = awd_reginfo[i].awdch_shift;
+
+ val = stm32_adc_readl(adc, awd_reginfo[i].reg);
+ val &= ~mask;
+ val |= (evt->chan->channel << shift) & mask;
+
+ if (adc->injected)
+ val |= awd_reginfo[i].jen_bits;
+ else
+ val |= awd_reginfo[i].en_bits;
+ stm32_adc_writel(adc, awd_reginfo[i].reg, val);
+ } else {
+ stm32_adc_writel(adc, awd_reginfo[i].reg,
+ BIT(evt->chan->channel));
+ }
+
+ /* Enable AWD interrupt */
+ adc->awd_mask |= awd_reginfo[i].isr_msk;
+ stm32_adc_set_bits(adc, ier, awd_reginfo[i].ier_msk);
+ }
+
+ return 0;
+}
+
+/**
+ * stm32_adc_conf_scan_seq() - Build channels scan sequence
* @indio_dev: IIO device
* @scan_mask: channels to be converted
*
* Conversion sequence :
* Apply sampling time settings for all channels.
* Configure ADC scan sequence based on selected channels in scan_mask.
- * Add channels to SQR registers, from scan_mask LSB to MSB, then
+ * Add channels to (J)SQR registers, from scan_mask LSB to MSB, then
* program sequence len.
*/
static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
struct stm32_adc *adc = iio_priv(indio_dev);
- const struct stm32_adc_regs *sqr = adc->cfg->regs->sqr;
+ u32 *smpr_val = adc->common->smpr_val[adc->id];
+ const struct stm32_adc_regs *sqr;
const struct iio_chan_spec *chan;
u32 val, bit;
- int i = 0;
+ int sq_max, i = 0;
+
+ if (adc->injected) {
+ sqr = adc->cfg->regs->jsqr;
+ sq_max = STM32_ADC_MAX_JSQ;
+ } else {
+ sqr = adc->cfg->regs->sqr;
+ sq_max = STM32_ADC_MAX_SQ;
+ }
/* Apply sampling time settings */
- stm32_adc_writel(adc, adc->cfg->regs->smpr[0], adc->smpr_val[0]);
- stm32_adc_writel(adc, adc->cfg->regs->smpr[1], adc->smpr_val[1]);
+ stm32_adc_writel(adc, adc->cfg->regs->smpr[0], smpr_val[0]);
+ stm32_adc_writel(adc, adc->cfg->regs->smpr[1], smpr_val[1]);
for_each_set_bit(bit, scan_mask, indio_dev->masklength) {
chan = indio_dev->channels + bit;
@@ -1037,11 +1570,12 @@ static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev,
* sequence, starting with SQ1.
*/
i++;
- if (i > STM32_ADC_MAX_SQ)
+ if (i > sq_max)
return -EINVAL;
- dev_dbg(&indio_dev->dev, "%s chan %d to SQ%d\n",
- __func__, chan->channel, i);
+ dev_dbg(&indio_dev->dev, "%s chan %d to %s%d\n",
+ __func__, chan->channel, adc->injected ? "JSQ" : "SQ",
+ i);
val = stm32_adc_readl(adc, sqr[i].reg);
val &= ~sqr[i].mask;
@@ -1071,18 +1605,36 @@ static int stm32_adc_get_trig_extsel(struct iio_dev *indio_dev,
struct iio_trigger *trig)
{
struct stm32_adc *adc = iio_priv(indio_dev);
+ struct stm32_adc_trig_info *trinfo;
+ struct iio_trigger *tr;
int i;
/* lookup triggers registered by stm32 timer trigger driver */
for (i = 0; adc->cfg->trigs[i].name; i++) {
+ trinfo = &adc->cfg->trigs[i];
/**
* Checking both stm32 timer trigger type and trig name
* should be safe against arbitrary trigger names.
*/
if ((is_stm32_timer_trigger(trig) ||
is_stm32_lptim_trigger(trig)) &&
- !strcmp(adc->cfg->trigs[i].name, trig->name)) {
- return adc->cfg->trigs[i].extsel;
+ !strcmp(trinfo->name, trig->name)) {
+ if (adc->injected && (trinfo->flags & TRG_INJECTED))
+ return trinfo->jextsel;
+
+ if (!adc->injected && (trinfo->flags & TRG_REGULAR))
+ return trinfo->extsel;
+ }
+ }
+
+ /* loop for triggers registered by stm32-adc-core */
+ list_for_each_entry(tr, &adc->common->extrig_list, alloc_list) {
+ if (tr == trig) {
+ trinfo = iio_trigger_get_drvdata(trig);
+ if (adc->injected && (trinfo->flags & TRG_INJECTED))
+ return trinfo->jextsel;
+ if (!adc->injected && (trinfo->flags & TRG_REGULAR))
+ return trinfo->extsel;
}
}
@@ -1102,10 +1654,24 @@ static int stm32_adc_set_trig(struct iio_dev *indio_dev,
struct iio_trigger *trig)
{
struct stm32_adc *adc = iio_priv(indio_dev);
- u32 val, extsel = 0, exten = STM32_EXTEN_SWTRIG;
+ u32 val, extsel = 0, exten = STM32_EXTEN_SWTRIG, reg, mask,
+ exten_shift, extsel_shift;
unsigned long flags;
int ret;
+ if (adc->injected) {
+ reg = adc->cfg->regs->jexten.reg;
+ mask = adc->cfg->regs->jexten.mask |
+ adc->cfg->regs->jextsel.mask;
+ exten_shift = adc->cfg->regs->jexten.shift;
+ extsel_shift = adc->cfg->regs->jextsel.shift;
+ } else {
+ reg = adc->cfg->regs->exten.reg;
+ mask = adc->cfg->regs->exten.mask | adc->cfg->regs->extsel.mask;
+ exten_shift = adc->cfg->regs->exten.shift;
+ extsel_shift = adc->cfg->regs->extsel.shift;
+ }
+
if (trig) {
ret = stm32_adc_get_trig_extsel(indio_dev, trig);
if (ret < 0)
@@ -1117,11 +1683,9 @@ static int stm32_adc_set_trig(struct iio_dev *indio_dev,
}
spin_lock_irqsave(&adc->lock, flags);
- val = stm32_adc_readl(adc, adc->cfg->regs->exten.reg);
- val &= ~(adc->cfg->regs->exten.mask | adc->cfg->regs->extsel.mask);
- val |= exten << adc->cfg->regs->exten.shift;
- val |= extsel << adc->cfg->regs->extsel.shift;
- stm32_adc_writel(adc, adc->cfg->regs->exten.reg, val);
+ val = stm32_adc_readl(adc, reg) & ~mask;
+ val |= (exten << exten_shift) | (extsel << extsel_shift);
+ stm32_adc_writel(adc, reg, val);
spin_unlock_irqrestore(&adc->lock, flags);
return 0;
@@ -1174,36 +1738,47 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev,
int *res)
{
struct stm32_adc *adc = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
const struct stm32_adc_regspec *regs = adc->cfg->regs;
+ u32 *smpr_val = adc->common->smpr_val[adc->id];
+ const struct stm32_adc_regs *sqr;
long timeout;
u32 val;
int ret;
reinit_completion(&adc->completion);
+ adc->num_conv = 1;
adc->bufi = 0;
- if (adc->cfg->prepare) {
- ret = adc->cfg->prepare(adc);
- if (ret)
- return ret;
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(dev);
+ return ret;
}
/* Apply sampling time settings */
- stm32_adc_writel(adc, regs->smpr[0], adc->smpr_val[0]);
- stm32_adc_writel(adc, regs->smpr[1], adc->smpr_val[1]);
+ stm32_adc_writel(adc, regs->smpr[0], smpr_val[0]);
+ stm32_adc_writel(adc, regs->smpr[1], smpr_val[1]);
+
+ if (adc->injected)
+ sqr = regs->jsqr;
+ else
+ sqr = regs->sqr;
/* Program chan number in regular sequence (SQ1) */
- val = stm32_adc_readl(adc, regs->sqr[1].reg);
- val &= ~regs->sqr[1].mask;
- val |= chan->channel << regs->sqr[1].shift;
- stm32_adc_writel(adc, regs->sqr[1].reg, val);
+ val = stm32_adc_readl(adc, sqr[1].reg) & ~sqr[1].mask;
+ val |= chan->channel << sqr[1].shift;
+ stm32_adc_writel(adc, sqr[1].reg, val);
/* Set regular sequence len (0 for 1 conversion) */
- stm32_adc_clr_bits(adc, regs->sqr[0].reg, regs->sqr[0].mask);
+ stm32_adc_clr_bits(adc, sqr[0].reg, sqr[0].mask);
/* Trigger detection disabled (conversion can be launched in SW) */
- stm32_adc_clr_bits(adc, regs->exten.reg, regs->exten.mask);
+ if (adc->injected)
+ stm32_adc_clr_bits(adc, regs->jexten.reg, regs->jexten.mask);
+ else
+ stm32_adc_clr_bits(adc, regs->exten.reg, regs->exten.mask);
stm32_adc_conv_irq_enable(adc);
@@ -1224,8 +1799,8 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev,
stm32_adc_conv_irq_disable(adc);
- if (adc->cfg->unprepare)
- adc->cfg->unprepare(adc);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
return ret;
}
@@ -1272,14 +1847,72 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
}
}
+static irqreturn_t stm32_adc_threaded_isr(int irq, void *data)
+{
+ struct stm32_adc *adc = data;
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ struct stm32_adc_evt *evt;
+ const struct stm32_adc_regspec *regs = adc->cfg->regs;
+ const struct stm32_adc_awd_reginfo *awd_reginfo = regs->awd_reginfo;
+ u32 ier = regs->ier_eoc.reg, isr = regs->isr_eoc.reg;
+ u32 status = stm32_adc_readl(adc, isr);
+ irqreturn_t ret = IRQ_NONE;
+
+ /* Handle analog watchdog events */
+ list_for_each_entry(evt, &adc->evt_list, list) {
+ if (!evt->set || !(status & awd_reginfo[evt->awd_id].isr_msk))
+ continue;
+
+ /* We don't know whether it is a upper or lower threshold. */
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(evt->chan->type,
+ evt->chan->channel,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ iio_get_time_ns(indio_dev));
+
+ /* clear analog watchdog flag */
+ if (regs->write_one_to_clear)
+ stm32_adc_set_bits(adc, isr,
+ awd_reginfo[evt->awd_id].isr_msk);
+ else
+ stm32_adc_clr_bits(adc, isr,
+ awd_reginfo[evt->awd_id].isr_msk);
+
+ /* re-enable current awd interrupt */
+ stm32_adc_set_bits(adc, ier, awd_reginfo[evt->awd_id].ier_msk);
+
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
static irqreturn_t stm32_adc_isr(int irq, void *data)
{
struct stm32_adc *adc = data;
struct iio_dev *indio_dev = iio_priv_to_dev(adc);
const struct stm32_adc_regspec *regs = adc->cfg->regs;
+ const struct stm32_adc_awd_reginfo *awd_reginfo = regs->awd_reginfo;
u32 status = stm32_adc_readl(adc, regs->isr_eoc.reg);
+ u32 ier = adc->cfg->regs->ier_eoc.reg;
+ irqreturn_t ret = IRQ_NONE;
+ int i;
+
+ if (!adc->injected && (status & regs->isr_ovr.mask)) {
+ /*
+ * Overrun occured on regular conversions. Can't recover easily
+ * especially in scan mode: data for wrong channel may be read.
+ * Then, unconditionally disable interrupts to stop processing
+ * data, and lazily print error message (once).
+ */
+ stm32_adc_ovr_irq_disable(adc);
+ stm32_adc_conv_irq_disable(adc);
+ dev_err(&indio_dev->dev, "Overrun interrupt, stopping.\n");
+ return IRQ_HANDLED;
+ }
- if (status & regs->isr_eoc.mask) {
+ if (!adc->injected && (status & regs->isr_eoc.mask)) {
/* Reading DR also clears EOC status flag */
adc->buffer[adc->bufi] = stm32_adc_readw(adc, regs->dr);
if (iio_buffer_enabled(indio_dev)) {
@@ -1291,10 +1924,48 @@ static irqreturn_t stm32_adc_isr(int irq, void *data)
} else {
complete(&adc->completion);
}
- return IRQ_HANDLED;
+ ret = IRQ_HANDLED;
}
- return IRQ_NONE;
+ if (adc->injected && (status & regs->isr_jeoc.mask)) {
+ int i;
+
+ if (regs->write_one_to_clear)
+ stm32_adc_writel(adc, regs->isr_jeoc.reg,
+ regs->isr_jeoc.mask);
+ else
+ stm32_adc_writel(adc, regs->isr_jeoc.reg,
+ ~regs->isr_jeoc.mask);
+
+ for (i = 0; i < adc->num_conv; i++) {
+ adc->buffer[i] = stm32_adc_readw(adc, regs->jdr[i]);
+ adc->bufi++;
+ }
+
+ if (iio_buffer_enabled(indio_dev)) {
+ stm32_adc_conv_irq_disable(adc);
+ iio_trigger_poll(indio_dev->trig);
+ } else {
+ complete(&adc->completion);
+ }
+ ret = IRQ_HANDLED;
+ }
+
+ /* only check AWD assigned to this ADC (e.g. regular or injected) */
+ status &= adc->awd_mask;
+ if (status) {
+ for (i = 0; i < adc->cfg->regs->num_awd; i++) {
+ /* mask current awd interrupt */
+ if (status & awd_reginfo[i].isr_msk)
+ stm32_adc_clr_bits(adc, ier,
+ awd_reginfo[i].ier_msk);
+ }
+
+ /* AWD has detected an event, need to wake IRQ thread */
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ return ret;
}
/**
@@ -1333,13 +2004,168 @@ static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
struct stm32_adc *adc = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
int ret;
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(dev);
+ return ret;
+ }
+
adc->num_conv = bitmap_weight(scan_mask, indio_dev->masklength);
ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask);
- if (ret)
- return ret;
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+/*
+ * stm32 awd monitors specified channel(s) are within window range.
+ * Define events here as high/low thresholds, with a common enable for
+ * both directions. There is no way to know from interrupt flags, which
+ * direction an event occurred. It's up to upper layers then to check
+ * value.
+ */
+static const struct iio_event_spec stm32_adc_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+static int stm32_adc_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct stm32_adc_evt *evt;
+
+ list_for_each_entry(evt, &adc->evt_list, list)
+ if (evt->chan == chan)
+ return evt->enabled;
+
+ return 0;
+}
+
+static int stm32_adc_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ int state)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct stm32_adc_evt *evt;
+ bool found = false;
+ int i = 0;
+
+ /* AWD can only be configured before starting conversions */
+ if (adc->cfg->is_started(adc))
+ return -EBUSY;
+
+ list_for_each_entry(evt, &adc->evt_list, list) {
+ if (evt->chan == chan) {
+ found = true;
+ evt->enabled = !!state;
+ }
+
+ /* number of enabled AWD for this adc instance */
+ if (evt->enabled)
+ i++;
+
+ /* unique event per AWD: don't exceed number of AWD */
+ if (i > adc->cfg->regs->num_awd)
+ goto err_busy;
+ }
+
+ /* In case no threshold have been configured, can't enable evt */
+ if (!found)
+ return -EINVAL;
+
+ return 0;
+
+err_busy:
+ dev_err(&indio_dev->dev, "Number of awd exceeded\n");
+
+ list_for_each_entry(evt, &adc->evt_list, list)
+ if (evt->chan == chan)
+ evt->enabled = false;
+
+ return -EBUSY;
+}
+
+static int stm32_adc_read_thresh(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info, int *val,
+ int *val2)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct stm32_adc_evt *evt;
+
+ *val = 0;
+
+ list_for_each_entry(evt, &adc->evt_list, list) {
+ if (evt->chan == chan) {
+ if (dir == IIO_EV_DIR_RISING)
+ *val = evt->hthresh;
+ else
+ *val = evt->lthresh;
+ break;
+ }
+ }
+
+ return IIO_VAL_INT;
+}
+
+static int stm32_adc_write_thresh(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info, int val,
+ int val2)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct stm32_adc_evt *evt;
+ unsigned long flags;
+
+ if (adc->cfg->is_started(adc))
+ return -EBUSY;
+
+ /* Look for existing evt for this channel */
+ list_for_each_entry(evt, &adc->evt_list, list)
+ if (evt->chan == chan)
+ goto found;
+
+ /* Allocate new event: up to num_channels evts */
+ evt = devm_kzalloc(&indio_dev->dev, sizeof(*evt), GFP_KERNEL);
+ if (!evt)
+ return -ENOMEM;
+
+ evt->chan = chan;
+
+ spin_lock_irqsave(&adc->lock, flags);
+ list_add_tail(&evt->list, &adc->evt_list);
+ spin_unlock_irqrestore(&adc->lock, flags);
+
+found:
+ if (dir == IIO_EV_DIR_RISING)
+ evt->hthresh = val;
+ else
+ evt->lthresh = val;
return 0;
}
@@ -1371,12 +2197,23 @@ static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev,
unsigned *readval)
{
struct stm32_adc *adc = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ int ret;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(dev);
+ return ret;
+ }
if (!readval)
stm32_adc_writel(adc, reg, writeval);
else
*readval = stm32_adc_readl(adc, reg);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
return 0;
}
@@ -1385,6 +2222,10 @@ static const struct iio_info stm32_adc_iio_info = {
.validate_trigger = stm32_adc_validate_trigger,
.hwfifo_set_watermark = stm32_adc_set_watermark,
.update_scan_mode = stm32_adc_update_scan_mode,
+ .read_event_config = &stm32_adc_read_event_config,
+ .write_event_config = &stm32_adc_write_event_config,
+ .read_event_value = stm32_adc_read_thresh,
+ .write_event_value = stm32_adc_write_thresh,
.debugfs_reg_access = stm32_adc_debugfs_reg_access,
.of_xlate = stm32_adc_of_xlate,
};
@@ -1414,11 +2255,32 @@ static unsigned int stm32_adc_dma_residue(struct stm32_adc *adc)
return 0;
}
+static void stm32_adc_dma_irq_work(struct irq_work *work)
+{
+ struct stm32_adc *adc = container_of(work, struct stm32_adc, work);
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+
+ /*
+ * iio_trigger_poll calls generic_handle_irq(). So, it requires hard
+ * irq context, and cannot be called directly from dma callback,
+ * dma cb has to schedule this work instead.
+ */
+ iio_trigger_poll(indio_dev->trig);
+}
+
static void stm32_adc_dma_buffer_done(void *data)
{
struct iio_dev *indio_dev = data;
+ struct stm32_adc *adc = iio_priv(indio_dev);
- iio_trigger_poll_chained(indio_dev->trig);
+ /*
+ * Invoques iio_trigger_poll() from hard irq context: We can't
+ * call iio_trigger_poll() nor iio_trigger_poll_chained()
+ * directly from DMA callback (under tasklet e.g. softirq).
+ * They require respectively HW IRQ and threaded IRQ context
+ * as it might sleep.
+ */
+ irq_work_queue(&adc->work);
}
static int stm32_adc_dma_start(struct iio_dev *indio_dev)
@@ -1449,7 +2311,7 @@ static int stm32_adc_dma_start(struct iio_dev *indio_dev)
cookie = dmaengine_submit(desc);
ret = dma_submit_error(cookie);
if (ret) {
- dmaengine_terminate_all(adc->dma_chan);
+ dmaengine_terminate_sync(adc->dma_chan);
return ret;
}
@@ -1459,21 +2321,28 @@ static int stm32_adc_dma_start(struct iio_dev *indio_dev)
return 0;
}
-static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
+static int __stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
{
struct stm32_adc *adc = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
int ret;
- if (adc->cfg->prepare) {
- ret = adc->cfg->prepare(adc);
- if (ret)
- return ret;
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(dev);
+ return ret;
+ }
+
+ ret = stm32_adc_awd_set(adc);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Failed to configure awd\n");
+ goto err_pm_put;
}
ret = stm32_adc_set_trig(indio_dev, indio_dev->trig);
if (ret) {
dev_err(&indio_dev->dev, "Can't set trigger\n");
- goto err_unprepare;
+ goto err_clr_awd;
}
ret = stm32_adc_dma_start(indio_dev);
@@ -1482,13 +2351,11 @@ static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
goto err_clr_trig;
}
- ret = iio_triggered_buffer_postenable(indio_dev);
- if (ret < 0)
- goto err_stop_dma;
-
/* Reset adc buffer index */
adc->bufi = 0;
+ stm32_adc_ovr_irq_enable(adc);
+
if (!adc->dma_chan)
stm32_adc_conv_irq_enable(adc);
@@ -1496,39 +2363,66 @@ static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
return 0;
-err_stop_dma:
- if (adc->dma_chan)
- dmaengine_terminate_all(adc->dma_chan);
err_clr_trig:
stm32_adc_set_trig(indio_dev, NULL);
-err_unprepare:
- if (adc->cfg->unprepare)
- adc->cfg->unprepare(adc);
+err_clr_awd:
+ stm32_adc_awd_clear(adc);
+err_pm_put:
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
return ret;
}
-static int stm32_adc_buffer_predisable(struct iio_dev *indio_dev)
+static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
{
- struct stm32_adc *adc = iio_priv(indio_dev);
int ret;
+ ret = iio_triggered_buffer_postenable(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ ret = __stm32_adc_buffer_postenable(indio_dev);
+ if (ret < 0)
+ iio_triggered_buffer_predisable(indio_dev);
+
+ return ret;
+}
+
+static void __stm32_adc_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+
adc->cfg->stop_conv(adc);
if (!adc->dma_chan)
stm32_adc_conv_irq_disable(adc);
- ret = iio_triggered_buffer_predisable(indio_dev);
- if (ret < 0)
- dev_err(&indio_dev->dev, "predisable failed\n");
+ stm32_adc_ovr_irq_disable(adc);
- if (adc->dma_chan)
- dmaengine_terminate_all(adc->dma_chan);
+ if (adc->dma_chan) {
+ dmaengine_terminate_sync(adc->dma_chan);
+ irq_work_sync(&adc->work);
+ }
if (stm32_adc_set_trig(indio_dev, NULL))
dev_err(&indio_dev->dev, "Can't clear trigger\n");
- if (adc->cfg->unprepare)
- adc->cfg->unprepare(adc);
+ stm32_adc_awd_clear(adc);
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+}
+
+static int stm32_adc_buffer_predisable(struct iio_dev *indio_dev)
+{
+ int ret;
+
+ __stm32_adc_buffer_predisable(indio_dev);
+
+ ret = iio_triggered_buffer_predisable(indio_dev);
+ if (ret < 0)
+ dev_err(&indio_dev->dev, "predisable failed\n");
return ret;
}
@@ -1613,6 +2507,7 @@ static int stm32_adc_of_get_resolution(struct iio_dev *indio_dev)
static void stm32_adc_smpr_init(struct stm32_adc *adc, int channel, u32 smp_ns)
{
const struct stm32_adc_regs *smpr = &adc->cfg->regs->smp_bits[channel];
+ u32 *smpr_val = adc->common->smpr_val[adc->id];
u32 period_ns, shift = smpr->shift, mask = smpr->mask;
unsigned int smp, r = smpr->reg;
@@ -1625,7 +2520,7 @@ static void stm32_adc_smpr_init(struct stm32_adc *adc, int channel, u32 smp_ns)
smp = STM32_ADC_MAX_SMP;
/* pre-build sampling time registers (e.g. smpr1, smpr2) */
- adc->smpr_val[r] = (adc->smpr_val[r] & ~mask) | (smp << shift);
+ smpr_val[r] = (smpr_val[r] & ~mask) | (smp << shift);
}
static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
@@ -1634,6 +2529,8 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
{
struct stm32_adc *adc = iio_priv(indio_dev);
char *name = adc->chan_name[vinp];
+ u32 *difsel = &adc->common->difsel[adc->id];
+ u32 *pcsel = &adc->common->pcsel[adc->id];
chan->type = IIO_VOLTAGE;
chan->channel = vinp;
@@ -1654,14 +2551,16 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
chan->scan_type.realbits = adc->cfg->adc_info->resolutions[adc->res];
chan->scan_type.storagebits = 16;
chan->ext_info = stm32_adc_ext_info;
+ chan->event_spec = stm32_adc_events;
+ chan->num_event_specs = ARRAY_SIZE(stm32_adc_events);
/* pre-build selected channels mask */
- adc->pcsel |= BIT(chan->channel);
+ *pcsel |= BIT(chan->channel);
if (differential) {
/* pre-build diff channels mask */
- adc->difsel |= BIT(chan->channel);
+ *difsel |= BIT(chan->channel);
/* Also add negative input to pre-selected channels */
- adc->pcsel |= BIT(chan->channel2);
+ *pcsel |= BIT(chan->channel2);
}
}
@@ -1677,6 +2576,11 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
int scan_index = 0, num_channels = 0, num_diff = 0, ret, i;
u32 val, smp = 0;
+ if (of_property_read_bool(node, "st,injected")) {
+ dev_dbg(&indio_dev->dev, "Configured to use injected\n");
+ adc->injected = true;
+ }
+
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");
@@ -1797,6 +2701,8 @@ static int stm32_adc_dma_request(struct iio_dev *indio_dev)
if (ret)
goto err_free;
+ init_irq_work(&adc->work, stm32_adc_dma_irq_work);
+
return 0;
err_free:
@@ -1828,6 +2734,7 @@ static int stm32_adc_probe(struct platform_device *pdev)
init_completion(&adc->completion);
adc->cfg = (const struct stm32_adc_cfg *)
of_match_device(dev->driver->of_match_table, dev)->data;
+ INIT_LIST_HEAD(&adc->evt_list);
indio_dev->name = dev_name(&pdev->dev);
indio_dev->dev.parent = &pdev->dev;
@@ -1838,10 +2745,18 @@ static int stm32_adc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, adc);
ret = of_property_read_u32(pdev->dev.of_node, "reg", &adc->offset);
- if (ret != 0) {
+ if (ret != 0 || adc->offset >= STM32_ADCX_COMN_OFFSET) {
dev_err(&pdev->dev, "missing reg property\n");
return -EINVAL;
}
+ adc->id = adc->offset / STM32_ADC_OFFSET;
+
+ of_property_read_u32(pdev->dev.of_node, "st,trigger-polarity",
+ &adc->trigger_polarity);
+ if (adc->trigger_polarity >= ARRAY_SIZE(stm32_trig_pol_items)) {
+ dev_err(&pdev->dev, "Invalid st,trigger-polarity property\n");
+ return -EINVAL;
+ }
adc->irq = platform_get_irq(pdev, 0);
if (adc->irq < 0) {
@@ -1849,8 +2764,9 @@ static int stm32_adc_probe(struct platform_device *pdev)
return adc->irq;
}
- ret = devm_request_irq(&pdev->dev, adc->irq, stm32_adc_isr,
- 0, pdev->name, adc);
+ ret = devm_request_threaded_irq(&pdev->dev, adc->irq, stm32_adc_isr,
+ stm32_adc_threaded_isr,
+ 0, pdev->name, adc);
if (ret) {
dev_err(&pdev->dev, "failed to request IRQ\n");
return ret;
@@ -1867,32 +2783,17 @@ static int stm32_adc_probe(struct platform_device *pdev)
}
}
- if (adc->clk) {
- ret = clk_prepare_enable(adc->clk);
- if (ret < 0) {
- dev_err(&pdev->dev, "clk enable failed\n");
- return ret;
- }
- }
-
ret = stm32_adc_of_get_resolution(indio_dev);
if (ret < 0)
- goto err_clk_disable;
- stm32_adc_set_res(adc);
-
- if (adc->cfg->selfcalib) {
- ret = adc->cfg->selfcalib(adc);
- if (ret)
- goto err_clk_disable;
- }
+ return ret;
ret = stm32_adc_chan_of_init(indio_dev);
if (ret < 0)
- goto err_clk_disable;
+ return ret;
ret = stm32_adc_dma_request(indio_dev);
if (ret < 0)
- goto err_clk_disable;
+ return ret;
ret = iio_triggered_buffer_setup(indio_dev,
&iio_pollfunc_store_time,
@@ -1903,15 +2804,35 @@ static int stm32_adc_probe(struct platform_device *pdev)
goto err_dma_disable;
}
+ /* Get stm32-adc-core PM online */
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_set_autosuspend_delay(dev, STM32_ADC_HW_STOP_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_enable(dev);
+
+ ret = stm32_adc_hw_start(dev);
+ if (ret)
+ goto err_buffer_cleanup;
+
ret = iio_device_register(indio_dev);
if (ret) {
dev_err(&pdev->dev, "iio dev register failed\n");
- goto err_buffer_cleanup;
+ goto err_hw_stop;
}
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
return 0;
+err_hw_stop:
+ stm32_adc_hw_stop(dev);
+
err_buffer_cleanup:
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(dev);
iio_triggered_buffer_cleanup(indio_dev);
err_dma_disable:
@@ -1921,9 +2842,6 @@ static int stm32_adc_probe(struct platform_device *pdev)
adc->rx_buf, adc->rx_dma_buf);
dma_release_channel(adc->dma_chan);
}
-err_clk_disable:
- if (adc->clk)
- clk_disable_unprepare(adc->clk);
return ret;
}
@@ -1933,7 +2851,12 @@ static int stm32_adc_remove(struct platform_device *pdev)
struct stm32_adc *adc = platform_get_drvdata(pdev);
struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ pm_runtime_get_sync(&pdev->dev);
iio_device_unregister(indio_dev);
+ stm32_adc_hw_stop(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
iio_triggered_buffer_cleanup(indio_dev);
if (adc->dma_chan) {
dma_free_coherent(adc->dma_chan->device->dev,
@@ -1941,12 +2864,62 @@ static int stm32_adc_remove(struct platform_device *pdev)
adc->rx_buf, adc->rx_dma_buf);
dma_release_channel(adc->dma_chan);
}
- if (adc->clk)
- clk_disable_unprepare(adc->clk);
return 0;
}
+#if defined(CONFIG_PM_SLEEP)
+static int stm32_adc_suspend(struct device *dev)
+{
+ struct stm32_adc *adc = dev_get_drvdata(dev);
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+
+ if (iio_buffer_enabled(indio_dev))
+ __stm32_adc_buffer_predisable(indio_dev);
+
+ return pm_runtime_force_suspend(dev);
+}
+
+static int stm32_adc_resume(struct device *dev)
+{
+ struct stm32_adc *adc = dev_get_drvdata(dev);
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret < 0)
+ return ret;
+
+ if (!iio_buffer_enabled(indio_dev))
+ return 0;
+
+ ret = stm32_adc_update_scan_mode(indio_dev,
+ indio_dev->active_scan_mask);
+ if (ret < 0)
+ return ret;
+
+ return __stm32_adc_buffer_postenable(indio_dev);
+}
+#endif
+
+#if defined(CONFIG_PM)
+static int stm32_adc_runtime_suspend(struct device *dev)
+{
+ return stm32_adc_hw_stop(dev);
+}
+
+static int stm32_adc_runtime_resume(struct device *dev)
+{
+ return stm32_adc_hw_start(dev);
+}
+#endif
+
+static const struct dev_pm_ops stm32_adc_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(stm32_adc_suspend, stm32_adc_resume)
+ SET_RUNTIME_PM_OPS(stm32_adc_runtime_suspend, stm32_adc_runtime_resume,
+ NULL)
+};
+
static const struct stm32_adc_cfg stm32f4_adc_cfg = {
.regs = &stm32f4_adc_regspec,
.adc_info = &stm32f4_adc_info,
@@ -1955,18 +2928,19 @@ static const struct stm32_adc_cfg stm32f4_adc_cfg = {
.start_conv = stm32f4_adc_start_conv,
.stop_conv = stm32f4_adc_stop_conv,
.smp_cycles = stm32f4_adc_smp_cycles,
+ .is_started = stm32f4_adc_is_started,
};
static const struct stm32_adc_cfg stm32h7_adc_cfg = {
.regs = &stm32h7_adc_regspec,
.adc_info = &stm32h7_adc_info,
.trigs = stm32h7_adc_trigs,
- .selfcalib = stm32h7_adc_selfcalib,
.start_conv = stm32h7_adc_start_conv,
.stop_conv = stm32h7_adc_stop_conv,
.prepare = stm32h7_adc_prepare,
.unprepare = stm32h7_adc_unprepare,
.smp_cycles = stm32h7_adc_smp_cycles,
+ .is_started = stm32h7_adc_is_started,
};
static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
@@ -1974,12 +2948,12 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
.adc_info = &stm32h7_adc_info,
.trigs = stm32h7_adc_trigs,
.has_vregready = true,
- .selfcalib = stm32h7_adc_selfcalib,
.start_conv = stm32h7_adc_start_conv,
.stop_conv = stm32h7_adc_stop_conv,
.prepare = stm32h7_adc_prepare,
.unprepare = stm32h7_adc_unprepare,
.smp_cycles = stm32h7_adc_smp_cycles,
+ .is_started = stm32h7_adc_is_started,
};
static const struct of_device_id stm32_adc_of_match[] = {
@@ -1996,6 +2970,7 @@ static struct platform_driver stm32_adc_driver = {
.driver = {
.name = "stm32-adc",
.of_match_table = stm32_adc_of_match,
+ .pm = &stm32_adc_pm_ops,
},
};
module_platform_driver(stm32_adc_driver);
diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
index fcd4a1c..ae6b166 100644
--- a/drivers/iio/adc/stm32-dfsdm-adc.c
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -12,6 +12,11 @@
#include <linux/iio/buffer.h>
#include <linux/iio/hw-consumer.h>
#include <linux/iio/sysfs.h>
+#include <linux/iio/timer/stm32-lptim-trigger.h>
+#include <linux/iio/timer/stm32-timer-trigger.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of_device.h>
@@ -34,9 +39,21 @@
#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)
+/* Limit filter output resolution to 31 bits. (i.e. sample range is +/-2^30) */
+#define DFSDM_DATA_MAX BIT(30)
+/*
+ * Data are output as twos complement data in a 24 bit field.
+ * Data from filters are in the range +/-2^(n-1)
+ * 2^(n-1) maximum positive value cannot be coded in 2's complement n bits
+ * An extra bit is required to avoid wrap-around of the binary code for 2^(n-1)
+ * So, the resolution of samples from filter is actually limited to 23 bits
+ */
+#define DFSDM_DATA_RES 24
+
+/* Filter configuration */
+#define DFSDM_CR1_CFG_MASK (DFSDM_CR1_RCH_MASK | DFSDM_CR1_RCONT_MASK | \
+ DFSDM_CR1_RSYNC_MASK | DFSDM_CR1_JSYNC_MASK | \
+ DFSDM_CR1_JSCAN_MASK)
enum sd_converter_type {
DFSDM_AUDIO,
@@ -54,6 +71,8 @@ struct stm32_dfsdm_adc {
struct stm32_dfsdm *dfsdm;
const struct stm32_dfsdm_dev_data *dev_data;
unsigned int fl_id;
+ unsigned int nconv;
+ unsigned long smask;
/* ADC specific */
unsigned int oversamp;
@@ -114,14 +133,70 @@ static int stm32_dfsdm_str2val(const char *str,
return -EINVAL;
}
-static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl,
- unsigned int fast, unsigned int oversamp)
+/**
+ * struct stm32_dfsdm_trig_info - DFSDM trigger info
+ * @name: name of the trigger, corresponding to its source
+ * @jextsel: trigger signal selection
+ */
+struct stm32_dfsdm_trig_info {
+ const char *name;
+ unsigned int jextsel;
+};
+
+/* hardware injected trigger enable, edge selection */
+enum stm32_dfsdm_jexten {
+ STM32_DFSDM_JEXTEN_DISABLED,
+ STM32_DFSDM_JEXTEN_RISING_EDGE,
+ STM32_DFSDM_JEXTEN_FALLING_EDGE,
+ STM32_DFSDM_EXTEN_BOTH_EDGES,
+};
+
+static const struct stm32_dfsdm_trig_info stm32_dfsdm_trigs[] = {
+ { TIM1_TRGO, 0 },
+ { TIM1_TRGO2, 1 },
+ { TIM8_TRGO, 2 },
+ { TIM8_TRGO2, 3 },
+ { TIM3_TRGO, 4 },
+ { TIM4_TRGO, 5 },
+ { TIM16_OC1, 6 },
+ { TIM6_TRGO, 7 },
+ { TIM7_TRGO, 8 },
+ { LPTIM1_OUT, 26 },
+ { LPTIM2_OUT, 27 },
+ { LPTIM3_OUT, 28 },
+ {},
+};
+
+static int stm32_dfsdm_get_jextsel(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ int i;
+
+ /* lookup triggers registered by stm32 timer trigger driver */
+ for (i = 0; stm32_dfsdm_trigs[i].name; i++) {
+ /**
+ * Checking both stm32 timer trigger type and trig name
+ * should be safe against arbitrary trigger names.
+ */
+ if ((is_stm32_timer_trigger(trig) ||
+ is_stm32_lptim_trigger(trig)) &&
+ !strcmp(stm32_dfsdm_trigs[i].name, trig->name)) {
+ return stm32_dfsdm_trigs[i].jextsel;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int stm32_dfsdm_compute_osrs(struct stm32_dfsdm_filter *fl,
+ unsigned int fast, unsigned int oversamp)
{
unsigned int i, d, fosr, iosr;
- u64 res;
- s64 delta;
+ u64 res, max;
+ int bits, shift;
unsigned int m = 1; /* multiplication factor */
unsigned int p = fl->ford; /* filter order (ford) */
+ struct stm32_dfsdm_filter_osr *flo = &fl->flo[fast];
pr_debug("%s: Requested oversampling: %d\n", __func__, oversamp);
/*
@@ -140,11 +215,8 @@ static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl,
/*
* 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.
+ * to maximize data output resolution.
*/
- fl->res = 0;
for (fosr = 1; fosr <= DFSDM_MAX_FL_OVERSAMPLING; fosr++) {
for (iosr = 1; iosr <= DFSDM_MAX_INT_OVERSAMPLING; iosr++) {
if (fast)
@@ -169,50 +241,128 @@ static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl,
res = fosr;
for (i = p - 1; i > 0; i--) {
res = res * (u64)fosr;
- if (res > DFSDM_MAX_RES)
+ if (res > DFSDM_DATA_MAX)
break;
}
- if (res > DFSDM_MAX_RES)
+ if (res > DFSDM_DATA_MAX)
continue;
+
res = res * (u64)m * (u64)iosr;
- if (res > DFSDM_MAX_RES)
+ if (res > DFSDM_DATA_MAX)
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 (res >= flo->res) {
+ flo->res = res;
+ flo->fosr = fosr;
+ flo->iosr = iosr;
+
+ bits = fls(flo->res);
+ /* 8 LBSs in data register contain chan info */
+ max = flo->res << 8;
+
+ /* if resolution is not a power of two */
+ if (flo->res > BIT(bits - 1))
+ bits++;
+ else
+ max--;
+
+ shift = DFSDM_DATA_RES - bits;
+ /*
+ * Compute right/left shift
+ * Right shift is performed by hardware
+ * when transferring samples to data register.
+ * Left shift is done by software on buffer
+ */
+ if (shift > 0) {
+ /* Resolution is lower than 24 bits */
+ flo->rshift = 0;
+ flo->lshift = shift;
+ } else {
+ /*
+ * If resolution is 24 bits or more,
+ * max positive value may be ambiguous
+ * (equal to max negative value as sign
+ * bit is dropped).
+ * Reduce resolution to 23 bits (rshift)
+ * to keep the sign on bit 23 and treat
+ * saturation before rescaling on 24
+ * bits (lshift).
+ */
+ flo->rshift = 1 - shift;
+ flo->lshift = 1;
+ max >>= flo->rshift;
+ }
+ flo->max = (s32)max;
+
+ pr_debug("%s: fast %d, fosr %d, iosr %d, res 0x%llx/%d bits, rshift %d, lshift %d\n",
+ __func__, fast, flo->fosr, flo->iosr,
+ flo->res, bits, flo->rshift,
+ flo->lshift);
}
-
- if (!delta)
- return 0;
}
}
- if (!fl->res)
+ if (!flo->res)
return -EINVAL;
return 0;
}
-static int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm,
- unsigned int ch_id)
+static int stm32_dfsdm_compute_all_osrs(struct iio_dev *indio_dev,
+ unsigned int oversamp)
{
- return regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
- DFSDM_CHCFGR1_CHEN_MASK,
- DFSDM_CHCFGR1_CHEN(1));
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
+ int ret0, ret1;
+
+ memset(&fl->flo[0], 0, sizeof(fl->flo[0]));
+ memset(&fl->flo[1], 0, sizeof(fl->flo[1]));
+
+ ret0 = stm32_dfsdm_compute_osrs(fl, 0, oversamp);
+ ret1 = stm32_dfsdm_compute_osrs(fl, 1, oversamp);
+ if (ret0 < 0 && ret1 < 0) {
+ dev_err(&indio_dev->dev,
+ "Filter parameters not found: errors %d/%d\n",
+ ret0, ret1);
+ return -EINVAL;
+ }
+
+ return 0;
}
-static void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm,
- unsigned int ch_id)
+static int stm32_dfsdm_start_channel(struct stm32_dfsdm_adc *adc)
{
- regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
- DFSDM_CHCFGR1_CHEN_MASK, DFSDM_CHCFGR1_CHEN(0));
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ struct regmap *regmap = adc->dfsdm->regmap;
+ const struct iio_chan_spec *chan;
+ unsigned int bit;
+ int ret;
+
+ for_each_set_bit(bit, &adc->smask, sizeof(adc->smask) * BITS_PER_BYTE) {
+ chan = indio_dev->channels + bit;
+ ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(chan->channel),
+ DFSDM_CHCFGR1_CHEN_MASK,
+ DFSDM_CHCFGR1_CHEN(1));
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void stm32_dfsdm_stop_channel(struct stm32_dfsdm_adc *adc)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ struct regmap *regmap = adc->dfsdm->regmap;
+ const struct iio_chan_spec *chan;
+ unsigned int bit;
+
+ for_each_set_bit(bit, &adc->smask, sizeof(adc->smask) * BITS_PER_BYTE) {
+ chan = indio_dev->channels + bit;
+ regmap_update_bits(regmap, DFSDM_CHCFGR1(chan->channel),
+ DFSDM_CHCFGR1_CHEN_MASK,
+ DFSDM_CHCFGR1_CHEN(0));
+ }
}
static int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
@@ -237,9 +387,11 @@ static int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
DFSDM_CHCFGR1_CHINSEL(ch->alt_si));
}
-static int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm,
- unsigned int fl_id)
+static int stm32_dfsdm_start_filter(struct stm32_dfsdm_adc *adc,
+ unsigned int fl_id,
+ struct iio_trigger *trig)
{
+ struct stm32_dfsdm *dfsdm = adc->dfsdm;
int ret;
/* Enable filter */
@@ -248,7 +400,11 @@ static int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm,
if (ret < 0)
return ret;
- /* Start conversion */
+ /* Nothing more to do for injected (scan mode/triggered) conversions */
+ if (adc->nconv > 1 || trig)
+ return 0;
+
+ /* Software start (single or continuous) regular conversion */
return regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
DFSDM_CR1_RSWSTART_MASK,
DFSDM_CR1_RSWSTART(1));
@@ -262,22 +418,101 @@ static void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm,
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)
+static int stm32_dfsdm_filter_set_trig(struct stm32_dfsdm_adc *adc,
+ unsigned int fl_id,
+ struct iio_trigger *trig)
{
- struct regmap *regmap = dfsdm->regmap;
- struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[fl_id];
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ struct regmap *regmap = adc->dfsdm->regmap;
+ u32 jextsel = 0, jexten = STM32_DFSDM_JEXTEN_DISABLED;
+ int ret;
+
+ if (trig) {
+ ret = stm32_dfsdm_get_jextsel(indio_dev, trig);
+ if (ret < 0)
+ return ret;
+
+ /* set trigger source and polarity (default to rising edge) */
+ jextsel = ret;
+ jexten = STM32_DFSDM_JEXTEN_RISING_EDGE;
+ }
+
+ ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
+ DFSDM_CR1_JEXTSEL_MASK | DFSDM_CR1_JEXTEN_MASK,
+ DFSDM_CR1_JEXTSEL(jextsel) |
+ DFSDM_CR1_JEXTEN(jexten));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int stm32_dfsdm_channels_configure(struct stm32_dfsdm_adc *adc,
+ unsigned int fl_id,
+ struct iio_trigger *trig)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ struct regmap *regmap = adc->dfsdm->regmap;
+ struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[fl_id];
+ struct stm32_dfsdm_filter_osr *flo = &fl->flo[0];
+ const struct iio_chan_spec *chan;
+ unsigned int bit;
+ int ret;
+
+ fl->fast = 0;
+
+ /*
+ * In continuous mode, use fast mode configuration,
+ * if it provides a better resolution.
+ */
+ if (adc->nconv == 1 && !trig &&
+ (indio_dev->currentmode & INDIO_BUFFER_SOFTWARE)) {
+ if (fl->flo[1].res >= fl->flo[0].res) {
+ fl->fast = 1;
+ flo = &fl->flo[1];
+ }
+ }
+
+ if (!flo->res)
+ return -EINVAL;
+
+ for_each_set_bit(bit, &adc->smask,
+ sizeof(adc->smask) * BITS_PER_BYTE) {
+ chan = indio_dev->channels + bit;
+
+ ret = regmap_update_bits(regmap,
+ DFSDM_CHCFGR2(chan->channel),
+ DFSDM_CHCFGR2_DTRBS_MASK,
+ DFSDM_CHCFGR2_DTRBS(flo->rshift));
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc,
+ unsigned int fl_id,
+ struct iio_trigger *trig)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ struct regmap *regmap = adc->dfsdm->regmap;
+ struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[fl_id];
+ struct stm32_dfsdm_filter_osr *flo = &fl->flo[fl->fast];
+ u32 cr1;
+ const struct iio_chan_spec *chan;
+ unsigned int bit, jchg = 0;
int ret;
/* Average integrator oversampling */
ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
- DFSDM_FCR_IOSR(fl->iosr - 1));
+ DFSDM_FCR_IOSR(flo->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));
+ DFSDM_FCR_FOSR(flo->fosr - 1));
if (ret)
return ret;
@@ -286,15 +521,74 @@ static int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm,
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));
+ ret = stm32_dfsdm_filter_set_trig(adc, fl_id, trig);
if (ret)
return ret;
- return regmap_update_bits(regmap, DFSDM_CR1(fl_id),
- DFSDM_CR1_RSYNC_MASK,
- DFSDM_CR1_RSYNC(fl->sync_mode));
+ ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
+ DFSDM_CR1_FAST_MASK,
+ DFSDM_CR1_FAST(fl->fast));
+ if (ret)
+ return ret;
+
+ /*
+ * DFSDM modes configuration W.R.T audio/iio type modes
+ * ----------------------------------------------------------------
+ * Modes | regular | regular | injected | injected |
+ * | | continuous | | + scan |
+ * --------------|---------|--------------|----------|------------|
+ * single conv | x | | | |
+ * (1 chan) | | | | |
+ * --------------|---------|--------------|----------|------------|
+ * 1 Audio chan | | sample freq | | |
+ * | | or sync_mode | | |
+ * --------------|---------|--------------|----------|------------|
+ * 1 IIO chan | | sample freq | trigger | |
+ * | | or sync_mode | | |
+ * --------------|---------|--------------|----------|------------|
+ * 2+ IIO chans | | | | trigger or |
+ * | | | | sync_mode |
+ * ----------------------------------------------------------------
+ */
+ if (adc->nconv == 1 && !trig) {
+ bit = __ffs(adc->smask);
+ chan = indio_dev->channels + bit;
+
+ /* Use regular conversion for single channel without trigger */
+ cr1 = DFSDM_CR1_RCH(chan->channel);
+
+ /* Continuous conversions triggered by SPI clock in buffer mode */
+ if (indio_dev->currentmode & INDIO_BUFFER_SOFTWARE)
+ cr1 |= DFSDM_CR1_RCONT(1);
+
+ cr1 |= DFSDM_CR1_RSYNC(fl->sync_mode);
+ } else {
+ /* Use injected conversion for multiple channels */
+ for_each_set_bit(bit, &adc->smask,
+ sizeof(adc->smask) * BITS_PER_BYTE) {
+ chan = indio_dev->channels + bit;
+ jchg |= BIT(chan->channel);
+ }
+ ret = regmap_write(regmap, DFSDM_JCHGR(fl_id), jchg);
+ if (ret < 0)
+ return ret;
+
+ /* Use scan mode for multiple channels */
+ cr1 = DFSDM_CR1_JSCAN(!!(adc->nconv > 1));
+
+ /*
+ * Continuous conversions not supported in injected mode,
+ * either use:
+ * - conversions in sync with filter 0
+ * - triggered conversions
+ */
+ if (!fl->sync_mode && !trig)
+ return -EINVAL;
+ cr1 |= DFSDM_CR1_JSYNC(fl->sync_mode);
+ }
+
+ return regmap_update_bits(regmap, DFSDM_CR1(fl_id), DFSDM_CR1_CFG_MASK,
+ cr1);
}
static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
@@ -378,13 +672,36 @@ static ssize_t dfsdm_adc_audio_get_spiclk(struct iio_dev *indio_dev,
return snprintf(buf, PAGE_SIZE, "%d\n", adc->spi_freq);
}
+static int dfsdm_adc_set_samp_freq(struct iio_dev *indio_dev,
+ unsigned int sample_freq,
+ unsigned int spi_freq)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ unsigned int oversamp;
+ int ret;
+
+ oversamp = DIV_ROUND_CLOSEST(spi_freq, sample_freq);
+ if (spi_freq % sample_freq)
+ dev_dbg(&indio_dev->dev,
+ "Rate not accurate. requested (%u), actual (%u)\n",
+ sample_freq, spi_freq / oversamp);
+
+ ret = stm32_dfsdm_compute_all_osrs(indio_dev, oversamp);
+ if (ret < 0)
+ return ret;
+
+ adc->sample_freq = spi_freq / oversamp;
+ adc->oversamp = oversamp;
+
+ return 0;
+}
+
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[chan->channel];
unsigned int sample_freq = adc->sample_freq;
unsigned int spi_freq;
@@ -403,17 +720,9 @@ static ssize_t dfsdm_adc_audio_set_spiclk(struct iio_dev *indio_dev,
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");
+ ret = dfsdm_adc_set_samp_freq(indio_dev, sample_freq, spi_freq);
+ if (ret < 0)
return ret;
- }
}
adc->spi_freq = spi_freq;
@@ -421,72 +730,48 @@ static ssize_t dfsdm_adc_audio_set_spiclk(struct iio_dev *indio_dev,
}
static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc,
- const struct iio_chan_spec *chan,
- bool dma)
+ struct iio_trigger *trig)
{
struct regmap *regmap = adc->dfsdm->regmap;
int ret;
- unsigned int dma_en = 0, cont_en = 0;
- ret = stm32_dfsdm_start_channel(adc->dfsdm, chan->channel);
+ ret = stm32_dfsdm_channels_configure(adc, adc->fl_id, trig);
if (ret < 0)
return ret;
- ret = stm32_dfsdm_filter_configure(adc->dfsdm, adc->fl_id,
- chan->channel);
- 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);
+ ret = stm32_dfsdm_start_channel(adc);
if (ret < 0)
- goto stop_channels;
+ return ret;
- /* Enable conversion triggered by SPI clock*/
- ret = regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
- DFSDM_CR1_RCONT_MASK, cont_en);
+ ret = stm32_dfsdm_filter_configure(adc, adc->fl_id, trig);
if (ret < 0)
goto stop_channels;
- ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id);
+ ret = stm32_dfsdm_start_filter(adc, adc->fl_id, trig);
if (ret < 0)
- goto stop_channels;
+ goto filter_unconfigure;
return 0;
-stop_channels:
- regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
- DFSDM_CR1_RDMAEN_MASK, 0);
-
+filter_unconfigure:
regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
- DFSDM_CR1_RCONT_MASK, 0);
- stm32_dfsdm_stop_channel(adc->dfsdm, chan->channel);
+ DFSDM_CR1_CFG_MASK, 0);
+stop_channels:
+ stm32_dfsdm_stop_channel(adc);
return ret;
}
-static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc,
- const struct iio_chan_spec *chan)
+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);
+ DFSDM_CR1_CFG_MASK, 0);
- stm32_dfsdm_stop_channel(adc->dfsdm, chan->channel);
+ stm32_dfsdm_stop_channel(adc);
}
static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
@@ -494,6 +779,7 @@ static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2;
+ unsigned int rx_buf_sz = DFSDM_DMA_BUFFER_SIZE;
/*
* DMA cyclic transfers are used, buffer is split into two periods.
@@ -502,7 +788,7 @@ static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
* - one buffer (period) driver pushed to ASoC side.
*/
watermark = min(watermark, val * (unsigned int)(sizeof(u32)));
- adc->buf_sz = watermark * 2;
+ adc->buf_sz = min(rx_buf_sz, watermark * 2 * adc->nconv);
return 0;
}
@@ -532,13 +818,67 @@ static unsigned int stm32_dfsdm_adc_dma_residue(struct stm32_dfsdm_adc *adc)
return 0;
}
-static void stm32_dfsdm_audio_dma_buffer_done(void *data)
+static inline void stm32_dfsdm_process_data(struct stm32_dfsdm_adc *adc,
+ s32 *buffer)
+{
+ struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
+ struct stm32_dfsdm_filter_osr *flo = &fl->flo[fl->fast];
+ unsigned int i = adc->nconv;
+ s32 *ptr = buffer;
+
+ while (i--) {
+ /* Mask 8 LSB that contains the channel ID */
+ *ptr &= 0xFFFFFF00;
+ /* Convert 2^(n-1) sample to 2^(n-1)-1 to avoid wrap-around */
+ if (*ptr > flo->max)
+ *ptr -= 1;
+ /*
+ * Samples from filter are retrieved with 23 bits resolution
+ * or less. Shift left to align MSB on 24 bits.
+ */
+ *ptr <<= flo->lshift;
+
+ ptr++;
+ }
+}
+
+static irqreturn_t stm32_dfsdm_adc_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ int available = stm32_dfsdm_adc_dma_residue(adc);
+
+ while (available >= indio_dev->scan_bytes) {
+ s32 *buffer = (s32 *)&adc->rx_buf[adc->bufi];
+
+ stm32_dfsdm_process_data(adc, buffer);
+
+ iio_push_to_buffers_with_timestamp(indio_dev, buffer,
+ pf->timestamp);
+ available -= indio_dev->scan_bytes;
+ adc->bufi += indio_dev->scan_bytes;
+ if (adc->bufi >= adc->buf_sz)
+ adc->bufi = 0;
+ }
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static void stm32_dfsdm_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;
+ if (indio_dev->currentmode & INDIO_BUFFER_TRIGGERED) {
+ iio_trigger_poll_chained(indio_dev->trig);
+ return;
+ }
+
/*
* FIXME: In Kernel interface does not support cyclic DMA buffer,and
* offers only an interface to push data samples per samples.
@@ -553,10 +893,10 @@ static void stm32_dfsdm_audio_dma_buffer_done(void *data)
old_pos = adc->bufi;
while (available >= indio_dev->scan_bytes) {
- u32 *buffer = (u32 *)&adc->rx_buf[adc->bufi];
+ s32 *buffer = (s32 *)&adc->rx_buf[adc->bufi];
+
+ stm32_dfsdm_process_data(adc, buffer);
- /* 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) {
@@ -566,6 +906,9 @@ static void stm32_dfsdm_audio_dma_buffer_done(void *data)
adc->bufi = 0;
old_pos = 0;
}
+ /* regular iio buffer without trigger */
+ if (adc->dev_data->type == DFSDM_IIO)
+ iio_push_to_buffers(indio_dev, buffer);
}
if (adc->cb)
adc->cb(&adc->rx_buf[old_pos], adc->bufi - old_pos,
@@ -575,6 +918,15 @@ static void stm32_dfsdm_audio_dma_buffer_done(void *data)
static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ /*
+ * The DFSDM supports half-word transfers. However, for 16 bits record,
+ * 4 bytes buswidth is kept, to avoid losing samples LSBs when left
+ * shift is required.
+ */
+ struct dma_slave_config config = {
+ .src_addr = (dma_addr_t)adc->dfsdm->phys_base,
+ .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ };
struct dma_async_tx_descriptor *desc;
dma_cookie_t cookie;
int ret;
@@ -585,6 +937,14 @@ static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev)
dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__,
adc->buf_sz, adc->buf_sz / 2);
+ if (adc->nconv == 1 && !indio_dev->trig)
+ config.src_addr += DFSDM_RDATAR(adc->fl_id);
+ else
+ config.src_addr += DFSDM_JDATAR(adc->fl_id);
+ ret = dmaengine_slave_config(adc->dma_chan, &config);
+ if (ret)
+ return ret;
+
/* Prepare a DMA cyclic transaction */
desc = dmaengine_prep_dma_cyclic(adc->dma_chan,
adc->dma_buf,
@@ -594,71 +954,154 @@ static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev)
if (!desc)
return -EBUSY;
- desc->callback = stm32_dfsdm_audio_dma_buffer_done;
+ desc->callback = stm32_dfsdm_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;
- }
+ if (ret)
+ goto err_stop_dma;
/* Issue pending DMA requests */
dma_async_issue_pending(adc->dma_chan);
+ if (adc->nconv == 1 && !indio_dev->trig) {
+ /* Enable regular DMA transfer*/
+ ret = regmap_update_bits(adc->dfsdm->regmap,
+ DFSDM_CR1(adc->fl_id),
+ DFSDM_CR1_RDMAEN_MASK,
+ DFSDM_CR1_RDMAEN_MASK);
+ } else {
+ /* Enable injected DMA transfer*/
+ ret = regmap_update_bits(adc->dfsdm->regmap,
+ DFSDM_CR1(adc->fl_id),
+ DFSDM_CR1_JDMAEN_MASK,
+ DFSDM_CR1_JDMAEN_MASK);
+ }
+
+ if (ret < 0)
+ goto err_stop_dma;
+
return 0;
+
+err_stop_dma:
+ dmaengine_terminate_all(adc->dma_chan);
+
+ return ret;
}
-static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
+static void stm32_dfsdm_adc_dma_stop(struct iio_dev *indio_dev)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+ if (!adc->dma_chan)
+ return;
+
+ regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR1(adc->fl_id),
+ DFSDM_CR1_RDMAEN_MASK | DFSDM_CR1_JDMAEN_MASK, 0);
+ dmaengine_terminate_all(adc->dma_chan);
+}
+
+static int stm32_dfsdm_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+ adc->nconv = bitmap_weight(scan_mask, indio_dev->masklength);
+ adc->smask = *scan_mask;
+
+ dev_dbg(&indio_dev->dev, "nconv=%d mask=%lx\n", adc->nconv, *scan_mask);
+
+ return 0;
+}
+
+static int __stm32_dfsdm_postenable(struct iio_dev *indio_dev)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
- const struct iio_chan_spec *chan = &indio_dev->channels[0];
int ret;
/* Reset adc buffer index */
adc->bufi = 0;
+ if (adc->hwc) {
+ ret = iio_hw_consumer_enable(adc->hwc);
+ if (ret < 0)
+ return ret;
+ }
+
ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
if (ret < 0)
- return ret;
+ goto err_stop_hwc;
- ret = stm32_dfsdm_start_conv(adc, chan, true);
+ ret = stm32_dfsdm_adc_dma_start(indio_dev);
if (ret) {
- dev_err(&indio_dev->dev, "Can't start conversion\n");
+ dev_err(&indio_dev->dev, "Can't start DMA\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;
- }
+ ret = stm32_dfsdm_start_conv(adc, indio_dev->trig);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Can't start conversion\n");
+ goto err_stop_dma;
}
return 0;
-err_stop_conv:
- stm32_dfsdm_stop_conv(adc, chan);
+err_stop_dma:
+ stm32_dfsdm_adc_dma_stop(indio_dev);
stop_dfsdm:
stm32_dfsdm_stop_dfsdm(adc->dfsdm);
+err_stop_hwc:
+ if (adc->hwc)
+ iio_hw_consumer_disable(adc->hwc);
return ret;
}
-static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
+static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
+{
+ int ret;
+
+ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
+ ret = iio_triggered_buffer_postenable(indio_dev);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = __stm32_dfsdm_postenable(indio_dev);
+ if (ret < 0)
+ goto err_predisable;
+
+ return 0;
+
+err_predisable:
+ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED)
+ iio_triggered_buffer_predisable(indio_dev);
+
+ return ret;
+}
+
+static void __stm32_dfsdm_predisable(struct iio_dev *indio_dev)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
- const struct iio_chan_spec *chan = &indio_dev->channels[0];
- if (adc->dma_chan)
- dmaengine_terminate_all(adc->dma_chan);
+ stm32_dfsdm_stop_conv(adc);
- stm32_dfsdm_stop_conv(adc, chan);
+ stm32_dfsdm_adc_dma_stop(indio_dev);
stm32_dfsdm_stop_dfsdm(adc->dfsdm);
+ if (adc->hwc)
+ iio_hw_consumer_disable(adc->hwc);
+}
+
+static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
+{
+ __stm32_dfsdm_predisable(indio_dev);
+
+ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED)
+ iio_triggered_buffer_predisable(indio_dev);
+
return 0;
}
@@ -736,7 +1179,9 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
if (ret < 0)
goto stop_dfsdm;
- ret = stm32_dfsdm_start_conv(adc, chan, false);
+ adc->nconv = 1;
+ adc->smask = BIT(chan->scan_index);
+ ret = stm32_dfsdm_start_conv(adc, NULL);
if (ret < 0) {
regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
@@ -757,7 +1202,9 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
else
ret = IIO_VAL_INT;
- stm32_dfsdm_stop_conv(adc, chan);
+ stm32_dfsdm_stop_conv(adc);
+
+ stm32_dfsdm_process_data(adc, res);
stop_dfsdm:
stm32_dfsdm_stop_dfsdm(adc->dfsdm);
@@ -770,23 +1217,29 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
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[chan->channel];
unsigned int spi_freq;
int ret = -EINVAL;
switch (mask) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
- ret = stm32_dfsdm_set_osrs(fl, 0, val);
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+ ret = stm32_dfsdm_compute_all_osrs(indio_dev, val);
if (!ret)
adc->oversamp = val;
-
+ iio_device_release_direct_mode(indio_dev);
return ret;
case IIO_CHAN_INFO_SAMP_FREQ:
if (!val)
return -EINVAL;
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
switch (ch->src) {
case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL:
spi_freq = adc->dfsdm->spi_master_freq;
@@ -799,20 +1252,9 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
spi_freq = adc->spi_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;
+ ret = dfsdm_adc_set_samp_freq(indio_dev, val, spi_freq);
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
}
return -EINVAL;
@@ -827,11 +1269,15 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
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);
+ iio_device_release_direct_mode(indio_dev);
return ret;
}
ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
@@ -840,8 +1286,10 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
dev_err(&indio_dev->dev,
"%s: Conversion failed (channel %d)\n",
__func__, chan->channel);
+ iio_device_release_direct_mode(indio_dev);
return ret;
}
+ iio_device_release_direct_mode(indio_dev);
return IIO_VAL_INT;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
@@ -858,15 +1306,25 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
+static int stm32_dfsdm_validate_trigger(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ return stm32_dfsdm_get_jextsel(indio_dev, trig) < 0 ? -EINVAL : 0;
+}
+
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,
+ .update_scan_mode = stm32_dfsdm_update_scan_mode,
};
static const struct iio_info stm32_dfsdm_info_adc = {
+ .hwfifo_set_watermark = stm32_dfsdm_set_watermark,
.read_raw = stm32_dfsdm_read_raw,
.write_raw = stm32_dfsdm_write_raw,
+ .update_scan_mode = stm32_dfsdm_update_scan_mode,
+ .validate_trigger = stm32_dfsdm_validate_trigger,
};
static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
@@ -926,12 +1384,6 @@ static void stm32_dfsdm_dma_release(struct iio_dev *indio_dev)
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)
@@ -941,23 +1393,14 @@ static int stm32_dfsdm_dma_request(struct iio_dev *indio_dev)
DFSDM_DMA_BUFFER_SIZE,
&adc->dma_buf, GFP_KERNEL);
if (!adc->rx_buf) {
- ret = -ENOMEM;
- goto err_release;
+ dma_release_channel(adc->dma_chan);
+ return -ENOMEM;
}
- ret = dmaengine_slave_config(adc->dma_chan, &config);
- if (ret)
- goto err_free;
+ indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
+ indio_dev->setup_ops = &stm32_dfsdm_buffer_setup_ops;
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,
@@ -978,14 +1421,15 @@ static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
* 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);
+ ch->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ);
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.shift = 8;
}
+ ch->scan_type.sign = 's';
ch->scan_type.realbits = 24;
ch->scan_type.storagebits = 32;
@@ -1000,9 +1444,6 @@ static int stm32_dfsdm_audio_init(struct iio_dev *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;
@@ -1034,8 +1475,7 @@ static int stm32_dfsdm_adc_init(struct iio_dev *indio_dev)
int ret, chan_idx;
adc->oversamp = DFSDM_DEFAULT_OVERSAMPLING;
- ret = stm32_dfsdm_set_osrs(&adc->dfsdm->fl_list[adc->fl_id], 0,
- adc->oversamp);
+ ret = stm32_dfsdm_compute_all_osrs(indio_dev, adc->oversamp);
if (ret < 0)
return ret;
@@ -1070,6 +1510,25 @@ static int stm32_dfsdm_adc_init(struct iio_dev *indio_dev)
init_completion(&adc->completion);
+ /* Optionally request DMA */
+ if (stm32_dfsdm_dma_request(indio_dev)) {
+ dev_dbg(&indio_dev->dev, "No DMA support\n");
+ return 0;
+ }
+
+ ret = iio_triggered_buffer_setup(indio_dev,
+ &iio_pollfunc_store_time,
+ &stm32_dfsdm_adc_trigger_handler,
+ &stm32_dfsdm_buffer_setup_ops);
+ if (ret) {
+ stm32_dfsdm_dma_release(indio_dev);
+ dev_err(&indio_dev->dev, "buffer setup failed\n");
+ return ret;
+ }
+
+ /* lptimer/timer hardware triggers */
+ indio_dev->modes |= INDIO_HARDWARE_TRIGGERED;
+
return 0;
}
@@ -1117,7 +1576,7 @@ static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
iio->dev.parent = dev;
iio->dev.of_node = np;
- iio->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
+ iio->modes = INDIO_DIRECT_MODE;
platform_set_drvdata(pdev, adc);
@@ -1144,6 +1603,12 @@ static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
* So IRQ associated to filter instance 0 is dedicated to the Filter 0.
*/
irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ if (irq != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get IRQ: %d\n", irq);
+ return irq;
+ }
+
ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
0, pdev->name, adc);
if (ret < 0) {
@@ -1203,10 +1668,48 @@ static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int stm32_dfsdm_adc_suspend(struct device *dev)
+{
+ struct stm32_dfsdm_adc *adc = dev_get_drvdata(dev);
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+
+ if (iio_buffer_enabled(indio_dev))
+ __stm32_dfsdm_predisable(indio_dev);
+
+ return 0;
+}
+static int stm32_dfsdm_adc_resume(struct device *dev)
+{
+ struct stm32_dfsdm_adc *adc = dev_get_drvdata(dev);
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ const struct iio_chan_spec *chan;
+ struct stm32_dfsdm_channel *ch;
+ int i, ret;
+
+ /* restore channels configuration */
+ for (i = 0; i < indio_dev->num_channels; i++) {
+ chan = indio_dev->channels + i;
+ ch = &adc->dfsdm->ch_list[chan->channel];
+ ret = stm32_dfsdm_chan_configure(adc->dfsdm, ch);
+ if (ret)
+ return ret;
+ }
+
+ if (iio_buffer_enabled(indio_dev))
+ __stm32_dfsdm_postenable(indio_dev);
+
+ return 0;
+}
+#endif
+static SIMPLE_DEV_PM_OPS(stm32_dfsdm_adc_pm_ops,
+ stm32_dfsdm_adc_suspend, stm32_dfsdm_adc_resume);
+
static struct platform_driver stm32_dfsdm_adc_driver = {
.driver = {
.name = "stm32-dfsdm-adc",
.of_match_table = stm32_dfsdm_adc_match,
+ .pm = &stm32_dfsdm_adc_pm_ops,
},
.probe = stm32_dfsdm_adc_probe,
.remove = stm32_dfsdm_adc_remove,
diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c
index bf089f5..9609515 100644
--- a/drivers/iio/adc/stm32-dfsdm-core.c
+++ b/drivers/iio/adc/stm32-dfsdm-core.c
@@ -12,6 +12,8 @@
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -90,6 +92,36 @@ struct dfsdm_priv {
struct clk *aclk; /* audio clock */
};
+static inline struct dfsdm_priv *to_stm32_dfsdm_priv(struct stm32_dfsdm *dfsdm)
+{
+ return container_of(dfsdm, struct dfsdm_priv, dfsdm);
+}
+
+static int stm32_dfsdm_clk_prepare_enable(struct stm32_dfsdm *dfsdm)
+{
+ struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
+ int ret;
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret || !priv->aclk)
+ return ret;
+
+ ret = clk_prepare_enable(priv->aclk);
+ if (ret)
+ clk_disable_unprepare(priv->clk);
+
+ return ret;
+}
+
+static void stm32_dfsdm_clk_disable_unprepare(struct stm32_dfsdm *dfsdm)
+{
+ struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
+
+ if (priv->aclk)
+ clk_disable_unprepare(priv->aclk);
+ clk_disable_unprepare(priv->clk);
+}
+
/**
* stm32_dfsdm_start_dfsdm - start global dfsdm interface.
*
@@ -98,24 +130,17 @@ struct dfsdm_priv {
*/
int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
{
- struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+ struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
struct device *dev = &priv->pdev->dev;
unsigned int clk_div = priv->spi_clk_out_div, clk_src;
int ret;
if (atomic_inc_return(&priv->n_active_ch) == 1) {
- ret = clk_prepare_enable(priv->clk);
+ ret = pm_runtime_get_sync(dev);
if (ret < 0) {
- dev_err(dev, "Failed to start clock\n");
+ pm_runtime_put_noidle(dev);
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;
- }
- }
/* select clock source, e.g. 0 for "dfsdm" or 1 for "audio" */
clk_src = priv->aclk ? 1 : 0;
@@ -123,21 +148,21 @@ int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
DFSDM_CHCFGR1_CKOUTSRC_MASK,
DFSDM_CHCFGR1_CKOUTSRC(clk_src));
if (ret < 0)
- goto disable_aclk;
+ goto pm_put;
/* 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;
+ goto pm_put;
/* 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;
+ goto pm_put;
}
dev_dbg(dev, "%s: n_active_ch %d\n", __func__,
@@ -145,11 +170,8 @@ int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
return 0;
-disable_aclk:
- clk_disable_unprepare(priv->aclk);
-disable_clk:
- clk_disable_unprepare(priv->clk);
-
+pm_put:
+ pm_runtime_put_sync(dev);
error_ret:
atomic_dec(&priv->n_active_ch);
@@ -165,7 +187,7 @@ EXPORT_SYMBOL_GPL(stm32_dfsdm_start_dfsdm);
*/
int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm)
{
- struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+ struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
int ret;
if (atomic_dec_and_test(&priv->n_active_ch)) {
@@ -183,9 +205,7 @@ int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm)
if (ret < 0)
return ret;
- clk_disable_unprepare(priv->clk);
- if (priv->aclk)
- clk_disable_unprepare(priv->aclk);
+ pm_runtime_put_sync(&priv->pdev->dev);
}
dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
atomic_read(&priv->n_active_ch));
@@ -213,6 +233,8 @@ static int stm32_dfsdm_parse_of(struct platform_device *pdev,
}
priv->dfsdm.phys_base = res->start;
priv->dfsdm.base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->dfsdm.base))
+ return PTR_ERR(priv->dfsdm.base);
/*
* "dfsdm" clock is mandatory for DFSDM peripheral clocking.
@@ -222,8 +244,10 @@ static int stm32_dfsdm_parse_of(struct platform_device *pdev,
*/
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;
+ ret = PTR_ERR(priv->clk);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Failed to get clock (%d)\n", ret);
+ return ret;
}
priv->aclk = devm_clk_get(&pdev->dev, "audio");
@@ -243,13 +267,18 @@ static int stm32_dfsdm_parse_of(struct platform_device *pdev,
return 0;
}
- priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1;
+ priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem);
+
+ /* round up divider when clkout isn't accurate (e.g. !rem) */
+ if (priv->spi_clk_out_div && !rem)
+ priv->spi_clk_out_div--;
+
if (!priv->spi_clk_out_div) {
/* spi_clk_out_div == 0 means ckout is OFF */
dev_err(&pdev->dev, "spi-max-frequency not achievable\n");
return -EINVAL;
}
- priv->dfsdm.spi_master_freq = spi_freq;
+ priv->dfsdm.spi_master_freq = clk_freq / (priv->spi_clk_out_div + 1);
if (rem) {
dev_warn(&pdev->dev, "SPI clock not accurate\n");
@@ -318,14 +347,115 @@ static int stm32_dfsdm_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dfsdm);
- return devm_of_platform_populate(&pdev->dev);
+ ret = stm32_dfsdm_clk_prepare_enable(dfsdm);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to start clock\n");
+ return ret;
+ }
+
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+ if (ret)
+ goto pm_put;
+
+ pm_runtime_put(&pdev->dev);
+
+ return 0;
+
+pm_put:
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+ stm32_dfsdm_clk_disable_unprepare(dfsdm);
+
+ return ret;
+}
+
+static int stm32_dfsdm_core_remove(struct platform_device *pdev)
+{
+ struct stm32_dfsdm *dfsdm = platform_get_drvdata(pdev);
+
+ pm_runtime_get_sync(&pdev->dev);
+ of_platform_depopulate(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+ stm32_dfsdm_clk_disable_unprepare(dfsdm);
+
+ return 0;
+}
+
+#if defined CONFIG_PM_SLEEP
+static int stm32_dfsdm_core_suspend(struct device *dev)
+{
+ struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);
+ struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
+ int ret;
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret)
+ return ret;
+
+ /* Balance devm_regmap_init_mmio_clk() clk_prepare() */
+ clk_unprepare(priv->clk);
+
+ return pinctrl_pm_select_sleep_state(dev);
}
+static int stm32_dfsdm_core_resume(struct device *dev)
+{
+ struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);
+ struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
+ int ret;
+
+ ret = pinctrl_pm_select_default_state(dev);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare(priv->clk);
+ if (ret)
+ return ret;
+
+ return pm_runtime_force_resume(dev);
+}
+#endif
+
+#ifdef CONFIG_PM
+static int stm32_dfsdm_core_runtime_suspend(struct device *dev)
+{
+ struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);
+
+ stm32_dfsdm_clk_disable_unprepare(dfsdm);
+
+ return 0;
+}
+
+static int stm32_dfsdm_core_runtime_resume(struct device *dev)
+{
+ struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);
+
+ return stm32_dfsdm_clk_prepare_enable(dfsdm);
+}
+#endif
+
+static const struct dev_pm_ops stm32_dfsdm_core_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(stm32_dfsdm_core_suspend,
+ stm32_dfsdm_core_resume)
+ SET_RUNTIME_PM_OPS(stm32_dfsdm_core_runtime_suspend,
+ stm32_dfsdm_core_runtime_resume,
+ NULL)
+};
+
static struct platform_driver stm32_dfsdm_driver = {
.probe = stm32_dfsdm_probe,
+ .remove = stm32_dfsdm_core_remove,
.driver = {
.name = "stm32-dfsdm",
.of_match_table = stm32_dfsdm_of_match,
+ .pm = &stm32_dfsdm_core_pm_ops,
},
};
diff --git a/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h
index 8708394..5dbdae4 100644
--- a/drivers/iio/adc/stm32-dfsdm.h
+++ b/drivers/iio/adc/stm32-dfsdm.h
@@ -243,19 +243,33 @@ enum stm32_dfsdm_sinc_order {
};
/**
- * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
+ * struct stm32_dfsdm_filter_osr - DFSDM filter settings linked to oversampling
* @iosr: integrator oversampling
* @fosr: filter oversampling
- * @ford: filter order
+ * @rshift: output sample right shift (hardware shift)
+ * @lshift: output sample left shift (software shift)
* @res: output sample resolution
+ * @max: output sample maximum positive value
+ */
+struct stm32_dfsdm_filter_osr {
+ unsigned int iosr;
+ unsigned int fosr;
+ unsigned int rshift;
+ unsigned int lshift;
+ u64 res;
+ s32 max;
+};
+
+/**
+ * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
+ * @ford: filter order
+ * @flo: filter oversampling data table indexed by fast mode flag
* @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;
+ struct stm32_dfsdm_filter_osr flo[2];
unsigned int sync_mode;
unsigned int fast;
};
diff --git a/drivers/iio/counter/stm32-lptimer-cnt.c b/drivers/iio/counter/stm32-lptimer-cnt.c
index 42fb8ba..2a49cce0e 100644
--- a/drivers/iio/counter/stm32-lptimer-cnt.c
+++ b/drivers/iio/counter/stm32-lptimer-cnt.c
@@ -14,6 +14,7 @@
#include <linux/iio/iio.h>
#include <linux/mfd/stm32-lptimer.h>
#include <linux/module.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
struct stm32_lptim_cnt {
@@ -23,6 +24,7 @@ struct stm32_lptim_cnt {
u32 preset;
u32 polarity;
u32 quadrature_mode;
+ bool enabled;
};
static int stm32_lptim_is_enabled(struct stm32_lptim_cnt *priv)
@@ -50,6 +52,7 @@ static int stm32_lptim_set_enable_state(struct stm32_lptim_cnt *priv,
if (!enable) {
clk_disable(priv->clk);
+ priv->enabled = false;
return 0;
}
@@ -79,6 +82,7 @@ static int stm32_lptim_set_enable_state(struct stm32_lptim_cnt *priv,
regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
return ret;
}
+ priv->enabled = true;
/* Start LP timer in continuous mode */
return regmap_update_bits(priv->regmap, STM32_LPTIM_CR,
@@ -361,6 +365,56 @@ static int stm32_lptim_cnt_probe(struct platform_device *pdev)
return devm_iio_device_register(&pdev->dev, indio_dev);
}
+#ifdef CONFIG_PM_SLEEP
+static int stm32_lptim_cnt_suspend(struct device *dev)
+{
+ struct stm32_lptim_cnt *priv = dev_get_drvdata(dev);
+ int ret;
+
+ /* Only take care of enabled counter: don't disturb other MFD child */
+ if (priv->enabled) {
+ ret = stm32_lptim_setup(priv, 0);
+ if (ret)
+ return ret;
+
+ ret = stm32_lptim_set_enable_state(priv, 0);
+ if (ret)
+ return ret;
+
+ /* Force enable state for later resume */
+ priv->enabled = true;
+ }
+
+ return pinctrl_pm_select_sleep_state(dev);
+}
+
+static int stm32_lptim_cnt_resume(struct device *dev)
+{
+ struct stm32_lptim_cnt *priv = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pinctrl_pm_select_default_state(dev);
+ if (ret)
+ return ret;
+
+ if (priv->enabled) {
+ priv->enabled = false;
+ ret = stm32_lptim_setup(priv, 1);
+ if (ret)
+ return ret;
+
+ ret = stm32_lptim_set_enable_state(priv, 1);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(stm32_lptim_cnt_pm_ops, stm32_lptim_cnt_suspend,
+ stm32_lptim_cnt_resume);
+
static const struct of_device_id stm32_lptim_cnt_of_match[] = {
{ .compatible = "st,stm32-lptimer-counter", },
{},
@@ -372,6 +426,7 @@ static struct platform_driver stm32_lptim_cnt_driver = {
.driver = {
.name = "stm32-lptimer-counter",
.of_match_table = stm32_lptim_cnt_of_match,
+ .pm = &stm32_lptim_cnt_pm_ops,
},
};
module_platform_driver(stm32_lptim_cnt_driver);
diff --git a/drivers/iio/dac/stm32-dac-core.c b/drivers/iio/dac/stm32-dac-core.c
index d0fb312..280322b 100644
--- a/drivers/iio/dac/stm32-dac-core.c
+++ b/drivers/iio/dac/stm32-dac-core.c
@@ -11,6 +11,7 @@
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
@@ -50,6 +51,41 @@ static const struct regmap_config stm32_dac_regmap_cfg = {
.max_register = 0x3fc,
};
+static int stm32_dac_core_hw_start(struct device *dev)
+{
+ struct stm32_dac_common *common = dev_get_drvdata(dev);
+ struct stm32_dac_priv *priv = to_stm32_dac_priv(common);
+ int ret;
+
+ ret = regulator_enable(priv->vref);
+ if (ret < 0) {
+ dev_err(dev, "vref enable failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(priv->pclk);
+ if (ret < 0) {
+ dev_err(dev, "pclk enable failed: %d\n", ret);
+ goto err_regulator_disable;
+ }
+
+ return 0;
+
+err_regulator_disable:
+ regulator_disable(priv->vref);
+
+ return ret;
+}
+
+static void stm32_dac_core_hw_stop(struct device *dev)
+{
+ struct stm32_dac_common *common = dev_get_drvdata(dev);
+ struct stm32_dac_priv *priv = to_stm32_dac_priv(common);
+
+ clk_disable_unprepare(priv->pclk);
+ regulator_disable(priv->vref);
+}
+
static int stm32_dac_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -66,6 +102,8 @@ static int stm32_dac_probe(struct platform_device *pdev)
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
+ platform_set_drvdata(pdev, &priv->common);
+
cfg = (const struct stm32_dac_cfg *)
of_match_device(dev->driver->of_match_table, dev)->data;
@@ -74,11 +112,19 @@ static int stm32_dac_probe(struct platform_device *pdev)
if (IS_ERR(mmio))
return PTR_ERR(mmio);
- regmap = devm_regmap_init_mmio(dev, mmio, &stm32_dac_regmap_cfg);
+ regmap = devm_regmap_init_mmio_clk(dev, "pclk", mmio,
+ &stm32_dac_regmap_cfg);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
priv->common.regmap = regmap;
+ priv->pclk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(priv->pclk)) {
+ ret = PTR_ERR(priv->pclk);
+ dev_err(dev, "pclk get failed\n");
+ return ret;
+ }
+
priv->vref = devm_regulator_get(dev, "vref");
if (IS_ERR(priv->vref)) {
ret = PTR_ERR(priv->vref);
@@ -86,33 +132,22 @@ static int stm32_dac_probe(struct platform_device *pdev)
return ret;
}
- ret = regulator_enable(priv->vref);
- if (ret < 0) {
- dev_err(dev, "vref enable failed\n");
- return ret;
- }
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ ret = stm32_dac_core_hw_start(dev);
+ if (ret)
+ goto err_pm_stop;
ret = regulator_get_voltage(priv->vref);
if (ret < 0) {
dev_err(dev, "vref get voltage failed, %d\n", ret);
- goto err_vref;
+ goto err_hw_stop;
}
priv->common.vref_mv = ret / 1000;
dev_dbg(dev, "vref+=%dmV\n", priv->common.vref_mv);
- priv->pclk = devm_clk_get(dev, "pclk");
- if (IS_ERR(priv->pclk)) {
- ret = PTR_ERR(priv->pclk);
- dev_err(dev, "pclk get failed\n");
- goto err_vref;
- }
-
- ret = clk_prepare_enable(priv->pclk);
- if (ret < 0) {
- dev_err(dev, "pclk enable failed\n");
- goto err_vref;
- }
-
priv->rst = devm_reset_control_get_exclusive(dev, NULL);
if (!IS_ERR(priv->rst)) {
reset_control_assert(priv->rst);
@@ -128,39 +163,83 @@ static int stm32_dac_probe(struct platform_device *pdev)
priv->common.hfsel ?
STM32H7_DAC_CR_HFSEL : 0);
if (ret)
- goto err_pclk;
+ goto err_hw_stop;
}
- platform_set_drvdata(pdev, &priv->common);
ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, dev);
if (ret < 0) {
dev_err(dev, "failed to populate DT children\n");
- goto err_pclk;
+ goto err_hw_stop;
}
+ pm_runtime_put(dev);
+
return 0;
-err_pclk:
- clk_disable_unprepare(priv->pclk);
-err_vref:
- regulator_disable(priv->vref);
+err_hw_stop:
+ stm32_dac_core_hw_stop(dev);
+err_pm_stop:
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(dev);
return ret;
}
static int stm32_dac_remove(struct platform_device *pdev)
{
- struct stm32_dac_common *common = platform_get_drvdata(pdev);
+ pm_runtime_get_sync(&pdev->dev);
+ of_platform_depopulate(&pdev->dev);
+ stm32_dac_core_hw_stop(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+
+ return 0;
+}
+
+#if defined(CONFIG_PM_SLEEP)
+static int stm32_dac_core_resume(struct device *dev)
+{
+ struct stm32_dac_common *common = dev_get_drvdata(dev);
struct stm32_dac_priv *priv = to_stm32_dac_priv(common);
+ int ret;
- of_platform_depopulate(&pdev->dev);
- clk_disable_unprepare(priv->pclk);
- regulator_disable(priv->vref);
+ /* Unconditionally restore hfsel (maybe lost under low power state) */
+ if (priv->common.hfsel) {
+ ret = regmap_update_bits(priv->common.regmap, STM32_DAC_CR,
+ STM32H7_DAC_CR_HFSEL,
+ STM32H7_DAC_CR_HFSEL);
+ if (ret)
+ return ret;
+ }
+
+ return pm_runtime_force_resume(dev);
+}
+#endif
+
+#if defined(CONFIG_PM)
+static int stm32_dac_core_runtime_suspend(struct device *dev)
+{
+ stm32_dac_core_hw_stop(dev);
return 0;
}
+static int stm32_dac_core_runtime_resume(struct device *dev)
+{
+ return stm32_dac_core_hw_start(dev);
+}
+#endif
+
+static const struct dev_pm_ops stm32_dac_core_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, stm32_dac_core_resume)
+ SET_RUNTIME_PM_OPS(stm32_dac_core_runtime_suspend,
+ stm32_dac_core_runtime_resume,
+ NULL)
+};
+
static const struct stm32_dac_cfg stm32h7_dac_cfg = {
.has_hfsel = true,
};
@@ -182,6 +261,7 @@ static struct platform_driver stm32_dac_driver = {
.driver = {
.name = "stm32-dac-core",
.of_match_table = stm32_dac_of_match,
+ .pm = &stm32_dac_core_pm_ops,
},
};
module_platform_driver(stm32_dac_driver);
diff --git a/drivers/iio/dac/stm32-dac.c b/drivers/iio/dac/stm32-dac.c
index cce26a3..0a8abc5 100644
--- a/drivers/iio/dac/stm32-dac.c
+++ b/drivers/iio/dac/stm32-dac.c
@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include "stm32-dac-core.h"
@@ -20,6 +21,8 @@
#define STM32_DAC_CHANNEL_2 2
#define STM32_DAC_IS_CHAN_1(ch) ((ch) & STM32_DAC_CHANNEL_1)
+#define STM32_DAC_AUTO_SUSPEND_DELAY_MS 2000
+
/**
* struct stm32_dac - private data of DAC driver
* @common: reference to DAC common data
@@ -49,15 +52,34 @@ static int stm32_dac_set_enable_state(struct iio_dev *indio_dev, int ch,
bool enable)
{
struct stm32_dac *dac = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
u32 msk = STM32_DAC_IS_CHAN_1(ch) ? STM32_DAC_CR_EN1 : STM32_DAC_CR_EN2;
u32 en = enable ? msk : 0;
int ret;
+ /* already enabled / disabled ? */
+ mutex_lock(&indio_dev->mlock);
+ ret = stm32_dac_is_enabled(indio_dev, ch);
+ if (ret < 0 || enable == !!ret) {
+ mutex_unlock(&indio_dev->mlock);
+ return ret < 0 ? ret : 0;
+ }
+
+ if (enable) {
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(dev);
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+ }
+ }
+
ret = regmap_update_bits(dac->common->regmap, STM32_DAC_CR, msk, en);
+ mutex_unlock(&indio_dev->mlock);
if (ret < 0) {
dev_err(&indio_dev->dev, "%s failed\n", en ?
"Enable" : "Disable");
- return ret;
+ goto err_put_pm;
}
/*
@@ -68,7 +90,20 @@ static int stm32_dac_set_enable_state(struct iio_dev *indio_dev, int ch,
if (en && dac->common->hfsel)
udelay(1);
+ if (!enable) {
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ }
+
return 0;
+
+err_put_pm:
+ if (enable) {
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ }
+
+ return ret;
}
static int stm32_dac_get_value(struct stm32_dac *dac, int channel, int *val)
@@ -272,6 +307,7 @@ static int stm32_dac_chan_of_init(struct iio_dev *indio_dev)
static int stm32_dac_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
struct iio_dev *indio_dev;
struct stm32_dac *dac;
int ret;
@@ -296,9 +332,63 @@ static int stm32_dac_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
- return devm_iio_device_register(&pdev->dev, indio_dev);
+ /* Get stm32-dac-core PM online */
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_set_autosuspend_delay(dev, STM32_DAC_AUTO_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_enable(dev);
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto err_pm_put;
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+
+err_pm_put:
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(dev);
+
+ return ret;
}
+static int stm32_dac_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+ pm_runtime_get_sync(&pdev->dev);
+ iio_device_unregister(indio_dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+
+ return 0;
+}
+
+#if defined(CONFIG_PM_SLEEP)
+static int stm32_dac_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ int channel = indio_dev->channels[0].channel;
+ int ret;
+
+ /* Ensure DAC is disabled before suspend */
+ ret = stm32_dac_is_enabled(indio_dev, channel);
+ if (ret)
+ return ret < 0 ? ret : -EBUSY;
+
+ return pm_runtime_force_suspend(dev);
+}
+#endif
+
+static const struct dev_pm_ops stm32_dac_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(stm32_dac_suspend, pm_runtime_force_resume)
+};
+
static const struct of_device_id stm32_dac_of_match[] = {
{ .compatible = "st,stm32-dac", },
{},
@@ -307,9 +397,11 @@ MODULE_DEVICE_TABLE(of, stm32_dac_of_match);
static struct platform_driver stm32_dac_driver = {
.probe = stm32_dac_probe,
+ .remove = stm32_dac_remove,
.driver = {
.name = "stm32-dac",
.of_match_table = stm32_dac_of_match,
+ .pm = &stm32_dac_pm_ops,
},
};
module_platform_driver(stm32_dac_driver);
diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c
index ccf1ce6..4a4ce3c 100644
--- a/drivers/iio/trigger/stm32-timer-trigger.c
+++ b/drivers/iio/trigger/stm32-timer-trigger.c
@@ -12,6 +12,7 @@
#include <linux/iio/trigger.h>
#include <linux/mfd/stm32-timers.h>
#include <linux/module.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
@@ -79,10 +80,20 @@ struct stm32_timer_trigger {
struct device *dev;
struct regmap *regmap;
struct clk *clk;
+ bool clk_enabled;
u32 max_arr;
const void *triggers;
const void *valids;
bool has_trgo2;
+ struct mutex lock; /* concurrent sysfs configuration */
+ unsigned int freq;
+ bool counter_en;
+ u32 cr1;
+ u32 cr2;
+ u32 psc;
+ u32 arr;
+ u32 cnt;
+ u32 smcr;
};
struct stm32_timer_trigger_cfg {
@@ -106,7 +117,7 @@ static int stm32_timer_start(struct stm32_timer_trigger *priv,
{
unsigned long long prd, div;
int prescaler = 0;
- u32 ccer, cr1;
+ u32 ccer;
/* Period and prescaler values depends of clock rate */
div = (unsigned long long)clk_get_rate(priv->clk);
@@ -136,9 +147,11 @@ static int stm32_timer_start(struct stm32_timer_trigger *priv,
if (ccer & TIM_CCER_CCXE)
return -EBUSY;
- regmap_read(priv->regmap, TIM_CR1, &cr1);
- if (!(cr1 & TIM_CR1_CEN))
+ mutex_lock(&priv->lock);
+ if (!priv->clk_enabled) {
+ priv->clk_enabled = true;
clk_enable(priv->clk);
+ }
regmap_write(priv->regmap, TIM_PSC, prescaler);
regmap_write(priv->regmap, TIM_ARR, prd - 1);
@@ -157,30 +170,41 @@ static int stm32_timer_start(struct stm32_timer_trigger *priv,
/* Enable controller */
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN);
+ mutex_unlock(&priv->lock);
return 0;
}
-static void stm32_timer_stop(struct stm32_timer_trigger *priv)
+static void stm32_timer_stop(struct stm32_timer_trigger *priv,
+ struct iio_trigger *trig)
{
- u32 ccer, cr1;
+ u32 ccer;
regmap_read(priv->regmap, TIM_CCER, &ccer);
if (ccer & TIM_CCER_CCXE)
return;
- regmap_read(priv->regmap, TIM_CR1, &cr1);
- if (cr1 & TIM_CR1_CEN)
- clk_disable(priv->clk);
-
+ mutex_lock(&priv->lock);
/* Stop timer */
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, 0);
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0);
regmap_write(priv->regmap, TIM_PSC, 0);
regmap_write(priv->regmap, TIM_ARR, 0);
+ /* Force disable master mode */
+ if (stm32_timer_is_trgo2_name(trig->name))
+ regmap_update_bits(priv->regmap, TIM_CR2, TIM_CR2_MMS2, 0);
+ else
+ regmap_update_bits(priv->regmap, TIM_CR2, TIM_CR2_MMS, 0);
+
/* Make sure that registers are updated */
regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG);
+
+ if (priv->clk_enabled) {
+ priv->clk_enabled = false;
+ clk_disable(priv->clk);
+ }
+ mutex_unlock(&priv->lock);
}
static ssize_t stm32_tt_store_frequency(struct device *dev,
@@ -197,12 +221,13 @@ static ssize_t stm32_tt_store_frequency(struct device *dev,
return ret;
if (freq == 0) {
- stm32_timer_stop(priv);
+ stm32_timer_stop(priv, trig);
} else {
ret = stm32_timer_start(priv, trig, freq);
if (ret)
return ret;
}
+ priv->freq = freq;
return len;
}
@@ -295,11 +320,15 @@ static ssize_t stm32_tt_store_master_mode(struct device *dev,
for (i = 0; i <= master_mode_max; i++) {
if (!strncmp(master_mode_table[i], buf,
strlen(master_mode_table[i]))) {
+ mutex_lock(&priv->lock);
+ if (!priv->clk_enabled) {
+ /* Clock should be enabled first */
+ priv->clk_enabled = true;
+ clk_enable(priv->clk);
+ }
regmap_update_bits(priv->regmap, TIM_CR2, mask,
i << shift);
- /* Make sure that registers are updated */
- regmap_update_bits(priv->regmap, TIM_EGR,
- TIM_EGR_UG, TIM_EGR_UG);
+ mutex_unlock(&priv->lock);
return len;
}
}
@@ -437,7 +466,6 @@ static int stm32_counter_write_raw(struct iio_dev *indio_dev,
int val, int val2, long mask)
{
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
- u32 dat;
switch (mask) {
case IIO_CHAN_INFO_RAW:
@@ -448,19 +476,24 @@ static int stm32_counter_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
case IIO_CHAN_INFO_ENABLE:
+ mutex_lock(&priv->lock);
if (val) {
- regmap_read(priv->regmap, TIM_CR1, &dat);
- if (!(dat & TIM_CR1_CEN))
+ if (!priv->clk_enabled) {
+ priv->clk_enabled = true;
clk_enable(priv->clk);
+ }
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN,
TIM_CR1_CEN);
} else {
- regmap_read(priv->regmap, TIM_CR1, &dat);
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN,
0);
- if (dat & TIM_CR1_CEN)
+ if (priv->clk_enabled) {
+ priv->clk_enabled = false;
clk_disable(priv->clk);
+ }
}
+ priv->counter_en = !!val;
+ mutex_unlock(&priv->lock);
return 0;
}
@@ -556,7 +589,6 @@ static int stm32_set_enable_mode(struct iio_dev *indio_dev,
{
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
int sms = stm32_enable_mode2sms(mode);
- u32 val;
if (sms < 0)
return sms;
@@ -564,11 +596,12 @@ static int stm32_set_enable_mode(struct iio_dev *indio_dev,
* Triggered mode sets CEN bit automatically by hardware. So, first
* enable counter clock, so it can use it. Keeps it in sync with CEN.
*/
- if (sms == 6) {
- regmap_read(priv->regmap, TIM_CR1, &val);
- if (!(val & TIM_CR1_CEN))
- clk_enable(priv->clk);
+ mutex_lock(&priv->lock);
+ if (sms == 6 && !priv->clk_enabled) {
+ clk_enable(priv->clk);
+ priv->clk_enabled = true;
}
+ mutex_unlock(&priv->lock);
regmap_update_bits(priv->regmap, TIM_SMCR, TIM_SMCR_SMS, sms);
@@ -836,6 +869,7 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev)
priv->triggers = triggers_table[index];
priv->valids = cfg->valids_table[index];
stm32_timer_detect_trgo2(priv);
+ mutex_init(&priv->lock);
ret = stm32_setup_iio_triggers(priv);
if (ret)
@@ -846,6 +880,91 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev)
return 0;
}
+static int stm32_timer_trigger_remove(struct platform_device *pdev)
+{
+ struct stm32_timer_trigger *priv = platform_get_drvdata(pdev);
+ u32 val;
+
+ /* Check if nobody else use the timer, then disable it */
+ regmap_read(priv->regmap, TIM_CCER, &val);
+ if (!(val & TIM_CCER_CCXE))
+ regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0);
+
+ if (priv->clk_enabled)
+ clk_disable(priv->clk);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int stm32_tt_suspend(struct device *dev)
+{
+ struct stm32_timer_trigger *priv = dev_get_drvdata(dev);
+
+ /* Disable the timer */
+ if (priv->freq)
+ regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0);
+ /* Register contents may be lost depending on low power mode. */
+ regmap_read(priv->regmap, TIM_CR1, &priv->cr1);
+ regmap_read(priv->regmap, TIM_CR2, &priv->cr2);
+ regmap_read(priv->regmap, TIM_PSC, &priv->psc);
+ regmap_read(priv->regmap, TIM_ARR, &priv->arr);
+ regmap_read(priv->regmap, TIM_CNT, &priv->cnt);
+ regmap_read(priv->regmap, TIM_SMCR, &priv->smcr);
+
+ if (priv->clk_enabled)
+ clk_disable(priv->clk);
+
+ return pinctrl_pm_select_sleep_state(dev);
+}
+
+static int stm32_tt_resume(struct device *dev)
+{
+ struct stm32_timer_trigger *priv = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pinctrl_pm_select_default_state(dev);
+ if (ret)
+ return ret;
+
+ if (priv->clk_enabled) {
+ ret = clk_enable(priv->clk);
+ if (ret)
+ return ret;
+ }
+
+ /* restore master/slave modes */
+ regmap_write(priv->regmap, TIM_SMCR, priv->smcr);
+ regmap_update_bits(priv->regmap, TIM_CR2, TIM_CR2_MMS | TIM_CR2_MMS2,
+ priv->cr2);
+
+ if (priv->freq) {
+ /* restore sampling_frequency (trgo / trgo2 triggers) */
+ regmap_write(priv->regmap, TIM_PSC, priv->psc);
+ regmap_write(priv->regmap, TIM_ARR, priv->arr);
+ regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE,
+ TIM_CR1_ARPE);
+ regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG,
+ TIM_EGR_UG);
+ }
+
+ if (priv->counter_en) {
+ /* restore counter value, count_direction */
+ regmap_write(priv->regmap, TIM_CNT, priv->cnt);
+ regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_DIR,
+ priv->cr1);
+ }
+
+ if (priv->freq || priv->counter_en)
+ regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN,
+ TIM_CR1_CEN);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(stm32_tt_pm_ops, stm32_tt_suspend, stm32_tt_resume);
+
static const struct stm32_timer_trigger_cfg stm32_timer_trg_cfg = {
.valids_table = valids_table,
.num_valids_table = ARRAY_SIZE(valids_table),
@@ -870,9 +989,11 @@ MODULE_DEVICE_TABLE(of, stm32_trig_of_match);
static struct platform_driver stm32_timer_trigger_driver = {
.probe = stm32_timer_trigger_probe,
+ .remove = stm32_timer_trigger_remove,
.driver = {
.name = "stm32-timer-trigger",
.of_match_table = stm32_trig_of_match,
+ .pm = &stm32_tt_pm_ops,
},
};
module_platform_driver(stm32_timer_trigger_driver);
--
2.7.4