diff --git a/recipes-kernel/linux/linux-stm32mp.inc b/recipes-kernel/linux/linux-stm32mp.inc new file mode 100644 index 0000000..195d76f --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp.inc @@ -0,0 +1,81 @@ +COMPATIBLE_MACHINE = "(stm32mpcommon)" + +inherit kernel + +DEPENDS += "openssl-native util-linux-native" + +B = "${WORKDIR}/linux-${MACHINE}-standard-build" +# Configure build dir for externalsrc class usage through devtool +EXTERNALSRC_BUILD_pn-${PN} = "${WORKDIR}/linux-${MACHINE}-standard-build" + +# To share config fragments between layers +FILESEXTRAPATHS_prepend := "${THISDIR}:" + +# ------------------------------------------------------------- +# Do not deploy kernel module with specfic tarball +MODULE_TARBALL_DEPLOY = "0" + +# --------------------------------------------------------------------- +# Defconfig +# +#If the defconfig is contained on the kernel tree (arch/${ARCH}/configs) +#you must use the following line +do_configure_prepend() { + unset CFLAGS CPPFLAGS CXXFLAGS LDFLAGS MACHINE + if [ ! -z ${KERNEL_DEFCONFIG} ]; + then + bbnote "Kernel customized: configuration of linux STM by using DEFCONFIG: ${KERNEL_DEFCONFIG}" + oe_runmake ${PARALLEL_MAKE} -C ${S} O=${B} CC="${KERNEL_CC}" LD="${KERNEL_LD}" ${KERNEL_DEFCONFIG} + else + if [ ! -z ${KERNEL_EXTERNAL_DEFCONFIG} ]; + then + bbnote "Kernel customized: configuration of linux STM by using external DEFCONFIG" + install -m 0644 ${WORKDIR}/${KERNEL_EXTERNAL_DEFCONFIG} ${B}/.config + oe_runmake -C ${S} O=${B} CC="${KERNEL_CC}" LD="${KERNEL_LD}" oldconfig + else + bbwarn "You must specify KERNEL_DEFCONFIG or KERNEL_EXTERNAL_DEFCONFIG" + die "NO DEFCONFIG SPECIFIED" + fi + fi + + if [ ! -z "${KERNEL_CONFIG_FRAGMENTS}" ] + then + for f in ${KERNEL_CONFIG_FRAGMENTS} + do + bbnote "file = $f" + # Check if the config fragment was copied into the WORKDIR from + # the OE meta data + if [ ! -e "$f" ] + then + echo "Could not find kernel config fragment $f" + exit 1 + fi + done + + bbnote "${S}/scripts/kconfig/merge_config.sh -m -r -O ${B} ${B}/.config ${KERNEL_CONFIG_FRAGMENTS} 1>&2" + # Now that all the fragments are located merge them. + (${S}/scripts/kconfig/merge_config.sh -m -r -O ${B} ${B}/.config ${KERNEL_CONFIG_FRAGMENTS} 1>&2 ) + fi + + yes '' | oe_runmake -C ${S} O=${B} CC="${KERNEL_CC}" LD="${KERNEL_LD}" oldconfig + #oe_runmake -C ${S} O=${B} savedefconfig && cp ${B}/defconfig ${WORKDIR}/defconfig.saved +} + +# --------------------------------------------------------------------- +do_install_append() { + # Install KERNEL_IMAGETYPE for kernel-imagebootfs package + install -m 0644 ${KERNEL_OUTPUT_DIR}/${KERNEL_IMAGETYPE} ${D}/${KERNEL_IMAGEDEST} +} + +# --------------------------------------------------------------------- +# Support checking the kernel load address parameter: expecting proper value for ST kernel. +# +python do_loadaddrcheck() { + if not d.getVar('ST_KERNEL_LOADADDR'): + bb.fatal('Missing ST_KERNEL_LOADADDR value for ST kernel build: please define it in your machine.') +} + +PACKAGES =+ "${KERNEL_PACKAGE_NAME}-headers ${KERNEL_PACKAGE_NAME}-imagebootfs" +FILES_${KERNEL_PACKAGE_NAME}-headers = "${exec_prefix}/src/linux*" +FILES_${KERNEL_PACKAGE_NAME}-image += "boot/ ${KERNEL_IMAGEDEST}" +FILES_${KERNEL_PACKAGE_NAME}-imagebootfs = "boot/*.dtb boot/${KERNEL_IMAGETYPE}" diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0001-ARM-stm32mp1-r0-rc1-MACHINE.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0001-ARM-stm32mp1-r0-rc1-MACHINE.patch new file mode 100644 index 0000000..933bbf8 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0001-ARM-stm32mp1-r0-rc1-MACHINE.patch @@ -0,0 +1,47 @@ +From 07a8a17983431bcc47ef52be1370f156e56119ce Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Tue, 13 Nov 2018 12:14:29 +0100 +Subject: [PATCH 01/52] ARM: stm32mp1-r0-rc1: MACHINE + +--- + arch/arm/mach-stm32/Kconfig | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git a/arch/arm/mach-stm32/Kconfig b/arch/arm/mach-stm32/Kconfig +index 713c068..651bdf4 100644 +--- a/arch/arm/mach-stm32/Kconfig ++++ b/arch/arm/mach-stm32/Kconfig +@@ -4,6 +4,7 @@ menuconfig ARCH_STM32 + select HAVE_ARM_ARCH_TIMER if ARCH_MULTI_V7 + select ARM_GIC if ARCH_MULTI_V7 + select ARM_PSCI if ARCH_MULTI_V7 ++ select ARM_AMBA + select ARCH_HAS_RESET_CONTROLLER + select CLKSRC_STM32 + select PINCTRL +@@ -18,22 +19,18 @@ if ARM_SINGLE_ARMV7M + + config MACH_STM32F429 + bool "STMicroelectronics STM32F429" +- select ARM_AMBA + default y + + config MACH_STM32F469 + bool "STMicroelectronics STM32F469" +- select ARM_AMBA + default y + + config MACH_STM32F746 + bool "STMicroelectronics STM32F746" +- select ARM_AMBA + default y + + config MACH_STM32F769 + bool "STMicroelectronics STM32F769" +- select ARM_AMBA + default y + + config MACH_STM32H743 +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0002-ARM-stm32mp1-r0-rc1-CLOCK.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0002-ARM-stm32mp1-r0-rc1-CLOCK.patch new file mode 100644 index 0000000..43b9b90 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0002-ARM-stm32mp1-r0-rc1-CLOCK.patch @@ -0,0 +1,1471 @@ +From 2eae46b85e51f0b995ddfdd6176501d4a6cf1520 Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Tue, 13 Nov 2018 12:15:45 +0100 +Subject: [PATCH 02/52] ARM: stm32mp1-r0-rc1: CLOCK + +--- + drivers/clk/clk-stm32mp1.c | 1022 ++++++++++++++++++++++++++--- + drivers/clk/clk.c | 24 +- + include/dt-bindings/clock/stm32mp1-clks.h | 3 - + include/linux/clk.h | 1 + + 4 files changed, 968 insertions(+), 82 deletions(-) + +diff --git a/drivers/clk/clk-stm32mp1.c b/drivers/clk/clk-stm32mp1.c +index a907555..50d739a 100644 +--- a/drivers/clk/clk-stm32mp1.c ++++ b/drivers/clk/clk-stm32mp1.c +@@ -5,15 +5,22 @@ + * Author: Gabriel Fernandez for STMicroelectronics. + */ + ++#include + #include + #include + #include + #include ++#include + #include ++#include ++#include + #include + #include ++#include ++#include + #include + #include ++#include + + #include + +@@ -101,6 +108,10 @@ static DEFINE_SPINLOCK(rlock); + #define RCC_TIMG2PRER 0x82C + #define RCC_RTCDIVR 0x44 + #define RCC_DBGCFGR 0x80C ++#define RCC_SREQSETR 0x104 ++#define RCC_SREQCLRR 0x108 ++#define RCC_CIER 0x414 ++#define RCC_CIFR 0x418 + + #define RCC_CLR 0x4 + +@@ -121,7 +132,7 @@ static const char * const cpu_src[] = { + }; + + static const char * const axi_src[] = { +- "ck_hsi", "ck_hse", "pll2_p", "pll3_p" ++ "ck_hsi", "ck_hse", "pll2_p" + }; + + static const char * const per_src[] = { +@@ -225,19 +236,19 @@ static const char * const usart6_src[] = { + }; + + static const char * const fdcan_src[] = { +- "ck_hse", "pll3_q", "pll4_q" ++ "ck_hse", "pll3_q", "pll4_q", "pll4_r" + }; + + static const char * const sai_src[] = { +- "pll4_q", "pll3_q", "i2s_ckin", "ck_per" ++ "pll4_q", "pll3_q", "i2s_ckin", "ck_per", "pll3_r" + }; + + static const char * const sai2_src[] = { +- "pll4_q", "pll3_q", "i2s_ckin", "ck_per", "spdif_ck_symb" ++ "pll4_q", "pll3_q", "i2s_ckin", "ck_per", "spdif_ck_symb", "pll3_r" + }; + + static const char * const adc12_src[] = { +- "pll4_q", "ck_per" ++ "pll4_r", "ck_per", "pll3_q" + }; + + static const char * const dsi_src[] = { +@@ -269,7 +280,7 @@ static const struct clk_div_table axi_div_table[] = { + static const struct clk_div_table mcu_div_table[] = { + { 0, 1 }, { 1, 2 }, { 2, 4 }, { 3, 8 }, + { 4, 16 }, { 5, 32 }, { 6, 64 }, { 7, 128 }, +- { 8, 512 }, { 9, 512 }, { 10, 512}, { 11, 512 }, ++ { 8, 256 }, { 9, 512 }, { 10, 512}, { 11, 512 }, + { 12, 512 }, { 13, 512 }, { 14, 512}, { 15, 512 }, + { 0 }, + }; +@@ -356,17 +367,20 @@ struct stm32_gate_cfg { + struct gate_cfg *gate; + struct stm32_mgate *mgate; + const struct clk_ops *ops; ++ const struct clk_ops *ops_sec; + }; + + struct stm32_div_cfg { + struct div_cfg *div; + const struct clk_ops *ops; ++ const struct clk_ops *ops_sec; + }; + + struct stm32_mux_cfg { + struct mux_cfg *mux; + struct stm32_mmux *mmux; + const struct clk_ops *ops; ++ const struct clk_ops *ops_sec; + }; + + /* STM32 Composite clock */ +@@ -376,6 +390,11 @@ struct stm32_composite_cfg { + const struct stm32_mux_cfg *mux; + }; + ++static inline int _is_soc_secured(void __iomem *base) ++{ ++ return readl_relaxed(base) & 0x1; ++} ++ + static struct clk_hw * + _clk_hw_register_gate(struct device *dev, + struct clk_hw_onecell_data *clk_data, +@@ -592,6 +611,9 @@ clk_stm32_register_gate_ops(struct device *dev, + if (cfg->ops) + init.ops = cfg->ops; + ++ if (cfg->ops_sec && _is_soc_secured(base)) ++ init.ops = cfg->ops_sec; ++ + hw = _get_stm32_gate(base, cfg, lock); + if (IS_ERR(hw)) + return ERR_PTR(-ENOMEM); +@@ -630,6 +652,9 @@ clk_stm32_register_composite(struct device *dev, + + if (cfg->mux->ops) + mux_ops = cfg->mux->ops; ++ ++ if (cfg->mux->ops_sec && _is_soc_secured(base)) ++ mux_ops = cfg->mux->ops_sec; + } + } + +@@ -641,6 +666,9 @@ clk_stm32_register_composite(struct device *dev, + + if (cfg->div->ops) + div_ops = cfg->div->ops; ++ ++ if (cfg->div->ops_sec && _is_soc_secured(base)) ++ div_ops = cfg->div->ops_sec; + } + } + +@@ -652,6 +680,9 @@ clk_stm32_register_composite(struct device *dev, + + if (cfg->gate->ops) + gate_ops = cfg->gate->ops; ++ ++ if (cfg->gate->ops_sec && _is_soc_secured(base)) ++ gate_ops = cfg->gate->ops_sec; + } + } + +@@ -1193,7 +1224,8 @@ _clk_stm32_register_composite(struct device *dev, + .func = _clk_stm32_register_gate,\ + } + +-#define _STM32_GATE(_gate_offset, _gate_bit_idx, _gate_flags, _mgate, _ops)\ ++#define _STM32_GATE(_gate_offset, _gate_bit_idx, _gate_flags, _mgate, _ops,\ ++ _ops_sec)\ + (&(struct stm32_gate_cfg) {\ + &(struct gate_cfg) {\ + .reg_off = _gate_offset,\ +@@ -1202,6 +1234,7 @@ _clk_stm32_register_composite(struct device *dev, + },\ + .mgate = _mgate,\ + .ops = _ops,\ ++ .ops_sec = _ops_sec,\ + }) + + #define _STM32_MGATE(_mgate)\ +@@ -1209,11 +1242,11 @@ _clk_stm32_register_composite(struct device *dev, + + #define _GATE(_gate_offset, _gate_bit_idx, _gate_flags)\ + _STM32_GATE(_gate_offset, _gate_bit_idx, _gate_flags,\ +- NULL, NULL)\ ++ NULL, NULL, NULL)\ + + #define _GATE_MP1(_gate_offset, _gate_bit_idx, _gate_flags)\ + _STM32_GATE(_gate_offset, _gate_bit_idx, _gate_flags,\ +- NULL, &mp1_gate_clk_ops)\ ++ NULL, &mp1_gate_clk_ops, NULL)\ + + #define _MGATE_MP1(_mgate)\ + .gate = &per_gate_cfg[_mgate] +@@ -1227,7 +1260,7 @@ _clk_stm32_register_composite(struct device *dev, + _STM32_MGATE(_mgate)) + + #define _STM32_DIV(_div_offset, _div_shift, _div_width,\ +- _div_flags, _div_table, _ops)\ ++ _div_flags, _div_table, _ops, _ops_sec)\ + .div = &(struct stm32_div_cfg) {\ + &(struct div_cfg) {\ + .reg_off = _div_offset,\ +@@ -1237,13 +1270,14 @@ _clk_stm32_register_composite(struct device *dev, + .table = _div_table,\ + },\ + .ops = _ops,\ ++ .ops_sec = _ops_sec,\ + } + + #define _DIV(_div_offset, _div_shift, _div_width, _div_flags, _div_table)\ + _STM32_DIV(_div_offset, _div_shift, _div_width,\ +- _div_flags, _div_table, NULL)\ ++ _div_flags, _div_table, NULL, NULL)\ + +-#define _STM32_MUX(_offset, _shift, _width, _mux_flags, _mmux, _ops)\ ++#define _STM32_MUX(_offset, _shift, _width, _mux_flags, _mmux, _ops, _ops_sec)\ + .mux = &(struct stm32_mux_cfg) {\ + &(struct mux_cfg) {\ + .reg_off = _offset,\ +@@ -1254,10 +1288,11 @@ _clk_stm32_register_composite(struct device *dev, + },\ + .mmux = _mmux,\ + .ops = _ops,\ ++ .ops_sec = _ops_sec,\ + } + + #define _MUX(_offset, _shift, _width, _mux_flags)\ +- _STM32_MUX(_offset, _shift, _width, _mux_flags, NULL, NULL)\ ++ _STM32_MUX(_offset, _shift, _width, _mux_flags, NULL, NULL, NULL) + + #define _MMUX(_mmux) .mux = &ker_mux_cfg[_mmux] + +@@ -1286,10 +1321,513 @@ _clk_stm32_register_composite(struct device *dev, + MGATE_MP1(_id, _name, _parent, _flags, _mgate) + + #define KCLK(_id, _name, _parents, _flags, _mgate, _mmux)\ +- COMPOSITE(_id, _name, _parents, CLK_OPS_PARENT_ENABLE | _flags,\ +- _MGATE_MP1(_mgate),\ +- _MMUX(_mmux),\ +- _NO_DIV) ++ COMPOSITE(_id, _name, _parents, CLK_OPS_PARENT_ENABLE |\ ++ CLK_SET_RATE_NO_REPARENT | _flags,\ ++ _MGATE_MP1(_mgate),\ ++ _MMUX(_mmux),\ ++ _NO_DIV) ++/* ++ * ++ * Security management ++ * ++ */ ++ ++#define STM32_SVC_RCC 0x82001000 ++#define STM32_WRITE 0x1 ++#define STM32_SET_BITS 0x2 ++#define STM32_CLR_BITS 0x3 ++ ++#define SMC(class, op, address, val)\ ++ ({\ ++ struct arm_smccc_res res;\ ++ arm_smccc_smc(class, op, address, val,\ ++ 0, 0, 0, 0, &res);\ ++ }) ++ ++static u32 stm32_clk_writel_secure(u32 value, void __iomem *reg) ++{ ++ struct arm_smccc_res res; ++ u32 address; ++ ++ address = offset_in_page(reg); ++ ++ arm_smccc_smc(STM32_SVC_RCC, STM32_WRITE, address, value, 0, 0, 0, ++ 0, &res); ++ ++ if (res.a0) ++ pr_warn("%s: Failed to write in secure mode at 0x%x (err = %ld)\n" ++ , __func__ ++ , address ++ , res.a0); ++ ++ return res.a0; ++} ++ ++static u32 stm32_clk_bit_secure(u32 cmd, u32 value, void __iomem *reg) ++{ ++ struct arm_smccc_res res; ++ u32 address; ++ ++ address = offset_in_page(reg); ++ ++ arm_smccc_smc(STM32_SVC_RCC, cmd, address, value, 0, 0, 0, ++ 0, &res); ++ ++ if (res.a0) ++ pr_warn("%s: Failed to write in secure mode at 0x%x (err = %ld)\n" ++ , __func__ ++ , address ++ , res.a0); ++ ++ return res.a0; ++} ++ ++static void clk_sgate_endisable(struct clk_hw *hw, int enable) ++{ ++ struct clk_gate *gate = to_clk_gate(hw); ++ unsigned long flags = 0; ++ u32 cmd; ++ ++ spin_lock_irqsave(gate->lock, flags); ++ ++ if (enable) ++ cmd = STM32_SET_BITS; ++ else ++ cmd = STM32_CLR_BITS; ++ ++ stm32_clk_bit_secure(cmd, BIT(gate->bit_idx), gate->reg); ++ ++ spin_unlock_irqrestore(gate->lock, flags); ++} ++ ++static int clk_sgate_enable(struct clk_hw *hw) ++{ ++ clk_sgate_endisable(hw, 1); ++ ++ return 0; ++} ++ ++static void clk_sgate_disable(struct clk_hw *hw) ++{ ++ clk_sgate_endisable(hw, 0); ++} ++ ++static const struct clk_ops clk_sgate_ops = { ++ .enable = clk_sgate_enable, ++ .disable = clk_sgate_disable, ++ .is_enabled = clk_gate_is_enabled, ++}; ++ ++static u8 clk_smux_get_parent(struct clk_hw *hw) ++{ ++ return clk_mux_ops.get_parent(hw); ++} ++ ++static int clk_smux_set_parent(struct clk_hw *hw, u8 index) ++{ ++ struct clk_mux *mux = to_clk_mux(hw); ++ u32 val; ++ unsigned long flags = 0; ++ ++ if (mux->table) { ++ index = mux->table[index]; ++ } else { ++ if (mux->flags & CLK_MUX_INDEX_BIT) ++ index = 1 << index; ++ ++ if (mux->flags & CLK_MUX_INDEX_ONE) ++ index++; ++ } ++ ++ spin_lock_irqsave(mux->lock, flags); ++ ++ val = clk_readl(mux->reg); ++ val &= ~(mux->mask << mux->shift); ++ val |= index << mux->shift; ++ ++ stm32_clk_writel_secure(val, mux->reg); ++ ++ spin_unlock_irqrestore(mux->lock, flags); ++ ++ return 0; ++} ++ ++static const struct clk_ops clk_smux_ops = { ++ .get_parent = clk_smux_get_parent, ++ .set_parent = clk_smux_set_parent, ++ .determine_rate = __clk_mux_determine_rate, ++}; ++ ++static struct clk_hw *clk_hw_register_smux(struct device *dev, ++ const char *name, ++ const char * const *parent_names, ++ u8 num_parents, ++ unsigned long flags, ++ void __iomem *reg, u8 shift, ++ u8 width, ++ u8 clk_mux_flags, ++ spinlock_t *lock) ++{ ++ u32 mask = BIT(width) - 1; ++ struct clk_mux *mux; ++ struct clk_hw *hw; ++ struct clk_init_data init; ++ int ret; ++ ++ /* allocate the mux */ ++ mux = kzalloc(sizeof(*mux), GFP_KERNEL); ++ if (!mux) ++ return ERR_PTR(-ENOMEM); ++ ++ init.name = name; ++ ++ init.ops = &clk_smux_ops; ++ ++ init.flags = flags | CLK_IS_BASIC; ++ init.parent_names = parent_names; ++ init.num_parents = num_parents; ++ ++ /* struct clk_mux assignments */ ++ mux->reg = reg; ++ mux->shift = shift; ++ mux->mask = mask; ++ mux->flags = clk_mux_flags; ++ mux->lock = lock; ++ mux->table = NULL; ++ mux->hw.init = &init; ++ ++ hw = &mux->hw; ++ ret = clk_hw_register(dev, hw); ++ if (ret) { ++ kfree(mux); ++ hw = ERR_PTR(ret); ++ } ++ ++ return hw; ++} ++ ++static struct clk_hw * ++__clk_hw_register_mux(struct device *dev, ++ struct clk_hw_onecell_data *clk_data, ++ void __iomem *base, spinlock_t *lock, ++ const struct clock_config *cfg) ++{ ++ struct mux_cfg *mux_cfg = cfg->cfg; ++ ++ if (!_is_soc_secured(base)) ++ return clk_hw_register_mux(dev, cfg->name, cfg->parent_names, ++ cfg->num_parents, cfg->flags, ++ mux_cfg->reg_off + base, ++ mux_cfg->shift, ++ mux_cfg->width, mux_cfg->mux_flags, ++ lock); ++ else ++ return clk_hw_register_smux(dev, cfg->name, ++ cfg->parent_names, ++ cfg->num_parents, cfg->flags, ++ mux_cfg->reg_off + base, ++ mux_cfg->shift, ++ mux_cfg->width, ++ mux_cfg->mux_flags, ++ lock); ++} ++ ++struct clk_div_secure { ++ struct clk_divider div; ++ u8 secure; ++}; ++ ++#define to_clk_div_secure(_hw) container_of(_hw, struct clk_div_secure, div) ++ ++static unsigned long clk_sdivider_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ return clk_divider_ops.recalc_rate(hw, parent_rate); ++} ++ ++static long clk_sdivider_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *prate) ++{ ++ return clk_divider_ops.round_rate(hw, rate, prate); ++} ++ ++#define div_mask(width) ((1 << (width)) - 1) ++ ++static int clk_sdivider_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct clk_divider *divider = to_clk_divider(hw); ++ unsigned int value; ++ unsigned long flags = 0; ++ u32 val; ++ ++ value = divider_get_val(rate, parent_rate, divider->table, ++ divider->width, divider->flags); ++ ++ if (value < 0) ++ return value; ++ ++ spin_lock_irqsave(divider->lock, flags); ++ ++ if (divider->flags & CLK_DIVIDER_HIWORD_MASK) { ++ val = div_mask(divider->width) << (divider->shift + 16); ++ } else { ++ val = clk_readl(divider->reg); ++ val &= ~(div_mask(divider->width) << divider->shift); ++ } ++ val |= (u32)value << divider->shift; ++ ++ stm32_clk_writel_secure(val, divider->reg); ++ ++ spin_unlock_irqrestore(divider->lock, flags); ++ ++ return 0; ++} ++ ++static const struct clk_ops clk_sdivider_ops = { ++ .recalc_rate = clk_sdivider_recalc_rate, ++ .round_rate = clk_sdivider_round_rate, ++ .set_rate = clk_sdivider_set_rate, ++}; ++ ++static struct clk_hw * ++clk_hw_register_sdivider_table(struct device *dev, const char *name, ++ const char *parent_name, ++ unsigned long flags, ++ void __iomem *reg, ++ u8 shift, u8 width, ++ u8 clk_divider_flags, ++ const struct clk_div_table *table, ++ spinlock_t *lock) ++{ ++ struct clk_divider *div; ++ struct clk_hw *hw; ++ struct clk_init_data init; ++ int ret; ++ ++ /* allocate the divider */ ++ div = kzalloc(sizeof(*div), GFP_KERNEL); ++ if (!div) ++ return ERR_PTR(-ENOMEM); ++ ++ init.name = name; ++ if (clk_divider_flags & CLK_DIVIDER_READ_ONLY) ++ init.ops = &clk_divider_ro_ops; ++ else ++ init.ops = &clk_sdivider_ops; ++ ++ init.flags = flags | CLK_IS_BASIC; ++ init.parent_names = (parent_name ? &parent_name : NULL); ++ init.num_parents = (parent_name ? 1 : 0); ++ ++ /* struct clk_divider assignments */ ++ div->reg = reg; ++ div->shift = shift; ++ div->width = width; ++ div->flags = clk_divider_flags; ++ div->lock = lock; ++ div->hw.init = &init; ++ div->table = table; ++ ++ /* register the clock */ ++ hw = &div->hw; ++ ++ ret = clk_hw_register(dev, hw); ++ if (ret) { ++ kfree(div); ++ hw = ERR_PTR(ret); ++ } ++ ++ return hw; ++} ++ ++static struct clk_hw * ++__clk_hw_register_divider_table(struct device *dev, ++ struct clk_hw_onecell_data *clk_data, ++ void __iomem *base, spinlock_t *lock, ++ const struct clock_config *cfg) ++{ ++ struct div_cfg *div_cfg = cfg->cfg; ++ ++ if (!_is_soc_secured(base)) ++ return clk_hw_register_divider_table(dev, cfg->name, ++ cfg->parent_name, ++ cfg->flags, ++ div_cfg->reg_off + base, ++ div_cfg->shift, ++ div_cfg->width, ++ div_cfg->div_flags, ++ div_cfg->table, ++ lock); ++ else ++ return clk_hw_register_sdivider_table(dev, cfg->name, ++ cfg->parent_name, ++ cfg->flags, ++ div_cfg->reg_off + base, ++ div_cfg->shift, ++ div_cfg->width, ++ div_cfg->div_flags, ++ div_cfg->table, ++ lock); ++} ++ ++static int mp1_sgate_clk_enable(struct clk_hw *hw) ++{ ++ struct clk_gate *gate = to_clk_gate(hw); ++ unsigned long flags = 0; ++ ++ spin_lock_irqsave(gate->lock, flags); ++ ++ stm32_clk_bit_secure(STM32_SET_BITS, BIT(gate->bit_idx), ++ gate->reg); ++ ++ spin_unlock_irqrestore(gate->lock, flags); ++ ++ return 0; ++} ++ ++static void mp1_sgate_clk_disable(struct clk_hw *hw) ++{ ++ struct clk_gate *gate = to_clk_gate(hw); ++ unsigned long flags = 0; ++ ++ spin_lock_irqsave(gate->lock, flags); ++ ++ stm32_clk_bit_secure(STM32_SET_BITS, BIT(gate->bit_idx), ++ gate->reg + RCC_CLR); ++ ++ spin_unlock_irqrestore(gate->lock, flags); ++} ++ ++static const struct clk_ops mp1_sgate_clk_ops = { ++ .enable = mp1_sgate_clk_enable, ++ .disable = mp1_sgate_clk_disable, ++ .is_enabled = clk_gate_is_enabled, ++}; ++ ++static int mp1_s_mgate_clk_enable(struct clk_hw *hw) ++{ ++ struct clk_gate *gate = to_clk_gate(hw); ++ struct stm32_clk_mgate *clk_mgate = to_clk_mgate(gate); ++ ++ clk_mgate->mgate->flag |= clk_mgate->mask; ++ ++ mp1_sgate_clk_enable(hw); ++ ++ return 0; ++} ++ ++static void mp1_s_mgate_clk_disable(struct clk_hw *hw) ++{ ++ struct clk_gate *gate = to_clk_gate(hw); ++ struct stm32_clk_mgate *clk_mgate = to_clk_mgate(gate); ++ ++ clk_mgate->mgate->flag &= ~clk_mgate->mask; ++ ++ if (clk_mgate->mgate->flag == 0) ++ mp1_sgate_clk_disable(hw); ++} ++ ++static const struct clk_ops mp1_s_mgate_clk_ops = { ++ .enable = mp1_s_mgate_clk_enable, ++ .disable = mp1_s_mgate_clk_disable, ++ .is_enabled = clk_gate_is_enabled, ++ ++}; ++ ++static u8 clk_s_mmux_get_parent(struct clk_hw *hw) ++{ ++ return clk_smux_ops.get_parent(hw); ++} ++ ++static int clk_s_mmux_set_parent(struct clk_hw *hw, u8 index) ++{ ++ struct clk_mux *mux = to_clk_mux(hw); ++ struct stm32_clk_mmux *clk_mmux = to_clk_mmux(mux); ++ struct clk_hw *hwp; ++ int ret, n; ++ ++ ret = clk_smux_ops.set_parent(hw, index); ++ if (ret) ++ return ret; ++ ++ hwp = clk_hw_get_parent(hw); ++ ++ for (n = 0; n < clk_mmux->mmux->nbr_clk; n++) ++ if (clk_mmux->mmux->hws[n] != hw) ++ clk_hw_reparent(clk_mmux->mmux->hws[n], hwp); ++ ++ return 0; ++} ++ ++static const struct clk_ops clk_s_mmux_ops = { ++ .get_parent = clk_s_mmux_get_parent, ++ .set_parent = clk_s_mmux_set_parent, ++ .determine_rate = __clk_mux_determine_rate, ++}; ++ ++#define SMUX(_id, _name, _parents, _flags,\ ++ _offset, _shift, _width, _mux_flags)\ ++{\ ++ .id = _id,\ ++ .name = _name,\ ++ .parent_names = _parents,\ ++ .num_parents = ARRAY_SIZE(_parents),\ ++ .flags = _flags,\ ++ .cfg = &(struct mux_cfg) {\ ++ .reg_off = _offset,\ ++ .shift = _shift,\ ++ .width = _width,\ ++ .mux_flags = _mux_flags,\ ++ },\ ++ .func = __clk_hw_register_mux,\ ++} ++ ++#define SDIV_TABLE(_id, _name, _parent, _flags, _offset, _shift, _width,\ ++ _div_flags, _div_table)\ ++{\ ++ .id = _id,\ ++ .name = _name,\ ++ .parent_name = _parent,\ ++ .flags = _flags,\ ++ .cfg = &(struct div_cfg) {\ ++ .reg_off = _offset,\ ++ .shift = _shift,\ ++ .width = _width,\ ++ .div_flags = _div_flags,\ ++ .table = _div_table,\ ++ },\ ++ .func = __clk_hw_register_divider_table,\ ++} ++ ++#define SDIV(_id, _name, _parent, _flags, _offset, _shift, _width,\ ++ _div_flags)\ ++ SDIV_TABLE(_id, _name, _parent, _flags, _offset, _shift, _width,\ ++ _div_flags, NULL) ++ ++#define _S_GATE(_gate_offset, _gate_bit_idx, _gate_flags)\ ++ _STM32_GATE(_gate_offset, _gate_bit_idx, _gate_flags,\ ++ NULL, NULL, &clk_sgate_ops) ++ ++#define SGATE(_id, _name, _parent, _flags, _offset, _bit_idx, _gate_flags)\ ++ STM32_GATE(_id, _name, _parent, _flags,\ ++ _S_GATE(_offset, _bit_idx, _gate_flags)) ++ ++#define _S_GATE_MP1(_gate_offset, _gate_bit_idx, _gate_flags)\ ++ _STM32_GATE(_gate_offset, _gate_bit_idx, _gate_flags,\ ++ NULL, &mp1_gate_clk_ops, &mp1_sgate_clk_ops) ++ ++#define SGATE_MP1(_id, _name, _parent, _flags, _offset, _bit_idx, _gate_flags)\ ++ STM32_GATE(_id, _name, _parent, _flags,\ ++ _S_GATE_MP1(_offset, _bit_idx, _gate_flags)) ++ ++#define _S_DIV(_div_offset, _div_shift, _div_width, _div_flags, _div_table)\ ++ _STM32_DIV(_div_offset, _div_shift, _div_width,\ ++ _div_flags, _div_table, NULL, &clk_sdivider_ops) ++ ++#define _S_MUX(_offset, _shift, _width, _mux_flags)\ ++ _STM32_MUX(_offset, _shift, _width, _mux_flags,\ ++ NULL, NULL, &clk_smux_ops) + + enum { + G_SAI1, +@@ -1408,7 +1946,7 @@ enum { + static struct stm32_mgate mp1_mgate[G_LAST]; + + #define _K_GATE(_id, _gate_offset, _gate_bit_idx, _gate_flags,\ +- _mgate, _ops)\ ++ _mgate, _ops, _ops_sec)\ + [_id] = {\ + &(struct gate_cfg) {\ + .reg_off = _gate_offset,\ +@@ -1417,15 +1955,24 @@ static struct stm32_mgate mp1_mgate[G_LAST]; + },\ + .mgate = _mgate,\ + .ops = _ops,\ ++ .ops_sec = _ops_sec,\ + } + + #define K_GATE(_id, _gate_offset, _gate_bit_idx, _gate_flags)\ + _K_GATE(_id, _gate_offset, _gate_bit_idx, _gate_flags,\ +- NULL, &mp1_gate_clk_ops) ++ NULL, &mp1_gate_clk_ops, NULL) ++ ++#define K_GATE_S(_id, _gate_offset, _gate_bit_idx, _gate_flags)\ ++ _K_GATE(_id, _gate_offset, _gate_bit_idx, _gate_flags,\ ++ NULL, &mp1_gate_clk_ops, &mp1_sgate_clk_ops) + + #define K_MGATE(_id, _gate_offset, _gate_bit_idx, _gate_flags)\ + _K_GATE(_id, _gate_offset, _gate_bit_idx, _gate_flags,\ +- &mp1_mgate[_id], &mp1_mgate_clk_ops) ++ &mp1_mgate[_id], &mp1_mgate_clk_ops, NULL) ++ ++#define K_MGATE_S(_id, _gate_offset, _gate_bit_idx, _gate_flags)\ ++ _K_GATE(_id, _gate_offset, _gate_bit_idx, _gate_flags,\ ++ &mp1_mgate[_id], &mp1_mgate_clk_ops, &mp1_s_mgate_clk_ops) + + /* Peripheral gates */ + static struct stm32_gate_cfg per_gate_cfg[G_LAST] = { +@@ -1490,17 +2037,17 @@ static struct stm32_gate_cfg per_gate_cfg[G_LAST] = { + K_MGATE(G_DSI, RCC_APB4ENSETR, 4, 0), + K_MGATE(G_LTDC, RCC_APB4ENSETR, 0, 0), + +- K_GATE(G_STGEN, RCC_APB5ENSETR, 20, 0), +- K_GATE(G_BSEC, RCC_APB5ENSETR, 16, 0), +- K_GATE(G_IWDG1, RCC_APB5ENSETR, 15, 0), +- K_GATE(G_TZPC, RCC_APB5ENSETR, 13, 0), +- K_GATE(G_TZC2, RCC_APB5ENSETR, 12, 0), +- K_GATE(G_TZC1, RCC_APB5ENSETR, 11, 0), +- K_GATE(G_RTCAPB, RCC_APB5ENSETR, 8, 0), +- K_MGATE(G_USART1, RCC_APB5ENSETR, 4, 0), +- K_MGATE(G_I2C6, RCC_APB5ENSETR, 3, 0), +- K_MGATE(G_I2C4, RCC_APB5ENSETR, 2, 0), +- K_MGATE(G_SPI6, RCC_APB5ENSETR, 0, 0), ++ K_GATE_S(G_STGEN, RCC_APB5ENSETR, 20, 0), ++ K_GATE_S(G_BSEC, RCC_APB5ENSETR, 16, 0), ++ K_GATE_S(G_IWDG1, RCC_APB5ENSETR, 15, 0), ++ K_GATE_S(G_TZPC, RCC_APB5ENSETR, 13, 0), ++ K_GATE_S(G_TZC2, RCC_APB5ENSETR, 12, 0), ++ K_GATE_S(G_TZC1, RCC_APB5ENSETR, 11, 0), ++ K_GATE_S(G_RTCAPB, RCC_APB5ENSETR, 8, 0), ++ K_MGATE_S(G_USART1, RCC_APB5ENSETR, 4, 0), ++ K_MGATE_S(G_I2C6, RCC_APB5ENSETR, 3, 0), ++ K_MGATE_S(G_I2C4, RCC_APB5ENSETR, 2, 0), ++ K_MGATE_S(G_SPI6, RCC_APB5ENSETR, 0, 0), + + K_MGATE(G_SDMMC3, RCC_AHB2ENSETR, 16, 0), + K_MGATE(G_USBO, RCC_AHB2ENSETR, 8, 0), +@@ -1529,11 +2076,11 @@ static struct stm32_gate_cfg per_gate_cfg[G_LAST] = { + K_GATE(G_GPIOB, RCC_AHB4ENSETR, 1, 0), + K_GATE(G_GPIOA, RCC_AHB4ENSETR, 0, 0), + +- K_GATE(G_BKPSRAM, RCC_AHB5ENSETR, 8, 0), +- K_MGATE(G_RNG1, RCC_AHB5ENSETR, 6, 0), +- K_GATE(G_HASH1, RCC_AHB5ENSETR, 5, 0), +- K_GATE(G_CRYP1, RCC_AHB5ENSETR, 4, 0), +- K_GATE(G_GPIOZ, RCC_AHB5ENSETR, 0, 0), ++ K_GATE_S(G_BKPSRAM, RCC_AHB5ENSETR, 8, 0), ++ K_MGATE_S(G_RNG1, RCC_AHB5ENSETR, 6, 0), ++ K_GATE_S(G_HASH1, RCC_AHB5ENSETR, 5, 0), ++ K_GATE_S(G_CRYP1, RCC_AHB5ENSETR, 4, 0), ++ K_GATE_S(G_GPIOZ, RCC_AHB5ENSETR, 0, 0), + + K_GATE(G_USBH, RCC_AHB6ENSETR, 24, 0), + K_GATE(G_CRC1, RCC_AHB6ENSETR, 20, 0), +@@ -1541,12 +2088,15 @@ static struct stm32_gate_cfg per_gate_cfg[G_LAST] = { + K_MGATE(G_SDMMC1, RCC_AHB6ENSETR, 16, 0), + K_MGATE(G_QSPI, RCC_AHB6ENSETR, 14, 0), + K_MGATE(G_FMC, RCC_AHB6ENSETR, 12, 0), ++ + K_GATE(G_ETHMAC, RCC_AHB6ENSETR, 10, 0), + K_GATE(G_ETHRX, RCC_AHB6ENSETR, 9, 0), + K_GATE(G_ETHTX, RCC_AHB6ENSETR, 8, 0), + K_GATE(G_ETHCK, RCC_AHB6ENSETR, 7, 0), ++ + K_MGATE(G_GPU, RCC_AHB6ENSETR, 5, 0), + K_GATE(G_MDMA, RCC_AHB6ENSETR, 0, 0), ++ + K_GATE(G_ETHSTP, RCC_AHB6LPENSETR, 11, 0), + }; + +@@ -1591,7 +2141,7 @@ enum { + + static struct stm32_mmux ker_mux[M_LAST]; + +-#define _K_MUX(_id, _offset, _shift, _width, _mux_flags, _mmux, _ops)\ ++#define _K_MUX(_id, _offset, _shift, _width, _mux_flags, _mmux, _ops, _ops_sec)\ + [_id] = {\ + &(struct mux_cfg) {\ + .reg_off = _offset,\ +@@ -1602,15 +2152,24 @@ static struct stm32_mmux ker_mux[M_LAST]; + },\ + .mmux = _mmux,\ + .ops = _ops,\ ++ .ops_sec = _ops_sec,\ + } + + #define K_MUX(_id, _offset, _shift, _width, _mux_flags)\ + _K_MUX(_id, _offset, _shift, _width, _mux_flags,\ +- NULL, NULL) ++ NULL, NULL, NULL) ++ ++#define K_MUX_S(_id, _offset, _shift, _width, _mux_flags)\ ++ _K_MUX(_id, _offset, _shift, _width, _mux_flags,\ ++ NULL, NULL, &clk_smux_ops) + + #define K_MMUX(_id, _offset, _shift, _width, _mux_flags)\ + _K_MUX(_id, _offset, _shift, _width, _mux_flags,\ +- &ker_mux[_id], &clk_mmux_ops) ++ &ker_mux[_id], &clk_mmux_ops, NULL) ++ ++#define K_MMUX_S(_id, _offset, _shift, _width, _mux_flags)\ ++ _K_MUX(_id, _offset, _shift, _width, _mux_flags,\ ++ &ker_mux[_id], &clk_mmux_ops, &clk_s_mmux_ops) + + static const struct stm32_mux_cfg ker_mux_cfg[M_LAST] = { + /* Kernel multi mux */ +@@ -1626,7 +2185,7 @@ static const struct stm32_mux_cfg ker_mux_cfg[M_LAST] = { + K_MMUX(M_UART78, RCC_UART78CKSELR, 0, 3, 0), + K_MMUX(M_SAI1, RCC_SAI1CKSELR, 0, 3, 0), + K_MMUX(M_ETHCK, RCC_ETHCKSELR, 0, 2, 0), +- K_MMUX(M_I2C46, RCC_I2C46CKSELR, 0, 3, 0), ++ K_MMUX_S(M_I2C46, RCC_I2C46CKSELR, 0, 3, 0), + + /* Kernel simple mux */ + K_MUX(M_RNG2, RCC_RNG2CKSELR, 0, 2, 0), +@@ -1647,10 +2206,10 @@ static const struct stm32_mux_cfg ker_mux_cfg[M_LAST] = { + K_MUX(M_ADC12, RCC_ADCCKSELR, 0, 2, 0), + K_MUX(M_DSI, RCC_DSICKSELR, 0, 1, 0), + K_MUX(M_CKPER, RCC_CPERCKSELR, 0, 2, 0), +- K_MUX(M_RNG1, RCC_RNG1CKSELR, 0, 2, 0), +- K_MUX(M_STGEN, RCC_STGENCKSELR, 0, 2, 0), +- K_MUX(M_USART1, RCC_UART1CKSELR, 0, 3, 0), +- K_MUX(M_SPI6, RCC_SPI6CKSELR, 0, 3, 0), ++ K_MUX_S(M_RNG1, RCC_RNG1CKSELR, 0, 2, 0), ++ K_MUX_S(M_STGEN, RCC_STGENCKSELR, 0, 2, 0), ++ K_MUX_S(M_USART1, RCC_UART1CKSELR, 0, 3, 0), ++ K_MUX_S(M_SPI6, RCC_SPI6CKSELR, 0, 3, 0), + }; + + static const struct clock_config stm32mp1_clock_cfg[] = { +@@ -1659,11 +2218,12 @@ static const struct clock_config stm32mp1_clock_cfg[] = { + CLK_DIVIDER_READ_ONLY), + + /* External / Internal Oscillators */ +- GATE_MP1(CK_HSE, "ck_hse", "clk-hse", 0, RCC_OCENSETR, 8, 0), +- GATE_MP1(CK_CSI, "ck_csi", "clk-csi", 0, RCC_OCENSETR, 4, 0), +- GATE_MP1(CK_HSI, "ck_hsi", "clk-hsi-div", 0, RCC_OCENSETR, 0, 0), +- GATE(CK_LSI, "ck_lsi", "clk-lsi", 0, RCC_RDLSICR, 0, 0), +- GATE(CK_LSE, "ck_lse", "clk-lse", 0, RCC_BDCR, 0, 0), ++ SGATE_MP1(CK_HSE, "ck_hse", "clk-hse", 0, RCC_OCENSETR, 8, 0), ++ SGATE_MP1(CK_CSI, "ck_csi", "clk-csi", CLK_IS_CRITICAL, ++ RCC_OCENSETR, 4, 0), ++ SGATE_MP1(CK_HSI, "ck_hsi", "clk-hsi-div", 0, RCC_OCENSETR, 0, 0), ++ SGATE(CK_LSI, "ck_lsi", "clk-lsi", 0, RCC_RDLSICR, 0, 0), ++ SGATE(CK_LSE, "ck_lse", "clk-lse", 0, RCC_BDCR, 0, 0), + + FIXED_FACTOR(CK_HSE_DIV2, "clk-hse-div2", "ck_hse", 0, 1, 2), + +@@ -1685,24 +2245,24 @@ static const struct clock_config stm32mp1_clock_cfg[] = { + + /* ODF */ + COMPOSITE(PLL1_P, "pll1_p", PARENT("pll1"), 0, +- _GATE(RCC_PLL1CR, 4, 0), ++ _S_GATE(RCC_PLL1CR, 4, 0), + _NO_MUX, +- _DIV(RCC_PLL1CFGR2, 0, 7, 0, NULL)), ++ _S_DIV(RCC_PLL1CFGR2, 0, 7, 0, NULL)), + + COMPOSITE(PLL2_P, "pll2_p", PARENT("pll2"), 0, +- _GATE(RCC_PLL2CR, 4, 0), ++ _S_GATE(RCC_PLL2CR, 4, 0), + _NO_MUX, +- _DIV(RCC_PLL2CFGR2, 0, 7, 0, NULL)), ++ _S_DIV(RCC_PLL2CFGR2, 0, 7, 0, NULL)), + + COMPOSITE(PLL2_Q, "pll2_q", PARENT("pll2"), 0, +- _GATE(RCC_PLL2CR, 5, 0), ++ _S_GATE(RCC_PLL2CR, 5, 0), + _NO_MUX, +- _DIV(RCC_PLL2CFGR2, 8, 7, 0, NULL)), ++ _S_DIV(RCC_PLL2CFGR2, 8, 7, 0, NULL)), + + COMPOSITE(PLL2_R, "pll2_r", PARENT("pll2"), CLK_IS_CRITICAL, +- _GATE(RCC_PLL2CR, 6, 0), ++ _S_GATE(RCC_PLL2CR, 6, 0), + _NO_MUX, +- _DIV(RCC_PLL2CFGR2, 16, 7, 0, NULL)), ++ _S_DIV(RCC_PLL2CFGR2, 16, 7, 0, NULL)), + + COMPOSITE(PLL3_P, "pll3_p", PARENT("pll3"), 0, + _GATE(RCC_PLL3CR, 4, 0), +@@ -1738,20 +2298,20 @@ static const struct clock_config stm32mp1_clock_cfg[] = { + MUX(CK_PER, "ck_per", per_src, CLK_OPS_PARENT_ENABLE, + RCC_CPERCKSELR, 0, 2, 0), + +- MUX(CK_MPU, "ck_mpu", cpu_src, CLK_OPS_PARENT_ENABLE | ++ SMUX(CK_MPU, "ck_mpu", cpu_src, CLK_OPS_PARENT_ENABLE | + CLK_IS_CRITICAL, RCC_MPCKSELR, 0, 2, 0), + + COMPOSITE(CK_AXI, "ck_axi", axi_src, CLK_IS_CRITICAL | + CLK_OPS_PARENT_ENABLE, + _NO_GATE, +- _MUX(RCC_ASSCKSELR, 0, 2, 0), +- _DIV(RCC_AXIDIVR, 0, 3, 0, axi_div_table)), ++ _S_MUX(RCC_ASSCKSELR, 0, 2, 0), ++ _S_DIV(RCC_AXIDIVR, 0, 3, 0, axi_div_table)), + + COMPOSITE(CK_MCU, "ck_mcu", mcu_src, CLK_IS_CRITICAL | + CLK_OPS_PARENT_ENABLE, + _NO_GATE, +- _MUX(RCC_MSSCKSELR, 0, 2, 0), +- _DIV(RCC_MCUDIVR, 0, 4, 0, mcu_div_table)), ++ _S_MUX(RCC_MSSCKSELR, 0, 2, 0), ++ _S_DIV(RCC_MCUDIVR, 0, 4, 0, mcu_div_table)), + + DIV_TABLE(NO_ID, "pclk1", "ck_mcu", CLK_IGNORE_UNUSED, RCC_APB1DIVR, 0, + 3, CLK_DIVIDER_READ_ONLY, apb_div_table), +@@ -1906,7 +2466,7 @@ static const struct clock_config stm32mp1_clock_cfg[] = { + KCLK(RNG1_K, "rng1_k", rng_src, 0, G_RNG1, M_RNG1), + KCLK(RNG2_K, "rng2_k", rng_src, 0, G_RNG2, M_RNG2), + KCLK(USBPHY_K, "usbphy_k", usbphy_src, 0, G_USBPHY, M_USBPHY), +- KCLK(STGEN_K, "stgen_k", stgen_src, CLK_IS_CRITICAL, G_STGEN, M_STGEN), ++ KCLK(STGEN_K, "stgen_k", stgen_src, CLK_IS_CRITICAL, G_STGEN, M_STGEN), + KCLK(SPDIF_K, "spdif_k", spdif_src, 0, G_SPDIF, M_SPDIF), + KCLK(SPI1_K, "spi1_k", spi123_src, 0, G_SPI1, M_SPI1), + KCLK(SPI2_K, "spi2_k", spi123_src, 0, G_SPI2, M_SPI23), +@@ -1952,19 +2512,20 @@ static const struct clock_config stm32mp1_clock_cfg[] = { + MGATE_MP1(GPU_K, "gpu_k", "pll2_q", 0, G_GPU), + MGATE_MP1(DAC12_K, "dac12_k", "ck_lsi", 0, G_DAC12), + +- COMPOSITE(ETHPTP_K, "ethptp_k", eth_src, CLK_OPS_PARENT_ENABLE, ++ COMPOSITE(ETHPTP_K, "ethptp_k", eth_src, CLK_OPS_PARENT_ENABLE | ++ CLK_SET_RATE_NO_REPARENT, + _NO_GATE, + _MMUX(M_ETHCK), + _DIV(RCC_ETHCKSELR, 4, 4, CLK_DIVIDER_ALLOW_ZERO, NULL)), + + /* RTC clock */ +- DIV(NO_ID, "ck_hse_rtc", "ck_hse", 0, RCC_RTCDIVR, 0, 7, +- CLK_DIVIDER_ALLOW_ZERO), ++ SDIV(NO_ID, "ck_hse_rtc", "ck_hse", 0, RCC_RTCDIVR, 0, 7, ++ CLK_DIVIDER_ALLOW_ZERO), + + COMPOSITE(RTC, "ck_rtc", rtc_src, CLK_OPS_PARENT_ENABLE | + CLK_SET_RATE_PARENT, +- _GATE(RCC_BDCR, 20, 0), +- _MUX(RCC_BDCR, 16, 2, 0), ++ _S_GATE(RCC_BDCR, 20, 0), ++ _S_MUX(RCC_BDCR, 16, 2, 0), + _NO_DIV), + + /* MCO clocks */ +@@ -2082,21 +2643,334 @@ static int stm32_rcc_init(struct device_node *np, + return of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data); + } + ++static void __iomem *rcc_base; ++ ++static int stm32_rcc_init_pwr(struct device_node *np); ++ + static void stm32mp1_rcc_init(struct device_node *np) + { +- void __iomem *base; +- +- base = of_iomap(np, 0); +- if (!base) { ++ rcc_base = of_iomap(np, 0); ++ if (!rcc_base) { + pr_err("%s: unable to map resource", np->name); + of_node_put(np); + return; + } + +- if (stm32_rcc_init(np, base, stm32mp1_match_data)) { +- iounmap(base); ++ if (stm32_rcc_init(np, rcc_base, stm32mp1_match_data)) { ++ iounmap(rcc_base); + of_node_put(np); ++ return; + } ++ ++ stm32_rcc_init_pwr(np); + } + + CLK_OF_DECLARE_DRIVER(stm32mp1_rcc, "st,stm32mp1-rcc", stm32mp1_rcc_init); ++ ++/* ++ * RCC POWER ++ * ++ */ ++ ++static struct regmap *pwr_syscon; ++ ++struct reg { ++ u32 address; ++ u32 val; ++}; ++ ++/* This table lists the IPs for which CSLEEP is enabled */ ++static const struct reg lp_table[] = { ++ { 0xB04, 0x00000000 }, /* APB1 */ ++ { 0xB0C, 0x00000000 }, /* APB2 */ ++ { 0xB14, 0x00000800 }, /* APB3 */ ++ { 0x304, 0x00000000 }, /* APB4 */ ++ { 0xB1C, 0x00000000 }, /* AHB2 */ ++ { 0xB24, 0x00000000 }, /* AHB3 */ ++ { 0xB2C, 0x00000000 }, /* AHB4 */ ++ { 0x31C, 0x00000000 }, /* AHB6 */ ++ { 0xB34, 0x00000000 }, /* AXIM */ ++ { 0xB3C, 0x00000000 }, /* MLAHB */ ++}; ++ ++struct sreg { ++ u32 address; ++ u32 secured; ++ u32 val; ++}; ++ ++static struct sreg clock_gating[] = { ++ { 0xA00, 0 }, /* APB1 */ ++ { 0xA08, 0 }, /* APB2 */ ++ { 0xA10, 0 }, /* APB3 */ ++ { 0x200, 0 }, /* APB4 */ ++ { 0x208, 1 }, /* APB5 */ ++ { 0x210, 1 }, /* AHB5 */ ++ { 0x218, 0 }, /* AHB6 */ ++ { 0xA18, 0 }, /* AHB2 */ ++ { 0xA20, 0 }, /* AHB3 */ ++ { 0xA28, 0 }, /* AHB4 */ ++ { 0xA38, 0 }, /* MLAHB */ ++ { 0x800, 0 }, /* MCO1 */ ++ { 0x804, 0 }, /* MCO2 */ ++ { 0x894, 0 }, /* PLL4 */ ++ { 0x89C, 0 }, /* PLL4CFGR2 */ ++}; ++ ++struct smux { ++ const char *name; ++ struct clk *clk; ++ struct clk *clkp; ++}; ++ ++#define KER_SRC(_clk_name)\ ++{\ ++ .name = _clk_name,\ ++} ++ ++struct smux _mux_kernel[] = { ++ KER_SRC("sdmmc1_k"), ++ KER_SRC("spi2_k"), ++ KER_SRC("spi4_k"), ++ KER_SRC("i2c1_k"), ++ KER_SRC("i2c3_k"), ++ KER_SRC("lptim2_k"), ++ KER_SRC("lptim3_k"), ++ KER_SRC("usart2_k"), ++ KER_SRC("usart3_k"), ++ KER_SRC("uart7_k"), ++ KER_SRC("sai1_k"), ++ KER_SRC("ethck_k"), ++ KER_SRC("i2c4_k"), ++ KER_SRC("rng2_k"), ++ KER_SRC("sdmmc3_k"), ++ KER_SRC("fmc_k"), ++ KER_SRC("qspi_k"), ++ KER_SRC("usbphy_k"), ++ KER_SRC("usbo_k"), ++ KER_SRC("spdif_k"), ++ KER_SRC("spi1_k"), ++ KER_SRC("cec_k"), ++ KER_SRC("lptim1_k"), ++ KER_SRC("uart6_k"), ++ KER_SRC("fdcan_k"), ++ KER_SRC("sai2_k"), ++ KER_SRC("sai3_k"), ++ KER_SRC("sai4_k"), ++ KER_SRC("adc12_k"), ++ KER_SRC("dsi_k"), ++ KER_SRC("ck_per"), ++ KER_SRC("rng1_k"), ++ KER_SRC("stgen_k"), ++ KER_SRC("usart1_k"), ++ KER_SRC("spi6_k"), ++}; ++ ++static struct sreg pll_clock[] = { ++ { 0x880, 0 }, /* PLL3 */ ++ { 0x894, 0 }, /* PLL4 */ ++}; ++ ++static struct sreg mcu_source[] = { ++ { 0x048, 0 }, /* MSSCKSELR */ ++}; ++ ++#define RCC_IRQ_FLAGS_MASK 0x110F1F ++#define RCC_STOP_MASK (BIT(0) | BIT(1)) ++#define RCC_RSTSR 0x408 ++#define PWR_MPUCR 0x10 ++#define PLL_EN (BIT(0)) ++#define STOP_FLAG (BIT(5)) ++#define SBF (BIT(11)) ++#define SBF_MPU (BIT(12)) ++ ++ ++ ++ ++static irqreturn_t stm32mp1_rcc_irq_handler(int irq, void *sdata) ++{ ++ pr_info("RCC generic interrupt received\n"); ++ ++ /* clear interrupt flag */ ++ SMC(STM32_SVC_RCC, STM32_WRITE, RCC_CIFR, RCC_IRQ_FLAGS_MASK); ++ ++ return IRQ_HANDLED; ++} ++ ++static void stm32mp1_backup_sreg(struct sreg *sreg, int size) ++{ ++ int i; ++ ++ for (i = 0; i < size; i++) ++ sreg[i].val = readl_relaxed(rcc_base + sreg[i].address); ++} ++ ++static void stm32mp1_restore_sreg(struct sreg *sreg, int size) ++{ ++ int i; ++ u32 val, address; ++ int soc_secured; ++ ++ soc_secured = _is_soc_secured(rcc_base); ++ ++ for (i = 0; i < size; i++) { ++ val = sreg[i].val; ++ address = sreg[i].address; ++ ++ if (soc_secured && sreg[i].secured) ++ SMC(STM32_SVC_RCC, STM32_WRITE, ++ address, val); ++ else ++ writel_relaxed(val, rcc_base + address); ++ } ++} ++ ++static void stm32mp1_restore_pll(struct sreg *sreg, int size) ++{ ++ int i; ++ u32 val; ++ void __iomem *address; ++ ++ for (i = 0; i < size; i++) { ++ val = sreg[i].val; ++ address = sreg[i].address + rcc_base; ++ ++ /* if pll was off turn it on before */ ++ if ((readl_relaxed(address) & PLL_EN) == 0) { ++ writel_relaxed(PLL_EN, address); ++ while ((readl_relaxed(address) & PLL_RDY) == 0) ++ ; ++ } ++ ++ /* 2sd step: restore odf */ ++ writel_relaxed(val, address); ++ } ++} ++ ++static void stm32mp1_backup_mux(struct smux *smux, int size) ++{ ++ int i; ++ ++ for (i = 0; i < size; i++) { ++ smux[i].clk = __clk_lookup(smux[i].name); ++ smux[i].clkp = clk_get_parent(smux[i].clk); ++ } ++} ++ ++static void stm32mp1_restore_mux(struct smux *smux, int size) ++{ ++ int i; ++ ++ for (i = 0; i < size; i++) ++ clk_set_parent_force(smux[i].clk, smux[i].clkp); ++} ++ ++#define RCC_BIT_HSI 0 ++#define RCC_BIT_CSI 4 ++#define RCC_BIT_HSE 8 ++ ++#define RCC_CK_OSC_MASK (BIT(RCC_BIT_HSE) | BIT(RCC_BIT_CSI) | BIT(RCC_BIT_HSI)) ++ ++#define RCC_CK_XXX_KER_MASK (RCC_CK_OSC_MASK << 1) ++ ++static int stm32mp1_clk_suspend(void) ++{ ++ u32 reg; ++ ++ /* Save pll regs */ ++ stm32mp1_backup_sreg(pll_clock, ARRAY_SIZE(pll_clock)); ++ ++ /* Save mcu source */ ++ stm32mp1_backup_sreg(mcu_source, ARRAY_SIZE(mcu_source)); ++ ++ /* Save clock gating regs */ ++ stm32mp1_backup_sreg(clock_gating, ARRAY_SIZE(clock_gating)); ++ ++ /* Save kernel clock regs */ ++ stm32mp1_backup_mux(_mux_kernel, ARRAY_SIZE(_mux_kernel)); ++ ++ /* Enable ck_xxx_ker clocks if ck_xxx was on */ ++ reg = readl_relaxed(rcc_base + RCC_OCENSETR) & RCC_CK_OSC_MASK; ++ writel_relaxed(reg << 1, rcc_base + RCC_OCENSETR); ++ ++ return 0; ++} ++ ++static void stm32mp1_clk_resume(void) ++{ ++ u32 power_flags_rcc, power_flags_pwr; ++ ++ /* Read power flags and decide what to resume */ ++ regmap_read(pwr_syscon, PWR_MPUCR, &power_flags_pwr); ++ power_flags_rcc = readl_relaxed(rcc_base + RCC_RSTSR); ++ ++ if ((power_flags_pwr & STOP_FLAG) == STOP_FLAG) { ++ /* Restore pll */ ++ stm32mp1_restore_pll(pll_clock, ARRAY_SIZE(pll_clock)); ++ ++ /* Restore mcu source */ ++ stm32mp1_restore_sreg(mcu_source, ARRAY_SIZE(mcu_source)); ++ } else if (((power_flags_rcc & SBF) == SBF) || ++ ((power_flags_rcc & SBF_MPU) == SBF_MPU)) { ++ stm32mp1_restore_sreg(clock_gating, ARRAY_SIZE(clock_gating)); ++ ++ stm32mp1_restore_mux(_mux_kernel, ARRAY_SIZE(_mux_kernel)); ++ } ++ ++ SMC(STM32_SVC_RCC, STM32_WRITE, RCC_RSTSR, 0); ++ ++ /* Disable ck_xxx_ker clocks */ ++ stm32_clk_bit_secure(STM32_SET_BITS, RCC_CK_XXX_KER_MASK, ++ rcc_base + RCC_OCENSETR + RCC_CLR); ++} ++ ++static struct syscore_ops stm32mp1_clk_ops = { ++ .suspend = stm32mp1_clk_suspend, ++ .resume = stm32mp1_clk_resume, ++}; ++ ++static struct irqaction rcc_irq = { ++ .name = "rcc irq", ++ .flags = IRQF_ONESHOT, ++ .handler = stm32mp1_rcc_irq_handler, ++}; ++ ++static int stm32_rcc_init_pwr(struct device_node *np) ++{ ++ int irq; ++ int ret; ++ int i; ++ ++ pwr_syscon = syscon_regmap_lookup_by_phandle(np, "st,pwr"); ++ if (IS_ERR(pwr_syscon)) { ++ pr_err("%s: pwr syscon required !\n", __func__); ++ return PTR_ERR(pwr_syscon); ++ } ++ ++ /* register generic irq */ ++ irq = of_irq_get(np, 0); ++ if (irq < 0) { ++ pr_err("%s: failed to get RCC generic IRQ\n", __func__); ++ return irq; ++ } ++ ++ ret = setup_irq(irq, &rcc_irq); ++ if (ret) { ++ pr_err("%s: failed to register generic IRQ\n", __func__); ++ return ret; ++ } ++ ++ ++ /* Configure LPEN static table */ ++ for (i = 0; i < ARRAY_SIZE(lp_table); i++) ++ writel_relaxed(lp_table[i].val, rcc_base + lp_table[i].address); ++ ++ /* cleanup RCC flags */ ++ SMC(STM32_SVC_RCC, STM32_WRITE, RCC_CIFR, RCC_IRQ_FLAGS_MASK); ++ ++ SMC(STM32_SVC_RCC, STM32_WRITE, RCC_SREQCLRR, RCC_STOP_MASK); ++ ++ register_syscore_ops(&stm32mp1_clk_ops); ++ ++ return 0; ++} +diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c +index d31055a..6d8326d 100644 +--- a/drivers/clk/clk.c ++++ b/drivers/clk/clk.c +@@ -2204,7 +2204,8 @@ bool clk_has_parent(struct clk *clk, struct clk *parent) + EXPORT_SYMBOL_GPL(clk_has_parent); + + static int clk_core_set_parent_nolock(struct clk_core *core, +- struct clk_core *parent) ++ struct clk_core *parent, ++ bool force) + { + int ret = 0; + int p_index = 0; +@@ -2215,7 +2216,7 @@ static int clk_core_set_parent_nolock(struct clk_core *core, + if (!core) + return 0; + +- if (core->parent == parent) ++ if (core->parent == parent && !force) + return 0; + + /* verify ops for for multi-parent clks */ +@@ -2272,6 +2273,7 @@ static int clk_core_set_parent_nolock(struct clk_core *core, + * clk_set_parent - switch the parent of a mux clk + * @clk: the mux clk whose input we are switching + * @parent: the new input to clk ++ * @force: don't test if parent is already set + * + * Re-parent clk to use parent as its new input source. If clk is in + * prepared state, the clk will get enabled for the duration of this call. If +@@ -2285,7 +2287,7 @@ static int clk_core_set_parent_nolock(struct clk_core *core, + * + * Returns 0 on success, -EERROR otherwise. + */ +-int clk_set_parent(struct clk *clk, struct clk *parent) ++int _clk_set_parent(struct clk *clk, struct clk *parent, bool force) + { + int ret; + +@@ -2298,7 +2300,8 @@ int clk_set_parent(struct clk *clk, struct clk *parent) + clk_core_rate_unprotect(clk->core); + + ret = clk_core_set_parent_nolock(clk->core, +- parent ? parent->core : NULL); ++ parent ? parent->core : NULL, ++ force); + + if (clk->exclusive_count) + clk_core_rate_protect(clk->core); +@@ -2307,8 +2310,19 @@ int clk_set_parent(struct clk *clk, struct clk *parent) + + return ret; + } ++ ++int clk_set_parent(struct clk *clk, struct clk *parent) ++{ ++ return _clk_set_parent(clk, parent, 0); ++} + EXPORT_SYMBOL_GPL(clk_set_parent); + ++int clk_set_parent_force(struct clk *clk, struct clk *parent) ++{ ++ return _clk_set_parent(clk, parent, 1); ++} ++EXPORT_SYMBOL_GPL(clk_set_parent_force); ++ + static int clk_core_set_phase_nolock(struct clk_core *core, int degrees) + { + int ret = -EINVAL; +@@ -3350,7 +3364,7 @@ void clk_unregister(struct clk *clk) + /* Reparent all children to the orphan list. */ + hlist_for_each_entry_safe(child, t, &clk->core->children, + child_node) +- clk_core_set_parent_nolock(child, NULL); ++ clk_core_set_parent_nolock(child, NULL, 0); + } + + hlist_del_init(&clk->core->child_node); +diff --git a/include/dt-bindings/clock/stm32mp1-clks.h b/include/dt-bindings/clock/stm32mp1-clks.h +index 90ec780..4cdaf13 100644 +--- a/include/dt-bindings/clock/stm32mp1-clks.h ++++ b/include/dt-bindings/clock/stm32mp1-clks.h +@@ -248,7 +248,4 @@ + + #define STM32MP1_LAST_CLK 232 + +-#define LTDC_K LTDC_PX +-#define ETHMAC_K ETHCK_K +- + #endif /* _DT_BINDINGS_STM32MP1_CLKS_H_ */ +diff --git a/include/linux/clk.h b/include/linux/clk.h +index 4f750c4..ffbae16 100644 +--- a/include/linux/clk.h ++++ b/include/linux/clk.h +@@ -602,6 +602,7 @@ int clk_set_max_rate(struct clk *clk, unsigned long rate); + * Returns success (0) or negative errno. + */ + int clk_set_parent(struct clk *clk, struct clk *parent); ++int clk_set_parent_force(struct clk *clk, struct clk *parent); + + /** + * clk_get_parent - get the parent clock source for this clock +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0003-ARM-stm32mp1-r0-rc1-DMA.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0003-ARM-stm32mp1-r0-rc1-DMA.patch new file mode 100644 index 0000000..9f436ab --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0003-ARM-stm32mp1-r0-rc1-DMA.patch @@ -0,0 +1,2054 @@ +From 234beab933e3faccae9ea8ea383ac85fa718a886 Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Tue, 13 Nov 2018 12:16:55 +0100 +Subject: [PATCH 03/52] ARM: stm32mp1-r0-rc1: DMA + +--- + drivers/dma/stm32-dma.c | 1030 ++++++++++++++++++++++++++++++++++++++------ + drivers/dma/stm32-dmamux.c | 110 ++++- + drivers/dma/stm32-mdma.c | 222 +++++++++- + 3 files changed, 1195 insertions(+), 167 deletions(-) + +diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c +index 379e8d5..4830f8e 100644 +--- a/drivers/dma/stm32-dma.c ++++ b/drivers/dma/stm32-dma.c +@@ -15,14 +15,18 @@ + #include + #include + #include ++#include + #include ++#include + #include + #include + #include + #include ++#include + #include + #include + #include ++#include + #include + #include + #include +@@ -118,6 +122,7 @@ + #define STM32_DMA_FIFO_THRESHOLD_FULL 0x03 + + #define STM32_DMA_MAX_DATA_ITEMS 0xffff ++#define STM32_DMA_SRAM_GRANULARITY PAGE_SIZE + /* + * Valid transfer starts from @0 to @0xFFFE leading to unaligned scatter + * gather at boundary. Thus it's safer to round down this value on FIFO +@@ -135,6 +140,12 @@ + /* DMA Features */ + #define STM32_DMA_THRESHOLD_FTR_MASK GENMASK(1, 0) + #define STM32_DMA_THRESHOLD_FTR_GET(n) ((n) & STM32_DMA_THRESHOLD_FTR_MASK) ++#define STM32_DMA_MDMA_CHAIN_FTR_MASK BIT(2) ++#define STM32_DMA_MDMA_CHAIN_FTR_GET(n) (((n) & STM32_DMA_MDMA_CHAIN_FTR_MASK) \ ++ >> 2) ++#define STM32_DMA_MDMA_SRAM_SIZE_MASK GENMASK(4, 3) ++#define STM32_DMA_MDMA_SRAM_SIZE_GET(n) (((n) & STM32_DMA_MDMA_SRAM_SIZE_MASK) \ ++ >> 3) + + enum stm32_dma_width { + STM32_DMA_BYTE, +@@ -176,15 +187,31 @@ struct stm32_dma_chan_reg { + u32 dma_sfcr; + }; + ++struct stm32_dma_mdma_desc { ++ struct sg_table sgt; ++ struct dma_async_tx_descriptor *desc; ++}; ++ ++struct stm32_dma_mdma { ++ struct dma_chan *chan; ++ enum dma_transfer_direction dir; ++ dma_addr_t sram_buf; ++ u32 sram_period; ++ u32 num_sgs; ++}; ++ + struct stm32_dma_sg_req { +- u32 len; ++ struct scatterlist stm32_sgl_req; + struct stm32_dma_chan_reg chan_reg; ++ struct stm32_dma_mdma_desc m_desc; + }; + + struct stm32_dma_desc { + struct virt_dma_desc vdesc; + bool cyclic; + u32 num_sgs; ++ dma_addr_t dma_buf; ++ void *dma_buf_cpu; + struct stm32_dma_sg_req sg_req[]; + }; + +@@ -201,6 +228,10 @@ struct stm32_dma_chan { + u32 threshold; + u32 mem_burst; + u32 mem_width; ++ struct stm32_dma_mdma mchan; ++ u32 use_mdma; ++ u32 sram_size; ++ u32 residue_after_drain; + }; + + struct stm32_dma_device { +@@ -210,6 +241,7 @@ struct stm32_dma_device { + struct reset_control *rst; + bool mem2mem; + struct stm32_dma_chan chan[STM32_DMA_MAX_CHANNELS]; ++ struct gen_pool *sram_pool; + }; + + static struct stm32_dma_device *stm32_dma_get_dev(struct stm32_dma_chan *chan) +@@ -308,20 +340,12 @@ static bool stm32_dma_fifo_threshold_is_allowed(u32 burst, u32 threshold, + + static bool stm32_dma_is_burst_possible(u32 buf_len, u32 threshold) + { +- switch (threshold) { +- case STM32_DMA_FIFO_THRESHOLD_FULL: +- if (buf_len >= STM32_DMA_MAX_BURST) +- return true; +- else +- return false; +- case STM32_DMA_FIFO_THRESHOLD_HALFFULL: +- if (buf_len >= STM32_DMA_MAX_BURST / 2) +- return true; +- else +- return false; +- default: +- return false; +- } ++ /* ++ * Buffer or period length has to be aligned on FIFO depth. ++ * Otherwise bytes may be stuck within FIFO at buffer or period ++ * length. ++ */ ++ return ((buf_len % ((threshold + 1) * 4)) == 0); + } + + static u32 stm32_dma_get_best_burst(u32 buf_len, u32 max_burst, u32 threshold, +@@ -497,11 +521,15 @@ static void stm32_dma_stop(struct stm32_dma_chan *chan) + static int stm32_dma_terminate_all(struct dma_chan *c) + { + struct stm32_dma_chan *chan = to_stm32_dma_chan(c); ++ struct stm32_dma_mdma *mchan = &chan->mchan; + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&chan->vchan.lock, flags); + ++ if (chan->use_mdma) ++ dmaengine_terminate_async(mchan->chan); ++ + if (chan->busy) { + stm32_dma_stop(chan); + chan->desc = NULL; +@@ -514,9 +542,96 @@ static int stm32_dma_terminate_all(struct dma_chan *c) + return 0; + } + ++static u32 stm32_dma_get_remaining_bytes(struct stm32_dma_chan *chan) ++{ ++ u32 dma_scr, width, ndtr; ++ struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan); ++ ++ dma_scr = stm32_dma_read(dmadev, STM32_DMA_SCR(chan->id)); ++ width = STM32_DMA_SCR_PSIZE_GET(dma_scr); ++ ndtr = stm32_dma_read(dmadev, STM32_DMA_SNDTR(chan->id)); ++ ++ return ndtr << width; ++} ++ ++static int stm32_dma_mdma_drain(struct stm32_dma_chan *chan) ++{ ++ struct stm32_dma_mdma *mchan = &chan->mchan; ++ struct stm32_dma_sg_req *sg_req; ++ struct dma_device *ddev = mchan->chan->device; ++ struct dma_async_tx_descriptor *desc = NULL; ++ enum dma_status status; ++ dma_addr_t src_buf, dst_buf; ++ u32 mdma_residue, mdma_wrote, dma_to_write, len; ++ struct dma_tx_state state; ++ int ret; ++ ++ /* DMA/MDMA chain: drain remaining data in SRAM */ ++ ++ /* Get the residue on MDMA side */ ++ status = dmaengine_tx_status(mchan->chan, mchan->chan->cookie, &state); ++ if (status == DMA_COMPLETE) ++ return status; ++ ++ mdma_residue = state.residue; ++ sg_req = &chan->desc->sg_req[chan->next_sg - 1]; ++ len = sg_dma_len(&sg_req->stm32_sgl_req); ++ ++ /* ++ * Total = mdma blocks * sram_period + rest (< sram_period) ++ * so mdma blocks * sram_period = len - mdma residue - rest ++ */ ++ mdma_wrote = len - mdma_residue - (len % mchan->sram_period); ++ ++ /* Remaining data stuck in SRAM */ ++ dma_to_write = mchan->sram_period - stm32_dma_get_remaining_bytes(chan); ++ if (dma_to_write > 0) { ++ /* Stop DMA current operation */ ++ stm32_dma_disable_chan(chan); ++ ++ /* Terminate current MDMA to initiate a new one */ ++ dmaengine_terminate_all(mchan->chan); ++ ++ /* Double buffer management */ ++ src_buf = mchan->sram_buf + ++ ((mdma_wrote / mchan->sram_period) & 0x1) * ++ mchan->sram_period; ++ dst_buf = sg_dma_address(&sg_req->stm32_sgl_req) + mdma_wrote; ++ ++ desc = ddev->device_prep_dma_memcpy(mchan->chan, ++ dst_buf, src_buf, ++ dma_to_write, ++ DMA_PREP_INTERRUPT); ++ if (!desc) ++ return -EINVAL; ++ ++ ret = dma_submit_error(dmaengine_submit(desc)); ++ if (ret < 0) ++ return ret; ++ ++ status = dma_wait_for_async_tx(desc); ++ if (status != DMA_COMPLETE) { ++ dev_err(chan2dev(chan), "flush() dma_wait_for_async_tx error\n"); ++ dmaengine_terminate_async(mchan->chan); ++ return -EBUSY; ++ } ++ ++ /* We need to store residue for tx_status() */ ++ chan->residue_after_drain = len - (mdma_wrote + dma_to_write); ++ } ++ ++ return 0; ++} ++ + static void stm32_dma_synchronize(struct dma_chan *c) + { + struct stm32_dma_chan *chan = to_stm32_dma_chan(c); ++ struct stm32_dma_mdma *mchan = &chan->mchan; ++ ++ if (chan->desc && chan->use_mdma && mchan->dir == DMA_DEV_TO_MEM) ++ if (stm32_dma_mdma_drain(chan)) ++ dev_err(chan2dev(chan), "%s: can't drain DMA\n", ++ __func__); + + vchan_synchronize(&chan->vchan); + } +@@ -539,62 +654,206 @@ static void stm32_dma_dump_reg(struct stm32_dma_chan *chan) + dev_dbg(chan2dev(chan), "SFCR: 0x%08x\n", sfcr); + } + +-static void stm32_dma_configure_next_sg(struct stm32_dma_chan *chan); +- +-static void stm32_dma_start_transfer(struct stm32_dma_chan *chan) ++static int stm32_dma_dummy_memcpy_xfer(struct stm32_dma_chan *chan) + { + struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan); +- struct virt_dma_desc *vdesc; ++ struct dma_device *ddev = &dmadev->ddev; ++ struct stm32_dma_chan_reg reg; ++ u8 src_buf, dst_buf; ++ dma_addr_t dma_src_buf, dma_dst_buf; ++ u32 ndtr, status; ++ int len, ret; ++ ++ ret = 0; ++ src_buf = 0; ++ len = 1; ++ ++ dma_src_buf = dma_map_single(ddev->dev, &src_buf, len, DMA_TO_DEVICE); ++ ret = dma_mapping_error(ddev->dev, dma_src_buf); ++ if (ret < 0) { ++ dev_err(chan2dev(chan), "Source buffer map failed\n"); ++ return ret; ++ } ++ ++ dma_dst_buf = dma_map_single(ddev->dev, &dst_buf, len, DMA_FROM_DEVICE); ++ ret = dma_mapping_error(ddev->dev, dma_dst_buf); ++ if (ret < 0) { ++ dev_err(chan2dev(chan), "Destination buffer map failed\n"); ++ dma_unmap_single(ddev->dev, dma_src_buf, len, DMA_TO_DEVICE); ++ return ret; ++ } ++ ++ reg.dma_scr = STM32_DMA_SCR_DIR(STM32_DMA_MEM_TO_MEM) | ++ STM32_DMA_SCR_PBURST(STM32_DMA_BURST_SINGLE) | ++ STM32_DMA_SCR_MBURST(STM32_DMA_BURST_SINGLE) | ++ STM32_DMA_SCR_MINC | ++ STM32_DMA_SCR_PINC | ++ STM32_DMA_SCR_TEIE; ++ reg.dma_spar = dma_src_buf; ++ reg.dma_sm0ar = dma_dst_buf; ++ reg.dma_sfcr = STM32_DMA_SFCR_MASK | ++ STM32_DMA_SFCR_FTH(STM32_DMA_FIFO_THRESHOLD_FULL); ++ reg.dma_sm1ar = dma_dst_buf; ++ reg.dma_sndtr = 1; ++ ++ stm32_dma_write(dmadev, STM32_DMA_SCR(chan->id), reg.dma_scr); ++ stm32_dma_write(dmadev, STM32_DMA_SPAR(chan->id), reg.dma_spar); ++ stm32_dma_write(dmadev, STM32_DMA_SM0AR(chan->id), reg.dma_sm0ar); ++ stm32_dma_write(dmadev, STM32_DMA_SFCR(chan->id), reg.dma_sfcr); ++ stm32_dma_write(dmadev, STM32_DMA_SM1AR(chan->id), reg.dma_sm1ar); ++ stm32_dma_write(dmadev, STM32_DMA_SNDTR(chan->id), reg.dma_sndtr); ++ ++ /* Clear interrupt status if it is there */ ++ status = stm32_dma_irq_status(chan); ++ if (status) ++ stm32_dma_irq_clear(chan, status); ++ ++ stm32_dma_dump_reg(chan); ++ ++ chan->busy = true; ++ /* Start DMA */ ++ reg.dma_scr |= STM32_DMA_SCR_EN; ++ stm32_dma_write(dmadev, STM32_DMA_SCR(chan->id), reg.dma_scr); ++ ++ ret = readl_relaxed_poll_timeout_atomic(dmadev->base + ++ STM32_DMA_SNDTR(chan->id), ++ ndtr, !ndtr, 10, 1000); ++ if (ret) { ++ dev_err(chan2dev(chan), "%s: timeout!\n", __func__); ++ ret = -EBUSY; ++ } ++ ++ chan->busy = false; ++ ++ ret = stm32_dma_disable_chan(chan); ++ status = stm32_dma_irq_status(chan); ++ if (status) ++ stm32_dma_irq_clear(chan, status); ++ ++ dma_unmap_single(ddev->dev, dma_src_buf, len, DMA_TO_DEVICE); ++ dma_unmap_single(ddev->dev, dma_dst_buf, len, DMA_FROM_DEVICE); ++ ++ return ret; ++} ++ ++static int stm32_dma_mdma_flush_remaining(struct stm32_dma_chan *chan) ++{ ++ struct stm32_dma_mdma *mchan = &chan->mchan; + struct stm32_dma_sg_req *sg_req; +- struct stm32_dma_chan_reg *reg; +- u32 status; ++ struct dma_device *ddev = mchan->chan->device; ++ struct dma_async_tx_descriptor *desc = NULL; ++ enum dma_status status; ++ dma_addr_t src_buf, dst_buf; ++ u32 residue, remain, len; + int ret; + +- ret = stm32_dma_disable_chan(chan); +- if (ret < 0) +- return; ++ sg_req = &chan->desc->sg_req[chan->next_sg - 1]; + +- if (!chan->desc) { +- vdesc = vchan_next_desc(&chan->vchan); +- if (!vdesc) +- return; ++ residue = stm32_dma_get_remaining_bytes(chan); ++ len = sg_dma_len(&sg_req->stm32_sgl_req); ++ remain = len % mchan->sram_period; + +- chan->desc = to_stm32_dma_desc(vdesc); +- chan->next_sg = 0; ++ if (residue > 0 && len > mchan->sram_period && ++ ((len % mchan->sram_period) != 0)) { ++ unsigned long dma_sync_wait_timeout = ++ jiffies + msecs_to_jiffies(5000); ++ ++ while (residue > 0 && ++ residue > (mchan->sram_period - remain)) { ++ if (time_after_eq(jiffies, dma_sync_wait_timeout)) { ++ dev_err(chan2dev(chan), ++ "%s timeout waiting for last bytes\n", ++ __func__); ++ break; ++ } ++ cpu_relax(); ++ residue = stm32_dma_get_remaining_bytes(chan); ++ } ++ stm32_dma_disable_chan(chan); ++ ++ src_buf = mchan->sram_buf + ((len / mchan->sram_period) & 0x1) ++ * mchan->sram_period; ++ dst_buf = sg_dma_address(&sg_req->stm32_sgl_req) + len - ++ (len % mchan->sram_period); ++ ++ desc = ddev->device_prep_dma_memcpy(mchan->chan, ++ dst_buf, src_buf, ++ len % mchan->sram_period, ++ DMA_PREP_INTERRUPT); ++ ++ if (!desc) ++ return -EINVAL; ++ ++ ret = dma_submit_error(dmaengine_submit(desc)); ++ if (ret < 0) ++ return ret; ++ ++ status = dma_wait_for_async_tx(desc); ++ if (status != DMA_COMPLETE) { ++ dmaengine_terminate_async(mchan->chan); ++ return -EBUSY; ++ } + } + +- if (chan->next_sg == chan->desc->num_sgs) +- chan->next_sg = 0; ++ return 0; ++} + +- sg_req = &chan->desc->sg_req[chan->next_sg]; +- reg = &sg_req->chan_reg; ++static void stm32_dma_start_transfer(struct stm32_dma_chan *chan); + +- stm32_dma_write(dmadev, STM32_DMA_SCR(chan->id), reg->dma_scr); +- stm32_dma_write(dmadev, STM32_DMA_SPAR(chan->id), reg->dma_spar); +- stm32_dma_write(dmadev, STM32_DMA_SM0AR(chan->id), reg->dma_sm0ar); +- stm32_dma_write(dmadev, STM32_DMA_SFCR(chan->id), reg->dma_sfcr); +- stm32_dma_write(dmadev, STM32_DMA_SM1AR(chan->id), reg->dma_sm1ar); +- stm32_dma_write(dmadev, STM32_DMA_SNDTR(chan->id), reg->dma_sndtr); ++static void stm32_mdma_chan_complete(void *param, ++ const struct dmaengine_result *result) ++{ ++ struct stm32_dma_chan *chan = param; + +- chan->next_sg++; ++ chan->busy = false; ++ if (result->result == DMA_TRANS_NOERROR) { ++ if (stm32_dma_mdma_flush_remaining(chan)) { ++ dev_err(chan2dev(chan), "Can't flush DMA\n"); ++ return; ++ } + +- /* Clear interrupt status if it is there */ +- status = stm32_dma_irq_status(chan); +- if (status) +- stm32_dma_irq_clear(chan, status); ++ if (chan->next_sg == chan->desc->num_sgs) { ++ list_del(&chan->desc->vdesc.node); ++ vchan_cookie_complete(&chan->desc->vdesc); ++ chan->desc = NULL; ++ } ++ stm32_dma_start_transfer(chan); ++ } else { ++ dev_err(chan2dev(chan), "MDMA transfer error: %d\n", ++ result->result); ++ } ++} + +- if (chan->desc->cyclic) +- stm32_dma_configure_next_sg(chan); ++static int stm32_dma_mdma_start(struct stm32_dma_chan *chan, ++ struct stm32_dma_sg_req *sg_req) ++{ ++ struct stm32_dma_mdma *mchan = &chan->mchan; ++ struct stm32_dma_mdma_desc *m_desc = &sg_req->m_desc; ++ int ret; + +- stm32_dma_dump_reg(chan); ++ ret = dma_submit_error(dmaengine_submit(m_desc->desc)); ++ if (ret < 0) { ++ dev_err(chan2dev(chan), "MDMA submit failed\n"); ++ goto error; ++ } + +- /* Start DMA */ +- reg->dma_scr |= STM32_DMA_SCR_EN; +- stm32_dma_write(dmadev, STM32_DMA_SCR(chan->id), reg->dma_scr); ++ dma_async_issue_pending(mchan->chan); + +- chan->busy = true; ++ /* ++ * In case of M2D transfer, we have to generate dummy DMA transfer to ++ * copy 1st sg data into SRAM ++ */ ++ if (mchan->dir == DMA_MEM_TO_DEV) { ++ ret = stm32_dma_dummy_memcpy_xfer(chan); ++ if (ret < 0) { ++ dmaengine_terminate_async(mchan->chan); ++ goto error; ++ } ++ } + +- dev_dbg(chan2dev(chan), "vchan %pK: started\n", &chan->vchan); ++ return 0; ++error: ++ return ret; + } + + static void stm32_dma_configure_next_sg(struct stm32_dma_chan *chan) +@@ -626,35 +885,146 @@ static void stm32_dma_configure_next_sg(struct stm32_dma_chan *chan) + } + } + +-static void stm32_dma_handle_chan_done(struct stm32_dma_chan *chan) ++static void stm32_dma_start_transfer(struct stm32_dma_chan *chan) + { +- if (chan->desc) { +- if (chan->desc->cyclic) { +- vchan_cyclic_callback(&chan->desc->vdesc); +- chan->next_sg++; +- stm32_dma_configure_next_sg(chan); ++ struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan); ++ struct virt_dma_desc *vdesc; ++ struct stm32_dma_sg_req *sg_req; ++ struct stm32_dma_chan_reg *reg; ++ u32 status; ++ int ret; ++ ++ ret = stm32_dma_disable_chan(chan); ++ if (ret < 0) ++ return; ++ ++ if (!chan->desc) { ++ vdesc = vchan_next_desc(&chan->vchan); ++ if (!vdesc) ++ return; ++ ++ chan->desc = to_stm32_dma_desc(vdesc); ++ chan->next_sg = 0; ++ } else { ++ vdesc = &chan->desc->vdesc; ++ } ++ ++ if (chan->next_sg == chan->desc->num_sgs) ++ chan->next_sg = 0; ++ ++ sg_req = &chan->desc->sg_req[chan->next_sg]; ++ reg = &sg_req->chan_reg; ++ ++ /* Clear interrupt status if it is there */ ++ status = stm32_dma_irq_status(chan); ++ if (status) ++ stm32_dma_irq_clear(chan, status); ++ ++ if (chan->use_mdma) { ++ if (chan->next_sg == 0) { ++ struct stm32_dma_mdma_desc *m_desc; ++ ++ m_desc = &sg_req->m_desc; ++ if (chan->desc->cyclic) { ++ /* ++ * If one callback is set, it will be called by ++ * MDMA driver. ++ */ ++ if (vdesc->tx.callback) { ++ m_desc->desc->callback = ++ vdesc->tx.callback; ++ m_desc->desc->callback_param = ++ vdesc->tx.callback_param; ++ vdesc->tx.callback = NULL; ++ vdesc->tx.callback_param = NULL; ++ } ++ } ++ } ++ ++ if (chan->mchan.dir == DMA_MEM_TO_DEV) { ++ ret = stm32_dma_dummy_memcpy_xfer(chan); ++ if (ret < 0) { ++ dmaengine_terminate_async(chan->mchan.chan); ++ chan->desc = NULL; ++ return; ++ } + } else { +- chan->busy = false; +- if (chan->next_sg == chan->desc->num_sgs) { +- list_del(&chan->desc->vdesc.node); +- vchan_cookie_complete(&chan->desc->vdesc); ++ reg->dma_scr &= ~STM32_DMA_SCR_TCIE; ++ } ++ ++ if (!chan->desc->cyclic) { ++ /* MDMA already started */ ++ if (chan->mchan.dir != DMA_MEM_TO_DEV && ++ sg_dma_len(&sg_req->stm32_sgl_req) > ++ chan->mchan.sram_period) ++ reg->dma_scr |= STM32_DMA_SCR_DBM; ++ ret = stm32_dma_mdma_start(chan, sg_req); ++ if (ret < 0) { + chan->desc = NULL; ++ return; + } +- stm32_dma_start_transfer(chan); + } + } ++ ++ chan->next_sg++; ++ ++ reg->dma_scr &= ~STM32_DMA_SCR_EN; ++ stm32_dma_write(dmadev, STM32_DMA_SCR(chan->id), reg->dma_scr); ++ stm32_dma_write(dmadev, STM32_DMA_SPAR(chan->id), reg->dma_spar); ++ stm32_dma_write(dmadev, STM32_DMA_SM0AR(chan->id), reg->dma_sm0ar); ++ stm32_dma_write(dmadev, STM32_DMA_SFCR(chan->id), reg->dma_sfcr); ++ stm32_dma_write(dmadev, STM32_DMA_SM1AR(chan->id), reg->dma_sm1ar); ++ stm32_dma_write(dmadev, STM32_DMA_SNDTR(chan->id), reg->dma_sndtr); ++ ++ if (chan->desc->cyclic) ++ stm32_dma_configure_next_sg(chan); ++ ++ stm32_dma_dump_reg(chan); ++ ++ /* Start DMA */ ++ chan->busy = true; ++ reg->dma_scr |= STM32_DMA_SCR_EN; ++ stm32_dma_write(dmadev, STM32_DMA_SCR(chan->id), reg->dma_scr); ++ ++ dev_dbg(chan2dev(chan), "vchan %pK: started\n", &chan->vchan); ++} ++ ++static void stm32_dma_handle_chan_done(struct stm32_dma_chan *chan) ++{ ++ if (!chan->desc) ++ return; ++ ++ if (chan->desc->cyclic) { ++ vchan_cyclic_callback(&chan->desc->vdesc); ++ if (chan->use_mdma) ++ return; ++ chan->next_sg++; ++ stm32_dma_configure_next_sg(chan); ++ } else { ++ chan->busy = false; ++ if (chan->use_mdma && chan->mchan.dir != DMA_MEM_TO_DEV) ++ return; ++ if (chan->next_sg == chan->desc->num_sgs) { ++ list_del(&chan->desc->vdesc.node); ++ vchan_cookie_complete(&chan->desc->vdesc); ++ chan->desc = NULL; ++ } ++ ++ stm32_dma_start_transfer(chan); ++ } + } + + static irqreturn_t stm32_dma_chan_irq(int irq, void *devid) + { + struct stm32_dma_chan *chan = devid; + struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan); +- u32 status, scr; ++ u32 status, scr, sfcr; + + spin_lock(&chan->vchan.lock); + + status = stm32_dma_irq_status(chan); + scr = stm32_dma_read(dmadev, STM32_DMA_SCR(chan->id)); ++ sfcr = stm32_dma_read(dmadev, STM32_DMA_SFCR(chan->id)); + + if (status & STM32_DMA_TCI) { + stm32_dma_irq_clear(chan, STM32_DMA_TCI); +@@ -669,10 +1039,12 @@ static irqreturn_t stm32_dma_chan_irq(int irq, void *devid) + if (status & STM32_DMA_FEI) { + stm32_dma_irq_clear(chan, STM32_DMA_FEI); + status &= ~STM32_DMA_FEI; +- if (!(scr & STM32_DMA_SCR_EN)) +- dev_err(chan2dev(chan), "FIFO Error\n"); +- else +- dev_dbg(chan2dev(chan), "FIFO over/underrun\n"); ++ if (sfcr & STM32_DMA_SFCR_FEIE) { ++ if (!(scr & STM32_DMA_SCR_EN)) ++ dev_err(chan2dev(chan), "FIFO Error\n"); ++ else ++ dev_dbg(chan2dev(chan), "FIFO over/underrun\n"); ++ } + } + if (status) { + stm32_dma_irq_clear(chan, status); +@@ -695,7 +1067,6 @@ static void stm32_dma_issue_pending(struct dma_chan *c) + if (vchan_issue_pending(&chan->vchan) && !chan->desc && !chan->busy) { + dev_dbg(chan2dev(chan), "vchan %pK: issued\n", &chan->vchan); + stm32_dma_start_transfer(chan); +- + } + spin_unlock_irqrestore(&chan->vchan.lock, flags); + } +@@ -836,16 +1207,167 @@ static void stm32_dma_clear_reg(struct stm32_dma_chan_reg *regs) + memset(regs, 0, sizeof(struct stm32_dma_chan_reg)); + } + ++static int stm32_dma_mdma_prep_slave_sg(struct stm32_dma_chan *chan, ++ struct scatterlist *sgl, u32 sg_len, ++ struct stm32_dma_desc *desc, ++ unsigned long flags) ++{ ++ struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan); ++ struct stm32_dma_mdma *mchan = &chan->mchan; ++ struct scatterlist *sg, *m_sg; ++ dma_addr_t dma_buf; ++ u32 len, num_sgs, sram_period; ++ int i, j, ret; ++ ++ desc->dma_buf_cpu = gen_pool_dma_alloc(dmadev->sram_pool, ++ chan->sram_size, ++ &desc->dma_buf); ++ if (!desc->dma_buf_cpu) ++ return -ENOMEM; ++ ++ sram_period = chan->sram_size / 2; ++ ++ for_each_sg(sgl, sg, sg_len, i) { ++ struct stm32_dma_mdma_desc *m_desc = &desc->sg_req[i].m_desc; ++ struct dma_slave_config config; ++ ++ len = sg_dma_len(sg); ++ desc->sg_req[i].stm32_sgl_req = *sg; ++ num_sgs = 1; ++ ++ if (mchan->dir == DMA_MEM_TO_DEV) { ++ if (len > chan->sram_size) { ++ dev_err(chan2dev(chan), ++ "max buf size = %d bytes\n", ++ chan->sram_size); ++ goto free_alloc; ++ } ++ } else { ++ /* ++ * Build new sg for MDMA transfer ++ * Scatter DMA Req into several SDRAM transfer ++ */ ++ if (len > sram_period) ++ num_sgs = len / sram_period; ++ } ++ ++ ret = sg_alloc_table(&m_desc->sgt, num_sgs, GFP_ATOMIC); ++ if (ret) { ++ dev_err(chan2dev(chan), "MDMA sg table alloc failed\n"); ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ dma_buf = sg_dma_address(sg); ++ for_each_sg(m_desc->sgt.sgl, m_sg, num_sgs, j) { ++ size_t bytes = min_t(size_t, len, sram_period); ++ ++ sg_dma_address(m_sg) = dma_buf; ++ sg_dma_len(m_sg) = bytes; ++ dma_buf += bytes; ++ len -= bytes; ++ } ++ ++ /* Configure MDMA channel */ ++ memset(&config, 0, sizeof(config)); ++ if (mchan->dir == DMA_MEM_TO_DEV) ++ config.dst_addr = desc->dma_buf; ++ else ++ config.src_addr = desc->dma_buf; ++ ++ ret = dmaengine_slave_config(mchan->chan, &config); ++ if (ret < 0) ++ goto err; ++ ++ /* Prepare MDMA descriptor */ ++ m_desc->desc = dmaengine_prep_slave_sg(mchan->chan, ++ m_desc->sgt.sgl, ++ m_desc->sgt.nents, ++ mchan->dir, ++ DMA_PREP_INTERRUPT); ++ ++ if (!m_desc->desc) { ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ if (flags & DMA_CTRL_REUSE) ++ dmaengine_desc_set_reuse(m_desc->desc); ++ ++ if (mchan->dir != DMA_MEM_TO_DEV) { ++ m_desc->desc->callback_result = ++ stm32_mdma_chan_complete; ++ m_desc->desc->callback_param = chan; ++ } ++ } ++ ++ chan->mchan.sram_buf = desc->dma_buf; ++ chan->mchan.sram_period = sram_period; ++ chan->mchan.num_sgs = num_sgs; ++ ++ return 0; ++ ++err: ++ for (j = 0; j < i; j++) { ++ struct stm32_dma_mdma_desc *m_desc = &desc->sg_req[j].m_desc; ++ ++ m_desc->desc = NULL; ++ sg_free_table(&desc->sg_req[j].m_desc.sgt); ++ } ++free_alloc: ++ gen_pool_free(dmadev->sram_pool, (unsigned long)desc->dma_buf_cpu, ++ chan->sram_size); ++ return ret; ++} ++ ++static int stm32_dma_setup_sg_requests(struct stm32_dma_chan *chan, ++ struct scatterlist *sgl, ++ unsigned int sg_len, ++ enum dma_transfer_direction direction, ++ struct stm32_dma_desc *desc) ++{ ++ struct scatterlist *sg; ++ u32 nb_data_items; ++ int i, ret; ++ enum dma_slave_buswidth buswidth; ++ ++ for_each_sg(sgl, sg, sg_len, i) { ++ ret = stm32_dma_set_xfer_param(chan, direction, &buswidth, ++ sg_dma_len(sg)); ++ if (ret < 0) ++ return ret; ++ ++ nb_data_items = sg_dma_len(sg) / buswidth; ++ if (nb_data_items > STM32_DMA_ALIGNED_MAX_DATA_ITEMS) { ++ dev_err(chan2dev(chan), "nb items not supported\n"); ++ return -EINVAL; ++ } ++ ++ stm32_dma_clear_reg(&desc->sg_req[i].chan_reg); ++ desc->sg_req[i].chan_reg.dma_scr = chan->chan_reg.dma_scr; ++ desc->sg_req[i].chan_reg.dma_sfcr = chan->chan_reg.dma_sfcr; ++ desc->sg_req[i].chan_reg.dma_spar = chan->chan_reg.dma_spar; ++ desc->sg_req[i].chan_reg.dma_sm0ar = sg_dma_address(sg); ++ desc->sg_req[i].chan_reg.dma_sm1ar = sg_dma_address(sg); ++ if (chan->use_mdma) ++ desc->sg_req[i].chan_reg.dma_sm1ar += ++ chan->mchan.sram_period; ++ desc->sg_req[i].chan_reg.dma_sndtr = nb_data_items; ++ } ++ ++ desc->num_sgs = sg_len; ++ ++ return 0; ++} ++ + static struct dma_async_tx_descriptor *stm32_dma_prep_slave_sg( + struct dma_chan *c, struct scatterlist *sgl, + u32 sg_len, enum dma_transfer_direction direction, + unsigned long flags, void *context) + { + struct stm32_dma_chan *chan = to_stm32_dma_chan(c); ++ + struct stm32_dma_desc *desc; +- struct scatterlist *sg; +- enum dma_slave_buswidth buswidth; +- u32 nb_data_items; + int i, ret; + + if (!chan->config_init) { +@@ -868,48 +1390,142 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_slave_sg( + else + chan->chan_reg.dma_scr &= ~STM32_DMA_SCR_PFCTRL; + +- for_each_sg(sgl, sg, sg_len, i) { +- ret = stm32_dma_set_xfer_param(chan, direction, &buswidth, +- sg_dma_len(sg)); +- if (ret < 0) +- goto err; ++ if (chan->use_mdma) { ++ struct sg_table new_sgt; ++ struct scatterlist *s, *_sgl; + +- desc->sg_req[i].len = sg_dma_len(sg); +- +- nb_data_items = desc->sg_req[i].len / buswidth; +- if (nb_data_items > STM32_DMA_ALIGNED_MAX_DATA_ITEMS) { +- dev_err(chan2dev(chan), "nb items not supported\n"); +- goto err; ++ chan->mchan.dir = direction; ++ ret = stm32_dma_mdma_prep_slave_sg(chan, sgl, sg_len, desc, ++ flags); ++ if (ret < 0) ++ return NULL; ++ ++ ret = sg_alloc_table(&new_sgt, sg_len, GFP_ATOMIC); ++ if (ret) ++ dev_err(chan2dev(chan), "DMA sg table alloc failed\n"); ++ ++ for_each_sg(new_sgt.sgl, s, sg_len, i) { ++ _sgl = sgl; ++ sg_dma_len(s) = ++ min(sg_dma_len(_sgl), chan->mchan.sram_period); ++ s->dma_address = chan->mchan.sram_buf; ++ _sgl = sg_next(_sgl); + } + +- stm32_dma_clear_reg(&desc->sg_req[i].chan_reg); +- desc->sg_req[i].chan_reg.dma_scr = chan->chan_reg.dma_scr; +- desc->sg_req[i].chan_reg.dma_sfcr = chan->chan_reg.dma_sfcr; +- desc->sg_req[i].chan_reg.dma_spar = chan->chan_reg.dma_spar; +- desc->sg_req[i].chan_reg.dma_sm0ar = sg_dma_address(sg); +- desc->sg_req[i].chan_reg.dma_sm1ar = sg_dma_address(sg); +- desc->sg_req[i].chan_reg.dma_sndtr = nb_data_items; ++ ret = stm32_dma_setup_sg_requests(chan, new_sgt.sgl, sg_len, ++ direction, desc); ++ sg_free_table(&new_sgt); ++ if (ret < 0) ++ goto err; ++ } else { ++ /* Prepare a normal DMA transfer */ ++ ret = stm32_dma_setup_sg_requests(chan, sgl, sg_len, direction, ++ desc); ++ if (ret < 0) ++ goto err; + } + +- desc->num_sgs = sg_len; + desc->cyclic = false; + + return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); +- + err: ++ if (chan->use_mdma) { ++ struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan); ++ ++ for (i = 0; i < sg_len; i++) ++ sg_free_table(&desc->sg_req[i].m_desc.sgt); ++ ++ gen_pool_free(dmadev->sram_pool, ++ (unsigned long)desc->dma_buf_cpu, ++ chan->sram_size); ++ } + kfree(desc); ++ + return NULL; + } + ++static int stm32_dma_mdma_prep_dma_cyclic(struct stm32_dma_chan *chan, ++ dma_addr_t buf_addr, size_t buf_len, ++ size_t period_len, ++ struct stm32_dma_desc *desc) ++{ ++ struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan); ++ struct stm32_dma_mdma *mchan = &chan->mchan; ++ struct stm32_dma_mdma_desc *m_desc = &desc->sg_req[0].m_desc; ++ struct dma_slave_config config; ++ dma_addr_t mem; ++ int ret; ++ ++ chan->sram_size = ALIGN(period_len, STM32_DMA_SRAM_GRANULARITY); ++ desc->dma_buf_cpu = gen_pool_dma_alloc(dmadev->sram_pool, ++ 2 * chan->sram_size, ++ &desc->dma_buf); ++ if (!desc->dma_buf_cpu) ++ return -ENOMEM; ++ ++ memset(&config, 0, sizeof(config)); ++ mem = buf_addr; ++ ++ /* Configure MDMA channel */ ++ if (chan->mchan.dir == DMA_MEM_TO_DEV) ++ config.dst_addr = desc->dma_buf; ++ else ++ config.src_addr = desc->dma_buf; ++ ret = dmaengine_slave_config(mchan->chan, &config); ++ if (ret < 0) ++ goto err; ++ ++ /* Prepare MDMA descriptor */ ++ m_desc->desc = dmaengine_prep_dma_cyclic(mchan->chan, buf_addr, buf_len, ++ period_len, chan->mchan.dir, ++ DMA_PREP_INTERRUPT); ++ ++ if (!m_desc->desc) { ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ ret = dma_submit_error(dmaengine_submit(m_desc->desc)); ++ if (ret < 0) { ++ dev_err(chan2dev(chan), "MDMA submit failed\n"); ++ goto err; ++ } ++ ++ dma_async_issue_pending(mchan->chan); ++ ++ /* ++ * In case of M2D transfer, we have to generate dummy DMA transfer to ++ * copy 1 period of data into SRAM ++ */ ++ if (chan->mchan.dir == DMA_MEM_TO_DEV) { ++ ret = stm32_dma_dummy_memcpy_xfer(chan); ++ if (ret < 0) { ++ dev_err(chan2dev(chan), ++ "stm32_dma_dummy_memcpy_xfer failed\n"); ++ dmaengine_terminate_async(mchan->chan); ++ goto err; ++ } ++ } ++ ++ return 0; ++err: ++ gen_pool_free(dmadev->sram_pool, ++ (unsigned long)desc->dma_buf_cpu, ++ chan->sram_size); ++ return ret; ++} ++ + static struct dma_async_tx_descriptor *stm32_dma_prep_dma_cyclic( + struct dma_chan *c, dma_addr_t buf_addr, size_t buf_len, + size_t period_len, enum dma_transfer_direction direction, + unsigned long flags) + { + struct stm32_dma_chan *chan = to_stm32_dma_chan(c); ++ struct stm32_dma_chan_reg *chan_reg = &chan->chan_reg; + struct stm32_dma_desc *desc; + enum dma_slave_buswidth buswidth; + u32 num_periods, nb_data_items; ++ dma_addr_t dma_buf = 0; + int i, ret; + + if (!buf_len || !period_len) { +@@ -957,28 +1573,49 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_cyclic( + /* Clear periph ctrl if client set it */ + chan->chan_reg.dma_scr &= ~STM32_DMA_SCR_PFCTRL; + +- num_periods = buf_len / period_len; ++ if (chan->use_mdma) ++ num_periods = 1; ++ else ++ num_periods = buf_len / period_len; + + desc = stm32_dma_alloc_desc(num_periods); + if (!desc) + return NULL; + +- for (i = 0; i < num_periods; i++) { +- desc->sg_req[i].len = period_len; ++ desc->num_sgs = num_periods; ++ desc->cyclic = true; ++ ++ if (chan->use_mdma) { ++ chan->mchan.dir = direction; ++ ++ ret = stm32_dma_mdma_prep_dma_cyclic(chan, buf_addr, buf_len, ++ period_len, desc); ++ if (ret < 0) ++ return NULL; ++ dma_buf = desc->dma_buf; ++ } else { ++ dma_buf = buf_addr; ++ } + ++ for (i = 0; i < num_periods; i++) { ++ sg_dma_len(&desc->sg_req[i].stm32_sgl_req) = period_len; ++ sg_dma_address(&desc->sg_req[i].stm32_sgl_req) = dma_buf; + stm32_dma_clear_reg(&desc->sg_req[i].chan_reg); +- desc->sg_req[i].chan_reg.dma_scr = chan->chan_reg.dma_scr; +- desc->sg_req[i].chan_reg.dma_sfcr = chan->chan_reg.dma_sfcr; +- desc->sg_req[i].chan_reg.dma_spar = chan->chan_reg.dma_spar; +- desc->sg_req[i].chan_reg.dma_sm0ar = buf_addr; +- desc->sg_req[i].chan_reg.dma_sm1ar = buf_addr; ++ desc->sg_req[i].chan_reg.dma_scr = chan_reg->dma_scr; ++ desc->sg_req[i].chan_reg.dma_sfcr = chan_reg->dma_sfcr; ++ desc->sg_req[i].chan_reg.dma_spar = chan_reg->dma_spar; ++ if (chan->use_mdma) { ++ desc->sg_req[i].chan_reg.dma_sm0ar = desc->dma_buf; ++ desc->sg_req[i].chan_reg.dma_sm1ar = desc->dma_buf + ++ chan->sram_size; ++ } else { ++ desc->sg_req[i].chan_reg.dma_sm0ar = dma_buf; ++ desc->sg_req[i].chan_reg.dma_sm1ar = dma_buf; ++ dma_buf += period_len; ++ } + desc->sg_req[i].chan_reg.dma_sndtr = nb_data_items; +- buf_addr += period_len; + } + +- desc->num_sgs = num_periods; +- desc->cyclic = true; +- + return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); + } + +@@ -1019,13 +1656,13 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_memcpy( + STM32_DMA_SCR_PINC | + STM32_DMA_SCR_TCIE | + STM32_DMA_SCR_TEIE; +- desc->sg_req[i].chan_reg.dma_sfcr |= STM32_DMA_SFCR_MASK; ++ desc->sg_req[i].chan_reg.dma_sfcr &= ~STM32_DMA_SFCR_MASK; + desc->sg_req[i].chan_reg.dma_sfcr |= + STM32_DMA_SFCR_FTH(threshold); + desc->sg_req[i].chan_reg.dma_spar = src + offset; + desc->sg_req[i].chan_reg.dma_sm0ar = dest + offset; + desc->sg_req[i].chan_reg.dma_sndtr = xfer_count; +- desc->sg_req[i].len = xfer_count; ++ sg_dma_len(&desc->sg_req[i].stm32_sgl_req) = xfer_count; + } + + desc->num_sgs = num_sgs; +@@ -1034,18 +1671,6 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_memcpy( + return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); + } + +-static u32 stm32_dma_get_remaining_bytes(struct stm32_dma_chan *chan) +-{ +- u32 dma_scr, width, ndtr; +- struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan); +- +- dma_scr = stm32_dma_read(dmadev, STM32_DMA_SCR(chan->id)); +- width = STM32_DMA_SCR_PSIZE_GET(dma_scr); +- ndtr = stm32_dma_read(dmadev, STM32_DMA_SNDTR(chan->id)); +- +- return ndtr << width; +-} +- + static size_t stm32_dma_desc_residue(struct stm32_dma_chan *chan, + struct stm32_dma_desc *desc, + u32 next_sg) +@@ -1054,6 +1679,10 @@ static size_t stm32_dma_desc_residue(struct stm32_dma_chan *chan, + u32 residue = 0; + int i; + ++ /* Drain case */ ++ if (chan->residue_after_drain) ++ return chan->residue_after_drain; ++ + /* + * In cyclic mode, for the last period, residue = remaining bytes from + * NDTR +@@ -1069,7 +1698,7 @@ static size_t stm32_dma_desc_residue(struct stm32_dma_chan *chan, + * transferred + */ + for (i = next_sg; i < desc->num_sgs; i++) +- residue += desc->sg_req[i].len; ++ residue += sg_dma_len(&desc->sg_req[i].stm32_sgl_req); + residue += stm32_dma_get_remaining_bytes(chan); + + end: +@@ -1089,11 +1718,23 @@ static enum dma_status stm32_dma_tx_status(struct dma_chan *c, + struct dma_tx_state *state) + { + struct stm32_dma_chan *chan = to_stm32_dma_chan(c); ++ struct stm32_dma_mdma *mchan = &chan->mchan; + struct virt_dma_desc *vdesc; + enum dma_status status; + unsigned long flags; + u32 residue = 0; + ++ /* ++ * When DMA/MDMA chain is used, we return the status of MDMA in cyclic ++ * mode and for D2M transfer in sg mode in order to return the correct ++ * residue if any ++ */ ++ if (chan->desc && chan->use_mdma && ++ (mchan->dir != DMA_MEM_TO_DEV || chan->desc->cyclic) && ++ !chan->residue_after_drain) ++ return dmaengine_tx_status(mchan->chan, mchan->chan->cookie, ++ state); ++ + status = dma_cookie_status(c, cookie, state); + if (status == DMA_COMPLETE || !state) + return status; +@@ -1120,15 +1761,14 @@ static int stm32_dma_alloc_chan_resources(struct dma_chan *c) + int ret; + + chan->config_init = false; +- ret = clk_prepare_enable(dmadev->clk); +- if (ret < 0) { +- dev_err(chan2dev(chan), "clk_prepare_enable failed: %d\n", ret); ++ ++ ret = pm_runtime_get_sync(dmadev->ddev.dev); ++ if (ret < 0) + return ret; +- } + + ret = stm32_dma_disable_chan(chan); + if (ret < 0) +- clk_disable_unprepare(dmadev->clk); ++ pm_runtime_put(dmadev->ddev.dev); + + return ret; + } +@@ -1148,28 +1788,48 @@ static void stm32_dma_free_chan_resources(struct dma_chan *c) + spin_unlock_irqrestore(&chan->vchan.lock, flags); + } + +- clk_disable_unprepare(dmadev->clk); ++ pm_runtime_put(dmadev->ddev.dev); + + vchan_free_chan_resources(to_virt_chan(c)); + } + + static void stm32_dma_desc_free(struct virt_dma_desc *vdesc) + { +- kfree(container_of(vdesc, struct stm32_dma_desc, vdesc)); ++ struct stm32_dma_desc *desc = to_stm32_dma_desc(vdesc); ++ struct stm32_dma_chan *chan = to_stm32_dma_chan(vdesc->tx.chan); ++ struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan); ++ int i; ++ ++ if (chan->use_mdma) { ++ struct stm32_dma_mdma_desc *m_desc; ++ ++ for (i = 0; i < desc->num_sgs; i++) { ++ m_desc = &desc->sg_req[i].m_desc; ++ if (dmaengine_desc_test_reuse(&vdesc->tx)) ++ dmaengine_desc_free(m_desc->desc); ++ m_desc->desc = NULL; ++ sg_free_table(&m_desc->sgt); ++ } ++ ++ gen_pool_free(dmadev->sram_pool, ++ (unsigned long)desc->dma_buf_cpu, ++ chan->sram_size); ++ } ++ ++ kfree(desc); + } + + static void stm32_dma_set_config(struct stm32_dma_chan *chan, + struct stm32_dma_cfg *cfg) + { + stm32_dma_clear_reg(&chan->chan_reg); +- + chan->chan_reg.dma_scr = cfg->stream_config & STM32_DMA_SCR_CFG_MASK; + chan->chan_reg.dma_scr |= STM32_DMA_SCR_REQ(cfg->request_line); +- +- /* Enable Interrupts */ + chan->chan_reg.dma_scr |= STM32_DMA_SCR_TEIE | STM32_DMA_SCR_TCIE; +- + chan->threshold = STM32_DMA_THRESHOLD_FTR_GET(cfg->features); ++ chan->use_mdma = STM32_DMA_MDMA_CHAIN_FTR_GET(cfg->features); ++ chan->sram_size = (1 << STM32_DMA_MDMA_SRAM_SIZE_GET(cfg->features)) * ++ STM32_DMA_SRAM_GRANULARITY; + } + + static struct dma_chan *stm32_dma_of_xlate(struct of_phandle_args *dma_spec, +@@ -1207,6 +1867,9 @@ static struct dma_chan *stm32_dma_of_xlate(struct of_phandle_args *dma_spec, + + stm32_dma_set_config(chan, &cfg); + ++ if (!dmadev->sram_pool || !chan->mchan.chan) ++ chan->use_mdma = 0; ++ + return c; + } + +@@ -1219,10 +1882,12 @@ MODULE_DEVICE_TABLE(of, stm32_dma_of_match); + static int stm32_dma_probe(struct platform_device *pdev) + { + struct stm32_dma_chan *chan; ++ struct stm32_dma_mdma *mchan; + struct stm32_dma_device *dmadev; + struct dma_device *dd; + const struct of_device_id *match; + struct resource *res; ++ char name[4]; + int i, ret; + + match = of_match_device(stm32_dma_of_match, &pdev->dev); +@@ -1248,6 +1913,12 @@ static int stm32_dma_probe(struct platform_device *pdev) + return PTR_ERR(dmadev->clk); + } + ++ ret = clk_prepare_enable(dmadev->clk); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "clk_prep_enable error: %d\n", ret); ++ return ret; ++ } ++ + dmadev->mem2mem = of_property_read_bool(pdev->dev.of_node, + "st,mem2mem"); + +@@ -1258,6 +1929,13 @@ static int stm32_dma_probe(struct platform_device *pdev) + reset_control_deassert(dmadev->rst); + } + ++ dmadev->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0); ++ if (!dmadev->sram_pool) ++ dev_info(&pdev->dev, "no dma pool: can't use MDMA: %d\n", ret); ++ else ++ dev_dbg(&pdev->dev, "SRAM pool: %zu KiB\n", ++ gen_pool_size(dmadev->sram_pool) / 1024); ++ + dma_cap_set(DMA_SLAVE, dd->cap_mask); + dma_cap_set(DMA_PRIVATE, dd->cap_mask); + dma_cap_set(DMA_CYCLIC, dd->cap_mask); +@@ -1279,6 +1957,7 @@ static int stm32_dma_probe(struct platform_device *pdev) + dd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + dd->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; + dd->max_burst = STM32_DMA_MAX_BURST; ++ dd->descriptor_reuse = true; + dd->dev = &pdev->dev; + INIT_LIST_HEAD(&dd->channels); + +@@ -1293,11 +1972,21 @@ static int stm32_dma_probe(struct platform_device *pdev) + chan->id = i; + chan->vchan.desc_free = stm32_dma_desc_free; + vchan_init(&chan->vchan, dd); ++ ++ mchan = &chan->mchan; ++ if (dmadev->sram_pool) { ++ snprintf(name, sizeof(name), "ch%d", chan->id); ++ mchan->chan = dma_request_slave_channel(dd->dev, name); ++ if (!mchan->chan) ++ dev_info(&pdev->dev, ++ "can't request MDMA chan for %s\n", ++ name); ++ } + } + + ret = dma_async_device_register(dd); + if (ret) +- return ret; ++ goto clk_free; + + for (i = 0; i < STM32_DMA_MAX_CHANNELS; i++) { + chan = &dmadev->chan[i]; +@@ -1329,20 +2018,95 @@ static int stm32_dma_probe(struct platform_device *pdev) + + platform_set_drvdata(pdev, dmadev); + ++ pm_runtime_set_active(&pdev->dev); ++ pm_runtime_enable(&pdev->dev); ++ pm_runtime_get_noresume(&pdev->dev); ++ pm_runtime_put(&pdev->dev); ++ + dev_info(&pdev->dev, "STM32 DMA driver registered\n"); + + return 0; + + err_unregister: + dma_async_device_unregister(dd); ++clk_free: ++ clk_disable_unprepare(dmadev->clk); + + return ret; + } + ++#ifdef CONFIG_PM ++static int stm32_dma_runtime_suspend(struct device *dev) ++{ ++ struct stm32_dma_device *dmadev = dev_get_drvdata(dev); ++ ++ clk_disable_unprepare(dmadev->clk); ++ ++ return 0; ++} ++ ++static int stm32_dma_runtime_resume(struct device *dev) ++{ ++ struct stm32_dma_device *dmadev = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = clk_prepare_enable(dmadev->clk); ++ if (ret) { ++ dev_err(dev, "failed to prepare_enable clock\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++#endif ++ ++#ifdef CONFIG_PM_SLEEP ++static int stm32_dma_suspend(struct device *dev) ++{ ++ struct stm32_dma_device *dmadev = dev_get_drvdata(dev); ++ int id, ret, scr; ++ ++ ret = pm_runtime_get_sync(dev); ++ if (ret < 0) ++ return ret; ++ ++ for (id = 0; id < STM32_DMA_MAX_CHANNELS; id++) { ++ scr = stm32_dma_read(dmadev, STM32_DMA_SCR(id)); ++ if (scr & STM32_DMA_SCR_EN) { ++ dev_warn(dev, "Suspend is prevented by Chan %i\n", id); ++ return -EBUSY; ++ } ++ } ++ ++ pm_runtime_put_sync(dev); ++ ++ pm_runtime_force_suspend(dev); ++ return 0; ++} ++ ++static int stm32_dma_resume(struct device *dev) ++{ ++ int ret; ++ ++ ret = pm_runtime_force_resume(dev); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++#endif ++ ++static const struct dev_pm_ops stm32_dma_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(stm32_dma_suspend, stm32_dma_resume) ++ SET_RUNTIME_PM_OPS(stm32_dma_runtime_suspend, ++ stm32_dma_runtime_resume, NULL) ++}; ++ + static struct platform_driver stm32_dma_driver = { + .driver = { + .name = "stm32-dma", + .of_match_table = stm32_dma_of_match, ++ .pm = &stm32_dma_pm_ops, + }, + }; + +@@ -1350,4 +2114,4 @@ static int __init stm32_dma_init(void) + { + return platform_driver_probe(&stm32_dma_driver, stm32_dma_probe); + } +-subsys_initcall(stm32_dma_init); ++device_initcall(stm32_dma_init); +diff --git a/drivers/dma/stm32-dmamux.c b/drivers/dma/stm32-dmamux.c +index b922db9..a878b7c 100644 +--- a/drivers/dma/stm32-dmamux.c ++++ b/drivers/dma/stm32-dmamux.c +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -51,6 +52,9 @@ struct stm32_dmamux_data { + u32 dmamux_requests; /* Number of DMA requests routed toward DMAs */ + spinlock_t lock; /* Protects register access */ + unsigned long *dma_inuse; /* Used DMA channel */ ++ u32 ccr[STM32_DMAMUX_MAX_DMA_REQUESTS]; /* Use to backup CCR register ++ * in suspend ++ */ + u32 dma_reqs[]; /* Number of DMA Request per DMA masters. + * [0] holds number of DMA Masters. + * To be kept at very end end of this structure +@@ -79,8 +83,7 @@ static void stm32_dmamux_free(struct device *dev, void *route_data) + stm32_dmamux_write(dmamux->iomem, STM32_DMAMUX_CCR(mux->chan_id), 0); + clear_bit(mux->chan_id, dmamux->dma_inuse); + +- if (!IS_ERR(dmamux->clk)) +- clk_disable(dmamux->clk); ++ pm_runtime_put_sync(dev); + + spin_unlock_irqrestore(&dmamux->lock, flags); + +@@ -146,13 +149,10 @@ static void *stm32_dmamux_route_allocate(struct of_phandle_args *dma_spec, + + /* Set dma request */ + spin_lock_irqsave(&dmamux->lock, flags); +- if (!IS_ERR(dmamux->clk)) { +- ret = clk_enable(dmamux->clk); +- if (ret < 0) { +- spin_unlock_irqrestore(&dmamux->lock, flags); +- dev_err(&pdev->dev, "clk_prep_enable issue: %d\n", ret); +- goto error; +- } ++ ret = pm_runtime_get_sync(&pdev->dev); ++ if (ret < 0) { ++ spin_unlock_irqrestore(&dmamux->lock, flags); ++ goto error; + } + spin_unlock_irqrestore(&dmamux->lock, flags); + +@@ -254,6 +254,7 @@ static int stm32_dmamux_probe(struct platform_device *pdev) + dev_warn(&pdev->dev, "DMAMUX defaulting on %u requests\n", + stm32_dmamux->dmamux_requests); + } ++ pm_runtime_get_noresume(&pdev->dev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iomem = devm_ioremap_resource(&pdev->dev, res); +@@ -282,6 +283,8 @@ static int stm32_dmamux_probe(struct platform_device *pdev) + stm32_dmamux->dmarouter.route_free = stm32_dmamux_free; + + platform_set_drvdata(pdev, stm32_dmamux); ++ pm_runtime_set_active(&pdev->dev); ++ pm_runtime_enable(&pdev->dev); + + if (!IS_ERR(stm32_dmamux->clk)) { + ret = clk_prepare_enable(stm32_dmamux->clk); +@@ -291,17 +294,101 @@ static int stm32_dmamux_probe(struct platform_device *pdev) + } + } + ++ pm_runtime_get_noresume(&pdev->dev); ++ + /* Reset the dmamux */ + for (i = 0; i < stm32_dmamux->dma_requests; i++) + stm32_dmamux_write(stm32_dmamux->iomem, STM32_DMAMUX_CCR(i), 0); + +- if (!IS_ERR(stm32_dmamux->clk)) +- clk_disable(stm32_dmamux->clk); ++ pm_runtime_put(&pdev->dev); + + return of_dma_router_register(node, stm32_dmamux_route_allocate, + &stm32_dmamux->dmarouter); + } + ++#ifdef CONFIG_PM ++static int stm32_dmamux_runtime_suspend(struct device *dev) ++{ ++ struct platform_device *pdev = ++ container_of(dev, struct platform_device, dev); ++ struct stm32_dmamux_data *stm32_dmamux = platform_get_drvdata(pdev); ++ ++ clk_disable_unprepare(stm32_dmamux->clk); ++ ++ return 0; ++} ++ ++static int stm32_dmamux_runtime_resume(struct device *dev) ++{ ++ struct platform_device *pdev = ++ container_of(dev, struct platform_device, dev); ++ struct stm32_dmamux_data *stm32_dmamux = platform_get_drvdata(pdev); ++ int ret; ++ ++ ret = clk_prepare_enable(stm32_dmamux->clk); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to prepare_enable clock\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++#endif ++ ++#ifdef CONFIG_PM_SLEEP ++static int stm32_dmamux_suspend(struct device *dev) ++{ ++ struct platform_device *pdev = ++ container_of(dev, struct platform_device, dev); ++ struct stm32_dmamux_data *stm32_dmamux = platform_get_drvdata(pdev); ++ int i, ret; ++ ++ ret = pm_runtime_get_sync(dev); ++ if (ret < 0) ++ return ret; ++ ++ for (i = 0; i < stm32_dmamux->dma_requests; i++) ++ stm32_dmamux->ccr[i] = stm32_dmamux_read(stm32_dmamux->iomem, ++ STM32_DMAMUX_CCR(i)); ++ ++ pm_runtime_put_sync(dev); ++ ++ pm_runtime_force_suspend(dev); ++ ++ return 0; ++} ++ ++static int stm32_dmamux_resume(struct device *dev) ++{ ++ struct platform_device *pdev = ++ container_of(dev, struct platform_device, dev); ++ struct stm32_dmamux_data *stm32_dmamux = platform_get_drvdata(pdev); ++ int i, ret; ++ ++ ret = pm_runtime_force_resume(dev); ++ if (ret < 0) ++ return ret; ++ ++ ret = pm_runtime_get_sync(dev); ++ if (ret < 0) ++ return ret; ++ ++ for (i = 0; i < stm32_dmamux->dma_requests; i++) ++ stm32_dmamux_write(stm32_dmamux->iomem, STM32_DMAMUX_CCR(i), ++ stm32_dmamux->ccr[i]); ++ ++ pm_runtime_put_sync(dev); ++ ++ return 0; ++} ++#endif ++ ++static const struct dev_pm_ops stm32_dmamux_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(stm32_dmamux_suspend, stm32_dmamux_resume) ++ SET_RUNTIME_PM_OPS(stm32_dmamux_runtime_suspend, ++ stm32_dmamux_runtime_resume, NULL) ++}; ++ + static const struct of_device_id stm32_dmamux_match[] = { + { .compatible = "st,stm32h7-dmamux" }, + {}, +@@ -312,6 +399,7 @@ static struct platform_driver stm32_dmamux_driver = { + .driver = { + .name = "stm32-dmamux", + .of_match_table = stm32_dmamux_match, ++ .pm = &stm32_dmamux_pm_ops, + }, + }; + +diff --git a/drivers/dma/stm32-mdma.c b/drivers/dma/stm32-mdma.c +index 06dd172..077ef5c 100644 +--- a/drivers/dma/stm32-mdma.c ++++ b/drivers/dma/stm32-mdma.c +@@ -37,6 +37,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -211,6 +212,8 @@ + #define STM32_MDMA_MAX_BURST 128 + #define STM32_MDMA_VERY_HIGH_PRIORITY 0x11 + ++#define STM32_DMA_SRAM_GRANULARITY PAGE_SIZE ++ + enum stm32_mdma_trigger_mode { + STM32_MDMA_BUFFER, + STM32_MDMA_BLOCK, +@@ -237,6 +240,7 @@ struct stm32_mdma_chan_config { + u32 transfer_config; + u32 mask_addr; + u32 mask_data; ++ bool m2m_hw; + }; + + struct stm32_mdma_hwdesc { +@@ -262,6 +266,7 @@ struct stm32_mdma_desc { + u32 ccr; + bool cyclic; + u32 count; ++ enum dma_transfer_direction dir; + struct stm32_mdma_desc_node node[]; + }; + +@@ -577,13 +582,25 @@ static int stm32_mdma_set_xfer_param(struct stm32_mdma_chan *chan, + dst_addr = chan->dma_config.dst_addr; + + /* Set device data size */ ++ if (chan_config->m2m_hw) ++ dst_addr_width = ++ stm32_mdma_get_max_width(dst_addr, buf_len, ++ STM32_MDMA_MAX_BUF_LEN); ++ + dst_bus_width = stm32_mdma_get_width(chan, dst_addr_width); + if (dst_bus_width < 0) + return dst_bus_width; + ctcr &= ~STM32_MDMA_CTCR_DSIZE_MASK; + ctcr |= STM32_MDMA_CTCR_DSIZE(dst_bus_width); ++ if (chan_config->m2m_hw) { ++ ctcr &= ~STM32_MDMA_CTCR_DINCOS_MASK; ++ ctcr |= STM32_MDMA_CTCR_DINCOS(dst_bus_width); ++ } + + /* Set device burst value */ ++ if (chan_config->m2m_hw) ++ dst_maxburst = STM32_MDMA_MAX_BUF_LEN / dst_addr_width; ++ + dst_best_burst = stm32_mdma_get_best_burst(buf_len, tlen, + dst_maxburst, + dst_addr_width); +@@ -626,13 +643,25 @@ static int stm32_mdma_set_xfer_param(struct stm32_mdma_chan *chan, + src_addr = chan->dma_config.src_addr; + + /* Set device data size */ ++ if (chan_config->m2m_hw) ++ src_addr_width = ++ stm32_mdma_get_max_width(src_addr, buf_len, ++ STM32_MDMA_MAX_BUF_LEN); ++ + src_bus_width = stm32_mdma_get_width(chan, src_addr_width); + if (src_bus_width < 0) + return src_bus_width; + ctcr &= ~STM32_MDMA_CTCR_SSIZE_MASK; + ctcr |= STM32_MDMA_CTCR_SSIZE(src_bus_width); ++ if (chan_config->m2m_hw) { ++ ctcr &= ~STM32_MDMA_CTCR_SINCOS_MASK; ++ ctcr |= STM32_MDMA_CTCR_SINCOS(src_bus_width); ++ } + + /* Set device burst value */ ++ if (chan_config->m2m_hw) ++ src_maxburst = STM32_MDMA_MAX_BUF_LEN / src_addr_width; ++ + src_best_burst = stm32_mdma_get_best_burst(buf_len, tlen, + src_maxburst, + src_addr_width); +@@ -740,6 +769,7 @@ static int stm32_mdma_setup_xfer(struct stm32_mdma_chan *chan, + { + struct stm32_mdma_device *dmadev = stm32_mdma_get_dev(chan); + struct dma_slave_config *dma_config = &chan->dma_config; ++ struct stm32_mdma_chan_config *chan_config = &chan->chan_config; + struct scatterlist *sg; + dma_addr_t src_addr, dst_addr; + u32 ccr, ctcr, ctbr; +@@ -762,6 +792,8 @@ static int stm32_mdma_setup_xfer(struct stm32_mdma_chan *chan, + } else { + src_addr = dma_config->src_addr; + dst_addr = sg_dma_address(sg); ++ if (chan_config->m2m_hw) ++ src_addr += ((i & 1) ? sg_dma_len(sg) : 0); + ret = stm32_mdma_set_xfer_param(chan, direction, &ccr, + &ctcr, &ctbr, dst_addr, + sg_dma_len(sg)); +@@ -780,8 +812,6 @@ static int stm32_mdma_setup_xfer(struct stm32_mdma_chan *chan, + /* Enable interrupts */ + ccr &= ~STM32_MDMA_CCR_IRQ_MASK; + ccr |= STM32_MDMA_CCR_TEIE | STM32_MDMA_CCR_CTCIE; +- if (sg_len > 1) +- ccr |= STM32_MDMA_CCR_BTIE; + desc->ccr = ccr; + + return 0; +@@ -793,7 +823,9 @@ stm32_mdma_prep_slave_sg(struct dma_chan *c, struct scatterlist *sgl, + unsigned long flags, void *context) + { + struct stm32_mdma_chan *chan = to_stm32_mdma_chan(c); ++ struct stm32_mdma_chan_config *chan_config = &chan->chan_config; + struct stm32_mdma_desc *desc; ++ struct stm32_mdma_hwdesc *hwdesc; + int i, ret; + + /* +@@ -815,6 +847,20 @@ stm32_mdma_prep_slave_sg(struct dma_chan *c, struct scatterlist *sgl, + if (ret < 0) + goto xfer_setup_err; + ++ /* ++ * In case of M2M HW transfer triggered by STM32 DMA, we do not have to ++ * clear the transfer complete flag by hardware in order to let the ++ * CPU rearm the DMA with the next sg element and update some data in ++ * dmaengine framework ++ */ ++ if (chan_config->m2m_hw && direction == DMA_MEM_TO_DEV) { ++ for (i = 0; i < sg_len; i++) { ++ hwdesc = desc->node[i].hwdesc; ++ hwdesc->cmar = 0; ++ hwdesc->cmdr = 0; ++ } ++ } ++ + desc->cyclic = false; + + return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); +@@ -836,9 +882,10 @@ stm32_mdma_prep_dma_cyclic(struct dma_chan *c, dma_addr_t buf_addr, + struct stm32_mdma_chan *chan = to_stm32_mdma_chan(c); + struct stm32_mdma_device *dmadev = stm32_mdma_get_dev(chan); + struct dma_slave_config *dma_config = &chan->dma_config; ++ struct stm32_mdma_chan_config *chan_config = &chan->chan_config; + struct stm32_mdma_desc *desc; + dma_addr_t src_addr, dst_addr; +- u32 ccr, ctcr, ctbr, count; ++ u32 ccr, ctcr, ctbr, count, offset; + int i, ret; + + /* +@@ -892,12 +939,29 @@ stm32_mdma_prep_dma_cyclic(struct dma_chan *c, dma_addr_t buf_addr, + desc->ccr = ccr; + + /* Configure hwdesc list */ ++ offset = ALIGN(period_len, STM32_DMA_SRAM_GRANULARITY); + for (i = 0; i < count; i++) { + if (direction == DMA_MEM_TO_DEV) { ++ /* ++ * When the DMA is configured in double buffer mode, ++ * the MDMA has to use 2 destination buffers to be ++ * compliant with this mode. ++ */ ++ if (chan_config->m2m_hw && count > 1 && i % 2) ++ dst_addr = dma_config->dst_addr + offset; ++ else ++ dst_addr = dma_config->dst_addr; + src_addr = buf_addr + i * period_len; +- dst_addr = dma_config->dst_addr; + } else { +- src_addr = dma_config->src_addr; ++ /* ++ * When the DMA is configured in double buffer mode, ++ * the MDMA has to use 2 destination buffers to be ++ * compliant with this mode. ++ */ ++ if (chan_config->m2m_hw && count > 1 && i % 2) ++ src_addr = dma_config->src_addr + offset; ++ else ++ src_addr = dma_config->src_addr; + dst_addr = buf_addr + i * period_len; + } + +@@ -907,6 +971,7 @@ stm32_mdma_prep_dma_cyclic(struct dma_chan *c, dma_addr_t buf_addr, + } + + desc->cyclic = true; ++ desc->dir = direction; + + return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); + +@@ -1287,14 +1352,28 @@ static size_t stm32_mdma_desc_residue(struct stm32_mdma_chan *chan, + { + struct stm32_mdma_device *dmadev = stm32_mdma_get_dev(chan); + struct stm32_mdma_hwdesc *hwdesc = desc->node[0].hwdesc; +- u32 cbndtr, residue, modulo, burst_size; ++ u32 residue = 0; ++ u32 modulo, burst_size; ++ dma_addr_t next_clar; ++ u32 cbndtr; + int i; + +- residue = 0; +- for (i = curr_hwdesc + 1; i < desc->count; i++) { ++ /* ++ * Get the residue of pending descriptors ++ */ ++ /* Get the next hw descriptor to process from current transfer */ ++ next_clar = stm32_mdma_read(dmadev, STM32_MDMA_CLAR(chan->id)); ++ for (i = desc->count - 1; i >= 0; i--) { + hwdesc = desc->node[i].hwdesc; ++ ++ if (hwdesc->clar == next_clar) ++ break;/* Current transfer found, stop cumulating */ ++ ++ /* Cumulate residue of unprocessed hw descriptors */ + residue += STM32_MDMA_CBNDTR_BNDT(hwdesc->cbndtr); + } ++ ++ /* Read & cumulate the residue of the current transfer */ + cbndtr = stm32_mdma_read(dmadev, STM32_MDMA_CBNDTR(chan->id)); + residue += cbndtr & STM32_MDMA_CBNDTR_BNDT_MASK; + +@@ -1314,24 +1393,39 @@ static enum dma_status stm32_mdma_tx_status(struct dma_chan *c, + struct dma_tx_state *state) + { + struct stm32_mdma_chan *chan = to_stm32_mdma_chan(c); ++ struct stm32_mdma_chan_config *chan_config = &chan->chan_config; + struct virt_dma_desc *vdesc; + enum dma_status status; + unsigned long flags; + u32 residue = 0; + + status = dma_cookie_status(c, cookie, state); +- if ((status == DMA_COMPLETE) || (!state)) ++ if (status == DMA_COMPLETE || !state) + return status; + + spin_lock_irqsave(&chan->vchan.lock, flags); + + vdesc = vchan_find_desc(&chan->vchan, cookie); +- if (chan->desc && cookie == chan->desc->vdesc.tx.cookie) +- residue = stm32_mdma_desc_residue(chan, chan->desc, +- chan->curr_hwdesc); +- else if (vdesc) ++ if (chan->desc && cookie == chan->desc->vdesc.tx.cookie) { ++ /* ++ * In case of M2D transfer triggered by STM32 DMA, the MDMA has ++ * always one period in advance in cyclic mode. So, we have to ++ * add 1 period of data to return the good residue to the ++ * client ++ */ ++ if (chan_config->m2m_hw && chan->desc->dir == DMA_MEM_TO_DEV && ++ chan->curr_hwdesc > 1) ++ residue = ++ stm32_mdma_desc_residue(chan, chan->desc, ++ chan->curr_hwdesc - 1); ++ else ++ residue = stm32_mdma_desc_residue(chan, chan->desc, ++ chan->curr_hwdesc); ++ } else if (vdesc) { + residue = stm32_mdma_desc_residue(chan, + to_stm32_mdma_desc(vdesc), 0); ++ } ++ + dma_set_residue(state, residue); + + spin_unlock_irqrestore(&chan->vchan.lock, flags); +@@ -1456,15 +1550,13 @@ static int stm32_mdma_alloc_chan_resources(struct dma_chan *c) + return -ENOMEM; + } + +- ret = clk_prepare_enable(dmadev->clk); +- if (ret < 0) { +- dev_err(chan2dev(chan), "clk_prepare_enable failed: %d\n", ret); ++ ret = pm_runtime_get_sync(dmadev->ddev.dev); ++ if (ret < 0) + return ret; +- } + + ret = stm32_mdma_disable_chan(chan); + if (ret < 0) +- clk_disable_unprepare(dmadev->clk); ++ pm_runtime_put(dmadev->ddev.dev); + + return ret; + } +@@ -1484,7 +1576,7 @@ static void stm32_mdma_free_chan_resources(struct dma_chan *c) + spin_unlock_irqrestore(&chan->vchan.lock, flags); + } + +- clk_disable_unprepare(dmadev->clk); ++ pm_runtime_put(dmadev->ddev.dev); + vchan_free_chan_resources(to_virt_chan(c)); + dmam_pool_destroy(chan->desc_pool); + chan->desc_pool = NULL; +@@ -1498,7 +1590,7 @@ static struct dma_chan *stm32_mdma_of_xlate(struct of_phandle_args *dma_spec, + struct dma_chan *c; + struct stm32_mdma_chan_config config; + +- if (dma_spec->args_count < 5) { ++ if (dma_spec->args_count < 6) { + dev_err(mdma2dev(dmadev), "Bad number of args\n"); + return NULL; + } +@@ -1508,6 +1600,7 @@ static struct dma_chan *stm32_mdma_of_xlate(struct of_phandle_args *dma_spec, + config.transfer_config = dma_spec->args[2]; + config.mask_addr = dma_spec->args[3]; + config.mask_data = dma_spec->args[4]; ++ config.m2m_hw = dma_spec->args[5]; + + if (config.request >= dmadev->nr_requests) { + dev_err(mdma2dev(dmadev), "Bad request line\n"); +@@ -1597,6 +1690,12 @@ static int stm32_mdma_probe(struct platform_device *pdev) + return ret; + } + ++ ret = clk_prepare_enable(dmadev->clk); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "clk_prep_enable error: %d\n", ret); ++ return ret; ++ } ++ + dmadev->rst = devm_reset_control_get(&pdev->dev, NULL); + if (!IS_ERR(dmadev->rst)) { + reset_control_assert(dmadev->rst); +@@ -1621,6 +1720,8 @@ static int stm32_mdma_probe(struct platform_device *pdev) + dd->device_resume = stm32_mdma_resume; + dd->device_terminate_all = stm32_mdma_terminate_all; + dd->device_synchronize = stm32_mdma_synchronize; ++ dd->descriptor_reuse = true; ++ + dd->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | +@@ -1646,19 +1747,20 @@ static int stm32_mdma_probe(struct platform_device *pdev) + dmadev->irq = platform_get_irq(pdev, 0); + if (dmadev->irq < 0) { + dev_err(&pdev->dev, "failed to get IRQ\n"); +- return dmadev->irq; ++ ret = dmadev->irq; ++ goto clk_free; + } + + ret = devm_request_irq(&pdev->dev, dmadev->irq, stm32_mdma_irq_handler, + 0, dev_name(&pdev->dev), dmadev); + if (ret) { + dev_err(&pdev->dev, "failed to request IRQ\n"); +- return ret; ++ goto clk_free; + } + + ret = dma_async_device_register(dd); + if (ret) +- return ret; ++ goto clk_free; + + ret = of_dma_controller_register(of_node, stm32_mdma_of_xlate, dmadev); + if (ret < 0) { +@@ -1668,6 +1770,10 @@ static int stm32_mdma_probe(struct platform_device *pdev) + } + + platform_set_drvdata(pdev, dmadev); ++ pm_runtime_set_active(&pdev->dev); ++ pm_runtime_enable(&pdev->dev); ++ pm_runtime_get_noresume(&pdev->dev); ++ pm_runtime_put(&pdev->dev); + + dev_info(&pdev->dev, "STM32 MDMA driver registered\n"); + +@@ -1675,15 +1781,85 @@ static int stm32_mdma_probe(struct platform_device *pdev) + + err_unregister: + dma_async_device_unregister(dd); ++clk_free: ++ clk_disable_unprepare(dmadev->clk); + + return ret; + } + ++#ifdef CONFIG_PM ++static int stm32_mdma_runtime_suspend(struct device *dev) ++{ ++ struct stm32_mdma_device *dmadev = dev_get_drvdata(dev); ++ ++ clk_disable_unprepare(dmadev->clk); ++ ++ return 0; ++} ++ ++static int stm32_mdma_runtime_resume(struct device *dev) ++{ ++ struct stm32_mdma_device *dmadev = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = clk_prepare_enable(dmadev->clk); ++ if (ret) { ++ dev_err(dev, "failed to prepare_enable clock\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++#endif ++ ++#ifdef CONFIG_PM_SLEEP ++static int stm32_mdma_pw_suspend(struct device *dev) ++{ ++ struct stm32_mdma_device *dmadev = dev_get_drvdata(dev); ++ u32 ccr, id, ret; ++ ++ ret = pm_runtime_get_sync(dev); ++ if (ret < 0) ++ return ret; ++ ++ for (id = 0; id < dmadev->nr_channels; id++) { ++ ccr = stm32_mdma_read(dmadev, STM32_MDMA_CCR(id)); ++ if (ccr & STM32_MDMA_CCR_EN) { ++ dev_warn(dev, "Suspend is prevented by Chan %i\n", id); ++ return -EBUSY; ++ } ++ } ++ ++ pm_runtime_put_sync(dev); ++ ++ pm_runtime_force_suspend(dev); ++ return 0; ++} ++ ++static int stm32_mdma_pw_resume(struct device *dev) ++{ ++ int ret; ++ ++ ret = pm_runtime_force_resume(dev); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++#endif ++ ++static const struct dev_pm_ops stm32_mdma_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(stm32_mdma_pw_suspend, stm32_mdma_pw_resume) ++ SET_RUNTIME_PM_OPS(stm32_mdma_runtime_suspend, ++ stm32_mdma_runtime_resume, NULL) ++}; ++ + static struct platform_driver stm32_mdma_driver = { + .probe = stm32_mdma_probe, + .driver = { + .name = "stm32-mdma", + .of_match_table = stm32_mdma_of_match, ++ .pm = &stm32_mdma_pm_ops, + }, + }; + +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0004-ARM-stm32mp1-r0-rc1-I2C.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0004-ARM-stm32mp1-r0-rc1-I2C.patch new file mode 100644 index 0000000..8a921dd --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0004-ARM-stm32mp1-r0-rc1-I2C.patch @@ -0,0 +1,638 @@ +From 656c7df3520f4422b871a4e69c68e6a9daf7ba61 Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Tue, 13 Nov 2018 12:18:06 +0100 +Subject: [PATCH 04/52] ARM: stm32mp1-r0-rc1: I2C + +--- + drivers/i2c/busses/i2c-stm32f7.c | 369 +++++++++++++++++++++++++++++++++------ + 1 file changed, 320 insertions(+), 49 deletions(-) + +diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c +index 62d023e..c1cbf93 100644 +--- a/drivers/i2c/busses/i2c-stm32f7.c ++++ b/drivers/i2c/busses/i2c-stm32f7.c +@@ -21,12 +21,17 @@ + #include + #include + #include ++#include + #include + #include + #include + #include + #include + #include ++#include ++#include ++#include ++#include + #include + #include + +@@ -46,6 +51,7 @@ + + /* STM32F7 I2C control 1 */ + #define STM32F7_I2C_CR1_PECEN BIT(23) ++#define STM32F7_I2C_CR1_WUPEN BIT(18) + #define STM32F7_I2C_CR1_SBC BIT(16) + #define STM32F7_I2C_CR1_RXDMAEN BIT(15) + #define STM32F7_I2C_CR1_TXDMAEN BIT(14) +@@ -163,6 +169,26 @@ + #define STM32F7_SCLH_MAX BIT(8) + #define STM32F7_SCLL_MAX BIT(8) + ++#define STM32F7_AUTOSUSPEND_DELAY (HZ / 100) ++ ++/** ++ * struct stm32f7_i2c_regs - i2c f7 registers backup ++ * @cr1: Control register 1 ++ * @cr2: Control register 2 ++ * @oar1: Own address 1 register ++ * @oar2: Own address 2 register ++ * @pecr: PEC register ++ * @timingr: Timing register ++ */ ++struct stm32f7_i2c_regs { ++ u32 cr1; ++ u32 cr2; ++ u32 oar1; ++ u32 oar2; ++ u32 pecr; ++ u32 tmgr; ++}; ++ + /** + * struct stm32f7_i2c_spec - private i2c specification timing + * @rate: I2C bus speed (Hz) +@@ -259,6 +285,8 @@ struct stm32f7_i2c_msg { + * struct stm32f7_i2c_dev - private data of the controller + * @adap: I2C adapter for this controller + * @dev: device for this controller ++ * @irq_event: interrupt event line for the controller ++ * @irq_wakeup: interrupt wakeup line for the controller + * @base: virtual memory area + * @complete: completion of I2C message + * @clk: hw i2c clock +@@ -276,11 +304,14 @@ struct stm32f7_i2c_msg { + * slave) + * @dma: dma data + * @use_dma: boolean to know if dma is used in the current transfer ++ * @regmap: holds SYSCFG phandle for Fast Mode Plus bits + */ + struct stm32f7_i2c_dev { + struct i2c_adapter adap; + struct device *dev; + void __iomem *base; ++ int irq_event; ++ int irq_wakeup; + struct completion complete; + struct clk *clk; + int speed; +@@ -292,10 +323,12 @@ struct stm32f7_i2c_dev { + struct stm32f7_i2c_timings timing; + struct i2c_client *slave[STM32F7_I2C_MAX_SLAVE]; + struct i2c_client *slave_running; ++ struct stm32f7_i2c_regs regs; + u32 slave_dir; + bool master_mode; + struct stm32_i2c_dma *dma; + bool use_dma; ++ struct regmap *regmap; + }; + + /** +@@ -1545,15 +1578,13 @@ static int stm32f7_i2c_xfer(struct i2c_adapter *i2c_adap, + i2c_dev->msg_id = 0; + f7_msg->smbus = false; + +- ret = clk_enable(i2c_dev->clk); +- if (ret) { +- dev_err(i2c_dev->dev, "Failed to enable clock\n"); ++ ret = pm_runtime_get_sync(i2c_dev->dev); ++ if (ret < 0) + return ret; +- } + + ret = stm32f7_i2c_wait_free_bus(i2c_dev); + if (ret) +- goto clk_free; ++ goto pm_free; + + stm32f7_i2c_xfer_msg(i2c_dev, msgs); + +@@ -1569,8 +1600,9 @@ static int stm32f7_i2c_xfer(struct i2c_adapter *i2c_adap, + ret = -ETIMEDOUT; + } + +-clk_free: +- clk_disable(i2c_dev->clk); ++pm_free: ++ pm_runtime_mark_last_busy(i2c_dev->dev); ++ pm_runtime_put_autosuspend(i2c_dev->dev); + + return (ret < 0) ? ret : num; + } +@@ -1592,39 +1624,37 @@ static int stm32f7_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, + f7_msg->read_write = read_write; + f7_msg->smbus = true; + +- ret = clk_enable(i2c_dev->clk); +- if (ret) { +- dev_err(i2c_dev->dev, "Failed to enable clock\n"); ++ ret = pm_runtime_get_sync(dev); ++ if (ret < 0) + return ret; +- } + + ret = stm32f7_i2c_wait_free_bus(i2c_dev); + if (ret) +- goto clk_free; ++ goto pm_free; + + ret = stm32f7_i2c_smbus_xfer_msg(i2c_dev, flags, command, data); + if (ret) +- goto clk_free; ++ goto pm_free; + + timeout = wait_for_completion_timeout(&i2c_dev->complete, + i2c_dev->adap.timeout); + ret = f7_msg->result; + if (ret) +- goto clk_free; ++ goto pm_free; + + if (!timeout) { + dev_dbg(dev, "Access to slave 0x%x timed out\n", f7_msg->addr); + if (i2c_dev->use_dma) + dmaengine_terminate_all(dma->chan_using); + ret = -ETIMEDOUT; +- goto clk_free; ++ goto pm_free; + } + + /* Check PEC */ + if ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK && read_write) { + ret = stm32f7_i2c_smbus_check_pec(i2c_dev); + if (ret) +- goto clk_free; ++ goto pm_free; + } + + if (read_write && size != I2C_SMBUS_QUICK) { +@@ -1649,11 +1679,15 @@ static int stm32f7_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, + } + } + +-clk_free: +- clk_disable(i2c_dev->clk); ++pm_free: ++ pm_runtime_mark_last_busy(dev); ++ pm_runtime_put_autosuspend(dev); + return ret; + } + ++static void stm32f7_i2c_enable_wakeup(struct stm32f7_i2c_dev *i2c_dev, ++ bool enable); ++ + static int stm32f7_i2c_reg_slave(struct i2c_client *slave) + { + struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(slave->adapter); +@@ -1676,13 +1710,12 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave) + if (ret) + return ret; + +- if (!(stm32f7_i2c_is_slave_registered(i2c_dev))) { +- ret = clk_enable(i2c_dev->clk); +- if (ret) { +- dev_err(dev, "Failed to enable clock\n"); +- return ret; +- } +- } ++ ret = pm_runtime_get_sync(dev); ++ if (ret < 0) ++ return ret; ++ ++ if (!stm32f7_i2c_is_slave_registered(i2c_dev)) ++ stm32f7_i2c_enable_wakeup(i2c_dev, true); + + if (id == 0) { + /* Configure Own Address 1 */ +@@ -1703,7 +1736,7 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave) + oar2 &= ~STM32F7_I2C_OAR2_MASK; + if (slave->flags & I2C_CLIENT_TEN) { + ret = -EOPNOTSUPP; +- goto exit; ++ goto pm_free; + } + + oar2 |= STM32F7_I2C_OAR2_OA2_7(slave->addr); +@@ -1712,7 +1745,7 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave) + writel_relaxed(oar2, i2c_dev->base + STM32F7_I2C_OAR2); + } else { + ret = -ENODEV; +- goto exit; ++ goto pm_free; + } + + /* Enable ACK */ +@@ -1723,11 +1756,13 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave) + STM32F7_I2C_CR1_PE; + stm32f7_i2c_set_bits(base + STM32F7_I2C_CR1, mask); + +- return 0; ++ ret = 0; ++pm_free: ++ if (!stm32f7_i2c_is_slave_registered(i2c_dev)) ++ stm32f7_i2c_enable_wakeup(i2c_dev, false); + +-exit: +- if (!(stm32f7_i2c_is_slave_registered(i2c_dev))) +- clk_disable(i2c_dev->clk); ++ pm_runtime_mark_last_busy(dev); ++ pm_runtime_put_autosuspend(dev); + + return ret; + } +@@ -1745,6 +1780,10 @@ static int stm32f7_i2c_unreg_slave(struct i2c_client *slave) + + WARN_ON(!i2c_dev->slave[id]); + ++ ret = pm_runtime_get_sync(i2c_dev->dev); ++ if (ret < 0) ++ return ret; ++ + if (id == 0) { + mask = STM32F7_I2C_OAR1_OA1EN; + stm32f7_i2c_clr_bits(base + STM32F7_I2C_OAR1, mask); +@@ -1755,14 +1794,56 @@ static int stm32f7_i2c_unreg_slave(struct i2c_client *slave) + + i2c_dev->slave[id] = NULL; + +- if (!(stm32f7_i2c_is_slave_registered(i2c_dev))) { ++ if (!stm32f7_i2c_is_slave_registered(i2c_dev)) { + stm32f7_i2c_disable_irq(i2c_dev, STM32F7_I2C_ALL_IRQ_MASK); +- clk_disable(i2c_dev->clk); ++ stm32f7_i2c_enable_wakeup(i2c_dev, false); + } + ++ pm_runtime_mark_last_busy(i2c_dev->dev); ++ pm_runtime_put_autosuspend(i2c_dev->dev); ++ + return 0; + } + ++static int stm32f7_i2c_setup_wakeup(struct stm32f7_i2c_dev *i2c_dev) ++{ ++ int ret; ++ ++ device_init_wakeup(i2c_dev->dev, true); ++ ret = dev_pm_set_dedicated_wake_irq(i2c_dev->dev, i2c_dev->irq_wakeup); ++ if (ret) { ++ device_init_wakeup(i2c_dev->dev, false); ++ dev_warn(i2c_dev->dev, "failed to set up wakeup irq"); ++ return ret; ++ } ++ ++ return device_set_wakeup_enable(i2c_dev->dev, false); ++} ++ ++static int stm32f7_i2c_setup_fm_plus_bits(struct platform_device *pdev, ++ struct stm32f7_i2c_dev *i2c_dev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ int ret; ++ u32 reg, mask; ++ ++ i2c_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg-fmp"); ++ if (IS_ERR(i2c_dev->regmap)) { ++ /* Optional */ ++ return 0; ++ } ++ ++ ret = of_property_read_u32_index(np, "st,syscfg-fmp", 1, ®); ++ if (ret) ++ return ret; ++ ++ ret = of_property_read_u32_index(np, "st,syscfg-fmp", 2, &mask); ++ if (ret) ++ return ret; ++ ++ return regmap_update_bits(i2c_dev->regmap, reg, mask, mask); ++} ++ + static u32 stm32f7_i2c_func(struct i2c_adapter *adap) + { + return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SLAVE | +@@ -1786,7 +1867,7 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) + struct stm32f7_i2c_dev *i2c_dev; + const struct stm32f7_i2c_setup *setup; + struct resource *res; +- u32 irq_error, irq_event, clk_rate, rise_time, fall_time; ++ u32 irq_error, clk_rate, rise_time, fall_time; + struct i2c_adapter *adap; + struct reset_control *rst; + dma_addr_t phy_addr; +@@ -1802,13 +1883,13 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) + return PTR_ERR(i2c_dev->base); + phy_addr = (dma_addr_t)res->start; + +- irq_event = irq_of_parse_and_map(np, 0); +- if (!irq_event) { ++ i2c_dev->irq_event = of_irq_get_byname(np, "event"); ++ if (!i2c_dev->irq_event) { + dev_err(&pdev->dev, "IRQ event missing or invalid\n"); + return -EINVAL; + } + +- irq_error = irq_of_parse_and_map(np, 1); ++ irq_error = of_irq_get_byname(np, "error"); + if (!irq_error) { + dev_err(&pdev->dev, "IRQ error missing or invalid\n"); + return -EINVAL; +@@ -1819,6 +1900,7 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) + dev_err(&pdev->dev, "Error: Missing controller clock\n"); + return PTR_ERR(i2c_dev->clk); + } ++ + ret = clk_prepare_enable(i2c_dev->clk); + if (ret) { + dev_err(&pdev->dev, "Failed to prepare_enable clock\n"); +@@ -1828,12 +1910,16 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) + i2c_dev->speed = STM32_I2C_SPEED_STANDARD; + ret = device_property_read_u32(&pdev->dev, "clock-frequency", + &clk_rate); +- if (!ret && clk_rate >= 1000000) ++ if (!ret && clk_rate >= 1000000) { + i2c_dev->speed = STM32_I2C_SPEED_FAST_PLUS; +- else if (!ret && clk_rate >= 400000) ++ ret = stm32f7_i2c_setup_fm_plus_bits(pdev, i2c_dev); ++ if (ret) ++ goto clk_free; ++ } else if (!ret && clk_rate >= 400000) { + i2c_dev->speed = STM32_I2C_SPEED_FAST; +- else if (!ret && clk_rate >= 100000) ++ } else if (!ret && clk_rate >= 100000) { + i2c_dev->speed = STM32_I2C_SPEED_STANDARD; ++ } + + rst = devm_reset_control_get(&pdev->dev, NULL); + if (IS_ERR(rst)) { +@@ -1847,14 +1933,14 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) + + i2c_dev->dev = &pdev->dev; + +- ret = devm_request_threaded_irq(&pdev->dev, irq_event, ++ ret = devm_request_threaded_irq(&pdev->dev, i2c_dev->irq_event, + stm32f7_i2c_isr_event, + stm32f7_i2c_isr_event_thread, + IRQF_ONESHOT, + pdev->name, i2c_dev); + if (ret) { + dev_err(&pdev->dev, "Failed to request irq event %i\n", +- irq_event); ++ i2c_dev->irq_event); + goto clk_free; + } + +@@ -1888,8 +1974,6 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) + if (ret) + goto clk_free; + +- stm32f7_i2c_hw_config(i2c_dev); +- + adap = &i2c_dev->adap; + i2c_set_adapdata(adap, i2c_dev); + snprintf(adap->name, sizeof(adap->name), "STM32F7 I2C(%pa)", +@@ -1908,18 +1992,45 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) + STM32F7_I2C_TXDR, + STM32F7_I2C_RXDR); + +- ret = i2c_add_adapter(adap); +- if (ret) +- goto clk_free; ++ i2c_dev->irq_wakeup = of_irq_get_byname(np, "wakeup"); ++ if (i2c_dev->irq_wakeup > 0) { ++ ret = stm32f7_i2c_setup_wakeup(i2c_dev); ++ if (ret) ++ goto clk_free; ++ } + + platform_set_drvdata(pdev, i2c_dev); + +- clk_disable(i2c_dev->clk); ++ pm_runtime_set_autosuspend_delay(i2c_dev->dev, ++ STM32F7_AUTOSUSPEND_DELAY); ++ pm_runtime_use_autosuspend(i2c_dev->dev); ++ pm_runtime_set_active(i2c_dev->dev); ++ pm_runtime_enable(i2c_dev->dev); ++ ++ pm_runtime_get_noresume(&pdev->dev); ++ ++ stm32f7_i2c_hw_config(i2c_dev); ++ ++ ret = i2c_add_adapter(adap); ++ if (ret) ++ goto pm_disable; + + dev_info(i2c_dev->dev, "STM32F7 I2C-%d bus adapter\n", adap->nr); + ++ pm_runtime_mark_last_busy(i2c_dev->dev); ++ pm_runtime_put_autosuspend(i2c_dev->dev); ++ + return 0; + ++pm_disable: ++ dev_pm_clear_wake_irq(i2c_dev->dev); ++ device_init_wakeup(i2c_dev->dev, false); ++ ++ pm_runtime_put_noidle(i2c_dev->dev); ++ pm_runtime_disable(i2c_dev->dev); ++ pm_runtime_set_suspended(i2c_dev->dev); ++ pm_runtime_dont_use_autosuspend(i2c_dev->dev); ++ + clk_free: + clk_disable_unprepare(i2c_dev->clk); + +@@ -1936,11 +2047,170 @@ static int stm32f7_i2c_remove(struct platform_device *pdev) + } + + i2c_del_adapter(&i2c_dev->adap); ++ pm_runtime_get_sync(i2c_dev->dev); ++ ++ dev_pm_clear_wake_irq(i2c_dev->dev); ++ device_init_wakeup(i2c_dev->dev, false); + +- clk_unprepare(i2c_dev->clk); ++ clk_disable_unprepare(i2c_dev->clk); ++ ++ pm_runtime_put_noidle(i2c_dev->dev); ++ pm_runtime_disable(i2c_dev->dev); ++ pm_runtime_set_suspended(i2c_dev->dev); ++ pm_runtime_dont_use_autosuspend(i2c_dev->dev); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int stm32f7_i2c_runtime_suspend(struct device *dev) ++{ ++ struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev); ++ ++ if (!stm32f7_i2c_is_slave_registered(i2c_dev)) ++ clk_disable_unprepare(i2c_dev->clk); ++ ++ return 0; ++} ++ ++static int stm32f7_i2c_runtime_resume(struct device *dev) ++{ ++ struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev); ++ int ret; ++ ++ if (!stm32f7_i2c_is_slave_registered(i2c_dev)) { ++ ret = clk_prepare_enable(i2c_dev->clk); ++ if (ret) { ++ dev_err(dev, "failed to prepare_enable clock\n"); ++ return ret; ++ } ++ } + + return 0; + } ++#endif ++ ++#ifdef CONFIG_PM_SLEEP ++static int stm32f7_i2c_regs_backup(struct stm32f7_i2c_dev *i2c_dev) ++{ ++ int ret; ++ ++ ret = pm_runtime_get_sync(i2c_dev->dev); ++ if (ret < 0) ++ return ret; ++ ++ i2c_dev->regs.cr1 = readl_relaxed(i2c_dev->base + STM32F7_I2C_CR1); ++ i2c_dev->regs.cr2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_CR2); ++ i2c_dev->regs.oar1 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR1); ++ i2c_dev->regs.oar2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR2); ++ i2c_dev->regs.pecr = readl_relaxed(i2c_dev->base + STM32F7_I2C_PECR); ++ i2c_dev->regs.tmgr = readl_relaxed(i2c_dev->base + STM32F7_I2C_TIMINGR); ++ ++ pm_runtime_put_sync(i2c_dev->dev); ++ ++ return ret; ++} ++ ++static int stm32f7_i2c_regs_restore(struct stm32f7_i2c_dev *i2c_dev) ++{ ++ u32 cr1; ++ int ret; ++ ++ ret = pm_runtime_get_sync(i2c_dev->dev); ++ if (ret < 0) ++ return ret; ++ ++ cr1 = readl_relaxed(i2c_dev->base + STM32F7_I2C_CR1); ++ if (cr1 & STM32F7_I2C_CR1_PE) ++ stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1, ++ STM32F7_I2C_CR1_PE); ++ ++ writel_relaxed(i2c_dev->regs.tmgr, i2c_dev->base + STM32F7_I2C_TIMINGR); ++ writel_relaxed(i2c_dev->regs.cr1 & ~STM32F7_I2C_CR1_PE, ++ i2c_dev->base + STM32F7_I2C_CR1); ++ if (i2c_dev->regs.cr1 & STM32F7_I2C_CR1_PE) ++ stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1, ++ STM32F7_I2C_CR1_PE); ++ writel_relaxed(i2c_dev->regs.cr2, i2c_dev->base + STM32F7_I2C_CR2); ++ writel_relaxed(i2c_dev->regs.oar1, i2c_dev->base + STM32F7_I2C_OAR1); ++ writel_relaxed(i2c_dev->regs.oar2, i2c_dev->base + STM32F7_I2C_OAR2); ++ writel_relaxed(i2c_dev->regs.pecr, i2c_dev->base + STM32F7_I2C_PECR); ++ ++ pm_runtime_put_sync(i2c_dev->dev); ++ ++ return ret; ++} ++ ++static void stm32f7_i2c_enable_wakeup(struct stm32f7_i2c_dev *i2c_dev, ++ bool enable) ++{ ++ void __iomem *base = i2c_dev->base; ++ u32 mask = STM32F7_I2C_CR1_WUPEN; ++ ++ if (i2c_dev->irq_wakeup <= 0) ++ return; ++ ++ if (enable) { ++ device_set_wakeup_enable(i2c_dev->dev, true); ++ enable_irq_wake(i2c_dev->irq_wakeup); ++ enable_irq_wake(i2c_dev->irq_event); ++ stm32f7_i2c_set_bits(base + STM32F7_I2C_CR1, mask); ++ readl_relaxed(i2c_dev->base + STM32F7_I2C_CR1); ++ } else { ++ disable_irq_wake(i2c_dev->irq_wakeup); ++ disable_irq_wake(i2c_dev->irq_event); ++ device_set_wakeup_enable(i2c_dev->dev, false); ++ stm32f7_i2c_clr_bits(base + STM32F7_I2C_CR1, mask); ++ } ++} ++ ++static int stm32f7_i2c_suspend(struct device *dev) ++{ ++ struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = stm32f7_i2c_regs_backup(i2c_dev); ++ if (ret < 0) ++ return ret; ++ ++ if (!stm32f7_i2c_is_slave_registered(i2c_dev)) { ++ pinctrl_pm_select_sleep_state(dev); ++ pm_runtime_force_suspend(dev); ++ } ++ ++ return 0; ++} ++ ++static int stm32f7_i2c_resume(struct device *dev) ++{ ++ struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev); ++ int ret; ++ ++ if (!stm32f7_i2c_is_slave_registered(i2c_dev)) { ++ ret = pm_runtime_force_resume(dev); ++ if (ret < 0) ++ return ret; ++ pinctrl_pm_select_default_state(dev); ++ } ++ ++ ret = stm32f7_i2c_regs_restore(i2c_dev); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++#else ++static void stm32f7_i2c_enable_wakeup(struct stm32f7_i2c_dev *i2c_dev, ++ bool enable) ++{ ++} ++#endif ++ ++static const struct dev_pm_ops stm32f7_i2c_pm_ops = { ++ SET_RUNTIME_PM_OPS(stm32f7_i2c_runtime_suspend, ++ stm32f7_i2c_runtime_resume, NULL) ++ SET_SYSTEM_SLEEP_PM_OPS(stm32f7_i2c_suspend, stm32f7_i2c_resume) ++}; + + static const struct of_device_id stm32f7_i2c_match[] = { + { .compatible = "st,stm32f7-i2c", .data = &stm32f7_setup}, +@@ -1952,6 +2222,7 @@ static struct platform_driver stm32f7_i2c_driver = { + .driver = { + .name = "stm32f7-i2c", + .of_match_table = stm32f7_i2c_match, ++ .pm = &stm32f7_i2c_pm_ops, + }, + .probe = stm32f7_i2c_probe, + .remove = stm32f7_i2c_remove, +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0005-ARM-stm32mp1-r0-rc1-IIO.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0005-ARM-stm32mp1-r0-rc1-IIO.patch new file mode 100644 index 0000000..4aa5eeb --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0005-ARM-stm32mp1-r0-rc1-IIO.patch @@ -0,0 +1,5144 @@ +From 4134cd0a2ec7d17327dbdf141ec05268e195cd71 Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +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 ++#include ++#include + #include + #include + #include + #include + #include + #include ++#include ++#include + #include + #include + +@@ -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 for STMicroelectronics. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 "); ++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 + #include + #include ++#include + #include + #include + #include +@@ -22,6 +23,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -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 + #include + #include ++#include ++#include ++#include ++#include ++#include + #include + #include + #include +@@ -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 + #include + #include ++#include ++#include + #include + #include + +@@ -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 + #include + #include ++#include + #include + + 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 + #include + #include ++#include + #include + #include + +@@ -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 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0006-ARM-stm32mp1-r0-rc1-IRQ-Mailbox.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0006-ARM-stm32mp1-r0-rc1-IRQ-Mailbox.patch new file mode 100644 index 0000000..d31883e --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0006-ARM-stm32mp1-r0-rc1-IRQ-Mailbox.patch @@ -0,0 +1,154 @@ +From 358a1325502b2362a6fb74af3027f3f165684375 Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Tue, 13 Nov 2018 12:19:47 +0100 +Subject: [PATCH 06/52] ARM: stm32mp1-r0-rc1: IRQ Mailbox + +--- + drivers/irqchip/irq-stm32-exti.c | 23 ++++++++++++++++++----- + drivers/mailbox/mailbox-test.c | 26 ++++++++++++++------------ + drivers/mailbox/stm32-ipcc.c | 4 ++-- + 3 files changed, 34 insertions(+), 19 deletions(-) + +diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c +index 0a2088e..e185ed8 100644 +--- a/drivers/irqchip/irq-stm32-exti.c ++++ b/drivers/irqchip/irq-stm32-exti.c +@@ -650,11 +650,6 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, + */ + writel_relaxed(0, base + stm32_bank->imr_ofst); + writel_relaxed(0, base + stm32_bank->emr_ofst); +- writel_relaxed(0, base + stm32_bank->rtsr_ofst); +- writel_relaxed(0, base + stm32_bank->ftsr_ofst); +- writel_relaxed(~0UL, base + stm32_bank->rpr_ofst); +- if (stm32_bank->fpr_ofst != UNDEF_REG) +- writel_relaxed(~0UL, base + stm32_bank->fpr_ofst); + + pr_info("%s: bank%d, External IRQs available:%#x\n", + node->full_name, bank_idx, irqs_mask); +@@ -735,9 +730,27 @@ static int __init stm32_exti_init(const struct stm32_exti_drv_data *drv_data, + return ret; + } + ++static int stm32_exti_h_translate(struct irq_domain *d, ++ struct irq_fwspec *fwspec, ++ unsigned long *out_hwirq, ++ unsigned int *out_type) ++{ ++ if (is_of_node(fwspec->fwnode)) { ++ if (fwspec->param_count != 2) ++ return -EINVAL; ++ ++ *out_hwirq = fwspec->param[0]; ++ *out_type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ + static const struct irq_domain_ops stm32_exti_h_domain_ops = { + .alloc = stm32_exti_h_domain_alloc, + .free = irq_domain_free_irqs_common, ++ .translate = stm32_exti_h_translate, + }; + + static int +diff --git a/drivers/mailbox/mailbox-test.c b/drivers/mailbox/mailbox-test.c +index 58bfafc..4e4ac4b 100644 +--- a/drivers/mailbox/mailbox-test.c ++++ b/drivers/mailbox/mailbox-test.c +@@ -31,7 +31,6 @@ + (MBOX_MAX_MSG_LEN / MBOX_BYTES_PER_LINE)) + + static bool mbox_data_ready; +-static struct dentry *root_debugfs_dir; + + struct mbox_test_device { + struct device *dev; +@@ -45,6 +44,7 @@ struct mbox_test_device { + spinlock_t lock; + wait_queue_head_t waitq; + struct fasync_struct *async_queue; ++ struct dentry *root_debugfs_dir; + }; + + static ssize_t mbox_test_signal_write(struct file *filp, +@@ -262,16 +262,16 @@ static int mbox_test_add_debugfs(struct platform_device *pdev, + if (!debugfs_initialized()) + return 0; + +- root_debugfs_dir = debugfs_create_dir("mailbox", NULL); +- if (!root_debugfs_dir) { ++ tdev->root_debugfs_dir = debugfs_create_dir(dev_name(&pdev->dev), NULL); ++ if (!tdev->root_debugfs_dir) { + dev_err(&pdev->dev, "Failed to create Mailbox debugfs\n"); + return -EINVAL; + } + +- debugfs_create_file("message", 0600, root_debugfs_dir, ++ debugfs_create_file("message", 0600, tdev->root_debugfs_dir, + tdev, &mbox_test_message_ops); + +- debugfs_create_file("signal", 0200, root_debugfs_dir, ++ debugfs_create_file("signal", 0200, tdev->root_debugfs_dir, + tdev, &mbox_test_signal_ops); + + return 0; +@@ -363,22 +363,24 @@ static int mbox_test_probe(struct platform_device *pdev) + + /* It's okay for MMIO to be NULL */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- size = resource_size(res); + tdev->tx_mmio = devm_ioremap_resource(&pdev->dev, res); +- if (PTR_ERR(tdev->tx_mmio) == -EBUSY) ++ if (PTR_ERR(tdev->tx_mmio) == -EBUSY) { + /* if reserved area in SRAM, try just ioremap */ ++ size = resource_size(res); + tdev->tx_mmio = devm_ioremap(&pdev->dev, res->start, size); +- else if (IS_ERR(tdev->tx_mmio)) ++ } else if (IS_ERR(tdev->tx_mmio)) { + tdev->tx_mmio = NULL; ++ } + + /* If specified, second reg entry is Rx MMIO */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); +- size = resource_size(res); + tdev->rx_mmio = devm_ioremap_resource(&pdev->dev, res); +- if (PTR_ERR(tdev->rx_mmio) == -EBUSY) ++ if (PTR_ERR(tdev->rx_mmio) == -EBUSY) { ++ size = resource_size(res); + tdev->rx_mmio = devm_ioremap(&pdev->dev, res->start, size); +- else if (IS_ERR(tdev->rx_mmio)) ++ } else if (IS_ERR(tdev->rx_mmio)) { + tdev->rx_mmio = tdev->tx_mmio; ++ } + + tdev->tx_channel = mbox_test_request_channel(pdev, "tx"); + tdev->rx_channel = mbox_test_request_channel(pdev, "rx"); +@@ -416,7 +418,7 @@ static int mbox_test_remove(struct platform_device *pdev) + { + struct mbox_test_device *tdev = platform_get_drvdata(pdev); + +- debugfs_remove_recursive(root_debugfs_dir); ++ debugfs_remove_recursive(tdev->root_debugfs_dir); + + if (tdev->tx_channel) + mbox_free_channel(tdev->tx_channel); +diff --git a/drivers/mailbox/stm32-ipcc.c b/drivers/mailbox/stm32-ipcc.c +index 533b0da..cd5ceca 100644 +--- a/drivers/mailbox/stm32-ipcc.c ++++ b/drivers/mailbox/stm32-ipcc.c +@@ -276,8 +276,8 @@ static int stm32_ipcc_probe(struct platform_device *pdev) + dev_err(dev, "Failed to set wake up irq\n"); + goto err_init_wkp; + } +- } else { +- device_init_wakeup(dev, false); ++ /* disable the wakeup source, let the user enable it or not */ ++ device_set_wakeup_enable(dev, false); + } + + /* mailbox controller */ +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0007-ARM-stm32mp1-r0-rc1-INPUT-TTY.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0007-ARM-stm32mp1-r0-rc1-INPUT-TTY.patch new file mode 100644 index 0000000..8e1e08d --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0007-ARM-stm32mp1-r0-rc1-INPUT-TTY.patch @@ -0,0 +1,1099 @@ +From feb0bf2d9b83cb19c75be4cd12b3e7a584f513b5 Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Tue, 13 Nov 2018 12:21:06 +0100 +Subject: [PATCH 07/52] ARM: stm32mp1-r0-rc1: INPUT TTY + +--- + drivers/input/misc/Kconfig | 11 + + drivers/input/misc/Makefile | 2 + + drivers/input/misc/stpmic1_onkey.c | 197 ++++++++++++++++++ + drivers/tty/serial/stm32-usart.c | 398 +++++++++++++++++++++++++++++-------- + drivers/tty/serial/stm32-usart.h | 34 +++- + 5 files changed, 550 insertions(+), 92 deletions(-) + create mode 100644 drivers/input/misc/stpmic1_onkey.c + +diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig +index ca59a2b..279fb02 100644 +--- a/drivers/input/misc/Kconfig ++++ b/drivers/input/misc/Kconfig +@@ -851,4 +851,15 @@ config INPUT_SC27XX_VIBRA + To compile this driver as a module, choose M here. The module will + be called sc27xx_vibra. + ++config INPUT_STPMIC1_ONKEY ++ tristate "STPMIC1 PMIC Onkey support" ++ depends on MFD_STPMIC1 ++ help ++ Say Y to enable support of onkey embedded into STPMIC1 PMIC. onkey ++ can be used to wakeup from low power modes and force a shut-down on ++ long press. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called stpmic1_onkey. ++ + endif +diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile +index 9d0f9d1..1b44202 100644 +--- a/drivers/input/misc/Makefile ++++ b/drivers/input/misc/Makefile +@@ -71,6 +71,7 @@ obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o + obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o + obj-$(CONFIG_INPUT_SOC_BUTTON_ARRAY) += soc_button_array.o + obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o ++obj-$(CONFIG_INPUT_STPMIC1_ONKEY) += stpmic1_onkey.o + obj-$(CONFIG_INPUT_TPS65218_PWRBUTTON) += tps65218-pwrbutton.o + obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o + obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o +@@ -81,3 +82,4 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o + obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o + obj-$(CONFIG_INPUT_YEALINK) += yealink.o + obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o ++ +diff --git a/drivers/input/misc/stpmic1_onkey.c b/drivers/input/misc/stpmic1_onkey.c +new file mode 100644 +index 0000000..6a7f08b +--- /dev/null ++++ b/drivers/input/misc/stpmic1_onkey.c +@@ -0,0 +1,197 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (C) STMicroelectronics 2018 ++// Author: Pascal Paillet for STMicroelectronics. ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/** ++ * struct stpmic1_onkey - OnKey data ++ * @input_dev: pointer to input device ++ * @irq_falling: irq that we are hooked on to ++ * @irq_rising: irq that we are hooked on to ++ */ ++struct stpmic1_onkey { ++ struct input_dev *input_dev; ++ int irq_falling; ++ int irq_rising; ++}; ++ ++static irqreturn_t onkey_falling_irq(int irq, void *ponkey) ++{ ++ struct stpmic1_onkey *onkey = ponkey; ++ struct input_dev *input_dev = onkey->input_dev; ++ ++ input_report_key(input_dev, KEY_POWER, 1); ++ pm_wakeup_event(input_dev->dev.parent, 0); ++ input_sync(input_dev); ++ ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t onkey_rising_irq(int irq, void *ponkey) ++{ ++ struct stpmic1_onkey *onkey = ponkey; ++ struct input_dev *input_dev = onkey->input_dev; ++ ++ input_report_key(input_dev, KEY_POWER, 0); ++ pm_wakeup_event(input_dev->dev.parent, 0); ++ input_sync(input_dev); ++ ++ return IRQ_HANDLED; ++} ++ ++static int stpmic1_onkey_probe(struct platform_device *pdev) ++{ ++ struct stpmic1 *pmic = dev_get_drvdata(pdev->dev.parent); ++ struct device *dev = &pdev->dev; ++ struct input_dev *input_dev; ++ struct stpmic1_onkey *onkey; ++ unsigned int val, reg = 0; ++ int error; ++ ++ onkey = devm_kzalloc(dev, sizeof(*onkey), GFP_KERNEL); ++ if (!onkey) ++ return -ENOMEM; ++ ++ onkey->irq_falling = platform_get_irq_byname(pdev, "onkey-falling"); ++ if (onkey->irq_falling < 0) { ++ dev_err(dev, "failed: request IRQ onkey-falling %d\n", ++ onkey->irq_falling); ++ return onkey->irq_falling; ++ } ++ ++ onkey->irq_rising = platform_get_irq_byname(pdev, "onkey-rising"); ++ if (onkey->irq_rising < 0) { ++ dev_err(dev, "failed: request IRQ onkey-rising %d\n", ++ onkey->irq_rising); ++ return onkey->irq_rising; ++ } ++ ++ if (!device_property_read_u32(dev, "power-off-time-sec", &val)) { ++ if ((val > 0) && (val <= 16)) { ++ dev_dbg(dev, "power-off-time=%d seconds\n", val); ++ reg |= PONKEY_PWR_OFF; ++ reg |= ((16 - val) & PONKEY_TURNOFF_TIMER_MASK); ++ } else { ++ dev_err(dev, "power-off-time-sec out of range\n"); ++ return -EINVAL; ++ } ++ } ++ ++ if (device_property_present(dev, "st,onkey-clear-cc-flag")) ++ reg |= PONKEY_CC_FLAG_CLEAR; ++ ++ error = regmap_update_bits(pmic->regmap, PKEY_TURNOFF_CR, ++ PONKEY_TURNOFF_MASK, reg); ++ if (error) { ++ dev_err(dev, "PKEY_TURNOFF_CR write failed: %d\n", error); ++ return error; ++ } ++ ++ if (device_property_present(dev, "st,onkey-pu-inactive")) { ++ error = regmap_update_bits(pmic->regmap, PADS_PULL_CR, ++ PONKEY_PU_INACTIVE, ++ PONKEY_PU_INACTIVE); ++ if (error) { ++ dev_err(dev, "ONKEY Pads configuration failed: %d\n", error); ++ return error; ++ } ++ } ++ ++ input_dev = devm_input_allocate_device(dev); ++ if (!input_dev) { ++ dev_err(dev, "Can't allocate Pwr Onkey Input Device\n"); ++ return -ENOMEM; ++ } ++ ++ input_dev->name = "pmic_onkey"; ++ input_dev->phys = "pmic_onkey/input0"; ++ ++ input_set_capability(input_dev, EV_KEY, KEY_POWER); ++ ++ onkey->input_dev = input_dev; ++ ++ /* interrupt is nested in a thread */ ++ error = devm_request_threaded_irq(dev, onkey->irq_falling, NULL, ++ onkey_falling_irq, IRQF_ONESHOT, ++ dev_name(dev), onkey); ++ if (error) { ++ dev_err(dev, "Can't get IRQ Onkey Falling: %d\n", error); ++ return error; ++ } ++ ++ error = devm_request_threaded_irq(dev, onkey->irq_rising, NULL, ++ onkey_rising_irq, IRQF_ONESHOT, ++ dev_name(dev), onkey); ++ if (error) { ++ dev_err(dev, "Can't get IRQ Onkey Rising: %d\n", error); ++ return error; ++ } ++ ++ error = input_register_device(input_dev); ++ if (error) { ++ dev_err(dev, "Can't register power button: %d\n", error); ++ return error; ++ } ++ ++ platform_set_drvdata(pdev, onkey); ++ device_init_wakeup(dev, true); ++ ++ return 0; ++} ++ ++static int __maybe_unused stpmic1_onkey_suspend(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct stpmic1_onkey *onkey = platform_get_drvdata(pdev); ++ ++ if (device_may_wakeup(dev)) { ++ enable_irq_wake(onkey->irq_falling); ++ enable_irq_wake(onkey->irq_rising); ++ } ++ return 0; ++} ++ ++static int __maybe_unused stpmic1_onkey_resume(struct device *dev) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct stpmic1_onkey *onkey = platform_get_drvdata(pdev); ++ ++ if (device_may_wakeup(dev)) { ++ disable_irq_wake(onkey->irq_falling); ++ disable_irq_wake(onkey->irq_rising); ++ } ++ return 0; ++} ++ ++static SIMPLE_DEV_PM_OPS(stpmic1_onkey_pm, ++ stpmic1_onkey_suspend, ++ stpmic1_onkey_resume); ++ ++static const struct of_device_id of_stpmic1_onkey_match[] = { ++ { .compatible = "st,stpmic1-onkey" }, ++ { }, ++}; ++ ++MODULE_DEVICE_TABLE(of, of_stpmic1_onkey_match); ++ ++static struct platform_driver stpmic1_onkey_driver = { ++ .probe = stpmic1_onkey_probe, ++ .driver = { ++ .name = "stpmic1_onkey", ++ .of_match_table = of_match_ptr(of_stpmic1_onkey_match), ++ .pm = &stpmic1_onkey_pm, ++ }, ++}; ++module_platform_driver(stpmic1_onkey_driver); ++ ++MODULE_DESCRIPTION("Onkey driver for STPMIC1"); ++MODULE_AUTHOR("Pascal Paillet "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c +index e8d7a7b..5c6c3c0 100644 +--- a/drivers/tty/serial/stm32-usart.c ++++ b/drivers/tty/serial/stm32-usart.c +@@ -24,6 +24,8 @@ + #include + #include + #include ++#include ++#include + #include + #include + #include +@@ -180,14 +182,18 @@ static int stm32_pending_rx(struct uart_port *port, u32 *sr, int *last_res, + *sr = readl_relaxed(port->membase + ofs->isr); + + if (threaded && stm32_port->rx_ch) { ++ if (stm32_port->rx_dma_cb == CALLBACK_CALLED) ++ return 1; + status = dmaengine_tx_status(stm32_port->rx_ch, + stm32_port->rx_ch->cookie, + &state); + if ((status == DMA_IN_PROGRESS) && +- (*last_res != state.residue)) ++ (*last_res != state.residue)) { ++ stm32_port->rx_dma_cb = CALLBACK_IGNORED; + return 1; +- else ++ } else { + return 0; ++ } + } else if (*sr & USART_SR_RXNE) { + return 1; + } +@@ -203,8 +209,11 @@ stm32_get_char(struct uart_port *port, u32 *sr, int *last_res) + + if (stm32_port->rx_ch) { + c = stm32_port->rx_buf[RX_BUF_L - (*last_res)--]; +- if ((*last_res) == 0) ++ if ((*last_res) == 0) { + *last_res = RX_BUF_L; ++ if (stm32_port->rx_dma_cb == CALLBACK_CALLED) ++ stm32_port->rx_dma_cb = CALLBACK_NOT_CALLED; ++ } + return c; + } else { + return readl_relaxed(port->membase + ofs->rdr); +@@ -271,22 +280,8 @@ static void stm32_tx_dma_complete(void *arg) + struct uart_port *port = arg; + struct stm32_port *stm32port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32port->info->ofs; +- unsigned int isr; +- int ret; +- +- ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr, +- isr, +- (isr & USART_SR_TC), +- 10, 100000); +- +- if (ret) +- dev_err(port->dev, "terminal count not set\n"); +- +- if (ofs->icr == UNDEF_REG) +- stm32_clr_bits(port, ofs->isr, USART_SR_TC); +- else +- stm32_set_bits(port, ofs->icr, USART_CR_TC); + ++ dmaengine_terminate_async(stm32port->tx_ch); + stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); + stm32port->tx_dma_busy = false; + +@@ -294,32 +289,56 @@ static void stm32_tx_dma_complete(void *arg) + stm32_transmit_chars(port); + } + ++static void stm32_rx_dma_complete(void *arg) ++{ ++ struct uart_port *port = arg; ++ struct stm32_port *stm32port = to_stm32_port(port); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&port->lock, flags); ++ ++ /* ++ * If the dma controller is sending the data on ++ * the fly then we have CALLBACK_IGNORED ++ */ ++ ++ if (stm32port->rx_dma_cb == CALLBACK_NOT_CALLED) { ++ stm32port->rx_dma_cb = CALLBACK_CALLED; ++ stm32_receive_chars(port, true); ++ } ++ ++ spin_unlock_irqrestore(&port->lock, flags); ++} ++ + static void stm32_transmit_chars_pio(struct uart_port *port) + { + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + struct circ_buf *xmit = &port->state->xmit; +- unsigned int isr; +- int ret; + + if (stm32_port->tx_dma_busy) { + stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); + stm32_port->tx_dma_busy = false; + } + +- ret = readl_relaxed_poll_timeout_atomic(port->membase + ofs->isr, +- isr, +- (isr & USART_SR_TXE), +- 10, 100000); +- +- if (ret) +- dev_err(port->dev, "tx empty not set\n"); +- +- stm32_set_bits(port, ofs->cr1, USART_CR1_TXEIE); ++ while (!uart_circ_empty(xmit)) { ++ if (!(readl_relaxed(port->membase + ofs->isr) & USART_SR_TXE)) ++ break; ++ writel_relaxed(xmit->buf[xmit->tail], port->membase + ofs->tdr); ++ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); ++ port->icount.tx++; ++ } + +- writel_relaxed(xmit->buf[xmit->tail], port->membase + ofs->tdr); +- xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); +- port->icount.tx++; ++ if (uart_circ_empty(xmit)) ++ if (stm32_port->fifoen) ++ stm32_clr_bits(port, ofs->cr3, USART_CR3_TXFTIE); ++ else ++ stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); ++ else ++ if (stm32_port->fifoen) ++ stm32_set_bits(port, ofs->cr3, USART_CR3_TXFTIE); ++ else ++ stm32_set_bits(port, ofs->cr1, USART_CR1_TXEIE); + } + + static void stm32_transmit_chars_dma(struct uart_port *port) +@@ -377,7 +396,6 @@ static void stm32_transmit_chars_dma(struct uart_port *port) + /* Issue pending DMA TX requests */ + dma_async_issue_pending(stm32port->tx_ch); + +- stm32_clr_bits(port, ofs->isr, USART_SR_TC); + stm32_set_bits(port, ofs->cr3, USART_CR3_DMAT); + + xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1); +@@ -401,15 +419,18 @@ static void stm32_transmit_chars(struct uart_port *port) + return; + } + +- if (uart_tx_stopped(port)) { +- stm32_stop_tx(port); ++ if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { ++ if (stm32_port->fifoen) ++ stm32_clr_bits(port, ofs->cr3, USART_CR3_TXFTIE); ++ else ++ stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); + return; + } + +- if (uart_circ_empty(xmit)) { +- stm32_stop_tx(port); +- return; +- } ++ if (ofs->icr == UNDEF_REG) ++ stm32_clr_bits(port, ofs->isr, USART_SR_TC); ++ else ++ stm32_set_bits(port, ofs->icr, USART_ICR_TCCF); + + if (stm32_port->tx_ch) + stm32_transmit_chars_dma(port); +@@ -419,8 +440,12 @@ static void stm32_transmit_chars(struct uart_port *port) + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + +- if (uart_circ_empty(xmit)) +- stm32_stop_tx(port); ++ if (uart_circ_empty(xmit)) { ++ if (stm32_port->fifoen) ++ stm32_clr_bits(port, ofs->cr3, USART_CR3_TXFTIE); ++ else ++ stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); ++ } + } + + static irqreturn_t stm32_interrupt(int irq, void *ptr) +@@ -434,6 +459,10 @@ static irqreturn_t stm32_interrupt(int irq, void *ptr) + + sr = readl_relaxed(port->membase + ofs->isr); + ++ if ((sr & USART_SR_RTOF) && (ofs->icr != UNDEF_REG)) ++ writel_relaxed(USART_ICR_RTOCF, ++ port->membase + ofs->icr); ++ + if ((sr & USART_SR_WUF) && (ofs->icr != UNDEF_REG)) + writel_relaxed(USART_ICR_WUCF, + port->membase + ofs->icr); +@@ -456,11 +485,21 @@ static irqreturn_t stm32_threaded_interrupt(int irq, void *ptr) + { + struct uart_port *port = ptr; + struct stm32_port *stm32_port = to_stm32_port(port); ++ struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + + spin_lock(&port->lock); + +- if (stm32_port->rx_ch) ++ if (stm32_port->rx_ch) { ++ stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAR); ++ dma_sync_single_for_cpu(port->dev, ++ stm32_port->rx_dma_buf, ++ RX_BUF_L, DMA_FROM_DEVICE); + stm32_receive_chars(port, true); ++ dma_sync_single_for_device(port->dev, ++ stm32_port->rx_dma_buf, ++ RX_BUF_L, DMA_FROM_DEVICE); ++ stm32_set_bits(port, ofs->cr3, USART_CR3_DMAR); ++ } + + spin_unlock(&port->lock); + +@@ -472,7 +511,10 @@ static unsigned int stm32_tx_empty(struct uart_port *port) + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + +- return readl_relaxed(port->membase + ofs->isr) & USART_SR_TXE; ++ if (readl_relaxed(port->membase + ofs->isr) & USART_SR_TC) ++ return TIOCSER_TEMT; ++ ++ return 0; + } + + static void stm32_set_mctrl(struct uart_port *port, unsigned int mctrl) +@@ -498,7 +540,15 @@ static void stm32_stop_tx(struct uart_port *port) + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + +- stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); ++ if (stm32_port->fifoen) ++ stm32_clr_bits(port, ofs->cr3, USART_CR3_TXFTIE); ++ else ++ stm32_clr_bits(port, ofs->cr1, USART_CR1_TXEIE); ++ ++ if (stm32_port->tx_dma_busy) { ++ dmaengine_terminate_async(stm32_port->tx_ch); ++ stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); ++ } + } + + /* There are probably characters waiting to be transmitted. */ +@@ -512,6 +562,22 @@ static void stm32_start_tx(struct uart_port *port) + stm32_transmit_chars(port); + } + ++/* Flush the transmit buffer. */ ++static void stm32_flush_buffer(struct uart_port *port) ++{ ++ struct stm32_port *stm32_port = to_stm32_port(port); ++ struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; ++ ++ if (stm32_port->tx_ch) { ++ spin_lock(&port->lock); ++ dmaengine_terminate_async(stm32_port->tx_ch); ++ spin_unlock(&port->lock); ++ ++ stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); ++ stm32_port->tx_dma_busy = false; ++ } ++} ++ + /* Throttle the remote when input buffer is about to overflow. */ + static void stm32_throttle(struct uart_port *port) + { +@@ -520,7 +586,10 @@ static void stm32_throttle(struct uart_port *port) + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); +- stm32_clr_bits(port, ofs->cr1, USART_CR1_RXNEIE); ++ if (stm32_port->cr3_irq) ++ stm32_clr_bits(port, ofs->cr3, stm32_port->cr3_irq); ++ ++ stm32_clr_bits(port, ofs->cr1, stm32_port->cr1_irq); + spin_unlock_irqrestore(&port->lock, flags); + } + +@@ -532,7 +601,10 @@ static void stm32_unthrottle(struct uart_port *port) + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); +- stm32_set_bits(port, ofs->cr1, USART_CR1_RXNEIE); ++ if (stm32_port->cr3_irq) ++ stm32_set_bits(port, ofs->cr3, stm32_port->cr3_irq); ++ ++ stm32_set_bits(port, ofs->cr1, stm32_port->cr1_irq); + spin_unlock_irqrestore(&port->lock, flags); + } + +@@ -542,7 +614,10 @@ static void stm32_stop_rx(struct uart_port *port) + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + +- stm32_clr_bits(port, ofs->cr1, USART_CR1_RXNEIE); ++ if (stm32_port->cr3_irq) ++ stm32_clr_bits(port, ofs->cr3, stm32_port->cr3_irq); ++ ++ stm32_clr_bits(port, ofs->cr1, stm32_port->cr1_irq); + } + + /* Handle breaks - ignored by us */ +@@ -554,31 +629,41 @@ static int stm32_startup(struct uart_port *port) + { + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; +- struct stm32_usart_config *cfg = &stm32_port->info->cfg; + const char *name = to_platform_device(port->dev)->name; +- u32 val; ++ u32 val, sr, dr; + int ret; + ++ sr = readl_relaxed(port->membase + ofs->isr); ++ ++ if ((sr & USART_SR_RXNE) && (stm32_port->rx_ch)) { ++ ret = readl_relaxed_poll_timeout_atomic(port->membase + ++ ofs->rdr, ++ dr, ++ !(sr & USART_SR_RXNE), ++ 10, 100000); ++ if (ret) ++ return ret; ++ } ++ + ret = request_threaded_irq(port->irq, stm32_interrupt, + stm32_threaded_interrupt, + IRQF_NO_SUSPEND, name, port); + if (ret) + return ret; + +- if (cfg->has_wakeup && stm32_port->wakeirq >= 0) { +- ret = dev_pm_set_dedicated_wake_irq(port->dev, +- stm32_port->wakeirq); +- if (ret) { +- free_irq(port->irq, port); +- return ret; +- } +- } +- +- val = USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE; ++ val = stm32_port->cr1_irq | USART_CR1_TE | USART_CR1_RE; + if (stm32_port->fifoen) + val |= USART_CR1_FIFOEN; + stm32_set_bits(port, ofs->cr1, val); + ++ if (stm32_port->fifoen) { ++ val = readl_relaxed(port->membase + ofs->cr3); ++ val &= ~(USART_CR3_TXFTCFG_MASK | USART_CR3_RXFTCFG_MASK); ++ val |= USART_CR3_TXFTCFG_HALF << USART_CR3_TXFTCFG_SHIFT; ++ val |= USART_CR3_RXFTCFG_HALF << USART_CR3_RXFTCFG_SHIFT; ++ writel_relaxed(val, port->membase + ofs->cr3); ++ } ++ + return 0; + } + +@@ -587,15 +672,29 @@ static void stm32_shutdown(struct uart_port *port) + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + struct stm32_usart_config *cfg = &stm32_port->info->cfg; +- u32 val; ++ u32 val, isr; ++ int ret; + +- val = USART_CR1_TXEIE | USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE; ++ val = USART_CR1_TXEIE | USART_CR1_TE; ++ val |= stm32_port->cr1_irq | USART_CR1_RE; + val |= BIT(cfg->uart_enable_bit); + if (stm32_port->fifoen) + val |= USART_CR1_FIFOEN; ++ ++ ret = readl_relaxed_poll_timeout(port->membase + ofs->isr, ++ isr, ++ (isr & USART_SR_TC), ++ 10, 100000); ++ ++ if (ret) ++ dev_err(port->dev, "transmission complete not set\n"); ++ + stm32_clr_bits(port, ofs->cr1, val); + +- dev_pm_clear_wake_irq(port->dev); ++ if (stm32_port->fifoen) ++ stm32_clr_bits(port, ofs->cr3, ++ USART_CR3_TXFTIE | USART_CR3_RXFTIE); ++ + free_irq(port->irq, port); + } + +@@ -606,7 +705,7 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + struct stm32_usart_config *cfg = &stm32_port->info->cfg; + struct serial_rs485 *rs485conf = &port->rs485; +- unsigned int baud; ++ unsigned int baud, bits; + u32 usartdiv, mantissa, fraction, oversampling; + tcflag_t cflag = termios->c_cflag; + u32 cr1, cr2, cr3; +@@ -622,16 +721,50 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, + /* Stop serial port and reset value */ + writel_relaxed(0, port->membase + ofs->cr1); + +- cr1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE; ++ cr1 = USART_CR1_TE | USART_CR1_RE; + + if (stm32_port->fifoen) + cr1 |= USART_CR1_FIFOEN; + cr2 = 0; +- cr3 = 0; ++ cr3 = readl_relaxed(port->membase + ofs->cr3); ++ cr3 &= USART_CR3_TXFTIE | USART_CR3_RXFTCFG_MASK | USART_CR3_RXFTIE ++ | USART_CR3_TXFTCFG_MASK; + + if (cflag & CSTOPB) + cr2 |= USART_CR2_STOP_2B; + ++ if (ofs->rtor != UNDEF_REG && (stm32_port->rx_ch || ++ stm32_port->fifoen)) { ++ switch (cflag & CSIZE) { ++ case CS5: ++ bits = 7; ++ break; ++ case CS6: ++ bits = 8; ++ break; ++ case CS7: ++ bits = 9; ++ break; ++ default: ++ bits = 10; /* CS8 */ ++ break; ++ } ++ ++ if (cflag & CSTOPB) ++ bits++; /* 2 stop bits */ ++ ++ /* RX timeout irq to occur after last stop bit + bits */ ++ stm32_port->cr1_irq = USART_CR1_RTOIE; ++ writel_relaxed(bits, port->membase + ofs->rtor); ++ cr2 |= USART_CR2_RTOEN; ++ ++ /* Not using dma, enable fifo threshold irq */ ++ if (!stm32_port->rx_ch) ++ stm32_port->cr3_irq = USART_CR3_RXFTIE; ++ } ++ cr1 |= stm32_port->cr1_irq; ++ cr3 |= stm32_port->cr3_irq; ++ + if (cflag & PARENB) { + cr1 |= USART_CR1_PCE; + if ((cflag & CSIZE) == CS8) { +@@ -715,8 +848,10 @@ static void stm32_set_termios(struct uart_port *port, struct ktermios *termios, + } + + } else { +- cr3 &= ~(USART_CR3_DEM | USART_CR3_DEP); +- cr1 &= ~(USART_CR1_DEDT_MASK | USART_CR1_DEAT_MASK); ++ cr3 &= ~USART_CR3_DEM; ++ cr3 &= ~USART_CR3_DEP; ++ cr1 &= ~USART_CR1_DEDT_MASK; ++ cr1 &= ~USART_CR1_DEAT_MASK; + } + + writel_relaxed(cr3, port->membase + ofs->cr3); +@@ -765,13 +900,13 @@ static void stm32_pm(struct uart_port *port, unsigned int state, + + switch (state) { + case UART_PM_STATE_ON: +- clk_prepare_enable(stm32port->clk); ++ pm_runtime_get_sync(port->dev); + break; + case UART_PM_STATE_OFF: + spin_lock_irqsave(&port->lock, flags); + stm32_clr_bits(port, ofs->cr1, BIT(cfg->uart_enable_bit)); + spin_unlock_irqrestore(&port->lock, flags); +- clk_disable_unprepare(stm32port->clk); ++ pm_runtime_put_sync(port->dev); + break; + } + } +@@ -788,6 +923,7 @@ static const struct uart_ops stm32_uart_ops = { + .break_ctl = stm32_break_ctl, + .startup = stm32_startup, + .shutdown = stm32_shutdown, ++ .flush_buffer = stm32_flush_buffer, + .set_termios = stm32_set_termios, + .pm = stm32_pm, + .type = stm32_type, +@@ -808,13 +944,19 @@ static int stm32_init_port(struct stm32_port *stm32port, + port->flags = UPF_BOOT_AUTOCONF; + port->ops = &stm32_uart_ops; + port->dev = &pdev->dev; +- port->irq = platform_get_irq(pdev, 0); +- port->rs485_config = stm32_config_rs485; ++ port->irq = platform_get_irq_byname(pdev, "event"); ++ port->fifosize = stm32port->info->cfg.fifosize; + ++ port->rs485_config = stm32_config_rs485; + stm32_init_rs485(port, pdev); + +- stm32port->wakeirq = platform_get_irq(pdev, 1); ++ stm32port->wakeirq = platform_get_irq_byname(pdev, "wakeup"); + stm32port->fifoen = stm32port->info->cfg.has_fifo; ++ stm32port->console_pins = pinctrl_lookup_state(pdev->dev.pins->p, ++ "no_console_suspend"); ++ if (IS_ERR(stm32port->console_pins) ++ && PTR_ERR(stm32port->console_pins) != -ENODEV) ++ return PTR_ERR(stm32port->console_pins); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + port->membase = devm_ioremap_resource(&pdev->dev, res); +@@ -862,7 +1004,11 @@ static struct stm32_port *stm32_of_get_stm32_port(struct platform_device *pdev) + stm32_ports[id].hw_flow_control = of_property_read_bool(np, + "st,hw-flow-ctrl"); + stm32_ports[id].port.line = id; ++ stm32_ports[id].cr1_irq = USART_CR1_RXNEIE; ++ stm32_ports[id].cr3_irq = 0; + stm32_ports[id].last_res = RX_BUF_L; ++ stm32_ports[id].rx_dma_buf = 0; ++ stm32_ports[id].tx_dma_buf = 0; + return &stm32_ports[id]; + } + +@@ -890,10 +1036,8 @@ static int stm32_of_dma_rx_probe(struct stm32_port *stm32port, + + /* Request DMA RX channel */ + stm32port->rx_ch = dma_request_slave_channel(dev, "rx"); +- if (!stm32port->rx_ch) { +- dev_info(dev, "rx dma alloc failed\n"); ++ if (!stm32port->rx_ch) + return -ENODEV; +- } + stm32port->rx_buf = dma_alloc_coherent(&pdev->dev, RX_BUF_L, + &stm32port->rx_dma_buf, + GFP_KERNEL); +@@ -925,9 +1069,8 @@ static int stm32_of_dma_rx_probe(struct stm32_port *stm32port, + goto config_err; + } + +- /* No callback as dma buffer is drained on usart interrupt */ +- desc->callback = NULL; +- desc->callback_param = NULL; ++ desc->callback = stm32_rx_dma_complete; ++ desc->callback_param = port; + + /* Push current DMA transaction in the pending queue */ + cookie = dmaengine_submit(desc); +@@ -962,10 +1105,8 @@ static int stm32_of_dma_tx_probe(struct stm32_port *stm32port, + + /* Request DMA TX channel */ + stm32port->tx_ch = dma_request_slave_channel(dev, "tx"); +- if (!stm32port->tx_ch) { +- dev_info(dev, "tx dma alloc failed\n"); ++ if (!stm32port->tx_ch) + return -ENODEV; +- } + stm32port->tx_buf = dma_alloc_coherent(&pdev->dev, TX_BUF_L, + &stm32port->tx_dma_buf, + GFP_KERNEL); +@@ -1024,11 +1165,18 @@ static int stm32_serial_probe(struct platform_device *pdev) + ret = device_init_wakeup(&pdev->dev, true); + if (ret) + goto err_uninit; ++ ++ ret = dev_pm_set_dedicated_wake_irq(&pdev->dev, ++ stm32port->wakeirq); ++ if (ret) ++ goto err_nowup; ++ ++ device_set_wakeup_enable(&pdev->dev, false); + } + + ret = uart_add_one_port(&stm32_usart_driver, &stm32port->port); + if (ret) +- goto err_nowup; ++ goto err_wirq; + + ret = stm32_of_dma_rx_probe(stm32port, pdev); + if (ret) +@@ -1040,8 +1188,17 @@ static int stm32_serial_probe(struct platform_device *pdev) + + platform_set_drvdata(pdev, &stm32port->port); + ++ pm_runtime_get_noresume(&pdev->dev); ++ pm_runtime_set_active(&pdev->dev); ++ pm_runtime_enable(&pdev->dev); ++ pm_runtime_put_sync(&pdev->dev); ++ + return 0; + ++err_wirq: ++ if (stm32port->info->cfg.has_wakeup && stm32port->wakeirq >= 0) ++ dev_pm_clear_wake_irq(&pdev->dev); ++ + err_nowup: + if (stm32port->info->cfg.has_wakeup && stm32port->wakeirq >= 0) + device_init_wakeup(&pdev->dev, false); +@@ -1058,11 +1215,16 @@ static int stm32_serial_remove(struct platform_device *pdev) + struct stm32_port *stm32_port = to_stm32_port(port); + struct stm32_usart_offsets *ofs = &stm32_port->info->ofs; + struct stm32_usart_config *cfg = &stm32_port->info->cfg; ++ int err; ++ ++ pm_runtime_get_sync(&pdev->dev); + + stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAR); + +- if (stm32_port->rx_ch) ++ if (stm32_port->rx_ch) { ++ dmaengine_terminate_async(stm32_port->rx_ch); + dma_release_channel(stm32_port->rx_ch); ++ } + + if (stm32_port->rx_dma_buf) + dma_free_coherent(&pdev->dev, +@@ -1071,20 +1233,29 @@ static int stm32_serial_remove(struct platform_device *pdev) + + stm32_clr_bits(port, ofs->cr3, USART_CR3_DMAT); + +- if (stm32_port->tx_ch) ++ if (stm32_port->tx_ch) { ++ dmaengine_terminate_async(stm32_port->tx_ch); + dma_release_channel(stm32_port->tx_ch); ++ } + + if (stm32_port->tx_dma_buf) + dma_free_coherent(&pdev->dev, + TX_BUF_L, stm32_port->tx_buf, + stm32_port->tx_dma_buf); + +- if (cfg->has_wakeup && stm32_port->wakeirq >= 0) ++ if (cfg->has_wakeup && stm32_port->wakeirq >= 0) { ++ dev_pm_clear_wake_irq(&pdev->dev); + device_init_wakeup(&pdev->dev, false); ++ } + + clk_disable_unprepare(stm32_port->clk); + +- return uart_remove_one_port(&stm32_usart_driver, port); ++ err = uart_remove_one_port(&stm32_usart_driver, port); ++ ++ pm_runtime_disable(&pdev->dev); ++ pm_runtime_put_noidle(&pdev->dev); ++ ++ return err; + } + + +@@ -1215,14 +1386,41 @@ static void stm32_serial_enable_wakeup(struct uart_port *port, bool enable) + static int stm32_serial_suspend(struct device *dev) + { + struct uart_port *port = dev_get_drvdata(dev); ++ struct stm32_port *stm32_port = to_stm32_port(port); ++ struct tty_struct *tty = port->state->port.tty; + +- uart_suspend_port(&stm32_usart_driver, port); ++ if (tty) { ++ struct device *tty_dev = tty->dev; ++ ++ if (tty_dev && (device_may_wakeup(tty_dev) ++ != device_may_wakeup(dev))) { ++ dev_err(port->dev, ++ "UART and TTY wakeup are not coherent\n"); ++ return -EINVAL; ++ } ++ } + + if (device_may_wakeup(dev)) + stm32_serial_enable_wakeup(port, true); + else + stm32_serial_enable_wakeup(port, false); + ++ uart_suspend_port(&stm32_usart_driver, port); ++ ++ if (uart_console(port) && !console_suspend_enabled) { ++ if (IS_ERR(stm32_port->console_pins)) { ++ dev_err(dev, "no_console_suspend pinctrl not found\n"); ++ return PTR_ERR(stm32_port->console_pins); ++ } ++ ++ pinctrl_select_state(dev->pins->p, stm32_port->console_pins); ++ } else { ++ if (device_may_wakeup(dev)) ++ pinctrl_pm_select_idle_state(dev); ++ else ++ pinctrl_pm_select_sleep_state(dev); ++ } ++ + return 0; + } + +@@ -1230,6 +1428,8 @@ static int stm32_serial_resume(struct device *dev) + { + struct uart_port *port = dev_get_drvdata(dev); + ++ pinctrl_pm_select_default_state(dev); ++ + if (device_may_wakeup(dev)) + stm32_serial_enable_wakeup(port, false); + +@@ -1237,7 +1437,31 @@ static int stm32_serial_resume(struct device *dev) + } + #endif /* CONFIG_PM_SLEEP */ + ++#ifdef CONFIG_PM ++static int stm32_serial_runtime_suspend(struct device *dev) ++{ ++ struct uart_port *port = dev_get_drvdata(dev); ++ struct stm32_port *stm32port = container_of(port, ++ struct stm32_port, port); ++ ++ clk_disable_unprepare(stm32port->clk); ++ ++ return 0; ++} ++ ++static int stm32_serial_runtime_resume(struct device *dev) ++{ ++ struct uart_port *port = dev_get_drvdata(dev); ++ struct stm32_port *stm32port = container_of(port, ++ struct stm32_port, port); ++ ++ return clk_prepare_enable(stm32port->clk); ++} ++#endif /* CONFIG_PM */ ++ + static const struct dev_pm_ops stm32_serial_pm_ops = { ++ SET_RUNTIME_PM_OPS(stm32_serial_runtime_suspend, ++ stm32_serial_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(stm32_serial_suspend, stm32_serial_resume) + }; + +diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h +index 6f294e2..f1f5c1c 100644 +--- a/drivers/tty/serial/stm32-usart.h ++++ b/drivers/tty/serial/stm32-usart.h +@@ -27,6 +27,7 @@ struct stm32_usart_config { + bool has_7bits_data; + bool has_wakeup; + bool has_fifo; ++ int fifosize; + }; + + struct stm32_usart_info { +@@ -54,6 +55,7 @@ struct stm32_usart_info stm32f4_info = { + .cfg = { + .uart_enable_bit = 13, + .has_7bits_data = false, ++ .fifosize = 1, + } + }; + +@@ -74,6 +76,7 @@ struct stm32_usart_info stm32f7_info = { + .cfg = { + .uart_enable_bit = 0, + .has_7bits_data = true, ++ .fifosize = 1, + } + }; + +@@ -96,6 +99,7 @@ struct stm32_usart_info stm32h7_info = { + .has_7bits_data = true, + .has_wakeup = true, + .has_fifo = true, ++ .fifosize = 16, + } + }; + +@@ -125,9 +129,6 @@ struct stm32_usart_info stm32h7_info = { + /* Dummy bits */ + #define USART_SR_DUMMY_RX BIT(16) + +-/* USART_ICR (F7) */ +-#define USART_CR_TC BIT(6) +- + /* USART_DR */ + #define USART_DR_MASK GENMASK(8, 0) + +@@ -209,6 +210,19 @@ struct stm32_usart_info stm32h7_info = { + #define USART_CR3_WUS_MASK GENMASK(21, 20) /* H7 */ + #define USART_CR3_WUS_START_BIT BIT(21) /* H7 */ + #define USART_CR3_WUFIE BIT(22) /* H7 */ ++#define USART_CR3_TXFTIE BIT(23) /* H7 */ ++#define USART_CR3_TCBGTIE BIT(24) /* H7 */ ++#define USART_CR3_RXFTCFG_MASK GENMASK(27, 25) /* H7 */ ++#define USART_CR3_RXFTCFG_SHIFT 25 /* H7 */ ++#define USART_CR3_RXFTIE BIT(28) /* H7 */ ++#define USART_CR3_TXFTCFG_MASK GENMASK(31, 29) /* H7 */ ++#define USART_CR3_TXFTCFG_SHIFT 29 /* H7 */ ++ ++/* TX FIFO threashold set to half of its depth */ ++#define USART_CR3_TXFTCFG_HALF 0x2 ++ ++/* RX FIFO threashold set to half of its depth */ ++#define USART_CR3_RXFTCFG_HALF 0x2 + + /* USART_GTPR */ + #define USART_GTPR_PSC_MASK GENMASK(7, 0) +@@ -242,9 +256,15 @@ struct stm32_usart_info stm32h7_info = { + #define STM32_SERIAL_NAME "ttySTM" + #define STM32_MAX_PORTS 8 + +-#define RX_BUF_L 200 /* dma rx buffer length */ ++#define RX_BUF_L 160 /* dma rx buffer length */ + #define RX_BUF_P RX_BUF_L /* dma rx buffer period */ +-#define TX_BUF_L 200 /* dma tx buffer length */ ++#define TX_BUF_L RX_BUF_L /* dma tx buffer length */ ++ ++enum dma_cb { ++ CALLBACK_NOT_CALLED, ++ CALLBACK_CALLED, ++ CALLBACK_IGNORED, ++}; + + struct stm32_port { + struct uart_port port; +@@ -256,11 +276,15 @@ struct stm32_port { + struct dma_chan *tx_ch; /* dma tx channel */ + dma_addr_t tx_dma_buf; /* dma tx buffer bus address */ + unsigned char *tx_buf; /* dma tx buffer cpu address */ ++ u32 cr1_irq; /* USART_CR1_RXNEIE or RTOIE */ ++ u32 cr3_irq; /* USART_CR3_RXFTIE */ + int last_res; ++ enum dma_cb rx_dma_cb; /* dma rx callback status */ + bool tx_dma_busy; /* dma tx busy */ + bool hw_flow_control; + bool fifoen; + int wakeirq; ++ struct pinctrl_state *console_pins; + }; + + static struct stm32_port stm32_ports[STM32_MAX_PORTS]; +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0008-ARM-stm32mp1-r0-rc1-MFD.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0008-ARM-stm32mp1-r0-rc1-MFD.patch new file mode 100644 index 0000000..36b8687 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0008-ARM-stm32mp1-r0-rc1-MFD.patch @@ -0,0 +1,1735 @@ +From 254a1f3c99a65578044558234a01b4fbc180b3d6 Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Tue, 13 Nov 2018 12:22:30 +0100 +Subject: [PATCH 08/52] ARM: stm32mp1-r0-rc1: MFD + +--- + drivers/mfd/Kconfig | 31 ++ + drivers/mfd/Makefile | 4 +- + drivers/mfd/stm32-pwr.c | 287 ++++++++++++++++ + drivers/mfd/stmfx.c | 626 +++++++++++++++++++++++++++++++++++ + drivers/mfd/stpmic1.c | 413 +++++++++++++++++++++++ + include/dt-bindings/mfd/st,stpmic1.h | 46 +++ + include/linux/mfd/stmfx.h | 27 ++ + include/linux/mfd/stpmic1.h | 213 ++++++++++++ + 8 files changed, 1646 insertions(+), 1 deletion(-) + create mode 100644 drivers/mfd/stm32-pwr.c + create mode 100644 drivers/mfd/stmfx.c + create mode 100644 drivers/mfd/stpmic1.c + create mode 100644 include/dt-bindings/mfd/st,stpmic1.h + create mode 100644 include/linux/mfd/stmfx.h + create mode 100644 include/linux/mfd/stpmic1.h + +diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig +index 11841f4..cc25a19 100644 +--- a/drivers/mfd/Kconfig ++++ b/drivers/mfd/Kconfig +@@ -1855,6 +1855,37 @@ config MFD_STM32_TIMERS + for PWM and IIO Timer. This driver allow to share the + registers between the others drivers. + ++config MFD_STM32MP1_PWR ++ bool "STM32MP1 wake-up pins" ++ depends on MACH_STM32MP157 ++ default y ++ ++config MFD_STMFX ++ tristate "Support for STMicroelectronics Multi-Function eXpander (STMFX)" ++ depends on I2C ++ depends on OF || COMPILE_TEST ++ select MFD_CORE ++ select REGMAP_I2C ++ help ++ Support for the STMicroelectronics Multi-Function eXpander. ++ ++ This driver provides common support for accessing the device, ++ additional drivers must be enabled in order to use the functionality ++ of the device. ++ ++config MFD_STPMIC1 ++ tristate "Support for STPMIC1 PMIC" ++ depends on (I2C=y && OF) ++ select REGMAP_I2C ++ select REGMAP_IRQ ++ select MFD_CORE ++ help ++ Support for ST Microelectronics STPMIC1 PMIC. STPMIC1 MFD driver is ++ the core driver for STPMIC1 component that mainly handles interrupts. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called stpmic1. ++ + menu "Multimedia Capabilities Port drivers" + depends on ARCH_SA1100 + +diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile +index 5856a94..d794a2d 100644 +--- a/drivers/mfd/Makefile ++++ b/drivers/mfd/Makefile +@@ -232,12 +232,14 @@ obj-$(CONFIG_INTEL_SOC_PMIC_CHTDC_TI) += intel_soc_pmic_chtdc_ti.o + obj-$(CONFIG_MFD_MT6397) += mt6397-core.o + + obj-$(CONFIG_MFD_ALTERA_A10SR) += altera-a10sr.o ++obj-$(CONFIG_MFD_STPMIC1) += stpmic1.o + obj-$(CONFIG_MFD_SUN4I_GPADC) += sun4i-gpadc.o + + obj-$(CONFIG_MFD_STM32_LPTIMER) += stm32-lptimer.o + obj-$(CONFIG_MFD_STM32_TIMERS) += stm32-timers.o ++obj-$(CONFIG_MFD_STM32MP1_PWR) += stm32-pwr.o + obj-$(CONFIG_MFD_MXS_LRADC) += mxs-lradc.o + obj-$(CONFIG_MFD_SC27XX_PMIC) += sprd-sc27xx-spi.o + obj-$(CONFIG_RAVE_SP_CORE) += rave-sp.o + obj-$(CONFIG_MFD_ROHM_BD718XX) += rohm-bd718x7.o +- ++obj-$(CONFIG_MFD_STMFX) += stmfx.o +diff --git a/drivers/mfd/stm32-pwr.c b/drivers/mfd/stm32-pwr.c +new file mode 100644 +index 0000000..377e2f5 +--- /dev/null ++++ b/drivers/mfd/stm32-pwr.c +@@ -0,0 +1,287 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) STMicroelectronics 2017 - All Rights Reserved ++ * Author: Pascal Paillet for STMicroelectronics. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#define NB_WAKEUPPINS 6 ++ ++#define STM32_SVC_PWR 0x82001001 ++#define STM32_WRITE 0x1 ++#define STM32_SET_BITS 0x2 ++#define STM32_CLEAR_BITS 0x3 ++ ++#define SMC_PWR_BASE 0x50001000 ++// PWR Registers ++#define WKUPCR 0x20 ++#define WKUPFR 0x24 ++#define MPUWKUPENR 0x28 ++ ++#define WKUP_FLAGS_MASK GENMASK(5, 0) ++ ++// WKUPCR bits definition ++#define WKUP_EDGE_SHIFT 8 ++#define WKUP_PULL_SHIFT 16 ++#define WKUP_PULL_MASK GENMASK(1, 0) ++ ++enum wkup_pull_setting { ++ WKUP_NO_PULL = 0, ++ WKUP_PULL_UP, ++ WKUP_PULL_DOWN, ++ WKUP_PULL_RESERVED ++}; ++ ++#define SMC(class, op, offset, val) \ ++{ \ ++ struct arm_smccc_res res; \ ++ arm_smccc_smc(class, op, SMC_PWR_BASE + offset, val, \ ++ 0, 0, 0, 0, &res); \ ++} \ ++ ++struct stm32_pwr_data { ++ struct device *dev; /* self device */ ++ void __iomem *base; /* IO Memory base address */ ++ struct irq_domain *domain; /* Domain for this controller */ ++ int irq; /* Parent interrupt */ ++}; ++ ++static void stm32_pwr_irq_ack(struct irq_data *d) ++{ ++ struct stm32_pwr_data *priv = d->domain->host_data; ++ ++ dev_dbg(priv->dev, "irq:%lu\n", d->hwirq); ++ SMC(STM32_SVC_PWR, STM32_SET_BITS, WKUPCR, BIT(d->hwirq)); ++} ++ ++static void stm32_pwr_irq_mask(struct irq_data *d) ++{ ++ struct stm32_pwr_data *priv = d->domain->host_data; ++ ++ dev_dbg(priv->dev, "irq:%lu\n", d->hwirq); ++ SMC(STM32_SVC_PWR, STM32_CLEAR_BITS, MPUWKUPENR, BIT(d->hwirq)); ++} ++ ++static void stm32_pwr_irq_unmask(struct irq_data *d) ++{ ++ struct stm32_pwr_data *priv = d->domain->host_data; ++ ++ dev_dbg(priv->dev, "irq:%lu\n", d->hwirq); ++ SMC(STM32_SVC_PWR, STM32_SET_BITS, MPUWKUPENR, BIT(d->hwirq)); ++} ++ ++static int stm32_pwr_irq_set_wake(struct irq_data *d, unsigned int on) ++{ ++ struct stm32_pwr_data *priv = d->domain->host_data; ++ ++ dev_dbg(priv->dev, "irq:%lu on:%d\n", d->hwirq, on); ++ if (on) ++ enable_irq_wake(priv->irq); ++ else ++ disable_irq_wake(priv->irq); ++ ++ return 0; ++} ++ ++static int stm32_pwr_irq_set_type(struct irq_data *d, unsigned int flow_type) ++{ ++ struct stm32_pwr_data *priv = d->domain->host_data; ++ int pin_id = d->hwirq; ++ u32 wkupcr; ++ int en; ++ ++ dev_dbg(priv->dev, "irq:%lu\n", d->hwirq); ++ ++ en = readl_relaxed(priv->base + MPUWKUPENR) & BIT(pin_id); ++ /* reference manual request to disable the wakeup pin while ++ * changing the edge detection setting ++ */ ++ if (en) ++ stm32_pwr_irq_mask(d); ++ ++ wkupcr = readl_relaxed(priv->base + WKUPCR); ++ switch (flow_type & IRQ_TYPE_SENSE_MASK) { ++ case IRQF_TRIGGER_FALLING: ++ wkupcr |= (1 << (WKUP_EDGE_SHIFT + pin_id)); ++ break; ++ case IRQF_TRIGGER_RISING: ++ wkupcr &= ~(1 << (WKUP_EDGE_SHIFT + pin_id)); ++ break; ++ default: ++ return -EINVAL; ++ } ++ SMC(STM32_SVC_PWR, STM32_WRITE, WKUPCR, wkupcr); ++ ++ if (en) ++ stm32_pwr_irq_unmask(d); ++ ++ return 0; ++} ++ ++static struct irq_chip stm32_pwr_irq_chip = { ++ .name = "stm32_pwr-irq", ++ .irq_ack = stm32_pwr_irq_ack, ++ .irq_mask = stm32_pwr_irq_mask, ++ .irq_unmask = stm32_pwr_irq_unmask, ++ .irq_set_type = stm32_pwr_irq_set_type, ++ .irq_set_wake = stm32_pwr_irq_set_wake, ++}; ++ ++static int stm32_pwr_irq_map(struct irq_domain *h, unsigned int virq, ++ irq_hw_number_t hw) ++{ ++ irq_set_chip_and_handler(virq, &stm32_pwr_irq_chip, handle_edge_irq); ++ return 0; ++} ++ ++static int stm32_pwr_irq_set_pull_config(struct irq_domain *d, int pin_id, ++ enum wkup_pull_setting config) ++{ ++ struct stm32_pwr_data *priv = d->host_data; ++ u32 wkupcr; ++ ++ dev_dbg(priv->dev, "irq:%d pull config:0x%x\n", pin_id, config); ++ ++ if (config >= WKUP_PULL_RESERVED) { ++ dev_err(priv->dev, "%s: bad irq pull config\n", __func__); ++ return -EINVAL; ++ } ++ ++ wkupcr = readl_relaxed(priv->base + WKUPCR); ++ wkupcr &= ~((WKUP_PULL_MASK) << (WKUP_PULL_SHIFT + pin_id * 2)); ++ wkupcr |= (config & WKUP_PULL_MASK) << (WKUP_PULL_SHIFT + pin_id * 2); ++ SMC(STM32_SVC_PWR, STM32_WRITE, WKUPCR, wkupcr); ++ ++ return 0; ++} ++ ++static int stm32_pwr_xlate(struct irq_domain *d, struct device_node *ctrlr, ++ const u32 *intspec, unsigned int intsize, ++ irq_hw_number_t *out_hwirq, unsigned int *out_type) ++{ ++ struct stm32_pwr_data *priv = d->host_data; ++ ++ if (WARN_ON(intsize < 3)) { ++ dev_err(priv->dev, "%s: bad irq config parameters\n", __func__); ++ return -EINVAL; ++ } ++ ++ *out_hwirq = intspec[0]; ++ *out_type = intspec[1] & (IRQ_TYPE_SENSE_MASK); ++ ++ return stm32_pwr_irq_set_pull_config(d, intspec[0], intspec[2]); ++} ++ ++static const struct irq_domain_ops stm32_pwr_irq_domain_ops = { ++ .map = stm32_pwr_irq_map, ++ .xlate = stm32_pwr_xlate, ++}; ++ ++/* ++ * Handler for the cascaded IRQ. ++ */ ++static void stm32_pwr_handle_irq(struct irq_desc *desc) ++{ ++ struct stm32_pwr_data *priv = irq_desc_get_handler_data(desc); ++ struct irq_chip *chip = irq_desc_get_chip(desc); ++ u32 wkupfr, wkupenr, i; ++ ++ chained_irq_enter(chip, desc); ++ ++ wkupfr = readl_relaxed(priv->base + WKUPFR); ++ wkupenr = readl_relaxed(priv->base + MPUWKUPENR); ++ for (i = 0; i < NB_WAKEUPPINS; i++) { ++ if ((wkupfr & BIT(i)) && (wkupenr & BIT(i))) { ++ dev_dbg(priv->dev, "handle wkup irq:%d\n", i); ++ generic_handle_irq(irq_find_mapping(priv->domain, i)); ++ } ++ } ++ ++ chained_irq_exit(chip, desc); ++} ++ ++static int stm32_pwr_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *np = pdev->dev.of_node; ++ struct stm32_pwr_data *priv; ++ ++ struct resource *res; ++ int ret; ++ ++ priv = devm_kzalloc(dev, sizeof(struct stm32_pwr_data), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, priv); ++ priv->dev = dev; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ priv->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(priv->base)) { ++ dev_err(dev, "%s: Unable to map IO memory\n", __func__); ++ return PTR_ERR(priv->base); ++ } ++ ++ /* Disable all wake-up pins */ ++ SMC(STM32_SVC_PWR, STM32_WRITE, MPUWKUPENR, 0); ++ /* Clear all interrupts flags */ ++ SMC(STM32_SVC_PWR, STM32_SET_BITS, WKUPCR, WKUP_FLAGS_MASK); ++ ++ priv->domain = irq_domain_add_linear(np, NB_WAKEUPPINS, ++ &stm32_pwr_irq_domain_ops, priv); ++ if (!priv->domain) { ++ dev_err(dev, "%s: Unable to add irq domain!\n", __func__); ++ goto out; ++ } ++ ++ ret = platform_get_irq(pdev, 0); ++ if (ret < 0) { ++ dev_err(dev, "failed to get PWR IRQ\n"); ++ goto err; ++ } ++ priv->irq = ret; ++ ++ irq_set_chained_handler_and_data(priv->irq, ++ stm32_pwr_handle_irq, priv); ++ ++out: ++ return 0; ++err: ++ irq_domain_remove(priv->domain); ++ return ret; ++} ++ ++static int stm32_pwr_remove(struct platform_device *pdev) ++{ ++ struct stm32_pwr_data *priv = platform_get_drvdata(pdev); ++ ++ irq_domain_remove(priv->domain); ++ return 0; ++} ++ ++static const struct of_device_id stm32_pwr_match[] = { ++ { .compatible = "st,stm32mp1-pwr" }, ++ {}, ++}; ++ ++static struct platform_driver stm32_pwr_driver = { ++ .probe = stm32_pwr_probe, ++ .remove = stm32_pwr_remove, ++ .driver = { ++ .name = "stm32-pwr", ++ .owner = THIS_MODULE, ++ .of_match_table = stm32_pwr_match, ++ }, ++}; ++ ++static int __init stm32_pwr_init(void) ++{ ++ return platform_driver_register(&stm32_pwr_driver); ++} ++arch_initcall(stm32_pwr_init); +diff --git a/drivers/mfd/stmfx.c b/drivers/mfd/stmfx.c +new file mode 100644 +index 0000000..cfd4fca +--- /dev/null ++++ b/drivers/mfd/stmfx.c +@@ -0,0 +1,626 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Driver for STMicroelectronics Multi-Function eXpander (STMFX) core ++ * ++ * Copyright (C) 2018 STMicroelectronics ++ * Author(s): Amelie Delaunay . ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* General */ ++#define STMFX_REG_CHIP_ID 0x00 /* R */ ++#define STMFX_REG_FW_VERSION_MSB 0x01 /* R */ ++#define STMFX_REG_FW_VERSION_LSB 0x02 /* R */ ++#define STMFX_REG_SYS_CTRL 0x40 /* RW */ ++/* IRQ output management */ ++#define STMFX_REG_IRQ_OUT_PIN 0x41 /* RW */ ++#define STMFX_REG_IRQ_SRC_EN 0x42 /* RW */ ++#define STMFX_REG_IRQ_PENDING 0x08 /* R */ ++#define STMFX_REG_IRQ_ACK 0x44 /* RW */ ++/* GPIO management */ ++#define STMFX_REG_IRQ_GPI_PENDING1 0x0C /* R */ ++#define STMFX_REG_IRQ_GPI_PENDING2 0x0D /* R */ ++#define STMFX_REG_IRQ_GPI_PENDING3 0x0E /* R */ ++#define STMFX_REG_GPIO_STATE1 0x10 /* R */ ++#define STMFX_REG_GPIO_STATE2 0x11 /* R */ ++#define STMFX_REG_GPIO_STATE3 0x12 /* R */ ++#define STMFX_REG_IRQ_GPI_SRC1 0x48 /* RW */ ++#define STMFX_REG_IRQ_GPI_SRC2 0x49 /* RW */ ++#define STMFX_REG_IRQ_GPI_SRC3 0x4A /* RW */ ++#define STMFX_REG_GPO_SET1 0x6C /* RW */ ++#define STMFX_REG_GPO_SET2 0x6D /* RW */ ++#define STMFX_REG_GPO_SET3 0x6E /* RW */ ++#define STMFX_REG_GPO_CLR1 0x70 /* RW */ ++#define STMFX_REG_GPO_CLR2 0x71 /* RW */ ++#define STMFX_REG_GPO_CLR3 0x72 /* RW */ ++ ++#define STMFX_REG_MAX 0xB0 ++ ++/* MFX boot time is around 10ms, so after reset, we have to wait this delay */ ++#define STMFX_BOOT_TIME_MS 10 ++ ++/* STMFX_REG_CHIP_ID bitfields */ ++#define STMFX_REG_CHIP_ID_MASK GENMASK(7, 0) ++ ++/* STMFX_REG_SYS_CTRL bitfields */ ++#define STMFX_REG_SYS_CTRL_GPIO_EN BIT(0) ++#define STMFX_REG_SYS_CTRL_TS_EN BIT(1) ++#define STMFX_REG_SYS_CTRL_IDD_EN BIT(2) ++#define STMFX_REG_SYS_CTRL_ALTGPIO_EN BIT(3) ++#define STMFX_REG_SYS_CTRL_SWRST BIT(7) ++ ++/* STMFX_REG_IRQ_OUT_PIN bitfields */ ++#define STMFX_REG_IRQ_OUT_PIN_TYPE BIT(0) /* 0-OD 1-PP */ ++#define STMFX_REG_IRQ_OUT_PIN_POL BIT(1) /* 0-active LOW 1-active HIGH */ ++ ++/* STMFX_REG_IRQ_(SRC_EN/PENDING/ACK) bit shift */ ++enum stmfx_irqs { ++ STMFX_REG_IRQ_SRC_EN_GPIO = 0, ++ STMFX_REG_IRQ_SRC_EN_IDD, ++ STMFX_REG_IRQ_SRC_EN_ERROR, ++ STMFX_REG_IRQ_SRC_EN_TS_DET, ++ STMFX_REG_IRQ_SRC_EN_TS_NE, ++ STMFX_REG_IRQ_SRC_EN_TS_TH, ++ STMFX_REG_IRQ_SRC_EN_TS_FULL, ++ STMFX_REG_IRQ_SRC_EN_TS_OVF, ++ STMFX_REG_IRQ_SRC_MAX, ++}; ++ ++/** ++ * struct stmfx_ddata - STMFX MFD private structure ++ * @stmfx: state holder with device for logs and register map ++ * @vdd: STMFX power supply ++ * @irq_domain: IRQ domain ++ * @lock: IRQ bus lock ++ * @irq_src: cache of IRQ_SRC_EN register for bus_lock ++ * @bkp_sysctrl: backup of SYS_CTRL register for suspend/resume ++ * @bkp_irqoutpin: backup of IRQ_OUT_PIN register for suspend/resume ++ */ ++struct stmfx_ddata { ++ struct stmfx *stmfx; ++ struct regulator *vdd; ++ struct irq_domain *irq_domain; ++ struct mutex lock; /* IRQ bus lock */ ++ u8 irq_src; ++#ifdef CONFIG_PM ++ u8 bkp_sysctrl; ++ u8 bkp_irqoutpin; ++#endif ++}; ++ ++static u8 stmfx_func_to_mask(u32 func) ++{ ++ u8 mask = 0; ++ ++ if (func & STMFX_FUNC_GPIO) ++ mask |= STMFX_REG_SYS_CTRL_GPIO_EN; ++ ++ if ((func & STMFX_FUNC_ALTGPIO_LOW) || (func & STMFX_FUNC_ALTGPIO_HIGH)) ++ mask |= STMFX_REG_SYS_CTRL_ALTGPIO_EN; ++ ++ if (func & STMFX_FUNC_TS) ++ mask |= STMFX_REG_SYS_CTRL_TS_EN; ++ ++ if (func & STMFX_FUNC_IDD) ++ mask |= STMFX_REG_SYS_CTRL_IDD_EN; ++ ++ return mask; ++} ++ ++int stmfx_function_enable(struct stmfx *stmfx, u32 func) ++{ ++ u32 sys_ctrl; ++ u8 mask; ++ int ret; ++ ++ ret = regmap_read(stmfx->map, STMFX_REG_SYS_CTRL, &sys_ctrl); ++ if (ret) ++ return ret; ++ ++ /* ++ * IDD and TS have priority in STMFX FW, so if IDD and TS are enabled, ++ * ALTGPIO function is disabled by STMFX FW. If IDD or TS is enabled, ++ * the number of aGPIO available decreases. To avoid GPIO management ++ * disturbance, abort IDD or TS function enable in this case. ++ */ ++ if (((func & STMFX_FUNC_IDD) || (func & STMFX_FUNC_TS)) && ++ (sys_ctrl & STMFX_REG_SYS_CTRL_ALTGPIO_EN)) { ++ dev_err(stmfx->dev, "ALTGPIO function already enabled\n"); ++ return -EBUSY; ++ } ++ ++ /* If TS is enabled, aGPIO[3:0] cannot be used */ ++ if ((func & STMFX_FUNC_ALTGPIO_LOW) && ++ (sys_ctrl & STMFX_REG_SYS_CTRL_TS_EN)) { ++ dev_err(stmfx->dev, "TS in use, aGPIO[3:0] unavailable\n"); ++ return -EBUSY; ++ } ++ ++ /* If IDD is enabled, aGPIO[7:4] cannot be used */ ++ if ((func & STMFX_FUNC_ALTGPIO_HIGH) && ++ (sys_ctrl & STMFX_REG_SYS_CTRL_IDD_EN)) { ++ dev_err(stmfx->dev, "IDD in use, aGPIO[7:4] unavailable\n"); ++ return -EBUSY; ++ } ++ ++ mask = stmfx_func_to_mask(func); ++ ++ return regmap_update_bits(stmfx->map, STMFX_REG_SYS_CTRL, mask, mask); ++} ++EXPORT_SYMBOL_GPL(stmfx_function_enable); ++ ++int stmfx_function_disable(struct stmfx *stmfx, u32 func) ++{ ++ u8 mask = stmfx_func_to_mask(func); ++ ++ return regmap_update_bits(stmfx->map, STMFX_REG_SYS_CTRL, mask, 0); ++} ++EXPORT_SYMBOL_GPL(stmfx_function_disable); ++ ++static void stmfx_irq_bus_lock(struct irq_data *data) ++{ ++ struct stmfx_ddata *ddata = irq_data_get_irq_chip_data(data); ++ ++ mutex_lock(&ddata->lock); ++} ++ ++static void stmfx_irq_bus_sync_unlock(struct irq_data *data) ++{ ++ struct stmfx_ddata *ddata = irq_data_get_irq_chip_data(data); ++ ++ regmap_write(ddata->stmfx->map, STMFX_REG_IRQ_SRC_EN, ddata->irq_src); ++ ++ mutex_unlock(&ddata->lock); ++} ++ ++static void stmfx_irq_mask(struct irq_data *data) ++{ ++ struct stmfx_ddata *ddata = irq_data_get_irq_chip_data(data); ++ ++ ddata->irq_src &= ~BIT(data->hwirq % 8); ++} ++ ++static void stmfx_irq_unmask(struct irq_data *data) ++{ ++ struct stmfx_ddata *ddata = irq_data_get_irq_chip_data(data); ++ ++ ddata->irq_src |= BIT(data->hwirq % 8); ++} ++ ++static struct irq_chip stmfx_irq_chip = { ++ .name = "stmfx-core", ++ .irq_bus_lock = stmfx_irq_bus_lock, ++ .irq_bus_sync_unlock = stmfx_irq_bus_sync_unlock, ++ .irq_mask = stmfx_irq_mask, ++ .irq_unmask = stmfx_irq_unmask, ++}; ++ ++static irqreturn_t stmfx_irq_handler(int irq, void *data) ++{ ++ struct stmfx_ddata *ddata = data; ++ unsigned long n, pending; ++ u32 ack; ++ int ret; ++ ++ ret = regmap_read(ddata->stmfx->map, STMFX_REG_IRQ_PENDING, ++ (u32 *)&pending); ++ if (ret) ++ return IRQ_NONE; ++ ++ /* ++ * There is no ACK for GPIO, MFX_REG_IRQ_PENDING_GPIO is a logical OR ++ * of MFX_REG_IRQ_GPI _PENDING1/_PENDING2/_PENDING3 ++ */ ++ ack = pending & ~BIT(STMFX_REG_IRQ_SRC_EN_GPIO); ++ if (ack) { ++ ret = regmap_write(ddata->stmfx->map, STMFX_REG_IRQ_ACK, ack); ++ if (ret) ++ return IRQ_NONE; ++ } ++ ++ for_each_set_bit(n, &pending, STMFX_REG_IRQ_SRC_MAX) ++ handle_nested_irq(irq_find_mapping(ddata->irq_domain, n)); ++ ++ return IRQ_HANDLED; ++} ++ ++static int stmfx_irq_map(struct irq_domain *d, unsigned int virq, ++ irq_hw_number_t hwirq) ++{ ++ irq_set_chip_data(virq, d->host_data); ++ irq_set_chip_and_handler(virq, &stmfx_irq_chip, handle_simple_irq); ++ irq_set_nested_thread(virq, 1); ++ irq_set_noprobe(virq); ++ ++ return 0; ++} ++ ++static void stmfx_irq_unmap(struct irq_domain *d, unsigned int virq) ++{ ++ irq_set_chip_and_handler(virq, NULL, NULL); ++ irq_set_chip_data(virq, NULL); ++} ++ ++static const struct irq_domain_ops stmfx_irq_ops = { ++ .map = stmfx_irq_map, ++ .unmap = stmfx_irq_unmap, ++}; ++ ++static void stmfx_irq_exit(struct stmfx_ddata *ddata) ++{ ++ int hwirq; ++ ++ for (hwirq = 0; hwirq < STMFX_REG_IRQ_SRC_MAX; hwirq++) ++ irq_dispose_mapping(irq_find_mapping(ddata->irq_domain, hwirq)); ++ irq_domain_remove(ddata->irq_domain); ++} ++ ++static int stmfx_irq_init(struct stmfx_ddata *ddata, int irq) ++{ ++ struct device *dev = ddata->stmfx->dev; ++ u32 irqoutpin = 0, irqtrigger; ++ int ret; ++ ++ ddata->irq_domain = irq_domain_add_simple(dev->of_node, ++ STMFX_REG_IRQ_SRC_MAX, 0, ++ &stmfx_irq_ops, ddata); ++ if (!ddata->irq_domain) { ++ dev_err(dev, "failed to create irq domain\n"); ++ return -EINVAL; ++ } ++ ++ if (!of_property_read_bool(dev->of_node, "drive-open-drain")) ++ irqoutpin |= STMFX_REG_IRQ_OUT_PIN_TYPE; ++ ++ irqtrigger = irq_get_trigger_type(irq); ++ if ((irqtrigger & IRQ_TYPE_EDGE_RISING) || ++ (irqtrigger & IRQ_TYPE_LEVEL_HIGH)) ++ irqoutpin |= STMFX_REG_IRQ_OUT_PIN_POL; ++ ++ ret = regmap_write(ddata->stmfx->map, STMFX_REG_IRQ_OUT_PIN, irqoutpin); ++ if (ret) ++ return ret; ++ ++ ret = devm_request_threaded_irq(dev, irq, NULL, stmfx_irq_handler, ++ irqtrigger | IRQF_ONESHOT, ++ "stmfx", ddata); ++ if (ret) ++ stmfx_irq_exit(ddata); ++ ++ return ret; ++} ++ ++static int stmfx_chip_reset(struct stmfx_ddata *ddata) ++{ ++ int ret; ++ ++ ret = regmap_write(ddata->stmfx->map, STMFX_REG_SYS_CTRL, ++ STMFX_REG_SYS_CTRL_SWRST); ++ if (ret) ++ return ret; ++ ++ msleep(STMFX_BOOT_TIME_MS); ++ ++ return ret; ++} ++ ++static int stmfx_chip_init(struct stmfx_ddata *ddata, struct i2c_client *client) ++{ ++ u32 id; ++ u8 version[2]; ++ int ret; ++ ++ ddata->vdd = devm_regulator_get_optional(&client->dev, "vdd"); ++ if (IS_ERR(ddata->vdd)) { ++ ret = PTR_ERR(ddata->vdd); ++ if (ret != -ENODEV) { ++ if (ret != -EPROBE_DEFER) ++ dev_err(&client->dev, ++ "no vdd regulator found:%d\n", ret); ++ return ret; ++ } ++ } ++ ++ if (!IS_ERR(ddata->vdd)) { ++ ret = regulator_enable(ddata->vdd); ++ if (ret) { ++ dev_err(&client->dev, "vdd enable failed: %d\n", ret); ++ return ret; ++ } ++ } ++ ++ ret = regmap_read(ddata->stmfx->map, STMFX_REG_CHIP_ID, &id); ++ if (ret) { ++ dev_err(&client->dev, "error reading chip id: %d\n", ret); ++ goto err; ++ } ++ ++ /* ++ * Check that ID is the complement of the I2C address: ++ * STMFX I2C address follows the 7-bit format (MSB), that's why ++ * client->addr is shifted. ++ * ++ * STMFX_I2C_ADDR| STMFX | Linux ++ * input pin | I2C device address | I2C device address ++ *--------------------------------------------------------- ++ * 0 | b: 1000 010x h:0x84 | 0x42 ++ * 1 | b: 1000 011x h:0x86 | 0x43 ++ */ ++ if (FIELD_GET(STMFX_REG_CHIP_ID_MASK, ~id) != (client->addr << 1)) { ++ dev_err(&client->dev, "unknown chip id: %#x\n", id); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ ret = regmap_bulk_read(ddata->stmfx->map, STMFX_REG_FW_VERSION_MSB, ++ version, ARRAY_SIZE(version)); ++ if (ret) { ++ dev_err(&client->dev, "error reading fw version: %d\n", ret); ++ goto err; ++ } ++ ++ dev_info(&client->dev, "STMFX id: %#x, fw version: %x.%02x\n", ++ id, version[0], version[1]); ++ ++ return stmfx_chip_reset(ddata); ++ ++err: ++ if (!IS_ERR(ddata->vdd)) ++ return regulator_disable(ddata->vdd); ++ ++ return ret; ++} ++ ++static int stmfx_chip_exit(struct stmfx_ddata *ddata) ++{ ++ regmap_write(ddata->stmfx->map, STMFX_REG_IRQ_SRC_EN, 0); ++ regmap_write(ddata->stmfx->map, STMFX_REG_SYS_CTRL, 0); ++ ++ if (!IS_ERR(ddata->vdd)) ++ return regulator_disable(ddata->vdd); ++ ++ return 0; ++} ++ ++static const struct resource stmfx_pinctrl_resources[] = { ++ DEFINE_RES_IRQ(STMFX_REG_IRQ_SRC_EN_GPIO), ++}; ++ ++static struct mfd_cell stmfx_cells[] = { ++ { ++ .of_compatible = "st,stmfx-0300-pinctrl", ++ .name = "stmfx-pinctrl", ++ .resources = stmfx_pinctrl_resources, ++ .num_resources = ARRAY_SIZE(stmfx_pinctrl_resources), ++ } ++}; ++ ++static bool stmfx_reg_volatile(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case STMFX_REG_SYS_CTRL: ++ case STMFX_REG_IRQ_SRC_EN: ++ case STMFX_REG_IRQ_PENDING: ++ case STMFX_REG_IRQ_GPI_PENDING1: ++ case STMFX_REG_IRQ_GPI_PENDING2: ++ case STMFX_REG_IRQ_GPI_PENDING3: ++ case STMFX_REG_GPIO_STATE1: ++ case STMFX_REG_GPIO_STATE2: ++ case STMFX_REG_GPIO_STATE3: ++ case STMFX_REG_IRQ_GPI_SRC1: ++ case STMFX_REG_IRQ_GPI_SRC2: ++ case STMFX_REG_IRQ_GPI_SRC3: ++ case STMFX_REG_GPO_SET1: ++ case STMFX_REG_GPO_SET2: ++ case STMFX_REG_GPO_SET3: ++ case STMFX_REG_GPO_CLR1: ++ case STMFX_REG_GPO_CLR2: ++ case STMFX_REG_GPO_CLR3: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static bool stmfx_reg_writeable(struct device *dev, unsigned int reg) ++{ ++ return (reg >= STMFX_REG_SYS_CTRL); ++} ++ ++static const struct regmap_config stmfx_regmap_config = { ++ .reg_bits = 8, ++ .reg_stride = 1, ++ .val_bits = 8, ++ .max_register = STMFX_REG_MAX, ++ .volatile_reg = stmfx_reg_volatile, ++ .writeable_reg = stmfx_reg_writeable, ++ .cache_type = REGCACHE_RBTREE, ++}; ++ ++static int stmfx_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct device *dev = &client->dev; ++ struct stmfx_ddata *ddata; ++ int i, ret; ++ ++ ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); ++ if (!ddata) ++ return -ENOMEM; ++ /* State holder */ ++ ddata->stmfx = devm_kzalloc(dev, sizeof(*ddata->stmfx), GFP_KERNEL); ++ if (!ddata->stmfx) ++ return -ENOMEM; ++ ++ i2c_set_clientdata(client, ddata); ++ ++ ddata->stmfx->dev = dev; ++ ++ ddata->stmfx->map = devm_regmap_init_i2c(client, &stmfx_regmap_config); ++ if (IS_ERR(ddata->stmfx->map)) { ++ ret = PTR_ERR(ddata->stmfx->map); ++ dev_err(dev, "failed to allocate register map: %d\n", ret); ++ return ret; ++ } ++ ++ mutex_init(&ddata->lock); ++ ++ ret = stmfx_chip_init(ddata, client); ++ if (ret) { ++ if (ret == -ETIMEDOUT) ++ return -EPROBE_DEFER; ++ return ret; ++ } ++ ++ if (client->irq < 0) { ++ dev_err(dev, "failed to get irq: %d\n", client->irq); ++ ret = client->irq; ++ goto err_chip_exit; ++ } ++ ++ ret = stmfx_irq_init(ddata, client->irq); ++ if (ret) ++ goto err_chip_exit; ++ ++ for (i = 0; i < ARRAY_SIZE(stmfx_cells); i++) { ++ stmfx_cells[i].platform_data = ddata->stmfx; ++ stmfx_cells[i].pdata_size = sizeof(struct stmfx); ++ } ++ ++ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, ++ stmfx_cells, ARRAY_SIZE(stmfx_cells), NULL, ++ 0, ddata->irq_domain); ++ if (ret) ++ goto err_irq_exit; ++ ++ return 0; ++ ++err_irq_exit: ++ stmfx_irq_exit(ddata); ++err_chip_exit: ++ stmfx_chip_exit(ddata); ++ ++ return ret; ++} ++ ++static int stmfx_remove(struct i2c_client *client) ++{ ++ struct stmfx_ddata *ddata = i2c_get_clientdata(client); ++ ++ stmfx_irq_exit(ddata); ++ ++ return stmfx_chip_exit(ddata); ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int stmfx_backup_regs(struct stmfx_ddata *ddata) ++{ ++ int ret; ++ ++ ret = regmap_raw_read(ddata->stmfx->map, STMFX_REG_SYS_CTRL, ++ &ddata->bkp_sysctrl, sizeof(ddata->bkp_sysctrl)); ++ if (ret) ++ return ret; ++ ret = regmap_raw_read(ddata->stmfx->map, STMFX_REG_IRQ_OUT_PIN, ++ &ddata->bkp_irqoutpin, ++ sizeof(ddata->bkp_irqoutpin)); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int stmfx_restore_regs(struct stmfx_ddata *ddata) ++{ ++ int ret; ++ ++ ret = regmap_raw_write(ddata->stmfx->map, STMFX_REG_SYS_CTRL, ++ &ddata->bkp_sysctrl, sizeof(ddata->bkp_sysctrl)); ++ if (ret) ++ return ret; ++ ret = regmap_raw_write(ddata->stmfx->map, STMFX_REG_IRQ_OUT_PIN, ++ &ddata->bkp_irqoutpin, ++ sizeof(ddata->bkp_irqoutpin)); ++ if (ret) ++ return ret; ++ ret = regmap_raw_write(ddata->stmfx->map, STMFX_REG_IRQ_SRC_EN, ++ &ddata->irq_src, sizeof(ddata->irq_src)); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int stmfx_suspend(struct device *dev) ++{ ++ struct stmfx_ddata *ddata = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = stmfx_backup_regs(ddata); ++ if (ret) { ++ dev_err(ddata->stmfx->dev, "registers backup failure\n"); ++ return ret; ++ } ++ ++ if (!IS_ERR(ddata->vdd)) { ++ ret = regulator_disable(ddata->vdd); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int stmfx_resume(struct device *dev) ++{ ++ struct stmfx_ddata *ddata = dev_get_drvdata(dev); ++ int ret; ++ ++ if (!IS_ERR(ddata->vdd)) { ++ ret = regulator_enable(ddata->vdd); ++ if (ret) { ++ dev_err(ddata->stmfx->dev, ++ "vdd enable failed: %d\n", ret); ++ return ret; ++ } ++ } ++ ++ ret = stmfx_restore_regs(ddata); ++ if (ret) { ++ dev_err(ddata->stmfx->dev, "registers restoration failure\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++#endif ++ ++static SIMPLE_DEV_PM_OPS(stmfx_dev_pm_ops, stmfx_suspend, stmfx_resume); ++ ++static const struct of_device_id stmfx_of_match[] = { ++ { .compatible = "st,stmfx-0300", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, stmfx_of_match); ++ ++static struct i2c_driver stmfx_driver = { ++ .driver = { ++ .name = "stmfx-core", ++ .of_match_table = of_match_ptr(stmfx_of_match), ++ .pm = &stmfx_dev_pm_ops, ++ }, ++ .probe = stmfx_probe, ++ .remove = stmfx_remove, ++}; ++module_i2c_driver(stmfx_driver); ++ ++MODULE_DESCRIPTION("STMFX core driver"); ++MODULE_AUTHOR("Amelie Delaunay "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/mfd/stpmic1.c b/drivers/mfd/stpmic1.c +new file mode 100644 +index 0000000..5bf6328 +--- /dev/null ++++ b/drivers/mfd/stpmic1.c +@@ -0,0 +1,413 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (C) STMicroelectronics 2018 ++// Author: Pascal Paillet ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define STPMIC1_MAIN_IRQ 0 ++#define STPMIC1_WAKEUP_IRQ 1 ++ ++static bool stpmic1_reg_readable(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case TURN_ON_SR: ++ case TURN_OFF_SR: ++ case ICC_LDO_TURN_OFF_SR: ++ case ICC_BUCK_TURN_OFF_SR: ++ case RREQ_STATE_SR: ++ case VERSION_SR: ++ case SWOFF_PWRCTRL_CR: ++ case PADS_PULL_CR: ++ case BUCKS_PD_CR: ++ case LDO14_PD_CR: ++ case LDO56_VREF_PD_CR: ++ case VBUS_DET_VIN_CR: ++ case PKEY_TURNOFF_CR: ++ case BUCKS_MASK_RANK_CR: ++ case BUCKS_MASK_RESET_CR: ++ case LDOS_MASK_RANK_CR: ++ case LDOS_MASK_RESET_CR: ++ case WCHDG_CR: ++ case WCHDG_TIMER_CR: ++ case BUCKS_ICCTO_CR: ++ case LDOS_ICCTO_CR: ++ case BUCK1_ACTIVE_CR: ++ case BUCK2_ACTIVE_CR: ++ case BUCK3_ACTIVE_CR: ++ case BUCK4_ACTIVE_CR: ++ case VREF_DDR_ACTIVE_CR: ++ case LDO1_ACTIVE_CR: ++ case LDO2_ACTIVE_CR: ++ case LDO3_ACTIVE_CR: ++ case LDO4_ACTIVE_CR: ++ case LDO5_ACTIVE_CR: ++ case LDO6_ACTIVE_CR: ++ case BUCK1_STDBY_CR: ++ case BUCK2_STDBY_CR: ++ case BUCK3_STDBY_CR: ++ case BUCK4_STDBY_CR: ++ case VREF_DDR_STDBY_CR: ++ case LDO1_STDBY_CR: ++ case LDO2_STDBY_CR: ++ case LDO3_STDBY_CR: ++ case LDO4_STDBY_CR: ++ case LDO5_STDBY_CR: ++ case LDO6_STDBY_CR: ++ case BST_SW_CR: ++ case INT_PENDING_R1: ++ case INT_PENDING_R2: ++ case INT_PENDING_R3: ++ case INT_PENDING_R4: ++ case INT_DBG_LATCH_R1: ++ case INT_DBG_LATCH_R2: ++ case INT_DBG_LATCH_R3: ++ case INT_DBG_LATCH_R4: ++ case INT_CLEAR_R1: ++ case INT_CLEAR_R2: ++ case INT_CLEAR_R3: ++ case INT_CLEAR_R4: ++ case INT_MASK_R1: ++ case INT_MASK_R2: ++ case INT_MASK_R3: ++ case INT_MASK_R4: ++ case INT_SET_MASK_R1: ++ case INT_SET_MASK_R2: ++ case INT_SET_MASK_R3: ++ case INT_SET_MASK_R4: ++ case INT_CLEAR_MASK_R1: ++ case INT_CLEAR_MASK_R2: ++ case INT_CLEAR_MASK_R3: ++ case INT_CLEAR_MASK_R4: ++ case INT_SRC_R1: ++ case INT_SRC_R2: ++ case INT_SRC_R3: ++ case INT_SRC_R4: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static bool stpmic1_reg_writeable(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case SWOFF_PWRCTRL_CR: ++ case PADS_PULL_CR: ++ case BUCKS_PD_CR: ++ case LDO14_PD_CR: ++ case LDO56_VREF_PD_CR: ++ case VBUS_DET_VIN_CR: ++ case PKEY_TURNOFF_CR: ++ case BUCKS_MASK_RANK_CR: ++ case BUCKS_MASK_RESET_CR: ++ case LDOS_MASK_RANK_CR: ++ case LDOS_MASK_RESET_CR: ++ case WCHDG_CR: ++ case WCHDG_TIMER_CR: ++ case BUCKS_ICCTO_CR: ++ case LDOS_ICCTO_CR: ++ case BUCK1_ACTIVE_CR: ++ case BUCK2_ACTIVE_CR: ++ case BUCK3_ACTIVE_CR: ++ case BUCK4_ACTIVE_CR: ++ case VREF_DDR_ACTIVE_CR: ++ case LDO1_ACTIVE_CR: ++ case LDO2_ACTIVE_CR: ++ case LDO3_ACTIVE_CR: ++ case LDO4_ACTIVE_CR: ++ case LDO5_ACTIVE_CR: ++ case LDO6_ACTIVE_CR: ++ case BUCK1_STDBY_CR: ++ case BUCK2_STDBY_CR: ++ case BUCK3_STDBY_CR: ++ case BUCK4_STDBY_CR: ++ case VREF_DDR_STDBY_CR: ++ case LDO1_STDBY_CR: ++ case LDO2_STDBY_CR: ++ case LDO3_STDBY_CR: ++ case LDO4_STDBY_CR: ++ case LDO5_STDBY_CR: ++ case LDO6_STDBY_CR: ++ case BST_SW_CR: ++ case INT_DBG_LATCH_R1: ++ case INT_DBG_LATCH_R2: ++ case INT_DBG_LATCH_R3: ++ case INT_DBG_LATCH_R4: ++ case INT_CLEAR_R1: ++ case INT_CLEAR_R2: ++ case INT_CLEAR_R3: ++ case INT_CLEAR_R4: ++ case INT_SET_MASK_R1: ++ case INT_SET_MASK_R2: ++ case INT_SET_MASK_R3: ++ case INT_SET_MASK_R4: ++ case INT_CLEAR_MASK_R1: ++ case INT_CLEAR_MASK_R2: ++ case INT_CLEAR_MASK_R3: ++ case INT_CLEAR_MASK_R4: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static bool stpmic1_reg_volatile(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case TURN_ON_SR: ++ case TURN_OFF_SR: ++ case ICC_LDO_TURN_OFF_SR: ++ case ICC_BUCK_TURN_OFF_SR: ++ case RREQ_STATE_SR: ++ case INT_PENDING_R1: ++ case INT_PENDING_R2: ++ case INT_PENDING_R3: ++ case INT_PENDING_R4: ++ case INT_SRC_R1: ++ case INT_SRC_R2: ++ case INT_SRC_R3: ++ case INT_SRC_R4: ++ case WCHDG_CR: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++const struct regmap_config stpmic1_regmap_config = { ++ .reg_bits = 8, ++ .val_bits = 8, ++ .cache_type = REGCACHE_RBTREE, ++ .max_register = PMIC_MAX_REGISTER_ADDRESS, ++ .readable_reg = stpmic1_reg_readable, ++ .writeable_reg = stpmic1_reg_writeable, ++ .volatile_reg = stpmic1_reg_volatile, ++}; ++ ++static const struct regmap_irq stpmic1_irqs[] = { ++ [IT_PONKEY_F] = { .mask = 0x01 }, ++ [IT_PONKEY_R] = { .mask = 0x02 }, ++ [IT_WAKEUP_F] = { .mask = 0x04 }, ++ [IT_WAKEUP_R] = { .mask = 0x08 }, ++ [IT_VBUS_OTG_F] = { .mask = 0x10 }, ++ [IT_VBUS_OTG_R] = { .mask = 0x20 }, ++ [IT_SWOUT_F] = { .mask = 0x40 }, ++ [IT_SWOUT_R] = { .mask = 0x80 }, ++ ++ [IT_CURLIM_BUCK1] = { .reg_offset = 1, .mask = 0x01 }, ++ [IT_CURLIM_BUCK2] = { .reg_offset = 1, .mask = 0x02 }, ++ [IT_CURLIM_BUCK3] = { .reg_offset = 1, .mask = 0x04 }, ++ [IT_CURLIM_BUCK4] = { .reg_offset = 1, .mask = 0x08 }, ++ [IT_OCP_OTG] = { .reg_offset = 1, .mask = 0x10 }, ++ [IT_OCP_SWOUT] = { .reg_offset = 1, .mask = 0x20 }, ++ [IT_OCP_BOOST] = { .reg_offset = 1, .mask = 0x40 }, ++ [IT_OVP_BOOST] = { .reg_offset = 1, .mask = 0x80 }, ++ ++ [IT_CURLIM_LDO1] = { .reg_offset = 2, .mask = 0x01 }, ++ [IT_CURLIM_LDO2] = { .reg_offset = 2, .mask = 0x02 }, ++ [IT_CURLIM_LDO3] = { .reg_offset = 2, .mask = 0x04 }, ++ [IT_CURLIM_LDO4] = { .reg_offset = 2, .mask = 0x08 }, ++ [IT_CURLIM_LDO5] = { .reg_offset = 2, .mask = 0x10 }, ++ [IT_CURLIM_LDO6] = { .reg_offset = 2, .mask = 0x20 }, ++ [IT_SHORT_SWOTG] = { .reg_offset = 2, .mask = 0x40 }, ++ [IT_SHORT_SWOUT] = { .reg_offset = 2, .mask = 0x80 }, ++ ++ [IT_TWARN_F] = { .reg_offset = 3, .mask = 0x01 }, ++ [IT_TWARN_R] = { .reg_offset = 3, .mask = 0x02 }, ++ [IT_VINLOW_F] = { .reg_offset = 3, .mask = 0x04 }, ++ [IT_VINLOW_R] = { .reg_offset = 3, .mask = 0x08 }, ++ [IT_SWIN_F] = { .reg_offset = 3, .mask = 0x40 }, ++ [IT_SWIN_R] = { .reg_offset = 3, .mask = 0x80 }, ++}; ++ ++static const struct regmap_irq_chip stpmic1_regmap_irq_chip = { ++ .name = "pmic_irq", ++ .status_base = INT_PENDING_R1, ++ .mask_base = INT_CLEAR_MASK_R1, ++ .unmask_base = INT_SET_MASK_R1, ++ .ack_base = INT_CLEAR_R1, ++ .num_regs = STPMIC1_PMIC_NUM_IRQ_REGS, ++ .irqs = stpmic1_irqs, ++ .num_irqs = ARRAY_SIZE(stpmic1_irqs), ++}; ++ ++static int stpmic1_probe(struct i2c_client *i2c, ++ const struct i2c_device_id *id) ++{ ++ struct stpmic1 *ddata; ++ struct device *dev = &i2c->dev; ++ int ret; ++ struct device_node *np = dev->of_node; ++ u32 reg; ++ ++ ddata = devm_kzalloc(dev, sizeof(struct stpmic1), GFP_KERNEL); ++ if (!ddata) ++ return -ENOMEM; ++ ++ i2c_set_clientdata(i2c, ddata); ++ ddata->dev = dev; ++ ++ ddata->regmap = devm_regmap_init_i2c(i2c, &stpmic1_regmap_config); ++ if (IS_ERR(ddata->regmap)) ++ return PTR_ERR(ddata->regmap); ++ ++ ddata->irq = of_irq_get(np, STPMIC1_MAIN_IRQ); ++ if (ddata->irq < 0) { ++ dev_err(dev, "Failed to get main IRQ: %d\n", ddata->irq); ++ return ddata->irq; ++ } ++ ++ ddata->irq_wake = of_irq_get(np, STPMIC1_WAKEUP_IRQ); ++ if (ddata->irq_wake > 0) { ++ device_init_wakeup(dev, true); ++ ret = dev_pm_set_dedicated_wake_irq(dev, ddata->irq_wake); ++ if (ret) ++ dev_warn(dev, "failed to set up wakeup irq"); ++ } ++ ++ if (!of_property_read_u32(np, "st,main-control-register", ®)) { ++ ret = regmap_update_bits(ddata->regmap, ++ SWOFF_PWRCTRL_CR, ++ PWRCTRL_POLARITY_HIGH | ++ PWRCTRL_PIN_VALID | ++ RESTART_REQUEST_ENABLED, ++ reg); ++ if (ret) { ++ dev_err(dev, ++ "Failed to update main control register: %d\n", ++ ret); ++ return ret; ++ } ++ } ++ ++ /* Read Version ID */ ++ ret = regmap_read(ddata->regmap, VERSION_SR, ®); ++ if (ret) { ++ dev_err(dev, "Unable to read pmic version\n"); ++ return ret; ++ } ++ dev_info(dev, "PMIC Chip Version: 0x%x\n", reg); ++ ++ if (!of_property_read_u32(np, "st,pads-pull-register", ®)) { ++ ret = regmap_update_bits(ddata->regmap, ++ PADS_PULL_CR, ++ WAKEUP_DETECTOR_DISABLED | ++ PWRCTRL_PD_ACTIVE | ++ PWRCTRL_PU_ACTIVE | ++ WAKEUP_PD_ACTIVE, ++ reg); ++ if (ret) { ++ dev_err(dev, ++ "Failed to update pads control register: %d\n", ++ ret); ++ return ret; ++ } ++ } ++ ++ if (!of_property_read_u32(np, "st,vin-control-register", ®)) { ++ ret = regmap_update_bits(ddata->regmap, ++ VBUS_DET_VIN_CR, ++ VINLOW_CTRL_REG_MASK, ++ reg); ++ if (ret) { ++ dev_err(dev, ++ "Failed to update vin control register: %d\n", ++ ret); ++ return ret; ++ } ++ } ++ ++ if (!of_property_read_u32(np, "st,usb-control-register", ®)) { ++ ret = regmap_update_bits(ddata->regmap, BST_SW_CR, ++ BOOST_OVP_DISABLED | ++ VBUS_OTG_DETECTION_DISABLED | ++ SW_OUT_DISCHARGE | ++ VBUS_OTG_DISCHARGE | ++ OCP_LIMIT_HIGH, ++ reg); ++ if (ret) { ++ dev_err(dev, ++ "Failed to update usb control register: %d\n", ++ ret); ++ return ret; ++ } ++ } ++ ++ /* Initialize PMIC IRQ Chip & IRQ domains associated */ ++ ret = devm_regmap_add_irq_chip(dev, ddata->regmap, ddata->irq, ++ IRQF_ONESHOT | IRQF_SHARED, ++ 0, &stpmic1_regmap_irq_chip, ++ &ddata->irq_data); ++ if (ret) { ++ dev_err(dev, "IRQ Chip registration failed: %d\n", ret); ++ return ret; ++ } ++ ++ return devm_of_platform_populate(dev); ++} ++ ++static const struct i2c_device_id stpmic1_id[] = { ++ { "stpmic1"}, ++ {} ++}; ++ ++MODULE_DEVICE_TABLE(i2c, stpmic1_id); ++ ++#ifdef CONFIG_PM_SLEEP ++static int stpmic1_suspend(struct device *dev) ++{ ++ struct i2c_client *i2c = to_i2c_client(dev); ++ struct stpmic1 *pmic_dev = i2c_get_clientdata(i2c); ++ ++ disable_irq(pmic_dev->irq); ++ if ((pmic_dev->irq_wake > 0) && device_may_wakeup(dev)) ++ enable_irq_wake(pmic_dev->irq_wake); ++ ++ return 0; ++} ++ ++static int stpmic1_resume(struct device *dev) ++{ ++ struct i2c_client *i2c = to_i2c_client(dev); ++ struct stpmic1 *pmic_dev = i2c_get_clientdata(i2c); ++ int ret; ++ ++ ret = regcache_sync(pmic_dev->regmap); ++ if (ret) ++ return ret; ++ ++ enable_irq(pmic_dev->irq); ++ if ((pmic_dev->irq_wake > 0) && device_may_wakeup(dev)) ++ disable_irq_wake(pmic_dev->irq_wake); ++ ++ return 0; ++} ++#endif ++ ++static SIMPLE_DEV_PM_OPS(stpmic1_pm, stpmic1_suspend, stpmic1_resume); ++ ++static struct i2c_driver stpmic1_driver = { ++ .driver = { ++ .name = "stpmic1", ++ .pm = &stpmic1_pm, ++ }, ++ .probe = stpmic1_probe, ++ .id_table = stpmic1_id, ++}; ++ ++module_i2c_driver(stpmic1_driver); ++ ++MODULE_DESCRIPTION("STPMIC1 PMIC Driver"); ++MODULE_AUTHOR("Pascal Paillet "); ++MODULE_LICENSE("GPL v2"); +diff --git a/include/dt-bindings/mfd/st,stpmic1.h b/include/dt-bindings/mfd/st,stpmic1.h +new file mode 100644 +index 0000000..b2d6c83 +--- /dev/null ++++ b/include/dt-bindings/mfd/st,stpmic1.h +@@ -0,0 +1,46 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved ++ * Author: Philippe Peurichard , ++ * Pascal Paillet for STMicroelectronics. ++ */ ++ ++#ifndef __DT_BINDINGS_STPMIC1_H__ ++#define __DT_BINDINGS_STPMIC1_H__ ++ ++/* IRQ definitions */ ++#define IT_PONKEY_F 0 ++#define IT_PONKEY_R 1 ++#define IT_WAKEUP_F 2 ++#define IT_WAKEUP_R 3 ++#define IT_VBUS_OTG_F 4 ++#define IT_VBUS_OTG_R 5 ++#define IT_SWOUT_F 6 ++#define IT_SWOUT_R 7 ++ ++#define IT_CURLIM_BUCK1 8 ++#define IT_CURLIM_BUCK2 9 ++#define IT_CURLIM_BUCK3 10 ++#define IT_CURLIM_BUCK4 11 ++#define IT_OCP_OTG 12 ++#define IT_OCP_SWOUT 13 ++#define IT_OCP_BOOST 14 ++#define IT_OVP_BOOST 15 ++ ++#define IT_CURLIM_LDO1 16 ++#define IT_CURLIM_LDO2 17 ++#define IT_CURLIM_LDO3 18 ++#define IT_CURLIM_LDO4 19 ++#define IT_CURLIM_LDO5 20 ++#define IT_CURLIM_LDO6 21 ++#define IT_SHORT_SWOTG 22 ++#define IT_SHORT_SWOUT 23 ++ ++#define IT_TWARN_F 24 ++#define IT_TWARN_R 25 ++#define IT_VINLOW_F 26 ++#define IT_VINLOW_R 27 ++#define IT_SWIN_F 30 ++#define IT_SWIN_R 31 ++ ++#endif /* __DT_BINDINGS_STPMIC1_H__ */ +diff --git a/include/linux/mfd/stmfx.h b/include/linux/mfd/stmfx.h +new file mode 100644 +index 0000000..35c3d42 +--- /dev/null ++++ b/include/linux/mfd/stmfx.h +@@ -0,0 +1,27 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) 2018 STMicroelectronics ++ * Author(s): Amelie Delaunay . ++ */ ++ ++#ifndef MFD_STMFX_H ++#define MFX_STMFX_H ++ ++#include ++ ++enum stmfx_functions { ++ STMFX_FUNC_GPIO = BIT(0), /* GPIO[15:0] */ ++ STMFX_FUNC_ALTGPIO_LOW = BIT(1), /* aGPIO[3:0] */ ++ STMFX_FUNC_ALTGPIO_HIGH = BIT(2), /* aGPIO[7:4] */ ++ STMFX_FUNC_TS = BIT(3), ++ STMFX_FUNC_IDD = BIT(4), ++}; ++ ++struct stmfx { ++ struct device *dev; ++ struct regmap *map; ++}; ++ ++int stmfx_function_enable(struct stmfx *stmfx, u32 func); ++int stmfx_function_disable(struct stmfx *stmfx, u32 func); ++#endif +diff --git a/include/linux/mfd/stpmic1.h b/include/linux/mfd/stpmic1.h +new file mode 100644 +index 0000000..4abe5f1 +--- /dev/null ++++ b/include/linux/mfd/stpmic1.h +@@ -0,0 +1,213 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved ++ * Author: Philippe Peurichard , ++ * Pascal Paillet for STMicroelectronics. ++ */ ++ ++#ifndef __LINUX_MFD_STPMIC1_H ++#define __LINUX_MFD_STPMIC1_H ++ ++#define TURN_ON_SR 0x1 ++#define TURN_OFF_SR 0x2 ++#define ICC_LDO_TURN_OFF_SR 0x3 ++#define ICC_BUCK_TURN_OFF_SR 0x4 ++#define RREQ_STATE_SR 0x5 ++#define VERSION_SR 0x6 ++ ++#define SWOFF_PWRCTRL_CR 0x10 ++#define PADS_PULL_CR 0x11 ++#define BUCKS_PD_CR 0x12 ++#define LDO14_PD_CR 0x13 ++#define LDO56_VREF_PD_CR 0x14 ++#define VBUS_DET_VIN_CR 0x15 ++#define PKEY_TURNOFF_CR 0x16 ++#define BUCKS_MASK_RANK_CR 0x17 ++#define BUCKS_MASK_RESET_CR 0x18 ++#define LDOS_MASK_RANK_CR 0x19 ++#define LDOS_MASK_RESET_CR 0x1A ++#define WCHDG_CR 0x1B ++#define WCHDG_TIMER_CR 0x1C ++#define BUCKS_ICCTO_CR 0x1D ++#define LDOS_ICCTO_CR 0x1E ++ ++#define BUCK1_ACTIVE_CR 0x20 ++#define BUCK2_ACTIVE_CR 0x21 ++#define BUCK3_ACTIVE_CR 0x22 ++#define BUCK4_ACTIVE_CR 0x23 ++#define VREF_DDR_ACTIVE_CR 0x24 ++#define LDO1_ACTIVE_CR 0x25 ++#define LDO2_ACTIVE_CR 0x26 ++#define LDO3_ACTIVE_CR 0x27 ++#define LDO4_ACTIVE_CR 0x28 ++#define LDO5_ACTIVE_CR 0x29 ++#define LDO6_ACTIVE_CR 0x2A ++ ++#define BUCK1_STDBY_CR 0x30 ++#define BUCK2_STDBY_CR 0x31 ++#define BUCK3_STDBY_CR 0x32 ++#define BUCK4_STDBY_CR 0x33 ++#define VREF_DDR_STDBY_CR 0x34 ++#define LDO1_STDBY_CR 0x35 ++#define LDO2_STDBY_CR 0x36 ++#define LDO3_STDBY_CR 0x37 ++#define LDO4_STDBY_CR 0x38 ++#define LDO5_STDBY_CR 0x39 ++#define LDO6_STDBY_CR 0x3A ++ ++#define BST_SW_CR 0x40 ++ ++#define INT_PENDING_R1 0x50 ++#define INT_PENDING_R2 0x51 ++#define INT_PENDING_R3 0x52 ++#define INT_PENDING_R4 0x53 ++ ++#define INT_DBG_LATCH_R1 0x60 ++#define INT_DBG_LATCH_R2 0x61 ++#define INT_DBG_LATCH_R3 0x62 ++#define INT_DBG_LATCH_R4 0x63 ++ ++#define INT_CLEAR_R1 0x70 ++#define INT_CLEAR_R2 0x71 ++#define INT_CLEAR_R3 0x72 ++#define INT_CLEAR_R4 0x73 ++ ++#define INT_MASK_R1 0x80 ++#define INT_MASK_R2 0x81 ++#define INT_MASK_R3 0x82 ++#define INT_MASK_R4 0x83 ++ ++#define INT_SET_MASK_R1 0x90 ++#define INT_SET_MASK_R2 0x91 ++#define INT_SET_MASK_R3 0x92 ++#define INT_SET_MASK_R4 0x93 ++ ++#define INT_CLEAR_MASK_R1 0xA0 ++#define INT_CLEAR_MASK_R2 0xA1 ++#define INT_CLEAR_MASK_R3 0xA2 ++#define INT_CLEAR_MASK_R4 0xA3 ++ ++#define INT_SRC_R1 0xB0 ++#define INT_SRC_R2 0xB1 ++#define INT_SRC_R3 0xB2 ++#define INT_SRC_R4 0xB3 ++ ++#define PMIC_MAX_REGISTER_ADDRESS INT_SRC_R4 ++ ++#define STPMIC1_PMIC_NUM_IRQ_REGS 4 ++ ++#define TURN_OFF_SR_ICC_EVENT 0x08 ++ ++#define LDO_VOLTAGE_MASK GENMASK(6, 2) ++#define BUCK_VOLTAGE_MASK GENMASK(7, 2) ++#define LDO_BUCK_VOLTAGE_SHIFT 2 ++ ++#define LDO_ENABLE_MASK BIT(0) ++#define BUCK_ENABLE_MASK BIT(0) ++ ++#define BUCK_HPLP_ENABLE_MASK BIT(1) ++#define BUCK_HPLP_SHIFT 1 ++ ++#define STDBY_ENABLE_MASK BIT(0) ++ ++#define BUCKS_PD_CR_REG_MASK GENMASK(7, 0) ++#define BUCK_MASK_RANK_REGISTER_MASK GENMASK(3, 0) ++#define BUCK_MASK_RESET_REGISTER_MASK GENMASK(3, 0) ++#define LDO1234_PULL_DOWN_REGISTER_MASK GENMASK(7, 0) ++#define LDO56_VREF_PD_CR_REG_MASK GENMASK(5, 0) ++#define LDO_MASK_RANK_REGISTER_MASK GENMASK(5, 0) ++#define LDO_MASK_RESET_REGISTER_MASK GENMASK(5, 0) ++ ++#define BUCK1_PULL_DOWN_REG BUCKS_PD_CR ++#define BUCK1_PULL_DOWN_MASK BIT(0) ++#define BUCK2_PULL_DOWN_REG BUCKS_PD_CR ++#define BUCK2_PULL_DOWN_MASK BIT(2) ++#define BUCK3_PULL_DOWN_REG BUCKS_PD_CR ++#define BUCK3_PULL_DOWN_MASK BIT(4) ++#define BUCK4_PULL_DOWN_REG BUCKS_PD_CR ++#define BUCK4_PULL_DOWN_MASK BIT(6) ++ ++#define LDO1_PULL_DOWN_REG LDO14_PD_CR ++#define LDO1_PULL_DOWN_MASK BIT(0) ++#define LDO2_PULL_DOWN_REG LDO14_PD_CR ++#define LDO2_PULL_DOWN_MASK BIT(2) ++#define LDO3_PULL_DOWN_REG LDO14_PD_CR ++#define LDO3_PULL_DOWN_MASK BIT(4) ++#define LDO4_PULL_DOWN_REG LDO14_PD_CR ++#define LDO4_PULL_DOWN_MASK BIT(6) ++#define LDO5_PULL_DOWN_REG LDO56_VREF_PD_CR ++#define LDO5_PULL_DOWN_MASK BIT(0) ++#define LDO6_PULL_DOWN_REG LDO56_VREF_PD_CR ++#define LDO6_PULL_DOWN_MASK BIT(2) ++#define VREF_DDR_PULL_DOWN_REG LDO56_VREF_PD_CR ++#define VREF_DDR_PULL_DOWN_MASK BIT(4) ++ ++#define BUCKS_ICCTO_CR_REG_MASK GENMASK(6, 0) ++#define LDOS_ICCTO_CR_REG_MASK GENMASK(5, 0) ++ ++#define LDO_BYPASS_MASK BIT(7) ++ ++/* Main PMIC Control Register ++ * SWOFF_PWRCTRL_CR ++ * Address : 0x10 ++ */ ++#define ICC_EVENT_ENABLED BIT(4) ++#define PWRCTRL_POLARITY_HIGH BIT(3) ++#define PWRCTRL_PIN_VALID BIT(2) ++#define RESTART_REQUEST_ENABLED BIT(1) ++#define SOFTWARE_SWITCH_OFF_ENABLED BIT(0) ++ ++/* Main PMIC PADS Control Register ++ * PADS_PULL_CR ++ * Address : 0x11 ++ */ ++#define WAKEUP_DETECTOR_DISABLED BIT(4) ++#define PWRCTRL_PD_ACTIVE BIT(3) ++#define PWRCTRL_PU_ACTIVE BIT(2) ++#define WAKEUP_PD_ACTIVE BIT(1) ++#define PONKEY_PU_INACTIVE BIT(0) ++ ++/* Main PMIC VINLOW Control Register ++ * VBUS_DET_VIN_CRC DMSC ++ * Address : 0x15 ++ */ ++#define SWIN_DETECTOR_ENABLED BIT(7) ++#define SWOUT_DETECTOR_ENABLED BIT(6) ++#define VINLOW_ENABLED BIT(0) ++#define VINLOW_CTRL_REG_MASK GENMASK(7, 0) ++ ++/* USB Control Register ++ * Address : 0x40 ++ */ ++#define BOOST_OVP_DISABLED BIT(7) ++#define VBUS_OTG_DETECTION_DISABLED BIT(6) ++#define SW_OUT_DISCHARGE BIT(5) ++#define VBUS_OTG_DISCHARGE BIT(4) ++#define OCP_LIMIT_HIGH BIT(3) ++#define SWIN_SWOUT_ENABLED BIT(2) ++#define USBSW_OTG_SWITCH_ENABLED BIT(1) ++#define BOOST_ENABLED BIT(0) ++ ++/* PKEY_TURNOFF_CR ++ * Address : 0x16 ++ */ ++#define PONKEY_PWR_OFF BIT(7) ++#define PONKEY_CC_FLAG_CLEAR BIT(6) ++#define PONKEY_TURNOFF_TIMER_MASK GENMASK(3, 0) ++#define PONKEY_TURNOFF_MASK GENMASK(7, 0) ++ ++/* ++ * struct stpmic1 - stpmic1 master device for sub-drivers ++ * @dev: master device of the chip (can be used to access platform data) ++ * @irq: main IRQ number ++ * @regmap_irq_chip_data: irq chip data ++ */ ++struct stpmic1 { ++ struct device *dev; ++ struct regmap *regmap; ++ int irq; ++ int irq_wake; ++ struct regmap_irq_chip_data *irq_data; ++}; ++ ++#endif /* __LINUX_MFD_STPMIC1_H */ +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0009-ARM-stm32mp1-r0-rc1-MMC-MTD.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0009-ARM-stm32mp1-r0-rc1-MMC-MTD.patch new file mode 100644 index 0000000..d11c0d2 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0009-ARM-stm32mp1-r0-rc1-MMC-MTD.patch @@ -0,0 +1,4216 @@ +From c40b273973671e355eacd8bad62bd938b0ebc05f Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Tue, 13 Nov 2018 12:23:12 +0100 +Subject: [PATCH 09/52] ARM: stm32mp1-r0-rc1: MMC MTD + +--- + drivers/mmc/host/Kconfig | 10 + + drivers/mmc/host/Makefile | 1 + + drivers/mmc/host/mmci.c | 811 +++++++++---- + drivers/mmc/host/mmci.h | 180 ++- + drivers/mmc/host/mmci_qcom_dml.c | 17 +- + drivers/mmc/host/mmci_stm32_sdmmc.c | 282 +++++ + drivers/mtd/nand/raw/Kconfig | 9 + + drivers/mtd/nand/raw/Makefile | 1 + + drivers/mtd/nand/raw/stm32_fmc2_nand.c | 2031 ++++++++++++++++++++++++++++++++ + include/linux/amba/mmci.h | 11 +- + 10 files changed, 3067 insertions(+), 286 deletions(-) + create mode 100644 drivers/mmc/host/mmci_stm32_sdmmc.c + create mode 100644 drivers/mtd/nand/raw/stm32_fmc2_nand.c + +diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig +index 694d082..9742519 100644 +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -34,6 +34,16 @@ config MMC_QCOM_DML + + if unsure, say N. + ++config MMC_STM32_SDMMC ++ bool "STMicroelectronics STM32 SDMMC Controller" ++ depends on MMC_ARMMMCI ++ default y ++ help ++ This selects the STMicroelectronics STM32 SDMMC host controller. ++ If you have a STM32 sdmmc host with internal DMA say Y here. ++ ++ If unsure, say N. ++ + config MMC_PXA + tristate "Intel PXA25x/26x/27x Multimedia Card Interface support" + depends on ARCH_PXA +diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile +index ce8398e..f14410f 100644 +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -6,6 +6,7 @@ + obj-$(CONFIG_MMC_ARMMMCI) += armmmci.o + armmmci-y := mmci.o + armmmci-$(CONFIG_MMC_QCOM_DML) += mmci_qcom_dml.o ++armmmci-$(CONFIG_MMC_STM32_SDMMC) += mmci_stm32_sdmmc.o + obj-$(CONFIG_MMC_PXA) += pxamci.o + obj-$(CONFIG_MMC_MXC) += mxcmmc.o + obj-$(CONFIG_MMC_MXS) += mxs-mmc.o +diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c +index 1841d250..db50d1e 100644 +--- a/drivers/mmc/host/mmci.c ++++ b/drivers/mmc/host/mmci.c +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -28,8 +29,7 @@ + #include + #include + #include +-#include +-#include ++#include + #include + #include + #include +@@ -37,6 +37,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -46,41 +47,79 @@ + + #define DRIVER_NAME "mmci-pl18x" + ++#ifdef CONFIG_DMA_ENGINE ++void mmci_variant_init(struct mmci_host *host); ++#else ++static inline void mmci_variant_init(struct mmci_host *host) {} ++#endif ++ ++#ifdef CONFIG_MMC_STM32_SDMMC ++void sdmmc_variant_init(struct mmci_host *host); ++#else ++static inline void sdmmc_variant_init(struct mmci_host *host) {} ++#endif ++static void ++mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c); ++ + static unsigned int fmax = 515633; + + static struct variant_data variant_arm = { + .fifosize = 16 * 4, + .fifohalfsize = 8 * 4, ++ .cmdreg_cpsm_enable = MCI_CPSM_ENABLE, ++ .cmdreg_lrsp_crc = MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP, ++ .cmdreg_srsp_crc = MCI_CPSM_RESPONSE, ++ .cmdreg_srsp = MCI_CPSM_RESPONSE, + .datalength_bits = 16, ++ .datactrl_blocksz = 11, ++ .datactrl_dpsm_enable = MCI_DPSM_ENABLE, + .pwrreg_powerup = MCI_PWR_UP, + .f_max = 100000000, + .reversed_irq_handling = true, + .mmcimask1 = true, ++ .irq_pio_mask = MCI_IRQ_PIO_MASK, + .start_err = MCI_STARTBITERR, + .opendrain = MCI_ROD, ++ .init = mmci_variant_init, + }; + + static struct variant_data variant_arm_extended_fifo = { + .fifosize = 128 * 4, + .fifohalfsize = 64 * 4, ++ .cmdreg_cpsm_enable = MCI_CPSM_ENABLE, ++ .cmdreg_lrsp_crc = MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP, ++ .cmdreg_srsp_crc = MCI_CPSM_RESPONSE, ++ .cmdreg_srsp = MCI_CPSM_RESPONSE, + .datalength_bits = 16, ++ .datactrl_blocksz = 11, ++ .datactrl_dpsm_enable = MCI_DPSM_ENABLE, + .pwrreg_powerup = MCI_PWR_UP, + .f_max = 100000000, + .mmcimask1 = true, ++ .irq_pio_mask = MCI_IRQ_PIO_MASK, + .start_err = MCI_STARTBITERR, + .opendrain = MCI_ROD, ++ .init = mmci_variant_init, + }; + + static struct variant_data variant_arm_extended_fifo_hwfc = { + .fifosize = 128 * 4, + .fifohalfsize = 64 * 4, + .clkreg_enable = MCI_ARM_HWFCEN, ++ .cmdreg_cpsm_enable = MCI_CPSM_ENABLE, ++ .cmdreg_lrsp_crc = MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP, ++ .cmdreg_srsp_crc = MCI_CPSM_RESPONSE, ++ .cmdreg_srsp = MCI_CPSM_RESPONSE, + .datalength_bits = 16, ++ .datactrl_blocksz = 11, ++ .datactrl_dpsm_enable = MCI_DPSM_ENABLE, + .pwrreg_powerup = MCI_PWR_UP, + .f_max = 100000000, + .mmcimask1 = true, ++ .irq_pio_mask = MCI_IRQ_PIO_MASK, + .start_err = MCI_STARTBITERR, + .opendrain = MCI_ROD, ++ .init = mmci_variant_init, + }; + + static struct variant_data variant_u300 = { +@@ -88,7 +127,13 @@ static struct variant_data variant_u300 = { + .fifohalfsize = 8 * 4, + .clkreg_enable = MCI_ST_U300_HWFCEN, + .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS, ++ .cmdreg_cpsm_enable = MCI_CPSM_ENABLE, ++ .cmdreg_lrsp_crc = MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP, ++ .cmdreg_srsp_crc = MCI_CPSM_RESPONSE, ++ .cmdreg_srsp = MCI_CPSM_RESPONSE, + .datalength_bits = 16, ++ .datactrl_blocksz = 11, ++ .datactrl_dpsm_enable = MCI_DPSM_ENABLE, + .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, + .st_sdio = true, + .pwrreg_powerup = MCI_PWR_ON, +@@ -97,8 +142,10 @@ static struct variant_data variant_u300 = { + .pwrreg_clkgate = true, + .pwrreg_nopower = true, + .mmcimask1 = true, ++ .irq_pio_mask = MCI_IRQ_PIO_MASK, + .start_err = MCI_STARTBITERR, + .opendrain = MCI_OD, ++ .init = mmci_variant_init, + }; + + static struct variant_data variant_nomadik = { +@@ -106,7 +153,13 @@ static struct variant_data variant_nomadik = { + .fifohalfsize = 8 * 4, + .clkreg = MCI_CLK_ENABLE, + .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS, ++ .cmdreg_cpsm_enable = MCI_CPSM_ENABLE, ++ .cmdreg_lrsp_crc = MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP, ++ .cmdreg_srsp_crc = MCI_CPSM_RESPONSE, ++ .cmdreg_srsp = MCI_CPSM_RESPONSE, + .datalength_bits = 24, ++ .datactrl_blocksz = 11, ++ .datactrl_dpsm_enable = MCI_DPSM_ENABLE, + .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, + .st_sdio = true, + .st_clkdiv = true, +@@ -116,8 +169,10 @@ static struct variant_data variant_nomadik = { + .pwrreg_clkgate = true, + .pwrreg_nopower = true, + .mmcimask1 = true, ++ .irq_pio_mask = MCI_IRQ_PIO_MASK, + .start_err = MCI_STARTBITERR, + .opendrain = MCI_OD, ++ .init = mmci_variant_init, + }; + + static struct variant_data variant_ux500 = { +@@ -127,7 +182,13 @@ static struct variant_data variant_ux500 = { + .clkreg_enable = MCI_ST_UX500_HWFCEN, + .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS, + .clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE, ++ .cmdreg_cpsm_enable = MCI_CPSM_ENABLE, ++ .cmdreg_lrsp_crc = MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP, ++ .cmdreg_srsp_crc = MCI_CPSM_RESPONSE, ++ .cmdreg_srsp = MCI_CPSM_RESPONSE, + .datalength_bits = 24, ++ .datactrl_blocksz = 11, ++ .datactrl_dpsm_enable = MCI_DPSM_ENABLE, + .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, + .st_sdio = true, + .st_clkdiv = true, +@@ -141,8 +202,10 @@ static struct variant_data variant_ux500 = { + .busy_detect_mask = MCI_ST_BUSYENDMASK, + .pwrreg_nopower = true, + .mmcimask1 = true, ++ .irq_pio_mask = MCI_IRQ_PIO_MASK, + .start_err = MCI_STARTBITERR, + .opendrain = MCI_OD, ++ .init = mmci_variant_init, + }; + + static struct variant_data variant_ux500v2 = { +@@ -152,8 +215,14 @@ static struct variant_data variant_ux500v2 = { + .clkreg_enable = MCI_ST_UX500_HWFCEN, + .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS, + .clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE, ++ .cmdreg_cpsm_enable = MCI_CPSM_ENABLE, ++ .cmdreg_lrsp_crc = MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP, ++ .cmdreg_srsp_crc = MCI_CPSM_RESPONSE, ++ .cmdreg_srsp = MCI_CPSM_RESPONSE, + .datactrl_mask_ddrmode = MCI_DPSM_ST_DDRMODE, + .datalength_bits = 24, ++ .datactrl_blocksz = 11, ++ .datactrl_dpsm_enable = MCI_DPSM_ENABLE, + .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, + .st_sdio = true, + .st_clkdiv = true, +@@ -168,8 +237,10 @@ static struct variant_data variant_ux500v2 = { + .busy_detect_mask = MCI_ST_BUSYENDMASK, + .pwrreg_nopower = true, + .mmcimask1 = true, ++ .irq_pio_mask = MCI_IRQ_PIO_MASK, + .start_err = MCI_STARTBITERR, + .opendrain = MCI_OD, ++ .init = mmci_variant_init, + }; + + static struct variant_data variant_stm32 = { +@@ -179,7 +250,14 @@ static struct variant_data variant_stm32 = { + .clkreg_enable = MCI_ST_UX500_HWFCEN, + .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS, + .clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE, ++ .cmdreg_cpsm_enable = MCI_CPSM_ENABLE, ++ .cmdreg_lrsp_crc = MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP, ++ .cmdreg_srsp_crc = MCI_CPSM_RESPONSE, ++ .cmdreg_srsp = MCI_CPSM_RESPONSE, ++ .irq_pio_mask = MCI_IRQ_PIO_MASK, + .datalength_bits = 24, ++ .datactrl_blocksz = 11, ++ .datactrl_dpsm_enable = MCI_DPSM_ENABLE, + .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, + .st_sdio = true, + .st_clkdiv = true, +@@ -187,6 +265,56 @@ static struct variant_data variant_stm32 = { + .f_max = 48000000, + .pwrreg_clkgate = true, + .pwrreg_nopower = true, ++ .init = mmci_variant_init, ++}; ++ ++static struct variant_data variant_stm32_sdmmc = { ++ .fifosize = 16 * 4, ++ .fifohalfsize = 8 * 4, ++ .f_max = 208000000, ++ .stm32_clkdiv = true, ++ .cmdreg_cpsm_enable = MCI_CPSM_STM32_ENABLE, ++ .cmdreg_lrsp_crc = MCI_CPSM_STM32_LRSP_CRC, ++ .cmdreg_srsp_crc = MCI_CPSM_STM32_SRSP_CRC, ++ .cmdreg_srsp = MCI_CPSM_STM32_SRSP, ++ .cmdreg_stop = MCI_CPSM_STM32_CMDSTOP, ++ .data_cmd_enable = MCI_CPSM_STM32_CMDTRANS, ++ .irq_pio_mask = MCI_IRQ_PIO_STM32_MASK, ++ .datactrl_first = true, ++ .datacnt_useless = true, ++ .datalength_bits = 25, ++ .datactrl_blocksz = 14, ++ .stm32_idmabsize_mask = GENMASK(12, 5), ++ .busy_detect = true, ++ .busy_timeout = true, ++ .busy_detect_flag = MCI_STM32_BUSYD0, ++ .busy_detect_mask = MCI_STM32_BUSYD0ENDMASK, ++ .init = sdmmc_variant_init, ++}; ++ ++static struct variant_data variant_stm32_sdmmcv2 = { ++ .fifosize = 16 * 4, ++ .fifohalfsize = 8 * 4, ++ .f_max = 208000000, ++ .stm32_clkdiv = true, ++ .cmdreg_cpsm_enable = MCI_CPSM_STM32_ENABLE, ++ .cmdreg_lrsp_crc = MCI_CPSM_STM32_LRSP_CRC, ++ .cmdreg_srsp_crc = MCI_CPSM_STM32_SRSP_CRC, ++ .cmdreg_srsp = MCI_CPSM_STM32_SRSP, ++ .cmdreg_stop = MCI_CPSM_STM32_CMDSTOP, ++ .data_cmd_enable = MCI_CPSM_STM32_CMDTRANS, ++ .irq_pio_mask = MCI_IRQ_PIO_STM32_MASK, ++ .datactrl_first = true, ++ .datacnt_useless = true, ++ .datalength_bits = 25, ++ .datactrl_blocksz = 14, ++ .stm32_idmabsize_mask = GENMASK(16, 5), ++ .dma_lli = true, ++ .busy_detect = true, ++ .busy_timeout = true, ++ .busy_detect_flag = MCI_STM32_BUSYD0, ++ .busy_detect_mask = MCI_STM32_BUSYD0ENDMASK, ++ .init = sdmmc_variant_init, + }; + + static struct variant_data variant_qcom = { +@@ -197,15 +325,22 @@ static struct variant_data variant_qcom = { + MCI_QCOM_CLK_SELECT_IN_FBCLK, + .clkreg_8bit_bus_enable = MCI_QCOM_CLK_WIDEBUS_8, + .datactrl_mask_ddrmode = MCI_QCOM_CLK_SELECT_IN_DDR_MODE, ++ .cmdreg_cpsm_enable = MCI_CPSM_ENABLE, ++ .cmdreg_lrsp_crc = MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP, ++ .cmdreg_srsp_crc = MCI_CPSM_RESPONSE, ++ .cmdreg_srsp = MCI_CPSM_RESPONSE, + .data_cmd_enable = MCI_CPSM_QCOM_DATCMD, + .blksz_datactrl4 = true, + .datalength_bits = 24, ++ .datactrl_blocksz = 11, ++ .datactrl_dpsm_enable = MCI_DPSM_ENABLE, + .pwrreg_powerup = MCI_PWR_UP, + .f_max = 208000000, + .explicit_mclk_control = true, + .qcom_fifo = true, + .qcom_dml = true, + .mmcimask1 = true, ++ .irq_pio_mask = MCI_IRQ_PIO_MASK, + .start_err = MCI_STARTBITERR, + .opendrain = MCI_ROD, + .init = qcom_variant_init, +@@ -226,24 +361,6 @@ static int mmci_card_busy(struct mmc_host *mmc) + return busy; + } + +-/* +- * Validate mmc prerequisites +- */ +-static int mmci_validate_data(struct mmci_host *host, +- struct mmc_data *data) +-{ +- if (!data) +- return 0; +- +- if (!is_power_of_2(data->blksz)) { +- dev_err(mmc_dev(host->mmc), +- "unsupported block size (%d bytes)\n", data->blksz); +- return -EINVAL; +- } +- +- return 0; +-} +- + static void mmci_reg_delay(struct mmci_host *host) + { + /* +@@ -262,7 +379,7 @@ static void mmci_reg_delay(struct mmci_host *host) + /* + * This must be called with host->lock held + */ +-static void mmci_write_clkreg(struct mmci_host *host, u32 clk) ++void mmci_write_clkreg(struct mmci_host *host, u32 clk) + { + if (host->clk_reg != clk) { + host->clk_reg = clk; +@@ -273,7 +390,7 @@ static void mmci_write_clkreg(struct mmci_host *host, u32 clk) + /* + * This must be called with host->lock held + */ +-static void mmci_write_pwrreg(struct mmci_host *host, u32 pwr) ++void mmci_write_pwrreg(struct mmci_host *host, u32 pwr) + { + if (host->pwr_reg != pwr) { + host->pwr_reg = pwr; +@@ -357,9 +474,156 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired) + mmci_write_clkreg(host, clk); + } + ++void mmci_dma_release(struct mmci_host *host) ++{ ++ if (host->ops && host->ops->dma_release) ++ host->ops->dma_release(host); ++ ++ host->use_dma = false; ++} ++ ++void mmci_dma_setup(struct mmci_host *host) ++{ ++ if (!host->ops || !host->ops->dma_setup) ++ return; ++ ++ if (host->ops->dma_setup(host)) ++ return; ++ ++ /* initialize pre request cookie */ ++ host->next_cookie = 1; ++ ++ host->use_dma = true; ++} ++ ++/* ++ * Validate mmc prerequisites ++ */ ++static int mmci_validate_data(struct mmci_host *host, ++ struct mmc_data *data) ++{ ++ if (!data) ++ return 0; ++ ++ if (!is_power_of_2(data->blksz)) { ++ dev_err(mmc_dev(host->mmc), ++ "unsupported block size (%d bytes)\n", data->blksz); ++ return -EINVAL; ++ } ++ ++ if (host->ops && host->ops->validate_data) ++ return host->ops->validate_data(host, data); ++ ++ return 0; ++} ++ ++int mmci_prep_data(struct mmci_host *host, struct mmc_data *data, bool next) ++{ ++ int err; ++ ++ if (!host->ops || !host->ops->prep_data) ++ return 0; ++ ++ err = host->ops->prep_data(host, data, next); ++ ++ if (next && !err) ++ data->host_cookie = ++host->next_cookie < 0 ? ++ 1 : host->next_cookie; ++ ++ return err; ++} ++ ++void mmci_unprep_data(struct mmci_host *host, struct mmc_data *data, ++ int err) ++{ ++ if (host->ops && host->ops->unprep_data) ++ host->ops->unprep_data(host, data, err); ++ ++ data->host_cookie = 0; ++} ++ ++void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data) ++{ ++ WARN_ON(data->host_cookie && data->host_cookie != host->next_cookie); ++ ++ if (host->ops && host->ops->get_next_data) ++ host->ops->get_next_data(host, data); ++} ++ ++int mmci_dma_start(struct mmci_host *host, unsigned int datactrl) ++{ ++ struct mmc_data *data = host->data; ++ int ret; ++ ++ if (!host->use_dma) ++ return -EINVAL; ++ ++ ret = mmci_prep_data(host, data, false); ++ if (ret) ++ return ret; ++ ++ if (!host->ops || !host->ops->dma_start) ++ return -EINVAL; ++ ++ /* Okay, go for it. */ ++ dev_vdbg(mmc_dev(host->mmc), ++ "Submit MMCI DMA job, sglen %d blksz %04x blks %04x flags %08x\n", ++ data->sg_len, data->blksz, data->blocks, data->flags); ++ ++ host->ops->dma_start(host, &datactrl); ++ ++ /* Trigger the DMA transfer */ ++ mmci_write_datactrlreg(host, datactrl); ++ ++ /* ++ * Let the MMCI say when the data is ended and it's time ++ * to fire next DMA request. When that happens, MMCI will ++ * call mmci_data_end() ++ */ ++ writel(readl(host->base + MMCIMASK0) | MCI_DATAENDMASK, ++ host->base + MMCIMASK0); ++ return 0; ++} ++ ++void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data) ++{ ++ if (!host->use_dma) ++ return; ++ ++ if (host->ops && host->ops->dma_finalize) ++ host->ops->dma_finalize(host, data); ++} ++ ++void mmci_dma_error(struct mmci_host *host) ++{ ++ if (!host->use_dma) ++ return; ++ ++ if (host->ops && host->ops->dma_error) ++ host->ops->dma_error(host); ++} ++ + static void + mmci_request_end(struct mmci_host *host, struct mmc_request *mrq) + { ++ /* ++ * If an error happens on command or data step, some variants ++ * require a stop command to reinit the DPSM. ++ * If it's not done the next data command freeze hardware block. ++ */ ++ if (host->variant->cmdreg_stop) { ++ u32 dpsm; ++ ++ dpsm = readl_relaxed(host->base + MMCISTATUS); ++ dpsm &= MCI_STM32_DPSMACTIVE; ++ ++ if (dpsm && ((mrq->cmd && mrq->cmd->error) || ++ (mrq->data && mrq->data->error))) { ++ mmci_start_command(host, &host->stop_abort, 0); ++ return; ++ } ++ } ++ + writel(0, host->base + MMCICOMMAND); + + BUG_ON(host->data); +@@ -378,7 +642,7 @@ static void mmci_set_mask1(struct mmci_host *host, unsigned int mask) + if (host->singleirq) { + unsigned int mask0 = readl(base + MMCIMASK0); + +- mask0 &= ~MCI_IRQ1MASK; ++ mask0 &= ~variant->irq_pio_mask; + mask0 |= mask; + + writel(mask0, base + MMCIMASK0); +@@ -415,31 +679,50 @@ static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data) + * no custom DMA interfaces are supported. + */ + #ifdef CONFIG_DMA_ENGINE +-static void mmci_dma_setup(struct mmci_host *host) ++struct mmci_dmae_next { ++ struct dma_async_tx_descriptor *desc; ++ struct dma_chan *chan; ++}; ++ ++struct mmci_dmae_priv { ++ struct dma_chan *cur; ++ struct dma_chan *rx_channel; ++ struct dma_chan *tx_channel; ++ struct dma_async_tx_descriptor *desc_current; ++ struct mmci_dmae_next next_data; ++}; ++ ++int mmci_dmae_setup(struct mmci_host *host) + { + const char *rxname, *txname; ++ struct mmci_dmae_priv *dmae; + +- host->dma_rx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "rx"); +- host->dma_tx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "tx"); ++ dmae = devm_kzalloc(mmc_dev(host->mmc), sizeof(*dmae), GFP_KERNEL); ++ if (!dmae) ++ return -ENOMEM; + +- /* initialize pre request cookie */ +- host->next_data.cookie = 1; ++ host->dma_priv = dmae; ++ ++ dmae->rx_channel = dma_request_slave_channel(mmc_dev(host->mmc), ++ "rx"); ++ dmae->tx_channel = dma_request_slave_channel(mmc_dev(host->mmc), ++ "tx"); + + /* + * If only an RX channel is specified, the driver will + * attempt to use it bidirectionally, however if it is + * is specified but cannot be located, DMA will be disabled. + */ +- if (host->dma_rx_channel && !host->dma_tx_channel) +- host->dma_tx_channel = host->dma_rx_channel; ++ if (dmae->rx_channel && !dmae->tx_channel) ++ dmae->tx_channel = dmae->rx_channel; + +- if (host->dma_rx_channel) +- rxname = dma_chan_name(host->dma_rx_channel); ++ if (dmae->rx_channel) ++ rxname = dma_chan_name(dmae->rx_channel); + else + rxname = "none"; + +- if (host->dma_tx_channel) +- txname = dma_chan_name(host->dma_tx_channel); ++ if (dmae->tx_channel) ++ txname = dma_chan_name(dmae->tx_channel); + else + txname = "none"; + +@@ -450,66 +733,84 @@ static void mmci_dma_setup(struct mmci_host *host) + * Limit the maximum segment size in any SG entry according to + * the parameters of the DMA engine device. + */ +- if (host->dma_tx_channel) { +- struct device *dev = host->dma_tx_channel->device->dev; ++ if (dmae->tx_channel) { ++ struct device *dev = dmae->tx_channel->device->dev; + unsigned int max_seg_size = dma_get_max_seg_size(dev); + + if (max_seg_size < host->mmc->max_seg_size) + host->mmc->max_seg_size = max_seg_size; + } +- if (host->dma_rx_channel) { +- struct device *dev = host->dma_rx_channel->device->dev; ++ if (dmae->rx_channel) { ++ struct device *dev = dmae->rx_channel->device->dev; + unsigned int max_seg_size = dma_get_max_seg_size(dev); + + if (max_seg_size < host->mmc->max_seg_size) + host->mmc->max_seg_size = max_seg_size; + } + +- if (host->ops && host->ops->dma_setup) +- host->ops->dma_setup(host); ++ if (!dmae->tx_channel || !dmae->rx_channel) { ++ mmci_dmae_release(host); ++ return -EINVAL; ++ } ++ ++ return 0; + } + + /* + * This is used in or so inline it + * so it can be discarded. + */ +-static inline void mmci_dma_release(struct mmci_host *host) ++void mmci_dmae_release(struct mmci_host *host) + { +- if (host->dma_rx_channel) +- dma_release_channel(host->dma_rx_channel); +- if (host->dma_tx_channel) +- dma_release_channel(host->dma_tx_channel); +- host->dma_rx_channel = host->dma_tx_channel = NULL; +-} ++ struct mmci_dmae_priv *dmae = host->dma_priv; + +-static void mmci_dma_data_error(struct mmci_host *host) +-{ +- dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n"); +- dmaengine_terminate_all(host->dma_current); +- host->dma_in_progress = false; +- host->dma_current = NULL; +- host->dma_desc_current = NULL; +- host->data->host_cookie = 0; ++ if (dmae->rx_channel) ++ dma_release_channel(dmae->rx_channel); ++ if (dmae->tx_channel) ++ dma_release_channel(dmae->tx_channel); ++ dmae->rx_channel = dmae->tx_channel = NULL; + } + + static void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data) + { ++ struct mmci_dmae_priv *dmae = host->dma_priv; + struct dma_chan *chan; + + if (data->flags & MMC_DATA_READ) +- chan = host->dma_rx_channel; ++ chan = dmae->rx_channel; + else +- chan = host->dma_tx_channel; ++ chan = dmae->tx_channel; + + dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, + mmc_get_dma_dir(data)); + } + +-static void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data) ++void mmci_dmae_error(struct mmci_host *host) ++{ ++ struct mmci_dmae_priv *dmae = host->dma_priv; ++ ++ if (!dma_inprogress(host)) ++ return; ++ ++ dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n"); ++ dmaengine_terminate_all(dmae->cur); ++ host->dma_in_progress = false; ++ dmae->cur = NULL; ++ dmae->desc_current = NULL; ++ host->data->host_cookie = 0; ++ ++ mmci_dma_unmap(host, host->data); ++} ++ ++void mmci_dmae_finalize(struct mmci_host *host, struct mmc_data *data) + { ++ struct mmci_dmae_priv *dmae = host->dma_priv; + u32 status; + int i; + ++ if (!dma_inprogress(host)) ++ return; ++ + /* Wait up to 1ms for the DMA to complete */ + for (i = 0; ; i++) { + status = readl(host->base + MMCISTATUS); +@@ -525,13 +826,12 @@ static void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data) + * contiguous buffers. On TX, we'll get a FIFO underrun error. + */ + if (status & MCI_RXDATAAVLBLMASK) { +- mmci_dma_data_error(host); ++ mmci_dma_error(host); + if (!data->error) + data->error = -EIO; +- } +- +- if (!data->host_cookie) ++ } else if (!data->host_cookie) { + mmci_dma_unmap(host, data); ++ } + + /* + * Use of DMA with scatter-gather is impossible. +@@ -543,15 +843,16 @@ static void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data) + } + + host->dma_in_progress = false; +- host->dma_current = NULL; +- host->dma_desc_current = NULL; ++ dmae->cur = NULL; ++ dmae->desc_current = NULL; + } + + /* prepares DMA channel and DMA descriptor, returns non-zero on failure */ +-static int __mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, ++static int _mmci_dmae_prep_data(struct mmci_host *host, struct mmc_data *data, + struct dma_chan **dma_chan, + struct dma_async_tx_descriptor **dma_desc) + { ++ struct mmci_dmae_priv *dmae = host->dma_priv; + struct variant_data *variant = host->variant; + struct dma_slave_config conf = { + .src_addr = host->phybase + MMCIFIFO, +@@ -570,10 +871,10 @@ static int __mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, + + if (data->flags & MMC_DATA_READ) { + conf.direction = DMA_DEV_TO_MEM; +- chan = host->dma_rx_channel; ++ chan = dmae->rx_channel; + } else { + conf.direction = DMA_MEM_TO_DEV; +- chan = host->dma_tx_channel; ++ chan = dmae->tx_channel; + } + + /* If there's no DMA channel, fall back to PIO */ +@@ -610,160 +911,137 @@ static int __mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, + return -ENOMEM; + } + +-static inline int mmci_dma_prep_data(struct mmci_host *host, +- struct mmc_data *data) ++int mmci_dmae_prep_data(struct mmci_host *host, ++ struct mmc_data *data, ++ bool next) + { ++ struct mmci_dmae_priv *dmae = host->dma_priv; ++ struct mmci_dmae_next *nd = &dmae->next_data; ++ ++ if (!host->use_dma) ++ return -EINVAL; ++ ++ if (next) ++ return _mmci_dmae_prep_data(host, data, &nd->chan, &nd->desc); + /* Check if next job is already prepared. */ +- if (host->dma_current && host->dma_desc_current) ++ if (dmae->cur && dmae->desc_current) + return 0; + + /* No job were prepared thus do it now. */ +- return __mmci_dma_prep_data(host, data, &host->dma_current, +- &host->dma_desc_current); ++ return _mmci_dmae_prep_data(host, data, &dmae->cur, ++ &dmae->desc_current); + } + +-static inline int mmci_dma_prep_next(struct mmci_host *host, +- struct mmc_data *data) ++int mmci_dmae_start(struct mmci_host *host, unsigned int *datactrl) + { +- struct mmci_host_next *nd = &host->next_data; +- return __mmci_dma_prep_data(host, data, &nd->dma_chan, &nd->dma_desc); +-} +- +-static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl) +-{ +- int ret; ++ struct mmci_dmae_priv *dmae = host->dma_priv; + struct mmc_data *data = host->data; + +- ret = mmci_dma_prep_data(host, host->data); +- if (ret) +- return ret; +- +- /* Okay, go for it. */ +- dev_vdbg(mmc_dev(host->mmc), +- "Submit MMCI DMA job, sglen %d blksz %04x blks %04x flags %08x\n", +- data->sg_len, data->blksz, data->blocks, data->flags); + host->dma_in_progress = true; +- dmaengine_submit(host->dma_desc_current); +- dma_async_issue_pending(host->dma_current); ++ dmaengine_submit(dmae->desc_current); ++ dma_async_issue_pending(dmae->cur); + + if (host->variant->qcom_dml) + dml_start_xfer(host, data); + +- datactrl |= MCI_DPSM_DMAENABLE; ++ *datactrl |= MCI_DPSM_DMAENABLE; + +- /* Trigger the DMA transfer */ +- mmci_write_datactrlreg(host, datactrl); +- +- /* +- * Let the MMCI say when the data is ended and it's time +- * to fire next DMA request. When that happens, MMCI will +- * call mmci_data_end() +- */ +- writel(readl(host->base + MMCIMASK0) | MCI_DATAENDMASK, +- host->base + MMCIMASK0); + return 0; + } + +-static void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data) ++void mmci_dmae_get_next_data(struct mmci_host *host, struct mmc_data *data) + { +- struct mmci_host_next *next = &host->next_data; +- +- WARN_ON(data->host_cookie && data->host_cookie != next->cookie); +- WARN_ON(!data->host_cookie && (next->dma_desc || next->dma_chan)); ++ struct mmci_dmae_priv *dmae = host->dma_priv; ++ struct mmci_dmae_next *next = &dmae->next_data; + +- host->dma_desc_current = next->dma_desc; +- host->dma_current = next->dma_chan; +- next->dma_desc = NULL; +- next->dma_chan = NULL; +-} +- +-static void mmci_pre_request(struct mmc_host *mmc, struct mmc_request *mrq) +-{ +- struct mmci_host *host = mmc_priv(mmc); +- struct mmc_data *data = mrq->data; +- struct mmci_host_next *nd = &host->next_data; +- +- if (!data) ++ if (!host->use_dma) + return; + +- BUG_ON(data->host_cookie); ++ WARN_ON(!data->host_cookie && (next->desc || next->chan)); + +- if (mmci_validate_data(host, data)) +- return; +- +- if (!mmci_dma_prep_next(host, data)) +- data->host_cookie = ++nd->cookie < 0 ? 1 : nd->cookie; ++ dmae->desc_current = next->desc; ++ dmae->cur = next->chan; ++ next->desc = NULL; ++ next->chan = NULL; + } + +-static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq, +- int err) ++void mmci_dmae_unprep_data(struct mmci_host *host, ++ struct mmc_data *data, int err) ++ + { +- struct mmci_host *host = mmc_priv(mmc); +- struct mmc_data *data = mrq->data; ++ struct mmci_dmae_priv *dmae = host->dma_priv; + +- if (!data || !data->host_cookie) ++ if (!host->use_dma) + return; + + mmci_dma_unmap(host, data); + + if (err) { +- struct mmci_host_next *next = &host->next_data; ++ struct mmci_dmae_next *next = &dmae->next_data; + struct dma_chan *chan; + if (data->flags & MMC_DATA_READ) +- chan = host->dma_rx_channel; ++ chan = dmae->rx_channel; + else +- chan = host->dma_tx_channel; ++ chan = dmae->tx_channel; + dmaengine_terminate_all(chan); + +- if (host->dma_desc_current == next->dma_desc) +- host->dma_desc_current = NULL; ++ if (dmae->desc_current == next->desc) ++ dmae->desc_current = NULL; + +- if (host->dma_current == next->dma_chan) { ++ if (dmae->cur == next->chan) { + host->dma_in_progress = false; +- host->dma_current = NULL; ++ dmae->cur = NULL; + } + +- next->dma_desc = NULL; +- next->dma_chan = NULL; +- data->host_cookie = 0; ++ next->desc = NULL; ++ next->chan = NULL; + } + } + +-#else +-/* Blank functions if the DMA engine is not available */ +-static void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data) +-{ +-} +-static inline void mmci_dma_setup(struct mmci_host *host) +-{ +-} ++static struct mmci_host_ops mmci_variant_ops = { ++ .prep_data = mmci_dmae_prep_data, ++ .unprep_data = mmci_dmae_unprep_data, ++ .get_next_data = mmci_dmae_get_next_data, ++ .dma_setup = mmci_dmae_setup, ++ .dma_release = mmci_dmae_release, ++ .dma_start = mmci_dmae_start, ++ .dma_finalize = mmci_dmae_finalize, ++ .dma_error = mmci_dmae_error, ++}; + +-static inline void mmci_dma_release(struct mmci_host *host) ++void mmci_variant_init(struct mmci_host *host) + { ++ host->ops = &mmci_variant_ops; + } ++#endif + +-static inline void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data) ++static void mmci_pre_request(struct mmc_host *mmc, struct mmc_request *mrq) + { +-} ++ struct mmci_host *host = mmc_priv(mmc); ++ struct mmc_data *data = mrq->data; + +-static inline void mmci_dma_finalize(struct mmci_host *host, +- struct mmc_data *data) +-{ +-} ++ if (!data) ++ return; + +-static inline void mmci_dma_data_error(struct mmci_host *host) +-{ ++ WARN_ON(data->host_cookie); ++ ++ if (mmci_validate_data(host, data)) ++ return; ++ ++ mmci_prep_data(host, data, true); + } + +-static inline int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl) ++static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq, ++ int err) + { +- return -ENOSYS; +-} ++ struct mmci_host *host = mmc_priv(mmc); ++ struct mmc_data *data = mrq->data; + +-#define mmci_pre_request NULL +-#define mmci_post_request NULL ++ if (!data || !data->host_cookie) ++ return; + +-#endif ++ mmci_unprep_data(host, data, err); ++} + + static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) + { +@@ -793,11 +1071,11 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) + BUG_ON(1 << blksz_bits != data->blksz); + + if (variant->blksz_datactrl16) +- datactrl = MCI_DPSM_ENABLE | (data->blksz << 16); ++ datactrl = variant->datactrl_dpsm_enable | (data->blksz << 16); + else if (variant->blksz_datactrl4) +- datactrl = MCI_DPSM_ENABLE | (data->blksz << 4); ++ datactrl = variant->datactrl_dpsm_enable | (data->blksz << 4); + else +- datactrl = MCI_DPSM_ENABLE | blksz_bits << 4; ++ datactrl = variant->datactrl_dpsm_enable | blksz_bits << 4; + + if (data->flags & MMC_DATA_READ) + datactrl |= MCI_DPSM_DIRECTION; +@@ -831,7 +1109,7 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) + * Attempt to use DMA operation mode, if this + * should fail, fall back to PIO mode + */ +- if (!mmci_dma_start_data(host, datactrl)) ++ if (!mmci_dma_start(host, datactrl)) + return; + + /* IRQ mode, map the SG list for CPU reading/writing */ +@@ -864,21 +1142,40 @@ static void + mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c) + { + void __iomem *base = host->base; ++ unsigned long long clks; + + dev_dbg(mmc_dev(host->mmc), "op %02x arg %08x flags %08x\n", + cmd->opcode, cmd->arg, cmd->flags); + +- if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) { ++ if (readl(base + MMCICOMMAND) & host->variant->cmdreg_cpsm_enable) { + writel(0, base + MMCICOMMAND); + mmci_reg_delay(host); + } + +- c |= cmd->opcode | MCI_CPSM_ENABLE; ++ if (host->variant->cmdreg_stop && ++ cmd->opcode == MMC_STOP_TRANSMISSION) ++ c |= host->variant->cmdreg_stop; ++ ++ c |= cmd->opcode | host->variant->cmdreg_cpsm_enable; + if (cmd->flags & MMC_RSP_PRESENT) { + if (cmd->flags & MMC_RSP_136) +- c |= MCI_CPSM_LONGRSP; +- c |= MCI_CPSM_RESPONSE; ++ c |= host->variant->cmdreg_lrsp_crc; ++ else if (cmd->flags & MMC_RSP_CRC) ++ c |= host->variant->cmdreg_srsp_crc; ++ else ++ c |= host->variant->cmdreg_srsp; ++ } ++ ++ if (host->variant->busy_timeout && cmd->flags & MMC_RSP_BUSY) { ++ if (!cmd->busy_timeout) ++ cmd->busy_timeout = 1000; ++ ++ clks = (unsigned long long)cmd->busy_timeout * host->cclk; ++ do_div(clks, MSEC_PER_SEC); ++ ++ writel_relaxed(clks, host->base + MMCIDATATIMER); + } ++ + if (/*interrupt*/0) + c |= MCI_CPSM_INTERRUPT; + +@@ -895,21 +1192,22 @@ static void + mmci_data_irq(struct mmci_host *host, struct mmc_data *data, + unsigned int status) + { ++ unsigned int status_err; ++ + /* Make sure we have data to handle */ + if (!data) + return; + + /* First check for errors */ +- if (status & (MCI_DATACRCFAIL | MCI_DATATIMEOUT | +- host->variant->start_err | +- MCI_TXUNDERRUN | MCI_RXOVERRUN)) { ++ status_err = status & (host->variant->start_err | ++ MCI_DATACRCFAIL | MCI_DATATIMEOUT | ++ MCI_TXUNDERRUN | MCI_RXOVERRUN); ++ ++ if (status_err) { + u32 remain, success; + + /* Terminate the DMA transfer */ +- if (dma_inprogress(host)) { +- mmci_dma_data_error(host); +- mmci_dma_unmap(host, data); +- } ++ mmci_dma_error(host); + + /* + * Calculate how far we are into the transfer. Note that +@@ -918,22 +1216,26 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data, + * can be as much as a FIFO-worth of data ahead. This + * matters for FIFO overruns only. + */ +- remain = readl(host->base + MMCIDATACNT); +- success = data->blksz * data->blocks - remain; ++ if (!host->variant->datacnt_useless) { ++ remain = readl(host->base + MMCIDATACNT); ++ success = data->blksz * data->blocks - remain; ++ } else { ++ success = 0; ++ } + + dev_dbg(mmc_dev(host->mmc), "MCI ERROR IRQ, status 0x%08x at 0x%08x\n", +- status, success); +- if (status & MCI_DATACRCFAIL) { ++ status_err, success); ++ if (status_err & MCI_DATACRCFAIL) { + /* Last block was not successful */ + success -= 1; + data->error = -EILSEQ; +- } else if (status & MCI_DATATIMEOUT) { ++ } else if (status_err & MCI_DATATIMEOUT) { + data->error = -ETIMEDOUT; +- } else if (status & MCI_STARTBITERR) { ++ } else if (status_err & MCI_STARTBITERR) { + data->error = -ECOMM; +- } else if (status & MCI_TXUNDERRUN) { ++ } else if (status_err & MCI_TXUNDERRUN) { + data->error = -EIO; +- } else if (status & MCI_RXOVERRUN) { ++ } else if (status_err & MCI_RXOVERRUN) { + if (success > host->variant->fifosize) + success -= host->variant->fifosize; + else +@@ -947,8 +1249,8 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data, + dev_err(mmc_dev(host->mmc), "stray MCI_DATABLOCKEND interrupt\n"); + + if (status & MCI_DATAEND || data->error) { +- if (dma_inprogress(host)) +- mmci_dma_finalize(host, data); ++ mmci_dma_finalize(host, data); ++ + mmci_stop_data(host); + + if (!data->error) +@@ -968,7 +1270,9 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, + unsigned int status) + { + void __iomem *base = host->base; ++ bool busy_resp = !!(cmd->flags & MMC_RSP_BUSY); + bool sbc; ++ u32 err_msk; + + if (!cmd) + return; +@@ -980,18 +1284,21 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, + * handling. Note that we tag on any latent IRQs postponed + * due to waiting for busy status. + */ ++ err_msk = MCI_CMDCRCFAIL | MCI_CMDTIMEOUT; ++ if (host->variant->busy_timeout && busy_resp) ++ err_msk |= MCI_DATATIMEOUT; ++ + if (!((status|host->busy_status) & +- (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND))) ++ (err_msk|MCI_CMDSENT|MCI_CMDRESPEND))) + return; + + /* + * ST Micro variant: handle busy detection. + */ +- if (host->variant->busy_detect) { +- bool busy_resp = !!(cmd->flags & MMC_RSP_BUSY); ++ if (busy_resp && host->variant->busy_detect) { + + /* We are busy with a command, return */ +- if (host->busy_status && ++ if (host->busy_status && !(status & (err_msk)) && + (status & host->variant->busy_detect_flag)) + return; + +@@ -1001,9 +1308,9 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, + * that the special busy status bit is still set before + * proceeding. + */ +- if (!host->busy_status && busy_resp && +- !(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) && +- (readl(base + MMCISTATUS) & host->variant->busy_detect_flag)) { ++ if (!host->busy_status && !(status & (err_msk)) && ++ (readl(base + MMCISTATUS) & ++ host->variant->busy_detect_flag)) { + + /* Clear the busy start IRQ */ + writel(host->variant->busy_detect_mask, +@@ -1045,6 +1352,9 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, + cmd->error = -ETIMEDOUT; + } else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) { + cmd->error = -EILSEQ; ++ } else if (busy_resp && host->variant->busy_timeout && ++ status & MCI_DATATIMEOUT) { ++ cmd->error = -ETIMEDOUT; + } else { + cmd->resp[0] = readl(base + MMCIRESPONSE0); + cmd->resp[1] = readl(base + MMCIRESPONSE1); +@@ -1055,16 +1365,15 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, + if ((!sbc && !cmd->data) || cmd->error) { + if (host->data) { + /* Terminate the DMA transfer */ +- if (dma_inprogress(host)) { +- mmci_dma_data_error(host); +- mmci_dma_unmap(host, host->data); +- } ++ mmci_dma_error(host); ++ + mmci_stop_data(host); + } + mmci_request_end(host, host->mrq); + } else if (sbc) { + mmci_start_command(host, host->mrq->cmd, 0); +- } else if (!(cmd->data->flags & MMC_DATA_READ)) { ++ } else if (!host->variant->datactrl_first && ++ !(cmd->data->flags & MMC_DATA_READ)) { + mmci_start_data(host, cmd->data); + } + } +@@ -1264,7 +1573,7 @@ static irqreturn_t mmci_irq(int irq, void *dev_id) + if (status & host->mask1_reg) + mmci_pio_irq(irq, dev_id); + +- status &= ~MCI_IRQ1MASK; ++ status &= ~host->variant->irq_pio_mask; + } + + /* +@@ -1277,7 +1586,8 @@ static irqreturn_t mmci_irq(int irq, void *dev_id) + * to make sure that both start and end interrupts are always + * cleared one after the other. + */ +- status &= readl(host->base + MMCIMASK0); ++ status &= readl(host->base + MMCIMASK0) | ++ host->variant->busy_detect_flag; + if (host->variant->busy_detect) + writel(status & ~host->variant->busy_detect_mask, + host->base + MMCICLEAR); +@@ -1301,6 +1611,7 @@ static irqreturn_t mmci_irq(int irq, void *dev_id) + status &= ~host->variant->busy_detect_flag; + + ret = 1; ++ + } while (status); + + spin_unlock(&host->lock); +@@ -1328,7 +1639,8 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq) + if (mrq->data) + mmci_get_next_data(host, mrq->data); + +- if (mrq->data && mrq->data->flags & MMC_DATA_READ) ++ if (mrq->data && ++ (host->variant->datactrl_first || mrq->data->flags & MMC_DATA_READ)) + mmci_start_data(host, mrq->data); + + if (mrq->sbc) +@@ -1438,8 +1750,16 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) + + spin_lock_irqsave(&host->lock, flags); + +- mmci_set_clkreg(host, ios->clock); +- mmci_write_pwrreg(host, pwr); ++ if (host->ops && host->ops->set_clkreg) ++ host->ops->set_clkreg(host, ios->clock); ++ else ++ mmci_set_clkreg(host, ios->clock); ++ ++ if (host->ops && host->ops->set_pwrreg) ++ host->ops->set_pwrreg(host, pwr); ++ else ++ mmci_write_pwrreg(host, pwr); ++ + mmci_reg_delay(host); + + spin_unlock_irqrestore(&host->lock, flags); +@@ -1518,6 +1838,12 @@ static int mmci_of_parse(struct device_node *np, struct mmc_host *mmc) + host->pwr_reg_add |= MCI_ST_CMDDIREN; + if (of_get_property(np, "st,sig-pin-fbclk", NULL)) + host->pwr_reg_add |= MCI_ST_FBCLKEN; ++ if (of_get_property(np, "st,sig-dir", NULL)) ++ host->pwr_reg_add |= MCI_STM32_DIRPOL; ++ if (of_get_property(np, "st,neg-edge", NULL)) ++ host->clk_reg_add |= MCI_STM32_CLK_NEGEDGE; ++ if (of_get_property(np, "st,use-ckin", NULL)) ++ host->clk_reg_add |= MCI_STM32_CLK_SELCKIN; + + if (of_get_property(np, "mmc-cap-mmc-highspeed", NULL)) + mmc->caps |= MMC_CAP_MMC_HIGHSPEED; +@@ -1644,6 +1970,8 @@ static int mmci_probe(struct amba_device *dev, + */ + if (variant->st_clkdiv) + mmc->f_min = DIV_ROUND_UP(host->mclk, 257); ++ else if (variant->stm32_clkdiv) ++ mmc->f_min = DIV_ROUND_UP(host->mclk, 2046); + else if (variant->explicit_mclk_control) + mmc->f_min = clk_round_rate(host->clk, 100000); + else +@@ -1665,6 +1993,12 @@ static int mmci_probe(struct amba_device *dev, + + dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max); + ++ host->rst = devm_reset_control_get_optional_exclusive(&dev->dev, NULL); ++ if (IS_ERR(host->rst)) { ++ ret = PTR_ERR(host->rst); ++ goto clk_disable; ++ } ++ + /* Get regulators and the supported OCR mask */ + ret = mmc_regulator_get_supply(mmc); + if (ret) +@@ -1675,13 +2009,6 @@ static int mmci_probe(struct amba_device *dev, + else if (plat->ocr_mask) + dev_warn(mmc_dev(mmc), "Platform OCR mask is ignored\n"); + +- /* DT takes precedence over platform data. */ +- if (!np) { +- if (!plat->cd_invert) +- mmc->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH; +- mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; +- } +- + /* We support these capabilities. */ + mmc->caps |= MMC_CAP_CMD23; + +@@ -1689,6 +2016,8 @@ static int mmci_probe(struct amba_device *dev, + * Enable busy detection. + */ + if (variant->busy_detect) { ++ u32 max_busy_timeout = 0; ++ + mmci_ops.card_busy = mmci_card_busy; + /* + * Not all variants have a flag to enable busy detection +@@ -1698,7 +2027,18 @@ static int mmci_probe(struct amba_device *dev, + mmci_write_datactrlreg(host, + host->variant->busy_dpsm_flag); + mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; +- mmc->max_busy_timeout = 0; ++ ++ if (variant->busy_timeout) ++ max_busy_timeout = ~0UL / (mmc->f_max / MSEC_PER_SEC); ++ ++ mmc->max_busy_timeout = max_busy_timeout; ++ } ++ ++ /* prepare the stop command, used to abort and reinitialized the DPSM */ ++ if (variant->cmdreg_stop) { ++ host->stop_abort.opcode = MMC_STOP_TRANSMISSION; ++ host->stop_abort.arg = 0; ++ host->stop_abort.flags = MMC_RSP_R1B | MMC_CMD_AC; + } + + mmc->ops = &mmci_ops; +@@ -1727,13 +2067,13 @@ static int mmci_probe(struct amba_device *dev, + /* + * Block size can be up to 2048 bytes, but must be a power of two. + */ +- mmc->max_blk_size = 1 << 11; ++ mmc->max_blk_size = 1 << variant->datactrl_blocksz; + + /* + * Limit the number of blocks transferred so that we don't overflow + * the maximum request size. + */ +- mmc->max_blk_count = mmc->max_req_size >> 11; ++ mmc->max_blk_count = mmc->max_req_size >> variant->datactrl_blocksz; + + spin_lock_init(&host->lock); + +@@ -1749,30 +2089,16 @@ static int mmci_probe(struct amba_device *dev, + * - not using DT but using a descriptor table, or + * - using a table of descriptors ALONGSIDE DT, or + * look up these descriptors named "cd" and "wp" right here, fail +- * silently of these do not exist and proceed to try platform data ++ * silently of these do not exist + */ + if (!np) { + ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL); +- if (ret < 0) { +- if (ret == -EPROBE_DEFER) +- goto clk_disable; +- else if (gpio_is_valid(plat->gpio_cd)) { +- ret = mmc_gpio_request_cd(mmc, plat->gpio_cd, 0); +- if (ret) +- goto clk_disable; +- } +- } ++ if (ret == -EPROBE_DEFER) ++ goto clk_disable; + + ret = mmc_gpiod_request_ro(mmc, "wp", 0, false, 0, NULL); +- if (ret < 0) { +- if (ret == -EPROBE_DEFER) +- goto clk_disable; +- else if (gpio_is_valid(plat->gpio_wp)) { +- ret = mmc_gpio_request_ro(mmc, plat->gpio_wp); +- if (ret) +- goto clk_disable; +- } +- } ++ if (ret == -EPROBE_DEFER) ++ goto clk_disable; + } + + ret = devm_request_irq(&dev->dev, dev->irq[0], mmci_irq, IRQF_SHARED, +@@ -1789,7 +2115,7 @@ static int mmci_probe(struct amba_device *dev, + goto clk_disable; + } + +- writel(MCI_IRQENABLE, host->base + MMCIMASK0); ++ writel(MCI_IRQENABLE | variant->start_err, host->base + MMCIMASK0); + + amba_set_drvdata(dev, mmc); + +@@ -1876,7 +2202,8 @@ static void mmci_restore(struct mmci_host *host) + writel(host->datactrl_reg, host->base + MMCIDATACTRL); + writel(host->pwr_reg, host->base + MMCIPOWER); + } +- writel(MCI_IRQENABLE, host->base + MMCIMASK0); ++ writel(MCI_IRQENABLE | host->variant->start_err, ++ host->base + MMCIMASK0); + mmci_reg_delay(host); + + spin_unlock_irqrestore(&host->lock, flags); +@@ -1971,6 +2298,16 @@ static const struct amba_id mmci_ids[] = { + .mask = 0x00ffffff, + .data = &variant_stm32, + }, ++ { ++ .id = 0x10153180, ++ .mask = 0xf0ffffff, ++ .data = &variant_stm32_sdmmc, ++ }, ++ { ++ .id = 0x00253180, ++ .mask = 0xf0ffffff, ++ .data = &variant_stm32_sdmmcv2, ++ }, + /* Qualcomm variants */ + { + .id = 0x00051180, +diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h +index 517591d..36a744a 100644 +--- a/drivers/mmc/host/mmci.h ++++ b/drivers/mmc/host/mmci.h +@@ -23,6 +23,14 @@ + #define MCI_ST_DATA31DIREN (1 << 5) + #define MCI_ST_FBCLKEN (1 << 7) + #define MCI_ST_DATA74DIREN (1 << 8) ++/* ++ * The STM32 sdmmc does not have PWR_UP/OD/ROD ++ * and uses the power register for ++ */ ++#define MCI_STM32_PWR_CYC 0x02 ++#define MCI_STM32_VSWITCH BIT(2) ++#define MCI_STM32_VSWITCHEN BIT(3) ++#define MCI_STM32_DIRPOL BIT(4) + + #define MMCICLOCK 0x004 + #define MCI_CLK_ENABLE (1 << 8) +@@ -50,6 +58,19 @@ + #define MCI_QCOM_CLK_SELECT_IN_FBCLK BIT(15) + #define MCI_QCOM_CLK_SELECT_IN_DDR_MODE (BIT(14) | BIT(15)) + ++/* Modified on STM32 sdmmc */ ++#define MCI_STM32_CLK_CLKDIV_MSK GENMASK(9, 0) ++#define MCI_STM32_CLK_WIDEBUS_4 BIT(14) ++#define MCI_STM32_CLK_WIDEBUS_8 BIT(15) ++#define MCI_STM32_CLK_NEGEDGE BIT(16) ++#define MCI_STM32_CLK_HWFCEN BIT(17) ++#define MCI_STM32_CLK_DDR BIT(18) ++#define MCI_STM32_CLK_BUSSPEED BIT(19) ++#define MCI_STM32_CLK_SEL_MSK GENMASK(21, 20) ++#define MCI_STM32_CLK_SELCK (0 << 20) ++#define MCI_STM32_CLK_SELCKIN (1 << 20) ++#define MCI_STM32_CLK_SELFBCK (2 << 20) ++ + #define MMCIARGUMENT 0x008 + + /* The command register controls the Command Path State Machine (CPSM) */ +@@ -72,6 +93,15 @@ + #define MCI_CPSM_QCOM_CCSDISABLE BIT(15) + #define MCI_CPSM_QCOM_AUTO_CMD19 BIT(16) + #define MCI_CPSM_QCOM_AUTO_CMD21 BIT(21) ++/* Command register in STM32 sdmmc versions */ ++#define MCI_CPSM_STM32_CMDTRANS BIT(6) ++#define MCI_CPSM_STM32_CMDSTOP BIT(7) ++#define MCI_CPSM_STM32_WAITRESP_MASK GENMASK(9, 8) ++#define MCI_CPSM_STM32_NORSP (0 << 8) ++#define MCI_CPSM_STM32_SRSP_CRC (1 << 8) ++#define MCI_CPSM_STM32_SRSP (2 << 8) ++#define MCI_CPSM_STM32_LRSP_CRC (3 << 8) ++#define MCI_CPSM_STM32_ENABLE BIT(12) + + #define MMCIRESPCMD 0x010 + #define MMCIRESPONSE0 0x014 +@@ -130,6 +160,10 @@ + #define MCI_ST_SDIOIT (1 << 22) + #define MCI_ST_CEATAEND (1 << 23) + #define MCI_ST_CARDBUSY (1 << 24) ++/* Extended status bits for the STM32 variants */ ++#define MCI_STM32_DPSMACTIVE BIT(12) ++#define MCI_STM32_BUSYD0 BIT(20) ++#define MCI_STM32_BUSYD0END BIT(21) + + #define MMCICLEAR 0x038 + #define MCI_CMDCRCFAILCLR (1 << 0) +@@ -175,21 +209,45 @@ + #define MCI_ST_SDIOITMASK (1 << 22) + #define MCI_ST_CEATAENDMASK (1 << 23) + #define MCI_ST_BUSYENDMASK (1 << 24) ++/* Extended status bits for the STM32 variants */ ++#define MCI_STM32_BUSYD0ENDMASK BIT(21) + + #define MMCIMASK1 0x040 + #define MMCIFIFOCNT 0x048 + #define MMCIFIFO 0x080 /* to 0x0bc */ + ++/* STM32 sdmmc registers for IDMA (Internal DMA) */ ++#define MMCI_STM32_IDMACTRLR 0x050 ++#define MMCI_STM32_IDMAEN BIT(0) ++#define MMCI_STM32_IDMALLIEN BIT(1) ++ ++#define MMCI_STM32_IDMABSIZER 0x054 ++#define MMCI_STM32_IDMABNDT_SHIFT 5 ++#define MMCI_STM32_IDMABNDT_MASK GENMASK(12, 5) ++ ++#define MMCI_STM32_IDMABASE0R 0x058 ++ ++#define MMCI_STM32_IDMALAR 0x64 ++#define MMCI_STM32_IDMALA_MASK GENMASK(13, 0) ++#define MMCI_STM32_ABR BIT(29) ++#define MMCI_STM32_ULS BIT(30) ++#define MMCI_STM32_ULA BIT(31) ++ ++#define MMCI_STM32_IDMABAR 0x68 ++ + #define MCI_IRQENABLE \ +- (MCI_CMDCRCFAILMASK|MCI_DATACRCFAILMASK|MCI_CMDTIMEOUTMASK| \ +- MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK| \ +- MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_STARTBITERRMASK) ++ (MCI_CMDCRCFAILMASK | MCI_DATACRCFAILMASK | MCI_CMDTIMEOUTMASK | \ ++ MCI_DATATIMEOUTMASK | MCI_TXUNDERRUNMASK | MCI_RXOVERRUNMASK | \ ++ MCI_CMDRESPENDMASK | MCI_CMDSENTMASK) + + /* These interrupts are directed to IRQ1 when two IRQ lines are available */ +-#define MCI_IRQ1MASK \ ++#define MCI_IRQ_PIO_MASK \ + (MCI_RXFIFOHALFFULLMASK | MCI_RXDATAAVLBLMASK | \ + MCI_TXFIFOHALFEMPTYMASK) + ++#define MCI_IRQ_PIO_STM32_MASK \ ++ (MCI_RXFIFOHALFFULLMASK | MCI_TXFIFOHALFEMPTYMASK) ++ + #define NR_SG 128 + + #define MMCI_PINCTRL_STATE_OPENDRAIN "opendrain" +@@ -204,6 +262,11 @@ struct mmci_host; + * @clkreg_enable: enable value for MMCICLOCK register + * @clkreg_8bit_bus_enable: enable value for 8 bit bus + * @clkreg_neg_edge_enable: enable value for inverted data/cmd output ++ * @cmdreg_cpsm_enable: enable value for CPSM ++ * @cmdreg_lrsp_crc: enable value for long response with crc ++ * @cmdreg_srsp_crc: enable value for short response with crc ++ * @cmdreg_srsp: enable value for short response without crc ++ * @cmdreg_stop: enable value for stop and abort transmission + * @datalength_bits: number of bits in the MMCIDATALENGTH register + * @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY + * is asserted (likewise for RX) +@@ -212,11 +275,17 @@ struct mmci_host; + * @data_cmd_enable: enable value for data commands. + * @st_sdio: enable ST specific SDIO logic + * @st_clkdiv: true if using a ST-specific clock divider algorithm ++ * @stm32_clkdiv: true if using a STM32-specific clock divider algorithm + * @datactrl_mask_ddrmode: ddr mode mask in datactrl register. + * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register + * @blksz_datactrl4: true if Block size is at b4..b16 position in datactrl + * register + * @datactrl_mask_sdio: SDIO enable mask in datactrl register ++ * @datactrl_blksz: block size in power of two ++ * @datactrl_dpsm_enable: enable value for DPSM ++ * @datactrl_first: true if data must be setup before send command ++ * @datacnt_useless: true if you could not use datacnt register to read ++ * remaining data + * @pwrreg_powerup: power up value for MMCIPOWER register + * @f_max: maximum clk frequency supported by the controller. + * @signal_direction: input/out direction of bus signals can be indicated +@@ -233,53 +302,77 @@ struct mmci_host; + * @qcom_dml: enables qcom specific dma glue for dma transfers. + * @reversed_irq_handling: handle data irq before cmd irq. + * @mmcimask1: true if variant have a MMCIMASK1 register. ++ * @irq_pio_mask: bitmask used to manage interrupt pio transfert in mmcimask ++ * register + * @start_err: bitmask identifying the STARTBITERR bit inside MMCISTATUS + * register. + * @opendrain: bitmask identifying the OPENDRAIN bit inside MMCIPOWER register ++ * @dma_lli: true if variant has dma link list feature. ++ * @stm32_idmabsize_mask: stm32 sdmmc idma buffer size. + */ + struct variant_data { + unsigned int clkreg; + unsigned int clkreg_enable; + unsigned int clkreg_8bit_bus_enable; + unsigned int clkreg_neg_edge_enable; ++ unsigned int cmdreg_cpsm_enable; ++ unsigned int cmdreg_lrsp_crc; ++ unsigned int cmdreg_srsp_crc; ++ unsigned int cmdreg_srsp; ++ unsigned int cmdreg_stop; + unsigned int datalength_bits; + unsigned int fifosize; + unsigned int fifohalfsize; + unsigned int data_cmd_enable; + unsigned int datactrl_mask_ddrmode; + unsigned int datactrl_mask_sdio; +- bool st_sdio; +- bool st_clkdiv; +- bool blksz_datactrl16; +- bool blksz_datactrl4; ++ unsigned int datactrl_blocksz; ++ unsigned int datactrl_dpsm_enable; ++ u8 datactrl_first:1; ++ u8 datacnt_useless:1; ++ u8 st_sdio:1; ++ u8 st_clkdiv:1; ++ u8 stm32_clkdiv:1; ++ u8 blksz_datactrl16:1; ++ u8 blksz_datactrl4:1; + u32 pwrreg_powerup; + u32 f_max; +- bool signal_direction; +- bool pwrreg_clkgate; +- bool busy_detect; ++ u8 signal_direction:1; ++ u8 pwrreg_clkgate:1; ++ u8 busy_detect:1; ++ u8 busy_timeout:1; + u32 busy_dpsm_flag; + u32 busy_detect_flag; + u32 busy_detect_mask; +- bool pwrreg_nopower; +- bool explicit_mclk_control; +- bool qcom_fifo; +- bool qcom_dml; +- bool reversed_irq_handling; +- bool mmcimask1; ++ u8 pwrreg_nopower:1; ++ u8 explicit_mclk_control:1; ++ u8 qcom_fifo:1; ++ u8 qcom_dml:1; ++ u8 reversed_irq_handling:1; ++ u8 mmcimask1:1; ++ unsigned int irq_pio_mask; + u32 start_err; + u32 opendrain; ++ u8 dma_lli:1; ++ u32 stm32_idmabsize_mask; + void (*init)(struct mmci_host *host); + }; + + /* mmci variant callbacks */ + struct mmci_host_ops { +- void (*dma_setup)(struct mmci_host *host); +-}; +- +-struct mmci_host_next { +- struct dma_async_tx_descriptor *dma_desc; +- struct dma_chan *dma_chan; +- s32 cookie; ++ int (*validate_data)(struct mmci_host *host, struct mmc_data *data); ++ int (*prep_data)(struct mmci_host *host, struct mmc_data *data, ++ bool next); ++ void (*unprep_data)(struct mmci_host *host, struct mmc_data *data, ++ int err); ++ void (*get_next_data)(struct mmci_host *host, struct mmc_data *data); ++ int (*dma_setup)(struct mmci_host *host); ++ void (*dma_release)(struct mmci_host *host); ++ int (*dma_start)(struct mmci_host *host, unsigned int *datactrl); ++ void (*dma_finalize)(struct mmci_host *host, struct mmc_data *data); ++ void (*dma_error)(struct mmci_host *host); ++ void (*set_clkreg)(struct mmci_host *host, unsigned int desired); ++ void (*set_pwrreg)(struct mmci_host *host, unsigned int pwr); + }; + + struct mmci_host { +@@ -287,10 +380,13 @@ struct mmci_host { + void __iomem *base; + struct mmc_request *mrq; + struct mmc_command *cmd; ++ struct mmc_command stop_abort; + struct mmc_data *data; + struct mmc_host *mmc; + struct clk *clk; +- bool singleirq; ++ u8 singleirq:1; ++ ++ struct reset_control *rst; + + spinlock_t lock; + +@@ -301,10 +397,11 @@ struct mmci_host { + u32 pwr_reg; + u32 pwr_reg_add; + u32 clk_reg; ++ u32 clk_reg_add; + u32 datactrl_reg; + u32 busy_status; + u32 mask1_reg; +- bool vqmmc_enabled; ++ u8 vqmmc_enabled:1; + struct mmci_platform_data *plat; + struct mmci_host_ops *ops; + struct variant_data *variant; +@@ -323,18 +420,25 @@ struct mmci_host { + unsigned int size; + int (*get_rx_fifocnt)(struct mmci_host *h, u32 status, int remain); + +-#ifdef CONFIG_DMA_ENGINE +- /* DMA stuff */ +- struct dma_chan *dma_current; +- struct dma_chan *dma_rx_channel; +- struct dma_chan *dma_tx_channel; +- struct dma_async_tx_descriptor *dma_desc_current; +- struct mmci_host_next next_data; +- bool dma_in_progress; ++ u8 use_dma:1; ++ u8 dma_in_progress:1; ++ void *dma_priv; + +-#define dma_inprogress(host) ((host)->dma_in_progress) +-#else +-#define dma_inprogress(host) (0) +-#endif ++ s32 next_cookie; + }; + ++#define dma_inprogress(host) ((host)->dma_in_progress) ++ ++void mmci_write_clkreg(struct mmci_host *host, u32 clk); ++void mmci_write_pwrreg(struct mmci_host *host, u32 pwr); ++ ++int mmci_dmae_prep_data(struct mmci_host *host, struct mmc_data *data, ++ bool next); ++void mmci_dmae_unprep_data(struct mmci_host *host, struct mmc_data *data, ++ int err); ++void mmci_dmae_get_next_data(struct mmci_host *host, struct mmc_data *data); ++int mmci_dmae_setup(struct mmci_host *host); ++void mmci_dmae_release(struct mmci_host *host); ++int mmci_dmae_start(struct mmci_host *host, unsigned int *datactrl); ++void mmci_dmae_finalize(struct mmci_host *host, struct mmc_data *data); ++void mmci_dmae_error(struct mmci_host *host); +diff --git a/drivers/mmc/host/mmci_qcom_dml.c b/drivers/mmc/host/mmci_qcom_dml.c +index be3fab5..25d0a75 100644 +--- a/drivers/mmc/host/mmci_qcom_dml.c ++++ b/drivers/mmc/host/mmci_qcom_dml.c +@@ -119,19 +119,23 @@ static int of_get_dml_pipe_index(struct device_node *np, const char *name) + } + + /* Initialize the dml hardware connected to SD Card controller */ +-static void qcom_dma_setup(struct mmci_host *host) ++static int qcom_dma_setup(struct mmci_host *host) + { + u32 config; + void __iomem *base; + int consumer_id, producer_id; + struct device_node *np = host->mmc->parent->of_node; + ++ if (mmci_dmae_setup(host)) ++ return -EINVAL; ++ + consumer_id = of_get_dml_pipe_index(np, "tx"); + producer_id = of_get_dml_pipe_index(np, "rx"); + + if (producer_id < 0 || consumer_id < 0) { + host->variant->qcom_dml = false; +- return; ++ mmci_dmae_release(host); ++ return -EINVAL; + } + + base = host->base + DML_OFFSET; +@@ -175,10 +179,19 @@ static void qcom_dma_setup(struct mmci_host *host) + + /* Make sure dml initialization is finished */ + mb(); ++ ++ return 0; + } + + static struct mmci_host_ops qcom_variant_ops = { ++ .prep_data = mmci_dmae_prep_data, ++ .unprep_data = mmci_dmae_unprep_data, ++ .get_next_data = mmci_dmae_get_next_data, + .dma_setup = qcom_dma_setup, ++ .dma_release = mmci_dmae_release, ++ .dma_start = mmci_dmae_start, ++ .dma_finalize = mmci_dmae_finalize, ++ .dma_error = mmci_dmae_error, + }; + + void qcom_variant_init(struct mmci_host *host) +diff --git a/drivers/mmc/host/mmci_stm32_sdmmc.c b/drivers/mmc/host/mmci_stm32_sdmmc.c +new file mode 100644 +index 0000000..cfbfc6f +--- /dev/null ++++ b/drivers/mmc/host/mmci_stm32_sdmmc.c +@@ -0,0 +1,282 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved ++ * Author: Ludovic.barre@st.com for STMicroelectronics. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "mmci.h" ++ ++#define SDMMC_LLI_BUF_LEN PAGE_SIZE ++#define SDMMC_IDMA_BURST BIT(MMCI_STM32_IDMABNDT_SHIFT) ++ ++struct sdmmc_lli_desc { ++ u32 idmalar; ++ u32 idmabase; ++ u32 idmasize; ++}; ++ ++struct sdmmc_priv { ++ dma_addr_t sg_dma; ++ void *sg_cpu; ++}; ++ ++int sdmmc_idma_validate_data(struct mmci_host *host, ++ struct mmc_data *data) ++{ ++ struct scatterlist *sg; ++ int i; ++ ++ /* ++ * idma has constraints on idmabase & idmasize for each element ++ * excepted the last element which has no constraint on idmasize ++ */ ++ for_each_sg(data->sg, sg, data->sg_len - 1, i) { ++ if (!IS_ALIGNED(sg_dma_address(data->sg), sizeof(u32)) || ++ !IS_ALIGNED(sg_dma_len(data->sg), SDMMC_IDMA_BURST)) { ++ dev_err(mmc_dev(host->mmc), ++ "unaligned scatterlist: ofst:%x length:%d\n", ++ data->sg->offset, data->sg->length); ++ return -EINVAL; ++ } ++ } ++ ++ if (!IS_ALIGNED(sg_dma_address(data->sg), sizeof(u32))) { ++ dev_err(mmc_dev(host->mmc), ++ "unaligned last scatterlist: ofst:%x length:%d\n", ++ data->sg->offset, data->sg->length); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int _sdmmc_idma_prep_data(struct mmci_host *host, ++ struct mmc_data *data) ++{ ++ int n_elem; ++ ++ n_elem = dma_map_sg(mmc_dev(host->mmc), ++ data->sg, ++ data->sg_len, ++ mmc_get_dma_dir(data)); ++ ++ if (!n_elem) { ++ dev_err(mmc_dev(host->mmc), "dma_map_sg failed\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int sdmmc_idma_prep_data(struct mmci_host *host, ++ struct mmc_data *data, bool next) ++{ ++ /* Check if job is already prepared. */ ++ if (!next && data->host_cookie == host->next_cookie) ++ return 0; ++ ++ return _sdmmc_idma_prep_data(host, data); ++} ++ ++static void sdmmc_idma_unprep_data(struct mmci_host *host, ++ struct mmc_data *data, int err) ++{ ++ dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, ++ mmc_get_dma_dir(data)); ++} ++ ++static int sdmmc_idma_setup(struct mmci_host *host) ++{ ++ struct sdmmc_priv *idma; ++ ++ idma = devm_kzalloc(mmc_dev(host->mmc), sizeof(*idma), GFP_KERNEL); ++ if (!idma) ++ return -ENOMEM; ++ ++ host->dma_priv = idma; ++ ++ if (host->variant->dma_lli) { ++ idma->sg_cpu = dmam_alloc_coherent(mmc_dev(host->mmc), ++ SDMMC_LLI_BUF_LEN, ++ &idma->sg_dma, GFP_KERNEL); ++ if (!idma->sg_cpu) { ++ dev_err(mmc_dev(host->mmc), ++ "Failed to alloc IDMA descriptor\n"); ++ return -ENOMEM; ++ } ++ host->mmc->max_segs = SDMMC_LLI_BUF_LEN / ++ sizeof(struct sdmmc_lli_desc); ++ host->mmc->max_seg_size = host->variant->stm32_idmabsize_mask; ++ } else { ++ host->mmc->max_segs = 1; ++ host->mmc->max_seg_size = host->mmc->max_req_size; ++ } ++ ++ return 0; ++} ++ ++static int sdmmc_idma_start(struct mmci_host *host, unsigned int *datactrl) ++ ++{ ++ struct sdmmc_priv *idma = host->dma_priv; ++ struct sdmmc_lli_desc *desc = (struct sdmmc_lli_desc *)idma->sg_cpu; ++ struct mmc_data *data = host->data; ++ struct scatterlist *sg; ++ int i; ++ ++ if (!host->variant->dma_lli || data->sg_len == 1) { ++ writel_relaxed(sg_dma_address(data->sg), ++ host->base + MMCI_STM32_IDMABASE0R); ++ writel_relaxed(MMCI_STM32_IDMAEN, ++ host->base + MMCI_STM32_IDMACTRLR); ++ return 0; ++ } ++ ++ for_each_sg(data->sg, sg, data->sg_len, i) { ++ desc[i].idmalar = (i + 1) * sizeof(struct sdmmc_lli_desc); ++ desc[i].idmalar |= MMCI_STM32_ULA | MMCI_STM32_ULS ++ | MMCI_STM32_ABR; ++ desc[i].idmabase = sg_dma_address(sg); ++ desc[i].idmasize = sg_dma_len(sg); ++ } ++ ++ /* notice the end of link list */ ++ desc[data->sg_len - 1].idmalar &= ~MMCI_STM32_ULA; ++ ++ dma_wmb(); ++ writel_relaxed(idma->sg_dma, host->base + MMCI_STM32_IDMABAR); ++ writel_relaxed(desc[0].idmalar, host->base + MMCI_STM32_IDMALAR); ++ writel_relaxed(desc[0].idmabase, host->base + MMCI_STM32_IDMABASE0R); ++ writel_relaxed(desc[0].idmasize, host->base + MMCI_STM32_IDMABSIZER); ++ writel_relaxed(MMCI_STM32_IDMAEN | MMCI_STM32_IDMALLIEN, ++ host->base + MMCI_STM32_IDMACTRLR); ++ ++ return 0; ++} ++ ++static void sdmmc_idma_finalize(struct mmci_host *host, struct mmc_data *data) ++{ ++ writel_relaxed(0, host->base + MMCI_STM32_IDMACTRLR); ++} ++ ++static void mmci_sdmmc_set_clkreg(struct mmci_host *host, unsigned int desired) ++{ ++ unsigned int clk = 0, ddr = 0; ++ ++ if (host->mmc->ios.timing == MMC_TIMING_MMC_DDR52 || ++ host->mmc->ios.timing == MMC_TIMING_UHS_DDR50) ++ ddr = MCI_STM32_CLK_DDR; ++ ++ /* ++ * cclk = mclk / (2 * clkdiv) ++ * clkdiv 0 => bypass ++ * in ddr mode bypass is not possible ++ */ ++ if (desired) { ++ if (desired >= host->mclk && !ddr) { ++ host->cclk = host->mclk; ++ } else { ++ clk = DIV_ROUND_UP(host->mclk, 2 * desired); ++ if (clk > MCI_STM32_CLK_CLKDIV_MSK) ++ clk = MCI_STM32_CLK_CLKDIV_MSK; ++ host->cclk = host->mclk / (2 * clk); ++ } ++ } else { ++ /* ++ * while power-on phase the clock can't be define to 0, ++ * Only power-off and power-cyc deactivate the clock. ++ * if desired clock is 0, set max divider ++ */ ++ clk = MCI_STM32_CLK_CLKDIV_MSK; ++ host->cclk = host->mclk / (2 * clk); ++ } ++ ++ /* Set actual clock for debug */ ++ if (host->mmc->ios.power_mode == MMC_POWER_ON) ++ host->mmc->actual_clock = host->cclk; ++ else ++ host->mmc->actual_clock = 0; ++ ++ if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4) ++ clk |= MCI_STM32_CLK_WIDEBUS_4; ++ if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8) ++ clk |= MCI_STM32_CLK_WIDEBUS_8; ++ ++ clk |= MCI_STM32_CLK_HWFCEN; ++ clk |= host->clk_reg_add; ++ clk |= ddr; ++ ++ /* ++ * SDMMC_FBCK is selected when an external Delay Block is needed ++ * with SDR104. ++ */ ++ if (host->mmc->ios.timing >= MMC_TIMING_UHS_SDR50) { ++ clk |= MCI_STM32_CLK_BUSSPEED; ++ if (host->mmc->ios.timing == MMC_TIMING_UHS_SDR104) { ++ clk &= ~MCI_STM32_CLK_SEL_MSK; ++ clk |= MCI_STM32_CLK_SELFBCK; ++ } ++ } ++ ++ mmci_write_clkreg(host, clk); ++} ++ ++static void mmci_sdmmc_set_pwrreg(struct mmci_host *host, unsigned int pwr) ++{ ++ struct mmc_ios ios = host->mmc->ios; ++ ++ pwr = host->pwr_reg_add; ++ ++ if (ios.power_mode == MMC_POWER_OFF) { ++ /* Only a reset could power-off sdmmc */ ++ reset_control_assert(host->rst); ++ udelay(2); ++ reset_control_deassert(host->rst); ++ ++ /* ++ * Set the SDMMC in Power-cycle state. ++ * This will make that the SDMMC_D[7:0], SDMMC_CMD and SDMMC_CK ++ * are driven low, to prevent the Card from being supplied ++ * through the signal lines. ++ */ ++ mmci_write_pwrreg(host, MCI_STM32_PWR_CYC | pwr); ++ } else if (ios.power_mode == MMC_POWER_ON) { ++ /* ++ * After power-off (reset): the irq mask defined in probe ++ * functionis lost ++ * ault irq mask (probe) must be activated ++ */ ++ writel(MCI_IRQENABLE | host->variant->start_err, ++ host->base + MMCIMASK0); ++ ++ /* ++ * After a power-cycle state, we must set the SDMMC in ++ * Power-off. The SDMMC_D[7:0], SDMMC_CMD and SDMMC_CK are ++ * driven high. Then we can set the SDMMC to Power-on state ++ */ ++ mmci_write_pwrreg(host, MCI_PWR_OFF | pwr); ++ mdelay(1); ++ mmci_write_pwrreg(host, MCI_PWR_ON | pwr); ++ } ++} ++ ++static struct mmci_host_ops sdmmc_variant_ops = { ++ .validate_data = sdmmc_idma_validate_data, ++ .prep_data = sdmmc_idma_prep_data, ++ .unprep_data = sdmmc_idma_unprep_data, ++ .dma_setup = sdmmc_idma_setup, ++ .dma_start = sdmmc_idma_start, ++ .dma_finalize = sdmmc_idma_finalize, ++ .set_clkreg = mmci_sdmmc_set_clkreg, ++ .set_pwrreg = mmci_sdmmc_set_pwrreg, ++}; ++ ++void sdmmc_variant_init(struct mmci_host *host) ++{ ++ host->ops = &sdmmc_variant_ops; ++} +diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig +index 5fc9a1b..70f26c2 100644 +--- a/drivers/mtd/nand/raw/Kconfig ++++ b/drivers/mtd/nand/raw/Kconfig +@@ -561,4 +561,13 @@ config MTD_NAND_TEGRA + is supported. Extra OOB bytes when using HW ECC are currently + not supported. + ++config MTD_NAND_STM32_FMC2 ++ tristate "Support for NAND controller on STM32MP SoCs" ++ depends on MACH_STM32MP157 || COMPILE_TEST ++ help ++ Enables support for NAND Flash chips on SoCs containing the FMC2 ++ NAND controller. This controller is found on STM32MP SoCs. ++ The controller supports a maximum 8k page size and supports ++ a maximum 8-bit correction error per sector of 512 bytes. ++ + endif # MTD_NAND +diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile +index d5a5f98..4ef7559 100644 +--- a/drivers/mtd/nand/raw/Makefile ++++ b/drivers/mtd/nand/raw/Makefile +@@ -57,6 +57,7 @@ obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/ + obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o + obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o + obj-$(CONFIG_MTD_NAND_TEGRA) += tegra_nand.o ++obj-$(CONFIG_MTD_NAND_STM32_FMC2) += stm32_fmc2_nand.o + + nand-objs := nand_base.o nand_bbt.o nand_timings.o nand_ids.o + nand-objs += nand_amd.o +diff --git a/drivers/mtd/nand/raw/stm32_fmc2_nand.c b/drivers/mtd/nand/raw/stm32_fmc2_nand.c +new file mode 100644 +index 0000000..a6d60c2 +--- /dev/null ++++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c +@@ -0,0 +1,2031 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) STMicroelectronics 2018 ++ * Author: Christophe Kerello ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Bad block marker length */ ++#define FMC2_BBM_LEN 2 ++ ++/* ECC step size */ ++#define FMC2_ECC_STEP_SIZE 512 ++ ++/* BCHDSRx registers length */ ++#define FMC2_BCHDSRS_LEN 20 ++ ++/* HECCR length */ ++#define FMC2_HECCR_LEN 4 ++ ++/* Max requests done for a 8k nand page size */ ++#define FMC2_MAX_SG 16 ++ ++/* Max chip enable */ ++#define FMC2_MAX_CE 2 ++ ++/* Max ecc buffer length */ ++#define FMC2_MAX_ECC_BUF_LEN (FMC2_BCHDSRS_LEN * FMC2_MAX_SG) ++ ++/* Timings */ ++#define FMC2_THIZ 1 ++#define FMC2_TIO 8000 ++#define FMC2_TSYNC 3000 ++#define FMC2_PCR_TIMING_MASK 0xf ++#define FMC2_PMEM_PATT_TIMING_MASK 0xff ++ ++/* FMC2 Controller Registers */ ++#define FMC2_BCR1 0x0 ++#define FMC2_PCR 0x80 ++#define FMC2_SR 0x84 ++#define FMC2_PMEM 0x88 ++#define FMC2_PATT 0x8c ++#define FMC2_HECCR 0x94 ++#define FMC2_CSQCR 0x200 ++#define FMC2_CSQCFGR1 0x204 ++#define FMC2_CSQCFGR2 0x208 ++#define FMC2_CSQCFGR3 0x20c ++#define FMC2_CSQAR1 0x210 ++#define FMC2_CSQAR2 0x214 ++#define FMC2_CSQIER 0x220 ++#define FMC2_CSQISR 0x224 ++#define FMC2_CSQICR 0x228 ++#define FMC2_CSQEMSR 0x230 ++#define FMC2_BCHIER 0x250 ++#define FMC2_BCHISR 0x254 ++#define FMC2_BCHICR 0x258 ++#define FMC2_BCHPBR1 0x260 ++#define FMC2_BCHPBR2 0x264 ++#define FMC2_BCHPBR3 0x268 ++#define FMC2_BCHPBR4 0x26c ++#define FMC2_BCHDSR0 0x27c ++#define FMC2_BCHDSR1 0x280 ++#define FMC2_BCHDSR2 0x284 ++#define FMC2_BCHDSR3 0x288 ++#define FMC2_BCHDSR4 0x28c ++ ++/* Register: FMC2_BCR1 */ ++#define FMC2_BCR1_FMC2EN BIT(31) ++ ++/* Register: FMC2_PCR */ ++#define FMC2_PCR_PWAITEN BIT(1) ++#define FMC2_PCR_PBKEN BIT(2) ++#define FMC2_PCR_PWID_MASK GENMASK(5, 4) ++#define FMC2_PCR_PWID(x) (((x) & 0x3) << 4) ++#define FMC2_PCR_PWID_BUSWIDTH_8 0 ++#define FMC2_PCR_PWID_BUSWIDTH_16 1 ++#define FMC2_PCR_ECCEN BIT(6) ++#define FMC2_PCR_ECCALG BIT(8) ++#define FMC2_PCR_TCLR_MASK GENMASK(12, 9) ++#define FMC2_PCR_TCLR(x) (((x) & 0xf) << 9) ++#define FMC2_PCR_TCLR_DEFAULT 0xf ++#define FMC2_PCR_TAR_MASK GENMASK(16, 13) ++#define FMC2_PCR_TAR(x) (((x) & 0xf) << 13) ++#define FMC2_PCR_TAR_DEFAULT 0xf ++#define FMC2_PCR_ECCSS_MASK GENMASK(19, 17) ++#define FMC2_PCR_ECCSS(x) (((x) & 0x7) << 17) ++#define FMC2_PCR_ECCSS_512 1 ++#define FMC2_PCR_ECCSS_2048 3 ++#define FMC2_PCR_BCHECC BIT(24) ++#define FMC2_PCR_WEN BIT(25) ++ ++/* Register: FMC2_SR */ ++#define FMC2_SR_NWRF BIT(6) ++ ++/* Register: FMC2_PMEM */ ++#define FMC2_PMEM_MEMSET(x) (((x) & 0xff) << 0) ++#define FMC2_PMEM_MEMWAIT(x) (((x) & 0xff) << 8) ++#define FMC2_PMEM_MEMHOLD(x) (((x) & 0xff) << 16) ++#define FMC2_PMEM_MEMHIZ(x) (((x) & 0xff) << 24) ++#define FMC2_PMEM_DEFAULT 0x0a0a0a0a ++ ++/* Register: FMC2_PATT */ ++#define FMC2_PATT_ATTSET(x) (((x) & 0xff) << 0) ++#define FMC2_PATT_ATTWAIT(x) (((x) & 0xff) << 8) ++#define FMC2_PATT_ATTHOLD(x) (((x) & 0xff) << 16) ++#define FMC2_PATT_ATTHIZ(x) (((x) & 0xff) << 24) ++#define FMC2_PATT_DEFAULT 0x0a0a0a0a ++ ++/* Register: FMC2_CSQCR */ ++#define FMC2_CSQCR_CSQSTART BIT(0) ++ ++/* Register: FMC2_CSQCFGR1 */ ++#define FMC2_CSQCFGR1_CMD2EN BIT(1) ++#define FMC2_CSQCFGR1_DMADEN BIT(2) ++#define FMC2_CSQCFGR1_ACYNBR(x) (((x) & 0x7) << 4) ++#define FMC2_CSQCFGR1_CMD1(x) (((x) & 0xff) << 8) ++#define FMC2_CSQCFGR1_CMD2(x) (((x) & 0xff) << 16) ++#define FMC2_CSQCFGR1_CMD1T BIT(24) ++#define FMC2_CSQCFGR1_CMD2T BIT(25) ++ ++/* Register: FMC2_CSQCFGR2 */ ++#define FMC2_CSQCFGR2_SQSDTEN BIT(0) ++#define FMC2_CSQCFGR2_RCMD2EN BIT(1) ++#define FMC2_CSQCFGR2_DMASEN BIT(2) ++#define FMC2_CSQCFGR2_RCMD1(x) (((x) & 0xff) << 8) ++#define FMC2_CSQCFGR2_RCMD2(x) (((x) & 0xff) << 16) ++#define FMC2_CSQCFGR2_RCMD1T BIT(24) ++#define FMC2_CSQCFGR2_RCMD2T BIT(25) ++ ++/* Register: FMC2_CSQCFGR3 */ ++#define FMC2_CSQCFGR3_SNBR(x) (((x) & 0x1f) << 8) ++#define FMC2_CSQCFGR3_AC1T BIT(16) ++#define FMC2_CSQCFGR3_AC2T BIT(17) ++#define FMC2_CSQCFGR3_AC3T BIT(18) ++#define FMC2_CSQCFGR3_AC4T BIT(19) ++#define FMC2_CSQCFGR3_AC5T BIT(20) ++#define FMC2_CSQCFGR3_SDT BIT(21) ++#define FMC2_CSQCFGR3_RAC1T BIT(22) ++#define FMC2_CSQCFGR3_RAC2T BIT(23) ++ ++/* Register: FMC2_CSQCAR1 */ ++#define FMC2_CSQCAR1_ADDC1(x) (((x) & 0xff) << 0) ++#define FMC2_CSQCAR1_ADDC2(x) (((x) & 0xff) << 8) ++#define FMC2_CSQCAR1_ADDC3(x) (((x) & 0xff) << 16) ++#define FMC2_CSQCAR1_ADDC4(x) (((x) & 0xff) << 24) ++ ++/* Register: FMC2_CSQCAR2 */ ++#define FMC2_CSQCAR2_ADDC5(x) (((x) & 0xff) << 0) ++#define FMC2_CSQCAR2_NANDCEN(x) (((x) & 0x3) << 10) ++#define FMC2_CSQCAR2_SAO(x) (((x) & 0xffff) << 16) ++ ++/* Register: FMC2_CSQIER */ ++#define FMC2_CSQIER_TCIE BIT(0) ++ ++/* Register: FMC2_CSQICR */ ++#define FMC2_CSQICR_CLEAR_IRQ GENMASK(4, 0) ++ ++/* Register: FMC2_CSQEMSR */ ++#define FMC2_CSQEMSR_SEM GENMASK(15, 0) ++ ++/* Register: FMC2_BCHIER */ ++#define FMC2_BCHIER_DERIE BIT(1) ++#define FMC2_BCHIER_EPBRIE BIT(4) ++ ++/* Register: FMC2_BCHICR */ ++#define FMC2_BCHICR_CLEAR_IRQ GENMASK(4, 0) ++ ++/* Register: FMC2_BCHDSR0 */ ++#define FMC2_BCHDSR0_DUE BIT(0) ++#define FMC2_BCHDSR0_DEF BIT(1) ++#define FMC2_BCHDSR0_DEN_MASK GENMASK(7, 4) ++#define FMC2_BCHDSR0_DEN_SHIFT 4 ++ ++/* Register: FMC2_BCHDSR1 */ ++#define FMC2_BCHDSR1_EBP1_MASK GENMASK(12, 0) ++#define FMC2_BCHDSR1_EBP2_MASK GENMASK(28, 16) ++#define FMC2_BCHDSR1_EBP2_SHIFT 16 ++ ++/* Register: FMC2_BCHDSR2 */ ++#define FMC2_BCHDSR2_EBP3_MASK GENMASK(12, 0) ++#define FMC2_BCHDSR2_EBP4_MASK GENMASK(28, 16) ++#define FMC2_BCHDSR2_EBP4_SHIFT 16 ++ ++/* Register: FMC2_BCHDSR3 */ ++#define FMC2_BCHDSR3_EBP5_MASK GENMASK(12, 0) ++#define FMC2_BCHDSR3_EBP6_MASK GENMASK(28, 16) ++#define FMC2_BCHDSR3_EBP6_SHIFT 16 ++ ++/* Register: FMC2_BCHDSR4 */ ++#define FMC2_BCHDSR4_EBP7_MASK GENMASK(12, 0) ++#define FMC2_BCHDSR4_EBP8_MASK GENMASK(28, 16) ++#define FMC2_BCHDSR4_EBP8_SHIFT 16 ++ ++enum stm32_fmc2_ecc { ++ FMC2_ECC_HAM = 1, ++ FMC2_ECC_BCH4 = 4, ++ FMC2_ECC_BCH8 = 8 ++}; ++ ++enum stm32_fmc2_irq_state { ++ FMC2_IRQ_UNKNOWN = 0, ++ FMC2_IRQ_BCH, ++ FMC2_IRQ_SEQ ++}; ++ ++struct stm32_fmc2_timings { ++ u8 tclr; ++ u8 tar; ++ u8 thiz; ++ u8 twait; ++ u8 thold_mem; ++ u8 tset_mem; ++ u8 thold_att; ++ u8 tset_att; ++}; ++ ++struct stm32_fmc2_nand { ++ struct nand_chip chip; ++ struct stm32_fmc2_timings timings; ++ int ncs; ++ int cs_used[FMC2_MAX_CE]; ++}; ++ ++static inline struct stm32_fmc2_nand *to_fmc2_nand(struct nand_chip *chip) ++{ ++ return container_of(chip, struct stm32_fmc2_nand, chip); ++} ++ ++struct stm32_fmc2_nfc { ++ struct nand_controller base; ++ struct stm32_fmc2_nand nand; ++ struct device *dev; ++ void __iomem *io_base; ++ void __iomem *data_base[FMC2_MAX_CE]; ++ void __iomem *cmd_base[FMC2_MAX_CE]; ++ void __iomem *addr_base[FMC2_MAX_CE]; ++ phys_addr_t io_phys_addr; ++ phys_addr_t data_phys_addr[FMC2_MAX_CE]; ++ struct clk *clk; ++ u8 irq_state; ++ ++ struct dma_chan *dma_tx_ch; ++ struct dma_chan *dma_rx_ch; ++ struct dma_chan *dma_ecc_ch; ++ struct sg_table dma_data_sg; ++ struct sg_table dma_ecc_sg; ++ u8 *ecc_buf; ++ int dma_ecc_len; ++ ++ struct completion complete; ++ struct completion dma_data_complete; ++ struct completion dma_ecc_complete; ++ ++ u8 cs_assigned; ++ int cs_sel; ++}; ++ ++static inline struct stm32_fmc2_nfc *to_stm32_nfc(struct nand_controller *base) ++{ ++ return container_of(base, struct stm32_fmc2_nfc, base); ++} ++ ++/* Enable irq sources in case of the sequencer is used */ ++static inline void stm32_fmc2_enable_seq_irq(struct stm32_fmc2_nfc *fmc2) ++{ ++ u32 csqier = readl_relaxed(fmc2->io_base + FMC2_CSQIER); ++ ++ csqier |= FMC2_CSQIER_TCIE; ++ ++ fmc2->irq_state = FMC2_IRQ_SEQ; ++ ++ writel_relaxed(csqier, fmc2->io_base + FMC2_CSQIER); ++} ++ ++/* Disable irq sources in case of the sequencer is used */ ++static inline void stm32_fmc2_disable_seq_irq(struct stm32_fmc2_nfc *fmc2) ++{ ++ u32 csqier = readl_relaxed(fmc2->io_base + FMC2_CSQIER); ++ ++ csqier &= ~FMC2_CSQIER_TCIE; ++ ++ writel_relaxed(csqier, fmc2->io_base + FMC2_CSQIER); ++ ++ fmc2->irq_state = FMC2_IRQ_UNKNOWN; ++} ++ ++/* Clear irq sources in case of the sequencer is used */ ++static inline void stm32_fmc2_clear_seq_irq(struct stm32_fmc2_nfc *fmc2) ++{ ++ writel_relaxed(FMC2_CSQICR_CLEAR_IRQ, fmc2->io_base + FMC2_CSQICR); ++} ++ ++/* Enable irq sources in case of bch is used */ ++static inline void stm32_fmc2_enable_bch_irq(struct stm32_fmc2_nfc *fmc2, ++ int mode) ++{ ++ u32 bchier = readl_relaxed(fmc2->io_base + FMC2_BCHIER); ++ ++ if (mode == NAND_ECC_WRITE) ++ bchier |= FMC2_BCHIER_EPBRIE; ++ else ++ bchier |= FMC2_BCHIER_DERIE; ++ ++ fmc2->irq_state = FMC2_IRQ_BCH; ++ ++ writel_relaxed(bchier, fmc2->io_base + FMC2_BCHIER); ++} ++ ++/* Disable irq sources in case of bch is used */ ++static inline void stm32_fmc2_disable_bch_irq(struct stm32_fmc2_nfc *fmc2) ++{ ++ u32 bchier = readl_relaxed(fmc2->io_base + FMC2_BCHIER); ++ ++ bchier &= ~FMC2_BCHIER_DERIE; ++ bchier &= ~FMC2_BCHIER_EPBRIE; ++ ++ writel_relaxed(bchier, fmc2->io_base + FMC2_BCHIER); ++ ++ fmc2->irq_state = FMC2_IRQ_UNKNOWN; ++} ++ ++/* Clear irq sources in case of bch is used */ ++static inline void stm32_fmc2_clear_bch_irq(struct stm32_fmc2_nfc *fmc2) ++{ ++ writel_relaxed(FMC2_BCHICR_CLEAR_IRQ, fmc2->io_base + FMC2_BCHICR); ++} ++ ++/* ++ * Enable ecc logic and reset syndrome/parity bits previously calculated ++ * Syndrome/parity bits is cleared by setting the ECCEN bit to 0 ++ */ ++static void stm32_fmc2_hwctl(struct mtd_info *mtd, int mode) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct stm32_fmc2_nfc *fmc2 = to_stm32_nfc(chip->controller); ++ u32 pcr = readl_relaxed(fmc2->io_base + FMC2_PCR); ++ ++ pcr &= ~FMC2_PCR_ECCEN; ++ writel_relaxed(pcr, fmc2->io_base + FMC2_PCR); ++ ++ if (chip->ecc.strength != FMC2_ECC_HAM) { ++ if (mode == NAND_ECC_WRITE) ++ pcr |= FMC2_PCR_WEN; ++ else ++ pcr &= ~FMC2_PCR_WEN; ++ writel_relaxed(pcr, fmc2->io_base + FMC2_PCR); ++ ++ reinit_completion(&fmc2->complete); ++ stm32_fmc2_clear_bch_irq(fmc2); ++ stm32_fmc2_enable_bch_irq(fmc2, mode); ++ } ++ ++ pcr = readl_relaxed(fmc2->io_base + FMC2_PCR); ++ pcr |= FMC2_PCR_ECCEN; ++ writel_relaxed(pcr, fmc2->io_base + FMC2_PCR); ++} ++ ++/* ++ * ECC Hamming calculation ++ * ECC is 3 bytes for 512 bytes of data (supports error correction up to ++ * max of 1-bit) ++ */ ++static inline void stm32_fmc2_ham_set_ecc(const u32 ecc_sta, u8 *ecc) ++{ ++ ecc[0] = ecc_sta; ++ ecc[1] = ecc_sta >> 8; ++ ecc[2] = ecc_sta >> 16; ++} ++ ++static int stm32_fmc2_ham_calculate(struct mtd_info *mtd, const u8 *data, ++ u8 *ecc) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct stm32_fmc2_nfc *fmc2 = to_stm32_nfc(chip->controller); ++ u32 sr, heccr, pcr; ++ int ret; ++ ++ ret = readl_relaxed_poll_timeout(fmc2->io_base + FMC2_SR, ++ sr, sr & FMC2_SR_NWRF, 10, 1000); ++ if (ret) { ++ dev_err(fmc2->dev, "ham timeout\n"); ++ return ret; ++ } ++ ++ heccr = readl_relaxed(fmc2->io_base + FMC2_HECCR); ++ ++ stm32_fmc2_ham_set_ecc(heccr, ecc); ++ ++ /* Disable ecc */ ++ pcr = readl_relaxed(fmc2->io_base + FMC2_PCR); ++ pcr &= ~FMC2_PCR_ECCEN; ++ writel_relaxed(pcr, fmc2->io_base + FMC2_PCR); ++ ++ return 0; ++} ++ ++static int stm32_fmc2_ham_correct(struct mtd_info *mtd, u8 *dat, ++ u8 *read_ecc, u8 *calc_ecc) ++{ ++ u8 bit_position = 0, b0, b1, b2; ++ u32 byte_addr = 0, b; ++ u32 i, shifting = 1; ++ ++ /* Indicate which bit and byte is faulty (if any) */ ++ b0 = read_ecc[0] ^ calc_ecc[0]; ++ b1 = read_ecc[1] ^ calc_ecc[1]; ++ b2 = read_ecc[2] ^ calc_ecc[2]; ++ b = b0 | (b1 << 8) | (b2 << 16); ++ ++ /* No errors */ ++ if (likely(!b)) ++ return 0; ++ ++ /* Calculate bit position */ ++ for (i = 0; i < 3; i++) { ++ switch (b % 4) { ++ case 2: ++ bit_position += shifting; ++ case 1: ++ break; ++ default: ++ return -EBADMSG; ++ } ++ shifting <<= 1; ++ b >>= 2; ++ } ++ ++ /* Calculate byte position */ ++ shifting = 1; ++ for (i = 0; i < 9; i++) { ++ switch (b % 4) { ++ case 2: ++ byte_addr += shifting; ++ case 1: ++ break; ++ default: ++ return -EBADMSG; ++ } ++ shifting <<= 1; ++ b >>= 2; ++ } ++ ++ /* Flip the bit */ ++ dat[byte_addr] ^= (1 << bit_position); ++ ++ return 1; ++} ++ ++/* ++ * ECC BCH calculation and correction ++ * ECC is 7/13 bytes for 512 bytes of data (supports error correction up to ++ * max of 4-bit/8-bit) ++ */ ++static int stm32_fmc2_bch_calculate(struct mtd_info *mtd, const u8 *data, ++ u8 *ecc) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct stm32_fmc2_nfc *fmc2 = to_stm32_nfc(chip->controller); ++ u32 bchpbr, pcr; ++ ++ /* Wait until the BCH code is ready */ ++ if (!wait_for_completion_timeout(&fmc2->complete, ++ msecs_to_jiffies(1000))) { ++ dev_err(fmc2->dev, "bch timeout\n"); ++ stm32_fmc2_disable_bch_irq(fmc2); ++ return -ETIMEDOUT; ++ } ++ ++ /* Read parity bits */ ++ bchpbr = readl_relaxed(fmc2->io_base + FMC2_BCHPBR1); ++ ecc[0] = bchpbr; ++ ecc[1] = bchpbr >> 8; ++ ecc[2] = bchpbr >> 16; ++ ecc[3] = bchpbr >> 24; ++ ++ bchpbr = readl_relaxed(fmc2->io_base + FMC2_BCHPBR2); ++ ecc[4] = bchpbr; ++ ecc[5] = bchpbr >> 8; ++ ecc[6] = bchpbr >> 16; ++ ++ if (chip->ecc.strength == FMC2_ECC_BCH8) { ++ ecc[7] = bchpbr >> 24; ++ ++ bchpbr = readl_relaxed(fmc2->io_base + FMC2_BCHPBR3); ++ ecc[8] = bchpbr; ++ ecc[9] = bchpbr >> 8; ++ ecc[10] = bchpbr >> 16; ++ ecc[11] = bchpbr >> 24; ++ ++ bchpbr = readl_relaxed(fmc2->io_base + FMC2_BCHPBR4); ++ ecc[12] = bchpbr; ++ } ++ ++ /* Disable ecc */ ++ pcr = readl_relaxed(fmc2->io_base + FMC2_PCR); ++ pcr &= ~FMC2_PCR_ECCEN; ++ writel_relaxed(pcr, fmc2->io_base + FMC2_PCR); ++ ++ return 0; ++} ++ ++/* BCH algorithm correction */ ++static int stm32_fmc2_bch_decode(int eccsize, u8 *dat, u32 *ecc_sta) ++{ ++ u32 bchdsr0 = ecc_sta[0]; ++ u32 bchdsr1 = ecc_sta[1]; ++ u32 bchdsr2 = ecc_sta[2]; ++ u32 bchdsr3 = ecc_sta[3]; ++ u32 bchdsr4 = ecc_sta[4]; ++ u16 pos[8]; ++ int i, den; ++ unsigned int nb_errs = 0; ++ ++ /* No errors found */ ++ if (likely(!(bchdsr0 & FMC2_BCHDSR0_DEF))) ++ return 0; ++ ++ /* Too many errors detected */ ++ if (unlikely(bchdsr0 & FMC2_BCHDSR0_DUE)) ++ return -EBADMSG; ++ ++ pos[0] = bchdsr1 & FMC2_BCHDSR1_EBP1_MASK; ++ pos[1] = (bchdsr1 & FMC2_BCHDSR1_EBP2_MASK) >> FMC2_BCHDSR1_EBP2_SHIFT; ++ pos[2] = bchdsr2 & FMC2_BCHDSR2_EBP3_MASK; ++ pos[3] = (bchdsr2 & FMC2_BCHDSR2_EBP4_MASK) >> FMC2_BCHDSR2_EBP4_SHIFT; ++ pos[4] = bchdsr3 & FMC2_BCHDSR3_EBP5_MASK; ++ pos[5] = (bchdsr3 & FMC2_BCHDSR3_EBP6_MASK) >> FMC2_BCHDSR3_EBP6_SHIFT; ++ pos[6] = bchdsr4 & FMC2_BCHDSR4_EBP7_MASK; ++ pos[7] = (bchdsr4 & FMC2_BCHDSR4_EBP8_MASK) >> FMC2_BCHDSR4_EBP8_SHIFT; ++ ++ den = (bchdsr0 & FMC2_BCHDSR0_DEN_MASK) >> FMC2_BCHDSR0_DEN_SHIFT; ++ for (i = 0; i < den; i++) { ++ if (pos[i] < eccsize * 8) { ++ change_bit(pos[i], (unsigned long *)dat); ++ nb_errs++; ++ } ++ } ++ ++ return nb_errs; ++} ++ ++static int stm32_fmc2_bch_correct(struct mtd_info *mtd, u8 *dat, ++ u8 *read_ecc, u8 *calc_ecc) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct stm32_fmc2_nfc *fmc2 = to_stm32_nfc(chip->controller); ++ u32 ecc_sta[5], pcr; ++ ++ /* Wait until the decoding error is ready */ ++ if (!wait_for_completion_timeout(&fmc2->complete, ++ msecs_to_jiffies(1000))) { ++ dev_err(fmc2->dev, "bch timeout\n"); ++ stm32_fmc2_disable_bch_irq(fmc2); ++ return -ETIMEDOUT; ++ } ++ ++ ecc_sta[0] = readl_relaxed(fmc2->io_base + FMC2_BCHDSR0); ++ ecc_sta[1] = readl_relaxed(fmc2->io_base + FMC2_BCHDSR1); ++ ecc_sta[2] = readl_relaxed(fmc2->io_base + FMC2_BCHDSR2); ++ ecc_sta[3] = readl_relaxed(fmc2->io_base + FMC2_BCHDSR3); ++ ecc_sta[4] = readl_relaxed(fmc2->io_base + FMC2_BCHDSR4); ++ ++ /* Disable ecc */ ++ pcr = readl_relaxed(fmc2->io_base + FMC2_PCR); ++ pcr &= ~FMC2_PCR_ECCEN; ++ writel_relaxed(pcr, fmc2->io_base + FMC2_PCR); ++ ++ return stm32_fmc2_bch_decode(chip->ecc.size, dat, ecc_sta); ++} ++ ++static int stm32_fmc2_read_page(struct mtd_info *mtd, ++ struct nand_chip *chip, u8 *buf, ++ int oob_required, int page) ++{ ++ int ret, i, s, stat, eccsize = chip->ecc.size; ++ int eccbytes = chip->ecc.bytes; ++ int eccsteps = chip->ecc.steps; ++ int eccstrength = chip->ecc.strength; ++ u8 *p = buf; ++ u8 *ecc_calc = chip->ecc.calc_buf; ++ u8 *ecc_code = chip->ecc.code_buf; ++ unsigned int max_bitflips = 0; ++ ++ ret = nand_read_page_op(chip, page, 0, NULL, 0); ++ if (ret) ++ return ret; ++ ++ for (i = mtd->writesize + FMC2_BBM_LEN, s = 0; s < eccsteps; ++ s++, i += eccbytes, p += eccsize) { ++ chip->ecc.hwctl(mtd, NAND_ECC_READ); ++ ++ /* Read the nand page sector (512 bytes) */ ++ ret = nand_change_read_column_op(chip, s * eccsize, p, ++ eccsize, false); ++ if (ret) ++ return ret; ++ ++ /* Read the corresponding ecc bytes */ ++ ret = nand_change_read_column_op(chip, i, ecc_code, ++ eccbytes, false); ++ if (ret) ++ return ret; ++ ++ /* Correct the data */ ++ stat = chip->ecc.correct(mtd, p, ecc_code, ecc_calc); ++ if (stat == -EBADMSG) ++ /* Check for empty pages with bitflips */ ++ stat = nand_check_erased_ecc_chunk(p, eccsize, ++ ecc_code, eccbytes, ++ NULL, 0, ++ eccstrength); ++ ++ if (stat < 0) { ++ mtd->ecc_stats.failed++; ++ } else { ++ mtd->ecc_stats.corrected += stat; ++ max_bitflips = max_t(unsigned int, max_bitflips, stat); ++ } ++ } ++ ++ /* Read oob */ ++ if (oob_required) { ++ ret = nand_change_read_column_op(chip, mtd->writesize, ++ chip->oob_poi, mtd->oobsize, ++ false); ++ if (ret) ++ return ret; ++ } ++ ++ return max_bitflips; ++} ++ ++/* Sequencer read/write configuration */ ++static void stm32_fmc2_rw_page_init(struct nand_chip *chip, int page, ++ int raw, bool write_data) ++{ ++ struct stm32_fmc2_nfc *fmc2 = to_stm32_nfc(chip->controller); ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ u32 csqcfgr1, csqcfgr2, csqcfgr3; ++ u32 csqar1, csqar2; ++ u32 ecc_offset = mtd->writesize + FMC2_BBM_LEN; ++ u32 pcr = readl_relaxed(fmc2->io_base + FMC2_PCR); ++ ++ if (write_data) ++ pcr |= FMC2_PCR_WEN; ++ else ++ pcr &= ~FMC2_PCR_WEN; ++ writel_relaxed(pcr, fmc2->io_base + FMC2_PCR); ++ ++ /* ++ * - Set Program Page/Page Read command ++ * - Enable DMA request data ++ * - Set timings ++ */ ++ csqcfgr1 = FMC2_CSQCFGR1_DMADEN | FMC2_CSQCFGR1_CMD1T; ++ if (write_data) ++ csqcfgr1 |= FMC2_CSQCFGR1_CMD1(NAND_CMD_SEQIN); ++ else ++ csqcfgr1 |= FMC2_CSQCFGR1_CMD1(NAND_CMD_READ0) | ++ FMC2_CSQCFGR1_CMD2EN | ++ FMC2_CSQCFGR1_CMD2(NAND_CMD_READSTART) | ++ FMC2_CSQCFGR1_CMD2T; ++ ++ /* ++ * - Set Random Data Input/Random Data Read command ++ * - Enable the sequencer to access the Spare data area ++ * - Enable DMA request status decoding for read ++ * - Set timings ++ */ ++ if (write_data) ++ csqcfgr2 = FMC2_CSQCFGR2_RCMD1(NAND_CMD_RNDIN); ++ else ++ csqcfgr2 = FMC2_CSQCFGR2_RCMD1(NAND_CMD_RNDOUT) | ++ FMC2_CSQCFGR2_RCMD2EN | ++ FMC2_CSQCFGR2_RCMD2(NAND_CMD_RNDOUTSTART) | ++ FMC2_CSQCFGR2_RCMD1T | ++ FMC2_CSQCFGR2_RCMD2T; ++ if (!raw) { ++ csqcfgr2 |= write_data ? 0 : FMC2_CSQCFGR2_DMASEN; ++ csqcfgr2 |= FMC2_CSQCFGR2_SQSDTEN; ++ } ++ ++ /* ++ * - Set the number of sectors to be written ++ * - Set timings ++ */ ++ csqcfgr3 = FMC2_CSQCFGR3_SNBR(chip->ecc.steps - 1); ++ if (write_data) { ++ csqcfgr3 |= FMC2_CSQCFGR3_RAC2T; ++ if (chip->options & NAND_ROW_ADDR_3) ++ csqcfgr3 |= FMC2_CSQCFGR3_AC5T; ++ else ++ csqcfgr3 |= FMC2_CSQCFGR3_AC4T; ++ } ++ ++ /* ++ * Set the fourth first address cycles ++ * Byte 1 and byte 2 => column, we start at 0x0 ++ * Byte 3 and byte 4 => page ++ */ ++ csqar1 = FMC2_CSQCAR1_ADDC3(page); ++ csqar1 |= FMC2_CSQCAR1_ADDC4(page >> 8); ++ ++ /* ++ * - Set chip enable number ++ * - Set ecc byte offset in the spare area ++ * - Calculate the number of address cycles to be issued ++ * - Set byte 5 of address cycle if needed ++ */ ++ csqar2 = FMC2_CSQCAR2_NANDCEN(fmc2->cs_sel); ++ if (chip->options & NAND_BUSWIDTH_16) ++ csqar2 |= FMC2_CSQCAR2_SAO(ecc_offset >> 1); ++ else ++ csqar2 |= FMC2_CSQCAR2_SAO(ecc_offset); ++ if (chip->options & NAND_ROW_ADDR_3) { ++ csqcfgr1 |= FMC2_CSQCFGR1_ACYNBR(5); ++ csqar2 |= FMC2_CSQCAR2_ADDC5(page >> 16); ++ } else { ++ csqcfgr1 |= FMC2_CSQCFGR1_ACYNBR(4); ++ } ++ ++ writel_relaxed(csqcfgr1, fmc2->io_base + FMC2_CSQCFGR1); ++ writel_relaxed(csqcfgr2, fmc2->io_base + FMC2_CSQCFGR2); ++ writel_relaxed(csqcfgr3, fmc2->io_base + FMC2_CSQCFGR3); ++ writel_relaxed(csqar1, fmc2->io_base + FMC2_CSQAR1); ++ writel_relaxed(csqar2, fmc2->io_base + FMC2_CSQAR2); ++} ++ ++static void stm32_fmc2_dma_callback(void *arg) ++{ ++ complete((struct completion *)arg); ++} ++ ++/* Read/write data from/to a page */ ++static int stm32_fmc2_xfer(struct nand_chip *chip, const u8 *buf, ++ int raw, bool write_data) ++{ ++ struct stm32_fmc2_nfc *fmc2 = to_stm32_nfc(chip->controller); ++ struct dma_async_tx_descriptor *desc_data, *desc_ecc; ++ struct scatterlist *sg; ++ struct dma_chan *dma_ch = fmc2->dma_rx_ch; ++ enum dma_data_direction dma_data_dir = DMA_FROM_DEVICE; ++ enum dma_transfer_direction dma_transfer_dir = DMA_DEV_TO_MEM; ++ u32 csqcr = readl_relaxed(fmc2->io_base + FMC2_CSQCR); ++ int eccsteps = chip->ecc.steps; ++ int eccsize = chip->ecc.size; ++ const u8 *p = buf; ++ int s, ret; ++ ++ /* Configure DMA data */ ++ if (write_data) { ++ dma_data_dir = DMA_TO_DEVICE; ++ dma_transfer_dir = DMA_MEM_TO_DEV; ++ dma_ch = fmc2->dma_tx_ch; ++ } ++ ++ for_each_sg(fmc2->dma_data_sg.sgl, sg, eccsteps, s) { ++ sg_set_buf(sg, p, eccsize); ++ p += eccsize; ++ } ++ ++ ret = dma_map_sg(fmc2->dev, fmc2->dma_data_sg.sgl, ++ eccsteps, dma_data_dir); ++ if (ret < 0) ++ return ret; ++ ++ desc_data = dmaengine_prep_slave_sg(dma_ch, fmc2->dma_data_sg.sgl, ++ eccsteps, dma_transfer_dir, ++ DMA_PREP_INTERRUPT); ++ if (!desc_data) { ++ ret = -ENOMEM; ++ goto err_unmap_data; ++ } ++ ++ reinit_completion(&fmc2->dma_data_complete); ++ reinit_completion(&fmc2->complete); ++ desc_data->callback = stm32_fmc2_dma_callback; ++ desc_data->callback_param = &fmc2->dma_data_complete; ++ ret = dma_submit_error(dmaengine_submit(desc_data)); ++ if (ret) ++ goto err_unmap_data; ++ ++ dma_async_issue_pending(dma_ch); ++ ++ if (!write_data && !raw) { ++ /* Configure DMA ecc status */ ++ p = fmc2->ecc_buf; ++ for_each_sg(fmc2->dma_ecc_sg.sgl, sg, eccsteps, s) { ++ sg_set_buf(sg, p, fmc2->dma_ecc_len); ++ p += fmc2->dma_ecc_len; ++ } ++ ++ ret = dma_map_sg(fmc2->dev, fmc2->dma_ecc_sg.sgl, ++ eccsteps, dma_data_dir); ++ if (ret < 0) ++ goto err_unmap_data; ++ ++ desc_ecc = dmaengine_prep_slave_sg(fmc2->dma_ecc_ch, ++ fmc2->dma_ecc_sg.sgl, ++ eccsteps, dma_transfer_dir, ++ DMA_PREP_INTERRUPT); ++ if (!desc_ecc) { ++ ret = -ENOMEM; ++ goto err_unmap_ecc; ++ } ++ ++ reinit_completion(&fmc2->dma_ecc_complete); ++ desc_ecc->callback = stm32_fmc2_dma_callback; ++ desc_ecc->callback_param = &fmc2->dma_ecc_complete; ++ ret = dma_submit_error(dmaengine_submit(desc_ecc)); ++ if (ret) ++ goto err_unmap_ecc; ++ ++ dma_async_issue_pending(fmc2->dma_ecc_ch); ++ } ++ ++ stm32_fmc2_clear_seq_irq(fmc2); ++ stm32_fmc2_enable_seq_irq(fmc2); ++ ++ /* Start the transfer */ ++ csqcr |= FMC2_CSQCR_CSQSTART; ++ writel_relaxed(csqcr, fmc2->io_base + FMC2_CSQCR); ++ ++ /* Wait end of sequencer transfer */ ++ if (!wait_for_completion_timeout(&fmc2->complete, ++ msecs_to_jiffies(1000))) { ++ dev_err(fmc2->dev, "seq timeout\n"); ++ stm32_fmc2_disable_seq_irq(fmc2); ++ dmaengine_terminate_all(dma_ch); ++ if (!write_data && !raw) ++ dmaengine_terminate_all(fmc2->dma_ecc_ch); ++ ret = -ETIMEDOUT; ++ goto err_unmap_ecc; ++ } ++ ++ /* Wait DMA data transfer completion */ ++ if (!wait_for_completion_timeout(&fmc2->dma_data_complete, ++ msecs_to_jiffies(100))) { ++ dev_err(fmc2->dev, "data DMA timeout\n"); ++ dmaengine_terminate_all(dma_ch); ++ ret = -ETIMEDOUT; ++ } ++ ++ /* Wait DMA ecc transfer completion */ ++ if (!write_data && !raw) { ++ if (!wait_for_completion_timeout(&fmc2->dma_ecc_complete, ++ msecs_to_jiffies(100))) { ++ dev_err(fmc2->dev, "ecc DMA timeout\n"); ++ dmaengine_terminate_all(fmc2->dma_ecc_ch); ++ ret = -ETIMEDOUT; ++ } ++ } ++ ++err_unmap_ecc: ++ if (!write_data && !raw) ++ dma_unmap_sg(fmc2->dev, fmc2->dma_ecc_sg.sgl, ++ eccsteps, dma_data_dir); ++ ++err_unmap_data: ++ dma_unmap_sg(fmc2->dev, fmc2->dma_data_sg.sgl, eccsteps, dma_data_dir); ++ ++ return ret; ++} ++ ++static int stm32_fmc2_sequencer_write(struct nand_chip *chip, ++ const u8 *buf, int oob_required, ++ int page, int raw) ++{ ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ int ret; ++ ++ /* Configure the sequencer */ ++ stm32_fmc2_rw_page_init(chip, page, raw, true); ++ ++ /* Write the page */ ++ ret = stm32_fmc2_xfer(chip, buf, raw, true); ++ if (ret) ++ return ret; ++ ++ /* Write oob */ ++ if (oob_required) { ++ ret = nand_change_write_column_op(chip, mtd->writesize, ++ chip->oob_poi, mtd->oobsize, ++ false); ++ if (ret) ++ return ret; ++ } ++ ++ return nand_prog_page_end_op(chip); ++} ++ ++static int stm32_fmc2_sequencer_write_page(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ const u8 *buf, ++ int oob_required, ++ int page) ++{ ++ return stm32_fmc2_sequencer_write(chip, buf, oob_required, page, false); ++} ++ ++static int stm32_fmc2_sequencer_write_page_raw(struct mtd_info *mtd, ++ struct nand_chip *chip, ++ const u8 *buf, ++ int oob_required, ++ int page) ++{ ++ return stm32_fmc2_sequencer_write(chip, buf, oob_required, page, true); ++} ++ ++/* Get a status indicating which sectors have errors */ ++static inline u16 stm32_fmc2_get_mapping_status(struct stm32_fmc2_nfc *fmc2) ++{ ++ u32 csqemsr = readl_relaxed(fmc2->io_base + FMC2_CSQEMSR); ++ ++ return csqemsr & FMC2_CSQEMSR_SEM; ++} ++ ++static int stm32_fmc2_sequencer_correct(struct mtd_info *mtd, u8 *dat, ++ u8 *read_ecc, u8 *calc_ecc) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct stm32_fmc2_nfc *fmc2 = to_stm32_nfc(chip->controller); ++ int eccbytes = chip->ecc.bytes; ++ int eccsteps = chip->ecc.steps; ++ int eccstrength = chip->ecc.strength; ++ int i, s, eccsize = chip->ecc.size; ++ u32 *ecc_sta = (u32 *)fmc2->ecc_buf; ++ u16 sta_map = stm32_fmc2_get_mapping_status(fmc2); ++ unsigned int max_bitflips = 0; ++ ++ for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, dat += eccsize) { ++ int stat = 0; ++ ++ if (eccstrength == FMC2_ECC_HAM) { ++ /* Ecc_sta = FMC2_HECCR */ ++ if (sta_map & BIT(s)) { ++ stm32_fmc2_ham_set_ecc(*ecc_sta, &calc_ecc[i]); ++ stat = stm32_fmc2_ham_correct(mtd, dat, ++ &read_ecc[i], ++ &calc_ecc[i]); ++ } ++ ecc_sta++; ++ } else { ++ /* ++ * Ecc_sta[0] = FMC2_BCHDSR0 ++ * Ecc_sta[1] = FMC2_BCHDSR1 ++ * Ecc_sta[2] = FMC2_BCHDSR2 ++ * Ecc_sta[3] = FMC2_BCHDSR3 ++ * Ecc_sta[4] = FMC2_BCHDSR4 ++ */ ++ if (sta_map & BIT(s)) ++ stat = stm32_fmc2_bch_decode(eccsize, dat, ++ ecc_sta); ++ ecc_sta += 5; ++ } ++ ++ if (stat == -EBADMSG) ++ /* Check for empty pages with bitflips */ ++ stat = nand_check_erased_ecc_chunk(dat, eccsize, ++ &read_ecc[i], ++ eccbytes, ++ NULL, 0, ++ eccstrength); ++ ++ if (stat < 0) { ++ mtd->ecc_stats.failed++; ++ } else { ++ mtd->ecc_stats.corrected += stat; ++ max_bitflips = max_t(unsigned int, max_bitflips, stat); ++ } ++ } ++ ++ return max_bitflips; ++} ++ ++static int stm32_fmc2_sequencer_read_page(struct mtd_info *mtd, ++ struct nand_chip *chip, u8 *buf, ++ int oob_required, int page) ++{ ++ struct stm32_fmc2_nfc *fmc2 = to_stm32_nfc(chip->controller); ++ u8 *ecc_calc = chip->ecc.calc_buf; ++ u8 *ecc_code = chip->ecc.code_buf; ++ u16 sta_map; ++ int ret; ++ ++ /* Configure the sequencer */ ++ stm32_fmc2_rw_page_init(chip, page, 0, false); ++ ++ /* Read the page */ ++ ret = stm32_fmc2_xfer(chip, buf, 0, false); ++ if (ret) ++ return ret; ++ ++ sta_map = stm32_fmc2_get_mapping_status(fmc2); ++ ++ /* Check if errors happen */ ++ if (likely(!sta_map)) { ++ if (oob_required) ++ return nand_change_read_column_op(chip, mtd->writesize, ++ chip->oob_poi, ++ mtd->oobsize, false); ++ ++ return 0; ++ } ++ ++ /* Read oob */ ++ ret = nand_change_read_column_op(chip, mtd->writesize, ++ chip->oob_poi, mtd->oobsize, false); ++ if (ret) ++ return ret; ++ ++ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0, ++ chip->ecc.total); ++ if (ret) ++ return ret; ++ ++ /* Correct data */ ++ return chip->ecc.correct(mtd, buf, ecc_code, ecc_calc); ++} ++ ++static int stm32_fmc2_sequencer_read_page_raw(struct mtd_info *mtd, ++ struct nand_chip *chip, u8 *buf, ++ int oob_required, int page) ++{ ++ int ret; ++ ++ /* Configure the sequencer */ ++ stm32_fmc2_rw_page_init(chip, page, 1, false); ++ ++ /* Read the page */ ++ ret = stm32_fmc2_xfer(chip, buf, 1, false); ++ if (ret) ++ return ret; ++ ++ /* Read oob */ ++ if (oob_required) ++ return nand_change_read_column_op(chip, mtd->writesize, ++ chip->oob_poi, mtd->oobsize, ++ false); ++ ++ return 0; ++} ++ ++static irqreturn_t stm32_fmc2_irq(int irq, void *dev_id) ++{ ++ struct stm32_fmc2_nfc *fmc2 = (struct stm32_fmc2_nfc *)dev_id; ++ ++ if (fmc2->irq_state == FMC2_IRQ_SEQ) ++ /* Sequencer is used */ ++ stm32_fmc2_disable_seq_irq(fmc2); ++ else if (fmc2->irq_state == FMC2_IRQ_BCH) ++ /* BCH is used */ ++ stm32_fmc2_disable_bch_irq(fmc2); ++ ++ complete(&fmc2->complete); ++ ++ return IRQ_HANDLED; ++} ++ ++static void stm32_fmc2_set_buswidth_16(struct stm32_fmc2_nfc *fmc2, bool set) ++{ ++ u32 pcr = readl_relaxed(fmc2->io_base + FMC2_PCR); ++ ++ pcr &= ~FMC2_PCR_PWID_MASK; ++ if (set) ++ pcr |= FMC2_PCR_PWID(FMC2_PCR_PWID_BUSWIDTH_16); ++ writel_relaxed(pcr, fmc2->io_base + FMC2_PCR); ++} ++ ++static void stm32_fmc2_read_data(struct nand_chip *chip, void *buf, ++ unsigned int len, bool force_8bit) ++{ ++ struct stm32_fmc2_nfc *fmc2 = to_stm32_nfc(chip->controller); ++ void __iomem *io_addr_r = fmc2->data_base[fmc2->cs_sel]; ++ u8 *p = buf; ++ unsigned int i; ++ ++ if (force_8bit) ++ goto read_8bit; ++ ++ if (IS_ALIGNED(len, sizeof(u32))) { ++ u32 *p = buf; ++ ++ len /= sizeof(u32); ++ for (i = 0; i < len; i++) ++ p[i] = readl_relaxed(io_addr_r); ++ return; ++ } ++ ++ if (chip->options & NAND_BUSWIDTH_16) { ++ u16 *p = buf; ++ ++ len /= sizeof(u16); ++ for (i = 0; i < len; i++) ++ p[i] = readw_relaxed(io_addr_r); ++ return; ++ } ++ ++read_8bit: ++ if (chip->options & NAND_BUSWIDTH_16) ++ /* Reconfigure bus width to 8-bit */ ++ stm32_fmc2_set_buswidth_16(fmc2, false); ++ ++ for (i = 0; i < len; i++) ++ p[i] = readb_relaxed(io_addr_r); ++ ++ if (chip->options & NAND_BUSWIDTH_16) ++ /* Reconfigure bus width to 16-bit */ ++ stm32_fmc2_set_buswidth_16(fmc2, true); ++} ++ ++static void stm32_fmc2_write_data(struct nand_chip *chip, const void *buf, ++ unsigned int len, bool force_8bit) ++{ ++ struct stm32_fmc2_nfc *fmc2 = to_stm32_nfc(chip->controller); ++ void __iomem *io_addr_w = fmc2->data_base[fmc2->cs_sel]; ++ const u8 *p = buf; ++ unsigned int i; ++ ++ if (force_8bit) ++ goto write_8bit; ++ ++ if (IS_ALIGNED(len, sizeof(u32))) { ++ const u32 *p = buf; ++ ++ len /= sizeof(u32); ++ for (i = 0; i < len; i++) ++ writel_relaxed(p[i], io_addr_w); ++ return; ++ } ++ ++ if (chip->options & NAND_BUSWIDTH_16) { ++ const u16 *p = buf; ++ ++ len /= sizeof(u16); ++ for (i = 0; i < len; i++) ++ writew_relaxed(p[i], io_addr_w); ++ return; ++ } ++ ++write_8bit: ++ if (chip->options & NAND_BUSWIDTH_16) ++ /* Reconfigure bus width to 8-bit */ ++ stm32_fmc2_set_buswidth_16(fmc2, false); ++ ++ for (i = 0; i < len; i++) ++ writeb_relaxed(p[i], io_addr_w); ++ ++ if (chip->options & NAND_BUSWIDTH_16) ++ /* Reconfigure bus width to 16-bit */ ++ stm32_fmc2_set_buswidth_16(fmc2, true); ++} ++ ++static int stm32_fmc2_exec_op(struct nand_chip *chip, ++ const struct nand_operation *op, ++ bool check_only) ++{ ++ struct stm32_fmc2_nfc *fmc2 = to_stm32_nfc(chip->controller); ++ const struct nand_op_instr *instr = NULL; ++ unsigned int op_id, i; ++ int ret = 0; ++ ++ if (check_only) ++ return ret; ++ ++ for (op_id = 0; op_id < op->ninstrs; op_id++) { ++ instr = &op->instrs[op_id]; ++ ++ switch (instr->type) { ++ case NAND_OP_CMD_INSTR: ++ writeb_relaxed(instr->ctx.cmd.opcode, ++ fmc2->cmd_base[fmc2->cs_sel]); ++ break; ++ ++ case NAND_OP_ADDR_INSTR: ++ for (i = 0; i < instr->ctx.addr.naddrs; i++) ++ writeb_relaxed(instr->ctx.addr.addrs[i], ++ fmc2->addr_base[fmc2->cs_sel]); ++ break; ++ ++ case NAND_OP_DATA_IN_INSTR: ++ stm32_fmc2_read_data(chip, instr->ctx.data.buf.in, ++ instr->ctx.data.len, ++ instr->ctx.data.force_8bit); ++ break; ++ ++ case NAND_OP_DATA_OUT_INSTR: ++ stm32_fmc2_write_data(chip, instr->ctx.data.buf.out, ++ instr->ctx.data.len, ++ instr->ctx.data.force_8bit); ++ break; ++ ++ case NAND_OP_WAITRDY_INSTR: ++ ret = nand_soft_waitrdy(chip, ++ instr->ctx.waitrdy.timeout_ms); ++ break; ++ } ++ } ++ ++ return ret; ++} ++ ++/* Timings configuration */ ++static void stm32_fmc2_timings_init(struct nand_chip *chip) ++{ ++ struct stm32_fmc2_nfc *fmc2 = to_stm32_nfc(chip->controller); ++ struct stm32_fmc2_nand *nand = to_fmc2_nand(chip); ++ struct stm32_fmc2_timings *timings = &nand->timings; ++ u32 pcr = readl_relaxed(fmc2->io_base + FMC2_PCR); ++ u32 pmem, patt; ++ ++ /* Set tclr/tar timings */ ++ pcr &= ~FMC2_PCR_TCLR_MASK; ++ pcr |= FMC2_PCR_TCLR(timings->tclr); ++ pcr &= ~FMC2_PCR_TAR_MASK; ++ pcr |= FMC2_PCR_TAR(timings->tar); ++ ++ /* Set tset/twait/thold/thiz timings in common bank */ ++ pmem = FMC2_PMEM_MEMSET(timings->tset_mem); ++ pmem |= FMC2_PMEM_MEMWAIT(timings->twait); ++ pmem |= FMC2_PMEM_MEMHOLD(timings->thold_mem); ++ pmem |= FMC2_PMEM_MEMHIZ(timings->thiz); ++ ++ /* Set tset/twait/thold/thiz timings in attribut bank */ ++ patt = FMC2_PATT_ATTSET(timings->tset_att); ++ patt |= FMC2_PATT_ATTWAIT(timings->twait); ++ patt |= FMC2_PATT_ATTHOLD(timings->thold_att); ++ patt |= FMC2_PATT_ATTHIZ(timings->thiz); ++ ++ writel_relaxed(pcr, fmc2->io_base + FMC2_PCR); ++ writel_relaxed(pmem, fmc2->io_base + FMC2_PMEM); ++ writel_relaxed(patt, fmc2->io_base + FMC2_PATT); ++} ++ ++/* Controller initialization */ ++static void stm32_fmc2_init(struct stm32_fmc2_nfc *fmc2) ++{ ++ u32 pcr = readl_relaxed(fmc2->io_base + FMC2_PCR); ++ u32 bcr1 = readl_relaxed(fmc2->io_base + FMC2_BCR1); ++ ++ /* Set CS used to undefined */ ++ fmc2->cs_sel = -1; ++ ++ /* Enable wait feature and nand flash memory bank */ ++ pcr |= FMC2_PCR_PWAITEN; ++ pcr |= FMC2_PCR_PBKEN; ++ ++ /* Set buswidth to 8 bits mode for identification */ ++ pcr &= ~FMC2_PCR_PWID_MASK; ++ ++ /* Ecc logic is disabled */ ++ pcr &= ~FMC2_PCR_ECCEN; ++ ++ /* Default mode */ ++ pcr &= ~FMC2_PCR_ECCALG; ++ pcr &= ~FMC2_PCR_BCHECC; ++ pcr &= ~FMC2_PCR_WEN; ++ ++ /* Set default ecc sector size */ ++ pcr &= ~FMC2_PCR_ECCSS_MASK; ++ pcr |= FMC2_PCR_ECCSS(FMC2_PCR_ECCSS_2048); ++ ++ /* Set default tclr/tar timings */ ++ pcr &= ~FMC2_PCR_TCLR_MASK; ++ pcr |= FMC2_PCR_TCLR(FMC2_PCR_TCLR_DEFAULT); ++ pcr &= ~FMC2_PCR_TAR_MASK; ++ pcr |= FMC2_PCR_TAR(FMC2_PCR_TAR_DEFAULT); ++ ++ /* Enable FMC2 controller */ ++ bcr1 |= FMC2_BCR1_FMC2EN; ++ ++ writel_relaxed(bcr1, fmc2->io_base + FMC2_BCR1); ++ writel_relaxed(pcr, fmc2->io_base + FMC2_PCR); ++ writel_relaxed(FMC2_PMEM_DEFAULT, fmc2->io_base + FMC2_PMEM); ++ writel_relaxed(FMC2_PATT_DEFAULT, fmc2->io_base + FMC2_PATT); ++} ++ ++/* Controller configuration */ ++static void stm32_fmc2_setup(struct nand_chip *chip) ++{ ++ struct stm32_fmc2_nfc *fmc2 = to_stm32_nfc(chip->controller); ++ u32 pcr = readl_relaxed(fmc2->io_base + FMC2_PCR); ++ ++ /* Configure ecc algorithm (default configuration is Hamming) */ ++ pcr &= ~FMC2_PCR_ECCALG; ++ pcr &= ~FMC2_PCR_BCHECC; ++ if (chip->ecc.strength == FMC2_ECC_BCH8) { ++ pcr |= FMC2_PCR_ECCALG; ++ pcr |= FMC2_PCR_BCHECC; ++ } else if (chip->ecc.strength == FMC2_ECC_BCH4) { ++ pcr |= FMC2_PCR_ECCALG; ++ } ++ ++ /* Set buswidth */ ++ pcr &= ~FMC2_PCR_PWID_MASK; ++ if (chip->options & NAND_BUSWIDTH_16) ++ pcr |= FMC2_PCR_PWID(FMC2_PCR_PWID_BUSWIDTH_16); ++ ++ /* Set ecc sector size */ ++ pcr &= ~FMC2_PCR_ECCSS_MASK; ++ pcr |= FMC2_PCR_ECCSS(FMC2_PCR_ECCSS_512); ++ ++ writel_relaxed(pcr, fmc2->io_base + FMC2_PCR); ++} ++ ++/* Select function */ ++static void stm32_fmc2_select_chip(struct mtd_info *mtd, int chipnr) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct stm32_fmc2_nfc *fmc2 = to_stm32_nfc(chip->controller); ++ struct stm32_fmc2_nand *nand = to_fmc2_nand(chip); ++ struct dma_slave_config dma_cfg; ++ ++ if (chipnr < 0 || chipnr >= nand->ncs) ++ return; ++ ++ if (nand->cs_used[chipnr] == fmc2->cs_sel) ++ return; ++ ++ fmc2->cs_sel = nand->cs_used[chipnr]; ++ ++ /* FMC2 setup routine */ ++ stm32_fmc2_setup(chip); ++ ++ /* Apply timings */ ++ stm32_fmc2_timings_init(chip); ++ ++ if (fmc2->dma_tx_ch && fmc2->dma_rx_ch) { ++ memset(&dma_cfg, 0, sizeof(dma_cfg)); ++ dma_cfg.src_addr = fmc2->data_phys_addr[fmc2->cs_sel]; ++ dma_cfg.dst_addr = fmc2->data_phys_addr[fmc2->cs_sel]; ++ dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ dma_cfg.src_maxburst = 32; ++ dma_cfg.dst_maxburst = 32; ++ ++ if (dmaengine_slave_config(fmc2->dma_tx_ch, &dma_cfg)) ++ dev_warn(fmc2->dev, "tx DMA engine slave config failed\n"); ++ ++ if (dmaengine_slave_config(fmc2->dma_rx_ch, &dma_cfg)) ++ dev_warn(fmc2->dev, "rx DMA engine slave config failed\n"); ++ } ++ ++ if (fmc2->dma_ecc_ch) { ++ /* ++ * Hamming: we read HECCR register ++ * BCH4/BCH8: we read BCHDSRSx registers ++ */ ++ memset(&dma_cfg, 0, sizeof(dma_cfg)); ++ dma_cfg.src_addr = fmc2->io_phys_addr; ++ dma_cfg.src_addr += chip->ecc.strength == FMC2_ECC_HAM ? ++ FMC2_HECCR : FMC2_BCHDSR0; ++ dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; ++ ++ if (dmaengine_slave_config(fmc2->dma_ecc_ch, &dma_cfg)) ++ dev_warn(fmc2->dev, "ecc DMA engine slave config failed\n"); ++ ++ /* Calculate ecc length needed for one sector */ ++ fmc2->dma_ecc_len = chip->ecc.strength == FMC2_ECC_HAM ? ++ FMC2_HECCR_LEN : FMC2_BCHDSRS_LEN; ++ } ++} ++ ++/* Controller timings */ ++static void stm32_fmc2_calc_timings(struct nand_chip *chip, ++ const struct nand_sdr_timings *sdrt) ++{ ++ struct stm32_fmc2_nfc *fmc2 = to_stm32_nfc(chip->controller); ++ struct stm32_fmc2_nand *nand = to_fmc2_nand(chip); ++ struct stm32_fmc2_timings *tims = &nand->timings; ++ unsigned long hclk = clk_get_rate(fmc2->clk); ++ unsigned long hclkp = NSEC_PER_SEC / (hclk / 1000); ++ int tar, tclr, thiz, twait, tset_mem, tset_att, thold_mem, thold_att; ++ ++ tar = hclkp; ++ if (tar < sdrt->tAR_min) ++ tar = sdrt->tAR_min; ++ tims->tar = DIV_ROUND_UP(tar, hclkp) - 1; ++ if (tims->tar > FMC2_PCR_TIMING_MASK) ++ tims->tar = FMC2_PCR_TIMING_MASK; ++ ++ tclr = hclkp; ++ if (tclr < sdrt->tCLR_min) ++ tclr = sdrt->tCLR_min; ++ tims->tclr = DIV_ROUND_UP(tclr, hclkp) - 1; ++ if (tims->tclr > FMC2_PCR_TIMING_MASK) ++ tims->tclr = FMC2_PCR_TIMING_MASK; ++ ++ tims->thiz = FMC2_THIZ; ++ thiz = (tims->thiz + 1) * hclkp; ++ ++ /* ++ * tWAIT > tRP ++ * tWAIT > tWP ++ * tWAIT > tREA + tIO ++ */ ++ twait = hclkp; ++ if (twait < sdrt->tRP_min) ++ twait = sdrt->tRP_min; ++ if (twait < sdrt->tWP_min) ++ twait = sdrt->tWP_min; ++ if (twait < sdrt->tREA_max + FMC2_TIO) ++ twait = sdrt->tREA_max + FMC2_TIO; ++ tims->twait = DIV_ROUND_UP(twait, hclkp); ++ if (tims->twait == 0) ++ tims->twait = 1; ++ else if (tims->twait > FMC2_PMEM_PATT_TIMING_MASK) ++ tims->twait = FMC2_PMEM_PATT_TIMING_MASK; ++ ++ /* ++ * tSETUP_MEM > tCS - tWAIT ++ * tSETUP_MEM > tALS - tWAIT ++ * tSETUP_MEM > tDS - (tWAIT - tHIZ) ++ */ ++ tset_mem = hclkp; ++ if (sdrt->tCS_min > twait && (tset_mem < sdrt->tCS_min - twait)) ++ tset_mem = sdrt->tCS_min - twait; ++ if (sdrt->tALS_min > twait && (tset_mem < sdrt->tALS_min - twait)) ++ tset_mem = sdrt->tALS_min - twait; ++ if (twait > thiz && (sdrt->tDS_min > twait - thiz) && ++ (tset_mem < sdrt->tDS_min - (twait - thiz))) ++ tset_mem = sdrt->tDS_min - (twait - thiz); ++ tims->tset_mem = DIV_ROUND_UP(tset_mem, hclkp); ++ if (tims->tset_mem == 0) ++ tims->tset_mem = 1; ++ else if (tims->tset_mem > FMC2_PMEM_PATT_TIMING_MASK) ++ tims->tset_mem = FMC2_PMEM_PATT_TIMING_MASK; ++ ++ /* ++ * tHOLD_MEM > tCH ++ * tHOLD_MEM > tREH - tSETUP_MEM ++ * tHOLD_MEM > max(tRC, tWC) - (tSETUP_MEM + tWAIT) ++ */ ++ thold_mem = hclkp; ++ if (thold_mem < sdrt->tCH_min) ++ thold_mem = sdrt->tCH_min; ++ if (sdrt->tREH_min > tset_mem && ++ (thold_mem < sdrt->tREH_min - tset_mem)) ++ thold_mem = sdrt->tREH_min - tset_mem; ++ if ((sdrt->tRC_min > tset_mem + twait) && ++ (thold_mem < sdrt->tRC_min - (tset_mem + twait))) ++ thold_mem = sdrt->tRC_min - (tset_mem + twait); ++ if ((sdrt->tWC_min > tset_mem + twait) && ++ (thold_mem < sdrt->tWC_min - (tset_mem + twait))) ++ thold_mem = sdrt->tWC_min - (tset_mem + twait); ++ tims->thold_mem = DIV_ROUND_UP(thold_mem, hclkp); ++ if (tims->thold_mem == 0) ++ tims->thold_mem = 1; ++ else if (tims->thold_mem > FMC2_PMEM_PATT_TIMING_MASK) ++ tims->thold_mem = FMC2_PMEM_PATT_TIMING_MASK; ++ ++ /* ++ * tSETUP_ATT > tCS - tWAIT ++ * tSETUP_ATT > tCLS - tWAIT ++ * tSETUP_ATT > tALS - tWAIT ++ * tSETUP_ATT > tRHW - tHOLD_MEM ++ * tSETUP_ATT > tDS - (tWAIT - tHIZ) ++ */ ++ tset_att = hclkp; ++ if (sdrt->tCS_min > twait && (tset_att < sdrt->tCS_min - twait)) ++ tset_att = sdrt->tCS_min - twait; ++ if (sdrt->tCLS_min > twait && (tset_att < sdrt->tCLS_min - twait)) ++ tset_att = sdrt->tCLS_min - twait; ++ if (sdrt->tALS_min > twait && (tset_att < sdrt->tALS_min - twait)) ++ tset_att = sdrt->tALS_min - twait; ++ if (sdrt->tRHW_min > thold_mem && ++ (tset_att < sdrt->tRHW_min - thold_mem)) ++ tset_att = sdrt->tRHW_min - thold_mem; ++ if (twait > thiz && (sdrt->tDS_min > twait - thiz) && ++ (tset_att < sdrt->tDS_min - (twait - thiz))) ++ tset_att = sdrt->tDS_min - (twait - thiz); ++ tims->tset_att = DIV_ROUND_UP(tset_att, hclkp); ++ if (tims->tset_att == 0) ++ tims->tset_att = 1; ++ else if (tims->tset_att > FMC2_PMEM_PATT_TIMING_MASK) ++ tims->tset_att = FMC2_PMEM_PATT_TIMING_MASK; ++ ++ /* ++ * tHOLD_ATT > tALH ++ * tHOLD_ATT > tCH ++ * tHOLD_ATT > tCLH ++ * tHOLD_ATT > tCOH ++ * tHOLD_ATT > tDH ++ * tHOLD_ATT > tWB + tIO + tSYNC - tSETUP_MEM ++ * tHOLD_ATT > tADL - tSETUP_MEM ++ * tHOLD_ATT > tWH - tSETUP_MEM ++ * tHOLD_ATT > tWHR - tSETUP_MEM ++ * tHOLD_ATT > tRC - (tSETUP_ATT + tWAIT) ++ * tHOLD_ATT > tWC - (tSETUP_ATT + tWAIT) ++ */ ++ thold_att = hclkp; ++ if (thold_att < sdrt->tALH_min) ++ thold_att = sdrt->tALH_min; ++ if (thold_att < sdrt->tCH_min) ++ thold_att = sdrt->tCH_min; ++ if (thold_att < sdrt->tCLH_min) ++ thold_att = sdrt->tCLH_min; ++ if (thold_att < sdrt->tCOH_min) ++ thold_att = sdrt->tCOH_min; ++ if (thold_att < sdrt->tDH_min) ++ thold_att = sdrt->tDH_min; ++ if ((sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC > tset_mem) && ++ (thold_att < sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC - tset_mem)) ++ thold_att = sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC - tset_mem; ++ if (sdrt->tADL_min > tset_mem && ++ (thold_att < sdrt->tADL_min - tset_mem)) ++ thold_att = sdrt->tADL_min - tset_mem; ++ if (sdrt->tWH_min > tset_mem && ++ (thold_att < sdrt->tWH_min - tset_mem)) ++ thold_att = sdrt->tWH_min - tset_mem; ++ if (sdrt->tWHR_min > tset_mem && ++ (thold_att < sdrt->tWHR_min - tset_mem)) ++ thold_att = sdrt->tWHR_min - tset_mem; ++ if ((sdrt->tRC_min > tset_att + twait) && ++ (thold_att < sdrt->tRC_min - (tset_att + twait))) ++ thold_att = sdrt->tRC_min - (tset_att + twait); ++ if ((sdrt->tWC_min > tset_att + twait) && ++ (thold_att < sdrt->tWC_min - (tset_att + twait))) ++ thold_att = sdrt->tWC_min - (tset_att + twait); ++ tims->thold_att = DIV_ROUND_UP(thold_att, hclkp); ++ if (tims->thold_att == 0) ++ tims->thold_att = 1; ++ else if (tims->thold_att > FMC2_PMEM_PATT_TIMING_MASK) ++ tims->thold_att = FMC2_PMEM_PATT_TIMING_MASK; ++} ++ ++static int stm32_fmc2_setup_interface(struct mtd_info *mtd, int chipnr, ++ const struct nand_data_interface *conf) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ const struct nand_sdr_timings *sdrt; ++ ++ sdrt = nand_get_sdr_timings(conf); ++ if (IS_ERR(sdrt)) ++ return PTR_ERR(sdrt); ++ ++ if (chipnr == NAND_DATA_IFACE_CHECK_ONLY) ++ return 0; ++ ++ stm32_fmc2_calc_timings(chip, sdrt); ++ ++ /* Apply timings */ ++ stm32_fmc2_timings_init(chip); ++ ++ return 0; ++} ++ ++/* DMA configuration */ ++static int stm32_fmc2_dma_setup(struct stm32_fmc2_nfc *fmc2) ++{ ++ int ret; ++ ++ fmc2->dma_tx_ch = dma_request_slave_channel(fmc2->dev, "tx"); ++ fmc2->dma_rx_ch = dma_request_slave_channel(fmc2->dev, "rx"); ++ fmc2->dma_ecc_ch = dma_request_slave_channel(fmc2->dev, "ecc"); ++ ++ if (!fmc2->dma_tx_ch || !fmc2->dma_rx_ch || !fmc2->dma_ecc_ch) { ++ dev_warn(fmc2->dev, "DMAs not defined in the device tree, manual mode is used\n"); ++ return 0; ++ } ++ ++ ret = sg_alloc_table(&fmc2->dma_ecc_sg, FMC2_MAX_SG, GFP_KERNEL); ++ if (ret) ++ return ret; ++ ++ /* Allocate a buffer to store ecc status registers */ ++ fmc2->ecc_buf = devm_kzalloc(fmc2->dev, FMC2_MAX_ECC_BUF_LEN, ++ GFP_KERNEL); ++ if (!fmc2->ecc_buf) ++ return -ENOMEM; ++ ++ ret = sg_alloc_table(&fmc2->dma_data_sg, FMC2_MAX_SG, GFP_KERNEL); ++ if (ret) ++ return ret; ++ ++ init_completion(&fmc2->dma_data_complete); ++ init_completion(&fmc2->dma_ecc_complete); ++ ++ return 0; ++} ++ ++/* NAND callbacks setup */ ++static void stm32_fmc2_nand_callbacks_setup(struct nand_chip *chip) ++{ ++ struct stm32_fmc2_nfc *fmc2 = to_stm32_nfc(chip->controller); ++ ++ /* ++ * Specific callbacks to read/write a page depending on ++ * the mode (manual/sequencer) and the algo used (Hamming, BCH). ++ */ ++ if (fmc2->dma_tx_ch && fmc2->dma_rx_ch && fmc2->dma_ecc_ch) { ++ /* DMA => use sequencer mode callbacks */ ++ chip->ecc.correct = stm32_fmc2_sequencer_correct; ++ chip->ecc.write_page = stm32_fmc2_sequencer_write_page; ++ chip->ecc.read_page = stm32_fmc2_sequencer_read_page; ++ chip->ecc.write_page_raw = stm32_fmc2_sequencer_write_page_raw; ++ chip->ecc.read_page_raw = stm32_fmc2_sequencer_read_page_raw; ++ } else { ++ /* No DMA => use manual mode callbacks */ ++ chip->ecc.hwctl = stm32_fmc2_hwctl; ++ if (chip->ecc.strength == FMC2_ECC_HAM) { ++ /* Hamming is used */ ++ chip->ecc.calculate = stm32_fmc2_ham_calculate; ++ chip->ecc.correct = stm32_fmc2_ham_correct; ++ chip->ecc.options |= NAND_ECC_GENERIC_ERASED_CHECK; ++ } else { ++ /* BCH is used */ ++ chip->ecc.calculate = stm32_fmc2_bch_calculate; ++ chip->ecc.correct = stm32_fmc2_bch_correct; ++ chip->ecc.read_page = stm32_fmc2_read_page; ++ } ++ } ++ ++ /* Specific configurations depending on the algo used */ ++ if (chip->ecc.strength == FMC2_ECC_HAM) ++ chip->ecc.bytes = chip->options & NAND_BUSWIDTH_16 ? 4 : 3; ++ else if (chip->ecc.strength == FMC2_ECC_BCH8) ++ chip->ecc.bytes = chip->options & NAND_BUSWIDTH_16 ? 14 : 13; ++ else ++ chip->ecc.bytes = chip->options & NAND_BUSWIDTH_16 ? 8 : 7; ++} ++ ++/* FMC2 layout */ ++static int stm32_fmc2_nand_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->length = ecc->total; ++ oobregion->offset = FMC2_BBM_LEN; ++ ++ return 0; ++} ++ ++static int stm32_fmc2_nand_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *oobregion) ++{ ++ struct nand_chip *chip = mtd_to_nand(mtd); ++ struct nand_ecc_ctrl *ecc = &chip->ecc; ++ ++ if (section) ++ return -ERANGE; ++ ++ oobregion->length = mtd->oobsize - ecc->total - FMC2_BBM_LEN; ++ oobregion->offset = ecc->total + FMC2_BBM_LEN; ++ ++ return 0; ++} ++ ++const struct mtd_ooblayout_ops stm32_fmc2_nand_ooblayout_ops = { ++ .ecc = stm32_fmc2_nand_ooblayout_ecc, ++ .free = stm32_fmc2_nand_ooblayout_free, ++}; ++ ++/* FMC2 caps */ ++static int stm32_fmc2_calc_ecc_bytes(int step_size, int strength) ++{ ++ /* Hamming */ ++ if (strength == FMC2_ECC_HAM) ++ return 4; ++ ++ /* BCH8 */ ++ if (strength == FMC2_ECC_BCH8) ++ return 14; ++ ++ /* BCH4 */ ++ return 8; ++} ++ ++NAND_ECC_CAPS_SINGLE(stm32_fmc2_ecc_caps, stm32_fmc2_calc_ecc_bytes, ++ FMC2_ECC_STEP_SIZE, ++ FMC2_ECC_HAM, FMC2_ECC_BCH4, FMC2_ECC_BCH8); ++ ++/* FMC2 controller ops */ ++static int stm32_fmc2_attach_chip(struct nand_chip *chip) ++{ ++ struct stm32_fmc2_nfc *fmc2 = to_stm32_nfc(chip->controller); ++ struct mtd_info *mtd = nand_to_mtd(chip); ++ int ret; ++ ++ /* ++ * Only NAND_ECC_HW mode is actually supported ++ * Hamming => ecc.strength = 1 ++ * BCH4 => ecc.strength = 4 ++ * BCH8 => ecc.strength = 8 ++ * ecc sector size = 512 ++ */ ++ if (chip->ecc.mode != NAND_ECC_HW) { ++ dev_err(fmc2->dev, "nand_ecc_mode is not well defined in the DT\n"); ++ return -EINVAL; ++ } ++ ++ ret = nand_ecc_choose_conf(chip, &stm32_fmc2_ecc_caps, ++ mtd->oobsize - FMC2_BBM_LEN); ++ if (ret) { ++ dev_err(fmc2->dev, "no valid ECC settings set\n"); ++ return ret; ++ } ++ ++ if (mtd->writesize / chip->ecc.size > FMC2_MAX_SG) { ++ dev_err(fmc2->dev, "nand page size is not supported\n"); ++ return -EINVAL; ++ } ++ ++ if (chip->bbt_options & NAND_BBT_USE_FLASH) ++ chip->bbt_options |= NAND_BBT_NO_OOB; ++ ++ /* NAND callbacks setup */ ++ stm32_fmc2_nand_callbacks_setup(chip); ++ ++ /* Define ECC layout */ ++ mtd_set_ooblayout(mtd, &stm32_fmc2_nand_ooblayout_ops); ++ ++ /* Configure bus width to 16-bit */ ++ if (chip->options & NAND_BUSWIDTH_16) ++ stm32_fmc2_set_buswidth_16(fmc2, true); ++ ++ return 0; ++} ++ ++static const struct nand_controller_ops stm32_fmc2_nand_controller_ops = { ++ .attach_chip = stm32_fmc2_attach_chip, ++}; ++ ++/* FMC2 probe */ ++static int stm32_fmc2_parse_child(struct stm32_fmc2_nfc *fmc2, ++ struct device_node *dn) ++{ ++ struct stm32_fmc2_nand *nand = &fmc2->nand; ++ u32 cs; ++ int ret, i; ++ ++ if (!of_get_property(dn, "reg", &nand->ncs)) ++ return -EINVAL; ++ ++ nand->ncs /= sizeof(u32); ++ if (!nand->ncs) { ++ dev_err(fmc2->dev, "invalid reg property size\n"); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < nand->ncs; i++) { ++ ret = of_property_read_u32_index(dn, "reg", i, &cs); ++ if (ret) { ++ dev_err(fmc2->dev, "could not retrieve reg property: %d\n", ++ ret); ++ return ret; ++ } ++ ++ if (cs > FMC2_MAX_CE) { ++ dev_err(fmc2->dev, "invalid reg value: %d\n", cs); ++ return -EINVAL; ++ } ++ ++ if (fmc2->cs_assigned & BIT(cs)) { ++ dev_err(fmc2->dev, "cs already assigned: %d\n", cs); ++ return -EINVAL; ++ } ++ ++ fmc2->cs_assigned |= BIT(cs); ++ nand->cs_used[i] = cs; ++ } ++ ++ nand_set_flash_node(&nand->chip, dn); ++ ++ return 0; ++} ++ ++static int stm32_fmc2_parse_dt(struct stm32_fmc2_nfc *fmc2) ++{ ++ struct device_node *dn = fmc2->dev->of_node; ++ struct device_node *child; ++ int nchips = of_get_child_count(dn); ++ int ret = 0; ++ ++ if (!nchips) { ++ dev_err(fmc2->dev, "NAND chip not defined\n"); ++ return -EINVAL; ++ } ++ ++ if (nchips > 1) { ++ dev_err(fmc2->dev, "too many NAND chips defined\n"); ++ return -EINVAL; ++ } ++ ++ for_each_child_of_node(dn, child) { ++ ret = stm32_fmc2_parse_child(fmc2, child); ++ if (ret < 0) { ++ of_node_put(child); ++ return ret; ++ } ++ } ++ ++ return ret; ++} ++ ++static int stm32_fmc2_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct reset_control *rstc; ++ struct stm32_fmc2_nfc *fmc2; ++ struct stm32_fmc2_nand *nand; ++ struct resource *res; ++ struct mtd_info *mtd; ++ struct nand_chip *chip; ++ int chip_cs, mem_region, ret, irq; ++ ++ fmc2 = devm_kzalloc(dev, sizeof(*fmc2), GFP_KERNEL); ++ if (!fmc2) ++ return -ENOMEM; ++ ++ fmc2->dev = dev; ++ nand_controller_init(&fmc2->base); ++ fmc2->base.ops = &stm32_fmc2_nand_controller_ops; ++ ++ ret = stm32_fmc2_parse_dt(fmc2); ++ if (ret) ++ return ret; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ fmc2->io_base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(fmc2->io_base)) ++ return PTR_ERR(fmc2->io_base); ++ ++ fmc2->io_phys_addr = res->start; ++ ++ for (chip_cs = 0, mem_region = 1; chip_cs < FMC2_MAX_CE; ++ chip_cs++, mem_region += 3) { ++ if (!(fmc2->cs_assigned & BIT(chip_cs))) ++ continue; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, mem_region); ++ fmc2->data_base[chip_cs] = devm_ioremap_resource(dev, res); ++ if (IS_ERR(fmc2->data_base[chip_cs])) ++ return PTR_ERR(fmc2->data_base[chip_cs]); ++ ++ fmc2->data_phys_addr[chip_cs] = res->start; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, ++ mem_region + 1); ++ fmc2->cmd_base[chip_cs] = devm_ioremap_resource(dev, res); ++ if (IS_ERR(fmc2->cmd_base[chip_cs])) ++ return PTR_ERR(fmc2->cmd_base[chip_cs]); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, ++ mem_region + 2); ++ fmc2->addr_base[chip_cs] = devm_ioremap_resource(dev, res); ++ if (IS_ERR(fmc2->addr_base[chip_cs])) ++ return PTR_ERR(fmc2->addr_base[chip_cs]); ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ ret = devm_request_irq(dev, irq, stm32_fmc2_irq, 0, ++ dev_name(dev), fmc2); ++ if (ret) { ++ dev_err(dev, "failed to request irq\n"); ++ return ret; ++ } ++ ++ init_completion(&fmc2->complete); ++ ++ fmc2->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(fmc2->clk)) ++ return PTR_ERR(fmc2->clk); ++ ++ ret = clk_prepare_enable(fmc2->clk); ++ if (ret) { ++ dev_err(dev, "can not enable the clock\n"); ++ return ret; ++ } ++ ++ rstc = devm_reset_control_get(dev, NULL); ++ if (!IS_ERR(rstc)) { ++ reset_control_assert(rstc); ++ reset_control_deassert(rstc); ++ } ++ ++ /* DMA setup */ ++ ret = stm32_fmc2_dma_setup(fmc2); ++ if (ret) ++ return ret; ++ ++ /* FMC2 init routine */ ++ stm32_fmc2_init(fmc2); ++ ++ nand = &fmc2->nand; ++ chip = &nand->chip; ++ mtd = nand_to_mtd(chip); ++ mtd->dev.parent = dev; ++ ++ chip->controller = &fmc2->base; ++ chip->exec_op = stm32_fmc2_exec_op; ++ chip->select_chip = stm32_fmc2_select_chip; ++ chip->setup_data_interface = stm32_fmc2_setup_interface; ++ chip->options |= NAND_BUSWIDTH_AUTO | NAND_NO_SUBPAGE_WRITE | ++ NAND_USE_BOUNCE_BUFFER; ++ ++ /* Default settings */ ++ chip->ecc.mode = NAND_ECC_HW; ++ chip->ecc.size = FMC2_ECC_STEP_SIZE; ++ chip->ecc.strength = FMC2_ECC_BCH8; ++ ++ /* Scan to find existence of the device */ ++ ret = nand_scan(mtd, nand->ncs); ++ if (ret) ++ goto err_scan; ++ ++ ret = mtd_device_register(mtd, NULL, 0); ++ if (ret) ++ goto err_device_register; ++ ++ platform_set_drvdata(pdev, fmc2); ++ ++ return 0; ++ ++err_device_register: ++ nand_cleanup(chip); ++ ++err_scan: ++ if (fmc2->dma_ecc_ch) ++ dma_release_channel(fmc2->dma_ecc_ch); ++ if (fmc2->dma_tx_ch) ++ dma_release_channel(fmc2->dma_tx_ch); ++ if (fmc2->dma_rx_ch) ++ dma_release_channel(fmc2->dma_rx_ch); ++ ++ sg_free_table(&fmc2->dma_data_sg); ++ sg_free_table(&fmc2->dma_ecc_sg); ++ ++ clk_disable_unprepare(fmc2->clk); ++ ++ return ret; ++} ++ ++static int stm32_fmc2_remove(struct platform_device *pdev) ++{ ++ struct stm32_fmc2_nfc *fmc2 = platform_get_drvdata(pdev); ++ struct stm32_fmc2_nand *nand = &fmc2->nand; ++ ++ nand_release(nand_to_mtd(&nand->chip)); ++ ++ if (fmc2->dma_ecc_ch) ++ dma_release_channel(fmc2->dma_ecc_ch); ++ if (fmc2->dma_tx_ch) ++ dma_release_channel(fmc2->dma_tx_ch); ++ if (fmc2->dma_rx_ch) ++ dma_release_channel(fmc2->dma_rx_ch); ++ ++ sg_free_table(&fmc2->dma_data_sg); ++ sg_free_table(&fmc2->dma_ecc_sg); ++ ++ clk_disable_unprepare(fmc2->clk); ++ ++ return 0; ++} ++ ++static int __maybe_unused stm32_fmc2_suspend(struct device *dev) ++{ ++ struct stm32_fmc2_nfc *fmc2 = dev_get_drvdata(dev); ++ ++ clk_disable_unprepare(fmc2->clk); ++ ++ pinctrl_pm_select_sleep_state(dev); ++ ++ return 0; ++} ++ ++static int __maybe_unused stm32_fmc2_resume(struct device *dev) ++{ ++ struct stm32_fmc2_nfc *fmc2 = dev_get_drvdata(dev); ++ struct stm32_fmc2_nand *nand = &fmc2->nand; ++ int chip_cs, ret; ++ ++ pinctrl_pm_select_default_state(dev); ++ ++ ret = clk_prepare_enable(fmc2->clk); ++ if (ret) { ++ dev_err(dev, "can not enable the clock\n"); ++ return ret; ++ } ++ ++ stm32_fmc2_init(fmc2); ++ ++ for (chip_cs = 0; chip_cs < FMC2_MAX_CE; chip_cs++) { ++ if (!(fmc2->cs_assigned & BIT(chip_cs))) ++ continue; ++ ++ nand_reset(&nand->chip, chip_cs); ++ } ++ ++ return 0; ++} ++ ++static SIMPLE_DEV_PM_OPS(stm32_fmc2_pm_ops, stm32_fmc2_suspend, ++ stm32_fmc2_resume); ++ ++static const struct of_device_id stm32_fmc2_match[] = { ++ {.compatible = "st,stm32mp15-fmc2"}, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, stm32_fmc2_match); ++ ++static struct platform_driver stm32_fmc2_driver = { ++ .probe = stm32_fmc2_probe, ++ .remove = stm32_fmc2_remove, ++ .driver = { ++ .name = "stm32_fmc2_nand", ++ .of_match_table = stm32_fmc2_match, ++ .pm = &stm32_fmc2_pm_ops, ++ }, ++}; ++module_platform_driver(stm32_fmc2_driver); ++ ++MODULE_ALIAS("platform:stm32_fmc2_nand"); ++MODULE_AUTHOR("Christophe Kerello "); ++MODULE_DESCRIPTION("STMicroelectronics STM32 FMC2 nand driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/include/linux/amba/mmci.h b/include/linux/amba/mmci.h +index da8357b..c92ebc3 100644 +--- a/include/linux/amba/mmci.h ++++ b/include/linux/amba/mmci.h +@@ -18,20 +18,13 @@ + * mask into a value to be binary (or set some other custom bits + * in MMCIPWR) or:ed and written into the MMCIPWR register of the + * block. May also control external power based on the power_mode. +- * @status: if no GPIO read function was given to the block in +- * gpio_wp (below) this function will be called to determine +- * whether a card is present in the MMC slot or not +- * @gpio_wp: read this GPIO pin to see if the card is write protected +- * @gpio_cd: read this GPIO pin to detect card insertion +- * @cd_invert: true if the gpio_cd pin value is active low ++ * @status: if no GPIO line was given to the block in this function will ++ * be called to determine whether a card is present in the MMC slot or not + */ + struct mmci_platform_data { + unsigned int ocr_mask; + int (*ios_handler)(struct device *, struct mmc_ios *); + unsigned int (*status)(struct device *); +- int gpio_wp; +- int gpio_cd; +- bool cd_invert; + }; + + #endif +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0010-ARM-stm32mp1-r0-rc1-ETH.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0010-ARM-stm32mp1-r0-rc1-ETH.patch new file mode 100644 index 0000000..e8c9b06 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0010-ARM-stm32mp1-r0-rc1-ETH.patch @@ -0,0 +1,78 @@ +From 82c6107d054adcc906e43190ef150840876c2618 Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Tue, 13 Nov 2018 12:23:50 +0100 +Subject: [PATCH 10/52] ARM: stm32mp1-r0-rc1: ETH + +--- + drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c | 30 ++++++++++++++++++++++- + 1 file changed, 29 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c +index 7e2e79d..d1cf145 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c +@@ -42,6 +42,7 @@ struct stm32_dwmac { + struct clk *clk_ethstp; + struct clk *syscfg_clk; + bool int_phyclk; /* Clock from RCC to drive PHY */ ++ int irq_pwr_wakeup; + u32 mode_reg; /* MAC glue-logic mode register */ + struct regmap *regmap; + u32 speed; +@@ -232,7 +233,9 @@ static int stm32_dwmac_parse_data(struct stm32_dwmac *dwmac, + static int stm32mp1_parse_data(struct stm32_dwmac *dwmac, + struct device *dev) + { ++ struct platform_device *pdev = to_platform_device(dev); + struct device_node *np = dev->of_node; ++ int err = 0; + + dwmac->int_phyclk = of_property_read_bool(np, "st,int-phyclk"); + +@@ -260,7 +263,26 @@ static int stm32mp1_parse_data(struct stm32_dwmac *dwmac, + return PTR_ERR(dwmac->syscfg_clk); + } + +- return 0; ++ /* Get IRQ information early to have an ability to ask for deferred ++ * probe if needed before we went too far with resource allocation. ++ */ ++ dwmac->irq_pwr_wakeup = platform_get_irq_byname(pdev, ++ "stm32_pwr_wakeup"); ++ if (!dwmac->int_phyclk && dwmac->irq_pwr_wakeup >= 0) { ++ err = device_init_wakeup(&pdev->dev, true); ++ if (err) { ++ dev_err(&pdev->dev, "Failed to init wake up irq\n"); ++ return err; ++ } ++ err = dev_pm_set_dedicated_wake_irq(&pdev->dev, ++ dwmac->irq_pwr_wakeup); ++ if (err) { ++ dev_err(&pdev->dev, "Failed to set wake up irq\n"); ++ device_init_wakeup(&pdev->dev, false); ++ } ++ device_set_wakeup_enable(&pdev->dev, false); ++ } ++ return err; + } + + static int stm32_dwmac_probe(struct platform_device *pdev) +@@ -326,9 +348,15 @@ static int stm32_dwmac_remove(struct platform_device *pdev) + struct net_device *ndev = platform_get_drvdata(pdev); + struct stmmac_priv *priv = netdev_priv(ndev); + int ret = stmmac_dvr_remove(&pdev->dev); ++ struct stm32_dwmac *dwmac = priv->plat->bsp_priv; + + stm32_dwmac_clk_disable(priv->plat->bsp_priv); + ++ if (dwmac->irq_pwr_wakeup >= 0) { ++ dev_pm_clear_wake_irq(&pdev->dev); ++ device_init_wakeup(&pdev->dev, false); ++ } ++ + return ret; + } + +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0011-ARM-stm32mp1-r0-rc1-NVMEM.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0011-ARM-stm32mp1-r0-rc1-NVMEM.patch new file mode 100644 index 0000000..2e2cad7 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0011-ARM-stm32mp1-r0-rc1-NVMEM.patch @@ -0,0 +1,335 @@ +From a38a0eadf1db60bd8d1ff084c2ddc8016432b4fb Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Tue, 13 Nov 2018 12:25:05 +0100 +Subject: [PATCH 11/52] ARM: stm32mp1-r0-rc1: NVMEM + +--- + drivers/nvmem/Kconfig | 10 ++ + drivers/nvmem/Makefile | 2 + + drivers/nvmem/core.c | 37 ++++++++ + drivers/nvmem/stm32-romem.c | 205 +++++++++++++++++++++++++++++++++++++++++ + include/linux/nvmem-consumer.h | 7 ++ + 5 files changed, 261 insertions(+) + create mode 100644 drivers/nvmem/stm32-romem.c + +diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig +index 0a7a470e..f398b18 100644 +--- a/drivers/nvmem/Kconfig ++++ b/drivers/nvmem/Kconfig +@@ -113,6 +113,16 @@ config NVMEM_BCM_OCOTP + This driver can also be built as a module. If so, the module + will be called nvmem-bcm-ocotp. + ++config NVMEM_STM32_ROMEM ++ tristate "STMicroelectronics STM32 factory-programmed memory support" ++ depends on ARCH_STM32 || COMPILE_TEST ++ help ++ Say y here to enable read-only access for STMicroelectronics STM32 ++ factory-programmed memory area. ++ ++ This driver can also be built as a module. If so, the module ++ will be called nvmem-stm32-romem. ++ + config NVMEM_SUNXI_SID + tristate "Allwinner SoCs SID support" + depends on ARCH_SUNXI +diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile +index 4e8c616..e85c946 100644 +--- a/drivers/nvmem/Makefile ++++ b/drivers/nvmem/Makefile +@@ -26,6 +26,8 @@ nvmem_qfprom-y := qfprom.o + obj-$(CONFIG_ROCKCHIP_EFUSE) += nvmem_rockchip_efuse.o + nvmem_rockchip_efuse-y := rockchip-efuse.o + obj-$(CONFIG_NVMEM_SUNXI_SID) += nvmem_sunxi_sid.o ++nvmem_stm32_romem-y := stm32-romem.o ++obj-$(CONFIG_NVMEM_STM32_ROMEM) += nvmem_stm32_romem.o + nvmem_sunxi_sid-y := sunxi_sid.o + obj-$(CONFIG_UNIPHIER_EFUSE) += nvmem-uniphier-efuse.o + nvmem-uniphier-efuse-y := uniphier-efuse.o +diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c +index 7c530c8..60dacd7 100644 +--- a/drivers/nvmem/core.c ++++ b/drivers/nvmem/core.c +@@ -1234,6 +1234,43 @@ int nvmem_cell_read_u32(struct device *dev, const char *cell_id, u32 *val) + EXPORT_SYMBOL_GPL(nvmem_cell_read_u32); + + /** ++ * nvmem_cell_read_u16() - Read a cell value as an u16 ++ * ++ * @dev: Device that requests the nvmem cell. ++ * @cell_id: Name of nvmem cell to read. ++ * @val: pointer to output value. ++ * ++ * Return: 0 on success or negative errno. ++ */ ++int nvmem_cell_read_u16(struct device *dev, const char *cell_id, u16 *val) ++{ ++ struct nvmem_cell *cell; ++ void *buf; ++ size_t len; ++ ++ cell = nvmem_cell_get(dev, cell_id); ++ if (IS_ERR(cell)) ++ return PTR_ERR(cell); ++ ++ buf = nvmem_cell_read(cell, &len); ++ if (IS_ERR(buf)) { ++ nvmem_cell_put(cell); ++ return PTR_ERR(buf); ++ } ++ if (len != sizeof(*val)) { ++ kfree(buf); ++ nvmem_cell_put(cell); ++ return -EINVAL; ++ } ++ memcpy(val, buf, sizeof(*val)); ++ kfree(buf); ++ nvmem_cell_put(cell); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(nvmem_cell_read_u16); ++ ++/** + * nvmem_device_cell_read() - Read a given nvmem device and cell + * + * @nvmem: nvmem device to read from. +diff --git a/drivers/nvmem/stm32-romem.c b/drivers/nvmem/stm32-romem.c +new file mode 100644 +index 0000000..198872f +--- /dev/null ++++ b/drivers/nvmem/stm32-romem.c +@@ -0,0 +1,205 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * STM32 Factory-programmed memory read access driver ++ * ++ * Copyright (C) 2017, STMicroelectronics - All Rights Reserved ++ * Author: Fabrice Gasnier for STMicroelectronics. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++/* BSEC secure service access from non-secure */ ++#define STM32_SMC_BSEC 0x82001003 ++#define STM32_SMC_READ_SHADOW 0x01 ++#define STM32_SMC_PROG_OTP 0x02 ++#define STM32_SMC_WRITE_SHADOW 0x03 ++#define STM32_SMC_READ_OTP 0x04 ++ ++struct stm32_romem_cfg { ++ int size; ++}; ++ ++struct stm32_romem_priv { ++ void __iomem *base; ++ struct nvmem_config cfg; ++ struct device *dev; ++}; ++ ++static int stm32_romem_read(void *context, unsigned int offset, void *buf, ++ size_t bytes) ++{ ++ struct stm32_romem_priv *priv = context; ++ u8 *buf8 = buf; ++ int i; ++ ++ for (i = offset; i < offset + bytes; i++) ++ *buf8++ = readb_relaxed(priv->base + i); ++ ++ return 0; ++} ++ ++static int stm32_bsec_smc(u8 op, u32 otp, u32 data, u32 *result) ++{ ++#if IS_ENABLED(CONFIG_HAVE_ARM_SMCCC) ++ struct arm_smccc_res res; ++ ++ arm_smccc_smc(STM32_SMC_BSEC, op, otp, data, 0, 0, 0, 0, &res); ++ if (res.a0) ++ return -EIO; ++ ++ if (result) ++ *result = (u32)res.a1; ++ ++ return 0; ++#else ++ return -ENXIO; ++#endif ++} ++ ++static int stm32_bsec_read(void *context, unsigned int offset, void *buf, ++ size_t bytes) ++{ ++ struct stm32_romem_priv *priv = context; ++ u32 roffset, rbytes, val; ++ u8 *buf8 = buf, *val8 = (u8 *)&val; ++ int i, j = 0, ret, skip_bytes, size; ++ ++ /* Round unaligned access to 32-bits */ ++ roffset = rounddown(offset, 4); ++ skip_bytes = offset & 0x3; ++ rbytes = roundup(bytes + skip_bytes, 4); ++ ++ if (roffset + rbytes > priv->cfg.size) ++ return -EINVAL; ++ ++ for (i = roffset; (i < roffset + rbytes); i += 4) { ++ ret = stm32_bsec_smc(STM32_SMC_READ_OTP, i >> 2, 0, &val); ++ if (ret) { ++ dev_err(priv->dev, "Failed to read data%d (%d)\n", ++ i >> 2, ret); ++ return ret; ++ } ++ ++ /* skip first bytes in case of unaligned read */ ++ if (skip_bytes) ++ size = min(bytes, (size_t)(4 - skip_bytes)); ++ else ++ size = min(bytes, (size_t)4); ++ memcpy(&buf8[j], &val8[skip_bytes], size); ++ bytes -= size; ++ j += size; ++ skip_bytes = 0; ++ } ++ ++ return 0; ++} ++ ++static int stm32_bsec_write(void *context, unsigned int offset, void *buf, ++ size_t bytes) ++{ ++ struct stm32_romem_priv *priv = context; ++ u32 *buf32 = buf; ++ int ret, i; ++ ++ /* Allow only writing complete 32-bits aligned words */ ++ if ((bytes % 4) || (offset % 4)) ++ return -EINVAL; ++ ++ for (i = offset; i < offset + bytes; i += 4) { ++ ret = stm32_bsec_smc(STM32_SMC_PROG_OTP, i >> 2, *buf32++, ++ NULL); ++ if (ret) { ++ dev_err(priv->dev, "Failed to write data%d (%d)\n", ++ i >> 2, ret); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static int stm32_romem_probe(struct platform_device *pdev) ++{ ++ const struct stm32_romem_cfg *cfg; ++ struct device *dev = &pdev->dev; ++ struct stm32_romem_priv *priv; ++ struct nvmem_device *nvmem; ++ struct resource *res; ++ ++ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ priv->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(priv->base)) ++ return PTR_ERR(priv->base); ++ ++ priv->dev = dev; ++ priv->cfg.name = "stm32-romem"; ++ priv->cfg.word_size = 1; ++ priv->cfg.stride = 1; ++ priv->cfg.dev = &pdev->dev; ++ priv->cfg.priv = priv; ++ priv->cfg.owner = THIS_MODULE; ++ ++ cfg = (const struct stm32_romem_cfg *) ++ of_match_device(dev->driver->of_match_table, dev)->data; ++ if (!cfg) { ++ priv->cfg.read_only = true; ++ priv->cfg.size = resource_size(res); ++ priv->cfg.reg_read = stm32_romem_read; ++ } else { ++ priv->cfg.read_only = false; ++ priv->cfg.size = cfg->size; ++ priv->cfg.reg_read = stm32_bsec_read; ++ priv->cfg.reg_write = stm32_bsec_write; ++ } ++ ++ nvmem = nvmem_register(&priv->cfg); ++ if (IS_ERR(nvmem)) ++ return PTR_ERR(nvmem); ++ ++ platform_set_drvdata(pdev, nvmem); ++ ++ return 0; ++} ++ ++static int stm32_romem_remove(struct platform_device *pdev) ++{ ++ struct nvmem_device *nvmem = platform_get_drvdata(pdev); ++ ++ return nvmem_unregister(nvmem); ++} ++ ++static const struct stm32_romem_cfg stm32mp15_bsec_cfg = { ++ .size = 384, /* 96 x 32-bits data words */ ++}; ++ ++static const struct of_device_id stm32_romem_of_match[] = { ++ { .compatible = "st,stm32-romem", }, { ++ .compatible = "st,stm32mp15-bsec", ++ .data = (void *)&stm32mp15_bsec_cfg, ++ }, { ++ }, ++}; ++MODULE_DEVICE_TABLE(of, stm32_romem_of_match); ++ ++static struct platform_driver stm32_romem_driver = { ++ .probe = stm32_romem_probe, ++ .remove = stm32_romem_remove, ++ .driver = { ++ .name = "stm32-romem", ++ .of_match_table = of_match_ptr(stm32_romem_of_match), ++ }, ++}; ++module_platform_driver(stm32_romem_driver); ++ ++MODULE_AUTHOR("Fabrice Gasnier "); ++MODULE_DESCRIPTION("STMicroelectronics STM32 RO-MEM"); ++MODULE_ALIAS("platform:nvmem-stm32-romem"); ++MODULE_LICENSE("GPL v2"); +diff --git a/include/linux/nvmem-consumer.h b/include/linux/nvmem-consumer.h +index 4e85447..f303008 100644 +--- a/include/linux/nvmem-consumer.h ++++ b/include/linux/nvmem-consumer.h +@@ -39,6 +39,7 @@ void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell); + void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len); + int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len); + int nvmem_cell_read_u32(struct device *dev, const char *cell_id, u32 *val); ++int nvmem_cell_read_u16(struct device *dev, const char *cell_id, u16 *val); + + /* direct nvmem device read/write interface */ + struct nvmem_device *nvmem_device_get(struct device *dev, const char *name); +@@ -95,6 +96,12 @@ static inline int nvmem_cell_read_u32(struct device *dev, + return -ENOSYS; + } + ++static inline int nvmem_cell_read_u16(struct device *dev, ++ const char *cell_id, u16 *val) ++{ ++ return -ENOSYS; ++} ++ + static inline struct nvmem_device *nvmem_device_get(struct device *dev, + const char *name) + { +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0012-ARM-stm32mp1-r0-rc1-PINCTRL-PWM-RESET-RTC.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0012-ARM-stm32mp1-r0-rc1-PINCTRL-PWM-RESET-RTC.patch new file mode 100644 index 0000000..a2031ca --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0012-ARM-stm32mp1-r0-rc1-PINCTRL-PWM-RESET-RTC.patch @@ -0,0 +1,5965 @@ +From c617af0e078307484930384232ec1cd8d33eed2c Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Tue, 13 Nov 2018 12:26:32 +0100 +Subject: [PATCH 12/52] ARM: stm32mp1-r0-rc1: PINCTRL PWM RESET RTC + +--- + drivers/pinctrl/Kconfig | 12 + + drivers/pinctrl/Makefile | 1 + + drivers/pinctrl/pinctrl-stmfx.c | 821 ++++++++++++++++++++ + drivers/pinctrl/stm32/pinctrl-stm32.c | 203 ++++- + drivers/pinctrl/stm32/pinctrl-stm32.h | 52 +- + drivers/pinctrl/stm32/pinctrl-stm32mp157.c | 1095 ++++++++++++++++----------- + drivers/pwm/pwm-stm32-lp.c | 44 ++ + drivers/pwm/pwm-stm32.c | 103 ++- + drivers/pwm/sysfs.c | 12 +- + drivers/regulator/Kconfig | 19 + + drivers/regulator/Makefile | 2 + + drivers/regulator/stm32-pwr.c | 242 ++++++ + drivers/regulator/stm32-vrefbuf.c | 123 ++- + drivers/regulator/stpmic1_regulator.c | 674 +++++++++++++++++ + drivers/reset/reset-stm32mp1.c | 48 ++ + drivers/rtc/Kconfig | 1 + + drivers/rtc/rtc-stm32.c | 119 +++ + include/dt-bindings/pinctrl/stm32-pinfunc.h | 6 + + include/dt-bindings/rtc/rtc-stm32.h | 13 + + 19 files changed, 3084 insertions(+), 506 deletions(-) + create mode 100644 drivers/pinctrl/pinctrl-stmfx.c + create mode 100644 drivers/regulator/stm32-pwr.c + create mode 100644 drivers/regulator/stpmic1_regulator.c + create mode 100644 include/dt-bindings/rtc/rtc-stm32.h + +diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig +index e86752b..bbd3908 100644 +--- a/drivers/pinctrl/Kconfig ++++ b/drivers/pinctrl/Kconfig +@@ -244,6 +244,18 @@ config PINCTRL_ST + select PINCONF + select GPIOLIB_IRQCHIP + ++config PINCTRL_STMFX ++ tristate "STMicroelectronics STMFX GPIO expander pinctrl driver" ++ select GENERIC_PINCONF ++ select GPIOLIB_IRQCHIP ++ select MFD_STMFX ++ help ++ Driver for STMicroelectronics Multi-Function eXpander (STMFX) ++ GPIO expander. ++ This provides a GPIO interface supporting inputs and outputs, ++ and configuring push-pull, open-drain, and can also be used as ++ interrupt-controller. ++ + config PINCTRL_U300 + bool "U300 pin controller driver" + depends on ARCH_U300 +diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile +index 46ef9bd..9abcaa59 100644 +--- a/drivers/pinctrl/Makefile ++++ b/drivers/pinctrl/Makefile +@@ -38,6 +38,7 @@ obj-$(CONFIG_PINCTRL_LANTIQ) += pinctrl-lantiq.o + obj-$(CONFIG_PINCTRL_LPC18XX) += pinctrl-lpc18xx.o + obj-$(CONFIG_PINCTRL_TB10X) += pinctrl-tb10x.o + obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o ++obj-$(CONFIG_PINCTRL_STMFX) += pinctrl-stmfx.o + obj-$(CONFIG_PINCTRL_ZYNQ) += pinctrl-zynq.o + obj-$(CONFIG_PINCTRL_INGENIC) += pinctrl-ingenic.o + obj-$(CONFIG_PINCTRL_RK805) += pinctrl-rk805.o +diff --git a/drivers/pinctrl/pinctrl-stmfx.c b/drivers/pinctrl/pinctrl-stmfx.c +new file mode 100644 +index 0000000..e253ed1 +--- /dev/null ++++ b/drivers/pinctrl/pinctrl-stmfx.c +@@ -0,0 +1,821 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Driver for STMicroelectronics Multi-Function eXpander (STMFX) GPIO expander ++ * ++ * Copyright (C) 2018 STMicroelectronics ++ * Author(s): Amelie Delaunay . ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "core.h" ++#include "pinctrl-utils.h" ++ ++/* GPIOs expander */ ++/* GPIO_STATE1 0x10, GPIO_STATE2 0x11, GPIO_STATE3 0x12 */ ++#define STMFX_REG_GPIO_STATE 0x10 /* R */ ++/* GPIO_DIR1 0x60, GPIO_DIR2 0x61, GPIO_DIR3 0x63 */ ++#define STMFX_REG_GPIO_DIR 0x60 /* RW */ ++/* GPIO_TYPE1 0x64, GPIO_TYPE2 0x65, GPIO_TYPE3 0x66 */ ++#define STMFX_REG_GPIO_TYPE 0x64 /* RW */ ++/* GPIO_PUPD1 0x68, GPIO_PUPD2 0x69, GPIO_PUPD3 0x6A */ ++#define STMFX_REG_GPIO_PUPD 0x68 /* RW */ ++/* GPO_SET1 0x6C, GPO_SET2 0x6D, GPO_SET3 0x6E */ ++#define STMFX_REG_GPO_SET 0x6C /* RW */ ++/* GPO_CLR1 0x70, GPO_CLR2 0x71, GPO_CLR3 0x72 */ ++#define STMFX_REG_GPO_CLR 0x70 /* RW */ ++/* IRQ_GPI_SRC1 0x48, IRQ_GPI_SRC2 0x49, IRQ_GPI_SRC3 0x4A */ ++#define STMFX_REG_IRQ_GPI_SRC 0x48 /* RW */ ++/* IRQ_GPI_EVT1 0x4C, IRQ_GPI_EVT2 0x4D, IRQ_GPI_EVT3 0x4E */ ++#define STMFX_REG_IRQ_GPI_EVT 0x4C /* RW */ ++/* IRQ_GPI_TYPE1 0x50, IRQ_GPI_TYPE2 0x51, IRQ_GPI_TYPE3 0x52 */ ++#define STMFX_REG_IRQ_GPI_TYPE 0x50 /* RW */ ++/* IRQ_GPI_PENDING1 0x0C, IRQ_GPI_PENDING2 0x0D, IRQ_GPI_PENDING3 0x0E*/ ++#define STMFX_REG_IRQ_GPI_PENDING 0x0C /* R */ ++/* IRQ_GPI_ACK1 0x54, IRQ_GPI_ACK2 0x55, IRQ_GPI_ACK3 0x56 */ ++#define STMFX_REG_IRQ_GPI_ACK 0x54 /* RW */ ++ ++/* STMFX_REG_IRQ_PENDING bitfields */ ++#define STMFX_REG_IRQ_PENDING_GPIO BIT(0) ++ ++#define NR_GPIO_REGS 3 ++#define NR_GPIOS_PER_REG 8 ++#define get_reg(offset) ((offset) / NR_GPIOS_PER_REG) ++#define get_shift(offset) ((offset) % NR_GPIOS_PER_REG) ++#define get_mask(offset) (BIT(get_shift(offset))) ++ ++/* ++ * STMFX pinctrl can have up to 24 pins if STMFX other functions are not used. ++ * Pins availability is managed thanks to gpio-ranges property. ++ */ ++static const struct pinctrl_pin_desc stmfx_pins[] = { ++ PINCTRL_PIN(0, "gpio0"), ++ PINCTRL_PIN(1, "gpio1"), ++ PINCTRL_PIN(2, "gpio2"), ++ PINCTRL_PIN(3, "gpio3"), ++ PINCTRL_PIN(4, "gpio4"), ++ PINCTRL_PIN(5, "gpio5"), ++ PINCTRL_PIN(6, "gpio6"), ++ PINCTRL_PIN(7, "gpio7"), ++ PINCTRL_PIN(8, "gpio8"), ++ PINCTRL_PIN(9, "gpio9"), ++ PINCTRL_PIN(10, "gpio10"), ++ PINCTRL_PIN(11, "gpio11"), ++ PINCTRL_PIN(12, "gpio12"), ++ PINCTRL_PIN(13, "gpio13"), ++ PINCTRL_PIN(14, "gpio14"), ++ PINCTRL_PIN(15, "gpio15"), ++ PINCTRL_PIN(16, "agpio0"), ++ PINCTRL_PIN(17, "agpio1"), ++ PINCTRL_PIN(18, "agpio2"), ++ PINCTRL_PIN(19, "agpio3"), ++ PINCTRL_PIN(20, "agpio4"), ++ PINCTRL_PIN(21, "agpio5"), ++ PINCTRL_PIN(22, "agpio6"), ++ PINCTRL_PIN(23, "agpio7"), ++}; ++ ++struct stmfx_pinctrl { ++ struct device *dev; ++ struct stmfx *stmfx; ++ struct pinctrl_dev *pctl_dev; ++ struct pinctrl_desc pctl_desc; ++ struct gpio_chip gpio_chip; ++ struct irq_chip irq_chip; ++ struct mutex lock; /* IRQ bus lock */ ++ unsigned long gpio_valid_mask; ++ /* Cache of IRQ_GPI_* registers for bus_lock */ ++ u8 irq_gpi_src[NR_GPIO_REGS]; ++ u8 irq_gpi_type[NR_GPIO_REGS]; ++ u8 irq_gpi_evt[NR_GPIO_REGS]; ++ u8 irq_toggle_edge[NR_GPIO_REGS]; ++#ifdef CONFIG_PM ++ /* Backup of GPIO_* registers for suspend/resume */ ++ u8 bkp_gpio_state[NR_GPIO_REGS]; ++ u8 bkp_gpio_dir[NR_GPIO_REGS]; ++ u8 bkp_gpio_type[NR_GPIO_REGS]; ++ u8 bkp_gpio_pupd[NR_GPIO_REGS]; ++#endif ++}; ++ ++static int stmfx_gpio_get(struct gpio_chip *gc, unsigned int offset) ++{ ++ struct stmfx_pinctrl *pctl = gpiochip_get_data(gc); ++ u32 reg = STMFX_REG_GPIO_STATE + get_reg(offset); ++ u32 mask = get_mask(offset); ++ u32 value; ++ int ret; ++ ++ ret = regmap_read(pctl->stmfx->map, reg, &value); ++ ++ return ret ? ret : !!(value & mask); ++} ++ ++static void stmfx_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) ++{ ++ struct stmfx_pinctrl *pctl = gpiochip_get_data(gc); ++ u32 reg = value ? STMFX_REG_GPO_SET : STMFX_REG_GPO_CLR; ++ u32 mask = get_mask(offset); ++ ++ regmap_write_bits(pctl->stmfx->map, reg + get_reg(offset), ++ mask, mask); ++} ++ ++static int stmfx_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) ++{ ++ struct stmfx_pinctrl *pctl = gpiochip_get_data(gc); ++ u32 reg = STMFX_REG_GPIO_DIR + get_reg(offset); ++ u32 mask = get_mask(offset); ++ u32 val; ++ int ret; ++ ++ ret = regmap_read(pctl->stmfx->map, reg, &val); ++ /* ++ * On stmfx, gpio pins direction is (0)input, (1)output. ++ * .get_direction returns 0=out, 1=in ++ */ ++ ++ return ret ? ret : !(val & mask); ++} ++ ++static int stmfx_gpio_direction_input(struct gpio_chip *gc, unsigned int offset) ++{ ++ struct stmfx_pinctrl *pctl = gpiochip_get_data(gc); ++ u32 reg = STMFX_REG_GPIO_DIR + get_reg(offset); ++ u32 mask = get_mask(offset); ++ ++ return regmap_write_bits(pctl->stmfx->map, reg, mask, 0); ++} ++ ++static int stmfx_gpio_direction_output(struct gpio_chip *gc, ++ unsigned int offset, int value) ++{ ++ struct stmfx_pinctrl *pctl = gpiochip_get_data(gc); ++ u32 reg = STMFX_REG_GPIO_DIR + get_reg(offset); ++ u32 mask = get_mask(offset); ++ ++ stmfx_gpio_set(gc, offset, value); ++ ++ return regmap_write_bits(pctl->stmfx->map, reg, mask, mask); ++} ++ ++static int stmfx_pinconf_get_pupd(struct stmfx_pinctrl *pctl, ++ unsigned int offset) ++{ ++ u32 reg = STMFX_REG_GPIO_PUPD + get_reg(offset); ++ u32 pupd, mask = get_mask(offset); ++ int ret; ++ ++ ret = regmap_read(pctl->stmfx->map, reg, &pupd); ++ if (ret) ++ return ret; ++ ++ return !!(pupd & mask); ++} ++ ++static int stmfx_pinconf_set_pupd(struct stmfx_pinctrl *pctl, ++ unsigned int offset, u32 pupd) ++{ ++ u32 reg = STMFX_REG_GPIO_PUPD + get_reg(offset); ++ u32 mask = get_mask(offset); ++ ++ return regmap_write_bits(pctl->stmfx->map, reg, mask, pupd ? mask : 0); ++} ++ ++static int stmfx_pinconf_get_type(struct stmfx_pinctrl *pctl, ++ unsigned int offset) ++{ ++ u32 reg = STMFX_REG_GPIO_TYPE + get_reg(offset); ++ u32 type, mask = get_mask(offset); ++ int ret; ++ ++ ret = regmap_read(pctl->stmfx->map, reg, &type); ++ if (ret) ++ return ret; ++ ++ return !!(type & mask); ++} ++ ++static int stmfx_pinconf_set_type(struct stmfx_pinctrl *pctl, ++ unsigned int offset, u32 type) ++{ ++ u32 reg = STMFX_REG_GPIO_TYPE + get_reg(offset); ++ u32 mask = get_mask(offset); ++ ++ return regmap_write_bits(pctl->stmfx->map, reg, mask, type ? mask : 0); ++} ++ ++static int stmfx_pinconf_get(struct pinctrl_dev *pctldev, ++ unsigned int pin, unsigned long *config) ++{ ++ struct stmfx_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); ++ u32 param = pinconf_to_config_param(*config); ++ struct pinctrl_gpio_range *range; ++ u32 dir, type, pupd; ++ u32 arg = 0; ++ int ret; ++ ++ range = pinctrl_find_gpio_range_from_pin_nolock(pctldev, pin); ++ if (!range) ++ return -EINVAL; ++ ++ dir = stmfx_gpio_get_direction(&pctl->gpio_chip, pin); ++ if (dir < 0) ++ return dir; ++ type = stmfx_pinconf_get_type(pctl, pin); ++ if (type < 0) ++ return type; ++ pupd = stmfx_pinconf_get_pupd(pctl, pin); ++ if (pupd < 0) ++ return pupd; ++ ++ switch (param) { ++ case PIN_CONFIG_BIAS_DISABLE: ++ if ((!dir && (!type || !pupd)) || (dir && !type)) ++ arg = 1; ++ break; ++ case PIN_CONFIG_BIAS_PULL_DOWN: ++ if (dir && type && !pupd) ++ arg = 1; ++ break; ++ case PIN_CONFIG_BIAS_PULL_UP: ++ if (type && pupd) ++ arg = 1; ++ break; ++ case PIN_CONFIG_DRIVE_OPEN_DRAIN: ++ if ((!dir && type) || (dir && !type)) ++ arg = 1; ++ break; ++ case PIN_CONFIG_DRIVE_PUSH_PULL: ++ if ((!dir && !type) || (dir && type)) ++ arg = 1; ++ break; ++ case PIN_CONFIG_OUTPUT: ++ if (dir) ++ return -EINVAL; ++ ++ ret = stmfx_gpio_get(&pctl->gpio_chip, pin); ++ if (ret < 0) ++ return ret; ++ ++ arg = ret; ++ break; ++ default: ++ return -ENOTSUPP; ++ } ++ ++ *config = pinconf_to_config_packed(param, arg); ++ ++ return 0; ++} ++ ++static int stmfx_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, ++ unsigned long *configs, unsigned int num_configs) ++{ ++ struct stmfx_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); ++ struct pinctrl_gpio_range *range; ++ enum pin_config_param param; ++ u32 arg; ++ int dir, i, ret; ++ ++ range = pinctrl_find_gpio_range_from_pin_nolock(pctldev, pin); ++ if (!range) { ++ dev_err(pctldev->dev, "pin %d is not available\n", pin); ++ return -EINVAL; ++ } ++ ++ dir = stmfx_gpio_get_direction(&pctl->gpio_chip, pin); ++ if (dir < 0) ++ return dir; ++ ++ for (i = 0; i < num_configs; i++) { ++ param = pinconf_to_config_param(configs[i]); ++ arg = pinconf_to_config_argument(configs[i]); ++ ++ switch (param) { ++ case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: ++ case PIN_CONFIG_BIAS_DISABLE: ++ case PIN_CONFIG_BIAS_PULL_DOWN: ++ ret = stmfx_pinconf_set_pupd(pctl, pin, 0); ++ if (ret) ++ return ret; ++ break; ++ case PIN_CONFIG_BIAS_PULL_UP: ++ ret = stmfx_pinconf_set_pupd(pctl, pin, 1); ++ if (ret) ++ return ret; ++ break; ++ case PIN_CONFIG_DRIVE_OPEN_DRAIN: ++ if (!dir) ++ ret = stmfx_pinconf_set_type(pctl, pin, 1); ++ else ++ ret = stmfx_pinconf_set_type(pctl, pin, 0); ++ if (ret) ++ return ret; ++ break; ++ case PIN_CONFIG_DRIVE_PUSH_PULL: ++ if (!dir) ++ ret = stmfx_pinconf_set_type(pctl, pin, 0); ++ else ++ ret = stmfx_pinconf_set_type(pctl, pin, 1); ++ if (ret) ++ return ret; ++ break; ++ case PIN_CONFIG_OUTPUT: ++ ret = stmfx_gpio_direction_output(&pctl->gpio_chip, ++ pin, arg); ++ if (ret) ++ return ret; ++ break; ++ default: ++ return -ENOTSUPP; ++ } ++ } ++ ++ return 0; ++} ++ ++static void stmfx_pinconf_dbg_show(struct pinctrl_dev *pctldev, ++ struct seq_file *s, unsigned int offset) ++{ ++ struct stmfx_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); ++ struct pinctrl_gpio_range *range; ++ int dir, type, pupd, val; ++ ++ range = pinctrl_find_gpio_range_from_pin_nolock(pctldev, offset); ++ if (!range) ++ return; ++ ++ dir = stmfx_gpio_get_direction(&pctl->gpio_chip, offset); ++ if (dir < 0) ++ return; ++ type = stmfx_pinconf_get_type(pctl, offset); ++ if (type < 0) ++ return; ++ pupd = stmfx_pinconf_get_pupd(pctl, offset); ++ if (pupd < 0) ++ return; ++ val = stmfx_gpio_get(&pctl->gpio_chip, offset); ++ if (val < 0) ++ return; ++ ++ if (!dir) { ++ seq_printf(s, "output %s ", val ? "high" : "low"); ++ if (type) ++ seq_printf(s, "open drain %s internal pull-up ", ++ pupd ? "with" : "without"); ++ else ++ seq_puts(s, "push pull no pull "); ++ } else { ++ seq_printf(s, "input %s ", val ? "high" : "low"); ++ if (type) ++ seq_printf(s, "with internal pull-%s ", ++ pupd ? "up" : "down"); ++ else ++ seq_printf(s, "%s ", pupd ? "floating" : "analog"); ++ } ++} ++ ++static const struct pinconf_ops stmfx_pinconf_ops = { ++ .pin_config_get = stmfx_pinconf_get, ++ .pin_config_set = stmfx_pinconf_set, ++ .pin_config_dbg_show = stmfx_pinconf_dbg_show, ++}; ++ ++static int stmfx_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) ++{ ++ return 0; ++} ++ ++static const char *stmfx_pinctrl_get_group_name(struct pinctrl_dev *pctldev, ++ unsigned int selector) ++{ ++ return NULL; ++} ++ ++static int stmfx_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, ++ unsigned int selector, ++ const unsigned int **pins, ++ unsigned int *num_pins) ++{ ++ return -ENOTSUPP; ++} ++ ++static const struct pinctrl_ops stmfx_pinctrl_ops = { ++ .get_groups_count = stmfx_pinctrl_get_groups_count, ++ .get_group_name = stmfx_pinctrl_get_group_name, ++ .get_group_pins = stmfx_pinctrl_get_group_pins, ++ .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, ++ .dt_free_map = pinctrl_utils_free_map, ++}; ++ ++static void stmfx_pinctrl_irq_mask(struct irq_data *data) ++{ ++ struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data); ++ struct stmfx_pinctrl *pctl = gpiochip_get_data(gpio_chip); ++ u32 reg = get_reg(data->hwirq); ++ u32 mask = get_mask(data->hwirq); ++ ++ pctl->irq_gpi_src[reg] &= ~mask; ++} ++ ++static void stmfx_pinctrl_irq_unmask(struct irq_data *data) ++{ ++ struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data); ++ struct stmfx_pinctrl *pctl = gpiochip_get_data(gpio_chip); ++ u32 reg = get_reg(data->hwirq); ++ u32 mask = get_mask(data->hwirq); ++ ++ pctl->irq_gpi_src[reg] |= mask; ++} ++ ++static int stmfx_pinctrl_irq_set_type(struct irq_data *data, unsigned int type) ++{ ++ struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data); ++ struct stmfx_pinctrl *pctl = gpiochip_get_data(gpio_chip); ++ u32 reg = get_reg(data->hwirq); ++ u32 mask = get_mask(data->hwirq); ++ ++ if (type & IRQ_TYPE_NONE) ++ return -EINVAL; ++ ++ if (type & IRQ_TYPE_EDGE_BOTH) { ++ pctl->irq_gpi_evt[reg] |= mask; ++ irq_set_handler_locked(data, handle_edge_irq); ++ } else { ++ pctl->irq_gpi_evt[reg] &= ~mask; ++ irq_set_handler_locked(data, handle_level_irq); ++ } ++ ++ if ((type & IRQ_TYPE_EDGE_RISING) || (type & IRQ_TYPE_LEVEL_HIGH)) ++ pctl->irq_gpi_type[reg] |= mask; ++ else ++ pctl->irq_gpi_type[reg] &= ~mask; ++ ++ /* ++ * In case of (type & IRQ_TYPE_EDGE_BOTH), we need to know current ++ * GPIO value to set the right edge trigger. But in atomic context ++ * here we can't access registers over I2C. That's why (type & ++ * IRQ_TYPE_EDGE_BOTH) will be managed in .irq_sync_unlock. ++ */ ++ ++ if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) ++ pctl->irq_toggle_edge[reg] |= mask; ++ else ++ pctl->irq_toggle_edge[reg] &= mask; ++ ++ return 0; ++} ++ ++static void stmfx_pinctrl_irq_bus_lock(struct irq_data *data) ++{ ++ struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data); ++ struct stmfx_pinctrl *pctl = gpiochip_get_data(gpio_chip); ++ ++ mutex_lock(&pctl->lock); ++} ++ ++static void stmfx_pinctrl_irq_bus_sync_unlock(struct irq_data *data) ++{ ++ struct gpio_chip *gpio_chip = irq_data_get_irq_chip_data(data); ++ struct stmfx_pinctrl *pctl = gpiochip_get_data(gpio_chip); ++ u32 reg = get_reg(data->hwirq); ++ u32 mask = get_mask(data->hwirq); ++ ++ /* ++ * In case of IRQ_TYPE_EDGE_BOTH), read the current GPIO value ++ * (this couldn't be done in .irq_set_type because of atomic context) ++ * to set the right irq trigger type. ++ */ ++ if (pctl->irq_toggle_edge[reg] & mask) { ++ if (stmfx_gpio_get(gpio_chip, data->hwirq)) ++ pctl->irq_gpi_type[reg] &= ~mask; ++ else ++ pctl->irq_gpi_type[reg] |= mask; ++ } ++ ++ regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_EVT, ++ pctl->irq_gpi_evt, NR_GPIO_REGS); ++ regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_TYPE, ++ pctl->irq_gpi_type, NR_GPIO_REGS); ++ regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_SRC, ++ pctl->irq_gpi_src, NR_GPIO_REGS); ++ ++ mutex_unlock(&pctl->lock); ++} ++ ++static void stmfx_pinctrl_irq_toggle_trigger(struct stmfx_pinctrl *pctl, ++ unsigned int offset) ++{ ++ u32 reg = get_reg(offset); ++ u32 mask = get_mask(offset); ++ int val; ++ ++ if (!(pctl->irq_toggle_edge[reg] & mask)) ++ return; ++ ++ val = stmfx_gpio_get(&pctl->gpio_chip, offset); ++ if (val < 0) ++ return; ++ ++ if (val) { ++ pctl->irq_gpi_type[reg] &= mask; ++ regmap_write_bits(pctl->stmfx->map, ++ STMFX_REG_IRQ_GPI_TYPE + reg, ++ mask, 0); ++ ++ } else { ++ pctl->irq_gpi_type[reg] |= mask; ++ regmap_write_bits(pctl->stmfx->map, ++ STMFX_REG_IRQ_GPI_TYPE + reg, ++ mask, mask); ++ } ++} ++ ++static irqreturn_t stmfx_pinctrl_irq_thread_fn(int irq, void *dev_id) ++{ ++ struct stmfx_pinctrl *pctl = (struct stmfx_pinctrl *)dev_id; ++ struct gpio_chip *gc = &pctl->gpio_chip; ++ u8 pending[NR_GPIO_REGS]; ++ unsigned long n, status; ++ int ret; ++ ++ ret = regmap_bulk_read(pctl->stmfx->map, STMFX_REG_IRQ_GPI_PENDING, ++ &pending, NR_GPIO_REGS); ++ if (ret) ++ return IRQ_NONE; ++ ++ ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_ACK, ++ pending, NR_GPIO_REGS); ++ if (ret) ++ return IRQ_NONE; ++ ++ status = *(unsigned long *)pending; ++ for_each_set_bit(n, &status, gc->ngpio) { ++ handle_nested_irq(irq_find_mapping(gc->irq.domain, n)); ++ stmfx_pinctrl_irq_toggle_trigger(pctl, n); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static int stmfx_pinctrl_gpio_function_enable(struct stmfx_pinctrl *pctl) ++{ ++ struct pinctrl_gpio_range *gpio_range; ++ struct pinctrl_dev *pctl_dev = pctl->pctl_dev; ++ u32 func = STMFX_FUNC_GPIO; ++ ++ pctl->gpio_valid_mask = GENMASK(15, 0); ++ ++ gpio_range = pinctrl_find_gpio_range_from_pin(pctl_dev, 16); ++ if (gpio_range) { ++ func |= STMFX_FUNC_ALTGPIO_LOW; ++ pctl->gpio_valid_mask |= GENMASK(19, 16); ++ } ++ ++ gpio_range = pinctrl_find_gpio_range_from_pin(pctl_dev, 20); ++ if (gpio_range) { ++ func |= STMFX_FUNC_ALTGPIO_HIGH; ++ pctl->gpio_valid_mask |= GENMASK(23, 20); ++ } ++ ++ return stmfx_function_enable(pctl->stmfx, func); ++} ++ ++static int stmfx_pinctrl_probe(struct platform_device *pdev) ++{ ++ struct stmfx *stmfx = dev_get_platdata(&pdev->dev); ++ struct device_node *np = pdev->dev.of_node; ++ struct stmfx_pinctrl *pctl; ++ u32 n; ++ int irq, ret; ++ ++ pctl = devm_kzalloc(stmfx->dev, sizeof(*pctl), GFP_KERNEL); ++ if (!pctl) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, pctl); ++ ++ pctl->dev = &pdev->dev; ++ pctl->stmfx = stmfx; ++ ++ if (!of_find_property(np, "gpio-ranges", NULL)) { ++ dev_err(pctl->dev, "missing required gpio-ranges property\n"); ++ return -EINVAL; ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq <= 0) { ++ dev_err(pctl->dev, "failed to get irq\n"); ++ return -ENXIO; ++ } ++ ++ mutex_init(&pctl->lock); ++ ++ /* Register pin controller */ ++ pctl->pctl_desc.name = "stmfx-pinctrl"; ++ pctl->pctl_desc.pctlops = &stmfx_pinctrl_ops; ++ pctl->pctl_desc.confops = &stmfx_pinconf_ops; ++ pctl->pctl_desc.pins = stmfx_pins; ++ pctl->pctl_desc.npins = ARRAY_SIZE(stmfx_pins); ++ pctl->pctl_desc.owner = THIS_MODULE; ++ ++ ret = devm_pinctrl_register_and_init(pctl->dev, &pctl->pctl_desc, ++ pctl, &pctl->pctl_dev); ++ if (ret) { ++ dev_err(pctl->dev, "pinctrl registration failed\n"); ++ return ret; ++ } ++ ++ ret = pinctrl_enable(pctl->pctl_dev); ++ if (ret) { ++ dev_err(pctl->dev, "pinctrl enable failed\n"); ++ return ret; ++ } ++ ++ /* Register gpio controller */ ++ pctl->gpio_chip.label = "stmfx-gpio"; ++ pctl->gpio_chip.parent = pctl->dev; ++ pctl->gpio_chip.get_direction = stmfx_gpio_get_direction; ++ pctl->gpio_chip.direction_input = stmfx_gpio_direction_input; ++ pctl->gpio_chip.direction_output = stmfx_gpio_direction_output; ++ pctl->gpio_chip.get = stmfx_gpio_get; ++ pctl->gpio_chip.set = stmfx_gpio_set; ++ pctl->gpio_chip.set_config = gpiochip_generic_config; ++ pctl->gpio_chip.base = -1; ++ pctl->gpio_chip.ngpio = pctl->pctl_desc.npins; ++ pctl->gpio_chip.can_sleep = true; ++ pctl->gpio_chip.of_node = np; ++ pctl->gpio_chip.need_valid_mask = true; ++ ++ ret = devm_gpiochip_add_data(pctl->dev, &pctl->gpio_chip, pctl); ++ if (ret) { ++ dev_err(pctl->dev, "gpio_chip registration failed\n"); ++ return ret; ++ } ++ ++ ret = stmfx_pinctrl_gpio_function_enable(pctl); ++ if (ret) ++ return ret; ++ ++ pctl->irq_chip.name = dev_name(pctl->dev); ++ pctl->irq_chip.irq_mask = stmfx_pinctrl_irq_mask; ++ pctl->irq_chip.irq_unmask = stmfx_pinctrl_irq_unmask; ++ pctl->irq_chip.irq_set_type = stmfx_pinctrl_irq_set_type; ++ pctl->irq_chip.irq_bus_lock = stmfx_pinctrl_irq_bus_lock; ++ pctl->irq_chip.irq_bus_sync_unlock = stmfx_pinctrl_irq_bus_sync_unlock; ++ for_each_clear_bit(n, &pctl->gpio_valid_mask, pctl->gpio_chip.ngpio) ++ clear_bit(n, pctl->gpio_chip.valid_mask); ++ ++ ret = gpiochip_irqchip_add_nested(&pctl->gpio_chip, &pctl->irq_chip, ++ 0, handle_bad_irq, IRQ_TYPE_NONE); ++ if (ret) { ++ dev_err(pctl->dev, "cannot add irqchip to gpiochip\n"); ++ return ret; ++ } ++ ++ ret = devm_request_threaded_irq(pctl->dev, irq, NULL, ++ stmfx_pinctrl_irq_thread_fn, ++ IRQF_ONESHOT, ++ pctl->irq_chip.name, pctl); ++ if (ret) { ++ dev_err(pctl->dev, "cannot request irq%d\n", irq); ++ return ret; ++ } ++ ++ gpiochip_set_nested_irqchip(&pctl->gpio_chip, &pctl->irq_chip, irq); ++ ++ dev_info(pctl->dev, ++ "%ld GPIOs available\n", hweight_long(pctl->gpio_valid_mask)); ++ ++ return 0; ++} ++ ++static int stmfx_pinctrl_remove(struct platform_device *pdev) ++{ ++ struct stmfx *stmfx = dev_get_platdata(&pdev->dev); ++ ++ return stmfx_function_disable(stmfx, ++ STMFX_FUNC_GPIO | ++ STMFX_FUNC_ALTGPIO_LOW | ++ STMFX_FUNC_ALTGPIO_HIGH); ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int stmfx_pinctrl_backup_regs(struct stmfx_pinctrl *pctl) ++{ ++ int ret; ++ ++ ret = regmap_bulk_read(pctl->stmfx->map, STMFX_REG_GPIO_STATE, ++ &pctl->bkp_gpio_state, NR_GPIO_REGS); ++ if (ret) ++ return ret; ++ ret = regmap_bulk_read(pctl->stmfx->map, STMFX_REG_GPIO_DIR, ++ &pctl->bkp_gpio_dir, NR_GPIO_REGS); ++ if (ret) ++ return ret; ++ ret = regmap_bulk_read(pctl->stmfx->map, STMFX_REG_GPIO_TYPE, ++ &pctl->bkp_gpio_type, NR_GPIO_REGS); ++ if (ret) ++ return ret; ++ ret = regmap_bulk_read(pctl->stmfx->map, STMFX_REG_GPIO_PUPD, ++ &pctl->bkp_gpio_pupd, NR_GPIO_REGS); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int stmfx_pinctrl_restore_regs(struct stmfx_pinctrl *pctl) ++{ ++ int ret; ++ ++ ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_GPIO_DIR, ++ pctl->bkp_gpio_dir, NR_GPIO_REGS); ++ if (ret) ++ return ret; ++ ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_GPIO_TYPE, ++ pctl->bkp_gpio_type, NR_GPIO_REGS); ++ if (ret) ++ return ret; ++ ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_GPIO_PUPD, ++ pctl->bkp_gpio_pupd, NR_GPIO_REGS); ++ if (ret) ++ return ret; ++ ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_GPO_SET, ++ pctl->bkp_gpio_state, NR_GPIO_REGS); ++ if (ret) ++ return ret; ++ ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_EVT, ++ pctl->irq_gpi_evt, NR_GPIO_REGS); ++ if (ret) ++ return ret; ++ ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_TYPE, ++ pctl->irq_gpi_type, NR_GPIO_REGS); ++ if (ret) ++ return ret; ++ ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_SRC, ++ pctl->irq_gpi_src, NR_GPIO_REGS); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static int stmfx_pinctrl_suspend(struct device *dev) ++{ ++ struct stmfx_pinctrl *pctl = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = stmfx_pinctrl_backup_regs(pctl); ++ if (ret) { ++ dev_err(pctl->dev, "registers backup failure\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int stmfx_pinctrl_resume(struct device *dev) ++{ ++ struct stmfx_pinctrl *pctl = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = stmfx_pinctrl_restore_regs(pctl); ++ if (ret) { ++ dev_err(pctl->dev, "registers restoration failure\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++#endif ++ ++static SIMPLE_DEV_PM_OPS(stmfx_pinctrl_dev_pm_ops, ++ stmfx_pinctrl_suspend, stmfx_pinctrl_resume); ++ ++static const struct of_device_id stmfx_pinctrl_of_match[] = { ++ { .compatible = "st,stmfx-0300-pinctrl", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, stmfx_pinctrl_of_match); ++ ++static struct platform_driver stmfx_pinctrl_driver = { ++ .driver = { ++ .name = "stmfx-pinctrl", ++ .of_match_table = stmfx_pinctrl_of_match, ++ .pm = &stmfx_pinctrl_dev_pm_ops, ++ }, ++ .probe = stmfx_pinctrl_probe, ++ .remove = stmfx_pinctrl_remove, ++}; ++module_platform_driver(stmfx_pinctrl_driver); ++ ++MODULE_DESCRIPTION("STMFX pinctrl/GPIO driver"); ++MODULE_AUTHOR("Amelie Delaunay "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/pinctrl/stm32/pinctrl-stm32.c b/drivers/pinctrl/stm32/pinctrl-stm32.c +index a9bec6e..e25917f 100644 +--- a/drivers/pinctrl/stm32/pinctrl-stm32.c ++++ b/drivers/pinctrl/stm32/pinctrl-stm32.c +@@ -31,6 +31,7 @@ + #include "../pinconf.h" + #include "../pinctrl-utils.h" + #include "pinctrl-stm32.h" ++#include "../pinmux.h" + + #define STM32_GPIO_MODER 0x00 + #define STM32_GPIO_TYPER 0x04 +@@ -43,6 +44,18 @@ + #define STM32_GPIO_AFRL 0x20 + #define STM32_GPIO_AFRH 0x24 + ++/* custom bitfield to backup pin status */ ++#define STM32_GPIO_BKP_MODE_SHIFT 0 ++#define STM32_GPIO_BKP_MODE_MASK GENMASK(1, 0) ++#define STM32_GPIO_BKP_ALT_SHIFT 2 ++#define STM32_GPIO_BKP_ALT_MASK GENMASK(5, 2) ++#define STM32_GPIO_BKP_SPEED_SHIFT 6 ++#define STM32_GPIO_BKP_SPEED_MASK GENMASK(7, 6) ++#define STM32_GPIO_BKP_PUPD_SHIFT 8 ++#define STM32_GPIO_BKP_PUPD_MASK GENMASK(9, 8) ++#define STM32_GPIO_BKP_TYPE 10 ++#define STM32_GPIO_BKP_VAL 11 ++ + #define STM32_GPIO_PINS_PER_BANK 16 + #define STM32_GPIO_IRQ_LINE 16 + +@@ -76,6 +89,9 @@ struct stm32_gpio_bank { + struct irq_domain *domain; + u32 bank_nr; + u32 bank_ioport_nr; ++#ifdef CONFIG_PM ++ u32 pin_backup[STM32_GPIO_PINS_PER_BANK]; ++#endif + }; + + struct stm32_pinctrl { +@@ -91,6 +107,10 @@ struct stm32_pinctrl { + struct irq_domain *domain; + struct regmap *regmap; + struct regmap_field *irqmux[STM32_GPIO_PINS_PER_BANK]; ++ struct stm32_desc_pin *pins; ++ u32 npins; ++ u32 pkg; ++ u32 pin_base_shift; + }; + + static inline int stm32_gpio_pin(int gpio) +@@ -131,6 +151,8 @@ static inline u32 stm32_gpio_get_alt(u32 function) + static inline void __stm32_gpio_set(struct stm32_gpio_bank *bank, + unsigned offset, int value) + { ++ stm32_gpio_backup_value(bank, offset, value); ++ + if (!value) + offset += STM32_GPIO_PINS_PER_BANK; + +@@ -352,16 +374,16 @@ stm32_pctrl_find_group_by_pin(struct stm32_pinctrl *pctl, u32 pin) + static bool stm32_pctrl_is_function_valid(struct stm32_pinctrl *pctl, + u32 pin_num, u32 fnum) + { +- int i; ++ int i, k; + +- for (i = 0; i < pctl->match_data->npins; i++) { +- const struct stm32_desc_pin *pin = pctl->match_data->pins + i; ++ for (i = 0; i < pctl->npins; i++) { ++ const struct stm32_desc_pin *pin = pctl->pins + i; + const struct stm32_desc_function *func = pin->functions; + + if (pin->pin.number != pin_num) + continue; + +- while (func && func->name) { ++ for (k = 0; k < STM32_CONFIG_NUM; k++) { + if (func->num == fnum) + return true; + func++; +@@ -594,6 +616,8 @@ static void stm32_pmx_set_mode(struct stm32_gpio_bank *bank, + val |= mode << (pin * 2); + writel_relaxed(val, bank->base + STM32_GPIO_MODER); + ++ stm32_gpio_backup_mode(bank, pin, mode, alt); ++ + spin_unlock_irqrestore(&bank->lock, flags); + clk_disable(bank->clk); + } +@@ -694,6 +718,8 @@ static void stm32_pconf_set_driving(struct stm32_gpio_bank *bank, + val |= drive << offset; + writel_relaxed(val, bank->base + STM32_GPIO_TYPER); + ++ stm32_gpio_backup_driving(bank, offset, drive); ++ + spin_unlock_irqrestore(&bank->lock, flags); + clk_disable(bank->clk); + } +@@ -730,6 +756,8 @@ static void stm32_pconf_set_speed(struct stm32_gpio_bank *bank, + val |= speed << (offset * 2); + writel_relaxed(val, bank->base + STM32_GPIO_SPEEDR); + ++ stm32_gpio_backup_speed(bank, offset, speed); ++ + spin_unlock_irqrestore(&bank->lock, flags); + clk_disable(bank->clk); + } +@@ -766,6 +794,8 @@ static void stm32_pconf_set_bias(struct stm32_gpio_bank *bank, + val |= bias << (offset * 2); + writel_relaxed(val, bank->base + STM32_GPIO_PUPDR); + ++ stm32_gpio_backup_bias(bank, offset, bias); ++ + spin_unlock_irqrestore(&bank->lock, flags); + clk_disable(bank->clk); + } +@@ -893,6 +923,8 @@ static void stm32_pconf_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned int pin) + { ++ struct stm32_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); ++ const struct stm32_desc_pin *pin_desc; + struct pinctrl_gpio_range *range; + struct stm32_gpio_bank *bank; + int offset; +@@ -942,7 +974,9 @@ static void stm32_pconf_dbg_show(struct pinctrl_dev *pctldev, + case 2: + drive = stm32_pconf_get_driving(bank, offset); + speed = stm32_pconf_get_speed(bank, offset); +- seq_printf(s, "%d - %s - %s - %s %s", alt, ++ pin_desc = pctl->pins + (pin - pctl->pin_base_shift); ++ seq_printf(s, "%d (%s) - %s - %s - %s %s", alt, ++ pin_desc->functions[alt + 1].name, + drive ? "open drain" : "push pull", + biasing[bias], + speeds[speed], "speed"); +@@ -1105,7 +1139,7 @@ static int stm32_pctrl_build_state(struct platform_device *pdev) + struct stm32_pinctrl *pctl = platform_get_drvdata(pdev); + int i; + +- pctl->ngroups = pctl->match_data->npins; ++ pctl->ngroups = pctl->npins; + + /* Allocate groups */ + pctl->groups = devm_kcalloc(&pdev->dev, pctl->ngroups, +@@ -1119,19 +1153,51 @@ static int stm32_pctrl_build_state(struct platform_device *pdev) + if (!pctl->grp_names) + return -ENOMEM; + +- for (i = 0; i < pctl->match_data->npins; i++) { +- const struct stm32_desc_pin *pin = pctl->match_data->pins + i; ++ for (i = 0; i < pctl->npins; i++) { ++ const struct stm32_desc_pin *pin = pctl->pins + i; + struct stm32_pinctrl_group *group = pctl->groups + i; + + group->name = pin->pin.name; + group->pin = pin->pin.number; +- + pctl->grp_names[i] = pin->pin.name; + } + + return 0; + } + ++static int stm32_pctrl_create_pins_tab(struct stm32_pinctrl *pctl, ++ struct stm32_desc_pin *pins) ++{ ++ const struct stm32_desc_pin *p; ++ int i, nb_pins_available = 0; ++ ++ for (i = 0; i < pctl->match_data->npins; i++) { ++ p = pctl->match_data->pins + i; ++ if (pctl->pkg && !(pctl->pkg & p->pkg)) ++ continue; ++ pins->pin = p->pin; ++ memcpy((struct stm32_desc_pin *)pins->functions, p->functions, ++ STM32_CONFIG_NUM * sizeof(struct stm32_desc_function)); ++ pins++; ++ nb_pins_available++; ++ } ++ ++ pctl->npins = nb_pins_available; ++ ++ return 0; ++} ++ ++static void stm32_pctl_get_package(struct device_node *np, ++ struct stm32_pinctrl *pctl) ++{ ++ if (of_property_read_u32(np, "st,package", &pctl->pkg)) { ++ pctl->pkg = 0; ++ dev_warn(pctl->dev, "No package detected, use default one\n"); ++ } else { ++ dev_dbg(pctl->dev, "package detected: %x\n", pctl->pkg); ++ } ++} ++ + int stm32_pctl_probe(struct platform_device *pdev) + { + struct device_node *np = pdev->dev.of_node; +@@ -1162,6 +1228,19 @@ int stm32_pctl_probe(struct platform_device *pdev) + + pctl->dev = dev; + pctl->match_data = match->data; ++ ++ /* get package information */ ++ stm32_pctl_get_package(np, pctl); ++ ++ pctl->pins = devm_kcalloc(pctl->dev, pctl->match_data->npins, ++ sizeof(*pctl->pins), GFP_KERNEL); ++ if (!pctl->pins) ++ return -ENOMEM; ++ ++ ret = stm32_pctrl_create_pins_tab(pctl, pctl->pins); ++ if (ret) ++ return ret; ++ + ret = stm32_pctrl_build_state(pdev); + if (ret) { + dev_err(dev, "build state failed: %d\n", ret); +@@ -1174,22 +1253,23 @@ int stm32_pctl_probe(struct platform_device *pdev) + return ret; + } + +- pins = devm_kcalloc(&pdev->dev, pctl->match_data->npins, sizeof(*pins), ++ pins = devm_kcalloc(&pdev->dev, pctl->npins, sizeof(*pins), + GFP_KERNEL); + if (!pins) + return -ENOMEM; + +- for (i = 0; i < pctl->match_data->npins; i++) +- pins[i] = pctl->match_data->pins[i].pin; ++ for (i = 0; i < pctl->npins; i++) ++ pins[i] = pctl->pins[i].pin; + + pctl->pctl_desc.name = dev_name(&pdev->dev); + pctl->pctl_desc.owner = THIS_MODULE; + pctl->pctl_desc.pins = pins; +- pctl->pctl_desc.npins = pctl->match_data->npins; ++ pctl->pctl_desc.npins = pctl->npins; + pctl->pctl_desc.confops = &stm32_pconf_ops; + pctl->pctl_desc.pctlops = &stm32_pctrl_ops; + pctl->pctl_desc.pmxops = &stm32_pmx_ops; + pctl->dev = &pdev->dev; ++ pctl->pin_base_shift = pctl->match_data->pin_base_shift; + + pctl->pctl_dev = devm_pinctrl_register(&pdev->dev, &pctl->pctl_desc, + pctl); +@@ -1227,3 +1307,100 @@ int stm32_pctl_probe(struct platform_device *pdev) + return 0; + } + ++#ifdef CONFIG_PM ++void stm32_gpio_backup_value(struct stm32_gpio_bank *bank, ++ u32 offset, u32 value) ++{ ++ bank->pin_backup[offset] &= ~BIT(STM32_GPIO_BKP_VAL); ++ bank->pin_backup[offset] |= value << STM32_GPIO_BKP_VAL; ++} ++ ++void stm32_gpio_backup_mode(struct stm32_gpio_bank *bank, ++ u32 offset, u32 mode, u32 alt) ++{ ++ bank->pin_backup[offset] &= ~(STM32_GPIO_BKP_MODE_MASK | ++ STM32_GPIO_BKP_ALT_MASK); ++ bank->pin_backup[offset] |= mode << STM32_GPIO_BKP_MODE_SHIFT; ++ bank->pin_backup[offset] |= alt << STM32_GPIO_BKP_ALT_SHIFT; ++} ++ ++void stm32_gpio_backup_driving(struct stm32_gpio_bank *bank, ++ u32 offset, u32 drive) ++{ ++ bank->pin_backup[offset] &= ~BIT(STM32_GPIO_BKP_TYPE); ++ bank->pin_backup[offset] |= drive << STM32_GPIO_BKP_TYPE; ++} ++ ++void stm32_gpio_backup_speed(struct stm32_gpio_bank *bank, ++ u32 offset, u32 speed) ++{ ++ bank->pin_backup[offset] &= ~STM32_GPIO_BKP_SPEED_MASK; ++ bank->pin_backup[offset] |= speed << STM32_GPIO_BKP_SPEED_SHIFT; ++} ++ ++void stm32_gpio_backup_bias(struct stm32_gpio_bank *bank, ++ u32 offset, u32 bias) ++{ ++ bank->pin_backup[offset] &= ~STM32_GPIO_BKP_PUPD_MASK; ++ bank->pin_backup[offset] |= bias << STM32_GPIO_BKP_PUPD_SHIFT; ++} ++ ++void stm32_pinctrl_restore_gpio_regs(struct stm32_pinctrl *pctl, u32 pin) ++{ ++ const struct pin_desc *desc = pin_desc_get(pctl->pctl_dev, pin); ++ struct pinctrl_gpio_range *range; ++ struct stm32_gpio_bank *bank; ++ u32 val, alt, mode, offset = stm32_gpio_pin(pin); ++ bool pin_is_irq; ++ ++ range = pinctrl_find_gpio_range_from_pin(pctl->pctl_dev, pin); ++ if (!range) ++ return; ++ ++ pin_is_irq = gpiochip_line_is_irq(range->gc, offset); ++ ++ if (!desc || (!pin_is_irq && !desc->gpio_owner)) ++ return; ++ ++ bank = gpiochip_get_data(range->gc); ++ ++ alt = bank->pin_backup[offset] & STM32_GPIO_BKP_ALT_MASK; ++ alt >>= STM32_GPIO_BKP_ALT_SHIFT; ++ mode = bank->pin_backup[offset] & STM32_GPIO_BKP_MODE_MASK; ++ mode >>= STM32_GPIO_BKP_MODE_SHIFT; ++ ++ stm32_pmx_set_mode(bank, offset, mode, alt); ++ if (mode == 1) { ++ val = bank->pin_backup[offset] & BIT(STM32_GPIO_BKP_VAL); ++ val = val >> STM32_GPIO_BKP_VAL; ++ __stm32_gpio_set(bank, offset, val); ++ } ++ ++ val = bank->pin_backup[offset] & BIT(STM32_GPIO_BKP_TYPE); ++ val >>= STM32_GPIO_BKP_TYPE; ++ stm32_pconf_set_driving(bank, offset, val); ++ ++ val = bank->pin_backup[offset] & STM32_GPIO_BKP_SPEED_MASK; ++ val >>= STM32_GPIO_BKP_SPEED_SHIFT; ++ stm32_pconf_set_speed(bank, offset, val); ++ ++ val = bank->pin_backup[offset] & STM32_GPIO_BKP_PUPD_MASK; ++ val >>= STM32_GPIO_BKP_PUPD_SHIFT; ++ stm32_pconf_set_bias(bank, offset, val); ++ ++ if (pin_is_irq) ++ regmap_field_write(pctl->irqmux[offset], bank->bank_ioport_nr); ++} ++ ++int stm32_pinctrl_resume(struct device *dev) ++{ ++ struct stm32_pinctrl *pctl = dev_get_drvdata(dev); ++ struct stm32_pinctrl_group *g = pctl->groups; ++ int i; ++ ++ for (i = g->pin; i < g->pin + pctl->ngroups; i++) ++ stm32_pinctrl_restore_gpio_regs(pctl, i); ++ ++ return 0; ++} ++#endif /* CONFIG_PM */ +diff --git a/drivers/pinctrl/stm32/pinctrl-stm32.h b/drivers/pinctrl/stm32/pinctrl-stm32.h +index 473a623..eb6ed85 100644 +--- a/drivers/pinctrl/stm32/pinctrl-stm32.h ++++ b/drivers/pinctrl/stm32/pinctrl-stm32.h +@@ -17,6 +17,15 @@ + #define STM32_PIN_GPIO 0 + #define STM32_PIN_AF(x) ((x) + 1) + #define STM32_PIN_ANALOG (STM32_PIN_AF(15) + 1) ++#define STM32_CONFIG_NUM 18 ++ ++/* package information */ ++#define STM32MP157CAA BIT(0) ++#define STM32MP157CAB BIT(1) ++#define STM32MP157CAC BIT(2) ++#define STM32MP157CAD BIT(3) ++ ++#define STM32MP157_Z_BASE_SHIFT 400 + + struct stm32_desc_function { + const char *name; +@@ -25,7 +34,8 @@ struct stm32_desc_function { + + struct stm32_desc_pin { + struct pinctrl_pin_desc pin; +- const struct stm32_desc_function *functions; ++ const struct stm32_desc_function functions[STM32_CONFIG_NUM]; ++ const unsigned int pkg; + }; + + #define STM32_PIN(_pin, ...) \ +@@ -35,8 +45,15 @@ struct stm32_desc_pin { + __VA_ARGS__, { } }, \ + } + +-#define STM32_FUNCTION(_num, _name) \ ++#define STM32_PIN_PKG(_pin, _pkg, ...) \ + { \ ++ .pin = _pin, \ ++ .pkg = _pkg, \ ++ .functions = { \ ++ __VA_ARGS__}, \ ++ } ++#define STM32_FUNCTION(_num, _name) \ ++ [_num] = { \ + .num = _num, \ + .name = _name, \ + } +@@ -44,6 +61,7 @@ struct stm32_desc_pin { + struct stm32_pinctrl_match_data { + const struct stm32_desc_pin *pins; + const unsigned int npins; ++ const unsigned int pin_base_shift; + }; + + struct stm32_gpio_bank; +@@ -51,5 +69,35 @@ struct stm32_gpio_bank; + int stm32_pctl_probe(struct platform_device *pdev); + void stm32_pmx_get_mode(struct stm32_gpio_bank *bank, + int pin, u32 *mode, u32 *alt); ++ ++#ifdef CONFIG_PM ++void stm32_gpio_backup_value(struct stm32_gpio_bank *bank, ++ u32 offset, u32 value); ++void stm32_gpio_backup_driving(struct stm32_gpio_bank *bank, ++ u32 offset, u32 drive); ++void stm32_gpio_backup_speed(struct stm32_gpio_bank *bank, ++ u32 offset, u32 speed); ++void stm32_gpio_backup_mode(struct stm32_gpio_bank *bank, ++ u32 offset, u32 mode, u32 alt); ++void stm32_gpio_backup_bias(struct stm32_gpio_bank *bank, ++ u32 offset, u32 bias); ++int stm32_pinctrl_resume(struct device *dev); ++#else ++static void stm32_gpio_backup_value(struct stm32_gpio_bank *bank, ++ u32 offset, u32 value) ++{} ++static void stm32_gpio_backup_driving(struct stm32_gpio_bank *bank, ++ u32 offset, u32 drive) ++{} ++static void stm32_gpio_backup_speed(struct stm32_gpio_bank *bank, ++ u32 offset, u32 speed) ++{} ++static void stm32_gpio_backup_mode(struct stm32_gpio_bank *bank, ++ u32 offset, u32 mode, u32 alt) ++{} ++static void stm32_gpio_backup_bias(struct stm32_gpio_bank *bank, ++ u32 offset, u32 bias) ++{} ++#endif /* CONFIG_PM */ + #endif /* __PINCTRL_STM32_H */ + +diff --git a/drivers/pinctrl/stm32/pinctrl-stm32mp157.c b/drivers/pinctrl/stm32/pinctrl-stm32mp157.c +index 7c7d628..e1a8a89 100644 +--- a/drivers/pinctrl/stm32/pinctrl-stm32mp157.c ++++ b/drivers/pinctrl/stm32/pinctrl-stm32mp157.c +@@ -10,77 +10,82 @@ + #include "pinctrl-stm32.h" + + static const struct stm32_desc_pin stm32mp157_pins[] = { +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(0, "PA0"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOA0"), + STM32_FUNCTION(2, "TIM2_CH1 TIM2_ETR"), + STM32_FUNCTION(3, "TIM5_CH1"), + STM32_FUNCTION(4, "TIM8_ETR"), + STM32_FUNCTION(5, "TIM15_BKIN"), +- STM32_FUNCTION(8, "USART2_CTS_NSS USART_BOOT2_CTS_NSS"), ++ STM32_FUNCTION(8, "USART2_CTS USART2_NSS"), + STM32_FUNCTION(9, "UART4_TX"), + STM32_FUNCTION(10, "SDMMC2_CMD"), + STM32_FUNCTION(11, "SAI2_SD_B"), +- STM32_FUNCTION(12, "ETH_GMII_CRS ETH_MII_CRS"), ++ STM32_FUNCTION(12, "ETH1_GMII_CRS ETH1_MII_CRS"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(1, "PA1"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOA1"), + STM32_FUNCTION(1, "ETH_CLK"), + STM32_FUNCTION(2, "TIM2_CH2"), + STM32_FUNCTION(3, "TIM5_CH2"), + STM32_FUNCTION(4, "LPTIM3_OUT"), + STM32_FUNCTION(5, "TIM15_CH1N"), +- STM32_FUNCTION(8, "USART2_RTS USART_BOOT2_RTS"), ++ STM32_FUNCTION(8, "USART2_RTS USART2_DE"), + STM32_FUNCTION(9, "UART4_RX"), +- STM32_FUNCTION(10, "QUADSPI_BK1_IO3 QUADSPI_BOOTBK1_IO3"), ++ STM32_FUNCTION(10, "QUADSPI_BK1_IO3"), + STM32_FUNCTION(11, "SAI2_MCLK_B"), +- STM32_FUNCTION(12, "ETH_GMII_RX_CLK ETH_MII_RX_CLK ETH_RGMII_RX_CLK ETH_RMII_REF_CLK"), ++ STM32_FUNCTION(12, "ETH1_GMII_RX_CLK ETH1_MII_RX_CLK ETH1_RGMII_RX_CLK ETH1_RMII_REF_CLK"), + STM32_FUNCTION(15, "LCD_R2"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(2, "PA2"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOA2"), + STM32_FUNCTION(2, "TIM2_CH3"), + STM32_FUNCTION(3, "TIM5_CH3"), + STM32_FUNCTION(4, "LPTIM4_OUT"), + STM32_FUNCTION(5, "TIM15_CH1"), +- STM32_FUNCTION(8, "USART2_TX USART_BOOT2_TX"), ++ STM32_FUNCTION(8, "USART2_TX"), + STM32_FUNCTION(9, "SAI2_SCK_B"), +- STM32_FUNCTION(11, "SDMMC2_D0DIR SDMMC_BOOT2_D0DIR"), +- STM32_FUNCTION(12, "ETH_MDIO"), ++ STM32_FUNCTION(11, "SDMMC2_D0DIR"), ++ STM32_FUNCTION(12, "ETH1_MDIO"), + STM32_FUNCTION(13, "MDIOS_MDIO"), + STM32_FUNCTION(15, "LCD_R1"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(3, "PA3"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOA3"), + STM32_FUNCTION(2, "TIM2_CH4"), + STM32_FUNCTION(3, "TIM5_CH4"), + STM32_FUNCTION(4, "LPTIM5_OUT"), + STM32_FUNCTION(5, "TIM15_CH2"), +- STM32_FUNCTION(8, "USART2_RX USART_BOOT2_RX"), ++ STM32_FUNCTION(8, "USART2_RX"), + STM32_FUNCTION(10, "LCD_B2"), +- STM32_FUNCTION(12, "ETH_GMII_COL ETH_MII_COL"), ++ STM32_FUNCTION(12, "ETH1_GMII_COL ETH1_MII_COL"), + STM32_FUNCTION(15, "LCD_B5"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(4, "PA4"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOA4"), + STM32_FUNCTION(1, "HDP0"), + STM32_FUNCTION(3, "TIM5_ETR"), + STM32_FUNCTION(5, "SAI4_D2"), + STM32_FUNCTION(6, "SPI1_NSS I2S1_WS"), + STM32_FUNCTION(7, "SPI3_NSS I2S3_WS"), +- STM32_FUNCTION(8, "USART2_CK USART_BOOT2_CK"), ++ STM32_FUNCTION(8, "USART2_CK"), + STM32_FUNCTION(9, "SPI6_NSS"), + STM32_FUNCTION(13, "SAI4_FS_A"), + STM32_FUNCTION(14, "DCMI_HSYNC"), +@@ -88,8 +93,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(5, "PA5"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOA5"), + STM32_FUNCTION(2, "TIM2_CH1 TIM2_ETR"), + STM32_FUNCTION(4, "TIM8_CH1N"), +@@ -101,8 +107,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(6, "PA6"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOA6"), + STM32_FUNCTION(2, "TIM1_BKIN"), + STM32_FUNCTION(3, "TIM3_CH1"), +@@ -118,8 +125,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(7, "PA7"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOA7"), + STM32_FUNCTION(2, "TIM1_CH1N"), + STM32_FUNCTION(3, "TIM3_CH2"), +@@ -129,13 +137,14 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(9, "SPI6_MOSI"), + STM32_FUNCTION(10, "TIM14_CH1"), + STM32_FUNCTION(11, "QUADSPI_CLK"), +- STM32_FUNCTION(12, "ETH_GMII_RX_DV ETH_MII_RX_DV ETH_RGMII_RX_CTL ETH_RMII_CRS_DV"), ++ STM32_FUNCTION(12, "ETH1_GMII_RX_DV ETH1_MII_RX_DV ETH1_RGMII_RX_CTL ETH1_RMII_CRS_DV"), + STM32_FUNCTION(13, "SAI4_SD_A"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(8, "PA8"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOA8"), + STM32_FUNCTION(1, "MCO1"), + STM32_FUNCTION(2, "TIM1_CH1"), +@@ -143,37 +152,37 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(5, "I2C3_SCL"), + STM32_FUNCTION(6, "SPI3_MOSI I2S3_SDO"), + STM32_FUNCTION(8, "USART1_CK"), +- STM32_FUNCTION(9, "SDMMC2_CKIN SDMMC_BOOT2_CKIN"), +- STM32_FUNCTION(10, "SDMMC2_D4 SDMMC_BOOT2_D4"), +- STM32_FUNCTION(11, "USBO_SOF"), ++ STM32_FUNCTION(9, "SDMMC2_CKIN"), ++ STM32_FUNCTION(10, "SDMMC2_D4"), ++ STM32_FUNCTION(11, "OTG_FS_SOF OTG_HS_SOF"), + STM32_FUNCTION(13, "SAI4_SD_B"), + STM32_FUNCTION(14, "UART7_RX"), + STM32_FUNCTION(15, "LCD_R6"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(9, "PA9"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOA9"), + STM32_FUNCTION(2, "TIM1_CH2"), + STM32_FUNCTION(5, "I2C3_SMBA"), + STM32_FUNCTION(6, "SPI2_SCK I2S2_CK"), + STM32_FUNCTION(8, "USART1_TX"), +- STM32_FUNCTION(9, "SDMMC2_CDIR SDMMC_BOOT2_CDIR"), +- STM32_FUNCTION(10, "CAN1_RXFD"), +- STM32_FUNCTION(11, "SDMMC2_D5 SDMMC_BOOT2_D5"), ++ STM32_FUNCTION(9, "SDMMC2_CDIR"), ++ STM32_FUNCTION(11, "SDMMC2_D5"), + STM32_FUNCTION(14, "DCMI_D0"), + STM32_FUNCTION(15, "LCD_R5"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(10, "PA10"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOA10"), + STM32_FUNCTION(2, "TIM1_CH3"), + STM32_FUNCTION(6, "SPI3_NSS I2S3_WS"), + STM32_FUNCTION(8, "USART1_RX"), +- STM32_FUNCTION(10, "CAN1_TXFD"), + STM32_FUNCTION(12, "MDIOS_MDIO"), + STM32_FUNCTION(13, "SAI4_FS_B"), + STM32_FUNCTION(14, "DCMI_D1"), +@@ -181,37 +190,39 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(11, "PA11"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOA11"), + STM32_FUNCTION(2, "TIM1_CH4"), + STM32_FUNCTION(3, "I2C6_SCL"), + STM32_FUNCTION(5, "I2C5_SCL"), + STM32_FUNCTION(6, "SPI2_NSS I2S2_WS"), + STM32_FUNCTION(7, "UART4_RX"), +- STM32_FUNCTION(8, "USART1_CTS_NSS"), +- STM32_FUNCTION(10, "CAN1_RX"), ++ STM32_FUNCTION(8, "USART1_CTS USART1_NSS"), ++ STM32_FUNCTION(10, "FDCAN1_RX"), + STM32_FUNCTION(15, "LCD_R4"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(12, "PA12"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOA12"), + STM32_FUNCTION(2, "TIM1_ETR"), + STM32_FUNCTION(3, "I2C6_SDA"), + STM32_FUNCTION(5, "I2C5_SDA"), +- STM32_FUNCTION(6, "SPI2_SCK I2S2_CK"), + STM32_FUNCTION(7, "UART4_TX"), +- STM32_FUNCTION(8, "USART1_RTS"), ++ STM32_FUNCTION(8, "USART1_RTS USART1_DE"), + STM32_FUNCTION(9, "SAI2_FS_B"), +- STM32_FUNCTION(10, "CAN1_TX"), ++ STM32_FUNCTION(10, "FDCAN1_TX"), + STM32_FUNCTION(15, "LCD_R5"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(13, "PA13"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOA13"), + STM32_FUNCTION(1, "DBTRGO"), + STM32_FUNCTION(2, "DBTRGI"), +@@ -220,8 +231,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(14, "PA14"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOA14"), + STM32_FUNCTION(1, "DBTRGO"), + STM32_FUNCTION(2, "DBTRGI"), +@@ -229,73 +241,79 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(15, "PA15"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOA15"), + STM32_FUNCTION(1, "DBTRGI"), + STM32_FUNCTION(2, "TIM2_CH1 TIM2_ETR"), + STM32_FUNCTION(3, "SAI4_D2"), + STM32_FUNCTION(4, "SDMMC1_CDIR"), +- STM32_FUNCTION(5, "HDMI_CEC"), ++ STM32_FUNCTION(5, "CEC"), + STM32_FUNCTION(6, "SPI1_NSS I2S1_WS"), + STM32_FUNCTION(7, "SPI3_NSS I2S3_WS"), + STM32_FUNCTION(8, "SPI6_NSS"), +- STM32_FUNCTION(9, "UART4_RTS UART_BOOT4_RTS"), +- STM32_FUNCTION(10, "SDMMC2_D5 SDMMC_BOOT2_D5"), +- STM32_FUNCTION(11, "SDMMC2_CDIR SDMMC_BOOT2_CDIR"), +- STM32_FUNCTION(12, "SDMMC1_D5 SDMMC_BOOT1_D5"), ++ STM32_FUNCTION(9, "UART4_RTS UART4_DE"), ++ STM32_FUNCTION(10, "SDMMC2_D5"), ++ STM32_FUNCTION(11, "SDMMC2_CDIR"), ++ STM32_FUNCTION(12, "SDMMC1_D5"), + STM32_FUNCTION(13, "SAI4_FS_A"), + STM32_FUNCTION(14, "UART7_TX"), ++ STM32_FUNCTION(15, "LCD_R1"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(16, "PB0"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOB0"), + STM32_FUNCTION(2, "TIM1_CH2N"), + STM32_FUNCTION(3, "TIM3_CH3"), + STM32_FUNCTION(4, "TIM8_CH2N"), +- STM32_FUNCTION(7, "DFSDM_CKOUT"), +- STM32_FUNCTION(9, "UART4_CTS UART_BOOT4_CTS"), ++ STM32_FUNCTION(7, "DFSDM1_CKOUT"), ++ STM32_FUNCTION(9, "UART4_CTS"), + STM32_FUNCTION(10, "LCD_R3"), +- STM32_FUNCTION(12, "ETH_GMII_RXD2 ETH_MII_RXD2 ETH_RGMII_RXD2"), ++ STM32_FUNCTION(12, "ETH1_GMII_RXD2 ETH1_MII_RXD2 ETH1_RGMII_RXD2"), + STM32_FUNCTION(13, "MDIOS_MDIO"), + STM32_FUNCTION(15, "LCD_G1"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(17, "PB1"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOB1"), + STM32_FUNCTION(2, "TIM1_CH3N"), + STM32_FUNCTION(3, "TIM3_CH4"), + STM32_FUNCTION(4, "TIM8_CH3N"), +- STM32_FUNCTION(7, "DFSDM_DATA1"), ++ STM32_FUNCTION(7, "DFSDM1_DATIN1"), + STM32_FUNCTION(10, "LCD_R6"), +- STM32_FUNCTION(12, "ETH_GMII_RXD3 ETH_MII_RXD3 ETH_RGMII_RXD3"), ++ STM32_FUNCTION(12, "ETH1_GMII_RXD3 ETH1_MII_RXD3 ETH1_RGMII_RXD3"), + STM32_FUNCTION(13, "MDIOS_MDC"), + STM32_FUNCTION(15, "LCD_G0"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(18, "PB2"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOB2"), + STM32_FUNCTION(1, "TRACED4"), + STM32_FUNCTION(2, "RTC_OUT2"), + STM32_FUNCTION(3, "SAI1_D1"), +- STM32_FUNCTION(4, "DFSDM_CK1"), ++ STM32_FUNCTION(4, "DFSDM1_CKIN1"), + STM32_FUNCTION(5, "USART1_RX"), + STM32_FUNCTION(6, "I2S_CKIN"), + STM32_FUNCTION(7, "SAI1_SD_A"), + STM32_FUNCTION(8, "SPI3_MOSI I2S3_SDO"), +- STM32_FUNCTION(9, "UART4_RX UART_BOOT4_RX"), ++ STM32_FUNCTION(9, "UART4_RX"), + STM32_FUNCTION(10, "QUADSPI_CLK"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(19, "PB3"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOB3"), + STM32_FUNCTION(1, "TRACED9"), + STM32_FUNCTION(2, "TIM2_CH2"), +@@ -303,14 +321,15 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(6, "SPI1_SCK I2S1_CK"), + STM32_FUNCTION(7, "SPI3_SCK I2S3_CK"), + STM32_FUNCTION(9, "SPI6_SCK"), +- STM32_FUNCTION(10, "SDMMC2_D2 SDMMC_BOOT2_D2"), ++ STM32_FUNCTION(10, "SDMMC2_D2"), + STM32_FUNCTION(13, "SAI4_MCLK_A"), + STM32_FUNCTION(14, "UART7_RX"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(20, "PB4"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOB4"), + STM32_FUNCTION(1, "TRACED8"), + STM32_FUNCTION(2, "TIM16_BKIN"), +@@ -320,14 +339,15 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(7, "SPI3_MISO I2S3_SDI"), + STM32_FUNCTION(8, "SPI2_NSS I2S2_WS"), + STM32_FUNCTION(9, "SPI6_MISO"), +- STM32_FUNCTION(10, "SDMMC2_D3 SDMMC_BOOT2_D3"), ++ STM32_FUNCTION(10, "SDMMC2_D3"), + STM32_FUNCTION(13, "SAI4_SCK_A"), + STM32_FUNCTION(14, "UART7_TX"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(21, "PB5"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOB5"), + STM32_FUNCTION(1, "ETH_CLK"), + STM32_FUNCTION(2, "TIM17_BKIN"), +@@ -338,166 +358,175 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(7, "I2C4_SMBA"), + STM32_FUNCTION(8, "SPI3_MOSI I2S3_SDO"), + STM32_FUNCTION(9, "SPI6_MOSI"), +- STM32_FUNCTION(10, "CAN2_RX"), ++ STM32_FUNCTION(10, "FDCAN2_RX"), + STM32_FUNCTION(11, "SAI4_SD_A"), +- STM32_FUNCTION(12, "ETH_PPS_OUT"), +- STM32_FUNCTION(13, "UART5_RX UART_BOOT5_RX"), ++ STM32_FUNCTION(12, "ETH1_PPS_OUT"), ++ STM32_FUNCTION(13, "UART5_RX"), + STM32_FUNCTION(14, "DCMI_D10"), + STM32_FUNCTION(15, "LCD_G7"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(22, "PB6"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOB6"), + STM32_FUNCTION(2, "TIM16_CH1N"), + STM32_FUNCTION(3, "TIM4_CH1"), + STM32_FUNCTION(5, "I2C1_SCL"), +- STM32_FUNCTION(6, "HDMI_CEC"), ++ STM32_FUNCTION(6, "CEC"), + STM32_FUNCTION(7, "I2C4_SCL"), + STM32_FUNCTION(8, "USART1_TX"), +- STM32_FUNCTION(10, "CAN2_TX"), +- STM32_FUNCTION(11, "QUADSPI_BK1_NCS QUADSPI_BOOTBK1_NCS"), +- STM32_FUNCTION(12, "DFSDM_DATA5"), ++ STM32_FUNCTION(10, "FDCAN2_TX"), ++ STM32_FUNCTION(11, "QUADSPI_BK1_NCS"), ++ STM32_FUNCTION(12, "DFSDM1_DATIN5"), + STM32_FUNCTION(13, "UART5_TX"), + STM32_FUNCTION(14, "DCMI_D5"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(23, "PB7"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOB7"), + STM32_FUNCTION(2, "TIM17_CH1N"), + STM32_FUNCTION(3, "TIM4_CH2"), + STM32_FUNCTION(5, "I2C1_SDA"), + STM32_FUNCTION(7, "I2C4_SDA"), + STM32_FUNCTION(8, "USART1_RX"), +- STM32_FUNCTION(10, "CAN2_TXFD"), +- STM32_FUNCTION(11, "SDMMC2_D1 SDMMC_BOOT2_D1"), +- STM32_FUNCTION(12, "DFSDM_CK5"), ++ STM32_FUNCTION(11, "SDMMC2_D1"), ++ STM32_FUNCTION(12, "DFSDM1_CKIN5"), + STM32_FUNCTION(13, "FMC_NL"), + STM32_FUNCTION(14, "DCMI_VSYNC"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(24, "PB8"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOB8"), + STM32_FUNCTION(1, "HDP6"), + STM32_FUNCTION(2, "TIM16_CH1"), + STM32_FUNCTION(3, "TIM4_CH3"), +- STM32_FUNCTION(4, "DFSDM_CK7"), ++ STM32_FUNCTION(4, "DFSDM1_CKIN7"), + STM32_FUNCTION(5, "I2C1_SCL"), +- STM32_FUNCTION(6, "SDMMC1_CKIN SDMMC_BOOT1_CKIN"), ++ STM32_FUNCTION(6, "SDMMC1_CKIN"), + STM32_FUNCTION(7, "I2C4_SCL"), +- STM32_FUNCTION(8, "SDMMC2_CKIN SDMMC_BOOT2_CKIN"), ++ STM32_FUNCTION(8, "SDMMC2_CKIN"), + STM32_FUNCTION(9, "UART4_RX"), +- STM32_FUNCTION(10, "CAN1_RX"), +- STM32_FUNCTION(11, "SDMMC2_D4 SDMMC_BOOT2_D4"), +- STM32_FUNCTION(12, "ETH_GMII_TXD3 ETH_MII_TXD3 ETH_RGMII_TXD3"), +- STM32_FUNCTION(13, "SDMMC1_D4 SDMMC_BOOT1_D4"), ++ STM32_FUNCTION(10, "FDCAN1_RX"), ++ STM32_FUNCTION(11, "SDMMC2_D4"), ++ STM32_FUNCTION(12, "ETH1_GMII_TXD3 ETH1_MII_TXD3 ETH1_RGMII_TXD3"), ++ STM32_FUNCTION(13, "SDMMC1_D4"), + STM32_FUNCTION(14, "DCMI_D6"), + STM32_FUNCTION(15, "LCD_B6"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(25, "PB9"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOB9"), + STM32_FUNCTION(1, "HDP7"), + STM32_FUNCTION(2, "TIM17_CH1"), + STM32_FUNCTION(3, "TIM4_CH4"), +- STM32_FUNCTION(4, "DFSDM_DATA7"), ++ STM32_FUNCTION(4, "DFSDM1_DATIN7"), + STM32_FUNCTION(5, "I2C1_SDA"), + STM32_FUNCTION(6, "SPI2_NSS I2S2_WS"), + STM32_FUNCTION(7, "I2C4_SDA"), +- STM32_FUNCTION(8, "SDMMC2_CDIR SDMMC_BOOT2_CDIR"), ++ STM32_FUNCTION(8, "SDMMC2_CDIR"), + STM32_FUNCTION(9, "UART4_TX"), +- STM32_FUNCTION(10, "CAN1_TX"), +- STM32_FUNCTION(11, "SDMMC2_D5 SDMMC_BOOT2_D5"), +- STM32_FUNCTION(12, "SDMMC1_CDIR SDMMC_BOOT1_CDIR"), +- STM32_FUNCTION(13, "SDMMC1_D5 SDMMC_BOOT1_D5"), ++ STM32_FUNCTION(10, "FDCAN1_TX"), ++ STM32_FUNCTION(11, "SDMMC2_D5"), ++ STM32_FUNCTION(12, "SDMMC1_CDIR"), ++ STM32_FUNCTION(13, "SDMMC1_D5"), + STM32_FUNCTION(14, "DCMI_D7"), + STM32_FUNCTION(15, "LCD_B7"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(26, "PB10"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOB10"), + STM32_FUNCTION(2, "TIM2_CH3"), + STM32_FUNCTION(4, "LPTIM2_IN1"), + STM32_FUNCTION(5, "I2C2_SCL"), + STM32_FUNCTION(6, "SPI2_SCK I2S2_CK"), +- STM32_FUNCTION(7, "DFSDM_DATA7"), +- STM32_FUNCTION(8, "USART3_TX USART_BOOT3_TX"), ++ STM32_FUNCTION(7, "DFSDM1_DATIN7"), ++ STM32_FUNCTION(8, "USART3_TX"), + STM32_FUNCTION(10, "QUADSPI_BK1_NCS"), +- STM32_FUNCTION(12, "ETH_GMII_RX_ER ETH_MII_RX_ER"), ++ STM32_FUNCTION(12, "ETH1_GMII_RX_ER ETH1_MII_RX_ER"), + STM32_FUNCTION(15, "LCD_G4"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(27, "PB11"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOB11"), + STM32_FUNCTION(2, "TIM2_CH4"), + STM32_FUNCTION(4, "LPTIM2_ETR"), + STM32_FUNCTION(5, "I2C2_SDA"), +- STM32_FUNCTION(7, "DFSDM_CK7"), ++ STM32_FUNCTION(7, "DFSDM1_CKIN7"), + STM32_FUNCTION(8, "USART3_RX"), +- STM32_FUNCTION(12, "ETH_GMII_TX_EN ETH_MII_TX_EN ETH_RGMII_TX_CTL ETH_RMII_TX_EN"), ++ STM32_FUNCTION(12, "ETH1_GMII_TX_EN ETH1_MII_TX_EN ETH1_RGMII_TX_CTL ETH1_RMII_TX_EN"), + STM32_FUNCTION(14, "DSI_TE"), + STM32_FUNCTION(15, "LCD_G5"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(28, "PB12"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOB12"), + STM32_FUNCTION(2, "TIM1_BKIN"), + STM32_FUNCTION(3, "I2C6_SMBA"), + STM32_FUNCTION(5, "I2C2_SMBA"), + STM32_FUNCTION(6, "SPI2_NSS I2S2_WS"), +- STM32_FUNCTION(7, "DFSDM_DATA1"), +- STM32_FUNCTION(8, "USART3_CK USART_BOOT3_CK"), +- STM32_FUNCTION(9, "USART3_RX USART_BOOT3_RX"), +- STM32_FUNCTION(10, "CAN2_RX"), +- STM32_FUNCTION(12, "ETH_GMII_TXD0 ETH_MII_TXD0 ETH_RGMII_TXD0 ETH_RMII_TXD0"), ++ STM32_FUNCTION(7, "DFSDM1_DATIN1"), ++ STM32_FUNCTION(8, "USART3_CK"), ++ STM32_FUNCTION(9, "USART3_RX"), ++ STM32_FUNCTION(10, "FDCAN2_RX"), ++ STM32_FUNCTION(12, "ETH1_GMII_TXD0 ETH1_MII_TXD0 ETH1_RGMII_TXD0 ETH1_RMII_TXD0"), + STM32_FUNCTION(15, "UART5_RX"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(29, "PB13"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOB13"), + STM32_FUNCTION(2, "TIM1_CH1N"), +- STM32_FUNCTION(4, "DFSDM_CKOUT"), ++ STM32_FUNCTION(4, "DFSDM1_CKOUT"), + STM32_FUNCTION(5, "LPTIM2_OUT"), + STM32_FUNCTION(6, "SPI2_SCK I2S2_CK"), +- STM32_FUNCTION(7, "DFSDM_CK1"), +- STM32_FUNCTION(8, "USART3_CTS_NSS USART_BOOT3_CTS_NSS"), +- STM32_FUNCTION(10, "CAN2_TX"), +- STM32_FUNCTION(12, "ETH_GMII_TXD1 ETH_MII_TXD1 ETH_RGMII_TXD1 ETH_RMII_TXD1"), +- STM32_FUNCTION(15, "UART5_TX UART_BOOT5_TX"), ++ STM32_FUNCTION(7, "DFSDM1_CKIN1"), ++ STM32_FUNCTION(8, "USART3_CTS USART3_NSS"), ++ STM32_FUNCTION(10, "FDCAN2_TX"), ++ STM32_FUNCTION(12, "ETH1_GMII_TXD1 ETH1_MII_TXD1 ETH1_RGMII_TXD1 ETH1_RMII_TXD1"), ++ STM32_FUNCTION(15, "UART5_TX"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(30, "PB14"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOB14"), + STM32_FUNCTION(2, "TIM1_CH2N"), + STM32_FUNCTION(3, "TIM12_CH1"), + STM32_FUNCTION(4, "TIM8_CH2N"), + STM32_FUNCTION(5, "USART1_TX"), + STM32_FUNCTION(6, "SPI2_MISO I2S2_SDI"), +- STM32_FUNCTION(7, "DFSDM_DATA2"), +- STM32_FUNCTION(8, "USART3_RTS USART_BOOT3_RTS"), +- STM32_FUNCTION(10, "SDMMC2_D0 SDMMC_BOOT2_D0"), ++ STM32_FUNCTION(7, "DFSDM1_DATIN2"), ++ STM32_FUNCTION(8, "USART3_RTS USART3_DE"), ++ STM32_FUNCTION(10, "SDMMC2_D0"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(31, "PB15"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOB15"), + STM32_FUNCTION(1, "RTC_REFIN"), + STM32_FUNCTION(2, "TIM1_CH3N"), +@@ -505,523 +534,557 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(4, "TIM8_CH3N"), + STM32_FUNCTION(5, "USART1_RX"), + STM32_FUNCTION(6, "SPI2_MOSI I2S2_SDO"), +- STM32_FUNCTION(7, "DFSDM_CK2"), +- STM32_FUNCTION(10, "SDMMC2_D1 SDMMC_BOOT2_D1"), ++ STM32_FUNCTION(7, "DFSDM1_CKIN2"), ++ STM32_FUNCTION(10, "SDMMC2_D1"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(32, "PC0"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOC0"), +- STM32_FUNCTION(4, "DFSDM_CK0"), ++ STM32_FUNCTION(4, "DFSDM1_CKIN0"), + STM32_FUNCTION(5, "LPTIM2_IN2"), +- STM32_FUNCTION(7, "DFSDM_DATA4"), ++ STM32_FUNCTION(7, "DFSDM1_DATIN4"), + STM32_FUNCTION(9, "SAI2_FS_B"), +- STM32_FUNCTION(11, "QUADSPI_BK2_NCS QUADSPI_BOOTBK2_NCS"), ++ STM32_FUNCTION(11, "QUADSPI_BK2_NCS"), + STM32_FUNCTION(15, "LCD_R5"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(33, "PC1"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOC1"), + STM32_FUNCTION(1, "TRACED0"), + STM32_FUNCTION(3, "SAI1_D1"), +- STM32_FUNCTION(4, "DFSDM_DATA0"), +- STM32_FUNCTION(5, "DFSDM_CK4"), ++ STM32_FUNCTION(4, "DFSDM1_DATIN0"), ++ STM32_FUNCTION(5, "DFSDM1_CKIN4"), + STM32_FUNCTION(6, "SPI2_MOSI I2S2_SDO"), + STM32_FUNCTION(7, "SAI1_SD_A"), + STM32_FUNCTION(10, "SDMMC2_CK"), +- STM32_FUNCTION(12, "ETH_MDC"), ++ STM32_FUNCTION(12, "ETH1_MDC"), + STM32_FUNCTION(13, "MDIOS_MDC"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(34, "PC2"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOC2"), +- STM32_FUNCTION(4, "DFSDM_CK1"), ++ STM32_FUNCTION(4, "DFSDM1_CKIN1"), + STM32_FUNCTION(6, "SPI2_MISO I2S2_SDI"), +- STM32_FUNCTION(7, "DFSDM_CKOUT"), +- STM32_FUNCTION(12, "ETH_GMII_TXD2 ETH_MII_TXD2 ETH_RGMII_TXD2"), ++ STM32_FUNCTION(7, "DFSDM1_CKOUT"), ++ STM32_FUNCTION(12, "ETH1_GMII_TXD2 ETH1_MII_TXD2 ETH1_RGMII_TXD2"), ++ STM32_FUNCTION(14, "DCMI_PIXCLK"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(35, "PC3"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOC3"), + STM32_FUNCTION(1, "TRACECLK"), +- STM32_FUNCTION(4, "DFSDM_DATA1"), ++ STM32_FUNCTION(4, "DFSDM1_DATIN1"), + STM32_FUNCTION(6, "SPI2_MOSI I2S2_SDO"), +- STM32_FUNCTION(12, "ETH_GMII_TX_CLK ETH_MII_TX_CLK"), ++ STM32_FUNCTION(12, "ETH1_GMII_TX_CLK ETH1_MII_TX_CLK"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(36, "PC4"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOC4"), +- STM32_FUNCTION(4, "DFSDM_CK2"), ++ STM32_FUNCTION(4, "DFSDM1_CKIN2"), + STM32_FUNCTION(6, "I2S1_MCK"), +- STM32_FUNCTION(10, "SPDIF_IN2"), +- STM32_FUNCTION(12, "ETH_GMII_RXD0 ETH_MII_RXD0 ETH_RGMII_RXD0 ETH_RMII_RXD0"), ++ STM32_FUNCTION(10, "SPDIFRX_IN2"), ++ STM32_FUNCTION(12, "ETH1_GMII_RXD0 ETH1_MII_RXD0 ETH1_RGMII_RXD0 ETH1_RMII_RXD0"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(37, "PC5"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOC5"), + STM32_FUNCTION(3, "SAI1_D3"), +- STM32_FUNCTION(4, "DFSDM_DATA2"), ++ STM32_FUNCTION(4, "DFSDM1_DATIN2"), + STM32_FUNCTION(5, "SAI4_D4"), + STM32_FUNCTION(7, "SAI1_D4"), +- STM32_FUNCTION(10, "SPDIF_IN3"), +- STM32_FUNCTION(12, "ETH_GMII_RXD1 ETH_MII_RXD1 ETH_RGMII_RXD1 ETH_RMII_RXD1"), ++ STM32_FUNCTION(10, "SPDIFRX_IN3"), ++ STM32_FUNCTION(12, "ETH1_GMII_RXD1 ETH1_MII_RXD1 ETH1_RGMII_RXD1 ETH1_RMII_RXD1"), + STM32_FUNCTION(13, "SAI4_D3"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(38, "PC6"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOC6"), + STM32_FUNCTION(1, "HDP1"), + STM32_FUNCTION(3, "TIM3_CH1"), + STM32_FUNCTION(4, "TIM8_CH1"), +- STM32_FUNCTION(5, "DFSDM_CK3"), ++ STM32_FUNCTION(5, "DFSDM1_CKIN3"), + STM32_FUNCTION(6, "I2S2_MCK"), +- STM32_FUNCTION(8, "USART6_TX USART_BOOT6_TX"), +- STM32_FUNCTION(9, "SDMMC1_D0DIR SDMMC_BOOT1_D0DIR"), +- STM32_FUNCTION(10, "SDMMC2_D0DIR SDMMC_BOOT2_D0DIR"), +- STM32_FUNCTION(11, "SDMMC2_D6 SDMMC_BOOT2_D6"), ++ STM32_FUNCTION(8, "USART6_TX"), ++ STM32_FUNCTION(9, "SDMMC1_D0DIR"), ++ STM32_FUNCTION(10, "SDMMC2_D0DIR"), ++ STM32_FUNCTION(11, "SDMMC2_D6"), + STM32_FUNCTION(12, "DSI_TE"), +- STM32_FUNCTION(13, "SDMMC1_D6 SDMMC_BOOT1_D6"), ++ STM32_FUNCTION(13, "SDMMC1_D6"), + STM32_FUNCTION(14, "DCMI_D0"), + STM32_FUNCTION(15, "LCD_HSYNC"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(39, "PC7"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOC7"), + STM32_FUNCTION(1, "HDP4"), + STM32_FUNCTION(3, "TIM3_CH2"), + STM32_FUNCTION(4, "TIM8_CH2"), +- STM32_FUNCTION(5, "DFSDM_DATA3"), ++ STM32_FUNCTION(5, "DFSDM1_DATIN3"), + STM32_FUNCTION(7, "I2S3_MCK"), +- STM32_FUNCTION(8, "USART6_RX USART_BOOT6_RX"), +- STM32_FUNCTION(9, "SDMMC1_D123DIR SDMMC_BOOT1_D123DIR"), +- STM32_FUNCTION(10, "SDMMC2_D123DIR SDMMC_BOOT2_D123DIR"), +- STM32_FUNCTION(11, "SDMMC2_D7 SDMMC_BOOT2_D7"), +- STM32_FUNCTION(13, "SDMMC1_D7 SDMMC_BOOT1_D7"), ++ STM32_FUNCTION(8, "USART6_RX"), ++ STM32_FUNCTION(9, "SDMMC1_D123DIR"), ++ STM32_FUNCTION(10, "SDMMC2_D123DIR"), ++ STM32_FUNCTION(11, "SDMMC2_D7"), ++ STM32_FUNCTION(13, "SDMMC1_D7"), + STM32_FUNCTION(14, "DCMI_D1"), + STM32_FUNCTION(15, "LCD_G6"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(40, "PC8"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOC8"), + STM32_FUNCTION(1, "TRACED0"), + STM32_FUNCTION(3, "TIM3_CH3"), + STM32_FUNCTION(4, "TIM8_CH3"), + STM32_FUNCTION(7, "UART4_TX"), +- STM32_FUNCTION(8, "USART6_CK USART_BOOT6_CK"), +- STM32_FUNCTION(9, "UART5_RTS UART_BOOT5_RTS"), +- STM32_FUNCTION(13, "SDMMC1_D0 SDMMC_BOOT1_D0"), ++ STM32_FUNCTION(8, "USART6_CK"), ++ STM32_FUNCTION(9, "UART5_RTS UART5_DE"), ++ STM32_FUNCTION(13, "SDMMC1_D0"), + STM32_FUNCTION(14, "DCMI_D2"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(41, "PC9"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOC9"), + STM32_FUNCTION(1, "TRACED1"), + STM32_FUNCTION(3, "TIM3_CH4"), + STM32_FUNCTION(4, "TIM8_CH4"), + STM32_FUNCTION(5, "I2C3_SDA"), + STM32_FUNCTION(6, "I2S_CKIN"), +- STM32_FUNCTION(9, "UART5_CTS UART_BOOT5_CTS"), ++ STM32_FUNCTION(9, "UART5_CTS"), + STM32_FUNCTION(10, "QUADSPI_BK1_IO0"), +- STM32_FUNCTION(13, "SDMMC1_D1 SDMMC_BOOT1_D1"), ++ STM32_FUNCTION(13, "SDMMC1_D1"), + STM32_FUNCTION(14, "DCMI_D3"), + STM32_FUNCTION(15, "LCD_B2"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(42, "PC10"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOC10"), + STM32_FUNCTION(1, "TRACED2"), +- STM32_FUNCTION(4, "DFSDM_CK5"), ++ STM32_FUNCTION(4, "DFSDM1_CKIN5"), + STM32_FUNCTION(7, "SPI3_SCK I2S3_CK"), + STM32_FUNCTION(8, "USART3_TX"), + STM32_FUNCTION(9, "UART4_TX"), + STM32_FUNCTION(10, "QUADSPI_BK1_IO1"), + STM32_FUNCTION(11, "SAI4_MCLK_B"), +- STM32_FUNCTION(13, "SDMMC1_D2 SDMMC_BOOT1_D2"), ++ STM32_FUNCTION(13, "SDMMC1_D2"), + STM32_FUNCTION(14, "DCMI_D8"), + STM32_FUNCTION(15, "LCD_R2"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(43, "PC11"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOC11"), + STM32_FUNCTION(1, "TRACED3"), +- STM32_FUNCTION(4, "DFSDM_DATA5"), ++ STM32_FUNCTION(4, "DFSDM1_DATIN5"), + STM32_FUNCTION(7, "SPI3_MISO I2S3_SDI"), + STM32_FUNCTION(8, "USART3_RX"), + STM32_FUNCTION(9, "UART4_RX"), +- STM32_FUNCTION(10, "QUADSPI_BK2_NCS QUADSPI_BOOTBK2_NCS"), ++ STM32_FUNCTION(10, "QUADSPI_BK2_NCS"), + STM32_FUNCTION(11, "SAI4_SCK_B"), +- STM32_FUNCTION(13, "SDMMC1_D3 SDMMC_BOOT1_D3"), ++ STM32_FUNCTION(13, "SDMMC1_D3"), + STM32_FUNCTION(14, "DCMI_D4"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(44, "PC12"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOC12"), + STM32_FUNCTION(1, "TRACECLK"), + STM32_FUNCTION(2, "MCO2"), + STM32_FUNCTION(3, "SAI4_D3"), + STM32_FUNCTION(7, "SPI3_MOSI I2S3_SDO"), +- STM32_FUNCTION(8, "USART3_CK USART_BOOT3_CK"), ++ STM32_FUNCTION(8, "USART3_CK"), + STM32_FUNCTION(9, "UART5_TX"), + STM32_FUNCTION(11, "SAI4_SD_B"), +- STM32_FUNCTION(13, "SDMMC1_CK SDMMC_BOOT1_CK"), ++ STM32_FUNCTION(13, "SDMMC1_CK"), + STM32_FUNCTION(14, "DCMI_D9"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(45, "PC13"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOC13"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(46, "PC14"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOC14"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(47, "PC15"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOC15"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(48, "PD0"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOD0"), + STM32_FUNCTION(3, "I2C6_SDA"), +- STM32_FUNCTION(4, "DFSDM_CK6"), ++ STM32_FUNCTION(4, "DFSDM1_CKIN6"), + STM32_FUNCTION(5, "I2C5_SDA"), + STM32_FUNCTION(7, "SAI3_SCK_A"), + STM32_FUNCTION(9, "UART4_RX"), +- STM32_FUNCTION(10, "CAN1_RX"), ++ STM32_FUNCTION(10, "FDCAN1_RX"), + STM32_FUNCTION(11, "SDMMC3_CMD"), +- STM32_FUNCTION(12, "DFSDM_DATA7"), +- STM32_FUNCTION(13, "FMC_D2"), ++ STM32_FUNCTION(12, "DFSDM1_DATIN7"), ++ STM32_FUNCTION(13, "FMC_D2 FMC_DA2"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(49, "PD1"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOD1"), + STM32_FUNCTION(3, "I2C6_SCL"), +- STM32_FUNCTION(4, "DFSDM_DATA6"), ++ STM32_FUNCTION(4, "DFSDM1_DATIN6"), + STM32_FUNCTION(5, "I2C5_SCL"), + STM32_FUNCTION(7, "SAI3_SD_A"), + STM32_FUNCTION(9, "UART4_TX"), +- STM32_FUNCTION(10, "CAN1_TX"), ++ STM32_FUNCTION(10, "FDCAN1_TX"), + STM32_FUNCTION(11, "SDMMC3_D0"), +- STM32_FUNCTION(12, "DFSDM_CK7"), +- STM32_FUNCTION(13, "FMC_D3"), ++ STM32_FUNCTION(12, "DFSDM1_CKIN7"), ++ STM32_FUNCTION(13, "FMC_D3 FMC_DA3"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(50, "PD2"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOD2"), + STM32_FUNCTION(3, "TIM3_ETR"), + STM32_FUNCTION(5, "I2C5_SMBA"), + STM32_FUNCTION(7, "UART4_RX"), + STM32_FUNCTION(9, "UART5_RX"), +- STM32_FUNCTION(13, "SDMMC1_CMD SDMMC_BOOT1_CMD"), ++ STM32_FUNCTION(13, "SDMMC1_CMD"), + STM32_FUNCTION(14, "DCMI_D11"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(51, "PD3"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOD3"), + STM32_FUNCTION(1, "HDP5"), +- STM32_FUNCTION(4, "DFSDM_CKOUT"), ++ STM32_FUNCTION(4, "DFSDM1_CKOUT"), + STM32_FUNCTION(6, "SPI2_SCK I2S2_CK"), +- STM32_FUNCTION(7, "DFSDM_DATA0"), +- STM32_FUNCTION(8, "USART2_CTS_NSS USART_BOOT2_CTS_NSS"), +- STM32_FUNCTION(9, "SDMMC1_D123DIR SDMMC_BOOT1_D123DIR"), +- STM32_FUNCTION(10, "SDMMC2_D7 SDMMC_BOOT2_D7"), +- STM32_FUNCTION(11, "SDMMC2_D123DIR SDMMC_BOOT2_D123DIR"), +- STM32_FUNCTION(12, "SDMMC1_D7 SDMMC_BOOT1_D7"), ++ STM32_FUNCTION(7, "DFSDM1_DATIN0"), ++ STM32_FUNCTION(8, "USART2_CTS USART2_NSS"), ++ STM32_FUNCTION(9, "SDMMC1_D123DIR"), ++ STM32_FUNCTION(10, "SDMMC2_D7"), ++ STM32_FUNCTION(11, "SDMMC2_D123DIR"), ++ STM32_FUNCTION(12, "SDMMC1_D7"), + STM32_FUNCTION(13, "FMC_CLK"), + STM32_FUNCTION(14, "DCMI_D5"), + STM32_FUNCTION(15, "LCD_G7"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(52, "PD4"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOD4"), + STM32_FUNCTION(7, "SAI3_FS_A"), +- STM32_FUNCTION(8, "USART2_RTS USART_BOOT2_RTS"), +- STM32_FUNCTION(10, "CAN1_RXFD"), ++ STM32_FUNCTION(8, "USART2_RTS USART2_DE"), + STM32_FUNCTION(11, "SDMMC3_D1"), +- STM32_FUNCTION(12, "DFSDM_CK0"), ++ STM32_FUNCTION(12, "DFSDM1_CKIN0"), + STM32_FUNCTION(13, "FMC_NOE"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(53, "PD5"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOD5"), + STM32_FUNCTION(8, "USART2_TX"), +- STM32_FUNCTION(10, "CAN1_TXFD"), + STM32_FUNCTION(11, "SDMMC3_D2"), + STM32_FUNCTION(13, "FMC_NWE"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(54, "PD6"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOD6"), + STM32_FUNCTION(2, "TIM16_CH1N"), + STM32_FUNCTION(3, "SAI1_D1"), +- STM32_FUNCTION(4, "DFSDM_CK4"), +- STM32_FUNCTION(5, "DFSDM_DATA1"), ++ STM32_FUNCTION(4, "DFSDM1_CKIN4"), ++ STM32_FUNCTION(5, "DFSDM1_DATIN1"), + STM32_FUNCTION(6, "SPI3_MOSI I2S3_SDO"), + STM32_FUNCTION(7, "SAI1_SD_A"), + STM32_FUNCTION(8, "USART2_RX"), +- STM32_FUNCTION(10, "CAN2_RXFD"), +- STM32_FUNCTION(11, "FMC_INT"), + STM32_FUNCTION(13, "FMC_NWAIT"), + STM32_FUNCTION(14, "DCMI_D10"), + STM32_FUNCTION(15, "LCD_B2"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(55, "PD7"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOD7"), + STM32_FUNCTION(1, "TRACED6"), +- STM32_FUNCTION(4, "DFSDM_DATA4"), ++ STM32_FUNCTION(4, "DFSDM1_DATIN4"), + STM32_FUNCTION(5, "I2C2_SCL"), +- STM32_FUNCTION(7, "DFSDM_CK1"), +- STM32_FUNCTION(8, "USART2_CK USART_BOOT2_CK"), +- STM32_FUNCTION(10, "SPDIF_IN0"), ++ STM32_FUNCTION(7, "DFSDM1_CKIN1"), ++ STM32_FUNCTION(8, "USART2_CK"), ++ STM32_FUNCTION(10, "SPDIFRX_IN0"), + STM32_FUNCTION(11, "SDMMC3_D3"), + STM32_FUNCTION(13, "FMC_NE1"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(56, "PD8"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOD8"), +- STM32_FUNCTION(4, "DFSDM_CK3"), ++ STM32_FUNCTION(4, "DFSDM1_CKIN3"), + STM32_FUNCTION(7, "SAI3_SCK_B"), + STM32_FUNCTION(8, "USART3_TX"), +- STM32_FUNCTION(10, "SPDIF_IN1"), +- STM32_FUNCTION(13, "FMC_D13"), ++ STM32_FUNCTION(10, "SPDIFRX_IN1"), ++ STM32_FUNCTION(13, "FMC_D13 FMC_DA13"), + STM32_FUNCTION(15, "LCD_B7"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(57, "PD9"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOD9"), +- STM32_FUNCTION(4, "DFSDM_DATA3"), ++ STM32_FUNCTION(4, "DFSDM1_DATIN3"), + STM32_FUNCTION(7, "SAI3_SD_B"), + STM32_FUNCTION(8, "USART3_RX"), +- STM32_FUNCTION(10, "CAN2_RXFD"), +- STM32_FUNCTION(13, "FMC_D14"), ++ STM32_FUNCTION(13, "FMC_D14 FMC_DA14"), ++ STM32_FUNCTION(14, "DCMI_HSYNC"), + STM32_FUNCTION(15, "LCD_B0"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(58, "PD10"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOD10"), + STM32_FUNCTION(1, "RTC_REFIN"), + STM32_FUNCTION(2, "TIM16_BKIN"), +- STM32_FUNCTION(4, "DFSDM_CKOUT"), ++ STM32_FUNCTION(4, "DFSDM1_CKOUT"), + STM32_FUNCTION(5, "I2C5_SMBA"), + STM32_FUNCTION(6, "SPI3_MISO I2S3_SDI"), + STM32_FUNCTION(7, "SAI3_FS_B"), +- STM32_FUNCTION(8, "USART3_CK USART_BOOT3_CK"), +- STM32_FUNCTION(10, "CAN2_TXFD"), +- STM32_FUNCTION(13, "FMC_D15"), ++ STM32_FUNCTION(8, "USART3_CK"), ++ STM32_FUNCTION(13, "FMC_D15 FMC_DA15"), + STM32_FUNCTION(15, "LCD_B3"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(59, "PD11"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOD11"), + STM32_FUNCTION(4, "LPTIM2_IN2"), + STM32_FUNCTION(5, "I2C4_SMBA"), + STM32_FUNCTION(6, "I2C1_SMBA"), +- STM32_FUNCTION(8, "USART3_CTS_NSS USART_BOOT3_CTS_NSS"), ++ STM32_FUNCTION(8, "USART3_CTS USART3_NSS"), + STM32_FUNCTION(10, "QUADSPI_BK1_IO0"), + STM32_FUNCTION(11, "SAI2_SD_A"), +- STM32_FUNCTION(13, "FMC_A16 FMC_CLE"), ++ STM32_FUNCTION(13, "FMC_CLE FMC_A16"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(60, "PD12"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOD12"), + STM32_FUNCTION(2, "LPTIM1_IN1"), + STM32_FUNCTION(3, "TIM4_CH1"), + STM32_FUNCTION(4, "LPTIM2_IN1"), + STM32_FUNCTION(5, "I2C4_SCL"), + STM32_FUNCTION(6, "I2C1_SCL"), +- STM32_FUNCTION(8, "USART3_RTS USART_BOOT3_RTS"), ++ STM32_FUNCTION(8, "USART3_RTS USART3_DE"), + STM32_FUNCTION(10, "QUADSPI_BK1_IO1"), + STM32_FUNCTION(11, "SAI2_FS_A"), +- STM32_FUNCTION(13, "FMC_A17 FMC_ALE"), ++ STM32_FUNCTION(13, "FMC_ALE FMC_A17"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(61, "PD13"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOD13"), + STM32_FUNCTION(2, "LPTIM1_OUT"), + STM32_FUNCTION(3, "TIM4_CH2"), + STM32_FUNCTION(5, "I2C4_SDA"), + STM32_FUNCTION(6, "I2C1_SDA"), + STM32_FUNCTION(7, "I2S3_MCK"), +- STM32_FUNCTION(10, "QUADSPI_BK1_IO3 QUADSPI_BOOTBK1_IO3"), ++ STM32_FUNCTION(10, "QUADSPI_BK1_IO3"), + STM32_FUNCTION(11, "SAI2_SCK_A"), + STM32_FUNCTION(13, "FMC_A18"), + STM32_FUNCTION(14, "DSI_TE"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(62, "PD14"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOD14"), + STM32_FUNCTION(3, "TIM4_CH3"), + STM32_FUNCTION(7, "SAI3_MCLK_B"), +- STM32_FUNCTION(9, "UART8_CTS UART_BOOT8_CTS"), +- STM32_FUNCTION(13, "FMC_D0"), ++ STM32_FUNCTION(9, "UART8_CTS"), ++ STM32_FUNCTION(13, "FMC_D0 FMC_DA0"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(63, "PD15"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOD15"), + STM32_FUNCTION(3, "TIM4_CH4"), + STM32_FUNCTION(7, "SAI3_MCLK_A"), +- STM32_FUNCTION(9, "UART8_CTS UART_BOOT8_CTS"), +- STM32_FUNCTION(13, "FMC_D1"), ++ STM32_FUNCTION(9, "UART8_CTS"), ++ STM32_FUNCTION(13, "FMC_D1 FMC_DA1"), ++ STM32_FUNCTION(15, "LCD_R1"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(64, "PE0"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOE0"), + STM32_FUNCTION(2, "LPTIM1_ETR"), + STM32_FUNCTION(3, "TIM4_ETR"), + STM32_FUNCTION(5, "LPTIM2_ETR"), + STM32_FUNCTION(6, "SPI3_SCK I2S3_CK"), + STM32_FUNCTION(7, "SAI4_MCLK_B"), +- STM32_FUNCTION(9, "UART8_RX UART_BOOT8_RX"), +- STM32_FUNCTION(10, "CAN1_RXFD"), ++ STM32_FUNCTION(9, "UART8_RX"), + STM32_FUNCTION(11, "SAI2_MCLK_A"), + STM32_FUNCTION(13, "FMC_NBL0"), + STM32_FUNCTION(14, "DCMI_D2"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(65, "PE1"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOE1"), + STM32_FUNCTION(2, "LPTIM1_IN2"), + STM32_FUNCTION(6, "I2S2_MCK"), + STM32_FUNCTION(7, "SAI3_SD_B"), +- STM32_FUNCTION(9, "UART8_TX UART_BOOT8_TX"), +- STM32_FUNCTION(10, "CAN1_TXFD"), ++ STM32_FUNCTION(9, "UART8_TX"), + STM32_FUNCTION(13, "FMC_NBL1"), + STM32_FUNCTION(14, "DCMI_D3"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(66, "PE2"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOE2"), + STM32_FUNCTION(1, "TRACECLK"), + STM32_FUNCTION(3, "SAI1_CK1"), + STM32_FUNCTION(5, "I2C4_SCL"), + STM32_FUNCTION(6, "SPI4_SCK"), + STM32_FUNCTION(7, "SAI1_MCLK_A"), +- STM32_FUNCTION(10, "QUADSPI_BK1_IO2 QUADSPI_BOOTBK1_IO2"), +- STM32_FUNCTION(12, "ETH_GMII_TXD3 ETH_MII_TXD3 ETH_RGMII_TXD3"), ++ STM32_FUNCTION(10, "QUADSPI_BK1_IO2"), ++ STM32_FUNCTION(12, "ETH1_GMII_TXD3 ETH1_MII_TXD3 ETH1_RGMII_TXD3"), + STM32_FUNCTION(13, "FMC_A23"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(67, "PE3"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOE3"), + STM32_FUNCTION(1, "TRACED0"), + STM32_FUNCTION(5, "TIM15_BKIN"), + STM32_FUNCTION(7, "SAI1_SD_B"), +- STM32_FUNCTION(10, "SDMMC2_CK SDMMC_BOOT2_CK"), ++ STM32_FUNCTION(10, "SDMMC2_CK"), + STM32_FUNCTION(13, "FMC_A19"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(68, "PE4"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOE4"), + STM32_FUNCTION(1, "TRACED1"), + STM32_FUNCTION(3, "SAI1_D2"), +- STM32_FUNCTION(4, "DFSDM_DATA3"), ++ STM32_FUNCTION(4, "DFSDM1_DATIN3"), + STM32_FUNCTION(5, "TIM15_CH1N"), + STM32_FUNCTION(6, "SPI4_NSS"), + STM32_FUNCTION(7, "SAI1_FS_A"), +- STM32_FUNCTION(8, "SDMMC2_CKIN SDMMC_BOOT2_CKIN"), +- STM32_FUNCTION(9, "SDMMC1_CKIN SDMMC_BOOT1_CKIN"), +- STM32_FUNCTION(10, "SDMMC2_D4 SDMMC_BOOT2_D4"), +- STM32_FUNCTION(12, "SDMMC1_D4 SDMMC_BOOT1_D4"), ++ STM32_FUNCTION(8, "SDMMC2_CKIN"), ++ STM32_FUNCTION(9, "SDMMC1_CKIN"), ++ STM32_FUNCTION(10, "SDMMC2_D4"), ++ STM32_FUNCTION(12, "SDMMC1_D4"), + STM32_FUNCTION(13, "FMC_A20"), + STM32_FUNCTION(14, "DCMI_D4"), + STM32_FUNCTION(15, "LCD_B0"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(69, "PE5"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOE5"), + STM32_FUNCTION(1, "TRACED3"), + STM32_FUNCTION(3, "SAI1_CK2"), +- STM32_FUNCTION(4, "DFSDM_CK3"), ++ STM32_FUNCTION(4, "DFSDM1_CKIN3"), + STM32_FUNCTION(5, "TIM15_CH1"), + STM32_FUNCTION(6, "SPI4_MISO"), + STM32_FUNCTION(7, "SAI1_SCK_A"), +- STM32_FUNCTION(8, "SDMMC2_D0DIR SDMMC_BOOT2_D0DIR"), +- STM32_FUNCTION(9, "SDMMC1_D0DIR SDMMC_BOOT1_D0DIR"), +- STM32_FUNCTION(10, "SDMMC2_D6 SDMMC_BOOT2_D6"), +- STM32_FUNCTION(12, "SDMMC1_D6 SDMMC_BOOT1_D6"), ++ STM32_FUNCTION(8, "SDMMC2_D0DIR"), ++ STM32_FUNCTION(9, "SDMMC1_D0DIR"), ++ STM32_FUNCTION(10, "SDMMC2_D6"), ++ STM32_FUNCTION(12, "SDMMC1_D6"), + STM32_FUNCTION(13, "FMC_A21"), + STM32_FUNCTION(14, "DCMI_D6"), + STM32_FUNCTION(15, "LCD_G0"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(70, "PE6"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOE6"), + STM32_FUNCTION(1, "TRACED2"), + STM32_FUNCTION(2, "TIM1_BKIN2"), +@@ -1030,7 +1093,7 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(6, "SPI4_MOSI"), + STM32_FUNCTION(7, "SAI1_SD_A"), + STM32_FUNCTION(8, "SDMMC2_D0"), +- STM32_FUNCTION(9, "SDMMC1_D2 SDMMC_BOOT1_D2"), ++ STM32_FUNCTION(9, "SDMMC1_D2"), + STM32_FUNCTION(11, "SAI2_MCLK_B"), + STM32_FUNCTION(13, "FMC_A22"), + STM32_FUNCTION(14, "DCMI_D7"), +@@ -1038,119 +1101,132 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(71, "PE7"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOE7"), + STM32_FUNCTION(2, "TIM1_ETR"), + STM32_FUNCTION(3, "TIM3_ETR"), +- STM32_FUNCTION(4, "DFSDM_DATA2"), ++ STM32_FUNCTION(4, "DFSDM1_DATIN2"), + STM32_FUNCTION(8, "UART7_RX"), +- STM32_FUNCTION(11, "QUADSPI_BK2_IO0 QUADSPI_BOOTBK2_IO0"), +- STM32_FUNCTION(13, "FMC_D4"), ++ STM32_FUNCTION(11, "QUADSPI_BK2_IO0"), ++ STM32_FUNCTION(13, "FMC_D4 FMC_DA4"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(72, "PE8"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOE8"), + STM32_FUNCTION(2, "TIM1_CH1N"), +- STM32_FUNCTION(4, "DFSDM_CK2"), ++ STM32_FUNCTION(4, "DFSDM1_CKIN2"), + STM32_FUNCTION(8, "UART7_TX"), +- STM32_FUNCTION(11, "QUADSPI_BK2_IO1 QUADSPI_BOOTBK2_IO1"), +- STM32_FUNCTION(13, "FMC_D5"), ++ STM32_FUNCTION(11, "QUADSPI_BK2_IO1"), ++ STM32_FUNCTION(13, "FMC_D5 FMC_DA5"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(73, "PE9"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOE9"), + STM32_FUNCTION(2, "TIM1_CH1"), +- STM32_FUNCTION(4, "DFSDM_CKOUT"), +- STM32_FUNCTION(8, "UART7_RTS UART_BOOT7_RTS"), +- STM32_FUNCTION(11, "QUADSPI_BK2_IO2 QUADSPI_BOOTBK2_IO2"), +- STM32_FUNCTION(13, "FMC_D6"), ++ STM32_FUNCTION(4, "DFSDM1_CKOUT"), ++ STM32_FUNCTION(8, "UART7_RTS UART7_DE"), ++ STM32_FUNCTION(11, "QUADSPI_BK2_IO2"), ++ STM32_FUNCTION(13, "FMC_D6 FMC_DA6"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(74, "PE10"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOE10"), + STM32_FUNCTION(2, "TIM1_CH2N"), +- STM32_FUNCTION(4, "DFSDM_DATA4"), +- STM32_FUNCTION(8, "UART7_CTS UART_BOOT7_CTS"), +- STM32_FUNCTION(11, "QUADSPI_BK2_IO3 QUADSPI_BOOTBK2_IO3"), +- STM32_FUNCTION(13, "FMC_D7"), ++ STM32_FUNCTION(4, "DFSDM1_DATIN4"), ++ STM32_FUNCTION(8, "UART7_CTS"), ++ STM32_FUNCTION(11, "QUADSPI_BK2_IO3"), ++ STM32_FUNCTION(13, "FMC_D7 FMC_DA7"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(75, "PE11"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOE11"), + STM32_FUNCTION(2, "TIM1_CH2"), +- STM32_FUNCTION(4, "DFSDM_CK4"), ++ STM32_FUNCTION(4, "DFSDM1_CKIN4"), + STM32_FUNCTION(6, "SPI4_NSS"), +- STM32_FUNCTION(8, "USART6_CK USART_BOOT6_CK"), ++ STM32_FUNCTION(8, "USART6_CK"), + STM32_FUNCTION(11, "SAI2_SD_B"), +- STM32_FUNCTION(13, "FMC_D8"), ++ STM32_FUNCTION(13, "FMC_D8 FMC_DA8"), ++ STM32_FUNCTION(14, "DCMI_D4"), + STM32_FUNCTION(15, "LCD_G3"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(76, "PE12"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOE12"), + STM32_FUNCTION(2, "TIM1_CH3N"), +- STM32_FUNCTION(4, "DFSDM_DATA5"), ++ STM32_FUNCTION(4, "DFSDM1_DATIN5"), + STM32_FUNCTION(6, "SPI4_SCK"), +- STM32_FUNCTION(9, "SDMMC1_D0DIR SDMMC_BOOT1_D0DIR"), ++ STM32_FUNCTION(9, "SDMMC1_D0DIR"), + STM32_FUNCTION(11, "SAI2_SCK_B"), +- STM32_FUNCTION(13, "FMC_D9"), ++ STM32_FUNCTION(13, "FMC_D9 FMC_DA9"), + STM32_FUNCTION(15, "LCD_B4"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(77, "PE13"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOE13"), + STM32_FUNCTION(1, "HDP2"), + STM32_FUNCTION(2, "TIM1_CH3"), +- STM32_FUNCTION(4, "DFSDM_CK5"), ++ STM32_FUNCTION(4, "DFSDM1_CKIN5"), + STM32_FUNCTION(6, "SPI4_MISO"), + STM32_FUNCTION(11, "SAI2_FS_B"), +- STM32_FUNCTION(13, "FMC_D10"), ++ STM32_FUNCTION(13, "FMC_D10 FMC_DA10"), ++ STM32_FUNCTION(14, "DCMI_D6"), + STM32_FUNCTION(15, "LCD_DE"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(78, "PE14"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOE14"), + STM32_FUNCTION(2, "TIM1_CH4"), + STM32_FUNCTION(6, "SPI4_MOSI"), +- STM32_FUNCTION(9, "UART8_RTS UART_BOOT8_RTS"), ++ STM32_FUNCTION(9, "UART8_RTS UART8_DE"), + STM32_FUNCTION(11, "SAI2_MCLK_B"), +- STM32_FUNCTION(12, "SDMMC1_D123DIR SDMMC_BOOT1_D123DIR"), +- STM32_FUNCTION(13, "FMC_D11"), ++ STM32_FUNCTION(12, "SDMMC1_D123DIR"), ++ STM32_FUNCTION(13, "FMC_D11 FMC_DA11"), + STM32_FUNCTION(14, "LCD_G0"), + STM32_FUNCTION(15, "LCD_CLK"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(79, "PE15"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOE15"), + STM32_FUNCTION(1, "HDP3"), + STM32_FUNCTION(2, "TIM1_BKIN"), + STM32_FUNCTION(5, "TIM15_BKIN"), +- STM32_FUNCTION(8, "USART2_CTS_NSS USART_BOOT2_CTS_NSS"), +- STM32_FUNCTION(9, "UART8_CTS UART_BOOT8_CTS"), +- STM32_FUNCTION(13, "FMC_D12"), ++ STM32_FUNCTION(8, "USART2_CTS USART2_NSS"), ++ STM32_FUNCTION(9, "UART8_CTS"), ++ STM32_FUNCTION(11, "FMC_NCE2"), ++ STM32_FUNCTION(13, "FMC_D12 FMC_DA12"), + STM32_FUNCTION(15, "LCD_R7"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(80, "PF0"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOF0"), + STM32_FUNCTION(5, "I2C2_SDA"), + STM32_FUNCTION(10, "SDMMC3_D0"), +@@ -1159,8 +1235,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(81, "PF1"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOF1"), + STM32_FUNCTION(5, "I2C2_SCL"), + STM32_FUNCTION(10, "SDMMC3_CMD"), +@@ -1169,27 +1246,30 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(82, "PF2"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOF2"), + STM32_FUNCTION(5, "I2C2_SMBA"), +- STM32_FUNCTION(10, "SDMMC2_D0DIR SDMMC_BOOT2_D0DIR"), ++ STM32_FUNCTION(10, "SDMMC2_D0DIR"), + STM32_FUNCTION(11, "SDMMC3_D0DIR"), +- STM32_FUNCTION(12, "SDMMC1_D0DIR SDMMC_BOOT1_D0DIR"), ++ STM32_FUNCTION(12, "SDMMC1_D0DIR"), + STM32_FUNCTION(13, "FMC_A2"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(83, "PF3"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOF3"), +- STM32_FUNCTION(12, "ETH_GMII_TX_ER ETH_MII_TX_ER"), ++ STM32_FUNCTION(12, "ETH1_GMII_TX_ER ETH1_MII_TX_ER"), + STM32_FUNCTION(13, "FMC_A3"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(84, "PF4"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOF4"), + STM32_FUNCTION(8, "USART2_RX"), + STM32_FUNCTION(10, "SDMMC3_D1"), +@@ -1198,8 +1278,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(85, "PF5"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOF5"), + STM32_FUNCTION(8, "USART2_TX"), + STM32_FUNCTION(10, "SDMMC3_D2"), +@@ -1207,71 +1288,77 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(86, "PF6"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOF6"), + STM32_FUNCTION(2, "TIM16_CH1"), + STM32_FUNCTION(6, "SPI5_NSS"), + STM32_FUNCTION(7, "SAI1_SD_B"), +- STM32_FUNCTION(8, "UART7_RX UART_BOOT7_RX"), +- STM32_FUNCTION(10, "QUADSPI_BK1_IO3 QUADSPI_BOOTBK1_IO3"), ++ STM32_FUNCTION(8, "UART7_RX"), ++ STM32_FUNCTION(10, "QUADSPI_BK1_IO3"), + STM32_FUNCTION(13, "SAI4_SCK_B"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(87, "PF7"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOF7"), + STM32_FUNCTION(2, "TIM17_CH1"), + STM32_FUNCTION(6, "SPI5_SCK"), + STM32_FUNCTION(7, "SAI1_MCLK_B"), +- STM32_FUNCTION(8, "UART7_TX UART_BOOT7_TX"), +- STM32_FUNCTION(10, "QUADSPI_BK1_IO2 QUADSPI_BOOTBK1_IO2"), ++ STM32_FUNCTION(8, "UART7_TX"), ++ STM32_FUNCTION(10, "QUADSPI_BK1_IO2"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(88, "PF8"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOF8"), + STM32_FUNCTION(1, "TRACED12"), + STM32_FUNCTION(2, "TIM16_CH1N"), + STM32_FUNCTION(6, "SPI5_MISO"), + STM32_FUNCTION(7, "SAI1_SCK_B"), +- STM32_FUNCTION(8, "UART7_RTS UART_BOOT7_RTS"), ++ STM32_FUNCTION(8, "UART7_RTS UART7_DE"), + STM32_FUNCTION(10, "TIM13_CH1"), +- STM32_FUNCTION(11, "QUADSPI_BK1_IO0 QUADSPI_BOOTBK1_IO0"), ++ STM32_FUNCTION(11, "QUADSPI_BK1_IO0"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(89, "PF9"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOF9"), + STM32_FUNCTION(1, "TRACED13"), + STM32_FUNCTION(2, "TIM17_CH1N"), + STM32_FUNCTION(6, "SPI5_MOSI"), + STM32_FUNCTION(7, "SAI1_FS_B"), +- STM32_FUNCTION(8, "UART7_CTS UART_BOOT7_CTS"), ++ STM32_FUNCTION(8, "UART7_CTS"), + STM32_FUNCTION(10, "TIM14_CH1"), +- STM32_FUNCTION(11, "QUADSPI_BK1_IO1 QUADSPI_BOOTBK1_IO1"), ++ STM32_FUNCTION(11, "QUADSPI_BK1_IO1"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(90, "PF10"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOF10"), + STM32_FUNCTION(2, "TIM16_BKIN"), + STM32_FUNCTION(3, "SAI1_D3"), + STM32_FUNCTION(4, "SAI4_D4"), + STM32_FUNCTION(7, "SAI1_D4"), +- STM32_FUNCTION(10, "QUADSPI_CLK QUADSPI_BOOTCLK"), ++ STM32_FUNCTION(10, "QUADSPI_CLK"), + STM32_FUNCTION(13, "SAI4_D3"), + STM32_FUNCTION(14, "DCMI_D11"), + STM32_FUNCTION(15, "LCD_DE"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(91, "PF11"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOF11"), + STM32_FUNCTION(6, "SPI5_MOSI"), + STM32_FUNCTION(11, "SAI2_SD_B"), +@@ -1280,138 +1367,151 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(92, "PF12"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOF12"), + STM32_FUNCTION(1, "TRACED4"), +- STM32_FUNCTION(12, "ETH_GMII_RXD4"), ++ STM32_FUNCTION(12, "ETH1_GMII_RXD4"), + STM32_FUNCTION(13, "FMC_A6"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(93, "PF13"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOF13"), + STM32_FUNCTION(1, "TRACED5"), +- STM32_FUNCTION(4, "DFSDM_DATA6"), ++ STM32_FUNCTION(4, "DFSDM1_DATIN6"), + STM32_FUNCTION(5, "I2C4_SMBA"), + STM32_FUNCTION(6, "I2C1_SMBA"), +- STM32_FUNCTION(7, "DFSDM_DATA3"), +- STM32_FUNCTION(12, "ETH_GMII_RXD5"), ++ STM32_FUNCTION(7, "DFSDM1_DATIN3"), ++ STM32_FUNCTION(12, "ETH1_GMII_RXD5"), + STM32_FUNCTION(13, "FMC_A7"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(94, "PF14"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOF14"), + STM32_FUNCTION(1, "TRACED6"), +- STM32_FUNCTION(4, "DFSDM_CK6"), ++ STM32_FUNCTION(4, "DFSDM1_CKIN6"), + STM32_FUNCTION(5, "I2C4_SCL"), + STM32_FUNCTION(6, "I2C1_SCL"), +- STM32_FUNCTION(12, "ETH_GMII_RXD6"), ++ STM32_FUNCTION(12, "ETH1_GMII_RXD6"), + STM32_FUNCTION(13, "FMC_A8"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(95, "PF15"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOF15"), + STM32_FUNCTION(1, "TRACED7"), + STM32_FUNCTION(5, "I2C4_SDA"), + STM32_FUNCTION(6, "I2C1_SDA"), +- STM32_FUNCTION(12, "ETH_GMII_RXD7"), ++ STM32_FUNCTION(12, "ETH1_GMII_RXD7"), + STM32_FUNCTION(13, "FMC_A9"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(96, "PG0"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOG0"), + STM32_FUNCTION(1, "TRACED0"), +- STM32_FUNCTION(4, "DFSDM_DATA0"), +- STM32_FUNCTION(12, "ETH_GMII_TXD4"), ++ STM32_FUNCTION(4, "DFSDM1_DATIN0"), ++ STM32_FUNCTION(12, "ETH1_GMII_TXD4"), + STM32_FUNCTION(13, "FMC_A10"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(97, "PG1"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOG1"), + STM32_FUNCTION(1, "TRACED1"), +- STM32_FUNCTION(12, "ETH_GMII_TXD5"), ++ STM32_FUNCTION(12, "ETH1_GMII_TXD5"), + STM32_FUNCTION(13, "FMC_A11"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(98, "PG2"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOG2"), + STM32_FUNCTION(1, "TRACED2"), + STM32_FUNCTION(2, "MCO2"), + STM32_FUNCTION(4, "TIM8_BKIN"), +- STM32_FUNCTION(12, "ETH_GMII_TXD6"), ++ STM32_FUNCTION(12, "ETH1_GMII_TXD6"), + STM32_FUNCTION(13, "FMC_A12"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(99, "PG3"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOG3"), + STM32_FUNCTION(1, "TRACED3"), + STM32_FUNCTION(4, "TIM8_BKIN2"), +- STM32_FUNCTION(5, "DFSDM_CK1"), +- STM32_FUNCTION(12, "ETH_GMII_TXD7"), ++ STM32_FUNCTION(5, "DFSDM1_CKIN1"), ++ STM32_FUNCTION(12, "ETH1_GMII_TXD7"), + STM32_FUNCTION(13, "FMC_A13"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(100, "PG4"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOG4"), + STM32_FUNCTION(2, "TIM1_BKIN2"), +- STM32_FUNCTION(12, "ETH_GMII_GTX_CLK ETH_RGMII_GTX_CLK"), ++ STM32_FUNCTION(12, "ETH1_GMII_GTX_CLK ETH1_RGMII_GTX_CLK"), + STM32_FUNCTION(13, "FMC_A14"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(101, "PG5"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOG5"), + STM32_FUNCTION(2, "TIM1_ETR"), +- STM32_FUNCTION(12, "ETH_GMII_CLK125 ETH_RGMII_CLK125"), ++ STM32_FUNCTION(12, "ETH1_GMII_CLK125 ETH1_RGMII_CLK125"), + STM32_FUNCTION(13, "FMC_A15"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(102, "PG6"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOG6"), + STM32_FUNCTION(1, "TRACED14"), + STM32_FUNCTION(2, "TIM17_BKIN"), +- STM32_FUNCTION(11, "SDMMC2_CMD SDMMC_BOOT2_CMD"), ++ STM32_FUNCTION(11, "SDMMC2_CMD"), + STM32_FUNCTION(14, "DCMI_D12"), + STM32_FUNCTION(15, "LCD_R7"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(103, "PG7"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOG7"), + STM32_FUNCTION(1, "TRACED5"), + STM32_FUNCTION(7, "SAI1_MCLK_A"), +- STM32_FUNCTION(8, "USART6_CK USART_BOOT6_CK"), +- STM32_FUNCTION(9, "UART8_RTS UART_BOOT8_RTS"), ++ STM32_FUNCTION(8, "USART6_CK"), ++ STM32_FUNCTION(9, "UART8_RTS UART8_DE"), + STM32_FUNCTION(10, "QUADSPI_CLK"), +- STM32_FUNCTION(12, "QUADSPI_BK2_IO3 QUADSPI_BOOTBK2_IO3"), ++ STM32_FUNCTION(12, "QUADSPI_BK2_IO3"), + STM32_FUNCTION(13, "FMC_INT"), + STM32_FUNCTION(14, "DCMI_D13"), + STM32_FUNCTION(15, "LCD_CLK"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(104, "PG8"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOG8"), + STM32_FUNCTION(1, "TRACED15"), + STM32_FUNCTION(2, "TIM2_CH1 TIM2_ETR"), +@@ -1419,73 +1519,79 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(4, "TIM8_ETR"), + STM32_FUNCTION(6, "SPI6_NSS"), + STM32_FUNCTION(7, "SAI4_D2"), +- STM32_FUNCTION(8, "USART6_RTS USART_BOOT6_RTS"), +- STM32_FUNCTION(9, "USART3_RTS"), +- STM32_FUNCTION(10, "SPDIF_IN2"), ++ STM32_FUNCTION(8, "USART6_RTS USART6_DE"), ++ STM32_FUNCTION(9, "USART3_RTS USART3_DE"), ++ STM32_FUNCTION(10, "SPDIFRX_IN2"), + STM32_FUNCTION(11, "SAI4_FS_A"), +- STM32_FUNCTION(12, "ETH_PPS_OUT"), ++ STM32_FUNCTION(12, "ETH1_PPS_OUT"), + STM32_FUNCTION(15, "LCD_G7"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(105, "PG9"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOG9"), + STM32_FUNCTION(1, "DBTRGO"), + STM32_FUNCTION(8, "USART6_RX"), +- STM32_FUNCTION(9, "SPDIF_IN3"), +- STM32_FUNCTION(10, "QUADSPI_BK2_IO2 QUADSPI_BOOTBK2_IO2"), ++ STM32_FUNCTION(9, "SPDIFRX_IN3"), ++ STM32_FUNCTION(10, "QUADSPI_BK2_IO2"), + STM32_FUNCTION(11, "SAI2_FS_B"), +- STM32_FUNCTION(13, "FMC_NE2 FMC_NCE"), ++ STM32_FUNCTION(13, "FMC_NCE FMC_NE2"), + STM32_FUNCTION(14, "DCMI_VSYNC"), ++ STM32_FUNCTION(15, "LCD_R1"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(106, "PG10"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOG10"), + STM32_FUNCTION(1, "TRACED10"), +- STM32_FUNCTION(9, "UART8_CTS UART_BOOT8_CTS"), ++ STM32_FUNCTION(9, "UART8_CTS"), + STM32_FUNCTION(10, "LCD_G3"), + STM32_FUNCTION(11, "SAI2_SD_B"), +- STM32_FUNCTION(12, "QUADSPI_BK2_IO2 QUADSPI_BOOTBK2_IO2"), ++ STM32_FUNCTION(12, "QUADSPI_BK2_IO2"), + STM32_FUNCTION(13, "FMC_NE3"), + STM32_FUNCTION(14, "DCMI_D2"), + STM32_FUNCTION(15, "LCD_B2"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(107, "PG11"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOG11"), + STM32_FUNCTION(1, "TRACED11"), + STM32_FUNCTION(5, "USART1_TX"), +- STM32_FUNCTION(7, "UART4_TX UART_BOOT4_TX"), +- STM32_FUNCTION(9, "SPDIF_IN0"), +- STM32_FUNCTION(12, "ETH_GMII_TX_EN ETH_MII_TX_EN ETH_RGMII_TX_CTL ETH_RMII_TX_EN"), ++ STM32_FUNCTION(7, "UART4_TX"), ++ STM32_FUNCTION(9, "SPDIFRX_IN0"), ++ STM32_FUNCTION(12, "ETH1_GMII_TX_EN ETH1_MII_TX_EN ETH1_RGMII_TX_CTL ETH1_RMII_TX_EN"), + STM32_FUNCTION(14, "DCMI_D3"), + STM32_FUNCTION(15, "LCD_B3"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(108, "PG12"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOG12"), + STM32_FUNCTION(2, "LPTIM1_IN1"), + STM32_FUNCTION(6, "SPI6_MISO"), + STM32_FUNCTION(7, "SAI4_CK2"), +- STM32_FUNCTION(8, "USART6_RTS USART_BOOT6_RTS"), +- STM32_FUNCTION(9, "SPDIF_IN1"), ++ STM32_FUNCTION(8, "USART6_RTS USART6_DE"), ++ STM32_FUNCTION(9, "SPDIFRX_IN1"), + STM32_FUNCTION(10, "LCD_B4"), + STM32_FUNCTION(11, "SAI4_SCK_A"), +- STM32_FUNCTION(12, "ETH_PHY_INTN"), ++ STM32_FUNCTION(12, "ETH1_PHY_INTN"), + STM32_FUNCTION(13, "FMC_NE4"), + STM32_FUNCTION(15, "LCD_B1"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(109, "PG13"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOG13"), + STM32_FUNCTION(1, "TRACED0"), + STM32_FUNCTION(2, "LPTIM1_OUT"), +@@ -1493,79 +1599,86 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(5, "SAI4_CK1"), + STM32_FUNCTION(6, "SPI6_SCK"), + STM32_FUNCTION(7, "SAI1_SCK_A"), +- STM32_FUNCTION(8, "USART6_CTS_NSS USART_BOOT6_CTS_NSS"), ++ STM32_FUNCTION(8, "USART6_CTS USART6_NSS"), + STM32_FUNCTION(11, "SAI4_MCLK_A"), +- STM32_FUNCTION(12, "ETH_GMII_TXD0 ETH_MII_TXD0 ETH_RGMII_TXD0 ETH_RMII_TXD0"), ++ STM32_FUNCTION(12, "ETH1_GMII_TXD0 ETH1_MII_TXD0 ETH1_RGMII_TXD0 ETH1_RMII_TXD0"), + STM32_FUNCTION(13, "FMC_A24"), + STM32_FUNCTION(15, "LCD_R0"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(110, "PG14"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOG14"), + STM32_FUNCTION(1, "TRACED1"), + STM32_FUNCTION(2, "LPTIM1_ETR"), + STM32_FUNCTION(6, "SPI6_MOSI"), + STM32_FUNCTION(7, "SAI4_D1"), + STM32_FUNCTION(8, "USART6_TX"), +- STM32_FUNCTION(10, "QUADSPI_BK2_IO3 QUADSPI_BOOTBK2_IO3"), ++ STM32_FUNCTION(10, "QUADSPI_BK2_IO3"), + STM32_FUNCTION(11, "SAI4_SD_A"), +- STM32_FUNCTION(12, "ETH_GMII_TXD1 ETH_MII_TXD1 ETH_RGMII_TXD1 ETH_RMII_TXD1"), ++ STM32_FUNCTION(12, "ETH1_GMII_TXD1 ETH1_MII_TXD1 ETH1_RGMII_TXD1 ETH1_RMII_TXD1"), + STM32_FUNCTION(13, "FMC_A25"), + STM32_FUNCTION(15, "LCD_B0"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(111, "PG15"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOG15"), + STM32_FUNCTION(1, "TRACED7"), + STM32_FUNCTION(3, "SAI1_D2"), + STM32_FUNCTION(5, "I2C2_SDA"), + STM32_FUNCTION(7, "SAI1_FS_A"), +- STM32_FUNCTION(8, "USART6_CTS_NSS USART_BOOT6_CTS_NSS"), ++ STM32_FUNCTION(8, "USART6_CTS USART6_NSS"), + STM32_FUNCTION(11, "SDMMC3_CK"), + STM32_FUNCTION(14, "DCMI_D13"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(112, "PH0"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOH0"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(113, "PH1"), ++ STM32MP157CAA | STM32MP157CAC | STM32MP157CAB | STM32MP157CAD, + STM32_FUNCTION(0, "GPIOH1"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(114, "PH2"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOH2"), + STM32_FUNCTION(2, "LPTIM1_IN2"), +- STM32_FUNCTION(10, "QUADSPI_BK2_IO0 QUADSPI_BOOTBK2_IO0"), ++ STM32_FUNCTION(10, "QUADSPI_BK2_IO0"), + STM32_FUNCTION(11, "SAI2_SCK_B"), +- STM32_FUNCTION(12, "ETH_GMII_CRS ETH_MII_CRS"), ++ STM32_FUNCTION(12, "ETH1_GMII_CRS ETH1_MII_CRS"), + STM32_FUNCTION(15, "LCD_R0"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(115, "PH3"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOH3"), +- STM32_FUNCTION(4, "DFSDM_CK4"), +- STM32_FUNCTION(10, "QUADSPI_BK2_IO1 QUADSPI_BOOTBK2_IO1"), ++ STM32_FUNCTION(4, "DFSDM1_CKIN4"), ++ STM32_FUNCTION(10, "QUADSPI_BK2_IO1"), + STM32_FUNCTION(11, "SAI2_MCLK_B"), +- STM32_FUNCTION(12, "ETH_GMII_COL ETH_MII_COL"), ++ STM32_FUNCTION(12, "ETH1_GMII_COL ETH1_MII_COL"), + STM32_FUNCTION(15, "LCD_R1"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(116, "PH4"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOH4"), + STM32_FUNCTION(5, "I2C2_SCL"), + STM32_FUNCTION(10, "LCD_G5"), +@@ -1573,8 +1686,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(117, "PH5"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOH5"), + STM32_FUNCTION(5, "I2C2_SDA"), + STM32_FUNCTION(6, "SPI5_NSS"), +@@ -1582,31 +1696,34 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(118, "PH6"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOH6"), + STM32_FUNCTION(3, "TIM12_CH1"), + STM32_FUNCTION(5, "I2C2_SMBA"), + STM32_FUNCTION(6, "SPI5_SCK"), +- STM32_FUNCTION(12, "ETH_GMII_RXD2 ETH_MII_RXD2 ETH_RGMII_RXD2"), ++ STM32_FUNCTION(12, "ETH1_GMII_RXD2 ETH1_MII_RXD2 ETH1_RGMII_RXD2"), + STM32_FUNCTION(13, "MDIOS_MDIO"), + STM32_FUNCTION(14, "DCMI_D8"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(119, "PH7"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOH7"), + STM32_FUNCTION(5, "I2C3_SCL"), + STM32_FUNCTION(6, "SPI5_MISO"), +- STM32_FUNCTION(12, "ETH_GMII_RXD3 ETH_MII_RXD3 ETH_RGMII_RXD3"), ++ STM32_FUNCTION(12, "ETH1_GMII_RXD3 ETH1_MII_RXD3 ETH1_RGMII_RXD3"), + STM32_FUNCTION(13, "MDIOS_MDC"), + STM32_FUNCTION(14, "DCMI_D9"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(120, "PH8"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOH8"), + STM32_FUNCTION(3, "TIM5_ETR"), + STM32_FUNCTION(5, "I2C3_SDA"), +@@ -1615,8 +1732,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(121, "PH9"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOH9"), + STM32_FUNCTION(3, "TIM12_CH2"), + STM32_FUNCTION(5, "I2C3_SMBA"), +@@ -1625,8 +1743,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(122, "PH10"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOH10"), + STM32_FUNCTION(3, "TIM5_CH1"), + STM32_FUNCTION(5, "I2C4_SMBA"), +@@ -1636,8 +1755,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(123, "PH11"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOH11"), + STM32_FUNCTION(3, "TIM5_CH2"), + STM32_FUNCTION(5, "I2C4_SCL"), +@@ -1647,8 +1767,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(124, "PH12"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOH12"), + STM32_FUNCTION(1, "HDP2"), + STM32_FUNCTION(3, "TIM5_CH3"), +@@ -1659,50 +1780,53 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(125, "PH13"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOH13"), + STM32_FUNCTION(4, "TIM8_CH1N"), + STM32_FUNCTION(9, "UART4_TX"), +- STM32_FUNCTION(10, "CAN1_TX"), ++ STM32_FUNCTION(10, "FDCAN1_TX"), + STM32_FUNCTION(15, "LCD_G2"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(126, "PH14"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOH14"), + STM32_FUNCTION(4, "TIM8_CH2N"), + STM32_FUNCTION(9, "UART4_RX"), +- STM32_FUNCTION(10, "CAN1_RX"), ++ STM32_FUNCTION(10, "FDCAN1_RX"), + STM32_FUNCTION(14, "DCMI_D4"), + STM32_FUNCTION(15, "LCD_G3"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(127, "PH15"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOH15"), + STM32_FUNCTION(4, "TIM8_CH3N"), +- STM32_FUNCTION(10, "CAN1_TXFD"), + STM32_FUNCTION(14, "DCMI_D11"), + STM32_FUNCTION(15, "LCD_G4"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(128, "PI0"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOI0"), + STM32_FUNCTION(3, "TIM5_CH4"), + STM32_FUNCTION(6, "SPI2_NSS I2S2_WS"), +- STM32_FUNCTION(10, "CAN1_RXFD"), + STM32_FUNCTION(14, "DCMI_D13"), + STM32_FUNCTION(15, "LCD_G5"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(129, "PI1"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOI1"), + STM32_FUNCTION(4, "TIM8_BKIN2"), + STM32_FUNCTION(6, "SPI2_SCK I2S2_CK"), +@@ -1711,8 +1835,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(130, "PI2"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOI2"), + STM32_FUNCTION(4, "TIM8_CH4"), + STM32_FUNCTION(6, "SPI2_MISO I2S2_SDI"), +@@ -1721,8 +1846,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(131, "PI3"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOI3"), + STM32_FUNCTION(4, "TIM8_ETR"), + STM32_FUNCTION(6, "SPI2_MOSI I2S2_SDO"), +@@ -1730,8 +1856,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(132, "PI4"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOI4"), + STM32_FUNCTION(4, "TIM8_BKIN"), + STM32_FUNCTION(11, "SAI2_MCLK_A"), +@@ -1740,8 +1867,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(133, "PI5"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOI5"), + STM32_FUNCTION(4, "TIM8_CH1"), + STM32_FUNCTION(11, "SAI2_SCK_A"), +@@ -1750,8 +1878,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(134, "PI6"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOI6"), + STM32_FUNCTION(4, "TIM8_CH2"), + STM32_FUNCTION(11, "SAI2_SD_A"), +@@ -1760,8 +1889,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(135, "PI7"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOI7"), + STM32_FUNCTION(4, "TIM8_CH3"), + STM32_FUNCTION(11, "SAI2_FS_A"), +@@ -1770,35 +1900,38 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(136, "PI8"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOI8"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(137, "PI9"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOI9"), + STM32_FUNCTION(1, "HDP1"), + STM32_FUNCTION(9, "UART4_RX"), +- STM32_FUNCTION(10, "CAN1_RX"), ++ STM32_FUNCTION(10, "FDCAN1_RX"), + STM32_FUNCTION(15, "LCD_VSYNC"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(138, "PI10"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOI10"), + STM32_FUNCTION(1, "HDP0"), +- STM32_FUNCTION(9, "USART3_CTS_NSS USART_BOOT3_CTS_NSS"), +- STM32_FUNCTION(10, "CAN1_RXFD"), +- STM32_FUNCTION(12, "ETH_GMII_RX_ER ETH_MII_RX_ER"), ++ STM32_FUNCTION(9, "USART3_CTS USART3_NSS"), ++ STM32_FUNCTION(12, "ETH1_GMII_RX_ER ETH1_MII_RX_ER"), + STM32_FUNCTION(15, "LCD_HSYNC"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(139, "PI11"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOI11"), + STM32_FUNCTION(1, "MCO1"), + STM32_FUNCTION(6, "I2S_CKIN"), +@@ -1806,8 +1939,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(140, "PI12"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOI12"), + STM32_FUNCTION(1, "TRACED0"), + STM32_FUNCTION(3, "HDP0"), +@@ -1815,8 +1949,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(141, "PI13"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOI13"), + STM32_FUNCTION(1, "TRACED1"), + STM32_FUNCTION(3, "HDP1"), +@@ -1824,24 +1959,27 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(142, "PI14"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOI14"), + STM32_FUNCTION(1, "TRACECLK"), + STM32_FUNCTION(15, "LCD_CLK"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(143, "PI15"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOI15"), + STM32_FUNCTION(10, "LCD_G2"), + STM32_FUNCTION(15, "LCD_R0"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(144, "PJ0"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOJ0"), + STM32_FUNCTION(1, "TRACED8"), + STM32_FUNCTION(10, "LCD_R7"), +@@ -1849,16 +1987,18 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(145, "PJ1"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOJ1"), + STM32_FUNCTION(1, "TRACED9"), + STM32_FUNCTION(15, "LCD_R2"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(146, "PJ2"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOJ2"), + STM32_FUNCTION(1, "TRACED10"), + STM32_FUNCTION(14, "DSI_TE"), +@@ -1866,24 +2006,27 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(147, "PJ3"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOJ3"), + STM32_FUNCTION(1, "TRACED11"), + STM32_FUNCTION(15, "LCD_R4"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(148, "PJ4"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOJ4"), + STM32_FUNCTION(1, "TRACED12"), + STM32_FUNCTION(15, "LCD_R5"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(149, "PJ5"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOJ5"), + STM32_FUNCTION(1, "TRACED2"), + STM32_FUNCTION(3, "HDP2"), +@@ -1891,8 +2034,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(150, "PJ6"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOJ6"), + STM32_FUNCTION(1, "TRACED3"), + STM32_FUNCTION(3, "HDP3"), +@@ -1901,8 +2045,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(151, "PJ7"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOJ7"), + STM32_FUNCTION(1, "TRACED13"), + STM32_FUNCTION(4, "TIM8_CH2N"), +@@ -1910,8 +2055,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(152, "PJ8"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOJ8"), + STM32_FUNCTION(1, "TRACED14"), + STM32_FUNCTION(2, "TIM1_CH3N"), +@@ -1921,8 +2067,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(153, "PJ9"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOJ9"), + STM32_FUNCTION(1, "TRACED15"), + STM32_FUNCTION(2, "TIM1_CH3"), +@@ -1932,8 +2079,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(154, "PJ10"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOJ10"), + STM32_FUNCTION(2, "TIM1_CH2N"), + STM32_FUNCTION(4, "TIM8_CH2"), +@@ -1942,8 +2090,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(155, "PJ11"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOJ11"), + STM32_FUNCTION(2, "TIM1_CH2"), + STM32_FUNCTION(4, "TIM8_CH2N"), +@@ -1952,38 +2101,43 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(156, "PJ12"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOJ12"), + STM32_FUNCTION(10, "LCD_G3"), + STM32_FUNCTION(15, "LCD_B0"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(157, "PJ13"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOJ13"), + STM32_FUNCTION(10, "LCD_G4"), + STM32_FUNCTION(15, "LCD_B1"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(158, "PJ14"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOJ14"), + STM32_FUNCTION(15, "LCD_B2"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(159, "PJ15"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOJ15"), + STM32_FUNCTION(15, "LCD_B3"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(160, "PK0"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOK0"), + STM32_FUNCTION(2, "TIM1_CH1N"), + STM32_FUNCTION(4, "TIM8_CH3"), +@@ -1992,8 +2146,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(161, "PK1"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOK1"), + STM32_FUNCTION(1, "TRACED4"), + STM32_FUNCTION(2, "TIM1_CH1"), +@@ -2004,8 +2159,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(162, "PK2"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOK2"), + STM32_FUNCTION(1, "TRACED5"), + STM32_FUNCTION(2, "TIM1_BKIN"), +@@ -2015,22 +2171,25 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(163, "PK3"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOK3"), + STM32_FUNCTION(15, "LCD_B4"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(164, "PK4"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOK4"), + STM32_FUNCTION(15, "LCD_B5"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(165, "PK5"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOK5"), + STM32_FUNCTION(1, "TRACED6"), + STM32_FUNCTION(3, "HDP6"), +@@ -2038,8 +2197,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(166, "PK6"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOK6"), + STM32_FUNCTION(1, "TRACED7"), + STM32_FUNCTION(3, "HDP7"), +@@ -2047,8 +2207,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(167, "PK7"), ++ STM32MP157CAA, + STM32_FUNCTION(0, "GPIOK7"), + STM32_FUNCTION(15, "LCD_DE"), + STM32_FUNCTION(16, "EVENTOUT"), +@@ -2057,8 +2218,9 @@ static const struct stm32_desc_pin stm32mp157_pins[] = { + }; + + static const struct stm32_desc_pin stm32mp157_z_pins[] = { +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(400, "PZ0"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOZ0"), + STM32_FUNCTION(3, "I2C6_SCL"), + STM32_FUNCTION(4, "I2C2_SCL"), +@@ -2068,8 +2230,9 @@ static const struct stm32_desc_pin stm32mp157_z_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(401, "PZ1"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOZ1"), + STM32_FUNCTION(3, "I2C6_SDA"), + STM32_FUNCTION(4, "I2C2_SDA"), +@@ -2081,8 +2244,9 @@ static const struct stm32_desc_pin stm32mp157_z_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(402, "PZ2"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOZ2"), + STM32_FUNCTION(3, "I2C6_SCL"), + STM32_FUNCTION(4, "I2C2_SCL"), +@@ -2094,21 +2258,23 @@ static const struct stm32_desc_pin stm32mp157_z_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(403, "PZ3"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOZ3"), + STM32_FUNCTION(3, "I2C6_SDA"), + STM32_FUNCTION(4, "I2C2_SDA"), + STM32_FUNCTION(5, "I2C5_SDA"), + STM32_FUNCTION(6, "SPI1_NSS I2S1_WS"), + STM32_FUNCTION(7, "I2C4_SDA"), +- STM32_FUNCTION(8, "USART1_CTS_NSS"), ++ STM32_FUNCTION(8, "USART1_CTS USART1_NSS"), + STM32_FUNCTION(9, "SPI6_NSS"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(404, "PZ4"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOZ4"), + STM32_FUNCTION(3, "I2C6_SCL"), + STM32_FUNCTION(4, "I2C2_SCL"), +@@ -2117,19 +2283,21 @@ static const struct stm32_desc_pin stm32mp157_z_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(405, "PZ5"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOZ5"), + STM32_FUNCTION(3, "I2C6_SDA"), + STM32_FUNCTION(4, "I2C2_SDA"), + STM32_FUNCTION(5, "I2C5_SDA"), + STM32_FUNCTION(7, "I2C4_SDA"), +- STM32_FUNCTION(8, "USART1_RTS"), ++ STM32_FUNCTION(8, "USART1_RTS USART1_DE"), + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(406, "PZ6"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOZ6"), + STM32_FUNCTION(3, "I2C6_SCL"), + STM32_FUNCTION(4, "I2C2_SCL"), +@@ -2140,8 +2308,9 @@ static const struct stm32_desc_pin stm32mp157_z_pins[] = { + STM32_FUNCTION(16, "EVENTOUT"), + STM32_FUNCTION(17, "ANALOG") + ), +- STM32_PIN( ++ STM32_PIN_PKG( + PINCTRL_PIN(407, "PZ7"), ++ STM32MP157CAA | STM32MP157CAC, + STM32_FUNCTION(0, "GPIOZ7"), + STM32_FUNCTION(3, "I2C6_SDA"), + STM32_FUNCTION(4, "I2C2_SDA"), +@@ -2159,6 +2328,7 @@ static struct stm32_pinctrl_match_data stm32mp157_match_data = { + static struct stm32_pinctrl_match_data stm32mp157_z_match_data = { + .pins = stm32mp157_z_pins, + .npins = ARRAY_SIZE(stm32mp157_z_pins), ++ .pin_base_shift = STM32MP157_Z_BASE_SHIFT, + }; + + static const struct of_device_id stm32mp157_pctrl_match[] = { +@@ -2173,11 +2343,16 @@ static const struct of_device_id stm32mp157_pctrl_match[] = { + { } + }; + ++static const struct dev_pm_ops stm32_pinctrl_dev_pm_ops = { ++ SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, stm32_pinctrl_resume) ++}; ++ + static struct platform_driver stm32mp157_pinctrl_driver = { + .probe = stm32_pctl_probe, + .driver = { + .name = "stm32mp157-pinctrl", + .of_match_table = stm32mp157_pctrl_match, ++ .pm = &stm32_pinctrl_dev_pm_ops, + }, + }; + +diff --git a/drivers/pwm/pwm-stm32-lp.c b/drivers/pwm/pwm-stm32-lp.c +index 0059b24c..322df1f 100644 +--- a/drivers/pwm/pwm-stm32-lp.c ++++ b/drivers/pwm/pwm-stm32-lp.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -20,6 +21,8 @@ struct stm32_pwm_lp { + struct pwm_chip chip; + struct clk *clk; + struct regmap *regmap; ++ struct pwm_state suspend; ++ bool suspended; + }; + + static inline struct stm32_pwm_lp *to_stm32_pwm_lp(struct pwm_chip *chip) +@@ -58,6 +61,12 @@ static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm, + /* Calculate the period and prescaler value */ + div = (unsigned long long)clk_get_rate(priv->clk) * state->period; + do_div(div, NSEC_PER_SEC); ++ if (!div) { ++ /* Fall here in case source clock < period */ ++ dev_err(priv->chip.dev, "Can't reach expected period\n"); ++ return -EINVAL; ++ } ++ + prd = div; + while (div > STM32_LPTIM_MAX_ARR) { + presc++; +@@ -223,6 +232,40 @@ static int stm32_pwm_lp_remove(struct platform_device *pdev) + return pwmchip_remove(&priv->chip); + } + ++#ifdef CONFIG_PM_SLEEP ++static int stm32_pwm_lp_suspend(struct device *dev) ++{ ++ struct stm32_pwm_lp *priv = dev_get_drvdata(dev); ++ ++ pwm_get_state(&priv->chip.pwms[0], &priv->suspend); ++ priv->suspended = priv->suspend.enabled; ++ ++ /* safe to call pwm_disable() for already disabled pwm */ ++ pwm_disable(&priv->chip.pwms[0]); ++ ++ return pinctrl_pm_select_sleep_state(dev); ++} ++ ++static int stm32_pwm_lp_resume(struct device *dev) ++{ ++ struct stm32_pwm_lp *priv = dev_get_drvdata(dev); ++ int ret; ++ ++ ret = pinctrl_pm_select_default_state(dev); ++ if (ret) ++ return ret; ++ ++ /* Only restore suspended pwm, not to disrupt other MFD child */ ++ if (!priv->suspended) ++ return 0; ++ ++ return pwm_apply_state(&priv->chip.pwms[0], &priv->suspend); ++} ++#endif ++ ++static SIMPLE_DEV_PM_OPS(stm32_pwm_lp_pm_ops, stm32_pwm_lp_suspend, ++ stm32_pwm_lp_resume); ++ + static const struct of_device_id stm32_pwm_lp_of_match[] = { + { .compatible = "st,stm32-pwm-lp", }, + {}, +@@ -235,6 +278,7 @@ static struct platform_driver stm32_pwm_lp_driver = { + .driver = { + .name = "stm32-pwm-lp", + .of_match_table = of_match_ptr(stm32_pwm_lp_of_match), ++ .pm = &stm32_pwm_lp_pm_ops, + }, + }; + module_platform_driver(stm32_pwm_lp_driver); +diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c +index 4f84255..5a6c765 100644 +--- a/drivers/pwm/pwm-stm32.c ++++ b/drivers/pwm/pwm-stm32.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -19,6 +20,12 @@ + #define CCMR_CHANNEL_MASK 0xFF + #define MAX_BREAKINPUT 2 + ++struct stm32_breakinput { ++ u32 index; ++ u32 level; ++ u32 filter; ++}; ++ + struct stm32_pwm { + struct pwm_chip chip; + struct mutex lock; /* protect pwm config/enable */ +@@ -26,15 +33,13 @@ struct stm32_pwm { + struct regmap *regmap; + u32 max_arr; + bool have_complementary_output; ++ struct stm32_breakinput breakinput[MAX_BREAKINPUT]; ++ unsigned int nbreakinput; ++ struct pwm_state suspend[4]; ++ bool suspended[4]; + u32 capture[4] ____cacheline_aligned; /* DMA'able buffer */ + }; + +-struct stm32_breakinput { +- u32 index; +- u32 level; +- u32 filter; +-}; +- + static inline struct stm32_pwm *to_stm32_pwm_dev(struct pwm_chip *chip) + { + return container_of(chip, struct stm32_pwm, chip); +@@ -374,9 +379,7 @@ static int stm32_pwm_config(struct stm32_pwm *priv, int ch, + else + regmap_update_bits(priv->regmap, TIM_CCMR2, mask, ccmr); + +- regmap_update_bits(priv->regmap, TIM_BDTR, +- TIM_BDTR_MOE | TIM_BDTR_AOE, +- TIM_BDTR_MOE | TIM_BDTR_AOE); ++ regmap_update_bits(priv->regmap, TIM_BDTR, TIM_BDTR_MOE, TIM_BDTR_MOE); + + return 0; + } +@@ -512,15 +515,27 @@ static int stm32_pwm_set_breakinput(struct stm32_pwm *priv, + return (bdtr & bke) ? 0 : -EINVAL; + } + +-static int stm32_pwm_apply_breakinputs(struct stm32_pwm *priv, ++static int stm32_pwm_apply_breakinputs(struct stm32_pwm *priv) ++{ ++ int i, ret = 0; ++ ++ for (i = 0; i < priv->nbreakinput && !ret; i++) { ++ ret = stm32_pwm_set_breakinput(priv, ++ priv->breakinput[i].index, ++ priv->breakinput[i].level, ++ priv->breakinput[i].filter); ++ } ++ ++ return ret; ++} ++ ++static int stm32_pwm_probe_breakinputs(struct stm32_pwm *priv, + struct device_node *np) + { +- struct stm32_breakinput breakinput[MAX_BREAKINPUT]; +- int nb, ret, i, array_size; ++ int nb, ret, array_size; + + nb = of_property_count_elems_of_size(np, "st,breakinput", + sizeof(struct stm32_breakinput)); +- + /* + * Because "st,breakinput" parameter is optional do not make probe + * failed if it doesn't exist. +@@ -531,20 +546,14 @@ static int stm32_pwm_apply_breakinputs(struct stm32_pwm *priv, + if (nb > MAX_BREAKINPUT) + return -EINVAL; + ++ priv->nbreakinput = nb; + array_size = nb * sizeof(struct stm32_breakinput) / sizeof(u32); + ret = of_property_read_u32_array(np, "st,breakinput", +- (u32 *)breakinput, array_size); ++ (u32 *)priv->breakinput, array_size); + if (ret) + return ret; + +- for (i = 0; i < nb && !ret; i++) { +- ret = stm32_pwm_set_breakinput(priv, +- breakinput[i].index, +- breakinput[i].level, +- breakinput[i].filter); +- } +- +- return ret; ++ return stm32_pwm_apply_breakinputs(priv); + } + + static void stm32_pwm_detect_complementary(struct stm32_pwm *priv) +@@ -612,7 +621,7 @@ static int stm32_pwm_probe(struct platform_device *pdev) + if (!priv->regmap || !priv->clk) + return -EINVAL; + +- ret = stm32_pwm_apply_breakinputs(priv, np); ++ ret = stm32_pwm_probe_breakinputs(priv, np); + if (ret) + return ret; + +@@ -645,6 +654,53 @@ static int stm32_pwm_remove(struct platform_device *pdev) + return 0; + } + ++#ifdef CONFIG_PM_SLEEP ++static int stm32_pwm_suspend(struct device *dev) ++{ ++ struct stm32_pwm *priv = dev_get_drvdata(dev); ++ unsigned int i; ++ ++ for (i = 0; i < priv->chip.npwm; i++) { ++ pwm_get_state(&priv->chip.pwms[i], &priv->suspend[i]); ++ priv->suspended[i] = priv->suspend[i].enabled; ++ ++ /* safe to call pwm_disable() for already disabled pwm */ ++ pwm_disable(&priv->chip.pwms[i]); ++ } ++ ++ return pinctrl_pm_select_sleep_state(dev); ++} ++ ++static int stm32_pwm_resume(struct device *dev) ++{ ++ struct stm32_pwm *priv = dev_get_drvdata(dev); ++ unsigned int i; ++ int ret; ++ ++ ret = pinctrl_pm_select_default_state(dev); ++ if (ret) ++ return ret; ++ ++ ret = stm32_pwm_apply_breakinputs(priv); ++ if (ret) ++ return ret; ++ ++ for (i = 0; i < priv->chip.npwm; i++) { ++ /* Only resume active pwm, not to disrupt other MFD child */ ++ if (!priv->suspended[i]) ++ continue; ++ ++ ret = pwm_apply_state(&priv->chip.pwms[i], &priv->suspend[i]); ++ if (ret) ++ break; ++ } ++ ++ return ret; ++} ++#endif ++ ++static SIMPLE_DEV_PM_OPS(stm32_pwm_pm_ops, stm32_pwm_suspend, stm32_pwm_resume); ++ + static const struct of_device_id stm32_pwm_of_match[] = { + { .compatible = "st,stm32-pwm", }, + { /* end node */ }, +@@ -657,6 +713,7 @@ static struct platform_driver stm32_pwm_driver = { + .driver = { + .name = "stm32-pwm", + .of_match_table = stm32_pwm_of_match, ++ .pm = &stm32_pwm_pm_ops, + }, + }; + module_platform_driver(stm32_pwm_driver); +diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c +index 7c71cdb..ceb233d 100644 +--- a/drivers/pwm/sysfs.c ++++ b/drivers/pwm/sysfs.c +@@ -249,6 +249,7 @@ static void pwm_export_release(struct device *child) + static int pwm_export_child(struct device *parent, struct pwm_device *pwm) + { + struct pwm_export *export; ++ char *pwm_prop[2]; + int ret; + + if (test_and_set_bit(PWMF_EXPORTED, &pwm->flags)) +@@ -263,7 +264,6 @@ static int pwm_export_child(struct device *parent, struct pwm_device *pwm) + export->pwm = pwm; + mutex_init(&export->lock); + +- export->child.class = parent->class; + export->child.release = pwm_export_release; + export->child.parent = parent; + export->child.devt = MKDEV(0, 0); +@@ -277,6 +277,10 @@ static int pwm_export_child(struct device *parent, struct pwm_device *pwm) + export = NULL; + return ret; + } ++ pwm_prop[0] = kasprintf(GFP_KERNEL, "EXPORT=pwm%u", pwm->hwpwm); ++ pwm_prop[1] = NULL; ++ kobject_uevent_env(&parent->kobj, KOBJ_CHANGE, pwm_prop); ++ kfree(pwm_prop[0]); + + return 0; + } +@@ -289,6 +293,7 @@ static int pwm_unexport_match(struct device *child, void *data) + static int pwm_unexport_child(struct device *parent, struct pwm_device *pwm) + { + struct device *child; ++ char *pwm_prop[2]; + + if (!test_and_clear_bit(PWMF_EXPORTED, &pwm->flags)) + return -ENODEV; +@@ -297,6 +302,11 @@ static int pwm_unexport_child(struct device *parent, struct pwm_device *pwm) + if (!child) + return -ENODEV; + ++ pwm_prop[0] = kasprintf(GFP_KERNEL, "UNEXPORT=pwm%u", pwm->hwpwm); ++ pwm_prop[1] = NULL; ++ kobject_uevent_env(&parent->kobj, KOBJ_CHANGE, pwm_prop); ++ kfree(pwm_prop[0]); ++ + /* for device_find_child() */ + put_device(child); + device_unregister(child); +diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig +index 329cdd3..9ecafba 100644 +--- a/drivers/regulator/Kconfig ++++ b/drivers/regulator/Kconfig +@@ -803,6 +803,25 @@ config REGULATOR_STM32_VREFBUF + This driver can also be built as a module. If so, the module + will be called stm32-vrefbuf. + ++config REGULATOR_STM32_PWR ++ bool "STMicroelectronics STM32 PWR" ++ depends on ARCH_STM32 ++ help ++ This driver supports internal regulators (1V1 & 1V8) in the ++ STMicroelectronics STM32 chips. ++ ++config REGULATOR_STPMIC1 ++ tristate "STMicroelectronics STPMIC1 PMIC Regulators" ++ depends on MFD_STPMIC1 ++ help ++ This driver supports STMicroelectronics STPMIC1 PMIC voltage ++ regulators and switches. The STPMIC1 regulators supply power to ++ an application processor as well as to external system ++ peripherals such as DDR, Flash memories and system devices. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called stpmic1_regulator. ++ + config REGULATOR_TI_ABB + tristate "TI Adaptive Body Bias on-chip LDO" + depends on ARCH_OMAP +diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile +index 801d9a3..3506ec2 100644 +--- a/drivers/regulator/Makefile ++++ b/drivers/regulator/Makefile +@@ -101,6 +101,8 @@ obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o + obj-$(CONFIG_REGULATOR_SC2731) += sc2731-regulator.o + obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o + obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o ++obj-$(CONFIG_REGULATOR_STM32_PWR) += stm32-pwr.o ++obj-$(CONFIG_REGULATOR_STPMIC1) += stpmic1_regulator.o + obj-$(CONFIG_REGULATOR_STW481X_VMMC) += stw481x-vmmc.o + obj-$(CONFIG_REGULATOR_SY8106A) += sy8106a-regulator.o + obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o +diff --git a/drivers/regulator/stm32-pwr.c b/drivers/regulator/stm32-pwr.c +new file mode 100644 +index 0000000..f4e1198 +--- /dev/null ++++ b/drivers/regulator/stm32-pwr.c +@@ -0,0 +1,242 @@ ++/* ++ * Copyright (C) STMicroelectronics SA 2017 ++ * Author: Gabriel Fernandez ++ * ++ * License terms: GPL V2.0. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see . ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * Registers ++ */ ++#define REG_PWR_CR3 0x0C ++ ++/* ++ * SYSTEM_PARAMETER ++ */ ++#define REG_1_1_EN BIT(30) ++#define REG_1_8_EN BIT(28) ++#define USB_3_3_EN BIT(24) ++ ++#define STM32_SMC_PWR 0x82001001 ++#define STM32_WRITE 0x1 ++#define STM32_SMC_REG_SET 0x2 ++#define STM32_SMC_REG_CLEAR 0x3 ++ ++#define SMC(class, op, address, val)\ ++ ({\ ++ struct arm_smccc_res res;\ ++ arm_smccc_smc(class, op, address, val,\ ++ 0, 0, 0, 0, &res);\ ++ }) ++ ++static int stm32_pwr_secure_regulator_enable(struct regulator_dev *rdev) ++{ ++ SMC(STM32_SMC_PWR, STM32_SMC_REG_SET, rdev->desc->enable_reg, ++ rdev->desc->enable_mask); ++ ++ return 0; ++} ++ ++static int stm32_pwr_secure_regulator_disable(struct regulator_dev *rdev) ++{ ++ SMC(STM32_SMC_PWR, STM32_SMC_REG_CLEAR, rdev->desc->enable_reg, ++ rdev->desc->enable_mask); ++ ++ return 0; ++} ++ ++static const struct regulator_ops stm32_pwr_reg_ops = { ++ .list_voltage = regulator_list_voltage_linear, ++ .enable = regulator_enable_regmap, ++ .disable = regulator_disable_regmap, ++ .is_enabled = regulator_is_enabled_regmap, ++}; ++ ++static const struct regulator_ops stm32_pwr_reg_secure_ops = { ++ .list_voltage = regulator_list_voltage_linear, ++ .enable = stm32_pwr_secure_regulator_enable, ++ .disable = stm32_pwr_secure_regulator_disable, ++ .is_enabled = regulator_is_enabled_regmap, ++}; ++ ++static const struct regulator_desc stm32_pwr_reg11 = { ++ .name = "REG11", ++ .of_match = of_match_ptr("reg11"), ++ .n_voltages = 1, ++ .type = REGULATOR_VOLTAGE, ++ .min_uV = 1100000, ++ .fixed_uV = 1100000, ++ .ops = &stm32_pwr_reg_ops, ++ .enable_reg = REG_PWR_CR3, ++ .enable_mask = REG_1_1_EN, ++ .owner = THIS_MODULE, ++}; ++ ++static const struct regulator_desc stm32_pwr_reg18 = { ++ .name = "REG18", ++ .of_match = of_match_ptr("reg18"), ++ .n_voltages = 1, ++ .type = REGULATOR_VOLTAGE, ++ .min_uV = 1800000, ++ .fixed_uV = 1800000, ++ .ops = &stm32_pwr_reg_ops, ++ .enable_reg = REG_PWR_CR3, ++ .enable_mask = REG_1_8_EN, ++ .owner = THIS_MODULE, ++}; ++ ++static const struct regulator_desc stm32_pwr_usb33 = { ++ .name = "USB33", ++ .of_match = of_match_ptr("usb33"), ++ .n_voltages = 1, ++ .type = REGULATOR_VOLTAGE, ++ .min_uV = 3300000, ++ .fixed_uV = 3300000, ++ .ops = &stm32_pwr_reg_ops, ++ .enable_reg = REG_PWR_CR3, ++ .enable_mask = USB_3_3_EN, ++ .owner = THIS_MODULE, ++}; ++ ++static struct of_regulator_match stm32_pwr_reg_matches[] = { ++ { .name = "reg11", .driver_data = (void *)&stm32_pwr_reg11 }, ++ { .name = "reg18", .driver_data = (void *)&stm32_pwr_reg18 }, ++ { .name = "usb33", .driver_data = (void *)&stm32_pwr_usb33 }, ++}; ++ ++#define STM32PWR_REG_NUM_REGS ARRAY_SIZE(stm32_pwr_reg_matches) ++ ++static int is_stm32_soc_secured(struct platform_device *pdev, int *val) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct regmap *syscon; ++ u32 reg, mask; ++ int tzc_val = 0; ++ int err; ++ ++ syscon = syscon_regmap_lookup_by_phandle(np, "st,tzcr"); ++ if (IS_ERR(syscon)) { ++ dev_err(&pdev->dev, "tzcr syscon required !\n"); ++ return PTR_ERR(syscon); ++ } ++ ++ err = of_property_read_u32_index(np, "st,tzcr", 1, ®); ++ if (err) { ++ dev_err(&pdev->dev, "tzcr offset required !\n"); ++ return err; ++ } ++ ++ err = of_property_read_u32_index(np, "st,tzcr", 2, &mask); ++ if (err) { ++ dev_err(&pdev->dev, "tzcr mask required !\n"); ++ return err; ++ } ++ ++ err = regmap_read(syscon, reg, &tzc_val); ++ if (err) { ++ dev_err(&pdev->dev, "failed to read tzcr status !\n"); ++ return err; ++ } ++ ++ *val = tzc_val & mask; ++ ++ return 0; ++} ++ ++static int stm32_power_regulator_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct regulator_dev *rdev; ++ struct regulator_config config = { }; ++ struct regmap *regmap; ++ struct regulator_desc *desc; ++ int i, ret = 0; ++ int tzen = 0; ++ ++ of_regulator_match(&pdev->dev, np, stm32_pwr_reg_matches, ++ STM32PWR_REG_NUM_REGS); ++ ++ regmap = syscon_node_to_regmap(pdev->dev.parent->of_node); ++ if (IS_ERR(regmap)) ++ return PTR_ERR(regmap); ++ ++ config.regmap = regmap; ++ config.dev = &pdev->dev; ++ ++ ret = is_stm32_soc_secured(pdev, &tzen); ++ if (ret) ++ return ret; ++ ++ for (i = 0; i < STM32PWR_REG_NUM_REGS; i++) { ++ struct of_regulator_match *match = &stm32_pwr_reg_matches[i]; ++ ++ if (!match->init_data || ++ !match->of_node) ++ continue; ++ ++ config.init_data = match->init_data; ++ config.driver_data = match->driver_data; ++ config.of_node = match->of_node; ++ ++ if (tzen) { ++ desc = match->driver_data; ++ desc->ops = &stm32_pwr_reg_secure_ops; ++ } ++ ++ rdev = devm_regulator_register(&pdev->dev, ++ match->driver_data, ++ &config); ++ if (IS_ERR(rdev)) { ++ ret = PTR_ERR(rdev); ++ dev_err(&pdev->dev, ++ "Failed to register regulator: %d\n", ret); ++ break; ++ } ++ } ++ return ret; ++} ++ ++static const struct of_device_id stm32_pwr_reg_of_match[] = { ++ { .compatible = "st,stm32mp1,pwr-reg", }, ++ {}, ++}; ++ ++static struct platform_driver stm32_pwr_reg_driver = { ++ .probe = stm32_power_regulator_probe, ++ .driver = { ++ .name = "stm32-pwr-regulator", ++ .of_match_table = of_match_ptr(stm32_pwr_reg_of_match), ++ }, ++}; ++ ++static int __init stm32_pwr_regulator_init(void) ++{ ++ return platform_driver_register(&stm32_pwr_reg_driver); ++} ++subsys_initcall(stm32_pwr_regulator_init); +diff --git a/drivers/regulator/stm32-vrefbuf.c b/drivers/regulator/stm32-vrefbuf.c +index e0a9c44..29cca32 100644 +--- a/drivers/regulator/stm32-vrefbuf.c ++++ b/drivers/regulator/stm32-vrefbuf.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + + /* STM32 VREFBUF registers */ + #define STM32_VREFBUF_CSR 0x00 +@@ -25,9 +26,12 @@ + #define STM32_HIZ BIT(1) + #define STM32_ENVR BIT(0) + ++#define STM32_VREFBUF_AUTO_SUSPEND_DELAY_MS 10 ++ + struct stm32_vrefbuf { + void __iomem *base; + struct clk *clk; ++ struct device *dev; + }; + + static const unsigned int stm32_vrefbuf_voltages[] = { +@@ -38,9 +42,16 @@ static const unsigned int stm32_vrefbuf_voltages[] = { + static int stm32_vrefbuf_enable(struct regulator_dev *rdev) + { + struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev); +- u32 val = readl_relaxed(priv->base + STM32_VREFBUF_CSR); ++ u32 val; + int ret; + ++ ret = pm_runtime_get_sync(priv->dev); ++ if (ret < 0) { ++ pm_runtime_put_noidle(priv->dev); ++ return ret; ++ } ++ ++ val = readl_relaxed(priv->base + STM32_VREFBUF_CSR); + val = (val & ~STM32_HIZ) | STM32_ENVR; + writel_relaxed(val, priv->base + STM32_VREFBUF_CSR); + +@@ -59,45 +70,95 @@ static int stm32_vrefbuf_enable(struct regulator_dev *rdev) + writel_relaxed(val, priv->base + STM32_VREFBUF_CSR); + } + ++ pm_runtime_mark_last_busy(priv->dev); ++ pm_runtime_put_autosuspend(priv->dev); ++ + return ret; + } + + static int stm32_vrefbuf_disable(struct regulator_dev *rdev) + { + struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev); +- u32 val = readl_relaxed(priv->base + STM32_VREFBUF_CSR); ++ u32 val; ++ int ret; + ++ ret = pm_runtime_get_sync(priv->dev); ++ if (ret < 0) { ++ pm_runtime_put_noidle(priv->dev); ++ return ret; ++ } ++ ++ val = readl_relaxed(priv->base + STM32_VREFBUF_CSR); + val = (val & ~STM32_ENVR) | STM32_HIZ; + writel_relaxed(val, priv->base + STM32_VREFBUF_CSR); + ++ pm_runtime_mark_last_busy(priv->dev); ++ pm_runtime_put_autosuspend(priv->dev); ++ + return 0; + } + + static int stm32_vrefbuf_is_enabled(struct regulator_dev *rdev) + { + struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev); ++ int ret; + +- return readl_relaxed(priv->base + STM32_VREFBUF_CSR) & STM32_ENVR; ++ ret = pm_runtime_get_sync(priv->dev); ++ if (ret < 0) { ++ pm_runtime_put_noidle(priv->dev); ++ return ret; ++ } ++ ++ ret = readl_relaxed(priv->base + STM32_VREFBUF_CSR) & STM32_ENVR; ++ ++ pm_runtime_mark_last_busy(priv->dev); ++ pm_runtime_put_autosuspend(priv->dev); ++ ++ return ret; + } + + static int stm32_vrefbuf_set_voltage_sel(struct regulator_dev *rdev, + unsigned sel) + { + struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev); +- u32 val = readl_relaxed(priv->base + STM32_VREFBUF_CSR); ++ u32 val; ++ int ret; + ++ ret = pm_runtime_get_sync(priv->dev); ++ if (ret < 0) { ++ pm_runtime_put_noidle(priv->dev); ++ return ret; ++ } ++ ++ val = readl_relaxed(priv->base + STM32_VREFBUF_CSR); + val = (val & ~STM32_VRS) | FIELD_PREP(STM32_VRS, sel); + writel_relaxed(val, priv->base + STM32_VREFBUF_CSR); + ++ pm_runtime_mark_last_busy(priv->dev); ++ pm_runtime_put_autosuspend(priv->dev); ++ + return 0; + } + + static int stm32_vrefbuf_get_voltage_sel(struct regulator_dev *rdev) + { + struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev); +- u32 val = readl_relaxed(priv->base + STM32_VREFBUF_CSR); ++ u32 val; ++ int ret; + +- return FIELD_GET(STM32_VRS, val); ++ ret = pm_runtime_get_sync(priv->dev); ++ if (ret < 0) { ++ pm_runtime_put_noidle(priv->dev); ++ return ret; ++ } ++ ++ val = readl_relaxed(priv->base + STM32_VREFBUF_CSR); ++ ret = FIELD_GET(STM32_VRS, val); ++ ++ pm_runtime_mark_last_busy(priv->dev); ++ pm_runtime_put_autosuspend(priv->dev); ++ ++ return ret; + } + + static const struct regulator_ops stm32_vrefbuf_volt_ops = { +@@ -130,6 +191,7 @@ static int stm32_vrefbuf_probe(struct platform_device *pdev) + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; ++ priv->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(&pdev->dev, res); +@@ -140,10 +202,17 @@ static int stm32_vrefbuf_probe(struct platform_device *pdev) + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + ++ pm_runtime_get_noresume(&pdev->dev); ++ pm_runtime_set_active(&pdev->dev); ++ pm_runtime_set_autosuspend_delay(&pdev->dev, ++ STM32_VREFBUF_AUTO_SUSPEND_DELAY_MS); ++ pm_runtime_use_autosuspend(&pdev->dev); ++ pm_runtime_enable(&pdev->dev); ++ + ret = clk_prepare_enable(priv->clk); + if (ret) { + dev_err(&pdev->dev, "clk prepare failed with error %d\n", ret); +- return ret; ++ goto err_pm_stop; + } + + config.dev = &pdev->dev; +@@ -161,10 +230,17 @@ static int stm32_vrefbuf_probe(struct platform_device *pdev) + } + platform_set_drvdata(pdev, rdev); + ++ pm_runtime_mark_last_busy(&pdev->dev); ++ pm_runtime_put_autosuspend(&pdev->dev); ++ + return 0; + + err_clk_dis: + clk_disable_unprepare(priv->clk); ++err_pm_stop: ++ pm_runtime_disable(&pdev->dev); ++ pm_runtime_set_suspended(&pdev->dev); ++ pm_runtime_put_noidle(&pdev->dev); + + return ret; + } +@@ -174,12 +250,44 @@ static int stm32_vrefbuf_remove(struct platform_device *pdev) + struct regulator_dev *rdev = platform_get_drvdata(pdev); + struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev); + ++ pm_runtime_get_sync(&pdev->dev); + regulator_unregister(rdev); + clk_disable_unprepare(priv->clk); ++ 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_vrefbuf_runtime_suspend(struct device *dev) ++{ ++ struct regulator_dev *rdev = dev_get_drvdata(dev); ++ struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev); ++ ++ clk_disable_unprepare(priv->clk); ++ ++ return 0; ++} ++ ++static int stm32_vrefbuf_runtime_resume(struct device *dev) ++{ ++ struct regulator_dev *rdev = dev_get_drvdata(dev); ++ struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev); ++ ++ return clk_prepare_enable(priv->clk); ++} ++#endif ++ ++static const struct dev_pm_ops stm32_vrefbuf_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, ++ pm_runtime_force_resume) ++ SET_RUNTIME_PM_OPS(stm32_vrefbuf_runtime_suspend, ++ stm32_vrefbuf_runtime_resume, ++ NULL) ++}; ++ + static const struct of_device_id stm32_vrefbuf_of_match[] = { + { .compatible = "st,stm32-vrefbuf", }, + {}, +@@ -192,6 +300,7 @@ static struct platform_driver stm32_vrefbuf_driver = { + .driver = { + .name = "stm32-vrefbuf", + .of_match_table = of_match_ptr(stm32_vrefbuf_of_match), ++ .pm = &stm32_vrefbuf_pm_ops, + }, + }; + module_platform_driver(stm32_vrefbuf_driver); +diff --git a/drivers/regulator/stpmic1_regulator.c b/drivers/regulator/stpmic1_regulator.c +new file mode 100644 +index 0000000..96f1808 +--- /dev/null ++++ b/drivers/regulator/stpmic1_regulator.c +@@ -0,0 +1,674 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (C) STMicroelectronics 2018 ++// Author: Pascal Paillet for STMicroelectronics. ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/** ++ * stpmic1 regulator description ++ * @desc: regulator framework description ++ * @mask_reset_reg: mask reset register address ++ * @mask_reset_mask: mask rank and mask reset register mask ++ * @icc_reg: icc register address ++ * @icc_mask: icc register mask ++ */ ++struct stpmic1_regulator_cfg { ++ struct regulator_desc desc; ++ u8 mask_reset_reg; ++ u8 mask_reset_mask; ++ u8 icc_reg; ++ u8 icc_mask; ++}; ++ ++/** ++ * stpmic1 regulator data: this structure is used as driver data ++ * @regul_id: regulator id ++ * @reg_node: DT node of regulator (unused on non-DT platforms) ++ * @cfg: stpmic specific regulator description ++ * @mask_reset: mask_reset bit value ++ * @irq_curlim: current limit interrupt number ++ * @regmap: point to parent regmap structure ++ */ ++struct stpmic1_regulator { ++ unsigned int regul_id; ++ struct device_node *reg_node; ++ struct stpmic1_regulator_cfg *cfg; ++ u8 mask_reset; ++ int irq_curlim; ++ struct regmap *regmap; ++}; ++ ++static int stpmic1_set_mode(struct regulator_dev *rdev, unsigned int mode); ++static unsigned int stpmic1_get_mode(struct regulator_dev *rdev); ++static int stpmic1_set_icc(struct regulator_dev *rdev); ++static int stpmic1_regulator_parse_dt(void *driver_data); ++static unsigned int stpmic1_map_mode(unsigned int mode); ++ ++enum { ++ STPMIC1_BUCK1 = 0, ++ STPMIC1_BUCK2 = 1, ++ STPMIC1_BUCK3 = 2, ++ STPMIC1_BUCK4 = 3, ++ STPMIC1_LDO1 = 4, ++ STPMIC1_LDO2 = 5, ++ STPMIC1_LDO3 = 6, ++ STPMIC1_LDO4 = 7, ++ STPMIC1_LDO5 = 8, ++ STPMIC1_LDO6 = 9, ++ STPMIC1_VREF_DDR = 10, ++ STPMIC1_BOOST = 11, ++ STPMIC1_VBUS_OTG = 12, ++ STPMIC1_SW_OUT = 13, ++}; ++ ++/* Enable time worst case is 5000mV/(2250uV/uS) */ ++#define PMIC_ENABLE_TIME_US 2200 ++ ++#define STPMIC1_BUCK_MODE_NORMAL 0 ++#define STPMIC1_BUCK_MODE_LP BUCK_HPLP_ENABLE_MASK ++ ++struct regulator_linear_range buck1_ranges[] = { ++ REGULATOR_LINEAR_RANGE(600000, 0, 30, 25000), ++ REGULATOR_LINEAR_RANGE(1350000, 31, 63, 0), ++}; ++ ++struct regulator_linear_range buck2_ranges[] = { ++ REGULATOR_LINEAR_RANGE(1000000, 0, 17, 0), ++ REGULATOR_LINEAR_RANGE(1050000, 18, 19, 0), ++ REGULATOR_LINEAR_RANGE(1100000, 20, 21, 0), ++ REGULATOR_LINEAR_RANGE(1150000, 22, 23, 0), ++ REGULATOR_LINEAR_RANGE(1200000, 24, 25, 0), ++ REGULATOR_LINEAR_RANGE(1250000, 26, 27, 0), ++ REGULATOR_LINEAR_RANGE(1300000, 28, 29, 0), ++ REGULATOR_LINEAR_RANGE(1350000, 30, 31, 0), ++ REGULATOR_LINEAR_RANGE(1400000, 32, 33, 0), ++ REGULATOR_LINEAR_RANGE(1450000, 34, 35, 0), ++ REGULATOR_LINEAR_RANGE(1500000, 36, 63, 0), ++}; ++ ++struct regulator_linear_range buck3_ranges[] = { ++ REGULATOR_LINEAR_RANGE(1000000, 0, 19, 0), ++ REGULATOR_LINEAR_RANGE(1100000, 20, 23, 0), ++ REGULATOR_LINEAR_RANGE(1200000, 24, 27, 0), ++ REGULATOR_LINEAR_RANGE(1300000, 28, 31, 0), ++ REGULATOR_LINEAR_RANGE(1400000, 32, 35, 0), ++ REGULATOR_LINEAR_RANGE(1500000, 36, 55, 100000), ++ REGULATOR_LINEAR_RANGE(3400000, 56, 63, 0), ++ ++}; ++ ++struct regulator_linear_range buck4_ranges[] = { ++ REGULATOR_LINEAR_RANGE(600000, 0, 27, 25000), ++ REGULATOR_LINEAR_RANGE(1300000, 28, 29, 0), ++ REGULATOR_LINEAR_RANGE(1350000, 30, 31, 0), ++ REGULATOR_LINEAR_RANGE(1400000, 32, 33, 0), ++ REGULATOR_LINEAR_RANGE(1450000, 34, 35, 0), ++ REGULATOR_LINEAR_RANGE(1500000, 36, 60, 100000), ++ REGULATOR_LINEAR_RANGE(3900000, 61, 63, 0), ++ ++}; ++ ++struct regulator_linear_range ldo1_ranges[] = { ++ REGULATOR_LINEAR_RANGE(1700000, 0, 7, 0), ++ REGULATOR_LINEAR_RANGE(1700000, 8, 24, 100000), ++ REGULATOR_LINEAR_RANGE(3300000, 25, 31, 0), ++ ++}; ++ ++struct regulator_linear_range ldo2_ranges[] = { ++ REGULATOR_LINEAR_RANGE(1700000, 0, 7, 0), ++ REGULATOR_LINEAR_RANGE(1700000, 8, 24, 100000), ++ REGULATOR_LINEAR_RANGE(3300000, 25, 30, 0), ++ ++}; ++ ++struct regulator_linear_range ldo3_ranges[] = { ++ REGULATOR_LINEAR_RANGE(1700000, 0, 7, 0), ++ REGULATOR_LINEAR_RANGE(1700000, 8, 24, 100000), ++ REGULATOR_LINEAR_RANGE(3300000, 25, 30, 0), ++ /* with index 31 LDO3 is in DDR mode */ ++ REGULATOR_LINEAR_RANGE(500000, 31, 31, 0), ++}; ++ ++struct regulator_linear_range ldo5_ranges[] = { ++ REGULATOR_LINEAR_RANGE(1700000, 0, 7, 0), ++ REGULATOR_LINEAR_RANGE(1700000, 8, 30, 100000), ++ REGULATOR_LINEAR_RANGE(3900000, 31, 31, 0), ++}; ++ ++struct regulator_linear_range ldo6_ranges[] = { ++ REGULATOR_LINEAR_RANGE(900000, 0, 24, 100000), ++ REGULATOR_LINEAR_RANGE(3300000, 25, 31, 0), ++}; ++ ++static struct regulator_ops stpmic1_ldo_ops = { ++ .list_voltage = regulator_list_voltage_linear_range, ++ .map_voltage = regulator_map_voltage_linear_range, ++ .is_enabled = regulator_is_enabled_regmap, ++ .enable = regulator_enable_regmap, ++ .disable = regulator_disable_regmap, ++ .get_voltage_sel = regulator_get_voltage_sel_regmap, ++ .set_voltage_sel = regulator_set_voltage_sel_regmap, ++ .set_pull_down = regulator_set_pull_down_regmap, ++ .set_over_current_protection = stpmic1_set_icc, ++}; ++ ++static struct regulator_ops stpmic1_ldo3_ops = { ++ .list_voltage = regulator_list_voltage_linear_range, ++ .map_voltage = regulator_map_voltage_iterate, ++ .is_enabled = regulator_is_enabled_regmap, ++ .enable = regulator_enable_regmap, ++ .disable = regulator_disable_regmap, ++ .get_voltage_sel = regulator_get_voltage_sel_regmap, ++ .set_voltage_sel = regulator_set_voltage_sel_regmap, ++ .set_pull_down = regulator_set_pull_down_regmap, ++ .get_bypass = regulator_get_bypass_regmap, ++ .set_bypass = regulator_set_bypass_regmap, ++ .set_over_current_protection = stpmic1_set_icc, ++}; ++ ++static struct regulator_ops stpmic1_ldo4_fixed_regul_ops = { ++ .is_enabled = regulator_is_enabled_regmap, ++ .enable = regulator_enable_regmap, ++ .disable = regulator_disable_regmap, ++ .set_pull_down = regulator_set_pull_down_regmap, ++ .set_over_current_protection = stpmic1_set_icc, ++}; ++ ++static struct regulator_ops stpmic1_buck_ops = { ++ .list_voltage = regulator_list_voltage_linear_range, ++ .map_voltage = regulator_map_voltage_linear_range, ++ .is_enabled = regulator_is_enabled_regmap, ++ .enable = regulator_enable_regmap, ++ .disable = regulator_disable_regmap, ++ .get_voltage_sel = regulator_get_voltage_sel_regmap, ++ .set_voltage_sel = regulator_set_voltage_sel_regmap, ++ .set_pull_down = regulator_set_pull_down_regmap, ++ .set_mode = stpmic1_set_mode, ++ .get_mode = stpmic1_get_mode, ++ .set_over_current_protection = stpmic1_set_icc, ++}; ++ ++static struct regulator_ops stpmic1_vref_ddr_ops = { ++ .is_enabled = regulator_is_enabled_regmap, ++ .enable = regulator_enable_regmap, ++ .disable = regulator_disable_regmap, ++ .set_pull_down = regulator_set_pull_down_regmap, ++}; ++ ++static struct regulator_ops stpmic1_switch_regul_ops = { ++ .is_enabled = regulator_is_enabled_regmap, ++ .enable = regulator_enable_regmap, ++ .disable = regulator_disable_regmap, ++ .set_over_current_protection = stpmic1_set_icc, ++}; ++ ++#define REG_LDO(ids, base) { \ ++ .name = #ids, \ ++ .id = STPMIC1_##ids, \ ++ .n_voltages = 32, \ ++ .ops = &stpmic1_ldo_ops, \ ++ .linear_ranges = base ## _ranges, \ ++ .n_linear_ranges = ARRAY_SIZE(base ## _ranges), \ ++ .type = REGULATOR_VOLTAGE, \ ++ .owner = THIS_MODULE, \ ++ .vsel_reg = ids##_ACTIVE_CR, \ ++ .vsel_mask = LDO_VOLTAGE_MASK, \ ++ .enable_reg = ids##_ACTIVE_CR, \ ++ .enable_mask = LDO_ENABLE_MASK, \ ++ .enable_val = 1, \ ++ .disable_val = 0, \ ++ .enable_time = PMIC_ENABLE_TIME_US, \ ++ .pull_down_reg = ids##_PULL_DOWN_REG, \ ++ .pull_down_mask = ids##_PULL_DOWN_MASK, \ ++ .supply_name = #base, \ ++} ++ ++#define REG_LDO3(ids, base) { \ ++ .name = #ids, \ ++ .id = STPMIC1_##ids, \ ++ .n_voltages = 32, \ ++ .ops = &stpmic1_ldo3_ops, \ ++ .linear_ranges = ldo3_ranges, \ ++ .n_linear_ranges = ARRAY_SIZE(ldo3_ranges), \ ++ .type = REGULATOR_VOLTAGE, \ ++ .owner = THIS_MODULE, \ ++ .vsel_reg = LDO3_ACTIVE_CR, \ ++ .vsel_mask = LDO_VOLTAGE_MASK, \ ++ .enable_reg = LDO3_ACTIVE_CR, \ ++ .enable_mask = LDO_ENABLE_MASK, \ ++ .enable_val = 1, \ ++ .disable_val = 0, \ ++ .enable_time = PMIC_ENABLE_TIME_US, \ ++ .bypass_reg = LDO3_ACTIVE_CR, \ ++ .bypass_mask = LDO_BYPASS_MASK, \ ++ .bypass_val_on = LDO_BYPASS_MASK, \ ++ .bypass_val_off = 0, \ ++ .pull_down_reg = ids##_PULL_DOWN_REG, \ ++ .pull_down_mask = ids##_PULL_DOWN_MASK, \ ++ .supply_name = #base, \ ++} ++ ++#define REG_LDO4(ids, base) { \ ++ .name = #ids, \ ++ .id = STPMIC1_##ids, \ ++ .n_voltages = 1, \ ++ .ops = &stpmic1_ldo4_fixed_regul_ops, \ ++ .type = REGULATOR_VOLTAGE, \ ++ .owner = THIS_MODULE, \ ++ .min_uV = 3300000, \ ++ .fixed_uV = 3300000, \ ++ .enable_reg = LDO4_ACTIVE_CR, \ ++ .enable_mask = LDO_ENABLE_MASK, \ ++ .enable_val = 1, \ ++ .disable_val = 0, \ ++ .enable_time = PMIC_ENABLE_TIME_US, \ ++ .pull_down_reg = ids##_PULL_DOWN_REG, \ ++ .pull_down_mask = ids##_PULL_DOWN_MASK, \ ++ .supply_name = #base, \ ++} ++ ++#define REG_BUCK(ids, base) { \ ++ .name = #ids, \ ++ .id = STPMIC1_##ids, \ ++ .ops = &stpmic1_buck_ops, \ ++ .n_voltages = 64, \ ++ .linear_ranges = base ## _ranges, \ ++ .n_linear_ranges = ARRAY_SIZE(base ## _ranges), \ ++ .type = REGULATOR_VOLTAGE, \ ++ .owner = THIS_MODULE, \ ++ .vsel_reg = ids##_ACTIVE_CR, \ ++ .vsel_mask = BUCK_VOLTAGE_MASK, \ ++ .enable_reg = ids##_ACTIVE_CR, \ ++ .enable_mask = BUCK_ENABLE_MASK, \ ++ .enable_val = 1, \ ++ .disable_val = 0, \ ++ .enable_time = PMIC_ENABLE_TIME_US, \ ++ .of_map_mode = stpmic1_map_mode, \ ++ .pull_down_reg = ids##_PULL_DOWN_REG, \ ++ .pull_down_mask = ids##_PULL_DOWN_MASK, \ ++ .supply_name = #base, \ ++} ++ ++#define REG_VREF_DDR(ids, base) { \ ++ .name = #ids, \ ++ .id = STPMIC1_##ids, \ ++ .n_voltages = 1, \ ++ .ops = &stpmic1_vref_ddr_ops, \ ++ .type = REGULATOR_VOLTAGE, \ ++ .owner = THIS_MODULE, \ ++ .min_uV = 500000, \ ++ .fixed_uV = 500000, \ ++ .enable_reg = VREF_DDR_ACTIVE_CR, \ ++ .enable_mask = BUCK_ENABLE_MASK, \ ++ .enable_val = 1, \ ++ .disable_val = 0, \ ++ .enable_time = PMIC_ENABLE_TIME_US, \ ++ .pull_down_reg = ids##_PULL_DOWN_REG, \ ++ .pull_down_mask = ids##_PULL_DOWN_MASK, \ ++ .supply_name = #base, \ ++} ++ ++#define REG_SWITCH(ids, base, reg, mask, val) { \ ++ .name = #ids, \ ++ .id = STPMIC1_##ids, \ ++ .n_voltages = 1, \ ++ .ops = &stpmic1_switch_regul_ops, \ ++ .type = REGULATOR_VOLTAGE, \ ++ .owner = THIS_MODULE, \ ++ .min_uV = 0, \ ++ .fixed_uV = 5000000, \ ++ .enable_reg = (reg), \ ++ .enable_mask = (mask), \ ++ .enable_val = (val), \ ++ .disable_val = 0, \ ++ .enable_time = PMIC_ENABLE_TIME_US, \ ++ .supply_name = #base, \ ++} ++ ++struct stpmic1_regulator_cfg stpmic1_regulator_cfgs[] = { ++ [STPMIC1_BUCK1] = { ++ .desc = REG_BUCK(BUCK1, buck1), ++ .icc_reg = BUCKS_ICCTO_CR, ++ .icc_mask = BIT(0), ++ .mask_reset_reg = BUCKS_MASK_RESET_CR, ++ .mask_reset_mask = BIT(0), ++ }, ++ [STPMIC1_BUCK2] = { ++ .desc = REG_BUCK(BUCK2, buck2), ++ .icc_reg = BUCKS_ICCTO_CR, ++ .icc_mask = BIT(1), ++ .mask_reset_reg = BUCKS_MASK_RESET_CR, ++ .mask_reset_mask = BIT(1), ++ }, ++ [STPMIC1_BUCK3] = { ++ .desc = REG_BUCK(BUCK3, buck3), ++ .icc_reg = BUCKS_ICCTO_CR, ++ .icc_mask = BIT(2), ++ .mask_reset_reg = BUCKS_MASK_RESET_CR, ++ .mask_reset_mask = BIT(2), ++ }, ++ [STPMIC1_BUCK4] = { ++ .desc = REG_BUCK(BUCK4, buck4), ++ .icc_reg = BUCKS_ICCTO_CR, ++ .icc_mask = BIT(3), ++ .mask_reset_reg = BUCKS_MASK_RESET_CR, ++ .mask_reset_mask = BIT(3), ++ }, ++ [STPMIC1_LDO1] = { ++ .desc = REG_LDO(LDO1, ldo1), ++ .icc_reg = LDOS_ICCTO_CR, ++ .icc_mask = BIT(0), ++ .mask_reset_reg = LDOS_MASK_RESET_CR, ++ .mask_reset_mask = BIT(0), ++ }, ++ [STPMIC1_LDO2] = { ++ .desc = REG_LDO(LDO2, ldo2), ++ .icc_reg = LDOS_ICCTO_CR, ++ .icc_mask = BIT(1), ++ .mask_reset_reg = LDOS_MASK_RESET_CR, ++ .mask_reset_mask = BIT(1), ++ }, ++ [STPMIC1_LDO3] = { ++ .desc = REG_LDO3(LDO3, ldo3), ++ .icc_reg = LDOS_ICCTO_CR, ++ .icc_mask = BIT(2), ++ .mask_reset_reg = LDOS_MASK_RESET_CR, ++ .mask_reset_mask = BIT(2), ++ }, ++ [STPMIC1_LDO4] = { ++ .desc = REG_LDO4(LDO4, ldo4), ++ .icc_reg = LDOS_ICCTO_CR, ++ .icc_mask = BIT(3), ++ .mask_reset_reg = LDOS_MASK_RESET_CR, ++ .mask_reset_mask = BIT(3), ++ }, ++ [STPMIC1_LDO5] = { ++ .desc = REG_LDO(LDO5, ldo5), ++ .icc_reg = LDOS_ICCTO_CR, ++ .icc_mask = BIT(4), ++ .mask_reset_reg = LDOS_MASK_RESET_CR, ++ .mask_reset_mask = BIT(4), ++ }, ++ [STPMIC1_LDO6] = { ++ .desc = REG_LDO(LDO6, ldo6), ++ .icc_reg = LDOS_ICCTO_CR, ++ .icc_mask = BIT(5), ++ .mask_reset_reg = LDOS_MASK_RESET_CR, ++ .mask_reset_mask = BIT(5), ++ }, ++ [STPMIC1_VREF_DDR] = { ++ .desc = REG_VREF_DDR(VREF_DDR, vref_ddr), ++ .mask_reset_reg = LDOS_MASK_RESET_CR, ++ .mask_reset_mask = BIT(6), ++ }, ++ [STPMIC1_BOOST] = { ++ .desc = REG_SWITCH(BOOST, boost, BST_SW_CR, ++ BOOST_ENABLED, ++ BOOST_ENABLED), ++ .icc_reg = BUCKS_ICCTO_CR, ++ .icc_mask = BIT(6), ++ }, ++ [STPMIC1_VBUS_OTG] = { ++ .desc = REG_SWITCH(VBUS_OTG, pwr_sw1, BST_SW_CR, ++ USBSW_OTG_SWITCH_ENABLED, ++ USBSW_OTG_SWITCH_ENABLED), ++ .icc_reg = BUCKS_ICCTO_CR, ++ .icc_mask = BIT(4), ++ }, ++ [STPMIC1_SW_OUT] = { ++ .desc = REG_SWITCH(SW_OUT, pwr_sw2, BST_SW_CR, ++ SWIN_SWOUT_ENABLED, ++ SWIN_SWOUT_ENABLED), ++ .icc_reg = BUCKS_ICCTO_CR, ++ .icc_mask = BIT(5), ++ }, ++}; ++ ++static unsigned int stpmic1_map_mode(unsigned int mode) ++{ ++ switch (mode) { ++ case STPMIC1_BUCK_MODE_NORMAL: ++ return REGULATOR_MODE_NORMAL; ++ case STPMIC1_BUCK_MODE_LP: ++ return REGULATOR_MODE_STANDBY; ++ default: ++ return -EINVAL; ++ } ++} ++ ++static unsigned int stpmic1_get_mode(struct regulator_dev *rdev) ++{ ++ int value; ++ ++ regmap_read(rdev->regmap, rdev->desc->enable_reg, &value); ++ ++ if (value & STPMIC1_BUCK_MODE_LP) ++ return REGULATOR_MODE_STANDBY; ++ ++ return REGULATOR_MODE_NORMAL; ++} ++ ++static int stpmic1_set_mode(struct regulator_dev *rdev, unsigned int mode) ++{ ++ int value; ++ ++ switch (mode) { ++ case REGULATOR_MODE_NORMAL: ++ value = STPMIC1_BUCK_MODE_NORMAL; ++ break; ++ case REGULATOR_MODE_STANDBY: ++ value = STPMIC1_BUCK_MODE_LP; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, ++ STPMIC1_BUCK_MODE_LP, value); ++} ++ ++static int stpmic1_set_icc(struct regulator_dev *rdev) ++{ ++ struct stpmic1_regulator *regul = rdev_get_drvdata(rdev); ++ ++ /* enable switch off in case of over current */ ++ return regmap_update_bits(regul->regmap, regul->cfg->icc_reg, ++ regul->cfg->icc_mask, regul->cfg->icc_mask); ++} ++ ++static irqreturn_t stpmic1_curlim_irq_handler(int irq, void *data) ++{ ++ struct regulator_dev *rdev = (struct regulator_dev *)data; ++ ++ mutex_lock(&rdev->mutex); ++ ++ /* Send an overcurrent notification */ ++ regulator_notifier_call_chain(rdev, ++ REGULATOR_EVENT_OVER_CURRENT, ++ NULL); ++ ++ mutex_unlock(&rdev->mutex); ++ ++ return IRQ_HANDLED; ++} ++ ++static int stpmic1_regulator_init(struct platform_device *pdev, ++ struct regulator_dev *rdev) ++{ ++ struct stpmic1_regulator *regul = rdev_get_drvdata(rdev); ++ int ret = 0; ++ ++ /* set mask reset */ ++ if (regul->mask_reset && regul->cfg->mask_reset_reg != 0) { ++ ret = regmap_update_bits(regul->regmap, ++ regul->cfg->mask_reset_reg, ++ regul->cfg->mask_reset_mask, ++ regul->cfg->mask_reset_mask); ++ if (ret) { ++ dev_err(&pdev->dev, "set mask reset failed\n"); ++ return ret; ++ } ++ } ++ ++ /* setup an irq handler for over-current detection */ ++ if (regul->irq_curlim > 0) { ++ ret = devm_request_threaded_irq(&pdev->dev, ++ regul->irq_curlim, NULL, ++ stpmic1_curlim_irq_handler, ++ IRQF_ONESHOT | IRQF_SHARED, ++ pdev->name, rdev); ++ if (ret) { ++ dev_err(&pdev->dev, "Request IRQ failed\n"); ++ return ret; ++ } ++ } ++ return 0; ++} ++ ++#define MATCH(_name, _id) \ ++ [STPMIC1_##_id] = { \ ++ .name = #_name, \ ++ .desc = &stpmic1_regulator_cfgs[STPMIC1_##_id].desc, \ ++ } ++ ++static struct of_regulator_match stpmic1_regulators_matches[] = { ++ MATCH(buck1, BUCK1), ++ MATCH(buck2, BUCK2), ++ MATCH(buck3, BUCK3), ++ MATCH(buck4, BUCK4), ++ MATCH(ldo1, LDO1), ++ MATCH(ldo2, LDO2), ++ MATCH(ldo3, LDO3), ++ MATCH(ldo4, LDO4), ++ MATCH(ldo5, LDO5), ++ MATCH(ldo6, LDO6), ++ MATCH(vref_ddr, VREF_DDR), ++ MATCH(boost, BOOST), ++ MATCH(pwr_sw1, VBUS_OTG), ++ MATCH(pwr_sw2, SW_OUT), ++}; ++ ++static int stpmic1_regulator_parse_dt(void *driver_data) ++{ ++ struct stpmic1_regulator *regul = ++ (struct stpmic1_regulator *)driver_data; ++ ++ if (!regul) ++ return -EINVAL; ++ ++ if (of_get_property(regul->reg_node, "st,mask-reset", NULL)) ++ regul->mask_reset = 1; ++ ++ regul->irq_curlim = of_irq_get(regul->reg_node, 0); ++ ++ return 0; ++} ++ ++static struct ++regulator_dev *stpmic1_regulator_register(struct platform_device *pdev, int id, ++ struct regulator_init_data *init_data, ++ struct stpmic1_regulator *regul) ++{ ++ struct stpmic1 *pmic_dev = dev_get_drvdata(pdev->dev.parent); ++ struct regulator_dev *rdev; ++ struct regulator_config config = {}; ++ ++ config.dev = &pdev->dev; ++ config.init_data = init_data; ++ config.of_node = stpmic1_regulators_matches[id].of_node; ++ config.regmap = pmic_dev->regmap; ++ config.driver_data = regul; ++ ++ regul->regul_id = id; ++ regul->reg_node = config.of_node; ++ regul->cfg = &stpmic1_regulator_cfgs[id]; ++ regul->regmap = pmic_dev->regmap; ++ ++ rdev = devm_regulator_register(&pdev->dev, ®ul->cfg->desc, &config); ++ if (IS_ERR(rdev)) { ++ dev_err(&pdev->dev, "failed to register %s regulator\n", ++ regul->cfg->desc.name); ++ } ++ ++ return rdev; ++} ++ ++static int stpmic1_regulator_probe(struct platform_device *pdev) ++{ ++ struct regulator_dev *rdev; ++ struct stpmic1_regulator *regul; ++ struct regulator_init_data *init_data; ++ struct device_node *np; ++ int i, ret; ++ ++ np = pdev->dev.of_node; ++ ++ ret = of_regulator_match(&pdev->dev, np, ++ stpmic1_regulators_matches, ++ ARRAY_SIZE(stpmic1_regulators_matches)); ++ if (ret < 0) { ++ dev_err(&pdev->dev, ++ "Error in PMIC regulator device tree node"); ++ return ret; ++ } ++ ++ regul = devm_kzalloc(&pdev->dev, ARRAY_SIZE(stpmic1_regulator_cfgs) * ++ sizeof(struct stpmic1_regulator), ++ GFP_KERNEL); ++ if (!regul) ++ return -ENOMEM; ++ ++ for (i = 0; i < ARRAY_SIZE(stpmic1_regulator_cfgs); i++) { ++ /* Parse DT & find regulators to register */ ++ init_data = stpmic1_regulators_matches[i].init_data; ++ if (init_data) ++ init_data->regulator_init = &stpmic1_regulator_parse_dt; ++ ++ rdev = stpmic1_regulator_register(pdev, i, init_data, regul); ++ if (IS_ERR(rdev)) ++ return PTR_ERR(rdev); ++ ++ ret = stpmic1_regulator_init(pdev, rdev); ++ if (ret) { ++ dev_err(&pdev->dev, ++ "failed to initialize regulator %d\n", ret); ++ return ret; ++ } ++ ++ regul++; ++ } ++ ++ dev_dbg(&pdev->dev, "stpmic1_regulator driver probed\n"); ++ ++ return 0; ++} ++ ++static const struct of_device_id of_pmic_regulator_match[] = { ++ { .compatible = "st,stpmic1-regulators" }, ++ { }, ++}; ++ ++MODULE_DEVICE_TABLE(of, of_pmic_regulator_match); ++ ++static struct platform_driver stpmic1_regulator_driver = { ++ .driver = { ++ .name = "stpmic1-regulator", ++ .of_match_table = of_match_ptr(of_pmic_regulator_match), ++ }, ++ .probe = stpmic1_regulator_probe, ++}; ++ ++module_platform_driver(stpmic1_regulator_driver); ++ ++MODULE_DESCRIPTION("STPMIC1 PMIC voltage regulator driver"); ++MODULE_AUTHOR("Pascal Paillet "); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/reset/reset-stm32mp1.c b/drivers/reset/reset-stm32mp1.c +index b221a28..d46c47b 100644 +--- a/drivers/reset/reset-stm32mp1.c ++++ b/drivers/reset/reset-stm32mp1.c +@@ -4,6 +4,7 @@ + * Author: Gabriel Fernandez for STMicroelectronics. + */ + ++#include + #include + #include + #include +@@ -13,11 +14,50 @@ + + #define CLR_OFFSET 0x4 + ++#define STM32_RCC_TZCR 0x0 ++#define CLR_OFFSET 0x4 ++ ++#define STM32MP1_SVC_RCC 0x82001000 ++ ++#define SMT32_SPI6_R 3136 ++#define STM32_AXIM_R 3216 ++#define STM32_MCU_R 8225 ++ + struct stm32_reset_data { + struct reset_controller_dev rcdev; + void __iomem *membase; + }; + ++static int soc_secured; ++ ++static int is_stm32_id_secured(unsigned long id) ++{ ++ if (id >= SMT32_SPI6_R && id <= STM32_AXIM_R) ++ return 1; ++ ++ if (id == STM32_MCU_R) ++ return 1; ++ ++ return 0; ++} ++ ++static int reset_stm32_secure_update(struct reset_controller_dev *rcdev, ++ unsigned long id, bool assert) ++{ ++ struct arm_smccc_res res; ++ int bank = id / BITS_PER_LONG; ++ int offset = id % BITS_PER_LONG; ++ ++ if (assert) ++ arm_smccc_smc(STM32MP1_SVC_RCC, 0x1, (bank * 4), ++ BIT(offset), 0, 0, 0, 0, &res); ++ else ++ arm_smccc_smc(STM32MP1_SVC_RCC, 0x1, (bank * 4) + CLR_OFFSET, ++ BIT(offset), 0, 0, 0, 0, &res); ++ ++ return 0; ++} ++ + static inline struct stm32_reset_data * + to_stm32_reset_data(struct reset_controller_dev *rcdev) + { +@@ -45,12 +85,18 @@ static int stm32_reset_update(struct reset_controller_dev *rcdev, + static int stm32_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) + { ++ if (soc_secured && is_stm32_id_secured(id)) ++ return reset_stm32_secure_update(rcdev, id, true); ++ + return stm32_reset_update(rcdev, id, true); + } + + static int stm32_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) + { ++ if (soc_secured && is_stm32_id_secured(id)) ++ return reset_stm32_secure_update(rcdev, id, false); ++ + return stm32_reset_update(rcdev, id, false); + } + +@@ -101,6 +147,8 @@ static int stm32_reset_probe(struct platform_device *pdev) + data->rcdev.ops = &stm32_reset_ops; + data->rcdev.of_node = dev->of_node; + ++ soc_secured = readl(membase + STM32_RCC_TZCR) & 0x1; ++ + return devm_reset_controller_register(dev, &data->rcdev); + } + +diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig +index 7d7be60..6e201ff 100644 +--- a/drivers/rtc/Kconfig ++++ b/drivers/rtc/Kconfig +@@ -1770,6 +1770,7 @@ config RTC_DRV_R7301 + config RTC_DRV_STM32 + tristate "STM32 RTC" + select REGMAP_MMIO ++ depends on COMMON_CLK + depends on ARCH_STM32 || COMPILE_TEST + help + If you say yes here you get support for the STM32 On-Chip +diff --git a/drivers/rtc/rtc-stm32.c b/drivers/rtc/rtc-stm32.c +index c5908cf..87f26c1 100644 +--- a/drivers/rtc/rtc-stm32.c ++++ b/drivers/rtc/rtc-stm32.c +@@ -6,6 +6,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -15,6 +16,8 @@ + #include + #include + ++#include ++ + #define DRIVER_NAME "stm32_rtc" + + /* STM32_RTC_TR bit fields */ +@@ -39,6 +42,12 @@ + #define STM32_RTC_CR_FMT BIT(6) + #define STM32_RTC_CR_ALRAE BIT(8) + #define STM32_RTC_CR_ALRAIE BIT(12) ++#define STM32_RTC_CR_COSEL BIT(19) ++#define STM32_RTC_CR_OSEL_SHIFT 21 ++#define STM32_RTC_CR_OSEL GENMASK(22, 21) ++#define STM32_RTC_CR_COE BIT(23) ++#define STM32_RTC_CR_TAMPOE BIT(26) ++#define STM32_RTC_CR_OUT2EN BIT(31) + + /* STM32_RTC_ISR/STM32_RTC_ICSR bit fields */ + #define STM32_RTC_ISR_ALRAWF BIT(0) +@@ -75,6 +84,11 @@ + /* STM32_RTC_SR/_SCR bit fields */ + #define STM32_RTC_SR_ALRA BIT(0) + ++/* STM32_RTC_CFGR bit fields */ ++#define STM32_RTC_CFGR_OUT2_RMP BIT(0) ++#define STM32_RTC_CFGR_LSCOEN_OUT1 1 ++#define STM32_RTC_CFGR_LSCOEN_OUT2_RMP 2 ++ + /* STM32_RTC_VERR bit fields */ + #define STM32_RTC_VERR_MINREV_SHIFT 0 + #define STM32_RTC_VERR_MINREV GENMASK(3, 0) +@@ -101,6 +115,7 @@ struct stm32_rtc_registers { + u16 wpr; + u16 sr; + u16 scr; ++ u16 cfgr; + u16 verr; + }; + +@@ -115,6 +130,7 @@ struct stm32_rtc_data { + bool has_pclk; + bool need_dbp; + bool has_wakeirq; ++ bool has_lsco; + }; + + struct stm32_rtc { +@@ -128,8 +144,87 @@ struct stm32_rtc { + const struct stm32_rtc_data *data; + int irq_alarm; + int wakeirq_alarm; ++ int lsco; ++ struct clk *clk_lsco; + }; + ++/* ++ * ------------------------------------------------------------------------- ++ * | TAMPOE | OSEL[1:0] | COE | OUT2EN | RTC_OUT1 | RTC_OUT2 | ++ * | | | | | | or RTC_OUT2_RMP | ++ * |-------------------------------------------------------------------------| ++ * | 0 | 00 | 0 | 0 or 1 | - | - | ++ * |--------|-----------|-----|--------|------------------|------------------| ++ * | 0 | 00 | 1 | 0 | CALIB | - | ++ * |--------|-----------|-----|--------|------------------|------------------| ++ * | 0 or 1 | !=00 | 0 | 0 | TAMPALRM | - | ++ * |--------|-----------|-----|--------|------------------|------------------| ++ * | 0 | 00 | 1 | 1 | - | CALIB | ++ * |--------|-----------|-----|--------|------------------|------------------| ++ * | 0 or 1 | !=00 | 0 | 1 | - | TAMPALRM | ++ * |--------|-----------|-----|--------|------------------|------------------| ++ * | 0 or 1 | !=00 | 1 | 1 | TAMPALRM | CALIB | ++ * ------------------------------------------------------------------------- ++ */ ++static int stm32_rtc_clk_lsco_check_availability(struct stm32_rtc *rtc) ++{ ++ struct stm32_rtc_registers regs = rtc->data->regs; ++ unsigned int cr = readl_relaxed(rtc->base + regs.cr); ++ unsigned int cfgr = readl_relaxed(rtc->base + regs.cfgr); ++ unsigned int calib = STM32_RTC_CR_COE; ++ unsigned int tampalrm = STM32_RTC_CR_TAMPOE | STM32_RTC_CR_OSEL; ++ ++ switch (rtc->lsco) { ++ case RTC_OUT1: ++ if ((!(cr & STM32_RTC_CR_OUT2EN) && ++ ((cr & calib) || cr & tampalrm)) || ++ ((cr & calib) && (cr & tampalrm))) ++ return -EBUSY; ++ break; ++ case RTC_OUT2_RMP: ++ if ((cr & STM32_RTC_CR_OUT2EN) && ++ (cfgr & STM32_RTC_CFGR_OUT2_RMP) && ++ ((cr & calib) || (cr & tampalrm))) ++ return -EBUSY; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (clk_get_rate(rtc->rtc_ck) != 32768) ++ return -ERANGE; ++ ++ return 0; ++} ++ ++static int stm32_rtc_clk_lsco_register(struct platform_device *pdev) ++{ ++ struct stm32_rtc *rtc = platform_get_drvdata(pdev); ++ struct stm32_rtc_registers regs = rtc->data->regs; ++ u8 lscoen; ++ int ret; ++ ++ ret = stm32_rtc_clk_lsco_check_availability(rtc); ++ if (ret) ++ return ret; ++ ++ lscoen = (rtc->lsco == RTC_OUT1) ? STM32_RTC_CFGR_LSCOEN_OUT1 : ++ STM32_RTC_CFGR_LSCOEN_OUT2_RMP; ++ ++ rtc->clk_lsco = clk_register_gate(&pdev->dev, "rtc_lsco", ++ __clk_get_name(rtc->rtc_ck), ++ CLK_IGNORE_UNUSED | CLK_IS_CRITICAL, ++ rtc->base + regs.cfgr, lscoen, ++ 0, NULL); ++ if (IS_ERR(rtc->clk_lsco)) ++ return PTR_ERR(rtc->clk_lsco); ++ ++ of_clk_add_provider(pdev->dev.of_node, ++ of_clk_src_simple_get, rtc->clk_lsco); ++ ++ return 0; ++} ++ + static void stm32_rtc_wpr_unlock(struct stm32_rtc *rtc) + { + const struct stm32_rtc_registers *regs = &rtc->data->regs; +@@ -552,6 +647,7 @@ static const struct stm32_rtc_data stm32_rtc_data = { + .has_pclk = false, + .need_dbp = true, + .has_wakeirq = false, ++ .has_lsco = false, + .regs = { + .tr = 0x00, + .dr = 0x04, +@@ -562,6 +658,7 @@ static const struct stm32_rtc_data stm32_rtc_data = { + .wpr = 0x24, + .sr = 0x0C, /* set to ISR offset to ease alarm management */ + .scr = UNDEF_REG, ++ .cfgr = UNDEF_REG, + .verr = UNDEF_REG, + }, + .events = { +@@ -574,6 +671,7 @@ static const struct stm32_rtc_data stm32h7_rtc_data = { + .has_pclk = true, + .need_dbp = true, + .has_wakeirq = false, ++ .has_lsco = false, + .regs = { + .tr = 0x00, + .dr = 0x04, +@@ -584,6 +682,7 @@ static const struct stm32_rtc_data stm32h7_rtc_data = { + .wpr = 0x24, + .sr = 0x0C, /* set to ISR offset to ease alarm management */ + .scr = UNDEF_REG, ++ .cfgr = UNDEF_REG, + .verr = UNDEF_REG, + }, + .events = { +@@ -605,6 +704,7 @@ static const struct stm32_rtc_data stm32mp1_data = { + .has_pclk = true, + .need_dbp = false, + .has_wakeirq = true, ++ .has_lsco = true, + .regs = { + .tr = 0x00, + .dr = 0x04, +@@ -615,6 +715,7 @@ static const struct stm32_rtc_data stm32mp1_data = { + .wpr = 0x24, + .sr = 0x50, + .scr = 0x5C, ++ .cfgr = 0x60, + .verr = 0x3F4, + }, + .events = { +@@ -818,6 +919,21 @@ static int stm32_rtc_probe(struct platform_device *pdev) + goto err; + } + ++ if (rtc->data->has_lsco) { ++ ret = of_property_read_s32(pdev->dev.of_node, ++ "st,lsco", &rtc->lsco); ++ if (!ret) { ++ ret = stm32_rtc_clk_lsco_register(pdev); ++ if (ret) ++ dev_warn(&pdev->dev, ++ "LSCO clock registration failed: %d\n", ++ ret); ++ } else { ++ rtc->lsco = ret; ++ dev_dbg(&pdev->dev, "No LSCO clock: %d\n", ret); ++ } ++ } ++ + /* + * If INITS flag is reset (calendar year field set to 0x00), calendar + * must be initialized +@@ -854,6 +970,9 @@ static int stm32_rtc_remove(struct platform_device *pdev) + const struct stm32_rtc_registers *regs = &rtc->data->regs; + unsigned int cr; + ++ if (!IS_ERR_OR_NULL(rtc->clk_lsco)) ++ clk_unregister_gate(rtc->clk_lsco); ++ + /* Disable interrupts */ + stm32_rtc_wpr_unlock(rtc); + cr = readl_relaxed(rtc->base + regs->cr); +diff --git a/include/dt-bindings/pinctrl/stm32-pinfunc.h b/include/dt-bindings/pinctrl/stm32-pinfunc.h +index b5a2174..e928aea 100644 +--- a/include/dt-bindings/pinctrl/stm32-pinfunc.h ++++ b/include/dt-bindings/pinctrl/stm32-pinfunc.h +@@ -32,5 +32,11 @@ + + #define STM32_PINMUX(port, line, mode) (((PIN_NO(port, line)) << 8) | (mode)) + ++/* package information */ ++#define STM32MP157CAA 0x1 ++#define STM32MP157CAB 0x2 ++#define STM32MP157CAC 0x4 ++#define STM32MP157CAD 0x8 ++ + #endif /* _DT_BINDINGS_STM32_PINFUNC_H */ + +diff --git a/include/dt-bindings/rtc/rtc-stm32.h b/include/dt-bindings/rtc/rtc-stm32.h +new file mode 100644 +index 0000000..4373c4d +--- /dev/null ++++ b/include/dt-bindings/rtc/rtc-stm32.h +@@ -0,0 +1,13 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * This header provides constants for STM32_RTC bindings. ++ */ ++ ++#ifndef _DT_BINDINGS_RTC_RTC_STM32_H ++#define _DT_BINDINGS_RTC_RTC_STM32_H ++ ++#define RTC_OUT1 0 ++#define RTC_OUT2 1 ++#define RTC_OUT2_RMP 2 ++ ++#endif +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0013-ARM-stm32mp1-r0-rc1-REMOTEPROC-RPMSG.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0013-ARM-stm32mp1-r0-rc1-REMOTEPROC-RPMSG.patch new file mode 100644 index 0000000..ca00097 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0013-ARM-stm32mp1-r0-rc1-REMOTEPROC-RPMSG.patch @@ -0,0 +1,2648 @@ +From 80b6c916e8d8900d2449726c59ed7ad8609c9976 Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Tue, 13 Nov 2018 12:28:00 +0100 +Subject: [PATCH 13/52] ARM: stm32mp1-r0-rc1: REMOTEPROC RPMSG + +--- + Documentation/remoteproc.txt | 23 + + drivers/remoteproc/Kconfig | 36 ++ + drivers/remoteproc/Makefile | 3 + + drivers/remoteproc/remoteproc_core.c | 45 +- + drivers/remoteproc/rproc_srm_core.c | 303 ++++++++++++ + drivers/remoteproc/rproc_srm_core.h | 110 +++++ + drivers/remoteproc/rproc_srm_dev.c | 928 +++++++++++++++++++++++++++++++++++ + drivers/remoteproc/stm32_rproc.c | 585 ++++++++++++++++++++++ + drivers/rpmsg/Kconfig | 9 + + drivers/rpmsg/Makefile | 1 + + drivers/rpmsg/rpmsg_core.c | 19 + + drivers/rpmsg/rpmsg_internal.h | 2 + + drivers/rpmsg/rpmsg_tty.c | 305 ++++++++++++ + drivers/rpmsg/virtio_rpmsg_bus.c | 11 + + include/linux/remoteproc.h | 2 + + include/linux/rpmsg.h | 9 + + 16 files changed, 2382 insertions(+), 9 deletions(-) + create mode 100644 drivers/remoteproc/rproc_srm_core.c + create mode 100644 drivers/remoteproc/rproc_srm_core.h + create mode 100644 drivers/remoteproc/rproc_srm_dev.c + create mode 100644 drivers/remoteproc/stm32_rproc.c + create mode 100644 drivers/rpmsg/rpmsg_tty.c + +diff --git a/Documentation/remoteproc.txt b/Documentation/remoteproc.txt +index 77fb03a..bec2177 100644 +--- a/Documentation/remoteproc.txt ++++ b/Documentation/remoteproc.txt +@@ -353,3 +353,26 @@ Of course, RSC_VDEV resource entries are only good enough for static + allocation of virtio devices. Dynamic allocations will also be made possible + using the rpmsg bus (similar to how we already do dynamic allocations of + rpmsg channels; read more about it in rpmsg.txt). ++ ++8. System Resource Manager (SRM) ++ ++Since some resources are shared (directly or not) between the processors, a ++processor cannot manage such resources without potentially impacting the other ++processors : as an example, if a processor changes the frequency of a clock, the ++frequency of another clock managed by another processor may be updated too. ++ ++The System Resource Manager prevents such resource conflicts between the ++processors : it reserves and initializes the system resources of the peripherals ++assigned to a remote processor. ++ ++As of today the following resources are controlled by the SRM: ++- clocks ++- gpios (pinctrl) ++- regulators (power supplies) ++ ++The SRM is implemented as an 'rproc_subdev' and registered to remoteproc_core. ++Unlike the virtio device (vdev), the SRM subdev is probed *before* the rproc ++boots, ensuring the availability of the resources before the remoteproc starts. ++ ++The resources handled by the SRM are defined in the DeviceTree: please read ++Documentation/devicetree/bindings/remoteproc/rproc-srm.txt for details. +diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig +index 052d4dd..c1f3f00 100644 +--- a/drivers/remoteproc/Kconfig ++++ b/drivers/remoteproc/Kconfig +@@ -13,6 +13,25 @@ config REMOTEPROC + + if REMOTEPROC + ++config REMOTEPROC_SRM_CORE ++ tristate "Remoteproc System Resource Manager core" ++ depends on RPMSG ++ help ++ Say y here to enable the core driver of the remoteproc System Resource ++ Manager (SRM). ++ The SRM handles resources allocated to remote processors. ++ The core part is in charge of controlling the device children. ++ ++config REMOTEPROC_SRM_DEV ++ tristate "Remoteproc System Resource Manager device" ++ depends on REMOTEPROC_SRM_CORE ++ help ++ Say y here to enable the device driver of the remoteproc System ++ Resource Manager (SRM). ++ The SRM handles resources allocated to remote processors. ++ The device part is in charge of reserving and initializing resources ++ for a peripheral assigned to a coprocessor. ++ + config IMX_REMOTEPROC + tristate "IMX6/7 remoteproc support" + depends on SOC_IMX6SX || SOC_IMX7D +@@ -181,6 +200,23 @@ config ST_REMOTEPROC + config ST_SLIM_REMOTEPROC + tristate + ++config STM32_RPROC ++ tristate "STM32 remoteproc support" ++ depends on ARCH_STM32 ++ depends on REMOTEPROC ++ select MAILBOX ++ select REMOTEPROC_SRM_CORE ++ select REMOTEPROC_SRM_DEV ++ help ++ Say y here to support STM32 MCU processors via the ++ remote processor framework. ++ ++ You want to say y here in order to enable AMP ++ use-cases to run on your platform (dedicated firmware could be ++ offloaded to remote MCU processors using this framework). ++ ++ This can be either built-in or a loadable module. ++ + endif # REMOTEPROC + + endmenu +diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile +index 03332fa..1e43aa6 100644 +--- a/drivers/remoteproc/Makefile ++++ b/drivers/remoteproc/Makefile +@@ -9,6 +9,8 @@ remoteproc-y += remoteproc_debugfs.o + remoteproc-y += remoteproc_sysfs.o + remoteproc-y += remoteproc_virtio.o + remoteproc-y += remoteproc_elf_loader.o ++obj-$(CONFIG_REMOTEPROC_SRM_CORE) += rproc_srm_core.o ++obj-$(CONFIG_REMOTEPROC_SRM_DEV) += rproc_srm_dev.o + obj-$(CONFIG_IMX_REMOTEPROC) += imx_rproc.o + obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o + obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o +@@ -25,3 +27,4 @@ qcom_wcnss_pil-y += qcom_wcnss.o + qcom_wcnss_pil-y += qcom_wcnss_iris.o + obj-$(CONFIG_ST_REMOTEPROC) += st_remoteproc.o + obj-$(CONFIG_ST_SLIM_REMOTEPROC) += st_slim_rproc.o ++obj-$(CONFIG_STM32_RPROC) += stm32_rproc.o +diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c +index aa62067..21726f0 100644 +--- a/drivers/remoteproc/remoteproc_core.c ++++ b/drivers/remoteproc/remoteproc_core.c +@@ -41,6 +41,7 @@ + #include + #include + #include ++#include + #include + + #include "remoteproc_internal.h" +@@ -987,7 +988,11 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw) + if (ret) + return ret; + +- dev_info(dev, "Booting fw image %s, size %zd\n", name, fw->size); ++ if (fw) ++ dev_info(dev, "Booting fw image %s, size %zd\n", name, ++ fw->size); ++ else ++ dev_info(dev, "Synchronizing with early booted co-processor\n"); + + /* + * if enabling an IOMMU isn't relevant for this rproc, this is +@@ -1290,7 +1295,7 @@ static void rproc_crash_handler_work(struct work_struct *work) + */ + int rproc_boot(struct rproc *rproc) + { +- const struct firmware *firmware_p; ++ const struct firmware *firmware_p = NULL; + struct device *dev; + int ret; + +@@ -1321,11 +1326,17 @@ int rproc_boot(struct rproc *rproc) + + dev_info(dev, "powering up %s\n", rproc->name); + +- /* load firmware */ +- ret = request_firmware(&firmware_p, rproc->firmware, dev); +- if (ret < 0) { +- dev_err(dev, "request_firmware failed: %d\n", ret); +- goto downref_rproc; ++ if (!rproc->early_boot) { ++ /* load firmware */ ++ ret = request_firmware(&firmware_p, rproc->firmware, dev); ++ if (ret < 0) { ++ dev_err(dev, "request_firmware failed: %d\n", ret); ++ goto downref_rproc; ++ } ++ } else { ++ /* set firmware name to null as unknown */ ++ kfree(rproc->firmware); ++ rproc->firmware = NULL; + } + + ret = rproc_fw_boot(rproc, firmware_p); +@@ -1479,8 +1490,22 @@ int rproc_add(struct rproc *rproc) + /* create debugfs entries */ + rproc_create_debug_dir(rproc); + +- /* if rproc is marked always-on, request it to boot */ +- if (rproc->auto_boot) { ++ /* add resource manager device */ ++ ret = devm_of_platform_populate(dev->parent); ++ if (ret < 0) ++ return ret; ++ ++ if (rproc->early_boot) { ++ /* ++ * If rproc is marked already booted, no need to wait ++ * for firmware. ++ * Just handle associated resources and start sub devices ++ */ ++ ret = rproc_boot(rproc); ++ if (ret < 0) ++ return ret; ++ } else if (rproc->auto_boot) { ++ /* if rproc is marked always-on, request it to boot */ + ret = rproc_trigger_auto_boot(rproc); + if (ret < 0) + return ret; +@@ -1706,6 +1731,8 @@ int rproc_del(struct rproc *rproc) + list_del(&rproc->node); + mutex_unlock(&rproc_list_mutex); + ++ of_platform_depopulate(rproc->dev.parent); ++ + device_del(&rproc->dev); + + return 0; +diff --git a/drivers/remoteproc/rproc_srm_core.c b/drivers/remoteproc/rproc_srm_core.c +new file mode 100644 +index 0000000..66be92e +--- /dev/null ++++ b/drivers/remoteproc/rproc_srm_core.c +@@ -0,0 +1,303 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved ++ * Author: Fabien Dessenne for STMicroelectronics. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "rproc_srm_core.h" ++ ++#define BIND_TIMEOUT 10000 ++ ++struct rproc_srm_core { ++ struct device *dev; ++ struct completion all_bound; ++ int bind_status; ++ atomic_t prepared; ++ struct rproc_subdev subdev; ++ struct rpmsg_driver rpdrv; ++ struct blocking_notifier_head notifier; ++}; ++ ++#define to_rproc_srm_core(s) container_of(s, struct rproc_srm_core, subdev) ++ ++static struct rproc_srm_core *rpmsg_srm_to_core(struct rpmsg_device *rpdev) ++{ ++ struct rpmsg_driver *rpdrv; ++ struct rproc_srm_core *core; ++ ++ rpdrv = container_of(rpdev->dev.driver, struct rpmsg_driver, drv); ++ core = container_of(rpdrv, struct rproc_srm_core, rpdrv); ++ ++ return core; ++} ++ ++int rpmsg_srm_send(struct rpmsg_endpoint *ept, struct rpmsg_srm_msg *msg) ++{ ++ int ret; ++ ++ ret = rpmsg_send(ept, (void *)msg, sizeof(*msg)); ++ if (ret) ++ dev_err(&ept->rpdev->dev, "rpmsg_send failed: %d\n", ret); ++ ++ return ret; ++} ++EXPORT_SYMBOL(rpmsg_srm_send); ++ ++static int rpmsg_srm_cb(struct rpmsg_device *rpdev, void *data, int len, ++ void *priv, u32 src) ++{ ++ struct rproc_srm_core *core = rpmsg_srm_to_core(rpdev); ++ struct rpmsg_srm_msg_desc desc; ++ int ret; ++ ++ desc.ept = rpdev->ept; ++ desc.msg = data; ++ ++ ret = blocking_notifier_call_chain(&core->notifier, 0, &desc); ++ ++ if (!(ret & NOTIFY_STOP_MASK)) { ++ dev_warn(&rpdev->dev, "unknown device\n"); ++ desc.msg->message_type = RPROC_SRM_MSG_ERROR; ++ rpmsg_srm_send(desc.ept, desc.msg); ++ } ++ ++ return 0; ++} ++ ++static int rpmsg_srm_probe(struct rpmsg_device *rpdev) ++{ ++ int ret; ++ ++ dev_dbg(&rpdev->dev, "%s\n", __func__); ++ ++ /* Send an empty message to complete the initialization */ ++ ret = rpmsg_send(rpdev->ept, NULL, 0); ++ if (ret) ++ dev_err(&rpdev->dev, "failed to send init message\n"); ++ ++ return ret; ++} ++ ++static void rpmsg_srm_remove(struct rpmsg_device *rpdev) ++{ ++ /* Note : the remove ops is mandatory */ ++ dev_dbg(&rpdev->dev, "%s\n", __func__); ++} ++ ++static struct rpmsg_device_id rpmsg_srm_id_table[] = { ++ { .name = "rproc-srm" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(rpmsg, rpmsg_srm_id_table); ++ ++static struct rpmsg_driver rpmsg_srm_drv = { ++ .drv.name = "rpmsg_srm", ++ .id_table = rpmsg_srm_id_table, ++ .probe = rpmsg_srm_probe, ++ .callback = rpmsg_srm_cb, ++ .remove = rpmsg_srm_remove, ++}; ++ ++int rproc_srm_core_register_notifier(struct rproc_srm_core *core, ++ struct notifier_block *nb) ++{ ++ return blocking_notifier_chain_register(&core->notifier, nb); ++} ++EXPORT_SYMBOL(rproc_srm_core_register_notifier); ++ ++int rproc_srm_core_unregister_notifier(struct rproc_srm_core *core, ++ struct notifier_block *nb) ++{ ++ return blocking_notifier_chain_unregister(&core->notifier, nb); ++} ++EXPORT_SYMBOL(rproc_srm_core_unregister_notifier); ++ ++static int compare_of(struct device *dev, void *data) ++{ ++ return dev->of_node == data; ++} ++ ++static void release_of(struct device *dev, void *data) ++{ ++ of_node_put(data); ++} ++ ++static void rproc_srm_core_unbind(struct device *dev) ++{ ++ component_unbind_all(dev, NULL); ++} ++ ++static int rproc_srm_core_bind(struct device *dev) ++{ ++ struct rproc_srm_core *rproc_srm_core = dev_get_drvdata(dev); ++ ++ rproc_srm_core->bind_status = component_bind_all(dev, NULL); ++ complete(&rproc_srm_core->all_bound); ++ ++ return rproc_srm_core->bind_status; ++} ++ ++static const struct component_master_ops srm_comp_ops = { ++ .bind = rproc_srm_core_bind, ++ .unbind = rproc_srm_core_unbind, ++}; ++ ++static int rproc_srm_core_prepare(struct rproc_subdev *subdev) ++{ ++ struct rproc_srm_core *rproc_srm_core = to_rproc_srm_core(subdev); ++ struct device *dev = rproc_srm_core->dev; ++ struct device_node *node = dev->of_node; ++ struct device_node *child_np; ++ struct component_match *match = NULL; ++ int ret; ++ ++ dev_dbg(dev, "%s\n", __func__); ++ ++ init_completion(&rproc_srm_core->all_bound); ++ ++ ret = devm_of_platform_populate(dev); ++ if (ret) { ++ dev_err(dev, "cannot populate node (%d)\n", ret); ++ return ret; ++ } ++ ++ child_np = of_get_next_available_child(node, NULL); ++ ++ while (child_np) { ++ of_node_get(child_np); ++ component_match_add_release(dev, &match, release_of, compare_of, ++ child_np); ++ child_np = of_get_next_available_child(node, child_np); ++ } ++ ++ if (!match) { ++ dev_dbg(dev, "No available child\n"); ++ goto done; ++ } ++ ++ ret = component_master_add_with_match(dev, &srm_comp_ops, match); ++ if (ret) ++ goto depopulate; ++ ++ /* Wait for every child to be bound */ ++ if (!wait_for_completion_timeout(&rproc_srm_core->all_bound, ++ msecs_to_jiffies(BIND_TIMEOUT))) { ++ dev_err(dev, "bind timeout\n"); ++ ret = -ETIMEDOUT; ++ goto master; ++ } ++ ++ ret = rproc_srm_core->bind_status; ++ if (ret) { ++ dev_err(dev, "failed to bind\n"); ++ goto master; ++ } ++ ++ /* Register rpmsg driver for dynamic management */ ++ rproc_srm_core->rpdrv = rpmsg_srm_drv; ++ ret = register_rpmsg_driver(&rproc_srm_core->rpdrv); ++ if (ret) { ++ dev_err(dev, "failed to register rpmsg drv\n"); ++ goto master; ++ } ++ ++done: ++ atomic_inc(&rproc_srm_core->prepared); ++ ++ return 0; ++ ++master: ++ component_master_del(dev, &srm_comp_ops); ++depopulate: ++ devm_of_platform_depopulate(dev); ++ return ret; ++} ++ ++static void rproc_srm_core_unprepare(struct rproc_subdev *subdev) ++{ ++ struct rproc_srm_core *rproc_srm_core = to_rproc_srm_core(subdev); ++ struct device *dev = rproc_srm_core->dev; ++ ++ dev_dbg(dev, "%s\n", __func__); ++ ++ if (!atomic_read(&rproc_srm_core->prepared)) ++ return; ++ ++ atomic_dec(&rproc_srm_core->prepared); ++ ++ unregister_rpmsg_driver(&rproc_srm_core->rpdrv); ++ ++ component_master_del(dev, &srm_comp_ops); ++ devm_of_platform_depopulate(dev); ++} ++ ++static int rproc_srm_core_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct rproc *rproc = dev_get_drvdata(dev->parent); ++ struct rproc_srm_core *rproc_srm_core; ++ ++ dev_dbg(dev, "%s\n", __func__); ++ ++ rproc_srm_core = devm_kzalloc(dev, sizeof(struct rproc_srm_core), ++ GFP_KERNEL); ++ if (!rproc_srm_core) ++ return -ENOMEM; ++ ++ rproc_srm_core->dev = dev; ++ BLOCKING_INIT_NOTIFIER_HEAD(&rproc_srm_core->notifier); ++ ++ /* Register rproc subdevice with (un)prepare ops */ ++ rproc_srm_core->subdev.prepare = rproc_srm_core_prepare; ++ rproc_srm_core->subdev.unprepare = rproc_srm_core_unprepare; ++ rproc_add_subdev(rproc, &rproc_srm_core->subdev); ++ ++ dev_set_drvdata(dev, rproc_srm_core); ++ ++ return 0; ++} ++ ++static int rproc_srm_core_remove(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct rproc_srm_core *rproc_srm_core = dev_get_drvdata(dev); ++ struct rproc *rproc = dev_get_drvdata(dev->parent); ++ ++ dev_dbg(dev, "%s\n", __func__); ++ ++ if (atomic_read(&rproc->power) > 0) ++ dev_warn(dev, "Releasing resources while firmware running!\n"); ++ ++ rproc_srm_core_unprepare(&rproc_srm_core->subdev); ++ ++ return 0; ++} ++ ++static const struct of_device_id rproc_srm_core_match[] = { ++ { .compatible = "rproc-srm-core", }, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, rproc_srm_core_match); ++ ++static struct platform_driver rproc_srm_core_driver = { ++ .probe = rproc_srm_core_probe, ++ .remove = rproc_srm_core_remove, ++ .driver = { ++ .name = "rproc-srm-core", ++ .of_match_table = of_match_ptr(rproc_srm_core_match), ++ }, ++}; ++ ++module_platform_driver(rproc_srm_core_driver); ++ ++MODULE_AUTHOR("Fabien Dessenne "); ++MODULE_DESCRIPTION("Remoteproc System Resource Manager driver - core"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/remoteproc/rproc_srm_core.h b/drivers/remoteproc/rproc_srm_core.h +new file mode 100644 +index 0000000..7915f35 +--- /dev/null ++++ b/drivers/remoteproc/rproc_srm_core.h +@@ -0,0 +1,110 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved ++ * Author: Fabien Dessenne for STMicroelectronics. ++ */ ++ ++#ifndef _RPROC_SRM_CORE_H_ ++#define _RPROC_SRM_CORE_H_ ++ ++/** ++ * Message type used in resource manager rpmsg: ++ * RPROC_SRM_MSG_GETCONFIG: Request to get the configuration of a resource ++ * RPROC_SRM_MSG_SETCONFIG: Request to set the configuration of a resource ++ * RPROC_SRM_MSG_ERROR: Error when processing a request ++ */ ++#define RPROC_SRM_MSG_GETCONFIG 0x00 ++#define RPROC_SRM_MSG_SETCONFIG 0x01 ++#define RPROC_SRM_MSG_ERROR 0xFF ++ ++/** ++ * Resource type used in resource manager rpmsg: ++ * RPROC_SRM_RSC_CLOCK: clock resource ++ * RPROC_SRM_RSC_PIN: pin resource ++ * RPROC_SRM_RSC_REGU: regulator resource ++ */ ++#define RPROC_SRM_RSC_CLOCK 0x00 ++#define RPROC_SRM_RSC_PIN 0x01 ++#define RPROC_SRM_RSC_REGU 0x02 ++ ++/** ++ * struct clock_cfg - clock configuration used in resource manager rpmsg ++ * @index: clock index ++ * @name: clock name ++ * @rate: clock rate request (in SetConfig message) or current status (in ++ * GetConfig message) ++ */ ++struct clock_cfg { ++ u32 index; ++ u8 name[16]; ++ u32 rate; ++}; ++ ++/** ++ * struct regu_cfg - regu configuration used in resource manager rpmsg ++ * @index: regulator index ++ * @name: regulator name ++ * @enable: regulator enable/disable request (in SetConfig message) or ++ * current status (in GetConfig message) ++ * @curr_voltage_mv: current regulator voltage in mV (meaningful in ++ * SetConfig message) ++ * @min_voltage_mv: regulator min voltage request in mV (meaningful in ++ * SetConfig message) ++ * @max_voltage_mv: regulator max voltage request in mV (meaningful in ++ * SetConfig message) ++ */ ++struct regu_cfg { ++ u32 index; ++ u8 name[16]; ++ u32 enable; ++ u32 curr_voltage_mv; ++ u32 min_voltage_mv; ++ u32 max_voltage_mv; ++}; ++ ++/** ++ * struct pin_cfg - pin configuration used in resource manager rpmsg ++ * @name: current pin configuration name (meaningful in GetConfig message) ++ */ ++struct pin_cfg { ++ u8 name[16]; ++}; ++ ++/** ++ * struct rpmsg_srm_msg - message structure used between processors to ++ * dynamically update resources configuration ++ * @message_type: type of the message: see RPROC_SRM_MSG* ++ * @device_id: an identifier specifying the device owning the resources. ++ * This is implementation dependent. As example it may be the ++ * device name or the device address. ++ * @rsc_type: the type of the resource for which the configuration applies: ++ * see RPROC_SRM_RSC* ++ * @clock_cfg: clock config - relevant if &rsc_type is RPROC_SRM_RSC_CLOCK ++ * @regu_cfg: regulator config - relevant if &rsc_type is RPROC_SRM_RSC_REGU ++ * @pin_cfg: pin config - relevant if &rsc_type is RPROC_SRM_RSC_PIN ++ */ ++struct rpmsg_srm_msg { ++ u32 message_type; ++ u8 device_id[32]; ++ u32 rsc_type; ++ union { ++ struct clock_cfg clock_cfg; ++ struct regu_cfg regu_cfg; ++ struct pin_cfg pin_cfg; ++ }; ++}; ++ ++struct rpmsg_srm_msg_desc { ++ struct rpmsg_endpoint *ept; ++ struct rpmsg_srm_msg *msg; ++}; ++ ++struct rproc_srm_core; ++ ++int rproc_srm_core_register_notifier(struct rproc_srm_core *core, ++ struct notifier_block *nb); ++int rproc_srm_core_unregister_notifier(struct rproc_srm_core *core, ++ struct notifier_block *nb); ++int rpmsg_srm_send(struct rpmsg_endpoint *ept, struct rpmsg_srm_msg *msg); ++ ++#endif +diff --git a/drivers/remoteproc/rproc_srm_dev.c b/drivers/remoteproc/rproc_srm_dev.c +new file mode 100644 +index 0000000..b026f961 +--- /dev/null ++++ b/drivers/remoteproc/rproc_srm_dev.c +@@ -0,0 +1,928 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved ++ * Author: Fabien Dessenne for STMicroelectronics. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "rproc_srm_core.h" ++ ++struct rproc_srm_clk_info { ++ struct list_head list; ++ unsigned int index; ++ struct clk *clk; ++ const char *name; ++ bool parent_enabled; ++}; ++ ++struct rproc_srm_pin_info { ++ struct list_head list; ++ unsigned int index; ++ char *name; ++ bool selected; ++}; ++ ++struct rproc_srm_regu_info { ++ struct list_head list; ++ unsigned int index; ++ struct regulator *regu; ++ const char *name; ++ bool enabled; ++}; ++ ++struct rproc_srm_irq_info { ++ struct list_head list; ++ unsigned int index; ++ char *name; ++ int irq; ++ bool enabled; ++}; ++ ++struct rproc_srm_dev { ++ struct device *dev; ++ struct rproc_srm_core *core; ++ struct notifier_block nb; ++ struct pinctrl *pctrl; ++ bool early_boot; ++ ++ struct list_head clk_list_head; ++ struct list_head regu_list_head; ++ struct list_head pin_list_head; ++ struct list_head irq_list_head; ++}; ++ ++/* Irqs */ ++static void rproc_srm_dev_irqs_put(struct rproc_srm_dev *rproc_srm_dev) ++{ ++ struct device *dev = rproc_srm_dev->dev; ++ struct rproc_srm_irq_info *i, *tmp; ++ ++ list_for_each_entry_safe(i, tmp, &rproc_srm_dev->irq_list_head, list) { ++ devm_free_irq(dev, i->irq, NULL); ++ dev_dbg(dev, "Put irq %d (%s)\n", i->irq, i->name); ++ list_del(&i->list); ++ } ++} ++ ++static irqreturn_t rproc_srm_dev_irq_handler(int irq, void *dev) ++{ ++ dev_warn(dev, "Spurious interrupt\n"); ++ return IRQ_HANDLED; ++} ++ ++static int rproc_srm_dev_irqs_get(struct rproc_srm_dev *rproc_srm_dev) ++{ ++ struct device *dev = rproc_srm_dev->dev; ++ struct platform_device *pdev = to_platform_device(dev); ++ struct device_node *np = dev->of_node; ++ struct rproc_srm_irq_info *info; ++ const char *name; ++ int nr, ret, irq; ++ unsigned int i; ++ ++ if (!np) ++ return 0; ++ ++ nr = platform_irq_count(pdev); ++ if (!nr) ++ return 0; ++ ++ if (rproc_srm_dev->early_boot) ++ /* ++ * Do not overwrite the irq configuration. ++ * No need to parse irq from DT since the resource manager does ++ * not offer any service to update the irq config. ++ */ ++ return 0; ++ ++ for (i = 0; i < nr; i++) { ++ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); ++ if (!info) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ irq = platform_get_irq(pdev, i); ++ if (irq <= 0) { ++ ret = irq; ++ dev_err(dev, "Failed to get irq (%d)\n", ret); ++ goto err; ++ } ++ ++ info->irq = irq; ++ ++ /* Register a dummy irq handleras not used by Linux */ ++ ret = devm_request_irq(dev, info->irq, ++ rproc_srm_dev_irq_handler, 0, ++ dev_name(dev), NULL); ++ if (ret) { ++ dev_err(dev, "Failed to request irq (%d)\n", ret); ++ goto err; ++ } ++ ++ /* ++ * Disable IRQ. Since it is used by the remote processor we ++ * must not use the 'irq lazy disable' optimization ++ */ ++ irq_set_status_flags(info->irq, IRQ_DISABLE_UNLAZY); ++ disable_irq(info->irq); ++ ++ /* Note: "interrupt-names" is optional */ ++ if (!of_property_read_string_index(np, "interrupt-names", i, ++ &name)) ++ info->name = devm_kstrdup(dev, name, GFP_KERNEL); ++ else ++ info->name = devm_kstrdup(dev, "", GFP_KERNEL); ++ ++ info->index = i; ++ ++ list_add_tail(&info->list, &rproc_srm_dev->irq_list_head); ++ dev_dbg(dev, "Got irq %d (%s)\n", info->irq, info->name); ++ } ++ ++ return 0; ++ ++err: ++ rproc_srm_dev_irqs_put(rproc_srm_dev); ++ ++ return ret; ++} ++ ++/* Clocks */ ++static void rproc_srm_dev_clocks_unsetup(struct rproc_srm_dev *rproc_srm_dev) ++{ ++ struct rproc_srm_clk_info *c; ++ ++ list_for_each_entry(c, &rproc_srm_dev->clk_list_head, list) { ++ if (!c->parent_enabled) ++ continue; ++ ++ clk_disable_unprepare(clk_get_parent(c->clk)); ++ c->parent_enabled = false; ++ dev_dbg(rproc_srm_dev->dev, "clk %d (%s) unsetup\n", ++ c->index, c->name); ++ } ++} ++ ++static int rproc_srm_dev_clocks_setup(struct rproc_srm_dev *rproc_srm_dev) ++{ ++ struct rproc_srm_clk_info *c; ++ int ret; ++ ++ /* ++ * Prepare and enable the parent clocks. ++ * Since the clock tree is under the exclusive control of the master ++ * processor, we need to configure the clock tree of the targeted clock. ++ * We do not want to enable the clock itself, which is under the ++ * responsibility of the remote processor. ++ * Hence we prepare and enable the parent clock. ++ */ ++ ++ list_for_each_entry(c, &rproc_srm_dev->clk_list_head, list) { ++ if (c->parent_enabled) ++ continue; ++ ++ ret = clk_prepare_enable(clk_get_parent(c->clk)); ++ if (ret) { ++ dev_err(rproc_srm_dev->dev, ++ "clk %d (%s) parent enable failed\n", ++ c->index, c->name); ++ rproc_srm_dev_clocks_unsetup(rproc_srm_dev); ++ return ret; ++ } ++ c->parent_enabled = true; ++ dev_dbg(rproc_srm_dev->dev, "clk %d (%s) parent enabled\n", ++ c->index, c->name); ++ } ++ ++ return 0; ++} ++ ++static struct rproc_srm_clk_info ++ *rproc_srm_dev_clock_find(struct rproc_srm_dev *rproc_srm_dev, ++ struct clock_cfg *cfg) ++{ ++ struct rproc_srm_clk_info *ci; ++ ++ /* Search by index (if valid value) otherwise search by name */ ++ list_for_each_entry(ci, &rproc_srm_dev->clk_list_head, list) { ++ if (cfg->index != U32_MAX) { ++ if (ci->index == cfg->index) ++ return ci; ++ } else { ++ if (!strcmp(ci->name, cfg->name)) ++ return ci; ++ } ++ } ++ ++ return NULL; ++} ++ ++static int rproc_srm_dev_clock_set_cfg(struct rproc_srm_dev *rproc_srm_dev, ++ struct clock_cfg *cfg) ++{ ++ struct rproc_srm_clk_info *c; ++ struct device *dev = rproc_srm_dev->dev; ++ int ret; ++ ++ c = rproc_srm_dev_clock_find(rproc_srm_dev, cfg); ++ ++ if (!c) { ++ dev_err(dev, "unknown clock (id %d)\n", cfg->index); ++ return -EINVAL; ++ } ++ ++ if (cfg->rate && clk_get_rate(c->clk) != cfg->rate) { ++ ret = clk_set_rate(c->clk, cfg->rate); ++ if (ret) { ++ dev_err(dev, "clk set rate failed\n"); ++ return ret; ++ } ++ ++ dev_dbg(dev, "clk %d (%s) rate = %d\n", c->index, c->name, ++ cfg->rate); ++ } ++ ++ return 0; ++} ++ ++static int rproc_srm_dev_clock_get_cfg(struct rproc_srm_dev *rproc_srm_dev, ++ struct clock_cfg *cfg) ++{ ++ struct rproc_srm_clk_info *c; ++ ++ c = rproc_srm_dev_clock_find(rproc_srm_dev, cfg); ++ if (!c) { ++ dev_err(rproc_srm_dev->dev, "unknown clock (%d)\n", cfg->index); ++ return -EINVAL; ++ } ++ ++ strlcpy(cfg->name, c->name, sizeof(cfg->name)); ++ cfg->index = c->index; ++ cfg->rate = (u32)clk_get_rate(c->clk); ++ ++ return 0; ++} ++ ++static void rproc_srm_dev_clocks_put(struct rproc_srm_dev *rproc_srm_dev) ++{ ++ struct device *dev = rproc_srm_dev->dev; ++ struct rproc_srm_clk_info *c, *tmp; ++ ++ list_for_each_entry_safe(c, tmp, &rproc_srm_dev->clk_list_head, list) { ++ clk_put(c->clk); ++ dev_dbg(dev, "put clock %d (%s)\n", c->index, c->name); ++ list_del(&c->list); ++ } ++} ++ ++static int rproc_srm_dev_clocks_get(struct rproc_srm_dev *rproc_srm_dev) ++{ ++ struct device *dev = rproc_srm_dev->dev; ++ struct device_node *np = dev->of_node; ++ struct rproc_srm_clk_info *c; ++ const char *name; ++ int nb_c, ret; ++ unsigned int i; ++ ++ if (!np) ++ return 0; ++ ++ nb_c = of_clk_get_parent_count(np); ++ if (!nb_c) ++ return 0; ++ ++ for (i = 0; i < nb_c; i++) { ++ c = devm_kzalloc(dev, sizeof(*c), GFP_KERNEL); ++ if (!c) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ c->clk = of_clk_get(np, i); ++ if (IS_ERR(c->clk)) { ++ dev_err(dev, "clock %d KO (%ld)\n", i, ++ PTR_ERR(c->clk)); ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ /* Note: "clock-names" is optional */ ++ if (!of_property_read_string_index(np, "clock-names", i, ++ &name)) ++ c->name = devm_kstrdup(dev, name, GFP_KERNEL); ++ else ++ c->name = devm_kstrdup(dev, "", GFP_KERNEL); ++ ++ c->index = i; ++ ++ list_add_tail(&c->list, &rproc_srm_dev->clk_list_head); ++ dev_dbg(dev, "got clock %d (%s)\n", c->index, c->name); ++ } ++ ++ return 0; ++ ++err: ++ rproc_srm_dev_clocks_put(rproc_srm_dev); ++ return ret; ++} ++ ++/* Regulators */ ++static void rproc_srm_dev_regus_unsetup(struct rproc_srm_dev *rproc_srm_dev) ++{ ++ struct rproc_srm_regu_info *r; ++ struct device *dev = rproc_srm_dev->dev; ++ ++ list_for_each_entry(r, &rproc_srm_dev->regu_list_head, list) { ++ if (!r->enabled) ++ continue; ++ ++ if (regulator_disable(r->regu)) { ++ dev_warn(dev, "regu %d disabled failed\n", r->index); ++ continue; ++ } ++ ++ r->enabled = false; ++ dev_dbg(dev, "regu %d (%s) disabled\n", r->index, r->name); ++ } ++} ++ ++static int rproc_srm_dev_regus_setup(struct rproc_srm_dev *rproc_srm_dev) ++{ ++ struct rproc_srm_regu_info *r; ++ int ret; ++ ++ /* Enable all the regulators */ ++ list_for_each_entry(r, &rproc_srm_dev->regu_list_head, list) { ++ if (r->enabled) ++ continue; ++ ++ /* in early_boot mode sync on hw */ ++ if (rproc_srm_dev->early_boot && !regulator_is_enabled(r->regu)) ++ continue; ++ ++ ret = regulator_enable(r->regu); ++ if (ret) { ++ dev_err(rproc_srm_dev->dev, "regu %d (%s) failed\n", ++ r->index, r->name); ++ rproc_srm_dev_regus_unsetup(rproc_srm_dev); ++ return ret; ++ } ++ r->enabled = true; ++ dev_dbg(rproc_srm_dev->dev, "regu %d (%s) enabled\n", ++ r->index, r->name); ++ } ++ ++ return 0; ++} ++ ++static struct rproc_srm_regu_info ++ *rproc_srm_dev_regu_find(struct rproc_srm_dev *rproc_srm_dev, ++ struct regu_cfg *cfg) ++{ ++ struct rproc_srm_regu_info *ri; ++ ++ list_for_each_entry(ri, &rproc_srm_dev->regu_list_head, list) { ++ if (cfg->index != U32_MAX) { ++ if (ri->index == cfg->index) ++ return ri; ++ } else { ++ if (!strcmp(ri->name, cfg->name)) ++ return ri; ++ } ++ } ++ ++ return NULL; ++} ++ ++static int rproc_srm_dev_regu_set_cfg(struct rproc_srm_dev *rproc_srm_dev, ++ struct regu_cfg *cfg) ++{ ++ struct rproc_srm_regu_info *r; ++ struct device *dev = rproc_srm_dev->dev; ++ int ret; ++ ++ r = rproc_srm_dev_regu_find(rproc_srm_dev, cfg); ++ if (!r) { ++ dev_err(dev, "unknown regu (%d)\n", cfg->index); ++ return -EINVAL; ++ } ++ ++ if (!r->enabled && cfg->enable) { ++ ret = regulator_enable(r->regu); ++ if (ret) { ++ dev_err(dev, "regu %d enable failed\n", r->index); ++ return ret; ++ } ++ r->enabled = true; ++ dev_dbg(dev, "regu %d (%s) enabled\n", r->index, r->name); ++ } else if (r->enabled && !cfg->enable) { ++ ret = regulator_disable(r->regu); ++ if (ret) { ++ dev_err(dev, "regu %d disable failed\n", r->index); ++ return ret; ++ } ++ r->enabled = false; ++ dev_dbg(dev, "regu %d (%s) disabled\n", r->index, r->name); ++ } ++ ++ if (cfg->min_voltage_mv || cfg->max_voltage_mv) { ++ ret = regulator_set_voltage(r->regu, cfg->min_voltage_mv * 1000, ++ cfg->max_voltage_mv * 1000); ++ if (ret) { ++ dev_err(dev, "regu %d set voltage failed\n", r->index); ++ return ret; ++ } ++ ++ dev_dbg(dev, "regu %d (%s) voltage = [%d - %d] mv\n", r->index, ++ r->name, cfg->min_voltage_mv, cfg->max_voltage_mv); ++ } ++ ++ return 0; ++} ++ ++static int rproc_srm_dev_regu_get_cfg(struct rproc_srm_dev *rproc_srm_dev, ++ struct regu_cfg *cfg) ++{ ++ struct rproc_srm_regu_info *r; ++ struct device *dev = rproc_srm_dev->dev; ++ int v; ++ ++ r = rproc_srm_dev_regu_find(rproc_srm_dev, cfg); ++ if (!r) { ++ dev_err(dev, "unknown regu (%d)\n", cfg->index); ++ return -EINVAL; ++ } ++ ++ strlcpy(cfg->name, r->name, sizeof(cfg->name)); ++ cfg->index = r->index; ++ cfg->enable = r->enabled; ++ cfg->min_voltage_mv = 0; ++ cfg->max_voltage_mv = 0; ++ ++ v = regulator_get_voltage(r->regu); ++ if (v < 0) { ++ dev_warn(dev, "cannot get %s voltage\n", r->name); ++ cfg->curr_voltage_mv = 0; ++ } else { ++ cfg->curr_voltage_mv = v / 1000; ++ } ++ ++ return 0; ++} ++ ++static void rproc_srm_dev_regus_put(struct rproc_srm_dev *rproc_srm_dev) ++{ ++ struct device *dev = rproc_srm_dev->dev; ++ struct rproc_srm_regu_info *r, *tmp; ++ ++ list_for_each_entry_safe(r, tmp, &rproc_srm_dev->regu_list_head, list) { ++ devm_regulator_put(r->regu); ++ dev_dbg(dev, "put regu %d (%s)\n", r->index, r->name); ++ list_del(&r->list); ++ } ++} ++ ++static int rproc_srm_dev_regus_get(struct rproc_srm_dev *rproc_srm_dev) ++{ ++ struct device *dev = rproc_srm_dev->dev; ++ struct device_node *np = dev->of_node; ++ struct property *p; ++ const char *n; ++ char *name; ++ struct rproc_srm_regu_info *r; ++ int ret, nb_s = 0; ++ ++ if (!np) ++ return 0; ++ ++ for_each_property_of_node(np, p) { ++ n = strstr(p->name, "-supply"); ++ if (!n || n == p->name) ++ continue; ++ ++ r = devm_kzalloc(dev, sizeof(*r), GFP_KERNEL); ++ if (!r) { ++ ret = -ENOMEM; ++ goto err_list; ++ } ++ ++ name = devm_kstrdup(dev, p->name, GFP_KERNEL); ++ name[strlen(p->name) - strlen("-supply")] = '\0'; ++ r->name = name; ++ ++ r->regu = devm_regulator_get(dev, r->name); ++ if (IS_ERR(r->regu)) { ++ dev_err(dev, "cannot get regu %s\n", r->name); ++ ret = -EINVAL; ++ goto err_list; ++ } ++ ++ r->index = nb_s++; ++ ++ list_add_tail(&r->list, &rproc_srm_dev->regu_list_head); ++ dev_dbg(dev, "got regu %d (%s)\n", r->index, r->name); ++ } ++ ++ return 0; ++ ++err_list: ++ rproc_srm_dev_regus_put(rproc_srm_dev); ++ return ret; ++} ++ ++/* Pins */ ++static int rproc_srm_dev_pin_set_cfg(struct rproc_srm_dev *rproc_srm_dev, ++ struct pin_cfg *cfg) ++{ ++ struct rproc_srm_pin_info *pi, *p = NULL; ++ struct device *dev = rproc_srm_dev->dev; ++ struct pinctrl_state *state; ++ int ret; ++ ++ list_for_each_entry(pi, &rproc_srm_dev->pin_list_head, list) { ++ if (!strcmp(pi->name, cfg->name)) { ++ p = pi; ++ break; ++ } ++ } ++ ++ if (!p) { ++ dev_err(dev, "unknown pin config (%s)\n", cfg->name); ++ return -EINVAL; ++ } ++ ++ state = pinctrl_lookup_state(rproc_srm_dev->pctrl, cfg->name); ++ if (IS_ERR(state)) { ++ dev_err(dev, "cannot get pin config (%s)\n", cfg->name); ++ return -EINVAL; ++ } ++ ++ ret = pinctrl_select_state(rproc_srm_dev->pctrl, state); ++ if (ret < 0) { ++ dev_err(dev, "cannot set pin config (%s)\n", cfg->name); ++ return ret; ++ } ++ ++ list_for_each_entry(pi, &rproc_srm_dev->pin_list_head, list) { ++ pi->selected = (pi == p); ++ } ++ ++ dev_dbg(dev, "pin config (%s) selected\n", p->name); ++ ++ return 0; ++} ++ ++static int rproc_srm_dev_pin_get_cfg(struct rproc_srm_dev *rproc_srm_dev, ++ struct pin_cfg *cfg) ++{ ++ struct rproc_srm_pin_info *p; ++ ++ list_for_each_entry(p, &rproc_srm_dev->pin_list_head, list) { ++ if (p->selected) { ++ strlcpy(cfg->name, p->name, sizeof(cfg->name)); ++ return 0; ++ } ++ } ++ ++ dev_warn(rproc_srm_dev->dev, "cannot find selected pin state\n"); ++ strcpy(cfg->name, ""); ++ ++ return 0; ++} ++ ++static int rproc_srm_dev_pins_setup(struct rproc_srm_dev *rproc_srm_dev) ++{ ++ struct rproc_srm_pin_info *p; ++ struct pin_cfg cfg = { .name = "rproc_default" }; ++ ++ if (rproc_srm_dev->early_boot) ++ /* in early_boot mode do not update pin config */ ++ return 0; ++ ++ /* set the "rproc_default" pin config if defined */ ++ list_for_each_entry(p, &rproc_srm_dev->pin_list_head, list) { ++ if (!strcmp(p->name, cfg.name)) ++ return rproc_srm_dev_pin_set_cfg(rproc_srm_dev, &cfg); ++ } ++ ++ return 0; ++} ++ ++static void rproc_srm_dev_pins_put(struct rproc_srm_dev *rproc_srm_dev) ++{ ++ struct device *dev = rproc_srm_dev->dev; ++ struct rproc_srm_pin_info *p, *tmp; ++ ++ list_for_each_entry_safe(p, tmp, &rproc_srm_dev->pin_list_head, list) { ++ devm_kfree(dev, p->name); ++ devm_kfree(dev, p); ++ dev_dbg(dev, "remove pin cfg %d (%s)\n", p->index, p->name); ++ list_del(&p->list); ++ } ++ ++ if (!IS_ERR_OR_NULL(rproc_srm_dev->pctrl)) { ++ devm_pinctrl_put(rproc_srm_dev->pctrl); ++ rproc_srm_dev->pctrl = NULL; ++ } ++} ++ ++static int rproc_srm_dev_pins_get(struct rproc_srm_dev *rproc_srm_dev) ++{ ++ struct device *dev = rproc_srm_dev->dev; ++ struct device_node *np = dev->of_node; ++ struct rproc_srm_pin_info *p; ++ int ret, nb_p; ++ unsigned int i; ++ const char *name; ++ ++ if (!np) ++ return 0; ++ ++ rproc_srm_dev->pctrl = devm_pinctrl_get(dev); ++ if (IS_ERR(rproc_srm_dev->pctrl)) ++ return 0; ++ ++ nb_p = of_property_count_strings(np, "pinctrl-names"); ++ if (nb_p <= 0) { ++ dev_err(dev, "pinctrl-names not defined\n"); ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ for (i = 0; i < nb_p; i++) { ++ p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); ++ if (!p) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ if (of_property_read_string_index(np, "pinctrl-names", i, ++ &name)) { ++ dev_err(dev, "no pinctrl-names (pin %d)\n", i); ++ ret = -EINVAL; ++ goto err; ++ } ++ p->name = devm_kstrdup(dev, name, GFP_KERNEL); ++ ++ if (!strcmp(p->name, PINCTRL_STATE_DEFAULT)) { ++ if (rproc_srm_dev->early_boot) ++ dev_warn(dev, "pin config potentially overwritten!\n"); ++ p->selected = true; ++ } ++ ++ p->index = i; ++ ++ list_add_tail(&p->list, &rproc_srm_dev->pin_list_head); ++ dev_dbg(dev, "found pin cfg %d (%s)\n", p->index, p->name); ++ } ++ return 0; ++ ++err: ++ rproc_srm_dev_pins_put(rproc_srm_dev); ++ return ret; ++} ++ ++/* Core */ ++static int rproc_srm_dev_notify_cb(struct notifier_block *nb, unsigned long evt, ++ void *data) ++{ ++ struct rproc_srm_dev *rproc_srm_dev = ++ container_of(nb, struct rproc_srm_dev, nb); ++ struct device *dev = rproc_srm_dev->dev; ++ struct rpmsg_srm_msg_desc *desc; ++ struct rpmsg_srm_msg *i, o; ++ int ret = 0; ++ ++ dev_dbg(dev, "%s\n", __func__); ++ ++ desc = (struct rpmsg_srm_msg_desc *)data; ++ i = desc->msg; ++ o = *i; ++ ++ /* Check if 'device_id' (name / addr ) matches this device */ ++ if (!strstr(dev_name(dev), i->device_id)) ++ return NOTIFY_DONE; ++ ++ switch (i->message_type) { ++ case RPROC_SRM_MSG_SETCONFIG: ++ switch (i->rsc_type) { ++ case RPROC_SRM_RSC_CLOCK: ++ ret = rproc_srm_dev_clock_set_cfg(rproc_srm_dev, ++ &i->clock_cfg); ++ if (!ret) ++ ret = rproc_srm_dev_clock_get_cfg(rproc_srm_dev, ++ &o.clock_cfg); ++ break; ++ case RPROC_SRM_RSC_PIN: ++ ret = rproc_srm_dev_pin_set_cfg(rproc_srm_dev, ++ &i->pin_cfg); ++ if (!ret) ++ ret = rproc_srm_dev_pin_get_cfg(rproc_srm_dev, ++ &o.pin_cfg); ++ break; ++ case RPROC_SRM_RSC_REGU: ++ ret = rproc_srm_dev_regu_set_cfg(rproc_srm_dev, ++ &i->regu_cfg); ++ if (!ret) ++ ret = rproc_srm_dev_regu_get_cfg(rproc_srm_dev, ++ &o.regu_cfg); ++ break; ++ default: ++ dev_warn(dev, "bad rsc type (%d)\n", i->rsc_type); ++ ret = -EINVAL; ++ break; ++ } ++ break; ++ case RPROC_SRM_MSG_GETCONFIG: ++ switch (i->rsc_type) { ++ case RPROC_SRM_RSC_CLOCK: ++ ret = rproc_srm_dev_clock_get_cfg(rproc_srm_dev, ++ &o.clock_cfg); ++ break; ++ case RPROC_SRM_RSC_PIN: ++ ret = rproc_srm_dev_pin_get_cfg(rproc_srm_dev, ++ &o.pin_cfg); ++ break; ++ case RPROC_SRM_RSC_REGU: ++ ret = rproc_srm_dev_regu_get_cfg(rproc_srm_dev, ++ &o.regu_cfg); ++ break; ++ default: ++ dev_warn(dev, "bad rsc type (%d)\n", i->rsc_type); ++ ret = -EINVAL; ++ break; ++ } ++ break; ++ default: ++ dev_warn(dev, "bad msg type (%d)\n", i->message_type); ++ ret = -EINVAL; ++ break; ++ } ++ ++ /* Send return msg */ ++ if (ret) ++ o.message_type = RPROC_SRM_MSG_ERROR; ++ ++ ret = rpmsg_srm_send(desc->ept, &o); ++ ++ return ret ? NOTIFY_BAD : NOTIFY_STOP; ++} ++ ++static void ++rproc_srm_dev_unbind(struct device *dev, struct device *master, void *data) ++{ ++ struct rproc_srm_dev *rproc_srm_dev = dev_get_drvdata(dev); ++ ++ dev_dbg(dev, "%s\n", __func__); ++ ++ rproc_srm_dev_regus_unsetup(rproc_srm_dev); ++ rproc_srm_dev_clocks_unsetup(rproc_srm_dev); ++ ++ /* For pins and IRQs: nothing to unsetup */ ++} ++ ++static int ++rproc_srm_dev_bind(struct device *dev, struct device *master, void *data) ++{ ++ struct rproc_srm_dev *rproc_srm_dev = dev_get_drvdata(dev); ++ int ret; ++ ++ dev_dbg(dev, "%s\n", __func__); ++ ++ ret = rproc_srm_dev_clocks_setup(rproc_srm_dev); ++ if (ret) ++ return ret; ++ ++ ret = rproc_srm_dev_regus_setup(rproc_srm_dev); ++ if (ret) ++ return ret; ++ ++ ret = rproc_srm_dev_pins_setup(rproc_srm_dev); ++ if (ret) ++ return ret; ++ ++ /* For IRQs: nothing to setup */ ++ return 0; ++} ++ ++static const struct component_ops rproc_srm_dev_ops = { ++ .bind = rproc_srm_dev_bind, ++ .unbind = rproc_srm_dev_unbind, ++}; ++ ++static int rproc_srm_dev_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct rproc_srm_dev *rproc_srm_dev; ++ struct rproc *rproc; ++ int ret; ++ ++ dev_dbg(dev, "%s for node %s\n", __func__, dev->of_node->name); ++ ++ rproc_srm_dev = devm_kzalloc(dev, sizeof(struct rproc_srm_dev), ++ GFP_KERNEL); ++ if (!rproc_srm_dev) ++ return -ENOMEM; ++ ++ rproc_srm_dev->dev = dev; ++ rproc = (struct rproc *)dev_get_drvdata(dev->parent->parent); ++ rproc_srm_dev->early_boot = rproc->early_boot; ++ rproc_srm_dev->core = dev_get_drvdata(dev->parent); ++ ++ INIT_LIST_HEAD(&rproc_srm_dev->clk_list_head); ++ INIT_LIST_HEAD(&rproc_srm_dev->pin_list_head); ++ INIT_LIST_HEAD(&rproc_srm_dev->regu_list_head); ++ INIT_LIST_HEAD(&rproc_srm_dev->irq_list_head); ++ ++ /* Get clocks, regu, irqs and pinctrl */ ++ ret = rproc_srm_dev_clocks_get(rproc_srm_dev); ++ if (ret) ++ return ret; ++ ++ ret = rproc_srm_dev_regus_get(rproc_srm_dev); ++ if (ret) ++ goto err_get; ++ ++ ret = rproc_srm_dev_pins_get(rproc_srm_dev); ++ if (ret) ++ goto err_get; ++ ++ ret = rproc_srm_dev_irqs_get(rproc_srm_dev); ++ if (ret) ++ goto err_get; ++ ++ rproc_srm_dev->nb.notifier_call = rproc_srm_dev_notify_cb; ++ ret = rproc_srm_core_register_notifier(rproc_srm_dev->core, ++ &rproc_srm_dev->nb); ++ if (ret) ++ goto err_register; ++ ++ dev_set_drvdata(dev, rproc_srm_dev); ++ ++ return component_add(dev, &rproc_srm_dev_ops); ++ ++err_register: ++ rproc_srm_core_unregister_notifier(rproc_srm_dev->core, ++ &rproc_srm_dev->nb); ++err_get: ++ rproc_srm_dev_irqs_put(rproc_srm_dev); ++ rproc_srm_dev_pins_put(rproc_srm_dev); ++ rproc_srm_dev_regus_put(rproc_srm_dev); ++ rproc_srm_dev_clocks_put(rproc_srm_dev); ++ return ret; ++} ++ ++static int rproc_srm_dev_remove(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct rproc_srm_dev *rproc_srm_dev = dev_get_drvdata(dev); ++ ++ dev_dbg(dev, "%s\n", __func__); ++ ++ component_del(dev, &rproc_srm_dev_ops); ++ ++ rproc_srm_core_unregister_notifier(rproc_srm_dev->core, ++ &rproc_srm_dev->nb); ++ ++ rproc_srm_dev_irqs_put(rproc_srm_dev); ++ rproc_srm_dev_regus_put(rproc_srm_dev); ++ rproc_srm_dev_pins_put(rproc_srm_dev); ++ rproc_srm_dev_clocks_put(rproc_srm_dev); ++ ++ return 0; ++} ++ ++static const struct of_device_id rproc_srm_dev_match[] = { ++ { .compatible = "rproc-srm-dev", }, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, rproc_srm_dev_match); ++ ++static struct platform_driver rproc_srm_dev_driver = { ++ .probe = rproc_srm_dev_probe, ++ .remove = rproc_srm_dev_remove, ++ .driver = { ++ .name = "rproc-srm-dev", ++ .of_match_table = of_match_ptr(rproc_srm_dev_match), ++ }, ++}; ++ ++module_platform_driver(rproc_srm_dev_driver); ++ ++MODULE_AUTHOR("Fabien Dessenne "); ++MODULE_DESCRIPTION("Remoteproc System Resource Manager driver - dev"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c +new file mode 100644 +index 0000000..998de67 +--- /dev/null ++++ b/drivers/remoteproc/stm32_rproc.c +@@ -0,0 +1,585 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved ++ * Authors: Ludovic Barre for STMicroelectronics. ++ * Fabien Dessenne for STMicroelectronics. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "remoteproc_internal.h" ++ ++#define HOLD_BOOT 0 ++#define RELEASE_BOOT 1 ++ ++#define MBOX_NB_VQ 2 ++#define MBOX_NB_MBX 3 ++ ++#define STM32_SMC_RCC 0x82001000 ++#define STM32_SMC_REG_WRITE 0x1 ++ ++#define STM32_MBX_VQ0 "vq0" ++#define STM32_MBX_VQ1 "vq1" ++#define STM32_MBX_SHUTDOWN "init_shdn" ++ ++struct stm32_syscon { ++ struct regmap *map; ++ u32 reg; ++ u32 mask; ++}; ++ ++struct stm32_rproc_mem { ++ void __iomem *cpu_addr; ++ phys_addr_t bus_addr; ++ u32 dev_addr; ++ size_t size; ++}; ++ ++struct stm32_mbox { ++ const unsigned char name[10]; ++ struct mbox_chan *chan; ++ struct mbox_client client; ++ int vq_id; ++}; ++ ++struct stm32_rproc { ++ struct reset_control *rst; ++ struct stm32_syscon hold_boot; ++ struct stm32_syscon pdds; ++ struct stm32_rproc_mem ram[2]; ++ struct stm32_mbox mb[MBOX_NB_MBX]; ++ bool secured_soc; ++ u32 rsc_addr; ++ u32 rsc_len; ++}; ++ ++static int stm32_rproc_elf_load_segments(struct rproc *rproc, ++ const struct firmware *fw) ++{ ++ if (!rproc->early_boot) ++ return rproc_elf_load_segments(rproc, fw); ++ ++ return 0; ++} ++ ++static int stm32_rproc_mbox_idx(struct rproc *rproc, const unsigned char *name) ++{ ++ struct stm32_rproc *ddata = rproc->priv; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(ddata->mb); i++) { ++ if (!strncmp(ddata->mb[i].name, name, strlen(name))) ++ return i; ++ } ++ dev_err(&rproc->dev, "mailbox %s not found\n", name); ++ ++ return -EINVAL; ++} ++ ++static int stm32_rproc_elf_load_rsc_table(struct rproc *rproc, ++ const struct firmware *fw) ++{ ++ int status; ++ struct resource_table *table = NULL; ++ struct stm32_rproc *ddata = rproc->priv; ++ size_t tablesz = 0; ++ ++ if (!rproc->early_boot) { ++ status = rproc_elf_load_rsc_table(rproc, fw); ++ if (status) ++ goto no_rsc_table; ++ ++ return 0; ++ } ++ ++ if (ddata->rsc_addr) { ++ tablesz = ddata->rsc_len; ++ table = (struct resource_table *) ++ rproc_da_to_va(rproc, (u64)ddata->rsc_addr, ++ ddata->rsc_len); ++ rproc->cached_table = kmemdup(table, tablesz, GFP_KERNEL); ++ if (!rproc->cached_table) ++ return -ENOMEM; ++ ++ rproc->table_ptr = rproc->cached_table; ++ rproc->table_sz = tablesz; ++ return 0; ++ } ++ ++ rproc->cached_table = NULL; ++ rproc->table_ptr = NULL; ++ rproc->table_sz = 0; ++ ++no_rsc_table: ++ dev_warn(&rproc->dev, "not resource table found for this firmware\n"); ++ return 0; ++} ++ ++static struct resource_table * ++stm32_rproc_elf_find_loaded_rsc_table(struct rproc *rproc, ++ const struct firmware *fw) ++{ ++ struct stm32_rproc *ddata = rproc->priv; ++ ++ if (!rproc->early_boot) ++ return rproc_elf_find_loaded_rsc_table(rproc, fw); ++ ++ if (ddata->rsc_addr) ++ return (struct resource_table *) ++ rproc_da_to_va(rproc, (u64)ddata->rsc_addr, ++ ddata->rsc_len); ++ ++ return NULL; ++} ++ ++static int stm32_rproc_elf_sanity_check(struct rproc *rproc, ++ const struct firmware *fw) ++{ ++ if (!rproc->early_boot) ++ return rproc_elf_sanity_check(rproc, fw); ++ ++ return 0; ++} ++ ++static u32 stm32_rproc_elf_get_boot_addr(struct rproc *rproc, ++ const struct firmware *fw) ++{ ++ if (!rproc->early_boot) ++ return rproc_elf_get_boot_addr(rproc, fw); ++ ++ return 0; ++} ++ ++static irqreturn_t stm32_rproc_wdg(int irq, void *data) ++{ ++ struct rproc *rproc = data; ++ ++ rproc_report_crash(rproc, RPROC_WATCHDOG); ++ ++ return IRQ_HANDLED; ++} ++ ++static void stm32_rproc_mb_callback(struct mbox_client *cl, void *data) ++{ ++ struct rproc *rproc = dev_get_drvdata(cl->dev); ++ struct stm32_mbox *mb = container_of(cl, struct stm32_mbox, client); ++ ++ if (rproc_vq_interrupt(rproc, mb->vq_id) == IRQ_NONE) ++ dev_dbg(&rproc->dev, "no message found in vq%d\n", mb->vq_id); ++} ++ ++static void stm32_rproc_free_mbox(struct rproc *rproc) ++{ ++ struct stm32_rproc *ddata = rproc->priv; ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(ddata->mb); i++) { ++ if (ddata->mb[i].chan) ++ mbox_free_channel(ddata->mb[i].chan); ++ ddata->mb[i].chan = NULL; ++ } ++} ++ ++static const struct stm32_mbox stm32_rproc_mbox[MBOX_NB_MBX] = { ++ { ++ .name = STM32_MBX_VQ0, ++ .vq_id = 0, ++ .client = { ++ .rx_callback = stm32_rproc_mb_callback, ++ .tx_block = false, ++ }, ++ }, ++ { ++ .name = STM32_MBX_VQ1, ++ .vq_id = 1, ++ .client = { ++ .rx_callback = stm32_rproc_mb_callback, ++ .tx_block = false, ++ }, ++ }, ++ { ++ .name = STM32_MBX_SHUTDOWN, ++ .vq_id = -1, ++ .client = { ++ .tx_block = true, ++ .tx_done = NULL, ++ .tx_tout = 500, /* 500 ms time out */ ++ }, ++ } ++}; ++ ++static void stm32_rproc_request_mbox(struct rproc *rproc) ++{ ++ struct stm32_rproc *ddata = rproc->priv; ++ struct device *dev = &rproc->dev; ++ unsigned int i; ++ const unsigned char *name; ++ struct mbox_client *cl; ++ ++ /* Initialise mailbox structure table */ ++ memcpy(ddata->mb, stm32_rproc_mbox, sizeof(stm32_rproc_mbox)); ++ ++ for (i = 0; i < MBOX_NB_MBX; i++) { ++ name = ddata->mb[i].name; ++ ++ cl = &ddata->mb[i].client; ++ cl->dev = dev->parent; ++ ++ ddata->mb[i].chan = mbox_request_channel_byname(cl, name); ++ if (IS_ERR(ddata->mb[i].chan)) { ++ dev_warn(dev, "cannot get %s mbox\n", name); ++ ddata->mb[i].chan = NULL; ++ } ++ } ++} ++ ++static int stm32_rproc_set_hold_boot(struct rproc *rproc, bool hold) ++{ ++ struct stm32_rproc *ddata = rproc->priv; ++ struct stm32_syscon hold_boot = ddata->hold_boot; ++ struct arm_smccc_res smc_res; ++ int val, err; ++ ++ val = hold ? HOLD_BOOT : RELEASE_BOOT; ++ ++ if (ddata->secured_soc) { ++ arm_smccc_smc(STM32_SMC_RCC, STM32_SMC_REG_WRITE, ++ hold_boot.reg, val, 0, 0, 0, 0, &smc_res); ++ err = smc_res.a0; ++ } else { ++ err = regmap_update_bits(hold_boot.map, hold_boot.reg, ++ hold_boot.mask, val); ++ } ++ ++ if (err) ++ dev_err(&rproc->dev, "failed to set hold boot\n"); ++ ++ return err; ++} ++ ++static int stm32_rproc_start(struct rproc *rproc) ++{ ++ int err; ++ ++ /* ++ * If M4 previously started by bootloader, just guarantee holdboot ++ * is set to catch any crash. ++ */ ++ if (!rproc->early_boot) { ++ err = stm32_rproc_set_hold_boot(rproc, false); ++ if (err) ++ return err; ++ } ++ ++ return stm32_rproc_set_hold_boot(rproc, true); ++} ++ ++static int stm32_rproc_stop(struct rproc *rproc) ++{ ++ struct stm32_rproc *ddata = rproc->priv; ++ int err, dummy_data, idx; ++ ++ /* request shutdown of the remote processor */ ++ if (rproc->state != RPROC_OFFLINE) { ++ idx = stm32_rproc_mbox_idx(rproc, STM32_MBX_SHUTDOWN); ++ if (idx >= 0 && ddata->mb[idx].chan) { ++ /* a dummy data is sent to allow to block on transmit */ ++ err = mbox_send_message(ddata->mb[idx].chan, ++ &dummy_data); ++ if (err < 0) ++ dev_warn(&rproc->dev, "warning: remote FW shutdown without ack\n"); ++ } ++ } ++ ++ err = stm32_rproc_set_hold_boot(rproc, true); ++ if (err) ++ return err; ++ ++ err = reset_control_assert(ddata->rst); ++ if (err) { ++ dev_err(&rproc->dev, "failed to assert the reset\n"); ++ return err; ++ } ++ ++ /* to allow platform Standby power mode, set remote proc Deep Sleep */ ++ if (ddata->pdds.map) { ++ err = regmap_update_bits(ddata->pdds.map, ddata->pdds.reg, ++ ddata->pdds.mask, 1); ++ if (err) { ++ dev_err(&rproc->dev, "failed to set pdds\n"); ++ return err; ++ } ++ } ++ ++ /* Reset early_boot state as we stop the co-processor */ ++ rproc->early_boot = false; ++ ++ return 0; ++} ++ ++static void stm32_rproc_kick(struct rproc *rproc, int vqid) ++{ ++ struct stm32_rproc *ddata = rproc->priv; ++ unsigned int i; ++ int err; ++ ++ if (WARN_ON(vqid >= MBOX_NB_VQ)) ++ return; ++ ++ for (i = 0; i < MBOX_NB_MBX; i++) { ++ if (vqid != ddata->mb[i].vq_id) ++ continue; ++ if (!ddata->mb[i].chan) ++ return; ++ err = mbox_send_message(ddata->mb[i].chan, (void *)vqid); ++ if (err < 0) ++ dev_err(&rproc->dev, "%s: failed (%s, err:%d)\n", ++ __func__, ddata->mb[i].name, err); ++ return; ++ } ++} ++ ++static void *stm32_rproc_da_to_va(struct rproc *rproc, u64 da, int len) ++{ ++ struct stm32_rproc *ddata = rproc->priv; ++ void *va = NULL; ++ u32 offset; ++ unsigned int i; ++ ++ for (i = 0; i < 2; i++) { ++ if (da >= ddata->ram[i].dev_addr && da + len <= ++ ddata->ram[i].dev_addr + ddata->ram[i].size) { ++ offset = da - ddata->ram[i].dev_addr; ++ /* __force to make sparse happy with type conversion */ ++ va = (__force void *)(ddata->ram[i].cpu_addr + offset); ++ break; ++ } ++ } ++ ++ return va; ++} ++ ++static struct rproc_ops st_rproc_ops = { ++ .start = stm32_rproc_start, ++ .stop = stm32_rproc_stop, ++ .kick = stm32_rproc_kick, ++ .da_to_va = stm32_rproc_da_to_va, ++ .load = stm32_rproc_elf_load_segments, ++ .parse_fw = stm32_rproc_elf_load_rsc_table, ++ .find_loaded_rsc_table = stm32_rproc_elf_find_loaded_rsc_table, ++ .sanity_check = stm32_rproc_elf_sanity_check, ++ .get_boot_addr = stm32_rproc_elf_get_boot_addr, ++}; ++ ++static const struct of_device_id stm32_rproc_match[] = { ++ { .compatible = "st,stm32mp1-rproc" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, stm32_rproc_match); ++ ++static int stm32_rproc_get_syscon(struct device_node *np, const char *prop, ++ struct stm32_syscon *syscon) ++{ ++ int err = 0; ++ ++ syscon->map = syscon_regmap_lookup_by_phandle(np, prop); ++ if (IS_ERR(syscon->map)) { ++ err = PTR_ERR(syscon->map); ++ syscon->map = NULL; ++ goto out; ++ } ++ ++ err = of_property_read_u32_index(np, prop, 1, &syscon->reg); ++ if (err) ++ goto out; ++ ++ err = of_property_read_u32_index(np, prop, 2, &syscon->mask); ++ ++out: ++ return err; ++} ++ ++static int stm32_rproc_parse_dt(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct rproc *rproc = platform_get_drvdata(pdev); ++ struct stm32_rproc *ddata = rproc->priv; ++ struct resource *res; ++ struct stm32_syscon tz; ++ unsigned int tzen, i = 0; ++ int err, irq; ++ ++ irq = platform_get_irq_byname(pdev, "wdg"); ++ if (irq > 0) { ++ err = devm_request_irq(dev, irq, stm32_rproc_wdg, 0, ++ dev_name(dev), rproc); ++ if (err) { ++ dev_err(dev, "failed to request wdg irq\n"); ++ return err; ++ } ++ ++ dev_info(dev, "wdg irq registered\n"); ++ } ++ ++ ddata->rst = devm_reset_control_get(dev, "mcu_rst"); ++ if (IS_ERR(ddata->rst)) { ++ dev_err(dev, "failed to get mcu reset\n"); ++ return PTR_ERR(ddata->rst); ++ } ++ ++ /* ++ * if platform is secured the hold boot bit must be written by ++ * smc call and read normally. ++ * if not secure the hold boot bit could be read/write normally ++ */ ++ err = stm32_rproc_get_syscon(np, "st,syscfg-tz", &tz); ++ if (err) { ++ dev_err(dev, "failed to get tz syscfg\n"); ++ return err; ++ } ++ ++ err = regmap_read(tz.map, tz.reg, &tzen); ++ if (err) { ++ dev_err(&rproc->dev, "failed to read tzen\n"); ++ return err; ++ } ++ ddata->secured_soc = tzen & tz.mask; ++ ++ err = stm32_rproc_get_syscon(np, "st,syscfg-holdboot", ++ &ddata->hold_boot); ++ if (err) { ++ dev_err(dev, "failed to get hold boot\n"); ++ return err; ++ } ++ ++ err = stm32_rproc_get_syscon(np, "st,syscfg-pdds", &ddata->pdds); ++ if (err) ++ dev_warn(dev, "failed to get pdds\n"); ++ ++ while ((res = platform_get_resource(pdev, IORESOURCE_MEM, i))) { ++ ddata->ram[i].cpu_addr = devm_ioremap_resource(dev, res); ++ if (IS_ERR(ddata->ram[i].cpu_addr)) ++ return err; ++ ++ ddata->ram[i].bus_addr = res->start; ++ ddata->ram[i].size = resource_size(res); ++ ++ /* ++ * the m4 has retram at address 0 in its view (DA) ++ * so for retram DA=0x0 PA=bus_addr else DA=PA=bus_addr ++ */ ++ if (i == 0) ++ ddata->ram[i].dev_addr = 0x0; ++ else ++ ddata->ram[i].dev_addr = ddata->ram[i].bus_addr; ++ ++ i++; ++ } ++ ++ rproc->auto_boot = of_property_read_bool(np, "auto_boot"); ++ rproc->recovery_disabled = !of_property_read_bool(np, "recovery"); ++ ++ if (of_property_read_bool(np, "early-booted")) { ++ rproc->early_boot = true; ++ ++ err = of_property_read_u32(np, "rsc-address", &ddata->rsc_addr); ++ if (!err) { ++ err = of_property_read_u32(np, "rsc-size", ++ &ddata->rsc_len); ++ ++ if (err) { ++ dev_err(dev, "resource table size required as address defined\n"); ++ return err; ++ } ++ } ++ } ++ ++ of_reserved_mem_device_init(dev); ++ ++ return 0; ++} ++ ++static int stm32_rproc_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct stm32_rproc *ddata; ++ struct device_node *np = dev->of_node; ++ struct rproc *rproc; ++ int ret; ++ ++ rproc = rproc_alloc(dev, np->name, &st_rproc_ops, NULL, sizeof(*ddata)); ++ if (!rproc) ++ return -ENOMEM; ++ ++ rproc->has_iommu = false; ++ ddata = rproc->priv; ++ ++ platform_set_drvdata(pdev, rproc); ++ ++ ret = stm32_rproc_parse_dt(pdev); ++ if (ret) ++ goto free_rproc; ++ ++ if (!rproc->early_boot) { ++ ret = stm32_rproc_stop(rproc); ++ if (ret) ++ goto free_mem; ++ } ++ ++ stm32_rproc_request_mbox(rproc); ++ ++ ret = rproc_add(rproc); ++ if (ret) ++ goto free_mb; ++ ++ return 0; ++ ++free_mb: ++ stm32_rproc_free_mbox(rproc); ++free_mem: ++ of_reserved_mem_device_release(dev); ++free_rproc: ++ rproc_free(rproc); ++ return ret; ++} ++ ++static int stm32_rproc_remove(struct platform_device *pdev) ++{ ++ struct rproc *rproc = platform_get_drvdata(pdev); ++ struct device *dev = &pdev->dev; ++ ++ if (atomic_read(&rproc->power) > 0) ++ dev_warn(dev, "Releasing rproc while firmware running!\n"); ++ ++ rproc_del(rproc); ++ stm32_rproc_free_mbox(rproc); ++ of_reserved_mem_device_release(dev); ++ rproc_free(rproc); ++ ++ return 0; ++} ++ ++static struct platform_driver stm32_rproc_driver = { ++ .probe = stm32_rproc_probe, ++ .remove = stm32_rproc_remove, ++ .driver = { ++ .name = "stm32-rproc", ++ .of_match_table = of_match_ptr(stm32_rproc_match), ++ }, ++}; ++module_platform_driver(stm32_rproc_driver); ++ ++MODULE_DESCRIPTION("STM32 Remote Processor Control Driver"); ++MODULE_AUTHOR("Ludovic Barre "); ++MODULE_LICENSE("GPL v2"); ++ +diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig +index d0322b4..88759a4 100644 +--- a/drivers/rpmsg/Kconfig ++++ b/drivers/rpmsg/Kconfig +@@ -55,4 +55,13 @@ config RPMSG_VIRTIO + select RPMSG + select VIRTIO + ++config RPMSG_TTY ++ tristate "RPMSG tty driver" ++ depends on RPMSG ++ help ++ Say y here to export rpmsg endpoints as tty console, usually found ++ in /dev/ttyRPMSG. ++ This makes it possible for user-space programs to send and receive ++ rpmsg messages as a standard tty protocol. ++ + endmenu +diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile +index 9aa8595..107145c 100644 +--- a/drivers/rpmsg/Makefile ++++ b/drivers/rpmsg/Makefile +@@ -5,4 +5,5 @@ obj-$(CONFIG_RPMSG_QCOM_GLINK_RPM) += qcom_glink_rpm.o + obj-$(CONFIG_RPMSG_QCOM_GLINK_NATIVE) += qcom_glink_native.o + obj-$(CONFIG_RPMSG_QCOM_GLINK_SMEM) += qcom_glink_smem.o + obj-$(CONFIG_RPMSG_QCOM_SMD) += qcom_smd.o ++obj-$(CONFIG_RPMSG_TTY) += rpmsg_tty.o + obj-$(CONFIG_RPMSG_VIRTIO) += virtio_rpmsg_bus.o +diff --git a/drivers/rpmsg/rpmsg_core.c b/drivers/rpmsg/rpmsg_core.c +index 8122807..edb8549 100644 +--- a/drivers/rpmsg/rpmsg_core.c ++++ b/drivers/rpmsg/rpmsg_core.c +@@ -283,6 +283,25 @@ int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst, + } + EXPORT_SYMBOL(rpmsg_trysend_offchannel); + ++/** ++ * rpmsg_get_buffer_size() ++ * This function returns buffer size available for sending messages. ++ * ++ * @ept: the rpmsg endpoint ++ * ++ * Returns buffer size on success and an appropriate error value on failure. ++ */ ++int rpmsg_get_buffer_size(struct rpmsg_endpoint *ept) ++{ ++ if (WARN_ON(!ept)) ++ return -EINVAL; ++ if (!ept->ops->get_buffer_size) ++ return -ENXIO; ++ ++ return ept->ops->get_buffer_size(ept); ++} ++EXPORT_SYMBOL(rpmsg_get_buffer_size); ++ + /* + * match an rpmsg channel with a channel info struct. + * this is used to make sure we're not creating rpmsg devices for channels +diff --git a/drivers/rpmsg/rpmsg_internal.h b/drivers/rpmsg/rpmsg_internal.h +index 0d791c3..65bcb52 100644 +--- a/drivers/rpmsg/rpmsg_internal.h ++++ b/drivers/rpmsg/rpmsg_internal.h +@@ -46,6 +46,7 @@ struct rpmsg_device_ops { + * @trysend: see @rpmsg_trysend(), required + * @trysendto: see @rpmsg_trysendto(), optional + * @trysend_offchannel: see @rpmsg_trysend_offchannel(), optional ++ * @get_buffer_size: see @rpmsg_get_buffer_size(), optional + * + * Indirection table for the operations that a rpmsg backend should implement. + * In addition to @destroy_ept, the backend must at least implement @send and +@@ -65,6 +66,7 @@ struct rpmsg_endpoint_ops { + void *data, int len); + __poll_t (*poll)(struct rpmsg_endpoint *ept, struct file *filp, + poll_table *wait); ++ int (*get_buffer_size)(struct rpmsg_endpoint *ept); + }; + + int rpmsg_register_device(struct rpmsg_device *rpdev); +diff --git a/drivers/rpmsg/rpmsg_tty.c b/drivers/rpmsg/rpmsg_tty.c +new file mode 100644 +index 0000000..2826118 +--- /dev/null ++++ b/drivers/rpmsg/rpmsg_tty.c +@@ -0,0 +1,305 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved ++ * Author: Arnaud Pouliquen for STMicroelectronics. ++ * Derived from the imx-rmpsg and omap-rpmsg implementations. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define MAX_TTY_RPMSG_INDEX 32 /* Should be enough for a while */ ++ ++static LIST_HEAD(rpmsg_tty_list); /* tty instances list */ ++static DEFINE_MUTEX(rpmsg_tty_lock); /* protect tty list */ ++ ++static struct tty_driver *rpmsg_tty_driver; ++static struct tty_port_operations rpmsg_tty_port_ops = { }; ++ ++struct rpmsg_tty_port { ++ struct tty_port port; /* TTY port data */ ++ struct list_head list; /* TTY device list */ ++ u32 id; /* tty rpmsg index */ ++ spinlock_t rx_lock; /* message reception lock */ ++ struct rpmsg_device *rpdev; /* handle rpmsg device */ ++}; ++ ++static int rpmsg_tty_cb(struct rpmsg_device *rpdev, void *data, int len, ++ void *priv, u32 src) ++{ ++ int space; ++ unsigned char *cbuf; ++ struct rpmsg_tty_port *cport = dev_get_drvdata(&rpdev->dev); ++ ++ /* Flush the recv-ed none-zero data to tty node */ ++ if (len == 0) ++ return -EINVAL; ++ ++ dev_dbg(&rpdev->dev, "msg(<- src 0x%x) len %d\n", src, len); ++ ++ print_hex_dump_debug(__func__, DUMP_PREFIX_NONE, 16, 1, data, len, ++ true); ++ ++ spin_lock(&cport->rx_lock); ++ space = tty_prepare_flip_string(&cport->port, &cbuf, len); ++ if (space <= 0) { ++ dev_err(&rpdev->dev, "No memory for tty_prepare_flip_string\n"); ++ spin_unlock(&cport->rx_lock); ++ return -ENOMEM; ++ } ++ ++ if (space != len) ++ dev_warn(&rpdev->dev, "Trunc buffer from %d to %d\n", ++ len, space); ++ ++ memcpy(cbuf, data, space); ++ tty_flip_buffer_push(&cport->port); ++ spin_unlock(&cport->rx_lock); ++ ++ return 0; ++} ++ ++static struct rpmsg_tty_port *rpmsg_tty_get(unsigned int index) ++{ ++ struct rpmsg_tty_port *cport; ++ ++ mutex_lock(&rpmsg_tty_lock); ++ list_for_each_entry(cport, &rpmsg_tty_list, list) { ++ if (index == cport->id) { ++ mutex_unlock(&rpmsg_tty_lock); ++ return cport; ++ } ++ } ++ mutex_unlock(&rpmsg_tty_lock); ++ ++ return NULL; ++} ++ ++static int rpmsg_tty_install(struct tty_driver *driver, struct tty_struct *tty) ++{ ++ struct rpmsg_tty_port *cport = rpmsg_tty_get(tty->index); ++ ++ if (!cport) ++ return -ENODEV; ++ ++ return tty_port_install(&cport->port, driver, tty); ++} ++ ++static int rpmsg_tty_open(struct tty_struct *tty, struct file *filp) ++{ ++ struct rpmsg_tty_port *cport = rpmsg_tty_get(tty->index); ++ ++ if (!cport) ++ return -ENODEV; ++ ++ return tty_port_open(tty->port, tty, filp); ++} ++ ++static void rpmsg_tty_close(struct tty_struct *tty, struct file *filp) ++{ ++ struct rpmsg_tty_port *cport = rpmsg_tty_get(tty->index); ++ ++ if (!cport) ++ return; ++ return tty_port_close(tty->port, tty, filp); ++} ++ ++static int rpmsg_tty_write(struct tty_struct *tty, const unsigned char *buf, ++ int total) ++{ ++ int count, ret = 0; ++ const unsigned char *tbuf; ++ struct rpmsg_tty_port *cport = container_of(tty->port, ++ struct rpmsg_tty_port, port); ++ struct rpmsg_device *rpdev = cport->rpdev; ++ int msg_size; ++ ++ dev_dbg(&rpdev->dev, "%s: send message from tty->index = %d\n", ++ __func__, tty->index); ++ ++ if (!buf) { ++ dev_err(&rpdev->dev, "buf shouldn't be null.\n"); ++ return -ENOMEM; ++ } ++ ++ msg_size = rpmsg_get_buffer_size(rpdev->ept); ++ if (msg_size < 0) ++ return msg_size; ++ ++ count = total; ++ tbuf = buf; ++ do { ++ /* send a message to our remote processor */ ++ ret = rpmsg_send(rpdev->ept, (void *)tbuf, ++ count > msg_size ? msg_size : count); ++ if (ret) { ++ dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret); ++ return ret; ++ } ++ ++ if (count > msg_size) { ++ count -= msg_size; ++ tbuf += msg_size; ++ } else { ++ count = 0; ++ } ++ } while (count > 0); ++ ++ return total; ++} ++ ++static int rpmsg_tty_write_room(struct tty_struct *tty) ++{ ++ struct rpmsg_tty_port *cport = container_of(tty->port, ++ struct rpmsg_tty_port, port); ++ struct rpmsg_device *rpdev = cport->rpdev; ++ ++ /* report the space in the rpmsg buffer */ ++ return rpmsg_get_buffer_size(rpdev->ept); ++} ++ ++static const struct tty_operations rpmsg_tty_ops = { ++ .install = rpmsg_tty_install, ++ .open = rpmsg_tty_open, ++ .close = rpmsg_tty_close, ++ .write = rpmsg_tty_write, ++ .write_room = rpmsg_tty_write_room, ++}; ++ ++static int rpmsg_tty_probe(struct rpmsg_device *rpdev) ++{ ++ struct rpmsg_tty_port *cport, *tmp; ++ unsigned int index; ++ struct device *tty_dev; ++ ++ cport = devm_kzalloc(&rpdev->dev, sizeof(*cport), GFP_KERNEL); ++ if (!cport) ++ return -ENOMEM; ++ ++ tty_port_init(&cport->port); ++ cport->port.ops = &rpmsg_tty_port_ops; ++ spin_lock_init(&cport->rx_lock); ++ ++ cport->port.low_latency = cport->port.flags | ASYNC_LOW_LATENCY; ++ ++ cport->rpdev = rpdev; ++ ++ /* get free index */ ++ mutex_lock(&rpmsg_tty_lock); ++ for (index = 0; index < MAX_TTY_RPMSG_INDEX; index++) { ++ bool id_found = false; ++ ++ list_for_each_entry(tmp, &rpmsg_tty_list, list) { ++ if (index == tmp->id) { ++ id_found = true; ++ break; ++ } ++ } ++ if (!id_found) ++ break; ++ } ++ ++ tty_dev = tty_port_register_device(&cport->port, rpmsg_tty_driver, ++ index, &rpdev->dev); ++ if (IS_ERR(tty_dev)) { ++ dev_err(&rpdev->dev, "failed to register tty port\n"); ++ tty_port_destroy(&cport->port); ++ mutex_unlock(&rpmsg_tty_lock); ++ return PTR_ERR(tty_dev); ++ } ++ ++ cport->id = index; ++ list_add_tail(&cport->list, &rpmsg_tty_list); ++ mutex_unlock(&rpmsg_tty_lock); ++ dev_set_drvdata(&rpdev->dev, cport); ++ ++ dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x : ttyRPMSG%d\n", ++ rpdev->src, rpdev->dst, index); ++ ++ return 0; ++} ++ ++static void rpmsg_tty_remove(struct rpmsg_device *rpdev) ++{ ++ struct rpmsg_tty_port *cport = dev_get_drvdata(&rpdev->dev); ++ ++ /* User hang up to release the tty */ ++ if (tty_port_initialized(&cport->port)) ++ tty_port_tty_hangup(&cport->port, false); ++ tty_port_destroy(&cport->port); ++ tty_unregister_device(rpmsg_tty_driver, cport->id); ++ list_del(&cport->list); ++ ++ dev_info(&rpdev->dev, "rpmsg tty device %d is removed\n", cport->id); ++} ++ ++static struct rpmsg_device_id rpmsg_driver_tty_id_table[] = { ++ { .name = "rpmsg-tty-channel" }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_tty_id_table); ++ ++static struct rpmsg_driver rpmsg_tty_rmpsg_drv = { ++ .drv.name = KBUILD_MODNAME, ++ .drv.owner = THIS_MODULE, ++ .id_table = rpmsg_driver_tty_id_table, ++ .probe = rpmsg_tty_probe, ++ .callback = rpmsg_tty_cb, ++ .remove = rpmsg_tty_remove, ++}; ++ ++static int __init rpmsg_tty_init(void) ++{ ++ int err; ++ ++ rpmsg_tty_driver = tty_alloc_driver(MAX_TTY_RPMSG_INDEX, 0); ++ if (IS_ERR(rpmsg_tty_driver)) ++ return PTR_ERR(rpmsg_tty_driver); ++ ++ rpmsg_tty_driver->driver_name = "rpmsg_tty"; ++ rpmsg_tty_driver->name = "ttyRPMSG"; ++ rpmsg_tty_driver->major = TTYAUX_MAJOR; ++ rpmsg_tty_driver->minor_start = 3; ++ rpmsg_tty_driver->type = TTY_DRIVER_TYPE_CONSOLE; ++ rpmsg_tty_driver->init_termios = tty_std_termios; ++ rpmsg_tty_driver->flags = TTY_DRIVER_REAL_RAW | ++ TTY_DRIVER_DYNAMIC_DEV; ++ ++ tty_set_operations(rpmsg_tty_driver, &rpmsg_tty_ops); ++ ++ err = tty_register_driver(rpmsg_tty_driver); ++ if (err < 0) { ++ pr_err("Couldn't install rpmsg tty driver: err %d\n", err); ++ goto tty_error; ++ } ++ err = register_rpmsg_driver(&rpmsg_tty_rmpsg_drv); ++ ++ if (!err) ++ return 0; ++ ++ tty_unregister_driver(rpmsg_tty_driver); ++ ++tty_error: ++ put_tty_driver(rpmsg_tty_driver); ++ ++ return err; ++} ++ ++static void __exit rpmsg_tty_exit(void) ++{ ++ unregister_rpmsg_driver(&rpmsg_tty_rmpsg_drv); ++ tty_unregister_driver(rpmsg_tty_driver); ++ put_tty_driver(rpmsg_tty_driver); ++} ++ ++module_init(rpmsg_tty_init); ++module_exit(rpmsg_tty_exit); ++ ++MODULE_AUTHOR("Arnaud Pouliquen "); ++MODULE_DESCRIPTION("virtio remote processor messaging tty driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c +index 664f957..481eaea 100644 +--- a/drivers/rpmsg/virtio_rpmsg_bus.c ++++ b/drivers/rpmsg/virtio_rpmsg_bus.c +@@ -175,6 +175,7 @@ static int virtio_rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data, + int len, u32 dst); + static int virtio_rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, + u32 dst, void *data, int len); ++static int virtio_get_buffer_size(struct rpmsg_endpoint *ept); + + static const struct rpmsg_endpoint_ops virtio_endpoint_ops = { + .destroy_ept = virtio_rpmsg_destroy_ept, +@@ -184,6 +185,7 @@ static const struct rpmsg_endpoint_ops virtio_endpoint_ops = { + .trysend = virtio_rpmsg_trysend, + .trysendto = virtio_rpmsg_trysendto, + .trysend_offchannel = virtio_rpmsg_trysend_offchannel, ++ .get_buffer_size = virtio_get_buffer_size, + }; + + /** +@@ -699,6 +701,15 @@ static int virtio_rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, + return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, false); + } + ++static int virtio_get_buffer_size(struct rpmsg_endpoint *ept) ++{ ++ struct rpmsg_device *rpdev = ept->rpdev; ++ struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(rpdev); ++ struct virtproc_info *vrp = vch->vrp; ++ ++ return vrp->buf_size; ++} ++ + static int rpmsg_recv_single(struct virtproc_info *vrp, struct device *dev, + struct rpmsg_hdr *msg, unsigned int len) + { +diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h +index e3c5d85..787fd18 100644 +--- a/include/linux/remoteproc.h ++++ b/include/linux/remoteproc.h +@@ -440,6 +440,7 @@ struct rproc_dump_segment { + * @table_sz: size of @cached_table + * @has_iommu: flag to indicate if remote processor is behind an MMU + * @dump_segments: list of segments in the firmware ++ * @early_boot: remote processor has been booted before kernel boot + */ + struct rproc { + struct list_head node; +@@ -472,6 +473,7 @@ struct rproc { + bool has_iommu; + bool auto_boot; + struct list_head dump_segments; ++ bool early_boot; + }; + + /** +diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h +index 9fe156d..2af7674 100644 +--- a/include/linux/rpmsg.h ++++ b/include/linux/rpmsg.h +@@ -135,6 +135,7 @@ int rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst, + __poll_t rpmsg_poll(struct rpmsg_endpoint *ept, struct file *filp, + poll_table *wait); + ++int rpmsg_get_buffer_size(struct rpmsg_endpoint *ept); + #else + + static inline int register_rpmsg_device(struct rpmsg_device *dev) +@@ -242,6 +243,14 @@ static inline __poll_t rpmsg_poll(struct rpmsg_endpoint *ept, + return 0; + } + ++static int rpmsg_get_buffer_size(struct rpmsg_endpoint *ept) ++{ ++ /* This shouldn't be possible */ ++ WARN_ON(1); ++ ++ return -ENXIO; ++} ++ + #endif /* IS_ENABLED(CONFIG_RPMSG) */ + + /* use a macro to avoid include chaining to get THIS_MODULE */ +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0014-ARM-stm32mp1-r0-rc1-WATCHDOG.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0014-ARM-stm32mp1-r0-rc1-WATCHDOG.patch new file mode 100644 index 0000000..9f3bd80 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0014-ARM-stm32mp1-r0-rc1-WATCHDOG.patch @@ -0,0 +1,192 @@ +From 0d162ed61018cda930bb77680ab88b63633d5ce0 Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Tue, 13 Nov 2018 12:29:27 +0100 +Subject: [PATCH 14/52] ARM: stm32mp1-r0-rc1: WATCHDOG + +--- + drivers/watchdog/Kconfig | 12 ++++ + drivers/watchdog/Makefile | 1 + + drivers/watchdog/stpmic1_wdt.c | 139 +++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 152 insertions(+) + create mode 100644 drivers/watchdog/stpmic1_wdt.c + +diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig +index 5ea8909..6d2ffef 100644 +--- a/drivers/watchdog/Kconfig ++++ b/drivers/watchdog/Kconfig +@@ -806,6 +806,18 @@ config STM32_WATCHDOG + To compile this driver as a module, choose M here: the + module will be called stm32_iwdg. + ++config STPMIC1_WATCHDOG ++ tristate "STPMIC1 PMIC watchdog support" ++ depends on MFD_STPMIC1 ++ select WATCHDOG_CORE ++ help ++ Say Y here to include watchdog support embedded into STPMIC1 PMIC. ++ If the watchdog timer expires, stpmic1 will shut down all its power ++ supplies. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called spmic1_wdt. ++ + config UNIPHIER_WATCHDOG + tristate "UniPhier watchdog support" + depends on ARCH_UNIPHIER || COMPILE_TEST +diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile +index bf92e7b..2649cf3 100644 +--- a/drivers/watchdog/Makefile ++++ b/drivers/watchdog/Makefile +@@ -217,3 +217,4 @@ obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o + obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o + obj-$(CONFIG_MENZ069_WATCHDOG) += menz69_wdt.o + obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o ++obj-$(CONFIG_STPMIC1_WATCHDOG) += stpmic1_wdt.o +diff --git a/drivers/watchdog/stpmic1_wdt.c b/drivers/watchdog/stpmic1_wdt.c +new file mode 100644 +index 0000000..a6cbc27 +--- /dev/null ++++ b/drivers/watchdog/stpmic1_wdt.c +@@ -0,0 +1,139 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (C) STMicroelectronics 2018 ++// Author: Pascal Paillet for STMicroelectronics. ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* WATCHDOG CONTROL REGISTER bit */ ++#define WDT_START BIT(0) ++#define WDT_PING BIT(1) ++#define WDT_START_MASK BIT(0) ++#define WDT_PING_MASK BIT(1) ++#define WDT_STOP 0 ++ ++#define PMIC_WDT_MIN_TIMEOUT 1 ++#define PMIC_WDT_MAX_TIMEOUT 256 ++#define PMIC_WDT_DEFAULT_TIMEOUT 30 ++ ++static bool nowayout = WATCHDOG_NOWAYOUT; ++module_param(nowayout, bool, 0); ++MODULE_PARM_DESC(nowayout, ++ "Watchdog cannot be stopped once started (default=" ++ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); ++ ++struct stpmic1_wdt { ++ struct stpmic1 *pmic; ++ struct watchdog_device wdtdev; ++}; ++ ++static int pmic_wdt_start(struct watchdog_device *wdd) ++{ ++ struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd); ++ ++ return regmap_update_bits(wdt->pmic->regmap, ++ WCHDG_CR, WDT_START_MASK, WDT_START); ++} ++ ++static int pmic_wdt_stop(struct watchdog_device *wdd) ++{ ++ struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd); ++ ++ return regmap_update_bits(wdt->pmic->regmap, ++ WCHDG_CR, WDT_START_MASK, WDT_STOP); ++} ++ ++static int pmic_wdt_ping(struct watchdog_device *wdd) ++{ ++ struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd); ++ ++ return regmap_update_bits(wdt->pmic->regmap, ++ WCHDG_CR, WDT_PING_MASK, WDT_PING); ++} ++ ++static int pmic_wdt_set_timeout(struct watchdog_device *wdd, ++ unsigned int timeout) ++{ ++ struct stpmic1_wdt *wdt = watchdog_get_drvdata(wdd); ++ ++ wdd->timeout = timeout; ++ /* timeout is equal to register value + 1 */ ++ return regmap_write(wdt->pmic->regmap, WCHDG_TIMER_CR, timeout - 1); ++} ++ ++static const struct watchdog_info pmic_watchdog_info = { ++ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, ++ .identity = "STPMIC1 PMIC Watchdog", ++}; ++ ++static const struct watchdog_ops pmic_watchdog_ops = { ++ .owner = THIS_MODULE, ++ .start = pmic_wdt_start, ++ .stop = pmic_wdt_stop, ++ .ping = pmic_wdt_ping, ++ .set_timeout = pmic_wdt_set_timeout, ++}; ++ ++static int pmic_wdt_probe(struct platform_device *pdev) ++{ ++ int ret; ++ struct stpmic1 *pmic; ++ struct stpmic1_wdt *wdt; ++ ++ if (!pdev->dev.parent) ++ return -EINVAL; ++ ++ pmic = dev_get_drvdata(pdev->dev.parent); ++ if (!pmic) ++ return -EINVAL; ++ ++ wdt = devm_kzalloc(&pdev->dev, sizeof(struct stpmic1_wdt), GFP_KERNEL); ++ if (!wdt) ++ return -ENOMEM; ++ ++ wdt->pmic = pmic; ++ ++ wdt->wdtdev.info = &pmic_watchdog_info; ++ wdt->wdtdev.ops = &pmic_watchdog_ops; ++ wdt->wdtdev.min_timeout = PMIC_WDT_MIN_TIMEOUT; ++ wdt->wdtdev.max_timeout = PMIC_WDT_MAX_TIMEOUT; ++ ++ wdt->wdtdev.timeout = PMIC_WDT_DEFAULT_TIMEOUT; ++ watchdog_init_timeout(&wdt->wdtdev, 0, &pdev->dev); ++ ++ watchdog_set_nowayout(&wdt->wdtdev, nowayout); ++ watchdog_set_drvdata(&wdt->wdtdev, wdt); ++ ++ ret = devm_watchdog_register_device(&pdev->dev, &wdt->wdtdev); ++ if (ret) ++ return ret; ++ ++ dev_dbg(wdt->pmic->dev, "PMIC Watchdog driver probed\n"); ++ return 0; ++} ++ ++static const struct of_device_id of_pmic_wdt_match[] = { ++ { .compatible = "st,stpmic1-wdt" }, ++ { }, ++}; ++ ++MODULE_DEVICE_TABLE(of, of_pmic_wdt_match); ++ ++static struct platform_driver stpmic1_wdt_driver = { ++ .probe = pmic_wdt_probe, ++ .driver = { ++ .name = "stpmic1-wdt", ++ .of_match_table = of_pmic_wdt_match, ++ }, ++}; ++module_platform_driver(stpmic1_wdt_driver); ++ ++MODULE_DESCRIPTION("Watchdog driver for STPMIC1 device"); ++MODULE_AUTHOR("Pascal Paillet "); ++MODULE_LICENSE("GPL v2"); +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0015-ARM-stm32mp1-r0-rc1-MISC.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0015-ARM-stm32mp1-r0-rc1-MISC.patch new file mode 100644 index 0000000..4c9cfe7 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0015-ARM-stm32mp1-r0-rc1-MISC.patch @@ -0,0 +1,901 @@ +From 535343fc0142b45ec82958a4cbea41945d4d93c9 Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Tue, 13 Nov 2018 12:30:43 +0100 +Subject: [PATCH 15/52] ARM: stm32mp1-r0-rc1: MISC + +--- + arch/arm/mach-integrator/integrator_cp.c | 2 - + arch/arm/mach-versatile/versatile_dt.c | 4 - + drivers/soc/Kconfig | 1 + + drivers/soc/Makefile | 1 + + drivers/soc/st/Kconfig | 9 + + drivers/soc/st/Makefile | 1 + + drivers/soc/st/stm32_pm_domain.c | 212 ++++++++++++ + drivers/spi/Kconfig | 9 + + drivers/spi/Makefile | 1 + + drivers/spi/spi-stm32-qspi.c | 541 +++++++++++++++++++++++++++++++ + 10 files changed, 775 insertions(+), 6 deletions(-) + create mode 100644 drivers/soc/st/Kconfig + create mode 100644 drivers/soc/st/Makefile + create mode 100644 drivers/soc/st/stm32_pm_domain.c + create mode 100644 drivers/spi/spi-stm32-qspi.c + +diff --git a/arch/arm/mach-integrator/integrator_cp.c b/arch/arm/mach-integrator/integrator_cp.c +index 772a7cf..976ded5 100644 +--- a/arch/arm/mach-integrator/integrator_cp.c ++++ b/arch/arm/mach-integrator/integrator_cp.c +@@ -80,8 +80,6 @@ static unsigned int mmc_status(struct device *dev) + static struct mmci_platform_data mmc_data = { + .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, + .status = mmc_status, +- .gpio_wp = -1, +- .gpio_cd = -1, + }; + + static u64 notrace intcp_read_sched_clock(void) +diff --git a/arch/arm/mach-versatile/versatile_dt.c b/arch/arm/mach-versatile/versatile_dt.c +index 3c8d39c..e9d6068 100644 +--- a/arch/arm/mach-versatile/versatile_dt.c ++++ b/arch/arm/mach-versatile/versatile_dt.c +@@ -89,15 +89,11 @@ unsigned int mmc_status(struct device *dev) + static struct mmci_platform_data mmc0_plat_data = { + .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, + .status = mmc_status, +- .gpio_wp = -1, +- .gpio_cd = -1, + }; + + static struct mmci_platform_data mmc1_plat_data = { + .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, + .status = mmc_status, +- .gpio_wp = -1, +- .gpio_cd = -1, + }; + + /* +diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig +index c07b4a8..f2bd1ce 100644 +--- a/drivers/soc/Kconfig ++++ b/drivers/soc/Kconfig +@@ -11,6 +11,7 @@ source "drivers/soc/qcom/Kconfig" + source "drivers/soc/renesas/Kconfig" + source "drivers/soc/rockchip/Kconfig" + source "drivers/soc/samsung/Kconfig" ++source "drivers/soc/st/Kconfig" + source "drivers/soc/sunxi/Kconfig" + source "drivers/soc/tegra/Kconfig" + source "drivers/soc/ti/Kconfig" +diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile +index 113e884..a16f673 100644 +--- a/drivers/soc/Makefile ++++ b/drivers/soc/Makefile +@@ -18,6 +18,7 @@ obj-y += qcom/ + obj-y += renesas/ + obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ + obj-$(CONFIG_SOC_SAMSUNG) += samsung/ ++obj-$(CONFIG_ARCH_STM32) += st/ + obj-$(CONFIG_ARCH_SUNXI) += sunxi/ + obj-$(CONFIG_ARCH_TEGRA) += tegra/ + obj-$(CONFIG_SOC_TI) += ti/ +diff --git a/drivers/soc/st/Kconfig b/drivers/soc/st/Kconfig +new file mode 100644 +index 0000000..82ee423 +--- /dev/null ++++ b/drivers/soc/st/Kconfig +@@ -0,0 +1,9 @@ ++if ARCH_STM32 ++ ++config STM32_PM_DOMAINS ++ bool "STM32 PM domains" ++ depends on MACH_STM32MP157 ++ select PM_GENERIC_DOMAINS ++ default y if MACH_STM32MP157 ++ ++endif # ARCH_STM32 +diff --git a/drivers/soc/st/Makefile b/drivers/soc/st/Makefile +new file mode 100644 +index 0000000..8d7f291 +--- /dev/null ++++ b/drivers/soc/st/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_STM32_PM_DOMAINS) += stm32_pm_domain.o +diff --git a/drivers/soc/st/stm32_pm_domain.c b/drivers/soc/st/stm32_pm_domain.c +new file mode 100644 +index 0000000..0386624 +--- /dev/null ++++ b/drivers/soc/st/stm32_pm_domain.c +@@ -0,0 +1,212 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved ++ * Author: Alexandre Torgue for STMicroelectronics. ++ * Author: Olivier Bideau for STMicroelectronics. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define SMC(domain, state) \ ++{ \ ++ struct arm_smccc_res res; \ ++ arm_smccc_smc(0x82001008, domain, state, 0, \ ++ 0, 0, 0, 0, &res); \ ++} ++ ++#define STM32_SMC_PD_DOMAIN_ON 0 ++#define STM32_SMC_PD_DOMAIN_OFF 1 ++ ++struct stm32_pm_domain { ++ struct device *dev; ++ struct generic_pm_domain genpd; ++ int id; ++}; ++ ++static int stm32_pd_power_off(struct generic_pm_domain *domain) ++{ ++ struct stm32_pm_domain *priv = container_of(domain, ++ struct stm32_pm_domain, ++ genpd); ++ ++ SMC(priv->id, STM32_SMC_PD_DOMAIN_OFF); ++ ++ dev_dbg(priv->dev, "%s OFF\n", domain->name); ++ ++ return 0; ++} ++ ++static int stm32_pd_power_on(struct generic_pm_domain *domain) ++{ ++ struct stm32_pm_domain *priv = container_of(domain, ++ struct stm32_pm_domain, ++ genpd); ++ ++ SMC(priv->id, STM32_SMC_PD_DOMAIN_ON); ++ ++ dev_dbg(priv->dev, "%s ON\n", domain->name); ++ ++ return 0; ++} ++ ++static void stm32_pm_domain_remove(struct stm32_pm_domain *domain) ++{ ++ int ret; ++ ++ ret = pm_genpd_remove(&domain->genpd); ++ if (ret) ++ dev_err(domain->dev, "failed to remove PM domain %s: %d\n", ++ domain->genpd.name, ret); ++} ++ ++static int stm32_pm_domain_add(struct stm32_pm_domain *domain, ++ struct device *dev, ++ struct device_node *np) ++{ ++ int ret; ++ ++ domain->dev = dev; ++ domain->genpd.name = np->name; ++ domain->genpd.power_off = stm32_pd_power_off; ++ domain->genpd.power_on = stm32_pd_power_on; ++ domain->genpd.flags |= GENPD_FLAG_ACTIVE_WAKEUP; ++ ++ ret = of_property_read_u32(np, "reg", &domain->id); ++ if (ret) { ++ dev_err(domain->dev, "no domain ID\n"); ++ return ret; ++ } ++ ++ ret = pm_genpd_init(&domain->genpd, NULL, 0); ++ if (ret < 0) { ++ dev_err(domain->dev, "failed to initialise PM domain %s: %d\n", ++ np->name, ret); ++ return ret; ++ } ++ ++ ret = of_genpd_add_provider_simple(np, &domain->genpd); ++ if (ret < 0) { ++ dev_err(domain->dev, "failed to register PM domain %s: %d\n", ++ np->name, ret); ++ stm32_pm_domain_remove(domain); ++ return ret; ++ } ++ ++ dev_info(domain->dev, "domain %s registered\n", np->name); ++ ++ return 0; ++} ++ ++static void stm32_pm_subdomain_add(struct stm32_pm_domain *domain, ++ struct device *dev, ++ struct device_node *np) ++{ ++ struct device_node *np_child; ++ int ret; ++ ++ for_each_child_of_node(np, np_child) { ++ struct stm32_pm_domain *sub_domain; ++ ++ sub_domain = devm_kzalloc(dev, sizeof(*sub_domain), GFP_KERNEL); ++ if (!sub_domain) ++ continue; ++ ++ sub_domain->dev = dev; ++ sub_domain->genpd.name = np_child->name; ++ sub_domain->genpd.power_off = stm32_pd_power_off; ++ sub_domain->genpd.power_on = stm32_pd_power_on; ++ sub_domain->genpd.flags |= GENPD_FLAG_ACTIVE_WAKEUP; ++ ++ ret = of_property_read_u32(np_child, "reg", &sub_domain->id); ++ if (ret) { ++ dev_err(sub_domain->dev, "no domain ID\n"); ++ devm_kfree(dev, sub_domain); ++ continue; ++ } ++ ++ ret = pm_genpd_init(&sub_domain->genpd, NULL, 0); ++ if (ret < 0) { ++ dev_err(sub_domain->dev, "failed to initialise PM domain %s: %d\n" ++ , np_child->name, ret); ++ devm_kfree(dev, sub_domain); ++ continue; ++ } ++ ++ ret = of_genpd_add_provider_simple(np_child, ++ &sub_domain->genpd); ++ if (ret < 0) { ++ dev_err(sub_domain->dev, "failed to register PM domain %s: %d\n" ++ , np_child->name, ret); ++ stm32_pm_domain_remove(sub_domain); ++ devm_kfree(dev, sub_domain); ++ continue; ++ } ++ ++ ret = pm_genpd_add_subdomain(&domain->genpd, ++ &sub_domain->genpd); ++ ++ if (ret < 0) { ++ dev_err(sub_domain->dev, "failed to add Sub PM domain %s: %d\n" ++ , np_child->name, ret); ++ stm32_pm_domain_remove(sub_domain); ++ devm_kfree(dev, sub_domain); ++ continue; ++ } ++ ++ dev_info(sub_domain->dev, "subdomain %s registered\n", ++ np_child->name); ++ } ++} ++ ++static int stm32_pm_domain_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node, *child_np; ++ int ret; ++ ++ for_each_child_of_node(np, child_np) { ++ struct stm32_pm_domain *domain; ++ ++ domain = devm_kzalloc(dev, sizeof(*domain), GFP_KERNEL); ++ if (!domain) ++ continue; ++ ++ ret = stm32_pm_domain_add(domain, dev, child_np); ++ if (ret) { ++ devm_kfree(dev, domain); ++ continue; ++ } ++ ++ stm32_pm_subdomain_add(domain, dev, child_np); ++ } ++ ++ dev_info(dev, "domains probed\n"); ++ ++ return 0; ++} ++ ++static const struct of_device_id stm32_pm_domain_matches[] = { ++ { .compatible = "st,stm32mp157c-pd", }, ++ { }, ++}; ++ ++static struct platform_driver stm32_pm_domains_driver = { ++ .probe = stm32_pm_domain_probe, ++ .driver = { ++ .name = "stm32-pm-domain", ++ .of_match_table = stm32_pm_domain_matches, ++ }, ++}; ++ ++static int __init stm32_pm_domains_init(void) ++{ ++ return platform_driver_register(&stm32_pm_domains_driver); ++} ++core_initcall(stm32_pm_domains_init); +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index 671d078..448d441 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -613,6 +613,15 @@ config SPI_STM32 + is not available, the driver automatically falls back to + PIO mode. + ++config SPI_STM32_QSPI ++ tristate "STMicroelectronics STM32 QUAD SPI controller" ++ depends on ARCH_STM32 || COMPILE_TEST ++ depends on OF ++ help ++ This enables support for the Quad SPI controller in master mode. ++ This driver does not support generic SPI. The implementation only ++ supports spi-mem interface. ++ + config SPI_ST_SSC4 + tristate "STMicroelectronics SPI SSC-based driver" + depends on ARCH_STI || COMPILE_TEST +diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile +index a90d559..68a3c4e 100644 +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -90,6 +90,7 @@ obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o + obj-$(CONFIG_SPI_SIRF) += spi-sirf.o + obj-$(CONFIG_SPI_SPRD_ADI) += spi-sprd-adi.o + obj-$(CONFIG_SPI_STM32) += spi-stm32.o ++obj-$(CONFIG_SPI_STM32_QSPI) += spi-stm32-qspi.o + obj-$(CONFIG_SPI_ST_SSC4) += spi-st-ssc4.o + obj-$(CONFIG_SPI_SUN4I) += spi-sun4i.o + obj-$(CONFIG_SPI_SUN6I) += spi-sun6i.o +diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c +new file mode 100644 +index 0000000..3e8ca10 +--- /dev/null ++++ b/drivers/spi/spi-stm32-qspi.c +@@ -0,0 +1,541 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved ++ * Author: Ludovic Barre for STMicroelectronics. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define QSPI_CR 0x00 ++#define CR_EN BIT(0) ++#define CR_ABORT BIT(1) ++#define CR_DMAEN BIT(2) ++#define CR_TCEN BIT(3) ++#define CR_SSHIFT BIT(4) ++#define CR_DFM BIT(6) ++#define CR_FSEL BIT(7) ++#define CR_FTHRES_MASK GENMASK(12, 8) ++#define CR_TEIE BIT(16) ++#define CR_TCIE BIT(17) ++#define CR_FTIE BIT(18) ++#define CR_SMIE BIT(19) ++#define CR_TOIE BIT(20) ++#define CR_PRESC_MASK GENMASK(31, 24) ++ ++#define QSPI_DCR 0x04 ++#define DCR_FSIZE_MASK GENMASK(20, 16) ++ ++#define QSPI_SR 0x08 ++#define SR_TEF BIT(0) ++#define SR_TCF BIT(1) ++#define SR_FTF BIT(2) ++#define SR_SMF BIT(3) ++#define SR_TOF BIT(4) ++#define SR_BUSY BIT(5) ++#define SR_FLEVEL_MASK GENMASK(13, 8) ++ ++#define QSPI_FCR 0x0c ++#define FCR_CTEF BIT(0) ++#define FCR_CTCF BIT(1) ++ ++#define QSPI_DLR 0x10 ++ ++#define QSPI_CCR 0x14 ++#define CCR_INST_MASK GENMASK(7, 0) ++#define CCR_IMODE_MASK GENMASK(9, 8) ++#define CCR_ADMODE_MASK GENMASK(11, 10) ++#define CCR_ADSIZE_MASK GENMASK(13, 12) ++#define CCR_DCYC_MASK GENMASK(22, 18) ++#define CCR_DMODE_MASK GENMASK(25, 24) ++#define CCR_FMODE_MASK GENMASK(27, 26) ++#define CCR_FMODE_INDW (0U << 26) ++#define CCR_FMODE_INDR (1U << 26) ++#define CCR_FMODE_APM (2U << 26) ++#define CCR_FMODE_MM (3U << 26) ++#define CCR_BUSWIDTH_0 0x0 ++#define CCR_BUSWIDTH_1 0x1 ++#define CCR_BUSWIDTH_2 0x2 ++#define CCR_BUSWIDTH_4 0x3 ++ ++#define QSPI_AR 0x18 ++#define QSPI_ABR 0x1c ++#define QSPI_DR 0x20 ++#define QSPI_PSMKR 0x24 ++#define QSPI_PSMAR 0x28 ++#define QSPI_PIR 0x2c ++#define QSPI_LPTR 0x30 ++ ++#define STM32_QSPI_MAX_MMAP_SZ SZ_256M ++#define STM32_QSPI_MAX_NORCHIP 2 ++ ++#define STM32_FIFO_TIMEOUT_US 30000 ++#define STM32_BUSY_TIMEOUT_US 100000 ++#define STM32_ABT_TIMEOUT_US 100000 ++ ++struct stm32_qspi_flash { ++ struct stm32_qspi *qspi; ++ u32 cs; ++ u32 presc; ++}; ++ ++struct stm32_qspi { ++ struct device *dev; ++ void __iomem *io_base; ++ void __iomem *mm_base; ++ resource_size_t mm_size; ++ struct clk *clk; ++ u32 clk_rate; ++ struct stm32_qspi_flash flash[STM32_QSPI_MAX_NORCHIP]; ++ struct completion data_completion; ++ u32 fmode; ++ ++ u32 cr_reg; ++ u32 dcr_reg; ++ ++ /* ++ * to protect device configuration, could be different between ++ * 2 flash access (bk1, bk2) ++ */ ++ struct mutex lock; ++}; ++ ++static irqreturn_t stm32_qspi_irq(int irq, void *dev_id) ++{ ++ struct stm32_qspi *qspi = (struct stm32_qspi *)dev_id; ++ u32 cr, sr; ++ ++ sr = readl_relaxed(qspi->io_base + QSPI_SR); ++ ++ if (sr & (SR_TEF | SR_TCF)) { ++ /* disable irq */ ++ cr = readl_relaxed(qspi->io_base + QSPI_CR); ++ cr &= ~CR_TCIE & ~CR_TEIE; ++ writel_relaxed(cr, qspi->io_base + QSPI_CR); ++ complete(&qspi->data_completion); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static void stm32_qspi_read_fifo(u8 *val, void __iomem *addr) ++{ ++ *val = readb_relaxed(addr); ++} ++ ++static void stm32_qspi_write_fifo(u8 *val, void __iomem *addr) ++{ ++ writeb_relaxed(*val, addr); ++} ++ ++static int stm32_qspi_tx_poll(struct stm32_qspi *qspi, ++ const struct spi_mem_op *op) ++{ ++ void (*tx_fifo)(u8 *val, void __iomem *addr); ++ u32 len = op->data.nbytes, sr; ++ u8 *buf; ++ int ret; ++ ++ if (op->data.dir == SPI_MEM_DATA_IN) { ++ tx_fifo = stm32_qspi_read_fifo; ++ buf = op->data.buf.in; ++ ++ } else { ++ tx_fifo = stm32_qspi_write_fifo; ++ buf = (u8 *)op->data.buf.out; ++ } ++ ++ while (len--) { ++ ret = readl_relaxed_poll_timeout_atomic(qspi->io_base + QSPI_SR, ++ sr, (sr & SR_FTF), 1, ++ STM32_FIFO_TIMEOUT_US); ++ if (ret) { ++ dev_err(qspi->dev, "fifo timeout (len:%d stat:%#x)\n", ++ len, sr); ++ return ret; ++ } ++ tx_fifo(buf++, qspi->io_base + QSPI_DR); ++ } ++ ++ return 0; ++} ++ ++static int stm32_qspi_tx_mm(struct stm32_qspi *qspi, ++ const struct spi_mem_op *op) ++{ ++ memcpy_fromio(op->data.buf.in, qspi->mm_base + op->addr.val, ++ op->data.nbytes); ++ return 0; ++} ++ ++static int stm32_qspi_tx(struct stm32_qspi *qspi, const struct spi_mem_op *op) ++{ ++ if (!op->data.nbytes) ++ return 0; ++ ++ if (qspi->fmode == CCR_FMODE_MM) ++ return stm32_qspi_tx_mm(qspi, op); ++ ++ return stm32_qspi_tx_poll(qspi, op); ++} ++ ++static int stm32_qspi_wait_nobusy(struct stm32_qspi *qspi) ++{ ++ u32 sr; ++ ++ return readl_relaxed_poll_timeout_atomic(qspi->io_base + QSPI_SR, sr, ++ !(sr & SR_BUSY), 1, ++ STM32_BUSY_TIMEOUT_US); ++} ++ ++static int stm32_qspi_wait_cmd(struct stm32_qspi *qspi, ++ const struct spi_mem_op *op) ++{ ++ u32 cr, sr; ++ int err = 0; ++ ++ if (!op->data.nbytes) ++ return stm32_qspi_wait_nobusy(qspi); ++ ++ if (readl_relaxed(qspi->io_base + QSPI_SR) & SR_TCF) ++ goto out; ++ ++ reinit_completion(&qspi->data_completion); ++ cr = readl_relaxed(qspi->io_base + QSPI_CR); ++ writel_relaxed(cr | CR_TCIE | CR_TEIE, qspi->io_base + QSPI_CR); ++ ++ if (!wait_for_completion_interruptible_timeout(&qspi->data_completion, ++ msecs_to_jiffies(1000))) { ++ err = -ETIMEDOUT; ++ } else { ++ sr = readl_relaxed(qspi->io_base + QSPI_SR); ++ if (sr & SR_TEF) ++ err = -EIO; ++ } ++ ++out: ++ /* clear flags */ ++ writel_relaxed(FCR_CTCF | FCR_CTEF, qspi->io_base + QSPI_FCR); ++ ++ return err; ++} ++ ++static int stm32_qspi_get_mode(struct stm32_qspi *qspi, u8 buswidth) ++{ ++ if (buswidth == 4) ++ return CCR_BUSWIDTH_4; ++ ++ return buswidth; ++} ++ ++static int stm32_qspi_send(struct spi_mem *mem, const struct spi_mem_op *op) ++{ ++ struct stm32_qspi *qspi = spi_controller_get_devdata(mem->spi->master); ++ struct stm32_qspi_flash *flash = &qspi->flash[mem->spi->chip_select]; ++ u32 ccr, cr, addr_max; ++ int timeout, err = 0; ++ ++ dev_dbg(qspi->dev, "cmd:%#x mode:%d.%d.%d.%d addr:%#llx len:%#x\n", ++ op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth, ++ op->dummy.buswidth, op->data.buswidth, ++ op->addr.val, op->data.nbytes); ++ ++ err = stm32_qspi_wait_nobusy(qspi); ++ if (err) ++ goto abort; ++ ++ addr_max = op->addr.val + op->data.nbytes + 1; ++ ++ if (op->data.dir == SPI_MEM_DATA_IN) { ++ if (addr_max < qspi->mm_size && ++ op->addr.buswidth) ++ qspi->fmode = CCR_FMODE_MM; ++ else ++ qspi->fmode = CCR_FMODE_INDR; ++ } else { ++ qspi->fmode = CCR_FMODE_INDW; ++ } ++ ++ cr = readl_relaxed(qspi->io_base + QSPI_CR); ++ cr &= ~CR_PRESC_MASK & ~CR_FSEL; ++ cr |= FIELD_PREP(CR_PRESC_MASK, flash->presc); ++ cr |= FIELD_PREP(CR_FSEL, flash->cs); ++ writel_relaxed(cr, qspi->io_base + QSPI_CR); ++ ++ if (op->data.nbytes) ++ writel_relaxed(op->data.nbytes - 1, ++ qspi->io_base + QSPI_DLR); ++ else ++ qspi->fmode = CCR_FMODE_INDW; ++ ++ ccr = qspi->fmode; ++ ccr |= FIELD_PREP(CCR_INST_MASK, op->cmd.opcode); ++ ccr |= FIELD_PREP(CCR_IMODE_MASK, ++ stm32_qspi_get_mode(qspi, op->cmd.buswidth)); ++ ++ if (op->addr.nbytes) { ++ ccr |= FIELD_PREP(CCR_ADMODE_MASK, ++ stm32_qspi_get_mode(qspi, op->addr.buswidth)); ++ ccr |= FIELD_PREP(CCR_ADSIZE_MASK, op->addr.nbytes - 1); ++ } ++ ++ if (op->dummy.buswidth && op->dummy.nbytes) ++ ccr |= FIELD_PREP(CCR_DCYC_MASK, ++ op->dummy.nbytes * 8 / op->dummy.buswidth); ++ ++ if (op->data.nbytes) { ++ ccr |= FIELD_PREP(CCR_DMODE_MASK, ++ stm32_qspi_get_mode(qspi, op->data.buswidth)); ++ } ++ ++ writel_relaxed(ccr, qspi->io_base + QSPI_CCR); ++ ++ if (op->addr.nbytes && qspi->fmode != CCR_FMODE_MM) ++ writel_relaxed(op->addr.val, qspi->io_base + QSPI_AR); ++ ++ err = stm32_qspi_tx(qspi, op); ++ ++ /* ++ * Abort in: ++ * -error case ++ * -read memory map: prefetching must be stopped if we read the last ++ * byte of device (device size - fifo size). like device size is not ++ * knows, the prefetching is always stop. ++ */ ++ if (err || qspi->fmode == CCR_FMODE_MM) ++ goto abort; ++ ++ /* wait end of tx in indirect mode */ ++ err = stm32_qspi_wait_cmd(qspi, op); ++ if (err) ++ goto abort; ++ ++ return 0; ++ ++abort: ++ cr = readl_relaxed(qspi->io_base + QSPI_CR) | CR_ABORT; ++ writel_relaxed(cr, qspi->io_base + QSPI_CR); ++ ++ /* wait clear of abort bit by hw */ ++ timeout = readl_relaxed_poll_timeout_atomic(qspi->io_base + QSPI_CR, ++ cr, !(cr & CR_ABORT), 1, ++ STM32_ABT_TIMEOUT_US); ++ ++ writel_relaxed(FCR_CTCF, qspi->io_base + QSPI_FCR); ++ ++ if (err || timeout) ++ dev_err(qspi->dev, "%s err:%d abort timeout:%d\n", ++ __func__, err, timeout); ++ ++ return err; ++} ++ ++static int stm32_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) ++{ ++ struct stm32_qspi *qspi = spi_controller_get_devdata(mem->spi->master); ++ int ret; ++ ++ mutex_lock(&qspi->lock); ++ ret = stm32_qspi_send(mem, op); ++ mutex_unlock(&qspi->lock); ++ ++ return ret; ++} ++ ++static int stm32_qspi_setup(struct spi_device *spi) ++{ ++ struct spi_controller *ctrl = spi->master; ++ struct stm32_qspi *qspi = spi_controller_get_devdata(ctrl); ++ struct stm32_qspi_flash *flash; ++ u32 presc; ++ ++ if (ctrl->busy) ++ return -EBUSY; ++ ++ if (!spi->max_speed_hz) ++ return -EINVAL; ++ ++ presc = DIV_ROUND_UP(qspi->clk_rate, spi->max_speed_hz) - 1; ++ ++ flash = &qspi->flash[spi->chip_select]; ++ flash->qspi = qspi; ++ flash->cs = spi->chip_select; ++ flash->presc = presc; ++ ++ mutex_lock(&qspi->lock); ++ qspi->cr_reg = FIELD_PREP(CR_FTHRES_MASK, 3) | CR_SSHIFT | CR_EN; ++ writel_relaxed(qspi->cr_reg, qspi->io_base + QSPI_CR); ++ ++ /* set dcr fsize to max address */ ++ qspi->dcr_reg = DCR_FSIZE_MASK; ++ writel_relaxed(qspi->dcr_reg, qspi->io_base + QSPI_DCR); ++ mutex_unlock(&qspi->lock); ++ ++ return 0; ++} ++ ++/* ++ * no special host constraint, so use default spi_mem_default_supports_op ++ * to check supported mode. ++ */ ++static const struct spi_controller_mem_ops stm32_qspi_mem_ops = { ++ .exec_op = stm32_qspi_exec_op, ++}; ++ ++static void stm32_qspi_release(struct stm32_qspi *qspi) ++{ ++ /* disable qspi */ ++ writel_relaxed(0, qspi->io_base + QSPI_CR); ++ mutex_destroy(&qspi->lock); ++ clk_disable_unprepare(qspi->clk); ++} ++ ++static int stm32_qspi_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct spi_controller *ctrl; ++ struct reset_control *rstc; ++ struct stm32_qspi *qspi; ++ struct resource *res; ++ int ret, irq; ++ ++ ctrl = spi_alloc_master(dev, sizeof(*qspi)); ++ if (!ctrl) ++ return -ENOMEM; ++ ++ qspi = spi_controller_get_devdata(ctrl); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi"); ++ qspi->io_base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(qspi->io_base)) ++ return PTR_ERR(qspi->io_base); ++ ++ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi_mm"); ++ qspi->mm_base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(qspi->mm_base)) ++ return PTR_ERR(qspi->mm_base); ++ ++ qspi->mm_size = resource_size(res); ++ if (qspi->mm_size > STM32_QSPI_MAX_MMAP_SZ) ++ return -EINVAL; ++ ++ irq = platform_get_irq(pdev, 0); ++ ret = devm_request_irq(dev, irq, stm32_qspi_irq, 0, ++ dev_name(dev), qspi); ++ if (ret) { ++ dev_err(dev, "failed to request irq\n"); ++ return ret; ++ } ++ ++ init_completion(&qspi->data_completion); ++ ++ qspi->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(qspi->clk)) ++ return PTR_ERR(qspi->clk); ++ ++ qspi->clk_rate = clk_get_rate(qspi->clk); ++ if (!qspi->clk_rate) ++ return -EINVAL; ++ ++ ret = clk_prepare_enable(qspi->clk); ++ if (ret) { ++ dev_err(dev, "can not enable the clock\n"); ++ return ret; ++ } ++ ++ rstc = devm_reset_control_get_exclusive(dev, NULL); ++ if (!IS_ERR(rstc)) { ++ reset_control_assert(rstc); ++ udelay(2); ++ reset_control_deassert(rstc); ++ } ++ ++ qspi->dev = dev; ++ platform_set_drvdata(pdev, qspi); ++ mutex_init(&qspi->lock); ++ ++ ctrl->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD ++ | SPI_TX_DUAL | SPI_TX_QUAD; ++ ctrl->setup = stm32_qspi_setup; ++ ctrl->bus_num = -1; ++ ctrl->mem_ops = &stm32_qspi_mem_ops; ++ ctrl->num_chipselect = STM32_QSPI_MAX_NORCHIP; ++ ctrl->dev.of_node = dev->of_node; ++ ++ ret = devm_spi_register_master(dev, ctrl); ++ if (ret) ++ goto err_spi_register; ++ ++ return 0; ++ ++err_spi_register: ++ stm32_qspi_release(qspi); ++ ++ return ret; ++} ++ ++static int stm32_qspi_remove(struct platform_device *pdev) ++{ ++ struct stm32_qspi *qspi = platform_get_drvdata(pdev); ++ ++ stm32_qspi_release(qspi); ++ return 0; ++} ++ ++static int __maybe_unused stm32_qspi_suspend(struct device *dev) ++{ ++ struct stm32_qspi *qspi = dev_get_drvdata(dev); ++ ++ clk_disable_unprepare(qspi->clk); ++ pinctrl_pm_select_sleep_state(dev); ++ ++ return 0; ++} ++ ++static int __maybe_unused stm32_qspi_resume(struct device *dev) ++{ ++ struct stm32_qspi *qspi = dev_get_drvdata(dev); ++ ++ pinctrl_pm_select_default_state(dev); ++ clk_prepare_enable(qspi->clk); ++ ++ writel_relaxed(qspi->cr_reg, qspi->io_base + QSPI_CR); ++ writel_relaxed(qspi->dcr_reg, qspi->io_base + QSPI_DCR); ++ ++ return 0; ++} ++ ++SIMPLE_DEV_PM_OPS(stm32_qspi_pm_ops, stm32_qspi_suspend, stm32_qspi_resume); ++ ++static const struct of_device_id stm32_qspi_match[] = { ++ {.compatible = "st,stm32f469-qspi"}, ++ {} ++}; ++MODULE_DEVICE_TABLE(of, stm32_qspi_match); ++ ++static struct platform_driver stm32_qspi_driver = { ++ .probe = stm32_qspi_probe, ++ .remove = stm32_qspi_remove, ++ .driver = { ++ .name = "stm32-qspi", ++ .of_match_table = stm32_qspi_match, ++ .pm = &stm32_qspi_pm_ops, ++ }, ++}; ++module_platform_driver(stm32_qspi_driver); ++ ++MODULE_AUTHOR("Ludovic Barre "); ++MODULE_DESCRIPTION("STMicroelectronics STM32 quad spi driver"); ++MODULE_LICENSE("GPL v2"); +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0016-ARM-stm32mp1-r0-rc1-DEVICETREE.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0016-ARM-stm32mp1-r0-rc1-DEVICETREE.patch new file mode 100644 index 0000000..6a57985 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0016-ARM-stm32mp1-r0-rc1-DEVICETREE.patch @@ -0,0 +1,4893 @@ +From adbdec4cf36d1cf2127da0c16b43ec88a5e7253c Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Tue, 13 Nov 2018 12:31:28 +0100 +Subject: [PATCH 16/52] ARM: stm32mp1-r0-rc1: DEVICETREE + +--- + .../devicetree/bindings/dma/stm32-dma.txt | 32 +- + .../devicetree/bindings/dma/stm32-dmamux.txt | 5 +- + .../devicetree/bindings/dma/stm32-mdma.txt | 22 +- + .../devicetree/bindings/i2c/i2c-stm32.txt | 48 +- + .../devicetree/bindings/iio/adc/st,stm32-adc.txt | 64 ++- + .../bindings/iio/counter/stm32-lptimer-cnt.txt | 8 +- + .../bindings/iio/timer/stm32-timer-trigger.txt | 9 + + .../devicetree/bindings/input/st,stpmic1-onkey.txt | 28 + + .../devicetree/bindings/mfd/st,stm32mp1-pwr.txt | 53 ++ + .../devicetree/bindings/mfd/st,stpmic1.txt | 132 +++++ + Documentation/devicetree/bindings/mfd/stmfx.txt | 28 + + Documentation/devicetree/bindings/mmc/mmci.txt | 11 + + .../devicetree/bindings/mtd/stm32-fmc2-nand.txt | 59 ++ + .../devicetree/bindings/nvmem/st,stm32-romem.txt | 31 ++ + .../devicetree/bindings/pinctrl/pinctrl-stmfx.txt | 116 ++++ + .../bindings/pinctrl/st,stm32-pinctrl.txt | 2 + + .../devicetree/bindings/pwm/pwm-stm32-lp.txt | 9 +- + .../devicetree/bindings/pwm/pwm-stm32.txt | 8 +- + .../bindings/regulator/st,stm32mp1-pwr-reg.txt | 31 ++ + .../bindings/regulator/st,stpmic1-regulator.txt | 68 +++ + .../devicetree/bindings/remoteproc/rproc-srm.txt | 57 ++ + .../devicetree/bindings/remoteproc/stm32-rproc.txt | 78 +++ + .../devicetree/bindings/rtc/st,stm32-rtc.txt | 10 +- + .../devicetree/bindings/serial/st,stm32-usart.txt | 40 +- + .../devicetree/bindings/spi/spi-stm32-qspi.txt | 44 ++ + .../bindings/watchdog/st,stpmic1-wdt.txt | 11 + + arch/arm/boot/dts/Makefile | 2 + + arch/arm/boot/dts/stm32mp157-pinctrl.dtsi | 603 ++++++++++++++++++++- + arch/arm/boot/dts/stm32mp157a-dk1.dts | 412 ++++++++++++++ + arch/arm/boot/dts/stm32mp157c-dk2.dts | 22 + + arch/arm/boot/dts/stm32mp157c-ed1.dts | 301 +++++++++- + arch/arm/boot/dts/stm32mp157c-ev1.dts | 121 ++++- + arch/arm/boot/dts/stm32mp157c-m4-srm.dtsi | 436 +++++++++++++++ + arch/arm/boot/dts/stm32mp157c.dtsi | 457 ++++++++++++++-- + arch/arm/boot/dts/stm32mp157caa-pinctrl.dtsi | 90 +++ + arch/arm/boot/dts/stm32mp157cab-pinctrl.dtsi | 62 +++ + arch/arm/boot/dts/stm32mp157cac-pinctrl.dtsi | 78 +++ + arch/arm/boot/dts/stm32mp157cad-pinctrl.dtsi | 62 +++ + 38 files changed, 3526 insertions(+), 124 deletions(-) + create mode 100644 Documentation/devicetree/bindings/input/st,stpmic1-onkey.txt + create mode 100644 Documentation/devicetree/bindings/mfd/st,stm32mp1-pwr.txt + create mode 100644 Documentation/devicetree/bindings/mfd/st,stpmic1.txt + create mode 100644 Documentation/devicetree/bindings/mfd/stmfx.txt + create mode 100644 Documentation/devicetree/bindings/mtd/stm32-fmc2-nand.txt + create mode 100644 Documentation/devicetree/bindings/nvmem/st,stm32-romem.txt + create mode 100644 Documentation/devicetree/bindings/pinctrl/pinctrl-stmfx.txt + create mode 100644 Documentation/devicetree/bindings/regulator/st,stm32mp1-pwr-reg.txt + create mode 100644 Documentation/devicetree/bindings/regulator/st,stpmic1-regulator.txt + create mode 100644 Documentation/devicetree/bindings/remoteproc/rproc-srm.txt + create mode 100644 Documentation/devicetree/bindings/remoteproc/stm32-rproc.txt + create mode 100644 Documentation/devicetree/bindings/spi/spi-stm32-qspi.txt + create mode 100644 Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt + create mode 100644 arch/arm/boot/dts/stm32mp157a-dk1.dts + create mode 100644 arch/arm/boot/dts/stm32mp157c-dk2.dts + create mode 100644 arch/arm/boot/dts/stm32mp157c-m4-srm.dtsi + create mode 100644 arch/arm/boot/dts/stm32mp157caa-pinctrl.dtsi + create mode 100644 arch/arm/boot/dts/stm32mp157cab-pinctrl.dtsi + create mode 100644 arch/arm/boot/dts/stm32mp157cac-pinctrl.dtsi + create mode 100644 arch/arm/boot/dts/stm32mp157cad-pinctrl.dtsi + +diff --git a/Documentation/devicetree/bindings/dma/stm32-dma.txt b/Documentation/devicetree/bindings/dma/stm32-dma.txt +index c5f5190..163be09 100644 +--- a/Documentation/devicetree/bindings/dma/stm32-dma.txt ++++ b/Documentation/devicetree/bindings/dma/stm32-dma.txt +@@ -17,6 +17,12 @@ Optional properties: + - resets: Reference to a reset controller asserting the DMA controller + - st,mem2mem: boolean; if defined, it indicates that the controller supports + memory-to-memory transfer ++- dmas: A list of eight dma specifiers, one for each entry in dma-names. ++ Refer to stm32-mdma.txt for more details. ++- dma-names: should contain "ch0", "ch1", "ch2", "ch3", "ch4", "ch5", "ch6" and ++ "ch7" and represents each STM32 DMA channel connected to a STM32 MDMA one. ++- memory-region : phandle to a node describing memory to be used for ++ M2M intermediate transfer between DMA and MDMA. + + Example: + +@@ -36,6 +42,16 @@ Example: + st,mem2mem; + resets = <&rcc 150>; + dma-requests = <8>; ++ dmas = <&mdma1 8 0x10 0x1200000a 0x40026408 0x00000020 1>, ++ <&mdma1 9 0x10 0x1200000a 0x40026408 0x00000800 1>, ++ <&mdma1 10 0x10 0x1200000a 0x40026408 0x00200000 1>, ++ <&mdma1 11 0x10 0x1200000a 0x40026408 0x08000000 1>, ++ <&mdma1 12 0x10 0x1200000a 0x4002640C 0x00000020 1>, ++ <&mdma1 13 0x10 0x1200000a 0x4002640C 0x00000800 1>, ++ <&mdma1 14 0x10 0x1200000a 0x4002640C 0x00200000 1>, ++ <&mdma1 15 0x10 0x1200000a 0x4002640C 0x08000000 1>; ++ dma-names = "ch0", "ch1", "ch2", "ch3", "ch4", "ch5", "ch6", "ch7"; ++ memory-region = <&sram_dmapool>; + }; + + * DMA client +@@ -62,13 +78,21 @@ channel: a phandle to the DMA controller plus the following four integer cells: + 0x1: medium + 0x2: high + 0x3: very high +-4. A 32bit bitfield value specifying DMA features which are device dependent: ++4. A bitfield value specifying DMA features which are device dependent: + -bit 0-1: DMA FIFO threshold selection + 0x0: 1/4 full FIFO + 0x1: 1/2 full FIFO + 0x2: 3/4 full FIFO + 0x3: full FIFO +- ++ -bit 2: Intermediate M2M transfer from/to DDR to/from SRAM throughout MDMA ++ 0: MDMA not used to generate an intermediate M2M transfer ++ 1: MDMA used to generate an intermediate M2M transfer. ++ -bit 3-4: indicated SRAM Buffer size in (2^order)*PAGE_SIZE. ++ Order is given by those 2 bits starting at 0. ++ Valid only whether Intermediate M2M transfer is set. ++ For cyclic, whether Intermediate M2M transfer is chosen, any value can ++ be set: SRAM buffer size will rely on period size and not on this DT ++ value. + + Example: + +@@ -77,7 +101,7 @@ Example: + reg = <0x40011000 0x400>; + interrupts = <37>; + clocks = <&clk_pclk2>; +- dmas = <&dma2 2 4 0x10400 0x3>, +- <&dma2 7 5 0x10200 0x3>; ++ dmas = <&dma2 2 4 0x10400 0x1>, ++ <&dma2 7 5 0x10200 0x1>; + dma-names = "rx", "tx"; + }; +diff --git a/Documentation/devicetree/bindings/dma/stm32-dmamux.txt b/Documentation/devicetree/bindings/dma/stm32-dmamux.txt +index 1b893b2..8e092d2 100644 +--- a/Documentation/devicetree/bindings/dma/stm32-dmamux.txt ++++ b/Documentation/devicetree/bindings/dma/stm32-dmamux.txt +@@ -4,9 +4,6 @@ Required properties: + - compatible: "st,stm32h7-dmamux" + - reg: Memory map for accessing module + - #dma-cells: Should be set to <3>. +- First parameter is request line number. +- Second is DMA channel configuration +- Third is Fifo threshold + For more details about the three cells, please see + stm32-dma.txt documentation binding file + - dma-masters: Phandle pointing to the DMA controllers. +@@ -53,7 +50,7 @@ dma2: dma@40020400 { + <68>, + <69>, + <70>; +- clocks = <&timer_clk>; ++ clocks = <&clk_hclk>; + #dma-cells = <4>; + st,mem2mem; + resets = <&rcc 150>; +diff --git a/Documentation/devicetree/bindings/dma/stm32-mdma.txt b/Documentation/devicetree/bindings/dma/stm32-mdma.txt +index d18772d..1810f87 100644 +--- a/Documentation/devicetree/bindings/dma/stm32-mdma.txt ++++ b/Documentation/devicetree/bindings/dma/stm32-mdma.txt +@@ -10,7 +10,7 @@ Required properties: + - interrupts: Should contain the MDMA interrupt. + - clocks: Should contain the input clock of the DMA instance. + - resets: Reference to a reset controller asserting the DMA controller. +-- #dma-cells : Must be <5>. See DMA client paragraph for more details. ++- #dma-cells : Must be <6>. See DMA client paragraph for more details. + + Optional properties: + - dma-channels: Number of DMA channels supported by the controller. +@@ -26,7 +26,7 @@ Example: + interrupts = <122>; + clocks = <&timer_clk>; + resets = <&rcc 992>; +- #dma-cells = <5>; ++ #dma-cells = <6>; + dma-channels = <16>; + dma-requests = <32>; + st,ahb-addr-masks = <0x20000000>, <0x00000000>; +@@ -35,8 +35,8 @@ Example: + * DMA client + + DMA clients connected to the STM32 MDMA controller must use the format +-described in the dma.txt file, using a five-cell specifier for each channel: +-a phandle to the MDMA controller plus the following five integer cells: ++described in the dma.txt file, using a six-cell specifier for each channel: ++a phandle to the MDMA controller plus the following six integer cells: + + 1. The request line number + 2. The priority level +@@ -76,19 +76,23 @@ a phandle to the MDMA controller plus the following five integer cells: + if no HW ack signal is used by the MDMA client + 5. A 32bit mask specifying the value to be written to acknowledge the request + if no HW ack signal is used by the MDMA client ++6. A bitfield value specifying if the MDMA client wants to generate M2M ++ transfer with HW trigger (1) or not (0). This bitfield should be only ++ enabled for M2M transfer triggered by STM32 DMA client. The memory devices ++ involved in this kind of transfer are SRAM and DDR. + + Example: + + i2c4: i2c@5c002000 { + compatible = "st,stm32f7-i2c"; + reg = <0x5c002000 0x400>; +- interrupts = <95>, +- <96>; +- clocks = <&timer_clk>; ++ interrupts = , ++ ; ++ clocks = <&clk_hsi>; + #address-cells = <1>; + #size-cells = <0>; +- dmas = <&mdma1 36 0x0 0x40008 0x0 0x0>, +- <&mdma1 37 0x0 0x40002 0x0 0x0>; ++ dmas = <&mdma1 36 0x0 0x40008 0x0 0x0 0>, ++ <&mdma1 37 0x0 0x40002 0x0 0x0 0>; + dma-names = "rx", "tx"; + status = "disabled"; + }; +diff --git a/Documentation/devicetree/bindings/i2c/i2c-stm32.txt b/Documentation/devicetree/bindings/i2c/i2c-stm32.txt +index 3b54899..e76fe82 100644 +--- a/Documentation/devicetree/bindings/i2c/i2c-stm32.txt ++++ b/Documentation/devicetree/bindings/i2c/i2c-stm32.txt +@@ -7,10 +7,12 @@ Required properties : + - reg : Offset and length of the register set for the device + - interrupts : Must contain the interrupt id for I2C event and then the + interrupt id for I2C error. ++ Optionnaly a wakeup interrupt may be specified. + - resets: Must contain the phandle to the reset controller. + - clocks: Must contain the input clock of the I2C instance. + - A pinctrl state named "default" must be defined to set pins in mode of +- operation for I2C transfer ++ operation for I2C transfer. An optional pinctrl state named "sleep" has to ++ be defined as well as to put I2C in low power mode in suspend mode. + - #address-cells = <1>; + - #size-cells = <0>; + +@@ -26,6 +28,11 @@ Optional properties : + - i2c-scl-falling-time-ns : Only for STM32F7, I2C SCL Falling time for the board + (default: 10) + I2C Timings are derived from these 2 values ++- st,syscfg-fmp: Only for STM32F7, use to set Fast Mode Plus bit within SYSCFG ++ whether Fast Mode Plus speed is selected by slave. ++ 1st cell : phandle to syscfg ++ 2nd cell : register offset within SYSCFG ++ 3rd cell : register bitmask for FMP bit + + Example : + +@@ -52,5 +59,42 @@ Example : + resets = <&rcc STM32F7_APB1_RESET(I2C1)>; + clocks = <&rcc 1 CLK_I2C1>; + pinctrl-0 = <&i2c1_sda_pin>, <&i2c1_scl_pin>; +- pinctrl-names = "default"; ++ pinctrl-1 = <&i2c1_sda_pin_sleep>, <&i2c1_scl_pin_sleep>; ++ pinctrl-names = "default", "sleep"; ++ st,syscfg-fmp = <&syscfg 0x4 0x1>; ++ }; ++ ++ i2c@40013000 { ++ compatible = "st,stm32f7-i2c"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0x40013000 0x400>; ++ interrupt-names = "event", "error", "wakeup"; ++ interrupts-extended = <&intc GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>, ++ <&intc GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>, ++ <&exti 22 1>; ++ clocks = <&rcc I2C2_K>; ++ resets = <&rcc I2C2_R>; ++ st,syscfg-fmp = <&syscfg 0x4 0x2>; ++ }; ++ ++ ++* I2C Devices ++ ++An I2C device connected onto STM32 I2C controller must use a format described by ++i2c.txt file. ++ ++Required properties : ++- compatible ++ Device driver compatible name ++- reg ++ I2C slave addresses (see i2c.txt for more details) ++ ++Optional properties : ++ ++ i2c@40013000 { ++ camera@3c { ++ compatible = "ovti,ov5640"; ++ reg = <0x3c>; ++ }; + }; +diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt +index 8346bcb..c46598c 100644 +--- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt ++++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt +@@ -46,6 +46,8 @@ Required properties: + Optional properties: + - A pinctrl state named "default" for each ADC channel may be defined to set + inX ADC pins in mode of operation for analog input on external pin. ++- st,max-clk-rate-hz: Allow to specify desired max clock rate used by analog ++ circuitry. + + Contents of a stm32 adc child node: + ----------------------------------- +@@ -63,8 +65,8 @@ Required properties: + - interrupts: IRQ Line for the ADC (e.g. may be 0 for adc@0, 1 for adc@100 or + 2 for adc@200). + - st,adc-channels: List of single-ended channels muxed for this ADC. +- It can have up to 16 channels on stm32f4 or 20 channels on stm32h7, numbered +- from 0 to 15 or 19 (resp. for in0..in15 or in0..in19). ++ It can have up to 19 channels on stm32f4 or 20 channels on stm32h7, numbered ++ from 0 to 18 or 19 (resp. for in0..in18 or in0..in19). + - st,adc-diff-channels: List of differential channels muxed for this ADC. + Depending on part used, some channels can be configured as differential + instead of single-ended (e.g. stm32h7). List here positive and negative +@@ -91,6 +93,38 @@ Optional properties: + fine tune of ADC sampling time may be recommended. + This can be either one value or an array that matches 'st,adc-channels' list, + to set sample time resp. for all channels, or independently for each channel. ++- st,trigger-polarity: Must be 0 (default), 1 or 2 to set default trigger ++ polarity to respectively "rising-edge", "falling-edge" or "both-edges". ++- st,injected: Use injected conversion sequence on an ADC, rather than regular. ++ ++Contents of a STM32 ADC temperature child node: ++----------------------------------------------- ++Required properties: ++- compatible: Should be one of: ++ "st,stm32f4-adc-temp" ++ "st,stm32h7-adc-temp" ++ "st,stm32mp1-adc-temp" ++- io-channels: Phandle to STM32 ADC temperature channel. ++- #io-channel-cells = <0>; ++- #thermal-sensor-cells = <0>; ++ ++Optional properties: ++- nvmem-cells: Phandles to nvmem cells that contain "ts_cal1" and "ts_cal2". ++- nvmem-cell-names: Must be "ts_cal1", "ts_cal2". ++ ++Contents of a stm32 adc EXTI trigger child node: ++------------------------------------------------ ++EXTI (External interrupt) can be used by STM32ADC as trigger source for ++conversions. ADC may use up to two EXTI GPIO lines: 11 & 15, e.g. external ++trigger signal can be routed to GPIOx (x is bank) pin 11 and/or 15. So, ++exti trigger child node is optional. ++ ++Required properties: ++- trigger-name: Must be exti11 (regular) or exti15 (injected). ++- interrupts: The exti interrupt source used as trigger. Generic interrupt ++ client node as described in ../../interrupt-controller/interrupts.txt ++ EXTI IRQ number must match with above trigger-name (e.g. 11 or 15). ++- interrupt-parent: Must be phandle to gpio bank. + + Example: + adc: adc@40012000 { +@@ -119,9 +153,16 @@ Example: + dmas = <&dma2 0 0 0x400 0x0>; + dma-names = "rx"; + assigned-resolution-bits = <8>; ++ st,trigger-polarity = <1>; + }; + ... + other adc child nodes follow... ++ ++ exti11 { ++ trigger-name = "exti11"; ++ interrupts = <11 0>; ++ interrupt-parent = <&gpioa>; ++ }; + }; + + Example to setup: +@@ -138,3 +179,22 @@ Example to setup: + st,adc-diff-channels = <2 6>, <3 7>; + }; + }; ++ ++Temperature sensor example: ++ adc: adc@40012000 { ++ compatible = "st,stm32f4-adc-core"; ++ ... ++ adc1: adc@0 { ++ ... ++ st,adc-channels = <18>; ++ st,min-sample-time-nsecs = <10000>; ++ }; ++ adc_temp: temp { ++ compatible = "st,stm32f4-adc-temp"; ++ io-channels = <&adc1 18>; ++ nvmem-cells = <&ts_cal1>, <&ts_cal2>; ++ nvmem-cell-names = "ts_cal1", "ts_cal2"; ++ #io-channel-cells = <0>; ++ #thermal-sensor-cells = <0>; ++ }; ++ }; +diff --git a/Documentation/devicetree/bindings/iio/counter/stm32-lptimer-cnt.txt b/Documentation/devicetree/bindings/iio/counter/stm32-lptimer-cnt.txt +index a04aa5c..e90bc47 100644 +--- a/Documentation/devicetree/bindings/iio/counter/stm32-lptimer-cnt.txt ++++ b/Documentation/devicetree/bindings/iio/counter/stm32-lptimer-cnt.txt +@@ -10,8 +10,9 @@ See ../mfd/stm32-lptimer.txt for details about the parent node. + + Required properties: + - compatible: Must be "st,stm32-lptimer-counter". +-- pinctrl-names: Set to "default". +-- pinctrl-0: List of phandles pointing to pin configuration nodes, ++- pinctrl-names: Set to "default". An additional "sleep" state can be ++ defined to set pins in sleep state. ++- pinctrl-n: List of phandles pointing to pin configuration nodes, + to set IN1/IN2 pins in mode of operation for Low-Power + Timer input on external pin. + +@@ -21,7 +22,8 @@ Example: + ... + counter { + compatible = "st,stm32-lptimer-counter"; +- pinctrl-names = "default"; ++ pinctrl-names = "default", "sleep"; + pinctrl-0 = <&lptim1_in_pins>; ++ pinctrl-1 = <&lptim1_sleep_in_pins>; + }; + }; +diff --git a/Documentation/devicetree/bindings/iio/timer/stm32-timer-trigger.txt b/Documentation/devicetree/bindings/iio/timer/stm32-timer-trigger.txt +index b8e8c76..4713ff1 100644 +--- a/Documentation/devicetree/bindings/iio/timer/stm32-timer-trigger.txt ++++ b/Documentation/devicetree/bindings/iio/timer/stm32-timer-trigger.txt +@@ -9,6 +9,12 @@ Required parameters: + "st,stm32h7-timer-trigger" + - reg: Identify trigger hardware block. + ++Optional properties: ++- pinctrl-names: Set to "default". An additional "sleep" state can be ++ defined to set pins in sleep state when in low power. ++- pinctrl-n: Phandle(s) pointing to pin configuration node for PWM, ++ respectively for "default" and "sleep" states. ++ + Example: + timers@40010000 { + #address-cells = <1>; +@@ -21,5 +27,8 @@ Example: + timer@0 { + compatible = "st,stm32-timer-trigger"; + reg = <0>; ++ pinctrl-0 = <&tim1_pins>; ++ pinctrl-1 = <&tim1_sleep_pins>; ++ pinctrl-names = "default", "sleep"; + }; + }; +diff --git a/Documentation/devicetree/bindings/input/st,stpmic1-onkey.txt b/Documentation/devicetree/bindings/input/st,stpmic1-onkey.txt +new file mode 100644 +index 0000000..4494613 +--- /dev/null ++++ b/Documentation/devicetree/bindings/input/st,stpmic1-onkey.txt +@@ -0,0 +1,28 @@ ++STMicroelectronics STPMIC1 Onkey ++ ++Required properties: ++ ++- compatible = "st,stpmic1-onkey"; ++- interrupts: interrupt line to use ++- interrupt-names = "onkey-falling", "onkey-rising" ++ onkey-falling: happens when onkey is pressed; IT_PONKEY_F of pmic ++ onkey-rising: happens when onkey is released; IT_PONKEY_R of pmic ++ ++Optional properties: ++ ++- st,onkey-clear-cc-flag: onkey is able power on after an ++ over-current shutdown event. ++- st,onkey-pu-inactive: onkey pull up is not active ++- power-off-time-sec: Duration in seconds which the key should be kept ++ pressed for device to power off automatically (from 1 to 16 seconds). ++ see See Documentation/devicetree/bindings/input/keys.txt ++ ++Example: ++ ++onkey { ++ compatible = "st,stpmic1-onkey"; ++ interrupt-parent = <&pmic>; ++ interrupts = ,; ++ interrupt-names = "onkey-falling", "onkey-rising"; ++ power-off-time-sec = <10>; ++}; +diff --git a/Documentation/devicetree/bindings/mfd/st,stm32mp1-pwr.txt b/Documentation/devicetree/bindings/mfd/st,stm32mp1-pwr.txt +new file mode 100644 +index 0000000..8e04895 +--- /dev/null ++++ b/Documentation/devicetree/bindings/mfd/st,stm32mp1-pwr.txt +@@ -0,0 +1,53 @@ ++STMicroelectronics STM32MP1 Power Management Controller ++======================================================= ++ ++The PWR IP is responsible for handling the power related resources such as ++clocks, power supplies and resets. It provides 6 wake-up pins that are handled ++by an interrupt-controller. Wake-up pin can be used to wake-up from STANDBY SoC state. ++ ++Required properties: ++- compatible should be: "st,stm32mp1-pwr" ++- reg: should be register base and length as documented in the ++ datasheet ++- interrupts: contains the reference to the gic wake-up pin interrupt ++- interrupt-controller; Enable interrupt controller for wake-up pins. ++- #interrupt-cells = <3> ++ ++Optional Properties: ++- pwr-supply: main soc power supply ++ ++Interrupt consumers have to specify 3 cells: ++ - cell 1: wake-up pin id from 0 to 5 ++ - cell 2: IRQ_TYPE_EDGE_FALLING or IRQ_TYPE_EDGE_RISING ++ - cell 3: Pull config: 0 = No Pull, 1=Pull Up, 2=Pull Down ++ ++ ++Example: ++ ++ pwr: pwr@50001000 { ++ compatible = "st,stm32mp1-pwr", "simple-mfd"; ++ reg = <0x50001000 0x400>; ++ interrupts = ; ++ interrupt-controller; ++ #interrupt-cells = <3>; ++ ++ pwr-supply = <&vdd>; ++ }; ++ ++ ++Example of interrupt user: ++gpio_keys { ++ compatible = "gpio-keys"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ button@4 { ++ label = "WakeUp4"; ++ linux,code = ; ++ interrupt-parent = <&pwr>; ++ interrupts = <3 IRQ_TYPE_EDGE_FALLING 1>; ++ status = "okay"; ++ wakeup-source; ++ }; ++}; ++ +diff --git a/Documentation/devicetree/bindings/mfd/st,stpmic1.txt b/Documentation/devicetree/bindings/mfd/st,stpmic1.txt +new file mode 100644 +index 0000000..54b64e2 +--- /dev/null ++++ b/Documentation/devicetree/bindings/mfd/st,stpmic1.txt +@@ -0,0 +1,132 @@ ++* STMicroelectronics STPMIC1 Power Management IC ++ ++Required parent device properties: ++- compatible: "st,stpmic1" ++- reg: The I2C slave address for the STPMIC1 chip. ++- interrupts: The interrupt lines the device is connected to. ++ The second interrupt is used for wake-up. ++- #interrupt-cells: Should be 2. ++- interrupt-controller: Describes the STPMIC1 as an interrupt ++ controller (has its own domain). Interrupt number are the following: ++ /* Interrupt Register 1 (0x50 for latch) */ ++ IT_SWOUT_R=0 ++ IT_SWOUT_F=1 ++ IT_VBUS_OTG_R=2 ++ IT_VBUS_OTG_F=3 ++ IT_WAKEUP_R=4 ++ IT_WAKEUP_F=5 ++ IT_PONKEY_R=6 ++ IT_PONKEY_F=7 ++ /* Interrupt Register 2 (0x51 for latch) */ ++ IT_OVP_BOOST=8 ++ IT_OCP_BOOST=9 ++ IT_OCP_SWOUT=10 ++ IT_OCP_OTG=11 ++ IT_CURLIM_BUCK4=12 ++ IT_CURLIM_BUCK3=13 ++ IT_CURLIM_BUCK2=14 ++ IT_CURLIM_BUCK1=15 ++ /* Interrupt Register 3 (0x52 for latch) */ ++ IT_SHORT_SWOUT=16 ++ IT_SHORT_SWOTG=17 ++ IT_CURLIM_LDO6=18 ++ IT_CURLIM_LDO5=19 ++ IT_CURLIM_LDO4=20 ++ IT_CURLIM_LDO3=21 ++ IT_CURLIM_LDO2=22 ++ IT_CURLIM_LDO1=23 ++ /* Interrupt Register 3 (0x52 for latch) */ ++ IT_SWIN_R=24 ++ IT_SWIN_F=25 ++ IT_RESERVED_1=26 ++ IT_RESERVED_2=27 ++ IT_VINLOW_R=28 ++ IT_VINLOW_F=29 ++ IT_TWARN_R=30 ++ IT_TWARN_F=31 ++ ++Optional parent device properties: ++- st,main-control-register: ++ -bit 1: Power cycling will be performed on turn OFF condition ++ -bit 2: PWRCTRL is functional ++ -bit 3: PWRCTRL active high ++- st,pads-pull-register: ++ -bit 1: WAKEUP pull down is not active ++ -bit 2: PWRCTRL pull up is active ++ -bit 3: PWRCTRL pull down is active ++ -bit 4: WAKEUP detector is disabled ++- st,vin-control-register: ++ -bit 0: VINLOW monitoring is enabled ++ -bit [1...3]: VINLOW rising threshold ++ 000 VINOK_f + 50mV ++ 001 VINOK_f + 100mV ++ 010 VINOK_f + 150mV ++ 011 VINOK_f + 200mV ++ 100 VINOK_f + 250mV ++ 101 VINOK_f + 300mV ++ 110 VINOK_f + 350mV ++ 111 VINOK_f + 400mV ++ -bit [4...5]: VINLOW hyst ++ 00 100mV ++ 01 200mV ++ 10 300mV ++ 11 400mV ++ -bit 6: SW_OUT detector is disabled ++ -bit 7: SW_IN detector is enabled. ++- st,usb-control-register: ++ -bit 3: SW_OUT current limit ++ 0: 600mA ++ 1: 1.1A ++ -bit 4: VBUS_OTG discharge is enabled ++ -bit 5: SW_OUT discharge is enabled ++ -bit 6: VBUS_OTG detection is enabled ++ -bit 7: BOOST_OVP is disabled ++ ++STPMIC1 consists in a varied group of sub-devices. ++Each sub-device binding is be described in own documentation file. ++ ++Device Description ++------ ------------ ++st,stpmic1-onkey : Power on key, see ../input/st,stpmic1-onkey.txt ++st,stpmic1-regulators : Regulators, see ../regulator/st,stpmic1-regulator.txt ++st,stpmic1-wdt : Watchdog, see ../watchdog/st,stpmic1-wdt.txt ++ ++Example: ++ ++pmic: pmic@33 { ++ compatible = "st,stpmic1"; ++ reg = <0x33>; ++ interrupt-parent = <&gpioa>; ++ interrupts = <0 2>; ++ st,main-control-register=<0x0c>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ ++ onkey { ++ compatible = "st,stpmic1-onkey"; ++ interrupts = ,; ++ interrupt-names = "onkey-falling", "onkey-rising"; ++ power-off-time-sec = <10>; ++ }; ++ ++ watchdog { ++ compatible = "st,stpmic1-wdt"; ++ }; ++ ++ regulators { ++ compatible = "st,stpmic1-regulators"; ++ ++ vdd_core: buck1 { ++ regulator-name = "vdd_core"; ++ regulator-boot-on; ++ regulator-min-microvolt = <700000>; ++ regulator-max-microvolt = <1200000>; ++ }; ++ vdd: buck3 { ++ regulator-name = "vdd"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-boot-on; ++ regulator-pull-down; ++ }; ++ }; +diff --git a/Documentation/devicetree/bindings/mfd/stmfx.txt b/Documentation/devicetree/bindings/mfd/stmfx.txt +new file mode 100644 +index 0000000..f0c2f7f +--- /dev/null ++++ b/Documentation/devicetree/bindings/mfd/stmfx.txt +@@ -0,0 +1,28 @@ ++STMicroelectonics Multi-Function eXpander (STMFX) Core bindings ++ ++ST Multi-Function eXpander (STMFX) is a slave controller using I2C for ++communication with the main MCU. Its main features are GPIO expansion, main ++MCU IDD measurement (IDD is the amount of current that flows through VDD) and ++resistive touchscreen controller. ++ ++Required properties: ++- compatible: should be "st,stmfx-0300". ++- reg: I2C slave address of the device. ++- interrupts: interrupt specifier triggered by MFX_IRQ_OUT signal. ++ Please refer to ../interrupt-controller/interrupt.txt ++ ++Optional properties: ++- drive-open-drain: configure MFX_IRQ_OUT as open drain. ++- vdd-supply: phandle of the regulator supplying STMFX. ++ ++Example: ++ ++ stmfx: stmfx@42 { ++ compatible = "st,stmfx-0300"; ++ reg = <0x42>; ++ interrupts = <8 IRQ_TYPE_EDGE_RISING>; ++ interrupt-parent = <&gpioi>; ++ vdd-supply = <&v3v3>; ++ }; ++ ++Please refer to ../pinctrl/pinctrl-stmfx.txt for STMFX GPIO expander function bindings. +diff --git a/Documentation/devicetree/bindings/mmc/mmci.txt b/Documentation/devicetree/bindings/mmc/mmci.txt +index 03796cf..6d3c626 100644 +--- a/Documentation/devicetree/bindings/mmc/mmci.txt ++++ b/Documentation/devicetree/bindings/mmc/mmci.txt +@@ -15,8 +15,11 @@ Required properties: + Optional properties: + - arm,primecell-periphid : contains the PrimeCell Peripheral ID, it overrides + the ID provided by the HW ++- resets : phandle to internal reset line. ++ Should be defined for sdmmc variant. + - vqmmc-supply : phandle to the regulator device tree node, mentioned + as the VCCQ/VDD_IO supply in the eMMC/SD specs. ++specific for ux500 variant: + - st,sig-dir-dat0 : bus signal direction pin used for DAT[0]. + - st,sig-dir-dat2 : bus signal direction pin used for DAT[2]. + - st,sig-dir-dat31 : bus signal direction pin used for DAT[3] and DAT[1]. +@@ -24,6 +27,14 @@ Optional properties: + - st,sig-dir-cmd : cmd signal direction pin used for CMD. + - st,sig-pin-fbclk : feedback clock signal pin used. + ++specific for sdmmc variant: ++- st,sig-dir : signal direction polarity used for cmd, dat0 dat123. ++- st,neg-edge : data & command phase relation, generated on ++ sd clock falling edge. ++- st,use-ckin : use ckin pin from an external driver to sample ++ the receive data (example: with voltage ++ switch transceiver). ++ + Deprecated properties: + - mmc-cap-mmc-highspeed : indicates whether MMC is high speed capable. + - mmc-cap-sd-highspeed : indicates whether SD is high speed capable. +diff --git a/Documentation/devicetree/bindings/mtd/stm32-fmc2-nand.txt b/Documentation/devicetree/bindings/mtd/stm32-fmc2-nand.txt +new file mode 100644 +index 0000000..70e76be +--- /dev/null ++++ b/Documentation/devicetree/bindings/mtd/stm32-fmc2-nand.txt +@@ -0,0 +1,59 @@ ++STMicroelectronics Flexible Memory Controller 2 (FMC2) ++NAND Interface ++ ++Required properties: ++- compatible: Should be one of: ++ * st,stm32mp15-fmc2 ++- reg: NAND flash controller memory areas. ++ First region contains the register location. ++ Regions 2 to 4 respectively contain the data, command, ++ and address space for CS0. ++ Regions 5 to 7 contain the same areas for CS1. ++- interrupts: The interrupt number ++- pinctrl-0: Standard Pinctrl phandle (see: pinctrl/pinctrl-bindings.txt) ++- clocks: The clock needed by the NAND flash controller ++ ++Optional properties: ++- resets: Reference to a reset controller asserting the FMC controller ++- dmas: DMA specifiers (see: dma/stm32-mdma.txt) ++- dma-names: Must be "tx", "rx" and "ecc" ++ ++Optional children nodes: ++Children nodes represent the available NAND chips. ++ ++Optional properties: ++- nand-on-flash-bbt: see nand.txt ++- nand-ecc-strength: see nand.txt ++- nand-ecc-step-size: see nand.txt ++ ++The following ECC strength and step size are currently supported: ++ - nand-ecc-strength = <1>, nand-ecc-step-size = <512> (Hamming) ++ - nand-ecc-strength = <4>, nand-ecc-step-size = <512> (BCH4) ++ - nand-ecc-strength = <8>, nand-ecc-step-size = <512> (BCH8) (default) ++ ++Example: ++ ++ fmc: nand-controller@58002000 { ++ compatible = "st,stm32mp15-fmc2"; ++ reg = <0x58002000 0x1000>, ++ <0x80000000 0x1000>, ++ <0x88010000 0x1000>, ++ <0x88020000 0x1000>, ++ <0x81000000 0x1000>, ++ <0x89010000 0x1000>, ++ <0x89020000 0x1000>; ++ interrupts = ; ++ clocks = <&rcc FMC_K>; ++ resets = <&rcc FMC_R>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&fmc_pins_a>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ nand@0 { ++ reg = <0>; ++ nand-on-flash-bbt; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ }; ++ }; +diff --git a/Documentation/devicetree/bindings/nvmem/st,stm32-romem.txt b/Documentation/devicetree/bindings/nvmem/st,stm32-romem.txt +new file mode 100644 +index 0000000..fbff52e +--- /dev/null ++++ b/Documentation/devicetree/bindings/nvmem/st,stm32-romem.txt +@@ -0,0 +1,31 @@ ++STMicroelectronics STM32 Factory-programmed data device tree bindings ++ ++This represents STM32 Factory-programmed read only non-volatile area: locked ++flash, OTP, read-only HW regs... This contains various information such as: ++analog calibration data for temperature sensor (e.g. TS_CAL1, TS_CAL2), ++internal vref (VREFIN_CAL), unique device ID... ++ ++Required properties: ++- compatible: Should be one of: ++ "st,stm32-romem" ++ "st,stm32mp15-bsec" ++- reg: Offset and length of factory-programmed area. ++- #address-cells: Should be '<1>'. ++- #size-cells: Should be '<1>'. ++ ++Optional Data cells: ++- Must be child nodes as described in nvmem.txt. ++ ++Example on stm32f4: ++ romem: nvmem@1fff7800 { ++ compatible = "st,stm32-romem"; ++ reg = <0x1fff7800 0x400>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ /* Data cells: ts_cal1 at 0x1fff7a2c */ ++ ts_cal1: calib@22c { ++ reg = <0x22c 0x2>; ++ }; ++ ... ++ }; +diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-stmfx.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-stmfx.txt +new file mode 100644 +index 0000000..c1b4c18 +--- /dev/null ++++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-stmfx.txt +@@ -0,0 +1,116 @@ ++STMicroelectronics Multi-Function eXpander (STMFX) GPIO expander bindings ++ ++ST Multi-Function eXpander (STMFX) offers up to 24 GPIOs expansion. ++Please refer to ../mfd/stmfx.txt for STMFX Core bindings. ++ ++Required properties: ++- compatible: should be "st,stmfx-0300-pinctrl". ++- #gpio-cells: should be <2>, the first cell is the GPIO number and the second ++ cell is the gpio flags in accordance with . ++- gpio-controller: marks the device as a GPIO controller. ++- #interrupt-cells: should be <2>, the first cell is the GPIO number and the ++ second cell is the interrupt flags in accordance with ++ . ++- interrupt-controller: marks the device as an interrupt controller. ++- gpio-ranges: specifies the mapping between gpio controller and pin ++ controller pins. Check "Concerning gpio-ranges property" below. ++Please refer to ../gpio/gpio.txt. ++ ++Please refer to pinctrl-bindings.txt for pin configuration. ++ ++Required properties for pin configuration sub-nodes: ++- pins: list of pins to which the configuration applies. ++ ++Optional properties for pin configuration sub-nodes (pinconf-generic ones): ++- bias-disable: disable any bias on the pin. ++- bias-pull-up: the pin will be pulled up. ++- bias-pull-pin-default: use the pin-default pull state. ++- bias-pull-down: the pin will be pulled down. ++- drive-open-drain: the pin will be driven with open drain. ++- drive-push-pull: the pin will be driven actively high and low. ++- output-high: the pin will be configured as an output driving high level. ++- output-low: the pin will be configured as an output driving low level. ++ ++Note that STMFX pins[15:0] are called "gpio[15:0]", and STMFX pins[23:16] are ++called "agpio[7:0]". Example, to refer to pin 18 of STMFX, use "agpio2". ++ ++Concerning gpio-ranges property: ++- if all STMFX pins[24:0] are available (no other STMFX function in use), you ++ should use gpio-ranges = <&stmfx_pinctrl 0 0 24>; ++- if agpio[3:0] are not available (STMFX Touchscreen function in use), you ++ should use gpio-ranges = <&stmfx_pinctrl 0 0 16>, <&stmfx_pinctrl 20 20 4>; ++- if agpio[7:4] are not available (STMFX IDD function in use), you ++ should use gpio-ranges = <&stmfx_pinctrl 0 0 20>; ++ ++ ++Example: ++ ++ stmfx: stmfx@42 { ++ ... ++ ++ stmfx_pinctrl: stmfx-pin-controller { ++ compatible = "st,stmfx-0300-pinctrl"; ++ #gpio-cells = <2>; ++ #interrupt-cells = <2>; ++ gpio-controller; ++ interrupt-controller; ++ gpio-ranges = <&stmfx_pinctrl 0 0 24>; ++ ++ joystick_pins: joystick { ++ pins = "gpio0", "gpio1", "gpio2", "gpio3", "gpio4"; ++ drive-push-pull; ++ bias-pull-up; ++ }; ++ }; ++ }; ++ ++Example of STMFX GPIO consumers: ++ ++ joystick { ++ compatible = "gpio-keys"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ pinctrl-0 = <&joystick_pins>; ++ pinctrl-names = "default"; ++ button-0 { ++ label = "JoySel"; ++ linux,code = ; ++ interrupt-parent = <&stmfx_pinctrl>; ++ interrupts = <0 IRQ_TYPE_EDGE_RISING>; ++ }; ++ button-1 { ++ label = "JoyDown"; ++ linux,code = ; ++ interrupt-parent = <&stmfx_pinctrl>; ++ interrupts = <1 IRQ_TYPE_EDGE_RISING>; ++ }; ++ button-2 { ++ label = "JoyLeft"; ++ linux,code = ; ++ interrupt-parent = <&stmfx_pinctrl>; ++ interrupts = <2 IRQ_TYPE_EDGE_RISING>; ++ }; ++ button-3 { ++ label = "JoyRight"; ++ linux,code = ; ++ interrupt-parent = <&stmfx_pinctrl>; ++ interrupts = <3 IRQ_TYPE_EDGE_RISING>; ++ }; ++ button-4 { ++ label = "JoyUp"; ++ linux,code = ; ++ interrupt-parent = <&stmfx_pinctrl>; ++ interrupts = <4 IRQ_TYPE_EDGE_RISING>; ++ }; ++ }; ++ ++ leds { ++ compatible = "gpio-leds"; ++ orange { ++ gpios = <&stmfx_pinctrl 17 1>; ++ }; ++ ++ blue { ++ gpios = <&stmfx_pinctrl 19 1>; ++ }; ++ } +diff --git a/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt +index ef4f2ff..286c981 100644 +--- a/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt ++++ b/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt +@@ -56,6 +56,8 @@ Optional properties: + More details in Documentation/devicetree/bindings/gpio/gpio.txt. + - st,bank-ioport: should correspond to the EXTI IOport selection (EXTI line + used to select GPIOs as interrupts). ++ - st,package: Indicates the SOC package used. ++ More details in include/dt-bindings/pinctrl/stm32-pinfunc.h + + Example 1: + #include +diff --git a/Documentation/devicetree/bindings/pwm/pwm-stm32-lp.txt b/Documentation/devicetree/bindings/pwm/pwm-stm32-lp.txt +index bd23302..6521bc4 100644 +--- a/Documentation/devicetree/bindings/pwm/pwm-stm32-lp.txt ++++ b/Documentation/devicetree/bindings/pwm/pwm-stm32-lp.txt +@@ -11,8 +11,10 @@ Required parameters: + bindings defined in pwm.txt. + + Optional properties: +-- pinctrl-names: Set to "default". +-- pinctrl-0: Phandle pointing to pin configuration node for PWM. ++- pinctrl-names: Set to "default". An additional "sleep" state can be ++ defined to set pins in sleep state when in low power. ++- pinctrl-n: Phandle(s) pointing to pin configuration node for PWM, ++ respectively for "default" and "sleep" states. + + Example: + timer@40002400 { +@@ -21,7 +23,8 @@ Example: + pwm { + compatible = "st,stm32-pwm-lp"; + #pwm-cells = <3>; +- pinctrl-names = "default"; ++ pinctrl-names = "default", "sleep"; + pinctrl-0 = <&lppwm1_pins>; ++ pinctrl-1 = <&lppwm1_sleep_pins>; + }; + }; +diff --git a/Documentation/devicetree/bindings/pwm/pwm-stm32.txt b/Documentation/devicetree/bindings/pwm/pwm-stm32.txt +index 3e6d550..0e7a30b 100644 +--- a/Documentation/devicetree/bindings/pwm/pwm-stm32.txt ++++ b/Documentation/devicetree/bindings/pwm/pwm-stm32.txt +@@ -5,8 +5,9 @@ See ../mfd/stm32-timers.txt for details about the parent node. + + Required parameters: + - compatible: Must be "st,stm32-pwm". +-- pinctrl-names: Set to "default". +-- pinctrl-0: List of phandles pointing to pin configuration nodes for PWM module. ++- pinctrl-names: Set to "default". An additional "sleep" state can be ++ defined to set pins in sleep state when in low power. ++- pinctrl-n: List of phandles pointing to pin configuration nodes for PWM module. + For Pinctrl properties see ../pinctrl/pinctrl-bindings.txt + + Optional parameters: +@@ -29,7 +30,8 @@ Example: + pwm { + compatible = "st,stm32-pwm"; + pinctrl-0 = <&pwm1_pins>; +- pinctrl-names = "default"; ++ pinctrl-1 = <&pwm1_sleep_pins>; ++ pinctrl-names = "default", "sleep"; + st,breakinput = <0 1 5>; + }; + }; +diff --git a/Documentation/devicetree/bindings/regulator/st,stm32mp1-pwr-reg.txt b/Documentation/devicetree/bindings/regulator/st,stm32mp1-pwr-reg.txt +new file mode 100644 +index 0000000..cee27d5 +--- /dev/null ++++ b/Documentation/devicetree/bindings/regulator/st,stm32mp1-pwr-reg.txt +@@ -0,0 +1,31 @@ ++STM32MP1 POWER Regulators ++------------------------- ++ ++Required properties: ++- compatible: Must be "st,stm32mp1,pwr-reg" ++- list of child nodes that specify the regulator ++ initialization data for defined regulators. The definition for each of ++ these nodes is defined using the standard binding for regulators found at ++ Documentation/devicetree/bindings/regulator/regulator.txt. ++- st,tzcr: syscon of Trust Zone Configuration Register. Usefull to know if we ++ are in secure mode. ++ st,tzcr = & ; ++ ++Example: ++ ++ pwr-regulators@c { ++ compatible = "st,stm32mp1,pwr-reg"; ++ st,tzcr = <&rcc 0x0 0x1>; ++ ++ reg11: reg11 { ++ regulator-name = "reg11"; ++ regulator-min-microvolt = <1100000>; ++ regulator-max-microvolt = <1100000>; ++ }; ++ ++ reg18: reg18 { ++ regulator-name = "reg18"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ }; ++ }; +diff --git a/Documentation/devicetree/bindings/regulator/st,stpmic1-regulator.txt b/Documentation/devicetree/bindings/regulator/st,stpmic1-regulator.txt +new file mode 100644 +index 0000000..a3f4762 +--- /dev/null ++++ b/Documentation/devicetree/bindings/regulator/st,stpmic1-regulator.txt +@@ -0,0 +1,68 @@ ++STMicroelectronics STPMIC1 Voltage regulators ++ ++Regulator Nodes are optional depending on needs. ++ ++Available Regulators in STPMIC1 device are: ++ - buck1 for Buck BUCK1 ++ - buck2 for Buck BUCK2 ++ - buck3 for Buck BUCK3 ++ - buck4 for Buck BUCK4 ++ - ldo1 for LDO LDO1 ++ - ldo2 for LDO LDO2 ++ - ldo3 for LDO LDO3 ++ - ldo4 for LDO LDO4 ++ - ldo5 for LDO LDO5 ++ - ldo6 for LDO LDO6 ++ - vref_ddr for LDO Vref DDR ++ - boost for Buck BOOST ++ - pwr_sw1 for VBUS_OTG switch ++ - pwr_sw2 for SW_OUT switch ++ ++Switches are fixed voltage regulators with only enable/disable capability. ++ ++Optional properties: ++- st,mask-reset: mask reset for this regulator: the regulator configuration ++ is maintained during pmic reset. ++- regulator-pull-down: enable high pull down ++ if not specified light pull down is used ++- regulator-over-current-protection: ++ if set, all regulators are switched off in case of over-current detection ++ on this regulator, ++ if not set, the driver only sends an over-current event. ++- interrupt-parent: phandle to the parent interrupt controller ++- interrupts: index of current limit detection interrupt ++- -supply: phandle to the parent supply/regulator node ++ each regulator supply can be described except vref_ddr. ++ ++Example: ++regulators { ++ compatible = "st,stpmic1-regulators"; ++ ++ ldo6-supply = <&v3v3>; ++ ++ vdd_core: buck1 { ++ regulator-name = "vdd_core"; ++ interrupts = ; ++ interrupt-parent = <&pmic>; ++ st,mask-reset; ++ regulator-pull-down; ++ regulator-min-microvolt = <700000>; ++ regulator-max-microvolt = <1200000>; ++ }; ++ ++ v3v3: buck4 { ++ regulator-name = "v3v3"; ++ interrupts = ; ++ interrupt-parent = <&mypmic>; ++ ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ ++ v1v8: ldo6 { ++ regulator-name = "v1v8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-over-current-protection; ++ }; ++}; +diff --git a/Documentation/devicetree/bindings/remoteproc/rproc-srm.txt b/Documentation/devicetree/bindings/remoteproc/rproc-srm.txt +new file mode 100644 +index 0000000..dce10c0 +--- /dev/null ++++ b/Documentation/devicetree/bindings/remoteproc/rproc-srm.txt +@@ -0,0 +1,57 @@ ++Remoteproc System Resource Manager ++---------------------------------- ++ ++The remoteproc SRM (System Resource Manager) handles resources allocated ++to remote processors. ++This makes it possible for remote proc to reserve and initialize system ++resources for a peripheral assigned to a coprocessor. ++ ++The devices are grouped in a core node ++ ++Core ++==== ++Required properties: ++- compatible: should be "rproc-srm-core" ++ ++Dev ++=== ++Required properties: ++- compatible: should be "rproc-srm-dev" ++ ++Optional properties: ++- reg: register base address and length ++- clocks: clocks required by the coprocessor ++- clock-names: see clock-bindings.txt ++- pinctrl-x: pins configurations required by the coprocessor ++- pinctrl-names: see pinctrl-bindings.txt. ++ "rproc_default" is a special pin configuration which is applied except ++ if the 'early-booted' property is set. ++ In a general way, it is recommended to use names prefixed with "rproc_". ++- x-supply: power supplies required by the coprocessor ++- interrupts: see interrupts.txt ++- interrupt-parent: see interrupts.txt ++- interrupt-names: see interrupts.txt ++ ++Example: ++ system_resources { ++ compatible = "rproc-srm-core"; ++ ++ mmc0: sdhci@09060000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x09060000 0x100>; ++ pinctrl-names = "rproc_default", "rproc_idle"; ++ pinctrl-0 = <&pinctrl_mmc0>; ++ pinctrl-1 = <&pinctrl_mmc1>; ++ clock-names = "mmc", "icn"; ++ clocks = <&clk_s_c0_flexgen CLK_MMC_0>, ++ <&clk_s_c0_flexgen CLK_RX_ICN_HVA>; ++ vdda-supply = <&vdda>; ++ }; ++ ++ button { ++ compatible = "rproc-srm-dev"; ++ interrupt-parent = <&gpioa>; ++ interrupts = <5 1>; ++ interrupt-names = "gpio_key"; ++ }; ++ }; +diff --git a/Documentation/devicetree/bindings/remoteproc/stm32-rproc.txt b/Documentation/devicetree/bindings/remoteproc/stm32-rproc.txt +new file mode 100644 +index 0000000..ee00f1c +--- /dev/null ++++ b/Documentation/devicetree/bindings/remoteproc/stm32-rproc.txt +@@ -0,0 +1,78 @@ ++STMicroelectronics STM32 Remoteproc ++----------------------------------- ++This document defines the binding for the remoteproc component that loads and ++boots firmwares on the ST32MP family chipset. ++ ++Required properties: ++- compatible: Must be "st,stm32mp1-rproc" ++- reg: Should contain the address ranges for specific internal memory ++ regions. ++- reg-names: Should contain the corresponding names for specific internal ++ memory regions. ++- resets: Reference to a reset controller asserting the remote processor. ++- reset-names: Must be "mcu_rst" ++- st,syscfg-holdboot: Reference to the system configuration controlling the ++ remote processor reset hold boot ++ 1st cell: phandle of syscon block ++ 2nd cell: register offset containing the hold boot setting ++ 3rd cell: register bitmask for the hold boot field ++- st,syscfg-tz: Reference to the system configuration controlling the trust zone ++ mode ++ 1st cell: phandle to syscon block ++ 2nd cell: register offset containing the trust zone mode setting ++ 3rd cell: register bitmask for the trust zone mode bit ++ ++Optional properties: ++- interrupt-parent: phandle to the interrupt controller node. ++- interrupts: Should contain the watchdog interrupt ++- interrupt-names: Must be "wdg" ++- mboxes: List of phandle and mailbox channel specifiers: ++ - a channel (a) used to communicate through virtqueues with the ++ remote proc. ++ Bi-directional channel: ++ - from local to remote = send message ++ - from remote to local = send message ack ++ - a channel (b) working the opposite direction of channel (a) ++ - a channel (c) used for two different purposes: ++ - used by the remote proc to signal when it has completed ++ its critical initalisation. ++ Mono-directional channel: from remote to local ++ - used by the local proc to notify the remote proc that it ++ is about to be shut down. ++ Mono-directional channel: from local to remote, where ACK ++ from the remote means that it is ready for shutdown ++- mbox-names: This property is required if the mboxes property is used. ++ - must be "vq0" for channel (a) ++ - must be "vq1" for channel (b) ++ - must be "init_shdn" for channel (c) ++- memory-region: phandle to the reserved memory node to be associated with the ++ remoteproc device. ++- st,syscfg-pdds: Reference to the system configuration controlling the remote ++ processor deep sleep setting ++ 1st cell: phandle to syscon block ++ 2nd cell: register offset containing the deep sleep setting ++ 3rd cell: register bitmask for the deep sleep bit ++- auto_boot: If defined, when remoteproc is probed, it looks for a default ++ firmware and if it finds some, it loads the firmware and starts ++ the remote processor. ++- recovery: If defined, remoteproc enables the crash recovery process. ++- early-booted: If defined, when remoteproc tries to boot a firmware, it ++ considers that the remote processor is already running and ++ attaches to this hardware state and updates accordingly (state, ++ resources, ...) ++- rsc-address: Resource table address of the early-booted firmware. Meaningful ++ only if 'early-booted' is defined. ++- rsc-size: Resource table size of the early-booted firmware. Meaningful ++ only if 'early-booted' is defined. ++ ++Example: ++ m4_rproc: m4 { ++ compatible = "st,stm32mp1-rproc"; ++ reg = <0x38000000 0x10000>, ++ <0x10000000 0x40000>; ++ reg-names = "retram", "mcusram"; ++ resets = <&rcc MCU_R>; ++ reset-names = "mcu_rst"; ++ st,syscfg-holdboot = <&rcc 0x10C 0x1>; ++ st,syscfg-tz = <&rcc 0x000 0x1>; ++ }; +diff --git a/Documentation/devicetree/bindings/rtc/st,stm32-rtc.txt b/Documentation/devicetree/bindings/rtc/st,stm32-rtc.txt +index 130ca5b..bab0df8 100644 +--- a/Documentation/devicetree/bindings/rtc/st,stm32-rtc.txt ++++ b/Documentation/devicetree/bindings/rtc/st,stm32-rtc.txt +@@ -21,9 +21,14 @@ Required properties: + domain (RTC registers) write protection. + It is required on stm32(f4/f7/h7). + +-Optional properties (to override default rtc_ck parent clock on stm32(f4/f7/h7): ++Optional properties: ++* to override default rtc_ck parent clock on stm32(f4/f7/h7): + - assigned-clocks: reference to the rtc_ck clock entry. + - assigned-clock-parents: phandle of the new parent clock of rtc_ck. ++* to select and enable RTC Low Speed Clock Output on stm32mp1: ++- st,lsco: defines the RTC output on which RTC Low-Speed Clock is Output. The ++ valid output values are defined in . ++- pinctrl state named "default" may be defined to reserve pin for RTC output. + + Example: + +@@ -58,4 +63,7 @@ Example: + clock-names = "pclk", "rtc_ck"; + interrupts-extended = <&intc GIC_SPI 3 IRQ_TYPE_NONE>, + <&exti 19 1>; ++ st,lsco = ; ++ pinctrl-0 = <&rtc_out2_rmp_pins_a>; ++ pinctrl-names = "default"; + }; +diff --git a/Documentation/devicetree/bindings/serial/st,stm32-usart.txt b/Documentation/devicetree/bindings/serial/st,stm32-usart.txt +index 9d3efed..90ba52f 100644 +--- a/Documentation/devicetree/bindings/serial/st,stm32-usart.txt ++++ b/Documentation/devicetree/bindings/serial/st,stm32-usart.txt +@@ -10,15 +10,48 @@ Required properties: + - interrupts: + - The interrupt line for the USART instance, + - An optional wake-up interrupt. ++- interrupt-names: Contains "event" for the USART interrupt line. + - clocks: The input clock of the USART instance + + Optional properties: +-- pinctrl: The reference on the pins configuration ++- pinctrl-names: Set to "default". An additional "sleep" state can be defined ++ to set pins in sleep state when in low power. In case the device is used as ++ a wakeup source, "idle" state is defined in order to keep RX pin active. ++ For a console device, an optional state "no_console_suspend" can be defined ++ to enable console messages during suspend. Typically, "no_console_suspend" and ++ "default" states can refer to the same pin configuration. ++- pinctrl-n: Phandle(s) pointing to pin configuration nodes. ++ For Pinctrl properties see ../pinctrl/pinctrl-bindings.txt + - st,hw-flow-ctrl: bool flag to enable hardware flow control. + - rs485-rts-delay, rs485-rx-during-tx, rs485-rts-active-low, + linux,rs485-enabled-at-boot-time: see rs485.txt. + - dmas: phandle(s) to DMA controller node(s). Refer to stm32-dma.txt + - dma-names: "rx" and/or "tx" ++- wakeup-source: bool flag to indicate this device has wakeup capabilities ++- interrupt-names : Should contain "wakeup" if optional wake-up interrupt is ++ used. ++ ++Note for dma using: ++- "tx" dma can be used without any constraint since it uses single ++dma transfers. ++- "rx" dma using requires some attention: ++ 1) if you cannot anticipate the length of your received packets ++ and if your usart device embeds an internal fifo, then DON'T use ++ dma mode. ++ 2) if you enable dma mode WITHOUT mdma intermediate copy (cf. ++ stm32-dma.txt), then the availability of the received data will ++ depend on the dma driver policy and it may be delayed until dma ++ internal fifo is full. The usart driver will see this checking ++ the dma residue when rx interrupt (RXNE or RTO) occurs. ++ 3) if you enable dma mode WITH mdma intermediate copy (cf. ++ stm32-dma.txt) then the usart driver will never see the dma ++ residue becoming smaller than RX_BUF_P but it will get its ++ rx dma complete callback called when the cyclic transfer period ++ (RX_BUF_P) is reached. ++The three possibilities above are ordered from the most cpu time ++consuming one to the least one. The counterpart of this optimisation ++is the reception granularity achievable by the usart driver, from ++one byte up to RX_BUF_P. + + Examples: + usart4: serial@40004c00 { +@@ -26,8 +59,11 @@ usart4: serial@40004c00 { + reg = <0x40004c00 0x400>; + interrupts = <52>; + clocks = <&clk_pclk1>; +- pinctrl-names = "default"; ++ pinctrl-names = "default", "sleep", "idle", "no_console_suspend"; + pinctrl-0 = <&pinctrl_usart4>; ++ pinctrl-1 = <&pinctrl_usart4_sleep>; ++ pinctrl-2 = <&pinctrl_usart4_idle>; ++ pinctrl-3 = <&pinctrl_usart4>; + }; + + usart2: serial@40004400 { +diff --git a/Documentation/devicetree/bindings/spi/spi-stm32-qspi.txt b/Documentation/devicetree/bindings/spi/spi-stm32-qspi.txt +new file mode 100644 +index 0000000..adeeb63 +--- /dev/null ++++ b/Documentation/devicetree/bindings/spi/spi-stm32-qspi.txt +@@ -0,0 +1,44 @@ ++* STMicroelectronics Quad Serial Peripheral Interface(QSPI) ++ ++Required properties: ++- compatible: should be "st,stm32f469-qspi" ++- reg: the first contains the register location and length. ++ the second contains the memory mapping address and length ++- reg-names: should contain the reg names "qspi" "qspi_mm" ++- interrupts: should contain the interrupt for the device ++- clocks: the phandle of the clock needed by the QSPI controller ++- A pinctrl must be defined to set pins in mode of operation for QSPI transfer ++ ++Optional properties: ++- resets: must contain the phandle to the reset controller. ++ ++A spi flash (NOR/NAND) must be a child of spi node and could have some ++properties. Also see jedec,spi-nor.txt. ++ ++Required properties: ++- reg: chip-Select number (QSPI controller may connect 2 flashes) ++- spi-max-frequency: max frequency of spi bus ++ ++Optional property: ++- spi-rx-bus-width: see ./spi-bus.txt for the description ++ ++Example: ++ ++qspi: spi@a0001000 { ++ compatible = "st,stm32f469-qspi"; ++ reg = <0xa0001000 0x1000>, <0x90000000 0x10000000>; ++ reg-names = "qspi", "qspi_mm"; ++ interrupts = <91>; ++ resets = <&rcc STM32F4_AHB3_RESET(QSPI)>; ++ clocks = <&rcc 0 STM32F4_AHB3_CLOCK(QSPI)>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_qspi0>; ++ ++ flash@0 { ++ compatible = "jedec,spi-nor"; ++ reg = <0>; ++ spi-rx-bus-width = <4>; ++ spi-max-frequency = <108000000>; ++ ... ++ }; ++}; +diff --git a/Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt b/Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt +new file mode 100644 +index 0000000..7cc1407 +--- /dev/null ++++ b/Documentation/devicetree/bindings/watchdog/st,stpmic1-wdt.txt +@@ -0,0 +1,11 @@ ++STMicroelectronics STPMIC1 Watchdog ++ ++Required properties: ++ ++- compatible : should be "st,stpmic1-wdt" ++ ++Example: ++ ++watchdog { ++ compatible = "st,stpmic1-wdt"; ++}; +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index b5bd3de..b264fa0 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -922,6 +922,8 @@ dtb-$(CONFIG_ARCH_STM32) += \ + stm32746g-eval.dtb \ + stm32h743i-eval.dtb \ + stm32h743i-disco.dtb \ ++ stm32mp157a-dk1.dtb \ ++ stm32mp157c-dk2.dtb \ + stm32mp157c-ed1.dtb \ + stm32mp157c-ev1.dtb + dtb-$(CONFIG_MACH_SUN4I) += \ +diff --git a/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi b/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi +index c485127..4409db2 100644 +--- a/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi ++++ b/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi +@@ -24,8 +24,7 @@ + reg = <0x0 0x400>; + clocks = <&rcc GPIOA>; + st,bank-name = "GPIOA"; +- ngpios = <16>; +- gpio-ranges = <&pinctrl 0 0 16>; ++ status = "disabled"; + }; + + gpiob: gpio@50003000 { +@@ -36,8 +35,7 @@ + reg = <0x1000 0x400>; + clocks = <&rcc GPIOB>; + st,bank-name = "GPIOB"; +- ngpios = <16>; +- gpio-ranges = <&pinctrl 0 16 16>; ++ status = "disabled"; + }; + + gpioc: gpio@50004000 { +@@ -48,8 +46,7 @@ + reg = <0x2000 0x400>; + clocks = <&rcc GPIOC>; + st,bank-name = "GPIOC"; +- ngpios = <16>; +- gpio-ranges = <&pinctrl 0 32 16>; ++ status = "disabled"; + }; + + gpiod: gpio@50005000 { +@@ -60,8 +57,7 @@ + reg = <0x3000 0x400>; + clocks = <&rcc GPIOD>; + st,bank-name = "GPIOD"; +- ngpios = <16>; +- gpio-ranges = <&pinctrl 0 48 16>; ++ status = "disabled"; + }; + + gpioe: gpio@50006000 { +@@ -72,8 +68,7 @@ + reg = <0x4000 0x400>; + clocks = <&rcc GPIOE>; + st,bank-name = "GPIOE"; +- ngpios = <16>; +- gpio-ranges = <&pinctrl 0 64 16>; ++ status = "disabled"; + }; + + gpiof: gpio@50007000 { +@@ -84,8 +79,7 @@ + reg = <0x5000 0x400>; + clocks = <&rcc GPIOF>; + st,bank-name = "GPIOF"; +- ngpios = <16>; +- gpio-ranges = <&pinctrl 0 80 16>; ++ status = "disabled"; + }; + + gpiog: gpio@50008000 { +@@ -96,8 +90,7 @@ + reg = <0x6000 0x400>; + clocks = <&rcc GPIOG>; + st,bank-name = "GPIOG"; +- ngpios = <16>; +- gpio-ranges = <&pinctrl 0 96 16>; ++ status = "disabled"; + }; + + gpioh: gpio@50009000 { +@@ -108,8 +101,7 @@ + reg = <0x7000 0x400>; + clocks = <&rcc GPIOH>; + st,bank-name = "GPIOH"; +- ngpios = <16>; +- gpio-ranges = <&pinctrl 0 112 16>; ++ status = "disabled"; + }; + + gpioi: gpio@5000a000 { +@@ -120,8 +112,7 @@ + reg = <0x8000 0x400>; + clocks = <&rcc GPIOI>; + st,bank-name = "GPIOI"; +- ngpios = <16>; +- gpio-ranges = <&pinctrl 0 128 16>; ++ status = "disabled"; + }; + + gpioj: gpio@5000b000 { +@@ -132,8 +123,7 @@ + reg = <0x9000 0x400>; + clocks = <&rcc GPIOJ>; + st,bank-name = "GPIOJ"; +- ngpios = <16>; +- gpio-ranges = <&pinctrl 0 144 16>; ++ status = "disabled"; + }; + + gpiok: gpio@5000c000 { +@@ -144,8 +134,29 @@ + reg = <0xa000 0x400>; + clocks = <&rcc GPIOK>; + st,bank-name = "GPIOK"; +- ngpios = <8>; +- gpio-ranges = <&pinctrl 0 160 8>; ++ status = "disabled"; ++ }; ++ ++ adc1_in6_pins_a: adc1-in6 { ++ pins { ++ pinmux = ; ++ }; ++ }; ++ ++ adc12_ain_pins_a: adc12-ain-0 { ++ pins { ++ pinmux = , /* ADC1 in13 */ ++ , /* ADC1 in6 */ ++ , /* ADC2 in2 */ ++ ; /* ADC2 in6 */ ++ }; ++ }; ++ ++ adc12_usb_pwr_pins_a: adc12-usb-pwr-pins-0 { ++ pins { ++ pinmux = , /* ADC12 in18 */ ++ ; /* ADC12 in19 */ ++ }; + }; + + cec_pins_a: cec-0 { +@@ -157,6 +168,57 @@ + }; + }; + ++ dac_ch1_pins_a: dac-ch1 { ++ pins { ++ pinmux = ; ++ }; ++ }; ++ ++ dac_ch2_pins_a: dac-ch2 { ++ pins { ++ pinmux = ; ++ }; ++ }; ++ ++ dfsdm_clkout_pins_a: dfsdm-clkout-pins-0 { ++ pins { ++ pinmux = ; /* DFSDM_CKOUT */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <1>; ++ }; ++ }; ++ ++ dfsdm_clkout_sleep_pins_a: dfsdm-clkout-sleep-pins-0 { ++ pins { ++ pinmux = ; /* DFSDM_CKOUT */ ++ }; ++ }; ++ ++ dfsdm_data1_pins_a: dfsdm-data1-pins-0 { ++ pins { ++ pinmux = ; /* DFSDM_DATA1 */ ++ }; ++ }; ++ ++ dfsdm_data1_sleep_pins_a: dfsdm-data1-sleep-pins-0 { ++ pins { ++ pinmux = ; /* DFSDM_DATA1 */ ++ }; ++ }; ++ ++ dfsdm_data3_pins_a: dfsdm-data3-pins-0 { ++ pins { ++ pinmux = ; /* DFSDM_DATA3 */ ++ }; ++ }; ++ ++ dfsdm_data3_sleep_pins_a: dfsdm-data3-sleep-pins-0 { ++ pins { ++ pinmux = ; /* DFSDM_DATA3 */ ++ }; ++ }; ++ + ethernet0_rgmii_pins_a: rgmii-0 { + pins1 { + pinmux = , /* ETH_RGMII_CLK125 */ +@@ -203,6 +265,50 @@ + }; + }; + ++ fmc_pins_a: fmc-0 { ++ pins1 { ++ pinmux = , /* FMC_NOE */ ++ , /* FMC_NWE */ ++ , /* FMC_A16_FMC_CLE */ ++ , /* FMC_A17_FMC_ALE */ ++ , /* FMC_D0 */ ++ , /* FMC_D1 */ ++ , /* FMC_D2 */ ++ , /* FMC_D3 */ ++ , /* FMC_D4 */ ++ , /* FMC_D5 */ ++ , /* FMC_D6 */ ++ , /* FMC_D7 */ ++ ; /* FMC_NE2_FMC_NCE */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <2>; ++ }; ++ pins2 { ++ pinmux = ; /* FMC_NWAIT */ ++ bias-pull-up; ++ }; ++ }; ++ ++ fmc_sleep_pins_a: fmc-sleep-0 { ++ pins { ++ pinmux = , /* FMC_NOE */ ++ , /* FMC_NWE */ ++ , /* FMC_A16_FMC_CLE */ ++ , /* FMC_A17_FMC_ALE */ ++ , /* FMC_D0 */ ++ , /* FMC_D1 */ ++ , /* FMC_D2 */ ++ , /* FMC_D3 */ ++ , /* FMC_D4 */ ++ , /* FMC_D5 */ ++ , /* FMC_D6 */ ++ , /* FMC_D7 */ ++ , /* FMC_NWAIT */ ++ ; /* FMC_NE2_FMC_NCE */ ++ }; ++ }; ++ + i2c1_pins_a: i2c1-0 { + pins { + pinmux = , /* I2C1_SCL */ +@@ -213,6 +319,13 @@ + }; + }; + ++ i2c1_pins_sleep_a: i2c1-1 { ++ pins { ++ pinmux = , /* I2C1_SCL */ ++ ; /* I2C1_SDA */ ++ }; ++ }; ++ + i2c2_pins_a: i2c2-0 { + pins { + pinmux = , /* I2C2_SCL */ +@@ -223,6 +336,13 @@ + }; + }; + ++ i2c2_pins_sleep_a: i2c2-1 { ++ pins { ++ pinmux = , /* I2C2_SCL */ ++ ; /* I2C2_SDA */ ++ }; ++ }; ++ + i2c5_pins_a: i2c5-0 { + pins { + pinmux = , /* I2C5_SCL */ +@@ -233,6 +353,14 @@ + }; + }; + ++ i2c5_pins_sleep_a: i2c5-1 { ++ pins { ++ pinmux = , /* I2C5_SCL */ ++ ; /* I2C5_SDA */ ++ ++ }; ++ }; ++ + m_can1_pins_a: m-can1-0 { + pins1 { + pinmux = ; /* CAN1_TX */ +@@ -246,6 +374,32 @@ + }; + }; + ++ m_can1_sleep_pins_a: m_can1-sleep@0 { ++ pins { ++ pinmux = , /* CAN1_TX */ ++ ; /* CAN1_RX */ ++ }; ++ }; ++ ++ pwm1_pins_a: pwm1-0 { ++ pins { ++ pinmux = , /* TIM1_CH1 */ ++ , /* TIM1_CH2 */ ++ ; /* TIM1_CH4 */ ++ bias-pull-down; ++ drive-push-pull; ++ slew-rate = <0>; ++ }; ++ }; ++ ++ pwm1_sleep_pins_a: pwm1-sleep-0 { ++ pins { ++ pinmux = , /* TIM1_CH1 */ ++ , /* TIM1_CH2 */ ++ ; /* TIM1_CH4 */ ++ }; ++ }; ++ + pwm2_pins_a: pwm2-0 { + pins { + pinmux = ; /* TIM2_CH4 */ +@@ -255,6 +409,74 @@ + }; + }; + ++ pwm2_sleep_pins_a: pwm2-sleep-0 { ++ pins { ++ pinmux = ; /* TIM2_CH4 */ ++ }; ++ }; ++ ++ pwm3_pins_a: pwm3-0 { ++ pins { ++ pinmux = ; /* TIM3_CH2 */ ++ bias-pull-down; ++ drive-push-pull; ++ slew-rate = <0>; ++ }; ++ }; ++ ++ pwm3_sleep_pins_a: pwm3-sleep-0 { ++ pins { ++ pinmux = ; /* TIM3_CH2 */ ++ }; ++ }; ++ ++ pwm4_pins_a: pwm4-0 { ++ pins { ++ pinmux = , /* TIM4_CH3 */ ++ ; /* TIM4_CH4 */ ++ bias-pull-down; ++ drive-push-pull; ++ slew-rate = <0>; ++ }; ++ }; ++ ++ pwm4_sleep_pins_a: pwm4-sleep-0 { ++ pins { ++ pinmux = , /* TIM4_CH3 */ ++ ; /* TIM4_CH4 */ ++ }; ++ }; ++ ++ pwm4_pins_b: pwm4-1 { ++ pins { ++ pinmux = ; /* TIM4_CH2 */ ++ bias-pull-down; ++ drive-push-pull; ++ slew-rate = <0>; ++ }; ++ }; ++ ++ pwm4_sleep_pins_b: pwm4-sleep-1 { ++ pins { ++ pinmux = ; /* TIM4_CH2 */ ++ }; ++ }; ++ ++ pwm5_pins_a: pwm5-0 { ++ pins { ++ pinmux = ; /* TIM5_CH2 */ ++ bias-pull-down; ++ drive-push-pull; ++ slew-rate = <0>; ++ }; ++ }; ++ ++ pwm5_sleep_pins_a: pwm5-sleep-0 { ++ pins { ++ pinmux = ; /* TIM5_CH2 */ ++ }; ++ }; ++ + pwm8_pins_a: pwm8-0 { + pins { + pinmux = ; /* TIM8_CH4 */ +@@ -264,6 +486,12 @@ + }; + }; + ++ pwm8_sleep_pins_a: pwm8-sleep-0 { ++ pins { ++ pinmux = ; /* TIM8_CH4 */ ++ }; ++ }; ++ + pwm12_pins_a: pwm12-0 { + pins { + pinmux = ; /* TIM12_CH1 */ +@@ -273,6 +501,12 @@ + }; + }; + ++ pwm12_sleep_pins_a: pwm12-sleep-0 { ++ pins { ++ pinmux = ; /* TIM12_CH1 */ ++ }; ++ }; ++ + qspi_clk_pins_a: qspi-clk-0 { + pins { + pinmux = ; /* QSPI_CLK */ +@@ -282,6 +516,12 @@ + }; + }; + ++ qspi_clk_sleep_pins_a: qspi-clk-sleep-0 { ++ pins { ++ pinmux = ; /* QSPI_CLK */ ++ }; ++ }; ++ + qspi_bk1_pins_a: qspi-bk1-0 { + pins1 { + pinmux = , /* QSPI_BK1_IO0 */ +@@ -300,6 +540,16 @@ + }; + }; + ++ qspi_bk1_sleep_pins_a: qspi-bk1-sleep-0 { ++ pins { ++ pinmux = , /* QSPI_BK1_IO0 */ ++ , /* QSPI_BK1_IO1 */ ++ , /* QSPI_BK1_IO2 */ ++ , /* QSPI_BK1_IO3 */ ++ ; /* QSPI_BK1_NCS */ ++ }; ++ }; ++ + qspi_bk2_pins_a: qspi-bk2-0 { + pins1 { + pinmux = , /* QSPI_BK2_IO0 */ +@@ -318,6 +568,180 @@ + }; + }; + ++ qspi_bk2_sleep_pins_a: qspi-bk2-sleep-0 { ++ pins { ++ pinmux = , /* QSPI_BK2_IO0 */ ++ , /* QSPI_BK2_IO1 */ ++ , /* QSPI_BK2_IO2 */ ++ , /* QSPI_BK2_IO3 */ ++ ; /* QSPI_BK2_NCS */ ++ }; ++ }; ++ ++ rtc_out2_rmp_pins_a: rtc-out2-rmp-pins@0 { ++ pins { ++ pinmux = ; /* RTC_OUT2_RMP */ ++ }; ++ }; ++ ++ sdmmc1_b4_pins_a: sdmmc1-b4-0 { ++ pins { ++ pinmux = , /* SDMMC1_D0 */ ++ , /* SDMMC1_D1 */ ++ , /* SDMMC1_D2 */ ++ , /* SDMMC1_D3 */ ++ , /* SDMMC1_CK */ ++ ; /* SDMMC1_CMD */ ++ slew-rate = <3>; ++ drive-push-pull; ++ bias-disable; ++ }; ++ }; ++ ++ sdmmc1_b4_od_pins_a: sdmmc1-b4-od-0 { ++ pins1 { ++ pinmux = , /* SDMMC1_D0 */ ++ , /* SDMMC1_D1 */ ++ , /* SDMMC1_D2 */ ++ , /* SDMMC1_D3 */ ++ ; /* SDMMC1_CK */ ++ slew-rate = <3>; ++ drive-push-pull; ++ bias-disable; ++ }; ++ pins2 { ++ pinmux = ; /* SDMMC1_CMD */ ++ slew-rate = <3>; ++ drive-open-drain; ++ bias-disable; ++ }; ++ }; ++ ++ sdmmc1_b4_sleep_pins_a: sdmmc1-b4-sleep-0 { ++ pins { ++ pinmux = , /* SDMMC1_D0 */ ++ , /* SDMMC1_D1 */ ++ , /* SDMMC1_D2 */ ++ , /* SDMMC1_D3 */ ++ , /* SDMMC1_CK */ ++ ; /* SDMMC1_CMD */ ++ }; ++ }; ++ ++ sdmmc1_dir_pins_a: sdmmc1-dir-0 { ++ pins1 { ++ pinmux = , /* SDMMC1_D0DIR */ ++ , /* SDMMC1_D123DIR */ ++ ; /* SDMMC1_CDIR */ ++ slew-rate = <3>; ++ drive-push-pull; ++ bias-pull-up; ++ }; ++ pins2{ ++ pinmux = ; /* SDMMC1_CKIN */ ++ bias-pull-up; ++ }; ++ }; ++ ++ sdmmc1_dir_sleep_pins_a: sdmmc1-dir-sleep-0 { ++ pins { ++ pinmux = , /* SDMMC1_D0DIR */ ++ , /* SDMMC1_D123DIR */ ++ , /* SDMMC1_CDIR */ ++ ; /* SDMMC1_CKIN */ ++ }; ++ }; ++ ++ sdmmc2_b4_pins_a: sdmmc2-b4-0 { ++ pins { ++ pinmux = , /* SDMMC2_D0 */ ++ , /* SDMMC2_D1 */ ++ , /* SDMMC2_D2 */ ++ , /* SDMMC2_D3 */ ++ , /* SDMMC2_CK */ ++ ; /* SDMMC2_CMD */ ++ slew-rate = <3>; ++ drive-push-pull; ++ bias-pull-up; ++ }; ++ }; ++ ++ sdmmc2_b4_od_pins_a: sdmmc2-b4-od-0 { ++ pins1 { ++ pinmux = , /* SDMMC2_D0 */ ++ , /* SDMMC2_D1 */ ++ , /* SDMMC2_D2 */ ++ , /* SDMMC2_D3 */ ++ ; /* SDMMC2_CK */ ++ slew-rate = <3>; ++ drive-push-pull; ++ bias-pull-up; ++ }; ++ pins2 { ++ pinmux = ; /* SDMMC2_CMD */ ++ slew-rate = <3>; ++ drive-open-drain; ++ bias-pull-up; ++ }; ++ }; ++ ++ sdmmc2_b4_sleep_pins_a: sdmmc2-b4-sleep-0 { ++ pins { ++ pinmux = , /* SDMMC2_D0 */ ++ , /* SDMMC2_D1 */ ++ , /* SDMMC2_D2 */ ++ , /* SDMMC2_D3 */ ++ , /* SDMMC2_CK */ ++ ; /* SDMMC2_CMD */ ++ }; ++ }; ++ ++ sdmmc2_d47_pins_a: sdmmc2-d47-0 { ++ pins { ++ pinmux = , /* SDMMC2_D4 */ ++ , /* SDMMC2_D5 */ ++ , /* SDMMC2_D6 */ ++ ; /* SDMMC2_D7 */ ++ slew-rate = <3>; ++ drive-push-pull; ++ bias-pull-up; ++ }; ++ }; ++ ++ sdmmc2_d47_sleep_pins_a: sdmmc2-d47-sleep-0 { ++ pins { ++ pinmux = , /* SDMMC2_D4 */ ++ , /* SDMMC2_D5 */ ++ , /* SDMMC2_D6 */ ++ ; /* SDMMC2_D7 */ ++ }; ++ }; ++ ++ sdmmc3_b4_pins_a: sdmmc3-b4-0 { ++ pins { ++ pinmux = , /* SDMMC3_D0 */ ++ , /* SDMMC3_D1 */ ++ , /* SDMMC3_D2 */ ++ , /* SDMMC3_D3 */ ++ , /* SDMMC3_CK */ ++ ; /* SDMMC3_CMD */ ++ slew-rate = <3>; ++ drive-push-pull; ++ bias-pull-up; ++ }; ++ }; ++ ++ sdmmc3_b4_sleep_pins_a: sdmmc3-b4-sleep-0 { ++ pins { ++ pinmux = , /* SDMMC3_D0 */ ++ , /* SDMMC3_D1 */ ++ , /* SDMMC3_D2 */ ++ , /* SDMMC3_D3 */ ++ , /* SDMMC3_CK */ ++ ; /* SDMMC3_CMD */ ++ }; ++ }; ++ + uart4_pins_a: uart4-0 { + pins1 { + pinmux = ; /* UART4_TX */ +@@ -330,6 +754,131 @@ + bias-disable; + }; + }; ++ ++ uart4_idle_pins_a: uart4-idle-0 { ++ pins1 { ++ pinmux = ; /* UART4_TX */ ++ }; ++ pins2 { ++ pinmux = ; /* UART4_RX */ ++ bias-disable; ++ }; ++ }; ++ ++ uart4_sleep_pins_a: uart4-sleep-0 { ++ pins { ++ pinmux = , /* UART4_TX */ ++ ; /* UART4_RX */ ++ }; ++ }; ++ ++ usart2_pins_a: usart2-0 { ++ pins1 { ++ pinmux = , /* USART2_TX */ ++ ; /* USART2_RTS */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <3>; ++ }; ++ pins2 { ++ pinmux = , /* USART2_RX */ ++ ; /* USART2_CTS_NSS */ ++ bias-disable; ++ }; ++ }; ++ ++ usart2_idle_pins_a: usart2-idle-0 { ++ pins1 { ++ pinmux = , /* USART2_TX */ ++ , /* USART2_RTS */ ++ ; /* USART2_CTS_NSS */ ++ }; ++ pins2 { ++ pinmux = ; /* USART2_RX */ ++ bias-disable; ++ }; ++ }; ++ ++ usart2_sleep_pins_a: usart2-sleep-0 { ++ pins { ++ pinmux = , /* USART2_TX */ ++ , /* USART2_RTS */ ++ , /* USART2_RX */ ++ ; /* USART2_CTS_NSS */ ++ }; ++ }; ++ ++ usart3_pins_a: usart3-0 { ++ pins1 { ++ pinmux = , /* USART3_TX */ ++ ; /* USART3_RTS */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <0>; ++ }; ++ pins2 { ++ pinmux = , /* USART3_RX */ ++ ; /* USART3_CTS_NSS */ ++ bias-disable; ++ }; ++ }; ++ ++ usart3_idle_pins_a: usart3-idle-0 { ++ pins1 { ++ pinmux = , /* USART3_TX */ ++ , /* USART3_RTS */ ++ ; /* USART3_CTS_NSS */ ++ }; ++ pins2 { ++ pinmux = ; /* USART3_RX */ ++ bias-disable; ++ }; ++ }; ++ ++ usart3_sleep_pins_a: usart3-sleep-0 { ++ pins { ++ pinmux = , /* USART3_TX */ ++ , /* USART3_RTS */ ++ , /* USART3_CTS_NSS */ ++ ; /* USART3_RX */ ++ }; ++ }; ++ ++ usart3_pins_b: usart3-1 { ++ pins1 { ++ pinmux = , /* USART3_TX */ ++ ; /* USART3_RTS */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <0>; ++ }; ++ pins2 { ++ pinmux = , /* USART3_RX */ ++ ; /* USART3_CTS_NSS */ ++ bias-disable; ++ }; ++ }; ++ ++ usart3_idle_pins_b: usart3-idle-1 { ++ pins1 { ++ pinmux = , /* USART3_TX */ ++ , /* USART3_RTS */ ++ ; /* USART3_CTS_NSS */ ++ }; ++ pins2 { ++ pinmux = ; /* USART3_RX */ ++ bias-disable; ++ }; ++ }; ++ ++ usart3_sleep_pins_b: usart3-sleep-1 { ++ pins { ++ pinmux = , /* USART3_TX */ ++ , /* USART3_RTS */ ++ , /* USART3_CTS_NSS */ ++ ; /* USART3_RX */ ++ }; ++ }; + }; + + pinctrl_z: pin-controller-z@54004000 { +@@ -350,8 +899,7 @@ + clocks = <&rcc GPIOZ>; + st,bank-name = "GPIOZ"; + st,bank-ioport = <11>; +- ngpios = <8>; +- gpio-ranges = <&pinctrl_z 0 400 8>; ++ status = "disabled"; + }; + + i2c4_pins_a: i2c4-0 { +@@ -364,6 +912,13 @@ + }; + }; + ++ i2c4_pins_sleep_a: i2c4-1 { ++ pins { ++ pinmux = , /* I2C4_SCL */ ++ ; /* I2C4_SDA */ ++ }; ++ }; ++ + spi1_pins_a: spi1-0 { + pins1 { + pinmux = , /* SPI1_SCK */ +diff --git a/arch/arm/boot/dts/stm32mp157a-dk1.dts b/arch/arm/boot/dts/stm32mp157a-dk1.dts +new file mode 100644 +index 0000000..866eed7 +--- /dev/null ++++ b/arch/arm/boot/dts/stm32mp157a-dk1.dts +@@ -0,0 +1,412 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) ++/* ++ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved ++ * Author: Alexandre Torgue . ++ */ ++ ++/dts-v1/; ++ ++#include "stm32mp157c.dtsi" ++#include "stm32mp157c-m4-srm.dtsi" ++#include "stm32mp157cac-pinctrl.dtsi" ++#include ++#include ++#include ++ ++/ { ++ model = "STMicroelectronics STM32MP157A-DK1 Discovery Board"; ++ compatible = "st,stm32mp157a-dk1", "st,stm32mp157"; ++ ++ chosen { ++ stdout-path = "serial0:115200n8"; ++ }; ++ ++ memory@c0000000 { ++ reg = <0xc0000000 0x20000000>; ++ }; ++ ++ reserved-memory { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ ++ ipc_share: sram_rproc@10040000 { ++ compatible = "shared-dma-pool"; ++ reg = <0x10040000 0x10000>; ++ no-map; ++ }; ++ }; ++ ++ aliases { ++ serial0 = &uart4; ++ }; ++ ++ iio-hwmon { ++ compatible = "iio-hwmon"; ++ io-channels = <&adc_temp>; ++ }; ++ ++ sram: sram@10050000 { ++ compatible = "mmio-sram"; ++ reg = <0x10050000 0x10000>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0 0x10050000 0x10000>; ++ ++ dma_pool: dma_pool@0 { ++ reg = <0x0 0x10000>; ++ pool; ++ }; ++ }; ++ ++ led { ++ compatible = "gpio-leds"; ++ blue { ++ label = "heartbeat"; ++ gpios = <&gpiod 11 GPIO_ACTIVE_HIGH>; ++ linux,default-trigger = "heartbeat"; ++ default-state = "off"; ++ }; ++ }; ++}; ++ ++&adc { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&adc12_usb_pwr_pins_a>; ++ vref-supply = <&vrefbuf>; ++ status = "okay"; ++ adc1: adc@0 { ++ /* ++ * Type-C USB_PWR_CC1 & USB_PWR_CC2 on in18 & in19. ++ * Use at least 5 * RC time, e.g. 5 * (Rp + Rd) * C: ++ * 5 * (56 + 47kOhms) * 5pF => 2.5us. ++ * Use arbitrary margin here (e.g. 5µs). ++ */ ++ st,min-sample-time-nsecs = <5000>; ++ /* ANA0, ANA1, USB Type-C CC1 & CC2 */ ++ st,adc-channels = <0 1 18 19>; ++ status = "okay"; ++ }; ++ adc2: adc@100 { ++ /* ANA0, ANA1, temp sensor, USB Type-C CC1 & CC2 */ ++ st,adc-channels = <0 1 12 18 19>; ++ /* temperature sensor min sample time */ ++ st,min-sample-time-nsecs = <10000>; ++ status = "okay"; ++ }; ++ adc_temp: temp { ++ status = "okay"; ++ }; ++}; ++ ++&dma1 { ++ sram = <&dma_pool>; ++}; ++ ++&dma2 { ++ sram = <&dma_pool>; ++}; ++ ++&i2c1 { ++ pinctrl-names = "default", "sleep"; ++ pinctrl-0 = <&i2c1_pins_a>; ++ pinctrl-1 = <&i2c1_pins_sleep_a>; ++ i2c-scl-rising-time-ns = <100>; ++ i2c-scl-falling-time-ns = <7>; ++ status = "okay"; ++ /delete-property/dmas; ++ /delete-property/dma-names; ++}; ++ ++&i2c4 { ++ pinctrl-names = "default", "sleep"; ++ pinctrl-0 = <&i2c4_pins_a>; ++ pinctrl-1 = <&i2c4_pins_sleep_a>; ++ i2c-scl-rising-time-ns = <185>; ++ i2c-scl-falling-time-ns = <20>; ++ status = "okay"; ++ /delete-property/dmas; ++ /delete-property/dma-names; ++ ++ pmic: stpmic@33 { ++ compatible = "st,stpmic1"; ++ reg = <0x33>; ++ interrupts-extended = <&pwr 0 IRQ_TYPE_EDGE_FALLING 1>, ++ <&exti 55 1>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ status = "okay"; ++ ++ st,main-control-register = <0x04>; ++ st,vin-control-register = <0xc0>; ++ st,usb-control-register = <0x30>; ++ ++ regulators { ++ compatible = "st,stpmic1-regulators"; ++ ++ ldo1-supply = <&v3v3>; ++ ldo3-supply = <&vdd_ddr>; ++ ldo6-supply = <&v3v3>; ++ pwr_sw1-supply = <&bst_out>; ++ pwr_sw2-supply = <&bst_out>; ++ ++ vddcore: buck1 { ++ regulator-name = "vddcore"; ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <1350000>; ++ regulator-always-on; ++ regulator-initial-mode = <0>; ++ regulator-over-current-protection; ++ }; ++ ++ vdd_ddr: buck2 { ++ regulator-name = "vdd_ddr"; ++ regulator-min-microvolt = <1350000>; ++ regulator-max-microvolt = <1350000>; ++ regulator-always-on; ++ regulator-initial-mode = <0>; ++ regulator-over-current-protection; ++ }; ++ ++ vdd: buck3 { ++ regulator-name = "vdd"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ st,mask-reset; ++ regulator-initial-mode = <2>; ++ regulator-over-current-protection; ++ }; ++ ++ v3v3: buck4 { ++ regulator-name = "v3v3"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ regulator-over-current-protection; ++ regulator-initial-mode = <2>; ++ }; ++ ++ v1v8_audio: ldo1 { ++ regulator-name = "v1v8_audio"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ regulator-always-on; ++ interrupts = ; ++ ++ }; ++ ++ v3v3_hdmi: ldo2 { ++ regulator-name = "v3v3_hdmi"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ interrupts = ; ++ ++ }; ++ ++ vtt_ddr: ldo3 { ++ regulator-name = "vtt_ddr"; ++ regulator-min-microvolt = <500000>; ++ regulator-max-microvolt = <750000>; ++ regulator-always-on; ++ regulator-over-current-protection; ++ }; ++ ++ vdd_usb: ldo4 { ++ regulator-name = "vdd_usb"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ interrupts = ; ++ }; ++ ++ vdda: ldo5 { ++ regulator-name = "vdda"; ++ regulator-min-microvolt = <2900000>; ++ regulator-max-microvolt = <2900000>; ++ interrupts = ; ++ regulator-boot-on; ++ }; ++ ++ v1v2_hdmi: ldo6 { ++ regulator-name = "v1v2_hdmi"; ++ regulator-min-microvolt = <1200000>; ++ regulator-max-microvolt = <1200000>; ++ regulator-always-on; ++ interrupts = ; ++ ++ }; ++ ++ vref_ddr: vref_ddr { ++ regulator-name = "vref_ddr"; ++ regulator-always-on; ++ regulator-over-current-protection; ++ }; ++ ++ bst_out: boost { ++ regulator-name = "bst_out"; ++ interrupts = ; ++ }; ++ ++ vbus_otg: pwr_sw1 { ++ regulator-name = "vbus_otg"; ++ interrupts = ; ++ regulator-active-discharge; ++ }; ++ ++ vbus_sw: pwr_sw2 { ++ regulator-name = "vbus_sw"; ++ interrupts = ; ++ regulator-active-discharge; ++ }; ++ }; ++ ++ onkey { ++ compatible = "st,stpmic1-onkey"; ++ interrupts = , ; ++ interrupt-names = "onkey-falling", "onkey-rising"; ++ status = "okay"; ++ }; ++ ++ watchdog { ++ compatible = "st,stpmic1-wdt"; ++ }; ++ }; ++}; ++ ++&ipcc { ++ status = "okay"; ++}; ++ ++&iwdg2 { ++ timeout-sec = <32>; ++ status = "okay"; ++}; ++ ++&m4_rproc { ++ memory-region = <&ipc_share>; ++ mboxes = <&ipcc 0>, <&ipcc 1>, <&ipcc 2>; ++ mbox-names = "vq0", "vq1", "init_shdn"; ++ interrupt-parent = <&exti>; ++ interrupts = <68 1>; ++ interrupt-names = "wdg"; ++ recovery; ++ status = "okay"; ++}; ++ ++&rng1 { ++ status = "okay"; ++}; ++ ++&rtc { ++ status = "okay"; ++}; ++ ++&sdmmc1 { ++ pinctrl-names = "default", "opendrain", "sleep"; ++ pinctrl-0 = <&sdmmc1_b4_pins_a>; ++ pinctrl-1 = <&sdmmc1_b4_od_pins_a>; ++ pinctrl-2 = <&sdmmc1_b4_sleep_pins_a>; ++ broken-cd; ++ st,negedge; ++ bus-width = <4>; ++ vmmc-supply = <&v3v3>; ++ status = "okay"; ++}; ++ ++&timers1 { ++ /* spare dmas for other usage */ ++ /delete-property/dmas; ++ /delete-property/dma-names; ++ pwm { ++ pinctrl-0 = <&pwm1_pins_a>; ++ pinctrl-1 = <&pwm1_sleep_pins_a>; ++ pinctrl-names = "default", "sleep"; ++ status = "okay"; ++ }; ++ timer@0 { ++ status = "okay"; ++ }; ++}; ++ ++&timers3 { ++ /delete-property/dmas; ++ /delete-property/dma-names; ++ pwm { ++ pinctrl-0 = <&pwm3_pins_a>; ++ pinctrl-1 = <&pwm3_sleep_pins_a>; ++ pinctrl-names = "default", "sleep"; ++ status = "okay"; ++ }; ++ timer@2 { ++ status = "okay"; ++ }; ++}; ++ ++&timers4 { ++ /delete-property/dmas; ++ /delete-property/dma-names; ++ pwm { ++ pinctrl-0 = <&pwm4_pins_a &pwm4_pins_b>; ++ pinctrl-1 = <&pwm4_sleep_pins_a &pwm4_sleep_pins_b>; ++ pinctrl-names = "default", "sleep"; ++ status = "okay"; ++ }; ++ timer@3 { ++ status = "okay"; ++ }; ++}; ++ ++&timers5 { ++ /delete-property/dmas; ++ /delete-property/dma-names; ++ pwm { ++ pinctrl-0 = <&pwm5_pins_a>; ++ pinctrl-1 = <&pwm5_sleep_pins_a>; ++ pinctrl-names = "default", "sleep"; ++ status = "okay"; ++ }; ++ timer@4 { ++ status = "okay"; ++ }; ++}; ++ ++&timers6 { ++ status = "okay"; ++ /* spare dmas for other usage */ ++ /delete-property/dmas; ++ /delete-property/dma-names; ++ timer@5 { ++ status = "okay"; ++ }; ++}; ++ ++&timers12 { ++ /delete-property/dmas; ++ /delete-property/dma-names; ++ pwm { ++ pinctrl-0 = <&pwm12_pins_a>; ++ pinctrl-1 = <&pwm12_sleep_pins_a>; ++ pinctrl-names = "default", "sleep"; ++ status = "okay"; ++ }; ++ timer@11 { ++ status = "okay"; ++ }; ++}; ++ ++&uart4 { ++ pinctrl-names = "default", "sleep", "idle", "no_console_suspend"; ++ pinctrl-0 = <&uart4_pins_a>; ++ pinctrl-1 = <&uart4_sleep_pins_a>; ++ pinctrl-2 = <&uart4_idle_pins_a>; ++ pinctrl-3 = <&uart4_pins_a>; ++ status = "okay"; ++}; ++ ++&vrefbuf { ++ regulator-min-microvolt = <2500000>; ++ regulator-max-microvolt = <2500000>; ++ vdda-supply = <&vdd>; ++ status = "okay"; ++}; +diff --git a/arch/arm/boot/dts/stm32mp157c-dk2.dts b/arch/arm/boot/dts/stm32mp157c-dk2.dts +new file mode 100644 +index 0000000..4175b65 +--- /dev/null ++++ b/arch/arm/boot/dts/stm32mp157c-dk2.dts +@@ -0,0 +1,22 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) ++/* ++ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved ++ * Author: Alexandre Torgue . ++ */ ++ ++/dts-v1/; ++ ++#include "stm32mp157a-dk1.dts" ++#include ++ ++/ { ++ model = "STMicroelectronics STM32MP157C-DK2 Discovery Board"; ++ compatible = "st,stm32mp157c-dk2", "st,stm32mp157"; ++}; ++ ++&rtc { ++ st,lsco = ; ++ pinctrl-0 = <&rtc_out2_rmp_pins_a>; ++ pinctrl-names = "default"; ++}; ++ +diff --git a/arch/arm/boot/dts/stm32mp157c-ed1.dts b/arch/arm/boot/dts/stm32mp157c-ed1.dts +index f77bea4..1b074e9 100644 +--- a/arch/arm/boot/dts/stm32mp157c-ed1.dts ++++ b/arch/arm/boot/dts/stm32mp157c-ed1.dts +@@ -6,7 +6,10 @@ + /dts-v1/; + + #include "stm32mp157c.dtsi" +-#include "stm32mp157-pinctrl.dtsi" ++#include "stm32mp157c-m4-srm.dtsi" ++#include "stm32mp157caa-pinctrl.dtsi" ++#include ++#include + + / { + model = "STMicroelectronics STM32MP157C eval daughter"; +@@ -20,41 +23,251 @@ + reg = <0xC0000000 0x40000000>; + }; + ++ reserved-memory { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges; ++ ++ ipc_share: sram_rproc@10040000 { ++ compatible = "shared-dma-pool"; ++ reg = <0x10040000 0x10000>; ++ no-map; ++ }; ++ }; ++ + aliases { + serial0 = &uart4; + }; + +- reg11: reg11 { +- compatible = "regulator-fixed"; +- regulator-name = "reg11"; +- regulator-min-microvolt = <1100000>; +- regulator-max-microvolt = <1100000>; +- regulator-always-on; ++ iio-hwmon { ++ compatible = "iio-hwmon"; ++ io-channels = <&adc_temp>; + }; + +- reg18: reg18 { +- compatible = "regulator-fixed"; +- regulator-name = "reg18"; +- regulator-min-microvolt = <1800000>; +- regulator-max-microvolt = <1800000>; +- regulator-always-on; ++ sram: sram@10050000 { ++ compatible = "mmio-sram"; ++ reg = <0x10050000 0x10000>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0 0x10050000 0x10000>; ++ ++ dma_pool: dma_pool@0 { ++ reg = <0x0 0x10000>; ++ pool; ++ }; + }; + +- vdd_usb: vdd-usb { +- compatible = "regulator-fixed"; +- regulator-name = "vdd_usb"; +- regulator-min-microvolt = <3300000>; +- regulator-max-microvolt = <3300000>; +- regulator-always-on; ++ led { ++ compatible = "gpio-leds"; ++ blue { ++ label = "heartbeat"; ++ gpios = <&gpiod 9 GPIO_ACTIVE_HIGH>; ++ linux,default-trigger = "heartbeat"; ++ default-state = "off"; ++ }; + }; + }; + +-&i2c4 { ++&adc { ++ /* ANA0, ANA1 are dedicated pins and don't need pinctrl: only in6. */ ++ vref-supply = <&vdda>; ++ status = "okay"; ++ adc1: adc@0 { ++ st,adc-channels = <0 1>; ++ /* 16.5 ck_cycles sampling time */ ++ st,min-sample-time-nsecs = <400>; ++ status = "okay"; ++ }; ++ jadc1: jadc@0 { ++ st,adc-channels = <0 1>; ++ /* 16.5 ck_cycles sampling time */ ++ st,min-sample-time-nsecs = <400>; ++ status = "okay"; ++ }; ++ /* temperature sensor on adc2 */ ++ adc2: adc@100 { ++ status = "okay"; ++ }; ++ adc_temp: temp { ++ status = "okay"; ++ }; ++}; ++ ++&dac { + pinctrl-names = "default"; ++ pinctrl-0 = <&dac_ch1_pins_a &dac_ch2_pins_a>; ++ vref-supply = <&vdda>; ++ status = "okay"; ++ dac1: dac@1 { ++ status = "okay"; ++ }; ++ dac2: dac@2 { ++ status = "okay"; ++ }; ++}; ++ ++&dma1 { ++ sram = <&dma_pool>; ++}; ++ ++&dma2 { ++ sram = <&dma_pool>; ++}; ++ ++&i2c4 { ++ pinctrl-names = "default", "sleep"; + pinctrl-0 = <&i2c4_pins_a>; ++ pinctrl-1 = <&i2c4_pins_sleep_a>; + i2c-scl-rising-time-ns = <185>; + i2c-scl-falling-time-ns = <20>; + status = "okay"; ++ /delete-property/dmas; ++ /delete-property/dma-names; ++ ++ pmic: stpmic@33 { ++ compatible = "st,stpmic1"; ++ reg = <0x33>; ++ interrupts-extended = <&pwr 0 IRQ_TYPE_EDGE_FALLING 1>, ++ <&exti 55 1>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ status = "okay"; ++ ++ st,main-control-register = <0x04>; ++ st,vin-control-register = <0xc0>; ++ st,usb-control-register = <0x30>; ++ ++ regulators { ++ compatible = "st,stpmic1-regulators"; ++ ++ ldo1-supply = <&v3v3>; ++ ldo2-supply = <&v3v3>; ++ ldo3-supply = <&vdd_ddr>; ++ ldo5-supply = <&v3v3>; ++ ldo6-supply = <&v3v3>; ++ pwr_sw1-supply = <&bst_out>; ++ pwr_sw2-supply = <&bst_out>; ++ ++ vddcore: buck1 { ++ regulator-name = "vddcore"; ++ regulator-min-microvolt = <800000>; ++ regulator-max-microvolt = <1350000>; ++ regulator-always-on; ++ regulator-initial-mode = <0>; ++ regulator-over-current-protection; ++ }; ++ ++ vdd_ddr: buck2 { ++ regulator-name = "vdd_ddr"; ++ regulator-min-microvolt = <1350000>; ++ regulator-max-microvolt = <1350000>; ++ regulator-always-on; ++ regulator-initial-mode = <0>; ++ regulator-over-current-protection; ++ }; ++ ++ vdd: buck3 { ++ regulator-name = "vdd"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ st,mask-reset; ++ regulator-initial-mode = <2>; ++ regulator-over-current-protection; ++ }; ++ ++ v3v3: buck4 { ++ regulator-name = "v3v3"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ regulator-over-current-protection; ++ regulator-initial-mode = <2>; ++ }; ++ ++ vdda: ldo1 { ++ regulator-name = "vdda"; ++ regulator-min-microvolt = <2900000>; ++ regulator-max-microvolt = <2900000>; ++ interrupts = ; ++ }; ++ ++ v2v8: ldo2 { ++ regulator-name = "v2v8"; ++ regulator-min-microvolt = <2800000>; ++ regulator-max-microvolt = <2800000>; ++ interrupts = ; ++ }; ++ ++ vtt_ddr: ldo3 { ++ regulator-name = "vtt_ddr"; ++ regulator-min-microvolt = <500000>; ++ regulator-max-microvolt = <750000>; ++ regulator-always-on; ++ regulator-over-current-protection; ++ }; ++ ++ vdd_usb: ldo4 { ++ regulator-name = "vdd_usb"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ interrupts = ; ++ }; ++ ++ vdd_sd: ldo5 { ++ regulator-name = "vdd_sd"; ++ regulator-min-microvolt = <2900000>; ++ regulator-max-microvolt = <2900000>; ++ interrupts = ; ++ regulator-boot-on; ++ }; ++ ++ v1v8: ldo6 { ++ regulator-name = "v1v8"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ interrupts = ; ++ }; ++ ++ vref_ddr: vref_ddr { ++ regulator-name = "vref_ddr"; ++ regulator-always-on; ++ regulator-over-current-protection; ++ }; ++ ++ bst_out: boost { ++ regulator-name = "bst_out"; ++ interrupts = ; ++ }; ++ ++ vbus_otg: pwr_sw1 { ++ regulator-name = "vbus_otg"; ++ interrupts = ; ++ regulator-active-discharge; ++ }; ++ ++ vbus_sw: pwr_sw2 { ++ regulator-name = "vbus_sw"; ++ interrupts = ; ++ regulator-active-discharge; ++ }; ++ }; ++ ++ onkey { ++ compatible = "st,stpmic1-onkey"; ++ interrupts = , ; ++ interrupt-names = "onkey-falling", "onkey-rising"; ++ status = "okay"; ++ }; ++ ++ watchdog { ++ compatible = "st,stpmic1-wdt"; ++ }; ++ }; ++}; ++ ++&ipcc { ++ status = "okay"; + }; + + &iwdg2 { +@@ -62,6 +275,17 @@ + status = "okay"; + }; + ++&m4_rproc { ++ memory-region = <&ipc_share>; ++ mboxes = <&ipcc 0>, <&ipcc 1>, <&ipcc 2>; ++ mbox-names = "vq0", "vq1", "init_shdn"; ++ interrupt-parent = <&exti>; ++ interrupts = <68 1>; ++ interrupt-names = "wdg"; ++ recovery; ++ status = "okay"; ++}; ++ + &rng1 { + status = "okay"; + }; +@@ -70,16 +294,51 @@ + status = "okay"; + }; + ++&sdmmc1 { ++ pinctrl-names = "default", "opendrain", "sleep"; ++ pinctrl-0 = <&sdmmc1_b4_pins_a &sdmmc1_dir_pins_a>; ++ pinctrl-1 = <&sdmmc1_b4_od_pins_a &sdmmc1_dir_pins_a>; ++ pinctrl-2 = <&sdmmc1_b4_sleep_pins_a &sdmmc1_dir_sleep_pins_a>; ++ broken-cd; ++ st,sig-dir; ++ st,neg-edge; ++ st,use-ckin; ++ bus-width = <4>; ++ vmmc-supply = <&vdd_sd>; ++ status = "okay"; ++}; ++ ++&sdmmc2 { ++ pinctrl-names = "default", "opendrain", "sleep"; ++ pinctrl-0 = <&sdmmc2_b4_pins_a &sdmmc2_d47_pins_a>; ++ pinctrl-1 = <&sdmmc2_b4_od_pins_a &sdmmc2_d47_pins_a>; ++ pinctrl-2 = <&sdmmc2_b4_sleep_pins_a &sdmmc2_d47_sleep_pins_a>; ++ non-removable; ++ no-sd; ++ no-sdio; ++ st,negedge; ++ bus-width = <8>; ++ vmmc-supply = <&v3v3>; ++ vqmmc-supply = <&v3v3>; ++ status = "okay"; ++}; ++ + &timers6 { + status = "okay"; ++ /* spare dmas for other usage */ ++ /delete-property/dmas; ++ /delete-property/dma-names; + timer@5 { + status = "okay"; + }; + }; + + &uart4 { +- pinctrl-names = "default"; ++ pinctrl-names = "default", "sleep", "idle", "no_console_suspend"; + pinctrl-0 = <&uart4_pins_a>; ++ pinctrl-1 = <&uart4_sleep_pins_a>; ++ pinctrl-2 = <&uart4_idle_pins_a>; ++ pinctrl-3 = <&uart4_pins_a>; + status = "okay"; + }; + +diff --git a/arch/arm/boot/dts/stm32mp157c-ev1.dts b/arch/arm/boot/dts/stm32mp157c-ev1.dts +index 372bc2e..fe59c6d 100644 +--- a/arch/arm/boot/dts/stm32mp157c-ev1.dts ++++ b/arch/arm/boot/dts/stm32mp157c-ev1.dts +@@ -6,6 +6,7 @@ + /dts-v1/; + + #include "stm32mp157c-ed1.dts" ++#include + + / { + model = "STMicroelectronics STM32MP157C eval daughter on eval mother"; +@@ -16,9 +17,46 @@ + }; + + aliases { +- serial0 = &uart4; ++ serial1 = &usart3; + ethernet0 = ðernet0; + }; ++ ++ joystick { ++ compatible = "gpio-keys"; ++ #size-cells = <0>; ++ pinctrl-0 = <&joystick_pins>; ++ pinctrl-names = "default"; ++ button-0 { ++ label = "JoySel"; ++ linux,code = ; ++ interrupt-parent = <&stmfx_pinctrl>; ++ interrupts = <0 IRQ_TYPE_EDGE_RISING>; ++ }; ++ button-1 { ++ label = "JoyDown"; ++ linux,code = ; ++ interrupt-parent = <&stmfx_pinctrl>; ++ interrupts = <1 IRQ_TYPE_EDGE_RISING>; ++ }; ++ button-2 { ++ label = "JoyLeft"; ++ linux,code = ; ++ interrupt-parent = <&stmfx_pinctrl>; ++ interrupts = <2 IRQ_TYPE_EDGE_RISING>; ++ }; ++ button-3 { ++ label = "JoyRight"; ++ linux,code = ; ++ interrupt-parent = <&stmfx_pinctrl>; ++ interrupts = <3 IRQ_TYPE_EDGE_RISING>; ++ }; ++ button-4 { ++ label = "JoyUp"; ++ linux,code = ; ++ interrupt-parent = <&stmfx_pinctrl>; ++ interrupts = <4 IRQ_TYPE_EDGE_RISING>; ++ }; ++ }; + }; + + ðernet0 { +@@ -46,37 +84,85 @@ + status = "okay"; + }; + ++&fmc { ++ pinctrl-names = "default", "sleep"; ++ pinctrl-0 = <&fmc_pins_a>; ++ pinctrl-1 = <&fmc_sleep_pins_a>; ++ status = "okay"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ nand: nand@0 { ++ reg = <0>; ++ nand-on-flash-bbt; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ }; ++}; ++ + &i2c2 { +- pinctrl-names = "default"; ++ pinctrl-names = "default", "sleep"; + pinctrl-0 = <&i2c2_pins_a>; ++ pinctrl-1 = <&i2c2_pins_sleep_a>; + i2c-scl-rising-time-ns = <185>; + i2c-scl-falling-time-ns = <20>; + status = "okay"; ++ /delete-property/dmas; ++ /delete-property/dma-names; ++ ++ stmfx: stmfx@42 { ++ compatible = "st,stmfx-0300"; ++ reg = <0x42>; ++ interrupts = <8 IRQ_TYPE_EDGE_RISING>; ++ interrupt-parent = <&gpioi>; ++ vdd-supply = <&v3v3>; ++ ++ stmfx_pinctrl: stmfx-pin-controller { ++ compatible = "st,stmfx-0300-pinctrl"; ++ gpio-controller; ++ #gpio-cells = <2>; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ gpio-ranges = <&stmfx_pinctrl 0 0 24>; ++ ++ joystick_pins: joystick { ++ pins = "gpio0", "gpio1", "gpio2", "gpio3", "gpio4"; ++ drive-push-pull; ++ bias-pull-down; ++ }; ++ }; ++ }; + }; + + &i2c5 { +- pinctrl-names = "default"; ++ pinctrl-names = "default", "sleep"; + pinctrl-0 = <&i2c5_pins_a>; ++ pinctrl-1 = <&i2c5_pins_sleep_a>; + i2c-scl-rising-time-ns = <185>; + i2c-scl-falling-time-ns = <20>; + status = "okay"; ++ /delete-property/dmas; ++ /delete-property/dma-names; + }; + + &m_can1 { +- pinctrl-names = "default"; ++ pinctrl-names = "default", "sleep"; + pinctrl-0 = <&m_can1_pins_a>; ++ pinctrl-1 = <&m_can1_sleep_pins_a>; + status = "okay"; + }; + + &qspi { +- pinctrl-names = "default"; ++ pinctrl-names = "default", "sleep"; + pinctrl-0 = <&qspi_clk_pins_a &qspi_bk1_pins_a &qspi_bk2_pins_a>; ++ pinctrl-1 = <&qspi_clk_sleep_pins_a &qspi_bk1_sleep_pins_a &qspi_bk2_sleep_pins_a>; + reg = <0x58003000 0x1000>, <0x70000000 0x4000000>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + flash0: mx66l51235l@0 { ++ compatible = "jedec,spi-nor"; + reg = <0>; + spi-rx-bus-width = <4>; + spi-max-frequency = <108000000>; +@@ -85,6 +171,7 @@ + }; + + flash1: mx66l51235l@1 { ++ compatible = "jedec,spi-nor"; + reg = <1>; + spi-rx-bus-width = <4>; + spi-max-frequency = <108000000>; +@@ -101,9 +188,13 @@ + + &timers2 { + status = "disabled"; ++ /* spare dmas for other usage (un-delete to enable pwm capture) */ ++ /delete-property/dmas; ++ /delete-property/dma-names; + pwm { + pinctrl-0 = <&pwm2_pins_a>; +- pinctrl-names = "default"; ++ pinctrl-1 = <&pwm2_sleep_pins_a>; ++ pinctrl-names = "default", "sleep"; + status = "okay"; + }; + timer@1 { +@@ -113,9 +204,12 @@ + + &timers8 { + status = "disabled"; ++ /delete-property/dmas; ++ /delete-property/dma-names; + pwm { + pinctrl-0 = <&pwm8_pins_a>; +- pinctrl-names = "default"; ++ pinctrl-1 = <&pwm8_sleep_pins_a>; ++ pinctrl-names = "default", "sleep"; + status = "okay"; + }; + timer@7 { +@@ -125,9 +219,12 @@ + + &timers12 { + status = "disabled"; ++ /delete-property/dmas; ++ /delete-property/dma-names; + pwm { + pinctrl-0 = <&pwm12_pins_a>; +- pinctrl-names = "default"; ++ pinctrl-1 = <&pwm12_sleep_pins_a>; ++ pinctrl-names = "default", "sleep"; + status = "okay"; + }; + timer@11 { +@@ -135,6 +232,14 @@ + }; + }; + ++&usart3 { ++ pinctrl-names = "default", "sleep", "idle"; ++ pinctrl-0 = <&usart3_pins_a>; ++ pinctrl-1 = <&usart3_sleep_pins_a>; ++ pinctrl-2 = <&usart3_idle_pins_a>; ++ status = "disabled"; ++}; ++ + &usbh_ehci { + phys = <&usbphyc_port0>; + phy-names = "usb"; +diff --git a/arch/arm/boot/dts/stm32mp157c-m4-srm.dtsi b/arch/arm/boot/dts/stm32mp157c-m4-srm.dtsi +new file mode 100644 +index 0000000..a1d132d0 +--- /dev/null ++++ b/arch/arm/boot/dts/stm32mp157c-m4-srm.dtsi +@@ -0,0 +1,436 @@ ++&m4_rproc { ++ m4_system_resources { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ m4_timers2: timer@40000000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x40000000 0x400>; ++ clocks = <&rcc TIM2_K>; ++ clock-names = "int"; ++ status = "disabled"; ++ }; ++ m4_timers3: timer@40001000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x40001000 0x400>; ++ clocks = <&rcc TIM3_K>; ++ clock-names = "int"; ++ status = "disabled"; ++ }; ++ m4_timers4: timer@40002000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x40002000 0x400>; ++ clocks = <&rcc TIM4_K>; ++ clock-names = "int"; ++ status = "disabled"; ++ }; ++ m4_timers5: timer@40003000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x40003000 0x400>; ++ clocks = <&rcc TIM5_K>; ++ clock-names = "int"; ++ status = "disabled"; ++ }; ++ m4_timers6: timer@40004000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x40004000 0x400>; ++ clocks = <&rcc TIM6_K>; ++ clock-names = "int"; ++ status = "disabled"; ++ }; ++ m4_timers7: timer@40005000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x40005000 0x400>; ++ clocks = <&rcc TIM7_K>; ++ clock-names = "int"; ++ status = "disabled"; ++ }; ++ m4_timers12: timer@40006000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x40006000 0x400>; ++ clocks = <&rcc TIM12_K>; ++ clock-names = "int"; ++ status = "disabled"; ++ }; ++ m4_timers13: timer@40007000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x40007000 0x400>; ++ clocks = <&rcc TIM13_K>; ++ clock-names = "int"; ++ status = "disabled"; ++ }; ++ m4_timers14: timer@40008000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x40008000 0x400>; ++ clocks = <&rcc TIM14_K>; ++ clock-names = "int"; ++ status = "disabled"; ++ }; ++ m4_lptimer1: timer@40009000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x40009000 0x400>; ++ clocks = <&rcc LPTIM1_K>; ++ clock-names = "mux"; ++ status = "disabled"; ++ }; ++ m4_spi2: spi@4000b000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x4000b000 0x400>; ++ clocks = <&rcc SPI2_K>; ++ status = "disabled"; ++ }; ++ m4_i2s2: audio-controller@4000b000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x4000b000 0x400>; ++ status = "disabled"; ++ }; ++ m4_spi3: spi@4000c000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x4000c000 0x400>; ++ clocks = <&rcc SPI3_K>; ++ status = "disabled"; ++ }; ++ m4_i2s3: audio-controller@4000c000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x4000c000 0x400>; ++ status = "disabled"; ++ }; ++ m4_spdifrx: audio-controller@4000d000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x4000d000 0x400>; ++ clocks = <&rcc SPDIF_K>; ++ clock-names = "kclk"; ++ status = "disabled"; ++ }; ++ m4_usart2: serial@4000e000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x4000e000 0x400>; ++ interrupt-parent = <&exti>; ++ interrupts = <27 1>; ++ clocks = <&rcc USART2_K>; ++ status = "disabled"; ++ }; ++ m4_usart3: serial@4000f000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x4000f000 0x400>; ++ interrupt-parent = <&exti>; ++ interrupts = <28 1>; ++ clocks = <&rcc USART3_K>; ++ status = "disabled"; ++ }; ++ m4_uart4: serial@40010000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x40010000 0x400>; ++ interrupt-parent = <&exti>; ++ interrupts = <30 1>; ++ clocks = <&rcc UART4_K>; ++ status = "disabled"; ++ }; ++ m4_uart5: serial@40011000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x40011000 0x400>; ++ interrupt-parent = <&exti>; ++ interrupts = <31 1>; ++ clocks = <&rcc UART5_K>; ++ status = "disabled"; ++ }; ++ m4_i2c1: i2c@40012000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x40012000 0x400>; ++ interrupt-parent = <&exti>; ++ interrupts = <21 1>; ++ clocks = <&rcc I2C1_K>; ++ status = "disabled"; ++ }; ++ m4_i2c2: i2c@40013000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x40013000 0x400>; ++ interrupt-parent = <&exti>; ++ interrupts = <22 1>; ++ clocks = <&rcc I2C2_K>; ++ status = "disabled"; ++ }; ++ m4_i2c3: i2c@40014000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x40014000 0x400>; ++ interrupt-parent = <&exti>; ++ interrupts = <23 1>; ++ clocks = <&rcc I2C3_K>; ++ status = "disabled"; ++ }; ++ m4_i2c5: i2c@40015000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x40015000 0x400>; ++ interrupt-parent = <&exti>; ++ interrupts = <25 1>; ++ clocks = <&rcc I2C5_K>; ++ status = "disabled"; ++ }; ++ m4_cec: cec@40016000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x40016000 0x400>; ++ interrupt-parent = <&exti>; ++ interrupts = <69 1>; ++ clocks = <&rcc CEC_K>, <&rcc CK_LSE>; ++ clock-names = "cec", "hdmi-cec"; ++ status = "disabled"; ++ }; ++ m4_dac: dac@40017000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x40017000 0x400>; ++ clocks = <&rcc DAC12>; ++ clock-names = "pclk"; ++ status = "disabled"; ++ }; ++ m4_uart7: serial@40018000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x40018000 0x400>; ++ interrupt-parent = <&exti>; ++ interrupts = <32 1>; ++ clocks = <&rcc UART7_K>; ++ status = "disabled"; ++ }; ++ m4_uart8: serial@40019000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x40019000 0x400>; ++ interrupt-parent = <&exti>; ++ interrupts = <33 1>; ++ clocks = <&rcc UART8_K>; ++ status = "disabled"; ++ }; ++ m4_timers1: timer@44000000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x44000000 0x400>; ++ clocks = <&rcc TIM1_K>; ++ clock-names = "int"; ++ status = "disabled"; ++ }; ++ m4_timers8: timer@44001000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x44001000 0x400>; ++ clocks = <&rcc TIM8_K>; ++ clock-names = "int"; ++ status = "disabled"; ++ }; ++ m4_usart6: serial@44003000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x44003000 0x400>; ++ interrupt-parent = <&exti>; ++ interrupts = <29 1>; ++ clocks = <&rcc USART6_K>; ++ status = "disabled"; ++ }; ++ m4_spi1: spi@44004000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x44004000 0x400>; ++ clocks = <&rcc SPI1_K>; ++ status = "disabled"; ++ }; ++ m4_i2s1: audio-controller@44004000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x44004000 0x400>; ++ status = "disabled"; ++ }; ++ m4_spi4: spi@44005000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x44005000 0x400>; ++ clocks = <&rcc SPI4_K>; ++ status = "disabled"; ++ }; ++ m4_timers15: timer@44006000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x44006000 0x400>; ++ clocks = <&rcc TIM15_K>; ++ clock-names = "int"; ++ status = "disabled"; ++ }; ++ m4_timers16: timer@44007000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x44007000 0x400>; ++ clocks = <&rcc TIM16_K>; ++ clock-names = "int"; ++ status = "disabled"; ++ }; ++ m4_timers17: timer@44008000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x44008000 0x400>; ++ clocks = <&rcc TIM17_K>; ++ clock-names = "int"; ++ status = "disabled"; ++ }; ++ m4_spi5: spi@44009000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x44009000 0x400>; ++ clocks = <&rcc SPI5_K>; ++ status = "disabled"; ++ }; ++ m4_sai1: sai@4400a000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x4400a000 0x4>; ++ clocks = <&rcc SAI1_K>; ++ clock-names = "sai_ck"; ++ status = "disabled"; ++ }; ++ m4_sai2: sai@4400b000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x4400b000 0x4>; ++ clocks = <&rcc SAI2_K>; ++ clock-names = "sai_ck"; ++ status = "disabled"; ++ }; ++ m4_sai3: sai@4400c000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x4400c000 0x4>; ++ clocks = <&rcc SAI3_K>; ++ clock-names = "sai_ck"; ++ status = "disabled"; ++ }; ++ m4_dfsdm: dfsdm@4400d000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x4400d000 0x800>; ++ clocks = <&rcc DFSDM_K>; ++ clock-names = "dfsdm"; ++ status = "disabled"; ++ }; ++ m4_m_can1: can@4400e000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x4400e000 0x400>, <0x44011000 0x2800>; ++ clocks = <&rcc CK_HSE>, <&rcc FDCAN_K>; ++ clock-names = "hclk", "cclk"; ++ status = "disabled"; ++ }; ++ m4_m_can2: can@4400f000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x4400f000 0x400>, <0x44011000 0x2800>; ++ clocks = <&rcc CK_HSE>, <&rcc FDCAN_K>; ++ clock-names = "hclk", "cclk"; ++ status = "disabled"; ++ }; ++ m4_dma1: dma@48000000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x48000000 0x400>; ++ clocks = <&rcc DMA1>; ++ status = "disabled"; ++ }; ++ m4_dma2: dma@48001000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x48001000 0x400>; ++ clocks = <&rcc DMA2>; ++ status = "disabled"; ++ }; ++ m4_dmamux1: dma-router@48002000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x48002000 0x1c>; ++ clocks = <&rcc DMAMUX>; ++ status = "disabled"; ++ }; ++ m4_adc: adc@48003000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x48003000 0x400>; ++ clocks = <&rcc ADC12>, <&rcc ADC12_K>; ++ clock-names = "bus", "adc"; ++ status = "disabled"; ++ }; ++ m4_sdmmc3: sdmmc@48004000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x48004000 0x400>, <0x48005000 0x400>; ++ clocks = <&rcc SDMMC3_K>; ++ status = "disabled"; ++ }; ++ m4_usbotg_hs: usb-otg@49000000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x49000000 0x10000>; ++ clocks = <&rcc USBO_K>; ++ clock-names = "otg"; ++ status = "disabled"; ++ }; ++ m4_hash2: hash@4c002000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x4c002000 0x400>; ++ clocks = <&rcc HASH2>; ++ status = "disabled"; ++ }; ++ m4_rng2: rng@4c003000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x4c003000 0x400>; ++ clocks = <&rcc RNG2_K>; ++ status = "disabled"; ++ }; ++ m4_crc2: crc@4c004000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x4c004000 0x400>; ++ clocks = <&rcc CRC2>; ++ status = "disabled"; ++ }; ++ m4_cryp2: cryp@4c005000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x4c005000 0x400>; ++ clocks = <&rcc CRYP2>; ++ status = "disabled"; ++ }; ++ m4_dcmi: dcmi@4c006000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x4c006000 0x400>; ++ clocks = <&rcc DCMI>; ++ clock-names = "mclk"; ++ status = "disabled"; ++ }; ++ m4_lptimer2: timer@50021000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x50021000 0x400>; ++ clocks = <&rcc LPTIM2_K>; ++ clock-names = "mux"; ++ status = "disabled"; ++ }; ++ m4_lptimer3: timer@50022000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x50022000 0x400>; ++ clocks = <&rcc LPTIM3_K>; ++ clock-names = "mux"; ++ status = "disabled"; ++ }; ++ m4_lptimer4: timer@50023000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x50023000 0x400>; ++ clocks = <&rcc LPTIM4_K>; ++ clock-names = "mux"; ++ status = "disabled"; ++ }; ++ m4_lptimer5: timer@50024000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x50024000 0x400>; ++ clocks = <&rcc LPTIM5_K>; ++ clock-names = "mux"; ++ status = "disabled"; ++ }; ++ m4_sai4: sai@50027000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x50027000 0x4>; ++ clocks = <&rcc SAI4_K>; ++ clock-names = "sai_ck"; ++ status = "disabled"; ++ }; ++ m4_qspi: qspi@58003000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x58003000 0x1000>, <0x70000000 0x10000000>; ++ clocks = <&rcc QSPI_K>; ++ status = "disabled"; ++ }; ++ m4_ethernet0: ethernet@5800a000 { ++ compatible = "rproc-srm-dev"; ++ reg = <0x5800a000 0x2000>; ++ clock-names = "stmmaceth", ++ "mac-clk-tx", ++ "mac-clk-rx", ++ "ethstp", ++ "syscfg-clk"; ++ clocks = <&rcc ETHMAC>, ++ <&rcc ETHTX>, ++ <&rcc ETHRX>, ++ <&rcc ETHSTP>, ++ <&rcc SYSCFG>; ++ status = "disabled"; ++ }; ++ }; ++}; ++ +diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi +index 185541a..b4bae4d 100644 +--- a/arch/arm/boot/dts/stm32mp157c.dtsi ++++ b/arch/arm/boot/dts/stm32mp157c.dtsi +@@ -29,10 +29,8 @@ + }; + + psci { +- compatible = "arm,psci"; ++ compatible = "arm,psci-1.0"; + method = "smc"; +- cpu_off = <0x84000002>; +- cpu_on = <0x84000003>; + }; + + intc: interrupt-controller@a0021000 { +@@ -84,6 +82,26 @@ + }; + }; + ++ pm_domain { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "st,stm32mp157c-pd"; ++ ++ pd_core_ret: core-ret-power-domain@1 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <1>; ++ #power-domain-cells = <0>; ++ label = "CORE-RETENTION"; ++ ++ pd_core: core-power-domain@2 { ++ reg = <2>; ++ #power-domain-cells = <0>; ++ label = "CORE"; ++ }; ++ }; ++ }; ++ + soc { + compatible = "simple-bus"; + #address-cells = <1>; +@@ -98,6 +116,12 @@ + reg = <0x40000000 0x400>; + clocks = <&rcc TIM2_K>; + clock-names = "int"; ++ dmas = <&dmamux1 18 0x400 0x5>, ++ <&dmamux1 19 0x400 0x5>, ++ <&dmamux1 20 0x400 0x5>, ++ <&dmamux1 21 0x400 0x5>, ++ <&dmamux1 22 0x400 0x5>; ++ dma-names = "ch1", "ch2", "ch3", "ch4", "up"; + status = "disabled"; + + pwm { +@@ -119,6 +143,13 @@ + reg = <0x40001000 0x400>; + clocks = <&rcc TIM3_K>; + clock-names = "int"; ++ dmas = <&dmamux1 23 0x400 0x5>, ++ <&dmamux1 24 0x400 0x5>, ++ <&dmamux1 25 0x400 0x5>, ++ <&dmamux1 26 0x400 0x5>, ++ <&dmamux1 27 0x400 0x5>, ++ <&dmamux1 28 0x400 0x5>; ++ dma-names = "ch1", "ch2", "ch3", "ch4", "up", "trig"; + status = "disabled"; + + pwm { +@@ -140,6 +171,11 @@ + reg = <0x40002000 0x400>; + clocks = <&rcc TIM4_K>; + clock-names = "int"; ++ dmas = <&dmamux1 29 0x400 0x5>, ++ <&dmamux1 30 0x400 0x5>, ++ <&dmamux1 31 0x400 0x5>, ++ <&dmamux1 32 0x400 0x5>; ++ dma-names = "ch1", "ch2", "ch3", "ch4"; + status = "disabled"; + + pwm { +@@ -161,6 +197,13 @@ + reg = <0x40003000 0x400>; + clocks = <&rcc TIM5_K>; + clock-names = "int"; ++ dmas = <&dmamux1 55 0x400 0x5>, ++ <&dmamux1 56 0x400 0x5>, ++ <&dmamux1 57 0x400 0x5>, ++ <&dmamux1 58 0x400 0x5>, ++ <&dmamux1 59 0x400 0x5>, ++ <&dmamux1 60 0x400 0x5>; ++ dma-names = "ch1", "ch2", "ch3", "ch4", "up", "trig"; + status = "disabled"; + + pwm { +@@ -182,6 +225,8 @@ + reg = <0x40004000 0x400>; + clocks = <&rcc TIM6_K>; + clock-names = "int"; ++ dmas = <&dmamux1 69 0x400 0x5>; ++ dma-names = "up"; + status = "disabled"; + + timer@5 { +@@ -198,6 +243,8 @@ + reg = <0x40005000 0x400>; + clocks = <&rcc TIM7_K>; + clock-names = "int"; ++ dmas = <&dmamux1 70 0x400 0x5>; ++ dma-names = "up"; + status = "disabled"; + + timer@6 { +@@ -277,6 +324,7 @@ + reg = <0x40009000 0x400>; + clocks = <&rcc LPTIM1_K>; + clock-names = "mux"; ++ power-domains = <&pd_core>; + status = "disabled"; + + pwm { +@@ -308,6 +356,7 @@ + dmas = <&dmamux1 39 0x400 0x05>, + <&dmamux1 40 0x400 0x05>; + dma-names = "rx", "tx"; ++ power-domains = <&pd_core>; + status = "disabled"; + }; + +@@ -322,90 +371,131 @@ + dmas = <&dmamux1 61 0x400 0x05>, + <&dmamux1 62 0x400 0x05>; + dma-names = "rx", "tx"; ++ power-domains = <&pd_core>; + status = "disabled"; + }; + + usart2: serial@4000e000 { + compatible = "st,stm32h7-uart"; + reg = <0x4000e000 0x400>; +- interrupts = ; ++ interrupt-names = "event", "wakeup"; ++ interrupts-extended = <&intc GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>, ++ <&exti 27 1>; + clocks = <&rcc USART2_K>; ++ wakeup-source; ++ power-domains = <&pd_core>; + status = "disabled"; + }; + + usart3: serial@4000f000 { + compatible = "st,stm32h7-uart"; + reg = <0x4000f000 0x400>; +- interrupts = ; ++ interrupt-names = "event", "wakeup"; ++ interrupts-extended = <&intc GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>, ++ <&exti 28 1>; + clocks = <&rcc USART3_K>; ++ wakeup-source; ++ power-domains = <&pd_core>; + status = "disabled"; + }; + + uart4: serial@40010000 { + compatible = "st,stm32h7-uart"; + reg = <0x40010000 0x400>; +- interrupts = ; ++ interrupt-names = "event", "wakeup"; ++ interrupts-extended = <&intc GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>, ++ <&exti 30 1>; + clocks = <&rcc UART4_K>; ++ wakeup-source; ++ power-domains = <&pd_core>; + status = "disabled"; + }; + + uart5: serial@40011000 { + compatible = "st,stm32h7-uart"; + reg = <0x40011000 0x400>; +- interrupts = ; ++ interrupt-names = "event", "wakeup"; ++ interrupts-extended = <&intc GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>, ++ <&exti 31 1>; + clocks = <&rcc UART5_K>; ++ wakeup-source; ++ power-domains = <&pd_core>; + status = "disabled"; + }; + + i2c1: i2c@40012000 { + compatible = "st,stm32f7-i2c"; + reg = <0x40012000 0x400>; +- interrupt-names = "event", "error"; +- interrupts = , +- ; ++ interrupt-names = "event", "error", "wakeup"; ++ interrupts-extended = <&intc GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>, ++ <&intc GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>, ++ <&exti 21 1>; + clocks = <&rcc I2C1_K>; + resets = <&rcc I2C1_R>; + #address-cells = <1>; + #size-cells = <0>; ++ dmas = <&dmamux1 33 0x400 0x05>, ++ <&dmamux1 34 0x400 0x05>; ++ dma-names = "rx", "tx"; ++ power-domains = <&pd_core>; ++ st,syscfg-fmp = <&syscfg 0x4 0x1>; + status = "disabled"; + }; + + i2c2: i2c@40013000 { + compatible = "st,stm32f7-i2c"; + reg = <0x40013000 0x400>; +- interrupt-names = "event", "error"; +- interrupts = , +- ; ++ interrupt-names = "event", "error", "wakeup"; ++ interrupts-extended = <&intc GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>, ++ <&intc GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>, ++ <&exti 22 1>; + clocks = <&rcc I2C2_K>; + resets = <&rcc I2C2_R>; + #address-cells = <1>; + #size-cells = <0>; ++ dmas = <&dmamux1 35 0x400 0x05>, ++ <&dmamux1 36 0x400 0x05>; ++ dma-names = "rx", "tx"; ++ power-domains = <&pd_core>; ++ st,syscfg-fmp = <&syscfg 0x4 0x2>; + status = "disabled"; + }; + + i2c3: i2c@40014000 { + compatible = "st,stm32f7-i2c"; + reg = <0x40014000 0x400>; +- interrupt-names = "event", "error"; +- interrupts = , +- ; ++ interrupt-names = "event", "error", "wakeup"; ++ interrupts-extended = <&intc GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>, ++ <&intc GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>, ++ <&exti 23 1>; + clocks = <&rcc I2C3_K>; + resets = <&rcc I2C3_R>; + #address-cells = <1>; + #size-cells = <0>; ++ dmas = <&dmamux1 73 0x400 0x05>, ++ <&dmamux1 74 0x400 0x05>; ++ dma-names = "rx", "tx"; ++ power-domains = <&pd_core>; ++ st,syscfg-fmp = <&syscfg 0x4 0x4>; + status = "disabled"; + }; + + i2c5: i2c@40015000 { + compatible = "st,stm32f7-i2c"; + reg = <0x40015000 0x400>; +- interrupt-names = "event", "error"; +- interrupts = , +- ; ++ interrupt-names = "event", "error", "wakeup"; ++ interrupts-extended = <&intc GIC_SPI 107 IRQ_TYPE_LEVEL_HIGH>, ++ <&intc GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>, ++ <&exti 25 1>; + clocks = <&rcc I2C5_K>; + resets = <&rcc I2C5_R>; + #address-cells = <1>; + #size-cells = <0>; ++ dmas = <&dmamux1 115 0x400 0x05>, ++ <&dmamux1 116 0x400 0x05>; ++ dma-names = "rx", "tx"; ++ power-domains = <&pd_core>; ++ st,syscfg-fmp = <&syscfg 0x4 0x10>; + status = "disabled"; + }; + +@@ -415,6 +505,7 @@ + interrupts = ; + clocks = <&rcc CEC_K>, <&clk_lse>; + clock-names = "cec", "hdmi-cec"; ++ power-domains = <&pd_core>; + status = "disabled"; + }; + +@@ -445,16 +536,24 @@ + uart7: serial@40018000 { + compatible = "st,stm32h7-uart"; + reg = <0x40018000 0x400>; +- interrupts = ; ++ interrupt-names = "event", "wakeup"; ++ interrupts-extended = <&intc GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>, ++ <&exti 32 1>; + clocks = <&rcc UART7_K>; ++ wakeup-source; ++ power-domains = <&pd_core>; + status = "disabled"; + }; + + uart8: serial@40019000 { + compatible = "st,stm32h7-uart"; + reg = <0x40019000 0x400>; +- interrupts = ; ++ interrupt-names = "event", "wakeup"; ++ interrupts-extended = <&intc GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>, ++ <&exti 33 1>; + clocks = <&rcc UART8_K>; ++ wakeup-source; ++ power-domains = <&pd_core>; + status = "disabled"; + }; + +@@ -465,6 +564,15 @@ + reg = <0x44000000 0x400>; + clocks = <&rcc TIM1_K>; + clock-names = "int"; ++ dmas = <&dmamux1 11 0x400 0x5>, ++ <&dmamux1 12 0x400 0x5>, ++ <&dmamux1 13 0x400 0x5>, ++ <&dmamux1 14 0x400 0x5>, ++ <&dmamux1 15 0x400 0x5>, ++ <&dmamux1 16 0x400 0x5>, ++ <&dmamux1 17 0x400 0x5>; ++ dma-names = "ch1", "ch2", "ch3", "ch4", ++ "up", "trig", "com"; + status = "disabled"; + + pwm { +@@ -486,6 +594,15 @@ + reg = <0x44001000 0x400>; + clocks = <&rcc TIM8_K>; + clock-names = "int"; ++ dmas = <&dmamux1 47 0x400 0x5>, ++ <&dmamux1 48 0x400 0x5>, ++ <&dmamux1 49 0x400 0x5>, ++ <&dmamux1 50 0x400 0x5>, ++ <&dmamux1 51 0x400 0x5>, ++ <&dmamux1 52 0x400 0x5>, ++ <&dmamux1 53 0x400 0x5>; ++ dma-names = "ch1", "ch2", "ch3", "ch4", ++ "up", "trig", "com"; + status = "disabled"; + + pwm { +@@ -503,8 +620,12 @@ + usart6: serial@44003000 { + compatible = "st,stm32h7-uart"; + reg = <0x44003000 0x400>; +- interrupts = ; ++ interrupt-names = "event", "wakeup"; ++ interrupts-extended = <&intc GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>, ++ <&exti 29 1>; + clocks = <&rcc USART6_K>; ++ wakeup-source; ++ power-domains = <&pd_core>; + status = "disabled"; + }; + +@@ -519,6 +640,7 @@ + dmas = <&dmamux1 37 0x400 0x05>, + <&dmamux1 38 0x400 0x05>; + dma-names = "rx", "tx"; ++ power-domains = <&pd_core>; + status = "disabled"; + }; + +@@ -533,6 +655,7 @@ + dmas = <&dmamux1 83 0x400 0x05>, + <&dmamux1 84 0x400 0x05>; + dma-names = "rx", "tx"; ++ power-domains = <&pd_core>; + status = "disabled"; + }; + +@@ -543,6 +666,11 @@ + reg = <0x44006000 0x400>; + clocks = <&rcc TIM15_K>; + clock-names = "int"; ++ dmas = <&dmamux1 105 0x400 0x5>, ++ <&dmamux1 106 0x400 0x5>, ++ <&dmamux1 107 0x400 0x5>, ++ <&dmamux1 108 0x400 0x5>; ++ dma-names = "ch1", "up", "trig", "com"; + status = "disabled"; + + pwm { +@@ -564,6 +692,9 @@ + reg = <0x44007000 0x400>; + clocks = <&rcc TIM16_K>; + clock-names = "int"; ++ dmas = <&dmamux1 109 0x400 0x5>, ++ <&dmamux1 110 0x400 0x5>; ++ dma-names = "ch1", "up"; + status = "disabled"; + + pwm { +@@ -584,6 +715,9 @@ + reg = <0x44008000 0x400>; + clocks = <&rcc TIM17_K>; + clock-names = "int"; ++ dmas = <&dmamux1 111 0x400 0x5>, ++ <&dmamux1 112 0x400 0x5>; ++ dma-names = "ch1", "up"; + status = "disabled"; + + pwm { +@@ -609,6 +743,7 @@ + dmas = <&dmamux1 85 0x400 0x05>, + <&dmamux1 86 0x400 0x05>; + dma-names = "rx", "tx"; ++ power-domains = <&pd_core>; + status = "disabled"; + }; + +@@ -684,7 +819,7 @@ + + m_can1: can@4400e000 { + compatible = "bosch,m_can"; +- reg = <0x4400e000 0x400>, <0x44011000 0x2800>; ++ reg = <0x4400e000 0x400>, <0x44011000 0x1400>; + reg-names = "m_can", "message_ram"; + interrupts = , + ; +@@ -704,7 +839,7 @@ + interrupt-names = "int0", "int1"; + clocks = <&rcc CK_HSE>, <&rcc FDCAN_K>; + clock-names = "hclk", "cclk"; +- bosch,mram-cfg = <0x0 0 0 32 0 0 2 2>; ++ bosch,mram-cfg = <0x1400 0 0 32 0 0 2 2>; + status = "disabled"; + }; + +@@ -723,6 +858,15 @@ + #dma-cells = <4>; + st,mem2mem; + dma-requests = <8>; ++ dmas = <&mdma1 0 0x11 0x1200000a 0x48000008 0x00000020 1>, ++ <&mdma1 1 0x11 0x1200000a 0x48000008 0x00000800 1>, ++ <&mdma1 2 0x11 0x1200000a 0x48000008 0x00200000 1>, ++ <&mdma1 3 0x11 0x1200000a 0x48000008 0x08000000 1>, ++ <&mdma1 4 0x11 0x1200000a 0x4800000C 0x00000020 1>, ++ <&mdma1 5 0x11 0x1200000a 0x4800000C 0x00000800 1>, ++ <&mdma1 6 0x11 0x1200000a 0x4800000C 0x00200000 1>, ++ <&mdma1 7 0x11 0x1200000a 0x4800000C 0x08000000 1>; ++ dma-names = "ch0", "ch1", "ch2", "ch3", "ch4", "ch5", "ch6", "ch7"; + }; + + dma2: dma@48001000 { +@@ -740,6 +884,15 @@ + #dma-cells = <4>; + st,mem2mem; + dma-requests = <8>; ++ dmas = <&mdma1 8 0x11 0x1200000a 0x48001008 0x00000020 1>, ++ <&mdma1 9 0x11 0x1200000a 0x48001008 0x00000800 1>, ++ <&mdma1 10 0x11 0x1200000a 0x48001008 0x00200000 1>, ++ <&mdma1 11 0x11 0x1200000a 0x48001008 0x08000000 1>, ++ <&mdma1 12 0x11 0x1200000a 0x4800100C 0x00000020 1>, ++ <&mdma1 13 0x11 0x1200000a 0x4800100C 0x00000800 1>, ++ <&mdma1 14 0x11 0x1200000a 0x4800100C 0x00200000 1>, ++ <&mdma1 15 0x11 0x1200000a 0x4800100C 0x08000000 1>; ++ dma-names = "ch0", "ch1", "ch2", "ch3", "ch4", "ch5", "ch6", "ch7"; + }; + + dmamux1: dma-router@48002000 { +@@ -784,10 +937,61 @@ + interrupts = <1>; + dmas = <&dmamux1 10 0x400 0x01>; + dma-names = "rx"; ++ /* temperature sensor */ ++ st,adc-channels = <12>; ++ st,min-sample-time-nsecs = <10000>; ++ status = "disabled"; ++ }; ++ ++ jadc1: jadc@0 { ++ compatible = "st,stm32mp1-adc"; ++ st,injected; ++ #io-channel-cells = <1>; ++ reg = <0x0>; ++ interrupt-parent = <&adc>; ++ interrupts = <3>; ++ status = "disabled"; ++ }; ++ ++ jadc2: jadc@100 { ++ compatible = "st,stm32mp1-adc"; ++ st,injected; ++ #io-channel-cells = <1>; ++ reg = <0x100>; ++ interrupt-parent = <&adc>; ++ interrupts = <4>; ++ /* temperature sensor */ ++ st,adc-channels = <12>; ++ st,min-sample-time-nsecs = <10000>; ++ status = "disabled"; ++ }; ++ ++ adc_temp: temp { ++ compatible = "st,stm32mp1-adc-temp"; ++ io-channels = <&adc2 12>; ++ nvmem-cells = <&ts_cal1>, <&ts_cal2>; ++ nvmem-cell-names = "ts_cal1", "ts_cal2"; ++ #io-channel-cells = <0>; ++ #thermal-sensor-cells = <0>; + status = "disabled"; + }; + }; + ++ sdmmc3: sdmmc@48004000 { ++ compatible = "arm,pl18x", "arm,primecell"; ++ arm,primecell-periphid = <0x10153180>; ++ reg = <0x48004000 0x400>; ++ interrupts = ; ++ interrupt-names = "cmd_irq"; ++ clocks = <&rcc SDMMC3_K>; ++ clock-names = "apb_pclk"; ++ resets = <&rcc SDMMC3_R>; ++ cap-sd-highspeed; ++ cap-mmc-highspeed; ++ max-frequency = <120000000>; ++ status = "disabled"; ++ }; ++ + usbotg_hs: usb-otg@49000000 { + compatible = "snps,dwc2"; + reg = <0x49000000 0x10000>; +@@ -800,6 +1004,23 @@ + g-np-tx-fifo-size = <32>; + g-tx-fifo-size = <128 128 64 64 64 64 32 32>; + dr_mode = "otg"; ++ power-domains = <&pd_core>; ++ status = "disabled"; ++ }; ++ ++ ipcc: mailbox@4c001000 { ++ compatible = "st,stm32mp1-ipcc"; ++ #mbox-cells = <1>; ++ reg = <0x4c001000 0x400>; ++ st,proc-id = <0>; ++ interrupts-extended = ++ <&intc GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>, ++ <&intc GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>, ++ <&exti 61 1>; ++ interrupt-names = "rx", "tx", "wakeup"; ++ clocks = <&rcc IPCC>; ++ wakeup-source; ++ power-domains = <&pd_core>; + status = "disabled"; + }; + +@@ -808,6 +1029,41 @@ + reg = <0x50000000 0x1000>; + #clock-cells = <1>; + #reset-cells = <1>; ++ interrupts = ; ++ st,pwr = <&pwr>; ++ }; ++ ++ pwr: pwr@50001000 { ++ compatible = "st,stm32mp1-pwr", "syscon", "simple-mfd"; ++ reg = <0x50001000 0x400>; ++ ++ interrupts = ; ++ ++ interrupt-controller; ++ #interrupt-cells = <3>; ++ ++ pwr-regulators { ++ compatible = "st,stm32mp1,pwr-reg"; ++ st,tzcr = <&rcc 0x0 0x1>; ++ ++ reg11: reg11 { ++ regulator-name = "reg11"; ++ regulator-min-microvolt = <1100000>; ++ regulator-max-microvolt = <1100000>; ++ }; ++ ++ reg18: reg18 { ++ regulator-name = "reg18"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ }; ++ ++ usb33: usb33 { ++ regulator-name = "usb33"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ }; ++ }; + }; + + exti: interrupt-controller@5000d000 { +@@ -829,6 +1085,7 @@ + reg = <0x50021000 0x400>; + clocks = <&rcc LPTIM2_K>; + clock-names = "mux"; ++ power-domains = <&pd_core>; + status = "disabled"; + + pwm { +@@ -856,6 +1113,7 @@ + reg = <0x50022000 0x400>; + clocks = <&rcc LPTIM3_K>; + clock-names = "mux"; ++ power-domains = <&pd_core>; + status = "disabled"; + + pwm { +@@ -876,6 +1134,7 @@ + reg = <0x50023000 0x400>; + clocks = <&rcc LPTIM4_K>; + clock-names = "mux"; ++ power-domains = <&pd_core>; + status = "disabled"; + + pwm { +@@ -890,6 +1149,7 @@ + reg = <0x50024000 0x400>; + clocks = <&rcc LPTIM5_K>; + clock-names = "mux"; ++ power-domains = <&pd_core>; + status = "disabled"; + + pwm { +@@ -923,7 +1183,7 @@ + interrupts = ; + clocks = <&rcc HASH1>; + resets = <&rcc HASH1_R>; +- dmas = <&mdma1 31 0x10 0x1000A02 0x0 0x0 0x0>; ++ dmas = <&mdma1 31 0x10 0x1000A02 0x0 0x0 0x0 0x0>; + dma-names = "in"; + dma-maxburst = <2>; + status = "disabled"; +@@ -942,11 +1202,30 @@ + reg = <0x58000000 0x1000>; + interrupts = ; + clocks = <&rcc MDMA>; +- #dma-cells = <5>; ++ #dma-cells = <6>; + dma-channels = <32>; + dma-requests = <48>; + }; + ++ fmc: nand-controller@58002000 { ++ compatible = "st,stm32mp15-fmc2"; ++ reg = <0x58002000 0x1000>, ++ <0x80000000 0x1000>, ++ <0x88010000 0x1000>, ++ <0x88020000 0x1000>, ++ <0x81000000 0x1000>, ++ <0x89010000 0x1000>, ++ <0x89020000 0x1000>; ++ interrupts = ; ++ dmas = <&mdma1 20 0x10 0x12000A02 0x0 0x0 0>, ++ <&mdma1 20 0x10 0x12000A08 0x0 0x0 0>, ++ <&mdma1 21 0x10 0x12000A0A 0x0 0x0 0>; ++ dma-names = "tx", "rx", "ecc"; ++ clocks = <&rcc FMC_K>; ++ resets = <&rcc FMC_R>; ++ status = "disabled"; ++ }; ++ + qspi: qspi@58003000 { + compatible = "st,stm32f469-qspi"; + reg = <0x58003000 0x1000>, <0x70000000 0x10000000>; +@@ -957,6 +1236,36 @@ + status = "disabled"; + }; + ++ sdmmc1: sdmmc@58005000 { ++ compatible = "arm,pl18x", "arm,primecell"; ++ arm,primecell-periphid = <0x10153180>; ++ reg = <0x58005000 0x1000>; ++ interrupts = ; ++ interrupt-names = "cmd_irq"; ++ clocks = <&rcc SDMMC1_K>; ++ clock-names = "apb_pclk"; ++ resets = <&rcc SDMMC1_R>; ++ cap-sd-highspeed; ++ cap-mmc-highspeed; ++ max-frequency = <120000000>; ++ status = "disabled"; ++ }; ++ ++ sdmmc2: sdmmc@58007000 { ++ compatible = "arm,pl18x", "arm,primecell"; ++ arm,primecell-periphid = <0x10153180>; ++ reg = <0x58007000 0x1000>; ++ interrupts = ; ++ interrupt-names = "cmd_irq"; ++ clocks = <&rcc SDMMC2_K>; ++ clock-names = "apb_pclk"; ++ resets = <&rcc SDMMC2_R>; ++ cap-sd-highspeed; ++ cap-mmc-highspeed; ++ max-frequency = <120000000>; ++ status = "disabled"; ++ }; ++ + crc1: crc@58009000 { + compatible = "st,stm32f7-crc"; + reg = <0x58009000 0x400>; +@@ -974,8 +1283,12 @@ + compatible = "st,stm32mp1-dwmac", "snps,dwmac-4.20a"; + reg = <0x5800a000 0x2000>; + reg-names = "stmmaceth"; +- interrupts-extended = <&intc GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>; +- interrupt-names = "macirq"; ++ interrupts-extended = <&intc GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>, ++ <&intc GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>, ++ <&exti 70 1>; ++ interrupt-names = "macirq", ++ "eth_wake_irq", ++ "stm32_pwr_wakeup"; + clock-names = "stmmaceth", + "mac-clk-tx", + "mac-clk-rx", +@@ -991,6 +1304,7 @@ + snps,pbl = <2>; + snps,axi-config = <&stmmac_axi_config_0>; + snps,tso; ++ power-domains = <&pd_core>; + status = "disabled"; + }; + +@@ -1065,8 +1379,12 @@ + usart1: serial@5c000000 { + compatible = "st,stm32h7-uart"; + reg = <0x5c000000 0x400>; +- interrupts = ; ++ interrupt-names = "event", "wakeup"; ++ interrupts-extended = <&intc GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>, ++ <&exti 26 1>; + clocks = <&rcc USART1_K>; ++ wakeup-source; ++ power-domains = <&pd_core>; + status = "disabled"; + }; + +@@ -1078,22 +1396,29 @@ + interrupts = ; + clocks = <&rcc SPI6_K>; + resets = <&rcc SPI6_R>; +- dmas = <&mdma1 34 0x0 0x40008 0x0 0x0>, +- <&mdma1 35 0x0 0x40002 0x0 0x0>; ++ dmas = <&mdma1 34 0x0 0x40008 0x0 0x0 0x0>, ++ <&mdma1 35 0x0 0x40002 0x0 0x0 0x0>; + dma-names = "rx", "tx"; ++ power-domains = <&pd_core>; + status = "disabled"; + }; + + i2c4: i2c@5c002000 { + compatible = "st,stm32f7-i2c"; + reg = <0x5c002000 0x400>; +- interrupt-names = "event", "error"; +- interrupts = , +- ; ++ interrupt-names = "event", "error", "wakeup"; ++ interrupts-extended = <&intc GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>, ++ <&intc GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>, ++ <&exti 24 1>; + clocks = <&rcc I2C4_K>; + resets = <&rcc I2C4_R>; + #address-cells = <1>; + #size-cells = <0>; ++ dmas = <&mdma1 36 0x0 0x40008 0x0 0x0 0>, ++ <&mdma1 37 0x0 0x40002 0x0 0x0 0>; ++ dma-names = "rx", "tx"; ++ power-domains = <&pd_core>; ++ st,syscfg-fmp = <&syscfg 0x4 0x8>; + status = "disabled"; + }; + +@@ -1102,20 +1427,76 @@ + reg = <0x5c004000 0x400>; + clocks = <&rcc RTCAPB>, <&rcc RTC>; + clock-names = "pclk", "rtc_ck"; +- interrupts = ; ++ interrupts-extended = <&intc GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>, ++ <&exti 19 1>; + status = "disabled"; + }; + ++ bsec: nvmem@5c005000 { ++ compatible = "st,stm32mp15-bsec"; ++ reg = <0x5c005000 0x400>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ts_cal1: calib@5c { ++ reg = <0x5c 0x2>; ++ }; ++ ts_cal2: calib@5e { ++ reg = <0x5e 0x2>; ++ }; ++ }; ++ + i2c6: i2c@5c009000 { + compatible = "st,stm32f7-i2c"; + reg = <0x5c009000 0x400>; +- interrupt-names = "event", "error"; +- interrupts = , +- ; ++ interrupt-names = "event", "error", "wakeup"; ++ interrupts-extended = <&intc GIC_SPI 135 IRQ_TYPE_LEVEL_HIGH>, ++ <&intc GIC_SPI 136 IRQ_TYPE_LEVEL_HIGH>, ++ <&exti 54 1>; + clocks = <&rcc I2C6_K>; + resets = <&rcc I2C6_R>; + #address-cells = <1>; + #size-cells = <0>; ++ dmas = <&mdma1 38 0x0 0x40008 0x0 0x0 0>, ++ <&mdma1 39 0x0 0x40002 0x0 0x0 0>; ++ dma-names = "rx", "tx"; ++ power-domains = <&pd_core>; ++ st,syscfg-fmp = <&syscfg 0x4 0x20>; ++ status = "disabled"; ++ }; ++ ++ tamp: tamp@5c00a000 { ++ compatible = "simple-bus", "syscon", "simple-mfd"; ++ reg = <0x5c00a000 0x400>; ++ ++ reboot-mode { ++ compatible = "syscon-reboot-mode"; ++ offset = <0x150>; /* reg20 */ ++ mask = <0xff>; ++ mode-normal = <0>; ++ mode-fastboot = <0x1>; ++ mode-recovery = <0x2>; ++ mode-stm32cubeprogrammer = <0x3>; ++ mode-ums_mmc0 = <0x10>; ++ mode-ums_mmc1 = <0x11>; ++ mode-ums_mmc2 = <0x12>; ++ }; ++ }; ++ }; ++ ++ m4_rproc: m4@0 { ++ compatible = "st,stm32mp1-rproc"; ++ reg = <0x38000000 0x10000>, ++ <0x10000000 0x40000>; ++ reg-names = "retram", "mcusram"; ++ resets = <&rcc MCU_R>; ++ reset-names = "mcu_rst"; ++ st,syscfg-pdds = <&pwr 0x014 0x1>; ++ st,syscfg-holdboot = <&rcc 0x10C 0x1>; ++ st,syscfg-tz = <&rcc 0x000 0x1>; ++ status = "disabled"; ++ ++ m4_system_resources { ++ compatible = "rproc-srm-core"; + status = "disabled"; + }; + }; +diff --git a/arch/arm/boot/dts/stm32mp157caa-pinctrl.dtsi b/arch/arm/boot/dts/stm32mp157caa-pinctrl.dtsi +new file mode 100644 +index 0000000..9b9cd08 +--- /dev/null ++++ b/arch/arm/boot/dts/stm32mp157caa-pinctrl.dtsi +@@ -0,0 +1,90 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) ++/* ++ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved ++ * Author: Alexandre Torgue ++ */ ++ ++#include "stm32mp157-pinctrl.dtsi" ++/ { ++ soc { ++ pinctrl: pin-controller@50002000 { ++ st,package = ; ++ ++ gpioa: gpio@50002000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 0 16>; ++ }; ++ ++ gpiob: gpio@50003000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 16 16>; ++ }; ++ ++ gpioc: gpio@50004000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 32 16>; ++ }; ++ ++ gpiod: gpio@50005000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 48 16>; ++ }; ++ ++ gpioe: gpio@50006000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 64 16>; ++ }; ++ ++ gpiof: gpio@50007000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 80 16>; ++ }; ++ ++ gpiog: gpio@50008000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 96 16>; ++ }; ++ ++ gpioh: gpio@50009000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 112 16>; ++ }; ++ ++ gpioi: gpio@5000a000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 128 16>; ++ }; ++ ++ gpioj: gpio@5000b000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 144 16>; ++ }; ++ ++ gpiok: gpio@5000c000 { ++ status = "okay"; ++ ngpios = <8>; ++ gpio-ranges = <&pinctrl 0 160 8>; ++ }; ++ }; ++ ++ pinctrl_z: pin-controller-z@54004000 { ++ st,package = ; ++ ++ gpioz: gpio@54004000 { ++ status = "okay"; ++ ngpios = <8>; ++ gpio-ranges = <&pinctrl_z 0 400 8>; ++ }; ++ }; ++ }; ++}; +diff --git a/arch/arm/boot/dts/stm32mp157cab-pinctrl.dtsi b/arch/arm/boot/dts/stm32mp157cab-pinctrl.dtsi +new file mode 100644 +index 0000000..c570cf9 +--- /dev/null ++++ b/arch/arm/boot/dts/stm32mp157cab-pinctrl.dtsi +@@ -0,0 +1,62 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) ++/* ++ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved ++ * Author: Alexandre Torgue ++ */ ++ ++#include "stm32mp157-pinctrl.dtsi" ++/ { ++ soc { ++ pinctrl: pin-controller@50002000 { ++ st,package = ; ++ ++ gpioa: gpio@50002000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 0 16>; ++ }; ++ ++ gpiob: gpio@50003000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 16 16>; ++ }; ++ ++ gpioc: gpio@50004000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 32 16>; ++ }; ++ ++ gpiod: gpio@50005000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 48 16>; ++ }; ++ ++ gpioe: gpio@50006000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 64 16>; ++ }; ++ ++ gpiof: gpio@50007000 { ++ status = "okay"; ++ ngpios = <6>; ++ gpio-ranges = <&pinctrl 6 86 6>; ++ }; ++ ++ gpiog: gpio@50008000 { ++ status = "okay"; ++ ngpios = <10>; ++ gpio-ranges = <&pinctrl 6 102 10>; ++ }; ++ ++ gpioh: gpio@50009000 { ++ status = "okay"; ++ ngpios = <2>; ++ gpio-ranges = <&pinctrl 0 112 2>; ++ }; ++ }; ++ }; ++}; +diff --git a/arch/arm/boot/dts/stm32mp157cac-pinctrl.dtsi b/arch/arm/boot/dts/stm32mp157cac-pinctrl.dtsi +new file mode 100644 +index 0000000..777f991 +--- /dev/null ++++ b/arch/arm/boot/dts/stm32mp157cac-pinctrl.dtsi +@@ -0,0 +1,78 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) ++/* ++ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved ++ * Author: Alexandre Torgue ++ */ ++ ++#include "stm32mp157-pinctrl.dtsi" ++/ { ++ soc { ++ pinctrl: pin-controller@50002000 { ++ st,package = ; ++ ++ gpioa: gpio@50002000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 0 16>; ++ }; ++ ++ gpiob: gpio@50003000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 16 16>; ++ }; ++ ++ gpioc: gpio@50004000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 32 16>; ++ }; ++ ++ gpiod: gpio@50005000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 48 16>; ++ }; ++ ++ gpioe: gpio@50006000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 64 16>; ++ }; ++ ++ gpiof: gpio@50007000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 80 16>; ++ }; ++ ++ gpiog: gpio@50008000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 96 16>; ++ }; ++ ++ gpioh: gpio@50009000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 112 16>; ++ }; ++ ++ gpioi: gpio@5000a000 { ++ status = "okay"; ++ ngpios = <12>; ++ gpio-ranges = <&pinctrl 0 128 12>; ++ }; ++ }; ++ ++ pinctrl_z: pin-controller-z@54004000 { ++ st,package = ; ++ ++ gpioz: gpio@54004000 { ++ status = "okay"; ++ ngpios = <8>; ++ gpio-ranges = <&pinctrl_z 0 400 8>; ++ }; ++ }; ++ }; ++}; +diff --git a/arch/arm/boot/dts/stm32mp157cad-pinctrl.dtsi b/arch/arm/boot/dts/stm32mp157cad-pinctrl.dtsi +new file mode 100644 +index 0000000..c4c303a +--- /dev/null ++++ b/arch/arm/boot/dts/stm32mp157cad-pinctrl.dtsi +@@ -0,0 +1,62 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) ++/* ++ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved ++ * Author: Alexandre Torgue ++ */ ++ ++#include "stm32mp157-pinctrl.dtsi" ++/ { ++ soc { ++ pinctrl: pin-controller@50002000 { ++ st,package = ; ++ ++ gpioa: gpio@50002000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 0 16>; ++ }; ++ ++ gpiob: gpio@50003000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 16 16>; ++ }; ++ ++ gpioc: gpio@50004000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 32 16>; ++ }; ++ ++ gpiod: gpio@50005000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 48 16>; ++ }; ++ ++ gpioe: gpio@50006000 { ++ status = "okay"; ++ ngpios = <16>; ++ gpio-ranges = <&pinctrl 0 64 16>; ++ }; ++ ++ gpiof: gpio@50007000 { ++ status = "okay"; ++ ngpios = <6>; ++ gpio-ranges = <&pinctrl 6 86 6>; ++ }; ++ ++ gpiog: gpio@50008000 { ++ status = "okay"; ++ ngpios = <10>; ++ gpio-ranges = <&pinctrl 6 102 10>; ++ }; ++ ++ gpioh: gpio@50009000 { ++ status = "okay"; ++ ngpios = <2>; ++ gpio-ranges = <&pinctrl 0 112 2>; ++ }; ++ }; ++ }; ++}; +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0017-ARM-stm32mp1-r0-rc1-DEFCONFIG.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0017-ARM-stm32mp1-r0-rc1-DEFCONFIG.patch new file mode 100644 index 0000000..481d7fb --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0017-ARM-stm32mp1-r0-rc1-DEFCONFIG.patch @@ -0,0 +1,558 @@ +From ae7d85a994c77d68e3e6a20a24160a5010ba42cc Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Tue, 13 Nov 2018 12:32:03 +0100 +Subject: [PATCH 17/52] ARM: stm32mp1-r0-rc1: DEFCONFIG + +--- + .../arm/configs/fragment-01-multiv7_cleanup.config | 69 +++ + arch/arm/configs/fragment-02-multiv7_addons.config | 462 +++++++++++++++++++++ + 2 files changed, 531 insertions(+) + create mode 100644 arch/arm/configs/fragment-01-multiv7_cleanup.config + create mode 100644 arch/arm/configs/fragment-02-multiv7_addons.config + +diff --git a/arch/arm/configs/fragment-01-multiv7_cleanup.config b/arch/arm/configs/fragment-01-multiv7_cleanup.config +new file mode 100644 +index 0000000..22f6ffb +--- /dev/null ++++ b/arch/arm/configs/fragment-01-multiv7_cleanup.config +@@ -0,0 +1,69 @@ ++# ++# CPU Core family selection ++# ++# CONFIG_ARCH_VIRT is not set ++# CONFIG_ARCH_MVEBU is not set ++# CONFIG_ARCH_ALPINE is not set ++# CONFIG_ARCH_ARTPEC is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_BCM is not set ++# CONFIG_ARCH_BERLIN is not set ++# CONFIG_ARCH_DIGICOLOR is not set ++# CONFIG_ARCH_HIGHBANK is not set ++# CONFIG_ARCH_HISI is not set ++# CONFIG_ARCH_KEYSTONE is not set ++# CONFIG_ARCH_MESON is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MEDIATEK is not set ++ ++# ++# TI OMAP/AM/DM/DRA Family ++# ++# CONFIG_ARCH_OMAP3 is not set ++# CONFIG_ARCH_OMAP4 is not set ++# CONFIG_SOC_OMAP5 is not set ++# CONFIG_SOC_AM33XX is not set ++# CONFIG_SOC_AM43XX is not set ++# CONFIG_SOC_DRA7XX is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_QCOM is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_ROCKCHIP is not set ++# CONFIG_ARCH_SOCFPGA is not set ++# CONFIG_PLAT_SPEAR is not set ++# CONFIG_ARCH_STI is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS is not set ++# CONFIG_ARCH_RENESAS is not set ++# CONFIG_ARCH_SUNXI is not set ++# CONFIG_ARCH_SIRF is not set ++# CONFIG_ARCH_TANGO is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_UNIPHIER is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_WM8850 is not set ++# CONFIG_ARCH_ZX is not set ++# CONFIG_ARCH_ZYNQ is not set ++ ++# CONFIG_CAN_RCAR is not set ++# CONFIG_GPIO_PCA953X is not set ++# CONFIG_GPIO_PCF857X is not set ++# CONFIG_STAGING is not set ++# CONFIG_CHROME_PLATFORMS is not set ++# CONFIG_SOC_BRCMSTB is not set ++# CONFIG_SUNXI_SRAM is not set ++# CONFIG_SOC_TI is not set ++# CONFIG_PM_DEVFREQ is not set ++# CONFIG_EXTCON is not set ++# CONFIG_MEMORY is not set ++# CONFIG_ENABLE_WARN_DEPRECATED is not set ++# CONFIG_ENABLE_MUST_CHECK is not set ++# CONFIG_SECTION_MISMATCH_WARN_ONLY is not set ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++ ++ ++ ++ ++ +diff --git a/arch/arm/configs/fragment-02-multiv7_addons.config b/arch/arm/configs/fragment-02-multiv7_addons.config +new file mode 100644 +index 0000000..c91840c +--- /dev/null ++++ b/arch/arm/configs/fragment-02-multiv7_addons.config +@@ -0,0 +1,462 @@ ++# ++# General setup ++# ++CONFIG_POSIX_MQUEUE=y ++CONFIG_USELIB=y ++ ++# ++# RCU Subsystem ++# ++# allow user to access kernel config through /proc/config.gz ++CONFIG_IKCONFIG=y ++CONFIG_IKCONFIG_PROC=y ++CONFIG_LOG_BUF_SHIFT=16 ++CONFIG_MEMCG=y ++CONFIG_NAMESPACES=y ++ ++# ++# Kernel Performance Events And Counters ++# ++CONFIG_PROFILING=y ++ ++# ++# CPU Core family selection ++# ++ ++# ++# Processor Features ++# ++# CONFIG_CACHE_L2X0 is not set ++ ++# ++# Bus support ++# ++# CONFIG_PCI is not set ++# CONFIG_PCI_DOMAINS_GENERIC is not set ++# CONFIG_PCI_SYSCALL is not set ++ ++# ++# Kernel Features ++# ++CONFIG_MCPM=y ++CONFIG_NR_CPUS=4 ++# CONFIG_PREEMPT_NONE is not set ++CONFIG_PREEMPT=y ++CONFIG_PREEMPT_COUNT=y ++CONFIG_AEABI=y ++CONFIG_HIGHMEM=y ++CONFIG_FORCE_MAX_ZONEORDER=12 ++ ++# ++# Boot options ++# ++# CONFIG_ATAGS is not set ++CONFIG_ZBOOT_ROM_TEXT=0x0 ++CONFIG_ZBOOT_ROM_BSS=0x0 ++# CONFIG_KEXEC is not set ++# CONFIG_EFI is not set ++ ++# ++# CPU Power Management ++# ++ ++# ++# CPU Frequency scaling ++# ++# CONFIG_CPU_FREQ is not set ++ ++# ++# CPU Idle ++# ++# CONFIG_CPU_IDLE is not set ++ ++# ++# Floating point emulation ++# ++CONFIG_VFP=y ++ ++# ++# Networking options ++# ++CONFIG_DNS_RESOLVER=y ++ ++# ++# CAN Device Drivers ++# ++CONFIG_CAN_M_CAN=y ++ ++# ++# CAN SPI interfaces ++# ++# CONFIG_CAN_MCP251X is not set ++ ++# ++# CAN USB interfaces ++# ++ ++# ++# Bluetooth device drivers ++# ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y ++ ++# ++# Default contiguous memory area size: ++# ++CONFIG_CMA_SIZE_MBYTES=128 ++ ++# ++# Disk-On-Chip Device Drivers ++# ++ ++# ++# LPDDR & LPDDR2 PCM memory drivers ++# ++CONFIG_OF_RESOLVE=y ++CONFIG_OF_OVERLAY=y ++CONFIG_OF_CONFIGFS=y ++ ++# ++# Misc devices ++# ++CONFIG_SRAM=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_CHR_DEV_SG=y ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_REALTEK_PHY is not set ++ ++# ++# Input Device Drivers ++# ++ ++# ++# Character devices ++# ++CONFIG_SERIAL_NONSTANDARD=y ++ ++# ++# Serial drivers ++# ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_BCM63XX is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_SERIAL_FSL_LPUART is not set ++# CONFIG_SERIAL_CONEXANT_DIGICOLOR is not set ++# CONFIG_SERIAL_ST_ASC is not set ++# CONFIG_VIRTIO_CONSOLE is not set ++ ++# ++# I2C Hardware Bus support ++# ++# CONFIG_I2C_DESIGNWARE_PLATFORM is not set ++# CONFIG_I2C_EMEV2 is not set ++# CONFIG_I2C_GPIO is not set ++# CONFIG_I2C_RK3X is not set ++# CONFIG_I2C_XILINX is not set ++ ++# ++# Other I2C/SMBus bus drivers ++# ++# CONFIG_I2C_SLAVE_EEPROM is not set ++ ++# ++# SPI Master Controller Drivers ++# ++CONFIG_SPI_STM32_QSPI=y ++ ++# ++# Pin controllers ++# ++CONFIG_PINCTRL_STMFX=y ++ ++# ++# Memory mapped GPIO drivers ++# ++ ++# ++# USB GPIO expanders ++# ++# CONFIG_POWER_AVS is not set ++CONFIG_POWER_RESET=y ++CONFIG_POWER_RESET_SYSCON=y ++CONFIG_SYSCON_REBOOT_MODE=y ++# CONFIG_POWER_SUPPLY is not set ++ ++# ++# Native drivers ++# ++CONFIG_SENSORS_IIO_HWMON=y ++CONFIG_THERMAL=y ++ ++# ++# STMicroelectronics thermal drivers ++# ++ ++ ++ ++# ++# Watchdog Device Drivers ++# ++ ++ ++# ++# Sonics Silicon Backplane ++# ++ ++# ++# Multifunction device drivers ++# ++CONFIG_PROTECTION_CONSUMER=y ++ ++# ++# STMicroelectronics STMPE Interface Drivers ++# ++# CONFIG_REGULATOR_WM8994 is not set ++ ++# ++# Multimedia core support ++# ++ ++# ++# USB HDMI CEC adapters ++# ++ ++# ++# Media ancillary drivers (tuners, sensors, i2c, spi, frontends) ++# ++# CONFIG_MEDIA_SUBDRV_AUTOSELECT is not set ++ ++# ++# Camera sensor devices ++# ++ ++# ++# Graphics support ++# ++# to solve issue on DK2 screen ++# CONFIG_DRM_FBDEV_EMULATION is not set ++# CONFIG_DRM_LOAD_EDID_FIRMWARE is not set ++ ++# ++# Sub-drivers ++# ++ ++# ++# Display Panels ++# ++ ++# ++# Display Interface Bridges ++# ++ ++# ++# Frame buffer hardware drivers ++# ++ ++# ++# Console display driver support ++# ++ ++# ++# HD-Audio ++# ++ ++# ++# CODEC drivers ++# ++ ++ ++# ++# USB Device Class drivers ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MUSB_HDRC is not set ++# CONFIG_USB_DWC3 is not set ++ ++# ++# Gadget/Dual-role mode requires USB Gadget support to be enabled ++# ++ ++# ++# USB Physical Layer drivers ++# ++ ++# ++# Platform Support ++# ++ ++# ++# Virtio drivers ++# ++ ++# ++# Clock Source drivers ++# ++ ++# ++# Rpmsg drivers ++# ++ ++# ++# File systems ++# ++CONFIG_OVERLAY_FS=y ++ ++# ++# Pseudo filesystems ++# ++CONFIG_TMPFS=y ++ ++# ++# Kernel hacking ++# ++ ++# ++# printk and dmesg options ++# ++CONFIG_DYNAMIC_DEBUG=y ++ ++# ++# Compile-time checks and compiler options ++# ++CONFIG_DEBUG_INFO=y ++CONFIG_GDB_SCRIPTS=y ++CONFIG_DEBUG_SECTION_MISMATCH=y ++ ++# ++# Debug Lockups and Hangs ++# ++# CONFIG_SCHED_DEBUG is not set ++CONFIG_DEBUG_PREEMPT=y ++ ++# ++# Runtime Testing ++# ++ ++# Security options ++# ++CONFIG_KEYS=y ++ ++ ++# ++# Library routines ++# ++CONFIG_CRC_ITU_T=m ++ ++# ++# STM32 PWR ++# ++CONFIG_MFD_STM32MP1_PWR=y ++CONFIG_REGULATOR_STM32_PWR=y ++ ++# ++# STPMIC1 ++# ++CONFIG_MFD_STPMIC1=y ++CONFIG_REGULATOR_STPMIC1=y ++CONFIG_STPMIC1_WATCHDOG=y ++CONFIG_INPUT_STPMIC1_ONKEY=y ++ ++# ++# STM32 TIMER ++# ++CONFIG_MFD_STM32_TIMERS=y ++CONFIG_IIO_STM32_TIMER_TRIGGER=y ++CONFIG_PWM_STM32=y ++ ++# ++# STM32 LPTIMER ++# ++CONFIG_MFD_STM32_LPTIMER=y ++CONFIG_PWM_STM32_LP=y ++CONFIG_IIO_STM32_LPTIMER_TRIGGER=y ++CONFIG_STM32_LPTIMER_CNT=y ++ ++# ++# STM32 DFSDM ++# ++CONFIG_SD_ADC_MODULATOR=y ++CONFIG_STM32_DFSDM_ADC=y ++ ++# ++# FMC2 ++# ++CONFIG_MTD_NAND_STM32_FMC2=y ++ ++# ++# STM32 VREFBUF ++# ++CONFIG_REGULATOR_STM32_VREFBUF=y ++ ++# ++# STM32 BSEC NVMEM ++# ++CONFIG_NVMEM_STM32_ROMEM=y ++ ++# ++# STM32 IPCC ++# ++CONFIG_STM32_IPCC=y ++ ++# ++# RPMSG_TTY ++# ++CONFIG_RPMSG_VIRTIO=y ++CONFIG_RPMSG_TTY=y ++ ++# ++# RPMSG client sample ++# ++CONFIG_SAMPLES=y ++CONFIG_SAMPLE_RPMSG_CLIENT=m ++ ++# ++# STM32 RPROC ++# ++# CONFIG_MAILBOX is not set ++CONFIG_REMOTEPROC=y ++CONFIG_STM32_RPROC=y ++CONFIG_RPMSG_VIRTIO=y ++ ++# ++# Cryptographic ++# ++CONFIG_CRYPTO_DEV_STM32_CRC=y ++CONFIG_CRYPTO_DEV_STM32_HASH=y ++CONFIG_CRYPTO_DEV_STM32_CRYP=y ++ ++# ++# STM32 ADC ++# ++CONFIG_STM32_ADC_CORE=y ++CONFIG_STM32_ADC=y ++CONFIG_STM32_ADC_TEMP=y ++ ++# ++# STM32 DAC ++# ++CONFIG_STM32_DAC=y +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0018-ARM-stm32mp1-r0-rc2-DRM-KMS.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0018-ARM-stm32mp1-r0-rc2-DRM-KMS.patch new file mode 100644 index 0000000..fbd7322 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0018-ARM-stm32mp1-r0-rc2-DRM-KMS.patch @@ -0,0 +1,1795 @@ +From fd965c7b7da4a0a21bd7750be8c3851be80a576f Mon Sep 17 00:00:00 2001 +From: Christophe Priouzeau +Date: Tue, 27 Nov 2018 09:39:21 +0100 +Subject: [PATCH 18/52] ARM-stm32mp1-r0-rc2-DRM-KMS + +--- + .../devicetree/bindings/display/bridge/sii902x.txt | 9 + + drivers/gpu/drm/bridge/Kconfig | 1 + + drivers/gpu/drm/bridge/sii902x.c | 844 +++++++++++++++++++-- + drivers/gpu/drm/drm_modes.c | 19 +- + drivers/gpu/drm/panel/panel-orisetech-otm8009a.c | 12 +- + drivers/gpu/drm/panel/panel-raydium-rm68200.c | 12 +- + drivers/gpu/drm/stm/drv.c | 46 +- + drivers/gpu/drm/stm/dw_mipi_dsi-stm.c | 53 ++ + drivers/gpu/drm/stm/ltdc.c | 191 +++-- + drivers/gpu/drm/stm/ltdc.h | 6 + + include/uapi/drm/drm_mode.h | 6 + + 11 files changed, 1060 insertions(+), 139 deletions(-) + +diff --git a/Documentation/devicetree/bindings/display/bridge/sii902x.txt b/Documentation/devicetree/bindings/display/bridge/sii902x.txt +index 72d2dc6..00e9e88 100644 +--- a/Documentation/devicetree/bindings/display/bridge/sii902x.txt ++++ b/Documentation/devicetree/bindings/display/bridge/sii902x.txt +@@ -13,6 +13,8 @@ Optional subnodes: + - video input: this subnode can contain a video input port node + to connect the bridge to a display controller output (See this + documentation [1]). ++ - audio input: this subnode can contain an audio input port node ++ to connect the bridge to an audio controller output. + + [1]: Documentation/devicetree/bindings/media/video-interfaces.txt + +@@ -31,5 +33,12 @@ Example: + remote-endpoint = <&dc_out>; + }; + }; ++ ++ port@1 { ++ reg = <1>; ++ codec_endpoint: endpoint { ++ remote-endpoint = <&i2s0_cpu_endpoint>; ++ }; ++ }; + }; + }; +diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig +index bf6cad6..fe91c20 100644 +--- a/drivers/gpu/drm/bridge/Kconfig ++++ b/drivers/gpu/drm/bridge/Kconfig +@@ -95,6 +95,7 @@ config DRM_SII902X + depends on OF + select DRM_KMS_HELPER + select REGMAP_I2C ++ select SND_SOC_HDMI_CODEC if SND_SOC + ---help--- + Silicon Image sii902x bridge chip driver. + +diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c +index e59a135..512eb03 100644 +--- a/drivers/gpu/drm/bridge/sii902x.c ++++ b/drivers/gpu/drm/bridge/sii902x.c +@@ -1,4 +1,6 @@ + /* ++ * Copyright (C) 2018 Renesas Electronics ++ * + * Copyright (C) 2016 Atmel + * Bo Shen + * +@@ -20,16 +22,23 @@ + * GNU General Public License for more details. + */ + ++#include + #include ++#include + #include + #include ++#include ++#include + #include ++#include + + #include + #include + #include + #include + ++#include ++ + #define SII902X_TPI_VIDEO_DATA 0x0 + + #define SII902X_TPI_PIXEL_REPETITION 0x8 +@@ -71,23 +80,229 @@ + #define SII902X_AVI_POWER_STATE_MSK GENMASK(1, 0) + #define SII902X_AVI_POWER_STATE_D(l) ((l) & SII902X_AVI_POWER_STATE_MSK) + ++#define SII902X_I2S_MAP 0x1f ++#define SII902X_I2S_MAP_SWITCH BIT(7) ++#define SII902X_I2S_MAP_SD_MSK GENMASK(5, 4) ++#define SII902X_I2S_MAP_SD(v) \ ++ FIELD_PREP(SII902X_I2S_MAP_SD_MSK, v) ++#define SII902X_I2S_MAP_DS BIT(3) ++#define SII902X_I2S_MAP_SWAP BIT(2) ++#define SII902X_I2S_MAP_FIFO_MSK GENMASK(1, 0) ++#define SII902X_I2S_MAP_FIFO(v) \ ++ FIELD_PREP(SII902X_I2S_MAP_FIFO_MSK, v) ++ ++#define SII902X_I2S_CONF 0x20 ++#define SII902X_I2S_CONF_SCK_STROBING BIT(7) ++#define SII902X_I2S_CONF_MCLK_RATIO_MSK GENMASK(6, 4) ++#define SII902X_I2S_CONF_WS_STROBING BIT(3) ++#define SII902X_I2S_CONF_JUSTIFY BIT(2) ++#define SII902X_I2S_CONF_LSB_DIR BIT(1) ++#define SII902X_I2S_CONF_NO_OFFSET BIT(0) ++ ++#define SII902X_I2S_CS0 0x21 ++ ++#define SII902X_I2S_CS1 0x22 ++ ++#define SII902X_I2S_CS2 0x23 ++#define SII902X_I2S_CS2_SRC_MSK GENMASK(3, 0) ++#define SII902X_I2S_CS2_CHAN_MSK GENMASK(7, 4) ++ ++#define SII902X_I2S_CS3 0x24 ++#define SII902X_I2S_CS3_FS_MSK GENMASK(3, 0) ++#define SII902X_I2S_CS3_FS(v) \ ++ FIELD_PREP(SII902X_I2S_CS3_FS_MSK, v) ++#define SII902X_I2S_CS3_ACC_MSK GENMASK(7, 4) ++ ++#define SII902X_I2S_CS4 0x25 ++#define SII902X_I2S_CS4_WL_MSK GENMASK(3, 0) ++#define SII902X_I2S_CS4_WL(v) \ ++ FIELD_PREP(SII902X_I2S_CS4_WL_MSK, v) ++ ++#define SII902X_AIF 0x26 ++#define SII902X_AIF_FMT_MSK GENMASK(7, 6) ++#define SII902X_AIF_FMT(v) \ ++ FIELD_PREP(SII902X_AIF_FMT_MSK, v) ++#define SII902X_AIF_LAYOUT BIT(5) ++#define SII902X_AIF_MUTE BIT(4) ++#define SII902X_AIF_CODING_MSK GENMASK(3, 0) ++#define SII902X_AIF_CODING(v) \ ++ FIELD_PREP(SII902X_AIF_CODING_MSK, v) ++ ++#define SII902X_I2S_AUD_FMT 0x27 ++#define SII902X_I2S_AUD_FMT_SZ_MSK GENMASK(7, 6) ++#define SII902X_I2S_AUD_FMT_SZ(v) \ ++ FIELD_PREP(SII902X_I2S_AUD_FMT_SZ_MSK, v) ++#define SII902X_I2S_AUD_FMT_FS_MSK GENMASK(5, 3) ++#define SII902X_I2S_AUD_FMT_FS(v) \ ++ FIELD_PREP(SII902X_I2S_AUD_FMT_FS_MSK, v) ++#define SII902X_I2S_AUD_FMT_FS_HBR BIT(2) ++ + #define SII902X_INT_ENABLE 0x3c + #define SII902X_INT_STATUS 0x3d + #define SII902X_HOTPLUG_EVENT BIT(0) + #define SII902X_PLUGGED_STATUS BIT(2) + ++#define SII902X_PLL_R1 0x82 ++#define SII902X_PLL_R1_TCLKSEL_MSK GENMASK(6, 5) ++#define SII902X_PLL_R1_TCLKSEL(v) \ ++ FIELD_PREP(SII902X_PLL_R1_TCLKSEL_MSK, v) ++#define SII902X_PLL_R2 0x83 ++#define SII902X_PLL_R2_CLKMUTLCTL_MSK GENMASK(5, 4) ++#define SII902X_PLL_R2_CLKMUTLCTL(v) \ ++ FIELD_PREP(SII902X_PLL_R2_CLKMUTLCTL_MSK, v) ++#define SII902X_PLL_R3 0x84 ++#define SII902X_PLL_R3_ACLKCNT_MSK GENMASK(5, 4) ++#define SII902X_PLL_R3_ACLKCNT(v) \ ++ FIELD_PREP(SII902X_PLL_R3_ACLKCNT_MSK, v) ++#define SII902X_PLL_R3_HLFCLKEN BIT(1) ++ ++#define SII902X_INDEXED_REG_PAGE 0xbc ++#define SII902X_INDEXED_REG_IDX 0xbd ++#define SII902X_INDEXED_REG_ACCESS 0xbe ++ ++#define SII902X_OTHER_IF 0xbf ++#define SII902X_OTHER_IF_SEL_MSK GENMASK(2, 0) ++#define SII902X_OTHER_IF_SEL(v) \ ++ FIELD_PREP(SII902X_OTHER_IF_SEL_MSK, v) ++#define SII902X_OTHER_IF_REPEAT BIT(6) ++#define SII902X_OTHER_IF_ENABLE BIT(7) ++ + #define SII902X_REG_TPI_RQB 0xc7 + + #define SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS 500 + ++#define SII902X_IF_AUDIO 2 ++ ++/* CEC device */ ++#define SII902X_CEC_I2C_ADDR 0x30 ++ ++#define SII902X_CEC_SETUP 0x8e ++ ++enum sii902x_i2s_map_sd { ++ SII902X_I2S_MAP_SD0, ++ SII902X_I2S_MAP_SD1, ++ SII902X_I2S_MAP_SD2, ++ SII902X_I2S_MAP_SD3, ++}; ++ ++enum sii902x_i2s_map_fifo { ++ SII902X_I2S_MAP_FIFO0, ++ SII902X_I2S_MAP_FIFO1, ++ SII902X_I2S_MAP_FIFO2, ++ SII902X_I2S_MAP_FIFO3, ++}; ++ ++enum sii902x_aif_format { ++ SII902X_AIF_FORMAT_SPDIF = 1, ++ SII902X_AIF_FORMAT_I2S, ++ SII902X_AIF_FORMAT_DSD, ++}; ++ ++enum sii902x_aif_coding { ++ SII902X_AIF_CODING_STREAM_HEADER, ++ SII902X_AIF_CODING_PCM, ++ SII902X_AIF_CODING_AC3, ++ SII902X_AIF_CODING_MPEG1, ++ SII902X_AIF_CODING_MP3, ++ SII902X_AIF_CODING_MPEG2, ++ SII902X_AIF_CODING_AAC, ++ SII902X_AIF_CODING_DTS, ++ SII902X_AIF_CODING_ATRAC, ++}; ++ ++enum sii902x_sample_rate { ++ SII902X_SAMPLE_RATE_32000 = 1, ++ SII902X_SAMPLE_RATE_44100, ++ SII902X_SAMPLE_RATE_48000, ++ SII902X_SAMPLE_RATE_88200, ++ SII902X_SAMPLE_RATE_96000, ++ SII902X_SAMPLE_RATE_176400, ++ SII902X_SAMPLE_RATE_192000, ++}; ++ ++enum sii902x_sample_width { ++ SII902X_SAMPLE_RATE_SIZE_16 = 1, ++ SII902X_SAMPLE_RATE_SIZE_20, ++ SII902X_SAMPLE_RATE_SIZE_24, ++}; ++ ++struct sii902x_audio_params { ++ unsigned int aes_size; ++ unsigned int aes_rate; ++}; ++ + struct sii902x { + struct i2c_client *i2c; + struct regmap *regmap; + struct drm_bridge bridge; + struct drm_connector connector; + struct gpio_desc *reset_gpio; ++ struct i2c_mux_core *i2cmux; ++ struct regulator_bulk_data supplies[2]; ++ struct platform_device *audio_pdev; ++ struct sii902x_audio_params audio; ++ struct edid *edid; + }; + ++static int sii902x_read_unlocked(struct i2c_client *i2c, u8 reg, u8 *val) ++{ ++ struct i2c_msg xfer[] = { ++ { ++ .addr = i2c->addr, ++ .flags = 0, ++ .len = 1, ++ .buf = ®, ++ }, { ++ .addr = i2c->addr, ++ .flags = I2C_M_RD, ++ .len = 1, ++ .buf = val, ++ } ++ }; ++ unsigned char xfers = ARRAY_SIZE(xfer); ++ int ret, retries = 5; ++ ++ do { ++ ret = __i2c_transfer(i2c->adapter, xfer, xfers); ++ if (ret < 0) ++ return ret; ++ } while (ret != xfers && --retries); ++ return ret == xfers ? 0 : -1; ++} ++ ++static int sii902x_write_unlocked(struct i2c_client *i2c, u8 reg, u8 val) ++{ ++ u8 data[2] = {reg, val}; ++ struct i2c_msg xfer = { ++ .addr = i2c->addr, ++ .flags = 0, ++ .len = sizeof(data), ++ .buf = data, ++ }; ++ int ret, retries = 5; ++ ++ do { ++ ret = __i2c_transfer(i2c->adapter, &xfer, 1); ++ if (ret < 0) ++ return ret; ++ } while (!ret && --retries); ++ return !ret ? -1 : 0; ++} ++ ++static int sii902x_update_bits_unlocked(struct i2c_client *i2c, u8 reg, u8 mask, ++ u8 val) ++{ ++ int ret; ++ u8 status; ++ ++ ret = sii902x_read_unlocked(i2c, reg, &status); ++ if (ret) ++ return ret; ++ status &= ~mask; ++ status |= val & mask; ++ return sii902x_write_unlocked(i2c, reg, status); ++} ++ + static inline struct sii902x *bridge_to_sii902x(struct drm_bridge *bridge) + { + return container_of(bridge, struct sii902x, bridge); +@@ -135,45 +350,18 @@ static const struct drm_connector_funcs sii902x_connector_funcs = { + static int sii902x_get_modes(struct drm_connector *connector) + { + struct sii902x *sii902x = connector_to_sii902x(connector); +- struct regmap *regmap = sii902x->regmap; + u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; +- struct device *dev = &sii902x->i2c->dev; +- unsigned long timeout; +- unsigned int retries; +- unsigned int status; + struct edid *edid; +- int num = 0; +- int ret; +- +- ret = regmap_update_bits(regmap, SII902X_SYS_CTRL_DATA, +- SII902X_SYS_CTRL_DDC_BUS_REQ, +- SII902X_SYS_CTRL_DDC_BUS_REQ); +- if (ret) +- return ret; +- +- timeout = jiffies + +- msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS); +- do { +- ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA, &status); +- if (ret) +- return ret; +- } while (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD) && +- time_before(jiffies, timeout)); ++ bool hdmi_mode = false; ++ int num = 0, ret; + +- if (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD)) { +- dev_err(dev, "failed to acquire the i2c bus\n"); +- return -ETIMEDOUT; +- } +- +- ret = regmap_write(regmap, SII902X_SYS_CTRL_DATA, status); +- if (ret) +- return ret; +- +- edid = drm_get_edid(connector, sii902x->i2c->adapter); ++ edid = drm_get_edid(connector, sii902x->i2cmux->adapter[0]); + drm_connector_update_edid_property(connector, edid); + if (edid) { + num = drm_add_edid_modes(connector, edid); +- kfree(edid); ++ hdmi_mode = drm_detect_hdmi_monitor(edid); ++ kfree(sii902x->edid); ++ sii902x->edid = edid; + } + + ret = drm_display_info_set_bus_formats(&connector->display_info, +@@ -181,41 +369,10 @@ static int sii902x_get_modes(struct drm_connector *connector) + if (ret) + return ret; + +- /* +- * Sometimes the I2C bus can stall after failure to use the +- * EDID channel. Retry a few times to see if things clear +- * up, else continue anyway. +- */ +- retries = 5; +- do { +- ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA, +- &status); +- retries--; +- } while (ret && retries); +- if (ret) +- dev_err(dev, "failed to read status (%d)\n", ret); +- +- ret = regmap_update_bits(regmap, SII902X_SYS_CTRL_DATA, +- SII902X_SYS_CTRL_DDC_BUS_REQ | +- SII902X_SYS_CTRL_DDC_BUS_GRTD, 0); +- if (ret) +- return ret; +- +- timeout = jiffies + +- msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS); +- do { +- ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA, &status); +- if (ret) +- return ret; +- } while (status & (SII902X_SYS_CTRL_DDC_BUS_REQ | +- SII902X_SYS_CTRL_DDC_BUS_GRTD) && +- time_before(jiffies, timeout)); +- +- if (status & (SII902X_SYS_CTRL_DDC_BUS_REQ | +- SII902X_SYS_CTRL_DDC_BUS_GRTD)) { +- dev_err(dev, "failed to release the i2c bus\n"); +- return -ETIMEDOUT; +- } ++ if (hdmi_mode) ++ regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA, ++ SII902X_SYS_CTRL_OUTPUT_MODE, ++ SII902X_SYS_CTRL_OUTPUT_HDMI); + + return num; + } +@@ -240,17 +397,29 @@ static void sii902x_bridge_disable(struct drm_bridge *bridge) + regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA, + SII902X_SYS_CTRL_PWR_DWN, + SII902X_SYS_CTRL_PWR_DWN); ++ pinctrl_pm_select_sleep_state(&sii902x->i2c->dev); + } + + static void sii902x_bridge_enable(struct drm_bridge *bridge) + { + struct sii902x *sii902x = bridge_to_sii902x(bridge); ++ bool hdmi_mode; + ++ pinctrl_pm_select_default_state(&sii902x->i2c->dev); + regmap_update_bits(sii902x->regmap, SII902X_PWR_STATE_CTRL, + SII902X_AVI_POWER_STATE_MSK, + SII902X_AVI_POWER_STATE_D(0)); + regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA, + SII902X_SYS_CTRL_PWR_DWN, 0); ++ ++ if(sii902x->edid) { ++ hdmi_mode = drm_detect_hdmi_monitor(sii902x->edid); ++ if (hdmi_mode) ++ regmap_update_bits(sii902x->regmap, ++ SII902X_SYS_CTRL_DATA, ++ SII902X_SYS_CTRL_OUTPUT_MODE, ++ SII902X_SYS_CTRL_OUTPUT_HDMI); ++ } + } + + static void sii902x_bridge_mode_set(struct drm_bridge *bridge, +@@ -329,6 +498,267 @@ static int sii902x_bridge_attach(struct drm_bridge *bridge) + return 0; + } + ++static int sii902x_audio_infoframe_config(struct sii902x *sii902x, ++ struct hdmi_codec_params *params) ++{ ++ u8 buf[HDMI_INFOFRAME_SIZE(AUDIO)]; ++ int ret; ++ ++ ret = hdmi_audio_infoframe_init(¶ms->cea); ++ if (ret) { ++ DRM_ERROR("Failed to init audio infoframe\n"); ++ return ret; ++ } ++ ++ ret = hdmi_audio_infoframe_pack(¶ms->cea, buf, sizeof(buf)); ++ if (ret < 0) { ++ DRM_ERROR("failed to pack audio infoframe: %d\n", ret); ++ return ret; ++ } ++ ++ regmap_update_bits(sii902x->regmap, ++ SII902X_OTHER_IF, ++ SII902X_OTHER_IF_SEL_MSK | ++ SII902X_OTHER_IF_REPEAT | ++ SII902X_OTHER_IF_ENABLE, ++ SII902X_OTHER_IF_SEL(SII902X_IF_AUDIO) | ++ SII902X_OTHER_IF_REPEAT | ++ SII902X_OTHER_IF_ENABLE); ++ ++ return regmap_bulk_write(sii902x->regmap, SII902X_OTHER_IF + 1, buf, ++ HDMI_INFOFRAME_SIZE(AUDIO) + ++ HDMI_INFOFRAME_HEADER_SIZE - 1); ++} ++ ++static int sii902x_audio_iec60958_config(struct sii902x *sii902x) ++{ ++ /* Bytes 0,1,2 are let to default setting. Configure bytes 3&4 */ ++ regmap_update_bits(sii902x->regmap, ++ SII902X_I2S_CS3, ++ SII902X_I2S_CS3_FS_MSK, ++ SII902X_I2S_CS3_FS(sii902x->audio.aes_rate)); ++ ++ return regmap_update_bits(sii902x->regmap, ++ SII902X_I2S_CS4, ++ SII902X_I2S_CS4_WL_MSK, ++ SII902X_I2S_CS4_WL(sii902x->audio.aes_size)); ++} ++ ++static int sii902x_i2s_configure(struct sii902x *sii902x, ++ struct hdmi_codec_params *params, ++ struct hdmi_codec_daifmt *fmt) ++{ ++ unsigned int rate, size, val, mask; ++ ++ /* Configure audio interface */ ++ regmap_update_bits(sii902x->regmap, SII902X_AIF, ++ SII902X_AIF_FMT_MSK | ++ SII902X_AIF_LAYOUT | ++ SII902X_AIF_MUTE | ++ SII902X_AIF_CODING_MSK, ++ SII902X_AIF_FMT(SII902X_AIF_FORMAT_I2S) | ++ SII902X_AIF_MUTE | ++ SII902X_AIF_CODING(SII902X_AIF_CODING_PCM)); ++ ++ switch (fmt->fmt) { ++ case HDMI_I2S: ++ val = SII902X_I2S_CONF_SCK_STROBING; ++ break; ++ case HDMI_LEFT_J: ++ val = SII902X_I2S_CONF_SCK_STROBING | ++ SII902X_I2S_CONF_WS_STROBING | ++ SII902X_I2S_CONF_NO_OFFSET; ++ break; ++ case HDMI_RIGHT_J: ++ val = SII902X_I2S_CONF_SCK_STROBING | ++ SII902X_I2S_CONF_WS_STROBING | ++ SII902X_I2S_CONF_NO_OFFSET | ++ SII902X_I2S_CONF_JUSTIFY; ++ break; ++ default: ++ DRM_ERROR("Unknown protocol %#x\n", fmt->fmt); ++ return -EINVAL; ++ } ++ mask = SII902X_I2S_CONF_NO_OFFSET | SII902X_I2S_CONF_WS_STROBING | ++ SII902X_I2S_CONF_JUSTIFY | SII902X_I2S_CONF_LSB_DIR | ++ SII902X_I2S_CONF_SCK_STROBING; ++ ++ if (fmt->frame_clk_inv) ++ val ^= SII902X_I2S_CONF_WS_STROBING; ++ ++ if (fmt->bit_clk_inv) ++ val ^= SII902X_I2S_CONF_SCK_STROBING; ++ ++ /* Configure i2s interface */ ++ regmap_update_bits(sii902x->regmap, ++ SII902X_I2S_CONF, mask, val); ++ ++ /* ++ * Configure i2s interface mapping ++ * Assume that only SD0 is used and connected to FIFO0 ++ */ ++ regmap_update_bits(sii902x->regmap, ++ SII902X_I2S_MAP, ++ SII902X_I2S_MAP_SWITCH | ++ SII902X_I2S_MAP_SD_MSK | SII902X_I2S_MAP_FIFO_MSK, ++ SII902X_I2S_MAP_SWITCH | ++ SII902X_I2S_MAP_SD0 | SII902X_I2S_MAP_FIFO0); ++ ++ switch (params->sample_rate) { ++ case 32000: ++ rate = SII902X_SAMPLE_RATE_32000; ++ sii902x->audio.aes_rate = IEC958_AES3_CON_FS_32000; ++ break; ++ case 44100: ++ rate = SII902X_SAMPLE_RATE_44100; ++ sii902x->audio.aes_rate = IEC958_AES3_CON_FS_44100; ++ break; ++ case 48000: ++ rate = SII902X_SAMPLE_RATE_48000; ++ sii902x->audio.aes_rate = IEC958_AES3_CON_FS_48000; ++ break; ++ case 88200: ++ rate = SII902X_SAMPLE_RATE_88200; ++ sii902x->audio.aes_rate = IEC958_AES3_CON_FS_88200; ++ break; ++ case 96000: ++ rate = SII902X_SAMPLE_RATE_96000; ++ sii902x->audio.aes_rate = IEC958_AES3_CON_FS_96000; ++ break; ++ case 176400: ++ rate = SII902X_SAMPLE_RATE_176400; ++ sii902x->audio.aes_rate = IEC958_AES3_CON_FS_176400; ++ break; ++ case 192000: ++ rate = SII902X_SAMPLE_RATE_192000; ++ sii902x->audio.aes_rate = IEC958_AES3_CON_FS_192000; ++ break; ++ default: ++ DRM_ERROR("Unknown sampling rate %d\n", params->sample_rate); ++ return -EINVAL; ++ } ++ ++ switch (params->sample_width) { ++ case 16: ++ size = SII902X_SAMPLE_RATE_SIZE_16; ++ sii902x->audio.aes_size = IEC958_AES4_CON_WORDLEN_20_16; ++ break; ++ case 20: ++ size = SII902X_SAMPLE_RATE_SIZE_20; ++ sii902x->audio.aes_size = IEC958_AES4_CON_WORDLEN_24_20; ++ break; ++ case 24: ++ case 32: ++ size = SII902X_SAMPLE_RATE_SIZE_24; ++ sii902x->audio.aes_size = IEC958_AES4_CON_WORDLEN_24_20 | ++ IEC958_AES4_CON_MAX_WORDLEN_24; ++ break; ++ default: ++ DRM_ERROR("Unknown sample width %d\n", params->sample_width); ++ return -EINVAL; ++ } ++ ++ /* Configure i2s interface rate and input/output word length */ ++ regmap_update_bits(sii902x->regmap, ++ SII902X_INDEXED_REG_PAGE, 0xff, 0x2); ++ regmap_update_bits(sii902x->regmap, ++ SII902X_INDEXED_REG_IDX, 0xff, 0x24); ++ regmap_update_bits(sii902x->regmap, ++ SII902X_INDEXED_REG_ACCESS, 0x0f, ++ sii902x->audio.aes_size); ++ ++ return regmap_update_bits(sii902x->regmap, ++ SII902X_I2S_AUD_FMT, ++ SII902X_I2S_AUD_FMT_FS_MSK | ++ SII902X_I2S_AUD_FMT_SZ_MSK, ++ SII902X_I2S_AUD_FMT_FS(rate) | ++ SII902X_I2S_AUD_FMT_SZ(size)); ++} ++ ++static int sii902x_audio_hw_params(struct device *dev, void *data, ++ struct hdmi_codec_daifmt *fmt, ++ struct hdmi_codec_params *params) ++{ ++ struct sii902x *sii902x = dev_get_drvdata(dev); ++ int ret; ++ ++ if (fmt->bit_clk_master || fmt->frame_clk_master) { ++ DRM_ERROR("Master mode not supported\n"); ++ return -EINVAL; ++ } ++ ++ if (fmt->fmt == HDMI_I2S || fmt->fmt == HDMI_RIGHT_J || ++ fmt->fmt == HDMI_LEFT_J) { ++ /* Configure i2s interface */ ++ ret = sii902x_i2s_configure(sii902x, params, fmt); ++ if (ret) ++ return ret; ++ ++ /* Configure iec958 channel status */ ++ ret = sii902x_audio_iec60958_config(sii902x); ++ if (ret) ++ return ret; ++ } else { ++ DRM_ERROR("Unsupported format 0x%x\n", fmt->fmt); ++ return -EINVAL; ++ } ++ ++ /* Configure audio infoframes */ ++ return sii902x_audio_infoframe_config(sii902x, params); ++} ++ ++static int sii902x_audio_digital_mute(struct device *dev, ++ void *data, bool enable) ++{ ++ struct sii902x *sii902x = dev_get_drvdata(dev); ++ int ret; ++ ++ DRM_DEBUG("%s audio\n", enable ? "mute" : "unmute"); ++ ++ if (enable) ++ ret = regmap_update_bits(sii902x->regmap, SII902X_AIF, ++ SII902X_AIF_MUTE, SII902X_AIF_MUTE); ++ else ++ ret = regmap_update_bits(sii902x->regmap, SII902X_AIF, ++ SII902X_AIF_MUTE, 0); ++ ++ return ret; ++} ++ ++static int sii902x_audio_get_eld(struct device *dev, void *data, ++ u8 *buf, size_t len) ++{ ++ struct sii902x *sii902x = dev_get_drvdata(dev); ++ struct drm_connector *connector = &sii902x->connector; ++ ++ if (!sii902x->edid) ++ return -ENODEV; ++ ++ memcpy(buf, connector->eld, min(sizeof(connector->eld), len)); ++ ++ return 0; ++} ++ ++static void sii902x_audio_shutdown(struct device *dev, void *data) ++{} ++ ++static int sii902x_audio_get_dai_id(struct snd_soc_component *component, ++ struct device_node *endpoint) ++{ ++ struct of_endpoint of_ep; ++ int ret; ++ ++ ret = of_graph_parse_endpoint(endpoint, &of_ep); ++ if (ret < 0) ++ return ret; ++ ++ /* HDMI sound should be located at reg = <1> */ ++ if (of_ep.port == 1) ++ return 0; ++ ++ return -EINVAL; ++} ++ + static const struct drm_bridge_funcs sii902x_bridge_funcs = { + .attach = sii902x_bridge_attach, + .mode_set = sii902x_bridge_mode_set, +@@ -348,10 +778,39 @@ static const struct regmap_access_table sii902x_volatile_table = { + static const struct regmap_config sii902x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, ++ /* map up to infoframe data registers. 0xbf-0xde */ ++ .max_register = 0xde, + .volatile_table = &sii902x_volatile_table, + .cache_type = REGCACHE_NONE, + }; + ++static const struct hdmi_codec_ops sii902x_codec_ops = { ++ .hw_params = sii902x_audio_hw_params, ++ .audio_shutdown = sii902x_audio_shutdown, ++ .get_dai_id = sii902x_audio_get_dai_id, ++ .digital_mute = sii902x_audio_digital_mute, ++ .get_eld = sii902x_audio_get_eld, ++}; ++ ++static int sii902x_register_audio_driver(struct device *dev, ++ struct sii902x *sii902x) ++{ ++ struct hdmi_codec_pdata codec_data = { ++ .ops = &sii902x_codec_ops, ++ .max_i2s_channels = 2, ++ .i2s = 1, ++ }; ++ ++ sii902x->audio_pdev = platform_device_register_data( ++ dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO, ++ &codec_data, sizeof(codec_data)); ++ ++ if (IS_ERR(sii902x->audio_pdev)) ++ return PTR_ERR(sii902x->audio_pdev); ++ ++ return 0; ++} ++ + static irqreturn_t sii902x_interrupt(int irq, void *data) + { + struct sii902x *sii902x = data; +@@ -366,15 +825,134 @@ static irqreturn_t sii902x_interrupt(int irq, void *data) + return IRQ_HANDLED; + } + ++/* ++ * The purpose of sii902x_i2c_bypass_select is to enable the pass through ++ * mode of the HDMI transmitter. Do not use regmap from within this function, ++ * only use sii902x_*_unlocked functions to read/modify/write registers. ++ * We are holding the parent adapter lock here, keep this in mind before ++ * adding more i2c transactions. ++ */ ++static int sii902x_i2c_bypass_select(struct i2c_mux_core *mux, u32 chan_id) ++{ ++ struct sii902x *sii902x = i2c_mux_priv(mux); ++ struct device *dev = &sii902x->i2c->dev; ++ unsigned long timeout; ++ u8 status; ++ int ret; ++ ++ ret = sii902x_update_bits_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA, ++ SII902X_SYS_CTRL_DDC_BUS_REQ, ++ SII902X_SYS_CTRL_DDC_BUS_REQ); ++ ++ timeout = jiffies + ++ msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS); ++ do { ++ ret = sii902x_read_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA, ++ &status); ++ if (ret) ++ return ret; ++ } while (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD) && ++ time_before(jiffies, timeout)); ++ ++ if (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD)) { ++ dev_err(dev, "Failed to acquire the i2c bus\n"); ++ return -ETIMEDOUT; ++ } ++ ++ ret = sii902x_write_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA, ++ status); ++ if (ret) ++ return ret; ++ return 0; ++} ++ ++/* ++ * The purpose of sii902x_i2c_bypass_deselect is to disable the pass through ++ * mode of the HDMI transmitter. Do not use regmap from within this function, ++ * only use sii902x_*_unlocked functions to read/modify/write registers. ++ * We are holding the parent adapter lock here, keep this in mind before ++ * adding more i2c transactions. ++ */ ++static int sii902x_i2c_bypass_deselect(struct i2c_mux_core *mux, u32 chan_id) ++{ ++ struct sii902x *sii902x = i2c_mux_priv(mux); ++ struct device *dev = &sii902x->i2c->dev; ++ unsigned long timeout; ++ unsigned int retries; ++ u8 status; ++ int ret; ++ ++ /* ++ * When the HDMI transmitter is in pass through mode, we need an ++ * (undocumented) additional delay between STOP and START conditions ++ * to guarantee the bus won't get stuck. ++ */ ++ udelay(30); ++ ++ /* ++ * Sometimes the I2C bus can stall after failure to use the ++ * EDID channel. Retry a few times to see if things clear ++ * up, else continue anyway. ++ */ ++ retries = 5; ++ do { ++ ret = sii902x_read_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA, ++ &status); ++ retries--; ++ } while (ret && retries); ++ if (ret) { ++ dev_err(dev, "failed to read status (%d)\n", ret); ++ return ret; ++ } ++ ++ ret = sii902x_update_bits_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA, ++ SII902X_SYS_CTRL_DDC_BUS_REQ | ++ SII902X_SYS_CTRL_DDC_BUS_GRTD, 0); ++ if (ret) ++ return ret; ++ ++ timeout = jiffies + ++ msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS); ++ do { ++ ret = sii902x_read_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA, ++ &status); ++ if (ret) ++ return ret; ++ } while (status & (SII902X_SYS_CTRL_DDC_BUS_REQ | ++ SII902X_SYS_CTRL_DDC_BUS_GRTD) && ++ time_before(jiffies, timeout)); ++ ++ if (status & (SII902X_SYS_CTRL_DDC_BUS_REQ | ++ SII902X_SYS_CTRL_DDC_BUS_GRTD)) { ++ dev_err(dev, "failed to release the i2c bus\n"); ++ return -ETIMEDOUT; ++ } ++ ++ return 0; ++} ++ + static int sii902x_probe(struct i2c_client *client, + const struct i2c_device_id *id) + { + struct device *dev = &client->dev; + unsigned int status = 0; + struct sii902x *sii902x; ++ unsigned char data[2] = { SII902X_CEC_SETUP, 0}; ++ struct i2c_msg msg = { ++ .addr = SII902X_CEC_I2C_ADDR << 1, ++ .flags = 0, ++ .len = 2, ++ .buf = data, ++ }; + u8 chipid[4]; + int ret; + ++ ret = i2c_check_functionality(client->adapter, I2C_FUNC_I2C); ++ if (!ret) { ++ dev_err(dev, "I2C adapter not suitable\n"); ++ return -EIO; ++ } ++ + sii902x = devm_kzalloc(dev, sizeof(*sii902x), GFP_KERNEL); + if (!sii902x) + return -ENOMEM; +@@ -392,39 +970,67 @@ static int sii902x_probe(struct i2c_client *client, + return PTR_ERR(sii902x->reset_gpio); + } + ++ sii902x->supplies[0].supply = "iovcc"; ++ sii902x->supplies[1].supply = "cvcc12"; ++ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(sii902x->supplies), ++ sii902x->supplies); ++ if (ret) { ++ if(ret != -EPROBE_DEFER) ++ dev_err(dev, "regulator_bulk_get failed\n"); ++ return ret; ++ } ++ ++ ret = regulator_bulk_enable(ARRAY_SIZE(sii902x->supplies), ++ sii902x->supplies); ++ if (ret) { ++ dev_err(dev, "regulator_bulk_enable failed\n"); ++ return ret; ++ } ++ ++ pinctrl_pm_select_sleep_state(&sii902x->i2c->dev); + sii902x_reset(sii902x); + + ret = regmap_write(sii902x->regmap, SII902X_REG_TPI_RQB, 0x0); + if (ret) +- return ret; ++ goto err_disable_regulator; + + ret = regmap_bulk_read(sii902x->regmap, SII902X_REG_CHIPID(0), + &chipid, 4); + if (ret) { + dev_err(dev, "regmap_read failed %d\n", ret); +- return ret; ++ goto err_disable_regulator; + } + + if (chipid[0] != 0xb0) { + dev_err(dev, "Invalid chipid: %02x (expecting 0xb0)\n", + chipid[0]); +- return -EINVAL; ++ ret = -EINVAL; ++ goto err_disable_regulator; + } + ++ /* ++ * By default, CEC must be disabled to allow other CEC devives ++ * to bypass the bridge. ++ */ ++ ret = i2c_transfer(client->adapter, &msg, 1); ++ if (ret < 0) ++ dev_warn(&client->dev, "Failed to disable CEC device!\n"); ++ + /* Clear all pending interrupts */ + regmap_read(sii902x->regmap, SII902X_INT_STATUS, &status); + regmap_write(sii902x->regmap, SII902X_INT_STATUS, status); + + if (client->irq > 0) { +- regmap_write(sii902x->regmap, SII902X_INT_ENABLE, +- SII902X_HOTPLUG_EVENT); ++ regmap_update_bits(sii902x->regmap, SII902X_INT_ENABLE, ++ SII902X_HOTPLUG_EVENT, ++ SII902X_HOTPLUG_EVENT); + + ret = devm_request_threaded_irq(dev, client->irq, NULL, + sii902x_interrupt, + IRQF_ONESHOT, dev_name(dev), + sii902x); + if (ret) +- return ret; ++ goto err_disable_regulator; + } + + sii902x->bridge.funcs = &sii902x_bridge_funcs; +@@ -433,7 +1039,31 @@ static int sii902x_probe(struct i2c_client *client, + + i2c_set_clientdata(client, sii902x); + ++ sii902x->i2cmux = i2c_mux_alloc(client->adapter, dev, ++ 1, 0, I2C_MUX_GATE, ++ sii902x_i2c_bypass_select, ++ sii902x_i2c_bypass_deselect); ++ if (!sii902x->i2cmux) { ++ dev_err(dev, "failed to allocate I2C mux\n"); ++ return -ENOMEM; ++ } ++ ++ sii902x->i2cmux->priv = sii902x; ++ ret = i2c_mux_add_adapter(sii902x->i2cmux, 0, 0, 0); ++ if (ret) { ++ dev_err(dev, "Couldn't add i2c mux adapter\n"); ++ return ret; ++ } ++ ++ sii902x_register_audio_driver(dev, sii902x); ++ + return 0; ++ ++err_disable_regulator: ++ regulator_bulk_disable(ARRAY_SIZE(sii902x->supplies), ++ sii902x->supplies); ++ ++ return ret; + } + + static int sii902x_remove(struct i2c_client *client) +@@ -441,11 +1071,76 @@ static int sii902x_remove(struct i2c_client *client) + { + struct sii902x *sii902x = i2c_get_clientdata(client); + ++ if (sii902x->i2cmux) ++ i2c_mux_del_adapters(sii902x->i2cmux); ++ + drm_bridge_remove(&sii902x->bridge); + ++ regulator_bulk_disable(ARRAY_SIZE(sii902x->supplies), ++ sii902x->supplies); ++ ++ return 0; ++} ++ ++static int sii902x_pm_suspend(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct sii902x *sii902x = i2c_get_clientdata(client); ++ ++ DRM_DEBUG_DRIVER("\n"); ++ ++ if (sii902x->reset_gpio) ++ gpiod_set_value(sii902x->reset_gpio, 1); ++ ++ regulator_bulk_disable(ARRAY_SIZE(sii902x->supplies), ++ sii902x->supplies); ++ + return 0; + } + ++static int sii902x_pm_resume(struct device *dev) ++{ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct sii902x *sii902x = i2c_get_clientdata(client); ++ unsigned char data[2] = { SII902X_CEC_SETUP, 0}; ++ struct i2c_msg msg = { ++ .addr = SII902X_CEC_I2C_ADDR << 1, ++ .flags = 0, ++ .len = 2, ++ .buf = data, ++ }; ++ int ret; ++ ++ DRM_DEBUG_DRIVER("\n"); ++ ++ ret = regulator_bulk_enable(ARRAY_SIZE(sii902x->supplies), ++ sii902x->supplies); ++ if (ret) { ++ DRM_ERROR("regulator_bulk_enable failed\n"); ++ return ret; ++ } ++ ++ if (sii902x->reset_gpio) ++ gpiod_set_value(sii902x->reset_gpio, 0); ++ ++ regmap_write(sii902x->regmap, SII902X_REG_TPI_RQB, 0x00); ++ ++ ret = i2c_transfer(client->adapter, &msg, 1); ++ if (ret < 0) ++ DRM_ERROR("Failed to disable CEC device!\n"); ++ ++ if (client->irq > 0) ++ regmap_update_bits(sii902x->regmap, SII902X_INT_ENABLE, ++ SII902X_HOTPLUG_EVENT, ++ SII902X_HOTPLUG_EVENT); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops sii902x_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(sii902x_pm_suspend, sii902x_pm_resume) ++}; ++ + static const struct of_device_id sii902x_dt_ids[] = { + { .compatible = "sil,sii9022", }, + { } +@@ -464,6 +1159,7 @@ static struct i2c_driver sii902x_driver = { + .driver = { + .name = "sii902x", + .of_match_table = sii902x_dt_ids, ++ .pm = &sii902x_pm_ops, + }, + .id_table = sii902x_i2c_ids, + }; +diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c +index 02db9ac..d9d9ad9 100644 +--- a/drivers/gpu/drm/drm_modes.c ++++ b/drivers/gpu/drm/drm_modes.c +@@ -130,7 +130,7 @@ EXPORT_SYMBOL(drm_mode_probed_add); + * according to the hdisplay, vdisplay, vrefresh. + * It is based from the VESA(TM) Coordinated Video Timing Generator by + * Graham Loveridge April 9, 2003 available at +- * http://www.elo.utfsm.cl/~elo212/docs/CVTd6r1.xls ++ * http://www.elo.utfsm.cl/~elo212/docs/CVTd6r1.xls + * + * And it is copied from xf86CVTmode in xserver/hw/xfree86/modes/xf86cvt.c. + * What I have done is to translate it by using integer calculation. +@@ -611,6 +611,15 @@ void drm_display_mode_from_videomode(const struct videomode *vm, + dmode->flags |= DRM_MODE_FLAG_DBLSCAN; + if (vm->flags & DISPLAY_FLAGS_DOUBLECLK) + dmode->flags |= DRM_MODE_FLAG_DBLCLK; ++ if (vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE) ++ dmode->flags |= DRM_MODE_FLAG_PPIXDATA; ++ else if (vm->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) ++ dmode->flags |= DRM_MODE_FLAG_NPIXDATA; ++ if (vm->flags & DISPLAY_FLAGS_DE_HIGH) ++ dmode->flags |= DRM_MODE_FLAG_PDE; ++ else if (vm->flags & DISPLAY_FLAGS_DE_LOW) ++ dmode->flags |= DRM_MODE_FLAG_NDE; ++ + drm_mode_set_name(dmode); + } + EXPORT_SYMBOL_GPL(drm_display_mode_from_videomode); +@@ -652,6 +661,14 @@ void drm_display_mode_to_videomode(const struct drm_display_mode *dmode, + vm->flags |= DISPLAY_FLAGS_DOUBLESCAN; + if (dmode->flags & DRM_MODE_FLAG_DBLCLK) + vm->flags |= DISPLAY_FLAGS_DOUBLECLK; ++ if (dmode->flags & DRM_MODE_FLAG_PPIXDATA) ++ vm->flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE; ++ else if (dmode->flags & DRM_MODE_FLAG_NPIXDATA) ++ vm->flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE; ++ if (dmode->flags & DRM_MODE_FLAG_PDE) ++ vm->flags |= DISPLAY_FLAGS_DE_HIGH; ++ else if (dmode->flags & DRM_MODE_FLAG_NDE) ++ vm->flags |= DISPLAY_FLAGS_DE_LOW; + } + EXPORT_SYMBOL_GPL(drm_display_mode_to_videomode); + +diff --git a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c +index 87fa316..a76d03a 100644 +--- a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c ++++ b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c +@@ -67,15 +67,15 @@ struct otm8009a { + }; + + static const struct drm_display_mode default_mode = { +- .clock = 32729, ++ .clock = 33000, + .hdisplay = 480, + .hsync_start = 480 + 120, +- .hsync_end = 480 + 120 + 63, +- .htotal = 480 + 120 + 63 + 120, ++ .hsync_end = 480 + 120 + 64, ++ .htotal = 480 + 120 + 64 + 120, + .vdisplay = 800, +- .vsync_start = 800 + 12, +- .vsync_end = 800 + 12 + 12, +- .vtotal = 800 + 12 + 12 + 12, ++ .vsync_start = 800 + 14, ++ .vsync_end = 800 + 14 + 14, ++ .vtotal = 800 + 14 + 14 + 14, + .vrefresh = 50, + .flags = 0, + .width_mm = 52, +diff --git a/drivers/gpu/drm/panel/panel-raydium-rm68200.c b/drivers/gpu/drm/panel/panel-raydium-rm68200.c +index 7759353..94436ea 100644 +--- a/drivers/gpu/drm/panel/panel-raydium-rm68200.c ++++ b/drivers/gpu/drm/panel/panel-raydium-rm68200.c +@@ -81,15 +81,15 @@ struct rm68200 { + }; + + static const struct drm_display_mode default_mode = { +- .clock = 52582, ++ .clock = 54000, + .hdisplay = 720, +- .hsync_start = 720 + 38, +- .hsync_end = 720 + 38 + 8, +- .htotal = 720 + 38 + 8 + 38, ++ .hsync_start = 720 + 48, ++ .hsync_end = 720 + 48 + 9, ++ .htotal = 720 + 48 + 9 + 48, + .vdisplay = 1280, + .vsync_start = 1280 + 12, +- .vsync_end = 1280 + 12 + 4, +- .vtotal = 1280 + 12 + 4 + 12, ++ .vsync_end = 1280 + 12 + 5, ++ .vtotal = 1280 + 12 + 5 + 12, + .vrefresh = 50, + .flags = 0, + .width_mm = 68, +diff --git a/drivers/gpu/drm/stm/drv.c b/drivers/gpu/drm/stm/drv.c +index f2021b2..cf61875 100644 +--- a/drivers/gpu/drm/stm/drv.c ++++ b/drivers/gpu/drm/stm/drv.c +@@ -26,7 +26,6 @@ + + static const struct drm_mode_config_funcs drv_mode_config_funcs = { + .fb_create = drm_gem_fb_create, +- .output_poll_changed = drm_fb_helper_output_poll_changed, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, + }; +@@ -52,7 +51,6 @@ DEFINE_DRM_GEM_CMA_FOPS(drv_driver_fops); + static struct drm_driver drv_driver = { + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | + DRIVER_ATOMIC, +- .lastclose = drm_fb_helper_lastclose, + .name = "stm", + .desc = "STMicroelectronics SoC DRM", + .date = "20170330", +@@ -72,6 +70,8 @@ static struct drm_driver drv_driver = { + .gem_prime_vmap = drm_gem_cma_prime_vmap, + .gem_prime_vunmap = drm_gem_cma_prime_vunmap, + .gem_prime_mmap = drm_gem_cma_prime_mmap, ++ .get_scanout_position = ltdc_crtc_scanoutpos, ++ .get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos, + }; + + static int drv_load(struct drm_device *ddev) +@@ -108,12 +108,6 @@ static int drv_load(struct drm_device *ddev) + drm_mode_config_reset(ddev); + drm_kms_helper_poll_init(ddev); + +- if (ddev->mode_config.num_connector) { +- ret = drm_fb_cma_fbdev_init(ddev, 16, 0); +- if (ret) +- DRM_DEBUG("Warning: fails to create fbdev\n"); +- } +- + platform_set_drvdata(pdev, ddev); + + return 0; +@@ -126,12 +120,43 @@ static void drv_unload(struct drm_device *ddev) + { + DRM_DEBUG("%s\n", __func__); + +- drm_fb_cma_fbdev_fini(ddev); + drm_kms_helper_poll_fini(ddev); + ltdc_unload(ddev); + drm_mode_config_cleanup(ddev); + } + ++static __maybe_unused int drv_suspend(struct device *dev) ++{ ++ struct drm_device *ddev = dev_get_drvdata(dev); ++ struct ltdc_device *ldev = ddev->dev_private; ++ struct drm_atomic_state *state; ++ ++ drm_kms_helper_poll_disable(ddev); ++ state = drm_atomic_helper_suspend(ddev); ++ if (IS_ERR(state)) { ++ drm_kms_helper_poll_enable(ddev); ++ return PTR_ERR(state); ++ } ++ ldev->suspend_state = state; ++ ++ return 0; ++} ++ ++static __maybe_unused int drv_resume(struct device *dev) ++{ ++ struct drm_device *ddev = dev_get_drvdata(dev); ++ struct ltdc_device *ldev = ddev->dev_private; ++ ++ drm_atomic_helper_resume(ddev, ldev->suspend_state); ++ drm_kms_helper_poll_enable(ddev); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops drv_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(drv_suspend, drv_resume) ++}; ++ + static int stm_drm_platform_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -154,6 +179,8 @@ static int stm_drm_platform_probe(struct platform_device *pdev) + if (ret) + goto err_put; + ++ drm_fbdev_generic_setup(ddev, 16); ++ + return 0; + + err_put: +@@ -187,6 +214,7 @@ static struct platform_driver stm_drm_platform_driver = { + .driver = { + .name = "stm32-display", + .of_match_table = drv_dt_ids, ++ .pm = &drv_pm_ops, + }, + }; + +diff --git a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c +index a514b59..a6edd86 100644 +--- a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c ++++ b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c +@@ -9,6 +9,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -76,6 +77,7 @@ struct dw_mipi_dsi_stm { + u32 hw_version; + int lane_min_kbps; + int lane_max_kbps; ++ struct regulator *vdd_supply; + }; + + static inline void dsi_write(struct dw_mipi_dsi_stm *dsi, u32 reg, u32 val) +@@ -318,16 +320,30 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev) + return PTR_ERR(dsi->base); + } + ++ dsi->vdd_supply = devm_regulator_get(dev, "phy-dsi"); ++ if (IS_ERR(dsi->vdd_supply)) { ++ DRM_ERROR("can't get power supply\n"); ++ return PTR_ERR(dsi->vdd_supply); ++ } ++ ++ ret = regulator_enable(dsi->vdd_supply); ++ if (ret) { ++ DRM_ERROR("can't enable power supply\n"); ++ return ret; ++ } ++ + dsi->pllref_clk = devm_clk_get(dev, "ref"); + if (IS_ERR(dsi->pllref_clk)) { + ret = PTR_ERR(dsi->pllref_clk); + dev_err(dev, "Unable to get pll reference clock: %d\n", ret); ++ regulator_disable(dsi->vdd_supply); + return ret; + } + + ret = clk_prepare_enable(dsi->pllref_clk); + if (ret) { + dev_err(dev, "%s: Failed to enable pllref_clk\n", __func__); ++ regulator_disable(dsi->vdd_supply); + return ret; + } + +@@ -339,6 +355,7 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev) + dsi->dsi = dw_mipi_dsi_probe(pdev, &dw_mipi_dsi_stm_plat_data); + if (IS_ERR(dsi->dsi)) { + DRM_ERROR("Failed to initialize mipi dsi host\n"); ++ regulator_disable(dsi->vdd_supply); + clk_disable_unprepare(dsi->pllref_clk); + return PTR_ERR(dsi->dsi); + } +@@ -351,17 +368,53 @@ static int dw_mipi_dsi_stm_remove(struct platform_device *pdev) + struct dw_mipi_dsi_stm *dsi = platform_get_drvdata(pdev); + + clk_disable_unprepare(dsi->pllref_clk); ++ regulator_disable(dsi->vdd_supply); + dw_mipi_dsi_remove(dsi->dsi); + + return 0; + } + ++static int __maybe_unused dw_mipi_dsi_stm_suspend(struct device *dev) ++{ ++ struct dw_mipi_dsi_stm *dsi = dw_mipi_dsi_stm_plat_data.priv_data; ++ ++ DRM_DEBUG_DRIVER("\n"); ++ ++ clk_disable_unprepare(dsi->pllref_clk); ++ regulator_disable(dsi->vdd_supply); ++ ++ return 0; ++} ++ ++static int __maybe_unused dw_mipi_dsi_stm_resume(struct device *dev) ++{ ++ struct dw_mipi_dsi_stm *dsi = dw_mipi_dsi_stm_plat_data.priv_data; ++ int ret; ++ ++ DRM_DEBUG_DRIVER("\n"); ++ ++ ret = regulator_enable(dsi->vdd_supply); ++ if (ret) { ++ DRM_ERROR("can't enable power supply\n"); ++ return ret; ++ } ++ clk_prepare_enable(dsi->pllref_clk); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops dw_mipi_dsi_stm_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(dw_mipi_dsi_stm_suspend, ++ dw_mipi_dsi_stm_resume) ++}; ++ + static struct platform_driver dw_mipi_dsi_stm_driver = { + .probe = dw_mipi_dsi_stm_probe, + .remove = dw_mipi_dsi_stm_remove, + .driver = { + .of_match_table = dw_mipi_dsi_stm_dt_ids, + .name = "stm32-display-dsi", ++ .pm = &dw_mipi_dsi_stm_pm_ops, + }, + }; + +diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c +index 808d9fb..599d2f8 100644 +--- a/drivers/gpu/drm/stm/ltdc.c ++++ b/drivers/gpu/drm/stm/ltdc.c +@@ -148,6 +148,8 @@ + #define IER_TERRIE BIT(2) /* Transfer ERRor Interrupt Enable */ + #define IER_RRIE BIT(3) /* Register Reload Interrupt enable */ + ++#define CPSR_CYPOS GENMASK(15, 0) /* Current Y position */ ++ + #define ISR_LIF BIT(0) /* Line Interrupt Flag */ + #define ISR_FUIF BIT(1) /* Fifo Underrun Interrupt Flag */ + #define ISR_TERRIF BIT(2) /* Transfer ERRor Interrupt Flag */ +@@ -348,6 +350,33 @@ static inline u32 get_pixelformat_without_alpha(u32 drm) + } + } + ++static int ltdc_power_up(struct ltdc_device *ldev) ++{ ++ int ret; ++ ++ DRM_DEBUG_DRIVER("\n"); ++ ++ if (!ldev->power_on) { ++ ret = clk_prepare_enable(ldev->pixel_clk); ++ if (ret) { ++ DRM_ERROR("failed to enable pixel clock (%d)\n", ++ ret); ++ return ret; ++ } ++ ldev->power_on = true; ++ } ++ ++ return 0; ++} ++ ++static void ltdc_power_down(struct ltdc_device *ldev) ++{ ++ DRM_DEBUG_DRIVER("\n"); ++ ++ clk_disable_unprepare(ldev->pixel_clk); ++ ldev->power_on = false; ++} ++ + static irqreturn_t ltdc_irq_thread(int irq, void *arg) + { + struct drm_device *ddev = arg; +@@ -408,9 +437,14 @@ static void ltdc_crtc_atomic_enable(struct drm_crtc *crtc, + struct drm_crtc_state *old_state) + { + struct ltdc_device *ldev = crtc_to_ltdc(crtc); ++ int ret; + + DRM_DEBUG_DRIVER("\n"); + ++ ret = ltdc_power_up(ldev); ++ if (ret) ++ return; ++ + /* Sets the background color value */ + reg_write(ldev->regs, LTDC_BCCR, BCCR_BCBLACK); + +@@ -443,6 +477,8 @@ static void ltdc_crtc_atomic_disable(struct drm_crtc *crtc, + + /* immediately commit disable of layers before switching off LTDC */ + reg_set(ldev->regs, LTDC_SRCR, SRCR_IMR); ++ ++ ltdc_power_down(ldev); + } + + #define CLK_TOLERANCE_HZ 50 +@@ -497,13 +533,10 @@ static bool ltdc_crtc_mode_fixup(struct drm_crtc *crtc, + * TODO clk_round_rate() does not work yet. When ready, it can + * be used instead of clk_set_rate() then clk_get_rate(). + */ +- +- clk_disable(ldev->pixel_clk); + if (clk_set_rate(ldev->pixel_clk, rate) < 0) { + DRM_ERROR("Cannot set rate (%dHz) for pixel clk\n", rate); + return false; + } +- clk_enable(ldev->pixel_clk); + + adjusted_mode->clock = clk_get_rate(ldev->pixel_clk) / 1000; + +@@ -518,6 +551,11 @@ static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc) + u32 hsync, vsync, accum_hbp, accum_vbp, accum_act_w, accum_act_h; + u32 total_width, total_height; + u32 val; ++ int ret; ++ ++ ret = ltdc_power_up(ldev); ++ if (ret) ++ return; + + drm_display_mode_to_videomode(mode, &vm); + +@@ -546,10 +584,10 @@ static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc) + if (vm.flags & DISPLAY_FLAGS_VSYNC_HIGH) + val |= GCR_VSPOL; + +- if (vm.flags & DISPLAY_FLAGS_DE_HIGH) ++ if (vm.flags & DISPLAY_FLAGS_DE_LOW) + val |= GCR_DEPOL; + +- if (vm.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) ++ if (vm.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE) + val |= GCR_PCPOL; + + reg_update_bits(ldev->regs, LTDC_GCR, +@@ -611,8 +649,14 @@ static const struct drm_crtc_helper_funcs ltdc_crtc_helper_funcs = { + static int ltdc_crtc_enable_vblank(struct drm_crtc *crtc) + { + struct ltdc_device *ldev = crtc_to_ltdc(crtc); ++ int ret; + + DRM_DEBUG_DRIVER("\n"); ++ ++ ret = ltdc_power_up(ldev); ++ if (ret) ++ return ret; ++ + reg_set(ldev->regs, LTDC_IER, IER_LIE); + + return 0; +@@ -623,9 +667,58 @@ static void ltdc_crtc_disable_vblank(struct drm_crtc *crtc) + struct ltdc_device *ldev = crtc_to_ltdc(crtc); + + DRM_DEBUG_DRIVER("\n"); ++ ++ if (!ldev->power_on) { ++ DRM_WARN("power is already down\n"); ++ return; ++ } ++ + reg_clear(ldev->regs, LTDC_IER, IER_LIE); + } + ++bool ltdc_crtc_scanoutpos(struct drm_device *ddev, unsigned int pipe, ++ bool in_vblank_irq, int *vpos, int *hpos, ++ ktime_t *stime, ktime_t *etime, ++ const struct drm_display_mode *mode) ++{ ++ struct ltdc_device *ldev = ddev->dev_private; ++ int line, vactive_start, vactive_end, vtotal; ++ ++ if (stime) ++ *stime = ktime_get(); ++ ++ /* The active area starts after vsync + front porch and ends ++ * at vsync + front porc + display size. ++ * The total height also include back porch. ++ * We have 3 possible cases to handle: ++ * - line < vactive_start: vpos = line - vactive_start and will be ++ * negative ++ * - vactive_start < line < vactive_end: vpos = line - vactive_start ++ * and will be positive ++ * - line > vactive_end: vpos = line - vtotal - vactive_start ++ * and will negative ++ * ++ * Computation for the two first cases are identical so we can ++ * simplify the code and only test if line > vactive_end ++ */ ++ line = reg_read(ldev->regs, LTDC_CPSR) & CPSR_CYPOS; ++ vactive_start = reg_read(ldev->regs, LTDC_BPCR) & BPCR_AVBP; ++ vactive_end = reg_read(ldev->regs, LTDC_AWCR) & AWCR_AAH; ++ vtotal = reg_read(ldev->regs, LTDC_TWCR) & TWCR_TOTALH; ++ ++ if (line > vactive_end) ++ *vpos = line - vtotal - vactive_start; ++ else ++ *vpos = line - vactive_start; ++ ++ *hpos = 0; ++ ++ if (etime) ++ *etime = ktime_get(); ++ ++ return true; ++} ++ + static const struct drm_crtc_funcs ltdc_crtc_funcs = { + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, +@@ -646,24 +739,44 @@ static int ltdc_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) + { + struct drm_framebuffer *fb = state->fb; +- u32 src_x, src_y, src_w, src_h; ++ struct drm_crtc_state *crtc_state; ++ struct drm_rect *src = &state->src; ++ struct drm_rect *dst = &state->dst; + + DRM_DEBUG_DRIVER("\n"); + + if (!fb) + return 0; + +- /* convert src_ from 16:16 format */ +- src_x = state->src_x >> 16; +- src_y = state->src_y >> 16; +- src_w = state->src_w >> 16; +- src_h = state->src_h >> 16; ++ /* convert src from 16:16 format */ ++ src->x1 = state->src_x >> 16; ++ src->y1 = state->src_y >> 16; ++ src->x2 = (state->src_w >> 16) + src->x1 - 1; ++ src->y2 = (state->src_h >> 16) + src->y1 - 1; ++ ++ dst->x1 = state->crtc_x; ++ dst->y1 = state->crtc_y; ++ dst->x2 = state->crtc_w + dst->x1 - 1; ++ dst->y2 = state->crtc_h + dst->y1 - 1; + +- /* Reject scaling */ +- if (src_w != state->crtc_w || src_h != state->crtc_h) { +- DRM_ERROR("Scaling is not supported"); ++ DRM_DEBUG_DRIVER("plane:%d fb:%d (%dx%d)@(%d,%d) -> (%dx%d)@(%d,%d)\n", ++ plane->base.id, fb->base.id, ++ src->x2 - src->x1 + 1, src->y2 - src->y1 + 1, ++ src->x1, src->y1, ++ dst->x2 - dst->x1 + 1, dst->y2 - dst->y1 + 1, ++ dst->x1, dst->y1); ++ ++ crtc_state = drm_atomic_get_existing_crtc_state(state->state, ++ state->crtc); ++ /* destination coordinates do not have to exceed display sizes */ ++ if (crtc_state && (crtc_state->mode.hdisplay <= dst->x2 || ++ crtc_state->mode.vdisplay <= dst->y2)) ++ return -EINVAL; ++ ++ /* source sizes do not have to exceed destination sizes */ ++ if (dst->x2 - dst->x1 < src->x2 - src->x1 || ++ dst->y2 - dst->y1 < src->y2 - src->y1) + return -EINVAL; +- } + + return 0; + } +@@ -673,44 +786,36 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane, + { + struct ltdc_device *ldev = plane_to_ltdc(plane); + struct drm_plane_state *state = plane->state; ++ struct drm_rect *src = &state->src; ++ struct drm_rect *dst = &state->dst; + struct drm_framebuffer *fb = state->fb; + u32 lofs = plane->index * LAY_OFS; +- u32 x0 = state->crtc_x; +- u32 x1 = state->crtc_x + state->crtc_w - 1; +- u32 y0 = state->crtc_y; +- u32 y1 = state->crtc_y + state->crtc_h - 1; +- u32 src_x, src_y, src_w, src_h; + u32 val, pitch_in_bytes, line_length, paddr, ahbp, avbp, bpcr; + enum ltdc_pix_fmt pf; ++ struct drm_rect dr; + + if (!state->crtc || !fb) { + DRM_DEBUG_DRIVER("fb or crtc NULL"); + return; + } + +- /* convert src_ from 16:16 format */ +- src_x = state->src_x >> 16; +- src_y = state->src_y >> 16; +- src_w = state->src_w >> 16; +- src_h = state->src_h >> 16; +- +- DRM_DEBUG_DRIVER("plane:%d fb:%d (%dx%d)@(%d,%d) -> (%dx%d)@(%d,%d)\n", +- plane->base.id, fb->base.id, +- src_w, src_h, src_x, src_y, +- state->crtc_w, state->crtc_h, +- state->crtc_x, state->crtc_y); ++ /* compute final coordinates of frame buffer */ ++ dr.x1 = src->x1 + dst->x1; ++ dr.y1 = src->y1 + dst->y1; ++ dr.x2 = src->x2 + dst->x1; ++ dr.y2 = src->y2 + dst->y1; + + bpcr = reg_read(ldev->regs, LTDC_BPCR); + ahbp = (bpcr & BPCR_AHBP) >> 16; + avbp = bpcr & BPCR_AVBP; + + /* Configures the horizontal start and stop position */ +- val = ((x1 + 1 + ahbp) << 16) + (x0 + 1 + ahbp); ++ val = ((dr.x2 + 1 + ahbp) << 16) + (dr.x1 + 1 + ahbp); + reg_update_bits(ldev->regs, LTDC_L1WHPCR + lofs, + LXWHPCR_WHSTPOS | LXWHPCR_WHSPPOS, val); + + /* Configures the vertical start and stop position */ +- val = ((y1 + 1 + avbp) << 16) + (y0 + 1 + avbp); ++ val = ((dr.y2 + 1 + avbp) << 16) + (dr.y1 + 1 + avbp); + reg_update_bits(ldev->regs, LTDC_L1WVPCR + lofs, + LXWVPCR_WVSTPOS | LXWVPCR_WVSPPOS, val); + +@@ -730,7 +835,7 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane, + /* Configures the color frame buffer pitch in bytes & line length */ + pitch_in_bytes = fb->pitches[0]; + line_length = drm_format_plane_cpp(fb->format->format, 0) * +- (x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1; ++ (dr.x2 - dr.x1 + 1) + (ldev->caps.bus_width >> 3) - 1; + val = ((pitch_in_bytes << 16) | line_length); + reg_update_bits(ldev->regs, LTDC_L1CFBLR + lofs, + LXCFBLR_CFBLL | LXCFBLR_CFBP, val); +@@ -753,7 +858,7 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane, + LXBFCR_BF2 | LXBFCR_BF1, val); + + /* Configures the frame buffer line number */ +- val = y1 - y0 + 1; ++ val = dr.y2 - dr.y1 + 1; + reg_update_bits(ldev->regs, LTDC_L1CFBLNR + lofs, LXCFBLNR_CFBLN, val); + + /* Sets the FB address */ +@@ -772,11 +877,11 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane, + + mutex_lock(&ldev->err_lock); + if (ldev->error_status & ISR_FUIF) { +- DRM_DEBUG_DRIVER("Fifo underrun\n"); ++ DRM_WARN("ltdc fifo underrun: please verify display mode\n"); + ldev->error_status &= ~ISR_FUIF; + } + if (ldev->error_status & ISR_TERRIF) { +- DRM_DEBUG_DRIVER("Transfer error\n"); ++ DRM_WARN("ltdc transfer error\n"); + ldev->error_status &= ~ISR_TERRIF; + } + mutex_unlock(&ldev->err_lock); +@@ -1055,10 +1160,10 @@ int ltdc_load(struct drm_device *ddev) + return -ENODEV; + } + +- if (clk_prepare_enable(ldev->pixel_clk)) { +- DRM_ERROR("Unable to prepare pixel clock\n"); +- return -ENODEV; +- } ++ ldev->power_on = false; ++ ret = ltdc_power_up(ldev); ++ if (ret) ++ return ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ldev->regs = devm_ioremap_resource(dev, res); +@@ -1150,7 +1255,7 @@ int ltdc_load(struct drm_device *ddev) + for (i = 0; i < MAX_ENDPOINTS; i++) + drm_panel_bridge_remove(bridge[i]); + +- clk_disable_unprepare(ldev->pixel_clk); ++ ltdc_power_down(ldev); + + return ret; + } +@@ -1165,7 +1270,7 @@ void ltdc_unload(struct drm_device *ddev) + for (i = 0; i < MAX_ENDPOINTS; i++) + drm_of_panel_bridge_remove(ddev->dev->of_node, 0, i); + +- clk_disable_unprepare(ldev->pixel_clk); ++ ltdc_power_down(ldev); + } + + MODULE_AUTHOR("Philippe Cornu "); +diff --git a/drivers/gpu/drm/stm/ltdc.h b/drivers/gpu/drm/stm/ltdc.h +index d5afb89..08bd69d 100644 +--- a/drivers/gpu/drm/stm/ltdc.h ++++ b/drivers/gpu/drm/stm/ltdc.h +@@ -36,8 +36,14 @@ struct ltdc_device { + u32 error_status; + u32 irq_status; + struct fps_info plane_fpsi[LTDC_MAX_LAYER]; ++ struct drm_atomic_state *suspend_state; ++ bool power_on; + }; + ++bool ltdc_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe, ++ bool in_vblank_irq, int *vpos, int *hpos, ++ ktime_t *stime, ktime_t *etime, ++ const struct drm_display_mode *mode); + int ltdc_load(struct drm_device *ddev); + void ltdc_unload(struct drm_device *ddev); + +diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h +index 8d67243..c20c4c3 100644 +--- a/include/uapi/drm/drm_mode.h ++++ b/include/uapi/drm/drm_mode.h +@@ -89,6 +89,12 @@ extern "C" { + #define DRM_MODE_FLAG_3D_TOP_AND_BOTTOM (7<<14) + #define DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF (8<<14) + ++/* flags for polarity clock & data enable polarities */ ++#define DRM_MODE_FLAG_PPIXDATA (1 << 19) ++#define DRM_MODE_FLAG_NPIXDATA (1 << 20) ++#define DRM_MODE_FLAG_PDE (1 << 21) ++#define DRM_MODE_FLAG_NDE (1 << 22) ++ + /* Picture aspect ratio options */ + #define DRM_MODE_PICTURE_ASPECT_NONE 0 + #define DRM_MODE_PICTURE_ASPECT_4_3 1 +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0019-ARM-stm32mp1-r0-rc2-SOUND.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0019-ARM-stm32mp1-r0-rc2-SOUND.patch new file mode 100644 index 0000000..a9c1094 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0019-ARM-stm32mp1-r0-rc2-SOUND.patch @@ -0,0 +1,1964 @@ +From f0e754855e06c3b8eb04ee09e6b1ea8a1bd9af7c Mon Sep 17 00:00:00 2001 +From: Christophe Priouzeau +Date: Mon, 26 Nov 2018 14:39:24 +0100 +Subject: [PATCH 19/52] ARM-stm32mp1-r0-rc2-SOUND + +--- + include/linux/mfd/wm8994/pdata.h | 6 + + sound/soc/codecs/Kconfig | 4 +- + sound/soc/codecs/cs42l51-i2c.c | 6 + + sound/soc/codecs/cs42l51.c | 155 +++++++++++++++--- + sound/soc/codecs/cs42l51.h | 1 + + sound/soc/codecs/wm8994.c | 79 ++++++++- + sound/soc/stm/Kconfig | 1 + + sound/soc/stm/stm32_adfsdm.c | 17 +- + sound/soc/stm/stm32_i2s.c | 172 +++++++++++++------- + sound/soc/stm/stm32_sai.c | 67 +++++++- + sound/soc/stm/stm32_sai.h | 57 +++++-- + sound/soc/stm/stm32_sai_sub.c | 335 +++++++++++++++++++++++++++++++++------ + sound/soc/stm/stm32_spdifrx.c | 81 +++++++++- + 13 files changed, 819 insertions(+), 162 deletions(-) + +diff --git a/include/linux/mfd/wm8994/pdata.h b/include/linux/mfd/wm8994/pdata.h +index b19c370..cdaf3f6 100644 +--- a/include/linux/mfd/wm8994/pdata.h ++++ b/include/linux/mfd/wm8994/pdata.h +@@ -239,6 +239,12 @@ struct wm8994_pdata { + * GPIO for the IRQ pin if host only supports edge triggering + */ + int irq_gpio; ++ ++ /* MCLK1 clock provider */ ++ struct clk *mclk1; ++ ++ /* MCLK2 clock provider */ ++ struct clk *mclk2; + }; + + #endif +diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig +index efb095d..58161d1 100644 +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -575,7 +575,7 @@ config SND_SOC_DA9055 + tristate + + config SND_SOC_DMIC +- tristate ++ tristate "Generic DMIC codec" + + config SND_SOC_HDMI_CODEC + tristate +@@ -1227,7 +1227,7 @@ config SND_SOC_WM8993 + tristate + + config SND_SOC_WM8994 +- tristate ++ tristate "Wolfson Microelectronics WM8994 codec" + + config SND_SOC_WM8995 + tristate +diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c +index 4b5731a..8333dbf 100644 +--- a/sound/soc/codecs/cs42l51-i2c.c ++++ b/sound/soc/codecs/cs42l51-i2c.c +@@ -35,12 +35,18 @@ static int cs42l51_i2c_probe(struct i2c_client *i2c, + return cs42l51_probe(&i2c->dev, devm_regmap_init_i2c(i2c, &config)); + } + ++static int cs42l51_i2c_remove(struct i2c_client *i2c) ++{ ++ return cs42l51_remove(&i2c->dev); ++} ++ + static struct i2c_driver cs42l51_i2c_driver = { + .driver = { + .name = "cs42l51", + .of_match_table = cs42l51_of_match, + }, + .probe = cs42l51_i2c_probe, ++ .remove = cs42l51_i2c_remove, + .id_table = cs42l51_i2c_id, + }; + +diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c +index 5080d7a3..fceee59 100644 +--- a/sound/soc/codecs/cs42l51.c ++++ b/sound/soc/codecs/cs42l51.c +@@ -21,6 +21,7 @@ + * - master mode *NOT* supported + */ + ++#include + #include + #include + #include +@@ -29,7 +30,9 @@ + #include + #include + #include ++#include + #include ++#include + + #include "cs42l51.h" + +@@ -39,10 +42,21 @@ enum master_slave_mode { + MODE_MASTER, + }; + ++#define CS42L51_NUM_SUPPLIES 4 ++static const char *cs42l51_supply_names[CS42L51_NUM_SUPPLIES] = { ++ "VL", ++ "VD", ++ "VA", ++ "VAHP", ++}; ++ + struct cs42l51_private { + unsigned int mclk; ++ struct clk *mclk_handle; + unsigned int audio_mode; /* The mode (I2S or left-justified) */ + enum master_slave_mode func; ++ struct regulator_bulk_data supplies[CS42L51_NUM_SUPPLIES]; ++ struct gpio_desc *reset_gpio; + }; + + #define CS42L51_FORMATS ( \ +@@ -165,6 +179,7 @@ static int cs42l51_pdn_event(struct snd_soc_dapm_widget *w, + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, CS42L51_POWER_CTL1, + CS42L51_POWER_CTL1_PDN, 0); ++ msleep(20); + break; + } + +@@ -193,7 +208,8 @@ static const struct snd_kcontrol_new cs42l51_adcr_mux_controls = + SOC_DAPM_ENUM("Route", cs42l51_adcr_mux_enum); + + static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = { +- SND_SOC_DAPM_MICBIAS("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1), ++ SND_SOC_DAPM_SUPPLY("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1, NULL, ++ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("Left PGA", CS42L51_POWER_CTL1, 3, 1, NULL, 0, + cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD), + SND_SOC_DAPM_PGA_E("Right PGA", CS42L51_POWER_CTL1, 4, 1, NULL, 0, +@@ -237,6 +253,10 @@ static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = { + &cs42l51_adcr_mux_controls), + }; + ++static const struct snd_soc_dapm_widget cs42l51_dapm_mclk_widgets[] = { ++ SND_SOC_DAPM_CLOCK_SUPPLY("MCLK") ++}; ++ + static const struct snd_soc_dapm_route cs42l51_routes[] = { + {"HPL", NULL, "Left DAC"}, + {"HPR", NULL, "Right DAC"}, +@@ -323,6 +343,19 @@ static struct cs42l51_ratios slave_auto_ratios[] = { + { 256, CS42L51_DSM_MODE, 1 }, { 384, CS42L51_DSM_MODE, 1 }, + }; + ++/* ++ * Master mode mclk/fs ratios. ++ * Recommended configurations are SSM for 4-50khz and DSM for 50-100kHz ranges ++ * The table below provides support of following ratios: ++ * 128: SSM (%128) with div2 disabled ++ * 256: SSM (%128) with div2 enabled ++ * In both cases, if sampling rate is above 50kHz, SSM is overridden ++ * with DSM (%128) configuration ++ */ ++static struct cs42l51_ratios master_ratios[] = { ++ { 128, CS42L51_SSM_MODE, 0 }, { 256, CS42L51_SSM_MODE, 1 }, ++}; ++ + static int cs42l51_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) + { +@@ -345,11 +378,13 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream, + unsigned int ratio; + struct cs42l51_ratios *ratios = NULL; + int nr_ratios = 0; +- int intf_ctl, power_ctl, fmt; ++ int intf_ctl, power_ctl, fmt, mode; + + switch (cs42l51->func) { + case MODE_MASTER: +- return -EINVAL; ++ ratios = master_ratios; ++ nr_ratios = ARRAY_SIZE(master_ratios); ++ break; + case MODE_SLAVE: + ratios = slave_ratios; + nr_ratios = ARRAY_SIZE(slave_ratios); +@@ -385,7 +420,16 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream, + switch (cs42l51->func) { + case MODE_MASTER: + intf_ctl |= CS42L51_INTF_CTL_MASTER; +- power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode); ++ mode = ratios[i].speed_mode; ++ /* Force DSM mode if sampling rate is above 50kHz */ ++ if (rate > 50000) ++ mode = CS42L51_DSM_MODE; ++ power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(mode); ++ /* ++ * Auto detect mode is not applicable for master mode and has to ++ * be disabled. Otherwise SPEED[1:0] bits will be ignored. ++ */ ++ power_ctl &= ~CS42L51_MIC_POWER_CTL_AUTO; + break; + case MODE_SLAVE: + power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode); +@@ -465,28 +509,42 @@ static const struct snd_soc_dai_ops cs42l51_dai_ops = { + .digital_mute = cs42l51_dai_mute, + }; + +-static struct snd_soc_dai_driver cs42l51_dai = { +- .name = "cs42l51-hifi", +- .playback = { +- .stream_name = "Playback", +- .channels_min = 1, +- .channels_max = 2, +- .rates = SNDRV_PCM_RATE_8000_96000, +- .formats = CS42L51_FORMATS, +- }, +- .capture = { +- .stream_name = "Capture", +- .channels_min = 1, +- .channels_max = 2, +- .rates = SNDRV_PCM_RATE_8000_96000, +- .formats = CS42L51_FORMATS, ++static struct snd_soc_dai_driver cs42l51_dai[] = { ++ { ++ .name = "cs42l51-hifi0", ++ .playback = { ++ .stream_name = "Playback", ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_96000, ++ .formats = CS42L51_FORMATS, ++ }, ++ .ops = &cs42l51_dai_ops, + }, +- .ops = &cs42l51_dai_ops, ++ { ++ .name = "cs42l51-hifi1", ++ .capture = { ++ .stream_name = "Capture", ++ .channels_min = 1, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_96000, ++ .formats = CS42L51_FORMATS, ++ }, ++ .ops = &cs42l51_dai_ops, ++ } + }; + + static int cs42l51_component_probe(struct snd_soc_component *component) + { + int ret, reg; ++ struct snd_soc_dapm_context *dapm; ++ struct cs42l51_private *cs42l51; ++ ++ cs42l51 = snd_soc_component_get_drvdata(component); ++ dapm = snd_soc_component_get_dapm(component); ++ ++ if (cs42l51->mclk_handle) ++ snd_soc_dapm_new_controls(dapm, cs42l51_dapm_mclk_widgets, 1); + + /* + * DAC configuration +@@ -528,7 +586,7 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap) + { + struct cs42l51_private *cs42l51; + unsigned int val; +- int ret; ++ int ret, i; + + if (IS_ERR(regmap)) + return PTR_ERR(regmap); +@@ -540,6 +598,41 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap) + + dev_set_drvdata(dev, cs42l51); + ++ cs42l51->mclk_handle = devm_clk_get(dev, "MCLK"); ++ if (IS_ERR(cs42l51->mclk_handle)) { ++ if (PTR_ERR(cs42l51->mclk_handle) != -ENOENT) ++ return PTR_ERR(cs42l51->mclk_handle); ++ cs42l51->mclk_handle = NULL; ++ } ++ ++ for (i = 0; i < ARRAY_SIZE(cs42l51->supplies); i++) ++ cs42l51->supplies[i].supply = cs42l51_supply_names[i]; ++ ++ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs42l51->supplies), ++ cs42l51->supplies); ++ if (ret != 0) { ++ dev_err(dev, "Failed to request supplies: %d\n", ret); ++ return ret; ++ } ++ ++ ret = regulator_bulk_enable(ARRAY_SIZE(cs42l51->supplies), ++ cs42l51->supplies); ++ if (ret != 0) { ++ dev_err(dev, "Failed to enable supplies: %d\n", ret); ++ return ret; ++ } ++ ++ cs42l51->reset_gpio = devm_gpiod_get_optional(dev, "reset", ++ GPIOD_OUT_LOW); ++ if (IS_ERR(cs42l51->reset_gpio)) ++ return PTR_ERR(cs42l51->reset_gpio); ++ ++ if (cs42l51->reset_gpio) { ++ dev_dbg(dev, "Release reset gpio\n"); ++ gpiod_set_value_cansleep(cs42l51->reset_gpio, 0); ++ mdelay(2); ++ } ++ + /* Verify that we have a CS42L51 */ + ret = regmap_read(regmap, CS42L51_CHIP_REV_ID, &val); + if (ret < 0) { +@@ -557,12 +650,30 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap) + val & CS42L51_CHIP_REV_MASK); + + ret = devm_snd_soc_register_component(dev, +- &soc_component_device_cs42l51, &cs42l51_dai, 1); ++ &soc_component_device_cs42l51, cs42l51_dai, 2); ++ if (ret < 0) ++ goto error; ++ ++ return 0; ++ + error: ++ regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies), ++ cs42l51->supplies); + return ret; + } + EXPORT_SYMBOL_GPL(cs42l51_probe); + ++int cs42l51_remove(struct device *dev) ++{ ++ struct cs42l51_private *cs42l51 = dev_get_drvdata(dev); ++ ++ gpiod_set_value_cansleep(cs42l51->reset_gpio, 1); ++ ++ return regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies), ++ cs42l51->supplies); ++} ++EXPORT_SYMBOL_GPL(cs42l51_remove); ++ + const struct of_device_id cs42l51_of_match[] = { + { .compatible = "cirrus,cs42l51", }, + { } +diff --git a/sound/soc/codecs/cs42l51.h b/sound/soc/codecs/cs42l51.h +index 0ca8054..aef0ede 100644 +--- a/sound/soc/codecs/cs42l51.h ++++ b/sound/soc/codecs/cs42l51.h +@@ -22,6 +22,7 @@ struct device; + + extern const struct regmap_config cs42l51_regmap; + int cs42l51_probe(struct device *dev, struct regmap *regmap); ++int cs42l51_remove(struct device *dev); + extern const struct of_device_id cs42l51_of_match[]; + + #define CS42L51_CHIP_ID 0x1B +diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c +index 14f1b0c..eb222da 100644 +--- a/sound/soc/codecs/wm8994.c ++++ b/sound/soc/codecs/wm8994.c +@@ -11,6 +11,7 @@ + * published by the Free Software Foundation. + */ + ++#include + #include + #include + #include +@@ -839,6 +840,42 @@ static int clk_sys_event(struct snd_soc_dapm_widget *w, + return 0; + } + ++static int mclk_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, int event) ++{ ++ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm); ++ struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(comp); ++ struct wm8994 *control = wm8994->wm8994; ++ struct wm8994_pdata *pdata = &control->pdata; ++ struct clk *mclk = pdata->mclk1; ++ int ret, mclk_id = 0; ++ ++ if (!strncmp(w->name, "MCLK2", 5)) { ++ mclk_id = 1; ++ mclk = pdata->mclk2; ++ } ++ ++ switch (event) { ++ case SND_SOC_DAPM_PRE_PMU: ++ dev_dbg(comp->dev, "Enable master clock %s\n", ++ mclk_id ? "MCLK2" : "MCLK1"); ++ ++ ret = clk_prepare_enable(mclk); ++ if (ret < 0) { ++ dev_err(comp->dev, "Failed to enable clock: %d\n", ret); ++ return ret; ++ } ++ break; ++ case SND_SOC_DAPM_POST_PMD: ++ dev_dbg(comp->dev, "Disable master clock %s\n", ++ mclk_id ? "MCLK1" : "MCLK2"); ++ clk_disable_unprepare(mclk); ++ break; ++ } ++ ++ return 0; ++} ++ + static void vmid_reference(struct snd_soc_component *component) + { + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); +@@ -1775,6 +1812,16 @@ static const struct snd_soc_dapm_widget wm8994_specific_dapm_widgets[] = { + SND_SOC_DAPM_MUX("AIF3ADC Mux", SND_SOC_NOPM, 0, 0, &wm8994_aif3adc_mux), + }; + ++static const struct snd_soc_dapm_widget wm8994_mclk1_dapm_widgets[] = { ++SND_SOC_DAPM_SUPPLY("MCLK1", SND_SOC_NOPM, 0, 0, mclk_event, ++ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), ++}; ++ ++static const struct snd_soc_dapm_widget wm8994_mclk2_dapm_widgets[] = { ++SND_SOC_DAPM_SUPPLY("MCLK2", SND_SOC_NOPM, 0, 0, mclk_event, ++ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), ++}; ++ + static const struct snd_soc_dapm_widget wm8958_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("AIF3", WM8994_POWER_MANAGEMENT_6, 5, 1, NULL, 0), + SND_SOC_DAPM_MUX("Mono PCM Out Mux", SND_SOC_NOPM, 0, 0, &mono_pcm_out_mux), +@@ -1999,10 +2046,10 @@ static const struct snd_soc_dapm_route wm8994_lateclk_intercon[] = { + }; + + static const struct snd_soc_dapm_route wm8994_revd_intercon[] = { +- { "AIF1DACDAT", NULL, "AIF2DACDAT" }, +- { "AIF2DACDAT", NULL, "AIF1DACDAT" }, +- { "AIF1ADCDAT", NULL, "AIF2ADCDAT" }, +- { "AIF2ADCDAT", NULL, "AIF1ADCDAT" }, ++// { "AIF1DACDAT", NULL, "AIF2DACDAT" }, ++// { "AIF2DACDAT", NULL, "AIF1DACDAT" }, ++// { "AIF1ADCDAT", NULL, "AIF2ADCDAT" }, ++// { "AIF2ADCDAT", NULL, "AIF1ADCDAT" }, + { "MICBIAS1", NULL, "CLK_SYS" }, + { "MICBIAS1", NULL, "MICBIAS Supply" }, + { "MICBIAS2", NULL, "CLK_SYS" }, +@@ -2376,11 +2423,26 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai, + { + struct snd_soc_component *component = dai->component; + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); ++ struct wm8994 *control = wm8994->wm8994; ++ struct wm8994_pdata *pdata = &control->pdata; + int i; + ++ /* ++ * Simple card provides unconditionnaly clock_id = 0. ++ * Workaround to select master clock for aif1/2 ++ */ + switch (dai->id) { + case 1: ++ if (pdata->mclk1) ++ clk_id = WM8994_SYSCLK_MCLK1; ++ else if (pdata->mclk2) ++ clk_id = WM8994_SYSCLK_MCLK2; ++ break; + case 2: ++ if (pdata->mclk2) ++ clk_id = WM8994_SYSCLK_MCLK2; ++ else if (pdata->mclk1) ++ clk_id = WM8994_SYSCLK_MCLK1; + break; + + default: +@@ -3990,6 +4052,7 @@ static int wm8994_component_probe(struct snd_soc_component *component) + { + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct wm8994 *control = dev_get_drvdata(component->dev->parent); ++ struct wm8994_pdata *pdata = &control->pdata; + struct wm8994_priv *wm8994 = snd_soc_component_get_drvdata(component); + unsigned int reg; + int ret, i; +@@ -4271,6 +4334,14 @@ static int wm8994_component_probe(struct snd_soc_component *component) + case WM8994: + snd_soc_dapm_new_controls(dapm, wm8994_specific_dapm_widgets, + ARRAY_SIZE(wm8994_specific_dapm_widgets)); ++ if (pdata->mclk1) ++ snd_soc_dapm_new_controls(dapm, wm8994_mclk1_dapm_widgets, ++ ARRAY_SIZE(wm8994_mclk1_dapm_widgets)); ++ ++ if (pdata->mclk2) ++ snd_soc_dapm_new_controls(dapm, wm8994_mclk2_dapm_widgets, ++ ARRAY_SIZE(wm8994_mclk2_dapm_widgets)); ++ + if (control->revision < 4) { + snd_soc_dapm_new_controls(dapm, wm8994_lateclk_revd_widgets, + ARRAY_SIZE(wm8994_lateclk_revd_widgets)); +diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig +index 9b26813..c66ffa7 100644 +--- a/sound/soc/stm/Kconfig ++++ b/sound/soc/stm/Kconfig +@@ -3,6 +3,7 @@ menu "STMicroelectronics STM32 SOC audio support" + config SND_SOC_STM32_SAI + tristate "STM32 SAI interface (Serial Audio Interface) support" + depends on (ARCH_STM32 && OF) || COMPILE_TEST ++ depends on COMMON_CLK + depends on SND_SOC + select SND_SOC_GENERIC_DMAENGINE_PCM + select REGMAP_MMIO +diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c +index 706ff00..71d341b 100644 +--- a/sound/soc/stm/stm32_adfsdm.c ++++ b/sound/soc/stm/stm32_adfsdm.c +@@ -9,6 +9,7 @@ + + #include + #include ++#include + #include + #include + +@@ -37,6 +38,8 @@ struct stm32_adfsdm_priv { + /* PCM buffer */ + unsigned char *pcm_buff; + unsigned int pos; ++ ++ struct mutex lock; /* protect against race condition on iio state */ + }; + + static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = { +@@ -62,10 +65,12 @@ static void stm32_adfsdm_shutdown(struct snd_pcm_substream *substream, + { + struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai); + ++ mutex_lock(&priv->lock); + if (priv->iio_active) { + iio_channel_stop_all_cb(priv->iio_cb); + priv->iio_active = false; + } ++ mutex_unlock(&priv->lock); + } + + static int stm32_adfsdm_dai_prepare(struct snd_pcm_substream *substream, +@@ -74,13 +79,19 @@ static int stm32_adfsdm_dai_prepare(struct snd_pcm_substream *substream, + struct stm32_adfsdm_priv *priv = snd_soc_dai_get_drvdata(dai); + int ret; + ++ mutex_lock(&priv->lock); ++ if (priv->iio_active) { ++ iio_channel_stop_all_cb(priv->iio_cb); ++ priv->iio_active = false; ++ } ++ + ret = iio_write_channel_attribute(priv->iio_ch, + substream->runtime->rate, 0, + IIO_CHAN_INFO_SAMP_FREQ); + if (ret < 0) { + dev_err(dai->dev, "%s: Failed to set %d sampling rate\n", + __func__, substream->runtime->rate); +- return ret; ++ goto out; + } + + if (!priv->iio_active) { +@@ -92,6 +103,9 @@ static int stm32_adfsdm_dai_prepare(struct snd_pcm_substream *substream, + __func__, ret); + } + ++out: ++ mutex_unlock(&priv->lock); ++ + return ret; + } + +@@ -298,6 +312,7 @@ static int stm32_adfsdm_probe(struct platform_device *pdev) + + priv->dev = &pdev->dev; + priv->dai_drv = stm32_adfsdm_dai; ++ mutex_init(&priv->lock); + + dev_set_drvdata(&pdev->dev, priv); + +diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c +index 6d0bf78..9dcf946 100644 +--- a/sound/soc/stm/stm32_i2s.c ++++ b/sound/soc/stm/stm32_i2s.c +@@ -16,6 +16,7 @@ + * details. + */ + ++#include + #include + #include + #include +@@ -37,6 +38,10 @@ + #define STM32_I2S_TXDR_REG 0X20 + #define STM32_I2S_RXDR_REG 0x30 + #define STM32_I2S_CGFR_REG 0X50 ++#define STM32_I2S_HWCFGR_REG 0x3F0 ++#define STM32_I2S_VERR_REG 0x3F4 ++#define STM32_I2S_IPIDR_REG 0x3F8 ++#define STM32_I2S_SIDR_REG 0x3FC + + /* Bit definition for SPI2S_CR1 register */ + #define I2S_CR1_SPE BIT(0) +@@ -143,6 +148,23 @@ + #define I2S_CGFR_ODD BIT(I2S_CGFR_ODD_SHIFT) + #define I2S_CGFR_MCKOE BIT(25) + ++/* Registers below apply to I2S version 1.1 and more */ ++ ++/* Bit definition for SPI_HWCFGR register */ ++#define I2S_HWCFGR_I2S_SUPPORT_MASK GENMASK(15, 12) ++ ++/* Bit definition for SPI_VERR register */ ++#define I2S_VERR_MIN_MASK GENMASK(3, 0) ++#define I2S_VERR_MAJ_MASK GENMASK(7, 4) ++ ++/* Bit definition for SPI_IPIDR register */ ++#define I2S_IPIDR_ID_MASK GENMASK(31, 0) ++ ++/* Bit definition for SPI_SIDR register */ ++#define I2S_SIDR_ID_MASK GENMASK(31, 0) ++ ++#define I2S_IPIDR_NUMBER 0x00130022 ++ + enum i2s_master_mode { + I2S_MS_NOT_SET, + I2S_MS_MASTER, +@@ -179,7 +201,6 @@ enum i2s_datlen { + I2S_I2SMOD_DATLEN_32, + }; + +-#define STM32_I2S_DAI_NAME_SIZE 20 + #define STM32_I2S_FIFO_SIZE 16 + + #define STM32_I2S_IS_MASTER(x) ((x)->ms_flg == I2S_MS_MASTER) +@@ -200,7 +221,6 @@ enum i2s_datlen { + * @base: mmio register base virtual address + * @phys_addr: I2S registers physical base address + * @lock_fd: lock to manage race conditions in full duplex mode +- * @dais_name: DAI name + * @mclk_rate: master clock frequency (Hz) + * @fmt: DAI protocol + * @refcount: keep count of opened streams on I2S +@@ -221,7 +241,6 @@ struct stm32_i2s_data { + void __iomem *base; + dma_addr_t phys_addr; + spinlock_t lock_fd; /* Manage race conditions for full duplex */ +- char dais_name[STM32_I2S_DAI_NAME_SIZE]; + unsigned int mclk_rate; + unsigned int fmt; + int refcount; +@@ -246,8 +265,8 @@ static irqreturn_t stm32_i2s_isr(int irq, void *devid) + return IRQ_NONE; + } + +- regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG, +- I2S_IFCR_MASK, flags); ++ regmap_write_bits(i2s->regmap, STM32_I2S_IFCR_REG, ++ I2S_IFCR_MASK, flags); + + if (flags & I2S_SR_OVR) { + dev_dbg(&pdev->dev, "Overrun\n"); +@@ -276,10 +295,13 @@ static bool stm32_i2s_readable_reg(struct device *dev, unsigned int reg) + case STM32_I2S_CFG2_REG: + case STM32_I2S_IER_REG: + case STM32_I2S_SR_REG: +- case STM32_I2S_IFCR_REG: + case STM32_I2S_TXDR_REG: + case STM32_I2S_RXDR_REG: + case STM32_I2S_CGFR_REG: ++ case STM32_I2S_HWCFGR_REG: ++ case STM32_I2S_VERR_REG: ++ case STM32_I2S_IPIDR_REG: ++ case STM32_I2S_SIDR_REG: + return true; + default: + return false; +@@ -488,7 +510,7 @@ static int stm32_i2s_configure(struct snd_soc_dai *cpu_dai, + { + struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); + int format = params_width(params); +- u32 cfgr, cfgr_mask, cfg1, cfg1_mask; ++ u32 cfgr, cfgr_mask, cfg1; + unsigned int fthlv; + int ret; + +@@ -501,7 +523,7 @@ static int stm32_i2s_configure(struct snd_soc_dai *cpu_dai, + switch (format) { + case 16: + cfgr = I2S_CGFR_DATLEN_SET(I2S_I2SMOD_DATLEN_16); +- cfgr_mask = I2S_CGFR_DATLEN_MASK; ++ cfgr_mask = I2S_CGFR_DATLEN_MASK | I2S_CGFR_CHLEN; + break; + case 32: + cfgr = I2S_CGFR_DATLEN_SET(I2S_I2SMOD_DATLEN_32) | +@@ -529,30 +551,29 @@ static int stm32_i2s_configure(struct snd_soc_dai *cpu_dai, + if (ret < 0) + return ret; + +- cfg1 = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN; +- cfg1_mask = cfg1; +- + fthlv = STM32_I2S_FIFO_SIZE * I2S_FIFO_TH_ONE_QUARTER / 4; +- cfg1 |= I2S_CFG1_FTHVL_SET(fthlv - 1); +- cfg1_mask |= I2S_CFG1_FTHVL_MASK; ++ cfg1 = I2S_CFG1_FTHVL_SET(fthlv - 1); + + return regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG, +- cfg1_mask, cfg1); ++ I2S_CFG1_FTHVL_MASK, cfg1); + } + + static int stm32_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) + { + struct stm32_i2s_data *i2s = snd_soc_dai_get_drvdata(cpu_dai); ++ int ret; + + i2s->substream = substream; + +- spin_lock(&i2s->lock_fd); +- i2s->refcount++; +- spin_unlock(&i2s->lock_fd); ++ ret = clk_prepare_enable(i2s->i2sclk); ++ if (ret < 0) { ++ dev_err(cpu_dai->dev, "Failed to enable clock: %d\n", ret); ++ return ret; ++ } + +- return regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG, +- I2S_IFCR_MASK, I2S_IFCR_MASK); ++ return regmap_write_bits(i2s->regmap, STM32_I2S_IFCR_REG, ++ I2S_IFCR_MASK, I2S_IFCR_MASK); + } + + static int stm32_i2s_hw_params(struct snd_pcm_substream *substream, +@@ -587,7 +608,12 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + /* Enable i2s */ +- dev_dbg(cpu_dai->dev, "start I2S\n"); ++ dev_dbg(cpu_dai->dev, "start I2S %s\n", ++ playback_flg ? "playback" : "capture"); ++ ++ cfg1_mask = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN; ++ regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG, ++ cfg1_mask, cfg1_mask); + + ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG, + I2S_CR1_SPE, I2S_CR1_SPE); +@@ -596,28 +622,29 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + return ret; + } + +- ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG, +- I2S_CR1_CSTART, I2S_CR1_CSTART); ++ ret = regmap_write_bits(i2s->regmap, STM32_I2S_CR1_REG, ++ I2S_CR1_CSTART, I2S_CR1_CSTART); + if (ret < 0) { + dev_err(cpu_dai->dev, "Error %d starting I2S\n", ret); + return ret; + } + +- regmap_update_bits(i2s->regmap, STM32_I2S_IFCR_REG, +- I2S_IFCR_MASK, I2S_IFCR_MASK); ++ regmap_write_bits(i2s->regmap, STM32_I2S_IFCR_REG, ++ I2S_IFCR_MASK, I2S_IFCR_MASK); + ++ spin_lock(&i2s->lock_fd); ++ i2s->refcount++; + if (playback_flg) { + ier = I2S_IER_UDRIE; + } else { + ier = I2S_IER_OVRIE; + +- spin_lock(&i2s->lock_fd); + if (i2s->refcount == 1) + /* dummy write to trigger capture */ + regmap_write(i2s->regmap, + STM32_I2S_TXDR_REG, 0); +- spin_unlock(&i2s->lock_fd); + } ++ spin_unlock(&i2s->lock_fd); + + if (STM32_I2S_IS_SLAVE(i2s)) + ier |= I2S_IER_TIFREIE; +@@ -627,6 +654,9 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ dev_dbg(cpu_dai->dev, "stop I2S %s\n", ++ playback_flg ? "playback" : "capture"); ++ + if (playback_flg) + regmap_update_bits(i2s->regmap, STM32_I2S_IER_REG, + I2S_IER_UDRIE, +@@ -642,16 +672,15 @@ static int stm32_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + spin_unlock(&i2s->lock_fd); + break; + } +- spin_unlock(&i2s->lock_fd); +- +- dev_dbg(cpu_dai->dev, "stop I2S\n"); + + ret = regmap_update_bits(i2s->regmap, STM32_I2S_CR1_REG, + I2S_CR1_SPE, 0); + if (ret < 0) { + dev_err(cpu_dai->dev, "Error %d disabling I2S\n", ret); ++ spin_unlock(&i2s->lock_fd); + return ret; + } ++ spin_unlock(&i2s->lock_fd); + + cfg1_mask = I2S_CFG1_RXDMAEN | I2S_CFG1_TXDMAEN; + regmap_update_bits(i2s->regmap, STM32_I2S_CFG1_REG, +@@ -673,6 +702,8 @@ static void stm32_i2s_shutdown(struct snd_pcm_substream *substream, + + regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, + I2S_CGFR_MCKOE, (unsigned int)~I2S_CGFR_MCKOE); ++ ++ clk_disable_unprepare(i2s->i2sclk); + } + + static int stm32_i2s_dai_probe(struct snd_soc_dai *cpu_dai) +@@ -698,11 +729,12 @@ static const struct regmap_config stm32_h7_i2s_regmap_conf = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, +- .max_register = STM32_I2S_CGFR_REG, ++ .max_register = STM32_I2S_SIDR_REG, + .readable_reg = stm32_i2s_readable_reg, + .volatile_reg = stm32_i2s_volatile_reg, + .writeable_reg = stm32_i2s_writeable_reg, + .fast_io = true, ++ .cache_type = REGCACHE_FLAT, + }; + + static const struct snd_soc_dai_ops stm32_i2s_pcm_dai_ops = { +@@ -717,7 +749,8 @@ static const struct snd_soc_dai_ops stm32_i2s_pcm_dai_ops = { + static const struct snd_pcm_hardware stm32_i2s_pcm_hw = { + .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP, + .buffer_bytes_max = 8 * PAGE_SIZE, +- .period_bytes_max = 2048, ++ .period_bytes_min = 1024, /* 5ms at 48kHz */ ++ .period_bytes_max = PAGE_SIZE, + .periods_min = 2, + .periods_max = 8, + }; +@@ -753,12 +786,8 @@ static int stm32_i2s_dais_init(struct platform_device *pdev, + if (!dai_ptr) + return -ENOMEM; + +- snprintf(i2s->dais_name, STM32_I2S_DAI_NAME_SIZE, +- "%s", dev_name(&pdev->dev)); +- + dai_ptr->probe = stm32_i2s_dai_probe; + dai_ptr->ops = &stm32_i2s_pcm_dai_ops; +- dai_ptr->name = i2s->dais_name; + dai_ptr->id = 1; + stm32_i2s_dai_init(&dai_ptr->playback, "playback"); + stm32_i2s_dai_init(&dai_ptr->capture, "capture"); +@@ -853,6 +882,7 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev, + static int stm32_i2s_probe(struct platform_device *pdev) + { + struct stm32_i2s_data *i2s; ++ u32 val; + int ret; + + i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); +@@ -872,47 +902,50 @@ static int stm32_i2s_probe(struct platform_device *pdev) + if (ret) + return ret; + +- i2s->regmap = devm_regmap_init_mmio(&pdev->dev, i2s->base, +- i2s->regmap_conf); ++ i2s->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "pclk", ++ i2s->base, i2s->regmap_conf); + if (IS_ERR(i2s->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + return PTR_ERR(i2s->regmap); + } + +- ret = clk_prepare_enable(i2s->pclk); +- if (ret) { +- dev_err(&pdev->dev, "Enable pclk failed: %d\n", ret); +- return ret; +- } +- +- ret = clk_prepare_enable(i2s->i2sclk); +- if (ret) { +- dev_err(&pdev->dev, "Enable i2sclk failed: %d\n", ret); +- goto err_pclk_disable; +- } +- + ret = devm_snd_soc_register_component(&pdev->dev, &stm32_i2s_component, + i2s->dai_drv, 1); + if (ret) +- goto err_clocks_disable; ++ return ret; + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, + &stm32_i2s_pcm_config, 0); + if (ret) +- goto err_clocks_disable; ++ return ret; + + /* Set SPI/I2S in i2s mode */ + ret = regmap_update_bits(i2s->regmap, STM32_I2S_CGFR_REG, + I2S_CGFR_I2SMOD, I2S_CGFR_I2SMOD); + if (ret) +- goto err_clocks_disable; ++ return ret; + +- return ret; ++ ret = regmap_read(i2s->regmap, STM32_I2S_IPIDR_REG, &val); ++ if (ret) ++ return ret; + +-err_clocks_disable: +- clk_disable_unprepare(i2s->i2sclk); +-err_pclk_disable: +- clk_disable_unprepare(i2s->pclk); ++ if (val == I2S_IPIDR_NUMBER) { ++ ret = regmap_read(i2s->regmap, STM32_I2S_HWCFGR_REG, &val); ++ if (ret) ++ return ret; ++ ++ if (!FIELD_GET(I2S_HWCFGR_I2S_SUPPORT_MASK, val)) { ++ dev_err(&pdev->dev, ++ "Device does not support i2s mode\n"); ++ return ret; ++ } ++ ++ ret = regmap_read(i2s->regmap, STM32_I2S_VERR_REG, &val); ++ ++ dev_dbg(&pdev->dev, "I2S version: %lu.%lu registered\n", ++ FIELD_GET(I2S_VERR_MAJ_MASK, val), ++ FIELD_GET(I2S_VERR_MIN_MASK, val)); ++ } + + return ret; + } +@@ -929,10 +962,35 @@ static int stm32_i2s_remove(struct platform_device *pdev) + + MODULE_DEVICE_TABLE(of, stm32_i2s_ids); + ++#ifdef CONFIG_PM_SLEEP ++static int stm32_i2s_suspend(struct device *dev) ++{ ++ struct stm32_i2s_data *i2s = dev_get_drvdata(dev); ++ ++ regcache_cache_only(i2s->regmap, true); ++ regcache_mark_dirty(i2s->regmap); ++ ++ return 0; ++} ++ ++static int stm32_i2s_resume(struct device *dev) ++{ ++ struct stm32_i2s_data *i2s = dev_get_drvdata(dev); ++ ++ regcache_cache_only(i2s->regmap, false); ++ return regcache_sync(i2s->regmap); ++} ++#endif /* CONFIG_PM_SLEEP */ ++ ++static const struct dev_pm_ops stm32_i2s_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(stm32_i2s_suspend, stm32_i2s_resume) ++}; ++ + static struct platform_driver stm32_i2s_driver = { + .driver = { + .name = "st,stm32-i2s", + .of_match_table = stm32_i2s_ids, ++ .pm = &stm32_i2s_pm_ops, + }, + .probe = stm32_i2s_probe, + .remove = stm32_i2s_remove, +diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c +index f226542..1426d90 100644 +--- a/sound/soc/stm/stm32_sai.c ++++ b/sound/soc/stm/stm32_sai.c +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + #include + + #include +@@ -29,13 +30,20 @@ + #include "stm32_sai.h" + + static const struct stm32_sai_conf stm32_sai_conf_f4 = { +- .version = SAI_STM32F4, +- .has_spdif = false, ++ .version = STM_SAI_STM32F4, ++ .fifo_size = 8, ++ .has_spdif_pdm = false, + }; + ++/* ++ * Default settings for stm32 H7 socs and next. ++ * These default settings will be overridden if the soc provides ++ * support of hardware configuration registers. ++ */ + static const struct stm32_sai_conf stm32_sai_conf_h7 = { +- .version = SAI_STM32H7, +- .has_spdif = true, ++ .version = STM_SAI_STM32H7, ++ .fifo_size = 8, ++ .has_spdif_pdm = true, + }; + + static const struct of_device_id stm32_sai_ids[] = { +@@ -130,6 +138,8 @@ static int stm32_sai_probe(struct platform_device *pdev) + struct reset_control *rst; + struct resource *res; + const struct of_device_id *of_id; ++ u32 val; ++ int ret; + + sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); + if (!sai) +@@ -142,7 +152,8 @@ static int stm32_sai_probe(struct platform_device *pdev) + + of_id = of_match_device(stm32_sai_ids, &pdev->dev); + if (of_id) +- sai->conf = (struct stm32_sai_conf *)of_id->data; ++ memcpy(&sai->conf, (const struct stm32_sai_conf *)of_id->data, ++ sizeof(struct stm32_sai_conf)); + else + return -EINVAL; + +@@ -181,6 +192,30 @@ static int stm32_sai_probe(struct platform_device *pdev) + reset_control_deassert(rst); + } + ++ /* Enable peripheral clock to allow register access */ ++ ret = clk_prepare_enable(sai->pclk); ++ if (ret) { ++ dev_err(&pdev->dev, "failed to enable clock: %d\n", ret); ++ return ret; ++ } ++ ++ val = FIELD_GET(SAI_IDR_ID_MASK, ++ readl_relaxed(sai->base + STM_SAI_IDR)); ++ if (val == SAI_IPIDR_NUMBER) { ++ val = readl_relaxed(sai->base + STM_SAI_HWCFGR); ++ sai->conf.fifo_size = FIELD_GET(SAI_HWCFGR_FIFO_SIZE, val); ++ sai->conf.has_spdif_pdm = !!FIELD_GET(SAI_HWCFGR_SPDIF_PDM, ++ val); ++ ++ val = readl_relaxed(sai->base + STM_SAI_VERR); ++ sai->conf.version = val; ++ ++ dev_dbg(&pdev->dev, "SAI version: %lu.%lu registered\n", ++ FIELD_GET(SAI_VERR_MAJ_MASK, val), ++ FIELD_GET(SAI_VERR_MIN_MASK, val)); ++ } ++ clk_disable_unprepare(sai->pclk); ++ + sai->pdev = pdev; + sai->set_sync = &stm32_sai_set_sync; + platform_set_drvdata(pdev, sai); +@@ -188,12 +223,34 @@ static int stm32_sai_probe(struct platform_device *pdev) + return devm_of_platform_populate(&pdev->dev); + } + ++#ifdef CONFIG_PM_SLEEP ++/* ++ * When pins are shared by two sai sub instances, pins have to be defined ++ * in sai parent node. In this case, pins state is not managed by alsa fw. ++ * These pins are managed in suspend/resume callbacks. ++ */ ++static int stm32_sai_suspend(struct device *dev) ++{ ++ return pinctrl_pm_select_sleep_state(dev); ++} ++ ++static int stm32_sai_resume(struct device *dev) ++{ ++ return pinctrl_pm_select_default_state(dev); ++} ++#endif /* CONFIG_PM_SLEEP */ ++ ++static const struct dev_pm_ops stm32_sai_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(stm32_sai_suspend, stm32_sai_resume) ++}; ++ + MODULE_DEVICE_TABLE(of, stm32_sai_ids); + + static struct platform_driver stm32_sai_driver = { + .driver = { + .name = "st,stm32-sai", + .of_match_table = stm32_sai_ids, ++ .pm = &stm32_sai_pm_ops, + }, + .probe = stm32_sai_probe, + }; +diff --git a/sound/soc/stm/stm32_sai.h b/sound/soc/stm/stm32_sai.h +index f254221..042f94f 100644 +--- a/sound/soc/stm/stm32_sai.h ++++ b/sound/soc/stm/stm32_sai.h +@@ -37,6 +37,12 @@ + #define STM_SAI_PDMCR_REGX 0x40 + #define STM_SAI_PDMLY_REGX 0x44 + ++/* Hardware configuration registers */ ++#define STM_SAI_HWCFGR 0x3F0 ++#define STM_SAI_VERR 0x3F4 ++#define STM_SAI_IDR 0x3F8 ++#define STM_SAI_SIDR 0x3FC ++ + /******************** Bit definition for SAI_GCR register *******************/ + #define SAI_GCR_SYNCIN_SHIFT 0 + #define SAI_GCR_SYNCIN_WDTH 2 +@@ -82,7 +88,7 @@ + #define SAI_XCR1_NODIV BIT(SAI_XCR1_NODIV_SHIFT) + + #define SAI_XCR1_MCKDIV_SHIFT 20 +-#define SAI_XCR1_MCKDIV_WIDTH(x) (((x) == SAI_STM32F4) ? 4 : 6) ++#define SAI_XCR1_MCKDIV_WIDTH(x) (((x) == STM_SAI_STM32F4) ? 4 : 6) + #define SAI_XCR1_MCKDIV_MASK(x) GENMASK((SAI_XCR1_MCKDIV_SHIFT + (x) - 1),\ + SAI_XCR1_MCKDIV_SHIFT) + #define SAI_XCR1_MCKDIV_SET(x) ((x) << SAI_XCR1_MCKDIV_SHIFT) +@@ -91,6 +97,9 @@ + #define SAI_XCR1_OSR_SHIFT 26 + #define SAI_XCR1_OSR BIT(SAI_XCR1_OSR_SHIFT) + ++#define SAI_XCR1_MCKEN_SHIFT 27 ++#define SAI_XCR1_MCKEN BIT(SAI_XCR1_MCKEN_SHIFT) ++ + /******************* Bit definition for SAI_XCR2 register *******************/ + #define SAI_XCR2_FTH_SHIFT 0 + #define SAI_XCR2_FTH_MASK GENMASK(2, SAI_XCR2_FTH_SHIFT) +@@ -231,8 +240,33 @@ + #define SAI_PDMDLY_4R_MASK GENMASK(30, SAI_PDMDLY_4R_SHIFT) + #define SAI_PDMDLY_4R_WIDTH 3 + +-#define STM_SAI_IS_F4(ip) ((ip)->conf->version == SAI_STM32F4) +-#define STM_SAI_IS_H7(ip) ((ip)->conf->version == SAI_STM32H7) ++/* Registers below apply to SAI version 2.1 and more */ ++ ++/* Bit definition for SAI_HWCFGR register */ ++#define SAI_HWCFGR_FIFO_SIZE GENMASK(7, 0) ++#define SAI_HWCFGR_SPDIF_PDM GENMASK(11, 8) ++#define SAI_HWCFGR_REGOUT GENMASK(19, 12) ++ ++/* Bit definition for SAI_VERR register */ ++#define SAI_VERR_MIN_MASK GENMASK(3, 0) ++#define SAI_VERR_MAJ_MASK GENMASK(7, 4) ++ ++/* Bit definition for SAI_IDR register */ ++#define SAI_IDR_ID_MASK GENMASK(31, 0) ++ ++/* Bit definition for SAI_SIDR register */ ++#define SAI_SIDR_ID_MASK GENMASK(31, 0) ++ ++#define SAI_IPIDR_NUMBER 0x00130031 ++ ++/* SAI version numbers are 1.x for F4. Major version number set to 1 for F4 */ ++#define STM_SAI_STM32F4 BIT(4) ++/* Dummy version number for H7 socs and next */ ++#define STM_SAI_STM32H7 0x0 ++ ++#define STM_SAI_IS_F4(ip) ((ip)->conf.version == STM_SAI_STM32F4) ++#define STM_SAI_HAS_SPDIF_PDM(ip)\ ++ ((ip)->pdata->conf.has_spdif_pdm) + + enum stm32_sai_syncout { + STM_SAI_SYNC_OUT_NONE, +@@ -240,19 +274,16 @@ enum stm32_sai_syncout { + STM_SAI_SYNC_OUT_B, + }; + +-enum stm32_sai_version { +- SAI_STM32F4, +- SAI_STM32H7 +-}; +- + /** + * struct stm32_sai_conf - SAI configuration + * @version: SAI version +- * @has_spdif: SAI S/PDIF support flag ++ * @fifo_size: SAI fifo size as words number ++ * @has_spdif_pdm: SAI S/PDIF and PDM features support flag + */ + struct stm32_sai_conf { +- int version; +- bool has_spdif; ++ u32 version; ++ u32 fifo_size; ++ bool has_spdif_pdm; + }; + + /** +@@ -262,7 +293,7 @@ struct stm32_sai_conf { + * @pclk: SAI bus clock + * @clk_x8k: SAI parent clock for sampling frequencies multiple of 8kHz + * @clk_x11k: SAI parent clock for sampling frequencies multiple of 11kHz +- * @version: SOC version ++ * @conf: SAI hardware capabitilites + * @irq: SAI interrupt line + * @set_sync: pointer to synchro mode configuration callback + */ +@@ -272,7 +303,7 @@ struct stm32_sai_data { + struct clk *pclk; + struct clk *clk_x8k; + struct clk *clk_x11k; +- struct stm32_sai_conf *conf; ++ struct stm32_sai_conf conf; + int irq; + int (*set_sync)(struct stm32_sai_data *sai, + struct device_node *np_provider, int synco, int synci); +diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c +index 06fba96..1f23ca4 100644 +--- a/sound/soc/stm/stm32_sai_sub.c ++++ b/sound/soc/stm/stm32_sai_sub.c +@@ -17,6 +17,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -44,7 +45,6 @@ + #define SAI_DATASIZE_24 0x6 + #define SAI_DATASIZE_32 0x7 + +-#define STM_SAI_FIFO_SIZE 8 + #define STM_SAI_DAI_NAME_SIZE 15 + + #define STM_SAI_IS_PLAYBACK(ip) ((ip)->dir == SNDRV_PCM_STREAM_PLAYBACK) +@@ -62,12 +62,15 @@ + #define SAI_SYNC_EXTERNAL 0x2 + + #define STM_SAI_PROTOCOL_IS_SPDIF(ip) ((ip)->spdif) +-#define STM_SAI_HAS_SPDIF(x) ((x)->pdata->conf->has_spdif) ++#define STM_SAI_HAS_SPDIF(x) ((x)->pdata->conf.has_spdif_pdm) ++#define STM_SAI_HAS_PDM(x) ((x)->pdata->conf.has_spdif_pdm) + #define STM_SAI_HAS_EXT_SYNC(x) (!STM_SAI_IS_F4(sai->pdata)) + + #define SAI_IEC60958_BLOCK_FRAMES 192 + #define SAI_IEC60958_STATUS_BYTES 24 + ++#define SAI_MCLK_NAME_LEN 32 ++ + /** + * struct stm32_sai_sub_data - private data of SAI sub block (block A or B) + * @pdev: device data pointer +@@ -80,6 +83,7 @@ + * @pdata: SAI block parent data pointer + * @np_sync_provider: synchronization provider node + * @sai_ck: kernel clock feeding the SAI clock generator ++ * @sai_mclk: master clock from SAI mclk provider + * @phys_addr: SAI registers physical base address + * @mclk_rate: SAI block master clock frequency (Hz). set at init + * @id: SAI sub block id corresponding to sub-block A or B +@@ -110,6 +114,7 @@ struct stm32_sai_sub_data { + struct stm32_sai_data *pdata; + struct device_node *np_sync_provider; + struct clk *sai_ck; ++ struct clk *sai_mclk; + dma_addr_t phys_addr; + unsigned int mclk_rate; + unsigned int id; +@@ -151,6 +156,10 @@ static bool stm32_sai_sub_readable_reg(struct device *dev, unsigned int reg) + case STM_SAI_DR_REGX: + case STM_SAI_PDMCR_REGX: + case STM_SAI_PDMLY_REGX: ++ case STM_SAI_HWCFGR: ++ case STM_SAI_VERR: ++ case STM_SAI_IDR: ++ case STM_SAI_SIDR: + return true; + default: + return false; +@@ -161,6 +170,7 @@ static bool stm32_sai_sub_volatile_reg(struct device *dev, unsigned int reg) + { + switch (reg) { + case STM_SAI_DR_REGX: ++ case STM_SAI_SR_REGX: + return true; + default: + return false; +@@ -175,7 +185,6 @@ static bool stm32_sai_sub_writeable_reg(struct device *dev, unsigned int reg) + case STM_SAI_FRCR_REGX: + case STM_SAI_SLOTR_REGX: + case STM_SAI_IMR_REGX: +- case STM_SAI_SR_REGX: + case STM_SAI_CLRFR_REGX: + case STM_SAI_DR_REGX: + case STM_SAI_PDMCR_REGX: +@@ -195,6 +204,7 @@ static const struct regmap_config stm32_sai_sub_regmap_config_f4 = { + .volatile_reg = stm32_sai_sub_volatile_reg, + .writeable_reg = stm32_sai_sub_writeable_reg, + .fast_io = true, ++ .cache_type = REGCACHE_FLAT, + }; + + static const struct regmap_config stm32_sai_sub_regmap_config_h7 = { +@@ -206,6 +216,7 @@ static const struct regmap_config stm32_sai_sub_regmap_config_h7 = { + .volatile_reg = stm32_sai_sub_volatile_reg, + .writeable_reg = stm32_sai_sub_writeable_reg, + .fast_io = true, ++ .cache_type = REGCACHE_FLAT, + }; + + static int snd_pcm_iec958_info(struct snd_kcontrol *kcontrol, +@@ -251,6 +262,175 @@ static const struct snd_kcontrol_new iec958_ctls = { + .put = snd_pcm_iec958_put, + }; + ++struct stm32_sai_mclk_data { ++ struct clk_hw hw; ++ unsigned long freq; ++ struct stm32_sai_sub_data *sai_data; ++}; ++ ++#define to_mclk_data(_hw) container_of(_hw, struct stm32_sai_mclk_data, hw) ++#define STM32_SAI_MAX_CLKS 1 ++ ++static int stm32_sai_get_clk_div(struct stm32_sai_sub_data *sai, ++ unsigned long input_rate, ++ unsigned long output_rate) ++{ ++ int version = sai->pdata->conf.version; ++ int div; ++ ++ div = DIV_ROUND_CLOSEST(input_rate, output_rate); ++ if (div > SAI_XCR1_MCKDIV_MAX(version)) { ++ dev_err(&sai->pdev->dev, "Divider %d out of range\n", div); ++ return -EINVAL; ++ } ++ dev_dbg(&sai->pdev->dev, "SAI divider %d\n", div); ++ ++ if (input_rate % div) ++ dev_dbg(&sai->pdev->dev, ++ "Rate not accurate. requested (%ld), actual (%ld)\n", ++ output_rate, input_rate / div); ++ ++ return div; ++} ++ ++static int stm32_sai_set_clk_div(struct stm32_sai_sub_data *sai, ++ unsigned int div) ++{ ++ int version = sai->pdata->conf.version; ++ int ret, cr1, mask; ++ ++ if (div > SAI_XCR1_MCKDIV_MAX(version)) { ++ dev_err(&sai->pdev->dev, "Divider %d out of range\n", div); ++ return -EINVAL; ++ } ++ ++ mask = SAI_XCR1_MCKDIV_MASK(SAI_XCR1_MCKDIV_WIDTH(version)); ++ cr1 = SAI_XCR1_MCKDIV_SET(div); ++ ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, mask, cr1); ++ if (ret < 0) ++ dev_err(&sai->pdev->dev, "Failed to update CR1 register\n"); ++ ++ return ret; ++} ++ ++static long stm32_sai_mclk_round_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long *prate) ++{ ++ struct stm32_sai_mclk_data *mclk = to_mclk_data(hw); ++ struct stm32_sai_sub_data *sai = mclk->sai_data; ++ int div; ++ ++ div = stm32_sai_get_clk_div(sai, *prate, rate); ++ if (div < 0) ++ return div; ++ ++ mclk->freq = *prate / div; ++ ++ return mclk->freq; ++} ++ ++static unsigned long stm32_sai_mclk_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ struct stm32_sai_mclk_data *mclk = to_mclk_data(hw); ++ ++ return mclk->freq; ++} ++ ++static int stm32_sai_mclk_set_rate(struct clk_hw *hw, unsigned long rate, ++ unsigned long parent_rate) ++{ ++ struct stm32_sai_mclk_data *mclk = to_mclk_data(hw); ++ struct stm32_sai_sub_data *sai = mclk->sai_data; ++ int div, ret; ++ ++ div = stm32_sai_get_clk_div(sai, parent_rate, rate); ++ if (div < 0) ++ return div; ++ ++ ret = stm32_sai_set_clk_div(sai, div); ++ if (ret) ++ return ret; ++ ++ mclk->freq = rate; ++ ++ return 0; ++} ++ ++static int stm32_sai_mclk_enable(struct clk_hw *hw) ++{ ++ struct stm32_sai_mclk_data *mclk = to_mclk_data(hw); ++ struct stm32_sai_sub_data *sai = mclk->sai_data; ++ ++ dev_dbg(&sai->pdev->dev, "Enable master clock\n"); ++ ++ return regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, ++ SAI_XCR1_MCKEN, SAI_XCR1_MCKEN); ++} ++ ++static void stm32_sai_mclk_disable(struct clk_hw *hw) ++{ ++ struct stm32_sai_mclk_data *mclk = to_mclk_data(hw); ++ struct stm32_sai_sub_data *sai = mclk->sai_data; ++ ++ dev_dbg(&sai->pdev->dev, "Disable master clock\n"); ++ ++ regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, SAI_XCR1_MCKEN, 0); ++} ++ ++static const struct clk_ops mclk_ops = { ++ .enable = stm32_sai_mclk_enable, ++ .disable = stm32_sai_mclk_disable, ++ .recalc_rate = stm32_sai_mclk_recalc_rate, ++ .round_rate = stm32_sai_mclk_round_rate, ++ .set_rate = stm32_sai_mclk_set_rate, ++}; ++ ++static int stm32_sai_add_mclk_provider(struct stm32_sai_sub_data *sai) ++{ ++ struct clk_hw *hw; ++ struct stm32_sai_mclk_data *mclk; ++ struct device *dev = &sai->pdev->dev; ++ const char *pname = __clk_get_name(sai->sai_ck); ++ char *mclk_name, *p, *s = (char *)pname; ++ int ret, i = 0; ++ ++ mclk = devm_kzalloc(dev, sizeof(*mclk), GFP_KERNEL); ++ if (!mclk) ++ return -ENOMEM; ++ ++ mclk_name = devm_kcalloc(dev, sizeof(char), ++ SAI_MCLK_NAME_LEN, GFP_KERNEL); ++ if (!mclk_name) ++ return -ENOMEM; ++ ++ /* ++ * Forge mclk clock name from parent clock name and suffix. ++ * String after "_" char is stripped in parent name. ++ */ ++ p = mclk_name; ++ while (*s && *s != '_' && (i < (SAI_MCLK_NAME_LEN - 7))) { ++ *p++ = *s++; ++ i++; ++ } ++ STM_SAI_IS_SUB_A(sai) ? strcat(p, "a_mclk") : strcat(p, "b_mclk"); ++ ++ mclk->hw.init = CLK_HW_INIT(mclk_name, pname, &mclk_ops, 0); ++ mclk->sai_data = sai; ++ hw = &mclk->hw; ++ ++ dev_dbg(dev, "Register master clock %s\n", mclk_name); ++ ret = devm_clk_hw_register(&sai->pdev->dev, hw); ++ if (ret) { ++ dev_err(dev, "mclk register returned %d\n", ret); ++ return ret; ++ } ++ sai->sai_mclk = hw->clk; ++ ++ /* register mclk provider */ ++ return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw); ++} ++ + static irqreturn_t stm32_sai_isr(int irq, void *devid) + { + struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid; +@@ -265,8 +445,8 @@ static irqreturn_t stm32_sai_isr(int irq, void *devid) + if (!flags) + return IRQ_NONE; + +- regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK, +- SAI_XCLRFR_MASK); ++ regmap_write_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK, ++ SAI_XCLRFR_MASK); + + if (!sai->substream) { + dev_err(&pdev->dev, "Device stopped. Spurious IRQ 0x%x\n", sr); +@@ -312,15 +492,25 @@ static int stm32_sai_set_sysclk(struct snd_soc_dai *cpu_dai, + struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); + int ret; + +- if ((dir == SND_SOC_CLOCK_OUT) && sai->master) { ++ if (dir == SND_SOC_CLOCK_OUT) { + ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, + SAI_XCR1_NODIV, + (unsigned int)~SAI_XCR1_NODIV); + if (ret < 0) + return ret; + +- sai->mclk_rate = freq; + dev_dbg(cpu_dai->dev, "SAI MCLK frequency is %uHz\n", freq); ++ sai->mclk_rate = freq; ++ ++ if (sai->sai_mclk) { ++ ret = clk_set_rate_exclusive(sai->sai_mclk, ++ sai->mclk_rate); ++ if (ret) { ++ dev_err(cpu_dai->dev, ++ "Could not set mclk rate\n"); ++ return ret; ++ } ++ } + } + + return 0; +@@ -505,9 +695,8 @@ static int stm32_sai_startup(struct snd_pcm_substream *substream, + } + + /* Enable ITs */ +- +- regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, +- SAI_XCLRFR_MASK, SAI_XCLRFR_MASK); ++ regmap_write_bits(sai->regmap, STM_SAI_CLRFR_REGX, ++ SAI_XCLRFR_MASK, SAI_XCLRFR_MASK); + + imr = SAI_XIMR_OVRUDRIE; + if (STM_SAI_IS_CAPTURE(sai)) { +@@ -539,10 +728,10 @@ static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai, + * SAI fifo threshold is set to half fifo, to keep enough space + * for DMA incoming bursts. + */ +- regmap_update_bits(sai->regmap, STM_SAI_CR2_REGX, +- SAI_XCR2_FFLUSH | SAI_XCR2_FTH_MASK, +- SAI_XCR2_FFLUSH | +- SAI_XCR2_FTH_SET(STM_SAI_FIFO_TH_HALF)); ++ regmap_write_bits(sai->regmap, STM_SAI_CR2_REGX, ++ SAI_XCR2_FFLUSH | SAI_XCR2_FTH_MASK, ++ SAI_XCR2_FFLUSH | ++ SAI_XCR2_FTH_SET(STM_SAI_FIFO_TH_HALF)); + + /* DS bits in CR1 not set for SPDIF (size forced to 24 bits).*/ + if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { +@@ -715,15 +904,9 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, + { + struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); + int cr1, mask, div = 0; +- int sai_clk_rate, mclk_ratio, den, ret; +- int version = sai->pdata->conf->version; ++ int sai_clk_rate, mclk_ratio, den; + unsigned int rate = params_rate(params); + +- if (!sai->mclk_rate) { +- dev_err(cpu_dai->dev, "Mclk rate is null\n"); +- return -EINVAL; +- } +- + if (!(rate % 11025)) + clk_set_parent(sai->sai_ck, sai->pdata->clk_x11k); + else +@@ -731,14 +914,22 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, + sai_clk_rate = clk_get_rate(sai->sai_ck); + + if (STM_SAI_IS_F4(sai->pdata)) { +- /* +- * mclk_rate = 256 * fs +- * MCKDIV = 0 if sai_ck < 3/2 * mclk_rate +- * MCKDIV = sai_ck / (2 * mclk_rate) otherwise ++ /* mclk on (NODIV=0) ++ * mclk_rate = 256 * fs ++ * MCKDIV = 0 if sai_ck < 3/2 * mclk_rate ++ * MCKDIV = sai_ck / (2 * mclk_rate) otherwise ++ * mclk off (NODIV=1) ++ * MCKDIV ignored. sck = sai_ck + */ +- if (2 * sai_clk_rate >= 3 * sai->mclk_rate) +- div = DIV_ROUND_CLOSEST(sai_clk_rate, +- 2 * sai->mclk_rate); ++ if (!sai->mclk_rate) ++ return 0; ++ ++ if (2 * sai_clk_rate >= 3 * sai->mclk_rate) { ++ div = stm32_sai_get_clk_div(sai, sai_clk_rate, ++ 2 * sai->mclk_rate); ++ if (div < 0) ++ return div; ++ } + } else { + /* + * TDM mode : +@@ -750,8 +941,10 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, + * Note: NOMCK/NODIV correspond to same bit. + */ + if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { +- div = DIV_ROUND_CLOSEST(sai_clk_rate, +- (params_rate(params) * 128)); ++ div = stm32_sai_get_clk_div(sai, sai_clk_rate, ++ rate * 128); ++ if (div < 0) ++ return div; + } else { + if (sai->mclk_rate) { + mclk_ratio = sai->mclk_rate / rate; +@@ -764,31 +957,22 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, + mclk_ratio); + return -EINVAL; + } +- div = DIV_ROUND_CLOSEST(sai_clk_rate, +- sai->mclk_rate); ++ div = stm32_sai_get_clk_div(sai, sai_clk_rate, ++ sai->mclk_rate); ++ if (div < 0) ++ return div; + } else { + /* mclk-fs not set, master clock not active */ + den = sai->fs_length * params_rate(params); +- div = DIV_ROUND_CLOSEST(sai_clk_rate, den); ++ div = stm32_sai_get_clk_div(sai, sai_clk_rate, ++ den); ++ if (div < 0) ++ return div; + } + } + } + +- if (div > SAI_XCR1_MCKDIV_MAX(version)) { +- dev_err(cpu_dai->dev, "Divider %d out of range\n", div); +- return -EINVAL; +- } +- dev_dbg(cpu_dai->dev, "SAI clock %d, divider %d\n", sai_clk_rate, div); +- +- mask = SAI_XCR1_MCKDIV_MASK(SAI_XCR1_MCKDIV_WIDTH(version)); +- cr1 = SAI_XCR1_MCKDIV_SET(div); +- ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, mask, cr1); +- if (ret < 0) { +- dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); +- return ret; +- } +- +- return 0; ++ return stm32_sai_set_clk_div(sai, div); + } + + static int stm32_sai_hw_params(struct snd_pcm_substream *substream, +@@ -881,6 +1065,9 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream, + SAI_XCR1_NODIV); + + clk_disable_unprepare(sai->sai_ck); ++ ++ clk_rate_exclusive_put(sai->sai_mclk); ++ + sai->substream = NULL; + } + +@@ -888,11 +1075,12 @@ static int stm32_sai_pcm_new(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_dai *cpu_dai) + { + struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev); ++ struct snd_kcontrol_new knew = iec958_ctls; + + if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { + dev_dbg(&sai->pdev->dev, "%s: register iec controls", __func__); +- return snd_ctl_add(rtd->pcm->card, +- snd_ctl_new1(&iec958_ctls, sai)); ++ knew.device = rtd->pcm->device; ++ return snd_ctl_add(rtd->pcm->card, snd_ctl_new1(&knew, sai)); + } + + return 0; +@@ -903,6 +1091,8 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) + struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev); + int cr1 = 0, cr1_mask; + ++ sai->cpu_dai = cpu_dai; ++ + sai->dma_params.addr = (dma_addr_t)(sai->phys_addr + STM_SAI_DR_REGX); + /* + * DMA supports 4, 8 or 16 burst sizes. Burst size 4 is the best choice, +@@ -910,6 +1100,8 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai) + * constraints). + */ + sai->dma_params.maxburst = 4; ++ if (sai->pdata->conf.fifo_size < 8) ++ sai->dma_params.maxburst = 1; + /* Buswidth will be set by framework at runtime */ + sai->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED; + +@@ -1080,7 +1272,7 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, + + sai->regmap_config = &stm32_sai_sub_regmap_config_f4; + /* Note: PDM registers not available for H7 sub-block B */ +- if (STM_SAI_IS_H7(sai->pdata) && STM_SAI_IS_SUB_A(sai)) ++ if (STM_SAI_HAS_PDM(sai) && STM_SAI_IS_SUB_A(sai)) + sai->regmap_config = &stm32_sai_sub_regmap_config_h7; + + sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "sai_ck", +@@ -1182,6 +1374,23 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev, + return PTR_ERR(sai->sai_ck); + } + ++ if (STM_SAI_IS_F4(sai->pdata)) ++ return 0; ++ ++ /* Register mclk provider if requested */ ++ if (of_find_property(np, "#clock-cells", NULL)) { ++ ret = stm32_sai_add_mclk_provider(sai); ++ if (ret < 0) ++ return ret; ++ } else { ++ sai->sai_mclk = devm_clk_get(&pdev->dev, "MCLK"); ++ if (IS_ERR(sai->sai_mclk)) { ++ if (PTR_ERR(sai->sai_mclk) != -ENOENT) ++ return PTR_ERR(sai->sai_mclk); ++ sai->sai_mclk = NULL; ++ } ++ } ++ + return 0; + } + +@@ -1266,10 +1475,34 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) + return 0; + } + ++#ifdef CONFIG_PM_SLEEP ++static int stm32_sai_sub_suspend(struct device *dev) ++{ ++ struct stm32_sai_sub_data *sai = dev_get_drvdata(dev); ++ ++ regcache_cache_only(sai->regmap, true); ++ regcache_mark_dirty(sai->regmap); ++ return 0; ++} ++ ++static int stm32_sai_sub_resume(struct device *dev) ++{ ++ struct stm32_sai_sub_data *sai = dev_get_drvdata(dev); ++ ++ regcache_cache_only(sai->regmap, false); ++ return regcache_sync(sai->regmap); ++} ++#endif /* CONFIG_PM_SLEEP */ ++ ++static const struct dev_pm_ops stm32_sai_sub_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(stm32_sai_sub_suspend, stm32_sai_sub_resume) ++}; ++ + static struct platform_driver stm32_sai_sub_driver = { + .driver = { + .name = "st,stm32-sai-sub", + .of_match_table = stm32_sai_sub_ids, ++ .pm = &stm32_sai_sub_pm_ops, + }, + .probe = stm32_sai_sub_probe, + }; +diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c +index 373df4f..693e30d 100644 +--- a/sound/soc/stm/stm32_spdifrx.c ++++ b/sound/soc/stm/stm32_spdifrx.c +@@ -16,11 +16,13 @@ + * details. + */ + ++#include + #include + #include + #include + #include + #include ++#include + #include + #include + +@@ -35,6 +37,9 @@ + #define STM32_SPDIFRX_DR 0x10 + #define STM32_SPDIFRX_CSR 0x14 + #define STM32_SPDIFRX_DIR 0x18 ++#define STM32_SPDIFRX_VERR 0x3F4 ++#define STM32_SPDIFRX_IDR 0x3F8 ++#define STM32_SPDIFRX_SIDR 0x3FC + + /* Bit definition for SPDIF_CR register */ + #define SPDIFRX_CR_SPDIFEN_SHIFT 0 +@@ -168,6 +173,18 @@ + #define SPDIFRX_SPDIFEN_SYNC 0x1 + #define SPDIFRX_SPDIFEN_ENABLE 0x3 + ++/* Bit definition for SPDIFRX_VERR register */ ++#define SPDIFRX_VERR_MIN_MASK GENMASK(3, 0) ++#define SPDIFRX_VERR_MAJ_MASK GENMASK(7, 4) ++ ++/* Bit definition for SPDIFRX_IDR register */ ++#define SPDIFRX_IDR_ID_MASK GENMASK(31, 0) ++ ++/* Bit definition for SPDIFRX_SIDR register */ ++#define SPDIFRX_SIDR_SID_MASK GENMASK(31, 0) ++ ++#define SPDIFRX_IPIDR_NUMBER 0x00130041 ++ + #define SPDIFRX_IN1 0x1 + #define SPDIFRX_IN2 0x2 + #define SPDIFRX_IN3 0x3 +@@ -471,6 +488,8 @@ static int stm32_spdifrx_get_ctrl_data(struct stm32_spdifrx_data *spdifrx) + memset(spdifrx->cs, 0, SPDIFRX_CS_BYTES_NB); + memset(spdifrx->ub, 0, SPDIFRX_UB_BYTES_NB); + ++ pinctrl_pm_select_default_state(&spdifrx->pdev->dev); ++ + ret = stm32_spdifrx_dma_ctrl_start(spdifrx); + if (ret < 0) + return ret; +@@ -493,7 +512,7 @@ static int stm32_spdifrx_get_ctrl_data(struct stm32_spdifrx_data *spdifrx) + if (wait_for_completion_interruptible_timeout(&spdifrx->cs_completion, + msecs_to_jiffies(100)) + <= 0) { +- dev_err(&spdifrx->pdev->dev, "Failed to get control data\n"); ++ dev_dbg(&spdifrx->pdev->dev, "Failed to get control data\n"); + ret = -EAGAIN; + } + +@@ -502,6 +521,7 @@ static int stm32_spdifrx_get_ctrl_data(struct stm32_spdifrx_data *spdifrx) + + end: + clk_disable_unprepare(spdifrx->kclk); ++ pinctrl_pm_select_sleep_state(&spdifrx->pdev->dev); + + return ret; + } +@@ -603,6 +623,9 @@ static bool stm32_spdifrx_readable_reg(struct device *dev, unsigned int reg) + case STM32_SPDIFRX_DR: + case STM32_SPDIFRX_CSR: + case STM32_SPDIFRX_DIR: ++ case STM32_SPDIFRX_VERR: ++ case STM32_SPDIFRX_IDR: ++ case STM32_SPDIFRX_SIDR: + return true; + default: + return false; +@@ -611,10 +634,15 @@ static bool stm32_spdifrx_readable_reg(struct device *dev, unsigned int reg) + + static bool stm32_spdifrx_volatile_reg(struct device *dev, unsigned int reg) + { +- if (reg == STM32_SPDIFRX_DR) ++ switch (reg) { ++ case STM32_SPDIFRX_DR: ++ case STM32_SPDIFRX_CSR: ++ case STM32_SPDIFRX_SR: ++ case STM32_SPDIFRX_DIR: + return true; +- +- return false; ++ default: ++ return false; ++ } + } + + static bool stm32_spdifrx_writeable_reg(struct device *dev, unsigned int reg) +@@ -633,11 +661,12 @@ static const struct regmap_config stm32_h7_spdifrx_regmap_conf = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, +- .max_register = STM32_SPDIFRX_DIR, ++ .max_register = STM32_SPDIFRX_SIDR, + .readable_reg = stm32_spdifrx_readable_reg, + .volatile_reg = stm32_spdifrx_volatile_reg, + .writeable_reg = stm32_spdifrx_writeable_reg, + .fast_io = true, ++ .cache_type = REGCACHE_FLAT, + }; + + static irqreturn_t stm32_spdifrx_isr(int irq, void *devid) +@@ -835,7 +864,8 @@ static struct snd_soc_dai_driver stm32_spdifrx_dai[] = { + static const struct snd_pcm_hardware stm32_spdifrx_pcm_hw = { + .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP, + .buffer_bytes_max = 8 * PAGE_SIZE, +- .period_bytes_max = 2048, /* MDMA constraint */ ++ .period_bytes_min = 1024, /* 5ms at 48kHz */ ++ .period_bytes_max = PAGE_SIZE, + .periods_min = 2, + .periods_max = 8, + }; +@@ -901,6 +931,7 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) + struct stm32_spdifrx_data *spdifrx; + struct reset_control *rst; + const struct snd_dmaengine_pcm_config *pcm_config = NULL; ++ u32 ver, idr; + int ret; + + spdifrx = devm_kzalloc(&pdev->dev, sizeof(*spdifrx), GFP_KERNEL); +@@ -957,7 +988,19 @@ static int stm32_spdifrx_probe(struct platform_device *pdev) + goto error; + } + +- return 0; ++ ret = regmap_read(spdifrx->regmap, STM32_SPDIFRX_IDR, &idr); ++ if (ret) ++ goto error; ++ ++ if (idr == SPDIFRX_IPIDR_NUMBER) { ++ ret = regmap_read(spdifrx->regmap, STM32_SPDIFRX_VERR, &ver); ++ ++ dev_dbg(&pdev->dev, "SPDIFRX version: %lu.%lu registered\n", ++ FIELD_GET(SPDIFRX_VERR_MAJ_MASK, ver), ++ FIELD_GET(SPDIFRX_VERR_MIN_MASK, ver)); ++ } ++ ++ return ret; + + error: + if (!IS_ERR(spdifrx->ctrl_chan)) +@@ -983,10 +1026,34 @@ static int stm32_spdifrx_remove(struct platform_device *pdev) + + MODULE_DEVICE_TABLE(of, stm32_spdifrx_ids); + ++#ifdef CONFIG_PM_SLEEP ++static int stm32_spdifrx_suspend(struct device *dev) ++{ ++ struct stm32_spdifrx_data *spdifrx = dev_get_drvdata(dev); ++ ++ regcache_cache_only(spdifrx->regmap, true); ++ regcache_mark_dirty(spdifrx->regmap); ++ return 0; ++} ++ ++static int stm32_spdifrx_resume(struct device *dev) ++{ ++ struct stm32_spdifrx_data *spdifrx = dev_get_drvdata(dev); ++ ++ regcache_cache_only(spdifrx->regmap, false); ++ return regcache_sync(spdifrx->regmap); ++} ++#endif /* CONFIG_PM_SLEEP */ ++ ++static const struct dev_pm_ops stm32_spdifrx_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(stm32_spdifrx_suspend, stm32_spdifrx_resume) ++}; ++ + static struct platform_driver stm32_spdifrx_driver = { + .driver = { + .name = "st,stm32-spdifrx", + .of_match_table = stm32_spdifrx_ids, ++ .pm = &stm32_spdifrx_pm_ops, + }, + .probe = stm32_spdifrx_probe, + .remove = stm32_spdifrx_remove, +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0020-ARM-stm32mp1-r0-rc2-MEDIA.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0020-ARM-stm32mp1-r0-rc2-MEDIA.patch new file mode 100644 index 0000000..ae23aee --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0020-ARM-stm32mp1-r0-rc2-MEDIA.patch @@ -0,0 +1,984 @@ +From 8f36e7dd830ff5d93e240a62da54ea2afc580cca Mon Sep 17 00:00:00 2001 +From: Christophe Priouzeau +Date: Mon, 26 Nov 2018 14:39:57 +0100 +Subject: [PATCH 20/52] ARM-stm32mp1-r0-rc2-MEDIA + +--- + .../devicetree/bindings/media/video-interfaces.txt | 2 + + drivers/media/i2c/ov5640.c | 663 ++++++++++++--------- + drivers/media/platform/stm32/stm32-dcmi.c | 41 +- + drivers/media/v4l2-core/v4l2-fwnode.c | 3 + + include/media/v4l2-fwnode.h | 2 + + 5 files changed, 413 insertions(+), 298 deletions(-) + +diff --git a/Documentation/devicetree/bindings/media/video-interfaces.txt b/Documentation/devicetree/bindings/media/video-interfaces.txt +index baf9d97..fa4c112 100644 +--- a/Documentation/devicetree/bindings/media/video-interfaces.txt ++++ b/Documentation/devicetree/bindings/media/video-interfaces.txt +@@ -147,6 +147,8 @@ Optional endpoint properties + as 0 (normal). This property is valid for serial busses only. + - strobe: Whether the clock signal is used as clock (0) or strobe (1). Used + with CCP2, for instance. ++- pclk-max-frequency: maximum pixel clock frequency admissible by video ++ host interface. + + Example + ------- +diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c +index 30b15e9..27b75e7 100644 +--- a/drivers/media/i2c/ov5640.c ++++ b/drivers/media/i2c/ov5640.c +@@ -66,6 +66,7 @@ + #define OV5640_REG_TIMING_VTS 0x380e + #define OV5640_REG_TIMING_TC_REG20 0x3820 + #define OV5640_REG_TIMING_TC_REG21 0x3821 ++#define OV5640_REG_DVP_PCLK_DIVIDER 0x3824 + #define OV5640_REG_AEC_CTRL00 0x3a00 + #define OV5640_REG_AEC_B50_STEP 0x3a08 + #define OV5640_REG_AEC_B60_STEP 0x3a0a +@@ -261,8 +262,8 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) + static const struct reg_value ov5640_init_setting_30fps_VGA[] = { + {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0}, + {0x3103, 0x03, 0, 0}, {0x3017, 0x00, 0, 0}, {0x3018, 0x00, 0, 0}, +- {0x3034, 0x18, 0, 0}, {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, +- {0x3037, 0x13, 0, 0}, {0x3630, 0x36, 0, 0}, ++ {0x3034, 0x18, 0, 0}, ++ {0x3630, 0x36, 0, 0}, + {0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0}, + {0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0}, + {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0}, +@@ -344,85 +345,8 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = { + {0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3c00, 0x04, 0, 300}, + }; + +-static const struct reg_value ov5640_setting_30fps_VGA_640_480[] = { +- {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, +- {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, +- {0x3814, 0x31, 0, 0}, +- {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, +- {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, +- {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, +- {0x3810, 0x00, 0, 0}, +- {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, +- {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, +- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, +- {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x0e, 0, 0}, +- {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, +- {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, +- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, +- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, +- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0}, +-}; +- +-static const struct reg_value ov5640_setting_15fps_VGA_640_480[] = { +- {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, +- {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, +- {0x3814, 0x31, 0, 0}, +- {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, +- {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, +- {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, +- {0x3810, 0x00, 0, 0}, +- {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, +- {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, +- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, +- {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, +- {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, +- {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, +- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, +- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, +- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, +-}; +- +-static const struct reg_value ov5640_setting_30fps_XGA_1024_768[] = { +- {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, +- {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, +- {0x3814, 0x31, 0, 0}, +- {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, +- {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, +- {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, +- {0x3810, 0x00, 0, 0}, +- {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, +- {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, +- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, +- {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x0e, 0, 0}, +- {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, +- {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, +- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, +- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, +- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0}, +- {0x3035, 0x12, 0, 0}, +-}; +- +-static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = { +- {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, +- {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, +- {0x3814, 0x31, 0, 0}, +- {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, +- {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, +- {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, +- {0x3810, 0x00, 0, 0}, +- {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, +- {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, +- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, +- {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, +- {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, +- {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, +- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, +- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, +- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, +-}; +- +-static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = { +- {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, ++static const struct reg_value ov5640_setting_VGA_640_480[] = { ++ {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, +@@ -436,12 +360,12 @@ static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = { + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, +- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, +- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, ++ {0x4407, 0x04, 0, 0}, ++ {0x5001, 0xa3, 0, 0}, + }; + +-static const struct reg_value ov5640_setting_15fps_QVGA_320_240[] = { +- {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, ++static const struct reg_value ov5640_setting_XGA_1024_768[] = { ++ {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, +@@ -455,12 +379,11 @@ static const struct reg_value ov5640_setting_15fps_QVGA_320_240[] = { + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, +- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, +- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0}, + }; + +-static const struct reg_value ov5640_setting_30fps_QCIF_176_144[] = { +- {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, ++static const struct reg_value ov5640_setting_QVGA_320_240[] = { ++ {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, +@@ -474,12 +397,11 @@ static const struct reg_value ov5640_setting_30fps_QCIF_176_144[] = { + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, +- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, +- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0}, + }; + +-static const struct reg_value ov5640_setting_15fps_QCIF_176_144[] = { +- {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, ++static const struct reg_value ov5640_setting_QCIF_176_144[] = { ++ {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, +@@ -493,12 +415,11 @@ static const struct reg_value ov5640_setting_15fps_QCIF_176_144[] = { + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, +- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, +- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0}, + }; + +-static const struct reg_value ov5640_setting_30fps_NTSC_720_480[] = { +- {0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, ++static const struct reg_value ov5640_setting_NTSC_720_480[] = { ++ {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, +@@ -512,31 +433,11 @@ static const struct reg_value ov5640_setting_30fps_NTSC_720_480[] = { + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, +- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, +- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0}, + }; + +-static const struct reg_value ov5640_setting_15fps_NTSC_720_480[] = { +- {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, +- {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, +- {0x3814, 0x31, 0, 0}, +- {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, +- {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, +- {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, +- {0x3810, 0x00, 0, 0}, +- {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0}, +- {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, +- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, +- {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, +- {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, +- {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, +- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, +- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, +- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, +-}; +- +-static const struct reg_value ov5640_setting_30fps_PAL_720_576[] = { +- {0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, ++static const struct reg_value ov5640_setting_PAL_720_576[] = { ++ {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, +@@ -550,52 +451,11 @@ static const struct reg_value ov5640_setting_30fps_PAL_720_576[] = { + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, +- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, +- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0}, + }; + +-static const struct reg_value ov5640_setting_15fps_PAL_720_576[] = { +- {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, +- {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, +- {0x3814, 0x31, 0, 0}, +- {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, +- {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, +- {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, +- {0x3810, 0x00, 0, 0}, +- {0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, +- {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, +- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, +- {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, +- {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, +- {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, +- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, +- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, +- {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, +-}; +- +-static const struct reg_value ov5640_setting_30fps_720P_1280_720[] = { +- {0x3008, 0x42, 0, 0}, +- {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, +- {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, +- {0x3814, 0x31, 0, 0}, +- {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, +- {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, +- {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, +- {0x3810, 0x00, 0, 0}, +- {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, +- {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, +- {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0}, +- {0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0}, +- {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0}, +- {0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0}, +- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x02, 0, 0}, +- {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, +- {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, {0x4005, 0x1a, 0, 0}, +- {0x3008, 0x02, 0, 0}, {0x3503, 0, 0, 0}, +-}; +- +-static const struct reg_value ov5640_setting_15fps_720P_1280_720[] = { +- {0x3035, 0x41, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, ++static const struct reg_value ov5640_setting_720P_1280_720[] = { ++ {0x3c07, 0x07, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3814, 0x31, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, +@@ -608,47 +468,13 @@ static const struct reg_value ov5640_setting_15fps_720P_1280_720[] = { + {0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0}, + {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0}, + {0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0}, +- {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x02, 0, 0}, +- {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, +- {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, +-}; +- +-static const struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = { +- {0x3008, 0x42, 0, 0}, +- {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0}, +- {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, +- {0x3814, 0x11, 0, 0}, +- {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, +- {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, +- {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, +- {0x3810, 0x00, 0, 0}, +- {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, +- {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0}, +- {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0}, +- {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, +- {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, +- {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, +- {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0}, +- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, +- {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3035, 0x11, 0, 0}, +- {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0}, +- {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, +- {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0}, +- {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0}, +- {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, +- {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0}, +- {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0}, +- {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0}, +- {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0}, +- {0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, +- {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0}, +- {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, +- {0x3503, 0, 0, 0}, ++ {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, ++ {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0}, + }; + +-static const struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = { ++static const struct reg_value ov5640_setting_1080P_1920_1080[] = { + {0x3008, 0x42, 0, 0}, +- {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0}, ++ {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3814, 0x11, 0, 0}, + {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, +@@ -662,9 +488,9 @@ static const struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = { + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0}, +- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, +- {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3035, 0x21, 0, 0}, +- {0x3036, 0x54, 0, 1}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0}, ++ {0x4407, 0x04, 0, 0}, ++ {0x5001, 0x83, 0, 0}, ++ {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0}, + {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0}, +@@ -674,12 +500,11 @@ static const struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = { + {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0}, + {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0}, + {0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, +- {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0}, + {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3503, 0, 0, 0}, + }; + +-static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = { +- {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0}, ++static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = { ++ {0x3c07, 0x08, 0, 0}, + {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, + {0x3814, 0x11, 0, 0}, + {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, +@@ -693,8 +518,8 @@ static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = { + {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, + {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0}, +- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, +- {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70}, ++ {0x4407, 0x04, 0, 0}, ++ {0x5001, 0x83, 0, 70}, + }; + + /* power-on sensor init reg table */ +@@ -705,79 +530,43 @@ static const struct ov5640_mode_info ov5640_mode_init_data = { + }; + + static const struct ov5640_mode_info +-ov5640_mode_data[OV5640_NUM_FRAMERATES][OV5640_NUM_MODES] = { +- { +- {OV5640_MODE_QCIF_176_144, SUBSAMPLING, +- 176, 1896, 144, 984, +- ov5640_setting_15fps_QCIF_176_144, +- ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)}, +- {OV5640_MODE_QVGA_320_240, SUBSAMPLING, +- 320, 1896, 240, 984, +- ov5640_setting_15fps_QVGA_320_240, +- ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)}, +- {OV5640_MODE_VGA_640_480, SUBSAMPLING, +- 640, 1896, 480, 1080, +- ov5640_setting_15fps_VGA_640_480, +- ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)}, +- {OV5640_MODE_NTSC_720_480, SUBSAMPLING, +- 720, 1896, 480, 984, +- ov5640_setting_15fps_NTSC_720_480, +- ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)}, +- {OV5640_MODE_PAL_720_576, SUBSAMPLING, +- 720, 1896, 576, 984, +- ov5640_setting_15fps_PAL_720_576, +- ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)}, +- {OV5640_MODE_XGA_1024_768, SUBSAMPLING, +- 1024, 1896, 768, 1080, +- ov5640_setting_15fps_XGA_1024_768, +- ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)}, +- {OV5640_MODE_720P_1280_720, SUBSAMPLING, +- 1280, 1892, 720, 740, +- ov5640_setting_15fps_720P_1280_720, +- ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)}, +- {OV5640_MODE_1080P_1920_1080, SCALING, +- 1920, 2500, 1080, 1120, +- ov5640_setting_15fps_1080P_1920_1080, +- ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)}, +- {OV5640_MODE_QSXGA_2592_1944, SCALING, +- 2592, 2844, 1944, 1968, +- ov5640_setting_15fps_QSXGA_2592_1944, +- ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)}, +- }, { +- {OV5640_MODE_QCIF_176_144, SUBSAMPLING, +- 176, 1896, 144, 984, +- ov5640_setting_30fps_QCIF_176_144, +- ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)}, +- {OV5640_MODE_QVGA_320_240, SUBSAMPLING, +- 320, 1896, 240, 984, +- ov5640_setting_30fps_QVGA_320_240, +- ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)}, +- {OV5640_MODE_VGA_640_480, SUBSAMPLING, +- 640, 1896, 480, 1080, +- ov5640_setting_30fps_VGA_640_480, +- ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)}, +- {OV5640_MODE_NTSC_720_480, SUBSAMPLING, +- 720, 1896, 480, 984, +- ov5640_setting_30fps_NTSC_720_480, +- ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)}, +- {OV5640_MODE_PAL_720_576, SUBSAMPLING, +- 720, 1896, 576, 984, +- ov5640_setting_30fps_PAL_720_576, +- ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)}, +- {OV5640_MODE_XGA_1024_768, SUBSAMPLING, +- 1024, 1896, 768, 1080, +- ov5640_setting_30fps_XGA_1024_768, +- ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)}, +- {OV5640_MODE_720P_1280_720, SUBSAMPLING, +- 1280, 1892, 720, 740, +- ov5640_setting_30fps_720P_1280_720, +- ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)}, +- {OV5640_MODE_1080P_1920_1080, SCALING, +- 1920, 2500, 1080, 1120, +- ov5640_setting_30fps_1080P_1920_1080, +- ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)}, +- {OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, 0, 0, NULL, 0}, +- }, ++ov5640_mode_data[OV5640_NUM_MODES] = { ++ {OV5640_MODE_QCIF_176_144, SUBSAMPLING, ++ 176, 1896, 144, 984, ++ ov5640_setting_QCIF_176_144, ++ ARRAY_SIZE(ov5640_setting_QCIF_176_144)}, ++ {OV5640_MODE_QVGA_320_240, SUBSAMPLING, ++ 320, 1896, 240, 984, ++ ov5640_setting_QVGA_320_240, ++ ARRAY_SIZE(ov5640_setting_QVGA_320_240)}, ++ {OV5640_MODE_VGA_640_480, SUBSAMPLING, ++ 640, 1896, 480, 1080, ++ ov5640_setting_VGA_640_480, ++ ARRAY_SIZE(ov5640_setting_VGA_640_480)}, ++ {OV5640_MODE_NTSC_720_480, SUBSAMPLING, ++ 720, 1896, 480, 984, ++ ov5640_setting_NTSC_720_480, ++ ARRAY_SIZE(ov5640_setting_NTSC_720_480)}, ++ {OV5640_MODE_PAL_720_576, SUBSAMPLING, ++ 720, 1896, 576, 984, ++ ov5640_setting_PAL_720_576, ++ ARRAY_SIZE(ov5640_setting_PAL_720_576)}, ++ {OV5640_MODE_XGA_1024_768, SUBSAMPLING, ++ 1024, 1896, 768, 1080, ++ ov5640_setting_XGA_1024_768, ++ ARRAY_SIZE(ov5640_setting_XGA_1024_768)}, ++ {OV5640_MODE_720P_1280_720, SUBSAMPLING, ++ 1280, 1892, 720, 740, ++ ov5640_setting_720P_1280_720, ++ ARRAY_SIZE(ov5640_setting_720P_1280_720)}, ++ {OV5640_MODE_1080P_1920_1080, SCALING, ++ 1920, 2500, 1080, 1120, ++ ov5640_setting_1080P_1920_1080, ++ ARRAY_SIZE(ov5640_setting_1080P_1920_1080)}, ++ {OV5640_MODE_QSXGA_2592_1944, SCALING, ++ 2592, 2844, 1944, 1968, ++ ov5640_setting_QSXGA_2592_1944, ++ ARRAY_SIZE(ov5640_setting_QSXGA_2592_1944)}, + }; + + static int ov5640_init_slave_id(struct ov5640_dev *sensor) +@@ -909,6 +698,272 @@ static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg, + return ov5640_write_reg(sensor, reg, val); + } + ++/* ++ * After spending way too much time trying the various combinations, I ++ * believe the clock tree is as follows: ++ * ++ * +--------------+ ++ * | Oscillator | ++ * +------+-------+ ++ * | ++ * +------+-------+ ++ * | System clock | - reg 0x3035, bits 4-7 ++ * +------+-------+ ++ * | ++ * +------+-------+ - reg 0x3036, for the multiplier ++ * | PLL | - reg 0x3037, bits 4 for the root divider ++ * +------+-------+ - reg 0x3037, bits 0-3 for the pre-divider ++ * | ++ * +------+-------+ ++ * | SCLK | - reg 0x3108, bits 0-1 for the root divider ++ * +------+-------+ ++ * | ++ * +------+-------+ ++ * | PCLK | - reg 0x3108, bits 4-5 for the root divider ++ * +--------------+ ++ * ++ * This is deviating from the datasheet at least for the register ++ * 0x3108, since it's said here that the PCLK would be clocked from ++ * the PLL. However, changing the SCLK divider value has a direct ++ * effect on the PCLK rate, which wouldn't be the case if both PCLK ++ * and SCLK were to be sourced from the PLL. ++ * ++ * These parameters also match perfectly the rate that is output by ++ * the sensor, so we shouldn't have too much factors missing (or they ++ * would be set to 1). ++ */ ++ ++/* ++ * FIXME: This is supposed to be ranging from 1 to 16, but the value ++ * is always set to either 1 or 2 in the vendor kernels. ++ * ++ * Moreover issues are seen with SYSDIV set to 1: ++ * Strange behaviour is observed when requesting 75MHz pixel clock output ++ * for 1280x720 (1892x740) resolution, pixel clock is about 100MHz with ++ * blanking (register values: 0x3035=0x11 and 0x3036=0x13). ++ * When forcing system clock divider to 2, pixel clock is 75Mhz continuous ++ * as expected (register values: 0x3035=0x21 and 0x3036=0x26). ++ */ ++#define OV5640_SYSDIV_MIN 2 ++#define OV5640_SYSDIV_MAX 2 ++ ++static unsigned long ov5640_calc_sysclk(struct ov5640_dev *sensor, ++ unsigned long rate, ++ u8 *sysdiv) ++{ ++ unsigned long best = ~0; ++ u8 best_sysdiv = 1; ++ u8 _sysdiv; ++ ++ for (_sysdiv = OV5640_SYSDIV_MIN; ++ _sysdiv <= OV5640_SYSDIV_MAX; ++ _sysdiv++) { ++ unsigned long tmp; ++ ++ tmp = sensor->xclk_freq / _sysdiv; ++ if (abs(rate - tmp) < abs(rate - best)) { ++ best = tmp; ++ best_sysdiv = _sysdiv; ++ } ++ ++ if (tmp == rate) ++ goto out; ++ } ++ ++out: ++ *sysdiv = best_sysdiv; ++ return best; ++} ++ ++/* ++ * FIXME: This is supposed to be ranging from 1 to 8, but the value is ++ * always set to 3 in the vendor kernels. ++ */ ++#define OV5640_PLL_PREDIV_MIN 3 ++#define OV5640_PLL_PREDIV_MAX 3 ++ ++/* ++ * FIXME: This is supposed to be ranging from 1 to 2, but the value is ++ * always set to 1 in the vendor kernels. ++ */ ++#define OV5640_PLL_ROOT_DIV_MIN 1 ++#define OV5640_PLL_ROOT_DIV_MAX 1 ++ ++#define OV5640_PLL_MULT_MIN 4 ++#define OV5640_PLL_MULT_MAX 252 ++ ++static unsigned long ov5640_calc_pll(struct ov5640_dev *sensor, ++ unsigned long rate, ++ u8 *sysdiv, u8 *prediv, u8 *rdiv, u8 *mult) ++{ ++ unsigned long best = ~0; ++ u8 best_sysdiv = 1, best_prediv = 1, best_mult = 1, best_rdiv = 1; ++ u8 _prediv, _mult, _rdiv; ++ ++ for (_prediv = OV5640_PLL_PREDIV_MIN; ++ _prediv <= OV5640_PLL_PREDIV_MAX; ++ _prediv++) { ++ for (_mult = OV5640_PLL_MULT_MIN; ++ _mult <= OV5640_PLL_MULT_MAX; ++ _mult++) { ++ for (_rdiv = OV5640_PLL_ROOT_DIV_MIN; ++ _rdiv <= OV5640_PLL_ROOT_DIV_MAX; ++ _rdiv++) { ++ unsigned long pll; ++ unsigned long sysclk; ++ u8 _sysdiv; ++ ++ /* ++ * The PLL multiplier cannot be odd if ++ * above 127. ++ */ ++ if (_mult > 127 && !(_mult % 2)) ++ continue; ++ ++ sysclk = rate * _prediv * _rdiv / _mult; ++ sysclk = ov5640_calc_sysclk(sensor, sysclk, ++ &_sysdiv); ++ ++ pll = sysclk / _rdiv / _prediv * _mult; ++ if (abs(rate - pll) < abs(rate - best)) { ++ best = pll; ++ best_sysdiv = _sysdiv; ++ best_prediv = _prediv; ++ best_mult = _mult; ++ best_rdiv = _rdiv; ++ } ++ ++ if (pll == rate) ++ goto out; ++ } ++ } ++ } ++ ++out: ++ *sysdiv = best_sysdiv; ++ *prediv = best_prediv; ++ *mult = best_mult; ++ *rdiv = best_rdiv; ++ ++ return best; ++} ++ ++/* ++ * FIXME: This is supposed to be ranging from 1 to 8, but the value is ++ * always set to 1 in the vendor kernels. ++ */ ++#define OV5640_PCLK_ROOT_DIV_MIN 1 ++#define OV5640_PCLK_ROOT_DIV_MAX 1 ++ ++static unsigned long ov5640_calc_pclk(struct ov5640_dev *sensor, ++ unsigned long rate, ++ u8 *sysdiv, u8 *prediv, u8 *pll_rdiv, ++ u8 *mult, u8 *pclk_rdiv) ++{ ++ unsigned long best = ~0; ++ u8 best_sysdiv = 1, best_prediv = 1, best_mult = 1, best_pll_rdiv = 1; ++ u8 best_pclk_rdiv = 1; ++ u8 _pclk_rdiv; ++ ++ for (_pclk_rdiv = OV5640_PCLK_ROOT_DIV_MIN; ++ _pclk_rdiv <= OV5640_PCLK_ROOT_DIV_MAX; ++ _pclk_rdiv <<= 1) { ++ unsigned long pll, pclk; ++ u8 sysdiv, prediv, mult, pll_rdiv; ++ ++ pll = rate * OV5640_SCLK_ROOT_DIVIDER_DEFAULT * _pclk_rdiv; ++ pll = ov5640_calc_pll(sensor, pll, &sysdiv, &prediv, &pll_rdiv, ++ &mult); ++ ++ pclk = pll / OV5640_SCLK_ROOT_DIVIDER_DEFAULT / _pclk_rdiv; ++ if (abs(rate - pclk) < abs(rate - best)) { ++ best = pclk; ++ best_sysdiv = sysdiv; ++ best_prediv = prediv; ++ best_pll_rdiv = pll_rdiv; ++ best_pclk_rdiv = _pclk_rdiv; ++ best_mult = mult; ++ } ++ ++ if (pclk == rate) ++ goto out; ++ } ++ ++out: ++ *sysdiv = best_sysdiv; ++ *prediv = best_prediv; ++ *pll_rdiv = best_pll_rdiv; ++ *mult = best_mult; ++ *pclk_rdiv = best_pclk_rdiv; ++ return best; ++} ++ ++static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, ++ const struct ov5640_mode_info *mode, ++ unsigned long rate) ++{ ++ u8 sysdiv, prediv, mult, pll_rdiv, pclk_rdiv; ++ int ret; ++ struct i2c_client *client = sensor->i2c_client; ++ u8 dvp_pclk_divider = mode->hact < 1024 ? 2 : 1;//FIXME ++ unsigned int pclk_freq, max_pclk_freq; ++ ++ ret = ov5640_write_reg(sensor, OV5640_REG_DVP_PCLK_DIVIDER, ++ dvp_pclk_divider); ++ if (ret) ++ return ret; ++ pclk_freq = rate / dvp_pclk_divider; ++ max_pclk_freq = sensor->ep.bus.parallel.pclk_max_frequency; ++ ++ /* clip rate according to optional maximum pixel clock limit */ ++ if (max_pclk_freq && (pclk_freq > max_pclk_freq)) { ++ rate = max_pclk_freq * dvp_pclk_divider; ++ dev_dbg(&client->dev, "DVP pixel clock too high (%d > %d Hz), reducing rate...\n", ++ pclk_freq, max_pclk_freq); ++ } ++ ++ ov5640_calc_pclk(sensor, rate, &sysdiv, &prediv, &pll_rdiv, &mult, ++ &pclk_rdiv); ++ ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1, ++ 0xf0, sysdiv << 4); ++ if (ret) ++ return ret; ++ ++ ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2, ++ 0xff, mult); ++ if (ret) ++ return ret; ++ ++ ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3, ++ 0xff, prediv | ((pll_rdiv - 1) << 4)); ++ if (ret) ++ return ret; ++ ++ return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, ++ 0x30, ilog2(pclk_rdiv) << 4); ++} ++ ++static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor, unsigned long rate) ++{ ++ u8 sysdiv, prediv, mult, pll_rdiv, pclk_rdiv; ++ int ret; ++ ++ ov5640_calc_pclk(sensor, rate, &sysdiv, &prediv, &pll_rdiv, &mult, ++ &pclk_rdiv); ++ ret = ov5640_write_reg(sensor, OV5640_REG_SC_PLL_CTRL1, ++ (sysdiv << 4) | pclk_rdiv); ++ if (ret) ++ return ret; ++ ++ ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2, ++ 0xff, mult); ++ if (ret) ++ return ret; ++ ++ return ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3, ++ 0xff, prediv | ((pll_rdiv - 1) << 4)); ++} ++ + /* download ov5640 settings to sensor through i2c */ + static int ov5640_set_timings(struct ov5640_dev *sensor, + const struct ov5640_mode_info *mode) +@@ -1444,8 +1499,8 @@ ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr, + { + const struct ov5640_mode_info *mode; + +- mode = v4l2_find_nearest_size(ov5640_mode_data[fr], +- ARRAY_SIZE(ov5640_mode_data[fr]), ++ mode = v4l2_find_nearest_size(ov5640_mode_data, ++ ARRAY_SIZE(ov5640_mode_data), + hact, vact, + width, height); + +@@ -1637,8 +1692,12 @@ static int ov5640_set_mode(struct ov5640_dev *sensor) + enum ov5640_downsize_mode dn_mode, orig_dn_mode; + bool auto_gain = sensor->ctrls.auto_gain->val == 1; + bool auto_exp = sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO; ++ unsigned long rate; + int ret; + ++ if (!orig_mode) ++ orig_mode = mode; ++ + dn_mode = mode->dn_mode; + orig_dn_mode = orig_mode->dn_mode; + +@@ -1655,6 +1714,24 @@ static int ov5640_set_mode(struct ov5640_dev *sensor) + goto restore_auto_gain; + } + ++ /* ++ * All the formats we support have 2 bytes per pixel, except for JPEG ++ * which is 1 byte per pixel, but JPEG requires the same rate ++ * than YUV (horizontal lines blanking). ++ */ ++ rate = mode->vtot * mode->htot * 2; ++ rate *= ov5640_framerates[sensor->current_fr]; ++ ++ if (sensor->ep.bus_type == V4L2_MBUS_CSI2) { ++ rate = rate / sensor->ep.bus.mipi_csi2.num_data_lanes; ++ ret = ov5640_set_mipi_pclk(sensor, rate); ++ } else { ++ ret = ov5640_set_dvp_pclk(sensor, mode, rate); ++ } ++ ++ if (ret < 0) ++ return 0; ++ + if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) || + (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) { + /* +@@ -2502,10 +2579,10 @@ static int ov5640_enum_frame_size(struct v4l2_subdev *sd, + return -EINVAL; + + fse->min_width = +- ov5640_mode_data[0][fse->index].hact; ++ ov5640_mode_data[fse->index].hact; + fse->max_width = fse->min_width; + fse->min_height = +- ov5640_mode_data[0][fse->index].vact; ++ ov5640_mode_data[fse->index].vact; + fse->max_height = fse->min_height; + + return 0; +@@ -2573,8 +2650,6 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd, + if (frame_rate < 0) + frame_rate = OV5640_15_FPS; + +- sensor->current_fr = frame_rate; +- sensor->frame_interval = fi->interval; + mode = ov5640_find_mode(sensor, frame_rate, mode->hact, + mode->vact, true); + if (!mode) { +@@ -2582,7 +2657,10 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd, + goto out; + } + +- if (mode != sensor->current_mode) { ++ if (mode != sensor->current_mode || ++ (frame_rate != sensor->current_fr)) { ++ sensor->current_fr = frame_rate; ++ sensor->frame_interval = fi->interval; + sensor->current_mode = mode; + sensor->pending_mode_change = true; + } +@@ -2613,7 +2691,8 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int enable) + + if (sensor->streaming == !enable) { + if (enable && sensor->pending_mode_change) { +- ret = ov5640_set_mode(sensor); ++ ret = ov5640_set_mode(sensor, sensor->last_mode); ++ + if (ret) + goto out; + } +@@ -2735,7 +2814,7 @@ static int ov5640_probe(struct i2c_client *client, + sensor->frame_interval.denominator = ov5640_framerates[OV5640_30_FPS]; + sensor->current_fr = OV5640_30_FPS; + sensor->current_mode = +- &ov5640_mode_data[OV5640_30_FPS][OV5640_MODE_VGA_640_480]; ++ &ov5640_mode_data[OV5640_MODE_VGA_640_480]; + sensor->last_mode = sensor->current_mode; + + sensor->ae_target = 52; +diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c +index 7215641..49849e6 100644 +--- a/drivers/media/platform/stm32/stm32-dcmi.c ++++ b/drivers/media/platform/stm32/stm32-dcmi.c +@@ -95,6 +95,9 @@ enum state { + #define MIN_HEIGHT 16U + #define MAX_HEIGHT 2592U + ++/* DMA can sustain YUV 720p@15fps max */ ++#define MAX_DMA_BANDWIDTH (1280 * 720 * 2 * 15) ++ + #define TIMEOUT_MS 1000 + + struct dcmi_graph_entity { +@@ -570,9 +573,9 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) + int ret; + + ret = pm_runtime_get_sync(dcmi->dev); +- if (ret) { +- dev_err(dcmi->dev, "%s: Failed to start streaming, cannot get sync\n", +- __func__); ++ if (ret < 0) { ++ dev_err(dcmi->dev, "%s: Failed to start streaming, cannot get sync (%d)\n", ++ __func__, ret); + goto err_release_buffers; + } + +@@ -621,8 +624,31 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) + dcmi_set_crop(dcmi); + + /* Enable jpeg capture */ +- if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG) +- reg_set(dcmi->regs, DCMI_CR, CR_CM);/* Snapshot mode */ ++ if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG) { ++ unsigned int rate; ++ struct v4l2_streamparm p = { ++ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE ++ }; ++ struct v4l2_fract frame_interval = {1, 30}; ++ ++ ret = v4l2_g_parm_cap(dcmi->vdev, dcmi->entity.subdev, &p); ++ if (!ret) ++ frame_interval = p.parm.capture.timeperframe; ++ ++ rate = dcmi->fmt.fmt.pix.sizeimage * ++ frame_interval.denominator / frame_interval.numerator; ++ ++ /* ++ * If rate exceed DMA capabilities, switch to snapshot mode ++ * to ensure that current DMA transfer is elapsed before ++ * capturing a new JPEG. ++ */ ++ if (rate > MAX_DMA_BANDWIDTH) { ++ reg_set(dcmi->regs, DCMI_CR, CR_CM);/* Snapshot mode */ ++ dev_dbg(dcmi->dev, "Capture rate is too high for continuous mode (%d > %d bytes/s), switch to snapshot mode\n", ++ rate, MAX_DMA_BANDWIDTH); ++ } ++ } + + /* Enable dcmi */ + reg_set(dcmi->regs, DCMI_CR, CR_ENABLE); +@@ -659,7 +685,10 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) + } + + /* Enable interruptions */ +- reg_set(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR); ++ if (dcmi->sd_format->fourcc == V4L2_PIX_FMT_JPEG) ++ reg_set(dcmi->regs, DCMI_IER, IT_FRAME | IT_OVR | IT_ERR); ++ else ++ reg_set(dcmi->regs, DCMI_IER, IT_OVR | IT_ERR); + + return 0; + +diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c +index 169bdbb..505338e 100644 +--- a/drivers/media/v4l2-core/v4l2-fwnode.c ++++ b/drivers/media/v4l2-core/v4l2-fwnode.c +@@ -158,6 +158,9 @@ static void v4l2_fwnode_endpoint_parse_parallel_bus( + flags |= v ? V4L2_MBUS_DATA_ENABLE_HIGH : + V4L2_MBUS_DATA_ENABLE_LOW; + ++ if (!fwnode_property_read_u32(fwnode, "pclk-max-frequency", &v)) ++ bus->pclk_max_frequency = v; ++ + bus->flags = flags; + + } +diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h +index 9cccab6..946b48d 100644 +--- a/include/media/v4l2-fwnode.h ++++ b/include/media/v4l2-fwnode.h +@@ -52,11 +52,13 @@ struct v4l2_fwnode_bus_mipi_csi2 { + * @flags: media bus (V4L2_MBUS_*) flags + * @bus_width: bus width in bits + * @data_shift: data shift in bits ++ * @max_pclk_frequency: maximum pixel clock in hertz + */ + struct v4l2_fwnode_bus_parallel { + unsigned int flags; + unsigned char bus_width; + unsigned char data_shift; ++ unsigned int pclk_max_frequency; + }; + + /** +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0021-ARM-stm32mp1-r0-rc2-PINCTRL.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0021-ARM-stm32mp1-r0-rc2-PINCTRL.patch new file mode 100644 index 0000000..568137e --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0021-ARM-stm32mp1-r0-rc2-PINCTRL.patch @@ -0,0 +1,288 @@ +From 85d48072cfb2e491d10a235c4ade2460e3bbc93a Mon Sep 17 00:00:00 2001 +From: Christophe Priouzeau +Date: Mon, 26 Nov 2018 14:40:42 +0100 +Subject: [PATCH 21/52] ARM-stm32mp1-r0-rc2-PINCTRL + +--- + .../bindings/pinctrl/st,stm32-pinctrl.txt | 1 + + drivers/pinctrl/core.c | 15 +---- + drivers/pinctrl/pinctrl-stmfx.c | 8 +++ + drivers/pinctrl/stm32/pinctrl-stm32.c | 71 +++++++++++++++++++++- + include/linux/pinctrl/pinctrl.h | 2 + + 5 files changed, 83 insertions(+), 14 deletions(-) + +diff --git a/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt +index 286c981..1a5d1e2 100644 +--- a/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt ++++ b/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt +@@ -58,6 +58,7 @@ Optional properties: + used to select GPIOs as interrupts). + - st,package: Indicates the SOC package used. + More details in include/dt-bindings/pinctrl/stm32-pinfunc.h ++ - hwlocks: reference to a phandle of a hardware spinlock provider node. + + Example 1: + #include +diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c +index a3dd777..0220d43 100644 +--- a/drivers/pinctrl/core.c ++++ b/drivers/pinctrl/core.c +@@ -1992,7 +1992,7 @@ pinctrl_init_controller(struct pinctrl_desc *pctldesc, struct device *dev, + return ERR_PTR(ret); + } + +-static int pinctrl_claim_hogs(struct pinctrl_dev *pctldev) ++int pinctrl_claim_hogs(struct pinctrl_dev *pctldev) + { + pctldev->p = create_pinctrl(pctldev->dev, pctldev); + if (PTR_ERR(pctldev->p) == -ENODEV) { +@@ -2030,21 +2030,10 @@ static int pinctrl_claim_hogs(struct pinctrl_dev *pctldev) + + return 0; + } ++EXPORT_SYMBOL_GPL(pinctrl_claim_hogs); + + int pinctrl_enable(struct pinctrl_dev *pctldev) + { +- int error; +- +- error = pinctrl_claim_hogs(pctldev); +- if (error) { +- dev_err(pctldev->dev, "could not claim hogs: %i\n", +- error); +- mutex_destroy(&pctldev->mutex); +- kfree(pctldev); +- +- return error; +- } +- + mutex_lock(&pinctrldev_list_mutex); + list_add_tail(&pctldev->node, &pinctrldev_list); + mutex_unlock(&pinctrldev_list_mutex); +diff --git a/drivers/pinctrl/pinctrl-stmfx.c b/drivers/pinctrl/pinctrl-stmfx.c +index e253ed1..15d5757 100644 +--- a/drivers/pinctrl/pinctrl-stmfx.c ++++ b/drivers/pinctrl/pinctrl-stmfx.c +@@ -663,6 +663,14 @@ static int stmfx_pinctrl_probe(struct platform_device *pdev) + if (ret) + return ret; + ++ /* ++ * Claim hogs after enabling gpio function, otherwise pin ++ * configuration will not apply ++ */ ++ ret = pinctrl_claim_hogs(pctl->pctl_dev); ++ if (ret) ++ return ret; ++ + pctl->irq_chip.name = dev_name(pctl->dev); + pctl->irq_chip.irq_mask = stmfx_pinctrl_irq_mask; + pctl->irq_chip.irq_unmask = stmfx_pinctrl_irq_unmask; +diff --git a/drivers/pinctrl/stm32/pinctrl-stm32.c b/drivers/pinctrl/stm32/pinctrl-stm32.c +index e25917f..914bee4 100644 +--- a/drivers/pinctrl/stm32/pinctrl-stm32.c ++++ b/drivers/pinctrl/stm32/pinctrl-stm32.c +@@ -8,6 +8,7 @@ + */ + #include + #include ++#include + #include + #include + #include +@@ -64,6 +65,8 @@ + #define gpio_range_to_bank(chip) \ + container_of(chip, struct stm32_gpio_bank, range) + ++#define HWSPINLOCK_TIMEOUT 5 /* msec */ ++ + static const char * const stm32_gpio_functions[] = { + "gpio", "af0", "af1", + "af2", "af3", "af4", +@@ -111,6 +114,7 @@ struct stm32_pinctrl { + u32 npins; + u32 pkg; + u32 pin_base_shift; ++ struct hwspinlock *hwlock; + }; + + static inline int stm32_gpio_pin(int gpio) +@@ -598,14 +602,24 @@ static int stm32_pmx_get_func_groups(struct pinctrl_dev *pctldev, + static void stm32_pmx_set_mode(struct stm32_gpio_bank *bank, + int pin, u32 mode, u32 alt) + { ++ struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent); + u32 val; + int alt_shift = (pin % 8) * 4; + int alt_offset = STM32_GPIO_AFRL + (pin / 8) * 4; + unsigned long flags; ++ int err = 0; + + clk_enable(bank->clk); + spin_lock_irqsave(&bank->lock, flags); + ++ if (pctl->hwlock) ++ err = hwspin_lock_timeout(pctl->hwlock, HWSPINLOCK_TIMEOUT); ++ ++ if (err) { ++ dev_err(pctl->dev, "Can't get hwspinlock\n"); ++ goto unlock; ++ } ++ + val = readl_relaxed(bank->base + alt_offset); + val &= ~GENMASK(alt_shift + 3, alt_shift); + val |= (alt << alt_shift); +@@ -618,6 +632,10 @@ static void stm32_pmx_set_mode(struct stm32_gpio_bank *bank, + + stm32_gpio_backup_mode(bank, pin, mode, alt); + ++ if (pctl->hwlock) ++ hwspin_unlock(pctl->hwlock); ++ ++unlock: + spin_unlock_irqrestore(&bank->lock, flags); + clk_disable(bank->clk); + } +@@ -707,12 +725,22 @@ static const struct pinmux_ops stm32_pmx_ops = { + static void stm32_pconf_set_driving(struct stm32_gpio_bank *bank, + unsigned offset, u32 drive) + { ++ struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent); + unsigned long flags; + u32 val; ++ int err = 0; + + clk_enable(bank->clk); + spin_lock_irqsave(&bank->lock, flags); + ++ if (pctl->hwlock) ++ err = hwspin_lock_timeout(pctl->hwlock, HWSPINLOCK_TIMEOUT); ++ ++ if (err) { ++ dev_err(pctl->dev, "Can't get hwspinlock\n"); ++ goto unlock; ++ } ++ + val = readl_relaxed(bank->base + STM32_GPIO_TYPER); + val &= ~BIT(offset); + val |= drive << offset; +@@ -720,6 +748,10 @@ static void stm32_pconf_set_driving(struct stm32_gpio_bank *bank, + + stm32_gpio_backup_driving(bank, offset, drive); + ++ if (pctl->hwlock) ++ hwspin_unlock(pctl->hwlock); ++ ++unlock: + spin_unlock_irqrestore(&bank->lock, flags); + clk_disable(bank->clk); + } +@@ -745,12 +777,22 @@ static u32 stm32_pconf_get_driving(struct stm32_gpio_bank *bank, + static void stm32_pconf_set_speed(struct stm32_gpio_bank *bank, + unsigned offset, u32 speed) + { ++ struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent); + unsigned long flags; + u32 val; ++ int err = 0; + + clk_enable(bank->clk); + spin_lock_irqsave(&bank->lock, flags); + ++ if (pctl->hwlock) ++ err = hwspin_lock_timeout(pctl->hwlock, HWSPINLOCK_TIMEOUT); ++ ++ if (err) { ++ dev_err(pctl->dev, "Can't get hwspinlock\n"); ++ goto unlock; ++ } ++ + val = readl_relaxed(bank->base + STM32_GPIO_SPEEDR); + val &= ~GENMASK(offset * 2 + 1, offset * 2); + val |= speed << (offset * 2); +@@ -758,6 +800,10 @@ static void stm32_pconf_set_speed(struct stm32_gpio_bank *bank, + + stm32_gpio_backup_speed(bank, offset, speed); + ++ if (pctl->hwlock) ++ hwspin_unlock(pctl->hwlock); ++ ++unlock: + spin_unlock_irqrestore(&bank->lock, flags); + clk_disable(bank->clk); + } +@@ -783,12 +829,22 @@ static u32 stm32_pconf_get_speed(struct stm32_gpio_bank *bank, + static void stm32_pconf_set_bias(struct stm32_gpio_bank *bank, + unsigned offset, u32 bias) + { ++ struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent); + unsigned long flags; + u32 val; ++ int err = 0; + + clk_enable(bank->clk); + spin_lock_irqsave(&bank->lock, flags); + ++ if (pctl->hwlock) ++ err = hwspin_lock_timeout(pctl->hwlock, HWSPINLOCK_TIMEOUT); ++ ++ if (err) { ++ dev_err(pctl->dev, "Can't get hwspinlock\n"); ++ goto unlock; ++ } ++ + val = readl_relaxed(bank->base + STM32_GPIO_PUPDR); + val &= ~GENMASK(offset * 2 + 1, offset * 2); + val |= bias << (offset * 2); +@@ -796,6 +852,10 @@ static void stm32_pconf_set_bias(struct stm32_gpio_bank *bank, + + stm32_gpio_backup_bias(bank, offset, bias); + ++ if (pctl->hwlock) ++ hwspin_unlock(pctl->hwlock); ++ ++unlock: + spin_unlock_irqrestore(&bank->lock, flags); + clk_disable(bank->clk); + } +@@ -1206,7 +1266,7 @@ int stm32_pctl_probe(struct platform_device *pdev) + struct device *dev = &pdev->dev; + struct stm32_pinctrl *pctl; + struct pinctrl_pin_desc *pins; +- int i, ret, banks = 0; ++ int i, ret, hwlock_id, banks = 0; + + if (!np) + return -EINVAL; +@@ -1226,6 +1286,15 @@ int stm32_pctl_probe(struct platform_device *pdev) + + platform_set_drvdata(pdev, pctl); + ++ /* hwspinlock is optional */ ++ hwlock_id = of_hwspin_lock_get_id(pdev->dev.of_node, 0); ++ if (hwlock_id < 0) { ++ if (hwlock_id == -EPROBE_DEFER) ++ return hwlock_id; ++ } else { ++ pctl->hwlock = hwspin_lock_request_specific(hwlock_id); ++ } ++ + pctl->dev = dev; + pctl->match_data = match->data; + +diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h +index 8f5dbb8..bbeaa29 100644 +--- a/include/linux/pinctrl/pinctrl.h ++++ b/include/linux/pinctrl/pinctrl.h +@@ -148,6 +148,8 @@ extern int pinctrl_register_and_init(struct pinctrl_desc *pctldesc, + struct pinctrl_dev **pctldev); + extern int pinctrl_enable(struct pinctrl_dev *pctldev); + ++extern int pinctrl_claim_hogs(struct pinctrl_dev *pctldev); ++ + /* Please use pinctrl_register_and_init() and pinctrl_enable() instead */ + extern struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, + struct device *dev, void *driver_data); +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0022-ARM-stm32mp1-r0-rc2-MFD-IRQ.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0022-ARM-stm32mp1-r0-rc2-MFD-IRQ.patch new file mode 100644 index 0000000..330c717 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0022-ARM-stm32mp1-r0-rc2-MFD-IRQ.patch @@ -0,0 +1,781 @@ +From aed35270f0be1c5c61a028d7c183e60269f3abe3 Mon Sep 17 00:00:00 2001 +From: Christophe Priouzeau +Date: Mon, 26 Nov 2018 14:41:37 +0100 +Subject: [PATCH 22/52] ARM-stm32mp1-r0-rc2-MFD-IRQ + +--- + .../interrupt-controller/st,stm32-exti.txt | 34 +++++- + .../devicetree/bindings/mfd/st,stpmic1.txt | 2 +- + Documentation/devicetree/bindings/mfd/syscon.txt | 1 + + drivers/irqchip/irq-stm32-exti.c | 133 ++++++++++++++++++--- + drivers/mfd/stm32-pwr.c | 131 +++++++++----------- + drivers/mfd/stpmic1.c | 16 +-- + drivers/mfd/syscon.c | 19 +++ + drivers/mfd/wm8994-core.c | 15 +++ + include/linux/mfd/stpmic1.h | 1 - + 9 files changed, 247 insertions(+), 105 deletions(-) + +diff --git a/Documentation/devicetree/bindings/interrupt-controller/st,stm32-exti.txt b/Documentation/devicetree/bindings/interrupt-controller/st,stm32-exti.txt +index 6a36bf6..abcf816 100644 +--- a/Documentation/devicetree/bindings/interrupt-controller/st,stm32-exti.txt ++++ b/Documentation/devicetree/bindings/interrupt-controller/st,stm32-exti.txt +@@ -14,7 +14,23 @@ Required properties: + (only needed for exti controller with multiple exti under + same parent interrupt: st,stm32-exti and st,stm32h7-exti) + +-Example: ++Optional properties: ++ ++- hwlocks: reference to a phandle of a hardware spinlock provider node. ++ ++Exti could have several parent interrupt controllers. In this case child nodes ++are used to describe those "extra" parent controllers. Properties to use are: ++ ++- interrupt-controller: Indentifies the node as an interrupt controller ++- #interrupt-cells: Specifies the number of cells to encode an interrupt ++ specifier, shall be 2 ++- interrupt-parent: Phandle to the interrupt parent node. ++- st,irq-number: Interrupt number mapped on the parent. ++ ++See example 2. ++ ++ ++Example 1: + + exti: interrupt-controller@40013c00 { + compatible = "st,stm32-exti"; +@@ -23,3 +39,19 @@ exti: interrupt-controller@40013c00 { + reg = <0x40013C00 0x400>; + interrupts = <1>, <2>, <3>, <6>, <7>, <8>, <9>, <10>, <23>, <40>, <41>, <42>, <62>, <76>; + }; ++ ++Example 2: ++ ++exti: interrupt-controller@5000d000 { ++ compatible = "st,stm32mp1-exti", "syscon"; ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ reg = <0x5000d000 0x400>; ++ ++ exti_pwr: exti-pwr { ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ interrupt-parent = <&pwr>; ++ st,irq-number = <6>; ++ }; ++}; +diff --git a/Documentation/devicetree/bindings/mfd/st,stpmic1.txt b/Documentation/devicetree/bindings/mfd/st,stpmic1.txt +index 54b64e2..0fab08a 100644 +--- a/Documentation/devicetree/bindings/mfd/st,stpmic1.txt ++++ b/Documentation/devicetree/bindings/mfd/st,stpmic1.txt +@@ -4,7 +4,6 @@ Required parent device properties: + - compatible: "st,stpmic1" + - reg: The I2C slave address for the STPMIC1 chip. + - interrupts: The interrupt lines the device is connected to. +- The second interrupt is used for wake-up. + - #interrupt-cells: Should be 2. + - interrupt-controller: Describes the STPMIC1 as an interrupt + controller (has its own domain). Interrupt number are the following: +@@ -81,6 +80,7 @@ Optional parent device properties: + -bit 5: SW_OUT discharge is enabled + -bit 6: VBUS_OTG detection is enabled + -bit 7: BOOST_OVP is disabled ++- wakeup-source: bool flag to indicate this device has wakeup capabilities + + STPMIC1 consists in a varied group of sub-devices. + Each sub-device binding is be described in own documentation file. +diff --git a/Documentation/devicetree/bindings/mfd/syscon.txt b/Documentation/devicetree/bindings/mfd/syscon.txt +index 25d9e9c..a9aaa51 100644 +--- a/Documentation/devicetree/bindings/mfd/syscon.txt ++++ b/Documentation/devicetree/bindings/mfd/syscon.txt +@@ -17,6 +17,7 @@ Optional property: + - reg-io-width: the size (in bytes) of the IO accesses that should be + performed on the device. + - hwlocks: reference to a phandle of a hardware spinlock provider node. ++- clocks: phandle to the syscon clock + + Examples: + gpr: iomuxc-gpr@20e0000 { +diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c +index e185ed8..9cc15f1 100644 +--- a/drivers/irqchip/irq-stm32-exti.c ++++ b/drivers/irqchip/irq-stm32-exti.c +@@ -6,6 +6,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -20,6 +21,8 @@ + + #define IRQS_PER_BANK 32 + ++#define HWSPINLOCK_TIMEOUT 5 /* msec */ ++ + struct stm32_exti_bank { + u32 imr_ofst; + u32 emr_ofst; +@@ -47,6 +50,7 @@ struct stm32_exti_drv_data { + struct stm32_exti_chip_data { + struct stm32_exti_host_data *host_data; + const struct stm32_exti_bank *reg_bank; ++ struct hwspinlock *hwlock; + struct raw_spinlock rlock; + u32 wake_active; + u32 mask_cache; +@@ -275,25 +279,34 @@ static int stm32_irq_set_type(struct irq_data *d, unsigned int type) + struct stm32_exti_chip_data *chip_data = gc->private; + const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + u32 rtsr, ftsr; +- int err; ++ int err = 0; + + irq_gc_lock(gc); + ++ if (chip_data->hwlock) ++ err = hwspin_lock_timeout(chip_data->hwlock, ++ HWSPINLOCK_TIMEOUT); ++ ++ if (err) ++ goto unlock; ++ + rtsr = irq_reg_readl(gc, stm32_bank->rtsr_ofst); + ftsr = irq_reg_readl(gc, stm32_bank->ftsr_ofst); + + err = stm32_exti_set_type(d, type, &rtsr, &ftsr); +- if (err) { +- irq_gc_unlock(gc); +- return err; +- } ++ if (err) ++ goto unspinlock; + + irq_reg_writel(gc, rtsr, stm32_bank->rtsr_ofst); + irq_reg_writel(gc, ftsr, stm32_bank->ftsr_ofst); + ++unspinlock: ++ if (chip_data->hwlock) ++ hwspin_unlock(chip_data->hwlock); ++unlock: + irq_gc_unlock(gc); + +- return 0; ++ return err; + } + + static void stm32_chip_suspend(struct stm32_exti_chip_data *chip_data, +@@ -457,22 +470,36 @@ static int stm32_exti_h_set_type(struct irq_data *d, unsigned int type) + const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + void __iomem *base = chip_data->host_data->base; + u32 rtsr, ftsr; +- int err; ++ int err = 0; + + raw_spin_lock(&chip_data->rlock); ++ ++ if (chip_data->hwlock) ++ err = hwspin_lock_timeout(chip_data->hwlock, ++ HWSPINLOCK_TIMEOUT); ++ ++ if (err) ++ goto unlock; ++ + rtsr = readl_relaxed(base + stm32_bank->rtsr_ofst); + ftsr = readl_relaxed(base + stm32_bank->ftsr_ofst); + + err = stm32_exti_set_type(d, type, &rtsr, &ftsr); +- if (err) { +- raw_spin_unlock(&chip_data->rlock); +- return err; +- } ++ if (err) ++ goto unspinlock; + + writel_relaxed(rtsr, base + stm32_bank->rtsr_ofst); + writel_relaxed(ftsr, base + stm32_bank->ftsr_ofst); ++ ++unspinlock: ++ if (chip_data->hwlock) ++ hwspin_unlock(chip_data->hwlock); ++unlock: + raw_spin_unlock(&chip_data->rlock); + ++ if (d->parent_data->chip) ++ irq_chip_set_type_parent(d, type); ++ + return 0; + } + +@@ -490,6 +517,9 @@ static int stm32_exti_h_set_wake(struct irq_data *d, unsigned int on) + + raw_spin_unlock(&chip_data->rlock); + ++ if (d->parent_data->chip) ++ irq_chip_set_wake_parent(d, on); ++ + return 0; + } + +@@ -502,6 +532,12 @@ static int stm32_exti_h_set_affinity(struct irq_data *d, + return -EINVAL; + } + ++static void stm32_exti_h_ack(struct irq_data *d) ++{ ++ if (d->parent_data->chip) ++ irq_chip_ack_parent(d); ++} ++ + #ifdef CONFIG_PM + static int stm32_exti_h_suspend(void) + { +@@ -547,6 +583,7 @@ static inline void stm32_exti_h_syscore_init(void) {} + static struct irq_chip stm32_exti_h_chip = { + .name = "stm32-exti-h", + .irq_eoi = stm32_exti_h_eoi, ++ .irq_ack = stm32_exti_h_ack, + .irq_mask = stm32_exti_h_mask, + .irq_unmask = stm32_exti_h_unmask, + .irq_retrigger = irq_chip_retrigger_hierarchy, +@@ -574,15 +611,29 @@ static int stm32_exti_h_domain_alloc(struct irq_domain *dm, + irq_domain_set_hwirq_and_chip(dm, virq, hwirq, + &stm32_exti_h_chip, chip_data); + +- p_irq = stm32_exti_to_irq(host_data->drv_data, hwirq); +- if (p_irq >= 0) { ++ /* ++ * EXTI 55 to 60 are mapped to PWR interrupt controller. ++ * The hwirq translation is done diferently than for GIC. ++ */ ++ if (hwirq >= 55 && hwirq <= 60) { + p_fwspec.fwnode = dm->parent->fwnode; +- p_fwspec.param_count = 3; +- p_fwspec.param[0] = GIC_SPI; +- p_fwspec.param[1] = p_irq; +- p_fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH; ++ p_fwspec.param_count = 2; ++ p_fwspec.param[0] = hwirq - 55; ++ p_fwspec.param[1] = fwspec->param[1]; + + return irq_domain_alloc_irqs_parent(dm, virq, 1, &p_fwspec); ++ } else { ++ p_irq = stm32_exti_to_irq(host_data->drv_data, hwirq); ++ if (p_irq >= 0) { ++ p_fwspec.fwnode = dm->parent->fwnode; ++ p_fwspec.param_count = 3; ++ p_fwspec.param[0] = GIC_SPI; ++ p_fwspec.param[1] = p_irq; ++ p_fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH; ++ ++ return irq_domain_alloc_irqs_parent(dm, virq, 1, ++ &p_fwspec); ++ } + } + + return 0; +@@ -665,6 +716,7 @@ static int __init stm32_exti_init(const struct stm32_exti_drv_data *drv_data, + int nr_irqs, ret, i; + struct irq_chip_generic *gc; + struct irq_domain *domain; ++ struct hwspinlock *hwlock = NULL; + + host_data = stm32_exti_host_init(drv_data, node); + if (!host_data) +@@ -687,12 +739,22 @@ static int __init stm32_exti_init(const struct stm32_exti_drv_data *drv_data, + goto out_free_domain; + } + ++ /* hwspinlock is optional */ ++ ret = of_hwspin_lock_get_id(node, 0); ++ if (ret < 0) { ++ if (ret == -EPROBE_DEFER) ++ goto out_free_domain; ++ } else { ++ hwlock = hwspin_lock_request_specific(ret); ++ } ++ + for (i = 0; i < drv_data->bank_nr; i++) { + const struct stm32_exti_bank *stm32_bank; + struct stm32_exti_chip_data *chip_data; + + stm32_bank = drv_data->exti_banks[i]; + chip_data = stm32_exti_chip_init(host_data, i, node); ++ chip_data->hwlock = hwlock; + + gc = irq_get_domain_generic_chip(domain, i * IRQS_PER_BANK); + +@@ -760,11 +822,12 @@ __init stm32_exti_hierarchy_init(const struct stm32_exti_drv_data *drv_data, + { + struct irq_domain *parent_domain, *domain; + struct stm32_exti_host_data *host_data; ++ struct device_node *child; + int ret, i; + + parent_domain = irq_find_host(parent); + if (!parent_domain) { +- pr_err("interrupt-parent not found\n"); ++ pr_err("GIC interrupt-parent not found\n"); + return -EINVAL; + } + +@@ -786,6 +849,40 @@ __init stm32_exti_hierarchy_init(const struct stm32_exti_drv_data *drv_data, + goto out_unmap; + } + ++ for_each_child_of_node(node, child) { ++ struct device_node *parent_node; ++ u32 nirqs; ++ ++ parent_node = of_irq_find_parent(child); ++ parent_domain = irq_find_host(parent_node); ++ ++ if (!parent_domain) { ++ pr_err("%s: child interrupt-parent not found\n", ++ child->name); ++ ret = -EINVAL; ++ goto out_unmap; ++ } ++ ++ ret = of_property_read_u32(child, "st,irq-number", &nirqs); ++ if (ret != 0 || nirqs == 0) { ++ pr_err("%s: Missing or bad irq-number property\n" ++ , __func__); ++ ret = -EINVAL; ++ goto out_unmap; ++ } ++ ++ domain = irq_domain_add_hierarchy(parent_domain, 0, nirqs, ++ child, ++ &stm32_exti_h_domain_ops, ++ host_data); ++ if (!domain) { ++ pr_err("%s: Could not register exti domain.\n", ++ node->name); ++ ret = -ENOMEM; ++ goto out_unmap; ++ } ++ } ++ + stm32_exti_h_syscore_init(); + + return 0; +diff --git a/drivers/mfd/stm32-pwr.c b/drivers/mfd/stm32-pwr.c +index 377e2f5..206a933 100644 +--- a/drivers/mfd/stm32-pwr.c ++++ b/drivers/mfd/stm32-pwr.c +@@ -5,7 +5,9 @@ + */ + + #include ++#include + #include ++#include + #include + #include + #include +@@ -45,7 +47,6 @@ enum wkup_pull_setting { + } \ + + struct stm32_pwr_data { +- struct device *dev; /* self device */ + void __iomem *base; /* IO Memory base address */ + struct irq_domain *domain; /* Domain for this controller */ + int irq; /* Parent interrupt */ +@@ -53,25 +54,19 @@ struct stm32_pwr_data { + + static void stm32_pwr_irq_ack(struct irq_data *d) + { +- struct stm32_pwr_data *priv = d->domain->host_data; +- +- dev_dbg(priv->dev, "irq:%lu\n", d->hwirq); ++ pr_debug("irq:%lu\n", d->hwirq); + SMC(STM32_SVC_PWR, STM32_SET_BITS, WKUPCR, BIT(d->hwirq)); + } + + static void stm32_pwr_irq_mask(struct irq_data *d) + { +- struct stm32_pwr_data *priv = d->domain->host_data; +- +- dev_dbg(priv->dev, "irq:%lu\n", d->hwirq); ++ pr_debug("irq:%lu\n", d->hwirq); + SMC(STM32_SVC_PWR, STM32_CLEAR_BITS, MPUWKUPENR, BIT(d->hwirq)); + } + + static void stm32_pwr_irq_unmask(struct irq_data *d) + { +- struct stm32_pwr_data *priv = d->domain->host_data; +- +- dev_dbg(priv->dev, "irq:%lu\n", d->hwirq); ++ pr_debug("irq:%lu\n", d->hwirq); + SMC(STM32_SVC_PWR, STM32_SET_BITS, MPUWKUPENR, BIT(d->hwirq)); + } + +@@ -79,7 +74,7 @@ static int stm32_pwr_irq_set_wake(struct irq_data *d, unsigned int on) + { + struct stm32_pwr_data *priv = d->domain->host_data; + +- dev_dbg(priv->dev, "irq:%lu on:%d\n", d->hwirq, on); ++ pr_debug("irq:%lu on:%d\n", d->hwirq, on); + if (on) + enable_irq_wake(priv->irq); + else +@@ -95,7 +90,7 @@ static int stm32_pwr_irq_set_type(struct irq_data *d, unsigned int flow_type) + u32 wkupcr; + int en; + +- dev_dbg(priv->dev, "irq:%lu\n", d->hwirq); ++ pr_debug("irq:%lu\n", d->hwirq); + + en = readl_relaxed(priv->base + MPUWKUPENR) & BIT(pin_id); + /* reference manual request to disable the wakeup pin while +@@ -115,6 +110,7 @@ static int stm32_pwr_irq_set_type(struct irq_data *d, unsigned int flow_type) + default: + return -EINVAL; + } ++ + SMC(STM32_SVC_PWR, STM32_WRITE, WKUPCR, wkupcr); + + if (en) +@@ -124,7 +120,7 @@ static int stm32_pwr_irq_set_type(struct irq_data *d, unsigned int flow_type) + } + + static struct irq_chip stm32_pwr_irq_chip = { +- .name = "stm32_pwr-irq", ++ .name = "stm32-pwr-irq", + .irq_ack = stm32_pwr_irq_ack, + .irq_mask = stm32_pwr_irq_mask, + .irq_unmask = stm32_pwr_irq_unmask, +@@ -132,29 +128,23 @@ static struct irq_chip stm32_pwr_irq_chip = { + .irq_set_wake = stm32_pwr_irq_set_wake, + }; + +-static int stm32_pwr_irq_map(struct irq_domain *h, unsigned int virq, +- irq_hw_number_t hw) +-{ +- irq_set_chip_and_handler(virq, &stm32_pwr_irq_chip, handle_edge_irq); +- return 0; +-} +- + static int stm32_pwr_irq_set_pull_config(struct irq_domain *d, int pin_id, + enum wkup_pull_setting config) + { + struct stm32_pwr_data *priv = d->host_data; + u32 wkupcr; + +- dev_dbg(priv->dev, "irq:%d pull config:0x%x\n", pin_id, config); ++ pr_debug("irq:%d pull config:0x%x\n", pin_id, config); + + if (config >= WKUP_PULL_RESERVED) { +- dev_err(priv->dev, "%s: bad irq pull config\n", __func__); ++ pr_err("%s: bad irq pull config\n", __func__); + return -EINVAL; + } + + wkupcr = readl_relaxed(priv->base + WKUPCR); + wkupcr &= ~((WKUP_PULL_MASK) << (WKUP_PULL_SHIFT + pin_id * 2)); + wkupcr |= (config & WKUP_PULL_MASK) << (WKUP_PULL_SHIFT + pin_id * 2); ++ + SMC(STM32_SVC_PWR, STM32_WRITE, WKUPCR, wkupcr); + + return 0; +@@ -164,10 +154,8 @@ static int stm32_pwr_xlate(struct irq_domain *d, struct device_node *ctrlr, + const u32 *intspec, unsigned int intsize, + irq_hw_number_t *out_hwirq, unsigned int *out_type) + { +- struct stm32_pwr_data *priv = d->host_data; +- + if (WARN_ON(intsize < 3)) { +- dev_err(priv->dev, "%s: bad irq config parameters\n", __func__); ++ pr_err("%s: bad irq config parameters\n", __func__); + return -EINVAL; + } + +@@ -177,9 +165,23 @@ static int stm32_pwr_xlate(struct irq_domain *d, struct device_node *ctrlr, + return stm32_pwr_irq_set_pull_config(d, intspec[0], intspec[2]); + } + ++static int stm32_pwr_alloc(struct irq_domain *d, unsigned int virq, ++ unsigned int nr_irqs, void *data) ++{ ++ struct irq_fwspec *fwspec = data; ++ irq_hw_number_t hwirq; ++ ++ hwirq = fwspec->param[0]; ++ irq_domain_set_info(d, virq, hwirq, &stm32_pwr_irq_chip, d->host_data, ++ handle_edge_irq, NULL, NULL); ++ ++ return 0; ++} ++ + static const struct irq_domain_ops stm32_pwr_irq_domain_ops = { +- .map = stm32_pwr_irq_map, ++ .alloc = stm32_pwr_alloc, + .xlate = stm32_pwr_xlate, ++ .free = irq_domain_free_irqs_common, + }; + + /* +@@ -195,9 +197,10 @@ static void stm32_pwr_handle_irq(struct irq_desc *desc) + + wkupfr = readl_relaxed(priv->base + WKUPFR); + wkupenr = readl_relaxed(priv->base + MPUWKUPENR); ++ + for (i = 0; i < NB_WAKEUPPINS; i++) { + if ((wkupfr & BIT(i)) && (wkupenr & BIT(i))) { +- dev_dbg(priv->dev, "handle wkup irq:%d\n", i); ++ pr_debug("handle wkup irq:%d\n", i); + generic_handle_irq(irq_find_mapping(priv->domain, i)); + } + } +@@ -205,27 +208,21 @@ static void stm32_pwr_handle_irq(struct irq_desc *desc) + chained_irq_exit(chip, desc); + } + +-static int stm32_pwr_probe(struct platform_device *pdev) ++static int __init stm32_pwr_init(struct device_node *np, ++ struct device_node *parent) + { +- struct device *dev = &pdev->dev; +- struct device_node *np = pdev->dev.of_node; + struct stm32_pwr_data *priv; +- +- struct resource *res; + int ret; + +- priv = devm_kzalloc(dev, sizeof(struct stm32_pwr_data), GFP_KERNEL); ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + +- platform_set_drvdata(pdev, priv); +- priv->dev = dev; +- +- res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- priv->base = devm_ioremap_resource(&pdev->dev, res); ++ priv->base = of_iomap(np, 0); + if (IS_ERR(priv->base)) { +- dev_err(dev, "%s: Unable to map IO memory\n", __func__); +- return PTR_ERR(priv->base); ++ pr_err("%s: Unable to map IO memory\n", __func__); ++ ret = PTR_ERR(priv->base); ++ goto out_free; + } + + /* Disable all wake-up pins */ +@@ -236,52 +233,38 @@ static int stm32_pwr_probe(struct platform_device *pdev) + priv->domain = irq_domain_add_linear(np, NB_WAKEUPPINS, + &stm32_pwr_irq_domain_ops, priv); + if (!priv->domain) { +- dev_err(dev, "%s: Unable to add irq domain!\n", __func__); +- goto out; ++ pr_err("%s: Unable to add irq domain!\n", __func__); ++ ret = -ENOMEM; ++ goto out_unmap; + } + +- ret = platform_get_irq(pdev, 0); +- if (ret < 0) { +- dev_err(dev, "failed to get PWR IRQ\n"); +- goto err; ++ priv->irq = irq_of_parse_and_map(np, 0); ++ if (priv->irq < 0) { ++ pr_err("failed to get PWR IRQ\n"); ++ ret = priv->irq; ++ goto out_domain; + } +- priv->irq = ret; + + irq_set_chained_handler_and_data(priv->irq, + stm32_pwr_handle_irq, priv); + +-out: ++ of_node_clear_flag(np, OF_POPULATED); ++ + return 0; +-err: ++ ++out_domain: + irq_domain_remove(priv->domain); ++out_unmap: ++ iounmap(priv->base); ++out_free: ++ kfree(priv); + return ret; + } + +-static int stm32_pwr_remove(struct platform_device *pdev) ++static int __init stm32_pwr_of_init(struct device_node *np, ++ struct device_node *parent) + { +- struct stm32_pwr_data *priv = platform_get_drvdata(pdev); +- +- irq_domain_remove(priv->domain); +- return 0; ++ return stm32_pwr_init(np, parent); + } + +-static const struct of_device_id stm32_pwr_match[] = { +- { .compatible = "st,stm32mp1-pwr" }, +- {}, +-}; +- +-static struct platform_driver stm32_pwr_driver = { +- .probe = stm32_pwr_probe, +- .remove = stm32_pwr_remove, +- .driver = { +- .name = "stm32-pwr", +- .owner = THIS_MODULE, +- .of_match_table = stm32_pwr_match, +- }, +-}; +- +-static int __init stm32_pwr_init(void) +-{ +- return platform_driver_register(&stm32_pwr_driver); +-} +-arch_initcall(stm32_pwr_init); ++IRQCHIP_DECLARE(stm32mp1_pwr_irq, "st,stm32mp1-pwr", stm32_pwr_of_init); +diff --git a/drivers/mfd/stpmic1.c b/drivers/mfd/stpmic1.c +index 5bf6328..648315d 100644 +--- a/drivers/mfd/stpmic1.c ++++ b/drivers/mfd/stpmic1.c +@@ -268,13 +268,8 @@ static int stpmic1_probe(struct i2c_client *i2c, + return ddata->irq; + } + +- ddata->irq_wake = of_irq_get(np, STPMIC1_WAKEUP_IRQ); +- if (ddata->irq_wake > 0) { ++ if (of_property_read_bool(np, "wakeup-source")) + device_init_wakeup(dev, true); +- ret = dev_pm_set_dedicated_wake_irq(dev, ddata->irq_wake); +- if (ret) +- dev_warn(dev, "failed to set up wakeup irq"); +- } + + if (!of_property_read_u32(np, "st,main-control-register", ®)) { + ret = regmap_update_bits(ddata->regmap, +@@ -371,8 +366,8 @@ static int stpmic1_suspend(struct device *dev) + struct stpmic1 *pmic_dev = i2c_get_clientdata(i2c); + + disable_irq(pmic_dev->irq); +- if ((pmic_dev->irq_wake > 0) && device_may_wakeup(dev)) +- enable_irq_wake(pmic_dev->irq_wake); ++ if (device_may_wakeup(dev)) ++ enable_irq_wake(pmic_dev->irq); + + return 0; + } +@@ -387,9 +382,10 @@ static int stpmic1_resume(struct device *dev) + if (ret) + return ret; + ++ if (device_may_wakeup(dev)) ++ disable_irq_wake(pmic_dev->irq); ++ + enable_irq(pmic_dev->irq); +- if ((pmic_dev->irq_wake > 0) && device_may_wakeup(dev)) +- disable_irq_wake(pmic_dev->irq_wake); + + return 0; + } +diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c +index b6d05cd..a0ba4ff 100644 +--- a/drivers/mfd/syscon.c ++++ b/drivers/mfd/syscon.c +@@ -12,6 +12,7 @@ + * (at your option) any later version. + */ + ++#include + #include + #include + #include +@@ -45,6 +46,7 @@ static const struct regmap_config syscon_regmap_config = { + + static struct syscon *of_syscon_register(struct device_node *np) + { ++ struct clk *clk; + struct syscon *syscon; + struct regmap *regmap; + void __iomem *base; +@@ -119,6 +121,18 @@ static struct syscon *of_syscon_register(struct device_node *np) + goto err_regmap; + } + ++ clk = of_clk_get(np, 0); ++ if (IS_ERR(clk)) { ++ ret = PTR_ERR(clk); ++ /* clock is optional */ ++ if (ret != -ENOENT) ++ goto err_clk; ++ } else { ++ ret = regmap_mmio_attach_clk(regmap, clk); ++ if (ret) ++ goto err_attach; ++ } ++ + syscon->regmap = regmap; + syscon->np = np; + +@@ -128,6 +142,11 @@ static struct syscon *of_syscon_register(struct device_node *np) + + return syscon; + ++err_attach: ++ if (!IS_ERR(clk)) ++ clk_put(clk); ++err_clk: ++ regmap_exit(regmap); + err_regmap: + iounmap(base); + err_map: +diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c +index 22bd652..755863a 100644 +--- a/drivers/mfd/wm8994-core.c ++++ b/drivers/mfd/wm8994-core.c +@@ -12,6 +12,7 @@ + * + */ + ++#include + #include + #include + #include +@@ -314,6 +315,20 @@ static int wm8994_set_pdata_from_of(struct wm8994 *wm8994) + if (pdata->ldo[1].enable < 0) + pdata->ldo[1].enable = 0; + ++ pdata->mclk1 = devm_clk_get(wm8994->dev, "MCLK1"); ++ if (IS_ERR(pdata->mclk1)) { ++ if (PTR_ERR(pdata->mclk1) != -ENOENT) ++ return PTR_ERR(pdata->mclk1); ++ pdata->mclk1 = NULL; ++ } ++ ++ pdata->mclk2 = devm_clk_get(wm8994->dev, "MCLK2"); ++ if (IS_ERR(pdata->mclk2)) { ++ if (PTR_ERR(pdata->mclk2) != -ENOENT) ++ return PTR_ERR(pdata->mclk2); ++ pdata->mclk2 = NULL; ++ } ++ + return 0; + } + #else +diff --git a/include/linux/mfd/stpmic1.h b/include/linux/mfd/stpmic1.h +index 4abe5f1..fa3f99f 100644 +--- a/include/linux/mfd/stpmic1.h ++++ b/include/linux/mfd/stpmic1.h +@@ -206,7 +206,6 @@ struct stpmic1 { + struct device *dev; + struct regmap *regmap; + int irq; +- int irq_wake; + struct regmap_irq_chip_data *irq_data; + }; + +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0023-ARM-stm32mp1-r0-rc2-USB.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0023-ARM-stm32mp1-r0-rc2-USB.patch new file mode 100644 index 0000000..c0eede0 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0023-ARM-stm32mp1-r0-rc2-USB.patch @@ -0,0 +1,1199 @@ +From 1829c178ee6739cc89484f6f6dc475a257e713ef Mon Sep 17 00:00:00 2001 +From: Christophe Priouzeau +Date: Mon, 26 Nov 2018 14:49:28 +0100 +Subject: [PATCH 23/52] ARM-stm32mp1-r0-rc2-USB + +--- + .../devicetree/bindings/phy/phy-stm32-usbphyc.txt | 65 ++- + Documentation/devicetree/bindings/usb/dwc2.txt | 7 + + Documentation/devicetree/bindings/usb/usb-ehci.txt | 1 + + drivers/phy/st/phy-stm32-usbphyc.c | 451 ++++++++++++++++++--- + drivers/usb/dwc2/core.c | 2 + + drivers/usb/dwc2/core.h | 7 + + drivers/usb/dwc2/hcd.c | 60 +-- + drivers/usb/dwc2/hw.h | 2 + + drivers/usb/dwc2/params.c | 31 ++ + drivers/usb/dwc2/platform.c | 47 +++ + drivers/usb/host/ehci-platform.c | 32 ++ + 11 files changed, 602 insertions(+), 103 deletions(-) + +diff --git a/Documentation/devicetree/bindings/phy/phy-stm32-usbphyc.txt b/Documentation/devicetree/bindings/phy/phy-stm32-usbphyc.txt +index 725ae71..cc44bf4 100644 +--- a/Documentation/devicetree/bindings/phy/phy-stm32-usbphyc.txt ++++ b/Documentation/devicetree/bindings/phy/phy-stm32-usbphyc.txt +@@ -23,8 +23,12 @@ Required properties: + - compatible: must be "st,stm32mp1-usbphyc" + - reg: address and length of the usb phy control register set + - clocks: phandle + clock specifier for the PLL phy clock ++- vdda1v1-supply: phandle to the regulator providing 1V1 power to the PHY ++- vdda1v8-supply: phandle to the regulator providing 1V8 power to the PHY ++- vdd3v3-supply: phandle to the regulator providing 3V3 power to the PHY + - #address-cells: number of address cells for phys sub-nodes, must be <1> + - #size-cells: number of size cells for phys sub-nodes, must be <0> ++- #clock-cells: number of clock cells for ck_usbo_48m consumer, must be <0> + + Optional properties: + - assigned-clocks: phandle + clock specifier for the PLL phy clock +@@ -34,40 +38,79 @@ Optional properties: + Required nodes: one sub-node per port the controller provides. + + Phy sub-nodes +-============== ++============= + + Required properties: + - reg: phy port index +-- phy-supply: phandle to the regulator providing 3V3 power to the PHY, +- see phy-bindings.txt in the same directory. +-- vdda1v1-supply: phandle to the regulator providing 1V1 power to the PHY +-- vdda1v8-supply: phandle to the regulator providing 1V8 power to the PHY + - #phy-cells: see phy-bindings.txt in the same directory, must be <0> for PHY + port#1 and must be <1> for PHY port#2, to select USB controller + ++Optional properties: ++- st,phy-tuning : phandle to the usb phy tuning node, see Phy tuning node below ++ ++Phy tuning node ++=============== ++ ++It may be necessary to adjust the phy settings to compensate parasitics, which ++can be due to USB connector/receptacle, routing, ESD protection component, ... ++ ++Here is the list of all optional parameters to tune the interface of the phy ++(HS for High-Speed, FS for Full-Speed, LS for Low-Speed) ++ ++Optional properties: ++- st,current-boost: <1> current boosting of 1mA ++ <2> current boosting of 2mA ++- st,no-lsfs-fb-cap: disables the LS/FS feedback capacitor ++- st,hs-slew-ctrl: slows the HS driver slew rate by 10% ++- st,hs-dc-level: <0> decreases the HS driver DC level by 5 to 7mV ++ <1> increases the HS driver DC level by 5 to 7mV ++ <2> increases the HS driver DC level by 10 to 14mV ++- st,fs-rftime-tuning: enables the FS rise/fall tuning option ++- st,hs-rftime-reduction: enables the HS rise/fall reduction feature ++- st,hs-current-trim: controls HS driver current trimming for choke ++- st,hs-impedance-trim: controls HS driver impedance tuning for choke ++- st,squelch-level: adjusts the squelch DC threshold value ++- st,hs-rx-gain-eq: enables the HS Rx gain equalizer ++- st,hs-rx-offset: adjusts the HS Rx offset ++- st,no-hs-ftime-ctrl: disables the HS fall time control of single ++ ended signals during pre-emphasis ++- st,no-lsfs-sc: disables the short circuit protection in LS/FS driver ++- st,hs-tx-staggering: enables the basic staggering in HS Tx mode ++ + + Example: ++ usb_phy_tuning: usb-phy-tuning { ++ st,current-boost = <2>; ++ st,no-lfs-fb-cap; ++ st,hs-dc-level = <2>; ++ st,hs-rftime-reduction; ++ st,hs-current-trim = <5>; ++ st,hs-impedance-trim = <0>; ++ st,squelch-level = <1>; ++ st,no-hs-ftime-ctrl; ++ st,hs-tx-staggering; ++ }; ++ + usbphyc: usb-phy@5a006000 { + compatible = "st,stm32mp1-usbphyc"; + reg = <0x5a006000 0x1000>; + clocks = <&rcc_clk USBPHY_K>; + resets = <&rcc_rst USBPHY_R>; ++ vdda1v1-supply = <®11>; ++ vdda1v8-supply = <®18>; ++ vdd3v3-supply = <&vdd_usb>; + #address-cells = <1>; + #size-cells = <0>; ++ #clock-cells = <0>; + + usbphyc_port0: usb-phy@0 { + reg = <0>; +- phy-supply = <&vdd_usb>; +- vdda1v1-supply = <®11>; +- vdda1v8-supply = <®18> + #phy-cells = <0>; + }; + + usbphyc_port1: usb-phy@1 { + reg = <1>; +- phy-supply = <&vdd_usb>; +- vdda1v1-supply = <®11>; +- vdda1v8-supply = <®18> + #phy-cells = <1>; ++ st,phy-tuning = <&usb_phy_tuning>; + }; + }; +diff --git a/Documentation/devicetree/bindings/usb/dwc2.txt b/Documentation/devicetree/bindings/usb/dwc2.txt +index 46da5f1..32b245c 100644 +--- a/Documentation/devicetree/bindings/usb/dwc2.txt ++++ b/Documentation/devicetree/bindings/usb/dwc2.txt +@@ -21,6 +21,9 @@ Required properties: + configured in HS mode; + - "st,stm32f7-hsotg": The DWC2 USB HS controller instance in STM32F7 SoCs + configured in HS mode; ++ - "st,stm32mp1-fsotg": The DWC2 USB controller instance in STM32MP1 SoCs, ++ configured in FS mode (using dedicated FS transceiver). ++ - "st,stm32mp1-hsotg": The DWC2 USB controller instance in STM32MP1 SoCs; + - reg : Should contain 1 register range (address and length) + - interrupts : Should contain 1 interrupt + - clocks: clock provider specifier +@@ -36,6 +39,10 @@ Refer to phy/phy-bindings.txt for generic phy consumer properties + - g-rx-fifo-size: size of rx fifo size in gadget mode. + - g-np-tx-fifo-size: size of non-periodic tx fifo size in gadget mode. + - g-tx-fifo-size: size of periodic tx fifo per endpoint (except ep0) in gadget mode. ++- vbus-supply: in Host mode, external VBUS charge pump, when drvvbus signal ++ doesn't drive it. ++- usb33d-supply: external VBUS and ID sensing comparators supply, in order to ++ perform OTG operation, used on STM32MP1 SoCs. + + Deprecated properties: + - g-use-dma: gadget DMA mode is automatically detected +diff --git a/Documentation/devicetree/bindings/usb/usb-ehci.txt b/Documentation/devicetree/bindings/usb/usb-ehci.txt +index 0f1b753..f5d91d0 100644 +--- a/Documentation/devicetree/bindings/usb/usb-ehci.txt ++++ b/Documentation/devicetree/bindings/usb/usb-ehci.txt +@@ -18,6 +18,7 @@ Optional properties: + - clocks : a list of phandle + clock specifier pairs + - phys : see usb-hcd.txt in the current directory + - resets : phandle + reset specifier pair ++ - vbus-supply : phandle of regulator supplying vbus + + additionally the properties from usb-hcd.txt (in the current directory) are + supported. +diff --git a/drivers/phy/st/phy-stm32-usbphyc.c b/drivers/phy/st/phy-stm32-usbphyc.c +index 1255cd1..c9c3c3e 100644 +--- a/drivers/phy/st/phy-stm32-usbphyc.c ++++ b/drivers/phy/st/phy-stm32-usbphyc.c +@@ -1,4 +1,4 @@ +-// SPDX-Licence-Identifier: GPL-2.0 ++// SPDX-License-Identifier: GPL-2.0 + /* + * STMicroelectronics STM32 USB PHY Controller driver + * +@@ -7,6 +7,7 @@ + */ + #include + #include ++#include + #include + #include + #include +@@ -17,6 +18,7 @@ + + #define STM32_USBPHYC_PLL 0x0 + #define STM32_USBPHYC_MISC 0x8 ++#define STM32_USBPHYC_TUNE(X) (0x10C + (X * 0x100)) + #define STM32_USBPHYC_VERSION 0x3F4 + + /* STM32_USBPHYC_PLL bit fields */ +@@ -32,16 +34,86 @@ + /* STM32_USBPHYC_MISC bit fields */ + #define SWITHOST BIT(0) + +-/* STM32_USBPHYC_VERSION bit fields */ +-#define MINREV GENMASK(3, 0) +-#define MAJREV GENMASK(7, 4) ++/* STM32_USBPHYC_TUNE bit fields */ ++#define INCURREN BIT(0) ++#define INCURRINT BIT(1) ++#define LFSCAPEN BIT(2) ++#define HSDRVSLEW BIT(3) ++#define HSDRVDCCUR BIT(4) ++#define HSDRVDCLEV BIT(5) ++#define HSDRVCURINCR BIT(6) ++#define FSDRVRFADJ BIT(7) ++#define HSDRVRFRED BIT(8) ++#define HSDRVCHKITRM GENMASK(12, 9) ++#define HSDRVCHKZTRM GENMASK(14, 13) ++#define OTPCOMP GENMASK(19, 15) ++#define SQLCHCTL GENMASK(21, 20) ++#define HDRXGNEQEN BIT(22) ++#define HSRXOFF GENMASK(24, 23) ++#define HSFALLPREEM BIT(25) ++#define SHTCCTCTLPROT BIT(26) ++#define STAGSEL BIT(27) ++ ++enum boosting_vals { ++ BOOST_1_MA = 1, ++ BOOST_2_MA, ++ BOOST_MAX, ++}; ++ ++enum dc_level_vals { ++ DC_MINUS_5_TO_7_MV, ++ DC_PLUS_5_TO_7_MV, ++ DC_PLUS_10_TO_14_MV, ++ DC_MAX, ++}; + +-static const char * const supplies_names[] = { +- "vdda1v1", /* 1V1 */ +- "vdda1v8", /* 1V8 */ ++enum current_trim { ++ CUR_NOMINAL, ++ CUR_PLUS_1_56_PCT, ++ CUR_PLUS_3_12_PCT, ++ CUR_PLUS_4_68_PCT, ++ CUR_PLUS_6_24_PCT, ++ CUR_PLUS_7_8_PCT, ++ CUR_PLUS_9_36_PCT, ++ CUR_PLUS_10_92_PCT, ++ CUR_PLUS_12_48_PCT, ++ CUR_PLUS_14_04_PCT, ++ CUR_PLUS_15_6_PCT, ++ CUR_PLUS_17_16_PCT, ++ CUR_PLUS_19_01_PCT, ++ CUR_PLUS_20_58_PCT, ++ CUR_PLUS_22_16_PCT, ++ CUR_PLUS_23_73_PCT, ++ CUR_MAX, + }; + +-#define NUM_SUPPLIES ARRAY_SIZE(supplies_names) ++enum impedance_trim { ++ IMP_NOMINAL, ++ IMP_MINUS_2_OHMS, ++ IMP_MINUS_4_OMHS, ++ IMP_MINUS_6_OHMS, ++ IMP_MAX, ++}; ++ ++enum squelch_level { ++ SQLCH_NOMINAL, ++ SQLCH_PLUS_7_MV, ++ SQLCH_MINUS_5_MV, ++ SQLCH_PLUS_14_MV, ++ SQLCH_MAX, ++}; ++ ++enum rx_offset { ++ NO_RX_OFFSET, ++ RX_OFFSET_PLUS_5_MV, ++ RX_OFFSET_PLUS_10_MV, ++ RX_OFFSET_MINUS_5_MV, ++ RX_OFFSET_MAX, ++}; ++ ++/* STM32_USBPHYC_VERSION bit fields */ ++#define MINREV GENMASK(3, 0) ++#define MAJREV GENMASK(7, 4) + + #define PLL_LOCK_TIME_US 100 + #define PLL_PWR_DOWN_TIME_US 5 +@@ -58,7 +130,6 @@ struct pll_params { + struct stm32_usbphyc_phy { + struct phy *phy; + struct stm32_usbphyc *usbphyc; +- struct regulator_bulk_data supplies[NUM_SUPPLIES]; + u32 index; + bool active; + }; +@@ -70,6 +141,10 @@ struct stm32_usbphyc { + struct reset_control *rst; + struct stm32_usbphyc_phy **phys; + int nphys; ++ struct regulator *vdda1v1; ++ struct regulator *vdda1v8; ++ struct regulator *vdd3v3; ++ struct clk_hw clk48_hw; + int switch_setup; + }; + +@@ -83,6 +158,49 @@ static inline void stm32_usbphyc_clr_bits(void __iomem *reg, u32 bits) + writel_relaxed(readl_relaxed(reg) & ~bits, reg); + } + ++static int stm32_usbphyc_regulators_enable(struct stm32_usbphyc *usbphyc) ++{ ++ int ret; ++ ++ ret = regulator_enable(usbphyc->vdda1v1); ++ if (ret) ++ return ret; ++ ++ ret = regulator_enable(usbphyc->vdda1v8); ++ if (ret) ++ goto vdda1v1_disable; ++ ++ ret = regulator_enable(usbphyc->vdd3v3); ++ if (ret) ++ goto vdda1v8_disable; ++ ++ return 0; ++ ++vdda1v8_disable: ++ regulator_disable(usbphyc->vdda1v8); ++vdda1v1_disable: ++ regulator_disable(usbphyc->vdda1v1); ++ ++ return ret; ++} ++ ++static int stm32_usbphyc_regulators_disable(struct stm32_usbphyc *usbphyc) ++{ ++ int ret; ++ ++ ret = regulator_disable(usbphyc->vdd3v3); ++ if (ret) ++ return ret; ++ ret = regulator_disable(usbphyc->vdda1v8); ++ if (ret) ++ return ret; ++ ret = regulator_disable(usbphyc->vdda1v1); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ + static void stm32_usbphyc_get_pll_params(u32 clk_rate, + struct pll_params *pll_params) + { +@@ -142,7 +260,7 @@ static int stm32_usbphyc_pll_init(struct stm32_usbphyc *usbphyc) + return 0; + } + +-static bool stm32_usbphyc_has_one_phy_active(struct stm32_usbphyc *usbphyc) ++static bool stm32_usbphyc_has_one_pll_consumer(struct stm32_usbphyc *usbphyc) + { + int i; + +@@ -150,60 +268,72 @@ static bool stm32_usbphyc_has_one_phy_active(struct stm32_usbphyc *usbphyc) + if (usbphyc->phys[i]->active) + return true; + ++ if (clk_hw_is_enabled(&usbphyc->clk48_hw)) ++ return true; ++ + return false; + } + ++static int stm32_usbphyc_pll_disable(struct stm32_usbphyc *usbphyc) ++{ ++ void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL; ++ ++ /* Check if a phy port is still active or clk48 in use */ ++ if (stm32_usbphyc_has_one_pll_consumer(usbphyc)) ++ return 0; ++ ++ stm32_usbphyc_clr_bits(pll_reg, PLLEN); ++ /* Wait for minimum width of powerdown pulse (ENABLE = Low) */ ++ udelay(PLL_PWR_DOWN_TIME_US); ++ ++ if (readl_relaxed(pll_reg) & PLLEN) { ++ dev_err(usbphyc->dev, "PLL not reset\n"); ++ return -EIO; ++ } ++ ++ return stm32_usbphyc_regulators_disable(usbphyc); ++} ++ + static int stm32_usbphyc_pll_enable(struct stm32_usbphyc *usbphyc) + { + void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL; +- bool pllen = (readl_relaxed(pll_reg) & PLLEN); ++ bool pllen = readl_relaxed(pll_reg) & PLLEN; + int ret; + +- /* Check if one phy port has already configured the pll */ +- if (pllen && stm32_usbphyc_has_one_phy_active(usbphyc)) ++ /* Check if a phy port or clk48 enable has configured the pll */ ++ if (pllen && stm32_usbphyc_has_one_pll_consumer(usbphyc)) + return 0; + + if (pllen) { +- stm32_usbphyc_clr_bits(pll_reg, PLLEN); +- /* Wait for minimum width of powerdown pulse (ENABLE = Low) */ +- udelay(PLL_PWR_DOWN_TIME_US); ++ ret = stm32_usbphyc_pll_disable(usbphyc); ++ if (ret) ++ return ret; + } + +- ret = stm32_usbphyc_pll_init(usbphyc); ++ ret = stm32_usbphyc_regulators_enable(usbphyc); + if (ret) + return ret; + +- stm32_usbphyc_set_bits(pll_reg, PLLEN); ++ ret = stm32_usbphyc_pll_init(usbphyc); ++ if (ret) ++ goto reg_disable; + ++ stm32_usbphyc_set_bits(pll_reg, PLLEN); + /* Wait for maximum lock time */ + udelay(PLL_LOCK_TIME_US); + + if (!(readl_relaxed(pll_reg) & PLLEN)) { + dev_err(usbphyc->dev, "PLLEN not set\n"); +- return -EIO; ++ ret = -EIO; ++ goto reg_disable; + } + + return 0; +-} +- +-static int stm32_usbphyc_pll_disable(struct stm32_usbphyc *usbphyc) +-{ +- void __iomem *pll_reg = usbphyc->base + STM32_USBPHYC_PLL; +- +- /* Check if other phy port active */ +- if (stm32_usbphyc_has_one_phy_active(usbphyc)) +- return 0; +- +- stm32_usbphyc_clr_bits(pll_reg, PLLEN); +- /* Wait for minimum width of powerdown pulse (ENABLE = Low) */ +- udelay(PLL_PWR_DOWN_TIME_US); + +- if (readl_relaxed(pll_reg) & PLLEN) { +- dev_err(usbphyc->dev, "PLL not reset\n"); +- return -EIO; +- } ++reg_disable: ++ stm32_usbphyc_regulators_disable(usbphyc); + +- return 0; ++ return ret; + } + + static int stm32_usbphyc_phy_init(struct phy *phy) +@@ -231,28 +361,180 @@ static int stm32_usbphyc_phy_exit(struct phy *phy) + return stm32_usbphyc_pll_disable(usbphyc); + } + +-static int stm32_usbphyc_phy_power_on(struct phy *phy) ++static const struct phy_ops stm32_usbphyc_phy_ops = { ++ .init = stm32_usbphyc_phy_init, ++ .exit = stm32_usbphyc_phy_exit, ++ .owner = THIS_MODULE, ++}; ++ ++static int stm32_usbphyc_clk48_prepare(struct clk_hw *hw) + { +- struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy); ++ struct stm32_usbphyc *usbphyc = container_of(hw, struct stm32_usbphyc, ++ clk48_hw); + +- return regulator_bulk_enable(NUM_SUPPLIES, usbphyc_phy->supplies); ++ return stm32_usbphyc_pll_enable(usbphyc); + } + +-static int stm32_usbphyc_phy_power_off(struct phy *phy) ++static void stm32_usbphyc_clk48_unprepare(struct clk_hw *hw) + { +- struct stm32_usbphyc_phy *usbphyc_phy = phy_get_drvdata(phy); ++ struct stm32_usbphyc *usbphyc = container_of(hw, struct stm32_usbphyc, ++ clk48_hw); + +- return regulator_bulk_disable(NUM_SUPPLIES, usbphyc_phy->supplies); ++ stm32_usbphyc_pll_disable(usbphyc); + } + +-static const struct phy_ops stm32_usbphyc_phy_ops = { +- .init = stm32_usbphyc_phy_init, +- .exit = stm32_usbphyc_phy_exit, +- .power_on = stm32_usbphyc_phy_power_on, +- .power_off = stm32_usbphyc_phy_power_off, +- .owner = THIS_MODULE, ++static int stm32_usbphyc_clk48_is_enabled(struct clk_hw *hw) ++{ ++ struct stm32_usbphyc *usbphyc = container_of(hw, struct stm32_usbphyc, ++ clk48_hw); ++ ++ return readl_relaxed(usbphyc->base + STM32_USBPHYC_PLL) & PLLEN; ++} ++ ++static unsigned long stm32_usbphyc_clk48_recalc_rate(struct clk_hw *hw, ++ unsigned long parent_rate) ++{ ++ return 48000000; ++} ++ ++static const struct clk_ops usbphyc_clk48_ops = { ++ .prepare = stm32_usbphyc_clk48_prepare, ++ .unprepare = stm32_usbphyc_clk48_unprepare, ++ .is_enabled = stm32_usbphyc_clk48_is_enabled, ++ .recalc_rate = stm32_usbphyc_clk48_recalc_rate, + }; + ++static void stm32_usbphyc_clk48_unregister(void *data) ++{ ++ struct stm32_usbphyc *usbphyc = data; ++ ++ of_clk_del_provider(usbphyc->dev->of_node); ++ clk_hw_unregister(&usbphyc->clk48_hw); ++} ++ ++static int stm32_usbphyc_clk48_register(struct stm32_usbphyc *usbphyc) ++{ ++ struct device_node *node = usbphyc->dev->of_node; ++ struct clk_init_data init = { }; ++ int ret = 0; ++ ++ init.name = "ck_usbo_48m"; ++ init.ops = &usbphyc_clk48_ops; ++ ++ usbphyc->clk48_hw.init = &init; ++ ++ ret = clk_hw_register(usbphyc->dev, &usbphyc->clk48_hw); ++ if (ret) ++ return ret; ++ ++ ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, ++ &usbphyc->clk48_hw); ++ if (ret) ++ return ret; ++ ++ ret = devm_add_action(usbphyc->dev, stm32_usbphyc_clk48_unregister, ++ usbphyc); ++ ++ return ret; ++} ++ ++static void stm32_usbphyc_phy_tuning(struct stm32_usbphyc *usbphyc, ++ struct device_node *np, u32 index) ++{ ++ struct device_node *tune_np; ++ u32 reg = STM32_USBPHYC_TUNE(index); ++ u32 otpcomp, val, tune = 0; ++ int ret; ++ ++ tune_np = of_parse_phandle(np, "st,phy-tuning", 0); ++ if (!tune_np) ++ return; ++ ++ /* Backup OTP compensation code */ ++ otpcomp = FIELD_GET(OTPCOMP, readl_relaxed(usbphyc->base + reg)); ++ ++ ret = of_property_read_u32(tune_np, "st,current-boost", &val); ++ if (!ret && val < BOOST_MAX) { ++ val = (val == BOOST_2_MA) ? 1 : 0; ++ tune |= INCURREN | FIELD_PREP(INCURRINT, val); ++ } else if (ret != -EINVAL) { ++ dev_warn(usbphyc->dev, ++ "phy%d: invalid st,current-boost value\n", index); ++ } ++ ++ if (!of_property_read_bool(tune_np, "st,no-lsfs-fb-cap")) ++ tune |= LFSCAPEN; ++ ++ if (of_property_read_bool(tune_np, "st,hs-slew-ctrl")) ++ tune |= HSDRVSLEW; ++ ++ ret = of_property_read_u32(tune_np, "st,hs-dc-level", &val); ++ if (!ret && val < DC_MAX) { ++ if (val == DC_MINUS_5_TO_7_MV) { ++ tune |= HSDRVDCCUR; ++ } else { ++ val = (val == DC_PLUS_10_TO_14_MV) ? 1 : 0; ++ tune |= HSDRVCURINCR | FIELD_PREP(HSDRVDCLEV, val); ++ } ++ } else if (ret != -EINVAL) { ++ dev_warn(usbphyc->dev, ++ "phy%d: invalid st,hs-dc-level value\n", index); ++ } ++ ++ if (of_property_read_bool(tune_np, "st,fs-rftime-tuning")) ++ tune |= FSDRVRFADJ; ++ ++ if (of_property_read_bool(tune_np, "st,hs-rftime-reduction")) ++ tune |= HSDRVRFRED; ++ ++ ret = of_property_read_u32(tune_np, "st,hs-current-trim", &val); ++ if (!ret && val < CUR_MAX) ++ tune |= FIELD_PREP(HSDRVCHKITRM, val); ++ else if (ret != -EINVAL) ++ dev_warn(usbphyc->dev, ++ "phy%d: invalid st,hs-current-trim value\n", index); ++ ++ ret = of_property_read_u32(tune_np, "st,hs-impedance-trim", &val); ++ if (!ret && val < IMP_MAX) ++ tune |= FIELD_PREP(HSDRVCHKZTRM, val); ++ else if (ret != -EINVAL) ++ dev_warn(usbphyc->dev, ++ "phy%d: invalid hs-impedance-trim value\n", index); ++ ++ ret = of_property_read_u32(tune_np, "st,squelch-level", &val); ++ if (!ret && val < SQLCH_MAX) ++ tune |= FIELD_PREP(SQLCHCTL, val); ++ else if (ret != -EINVAL) ++ dev_warn(usbphyc->dev, ++ "phy%d: invalid st,squelch-level value\n", index); ++ ++ if (of_property_read_bool(tune_np, "st,hs-rx-gain-eq")) ++ tune |= HDRXGNEQEN; ++ ++ ret = of_property_read_u32(tune_np, "st,hs-rx-offset", &val); ++ if (!ret && val < RX_OFFSET_MAX) ++ tune |= FIELD_PREP(HSRXOFF, val); ++ else if (ret != -EINVAL) ++ dev_warn(usbphyc->dev, ++ "phy%d: invalid st,hs-rx-offset value\n", index); ++ ++ if (of_property_read_bool(tune_np, "st,no-hs-ftime-ctrl")) ++ tune |= HSFALLPREEM; ++ ++ if (!of_property_read_bool(tune_np, "st,no-lsfs-sc")) ++ tune |= SHTCCTCTLPROT; ++ ++ if (of_property_read_bool(tune_np, "st,hs-tx-staggering")) ++ tune |= STAGSEL; ++ ++ of_node_put(tune_np); ++ ++ /* Restore OTP compensation code */ ++ tune |= FIELD_PREP(OTPCOMP, otpcomp); ++ ++ writel_relaxed(tune, usbphyc->base + reg); ++} ++ + static void stm32_usbphyc_switch_setup(struct stm32_usbphyc *usbphyc, + u32 utmi_switch) + { +@@ -345,7 +627,16 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) + reset_control_assert(usbphyc->rst); + udelay(2); + reset_control_deassert(usbphyc->rst); ++ } else { ++ stm32_usbphyc_clr_bits(usbphyc->base + STM32_USBPHYC_PLL, ++ PLLEN); + } ++ /* Wait for minimum width of powerdown pulse (ENABLE = Low) */ ++ udelay(PLL_PWR_DOWN_TIME_US); ++ ++ /* We have to ensure the PLL is disabled before phys initialization */ ++ if (readl_relaxed(usbphyc->base + STM32_USBPHYC_PLL) & PLLEN) ++ return -EPROBE_DEFER; + + usbphyc->switch_setup = -EINVAL; + usbphyc->nphys = of_get_child_count(np); +@@ -356,11 +647,34 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) + goto clk_disable; + } + ++ usbphyc->vdda1v1 = devm_regulator_get(dev, "vdda1v1"); ++ if (IS_ERR(usbphyc->vdda1v1)) { ++ ret = PTR_ERR(usbphyc->vdda1v1); ++ if (ret != -EPROBE_DEFER) ++ dev_err(dev, "failed to get vdda1v1 supply: %d\n", ret); ++ goto clk_disable; ++ } ++ ++ usbphyc->vdda1v8 = devm_regulator_get(dev, "vdda1v8"); ++ if (IS_ERR(usbphyc->vdda1v8)) { ++ ret = PTR_ERR(usbphyc->vdda1v8); ++ if (ret != -EPROBE_DEFER) ++ dev_err(dev, "failed to get vdda1v8 supply: %d\n", ret); ++ goto clk_disable; ++ } ++ ++ usbphyc->vdd3v3 = devm_regulator_get(dev, "vdd3v3"); ++ if (IS_ERR(usbphyc->vdd3v3)) { ++ ret = PTR_ERR(usbphyc->vdd3v3); ++ if (ret != -EPROBE_DEFER) ++ dev_err(dev, "failed to get vdd3v3 supply: %d\n", ret); ++ goto clk_disable; ++ } ++ + for_each_child_of_node(np, child) { + struct stm32_usbphyc_phy *usbphyc_phy; + struct phy *phy; + u32 index; +- int i; + + phy = devm_phy_create(dev, child, &stm32_usbphyc_phy_ops); + if (IS_ERR(phy)) { +@@ -378,24 +692,15 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) + goto put_child; + } + +- for (i = 0; i < NUM_SUPPLIES; i++) +- usbphyc_phy->supplies[i].supply = supplies_names[i]; +- +- ret = devm_regulator_bulk_get(&phy->dev, NUM_SUPPLIES, +- usbphyc_phy->supplies); +- if (ret) { +- if (ret != -EPROBE_DEFER) +- dev_err(&phy->dev, +- "failed to get regulators: %d\n", ret); +- goto put_child; +- } +- + ret = of_property_read_u32(child, "reg", &index); + if (ret || index > usbphyc->nphys) { + dev_err(&phy->dev, "invalid reg property: %d\n", ret); + goto put_child; + } + ++ /* Configure phy tuning */ ++ stm32_usbphyc_phy_tuning(usbphyc, child, index); ++ + usbphyc->phys[port] = usbphyc_phy; + phy_set_bus_width(phy, 8); + phy_set_drvdata(phy, usbphyc_phy); +@@ -416,6 +721,13 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) + goto clk_disable; + } + ++ ret = stm32_usbphyc_clk48_register(usbphyc); ++ if (ret) { ++ dev_err(dev, ++ "failed to register ck_usbo_48m clock: %d\n", ret); ++ goto clk_disable; ++ } ++ + version = readl_relaxed(usbphyc->base + STM32_USBPHYC_VERSION); + dev_info(dev, "registered rev:%lu.%lu\n", + FIELD_GET(MAJREV, version), FIELD_GET(MINREV, version)); +@@ -439,6 +751,20 @@ static int stm32_usbphyc_remove(struct platform_device *pdev) + return 0; + } + ++#ifdef CONFIG_PM_SLEEP ++static int stm32_usbphyc_resume(struct device *dev) ++{ ++ struct stm32_usbphyc *usbphyc = dev_get_drvdata(dev); ++ ++ if (usbphyc->switch_setup >= 0) ++ stm32_usbphyc_switch_setup(usbphyc, usbphyc->switch_setup); ++ ++ return 0; ++} ++#endif ++ ++static SIMPLE_DEV_PM_OPS(stm32_usbphyc_pm_ops, NULL, stm32_usbphyc_resume); ++ + static const struct of_device_id stm32_usbphyc_of_match[] = { + { .compatible = "st,stm32mp1-usbphyc", }, + { }, +@@ -451,6 +777,7 @@ static struct platform_driver stm32_usbphyc_driver = { + .driver = { + .of_match_table = stm32_usbphyc_of_match, + .name = "stm32-usbphyc", ++ .pm = &stm32_usbphyc_pm_ops, + } + }; + module_platform_driver(stm32_usbphyc_driver); +diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c +index 55d5ae2..a298fae 100644 +--- a/drivers/usb/dwc2/core.c ++++ b/drivers/usb/dwc2/core.c +@@ -83,6 +83,7 @@ int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg) + gr->pcgcctl1 = dwc2_readl(hsotg, PCGCCTL1); + gr->glpmcfg = dwc2_readl(hsotg, GLPMCFG); + gr->gi2cctl = dwc2_readl(hsotg, GI2CCTL); ++ gr->ggpio = dwc2_readl(hsotg, GGPIO); + gr->pcgcctl = dwc2_readl(hsotg, PCGCTL); + + gr->valid = true; +@@ -122,6 +123,7 @@ int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg) + dwc2_writel(hsotg, gr->pcgcctl1, PCGCCTL1); + dwc2_writel(hsotg, gr->glpmcfg, GLPMCFG); + dwc2_writel(hsotg, gr->pcgcctl, PCGCTL); ++ dwc2_writel(hsotg, gr->ggpio, GGPIO); + dwc2_writel(hsotg, gr->gi2cctl, GI2CCTL); + + return 0; +diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h +index cc9c93a..4c689d1 100644 +--- a/drivers/usb/dwc2/core.h ++++ b/drivers/usb/dwc2/core.h +@@ -397,6 +397,10 @@ enum dwc2_ep0_state { + * register. + * 0 - Deactivate the transceiver (default) + * 1 - Activate the transceiver ++ * @activate_stm_id_vb_detection: Activate external ID pin and Vbuslevel ++ * detection using GGPIO register. ++ * 0 - Deactivate the external level detection (default) ++ * 1 - Activate the external level detection + * @g_dma: Enables gadget dma usage (default: autodetect). + * @g_dma_desc: Enables gadget descriptor DMA (default: autodetect). + * @g_rx_fifo_size: The periodic rx fifo size for the device, in +@@ -463,6 +467,7 @@ struct dwc2_core_params { + bool hird_threshold_en; + u8 hird_threshold; + bool activate_stm_fs_transceiver; ++ bool activate_stm_id_vb_detection; + bool ipg_isoc_en; + u16 max_packet_count; + u32 max_transfer_size; +@@ -653,6 +658,7 @@ struct dwc2_hw_params { + * @grxfsiz: Backup of GRXFSIZ register + * @gnptxfsiz: Backup of GNPTXFSIZ register + * @gi2cctl: Backup of GI2CCTL register ++ * @ggpio: Backup of GGPIO register + * @glpmcfg: Backup of GLPMCFG register + * @gdfifocfg: Backup of GDFIFOCFG register + * @pcgcctl: Backup of PCGCCTL register +@@ -669,6 +675,7 @@ struct dwc2_gregs_backup { + u32 grxfsiz; + u32 gnptxfsiz; + u32 gi2cctl; ++ u32 ggpio; + u32 glpmcfg; + u32 pcgcctl; + u32 pcgcctl1; +diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c +index 260010a..12fa6c0 100644 +--- a/drivers/usb/dwc2/hcd.c ++++ b/drivers/usb/dwc2/hcd.c +@@ -125,7 +125,7 @@ static void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg) + + static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) + { +- u32 usbcfg, ggpio, i2cctl; ++ u32 usbcfg, i2cctl; + int retval = 0; + + /* +@@ -149,19 +149,6 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) + return retval; + } + } +- +- if (hsotg->params.activate_stm_fs_transceiver) { +- ggpio = dwc2_readl(hsotg, GGPIO); +- if (!(ggpio & GGPIO_STM32_OTG_GCCFG_PWRDWN)) { +- dev_dbg(hsotg->dev, "Activating transceiver\n"); +- /* +- * STM32F4x9 uses the GGPIO register as general +- * core configuration register. +- */ +- ggpio |= GGPIO_STM32_OTG_GCCFG_PWRDWN; +- dwc2_writel(hsotg, ggpio, GGPIO); +- } +- } + } + + /* +@@ -358,16 +345,10 @@ static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg) + + static int dwc2_vbus_supply_init(struct dwc2_hsotg *hsotg) + { +- int ret; +- +- hsotg->vbus_supply = devm_regulator_get_optional(hsotg->dev, "vbus"); +- if (IS_ERR(hsotg->vbus_supply)) { +- ret = PTR_ERR(hsotg->vbus_supply); +- hsotg->vbus_supply = NULL; +- return ret == -ENODEV ? 0 : ret; +- } ++ if (hsotg->vbus_supply) ++ return regulator_enable(hsotg->vbus_supply); + +- return regulator_enable(hsotg->vbus_supply); ++ return 0; + } + + static int dwc2_vbus_supply_exit(struct dwc2_hsotg *hsotg) +@@ -3564,6 +3545,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, + u32 port_status; + u32 speed; + u32 pcgctl; ++ u32 pwr; + + switch (typereq) { + case ClearHubFeature: +@@ -3612,8 +3594,11 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, + dev_dbg(hsotg->dev, + "ClearPortFeature USB_PORT_FEAT_POWER\n"); + hprt0 = dwc2_read_hprt0(hsotg); ++ pwr = hprt0 & HPRT0_PWR; + hprt0 &= ~HPRT0_PWR; + dwc2_writel(hsotg, hprt0, HPRT0); ++ if (pwr) ++ dwc2_vbus_supply_exit(hsotg); + break; + + case USB_PORT_FEAT_INDICATOR: +@@ -3823,8 +3808,11 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, + dev_dbg(hsotg->dev, + "SetPortFeature - USB_PORT_FEAT_POWER\n"); + hprt0 = dwc2_read_hprt0(hsotg); ++ pwr = hprt0 & HPRT0_PWR; + hprt0 |= HPRT0_PWR; + dwc2_writel(hsotg, hprt0, HPRT0); ++ if (!pwr) ++ dwc2_vbus_supply_init(hsotg); + break; + + case USB_PORT_FEAT_RESET: +@@ -3841,6 +3829,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, + dwc2_writel(hsotg, 0, PCGCTL); + + hprt0 = dwc2_read_hprt0(hsotg); ++ pwr = hprt0 & HPRT0_PWR; + /* Clear suspend bit if resetting from suspend state */ + hprt0 &= ~HPRT0_SUSP; + +@@ -3854,6 +3843,8 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, + dev_dbg(hsotg->dev, + "In host mode, hprt0=%08x\n", hprt0); + dwc2_writel(hsotg, hprt0, HPRT0); ++ if (!pwr) ++ dwc2_vbus_supply_init(hsotg); + } + + /* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */ +@@ -4393,6 +4384,7 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd) + struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); + struct usb_bus *bus = hcd_to_bus(hcd); + unsigned long flags; ++ u32 hprt0; + int ret; + + dev_dbg(hsotg->dev, "DWC OTG HCD START\n"); +@@ -4409,12 +4401,16 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd) + + dwc2_hcd_reinit(hsotg); + +- /* enable external vbus supply before resuming root hub */ +- spin_unlock_irqrestore(&hsotg->lock, flags); +- ret = dwc2_vbus_supply_init(hsotg); +- if (ret) +- return ret; +- spin_lock_irqsave(&hsotg->lock, flags); ++ hprt0 = dwc2_read_hprt0(hsotg); ++ /* Has vbus power been turned on in dwc2_core_host_init ? */ ++ if (hprt0 & HPRT0_PWR) { ++ /* Enable external vbus supply before resuming root hub */ ++ spin_unlock_irqrestore(&hsotg->lock, flags); ++ ret = dwc2_vbus_supply_init(hsotg); ++ if (ret) ++ return ret; ++ spin_lock_irqsave(&hsotg->lock, flags); ++ } + + /* Initialize and connect root hub if one is not already attached */ + if (bus->root_hub) { +@@ -4436,6 +4432,7 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd) + { + struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); + unsigned long flags; ++ u32 hprt0; + + /* Turn off all host-specific interrupts */ + dwc2_disable_host_interrupts(hsotg); +@@ -4444,6 +4441,7 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd) + synchronize_irq(hcd->irq); + + spin_lock_irqsave(&hsotg->lock, flags); ++ hprt0 = dwc2_read_hprt0(hsotg); + /* Ensure hcd is disconnected */ + dwc2_hcd_disconnect(hsotg, true); + dwc2_hcd_stop(hsotg); +@@ -4452,7 +4450,9 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd) + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + spin_unlock_irqrestore(&hsotg->lock, flags); + +- dwc2_vbus_supply_exit(hsotg); ++ /* keep balanced supply init/exit by checking HPRT0_PWR */ ++ if (hprt0 & HPRT0_PWR) ++ dwc2_vbus_supply_exit(hsotg); + + usleep_range(1000, 3000); + } +diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h +index 0ca8e7b..afde335 100644 +--- a/drivers/usb/dwc2/hw.h ++++ b/drivers/usb/dwc2/hw.h +@@ -227,6 +227,8 @@ + #define GPVNDCTL HSOTG_REG(0x0034) + #define GGPIO HSOTG_REG(0x0038) + #define GGPIO_STM32_OTG_GCCFG_PWRDWN BIT(16) ++#define GGPIO_STM32_OTG_GCCFG_VBDEN BIT(21) ++#define GGPIO_STM32_OTG_GCCFG_IDEN BIT(22) + + #define GUID HSOTG_REG(0x003c) + #define GSNPSID HSOTG_REG(0x0040) +diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c +index bf7052e..63ccfc9 100644 +--- a/drivers/usb/dwc2/params.c ++++ b/drivers/usb/dwc2/params.c +@@ -143,6 +143,33 @@ static void dwc2_set_stm32f7_hsotg_params(struct dwc2_hsotg *hsotg) + p->host_perio_tx_fifo_size = 256; + } + ++static void dwc2_set_stm32mp1_fsotg_params(struct dwc2_hsotg *hsotg) ++{ ++ struct dwc2_core_params *p = &hsotg->params; ++ ++ p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; ++ p->speed = DWC2_SPEED_PARAM_FULL; ++ p->host_rx_fifo_size = 128; ++ p->host_nperio_tx_fifo_size = 96; ++ p->host_perio_tx_fifo_size = 96; ++ p->max_packet_count = 256; ++ p->phy_type = DWC2_PHY_TYPE_PARAM_FS; ++ p->i2c_enable = false; ++ p->activate_stm_fs_transceiver = true; ++ p->activate_stm_id_vb_detection = true; ++} ++ ++static void dwc2_set_stm32mp1_hsotg_params(struct dwc2_hsotg *hsotg) ++{ ++ struct dwc2_core_params *p = &hsotg->params; ++ ++ p->activate_stm_id_vb_detection = true; ++ p->host_rx_fifo_size = 440; ++ p->host_nperio_tx_fifo_size = 256; ++ p->host_perio_tx_fifo_size = 256; ++ p->power_down = false; ++} ++ + const struct of_device_id dwc2_of_match_table[] = { + { .compatible = "brcm,bcm2835-usb", .data = dwc2_set_bcm_params }, + { .compatible = "hisilicon,hi6220-usb", .data = dwc2_set_his_params }, +@@ -163,6 +190,10 @@ const struct of_device_id dwc2_of_match_table[] = { + { .compatible = "st,stm32f4x9-hsotg" }, + { .compatible = "st,stm32f7-hsotg", + .data = dwc2_set_stm32f7_hsotg_params }, ++ { .compatible = "st,stm32mp1-fsotg", ++ .data = dwc2_set_stm32mp1_fsotg_params }, ++ { .compatible = "st,stm32mp1-hsotg", ++ .data = dwc2_set_stm32mp1_hsotg_params }, + {}, + }; + MODULE_DEVICE_TABLE(of, dwc2_of_match_table); +diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c +index 5776428..2061254 100644 +--- a/drivers/usb/dwc2/platform.c ++++ b/drivers/usb/dwc2/platform.c +@@ -432,6 +432,14 @@ static int dwc2_driver_probe(struct platform_device *dev) + if (retval) + return retval; + ++ hsotg->vbus_supply = devm_regulator_get_optional(hsotg->dev, "vbus"); ++ if (IS_ERR(hsotg->vbus_supply)) { ++ retval = PTR_ERR(hsotg->vbus_supply); ++ hsotg->vbus_supply = NULL; ++ if (retval != -ENODEV) ++ return retval; ++ } ++ + retval = dwc2_lowlevel_hw_enable(hsotg); + if (retval) + return retval; +@@ -466,6 +474,45 @@ static int dwc2_driver_probe(struct platform_device *dev) + if (retval) + goto error; + ++ if (hsotg->params.activate_stm_id_vb_detection) { ++ struct regulator *usb33d; ++ u32 ggpio; ++ ++ usb33d = devm_regulator_get(hsotg->dev, "usb33d"); ++ if (IS_ERR(usb33d)) { ++ retval = PTR_ERR(usb33d); ++ dev_err(hsotg->dev, ++ "can't get voltage level detector supply\n"); ++ goto error; ++ } ++ retval = regulator_enable(usb33d); ++ if (retval) { ++ dev_err(hsotg->dev, ++ "can't enable voltage level detector supply\n"); ++ goto error; ++ } ++ ++ ggpio = dwc2_readl(hsotg, GGPIO); ++ ggpio |= GGPIO_STM32_OTG_GCCFG_IDEN; ++ ggpio |= GGPIO_STM32_OTG_GCCFG_VBDEN; ++ dwc2_writel(hsotg, ggpio, GGPIO); ++ } ++ ++ if (hsotg->params.activate_stm_fs_transceiver) { ++ u32 ggpio; ++ ++ ggpio = dwc2_readl(hsotg, GGPIO); ++ if (!(ggpio & GGPIO_STM32_OTG_GCCFG_PWRDWN)) { ++ dev_dbg(hsotg->dev, "Activating transceiver\n"); ++ /* ++ * STM32 uses the GGPIO register as general ++ * core configuration register. ++ */ ++ ggpio |= GGPIO_STM32_OTG_GCCFG_PWRDWN; ++ dwc2_writel(hsotg, ggpio, GGPIO); ++ } ++ } ++ + if (hsotg->dr_mode != USB_DR_MODE_HOST) { + retval = dwc2_gadget_init(hsotg); + if (retval) +diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c +index 4c306fb..c429b52 100644 +--- a/drivers/usb/host/ehci-platform.c ++++ b/drivers/usb/host/ehci-platform.c +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -43,6 +44,7 @@ + struct ehci_platform_priv { + struct clk *clks[EHCI_MAX_CLKS]; + struct reset_control *rsts; ++ struct regulator *vbus_supply; + bool reset_on_resume; + }; + +@@ -73,6 +75,26 @@ static int ehci_platform_reset(struct usb_hcd *hcd) + return 0; + } + ++static int ehci_platform_port_power(struct usb_hcd *hcd, int portnum, ++ bool enable) ++{ ++ struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); ++ int ret; ++ ++ if (!priv->vbus_supply) ++ return 0; ++ ++ if (enable) ++ ret = regulator_enable(priv->vbus_supply); ++ else ++ ret = regulator_disable(priv->vbus_supply); ++ if (ret) ++ dev_err(hcd->self.controller, "failed to %s vbus supply: %d\n", ++ enable ? "enable" : "disable", ret); ++ ++ return ret; ++} ++ + static int ehci_platform_power_on(struct platform_device *dev) + { + struct usb_hcd *hcd = platform_get_drvdata(dev); +@@ -110,6 +132,7 @@ static struct hc_driver __read_mostly ehci_platform_hc_driver; + static const struct ehci_driver_overrides platform_overrides __initconst = { + .reset = ehci_platform_reset, + .extra_priv_size = sizeof(struct ehci_platform_priv), ++ .port_power = ehci_platform_port_power, + }; + + static struct usb_ehci_pdata ehci_platform_defaults = { +@@ -200,6 +223,15 @@ static int ehci_platform_probe(struct platform_device *dev) + if (err) + goto err_put_clks; + ++ priv->vbus_supply = devm_regulator_get_optional(&dev->dev, "vbus"); ++ if (IS_ERR(priv->vbus_supply)) { ++ err = PTR_ERR(priv->vbus_supply); ++ if (err == -ENODEV) ++ priv->vbus_supply = NULL; ++ else ++ goto err_reset; ++ } ++ + if (pdata->big_endian_desc) + ehci->big_endian_desc = 1; + if (pdata->big_endian_mmio) +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0024-ARM-stm32mp1-r0-rc2-THERMAL.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0024-ARM-stm32mp1-r0-rc2-THERMAL.patch new file mode 100644 index 0000000..76073e8 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0024-ARM-stm32mp1-r0-rc2-THERMAL.patch @@ -0,0 +1,914 @@ +From 7a16fa25c3bd9a0c078b4d9d2ae445b975afaf69 Mon Sep 17 00:00:00 2001 +From: Christophe Priouzeau +Date: Mon, 26 Nov 2018 14:42:41 +0100 +Subject: [PATCH 24/52] ARM-stm32mp1-r0-rc2-THERMAL + +--- + .../devicetree/bindings/thermal/stm32-thermal.txt | 61 ++ + drivers/thermal/Kconfig | 2 +- + drivers/thermal/Makefile | 2 +- + drivers/thermal/st/Kconfig | 14 + + drivers/thermal/st/Makefile | 1 + + drivers/thermal/st/stm_thermal.c | 760 +++++++++++++++++++++ + 6 files changed, 838 insertions(+), 2 deletions(-) + create mode 100644 Documentation/devicetree/bindings/thermal/stm32-thermal.txt + create mode 100644 drivers/thermal/st/stm_thermal.c + +diff --git a/Documentation/devicetree/bindings/thermal/stm32-thermal.txt b/Documentation/devicetree/bindings/thermal/stm32-thermal.txt +new file mode 100644 +index 0000000..8c0d5a4 +--- /dev/null ++++ b/Documentation/devicetree/bindings/thermal/stm32-thermal.txt +@@ -0,0 +1,61 @@ ++Binding for Thermal Sensor for STMicroelectronics STM32 series of SoCs. ++ ++On STM32 SoCs, the Digital Temperature Sensor (DTS) is in charge of managing an ++analog block which delivers a frequency depending on the internal SoC's ++temperature. By using a reference frequency, DTS is able to provide a sample ++number which can be translated into a temperature by the user. ++ ++DTS provides interrupt notification mechanism by threshold. This mechanism ++offers two temperature trip points: passive and critical. The first is intended ++for passive cooling notification while the second is used for over-temperature ++reset. ++ ++Required parameters: ++------------------- ++ ++compatible: Should be "st,stm32-thermal" ++reg: This should be the physical base address and length of the ++ sensor's registers. ++clocks: Phandle of the clock used by the thermal sensor. ++ See: Documentation/devicetree/bindings/clock/clock-bindings.txt ++clock-names: Should be "pclk" for register access clock and reference clock. ++ See: Documentation/devicetree/bindings/resource-names.txt ++#thermal-sensor-cells: Should be 0. See ./thermal.txt for a description. ++interrupts: Standard way to define interrupt number. ++ ++Example: ++ ++ thermal-zones { ++ cpu_thermal: cpu-thermal { ++ polling-delay-passive = <0>; ++ polling-delay = <0>; ++ ++ thermal-sensors = <&thermal>; ++ ++ trips { ++ cpu_alert1: cpu-alert1 { ++ temperature = <85000>; ++ hysteresis = <0>; ++ type = "passive"; ++ }; ++ ++ cpu-crit: cpu-crit { ++ temperature = <120000>; ++ hysteresis = <0>; ++ type = "critical"; ++ }; ++ }; ++ ++ cooling-maps { ++ }; ++ }; ++ }; ++ ++ thermal: thermal@50028000 { ++ compatible = "st,stm32-thermal"; ++ reg = <0x50028000 0x100>; ++ clocks = <&rcc TMPSENS>; ++ clock-names = "pclk"; ++ #thermal-sensor-cells = <0>; ++ interrupts = ; ++ }; +diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig +index 0e69edc..5422523 100644 +--- a/drivers/thermal/Kconfig ++++ b/drivers/thermal/Kconfig +@@ -432,7 +432,7 @@ source "drivers/thermal/samsung/Kconfig" + endmenu + + menu "STMicroelectronics thermal drivers" +-depends on ARCH_STI && OF ++depends on (ARCH_STI || ARCH_STM32) && OF + source "drivers/thermal/st/Kconfig" + endmenu + +diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile +index 610344e..82bb50d 100644 +--- a/drivers/thermal/Makefile ++++ b/drivers/thermal/Makefile +@@ -53,7 +53,7 @@ obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ + obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/ + obj-$(CONFIG_INTEL_BXT_PMIC_THERMAL) += intel_bxt_pmic_thermal.o + obj-$(CONFIG_INTEL_PCH_THERMAL) += intel_pch_thermal.o +-obj-$(CONFIG_ST_THERMAL) += st/ ++obj-y += st/ + obj-$(CONFIG_QCOM_TSENS) += qcom/ + obj-y += tegra/ + obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o +diff --git a/drivers/thermal/st/Kconfig b/drivers/thermal/st/Kconfig +index 490fdbe..b80f9a9 100644 +--- a/drivers/thermal/st/Kconfig ++++ b/drivers/thermal/st/Kconfig +@@ -1,3 +1,7 @@ ++# ++# STMicroelectronics thermal drivers configuration ++# ++ + config ST_THERMAL + tristate "Thermal sensors on STMicroelectronics STi series of SoCs" + help +@@ -10,3 +14,13 @@ config ST_THERMAL_SYSCFG + config ST_THERMAL_MEMMAP + select ST_THERMAL + tristate "STi series memory mapped access based thermal sensors" ++ ++config STM32_THERMAL ++ tristate "Thermal framework support on STMicroelectronics STM32 series of SoCs" ++ depends on MACH_STM32MP157 ++ default y ++ help ++ Support for thermal framework on STMicroelectronics STM32 series of ++ SoCs. This thermal driver allows to access to general thermal framework ++ functionalities and to acces to SoC sensor functionalities. This ++ configuration is fully dependent of MACH_STM32MP157. +diff --git a/drivers/thermal/st/Makefile b/drivers/thermal/st/Makefile +index b388789..b2b9e9b 100644 +--- a/drivers/thermal/st/Makefile ++++ b/drivers/thermal/st/Makefile +@@ -1,3 +1,4 @@ + obj-$(CONFIG_ST_THERMAL) := st_thermal.o + obj-$(CONFIG_ST_THERMAL_SYSCFG) += st_thermal_syscfg.o + obj-$(CONFIG_ST_THERMAL_MEMMAP) += st_thermal_memmap.o ++obj-$(CONFIG_STM32_THERMAL) := stm_thermal.o +\ No newline at end of file +diff --git a/drivers/thermal/st/stm_thermal.c b/drivers/thermal/st/stm_thermal.c +new file mode 100644 +index 0000000..bbd73c5 +--- /dev/null ++++ b/drivers/thermal/st/stm_thermal.c +@@ -0,0 +1,760 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved ++ * Author: David Hernandez Sanchez for ++ * STMicroelectronics. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "../thermal_core.h" ++#include "../thermal_hwmon.h" ++ ++/* DTS register offsets */ ++#define DTS_CFGR1_OFFSET 0x0 ++#define DTS_T0VALR1_OFFSET 0x8 ++#define DTS_RAMPVALR_OFFSET 0X10 ++#define DTS_ITR1_OFFSET 0x14 ++#define DTS_DR_OFFSET 0x1C ++#define DTS_SR_OFFSET 0x20 ++#define DTS_ITENR_OFFSET 0x24 ++#define DTS_CIFR_OFFSET 0x28 ++ ++/* DTS_CFGR1 register mask definitions */ ++#define HSREF_CLK_DIV_MASK GENMASK(30, 24) ++#define TS1_SMP_TIME_MASK GENMASK(19, 16) ++#define TS1_INTRIG_SEL_MASK GENMASK(11, 8) ++ ++/* DTS_T0VALR1 register mask definitions */ ++#define TS1_T0_MASK GENMASK(17, 16) ++#define TS1_FMT0_MASK GENMASK(15, 0) ++ ++/* DTS_RAMPVALR register mask definitions */ ++#define TS1_RAMP_COEFF_MASK GENMASK(15, 0) ++ ++/* DTS_ITR1 register mask definitions */ ++#define TS1_HITTHD_MASK GENMASK(31, 16) ++#define TS1_LITTHD_MASK GENMASK(15, 0) ++ ++/* DTS_DR register mask definitions */ ++#define TS1_MFREQ_MASK GENMASK(15, 0) ++ ++/* Less significant bit position definitions */ ++#define TS1_T0_POS 16 ++#define TS1_SMP_TIME_POS 16 ++#define TS1_HITTHD_POS 16 ++#define HSREF_CLK_DIV_POS 24 ++ ++/* DTS_CFGR1 bit definitions */ ++#define TS1_EN BIT(0) ++#define TS1_START BIT(4) ++#define REFCLK_SEL BIT(20) ++#define REFCLK_LSE REFCLK_SEL ++#define Q_MEAS_OPT BIT(21) ++#define CALIBRATION_CONTROL Q_MEAS_OPT ++ ++/* DTS_SR bit definitions */ ++#define TS_RDY BIT(15) ++/* Bit definitions below are common for DTS_SR, DTS_ITENR and DTS_CIFR */ ++#define HIGH_THRESHOLD BIT(2) ++#define LOW_THRESHOLD BIT(1) ++ ++/* Constants */ ++#define ADJUST 100 ++#define ONE_MHZ 1000000 ++#define POLL_TIMEOUT 5000 ++#define STARTUP_TIME 40 ++#define TS1_T0_VAL0 30 ++#define TS1_T0_VAL1 130 ++#define NO_HW_TRIG 0 ++ ++/* The Thermal Framework expects millidegrees */ ++#define mcelsius(temp) ((temp) * 1000) ++ ++/* The Sensor expects oC degrees */ ++#define celsius(temp) ((temp) / 1000) ++ ++struct stm_thermal_sensor { ++ struct device *dev; ++ struct thermal_zone_device *th_dev; ++ enum thermal_device_mode mode; ++ struct clk *clk; ++ int high_temp; ++ int low_temp; ++ int temp_critical; ++ int temp_passive; ++ unsigned int low_temp_enabled; ++ int num_trips; ++ int irq; ++ unsigned int irq_enabled; ++ void __iomem *base; ++ int t0, fmt0, ramp_coeff; ++}; ++ ++static irqreturn_t stm_thermal_alarm_irq(int irq, void *sdata) ++{ ++ struct stm_thermal_sensor *sensor = sdata; ++ ++ disable_irq_nosync(irq); ++ sensor->irq_enabled = false; ++ ++ return IRQ_WAKE_THREAD; ++} ++ ++static irqreturn_t stm_thermal_alarm_irq_thread(int irq, void *sdata) ++{ ++ u32 value; ++ struct stm_thermal_sensor *sensor = sdata; ++ ++ /* read IT reason in SR and clear flags */ ++ value = readl_relaxed(sensor->base + DTS_SR_OFFSET); ++ ++ if ((value & LOW_THRESHOLD) == LOW_THRESHOLD) ++ writel_relaxed(LOW_THRESHOLD, sensor->base + DTS_CIFR_OFFSET); ++ ++ if ((value & HIGH_THRESHOLD) == HIGH_THRESHOLD) ++ writel_relaxed(HIGH_THRESHOLD, sensor->base + DTS_CIFR_OFFSET); ++ ++ thermal_zone_device_update(sensor->th_dev, THERMAL_EVENT_UNSPECIFIED); ++ ++ return IRQ_HANDLED; ++} ++ ++static int stm_sensor_power_on(struct stm_thermal_sensor *sensor) ++{ ++ int ret; ++ u32 value; ++ ++ /* Enable sensor */ ++ value = readl_relaxed(sensor->base + DTS_CFGR1_OFFSET); ++ value |= TS1_EN; ++ writel_relaxed(value, sensor->base + DTS_CFGR1_OFFSET); ++ ++ /* ++ * The DTS block can be enabled by setting TSx_EN bit in ++ * DTS_CFGRx register. It requires a startup time of ++ * 40μs. Use 5 ms as arbitrary timeout. ++ */ ++ ret = readl_poll_timeout(sensor->base + DTS_SR_OFFSET, ++ value, (value & TS_RDY), ++ STARTUP_TIME, POLL_TIMEOUT); ++ if (ret) ++ return ret; ++ ++ /* Start continuous measuring */ ++ value = readl_relaxed(sensor->base + ++ DTS_CFGR1_OFFSET); ++ value |= TS1_START; ++ writel_relaxed(value, sensor->base + ++ DTS_CFGR1_OFFSET); ++ ++ return 0; ++} ++ ++static int stm_sensor_power_off(struct stm_thermal_sensor *sensor) ++{ ++ u32 value; ++ ++ /* Stop measuring */ ++ value = readl_relaxed(sensor->base + DTS_CFGR1_OFFSET); ++ value &= ~TS1_START; ++ writel_relaxed(value, sensor->base + DTS_CFGR1_OFFSET); ++ ++ /* Ensure stop is taken into account */ ++ usleep_range(STARTUP_TIME, POLL_TIMEOUT); ++ ++ /* Disable sensor */ ++ value = readl_relaxed(sensor->base + DTS_CFGR1_OFFSET); ++ value &= ~TS1_EN; ++ writel_relaxed(value, sensor->base + DTS_CFGR1_OFFSET); ++ ++ /* Ensure disable is taken into account */ ++ return readl_poll_timeout(sensor->base + DTS_SR_OFFSET, value, ++ !(value & TS_RDY), ++ STARTUP_TIME, POLL_TIMEOUT); ++} ++ ++static int stm_thermal_calibration(struct stm_thermal_sensor *sensor) ++{ ++ u32 value, clk_freq; ++ u32 prescaler; ++ ++ /* Figure out prescaler value for PCLK during calibration */ ++ clk_freq = clk_get_rate(sensor->clk); ++ if (!clk_freq) ++ return -EINVAL; ++ ++ prescaler = 0; ++ clk_freq /= ONE_MHZ; ++ if (clk_freq) { ++ while (prescaler <= clk_freq) ++ prescaler++; ++ } ++ ++ value = readl_relaxed(sensor->base + DTS_CFGR1_OFFSET); ++ ++ /* Clear prescaler */ ++ value &= ~HSREF_CLK_DIV_MASK; ++ ++ /* Set prescaler. pclk_freq/prescaler < 1MHz */ ++ value |= (prescaler << HSREF_CLK_DIV_POS); ++ ++ /* Select PCLK as reference clock */ ++ value &= ~REFCLK_SEL; ++ ++ /* Set maximal sampling time for better precision */ ++ value |= TS1_SMP_TIME_MASK; ++ ++ /* Measure with calibration */ ++ value &= ~CALIBRATION_CONTROL; ++ ++ /* select trigger */ ++ value &= ~TS1_INTRIG_SEL_MASK; ++ value |= NO_HW_TRIG; ++ ++ writel_relaxed(value, sensor->base + DTS_CFGR1_OFFSET); ++ ++ return 0; ++} ++ ++/* Fill in DTS structure with factory sensor values */ ++static int stm_thermal_read_factory_settings(struct stm_thermal_sensor *sensor) ++{ ++ /* Retrieve engineering calibration temperature */ ++ sensor->t0 = readl_relaxed(sensor->base + DTS_T0VALR1_OFFSET) & ++ TS1_T0_MASK; ++ if (!sensor->t0) ++ sensor->t0 = TS1_T0_VAL0; ++ else ++ sensor->t0 = TS1_T0_VAL1; ++ ++ /* Retrieve fmt0 and put it on Hz */ ++ sensor->fmt0 = ADJUST * (readl_relaxed(sensor->base + ++ DTS_T0VALR1_OFFSET) & TS1_FMT0_MASK); ++ ++ /* Retrieve ramp coefficient */ ++ sensor->ramp_coeff = readl_relaxed(sensor->base + DTS_RAMPVALR_OFFSET) & ++ TS1_RAMP_COEFF_MASK; ++ ++ if (!sensor->fmt0 || !sensor->ramp_coeff) { ++ dev_err(sensor->dev, "%s: wrong setting\n", __func__); ++ return -EINVAL; ++ } ++ ++ dev_dbg(sensor->dev, "%s: T0 = %doC, FMT0 = %dHz, RAMP_COEFF = %dHz/oC", ++ __func__, sensor->t0, sensor->fmt0, sensor->ramp_coeff); ++ ++ return 0; ++} ++ ++static int stm_thermal_calculate_threshold(struct stm_thermal_sensor *sensor, ++ int temp, u32 *th) ++{ ++ int freqM; ++ u32 sampling_time; ++ ++ /* Retrieve the number of periods to sample */ ++ sampling_time = (readl_relaxed(sensor->base + DTS_CFGR1_OFFSET) & ++ TS1_SMP_TIME_MASK) >> TS1_SMP_TIME_POS; ++ ++ /* Figure out the CLK_PTAT frequency for a given temperature */ ++ freqM = ((temp - sensor->t0) * sensor->ramp_coeff) ++ + sensor->fmt0; ++ ++ dev_dbg(sensor->dev, "%s: freqM for threshold = %d Hz", ++ __func__, freqM); ++ ++ /* Figure out the threshold sample number */ ++ *th = clk_get_rate(sensor->clk); ++ if (!*th) ++ return -EINVAL; ++ ++ *th = *th / freqM; ++ ++ *th *= sampling_time; ++ ++ return 0; ++} ++ ++static int stm_thermal_set_threshold(struct stm_thermal_sensor *sensor) ++{ ++ u32 value, th; ++ int ret; ++ ++ value = readl_relaxed(sensor->base + DTS_ITR1_OFFSET); ++ ++ /* Erase threshold content */ ++ value &= ~(TS1_LITTHD_MASK | TS1_HITTHD_MASK); ++ ++ /* Retrieve the sample threshold number th for a given temperature */ ++ ret = stm_thermal_calculate_threshold(sensor, sensor->high_temp, &th); ++ if (ret) ++ return ret; ++ ++ value |= th & TS1_LITTHD_MASK; ++ ++ if (sensor->low_temp_enabled) { ++ /* Retrieve the sample threshold */ ++ ret = stm_thermal_calculate_threshold(sensor, sensor->low_temp, ++ &th); ++ if (ret) ++ return ret; ++ ++ value |= (TS1_HITTHD_MASK & (th << TS1_HITTHD_POS)); ++ } ++ ++ /* Write value on the Low interrupt threshold */ ++ writel_relaxed(value, sensor->base + DTS_ITR1_OFFSET); ++ ++ return 0; ++} ++ ++/* Disable temperature interrupt */ ++static int stm_disable_irq(struct stm_thermal_sensor *sensor) ++{ ++ u32 value; ++ ++ /* Disable IT generation for low and high thresholds */ ++ value = readl_relaxed(sensor->base + DTS_ITENR_OFFSET); ++ writel_relaxed(value & ~(LOW_THRESHOLD | HIGH_THRESHOLD), ++ sensor->base + DTS_ITENR_OFFSET); ++ ++ dev_dbg(sensor->dev, "%s: IT disabled on sensor side", __func__); ++ ++ return 0; ++} ++ ++/* Enable temperature interrupt */ ++static int stm_enable_irq(struct stm_thermal_sensor *sensor) ++{ ++ u32 value; ++ ++ /* ++ * Code below enables High temperature threshold using a low threshold ++ * sampling value ++ */ ++ ++ /* Make sure LOW_THRESHOLD IT is clear before enabling */ ++ writel_relaxed(LOW_THRESHOLD, sensor->base + DTS_CIFR_OFFSET); ++ ++ /* Enable IT generation for low threshold */ ++ value = readl_relaxed(sensor->base + DTS_ITENR_OFFSET); ++ value |= LOW_THRESHOLD; ++ ++ /* Enable the low temperature threshold if needed */ ++ if (sensor->low_temp_enabled) { ++ /* Make sure HIGH_THRESHOLD IT is clear before enabling */ ++ writel_relaxed(HIGH_THRESHOLD, sensor->base + DTS_CIFR_OFFSET); ++ ++ /* Enable IT generation for high threshold */ ++ value |= HIGH_THRESHOLD; ++ } ++ ++ /* Enable thresholds */ ++ writel_relaxed(value, sensor->base + DTS_ITENR_OFFSET); ++ ++ dev_dbg(sensor->dev, "%s: IT enabled on sensor side", __func__); ++ ++ return 0; ++} ++ ++static int stm_thermal_update_threshold(struct stm_thermal_sensor *sensor) ++{ ++ int ret; ++ ++ sensor->mode = THERMAL_DEVICE_DISABLED; ++ ++ ret = stm_sensor_power_off(sensor); ++ if (ret) ++ return ret; ++ ++ ret = stm_disable_irq(sensor); ++ if (ret) ++ return ret; ++ ++ ret = stm_thermal_set_threshold(sensor); ++ if (ret) ++ return ret; ++ ++ ret = stm_enable_irq(sensor); ++ if (ret) ++ return ret; ++ ++ ret = stm_sensor_power_on(sensor); ++ if (ret) ++ return ret; ++ ++ sensor->mode = THERMAL_DEVICE_ENABLED; ++ ++ return 0; ++} ++ ++/* Callback to get temperature from HW */ ++static int stm_thermal_get_temp(void *data, int *temp) ++{ ++ struct stm_thermal_sensor *sensor = data; ++ u32 sampling_time; ++ int freqM, ret; ++ ++ if (sensor->mode != THERMAL_DEVICE_ENABLED) ++ return -EAGAIN; ++ ++ /* Retrieve the number of samples */ ++ ret = readl_poll_timeout(sensor->base + DTS_DR_OFFSET, freqM, ++ (freqM & TS1_MFREQ_MASK), STARTUP_TIME, ++ POLL_TIMEOUT); ++ ++ if (ret) ++ return ret; ++ ++ if (!freqM) ++ return -ENODATA; ++ ++ /* Retrieve the number of periods sampled */ ++ sampling_time = (readl_relaxed(sensor->base + DTS_CFGR1_OFFSET) & ++ TS1_SMP_TIME_MASK) >> TS1_SMP_TIME_POS; ++ ++ /* Figure out the number of samples per period */ ++ freqM /= sampling_time; ++ ++ /* Figure out the CLK_PTAT frequency */ ++ freqM = clk_get_rate(sensor->clk) / freqM; ++ if (!freqM) ++ return -EINVAL; ++ ++ dev_dbg(sensor->dev, "%s: freqM=%d\n", __func__, freqM); ++ ++ /* Figure out the temperature in mili celsius */ ++ *temp = mcelsius(sensor->t0 + ((freqM - sensor->fmt0) / ++ sensor->ramp_coeff)); ++ ++ dev_dbg(sensor->dev, "%s: temperature = %d millicelsius", ++ __func__, *temp); ++ ++ /* Update thresholds */ ++ if (sensor->num_trips > 1) { ++ /* Update alarm threshold value to next higher trip point */ ++ if (sensor->high_temp == sensor->temp_passive && ++ celsius(*temp) >= sensor->temp_passive) { ++ sensor->high_temp = sensor->temp_critical; ++ sensor->low_temp = sensor->temp_passive; ++ sensor->low_temp_enabled = true; ++ ret = stm_thermal_update_threshold(sensor); ++ if (ret) ++ return ret; ++ } ++ ++ if (sensor->high_temp == sensor->temp_critical && ++ celsius(*temp) < sensor->temp_passive) { ++ sensor->high_temp = sensor->temp_passive; ++ sensor->low_temp_enabled = false; ++ ret = stm_thermal_update_threshold(sensor); ++ if (ret) ++ return ret; ++ } ++ ++ /* ++ * Re-enable alarm IRQ if temperature below critical ++ * temperature ++ */ ++ if (!sensor->irq_enabled && ++ (celsius(*temp) < sensor->temp_critical)) { ++ sensor->irq_enabled = true; ++ enable_irq(sensor->irq); ++ } ++ } ++ ++ return 0; ++} ++ ++/* Registers DTS irq to be visible by GIC */ ++static int stm_register_irq(struct stm_thermal_sensor *sensor) ++{ ++ struct device *dev = sensor->dev; ++ struct platform_device *pdev = to_platform_device(dev); ++ int ret; ++ ++ sensor->irq = platform_get_irq(pdev, 0); ++ if (sensor->irq < 0) { ++ dev_err(dev, "%s: Unable to find IRQ\n", __func__); ++ return sensor->irq; ++ } ++ ++ ret = devm_request_threaded_irq(dev, sensor->irq, ++ stm_thermal_alarm_irq, ++ stm_thermal_alarm_irq_thread, ++ IRQF_ONESHOT, ++ dev->driver->name, sensor); ++ if (ret) { ++ dev_err(dev, "%s: Failed to register IRQ %d\n", __func__, ++ sensor->irq); ++ return ret; ++ } ++ ++ sensor->irq_enabled = true; ++ ++ dev_dbg(dev, "%s: thermal IRQ registered", __func__); ++ ++ return 0; ++} ++ ++static int stm_thermal_sensor_off(struct stm_thermal_sensor *sensor) ++{ ++ int ret; ++ ++ ret = stm_sensor_power_off(sensor); ++ if (ret) ++ return ret; ++ ++ clk_disable_unprepare(sensor->clk); ++ ++ return 0; ++} ++ ++static int stm_thermal_prepare(struct stm_thermal_sensor *sensor) ++{ ++ int ret; ++ struct device *dev = sensor->dev; ++ ++ ret = clk_prepare_enable(sensor->clk); ++ if (ret) ++ return ret; ++ ++ ret = stm_thermal_read_factory_settings(sensor); ++ if (ret) ++ goto thermal_unprepare; ++ ++ ret = stm_thermal_calibration(sensor); ++ if (ret) ++ goto thermal_unprepare; ++ ++ /* Set threshold(s) for IRQ */ ++ ret = stm_thermal_set_threshold(sensor); ++ if (ret) ++ goto thermal_unprepare; ++ ++ ret = stm_enable_irq(sensor); ++ if (ret) ++ goto thermal_unprepare; ++ ++ ret = stm_sensor_power_on(sensor); ++ if (ret) { ++ dev_err(dev, "%s: failed to power on sensor\n", __func__); ++ goto irq_disable; ++ } ++ ++ return 0; ++ ++irq_disable: ++ stm_disable_irq(sensor); ++ ++thermal_unprepare: ++ clk_disable_unprepare(sensor->clk); ++ ++ return ret; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int stm_thermal_suspend(struct device *dev) ++{ ++ int ret; ++ struct platform_device *pdev = to_platform_device(dev); ++ struct stm_thermal_sensor *sensor = platform_get_drvdata(pdev); ++ ++ ret = stm_thermal_sensor_off(sensor); ++ if (ret) ++ return ret; ++ ++ sensor->mode = THERMAL_DEVICE_DISABLED; ++ ++ return 0; ++} ++ ++static int stm_thermal_resume(struct device *dev) ++{ ++ int ret; ++ struct platform_device *pdev = to_platform_device(dev); ++ struct stm_thermal_sensor *sensor = platform_get_drvdata(pdev); ++ ++ ret = stm_thermal_prepare(sensor); ++ if (ret) ++ return ret; ++ ++ sensor->mode = THERMAL_DEVICE_ENABLED; ++ ++ return 0; ++} ++#endif /* CONFIG_PM_SLEEP */ ++ ++SIMPLE_DEV_PM_OPS(stm_thermal_pm_ops, stm_thermal_suspend, stm_thermal_resume); ++ ++static const struct thermal_zone_of_device_ops stm_tz_ops = { ++ .get_temp = stm_thermal_get_temp, ++}; ++ ++static const struct of_device_id stm_thermal_of_match[] = { ++ { .compatible = "st,stm32-thermal"}, ++ { /* sentinel */ } ++}; ++MODULE_DEVICE_TABLE(of, stm_thermal_of_match); ++ ++static int stm_thermal_probe(struct platform_device *pdev) ++{ ++ struct stm_thermal_sensor *sensor; ++ struct resource *res; ++ const struct thermal_trip *trip; ++ void __iomem *base; ++ int ret, i; ++ ++ if (!pdev->dev.of_node) { ++ dev_err(&pdev->dev, "%s: device tree node not found\n", ++ __func__); ++ return -EINVAL; ++ } ++ ++ sensor = devm_kzalloc(&pdev->dev, sizeof(*sensor), GFP_KERNEL); ++ if (!sensor) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, sensor); ++ ++ sensor->dev = &pdev->dev; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ /* Populate sensor */ ++ sensor->base = base; ++ ++ sensor->clk = devm_clk_get(&pdev->dev, "pclk"); ++ if (IS_ERR(sensor->clk)) { ++ dev_err(&pdev->dev, "%s: failed to fetch PCLK clock\n", ++ __func__); ++ return PTR_ERR(sensor->clk); ++ } ++ ++ /* Register IRQ into GIC */ ++ ret = stm_register_irq(sensor); ++ if (ret) ++ return ret; ++ ++ sensor->th_dev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, ++ sensor, ++ &stm_tz_ops); ++ ++ if (IS_ERR(sensor->th_dev)) { ++ dev_err(&pdev->dev, "%s: thermal zone sensor registering KO\n", ++ __func__); ++ ret = PTR_ERR(sensor->th_dev); ++ return ret; ++ } ++ ++ if (!sensor->th_dev->ops->get_crit_temp) { ++ /* Critical point must be provided */ ++ ret = -EINVAL; ++ goto err_tz; ++ } ++ ++ ret = sensor->th_dev->ops->get_crit_temp(sensor->th_dev, ++ &sensor->temp_critical); ++ if (ret) { ++ dev_err(&pdev->dev, ++ "Not able to read critical_temp: %d\n", ret); ++ goto err_tz; ++ } ++ ++ sensor->temp_critical = celsius(sensor->temp_critical); ++ ++ /* Set thresholds for IRQ */ ++ sensor->high_temp = sensor->temp_critical; ++ ++ trip = of_thermal_get_trip_points(sensor->th_dev); ++ sensor->num_trips = of_thermal_get_ntrips(sensor->th_dev); ++ ++ /* Find out passive temperature if it exists */ ++ for (i = (sensor->num_trips - 1); i >= 0; i--) { ++ if (trip[i].type == THERMAL_TRIP_PASSIVE) { ++ sensor->temp_passive = celsius(trip[i].temperature); ++ /* Update high temperature threshold */ ++ sensor->high_temp = sensor->temp_passive; ++ } ++ } ++ ++ /* ++ * Ensure low_temp_enabled flag is disabled. ++ * By disabling low_temp_enabled, low threshold IT will not be ++ * configured neither enabled because it is not needed as high ++ * threshold is set on the lowest temperature trip point after ++ * probe. ++ */ ++ sensor->low_temp_enabled = false; ++ ++ /* Configure and enable HW sensor */ ++ ret = stm_thermal_prepare(sensor); ++ if (ret) { ++ dev_err(&pdev->dev, ++ "Not able to enable sensor: %d\n", ret); ++ goto err_tz; ++ } ++ ++ /* ++ * Thermal_zone doesn't enable hwmon as default, ++ * enable it here ++ */ ++ sensor->th_dev->tzp->no_hwmon = false; ++ ret = thermal_add_hwmon_sysfs(sensor->th_dev); ++ if (ret) ++ goto err_tz; ++ ++ sensor->mode = THERMAL_DEVICE_ENABLED; ++ ++ dev_info(&pdev->dev, "%s: Driver initialized successfully\n", ++ __func__); ++ ++ return 0; ++ ++err_tz: ++ thermal_zone_of_sensor_unregister(&pdev->dev, sensor->th_dev); ++ return ret; ++} ++ ++static int stm_thermal_remove(struct platform_device *pdev) ++{ ++ struct stm_thermal_sensor *sensor = platform_get_drvdata(pdev); ++ ++ stm_thermal_sensor_off(sensor); ++ thermal_remove_hwmon_sysfs(sensor->th_dev); ++ thermal_zone_of_sensor_unregister(&pdev->dev, sensor->th_dev); ++ ++ return 0; ++} ++ ++static struct platform_driver stm_thermal_driver = { ++ .driver = { ++ .name = "stm_thermal", ++ .pm = &stm_thermal_pm_ops, ++ .of_match_table = stm_thermal_of_match, ++ }, ++ .probe = stm_thermal_probe, ++ .remove = stm_thermal_remove, ++}; ++module_platform_driver(stm_thermal_driver); ++ ++MODULE_DESCRIPTION("STMicroelectronics STM32 Thermal Sensor Driver"); ++MODULE_AUTHOR("David Hernandez Sanchez "); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:stm_thermal"); +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0025-ARM-stm32mp1-r0-rc2-REMOTEPROC.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0025-ARM-stm32mp1-r0-rc2-REMOTEPROC.patch new file mode 100644 index 0000000..56ed52d --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0025-ARM-stm32mp1-r0-rc2-REMOTEPROC.patch @@ -0,0 +1,372 @@ +From 222782ff647dfeb33bb442031792a893372fad82 Mon Sep 17 00:00:00 2001 +From: Christophe Priouzeau +Date: Mon, 26 Nov 2018 14:43:58 +0100 +Subject: [PATCH 25/52] ARM-stm32mp1-r0-rc2-REMOTEPROC + +--- + .../devicetree/bindings/remoteproc/rproc-srm.txt | 14 ++- + .../devicetree/bindings/remoteproc/stm32-rproc.txt | 15 ++- + drivers/nvmem/stm32-romem.c | 44 +++++---- + drivers/remoteproc/rproc_srm_core.h | 14 +-- + drivers/remoteproc/rproc_srm_dev.c | 103 +-------------------- + drivers/remoteproc/stm32_rproc.c | 2 +- + 6 files changed, 42 insertions(+), 150 deletions(-) + +diff --git a/Documentation/devicetree/bindings/remoteproc/rproc-srm.txt b/Documentation/devicetree/bindings/remoteproc/rproc-srm.txt +index dce10c0..19a5255 100644 +--- a/Documentation/devicetree/bindings/remoteproc/rproc-srm.txt ++++ b/Documentation/devicetree/bindings/remoteproc/rproc-srm.txt +@@ -23,12 +23,16 @@ Optional properties: + - clocks: clocks required by the coprocessor + - clock-names: see clock-bindings.txt + - pinctrl-x: pins configurations required by the coprocessor +-- pinctrl-names: see pinctrl-bindings.txt. +- "rproc_default" is a special pin configuration which is applied except +- if the 'early-booted' property is set. +- In a general way, it is recommended to use names prefixed with "rproc_". ++ The SRM reserves the pins for the coprocessor, which prevents the local ++ processor to use them. ++- pinctrl-names: all names must be prefixed with "rproc_" (ex: "rproc_default"). ++ This rule must be strictly followed in order to prevent the SRM to ++ (over)write a pin configuration which is done by the coprocessor. + - x-supply: power supplies required by the coprocessor +-- interrupts: see interrupts.txt ++- interrupts: external interrupts configurations required by the coprocessor. ++ This is optional since the configuration is done by the coprocessor. ++ When defined, the SRM (over)writes the configuration which allows the ++ interrupt controller to check for configuration conflicts. + - interrupt-parent: see interrupts.txt + - interrupt-names: see interrupts.txt + +diff --git a/Documentation/devicetree/bindings/remoteproc/stm32-rproc.txt b/Documentation/devicetree/bindings/remoteproc/stm32-rproc.txt +index ee00f1c..7df6a26 100644 +--- a/Documentation/devicetree/bindings/remoteproc/stm32-rproc.txt ++++ b/Documentation/devicetree/bindings/remoteproc/stm32-rproc.txt +@@ -33,18 +33,15 @@ Optional properties: + - from local to remote = send message + - from remote to local = send message ack + - a channel (b) working the opposite direction of channel (a) +- - a channel (c) used for two different purposes: +- - used by the remote proc to signal when it has completed +- its critical initalisation. +- Mono-directional channel: from remote to local +- - used by the local proc to notify the remote proc that it +- is about to be shut down. +- Mono-directional channel: from local to remote, where ACK +- from the remote means that it is ready for shutdown ++ - a channel (c) used by the local proc to notify the remote proc ++ that it is about to be shut down. ++ Mono-directional channel: ++ - from local to remote, where ACK from the remote means ++ that it is ready for shutdown + - mbox-names: This property is required if the mboxes property is used. + - must be "vq0" for channel (a) + - must be "vq1" for channel (b) +- - must be "init_shdn" for channel (c) ++ - must be "shutdown" for channel (c) + - memory-region: phandle to the reserved memory node to be associated with the + remoteproc device. + - st,syscfg-pdds: Reference to the system configuration controlling the remote +diff --git a/drivers/nvmem/stm32-romem.c b/drivers/nvmem/stm32-romem.c +index 198872f..34b388c 100644 +--- a/drivers/nvmem/stm32-romem.c ++++ b/drivers/nvmem/stm32-romem.c +@@ -19,6 +19,12 @@ + #define STM32_SMC_WRITE_SHADOW 0x03 + #define STM32_SMC_READ_OTP 0x04 + ++/* shadow registers offest */ ++#define STM32MP15_BSEC_DATA0 0x200 ++ ++/* 32 (x 32-bits) lower shadow registers */ ++#define STM32MP15_BSEC_NUM_LOWER 32 ++ + struct stm32_romem_cfg { + int size; + }; +@@ -77,13 +83,21 @@ static int stm32_bsec_read(void *context, unsigned int offset, void *buf, + return -EINVAL; + + for (i = roffset; (i < roffset + rbytes); i += 4) { +- ret = stm32_bsec_smc(STM32_SMC_READ_OTP, i >> 2, 0, &val); +- if (ret) { +- dev_err(priv->dev, "Failed to read data%d (%d)\n", +- i >> 2, ret); +- return ret; ++ u32 otp = i >> 2; ++ ++ if (otp < STM32MP15_BSEC_NUM_LOWER) { ++ /* read lower data from shadow registers */ ++ val = readl_relaxed( ++ priv->base + STM32MP15_BSEC_DATA0 + i); ++ } else { ++ ret = stm32_bsec_smc(STM32_SMC_READ_SHADOW, otp, 0, ++ &val); ++ if (ret) { ++ dev_err(priv->dev, "Can't read data%d (%d)\n", ++ otp, ret); ++ return ret; ++ } + } +- + /* skip first bytes in case of unaligned read */ + if (skip_bytes) + size = min(bytes, (size_t)(4 - skip_bytes)); +@@ -127,7 +141,6 @@ static int stm32_romem_probe(struct platform_device *pdev) + const struct stm32_romem_cfg *cfg; + struct device *dev = &pdev->dev; + struct stm32_romem_priv *priv; +- struct nvmem_device *nvmem; + struct resource *res; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); +@@ -154,26 +167,12 @@ static int stm32_romem_probe(struct platform_device *pdev) + priv->cfg.size = resource_size(res); + priv->cfg.reg_read = stm32_romem_read; + } else { +- priv->cfg.read_only = false; + priv->cfg.size = cfg->size; + priv->cfg.reg_read = stm32_bsec_read; + priv->cfg.reg_write = stm32_bsec_write; + } + +- nvmem = nvmem_register(&priv->cfg); +- if (IS_ERR(nvmem)) +- return PTR_ERR(nvmem); +- +- platform_set_drvdata(pdev, nvmem); +- +- return 0; +-} +- +-static int stm32_romem_remove(struct platform_device *pdev) +-{ +- struct nvmem_device *nvmem = platform_get_drvdata(pdev); +- +- return nvmem_unregister(nvmem); ++ return PTR_ERR_OR_ZERO(devm_nvmem_register(dev, &priv->cfg)); + } + + static const struct stm32_romem_cfg stm32mp15_bsec_cfg = { +@@ -191,7 +190,6 @@ MODULE_DEVICE_TABLE(of, stm32_romem_of_match); + + static struct platform_driver stm32_romem_driver = { + .probe = stm32_romem_probe, +- .remove = stm32_romem_remove, + .driver = { + .name = "stm32-romem", + .of_match_table = of_match_ptr(stm32_romem_of_match), +diff --git a/drivers/remoteproc/rproc_srm_core.h b/drivers/remoteproc/rproc_srm_core.h +index 7915f35..7dffdb38 100644 +--- a/drivers/remoteproc/rproc_srm_core.h ++++ b/drivers/remoteproc/rproc_srm_core.h +@@ -20,12 +20,10 @@ + /** + * Resource type used in resource manager rpmsg: + * RPROC_SRM_RSC_CLOCK: clock resource +- * RPROC_SRM_RSC_PIN: pin resource + * RPROC_SRM_RSC_REGU: regulator resource + */ + #define RPROC_SRM_RSC_CLOCK 0x00 +-#define RPROC_SRM_RSC_PIN 0x01 +-#define RPROC_SRM_RSC_REGU 0x02 ++#define RPROC_SRM_RSC_REGU 0x01 + + /** + * struct clock_cfg - clock configuration used in resource manager rpmsg +@@ -63,14 +61,6 @@ struct regu_cfg { + }; + + /** +- * struct pin_cfg - pin configuration used in resource manager rpmsg +- * @name: current pin configuration name (meaningful in GetConfig message) +- */ +-struct pin_cfg { +- u8 name[16]; +-}; +- +-/** + * struct rpmsg_srm_msg - message structure used between processors to + * dynamically update resources configuration + * @message_type: type of the message: see RPROC_SRM_MSG* +@@ -81,7 +71,6 @@ struct pin_cfg { + * see RPROC_SRM_RSC* + * @clock_cfg: clock config - relevant if &rsc_type is RPROC_SRM_RSC_CLOCK + * @regu_cfg: regulator config - relevant if &rsc_type is RPROC_SRM_RSC_REGU +- * @pin_cfg: pin config - relevant if &rsc_type is RPROC_SRM_RSC_PIN + */ + struct rpmsg_srm_msg { + u32 message_type; +@@ -90,7 +79,6 @@ struct rpmsg_srm_msg { + union { + struct clock_cfg clock_cfg; + struct regu_cfg regu_cfg; +- struct pin_cfg pin_cfg; + }; + }; + +diff --git a/drivers/remoteproc/rproc_srm_dev.c b/drivers/remoteproc/rproc_srm_dev.c +index b026f961..7dc99c5 100644 +--- a/drivers/remoteproc/rproc_srm_dev.c ++++ b/drivers/remoteproc/rproc_srm_dev.c +@@ -31,7 +31,6 @@ struct rproc_srm_pin_info { + struct list_head list; + unsigned int index; + char *name; +- bool selected; + }; + + struct rproc_srm_regu_info { +@@ -544,83 +543,6 @@ static int rproc_srm_dev_regus_get(struct rproc_srm_dev *rproc_srm_dev) + } + + /* Pins */ +-static int rproc_srm_dev_pin_set_cfg(struct rproc_srm_dev *rproc_srm_dev, +- struct pin_cfg *cfg) +-{ +- struct rproc_srm_pin_info *pi, *p = NULL; +- struct device *dev = rproc_srm_dev->dev; +- struct pinctrl_state *state; +- int ret; +- +- list_for_each_entry(pi, &rproc_srm_dev->pin_list_head, list) { +- if (!strcmp(pi->name, cfg->name)) { +- p = pi; +- break; +- } +- } +- +- if (!p) { +- dev_err(dev, "unknown pin config (%s)\n", cfg->name); +- return -EINVAL; +- } +- +- state = pinctrl_lookup_state(rproc_srm_dev->pctrl, cfg->name); +- if (IS_ERR(state)) { +- dev_err(dev, "cannot get pin config (%s)\n", cfg->name); +- return -EINVAL; +- } +- +- ret = pinctrl_select_state(rproc_srm_dev->pctrl, state); +- if (ret < 0) { +- dev_err(dev, "cannot set pin config (%s)\n", cfg->name); +- return ret; +- } +- +- list_for_each_entry(pi, &rproc_srm_dev->pin_list_head, list) { +- pi->selected = (pi == p); +- } +- +- dev_dbg(dev, "pin config (%s) selected\n", p->name); +- +- return 0; +-} +- +-static int rproc_srm_dev_pin_get_cfg(struct rproc_srm_dev *rproc_srm_dev, +- struct pin_cfg *cfg) +-{ +- struct rproc_srm_pin_info *p; +- +- list_for_each_entry(p, &rproc_srm_dev->pin_list_head, list) { +- if (p->selected) { +- strlcpy(cfg->name, p->name, sizeof(cfg->name)); +- return 0; +- } +- } +- +- dev_warn(rproc_srm_dev->dev, "cannot find selected pin state\n"); +- strcpy(cfg->name, ""); +- +- return 0; +-} +- +-static int rproc_srm_dev_pins_setup(struct rproc_srm_dev *rproc_srm_dev) +-{ +- struct rproc_srm_pin_info *p; +- struct pin_cfg cfg = { .name = "rproc_default" }; +- +- if (rproc_srm_dev->early_boot) +- /* in early_boot mode do not update pin config */ +- return 0; +- +- /* set the "rproc_default" pin config if defined */ +- list_for_each_entry(p, &rproc_srm_dev->pin_list_head, list) { +- if (!strcmp(p->name, cfg.name)) +- return rproc_srm_dev_pin_set_cfg(rproc_srm_dev, &cfg); +- } +- +- return 0; +-} +- + static void rproc_srm_dev_pins_put(struct rproc_srm_dev *rproc_srm_dev) + { + struct device *dev = rproc_srm_dev->dev; +@@ -677,11 +599,9 @@ static int rproc_srm_dev_pins_get(struct rproc_srm_dev *rproc_srm_dev) + } + p->name = devm_kstrdup(dev, name, GFP_KERNEL); + +- if (!strcmp(p->name, PINCTRL_STATE_DEFAULT)) { +- if (rproc_srm_dev->early_boot) +- dev_warn(dev, "pin config potentially overwritten!\n"); +- p->selected = true; +- } ++ /* pinctrl-names shall not be "default" (but "rproc_default") */ ++ if (!strcmp(p->name, PINCTRL_STATE_DEFAULT)) ++ dev_warn(dev, "pin config potentially overwritten!\n"); + + p->index = i; + +@@ -726,13 +646,6 @@ static int rproc_srm_dev_notify_cb(struct notifier_block *nb, unsigned long evt, + ret = rproc_srm_dev_clock_get_cfg(rproc_srm_dev, + &o.clock_cfg); + break; +- case RPROC_SRM_RSC_PIN: +- ret = rproc_srm_dev_pin_set_cfg(rproc_srm_dev, +- &i->pin_cfg); +- if (!ret) +- ret = rproc_srm_dev_pin_get_cfg(rproc_srm_dev, +- &o.pin_cfg); +- break; + case RPROC_SRM_RSC_REGU: + ret = rproc_srm_dev_regu_set_cfg(rproc_srm_dev, + &i->regu_cfg); +@@ -752,10 +665,6 @@ static int rproc_srm_dev_notify_cb(struct notifier_block *nb, unsigned long evt, + ret = rproc_srm_dev_clock_get_cfg(rproc_srm_dev, + &o.clock_cfg); + break; +- case RPROC_SRM_RSC_PIN: +- ret = rproc_srm_dev_pin_get_cfg(rproc_srm_dev, +- &o.pin_cfg); +- break; + case RPROC_SRM_RSC_REGU: + ret = rproc_srm_dev_regu_get_cfg(rproc_srm_dev, + &o.regu_cfg); +@@ -810,11 +719,7 @@ rproc_srm_dev_bind(struct device *dev, struct device *master, void *data) + if (ret) + return ret; + +- ret = rproc_srm_dev_pins_setup(rproc_srm_dev); +- if (ret) +- return ret; +- +- /* For IRQs: nothing to setup */ ++ /* For pins and IRQs: nothing to setup */ + return 0; + } + +diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c +index 998de67..548afdd 100644 +--- a/drivers/remoteproc/stm32_rproc.c ++++ b/drivers/remoteproc/stm32_rproc.c +@@ -29,7 +29,7 @@ + + #define STM32_MBX_VQ0 "vq0" + #define STM32_MBX_VQ1 "vq1" +-#define STM32_MBX_SHUTDOWN "init_shdn" ++#define STM32_MBX_SHUTDOWN "shutdown" + + struct stm32_syscon { + struct regmap *map; +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0026-ARM-stm32mp1-r0-rc2-NET.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0026-ARM-stm32mp1-r0-rc2-NET.patch new file mode 100644 index 0000000..b3ae5d9 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0026-ARM-stm32mp1-r0-rc2-NET.patch @@ -0,0 +1,273 @@ +From f03da721a19075ead436b2edbe4c4080feb8dac8 Mon Sep 17 00:00:00 2001 +From: Christophe Priouzeau +Date: Mon, 26 Nov 2018 14:44:24 +0100 +Subject: [PATCH 26/52] ARM-stm32mp1-r0-rc2-NET + +--- + .../devicetree/bindings/net/stm32-dwmac.txt | 6 +- + drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c | 102 +++++++++++++++++---- + .../net/ethernet/stmicro/stmmac/stmmac_platform.c | 3 + + .../wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c | 6 ++ + 4 files changed, 94 insertions(+), 23 deletions(-) + +diff --git a/Documentation/devicetree/bindings/net/stm32-dwmac.txt b/Documentation/devicetree/bindings/net/stm32-dwmac.txt +index 1341012..f42dc68 100644 +--- a/Documentation/devicetree/bindings/net/stm32-dwmac.txt ++++ b/Documentation/devicetree/bindings/net/stm32-dwmac.txt +@@ -24,9 +24,9 @@ Required properties: + encompases the glue register, and the offset of the control register. + + Optional properties: +-- clock-names: For MPU family "mac-clk-ck" for PHY without quartz +-- st,int-phyclk (boolean) : valid only where PHY do not have quartz and need to be clock +- by RCC ++- clock-names: For MPU family "eth-ck" for PHY without quartz ++- st,eth_clk_sel (boolean) : set this property in RGMII PHY when you do not want use 125Mhz ++- st,eth_ref_clk_sel (boolean) : set this property in RMII mode when you have PHY without crystal 50MHz + + Example: + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c +index d1cf145..545b168 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c +@@ -25,9 +25,24 @@ + + #define SYSCFG_MCU_ETH_MASK BIT(23) + #define SYSCFG_MP1_ETH_MASK GENMASK(23, 16) ++#define SYSCFG_PMCCLRR_OFFSET 0x40 + + #define SYSCFG_PMCR_ETH_CLK_SEL BIT(16) + #define SYSCFG_PMCR_ETH_REF_CLK_SEL BIT(17) ++ ++/* Ethernet PHY interface selection in register SYSCFG Configuration ++ *------------------------------------------ ++ * src |BIT(23)| BIT(22)| BIT(21)|BIT(20)| ++ *------------------------------------------ ++ * MII | 0 | 0 | 0 | 1 | ++ *------------------------------------------ ++ * GMII | 0 | 0 | 0 | 0 | ++ *------------------------------------------ ++ * RGMII | 0 | 0 | 1 | n/a | ++ *------------------------------------------ ++ * RMII | 1 | 0 | 0 | n/a | ++ *------------------------------------------ ++ */ + #define SYSCFG_PMCR_ETH_SEL_MII BIT(20) + #define SYSCFG_PMCR_ETH_SEL_RGMII BIT(21) + #define SYSCFG_PMCR_ETH_SEL_RMII BIT(23) +@@ -35,15 +50,54 @@ + #define SYSCFG_MCU_ETH_SEL_MII 0 + #define SYSCFG_MCU_ETH_SEL_RMII 1 + ++/* STM32MP1 register definitions ++ * ++ * Below table summarizes the clock requirement and clock sources for ++ * supported phy interface modes. ++ * __________________________________________________________________________ ++ *|PHY_MODE | Normal | PHY wo crystal| PHY wo crystal |No 125Mhz from PHY| ++ *| | | 25MHz | 50MHz | | ++ * --------------------------------------------------------------------------- ++ *| MII | - | eth-ck | n/a | n/a | ++ *| | | | | | ++ * --------------------------------------------------------------------------- ++ *| GMII | - | eth-ck | n/a | n/a | ++ *| | | | | | ++ * --------------------------------------------------------------------------- ++ *| RGMII | - | eth-ck | n/a | eth-ck (no pin) | ++ *| | | | | st,eth_clk_sel | ++ * --------------------------------------------------------------------------- ++ *| RMII | - | eth-ck | eth-ck | n/a | ++ *| | | | st,eth_ref_clk_sel | | ++ * --------------------------------------------------------------------------- ++ * ++ * BIT(17) : set this bit in RMII mode when you have PHY without crystal 50MHz ++ * BIT(16) : set this bit in GMII/RGMII PHY when you do not want use 125Mhz ++ * from PHY ++ *----------------------------------------------------- ++ * src | BIT(17) | BIT(16) | ++ *----------------------------------------------------- ++ * MII | n/a | n/a | ++ *----------------------------------------------------- ++ * GMII | n/a | st,eth_clk_sel | ++ *----------------------------------------------------- ++ * RGMII | n/a | st,eth_clk_sel | ++ *----------------------------------------------------- ++ * RMII | st,eth_ref_clk_sel | n/a | ++ *----------------------------------------------------- ++ * ++ */ ++ + struct stm32_dwmac { + struct clk *clk_tx; + struct clk *clk_rx; + struct clk *clk_eth_ck; + struct clk *clk_ethstp; + struct clk *syscfg_clk; +- bool int_phyclk; /* Clock from RCC to drive PHY */ ++ int eth_clk_sel_reg; ++ int eth_ref_clk_sel_reg; + int irq_pwr_wakeup; +- u32 mode_reg; /* MAC glue-logic mode register */ ++ u32 mode_reg; /* MAC glue-logic mode register */ + struct regmap *regmap; + u32 speed; + const struct stm32_ops *ops; +@@ -103,7 +157,7 @@ static int stm32mp1_clk_prepare(struct stm32_dwmac *dwmac, bool prepare) + if (ret) + return ret; + +- if (dwmac->int_phyclk) { ++ if (dwmac->clk_eth_ck) { + ret = clk_prepare_enable(dwmac->clk_eth_ck); + if (ret) { + clk_disable_unprepare(dwmac->syscfg_clk); +@@ -112,7 +166,7 @@ static int stm32mp1_clk_prepare(struct stm32_dwmac *dwmac, bool prepare) + } + } else { + clk_disable_unprepare(dwmac->syscfg_clk); +- if (dwmac->int_phyclk) ++ if (dwmac->clk_eth_ck) + clk_disable_unprepare(dwmac->clk_eth_ck); + } + return ret; +@@ -122,7 +176,7 @@ static int stm32mp1_set_mode(struct plat_stmmacenet_data *plat_dat) + { + struct stm32_dwmac *dwmac = plat_dat->bsp_priv; + u32 reg = dwmac->mode_reg; +- int val; ++ int val, ret; + + switch (plat_dat->interface) { + case PHY_INTERFACE_MODE_MII: +@@ -131,19 +185,19 @@ static int stm32mp1_set_mode(struct plat_stmmacenet_data *plat_dat) + break; + case PHY_INTERFACE_MODE_GMII: + val = SYSCFG_PMCR_ETH_SEL_GMII; +- if (dwmac->int_phyclk) ++ if (dwmac->eth_clk_sel_reg) + val |= SYSCFG_PMCR_ETH_CLK_SEL; + pr_debug("SYSCFG init : PHY_INTERFACE_MODE_GMII\n"); + break; + case PHY_INTERFACE_MODE_RMII: + val = SYSCFG_PMCR_ETH_SEL_RMII; +- if (dwmac->int_phyclk) ++ if (dwmac->eth_ref_clk_sel_reg) + val |= SYSCFG_PMCR_ETH_REF_CLK_SEL; + pr_debug("SYSCFG init : PHY_INTERFACE_MODE_RMII\n"); + break; + case PHY_INTERFACE_MODE_RGMII: + val = SYSCFG_PMCR_ETH_SEL_RGMII; +- if (dwmac->int_phyclk) ++ if (dwmac->eth_clk_sel_reg) + val |= SYSCFG_PMCR_ETH_CLK_SEL; + pr_debug("SYSCFG init : PHY_INTERFACE_MODE_RGMII\n"); + break; +@@ -154,6 +208,11 @@ static int stm32mp1_set_mode(struct plat_stmmacenet_data *plat_dat) + return -EINVAL; + } + ++ /* Need to update PMCCLRR (clear register) */ ++ ret = regmap_update_bits(dwmac->regmap, reg + SYSCFG_PMCCLRR_OFFSET, ++ dwmac->ops->syscfg_eth_mask, ~val); ++ ++ /* Update PMCSETR (set register) */ + return regmap_update_bits(dwmac->regmap, reg, + dwmac->ops->syscfg_eth_mask, val); + } +@@ -237,22 +296,25 @@ static int stm32mp1_parse_data(struct stm32_dwmac *dwmac, + struct device_node *np = dev->of_node; + int err = 0; + +- dwmac->int_phyclk = of_property_read_bool(np, "st,int-phyclk"); ++ /* Gigabit Ethernet 125MHz clock selection. */ ++ dwmac->eth_clk_sel_reg = of_property_read_bool(np, "st,eth_clk_sel"); + +- /* Check if internal clk from RCC selected */ +- if (dwmac->int_phyclk) { +- /* Get ETH_CLK clocks */ +- dwmac->clk_eth_ck = devm_clk_get(dev, "eth-ck"); +- if (IS_ERR(dwmac->clk_eth_ck)) { +- dev_err(dev, "No ETH CK clock provided...\n"); +- return PTR_ERR(dwmac->clk_eth_ck); +- } ++ /* Ethernet 50Mhz RMII clock selection */ ++ dwmac->eth_ref_clk_sel_reg = ++ of_property_read_bool(np, "st,eth_ref_clk_sel"); ++ ++ /* Get ETH_CLK clocks */ ++ dwmac->clk_eth_ck = devm_clk_get(dev, "eth-ck"); ++ if (IS_ERR(dwmac->clk_eth_ck)) { ++ dev_warn(dev, "No phy clock provided...\n"); ++ dwmac->clk_eth_ck = NULL; + } + + /* Clock used for low power mode */ + dwmac->clk_ethstp = devm_clk_get(dev, "ethstp"); + if (IS_ERR(dwmac->clk_ethstp)) { +- dev_err(dev, "No ETH peripheral clock provided for CStop mode ...\n"); ++ dev_err(dev, ++ "No ETH peripheral clock provided for CStop mode ...\n"); + return PTR_ERR(dwmac->clk_ethstp); + } + +@@ -268,7 +330,7 @@ static int stm32mp1_parse_data(struct stm32_dwmac *dwmac, + */ + dwmac->irq_pwr_wakeup = platform_get_irq_byname(pdev, + "stm32_pwr_wakeup"); +- if (!dwmac->int_phyclk && dwmac->irq_pwr_wakeup >= 0) { ++ if ((!dwmac->clk_eth_ck) && dwmac->irq_pwr_wakeup >= 0) { + err = device_init_wakeup(&pdev->dev, true); + if (err) { + dev_err(&pdev->dev, "Failed to init wake up irq\n"); +@@ -370,7 +432,7 @@ static int stm32mp1_suspend(struct stm32_dwmac *dwmac) + + clk_disable_unprepare(dwmac->clk_tx); + clk_disable_unprepare(dwmac->syscfg_clk); +- if (dwmac->int_phyclk) ++ if (dwmac->clk_eth_ck) + clk_disable_unprepare(dwmac->clk_eth_ck); + + return ret; +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +index 2b800ce..3031f2b 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +@@ -408,6 +408,9 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) + /* Default to phy auto-detection */ + plat->phy_addr = -1; + ++ /* Get clk_csr from device tree */ ++ of_property_read_u32(np, "clk_csr", &plat->clk_csr); ++ + /* "snps,phy-addr" is not a standard property. Mark it as deprecated + * and warn of its use. Remove this when phy node support is added. + */ +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +index d2f788d..c7b41ce 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +@@ -1129,7 +1129,10 @@ static int brcmf_ops_sdio_suspend(struct device *dev) + enable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr); + else + sdio_flags |= MMC_PM_WAKE_SDIO_IRQ; ++ } else { ++ brcmf_sdiod_intr_unregister(sdiodev); + } ++ + if (sdio_set_host_pm_flags(sdiodev->func1, sdio_flags)) + brcmf_err("Failed to set pm_flags %x\n", sdio_flags); + return 0; +@@ -1145,6 +1148,9 @@ static int brcmf_ops_sdio_resume(struct device *dev) + if (func->num != 2) + return 0; + ++ if (!sdiodev->wowl_enabled) ++ brcmf_sdiod_intr_register(sdiodev); ++ + brcmf_sdiod_freezer_off(sdiodev); + return 0; + } +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0027-ARM-stm32mp1-r0-rc2-HWCLK-SPI.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0027-ARM-stm32mp1-r0-rc2-HWCLK-SPI.patch new file mode 100644 index 0000000..975810c --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0027-ARM-stm32mp1-r0-rc2-HWCLK-SPI.patch @@ -0,0 +1,1082 @@ +From 064e22b81e0a4a24f48af50df7b6505a80fdcb42 Mon Sep 17 00:00:00 2001 +From: Christophe Priouzeau +Date: Mon, 26 Nov 2018 14:45:11 +0100 +Subject: [PATCH 27/52] ARM-stm32mp1-r0-rc2-HWCLK-SPI + +--- + .../bindings/hwlock/st,stm32-hwspinlock.txt | 23 ++ + arch/arm/include/debug/stm32.S | 48 +++ + drivers/clk/clk-stm32mp1.c | 100 +++--- + drivers/spi/spi-stm32.c | 380 +++++++++++++-------- + 4 files changed, 352 insertions(+), 199 deletions(-) + create mode 100644 Documentation/devicetree/bindings/hwlock/st,stm32-hwspinlock.txt + create mode 100644 arch/arm/include/debug/stm32.S + +diff --git a/Documentation/devicetree/bindings/hwlock/st,stm32-hwspinlock.txt b/Documentation/devicetree/bindings/hwlock/st,stm32-hwspinlock.txt +new file mode 100644 +index 0000000..adf4f000 +--- /dev/null ++++ b/Documentation/devicetree/bindings/hwlock/st,stm32-hwspinlock.txt +@@ -0,0 +1,23 @@ ++STM32 Hardware Spinlock Device Binding ++------------------------------------- ++ ++Required properties : ++- compatible : should be "st,stm32-hwspinlock". ++- reg : the register address of hwspinlock. ++- #hwlock-cells : hwlock users only use the hwlock id to represent a specific ++ hwlock, so the number of cells should be <1> here. ++- clock-names : Must contain "hsem". ++- clocks : Must contain a phandle entry for the clock in clock-names, see the ++ common clock bindings. ++ ++Please look at the generic hwlock binding for usage information for consumers, ++"Documentation/devicetree/bindings/hwlock/hwlock.txt" ++ ++Example of hwlock provider: ++ hwspinlock@4c000000 { ++ compatible = "st,stm32-hwspinlock"; ++ #hwlock-cells = <1>; ++ reg = <0x4c000000 0x400>; ++ clocks = <&rcc HSEM>; ++ clock-names = "hsem"; ++ }; +diff --git a/arch/arm/include/debug/stm32.S b/arch/arm/include/debug/stm32.S +new file mode 100644 +index 0000000..427561e +--- /dev/null ++++ b/arch/arm/include/debug/stm32.S +@@ -0,0 +1,48 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* ++ * Copyright (C) STMicroelectronics SA 2017 - All Rights Reserved ++ * Author: Gerald Baeza for STMicroelectronics. ++ */ ++ ++#ifdef CONFIG_ARM_SINGLE_ARMV7M ++#define STM32_UART_BASE_PHYS 0x40011000 /* USART1 */ ++#define STM32_UART_BASE_VIRT STM32_UART_BASE_PHYS /* no MMU */ ++#else ++#define STM32_UART_BASE_PHYS 0x40010000 /* UART4 */ ++#define STM32_UART_BASE_VIRT 0xfe010000 /* UART4 */ ++#endif ++ ++ ++#ifdef CONFIG_STM32F4_DEBUG_UART ++#define STM32_USART_SR_OFF 0x00 ++#define STM32_USART_TDR_OFF 0x04 ++#endif ++ ++#ifdef CONFIG_STM32F7_DEBUG_UART ++#define STM32_USART_SR_OFF 0x1C ++#define STM32_USART_TDR_OFF 0x28 ++#endif ++ ++#define STM32_USART_TC (1 << 6) /* Tx complete */ ++#define STM32_USART_TXE (1 << 7) /* Tx data reg empty */ ++ ++.macro addruart, rp, rv, tmp ++ ldr \rp, =STM32_UART_BASE_PHYS @ physical base ++ ldr \rv, =STM32_UART_BASE_VIRT @ virt base ++.endm ++ ++.macro senduart,rd,rx ++ strb \rd, [\rx, #STM32_USART_TDR_OFF] ++.endm ++ ++.macro waituart,rd,rx ++1001: ldr \rd, [\rx, #(STM32_USART_SR_OFF)] @ Read Status Register ++ tst \rd, #STM32_USART_TXE @ TXE = 1 = tx empty ++ beq 1001b ++.endm ++ ++.macro busyuart,rd,rx ++1001: ldr \rd, [\rx, #(STM32_USART_SR_OFF)] @ Read Status Register ++ tst \rd, #STM32_USART_TC @ TC = 1 = tx complete ++ beq 1001b ++.endm +diff --git a/drivers/clk/clk-stm32mp1.c b/drivers/clk/clk-stm32mp1.c +index 50d739a..7eccaa1 100644 +--- a/drivers/clk/clk-stm32mp1.c ++++ b/drivers/clk/clk-stm32mp1.c +@@ -52,6 +52,7 @@ static DEFINE_SPINLOCK(rlock); + #define RCC_AHB5ENSETR 0x210 + #define RCC_AHB6ENSETR 0x218 + #define RCC_AHB6LPENSETR 0x318 ++#define RCC_MLAHBENSETR 0xA38 + #define RCC_RCK12SELR 0x28 + #define RCC_RCK3SELR 0x820 + #define RCC_RCK4SELR 0x824 +@@ -2672,8 +2673,6 @@ CLK_OF_DECLARE_DRIVER(stm32mp1_rcc, "st,stm32mp1-rcc", stm32mp1_rcc_init); + * + */ + +-static struct regmap *pwr_syscon; +- + struct reg { + u32 address; + u32 val; +@@ -2697,24 +2696,31 @@ struct sreg { + u32 address; + u32 secured; + u32 val; ++ u8 setclr; + }; + ++#define SREG(_addr, _setclr, _sec) { \ ++ .address = _addr,\ ++ .setclr = _setclr,\ ++ .secured = _sec,\ ++ .val = 0,\ ++} ++ + static struct sreg clock_gating[] = { +- { 0xA00, 0 }, /* APB1 */ +- { 0xA08, 0 }, /* APB2 */ +- { 0xA10, 0 }, /* APB3 */ +- { 0x200, 0 }, /* APB4 */ +- { 0x208, 1 }, /* APB5 */ +- { 0x210, 1 }, /* AHB5 */ +- { 0x218, 0 }, /* AHB6 */ +- { 0xA18, 0 }, /* AHB2 */ +- { 0xA20, 0 }, /* AHB3 */ +- { 0xA28, 0 }, /* AHB4 */ +- { 0xA38, 0 }, /* MLAHB */ +- { 0x800, 0 }, /* MCO1 */ +- { 0x804, 0 }, /* MCO2 */ +- { 0x894, 0 }, /* PLL4 */ +- { 0x89C, 0 }, /* PLL4CFGR2 */ ++ SREG(RCC_APB1ENSETR, 1, 0), ++ SREG(RCC_APB2ENSETR, 1, 0), ++ SREG(RCC_APB3ENSETR, 1, 0), ++ SREG(RCC_APB4ENSETR, 1, 0), ++ SREG(RCC_APB5ENSETR, 1, 1), ++ SREG(RCC_AHB5ENSETR, 1, 1), ++ SREG(RCC_AHB6ENSETR, 1, 0), ++ SREG(RCC_AHB2ENSETR, 1, 0), ++ SREG(RCC_AHB3ENSETR, 1, 0), ++ SREG(RCC_AHB4ENSETR, 1, 0), ++ SREG(RCC_MLAHBENSETR, 1, 0), ++ SREG(RCC_MCO1CFGR, 0, 0), ++ SREG(RCC_MCO2CFGR, 0, 0), ++ SREG(RCC_PLL4CFGR2, 0, 0), + }; + + struct smux { +@@ -2767,12 +2773,13 @@ struct smux _mux_kernel[] = { + }; + + static struct sreg pll_clock[] = { +- { 0x880, 0 }, /* PLL3 */ +- { 0x894, 0 }, /* PLL4 */ ++ SREG(RCC_PLL3CR, 0, 0), ++ SREG(RCC_PLL4CR, 0, 0), + }; + + static struct sreg mcu_source[] = { +- { 0x048, 0 }, /* MSSCKSELR */ ++ SREG(RCC_MCUDIVR, 0, 0), ++ SREG(RCC_MSSCKSELR, 0, 0), + }; + + #define RCC_IRQ_FLAGS_MASK 0x110F1F +@@ -2784,9 +2791,6 @@ static struct sreg mcu_source[] = { + #define SBF (BIT(11)) + #define SBF_MPU (BIT(12)) + +- +- +- + static irqreturn_t stm32mp1_rcc_irq_handler(int irq, void *sdata) + { + pr_info("RCC generic interrupt received\n"); +@@ -2808,7 +2812,7 @@ static void stm32mp1_backup_sreg(struct sreg *sreg, int size) + static void stm32mp1_restore_sreg(struct sreg *sreg, int size) + { + int i; +- u32 val, address; ++ u32 val, address, reg; + int soc_secured; + + soc_secured = _is_soc_secured(rcc_base); +@@ -2817,11 +2821,21 @@ static void stm32mp1_restore_sreg(struct sreg *sreg, int size) + val = sreg[i].val; + address = sreg[i].address; + +- if (soc_secured && sreg[i].secured) +- SMC(STM32_SVC_RCC, STM32_WRITE, +- address, val); +- else ++ reg = readl_relaxed(rcc_base + address); ++ if (reg == val) ++ continue; ++ ++ if (soc_secured && sreg[i].secured) { ++ SMC(STM32_SVC_RCC, STM32_WRITE, address, val); ++ if (sreg[i].setclr) ++ SMC(STM32_SVC_RCC, STM32_WRITE, ++ address + RCC_CLR, ~val); ++ } else { + writel_relaxed(val, rcc_base + address); ++ if (sreg[i].setclr) ++ writel_relaxed(~val, ++ rcc_base + address + RCC_CLR); ++ } + } + } + +@@ -2893,31 +2907,23 @@ static int stm32mp1_clk_suspend(void) + reg = readl_relaxed(rcc_base + RCC_OCENSETR) & RCC_CK_OSC_MASK; + writel_relaxed(reg << 1, rcc_base + RCC_OCENSETR); + ++ SMC(STM32_SVC_RCC, STM32_WRITE, RCC_RSTSR, 0); ++ + return 0; + } + + static void stm32mp1_clk_resume(void) + { +- u32 power_flags_rcc, power_flags_pwr; + +- /* Read power flags and decide what to resume */ +- regmap_read(pwr_syscon, PWR_MPUCR, &power_flags_pwr); +- power_flags_rcc = readl_relaxed(rcc_base + RCC_RSTSR); ++ /* Restore pll */ ++ stm32mp1_restore_pll(pll_clock, ARRAY_SIZE(pll_clock)); + +- if ((power_flags_pwr & STOP_FLAG) == STOP_FLAG) { +- /* Restore pll */ +- stm32mp1_restore_pll(pll_clock, ARRAY_SIZE(pll_clock)); ++ /* Restore mcu source */ ++ stm32mp1_restore_sreg(mcu_source, ARRAY_SIZE(mcu_source)); + +- /* Restore mcu source */ +- stm32mp1_restore_sreg(mcu_source, ARRAY_SIZE(mcu_source)); +- } else if (((power_flags_rcc & SBF) == SBF) || +- ((power_flags_rcc & SBF_MPU) == SBF_MPU)) { +- stm32mp1_restore_sreg(clock_gating, ARRAY_SIZE(clock_gating)); ++ stm32mp1_restore_sreg(clock_gating, ARRAY_SIZE(clock_gating)); + +- stm32mp1_restore_mux(_mux_kernel, ARRAY_SIZE(_mux_kernel)); +- } +- +- SMC(STM32_SVC_RCC, STM32_WRITE, RCC_RSTSR, 0); ++ stm32mp1_restore_mux(_mux_kernel, ARRAY_SIZE(_mux_kernel)); + + /* Disable ck_xxx_ker clocks */ + stm32_clk_bit_secure(STM32_SET_BITS, RCC_CK_XXX_KER_MASK, +@@ -2941,12 +2947,6 @@ static int stm32_rcc_init_pwr(struct device_node *np) + int ret; + int i; + +- pwr_syscon = syscon_regmap_lookup_by_phandle(np, "st,pwr"); +- if (IS_ERR(pwr_syscon)) { +- pr_err("%s: pwr syscon required !\n", __func__); +- return PTR_ERR(pwr_syscon); +- } +- + /* register generic irq */ + irq = of_irq_get(np, 0); + if (irq < 0) { +diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c +index ad1e55d..789e335 100644 +--- a/drivers/spi/spi-stm32.c ++++ b/drivers/spi/spi-stm32.c +@@ -18,6 +18,7 @@ + * You should have received a copy of the GNU General Public License along with + * spi_stm32 driver. If not, see . + */ ++#include + #include + #include + #include +@@ -26,6 +27,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -54,27 +56,23 @@ + #define SPI_CR1_SSI BIT(12) + + /* STM32_SPI_CR2 bit fields */ +-#define SPI_CR2_TSIZE_SHIFT 0 + #define SPI_CR2_TSIZE GENMASK(15, 0) ++#define SPI_CR2_TSER GENMASK(31, 16) ++#define SPI_TSIZE_MAX FIELD_GET(SPI_CR2_TSIZE, SPI_CR2_TSIZE) ++#define SPI_TSER_MAX FIELD_GET(SPI_CR2_TSER, SPI_CR2_TSER) + + /* STM32_SPI_CFG1 bit fields */ +-#define SPI_CFG1_DSIZE_SHIFT 0 + #define SPI_CFG1_DSIZE GENMASK(4, 0) +-#define SPI_CFG1_FTHLV_SHIFT 5 + #define SPI_CFG1_FTHLV GENMASK(8, 5) + #define SPI_CFG1_RXDMAEN BIT(14) + #define SPI_CFG1_TXDMAEN BIT(15) +-#define SPI_CFG1_MBR_SHIFT 28 + #define SPI_CFG1_MBR GENMASK(30, 28) + #define SPI_CFG1_MBR_MIN 0 +-#define SPI_CFG1_MBR_MAX (GENMASK(30, 28) >> 28) ++#define SPI_CFG1_MBR_MAX FIELD_GET(SPI_CFG1_MBR, SPI_CFG1_MBR) + + /* STM32_SPI_CFG2 bit fields */ +-#define SPI_CFG2_MIDI_SHIFT 4 + #define SPI_CFG2_MIDI GENMASK(7, 4) +-#define SPI_CFG2_COMM_SHIFT 17 + #define SPI_CFG2_COMM GENMASK(18, 17) +-#define SPI_CFG2_SP_SHIFT 19 + #define SPI_CFG2_SP GENMASK(21, 19) + #define SPI_CFG2_MASTER BIT(22) + #define SPI_CFG2_LSBFRST BIT(23) +@@ -90,17 +88,17 @@ + #define SPI_IER_EOTIE BIT(3) + #define SPI_IER_TXTFIE BIT(4) + #define SPI_IER_OVRIE BIT(6) +-#define SPI_IER_MODFIE BIT(9) ++#define SPI_IER_TSERFIE BIT(10) + #define SPI_IER_ALL GENMASK(10, 0) + + /* STM32_SPI_SR bit fields */ + #define SPI_SR_RXP BIT(0) + #define SPI_SR_TXP BIT(1) + #define SPI_SR_EOT BIT(3) ++#define SPI_SR_TXTF BIT(4) + #define SPI_SR_OVR BIT(6) +-#define SPI_SR_MODF BIT(9) ++#define SPI_SR_TSERF BIT(10) + #define SPI_SR_SUSP BIT(11) +-#define SPI_SR_RXPLVL_SHIFT 13 + #define SPI_SR_RXPLVL GENMASK(14, 13) + #define SPI_SR_RXWNE BIT(15) + +@@ -120,8 +118,6 @@ + #define SPI_SIMPLEX_RX 2 + #define SPI_HALF_DUPLEX 3 + +-#define SPI_1HZ_NS 1000000000 +- + /** + * struct stm32_spi - private data of the SPI controller + * @dev: driver model representation of the controller +@@ -139,6 +135,7 @@ + * @cur_fthlv: fifo threshold level (data frames in a single data packet) + * @cur_comm: SPI communication mode + * @cur_xferlen: current transfer length in bytes ++ * @cur_reload: current transfer remaining bytes to be loaded + * @cur_usedma: boolean to know if dma is used in current transfer + * @tx_buf: data to be written, or NULL + * @rx_buf: data to be read, or NULL +@@ -165,6 +162,7 @@ struct stm32_spi { + unsigned int cur_fthlv; + unsigned int cur_comm; + unsigned int cur_xferlen; ++ unsigned int cur_reload; + bool cur_usedma; + + const void *tx_buf; +@@ -173,7 +171,10 @@ struct stm32_spi { + int rx_len; + struct dma_chan *dma_tx; + struct dma_chan *dma_rx; ++ struct completion dma_completion; + dma_addr_t phys_addr; ++ struct completion xfer_completion; ++ int xfer_status; + }; + + static inline void stm32_spi_set_bits(struct stm32_spi *spi, +@@ -233,8 +234,7 @@ static int stm32_spi_get_bpw_mask(struct stm32_spi *spi) + stm32_spi_set_bits(spi, STM32_SPI_CFG1, SPI_CFG1_DSIZE); + + cfg1 = readl_relaxed(spi->base + STM32_SPI_CFG1); +- max_bpw = (cfg1 & SPI_CFG1_DSIZE) >> SPI_CFG1_DSIZE_SHIFT; +- max_bpw += 1; ++ max_bpw = FIELD_GET(SPI_CFG1_DSIZE, cfg1) + 1; + + spin_unlock_irqrestore(&spi->lock, flags); + +@@ -282,19 +282,22 @@ static int stm32_spi_prepare_mbr(struct stm32_spi *spi, u32 speed_hz) + * stm32_spi_prepare_fthlv - Determine FIFO threshold level + * @spi: pointer to the spi controller data structure + */ +-static u32 stm32_spi_prepare_fthlv(struct stm32_spi *spi) ++static u32 stm32_spi_prepare_fthlv(struct stm32_spi *spi, u32 xfer_len) + { +- u32 fthlv, half_fifo; ++ u32 fthlv, half_fifo, packet; + + /* data packet should not exceed 1/2 of fifo space */ + half_fifo = (spi->fifo_size / 2); + ++ /* data_packet should not exceed transfer length */ ++ packet = (half_fifo > xfer_len) ? xfer_len : half_fifo; ++ + if (spi->cur_bpw <= 8) +- fthlv = half_fifo; ++ fthlv = packet; + else if (spi->cur_bpw <= 16) +- fthlv = half_fifo / 2; ++ fthlv = packet / 2; + else +- fthlv = half_fifo / 4; ++ fthlv = packet / 4; + + /* align packet size with data registers access */ + if (spi->cur_bpw > 8) +@@ -302,9 +305,28 @@ static u32 stm32_spi_prepare_fthlv(struct stm32_spi *spi) + else + fthlv -= (fthlv % 4); /* multiple of 4 */ + ++ if (!fthlv) ++ fthlv = 1; ++ + return fthlv; + } + ++static void stm32_spi_transfer_extension(struct stm32_spi *spi) ++{ ++ if (spi->cur_reload > 0) { ++ u32 cr2 = readl_relaxed(spi->base + STM32_SPI_CR2); ++ u32 tsize = FIELD_GET(SPI_CR2_TSIZE, cr2); ++ u32 tser = SPI_TSER_MAX - (SPI_TSER_MAX % spi->cur_fthlv); ++ ++ tser = min(spi->cur_reload, tser); ++ ++ writel_relaxed(FIELD_PREP(SPI_CR2_TSER, tser) | ++ FIELD_PREP(SPI_CR2_TSIZE, tsize), ++ spi->base + STM32_SPI_CR2); ++ spi->cur_reload -= tser; ++ } ++} ++ + /** + * stm32_spi_write_txfifo - Write bytes in Transmit Data Register + * @spi: pointer to the spi controller data structure +@@ -346,24 +368,24 @@ static void stm32_spi_write_txfifo(struct stm32_spi *spi) + * Write in rx_buf depends on remaining bytes to avoid to write beyond + * rx_buf end. + */ +-static void stm32_spi_read_rxfifo(struct stm32_spi *spi, bool flush) ++static void stm32_spi_read_rxfifo(struct stm32_spi *spi) + { + u32 sr = readl_relaxed(spi->base + STM32_SPI_SR); +- u32 rxplvl = (sr & SPI_SR_RXPLVL) >> SPI_SR_RXPLVL_SHIFT; ++ u32 rxplvl = FIELD_GET(SPI_SR_RXPLVL, sr); + + while ((spi->rx_len > 0) && + ((sr & SPI_SR_RXP) || +- (flush && ((sr & SPI_SR_RXWNE) || (rxplvl > 0))))) { ++ ((sr & SPI_SR_EOT) && ((sr & SPI_SR_RXWNE) || (rxplvl > 0))))) { + u32 offs = spi->cur_xferlen - spi->rx_len; + +- if ((spi->rx_len >= sizeof(u32)) || +- (flush && (sr & SPI_SR_RXWNE))) { ++ if ((spi->rx_len >= sizeof(u32)) || (sr & SPI_SR_RXWNE)) { + u32 *rx_buf32 = (u32 *)(spi->rx_buf + offs); + + *rx_buf32 = readl_relaxed(spi->base + STM32_SPI_RXDR); + spi->rx_len -= sizeof(u32); + } else if ((spi->rx_len >= sizeof(u16)) || +- (flush && (rxplvl >= 2 || spi->cur_bpw > 8))) { ++ (!(sr & SPI_SR_RXWNE) && ++ (rxplvl >= 2 || spi->cur_bpw > 8))) { + u16 *rx_buf16 = (u16 *)(spi->rx_buf + offs); + + *rx_buf16 = readw_relaxed(spi->base + STM32_SPI_RXDR); +@@ -376,11 +398,11 @@ static void stm32_spi_read_rxfifo(struct stm32_spi *spi, bool flush) + } + + sr = readl_relaxed(spi->base + STM32_SPI_SR); +- rxplvl = (sr & SPI_SR_RXPLVL) >> SPI_SR_RXPLVL_SHIFT; ++ rxplvl = FIELD_GET(SPI_SR_RXPLVL, sr); + } + +- dev_dbg(spi->dev, "%s%s: %d bytes left\n", __func__, +- flush ? "(flush)" : "", spi->rx_len); ++ dev_dbg(spi->dev, "%s: %d bytes left (sr=%08x)\n", ++ __func__, spi->rx_len, sr); + } + + /** +@@ -402,8 +424,7 @@ static void stm32_spi_enable(struct stm32_spi *spi) + * @spi: pointer to the spi controller data structure + * + * RX-Fifo is flushed when SPI controller is disabled. To prevent any data +- * loss, use stm32_spi_read_rxfifo(flush) to read the remaining bytes in +- * RX-Fifo. ++ * loss, use stm32_spi_read_rxfifo to read the remaining bytes in RX-Fifo. + */ + static void stm32_spi_disable(struct stm32_spi *spi) + { +@@ -438,7 +459,7 @@ static void stm32_spi_disable(struct stm32_spi *spi) + } + + if (!spi->cur_usedma && spi->rx_buf && (spi->rx_len > 0)) +- stm32_spi_read_rxfifo(spi, true); ++ stm32_spi_read_rxfifo(spi); + + if (spi->cur_usedma && spi->tx_buf) + dmaengine_terminate_all(spi->dma_tx); +@@ -483,7 +504,7 @@ static irqreturn_t stm32_spi_irq(int irq, void *dev_id) + { + struct spi_master *master = dev_id; + struct stm32_spi *spi = spi_master_get_devdata(master); +- u32 sr, ier, mask; ++ u32 sr, ier, mask, ifcr; + unsigned long flags; + bool end = false; + +@@ -491,77 +512,81 @@ static irqreturn_t stm32_spi_irq(int irq, void *dev_id) + + sr = readl_relaxed(spi->base + STM32_SPI_SR); + ier = readl_relaxed(spi->base + STM32_SPI_IER); ++ ifcr = 0; + + mask = ier; +- /* EOTIE is triggered on EOT, SUSP and TXC events. */ ++ /* ++ * EOTIE enables irq from EOT, SUSP and TXC events. We need to set ++ * SUSP to acknowledge it later. TXC is automatically cleared ++ */ + mask |= SPI_SR_SUSP; + /* +- * When TXTF is set, DXPIE and TXPIE are cleared. So in case of +- * Full-Duplex, need to poll RXP event to know if there are remaining +- * data, before disabling SPI. ++ * DXPIE is set in Full-Duplex, one IT will be raised if TXP and RXP ++ * are set. So in case of Full-Duplex, need to poll TXP and RXP event. + */ +- if (spi->rx_buf && !spi->cur_usedma) +- mask |= SPI_SR_RXP; ++ if ((spi->cur_comm == SPI_FULL_DUPLEX) && (!spi->cur_usedma)) ++ mask |= SPI_SR_TXP | SPI_SR_RXP; + +- if (!(sr & mask)) { +- dev_dbg(spi->dev, "spurious IT (sr=0x%08x, ier=0x%08x)\n", +- sr, ier); ++ mask &= sr; ++ ++ if (!mask) { ++ dev_warn(spi->dev, "spurious IT (sr=0x%08x, ier=0x%08x)\n", ++ sr, ier); + spin_unlock_irqrestore(&spi->lock, flags); + return IRQ_NONE; + } + +- if (sr & SPI_SR_SUSP) { +- dev_warn(spi->dev, "Communication suspended\n"); ++ if (mask & SPI_SR_TSERF) { ++ stm32_spi_transfer_extension(spi); ++ ifcr |= SPI_SR_TSERF; ++ } ++ ++ if (mask & SPI_SR_SUSP) { ++ dev_warn_once(spi->dev, ++ "System too slow is limiting data throughput\n"); ++ + if (!spi->cur_usedma && (spi->rx_buf && (spi->rx_len > 0))) +- stm32_spi_read_rxfifo(spi, false); +- /* +- * If communication is suspended while using DMA, it means +- * that something went wrong, so stop the current transfer +- */ +- if (spi->cur_usedma) +- end = true; ++ stm32_spi_read_rxfifo(spi); ++ ++ ifcr |= SPI_SR_SUSP; + } + +- if (sr & SPI_SR_MODF) { +- dev_warn(spi->dev, "Mode fault: transfer aborted\n"); ++ if (mask & SPI_SR_OVR) { ++ dev_err(spi->dev, "Overrun: RX data lost\n"); ++ spi->xfer_status = -EIO; + end = true; ++ ifcr |= SPI_SR_OVR; + } + +- if (sr & SPI_SR_OVR) { +- dev_warn(spi->dev, "Overrun: received value discarded\n"); +- if (!spi->cur_usedma && (spi->rx_buf && (spi->rx_len > 0))) +- stm32_spi_read_rxfifo(spi, false); +- /* +- * If overrun is detected while using DMA, it means that +- * something went wrong, so stop the current transfer +- */ +- if (spi->cur_usedma) +- end = true; +- } ++ if (mask & SPI_SR_TXTF) ++ ifcr |= SPI_SR_TXTF; + +- if (sr & SPI_SR_EOT) { ++ if (mask & SPI_SR_EOT) { + if (!spi->cur_usedma && (spi->rx_buf && (spi->rx_len > 0))) +- stm32_spi_read_rxfifo(spi, true); ++ stm32_spi_read_rxfifo(spi); + end = true; ++ ifcr |= SPI_SR_EOT; + } + +- if (sr & SPI_SR_TXP) ++ if (mask & SPI_SR_TXP) + if (!spi->cur_usedma && (spi->tx_buf && (spi->tx_len > 0))) + stm32_spi_write_txfifo(spi); + +- if (sr & SPI_SR_RXP) ++ if (mask & SPI_SR_RXP) + if (!spi->cur_usedma && (spi->rx_buf && (spi->rx_len > 0))) +- stm32_spi_read_rxfifo(spi, false); +- +- writel_relaxed(mask, spi->base + STM32_SPI_IFCR); +- +- spin_unlock_irqrestore(&spi->lock, flags); ++ stm32_spi_read_rxfifo(spi); + + if (end) { +- spi_finalize_current_transfer(master); +- stm32_spi_disable(spi); ++ /* Disable interrupts and clear status flags */ ++ writel_relaxed(0, spi->base + STM32_SPI_IER); ++ writel_relaxed(SPI_IFCR_ALL, spi->base + STM32_SPI_IFCR); ++ ++ complete(&spi->xfer_completion); ++ } else { ++ writel_relaxed(ifcr, spi->base + STM32_SPI_IFCR); + } + ++ spin_unlock_irqrestore(&spi->lock, flags); + return IRQ_HANDLED; + } + +@@ -642,25 +667,18 @@ static int stm32_spi_prepare_msg(struct spi_master *master, + /** + * stm32_spi_dma_cb - dma callback + * +- * DMA callback is called when the transfer is complete or when an error +- * occurs. If the transfer is complete, EOT flag is raised. ++ * DMA callback is called when the transfer is complete. + */ + static void stm32_spi_dma_cb(void *data) + { + struct stm32_spi *spi = data; + unsigned long flags; +- u32 sr; + + spin_lock_irqsave(&spi->lock, flags); + +- sr = readl_relaxed(spi->base + STM32_SPI_SR); ++ complete(&spi->dma_completion); + + spin_unlock_irqrestore(&spi->lock, flags); +- +- if (!(sr & SPI_SR_EOT)) +- dev_warn(spi->dev, "DMA error (sr=0x%08x)\n", sr); +- +- /* Now wait for EOT, or SUSP or OVR in case of error */ + } + + /** +@@ -709,11 +727,8 @@ static void stm32_spi_dma_config(struct stm32_spi *spi, + /** + * stm32_spi_transfer_one_irq - transfer a single spi_transfer using + * interrupts +- * +- * It must returns 0 if the transfer is finished or 1 if the transfer is still +- * in progress. + */ +-static int stm32_spi_transfer_one_irq(struct stm32_spi *spi) ++static void stm32_spi_transfer_one_irq(struct stm32_spi *spi) + { + unsigned long flags; + u32 ier = 0; +@@ -727,7 +742,9 @@ static int stm32_spi_transfer_one_irq(struct stm32_spi *spi) + ier |= SPI_IER_RXPIE; + + /* Enable the interrupts relative to the end of transfer */ +- ier |= SPI_IER_EOTIE | SPI_IER_TXTFIE | SPI_IER_OVRIE | SPI_IER_MODFIE; ++ ier |= SPI_IER_EOTIE | SPI_IER_TXTFIE | SPI_IER_OVRIE; ++ /* Enable the interrupt relative to transfer extension */ ++ ier |= SPI_IER_TSERFIE; + + spin_lock_irqsave(&spi->lock, flags); + +@@ -742,19 +759,15 @@ static int stm32_spi_transfer_one_irq(struct stm32_spi *spi) + writel_relaxed(ier, spi->base + STM32_SPI_IER); + + spin_unlock_irqrestore(&spi->lock, flags); +- +- return 1; + } + + /** + * stm32_spi_transfer_one_dma - transfer a single spi_transfer using DMA +- * +- * It must returns 0 if the transfer is finished or 1 if the transfer is still +- * in progress. + */ +-static int stm32_spi_transfer_one_dma(struct stm32_spi *spi, +- struct spi_transfer *xfer) ++static void stm32_spi_transfer_one_dma(struct stm32_spi *spi, ++ struct spi_transfer *xfer) + { ++ dma_async_tx_callback rx_done = NULL, tx_done = NULL; + struct dma_slave_config tx_dma_conf, rx_dma_conf; + struct dma_async_tx_descriptor *tx_dma_desc, *rx_dma_desc; + unsigned long flags; +@@ -762,6 +775,13 @@ static int stm32_spi_transfer_one_dma(struct stm32_spi *spi, + + spin_lock_irqsave(&spi->lock, flags); + ++ if (spi->rx_buf) ++ rx_done = stm32_spi_dma_cb; ++ else if (spi->tx_buf) ++ tx_done = stm32_spi_dma_cb; ++ ++ reinit_completion(&spi->dma_completion); ++ + rx_dma_desc = NULL; + if (spi->rx_buf) { + stm32_spi_dma_config(spi, &rx_dma_conf, DMA_DEV_TO_MEM); +@@ -794,7 +814,7 @@ static int stm32_spi_transfer_one_dma(struct stm32_spi *spi, + goto dma_desc_error; + + if (rx_dma_desc) { +- rx_dma_desc->callback = stm32_spi_dma_cb; ++ rx_dma_desc->callback = rx_done; + rx_dma_desc->callback_param = spi; + + if (dma_submit_error(dmaengine_submit(rx_dma_desc))) { +@@ -807,7 +827,7 @@ static int stm32_spi_transfer_one_dma(struct stm32_spi *spi, + + if (tx_dma_desc) { + if (spi->cur_comm == SPI_SIMPLEX_TX) { +- tx_dma_desc->callback = stm32_spi_dma_cb; ++ tx_dma_desc->callback = tx_done; + tx_dma_desc->callback_param = spi; + } + +@@ -823,7 +843,9 @@ static int stm32_spi_transfer_one_dma(struct stm32_spi *spi, + } + + /* Enable the interrupts relative to the end of transfer */ +- ier |= SPI_IER_EOTIE | SPI_IER_TXTFIE | SPI_IER_OVRIE | SPI_IER_MODFIE; ++ ier |= SPI_IER_EOTIE | SPI_IER_TXTFIE | SPI_IER_OVRIE; ++ /* Enable the interrupt relative to transfer extension */ ++ ier |= SPI_IER_TSERFIE; + writel_relaxed(ier, spi->base + STM32_SPI_IER); + + stm32_spi_enable(spi); +@@ -832,7 +854,7 @@ static int stm32_spi_transfer_one_dma(struct stm32_spi *spi, + + spin_unlock_irqrestore(&spi->lock, flags); + +- return 1; ++ return; + + dma_submit_error: + if (spi->rx_buf) +@@ -845,7 +867,8 @@ static int stm32_spi_transfer_one_dma(struct stm32_spi *spi, + + dev_info(spi->dev, "DMA issue: fall back to irq transfer\n"); + +- return stm32_spi_transfer_one_irq(spi); ++ spi->cur_usedma = false; ++ stm32_spi_transfer_one_irq(spi); + } + + /** +@@ -859,26 +882,26 @@ static int stm32_spi_transfer_one_setup(struct stm32_spi *spi, + { + unsigned long flags; + u32 cfg1_clrb = 0, cfg1_setb = 0, cfg2_clrb = 0, cfg2_setb = 0; +- u32 mode, nb_words; ++ u32 fthlv, mode, nb_words, tsize; + int ret = 0; + + spin_lock_irqsave(&spi->lock, flags); + + if (spi->cur_bpw != transfer->bits_per_word) { +- u32 bpw, fthlv; ++ u32 bpw; + + spi->cur_bpw = transfer->bits_per_word; + bpw = spi->cur_bpw - 1; + + cfg1_clrb |= SPI_CFG1_DSIZE; +- cfg1_setb |= (bpw << SPI_CFG1_DSIZE_SHIFT) & SPI_CFG1_DSIZE; ++ cfg1_setb |= FIELD_PREP(SPI_CFG1_DSIZE, bpw); ++ } + +- spi->cur_fthlv = stm32_spi_prepare_fthlv(spi); +- fthlv = spi->cur_fthlv - 1; ++ spi->cur_fthlv = stm32_spi_prepare_fthlv(spi, transfer->len); ++ fthlv = spi->cur_fthlv - 1; + +- cfg1_clrb |= SPI_CFG1_FTHLV; +- cfg1_setb |= (fthlv << SPI_CFG1_FTHLV_SHIFT) & SPI_CFG1_FTHLV; +- } ++ cfg1_clrb |= SPI_CFG1_FTHLV; ++ cfg1_setb |= FIELD_PREP(SPI_CFG1_FTHLV, fthlv); + + if (spi->cur_speed != transfer->speed_hz) { + int mbr; +@@ -893,7 +916,7 @@ static int stm32_spi_transfer_one_setup(struct stm32_spi *spi, + transfer->speed_hz = spi->cur_speed; + + cfg1_clrb |= SPI_CFG1_MBR; +- cfg1_setb |= ((u32)mbr << SPI_CFG1_MBR_SHIFT) & SPI_CFG1_MBR; ++ cfg1_setb |= FIELD_PREP(SPI_CFG1_MBR, (u32)mbr); + } + + if (cfg1_clrb || cfg1_setb) +@@ -924,19 +947,20 @@ static int stm32_spi_transfer_one_setup(struct stm32_spi *spi, + spi->cur_comm = mode; + + cfg2_clrb |= SPI_CFG2_COMM; +- cfg2_setb |= (mode << SPI_CFG2_COMM_SHIFT) & SPI_CFG2_COMM; ++ cfg2_setb |= FIELD_PREP(SPI_CFG2_COMM, mode); + } + + cfg2_clrb |= SPI_CFG2_MIDI; + if ((transfer->len > 1) && (spi->cur_midi > 0)) { +- u32 sck_period_ns = DIV_ROUND_UP(SPI_1HZ_NS, spi->cur_speed); +- u32 midi = min((u32)DIV_ROUND_UP(spi->cur_midi, sck_period_ns), +- (u32)SPI_CFG2_MIDI >> SPI_CFG2_MIDI_SHIFT); ++ u32 sck_period_ns = DIV_ROUND_UP(NSEC_PER_SEC, spi->cur_speed); ++ u32 midi = min_t(u32, ++ DIV_ROUND_UP(spi->cur_midi, sck_period_ns), ++ FIELD_GET(SPI_CFG2_MIDI, SPI_CFG2_MIDI)); + + dev_dbg(spi->dev, "period=%dns, midi=%d(=%dns)\n", + sck_period_ns, midi, midi * sck_period_ns); + +- cfg2_setb |= (midi << SPI_CFG2_MIDI_SHIFT) & SPI_CFG2_MIDI; ++ cfg2_setb |= FIELD_PREP(SPI_CFG2_MIDI, midi); + } + + if (cfg2_clrb || cfg2_setb) +@@ -950,15 +974,20 @@ static int stm32_spi_transfer_one_setup(struct stm32_spi *spi, + nb_words = DIV_ROUND_UP(transfer->len * 8, 16); + else + nb_words = DIV_ROUND_UP(transfer->len * 8, 32); +- nb_words <<= SPI_CR2_TSIZE_SHIFT; + +- if (nb_words <= SPI_CR2_TSIZE) { +- writel_relaxed(nb_words, spi->base + STM32_SPI_CR2); ++ if (nb_words <= SPI_TSIZE_MAX) { ++ tsize = nb_words; ++ spi->cur_reload = 0; + } else { +- ret = -EMSGSIZE; +- goto out; ++ tsize = SPI_TSIZE_MAX - (SPI_TSIZE_MAX % spi->cur_fthlv); ++ spi->cur_reload = nb_words - tsize; + } + ++ writel_relaxed(FIELD_PREP(SPI_CR2_TSIZE, tsize), ++ spi->base + STM32_SPI_CR2); ++ if (spi->cur_reload > 0) ++ stm32_spi_transfer_extension(spi); ++ + spi->cur_xferlen = transfer->len; + + dev_dbg(spi->dev, "transfer communication mode set to %d\n", +@@ -989,6 +1018,8 @@ static int stm32_spi_transfer_one(struct spi_master *master, + struct spi_transfer *transfer) + { + struct stm32_spi *spi = spi_master_get_devdata(master); ++ u32 xfer_time, midi_delay_ns; ++ unsigned long timeout; + int ret; + + spi->tx_buf = transfer->tx_buf; +@@ -1005,10 +1036,36 @@ static int stm32_spi_transfer_one(struct spi_master *master, + return ret; + } + ++ reinit_completion(&spi->xfer_completion); ++ spi->xfer_status = 0; ++ + if (spi->cur_usedma) +- return stm32_spi_transfer_one_dma(spi, transfer); ++ stm32_spi_transfer_one_dma(spi, transfer); + else +- return stm32_spi_transfer_one_irq(spi); ++ stm32_spi_transfer_one_irq(spi); ++ ++ /* Wait for transfer to complete */ ++ xfer_time = spi->cur_xferlen * 8 * MSEC_PER_SEC / spi->cur_speed; ++ midi_delay_ns = spi->cur_xferlen * 8 / spi->cur_bpw * spi->cur_midi; ++ xfer_time += DIV_ROUND_UP(midi_delay_ns, NSEC_PER_MSEC); ++ xfer_time = max(2 * xfer_time, 100U); ++ timeout = msecs_to_jiffies(xfer_time); ++ ++ timeout = wait_for_completion_timeout(&spi->xfer_completion, timeout); ++ if (timeout && spi->cur_usedma) ++ timeout = wait_for_completion_timeout(&spi->dma_completion, ++ timeout); ++ ++ if (!timeout) { ++ dev_err(spi->dev, "SPI transfer timeout (%u ms)\n", xfer_time); ++ spi->xfer_status = -ETIMEDOUT; ++ } ++ ++ stm32_spi_disable(spi); ++ ++ spi_finalize_current_transfer(master); ++ ++ return spi->xfer_status; + } + + /** +@@ -1076,7 +1133,7 @@ static int stm32_spi_probe(struct platform_device *pdev) + struct spi_master *master; + struct stm32_spi *spi; + struct resource *res; +- int i, ret; ++ int i, ret, num_cs, cs_gpio; + + master = spi_alloc_master(&pdev->dev, sizeof(struct stm32_spi)); + if (!master) { +@@ -1089,6 +1146,8 @@ static int stm32_spi_probe(struct platform_device *pdev) + spi->dev = &pdev->dev; + spi->master = master; + spin_lock_init(&spi->lock); ++ init_completion(&spi->xfer_completion); ++ init_completion(&spi->dma_completion); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + spi->base = devm_ioremap_resource(&pdev->dev, res); +@@ -1179,36 +1238,33 @@ static int stm32_spi_probe(struct platform_device *pdev) + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + +- ret = devm_spi_register_master(&pdev->dev, master); +- if (ret) { +- dev_err(&pdev->dev, "spi master registration failed: %d\n", +- ret); +- goto err_dma_release; +- } ++ num_cs = of_gpio_named_count(pdev->dev.of_node, "cs-gpios"); + +- if (!master->cs_gpios) { +- dev_err(&pdev->dev, "no CS gpios available\n"); +- ret = -EINVAL; +- goto err_dma_release; +- } +- +- for (i = 0; i < master->num_chipselect; i++) { +- if (!gpio_is_valid(master->cs_gpios[i])) { +- dev_err(&pdev->dev, "%i is not a valid gpio\n", +- master->cs_gpios[i]); +- ret = -EINVAL; ++ for (i = 0; i < num_cs; i++) { ++ cs_gpio = of_get_named_gpio(pdev->dev.of_node, "cs-gpios", i); ++ if (cs_gpio == -EPROBE_DEFER) { ++ ret = -EPROBE_DEFER; + goto err_dma_release; + } + +- ret = devm_gpio_request(&pdev->dev, master->cs_gpios[i], +- DRIVER_NAME); +- if (ret) { +- dev_err(&pdev->dev, "can't get CS gpio %i\n", +- master->cs_gpios[i]); +- goto err_dma_release; ++ if (gpio_is_valid(cs_gpio)) { ++ ret = devm_gpio_request(&pdev->dev, cs_gpio, ++ DRIVER_NAME); ++ if (ret) { ++ dev_err(&pdev->dev, "can't get CS gpio %i\n", ++ cs_gpio); ++ goto err_dma_release; ++ } + } + } + ++ ret = spi_register_master(master); ++ if (ret) { ++ dev_err(&pdev->dev, "spi master registration failed: %d\n", ++ ret); ++ goto err_dma_release; ++ } ++ + dev_info(&pdev->dev, "driver initialized\n"); + + return 0; +@@ -1233,6 +1289,9 @@ static int stm32_spi_remove(struct platform_device *pdev) + struct spi_master *master = platform_get_drvdata(pdev); + struct stm32_spi *spi = spi_master_get_devdata(master); + ++ pm_runtime_get_sync(&pdev->dev); ++ ++ spi_unregister_master(master); + stm32_spi_disable(spi); + + if (master->dma_tx) +@@ -1242,7 +1301,12 @@ static int stm32_spi_remove(struct platform_device *pdev) + + clk_disable_unprepare(spi->clk); + ++ pm_runtime_put_noidle(&pdev->dev); + pm_runtime_disable(&pdev->dev); ++ pm_runtime_set_suspended(&pdev->dev); ++ pm_runtime_dont_use_autosuspend(&pdev->dev); ++ ++ pinctrl_pm_select_sleep_state(&pdev->dev); + + return 0; + } +@@ -1255,13 +1319,18 @@ static int stm32_spi_runtime_suspend(struct device *dev) + + clk_disable_unprepare(spi->clk); + +- return 0; ++ return pinctrl_pm_select_sleep_state(dev); + } + + static int stm32_spi_runtime_resume(struct device *dev) + { + struct spi_master *master = dev_get_drvdata(dev); + struct stm32_spi *spi = spi_master_get_devdata(master); ++ int ret; ++ ++ ret = pinctrl_pm_select_default_state(dev); ++ if (ret) ++ return ret; + + return clk_prepare_enable(spi->clk); + } +@@ -1291,10 +1360,23 @@ static int stm32_spi_resume(struct device *dev) + return ret; + + ret = spi_master_resume(master); +- if (ret) ++ if (ret) { + clk_disable_unprepare(spi->clk); ++ return ret; ++ } + +- return ret; ++ ret = pm_runtime_get_sync(dev); ++ if (ret) { ++ dev_err(dev, "Unable to power device:%d\n", ret); ++ return ret; ++ } ++ ++ stm32_spi_config(spi); ++ ++ pm_runtime_mark_last_busy(dev); ++ pm_runtime_put_autosuspend(dev); ++ ++ return 0; + } + #endif + +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0028-ARM-stm32mp1-r0-rc2-MMC.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0028-ARM-stm32mp1-r0-rc2-MMC.patch new file mode 100644 index 0000000..e4edefb --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0028-ARM-stm32mp1-r0-rc2-MMC.patch @@ -0,0 +1,241 @@ +From bf3772b12539b3595e60b8b334051160269f9071 Mon Sep 17 00:00:00 2001 +From: Christophe Priouzeau +Date: Mon, 26 Nov 2018 14:45:33 +0100 +Subject: [PATCH 28/52] ARM-stm32mp1-r0-rc2-MMC + +--- + drivers/mmc/host/mmci.c | 36 +++++++++++++++++++++++++----------- + drivers/mmc/host/mmci.h | 13 +++++++++++-- + 2 files changed, 36 insertions(+), 13 deletions(-) + +diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c +index db50d1e..02b631f 100644 +--- a/drivers/mmc/host/mmci.c ++++ b/drivers/mmc/host/mmci.c +@@ -135,7 +135,6 @@ static struct variant_data variant_u300 = { + .datactrl_blocksz = 11, + .datactrl_dpsm_enable = MCI_DPSM_ENABLE, + .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, +- .st_sdio = true, + .pwrreg_powerup = MCI_PWR_ON, + .f_max = 100000000, + .signal_direction = true, +@@ -146,6 +145,7 @@ static struct variant_data variant_u300 = { + .start_err = MCI_STARTBITERR, + .opendrain = MCI_OD, + .init = mmci_variant_init, ++ .quirks = MMCI_QUIRK_ST_SDIO, + }; + + static struct variant_data variant_nomadik = { +@@ -161,7 +161,6 @@ static struct variant_data variant_nomadik = { + .datactrl_blocksz = 11, + .datactrl_dpsm_enable = MCI_DPSM_ENABLE, + .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, +- .st_sdio = true, + .st_clkdiv = true, + .pwrreg_powerup = MCI_PWR_ON, + .f_max = 100000000, +@@ -173,6 +172,7 @@ static struct variant_data variant_nomadik = { + .start_err = MCI_STARTBITERR, + .opendrain = MCI_OD, + .init = mmci_variant_init, ++ .quirks = MMCI_QUIRK_ST_SDIO, + }; + + static struct variant_data variant_ux500 = { +@@ -190,7 +190,6 @@ static struct variant_data variant_ux500 = { + .datactrl_blocksz = 11, + .datactrl_dpsm_enable = MCI_DPSM_ENABLE, + .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, +- .st_sdio = true, + .st_clkdiv = true, + .pwrreg_powerup = MCI_PWR_ON, + .f_max = 100000000, +@@ -206,6 +205,7 @@ static struct variant_data variant_ux500 = { + .start_err = MCI_STARTBITERR, + .opendrain = MCI_OD, + .init = mmci_variant_init, ++ .quirks = MMCI_QUIRK_ST_SDIO, + }; + + static struct variant_data variant_ux500v2 = { +@@ -224,7 +224,6 @@ static struct variant_data variant_ux500v2 = { + .datactrl_blocksz = 11, + .datactrl_dpsm_enable = MCI_DPSM_ENABLE, + .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, +- .st_sdio = true, + .st_clkdiv = true, + .blksz_datactrl16 = true, + .pwrreg_powerup = MCI_PWR_ON, +@@ -241,6 +240,7 @@ static struct variant_data variant_ux500v2 = { + .start_err = MCI_STARTBITERR, + .opendrain = MCI_OD, + .init = mmci_variant_init, ++ .quirks = MMCI_QUIRK_ST_SDIO, + }; + + static struct variant_data variant_stm32 = { +@@ -259,13 +259,13 @@ static struct variant_data variant_stm32 = { + .datactrl_blocksz = 11, + .datactrl_dpsm_enable = MCI_DPSM_ENABLE, + .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN, +- .st_sdio = true, + .st_clkdiv = true, + .pwrreg_powerup = MCI_PWR_ON, + .f_max = 48000000, + .pwrreg_clkgate = true, + .pwrreg_nopower = true, + .init = mmci_variant_init, ++ .quirks = MMCI_QUIRK_ST_SDIO, + }; + + static struct variant_data variant_stm32_sdmmc = { +@@ -284,12 +284,14 @@ static struct variant_data variant_stm32_sdmmc = { + .datacnt_useless = true, + .datalength_bits = 25, + .datactrl_blocksz = 14, ++ .datactrl_mask_sdio = MCI_DPSM_STM32_SDIOEN, + .stm32_idmabsize_mask = GENMASK(12, 5), + .busy_detect = true, + .busy_timeout = true, + .busy_detect_flag = MCI_STM32_BUSYD0, + .busy_detect_mask = MCI_STM32_BUSYD0ENDMASK, + .init = sdmmc_variant_init, ++ .quirks = MMCI_QUIRK_STM32_DTMODE, + }; + + static struct variant_data variant_stm32_sdmmcv2 = { +@@ -308,6 +310,7 @@ static struct variant_data variant_stm32_sdmmcv2 = { + .datacnt_useless = true, + .datalength_bits = 25, + .datactrl_blocksz = 14, ++ .datactrl_mask_sdio = MCI_DPSM_STM32_SDIOEN, + .stm32_idmabsize_mask = GENMASK(16, 5), + .dma_lli = true, + .busy_detect = true, +@@ -315,6 +318,7 @@ static struct variant_data variant_stm32_sdmmcv2 = { + .busy_detect_flag = MCI_STM32_BUSYD0, + .busy_detect_mask = MCI_STM32_BUSYD0ENDMASK, + .init = sdmmc_variant_init, ++ .quirks = MMCI_QUIRK_STM32_DTMODE, + }; + + static struct variant_data variant_qcom = { +@@ -505,7 +509,8 @@ static int mmci_validate_data(struct mmci_host *host, + if (!data) + return 0; + +- if (!is_power_of_2(data->blksz)) { ++ if ((host->mmc->card && !mmc_card_sdio(host->mmc->card)) && ++ !is_power_of_2(data->blksz)) { + dev_err(mmc_dev(host->mmc), + "unsupported block size (%d bytes)\n", data->blksz); + return -EINVAL; +@@ -1068,7 +1073,6 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) + writel(host->size, base + MMCIDATALENGTH); + + blksz_bits = ffs(data->blksz) - 1; +- BUG_ON(1 << blksz_bits != data->blksz); + + if (variant->blksz_datactrl16) + datactrl = variant->datactrl_dpsm_enable | (data->blksz << 16); +@@ -1077,6 +1081,16 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) + else + datactrl = variant->datactrl_dpsm_enable | blksz_bits << 4; + ++ if (variant->quirks & MMCI_QUIRK_STM32_DTMODE) { ++ if (host->mmc->card && mmc_card_sdio(host->mmc->card) && ++ data->blocks == 1) ++ datactrl |= MCI_DPSM_STM32_MODE_SDIO; ++ else if (data->stop && !host->mrq->sbc) ++ datactrl |= MCI_DPSM_STM32_MODE_BLOCK_STOP; ++ else ++ datactrl |= MCI_DPSM_STM32_MODE_BLOCK; ++ } ++ + if (data->flags & MMC_DATA_READ) + datactrl |= MCI_DPSM_DIRECTION; + +@@ -1091,7 +1105,8 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) + * otherwise the transfer will not start. The threshold + * depends on the rate of MCLK. + */ +- if (variant->st_sdio && data->flags & MMC_DATA_WRITE && ++ if (variant->quirks & MMCI_QUIRK_ST_SDIO && ++ data->flags & MMC_DATA_WRITE && + (host->size < 8 || + (host->size <= 8 && host->mclk > 50000000))) + clk = host->clk_reg & ~variant->clkreg_enable; +@@ -1257,11 +1272,10 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data, + /* The error clause is handled above, success! */ + data->bytes_xfered = data->blksz * data->blocks; + +- if (!data->stop || host->mrq->sbc) { ++ if (!data->stop || (host->mrq->sbc && !data->error)) + mmci_request_end(host, data->mrq); +- } else { ++ else + mmci_start_command(host, data->stop, 0); +- } + } + } + +diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h +index 36a744a..55867fc 100644 +--- a/drivers/mmc/host/mmci.h ++++ b/drivers/mmc/host/mmci.h +@@ -131,6 +131,12 @@ + /* Control register extensions in the Qualcomm versions */ + #define MCI_DPSM_QCOM_DATA_PEND BIT(17) + #define MCI_DPSM_QCOM_RX_DATA_PEND BIT(20) ++/* Control register extensions in STM32 versions */ ++#define MCI_DPSM_STM32_MODE_BLOCK (0 << 2) ++#define MCI_DPSM_STM32_MODE_SDIO (1 << 2) ++#define MCI_DPSM_STM32_MODE_STREAM (2 << 2) ++#define MCI_DPSM_STM32_MODE_BLOCK_STOP (3 << 2) ++#define MCI_DPSM_STM32_SDIOEN BIT(11) + + #define MMCIDATACNT 0x030 + #define MMCISTATUS 0x034 +@@ -273,7 +279,6 @@ struct mmci_host; + * @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY + * is asserted (likewise for RX) + * @data_cmd_enable: enable value for data commands. +- * @st_sdio: enable ST specific SDIO logic + * @st_clkdiv: true if using a ST-specific clock divider algorithm + * @stm32_clkdiv: true if using a STM32-specific clock divider algorithm + * @datactrl_mask_ddrmode: ddr mode mask in datactrl register. +@@ -309,6 +314,7 @@ struct mmci_host; + * @opendrain: bitmask identifying the OPENDRAIN bit inside MMCIPOWER register + * @dma_lli: true if variant has dma link list feature. + * @stm32_idmabsize_mask: stm32 sdmmc idma buffer size. ++ * @quirks: A bitmap of hardware quirks that require some special action. + */ + struct variant_data { + unsigned int clkreg; +@@ -330,7 +336,6 @@ struct variant_data { + unsigned int datactrl_dpsm_enable; + u8 datactrl_first:1; + u8 datacnt_useless:1; +- u8 st_sdio:1; + u8 st_clkdiv:1; + u8 stm32_clkdiv:1; + u8 blksz_datactrl16:1; +@@ -355,9 +360,13 @@ struct variant_data { + u32 opendrain; + u8 dma_lli:1; + u32 stm32_idmabsize_mask; ++ u32 quirks; + void (*init)(struct mmci_host *host); + }; + ++#define MMCI_QUIRK_STM32_DTMODE BIT(0) ++#define MMCI_QUIRK_ST_SDIO BIT(2) /* enable ST specific SDIO logic */ ++ + /* mmci variant callbacks */ + struct mmci_host_ops { + int (*validate_data)(struct mmci_host *host, struct mmc_data *data); +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0029-ARM-stm32mp1-r0-rc2-HWSPINLOCK-IIO-I2C.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0029-ARM-stm32mp1-r0-rc2-HWSPINLOCK-IIO-I2C.patch new file mode 100644 index 0000000..f1c6e73 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0029-ARM-stm32mp1-r0-rc2-HWSPINLOCK-IIO-I2C.patch @@ -0,0 +1,771 @@ +From 72179a889501ef339df094abe108846b19e7a06c Mon Sep 17 00:00:00 2001 +From: Christophe Priouzeau +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 for STMicroelectronics. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 "); +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 + #include + #include ++#include + #include + #include + #include + #include ++#include + #include + #include + +@@ -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 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0030-ARM-stm32mp1-r0-rc2-DEVICETREE.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0030-ARM-stm32mp1-r0-rc2-DEVICETREE.patch new file mode 100644 index 0000000..111a8cd --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0030-ARM-stm32mp1-r0-rc2-DEVICETREE.patch @@ -0,0 +1,2514 @@ +From ee54d2cd85d8254c5e31548986b545c33c75dd04 Mon Sep 17 00:00:00 2001 +From: Christophe Priouzeau +Date: Mon, 26 Nov 2018 14:46:41 +0100 +Subject: [PATCH 30/52] ARM-stm32mp1-r0-rc2-DEVICETREE + +--- + arch/arm/boot/dts/Makefile | 4 +- + arch/arm/boot/dts/stm32mp157-pinctrl.dtsi | 424 +++++++++++++++++- + arch/arm/boot/dts/stm32mp157a-dk1.dts | 274 +++++++++++- + arch/arm/boot/dts/stm32mp157c-dk2-m4-examples.dts | 156 +++++++ + arch/arm/boot/dts/stm32mp157c-dk2.dts | 112 +++++ + arch/arm/boot/dts/stm32mp157c-ed1.dts | 43 +- + arch/arm/boot/dts/stm32mp157c-ev1-m4-examples.dts | 161 +++++++ + arch/arm/boot/dts/stm32mp157c-ev1.dts | 499 +++++++++++++++++++++- + arch/arm/boot/dts/stm32mp157c.dtsi | 284 +++++++++++- + 9 files changed, 1910 insertions(+), 47 deletions(-) + create mode 100644 arch/arm/boot/dts/stm32mp157c-dk2-m4-examples.dts + create mode 100644 arch/arm/boot/dts/stm32mp157c-ev1-m4-examples.dts + +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index b264fa0..fdf53f5 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -924,8 +924,10 @@ dtb-$(CONFIG_ARCH_STM32) += \ + stm32h743i-disco.dtb \ + stm32mp157a-dk1.dtb \ + stm32mp157c-dk2.dtb \ ++ stm32mp157c-dk2-m4-examples.dtb \ + stm32mp157c-ed1.dtb \ +- stm32mp157c-ev1.dtb ++ stm32mp157c-ev1.dtb \ ++ stm32mp157c-ev1-m4-examples.dtb + dtb-$(CONFIG_MACH_SUN4I) += \ + sun4i-a10-a1000.dtb \ + sun4i-a10-ba10-tvbox.dtb \ +diff --git a/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi b/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi +index 4409db2..659094e 100644 +--- a/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi ++++ b/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi +@@ -14,6 +14,7 @@ + ranges = <0 0x50002000 0xa400>; + interrupt-parent = <&exti>; + st,syscfg = <&exti 0x60 0xff>; ++ hwlocks = <&hsem 0>; + pins-are-numbered; + + gpioa: gpio@50002000 { +@@ -168,6 +169,27 @@ + }; + }; + ++ cec_pins_sleep_a: cec-sleep-0 { ++ pins { ++ pinmux = ; /* HDMI_CEC */ ++ }; ++ }; ++ ++ cec_pins_b: cec-1 { ++ pins { ++ pinmux = ; ++ bias-disable; ++ drive-open-drain; ++ slew-rate = <0>; ++ }; ++ }; ++ ++ cec_pins_sleep_b: cec-sleep-1 { ++ pins { ++ pinmux = ; /* HDMI_CEC */ ++ }; ++ }; ++ + dac_ch1_pins_a: dac-ch1 { + pins { + pinmux = ; +@@ -180,6 +202,47 @@ + }; + }; + ++ dcmi_pins_a: dcmi-0 { ++ pins { ++ pinmux = ,/* DCMI_HSYNC */ ++ ,/* DCMI_VSYNC */ ++ ,/* DCMI_PIXCLK */ ++ ,/* DCMI_D0 */ ++ ,/* DCMI_D1 */ ++ ,/* DCMI_D2 */ ++ ,/* DCMI_D3 */ ++ ,/* DCMI_D4 */ ++ ,/* DCMI_D5 */ ++ ,/* DCMI_D6 */ ++ ,/* DCMI_D7 */ ++ ,/* DCMI_D8 */ ++ ,/* DCMI_D9 */ ++ ,/* DCMI_D10 */ ++ ;/* DCMI_D11 */ ++ bias-disable; ++ }; ++ }; ++ ++ dcmi_sleep_pins_a: dcmi-sleep-0 { ++ pins { ++ pinmux = ,/* DCMI_HSYNC */ ++ ,/* DCMI_VSYNC */ ++ ,/* DCMI_PIXCLK */ ++ ,/* DCMI_D0 */ ++ ,/* DCMI_D1 */ ++ ,/* DCMI_D2 */ ++ ,/* DCMI_D3 */ ++ ,/* DCMI_D4 */ ++ ,/* DCMI_D5 */ ++ ,/* DCMI_D6 */ ++ ,/* DCMI_D7 */ ++ ,/* DCMI_D8 */ ++ ,/* DCMI_D9 */ ++ ,/* DCMI_D10 */ ++ ;/* DCMI_D11 */ ++ }; ++ }; ++ + dfsdm_clkout_pins_a: dfsdm-clkout-pins-0 { + pins { + pinmux = ; /* DFSDM_CKOUT */ +@@ -361,6 +424,163 @@ + }; + }; + ++ i2s2_pins_a: i2s2-0 { ++ pins { ++ pinmux = , /* I2S2_SDO */ ++ , /* I2S2_WS */ ++ ; /* I2S2_CK */ ++ slew-rate = <1>; ++ drive-push-pull; ++ bias-disable; ++ }; ++ }; ++ ++ i2s2_pins_sleep_a: i2s2-1 { ++ pins { ++ pinmux = , /* I2S2_SDO */ ++ , /* I2S2_WS */ ++ ; /* I2S2_CK */ ++ }; ++ }; ++ ++ ltdc_pins_a: ltdc-a-0 { ++ pins { ++ pinmux = , /* LCD_CLK */ ++ , /* LCD_HSYNC */ ++ , /* LCD_VSYNC */ ++ , /* LCD_DE */ ++ , /* LCD_R0 */ ++ , /* LCD_R1 */ ++ , /* LCD_R2 */ ++ , /* LCD_R3 */ ++ , /* LCD_R4 */ ++ , /* LCD_R5 */ ++ , /* LCD_R6 */ ++ , /* LCD_R7 */ ++ , /* LCD_G0 */ ++ , /* LCD_G1 */ ++ , /* LCD_G2 */ ++ , /* LCD_G3 */ ++ , /* LCD_G4 */ ++ , /* LCD_G5 */ ++ , /* LCD_G6 */ ++ , /* LCD_G7 */ ++ , /* LCD_B0 */ ++ , /* LCD_B1 */ ++ , /* LCD_B2 */ ++ , /* LCD_B3 */ ++ , /* LCD_B4 */ ++ , /* LCD_B5 */ ++ , /* LCD_B6 */ ++ ; /* LCD_B7 */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <2>; ++ }; ++ }; ++ ++ ltdc_pins_sleep_a: ltdc-a-1 { ++ pins { ++ pinmux = , /* LCD_CLK */ ++ , /* LCD_HSYNC */ ++ , /* LCD_VSYNC */ ++ , /* LCD_DE */ ++ , /* LCD_R0 */ ++ , /* LCD_R1 */ ++ , /* LCD_R2 */ ++ , /* LCD_R3 */ ++ , /* LCD_R4 */ ++ , /* LCD_R5 */ ++ , /* LCD_R6 */ ++ , /* LCD_R7 */ ++ , /* LCD_G0 */ ++ , /* LCD_G1 */ ++ , /* LCD_G2 */ ++ , /* LCD_G3 */ ++ , /* LCD_G4 */ ++ , /* LCD_G5 */ ++ , /* LCD_G6 */ ++ , /* LCD_G7 */ ++ , /* LCD_B0 */ ++ , /* LCD_B1 */ ++ , /* LCD_B2 */ ++ , /* LCD_B3 */ ++ , /* LCD_B4 */ ++ , /* LCD_B5 */ ++ , /* LCD_B6 */ ++ ; /* LCD_B7 */ ++ }; ++ }; ++ ++ ltdc_pins_b: ltdc-b-0 { ++ pins { ++ pinmux = , /* LCD_CLK */ ++ , /* LCD_HSYNC */ ++ , /* LCD_VSYNC */ ++ , /* LCD_DE */ ++ , /* LCD_R0 */ ++ , /* LCD_R1 */ ++ , /* LCD_R2 */ ++ , /* LCD_R3 */ ++ , /* LCD_R4 */ ++ , /* LCD_R5 */ ++ , /* LCD_R6 */ ++ , /* LCD_R7 */ ++ , /* LCD_G0 */ ++ , /* LCD_G1 */ ++ , /* LCD_G2 */ ++ , /* LCD_G3 */ ++ , /* LCD_G4 */ ++ , /* LCD_G5 */ ++ , /* LCD_G6 */ ++ , /* LCD_G7 */ ++ , /* LCD_B0 */ ++ , /* LCD_B1 */ ++ , /* LCD_B2 */ ++ , /* LCD_B3 */ ++ , /* LCD_B4 */ ++ , /* LCD_B5 */ ++ , /* LCD_B6 */ ++ ; /* LCD_B7 */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <2>; ++ }; ++ }; ++ ++ ltdc_pins_sleep_b: ltdc-b-1 { ++ pins { ++ pinmux = , /* LCD_CLK */ ++ , /* LCD_HSYNC */ ++ , /* LCD_VSYNC */ ++ , /* LCD_DE */ ++ , /* LCD_R0 */ ++ , /* LCD_R1 */ ++ , /* LCD_R2 */ ++ , /* LCD_R3 */ ++ , /* LCD_R4 */ ++ , /* LCD_R5 */ ++ , /* LCD_R6 */ ++ , /* LCD_R7 */ ++ , /* LCD_G0 */ ++ , /* LCD_G1 */ ++ , /* LCD_G2 */ ++ , /* LCD_G3 */ ++ , /* LCD_G4 */ ++ , /* LCD_G5 */ ++ , /* LCD_G6 */ ++ , /* LCD_G7 */ ++ , /* LCD_B0 */ ++ , /* LCD_B1 */ ++ , /* LCD_B2 */ ++ , /* LCD_B3 */ ++ , /* LCD_B4 */ ++ , /* LCD_B5 */ ++ , /* LCD_B6 */ ++ ; /* LCD_B7 */ ++ }; ++ }; ++ + m_can1_pins_a: m-can1-0 { + pins1 { + pinmux = ; /* CAN1_TX */ +@@ -507,21 +727,6 @@ + }; + }; + +- qspi_clk_pins_a: qspi-clk-0 { +- pins { +- pinmux = ; /* QSPI_CLK */ +- bias-disable; +- drive-push-pull; +- slew-rate = <3>; +- }; +- }; +- +- qspi_clk_sleep_pins_a: qspi-clk-sleep-0 { +- pins { +- pinmux = ; /* QSPI_CLK */ +- }; +- }; +- + qspi_bk1_pins_a: qspi-bk1-0 { + pins1 { + pinmux = , /* QSPI_BK1_IO0 */ +@@ -578,12 +783,110 @@ + }; + }; + ++ qspi_clk_pins_a: qspi-clk-0 { ++ pins { ++ pinmux = ; /* QSPI_CLK */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <3>; ++ }; ++ }; ++ ++ qspi_clk_sleep_pins_a: qspi-clk-sleep-0 { ++ pins { ++ pinmux = ; /* QSPI_CLK */ ++ }; ++ }; ++ + rtc_out2_rmp_pins_a: rtc-out2-rmp-pins@0 { + pins { + pinmux = ; /* RTC_OUT2_RMP */ + }; + }; + ++ sai2a_pins_a: sai2a-0 { ++ pins1 { ++ pinmux = , /* SAI2_SCK_A */ ++ , /* SAI2_SD_A */ ++ ; /* SAI2_FS_A */ ++ slew-rate = <1>; ++ drive-push-pull; ++ bias-disable; ++ }; ++ pins2 { ++ pinmux = ; /* SAI2_MCLK_A */ ++ slew-rate = <2>; ++ drive-push-pull; ++ bias-disable; ++ }; ++ }; ++ ++ sai2a_sleep_pins_a: sai2a-1 { ++ pins { ++ pinmux = , /* SAI2_SCK_A */ ++ , /* SAI2_SD_A */ ++ , /* SAI2_FS_A */ ++ ; /* SAI2_MCLK_A */ ++ }; ++ }; ++ ++ sai2b_pins_a: sai2b-0 { ++ pins1 { ++ pinmux = , /* SAI2_SCK_B */ ++ ; /* SAI2_FS_B */ ++ slew-rate = <1>; ++ drive-push-pull; ++ bias-disable; ++ }; ++ pins2 { ++ pinmux = ; /* SAI2_MCLK_B */ ++ slew-rate = <2>; ++ drive-push-pull; ++ bias-disable; ++ }; ++ pins3 { ++ pinmux = ; /* SAI2_SD_B */ ++ bias-disable; ++ }; ++ }; ++ ++ sai2b_sleep_pins_a: sai2b-1 { ++ pins { ++ pinmux = , /* SAI2_SD_B */ ++ , /* SAI2_SCK_B */ ++ , /* SAI2_FS_B */ ++ ; /* SAI2_MCLK_B */ ++ }; ++ }; ++ ++ sai2b_pins_b: sai2b-2 { ++ pins { ++ pinmux = ; /* SAI2_SD_B */ ++ bias-disable; ++ }; ++ }; ++ ++ sai2b_sleep_pins_b: sai2b-3 { ++ pins { ++ pinmux = ; /* SAI2_SD_B */ ++ }; ++ }; ++ ++ sai4a_pins_a: sai4a-0 { ++ pins { ++ pinmux = ; /* SAI4_SD_A */ ++ slew-rate = <1>; ++ drive-push-pull; ++ bias-disable; ++ }; ++ }; ++ ++ sai4a_sleep_pins_a: sai4a-1 { ++ pins { ++ pinmux = ; /* SAI4_SD_A */ ++ }; ++ }; ++ + sdmmc1_b4_pins_a: sdmmc1-b4-0 { + pins { + pinmux = , /* SDMMC1_D0 */ +@@ -742,6 +1045,65 @@ + }; + }; + ++ spdifrx_pins_a: spdifrx-0 { ++ pins { ++ pinmux = ; /* SPDIF_IN1 */ ++ bias-disable; ++ }; ++ }; ++ ++ spdifrx_sleep_pins_a: spdifrx-1 { ++ pins { ++ pinmux = ; /* SPDIF_IN1 */ ++ }; ++ }; ++ ++ spi4_pins_a: spi4-0 { ++ pins1 { ++ pinmux = , /* SPI4_SCK */ ++ ; /* SPI4_MOSI */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <1>; ++ }; ++ ++ pins2 { ++ pinmux = ; /* SPI4_MISO */ ++ bias-disable; ++ }; ++ }; ++ ++ spi4_sleep_pins_a: spi4-sleep-0 { ++ pins { ++ pinmux = , /* SPI4_SCK */ ++ , /* SPI4_MISO */ ++ ; /* SPI4_MOSI */ ++ }; ++ }; ++ ++ spi5_pins_a: spi5-0 { ++ pins1 { ++ pinmux = , /* SPI5_SCK */ ++ ; /* SPI5_MOSI */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <1>; ++ }; ++ ++ pins2 { ++ pinmux = ; /* SPI5_MISO */ ++ bias-disable; ++ }; ++ }; ++ ++ spi5_sleep_pins_a: spi5-sleep-0 { ++ pins { ++ pinmux = , /* SPI5_SCK */ ++ , /* SPI5_MISO */ ++ ; /* SPI5_MOSI */ ++ }; ++ }; ++ + uart4_pins_a: uart4-0 { + pins1 { + pinmux = ; /* UART4_TX */ +@@ -879,6 +1241,19 @@ + ; /* USART3_RX */ + }; + }; ++ ++ usbotg_hs_pins_a: usbotg_hs-0 { ++ pins { ++ pinmux = ; /* OTG_ID */ ++ }; ++ }; ++ ++ usbotg_fs_dp_dm_pins_a: usbotg-fs-dp-dm-0 { ++ pins { ++ pinmux = , /* OTG_FS_DM */ ++ ; /* OTG_FS_DP */ ++ }; ++ }; + }; + + pinctrl_z: pin-controller-z@54004000 { +@@ -889,6 +1264,7 @@ + pins-are-numbered; + interrupt-parent = <&exti>; + st,syscfg = <&exti 0x60 0xff>; ++ hwlocks = <&hsem 1>; + + gpioz: gpio@54004000 { + gpio-controller; +@@ -902,6 +1278,16 @@ + status = "disabled"; + }; + ++ btreg: bt_reg_on-0 { ++ pins { ++ pinmux = ; ++ drive-push-pull; ++ bias-pull-up; ++ output-high; ++ slew-rate = <0>; ++ }; ++ }; ++ + i2c4_pins_a: i2c4-0 { + pins { + pinmux = , /* I2C4_SCL */ +@@ -933,6 +1319,14 @@ + bias-disable; + }; + }; ++ ++ spi1_sleep_pins_a: spi1-sleep-0 { ++ pins { ++ pinmux = , /* SPI1_SCK */ ++ , /* SPI1_MISO */ ++ ; /* SPI1_MOSI */ ++ }; ++ }; + }; + }; + }; +diff --git a/arch/arm/boot/dts/stm32mp157a-dk1.dts b/arch/arm/boot/dts/stm32mp157a-dk1.dts +index 866eed7..2f01e97 100644 +--- a/arch/arm/boot/dts/stm32mp157a-dk1.dts ++++ b/arch/arm/boot/dts/stm32mp157a-dk1.dts +@@ -17,6 +17,11 @@ + model = "STMicroelectronics STM32MP157A-DK1 Discovery Board"; + compatible = "st,stm32mp157a-dk1", "st,stm32mp157"; + ++ aliases { ++ ethernet0 = ðernet0; ++ serial0 = &uart4; ++ }; ++ + chosen { + stdout-path = "serial0:115200n8"; + }; +@@ -35,10 +40,11 @@ + reg = <0x10040000 0x10000>; + no-map; + }; +- }; + +- aliases { +- serial0 = &uart4; ++ gpu_reserved: gpu@dc000000 { ++ reg = <0xdc000000 0x4000000>; ++ no-map; ++ }; + }; + + iio-hwmon { +@@ -68,11 +74,34 @@ + default-state = "off"; + }; + }; ++ ++ sound { ++ compatible = "audio-graph-card"; ++ routing = ++ "Playback" , "MCLK", ++ "Capture" , "MCLK", ++ "MICL" , "Mic Bias"; ++ dais = <&sai2a_port &sai2b_port &i2s2_port>; ++ status = "okay"; ++ }; ++ ++ usb_phy_tuning: usb-phy-tuning { ++ st,hs-dc-level = <2>; ++ st,fs-rftime-tuning; ++ st,hs-rftime-reduction; ++ st,hs-current-trim = <15>; ++ st,hs-impedance-trim = <1>; ++ st,squelch-level = <3>; ++ st,hs-rx-offset = <2>; ++ st,no-lsfs-sc; ++ }; + }; + + &adc { + pinctrl-names = "default"; + pinctrl-0 = <&adc12_usb_pwr_pins_a>; ++ vdd-supply = <&vdd>; ++ vdda-supply = <&vdd>; + vref-supply = <&vrefbuf>; + status = "okay"; + adc1: adc@0 { +@@ -99,6 +128,13 @@ + }; + }; + ++&cec { ++ pinctrl-names = "default", "sleep"; ++ pinctrl-0 = <&cec_pins_b>; ++ pinctrl-1 = <&cec_pins_sleep_b>; ++ status = "okay"; ++}; ++ + &dma1 { + sram = <&dma_pool>; + }; +@@ -107,6 +143,34 @@ + sram = <&dma_pool>; + }; + ++ðernet0 { ++ status = "okay"; ++ pinctrl-0 = <ðernet0_rgmii_pins_a>; ++ pinctrl-1 = <ðernet0_rgmii_pins_sleep_a>; ++ pinctrl-names = "default", "sleep"; ++ phy-mode = "rgmii"; ++ max-speed = <1000>; ++ phy-handle = <&phy0>; ++ ++ mdio0 { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ compatible = "snps,dwmac-mdio"; ++ phy0: ethernet-phy@0 { ++ reg = <0>; ++ }; ++ }; ++}; ++ ++&gpu { ++ contiguous-area = <&gpu_reserved>; ++ status = "okay"; ++}; ++ ++&hsem { ++ status = "okay"; ++}; ++ + &i2c1 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&i2c1_pins_a>; +@@ -116,6 +180,75 @@ + status = "okay"; + /delete-property/dmas; + /delete-property/dma-names; ++ ++ cs42l51: cs42l51@4a { ++ compatible = "cirrus,cs42l51"; ++ reg = <0x4a>; ++ #sound-dai-cells = <0>; ++ status = "okay"; ++ ++ VL-supply = <&v3v3>; ++ VD-supply = <&v1v8_audio>; ++ VA-supply = <&v1v8_audio>; ++ VAHP-supply = <&v1v8_audio>; ++ ++ reset-gpios = <&gpiog 9 GPIO_ACTIVE_LOW>; ++ ++ clocks = <&sai2a>; ++ clock-names = "MCLK"; ++ ++ cs42l51_port: port { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ cs42l51_tx_endpoint: endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&sai2a_endpoint>; ++ frame-master; ++ bitclock-master; ++ }; ++ ++ cs42l51_rx_endpoint: endpoint@1 { ++ reg = <1>; ++ remote-endpoint = <&sai2b_endpoint>; ++ frame-master; ++ bitclock-master; ++ }; ++ }; ++ }; ++ ++ hdmi-transmitter@39 { ++ compatible = "sil,sii9022"; ++ reg = <0x39>; ++ iovcc-supply = <&v3v3_hdmi>; ++ cvcc12-supply = <&v1v2_hdmi>; ++ reset-gpios = <&gpioa 10 GPIO_ACTIVE_LOW>; ++ interrupts = <1 IRQ_TYPE_EDGE_FALLING>; ++ interrupt-parent = <&gpiog>; ++ pinctrl-names = "default", "sleep"; ++ pinctrl-0 = <<dc_pins_a>; ++ pinctrl-1 = <<dc_pins_sleep_a>; ++ status = "okay"; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ reg = <0>; ++ sii9022_in: endpoint { ++ remote-endpoint = <<dc_ep0_out>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ sii9022_tx_endpoint: endpoint { ++ remote-endpoint = <&i2s2_endpoint>; ++ }; ++ }; ++ }; ++ }; + }; + + &i2c4 { +@@ -131,8 +264,7 @@ + pmic: stpmic@33 { + compatible = "st,stpmic1"; + reg = <0x33>; +- interrupts-extended = <&pwr 0 IRQ_TYPE_EDGE_FALLING 1>, +- <&exti 55 1>; ++ interrupts-extended = <&exti_pwr 55 IRQ_TYPE_EDGE_FALLING>; + interrupt-controller; + #interrupt-cells = <2>; + status = "okay"; +@@ -274,6 +406,23 @@ + }; + }; + ++&i2s2 { ++ clocks = <&rcc SPI2>, <&rcc SPI2_K>, <&rcc PLL3_Q>, <&rcc PLL4_Q>; ++ clock-names = "pclk", "i2sclk", "x8k", "x11k"; ++ pinctrl-names = "default", "sleep"; ++ pinctrl-0 = <&i2s2_pins_a>; ++ pinctrl-1 = <&i2s2_pins_sleep_a>; ++ status = "okay"; ++ ++ i2s2_port: port { ++ i2s2_endpoint: endpoint { ++ remote-endpoint = <&sii9022_tx_endpoint>; ++ format = "i2s"; ++ mclk-fs = <256>; ++ }; ++ }; ++}; ++ + &ipcc { + status = "okay"; + }; +@@ -283,10 +432,24 @@ + status = "okay"; + }; + ++<dc { ++ status = "okay"; ++ ++ port { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ ltdc_ep0_out: endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&sii9022_in>; ++ }; ++ }; ++}; ++ + &m4_rproc { + memory-region = <&ipc_share>; + mboxes = <&ipcc 0>, <&ipcc 1>, <&ipcc 2>; +- mbox-names = "vq0", "vq1", "init_shdn"; ++ mbox-names = "vq0", "vq1", "shutdown"; + interrupt-parent = <&exti>; + interrupts = <68 1>; + interrupt-names = "wdg"; +@@ -294,6 +457,10 @@ + status = "okay"; + }; + ++&pwr { ++ pwr-supply = <&vdd>; ++}; ++ + &rng1 { + status = "okay"; + }; +@@ -302,18 +469,77 @@ + status = "okay"; + }; + ++&sai2 { ++ clocks = <&rcc SAI2>, <&rcc PLL3_Q>, <&rcc PLL3_Q>; ++ clock-names = "pclk", "x8k", "x11k"; ++ pinctrl-names = "default", "sleep"; ++ pinctrl-0 = <&sai2a_pins_a>, <&sai2b_pins_b>; ++ pinctrl-1 = <&sai2a_sleep_pins_a>, <&sai2b_sleep_pins_b>; ++ status = "okay"; ++ ++ sai2a: audio-controller@4400b004 { ++ #clock-cells = <0>; ++ dma-names = "tx"; ++ clocks = <&rcc SAI2_K>; ++ clock-names = "sai_ck"; ++ status = "okay"; ++ ++ sai2a_port: port { ++ sai2a_endpoint: endpoint { ++ remote-endpoint = <&cs42l51_tx_endpoint>; ++ format = "i2s"; ++ mclk-fs = <256>; ++ dai-tdm-slot-num = <2>; ++ dai-tdm-slot-width = <32>; ++ }; ++ }; ++ }; ++ ++ sai2b: audio-controller@4400b024 { ++ dma-names = "rx"; ++ st,sync = <&sai2a 2>; ++ status = "okay"; ++ clocks = <&rcc SAI2_K>, <&sai2a>; ++ clock-names = "sai_ck", "MCLK"; ++ ++ sai2b_port: port { ++ sai2b_endpoint: endpoint { ++ remote-endpoint = <&cs42l51_rx_endpoint>; ++ format = "i2s"; ++ mclk-fs = <256>; ++ dai-tdm-slot-num = <2>; ++ dai-tdm-slot-width = <32>; ++ }; ++ }; ++ }; ++}; ++ + &sdmmc1 { + pinctrl-names = "default", "opendrain", "sleep"; + pinctrl-0 = <&sdmmc1_b4_pins_a>; + pinctrl-1 = <&sdmmc1_b4_od_pins_a>; + pinctrl-2 = <&sdmmc1_b4_sleep_pins_a>; + broken-cd; +- st,negedge; ++ st,neg-edge; + bus-width = <4>; + vmmc-supply = <&v3v3>; + status = "okay"; + }; + ++&spi4 { ++ pinctrl-names = "default", "sleep"; ++ pinctrl-0 = <&spi4_pins_a>; ++ pinctrl-1 = <&spi4_sleep_pins_a>; ++ status = "disabled"; ++}; ++ ++&spi5 { ++ pinctrl-names = "default", "sleep"; ++ pinctrl-0 = <&spi5_pins_a>; ++ pinctrl-1 = <&spi5_sleep_pins_a>; ++ status = "disabled"; ++}; ++ + &timers1 { + /* spare dmas for other usage */ + /delete-property/dmas; +@@ -404,6 +630,40 @@ + status = "okay"; + }; + ++&usart3 { ++ pinctrl-names = "default", "sleep", "idle"; ++ pinctrl-0 = <&usart3_pins_b>; ++ pinctrl-1 = <&usart3_sleep_pins_b>; ++ pinctrl-2 = <&usart3_idle_pins_b>; ++ status = "disabled"; ++}; ++ ++&usbh_ehci { ++ phys = <&usbphyc_port0>; ++ phy-names = "usb"; ++ status = "okay"; ++}; ++ ++&usbotg_hs { ++ dr_mode = "peripheral"; ++ phys = <&usbphyc_port1 0>; ++ phy-names = "usb2-phy"; ++ status = "okay"; ++}; ++ ++&usbphyc { ++ vdd3v3-supply = <&vdd_usb>; ++ status = "okay"; ++}; ++ ++&usbphyc_port0 { ++ st,phy-tuning = <&usb_phy_tuning>; ++}; ++ ++&usbphyc_port1 { ++ st,phy-tuning = <&usb_phy_tuning>; ++}; ++ + &vrefbuf { + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <2500000>; +diff --git a/arch/arm/boot/dts/stm32mp157c-dk2-m4-examples.dts b/arch/arm/boot/dts/stm32mp157c-dk2-m4-examples.dts +new file mode 100644 +index 0000000..8956b06 +--- /dev/null ++++ b/arch/arm/boot/dts/stm32mp157c-dk2-m4-examples.dts +@@ -0,0 +1,156 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) ++/* ++ * Copyright (C) STMicroelectronics 2017 - All Rights Reserved ++ * Author: Alexandre Torgue for STMicroelectronics. ++ */ ++ ++/dts-v1/; ++ ++#include "stm32mp157c-dk2.dts" ++ ++/ { ++ model = "STMicroelectronics STM32MP157C-DK2 configured to run M4 examples"; ++ compatible = "st,stm32mp157c-dk2-m4-examples", "st,stm32mp157c-dk2", "st,stm32mp157"; ++}; ++ ++&adc { ++ status = "disabled"; ++}; ++ ++&dac { ++ status = "disabled"; ++}; ++ ++&dma2 { ++ status = "disabled"; ++}; ++ ++&dmamux1 { ++ dma-masters = <&dma1>; ++ dma-channels = <8>; ++}; ++ ++&m4_adc { ++ vref-supply = <&vrefbuf>; ++ status = "okay"; ++}; ++ ++&m4_dac { ++ status = "okay"; ++}; ++ ++&m4_dma2 { ++ status = "okay"; ++}; ++ ++&m4_crc2 { ++ status = "okay"; ++}; ++ ++&m4_cryp2 { ++ status = "okay"; ++}; ++ ++&m4_hash2 { ++ status = "okay"; ++}; ++ ++&m4_i2c5 { ++ pinctrl-names = "rproc_default"; ++ pinctrl-0 = <&i2c5_pins_a>; ++ status = "okay"; ++}; ++ ++&m4_rng2 { ++ status = "okay"; ++}; ++ ++&m4_rproc { ++ m4_system_resources { ++ status = "okay"; ++ ++ button { ++ compatible = "rproc-srm-dev"; ++ interrupt-parent = <&gpioa>; ++ interrupts = <14 2>; ++ interrupt-names = "irq"; ++ status = "okay"; ++ }; ++ ++ m4_led: m4_led { ++ compatible = "rproc-srm-dev"; ++ pinctrl-names = "rproc_default", "rproc_sleep"; ++ pinctrl-0 = <&leds_orange_pins>; ++ pinctrl-1 = <&leds_orange_sleep_pins>; ++ status = "okay"; ++ }; ++ }; ++}; ++ ++&m4_spi4 { ++ pinctrl-names = "rproc_default"; ++ pinctrl-0 = <&spi4_pins_a>; ++ status = "okay"; ++}; ++ ++ ++&m4_timers2 { ++ pinctrl-names = "rproc_default"; ++ status = "okay"; ++}; ++ ++&m4_timers1 { ++ pinctrl-names = "rproc_default"; ++ pinctrl-0 = <&timer1_pins>; ++ status = "okay"; ++}; ++ ++&m4_uart7 { ++ pinctrl-names = "rproc_default"; ++ pinctrl-0 = <&uart7_pins>; ++ status = "okay"; ++}; ++ ++&pinctrl { ++ uart7_pins: uart7-test-0 { ++ pins1 { ++ pinmux = ; /* UART7_TX */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <0>; ++ }; ++ pins2 { ++ pinmux = ; /* UART7_RX */ ++ bias-disable; ++ }; ++ }; ++ ++ timer1_pins: pwm1-test-0 { ++ pins { ++ pinmux = ; /* TIM1_CH1 */ ++ bias-pull-down; ++ drive-push-pull; ++ slew-rate = <0>; ++ }; ++ }; ++ ++ leds_orange_pins: leds_orange_test-0 { ++ pins { ++ pinmux = ; ++ bias-pull-up; ++ drive-push-pull; ++ output-low; ++ slew-rate = <0>; ++ }; ++ }; ++ ++ leds_orange_sleep_pins: leds_orange_sleep_test-0 { ++ pins { ++ pinmux = ; ++ }; ++ }; ++}; ++ ++&timers1 { ++ status = "disabled"; ++}; +diff --git a/arch/arm/boot/dts/stm32mp157c-dk2.dts b/arch/arm/boot/dts/stm32mp157c-dk2.dts +index 4175b65..fd00386 100644 +--- a/arch/arm/boot/dts/stm32mp157c-dk2.dts ++++ b/arch/arm/boot/dts/stm32mp157c-dk2.dts +@@ -12,6 +12,80 @@ + / { + model = "STMicroelectronics STM32MP157C-DK2 Discovery Board"; + compatible = "st,stm32mp157c-dk2", "st,stm32mp157"; ++ ++ aliases { ++ serial1 = &usart2; ++ }; ++ ++ wifi_pwrseq: wifi-pwrseq { ++ compatible = "mmc-pwrseq-simple"; ++ reset-gpios = <&gpioh 4 GPIO_ACTIVE_LOW>; ++ }; ++}; ++ ++&dsi { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ reg = <0>; ++ dsi_in: endpoint { ++ remote-endpoint = <<dc_ep1_out>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ dsi_out: endpoint { ++ remote-endpoint = <&panel_in>; ++ }; ++ }; ++ }; ++ ++ panel@0 { ++ compatible = "orisetech,otm8009a"; ++ reg = <0>; ++ reset-gpios = <&gpioe 4 GPIO_ACTIVE_LOW>; ++ status = "okay"; ++ ++ port { ++ panel_in: endpoint { ++ remote-endpoint = <&dsi_out>; ++ }; ++ }; ++ }; ++}; ++ ++&i2c1 { ++ touchscreen@2a { ++ compatible = "focaltech,ft6236"; ++ reg = <0x2a>; ++ interrupts = <2 2>; ++ interrupt-parent = <&gpiof>; ++ interrupt-controller; ++ touchscreen-size-x = <480>; ++ touchscreen-size-y = <800>; ++ status = "okay"; ++ }; ++}; ++ ++<dc { ++ status = "okay"; ++ ++ port { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ ltdc_ep1_out: endpoint@1 { ++ reg = <1>; ++ remote-endpoint = <&dsi_in>; ++ }; ++ }; + }; + + &rtc { +@@ -20,3 +94,41 @@ + pinctrl-names = "default"; + }; + ++/* Wifi */ ++&sdmmc2 { ++ pinctrl-names = "default", "opendrain", "sleep"; ++ pinctrl-0 = <&sdmmc2_b4_pins_a>; ++ pinctrl-1 = <&sdmmc2_b4_od_pins_a>; ++ pinctrl-2 = <&sdmmc2_b4_sleep_pins_a>; ++ non-removable; ++ st,neg-edge; ++ bus-width = <4>; ++ vmmc-supply = <&v3v3>; ++ mmc-pwrseq = <&wifi_pwrseq>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ keep-power-in-suspend; ++ status = "okay"; ++ ++ brcmf: bcrmf@1 { ++ reg = <1>; ++ compatible = "brcm,bcm4329-fmac"; ++ }; ++}; ++ ++/* Bluetooth */ ++&usart2 { ++ pinctrl-names = "default", "sleep", "idle"; ++ pinctrl-0 = <&usart2_pins_a>; ++ pinctrl-1 = <&usart2_sleep_pins_a>; ++ pinctrl-2 = <&usart2_idle_pins_a>; ++ st,hw-flow-ctrl; ++ status = "okay"; ++ ++ bluetooth { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&btreg>; ++ compatible = "brcm,bcm43438-bt"; ++ max-speed = <3000000>; ++ }; ++}; +diff --git a/arch/arm/boot/dts/stm32mp157c-ed1.dts b/arch/arm/boot/dts/stm32mp157c-ed1.dts +index 1b074e9..4f46b41 100644 +--- a/arch/arm/boot/dts/stm32mp157c-ed1.dts ++++ b/arch/arm/boot/dts/stm32mp157c-ed1.dts +@@ -33,6 +33,11 @@ + reg = <0x10040000 0x10000>; + no-map; + }; ++ ++ gpu_reserved: gpu@f8000000 { ++ reg = <0xf8000000 0x8000000>; ++ no-map; ++ }; + }; + + aliases { +@@ -70,6 +75,8 @@ + + &adc { + /* ANA0, ANA1 are dedicated pins and don't need pinctrl: only in6. */ ++ vdd-supply = <&vdd>; ++ vdda-supply = <&vdda>; + vref-supply = <&vdda>; + status = "okay"; + adc1: adc@0 { +@@ -114,6 +121,15 @@ + sram = <&dma_pool>; + }; + ++&gpu { ++ contiguous-area = <&gpu_reserved>; ++ status = "okay"; ++}; ++ ++&hsem { ++ status = "okay"; ++}; ++ + &i2c4 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&i2c4_pins_a>; +@@ -127,8 +143,7 @@ + pmic: stpmic@33 { + compatible = "st,stpmic1"; + reg = <0x33>; +- interrupts-extended = <&pwr 0 IRQ_TYPE_EDGE_FALLING 1>, +- <&exti 55 1>; ++ interrupts-extended = <&exti_pwr 55 IRQ_TYPE_EDGE_FALLING>; + interrupt-controller; + #interrupt-cells = <2>; + status = "okay"; +@@ -235,10 +250,10 @@ + regulator-over-current-protection; + }; + +- bst_out: boost { ++ bst_out: boost { + regulator-name = "bst_out"; + interrupts = ; +- }; ++ }; + + vbus_otg: pwr_sw1 { + regulator-name = "vbus_otg"; +@@ -278,7 +293,7 @@ + &m4_rproc { + memory-region = <&ipc_share>; + mboxes = <&ipcc 0>, <&ipcc 1>, <&ipcc 2>; +- mbox-names = "vq0", "vq1", "init_shdn"; ++ mbox-names = "vq0", "vq1", "shutdown"; + interrupt-parent = <&exti>; + interrupts = <68 1>; + interrupt-names = "wdg"; +@@ -286,6 +301,10 @@ + status = "okay"; + }; + ++&pwr { ++ pwr-supply = <&vdd>; ++}; ++ + &rng1 { + status = "okay"; + }; +@@ -316,7 +335,7 @@ + non-removable; + no-sd; + no-sdio; +- st,negedge; ++ st,neg-edge; + bus-width = <8>; + vmmc-supply = <&v3v3>; + vqmmc-supply = <&v3v3>; +@@ -342,14 +361,10 @@ + status = "okay"; + }; + +-&usbphyc_port0 { +- phy-supply = <&vdd_usb>; +- vdda1v1-supply = <®11>; +- vdda1v8-supply = <®18>; ++&usbotg_hs { ++ vbus-supply = <&vbus_otg>; + }; + +-&usbphyc_port1 { +- phy-supply = <&vdd_usb>; +- vdda1v1-supply = <®11>; +- vdda1v8-supply = <®18>; ++&usbphyc { ++ vdd3v3-supply = <&vdd_usb>; + }; +diff --git a/arch/arm/boot/dts/stm32mp157c-ev1-m4-examples.dts b/arch/arm/boot/dts/stm32mp157c-ev1-m4-examples.dts +new file mode 100644 +index 0000000..ef2938d +--- /dev/null ++++ b/arch/arm/boot/dts/stm32mp157c-ev1-m4-examples.dts +@@ -0,0 +1,161 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) ++/* ++ * Copyright (C) STMicroelectronics 2017 - All Rights Reserved ++ * Author: Alexandre Torgue for STMicroelectronics. ++ */ ++ ++/dts-v1/; ++ ++#include "stm32mp157c-ev1.dts" ++ ++/ { ++ model = "STMicroelectronics STM32MP157C-EV1 configured to run M4 examples"; ++ compatible = "st,stm32mp157c-ev1-m4-examples", "st,stm32mp157c-ev1", "st,stm32mp157c-ed1", "st,stm32mp157"; ++}; ++ ++&adc { ++ status = "disabled"; ++}; ++ ++&dac { ++ status = "disabled"; ++}; ++ ++&dcmi { ++ status = "disabled"; ++}; ++ ++&dma2 { ++ status = "disabled"; ++}; ++ ++&dmamux1 { ++ dma-masters = <&dma1>; ++ dma-channels = <8>; ++}; ++ ++&fmc { ++ status = "disabled"; ++}; ++ ++&i2c5 { ++ status = "disabled"; ++}; ++ ++&m4_adc { ++ vref-supply = <&vdda>; ++ status = "okay"; ++}; ++ ++&m4_crc2 { ++ status = "okay"; ++}; ++ ++&m4_cryp2 { ++ status = "okay"; ++}; ++ ++&m4_dac { ++ status = "okay"; ++}; ++ ++&m4_dma2 { ++ status = "okay"; ++}; ++ ++&m4_hash2 { ++ status = "okay"; ++}; ++ ++&m4_i2c5 { ++ pinctrl-names = "rproc_default"; ++ pinctrl-0 = <&i2c5_pins_a>; ++ status = "okay"; ++}; ++ ++&m4_qspi { ++ pinctrl-names = "rproc_default"; ++ pinctrl-0 = <&qspi_clk_pins_a &qspi_bk1_pins_a &qspi_bk2_pins_a>; ++ status = "okay"; ++}; ++ ++&m4_rproc { ++ m4_system_resources { ++ status = "okay"; ++ ++ button { ++ compatible = "rproc-srm-dev"; ++ interrupt-parent = <&gpioa>; ++ interrupts = <14 2>; ++ interrupt-names = "irq"; ++ status = "okay"; ++ }; ++ ++ m4_led: m4_led { ++ compatible = "rproc-srm-dev"; ++ pinctrl-names = "rproc_default", "rproc_sleep"; ++ pinctrl-0 = <&leds_orange_pins>; ++ pinctrl-1 = <&leds_orange_sleep_pins>; ++ status = "okay"; ++ }; ++ }; ++}; ++ ++&m4_rng2 { ++ status = "okay"; ++}; ++ ++&m4_spi1 { ++ pinctrl-names = "rproc_default"; ++ pinctrl-0 = <&spi1_pins_a>; ++ status = "okay"; ++}; ++ ++ ++&m4_timers2 { ++ pinctrl-names = "rproc_default"; ++ pinctrl-0 = <&pwm2_pins_a>; ++ status = "okay"; ++}; ++ ++&m4_timers1 { ++ pinctrl-names = "rproc_default"; ++ pinctrl-0 = <&pwm1_pins_a>; ++ status = "okay"; ++}; ++ ++&m4_usart3 { ++ pinctrl-names = "rproc_default"; ++ pinctrl-0 = <&usart3_pins_a>; ++ status = "okay"; ++}; ++ ++&pinctrl { ++ leds_orange_pins: leds_orange_test-0 { ++ pins { ++ pinmux = ; ++ bias-pull-up; ++ drive-push-pull; ++ output-low; ++ slew-rate = <0>; ++ }; ++ }; ++ ++ leds_orange_sleep_pins: leds_orange_sleep_test-0 { ++ pins { ++ pinmux = ; ++ }; ++ }; ++}; ++ ++&qspi { ++ status = "disabled"; ++}; ++ ++&sai2b { ++ status = "disabled"; ++}; ++ ++&timers2 { ++ status = "disabled"; ++}; +diff --git a/arch/arm/boot/dts/stm32mp157c-ev1.dts b/arch/arm/boot/dts/stm32mp157c-ev1.dts +index fe59c6d..b60bffc 100644 +--- a/arch/arm/boot/dts/stm32mp157c-ev1.dts ++++ b/arch/arm/boot/dts/stm32mp157c-ev1.dts +@@ -7,6 +7,7 @@ + + #include "stm32mp157c-ed1.dts" + #include ++#include + + / { + model = "STMicroelectronics STM32MP157C eval daughter on eval mother"; +@@ -21,6 +22,14 @@ + ethernet0 = ðernet0; + }; + ++ clocks { ++ clk_ext_camera: clk-ext-camera { ++ #clock-cells = <0>; ++ compatible = "fixed-clock"; ++ clock-frequency = <24000000>; ++ }; ++ }; ++ + joystick { + compatible = "gpio-keys"; + #size-cells = <0>; +@@ -57,6 +66,284 @@ + interrupts = <4 IRQ_TYPE_EDGE_RISING>; + }; + }; ++ ++ panel_backlight: panel-backlight { ++ compatible = "gpio-backlight"; ++ gpios = <&gpiod 13 GPIO_ACTIVE_LOW>; ++ default-on; ++ status = "okay"; ++ }; ++ ++ spdif_out: spdif-out { ++ #sound-dai-cells = <0>; ++ compatible = "linux,spdif-dit"; ++ status = "okay"; ++ ++ spdif_out_port: port { ++ spdif_out_endpoint: endpoint { ++ remote-endpoint = <&sai4a_endpoint>; ++ }; ++ }; ++ }; ++ ++ spdif_in: spdif-in { ++ #sound-dai-cells = <0>; ++ compatible = "linux,spdif-dir"; ++ status = "okay"; ++ ++ spdif_in_port: port { ++ spdif_in_endpoint: endpoint { ++ remote-endpoint = <&spdifrx_endpoint>; ++ }; ++ }; ++ }; ++ ++ sound { ++ compatible = "audio-graph-card"; ++ routing = ++ "AIF1CLK" , "MCLK1", ++ "AIF2CLK" , "MCLK1", ++ "IN1LN" , "MICBIAS2", ++ "DMIC2DAT" , "MICBIAS1", ++ "DMIC1DAT" , "MICBIAS1"; ++ dais = <&sai2a_port &sai2b_port &sai4a_port &spdifrx_port>; ++ status = "okay"; ++ }; ++ ++ dmics { ++ compatible = "audio-graph-card"; ++ dais = <&cpu_port0 &cpu_port1 &cpu_port2 &cpu_port3>; ++ status = "okay"; ++ }; ++ ++ dmic0: dmic-0 { ++ compatible = "dmic-codec"; ++ #sound-dai-cells = <1>; ++ status = "okay"; ++ ++ port { ++ dmic0_endpoint: endpoint { ++ remote-endpoint = <&dfsdm_endpoint0>; ++ }; ++ }; ++ }; ++ ++ dmic1: dmic-1 { ++ compatible = "dmic-codec"; ++ #sound-dai-cells = <1>; ++ status = "okay"; ++ ++ port { ++ dmic1_endpoint: endpoint { ++ remote-endpoint = <&dfsdm_endpoint1>; ++ }; ++ }; ++ }; ++ ++ dmic2: dmic-2 { ++ compatible = "dmic-codec"; ++ #sound-dai-cells = <1>; ++ status = "okay"; ++ ++ port { ++ dmic2_endpoint: endpoint { ++ remote-endpoint = <&dfsdm_endpoint2>; ++ }; ++ }; ++ }; ++ ++ dmic3: dmic-3 { ++ compatible = "dmic-codec"; ++ #sound-dai-cells = <1>; ++ status = "okay"; ++ ++ port { ++ dmic3_endpoint: endpoint { ++ remote-endpoint = <&dfsdm_endpoint3>; ++ }; ++ }; ++ }; ++ ++ usb_phy_tuning: usb-phy-tuning { ++ st,hs-dc-level = <2>; ++ st,fs-rftime-tuning; ++ st,hs-rftime-reduction; ++ st,hs-current-trim = <15>; ++ st,hs-impedance-trim = <1>; ++ st,squelch-level = <3>; ++ st,hs-rx-offset = <2>; ++ st,no-lsfs-sc; ++ }; ++}; ++ ++&cec { ++ pinctrl-names = "default", "sleep"; ++ pinctrl-0 = <&cec_pins_a>; ++ pinctrl-1 = <&cec_pins_sleep_a>; ++ status = "okay"; ++}; ++ ++&dcmi { ++ status = "okay"; ++ pinctrl-names = "default", "sleep"; ++ pinctrl-0 = <&dcmi_pins_a>; ++ pinctrl-1 = <&dcmi_sleep_pins_a>; ++ ++ port { ++ dcmi_0: endpoint { ++ remote-endpoint = <&ov5640_0>; ++ bus-width = <8>; ++ hsync-active = <0>; ++ vsync-active = <0>; ++ pclk-sample = <1>; ++ pclk-max-frequency = <77000000>; ++ }; ++ }; ++}; ++ ++&dfsdm { ++ pinctrl-names = "default", "sleep"; ++ pinctrl-0 = <&dfsdm_clkout_pins_a ++ &dfsdm_data1_pins_a &dfsdm_data3_pins_a>; ++ pinctrl-1 = <&dfsdm_clkout_sleep_pins_a ++ &dfsdm_data1_sleep_pins_a &dfsdm_data3_sleep_pins_a>; ++ spi-max-frequency = <2048000>; ++ ++ clocks = <&rcc DFSDM_K>, <&rcc ADFSDM_K>; ++ clock-names = "dfsdm", "audio"; ++ status = "okay"; ++ ++ dfsdm0: filter@0 { ++ compatible = "st,stm32-dfsdm-dmic"; ++ st,adc-channels = <3>; ++ st,adc-channel-names = "dmic_u1"; ++ st,adc-channel-types = "SPI_R"; ++ st,adc-channel-clk-src = "CLKOUT"; ++ st,filter-order = <3>; ++ status = "okay"; ++ ++ asoc_pdm0: dfsdm-dai { ++ compatible = "st,stm32h7-dfsdm-dai"; ++ #sound-dai-cells = <0>; ++ io-channels = <&dfsdm0 0>; ++ status = "okay"; ++ ++ cpu_port0: port { ++ dfsdm_endpoint0: endpoint { ++ remote-endpoint = <&dmic0_endpoint>; ++ }; ++ }; ++ }; ++ }; ++ ++ dfsdm1: filter@1 { ++ compatible = "st,stm32-dfsdm-dmic"; ++ st,adc-channels = <1>; ++ st,adc-channel-names = "dmic_u2"; ++ st,adc-channel-types = "SPI_F"; ++ st,adc-channel-clk-src = "CLKOUT"; ++ st,filter-order = <3>; ++ status = "okay"; ++ ++ asoc_pdm1: dfsdm-dai { ++ compatible = "st,stm32h7-dfsdm-dai"; ++ #sound-dai-cells = <0>; ++ io-channels = <&dfsdm1 0>; ++ status = "okay"; ++ ++ cpu_port1: port { ++ dfsdm_endpoint1: endpoint { ++ remote-endpoint = <&dmic1_endpoint>; ++ }; ++ }; ++ }; ++ }; ++ ++ dfsdm2: filter@2 { ++ compatible = "st,stm32-dfsdm-dmic"; ++ st,adc-channels = <3>; ++ st,adc-channel-names = "dmic_u3"; ++ st,adc-channel-types = "SPI_F"; ++ st,adc-channel-clk-src = "CLKOUT"; ++ st,filter-order = <3>; ++ status = "okay"; ++ ++ asoc_pdm2: dfsdm-dai { ++ compatible = "st,stm32h7-dfsdm-dai"; ++ #sound-dai-cells = <0>; ++ io-channels = <&dfsdm2 0>; ++ status = "okay"; ++ ++ cpu_port2: port { ++ dfsdm_endpoint2: endpoint { ++ remote-endpoint = <&dmic2_endpoint>; ++ }; ++ }; ++ }; ++ }; ++ ++ dfsdm3: filter@3 { ++ compatible = "st,stm32-dfsdm-dmic"; ++ st,adc-channels = <1>; ++ st,adc-channel-names = "dmic_u4"; ++ st,adc-channel-types = "SPI_R"; ++ st,adc-channel-clk-src = "CLKOUT"; ++ st,filter-order = <3>; ++ status = "okay"; ++ ++ asoc_pdm3: dfsdm-dai { ++ compatible = "st,stm32h7-dfsdm-dai"; ++ #sound-dai-cells = <0>; ++ io-channels = <&dfsdm3 0>; ++ status = "okay"; ++ ++ cpu_port3: port { ++ dfsdm_endpoint3: endpoint { ++ remote-endpoint = <&dmic3_endpoint>; ++ }; ++ }; ++ }; ++ }; ++}; ++ ++&dsi { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "okay"; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ reg = <0>; ++ dsi_in: endpoint { ++ remote-endpoint = <<dc_ep0_out>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ dsi_out: endpoint { ++ remote-endpoint = <&dsi_panel_in>; ++ }; ++ }; ++ }; ++ ++ panel-dsi@0 { ++ compatible = "raydium,rm68200"; ++ reg = <0>; ++ reset-gpios = <&gpiof 15 GPIO_ACTIVE_LOW>; ++ power-supply = <&v1v8>; ++ backlight = <&panel_backlight>; ++ status = "okay"; ++ ++ port { ++ dsi_panel_in: endpoint { ++ remote-endpoint = <&dsi_out>; ++ }; ++ }; ++ }; + }; + + ðernet0 { +@@ -78,12 +365,6 @@ + }; + }; + +-&cec { +- pinctrl-names = "default"; +- pinctrl-0 = <&cec_pins_a>; +- status = "okay"; +-}; +- + &fmc { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&fmc_pins_a>; +@@ -110,6 +391,74 @@ + /delete-property/dmas; + /delete-property/dma-names; + ++ wm8994: wm8994@1b { ++ compatible = "wlf,wm8994"; ++ #sound-dai-cells = <0>; ++ reg = <0x1b>; ++ status = "okay"; ++ ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ DBVDD-supply = <&vdd>; ++ SPKVDD1-supply = <&vdd>; ++ SPKVDD2-supply = <&vdd>; ++ AVDD2-supply = <&v1v8>; ++ CPVDD-supply = <&v1v8>; ++ ++ wlf,ldoena-always-driven; ++ ++ clocks = <&sai2a>; ++ clock-names = "MCLK1"; ++ ++ wlf,gpio-cfg = <0x8101 0xa100 0xa100 0xa100 0xa101 0xa101 0xa100 0xa101 0xa101 0xa101 0xa101>; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ wm8994_tx_port: port@0 { ++ reg = <0>; ++ wm8994_tx_endpoint: endpoint { ++ remote-endpoint = <&sai2a_endpoint>; ++ }; ++ }; ++ ++ wm8994_rx_port: port@1 { ++ reg = <1>; ++ wm8994_rx_endpoint: endpoint { ++ remote-endpoint = <&sai2b_endpoint>; ++ }; ++ }; ++ }; ++ }; ++ ++ ov5640: camera@3c { ++ compatible = "ovti,ov5640"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&ov5640_pins>; ++ reg = <0x3c>; ++ clocks = <&clk_ext_camera>; ++ clock-names = "xclk"; ++ DOVDD-supply = <&v2v8>; ++ powerdown-gpios = <&stmfx_pinctrl 18 GPIO_ACTIVE_HIGH>; ++ reset-gpios = <&stmfx_pinctrl 19 GPIO_ACTIVE_LOW>; ++ rotation = <180>; ++ status = "okay"; ++ ++ port { ++ ov5640_0: endpoint { ++ remote-endpoint = <&dcmi_0>; ++ bus-width = <8>; ++ data-shift = <2>; /* lines 9:2 are used */ ++ hsync-active = <0>; ++ vsync-active = <0>; ++ pclk-sample = <1>; ++ pclk-max-frequency = <77000000>; ++ }; ++ }; ++ }; ++ + stmfx: stmfx@42 { + compatible = "st,stmfx-0300"; + reg = <0x42>; +@@ -124,12 +473,45 @@ + interrupt-controller; + #interrupt-cells = <2>; + gpio-ranges = <&stmfx_pinctrl 0 0 24>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&hog_pins>; ++ ++ hog_pins: hog { ++ pins = "gpio14"; ++ drive-push-pull; ++ bias-pull-down; ++ }; + + joystick_pins: joystick { + pins = "gpio0", "gpio1", "gpio2", "gpio3", "gpio4"; + drive-push-pull; + bias-pull-down; + }; ++ ++ ov5640_pins: camera { ++ pins = "agpio2", "agpio3"; /* stmfx pins 18 & 19 */ ++ drive-push-pull; ++ output-low; ++ }; ++ }; ++ }; ++ ++ gt9147: goodix_ts@5d { ++ compatible = "goodix,gt9147"; ++ reg = <0x5d>; ++ status = "okay"; ++ ++ irq-gpios = <&stmfx_pinctrl 14 GPIO_ACTIVE_HIGH>; ++ irq-flags = ; ++ }; ++}; ++ ++&i2c4 { ++ pmic: stpmic@33 { ++ regulators { ++ v1v8: ldo6 { ++ regulator-enable-ramp-delay = <300000>; ++ }; + }; + }; + }; +@@ -145,6 +527,20 @@ + /delete-property/dma-names; + }; + ++<dc { ++ status = "okay"; ++ ++ port { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ ltdc_ep0_out: endpoint@0 { ++ reg = <0>; ++ remote-endpoint = <&dsi_in>; ++ }; ++ }; ++}; ++ + &m_can1 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&m_can1_pins_a>; +@@ -180,9 +576,86 @@ + }; + }; + ++&sai2 { ++ clocks = <&rcc SAI2>, <&rcc PLL3_Q>, <&rcc PLL4_Q>; ++ pinctrl-names = "default", "sleep"; ++ pinctrl-0 = <&sai2a_pins_a>, <&sai2b_pins_a>; ++ pinctrl-1 = <&sai2a_sleep_pins_a>, <&sai2b_sleep_pins_a>; ++ clock-names = "pclk", "x8k", "x11k"; ++ status = "okay"; ++ ++ sai2a: audio-controller@4400b004 { ++ #clock-cells = <0>; ++ dma-names = "tx"; ++ clocks = <&rcc SAI2_K>; ++ clock-names = "sai_ck"; ++ status = "okay"; ++ ++ sai2a_port: port { ++ sai2a_endpoint: endpoint { ++ remote-endpoint = <&wm8994_tx_endpoint>; ++ format = "i2s"; ++ mclk-fs = <256>; ++ }; ++ }; ++ }; ++ ++ sai2b: audio-controller@4400b024 { ++ dma-names = "rx"; ++ clocks = <&rcc SAI2_K>, <&sai2a>; ++ clock-names = "sai_ck", "MCLK"; ++ status = "okay"; ++ ++ sai2b_port: port { ++ sai2b_endpoint: endpoint { ++ remote-endpoint = <&wm8994_rx_endpoint>; ++ format = "i2s"; ++ mclk-fs = <256>; ++ }; ++ }; ++ }; ++}; ++ ++&sai4 { ++ clocks = <&rcc SAI4>, <&rcc PLL3_Q>, <&rcc PLL4_Q>; ++ clock-names = "pclk", "x8k", "x11k"; ++ status = "okay"; ++ ++ sai4a: audio-controller@50027004 { ++ pinctrl-names = "default", "sleep"; ++ pinctrl-0 = <&sai4a_pins_a>; ++ pinctrl-1 = <&sai4a_sleep_pins_a>; ++ dma-names = "tx"; ++ clocks = <&rcc SAI4_K>; ++ clock-names = "sai_ck"; ++ st,iec60958; ++ status = "okay"; ++ ++ sai4a_port: port { ++ sai4a_endpoint: endpoint { ++ remote-endpoint = <&spdif_out_endpoint>; ++ }; ++ }; ++ }; ++}; ++ ++&spdifrx { ++ pinctrl-names = "default", "sleep"; ++ pinctrl-0 = <&spdifrx_pins_a>; ++ pinctrl-1 = <&spdifrx_sleep_pins_a>; ++ status = "okay"; ++ ++ spdifrx_port: port { ++ spdifrx_endpoint: endpoint { ++ remote-endpoint = <&spdif_in_endpoint>; ++ }; ++ }; ++}; ++ + &spi1 { +- pinctrl-names = "default"; ++ pinctrl-names = "default", "sleep"; + pinctrl-0 = <&spi1_pins_a>; ++ pinctrl-1 = <&spi1_sleep_pins_a>; + status = "disabled"; + }; + +@@ -243,11 +716,13 @@ + &usbh_ehci { + phys = <&usbphyc_port0>; + phy-names = "usb"; ++ vbus-supply = <&vbus_sw>; + status = "okay"; + }; + + &usbotg_hs { +- dr_mode = "peripheral"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&usbotg_hs_pins_a>; + phys = <&usbphyc_port1 0>; + phy-names = "usb2-phy"; + status = "okay"; +@@ -256,3 +731,11 @@ + &usbphyc { + status = "okay"; + }; ++ ++&usbphyc_port0 { ++ st,phy-tuning = <&usb_phy_tuning>; ++}; ++ ++&usbphyc_port1 { ++ st,phy-tuning = <&usb_phy_tuning>; ++}; +diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi +index b4bae4d..cef7b3e 100644 +--- a/arch/arm/boot/dts/stm32mp157c.dtsi ++++ b/arch/arm/boot/dts/stm32mp157c.dtsi +@@ -19,15 +19,25 @@ + compatible = "arm,cortex-a7"; + device_type = "cpu"; + reg = <0>; ++ clock-frequency = <650000000>; + }; + + cpu1: cpu@1 { + compatible = "arm,cortex-a7"; + device_type = "cpu"; + reg = <1>; ++ clock-frequency = <650000000>; + }; + }; + ++ arm-pmu { ++ compatible = "arm,cortex-a7-pmu"; ++ interrupts = , ++ ; ++ interrupt-affinity = <&cpu0>, <&cpu1>; ++ interrupt-parent = <&intc>; ++ }; ++ + psci { + compatible = "arm,psci-1.0"; + method = "smc"; +@@ -80,6 +90,18 @@ + compatible = "fixed-clock"; + clock-frequency = <4000000>; + }; ++ ++ clk_i2s_ckin: i2s_ckin { ++ #clock-cells = <0>; ++ compatible = "fixed-clock"; ++ clock-frequency = <0>; ++ }; ++ ++ clk_dsi_phy: ck_dsi_phy { ++ #clock-cells = <0>; ++ compatible = "fixed-clock"; ++ clock-frequency = <0>; ++ }; + }; + + pm_domain { +@@ -102,6 +124,38 @@ + }; + }; + ++ thermal-zones { ++ cpu_thermal: cpu-thermal { ++ polling-delay-passive = <0>; ++ polling-delay = <0>; ++ thermal-sensors = <&dts>; ++ ++ trips { ++ cpu_alert1: cpu-alert1 { ++ temperature = <85000>; ++ hysteresis = <0>; ++ type = "passive"; ++ }; ++ ++ cpu-crit { ++ temperature = <120000>; ++ hysteresis = <0>; ++ type = "critical"; ++ }; ++ }; ++ ++ cooling-maps { ++ }; ++ }; ++ }; ++ ++ reboot { ++ compatible = "syscon-reboot"; ++ regmap = <&rcc>; ++ offset = <0x404>; ++ mask = <0x1>; ++ }; ++ + soc { + compatible = "simple-bus"; + #address-cells = <1>; +@@ -360,6 +414,17 @@ + status = "disabled"; + }; + ++ i2s2: audio-controller@4000b000 { ++ compatible = "st,stm32h7-i2s"; ++ #sound-dai-cells = <0>; ++ reg = <0x4000b000 0x400>; ++ interrupts = ; ++ dmas = <&dmamux1 39 0x400 0x01>, ++ <&dmamux1 40 0x400 0x01>; ++ dma-names = "rx", "tx"; ++ status = "disabled"; ++ }; ++ + spi3: spi@4000c000 { + #address-cells = <1>; + #size-cells = <0>; +@@ -375,6 +440,30 @@ + status = "disabled"; + }; + ++ i2s3: audio-controller@4000c000 { ++ compatible = "st,stm32h7-i2s"; ++ #sound-dai-cells = <0>; ++ reg = <0x4000c000 0x400>; ++ interrupts = ; ++ dmas = <&dmamux1 61 0x400 0x01>, ++ <&dmamux1 62 0x400 0x01>; ++ dma-names = "rx", "tx"; ++ status = "disabled"; ++ }; ++ ++ spdifrx: audio-controller@4000d000 { ++ compatible = "st,stm32h7-spdifrx"; ++ #sound-dai-cells = <0>; ++ reg = <0x4000d000 0x400>; ++ clocks = <&rcc SPDIF_K>; ++ clock-names = "kclk"; ++ interrupts = ; ++ dmas = <&dmamux1 93 0x400 0x01>, ++ <&dmamux1 94 0x400 0x01>; ++ dma-names = "rx", "rx-ctrl"; ++ status = "disabled"; ++ }; ++ + usart2: serial@4000e000 { + compatible = "st,stm32h7-uart"; + reg = <0x4000e000 0x400>; +@@ -644,6 +733,17 @@ + status = "disabled"; + }; + ++ i2s1: audio-controller@44004000 { ++ compatible = "st,stm32h7-i2s"; ++ #sound-dai-cells = <0>; ++ reg = <0x44004000 0x400>; ++ interrupts = ; ++ dmas = <&dmamux1 37 0x400 0x01>, ++ <&dmamux1 38 0x400 0x01>; ++ dma-names = "rx", "tx"; ++ status = "disabled"; ++ }; ++ + spi4: spi@44005000 { + #address-cells = <1>; + #size-cells = <0>; +@@ -747,6 +847,88 @@ + status = "disabled"; + }; + ++ sai1: sai@4400a000 { ++ compatible = "st,stm32h7-sai"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0 0x4400a000 0x400>; ++ reg = <0x4400a000 0x4>; ++ interrupts = ; ++ resets = <&rcc SAI1_R>; ++ status = "disabled"; ++ ++ sai1a: audio-controller@4400a004 { ++ #sound-dai-cells = <0>; ++ ++ compatible = "st,stm32-sai-sub-a"; ++ reg = <0x4 0x1c>; ++ dmas = <&dmamux1 87 0x400 0x01>; ++ status = "disabled"; ++ }; ++ ++ sai1b: audio-controller@4400a024 { ++ #sound-dai-cells = <0>; ++ compatible = "st,stm32-sai-sub-b"; ++ reg = <0x24 0x1c>; ++ dmas = <&dmamux1 88 0x400 0x01>; ++ status = "disabled"; ++ }; ++ }; ++ ++ sai2: sai@4400b000 { ++ compatible = "st,stm32h7-sai"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0 0x4400b000 0x400>; ++ reg = <0x4400b000 0x4>; ++ interrupts = ; ++ resets = <&rcc SAI2_R>; ++ status = "disabled"; ++ ++ sai2a: audio-controller@4400b004 { ++ #sound-dai-cells = <0>; ++ compatible = "st,stm32-sai-sub-a"; ++ reg = <0x4 0x1c>; ++ dmas = <&dmamux1 89 0x400 0x01>; ++ status = "disabled"; ++ }; ++ ++ sai2b: audio-controller@4400b024 { ++ #sound-dai-cells = <0>; ++ compatible = "st,stm32-sai-sub-b"; ++ reg = <0x24 0x1c>; ++ dmas = <&dmamux1 90 0x400 0x01>; ++ status = "disabled"; ++ }; ++ }; ++ ++ sai3: sai@4400c000 { ++ compatible = "st,stm32h7-sai"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0 0x4400c000 0x400>; ++ reg = <0x4400c000 0x4>; ++ interrupts = ; ++ resets = <&rcc SAI3_R>; ++ status = "disabled"; ++ ++ sai3a: audio-controller@4400c004 { ++ #sound-dai-cells = <0>; ++ compatible = "st,stm32-sai-sub-a"; ++ reg = <0x04 0x1c>; ++ dmas = <&dmamux1 113 0x400 0x01>; ++ status = "disabled"; ++ }; ++ ++ sai3b: audio-controller@4400c024 { ++ #sound-dai-cells = <0>; ++ compatible = "st,stm32-sai-sub-b"; ++ reg = <0x24 0x1c>; ++ dmas = <&dmamux1 114 0x400 0x01>; ++ status = "disabled"; ++ }; ++ }; ++ + dfsdm: dfsdm@4400d000 { + compatible = "st,stm32mp1-dfsdm"; + reg = <0x4400d000 0x800>; +@@ -913,6 +1095,10 @@ + clocks = <&rcc ADC12>, <&rcc ADC12_K>; + clock-names = "bus", "adc"; + interrupt-controller; ++ st,syscfg-vbooster = <&syscfg 0x4 0x100>; ++ st,syscfg-vbooster-clr = <&syscfg 0x44 0x100>; ++ st,syscfg-anaswvdd = <&syscfg 0x4 0x200>; ++ st,syscfg-anaswvdd-clr = <&syscfg 0x44 0x200>; + #interrupt-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; +@@ -993,7 +1179,7 @@ + }; + + usbotg_hs: usb-otg@49000000 { +- compatible = "snps,dwc2"; ++ compatible = "st,stm32mp1-hsotg", "snps,dwc2"; + reg = <0x49000000 0x10000>; + clocks = <&rcc USBO_K>; + clock-names = "otg"; +@@ -1004,10 +1190,20 @@ + g-np-tx-fifo-size = <32>; + g-tx-fifo-size = <128 128 64 64 64 64 32 32>; + dr_mode = "otg"; ++ usb33d-supply = <&usb33>; + power-domains = <&pd_core>; + status = "disabled"; + }; + ++ hsem: hwspinlock@4c000000 { ++ compatible = "st,stm32-hwspinlock"; ++ #hwlock-cells = <1>; ++ reg = <0x4c000000 0x400>; ++ clocks = <&rcc HSEM>; ++ clock-names = "hsem"; ++ status = "disabled"; ++ }; ++ + ipcc: mailbox@4c001000 { + compatible = "st,stm32mp1-ipcc"; + #mbox-cells = <1>; +@@ -1024,13 +1220,24 @@ + status = "disabled"; + }; + ++ dcmi: dcmi@4c006000 { ++ compatible = "st,stm32-dcmi"; ++ reg = <0x4c006000 0x400>; ++ interrupts = ; ++ resets = <&rcc CAMITF_R>; ++ clocks = <&rcc DCMI>; ++ clock-names = "mclk"; ++ dmas = <&dmamux1 75 0x400 0xd>; ++ dma-names = "tx"; ++ status = "disabled"; ++ }; ++ + rcc: rcc@50000000 { + compatible = "st,stm32mp1-rcc", "syscon"; + reg = <0x50000000 0x1000>; + #clock-cells = <1>; + #reset-cells = <1>; + interrupts = ; +- st,pwr = <&pwr>; + }; + + pwr: pwr@50001000 { +@@ -1071,11 +1278,24 @@ + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x5000d000 0x400>; ++ hwlocks = <&hsem 2>; ++ ++ /* exti_pwr is an extra interrupt controller used for ++ * EXTI 55 to 60. It's mapped on pwr interrupt ++ * controller. ++ */ ++ exti_pwr: exti-pwr { ++ interrupt-controller; ++ #interrupt-cells = <2>; ++ interrupt-parent = <&pwr>; ++ st,irq-number = <6>; ++ }; + }; + + syscfg: syscon@50020000 { + compatible = "st,stm32mp157-syscfg", "syscon"; + reg = <0x50020000 0x400>; ++ clocks = <&rcc SYSCFG>; + }; + + lptimer2: timer@50021000 { +@@ -1168,6 +1388,45 @@ + status = "disabled"; + }; + ++ sai4: sai@50027000 { ++ compatible = "st,stm32h7-sai"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0 0x50027000 0x400>; ++ reg = <0x50027000 0x4>; ++ interrupts = ; ++ resets = <&rcc SAI4_R>; ++ status = "disabled"; ++ ++ sai4a: audio-controller@50027004 { ++ #sound-dai-cells = <0>; ++ compatible = "st,stm32-sai-sub-a"; ++ reg = <0x04 0x1c>; ++ clocks = <&rcc SAI4_K>; ++ clock-names = "sai_ck"; ++ dmas = <&dmamux1 99 0x400 0x01>; ++ status = "disabled"; ++ }; ++ ++ sai4b: audio-controller@50027024 { ++ #sound-dai-cells = <0>; ++ compatible = "st,stm32-sai-sub-b"; ++ reg = <0x24 0x1c>; ++ dmas = <&dmamux1 100 0x400 0x01>; ++ status = "disabled"; ++ }; ++ }; ++ ++ dts: thermal@50028000 { ++ compatible = "st,stm32-thermal"; ++ reg = <0x50028000 0x100>; ++ interrupts = ; ++ clocks = <&rcc TMPSENS>; ++ clock-names = "pclk"; ++ #thermal-sensor-cells = <0>; ++ status = "disabled"; ++ }; ++ + cryp1: cryp@54001000 { + compatible = "st,stm32mp1-cryp"; + reg = <0x54001000 0x400>; +@@ -1327,6 +1586,16 @@ + status = "disabled"; + }; + ++ gpu: gpu@59000000 { ++ compatible = "vivante,gc"; ++ reg = <0x59000000 0x800>; ++ interrupts = ; ++ clocks = <&rcc GPU>, <&rcc GPU_K>; ++ clock-names = "bus" ,"core"; ++ resets = <&rcc GPU_R>; ++ status = "disabled"; ++ }; ++ + dsi: dsi@5a000000 { + compatible = "st,stm32-dsi"; + reg = <0x5a000000 0x800>; +@@ -1334,6 +1603,7 @@ + clock-names = "pclk", "ref", "px_clk"; + resets = <&rcc DSI_R>; + reset-names = "apb"; ++ phy-dsi-supply = <®18>; + status = "disabled"; + }; + +@@ -1359,10 +1629,13 @@ + usbphyc: usbphyc@5a006000 { + #address-cells = <1>; + #size-cells = <0>; ++ #clock-cells = <0>; + compatible = "st,stm32mp1-usbphyc"; + reg = <0x5a006000 0x1000>; + clocks = <&rcc USBPHY_K>; + resets = <&rcc USBPHY_R>; ++ vdda1v1-supply = <®11>; ++ vdda1v8-supply = <®18>; + status = "disabled"; + + usbphyc_port0: usb-phy@0 { +@@ -1500,4 +1773,11 @@ + status = "disabled"; + }; + }; ++ ++ firmware { ++ optee { ++ compatible = "linaro,optee-tz"; ++ method = "smc"; ++ }; ++ }; + }; +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0031-ARM-stm32mp1-r0-rc2-DEFCONFIG.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0031-ARM-stm32mp1-r0-rc2-DEFCONFIG.patch new file mode 100644 index 0000000..a10a14f --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0031-ARM-stm32mp1-r0-rc2-DEFCONFIG.patch @@ -0,0 +1,130 @@ +From 73813749b7f0f33a59eb8f321f7d0f9d88dbfbb3 Mon Sep 17 00:00:00 2001 +From: Christophe Priouzeau +Date: Mon, 26 Nov 2018 14:46:52 +0100 +Subject: [PATCH 31/52] ARM-stm32mp1-r0-rc2-DEFCONFIG + +--- + arch/arm/configs/fragment-02-multiv7_addons.config | 42 +++++++++++++++++++++- + 1 file changed, 41 insertions(+), 1 deletion(-) + +diff --git a/arch/arm/configs/fragment-02-multiv7_addons.config b/arch/arm/configs/fragment-02-multiv7_addons.config +index c91840c..4470d85 100644 +--- a/arch/arm/configs/fragment-02-multiv7_addons.config ++++ b/arch/arm/configs/fragment-02-multiv7_addons.config +@@ -3,6 +3,7 @@ + # + CONFIG_POSIX_MQUEUE=y + CONFIG_USELIB=y ++CONFIG_FUTEX=y + + # + # RCU Subsystem +@@ -153,6 +154,12 @@ CONFIG_SERIAL_NONSTANDARD=y + # CONFIG_SERIAL_8250 is not set + + # ++# Touchscreen drivers ++# ++CONFIG_TOUCHSCREEN_EDT_FT5X06=m ++CONFIG_TOUCHSCREEN_GOODIX=m ++ ++# + # Non-8250 serial port support + # + # CONFIG_SERIAL_BCM63XX is not set +@@ -179,6 +186,7 @@ CONFIG_SERIAL_NONSTANDARD=y + # + # SPI Master Controller Drivers + # ++CONFIG_SPI_STM32=y + CONFIG_SPI_STM32_QSPI=y + + # +@@ -237,6 +245,7 @@ CONFIG_PROTECTION_CONSUMER=y + # + # USB HDMI CEC adapters + # ++CONFIG_VIDEO_STM32_HDMI_CEC=m + + # + # Media ancillary drivers (tuners, sensors, i2c, spi, frontends) +@@ -246,6 +255,7 @@ CONFIG_PROTECTION_CONSUMER=y + # + # Camera sensor devices + # ++CONFIG_VIDEO_OV5640=y + + # + # Graphics support +@@ -261,10 +271,14 @@ CONFIG_PROTECTION_CONSUMER=y + # + # Display Panels + # ++CONFIG_DRM_PANEL_ORISETECH_OTM8009A=m ++CONFIG_DRM_PANEL_RAYDIUM_RM68200=m + + # + # Display Interface Bridges + # ++CONFIG_VIDEO_ADV7511=m ++CONFIG_DRM_SII902X=m + + # + # Frame buffer hardware drivers +@@ -273,15 +287,34 @@ CONFIG_PROTECTION_CONSUMER=y + # + # Console display driver support + # ++CONFIG_DRM_STM=m ++CONFIG_DRM_STM_DSI=m ++ ++# ++# Backlight support ++# ++CONFIG_BACKLIGHT_GPIO=y + + # + # HD-Audio + # ++CONFIG_SOUND=y ++CONFIG_SND=y ++CONFIG_SND_SOC=y ++CONFIG_SND_SOC_STM32_SAI=y ++CONFIG_SND_SOC_STM32_I2S=y ++CONFIG_SND_SOC_STM32_SPDIFRX=y ++CONFIG_SND_SOC_STM32_DFSDM=y ++CONFIG_SND_AUDIO_GRAPH_CARD=y + + # + # CODEC drivers + # +- ++CONFIG_MFD_WM8994=y ++CONFIG_SND_SOC_WM8994=y ++CONFIG_SND_SOC_DMIC=y ++CONFIG_SND_SOC_CS42L42=y ++CONFIG_SND_SOC_CS42L51_I2C=y + + # + # USB Device Class drivers +@@ -300,6 +333,7 @@ CONFIG_PROTECTION_CONSUMER=y + # + # Gadget/Dual-role mode requires USB Gadget support to be enabled + # ++CONFIG_USB_CONFIGFS=y + + # + # USB Physical Layer drivers +@@ -460,3 +494,9 @@ CONFIG_STM32_ADC_TEMP=y + # STM32 DAC + # + CONFIG_STM32_DAC=y ++ ++# ++# STM32 HSEM ++# ++CONFIG_HWSPINLOCK=y ++CONFIG_HWSPINLOCK_STM32=y +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0032-ARM-stm32mp1-r0-rc3-DMA.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0032-ARM-stm32mp1-r0-rc3-DMA.patch new file mode 100644 index 0000000..2ee2106 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0032-ARM-stm32mp1-r0-rc3-DMA.patch @@ -0,0 +1,76 @@ +From 1028166948f7116f4dcddc74094125db0a7595c8 Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Mon, 10 Dec 2018 15:34:56 +0100 +Subject: [PATCH 32/52] ARM: stm32mp1-r0-rc3: DMA + +--- + drivers/dma/stm32-dma.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c +index 4830f8e..1f9d606 100644 +--- a/drivers/dma/stm32-dma.c ++++ b/drivers/dma/stm32-dma.c +@@ -212,6 +212,7 @@ struct stm32_dma_desc { + u32 num_sgs; + dma_addr_t dma_buf; + void *dma_buf_cpu; ++ u32 dma_buf_size; + struct stm32_dma_sg_req sg_req[]; + }; + +@@ -1224,6 +1225,7 @@ static int stm32_dma_mdma_prep_slave_sg(struct stm32_dma_chan *chan, + &desc->dma_buf); + if (!desc->dma_buf_cpu) + return -ENOMEM; ++ desc->dma_buf_size = chan->sram_size; + + sram_period = chan->sram_size / 2; + +@@ -1316,7 +1318,7 @@ static int stm32_dma_mdma_prep_slave_sg(struct stm32_dma_chan *chan, + } + free_alloc: + gen_pool_free(dmadev->sram_pool, (unsigned long)desc->dma_buf_cpu, +- chan->sram_size); ++ desc->dma_buf_size); + return ret; + } + +@@ -1437,7 +1439,7 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_slave_sg( + + gen_pool_free(dmadev->sram_pool, + (unsigned long)desc->dma_buf_cpu, +- chan->sram_size); ++ desc->dma_buf_size); + } + kfree(desc); + +@@ -1462,6 +1464,7 @@ static int stm32_dma_mdma_prep_dma_cyclic(struct stm32_dma_chan *chan, + &desc->dma_buf); + if (!desc->dma_buf_cpu) + return -ENOMEM; ++ desc->dma_buf_size = 2 * chan->sram_size; + + memset(&config, 0, sizeof(config)); + mem = buf_addr; +@@ -1511,7 +1514,7 @@ static int stm32_dma_mdma_prep_dma_cyclic(struct stm32_dma_chan *chan, + err: + gen_pool_free(dmadev->sram_pool, + (unsigned long)desc->dma_buf_cpu, +- chan->sram_size); ++ desc->dma_buf_size); + return ret; + } + +@@ -1813,7 +1816,7 @@ static void stm32_dma_desc_free(struct virt_dma_desc *vdesc) + + gen_pool_free(dmadev->sram_pool, + (unsigned long)desc->dma_buf_cpu, +- chan->sram_size); ++ desc->dma_buf_size); + } + + kfree(desc); +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0033-ARM-stm32mp1-r0-rc3-DISPLAY.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0033-ARM-stm32mp1-r0-rc3-DISPLAY.patch new file mode 100644 index 0000000..915cf2c --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0033-ARM-stm32mp1-r0-rc3-DISPLAY.patch @@ -0,0 +1,133 @@ +From dc9ea19f397651b7671ec4268a12d3f49e2bbda0 Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Mon, 10 Dec 2018 15:48:07 +0100 +Subject: [PATCH 33/52] ARM: stm32mp1-r0-rc3: DISPLAY + +--- + drivers/gpu/drm/bridge/sii902x.c | 31 +++++++++++++++++++++++++++---- + drivers/gpu/drm/drm_gem.c | 6 ------ + 2 files changed, 27 insertions(+), 10 deletions(-) + +diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c +index 512eb03..170657a 100644 +--- a/drivers/gpu/drm/bridge/sii902x.c ++++ b/drivers/gpu/drm/bridge/sii902x.c +@@ -397,7 +397,6 @@ static void sii902x_bridge_disable(struct drm_bridge *bridge) + regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA, + SII902X_SYS_CTRL_PWR_DWN, + SII902X_SYS_CTRL_PWR_DWN); +- pinctrl_pm_select_sleep_state(&sii902x->i2c->dev); + } + + static void sii902x_bridge_enable(struct drm_bridge *bridge) +@@ -405,7 +404,6 @@ static void sii902x_bridge_enable(struct drm_bridge *bridge) + struct sii902x *sii902x = bridge_to_sii902x(bridge); + bool hdmi_mode; + +- pinctrl_pm_select_default_state(&sii902x->i2c->dev); + regmap_update_bits(sii902x->regmap, SII902X_PWR_STATE_CTRL, + SII902X_AVI_POWER_STATE_MSK, + SII902X_AVI_POWER_STATE_D(0)); +@@ -430,8 +428,17 @@ static void sii902x_bridge_mode_set(struct drm_bridge *bridge, + struct regmap *regmap = sii902x->regmap; + u8 buf[HDMI_INFOFRAME_SIZE(AVI)]; + struct hdmi_avi_infoframe frame; ++ unsigned int status = 0; + int ret; + ++ DRM_DEBUG_DRIVER("\n"); ++ ++ regmap_read(sii902x->regmap, SII902X_INT_STATUS, &status); ++ ++ /* due to old tv, need to restore pinctrl as soon as possible */ ++ if (status & SII902X_PLUGGED_STATUS) ++ pinctrl_pm_select_default_state(&sii902x->i2c->dev); ++ + buf[0] = adj->clock; + buf[1] = adj->clock >> 8; + buf[2] = adj->vrefresh; +@@ -819,6 +826,11 @@ static irqreturn_t sii902x_interrupt(int irq, void *data) + regmap_read(sii902x->regmap, SII902X_INT_STATUS, &status); + regmap_write(sii902x->regmap, SII902X_INT_STATUS, status); + ++ if (status & SII902X_PLUGGED_STATUS) ++ pinctrl_pm_select_default_state(&sii902x->i2c->dev); ++ else ++ pinctrl_pm_select_sleep_state(&sii902x->i2c->dev); ++ + if ((status & SII902X_HOTPLUG_EVENT) && sii902x->bridge.dev) + drm_helper_hpd_irq_event(sii902x->bridge.dev); + +@@ -1045,14 +1057,15 @@ static int sii902x_probe(struct i2c_client *client, + sii902x_i2c_bypass_deselect); + if (!sii902x->i2cmux) { + dev_err(dev, "failed to allocate I2C mux\n"); +- return -ENOMEM; ++ ret = -ENOMEM; ++ goto err_disable_regulator; + } + + sii902x->i2cmux->priv = sii902x; + ret = i2c_mux_add_adapter(sii902x->i2cmux, 0, 0, 0); + if (ret) { + dev_err(dev, "Couldn't add i2c mux adapter\n"); +- return ret; ++ goto err_disable_regulator; + } + + sii902x_register_audio_driver(dev, sii902x); +@@ -1060,6 +1073,7 @@ static int sii902x_probe(struct i2c_client *client, + return 0; + + err_disable_regulator: ++ pinctrl_pm_select_sleep_state(&sii902x->i2c->dev); + regulator_bulk_disable(ARRAY_SIZE(sii902x->supplies), + sii902x->supplies); + +@@ -1095,6 +1109,8 @@ static int sii902x_pm_suspend(struct device *dev) + regulator_bulk_disable(ARRAY_SIZE(sii902x->supplies), + sii902x->supplies); + ++ pinctrl_pm_select_sleep_state(&sii902x->i2c->dev); ++ + return 0; + } + +@@ -1109,10 +1125,17 @@ static int sii902x_pm_resume(struct device *dev) + .len = 2, + .buf = data, + }; ++ unsigned int status = 0; + int ret; + + DRM_DEBUG_DRIVER("\n"); + ++ regmap_read(sii902x->regmap, SII902X_INT_STATUS, &status); ++ ++ /* due to old tv, need to restore pinctrl as soon as possible */ ++ if (status & SII902X_PLUGGED_STATUS) ++ pinctrl_pm_select_default_state(&sii902x->i2c->dev); ++ + ret = regulator_bulk_enable(ARRAY_SIZE(sii902x->supplies), + sii902x->supplies); + if (ret) { +diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c +index bf90625..c7217b1 100644 +--- a/drivers/gpu/drm/drm_gem.c ++++ b/drivers/gpu/drm/drm_gem.c +@@ -326,12 +326,6 @@ int drm_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev, + if (!obj) + return -ENOENT; + +- /* Don't allow imported objects to be mapped */ +- if (obj->import_attach) { +- ret = -EINVAL; +- goto out; +- } +- + ret = drm_gem_create_mmap_offset(obj); + if (ret) + goto out; +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0034-ARM-stm32mp1-r0-rc3-ETH.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0034-ARM-stm32mp1-r0-rc3-ETH.patch new file mode 100644 index 0000000..8d15508 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0034-ARM-stm32mp1-r0-rc3-ETH.patch @@ -0,0 +1,169 @@ +From f7da805ac84601d1dbbaf51e8f080844e1e5ae4e Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Mon, 10 Dec 2018 15:40:00 +0100 +Subject: [PATCH 34/52] ARM: stm32mp1-r0-rc3: ETH + +--- + .../devicetree/bindings/net/stm32-dwmac.txt | 4 +-- + drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c | 36 ++++++++++++++-------- + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 20 ++++++------ + 3 files changed, 36 insertions(+), 24 deletions(-) + +diff --git a/Documentation/devicetree/bindings/net/stm32-dwmac.txt b/Documentation/devicetree/bindings/net/stm32-dwmac.txt +index f42dc68..5f6a6ba 100644 +--- a/Documentation/devicetree/bindings/net/stm32-dwmac.txt ++++ b/Documentation/devicetree/bindings/net/stm32-dwmac.txt +@@ -14,8 +14,7 @@ Required properties: + - clock-names: Should be "stmmaceth" for the host clock. + Should be "mac-clk-tx" for the MAC TX clock. + Should be "mac-clk-rx" for the MAC RX clock. +- For MPU family need to add also "ethstp" for power mode clock and, +- "syscfg-clk" for SYSCFG clock. ++ For MPU family need to add also "ethstp" for power mode clock. + - interrupt-names: Should contain a list of interrupt names corresponding to + the interrupts in the interrupts property, if available. + Should be "macirq" for the main MAC IRQ +@@ -25,6 +24,7 @@ Required properties: + + Optional properties: + - clock-names: For MPU family "eth-ck" for PHY without quartz ++ "syscfg-clk" for SYSCFG clock. + - st,eth_clk_sel (boolean) : set this property in RGMII PHY when you do not want use 125Mhz + - st,eth_ref_clk_sel (boolean) : set this property in RMII mode when you have PHY without crystal 50MHz + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c +index 545b168..8b4ca12 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c +@@ -153,23 +153,32 @@ static int stm32mp1_clk_prepare(struct stm32_dwmac *dwmac, bool prepare) + int ret = 0; + + if (prepare) { +- ret = clk_prepare_enable(dwmac->syscfg_clk); +- if (ret) +- return ret; +- ++ if (dwmac->syscfg_clk) { ++ ret = clk_prepare_enable(dwmac->syscfg_clk); ++ if (ret) ++ return ret; ++ } + if (dwmac->clk_eth_ck) { + ret = clk_prepare_enable(dwmac->clk_eth_ck); + if (ret) { +- clk_disable_unprepare(dwmac->syscfg_clk); ++ if (dwmac->syscfg_clk) ++ goto unprepare_syscfg; + return ret; + } + } + } else { +- clk_disable_unprepare(dwmac->syscfg_clk); ++ if (dwmac->syscfg_clk) ++ clk_disable_unprepare(dwmac->syscfg_clk); ++ + if (dwmac->clk_eth_ck) + clk_disable_unprepare(dwmac->clk_eth_ck); + } + return ret; ++ ++unprepare_syscfg: ++ clk_disable_unprepare(dwmac->syscfg_clk); ++ ++ return ret; + } + + static int stm32mp1_set_mode(struct plat_stmmacenet_data *plat_dat) +@@ -209,8 +218,8 @@ static int stm32mp1_set_mode(struct plat_stmmacenet_data *plat_dat) + } + + /* Need to update PMCCLRR (clear register) */ +- ret = regmap_update_bits(dwmac->regmap, reg + SYSCFG_PMCCLRR_OFFSET, +- dwmac->ops->syscfg_eth_mask, ~val); ++ ret = regmap_write(dwmac->regmap, reg + SYSCFG_PMCCLRR_OFFSET, ++ dwmac->ops->syscfg_eth_mask); + + /* Update PMCSETR (set register) */ + return regmap_update_bits(dwmac->regmap, reg, +@@ -318,11 +327,13 @@ static int stm32mp1_parse_data(struct stm32_dwmac *dwmac, + return PTR_ERR(dwmac->clk_ethstp); + } + +- /* Clock for sysconfig */ ++ /* Optional Clock for sysconfig */ + dwmac->syscfg_clk = devm_clk_get(dev, "syscfg-clk"); + if (IS_ERR(dwmac->syscfg_clk)) { +- dev_err(dev, "No syscfg clock provided...\n"); +- return PTR_ERR(dwmac->syscfg_clk); ++ err = PTR_ERR(dwmac->syscfg_clk); ++ if (err != -ENOENT) ++ return err; ++ dwmac->syscfg_clk = NULL; + } + + /* Get IRQ information early to have an ability to ask for deferred +@@ -431,7 +442,8 @@ static int stm32mp1_suspend(struct stm32_dwmac *dwmac) + return ret; + + clk_disable_unprepare(dwmac->clk_tx); +- clk_disable_unprepare(dwmac->syscfg_clk); ++ if (dwmac->syscfg_clk) ++ clk_disable_unprepare(dwmac->syscfg_clk); + if (dwmac->clk_eth_ck) + clk_disable_unprepare(dwmac->clk_eth_ck); + +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index 75896d6..281d9c7 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -2547,12 +2547,6 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) + netdev_warn(priv->dev, "PTP init failed\n"); + } + +-#ifdef CONFIG_DEBUG_FS +- ret = stmmac_init_fs(dev); +- if (ret < 0) +- netdev_warn(priv->dev, "%s: failed debugFS registration\n", +- __func__); +-#endif + priv->tx_lpi_timer = STMMAC_DEFAULT_TWT_LS; + + if (priv->use_riwt) { +@@ -2753,10 +2747,6 @@ static int stmmac_release(struct net_device *dev) + + netif_carrier_off(dev); + +-#ifdef CONFIG_DEBUG_FS +- stmmac_exit_fs(dev); +-#endif +- + stmmac_release_ptp(priv); + + return 0; +@@ -4394,6 +4384,13 @@ int stmmac_dvr_probe(struct device *device, + goto error_netdev_register; + } + ++#ifdef CONFIG_DEBUG_FS ++ ret = stmmac_init_fs(ndev); ++ if (ret < 0) ++ netdev_warn(priv->dev, "%s: failed debugFS registration\n", ++ __func__); ++#endif ++ + return ret; + + error_netdev_register: +@@ -4429,6 +4426,9 @@ int stmmac_dvr_remove(struct device *dev) + + netdev_info(priv->dev, "%s: removing driver", __func__); + ++#ifdef CONFIG_DEBUG_FS ++ stmmac_exit_fs(ndev); ++#endif + stmmac_stop_all_dma(priv); + + stmmac_mac_set(priv, priv->ioaddr, false); +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0035-ARM-stm32mp1-r0-rc3-IIO.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0035-ARM-stm32mp1-r0-rc3-IIO.patch new file mode 100644 index 0000000..a10435f --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0035-ARM-stm32mp1-r0-rc3-IIO.patch @@ -0,0 +1,479 @@ +From 461aa8f3143d009efd0365889bddf247cfeafdce Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Mon, 10 Dec 2018 15:35:33 +0100 +Subject: [PATCH 35/52] ARM: stm32mp1-r0-rc3: IIO + +--- + drivers/iio/adc/stm32-adc-core.c | 35 +++++----- + drivers/iio/dac/stm32-dac-core.c | 142 ++++++++++++++++++++++++++++++--------- + drivers/iio/dac/stm32-dac.c | 96 +++++++++++++++++++++++++- + 3 files changed, 220 insertions(+), 53 deletions(-) + +diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c +index 6234456..ed64bb0 100644 +--- a/drivers/iio/adc/stm32-adc-core.c ++++ b/drivers/iio/adc/stm32-adc-core.c +@@ -677,10 +677,9 @@ static int stm32_adc_switches_supply_en(struct device *dev) + 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); ++ ret = regmap_write(priv->vbooster_clr.regmap, ++ priv->vbooster_clr.reg, ++ priv->vbooster_clr.mask); + if (ret) { + dev_err(dev, "can't access voltage booster, %d\n", ret); + goto vdd_dis; +@@ -697,10 +696,9 @@ static int stm32_adc_switches_supply_en(struct device *dev) + 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); ++ ret = regmap_write(priv->anaswvdd_clr.regmap, ++ priv->anaswvdd_clr.reg, ++ priv->anaswvdd_clr.mask); + if (ret) { + dev_err(dev, "can't access anaswvdd, %d\n", ret); + goto booster_dis; +@@ -714,10 +712,9 @@ static int stm32_adc_switches_supply_en(struct device *dev) + 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); ++ regmap_write(priv->vbooster_clr.regmap, ++ priv->vbooster_clr.reg, ++ priv->vbooster_clr.mask); + vdd_dis: + if (!IS_ERR(priv->vdd) && !IS_ERR(priv->anaswvdd.regmap)) + regulator_disable(priv->vdd); +@@ -741,20 +738,18 @@ static void stm32_adc_switches_supply_dis(struct device *dev) + 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); ++ regmap_write(priv->anaswvdd_clr.regmap, ++ priv->anaswvdd_clr.reg, ++ priv->anaswvdd_clr.mask); + } + + if (IS_ERR(priv->vbooster_clr.regmap)) + regmap_update_bits(priv->vbooster.regmap, priv->vbooster.reg, + priv->vbooster.mask, 0); + else +- regmap_update_bits(priv->vbooster_clr.regmap, +- priv->vbooster_clr.reg, +- priv->vbooster_clr.mask, +- priv->vbooster_clr.mask); ++ regmap_write(priv->vbooster_clr.regmap, ++ priv->vbooster_clr.reg, ++ priv->vbooster_clr.mask); + + if (!IS_ERR(priv->vdd) && !IS_ERR(priv->anaswvdd.regmap)) + regulator_disable(priv->vdd); +diff --git a/drivers/iio/dac/stm32-dac-core.c b/drivers/iio/dac/stm32-dac-core.c +index d0fb312..280322b 100644 +--- a/drivers/iio/dac/stm32-dac-core.c ++++ b/drivers/iio/dac/stm32-dac-core.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -50,6 +51,41 @@ static const struct regmap_config stm32_dac_regmap_cfg = { + .max_register = 0x3fc, + }; + ++static int stm32_dac_core_hw_start(struct device *dev) ++{ ++ struct stm32_dac_common *common = dev_get_drvdata(dev); ++ struct stm32_dac_priv *priv = to_stm32_dac_priv(common); ++ int ret; ++ ++ ret = regulator_enable(priv->vref); ++ if (ret < 0) { ++ dev_err(dev, "vref enable failed: %d\n", ret); ++ return ret; ++ } ++ ++ ret = clk_prepare_enable(priv->pclk); ++ if (ret < 0) { ++ dev_err(dev, "pclk enable failed: %d\n", ret); ++ goto err_regulator_disable; ++ } ++ ++ return 0; ++ ++err_regulator_disable: ++ regulator_disable(priv->vref); ++ ++ return ret; ++} ++ ++static void stm32_dac_core_hw_stop(struct device *dev) ++{ ++ struct stm32_dac_common *common = dev_get_drvdata(dev); ++ struct stm32_dac_priv *priv = to_stm32_dac_priv(common); ++ ++ clk_disable_unprepare(priv->pclk); ++ regulator_disable(priv->vref); ++} ++ + static int stm32_dac_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; +@@ -66,6 +102,8 @@ static int stm32_dac_probe(struct platform_device *pdev) + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; ++ platform_set_drvdata(pdev, &priv->common); ++ + cfg = (const struct stm32_dac_cfg *) + of_match_device(dev->driver->of_match_table, dev)->data; + +@@ -74,11 +112,19 @@ static int stm32_dac_probe(struct platform_device *pdev) + if (IS_ERR(mmio)) + return PTR_ERR(mmio); + +- regmap = devm_regmap_init_mmio(dev, mmio, &stm32_dac_regmap_cfg); ++ regmap = devm_regmap_init_mmio_clk(dev, "pclk", mmio, ++ &stm32_dac_regmap_cfg); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + priv->common.regmap = regmap; + ++ priv->pclk = devm_clk_get(dev, "pclk"); ++ if (IS_ERR(priv->pclk)) { ++ ret = PTR_ERR(priv->pclk); ++ dev_err(dev, "pclk get failed\n"); ++ return ret; ++ } ++ + priv->vref = devm_regulator_get(dev, "vref"); + if (IS_ERR(priv->vref)) { + ret = PTR_ERR(priv->vref); +@@ -86,33 +132,22 @@ static int stm32_dac_probe(struct platform_device *pdev) + return ret; + } + +- ret = regulator_enable(priv->vref); +- if (ret < 0) { +- dev_err(dev, "vref enable failed\n"); +- return ret; +- } ++ pm_runtime_get_noresume(dev); ++ pm_runtime_set_active(dev); ++ pm_runtime_enable(dev); ++ ++ ret = stm32_dac_core_hw_start(dev); ++ if (ret) ++ goto err_pm_stop; + + ret = regulator_get_voltage(priv->vref); + if (ret < 0) { + dev_err(dev, "vref get voltage failed, %d\n", ret); +- goto err_vref; ++ goto err_hw_stop; + } + priv->common.vref_mv = ret / 1000; + dev_dbg(dev, "vref+=%dmV\n", priv->common.vref_mv); + +- priv->pclk = devm_clk_get(dev, "pclk"); +- if (IS_ERR(priv->pclk)) { +- ret = PTR_ERR(priv->pclk); +- dev_err(dev, "pclk get failed\n"); +- goto err_vref; +- } +- +- ret = clk_prepare_enable(priv->pclk); +- if (ret < 0) { +- dev_err(dev, "pclk enable failed\n"); +- goto err_vref; +- } +- + priv->rst = devm_reset_control_get_exclusive(dev, NULL); + if (!IS_ERR(priv->rst)) { + reset_control_assert(priv->rst); +@@ -128,39 +163,83 @@ static int stm32_dac_probe(struct platform_device *pdev) + priv->common.hfsel ? + STM32H7_DAC_CR_HFSEL : 0); + if (ret) +- goto err_pclk; ++ goto err_hw_stop; + } + +- platform_set_drvdata(pdev, &priv->common); + + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, dev); + if (ret < 0) { + dev_err(dev, "failed to populate DT children\n"); +- goto err_pclk; ++ goto err_hw_stop; + } + ++ pm_runtime_put(dev); ++ + return 0; + +-err_pclk: +- clk_disable_unprepare(priv->pclk); +-err_vref: +- regulator_disable(priv->vref); ++err_hw_stop: ++ stm32_dac_core_hw_stop(dev); ++err_pm_stop: ++ pm_runtime_disable(dev); ++ pm_runtime_set_suspended(dev); ++ pm_runtime_put_noidle(dev); + + return ret; + } + + static int stm32_dac_remove(struct platform_device *pdev) + { +- struct stm32_dac_common *common = platform_get_drvdata(pdev); ++ pm_runtime_get_sync(&pdev->dev); ++ of_platform_depopulate(&pdev->dev); ++ stm32_dac_core_hw_stop(&pdev->dev); ++ pm_runtime_disable(&pdev->dev); ++ pm_runtime_set_suspended(&pdev->dev); ++ pm_runtime_put_noidle(&pdev->dev); ++ ++ return 0; ++} ++ ++#if defined(CONFIG_PM_SLEEP) ++static int stm32_dac_core_resume(struct device *dev) ++{ ++ struct stm32_dac_common *common = dev_get_drvdata(dev); + struct stm32_dac_priv *priv = to_stm32_dac_priv(common); ++ int ret; + +- of_platform_depopulate(&pdev->dev); +- clk_disable_unprepare(priv->pclk); +- regulator_disable(priv->vref); ++ /* Unconditionally restore hfsel (maybe lost under low power state) */ ++ if (priv->common.hfsel) { ++ ret = regmap_update_bits(priv->common.regmap, STM32_DAC_CR, ++ STM32H7_DAC_CR_HFSEL, ++ STM32H7_DAC_CR_HFSEL); ++ if (ret) ++ return ret; ++ } ++ ++ return pm_runtime_force_resume(dev); ++} ++#endif ++ ++#if defined(CONFIG_PM) ++static int stm32_dac_core_runtime_suspend(struct device *dev) ++{ ++ stm32_dac_core_hw_stop(dev); + + return 0; + } + ++static int stm32_dac_core_runtime_resume(struct device *dev) ++{ ++ return stm32_dac_core_hw_start(dev); ++} ++#endif ++ ++static const struct dev_pm_ops stm32_dac_core_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, stm32_dac_core_resume) ++ SET_RUNTIME_PM_OPS(stm32_dac_core_runtime_suspend, ++ stm32_dac_core_runtime_resume, ++ NULL) ++}; ++ + static const struct stm32_dac_cfg stm32h7_dac_cfg = { + .has_hfsel = true, + }; +@@ -182,6 +261,7 @@ static struct platform_driver stm32_dac_driver = { + .driver = { + .name = "stm32-dac-core", + .of_match_table = stm32_dac_of_match, ++ .pm = &stm32_dac_core_pm_ops, + }, + }; + module_platform_driver(stm32_dac_driver); +diff --git a/drivers/iio/dac/stm32-dac.c b/drivers/iio/dac/stm32-dac.c +index cce26a3..0a8abc5 100644 +--- a/drivers/iio/dac/stm32-dac.c ++++ b/drivers/iio/dac/stm32-dac.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + + #include "stm32-dac-core.h" + +@@ -20,6 +21,8 @@ + #define STM32_DAC_CHANNEL_2 2 + #define STM32_DAC_IS_CHAN_1(ch) ((ch) & STM32_DAC_CHANNEL_1) + ++#define STM32_DAC_AUTO_SUSPEND_DELAY_MS 2000 ++ + /** + * struct stm32_dac - private data of DAC driver + * @common: reference to DAC common data +@@ -49,15 +52,34 @@ static int stm32_dac_set_enable_state(struct iio_dev *indio_dev, int ch, + bool enable) + { + struct stm32_dac *dac = iio_priv(indio_dev); ++ struct device *dev = indio_dev->dev.parent; + u32 msk = STM32_DAC_IS_CHAN_1(ch) ? STM32_DAC_CR_EN1 : STM32_DAC_CR_EN2; + u32 en = enable ? msk : 0; + int ret; + ++ /* already enabled / disabled ? */ ++ mutex_lock(&indio_dev->mlock); ++ ret = stm32_dac_is_enabled(indio_dev, ch); ++ if (ret < 0 || enable == !!ret) { ++ mutex_unlock(&indio_dev->mlock); ++ return ret < 0 ? ret : 0; ++ } ++ ++ if (enable) { ++ ret = pm_runtime_get_sync(dev); ++ if (ret < 0) { ++ pm_runtime_put_noidle(dev); ++ mutex_unlock(&indio_dev->mlock); ++ return ret; ++ } ++ } ++ + ret = regmap_update_bits(dac->common->regmap, STM32_DAC_CR, msk, en); ++ mutex_unlock(&indio_dev->mlock); + if (ret < 0) { + dev_err(&indio_dev->dev, "%s failed\n", en ? + "Enable" : "Disable"); +- return ret; ++ goto err_put_pm; + } + + /* +@@ -68,7 +90,20 @@ static int stm32_dac_set_enable_state(struct iio_dev *indio_dev, int ch, + if (en && dac->common->hfsel) + udelay(1); + ++ if (!enable) { ++ pm_runtime_mark_last_busy(dev); ++ pm_runtime_put_autosuspend(dev); ++ } ++ + return 0; ++ ++err_put_pm: ++ if (enable) { ++ pm_runtime_mark_last_busy(dev); ++ pm_runtime_put_autosuspend(dev); ++ } ++ ++ return ret; + } + + static int stm32_dac_get_value(struct stm32_dac *dac, int channel, int *val) +@@ -272,6 +307,7 @@ static int stm32_dac_chan_of_init(struct iio_dev *indio_dev) + static int stm32_dac_probe(struct platform_device *pdev) + { + struct device_node *np = pdev->dev.of_node; ++ struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct stm32_dac *dac; + int ret; +@@ -296,9 +332,63 @@ static int stm32_dac_probe(struct platform_device *pdev) + if (ret < 0) + return ret; + +- return devm_iio_device_register(&pdev->dev, indio_dev); ++ /* Get stm32-dac-core PM online */ ++ pm_runtime_get_noresume(dev); ++ pm_runtime_set_active(dev); ++ pm_runtime_set_autosuspend_delay(dev, STM32_DAC_AUTO_SUSPEND_DELAY_MS); ++ pm_runtime_use_autosuspend(dev); ++ pm_runtime_enable(dev); ++ ++ ret = iio_device_register(indio_dev); ++ if (ret) ++ goto err_pm_put; ++ ++ pm_runtime_mark_last_busy(dev); ++ pm_runtime_put_autosuspend(dev); ++ ++ return 0; ++ ++err_pm_put: ++ pm_runtime_disable(dev); ++ pm_runtime_set_suspended(dev); ++ pm_runtime_put_noidle(dev); ++ ++ return ret; + } + ++static int stm32_dac_remove(struct platform_device *pdev) ++{ ++ struct iio_dev *indio_dev = platform_get_drvdata(pdev); ++ ++ pm_runtime_get_sync(&pdev->dev); ++ iio_device_unregister(indio_dev); ++ pm_runtime_disable(&pdev->dev); ++ pm_runtime_set_suspended(&pdev->dev); ++ pm_runtime_put_noidle(&pdev->dev); ++ ++ return 0; ++} ++ ++#if defined(CONFIG_PM_SLEEP) ++static int stm32_dac_suspend(struct device *dev) ++{ ++ struct iio_dev *indio_dev = dev_get_drvdata(dev); ++ int channel = indio_dev->channels[0].channel; ++ int ret; ++ ++ /* Ensure DAC is disabled before suspend */ ++ ret = stm32_dac_is_enabled(indio_dev, channel); ++ if (ret) ++ return ret < 0 ? ret : -EBUSY; ++ ++ return pm_runtime_force_suspend(dev); ++} ++#endif ++ ++static const struct dev_pm_ops stm32_dac_pm_ops = { ++ SET_SYSTEM_SLEEP_PM_OPS(stm32_dac_suspend, pm_runtime_force_resume) ++}; ++ + static const struct of_device_id stm32_dac_of_match[] = { + { .compatible = "st,stm32-dac", }, + {}, +@@ -307,9 +397,11 @@ MODULE_DEVICE_TABLE(of, stm32_dac_of_match); + + static struct platform_driver stm32_dac_driver = { + .probe = stm32_dac_probe, ++ .remove = stm32_dac_remove, + .driver = { + .name = "stm32-dac", + .of_match_table = stm32_dac_of_match, ++ .pm = &stm32_dac_pm_ops, + }, + }; + module_platform_driver(stm32_dac_driver); +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0036-ARM-stm32mp1-r0-rc3-INPUT-TTY.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0036-ARM-stm32mp1-r0-rc3-INPUT-TTY.patch new file mode 100644 index 0000000..e96669a --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0036-ARM-stm32mp1-r0-rc3-INPUT-TTY.patch @@ -0,0 +1,135 @@ +From adfe9751275d51343a5b7f26ea93246ae617d986 Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Mon, 10 Dec 2018 15:37:06 +0100 +Subject: [PATCH 36/52] ARM: stm32mp1-r0-rc3: INPUT TTY + +--- + .../devicetree/bindings/serial/st,stm32-usart.txt | 1 + + drivers/input/touchscreen/edt-ft5x06.c | 8 +++++++- + drivers/input/touchscreen/goodix.c | 9 +++++++++ + drivers/tty/serial/stm32-usart.c | 18 ++++++++++++++++-- + 4 files changed, 33 insertions(+), 3 deletions(-) + +diff --git a/Documentation/devicetree/bindings/serial/st,stm32-usart.txt b/Documentation/devicetree/bindings/serial/st,stm32-usart.txt +index 90ba52f..08b4990 100644 +--- a/Documentation/devicetree/bindings/serial/st,stm32-usart.txt ++++ b/Documentation/devicetree/bindings/serial/st,stm32-usart.txt +@@ -14,6 +14,7 @@ Required properties: + - clocks: The input clock of the USART instance + + Optional properties: ++- resets: Must contain the phandle to the reset controller. + - pinctrl-names: Set to "default". An additional "sleep" state can be defined + to set pins in sleep state when in low power. In case the device is used as + a wakeup source, "idle" state is defined in order to keep RX pin active. +diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c +index 1e18ca0..c1c6f2a 100644 +--- a/drivers/input/touchscreen/edt-ft5x06.c ++++ b/drivers/input/touchscreen/edt-ft5x06.c +@@ -1033,7 +1033,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, + + error = edt_ft5x06_ts_identify(client, tsdata, fw_version); + if (error) { +- dev_err(&client->dev, "touchscreen probe failed\n"); ++ dev_dbg(&client->dev, "touchscreen probe failed\n"); + return error; + } + +@@ -1152,11 +1152,16 @@ static const struct edt_i2c_chip_data edt_ft6236_data = { + .max_support_points = 2, + }; + ++static const struct edt_i2c_chip_data edt_ft6336_data = { ++ .max_support_points = 2, ++}; ++ + static const struct i2c_device_id edt_ft5x06_ts_id[] = { + { .name = "edt-ft5x06", .driver_data = (long)&edt_ft5x06_data }, + { .name = "edt-ft5506", .driver_data = (long)&edt_ft5506_data }, + /* Note no edt- prefix for compatibility with the ft6236.c driver */ + { .name = "ft6236", .driver_data = (long)&edt_ft6236_data }, ++ { .name = "ft6336", .driver_data = (long)&edt_ft6336_data }, + { /* sentinel */ } + }; + MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id); +@@ -1169,6 +1174,7 @@ static const struct of_device_id edt_ft5x06_of_match[] = { + { .compatible = "edt,edt-ft5506", .data = &edt_ft5506_data }, + /* Note focaltech vendor prefix for compatibility with ft6236.c */ + { .compatible = "focaltech,ft6236", .data = &edt_ft6236_data }, ++ { .compatible = "focaltech,ft6336", .data = &edt_ft6336_data }, + { /* sentinel */ } + }; + MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match); +diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c +index f2d9c2c..9ce8db4 100644 +--- a/drivers/input/touchscreen/goodix.c ++++ b/drivers/input/touchscreen/goodix.c +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -357,6 +358,13 @@ static void goodix_free_irq(struct goodix_ts_data *ts) + + static int goodix_request_irq(struct goodix_ts_data *ts) + { ++ int gpio; ++ ++ gpio = desc_to_gpio(ts->gpiod_int); ++ ++ if (gpio_is_valid(gpio)) ++ ts->client->irq = gpio_to_irq(gpio); ++ + return devm_request_threaded_irq(&ts->client->dev, ts->client->irq, + NULL, goodix_ts_irq_handler, + ts->irq_flags, ts->client->name, ts); +@@ -949,6 +957,7 @@ static const struct of_device_id goodix_of_match[] = { + { .compatible = "goodix,gt9271" }, + { .compatible = "goodix,gt928" }, + { .compatible = "goodix,gt967" }, ++ { .compatible = "goodix,gt9147",}, + { } + }; + MODULE_DEVICE_TABLE(of, goodix_of_match); +diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c +index 5c6c3c0..d606eb5 100644 +--- a/drivers/tty/serial/stm32-usart.c ++++ b/drivers/tty/serial/stm32-usart.c +@@ -938,6 +938,7 @@ static int stm32_init_port(struct stm32_port *stm32port, + { + struct uart_port *port = &stm32port->port; + struct resource *res; ++ struct pinctrl *uart_pinctrl; + int ret; + + port->iotype = UPIO_MEM; +@@ -952,8 +953,21 @@ static int stm32_init_port(struct stm32_port *stm32port, + + stm32port->wakeirq = platform_get_irq_byname(pdev, "wakeup"); + stm32port->fifoen = stm32port->info->cfg.has_fifo; +- stm32port->console_pins = pinctrl_lookup_state(pdev->dev.pins->p, +- "no_console_suspend"); ++ ++ uart_pinctrl = devm_pinctrl_get(&pdev->dev); ++ if (IS_ERR(uart_pinctrl)) { ++ ret = PTR_ERR(uart_pinctrl); ++ if (ret != -ENODEV) { ++ dev_err(&pdev->dev,"Can't get pinctrl, error %d\n", ++ ret); ++ return ret; ++ } ++ stm32port->console_pins = ERR_PTR(-ENODEV); ++ } ++ else ++ stm32port->console_pins = pinctrl_lookup_state ++ (uart_pinctrl,"no_console_suspend"); ++ + if (IS_ERR(stm32port->console_pins) + && PTR_ERR(stm32port->console_pins) != -ENODEV) + return PTR_ERR(stm32port->console_pins); +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0037-ARM-stm32mp1-r0-rc3-IRQ-Mailbox.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0037-ARM-stm32mp1-r0-rc3-IRQ-Mailbox.patch new file mode 100644 index 0000000..7896510 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0037-ARM-stm32mp1-r0-rc3-IRQ-Mailbox.patch @@ -0,0 +1,277 @@ +From 4cc987cced2179b54ebc0a977ff3ef4210acb38c Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Mon, 10 Dec 2018 15:36:09 +0100 +Subject: [PATCH 37/52] ARM: stm32mp1-r0-rc3: IRQ Mailbox + +--- + drivers/irqchip/irq-stm32-exti.c | 122 ++++++++++++++++++++++++++------------- + 1 file changed, 83 insertions(+), 39 deletions(-) + +diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c +index 9cc15f1..223ee2e 100644 +--- a/drivers/irqchip/irq-stm32-exti.c ++++ b/drivers/irqchip/irq-stm32-exti.c +@@ -6,6 +6,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -21,7 +22,8 @@ + + #define IRQS_PER_BANK 32 + +-#define HWSPINLOCK_TIMEOUT 5 /* msec */ ++#define HWSPNLCK_TIMEOUT 1000 /* usec */ ++#define HWSPNLCK_RETRY_DELAY 100 /* usec */ + + struct stm32_exti_bank { + u32 imr_ofst; +@@ -35,6 +37,12 @@ struct stm32_exti_bank { + + #define UNDEF_REG ~0 + ++enum stm32_exti_hwspinlock { ++ HWSPINLOCK_UNKNOWN, ++ HWSPINLOCK_NONE, ++ HWSPINLOCK_READY, ++}; ++ + struct stm32_desc_irq { + u32 exti; + u32 irq_parent; +@@ -50,7 +58,6 @@ struct stm32_exti_drv_data { + struct stm32_exti_chip_data { + struct stm32_exti_host_data *host_data; + const struct stm32_exti_bank *reg_bank; +- struct hwspinlock *hwlock; + struct raw_spinlock rlock; + u32 wake_active; + u32 mask_cache; +@@ -62,6 +69,9 @@ struct stm32_exti_host_data { + void __iomem *base; + struct stm32_exti_chip_data *chips_data; + const struct stm32_exti_drv_data *drv_data; ++ struct device_node *node; ++ enum stm32_exti_hwspinlock hwlock_state; ++ struct hwspinlock *hwlock; + }; + + static struct stm32_exti_host_data *stm32_host_data; +@@ -273,20 +283,75 @@ static int stm32_exti_set_type(struct irq_data *d, + return 0; + } + ++static int stm32_exti_hwspin_lock(struct stm32_exti_chip_data *chip_data) ++{ ++ struct stm32_exti_host_data *host_data = chip_data->host_data; ++ struct hwspinlock *hwlock; ++ int id, ret = 0, timeout = 0; ++ ++ /* first time, check for hwspinlock availability */ ++ if (unlikely(host_data->hwlock_state == HWSPINLOCK_UNKNOWN)) { ++ id = of_hwspin_lock_get_id(host_data->node, 0); ++ if (id >= 0) { ++ hwlock = hwspin_lock_request_specific(id); ++ if (hwlock) { ++ /* found valid hwspinlock */ ++ host_data->hwlock_state = HWSPINLOCK_READY; ++ host_data->hwlock = hwlock; ++ pr_debug("%s hwspinlock = %d\n", __func__, id); ++ } else { ++ host_data->hwlock_state = HWSPINLOCK_NONE; ++ } ++ } else if (id != -EPROBE_DEFER) { ++ host_data->hwlock_state = HWSPINLOCK_NONE; ++ } else { ++ /* hwspinlock driver shall be ready at that stage */ ++ ret = -EPROBE_DEFER; ++ } ++ } ++ ++ if (likely(host_data->hwlock_state == HWSPINLOCK_READY)) { ++ /* ++ * Use the x_raw API since we are under spin_lock protection. ++ * Do not use the x_timeout API because we are under irq_disable ++ * mode (see __setup_irq()) ++ */ ++ do { ++ ret = hwspin_trylock_raw(host_data->hwlock); ++ if (!ret) ++ return 0; ++ ++ udelay(HWSPNLCK_RETRY_DELAY); ++ timeout += HWSPNLCK_RETRY_DELAY; ++ } while (timeout < HWSPNLCK_TIMEOUT); ++ ++ if (ret == -EBUSY) ++ ret = -ETIMEDOUT; ++ } ++ ++ if (ret) ++ pr_err("%s can't get hwspinlock (%d)\n", __func__, ret); ++ ++ return ret; ++} ++ ++static void stm32_exti_hwspin_unlock(struct stm32_exti_chip_data *chip_data) ++{ ++ if (likely(chip_data->host_data->hwlock_state == HWSPINLOCK_READY)) ++ hwspin_unlock_raw(chip_data->host_data->hwlock); ++} ++ + static int stm32_irq_set_type(struct irq_data *d, unsigned int type) + { + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct stm32_exti_chip_data *chip_data = gc->private; + const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + u32 rtsr, ftsr; +- int err = 0; ++ int err; + + irq_gc_lock(gc); + +- if (chip_data->hwlock) +- err = hwspin_lock_timeout(chip_data->hwlock, +- HWSPINLOCK_TIMEOUT); +- ++ err = stm32_exti_hwspin_lock(chip_data); + if (err) + goto unlock; + +@@ -301,8 +366,7 @@ static int stm32_irq_set_type(struct irq_data *d, unsigned int type) + irq_reg_writel(gc, ftsr, stm32_bank->ftsr_ofst); + + unspinlock: +- if (chip_data->hwlock) +- hwspin_unlock(chip_data->hwlock); ++ stm32_exti_hwspin_unlock(chip_data); + unlock: + irq_gc_unlock(gc); + +@@ -470,14 +534,11 @@ static int stm32_exti_h_set_type(struct irq_data *d, unsigned int type) + const struct stm32_exti_bank *stm32_bank = chip_data->reg_bank; + void __iomem *base = chip_data->host_data->base; + u32 rtsr, ftsr; +- int err = 0; ++ int err; + + raw_spin_lock(&chip_data->rlock); + +- if (chip_data->hwlock) +- err = hwspin_lock_timeout(chip_data->hwlock, +- HWSPINLOCK_TIMEOUT); +- ++ err = stm32_exti_hwspin_lock(chip_data); + if (err) + goto unlock; + +@@ -492,15 +553,14 @@ static int stm32_exti_h_set_type(struct irq_data *d, unsigned int type) + writel_relaxed(ftsr, base + stm32_bank->ftsr_ofst); + + unspinlock: +- if (chip_data->hwlock) +- hwspin_unlock(chip_data->hwlock); ++ stm32_exti_hwspin_unlock(chip_data); + unlock: + raw_spin_unlock(&chip_data->rlock); + + if (d->parent_data->chip) + irq_chip_set_type_parent(d, type); + +- return 0; ++ return err; + } + + static int stm32_exti_h_set_wake(struct irq_data *d, unsigned int on) +@@ -650,6 +710,8 @@ stm32_exti_host_data *stm32_exti_host_init(const struct stm32_exti_drv_data *dd, + return NULL; + + host_data->drv_data = dd; ++ host_data->node = node; ++ host_data->hwlock_state = HWSPINLOCK_UNKNOWN; + host_data->chips_data = kcalloc(dd->bank_nr, + sizeof(struct stm32_exti_chip_data), + GFP_KERNEL); +@@ -676,13 +738,11 @@ stm32_exti_host_data *stm32_exti_host_init(const struct stm32_exti_drv_data *dd, + + static struct + stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, +- u32 bank_idx, +- struct device_node *node) ++ u32 bank_idx) + { + const struct stm32_exti_bank *stm32_bank; + struct stm32_exti_chip_data *chip_data; + void __iomem *base = h_data->base; +- u32 irqs_mask; + + stm32_bank = h_data->drv_data->exti_banks[bank_idx]; + chip_data = &h_data->chips_data[bank_idx]; +@@ -691,10 +751,6 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, + + raw_spin_lock_init(&chip_data->rlock); + +- /* Determine number of irqs supported */ +- writel_relaxed(~0UL, base + stm32_bank->rtsr_ofst); +- irqs_mask = readl_relaxed(base + stm32_bank->rtsr_ofst); +- + /* + * This IP has no reset, so after hot reboot we should + * clear registers to avoid residue +@@ -702,8 +758,7 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, + writel_relaxed(0, base + stm32_bank->imr_ofst); + writel_relaxed(0, base + stm32_bank->emr_ofst); + +- pr_info("%s: bank%d, External IRQs available:%#x\n", +- node->full_name, bank_idx, irqs_mask); ++ pr_info("%pOF: bank%d\n", h_data->node, bank_idx); + + return chip_data; + } +@@ -716,7 +771,6 @@ static int __init stm32_exti_init(const struct stm32_exti_drv_data *drv_data, + int nr_irqs, ret, i; + struct irq_chip_generic *gc; + struct irq_domain *domain; +- struct hwspinlock *hwlock = NULL; + + host_data = stm32_exti_host_init(drv_data, node); + if (!host_data) +@@ -739,22 +793,12 @@ static int __init stm32_exti_init(const struct stm32_exti_drv_data *drv_data, + goto out_free_domain; + } + +- /* hwspinlock is optional */ +- ret = of_hwspin_lock_get_id(node, 0); +- if (ret < 0) { +- if (ret == -EPROBE_DEFER) +- goto out_free_domain; +- } else { +- hwlock = hwspin_lock_request_specific(ret); +- } +- + for (i = 0; i < drv_data->bank_nr; i++) { + const struct stm32_exti_bank *stm32_bank; + struct stm32_exti_chip_data *chip_data; + + stm32_bank = drv_data->exti_banks[i]; +- chip_data = stm32_exti_chip_init(host_data, i, node); +- chip_data->hwlock = hwlock; ++ chip_data = stm32_exti_chip_init(host_data, i); + + gc = irq_get_domain_generic_chip(domain, i * IRQS_PER_BANK); + +@@ -836,7 +880,7 @@ __init stm32_exti_hierarchy_init(const struct stm32_exti_drv_data *drv_data, + return -ENOMEM; + + for (i = 0; i < drv_data->bank_nr; i++) +- stm32_exti_chip_init(host_data, i, node); ++ stm32_exti_chip_init(host_data, i); + + domain = irq_domain_add_hierarchy(parent_domain, 0, + drv_data->bank_nr * IRQS_PER_BANK, +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0038-ARM-stm32mp1-r0-rc3-MEDIA.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0038-ARM-stm32mp1-r0-rc3-MEDIA.patch new file mode 100644 index 0000000..fe3c019 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0038-ARM-stm32mp1-r0-rc3-MEDIA.patch @@ -0,0 +1,26 @@ +From 6c1f5d14ed9c421276ad7beac5475e1114520369 Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Mon, 10 Dec 2018 15:48:55 +0100 +Subject: [PATCH 38/52] ARM: stm32mp1-r0-rc3: MEDIA + +--- + drivers/media/i2c/ov5640.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c +index 27b75e7..22ddfca4 100644 +--- a/drivers/media/i2c/ov5640.c ++++ b/drivers/media/i2c/ov5640.c +@@ -2691,8 +2691,7 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int enable) + + if (sensor->streaming == !enable) { + if (enable && sensor->pending_mode_change) { +- ret = ov5640_set_mode(sensor, sensor->last_mode); +- ++ ret = ov5640_set_mode(sensor); + if (ret) + goto out; + } +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0039-ARM-stm32mp1-r0-rc3-MMC-MTD.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0039-ARM-stm32mp1-r0-rc3-MMC-MTD.patch new file mode 100644 index 0000000..767d64c --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0039-ARM-stm32mp1-r0-rc3-MMC-MTD.patch @@ -0,0 +1,478 @@ +From 0affc90bd300ecd2c60aee7fd97251a8d2aad01f Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Mon, 10 Dec 2018 15:38:59 +0100 +Subject: [PATCH 39/52] ARM: stm32mp1-r0-rc3: MMC MTD + +--- + Documentation/devicetree/bindings/mmc/mmci.txt | 2 + + drivers/mmc/host/mmci.c | 61 +++++++++- + drivers/mmc/host/mmci.h | 8 +- + drivers/mmc/host/mmci_stm32_sdmmc.c | 162 ++++++++++++++++++++++++- + 4 files changed, 219 insertions(+), 14 deletions(-) + +diff --git a/Documentation/devicetree/bindings/mmc/mmci.txt b/Documentation/devicetree/bindings/mmc/mmci.txt +index 6d3c626..da6d59e 100644 +--- a/Documentation/devicetree/bindings/mmc/mmci.txt ++++ b/Documentation/devicetree/bindings/mmc/mmci.txt +@@ -15,6 +15,8 @@ Required properties: + Optional properties: + - arm,primecell-periphid : contains the PrimeCell Peripheral ID, it overrides + the ID provided by the HW ++- reg : sdmmc variant could have a second base register for ++ delay block. + - resets : phandle to internal reset line. + Should be defined for sdmmc variant. + - vqmmc-supply : phandle to the regulator device tree node, mentioned +diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c +index 02b631f..6c2b1a0 100644 +--- a/drivers/mmc/host/mmci.c ++++ b/drivers/mmc/host/mmci.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -25,6 +26,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -291,7 +293,8 @@ static struct variant_data variant_stm32_sdmmc = { + .busy_detect_flag = MCI_STM32_BUSYD0, + .busy_detect_mask = MCI_STM32_BUSYD0ENDMASK, + .init = sdmmc_variant_init, +- .quirks = MMCI_QUIRK_STM32_DTMODE, ++ .quirks = MMCI_QUIRK_STM32_DTMODE | ++ MMCI_QUIRK_STM32_VSWITCH, + }; + + static struct variant_data variant_stm32_sdmmcv2 = { +@@ -318,7 +321,8 @@ static struct variant_data variant_stm32_sdmmcv2 = { + .busy_detect_flag = MCI_STM32_BUSYD0, + .busy_detect_mask = MCI_STM32_BUSYD0ENDMASK, + .init = sdmmc_variant_init, +- .quirks = MMCI_QUIRK_STM32_DTMODE, ++ .quirks = MMCI_QUIRK_STM32_DTMODE | ++ MMCI_QUIRK_STM32_VSWITCH, + }; + + static struct variant_data variant_qcom = { +@@ -1191,6 +1195,10 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c) + writel_relaxed(clks, host->base + MMCIDATATIMER); + } + ++ if (host->variant->quirks & MMCI_QUIRK_STM32_VSWITCH && ++ cmd->opcode == SD_SWITCH_VOLTAGE) ++ mmci_write_pwrreg(host, host->pwr_reg | MCI_STM32_VSWITCHEN); ++ + if (/*interrupt*/0) + c |= MCI_CPSM_INTERRUPT; + +@@ -1284,13 +1292,13 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, + unsigned int status) + { + void __iomem *base = host->base; +- bool busy_resp = !!(cmd->flags & MMC_RSP_BUSY); +- bool sbc; ++ bool busy_resp, sbc; + u32 err_msk; + + if (!cmd) + return; + ++ busy_resp = !!(cmd->flags & MMC_RSP_BUSY); + sbc = (cmd == host->mrq->sbc); + + /* +@@ -1575,11 +1583,14 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id) + static irqreturn_t mmci_irq(int irq, void *dev_id) + { + struct mmci_host *host = dev_id; ++ bool busy_resp; + u32 status; + int ret = 0; + + spin_lock(&host->lock); + ++ busy_resp = host->cmd ? !!(host->cmd->flags & MMC_RSP_BUSY) : false; ++ + do { + status = readl(host->base + MMCISTATUS); + +@@ -1619,9 +1630,12 @@ static irqreturn_t mmci_irq(int irq, void *dev_id) + } + + /* +- * Don't poll for busy completion in irq context. ++ * Don't poll for: ++ * -busy completion in irq context. ++ * -cmd without busy response check like cmd11 + */ +- if (host->variant->busy_detect && host->busy_status) ++ if (host->variant->busy_detect && ++ (!busy_resp || host->busy_status)) + status &= ~host->variant->busy_detect_flag; + + ret = 1; +@@ -1796,6 +1810,8 @@ static int mmci_get_cd(struct mmc_host *mmc) + + static int mmci_sig_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios) + { ++ struct mmci_host *host = mmc_priv(mmc); ++ unsigned long flags; + int ret = 0; + + if (!IS_ERR(mmc->supply.vqmmc)) { +@@ -1808,6 +1824,28 @@ static int mmci_sig_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios) + case MMC_SIGNAL_VOLTAGE_180: + ret = regulator_set_voltage(mmc->supply.vqmmc, + 1700000, 1950000); ++ ++ if (ret) ++ break; ++ ++ if (host->variant->quirks & MMCI_QUIRK_STM32_VSWITCH) { ++ u32 status; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ mmci_write_pwrreg(host, host->pwr_reg | ++ MCI_STM32_VSWITCH); ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++ ++ /* wait voltage switch completion while 10ms */ ++ ret = readl_relaxed_poll_timeout( ++ host->base + MMCISTATUS, ++ status, ++ (status & MCI_STM32_VSWEND), ++ 10, 10000); ++ } ++ + break; + case MMC_SIGNAL_VOLTAGE_120: + ret = regulator_set_voltage(mmc->supply.vqmmc, +@@ -1822,6 +1860,16 @@ static int mmci_sig_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios) + return ret; + } + ++static int mmci_execute_tuning(struct mmc_host *mmc, u32 opcode) ++{ ++ struct mmci_host *host = mmc_priv(mmc); ++ ++ if (host->ops && host->ops->execute_tuning) ++ return host->ops->execute_tuning(mmc, opcode); ++ ++ return -EINVAL; ++} ++ + static struct mmc_host_ops mmci_ops = { + .request = mmci_request, + .pre_req = mmci_pre_request, +@@ -1830,6 +1878,7 @@ static struct mmc_host_ops mmci_ops = { + .get_ro = mmc_gpio_get_ro, + .get_cd = mmci_get_cd, + .start_signal_voltage_switch = mmci_sig_volt_switch, ++ .execute_tuning = mmci_execute_tuning, + }; + + static int mmci_of_parse(struct device_node *np, struct mmc_host *mmc) +diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h +index 55867fc..e10093e 100644 +--- a/drivers/mmc/host/mmci.h ++++ b/drivers/mmc/host/mmci.h +@@ -170,6 +170,7 @@ + #define MCI_STM32_DPSMACTIVE BIT(12) + #define MCI_STM32_BUSYD0 BIT(20) + #define MCI_STM32_BUSYD0END BIT(21) ++#define MCI_STM32_VSWEND BIT(25) + + #define MMCICLEAR 0x038 + #define MCI_CMDCRCFAILCLR (1 << 0) +@@ -364,8 +365,9 @@ struct variant_data { + void (*init)(struct mmci_host *host); + }; + +-#define MMCI_QUIRK_STM32_DTMODE BIT(0) +-#define MMCI_QUIRK_ST_SDIO BIT(2) /* enable ST specific SDIO logic */ ++#define MMCI_QUIRK_STM32_DTMODE BIT(0) ++#define MMCI_QUIRK_ST_SDIO BIT(2) /* enable ST specific SDIO logic */ ++#define MMCI_QUIRK_STM32_VSWITCH BIT(3) + + /* mmci variant callbacks */ + struct mmci_host_ops { +@@ -382,6 +384,7 @@ struct mmci_host_ops { + void (*dma_error)(struct mmci_host *host); + void (*set_clkreg)(struct mmci_host *host, unsigned int desired); + void (*set_pwrreg)(struct mmci_host *host, unsigned int pwr); ++ int (*execute_tuning)(struct mmc_host *mmc, u32 opcode); + }; + + struct mmci_host { +@@ -414,6 +417,7 @@ struct mmci_host { + struct mmci_platform_data *plat; + struct mmci_host_ops *ops; + struct variant_data *variant; ++ void *variant_priv; + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_opendrain; +diff --git a/drivers/mmc/host/mmci_stm32_sdmmc.c b/drivers/mmc/host/mmci_stm32_sdmmc.c +index cfbfc6f..e5ccc68 100644 +--- a/drivers/mmc/host/mmci_stm32_sdmmc.c ++++ b/drivers/mmc/host/mmci_stm32_sdmmc.c +@@ -3,14 +3,31 @@ + * Copyright (C) STMicroelectronics 2018 - All Rights Reserved + * Author: Ludovic.barre@st.com for STMicroelectronics. + */ ++#include + #include + #include ++#include + #include + #include ++#include + #include + #include + #include "mmci.h" + ++#define DLYB_CR 0x0 ++#define DLYB_CR_DEN BIT(0) ++#define DLYB_CR_SEN BIT(1) ++ ++#define DLYB_CFGR 0x4 ++#define DLYB_CFGR_SEL_MASK GENMASK(3, 0) ++#define DLYB_CFGR_UNIT_MASK GENMASK(14, 8) ++#define DLYB_CFGR_LNG_MASK GENMASK(27, 16) ++#define DLYB_CFGR_LNGF BIT(31) ++ ++#define DLYB_NB_DELAY 11 ++#define DLYB_CFGR_SEL_MAX (DLYB_NB_DELAY + 1) ++#define DLYB_CFGR_UNIT_MAX 127 ++ + #define SDMMC_LLI_BUF_LEN PAGE_SIZE + #define SDMMC_IDMA_BURST BIT(MMCI_STM32_IDMABNDT_SHIFT) + +@@ -20,11 +37,17 @@ struct sdmmc_lli_desc { + u32 idmasize; + }; + +-struct sdmmc_priv { ++struct sdmmc_idma { + dma_addr_t sg_dma; + void *sg_cpu; + }; + ++struct sdmmc_dlyb { ++ void __iomem *base; ++ u32 unit; ++ u32 max; ++}; ++ + int sdmmc_idma_validate_data(struct mmci_host *host, + struct mmc_data *data) + { +@@ -36,8 +59,8 @@ int sdmmc_idma_validate_data(struct mmci_host *host, + * excepted the last element which has no constraint on idmasize + */ + for_each_sg(data->sg, sg, data->sg_len - 1, i) { +- if (!IS_ALIGNED(sg_dma_address(data->sg), sizeof(u32)) || +- !IS_ALIGNED(sg_dma_len(data->sg), SDMMC_IDMA_BURST)) { ++ if (!IS_ALIGNED(data->sg->offset, sizeof(u32)) || ++ !IS_ALIGNED(data->sg->length, SDMMC_IDMA_BURST)) { + dev_err(mmc_dev(host->mmc), + "unaligned scatterlist: ofst:%x length:%d\n", + data->sg->offset, data->sg->length); +@@ -45,7 +68,7 @@ int sdmmc_idma_validate_data(struct mmci_host *host, + } + } + +- if (!IS_ALIGNED(sg_dma_address(data->sg), sizeof(u32))) { ++ if (!IS_ALIGNED(data->sg->offset, sizeof(u32))) { + dev_err(mmc_dev(host->mmc), + "unaligned last scatterlist: ofst:%x length:%d\n", + data->sg->offset, data->sg->length); +@@ -92,7 +115,7 @@ static void sdmmc_idma_unprep_data(struct mmci_host *host, + + static int sdmmc_idma_setup(struct mmci_host *host) + { +- struct sdmmc_priv *idma; ++ struct sdmmc_idma *idma; + + idma = devm_kzalloc(mmc_dev(host->mmc), sizeof(*idma), GFP_KERNEL); + if (!idma) +@@ -123,7 +146,7 @@ static int sdmmc_idma_setup(struct mmci_host *host) + static int sdmmc_idma_start(struct mmci_host *host, unsigned int *datactrl) + + { +- struct sdmmc_priv *idma = host->dma_priv; ++ struct sdmmc_idma *idma = host->dma_priv; + struct sdmmc_lli_desc *desc = (struct sdmmc_lli_desc *)idma->sg_cpu; + struct mmc_data *data = host->data; + struct scatterlist *sg; +@@ -226,12 +249,24 @@ static void mmci_sdmmc_set_clkreg(struct mmci_host *host, unsigned int desired) + mmci_write_clkreg(host, clk); + } + ++static void sdmmc_dlyb_input_ck(struct sdmmc_dlyb *dlyb) ++{ ++ if (!dlyb || !dlyb->base) ++ return; ++ ++ /* Output clock = Input clock */ ++ writel_relaxed(0, dlyb->base + DLYB_CR); ++} ++ + static void mmci_sdmmc_set_pwrreg(struct mmci_host *host, unsigned int pwr) + { + struct mmc_ios ios = host->mmc->ios; ++ struct sdmmc_dlyb *dlyb = host->variant_priv; + + pwr = host->pwr_reg_add; + ++ sdmmc_dlyb_input_ck(dlyb); ++ + if (ios.power_mode == MMC_POWER_OFF) { + /* Only a reset could power-off sdmmc */ + reset_control_assert(host->rst); +@@ -265,6 +300,105 @@ static void mmci_sdmmc_set_pwrreg(struct mmci_host *host, unsigned int pwr) + } + } + ++static void sdmmc_dlyb_set_cfgr(struct sdmmc_dlyb *dlyb, ++ int unit, int phase, bool sampler) ++{ ++ u32 cr, cfgr; ++ ++ writel_relaxed(DLYB_CR_SEN, dlyb->base + DLYB_CR); ++ ++ cfgr = FIELD_PREP(DLYB_CFGR_UNIT_MASK, unit) | ++ FIELD_PREP(DLYB_CFGR_SEL_MASK, phase); ++ writel_relaxed(cfgr, dlyb->base + DLYB_CFGR); ++ ++ cr = DLYB_CR_DEN; ++ if (sampler) ++ cr |= DLYB_CR_SEN; ++ ++ writel_relaxed(cr, dlyb->base + DLYB_CR); ++} ++ ++static int sdmmc_dlyb_lng_tuning(struct mmci_host *host) ++{ ++ struct sdmmc_dlyb *dlyb = host->variant_priv; ++ u32 cfgr; ++ int i, lng, ret; ++ ++ for (i = 0; i <= DLYB_CFGR_UNIT_MAX; i++) { ++ sdmmc_dlyb_set_cfgr(dlyb, i, DLYB_CFGR_SEL_MAX, true); ++ ++ ret = readl_relaxed_poll_timeout(dlyb->base + DLYB_CFGR, cfgr, ++ (cfgr & DLYB_CFGR_LNGF), ++ 1, 1000); ++ if (ret) { ++ dev_warn(mmc_dev(host->mmc), ++ "delay line cfg timeout unit:%d cfgr:%d\n", ++ i, cfgr); ++ continue; ++ } ++ ++ lng = FIELD_GET(DLYB_CFGR_LNG_MASK, cfgr); ++ if (lng < BIT(DLYB_NB_DELAY) && lng > 0) ++ break; ++ } ++ ++ if (i > DLYB_CFGR_UNIT_MAX) ++ return -EINVAL; ++ ++ dlyb->unit = i; ++ dlyb->max = __fls(lng); ++ ++ return 0; ++} ++ ++static int sdmmc_dlyb_phase_tuning(struct mmci_host *host, u32 opcode) ++{ ++ struct sdmmc_dlyb *dlyb = host->variant_priv; ++ int cur_len = 0, max_len = 0, end_of_len = 0; ++ int phase; ++ ++ for (phase = 0; phase <= dlyb->max; phase++) { ++ sdmmc_dlyb_set_cfgr(dlyb, dlyb->unit, phase, false); ++ ++ if (mmc_send_tuning(host->mmc, opcode, NULL)) { ++ cur_len = 0; ++ } else { ++ cur_len++; ++ if (cur_len > max_len) { ++ max_len = cur_len; ++ end_of_len = phase; ++ } ++ } ++ } ++ ++ if (!max_len) { ++ dev_err(mmc_dev(host->mmc), "no tuning point found\n"); ++ return -EINVAL; ++ } ++ ++ phase = end_of_len - max_len / 2; ++ sdmmc_dlyb_set_cfgr(dlyb, dlyb->unit, phase, false); ++ ++ dev_dbg(mmc_dev(host->mmc), "unit:%d max_dly:%d phase:%d\n", ++ dlyb->unit, dlyb->max, phase); ++ ++ return 0; ++} ++ ++static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode) ++{ ++ struct mmci_host *host = mmc_priv(mmc); ++ struct sdmmc_dlyb *dlyb = host->variant_priv; ++ ++ if (!dlyb || !dlyb->base) ++ return -EINVAL; ++ ++ if (sdmmc_dlyb_lng_tuning(host)) ++ return -EINVAL; ++ ++ return sdmmc_dlyb_phase_tuning(host, opcode); ++} ++ + static struct mmci_host_ops sdmmc_variant_ops = { + .validate_data = sdmmc_idma_validate_data, + .prep_data = sdmmc_idma_prep_data, +@@ -274,9 +408,25 @@ static struct mmci_host_ops sdmmc_variant_ops = { + .dma_finalize = sdmmc_idma_finalize, + .set_clkreg = mmci_sdmmc_set_clkreg, + .set_pwrreg = mmci_sdmmc_set_pwrreg, ++ .execute_tuning = sdmmc_execute_tuning, + }; + + void sdmmc_variant_init(struct mmci_host *host) + { ++ struct device_node *np = host->mmc->parent->of_node; ++ void __iomem *base_dlyb; ++ struct sdmmc_dlyb *dlyb; ++ + host->ops = &sdmmc_variant_ops; ++ ++ base_dlyb = devm_of_iomap(mmc_dev(host->mmc), np, 1, NULL); ++ if (IS_ERR(base_dlyb)) ++ return; ++ ++ dlyb = devm_kzalloc(mmc_dev(host->mmc), sizeof(*dlyb), GFP_KERNEL); ++ if (!dlyb) ++ return; ++ ++ dlyb->base = base_dlyb; ++ host->variant_priv = dlyb; + } +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0040-ARM-stm32mp1-r0-rc3-PINCTRL-PWM-RESET-RTC.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0040-ARM-stm32mp1-r0-rc3-PINCTRL-PWM-RESET-RTC.patch new file mode 100644 index 0000000..218a514 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0040-ARM-stm32mp1-r0-rc3-PINCTRL-PWM-RESET-RTC.patch @@ -0,0 +1,604 @@ +From d00006c83840de8fa5a6049ce31117d4e9c76184 Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Mon, 10 Dec 2018 15:40:47 +0100 +Subject: [PATCH 40/52] ARM: stm32mp1-r0-rc3: PINCTRL PWM RESET RTC + +--- + drivers/pinctrl/stm32/pinctrl-stm32.c | 219 +++++++++++++++++++++++----------- + drivers/regulator/stpmic1_regulator.c | 17 +-- + 2 files changed, 152 insertions(+), 84 deletions(-) + +diff --git a/drivers/pinctrl/stm32/pinctrl-stm32.c b/drivers/pinctrl/stm32/pinctrl-stm32.c +index 914bee4..7a27431 100644 +--- a/drivers/pinctrl/stm32/pinctrl-stm32.c ++++ b/drivers/pinctrl/stm32/pinctrl-stm32.c +@@ -7,6 +7,7 @@ + * Heavily based on Mediatek's pinctrl driver + */ + #include ++#include + #include + #include + #include +@@ -65,7 +66,8 @@ + #define gpio_range_to_bank(chip) \ + container_of(chip, struct stm32_gpio_bank, range) + +-#define HWSPINLOCK_TIMEOUT 5 /* msec */ ++#define HWSPNLCK_TIMEOUT 1000 /* usec */ ++#define HWSPNLCK_RETRY_DELAY 100 /* usec */ + + static const char * const stm32_gpio_functions[] = { + "gpio", "af0", "af1", +@@ -110,6 +112,8 @@ struct stm32_pinctrl { + struct irq_domain *domain; + struct regmap *regmap; + struct regmap_field *irqmux[STM32_GPIO_PINS_PER_BANK]; ++ u16 irqmux_map; ++ spinlock_t irqmux_lock; /* interrupt mux lock */ + struct stm32_desc_pin *pins; + u32 npins; + u32 pkg; +@@ -150,6 +154,26 @@ static inline u32 stm32_gpio_get_alt(u32 function) + return 0; + } + ++static int stm32_pctrl_hwspin_lock_timeout(struct hwspinlock *hwlock) ++{ ++ int ret, timeout = 0; ++ ++ /* ++ * Use the x_raw API since we are under spin_lock protection and do not ++ * use the x_timeout API because we are under irq_disable mode ++ */ ++ do { ++ ret = hwspin_trylock_raw(hwlock); ++ if (!ret) ++ return ret; ++ ++ udelay(HWSPNLCK_RETRY_DELAY); ++ timeout += HWSPNLCK_RETRY_DELAY; ++ } while (timeout < HWSPNLCK_TIMEOUT); ++ ++ return ret == -EBUSY ? -ETIMEDOUT : ret; ++} ++ + /* GPIO functions */ + + static inline void __stm32_gpio_set(struct stm32_gpio_bank *bank, +@@ -326,9 +350,40 @@ static int stm32_gpio_domain_activate(struct irq_domain *d, + { + struct stm32_gpio_bank *bank = d->host_data; + struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent); ++ unsigned long flags; ++ int ret = 0; ++ ++ /* ++ * gpio irq mux is shared between several banks, a lock has to be done ++ * to avoid overriding. ++ */ ++ spin_lock_irqsave(&pctl->irqmux_lock, flags); ++ ++ if (pctl->irqmux_map & BIT(irq_data->hwirq)) { ++ dev_err(pctl->dev, "irq line %ld already requested.\n", ++ irq_data->hwirq); ++ ret = -EBUSY; ++ goto unlock; ++ } else { ++ pctl->irqmux_map |= BIT(irq_data->hwirq); ++ } + + regmap_field_write(pctl->irqmux[irq_data->hwirq], bank->bank_ioport_nr); +- return 0; ++unlock: ++ spin_unlock_irqrestore(&pctl->irqmux_lock, flags); ++ return ret; ++} ++ ++static void stm32_gpio_domain_deactivate(struct irq_domain *d, ++ struct irq_data *irq_data) ++{ ++ struct stm32_gpio_bank *bank = d->host_data; ++ struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&pctl->irqmux_lock, flags); ++ pctl->irqmux_map &= ~BIT(irq_data->hwirq); ++ spin_unlock_irqrestore(&pctl->irqmux_lock, flags); + } + + static int stm32_gpio_domain_alloc(struct irq_domain *d, +@@ -357,6 +412,7 @@ static const struct irq_domain_ops stm32_gpio_domain_ops = { + .alloc = stm32_gpio_domain_alloc, + .free = irq_domain_free_irqs_common, + .activate = stm32_gpio_domain_activate, ++ .deactivate = stm32_gpio_domain_deactivate, + }; + + /* Pinctrl functions */ +@@ -436,7 +492,7 @@ static int stm32_pctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, + unsigned int num_configs; + bool has_config = 0; + unsigned reserve = 0; +- int num_pins, num_funcs, maps_per_pin, i, err; ++ int num_pins, num_funcs, maps_per_pin, i, err = 0; + + pctl = pinctrl_dev_get_drvdata(pctldev); + +@@ -463,41 +519,45 @@ static int stm32_pctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, + if (has_config && num_pins >= 1) + maps_per_pin++; + +- if (!num_pins || !maps_per_pin) +- return -EINVAL; ++ if (!num_pins || !maps_per_pin) { ++ err = -EINVAL; ++ goto exit; ++ } + + reserve = num_pins * maps_per_pin; + + err = pinctrl_utils_reserve_map(pctldev, map, + reserved_maps, num_maps, reserve); + if (err) +- return err; ++ goto exit; + + for (i = 0; i < num_pins; i++) { + err = of_property_read_u32_index(node, "pinmux", + i, &pinfunc); + if (err) +- return err; ++ goto exit; + + pin = STM32_GET_PIN_NO(pinfunc); + func = STM32_GET_PIN_FUNC(pinfunc); + + if (!stm32_pctrl_is_function_valid(pctl, pin, func)) { + dev_err(pctl->dev, "invalid function.\n"); +- return -EINVAL; ++ err = -EINVAL; ++ goto exit; + } + + grp = stm32_pctrl_find_group_by_pin(pctl, pin); + if (!grp) { + dev_err(pctl->dev, "unable to match pin %d to group\n", + pin); +- return -EINVAL; ++ err = -EINVAL; ++ goto exit; + } + + err = stm32_pctrl_dt_node_to_map_func(pctl, pin, func, grp, map, + reserved_maps, num_maps); + if (err) +- return err; ++ goto exit; + + if (has_config) { + err = pinctrl_utils_add_map_configs(pctldev, map, +@@ -505,11 +565,13 @@ static int stm32_pctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, + configs, num_configs, + PIN_MAP_TYPE_CONFIGS_GROUP); + if (err) +- return err; ++ goto exit; + } + } + +- return 0; ++exit: ++ kfree(configs); ++ return err; + } + + static int stm32_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, +@@ -599,8 +661,8 @@ static int stm32_pmx_get_func_groups(struct pinctrl_dev *pctldev, + return 0; + } + +-static void stm32_pmx_set_mode(struct stm32_gpio_bank *bank, +- int pin, u32 mode, u32 alt) ++static int stm32_pmx_set_mode(struct stm32_gpio_bank *bank, ++ int pin, u32 mode, u32 alt) + { + struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent); + u32 val; +@@ -612,12 +674,12 @@ static void stm32_pmx_set_mode(struct stm32_gpio_bank *bank, + clk_enable(bank->clk); + spin_lock_irqsave(&bank->lock, flags); + +- if (pctl->hwlock) +- err = hwspin_lock_timeout(pctl->hwlock, HWSPINLOCK_TIMEOUT); +- +- if (err) { +- dev_err(pctl->dev, "Can't get hwspinlock\n"); +- goto unlock; ++ if (pctl->hwlock) { ++ err = stm32_pctrl_hwspin_lock_timeout(pctl->hwlock); ++ if (err) { ++ dev_err(pctl->dev, "Can't get hwspinlock\n"); ++ goto unlock; ++ } + } + + val = readl_relaxed(bank->base + alt_offset); +@@ -633,11 +695,12 @@ static void stm32_pmx_set_mode(struct stm32_gpio_bank *bank, + stm32_gpio_backup_mode(bank, pin, mode, alt); + + if (pctl->hwlock) +- hwspin_unlock(pctl->hwlock); ++ hwspin_unlock_raw(pctl->hwlock); + + unlock: + spin_unlock_irqrestore(&bank->lock, flags); + clk_disable(bank->clk); ++ return err; + } + + void stm32_pmx_get_mode(struct stm32_gpio_bank *bank, int pin, u32 *mode, +@@ -694,9 +757,7 @@ static int stm32_pmx_set_mux(struct pinctrl_dev *pctldev, + mode = stm32_gpio_get_mode(function); + alt = stm32_gpio_get_alt(function); + +- stm32_pmx_set_mode(bank, pin, mode, alt); +- +- return 0; ++ return stm32_pmx_set_mode(bank, pin, mode, alt); + } + + static int stm32_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, +@@ -706,9 +767,7 @@ static int stm32_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, + struct stm32_gpio_bank *bank = gpiochip_get_data(range->gc); + int pin = stm32_gpio_pin(gpio); + +- stm32_pmx_set_mode(bank, pin, !input, 0); +- +- return 0; ++ return stm32_pmx_set_mode(bank, pin, !input, 0); + } + + static const struct pinmux_ops stm32_pmx_ops = { +@@ -722,8 +781,8 @@ static const struct pinmux_ops stm32_pmx_ops = { + + /* Pinconf functions */ + +-static void stm32_pconf_set_driving(struct stm32_gpio_bank *bank, +- unsigned offset, u32 drive) ++static int stm32_pconf_set_driving(struct stm32_gpio_bank *bank, ++ unsigned offset, u32 drive) + { + struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent); + unsigned long flags; +@@ -733,12 +792,12 @@ static void stm32_pconf_set_driving(struct stm32_gpio_bank *bank, + clk_enable(bank->clk); + spin_lock_irqsave(&bank->lock, flags); + +- if (pctl->hwlock) +- err = hwspin_lock_timeout(pctl->hwlock, HWSPINLOCK_TIMEOUT); +- +- if (err) { +- dev_err(pctl->dev, "Can't get hwspinlock\n"); +- goto unlock; ++ if (pctl->hwlock) { ++ err = stm32_pctrl_hwspin_lock_timeout(pctl->hwlock); ++ if (err) { ++ dev_err(pctl->dev, "Can't get hwspinlock\n"); ++ goto unlock; ++ } + } + + val = readl_relaxed(bank->base + STM32_GPIO_TYPER); +@@ -749,11 +808,12 @@ static void stm32_pconf_set_driving(struct stm32_gpio_bank *bank, + stm32_gpio_backup_driving(bank, offset, drive); + + if (pctl->hwlock) +- hwspin_unlock(pctl->hwlock); ++ hwspin_unlock_raw(pctl->hwlock); + + unlock: + spin_unlock_irqrestore(&bank->lock, flags); + clk_disable(bank->clk); ++ return err; + } + + static u32 stm32_pconf_get_driving(struct stm32_gpio_bank *bank, +@@ -774,8 +834,8 @@ static u32 stm32_pconf_get_driving(struct stm32_gpio_bank *bank, + return (val >> offset); + } + +-static void stm32_pconf_set_speed(struct stm32_gpio_bank *bank, +- unsigned offset, u32 speed) ++static int stm32_pconf_set_speed(struct stm32_gpio_bank *bank, ++ unsigned offset, u32 speed) + { + struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent); + unsigned long flags; +@@ -785,12 +845,12 @@ static void stm32_pconf_set_speed(struct stm32_gpio_bank *bank, + clk_enable(bank->clk); + spin_lock_irqsave(&bank->lock, flags); + +- if (pctl->hwlock) +- err = hwspin_lock_timeout(pctl->hwlock, HWSPINLOCK_TIMEOUT); +- +- if (err) { +- dev_err(pctl->dev, "Can't get hwspinlock\n"); +- goto unlock; ++ if (pctl->hwlock) { ++ err = stm32_pctrl_hwspin_lock_timeout(pctl->hwlock); ++ if (err) { ++ dev_err(pctl->dev, "Can't get hwspinlock\n"); ++ goto unlock; ++ } + } + + val = readl_relaxed(bank->base + STM32_GPIO_SPEEDR); +@@ -801,11 +861,12 @@ static void stm32_pconf_set_speed(struct stm32_gpio_bank *bank, + stm32_gpio_backup_speed(bank, offset, speed); + + if (pctl->hwlock) +- hwspin_unlock(pctl->hwlock); ++ hwspin_unlock_raw(pctl->hwlock); + + unlock: + spin_unlock_irqrestore(&bank->lock, flags); + clk_disable(bank->clk); ++ return err; + } + + static u32 stm32_pconf_get_speed(struct stm32_gpio_bank *bank, +@@ -826,8 +887,8 @@ static u32 stm32_pconf_get_speed(struct stm32_gpio_bank *bank, + return (val >> (offset * 2)); + } + +-static void stm32_pconf_set_bias(struct stm32_gpio_bank *bank, +- unsigned offset, u32 bias) ++static int stm32_pconf_set_bias(struct stm32_gpio_bank *bank, ++ unsigned offset, u32 bias) + { + struct stm32_pinctrl *pctl = dev_get_drvdata(bank->gpio_chip.parent); + unsigned long flags; +@@ -837,12 +898,12 @@ static void stm32_pconf_set_bias(struct stm32_gpio_bank *bank, + clk_enable(bank->clk); + spin_lock_irqsave(&bank->lock, flags); + +- if (pctl->hwlock) +- err = hwspin_lock_timeout(pctl->hwlock, HWSPINLOCK_TIMEOUT); +- +- if (err) { +- dev_err(pctl->dev, "Can't get hwspinlock\n"); +- goto unlock; ++ if (pctl->hwlock) { ++ err = stm32_pctrl_hwspin_lock_timeout(pctl->hwlock); ++ if (err) { ++ dev_err(pctl->dev, "Can't get hwspinlock\n"); ++ goto unlock; ++ } + } + + val = readl_relaxed(bank->base + STM32_GPIO_PUPDR); +@@ -853,11 +914,12 @@ static void stm32_pconf_set_bias(struct stm32_gpio_bank *bank, + stm32_gpio_backup_bias(bank, offset, bias); + + if (pctl->hwlock) +- hwspin_unlock(pctl->hwlock); ++ hwspin_unlock_raw(pctl->hwlock); + + unlock: + spin_unlock_irqrestore(&bank->lock, flags); + clk_disable(bank->clk); ++ return err; + } + + static u32 stm32_pconf_get_bias(struct stm32_gpio_bank *bank, +@@ -920,22 +982,22 @@ static int stm32_pconf_parse_conf(struct pinctrl_dev *pctldev, + + switch (param) { + case PIN_CONFIG_DRIVE_PUSH_PULL: +- stm32_pconf_set_driving(bank, offset, 0); ++ ret = stm32_pconf_set_driving(bank, offset, 0); + break; + case PIN_CONFIG_DRIVE_OPEN_DRAIN: +- stm32_pconf_set_driving(bank, offset, 1); ++ ret = stm32_pconf_set_driving(bank, offset, 1); + break; + case PIN_CONFIG_SLEW_RATE: +- stm32_pconf_set_speed(bank, offset, arg); ++ ret = stm32_pconf_set_speed(bank, offset, arg); + break; + case PIN_CONFIG_BIAS_DISABLE: +- stm32_pconf_set_bias(bank, offset, 0); ++ ret = stm32_pconf_set_bias(bank, offset, 0); + break; + case PIN_CONFIG_BIAS_PULL_UP: +- stm32_pconf_set_bias(bank, offset, 1); ++ ret = stm32_pconf_set_bias(bank, offset, 1); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: +- stm32_pconf_set_bias(bank, offset, 2); ++ ret = stm32_pconf_set_bias(bank, offset, 2); + break; + case PIN_CONFIG_OUTPUT: + __stm32_gpio_set(bank, offset, arg); +@@ -1295,6 +1357,8 @@ int stm32_pctl_probe(struct platform_device *pdev) + pctl->hwlock = hwspin_lock_request_specific(hwlock_id); + } + ++ spin_lock_init(&pctl->irqmux_lock); ++ + pctl->dev = dev; + pctl->match_data = match->data; + +@@ -1414,22 +1478,23 @@ void stm32_gpio_backup_bias(struct stm32_gpio_bank *bank, + bank->pin_backup[offset] |= bias << STM32_GPIO_BKP_PUPD_SHIFT; + } + +-void stm32_pinctrl_restore_gpio_regs(struct stm32_pinctrl *pctl, u32 pin) ++int stm32_pinctrl_restore_gpio_regs(struct stm32_pinctrl *pctl, u32 pin) + { + const struct pin_desc *desc = pin_desc_get(pctl->pctl_dev, pin); + struct pinctrl_gpio_range *range; + struct stm32_gpio_bank *bank; + u32 val, alt, mode, offset = stm32_gpio_pin(pin); + bool pin_is_irq; ++ int ret; + + range = pinctrl_find_gpio_range_from_pin(pctl->pctl_dev, pin); + if (!range) +- return; ++ return 0; + + pin_is_irq = gpiochip_line_is_irq(range->gc, offset); + + if (!desc || (!pin_is_irq && !desc->gpio_owner)) +- return; ++ return 0; + + bank = gpiochip_get_data(range->gc); + +@@ -1438,7 +1503,10 @@ void stm32_pinctrl_restore_gpio_regs(struct stm32_pinctrl *pctl, u32 pin) + mode = bank->pin_backup[offset] & STM32_GPIO_BKP_MODE_MASK; + mode >>= STM32_GPIO_BKP_MODE_SHIFT; + +- stm32_pmx_set_mode(bank, offset, mode, alt); ++ ret = stm32_pmx_set_mode(bank, offset, mode, alt); ++ if (ret) ++ return ret; ++ + if (mode == 1) { + val = bank->pin_backup[offset] & BIT(STM32_GPIO_BKP_VAL); + val = val >> STM32_GPIO_BKP_VAL; +@@ -1447,28 +1515,39 @@ void stm32_pinctrl_restore_gpio_regs(struct stm32_pinctrl *pctl, u32 pin) + + val = bank->pin_backup[offset] & BIT(STM32_GPIO_BKP_TYPE); + val >>= STM32_GPIO_BKP_TYPE; +- stm32_pconf_set_driving(bank, offset, val); ++ ret = stm32_pconf_set_driving(bank, offset, val); ++ if (ret) ++ return ret; + + val = bank->pin_backup[offset] & STM32_GPIO_BKP_SPEED_MASK; + val >>= STM32_GPIO_BKP_SPEED_SHIFT; +- stm32_pconf_set_speed(bank, offset, val); ++ ret = stm32_pconf_set_speed(bank, offset, val); ++ if (ret) ++ return ret; + + val = bank->pin_backup[offset] & STM32_GPIO_BKP_PUPD_MASK; + val >>= STM32_GPIO_BKP_PUPD_SHIFT; +- stm32_pconf_set_bias(bank, offset, val); ++ ret = stm32_pconf_set_bias(bank, offset, val); ++ if (ret) ++ return ret; + + if (pin_is_irq) + regmap_field_write(pctl->irqmux[offset], bank->bank_ioport_nr); ++ ++ return 0; + } + + int stm32_pinctrl_resume(struct device *dev) + { + struct stm32_pinctrl *pctl = dev_get_drvdata(dev); + struct stm32_pinctrl_group *g = pctl->groups; +- int i; ++ int i, ret; + +- for (i = g->pin; i < g->pin + pctl->ngroups; i++) +- stm32_pinctrl_restore_gpio_regs(pctl, i); ++ for (i = g->pin; i < g->pin + pctl->ngroups; i++) { ++ ret = stm32_pinctrl_restore_gpio_regs(pctl, i); ++ if (ret) ++ return ret; ++ } + + return 0; + } +diff --git a/drivers/regulator/stpmic1_regulator.c b/drivers/regulator/stpmic1_regulator.c +index 96f1808..31c960c 100644 +--- a/drivers/regulator/stpmic1_regulator.c ++++ b/drivers/regulator/stpmic1_regulator.c +@@ -76,8 +76,9 @@ enum { + #define STPMIC1_BUCK_MODE_LP BUCK_HPLP_ENABLE_MASK + + struct regulator_linear_range buck1_ranges[] = { +- REGULATOR_LINEAR_RANGE(600000, 0, 30, 25000), +- REGULATOR_LINEAR_RANGE(1350000, 31, 63, 0), ++ REGULATOR_LINEAR_RANGE(725000, 0, 4, 0), ++ REGULATOR_LINEAR_RANGE(725000, 5, 36, 25000), ++ REGULATOR_LINEAR_RANGE(1500000, 37, 63, 0), + }; + + struct regulator_linear_range buck2_ranges[] = { +@@ -157,7 +158,6 @@ static struct regulator_ops stpmic1_ldo_ops = { + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, +- .set_pull_down = regulator_set_pull_down_regmap, + .set_over_current_protection = stpmic1_set_icc, + }; + +@@ -169,7 +169,6 @@ static struct regulator_ops stpmic1_ldo3_ops = { + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, +- .set_pull_down = regulator_set_pull_down_regmap, + .get_bypass = regulator_get_bypass_regmap, + .set_bypass = regulator_set_bypass_regmap, + .set_over_current_protection = stpmic1_set_icc, +@@ -179,7 +178,6 @@ static struct regulator_ops stpmic1_ldo4_fixed_regul_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, +- .set_pull_down = regulator_set_pull_down_regmap, + .set_over_current_protection = stpmic1_set_icc, + }; + +@@ -201,7 +199,6 @@ static struct regulator_ops stpmic1_vref_ddr_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, +- .set_pull_down = regulator_set_pull_down_regmap, + }; + + static struct regulator_ops stpmic1_switch_regul_ops = { +@@ -227,8 +224,6 @@ static struct regulator_ops stpmic1_switch_regul_ops = { + .enable_val = 1, \ + .disable_val = 0, \ + .enable_time = PMIC_ENABLE_TIME_US, \ +- .pull_down_reg = ids##_PULL_DOWN_REG, \ +- .pull_down_mask = ids##_PULL_DOWN_MASK, \ + .supply_name = #base, \ + } + +@@ -252,8 +247,6 @@ static struct regulator_ops stpmic1_switch_regul_ops = { + .bypass_mask = LDO_BYPASS_MASK, \ + .bypass_val_on = LDO_BYPASS_MASK, \ + .bypass_val_off = 0, \ +- .pull_down_reg = ids##_PULL_DOWN_REG, \ +- .pull_down_mask = ids##_PULL_DOWN_MASK, \ + .supply_name = #base, \ + } + +@@ -271,8 +264,6 @@ static struct regulator_ops stpmic1_switch_regul_ops = { + .enable_val = 1, \ + .disable_val = 0, \ + .enable_time = PMIC_ENABLE_TIME_US, \ +- .pull_down_reg = ids##_PULL_DOWN_REG, \ +- .pull_down_mask = ids##_PULL_DOWN_MASK, \ + .supply_name = #base, \ + } + +@@ -312,8 +303,6 @@ static struct regulator_ops stpmic1_switch_regul_ops = { + .enable_val = 1, \ + .disable_val = 0, \ + .enable_time = PMIC_ENABLE_TIME_US, \ +- .pull_down_reg = ids##_PULL_DOWN_REG, \ +- .pull_down_mask = ids##_PULL_DOWN_MASK, \ + .supply_name = #base, \ + } + +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0041-ARM-stm32mp1-r0-rc3-REMOTEPROC-RPMSG.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0041-ARM-stm32mp1-r0-rc3-REMOTEPROC-RPMSG.patch new file mode 100644 index 0000000..44c6cfd --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0041-ARM-stm32mp1-r0-rc3-REMOTEPROC-RPMSG.patch @@ -0,0 +1,1617 @@ +From 0fbfdc3872adc5747402b6b9e91c00708b8ac842 Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Mon, 10 Dec 2018 15:41:19 +0100 +Subject: [PATCH 41/52] ARM: stm32mp1-r0-rc3: REMOTEPROC RPMSG + +--- + .../devicetree/bindings/remoteproc/stm32-rproc.txt | 10 +- + drivers/remoteproc/remoteproc_core.c | 634 ++++++++++++++++----- + drivers/remoteproc/remoteproc_debugfs.c | 22 +- + drivers/remoteproc/remoteproc_internal.h | 12 +- + drivers/remoteproc/remoteproc_virtio.c | 56 +- + drivers/remoteproc/stm32_rproc.c | 228 ++++++-- + drivers/rpmsg/virtio_rpmsg_bus.c | 6 +- + include/linux/remoteproc.h | 36 +- + 8 files changed, 808 insertions(+), 196 deletions(-) + +diff --git a/Documentation/devicetree/bindings/remoteproc/stm32-rproc.txt b/Documentation/devicetree/bindings/remoteproc/stm32-rproc.txt +index 7df6a26..98d1e31e 100644 +--- a/Documentation/devicetree/bindings/remoteproc/stm32-rproc.txt ++++ b/Documentation/devicetree/bindings/remoteproc/stm32-rproc.txt +@@ -5,10 +5,12 @@ boots firmwares on the ST32MP family chipset. + + Required properties: + - compatible: Must be "st,stm32mp1-rproc" +-- reg: Should contain the address ranges for specific internal memory +- regions. +-- reg-names: Should contain the corresponding names for specific internal +- memory regions. ++- ranges: Describe memory addresses translation between Linux processor ++ and the remote processor. ++ Each memory region, is declared with 3 parameters: ++ - param 1: device base address (remote processor address), ++ - param 2: physical base address (Linux Processor address), ++ - param 3: size in Byte of the memory region. + - resets: Reference to a reset controller asserting the remote processor. + - reset-names: Must be "mcu_rst" + - st,syscfg-holdboot: Reference to the system configuration controlling the +diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c +index 21726f0..dd7e348 100644 +--- a/drivers/remoteproc/remoteproc_core.c ++++ b/drivers/remoteproc/remoteproc_core.c +@@ -39,6 +39,8 @@ + #include + #include + #include ++#include ++#include + #include + #include + #include +@@ -54,6 +56,11 @@ typedef int (*rproc_handle_resources_t)(struct rproc *rproc, + typedef int (*rproc_handle_resource_t)(struct rproc *rproc, + void *, int offset, int avail); + ++static int rproc_alloc_carveout(struct rproc *rproc, ++ struct rproc_mem_entry *mem); ++static int rproc_release_carveout(struct rproc *rproc, ++ struct rproc_mem_entry *mem); ++ + /* Unique indices for remoteproc devices */ + static DEFINE_IDA(rproc_dev_index); + +@@ -141,6 +148,23 @@ static void rproc_disable_iommu(struct rproc *rproc) + iommu_domain_free(domain); + } + ++phys_addr_t rproc_va_to_pa(void *cpu_addr) ++{ ++ /* ++ * Return physical address according to virtual address location ++ * - in vmalloc: if region ioremapped or defined as dma_alloc_coherent ++ * - in kernel: if region allocated in generic dma memory pool ++ */ ++ if (is_vmalloc_addr(cpu_addr)) { ++ return page_to_phys(vmalloc_to_page(cpu_addr)) + ++ offset_in_page(cpu_addr); ++ } ++ ++ WARN_ON(!virt_addr_valid(cpu_addr)); ++ return virt_to_phys(cpu_addr); ++} ++EXPORT_SYMBOL(rproc_va_to_pa); ++ + /** + * rproc_da_to_va() - lookup the kernel virtual address for a remoteproc address + * @rproc: handle of a remote processor +@@ -184,6 +208,10 @@ void *rproc_da_to_va(struct rproc *rproc, u64 da, int len) + list_for_each_entry(carveout, &rproc->carveouts, node) { + int offset = da - carveout->da; + ++ /* Verify that carveout is allocated */ ++ if (!carveout->va) ++ continue; ++ + /* try next carveout if da is too small */ + if (offset < 0) + continue; +@@ -202,27 +230,128 @@ void *rproc_da_to_va(struct rproc *rproc, u64 da, int len) + } + EXPORT_SYMBOL(rproc_da_to_va); + ++/** ++ * rproc_find_carveout_by_name() - lookup the carveout region by a name ++ * @rproc: handle of a remote processor ++ * @name,..: carveout name to find (standard printf format) ++ * ++ * Platform driver has the capability to register some pre-allacoted carveout ++ * (physically contiguous memory regions) before rproc firmware loading and ++ * associated resource table analysis. These regions may be dedicated memory ++ * regions internal to the coprocessor or specified DDR region with specific ++ * attributes ++ * ++ * This function is a helper function with which we can go over the ++ * allocated carveouts and return associated region characteristics like ++ * coprocessor address, length or processor virtual address. ++ * ++ * Return: a valid pointer on carveout entry on success or NULL on failure. ++ */ ++struct rproc_mem_entry * ++rproc_find_carveout_by_name(struct rproc *rproc, const char *name, ...) ++{ ++ va_list args; ++ char _name[32]; ++ struct rproc_mem_entry *carveout, *mem = NULL; ++ ++ if (!name) ++ return NULL; ++ ++ va_start(args, name); ++ vsnprintf(_name, sizeof(_name), name, args); ++ va_end(args); ++ ++ list_for_each_entry(carveout, &rproc->carveouts, node) { ++ /* Compare carveout and requested names */ ++ if (!strcmp(carveout->name, _name)) { ++ mem = carveout; ++ break; ++ } ++ } ++ ++ return mem; ++} ++ ++/** ++ * rproc_check_carveout_da() - Check specified carveout da configuration ++ * @rproc: handle of a remote processor ++ * @mem: pointer on carveout to check ++ * @da: area device address ++ * @len: associated area size ++ * ++ * This function is a helper function to verify requested device area (couple ++ * da, len) is part of specified carevout. ++ * ++ * Return: 0 if carveout matchs request else -ENOMEM ++ */ ++static int rproc_check_carveout_da(struct rproc *rproc, ++ struct rproc_mem_entry *mem, u32 da, u32 len) ++{ ++ struct device *dev = &rproc->dev; ++ int delta; ++ ++ /* Check requested resource length */ ++ if (len > mem->len) { ++ dev_err(dev, "Registered carveout doesn't fit len request\n"); ++ return -EINVAL; ++ } ++ ++ if (da != FW_RSC_ADDR_ANY && mem->da == FW_RSC_ADDR_ANY) { ++ /* Address doesn't match registered carveout configuration */ ++ return -EINVAL; ++ } else if (da != FW_RSC_ADDR_ANY && mem->da != FW_RSC_ADDR_ANY) { ++ delta = da - mem->da; ++ ++ /* Check requested resource belongs to registered carveout */ ++ if (delta < 0) { ++ dev_err(dev, ++ "Registered carveout doesn't fit da request\n"); ++ return -EINVAL; ++ } ++ ++ if (delta + len > mem->len) { ++ dev_err(dev, ++ "Registered carveout doesn't fit len request\n"); ++ return -EINVAL; ++ } ++ } ++ ++ return 0; ++} ++ + int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) + { + struct rproc *rproc = rvdev->rproc; + struct device *dev = &rproc->dev; + struct rproc_vring *rvring = &rvdev->vring[i]; + struct fw_rsc_vdev *rsc; +- dma_addr_t dma; +- void *va; + int ret, size, notifyid; ++ struct rproc_mem_entry *mem; + + /* actual size of vring (in bytes) */ + size = PAGE_ALIGN(vring_size(rvring->len, rvring->align)); + +- /* +- * Allocate non-cacheable memory for the vring. In the future +- * this call will also configure the IOMMU for us +- */ +- va = dma_alloc_coherent(dev->parent, size, &dma, GFP_KERNEL); +- if (!va) { +- dev_err(dev->parent, "dma_alloc_coherent failed\n"); +- return -EINVAL; ++ rsc = (void *)rproc->table_ptr + rvdev->rsc_offset; ++ ++ /* Search for pre-registered carveout */ ++ mem = rproc_find_carveout_by_name(rproc, "vdev%dvring%d", rvdev->index, ++ i); ++ if (mem) { ++ if (rproc_check_carveout_da(rproc, mem, rsc->vring[i].da, size)) ++ return -ENOMEM; ++ } else { ++ /* Register carveout in in list */ ++ mem = rproc_mem_entry_init(dev, 0, 0, size, rsc->vring[i].da, ++ rproc_alloc_carveout, ++ rproc_release_carveout, ++ "vdev%dvring%d", ++ rvdev->index, i); ++ if (!mem) { ++ dev_err(dev, "Can't allocate memory entry structure\n"); ++ return -ENOMEM; ++ } ++ ++ rproc_add_carveout(rproc, mem); + } + + /* +@@ -233,7 +362,6 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) + ret = idr_alloc(&rproc->notifyids, rvring, 0, 0, GFP_KERNEL); + if (ret < 0) { + dev_err(dev, "idr_alloc failed: %d\n", ret); +- dma_free_coherent(dev->parent, size, va, dma); + return ret; + } + notifyid = ret; +@@ -242,21 +370,9 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) + if (notifyid > rproc->max_notifyid) + rproc->max_notifyid = notifyid; + +- dev_dbg(dev, "vring%d: va %pK dma %pad size 0x%x idr %d\n", +- i, va, &dma, size, notifyid); +- +- rvring->va = va; +- rvring->dma = dma; + rvring->notifyid = notifyid; + +- /* +- * Let the rproc know the notifyid and da of this vring. +- * Not all platforms use dma_alloc_coherent to automatically +- * set up the iommu. In this case the device address (da) will +- * hold the physical address and not the device address. +- */ +- rsc = (void *)rproc->table_ptr + rvdev->rsc_offset; +- rsc->vring[i].da = dma; ++ /* Let the rproc know the notifyid of this vring.*/ + rsc->vring[i].notifyid = notifyid; + return 0; + } +@@ -288,12 +404,10 @@ rproc_parse_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i) + + void rproc_free_vring(struct rproc_vring *rvring) + { +- int size = PAGE_ALIGN(vring_size(rvring->len, rvring->align)); + struct rproc *rproc = rvring->rvdev->rproc; + int idx = rvring->rvdev->vring - rvring; + struct fw_rsc_vdev *rsc; + +- dma_free_coherent(rproc->dev.parent, size, rvring->va, rvring->dma); + idr_remove(&rproc->notifyids, rvring->notifyid); + + /* reset resource entry info */ +@@ -317,6 +431,20 @@ static void rproc_vdev_do_stop(struct rproc_subdev *subdev, bool crashed) + } + + /** ++ * rproc_rvdev_release() - release the existence of a rvdev ++ * ++ * @dev: the subdevice's dev ++ */ ++static void rproc_rvdev_release(struct device *dev) ++{ ++ struct rproc_vdev *rvdev = container_of(dev, struct rproc_vdev, dev); ++ ++ of_reserved_mem_device_release(dev); ++ ++ kfree(rvdev); ++} ++ ++/** + * rproc_handle_vdev() - handle a vdev fw resource + * @rproc: the remote processor + * @rsc: the vring resource descriptor +@@ -349,6 +477,7 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, + struct device *dev = &rproc->dev; + struct rproc_vdev *rvdev; + int i, ret; ++ char name[16]; + + /* make sure resource isn't truncated */ + if (sizeof(*rsc) + rsc->num_of_vrings * sizeof(struct fw_rsc_vdev_vring) +@@ -380,6 +509,30 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, + + rvdev->id = rsc->id; + rvdev->rproc = rproc; ++ rvdev->index = rproc->nb_vdev++; ++ ++ /* Initialise vdev subdevice */ ++ snprintf(name, sizeof(name), "vdev%dbuffer", rvdev->index); ++ rvdev->dev.parent = rproc->dev.parent; ++ rvdev->dev.release = rproc_rvdev_release; ++ dev_set_name(&rvdev->dev, "%s#%s", dev_name(rvdev->dev.parent), name); ++ dev_set_drvdata(&rvdev->dev, rvdev); ++ ++ ret = device_register(&rvdev->dev); ++ if (ret) { ++ put_device(&rvdev->dev); ++ return ret; ++ } ++ /* Make device dma capable by inheriting from parent's capabilities */ ++ set_dma_ops(&rvdev->dev, get_dma_ops(rproc->dev.parent)); ++ ++ ret = dma_coerce_mask_and_coherent(&rvdev->dev, ++ dma_get_mask(rproc->dev.parent)); ++ if (ret) { ++ dev_warn(dev, ++ "Failed to set DMA mask %llx. Trying to continue... %x\n", ++ dma_get_mask(rproc->dev.parent), ret); ++ } + + /* parse the vrings */ + for (i = 0; i < rsc->num_of_vrings; i++) { +@@ -411,7 +564,7 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, + for (i--; i >= 0; i--) + rproc_free_vring(&rvdev->vring[i]); + free_rvdev: +- kfree(rvdev); ++ device_unregister(&rvdev->dev); + return ret; + } + +@@ -424,15 +577,12 @@ void rproc_vdev_release(struct kref *ref) + + for (id = 0; id < ARRAY_SIZE(rvdev->vring); id++) { + rvring = &rvdev->vring[id]; +- if (!rvring->va) +- continue; +- + rproc_free_vring(rvring); + } + + rproc_remove_subdev(rproc, &rvdev->subdev); + list_del(&rvdev->node); +- kfree(rvdev); ++ device_unregister(&rvdev->dev); + } + + /** +@@ -454,9 +604,8 @@ void rproc_vdev_release(struct kref *ref) + static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc, + int offset, int avail) + { +- struct rproc_mem_entry *trace; ++ struct rproc_debug_trace *trace; + struct device *dev = &rproc->dev; +- void *ptr; + char name[15]; + + if (sizeof(*rsc) > avail) { +@@ -470,28 +619,23 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc, + return -EINVAL; + } + +- /* what's the kernel address of this resource ? */ +- ptr = rproc_da_to_va(rproc, rsc->da, rsc->len); +- if (!ptr) { +- dev_err(dev, "erroneous trace resource entry\n"); +- return -EINVAL; +- } +- + trace = kzalloc(sizeof(*trace), GFP_KERNEL); + if (!trace) + return -ENOMEM; + + /* set the trace buffer dma properties */ +- trace->len = rsc->len; +- trace->va = ptr; ++ trace->trace_mem.len = rsc->len; ++ trace->trace_mem.da = rsc->da; ++ ++ /* set pointer on rproc device */ ++ trace->rproc = rproc; + + /* make sure snprintf always null terminates, even if truncating */ + snprintf(name, sizeof(name), "trace%d", rproc->num_traces); + + /* create the debugfs entry */ +- trace->priv = rproc_create_trace_file(name, rproc, trace); +- if (!trace->priv) { +- trace->va = NULL; ++ trace->tfile = rproc_create_trace_file(name, rproc, trace); ++ if (!trace->tfile) { + kfree(trace); + return -EINVAL; + } +@@ -500,8 +644,8 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc, + + rproc->num_traces++; + +- dev_dbg(dev, "%s added: va %pK, da 0x%x, len 0x%x\n", +- name, ptr, rsc->da, rsc->len); ++ dev_dbg(dev, "%s added: da 0x%x, len 0x%x\n", ++ name, rsc->da, rsc->len); + + return 0; + } +@@ -585,61 +729,43 @@ static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc, + } + + /** +- * rproc_handle_carveout() - handle phys contig memory allocation requests ++ * rproc_alloc_carveout() - allocated specified carveout + * @rproc: rproc handle +- * @rsc: the resource entry +- * @avail: size of available data (for image validation) ++ * @mem: the memory entry to allocate + * +- * This function will handle firmware requests for allocation of physically +- * contiguous memory regions. +- * +- * These request entries should come first in the firmware's resource table, +- * as other firmware entries might request placing other data objects inside +- * these memory regions (e.g. data/code segments, trace resource entries, ...). +- * +- * Allocating memory this way helps utilizing the reserved physical memory +- * (e.g. CMA) more efficiently, and also minimizes the number of TLB entries +- * needed to map it (in case @rproc is using an IOMMU). Reducing the TLB +- * pressure is important; it may have a substantial impact on performance. ++ * This function allocate specified memory entry @mem using ++ * dma_alloc_coherent() as default allocator + */ +-static int rproc_handle_carveout(struct rproc *rproc, +- struct fw_rsc_carveout *rsc, +- int offset, int avail) ++static int rproc_alloc_carveout(struct rproc *rproc, ++ struct rproc_mem_entry *mem) + { +- struct rproc_mem_entry *carveout, *mapping; ++ struct rproc_mem_entry *mapping = NULL; + struct device *dev = &rproc->dev; + dma_addr_t dma; + void *va; + int ret; + +- if (sizeof(*rsc) > avail) { +- dev_err(dev, "carveout rsc is truncated\n"); +- return -EINVAL; +- } +- +- /* make sure reserved bytes are zeroes */ +- if (rsc->reserved) { +- dev_err(dev, "carveout rsc has non zero reserved bytes\n"); +- return -EINVAL; +- } +- +- dev_dbg(dev, "carveout rsc: name: %s, da 0x%x, pa 0x%x, len 0x%x, flags 0x%x\n", +- rsc->name, rsc->da, rsc->pa, rsc->len, rsc->flags); +- +- carveout = kzalloc(sizeof(*carveout), GFP_KERNEL); +- if (!carveout) +- return -ENOMEM; +- +- va = dma_alloc_coherent(dev->parent, rsc->len, &dma, GFP_KERNEL); ++ va = dma_alloc_coherent(dev->parent, mem->len, &dma, GFP_KERNEL); + if (!va) { + dev_err(dev->parent, +- "failed to allocate dma memory: len 0x%x\n", rsc->len); +- ret = -ENOMEM; +- goto free_carv; ++ "failed to allocate dma memory: len 0x%x\n", mem->len); ++ return -ENOMEM; + } + + dev_dbg(dev, "carveout va %pK, dma %pad, len 0x%x\n", +- va, &dma, rsc->len); ++ va, &dma, mem->len); ++ ++ if (mem->da != FW_RSC_ADDR_ANY && !rproc->domain) { ++ /* ++ * Check requested da is equal to dma address ++ * and print a warn message in case of missalignment. ++ * Don't stop rproc_start sequence as coprocessor may ++ * build pa to da translation on its side. ++ */ ++ if (mem->da != (u32)dma) ++ dev_warn(dev->parent, ++ "Allocated carveout doesn't fit device address request\n"); ++ } + + /* + * Ok, this is non-standard. +@@ -658,15 +784,15 @@ static int rproc_handle_carveout(struct rproc *rproc, + * to use the iommu-based DMA API: we expect 'dma' to contain the + * physical address in this case. + */ +- if (rproc->domain) { ++ if (mem->da != FW_RSC_ADDR_ANY && rproc->domain) { + mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); + if (!mapping) { + ret = -ENOMEM; + goto dma_free; + } + +- ret = iommu_map(rproc->domain, rsc->da, dma, rsc->len, +- rsc->flags); ++ ret = iommu_map(rproc->domain, mem->da, dma, mem->len, ++ mem->flags); + if (ret) { + dev_err(dev, "iommu_map failed: %d\n", ret); + goto free_mapping; +@@ -679,52 +805,226 @@ static int rproc_handle_carveout(struct rproc *rproc, + * We can't trust the remote processor not to change the + * resource table, so we must maintain this info independently. + */ +- mapping->da = rsc->da; +- mapping->len = rsc->len; ++ mapping->da = mem->da; ++ mapping->len = mem->len; + list_add_tail(&mapping->node, &rproc->mappings); + + dev_dbg(dev, "carveout mapped 0x%x to %pad\n", +- rsc->da, &dma); ++ mem->da, &dma); + } + +- /* +- * Some remote processors might need to know the pa +- * even though they are behind an IOMMU. E.g., OMAP4's +- * remote M3 processor needs this so it can control +- * on-chip hardware accelerators that are not behind +- * the IOMMU, and therefor must know the pa. +- * +- * Generally we don't want to expose physical addresses +- * if we don't have to (remote processors are generally +- * _not_ trusted), so we might want to do this only for +- * remote processor that _must_ have this (e.g. OMAP4's +- * dual M3 subsystem). +- * +- * Non-IOMMU processors might also want to have this info. +- * In this case, the device address and the physical address +- * are the same. +- */ +- rsc->pa = dma; ++ if (mem->da == FW_RSC_ADDR_ANY) { ++ /* Update device address as undefined by requester */ ++ if (sizeof(dma_addr_t) > sizeof(u32)) ++ dev_warn(dev, "DMA address cast in 32bit to fit resource table format\n"); + +- carveout->va = va; +- carveout->len = rsc->len; +- carveout->dma = dma; +- carveout->da = rsc->da; ++ mem->da = (u32)dma; ++ } + +- list_add_tail(&carveout->node, &rproc->carveouts); ++ mem->dma = dma; ++ mem->va = va; + + return 0; + + free_mapping: + kfree(mapping); + dma_free: +- dma_free_coherent(dev->parent, rsc->len, va, dma); +-free_carv: +- kfree(carveout); ++ dma_free_coherent(dev->parent, mem->len, va, dma); + return ret; + } + +-/* ++/** ++ * rproc_release_carveout() - release acquired carveout ++ * @rproc: rproc handle ++ * @mem: the memory entry to release ++ * ++ * This function releases specified memory entry @mem allocated via ++ * rproc_alloc_carveout() function by @rproc. ++ */ ++static int rproc_release_carveout(struct rproc *rproc, ++ struct rproc_mem_entry *mem) ++{ ++ struct device *dev = &rproc->dev; ++ ++ /* clean up carveout allocations */ ++ dma_free_coherent(dev->parent, mem->len, mem->va, mem->dma); ++ return 0; ++} ++ ++/** ++ * rproc_handle_carveout() - handle phys contig memory allocation requests ++ * @rproc: rproc handle ++ * @rsc: the resource entry ++ * @avail: size of available data (for image validation) ++ * ++ * This function will handle firmware requests for allocation of physically ++ * contiguous memory regions. ++ * ++ * These request entries should come first in the firmware's resource table, ++ * as other firmware entries might request placing other data objects inside ++ * these memory regions (e.g. data/code segments, trace resource entries, ...). ++ * ++ * Allocating memory this way helps utilizing the reserved physical memory ++ * (e.g. CMA) more efficiently, and also minimizes the number of TLB entries ++ * needed to map it (in case @rproc is using an IOMMU). Reducing the TLB ++ * pressure is important; it may have a substantial impact on performance. ++ */ ++static int rproc_handle_carveout(struct rproc *rproc, ++ struct fw_rsc_carveout *rsc, ++ int offset, int avail) ++{ ++ struct rproc_mem_entry *carveout; ++ struct device *dev = &rproc->dev; ++ ++ if (sizeof(*rsc) > avail) { ++ dev_err(dev, "carveout rsc is truncated\n"); ++ return -EINVAL; ++ } ++ ++ /* make sure reserved bytes are zeroes */ ++ if (rsc->reserved) { ++ dev_err(dev, "carveout rsc has non zero reserved bytes\n"); ++ return -EINVAL; ++ } ++ ++ dev_dbg(dev, "carveout rsc: name: %s, da 0x%x, pa 0x%x, len 0x%x, flags 0x%x\n", ++ rsc->name, rsc->da, rsc->pa, rsc->len, rsc->flags); ++ ++ /* ++ * Check carveout rsc already part of a registered carveout, ++ * Search by name, then check the da and length ++ */ ++ carveout = rproc_find_carveout_by_name(rproc, rsc->name); ++ ++ if (carveout) { ++ if (carveout->rsc_offset != FW_RSC_ADDR_ANY) { ++ dev_err(dev, ++ "Carveout already associated to resource table\n"); ++ return -ENOMEM; ++ } ++ ++ if (rproc_check_carveout_da(rproc, carveout, rsc->da, rsc->len)) ++ return -ENOMEM; ++ ++ /* Update memory carveout with resource table info */ ++ carveout->rsc_offset = offset; ++ carveout->flags = rsc->flags; ++ ++ return 0; ++ } ++ ++ /* Register carveout in in list */ ++ carveout = rproc_mem_entry_init(dev, 0, 0, rsc->len, rsc->da, ++ rproc_alloc_carveout, ++ rproc_release_carveout, rsc->name); ++ if (!carveout) { ++ dev_err(dev, "Can't allocate memory entry structure\n"); ++ return -ENOMEM; ++ } ++ ++ carveout->flags = rsc->flags; ++ carveout->rsc_offset = offset; ++ rproc_add_carveout(rproc, carveout); ++ ++ return 0; ++} ++ ++/** ++ * rproc_add_carveout() - register an allocated carveout region ++ * @rproc: rproc handle ++ * @mem: memory entry to register ++ * ++ * This function registers specified memory entry in @rproc carveouts list. ++ * Specified carveout should have been allocated before registering. ++ */ ++void rproc_add_carveout(struct rproc *rproc, struct rproc_mem_entry *mem) ++{ ++ list_add_tail(&mem->node, &rproc->carveouts); ++} ++EXPORT_SYMBOL(rproc_add_carveout); ++ ++/** ++ * rproc_mem_entry_init() - allocate and initialize rproc_mem_entry struct ++ * @dev: pointer on device struct ++ * @va: virtual address ++ * @dma: dma address ++ * @len: memory carveout length ++ * @da: device address ++ * @alloc: memory carveout allocation function ++ * @release: memory carveout release function ++ * @name: carveout name ++ * ++ * This function allocates a rproc_mem_entry struct and fill it with parameters ++ * provided by client. ++ */ ++struct rproc_mem_entry * ++rproc_mem_entry_init(struct device *dev, ++ void *va, dma_addr_t dma, int len, u32 da, ++ int (*alloc)(struct rproc *, struct rproc_mem_entry *), ++ int (*release)(struct rproc *, struct rproc_mem_entry *), ++ const char *name, ...) ++{ ++ struct rproc_mem_entry *mem; ++ va_list args; ++ ++ mem = kzalloc(sizeof(*mem), GFP_KERNEL); ++ if (!mem) ++ return mem; ++ ++ mem->va = va; ++ mem->dma = dma; ++ mem->da = da; ++ mem->len = len; ++ mem->alloc = alloc; ++ mem->release = release; ++ mem->rsc_offset = FW_RSC_ADDR_ANY; ++ mem->of_resm_idx = -1; ++ ++ va_start(args, name); ++ vsnprintf(mem->name, sizeof(mem->name), name, args); ++ va_end(args); ++ ++ return mem; ++} ++EXPORT_SYMBOL(rproc_mem_entry_init); ++ ++/** ++ * rproc_of_resm_mem_entry_init() - allocate and initialize rproc_mem_entry struct ++ * from a reserved memory phandle ++ * @dev: pointer on device struct ++ * @of_resm_idx: reserved memory phandle index in "memory-region" ++ * @len: memory carveout length ++ * @da: device address ++ * @name: carveout name ++ * ++ * This function allocates a rproc_mem_entry struct and fill it with parameters ++ * provided by client. ++ */ ++struct rproc_mem_entry * ++rproc_of_resm_mem_entry_init(struct device *dev, u32 of_resm_idx, int len, ++ u32 da, const char *name, ...) ++{ ++ struct rproc_mem_entry *mem; ++ va_list args; ++ ++ mem = kzalloc(sizeof(*mem), GFP_KERNEL); ++ if (!mem) ++ return mem; ++ ++ mem->da = da; ++ mem->len = len; ++ mem->rsc_offset = FW_RSC_ADDR_ANY; ++ mem->of_resm_idx = of_resm_idx; ++ ++ va_start(args, name); ++ vsnprintf(mem->name, sizeof(mem->name), name, args); ++ va_end(args); ++ ++ return mem; ++} ++EXPORT_SYMBOL(rproc_of_resm_mem_entry_init); ++ ++/** + * A lookup table for resource handlers. The indices are defined in + * enum fw_resource_type. + */ +@@ -846,6 +1146,74 @@ static void rproc_unprepare_subdevices(struct rproc *rproc) + } + + /** ++ * rproc_alloc_registered_carveouts() - allocate all carveouts registered ++ * in the list ++ * @rproc: the remote processor handle ++ * ++ * This function parses registered carveout list, performs allocation ++ * if alloc() ops registered and updates resource table information ++ * if rsc_offset set. ++ * ++ * Return: 0 on success ++ */ ++static int rproc_alloc_registered_carveouts(struct rproc *rproc) ++{ ++ struct rproc_mem_entry *entry, *tmp; ++ struct fw_rsc_carveout *rsc; ++ struct device *dev = &rproc->dev; ++ int ret; ++ ++ list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) { ++ if (entry->alloc) { ++ ret = entry->alloc(rproc, entry); ++ if (ret) { ++ dev_err(dev, "Unable to allocate carveout %s: %d\n", ++ entry->name, ret); ++ return -ENOMEM; ++ } ++ } ++ ++ if (entry->rsc_offset != FW_RSC_ADDR_ANY) { ++ /* update resource table */ ++ rsc = (void *)rproc->table_ptr + entry->rsc_offset; ++ ++ /* ++ * Some remote processors might need to know the pa ++ * even though they are behind an IOMMU. E.g., OMAP4's ++ * remote M3 processor needs this so it can control ++ * on-chip hardware accelerators that are not behind ++ * the IOMMU, and therefor must know the pa. ++ * ++ * Generally we don't want to expose physical addresses ++ * if we don't have to (remote processors are generally ++ * _not_ trusted), so we might want to do this only for ++ * remote processor that _must_ have this (e.g. OMAP4's ++ * dual M3 subsystem). ++ * ++ * Non-IOMMU processors might also want to have this info. ++ * In this case, the device address and the physical address ++ * are the same. ++ */ ++ ++ /* Use va if defined else dma to generate pa */ ++ if (sizeof(dma_addr_t) > sizeof(u32) || ++ sizeof(phys_addr_t) > sizeof(u32)) ++ dev_warn(dev, "Physical address cast in 32bit to fit resource table format\n"); ++ ++ if (entry->va) ++ rsc->pa = (u32)rproc_va_to_pa(entry->va); ++ else ++ rsc->pa = (u32)entry->dma; ++ ++ rsc->da = entry->da; ++ rsc->len = entry->len; ++ } ++ } ++ ++ return 0; ++} ++ ++/** + * rproc_coredump_cleanup() - clean up dump_segments list + * @rproc: the remote processor handle + */ +@@ -868,16 +1236,17 @@ static void rproc_coredump_cleanup(struct rproc *rproc) + */ + static void rproc_resource_cleanup(struct rproc *rproc) + { ++ struct rproc_debug_trace *trace, *trace_tmp; + struct rproc_mem_entry *entry, *tmp; + struct rproc_vdev *rvdev, *rvtmp; + struct device *dev = &rproc->dev; + + /* clean up debugfs trace entries */ +- list_for_each_entry_safe(entry, tmp, &rproc->traces, node) { +- rproc_remove_trace_file(entry->priv); ++ list_for_each_entry_safe(trace, trace_tmp, &rproc->traces, node) { ++ rproc_remove_trace_file(trace->tfile); + rproc->num_traces--; +- list_del(&entry->node); +- kfree(entry); ++ list_del(&trace->node); ++ kfree(trace); + } + + /* clean up iommu mapping entries */ +@@ -897,8 +1266,8 @@ static void rproc_resource_cleanup(struct rproc *rproc) + + /* clean up carveout allocations */ + list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) { +- dma_free_coherent(dev->parent, entry->len, entry->va, +- entry->dma); ++ if (entry->release) ++ entry->release(rproc, entry); + list_del(&entry->node); + kfree(entry); + } +@@ -1014,6 +1383,9 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw) + /* reset max_notifyid */ + rproc->max_notifyid = -1; + ++ /* reset handled vdev */ ++ rproc->nb_vdev = 0; ++ + /* handle fw resources which are required to boot rproc */ + ret = rproc_handle_resources(rproc, rproc_loading_handlers); + if (ret) { +@@ -1021,6 +1393,14 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw) + goto clean_up_resources; + } + ++ /* Allocate carveout resources associated to rproc */ ++ ret = rproc_alloc_registered_carveouts(rproc); ++ if (ret) { ++ dev_err(dev, "Failed to allocate associated carveouts: %d\n", ++ ret); ++ goto clean_up_resources; ++ } ++ + ret = rproc_start(rproc, fw); + if (ret) + goto clean_up_resources; +diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c +index a5c29f2..11240b4 100644 +--- a/drivers/remoteproc/remoteproc_debugfs.c ++++ b/drivers/remoteproc/remoteproc_debugfs.c +@@ -47,10 +47,23 @@ static struct dentry *rproc_dbg; + static ssize_t rproc_trace_read(struct file *filp, char __user *userbuf, + size_t count, loff_t *ppos) + { +- struct rproc_mem_entry *trace = filp->private_data; +- int len = strnlen(trace->va, trace->len); ++ struct rproc_debug_trace *data = filp->private_data; ++ struct rproc_mem_entry *trace = &data->trace_mem; ++ void *va; ++ char buf[100]; ++ int len; ++ ++ va = rproc_da_to_va(data->rproc, trace->da, trace->len); ++ ++ if (!va) { ++ len = scnprintf(buf, sizeof(buf), "Trace %s not available\n", ++ trace->name); ++ va = buf; ++ } else { ++ len = strnlen(va, trace->len); ++ } + +- return simple_read_from_buffer(userbuf, count, ppos, trace->va, len); ++ return simple_read_from_buffer(userbuf, count, ppos, va, len); + } + + static const struct file_operations trace_rproc_ops = { +@@ -260,6 +273,7 @@ static int rproc_carveouts_show(struct seq_file *seq, void *p) + + list_for_each_entry(carveout, &rproc->carveouts, node) { + seq_puts(seq, "Carveout memory entry:\n"); ++ seq_printf(seq, "\tName: %s\n", carveout->name); + seq_printf(seq, "\tVirtual address: %pK\n", carveout->va); + seq_printf(seq, "\tDMA address: %pad\n", &carveout->dma); + seq_printf(seq, "\tDevice address: 0x%x\n", carveout->da); +@@ -287,7 +301,7 @@ void rproc_remove_trace_file(struct dentry *tfile) + } + + struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc, +- struct rproc_mem_entry *trace) ++ struct rproc_debug_trace *trace) + { + struct dentry *tfile; + +diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h +index 7570beb..b130a3d 100644 +--- a/drivers/remoteproc/remoteproc_internal.h ++++ b/drivers/remoteproc/remoteproc_internal.h +@@ -25,6 +25,13 @@ + + struct rproc; + ++struct rproc_debug_trace { ++ struct rproc *rproc; ++ struct dentry *tfile; ++ struct list_head node; ++ struct rproc_mem_entry trace_mem; ++}; ++ + /* from remoteproc_core.c */ + void rproc_release(struct kref *kref); + irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id); +@@ -37,7 +44,7 @@ void rproc_remove_virtio_dev(struct rproc_vdev *rvdev); + /* from remoteproc_debugfs.c */ + void rproc_remove_trace_file(struct dentry *tfile); + struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc, +- struct rproc_mem_entry *trace); ++ struct rproc_debug_trace *trace); + void rproc_delete_debug_dir(struct rproc *rproc); + void rproc_create_debug_dir(struct rproc *rproc); + void rproc_init_debugfs(void); +@@ -52,6 +59,7 @@ void rproc_free_vring(struct rproc_vring *rvring); + int rproc_alloc_vring(struct rproc_vdev *rvdev, int i); + + void *rproc_da_to_va(struct rproc *rproc, u64 da, int len); ++phys_addr_t rproc_va_to_pa(void *cpu_addr); + int rproc_trigger_recovery(struct rproc *rproc); + + int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw); +@@ -60,6 +68,8 @@ int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw); + int rproc_elf_load_rsc_table(struct rproc *rproc, const struct firmware *fw); + struct resource_table *rproc_elf_find_loaded_rsc_table(struct rproc *rproc, + const struct firmware *fw); ++struct rproc_mem_entry * ++rproc_find_carveout_by_name(struct rproc *rproc, const char *name, ...); + + static inline + int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw) +diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c +index bbecd44..9ee63c0 100644 +--- a/drivers/remoteproc/remoteproc_virtio.c ++++ b/drivers/remoteproc/remoteproc_virtio.c +@@ -17,7 +17,9 @@ + * GNU General Public License for more details. + */ + ++#include + #include ++#include + #include + #include + #include +@@ -76,7 +78,9 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, + struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); + struct rproc *rproc = vdev_to_rproc(vdev); + struct device *dev = &rproc->dev; ++ struct rproc_mem_entry *mem; + struct rproc_vring *rvring; ++ struct fw_rsc_vdev *rsc; + struct virtqueue *vq; + void *addr; + int len, size; +@@ -88,8 +92,14 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, + if (!name) + return NULL; + ++ /* Search allocated memory region by name */ ++ mem = rproc_find_carveout_by_name(rproc, "vdev%dvring%d", rvdev->index, ++ id); ++ if (!mem || !mem->va) ++ return ERR_PTR(-ENOMEM); ++ + rvring = &rvdev->vring[id]; +- addr = rvring->va; ++ addr = mem->va; + len = rvring->len; + + /* zero vring */ +@@ -114,6 +124,10 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, + rvring->vq = vq; + vq->priv = rvring; + ++ /* Update vring in resource table */ ++ rsc = (void *)rproc->table_ptr + rvdev->rsc_offset; ++ rsc->vring[id].da = mem->da; ++ + return vq; + } + +@@ -303,10 +317,48 @@ static void rproc_virtio_dev_release(struct device *dev) + int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id) + { + struct rproc *rproc = rvdev->rproc; +- struct device *dev = &rproc->dev; ++ struct device *dev = &rvdev->dev; + struct virtio_device *vdev = &rvdev->vdev; ++ struct rproc_mem_entry *mem; + int ret; + ++ /* Try to find dedicated vdev buffer carveout */ ++ mem = rproc_find_carveout_by_name(rproc, "vdev%dbuffer", rvdev->index); ++ if (mem) { ++ phys_addr_t pa; ++ ++ if (mem->of_resm_idx != -1) { ++ struct device_node *np = rproc->dev.parent->of_node; ++ ++ /* Associate reserved memory to vdev device */ ++ ret = of_reserved_mem_device_init_by_idx(dev, np, ++ mem->of_resm_idx); ++ if (ret) { ++ dev_err(dev, "Can't associate reserved memory\n"); ++ goto out; ++ } ++ } else { ++ if (mem->va) { ++ dev_warn(dev, "vdev %d buffer already mapped\n", ++ rvdev->index); ++ pa = rproc_va_to_pa(mem->va); ++ } else { ++ /* Use dma address as carveout no memmapped yet */ ++ pa = (phys_addr_t)mem->dma; ++ } ++ ++ /* Associate vdev buffer memory pool to vdev subdev */ ++ ret = dmam_declare_coherent_memory(dev, pa, ++ mem->da, ++ mem->len, ++ DMA_MEMORY_EXCLUSIVE); ++ if (ret < 0) { ++ dev_err(dev, "Failed to associate buffer\n"); ++ goto out; ++ } ++ } ++ } ++ + vdev->id.device = id, + vdev->config = &rproc_virtio_config_ops, + vdev->dev.parent = dev; +diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c +index 548afdd..9a7e034 100644 +--- a/drivers/remoteproc/stm32_rproc.c ++++ b/drivers/remoteproc/stm32_rproc.c +@@ -7,6 +7,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -38,12 +39,19 @@ struct stm32_syscon { + }; + + struct stm32_rproc_mem { ++ char name[20]; + void __iomem *cpu_addr; + phys_addr_t bus_addr; + u32 dev_addr; + size_t size; + }; + ++struct stm32_rproc_mem_ranges { ++ u32 dev_addr; ++ u32 bus_addr; ++ u32 size; ++}; ++ + struct stm32_mbox { + const unsigned char name[10]; + struct mbox_chan *chan; +@@ -55,13 +63,63 @@ struct stm32_rproc { + struct reset_control *rst; + struct stm32_syscon hold_boot; + struct stm32_syscon pdds; +- struct stm32_rproc_mem ram[2]; ++ u32 nb_rmems; ++ struct stm32_rproc_mem *rmems; + struct stm32_mbox mb[MBOX_NB_MBX]; + bool secured_soc; + u32 rsc_addr; + u32 rsc_len; + }; + ++static int stm32_rproc_pa_to_da(struct rproc *rproc, phys_addr_t pa, u64 *da) ++{ ++ unsigned int i; ++ struct stm32_rproc *ddata = rproc->priv; ++ struct stm32_rproc_mem *p_mem; ++ ++ for (i = 0; i < ddata->nb_rmems; i++) { ++ p_mem = &ddata->rmems[i]; ++ ++ if (pa < p_mem->bus_addr || ++ pa >= p_mem->bus_addr + p_mem->size) ++ continue; ++ *da = pa - p_mem->bus_addr + p_mem->dev_addr; ++ dev_dbg(rproc->dev.parent, "da %llx to pa %#x\n", *da, pa); ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++static int stm32_rproc_mem_alloc(struct rproc *rproc, ++ struct rproc_mem_entry *mem) ++{ ++ struct device *dev = rproc->dev.parent; ++ void *va; ++ ++ dev_dbg(dev, "map memory: %pa+%zx\n", &mem->dma, mem->len); ++ va = ioremap_wc(mem->dma, mem->len); ++ if (IS_ERR_OR_NULL(va)) { ++ dev_err(dev, "Unable to map memory region: %pa+%zx\n", ++ &mem->dma, mem->len); ++ return -ENOMEM; ++ } ++ ++ /* Update memory entry va */ ++ mem->va = va; ++ ++ return 0; ++} ++ ++static int stm32_rproc_mem_release(struct rproc *rproc, ++ struct rproc_mem_entry *mem) ++{ ++ dev_dbg(rproc->dev.parent, "unmap memory: %pa\n", &mem->dma); ++ iounmap(mem->va); ++ ++ return 0; ++} ++ + static int stm32_rproc_elf_load_segments(struct rproc *rproc, + const struct firmware *fw) + { +@@ -71,6 +129,56 @@ static int stm32_rproc_elf_load_segments(struct rproc *rproc, + return 0; + } + ++static int stm32_rproc_of_memory_translations(struct rproc *rproc) ++{ ++ struct device *dev = rproc->dev.parent; ++ struct stm32_rproc *ddata = rproc->priv; ++ struct device_node *np = dev->of_node; ++ struct stm32_rproc_mem *p_mems; ++ struct stm32_rproc_mem_ranges *mem_range; ++ int cnt, array_size, i, ret = 0; ++ ++ cnt = of_property_count_elems_of_size(np, "ranges", ++ sizeof(*mem_range)); ++ if (cnt <= 0) { ++ dev_err(dev, "%s: ranges property not defined\n", __func__); ++ return -EINVAL; ++ } ++ ++ p_mems = devm_kcalloc(dev, cnt, sizeof(*p_mems), GFP_KERNEL); ++ if (!p_mems) ++ return -ENOMEM; ++ mem_range = kcalloc(cnt, sizeof(*mem_range), GFP_KERNEL); ++ if (!mem_range) ++ return -ENOMEM; ++ ++ array_size = cnt * sizeof(struct stm32_rproc_mem_ranges) / sizeof(u32); ++ ++ ret = of_property_read_u32_array(np, "ranges", ++ (u32 *)mem_range, array_size); ++ if (ret) { ++ dev_err(dev, "error while get ranges property: %x\n", ret); ++ goto free_mem; ++ } ++ ++ for (i = 0; i < cnt; i++) { ++ p_mems[i].bus_addr = mem_range[i].bus_addr; ++ p_mems[i].dev_addr = mem_range[i].dev_addr; ++ p_mems[i].size = mem_range[i].size; ++ ++ dev_dbg(dev, "memory range[%i]: da %#x, pa %#x, size %#x:\n", ++ i, p_mems[i].dev_addr, p_mems[i].bus_addr, ++ p_mems[i].size); ++ } ++ ++ ddata->rmems = p_mems; ++ ddata->nb_rmems = cnt; ++ ++free_mem: ++ kfree(mem_range); ++ return ret; ++} ++ + static int stm32_rproc_mbox_idx(struct rproc *rproc, const unsigned char *name) + { + struct stm32_rproc *ddata = rproc->priv; +@@ -88,12 +196,22 @@ static int stm32_rproc_mbox_idx(struct rproc *rproc, const unsigned char *name) + static int stm32_rproc_elf_load_rsc_table(struct rproc *rproc, + const struct firmware *fw) + { +- int status; ++ int status, i; + struct resource_table *table = NULL; + struct stm32_rproc *ddata = rproc->priv; + size_t tablesz = 0; ++ const struct elf32_hdr *ehdr; ++ const struct elf32_phdr *phdr; + + if (!rproc->early_boot) { ++ /* set coredump segments */ ++ ehdr = (const struct elf32_hdr *)fw->data; ++ phdr = (const struct elf32_phdr *)(fw->data + ehdr->e_phoff); ++ for (i = 0; i < ehdr->e_phnum; i++, phdr++) ++ rproc_coredump_add_segment(rproc, phdr->p_paddr, ++ phdr->p_memsz); ++ ++ /* load resource table */ + status = rproc_elf_load_rsc_table(rproc, fw); + if (status) + goto no_rsc_table; +@@ -124,6 +242,58 @@ static int stm32_rproc_elf_load_rsc_table(struct rproc *rproc, + return 0; + } + ++static int stm32_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) ++{ ++ struct device *dev = rproc->dev.parent; ++ struct device_node *np = dev->of_node; ++ struct of_phandle_iterator it; ++ struct rproc_mem_entry *mem; ++ struct reserved_mem *rmem; ++ u64 da; ++ int index = 0; ++ ++ /* Register associated reserved memory regions */ ++ of_phandle_iterator_init(&it, np, "memory-region", NULL, 0); ++ while (of_phandle_iterator_next(&it) == 0) { ++ rmem = of_reserved_mem_lookup(it.node); ++ if (!rmem) { ++ dev_err(dev, "unable to acquire memory-region\n"); ++ return -EINVAL; ++ } ++ ++ if (stm32_rproc_pa_to_da(rproc, rmem->base, &da) < 0) { ++ dev_err(dev, "memory region not valid %pa\n", ++ &rmem->base); ++ return -EINVAL; ++ } ++ ++ /* No need to map vdev buffer */ ++ if (strcmp(it.node->name, "vdev0buffer")) { ++ /* Register memory region */ ++ mem = rproc_mem_entry_init(dev, NULL, ++ (dma_addr_t)rmem->base, ++ rmem->size, da, ++ stm32_rproc_mem_alloc, ++ stm32_rproc_mem_release, ++ it.node->name); ++ } else { ++ /* Register reserved memory for vdev buffer alloc */ ++ mem = rproc_of_resm_mem_entry_init(dev, index, ++ rmem->size, ++ rmem->base, ++ it.node->name); ++ } ++ ++ if (!mem) ++ return -ENOMEM; ++ ++ rproc_add_carveout(rproc, mem); ++ index++; ++ } ++ ++ return stm32_rproc_elf_load_rsc_table(rproc, fw); ++} ++ + static struct resource_table * + stm32_rproc_elf_find_loaded_rsc_table(struct rproc *rproc, + const struct firmware *fw) +@@ -348,33 +518,12 @@ static void stm32_rproc_kick(struct rproc *rproc, int vqid) + } + } + +-static void *stm32_rproc_da_to_va(struct rproc *rproc, u64 da, int len) +-{ +- struct stm32_rproc *ddata = rproc->priv; +- void *va = NULL; +- u32 offset; +- unsigned int i; +- +- for (i = 0; i < 2; i++) { +- if (da >= ddata->ram[i].dev_addr && da + len <= +- ddata->ram[i].dev_addr + ddata->ram[i].size) { +- offset = da - ddata->ram[i].dev_addr; +- /* __force to make sparse happy with type conversion */ +- va = (__force void *)(ddata->ram[i].cpu_addr + offset); +- break; +- } +- } +- +- return va; +-} +- + static struct rproc_ops st_rproc_ops = { + .start = stm32_rproc_start, + .stop = stm32_rproc_stop, + .kick = stm32_rproc_kick, +- .da_to_va = stm32_rproc_da_to_va, + .load = stm32_rproc_elf_load_segments, +- .parse_fw = stm32_rproc_elf_load_rsc_table, ++ .parse_fw = stm32_rproc_parse_fw, + .find_loaded_rsc_table = stm32_rproc_elf_find_loaded_rsc_table, + .sanity_check = stm32_rproc_elf_sanity_check, + .get_boot_addr = stm32_rproc_elf_get_boot_addr, +@@ -414,9 +563,8 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev) + struct device_node *np = dev->of_node; + struct rproc *rproc = platform_get_drvdata(pdev); + struct stm32_rproc *ddata = rproc->priv; +- struct resource *res; + struct stm32_syscon tz; +- unsigned int tzen, i = 0; ++ unsigned int tzen; + int err, irq; + + irq = platform_get_irq_byname(pdev, "wdg"); +@@ -466,25 +614,6 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev) + if (err) + dev_warn(dev, "failed to get pdds\n"); + +- while ((res = platform_get_resource(pdev, IORESOURCE_MEM, i))) { +- ddata->ram[i].cpu_addr = devm_ioremap_resource(dev, res); +- if (IS_ERR(ddata->ram[i].cpu_addr)) +- return err; +- +- ddata->ram[i].bus_addr = res->start; +- ddata->ram[i].size = resource_size(res); +- +- /* +- * the m4 has retram at address 0 in its view (DA) +- * so for retram DA=0x0 PA=bus_addr else DA=PA=bus_addr +- */ +- if (i == 0) +- ddata->ram[i].dev_addr = 0x0; +- else +- ddata->ram[i].dev_addr = ddata->ram[i].bus_addr; +- +- i++; +- } + + rproc->auto_boot = of_property_read_bool(np, "auto_boot"); + rproc->recovery_disabled = !of_property_read_bool(np, "recovery"); +@@ -504,9 +633,7 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev) + } + } + +- of_reserved_mem_device_init(dev); +- +- return 0; ++ return stm32_rproc_of_memory_translations(rproc); + } + + static int stm32_rproc_probe(struct platform_device *pdev) +@@ -533,7 +660,7 @@ static int stm32_rproc_probe(struct platform_device *pdev) + if (!rproc->early_boot) { + ret = stm32_rproc_stop(rproc); + if (ret) +- goto free_mem; ++ goto free_rproc; + } + + stm32_rproc_request_mbox(rproc); +@@ -546,8 +673,6 @@ static int stm32_rproc_probe(struct platform_device *pdev) + + free_mb: + stm32_rproc_free_mbox(rproc); +-free_mem: +- of_reserved_mem_device_release(dev); + free_rproc: + rproc_free(rproc); + return ret; +@@ -563,7 +688,6 @@ static int stm32_rproc_remove(struct platform_device *pdev) + + rproc_del(rproc); + stm32_rproc_free_mbox(rproc); +- of_reserved_mem_device_release(dev); + rproc_free(rproc); + + return 0; +diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c +index 481eaea..bc5877a 100644 +--- a/drivers/rpmsg/virtio_rpmsg_bus.c ++++ b/drivers/rpmsg/virtio_rpmsg_bus.c +@@ -923,7 +923,7 @@ static int rpmsg_probe(struct virtio_device *vdev) + total_buf_space = vrp->num_bufs * vrp->buf_size; + + /* allocate coherent memory for the buffers */ +- bufs_va = dma_alloc_coherent(vdev->dev.parent->parent, ++ bufs_va = dma_alloc_coherent(vdev->dev.parent, + total_buf_space, &vrp->bufs_dma, + GFP_KERNEL); + if (!bufs_va) { +@@ -991,7 +991,7 @@ static int rpmsg_probe(struct virtio_device *vdev) + return 0; + + free_coherent: +- dma_free_coherent(vdev->dev.parent->parent, total_buf_space, ++ dma_free_coherent(vdev->dev.parent, total_buf_space, + bufs_va, vrp->bufs_dma); + vqs_del: + vdev->config->del_vqs(vrp->vdev); +@@ -1026,7 +1026,7 @@ static void rpmsg_remove(struct virtio_device *vdev) + + vdev->config->del_vqs(vrp->vdev); + +- dma_free_coherent(vdev->dev.parent->parent, total_buf_space, ++ dma_free_coherent(vdev->dev.parent, total_buf_space, + vrp->rbufs, vrp->bufs_dma); + + kfree(vrp); +diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h +index 787fd18..cd540c0 100644 +--- a/include/linux/remoteproc.h ++++ b/include/linux/remoteproc.h +@@ -305,14 +305,22 @@ struct fw_rsc_vdev { + struct fw_rsc_vdev_vring vring[0]; + } __packed; + ++struct rproc; ++ + /** + * struct rproc_mem_entry - memory entry descriptor + * @va: virtual address + * @dma: dma address + * @len: length, in bytes + * @da: device address ++ * @release: release associated memory + * @priv: associated data ++ * @name: associated memory region name (optional) + * @node: list node ++ * @rsc_offset: offset in resource table ++ * @flags: iommu protection flags ++ * @of_resm_idx: reserved memory phandle index ++ * @alloc: specific memory allocator function + */ + struct rproc_mem_entry { + void *va; +@@ -320,10 +328,15 @@ struct rproc_mem_entry { + int len; + u32 da; + void *priv; ++ char name[32]; + struct list_head node; ++ u32 rsc_offset; ++ u32 flags; ++ u32 of_resm_idx; ++ int (*alloc)(struct rproc *rproc, struct rproc_mem_entry *mem); ++ int (*release)(struct rproc *rproc, struct rproc_mem_entry *mem); + }; + +-struct rproc; + struct firmware; + + /** +@@ -441,6 +454,7 @@ struct rproc_dump_segment { + * @has_iommu: flag to indicate if remote processor is behind an MMU + * @dump_segments: list of segments in the firmware + * @early_boot: remote processor has been booted before kernel boot ++ * @nb_vdev: number of vdev currently handled by rproc + */ + struct rproc { + struct list_head node; +@@ -474,6 +488,7 @@ struct rproc { + bool auto_boot; + struct list_head dump_segments; + bool early_boot; ++ int nb_vdev; + }; + + /** +@@ -501,7 +516,6 @@ struct rproc_subdev { + /** + * struct rproc_vring - remoteproc vring state + * @va: virtual address +- * @dma: dma address + * @len: length, in bytes + * @da: device address + * @align: vring alignment +@@ -511,7 +525,6 @@ struct rproc_subdev { + */ + struct rproc_vring { + void *va; +- dma_addr_t dma; + int len; + u32 da; + u32 align; +@@ -523,6 +536,7 @@ struct rproc_vring { + /** + * struct rproc_vdev - remoteproc state for a supported virtio device + * @refcount: reference counter for the vdev and vring allocations ++ * @dev: sub device associated to the virtio device + * @subdev: handle for registering the vdev as a rproc subdevice + * @id: virtio device id (as in virtio_ids.h) + * @node: list node +@@ -530,11 +544,13 @@ struct rproc_vring { + * @vdev: the virio device + * @vring: the vrings for this vdev + * @rsc_offset: offset of the vdev's resource entry ++ * @index: vdev position versus other vdev declared in resource table + */ + struct rproc_vdev { + struct kref refcount; + + struct rproc_subdev subdev; ++ struct device dev; + + unsigned int id; + struct list_head node; +@@ -542,6 +558,7 @@ struct rproc_vdev { + struct virtio_device vdev; + struct rproc_vring vring[RVDEV_NUM_VRINGS]; + u32 rsc_offset; ++ u32 index; + }; + + struct rproc *rproc_get_by_phandle(phandle phandle); +@@ -555,6 +572,19 @@ int rproc_add(struct rproc *rproc); + int rproc_del(struct rproc *rproc); + void rproc_free(struct rproc *rproc); + ++void rproc_add_carveout(struct rproc *rproc, struct rproc_mem_entry *mem); ++ ++struct rproc_mem_entry * ++rproc_mem_entry_init(struct device *dev, ++ void *va, dma_addr_t dma, int len, u32 da, ++ int (*alloc)(struct rproc *, struct rproc_mem_entry *), ++ int (*release)(struct rproc *, struct rproc_mem_entry *), ++ const char *name, ...); ++ ++struct rproc_mem_entry * ++rproc_of_resm_mem_entry_init(struct device *dev, u32 of_resm_idx, int len, ++ u32 da, const char *name, ...); ++ + int rproc_boot(struct rproc *rproc); + void rproc_shutdown(struct rproc *rproc); + void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type); +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0042-ARM-stm32mp1-r0-rc3-SOUND.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0042-ARM-stm32mp1-r0-rc3-SOUND.patch new file mode 100644 index 0000000..0b637d5 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0042-ARM-stm32mp1-r0-rc3-SOUND.patch @@ -0,0 +1,112 @@ +From 6c92e60d3e402e1fcbd1bfa11185c995ce41d190 Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Mon, 10 Dec 2018 15:47:43 +0100 +Subject: [PATCH 42/52] ARM: stm32mp1-r0-rc3: SOUND + +--- + sound/soc/stm/stm32_sai_sub.c | 58 ++++++++++++++++++++++++++++++------------- + 1 file changed, 41 insertions(+), 17 deletions(-) + +diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c +index 1f23ca4..3f0540a 100644 +--- a/sound/soc/stm/stm32_sai_sub.c ++++ b/sound/soc/stm/stm32_sai_sub.c +@@ -313,6 +313,23 @@ static int stm32_sai_set_clk_div(struct stm32_sai_sub_data *sai, + return ret; + } + ++static int stm32_sai_set_parent_clock(struct stm32_sai_sub_data *sai, ++ unsigned int rate) ++{ ++ struct platform_device *pdev = sai->pdev; ++ struct clk *parent_clk = sai->pdata->clk_x8k; ++ int ret; ++ ++ if (!(rate % 11025)) ++ parent_clk = sai->pdata->clk_x11k; ++ ++ ret = clk_set_parent(sai->sai_ck, parent_clk); ++ if (ret) ++ dev_err(&pdev->dev, "Set parent clock returned: %d\n", ret); ++ ++ return ret; ++} ++ + static long stm32_sai_mclk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) + { +@@ -492,25 +509,26 @@ static int stm32_sai_set_sysclk(struct snd_soc_dai *cpu_dai, + struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); + int ret; + +- if (dir == SND_SOC_CLOCK_OUT) { ++ if (dir == SND_SOC_CLOCK_OUT && sai->sai_mclk) { + ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, + SAI_XCR1_NODIV, + (unsigned int)~SAI_XCR1_NODIV); + if (ret < 0) + return ret; + +- dev_dbg(cpu_dai->dev, "SAI MCLK frequency is %uHz\n", freq); +- sai->mclk_rate = freq; ++ /* If master clock is used, set parent clock now */ ++ ret = stm32_sai_set_parent_clock(sai, freq); ++ if (ret) ++ return ret; + +- if (sai->sai_mclk) { +- ret = clk_set_rate_exclusive(sai->sai_mclk, +- sai->mclk_rate); +- if (ret) { +- dev_err(cpu_dai->dev, +- "Could not set mclk rate\n"); +- return ret; +- } ++ ret = clk_set_rate_exclusive(sai->sai_mclk, freq); ++ if (ret) { ++ dev_err(cpu_dai->dev, "Could not set mclk rate\n"); ++ return ret; + } ++ ++ dev_dbg(cpu_dai->dev, "SAI MCLK frequency is %uHz\n", freq); ++ sai->mclk_rate = freq; + } + + return 0; +@@ -906,11 +924,13 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai, + int cr1, mask, div = 0; + int sai_clk_rate, mclk_ratio, den; + unsigned int rate = params_rate(params); ++ int ret; + +- if (!(rate % 11025)) +- clk_set_parent(sai->sai_ck, sai->pdata->clk_x11k); +- else +- clk_set_parent(sai->sai_ck, sai->pdata->clk_x8k); ++ if (!sai->sai_mclk) { ++ ret = stm32_sai_set_parent_clock(sai, rate); ++ if (ret) ++ return ret; ++ } + sai_clk_rate = clk_get_rate(sai->sai_ck); + + if (STM_SAI_IS_F4(sai->pdata)) { +@@ -1064,9 +1084,13 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream, + regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, SAI_XCR1_NODIV, + SAI_XCR1_NODIV); + +- clk_disable_unprepare(sai->sai_ck); ++ /* Release mclk rate only if rate was actually set */ ++ if (sai->mclk_rate) { ++ clk_rate_exclusive_put(sai->sai_mclk); ++ sai->mclk_rate = 0; ++ } + +- clk_rate_exclusive_put(sai->sai_mclk); ++ clk_disable_unprepare(sai->sai_ck); + + sai->substream = NULL; + } +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0043-ARM-stm32mp1-r0-rc3-MISC.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0043-ARM-stm32mp1-r0-rc3-MISC.patch new file mode 100644 index 0000000..123e396 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0043-ARM-stm32mp1-r0-rc3-MISC.patch @@ -0,0 +1,1067 @@ +From 64b878ba46af224fa2c1d0bf7c784927e1890f3a Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Mon, 10 Dec 2018 15:53:08 +0100 +Subject: [PATCH 43/52] ARM: stm32mp1-r0-rc3: MISC + +--- + .../devicetree/bindings/soc/stm32/stm32_hdp.txt | 39 ++++ + arch/arm/Kconfig.debug | 30 ++- + arch/arm/boot/dts/Makefile | 2 + + arch/arm/include/debug/stm32.S | 19 +- + drivers/hwspinlock/hwspinlock_core.c | 15 +- + drivers/hwspinlock/stm32_hwspinlock.c | 7 + + drivers/hwtracing/coresight/coresight-stm.c | 58 ++++- + drivers/soc/st/Kconfig | 8 + + drivers/soc/st/Makefile | 1 + + drivers/soc/st/stm32_hdp.c | 242 +++++++++++++++++++++ + drivers/spi/spi-stm32-qspi.c | 192 +++++++++++++++- + include/dt-bindings/soc/stm32-hdp.h | 108 +++++++++ + 12 files changed, 694 insertions(+), 27 deletions(-) + create mode 100644 Documentation/devicetree/bindings/soc/stm32/stm32_hdp.txt + create mode 100644 drivers/soc/st/stm32_hdp.c + create mode 100644 include/dt-bindings/soc/stm32-hdp.h + +diff --git a/Documentation/devicetree/bindings/soc/stm32/stm32_hdp.txt b/Documentation/devicetree/bindings/soc/stm32/stm32_hdp.txt +new file mode 100644 +index 0000000..e2bd82f +--- /dev/null ++++ b/Documentation/devicetree/bindings/soc/stm32/stm32_hdp.txt +@@ -0,0 +1,39 @@ ++STM32 - STM32MP1- HDP Pin configuration for STM32MP1 ++======================================================= ++ ++The Hardware Debug Port (HDP) allows the observation of internal signals. By using multiplexers, ++up to 16 signals for each of 8-bit output can be observed. ++ ++Required Properties: ++ ++ - compatible: Must be "st,stm32mp1-hdp" ++ - muxing-hdp: Indicates for each HDP pins selected which HDP output among the 16 available signals you want ++ ++For each HDP pins you can select one of 16 signals which will be described in file : include/dt-bindings/soc/stm32-hdp.h ++ ++Example ++------- ++ ++In common dtsi file: ++ ++hdp: hdp@5002a000 { ++ compatible = "st,stm32mp1-hdp"; ++ reg = <0x5002a000 0x400>; ++ clocks = <&rcc HDP>; ++ clock-names = "hdp"; ++}; ++ ++In board-specific file: ++ ++In this example I've selected HDP0, HDP6 and HDP7, and for HDP0 the output signal is HDP0_GPOVAL_0, ++for HDP6 is HDP6_GPOVAL_6, and for HDP7 is HDP7_GPOVAL_7. ++ ++&hdp { ++ pinctrl-names = "default", "sleep"; ++ pinctrl-0 = <&hdp0_pins_a &hdp6_pins_a &hdp7_pins_a>; ++ pinctrl-1 = <&hdp0_pins_sleep_a &hdp6_pins_sleep_a &hdp7_pins_sleep_a>; ++ ++ muxing-hdp = <(STM32_HDP(0, HDP0_GPOVAL_0) | ++ STM32_HDP(6, HDP6_GPOVAL_6) | ++ STM32_HDP(7, HDP7_GPOVAL_7))>; ++}; +diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug +index d5ec6c3..88849c1 100644 +--- a/arch/arm/Kconfig.debug ++++ b/arch/arm/Kconfig.debug +@@ -1186,23 +1186,37 @@ choice + + config STM32F4_DEBUG_UART + bool "Use STM32F4 UART for low-level debug" +- depends on ARCH_STM32 ++ depends on ARCH_STM32 && ARM_SINGLE_ARMV7M + 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. ++ USART1, but another UART instance can be selected by modifying ++ CONFIG_DEBUG_UART_PHYS. + + If unsure, say N. + + config STM32F7_DEBUG_UART + bool "Use STM32F7 UART for low-level debug" +- depends on ARCH_STM32 ++ depends on ARCH_STM32 && ARM_SINGLE_ARMV7M + 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. ++ USART1, but another UART instance can be selected by modifying ++ CONFIG_DEBUG_UART_PHYS. ++ ++ If unsure, say N. ++ ++ config STM32MP1_DEBUG_UART ++ bool "Use STM32MP1 UART for low-level debug" ++ depends on ARCH_STM32 && ARCH_MULTI_V7 ++ select DEBUG_STM32_UART ++ help ++ Say Y here if you want kernel low-level debugging support ++ on STM32MP1 based platforms, wich default UART is wired on ++ UART4, but another UART instance can be selected by modifying ++ CONFIG_DEBUG_UART_PHYS and CONFIG_DEBUG_UART_VIRT. + + If unsure, say N. + +@@ -1607,6 +1621,8 @@ config DEBUG_UART_PHYS + default 0x3e000000 if DEBUG_BCM_KONA_UART + default 0x3f201000 if DEBUG_BCM2836 + default 0x4000e400 if DEBUG_LL_UART_EFM32 ++ default 0x40010000 if STM32MP1_DEBUG_UART ++ default 0x40011000 if STM32F4_DEBUG_UART || STM32F7_DEBUG_UART + default 0x40028000 if DEBUG_AT91_SAMV7_USART1 + default 0x40081000 if DEBUG_LPC18XX_UART0 + default 0x40090000 if DEBUG_LPC32XX +@@ -1700,10 +1716,12 @@ config DEBUG_UART_PHYS + DEBUG_S3C64XX_UART || \ + DEBUG_BCM63XX_UART || DEBUG_ASM9260_UART || \ + DEBUG_SIRFSOC_UART || DEBUG_DIGICOLOR_UA0 || \ +- DEBUG_AT91_UART ++ DEBUG_AT91_UART || STM32F4_DEBUG_UART || \ ++ STM32F7_DEBUG_UART || STM32MP1_DEBUG_UART + + config DEBUG_UART_VIRT + hex "Virtual base address of debug UART" ++ default 0xfe010000 if STM32MP1_DEBUG_UART + default 0xc881f000 if DEBUG_RV1108_UART2 + default 0xc8821000 if DEBUG_RV1108_UART1 + default 0xc8912000 if DEBUG_RV1108_UART0 +@@ -1815,7 +1833,7 @@ config DEBUG_UART_VIRT + DEBUG_S3C64XX_UART || \ + DEBUG_BCM63XX_UART || DEBUG_ASM9260_UART || \ + DEBUG_SIRFSOC_UART || DEBUG_DIGICOLOR_UA0 || \ +- DEBUG_AT91_UART ++ DEBUG_AT91_UART || STM32MP1_DEBUG_UART + + config DEBUG_UART_8250_SHIFT + int "Register offset shift for the 8250 debug UART" +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index fdf53f5..5665290 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -924,9 +924,11 @@ dtb-$(CONFIG_ARCH_STM32) += \ + stm32h743i-disco.dtb \ + stm32mp157a-dk1.dtb \ + stm32mp157c-dk2.dtb \ ++ stm32mp157c-dk2-a7-examples.dtb \ + stm32mp157c-dk2-m4-examples.dtb \ + stm32mp157c-ed1.dtb \ + stm32mp157c-ev1.dtb \ ++ stm32mp157c-ev1-a7-examples.dtb \ + stm32mp157c-ev1-m4-examples.dtb + dtb-$(CONFIG_MACH_SUN4I) += \ + sun4i-a10-a1000.dtb \ +diff --git a/arch/arm/include/debug/stm32.S b/arch/arm/include/debug/stm32.S +index 427561e..3353a81 100644 +--- a/arch/arm/include/debug/stm32.S ++++ b/arch/arm/include/debug/stm32.S +@@ -4,21 +4,12 @@ + * Author: Gerald Baeza for STMicroelectronics. + */ + +-#ifdef CONFIG_ARM_SINGLE_ARMV7M +-#define STM32_UART_BASE_PHYS 0x40011000 /* USART1 */ +-#define STM32_UART_BASE_VIRT STM32_UART_BASE_PHYS /* no MMU */ +-#else +-#define STM32_UART_BASE_PHYS 0x40010000 /* UART4 */ +-#define STM32_UART_BASE_VIRT 0xfe010000 /* UART4 */ +-#endif +- +- + #ifdef CONFIG_STM32F4_DEBUG_UART + #define STM32_USART_SR_OFF 0x00 + #define STM32_USART_TDR_OFF 0x04 + #endif + +-#ifdef CONFIG_STM32F7_DEBUG_UART ++#if defined(CONFIG_STM32F7_DEBUG_UART) || defined(CONFIG_STM32MP1_DEBUG_UART) + #define STM32_USART_SR_OFF 0x1C + #define STM32_USART_TDR_OFF 0x28 + #endif +@@ -27,8 +18,12 @@ + #define STM32_USART_TXE (1 << 7) /* Tx data reg empty */ + + .macro addruart, rp, rv, tmp +- ldr \rp, =STM32_UART_BASE_PHYS @ physical base +- ldr \rv, =STM32_UART_BASE_VIRT @ virt base ++ ldr \rp, =CONFIG_DEBUG_UART_PHYS @ physical base ++#if defined(CONFIG_MMU) ++ ldr \rv, =CONFIG_DEBUG_UART_VIRT @ virt base ++#else ++ ldr \rv, =CONFIG_DEBUG_UART_PHYS @ same as physical base ++#endif + .endm + + .macro senduart,rd,rx +diff --git a/drivers/hwspinlock/hwspinlock_core.c b/drivers/hwspinlock/hwspinlock_core.c +index 2bad40d..287e1b3 100644 +--- a/drivers/hwspinlock/hwspinlock_core.c ++++ b/drivers/hwspinlock/hwspinlock_core.c +@@ -333,6 +333,9 @@ int of_hwspin_lock_get_id(struct device_node *np, int index) + if (ret) + return ret; + ++ if (!of_device_is_available(args.np)) ++ return -ENOENT; ++ + /* Find the hwspinlock device: we need its base_id */ + ret = -EPROBE_DEFER; + rcu_read_lock(); +@@ -742,13 +745,11 @@ struct hwspinlock *hwspin_lock_request_specific(unsigned int id) + /* sanity check (this shouldn't happen) */ + WARN_ON(hwlock_to_id(hwlock) != id); + +- /* make sure this hwspinlock is unused */ +- ret = radix_tree_tag_get(&hwspinlock_tree, id, HWSPINLOCK_UNUSED); +- if (ret == 0) { +- pr_warn("hwspinlock %u is already in use\n", id); +- hwlock = NULL; +- goto out; +- } ++ /* ++ * We intentionally do not check for the HWSPINLOCK_UNUSED tag as ++ * we want to share HWSPINLOCK between several devices. This is safe ++ * since the lock/unlock requests are called under &hwlock->lock control ++ */ + + /* mark as used and power up */ + ret = __hwspin_lock_request(hwlock); +diff --git a/drivers/hwspinlock/stm32_hwspinlock.c b/drivers/hwspinlock/stm32_hwspinlock.c +index 3242b72..b9b9b99 100644 +--- a/drivers/hwspinlock/stm32_hwspinlock.c ++++ b/drivers/hwspinlock/stm32_hwspinlock.c +@@ -5,6 +5,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -42,9 +43,15 @@ static void stm32_hwspinlock_unlock(struct hwspinlock *lock) + writel(STM32_MUTEX_COREID, lock_addr); + } + ++static void stm32_hwspinlock_relax(struct hwspinlock *lock) ++{ ++ ndelay(50); ++} ++ + static const struct hwspinlock_ops stm32_hwspinlock_ops = { + .trylock = stm32_hwspinlock_trylock, + .unlock = stm32_hwspinlock_unlock, ++ .relax = stm32_hwspinlock_relax, + }; + + static int stm32_hwspinlock_probe(struct platform_device *pdev) +diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c +index c46c70a..65687c0 100644 +--- a/drivers/hwtracing/coresight/coresight-stm.c ++++ b/drivers/hwtracing/coresight/coresight-stm.c +@@ -40,6 +40,7 @@ + #define STMHETER 0xd20 + #define STMHEBSR 0xd60 + #define STMHEMCR 0xd64 ++#define STMHEEXTMUXR 0xd68 + #define STMHEMASTR 0xdf4 + #define STMHEFEAT1R 0xdf8 + #define STMHEIDR 0xdfc +@@ -125,9 +126,11 @@ struct channel_space { + * @stmheer: settings for register STMHEER. + * @stmheter: settings for register STMHETER. + * @stmhebsr: settings for register STMHEBSR. ++ * @stmheextmuxr: settings for register STMHEEXTMUXR. + */ + struct stm_drvdata { + void __iomem *base; ++ void __iomem *base_cti; + struct device *dev; + struct clk *atclk; + struct coresight_device *csdev; +@@ -143,6 +146,7 @@ struct stm_drvdata { + u32 stmheer; + u32 stmheter; + u32 stmhebsr; ++ u32 stmheextmuxr; + }; + + static void stm_hwevent_enable_hw(struct stm_drvdata *drvdata) +@@ -152,6 +156,7 @@ static void stm_hwevent_enable_hw(struct stm_drvdata *drvdata) + writel_relaxed(drvdata->stmhebsr, drvdata->base + STMHEBSR); + writel_relaxed(drvdata->stmheter, drvdata->base + STMHETER); + writel_relaxed(drvdata->stmheer, drvdata->base + STMHEER); ++ writel_relaxed(drvdata->stmheextmuxr, drvdata->base + STMHEEXTMUXR); + writel_relaxed(0x01 | /* Enable HW event tracing */ + 0x04, /* Error detection on event tracing */ + drvdata->base + STMHEMCR); +@@ -222,6 +227,7 @@ static void stm_hwevent_disable_hw(struct stm_drvdata *drvdata) + writel_relaxed(0x0, drvdata->base + STMHEMCR); + writel_relaxed(0x0, drvdata->base + STMHEER); + writel_relaxed(0x0, drvdata->base + STMHETER); ++ writel_relaxed(0x0, drvdata->base + STMHEEXTMUXR); + + CS_LOCK(drvdata->base); + } +@@ -455,6 +461,34 @@ static ssize_t notrace stm_generic_packet(struct stm_data *stm_data, + return size; + } + ++static ssize_t hwevent_extmux_select_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ unsigned long val = drvdata->stmheextmuxr; ++ ++ return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); ++} ++ ++static ssize_t hwevent_extmux_select_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); ++ unsigned long val; ++ int ret = 0; ++ ++ ret = kstrtoul(buf, 16, &val); ++ if (ret) ++ return -EINVAL; ++ ++ drvdata->stmheextmuxr = val; ++ ++ return size; ++} ++static DEVICE_ATTR_RW(hwevent_extmux_select); ++ + static ssize_t hwevent_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) + { +@@ -644,10 +678,16 @@ coresight_stm_reg(spfeat1r, STMSPFEAT1R); + coresight_stm_reg(spfeat2r, STMSPFEAT2R); + coresight_stm_reg(spfeat3r, STMSPFEAT3R); + coresight_stm_reg(devid, CORESIGHT_DEVID); ++coresight_stm_reg(stmheer, STMHEER); ++coresight_stm_reg(stmheter, STMHETER); ++coresight_stm_reg(stmhebsr, STMHEBSR); ++coresight_stm_reg(stmheextmux, STMHEEXTMUXR); ++coresight_stm_reg(stmhemcr, STMHEMCR); + + static struct attribute *coresight_stm_attrs[] = { + &dev_attr_hwevent_enable.attr, + &dev_attr_hwevent_select.attr, ++ &dev_attr_hwevent_extmux_select.attr, + &dev_attr_port_enable.attr, + &dev_attr_port_select.attr, + &dev_attr_traceid.attr, +@@ -667,6 +707,11 @@ static struct attribute *coresight_stm_mgmt_attrs[] = { + &dev_attr_spfeat2r.attr, + &dev_attr_spfeat3r.attr, + &dev_attr_devid.attr, ++ &dev_attr_stmheer.attr, ++ &dev_attr_stmheter.attr, ++ &dev_attr_stmhebsr.attr, ++ &dev_attr_stmheextmux.attr, ++ &dev_attr_stmhemcr.attr, + NULL, + }; + +@@ -792,7 +837,7 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id) + struct coresight_platform_data *pdata = NULL; + struct stm_drvdata *drvdata; + struct resource *res = &adev->res; +- struct resource ch_res; ++ struct resource ch_res, cti_res; + size_t res_size, bitmap_size; + struct coresight_desc desc = { 0 }; + struct device_node *np = adev->dev.of_node; +@@ -821,6 +866,17 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id) + return PTR_ERR(base); + drvdata->base = base; + ++ ret = stm_get_resource_byname(np, "cti-base", &cti_res); ++ if (ret) ++ return ret; ++ ++ base = devm_ioremap_resource(dev, &cti_res); ++ ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ drvdata->base_cti = base; ++ + ret = stm_get_resource_byname(np, "stm-stimulus-base", &ch_res); + if (ret) + return ret; +diff --git a/drivers/soc/st/Kconfig b/drivers/soc/st/Kconfig +index 82ee423..8ab6049 100644 +--- a/drivers/soc/st/Kconfig ++++ b/drivers/soc/st/Kconfig +@@ -6,4 +6,12 @@ config STM32_PM_DOMAINS + select PM_GENERIC_DOMAINS + default y if MACH_STM32MP157 + ++config STM32_HDP ++ bool "STMicroelectronics STM32MP157 Hardware Debug Port (HDP) pin control" ++ depends on MACH_STM32MP157 ++ default n if MACH_STM32MP157 ++ help ++ The Hardware Debug Port allows the observation of internal signals. By using multiplexers, ++ up to 16 signals for each of 8-bit output can be observed. ++ + endif # ARCH_STM32 +diff --git a/drivers/soc/st/Makefile b/drivers/soc/st/Makefile +index 8d7f291..85905b7 100644 +--- a/drivers/soc/st/Makefile ++++ b/drivers/soc/st/Makefile +@@ -1 +1,2 @@ + obj-$(CONFIG_STM32_PM_DOMAINS) += stm32_pm_domain.o ++obj-$(CONFIG_STM32_HDP) += stm32_hdp.o +diff --git a/drivers/soc/st/stm32_hdp.c b/drivers/soc/st/stm32_hdp.c +new file mode 100644 +index 0000000..6408ac6 +--- /dev/null ++++ b/drivers/soc/st/stm32_hdp.c +@@ -0,0 +1,242 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved ++ * Author: Christophe Roullier ++ * for STMicroelectronics. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define HDP_CTRL_ENABLE 1 ++#define HDP_CTRL_DISABLE 0 ++ ++enum { ++ HDP_CTRL = 0, ++ HDP_MUX = 0x4, ++ HDP_VAL = 0x10, ++ HDP_GPOSET = 0x14, ++ HDP_GPOCLR = 0x18, ++ HDP_GPOVAL = 0x1c, ++ HDP_VERR = 0x3f4, ++ HDP_IPIDR = 0x3f8, ++ HDP_SIDR = 0x3fc ++} HDP_register_offsets; ++ ++struct data_priv { ++ struct clk *clk; ++ int clk_is_enabled; ++ struct dentry *pwr_dentry; ++ unsigned char __iomem *hdp_membase; ++ unsigned int hdp_ctrl; ++ unsigned int hdp_mux; ++}; ++ ++/* enable/disable */ ++static int stm32_hdp_enable_set(void *data, int val) ++{ ++ struct data_priv *e = (struct data_priv *)data; ++ ++ if (!e->clk) ++ return -EPERM; ++ ++ if (val == 1) { ++ if (clk_prepare_enable(e->clk) < 0) { ++ pr_err("Failed to enable HDP clock\n"); ++ return -EPERM; ++ } ++ e->clk_is_enabled = 1; ++ } else { ++ clk_disable_unprepare(e->clk); ++ e->clk_is_enabled = 0; ++ } ++ return 0; ++} ++ ++static int stm32_hdp_fops_set(void *data, u64 val) ++{ ++ unsigned char __iomem *addr = (unsigned char __iomem *)data; ++ ++ writel_relaxed(val, addr); ++ ++ return 0; ++} ++ ++static int stm32_hdp_fops_get(void *data, u64 *val) ++{ ++ unsigned char __iomem *addr = (unsigned char __iomem *)data; ++ ++ *val = readl_relaxed(addr); ++ ++ return 0; ++} ++ ++DEFINE_SIMPLE_ATTRIBUTE(stm32_hdp_fops, stm32_hdp_fops_get, ++ stm32_hdp_fops_set, "0x%llx\n"); ++ ++int stm32_hdp_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct device *dev = &pdev->dev; ++ struct resource *res; ++ ++ struct data_priv *data; ++ struct dentry *r; ++ ++ int ret; ++ const __be32 *getmuxing; ++ u32 muxing, version; ++ ++ if (!np) ++ return -ENODEV; ++ ++ data = devm_kzalloc(&pdev->dev, sizeof(struct data_priv), GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ data->hdp_membase = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(data->hdp_membase)) ++ return PTR_ERR(data->hdp_membase); ++ ++ /* Get HDP clocks */ ++ data->clk = devm_clk_get(dev, "hdp"); ++ if (IS_ERR(data->clk)) { ++ dev_err(dev, "No HDP CK clock provided...\n"); ++ return PTR_ERR(data->clk); ++ } ++ ++ /* Enable clock */ ++ ret = stm32_hdp_enable_set(data, 1); ++ if (ret != 0) ++ return ret; ++ ++ getmuxing = of_get_property(np, "muxing-hdp", NULL); ++ if (!getmuxing) { ++ dev_err(dev, ++ "no muxing-hdp property in node\n"); ++ /* Disable clock */ ++ ret = stm32_hdp_enable_set(data, 0); ++ if (ret != 0) ++ return ret; ++ ++ return -EINVAL; ++ } ++ ++ /* add hdp directory */ ++ r = debugfs_create_dir("hdp", NULL); ++ if (!r) { ++ dev_err(dev, "Unable to create HDP debugFS\n"); ++ /* Disable clock */ ++ ret = stm32_hdp_enable_set(data, 0); ++ if (ret != 0) ++ return ret; ++ ++ return -ENODEV; ++ } ++ ++ debugfs_create_file("ctrl", 0644, r, ++ data->hdp_membase + HDP_CTRL, &stm32_hdp_fops); ++ debugfs_create_file("mux", 0644, r, ++ data->hdp_membase + HDP_MUX, &stm32_hdp_fops); ++ debugfs_create_file("val", 0644, r, ++ data->hdp_membase + HDP_VAL, &stm32_hdp_fops); ++ debugfs_create_file("gposet", 0644, r, ++ data->hdp_membase + HDP_GPOSET, &stm32_hdp_fops); ++ debugfs_create_file("gpoclr", 0644, r, ++ data->hdp_membase + HDP_GPOCLR, &stm32_hdp_fops); ++ debugfs_create_file("gpoval", 0644, r, ++ data->hdp_membase + HDP_GPOVAL, &stm32_hdp_fops); ++ ++ /* Enable HDP */ ++ writel(HDP_CTRL_ENABLE, data->hdp_membase + HDP_CTRL); ++ ++ /* HDP Multiplexing */ ++ muxing = of_read_number(getmuxing, ++ of_n_addr_cells(np)); ++ ++ writel(muxing, data->hdp_membase + HDP_MUX); ++ ++ platform_set_drvdata(pdev, data); ++ ++ /* Get Majeur, Minor version */ ++ version = readl(data->hdp_membase + HDP_VERR); ++ ++ dev_info(dev, "STM32 HDP version %d.%d initialized\n", ++ version >> 4, version & 0x0F); ++ ++ return 0; ++} ++ ++static int stm32_hdp_remove(struct platform_device *pdev) ++{ ++ struct data_priv *data = platform_get_drvdata(pdev); ++ ++ /* Disable HDP */ ++ writel(HDP_CTRL_DISABLE, data->hdp_membase + HDP_CTRL); ++ ++ if (data->clk) { ++ if (data->clk_is_enabled) ++ clk_disable_unprepare(data->clk); ++ } ++ ++ pr_info("driver STM32 HDP removed\n"); ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int stm32_hdp_suspend(struct device *dev) ++{ ++ struct data_priv *data = dev_get_drvdata(dev); ++ ++ data->hdp_ctrl = readl_relaxed(data->hdp_membase + HDP_CTRL); ++ data->hdp_mux = readl_relaxed(data->hdp_membase + HDP_MUX); ++ ++ pinctrl_pm_select_sleep_state(dev); ++ ++ return 0; ++} ++ ++static int stm32_hdp_resume(struct device *dev) ++{ ++ struct data_priv *data = dev_get_drvdata(dev); ++ ++ writel_relaxed(data->hdp_ctrl, data->hdp_membase + HDP_CTRL); ++ writel_relaxed(data->hdp_mux, data->hdp_membase + HDP_MUX); ++ ++ pinctrl_pm_select_default_state(dev); ++ ++ return 0; ++} ++#endif /* CONFIG_PM_SLEEP */ ++ ++static SIMPLE_DEV_PM_OPS(stm32_hdp_pm_ops, ++ stm32_hdp_suspend, ++ stm32_hdp_resume); ++ ++static const struct of_device_id hdp_match[] = { ++ { .compatible = "st,stm32mp1-hdp",}, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, hdp_match); ++ ++static struct platform_driver hdp_driver = { ++ .probe = stm32_hdp_probe, ++ .remove = stm32_hdp_remove, ++ .driver = { ++ .name = "hdp", ++ .of_match_table = hdp_match, ++ .pm = &stm32_hdp_pm_ops, ++ }, ++}; ++ ++module_platform_driver(hdp_driver); +diff --git a/drivers/spi/spi-stm32-qspi.c b/drivers/spi/spi-stm32-qspi.c +index 3e8ca10..9c67718 100644 +--- a/drivers/spi/spi-stm32-qspi.c ++++ b/drivers/spi/spi-stm32-qspi.c +@@ -5,7 +5,10 @@ + */ + #include + #include ++#include ++#include + #include ++#include + #include + #include + #include +@@ -84,6 +87,7 @@ + #define STM32_FIFO_TIMEOUT_US 30000 + #define STM32_BUSY_TIMEOUT_US 100000 + #define STM32_ABT_TIMEOUT_US 100000 ++#define STM32_COMP_TIMEOUT_MS 1000 + + struct stm32_qspi_flash { + struct stm32_qspi *qspi; +@@ -93,6 +97,7 @@ struct stm32_qspi_flash { + + struct stm32_qspi { + struct device *dev; ++ phys_addr_t phys_base; + void __iomem *io_base; + void __iomem *mm_base; + resource_size_t mm_size; +@@ -102,6 +107,11 @@ struct stm32_qspi { + struct completion data_completion; + u32 fmode; + ++ struct dma_chan *dma_chtx; ++ struct dma_chan *dma_chrx; ++ struct completion dma_completion; ++ struct sg_table dma_sgt; ++ + u32 cr_reg; + u32 dcr_reg; + +@@ -180,6 +190,131 @@ static int stm32_qspi_tx_mm(struct stm32_qspi *qspi, + return 0; + } + ++static void stm32_qspi_dma_callback(void *arg) ++{ ++ struct completion *dma_completion = arg; ++ ++ complete(dma_completion); ++} ++ ++static int stm32_qspi_tx_dma(struct stm32_qspi *qspi, ++ const struct spi_mem_op *op) ++{ ++ struct dma_async_tx_descriptor *desc; ++ enum dma_transfer_direction dma_dir; ++ enum dma_data_direction map_dir; ++ bool addr_valid, vmalloced_buf; ++ unsigned int dma_max_seg_size; ++ struct scatterlist *sg; ++ struct dma_chan *dma_ch; ++ struct page *vm_page; ++ dma_cookie_t cookie; ++ u32 cr, len, t_out; ++ int i, err, nents, desc_len; ++ u8 *buf; ++ ++ cr = readl_relaxed(qspi->io_base + QSPI_CR); ++ ++ if (op->data.dir == SPI_MEM_DATA_IN) { ++ map_dir = DMA_FROM_DEVICE; ++ dma_dir = DMA_DEV_TO_MEM; ++ dma_ch = qspi->dma_chrx; ++ buf = op->data.buf.in; ++ } else { ++ map_dir = DMA_TO_DEVICE; ++ dma_dir = DMA_MEM_TO_DEV; ++ dma_ch = qspi->dma_chtx; ++ buf = (u8 *)op->data.buf.out; ++ } ++ ++ /* the stm32 dma could tx MAX_DMA_BLOCK_LEN */ ++ dma_max_seg_size = dma_get_max_seg_size(dma_ch->device->dev); ++ len = op->data.nbytes; ++ ++ vmalloced_buf = is_vmalloc_addr(buf); ++ addr_valid = virt_addr_valid(buf); ++ if (addr_valid) { ++ desc_len = dma_max_seg_size; ++ nents = DIV_ROUND_UP(len, desc_len); ++ } else { ++ desc_len = min_t(int, dma_max_seg_size, PAGE_SIZE); ++ nents = DIV_ROUND_UP(len + offset_in_page(buf), desc_len); ++ } ++ ++ if (nents != qspi->dma_sgt.nents) { ++ sg_free_table(&qspi->dma_sgt); ++ ++ err = sg_alloc_table(&qspi->dma_sgt, nents, GFP_KERNEL); ++ if (err) ++ return err; ++ } ++ ++ for_each_sg(qspi->dma_sgt.sgl, sg, nents, i) { ++ size_t bytes; ++ ++ if (addr_valid) { ++ bytes = min_t(size_t, len, desc_len); ++ sg_set_buf(sg, buf, bytes); ++ } else { ++ bytes = min_t(size_t, len, ++ desc_len - offset_in_page(buf)); ++ if (vmalloced_buf) ++ vm_page = vmalloc_to_page(buf); ++ else ++ vm_page = kmap_to_page(buf); ++ if (!vm_page) { ++ sg_free_table(&qspi->dma_sgt); ++ return -ENOMEM; ++ } ++ sg_set_page(sg, vm_page, bytes, ++ offset_in_page(buf)); ++ } ++ ++ buf += bytes; ++ len -= bytes; ++ } ++ ++ if (dma_map_sg(qspi->dev, qspi->dma_sgt.sgl, nents, map_dir) != nents) ++ return -ENOMEM; ++ ++ desc = dmaengine_prep_slave_sg(dma_ch, qspi->dma_sgt.sgl, nents, ++ dma_dir, DMA_PREP_INTERRUPT); ++ if (!desc) { ++ err = -ENOMEM; ++ goto out_unmap; ++ } ++ ++ reinit_completion(&qspi->dma_completion); ++ desc->callback = stm32_qspi_dma_callback; ++ desc->callback_param = &qspi->dma_completion; ++ cookie = dmaengine_submit(desc); ++ err = dma_submit_error(cookie); ++ if (err) ++ goto out_unmap; ++ ++ dma_async_issue_pending(dma_ch); ++ ++ writel_relaxed(cr | CR_DMAEN, qspi->io_base + QSPI_CR); ++ ++ t_out = nents * STM32_COMP_TIMEOUT_MS; ++ if (!wait_for_completion_interruptible_timeout(&qspi->dma_completion, ++ msecs_to_jiffies(t_out))) ++ err = -ETIMEDOUT; ++ ++ if (dma_async_is_tx_complete(dma_ch, cookie, ++ NULL, NULL) != DMA_COMPLETE) ++ err = -ETIMEDOUT; ++ ++ if (err) ++ dmaengine_terminate_all(dma_ch); ++ ++out_unmap: ++ writel_relaxed(cr & ~CR_DMAEN, qspi->io_base + QSPI_CR); ++ dma_unmap_sg(qspi->dev, qspi->dma_sgt.sgl, nents, map_dir); ++ ++ return err; ++} ++ + static int stm32_qspi_tx(struct stm32_qspi *qspi, const struct spi_mem_op *op) + { + if (!op->data.nbytes) +@@ -187,6 +322,10 @@ static int stm32_qspi_tx(struct stm32_qspi *qspi, const struct spi_mem_op *op) + + if (qspi->fmode == CCR_FMODE_MM) + return stm32_qspi_tx_mm(qspi, op); ++ else if (op->data.dir == SPI_MEM_DATA_IN && qspi->dma_chrx) ++ return stm32_qspi_tx_dma(qspi, op); ++ else if (op->data.dir == SPI_MEM_DATA_OUT && qspi->dma_chtx) ++ return stm32_qspi_tx_dma(qspi, op); + + return stm32_qspi_tx_poll(qspi, op); + } +@@ -217,7 +356,7 @@ static int stm32_qspi_wait_cmd(struct stm32_qspi *qspi, + writel_relaxed(cr | CR_TCIE | CR_TEIE, qspi->io_base + QSPI_CR); + + if (!wait_for_completion_interruptible_timeout(&qspi->data_completion, +- msecs_to_jiffies(1000))) { ++ msecs_to_jiffies(STM32_COMP_TIMEOUT_MS))) { + err = -ETIMEDOUT; + } else { + sr = readl_relaxed(qspi->io_base + QSPI_SR); +@@ -386,6 +525,52 @@ static int stm32_qspi_setup(struct spi_device *spi) + return 0; + } + ++static void stm32_qspi_dma_setup(struct stm32_qspi *qspi) ++{ ++ struct dma_slave_config dma_cfg; ++ struct device *dev = qspi->dev; ++ int ret; ++ ++ memset(&dma_cfg, 0, sizeof(dma_cfg)); ++ ++ dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; ++ dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; ++ dma_cfg.src_addr = qspi->phys_base + QSPI_DR; ++ dma_cfg.dst_addr = qspi->phys_base + QSPI_DR; ++ dma_cfg.src_maxburst = 4; ++ dma_cfg.dst_maxburst = 4; ++ ++ qspi->dma_chrx = dma_request_slave_channel(dev, "rx"); ++ if (qspi->dma_chrx) { ++ ret = dmaengine_slave_config(qspi->dma_chrx, &dma_cfg); ++ if (ret) { ++ dev_err(dev, "dma rx config failed\n"); ++ dma_release_channel(qspi->dma_chrx); ++ qspi->dma_chrx = NULL; ++ } ++ } ++ ++ qspi->dma_chtx = dma_request_slave_channel(dev, "tx"); ++ if (qspi->dma_chtx) { ++ ret = dmaengine_slave_config(qspi->dma_chtx, &dma_cfg); ++ if (ret) { ++ dev_err(dev, "dma tx config failed\n"); ++ dma_release_channel(qspi->dma_chtx); ++ qspi->dma_chtx = NULL; ++ } ++ } ++ ++ init_completion(&qspi->dma_completion); ++} ++ ++static void stm32_qspi_dma_free(struct stm32_qspi *qspi) ++{ ++ if (qspi->dma_chtx) ++ dma_release_channel(qspi->dma_chtx); ++ if (qspi->dma_chrx) ++ dma_release_channel(qspi->dma_chrx); ++} ++ + /* + * no special host constraint, so use default spi_mem_default_supports_op + * to check supported mode. +@@ -398,6 +583,8 @@ static void stm32_qspi_release(struct stm32_qspi *qspi) + { + /* disable qspi */ + writel_relaxed(0, qspi->io_base + QSPI_CR); ++ stm32_qspi_dma_free(qspi); ++ sg_free_table(&qspi->dma_sgt); + mutex_destroy(&qspi->lock); + clk_disable_unprepare(qspi->clk); + } +@@ -422,6 +609,8 @@ static int stm32_qspi_probe(struct platform_device *pdev) + if (IS_ERR(qspi->io_base)) + return PTR_ERR(qspi->io_base); + ++ qspi->phys_base = res->start; ++ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi_mm"); + qspi->mm_base = devm_ioremap_resource(dev, res); + if (IS_ERR(qspi->mm_base)) +@@ -464,6 +653,7 @@ static int stm32_qspi_probe(struct platform_device *pdev) + + qspi->dev = dev; + platform_set_drvdata(pdev, qspi); ++ stm32_qspi_dma_setup(qspi); + mutex_init(&qspi->lock); + + ctrl->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD +diff --git a/include/dt-bindings/soc/stm32-hdp.h b/include/dt-bindings/soc/stm32-hdp.h +new file mode 100644 +index 0000000..d986653 +--- /dev/null ++++ b/include/dt-bindings/soc/stm32-hdp.h +@@ -0,0 +1,108 @@ ++/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ ++/* ++ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved ++ * Author: Roullier Christophe ++ * for STMicroelectronics. ++ */ ++ ++#ifndef _DT_BINDINGS_STM32_HDP_H ++#define _DT_BINDINGS_STM32_HDP_H ++ ++#define STM32_HDP(port, value) ((value) << ((port) * 4)) ++ ++/* define HDP Pins number*/ ++#define HDP0_PWR_PWRWAKE_SYS 0 ++#define HDP0_CM4_SLEEPDEEP 1 ++#define HDP0_PWR_STDBY_WKUP 2 ++#define HDP0_PWR_ENCOMP_VDDCORE 3 ++#define HDP0_BSEC_OUT_SEC_NIDEN 4 ++#define HDP0_RCC_CM4_SLEEPDEEP 6 ++#define HDP0_GPU_DBG7 7 ++#define HDP0_DDRCTRL_LP_REQ 8 ++#define HDP0_PWR_DDR_RET_ENABLE_N 9 ++#define HDP0_GPOVAL_0 15 ++ ++#define HDP1_PWR_PWRWAKE_MCU 0 ++#define HDP1_CM4_HALTED 1 ++#define HDP1_CA7_NAXIERRIRQ 2 ++#define HDP1_PWR_OKIN_MR 3 ++#define HDP1_BSEC_OUT_SEC_DBGEN 4 ++#define HDP1_EXTI_SYS_WAKEUP 5 ++#define HDP1_RCC_PWRDS_MPU 6 ++#define HDP1_GPU_DBG6 7 ++#define HDP1_DDRCTRL_DFI_CTRLUPD_REQ 8 ++#define HDP1_DDRCTRL_CACTIVE_DDRC_ASR 9 ++#define HDP1_GPOVAL_1 15 ++ ++#define HDP2_PWR_PWRWAKE_MPU 0 ++#define HDP2_CM4_RXEV 1 ++#define HDP2_CA7_NPMUIRQ1 2 ++#define HDP2_CA7_NFIQOUT1 3 ++#define HDP2_BSEC_IN_RSTCORE_N 4 ++#define HDP2_EXTI_C2_WAKEUP 5 ++#define HDP2_RCC_PWRDS_MCU 6 ++#define HDP2_GPU_DBG5 7 ++#define HDP2_DDRCTRL_DFI_INIT_COMPLETE 8 ++#define HDP2_DDRCTRL_PERF_OP_IS_REFRESH 9 ++#define HDP2_DDRCTRL_GSKP_DFI_LP_REQ 10 ++#define HDP2_GPOVAL_2 15 ++ ++#define HDP3_PWR_SEL_VTH_VDD_CORE 0 ++#define HDP3_CM4_TXEV 1 ++#define HDP3_CA7_NPMUIRQ0 2 ++#define HDP3_CA7_NFIQOUT0 3 ++#define HDP3_BSEC_OUT_SEC_DFTLOCK 4 ++#define HDP3_EXTI_C1_WAKEUP 5 ++#define HDP3_RCC_PWRDS_SYS 6 ++#define HDP3_GPU_DBG4 7 ++#define HDP3_DDRCTRL_STAT_DDRC_REG_SELREF_TYPE0 8 ++#define HDP3_DDRCTRL_CACTIVE_1 9 ++#define HDP3_GPOVAL_3 15 ++ ++#define HDP4_PWR_PDDS 0 ++#define HDP4_CM4_SLEEPING 1 ++#define HDP4_CA7_NRESET1 2 ++#define HDP4_CA7_NIRQOUT1 3 ++#define HDP4_BSEC_OUT_SEC_DFTEN 4 ++#define HDP4_BSEC_OUT_SEC_DBGSWENABLE 5 ++#define HDP4_ETH_OUT_PMT_INTR_O 6 ++#define HDP4_GPU_DBG3 7 ++#define HDP4_DDRCTRL_STAT_DDRC_REG_SELREF_TYPE1 8 ++#define HDP4_DDRCTRL_CACTIVE_0 9 ++#define HDP4_GPOVAL_4 15 ++ ++#define HDP5_CA7_STANDBYWFIL2 0 ++#define HDP5_PWR_VTH_VDDCORE_ACK 1 ++#define HDP5_CA7_NRESET0 2 ++#define HDP5_CA7_NIRQOUT0 3 ++#define HDP5_BSEC_IN_PWROK 4 ++#define HDP5_BSEC_OUT_SEC_DEVICEEN 5 ++#define HDP5_ETH_OUT_LPI_INTR_O 6 ++#define HDP5_GPU_DBG2 7 ++#define HDP5_DDRCTRL_CACTIVE_DDRC 8 ++#define HDP5_DDRCTRL_WR_CREDIT_CNT 9 ++#define HDP5_GPOVAL_5 15 ++ ++#define HDP6_CA7_STANDBYWFI1 0 ++#define HDP6_CA7_STANDBYWFE1 1 ++#define HDP6_CA7_EVENT0 2 ++#define HDP6_CA7_DBGACK1 3 ++#define HDP6_BSEC_OUT_SEC_SPNIDEN 5 ++#define HDP6_ETH_OUT_MAC_SPEED_O1 6 ++#define HDP6_GPU_DBG1 7 ++#define HDP6_DDRCTRL_CSYSACK_DDRC 8 ++#define HDP6_DDRCTRL_LPR_CREDIT_CNT 9 ++#define HDP6_GPOVAL_6 15 ++ ++#define HDP7_CA7_STANDBYWFI0 0 ++#define HDP7_CA7_STANDBYWFE0 1 ++#define HDP7_CA7_DBGACK0 3 ++#define HDP7_BSEC_OUT_FUSE_OK 4 ++#define HDP7_BSEC_OUT_SEC_SPIDEN 5 ++#define HDP7_ETH_OUT_MAC_SPEED_O0 6 ++#define HDP7_GPU_DBG0 7 ++#define HDP7_DDRCTRL_CSYSREQ_DDRC 8 ++#define HDP7_DDRCTRL_HPR_CREDIT_CNT 9 ++#define HDP7_GPOVAL_7 15 ++ ++#endif /* _DT_BINDINGS_STM32_HDP_H */ +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0044-ARM-stm32mp1-r0-rc3-DEVICETREE.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0044-ARM-stm32mp1-r0-rc3-DEVICETREE.patch new file mode 100644 index 0000000..084d363 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0044-ARM-stm32mp1-r0-rc3-DEVICETREE.patch @@ -0,0 +1,1392 @@ +From 494124b8ba0cd0d06a1cf099df668a93ee5e3f23 Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Mon, 10 Dec 2018 15:53:27 +0100 +Subject: [PATCH 44/52] ARM: stm32mp1-r0-rc3: DEVICETREE + +--- + arch/arm/boot/dts/stm32mp157-pinctrl.dtsi | 375 +++++++++++++++++++--- + arch/arm/boot/dts/stm32mp157a-dk1.dts | 47 ++- + arch/arm/boot/dts/stm32mp157c-dk2-a7-examples.dts | 14 + + arch/arm/boot/dts/stm32mp157c-dk2.dts | 10 + + arch/arm/boot/dts/stm32mp157c-ed1.dts | 66 +++- + arch/arm/boot/dts/stm32mp157c-ev1-a7-examples.dts | 15 + + arch/arm/boot/dts/stm32mp157c-ev1.dts | 46 ++- + arch/arm/boot/dts/stm32mp157c.dtsi | 221 ++++++++++++- + 8 files changed, 705 insertions(+), 89 deletions(-) + create mode 100644 arch/arm/boot/dts/stm32mp157c-dk2-a7-examples.dts + create mode 100644 arch/arm/boot/dts/stm32mp157c-ev1-a7-examples.dts + +diff --git a/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi b/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi +index 659094e..183d7ba 100644 +--- a/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi ++++ b/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi +@@ -295,7 +295,7 @@ + ; /* ETH_MDC */ + bias-disable; + drive-push-pull; +- slew-rate = <3>; ++ slew-rate = <2>; + }; + pins2 { + pinmux = , /* ETH_RGMII_RXD0 */ +@@ -345,7 +345,7 @@ + ; /* FMC_NE2_FMC_NCE */ + bias-disable; + drive-push-pull; +- slew-rate = <2>; ++ slew-rate = <1>; + }; + pins2 { + pinmux = ; /* FMC_NWAIT */ +@@ -372,6 +372,246 @@ + }; + }; + ++ hdp0_pins_a: hdp0-0 { ++ pins { ++ pinmux = ; /* HDP0 */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <2>; ++ }; ++ }; ++ ++ hdp0_pins_sleep_a: hdp0-sleep-0 { ++ pins { ++ pinmux = ; /* HDP0 */ ++ }; ++ }; ++ ++ hdp0_pins_b: hdp0-1 { ++ pins { ++ pinmux = ; /* HDP0 */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <2>; ++ }; ++ }; ++ ++ hdp0_pins_sleep_b: hdp0-sleep-1 { ++ pins { ++ pinmux = ; /* HDP0 */ ++ }; ++ }; ++ ++ hdp1_pins_a: hdp1-0 { ++ pins { ++ pinmux = ; /* HDP1 */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <2>; ++ }; ++ }; ++ ++ hdp1_pins_sleep_a: hdp1-sleep-0 { ++ pins { ++ pinmux = ; /* HDP1 */ ++ }; ++ }; ++ ++ hdp1_pins_b: hdp1-1 { ++ pins { ++ pinmux = ; /* HDP1 */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <2>; ++ }; ++ }; ++ ++ hdp1_pins_sleep_b: hdp1-sleep-1 { ++ pins { ++ pinmux = ; /* HDP1 */ ++ }; ++ }; ++ ++ hdp2_pins_a: hdp2-0 { ++ pins { ++ pinmux = ; /* HDP2 */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <2>; ++ }; ++ }; ++ ++ hdp2_pins_sleep_a: hdp2-sleep-0 { ++ pins { ++ pinmux = ; /* HDP2 */ ++ }; ++ }; ++ ++ hdp2_pins_b: hdp2-1 { ++ pins { ++ pinmux = ; /* HDP2 */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <2>; ++ }; ++ }; ++ ++ hdp2_pins_sleep_b: hdp2-sleep-1 { ++ pins { ++ pinmux = ; /* HDP2 */ ++ }; ++ }; ++ ++ hdp3_pins_a: hdp3-0 { ++ pins { ++ pinmux = ; /* HDP3 */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <2>; ++ }; ++ }; ++ ++ hdp3_pins_sleep_a: hdp3-sleep-0 { ++ pins { ++ pinmux = ; /* HDP3 */ ++ }; ++ }; ++ ++ hdp3_pins_b: hdp3-1 { ++ pins { ++ pinmux = ; /* HDP3 */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <2>; ++ }; ++ }; ++ ++ hdp3_pins_sleep_b: hdp3-sleep-1 { ++ pins { ++ pinmux = ; /* HDP3 */ ++ }; ++ }; ++ ++ hdp4_pins_a: hdp4-0 { ++ pins { ++ pinmux = ; /* HDP4 */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <2>; ++ }; ++ }; ++ ++ hdp4_pins_sleep_a: hdp4-sleep-0 { ++ pins { ++ pinmux = ; /* HDP4 */ ++ }; ++ }; ++ ++ hdp4_pins_b: hdp4-1 { ++ pins { ++ pinmux = ; /* HDP4 */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <2>; ++ }; ++ }; ++ ++ hdp4_pins_sleep_b: hdp4-sleep-1 { ++ pins { ++ pinmux = ; /* HDP4 */ ++ }; ++ }; ++ ++ hdp5_pins_a: hdp5-0 { ++ pins { ++ pinmux = ; /* HDP5 */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <2>; ++ }; ++ }; ++ ++ hdp5_pins_sleep_a: hdp5-sleep-0 { ++ pins { ++ pinmux = ; /* HDP5 */ ++ }; ++ }; ++ ++ hdp5_pins_b: hdp5-1 { ++ pins { ++ pinmux = ; /* HDP5 */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <2>; ++ }; ++ }; ++ ++ hdp5_pins_sleep_b: hdp5-sleep-1 { ++ pins { ++ pinmux = ; /* HDP5 */ ++ }; ++ }; ++ ++ hdp6_pins_a: hdp6-0 { ++ pins { ++ pinmux = ; /* HDP6 */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <2>; ++ }; ++ }; ++ ++ hdp6_pins_sleep_a: hdp6-sleep-0 { ++ pins { ++ pinmux = ; /* HDP6 */ ++ }; ++ }; ++ ++ hdp6_pins_b: hdp6-1 { ++ pins { ++ pinmux = ; /* HDP6 */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <2>; ++ }; ++ }; ++ ++ hdp6_pins_sleep_b: hdp6-sleep-1 { ++ pins { ++ pinmux = ; /* HDP6 */ ++ }; ++ }; ++ ++ hdp7_pins_a: hdp7-0 { ++ pins { ++ pinmux = ; /* HDP7 */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <2>; ++ }; ++ }; ++ ++ hdp7_pins_sleep_a: hdp7-sleep-0 { ++ pins { ++ pinmux = ; /* HDP7 */ ++ }; ++ }; ++ ++ hdp7_pins_b: hdp7-1 { ++ pins { ++ pinmux = ; /* HDP7 */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <2>; ++ }; ++ }; ++ ++ hdp7_pins_sleep_b: hdp7-sleep-1 { ++ pins { ++ pinmux = ; /* HDP7 */ ++ }; ++ }; ++ + i2c1_pins_a: i2c1-0 { + pins { + pinmux = , /* I2C1_SCL */ +@@ -475,7 +715,7 @@ + ; /* LCD_B7 */ + bias-disable; + drive-push-pull; +- slew-rate = <2>; ++ slew-rate = <1>; + }; + }; + +@@ -544,7 +784,7 @@ + ; /* LCD_B7 */ + bias-disable; + drive-push-pull; +- slew-rate = <2>; ++ slew-rate = <1>; + }; + }; + +@@ -584,7 +824,7 @@ + m_can1_pins_a: m-can1-0 { + pins1 { + pinmux = ; /* CAN1_TX */ +- slew-rate = <1>; ++ slew-rate = <0>; + drive-push-pull; + bias-disable; + }; +@@ -735,13 +975,13 @@ + ; /* QSPI_BK1_IO3 */ + bias-disable; + drive-push-pull; +- slew-rate = <3>; ++ slew-rate = <1>; + }; + pins2 { + pinmux = ; /* QSPI_BK1_NCS */ + bias-pull-up; + drive-push-pull; +- slew-rate = <3>; ++ slew-rate = <1>; + }; + }; + +@@ -763,13 +1003,13 @@ + ; /* QSPI_BK2_IO3 */ + bias-disable; + drive-push-pull; +- slew-rate = <3>; ++ slew-rate = <1>; + }; + pins2 { + pinmux = ; /* QSPI_BK2_NCS */ + bias-pull-up; + drive-push-pull; +- slew-rate = <3>; ++ slew-rate = <1>; + }; + }; + +@@ -805,17 +1045,12 @@ + }; + + sai2a_pins_a: sai2a-0 { +- pins1 { ++ pins { + pinmux = , /* SAI2_SCK_A */ + , /* SAI2_SD_A */ +- ; /* SAI2_FS_A */ +- slew-rate = <1>; +- drive-push-pull; +- bias-disable; +- }; +- pins2 { +- pinmux = ; /* SAI2_MCLK_A */ +- slew-rate = <2>; ++ , /* SAI2_FS_A */ ++ ; /* SAI2_MCLK_A */ ++ slew-rate = <0>; + drive-push-pull; + bias-disable; + }; +@@ -833,18 +1068,13 @@ + sai2b_pins_a: sai2b-0 { + pins1 { + pinmux = , /* SAI2_SCK_B */ +- ; /* SAI2_FS_B */ +- slew-rate = <1>; ++ , /* SAI2_FS_B */ ++ ; /* SAI2_MCLK_B */ ++ slew-rate = <0>; + drive-push-pull; + bias-disable; + }; + pins2 { +- pinmux = ; /* SAI2_MCLK_B */ +- slew-rate = <2>; +- drive-push-pull; +- bias-disable; +- }; +- pins3 { + pinmux = ; /* SAI2_SD_B */ + bias-disable; + }; +@@ -875,7 +1105,7 @@ + sai4a_pins_a: sai4a-0 { + pins { + pinmux = ; /* SAI4_SD_A */ +- slew-rate = <1>; ++ slew-rate = <0>; + drive-push-pull; + bias-disable; + }; +@@ -888,14 +1118,19 @@ + }; + + sdmmc1_b4_pins_a: sdmmc1-b4-0 { +- pins { ++ pins1 { + pinmux = , /* SDMMC1_D0 */ + , /* SDMMC1_D1 */ + , /* SDMMC1_D2 */ + , /* SDMMC1_D3 */ +- , /* SDMMC1_CK */ + ; /* SDMMC1_CMD */ +- slew-rate = <3>; ++ slew-rate = <1>; ++ drive-push-pull; ++ bias-disable; ++ }; ++ pins2 { ++ pinmux = ; /* SDMMC1_CK */ ++ slew-rate = <2>; + drive-push-pull; + bias-disable; + }; +@@ -906,15 +1141,20 @@ + pinmux = , /* SDMMC1_D0 */ + , /* SDMMC1_D1 */ + , /* SDMMC1_D2 */ +- , /* SDMMC1_D3 */ +- ; /* SDMMC1_CK */ +- slew-rate = <3>; ++ ; /* SDMMC1_D3 */ ++ slew-rate = <1>; + drive-push-pull; + bias-disable; + }; + pins2 { ++ pinmux = ; /* SDMMC1_CK */ ++ slew-rate = <2>; ++ drive-push-pull; ++ bias-disable; ++ }; ++ pins3 { + pinmux = ; /* SDMMC1_CMD */ +- slew-rate = <3>; ++ slew-rate = <1>; + drive-open-drain; + bias-disable; + }; +@@ -936,7 +1176,7 @@ + pinmux = , /* SDMMC1_D0DIR */ + , /* SDMMC1_D123DIR */ + ; /* SDMMC1_CDIR */ +- slew-rate = <3>; ++ slew-rate = <1>; + drive-push-pull; + bias-pull-up; + }; +@@ -956,14 +1196,19 @@ + }; + + sdmmc2_b4_pins_a: sdmmc2-b4-0 { +- pins { ++ pins1 { + pinmux = , /* SDMMC2_D0 */ + , /* SDMMC2_D1 */ + , /* SDMMC2_D2 */ + , /* SDMMC2_D3 */ +- , /* SDMMC2_CK */ + ; /* SDMMC2_CMD */ +- slew-rate = <3>; ++ slew-rate = <1>; ++ drive-push-pull; ++ bias-pull-up; ++ }; ++ pins2 { ++ pinmux = ; /* SDMMC2_CK */ ++ slew-rate = <2>; + drive-push-pull; + bias-pull-up; + }; +@@ -974,15 +1219,20 @@ + pinmux = , /* SDMMC2_D0 */ + , /* SDMMC2_D1 */ + , /* SDMMC2_D2 */ +- , /* SDMMC2_D3 */ +- ; /* SDMMC2_CK */ +- slew-rate = <3>; ++ ; /* SDMMC2_D3 */ ++ slew-rate = <1>; + drive-push-pull; + bias-pull-up; + }; + pins2 { ++ pinmux = ; /* SDMMC2_CK */ ++ slew-rate = <2>; ++ drive-push-pull; ++ bias-pull-up; ++ }; ++ pins3 { + pinmux = ; /* SDMMC2_CMD */ +- slew-rate = <3>; ++ slew-rate = <1>; + drive-open-drain; + bias-pull-up; + }; +@@ -1005,7 +1255,7 @@ + , /* SDMMC2_D5 */ + , /* SDMMC2_D6 */ + ; /* SDMMC2_D7 */ +- slew-rate = <3>; ++ slew-rate = <1>; + drive-push-pull; + bias-pull-up; + }; +@@ -1021,19 +1271,48 @@ + }; + + sdmmc3_b4_pins_a: sdmmc3-b4-0 { +- pins { ++ pins1 { + pinmux = , /* SDMMC3_D0 */ + , /* SDMMC3_D1 */ + , /* SDMMC3_D2 */ + , /* SDMMC3_D3 */ +- , /* SDMMC3_CK */ + ; /* SDMMC3_CMD */ +- slew-rate = <3>; ++ slew-rate = <1>; ++ drive-push-pull; ++ bias-pull-up; ++ }; ++ pins2 { ++ pinmux = ; /* SDMMC3_CK */ ++ slew-rate = <2>; + drive-push-pull; + bias-pull-up; + }; + }; + ++ sdmmc3_b4_od_pins_a: sdmmc3-b4-od-0 { ++ pins1 { ++ pinmux = , /* SDMMC3_D0 */ ++ , /* SDMMC3_D1 */ ++ , /* SDMMC3_D2 */ ++ ; /* SDMMC3_D3 */ ++ slew-rate = <1>; ++ drive-push-pull; ++ bias-pull-up; ++ }; ++ pins2 { ++ pinmux = ; /* SDMMC3_CK */ ++ slew-rate = <2>; ++ drive-push-pull; ++ bias-pull-up; ++ }; ++ pins3 { ++ pinmux = ; /* SDMMC2_CMD */ ++ slew-rate = <1>; ++ drive-open-drain; ++ bias-pull-up; ++ }; ++ }; ++ + sdmmc3_b4_sleep_pins_a: sdmmc3-b4-sleep-0 { + pins { + pinmux = , /* SDMMC3_D0 */ +@@ -1140,7 +1419,7 @@ + ; /* USART2_RTS */ + bias-disable; + drive-push-pull; +- slew-rate = <3>; ++ slew-rate = <0>; + }; + pins2 { + pinmux = , /* USART2_RX */ +@@ -1264,7 +1543,7 @@ + pins-are-numbered; + interrupt-parent = <&exti>; + st,syscfg = <&exti 0x60 0xff>; +- hwlocks = <&hsem 1>; ++ hwlocks = <&hsem 0>; + + gpioz: gpio@54004000 { + gpio-controller; +diff --git a/arch/arm/boot/dts/stm32mp157a-dk1.dts b/arch/arm/boot/dts/stm32mp157a-dk1.dts +index 2f01e97..0a6cf35 100644 +--- a/arch/arm/boot/dts/stm32mp157a-dk1.dts ++++ b/arch/arm/boot/dts/stm32mp157a-dk1.dts +@@ -35,9 +35,39 @@ + #size-cells = <1>; + ranges; + +- ipc_share: sram_rproc@10040000 { ++ retram: retram@0x38000000 { + compatible = "shared-dma-pool"; +- reg = <0x10040000 0x10000>; ++ reg = <0x38000000 0x10000>; ++ no-map; ++ }; ++ ++ mcuram: mcuram@0x30000000 { ++ compatible = "shared-dma-pool"; ++ reg = <0x30000000 0x40000>; ++ no-map; ++ }; ++ ++ mcuram2: mcuram2@0x10000000 { ++ compatible = "shared-dma-pool"; ++ reg = <0x10000000 0x40000>; ++ no-map; ++ }; ++ ++ vdev0vring0: vdev0vring0@10040000 { ++ compatible = "shared-dma-pool"; ++ reg = <0x10040000 0x2000>; ++ no-map; ++ }; ++ ++ vdev0vring1: vdev0vring1@10042000 { ++ compatible = "shared-dma-pool"; ++ reg = <0x10042000 0x2000>; ++ no-map; ++ }; ++ ++ vdev0buffer: vdev0buffer@10044000 { ++ compatible = "shared-dma-pool"; ++ reg = <0x10044000 0x4000>; + no-map; + }; + +@@ -77,6 +107,7 @@ + + sound { + compatible = "audio-graph-card"; ++ label = "STM32MP1-DK"; + routing = + "Playback" , "MCLK", + "Capture" , "MCLK", +@@ -306,7 +337,7 @@ + regulator-max-microvolt = <3300000>; + regulator-always-on; + st,mask-reset; +- regulator-initial-mode = <2>; ++ regulator-initial-mode = <0>; + regulator-over-current-protection; + }; + +@@ -316,7 +347,7 @@ + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-over-current-protection; +- regulator-initial-mode = <2>; ++ regulator-initial-mode = <0>; + }; + + v1v8_audio: ldo1 { +@@ -402,12 +433,13 @@ + + watchdog { + compatible = "st,stpmic1-wdt"; ++ status = "disabled"; + }; + }; + }; + + &i2s2 { +- clocks = <&rcc SPI2>, <&rcc SPI2_K>, <&rcc PLL3_Q>, <&rcc PLL4_Q>; ++ clocks = <&rcc SPI2>, <&rcc SPI2_K>, <&rcc PLL3_Q>, <&rcc PLL3_R>; + clock-names = "pclk", "i2sclk", "x8k", "x11k"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&i2s2_pins_a>; +@@ -447,7 +479,8 @@ + }; + + &m4_rproc { +- memory-region = <&ipc_share>; ++ memory-region = <&retram>, <&mcuram>, <&mcuram2>, <&vdev0vring0>, ++ <&vdev0vring1>, <&vdev0buffer>; + mboxes = <&ipcc 0>, <&ipcc 1>, <&ipcc 2>; + mbox-names = "vq0", "vq1", "shutdown"; + interrupt-parent = <&exti>; +@@ -470,7 +503,7 @@ + }; + + &sai2 { +- clocks = <&rcc SAI2>, <&rcc PLL3_Q>, <&rcc PLL3_Q>; ++ clocks = <&rcc SAI2>, <&rcc PLL3_Q>, <&rcc PLL3_R>; + clock-names = "pclk", "x8k", "x11k"; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&sai2a_pins_a>, <&sai2b_pins_b>; +diff --git a/arch/arm/boot/dts/stm32mp157c-dk2-a7-examples.dts b/arch/arm/boot/dts/stm32mp157c-dk2-a7-examples.dts +new file mode 100644 +index 0000000..9c89733 +--- /dev/null ++++ b/arch/arm/boot/dts/stm32mp157c-dk2-a7-examples.dts +@@ -0,0 +1,14 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) ++/* ++ * Copyright (C) STMicroelectronics 2017 - All Rights Reserved ++ * Author: Alexandre Torgue for STMicroelectronics. ++ */ ++ ++/dts-v1/; ++ ++#include "stm32mp157c-dk2.dts" ++ ++/ { ++ model = "STMicroelectronics STM32MP157C-DK2 configured to run Linux A7 examples"; ++ compatible = "st,stm32mp157c-dk2-a7-examples", "st,stm32mp157c-dk2", "st,stm32mp157"; ++}; +diff --git a/arch/arm/boot/dts/stm32mp157c-dk2.dts b/arch/arm/boot/dts/stm32mp157c-dk2.dts +index fd00386..c276c59 100644 +--- a/arch/arm/boot/dts/stm32mp157c-dk2.dts ++++ b/arch/arm/boot/dts/stm32mp157c-dk2.dts +@@ -72,6 +72,16 @@ + touchscreen-size-y = <800>; + status = "okay"; + }; ++ touchscreen@38 { ++ compatible = "focaltech,ft6336"; ++ reg = <0x38>; ++ interrupts = <2 2>; ++ interrupt-parent = <&gpiof>; ++ interrupt-controller; ++ touchscreen-size-x = <480>; ++ touchscreen-size-y = <800>; ++ status = "okay"; ++ }; + }; + + <dc { +diff --git a/arch/arm/boot/dts/stm32mp157c-ed1.dts b/arch/arm/boot/dts/stm32mp157c-ed1.dts +index 4f46b41..798580e 100644 +--- a/arch/arm/boot/dts/stm32mp157c-ed1.dts ++++ b/arch/arm/boot/dts/stm32mp157c-ed1.dts +@@ -28,9 +28,39 @@ + #size-cells = <1>; + ranges; + +- ipc_share: sram_rproc@10040000 { ++ retram: retram@0x38000000 { + compatible = "shared-dma-pool"; +- reg = <0x10040000 0x10000>; ++ reg = <0x38000000 0x10000>; ++ no-map; ++ }; ++ ++ mcuram: mcuram@0x30000000 { ++ compatible = "shared-dma-pool"; ++ reg = <0x30000000 0x40000>; ++ no-map; ++ }; ++ ++ mcuram2: mcuram2@0x10000000 { ++ compatible = "shared-dma-pool"; ++ reg = <0x10000000 0x40000>; ++ no-map; ++ }; ++ ++ vdev0vring0: vdev0vring0@10040000 { ++ compatible = "shared-dma-pool"; ++ reg = <0x10040000 0x2000>; ++ no-map; ++ }; ++ ++ vdev0vring1: vdev0vring1@10042000 { ++ compatible = "shared-dma-pool"; ++ reg = <0x10042000 0x2000>; ++ no-map; ++ }; ++ ++ vdev0buffer: vdev0buffer@10044000 { ++ compatible = "shared-dma-pool"; ++ reg = <0x10044000 0x4000>; + no-map; + }; + +@@ -71,6 +101,19 @@ + default-state = "off"; + }; + }; ++ ++ sd_switch: regulator-sd_switch { ++ compatible = "regulator-gpio"; ++ regulator-name = "sd_switch"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <2900000>; ++ regulator-type = "voltage"; ++ regulator-always-on; ++ ++ gpios = <&gpiof 14 GPIO_ACTIVE_HIGH>; ++ gpios-states = <0>; ++ states = <1800000 0x1 2900000 0x0>; ++ }; + }; + + &adc { +@@ -121,6 +164,10 @@ + sram = <&dma_pool>; + }; + ++&dts { ++ status = "okay"; ++}; ++ + &gpu { + contiguous-area = <&gpu_reserved>; + status = "okay"; +@@ -187,7 +234,7 @@ + regulator-max-microvolt = <3300000>; + regulator-always-on; + st,mask-reset; +- regulator-initial-mode = <2>; ++ regulator-initial-mode = <0>; + regulator-over-current-protection; + }; + +@@ -197,7 +244,7 @@ + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-over-current-protection; +- regulator-initial-mode = <2>; ++ regulator-initial-mode = <0>; + }; + + vdda: ldo1 { +@@ -277,6 +324,7 @@ + + watchdog { + compatible = "st,stpmic1-wdt"; ++ status = "disabled"; + }; + }; + }; +@@ -291,7 +339,8 @@ + }; + + &m4_rproc { +- memory-region = <&ipc_share>; ++ memory-region = <&retram>, <&mcuram>, <&mcuram2>, <&vdev0vring0>, ++ <&vdev0vring1>, <&vdev0buffer>; + mboxes = <&ipcc 0>, <&ipcc 1>, <&ipcc 2>; + mbox-names = "vq0", "vq1", "shutdown"; + interrupt-parent = <&exti>; +@@ -324,6 +373,12 @@ + st,use-ckin; + bus-width = <4>; + vmmc-supply = <&vdd_sd>; ++ vqmmc-supply = <&sd_switch>; ++ sd-uhs-sdr12; ++ sd-uhs-sdr25; ++ sd-uhs-sdr50; ++ sd-uhs-ddr50; ++ sd-uhs-sdr104; + status = "okay"; + }; + +@@ -339,6 +394,7 @@ + bus-width = <8>; + vmmc-supply = <&v3v3>; + vqmmc-supply = <&v3v3>; ++ mmc-ddr-3_3v; + status = "okay"; + }; + +diff --git a/arch/arm/boot/dts/stm32mp157c-ev1-a7-examples.dts b/arch/arm/boot/dts/stm32mp157c-ev1-a7-examples.dts +new file mode 100644 +index 0000000..5682fb3 +--- /dev/null ++++ b/arch/arm/boot/dts/stm32mp157c-ev1-a7-examples.dts +@@ -0,0 +1,15 @@ ++// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) ++/* ++ * Copyright (C) STMicroelectronics 2017 - All Rights Reserved ++ * Author: Alexandre Torgue for STMicroelectronics. ++ */ ++ ++/dts-v1/; ++ ++#include "stm32mp157c-ev1.dts" ++ ++/ { ++ model = "STMicroelectronics STM32MP157C-EV1 configured to run Linux A7 examples"; ++ compatible = "st,stm32mp157c-ev1-a7-examples", "st,stm32mp157c-ev1", "st,stm32mp157c-ed1", "st,stm32mp157"; ++}; ++ +diff --git a/arch/arm/boot/dts/stm32mp157c-ev1.dts b/arch/arm/boot/dts/stm32mp157c-ev1.dts +index b60bffc..49a62aa 100644 +--- a/arch/arm/boot/dts/stm32mp157c-ev1.dts ++++ b/arch/arm/boot/dts/stm32mp157c-ev1.dts +@@ -8,6 +8,7 @@ + #include "stm32mp157c-ed1.dts" + #include + #include ++#include + + / { + model = "STMicroelectronics STM32MP157C eval daughter on eval mother"; +@@ -100,19 +101,15 @@ + + sound { + compatible = "audio-graph-card"; ++ label = "STM32MP1-EV"; + routing = + "AIF1CLK" , "MCLK1", + "AIF2CLK" , "MCLK1", + "IN1LN" , "MICBIAS2", + "DMIC2DAT" , "MICBIAS1", + "DMIC1DAT" , "MICBIAS1"; +- dais = <&sai2a_port &sai2b_port &sai4a_port &spdifrx_port>; +- status = "okay"; +- }; +- +- dmics { +- compatible = "audio-graph-card"; +- dais = <&cpu_port0 &cpu_port1 &cpu_port2 &cpu_port3>; ++ dais = <&sai2a_port &sai2b_port &sai4a_port &spdifrx_port ++ &dfsdm0_port &dfsdm1_port &dfsdm2_port &dfsdm3_port>; + status = "okay"; + }; + +@@ -228,7 +225,7 @@ + io-channels = <&dfsdm0 0>; + status = "okay"; + +- cpu_port0: port { ++ dfsdm0_port: port { + dfsdm_endpoint0: endpoint { + remote-endpoint = <&dmic0_endpoint>; + }; +@@ -251,7 +248,7 @@ + io-channels = <&dfsdm1 0>; + status = "okay"; + +- cpu_port1: port { ++ dfsdm1_port: port { + dfsdm_endpoint1: endpoint { + remote-endpoint = <&dmic1_endpoint>; + }; +@@ -274,7 +271,7 @@ + io-channels = <&dfsdm2 0>; + status = "okay"; + +- cpu_port2: port { ++ dfsdm2_port: port { + dfsdm_endpoint2: endpoint { + remote-endpoint = <&dmic2_endpoint>; + }; +@@ -297,7 +294,7 @@ + io-channels = <&dfsdm3 0>; + status = "okay"; + +- cpu_port3: port { ++ dfsdm3_port: port { + dfsdm_endpoint3: endpoint { + remote-endpoint = <&dmic3_endpoint>; + }; +@@ -381,6 +378,17 @@ + }; + }; + ++&hdp { ++ pinctrl-names = "default", "sleep"; ++ pinctrl-0 = <&hdp0_pins_a &hdp6_pins_a &hdp7_pins_a>; ++ pinctrl-1 = <&hdp0_pins_sleep_a &hdp6_pins_sleep_a &hdp7_pins_sleep_a>; ++ status = "disabled"; ++ ++ muxing-hdp = <(STM32_HDP(0, HDP0_GPOVAL_0) | ++ STM32_HDP(6, HDP6_GPOVAL_6) | ++ STM32_HDP(7, HDP7_GPOVAL_7))>; ++}; ++ + &i2c2 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&i2c2_pins_a>; +@@ -577,7 +585,7 @@ + }; + + &sai2 { +- clocks = <&rcc SAI2>, <&rcc PLL3_Q>, <&rcc PLL4_Q>; ++ clocks = <&rcc SAI2>, <&rcc PLL3_Q>, <&rcc PLL3_R>; + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&sai2a_pins_a>, <&sai2b_pins_a>; + pinctrl-1 = <&sai2a_sleep_pins_a>, <&sai2b_sleep_pins_a>; +@@ -617,7 +625,7 @@ + }; + + &sai4 { +- clocks = <&rcc SAI4>, <&rcc PLL3_Q>, <&rcc PLL4_Q>; ++ clocks = <&rcc SAI4>, <&rcc PLL3_Q>, <&rcc PLL3_R>; + clock-names = "pclk", "x8k", "x11k"; + status = "okay"; + +@@ -639,6 +647,18 @@ + }; + }; + ++&sdmmc3 { ++ pinctrl-names = "default", "opendrain", "sleep"; ++ pinctrl-0 = <&sdmmc3_b4_pins_a>; ++ pinctrl-1 = <&sdmmc3_b4_od_pins_a>; ++ pinctrl-2 = <&sdmmc3_b4_sleep_pins_a>; ++ vmmc-supply = <&v3v3>; ++ broken-cd; ++ st,neg-edge; ++ bus-width = <4>; ++ status = "disabled"; ++}; ++ + &spdifrx { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&spdifrx_pins_a>; +diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi +index cef7b3e..7a7ef47 100644 +--- a/arch/arm/boot/dts/stm32mp157c.dtsi ++++ b/arch/arm/boot/dts/stm32mp157c.dtsi +@@ -156,6 +156,29 @@ + mask = <0x1>; + }; + ++ replicator { ++ /* ++ * non-configurable replicators don't show up on the ++ * AMBA bus. As such no need to add "arm,primecell" ++ */ ++ compatible = "arm,coresight-replicator"; ++ clocks = <&rcc CK_TRACE>; ++ clock-names = "apb_pclk"; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ /* replicator output ports */ ++ port@0 { ++ reg = <0>; ++ replicator_out_port0: endpoint { ++ remote-endpoint = <&funnel_in_port4>; ++ }; ++ }; ++ }; ++ }; ++ + soc { + compatible = "simple-bus"; + #address-cells = <1>; +@@ -471,6 +494,7 @@ + interrupts-extended = <&intc GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>, + <&exti 27 1>; + clocks = <&rcc USART2_K>; ++ resets = <&rcc USART2_R>; + wakeup-source; + power-domains = <&pd_core>; + status = "disabled"; +@@ -483,6 +507,7 @@ + interrupts-extended = <&intc GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>, + <&exti 28 1>; + clocks = <&rcc USART3_K>; ++ resets = <&rcc USART3_R>; + wakeup-source; + power-domains = <&pd_core>; + status = "disabled"; +@@ -495,6 +520,7 @@ + interrupts-extended = <&intc GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>, + <&exti 30 1>; + clocks = <&rcc UART4_K>; ++ resets = <&rcc UART4_R>; + wakeup-source; + power-domains = <&pd_core>; + status = "disabled"; +@@ -507,6 +533,7 @@ + interrupts-extended = <&intc GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>, + <&exti 31 1>; + clocks = <&rcc UART5_K>; ++ resets = <&rcc UART5_R>; + wakeup-source; + power-domains = <&pd_core>; + status = "disabled"; +@@ -629,6 +656,7 @@ + interrupts-extended = <&intc GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>, + <&exti 32 1>; + clocks = <&rcc UART7_K>; ++ resets = <&rcc UART7_R>; + wakeup-source; + power-domains = <&pd_core>; + status = "disabled"; +@@ -641,6 +669,7 @@ + interrupts-extended = <&intc GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>, + <&exti 33 1>; + clocks = <&rcc UART8_K>; ++ resets = <&rcc UART8_R>; + wakeup-source; + power-domains = <&pd_core>; + status = "disabled"; +@@ -713,6 +742,7 @@ + interrupts-extended = <&intc GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>, + <&exti 29 1>; + clocks = <&rcc USART6_K>; ++ resets = <&rcc USART6_R>; + wakeup-source; + power-domains = <&pd_core>; + status = "disabled"; +@@ -1110,7 +1140,7 @@ + reg = <0x0>; + interrupt-parent = <&adc>; + interrupts = <0>; +- dmas = <&dmamux1 9 0x400 0x01>; ++ dmas = <&dmamux1 9 0x400 0x05>; + dma-names = "rx"; + status = "disabled"; + }; +@@ -1121,7 +1151,7 @@ + reg = <0x100>; + interrupt-parent = <&adc>; + interrupts = <1>; +- dmas = <&dmamux1 10 0x400 0x01>; ++ dmas = <&dmamux1 10 0x400 0x05>; + dma-names = "rx"; + /* temperature sensor */ + st,adc-channels = <12>; +@@ -1165,8 +1195,8 @@ + + sdmmc3: sdmmc@48004000 { + compatible = "arm,pl18x", "arm,primecell"; +- arm,primecell-periphid = <0x10153180>; +- reg = <0x48004000 0x400>; ++ arm,primecell-periphid = <0x00253180>; ++ reg = <0x48004000 0x400>, <0x48005000 0x400>; + interrupts = ; + interrupt-names = "cmd_irq"; + clocks = <&rcc SDMMC3_K>; +@@ -1278,7 +1308,7 @@ + interrupt-controller; + #interrupt-cells = <2>; + reg = <0x5000d000 0x400>; +- hwlocks = <&hsem 2>; ++ hwlocks = <&hsem 1>; + + /* exti_pwr is an extra interrupt controller used for + * EXTI 55 to 60. It's mapped on pwr interrupt +@@ -1427,6 +1457,157 @@ + status = "disabled"; + }; + ++ hdp: hdp@5002a000 { ++ compatible = "st,stm32mp1-hdp"; ++ reg = <0x5002a000 0x400>; ++ clocks = <&rcc HDP>; ++ clock-names = "hdp"; ++ status = "disabled"; ++ }; ++ ++ funnel: funnel@50091000 { ++ compatible = "arm,coresight-funnel", "arm,primecell"; ++ reg = <0x50091000 0x1000>; ++ clocks = <&rcc CK_TRACE>; ++ clock-names = "apb_pclk"; ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ /* funnel input ports */ ++ port@0 { ++ reg = <0>; ++ funnel_in_port0: endpoint { ++ slave-mode; ++ remote-endpoint = <&stm_out_port>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <1>; ++ funnel_in_port1: endpoint { ++ slave-mode; /* A7-1 input */ ++ remote-endpoint = <&etm1_out_port>; ++ }; ++ }; ++ ++ port@2 { ++ reg = <2>; ++ funnel_in_port2: endpoint { ++ slave-mode; /* A7-2 input */ ++ remote-endpoint = <&etm2_out_port>; ++ }; ++ }; ++ ++ port@4 { ++ reg = <4>; ++ funnel_in_port4: endpoint { ++ slave-mode; /* REPLICATOR input */ ++ remote-endpoint = <&replicator_out_port0>; ++ }; ++ }; ++ ++ port@5 { ++ reg = <0>; ++ funnel_out_port0: endpoint { ++ remote-endpoint = <&etf_in_port>; ++ }; ++ }; ++ }; ++ }; ++ ++ etf: etf@50092000 { ++ compatible = "arm,coresight-tmc", "arm,primecell"; ++ reg = <0x50092000 0x1000>; ++ clocks = <&rcc CK_TRACE>; ++ clock-names = "apb_pclk"; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ reg = <0>; ++ etf_in_port: endpoint { ++ slave-mode; ++ remote-endpoint = <&funnel_out_port0>; ++ }; ++ }; ++ ++ port@1 { ++ reg = <0>; ++ etf_out_port: endpoint { ++ remote-endpoint = <&tpiu_in_port>; ++ }; ++ }; ++ }; ++ }; ++ ++ tpiu: tpiu@50093000 { ++ compatible = "arm,coresight-tpiu", "arm,primecell"; ++ reg = <0x50093000 0x1000>; ++ clocks = <&rcc CK_TRACE>; ++ clock-names = "apb_pclk"; ++ ++ port { ++ tpiu_in_port: endpoint { ++ slave-mode; ++ remote-endpoint = <&etf_out_port>; ++ }; ++ }; ++ }; ++ ++ stm: stm@500a0000 { ++ compatible = "arm,coresight-stm", "arm,primecell"; ++ reg = <0x500a0000 0x1000>, <0x90000000 0x1000000>, ++ <0x50094000 0x1000>; ++ reg-names = "stm-base", "stm-stimulus-base", "cti-base"; ++ ++ clocks = <&rcc CK_TRACE>; ++ clock-names = "apb_pclk"; ++ ++ ports { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ port@0 { ++ reg = <0>; ++ stm_out_port: endpoint { ++ remote-endpoint = <&funnel_in_port0>; ++ }; ++ }; ++ }; ++ }; ++ ++ /* Cortex A7-1 */ ++ etm1: etm@500dc000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0x500dc000 0x1000>; ++ cpu = <&cpu0>; ++ clocks = <&rcc CK_TRACE>; ++ clock-names = "apb_pclk"; ++ port { ++ etm1_out_port: endpoint { ++ remote-endpoint = <&funnel_in_port1>; ++ }; ++ }; ++ }; ++ ++ /* Cortex A7-2 */ ++ etm2: etm@500dd000 { ++ compatible = "arm,coresight-etm3x", "arm,primecell"; ++ reg = <0x500dd000 0x1000>; ++ cpu = <&cpu1>; ++ clocks = <&rcc CK_TRACE>; ++ clock-names = "apb_pclk"; ++ ++ port { ++ etm2_out_port: endpoint { ++ remote-endpoint = <&funnel_in_port2>; ++ }; ++ }; ++ }; ++ + cryp1: cryp@54001000 { + compatible = "st,stm32mp1-cryp"; + reg = <0x54001000 0x400>; +@@ -1490,6 +1671,9 @@ + reg = <0x58003000 0x1000>, <0x70000000 0x10000000>; + reg-names = "qspi", "qspi_mm"; + interrupts = ; ++ dmas = <&mdma1 22 0x10 0x100002 0x0 0x0 0x0>, ++ <&mdma1 22 0x10 0x100008 0x0 0x0 0x0>; ++ dma-names = "tx", "rx"; + clocks = <&rcc QSPI_K>; + resets = <&rcc QSPI_R>; + status = "disabled"; +@@ -1497,8 +1681,8 @@ + + sdmmc1: sdmmc@58005000 { + compatible = "arm,pl18x", "arm,primecell"; +- arm,primecell-periphid = <0x10153180>; +- reg = <0x58005000 0x1000>; ++ arm,primecell-periphid = <0x00253180>; ++ reg = <0x58005000 0x1000>, <0x58006000 0x1000>; + interrupts = ; + interrupt-names = "cmd_irq"; + clocks = <&rcc SDMMC1_K>; +@@ -1512,8 +1696,8 @@ + + sdmmc2: sdmmc@58007000 { + compatible = "arm,pl18x", "arm,primecell"; +- arm,primecell-periphid = <0x10153180>; +- reg = <0x58007000 0x1000>; ++ arm,primecell-periphid = <0x00253180>; ++ reg = <0x58007000 0x1000>, <0x58008000 0x1000>; + interrupts = ; + interrupt-names = "cmd_irq"; + clocks = <&rcc SDMMC2_K>; +@@ -1551,16 +1735,15 @@ + clock-names = "stmmaceth", + "mac-clk-tx", + "mac-clk-rx", +- "ethstp", +- "syscfg-clk"; ++ "ethstp"; + clocks = <&rcc ETHMAC>, + <&rcc ETHTX>, + <&rcc ETHRX>, +- <&rcc ETHSTP>, +- <&rcc SYSCFG>; ++ <&rcc ETHSTP>; + st,syscon = <&syscfg 0x4>; + snps,mixed-burst; + snps,pbl = <2>; ++ snps,en-tx-lpi-clockgating; + snps,axi-config = <&stmmac_axi_config_0>; + snps,tso; + power-domains = <&pd_core>; +@@ -1573,6 +1756,7 @@ + clocks = <&rcc USBH>; + resets = <&rcc USBH_R>; + interrupts = ; ++ power-domains = <&pd_core>; + status = "disabled"; + }; + +@@ -1583,6 +1767,7 @@ + resets = <&rcc USBH_R>; + interrupts = ; + companion = <&usbh_ohci>; ++ power-domains = <&pd_core>; + status = "disabled"; + }; + +@@ -1656,6 +1841,7 @@ + interrupts-extended = <&intc GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>, + <&exti 26 1>; + clocks = <&rcc USART1_K>; ++ resets = <&rcc USART1_R>; + wakeup-source; + power-domains = <&pd_core>; + status = "disabled"; +@@ -1758,9 +1944,12 @@ + + m4_rproc: m4@0 { + compatible = "st,stm32mp1-rproc"; +- reg = <0x38000000 0x10000>, +- <0x10000000 0x40000>; +- reg-names = "retram", "mcusram"; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ ranges = <0x00000000 0x38000000 0x10000>, ++ <0x30000000 0x30000000 0x60000>, ++ <0x10000000 0x10000000 0x60000>; + resets = <&rcc MCU_R>; + reset-names = "mcu_rst"; + st,syscfg-pdds = <&pwr 0x014 0x1>; +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0045-ARM-stm32mp1-r0-rc3-DEFCONFIG.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0045-ARM-stm32mp1-r0-rc3-DEFCONFIG.patch new file mode 100644 index 0000000..a36c956 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0045-ARM-stm32mp1-r0-rc3-DEFCONFIG.patch @@ -0,0 +1,101 @@ +From c38f0f42372e03b8b284804190fdf53ce02722db Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Mon, 10 Dec 2018 15:53:55 +0100 +Subject: [PATCH 45/52] ARM: stm32mp1-r0-rc3: DEFCONFIG + +--- + arch/arm/configs/fragment-02-multiv7_addons.config | 39 +++++++++++++++++----- + 1 file changed, 31 insertions(+), 8 deletions(-) + +diff --git a/arch/arm/configs/fragment-02-multiv7_addons.config b/arch/arm/configs/fragment-02-multiv7_addons.config +index 4470d85..6ae0453 100644 +--- a/arch/arm/configs/fragment-02-multiv7_addons.config ++++ b/arch/arm/configs/fragment-02-multiv7_addons.config +@@ -98,7 +98,10 @@ CONFIG_CAN_M_CAN=y + # + # Bluetooth device drivers + # +- ++CONFIG_BT_RTL=m ++CONFIG_BT_HCIBTUSB=m ++CONFIG_BT_HCIBTUSB=m ++CONFIG_BT_HCIBTUSB_RTL=y + # + # Device Drivers + # +@@ -138,7 +141,12 @@ CONFIG_CHR_DEV_SG=y + # MII PHY device drivers + # + # CONFIG_REALTEK_PHY is not set +- ++CONFIG_RTL_CARDS=m ++CONFIG_RTL8192CU=m ++CONFIG_RTLWIFI=m ++CONFIG_RTLWIFI_USB=m ++CONFIG_RTLWIFI_DEBUG=y ++CONFIG_RTL8192C_COMMON=m + # + # Input Device Drivers + # +@@ -271,14 +279,14 @@ CONFIG_VIDEO_OV5640=y + # + # Display Panels + # +-CONFIG_DRM_PANEL_ORISETECH_OTM8009A=m +-CONFIG_DRM_PANEL_RAYDIUM_RM68200=m ++CONFIG_DRM_PANEL_ORISETECH_OTM8009A=y ++CONFIG_DRM_PANEL_RAYDIUM_RM68200=y + + # + # Display Interface Bridges + # +-CONFIG_VIDEO_ADV7511=m +-CONFIG_DRM_SII902X=m ++CONFIG_VIDEO_ADV7511=y ++CONFIG_DRM_SII902X=y + + # + # Frame buffer hardware drivers +@@ -287,8 +295,8 @@ CONFIG_DRM_SII902X=m + # + # Console display driver support + # +-CONFIG_DRM_STM=m +-CONFIG_DRM_STM_DSI=m ++CONFIG_DRM_STM=y ++CONFIG_DRM_STM_DSI=y + + # + # Backlight support +@@ -359,6 +367,8 @@ CONFIG_USB_CONFIGFS=y + # File systems + # + CONFIG_OVERLAY_FS=y ++# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set ++CONFIG_JFFS2_FS=y + + # + # Pseudo filesystems +@@ -452,6 +462,19 @@ CONFIG_REGULATOR_STM32_VREFBUF=y + CONFIG_NVMEM_STM32_ROMEM=y + + # ++# STM32 CORESIGHT ++# ++CONFIG_STM_SOURCE_CONSOLE=y ++CONFIG_STM_SOURCE_FTRACE=y ++CONFIG_FUNCTION_TRACER=y ++CONFIG_CORESIGHT=y ++CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y ++CONFIG_CORESIGHT_SINK_TPIU=y ++CONFIG_CORESIGHT_SINK_ETBV10=y ++CONFIG_CORESIGHT_SOURCE_ETM3X=y ++CONFIG_CORESIGHT_STM=y ++ ++# + # STM32 IPCC + # + CONFIG_STM32_IPCC=y +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0046-ARM-stm32mp1-r0-rc4-MMC-MTD.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0046-ARM-stm32mp1-r0-rc4-MMC-MTD.patch new file mode 100644 index 0000000..52ec427 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0046-ARM-stm32mp1-r0-rc4-MMC-MTD.patch @@ -0,0 +1,25 @@ +From 1c6166a178939316cfdd437f8c999616ddf9a7bd Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Fri, 21 Dec 2018 16:57:57 +0100 +Subject: [PATCH 46/52] ARM: stm32mp1-r0-rc4: MMC MTD + +--- + drivers/mmc/host/mmci.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c +index 6c2b1a0..0b7b607 100644 +--- a/drivers/mmc/host/mmci.c ++++ b/drivers/mmc/host/mmci.c +@@ -2072,6 +2072,8 @@ static int mmci_probe(struct amba_device *dev, + else if (plat->ocr_mask) + dev_warn(mmc_dev(mmc), "Platform OCR mask is ignored\n"); + ++ host->pwr_reg = readl_relaxed(host->base + MMCIPOWER); ++ + /* We support these capabilities. */ + mmc->caps |= MMC_CAP_CMD23; + +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0047-ARM-stm32mp1-r0-rc4-PINCTRL-PWM-RESET-RTC.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0047-ARM-stm32mp1-r0-rc4-PINCTRL-PWM-RESET-RTC.patch new file mode 100644 index 0000000..fd29035 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0047-ARM-stm32mp1-r0-rc4-PINCTRL-PWM-RESET-RTC.patch @@ -0,0 +1,47 @@ +From 41d9204c142d8245f898ca877b4e4044d5db2426 Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Fri, 21 Dec 2018 16:58:12 +0100 +Subject: [PATCH 47/52] ARM: stm32mp1-r0-rc4: PINCTRL PWM RESET RTC + +--- + drivers/pinctrl/pinctrl-stmfx.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/drivers/pinctrl/pinctrl-stmfx.c b/drivers/pinctrl/pinctrl-stmfx.c +index 15d5757..b68fece 100644 +--- a/drivers/pinctrl/pinctrl-stmfx.c ++++ b/drivers/pinctrl/pinctrl-stmfx.c +@@ -542,6 +542,7 @@ static irqreturn_t stmfx_pinctrl_irq_thread_fn(int irq, void *dev_id) + struct stmfx_pinctrl *pctl = (struct stmfx_pinctrl *)dev_id; + struct gpio_chip *gc = &pctl->gpio_chip; + u8 pending[NR_GPIO_REGS]; ++ u8 src[NR_GPIO_REGS] = {0, 0, 0}; + unsigned long n, status; + int ret; + +@@ -550,10 +551,8 @@ static irqreturn_t stmfx_pinctrl_irq_thread_fn(int irq, void *dev_id) + if (ret) + return IRQ_NONE; + +- ret = regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_ACK, +- pending, NR_GPIO_REGS); +- if (ret) +- return IRQ_NONE; ++ regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_SRC, ++ src, NR_GPIO_REGS); + + status = *(unsigned long *)pending; + for_each_set_bit(n, &status, gc->ngpio) { +@@ -561,6 +560,9 @@ static irqreturn_t stmfx_pinctrl_irq_thread_fn(int irq, void *dev_id) + stmfx_pinctrl_irq_toggle_trigger(pctl, n); + } + ++ regmap_bulk_write(pctl->stmfx->map, STMFX_REG_IRQ_GPI_SRC, ++ pctl->irq_gpi_src, NR_GPIO_REGS); ++ + return IRQ_HANDLED; + } + +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0048-ARM-stm32mp1-r0-rc4-REMOTEPROC-RPMSG.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0048-ARM-stm32mp1-r0-rc4-REMOTEPROC-RPMSG.patch new file mode 100644 index 0000000..a33e96e --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0048-ARM-stm32mp1-r0-rc4-REMOTEPROC-RPMSG.patch @@ -0,0 +1,100 @@ +From 457cfdb049a39670f15df8cae1d78a2479962ef9 Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Fri, 21 Dec 2018 16:57:38 +0100 +Subject: [PATCH 48/52] ARM: stm32mp1-r0-rc4: REMOTEPROC RPMSG + +--- + drivers/remoteproc/rproc_srm_core.c | 2 +- + drivers/remoteproc/stm32_rproc.c | 40 +++++++++++++++++++++++++++---------- + 2 files changed, 30 insertions(+), 12 deletions(-) + +diff --git a/drivers/remoteproc/rproc_srm_core.c b/drivers/remoteproc/rproc_srm_core.c +index 66be92e..fc61e8b 100644 +--- a/drivers/remoteproc/rproc_srm_core.c ++++ b/drivers/remoteproc/rproc_srm_core.c +@@ -189,7 +189,7 @@ static int rproc_srm_core_prepare(struct rproc_subdev *subdev) + /* Wait for every child to be bound */ + if (!wait_for_completion_timeout(&rproc_srm_core->all_bound, + msecs_to_jiffies(BIND_TIMEOUT))) { +- dev_err(dev, "bind timeout\n"); ++ dev_err(dev, "failed to bind one or more system resource device(s)\n"); + ret = -ETIMEDOUT; + goto master; + } +diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c +index 9a7e034..70b7e55c 100644 +--- a/drivers/remoteproc/stm32_rproc.c ++++ b/drivers/remoteproc/stm32_rproc.c +@@ -196,22 +196,12 @@ static int stm32_rproc_mbox_idx(struct rproc *rproc, const unsigned char *name) + static int stm32_rproc_elf_load_rsc_table(struct rproc *rproc, + const struct firmware *fw) + { +- int status, i; ++ int status; + struct resource_table *table = NULL; + struct stm32_rproc *ddata = rproc->priv; + size_t tablesz = 0; +- const struct elf32_hdr *ehdr; +- const struct elf32_phdr *phdr; + + if (!rproc->early_boot) { +- /* set coredump segments */ +- ehdr = (const struct elf32_hdr *)fw->data; +- phdr = (const struct elf32_phdr *)(fw->data + ehdr->e_phoff); +- for (i = 0; i < ehdr->e_phnum; i++, phdr++) +- rproc_coredump_add_segment(rproc, phdr->p_paddr, +- phdr->p_memsz); +- +- /* load resource table */ + status = rproc_elf_load_rsc_table(rproc, fw); + if (status) + goto no_rsc_table; +@@ -276,6 +266,10 @@ static int stm32_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) + stm32_rproc_mem_alloc, + stm32_rproc_mem_release, + it.node->name); ++ ++ if (mem) ++ rproc_coredump_add_segment(rproc, da, ++ rmem->size); + } else { + /* Register reserved memory for vdev buffer alloc */ + mem = rproc_of_resm_mem_entry_init(dev, index, +@@ -436,10 +430,34 @@ static int stm32_rproc_set_hold_boot(struct rproc *rproc, bool hold) + return err; + } + ++static void stm32_rproc_add_coredump_trace(struct rproc *rproc) ++{ ++ struct rproc_debug_trace *trace; ++ struct rproc_dump_segment *segment; ++ bool already_added; ++ ++ list_for_each_entry(trace, &rproc->traces, node) { ++ already_added = false; ++ ++ list_for_each_entry(segment, &rproc->dump_segments, node) { ++ if (segment->da == trace->trace_mem.da) { ++ already_added = true; ++ break; ++ } ++ } ++ ++ if (!already_added) ++ rproc_coredump_add_segment(rproc, trace->trace_mem.da, ++ trace->trace_mem.len); ++ } ++} ++ + static int stm32_rproc_start(struct rproc *rproc) + { + int err; + ++ stm32_rproc_add_coredump_trace(rproc); ++ + /* + * If M4 previously started by bootloader, just guarantee holdboot + * is set to catch any crash. +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0049-ARM-stm32mp1-r0-rc4-SOUND.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0049-ARM-stm32mp1-r0-rc4-SOUND.patch new file mode 100644 index 0000000..4092145 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0049-ARM-stm32mp1-r0-rc4-SOUND.patch @@ -0,0 +1,112 @@ +From 06f5ef8ca4c11074b595a4318dba9766092886d5 Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Fri, 21 Dec 2018 16:58:47 +0100 +Subject: [PATCH 49/52] ARM: stm32mp1-r0-rc4: SOUND + +--- + sound/soc/codecs/wm8994.c | 3 +-- + sound/soc/stm/stm32_sai_sub.c | 21 ++++++++++++++++++++- + 2 files changed, 21 insertions(+), 3 deletions(-) + +diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c +index eb222da..c83ae39 100644 +--- a/sound/soc/codecs/wm8994.c ++++ b/sound/soc/codecs/wm8994.c +@@ -868,7 +868,7 @@ static int mclk_event(struct snd_soc_dapm_widget *w, + break; + case SND_SOC_DAPM_POST_PMD: + dev_dbg(comp->dev, "Disable master clock %s\n", +- mclk_id ? "MCLK1" : "MCLK2"); ++ mclk_id ? "MCLK2" : "MCLK1"); + clk_disable_unprepare(mclk); + break; + } +@@ -1193,7 +1193,6 @@ static int aif2clk_ev(struct snd_soc_dapm_widget *w, + else + adc = WM8994_AIF2ADCL_ENA | WM8994_AIF2ADCR_ENA; + +- + val = snd_soc_component_read32(component, WM8994_AIF2_CONTROL_2); + if ((val & WM8994_AIF2DACL_SRC) && + (val & WM8994_AIF2DACR_SRC)) +diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c +index 3f0540a..a9b37f9 100644 +--- a/sound/soc/stm/stm32_sai_sub.c ++++ b/sound/soc/stm/stm32_sai_sub.c +@@ -102,6 +102,7 @@ + * @spdif_frm_cnt: S/PDIF playback frame counter + * @snd_aes_iec958: iec958 data + * @ctrl_lock: control lock ++ * @spinlock_t: prevent race condition with IRQ + */ + struct stm32_sai_sub_data { + struct platform_device *pdev; +@@ -133,6 +134,7 @@ struct stm32_sai_sub_data { + unsigned int spdif_frm_cnt; + struct snd_aes_iec958 iec958; + struct mutex ctrl_lock; /* protect resources accessed by controls */ ++ spinlock_t irq_lock; /* used to prevent race condition with IRQ */ + }; + + enum stm32_sai_fifo_th { +@@ -497,8 +499,10 @@ static irqreturn_t stm32_sai_isr(int irq, void *devid) + status = SNDRV_PCM_STATE_XRUN; + } + +- if (status != SNDRV_PCM_STATE_RUNNING) ++ spin_lock(&sai->irq_lock); ++ if (status != SNDRV_PCM_STATE_RUNNING && sai->substream) + snd_pcm_stop_xrun(sai->substream); ++ spin_unlock(&sai->irq_lock); + + return IRQ_HANDLED; + } +@@ -703,8 +707,19 @@ static int stm32_sai_startup(struct snd_pcm_substream *substream, + { + struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); + int imr, cr2, ret; ++ unsigned long flags; + ++ spin_lock_irqsave(&sai->irq_lock, flags); + sai->substream = substream; ++ spin_unlock_irqrestore(&sai->irq_lock, flags); ++ ++ if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { ++ snd_pcm_hw_constraint_mask64(substream->runtime, ++ SNDRV_PCM_HW_PARAM_FORMAT, ++ SNDRV_PCM_FMTBIT_S32_LE); ++ snd_pcm_hw_constraint_single(substream->runtime, ++ SNDRV_PCM_HW_PARAM_CHANNELS, 2); ++ } + + ret = clk_prepare_enable(sai->sai_ck); + if (ret < 0) { +@@ -1078,6 +1093,7 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) + { + struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai); ++ unsigned long flags; + + regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX, SAI_XIMR_MASK, 0); + +@@ -1092,7 +1108,9 @@ static void stm32_sai_shutdown(struct snd_pcm_substream *substream, + + clk_disable_unprepare(sai->sai_ck); + ++ spin_lock_irqsave(&sai->irq_lock, flags); + sai->substream = NULL; ++ spin_unlock_irqrestore(&sai->irq_lock, flags); + } + + static int stm32_sai_pcm_new(struct snd_soc_pcm_runtime *rtd, +@@ -1459,6 +1477,7 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) + + sai->pdev = pdev; + mutex_init(&sai->ctrl_lock); ++ spin_lock_init(&sai->irq_lock); + platform_set_drvdata(pdev, sai); + + sai->pdata = dev_get_drvdata(pdev->dev.parent); +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0050-ARM-stm32mp1-r0-rc4-USB.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0050-ARM-stm32mp1-r0-rc4-USB.patch new file mode 100644 index 0000000..c95a2e4 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0050-ARM-stm32mp1-r0-rc4-USB.patch @@ -0,0 +1,266 @@ +From fc74bada7f6940619f8930556b7c61fe618f2a64 Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Fri, 21 Dec 2018 16:56:27 +0100 +Subject: [PATCH 50/52] ARM: stm32mp1-r0-rc4: USB + +--- + Documentation/devicetree/bindings/usb/dwc2.txt | 2 + + drivers/usb/dwc2/core.h | 11 ++++ + drivers/usb/dwc2/core_intr.c | 4 +- + drivers/usb/dwc2/hw.h | 2 + + drivers/usb/dwc2/params.c | 7 ++- + drivers/usb/dwc2/platform.c | 74 +++++++++++++++++++++++--- + 6 files changed, 90 insertions(+), 10 deletions(-) + +diff --git a/Documentation/devicetree/bindings/usb/dwc2.txt b/Documentation/devicetree/bindings/usb/dwc2.txt +index 32b245c..03c62de 100644 +--- a/Documentation/devicetree/bindings/usb/dwc2.txt ++++ b/Documentation/devicetree/bindings/usb/dwc2.txt +@@ -43,6 +43,8 @@ Refer to phy/phy-bindings.txt for generic phy consumer properties + doesn't drive it. + - usb33d-supply: external VBUS and ID sensing comparators supply, in order to + perform OTG operation, used on STM32MP1 SoCs. ++- force-b-session-valid: force B-peripheral session instead of relying on ++ VBUS sensing (only valid when dr_mode = "peripheral"). + + Deprecated properties: + - g-use-dma: gadget DMA mode is automatically detected +diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h +index 4c689d1..4c1736f 100644 +--- a/drivers/usb/dwc2/core.h ++++ b/drivers/usb/dwc2/core.h +@@ -415,6 +415,12 @@ enum dwc2_ep0_state { + * in DWORDS with possible values from from + * 16-32768 (default: 256, 256, 256, 256, 768, + * 768, 768, 768, 0, 0, 0, 0, 0, 0, 0). ++ * @force_b_session_valid: force B-peripheral session instead of relying on ++ * VBUS sensing (only valid when dr_mode = "peripheral"). ++ * @suspend_ignore_power_down: prevent the controller to enter low power mode ++ * upon suspend interrupt. This may help in device mode, ++ * when suspend (3ms idle bus) gets detected before ++ * device session end (VBUS discharge > 3ms). + * @change_speed_quirk: Change speed configuration to DWC2_SPEED_PARAM_FULL + * while full&low speed device connect. And change speed + * back to DWC2_SPEED_PARAM_HIGH while device is gone. +@@ -492,6 +498,8 @@ struct dwc2_core_params { + u32 g_rx_fifo_size; + u32 g_np_tx_fifo_size; + u32 g_tx_fifo_size[MAX_EPS_CHANNELS]; ++ bool force_b_session_valid; ++ bool suspend_ignore_power_down; + + bool change_speed_quirk; + }; +@@ -849,6 +857,8 @@ struct dwc2_hregs_backup { + * removed once all SoCs support usb transceiver. + * @supplies: Definition of USB power supplies + * @vbus_supply: Regulator supplying vbus. ++ * @usb33d: Optional 3.3v regulator used on some stm32 devices to ++ * supply ID and VBUS detection hardware. + * @phyif: PHY interface width + * @lock: Spinlock that protects all the driver data structures + * @priv: Stores a pointer to the struct usb_hcd +@@ -1032,6 +1042,7 @@ struct dwc2_hsotg { + struct dwc2_hsotg_plat *plat; + struct regulator_bulk_data supplies[DWC2_NUM_SUPPLIES]; + struct regulator *vbus_supply; ++ struct regulator *usb33d; + u32 phyif; + + spinlock_t lock; +diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c +index 19ae259..7b4162c 100644 +--- a/drivers/usb/dwc2/core_intr.c ++++ b/drivers/usb/dwc2/core_intr.c +@@ -492,7 +492,9 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) + hsotg->hw_params.hibernation); + + /* Ignore suspend request before enumeration */ +- if (!dwc2_is_device_connected(hsotg)) { ++ if (!dwc2_is_device_connected(hsotg) || ++ hsotg->params.force_b_session_valid || ++ hsotg->params.suspend_ignore_power_down) { + dev_dbg(hsotg->dev, + "ignore suspend request before enumeration\n"); + return; +diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h +index afde335..31f8c60 100644 +--- a/drivers/usb/dwc2/hw.h ++++ b/drivers/usb/dwc2/hw.h +@@ -54,6 +54,8 @@ + #define GOTGCTL_HSTSETHNPEN BIT(10) + #define GOTGCTL_HNPREQ BIT(9) + #define GOTGCTL_HSTNEGSCS BIT(8) ++#define GOTGCTL_BVALOVAL BIT(7) ++#define GOTGCTL_BVALOEN BIT(6) + #define GOTGCTL_SESREQ BIT(1) + #define GOTGCTL_SESREQSCS BIT(0) + +diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c +index 63ccfc9..7fef905 100644 +--- a/drivers/usb/dwc2/params.c ++++ b/drivers/usb/dwc2/params.c +@@ -167,7 +167,7 @@ static void dwc2_set_stm32mp1_hsotg_params(struct dwc2_hsotg *hsotg) + p->host_rx_fifo_size = 440; + p->host_nperio_tx_fifo_size = 256; + p->host_perio_tx_fifo_size = 256; +- p->power_down = false; ++ p->suspend_ignore_power_down = true; + } + + const struct of_device_id dwc2_of_match_table[] = { +@@ -404,6 +404,11 @@ static void dwc2_get_device_properties(struct dwc2_hsotg *hsotg) + + if (of_find_property(hsotg->dev->of_node, "disable-over-current", NULL)) + p->oc_disable = true; ++ ++ if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) ++ p->force_b_session_valid = ++ of_property_read_bool(hsotg->dev->of_node, ++ "force-b-session-valid"); + } + + static void dwc2_check_param_otg_cap(struct dwc2_hsotg *hsotg) +diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c +index 2061254..b2e5ddc 100644 +--- a/drivers/usb/dwc2/platform.c ++++ b/drivers/usb/dwc2/platform.c +@@ -324,6 +324,10 @@ static int dwc2_driver_remove(struct platform_device *dev) + if (hsotg->gadget_enabled) + dwc2_hsotg_remove(hsotg); + ++ if (hsotg->params.activate_stm_id_vb_detection && ++ !hsotg->params.force_b_session_valid) ++ regulator_disable(hsotg->usb33d); ++ + if (hsotg->ll_hw_enabled) + dwc2_lowlevel_hw_disable(hsotg); + +@@ -474,18 +478,18 @@ static int dwc2_driver_probe(struct platform_device *dev) + if (retval) + goto error; + +- if (hsotg->params.activate_stm_id_vb_detection) { +- struct regulator *usb33d; ++ if (hsotg->params.activate_stm_id_vb_detection && ++ !hsotg->params.force_b_session_valid) { + u32 ggpio; + +- usb33d = devm_regulator_get(hsotg->dev, "usb33d"); +- if (IS_ERR(usb33d)) { +- retval = PTR_ERR(usb33d); ++ hsotg->usb33d = devm_regulator_get(hsotg->dev, "usb33d"); ++ if (IS_ERR(hsotg->usb33d)) { ++ retval = PTR_ERR(hsotg->usb33d); + dev_err(hsotg->dev, + "can't get voltage level detector supply\n"); + goto error; + } +- retval = regulator_enable(usb33d); ++ retval = regulator_enable(hsotg->usb33d); + if (retval) { + dev_err(hsotg->dev, + "can't enable voltage level detector supply\n"); +@@ -498,6 +502,15 @@ static int dwc2_driver_probe(struct platform_device *dev) + dwc2_writel(hsotg, ggpio, GGPIO); + } + ++ if (hsotg->params.force_b_session_valid) { ++ u32 gotgctl; ++ ++ gotgctl = dwc2_readl(hsotg, GOTGCTL); ++ gotgctl |= GOTGCTL_BVALOVAL; /* B-peripheral session value */ ++ gotgctl |= GOTGCTL_BVALOEN; /* B-peripheral override enable */ ++ dwc2_writel(hsotg, gotgctl, GOTGCTL); ++ } ++ + if (hsotg->params.activate_stm_fs_transceiver) { + u32 ggpio; + +@@ -516,7 +529,7 @@ static int dwc2_driver_probe(struct platform_device *dev) + if (hsotg->dr_mode != USB_DR_MODE_HOST) { + retval = dwc2_gadget_init(hsotg); + if (retval) +- goto error; ++ goto error_init; + hsotg->gadget_enabled = 1; + } + +@@ -525,7 +538,7 @@ static int dwc2_driver_probe(struct platform_device *dev) + if (retval) { + if (hsotg->gadget_enabled) + dwc2_hsotg_remove(hsotg); +- goto error; ++ goto error_init; + } + hsotg->hcd_enabled = 1; + } +@@ -541,6 +554,10 @@ static int dwc2_driver_probe(struct platform_device *dev) + + return 0; + ++error_init: ++ if (hsotg->params.activate_stm_id_vb_detection && ++ !hsotg->params.force_b_session_valid) ++ regulator_disable(hsotg->usb33d); + error: + dwc2_lowlevel_hw_disable(hsotg); + return retval; +@@ -554,6 +571,18 @@ static int __maybe_unused dwc2_suspend(struct device *dev) + if (dwc2_is_device_mode(dwc2)) + dwc2_hsotg_suspend(dwc2); + ++ if (dwc2->params.activate_stm_id_vb_detection && ++ !dwc2->params.force_b_session_valid) { ++ u32 ggpio; ++ ++ ggpio = dwc2_readl(dwc2, GGPIO); ++ ggpio &= ~GGPIO_STM32_OTG_GCCFG_IDEN; ++ ggpio &= ~GGPIO_STM32_OTG_GCCFG_VBDEN; ++ dwc2_writel(dwc2, ggpio, GGPIO); ++ ++ regulator_disable(dwc2->usb33d); ++ } ++ + if (dwc2->ll_hw_enabled) + ret = __dwc2_lowlevel_hw_disable(dwc2); + +@@ -571,6 +600,35 @@ static int __maybe_unused dwc2_resume(struct device *dev) + return ret; + } + ++ /* Need to restore FORCEDEVMODE/FORCEHOSTMODE */ ++ dwc2_force_dr_mode(dwc2); ++ ++ if (dwc2->params.activate_stm_id_vb_detection && ++ !dwc2->params.force_b_session_valid) { ++ u32 ggpio; ++ ++ ret = regulator_enable(dwc2->usb33d); ++ if (ret) ++ return ret; ++ ++ ggpio = dwc2_readl(dwc2, GGPIO); ++ ggpio |= GGPIO_STM32_OTG_GCCFG_IDEN; ++ ggpio |= GGPIO_STM32_OTG_GCCFG_VBDEN; ++ dwc2_writel(dwc2, ggpio, GGPIO); ++ ++ /* ID/VBUS detection startup time */ ++ usleep_range(5000, 7000); ++ } ++ ++ if (dwc2->params.force_b_session_valid) { ++ u32 gotgctl; ++ ++ gotgctl = dwc2_readl(dwc2, GOTGCTL); ++ gotgctl |= GOTGCTL_BVALOVAL; /* B-peripheral session value */ ++ gotgctl |= GOTGCTL_BVALOEN; /* B-peripheral override enable */ ++ dwc2_writel(dwc2, gotgctl, GOTGCTL); ++ } ++ + if (dwc2_is_device_mode(dwc2)) + ret = dwc2_hsotg_resume(dwc2); + +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0051-ARM-stm32mp1-r0-rc4-DEVICETREE.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0051-ARM-stm32mp1-r0-rc4-DEVICETREE.patch new file mode 100644 index 0000000..f76dbb1 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0051-ARM-stm32mp1-r0-rc4-DEVICETREE.patch @@ -0,0 +1,607 @@ +From a3f6ac74fe67cfacb73bda2ba631d1e5e2680988 Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Fri, 21 Dec 2018 16:56:44 +0100 +Subject: [PATCH 51/52] ARM: stm32mp1-r0-rc4: DEVICETREE + +--- + arch/arm/boot/dts/stm32mp157a-dk1.dts | 14 ++-- + arch/arm/boot/dts/stm32mp157c-ed1.dts | 9 --- + arch/arm/boot/dts/stm32mp157c-ev1.dts | 2 - + arch/arm/boot/dts/stm32mp157c-m4-srm.dtsi | 122 +++++++++++++++--------------- + arch/arm/boot/dts/stm32mp157c.dtsi | 2 +- + 5 files changed, 67 insertions(+), 82 deletions(-) + +diff --git a/arch/arm/boot/dts/stm32mp157a-dk1.dts b/arch/arm/boot/dts/stm32mp157a-dk1.dts +index 0a6cf35..7e911f3 100644 +--- a/arch/arm/boot/dts/stm32mp157a-dk1.dts ++++ b/arch/arm/boot/dts/stm32mp157a-dk1.dts +@@ -77,11 +77,6 @@ + }; + }; + +- iio-hwmon { +- compatible = "iio-hwmon"; +- io-channels = <&adc_temp>; +- }; +- + sram: sram@10050000 { + compatible = "mmio-sram"; + reg = <0x10050000 0x10000>; +@@ -174,6 +169,10 @@ + sram = <&dma_pool>; + }; + ++&dts { ++ status = "okay"; ++}; ++ + ðernet0 { + status = "okay"; + pinctrl-0 = <ðernet0_rgmii_pins_a>; +@@ -198,10 +197,6 @@ + status = "okay"; + }; + +-&hsem { +- status = "okay"; +-}; +- + &i2c1 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&i2c1_pins_a>; +@@ -679,6 +674,7 @@ + + &usbotg_hs { + dr_mode = "peripheral"; ++ force-b-session-valid; + phys = <&usbphyc_port1 0>; + phy-names = "usb2-phy"; + status = "okay"; +diff --git a/arch/arm/boot/dts/stm32mp157c-ed1.dts b/arch/arm/boot/dts/stm32mp157c-ed1.dts +index 798580e..cf2750e 100644 +--- a/arch/arm/boot/dts/stm32mp157c-ed1.dts ++++ b/arch/arm/boot/dts/stm32mp157c-ed1.dts +@@ -74,11 +74,6 @@ + serial0 = &uart4; + }; + +- iio-hwmon { +- compatible = "iio-hwmon"; +- io-channels = <&adc_temp>; +- }; +- + sram: sram@10050000 { + compatible = "mmio-sram"; + reg = <0x10050000 0x10000>; +@@ -173,10 +168,6 @@ + status = "okay"; + }; + +-&hsem { +- status = "okay"; +-}; +- + &i2c4 { + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&i2c4_pins_a>; +diff --git a/arch/arm/boot/dts/stm32mp157c-ev1.dts b/arch/arm/boot/dts/stm32mp157c-ev1.dts +index 49a62aa..18742e8 100644 +--- a/arch/arm/boot/dts/stm32mp157c-ev1.dts ++++ b/arch/arm/boot/dts/stm32mp157c-ev1.dts +@@ -177,7 +177,6 @@ + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&cec_pins_a>; + pinctrl-1 = <&cec_pins_sleep_a>; +- status = "okay"; + }; + + &dcmi { +@@ -530,7 +529,6 @@ + pinctrl-1 = <&i2c5_pins_sleep_a>; + i2c-scl-rising-time-ns = <185>; + i2c-scl-falling-time-ns = <20>; +- status = "okay"; + /delete-property/dmas; + /delete-property/dma-names; + }; +diff --git a/arch/arm/boot/dts/stm32mp157c-m4-srm.dtsi b/arch/arm/boot/dts/stm32mp157c-m4-srm.dtsi +index a1d132d0..5ebe24b 100644 +--- a/arch/arm/boot/dts/stm32mp157c-m4-srm.dtsi ++++ b/arch/arm/boot/dts/stm32mp157c-m4-srm.dtsi +@@ -5,106 +5,106 @@ + + m4_timers2: timer@40000000 { + compatible = "rproc-srm-dev"; +- reg = <0x40000000 0x400>; ++ reg = <0x40000000>; + clocks = <&rcc TIM2_K>; + clock-names = "int"; + status = "disabled"; + }; + m4_timers3: timer@40001000 { + compatible = "rproc-srm-dev"; +- reg = <0x40001000 0x400>; ++ reg = <0x40001000>; + clocks = <&rcc TIM3_K>; + clock-names = "int"; + status = "disabled"; + }; + m4_timers4: timer@40002000 { + compatible = "rproc-srm-dev"; +- reg = <0x40002000 0x400>; ++ reg = <0x40002000>; + clocks = <&rcc TIM4_K>; + clock-names = "int"; + status = "disabled"; + }; + m4_timers5: timer@40003000 { + compatible = "rproc-srm-dev"; +- reg = <0x40003000 0x400>; ++ reg = <0x40003000>; + clocks = <&rcc TIM5_K>; + clock-names = "int"; + status = "disabled"; + }; + m4_timers6: timer@40004000 { + compatible = "rproc-srm-dev"; +- reg = <0x40004000 0x400>; ++ reg = <0x40004000>; + clocks = <&rcc TIM6_K>; + clock-names = "int"; + status = "disabled"; + }; + m4_timers7: timer@40005000 { + compatible = "rproc-srm-dev"; +- reg = <0x40005000 0x400>; ++ reg = <0x40005000>; + clocks = <&rcc TIM7_K>; + clock-names = "int"; + status = "disabled"; + }; + m4_timers12: timer@40006000 { + compatible = "rproc-srm-dev"; +- reg = <0x40006000 0x400>; ++ reg = <0x40006000>; + clocks = <&rcc TIM12_K>; + clock-names = "int"; + status = "disabled"; + }; + m4_timers13: timer@40007000 { + compatible = "rproc-srm-dev"; +- reg = <0x40007000 0x400>; ++ reg = <0x40007000>; + clocks = <&rcc TIM13_K>; + clock-names = "int"; + status = "disabled"; + }; + m4_timers14: timer@40008000 { + compatible = "rproc-srm-dev"; +- reg = <0x40008000 0x400>; ++ reg = <0x40008000>; + clocks = <&rcc TIM14_K>; + clock-names = "int"; + status = "disabled"; + }; + m4_lptimer1: timer@40009000 { + compatible = "rproc-srm-dev"; +- reg = <0x40009000 0x400>; ++ reg = <0x40009000>; + clocks = <&rcc LPTIM1_K>; + clock-names = "mux"; + status = "disabled"; + }; + m4_spi2: spi@4000b000 { + compatible = "rproc-srm-dev"; +- reg = <0x4000b000 0x400>; ++ reg = <0x4000b000>; + clocks = <&rcc SPI2_K>; + status = "disabled"; + }; + m4_i2s2: audio-controller@4000b000 { + compatible = "rproc-srm-dev"; +- reg = <0x4000b000 0x400>; ++ reg = <0x4000b000>; + status = "disabled"; + }; + m4_spi3: spi@4000c000 { + compatible = "rproc-srm-dev"; +- reg = <0x4000c000 0x400>; ++ reg = <0x4000c000>; + clocks = <&rcc SPI3_K>; + status = "disabled"; + }; + m4_i2s3: audio-controller@4000c000 { + compatible = "rproc-srm-dev"; +- reg = <0x4000c000 0x400>; ++ reg = <0x4000c000>; + status = "disabled"; + }; + m4_spdifrx: audio-controller@4000d000 { + compatible = "rproc-srm-dev"; +- reg = <0x4000d000 0x400>; ++ reg = <0x4000d000>; + clocks = <&rcc SPDIF_K>; + clock-names = "kclk"; + status = "disabled"; + }; + m4_usart2: serial@4000e000 { + compatible = "rproc-srm-dev"; +- reg = <0x4000e000 0x400>; ++ reg = <0x4000e000>; + interrupt-parent = <&exti>; + interrupts = <27 1>; + clocks = <&rcc USART2_K>; +@@ -112,7 +112,7 @@ + }; + m4_usart3: serial@4000f000 { + compatible = "rproc-srm-dev"; +- reg = <0x4000f000 0x400>; ++ reg = <0x4000f000>; + interrupt-parent = <&exti>; + interrupts = <28 1>; + clocks = <&rcc USART3_K>; +@@ -120,7 +120,7 @@ + }; + m4_uart4: serial@40010000 { + compatible = "rproc-srm-dev"; +- reg = <0x40010000 0x400>; ++ reg = <0x40010000>; + interrupt-parent = <&exti>; + interrupts = <30 1>; + clocks = <&rcc UART4_K>; +@@ -128,7 +128,7 @@ + }; + m4_uart5: serial@40011000 { + compatible = "rproc-srm-dev"; +- reg = <0x40011000 0x400>; ++ reg = <0x40011000>; + interrupt-parent = <&exti>; + interrupts = <31 1>; + clocks = <&rcc UART5_K>; +@@ -136,7 +136,7 @@ + }; + m4_i2c1: i2c@40012000 { + compatible = "rproc-srm-dev"; +- reg = <0x40012000 0x400>; ++ reg = <0x40012000>; + interrupt-parent = <&exti>; + interrupts = <21 1>; + clocks = <&rcc I2C1_K>; +@@ -144,7 +144,7 @@ + }; + m4_i2c2: i2c@40013000 { + compatible = "rproc-srm-dev"; +- reg = <0x40013000 0x400>; ++ reg = <0x40013000>; + interrupt-parent = <&exti>; + interrupts = <22 1>; + clocks = <&rcc I2C2_K>; +@@ -152,7 +152,7 @@ + }; + m4_i2c3: i2c@40014000 { + compatible = "rproc-srm-dev"; +- reg = <0x40014000 0x400>; ++ reg = <0x40014000>; + interrupt-parent = <&exti>; + interrupts = <23 1>; + clocks = <&rcc I2C3_K>; +@@ -160,7 +160,7 @@ + }; + m4_i2c5: i2c@40015000 { + compatible = "rproc-srm-dev"; +- reg = <0x40015000 0x400>; ++ reg = <0x40015000>; + interrupt-parent = <&exti>; + interrupts = <25 1>; + clocks = <&rcc I2C5_K>; +@@ -168,7 +168,7 @@ + }; + m4_cec: cec@40016000 { + compatible = "rproc-srm-dev"; +- reg = <0x40016000 0x400>; ++ reg = <0x40016000>; + interrupt-parent = <&exti>; + interrupts = <69 1>; + clocks = <&rcc CEC_K>, <&rcc CK_LSE>; +@@ -177,14 +177,14 @@ + }; + m4_dac: dac@40017000 { + compatible = "rproc-srm-dev"; +- reg = <0x40017000 0x400>; ++ reg = <0x40017000>; + clocks = <&rcc DAC12>; + clock-names = "pclk"; + status = "disabled"; + }; + m4_uart7: serial@40018000 { + compatible = "rproc-srm-dev"; +- reg = <0x40018000 0x400>; ++ reg = <0x40018000>; + interrupt-parent = <&exti>; + interrupts = <32 1>; + clocks = <&rcc UART7_K>; +@@ -192,7 +192,7 @@ + }; + m4_uart8: serial@40019000 { + compatible = "rproc-srm-dev"; +- reg = <0x40019000 0x400>; ++ reg = <0x40019000>; + interrupt-parent = <&exti>; + interrupts = <33 1>; + clocks = <&rcc UART8_K>; +@@ -200,21 +200,21 @@ + }; + m4_timers1: timer@44000000 { + compatible = "rproc-srm-dev"; +- reg = <0x44000000 0x400>; ++ reg = <0x44000000>; + clocks = <&rcc TIM1_K>; + clock-names = "int"; + status = "disabled"; + }; + m4_timers8: timer@44001000 { + compatible = "rproc-srm-dev"; +- reg = <0x44001000 0x400>; ++ reg = <0x44001000>; + clocks = <&rcc TIM8_K>; + clock-names = "int"; + status = "disabled"; + }; + m4_usart6: serial@44003000 { + compatible = "rproc-srm-dev"; +- reg = <0x44003000 0x400>; ++ reg = <0x44003000>; + interrupt-parent = <&exti>; + interrupts = <29 1>; + clocks = <&rcc USART6_K>; +@@ -222,203 +222,203 @@ + }; + m4_spi1: spi@44004000 { + compatible = "rproc-srm-dev"; +- reg = <0x44004000 0x400>; ++ reg = <0x44004000>; + clocks = <&rcc SPI1_K>; + status = "disabled"; + }; + m4_i2s1: audio-controller@44004000 { + compatible = "rproc-srm-dev"; +- reg = <0x44004000 0x400>; ++ reg = <0x44004000>; + status = "disabled"; + }; + m4_spi4: spi@44005000 { + compatible = "rproc-srm-dev"; +- reg = <0x44005000 0x400>; ++ reg = <0x44005000>; + clocks = <&rcc SPI4_K>; + status = "disabled"; + }; + m4_timers15: timer@44006000 { + compatible = "rproc-srm-dev"; +- reg = <0x44006000 0x400>; ++ reg = <0x44006000>; + clocks = <&rcc TIM15_K>; + clock-names = "int"; + status = "disabled"; + }; + m4_timers16: timer@44007000 { + compatible = "rproc-srm-dev"; +- reg = <0x44007000 0x400>; ++ reg = <0x44007000>; + clocks = <&rcc TIM16_K>; + clock-names = "int"; + status = "disabled"; + }; + m4_timers17: timer@44008000 { + compatible = "rproc-srm-dev"; +- reg = <0x44008000 0x400>; ++ reg = <0x44008000>; + clocks = <&rcc TIM17_K>; + clock-names = "int"; + status = "disabled"; + }; + m4_spi5: spi@44009000 { + compatible = "rproc-srm-dev"; +- reg = <0x44009000 0x400>; ++ reg = <0x44009000>; + clocks = <&rcc SPI5_K>; + status = "disabled"; + }; + m4_sai1: sai@4400a000 { + compatible = "rproc-srm-dev"; +- reg = <0x4400a000 0x4>; ++ reg = <0x4400a000>; + clocks = <&rcc SAI1_K>; + clock-names = "sai_ck"; + status = "disabled"; + }; + m4_sai2: sai@4400b000 { + compatible = "rproc-srm-dev"; +- reg = <0x4400b000 0x4>; ++ reg = <0x4400b000>; + clocks = <&rcc SAI2_K>; + clock-names = "sai_ck"; + status = "disabled"; + }; + m4_sai3: sai@4400c000 { + compatible = "rproc-srm-dev"; +- reg = <0x4400c000 0x4>; ++ reg = <0x4400c000>; + clocks = <&rcc SAI3_K>; + clock-names = "sai_ck"; + status = "disabled"; + }; + m4_dfsdm: dfsdm@4400d000 { + compatible = "rproc-srm-dev"; +- reg = <0x4400d000 0x800>; ++ reg = <0x4400d000>; + clocks = <&rcc DFSDM_K>; + clock-names = "dfsdm"; + status = "disabled"; + }; + m4_m_can1: can@4400e000 { + compatible = "rproc-srm-dev"; +- reg = <0x4400e000 0x400>, <0x44011000 0x2800>; ++ reg = <0x4400e000>, <0x44011000>; + clocks = <&rcc CK_HSE>, <&rcc FDCAN_K>; + clock-names = "hclk", "cclk"; + status = "disabled"; + }; + m4_m_can2: can@4400f000 { + compatible = "rproc-srm-dev"; +- reg = <0x4400f000 0x400>, <0x44011000 0x2800>; ++ reg = <0x4400f000>, <0x44011000>; + clocks = <&rcc CK_HSE>, <&rcc FDCAN_K>; + clock-names = "hclk", "cclk"; + status = "disabled"; + }; + m4_dma1: dma@48000000 { + compatible = "rproc-srm-dev"; +- reg = <0x48000000 0x400>; ++ reg = <0x48000000>; + clocks = <&rcc DMA1>; + status = "disabled"; + }; + m4_dma2: dma@48001000 { + compatible = "rproc-srm-dev"; +- reg = <0x48001000 0x400>; ++ reg = <0x48001000>; + clocks = <&rcc DMA2>; + status = "disabled"; + }; + m4_dmamux1: dma-router@48002000 { + compatible = "rproc-srm-dev"; +- reg = <0x48002000 0x1c>; ++ reg = <0x48002000>; + clocks = <&rcc DMAMUX>; + status = "disabled"; + }; + m4_adc: adc@48003000 { + compatible = "rproc-srm-dev"; +- reg = <0x48003000 0x400>; ++ reg = <0x48003000>; + clocks = <&rcc ADC12>, <&rcc ADC12_K>; + clock-names = "bus", "adc"; + status = "disabled"; + }; + m4_sdmmc3: sdmmc@48004000 { + compatible = "rproc-srm-dev"; +- reg = <0x48004000 0x400>, <0x48005000 0x400>; ++ reg = <0x48004000>, <0x48005000>; + clocks = <&rcc SDMMC3_K>; + status = "disabled"; + }; + m4_usbotg_hs: usb-otg@49000000 { + compatible = "rproc-srm-dev"; +- reg = <0x49000000 0x10000>; ++ reg = <0x49000000>; + clocks = <&rcc USBO_K>; + clock-names = "otg"; + status = "disabled"; + }; + m4_hash2: hash@4c002000 { + compatible = "rproc-srm-dev"; +- reg = <0x4c002000 0x400>; ++ reg = <0x4c002000>; + clocks = <&rcc HASH2>; + status = "disabled"; + }; + m4_rng2: rng@4c003000 { + compatible = "rproc-srm-dev"; +- reg = <0x4c003000 0x400>; ++ reg = <0x4c003000>; + clocks = <&rcc RNG2_K>; + status = "disabled"; + }; + m4_crc2: crc@4c004000 { + compatible = "rproc-srm-dev"; +- reg = <0x4c004000 0x400>; ++ reg = <0x4c004000>; + clocks = <&rcc CRC2>; + status = "disabled"; + }; + m4_cryp2: cryp@4c005000 { + compatible = "rproc-srm-dev"; +- reg = <0x4c005000 0x400>; ++ reg = <0x4c005000>; + clocks = <&rcc CRYP2>; + status = "disabled"; + }; + m4_dcmi: dcmi@4c006000 { + compatible = "rproc-srm-dev"; +- reg = <0x4c006000 0x400>; ++ reg = <0x4c006000>; + clocks = <&rcc DCMI>; + clock-names = "mclk"; + status = "disabled"; + }; + m4_lptimer2: timer@50021000 { + compatible = "rproc-srm-dev"; +- reg = <0x50021000 0x400>; ++ reg = <0x50021000>; + clocks = <&rcc LPTIM2_K>; + clock-names = "mux"; + status = "disabled"; + }; + m4_lptimer3: timer@50022000 { + compatible = "rproc-srm-dev"; +- reg = <0x50022000 0x400>; ++ reg = <0x50022000>; + clocks = <&rcc LPTIM3_K>; + clock-names = "mux"; + status = "disabled"; + }; + m4_lptimer4: timer@50023000 { + compatible = "rproc-srm-dev"; +- reg = <0x50023000 0x400>; ++ reg = <0x50023000>; + clocks = <&rcc LPTIM4_K>; + clock-names = "mux"; + status = "disabled"; + }; + m4_lptimer5: timer@50024000 { + compatible = "rproc-srm-dev"; +- reg = <0x50024000 0x400>; ++ reg = <0x50024000>; + clocks = <&rcc LPTIM5_K>; + clock-names = "mux"; + status = "disabled"; + }; + m4_sai4: sai@50027000 { + compatible = "rproc-srm-dev"; +- reg = <0x50027000 0x4>; ++ reg = <0x50027000>; + clocks = <&rcc SAI4_K>; + clock-names = "sai_ck"; + status = "disabled"; + }; + m4_qspi: qspi@58003000 { + compatible = "rproc-srm-dev"; +- reg = <0x58003000 0x1000>, <0x70000000 0x10000000>; ++ reg = <0x58003000>, <0x70000000>; + clocks = <&rcc QSPI_K>; + status = "disabled"; + }; + m4_ethernet0: ethernet@5800a000 { + compatible = "rproc-srm-dev"; +- reg = <0x5800a000 0x2000>; ++ reg = <0x5800a000>; + clock-names = "stmmaceth", + "mac-clk-tx", + "mac-clk-rx", +diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi +index 7a7ef47..4de499e 100644 +--- a/arch/arm/boot/dts/stm32mp157c.dtsi ++++ b/arch/arm/boot/dts/stm32mp157c.dtsi +@@ -1231,7 +1231,7 @@ + reg = <0x4c000000 0x400>; + clocks = <&rcc HSEM>; + clock-names = "hsem"; +- status = "disabled"; ++ status = "okay"; + }; + + ipcc: mailbox@4c001000 { +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0052-ARM-stm32mp1-r0-rc4-DEFCONFIG.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0052-ARM-stm32mp1-r0-rc4-DEFCONFIG.patch new file mode 100644 index 0000000..3632650 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0052-ARM-stm32mp1-r0-rc4-DEFCONFIG.patch @@ -0,0 +1,36 @@ +From 619ff8e35045cb1cb0f235bd24236ff36453186a Mon Sep 17 00:00:00 2001 +From: Romuald JEANNE +Date: Fri, 21 Dec 2018 16:57:19 +0100 +Subject: [PATCH 52/52] ARM: stm32mp1-r0-rc4: DEFCONFIG + +--- + arch/arm/configs/fragment-02-multiv7_addons.config | 13 ------------- + 1 file changed, 13 deletions(-) + +diff --git a/arch/arm/configs/fragment-02-multiv7_addons.config b/arch/arm/configs/fragment-02-multiv7_addons.config +index 6ae0453..38c5562 100644 +--- a/arch/arm/configs/fragment-02-multiv7_addons.config ++++ b/arch/arm/configs/fragment-02-multiv7_addons.config +@@ -462,19 +462,6 @@ CONFIG_REGULATOR_STM32_VREFBUF=y + CONFIG_NVMEM_STM32_ROMEM=y + + # +-# STM32 CORESIGHT +-# +-CONFIG_STM_SOURCE_CONSOLE=y +-CONFIG_STM_SOURCE_FTRACE=y +-CONFIG_FUNCTION_TRACER=y +-CONFIG_CORESIGHT=y +-CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y +-CONFIG_CORESIGHT_SINK_TPIU=y +-CONFIG_CORESIGHT_SINK_ETBV10=y +-CONFIG_CORESIGHT_SOURCE_ETM3X=y +-CONFIG_CORESIGHT_STM=y +- +-# + # STM32 IPCC + # + CONFIG_STM32_IPCC=y +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0053-ARM-stm32mp1-r0-rc4-hotfix-w903.1-DRIVERS.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0053-ARM-stm32mp1-r0-rc4-hotfix-w903.1-DRIVERS.patch new file mode 100644 index 0000000..30f5d81 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0053-ARM-stm32mp1-r0-rc4-hotfix-w903.1-DRIVERS.patch @@ -0,0 +1,1145 @@ +From 0ab8d3145b982810bf5b1c1d2388f5c3820151a8 Mon Sep 17 00:00:00 2001 +From: christophe montaud +Date: Mon, 14 Jan 2019 17:18:56 +0100 +Subject: [PATCH 53/55] ARM stm32mp1 r0 rc4 hotfix-w903.1 DRIVERS + +--- + .../bindings/connector/usb-connector.txt | 2 + + .../devicetree/bindings/usb/st,typec-stusb.txt | 32 ++ + drivers/bluetooth/hci_bcm.c | 3 +- + drivers/gpu/drm/stm/dw_mipi_dsi-stm.c | 9 +- + drivers/iio/adc/stm32-dfsdm-adc.c | 5 +- + drivers/media/platform/stm32/stm32-dcmi.c | 17 + + drivers/mfd/stm32-pwr.c | 14 - + .../net/wireless/broadcom/brcm80211/brcmfmac/pno.c | 5 + + drivers/remoteproc/remoteproc_core.c | 10 + + drivers/remoteproc/remoteproc_virtio.c | 2 + + drivers/remoteproc/stm32_rproc.c | 63 ++- + drivers/usb/typec/Kconfig | 9 + + drivers/usb/typec/Makefile | 1 + + drivers/usb/typec/class.c | 15 + + drivers/usb/typec/typec_stusb.c | 589 +++++++++++++++++++++ + include/linux/usb/typec.h | 1 + + sound/soc/stm/stm32_adfsdm.c | 21 +- + 17 files changed, 761 insertions(+), 37 deletions(-) + create mode 100644 Documentation/devicetree/bindings/usb/st,typec-stusb.txt + create mode 100644 drivers/usb/typec/typec_stusb.c + +diff --git a/Documentation/devicetree/bindings/connector/usb-connector.txt b/Documentation/devicetree/bindings/connector/usb-connector.txt +index 8855bfc..bf43ee9 100644 +--- a/Documentation/devicetree/bindings/connector/usb-connector.txt ++++ b/Documentation/devicetree/bindings/connector/usb-connector.txt +@@ -18,6 +18,8 @@ Optional properties: + Optional properties for usb-c-connector: + - power-role: should be one of "source", "sink" or "dual"(DRP) if typec + connector has power support. ++- power-opmode: should be one of "default", "1.5A", "3.0A" or ++ "usb_power_delivery" if typec connector has power support. + - try-power-role: preferred power role if "dual"(DRP) can support Try.SNK + or Try.SRC, should be "sink" for Try.SNK or "source" for Try.SRC. + - data-role: should be one of "host", "device", "dual"(DRD) if typec +diff --git a/Documentation/devicetree/bindings/usb/st,typec-stusb.txt b/Documentation/devicetree/bindings/usb/st,typec-stusb.txt +new file mode 100644 +index 0000000..817360d +--- /dev/null ++++ b/Documentation/devicetree/bindings/usb/st,typec-stusb.txt +@@ -0,0 +1,32 @@ ++STMicroelectronics STUSB Type-C Controller family ++ ++Required properties: ++ - compatible: should be "st,stusb1600". ++ - reg: I2C slave address of the device. ++ ++Optional properties: ++ - vdd-supply: main power supply [4.1V;22V]. ++ - vsys-supply: low power supply [3.0V;5.5V]. ++ - vconn-supply: power supply [2.7;5.5V] used to supply VConn on CC pin in ++ source or dual power role. ++ ++USB-C connector attached to STUSB Type-C port controller can be described in ++an optional connector sub-node. Refer to ../connector/usb-connector.txt. ++ ++Example : ++ ++ typec: stusb1600@28 { ++ compatible = "st,stusb1600"; ++ reg = <0x28>; ++ vdd-supply = <&vbus_drd>; ++ vsys-supply = <&vdd_usb>; ++ ++ usb_con: connector { ++ compatible = "usb-c-connector"; ++ label = "USB-C"; ++ power-role = "dual"; ++ power-opmode = "1.5A"; ++ data-role = "dual"; ++ }; ++ }; ++ +diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c +index ddbd8c6..7a23fee 100644 +--- a/drivers/bluetooth/hci_bcm.c ++++ b/drivers/bluetooth/hci_bcm.c +@@ -1295,7 +1295,8 @@ static int bcm_serdev_probe(struct serdev_device *serdev) + if (!bcmdev->shutdown) { + dev_warn(&serdev->dev, + "No reset resource, using default baud rate\n"); +- bcmdev->oper_speed = bcmdev->init_speed; ++ if (!bcmdev->oper_speed) ++ bcmdev->oper_speed = bcmdev->init_speed; + } + + err = bcm_gpio_set_power(bcmdev, false); +diff --git a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c +index a6edd86..a373651 100644 +--- a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c ++++ b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c +@@ -227,7 +227,6 @@ dw_mipi_dsi_get_lane_mbps(void *priv_data, struct drm_display_mode *mode, + u32 val; + + /* Update lane capabilities according to hw version */ +- dsi->hw_version = dsi_read(dsi, DSI_VERSION) & VERSION; + dsi->lane_min_kbps = LANE_MIN_KBPS; + dsi->lane_max_kbps = LANE_MAX_KBPS; + if (dsi->hw_version == HWVER_131) { +@@ -347,6 +346,14 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev) + return ret; + } + ++ dsi->hw_version = dsi_read(dsi, DSI_VERSION) & VERSION; ++ if (dsi->hw_version != HWVER_130 && dsi->hw_version != HWVER_131) { ++ dev_err(dev, "bad dsi hardware version\n"); ++ clk_disable_unprepare(dsi->pllref_clk); ++ regulator_disable(dsi->vdd_supply); ++ return -ENODEV; ++ } ++ + dw_mipi_dsi_stm_plat_data.base = dsi->base; + dw_mipi_dsi_stm_plat_data.priv_data = dsi; + +diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c +index c97d9ee..13854b7 100644 +--- a/drivers/iio/adc/stm32-dfsdm-adc.c ++++ b/drivers/iio/adc/stm32-dfsdm-adc.c +@@ -569,8 +569,9 @@ static int dfsdm_adc_set_samp_freq(struct iio_dev *indio_dev, + + 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); ++ dev_dbg(&indio_dev->dev, ++ "Rate not accurate. requested (%u), actual (%u)\n", ++ sample_freq, spi_freq / oversamp); + + ret = stm32_dfsdm_set_osrs(fl, 0, oversamp); + if (ret < 0) { +diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c +index 49849e6..6f6dc66 100644 +--- a/drivers/media/platform/stm32/stm32-dcmi.c ++++ b/drivers/media/platform/stm32/stm32-dcmi.c +@@ -167,6 +167,9 @@ struct stm32_dcmi { + int errors_count; + int overrun_count; + int buffers_count; ++ ++ /* Ensure DMA operations atomicity */ ++ struct mutex dma_lock; + }; + + static inline struct stm32_dcmi *notifier_to_dcmi(struct v4l2_async_notifier *n) +@@ -317,6 +320,13 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi, + return ret; + } + ++ /* ++ * Avoid call of dmaengine_terminate_all() between ++ * dmaengine_prep_slave_single() and dmaengine_submit() ++ * by locking the whole DMA submission sequence ++ */ ++ mutex_lock(&dcmi->dma_lock); ++ + /* Prepare a DMA transaction */ + desc = dmaengine_prep_slave_single(dcmi->dma_chan, buf->paddr, + buf->size, +@@ -325,6 +335,7 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi, + if (!desc) { + dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_single failed for buffer phy=%pad size=%zu\n", + __func__, &buf->paddr, buf->size); ++ mutex_unlock(&dcmi->dma_lock); + return -EINVAL; + } + +@@ -336,9 +347,12 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi, + dcmi->dma_cookie = dmaengine_submit(desc); + if (dma_submit_error(dcmi->dma_cookie)) { + dev_err(dcmi->dev, "%s: DMA submission failed\n", __func__); ++ mutex_unlock(&dcmi->dma_lock); + return -ENXIO; + } + ++ mutex_unlock(&dcmi->dma_lock); ++ + dma_async_issue_pending(dcmi->dma_chan); + + return 0; +@@ -746,7 +760,9 @@ static void dcmi_stop_streaming(struct vb2_queue *vq) + spin_unlock_irq(&dcmi->irqlock); + + /* Stop all pending DMA operations */ ++ mutex_lock(&dcmi->dma_lock); + dmaengine_terminate_all(dcmi->dma_chan); ++ mutex_unlock(&dcmi->dma_lock); + + pm_runtime_put(dcmi->dev); + +@@ -1740,6 +1756,7 @@ static int dcmi_probe(struct platform_device *pdev) + + spin_lock_init(&dcmi->irqlock); + mutex_init(&dcmi->lock); ++ mutex_init(&dcmi->dma_lock); + init_completion(&dcmi->complete); + INIT_LIST_HEAD(&dcmi->buffers); + +diff --git a/drivers/mfd/stm32-pwr.c b/drivers/mfd/stm32-pwr.c +index 206a933..088b3d6 100644 +--- a/drivers/mfd/stm32-pwr.c ++++ b/drivers/mfd/stm32-pwr.c +@@ -70,19 +70,6 @@ static void stm32_pwr_irq_unmask(struct irq_data *d) + SMC(STM32_SVC_PWR, STM32_SET_BITS, MPUWKUPENR, BIT(d->hwirq)); + } + +-static int stm32_pwr_irq_set_wake(struct irq_data *d, unsigned int on) +-{ +- struct stm32_pwr_data *priv = d->domain->host_data; +- +- pr_debug("irq:%lu on:%d\n", d->hwirq, on); +- if (on) +- enable_irq_wake(priv->irq); +- else +- disable_irq_wake(priv->irq); +- +- return 0; +-} +- + static int stm32_pwr_irq_set_type(struct irq_data *d, unsigned int flow_type) + { + struct stm32_pwr_data *priv = d->domain->host_data; +@@ -125,7 +112,6 @@ static struct irq_chip stm32_pwr_irq_chip = { + .irq_mask = stm32_pwr_irq_mask, + .irq_unmask = stm32_pwr_irq_unmask, + .irq_set_type = stm32_pwr_irq_set_type, +- .irq_set_wake = stm32_pwr_irq_set_wake, + }; + + static int stm32_pwr_irq_set_pull_config(struct irq_domain *d, int pin_id, +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c +index ffa243e..55974a4 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c +@@ -496,6 +496,11 @@ int brcmf_pno_stop_sched_scan(struct brcmf_if *ifp, u64 reqid) + brcmf_dbg(TRACE, "reqid=%llu\n", reqid); + + pi = ifp_to_pno(ifp); ++ ++ /* No PNO reqeuset */ ++ if (!pi->n_reqs) ++ return 0; ++ + err = brcmf_pno_remove_request(pi, reqid); + if (err) + return err; +diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c +index dd7e348..6430747 100644 +--- a/drivers/remoteproc/remoteproc_core.c ++++ b/drivers/remoteproc/remoteproc_core.c +@@ -1456,6 +1456,9 @@ static int rproc_stop(struct rproc *rproc, bool crashed) + struct device *dev = &rproc->dev; + int ret; + ++ if (rproc->state == RPROC_OFFLINE) ++ return 0; ++ + /* Stop any subdevices for the remote processor */ + rproc_stop_subdevices(rproc, crashed); + +@@ -1614,6 +1617,13 @@ int rproc_trigger_recovery(struct rproc *rproc) + /* generate coredump */ + rproc_coredump(rproc); + ++ if (!rproc->firmware) { ++ /* we don't know how to recover it, so try to shutdown it*/ ++ mutex_unlock(&rproc->lock); ++ rproc_shutdown(rproc); ++ return 0; ++ } ++ + /* load firmware */ + ret = request_firmware(&firmware_p, rproc->firmware, dev); + if (ret < 0) { +diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c +index 9ee63c0..78462f5 100644 +--- a/drivers/remoteproc/remoteproc_virtio.c ++++ b/drivers/remoteproc/remoteproc_virtio.c +@@ -359,6 +359,8 @@ int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id) + } + } + ++ /* Reset vdev struct as you don't know how it has been previously allocated */ ++ memset(vdev, 0, sizeof(struct virtio_device)); + vdev->id.device = id, + vdev->config = &rproc_virtio_config_ops, + vdev->dev.parent = dev; +diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c +index 70b7e55c..1d2be11 100644 +--- a/drivers/remoteproc/stm32_rproc.c ++++ b/drivers/remoteproc/stm32_rproc.c +@@ -67,7 +67,7 @@ struct stm32_rproc { + struct stm32_rproc_mem *rmems; + struct stm32_mbox mb[MBOX_NB_MBX]; + bool secured_soc; +- u32 rsc_addr; ++ void __iomem *rsc_va; + u32 rsc_len; + }; + +@@ -84,7 +84,27 @@ static int stm32_rproc_pa_to_da(struct rproc *rproc, phys_addr_t pa, u64 *da) + pa >= p_mem->bus_addr + p_mem->size) + continue; + *da = pa - p_mem->bus_addr + p_mem->dev_addr; +- dev_dbg(rproc->dev.parent, "da %llx to pa %#x\n", *da, pa); ++ dev_dbg(rproc->dev.parent, "pa %#x to da %llx\n", pa, *da); ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++static int stm32_rproc_da_to_pa(struct rproc *rproc, u64 da, phys_addr_t *pa) ++{ ++ unsigned int i; ++ struct stm32_rproc *ddata = rproc->priv; ++ struct stm32_rproc_mem *p_mem; ++ ++ for (i = 0; i < ddata->nb_rmems; i++) { ++ p_mem = &ddata->rmems[i]; ++ ++ if (da < p_mem->dev_addr || ++ da >= p_mem->dev_addr + p_mem->size) ++ continue; ++ *pa = da - p_mem->dev_addr + p_mem->bus_addr; ++ dev_err(rproc->dev.parent, "da %llx to pa %#x\n", da, *pa); + return 0; + } + +@@ -209,11 +229,9 @@ static int stm32_rproc_elf_load_rsc_table(struct rproc *rproc, + return 0; + } + +- if (ddata->rsc_addr) { ++ if (ddata->rsc_va) { + tablesz = ddata->rsc_len; +- table = (struct resource_table *) +- rproc_da_to_va(rproc, (u64)ddata->rsc_addr, +- ddata->rsc_len); ++ table = (struct resource_table *)ddata->rsc_va; + rproc->cached_table = kmemdup(table, tablesz, GFP_KERNEL); + if (!rproc->cached_table) + return -ENOMEM; +@@ -297,12 +315,7 @@ stm32_rproc_elf_find_loaded_rsc_table(struct rproc *rproc, + if (!rproc->early_boot) + return rproc_elf_find_loaded_rsc_table(rproc, fw); + +- if (ddata->rsc_addr) +- return (struct resource_table *) +- rproc_da_to_va(rproc, (u64)ddata->rsc_addr, +- ddata->rsc_len); +- +- return NULL; ++ return (struct resource_table *)ddata->rsc_va; + } + + static int stm32_rproc_elf_sanity_check(struct rproc *rproc, +@@ -582,6 +595,8 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev) + struct rproc *rproc = platform_get_drvdata(pdev); + struct stm32_rproc *ddata = rproc->priv; + struct stm32_syscon tz; ++ phys_addr_t rsc_pa; ++ u32 rsc_da; + unsigned int tzen; + int err, irq; + +@@ -636,10 +651,14 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev) + rproc->auto_boot = of_property_read_bool(np, "auto_boot"); + rproc->recovery_disabled = !of_property_read_bool(np, "recovery"); + ++ err = stm32_rproc_of_memory_translations(rproc); ++ if (err) ++ return err; ++ + if (of_property_read_bool(np, "early-booted")) { + rproc->early_boot = true; + +- err = of_property_read_u32(np, "rsc-address", &ddata->rsc_addr); ++ err = of_property_read_u32(np, "rsc-address", &rsc_da); + if (!err) { + err = of_property_read_u32(np, "rsc-size", + &ddata->rsc_len); +@@ -649,9 +668,19 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev) + return err; + } + } ++ err = stm32_rproc_da_to_pa(rproc, rsc_da, &rsc_pa); ++ if (err) ++ return err; ++ ddata->rsc_va = ioremap_wc(rsc_pa, ddata->rsc_len); ++ if (IS_ERR_OR_NULL(ddata->rsc_va)) { ++ dev_err(dev, "Unable to map memory region: %pa+%zx\n", ++ &rsc_pa, ddata->rsc_len); ++ ddata->rsc_va = NULL; ++ return -ENOMEM; ++ } + } + +- return stm32_rproc_of_memory_translations(rproc); ++ return 0; + } + + static int stm32_rproc_probe(struct platform_device *pdev) +@@ -699,13 +728,15 @@ static int stm32_rproc_probe(struct platform_device *pdev) + static int stm32_rproc_remove(struct platform_device *pdev) + { + struct rproc *rproc = platform_get_drvdata(pdev); +- struct device *dev = &pdev->dev; ++ struct stm32_rproc *ddata = rproc->priv; + + if (atomic_read(&rproc->power) > 0) +- dev_warn(dev, "Releasing rproc while firmware running!\n"); ++ rproc_shutdown(rproc); + + rproc_del(rproc); + stm32_rproc_free_mbox(rproc); ++ if (ddata->rsc_va) ++ iounmap(ddata->rsc_va); + rproc_free(rproc); + + return 0; +diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig +index 00878c3..1dbbf16 100644 +--- a/drivers/usb/typec/Kconfig ++++ b/drivers/usb/typec/Kconfig +@@ -102,6 +102,15 @@ config TYPEC_TPS6598X + If you choose to build this driver as a dynamically linked module, the + module will be called tps6598x.ko. + ++config TYPEC_STUSB ++ tristate "STMicroelectronics STUSB Type-C controller driver" ++ depends on I2C ++ select EXTCON ++ help ++ The STMicroelectronics STUSB Type-C controller driver that works ++ with Type-C Port Controller Manager to provide USB Type-C ++ functionalities. ++ + source "drivers/usb/typec/mux/Kconfig" + + source "drivers/usb/typec/altmodes/Kconfig" +diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile +index 45b0aef..aedb153 100644 +--- a/drivers/usb/typec/Makefile ++++ b/drivers/usb/typec/Makefile +@@ -7,6 +7,7 @@ obj-y += fusb302/ + obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o + obj-$(CONFIG_TYPEC_UCSI) += ucsi/ + obj-$(CONFIG_TYPEC_TPS6598X) += tps6598x.o ++obj-$(CONFIG_TYPEC_STUSB) += typec_stusb.o + obj-$(CONFIG_TYPEC) += mux/ + obj-$(CONFIG_TYPEC_TCPCI) += tcpci.o + obj-$(CONFIG_TYPEC_RT1711H) += tcpci_rt1711h.o +diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c +index e61dffb..b518360 100644 +--- a/drivers/usb/typec/class.c ++++ b/drivers/usb/typec/class.c +@@ -1382,6 +1382,21 @@ void typec_set_pwr_opmode(struct typec_port *port, + EXPORT_SYMBOL_GPL(typec_set_pwr_opmode); + + /** ++ * typec_find_power_opmode - Get the typec port power operation mode ++ * @name: port power operation mode string ++ * ++ * This routine is used to find the typec_pwr_opmodes by its string name. ++ * ++ * Returns typec_pwr_opmodes if success, otherwise negative error code. ++ */ ++int typec_find_port_power_opmode(const char *name) ++{ ++ return match_string(typec_pwr_opmodes, ++ ARRAY_SIZE(typec_pwr_opmodes), name); ++} ++EXPORT_SYMBOL_GPL(typec_find_port_power_opmode); ++ ++/** + * typec_find_port_power_role - Get the typec port power capability + * @name: port power capability string + * +diff --git a/drivers/usb/typec/typec_stusb.c b/drivers/usb/typec/typec_stusb.c +new file mode 100644 +index 0000000..a2884fd +--- /dev/null ++++ b/drivers/usb/typec/typec_stusb.c +@@ -0,0 +1,589 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * STMicroelectronics STUSB Type-C controller family driver ++ * ++ * Copyright (C) 2019, STMicroelectronics ++ * Author(s): Amelie Delaunay ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define STUSB_ALERT_STATUS 0x0B /* RC */ ++#define STUSB_ALERT_STATUS_MASK_CTRL 0x0C /* RW */ ++#define STUSB_CC_CONNECTION_STATUS_TRANS 0x0D /* RC */ ++#define STUSB_CC_CONNECTION_STATUS 0x0E /* RO */ ++#define STUSB_MONITORING_STATUS_TRANS 0x0F /* RC */ ++#define STUSB_MONITORING_STATUS 0x10 /* RO */ ++#define STUSB_CC_OPERATION_STATUS 0x11 /* RO */ ++#define STUSB_HW_FAULT_STATUS_TRANS 0x12 /* RC */ ++#define STUSB_HW_FAULT_STATUS 0x13 /* RO */ ++#define STUSB_CC_CAPABILITY_CTRL 0x18 /* RW */ ++#define STUSB_CC_VCONN_SWITCH_CTRL 0x1E /* RW */ ++#define STUSB_VCONN_MONITORING_CTRL 0x20 /* RW */ ++#define STUSB_VBUS_MONITORING_RANGE_CTRL 0x22 /* RW */ ++#define STUSB_RESET_CTRL 0x23 /* RW */ ++#define STUSB_VBUS_DISCHARGE_TIME_CTRL 0x25 /* RW */ ++#define STUSB_VBUS_DISCHARGE_STATUS 0x26 /* RO */ ++#define STUSB_VBUS_ENABLE_STATUS 0x27 /* RO */ ++#define STUSB_CC_POWER_MODE_CTRL 0x28 /* RW */ ++#define STUSB_VBUS_MONITORING_CTRL 0x2E /* RW */ ++#define STUSB1600_REG_MAX 0x2F /* RO - Reserved */ ++ ++/* STUSB_ALERT_STATUS/STUSB_ALERT_STATUS_MASK_CTRL bitfields */ ++#define STUSB_HW_FAULT BIT(4) ++#define STUSB_MONITORING BIT(5) ++#define STUSB_CC_CONNECTION BIT(6) ++#define STUSB_ALL_ALERTS GENMASK(6, 4) ++ ++/* STUSB_CC_CONNECTION_STATUS_TRANS bitfields */ ++#define STUSB_CC_ATTACH_TRANS BIT(0) ++ ++/* STUSB_CC_CONNECTION_STATUS bitfields */ ++#define STUSB_CC_ATTACH BIT(0) ++#define STUSB_CC_VCONN_SUPPLY BIT(1) ++#define STUSB_CC_DATA_ROLE(s) (!!((s) & BIT(2))) ++#define STUSB_CC_POWER_ROLE(s) (!!((s) & BIT(3))) ++#define STUSB_CC_ATTACHED_MODE GENMASK(7, 5) ++ ++/* STUSB_MONITORING_STATUS_TRANS bitfields */ ++#define STUSB_VCONN_PRESENCE_TRANS BIT(0) ++#define STUSB_VBUS_PRESENCE_TRANS BIT(1) ++#define STUSB_VBUS_VSAFE0V_TRANS BIT(2) ++#define STUSB_VBUS_VALID_TRANS BIT(3) ++ ++/* STUSB_MONITORING_STATUS bitfields */ ++#define STUSB_VCONN_PRESENCE BIT(0) ++#define STUSB_VBUS_PRESENCE BIT(1) ++#define STUSB_VBUS_VSAFE0V BIT(2) ++#define STUSB_VBUS_VALID BIT(3) ++ ++/* STUSB_CC_OPERATION_STATUS bitfields */ ++#define STUSB_TYPEC_FSM_STATE GENMASK(4, 0) ++#define STUSB_SINK_POWER_STATE GENMASK(6, 5) ++#define STUSB_CC_ATTACHED BIT(7) ++ ++/* STUSB_HW_FAULT_STATUS_TRANS bitfields */ ++#define STUSB_VCONN_SW_OVP_FAULT_TRANS BIT(0) ++#define STUSB_VCONN_SW_OCP_FAULT_TRANS BIT(1) ++#define STUSB_VCONN_SW_RVP_FAULT_TRANS BIT(2) ++#define STUSB_VPU_VALID_TRANS BIT(4) ++#define STUSB_VPU_OVP_FAULT_TRANS BIT(5) ++#define STUSB_THERMAL_FAULT BIT(7) ++ ++/* STUSB_HW_FAULT_STATUS bitfields */ ++#define STUSB_VCONN_SW_OVP_FAULT_CC2 BIT(0) ++#define STUSB_VCONN_SW_OVP_FAULT_CC1 BIT(1) ++#define STUSB_VCONN_SW_OCP_FAULT_CC2 BIT(2) ++#define STUSB_VCONN_SW_OCP_FAULT_CC1 BIT(3) ++#define STUSB_VCONN_SW_RVP_FAULT_CC2 BIT(4) ++#define STUSB_VCONN_SW_RVP_FAULT_CC1 BIT(5) ++#define STUSB_VPU_VALID BIT(6) ++#define STUSB_VPU_OVP_FAULT BIT(7) ++ ++/* STUSB_CC_CAPABILITY_CTRL bitfields */ ++#define STUSB_CC_VCONN_SUPPLY_EN BIT(0) ++#define STUSB_CC_VCONN_DISCHARGE_EN BIT(4) ++#define STUSB_CC_CURRENT_ADVERTISED GENMASK(7, 6) ++ ++/* STUSB_VCONN_SWITCH_CTRL bitfields */ ++#define STUSB_CC_VCONN_SWITCH_ILIM GENMASK(3, 0) ++ ++/* STUSB_VCONN_MONITORING_CTRL bitfields */ ++#define STUSB_VCONN_UVLO_THRESHOLD BIT(6) ++#define STUSB_VCONN_MONITORING_EN BIT(7) ++ ++/* STUSB_VBUS_MONITORING_RANGE_CTRL bitfields */ ++#define STUSB_SHIFT_LOW_VBUS_LIMIT GENMASK(3, 0) ++#define STUSB_SHIFT_HIGH_VBUS_LIMIT GENMASK(7, 4) ++ ++/* STUSB_RESET_CTRL bitfields */ ++#define STUSB_SW_RESET_EN BIT(0) ++ ++/* STUSB_VBUS_DISCHARGE_TIME_CTRL bitfields */ ++#define STUSBXX02_VBUS_DISCHARGE_TIME_TO_PDO GENMASK(3, 0) ++#define STUSB_VBUS_DISCHARGE_TIME_TO_0V GENMASK(7, 4) ++ ++/* STUSB_VBUS_DISCHARGE_STATUS bitfields */ ++#define STUSB_VBUS_DISCHARGE_EN BIT(7) ++ ++/* STUSB_VBUS_ENABLE_STATUS bitfields */ ++#define STUSB_VBUS_SOURCE_EN BIT(0) ++#define STUSB_VBUS_SINK_EN BIT(1) ++ ++/* STUSB_CC_POWER_MODE_CTRL bitfields */ ++#define STUSB_CC_POWER_MODE GENMASK(2, 0) ++ ++/* STUSB_VBUS_MONITORING_CTRL bitfields */ ++#define STUSB_VDD_UVLO_DISABLE BIT(0) ++#define STUSB_VBUS_VSAFE0V_THRESHOLD GENMASK(2, 1) ++#define STUSB_VBUS_RANGE_DISABLE BIT(4) ++#define STUSB_VDD_OVLO_DISABLE BIT(6) ++ ++enum stusb_pwr_mode { ++ SOURCE_WITH_ACCESSORY, ++ SINK_WITH_ACCESSORY, ++ SINK_WITHOUT_ACCESSORY, ++ DUAL_WITH_ACCESSORY, ++ DUAL_WITH_ACCESSORY_AND_TRY_SRC, ++ DUAL_WITH_ACCESSORY_AND_TRY_SNK, ++}; ++ ++struct stusb { ++ struct device *dev; ++ struct regmap *regmap; ++ struct regulator *vdd_supply; ++ struct regulator *vsys_supply; ++ struct regulator *vconn_supply; ++ ++ struct typec_port *port; ++ struct typec_capability capability; ++ ++ enum typec_port_type port_type; ++ enum typec_pwr_opmode pwr_opmode; ++}; ++ ++static bool stusb_reg_writeable(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case STUSB_ALERT_STATUS_MASK_CTRL: ++ case STUSB_CC_CAPABILITY_CTRL: ++ case STUSB_CC_VCONN_SWITCH_CTRL: ++ case STUSB_VCONN_MONITORING_CTRL: ++ case STUSB_VBUS_MONITORING_RANGE_CTRL: ++ case STUSB_RESET_CTRL: ++ case STUSB_VBUS_DISCHARGE_TIME_CTRL: ++ case STUSB_CC_POWER_MODE_CTRL: ++ case STUSB_VBUS_MONITORING_CTRL: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static bool stusb_reg_readable(struct device *dev, unsigned int reg) ++{ ++ if ((reg >= 0x00 && reg <= 0x0A) || ++ (reg >= 0x14 && reg <= 0x17) || ++ (reg >= 0x19 && reg <= 0x1D) || ++ (reg >= 0x29 && reg <= 0x2D) || ++ (reg == 0x1F || reg == 0x21 || reg == 0x24 || reg == 0x2F)) ++ return false; ++ else ++ return true; ++} ++ ++static bool stusb_reg_volatile(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case STUSB_ALERT_STATUS: ++ case STUSB_CC_CONNECTION_STATUS_TRANS: ++ case STUSB_CC_CONNECTION_STATUS: ++ case STUSB_MONITORING_STATUS_TRANS: ++ case STUSB_MONITORING_STATUS: ++ case STUSB_CC_OPERATION_STATUS: ++ case STUSB_HW_FAULT_STATUS_TRANS: ++ case STUSB_HW_FAULT_STATUS: ++ case STUSB_VBUS_DISCHARGE_STATUS: ++ case STUSB_VBUS_ENABLE_STATUS: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static bool stusb_reg_precious(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case STUSB_ALERT_STATUS: ++ case STUSB_CC_CONNECTION_STATUS_TRANS: ++ case STUSB_MONITORING_STATUS_TRANS: ++ case STUSB_HW_FAULT_STATUS_TRANS: ++ return true; ++ default: ++ return false; ++ } ++} ++ ++static const struct regmap_config stusb1600_regmap_config = { ++ .reg_bits = 8, ++ .reg_stride = 1, ++ .val_bits = 8, ++ .max_register = STUSB1600_REG_MAX, ++ .writeable_reg = stusb_reg_writeable, ++ .readable_reg = stusb_reg_readable, ++ .volatile_reg = stusb_reg_volatile, ++ .precious_reg = stusb_reg_precious, ++ .cache_type = REGCACHE_RBTREE, ++}; ++ ++static bool stusb_get_vconn(struct stusb *chip) ++{ ++ u32 val; ++ int ret; ++ ++ ret = regmap_read(chip->regmap, STUSB_CC_CAPABILITY_CTRL, &val); ++ if (ret) { ++ dev_err(chip->dev, "Unable to get Vconn status: %d\n", ret); ++ return false; ++ } ++ ++ return !!FIELD_GET(STUSB_CC_VCONN_SUPPLY_EN, val);; ++} ++ ++static int stusb_set_vconn(struct stusb *chip, bool on) ++{ ++ int ret; ++ ++ /* Manage VCONN input supply */ ++ if (chip->vconn_supply) { ++ if (on) { ++ ret = regulator_enable(chip->vconn_supply); ++ if (ret) { ++ dev_err(chip->dev, ++ "failed to enable vconn supply: %d\n", ++ ret); ++ return ret; ++ } ++ } else { ++ regulator_disable(chip->vconn_supply); ++ } ++ } ++ ++ /* Manage VCONN monitoring and power path */ ++ ret = regmap_update_bits(chip->regmap, STUSB_VCONN_MONITORING_CTRL, ++ STUSB_VCONN_MONITORING_EN, ++ on ? STUSB_VCONN_MONITORING_EN : 0); ++ if (ret) ++ goto vconn_reg_disable; ++ ++ return 0; ++ ++vconn_reg_disable: ++ if (chip->vconn_supply && on) ++ regulator_disable(chip->vconn_supply); ++ ++ return ret; ++} ++ ++static enum typec_pwr_opmode stusb_get_pwr_opmode(struct stusb *chip) ++{ ++ u32 val; ++ int ret; ++ ++ ret = regmap_read(chip->regmap, STUSB_CC_CAPABILITY_CTRL, &val); ++ if (ret) { ++ dev_err(chip->dev, "Unable to get pwr opmode: %d\n", ret); ++ return TYPEC_PWR_MODE_USB; ++ } ++ ++ return FIELD_GET(STUSB_CC_CURRENT_ADVERTISED, val); ++} ++ ++static int stusb_init(struct stusb *chip) ++{ ++ int ret; ++ ++ /* Change the default Type-C power mode */ ++ if (chip->port_type == TYPEC_PORT_SRC) ++ ret = regmap_update_bits(chip->regmap, ++ STUSB_CC_POWER_MODE_CTRL, ++ STUSB_CC_POWER_MODE, ++ SOURCE_WITH_ACCESSORY); ++ else if (chip->port_type == TYPEC_PORT_SNK) ++ ret = regmap_update_bits(chip->regmap, ++ STUSB_CC_POWER_MODE_CTRL, ++ STUSB_CC_POWER_MODE, ++ SINK_WITH_ACCESSORY); ++ else /* (capability->type == TYPEC_PORT_DRP) */ ++ ret = regmap_update_bits(chip->regmap, ++ STUSB_CC_POWER_MODE_CTRL, ++ STUSB_CC_POWER_MODE, ++ DUAL_WITH_ACCESSORY); ++ if (ret) ++ return ret; ++ ++ if (chip->port_type == TYPEC_PORT_SNK) ++ return 0; ++ ++ /* Change the default Type-C Source power operation mode capability */ ++ ret = regmap_update_bits(chip->regmap, STUSB_CC_CAPABILITY_CTRL, ++ STUSB_CC_CURRENT_ADVERTISED, ++ FIELD_PREP(STUSB_CC_CURRENT_ADVERTISED, ++ chip->pwr_opmode)); ++ if (ret) ++ return ret; ++ ++ /* Manage Type-C Source Vconn supply */ ++ if (stusb_get_vconn(chip)) { ++ ret = stusb_set_vconn(chip, true); ++ if (ret) ++ return ret; ++ } ++ ++ /* Mask all events interrupts - to be unmasked with interrupt support */ ++ ret = regmap_update_bits(chip->regmap, STUSB_ALERT_STATUS_MASK_CTRL, ++ STUSB_ALL_ALERTS, STUSB_ALL_ALERTS); ++ ++ return ret; ++} ++ ++static int stusb_fw_get_caps(struct stusb *chip) ++{ ++ struct fwnode_handle *fwnode = device_get_named_child_node(chip->dev, ++ "connector"); ++ const char *cap_str; ++ int ret; ++ ++ if (!fwnode) ++ return -EINVAL; ++ ++ chip->capability.fwnode = fwnode; ++ ++ ret = fwnode_property_read_string(fwnode, "power-role", &cap_str); ++ if (!ret) { ++ chip->port_type = typec_find_port_power_role(cap_str); ++ if (chip->port_type < 0) ++ return -EINVAL; ++ ++ chip->capability.type = chip->port_type; ++ } ++ ++ if (chip->port_type == TYPEC_PORT_SNK) ++ goto sink; ++ ++ if (chip->port_type == TYPEC_PORT_DRP) ++ chip->capability.prefer_role = TYPEC_SINK; ++ ++ ret = fwnode_property_read_string(fwnode, "power-opmode", &cap_str); ++ if (!ret) { ++ chip->pwr_opmode = typec_find_port_power_opmode(cap_str); ++ ++ /* Power delivery not yet supported */ ++ if (chip->pwr_opmode < 0 || ++ chip->pwr_opmode == TYPEC_PWR_MODE_PD) { ++ dev_err(chip->dev, "bad power operation mode: %d\n", ++ chip->pwr_opmode); ++ return -EINVAL; ++ } ++ ++ } else { ++ chip->pwr_opmode = stusb_get_pwr_opmode(chip); ++ } ++ ++sink: ++ return 0; ++} ++ ++static int stusb_get_caps(struct stusb *chip, bool *try) ++{ ++ enum typec_port_type *type = &chip->capability.type; ++ enum typec_port_data *data = &chip->capability.data; ++ enum typec_accessory *accessory = chip->capability.accessory; ++ u32 val; ++ int ret; ++ ++ chip->capability.revision = USB_TYPEC_REV_1_2; ++ ++ ret = regmap_read(chip->regmap, STUSB_CC_POWER_MODE_CTRL, &val); ++ if (ret) ++ return ret; ++ ++ *try = false; ++ ++ switch (FIELD_GET(STUSB_CC_POWER_MODE, val)) { ++ case SOURCE_WITH_ACCESSORY: ++ *type = TYPEC_PORT_SRC; ++ *data = TYPEC_PORT_DFP; ++ *accessory++ = TYPEC_ACCESSORY_AUDIO; ++ *accessory++ = TYPEC_ACCESSORY_DEBUG; ++ break; ++ case SINK_WITH_ACCESSORY: ++ *type = TYPEC_PORT_SNK; ++ *data = TYPEC_PORT_UFP; ++ *accessory++ = TYPEC_ACCESSORY_AUDIO; ++ *accessory++ = TYPEC_ACCESSORY_DEBUG; ++ break; ++ case SINK_WITHOUT_ACCESSORY: ++ *type = TYPEC_PORT_SNK; ++ *data = TYPEC_PORT_UFP; ++ break; ++ case DUAL_WITH_ACCESSORY: ++ *type = TYPEC_PORT_DRP; ++ *data = TYPEC_PORT_DRD; ++ *accessory++ = TYPEC_ACCESSORY_AUDIO; ++ *accessory++ = TYPEC_ACCESSORY_DEBUG; ++ break; ++ case DUAL_WITH_ACCESSORY_AND_TRY_SRC: ++ case DUAL_WITH_ACCESSORY_AND_TRY_SNK: ++ *type = TYPEC_PORT_DRP; ++ *data = TYPEC_PORT_DRD; ++ *accessory++ = TYPEC_ACCESSORY_AUDIO; ++ *accessory++ = TYPEC_ACCESSORY_DEBUG; ++ *try = true; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ chip->port_type = *type; ++ ++ return stusb_fw_get_caps(chip); ++} ++ ++static const struct of_device_id stusb_of_match[] = { ++ { .compatible = "st,stusb1600", .data = &stusb1600_regmap_config}, ++ {}, ++}; ++ ++static int stusb_probe(struct i2c_client *client, ++ const struct i2c_device_id *id) ++{ ++ struct stusb *chip; ++ const struct of_device_id *match; ++ struct regmap_config *regmap_config; ++ bool try_role; ++ int ret; ++ ++ chip = devm_kzalloc(&client->dev, sizeof(struct stusb), GFP_KERNEL); ++ if (!chip) ++ return -ENOMEM; ++ ++ i2c_set_clientdata(client, chip); ++ ++ match = i2c_of_match_device(stusb_of_match, client); ++ regmap_config = (struct regmap_config *)match->data; ++ chip->regmap = devm_regmap_init_i2c(client, regmap_config); ++ if (IS_ERR(chip->regmap)) { ++ ret = PTR_ERR(chip->regmap); ++ dev_err(&client->dev, ++ "Failed to allocate register map:%d\n", ret); ++ return ret; ++ } ++ ++ chip->dev = &client->dev; ++ ++ chip->vdd_supply = devm_regulator_get_optional(chip->dev, "vdd"); ++ if (IS_ERR(chip->vdd_supply)) { ++ ret = PTR_ERR(chip->vdd_supply); ++ if (ret != -ENODEV) ++ return ret; ++ chip->vdd_supply = NULL; ++ } else { ++ ret = regulator_enable(chip->vdd_supply); ++ if (ret) { ++ dev_err(chip->dev, ++ "Failed to enable vdd supply: %d\n", ret); ++ return ret; ++ } ++ } ++ ++ chip->vsys_supply = devm_regulator_get_optional(chip->dev, "vsys"); ++ if (IS_ERR(chip->vsys_supply)) { ++ ret = PTR_ERR(chip->vsys_supply); ++ if (ret != -ENODEV) ++ goto vdd_reg_disable; ++ chip->vsys_supply = NULL; ++ } else { ++ ret = regulator_enable(chip->vsys_supply); ++ if (ret) { ++ dev_err(chip->dev, ++ "Failed to enable vsys supply: %d\n", ret); ++ goto vdd_reg_disable; ++ } ++ } ++ ++ chip->vconn_supply = devm_regulator_get_optional(chip->dev, "vconn"); ++ if (IS_ERR(chip->vconn_supply)) { ++ ret = PTR_ERR(chip->vconn_supply); ++ if (ret != -ENODEV) ++ goto vsys_reg_disable; ++ chip->vconn_supply = NULL; ++ } ++ ++ ret = stusb_get_caps(chip, &try_role); ++ if (ret) { ++ dev_err(chip->dev, "failed to get port caps: %d\n", ret); ++ goto vsys_reg_disable; ++ } ++ ++ ret = stusb_init(chip); ++ if (ret) { ++ dev_err(chip->dev, "failed to init port: %d\n", ret); ++ goto vsys_reg_disable; ++ } ++ ++ chip->port = typec_register_port(chip->dev, &chip->capability); ++ if (!chip->port) { ++ ret = -ENODEV; ++ goto all_reg_disable; ++ } ++ ++ /* To be moved in attach/detach procedure with interrupt support */ ++ typec_set_pwr_opmode(chip->port, chip->pwr_opmode); ++ ++ dev_info(chip->dev, "STUSB driver registered\n"); ++ ++ return 0; ++ ++all_reg_disable: ++ if (stusb_get_vconn(chip)) ++ stusb_set_vconn(chip, false); ++vsys_reg_disable: ++ if (chip->vsys_supply) ++ regulator_disable(chip->vsys_supply); ++vdd_reg_disable: ++ if (chip->vdd_supply) ++ regulator_disable(chip->vdd_supply); ++ ++ return ret; ++} ++ ++static int stusb_remove(struct i2c_client *client) ++{ ++ struct stusb *chip = i2c_get_clientdata(client); ++ ++ typec_unregister_port(chip->port); ++ ++ if (stusb_get_vconn(chip)) ++ stusb_set_vconn(chip, false); ++ ++ if (chip->vdd_supply) ++ regulator_disable(chip->vdd_supply); ++ ++ if (chip->vsys_supply) ++ regulator_disable(chip->vsys_supply); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int stusb_resume(struct device *dev) ++{ ++ struct stusb *chip = dev_get_drvdata(dev); ++ ++ return stusb_init(chip); ++} ++#endif ++ ++static SIMPLE_DEV_PM_OPS(stusb_pm_ops, NULL, stusb_resume); ++ ++static struct i2c_driver stusb_driver = { ++ .driver = { ++ .name = "typec_stusb", ++ .pm = &stusb_pm_ops, ++ .of_match_table = stusb_of_match, ++ }, ++ .probe = stusb_probe, ++ .remove = stusb_remove, ++}; ++module_i2c_driver(stusb_driver); ++ ++MODULE_AUTHOR("Amelie Delaunay "); ++MODULE_DESCRIPTION("STMicroelectronics STUSB Type-C controller driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h +index 7df4eca..2671776 100644 +--- a/include/linux/usb/typec.h ++++ b/include/linux/usb/typec.h +@@ -241,6 +241,7 @@ int typec_set_orientation(struct typec_port *port, + enum typec_orientation typec_get_orientation(struct typec_port *port); + int typec_set_mode(struct typec_port *port, int mode); + ++int typec_find_port_power_opmode(const char *name); + int typec_find_port_power_role(const char *name); + int typec_find_power_role(const char *name); + int typec_find_port_data_role(const char *name); +diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c +index 71d341b..24948b9 100644 +--- a/sound/soc/stm/stm32_adfsdm.c ++++ b/sound/soc/stm/stm32_adfsdm.c +@@ -304,6 +304,7 @@ MODULE_DEVICE_TABLE(of, stm32_adfsdm_of_match); + static int stm32_adfsdm_probe(struct platform_device *pdev) + { + struct stm32_adfsdm_priv *priv; ++ struct snd_soc_component *component; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); +@@ -331,9 +332,15 @@ static int stm32_adfsdm_probe(struct platform_device *pdev) + if (IS_ERR(priv->iio_cb)) + return PTR_ERR(priv->iio_cb); + +- ret = devm_snd_soc_register_component(&pdev->dev, +- &stm32_adfsdm_soc_platform, +- NULL, 0); ++ component = devm_kzalloc(&pdev->dev, sizeof(*component), GFP_KERNEL); ++ if (!component) ++ return -ENOMEM; ++#ifdef CONFIG_DEBUG_FS ++ component->debugfs_prefix = "pcm"; ++#endif ++ ++ ret = snd_soc_add_component(&pdev->dev, component, ++ &stm32_adfsdm_soc_platform, NULL, 0); + if (ret < 0) + dev_err(&pdev->dev, "%s: Failed to register PCM platform\n", + __func__); +@@ -341,12 +348,20 @@ static int stm32_adfsdm_probe(struct platform_device *pdev) + return ret; + } + ++static int stm32_adfsdm_remove(struct platform_device *pdev) ++{ ++ snd_soc_unregister_component(&pdev->dev); ++ ++ return 0; ++} ++ + static struct platform_driver stm32_adfsdm_driver = { + .driver = { + .name = STM32_ADFSDM_DRV_NAME, + .of_match_table = stm32_adfsdm_of_match, + }, + .probe = stm32_adfsdm_probe, ++ .remove = stm32_adfsdm_remove, + }; + + module_platform_driver(stm32_adfsdm_driver); +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0054-ARM-stm32mp1-r0-rc4-hotfix-w903.1-DEVICETREE.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0054-ARM-stm32mp1-r0-rc4-hotfix-w903.1-DEVICETREE.patch new file mode 100644 index 0000000..6a592e5 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0054-ARM-stm32mp1-r0-rc4-hotfix-w903.1-DEVICETREE.patch @@ -0,0 +1,117 @@ +From 5648392b05ff7bb1f513ad9e9eb27b5420ce745d Mon Sep 17 00:00:00 2001 +From: christophe montaud +Date: Mon, 14 Jan 2019 17:19:51 +0100 +Subject: [PATCH 54/55] ARM stm32mp1 r0 rc4 hotfix-w903.1 DEVICETREE + +--- + arch/arm/boot/dts/stm32mp157a-dk1.dts | 13 +++++++++++++ + arch/arm/boot/dts/stm32mp157c-dk2.dts | 1 + + arch/arm/boot/dts/stm32mp157c.dtsi | 14 ++++++++++++++ + 3 files changed, 28 insertions(+) + +diff --git a/arch/arm/boot/dts/stm32mp157a-dk1.dts b/arch/arm/boot/dts/stm32mp157a-dk1.dts +index 7e911f3..28017e4 100644 +--- a/arch/arm/boot/dts/stm32mp157a-dk1.dts ++++ b/arch/arm/boot/dts/stm32mp157a-dk1.dts +@@ -287,6 +287,19 @@ + /delete-property/dmas; + /delete-property/dma-names; + ++ typec: stusb1600@28 { ++ compatible = "st,stusb1600"; ++ reg = <0x28>; ++ status = "okay"; ++ ++ typec_con: connector { ++ compatible = "usb-c-connector"; ++ label = "USB-C"; ++ power-role = "sink"; ++ power-opmode = "default"; ++ }; ++ }; ++ + pmic: stpmic@33 { + compatible = "st,stpmic1"; + reg = <0x33>; +diff --git a/arch/arm/boot/dts/stm32mp157c-dk2.dts b/arch/arm/boot/dts/stm32mp157c-dk2.dts +index c276c59..340e022 100644 +--- a/arch/arm/boot/dts/stm32mp157c-dk2.dts ++++ b/arch/arm/boot/dts/stm32mp157c-dk2.dts +@@ -106,6 +106,7 @@ + + /* Wifi */ + &sdmmc2 { ++ arm,primecell-periphid = <0x10153180>; + pinctrl-names = "default", "opendrain", "sleep"; + pinctrl-0 = <&sdmmc2_b4_pins_a>; + pinctrl-1 = <&sdmmc2_b4_od_pins_a>; +diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi +index 4de499e..b09ef8b 100644 +--- a/arch/arm/boot/dts/stm32mp157c.dtsi ++++ b/arch/arm/boot/dts/stm32mp157c.dtsi +@@ -892,6 +892,8 @@ + + compatible = "st,stm32-sai-sub-a"; + reg = <0x4 0x1c>; ++ clocks = <&rcc SAI1_K>; ++ clock-names = "sai_ck"; + dmas = <&dmamux1 87 0x400 0x01>; + status = "disabled"; + }; +@@ -900,6 +902,8 @@ + #sound-dai-cells = <0>; + compatible = "st,stm32-sai-sub-b"; + reg = <0x24 0x1c>; ++ clocks = <&rcc SAI1_K>; ++ clock-names = "sai_ck"; + dmas = <&dmamux1 88 0x400 0x01>; + status = "disabled"; + }; +@@ -919,6 +923,8 @@ + #sound-dai-cells = <0>; + compatible = "st,stm32-sai-sub-a"; + reg = <0x4 0x1c>; ++ clocks = <&rcc SAI2_K>; ++ clock-names = "sai_ck"; + dmas = <&dmamux1 89 0x400 0x01>; + status = "disabled"; + }; +@@ -927,6 +933,8 @@ + #sound-dai-cells = <0>; + compatible = "st,stm32-sai-sub-b"; + reg = <0x24 0x1c>; ++ clocks = <&rcc SAI2_K>; ++ clock-names = "sai_ck"; + dmas = <&dmamux1 90 0x400 0x01>; + status = "disabled"; + }; +@@ -946,6 +954,8 @@ + #sound-dai-cells = <0>; + compatible = "st,stm32-sai-sub-a"; + reg = <0x04 0x1c>; ++ clocks = <&rcc SAI3_K>; ++ clock-names = "sai_ck"; + dmas = <&dmamux1 113 0x400 0x01>; + status = "disabled"; + }; +@@ -954,6 +964,8 @@ + #sound-dai-cells = <0>; + compatible = "st,stm32-sai-sub-b"; + reg = <0x24 0x1c>; ++ clocks = <&rcc SAI3_K>; ++ clock-names = "sai_ck"; + dmas = <&dmamux1 114 0x400 0x01>; + status = "disabled"; + }; +@@ -1442,6 +1454,8 @@ + #sound-dai-cells = <0>; + compatible = "st,stm32-sai-sub-b"; + reg = <0x24 0x1c>; ++ clocks = <&rcc SAI4_K>; ++ clock-names = "sai_ck"; + dmas = <&dmamux1 100 0x400 0x01>; + status = "disabled"; + }; +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0055-ARM-stm32mp1-r0-rc4-hotfix-w903.1-DEFCONFIG.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0055-ARM-stm32mp1-r0-rc4-hotfix-w903.1-DEFCONFIG.patch new file mode 100644 index 0000000..3d4ed1f --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0055-ARM-stm32mp1-r0-rc4-hotfix-w903.1-DEFCONFIG.patch @@ -0,0 +1,25 @@ +From 449bea3af472f64774547738478ca3b2cfa91cae Mon Sep 17 00:00:00 2001 +From: christophe montaud +Date: Mon, 14 Jan 2019 17:20:30 +0100 +Subject: [PATCH 55/55] ARM stm32mp1 r0 rc4 hotfix-w903.1 DEFCONFIG + +--- + arch/arm/configs/fragment-02-multiv7_addons.config | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/arch/arm/configs/fragment-02-multiv7_addons.config b/arch/arm/configs/fragment-02-multiv7_addons.config +index 38c5562..ddf15a9 100644 +--- a/arch/arm/configs/fragment-02-multiv7_addons.config ++++ b/arch/arm/configs/fragment-02-multiv7_addons.config +@@ -346,6 +346,8 @@ CONFIG_USB_CONFIGFS=y + # + # USB Physical Layer drivers + # ++CONFIG_TYPEC=y ++CONFIG_TYPEC_STUSB=y + + # + # Platform Support +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0056-ARM-stm32mp1-r0-rc4-hotfix-w903.3-DRIVERS.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0056-ARM-stm32mp1-r0-rc4-hotfix-w903.3-DRIVERS.patch new file mode 100644 index 0000000..70b6a67 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0056-ARM-stm32mp1-r0-rc4-hotfix-w903.3-DRIVERS.patch @@ -0,0 +1,554 @@ +From b6678ec426d7dbc5248a2d98c0c2c7a5b55e4d5f Mon Sep 17 00:00:00 2001 +From: christophe montaud +Date: Wed, 16 Jan 2019 17:51:05 +0100 +Subject: [PATCH 56/58] ARM stm32mp1 r0 rc4 hotfix w903.3 DRIVERS + +--- + drivers/clk/clk-stm32mp1.c | 142 ++++++++++++++++++++-------------- + drivers/i2c/busses/i2c-stm32f7.c | 33 +++++++- + drivers/irqchip/irq-stm32-exti.c | 160 +++++++++++++++++++++++++-------------- + kernel/power/suspend.c | 1 - + 4 files changed, 218 insertions(+), 118 deletions(-) + +diff --git a/drivers/clk/clk-stm32mp1.c b/drivers/clk/clk-stm32mp1.c +index 7eccaa1..56d7b86 100644 +--- a/drivers/clk/clk-stm32mp1.c ++++ b/drivers/clk/clk-stm32mp1.c +@@ -2215,8 +2215,8 @@ static const struct stm32_mux_cfg ker_mux_cfg[M_LAST] = { + + static const struct clock_config stm32mp1_clock_cfg[] = { + /* Oscillator divider */ +- DIV(NO_ID, "clk-hsi-div", "clk-hsi", 0, RCC_HSICFGR, 0, 2, +- CLK_DIVIDER_READ_ONLY), ++ DIV(NO_ID, "clk-hsi-div", "clk-hsi", CLK_DIVIDER_POWER_OF_TWO, ++ RCC_HSICFGR, 0, 2, CLK_DIVIDER_READ_ONLY), + + /* External / Internal Oscillators */ + SGATE_MP1(CK_HSE, "ck_hse", "clk-hse", 0, RCC_OCENSETR, 8, 0), +@@ -2517,11 +2517,10 @@ static const struct clock_config stm32mp1_clock_cfg[] = { + CLK_SET_RATE_NO_REPARENT, + _NO_GATE, + _MMUX(M_ETHCK), +- _DIV(RCC_ETHCKSELR, 4, 4, CLK_DIVIDER_ALLOW_ZERO, NULL)), ++ _DIV(RCC_ETHCKSELR, 4, 4, 0, NULL)), + + /* RTC clock */ +- SDIV(NO_ID, "ck_hse_rtc", "ck_hse", 0, RCC_RTCDIVR, 0, 7, +- CLK_DIVIDER_ALLOW_ZERO), ++ SDIV(NO_ID, "ck_hse_rtc", "ck_hse", 0, RCC_RTCDIVR, 0, 6, 0), + + COMPOSITE(RTC, "ck_rtc", rtc_src, CLK_OPS_PARENT_ENABLE | + CLK_SET_RATE_PARENT, +@@ -2724,52 +2723,53 @@ static struct sreg clock_gating[] = { + }; + + struct smux { +- const char *name; +- struct clk *clk; +- struct clk *clkp; ++ u32 clk_id; ++ u32 mux_id; ++ struct clk_hw *hw; + }; + +-#define KER_SRC(_clk_name)\ ++#define KER_SRC(_clk_id, _mux_id)\ + {\ +- .name = _clk_name,\ +-} +- +-struct smux _mux_kernel[] = { +- KER_SRC("sdmmc1_k"), +- KER_SRC("spi2_k"), +- KER_SRC("spi4_k"), +- KER_SRC("i2c1_k"), +- KER_SRC("i2c3_k"), +- KER_SRC("lptim2_k"), +- KER_SRC("lptim3_k"), +- KER_SRC("usart2_k"), +- KER_SRC("usart3_k"), +- KER_SRC("uart7_k"), +- KER_SRC("sai1_k"), +- KER_SRC("ethck_k"), +- KER_SRC("i2c4_k"), +- KER_SRC("rng2_k"), +- KER_SRC("sdmmc3_k"), +- KER_SRC("fmc_k"), +- KER_SRC("qspi_k"), +- KER_SRC("usbphy_k"), +- KER_SRC("usbo_k"), +- KER_SRC("spdif_k"), +- KER_SRC("spi1_k"), +- KER_SRC("cec_k"), +- KER_SRC("lptim1_k"), +- KER_SRC("uart6_k"), +- KER_SRC("fdcan_k"), +- KER_SRC("sai2_k"), +- KER_SRC("sai3_k"), +- KER_SRC("sai4_k"), +- KER_SRC("adc12_k"), +- KER_SRC("dsi_k"), +- KER_SRC("ck_per"), +- KER_SRC("rng1_k"), +- KER_SRC("stgen_k"), +- KER_SRC("usart1_k"), +- KER_SRC("spi6_k"), ++ .clk_id = _clk_id,\ ++ .mux_id = _mux_id,\ ++} ++ ++struct smux _mux_kernel[M_LAST] = { ++ KER_SRC(SDMMC1_K, M_SDMMC12), ++ KER_SRC(SDMMC3_K, M_SDMMC3), ++ KER_SRC(FMC_K, M_FMC), ++ KER_SRC(QSPI_K, M_QSPI), ++ KER_SRC(RNG1_K, M_RNG1), ++ KER_SRC(RNG2_K, M_RNG2), ++ KER_SRC(USBPHY_K, M_USBPHY), ++ KER_SRC(USBO_K, M_USBO), ++ KER_SRC(STGEN_K, M_STGEN), ++ KER_SRC(SPDIF_K, M_SPDIF), ++ KER_SRC(SPI1_K, M_SPI1), ++ KER_SRC(SPI2_K, M_SPI23), ++ KER_SRC(SPI4_K, M_SPI45), ++ KER_SRC(SPI6_K, M_SPI6), ++ KER_SRC(CEC_K, M_CEC), ++ KER_SRC(I2C1_K, M_I2C12), ++ KER_SRC(I2C3_K, M_I2C35), ++ KER_SRC(I2C4_K, M_I2C46), ++ KER_SRC(LPTIM1_K, M_LPTIM1), ++ KER_SRC(LPTIM2_K, M_LPTIM23), ++ KER_SRC(LPTIM4_K, M_LPTIM45), ++ KER_SRC(USART1_K, M_USART1), ++ KER_SRC(USART2_K, M_UART24), ++ KER_SRC(USART3_K, M_UART35), ++ KER_SRC(USART6_K, M_USART6), ++ KER_SRC(UART7_K, M_UART78), ++ KER_SRC(SAI1_K, M_SAI1), ++ KER_SRC(SAI2_K, M_SAI2), ++ KER_SRC(SAI3_K, M_SAI3), ++ KER_SRC(SAI4_K, M_SAI4), ++ KER_SRC(DSI_K, M_DSI), ++ KER_SRC(FDCAN_K, M_FDCAN), ++ KER_SRC(ADC12_K, M_ADC12), ++ KER_SRC(ETHCK_K, M_ETHCK), ++ KER_SRC(CK_PER, M_CKPER), + }; + + static struct sreg pll_clock[] = { +@@ -2861,22 +2861,52 @@ static void stm32mp1_restore_pll(struct sreg *sreg, int size) + } + } + +-static void stm32mp1_backup_mux(struct smux *smux, int size) ++static void stm32mp1_backup_mux(struct device_node *np, ++ struct smux *smux, int size) + { + int i; ++ struct of_phandle_args clkspec; ++ ++ clkspec.np = np; ++ clkspec.args_count = 1; + + for (i = 0; i < size; i++) { +- smux[i].clk = __clk_lookup(smux[i].name); +- smux[i].clkp = clk_get_parent(smux[i].clk); ++ clkspec.args[0] = smux[i].clk_id; ++ smux[i].hw = __clk_get_hw(of_clk_get_from_provider(&clkspec)); + } + } + + static void stm32mp1_restore_mux(struct smux *smux, int size) + { + int i; ++ struct clk_hw *hw, *hwp1, *hwp2; ++ struct mux_cfg *mux; ++ u8 idx; + +- for (i = 0; i < size; i++) +- clk_set_parent_force(smux[i].clk, smux[i].clkp); ++ /* These MUX are glitch free. ++ * Then we have to restore mux thru clock framework ++ * to be sure that CLK_OPS_PARENT_ENABLE will be exploited ++ */ ++ for (i = 0; i < M_LAST; i++) { ++ /* get parent strored in clock framework */ ++ hw = smux[i].hw; ++ hwp1 = clk_hw_get_parent(hw); ++ ++ /* Get parent corresponding to mux register */ ++ mux = ker_mux_cfg[smux[i].mux_id].mux; ++ idx = readl_relaxed(rcc_base + mux->reg_off) >> mux->shift; ++ idx &= (BIT(mux->width) - 1); ++ hwp2 = clk_hw_get_parent_by_index(hw, idx); ++ ++ /* check if parent from mux & clock framework are differents */ ++ if (hwp1 != hwp2) { ++ /* update first clock framework with the true parent */ ++ clk_set_parent(hw->clk, hwp2->clk); ++ ++ /* Restore now new parent */ ++ clk_set_parent(hw->clk, hwp1->clk); ++ } ++ } + } + + #define RCC_BIT_HSI 0 +@@ -2900,9 +2930,6 @@ static int stm32mp1_clk_suspend(void) + /* Save clock gating regs */ + stm32mp1_backup_sreg(clock_gating, ARRAY_SIZE(clock_gating)); + +- /* Save kernel clock regs */ +- stm32mp1_backup_mux(_mux_kernel, ARRAY_SIZE(_mux_kernel)); +- + /* Enable ck_xxx_ker clocks if ck_xxx was on */ + reg = readl_relaxed(rcc_base + RCC_OCENSETR) & RCC_CK_OSC_MASK; + writel_relaxed(reg << 1, rcc_base + RCC_OCENSETR); +@@ -2970,6 +2997,9 @@ static int stm32_rcc_init_pwr(struct device_node *np) + + SMC(STM32_SVC_RCC, STM32_WRITE, RCC_SREQCLRR, RCC_STOP_MASK); + ++ /* Prepare kernel clock source backup */ ++ stm32mp1_backup_mux(np, _mux_kernel, ARRAY_SIZE(_mux_kernel)); ++ + register_syscore_ops(&stm32mp1_clk_ops); + + return 0; +diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c +index ac30aea..3af2637 100644 +--- a/drivers/i2c/busses/i2c-stm32f7.c ++++ b/drivers/i2c/busses/i2c-stm32f7.c +@@ -978,6 +978,26 @@ static int stm32f7_i2c_smbus_xfer_msg(struct stm32f7_i2c_dev *i2c_dev, + cr2 &= ~STM32F7_I2C_CR2_RD_WRN; + f7_msg->read_write = I2C_SMBUS_READ; + break; ++ case I2C_SMBUS_I2C_BLOCK_DATA: ++ if (data->block[0] > I2C_SMBUS_BLOCK_MAX) { ++ dev_err(dev, "Invalid block %s size %d\n", ++ f7_msg->read_write == I2C_SMBUS_READ ? ++ "read" : "write", ++ data->block[0]); ++ return -EINVAL; ++ } ++ ++ if (f7_msg->read_write) { ++ f7_msg->stop = false; ++ f7_msg->count = data->block[0]; ++ cr2 &= ~STM32F7_I2C_CR2_RD_WRN; ++ } else { ++ f7_msg->stop = true; ++ f7_msg->count = data->block[0] + 1; ++ for (i = 1; i <= data->block[0]; i++) ++ f7_msg->smbus_buf[i] = data->block[i]; ++ } ++ break; + default: + dev_err(dev, "Unsupported smbus protocol %d\n", f7_msg->size); + return -EOPNOTSUPP; +@@ -986,7 +1006,8 @@ static int stm32f7_i2c_smbus_xfer_msg(struct stm32f7_i2c_dev *i2c_dev, + f7_msg->buf = f7_msg->smbus_buf; + + /* Configure PEC */ +- if ((flags & I2C_CLIENT_PEC) && f7_msg->size != I2C_SMBUS_QUICK) { ++ if ((flags & I2C_CLIENT_PEC) && f7_msg->size != I2C_SMBUS_QUICK && ++ f7_msg->size != I2C_SMBUS_I2C_BLOCK_DATA) { + cr1 |= STM32F7_I2C_CR1_PECEN; + cr2 |= STM32F7_I2C_CR2_PECBYTE; + if (!f7_msg->read_write) +@@ -1655,7 +1676,8 @@ static int stm32f7_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, + } + + /* Check PEC */ +- if ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK && read_write) { ++ if ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK && ++ size != I2C_SMBUS_I2C_BLOCK_DATA && read_write) { + ret = stm32f7_i2c_smbus_check_pec(i2c_dev); + if (ret) + goto pm_free; +@@ -1672,6 +1694,10 @@ static int stm32f7_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, + data->word = f7_msg->smbus_buf[0] | + (f7_msg->smbus_buf[1] << 8); + break; ++ case I2C_SMBUS_I2C_BLOCK_DATA: ++ for (i = 0; i < data->block[0]; i++) ++ data->block[i + 1] = f7_msg->smbus_buf[i]; ++ break; + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_BLOCK_PROC_CALL: + for (i = 0; i <= f7_msg->smbus_buf[0]; i++) +@@ -1854,7 +1880,8 @@ static u32 stm32f7_i2c_func(struct i2c_adapter *adap) + I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL | +- I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_PEC; ++ I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_PEC | ++ I2C_FUNC_SMBUS_I2C_BLOCK; + } + + static struct i2c_algorithm stm32f7_i2c_algo = { +diff --git a/drivers/irqchip/irq-stm32-exti.c b/drivers/irqchip/irq-stm32-exti.c +index 223ee2e..793be075a 100644 +--- a/drivers/irqchip/irq-stm32-exti.c ++++ b/drivers/irqchip/irq-stm32-exti.c +@@ -14,8 +14,10 @@ + #include + #include + #include ++#include + #include + #include ++#include + #include + + #include +@@ -37,12 +39,6 @@ struct stm32_exti_bank { + + #define UNDEF_REG ~0 + +-enum stm32_exti_hwspinlock { +- HWSPINLOCK_UNKNOWN, +- HWSPINLOCK_NONE, +- HWSPINLOCK_READY, +-}; +- + struct stm32_desc_irq { + u32 exti; + u32 irq_parent; +@@ -69,8 +65,6 @@ struct stm32_exti_host_data { + void __iomem *base; + struct stm32_exti_chip_data *chips_data; + const struct stm32_exti_drv_data *drv_data; +- struct device_node *node; +- enum stm32_exti_hwspinlock hwlock_state; + struct hwspinlock *hwlock; + }; + +@@ -285,49 +279,27 @@ static int stm32_exti_set_type(struct irq_data *d, + + static int stm32_exti_hwspin_lock(struct stm32_exti_chip_data *chip_data) + { +- struct stm32_exti_host_data *host_data = chip_data->host_data; +- struct hwspinlock *hwlock; +- int id, ret = 0, timeout = 0; +- +- /* first time, check for hwspinlock availability */ +- if (unlikely(host_data->hwlock_state == HWSPINLOCK_UNKNOWN)) { +- id = of_hwspin_lock_get_id(host_data->node, 0); +- if (id >= 0) { +- hwlock = hwspin_lock_request_specific(id); +- if (hwlock) { +- /* found valid hwspinlock */ +- host_data->hwlock_state = HWSPINLOCK_READY; +- host_data->hwlock = hwlock; +- pr_debug("%s hwspinlock = %d\n", __func__, id); +- } else { +- host_data->hwlock_state = HWSPINLOCK_NONE; +- } +- } else if (id != -EPROBE_DEFER) { +- host_data->hwlock_state = HWSPINLOCK_NONE; +- } else { +- /* hwspinlock driver shall be ready at that stage */ +- ret = -EPROBE_DEFER; +- } +- } ++ int ret, timeout = 0; + +- if (likely(host_data->hwlock_state == HWSPINLOCK_READY)) { +- /* +- * Use the x_raw API since we are under spin_lock protection. +- * Do not use the x_timeout API because we are under irq_disable +- * mode (see __setup_irq()) +- */ +- do { +- ret = hwspin_trylock_raw(host_data->hwlock); +- if (!ret) +- return 0; +- +- udelay(HWSPNLCK_RETRY_DELAY); +- timeout += HWSPNLCK_RETRY_DELAY; +- } while (timeout < HWSPNLCK_TIMEOUT); +- +- if (ret == -EBUSY) +- ret = -ETIMEDOUT; +- } ++ if (!chip_data->host_data->hwlock) ++ return 0; ++ ++ /* ++ * Use the x_raw API since we are under spin_lock protection. ++ * Do not use the x_timeout API because we are under irq_disable ++ * mode (see __setup_irq()) ++ */ ++ do { ++ ret = hwspin_trylock_raw(chip_data->host_data->hwlock); ++ if (!ret) ++ return 0; ++ ++ udelay(HWSPNLCK_RETRY_DELAY); ++ timeout += HWSPNLCK_RETRY_DELAY; ++ } while (timeout < HWSPNLCK_TIMEOUT); ++ ++ if (ret == -EBUSY) ++ ret = -ETIMEDOUT; + + if (ret) + pr_err("%s can't get hwspinlock (%d)\n", __func__, ret); +@@ -337,7 +309,7 @@ static int stm32_exti_hwspin_lock(struct stm32_exti_chip_data *chip_data) + + static void stm32_exti_hwspin_unlock(struct stm32_exti_chip_data *chip_data) + { +- if (likely(chip_data->host_data->hwlock_state == HWSPINLOCK_READY)) ++ if (chip_data->host_data->hwlock) + hwspin_unlock_raw(chip_data->host_data->hwlock); + } + +@@ -710,8 +682,6 @@ stm32_exti_host_data *stm32_exti_host_init(const struct stm32_exti_drv_data *dd, + return NULL; + + host_data->drv_data = dd; +- host_data->node = node; +- host_data->hwlock_state = HWSPINLOCK_UNKNOWN; + host_data->chips_data = kcalloc(dd->bank_nr, + sizeof(struct stm32_exti_chip_data), + GFP_KERNEL); +@@ -738,7 +708,8 @@ stm32_exti_host_data *stm32_exti_host_init(const struct stm32_exti_drv_data *dd, + + static struct + stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, +- u32 bank_idx) ++ u32 bank_idx, ++ struct device_node *node) + { + const struct stm32_exti_bank *stm32_bank; + struct stm32_exti_chip_data *chip_data; +@@ -758,7 +729,7 @@ stm32_exti_chip_data *stm32_exti_chip_init(struct stm32_exti_host_data *h_data, + writel_relaxed(0, base + stm32_bank->imr_ofst); + writel_relaxed(0, base + stm32_bank->emr_ofst); + +- pr_info("%pOF: bank%d\n", h_data->node, bank_idx); ++ pr_info("%pOF: bank%d\n", node, bank_idx); + + return chip_data; + } +@@ -798,7 +769,7 @@ static int __init stm32_exti_init(const struct stm32_exti_drv_data *drv_data, + struct stm32_exti_chip_data *chip_data; + + stm32_bank = drv_data->exti_banks[i]; +- chip_data = stm32_exti_chip_init(host_data, i); ++ chip_data = stm32_exti_chip_init(host_data, i, node); + + gc = irq_get_domain_generic_chip(domain, i * IRQS_PER_BANK); + +@@ -880,7 +851,7 @@ __init stm32_exti_hierarchy_init(const struct stm32_exti_drv_data *drv_data, + return -ENOMEM; + + for (i = 0; i < drv_data->bank_nr; i++) +- stm32_exti_chip_init(host_data, i); ++ stm32_exti_chip_init(host_data, i, node); + + domain = irq_domain_add_hierarchy(parent_domain, 0, + drv_data->bank_nr * IRQS_PER_BANK, +@@ -938,6 +909,71 @@ __init stm32_exti_hierarchy_init(const struct stm32_exti_drv_data *drv_data, + return ret; + } + ++/* Note : stm32_exti_probe() is called after stm32*_exti_of_init() */ ++static int stm32_exti_probe(struct platform_device *pdev) ++{ ++ int id, ret = 0; ++ ++ id = of_hwspin_lock_get_id(pdev->dev.of_node, 0); ++ ++ if (id == -EPROBE_DEFER) ++ /* hwspinlock framework not ready */ ++ return -EPROBE_DEFER; ++ ++ if (id == -ENOENT) ++ /* no hwspinlock defined (not an error, it is optional) */ ++ return 0; ++ ++ if (id >= 0) { ++ stm32_host_data->hwlock = hwspin_lock_request_specific(id); ++ if (!stm32_host_data->hwlock) { ++ dev_err(&pdev->dev, "Failed to request hwspinlock\n"); ++ ret = -EINVAL; ++ } ++ } else { ++ dev_err(&pdev->dev, "Failed to get hwspinlock\n"); ++ ret = id; ++ } ++ ++ return ret; ++} ++ ++static int stm32_exti_remove(struct platform_device *pdev) ++{ ++ if (stm32_host_data->hwlock) ++ return hwspin_lock_free(stm32_host_data->hwlock); ++ ++ return 0; ++} ++ ++static const struct of_device_id stm32_exti_ids[] = { ++ { .compatible = "st,stm32mp1-exti", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, stm32_exti_ids); ++ ++static struct platform_driver stm32_exti_driver = { ++ .probe = stm32_exti_probe, ++ .remove = stm32_exti_remove, ++ .driver = { ++ .name = "stm32_exti", ++ .of_match_table = stm32_exti_ids, ++ }, ++}; ++ ++static int __init stm32_exti_arch_init(void) ++{ ++ return platform_driver_register(&stm32_exti_driver); ++} ++ ++static void __exit stm32_exti_arch_exit(void) ++{ ++ return platform_driver_unregister(&stm32_exti_driver); ++} ++ ++arch_initcall(stm32_exti_arch_init); ++module_exit(stm32_exti_arch_exit); ++ + static int __init stm32f4_exti_of_init(struct device_node *np, + struct device_node *parent) + { +@@ -957,7 +993,15 @@ IRQCHIP_DECLARE(stm32h7_exti, "st,stm32h7-exti", stm32h7_exti_of_init); + static int __init stm32mp1_exti_of_init(struct device_node *np, + struct device_node *parent) + { +- return stm32_exti_hierarchy_init(&stm32mp1_drv_data, np, parent); ++ int ret; ++ ++ ret = stm32_exti_hierarchy_init(&stm32mp1_drv_data, np, parent); ++ ++ /* Clear the OF_POPULATED flag so that stm32_exti_probe can be called */ ++ if (!ret) ++ of_node_clear_flag(np, OF_POPULATED); ++ ++ return ret; + } + + IRQCHIP_DECLARE(stm32mp1_exti, "st,stm32mp1-exti", stm32mp1_exti_of_init); +diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c +index 0bd595a..64f6aec 100644 +--- a/kernel/power/suspend.c ++++ b/kernel/power/suspend.c +@@ -36,7 +36,6 @@ + #include "power.h" + + const char * const pm_labels[] = { +- [PM_SUSPEND_TO_IDLE] = "freeze", + [PM_SUSPEND_STANDBY] = "standby", + [PM_SUSPEND_MEM] = "mem", + }; +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0057-ARM-stm32mp1-r0-rc4-hotfix-w903.3-DEVICETREE.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0057-ARM-stm32mp1-r0-rc4-hotfix-w903.3-DEVICETREE.patch new file mode 100644 index 0000000..311a32c --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0057-ARM-stm32mp1-r0-rc4-hotfix-w903.3-DEVICETREE.patch @@ -0,0 +1,119 @@ +From f30f869d9bb396c6981fc2800149c373d21586de Mon Sep 17 00:00:00 2001 +From: christophe montaud +Date: Wed, 16 Jan 2019 17:52:31 +0100 +Subject: [PATCH 57/58] ARM stm32mp1 r0 rc4 hotfix w903.3 DEVICETREE + +--- + arch/arm/boot/dts/stm32mp157-pinctrl.dtsi | 30 ++++++++++++++++++++++++++++++ + arch/arm/boot/dts/stm32mp157a-dk1.dts | 12 +++++++++++- + arch/arm/boot/dts/stm32mp157c-dk2.dts | 2 +- + arch/arm/boot/dts/stm32mp157c-ed1.dts | 2 +- + 4 files changed, 43 insertions(+), 3 deletions(-) + +diff --git a/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi b/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi +index 183d7ba..474e7e3 100644 +--- a/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi ++++ b/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi +@@ -1413,6 +1413,36 @@ + }; + }; + ++ uart7_pins_a: uart7-0 { ++ pins1 { ++ pinmux = ; /* USART7_TX */ ++ bias-disable; ++ drive-push-pull; ++ slew-rate = <0>; ++ }; ++ pins2 { ++ pinmux = ; /* USART7_RX */ ++ bias-disable; ++ }; ++ }; ++ ++ uart7_idle_pins_a: uart7-idle-0 { ++ pins1 { ++ pinmux = ; /* USART7_TX */ ++ }; ++ pins2 { ++ pinmux = ; /* USART7_RX */ ++ bias-disable; ++ }; ++ }; ++ ++ uart7_sleep_pins_a: uart7-sleep-0 { ++ pins { ++ pinmux = , /* USART7_TX */ ++ ; /* USART7_RX */ ++ }; ++ }; ++ + usart2_pins_a: usart2-0 { + pins1 { + pinmux = , /* USART2_TX */ +diff --git a/arch/arm/boot/dts/stm32mp157a-dk1.dts b/arch/arm/boot/dts/stm32mp157a-dk1.dts +index 28017e4..467c226 100644 +--- a/arch/arm/boot/dts/stm32mp157a-dk1.dts ++++ b/arch/arm/boot/dts/stm32mp157a-dk1.dts +@@ -20,6 +20,8 @@ + aliases { + ethernet0 = ðernet0; + serial0 = &uart4; ++ serial1 = &usart3; ++ serial2 = &uart7; + }; + + chosen { +@@ -310,7 +312,7 @@ + + st,main-control-register = <0x04>; + st,vin-control-register = <0xc0>; +- st,usb-control-register = <0x30>; ++ st,usb-control-register = <0x20>; + + regulators { + compatible = "st,stpmic1-regulators"; +@@ -671,6 +673,14 @@ + status = "okay"; + }; + ++&uart7 { ++ pinctrl-names = "default", "sleep", "idle"; ++ pinctrl-0 = <&uart7_pins_a>; ++ pinctrl-1 = <&uart7_sleep_pins_a>; ++ pinctrl-2 = <&uart7_idle_pins_a>; ++ status = "disabled"; ++}; ++ + &usart3 { + pinctrl-names = "default", "sleep", "idle"; + pinctrl-0 = <&usart3_pins_b>; +diff --git a/arch/arm/boot/dts/stm32mp157c-dk2.dts b/arch/arm/boot/dts/stm32mp157c-dk2.dts +index 340e022..4da15cd 100644 +--- a/arch/arm/boot/dts/stm32mp157c-dk2.dts ++++ b/arch/arm/boot/dts/stm32mp157c-dk2.dts +@@ -14,7 +14,7 @@ + compatible = "st,stm32mp157c-dk2", "st,stm32mp157"; + + aliases { +- serial1 = &usart2; ++ serial3 = &usart2; + }; + + wifi_pwrseq: wifi-pwrseq { +diff --git a/arch/arm/boot/dts/stm32mp157c-ed1.dts b/arch/arm/boot/dts/stm32mp157c-ed1.dts +index cf2750e..6d49f21 100644 +--- a/arch/arm/boot/dts/stm32mp157c-ed1.dts ++++ b/arch/arm/boot/dts/stm32mp157c-ed1.dts +@@ -188,7 +188,7 @@ + + st,main-control-register = <0x04>; + st,vin-control-register = <0xc0>; +- st,usb-control-register = <0x30>; ++ st,usb-control-register = <0x20>; + + regulators { + compatible = "st,stpmic1-regulators"; +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0058-ARM-stm32mp1-r0-rc4-hotfix-w903.3-DEFCONFIG.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0058-ARM-stm32mp1-r0-rc4-hotfix-w903.3-DEFCONFIG.patch new file mode 100644 index 0000000..ba00f78 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0058-ARM-stm32mp1-r0-rc4-hotfix-w903.3-DEFCONFIG.patch @@ -0,0 +1,26 @@ +From 2cfb67b0a4403e68016a50c1302da9a8eeb15a6b Mon Sep 17 00:00:00 2001 +From: christophe montaud +Date: Wed, 16 Jan 2019 17:53:15 +0100 +Subject: [PATCH 58/58] ARM stm32mp1 r0 rc4 hotfix w903.3 DEFCONFIG + +--- + arch/arm/configs/fragment-02-multiv7_addons.config | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/arch/arm/configs/fragment-02-multiv7_addons.config b/arch/arm/configs/fragment-02-multiv7_addons.config +index ddf15a9..7b489e4 100644 +--- a/arch/arm/configs/fragment-02-multiv7_addons.config ++++ b/arch/arm/configs/fragment-02-multiv7_addons.config +@@ -123,9 +123,6 @@ CONFIG_CMA_SIZE_MBYTES=128 + # + # LPDDR & LPDDR2 PCM memory drivers + # +-CONFIG_OF_RESOLVE=y +-CONFIG_OF_OVERLAY=y +-CONFIG_OF_CONFIGFS=y + + # + # Misc devices +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0059-ARM-stm32mp1-r0-rc4-hotfix-w904.3-DRIVERS.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0059-ARM-stm32mp1-r0-rc4-hotfix-w904.3-DRIVERS.patch new file mode 100644 index 0000000..3919425 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0059-ARM-stm32mp1-r0-rc4-hotfix-w904.3-DRIVERS.patch @@ -0,0 +1,927 @@ +From dbfd12db19506dc6d598c11d72aa0fb81c41aa82 Mon Sep 17 00:00:00 2001 +From: christophe montaud +Date: Thu, 24 Jan 2019 10:51:36 +0100 +Subject: [PATCH 59/60] ARM stm32mp1 r0 rc4 hotfix w904.3 DRIVERS + +--- + drivers/dma/stm32-dma.c | 4 + + drivers/gpu/drm/panel/panel-orisetech-otm8009a.c | 64 +++++------ + drivers/gpu/drm/stm/dw_mipi_dsi-stm.c | 51 ++++++--- + drivers/remoteproc/stm32_rproc.c | 21 ++-- + drivers/tty/serial/stm32-usart.c | 8 ++ + drivers/tty/serial/stm32-usart.h | 2 +- + drivers/usb/dwc2/core.h | 29 +++++ + drivers/usb/dwc2/debugfs.c | 1 + + drivers/usb/dwc2/gadget.c | 133 +++++++++++++++++++++-- + drivers/usb/dwc2/hcd.c | 3 - + drivers/usb/dwc2/hcd.h | 2 +- + drivers/usb/dwc2/hcd_queue.c | 19 ++-- + drivers/usb/dwc2/hw.h | 17 +++ + drivers/usb/dwc2/params.c | 18 ++- + 14 files changed, 294 insertions(+), 78 deletions(-) + +diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c +index 1f9d606..5abfa4f 100644 +--- a/drivers/dma/stm32-dma.c ++++ b/drivers/dma/stm32-dma.c +@@ -1242,6 +1242,7 @@ static int stm32_dma_mdma_prep_slave_sg(struct stm32_dma_chan *chan, + dev_err(chan2dev(chan), + "max buf size = %d bytes\n", + chan->sram_size); ++ ret = -EINVAL; + goto free_alloc; + } + } else { +@@ -1939,6 +1940,8 @@ static int stm32_dma_probe(struct platform_device *pdev) + dev_dbg(&pdev->dev, "SRAM pool: %zu KiB\n", + gen_pool_size(dmadev->sram_pool) / 1024); + ++ dma_set_max_seg_size(&pdev->dev, STM32_DMA_ALIGNED_MAX_DATA_ITEMS); ++ + dma_cap_set(DMA_SLAVE, dd->cap_mask); + dma_cap_set(DMA_PRIVATE, dd->cap_mask); + dma_cap_set(DMA_CYCLIC, dd->cap_mask); +@@ -1959,6 +1962,7 @@ static int stm32_dma_probe(struct platform_device *pdev) + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + dd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + dd->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; ++ dd->copy_align = DMAENGINE_ALIGN_32_BYTES; + dd->max_burst = STM32_DMA_MAX_BURST; + dd->descriptor_reuse = true; + dd->dev = &pdev->dev; +diff --git a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c +index a76d03a..ee7486b 100644 +--- a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c ++++ b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c +@@ -254,24 +254,12 @@ static int otm8009a_init_sequence(struct otm8009a *ctx) + static int otm8009a_disable(struct drm_panel *panel) + { + struct otm8009a *ctx = panel_to_otm8009a(panel); +- struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); +- int ret; + + if (!ctx->enabled) + return 0; /* This is not an issue so we return 0 here */ + + backlight_disable(ctx->bl_dev); + +- ret = mipi_dsi_dcs_set_display_off(dsi); +- if (ret) +- return ret; +- +- ret = mipi_dsi_dcs_enter_sleep_mode(dsi); +- if (ret) +- return ret; +- +- msleep(120); +- + ctx->enabled = false; + + return 0; +@@ -280,16 +268,23 @@ static int otm8009a_disable(struct drm_panel *panel) + static int otm8009a_unprepare(struct drm_panel *panel) + { + struct otm8009a *ctx = panel_to_otm8009a(panel); ++ struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); ++ int ret; + + if (!ctx->prepared) + return 0; + +- if (ctx->reset_gpio) { +- gpiod_set_value_cansleep(ctx->reset_gpio, 1); +- msleep(20); +- } ++ ret = mipi_dsi_dcs_set_display_off(dsi); ++ if (ret) ++ return ret; + +- regulator_disable(ctx->supply); ++ msleep(10); ++ ++ ret = mipi_dsi_dcs_enter_sleep_mode(dsi); ++ if (ret) ++ return ret; ++ ++ msleep(120); + + ctx->prepared = false; + +@@ -304,20 +299,6 @@ static int otm8009a_prepare(struct drm_panel *panel) + if (ctx->prepared) + return 0; + +- ret = regulator_enable(ctx->supply); +- if (ret < 0) { +- DRM_ERROR("failed to enable supply: %d\n", ret); +- return ret; +- } +- +- if (ctx->reset_gpio) { +- gpiod_set_value_cansleep(ctx->reset_gpio, 0); +- gpiod_set_value_cansleep(ctx->reset_gpio, 1); +- msleep(20); +- gpiod_set_value_cansleep(ctx->reset_gpio, 0); +- msleep(100); +- } +- + ret = otm8009a_init_sequence(ctx); + if (ret) + return ret; +@@ -475,6 +456,20 @@ static int otm8009a_probe(struct mipi_dsi_device *dsi) + return ret; + } + ++ ret = regulator_enable(ctx->supply); ++ if (ret < 0) { ++ DRM_ERROR("failed to enable supply: %d\n", ret); ++ return ret; ++ } ++ ++ if (ctx->reset_gpio) { ++ gpiod_set_value_cansleep(ctx->reset_gpio, 0); ++ gpiod_set_value_cansleep(ctx->reset_gpio, 1); ++ msleep(20); ++ gpiod_set_value_cansleep(ctx->reset_gpio, 0); ++ msleep(100); ++ } ++ + return 0; + } + +@@ -485,6 +480,13 @@ static int otm8009a_remove(struct mipi_dsi_device *dsi) + mipi_dsi_detach(dsi); + drm_panel_remove(&ctx->panel); + ++ if (ctx->reset_gpio) { ++ gpiod_set_value_cansleep(ctx->reset_gpio, 1); ++ msleep(20); ++ } ++ ++ regulator_disable(ctx->supply); ++ + return 0; + } + +diff --git a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c +index a373651..7b37f97 100644 +--- a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c ++++ b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c +@@ -305,6 +305,7 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; + struct dw_mipi_dsi_stm *dsi; ++ struct clk *pclk; + struct resource *res; + int ret; + +@@ -335,23 +336,13 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev) + if (IS_ERR(dsi->pllref_clk)) { + ret = PTR_ERR(dsi->pllref_clk); + dev_err(dev, "Unable to get pll reference clock: %d\n", ret); +- regulator_disable(dsi->vdd_supply); +- return ret; ++ goto err_clk_get; + } + + ret = clk_prepare_enable(dsi->pllref_clk); + if (ret) { + dev_err(dev, "%s: Failed to enable pllref_clk\n", __func__); +- regulator_disable(dsi->vdd_supply); +- return ret; +- } +- +- dsi->hw_version = dsi_read(dsi, DSI_VERSION) & VERSION; +- if (dsi->hw_version != HWVER_130 && dsi->hw_version != HWVER_131) { +- dev_err(dev, "bad dsi hardware version\n"); +- clk_disable_unprepare(dsi->pllref_clk); +- regulator_disable(dsi->vdd_supply); +- return -ENODEV; ++ goto err_clk_get; + } + + dw_mipi_dsi_stm_plat_data.base = dsi->base; +@@ -361,13 +352,43 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev) + + dsi->dsi = dw_mipi_dsi_probe(pdev, &dw_mipi_dsi_stm_plat_data); + if (IS_ERR(dsi->dsi)) { ++ ret = PTR_ERR(dsi->dsi); + DRM_ERROR("Failed to initialize mipi dsi host\n"); +- regulator_disable(dsi->vdd_supply); +- clk_disable_unprepare(dsi->pllref_clk); +- return PTR_ERR(dsi->dsi); ++ goto err_probe; ++ } ++ ++ pclk = devm_clk_get(dev, "pclk"); ++ if (IS_ERR(pclk)) { ++ ret = PTR_ERR(pclk); ++ dev_err(dev, "Unable to get peripheral clock: %d\n", ret); ++ goto err_probe; ++ } ++ ++ ret = clk_prepare_enable(pclk); ++ if (ret) { ++ dev_err(dev, "%s: Failed to enable peripheral clk\n", __func__); ++ goto err_probe; ++ } ++ ++ dsi->hw_version = dsi_read(dsi, DSI_VERSION) & VERSION; ++ clk_disable_unprepare(pclk); ++ ++ if (dsi->hw_version != HWVER_130 && dsi->hw_version != HWVER_131) { ++ ret = -ENODEV; ++ dev_err(dev, "bad dsi hardware version\n"); ++ goto err_probe; + } + + return 0; ++ ++err_probe: ++ clk_disable_unprepare(dsi->pllref_clk); ++ ++err_clk_get: ++ regulator_disable(dsi->vdd_supply); ++ ++ return ret; ++ + } + + static int dw_mipi_dsi_stm_remove(struct platform_device *pdev) +diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c +index 1d2be11..1827c25 100644 +--- a/drivers/remoteproc/stm32_rproc.c ++++ b/drivers/remoteproc/stm32_rproc.c +@@ -104,10 +104,12 @@ static int stm32_rproc_da_to_pa(struct rproc *rproc, u64 da, phys_addr_t *pa) + da >= p_mem->dev_addr + p_mem->size) + continue; + *pa = da - p_mem->dev_addr + p_mem->bus_addr; +- dev_err(rproc->dev.parent, "da %llx to pa %#x\n", da, *pa); ++ dev_dbg(rproc->dev.parent, "da %llx to pa %#x\n", da, *pa); + return 0; + } + ++ dev_err(rproc->dev.parent, "can't translate da %llx\n", da); ++ + return -EINVAL; + } + +@@ -657,20 +659,21 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev) + + if (of_property_read_bool(np, "early-booted")) { + rproc->early_boot = true; +- + err = of_property_read_u32(np, "rsc-address", &rsc_da); +- if (!err) { +- err = of_property_read_u32(np, "rsc-size", +- &ddata->rsc_len); ++ if (err) ++ /* no optional rsc table found */ ++ return 0; + +- if (err) { +- dev_err(dev, "resource table size required as address defined\n"); +- return err; +- } ++ err = of_property_read_u32(np, "rsc-size", &ddata->rsc_len); ++ if (err) { ++ dev_err(dev, "resource table size required as address defined\n"); ++ return err; + } ++ + err = stm32_rproc_da_to_pa(rproc, rsc_da, &rsc_pa); + if (err) + return err; ++ + ddata->rsc_va = ioremap_wc(rsc_pa, ddata->rsc_len); + if (IS_ERR_OR_NULL(ddata->rsc_va)) { + dev_err(dev, "Unable to map memory region: %pa+%zx\n", +diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c +index d606eb5..6fc57fc 100644 +--- a/drivers/tty/serial/stm32-usart.c ++++ b/drivers/tty/serial/stm32-usart.c +@@ -250,8 +250,16 @@ static void stm32_receive_chars(struct uart_port *port, bool threaded) + ofs->icr); + port->icount.overrun++; + } else if (sr & USART_SR_PE) { ++ if (ofs->icr != UNDEF_REG) ++ writel_relaxed(USART_ICR_PECF, ++ port->membase + ++ ofs->icr); + port->icount.parity++; + } else if (sr & USART_SR_FE) { ++ if (ofs->icr != UNDEF_REG) ++ writel_relaxed(USART_ICR_FECF, ++ port->membase + ++ ofs->icr); + port->icount.frame++; + } + +diff --git a/drivers/tty/serial/stm32-usart.h b/drivers/tty/serial/stm32-usart.h +index f1f5c1c..53d7e7b 100644 +--- a/drivers/tty/serial/stm32-usart.h ++++ b/drivers/tty/serial/stm32-usart.h +@@ -241,7 +241,7 @@ struct stm32_usart_info stm32h7_info = { + + /* USART_ICR */ + #define USART_ICR_PECF BIT(0) /* F7 */ +-#define USART_ICR_FFECF BIT(1) /* F7 */ ++#define USART_ICR_FECF BIT(1) /* F7 */ + #define USART_ICR_NCF BIT(2) /* F7 */ + #define USART_ICR_ORECF BIT(3) /* F7 */ + #define USART_ICR_IDLECF BIT(4) /* F7 */ +diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h +index 4c1736f..3d8fa58 100644 +--- a/drivers/usb/dwc2/core.h ++++ b/drivers/usb/dwc2/core.h +@@ -393,6 +393,20 @@ enum dwc2_ep0_state { + * 0 - No + * 1 - Yes + * @hird_threshold: Value of BESL or HIRD Threshold. ++ * @ref_clk_per: Indicates in terms of pico seconds the period ++ * of ref_clk. ++ * 62500 - 16MHz ++ * 58823 - 17MHz ++ * 52083 - 19.2MHz ++ * 50000 - 20MHz ++ * 41666 - 24MHz ++ * 33333 - 30MHz (default) ++ * 25000 - 40MHz ++ * @sof_cnt_wkup_alert: Indicates in term of number of SOF's after which ++ * the controller should generate an interrupt if the ++ * device had been in L1 state until that period. ++ * This is used by SW to initiate Remote WakeUp in the ++ * controller so as to sync to the uF number from the host. + * @activate_stm_fs_transceiver: Activate internal transceiver using GGPIO + * register. + * 0 - Deactivate the transceiver (default) +@@ -426,6 +440,9 @@ enum dwc2_ep0_state { + * back to DWC2_SPEED_PARAM_HIGH while device is gone. + * 0 - No (default) + * 1 - Yes ++ * @service_interval: Enable service interval based scheduling. ++ * 0 - No ++ * 1 - Yes + * + * The following parameters may be specified when starting the module. These + * parameters define how the DWC_otg controller should be configured. A +@@ -471,6 +488,7 @@ struct dwc2_core_params { + bool lpm_clock_gating; + bool besl; + bool hird_threshold_en; ++ bool service_interval; + u8 hird_threshold; + bool activate_stm_fs_transceiver; + bool activate_stm_id_vb_detection; +@@ -479,6 +497,10 @@ struct dwc2_core_params { + u32 max_transfer_size; + u32 ahbcfg; + ++ /* GREFCLK parameters */ ++ u32 ref_clk_per; ++ u16 sof_cnt_wkup_alert; ++ + /* Host parameters */ + bool host_dma; + bool dma_desc_enable; +@@ -618,6 +640,10 @@ struct dwc2_core_params { + * FIFO sizing is enabled 16 to 32768 + * Actual maximum value is autodetected and also + * the default. ++ * @service_interval_mode: For enabling service interval based scheduling in the ++ * controller. ++ * 0 - Disable ++ * 1 - Enable + */ + struct dwc2_hw_params { + unsigned op_mode:3; +@@ -648,6 +674,7 @@ struct dwc2_hw_params { + unsigned utmi_phy_data_width:2; + unsigned lpm_mode:1; + unsigned ipg_isoc_en:1; ++ unsigned service_interval_mode:1; + u32 snpsid; + u32 dev_ep_dirs; + u32 g_tx_fifo_size[MAX_EPS_CHANNELS]; +@@ -1372,6 +1399,7 @@ int dwc2_hsotg_tx_fifo_count(struct dwc2_hsotg *hsotg); + int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg); + int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg); + void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg); ++void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg); + #else + static inline int dwc2_hsotg_remove(struct dwc2_hsotg *dwc2) + { return 0; } +@@ -1406,6 +1434,7 @@ static inline int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg) + static inline int dwc2_hsotg_tx_fifo_average_depth(struct dwc2_hsotg *hsotg) + { return 0; } + static inline void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) {} ++static inline void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg) {} + #endif + + #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) +diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c +index 22d015b..7f62f4c 100644 +--- a/drivers/usb/dwc2/debugfs.c ++++ b/drivers/usb/dwc2/debugfs.c +@@ -701,6 +701,7 @@ static int params_show(struct seq_file *seq, void *v) + print_param(seq, p, besl); + print_param(seq, p, hird_threshold_en); + print_param(seq, p, hird_threshold); ++ print_param(seq, p, service_interval); + print_param(seq, p, host_dma); + print_param(seq, p, g_dma); + print_param(seq, p, g_dma_desc); +diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c +index 220c0f9..55ef3cc 100644 +--- a/drivers/usb/dwc2/gadget.c ++++ b/drivers/usb/dwc2/gadget.c +@@ -123,6 +123,24 @@ static inline void dwc2_gadget_incr_frame_num(struct dwc2_hsotg_ep *hs_ep) + } + + /** ++ * dwc2_gadget_dec_frame_num_by_one - Decrements the targeted frame number ++ * by one. ++ * @hs_ep: The endpoint. ++ * ++ * This function used in service interval based scheduling flow to calculate ++ * descriptor frame number filed value. For service interval mode frame ++ * number in descriptor should point to last (u)frame in the interval. ++ * ++ */ ++static inline void dwc2_gadget_dec_frame_num_by_one(struct dwc2_hsotg_ep *hs_ep) ++{ ++ if (hs_ep->target_frame) ++ hs_ep->target_frame -= 1; ++ else ++ hs_ep->target_frame = DSTS_SOFFN_LIMIT; ++} ++ ++/** + * dwc2_hsotg_en_gsint - enable one or more of the general interrupt + * @hsotg: The device state + * @ints: A bitmask of the interrupts to enable +@@ -228,6 +246,27 @@ int dwc2_hsotg_tx_fifo_total_depth(struct dwc2_hsotg *hsotg) + } + + /** ++ * dwc2_gadget_wkup_alert_handler - Handler for WKUP_ALERT interrupt ++ * ++ * @hsotg: Programming view of the DWC_otg controller ++ * ++ */ ++static void dwc2_gadget_wkup_alert_handler(struct dwc2_hsotg *hsotg) ++{ ++ u32 gintsts2; ++ u32 gintmsk2; ++ ++ gintsts2 = dwc2_readl(hsotg, GINTSTS2); ++ gintmsk2 = dwc2_readl(hsotg, GINTMSK2); ++ ++ if (gintsts2 & GINTSTS2_WKUP_ALERT_INT) { ++ dev_dbg(hsotg->dev, "%s: Wkup_Alert_Int\n", __func__); ++ dwc2_set_bit(hsotg, GINTSTS2, GINTSTS2_WKUP_ALERT_INT); ++ dwc2_set_bit(hsotg, DCTL, DCTL_RMTWKUPSIG); ++ } ++} ++ ++/** + * dwc2_hsotg_tx_fifo_average_depth - returns average depth of device mode + * TX FIFOs + * +@@ -2812,6 +2851,23 @@ static void dwc2_gadget_handle_nak(struct dwc2_hsotg_ep *hs_ep) + if (using_desc_dma(hsotg)) { + hs_ep->target_frame = hsotg->frame_number; + dwc2_gadget_incr_frame_num(hs_ep); ++ ++ /* In service interval mode target_frame must ++ * be set to last (u)frame of the service interval. ++ */ ++ if (hsotg->params.service_interval) { ++ /* Set target_frame to the first (u)frame of ++ * the service interval ++ */ ++ hs_ep->target_frame &= ~hs_ep->interval + 1; ++ ++ /* Set target_frame to the last (u)frame of ++ * the service interval ++ */ ++ dwc2_gadget_incr_frame_num(hs_ep); ++ dwc2_gadget_dec_frame_num_by_one(hs_ep); ++ } ++ + dwc2_gadget_start_isoc_ddma(hs_ep); + return; + } +@@ -3127,6 +3183,7 @@ void dwc2_hsotg_disconnect(struct dwc2_hsotg *hsotg) + hsotg->connected = 0; + hsotg->test_mode = 0; + ++ /* all endpoints should be shutdown */ + for (ep = 0; ep < hsotg->num_of_eps; ep++) { + if (hsotg->eps_in[ep]) + kill_all_requests(hsotg, hsotg->eps_in[ep], +@@ -3177,6 +3234,7 @@ static void dwc2_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic) + GINTSTS_PTXFEMP | \ + GINTSTS_RXFLVL) + ++static int dwc2_hsotg_ep_disable(struct usb_ep *ep); + /** + * dwc2_hsotg_core_init - issue softreset to the core + * @hsotg: The device state +@@ -3191,13 +3249,23 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, + u32 val; + u32 usbcfg; + u32 dcfg = 0; ++ int ep; + + /* Kill any ep0 requests as controller will be reinitialized */ + kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET); + +- if (!is_usb_reset) ++ if (!is_usb_reset) { + if (dwc2_core_reset(hsotg, true)) + return; ++ } else { ++ /* all endpoints should be shutdown */ ++ for (ep = 1; ep < hsotg->num_of_eps; ep++) { ++ if (hsotg->eps_in[ep]) ++ dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep); ++ if (hsotg->eps_out[ep]) ++ dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep); ++ } ++ } + + /* + * we must now enable ep0 ready for host detection and then +@@ -3312,6 +3380,10 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, + dwc2_set_bit(hsotg, DIEPMSK, DIEPMSK_BNAININTRMSK); + } + ++ /* Enable Service Interval mode if supported */ ++ if (using_desc_dma(hsotg) && hsotg->params.service_interval) ++ dwc2_set_bit(hsotg, DCTL, DCTL_SERVICE_INTERVAL_SUPPORTED); ++ + dwc2_writel(hsotg, 0, DAINTMSK); + + dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n", +@@ -3368,6 +3440,10 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, + /* configure the core to support LPM */ + dwc2_gadget_init_lpm(hsotg); + ++ /* program GREFCLK register if needed */ ++ if (using_desc_dma(hsotg) && hsotg->params.service_interval) ++ dwc2_gadget_program_ref_clk(hsotg); ++ + /* must be at-least 3ms to allow bus to see disconnect */ + mdelay(3); + +@@ -3676,6 +3752,10 @@ static irqreturn_t dwc2_hsotg_irq(int irq, void *pw) + if (gintsts & IRQ_RETRY_MASK && --retry_count > 0) + goto irq_retry; + ++ /* Check WKUP_ALERT interrupt*/ ++ if (hsotg->params.service_interval) ++ dwc2_gadget_wkup_alert_handler(hsotg); ++ + spin_unlock(&hsotg->lock); + + return IRQ_HANDLED; +@@ -3990,7 +4070,6 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep) + struct dwc2_hsotg *hsotg = hs_ep->parent; + int dir_in = hs_ep->dir_in; + int index = hs_ep->index; +- unsigned long flags; + u32 epctrl_reg; + u32 ctrl; + +@@ -4008,8 +4087,6 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep) + + epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index); + +- spin_lock_irqsave(&hsotg->lock, flags); +- + ctrl = dwc2_readl(hsotg, epctrl_reg); + + if (ctrl & DXEPCTL_EPENA) +@@ -4032,10 +4109,22 @@ static int dwc2_hsotg_ep_disable(struct usb_ep *ep) + hs_ep->fifo_index = 0; + hs_ep->fifo_size = 0; + +- spin_unlock_irqrestore(&hsotg->lock, flags); + return 0; + } + ++static int dwc2_hsotg_ep_disable_lock(struct usb_ep *ep) ++{ ++ struct dwc2_hsotg_ep *hs_ep = our_ep(ep); ++ struct dwc2_hsotg *hsotg = hs_ep->parent; ++ unsigned long flags; ++ int ret; ++ ++ spin_lock_irqsave(&hsotg->lock, flags); ++ ret = dwc2_hsotg_ep_disable(ep); ++ spin_unlock_irqrestore(&hsotg->lock, flags); ++ return ret; ++} ++ + /** + * on_list - check request is on the given endpoint + * @ep: The endpoint to check. +@@ -4183,7 +4272,7 @@ static int dwc2_hsotg_ep_sethalt_lock(struct usb_ep *ep, int value) + + static const struct usb_ep_ops dwc2_hsotg_ep_ops = { + .enable = dwc2_hsotg_ep_enable, +- .disable = dwc2_hsotg_ep_disable, ++ .disable = dwc2_hsotg_ep_disable_lock, + .alloc_request = dwc2_hsotg_ep_alloc_request, + .free_request = dwc2_hsotg_ep_free_request, + .queue = dwc2_hsotg_ep_queue_lock, +@@ -4323,9 +4412,9 @@ static int dwc2_hsotg_udc_stop(struct usb_gadget *gadget) + /* all endpoints should be shutdown */ + for (ep = 1; ep < hsotg->num_of_eps; ep++) { + if (hsotg->eps_in[ep]) +- dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep); ++ dwc2_hsotg_ep_disable_lock(&hsotg->eps_in[ep]->ep); + if (hsotg->eps_out[ep]) +- dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep); ++ dwc2_hsotg_ep_disable_lock(&hsotg->eps_out[ep]->ep); + } + + spin_lock_irqsave(&hsotg->lock, flags); +@@ -4773,9 +4862,9 @@ int dwc2_hsotg_suspend(struct dwc2_hsotg *hsotg) + + for (ep = 0; ep < hsotg->num_of_eps; ep++) { + if (hsotg->eps_in[ep]) +- dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep); ++ dwc2_hsotg_ep_disable_lock(&hsotg->eps_in[ep]->ep); + if (hsotg->eps_out[ep]) +- dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep); ++ dwc2_hsotg_ep_disable_lock(&hsotg->eps_out[ep]->ep); + } + } + +@@ -4942,8 +5031,32 @@ void dwc2_gadget_init_lpm(struct dwc2_hsotg *hsotg) + val |= hsotg->params.lpm_clock_gating ? GLPMCFG_ENBLSLPM : 0; + val |= hsotg->params.hird_threshold << GLPMCFG_HIRD_THRES_SHIFT; + val |= hsotg->params.besl ? GLPMCFG_ENBESL : 0; ++ val |= GLPMCFG_LPM_ACCEPT_CTRL_ISOC; + dwc2_writel(hsotg, val, GLPMCFG); + dev_dbg(hsotg->dev, "GLPMCFG=0x%08x\n", dwc2_readl(hsotg, GLPMCFG)); ++ ++ /* Unmask WKUP_ALERT Interrupt */ ++ if (hsotg->params.service_interval) ++ dwc2_set_bit(hsotg, GINTMSK2, GINTMSK2_WKUP_ALERT_INT_MSK); ++} ++ ++/** ++ * dwc2_gadget_program_ref_clk - Program GREFCLK register in device mode ++ * ++ * @hsotg: Programming view of DWC_otg controller ++ * ++ */ ++void dwc2_gadget_program_ref_clk(struct dwc2_hsotg *hsotg) ++{ ++ u32 val = 0; ++ ++ val |= GREFCLK_REF_CLK_MODE; ++ val |= hsotg->params.ref_clk_per << GREFCLK_REFCLKPER_SHIFT; ++ val |= hsotg->params.sof_cnt_wkup_alert << ++ GREFCLK_SOF_CNT_WKUP_ALERT_SHIFT; ++ ++ dwc2_writel(hsotg, val, GREFCLK); ++ dev_dbg(hsotg->dev, "GREFCLK=0x%08x\n", dwc2_readl(hsotg, GREFCLK)); + } + + /** +diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c +index 12fa6c0..7f128b1 100644 +--- a/drivers/usb/dwc2/hcd.c ++++ b/drivers/usb/dwc2/hcd.c +@@ -1309,14 +1309,11 @@ static void dwc2_hc_write_packet(struct dwc2_hsotg *hsotg, + u32 remaining_count; + u32 byte_count; + u32 dword_count; +- u32 __iomem *data_fifo; + u32 *data_buf = (u32 *)chan->xfer_buf; + + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "%s()\n", __func__); + +- data_fifo = (u32 __iomem *)(hsotg->regs + HCFIFO(chan->hc_num)); +- + remaining_count = chan->xfer_len - chan->xfer_count; + if (remaining_count > chan->max_packet) + byte_count = chan->max_packet; +diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h +index 3f9bccc..c089ffa 100644 +--- a/drivers/usb/dwc2/hcd.h ++++ b/drivers/usb/dwc2/hcd.h +@@ -366,7 +366,7 @@ struct dwc2_qh { + u32 desc_list_sz; + u32 *n_bytes; + struct timer_list unreserve_timer; +- struct timer_list wait_timer; ++ struct hrtimer wait_timer; + struct dwc2_tt *dwc_tt; + int ttport; + unsigned tt_buffer_dirty:1; +diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c +index 40839591..ea3aa64 100644 +--- a/drivers/usb/dwc2/hcd_queue.c ++++ b/drivers/usb/dwc2/hcd_queue.c +@@ -59,7 +59,7 @@ + #define DWC2_UNRESERVE_DELAY (msecs_to_jiffies(5)) + + /* If we get a NAK, wait this long before retrying */ +-#define DWC2_RETRY_WAIT_DELAY (msecs_to_jiffies(1)) ++#define DWC2_RETRY_WAIT_DELAY 1*1E6L + + /** + * dwc2_periodic_channel_available() - Checks that a channel is available for a +@@ -1464,10 +1464,12 @@ static void dwc2_deschedule_periodic(struct dwc2_hsotg *hsotg, + * qh back to the "inactive" list, then queues transactions. + * + * @t: Pointer to wait_timer in a qh. ++ * ++ * Return: HRTIMER_NORESTART to not automatically restart this timer. + */ +-static void dwc2_wait_timer_fn(struct timer_list *t) ++static enum hrtimer_restart dwc2_wait_timer_fn(struct hrtimer *t) + { +- struct dwc2_qh *qh = from_timer(qh, t, wait_timer); ++ struct dwc2_qh *qh = container_of(t, struct dwc2_qh, wait_timer); + struct dwc2_hsotg *hsotg = qh->hsotg; + unsigned long flags; + +@@ -1491,6 +1493,7 @@ static void dwc2_wait_timer_fn(struct timer_list *t) + } + + spin_unlock_irqrestore(&hsotg->lock, flags); ++ return HRTIMER_NORESTART; + } + + /** +@@ -1521,7 +1524,8 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, + /* Initialize QH */ + qh->hsotg = hsotg; + timer_setup(&qh->unreserve_timer, dwc2_unreserve_timer_fn, 0); +- timer_setup(&qh->wait_timer, dwc2_wait_timer_fn, 0); ++ hrtimer_init(&qh->wait_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ qh->wait_timer.function = &dwc2_wait_timer_fn; + qh->ep_type = ep_type; + qh->ep_is_in = ep_is_in; + +@@ -1690,7 +1694,7 @@ void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) + * won't do anything anyway, but we want it to finish before we free + * memory. + */ +- del_timer_sync(&qh->wait_timer); ++ hrtimer_cancel(&qh->wait_timer); + + dwc2_host_put_tt_info(hsotg, qh->dwc_tt); + +@@ -1716,6 +1720,7 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) + { + int status; + u32 intr_mask; ++ ktime_t delay; + + if (dbg_qh(qh)) + dev_vdbg(hsotg->dev, "%s()\n", __func__); +@@ -1734,8 +1739,8 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) + list_add_tail(&qh->qh_list_entry, + &hsotg->non_periodic_sched_waiting); + qh->wait_timer_cancel = false; +- mod_timer(&qh->wait_timer, +- jiffies + DWC2_RETRY_WAIT_DELAY + 1); ++ delay = ktime_set(0, DWC2_RETRY_WAIT_DELAY); ++ hrtimer_start(&qh->wait_timer, delay, HRTIMER_MODE_REL); + } else { + list_add_tail(&qh->qh_list_entry, + &hsotg->non_periodic_sched_inactive); +diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h +index 31f8c60..4980ecb 100644 +--- a/drivers/usb/dwc2/hw.h ++++ b/drivers/usb/dwc2/hw.h +@@ -316,6 +316,7 @@ + #define GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT 14 + #define GHWCFG4_ACG_SUPPORTED BIT(12) + #define GHWCFG4_IPG_ISOC_SUPPORTED BIT(11) ++#define GHWCFG4_SERVICE_INTERVAL_SUPPORTED BIT(10) + #define GHWCFG4_UTMI_PHY_DATA_WIDTH_8 0 + #define GHWCFG4_UTMI_PHY_DATA_WIDTH_16 1 + #define GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16 2 +@@ -336,6 +337,8 @@ + #define GLPMCFG_SNDLPM BIT(24) + #define GLPMCFG_RETRY_CNT_MASK (0x7 << 21) + #define GLPMCFG_RETRY_CNT_SHIFT 21 ++#define GLPMCFG_LPM_ACCEPT_CTRL_CONTROL BIT(21) ++#define GLPMCFG_LPM_ACCEPT_CTRL_ISOC BIT(22) + #define GLPMCFG_LPM_CHNL_INDX_MASK (0xf << 17) + #define GLPMCFG_LPM_CHNL_INDX_SHIFT 17 + #define GLPMCFG_L1RESUMEOK BIT(16) +@@ -408,6 +411,19 @@ + #define ADPCTL_PRB_DSCHRG_MASK (0x3 << 0) + #define ADPCTL_PRB_DSCHRG_SHIFT 0 + ++#define GREFCLK HSOTG_REG(0x0064) ++#define GREFCLK_REFCLKPER_MASK (0x1ffff << 15) ++#define GREFCLK_REFCLKPER_SHIFT 15 ++#define GREFCLK_REF_CLK_MODE BIT(14) ++#define GREFCLK_SOF_CNT_WKUP_ALERT_MASK (0x3ff) ++#define GREFCLK_SOF_CNT_WKUP_ALERT_SHIFT 0 ++ ++#define GINTMSK2 HSOTG_REG(0x0068) ++#define GINTMSK2_WKUP_ALERT_INT_MSK BIT(0) ++ ++#define GINTSTS2 HSOTG_REG(0x006c) ++#define GINTSTS2_WKUP_ALERT_INT BIT(0) ++ + #define HPTXFSIZ HSOTG_REG(0x100) + /* Use FIFOSIZE_* constants to access this register */ + +@@ -447,6 +463,7 @@ + #define DCFG_DEVSPD_FS48 3 + + #define DCTL HSOTG_REG(0x804) ++#define DCTL_SERVICE_INTERVAL_SUPPORTED BIT(19) + #define DCTL_PWRONPRGDONE BIT(11) + #define DCTL_CGOUTNAK BIT(10) + #define DCTL_SGOUTNAK BIT(9) +diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c +index 7fef905..bdcabb1 100644 +--- a/drivers/usb/dwc2/params.c ++++ b/drivers/usb/dwc2/params.c +@@ -71,6 +71,13 @@ static void dwc2_set_his_params(struct dwc2_hsotg *hsotg) + p->power_down = false; + } + ++static void dwc2_set_s3c6400_params(struct dwc2_hsotg *hsotg) ++{ ++ struct dwc2_core_params *p = &hsotg->params; ++ ++ p->power_down = 0; ++} ++ + static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg) + { + struct dwc2_core_params *p = &hsotg->params; +@@ -81,6 +88,7 @@ static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg) + p->host_perio_tx_fifo_size = 256; + p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << + GAHBCFG_HBSTLEN_SHIFT; ++ p->power_down = 0; + } + + static void dwc2_set_ltq_params(struct dwc2_hsotg *hsotg) +@@ -110,6 +118,7 @@ static void dwc2_set_amlogic_params(struct dwc2_hsotg *hsotg) + p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI; + p->ahbcfg = GAHBCFG_HBSTLEN_INCR8 << + GAHBCFG_HBSTLEN_SHIFT; ++ p->power_down = DWC2_POWER_DOWN_PARAM_NONE; + } + + static void dwc2_set_amcc_params(struct dwc2_hsotg *hsotg) +@@ -177,7 +186,8 @@ const struct of_device_id dwc2_of_match_table[] = { + { .compatible = "lantiq,arx100-usb", .data = dwc2_set_ltq_params }, + { .compatible = "lantiq,xrx200-usb", .data = dwc2_set_ltq_params }, + { .compatible = "snps,dwc2" }, +- { .compatible = "samsung,s3c6400-hsotg" }, ++ { .compatible = "samsung,s3c6400-hsotg", ++ .data = dwc2_set_s3c6400_params }, + { .compatible = "amlogic,meson8-usb", + .data = dwc2_set_amlogic_params }, + { .compatible = "amlogic,meson8b-usb", +@@ -330,9 +340,12 @@ static void dwc2_set_default_params(struct dwc2_hsotg *hsotg) + p->hird_threshold_en = true; + p->hird_threshold = 4; + p->ipg_isoc_en = false; ++ p->service_interval = false; + p->max_packet_count = hw->max_packet_count; + p->max_transfer_size = hw->max_transfer_size; + p->ahbcfg = GAHBCFG_HBSTLEN_INCR << GAHBCFG_HBSTLEN_SHIFT; ++ p->ref_clk_per = 33333; ++ p->sof_cnt_wkup_alert = 100; + + if ((hsotg->dr_mode == USB_DR_MODE_HOST) || + (hsotg->dr_mode == USB_DR_MODE_OTG)) { +@@ -628,6 +641,7 @@ static void dwc2_check_params(struct dwc2_hsotg *hsotg) + CHECK_BOOL(besl, (hsotg->hw_params.snpsid >= DWC2_CORE_REV_3_00a)); + CHECK_BOOL(hird_threshold_en, hsotg->params.lpm); + CHECK_RANGE(hird_threshold, 0, hsotg->params.besl ? 12 : 7, 0); ++ CHECK_BOOL(service_interval, hw->service_interval_mode); + CHECK_RANGE(max_packet_count, + 15, hw->max_packet_count, + hw->max_packet_count); +@@ -816,6 +830,8 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) + GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT; + hw->acg_enable = !!(hwcfg4 & GHWCFG4_ACG_SUPPORTED); + hw->ipg_isoc_en = !!(hwcfg4 & GHWCFG4_IPG_ISOC_SUPPORTED); ++ hw->service_interval_mode = !!(hwcfg4 & ++ GHWCFG4_SERVICE_INTERVAL_SUPPORTED); + + /* fifo sizes */ + hw->rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >> +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0060-ARM-stm32mp1-r0-rc4-hotfix-w904.3-DEVICETREE.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0060-ARM-stm32mp1-r0-rc4-hotfix-w904.3-DEVICETREE.patch new file mode 100644 index 0000000..5411f00 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0060-ARM-stm32mp1-r0-rc4-hotfix-w904.3-DEVICETREE.patch @@ -0,0 +1,186 @@ +From 56b9c52ec61df693bd997e3ef1465230a6cb1834 Mon Sep 17 00:00:00 2001 +From: christophe montaud +Date: Thu, 24 Jan 2019 10:52:11 +0100 +Subject: [PATCH 60/60] ARM stm32mp1 r0 rc4 hotfix w904.3 DEVICETREE + +--- + arch/arm/boot/dts/stm32mp157-pinctrl.dtsi | 43 +++++++++++++++++++++++++++++++ + arch/arm/boot/dts/stm32mp157a-dk1.dts | 2 +- + arch/arm/boot/dts/stm32mp157c-dk2.dts | 4 +-- + arch/arm/boot/dts/stm32mp157c-ed1.dts | 2 +- + arch/arm/boot/dts/stm32mp157c.dtsi | 22 +++++++--------- + 5 files changed, 57 insertions(+), 16 deletions(-) + +diff --git a/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi b/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi +index 474e7e3..6e4e5c9 100644 +--- a/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi ++++ b/arch/arm/boot/dts/stm32mp157-pinctrl.dtsi +@@ -1249,6 +1249,49 @@ + }; + }; + ++ sdmmc2_b4_pins_b: sdmmc2-b4-1 { ++ pins1 { ++ pinmux = , /* SDMMC2_D0 */ ++ , /* SDMMC2_D1 */ ++ , /* SDMMC2_D2 */ ++ , /* SDMMC2_D3 */ ++ ; /* SDMMC2_CMD */ ++ slew-rate = <1>; ++ drive-push-pull; ++ bias-disable; ++ }; ++ pins2 { ++ pinmux = ; /* SDMMC2_CK */ ++ slew-rate = <2>; ++ drive-push-pull; ++ bias-disable; ++ }; ++ }; ++ ++ sdmmc2_b4_od_pins_b: sdmmc2-b4-od-1 { ++ pins1 { ++ pinmux = , /* SDMMC2_D0 */ ++ , /* SDMMC2_D1 */ ++ , /* SDMMC2_D2 */ ++ ; /* SDMMC2_D3 */ ++ slew-rate = <1>; ++ drive-push-pull; ++ bias-disable; ++ }; ++ pins2 { ++ pinmux = ; /* SDMMC2_CK */ ++ slew-rate = <2>; ++ drive-push-pull; ++ bias-disable; ++ }; ++ pins3 { ++ pinmux = ; /* SDMMC2_CMD */ ++ slew-rate = <1>; ++ drive-open-drain; ++ bias-disable; ++ }; ++ }; ++ + sdmmc2_d47_pins_a: sdmmc2-d47-0 { + pins { + pinmux = , /* SDMMC2_D4 */ +diff --git a/arch/arm/boot/dts/stm32mp157a-dk1.dts b/arch/arm/boot/dts/stm32mp157a-dk1.dts +index 467c226..e3a36d3 100644 +--- a/arch/arm/boot/dts/stm32mp157a-dk1.dts ++++ b/arch/arm/boot/dts/stm32mp157a-dk1.dts +@@ -325,7 +325,7 @@ + + vddcore: buck1 { + regulator-name = "vddcore"; +- regulator-min-microvolt = <800000>; ++ regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + regulator-initial-mode = <0>; +diff --git a/arch/arm/boot/dts/stm32mp157c-dk2.dts b/arch/arm/boot/dts/stm32mp157c-dk2.dts +index 4da15cd..20a86f1 100644 +--- a/arch/arm/boot/dts/stm32mp157c-dk2.dts ++++ b/arch/arm/boot/dts/stm32mp157c-dk2.dts +@@ -108,8 +108,8 @@ + &sdmmc2 { + arm,primecell-periphid = <0x10153180>; + pinctrl-names = "default", "opendrain", "sleep"; +- pinctrl-0 = <&sdmmc2_b4_pins_a>; +- pinctrl-1 = <&sdmmc2_b4_od_pins_a>; ++ pinctrl-0 = <&sdmmc2_b4_pins_b>; ++ pinctrl-1 = <&sdmmc2_b4_od_pins_b>; + pinctrl-2 = <&sdmmc2_b4_sleep_pins_a>; + non-removable; + st,neg-edge; +diff --git a/arch/arm/boot/dts/stm32mp157c-ed1.dts b/arch/arm/boot/dts/stm32mp157c-ed1.dts +index 6d49f21..780c992 100644 +--- a/arch/arm/boot/dts/stm32mp157c-ed1.dts ++++ b/arch/arm/boot/dts/stm32mp157c-ed1.dts +@@ -203,7 +203,7 @@ + + vddcore: buck1 { + regulator-name = "vddcore"; +- regulator-min-microvolt = <800000>; ++ regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1350000>; + regulator-always-on; + regulator-initial-mode = <0>; +diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi +index b09ef8b..694e6e0 100644 +--- a/arch/arm/boot/dts/stm32mp157c.dtsi ++++ b/arch/arm/boot/dts/stm32mp157c.dtsi +@@ -430,8 +430,8 @@ + interrupts = ; + clocks = <&rcc SPI2_K>; + resets = <&rcc SPI2_R>; +- dmas = <&dmamux1 39 0x400 0x05>, +- <&dmamux1 40 0x400 0x05>; ++ dmas = <&dmamux1 39 0x400 0x01>, ++ <&dmamux1 40 0x400 0x01>; + dma-names = "rx", "tx"; + power-domains = <&pd_core>; + status = "disabled"; +@@ -456,8 +456,8 @@ + interrupts = ; + clocks = <&rcc SPI3_K>; + resets = <&rcc SPI3_R>; +- dmas = <&dmamux1 61 0x400 0x05>, +- <&dmamux1 62 0x400 0x05>; ++ dmas = <&dmamux1 61 0x400 0x01>, ++ <&dmamux1 62 0x400 0x01>; + dma-names = "rx", "tx"; + power-domains = <&pd_core>; + status = "disabled"; +@@ -756,8 +756,8 @@ + interrupts = ; + clocks = <&rcc SPI1_K>; + resets = <&rcc SPI1_R>; +- dmas = <&dmamux1 37 0x400 0x05>, +- <&dmamux1 38 0x400 0x05>; ++ dmas = <&dmamux1 37 0x400 0x01>, ++ <&dmamux1 38 0x400 0x01>; + dma-names = "rx", "tx"; + power-domains = <&pd_core>; + status = "disabled"; +@@ -782,8 +782,8 @@ + interrupts = ; + clocks = <&rcc SPI4_K>; + resets = <&rcc SPI4_R>; +- dmas = <&dmamux1 83 0x400 0x05>, +- <&dmamux1 84 0x400 0x05>; ++ dmas = <&dmamux1 83 0x400 0x01>, ++ <&dmamux1 84 0x400 0x01>; + dma-names = "rx", "tx"; + power-domains = <&pd_core>; + status = "disabled"; +@@ -870,8 +870,8 @@ + interrupts = ; + clocks = <&rcc SPI5_K>; + resets = <&rcc SPI5_R>; +- dmas = <&dmamux1 85 0x400 0x05>, +- <&dmamux1 86 0x400 0x05>; ++ dmas = <&dmamux1 85 0x400 0x01>, ++ <&dmamux1 86 0x400 0x01>; + dma-names = "rx", "tx"; + power-domains = <&pd_core>; + status = "disabled"; +@@ -1770,7 +1770,6 @@ + clocks = <&rcc USBH>; + resets = <&rcc USBH_R>; + interrupts = ; +- power-domains = <&pd_core>; + status = "disabled"; + }; + +@@ -1781,7 +1780,6 @@ + resets = <&rcc USBH_R>; + interrupts = ; + companion = <&usbh_ohci>; +- power-domains = <&pd_core>; + status = "disabled"; + }; + +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0061-ARM-stm32mp1-r0-rc4-hotfix-w904.5-DRIVERS.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0061-ARM-stm32mp1-r0-rc4-hotfix-w904.5-DRIVERS.patch new file mode 100644 index 0000000..3c843b6 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0061-ARM-stm32mp1-r0-rc4-hotfix-w904.5-DRIVERS.patch @@ -0,0 +1,111 @@ +From 5f558583046192d07b1718d6db15cef522fa0bce Mon Sep 17 00:00:00 2001 +From: christophe montaud +Date: Mon, 28 Jan 2019 10:49:20 +0100 +Subject: [PATCH 61/62] ARM stm32mp1 r0 rc4 hotfix w904.5 DRIVERS + +--- + drivers/gpu/drm/panel/panel-orisetech-otm8009a.c | 38 ++++++++++++++---------- + drivers/usb/dwc2/platform.c | 13 ++++++-- + 2 files changed, 33 insertions(+), 18 deletions(-) + +diff --git a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c +index ee7486b..78a7e62 100644 +--- a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c ++++ b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c +@@ -284,7 +284,9 @@ static int otm8009a_unprepare(struct drm_panel *panel) + if (ret) + return ret; + +- msleep(120); ++ msleep(10); ++ ++ regulator_disable(ctx->supply); + + ctx->prepared = false; + +@@ -299,6 +301,26 @@ static int otm8009a_prepare(struct drm_panel *panel) + if (ctx->prepared) + return 0; + ++ if (ctx->reset_gpio) { ++ gpiod_set_value_cansleep(ctx->reset_gpio, 0); ++ gpiod_set_value_cansleep(ctx->reset_gpio, 1); ++ } ++ ++ msleep(20); ++ ++ ret = regulator_enable(ctx->supply); ++ if (ret < 0) { ++ DRM_ERROR("failed to enable supply: %d\n", ret); ++ return ret; ++ } ++ ++ msleep(120); ++ ++ if (ctx->reset_gpio) { ++ gpiod_set_value_cansleep(ctx->reset_gpio, 0); ++ msleep(20); ++ } ++ + ret = otm8009a_init_sequence(ctx); + if (ret) + return ret; +@@ -456,20 +478,6 @@ static int otm8009a_probe(struct mipi_dsi_device *dsi) + return ret; + } + +- ret = regulator_enable(ctx->supply); +- if (ret < 0) { +- DRM_ERROR("failed to enable supply: %d\n", ret); +- return ret; +- } +- +- if (ctx->reset_gpio) { +- gpiod_set_value_cansleep(ctx->reset_gpio, 0); +- gpiod_set_value_cansleep(ctx->reset_gpio, 1); +- msleep(20); +- gpiod_set_value_cansleep(ctx->reset_gpio, 0); +- msleep(100); +- } +- + return 0; + } + +diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c +index b2e5ddc..b80d046 100644 +--- a/drivers/usb/dwc2/platform.c ++++ b/drivers/usb/dwc2/platform.c +@@ -568,13 +568,17 @@ static int __maybe_unused dwc2_suspend(struct device *dev) + struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); + int ret = 0; + +- if (dwc2_is_device_mode(dwc2)) +- dwc2_hsotg_suspend(dwc2); +- + if (dwc2->params.activate_stm_id_vb_detection && + !dwc2->params.force_b_session_valid) { + u32 ggpio; + ++ /* ++ * Need to force the mode to the current mode to avoid Mode ++ * Mismatch Interrupt when ID and VBUS detection will be ++ * disabled ++ */ ++ dwc2_force_mode(dwc2, dwc2_is_host_mode(dwc2)); ++ + ggpio = dwc2_readl(dwc2, GGPIO); + ggpio &= ~GGPIO_STM32_OTG_GCCFG_IDEN; + ggpio &= ~GGPIO_STM32_OTG_GCCFG_VBDEN; +@@ -583,6 +587,9 @@ static int __maybe_unused dwc2_suspend(struct device *dev) + regulator_disable(dwc2->usb33d); + } + ++ if (dwc2_is_device_mode(dwc2)) ++ dwc2_hsotg_suspend(dwc2); ++ + if (dwc2->ll_hw_enabled) + ret = __dwc2_lowlevel_hw_disable(dwc2); + +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0062-ARM-stm32mp1-r0-rc4-hotfix-w904.5-DEVICETREE.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0062-ARM-stm32mp1-r0-rc4-hotfix-w904.5-DEVICETREE.patch new file mode 100644 index 0000000..b830f15 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0062-ARM-stm32mp1-r0-rc4-hotfix-w904.5-DEVICETREE.patch @@ -0,0 +1,24 @@ +From 771fc61468f4af9f733beb9dbe070033ea2b826a Mon Sep 17 00:00:00 2001 +From: christophe montaud +Date: Mon, 28 Jan 2019 10:49:58 +0100 +Subject: [PATCH 62/62] ARM stm32mp1 r0 rc4 hotfix w904.5 DEVICETREE + +--- + arch/arm/boot/dts/stm32mp157c.dtsi | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi +index 694e6e0..9647119 100644 +--- a/arch/arm/boot/dts/stm32mp157c.dtsi ++++ b/arch/arm/boot/dts/stm32mp157c.dtsi +@@ -1233,7 +1233,6 @@ + g-tx-fifo-size = <128 128 64 64 64 64 32 32>; + dr_mode = "otg"; + usb33d-supply = <&usb33>; +- power-domains = <&pd_core>; + status = "disabled"; + }; + +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0063-ARM-stm32mp1-r0-rc4-hotfix-w905.2-DRIVERS.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0063-ARM-stm32mp1-r0-rc4-hotfix-w905.2-DRIVERS.patch new file mode 100644 index 0000000..3a5cc8c --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0063-ARM-stm32mp1-r0-rc4-hotfix-w905.2-DRIVERS.patch @@ -0,0 +1,188 @@ +From 67b204258838dd2c940d5b1508318b4890324a06 Mon Sep 17 00:00:00 2001 +From: christophe montaud +Date: Tue, 29 Jan 2019 17:31:19 +0100 +Subject: [PATCH 63/64] ARM stm32mp1 r0 rc4 hotfix w905.2 DRIVERS + +--- + drivers/dma/stm32-dma.c | 70 ++++++++++++++++++++++++++++++++++++--------- + drivers/usb/dwc2/platform.c | 38 ++++++++++++++++++------ + 2 files changed, 87 insertions(+), 21 deletions(-) + +diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c +index 5abfa4f..dc3ba91 100644 +--- a/drivers/dma/stm32-dma.c ++++ b/drivers/dma/stm32-dma.c +@@ -1675,37 +1675,81 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_memcpy( + return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); + } + ++static bool stm32_dma_is_current_sg(struct stm32_dma_chan *chan) ++{ ++ struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan); ++ struct stm32_dma_sg_req *sg_req; ++ u32 dma_scr, dma_smar, id; ++ ++ id = chan->id; ++ dma_scr = stm32_dma_read(dmadev, STM32_DMA_SCR(id)); ++ ++ if (!(dma_scr & STM32_DMA_SCR_DBM)) ++ return true; ++ ++ sg_req = &chan->desc->sg_req[chan->next_sg]; ++ ++ if (dma_scr & STM32_DMA_SCR_CT) { ++ dma_smar = stm32_dma_read(dmadev, STM32_DMA_SM0AR(id)); ++ return (dma_smar == sg_req->chan_reg.dma_sm0ar); ++ } ++ ++ dma_smar = stm32_dma_read(dmadev, STM32_DMA_SM1AR(id)); ++ ++ return (dma_smar == sg_req->chan_reg.dma_sm1ar); ++} ++ + static size_t stm32_dma_desc_residue(struct stm32_dma_chan *chan, + struct stm32_dma_desc *desc, + u32 next_sg) + { + u32 modulo, burst_size; +- u32 residue = 0; ++ u32 residue; ++ u32 n_sg = next_sg; ++ struct stm32_dma_sg_req *sg_req = &chan->desc->sg_req[chan->next_sg]; + int i; + + /* Drain case */ + if (chan->residue_after_drain) + return chan->residue_after_drain; + ++ residue = stm32_dma_get_remaining_bytes(chan); ++ + /* +- * In cyclic mode, for the last period, residue = remaining bytes from +- * NDTR ++ * Calculate the residue means compute the descriptors ++ * information: ++ * - the sg currently transferred ++ * - the remaining position in this sg (NDTR). ++ * ++ * The issue is that a race condition can occur if DMA is ++ * running. DMA can have started to transfer the next sg before ++ * the position in sg is read. In this case the remaing position ++ * can correspond to the new sg position. ++ * The strategy implemented in the stm32 driver is to check the ++ * sg transition. If detected we can not trust the SxNDTR register value ++ * this register can not be up to date during the transition. ++ * in this case we can assume that the dma is at the beginning of next ++ * sg so we calculate the residue in consequence. + */ +- if (chan->desc->cyclic && next_sg == 0) { +- residue = stm32_dma_get_remaining_bytes(chan); +- goto end; ++ ++ if (!stm32_dma_is_current_sg(chan)) { ++ n_sg++; ++ if (n_sg == chan->desc->num_sgs) ++ n_sg = 0; ++ residue = sg_dma_len(&sg_req->stm32_sgl_req); + } + + /* +- * For all other periods in cyclic mode, and in sg mode, +- * residue = remaining bytes from NDTR + remaining periods/sg to be +- * transferred ++ * In cyclic mode, for the last period, residue = remaining bytes ++ * from NDTR ++ * else for all other periods in cyclic mode, and in sg mode, ++ * residue = remaining bytes from NDTR + remaining ++ * periods/sg to be transferred + */ +- for (i = next_sg; i < desc->num_sgs; i++) +- residue += sg_dma_len(&desc->sg_req[i].stm32_sgl_req); +- residue += stm32_dma_get_remaining_bytes(chan); ++ if (!chan->desc->cyclic || n_sg != 0) ++ for (i = n_sg; i < desc->num_sgs; i++) ++ residue += sg_dma_len(&desc->sg_req[i].stm32_sgl_req); + +-end: + if (!chan->mem_burst) + return residue; + +diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c +index b80d046..7e6960c 100644 +--- a/drivers/usb/dwc2/platform.c ++++ b/drivers/usb/dwc2/platform.c +@@ -568,16 +568,35 @@ static int __maybe_unused dwc2_suspend(struct device *dev) + struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); + int ret = 0; + ++ if (dwc2_is_device_mode(dwc2)) ++ dwc2_hsotg_suspend(dwc2); ++ + if (dwc2->params.activate_stm_id_vb_detection && + !dwc2->params.force_b_session_valid) { +- u32 ggpio; ++ u32 ggpio, gotgctl; ++ int is_host = dwc2_is_host_mode(dwc2); + + /* + * Need to force the mode to the current mode to avoid Mode +- * Mismatch Interrupt when ID and VBUS detection will be +- * disabled ++ * Mismatch Interrupt when ID detection will be disabled. + */ +- dwc2_force_mode(dwc2, dwc2_is_host_mode(dwc2)); ++ dwc2_force_mode(dwc2, is_host); ++ ++ if (!is_host) { ++ gotgctl = dwc2_readl(dwc2, GOTGCTL); ++ /* ++ * We're about to disable Vbus detection hw before low ++ * power mode entry. Then an undesired disconnect ++ * interrupt may occur which is racy with low power ++ * (low-level hw disable). Then check valid session ++ * to force B-peripheral session value. ++ */ ++ if (gotgctl & GOTGCTL_BSESVLD) { ++ gotgctl |= GOTGCTL_BVALOVAL; ++ gotgctl |= GOTGCTL_BVALOEN; ++ dwc2_writel(dwc2, gotgctl, GOTGCTL); ++ } ++ } + + ggpio = dwc2_readl(dwc2, GGPIO); + ggpio &= ~GGPIO_STM32_OTG_GCCFG_IDEN; +@@ -587,9 +606,6 @@ static int __maybe_unused dwc2_suspend(struct device *dev) + regulator_disable(dwc2->usb33d); + } + +- if (dwc2_is_device_mode(dwc2)) +- dwc2_hsotg_suspend(dwc2); +- + if (dwc2->ll_hw_enabled) + ret = __dwc2_lowlevel_hw_disable(dwc2); + +@@ -612,7 +628,7 @@ static int __maybe_unused dwc2_resume(struct device *dev) + + if (dwc2->params.activate_stm_id_vb_detection && + !dwc2->params.force_b_session_valid) { +- u32 ggpio; ++ u32 ggpio, gotgctl; + + ret = regulator_enable(dwc2->usb33d); + if (ret) +@@ -625,6 +641,12 @@ static int __maybe_unused dwc2_resume(struct device *dev) + + /* ID/VBUS detection startup time */ + usleep_range(5000, 7000); ++ ++ /* Unconditionally clear B-Session Valid override */ ++ gotgctl = dwc2_readl(dwc2, GOTGCTL); ++ gotgctl &= ~GOTGCTL_BVALOVAL; ++ gotgctl &= ~GOTGCTL_BVALOEN; ++ dwc2_writel(dwc2, gotgctl, GOTGCTL); + } + + if (dwc2->params.force_b_session_valid) { +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0064-ARM-stm32mp1-r0-rc4-hotfix-w905.2-DEVICETREE.patch b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0064-ARM-stm32mp1-r0-rc4-hotfix-w905.2-DEVICETREE.patch new file mode 100644 index 0000000..c3d2916 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/4.19.9/0064-ARM-stm32mp1-r0-rc4-hotfix-w905.2-DEVICETREE.patch @@ -0,0 +1,24 @@ +From cad13a8b78d8d320394dbed545df8e47c199bbd1 Mon Sep 17 00:00:00 2001 +From: christophe montaud +Date: Tue, 29 Jan 2019 17:31:53 +0100 +Subject: [PATCH 64/64] ARM stm32mp1 r0 rc4 hotfix w905.2 DEVICETREE + +--- + arch/arm/boot/dts/stm32mp157c.dtsi | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi +index 9647119..5581a1c 100644 +--- a/arch/arm/boot/dts/stm32mp157c.dtsi ++++ b/arch/arm/boot/dts/stm32mp157c.dtsi +@@ -58,6 +58,7 @@ + , + ; + interrupt-parent = <&intc>; ++ always-on; + }; + + clocks { +-- +2.7.4 + diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/fragment-03-systemd.config b/recipes-kernel/linux/linux-stm32mp/4.19/fragment-03-systemd.config new file mode 100644 index 0000000..f17c66e --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/fragment-03-systemd.config @@ -0,0 +1,389 @@ +# +# systemd specific +# +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +# CONFIG_CGROUP_DEBUG is not set +CONFIG_CGROUP_FREEZER=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_CGROUP_SCHED=y +CONFIG_BLK_CGROUP=y +CONFIG_AUTOFS4_FS=y +CONFIG_SYSV_FS=y +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_SYSFS_DEPRECATED_V2 is not set +CONFIG_HOTPLUG=y +CONFIG_UEVENT_HELPER_PATH="" +CONFIG_UNIX=y +CONFIG_SYSFS=y +CONFIG_PROC_FS=y +CONFIG_TMPFS=y +CONFIG_INOTIFY_USER=y +CONFIG_SIGNALFD=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_BLK_DEV_BSG=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_NS=y +CONFIG_PROC_PID_CPUSET=y +CONFIG_RESOURCE_COUNTERS=y +CONFIG_FUSE_FS=y +CONFIG_TIMERFD=y +CONFIG_EPOLL=y +CONFIG_NET=y +CONFIG_FHANDLE=y +CONFIG_CRYPTO_USER_API_HASH=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_SHA256=y +# CONFIG_FW_LOADER_USER_HELPER is not set +CONFIG_DMIID=y +CONFIG_NET_NS=y +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +CONFIG_USER_NS=y +CONFIG_IPV6=y +CONFIG_TMPFS_XATTR=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_XFS_POSIX_ACL=y +CONFIG_BTRFS_FS_POSIX_ACL=y +CONFIG_CHECKPOINT_RESTORE=y +CONFIG_CGROUP_SCHED=y +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_SECCOMP=y +CONFIG_CFS_BANDWIDTH=y +CONFIG_CGROUP_BPF=y +# CONFIG_RT_GROUP_SCHED is not set +CONFIG_DEVPTS_MULTIPLE_INSTANCES=y +# CONFIG_AUDIT is not set +CONFIG_BPF_SYSCALL=y + +# ------------------------------------------- +# IP Table +# ------------------------------------------- +CONFIG_UNIX_DIAG=m +CONFIG_TLS=m +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_FIB_TRIE_STATS=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_NET_IPIP=m +CONFIG_NET_IPGRE_DEMUX=m +CONFIG_NET_IPGRE=m +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_IP_MROUTE=y +CONFIG_IP_MROUTE_MULTIPLE_TABLES=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_NET_IPVTI=m +CONFIG_NET_FOU_IP_TUNNELS=y +CONFIG_INET_AH=m +CONFIG_INET_ESP=m +CONFIG_INET_ESP_OFFLOAD=m +CONFIG_INET_IPCOMP=m +CONFIG_INET_XFRM_MODE_TRANSPORT=m +CONFIG_INET_XFRM_MODE_TUNNEL=m +CONFIG_INET_XFRM_MODE_BEET=m +CONFIG_INET_DIAG=m +CONFIG_INET_UDP_DIAG=m +CONFIG_INET_RAW_DIAG=mCONFIG_BPF_SYSCALL=y +CONFIG_INET_DIAG_DESTROY=y +CONFIG_TCP_CONG_ADVANCED=y +CONFIG_TCP_MD5SIG=y +CONFIG_IPV6=m +CONFIG_IPV6_ROUTE_INFO=y +CONFIG_INET6_ESP_OFFLOAD=m +CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION=m +CONFIG_IPV6_VTI=m +CONFIG_IPV6_SIT_6RD=y +CONFIG_IPV6_GRE=m +CONFIG_IPV6_SUBTREES=y +CONFIG_IPV6_MROUTE=y +CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y +CONFIG_IPV6_PIMSM_V2=y +CONFIG_IPV6_SEG6_LWTUNNEL=y +CONFIG_IPV6_SEG6_HMAC=y +CONFIG_NETWORK_SECMARK=y +CONFIG_NETWORK_PHY_TIMESTAMPING=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=m +CONFIG_NF_LOG_NETDEV=m +CONFIG_NF_CONNTRACK_SECMARK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CONNTRACK_TIMEOUT=y +CONFIG_NF_CONNTRACK_TIMESTAMP=y +CONFIG_NF_CONNTRACK_AMANDA=m +CONFIG_NF_CONNTRACK_FTP=m +CONFIG_NF_CONNTRACK_H323=m +CONFIG_NF_CONNTRACK_IRC=m +CONFIG_NF_CONNTRACK_NETBIOS_NS=m +CONFIG_NF_CONNTRACK_SNMP=m +CONFIG_NF_CONNTRACK_PPTP=m +CONFIG_NF_CONNTRACK_SIP=m +CONFIG_NF_CONNTRACK_TFTP=m +CONFIG_NF_CT_NETLINK=mCONFIG_BPF_SYSCALL=y +CONFIG_NF_CT_NETLINK_TIMEOUT=m +CONFIG_NF_CT_NETLINK_HELPER=m +CONFIG_NETFILTER_NETLINK_GLUE_CT=y +CONFIG_NF_TABLES=m +CONFIG_NF_TABLES_INET=m +CONFIG_NF_TABLES_NETDEV=m +CONFIG_NFT_EXTHDR=m +CONFIG_NFT_META=m +CONFIG_NFT_RT=m +CONFIG_NFT_NUMGEN=m +CONFIG_NFT_CT=m +CONFIG_NFT_SET_RBTREE=m +CONFIG_NFT_SET_HASH=m +CONFIG_NFT_SET_BITMAP=m +CONFIG_NFT_COUNTER=m +CONFIG_NFT_LOG=m +CONFIG_NFT_LIMIT=m +CONFIG_NFT_MASQ=m +CONFIG_NFT_REDIR=m +CONFIG_NFT_NAT=m +CONFIG_NFT_OBJREF=m +CONFIG_NFT_QUEUE=m +CONFIG_NFT_QUOTA=m +CONFIG_NFT_REJECT=m +CONFIG_NFT_HASH=m +CONFIG_NFT_DUP_NETDEV=m +CONFIG_NFT_FWD_NETDEV=m +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m +CONFIG_NETFILTER_XT_TARGET_CONNMARK=m +CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=m +CONFIG_NETFILTER_XT_TARGET_HMARK=m +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m +CONFIG_NETFILTER_XT_TARGET_LED=m +CONFIG_NETFILTER_XT_TARGET_LOG=m +CONFIG_NETFILTER_XT_TARGET_MARK=m +CONFIG_NETFILTER_XT_TARGET_NFLOG=m +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m +CONFIG_NETFILTER_XT_TARGET_TEE=m +CONFIG_NETFILTER_XT_TARGET_SECMARK=m +CONFIG_NETFILTER_XT_TARGET_TCPMSS=m +CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m +CONFIG_NETFILTER_XT_MATCH_BPF=m +CONFIG_NETFILTER_XT_MATCH_CGROUP=m +CONFIG_NETFILTER_XT_MATCH_CLUSTER=m +CONFIG_NETFILTER_XT_MATCH_COMMENT=m +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m +CONFIG_NETFILTER_XT_MATCH_CONNLABEL=m +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m +CONFIG_NETFILTER_XT_MATCH_CONNMARK=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m +CONFIG_NETFILTER_XT_MATCH_CPU=m +CONFIG_NETFILTER_XT_MATCH_DCCP=m +CONFIG_NETFILTER_XT_MATCH_DEVGROUP=m +CONFIG_NETFILTER_XT_MATCH_DSCP=m +CONFIG_NETFILTER_XT_MATCH_ESP=m +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m +CONFIG_NETFILTER_XT_MATCH_HELPER=m +CONFIG_NETFILTER_XT_MATCH_IPCOMP=m +CONFIG_NETFILTER_XT_MATCH_IPRANGE=m +CONFIG_NETFILTER_XT_MATCH_LENGTH=m +CONFIG_NETFILTER_XT_MATCH_LIMIT=m +CONFIG_NETFILTER_XT_MATCH_MAC=m +CONFIG_NETFILTER_XT_MATCH_MARK=m +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m +CONFIG_NETFILTER_XT_MATCH_NFACCT=m +CONFIG_NETFILTER_XT_MATCH_OSF=m +CONFIG_NETFILTER_XT_MATCH_OWNER=m +CONFIG_NETFILTER_XT_MATCH_POLICY=m +CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m +CONFIG_NETFILTER_XT_MATCH_QUOTA=m +CONFIG_NETFILTER_XT_MATCH_RATEEST=m +CONFIG_NETFILTER_XT_MATCH_REALM=m +CONFIG_NETFILTER_XT_MATCH_RECENT=m +CONFIG_NETFILTER_XT_MATCH_SCTP=m +CONFIG_NETFILTER_XT_MATCH_STATE=m +CONFIG_NETFILTER_XT_MATCH_STATISTIC=m +CONFIG_NETFILTER_XT_MATCH_STRING=m +CONFIG_NETFILTER_XT_MATCH_TCPMSS=m +CONFIG_NETFILTER_XT_MATCH_TIME=m +CONFIG_NETFILTER_XT_MATCH_U32=m +CONFIG_IP_SET=m +CONFIG_IP_VS=m +CONFIG_NF_CONNTRACK_IPV4=m +CONFIG_NF_SOCKET_IPV4=m +CONFIG_NFT_CHAIN_ROUTE_IPV4=m +CONFIG_NFT_DUP_IPV4=m +CONFIG_NFT_FIB_IPV4=m +CONFIG_NF_TABLES_ARP=m +CONFIG_NF_LOG_ARP=m +CONFIG_NFT_CHAIN_NAT_IPV4=m +CONFIG_NFT_MASQ_IPV4=m +CONFIG_NFT_REDIR_IPV4=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_AH=m +CONFIG_IP_NF_MATCH_ECN=m +CONFIG_IP_NF_MATCH_RPFILTER=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_SYNPROXY=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_NETMAP=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_TARGET_CLUSTERIP=m +CONFIG_IP_NF_TARGET_ECN=m +CONFIG_IP_NF_TARGET_TTL=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m +CONFIG_NF_CONNTRACK_IPV6=m +CONFIG_NF_SOCKET_IPV6=m +CONFIG_NFT_CHAIN_ROUTE_IPV6=m +CONFIG_NFT_DUP_IPV6=m +CONFIG_NFT_FIB_IPV6=m +CONFIG_NFT_CHAIN_NAT_IPV6=m +CONFIG_NFT_MASQ_IPV6=m +CONFIG_NFT_REDIR_IPV6=m +CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_MATCH_AH=m +CONFIG_IP6_NF_MATCH_EUI64=m +CONFIG_IP6_NF_MATCH_FRAG=m +CONFIG_IP6_NF_MATCH_OPTS=m +CONFIG_IP6_NF_MATCH_HL=m +CONFIG_IP6_NF_MATCH_IPV6HEADER=m +CONFIG_IP6_NF_MATCH_MH=m +CONFIG_IP6_NF_MATCH_RPFILTER=m +CONFIG_IP6_NF_MATCH_RT=m +CONFIG_IP6_NF_TARGET_HL=m +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_TARGET_REJECT=m +CONFIG_IP6_NF_TARGET_SYNPROXY=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_IP6_NF_RAW=m +CONFIG_IP6_NF_NAT=m +CONFIG_IP6_NF_TARGET_MASQUERADE=m +CONFIG_IP6_NF_TARGET_NPT=m +CONFIG_NF_TABLES_BRIDGE=m +CONFIG_NFT_BRIDGE_META=m +CONFIG_NFT_BRIDGE_REJECT=m +CONFIG_NF_LOG_BRIDGE=m +CONFIG_BRIDGE_NF_EBTABLES=m +CONFIG_BRIDGE_EBT_BROUTE=m +CONFIG_BRIDGE_EBT_T_FILTER=m +CONFIG_BRIDGE_EBT_T_NAT=m +CONFIG_BRIDGE_EBT_802_3=m +CONFIG_BRIDGE_EBT_AMONG=m +CONFIG_BRIDGE_EBT_ARP=m +CONFIG_BRIDGE_EBT_IP=m +CONFIG_BRIDGE_EBT_IP6=m +CONFIG_BRIDGE_EBT_LIMIT=m +CONFIG_BRIDGE_EBT_MARK=m +CONFIG_BRIDGE_EBT_PKTTYPE=m +CONFIG_BRIDGE_EBT_STP=m +CONFIG_BRIDGE_EBT_VLAN=m +CONFIG_BRIDGE_EBT_ARPREPLY=m +CONFIG_BRIDGE_EBT_DNAT=m +CONFIG_BRIDGE_EBT_MARK_T=m +CONFIG_BRIDGE_EBT_REDIRECT=m +CONFIG_BRIDGE_EBT_SNAT=m +CONFIG_BRIDGE_EBT_LOG=m +CONFIG_BRIDGE_EBT_NFLOG=m +CONFIG_L2TP=m +CONFIG_BRIDGE=m +CONFIG_VLAN_8021Q=m +CONFIG_VLAN_8021Q_GVRP=y +CONFIG_VLAN_8021Q_MVRP=y +CONFIG_LLC2=m +CONFIG_6LOWPAN=m +CONFIG_6LOWPAN_DEBUGFS=y +CONFIG_IEEE802154=m +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CBQ=m +CONFIG_NET_SCH_HTB=m +CONFIG_NET_SCH_HFSC=m +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_MULTIQ=m +CONFIG_NET_SCH_RED=m +CONFIG_NET_SCH_SFB=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TEQL=m +CONFIG_NET_SCH_TBF=m +CONFIG_NET_SCH_GRED=m +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_NETEM=m +CONFIG_NET_SCH_DRR=m +CONFIG_NET_SCH_MQPRIO=m +CONFIG_NET_SCH_CHOKE=m +CONFIG_NET_SCH_QFQ=m +CONFIG_NET_SCH_CODEL=m +CONFIG_NET_SCH_FQ_CODEL=m +CONFIG_NET_SCH_FQ=m +CONFIG_NET_SCH_HHF=m +CONFIG_NET_SCH_PIE=m +CONFIG_NET_SCH_PLUG=m +CONFIG_NET_SCH_DEFAULT=y +CONFIG_NET_CLS_BASIC=m +CONFIG_NET_CLS_TCINDEX=m +CONFIG_NET_CLS_ROUTE4=m +CONFIG_NET_CLS_FW=m +CONFIG_NET_CLS_U32=m +CONFIG_CLS_U32_PERF=y +CONFIG_CLS_U32_MARK=y +CONFIG_NET_CLS_RSVP=m +CONFIG_NET_CLS_RSVP6=m +CONFIG_NET_CLS_FLOW=m +CONFIG_NET_CLS_CGROUP=m +CONFIG_NET_CLS_BPF=m +CONFIG_NET_CLS_FLOWER=m +CONFIG_NET_CLS_MATCHALL=m +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_CMP=m +CONFIG_NET_EMATCH_NBYTE=m +CONFIG_NET_EMATCH_U32=m +CONFIG_NET_EMATCH_META=m +CONFIG_NET_EMATCH_TEXT=m +CONFIG_NET_EMATCH_CANID=m +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=m +CONFIG_NET_ACT_GACT=m +CONFIG_GACT_PROB=y +CONFIG_NET_ACT_MIRRED=m +CONFIG_NET_ACT_SAMPLE=m +CONFIG_NET_ACT_NAT=m +CONFIG_NET_ACT_PEDIT=m +CONFIG_NET_ACT_CSUM=m +CONFIG_NET_ACT_VLAN=m +CONFIG_NET_ACT_BPF=m +CONFIG_NET_ACT_SKBMOD=m +CONFIG_NET_ACT_IFE=m +CONFIG_NET_ACT_TUNNEL_KEY=m +CONFIG_NET_IFE_SKBMARK=m +CONFIG_NET_IFE_SKBPRIO=m +CONFIG_NET_IFE_SKBTCINDEX=m +CONFIG_NET_CLS_IND=y +CONFIG_DCB=y +CONFIG_BATMAN_ADV=m +CONFIG_BATMAN_ADV_BATMAN_V=y +CONFIG_BATMAN_ADV_DAT=y +CONFIG_BATMAN_ADV_NC=y +CONFIG_BATMAN_ADV_MCAST=y +CONFIG_BATMAN_ADV_DEBUG=y +CONFIG_OPENVSWITCH=m +CONFIG_VSOCKETS=m +CONFIG_VIRTIO_VSOCKETS=m +CONFIG_NETLINK_DIAG=m +CONFIG_NET_NSH=m +CONFIG_HSR=m +CONFIG_CGROUP_NET_PRIO=y +CONFIG_BPF_JIT=y +CONFIG_BT_6LOWPAN=m +CONFIG_MACVLAN=m +CONFIG_MACVTAP=m +CONFIG_TUN=m +CONFIG_TUN_VNET_CROSS_LE=y +CONFIG_VETH=m diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/fragment-04-optee.config b/recipes-kernel/linux/linux-stm32mp/4.19/fragment-04-optee.config new file mode 100644 index 0000000..07554cf --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/fragment-04-optee.config @@ -0,0 +1,2 @@ +CONFIG_TEE=y +CONFIG_OPTEE=y diff --git a/recipes-kernel/linux/linux-stm32mp/4.19/fragment-05-modules.config b/recipes-kernel/linux/linux-stm32mp/4.19/fragment-05-modules.config new file mode 100644 index 0000000..e9b3cf5 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/4.19/fragment-05-modules.config @@ -0,0 +1,13 @@ +# Allow to load kernel modules built with different kernel version +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODVERSIONS=y + +# Support of iks01a2 +CONFIG_IIO_BUFFER=y +CONFIG_IIO_KFIFO_BUF=y +CONFIG_IIO_TRIGGERED_BUFFER=y +CONFIG_HTS221=y +CONFIG_IIO_ST_PRESS=m +CONFIG_IIO_ST_LSM6DSX=m +CONFIG_IIO_ST_LSM6DSX_I2C=m + diff --git a/recipes-kernel/linux/linux-stm32mp/README.HOW_TO.txt b/recipes-kernel/linux/linux-stm32mp/README.HOW_TO.txt new file mode 100644 index 0000000..224037b --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp/README.HOW_TO.txt @@ -0,0 +1,346 @@ +Compilation of kernel: +1. Pre-requisite +2. Initialise cross-compilation via SDK +3. Prepare kernel source code +4. Manage the kernel source code +5. Configure kernel source code +6. Compile kernel source code +7. Update software on board + +1. Pre-requisite: +----------------- +OpenSTLinux SDK must be installed. + +For kernel build, you need to install: +- libncurses and libncursesw dev package + Ubuntu: sudo apt-get install libncurses5-dev libncursesw5-dev + Fedora: sudo yum install ncurses-devel +- mkimage + Ubuntu: sudo apt-get install u-boot-tools + Fedora: sudo yum install u-boot-tools + +Only if you like to have a git management of the code (see section 4 +[Manage the kernel source code]): +- git: + Ubuntu: sudo apt-get install git-core gitk + Fedora: sudo yum install git + +If you have never configured your git configuration, run the following commands: + $> git config --global user.name "your_name" + $> git config --global user.email "your_email@example.com" + +2. Initialise cross-compilation via SDK: +---------------------------------------- +Source SDK environment: + $> source /environment-setup-cortexa9hf-neon-openstlinux_weston-linux-gnueabi + +To verify if your cross-compilation environment has been put in place correctly, +run the following command: + $> set | grep CROSS + CROSS_COMPILE=arm-openstlinux_weston-linux-gnueabi- + +Warning: the environment is valid only on the shell session where you have +sourced the SDK environment. + +3. Prepare kernel source: +------------------------- +If you have the tarball and the list of patches, then you must extract the +tarball and apply the patches. + $> tar xfz .tar.gz + or + $> tar xfj .tar.bz2 + or + $> tar xfJ .tar.xz + + $> cd +NB: if you like to have a git management of the code, see section 4 [Manage the +kernel source code] + if there is some patch, please apply it on source code + $> for p in `ls -1 /*.patch`; do patch -p1 < $p; done + +4. Manage the kernel source code: +--------------------------------- +4.1 Setup kernel source code under git +-------------------------------------- +If you like to have a better management of change made on kernel source, you can +use git. + +* With the kernel source code extracted in the section 3 [Prepare kernel source] + $> cd + $> test -d .git || git init . && git add . && git commit -m "new kernel" && git gc + $> git checkout -b WORKING + Apply patches: + $> for p in `ls -1 /*.patch`; do git am $p; done + NB: this is the fastest way to get your kernel source code ready for development + +Or + +* With the kernel source code from the Linux kernel git repositories: + URL: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git + Branch: ##GIT_BRANCH## + Revision: ##GIT_SRCREV## + + $> git clone https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git + $> cd + $> git checkout -b WORKING ##GIT_SRCREV## + $> for p in `ls -1 /*.patch`; do git am $p; done + NB: this way is slightly slower than the tarball extraction but you get + advantage of all git history. + +4.2 Predefined kernel version vs auto-generated kernel version: +--------------------------------------------------------------- +If you are using git for managing your source code, kernel makefile get the SHA1 +of current git and add it to kernel version number generated. + ex.: 4.9.23-g3e866b0 (kernel version + SHA1 of current git commit) +To bypass this auto-generation of kernel version number: + $> cd + $> echo "" > .scmversion +This file avoid to have a kernel version with SHA1: +- With scmversion file: 4.9.23 +- Without scmversion file: 4.9.23-g3e866b0 +This configuration allows to build new kernel from modified source code without +any issue when using the new kernel binary on target regarding any external +kernel module already available on target rootfs (as built without scmversion). + +5. Configure kernel source code: +-------------------------------- +There are two methods to configure and compile kernel source code: +- Inside kernel source tree directory +- Outside kernel source tree in a build directory +We highly preconized the build is a build directory method as: +- It avoids mixing files generated by the build with the source files inside + same directories +- To remove all the files generated by the build, it's enough to remove the + build directory +- You can build for different configurations in several build directories, e.g.: + 1) build in "build_1" for a first kernel configuration + 2) build in "build_2" for a second kernel configuration + Then this leaves the 2 images available for tests + +* Configure on a build directory (different of kernel source code directory) + Here for example, build directory is located at the same level of kernel + source code + $> cd + $> mkdir -p ../build + $> make ARCH=arm O="$PWD/../build" multi_v7_defconfig fragment*.config + + If there are some fragments, apply them + * manually one by one: + $> scripts/kconfig/merge_config.sh -m -r -O $PWD/../build $PWD/../build/.config ../fragment-01-xxx.config + $> scripts/kconfig/merge_config.sh -m -r -O $PWD/../build $PWD/../build/.config ../fragment-02-xxx.config + ... + $> yes '' | make ARCH=arm oldconfig O="$PWD/../build" + * or, by loop: + $> for f in `ls -1 ../fragment*.config`; do scripts/kconfig/merge_config.sh -m -r -O $PWD/../build $PWD/../build/.config $f; done + $> yes '' | make ARCH=arm oldconfig O="$PWD/../build" + +* Configure on the current source code directory + $> cd + $> make ARCH=arm multi_v7_defconfig fragment*.config + + If there are some fragments, apply them + * manually one by one: + $> scripts/kconfig/merge_config.sh -m -r .config ../fragment-01-xxxx.config + $> scripts/kconfig/merge_config.sh -m -r .config ../fragment-02-xxxx.config + ... + $> yes '' | make oldconfig + * or, by loop: + $> for f in `ls -1 ../fragment*.config`; do scripts/kconfig/merge_config.sh -m -r .config $f; done + $> yes '' | make ARCH=arm oldconfig + +NB: Two types of fragments are provided: + * official fragments (fragment-xxx.config) + * optional fragments as example (optional-fragment-xxx.config) to add a + feature not enabled by default. + The order in which fragments are applied is determined by the number of the + fragment filename (fragment-001, fragment-002, e.g.). + Please pay special attention to the naming of your optional fragments to + ensure you select the right features. + +6. Compile kernel source code: +------------------------------ +You MUST compile from the directory on which the configuration has been done (i.e. +the directory which contains the '.config' file). + +It's preconized to use the method with dedicated build directory for a better +managment of changes made on source code (as all build artifacts will be located +inside the dedicated build directory). + +* Compile and install on a build directory (different of kernel source code directory) + $> cd + * Build kernel images (uImage and vmlinux) and device tree (dtbs) + $> make ARCH=arm uImage vmlinux dtbs LOADADDR=0xC2000040 O="$PWD/../build" + * Build kernel module + $> make ARCH=arm modules O="$PWD/../build" + * Generate output build artifacts + $> make ARCH=arm INSTALL_MOD_PATH="$PWD/../build/install_artifact" modules_install O="$PWD/../build" + $> mkdir -p $PWD/../build/install_artifact/boot/ + $> cp $PWD/../build/arch/arm/boot/uImage $PWD/../build/install_artifact/boot/ + $> cp $PWD/../build/arch/arm/boot/dts/st*.dtb $PWD/../build/install_artifact/boot/ + + or + + $> cd + * Build kernel images (uImage and vmlinux) and device tree (dtbs) + $> make ARCH=arm uImage vmlinux dtbs LOADADDR=0xC2000040 + * Build kernel module + $> make ARCH=arm modules + * Generate output build artifacts + $> make ARCH=arm INSTALL_MOD_PATH="$PWD/../build/install_artifact" modules_install + $> mkdir -p $PWD/../build/install_artifact/boot/ + $> cp $PWD/../build/arch/arm/boot/uImage $PWD/../build/install_artifact/boot/ + $> cp $PWD/../build/arch/arm/boot/dts/st*.dtb $PWD/../build/install_artifact/boot/ + +* Compile and install on the current source code directory + $> cd + * Build kernel images (uImage and vmlinux) and device tree (dtbs) + $> make ARCH=arm uImage vmlinux dtbs LOADADDR=0xC2000040 + * Build kernel module + $> make ARCH=arm modules + * Generate output build artifacts + $> make ARCH=arm INSTALL_MOD_PATH="$PWD/install_artifact" modules_install + $> mkdir -p $PWD/install_artifact/boot/ + $> cp $PWD/arch/arm/boot/uImage $PWD/install_artifact/boot/ + $> cp $PWD/arch/arm/boot/dts/st*.dtb $PWD/install_artifact/boot/ + +7. Update software on board: +---------------------------- +7.1. Partitioning of binaries: +------------------------------ +* Bootfs: + Bootfs contains the kernel and the devicetree. +* Rootfs: + Rootfs contains the external kernel modules. +Please refer to User guide for more details. + +7.2. Update via network: +------------------------ +* kernel + devicetree + $> cd /install_artifact + $> ssh root@ mount /boot + $> scp -r boot/* root@:/boot/ + $> ssh root@ umount /boot + +* kernel modules + $> cd /install_artifact + Remove the link on install_artifact/lib/modules// + $> rm lib/modules//source lib/modules//build + Optionally, strip kernel modules (to reduce the size of each kernel modules) + $> find . -name "*.ko" | xargs $STRIP --strip-debug --remove-section=.comment --remove-section=.note --preserve-dates + + Copy kernel modules: + $> scp -r lib/modules/* root@:/lib/modules/ + + Generate a list of module dependencies (modules.dep) and a list of symbols + provided by modules (modules.symbols): + $> ssh root@ /sbin/depmod -a + Synchronize data on disk with memory + $> ssh root@ sync + Reboot the board in order to take update into account + $> ssh root@ reboot + +7.3. Update via SDCARD on your Linux PC: +---------------------------------------- +* kernel + devicetree + $> cd /install_artifact + Verify sdcard are mounted on your Linux PC: /media/$USER/bootfs + $> cp -r boot/* /media/$USER/bootfs/ + Depending of your Linux configuration, you may call the command under sudo + $> sudo cp -r boot/* /media/$USER/bootfs/ + Don't forget to unmount properly sdcard + +* kernel modules + $> cd /install_artifact + Remove the link on install_artifact/lib/modules// + $> rm lib/modules//source lib/modules//build + Optionally, strip kernel modules (to reduce the size of each kernel modules) + $> find . -name "*.ko" | xargs $STRIP --strip-debug --remove-section=.comment --remove-section=.note --preserve-dates + + Verify sdcard are mounted on your Linux PC: /media/$USER/rootfs + Copy kernel modules: + $> cp -r lib/modules/* /media/$USER/rootfs/lib/modules/ + Depending of your Linux configuration, you may call the command under sudo + $> sudo cp -r lib/modules/* /media/$USER/rootfs/lib/modules/ + Don't forget to unmount properly sdcard + + Generate a list of module dependencies (modules.dep) and a list of symbols + provided by modules (modules.symbols): + $> ssh root@ depmod -a + Synchronize data on disk with memory + $> ssh root@ sync + Reboot the board in order to take update into account + $> ssh root@ reboot + +7.4. Update via SDCARD on your BOARD (via U-Boot): +-------------------------------------------------- +You MUST configure first, via U-Boot, the board into usb mass storage: +* Plug the SDCARD on Board. +* Start the board and stop on U-boot shell: + Hit any key to stop autoboot: 0 + STM32MP> +* plug an USB cable between the PC and the board via USB OTG port. +* On U-Boot shell, call the usb mass storage functionnality: + STM32MP> ums 0 mmc 0 + ums +Example: +For SDCARD: ums 0 mmc 0 +For USB Disk: ums 0 usb 0 + +* kernel + devicetree + $> cd /install_artifact + Remove the link on install_artifact/lib/modules// + $> rm lib/modules//source lib/modules//build + Optionally, strip kernel modules (to reduce the size of each kernel modules) + $> find . -name "*.ko" | xargs $STRIP --strip-debug --remove-section=.comment --remove-section=.note --preserve-dates + + Verify sdcard mount point are mounted on your Linux PC: /media/$USER/bootfs + $> cp -r boot/* /media/$USER/bootfs/ + Depending of your Linux configuration, you may call the command under sudo + $> sudo cp -rf boot/* /media/$USER/bootfs/ + Don't forget to unmount properly sdcard + Warning: kernel and device tree file name must be aligned between + extlinux.conf file and file system. + +* kernel modules + $> cd /install_artifact + Remove the link on install_artifact/lib/modules// + $> rm lib/modules//source lib/modules//build + Optionally, strip kernel modules (to reduce the size of each kernel modules) + $> find . -name "*.ko" | xargs $STRIP --strip-debug --remove-section=.comment --remove-section=.note --preserve-dates + + Verify sdcard mount point are mounted on your Linux PC: /media/$USER/rootfs + Copy kernel modules: + $> cp -rf lib/modules/* /media/$USER/rootfs/lib/modules/ + Depending of your Linux configuration, you may call the command under sudo + $> sudo cp -r lib/modules/* /media/$USER/rootfs/lib/modules/ + Don't forget to unmount properly sdcard + + At next runtime, don't forget to generate a list of module dependencies + (modules.dep) and a list of symbols provided by modules (modules.symbols): + $on board> depmod -a + Synchronize data on disk with memory + $on board> sync + Reboot the board in order to take update into account + $on board> reboot + +8. Useful information: +---------------------- +* How to re-generate kernel database on board: + $on board> depmod -a + (don't forget to synchronize the filesystem before to reboot) + $on board> sync + +* How to see the list of external kernel modules loaded: + $on board> lsmod + +* How to see information about kernel module: + $on board> modinfo ./install_artifact/lib/modules//kernel/drivers/leds/led-class-flash.ko +Example usage: +filename: ./install_artifact/lib/modules/4.9.23-g3e866b0/kernel/drivers/leds/led-class-flash.ko +license: GPL v2 +description: LED Flash class interface +author: Jacek Anaszewski +depends: +intree: Y +vermagic: 4.9.23-g3e866b0 SMP mod_unload ARMv7 p2v8 + diff --git a/recipes-kernel/linux/linux-stm32mp_4.19.bb b/recipes-kernel/linux/linux-stm32mp_4.19.bb new file mode 100644 index 0000000..4342dd6 --- /dev/null +++ b/recipes-kernel/linux/linux-stm32mp_4.19.bb @@ -0,0 +1,135 @@ +SUMMARY = "Linux STM32MP Kernel" +SECTION = "kernel" +LICENSE = "GPLv2" +LIC_FILES_CHKSUM = "file://COPYING;md5=bbea815ee2795b2f4230826c0c6b8814" + +include linux-stm32mp.inc + +SRC_URI = "https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.19.9.tar.xz" +SRC_URI[md5sum] = "d7e09d6be85ec8548c73e8713531e958" +SRC_URI[sha256sum] = "fc116cc6829c73944215d3b3ac0fc368dde9e8235b456744afffde001269dbf2" + +SRC_URI += " \ + file://${LINUX_VERSION}/4.19.9/0001-ARM-stm32mp1-r0-rc1-MACHINE.patch \ + file://${LINUX_VERSION}/4.19.9/0002-ARM-stm32mp1-r0-rc1-CLOCK.patch \ + file://${LINUX_VERSION}/4.19.9/0003-ARM-stm32mp1-r0-rc1-DMA.patch \ + file://${LINUX_VERSION}/4.19.9/0004-ARM-stm32mp1-r0-rc1-I2C.patch \ + file://${LINUX_VERSION}/4.19.9/0005-ARM-stm32mp1-r0-rc1-IIO.patch \ + file://${LINUX_VERSION}/4.19.9/0006-ARM-stm32mp1-r0-rc1-IRQ-Mailbox.patch \ + file://${LINUX_VERSION}/4.19.9/0007-ARM-stm32mp1-r0-rc1-INPUT-TTY.patch \ + file://${LINUX_VERSION}/4.19.9/0008-ARM-stm32mp1-r0-rc1-MFD.patch \ + file://${LINUX_VERSION}/4.19.9/0009-ARM-stm32mp1-r0-rc1-MMC-MTD.patch \ + file://${LINUX_VERSION}/4.19.9/0010-ARM-stm32mp1-r0-rc1-ETH.patch \ + file://${LINUX_VERSION}/4.19.9/0011-ARM-stm32mp1-r0-rc1-NVMEM.patch \ + file://${LINUX_VERSION}/4.19.9/0012-ARM-stm32mp1-r0-rc1-PINCTRL-PWM-RESET-RTC.patch \ + file://${LINUX_VERSION}/4.19.9/0013-ARM-stm32mp1-r0-rc1-REMOTEPROC-RPMSG.patch \ + file://${LINUX_VERSION}/4.19.9/0014-ARM-stm32mp1-r0-rc1-WATCHDOG.patch \ + file://${LINUX_VERSION}/4.19.9/0015-ARM-stm32mp1-r0-rc1-MISC.patch \ + file://${LINUX_VERSION}/4.19.9/0016-ARM-stm32mp1-r0-rc1-DEVICETREE.patch \ + file://${LINUX_VERSION}/4.19.9/0017-ARM-stm32mp1-r0-rc1-DEFCONFIG.patch \ + file://${LINUX_VERSION}/4.19.9/0018-ARM-stm32mp1-r0-rc2-DRM-KMS.patch \ + file://${LINUX_VERSION}/4.19.9/0019-ARM-stm32mp1-r0-rc2-SOUND.patch \ + file://${LINUX_VERSION}/4.19.9/0020-ARM-stm32mp1-r0-rc2-MEDIA.patch \ + file://${LINUX_VERSION}/4.19.9/0021-ARM-stm32mp1-r0-rc2-PINCTRL.patch \ + file://${LINUX_VERSION}/4.19.9/0022-ARM-stm32mp1-r0-rc2-MFD-IRQ.patch \ + file://${LINUX_VERSION}/4.19.9/0023-ARM-stm32mp1-r0-rc2-USB.patch \ + file://${LINUX_VERSION}/4.19.9/0024-ARM-stm32mp1-r0-rc2-THERMAL.patch \ + file://${LINUX_VERSION}/4.19.9/0025-ARM-stm32mp1-r0-rc2-REMOTEPROC.patch \ + file://${LINUX_VERSION}/4.19.9/0026-ARM-stm32mp1-r0-rc2-NET.patch \ + file://${LINUX_VERSION}/4.19.9/0027-ARM-stm32mp1-r0-rc2-HWCLK-SPI.patch \ + file://${LINUX_VERSION}/4.19.9/0028-ARM-stm32mp1-r0-rc2-MMC.patch \ + file://${LINUX_VERSION}/4.19.9/0029-ARM-stm32mp1-r0-rc2-HWSPINLOCK-IIO-I2C.patch \ + file://${LINUX_VERSION}/4.19.9/0030-ARM-stm32mp1-r0-rc2-DEVICETREE.patch \ + file://${LINUX_VERSION}/4.19.9/0031-ARM-stm32mp1-r0-rc2-DEFCONFIG.patch \ + file://${LINUX_VERSION}/4.19.9/0032-ARM-stm32mp1-r0-rc3-DMA.patch \ + file://${LINUX_VERSION}/4.19.9/0033-ARM-stm32mp1-r0-rc3-DISPLAY.patch \ + file://${LINUX_VERSION}/4.19.9/0034-ARM-stm32mp1-r0-rc3-ETH.patch \ + file://${LINUX_VERSION}/4.19.9/0035-ARM-stm32mp1-r0-rc3-IIO.patch \ + file://${LINUX_VERSION}/4.19.9/0036-ARM-stm32mp1-r0-rc3-INPUT-TTY.patch \ + file://${LINUX_VERSION}/4.19.9/0037-ARM-stm32mp1-r0-rc3-IRQ-Mailbox.patch \ + file://${LINUX_VERSION}/4.19.9/0038-ARM-stm32mp1-r0-rc3-MEDIA.patch \ + file://${LINUX_VERSION}/4.19.9/0039-ARM-stm32mp1-r0-rc3-MMC-MTD.patch \ + file://${LINUX_VERSION}/4.19.9/0040-ARM-stm32mp1-r0-rc3-PINCTRL-PWM-RESET-RTC.patch \ + file://${LINUX_VERSION}/4.19.9/0041-ARM-stm32mp1-r0-rc3-REMOTEPROC-RPMSG.patch \ + file://${LINUX_VERSION}/4.19.9/0042-ARM-stm32mp1-r0-rc3-SOUND.patch \ + file://${LINUX_VERSION}/4.19.9/0043-ARM-stm32mp1-r0-rc3-MISC.patch \ + file://${LINUX_VERSION}/4.19.9/0044-ARM-stm32mp1-r0-rc3-DEVICETREE.patch \ + file://${LINUX_VERSION}/4.19.9/0045-ARM-stm32mp1-r0-rc3-DEFCONFIG.patch \ + file://${LINUX_VERSION}/4.19.9/0046-ARM-stm32mp1-r0-rc4-MMC-MTD.patch \ + file://${LINUX_VERSION}/4.19.9/0047-ARM-stm32mp1-r0-rc4-PINCTRL-PWM-RESET-RTC.patch \ + file://${LINUX_VERSION}/4.19.9/0048-ARM-stm32mp1-r0-rc4-REMOTEPROC-RPMSG.patch \ + file://${LINUX_VERSION}/4.19.9/0049-ARM-stm32mp1-r0-rc4-SOUND.patch \ + file://${LINUX_VERSION}/4.19.9/0050-ARM-stm32mp1-r0-rc4-USB.patch \ + file://${LINUX_VERSION}/4.19.9/0051-ARM-stm32mp1-r0-rc4-DEVICETREE.patch \ + file://${LINUX_VERSION}/4.19.9/0052-ARM-stm32mp1-r0-rc4-DEFCONFIG.patch \ + \ + file://${LINUX_VERSION}/4.19.9/0053-ARM-stm32mp1-r0-rc4-hotfix-w903.1-DRIVERS.patch \ + file://${LINUX_VERSION}/4.19.9/0054-ARM-stm32mp1-r0-rc4-hotfix-w903.1-DEVICETREE.patch \ + file://${LINUX_VERSION}/4.19.9/0055-ARM-stm32mp1-r0-rc4-hotfix-w903.1-DEFCONFIG.patch \ + \ + file://${LINUX_VERSION}/4.19.9/0056-ARM-stm32mp1-r0-rc4-hotfix-w903.3-DRIVERS.patch \ + file://${LINUX_VERSION}/4.19.9/0057-ARM-stm32mp1-r0-rc4-hotfix-w903.3-DEVICETREE.patch \ + file://${LINUX_VERSION}/4.19.9/0058-ARM-stm32mp1-r0-rc4-hotfix-w903.3-DEFCONFIG.patch \ + \ + file://${LINUX_VERSION}/4.19.9/0059-ARM-stm32mp1-r0-rc4-hotfix-w904.3-DRIVERS.patch \ + file://${LINUX_VERSION}/4.19.9/0060-ARM-stm32mp1-r0-rc4-hotfix-w904.3-DEVICETREE.patch \ + \ + file://${LINUX_VERSION}/4.19.9/0061-ARM-stm32mp1-r0-rc4-hotfix-w904.5-DRIVERS.patch \ + file://${LINUX_VERSION}/4.19.9/0062-ARM-stm32mp1-r0-rc4-hotfix-w904.5-DEVICETREE.patch \ + \ + file://${LINUX_VERSION}/4.19.9/0063-ARM-stm32mp1-r0-rc4-hotfix-w905.2-DRIVERS.patch \ + file://${LINUX_VERSION}/4.19.9/0064-ARM-stm32mp1-r0-rc4-hotfix-w905.2-DEVICETREE.patch \ + " + +LINUX_VERSION = "4.19" + +PV = "${LINUX_VERSION}" + +S = "${WORKDIR}/linux-4.19.9" + +# --------------------------------- +# Configure devupstream class usage +# --------------------------------- +BBCLASSEXTEND = "devupstream:target" + +SRC_URI_class-devupstream = "git://github.com/STMicroelectronics/linux.git;protocol=https;branch=v${LINUX_VERSION}-stm32mp" +SRCREV_class-devupstream = "196201973b7048ccf75aa63ac3c3673f8b6ee1c1" + +# --------------------------------- +# Configure default preference to manage dynamic selection between tarball and github +# --------------------------------- +STM32MP_SOURCE_SELECTION ?= "tarball" + +DEFAULT_PREFERENCE = "${@bb.utils.contains('STM32MP_SOURCE_SELECTION', 'github', '-1', '1', d)}" + +# ------------------------------------------------------------- +# Defconfig +# +KERNEL_DEFCONFIG = "multi_v7_defconfig" +KERNEL_CONFIG_FRAGMENTS = "${@bb.utils.contains('KERNEL_DEFCONFIG', 'multi_v7_defconfig', '${S}/arch/arm/configs/fragment-01-multiv7_cleanup.config', '', d)}" +KERNEL_CONFIG_FRAGMENTS += "${@bb.utils.contains('KERNEL_DEFCONFIG', 'multi_v7_defconfig', '${S}/arch/arm/configs/fragment-02-multiv7_addons.config', '', d)}" +KERNEL_CONFIG_FRAGMENTS += "${@bb.utils.contains('DISTRO_FEATURES', 'systemd', '${WORKDIR}/fragments/4.19/fragment-03-systemd.config', '', d)} " +KERNEL_CONFIG_FRAGMENTS += "${@bb.utils.contains('COMBINED_FEATURES', 'optee', '${WORKDIR}/fragments/4.19/fragment-04-optee.config', '', d)}" +KERNEL_CONFIG_FRAGMENTS += "${WORKDIR}/fragments/4.19/fragment-05-modules.config" + +SRC_URI += "file://4.19/fragment-03-systemd.config;subdir=fragments" +SRC_URI += "file://4.19/fragment-04-optee.config;subdir=fragments" +SRC_URI += "file://4.19/fragment-05-modules.config;subdir=fragments" +# Don't forget to add/del for devupstream +SRC_URI_append_class-devupstream = " file://4.19/fragment-03-systemd.config;subdir=fragments " +SRC_URI_append_class-devupstream = " file://4.19/fragment-04-optee.config;subdir=fragments " +SRC_URI_append_class-devupstream = " file://4.19/fragment-05-modules.config;subdir=fragments " + +# ------------------------------------------------------------- +# Kernel Args +# +KERNEL_EXTRA_ARGS += "LOADADDR=${ST_KERNEL_LOADADDR}" + +# ------------------------------------------------------------- +# Archiver +# +inherit archiver +ARCHIVER_MODE[src] = "${@'original' if d.getVar('ST_ARCHIVER_ENABLE') == '1' else ''}" +SRC_URI =+ "file://README.HOW_TO.txt" + +inherit archiver_stm32mp_clean