5145 lines
149 KiB
Diff
5145 lines
149 KiB
Diff
From 4134cd0a2ec7d17327dbdf141ec05268e195cd71 Mon Sep 17 00:00:00 2001
|
|
From: Romuald JEANNE <romuald.jeanne@st.com>
|
|
Date: Tue, 13 Nov 2018 12:19:04 +0100
|
|
Subject: [PATCH 05/52] ARM: stm32mp1-r0-rc1: IIO
|
|
|
|
---
|
|
drivers/iio/adc/Kconfig | 10 +
|
|
drivers/iio/adc/Makefile | 1 +
|
|
drivers/iio/adc/stm32-adc-core.c | 399 ++++++--
|
|
drivers/iio/adc/stm32-adc-core.h | 87 ++
|
|
drivers/iio/adc/stm32-adc-temp.c | 412 +++++++++
|
|
drivers/iio/adc/stm32-adc.c | 1431 ++++++++++++++++++++++++-----
|
|
drivers/iio/adc/stm32-dfsdm-adc.c | 627 ++++++++++---
|
|
drivers/iio/adc/stm32-dfsdm-core.c | 176 +++-
|
|
drivers/iio/counter/stm32-lptimer-cnt.c | 55 ++
|
|
drivers/iio/trigger/stm32-timer-trigger.c | 167 +++-
|
|
10 files changed, 2870 insertions(+), 495 deletions(-)
|
|
create mode 100644 drivers/iio/adc/stm32-adc-temp.c
|
|
|
|
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
|
|
index 4a75492..f2c1d8b 100644
|
|
--- a/drivers/iio/adc/Kconfig
|
|
+++ b/drivers/iio/adc/Kconfig
|
|
@@ -679,6 +679,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..a32e826 100644
|
|
--- a/drivers/iio/adc/stm32-adc-core.c
|
|
+++ b/drivers/iio/adc/stm32-adc-core.c
|
|
@@ -10,12 +10,16 @@
|
|
*/
|
|
|
|
#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/module.h>
|
|
#include <linux/of_device.h>
|
|
+#include <linux/of_irq.h>
|
|
+#include <linux/pm_runtime.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/slab.h>
|
|
|
|
@@ -23,12 +27,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 +57,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
|
|
@@ -51,15 +91,23 @@
|
|
/**
|
|
* 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
|
|
*/
|
|
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;
|
|
};
|
|
|
|
struct stm32_adc_priv;
|
|
@@ -69,11 +117,13 @@ 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)
|
|
+ * @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;
|
|
+ struct stm32_adc_trig_info *exti_trigs;
|
|
};
|
|
|
|
/**
|
|
@@ -82,18 +132,22 @@ 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
|
|
*/
|
|
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 *vref;
|
|
const struct stm32_adc_priv_cfg *cfg;
|
|
struct stm32_adc_common common;
|
|
+ u32 ccr_bak;
|
|
};
|
|
|
|
static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com)
|
|
@@ -129,7 +183,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 +272,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 +292,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,16 +319,23 @@ 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,
|
|
};
|
|
|
|
/* 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,
|
|
};
|
|
|
|
/* ADC common interrupt for all instances */
|
|
@@ -296,6 +357,15 @@ static void stm32_adc_irq_handler(struct irq_desc *desc)
|
|
if (status & priv->cfg->regs->eoc3_msk)
|
|
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 +414,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 +439,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 +450,186 @@ 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_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 = regulator_enable(priv->vref);
|
|
+ if (ret < 0) {
|
|
+ dev_err(dev, "vref enable failed\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ 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);
|
|
+
|
|
+ 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);
|
|
+}
|
|
+
|
|
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 +637,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 +647,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 +657,60 @@ 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;
|
|
- }
|
|
-
|
|
- 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->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;
|
|
- }
|
|
+ pm_runtime_get_noresume(dev);
|
|
+ pm_runtime_set_active(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 +718,18 @@ static int stm32_adc_probe(struct platform_device *pdev)
|
|
goto err_irq_remove;
|
|
}
|
|
|
|
+ pm_runtime_put(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 +739,58 @@ 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,
|
|
.max_clk_rate_hz = 40000000,
|
|
+ .exti_trigs = stm32h7_adc_exti_trigs,
|
|
};
|
|
|
|
static const struct of_device_id stm32_adc_of_match[] = {
|
|
@@ -552,6 +814,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..a209ea4 100644
|
|
--- a/drivers/iio/adc/stm32-adc-core.h
|
|
+++ b/drivers/iio/adc/stm32-adc-core.h
|
|
@@ -25,20 +25,107 @@
|
|
* --------------------------------------------------------
|
|
*/
|
|
#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 - 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 - 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..980355e 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>
|
|
@@ -22,6 +23,7 @@
|
|
#include <linux/iopoll.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
+#include <linux/pm_runtime.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_device.h>
|
|
|
|
@@ -46,14 +48,26 @@
|
|
#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_JEOCIE BIT(7)
|
|
+#define STM32F4_AWDIE BIT(6)
|
|
#define STM32F4_EOCIE BIT(5)
|
|
+#define STM32F4_AWDCH_SHIFT 0
|
|
+#define STM32F4_AWDCH_MASK GENMASK(4, 0)
|
|
|
|
/* STM32F4_ADC_CR2 - bit fields */
|
|
#define STM32F4_SWSTART BIT(30)
|
|
@@ -61,6 +75,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)
|
|
@@ -74,21 +93,44 @@
|
|
#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_AWD3IE STM32H7_AWD3
|
|
+#define STM32H7_AWD2IE STM32H7_AWD2
|
|
+#define STM32H7_AWD1IE STM32H7_AWD1
|
|
+#define STM32H7_JEOSIE STM32H7_JEOS
|
|
+#define STM32H7_OVRIE STM32H7_OVR
|
|
#define STM32H7_EOCIE STM32H7_EOC
|
|
|
|
/* STM32H7_ADC_CR - bit fields */
|
|
@@ -104,12 +146,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 +175,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 +191,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_AUTO_SUSPEND_DELAY_MS 2000
|
|
|
|
#define STM32_DMA_BUFFER_SIZE PAGE_SIZE
|
|
|
|
@@ -159,53 +213,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 +226,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 +304,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 +317,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 +364,15 @@ 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
|
|
*/
|
|
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 +387,10 @@ 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 stm32_adc_diff_channel {
|
|
@@ -342,9 +415,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 +463,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 +547,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 +611,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 +683,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 +804,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 +818,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 +854,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 +917,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 +999,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 +1024,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 +1051,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 +1081,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 +1097,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 +1123,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 +1139,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 +1160,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;
|
|
-
|
|
-disable:
|
|
- stm32h7_adc_disable(adc);
|
|
+ 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;
|
|
|
|
- return ret;
|
|
+ return 0;
|
|
}
|
|
|
|
/**
|
|
@@ -832,11 +1189,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 +1208,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 +1235,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 +1260,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 +1289,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 +1306,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 +1329,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 +1373,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_conf_scan_seq() - Build regular channels scan sequence
|
|
+ * 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_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 +1571,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 +1606,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 +1655,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 +1684,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 +1739,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 +1800,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 +1848,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 +1925,48 @@ static irqreturn_t stm32_adc_isr(int irq, void *data)
|
|
} else {
|
|
complete(&adc->completion);
|
|
}
|
|
- return IRQ_HANDLED;
|
|
+ ret = IRQ_HANDLED;
|
|
+ }
|
|
+
|
|
+ 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 IRQ_NONE;
|
|
+ return ret;
|
|
}
|
|
|
|
/**
|
|
@@ -1333,13 +2005,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 +2198,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 +2223,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,
|
|
};
|
|
@@ -1459,21 +2301,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 +2331,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,30 +2343,42 @@ 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);
|
|
@@ -1527,8 +2386,21 @@ static int stm32_adc_buffer_predisable(struct iio_dev *indio_dev)
|
|
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 +2485,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 +2498,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 +2507,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 +2529,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 +2554,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");
|
|
@@ -1828,6 +2710,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 +2721,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 +2740,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 +2759,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 +2780,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_AUTO_SUSPEND_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 +2818,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 +2827,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 +2840,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 +2904,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 +2924,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 +2946,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..c97d9ee 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>
|
|
@@ -38,6 +43,11 @@
|
|
#define DFSDM_MAX_RES BIT(31)
|
|
#define DFSDM_DATA_RES BIT(23)
|
|
|
|
+/* 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,
|
|
DFSDM_IIO,
|
|
@@ -54,6 +64,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,6 +126,61 @@ static int stm32_dfsdm_str2val(const char *str,
|
|
return -EINVAL;
|
|
}
|
|
|
|
+/**
|
|
+ * 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_set_osrs(struct stm32_dfsdm_filter *fl,
|
|
unsigned int fast, unsigned int oversamp)
|
|
{
|
|
@@ -200,19 +267,39 @@ static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl,
|
|
return 0;
|
|
}
|
|
|
|
-static int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm,
|
|
- unsigned int ch_id)
|
|
+static int stm32_dfsdm_start_channel(struct stm32_dfsdm_adc *adc)
|
|
{
|
|
- return regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
|
|
- DFSDM_CHCFGR1_CHEN_MASK,
|
|
- DFSDM_CHCFGR1_CHEN(1));
|
|
+ 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 *dfsdm,
|
|
- unsigned int ch_id)
|
|
+static void stm32_dfsdm_stop_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;
|
|
+
|
|
+ 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 +324,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 +337,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,11 +355,45 @@ 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_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];
|
|
+ u32 cr1;
|
|
+ const struct iio_chan_spec *chan;
|
|
+ unsigned int bit, jchg = 0;
|
|
int ret;
|
|
|
|
/* Average integrator oversampling */
|
|
@@ -286,15 +413,68 @@ 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));
|
|
+ /*
|
|
+ * 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 +558,37 @@ 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);
|
|
+ struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
|
|
+ unsigned int oversamp;
|
|
+ int ret;
|
|
+
|
|
+ oversamp = DIV_ROUND_CLOSEST(spi_freq, sample_freq);
|
|
+ if (spi_freq % sample_freq)
|
|
+ dev_warn(&indio_dev->dev, "Sampling rate not accurate (%d)\n",
|
|
+ spi_freq / oversamp);
|
|
+
|
|
+ ret = stm32_dfsdm_set_osrs(fl, 0, oversamp);
|
|
+ if (ret < 0) {
|
|
+ dev_err(&indio_dev->dev, "No filter parameters that match!\n");
|
|
+ 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 +607,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 +617,44 @@ 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_start_channel(adc);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
- ret = stm32_dfsdm_filter_configure(adc->dfsdm, adc->fl_id,
|
|
- chan->channel);
|
|
+ ret = stm32_dfsdm_filter_configure(adc, adc->fl_id, trig);
|
|
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_filter(adc, adc->fl_id, trig);
|
|
if (ret < 0)
|
|
- goto stop_channels;
|
|
-
|
|
- /* Enable conversion triggered by SPI clock*/
|
|
- ret = regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
|
|
- DFSDM_CR1_RCONT_MASK, cont_en);
|
|
- if (ret < 0)
|
|
- goto stop_channels;
|
|
-
|
|
- ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id);
|
|
- if (ret < 0)
|
|
- goto stop_channels;
|
|
+ goto filter_unconfigure;
|
|
|
|
return 0;
|
|
|
|
-stop_channels:
|
|
+filter_unconfigure:
|
|
regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
|
|
- DFSDM_CR1_RDMAEN_MASK, 0);
|
|
-
|
|
- regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
|
|
- DFSDM_CR1_RCONT_MASK, 0);
|
|
- stm32_dfsdm_stop_channel(adc->dfsdm, 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 +662,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 +671,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 +701,41 @@ 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 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) {
|
|
+ u32 *buffer = (u32 *)&adc->rx_buf[adc->bufi];
|
|
+
|
|
+ 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.
|
|
@@ -566,6 +763,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 +775,10 @@ 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);
|
|
+ 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 +789,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 +806,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 +1031,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 +1054,7 @@ 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);
|
|
|
|
stop_dfsdm:
|
|
stm32_dfsdm_stop_dfsdm(adc->dfsdm);
|
|
@@ -777,16 +1074,23 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
|
|
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
|
+ ret = iio_device_claim_direct_mode(indio_dev);
|
|
+ if (ret)
|
|
+ return ret;
|
|
ret = stm32_dfsdm_set_osrs(fl, 0, 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 +1103,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 +1120,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 +1137,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 +1157,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 +1235,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 +1244,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,7 +1272,8 @@ 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';
|
|
@@ -1000,9 +1295,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;
|
|
@@ -1070,6 +1362,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 +1428,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);
|
|
|
|
@@ -1203,10 +1514,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..2d2c640 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));
|
|
@@ -243,13 +263,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 +343,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/counter/stm32-lptimer-cnt.c b/drivers/iio/counter/stm32-lptimer-cnt.c
|
|
index 42fb8ba..2a49cce 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/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
|
|
|