meta-st-stm32mp/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0029-ARM-stm32mp1-r0-rc2-HW...

772 lines
23 KiB
Diff

From 72179a889501ef339df094abe108846b19e7a06c Mon Sep 17 00:00:00 2001
From: Christophe Priouzeau <christophe.priouzeau@st.com>
Date: Mon, 26 Nov 2018 14:46:26 +0100
Subject: [PATCH 29/52] ARM-stm32mp1-r0-rc2-HWSPINLOCK-IIO-I2C
---
.../devicetree/bindings/iio/adc/st,stm32-adc.txt | 34 +++
arch/arm/Kconfig.debug | 27 ++
drivers/hwspinlock/Kconfig | 9 +
drivers/hwspinlock/Makefile | 1 +
drivers/hwspinlock/stm32_hwspinlock.c | 157 +++++++++++
drivers/i2c/busses/i2c-stm32f7.c | 6 +-
drivers/iio/adc/stm32-adc-core.c | 296 ++++++++++++++++++++-
drivers/iio/adc/stm32-adc.c | 4 +-
8 files changed, 529 insertions(+), 5 deletions(-)
create mode 100644 drivers/hwspinlock/stm32_hwspinlock.c
diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
index c46598c..a6aa796 100644
--- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
@@ -49,6 +49,40 @@ Optional properties:
- st,max-clk-rate-hz: Allow to specify desired max clock rate used by analog
circuitry.
+- vdda-supply: Phandle to the vdda input voltage. It can be used to supply ADC
+ analog inputs switches on stm32mp1 and stm32h7.
+
+- vdd-supply: Phandle to the vdd input voltage. It can be used to supply ADC
+ analog inputs switches on stm32mp1.
+
+- st,syscfg-vbooster: Voltage booster control for analog switches supply.
+ This is available on stm32mp1 and stm32h7 (requires vdda-supply property).
+ It must be composed of 3 cells:
+ 1st cell: phandle to syscfg
+ 2nd cell: register offset within SYSCFG
+ 3rd cell: bitmask for BOOSTE on stm32h7, EN_BOOSTER set bit on stm32mp1
+
+- st,syscfg-vbooster-clr: Voltage booster clear for analog switches supply.
+ This is available on stm32mp1 (requires st,syscfg-vbooster and vdda-supply).
+ 1st cell: phandle to syscfg
+ 2nd cell: clear register offset within SYSCFG
+ 3rd cell: bitmask for EN_BOOSTER clear bit on stm32mp1
+
+- st,syscfg-anaswvdd: VDDA / VDD selection for analog switches supply.
+ This is available on stm32mp1 (requires vdda-supply and vdd-supply).
+ It must be composed of 3 cells:
+ 1st cell: phandle to syscfg
+ 2nd cell: register offset within SYSCFG
+ 3rd cell: bitmask for ANASWVDD set bit
+
+- st,syscfg-anaswvdd-clr: VDDA / VDD selection clear for analog switches supply.
+ This is available on stm32mp1 (requires st,syscfg-anaswvdd, vdda-supply and
+ vdd-supply).
+ It must be composed of 3 cells:
+ 1st cell: phandle to syscfg
+ 2nd cell: clear register offset within SYSCFG
+ 3rd cell: bitmask for ANASWVDD clear bit
+
Contents of a stm32 adc child node:
-----------------------------------
An ADC block node should contain at least one subnode, representing an
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index f6fcb8a..d5ec6c3 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -1184,6 +1184,28 @@ choice
If unsure, say N.
+ config STM32F4_DEBUG_UART
+ bool "Use STM32F4 UART for low-level debug"
+ depends on ARCH_STM32
+ select DEBUG_STM32_UART
+ help
+ Say Y here if you want kernel low-level debugging support
+ on STM32F4 based platforms, which default UART is wired on
+ USART1.
+
+ If unsure, say N.
+
+ config STM32F7_DEBUG_UART
+ bool "Use STM32F7 UART for low-level debug"
+ depends on ARCH_STM32
+ select DEBUG_STM32_UART
+ help
+ Say Y here if you want kernel low-level debugging support
+ on STM32F7 based platforms, which default UART is wired on
+ USART1.
+
+ If unsure, say N.
+
config TEGRA_DEBUG_UART_AUTO_ODMDATA
bool "Kernel low-level debugging messages via Tegra UART via ODMDATA"
depends on ARCH_TEGRA
@@ -1468,6 +1490,10 @@ config DEBUG_STI_UART
bool
depends on ARCH_STI
+config DEBUG_STM32_UART
+ bool
+ depends on ARCH_STM32
+
config DEBUG_SIRFSOC_UART
bool
depends on ARCH_SIRF
@@ -1517,6 +1543,7 @@ config DEBUG_LL_INCLUDE
default "debug/s5pv210.S" if DEBUG_S5PV210_UART
default "debug/sirf.S" if DEBUG_SIRFSOC_UART
default "debug/sti.S" if DEBUG_STI_UART
+ default "debug/stm32.S" if DEBUG_STM32_UART
default "debug/tegra.S" if DEBUG_TEGRA_UART
default "debug/ux500.S" if DEBUG_UX500_UART
default "debug/vexpress.S" if DEBUG_VEXPRESS_UART0_DETECT
diff --git a/drivers/hwspinlock/Kconfig b/drivers/hwspinlock/Kconfig
index e895d29..7869c67 100644
--- a/drivers/hwspinlock/Kconfig
+++ b/drivers/hwspinlock/Kconfig
@@ -49,6 +49,15 @@ config HWSPINLOCK_SPRD
If unsure, say N.
+config HWSPINLOCK_STM32
+ tristate "STM32 Hardware Spinlock device"
+ depends on MACH_STM32MP157
+ depends on HWSPINLOCK
+ help
+ Say y here to support the STM32 Hardware Spinlock device.
+
+ If unsure, say N.
+
config HSEM_U8500
tristate "STE Hardware Semaphore functionality"
depends on HWSPINLOCK
diff --git a/drivers/hwspinlock/Makefile b/drivers/hwspinlock/Makefile
index b87c01a..ed053e3 100644
--- a/drivers/hwspinlock/Makefile
+++ b/drivers/hwspinlock/Makefile
@@ -8,4 +8,5 @@ obj-$(CONFIG_HWSPINLOCK_OMAP) += omap_hwspinlock.o
obj-$(CONFIG_HWSPINLOCK_QCOM) += qcom_hwspinlock.o
obj-$(CONFIG_HWSPINLOCK_SIRF) += sirf_hwspinlock.o
obj-$(CONFIG_HWSPINLOCK_SPRD) += sprd_hwspinlock.o
+obj-$(CONFIG_HWSPINLOCK_STM32) += stm32_hwspinlock.o
obj-$(CONFIG_HSEM_U8500) += u8500_hsem.o
diff --git a/drivers/hwspinlock/stm32_hwspinlock.c b/drivers/hwspinlock/stm32_hwspinlock.c
new file mode 100644
index 0000000..3242b72
--- /dev/null
+++ b/drivers/hwspinlock/stm32_hwspinlock.c
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics SA 2018
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ */
+
+#include <linux/clk.h>
+#include <linux/hwspinlock.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "hwspinlock_internal.h"
+
+#define STM32_MUTEX_COREID BIT(8)
+#define STM32_MUTEX_LOCK_BIT BIT(31)
+#define STM32_MUTEX_NUM_LOCKS 32
+
+struct stm32_hwspinlock {
+ struct clk *clk;
+ struct hwspinlock_device bank;
+};
+
+static int stm32_hwspinlock_trylock(struct hwspinlock *lock)
+{
+ void __iomem *lock_addr = lock->priv;
+ u32 status;
+
+ writel(STM32_MUTEX_LOCK_BIT | STM32_MUTEX_COREID, lock_addr);
+ status = readl(lock_addr);
+
+ return status == (STM32_MUTEX_LOCK_BIT | STM32_MUTEX_COREID);
+}
+
+static void stm32_hwspinlock_unlock(struct hwspinlock *lock)
+{
+ void __iomem *lock_addr = lock->priv;
+
+ writel(STM32_MUTEX_COREID, lock_addr);
+}
+
+static const struct hwspinlock_ops stm32_hwspinlock_ops = {
+ .trylock = stm32_hwspinlock_trylock,
+ .unlock = stm32_hwspinlock_unlock,
+};
+
+static int stm32_hwspinlock_probe(struct platform_device *pdev)
+{
+ struct stm32_hwspinlock *hw;
+ void __iomem *io_base;
+ struct resource *res;
+ size_t array_size;
+ int i, ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ io_base = devm_ioremap_resource(&pdev->dev, res);
+ if (!io_base)
+ return -ENOMEM;
+
+ array_size = STM32_MUTEX_NUM_LOCKS * sizeof(struct hwspinlock);
+ hw = devm_kzalloc(&pdev->dev, sizeof(*hw) + array_size, GFP_KERNEL);
+ if (!hw)
+ return -ENOMEM;
+
+ hw->clk = devm_clk_get(&pdev->dev, "hsem");
+ if (IS_ERR(hw->clk))
+ return PTR_ERR(hw->clk);
+
+ for (i = 0; i < STM32_MUTEX_NUM_LOCKS; i++)
+ hw->bank.lock[i].priv = io_base + i * sizeof(u32);
+
+ platform_set_drvdata(pdev, hw);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = hwspin_lock_register(&hw->bank, &pdev->dev, &stm32_hwspinlock_ops,
+ 0, STM32_MUTEX_NUM_LOCKS);
+
+ if (ret)
+ pm_runtime_disable(&pdev->dev);
+
+ return ret;
+}
+
+static int stm32_hwspinlock_remove(struct platform_device *pdev)
+{
+ struct stm32_hwspinlock *hw = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = hwspin_lock_unregister(&hw->bank);
+ if (ret)
+ dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret);
+
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static int __maybe_unused stm32_hwspinlock_runtime_suspend(struct device *dev)
+{
+ struct stm32_hwspinlock *hw = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(hw->clk);
+
+ return 0;
+}
+
+static int __maybe_unused stm32_hwspinlock_runtime_resume(struct device *dev)
+{
+ struct stm32_hwspinlock *hw = dev_get_drvdata(dev);
+
+ clk_prepare_enable(hw->clk);
+
+ return 0;
+}
+
+static const struct dev_pm_ops stm32_hwspinlock_pm_ops = {
+ SET_RUNTIME_PM_OPS(stm32_hwspinlock_runtime_suspend,
+ stm32_hwspinlock_runtime_resume,
+ NULL)
+};
+
+static const struct of_device_id stm32_hwpinlock_ids[] = {
+ { .compatible = "st,stm32-hwspinlock", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32_hwpinlock_ids);
+
+static struct platform_driver stm32_hwspinlock_driver = {
+ .probe = stm32_hwspinlock_probe,
+ .remove = stm32_hwspinlock_remove,
+ .driver = {
+ .name = "stm32_hwspinlock",
+ .of_match_table = stm32_hwpinlock_ids,
+ .pm = &stm32_hwspinlock_pm_ops,
+ },
+};
+
+static int __init stm32_hwspinlock_init(void)
+{
+ return platform_driver_register(&stm32_hwspinlock_driver);
+}
+
+/* board init code might need to reserve hwspinlocks for predefined purposes */
+postcore_initcall(stm32_hwspinlock_init);
+
+static void __exit stm32_hwspinlock_exit(void)
+{
+ platform_driver_unregister(&stm32_hwspinlock_driver);
+}
+module_exit(stm32_hwspinlock_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Hardware spinlock driver for STM32 SoCs");
+MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c
index c1cbf93..ac30aea 100644
--- a/drivers/i2c/busses/i2c-stm32f7.c
+++ b/drivers/i2c/busses/i2c-stm32f7.c
@@ -457,7 +457,7 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev,
STM32F7_I2C_ANALOG_FILTER_DELAY_MAX : 0);
dnf_delay = setup->dnf * i2cclk;
- sdadel_min = setup->fall_time - i2c_specs[setup->speed].hddat_min -
+ sdadel_min = i2c_specs[setup->speed].hddat_min + setup->fall_time -
af_delay_min - (setup->dnf + 3) * i2cclk;
sdadel_max = i2c_specs[setup->speed].vddat_max - setup->rise_time -
@@ -501,8 +501,12 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev,
list_add_tail(&v->node,
&solutions);
+ break;
}
}
+
+ if (p_prev == p)
+ break;
}
}
diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
index a32e826..6234456 100644
--- a/drivers/iio/adc/stm32-adc-core.c
+++ b/drivers/iio/adc/stm32-adc-core.c
@@ -16,10 +16,12 @@
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdesc.h>
#include <linux/irqdomain.h>
+#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
@@ -88,6 +90,8 @@
#define STM32H7_CKMODE_SHIFT 16
#define STM32H7_CKMODE_MASK GENMASK(17, 16)
+#define STM32_ADC_CORE_SLEEP_DELAY_MS 2000
+
/**
* stm32_adc_common_regs - stm32 common registers, compatible dependent data
* @csr: common status register offset
@@ -117,16 +121,30 @@ struct stm32_adc_priv;
* @regs: common registers for all instances
* @clk_sel: clock selection routine
* @max_clk_rate_hz: maximum analog clock rate (Hz, from datasheet)
+ * @has_syscfg_clr: analog switch control use set and clear registers
* @exti_trigs EXTI triggers info
*/
struct stm32_adc_priv_cfg {
const struct stm32_adc_common_regs *regs;
int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *);
u32 max_clk_rate_hz;
+ int has_syscfg_clr;
struct stm32_adc_trig_info *exti_trigs;
};
/**
+ * stm32_adc_syscfg - stm32 ADC SYSCFG data
+ * @regmap: reference to syscon
+ * @reg: register offset within SYSCFG
+ * @mask: bitmask within SYSCFG register
+ */
+struct stm32_adc_syscfg {
+ struct regmap *regmap;
+ u32 reg;
+ u32 mask;
+};
+
+/**
* struct stm32_adc_priv - stm32 ADC core private data
* @irq: irq(s) for ADC block
* @domain: irq domain reference
@@ -137,6 +155,10 @@ struct stm32_adc_priv_cfg {
* @cfg: compatible configuration data
* @common: common data for all ADC instances
* @ccr_bak: backup'ed CCR in low power mode
+ * @vbooster: BOOSTE syscfg / EN_BOOSTER syscfg set
+ * @vbooster_clr: EN_BOOSTER syscfg clear
+ * @anaswvdd: ANASWVDD syscfg set
+ * @anaswvdd_clr: ANASWVDD syscfg clear
*/
struct stm32_adc_priv {
int irq[STM32_ADC_MAX_ADCS];
@@ -144,10 +166,16 @@ struct stm32_adc_priv {
struct clk *aclk;
struct clk *bclk;
u32 max_clk_rate;
+ struct regulator *vdd;
+ struct regulator *vdda;
struct regulator *vref;
const struct stm32_adc_priv_cfg *cfg;
struct stm32_adc_common common;
u32 ccr_bak;
+ struct stm32_adc_syscfg vbooster;
+ struct stm32_adc_syscfg vbooster_clr;
+ struct stm32_adc_syscfg anaswvdd;
+ struct stm32_adc_syscfg anaswvdd_clr;
};
static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com)
@@ -567,16 +595,187 @@ static int stm32_adc_triggers_probe(struct platform_device *pdev,
return 0;
}
+static int stm32_adc_switches_supply_en(struct device *dev)
+{
+ struct stm32_adc_common *common = dev_get_drvdata(dev);
+ struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
+ int ret, vdda, vdd = 0;
+ u32 anaswvdd, en_booster;
+
+ /*
+ * On STM32H7 and STM32MP1, the ADC inputs are multiplexed with analog
+ * switches (e.g. PCSEL) which have reduced performances when their
+ * supply is below 2.7V (vdda by default):
+ * - Voltage booster can be used, to get full ADC performances
+ * (increases power consumption).
+ * - Vdd can be used if above 2.7V (STM32MP1 only).
+ *
+ * Make all this optional, since this is a trade-off between analog
+ * performance and power consumption.
+ */
+ if (IS_ERR(priv->vdda) || IS_ERR(priv->vbooster.regmap)) {
+ dev_dbg(dev, "%s: nothing to do\n", __func__);
+ return 0;
+ }
+
+ ret = regulator_enable(priv->vdda);
+ if (ret < 0) {
+ dev_err(dev, "vdda enable failed %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_get_voltage(priv->vdda);
+ if (ret < 0) {
+ dev_err(dev, "vdda get voltage failed %d\n", ret);
+ goto vdda_dis;
+ }
+ vdda = ret;
+
+ if (!IS_ERR(priv->vdd) && !IS_ERR(priv->anaswvdd.regmap)) {
+ ret = regulator_enable(priv->vdd);
+ if (ret < 0) {
+ dev_err(dev, "vdd enable failed %d\n", ret);
+ goto vdda_dis;
+ }
+
+ ret = regulator_get_voltage(priv->vdd);
+ if (ret < 0) {
+ dev_err(dev, "vdd get voltage failed %d\n", ret);
+ goto vdd_dis;
+ }
+ vdd = ret;
+ }
+
+ /*
+ * Recommended settings for ANASWVDD and EN_BOOSTER:
+ * - vdda > 2.7V: ANASWVDD = 0, EN_BOOSTER = 0
+ * - vdda < 2.7V and vdd < 2.7V: ANASWVDD = 0, EN_BOOSTER = 1
+ * - vdda < 2.7V but vdd > 2.7V: ANASWVDD = 1, EN_BOOSTER = 0 (stm32mp1)
+ */
+ if (vdda > 2700000) {
+ /* analog switches supplied by vdda (default) */
+ anaswvdd = 0;
+ en_booster = 0;
+ } else {
+ if (vdd < 2700000) {
+ /* Voltage booster enabled */
+ anaswvdd = 0;
+ en_booster = priv->vbooster.mask;
+ } else {
+ /* analog switches supplied by vdd */
+ anaswvdd = priv->anaswvdd.mask;
+ en_booster = 0;
+ }
+ }
+
+ dev_dbg(dev, "vdda=%d, vdd=%d, setting: en_booster=%x, anaswvdd=%x\n",
+ vdda, vdd, en_booster, anaswvdd);
+
+ /* direct write en_booster value (or use clear register) */
+ if (en_booster || IS_ERR(priv->vbooster_clr.regmap))
+ ret = regmap_update_bits(priv->vbooster.regmap,
+ priv->vbooster.reg,
+ priv->vbooster.mask, en_booster);
+ else
+ ret = regmap_update_bits(priv->vbooster_clr.regmap,
+ priv->vbooster_clr.reg,
+ priv->vbooster_clr.mask,
+ priv->vbooster_clr.mask);
+ if (ret) {
+ dev_err(dev, "can't access voltage booster, %d\n", ret);
+ goto vdd_dis;
+ }
+
+ /* Booster voltage can take up to 50 μs to stabilize */
+ if (en_booster)
+ usleep_range(50, 100);
+
+ if (!IS_ERR(priv->anaswvdd.regmap)) {
+ /* direct write anaswvdd value (or use clear register) */
+ if (anaswvdd || IS_ERR(priv->anaswvdd_clr.regmap))
+ ret = regmap_update_bits(priv->anaswvdd.regmap,
+ priv->anaswvdd.reg,
+ priv->anaswvdd.mask, anaswvdd);
+ else
+ ret = regmap_update_bits(priv->anaswvdd_clr.regmap,
+ priv->anaswvdd_clr.reg,
+ priv->anaswvdd_clr.mask,
+ priv->anaswvdd_clr.mask);
+ if (ret) {
+ dev_err(dev, "can't access anaswvdd, %d\n", ret);
+ goto booster_dis;
+ }
+ }
+
+ return ret;
+
+booster_dis:
+ if (IS_ERR(priv->vbooster_clr.regmap))
+ regmap_update_bits(priv->vbooster.regmap, priv->vbooster.reg,
+ priv->vbooster.mask, 0);
+ else
+ regmap_update_bits(priv->vbooster_clr.regmap,
+ priv->vbooster_clr.reg,
+ priv->vbooster_clr.mask,
+ priv->vbooster_clr.mask);
+vdd_dis:
+ if (!IS_ERR(priv->vdd) && !IS_ERR(priv->anaswvdd.regmap))
+ regulator_disable(priv->vdd);
+vdda_dis:
+ regulator_disable(priv->vdda);
+
+ return ret;
+}
+
+static void stm32_adc_switches_supply_dis(struct device *dev)
+{
+ struct stm32_adc_common *common = dev_get_drvdata(dev);
+ struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
+
+ if (IS_ERR(priv->vdda) || IS_ERR(priv->vbooster.regmap))
+ return;
+
+ if (!IS_ERR(priv->anaswvdd.regmap)) {
+ if (IS_ERR(priv->anaswvdd_clr.regmap))
+ regmap_update_bits(priv->anaswvdd.regmap,
+ priv->anaswvdd.reg,
+ priv->anaswvdd.mask, 0);
+ else
+ regmap_update_bits(priv->anaswvdd_clr.regmap,
+ priv->anaswvdd_clr.reg,
+ priv->anaswvdd_clr.mask,
+ priv->anaswvdd_clr.mask);
+ }
+
+ if (IS_ERR(priv->vbooster_clr.regmap))
+ regmap_update_bits(priv->vbooster.regmap, priv->vbooster.reg,
+ priv->vbooster.mask, 0);
+ else
+ regmap_update_bits(priv->vbooster_clr.regmap,
+ priv->vbooster_clr.reg,
+ priv->vbooster_clr.mask,
+ priv->vbooster_clr.mask);
+
+ if (!IS_ERR(priv->vdd) && !IS_ERR(priv->anaswvdd.regmap))
+ regulator_disable(priv->vdd);
+
+ regulator_disable(priv->vdda);
+}
+
static int stm32_adc_core_hw_start(struct device *dev)
{
struct stm32_adc_common *common = dev_get_drvdata(dev);
struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
int ret;
+ ret = stm32_adc_switches_supply_en(dev);
+ if (ret < 0)
+ return ret;
+
ret = regulator_enable(priv->vref);
if (ret < 0) {
dev_err(dev, "vref enable failed\n");
- return ret;
+ goto err_switches_disable;
}
if (priv->bclk) {
@@ -604,6 +803,8 @@ static int stm32_adc_core_hw_start(struct device *dev)
clk_disable_unprepare(priv->bclk);
err_regulator_disable:
regulator_disable(priv->vref);
+err_switches_disable:
+ stm32_adc_switches_supply_dis(dev);
return ret;
}
@@ -620,6 +821,68 @@ static void stm32_adc_core_hw_stop(struct device *dev)
if (priv->bclk)
clk_disable_unprepare(priv->bclk);
regulator_disable(priv->vref);
+ stm32_adc_switches_supply_dis(dev);
+}
+
+static int stm32_adc_get_syscfg_cell(struct device_node *np,
+ struct stm32_adc_syscfg *syscfg,
+ const char * prop)
+{
+ int ret;
+
+ syscfg->regmap = syscon_regmap_lookup_by_phandle(np, prop);
+ if (IS_ERR(syscfg->regmap)) {
+ pr_debug("FGA: %s %ld\n", prop, PTR_ERR(syscfg->regmap));
+ /* Optional */
+ if (PTR_ERR(syscfg->regmap) == -ENODEV)
+ return 0;
+ else
+ return PTR_ERR(syscfg->regmap);
+ }
+
+ ret = of_property_read_u32_index(np, prop, 1, &syscfg->reg);
+ if (ret)
+ return ret;
+
+ return of_property_read_u32_index(np, prop, 2, &syscfg->mask);
+}
+
+static int stm32_adc_syscfg_probe(struct platform_device *pdev,
+ struct stm32_adc_priv *priv)
+{
+ struct device_node *np = pdev->dev.of_node;
+ int ret;
+
+ /* Start to lookup BOOSTE/EN_BOOSTER first, for stm32h7/stm32mp1 */
+ ret = stm32_adc_get_syscfg_cell(np, &priv->vbooster,
+ "st,syscfg-vbooster");
+ if (ret)
+ return ret;
+
+ /* Continue with stm32mp1 EN_BOOSTER/ANASWVDD set and clear bits*/
+ ret = stm32_adc_get_syscfg_cell(np, &priv->vbooster_clr,
+ "st,syscfg-vbooster-clr");
+ if (ret)
+ return ret;
+
+ ret = stm32_adc_get_syscfg_cell(np, &priv->anaswvdd,
+ "st,syscfg-anaswvdd");
+ if (ret)
+ return ret;
+
+ ret = stm32_adc_get_syscfg_cell(np, &priv->anaswvdd_clr,
+ "st,syscfg-anaswvdd-clr");
+ if (ret)
+ return ret;
+
+ /* Sanity, check syscfg set/clear pairs are filled in */
+ if (priv->cfg->has_syscfg_clr && ((!IS_ERR(priv->vbooster.regmap) &&
+ IS_ERR(priv->vbooster_clr.regmap)) ||
+ (!IS_ERR(priv->anaswvdd.regmap) &&
+ IS_ERR(priv->anaswvdd_clr.regmap))))
+ return -EINVAL;
+
+ return ret;
}
static int stm32_adc_probe(struct platform_device *pdev)
@@ -657,6 +920,24 @@ static int stm32_adc_probe(struct platform_device *pdev)
return ret;
}
+ priv->vdda = devm_regulator_get_optional(&pdev->dev, "vdda");
+ if (IS_ERR(priv->vdda)) {
+ ret = PTR_ERR(priv->vdda);
+ if (ret != -ENODEV) {
+ dev_err(&pdev->dev, "vdda get failed, %d\n", ret);
+ return ret;
+ }
+ }
+
+ priv->vdd = devm_regulator_get_optional(&pdev->dev, "vdd");
+ if (IS_ERR(priv->vdd)) {
+ ret = PTR_ERR(priv->vdd);
+ if (ret != -ENODEV) {
+ dev_err(&pdev->dev, "vdd get failed, %d\n", ret);
+ return ret;
+ }
+ }
+
priv->aclk = devm_clk_get(&pdev->dev, "adc");
if (IS_ERR(priv->aclk)) {
ret = PTR_ERR(priv->aclk);
@@ -677,8 +958,17 @@ static int stm32_adc_probe(struct platform_device *pdev)
priv->bclk = NULL;
}
+ ret = stm32_adc_syscfg_probe(pdev, priv);
+ if (ret) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Can't probe syscfg: %d\n", ret);
+ return ret;
+ }
+
pm_runtime_get_noresume(dev);
pm_runtime_set_active(dev);
+ pm_runtime_set_autosuspend_delay(dev, STM32_ADC_CORE_SLEEP_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
pm_runtime_enable(dev);
ret = stm32_adc_core_hw_start(dev);
@@ -718,7 +1008,8 @@ static int stm32_adc_probe(struct platform_device *pdev)
goto err_irq_remove;
}
- pm_runtime_put(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
return 0;
@@ -789,6 +1080,7 @@ static const struct stm32_adc_priv_cfg stm32h7_adc_priv_cfg = {
static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = {
.regs = &stm32h7_adc_common_regs,
.clk_sel = stm32h7_adc_clk_sel,
+ .has_syscfg_clr = true,
.max_clk_rate_hz = 40000000,
.exti_trigs = stm32h7_adc_exti_trigs,
};
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 980355e..13d2e3c 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -201,7 +201,7 @@ enum stm32h7_adc_dmngt {
#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_ADC_HW_STOP_DELAY_MS 100
#define STM32_DMA_BUFFER_SIZE PAGE_SIZE
@@ -2783,7 +2783,7 @@ static int stm32_adc_probe(struct platform_device *pdev)
/* 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_set_autosuspend_delay(dev, STM32_ADC_HW_STOP_DELAY_MS);
pm_runtime_use_autosuspend(dev);
pm_runtime_enable(dev);
--
2.7.4