772 lines
23 KiB
Diff
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
|
|
|