From fc34f41d395d01f39adec5cda72053e274e796e4 Mon Sep 17 00:00:00 2001 From: Hal Feng Date: Thu, 9 Mar 2023 14:14:45 +0800 Subject: [PATCH 01/95] clk: starfive: Replace SOC_STARFIVE with ARCH_STARFIVE Using ARCH_FOO symbol is preferred than SOC_FOO. Set obj-y for starfive/ in Makefile, so the StarFive drivers can be compiled with COMPILE_TEST=y but ARCH_STARFIVE=n. Reviewed-by: Conor Dooley Signed-off-by: Hal Feng --- drivers/clk/Makefile | 2 +- drivers/clk/starfive/Kconfig | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index e3ca0d058a256..3bed88d67a3a0 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -117,7 +117,7 @@ obj-$(CONFIG_PLAT_SPEAR) += spear/ obj-y += sprd/ obj-$(CONFIG_ARCH_STI) += st/ obj-$(CONFIG_ARCH_STM32) += stm32/ -obj-$(CONFIG_SOC_STARFIVE) += starfive/ +obj-y += starfive/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-y += sunxi-ng/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ diff --git a/drivers/clk/starfive/Kconfig b/drivers/clk/starfive/Kconfig index 003bd2d56ce7e..ef3517f4a2764 100644 --- a/drivers/clk/starfive/Kconfig +++ b/drivers/clk/starfive/Kconfig @@ -2,8 +2,8 @@ config CLK_STARFIVE_JH7100 bool "StarFive JH7100 clock support" - depends on SOC_STARFIVE || COMPILE_TEST - default SOC_STARFIVE + depends on ARCH_STARFIVE || COMPILE_TEST + default ARCH_STARFIVE help Say yes here to support the clock controller on the StarFive JH7100 SoC. @@ -11,7 +11,7 @@ config CLK_STARFIVE_JH7100 config CLK_STARFIVE_JH7100_AUDIO tristate "StarFive JH7100 audio clock support" depends on CLK_STARFIVE_JH7100 - default m if SOC_STARFIVE + default m if ARCH_STARFIVE help Say Y or M here to support the audio clocks on the StarFive JH7100 SoC. From a0178bfa62325df1280db16ae9d59c46345a8e6b Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sun, 10 Jul 2022 11:07:13 +0200 Subject: [PATCH 02/95] clk: starfive: Factor out common JH7100 and JH7110 code The clock control registers on the StarFive JH7100 and JH7110 work identically, so factor out the code then drivers for the two SoCs can share it without depending on each other. No functional change. Tested-by: Tommaso Merciai Reviewed-by: Conor Dooley Signed-off-by: Emil Renner Berthing Signed-off-by: Hal Feng --- MAINTAINERS | 14 +- drivers/clk/starfive/Kconfig | 5 + drivers/clk/starfive/Makefile | 3 +- drivers/clk/starfive/clk-starfive-jh7100.c | 325 -------------------- drivers/clk/starfive/clk-starfive-jh7100.h | 2 + drivers/clk/starfive/clk-starfive-jh71x0.c | 333 +++++++++++++++++++++ 6 files changed, 349 insertions(+), 333 deletions(-) create mode 100644 drivers/clk/starfive/clk-starfive-jh71x0.c diff --git a/MAINTAINERS b/MAINTAINERS index d8ebab595b2a9..d9ad43857ac53 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19905,19 +19905,19 @@ M: Emil Renner Berthing S: Maintained F: arch/riscv/boot/dts/starfive/ -STARFIVE JH7100 CLOCK DRIVERS -M: Emil Renner Berthing -S: Maintained -F: Documentation/devicetree/bindings/clock/starfive,jh7100-*.yaml -F: drivers/clk/starfive/clk-starfive-jh7100* -F: include/dt-bindings/clock/starfive-jh7100*.h - STARFIVE JH7110 MMC/SD/SDIO DRIVER M: William Qiu S: Supported F: Documentation/devicetree/bindings/mmc/starfive* F: drivers/mmc/host/dw_mmc-starfive.c +STARFIVE JH71X0 CLOCK DRIVERS +M: Emil Renner Berthing +S: Maintained +F: Documentation/devicetree/bindings/clock/starfive,jh7100-*.yaml +F: drivers/clk/starfive/clk-starfive-jh71* +F: include/dt-bindings/clock/starfive-jh7100*.h + STARFIVE JH71X0 PINCTRL DRIVERS M: Emil Renner Berthing M: Jianlong Huang diff --git a/drivers/clk/starfive/Kconfig b/drivers/clk/starfive/Kconfig index ef3517f4a2764..3ceeb19b5edad 100644 --- a/drivers/clk/starfive/Kconfig +++ b/drivers/clk/starfive/Kconfig @@ -1,8 +1,12 @@ # SPDX-License-Identifier: GPL-2.0 +config CLK_STARFIVE_JH71X0 + bool + config CLK_STARFIVE_JH7100 bool "StarFive JH7100 clock support" depends on ARCH_STARFIVE || COMPILE_TEST + select CLK_STARFIVE_JH71X0 default ARCH_STARFIVE help Say yes here to support the clock controller on the StarFive JH7100 @@ -11,6 +15,7 @@ config CLK_STARFIVE_JH7100 config CLK_STARFIVE_JH7100_AUDIO tristate "StarFive JH7100 audio clock support" depends on CLK_STARFIVE_JH7100 + select CLK_STARFIVE_JH71X0 default m if ARCH_STARFIVE help Say Y or M here to support the audio clocks on the StarFive JH7100 diff --git a/drivers/clk/starfive/Makefile b/drivers/clk/starfive/Makefile index 0fa8ecb9ec1c6..82edfa9f9cb81 100644 --- a/drivers/clk/starfive/Makefile +++ b/drivers/clk/starfive/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -# StarFive Clock +obj-$(CONFIG_CLK_STARFIVE_JH71X0) += clk-starfive-jh71x0.o + obj-$(CONFIG_CLK_STARFIVE_JH7100) += clk-starfive-jh7100.o obj-$(CONFIG_CLK_STARFIVE_JH7100_AUDIO) += clk-starfive-jh7100-audio.o diff --git a/drivers/clk/starfive/clk-starfive-jh7100.c b/drivers/clk/starfive/clk-starfive-jh7100.c index 691aeebc70927..eea52f16af0d8 100644 --- a/drivers/clk/starfive/clk-starfive-jh7100.c +++ b/drivers/clk/starfive/clk-starfive-jh7100.c @@ -7,15 +7,10 @@ * Copyright (C) 2021 Emil Renner Berthing */ -#include #include -#include #include #include -#include -#include #include -#include #include #include @@ -269,326 +264,6 @@ static const struct jh7100_clk_data jh7100_clk_data[] __initconst = { JH7100_GATE(JH7100_CLK_SYSERR_APB, "syserr_apb", 0, JH7100_CLK_APB2_BUS), }; -static struct jh7100_clk *jh7100_clk_from(struct clk_hw *hw) -{ - return container_of(hw, struct jh7100_clk, hw); -} - -static struct jh7100_clk_priv *jh7100_priv_from(struct jh7100_clk *clk) -{ - return container_of(clk, struct jh7100_clk_priv, reg[clk->idx]); -} - -static u32 jh7100_clk_reg_get(struct jh7100_clk *clk) -{ - struct jh7100_clk_priv *priv = jh7100_priv_from(clk); - void __iomem *reg = priv->base + 4 * clk->idx; - - return readl_relaxed(reg); -} - -static void jh7100_clk_reg_rmw(struct jh7100_clk *clk, u32 mask, u32 value) -{ - struct jh7100_clk_priv *priv = jh7100_priv_from(clk); - void __iomem *reg = priv->base + 4 * clk->idx; - unsigned long flags; - - spin_lock_irqsave(&priv->rmw_lock, flags); - value |= readl_relaxed(reg) & ~mask; - writel_relaxed(value, reg); - spin_unlock_irqrestore(&priv->rmw_lock, flags); -} - -static int jh7100_clk_enable(struct clk_hw *hw) -{ - struct jh7100_clk *clk = jh7100_clk_from(hw); - - jh7100_clk_reg_rmw(clk, JH7100_CLK_ENABLE, JH7100_CLK_ENABLE); - return 0; -} - -static void jh7100_clk_disable(struct clk_hw *hw) -{ - struct jh7100_clk *clk = jh7100_clk_from(hw); - - jh7100_clk_reg_rmw(clk, JH7100_CLK_ENABLE, 0); -} - -static int jh7100_clk_is_enabled(struct clk_hw *hw) -{ - struct jh7100_clk *clk = jh7100_clk_from(hw); - - return !!(jh7100_clk_reg_get(clk) & JH7100_CLK_ENABLE); -} - -static unsigned long jh7100_clk_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct jh7100_clk *clk = jh7100_clk_from(hw); - u32 div = jh7100_clk_reg_get(clk) & JH7100_CLK_DIV_MASK; - - return div ? parent_rate / div : 0; -} - -static int jh7100_clk_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - struct jh7100_clk *clk = jh7100_clk_from(hw); - unsigned long parent = req->best_parent_rate; - unsigned long rate = clamp(req->rate, req->min_rate, req->max_rate); - unsigned long div = min_t(unsigned long, DIV_ROUND_UP(parent, rate), clk->max_div); - unsigned long result = parent / div; - - /* - * we want the result clamped by min_rate and max_rate if possible: - * case 1: div hits the max divider value, which means it's less than - * parent / rate, so the result is greater than rate and min_rate in - * particular. we can't do anything about result > max_rate because the - * divider doesn't go any further. - * case 2: div = DIV_ROUND_UP(parent, rate) which means the result is - * always lower or equal to rate and max_rate. however the result may - * turn out lower than min_rate, but then the next higher rate is fine: - * div - 1 = ceil(parent / rate) - 1 < parent / rate - * and thus - * min_rate <= rate < parent / (div - 1) - */ - if (result < req->min_rate && div > 1) - result = parent / (div - 1); - - req->rate = result; - return 0; -} - -static int jh7100_clk_set_rate(struct clk_hw *hw, - unsigned long rate, - unsigned long parent_rate) -{ - struct jh7100_clk *clk = jh7100_clk_from(hw); - unsigned long div = clamp(DIV_ROUND_CLOSEST(parent_rate, rate), - 1UL, (unsigned long)clk->max_div); - - jh7100_clk_reg_rmw(clk, JH7100_CLK_DIV_MASK, div); - return 0; -} - -static unsigned long jh7100_clk_frac_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct jh7100_clk *clk = jh7100_clk_from(hw); - u32 reg = jh7100_clk_reg_get(clk); - unsigned long div100 = 100 * (reg & JH7100_CLK_INT_MASK) + - ((reg & JH7100_CLK_FRAC_MASK) >> JH7100_CLK_FRAC_SHIFT); - - return (div100 >= JH7100_CLK_FRAC_MIN) ? 100 * parent_rate / div100 : 0; -} - -static int jh7100_clk_frac_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - unsigned long parent100 = 100 * req->best_parent_rate; - unsigned long rate = clamp(req->rate, req->min_rate, req->max_rate); - unsigned long div100 = clamp(DIV_ROUND_CLOSEST(parent100, rate), - JH7100_CLK_FRAC_MIN, JH7100_CLK_FRAC_MAX); - unsigned long result = parent100 / div100; - - /* clamp the result as in jh7100_clk_determine_rate() above */ - if (result > req->max_rate && div100 < JH7100_CLK_FRAC_MAX) - result = parent100 / (div100 + 1); - if (result < req->min_rate && div100 > JH7100_CLK_FRAC_MIN) - result = parent100 / (div100 - 1); - - req->rate = result; - return 0; -} - -static int jh7100_clk_frac_set_rate(struct clk_hw *hw, - unsigned long rate, - unsigned long parent_rate) -{ - struct jh7100_clk *clk = jh7100_clk_from(hw); - unsigned long div100 = clamp(DIV_ROUND_CLOSEST(100 * parent_rate, rate), - JH7100_CLK_FRAC_MIN, JH7100_CLK_FRAC_MAX); - u32 value = ((div100 % 100) << JH7100_CLK_FRAC_SHIFT) | (div100 / 100); - - jh7100_clk_reg_rmw(clk, JH7100_CLK_DIV_MASK, value); - return 0; -} - -static u8 jh7100_clk_get_parent(struct clk_hw *hw) -{ - struct jh7100_clk *clk = jh7100_clk_from(hw); - u32 value = jh7100_clk_reg_get(clk); - - return (value & JH7100_CLK_MUX_MASK) >> JH7100_CLK_MUX_SHIFT; -} - -static int jh7100_clk_set_parent(struct clk_hw *hw, u8 index) -{ - struct jh7100_clk *clk = jh7100_clk_from(hw); - u32 value = (u32)index << JH7100_CLK_MUX_SHIFT; - - jh7100_clk_reg_rmw(clk, JH7100_CLK_MUX_MASK, value); - return 0; -} - -static int jh7100_clk_mux_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - return clk_mux_determine_rate_flags(hw, req, 0); -} - -static int jh7100_clk_get_phase(struct clk_hw *hw) -{ - struct jh7100_clk *clk = jh7100_clk_from(hw); - u32 value = jh7100_clk_reg_get(clk); - - return (value & JH7100_CLK_INVERT) ? 180 : 0; -} - -static int jh7100_clk_set_phase(struct clk_hw *hw, int degrees) -{ - struct jh7100_clk *clk = jh7100_clk_from(hw); - u32 value; - - if (degrees == 0) - value = 0; - else if (degrees == 180) - value = JH7100_CLK_INVERT; - else - return -EINVAL; - - jh7100_clk_reg_rmw(clk, JH7100_CLK_INVERT, value); - return 0; -} - -#ifdef CONFIG_DEBUG_FS -static void jh7100_clk_debug_init(struct clk_hw *hw, struct dentry *dentry) -{ - static const struct debugfs_reg32 jh7100_clk_reg = { - .name = "CTRL", - .offset = 0, - }; - struct jh7100_clk *clk = jh7100_clk_from(hw); - struct jh7100_clk_priv *priv = jh7100_priv_from(clk); - struct debugfs_regset32 *regset; - - regset = devm_kzalloc(priv->dev, sizeof(*regset), GFP_KERNEL); - if (!regset) - return; - - regset->regs = &jh7100_clk_reg; - regset->nregs = 1; - regset->base = priv->base + 4 * clk->idx; - - debugfs_create_regset32("registers", 0400, dentry, regset); -} -#else -#define jh7100_clk_debug_init NULL -#endif - -static const struct clk_ops jh7100_clk_gate_ops = { - .enable = jh7100_clk_enable, - .disable = jh7100_clk_disable, - .is_enabled = jh7100_clk_is_enabled, - .debug_init = jh7100_clk_debug_init, -}; - -static const struct clk_ops jh7100_clk_div_ops = { - .recalc_rate = jh7100_clk_recalc_rate, - .determine_rate = jh7100_clk_determine_rate, - .set_rate = jh7100_clk_set_rate, - .debug_init = jh7100_clk_debug_init, -}; - -static const struct clk_ops jh7100_clk_fdiv_ops = { - .recalc_rate = jh7100_clk_frac_recalc_rate, - .determine_rate = jh7100_clk_frac_determine_rate, - .set_rate = jh7100_clk_frac_set_rate, - .debug_init = jh7100_clk_debug_init, -}; - -static const struct clk_ops jh7100_clk_gdiv_ops = { - .enable = jh7100_clk_enable, - .disable = jh7100_clk_disable, - .is_enabled = jh7100_clk_is_enabled, - .recalc_rate = jh7100_clk_recalc_rate, - .determine_rate = jh7100_clk_determine_rate, - .set_rate = jh7100_clk_set_rate, - .debug_init = jh7100_clk_debug_init, -}; - -static const struct clk_ops jh7100_clk_mux_ops = { - .determine_rate = jh7100_clk_mux_determine_rate, - .set_parent = jh7100_clk_set_parent, - .get_parent = jh7100_clk_get_parent, - .debug_init = jh7100_clk_debug_init, -}; - -static const struct clk_ops jh7100_clk_gmux_ops = { - .enable = jh7100_clk_enable, - .disable = jh7100_clk_disable, - .is_enabled = jh7100_clk_is_enabled, - .determine_rate = jh7100_clk_mux_determine_rate, - .set_parent = jh7100_clk_set_parent, - .get_parent = jh7100_clk_get_parent, - .debug_init = jh7100_clk_debug_init, -}; - -static const struct clk_ops jh7100_clk_mdiv_ops = { - .recalc_rate = jh7100_clk_recalc_rate, - .determine_rate = jh7100_clk_determine_rate, - .get_parent = jh7100_clk_get_parent, - .set_parent = jh7100_clk_set_parent, - .set_rate = jh7100_clk_set_rate, - .debug_init = jh7100_clk_debug_init, -}; - -static const struct clk_ops jh7100_clk_gmd_ops = { - .enable = jh7100_clk_enable, - .disable = jh7100_clk_disable, - .is_enabled = jh7100_clk_is_enabled, - .recalc_rate = jh7100_clk_recalc_rate, - .determine_rate = jh7100_clk_determine_rate, - .get_parent = jh7100_clk_get_parent, - .set_parent = jh7100_clk_set_parent, - .set_rate = jh7100_clk_set_rate, - .debug_init = jh7100_clk_debug_init, -}; - -static const struct clk_ops jh7100_clk_inv_ops = { - .get_phase = jh7100_clk_get_phase, - .set_phase = jh7100_clk_set_phase, - .debug_init = jh7100_clk_debug_init, -}; - -const struct clk_ops *starfive_jh7100_clk_ops(u32 max) -{ - if (max & JH7100_CLK_DIV_MASK) { - if (max & JH7100_CLK_MUX_MASK) { - if (max & JH7100_CLK_ENABLE) - return &jh7100_clk_gmd_ops; - return &jh7100_clk_mdiv_ops; - } - if (max & JH7100_CLK_ENABLE) - return &jh7100_clk_gdiv_ops; - if (max == JH7100_CLK_FRAC_MAX) - return &jh7100_clk_fdiv_ops; - return &jh7100_clk_div_ops; - } - - if (max & JH7100_CLK_MUX_MASK) { - if (max & JH7100_CLK_ENABLE) - return &jh7100_clk_gmux_ops; - return &jh7100_clk_mux_ops; - } - - if (max & JH7100_CLK_ENABLE) - return &jh7100_clk_gate_ops; - - return &jh7100_clk_inv_ops; -} -EXPORT_SYMBOL_GPL(starfive_jh7100_clk_ops); - static struct clk_hw *jh7100_clk_get(struct of_phandle_args *clkspec, void *data) { struct jh7100_clk_priv *priv = data; diff --git a/drivers/clk/starfive/clk-starfive-jh7100.h b/drivers/clk/starfive/clk-starfive-jh7100.h index f116be5740a5f..a8ba6e25b5ce2 100644 --- a/drivers/clk/starfive/clk-starfive-jh7100.h +++ b/drivers/clk/starfive/clk-starfive-jh7100.h @@ -4,6 +4,8 @@ #include #include +#include +#include /* register fields */ #define JH7100_CLK_ENABLE BIT(31) diff --git a/drivers/clk/starfive/clk-starfive-jh71x0.c b/drivers/clk/starfive/clk-starfive-jh71x0.c new file mode 100644 index 0000000000000..6c07b61b4a32c --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh71x0.c @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JH7100 Clock Generator Driver + * + * Copyright (C) 2021-2022 Emil Renner Berthing + */ + +#include +#include +#include +#include + +#include "clk-starfive-jh7100.h" + +static struct jh7100_clk *jh7100_clk_from(struct clk_hw *hw) +{ + return container_of(hw, struct jh7100_clk, hw); +} + +static struct jh7100_clk_priv *jh7100_priv_from(struct jh7100_clk *clk) +{ + return container_of(clk, struct jh7100_clk_priv, reg[clk->idx]); +} + +static u32 jh7100_clk_reg_get(struct jh7100_clk *clk) +{ + struct jh7100_clk_priv *priv = jh7100_priv_from(clk); + void __iomem *reg = priv->base + 4 * clk->idx; + + return readl_relaxed(reg); +} + +static void jh7100_clk_reg_rmw(struct jh7100_clk *clk, u32 mask, u32 value) +{ + struct jh7100_clk_priv *priv = jh7100_priv_from(clk); + void __iomem *reg = priv->base + 4 * clk->idx; + unsigned long flags; + + spin_lock_irqsave(&priv->rmw_lock, flags); + value |= readl_relaxed(reg) & ~mask; + writel_relaxed(value, reg); + spin_unlock_irqrestore(&priv->rmw_lock, flags); +} + +static int jh7100_clk_enable(struct clk_hw *hw) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + + jh7100_clk_reg_rmw(clk, JH7100_CLK_ENABLE, JH7100_CLK_ENABLE); + return 0; +} + +static void jh7100_clk_disable(struct clk_hw *hw) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + + jh7100_clk_reg_rmw(clk, JH7100_CLK_ENABLE, 0); +} + +static int jh7100_clk_is_enabled(struct clk_hw *hw) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + + return !!(jh7100_clk_reg_get(clk) & JH7100_CLK_ENABLE); +} + +static unsigned long jh7100_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + u32 div = jh7100_clk_reg_get(clk) & JH7100_CLK_DIV_MASK; + + return div ? parent_rate / div : 0; +} + +static int jh7100_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + unsigned long parent = req->best_parent_rate; + unsigned long rate = clamp(req->rate, req->min_rate, req->max_rate); + unsigned long div = min_t(unsigned long, DIV_ROUND_UP(parent, rate), clk->max_div); + unsigned long result = parent / div; + + /* + * we want the result clamped by min_rate and max_rate if possible: + * case 1: div hits the max divider value, which means it's less than + * parent / rate, so the result is greater than rate and min_rate in + * particular. we can't do anything about result > max_rate because the + * divider doesn't go any further. + * case 2: div = DIV_ROUND_UP(parent, rate) which means the result is + * always lower or equal to rate and max_rate. however the result may + * turn out lower than min_rate, but then the next higher rate is fine: + * div - 1 = ceil(parent / rate) - 1 < parent / rate + * and thus + * min_rate <= rate < parent / (div - 1) + */ + if (result < req->min_rate && div > 1) + result = parent / (div - 1); + + req->rate = result; + return 0; +} + +static int jh7100_clk_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + unsigned long div = clamp(DIV_ROUND_CLOSEST(parent_rate, rate), + 1UL, (unsigned long)clk->max_div); + + jh7100_clk_reg_rmw(clk, JH7100_CLK_DIV_MASK, div); + return 0; +} + +static unsigned long jh7100_clk_frac_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + u32 reg = jh7100_clk_reg_get(clk); + unsigned long div100 = 100 * (reg & JH7100_CLK_INT_MASK) + + ((reg & JH7100_CLK_FRAC_MASK) >> JH7100_CLK_FRAC_SHIFT); + + return (div100 >= JH7100_CLK_FRAC_MIN) ? 100 * parent_rate / div100 : 0; +} + +static int jh7100_clk_frac_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + unsigned long parent100 = 100 * req->best_parent_rate; + unsigned long rate = clamp(req->rate, req->min_rate, req->max_rate); + unsigned long div100 = clamp(DIV_ROUND_CLOSEST(parent100, rate), + JH7100_CLK_FRAC_MIN, JH7100_CLK_FRAC_MAX); + unsigned long result = parent100 / div100; + + /* clamp the result as in jh7100_clk_determine_rate() above */ + if (result > req->max_rate && div100 < JH7100_CLK_FRAC_MAX) + result = parent100 / (div100 + 1); + if (result < req->min_rate && div100 > JH7100_CLK_FRAC_MIN) + result = parent100 / (div100 - 1); + + req->rate = result; + return 0; +} + +static int jh7100_clk_frac_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + unsigned long div100 = clamp(DIV_ROUND_CLOSEST(100 * parent_rate, rate), + JH7100_CLK_FRAC_MIN, JH7100_CLK_FRAC_MAX); + u32 value = ((div100 % 100) << JH7100_CLK_FRAC_SHIFT) | (div100 / 100); + + jh7100_clk_reg_rmw(clk, JH7100_CLK_DIV_MASK, value); + return 0; +} + +static u8 jh7100_clk_get_parent(struct clk_hw *hw) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + u32 value = jh7100_clk_reg_get(clk); + + return (value & JH7100_CLK_MUX_MASK) >> JH7100_CLK_MUX_SHIFT; +} + +static int jh7100_clk_set_parent(struct clk_hw *hw, u8 index) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + u32 value = (u32)index << JH7100_CLK_MUX_SHIFT; + + jh7100_clk_reg_rmw(clk, JH7100_CLK_MUX_MASK, value); + return 0; +} + +static int jh7100_clk_mux_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + return clk_mux_determine_rate_flags(hw, req, 0); +} + +static int jh7100_clk_get_phase(struct clk_hw *hw) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + u32 value = jh7100_clk_reg_get(clk); + + return (value & JH7100_CLK_INVERT) ? 180 : 0; +} + +static int jh7100_clk_set_phase(struct clk_hw *hw, int degrees) +{ + struct jh7100_clk *clk = jh7100_clk_from(hw); + u32 value; + + if (degrees == 0) + value = 0; + else if (degrees == 180) + value = JH7100_CLK_INVERT; + else + return -EINVAL; + + jh7100_clk_reg_rmw(clk, JH7100_CLK_INVERT, value); + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static void jh7100_clk_debug_init(struct clk_hw *hw, struct dentry *dentry) +{ + static const struct debugfs_reg32 jh7100_clk_reg = { + .name = "CTRL", + .offset = 0, + }; + struct jh7100_clk *clk = jh7100_clk_from(hw); + struct jh7100_clk_priv *priv = jh7100_priv_from(clk); + struct debugfs_regset32 *regset; + + regset = devm_kzalloc(priv->dev, sizeof(*regset), GFP_KERNEL); + if (!regset) + return; + + regset->regs = &jh7100_clk_reg; + regset->nregs = 1; + regset->base = priv->base + 4 * clk->idx; + + debugfs_create_regset32("registers", 0400, dentry, regset); +} +#else +#define jh7100_clk_debug_init NULL +#endif + +static const struct clk_ops jh7100_clk_gate_ops = { + .enable = jh7100_clk_enable, + .disable = jh7100_clk_disable, + .is_enabled = jh7100_clk_is_enabled, + .debug_init = jh7100_clk_debug_init, +}; + +static const struct clk_ops jh7100_clk_div_ops = { + .recalc_rate = jh7100_clk_recalc_rate, + .determine_rate = jh7100_clk_determine_rate, + .set_rate = jh7100_clk_set_rate, + .debug_init = jh7100_clk_debug_init, +}; + +static const struct clk_ops jh7100_clk_fdiv_ops = { + .recalc_rate = jh7100_clk_frac_recalc_rate, + .determine_rate = jh7100_clk_frac_determine_rate, + .set_rate = jh7100_clk_frac_set_rate, + .debug_init = jh7100_clk_debug_init, +}; + +static const struct clk_ops jh7100_clk_gdiv_ops = { + .enable = jh7100_clk_enable, + .disable = jh7100_clk_disable, + .is_enabled = jh7100_clk_is_enabled, + .recalc_rate = jh7100_clk_recalc_rate, + .determine_rate = jh7100_clk_determine_rate, + .set_rate = jh7100_clk_set_rate, + .debug_init = jh7100_clk_debug_init, +}; + +static const struct clk_ops jh7100_clk_mux_ops = { + .determine_rate = jh7100_clk_mux_determine_rate, + .set_parent = jh7100_clk_set_parent, + .get_parent = jh7100_clk_get_parent, + .debug_init = jh7100_clk_debug_init, +}; + +static const struct clk_ops jh7100_clk_gmux_ops = { + .enable = jh7100_clk_enable, + .disable = jh7100_clk_disable, + .is_enabled = jh7100_clk_is_enabled, + .determine_rate = jh7100_clk_mux_determine_rate, + .set_parent = jh7100_clk_set_parent, + .get_parent = jh7100_clk_get_parent, + .debug_init = jh7100_clk_debug_init, +}; + +static const struct clk_ops jh7100_clk_mdiv_ops = { + .recalc_rate = jh7100_clk_recalc_rate, + .determine_rate = jh7100_clk_determine_rate, + .get_parent = jh7100_clk_get_parent, + .set_parent = jh7100_clk_set_parent, + .set_rate = jh7100_clk_set_rate, + .debug_init = jh7100_clk_debug_init, +}; + +static const struct clk_ops jh7100_clk_gmd_ops = { + .enable = jh7100_clk_enable, + .disable = jh7100_clk_disable, + .is_enabled = jh7100_clk_is_enabled, + .recalc_rate = jh7100_clk_recalc_rate, + .determine_rate = jh7100_clk_determine_rate, + .get_parent = jh7100_clk_get_parent, + .set_parent = jh7100_clk_set_parent, + .set_rate = jh7100_clk_set_rate, + .debug_init = jh7100_clk_debug_init, +}; + +static const struct clk_ops jh7100_clk_inv_ops = { + .get_phase = jh7100_clk_get_phase, + .set_phase = jh7100_clk_set_phase, + .debug_init = jh7100_clk_debug_init, +}; + +const struct clk_ops *starfive_jh7100_clk_ops(u32 max) +{ + if (max & JH7100_CLK_DIV_MASK) { + if (max & JH7100_CLK_MUX_MASK) { + if (max & JH7100_CLK_ENABLE) + return &jh7100_clk_gmd_ops; + return &jh7100_clk_mdiv_ops; + } + if (max & JH7100_CLK_ENABLE) + return &jh7100_clk_gdiv_ops; + if (max == JH7100_CLK_FRAC_MAX) + return &jh7100_clk_fdiv_ops; + return &jh7100_clk_div_ops; + } + + if (max & JH7100_CLK_MUX_MASK) { + if (max & JH7100_CLK_ENABLE) + return &jh7100_clk_gmux_ops; + return &jh7100_clk_mux_ops; + } + + if (max & JH7100_CLK_ENABLE) + return &jh7100_clk_gate_ops; + + return &jh7100_clk_inv_ops; +} +EXPORT_SYMBOL_GPL(starfive_jh7100_clk_ops); From 3f402febe359dd0c02aa00c01024886ff09252e7 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Tue, 1 Nov 2022 10:27:02 +0800 Subject: [PATCH 03/95] clk: starfive: Rename clk-starfive-jh7100.h to clk-starfive-jh71x0.h Rename clk-starfive-jh7100.h to clk-starfive-jh71x0.h for making the code to be common. Tested-by: Tommaso Merciai Reviewed-by: Conor Dooley Signed-off-by: Emil Renner Berthing Signed-off-by: Hal Feng --- drivers/clk/starfive/clk-starfive-jh7100-audio.c | 2 +- drivers/clk/starfive/clk-starfive-jh7100.c | 2 +- drivers/clk/starfive/clk-starfive-jh71x0.c | 2 +- .../starfive/{clk-starfive-jh7100.h => clk-starfive-jh71x0.h} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename drivers/clk/starfive/{clk-starfive-jh7100.h => clk-starfive-jh71x0.h} (100%) diff --git a/drivers/clk/starfive/clk-starfive-jh7100-audio.c b/drivers/clk/starfive/clk-starfive-jh7100-audio.c index 8473a65e219be..db0d9533bd9c6 100644 --- a/drivers/clk/starfive/clk-starfive-jh7100-audio.c +++ b/drivers/clk/starfive/clk-starfive-jh7100-audio.c @@ -16,7 +16,7 @@ #include -#include "clk-starfive-jh7100.h" +#include "clk-starfive-jh71x0.h" /* external clocks */ #define JH7100_AUDCLK_AUDIO_SRC (JH7100_AUDCLK_END + 0) diff --git a/drivers/clk/starfive/clk-starfive-jh7100.c b/drivers/clk/starfive/clk-starfive-jh7100.c index eea52f16af0d8..662eb8f74c120 100644 --- a/drivers/clk/starfive/clk-starfive-jh7100.c +++ b/drivers/clk/starfive/clk-starfive-jh7100.c @@ -15,7 +15,7 @@ #include -#include "clk-starfive-jh7100.h" +#include "clk-starfive-jh71x0.h" /* external clocks */ #define JH7100_CLK_OSC_SYS (JH7100_CLK_END + 0) diff --git a/drivers/clk/starfive/clk-starfive-jh71x0.c b/drivers/clk/starfive/clk-starfive-jh71x0.c index 6c07b61b4a32c..0140bdf27a013 100644 --- a/drivers/clk/starfive/clk-starfive-jh71x0.c +++ b/drivers/clk/starfive/clk-starfive-jh71x0.c @@ -10,7 +10,7 @@ #include #include -#include "clk-starfive-jh7100.h" +#include "clk-starfive-jh71x0.h" static struct jh7100_clk *jh7100_clk_from(struct clk_hw *hw) { diff --git a/drivers/clk/starfive/clk-starfive-jh7100.h b/drivers/clk/starfive/clk-starfive-jh71x0.h similarity index 100% rename from drivers/clk/starfive/clk-starfive-jh7100.h rename to drivers/clk/starfive/clk-starfive-jh71x0.h From 5aa27d1ea67e2f17e573bd23daec67b595903985 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Tue, 1 Nov 2022 10:27:02 +0800 Subject: [PATCH 04/95] clk: starfive: Rename "jh7100" to "jh71x0" for the common code Rename some variables from "jh7100" or "JH7100" to "jh71x0" or "JH71X0". Tested-by: Tommaso Merciai Reviewed-by: Conor Dooley Signed-off-by: Emil Renner Berthing Signed-off-by: Hal Feng --- .../clk/starfive/clk-starfive-jh7100-audio.c | 72 ++-- drivers/clk/starfive/clk-starfive-jh7100.c | 389 +++++++++--------- drivers/clk/starfive/clk-starfive-jh71x0.c | 282 ++++++------- drivers/clk/starfive/clk-starfive-jh71x0.h | 81 ++-- 4 files changed, 418 insertions(+), 406 deletions(-) diff --git a/drivers/clk/starfive/clk-starfive-jh7100-audio.c b/drivers/clk/starfive/clk-starfive-jh7100-audio.c index db0d9533bd9c6..02aefb7264f8f 100644 --- a/drivers/clk/starfive/clk-starfive-jh7100-audio.c +++ b/drivers/clk/starfive/clk-starfive-jh7100-audio.c @@ -28,66 +28,66 @@ #define JH7100_AUDCLK_I2SDAC_LRCLK_IOPAD (JH7100_AUDCLK_END + 6) #define JH7100_AUDCLK_VAD_INTMEM (JH7100_AUDCLK_END + 7) -static const struct jh7100_clk_data jh7100_audclk_data[] = { - JH7100__GMD(JH7100_AUDCLK_ADC_MCLK, "adc_mclk", 0, 15, 2, +static const struct jh71x0_clk_data jh7100_audclk_data[] = { + JH71X0__GMD(JH7100_AUDCLK_ADC_MCLK, "adc_mclk", 0, 15, 2, JH7100_AUDCLK_AUDIO_SRC, JH7100_AUDCLK_AUDIO_12288), - JH7100__GMD(JH7100_AUDCLK_I2S1_MCLK, "i2s1_mclk", 0, 15, 2, + JH71X0__GMD(JH7100_AUDCLK_I2S1_MCLK, "i2s1_mclk", 0, 15, 2, JH7100_AUDCLK_AUDIO_SRC, JH7100_AUDCLK_AUDIO_12288), - JH7100_GATE(JH7100_AUDCLK_I2SADC_APB, "i2sadc_apb", 0, JH7100_AUDCLK_APB0_BUS), - JH7100_MDIV(JH7100_AUDCLK_I2SADC_BCLK, "i2sadc_bclk", 31, 2, + JH71X0_GATE(JH7100_AUDCLK_I2SADC_APB, "i2sadc_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH71X0_MDIV(JH7100_AUDCLK_I2SADC_BCLK, "i2sadc_bclk", 31, 2, JH7100_AUDCLK_ADC_MCLK, JH7100_AUDCLK_I2SADC_BCLK_IOPAD), - JH7100__INV(JH7100_AUDCLK_I2SADC_BCLK_N, "i2sadc_bclk_n", JH7100_AUDCLK_I2SADC_BCLK), - JH7100_MDIV(JH7100_AUDCLK_I2SADC_LRCLK, "i2sadc_lrclk", 63, 3, + JH71X0__INV(JH7100_AUDCLK_I2SADC_BCLK_N, "i2sadc_bclk_n", JH7100_AUDCLK_I2SADC_BCLK), + JH71X0_MDIV(JH7100_AUDCLK_I2SADC_LRCLK, "i2sadc_lrclk", 63, 3, JH7100_AUDCLK_I2SADC_BCLK_N, JH7100_AUDCLK_I2SADC_LRCLK_IOPAD, JH7100_AUDCLK_I2SADC_BCLK), - JH7100_GATE(JH7100_AUDCLK_PDM_APB, "pdm_apb", 0, JH7100_AUDCLK_APB0_BUS), - JH7100__GMD(JH7100_AUDCLK_PDM_MCLK, "pdm_mclk", 0, 15, 2, + JH71X0_GATE(JH7100_AUDCLK_PDM_APB, "pdm_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH71X0__GMD(JH7100_AUDCLK_PDM_MCLK, "pdm_mclk", 0, 15, 2, JH7100_AUDCLK_AUDIO_SRC, JH7100_AUDCLK_AUDIO_12288), - JH7100_GATE(JH7100_AUDCLK_I2SVAD_APB, "i2svad_apb", 0, JH7100_AUDCLK_APB0_BUS), - JH7100__GMD(JH7100_AUDCLK_SPDIF, "spdif", 0, 15, 2, + JH71X0_GATE(JH7100_AUDCLK_I2SVAD_APB, "i2svad_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH71X0__GMD(JH7100_AUDCLK_SPDIF, "spdif", 0, 15, 2, JH7100_AUDCLK_AUDIO_SRC, JH7100_AUDCLK_AUDIO_12288), - JH7100_GATE(JH7100_AUDCLK_SPDIF_APB, "spdif_apb", 0, JH7100_AUDCLK_APB0_BUS), - JH7100_GATE(JH7100_AUDCLK_PWMDAC_APB, "pwmdac_apb", 0, JH7100_AUDCLK_APB0_BUS), - JH7100__GMD(JH7100_AUDCLK_DAC_MCLK, "dac_mclk", 0, 15, 2, + JH71X0_GATE(JH7100_AUDCLK_SPDIF_APB, "spdif_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH71X0_GATE(JH7100_AUDCLK_PWMDAC_APB, "pwmdac_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH71X0__GMD(JH7100_AUDCLK_DAC_MCLK, "dac_mclk", 0, 15, 2, JH7100_AUDCLK_AUDIO_SRC, JH7100_AUDCLK_AUDIO_12288), - JH7100_GATE(JH7100_AUDCLK_I2SDAC_APB, "i2sdac_apb", 0, JH7100_AUDCLK_APB0_BUS), - JH7100_MDIV(JH7100_AUDCLK_I2SDAC_BCLK, "i2sdac_bclk", 31, 2, + JH71X0_GATE(JH7100_AUDCLK_I2SDAC_APB, "i2sdac_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH71X0_MDIV(JH7100_AUDCLK_I2SDAC_BCLK, "i2sdac_bclk", 31, 2, JH7100_AUDCLK_DAC_MCLK, JH7100_AUDCLK_I2SDAC_BCLK_IOPAD), - JH7100__INV(JH7100_AUDCLK_I2SDAC_BCLK_N, "i2sdac_bclk_n", JH7100_AUDCLK_I2SDAC_BCLK), - JH7100_MDIV(JH7100_AUDCLK_I2SDAC_LRCLK, "i2sdac_lrclk", 31, 2, + JH71X0__INV(JH7100_AUDCLK_I2SDAC_BCLK_N, "i2sdac_bclk_n", JH7100_AUDCLK_I2SDAC_BCLK), + JH71X0_MDIV(JH7100_AUDCLK_I2SDAC_LRCLK, "i2sdac_lrclk", 31, 2, JH7100_AUDCLK_I2S1_MCLK, JH7100_AUDCLK_I2SDAC_BCLK_IOPAD), - JH7100_GATE(JH7100_AUDCLK_I2S1_APB, "i2s1_apb", 0, JH7100_AUDCLK_APB0_BUS), - JH7100_MDIV(JH7100_AUDCLK_I2S1_BCLK, "i2s1_bclk", 31, 2, + JH71X0_GATE(JH7100_AUDCLK_I2S1_APB, "i2s1_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH71X0_MDIV(JH7100_AUDCLK_I2S1_BCLK, "i2s1_bclk", 31, 2, JH7100_AUDCLK_I2S1_MCLK, JH7100_AUDCLK_I2SDAC_BCLK_IOPAD), - JH7100__INV(JH7100_AUDCLK_I2S1_BCLK_N, "i2s1_bclk_n", JH7100_AUDCLK_I2S1_BCLK), - JH7100_MDIV(JH7100_AUDCLK_I2S1_LRCLK, "i2s1_lrclk", 63, 3, + JH71X0__INV(JH7100_AUDCLK_I2S1_BCLK_N, "i2s1_bclk_n", JH7100_AUDCLK_I2S1_BCLK), + JH71X0_MDIV(JH7100_AUDCLK_I2S1_LRCLK, "i2s1_lrclk", 63, 3, JH7100_AUDCLK_I2S1_BCLK_N, JH7100_AUDCLK_I2SDAC_LRCLK_IOPAD), - JH7100_GATE(JH7100_AUDCLK_I2SDAC16K_APB, "i2s1dac16k_apb", 0, JH7100_AUDCLK_APB0_BUS), - JH7100__DIV(JH7100_AUDCLK_APB0_BUS, "apb0_bus", 8, JH7100_AUDCLK_DOM7AHB_BUS), - JH7100_GATE(JH7100_AUDCLK_DMA1P_AHB, "dma1p_ahb", 0, JH7100_AUDCLK_DOM7AHB_BUS), - JH7100_GATE(JH7100_AUDCLK_USB_APB, "usb_apb", CLK_IGNORE_UNUSED, JH7100_AUDCLK_APB_EN), - JH7100_GDIV(JH7100_AUDCLK_USB_LPM, "usb_lpm", CLK_IGNORE_UNUSED, 4, JH7100_AUDCLK_USB_APB), - JH7100_GDIV(JH7100_AUDCLK_USB_STB, "usb_stb", CLK_IGNORE_UNUSED, 3, JH7100_AUDCLK_USB_APB), - JH7100__DIV(JH7100_AUDCLK_APB_EN, "apb_en", 8, JH7100_AUDCLK_DOM7AHB_BUS), - JH7100__MUX(JH7100_AUDCLK_VAD_MEM, "vad_mem", 2, + JH71X0_GATE(JH7100_AUDCLK_I2SDAC16K_APB, "i2s1dac16k_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH71X0__DIV(JH7100_AUDCLK_APB0_BUS, "apb0_bus", 8, JH7100_AUDCLK_DOM7AHB_BUS), + JH71X0_GATE(JH7100_AUDCLK_DMA1P_AHB, "dma1p_ahb", 0, JH7100_AUDCLK_DOM7AHB_BUS), + JH71X0_GATE(JH7100_AUDCLK_USB_APB, "usb_apb", CLK_IGNORE_UNUSED, JH7100_AUDCLK_APB_EN), + JH71X0_GDIV(JH7100_AUDCLK_USB_LPM, "usb_lpm", CLK_IGNORE_UNUSED, 4, JH7100_AUDCLK_USB_APB), + JH71X0_GDIV(JH7100_AUDCLK_USB_STB, "usb_stb", CLK_IGNORE_UNUSED, 3, JH7100_AUDCLK_USB_APB), + JH71X0__DIV(JH7100_AUDCLK_APB_EN, "apb_en", 8, JH7100_AUDCLK_DOM7AHB_BUS), + JH71X0__MUX(JH7100_AUDCLK_VAD_MEM, "vad_mem", 2, JH7100_AUDCLK_VAD_INTMEM, JH7100_AUDCLK_AUDIO_12288), }; static struct clk_hw *jh7100_audclk_get(struct of_phandle_args *clkspec, void *data) { - struct jh7100_clk_priv *priv = data; + struct jh71x0_clk_priv *priv = data; unsigned int idx = clkspec->args[0]; if (idx < JH7100_AUDCLK_END) @@ -98,7 +98,7 @@ static struct clk_hw *jh7100_audclk_get(struct of_phandle_args *clkspec, void *d static int jh7100_audclk_probe(struct platform_device *pdev) { - struct jh7100_clk_priv *priv; + struct jh71x0_clk_priv *priv; unsigned int idx; int ret; @@ -117,12 +117,12 @@ static int jh7100_audclk_probe(struct platform_device *pdev) struct clk_parent_data parents[4] = {}; struct clk_init_data init = { .name = jh7100_audclk_data[idx].name, - .ops = starfive_jh7100_clk_ops(max), + .ops = starfive_jh71x0_clk_ops(max), .parent_data = parents, - .num_parents = ((max & JH7100_CLK_MUX_MASK) >> JH7100_CLK_MUX_SHIFT) + 1, + .num_parents = ((max & JH71X0_CLK_MUX_MASK) >> JH71X0_CLK_MUX_SHIFT) + 1, .flags = jh7100_audclk_data[idx].flags, }; - struct jh7100_clk *clk = &priv->reg[idx]; + struct jh71x0_clk *clk = &priv->reg[idx]; unsigned int i; for (i = 0; i < init.num_parents; i++) { @@ -140,7 +140,7 @@ static int jh7100_audclk_probe(struct platform_device *pdev) clk->hw.init = &init; clk->idx = idx; - clk->max_div = max & JH7100_CLK_DIV_MASK; + clk->max_div = max & JH71X0_CLK_DIV_MASK; ret = devm_clk_hw_register(priv->dev, &clk->hw); if (ret) diff --git a/drivers/clk/starfive/clk-starfive-jh7100.c b/drivers/clk/starfive/clk-starfive-jh7100.c index 662eb8f74c120..69cc11ea7e334 100644 --- a/drivers/clk/starfive/clk-starfive-jh7100.c +++ b/drivers/clk/starfive/clk-starfive-jh7100.c @@ -23,250 +23,253 @@ #define JH7100_CLK_GMAC_RMII_REF (JH7100_CLK_END + 2) #define JH7100_CLK_GMAC_GR_MII_RX (JH7100_CLK_END + 3) -static const struct jh7100_clk_data jh7100_clk_data[] __initconst = { - JH7100__MUX(JH7100_CLK_CPUNDBUS_ROOT, "cpundbus_root", 4, +static const struct jh71x0_clk_data jh7100_clk_data[] __initconst = { + JH71X0__MUX(JH7100_CLK_CPUNDBUS_ROOT, "cpundbus_root", 4, JH7100_CLK_OSC_SYS, JH7100_CLK_PLL0_OUT, JH7100_CLK_PLL1_OUT, JH7100_CLK_PLL2_OUT), - JH7100__MUX(JH7100_CLK_DLA_ROOT, "dla_root", 3, + JH71X0__MUX(JH7100_CLK_DLA_ROOT, "dla_root", 3, JH7100_CLK_OSC_SYS, JH7100_CLK_PLL1_OUT, JH7100_CLK_PLL2_OUT), - JH7100__MUX(JH7100_CLK_DSP_ROOT, "dsp_root", 4, + JH71X0__MUX(JH7100_CLK_DSP_ROOT, "dsp_root", 4, JH7100_CLK_OSC_SYS, JH7100_CLK_PLL0_OUT, JH7100_CLK_PLL1_OUT, JH7100_CLK_PLL2_OUT), - JH7100__MUX(JH7100_CLK_GMACUSB_ROOT, "gmacusb_root", 3, + JH71X0__MUX(JH7100_CLK_GMACUSB_ROOT, "gmacusb_root", 3, JH7100_CLK_OSC_SYS, JH7100_CLK_PLL0_OUT, JH7100_CLK_PLL2_OUT), - JH7100__MUX(JH7100_CLK_PERH0_ROOT, "perh0_root", 2, + JH71X0__MUX(JH7100_CLK_PERH0_ROOT, "perh0_root", 2, JH7100_CLK_OSC_SYS, JH7100_CLK_PLL0_OUT), - JH7100__MUX(JH7100_CLK_PERH1_ROOT, "perh1_root", 2, + JH71X0__MUX(JH7100_CLK_PERH1_ROOT, "perh1_root", 2, JH7100_CLK_OSC_SYS, JH7100_CLK_PLL2_OUT), - JH7100__MUX(JH7100_CLK_VIN_ROOT, "vin_root", 3, + JH71X0__MUX(JH7100_CLK_VIN_ROOT, "vin_root", 3, JH7100_CLK_OSC_SYS, JH7100_CLK_PLL1_OUT, JH7100_CLK_PLL2_OUT), - JH7100__MUX(JH7100_CLK_VOUT_ROOT, "vout_root", 3, + JH71X0__MUX(JH7100_CLK_VOUT_ROOT, "vout_root", 3, JH7100_CLK_OSC_AUD, JH7100_CLK_PLL0_OUT, JH7100_CLK_PLL2_OUT), - JH7100_GDIV(JH7100_CLK_AUDIO_ROOT, "audio_root", 0, 8, JH7100_CLK_PLL0_OUT), - JH7100__MUX(JH7100_CLK_CDECHIFI4_ROOT, "cdechifi4_root", 3, + JH71X0_GDIV(JH7100_CLK_AUDIO_ROOT, "audio_root", 0, 8, JH7100_CLK_PLL0_OUT), + JH71X0__MUX(JH7100_CLK_CDECHIFI4_ROOT, "cdechifi4_root", 3, JH7100_CLK_OSC_SYS, JH7100_CLK_PLL1_OUT, JH7100_CLK_PLL2_OUT), - JH7100__MUX(JH7100_CLK_CDEC_ROOT, "cdec_root", 3, + JH71X0__MUX(JH7100_CLK_CDEC_ROOT, "cdec_root", 3, JH7100_CLK_OSC_SYS, JH7100_CLK_PLL0_OUT, JH7100_CLK_PLL1_OUT), - JH7100__MUX(JH7100_CLK_VOUTBUS_ROOT, "voutbus_root", 3, + JH71X0__MUX(JH7100_CLK_VOUTBUS_ROOT, "voutbus_root", 3, JH7100_CLK_OSC_AUD, JH7100_CLK_PLL0_OUT, JH7100_CLK_PLL2_OUT), - JH7100__DIV(JH7100_CLK_CPUNBUS_ROOT_DIV, "cpunbus_root_div", 2, JH7100_CLK_CPUNDBUS_ROOT), - JH7100__DIV(JH7100_CLK_DSP_ROOT_DIV, "dsp_root_div", 4, JH7100_CLK_DSP_ROOT), - JH7100__DIV(JH7100_CLK_PERH0_SRC, "perh0_src", 4, JH7100_CLK_PERH0_ROOT), - JH7100__DIV(JH7100_CLK_PERH1_SRC, "perh1_src", 4, JH7100_CLK_PERH1_ROOT), - JH7100_GDIV(JH7100_CLK_PLL0_TESTOUT, "pll0_testout", 0, 31, JH7100_CLK_PERH0_SRC), - JH7100_GDIV(JH7100_CLK_PLL1_TESTOUT, "pll1_testout", 0, 31, JH7100_CLK_DLA_ROOT), - JH7100_GDIV(JH7100_CLK_PLL2_TESTOUT, "pll2_testout", 0, 31, JH7100_CLK_PERH1_SRC), - JH7100__MUX(JH7100_CLK_PLL2_REF, "pll2_refclk", 2, + JH71X0__DIV(JH7100_CLK_CPUNBUS_ROOT_DIV, "cpunbus_root_div", 2, JH7100_CLK_CPUNDBUS_ROOT), + JH71X0__DIV(JH7100_CLK_DSP_ROOT_DIV, "dsp_root_div", 4, JH7100_CLK_DSP_ROOT), + JH71X0__DIV(JH7100_CLK_PERH0_SRC, "perh0_src", 4, JH7100_CLK_PERH0_ROOT), + JH71X0__DIV(JH7100_CLK_PERH1_SRC, "perh1_src", 4, JH7100_CLK_PERH1_ROOT), + JH71X0_GDIV(JH7100_CLK_PLL0_TESTOUT, "pll0_testout", 0, 31, JH7100_CLK_PERH0_SRC), + JH71X0_GDIV(JH7100_CLK_PLL1_TESTOUT, "pll1_testout", 0, 31, JH7100_CLK_DLA_ROOT), + JH71X0_GDIV(JH7100_CLK_PLL2_TESTOUT, "pll2_testout", 0, 31, JH7100_CLK_PERH1_SRC), + JH71X0__MUX(JH7100_CLK_PLL2_REF, "pll2_refclk", 2, JH7100_CLK_OSC_SYS, JH7100_CLK_OSC_AUD), - JH7100__DIV(JH7100_CLK_CPU_CORE, "cpu_core", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), - JH7100__DIV(JH7100_CLK_CPU_AXI, "cpu_axi", 8, JH7100_CLK_CPU_CORE), - JH7100__DIV(JH7100_CLK_AHB_BUS, "ahb_bus", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), - JH7100__DIV(JH7100_CLK_APB1_BUS, "apb1_bus", 8, JH7100_CLK_AHB_BUS), - JH7100__DIV(JH7100_CLK_APB2_BUS, "apb2_bus", 8, JH7100_CLK_AHB_BUS), - JH7100_GATE(JH7100_CLK_DOM3AHB_BUS, "dom3ahb_bus", CLK_IS_CRITICAL, JH7100_CLK_AHB_BUS), - JH7100_GATE(JH7100_CLK_DOM7AHB_BUS, "dom7ahb_bus", CLK_IS_CRITICAL, JH7100_CLK_AHB_BUS), - JH7100_GATE(JH7100_CLK_U74_CORE0, "u74_core0", CLK_IS_CRITICAL, JH7100_CLK_CPU_CORE), - JH7100_GDIV(JH7100_CLK_U74_CORE1, "u74_core1", CLK_IS_CRITICAL, 8, JH7100_CLK_CPU_CORE), - JH7100_GATE(JH7100_CLK_U74_AXI, "u74_axi", CLK_IS_CRITICAL, JH7100_CLK_CPU_AXI), - JH7100_GATE(JH7100_CLK_U74RTC_TOGGLE, "u74rtc_toggle", CLK_IS_CRITICAL, JH7100_CLK_OSC_SYS), - JH7100_GATE(JH7100_CLK_SGDMA2P_AXI, "sgdma2p_axi", 0, JH7100_CLK_CPU_AXI), - JH7100_GATE(JH7100_CLK_DMA2PNOC_AXI, "dma2pnoc_axi", 0, JH7100_CLK_CPU_AXI), - JH7100_GATE(JH7100_CLK_SGDMA2P_AHB, "sgdma2p_ahb", 0, JH7100_CLK_AHB_BUS), - JH7100__DIV(JH7100_CLK_DLA_BUS, "dla_bus", 4, JH7100_CLK_DLA_ROOT), - JH7100_GATE(JH7100_CLK_DLA_AXI, "dla_axi", 0, JH7100_CLK_DLA_BUS), - JH7100_GATE(JH7100_CLK_DLANOC_AXI, "dlanoc_axi", 0, JH7100_CLK_DLA_BUS), - JH7100_GATE(JH7100_CLK_DLA_APB, "dla_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GDIV(JH7100_CLK_VP6_CORE, "vp6_core", 0, 4, JH7100_CLK_DSP_ROOT_DIV), - JH7100__DIV(JH7100_CLK_VP6BUS_SRC, "vp6bus_src", 4, JH7100_CLK_DSP_ROOT), - JH7100_GDIV(JH7100_CLK_VP6_AXI, "vp6_axi", 0, 4, JH7100_CLK_VP6BUS_SRC), - JH7100__DIV(JH7100_CLK_VCDECBUS_SRC, "vcdecbus_src", 4, JH7100_CLK_CDECHIFI4_ROOT), - JH7100__DIV(JH7100_CLK_VDEC_BUS, "vdec_bus", 8, JH7100_CLK_VCDECBUS_SRC), - JH7100_GATE(JH7100_CLK_VDEC_AXI, "vdec_axi", 0, JH7100_CLK_VDEC_BUS), - JH7100_GATE(JH7100_CLK_VDECBRG_MAIN, "vdecbrg_mainclk", 0, JH7100_CLK_VDEC_BUS), - JH7100_GDIV(JH7100_CLK_VDEC_BCLK, "vdec_bclk", 0, 8, JH7100_CLK_VCDECBUS_SRC), - JH7100_GDIV(JH7100_CLK_VDEC_CCLK, "vdec_cclk", 0, 8, JH7100_CLK_CDEC_ROOT), - JH7100_GATE(JH7100_CLK_VDEC_APB, "vdec_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GDIV(JH7100_CLK_JPEG_AXI, "jpeg_axi", 0, 8, JH7100_CLK_CPUNBUS_ROOT_DIV), - JH7100_GDIV(JH7100_CLK_JPEG_CCLK, "jpeg_cclk", 0, 8, JH7100_CLK_CPUNBUS_ROOT_DIV), - JH7100_GATE(JH7100_CLK_JPEG_APB, "jpeg_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GDIV(JH7100_CLK_GC300_2X, "gc300_2x", 0, 8, JH7100_CLK_CDECHIFI4_ROOT), - JH7100_GATE(JH7100_CLK_GC300_AHB, "gc300_ahb", 0, JH7100_CLK_AHB_BUS), - JH7100__DIV(JH7100_CLK_JPCGC300_AXIBUS, "jpcgc300_axibus", 8, JH7100_CLK_VCDECBUS_SRC), - JH7100_GATE(JH7100_CLK_GC300_AXI, "gc300_axi", 0, JH7100_CLK_JPCGC300_AXIBUS), - JH7100_GATE(JH7100_CLK_JPCGC300_MAIN, "jpcgc300_mainclk", 0, JH7100_CLK_JPCGC300_AXIBUS), - JH7100__DIV(JH7100_CLK_VENC_BUS, "venc_bus", 8, JH7100_CLK_VCDECBUS_SRC), - JH7100_GATE(JH7100_CLK_VENC_AXI, "venc_axi", 0, JH7100_CLK_VENC_BUS), - JH7100_GATE(JH7100_CLK_VENCBRG_MAIN, "vencbrg_mainclk", 0, JH7100_CLK_VENC_BUS), - JH7100_GDIV(JH7100_CLK_VENC_BCLK, "venc_bclk", 0, 8, JH7100_CLK_VCDECBUS_SRC), - JH7100_GDIV(JH7100_CLK_VENC_CCLK, "venc_cclk", 0, 8, JH7100_CLK_CDEC_ROOT), - JH7100_GATE(JH7100_CLK_VENC_APB, "venc_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GDIV(JH7100_CLK_DDRPLL_DIV2, "ddrpll_div2", CLK_IS_CRITICAL, 2, JH7100_CLK_PLL1_OUT), - JH7100_GDIV(JH7100_CLK_DDRPLL_DIV4, "ddrpll_div4", CLK_IS_CRITICAL, 2, JH7100_CLK_DDRPLL_DIV2), - JH7100_GDIV(JH7100_CLK_DDRPLL_DIV8, "ddrpll_div8", CLK_IS_CRITICAL, 2, JH7100_CLK_DDRPLL_DIV4), - JH7100_GDIV(JH7100_CLK_DDROSC_DIV2, "ddrosc_div2", CLK_IS_CRITICAL, 2, JH7100_CLK_OSC_SYS), - JH7100_GMUX(JH7100_CLK_DDRC0, "ddrc0", CLK_IS_CRITICAL, 4, + JH71X0__DIV(JH7100_CLK_CPU_CORE, "cpu_core", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), + JH71X0__DIV(JH7100_CLK_CPU_AXI, "cpu_axi", 8, JH7100_CLK_CPU_CORE), + JH71X0__DIV(JH7100_CLK_AHB_BUS, "ahb_bus", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), + JH71X0__DIV(JH7100_CLK_APB1_BUS, "apb1_bus", 8, JH7100_CLK_AHB_BUS), + JH71X0__DIV(JH7100_CLK_APB2_BUS, "apb2_bus", 8, JH7100_CLK_AHB_BUS), + JH71X0_GATE(JH7100_CLK_DOM3AHB_BUS, "dom3ahb_bus", CLK_IS_CRITICAL, JH7100_CLK_AHB_BUS), + JH71X0_GATE(JH7100_CLK_DOM7AHB_BUS, "dom7ahb_bus", CLK_IS_CRITICAL, JH7100_CLK_AHB_BUS), + JH71X0_GATE(JH7100_CLK_U74_CORE0, "u74_core0", CLK_IS_CRITICAL, JH7100_CLK_CPU_CORE), + JH71X0_GDIV(JH7100_CLK_U74_CORE1, "u74_core1", CLK_IS_CRITICAL, 8, JH7100_CLK_CPU_CORE), + JH71X0_GATE(JH7100_CLK_U74_AXI, "u74_axi", CLK_IS_CRITICAL, JH7100_CLK_CPU_AXI), + JH71X0_GATE(JH7100_CLK_U74RTC_TOGGLE, "u74rtc_toggle", CLK_IS_CRITICAL, JH7100_CLK_OSC_SYS), + JH71X0_GATE(JH7100_CLK_SGDMA2P_AXI, "sgdma2p_axi", 0, JH7100_CLK_CPU_AXI), + JH71X0_GATE(JH7100_CLK_DMA2PNOC_AXI, "dma2pnoc_axi", 0, JH7100_CLK_CPU_AXI), + JH71X0_GATE(JH7100_CLK_SGDMA2P_AHB, "sgdma2p_ahb", 0, JH7100_CLK_AHB_BUS), + JH71X0__DIV(JH7100_CLK_DLA_BUS, "dla_bus", 4, JH7100_CLK_DLA_ROOT), + JH71X0_GATE(JH7100_CLK_DLA_AXI, "dla_axi", 0, JH7100_CLK_DLA_BUS), + JH71X0_GATE(JH7100_CLK_DLANOC_AXI, "dlanoc_axi", 0, JH7100_CLK_DLA_BUS), + JH71X0_GATE(JH7100_CLK_DLA_APB, "dla_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GDIV(JH7100_CLK_VP6_CORE, "vp6_core", 0, 4, JH7100_CLK_DSP_ROOT_DIV), + JH71X0__DIV(JH7100_CLK_VP6BUS_SRC, "vp6bus_src", 4, JH7100_CLK_DSP_ROOT), + JH71X0_GDIV(JH7100_CLK_VP6_AXI, "vp6_axi", 0, 4, JH7100_CLK_VP6BUS_SRC), + JH71X0__DIV(JH7100_CLK_VCDECBUS_SRC, "vcdecbus_src", 4, JH7100_CLK_CDECHIFI4_ROOT), + JH71X0__DIV(JH7100_CLK_VDEC_BUS, "vdec_bus", 8, JH7100_CLK_VCDECBUS_SRC), + JH71X0_GATE(JH7100_CLK_VDEC_AXI, "vdec_axi", 0, JH7100_CLK_VDEC_BUS), + JH71X0_GATE(JH7100_CLK_VDECBRG_MAIN, "vdecbrg_mainclk", 0, JH7100_CLK_VDEC_BUS), + JH71X0_GDIV(JH7100_CLK_VDEC_BCLK, "vdec_bclk", 0, 8, JH7100_CLK_VCDECBUS_SRC), + JH71X0_GDIV(JH7100_CLK_VDEC_CCLK, "vdec_cclk", 0, 8, JH7100_CLK_CDEC_ROOT), + JH71X0_GATE(JH7100_CLK_VDEC_APB, "vdec_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GDIV(JH7100_CLK_JPEG_AXI, "jpeg_axi", 0, 8, JH7100_CLK_CPUNBUS_ROOT_DIV), + JH71X0_GDIV(JH7100_CLK_JPEG_CCLK, "jpeg_cclk", 0, 8, JH7100_CLK_CPUNBUS_ROOT_DIV), + JH71X0_GATE(JH7100_CLK_JPEG_APB, "jpeg_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GDIV(JH7100_CLK_GC300_2X, "gc300_2x", 0, 8, JH7100_CLK_CDECHIFI4_ROOT), + JH71X0_GATE(JH7100_CLK_GC300_AHB, "gc300_ahb", 0, JH7100_CLK_AHB_BUS), + JH71X0__DIV(JH7100_CLK_JPCGC300_AXIBUS, "jpcgc300_axibus", 8, JH7100_CLK_VCDECBUS_SRC), + JH71X0_GATE(JH7100_CLK_GC300_AXI, "gc300_axi", 0, JH7100_CLK_JPCGC300_AXIBUS), + JH71X0_GATE(JH7100_CLK_JPCGC300_MAIN, "jpcgc300_mainclk", 0, JH7100_CLK_JPCGC300_AXIBUS), + JH71X0__DIV(JH7100_CLK_VENC_BUS, "venc_bus", 8, JH7100_CLK_VCDECBUS_SRC), + JH71X0_GATE(JH7100_CLK_VENC_AXI, "venc_axi", 0, JH7100_CLK_VENC_BUS), + JH71X0_GATE(JH7100_CLK_VENCBRG_MAIN, "vencbrg_mainclk", 0, JH7100_CLK_VENC_BUS), + JH71X0_GDIV(JH7100_CLK_VENC_BCLK, "venc_bclk", 0, 8, JH7100_CLK_VCDECBUS_SRC), + JH71X0_GDIV(JH7100_CLK_VENC_CCLK, "venc_cclk", 0, 8, JH7100_CLK_CDEC_ROOT), + JH71X0_GATE(JH7100_CLK_VENC_APB, "venc_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GDIV(JH7100_CLK_DDRPLL_DIV2, "ddrpll_div2", CLK_IS_CRITICAL, 2, JH7100_CLK_PLL1_OUT), + JH71X0_GDIV(JH7100_CLK_DDRPLL_DIV4, "ddrpll_div4", CLK_IS_CRITICAL, 2, + JH7100_CLK_DDRPLL_DIV2), + JH71X0_GDIV(JH7100_CLK_DDRPLL_DIV8, "ddrpll_div8", CLK_IS_CRITICAL, 2, + JH7100_CLK_DDRPLL_DIV4), + JH71X0_GDIV(JH7100_CLK_DDROSC_DIV2, "ddrosc_div2", CLK_IS_CRITICAL, 2, JH7100_CLK_OSC_SYS), + JH71X0_GMUX(JH7100_CLK_DDRC0, "ddrc0", CLK_IS_CRITICAL, 4, JH7100_CLK_DDROSC_DIV2, JH7100_CLK_DDRPLL_DIV2, JH7100_CLK_DDRPLL_DIV4, JH7100_CLK_DDRPLL_DIV8), - JH7100_GMUX(JH7100_CLK_DDRC1, "ddrc1", CLK_IS_CRITICAL, 4, + JH71X0_GMUX(JH7100_CLK_DDRC1, "ddrc1", CLK_IS_CRITICAL, 4, JH7100_CLK_DDROSC_DIV2, JH7100_CLK_DDRPLL_DIV2, JH7100_CLK_DDRPLL_DIV4, JH7100_CLK_DDRPLL_DIV8), - JH7100_GATE(JH7100_CLK_DDRPHY_APB, "ddrphy_apb", 0, JH7100_CLK_APB1_BUS), - JH7100__DIV(JH7100_CLK_NOC_ROB, "noc_rob", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), - JH7100__DIV(JH7100_CLK_NOC_COG, "noc_cog", 8, JH7100_CLK_DLA_ROOT), - JH7100_GATE(JH7100_CLK_NNE_AHB, "nne_ahb", 0, JH7100_CLK_AHB_BUS), - JH7100__DIV(JH7100_CLK_NNEBUS_SRC1, "nnebus_src1", 4, JH7100_CLK_DSP_ROOT), - JH7100__MUX(JH7100_CLK_NNE_BUS, "nne_bus", 2, + JH71X0_GATE(JH7100_CLK_DDRPHY_APB, "ddrphy_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0__DIV(JH7100_CLK_NOC_ROB, "noc_rob", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), + JH71X0__DIV(JH7100_CLK_NOC_COG, "noc_cog", 8, JH7100_CLK_DLA_ROOT), + JH71X0_GATE(JH7100_CLK_NNE_AHB, "nne_ahb", 0, JH7100_CLK_AHB_BUS), + JH71X0__DIV(JH7100_CLK_NNEBUS_SRC1, "nnebus_src1", 4, JH7100_CLK_DSP_ROOT), + JH71X0__MUX(JH7100_CLK_NNE_BUS, "nne_bus", 2, JH7100_CLK_CPU_AXI, JH7100_CLK_NNEBUS_SRC1), - JH7100_GATE(JH7100_CLK_NNE_AXI, "nne_axi", 0, JH7100_CLK_NNE_BUS), - JH7100_GATE(JH7100_CLK_NNENOC_AXI, "nnenoc_axi", 0, JH7100_CLK_NNE_BUS), - JH7100_GATE(JH7100_CLK_DLASLV_AXI, "dlaslv_axi", 0, JH7100_CLK_NNE_BUS), - JH7100_GATE(JH7100_CLK_DSPX2C_AXI, "dspx2c_axi", CLK_IS_CRITICAL, JH7100_CLK_NNE_BUS), - JH7100__DIV(JH7100_CLK_HIFI4_SRC, "hifi4_src", 4, JH7100_CLK_CDECHIFI4_ROOT), - JH7100__DIV(JH7100_CLK_HIFI4_COREFREE, "hifi4_corefree", 8, JH7100_CLK_HIFI4_SRC), - JH7100_GATE(JH7100_CLK_HIFI4_CORE, "hifi4_core", 0, JH7100_CLK_HIFI4_COREFREE), - JH7100__DIV(JH7100_CLK_HIFI4_BUS, "hifi4_bus", 8, JH7100_CLK_HIFI4_COREFREE), - JH7100_GATE(JH7100_CLK_HIFI4_AXI, "hifi4_axi", 0, JH7100_CLK_HIFI4_BUS), - JH7100_GATE(JH7100_CLK_HIFI4NOC_AXI, "hifi4noc_axi", 0, JH7100_CLK_HIFI4_BUS), - JH7100__DIV(JH7100_CLK_SGDMA1P_BUS, "sgdma1p_bus", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), - JH7100_GATE(JH7100_CLK_SGDMA1P_AXI, "sgdma1p_axi", 0, JH7100_CLK_SGDMA1P_BUS), - JH7100_GATE(JH7100_CLK_DMA1P_AXI, "dma1p_axi", 0, JH7100_CLK_SGDMA1P_BUS), - JH7100_GDIV(JH7100_CLK_X2C_AXI, "x2c_axi", CLK_IS_CRITICAL, 8, JH7100_CLK_CPUNBUS_ROOT_DIV), - JH7100__DIV(JH7100_CLK_USB_BUS, "usb_bus", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), - JH7100_GATE(JH7100_CLK_USB_AXI, "usb_axi", 0, JH7100_CLK_USB_BUS), - JH7100_GATE(JH7100_CLK_USBNOC_AXI, "usbnoc_axi", 0, JH7100_CLK_USB_BUS), - JH7100__DIV(JH7100_CLK_USBPHY_ROOTDIV, "usbphy_rootdiv", 4, JH7100_CLK_GMACUSB_ROOT), - JH7100_GDIV(JH7100_CLK_USBPHY_125M, "usbphy_125m", 0, 8, JH7100_CLK_USBPHY_ROOTDIV), - JH7100_GDIV(JH7100_CLK_USBPHY_PLLDIV25M, "usbphy_plldiv25m", 0, 32, JH7100_CLK_USBPHY_ROOTDIV), - JH7100__MUX(JH7100_CLK_USBPHY_25M, "usbphy_25m", 2, + JH71X0_GATE(JH7100_CLK_NNE_AXI, "nne_axi", 0, JH7100_CLK_NNE_BUS), + JH71X0_GATE(JH7100_CLK_NNENOC_AXI, "nnenoc_axi", 0, JH7100_CLK_NNE_BUS), + JH71X0_GATE(JH7100_CLK_DLASLV_AXI, "dlaslv_axi", 0, JH7100_CLK_NNE_BUS), + JH71X0_GATE(JH7100_CLK_DSPX2C_AXI, "dspx2c_axi", CLK_IS_CRITICAL, JH7100_CLK_NNE_BUS), + JH71X0__DIV(JH7100_CLK_HIFI4_SRC, "hifi4_src", 4, JH7100_CLK_CDECHIFI4_ROOT), + JH71X0__DIV(JH7100_CLK_HIFI4_COREFREE, "hifi4_corefree", 8, JH7100_CLK_HIFI4_SRC), + JH71X0_GATE(JH7100_CLK_HIFI4_CORE, "hifi4_core", 0, JH7100_CLK_HIFI4_COREFREE), + JH71X0__DIV(JH7100_CLK_HIFI4_BUS, "hifi4_bus", 8, JH7100_CLK_HIFI4_COREFREE), + JH71X0_GATE(JH7100_CLK_HIFI4_AXI, "hifi4_axi", 0, JH7100_CLK_HIFI4_BUS), + JH71X0_GATE(JH7100_CLK_HIFI4NOC_AXI, "hifi4noc_axi", 0, JH7100_CLK_HIFI4_BUS), + JH71X0__DIV(JH7100_CLK_SGDMA1P_BUS, "sgdma1p_bus", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), + JH71X0_GATE(JH7100_CLK_SGDMA1P_AXI, "sgdma1p_axi", 0, JH7100_CLK_SGDMA1P_BUS), + JH71X0_GATE(JH7100_CLK_DMA1P_AXI, "dma1p_axi", 0, JH7100_CLK_SGDMA1P_BUS), + JH71X0_GDIV(JH7100_CLK_X2C_AXI, "x2c_axi", CLK_IS_CRITICAL, 8, JH7100_CLK_CPUNBUS_ROOT_DIV), + JH71X0__DIV(JH7100_CLK_USB_BUS, "usb_bus", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), + JH71X0_GATE(JH7100_CLK_USB_AXI, "usb_axi", 0, JH7100_CLK_USB_BUS), + JH71X0_GATE(JH7100_CLK_USBNOC_AXI, "usbnoc_axi", 0, JH7100_CLK_USB_BUS), + JH71X0__DIV(JH7100_CLK_USBPHY_ROOTDIV, "usbphy_rootdiv", 4, JH7100_CLK_GMACUSB_ROOT), + JH71X0_GDIV(JH7100_CLK_USBPHY_125M, "usbphy_125m", 0, 8, JH7100_CLK_USBPHY_ROOTDIV), + JH71X0_GDIV(JH7100_CLK_USBPHY_PLLDIV25M, "usbphy_plldiv25m", 0, 32, + JH7100_CLK_USBPHY_ROOTDIV), + JH71X0__MUX(JH7100_CLK_USBPHY_25M, "usbphy_25m", 2, JH7100_CLK_OSC_SYS, JH7100_CLK_USBPHY_PLLDIV25M), - JH7100_FDIV(JH7100_CLK_AUDIO_DIV, "audio_div", JH7100_CLK_AUDIO_ROOT), - JH7100_GATE(JH7100_CLK_AUDIO_SRC, "audio_src", 0, JH7100_CLK_AUDIO_DIV), - JH7100_GATE(JH7100_CLK_AUDIO_12288, "audio_12288", 0, JH7100_CLK_OSC_AUD), - JH7100_GDIV(JH7100_CLK_VIN_SRC, "vin_src", 0, 4, JH7100_CLK_VIN_ROOT), - JH7100__DIV(JH7100_CLK_ISP0_BUS, "isp0_bus", 8, JH7100_CLK_VIN_SRC), - JH7100_GATE(JH7100_CLK_ISP0_AXI, "isp0_axi", 0, JH7100_CLK_ISP0_BUS), - JH7100_GATE(JH7100_CLK_ISP0NOC_AXI, "isp0noc_axi", 0, JH7100_CLK_ISP0_BUS), - JH7100_GATE(JH7100_CLK_ISPSLV_AXI, "ispslv_axi", 0, JH7100_CLK_ISP0_BUS), - JH7100__DIV(JH7100_CLK_ISP1_BUS, "isp1_bus", 8, JH7100_CLK_VIN_SRC), - JH7100_GATE(JH7100_CLK_ISP1_AXI, "isp1_axi", 0, JH7100_CLK_ISP1_BUS), - JH7100_GATE(JH7100_CLK_ISP1NOC_AXI, "isp1noc_axi", 0, JH7100_CLK_ISP1_BUS), - JH7100__DIV(JH7100_CLK_VIN_BUS, "vin_bus", 8, JH7100_CLK_VIN_SRC), - JH7100_GATE(JH7100_CLK_VIN_AXI, "vin_axi", 0, JH7100_CLK_VIN_BUS), - JH7100_GATE(JH7100_CLK_VINNOC_AXI, "vinnoc_axi", 0, JH7100_CLK_VIN_BUS), - JH7100_GDIV(JH7100_CLK_VOUT_SRC, "vout_src", 0, 4, JH7100_CLK_VOUT_ROOT), - JH7100__DIV(JH7100_CLK_DISPBUS_SRC, "dispbus_src", 4, JH7100_CLK_VOUTBUS_ROOT), - JH7100__DIV(JH7100_CLK_DISP_BUS, "disp_bus", 4, JH7100_CLK_DISPBUS_SRC), - JH7100_GATE(JH7100_CLK_DISP_AXI, "disp_axi", 0, JH7100_CLK_DISP_BUS), - JH7100_GATE(JH7100_CLK_DISPNOC_AXI, "dispnoc_axi", 0, JH7100_CLK_DISP_BUS), - JH7100_GATE(JH7100_CLK_SDIO0_AHB, "sdio0_ahb", 0, JH7100_CLK_AHB_BUS), - JH7100_GDIV(JH7100_CLK_SDIO0_CCLKINT, "sdio0_cclkint", 0, 24, JH7100_CLK_PERH0_SRC), - JH7100__INV(JH7100_CLK_SDIO0_CCLKINT_INV, "sdio0_cclkint_inv", JH7100_CLK_SDIO0_CCLKINT), - JH7100_GATE(JH7100_CLK_SDIO1_AHB, "sdio1_ahb", 0, JH7100_CLK_AHB_BUS), - JH7100_GDIV(JH7100_CLK_SDIO1_CCLKINT, "sdio1_cclkint", 0, 24, JH7100_CLK_PERH1_SRC), - JH7100__INV(JH7100_CLK_SDIO1_CCLKINT_INV, "sdio1_cclkint_inv", JH7100_CLK_SDIO1_CCLKINT), - JH7100_GATE(JH7100_CLK_GMAC_AHB, "gmac_ahb", 0, JH7100_CLK_AHB_BUS), - JH7100__DIV(JH7100_CLK_GMAC_ROOT_DIV, "gmac_root_div", 8, JH7100_CLK_GMACUSB_ROOT), - JH7100_GDIV(JH7100_CLK_GMAC_PTP_REF, "gmac_ptp_refclk", 0, 31, JH7100_CLK_GMAC_ROOT_DIV), - JH7100_GDIV(JH7100_CLK_GMAC_GTX, "gmac_gtxclk", 0, 255, JH7100_CLK_GMAC_ROOT_DIV), - JH7100_GDIV(JH7100_CLK_GMAC_RMII_TX, "gmac_rmii_txclk", 0, 8, JH7100_CLK_GMAC_RMII_REF), - JH7100_GDIV(JH7100_CLK_GMAC_RMII_RX, "gmac_rmii_rxclk", 0, 8, JH7100_CLK_GMAC_RMII_REF), - JH7100__MUX(JH7100_CLK_GMAC_TX, "gmac_tx", 3, + JH71X0_FDIV(JH7100_CLK_AUDIO_DIV, "audio_div", JH7100_CLK_AUDIO_ROOT), + JH71X0_GATE(JH7100_CLK_AUDIO_SRC, "audio_src", 0, JH7100_CLK_AUDIO_DIV), + JH71X0_GATE(JH7100_CLK_AUDIO_12288, "audio_12288", 0, JH7100_CLK_OSC_AUD), + JH71X0_GDIV(JH7100_CLK_VIN_SRC, "vin_src", 0, 4, JH7100_CLK_VIN_ROOT), + JH71X0__DIV(JH7100_CLK_ISP0_BUS, "isp0_bus", 8, JH7100_CLK_VIN_SRC), + JH71X0_GATE(JH7100_CLK_ISP0_AXI, "isp0_axi", 0, JH7100_CLK_ISP0_BUS), + JH71X0_GATE(JH7100_CLK_ISP0NOC_AXI, "isp0noc_axi", 0, JH7100_CLK_ISP0_BUS), + JH71X0_GATE(JH7100_CLK_ISPSLV_AXI, "ispslv_axi", 0, JH7100_CLK_ISP0_BUS), + JH71X0__DIV(JH7100_CLK_ISP1_BUS, "isp1_bus", 8, JH7100_CLK_VIN_SRC), + JH71X0_GATE(JH7100_CLK_ISP1_AXI, "isp1_axi", 0, JH7100_CLK_ISP1_BUS), + JH71X0_GATE(JH7100_CLK_ISP1NOC_AXI, "isp1noc_axi", 0, JH7100_CLK_ISP1_BUS), + JH71X0__DIV(JH7100_CLK_VIN_BUS, "vin_bus", 8, JH7100_CLK_VIN_SRC), + JH71X0_GATE(JH7100_CLK_VIN_AXI, "vin_axi", 0, JH7100_CLK_VIN_BUS), + JH71X0_GATE(JH7100_CLK_VINNOC_AXI, "vinnoc_axi", 0, JH7100_CLK_VIN_BUS), + JH71X0_GDIV(JH7100_CLK_VOUT_SRC, "vout_src", 0, 4, JH7100_CLK_VOUT_ROOT), + JH71X0__DIV(JH7100_CLK_DISPBUS_SRC, "dispbus_src", 4, JH7100_CLK_VOUTBUS_ROOT), + JH71X0__DIV(JH7100_CLK_DISP_BUS, "disp_bus", 4, JH7100_CLK_DISPBUS_SRC), + JH71X0_GATE(JH7100_CLK_DISP_AXI, "disp_axi", 0, JH7100_CLK_DISP_BUS), + JH71X0_GATE(JH7100_CLK_DISPNOC_AXI, "dispnoc_axi", 0, JH7100_CLK_DISP_BUS), + JH71X0_GATE(JH7100_CLK_SDIO0_AHB, "sdio0_ahb", 0, JH7100_CLK_AHB_BUS), + JH71X0_GDIV(JH7100_CLK_SDIO0_CCLKINT, "sdio0_cclkint", 0, 24, JH7100_CLK_PERH0_SRC), + JH71X0__INV(JH7100_CLK_SDIO0_CCLKINT_INV, "sdio0_cclkint_inv", JH7100_CLK_SDIO0_CCLKINT), + JH71X0_GATE(JH7100_CLK_SDIO1_AHB, "sdio1_ahb", 0, JH7100_CLK_AHB_BUS), + JH71X0_GDIV(JH7100_CLK_SDIO1_CCLKINT, "sdio1_cclkint", 0, 24, JH7100_CLK_PERH1_SRC), + JH71X0__INV(JH7100_CLK_SDIO1_CCLKINT_INV, "sdio1_cclkint_inv", JH7100_CLK_SDIO1_CCLKINT), + JH71X0_GATE(JH7100_CLK_GMAC_AHB, "gmac_ahb", 0, JH7100_CLK_AHB_BUS), + JH71X0__DIV(JH7100_CLK_GMAC_ROOT_DIV, "gmac_root_div", 8, JH7100_CLK_GMACUSB_ROOT), + JH71X0_GDIV(JH7100_CLK_GMAC_PTP_REF, "gmac_ptp_refclk", 0, 31, JH7100_CLK_GMAC_ROOT_DIV), + JH71X0_GDIV(JH7100_CLK_GMAC_GTX, "gmac_gtxclk", 0, 255, JH7100_CLK_GMAC_ROOT_DIV), + JH71X0_GDIV(JH7100_CLK_GMAC_RMII_TX, "gmac_rmii_txclk", 0, 8, JH7100_CLK_GMAC_RMII_REF), + JH71X0_GDIV(JH7100_CLK_GMAC_RMII_RX, "gmac_rmii_rxclk", 0, 8, JH7100_CLK_GMAC_RMII_REF), + JH71X0__MUX(JH7100_CLK_GMAC_TX, "gmac_tx", 3, JH7100_CLK_GMAC_GTX, JH7100_CLK_GMAC_TX_INV, JH7100_CLK_GMAC_RMII_TX), - JH7100__INV(JH7100_CLK_GMAC_TX_INV, "gmac_tx_inv", JH7100_CLK_GMAC_TX), - JH7100__MUX(JH7100_CLK_GMAC_RX_PRE, "gmac_rx_pre", 2, + JH71X0__INV(JH7100_CLK_GMAC_TX_INV, "gmac_tx_inv", JH7100_CLK_GMAC_TX), + JH71X0__MUX(JH7100_CLK_GMAC_RX_PRE, "gmac_rx_pre", 2, JH7100_CLK_GMAC_GR_MII_RX, JH7100_CLK_GMAC_RMII_RX), - JH7100__INV(JH7100_CLK_GMAC_RX_INV, "gmac_rx_inv", JH7100_CLK_GMAC_RX_PRE), - JH7100_GATE(JH7100_CLK_GMAC_RMII, "gmac_rmii", 0, JH7100_CLK_GMAC_RMII_REF), - JH7100_GDIV(JH7100_CLK_GMAC_TOPHYREF, "gmac_tophyref", 0, 127, JH7100_CLK_GMAC_ROOT_DIV), - JH7100_GATE(JH7100_CLK_SPI2AHB_AHB, "spi2ahb_ahb", 0, JH7100_CLK_AHB_BUS), - JH7100_GDIV(JH7100_CLK_SPI2AHB_CORE, "spi2ahb_core", 0, 31, JH7100_CLK_PERH0_SRC), - JH7100_GATE(JH7100_CLK_EZMASTER_AHB, "ezmaster_ahb", 0, JH7100_CLK_AHB_BUS), - JH7100_GATE(JH7100_CLK_E24_AHB, "e24_ahb", 0, JH7100_CLK_AHB_BUS), - JH7100_GATE(JH7100_CLK_E24RTC_TOGGLE, "e24rtc_toggle", 0, JH7100_CLK_OSC_SYS), - JH7100_GATE(JH7100_CLK_QSPI_AHB, "qspi_ahb", 0, JH7100_CLK_AHB_BUS), - JH7100_GATE(JH7100_CLK_QSPI_APB, "qspi_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GDIV(JH7100_CLK_QSPI_REF, "qspi_refclk", 0, 31, JH7100_CLK_PERH0_SRC), - JH7100_GATE(JH7100_CLK_SEC_AHB, "sec_ahb", 0, JH7100_CLK_AHB_BUS), - JH7100_GATE(JH7100_CLK_AES, "aes_clk", 0, JH7100_CLK_SEC_AHB), - JH7100_GATE(JH7100_CLK_SHA, "sha_clk", 0, JH7100_CLK_SEC_AHB), - JH7100_GATE(JH7100_CLK_PKA, "pka_clk", 0, JH7100_CLK_SEC_AHB), - JH7100_GATE(JH7100_CLK_TRNG_APB, "trng_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GATE(JH7100_CLK_OTP_APB, "otp_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GATE(JH7100_CLK_UART0_APB, "uart0_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GDIV(JH7100_CLK_UART0_CORE, "uart0_core", 0, 63, JH7100_CLK_PERH1_SRC), - JH7100_GATE(JH7100_CLK_UART1_APB, "uart1_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GDIV(JH7100_CLK_UART1_CORE, "uart1_core", 0, 63, JH7100_CLK_PERH1_SRC), - JH7100_GATE(JH7100_CLK_SPI0_APB, "spi0_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GDIV(JH7100_CLK_SPI0_CORE, "spi0_core", 0, 63, JH7100_CLK_PERH1_SRC), - JH7100_GATE(JH7100_CLK_SPI1_APB, "spi1_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GDIV(JH7100_CLK_SPI1_CORE, "spi1_core", 0, 63, JH7100_CLK_PERH1_SRC), - JH7100_GATE(JH7100_CLK_I2C0_APB, "i2c0_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GDIV(JH7100_CLK_I2C0_CORE, "i2c0_core", 0, 63, JH7100_CLK_PERH1_SRC), - JH7100_GATE(JH7100_CLK_I2C1_APB, "i2c1_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GDIV(JH7100_CLK_I2C1_CORE, "i2c1_core", 0, 63, JH7100_CLK_PERH1_SRC), - JH7100_GATE(JH7100_CLK_GPIO_APB, "gpio_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GATE(JH7100_CLK_UART2_APB, "uart2_apb", 0, JH7100_CLK_APB2_BUS), - JH7100_GDIV(JH7100_CLK_UART2_CORE, "uart2_core", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GATE(JH7100_CLK_UART3_APB, "uart3_apb", 0, JH7100_CLK_APB2_BUS), - JH7100_GDIV(JH7100_CLK_UART3_CORE, "uart3_core", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GATE(JH7100_CLK_SPI2_APB, "spi2_apb", 0, JH7100_CLK_APB2_BUS), - JH7100_GDIV(JH7100_CLK_SPI2_CORE, "spi2_core", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GATE(JH7100_CLK_SPI3_APB, "spi3_apb", 0, JH7100_CLK_APB2_BUS), - JH7100_GDIV(JH7100_CLK_SPI3_CORE, "spi3_core", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GATE(JH7100_CLK_I2C2_APB, "i2c2_apb", 0, JH7100_CLK_APB2_BUS), - JH7100_GDIV(JH7100_CLK_I2C2_CORE, "i2c2_core", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GATE(JH7100_CLK_I2C3_APB, "i2c3_apb", 0, JH7100_CLK_APB2_BUS), - JH7100_GDIV(JH7100_CLK_I2C3_CORE, "i2c3_core", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GATE(JH7100_CLK_WDTIMER_APB, "wdtimer_apb", 0, JH7100_CLK_APB2_BUS), - JH7100_GDIV(JH7100_CLK_WDT_CORE, "wdt_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GDIV(JH7100_CLK_TIMER0_CORE, "timer0_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GDIV(JH7100_CLK_TIMER1_CORE, "timer1_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GDIV(JH7100_CLK_TIMER2_CORE, "timer2_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GDIV(JH7100_CLK_TIMER3_CORE, "timer3_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GDIV(JH7100_CLK_TIMER4_CORE, "timer4_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GDIV(JH7100_CLK_TIMER5_CORE, "timer5_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GDIV(JH7100_CLK_TIMER6_CORE, "timer6_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GATE(JH7100_CLK_VP6INTC_APB, "vp6intc_apb", 0, JH7100_CLK_APB2_BUS), - JH7100_GATE(JH7100_CLK_PWM_APB, "pwm_apb", 0, JH7100_CLK_APB2_BUS), - JH7100_GATE(JH7100_CLK_MSI_APB, "msi_apb", 0, JH7100_CLK_APB2_BUS), - JH7100_GATE(JH7100_CLK_TEMP_APB, "temp_apb", 0, JH7100_CLK_APB2_BUS), - JH7100_GDIV(JH7100_CLK_TEMP_SENSE, "temp_sense", 0, 31, JH7100_CLK_OSC_SYS), - JH7100_GATE(JH7100_CLK_SYSERR_APB, "syserr_apb", 0, JH7100_CLK_APB2_BUS), + JH71X0__INV(JH7100_CLK_GMAC_RX_INV, "gmac_rx_inv", JH7100_CLK_GMAC_RX_PRE), + JH71X0_GATE(JH7100_CLK_GMAC_RMII, "gmac_rmii", 0, JH7100_CLK_GMAC_RMII_REF), + JH71X0_GDIV(JH7100_CLK_GMAC_TOPHYREF, "gmac_tophyref", 0, 127, JH7100_CLK_GMAC_ROOT_DIV), + JH71X0_GATE(JH7100_CLK_SPI2AHB_AHB, "spi2ahb_ahb", 0, JH7100_CLK_AHB_BUS), + JH71X0_GDIV(JH7100_CLK_SPI2AHB_CORE, "spi2ahb_core", 0, 31, JH7100_CLK_PERH0_SRC), + JH71X0_GATE(JH7100_CLK_EZMASTER_AHB, "ezmaster_ahb", 0, JH7100_CLK_AHB_BUS), + JH71X0_GATE(JH7100_CLK_E24_AHB, "e24_ahb", 0, JH7100_CLK_AHB_BUS), + JH71X0_GATE(JH7100_CLK_E24RTC_TOGGLE, "e24rtc_toggle", 0, JH7100_CLK_OSC_SYS), + JH71X0_GATE(JH7100_CLK_QSPI_AHB, "qspi_ahb", 0, JH7100_CLK_AHB_BUS), + JH71X0_GATE(JH7100_CLK_QSPI_APB, "qspi_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GDIV(JH7100_CLK_QSPI_REF, "qspi_refclk", 0, 31, JH7100_CLK_PERH0_SRC), + JH71X0_GATE(JH7100_CLK_SEC_AHB, "sec_ahb", 0, JH7100_CLK_AHB_BUS), + JH71X0_GATE(JH7100_CLK_AES, "aes_clk", 0, JH7100_CLK_SEC_AHB), + JH71X0_GATE(JH7100_CLK_SHA, "sha_clk", 0, JH7100_CLK_SEC_AHB), + JH71X0_GATE(JH7100_CLK_PKA, "pka_clk", 0, JH7100_CLK_SEC_AHB), + JH71X0_GATE(JH7100_CLK_TRNG_APB, "trng_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GATE(JH7100_CLK_OTP_APB, "otp_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GATE(JH7100_CLK_UART0_APB, "uart0_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GDIV(JH7100_CLK_UART0_CORE, "uart0_core", 0, 63, JH7100_CLK_PERH1_SRC), + JH71X0_GATE(JH7100_CLK_UART1_APB, "uart1_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GDIV(JH7100_CLK_UART1_CORE, "uart1_core", 0, 63, JH7100_CLK_PERH1_SRC), + JH71X0_GATE(JH7100_CLK_SPI0_APB, "spi0_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GDIV(JH7100_CLK_SPI0_CORE, "spi0_core", 0, 63, JH7100_CLK_PERH1_SRC), + JH71X0_GATE(JH7100_CLK_SPI1_APB, "spi1_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GDIV(JH7100_CLK_SPI1_CORE, "spi1_core", 0, 63, JH7100_CLK_PERH1_SRC), + JH71X0_GATE(JH7100_CLK_I2C0_APB, "i2c0_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GDIV(JH7100_CLK_I2C0_CORE, "i2c0_core", 0, 63, JH7100_CLK_PERH1_SRC), + JH71X0_GATE(JH7100_CLK_I2C1_APB, "i2c1_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GDIV(JH7100_CLK_I2C1_CORE, "i2c1_core", 0, 63, JH7100_CLK_PERH1_SRC), + JH71X0_GATE(JH7100_CLK_GPIO_APB, "gpio_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GATE(JH7100_CLK_UART2_APB, "uart2_apb", 0, JH7100_CLK_APB2_BUS), + JH71X0_GDIV(JH7100_CLK_UART2_CORE, "uart2_core", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GATE(JH7100_CLK_UART3_APB, "uart3_apb", 0, JH7100_CLK_APB2_BUS), + JH71X0_GDIV(JH7100_CLK_UART3_CORE, "uart3_core", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GATE(JH7100_CLK_SPI2_APB, "spi2_apb", 0, JH7100_CLK_APB2_BUS), + JH71X0_GDIV(JH7100_CLK_SPI2_CORE, "spi2_core", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GATE(JH7100_CLK_SPI3_APB, "spi3_apb", 0, JH7100_CLK_APB2_BUS), + JH71X0_GDIV(JH7100_CLK_SPI3_CORE, "spi3_core", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GATE(JH7100_CLK_I2C2_APB, "i2c2_apb", 0, JH7100_CLK_APB2_BUS), + JH71X0_GDIV(JH7100_CLK_I2C2_CORE, "i2c2_core", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GATE(JH7100_CLK_I2C3_APB, "i2c3_apb", 0, JH7100_CLK_APB2_BUS), + JH71X0_GDIV(JH7100_CLK_I2C3_CORE, "i2c3_core", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GATE(JH7100_CLK_WDTIMER_APB, "wdtimer_apb", 0, JH7100_CLK_APB2_BUS), + JH71X0_GDIV(JH7100_CLK_WDT_CORE, "wdt_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GDIV(JH7100_CLK_TIMER0_CORE, "timer0_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GDIV(JH7100_CLK_TIMER1_CORE, "timer1_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GDIV(JH7100_CLK_TIMER2_CORE, "timer2_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GDIV(JH7100_CLK_TIMER3_CORE, "timer3_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GDIV(JH7100_CLK_TIMER4_CORE, "timer4_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GDIV(JH7100_CLK_TIMER5_CORE, "timer5_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GDIV(JH7100_CLK_TIMER6_CORE, "timer6_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GATE(JH7100_CLK_VP6INTC_APB, "vp6intc_apb", 0, JH7100_CLK_APB2_BUS), + JH71X0_GATE(JH7100_CLK_PWM_APB, "pwm_apb", 0, JH7100_CLK_APB2_BUS), + JH71X0_GATE(JH7100_CLK_MSI_APB, "msi_apb", 0, JH7100_CLK_APB2_BUS), + JH71X0_GATE(JH7100_CLK_TEMP_APB, "temp_apb", 0, JH7100_CLK_APB2_BUS), + JH71X0_GDIV(JH7100_CLK_TEMP_SENSE, "temp_sense", 0, 31, JH7100_CLK_OSC_SYS), + JH71X0_GATE(JH7100_CLK_SYSERR_APB, "syserr_apb", 0, JH7100_CLK_APB2_BUS), }; static struct clk_hw *jh7100_clk_get(struct of_phandle_args *clkspec, void *data) { - struct jh7100_clk_priv *priv = data; + struct jh71x0_clk_priv *priv = data; unsigned int idx = clkspec->args[0]; if (idx < JH7100_CLK_PLL0_OUT) @@ -280,7 +283,7 @@ static struct clk_hw *jh7100_clk_get(struct of_phandle_args *clkspec, void *data static int __init clk_starfive_jh7100_probe(struct platform_device *pdev) { - struct jh7100_clk_priv *priv; + struct jh71x0_clk_priv *priv; unsigned int idx; int ret; @@ -314,12 +317,12 @@ static int __init clk_starfive_jh7100_probe(struct platform_device *pdev) struct clk_parent_data parents[4] = {}; struct clk_init_data init = { .name = jh7100_clk_data[idx].name, - .ops = starfive_jh7100_clk_ops(max), + .ops = starfive_jh71x0_clk_ops(max), .parent_data = parents, - .num_parents = ((max & JH7100_CLK_MUX_MASK) >> JH7100_CLK_MUX_SHIFT) + 1, + .num_parents = ((max & JH71X0_CLK_MUX_MASK) >> JH71X0_CLK_MUX_SHIFT) + 1, .flags = jh7100_clk_data[idx].flags, }; - struct jh7100_clk *clk = &priv->reg[idx]; + struct jh71x0_clk *clk = &priv->reg[idx]; unsigned int i; for (i = 0; i < init.num_parents; i++) { @@ -341,7 +344,7 @@ static int __init clk_starfive_jh7100_probe(struct platform_device *pdev) clk->hw.init = &init; clk->idx = idx; - clk->max_div = max & JH7100_CLK_DIV_MASK; + clk->max_div = max & JH71X0_CLK_DIV_MASK; ret = devm_clk_hw_register(priv->dev, &clk->hw); if (ret) diff --git a/drivers/clk/starfive/clk-starfive-jh71x0.c b/drivers/clk/starfive/clk-starfive-jh71x0.c index 0140bdf27a013..b372083d11c34 100644 --- a/drivers/clk/starfive/clk-starfive-jh71x0.c +++ b/drivers/clk/starfive/clk-starfive-jh71x0.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * StarFive JH7100 Clock Generator Driver + * StarFive JH71X0 Clock Generator Driver * * Copyright (C) 2021-2022 Emil Renner Berthing */ @@ -12,27 +12,27 @@ #include "clk-starfive-jh71x0.h" -static struct jh7100_clk *jh7100_clk_from(struct clk_hw *hw) +static struct jh71x0_clk *jh71x0_clk_from(struct clk_hw *hw) { - return container_of(hw, struct jh7100_clk, hw); + return container_of(hw, struct jh71x0_clk, hw); } -static struct jh7100_clk_priv *jh7100_priv_from(struct jh7100_clk *clk) +static struct jh71x0_clk_priv *jh71x0_priv_from(struct jh71x0_clk *clk) { - return container_of(clk, struct jh7100_clk_priv, reg[clk->idx]); + return container_of(clk, struct jh71x0_clk_priv, reg[clk->idx]); } -static u32 jh7100_clk_reg_get(struct jh7100_clk *clk) +static u32 jh71x0_clk_reg_get(struct jh71x0_clk *clk) { - struct jh7100_clk_priv *priv = jh7100_priv_from(clk); + struct jh71x0_clk_priv *priv = jh71x0_priv_from(clk); void __iomem *reg = priv->base + 4 * clk->idx; return readl_relaxed(reg); } -static void jh7100_clk_reg_rmw(struct jh7100_clk *clk, u32 mask, u32 value) +static void jh71x0_clk_reg_rmw(struct jh71x0_clk *clk, u32 mask, u32 value) { - struct jh7100_clk_priv *priv = jh7100_priv_from(clk); + struct jh71x0_clk_priv *priv = jh71x0_priv_from(clk); void __iomem *reg = priv->base + 4 * clk->idx; unsigned long flags; @@ -42,41 +42,41 @@ static void jh7100_clk_reg_rmw(struct jh7100_clk *clk, u32 mask, u32 value) spin_unlock_irqrestore(&priv->rmw_lock, flags); } -static int jh7100_clk_enable(struct clk_hw *hw) +static int jh71x0_clk_enable(struct clk_hw *hw) { - struct jh7100_clk *clk = jh7100_clk_from(hw); + struct jh71x0_clk *clk = jh71x0_clk_from(hw); - jh7100_clk_reg_rmw(clk, JH7100_CLK_ENABLE, JH7100_CLK_ENABLE); + jh71x0_clk_reg_rmw(clk, JH71X0_CLK_ENABLE, JH71X0_CLK_ENABLE); return 0; } -static void jh7100_clk_disable(struct clk_hw *hw) +static void jh71x0_clk_disable(struct clk_hw *hw) { - struct jh7100_clk *clk = jh7100_clk_from(hw); + struct jh71x0_clk *clk = jh71x0_clk_from(hw); - jh7100_clk_reg_rmw(clk, JH7100_CLK_ENABLE, 0); + jh71x0_clk_reg_rmw(clk, JH71X0_CLK_ENABLE, 0); } -static int jh7100_clk_is_enabled(struct clk_hw *hw) +static int jh71x0_clk_is_enabled(struct clk_hw *hw) { - struct jh7100_clk *clk = jh7100_clk_from(hw); + struct jh71x0_clk *clk = jh71x0_clk_from(hw); - return !!(jh7100_clk_reg_get(clk) & JH7100_CLK_ENABLE); + return !!(jh71x0_clk_reg_get(clk) & JH71X0_CLK_ENABLE); } -static unsigned long jh7100_clk_recalc_rate(struct clk_hw *hw, +static unsigned long jh71x0_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct jh7100_clk *clk = jh7100_clk_from(hw); - u32 div = jh7100_clk_reg_get(clk) & JH7100_CLK_DIV_MASK; + struct jh71x0_clk *clk = jh71x0_clk_from(hw); + u32 div = jh71x0_clk_reg_get(clk) & JH71X0_CLK_DIV_MASK; return div ? parent_rate / div : 0; } -static int jh7100_clk_determine_rate(struct clk_hw *hw, +static int jh71x0_clk_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { - struct jh7100_clk *clk = jh7100_clk_from(hw); + struct jh71x0_clk *clk = jh71x0_clk_from(hw); unsigned long parent = req->best_parent_rate; unsigned long rate = clamp(req->rate, req->min_rate, req->max_rate); unsigned long div = min_t(unsigned long, DIV_ROUND_UP(parent, rate), clk->max_div); @@ -102,232 +102,232 @@ static int jh7100_clk_determine_rate(struct clk_hw *hw, return 0; } -static int jh7100_clk_set_rate(struct clk_hw *hw, +static int jh71x0_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { - struct jh7100_clk *clk = jh7100_clk_from(hw); + struct jh71x0_clk *clk = jh71x0_clk_from(hw); unsigned long div = clamp(DIV_ROUND_CLOSEST(parent_rate, rate), 1UL, (unsigned long)clk->max_div); - jh7100_clk_reg_rmw(clk, JH7100_CLK_DIV_MASK, div); + jh71x0_clk_reg_rmw(clk, JH71X0_CLK_DIV_MASK, div); return 0; } -static unsigned long jh7100_clk_frac_recalc_rate(struct clk_hw *hw, +static unsigned long jh71x0_clk_frac_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct jh7100_clk *clk = jh7100_clk_from(hw); - u32 reg = jh7100_clk_reg_get(clk); - unsigned long div100 = 100 * (reg & JH7100_CLK_INT_MASK) + - ((reg & JH7100_CLK_FRAC_MASK) >> JH7100_CLK_FRAC_SHIFT); + struct jh71x0_clk *clk = jh71x0_clk_from(hw); + u32 reg = jh71x0_clk_reg_get(clk); + unsigned long div100 = 100 * (reg & JH71X0_CLK_INT_MASK) + + ((reg & JH71X0_CLK_FRAC_MASK) >> JH71X0_CLK_FRAC_SHIFT); - return (div100 >= JH7100_CLK_FRAC_MIN) ? 100 * parent_rate / div100 : 0; + return (div100 >= JH71X0_CLK_FRAC_MIN) ? 100 * parent_rate / div100 : 0; } -static int jh7100_clk_frac_determine_rate(struct clk_hw *hw, +static int jh71x0_clk_frac_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { unsigned long parent100 = 100 * req->best_parent_rate; unsigned long rate = clamp(req->rate, req->min_rate, req->max_rate); unsigned long div100 = clamp(DIV_ROUND_CLOSEST(parent100, rate), - JH7100_CLK_FRAC_MIN, JH7100_CLK_FRAC_MAX); + JH71X0_CLK_FRAC_MIN, JH71X0_CLK_FRAC_MAX); unsigned long result = parent100 / div100; - /* clamp the result as in jh7100_clk_determine_rate() above */ - if (result > req->max_rate && div100 < JH7100_CLK_FRAC_MAX) + /* clamp the result as in jh71x0_clk_determine_rate() above */ + if (result > req->max_rate && div100 < JH71X0_CLK_FRAC_MAX) result = parent100 / (div100 + 1); - if (result < req->min_rate && div100 > JH7100_CLK_FRAC_MIN) + if (result < req->min_rate && div100 > JH71X0_CLK_FRAC_MIN) result = parent100 / (div100 - 1); req->rate = result; return 0; } -static int jh7100_clk_frac_set_rate(struct clk_hw *hw, +static int jh71x0_clk_frac_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { - struct jh7100_clk *clk = jh7100_clk_from(hw); + struct jh71x0_clk *clk = jh71x0_clk_from(hw); unsigned long div100 = clamp(DIV_ROUND_CLOSEST(100 * parent_rate, rate), - JH7100_CLK_FRAC_MIN, JH7100_CLK_FRAC_MAX); - u32 value = ((div100 % 100) << JH7100_CLK_FRAC_SHIFT) | (div100 / 100); + JH71X0_CLK_FRAC_MIN, JH71X0_CLK_FRAC_MAX); + u32 value = ((div100 % 100) << JH71X0_CLK_FRAC_SHIFT) | (div100 / 100); - jh7100_clk_reg_rmw(clk, JH7100_CLK_DIV_MASK, value); + jh71x0_clk_reg_rmw(clk, JH71X0_CLK_DIV_MASK, value); return 0; } -static u8 jh7100_clk_get_parent(struct clk_hw *hw) +static u8 jh71x0_clk_get_parent(struct clk_hw *hw) { - struct jh7100_clk *clk = jh7100_clk_from(hw); - u32 value = jh7100_clk_reg_get(clk); + struct jh71x0_clk *clk = jh71x0_clk_from(hw); + u32 value = jh71x0_clk_reg_get(clk); - return (value & JH7100_CLK_MUX_MASK) >> JH7100_CLK_MUX_SHIFT; + return (value & JH71X0_CLK_MUX_MASK) >> JH71X0_CLK_MUX_SHIFT; } -static int jh7100_clk_set_parent(struct clk_hw *hw, u8 index) +static int jh71x0_clk_set_parent(struct clk_hw *hw, u8 index) { - struct jh7100_clk *clk = jh7100_clk_from(hw); - u32 value = (u32)index << JH7100_CLK_MUX_SHIFT; + struct jh71x0_clk *clk = jh71x0_clk_from(hw); + u32 value = (u32)index << JH71X0_CLK_MUX_SHIFT; - jh7100_clk_reg_rmw(clk, JH7100_CLK_MUX_MASK, value); + jh71x0_clk_reg_rmw(clk, JH71X0_CLK_MUX_MASK, value); return 0; } -static int jh7100_clk_mux_determine_rate(struct clk_hw *hw, +static int jh71x0_clk_mux_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { return clk_mux_determine_rate_flags(hw, req, 0); } -static int jh7100_clk_get_phase(struct clk_hw *hw) +static int jh71x0_clk_get_phase(struct clk_hw *hw) { - struct jh7100_clk *clk = jh7100_clk_from(hw); - u32 value = jh7100_clk_reg_get(clk); + struct jh71x0_clk *clk = jh71x0_clk_from(hw); + u32 value = jh71x0_clk_reg_get(clk); - return (value & JH7100_CLK_INVERT) ? 180 : 0; + return (value & JH71X0_CLK_INVERT) ? 180 : 0; } -static int jh7100_clk_set_phase(struct clk_hw *hw, int degrees) +static int jh71x0_clk_set_phase(struct clk_hw *hw, int degrees) { - struct jh7100_clk *clk = jh7100_clk_from(hw); + struct jh71x0_clk *clk = jh71x0_clk_from(hw); u32 value; if (degrees == 0) value = 0; else if (degrees == 180) - value = JH7100_CLK_INVERT; + value = JH71X0_CLK_INVERT; else return -EINVAL; - jh7100_clk_reg_rmw(clk, JH7100_CLK_INVERT, value); + jh71x0_clk_reg_rmw(clk, JH71X0_CLK_INVERT, value); return 0; } #ifdef CONFIG_DEBUG_FS -static void jh7100_clk_debug_init(struct clk_hw *hw, struct dentry *dentry) +static void jh71x0_clk_debug_init(struct clk_hw *hw, struct dentry *dentry) { - static const struct debugfs_reg32 jh7100_clk_reg = { + static const struct debugfs_reg32 jh71x0_clk_reg = { .name = "CTRL", .offset = 0, }; - struct jh7100_clk *clk = jh7100_clk_from(hw); - struct jh7100_clk_priv *priv = jh7100_priv_from(clk); + struct jh71x0_clk *clk = jh71x0_clk_from(hw); + struct jh71x0_clk_priv *priv = jh71x0_priv_from(clk); struct debugfs_regset32 *regset; regset = devm_kzalloc(priv->dev, sizeof(*regset), GFP_KERNEL); if (!regset) return; - regset->regs = &jh7100_clk_reg; + regset->regs = &jh71x0_clk_reg; regset->nregs = 1; regset->base = priv->base + 4 * clk->idx; debugfs_create_regset32("registers", 0400, dentry, regset); } #else -#define jh7100_clk_debug_init NULL +#define jh71x0_clk_debug_init NULL #endif -static const struct clk_ops jh7100_clk_gate_ops = { - .enable = jh7100_clk_enable, - .disable = jh7100_clk_disable, - .is_enabled = jh7100_clk_is_enabled, - .debug_init = jh7100_clk_debug_init, +static const struct clk_ops jh71x0_clk_gate_ops = { + .enable = jh71x0_clk_enable, + .disable = jh71x0_clk_disable, + .is_enabled = jh71x0_clk_is_enabled, + .debug_init = jh71x0_clk_debug_init, }; -static const struct clk_ops jh7100_clk_div_ops = { - .recalc_rate = jh7100_clk_recalc_rate, - .determine_rate = jh7100_clk_determine_rate, - .set_rate = jh7100_clk_set_rate, - .debug_init = jh7100_clk_debug_init, +static const struct clk_ops jh71x0_clk_div_ops = { + .recalc_rate = jh71x0_clk_recalc_rate, + .determine_rate = jh71x0_clk_determine_rate, + .set_rate = jh71x0_clk_set_rate, + .debug_init = jh71x0_clk_debug_init, }; -static const struct clk_ops jh7100_clk_fdiv_ops = { - .recalc_rate = jh7100_clk_frac_recalc_rate, - .determine_rate = jh7100_clk_frac_determine_rate, - .set_rate = jh7100_clk_frac_set_rate, - .debug_init = jh7100_clk_debug_init, +static const struct clk_ops jh71x0_clk_fdiv_ops = { + .recalc_rate = jh71x0_clk_frac_recalc_rate, + .determine_rate = jh71x0_clk_frac_determine_rate, + .set_rate = jh71x0_clk_frac_set_rate, + .debug_init = jh71x0_clk_debug_init, }; -static const struct clk_ops jh7100_clk_gdiv_ops = { - .enable = jh7100_clk_enable, - .disable = jh7100_clk_disable, - .is_enabled = jh7100_clk_is_enabled, - .recalc_rate = jh7100_clk_recalc_rate, - .determine_rate = jh7100_clk_determine_rate, - .set_rate = jh7100_clk_set_rate, - .debug_init = jh7100_clk_debug_init, +static const struct clk_ops jh71x0_clk_gdiv_ops = { + .enable = jh71x0_clk_enable, + .disable = jh71x0_clk_disable, + .is_enabled = jh71x0_clk_is_enabled, + .recalc_rate = jh71x0_clk_recalc_rate, + .determine_rate = jh71x0_clk_determine_rate, + .set_rate = jh71x0_clk_set_rate, + .debug_init = jh71x0_clk_debug_init, }; -static const struct clk_ops jh7100_clk_mux_ops = { - .determine_rate = jh7100_clk_mux_determine_rate, - .set_parent = jh7100_clk_set_parent, - .get_parent = jh7100_clk_get_parent, - .debug_init = jh7100_clk_debug_init, +static const struct clk_ops jh71x0_clk_mux_ops = { + .determine_rate = jh71x0_clk_mux_determine_rate, + .set_parent = jh71x0_clk_set_parent, + .get_parent = jh71x0_clk_get_parent, + .debug_init = jh71x0_clk_debug_init, }; -static const struct clk_ops jh7100_clk_gmux_ops = { - .enable = jh7100_clk_enable, - .disable = jh7100_clk_disable, - .is_enabled = jh7100_clk_is_enabled, - .determine_rate = jh7100_clk_mux_determine_rate, - .set_parent = jh7100_clk_set_parent, - .get_parent = jh7100_clk_get_parent, - .debug_init = jh7100_clk_debug_init, +static const struct clk_ops jh71x0_clk_gmux_ops = { + .enable = jh71x0_clk_enable, + .disable = jh71x0_clk_disable, + .is_enabled = jh71x0_clk_is_enabled, + .determine_rate = jh71x0_clk_mux_determine_rate, + .set_parent = jh71x0_clk_set_parent, + .get_parent = jh71x0_clk_get_parent, + .debug_init = jh71x0_clk_debug_init, }; -static const struct clk_ops jh7100_clk_mdiv_ops = { - .recalc_rate = jh7100_clk_recalc_rate, - .determine_rate = jh7100_clk_determine_rate, - .get_parent = jh7100_clk_get_parent, - .set_parent = jh7100_clk_set_parent, - .set_rate = jh7100_clk_set_rate, - .debug_init = jh7100_clk_debug_init, +static const struct clk_ops jh71x0_clk_mdiv_ops = { + .recalc_rate = jh71x0_clk_recalc_rate, + .determine_rate = jh71x0_clk_determine_rate, + .get_parent = jh71x0_clk_get_parent, + .set_parent = jh71x0_clk_set_parent, + .set_rate = jh71x0_clk_set_rate, + .debug_init = jh71x0_clk_debug_init, }; -static const struct clk_ops jh7100_clk_gmd_ops = { - .enable = jh7100_clk_enable, - .disable = jh7100_clk_disable, - .is_enabled = jh7100_clk_is_enabled, - .recalc_rate = jh7100_clk_recalc_rate, - .determine_rate = jh7100_clk_determine_rate, - .get_parent = jh7100_clk_get_parent, - .set_parent = jh7100_clk_set_parent, - .set_rate = jh7100_clk_set_rate, - .debug_init = jh7100_clk_debug_init, +static const struct clk_ops jh71x0_clk_gmd_ops = { + .enable = jh71x0_clk_enable, + .disable = jh71x0_clk_disable, + .is_enabled = jh71x0_clk_is_enabled, + .recalc_rate = jh71x0_clk_recalc_rate, + .determine_rate = jh71x0_clk_determine_rate, + .get_parent = jh71x0_clk_get_parent, + .set_parent = jh71x0_clk_set_parent, + .set_rate = jh71x0_clk_set_rate, + .debug_init = jh71x0_clk_debug_init, }; -static const struct clk_ops jh7100_clk_inv_ops = { - .get_phase = jh7100_clk_get_phase, - .set_phase = jh7100_clk_set_phase, - .debug_init = jh7100_clk_debug_init, +static const struct clk_ops jh71x0_clk_inv_ops = { + .get_phase = jh71x0_clk_get_phase, + .set_phase = jh71x0_clk_set_phase, + .debug_init = jh71x0_clk_debug_init, }; -const struct clk_ops *starfive_jh7100_clk_ops(u32 max) +const struct clk_ops *starfive_jh71x0_clk_ops(u32 max) { - if (max & JH7100_CLK_DIV_MASK) { - if (max & JH7100_CLK_MUX_MASK) { - if (max & JH7100_CLK_ENABLE) - return &jh7100_clk_gmd_ops; - return &jh7100_clk_mdiv_ops; + if (max & JH71X0_CLK_DIV_MASK) { + if (max & JH71X0_CLK_MUX_MASK) { + if (max & JH71X0_CLK_ENABLE) + return &jh71x0_clk_gmd_ops; + return &jh71x0_clk_mdiv_ops; } - if (max & JH7100_CLK_ENABLE) - return &jh7100_clk_gdiv_ops; - if (max == JH7100_CLK_FRAC_MAX) - return &jh7100_clk_fdiv_ops; - return &jh7100_clk_div_ops; + if (max & JH71X0_CLK_ENABLE) + return &jh71x0_clk_gdiv_ops; + if (max == JH71X0_CLK_FRAC_MAX) + return &jh71x0_clk_fdiv_ops; + return &jh71x0_clk_div_ops; } - if (max & JH7100_CLK_MUX_MASK) { - if (max & JH7100_CLK_ENABLE) - return &jh7100_clk_gmux_ops; - return &jh7100_clk_mux_ops; + if (max & JH71X0_CLK_MUX_MASK) { + if (max & JH71X0_CLK_ENABLE) + return &jh71x0_clk_gmux_ops; + return &jh71x0_clk_mux_ops; } - if (max & JH7100_CLK_ENABLE) - return &jh7100_clk_gate_ops; + if (max & JH71X0_CLK_ENABLE) + return &jh71x0_clk_gate_ops; - return &jh7100_clk_inv_ops; + return &jh71x0_clk_inv_ops; } -EXPORT_SYMBOL_GPL(starfive_jh7100_clk_ops); +EXPORT_SYMBOL_GPL(starfive_jh71x0_clk_ops); diff --git a/drivers/clk/starfive/clk-starfive-jh71x0.h b/drivers/clk/starfive/clk-starfive-jh71x0.h index a8ba6e25b5ce2..34bb11c72eb73 100644 --- a/drivers/clk/starfive/clk-starfive-jh71x0.h +++ b/drivers/clk/starfive/clk-starfive-jh71x0.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __CLK_STARFIVE_JH7100_H -#define __CLK_STARFIVE_JH7100_H +#ifndef __CLK_STARFIVE_JH71X0_H +#define __CLK_STARFIVE_JH71X0_H #include #include @@ -8,107 +8,116 @@ #include /* register fields */ -#define JH7100_CLK_ENABLE BIT(31) -#define JH7100_CLK_INVERT BIT(30) -#define JH7100_CLK_MUX_MASK GENMASK(27, 24) -#define JH7100_CLK_MUX_SHIFT 24 -#define JH7100_CLK_DIV_MASK GENMASK(23, 0) -#define JH7100_CLK_FRAC_MASK GENMASK(15, 8) -#define JH7100_CLK_FRAC_SHIFT 8 -#define JH7100_CLK_INT_MASK GENMASK(7, 0) +#define JH71X0_CLK_ENABLE BIT(31) +#define JH71X0_CLK_INVERT BIT(30) +#define JH71X0_CLK_MUX_MASK GENMASK(27, 24) +#define JH71X0_CLK_MUX_SHIFT 24 +#define JH71X0_CLK_DIV_MASK GENMASK(23, 0) +#define JH71X0_CLK_FRAC_MASK GENMASK(15, 8) +#define JH71X0_CLK_FRAC_SHIFT 8 +#define JH71X0_CLK_INT_MASK GENMASK(7, 0) /* fractional divider min/max */ -#define JH7100_CLK_FRAC_MIN 100UL -#define JH7100_CLK_FRAC_MAX 25599UL +#define JH71X0_CLK_FRAC_MIN 100UL +#define JH71X0_CLK_FRAC_MAX 25599UL /* clock data */ -struct jh7100_clk_data { +struct jh71x0_clk_data { const char *name; unsigned long flags; u32 max; u8 parents[4]; }; -#define JH7100_GATE(_idx, _name, _flags, _parent) [_idx] = { \ +#define JH71X0_GATE(_idx, _name, _flags, _parent) \ +[_idx] = { \ .name = _name, \ .flags = CLK_SET_RATE_PARENT | (_flags), \ - .max = JH7100_CLK_ENABLE, \ + .max = JH71X0_CLK_ENABLE, \ .parents = { [0] = _parent }, \ } -#define JH7100__DIV(_idx, _name, _max, _parent) [_idx] = { \ +#define JH71X0__DIV(_idx, _name, _max, _parent) \ +[_idx] = { \ .name = _name, \ .flags = 0, \ .max = _max, \ .parents = { [0] = _parent }, \ } -#define JH7100_GDIV(_idx, _name, _flags, _max, _parent) [_idx] = { \ +#define JH71X0_GDIV(_idx, _name, _flags, _max, _parent) \ +[_idx] = { \ .name = _name, \ .flags = _flags, \ - .max = JH7100_CLK_ENABLE | (_max), \ + .max = JH71X0_CLK_ENABLE | (_max), \ .parents = { [0] = _parent }, \ } -#define JH7100_FDIV(_idx, _name, _parent) [_idx] = { \ +#define JH71X0_FDIV(_idx, _name, _parent) \ +[_idx] = { \ .name = _name, \ .flags = 0, \ - .max = JH7100_CLK_FRAC_MAX, \ + .max = JH71X0_CLK_FRAC_MAX, \ .parents = { [0] = _parent }, \ } -#define JH7100__MUX(_idx, _name, _nparents, ...) [_idx] = { \ +#define JH71X0__MUX(_idx, _name, _nparents, ...) \ +[_idx] = { \ .name = _name, \ .flags = 0, \ - .max = ((_nparents) - 1) << JH7100_CLK_MUX_SHIFT, \ + .max = ((_nparents) - 1) << JH71X0_CLK_MUX_SHIFT, \ .parents = { __VA_ARGS__ }, \ } -#define JH7100_GMUX(_idx, _name, _flags, _nparents, ...) [_idx] = { \ +#define JH71X0_GMUX(_idx, _name, _flags, _nparents, ...) \ +[_idx] = { \ .name = _name, \ .flags = _flags, \ - .max = JH7100_CLK_ENABLE | \ - (((_nparents) - 1) << JH7100_CLK_MUX_SHIFT), \ + .max = JH71X0_CLK_ENABLE | \ + (((_nparents) - 1) << JH71X0_CLK_MUX_SHIFT), \ .parents = { __VA_ARGS__ }, \ } -#define JH7100_MDIV(_idx, _name, _max, _nparents, ...) [_idx] = { \ +#define JH71X0_MDIV(_idx, _name, _max, _nparents, ...) \ +[_idx] = { \ .name = _name, \ .flags = 0, \ - .max = (((_nparents) - 1) << JH7100_CLK_MUX_SHIFT) | (_max), \ + .max = (((_nparents) - 1) << JH71X0_CLK_MUX_SHIFT) | (_max), \ .parents = { __VA_ARGS__ }, \ } -#define JH7100__GMD(_idx, _name, _flags, _max, _nparents, ...) [_idx] = { \ +#define JH71X0__GMD(_idx, _name, _flags, _max, _nparents, ...) \ +[_idx] = { \ .name = _name, \ .flags = _flags, \ - .max = JH7100_CLK_ENABLE | \ - (((_nparents) - 1) << JH7100_CLK_MUX_SHIFT) | (_max), \ + .max = JH71X0_CLK_ENABLE | \ + (((_nparents) - 1) << JH71X0_CLK_MUX_SHIFT) | (_max), \ .parents = { __VA_ARGS__ }, \ } -#define JH7100__INV(_idx, _name, _parent) [_idx] = { \ +#define JH71X0__INV(_idx, _name, _parent) \ +[_idx] = { \ .name = _name, \ .flags = CLK_SET_RATE_PARENT, \ - .max = JH7100_CLK_INVERT, \ + .max = JH71X0_CLK_INVERT, \ .parents = { [0] = _parent }, \ } -struct jh7100_clk { +struct jh71x0_clk { struct clk_hw hw; unsigned int idx; unsigned int max_div; }; -struct jh7100_clk_priv { +struct jh71x0_clk_priv { /* protect clk enable and set rate/parent from happening at the same time */ spinlock_t rmw_lock; struct device *dev; void __iomem *base; struct clk_hw *pll[3]; - struct jh7100_clk reg[]; + struct jh71x0_clk reg[]; }; -const struct clk_ops *starfive_jh7100_clk_ops(u32 max); +const struct clk_ops *starfive_jh71x0_clk_ops(u32 max); #endif From 3100fda69cb2b46ee8b054028306e2fc1783e695 Mon Sep 17 00:00:00 2001 From: Hal Feng Date: Thu, 9 Mar 2023 15:04:34 +0800 Subject: [PATCH 05/95] reset: starfive: Replace SOC_STARFIVE with ARCH_STARFIVE Using ARCH_FOO symbol is preferred than SOC_FOO. Reviewed-by: Philipp Zabel Reviewed-by: Conor Dooley Signed-off-by: Hal Feng --- drivers/reset/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 2a52c990d4fec..6ae5aa46a6b25 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -234,8 +234,8 @@ config RESET_SOCFPGA config RESET_STARFIVE_JH7100 bool "StarFive JH7100 Reset Driver" - depends on SOC_STARFIVE || COMPILE_TEST - default SOC_STARFIVE + depends on ARCH_STARFIVE || COMPILE_TEST + default ARCH_STARFIVE help This enables the reset controller driver for the StarFive JH7100 SoC. From 970f77ae087c620cb0d1ec7a5e0cfd066121dbb9 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sat, 20 Nov 2021 18:30:33 +0100 Subject: [PATCH 06/95] reset: Create subdirectory for StarFive drivers This moves the StarFive JH7100 reset driver to a new subdirectory in preparation for adding more StarFive reset drivers. Reviewed-by: Philipp Zabel Tested-by: Tommaso Merciai Reviewed-by: Conor Dooley Signed-off-by: Emil Renner Berthing Signed-off-by: Hal Feng --- MAINTAINERS | 2 +- drivers/reset/Kconfig | 8 +------- drivers/reset/Makefile | 2 +- drivers/reset/starfive/Kconfig | 8 ++++++++ drivers/reset/starfive/Makefile | 2 ++ drivers/reset/{ => starfive}/reset-starfive-jh7100.c | 0 6 files changed, 13 insertions(+), 9 deletions(-) create mode 100644 drivers/reset/starfive/Kconfig create mode 100644 drivers/reset/starfive/Makefile rename drivers/reset/{ => starfive}/reset-starfive-jh7100.c (100%) diff --git a/MAINTAINERS b/MAINTAINERS index d9ad43857ac53..335664479ead9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19932,7 +19932,7 @@ STARFIVE JH7100 RESET CONTROLLER DRIVER M: Emil Renner Berthing S: Maintained F: Documentation/devicetree/bindings/reset/starfive,jh7100-reset.yaml -F: drivers/reset/reset-starfive-jh7100.c +F: drivers/reset/starfive/reset-starfive-jh7100.c F: include/dt-bindings/reset/starfive-jh7100.h STARFIVE JH71XX PMU CONTROLLER DRIVER diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 6ae5aa46a6b25..6aa8f243b30c6 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -232,13 +232,6 @@ config RESET_SOCFPGA This enables the reset driver for the SoCFPGA ARMv7 platforms. This driver gets initialized early during platform init calls. -config RESET_STARFIVE_JH7100 - bool "StarFive JH7100 Reset Driver" - depends on ARCH_STARFIVE || COMPILE_TEST - default ARCH_STARFIVE - help - This enables the reset controller driver for the StarFive JH7100 SoC. - config RESET_SUNPLUS bool "Sunplus SoCs Reset Driver" if COMPILE_TEST default ARCH_SUNPLUS @@ -320,6 +313,7 @@ config RESET_ZYNQ help This enables the reset controller driver for Xilinx Zynq SoCs. +source "drivers/reset/starfive/Kconfig" source "drivers/reset/sti/Kconfig" source "drivers/reset/hisilicon/Kconfig" source "drivers/reset/tegra/Kconfig" diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 3e7e5fd633a8e..7fec5af6c9645 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-y += core.o obj-y += hisilicon/ +obj-y += starfive/ obj-$(CONFIG_ARCH_STI) += sti/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_RESET_A10SR) += reset-a10sr.o @@ -30,7 +31,6 @@ obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o obj-$(CONFIG_RESET_SCMI) += reset-scmi.o obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o -obj-$(CONFIG_RESET_STARFIVE_JH7100) += reset-starfive-jh7100.o obj-$(CONFIG_RESET_SUNPLUS) += reset-sunplus.o obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o diff --git a/drivers/reset/starfive/Kconfig b/drivers/reset/starfive/Kconfig new file mode 100644 index 0000000000000..abbf0c52d03ea --- /dev/null +++ b/drivers/reset/starfive/Kconfig @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config RESET_STARFIVE_JH7100 + bool "StarFive JH7100 Reset Driver" + depends on ARCH_STARFIVE || COMPILE_TEST + default ARCH_STARFIVE + help + This enables the reset controller driver for the StarFive JH7100 SoC. diff --git a/drivers/reset/starfive/Makefile b/drivers/reset/starfive/Makefile new file mode 100644 index 0000000000000..670d049423f54 --- /dev/null +++ b/drivers/reset/starfive/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_RESET_STARFIVE_JH7100) += reset-starfive-jh7100.o diff --git a/drivers/reset/reset-starfive-jh7100.c b/drivers/reset/starfive/reset-starfive-jh7100.c similarity index 100% rename from drivers/reset/reset-starfive-jh7100.c rename to drivers/reset/starfive/reset-starfive-jh7100.c From c2766bd00738914348926ae63e2c5b9087621b78 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sat, 9 Jul 2022 23:32:56 +0200 Subject: [PATCH 07/95] reset: starfive: Factor out common JH71X0 reset code The StarFive JH7100 SoC has additional reset controllers for audio and video, but the registers follow the same structure. On the JH7110 the reset registers don't get their own memory range, but instead follow the clock control registers. The registers still follow the same structure though, so let's factor out the common code to handle all these cases. Tested-by: Tommaso Merciai Reviewed-by: Conor Dooley Signed-off-by: Emil Renner Berthing Signed-off-by: Hal Feng --- MAINTAINERS | 4 +- drivers/reset/starfive/Kconfig | 4 + drivers/reset/starfive/Makefile | 2 + .../reset/starfive/reset-starfive-jh7100.c | 150 +--------------- .../reset/starfive/reset-starfive-jh71x0.c | 162 ++++++++++++++++++ .../reset/starfive/reset-starfive-jh71x0.h | 11 ++ 6 files changed, 182 insertions(+), 151 deletions(-) create mode 100644 drivers/reset/starfive/reset-starfive-jh71x0.c create mode 100644 drivers/reset/starfive/reset-starfive-jh71x0.h diff --git a/MAINTAINERS b/MAINTAINERS index 335664479ead9..4c788fa15cc47 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19928,11 +19928,11 @@ F: drivers/pinctrl/starfive/pinctrl-starfive-jh71* F: include/dt-bindings/pinctrl/pinctrl-starfive-jh7100.h F: include/dt-bindings/pinctrl/starfive,jh7110-pinctrl.h -STARFIVE JH7100 RESET CONTROLLER DRIVER +STARFIVE JH71X0 RESET CONTROLLER DRIVERS M: Emil Renner Berthing S: Maintained F: Documentation/devicetree/bindings/reset/starfive,jh7100-reset.yaml -F: drivers/reset/starfive/reset-starfive-jh7100.c +F: drivers/reset/starfive/reset-starfive-jh71* F: include/dt-bindings/reset/starfive-jh7100.h STARFIVE JH71XX PMU CONTROLLER DRIVER diff --git a/drivers/reset/starfive/Kconfig b/drivers/reset/starfive/Kconfig index abbf0c52d03ea..1927a5a3b53a3 100644 --- a/drivers/reset/starfive/Kconfig +++ b/drivers/reset/starfive/Kconfig @@ -1,8 +1,12 @@ # SPDX-License-Identifier: GPL-2.0-only +config RESET_STARFIVE_JH71X0 + bool + config RESET_STARFIVE_JH7100 bool "StarFive JH7100 Reset Driver" depends on ARCH_STARFIVE || COMPILE_TEST + select RESET_STARFIVE_JH71X0 default ARCH_STARFIVE help This enables the reset controller driver for the StarFive JH7100 SoC. diff --git a/drivers/reset/starfive/Makefile b/drivers/reset/starfive/Makefile index 670d049423f54..f6aa12466fad9 100644 --- a/drivers/reset/starfive/Makefile +++ b/drivers/reset/starfive/Makefile @@ -1,2 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_RESET_STARFIVE_JH71X0) += reset-starfive-jh71x0.o + obj-$(CONFIG_RESET_STARFIVE_JH7100) += reset-starfive-jh7100.o diff --git a/drivers/reset/starfive/reset-starfive-jh7100.c b/drivers/reset/starfive/reset-starfive-jh7100.c index fc44b2fb3e031..5a68327c1f6ab 100644 --- a/drivers/reset/starfive/reset-starfive-jh7100.c +++ b/drivers/reset/starfive/reset-starfive-jh7100.c @@ -5,158 +5,10 @@ * Copyright (C) 2021 Emil Renner Berthing */ -#include -#include -#include -#include #include #include -#include -#include -#include - -/* register offsets */ -#define JH7100_RESET_ASSERT0 0x00 -#define JH7100_RESET_ASSERT1 0x04 -#define JH7100_RESET_ASSERT2 0x08 -#define JH7100_RESET_ASSERT3 0x0c -#define JH7100_RESET_STATUS0 0x10 -#define JH7100_RESET_STATUS1 0x14 -#define JH7100_RESET_STATUS2 0x18 -#define JH7100_RESET_STATUS3 0x1c - -/* - * Writing a 1 to the n'th bit of the m'th ASSERT register asserts - * line 32m + n, and writing a 0 deasserts the same line. - * Most reset lines have their status inverted so a 0 bit in the STATUS - * register means the line is asserted and a 1 means it's deasserted. A few - * lines don't though, so store the expected value of the status registers when - * all lines are asserted. - */ -static const u64 jh7100_reset_asserted[2] = { - /* STATUS0 */ - BIT_ULL_MASK(JH7100_RST_U74) | - BIT_ULL_MASK(JH7100_RST_VP6_DRESET) | - BIT_ULL_MASK(JH7100_RST_VP6_BRESET) | - /* STATUS1 */ - BIT_ULL_MASK(JH7100_RST_HIFI4_DRESET) | - BIT_ULL_MASK(JH7100_RST_HIFI4_BRESET), - /* STATUS2 */ - BIT_ULL_MASK(JH7100_RST_E24) | - /* STATUS3 */ - 0, -}; - -struct jh7100_reset { - struct reset_controller_dev rcdev; - /* protect registers against concurrent read-modify-write */ - spinlock_t lock; - void __iomem *base; -}; - -static inline struct jh7100_reset * -jh7100_reset_from(struct reset_controller_dev *rcdev) -{ - return container_of(rcdev, struct jh7100_reset, rcdev); -} - -static int jh7100_reset_update(struct reset_controller_dev *rcdev, - unsigned long id, bool assert) -{ - struct jh7100_reset *data = jh7100_reset_from(rcdev); - unsigned long offset = BIT_ULL_WORD(id); - u64 mask = BIT_ULL_MASK(id); - void __iomem *reg_assert = data->base + JH7100_RESET_ASSERT0 + offset * sizeof(u64); - void __iomem *reg_status = data->base + JH7100_RESET_STATUS0 + offset * sizeof(u64); - u64 done = jh7100_reset_asserted[offset] & mask; - u64 value; - unsigned long flags; - int ret; - - if (!assert) - done ^= mask; - - spin_lock_irqsave(&data->lock, flags); - - value = readq(reg_assert); - if (assert) - value |= mask; - else - value &= ~mask; - writeq(value, reg_assert); - - /* if the associated clock is gated, deasserting might otherwise hang forever */ - ret = readq_poll_timeout_atomic(reg_status, value, (value & mask) == done, 0, 1000); - - spin_unlock_irqrestore(&data->lock, flags); - return ret; -} - -static int jh7100_reset_assert(struct reset_controller_dev *rcdev, - unsigned long id) -{ - return jh7100_reset_update(rcdev, id, true); -} - -static int jh7100_reset_deassert(struct reset_controller_dev *rcdev, - unsigned long id) -{ - return jh7100_reset_update(rcdev, id, false); -} - -static int jh7100_reset_reset(struct reset_controller_dev *rcdev, - unsigned long id) -{ - int ret; - - ret = jh7100_reset_assert(rcdev, id); - if (ret) - return ret; - - return jh7100_reset_deassert(rcdev, id); -} - -static int jh7100_reset_status(struct reset_controller_dev *rcdev, - unsigned long id) -{ - struct jh7100_reset *data = jh7100_reset_from(rcdev); - unsigned long offset = BIT_ULL_WORD(id); - u64 mask = BIT_ULL_MASK(id); - void __iomem *reg_status = data->base + JH7100_RESET_STATUS0 + offset * sizeof(u64); - u64 value = readq(reg_status); - - return !((value ^ jh7100_reset_asserted[offset]) & mask); -} - -static const struct reset_control_ops jh7100_reset_ops = { - .assert = jh7100_reset_assert, - .deassert = jh7100_reset_deassert, - .reset = jh7100_reset_reset, - .status = jh7100_reset_status, -}; - -static int __init jh7100_reset_probe(struct platform_device *pdev) -{ - struct jh7100_reset *data; - - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(data->base)) - return PTR_ERR(data->base); - - data->rcdev.ops = &jh7100_reset_ops; - data->rcdev.owner = THIS_MODULE; - data->rcdev.nr_resets = JH7100_RSTN_END; - data->rcdev.dev = &pdev->dev; - data->rcdev.of_node = pdev->dev.of_node; - spin_lock_init(&data->lock); - - return devm_reset_controller_register(&pdev->dev, &data->rcdev); -} +#include "reset-starfive-jh71x0.h" static const struct of_device_id jh7100_reset_dt_ids[] = { { .compatible = "starfive,jh7100-reset" }, diff --git a/drivers/reset/starfive/reset-starfive-jh71x0.c b/drivers/reset/starfive/reset-starfive-jh71x0.c new file mode 100644 index 0000000000000..114a13c4b8a61 --- /dev/null +++ b/drivers/reset/starfive/reset-starfive-jh71x0.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Reset driver for the StarFive JH7100 SoC + * + * Copyright (C) 2021 Emil Renner Berthing + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "reset-starfive-jh71x0.h" + +#include + +/* register offsets */ +#define JH7100_RESET_ASSERT0 0x00 +#define JH7100_RESET_ASSERT1 0x04 +#define JH7100_RESET_ASSERT2 0x08 +#define JH7100_RESET_ASSERT3 0x0c +#define JH7100_RESET_STATUS0 0x10 +#define JH7100_RESET_STATUS1 0x14 +#define JH7100_RESET_STATUS2 0x18 +#define JH7100_RESET_STATUS3 0x1c + +/* + * Writing a 1 to the n'th bit of the m'th ASSERT register asserts + * line 32m + n, and writing a 0 deasserts the same line. + * Most reset lines have their status inverted so a 0 bit in the STATUS + * register means the line is asserted and a 1 means it's deasserted. A few + * lines don't though, so store the expected value of the status registers when + * all lines are asserted. + */ +static const u64 jh7100_reset_asserted[2] = { + /* STATUS0 */ + BIT_ULL_MASK(JH7100_RST_U74) | + BIT_ULL_MASK(JH7100_RST_VP6_DRESET) | + BIT_ULL_MASK(JH7100_RST_VP6_BRESET) | + /* STATUS1 */ + BIT_ULL_MASK(JH7100_RST_HIFI4_DRESET) | + BIT_ULL_MASK(JH7100_RST_HIFI4_BRESET), + /* STATUS2 */ + BIT_ULL_MASK(JH7100_RST_E24) | + /* STATUS3 */ + 0, +}; + +struct jh7100_reset { + struct reset_controller_dev rcdev; + /* protect registers against concurrent read-modify-write */ + spinlock_t lock; + void __iomem *base; +}; + +static inline struct jh7100_reset * +jh7100_reset_from(struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct jh7100_reset, rcdev); +} + +static int jh7100_reset_update(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) +{ + struct jh7100_reset *data = jh7100_reset_from(rcdev); + unsigned long offset = BIT_ULL_WORD(id); + u64 mask = BIT_ULL_MASK(id); + void __iomem *reg_assert = data->base + JH7100_RESET_ASSERT0 + offset * sizeof(u64); + void __iomem *reg_status = data->base + JH7100_RESET_STATUS0 + offset * sizeof(u64); + u64 done = jh7100_reset_asserted[offset] & mask; + u64 value; + unsigned long flags; + int ret; + + if (!assert) + done ^= mask; + + spin_lock_irqsave(&data->lock, flags); + + value = readq(reg_assert); + if (assert) + value |= mask; + else + value &= ~mask; + writeq(value, reg_assert); + + /* if the associated clock is gated, deasserting might otherwise hang forever */ + ret = readq_poll_timeout_atomic(reg_status, value, (value & mask) == done, 0, 1000); + + spin_unlock_irqrestore(&data->lock, flags); + return ret; +} + +static int jh7100_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return jh7100_reset_update(rcdev, id, true); +} + +static int jh7100_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return jh7100_reset_update(rcdev, id, false); +} + +static int jh7100_reset_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + int ret; + + ret = jh7100_reset_assert(rcdev, id); + if (ret) + return ret; + + return jh7100_reset_deassert(rcdev, id); +} + +static int jh7100_reset_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct jh7100_reset *data = jh7100_reset_from(rcdev); + unsigned long offset = BIT_ULL_WORD(id); + u64 mask = BIT_ULL_MASK(id); + void __iomem *reg_status = data->base + JH7100_RESET_STATUS0 + offset * sizeof(u64); + u64 value = readq(reg_status); + + return !((value ^ jh7100_reset_asserted[offset]) & mask); +} + +static const struct reset_control_ops jh7100_reset_ops = { + .assert = jh7100_reset_assert, + .deassert = jh7100_reset_deassert, + .reset = jh7100_reset_reset, + .status = jh7100_reset_status, +}; + +int jh7100_reset_probe(struct platform_device *pdev) +{ + struct jh7100_reset *data; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(data->base)) + return PTR_ERR(data->base); + + data->rcdev.ops = &jh7100_reset_ops; + data->rcdev.owner = THIS_MODULE; + data->rcdev.nr_resets = JH7100_RSTN_END; + data->rcdev.dev = &pdev->dev; + data->rcdev.of_node = pdev->dev.of_node; + spin_lock_init(&data->lock); + + return devm_reset_controller_register(&pdev->dev, &data->rcdev); +} +EXPORT_SYMBOL_GPL(jh7100_reset_probe); diff --git a/drivers/reset/starfive/reset-starfive-jh71x0.h b/drivers/reset/starfive/reset-starfive-jh71x0.h new file mode 100644 index 0000000000000..318d7a0e096a0 --- /dev/null +++ b/drivers/reset/starfive/reset-starfive-jh71x0.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2021 Emil Renner Berthing + */ + +#ifndef __RESET_STARFIVE_JH71X0_H +#define __RESET_STARFIVE_JH71X0_H + +int jh7100_reset_probe(struct platform_device *pdev); + +#endif /* __RESET_STARFIVE_JH71X0_H */ From 60825d53ef14ca470244ae0aaa50b3f5ca384fb1 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sat, 9 Jul 2022 23:32:56 +0200 Subject: [PATCH 08/95] reset: starfive: Extract the common JH71X0 reset code Extract the common JH71X0 reset code for reusing them to support JH7110 SoC. Tested-by: Tommaso Merciai Reviewed-by: Conor Dooley Signed-off-by: Emil Renner Berthing Signed-off-by: Hal Feng --- .../reset/starfive/reset-starfive-jh7100.c | 49 ++++++++++++ .../reset/starfive/reset-starfive-jh71x0.c | 76 ++++++------------- .../reset/starfive/reset-starfive-jh71x0.h | 5 +- 3 files changed, 76 insertions(+), 54 deletions(-) diff --git a/drivers/reset/starfive/reset-starfive-jh7100.c b/drivers/reset/starfive/reset-starfive-jh7100.c index 5a68327c1f6ab..9d7cb4ed88699 100644 --- a/drivers/reset/starfive/reset-starfive-jh7100.c +++ b/drivers/reset/starfive/reset-starfive-jh7100.c @@ -10,6 +10,55 @@ #include "reset-starfive-jh71x0.h" +#include + +/* register offsets */ +#define JH7100_RESET_ASSERT0 0x00 +#define JH7100_RESET_ASSERT1 0x04 +#define JH7100_RESET_ASSERT2 0x08 +#define JH7100_RESET_ASSERT3 0x0c +#define JH7100_RESET_STATUS0 0x10 +#define JH7100_RESET_STATUS1 0x14 +#define JH7100_RESET_STATUS2 0x18 +#define JH7100_RESET_STATUS3 0x1c + +/* + * Writing a 1 to the n'th bit of the m'th ASSERT register asserts + * line 32m + n, and writing a 0 deasserts the same line. + * Most reset lines have their status inverted so a 0 bit in the STATUS + * register means the line is asserted and a 1 means it's deasserted. A few + * lines don't though, so store the expected value of the status registers when + * all lines are asserted. + */ +static const u64 jh7100_reset_asserted[2] = { + /* STATUS0 */ + BIT_ULL_MASK(JH7100_RST_U74) | + BIT_ULL_MASK(JH7100_RST_VP6_DRESET) | + BIT_ULL_MASK(JH7100_RST_VP6_BRESET) | + /* STATUS1 */ + BIT_ULL_MASK(JH7100_RST_HIFI4_DRESET) | + BIT_ULL_MASK(JH7100_RST_HIFI4_BRESET), + /* STATUS2 */ + BIT_ULL_MASK(JH7100_RST_E24) | + /* STATUS3 */ + 0, +}; + +static int __init jh7100_reset_probe(struct platform_device *pdev) +{ + void __iomem *base = devm_platform_ioremap_resource(pdev, 0); + + if (IS_ERR(base)) + return PTR_ERR(base); + + return reset_starfive_jh7100_register(&pdev->dev, pdev->dev.of_node, + base + JH7100_RESET_ASSERT0, + base + JH7100_RESET_STATUS0, + jh7100_reset_asserted, + JH7100_RSTN_END, + THIS_MODULE); +} + static const struct of_device_id jh7100_reset_dt_ids[] = { { .compatible = "starfive,jh7100-reset" }, { /* sentinel */ } diff --git a/drivers/reset/starfive/reset-starfive-jh71x0.c b/drivers/reset/starfive/reset-starfive-jh71x0.c index 114a13c4b8a61..3577444a89c62 100644 --- a/drivers/reset/starfive/reset-starfive-jh71x0.c +++ b/drivers/reset/starfive/reset-starfive-jh71x0.c @@ -10,51 +10,18 @@ #include #include #include -#include #include #include #include "reset-starfive-jh71x0.h" -#include - -/* register offsets */ -#define JH7100_RESET_ASSERT0 0x00 -#define JH7100_RESET_ASSERT1 0x04 -#define JH7100_RESET_ASSERT2 0x08 -#define JH7100_RESET_ASSERT3 0x0c -#define JH7100_RESET_STATUS0 0x10 -#define JH7100_RESET_STATUS1 0x14 -#define JH7100_RESET_STATUS2 0x18 -#define JH7100_RESET_STATUS3 0x1c - -/* - * Writing a 1 to the n'th bit of the m'th ASSERT register asserts - * line 32m + n, and writing a 0 deasserts the same line. - * Most reset lines have their status inverted so a 0 bit in the STATUS - * register means the line is asserted and a 1 means it's deasserted. A few - * lines don't though, so store the expected value of the status registers when - * all lines are asserted. - */ -static const u64 jh7100_reset_asserted[2] = { - /* STATUS0 */ - BIT_ULL_MASK(JH7100_RST_U74) | - BIT_ULL_MASK(JH7100_RST_VP6_DRESET) | - BIT_ULL_MASK(JH7100_RST_VP6_BRESET) | - /* STATUS1 */ - BIT_ULL_MASK(JH7100_RST_HIFI4_DRESET) | - BIT_ULL_MASK(JH7100_RST_HIFI4_BRESET), - /* STATUS2 */ - BIT_ULL_MASK(JH7100_RST_E24) | - /* STATUS3 */ - 0, -}; - struct jh7100_reset { struct reset_controller_dev rcdev; /* protect registers against concurrent read-modify-write */ spinlock_t lock; - void __iomem *base; + void __iomem *assert; + void __iomem *status; + const u64 *asserted; }; static inline struct jh7100_reset * @@ -69,9 +36,9 @@ static int jh7100_reset_update(struct reset_controller_dev *rcdev, struct jh7100_reset *data = jh7100_reset_from(rcdev); unsigned long offset = BIT_ULL_WORD(id); u64 mask = BIT_ULL_MASK(id); - void __iomem *reg_assert = data->base + JH7100_RESET_ASSERT0 + offset * sizeof(u64); - void __iomem *reg_status = data->base + JH7100_RESET_STATUS0 + offset * sizeof(u64); - u64 done = jh7100_reset_asserted[offset] & mask; + void __iomem *reg_assert = data->assert + offset * sizeof(u64); + void __iomem *reg_status = data->status + offset * sizeof(u64); + u64 done = data->asserted ? data->asserted[offset] & mask : 0; u64 value; unsigned long flags; int ret; @@ -125,10 +92,10 @@ static int jh7100_reset_status(struct reset_controller_dev *rcdev, struct jh7100_reset *data = jh7100_reset_from(rcdev); unsigned long offset = BIT_ULL_WORD(id); u64 mask = BIT_ULL_MASK(id); - void __iomem *reg_status = data->base + JH7100_RESET_STATUS0 + offset * sizeof(u64); + void __iomem *reg_status = data->status + offset * sizeof(u64); u64 value = readq(reg_status); - return !((value ^ jh7100_reset_asserted[offset]) & mask); + return !((value ^ data->asserted[offset]) & mask); } static const struct reset_control_ops jh7100_reset_ops = { @@ -138,25 +105,28 @@ static const struct reset_control_ops jh7100_reset_ops = { .status = jh7100_reset_status, }; -int jh7100_reset_probe(struct platform_device *pdev) +int reset_starfive_jh7100_register(struct device *dev, struct device_node *of_node, + void __iomem *assert, void __iomem *status, + const u64 *asserted, unsigned int nr_resets, + struct module *owner) { struct jh7100_reset *data; - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - data->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(data->base)) - return PTR_ERR(data->base); - data->rcdev.ops = &jh7100_reset_ops; - data->rcdev.owner = THIS_MODULE; - data->rcdev.nr_resets = JH7100_RSTN_END; - data->rcdev.dev = &pdev->dev; - data->rcdev.of_node = pdev->dev.of_node; + data->rcdev.owner = owner; + data->rcdev.nr_resets = nr_resets; + data->rcdev.dev = dev; + data->rcdev.of_node = of_node; + spin_lock_init(&data->lock); + data->assert = assert; + data->status = status; + data->asserted = asserted; - return devm_reset_controller_register(&pdev->dev, &data->rcdev); + return devm_reset_controller_register(dev, &data->rcdev); } -EXPORT_SYMBOL_GPL(jh7100_reset_probe); +EXPORT_SYMBOL_GPL(reset_starfive_jh7100_register); diff --git a/drivers/reset/starfive/reset-starfive-jh71x0.h b/drivers/reset/starfive/reset-starfive-jh71x0.h index 318d7a0e096a0..1fc5a648c8d83 100644 --- a/drivers/reset/starfive/reset-starfive-jh71x0.h +++ b/drivers/reset/starfive/reset-starfive-jh71x0.h @@ -6,6 +6,9 @@ #ifndef __RESET_STARFIVE_JH71X0_H #define __RESET_STARFIVE_JH71X0_H -int jh7100_reset_probe(struct platform_device *pdev); +int reset_starfive_jh7100_register(struct device *dev, struct device_node *of_node, + void __iomem *assert, void __iomem *status, + const u64 *asserted, unsigned int nr_resets, + struct module *owner); #endif /* __RESET_STARFIVE_JH71X0_H */ From 3f289475571295a59801967276ddebb8e132a3b4 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sat, 12 Nov 2022 16:25:53 +0800 Subject: [PATCH 09/95] reset: starfive: Rename "jh7100" to "jh71x0" for the common code For the common code will be shared with the StarFive JH7110 SoC. Tested-by: Tommaso Merciai Reviewed-by: Conor Dooley Signed-off-by: Emil Renner Berthing Signed-off-by: Hal Feng --- .../reset/starfive/reset-starfive-jh7100.c | 2 +- .../reset/starfive/reset-starfive-jh71x0.c | 50 +++++++++---------- .../reset/starfive/reset-starfive-jh71x0.h | 2 +- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/drivers/reset/starfive/reset-starfive-jh7100.c b/drivers/reset/starfive/reset-starfive-jh7100.c index 9d7cb4ed88699..5f06e5ae33464 100644 --- a/drivers/reset/starfive/reset-starfive-jh7100.c +++ b/drivers/reset/starfive/reset-starfive-jh7100.c @@ -51,7 +51,7 @@ static int __init jh7100_reset_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - return reset_starfive_jh7100_register(&pdev->dev, pdev->dev.of_node, + return reset_starfive_jh71x0_register(&pdev->dev, pdev->dev.of_node, base + JH7100_RESET_ASSERT0, base + JH7100_RESET_STATUS0, jh7100_reset_asserted, diff --git a/drivers/reset/starfive/reset-starfive-jh71x0.c b/drivers/reset/starfive/reset-starfive-jh71x0.c index 3577444a89c62..a689f4730ed77 100644 --- a/drivers/reset/starfive/reset-starfive-jh71x0.c +++ b/drivers/reset/starfive/reset-starfive-jh71x0.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Reset driver for the StarFive JH7100 SoC + * Reset driver for the StarFive JH71X0 SoCs * * Copyright (C) 2021 Emil Renner Berthing */ @@ -15,7 +15,7 @@ #include "reset-starfive-jh71x0.h" -struct jh7100_reset { +struct jh71x0_reset { struct reset_controller_dev rcdev; /* protect registers against concurrent read-modify-write */ spinlock_t lock; @@ -24,16 +24,16 @@ struct jh7100_reset { const u64 *asserted; }; -static inline struct jh7100_reset * -jh7100_reset_from(struct reset_controller_dev *rcdev) +static inline struct jh71x0_reset * +jh71x0_reset_from(struct reset_controller_dev *rcdev) { - return container_of(rcdev, struct jh7100_reset, rcdev); + return container_of(rcdev, struct jh71x0_reset, rcdev); } -static int jh7100_reset_update(struct reset_controller_dev *rcdev, +static int jh71x0_reset_update(struct reset_controller_dev *rcdev, unsigned long id, bool assert) { - struct jh7100_reset *data = jh7100_reset_from(rcdev); + struct jh71x0_reset *data = jh71x0_reset_from(rcdev); unsigned long offset = BIT_ULL_WORD(id); u64 mask = BIT_ULL_MASK(id); void __iomem *reg_assert = data->assert + offset * sizeof(u64); @@ -62,34 +62,34 @@ static int jh7100_reset_update(struct reset_controller_dev *rcdev, return ret; } -static int jh7100_reset_assert(struct reset_controller_dev *rcdev, +static int jh71x0_reset_assert(struct reset_controller_dev *rcdev, unsigned long id) { - return jh7100_reset_update(rcdev, id, true); + return jh71x0_reset_update(rcdev, id, true); } -static int jh7100_reset_deassert(struct reset_controller_dev *rcdev, +static int jh71x0_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id) { - return jh7100_reset_update(rcdev, id, false); + return jh71x0_reset_update(rcdev, id, false); } -static int jh7100_reset_reset(struct reset_controller_dev *rcdev, +static int jh71x0_reset_reset(struct reset_controller_dev *rcdev, unsigned long id) { int ret; - ret = jh7100_reset_assert(rcdev, id); + ret = jh71x0_reset_assert(rcdev, id); if (ret) return ret; - return jh7100_reset_deassert(rcdev, id); + return jh71x0_reset_deassert(rcdev, id); } -static int jh7100_reset_status(struct reset_controller_dev *rcdev, +static int jh71x0_reset_status(struct reset_controller_dev *rcdev, unsigned long id) { - struct jh7100_reset *data = jh7100_reset_from(rcdev); + struct jh71x0_reset *data = jh71x0_reset_from(rcdev); unsigned long offset = BIT_ULL_WORD(id); u64 mask = BIT_ULL_MASK(id); void __iomem *reg_status = data->status + offset * sizeof(u64); @@ -98,25 +98,25 @@ static int jh7100_reset_status(struct reset_controller_dev *rcdev, return !((value ^ data->asserted[offset]) & mask); } -static const struct reset_control_ops jh7100_reset_ops = { - .assert = jh7100_reset_assert, - .deassert = jh7100_reset_deassert, - .reset = jh7100_reset_reset, - .status = jh7100_reset_status, +static const struct reset_control_ops jh71x0_reset_ops = { + .assert = jh71x0_reset_assert, + .deassert = jh71x0_reset_deassert, + .reset = jh71x0_reset_reset, + .status = jh71x0_reset_status, }; -int reset_starfive_jh7100_register(struct device *dev, struct device_node *of_node, +int reset_starfive_jh71x0_register(struct device *dev, struct device_node *of_node, void __iomem *assert, void __iomem *status, const u64 *asserted, unsigned int nr_resets, struct module *owner) { - struct jh7100_reset *data; + struct jh71x0_reset *data; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - data->rcdev.ops = &jh7100_reset_ops; + data->rcdev.ops = &jh71x0_reset_ops; data->rcdev.owner = owner; data->rcdev.nr_resets = nr_resets; data->rcdev.dev = dev; @@ -129,4 +129,4 @@ int reset_starfive_jh7100_register(struct device *dev, struct device_node *of_no return devm_reset_controller_register(dev, &data->rcdev); } -EXPORT_SYMBOL_GPL(reset_starfive_jh7100_register); +EXPORT_SYMBOL_GPL(reset_starfive_jh71x0_register); diff --git a/drivers/reset/starfive/reset-starfive-jh71x0.h b/drivers/reset/starfive/reset-starfive-jh71x0.h index 1fc5a648c8d83..ac9e80dd3f594 100644 --- a/drivers/reset/starfive/reset-starfive-jh71x0.h +++ b/drivers/reset/starfive/reset-starfive-jh71x0.h @@ -6,7 +6,7 @@ #ifndef __RESET_STARFIVE_JH71X0_H #define __RESET_STARFIVE_JH71X0_H -int reset_starfive_jh7100_register(struct device *dev, struct device_node *of_node, +int reset_starfive_jh71x0_register(struct device *dev, struct device_node *of_node, void __iomem *assert, void __iomem *status, const u64 *asserted, unsigned int nr_resets, struct module *owner); From d7759ec13fbe02ef2621f5b9d3005a671ef600b1 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Wed, 24 Nov 2021 01:30:54 +0100 Subject: [PATCH 10/95] reset: starfive: jh71x0: Use 32bit I/O on 32bit registers We currently use 64bit I/O on the 32bit registers. This works because there are an even number of assert and status registers, so they're only ever accessed in pairs on 64bit boundaries. There are however other reset controllers for audio and video on the JH7100 SoC with only one status register that isn't 64bit aligned so 64bit I/O results in an unaligned access exception. Switch to 32bit I/O in preparation for supporting these resets too. Tested-by: Tommaso Merciai Reviewed-by: Conor Dooley Signed-off-by: Emil Renner Berthing Signed-off-by: Hal Feng --- .../reset/starfive/reset-starfive-jh7100.c | 14 ++++----- .../reset/starfive/reset-starfive-jh71x0.c | 31 +++++++++---------- .../reset/starfive/reset-starfive-jh71x0.h | 2 +- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/drivers/reset/starfive/reset-starfive-jh7100.c b/drivers/reset/starfive/reset-starfive-jh7100.c index 5f06e5ae33464..2a56f7fd4ba77 100644 --- a/drivers/reset/starfive/reset-starfive-jh7100.c +++ b/drivers/reset/starfive/reset-starfive-jh7100.c @@ -30,16 +30,16 @@ * lines don't though, so store the expected value of the status registers when * all lines are asserted. */ -static const u64 jh7100_reset_asserted[2] = { +static const u32 jh7100_reset_asserted[4] = { /* STATUS0 */ - BIT_ULL_MASK(JH7100_RST_U74) | - BIT_ULL_MASK(JH7100_RST_VP6_DRESET) | - BIT_ULL_MASK(JH7100_RST_VP6_BRESET) | + BIT(JH7100_RST_U74 % 32) | + BIT(JH7100_RST_VP6_DRESET % 32) | + BIT(JH7100_RST_VP6_BRESET % 32), /* STATUS1 */ - BIT_ULL_MASK(JH7100_RST_HIFI4_DRESET) | - BIT_ULL_MASK(JH7100_RST_HIFI4_BRESET), + BIT(JH7100_RST_HIFI4_DRESET % 32) | + BIT(JH7100_RST_HIFI4_BRESET % 32), /* STATUS2 */ - BIT_ULL_MASK(JH7100_RST_E24) | + BIT(JH7100_RST_E24 % 32), /* STATUS3 */ 0, }; diff --git a/drivers/reset/starfive/reset-starfive-jh71x0.c b/drivers/reset/starfive/reset-starfive-jh71x0.c index a689f4730ed77..55bbbd2de52cf 100644 --- a/drivers/reset/starfive/reset-starfive-jh71x0.c +++ b/drivers/reset/starfive/reset-starfive-jh71x0.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -21,7 +20,7 @@ struct jh71x0_reset { spinlock_t lock; void __iomem *assert; void __iomem *status; - const u64 *asserted; + const u32 *asserted; }; static inline struct jh71x0_reset * @@ -34,12 +33,12 @@ static int jh71x0_reset_update(struct reset_controller_dev *rcdev, unsigned long id, bool assert) { struct jh71x0_reset *data = jh71x0_reset_from(rcdev); - unsigned long offset = BIT_ULL_WORD(id); - u64 mask = BIT_ULL_MASK(id); - void __iomem *reg_assert = data->assert + offset * sizeof(u64); - void __iomem *reg_status = data->status + offset * sizeof(u64); - u64 done = data->asserted ? data->asserted[offset] & mask : 0; - u64 value; + unsigned long offset = id / 32; + u32 mask = BIT(id % 32); + void __iomem *reg_assert = data->assert + offset * sizeof(u32); + void __iomem *reg_status = data->status + offset * sizeof(u32); + u32 done = data->asserted ? data->asserted[offset] & mask : 0; + u32 value; unsigned long flags; int ret; @@ -48,15 +47,15 @@ static int jh71x0_reset_update(struct reset_controller_dev *rcdev, spin_lock_irqsave(&data->lock, flags); - value = readq(reg_assert); + value = readl(reg_assert); if (assert) value |= mask; else value &= ~mask; - writeq(value, reg_assert); + writel(value, reg_assert); /* if the associated clock is gated, deasserting might otherwise hang forever */ - ret = readq_poll_timeout_atomic(reg_status, value, (value & mask) == done, 0, 1000); + ret = readl_poll_timeout_atomic(reg_status, value, (value & mask) == done, 0, 1000); spin_unlock_irqrestore(&data->lock, flags); return ret; @@ -90,10 +89,10 @@ static int jh71x0_reset_status(struct reset_controller_dev *rcdev, unsigned long id) { struct jh71x0_reset *data = jh71x0_reset_from(rcdev); - unsigned long offset = BIT_ULL_WORD(id); - u64 mask = BIT_ULL_MASK(id); - void __iomem *reg_status = data->status + offset * sizeof(u64); - u64 value = readq(reg_status); + unsigned long offset = id / 32; + u32 mask = BIT(id % 32); + void __iomem *reg_status = data->status + offset * sizeof(u32); + u32 value = readl(reg_status); return !((value ^ data->asserted[offset]) & mask); } @@ -107,7 +106,7 @@ static const struct reset_control_ops jh71x0_reset_ops = { int reset_starfive_jh71x0_register(struct device *dev, struct device_node *of_node, void __iomem *assert, void __iomem *status, - const u64 *asserted, unsigned int nr_resets, + const u32 *asserted, unsigned int nr_resets, struct module *owner) { struct jh71x0_reset *data; diff --git a/drivers/reset/starfive/reset-starfive-jh71x0.h b/drivers/reset/starfive/reset-starfive-jh71x0.h index ac9e80dd3f594..db7d39a87f87d 100644 --- a/drivers/reset/starfive/reset-starfive-jh71x0.h +++ b/drivers/reset/starfive/reset-starfive-jh71x0.h @@ -8,7 +8,7 @@ int reset_starfive_jh71x0_register(struct device *dev, struct device_node *of_node, void __iomem *assert, void __iomem *status, - const u64 *asserted, unsigned int nr_resets, + const u32 *asserted, unsigned int nr_resets, struct module *owner); #endif /* __RESET_STARFIVE_JH71X0_H */ From 3ec262ac11b24f87596de7b671ae7be00118d33e Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Mon, 11 Jul 2022 20:59:24 +0200 Subject: [PATCH 11/95] dt-bindings: clock: Add StarFive JH7110 system clock and reset generator Add bindings for the system clock and reset generator (SYSCRG) on the JH7110 RISC-V SoC by StarFive Ltd. Reviewed-by: Conor Dooley Reviewed-by: Rob Herring Signed-off-by: Emil Renner Berthing Signed-off-by: Hal Feng --- .../clock/starfive,jh7110-syscrg.yaml | 104 +++++++++ MAINTAINERS | 8 +- .../dt-bindings/clock/starfive,jh7110-crg.h | 203 ++++++++++++++++++ .../dt-bindings/reset/starfive,jh7110-crg.h | 142 ++++++++++++ 4 files changed, 454 insertions(+), 3 deletions(-) create mode 100644 Documentation/devicetree/bindings/clock/starfive,jh7110-syscrg.yaml create mode 100644 include/dt-bindings/clock/starfive,jh7110-crg.h create mode 100644 include/dt-bindings/reset/starfive,jh7110-crg.h diff --git a/Documentation/devicetree/bindings/clock/starfive,jh7110-syscrg.yaml b/Documentation/devicetree/bindings/clock/starfive,jh7110-syscrg.yaml new file mode 100644 index 0000000000000..84373ae31644e --- /dev/null +++ b/Documentation/devicetree/bindings/clock/starfive,jh7110-syscrg.yaml @@ -0,0 +1,104 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/starfive,jh7110-syscrg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 System Clock and Reset Generator + +maintainers: + - Emil Renner Berthing + +properties: + compatible: + const: starfive,jh7110-syscrg + + reg: + maxItems: 1 + + clocks: + oneOf: + - items: + - description: Main Oscillator (24 MHz) + - description: GMAC1 RMII reference or GMAC1 RGMII RX + - description: External I2S TX bit clock + - description: External I2S TX left/right channel clock + - description: External I2S RX bit clock + - description: External I2S RX left/right channel clock + - description: External TDM clock + - description: External audio master clock + + - items: + - description: Main Oscillator (24 MHz) + - description: GMAC1 RMII reference + - description: GMAC1 RGMII RX + - description: External I2S TX bit clock + - description: External I2S TX left/right channel clock + - description: External I2S RX bit clock + - description: External I2S RX left/right channel clock + - description: External TDM clock + - description: External audio master clock + + clock-names: + oneOf: + - items: + - const: osc + - enum: + - gmac1_rmii_refin + - gmac1_rgmii_rxin + - const: i2stx_bclk_ext + - const: i2stx_lrck_ext + - const: i2srx_bclk_ext + - const: i2srx_lrck_ext + - const: tdm_ext + - const: mclk_ext + + - items: + - const: osc + - const: gmac1_rmii_refin + - const: gmac1_rgmii_rxin + - const: i2stx_bclk_ext + - const: i2stx_lrck_ext + - const: i2srx_bclk_ext + - const: i2srx_lrck_ext + - const: tdm_ext + - const: mclk_ext + + '#clock-cells': + const: 1 + description: + See for valid indices. + + '#reset-cells': + const: 1 + description: + See for valid indices. + +required: + - compatible + - reg + - clocks + - clock-names + - '#clock-cells' + - '#reset-cells' + +additionalProperties: false + +examples: + - | + clock-controller@13020000 { + compatible = "starfive,jh7110-syscrg"; + reg = <0x13020000 0x10000>; + clocks = <&osc>, <&gmac1_rmii_refin>, + <&gmac1_rgmii_rxin>, + <&i2stx_bclk_ext>, <&i2stx_lrck_ext>, + <&i2srx_bclk_ext>, <&i2srx_lrck_ext>, + <&tdm_ext>, <&mclk_ext>; + clock-names = "osc", "gmac1_rmii_refin", + "gmac1_rgmii_rxin", + "i2stx_bclk_ext", "i2stx_lrck_ext", + "i2srx_bclk_ext", "i2srx_lrck_ext", + "tdm_ext", "mclk_ext"; + #clock-cells = <1>; + #reset-cells = <1>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 4c788fa15cc47..9b66eaad0547f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19913,10 +19913,11 @@ F: drivers/mmc/host/dw_mmc-starfive.c STARFIVE JH71X0 CLOCK DRIVERS M: Emil Renner Berthing +M: Hal Feng S: Maintained -F: Documentation/devicetree/bindings/clock/starfive,jh7100-*.yaml +F: Documentation/devicetree/bindings/clock/starfive,jh71*.yaml F: drivers/clk/starfive/clk-starfive-jh71* -F: include/dt-bindings/clock/starfive-jh7100*.h +F: include/dt-bindings/clock/starfive?jh71*.h STARFIVE JH71X0 PINCTRL DRIVERS M: Emil Renner Berthing @@ -19930,10 +19931,11 @@ F: include/dt-bindings/pinctrl/starfive,jh7110-pinctrl.h STARFIVE JH71X0 RESET CONTROLLER DRIVERS M: Emil Renner Berthing +M: Hal Feng S: Maintained F: Documentation/devicetree/bindings/reset/starfive,jh7100-reset.yaml F: drivers/reset/starfive/reset-starfive-jh71* -F: include/dt-bindings/reset/starfive-jh7100.h +F: include/dt-bindings/reset/starfive?jh71*.h STARFIVE JH71XX PMU CONTROLLER DRIVER M: Walker Chen diff --git a/include/dt-bindings/clock/starfive,jh7110-crg.h b/include/dt-bindings/clock/starfive,jh7110-crg.h new file mode 100644 index 0000000000000..fdd1852e34ccf --- /dev/null +++ b/include/dt-bindings/clock/starfive,jh7110-crg.h @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright 2022 Emil Renner Berthing + */ + +#ifndef __DT_BINDINGS_CLOCK_STARFIVE_JH7110_CRG_H__ +#define __DT_BINDINGS_CLOCK_STARFIVE_JH7110_CRG_H__ + +/* SYSCRG clocks */ +#define JH7110_SYSCLK_CPU_ROOT 0 +#define JH7110_SYSCLK_CPU_CORE 1 +#define JH7110_SYSCLK_CPU_BUS 2 +#define JH7110_SYSCLK_GPU_ROOT 3 +#define JH7110_SYSCLK_PERH_ROOT 4 +#define JH7110_SYSCLK_BUS_ROOT 5 +#define JH7110_SYSCLK_NOCSTG_BUS 6 +#define JH7110_SYSCLK_AXI_CFG0 7 +#define JH7110_SYSCLK_STG_AXIAHB 8 +#define JH7110_SYSCLK_AHB0 9 +#define JH7110_SYSCLK_AHB1 10 +#define JH7110_SYSCLK_APB_BUS 11 +#define JH7110_SYSCLK_APB0 12 +#define JH7110_SYSCLK_PLL0_DIV2 13 +#define JH7110_SYSCLK_PLL1_DIV2 14 +#define JH7110_SYSCLK_PLL2_DIV2 15 +#define JH7110_SYSCLK_AUDIO_ROOT 16 +#define JH7110_SYSCLK_MCLK_INNER 17 +#define JH7110_SYSCLK_MCLK 18 +#define JH7110_SYSCLK_MCLK_OUT 19 +#define JH7110_SYSCLK_ISP_2X 20 +#define JH7110_SYSCLK_ISP_AXI 21 +#define JH7110_SYSCLK_GCLK0 22 +#define JH7110_SYSCLK_GCLK1 23 +#define JH7110_SYSCLK_GCLK2 24 +#define JH7110_SYSCLK_CORE 25 +#define JH7110_SYSCLK_CORE1 26 +#define JH7110_SYSCLK_CORE2 27 +#define JH7110_SYSCLK_CORE3 28 +#define JH7110_SYSCLK_CORE4 29 +#define JH7110_SYSCLK_DEBUG 30 +#define JH7110_SYSCLK_RTC_TOGGLE 31 +#define JH7110_SYSCLK_TRACE0 32 +#define JH7110_SYSCLK_TRACE1 33 +#define JH7110_SYSCLK_TRACE2 34 +#define JH7110_SYSCLK_TRACE3 35 +#define JH7110_SYSCLK_TRACE4 36 +#define JH7110_SYSCLK_TRACE_COM 37 +#define JH7110_SYSCLK_NOC_BUS_CPU_AXI 38 +#define JH7110_SYSCLK_NOC_BUS_AXICFG0_AXI 39 +#define JH7110_SYSCLK_OSC_DIV2 40 +#define JH7110_SYSCLK_PLL1_DIV4 41 +#define JH7110_SYSCLK_PLL1_DIV8 42 +#define JH7110_SYSCLK_DDR_BUS 43 +#define JH7110_SYSCLK_DDR_AXI 44 +#define JH7110_SYSCLK_GPU_CORE 45 +#define JH7110_SYSCLK_GPU_CORE_CLK 46 +#define JH7110_SYSCLK_GPU_SYS_CLK 47 +#define JH7110_SYSCLK_GPU_APB 48 +#define JH7110_SYSCLK_GPU_RTC_TOGGLE 49 +#define JH7110_SYSCLK_NOC_BUS_GPU_AXI 50 +#define JH7110_SYSCLK_ISP_TOP_CORE 51 +#define JH7110_SYSCLK_ISP_TOP_AXI 52 +#define JH7110_SYSCLK_NOC_BUS_ISP_AXI 53 +#define JH7110_SYSCLK_HIFI4_CORE 54 +#define JH7110_SYSCLK_HIFI4_AXI 55 +#define JH7110_SYSCLK_AXI_CFG1_MAIN 56 +#define JH7110_SYSCLK_AXI_CFG1_AHB 57 +#define JH7110_SYSCLK_VOUT_SRC 58 +#define JH7110_SYSCLK_VOUT_AXI 59 +#define JH7110_SYSCLK_NOC_BUS_DISP_AXI 60 +#define JH7110_SYSCLK_VOUT_TOP_AHB 61 +#define JH7110_SYSCLK_VOUT_TOP_AXI 62 +#define JH7110_SYSCLK_VOUT_TOP_HDMITX0_MCLK 63 +#define JH7110_SYSCLK_VOUT_TOP_MIPIPHY_REF 64 +#define JH7110_SYSCLK_JPEGC_AXI 65 +#define JH7110_SYSCLK_CODAJ12_AXI 66 +#define JH7110_SYSCLK_CODAJ12_CORE 67 +#define JH7110_SYSCLK_CODAJ12_APB 68 +#define JH7110_SYSCLK_VDEC_AXI 69 +#define JH7110_SYSCLK_WAVE511_AXI 70 +#define JH7110_SYSCLK_WAVE511_BPU 71 +#define JH7110_SYSCLK_WAVE511_VCE 72 +#define JH7110_SYSCLK_WAVE511_APB 73 +#define JH7110_SYSCLK_VDEC_JPG 74 +#define JH7110_SYSCLK_VDEC_MAIN 75 +#define JH7110_SYSCLK_NOC_BUS_VDEC_AXI 76 +#define JH7110_SYSCLK_VENC_AXI 77 +#define JH7110_SYSCLK_WAVE420L_AXI 78 +#define JH7110_SYSCLK_WAVE420L_BPU 79 +#define JH7110_SYSCLK_WAVE420L_VCE 80 +#define JH7110_SYSCLK_WAVE420L_APB 81 +#define JH7110_SYSCLK_NOC_BUS_VENC_AXI 82 +#define JH7110_SYSCLK_AXI_CFG0_MAIN_DIV 83 +#define JH7110_SYSCLK_AXI_CFG0_MAIN 84 +#define JH7110_SYSCLK_AXI_CFG0_HIFI4 85 +#define JH7110_SYSCLK_AXIMEM2_AXI 86 +#define JH7110_SYSCLK_QSPI_AHB 87 +#define JH7110_SYSCLK_QSPI_APB 88 +#define JH7110_SYSCLK_QSPI_REF_SRC 89 +#define JH7110_SYSCLK_QSPI_REF 90 +#define JH7110_SYSCLK_SDIO0_AHB 91 +#define JH7110_SYSCLK_SDIO1_AHB 92 +#define JH7110_SYSCLK_SDIO0_SDCARD 93 +#define JH7110_SYSCLK_SDIO1_SDCARD 94 +#define JH7110_SYSCLK_USB_125M 95 +#define JH7110_SYSCLK_NOC_BUS_STG_AXI 96 +#define JH7110_SYSCLK_GMAC1_AHB 97 +#define JH7110_SYSCLK_GMAC1_AXI 98 +#define JH7110_SYSCLK_GMAC_SRC 99 +#define JH7110_SYSCLK_GMAC1_GTXCLK 100 +#define JH7110_SYSCLK_GMAC1_RMII_RTX 101 +#define JH7110_SYSCLK_GMAC1_PTP 102 +#define JH7110_SYSCLK_GMAC1_RX 103 +#define JH7110_SYSCLK_GMAC1_RX_INV 104 +#define JH7110_SYSCLK_GMAC1_TX 105 +#define JH7110_SYSCLK_GMAC1_TX_INV 106 +#define JH7110_SYSCLK_GMAC1_GTXC 107 +#define JH7110_SYSCLK_GMAC0_GTXCLK 108 +#define JH7110_SYSCLK_GMAC0_PTP 109 +#define JH7110_SYSCLK_GMAC_PHY 110 +#define JH7110_SYSCLK_GMAC0_GTXC 111 +#define JH7110_SYSCLK_IOMUX_APB 112 +#define JH7110_SYSCLK_MAILBOX_APB 113 +#define JH7110_SYSCLK_INT_CTRL_APB 114 +#define JH7110_SYSCLK_CAN0_APB 115 +#define JH7110_SYSCLK_CAN0_TIMER 116 +#define JH7110_SYSCLK_CAN0_CAN 117 +#define JH7110_SYSCLK_CAN1_APB 118 +#define JH7110_SYSCLK_CAN1_TIMER 119 +#define JH7110_SYSCLK_CAN1_CAN 120 +#define JH7110_SYSCLK_PWM_APB 121 +#define JH7110_SYSCLK_WDT_APB 122 +#define JH7110_SYSCLK_WDT_CORE 123 +#define JH7110_SYSCLK_TIMER_APB 124 +#define JH7110_SYSCLK_TIMER0 125 +#define JH7110_SYSCLK_TIMER1 126 +#define JH7110_SYSCLK_TIMER2 127 +#define JH7110_SYSCLK_TIMER3 128 +#define JH7110_SYSCLK_TEMP_APB 129 +#define JH7110_SYSCLK_TEMP_CORE 130 +#define JH7110_SYSCLK_SPI0_APB 131 +#define JH7110_SYSCLK_SPI1_APB 132 +#define JH7110_SYSCLK_SPI2_APB 133 +#define JH7110_SYSCLK_SPI3_APB 134 +#define JH7110_SYSCLK_SPI4_APB 135 +#define JH7110_SYSCLK_SPI5_APB 136 +#define JH7110_SYSCLK_SPI6_APB 137 +#define JH7110_SYSCLK_I2C0_APB 138 +#define JH7110_SYSCLK_I2C1_APB 139 +#define JH7110_SYSCLK_I2C2_APB 140 +#define JH7110_SYSCLK_I2C3_APB 141 +#define JH7110_SYSCLK_I2C4_APB 142 +#define JH7110_SYSCLK_I2C5_APB 143 +#define JH7110_SYSCLK_I2C6_APB 144 +#define JH7110_SYSCLK_UART0_APB 145 +#define JH7110_SYSCLK_UART0_CORE 146 +#define JH7110_SYSCLK_UART1_APB 147 +#define JH7110_SYSCLK_UART1_CORE 148 +#define JH7110_SYSCLK_UART2_APB 149 +#define JH7110_SYSCLK_UART2_CORE 150 +#define JH7110_SYSCLK_UART3_APB 151 +#define JH7110_SYSCLK_UART3_CORE 152 +#define JH7110_SYSCLK_UART4_APB 153 +#define JH7110_SYSCLK_UART4_CORE 154 +#define JH7110_SYSCLK_UART5_APB 155 +#define JH7110_SYSCLK_UART5_CORE 156 +#define JH7110_SYSCLK_PWMDAC_APB 157 +#define JH7110_SYSCLK_PWMDAC_CORE 158 +#define JH7110_SYSCLK_SPDIF_APB 159 +#define JH7110_SYSCLK_SPDIF_CORE 160 +#define JH7110_SYSCLK_I2STX0_APB 161 +#define JH7110_SYSCLK_I2STX0_BCLK_MST 162 +#define JH7110_SYSCLK_I2STX0_BCLK_MST_INV 163 +#define JH7110_SYSCLK_I2STX0_LRCK_MST 164 +#define JH7110_SYSCLK_I2STX0_BCLK 165 +#define JH7110_SYSCLK_I2STX0_BCLK_INV 166 +#define JH7110_SYSCLK_I2STX0_LRCK 167 +#define JH7110_SYSCLK_I2STX1_APB 168 +#define JH7110_SYSCLK_I2STX1_BCLK_MST 169 +#define JH7110_SYSCLK_I2STX1_BCLK_MST_INV 170 +#define JH7110_SYSCLK_I2STX1_LRCK_MST 171 +#define JH7110_SYSCLK_I2STX1_BCLK 172 +#define JH7110_SYSCLK_I2STX1_BCLK_INV 173 +#define JH7110_SYSCLK_I2STX1_LRCK 174 +#define JH7110_SYSCLK_I2SRX_APB 175 +#define JH7110_SYSCLK_I2SRX_BCLK_MST 176 +#define JH7110_SYSCLK_I2SRX_BCLK_MST_INV 177 +#define JH7110_SYSCLK_I2SRX_LRCK_MST 178 +#define JH7110_SYSCLK_I2SRX_BCLK 179 +#define JH7110_SYSCLK_I2SRX_BCLK_INV 180 +#define JH7110_SYSCLK_I2SRX_LRCK 181 +#define JH7110_SYSCLK_PDM_DMIC 182 +#define JH7110_SYSCLK_PDM_APB 183 +#define JH7110_SYSCLK_TDM_AHB 184 +#define JH7110_SYSCLK_TDM_APB 185 +#define JH7110_SYSCLK_TDM_INTERNAL 186 +#define JH7110_SYSCLK_TDM_TDM 187 +#define JH7110_SYSCLK_TDM_TDM_INV 188 +#define JH7110_SYSCLK_JTAG_CERTIFICATION_TRNG 189 + +#define JH7110_SYSCLK_END 190 + +#endif /* __DT_BINDINGS_CLOCK_STARFIVE_JH7110_CRG_H__ */ diff --git a/include/dt-bindings/reset/starfive,jh7110-crg.h b/include/dt-bindings/reset/starfive,jh7110-crg.h new file mode 100644 index 0000000000000..b88216a4fe40a --- /dev/null +++ b/include/dt-bindings/reset/starfive,jh7110-crg.h @@ -0,0 +1,142 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright (C) 2022 Emil Renner Berthing + */ + +#ifndef __DT_BINDINGS_RESET_STARFIVE_JH7110_CRG_H__ +#define __DT_BINDINGS_RESET_STARFIVE_JH7110_CRG_H__ + +/* SYSCRG resets */ +#define JH7110_SYSRST_JTAG_APB 0 +#define JH7110_SYSRST_SYSCON_APB 1 +#define JH7110_SYSRST_IOMUX_APB 2 +#define JH7110_SYSRST_BUS 3 +#define JH7110_SYSRST_DEBUG 4 +#define JH7110_SYSRST_CORE0 5 +#define JH7110_SYSRST_CORE1 6 +#define JH7110_SYSRST_CORE2 7 +#define JH7110_SYSRST_CORE3 8 +#define JH7110_SYSRST_CORE4 9 +#define JH7110_SYSRST_CORE0_ST 10 +#define JH7110_SYSRST_CORE1_ST 11 +#define JH7110_SYSRST_CORE2_ST 12 +#define JH7110_SYSRST_CORE3_ST 13 +#define JH7110_SYSRST_CORE4_ST 14 +#define JH7110_SYSRST_TRACE0 15 +#define JH7110_SYSRST_TRACE1 16 +#define JH7110_SYSRST_TRACE2 17 +#define JH7110_SYSRST_TRACE3 18 +#define JH7110_SYSRST_TRACE4 19 +#define JH7110_SYSRST_TRACE_COM 20 +#define JH7110_SYSRST_GPU_APB 21 +#define JH7110_SYSRST_GPU_DOMA 22 +#define JH7110_SYSRST_NOC_BUS_APB 23 +#define JH7110_SYSRST_NOC_BUS_AXICFG0_AXI 24 +#define JH7110_SYSRST_NOC_BUS_CPU_AXI 25 +#define JH7110_SYSRST_NOC_BUS_DISP_AXI 26 +#define JH7110_SYSRST_NOC_BUS_GPU_AXI 27 +#define JH7110_SYSRST_NOC_BUS_ISP_AXI 28 +#define JH7110_SYSRST_NOC_BUS_DDRC 29 +#define JH7110_SYSRST_NOC_BUS_STG_AXI 30 +#define JH7110_SYSRST_NOC_BUS_VDEC_AXI 31 + +#define JH7110_SYSRST_NOC_BUS_VENC_AXI 32 +#define JH7110_SYSRST_AXI_CFG1_AHB 33 +#define JH7110_SYSRST_AXI_CFG1_MAIN 34 +#define JH7110_SYSRST_AXI_CFG0_MAIN 35 +#define JH7110_SYSRST_AXI_CFG0_MAIN_DIV 36 +#define JH7110_SYSRST_AXI_CFG0_HIFI4 37 +#define JH7110_SYSRST_DDR_AXI 38 +#define JH7110_SYSRST_DDR_OSC 39 +#define JH7110_SYSRST_DDR_APB 40 +#define JH7110_SYSRST_ISP_TOP 41 +#define JH7110_SYSRST_ISP_TOP_AXI 42 +#define JH7110_SYSRST_VOUT_TOP_SRC 43 +#define JH7110_SYSRST_CODAJ12_AXI 44 +#define JH7110_SYSRST_CODAJ12_CORE 45 +#define JH7110_SYSRST_CODAJ12_APB 46 +#define JH7110_SYSRST_WAVE511_AXI 47 +#define JH7110_SYSRST_WAVE511_BPU 48 +#define JH7110_SYSRST_WAVE511_VCE 49 +#define JH7110_SYSRST_WAVE511_APB 50 +#define JH7110_SYSRST_VDEC_JPG 51 +#define JH7110_SYSRST_VDEC_MAIN 52 +#define JH7110_SYSRST_AXIMEM0_AXI 53 +#define JH7110_SYSRST_WAVE420L_AXI 54 +#define JH7110_SYSRST_WAVE420L_BPU 55 +#define JH7110_SYSRST_WAVE420L_VCE 56 +#define JH7110_SYSRST_WAVE420L_APB 57 +#define JH7110_SYSRST_AXIMEM1_AXI 58 +#define JH7110_SYSRST_AXIMEM2_AXI 59 +#define JH7110_SYSRST_INTMEM 60 +#define JH7110_SYSRST_QSPI_AHB 61 +#define JH7110_SYSRST_QSPI_APB 62 +#define JH7110_SYSRST_QSPI_REF 63 + +#define JH7110_SYSRST_SDIO0_AHB 64 +#define JH7110_SYSRST_SDIO1_AHB 65 +#define JH7110_SYSRST_GMAC1_AXI 66 +#define JH7110_SYSRST_GMAC1_AHB 67 +#define JH7110_SYSRST_MAILBOX_APB 68 +#define JH7110_SYSRST_SPI0_APB 69 +#define JH7110_SYSRST_SPI1_APB 70 +#define JH7110_SYSRST_SPI2_APB 71 +#define JH7110_SYSRST_SPI3_APB 72 +#define JH7110_SYSRST_SPI4_APB 73 +#define JH7110_SYSRST_SPI5_APB 74 +#define JH7110_SYSRST_SPI6_APB 75 +#define JH7110_SYSRST_I2C0_APB 76 +#define JH7110_SYSRST_I2C1_APB 77 +#define JH7110_SYSRST_I2C2_APB 78 +#define JH7110_SYSRST_I2C3_APB 79 +#define JH7110_SYSRST_I2C4_APB 80 +#define JH7110_SYSRST_I2C5_APB 81 +#define JH7110_SYSRST_I2C6_APB 82 +#define JH7110_SYSRST_UART0_APB 83 +#define JH7110_SYSRST_UART0_CORE 84 +#define JH7110_SYSRST_UART1_APB 85 +#define JH7110_SYSRST_UART1_CORE 86 +#define JH7110_SYSRST_UART2_APB 87 +#define JH7110_SYSRST_UART2_CORE 88 +#define JH7110_SYSRST_UART3_APB 89 +#define JH7110_SYSRST_UART3_CORE 90 +#define JH7110_SYSRST_UART4_APB 91 +#define JH7110_SYSRST_UART4_CORE 92 +#define JH7110_SYSRST_UART5_APB 93 +#define JH7110_SYSRST_UART5_CORE 94 +#define JH7110_SYSRST_SPDIF_APB 95 + +#define JH7110_SYSRST_PWMDAC_APB 96 +#define JH7110_SYSRST_PDM_DMIC 97 +#define JH7110_SYSRST_PDM_APB 98 +#define JH7110_SYSRST_I2SRX_APB 99 +#define JH7110_SYSRST_I2SRX_BCLK 100 +#define JH7110_SYSRST_I2STX0_APB 101 +#define JH7110_SYSRST_I2STX0_BCLK 102 +#define JH7110_SYSRST_I2STX1_APB 103 +#define JH7110_SYSRST_I2STX1_BCLK 104 +#define JH7110_SYSRST_TDM_AHB 105 +#define JH7110_SYSRST_TDM_CORE 106 +#define JH7110_SYSRST_TDM_APB 107 +#define JH7110_SYSRST_PWM_APB 108 +#define JH7110_SYSRST_WDT_APB 109 +#define JH7110_SYSRST_WDT_CORE 110 +#define JH7110_SYSRST_CAN0_APB 111 +#define JH7110_SYSRST_CAN0_CORE 112 +#define JH7110_SYSRST_CAN0_TIMER 113 +#define JH7110_SYSRST_CAN1_APB 114 +#define JH7110_SYSRST_CAN1_CORE 115 +#define JH7110_SYSRST_CAN1_TIMER 116 +#define JH7110_SYSRST_TIMER_APB 117 +#define JH7110_SYSRST_TIMER0 118 +#define JH7110_SYSRST_TIMER1 119 +#define JH7110_SYSRST_TIMER2 120 +#define JH7110_SYSRST_TIMER3 121 +#define JH7110_SYSRST_INT_CTRL_APB 122 +#define JH7110_SYSRST_TEMP_APB 123 +#define JH7110_SYSRST_TEMP_CORE 124 +#define JH7110_SYSRST_JTAG_CERTIFICATION 125 + +#define JH7110_SYSRST_END 126 + +#endif /* __DT_BINDINGS_RESET_STARFIVE_JH7110_CRG_H__ */ From 02592e143e25abd9249f1af043329cbf3cff2fd4 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sun, 24 Jul 2022 21:03:29 +0200 Subject: [PATCH 12/95] dt-bindings: clock: Add StarFive JH7110 always-on clock and reset generator Add bindings for the always-on clock and reset generator (AONCRG) on the JH7110 RISC-V SoC by StarFive Ltd. Reviewed-by: Conor Dooley Reviewed-by: Rob Herring Signed-off-by: Emil Renner Berthing Signed-off-by: Hal Feng --- .../clock/starfive,jh7110-aoncrg.yaml | 107 ++++++++++++++++++ .../dt-bindings/clock/starfive,jh7110-crg.h | 18 +++ .../dt-bindings/reset/starfive,jh7110-crg.h | 12 ++ 3 files changed, 137 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/starfive,jh7110-aoncrg.yaml diff --git a/Documentation/devicetree/bindings/clock/starfive,jh7110-aoncrg.yaml b/Documentation/devicetree/bindings/clock/starfive,jh7110-aoncrg.yaml new file mode 100644 index 0000000000000..923680a44aef3 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/starfive,jh7110-aoncrg.yaml @@ -0,0 +1,107 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/starfive,jh7110-aoncrg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 Always-On Clock and Reset Generator + +maintainers: + - Emil Renner Berthing + +properties: + compatible: + const: starfive,jh7110-aoncrg + + reg: + maxItems: 1 + + clocks: + oneOf: + - items: + - description: Main Oscillator (24 MHz) + - description: GMAC0 RMII reference or GMAC0 RGMII RX + - description: STG AXI/AHB + - description: APB Bus + - description: GMAC0 GTX + + - items: + - description: Main Oscillator (24 MHz) + - description: GMAC0 RMII reference or GMAC0 RGMII RX + - description: STG AXI/AHB or GMAC0 RGMII RX + - description: APB Bus or STG AXI/AHB + - description: GMAC0 GTX or APB Bus + - description: RTC Oscillator (32.768 kHz) or GMAC0 GTX + + - items: + - description: Main Oscillator (24 MHz) + - description: GMAC0 RMII reference + - description: GMAC0 RGMII RX + - description: STG AXI/AHB + - description: APB Bus + - description: GMAC0 GTX + - description: RTC Oscillator (32.768 kHz) + + clock-names: + oneOf: + - minItems: 5 + items: + - const: osc + - enum: + - gmac0_rmii_refin + - gmac0_rgmii_rxin + - const: stg_axiahb + - const: apb_bus + - const: gmac0_gtxclk + - const: rtc_osc + + - minItems: 6 + items: + - const: osc + - const: gmac0_rmii_refin + - const: gmac0_rgmii_rxin + - const: stg_axiahb + - const: apb_bus + - const: gmac0_gtxclk + - const: rtc_osc + + '#clock-cells': + const: 1 + description: + See for valid indices. + + '#reset-cells': + const: 1 + description: + See for valid indices. + +required: + - compatible + - reg + - clocks + - clock-names + - '#clock-cells' + - '#reset-cells' + +additionalProperties: false + +examples: + - | + #include + + clock-controller@17000000 { + compatible = "starfive,jh7110-aoncrg"; + reg = <0x17000000 0x10000>; + clocks = <&osc>, <&gmac0_rmii_refin>, + <&gmac0_rgmii_rxin>, + <&syscrg JH7110_SYSCLK_STG_AXIAHB>, + <&syscrg JH7110_SYSCLK_APB_BUS>, + <&syscrg JH7110_SYSCLK_GMAC0_GTXCLK>, + <&rtc_osc>; + clock-names = "osc", "gmac0_rmii_refin", + "gmac0_rgmii_rxin", "stg_axiahb", + "apb_bus", "gmac0_gtxclk", + "rtc_osc"; + #clock-cells = <1>; + #reset-cells = <1>; + }; diff --git a/include/dt-bindings/clock/starfive,jh7110-crg.h b/include/dt-bindings/clock/starfive,jh7110-crg.h index fdd1852e34ccf..06257bfd9ac1e 100644 --- a/include/dt-bindings/clock/starfive,jh7110-crg.h +++ b/include/dt-bindings/clock/starfive,jh7110-crg.h @@ -200,4 +200,22 @@ #define JH7110_SYSCLK_END 190 +/* AONCRG clocks */ +#define JH7110_AONCLK_OSC_DIV4 0 +#define JH7110_AONCLK_APB_FUNC 1 +#define JH7110_AONCLK_GMAC0_AHB 2 +#define JH7110_AONCLK_GMAC0_AXI 3 +#define JH7110_AONCLK_GMAC0_RMII_RTX 4 +#define JH7110_AONCLK_GMAC0_TX 5 +#define JH7110_AONCLK_GMAC0_TX_INV 6 +#define JH7110_AONCLK_GMAC0_RX 7 +#define JH7110_AONCLK_GMAC0_RX_INV 8 +#define JH7110_AONCLK_OTPC_APB 9 +#define JH7110_AONCLK_RTC_APB 10 +#define JH7110_AONCLK_RTC_INTERNAL 11 +#define JH7110_AONCLK_RTC_32K 12 +#define JH7110_AONCLK_RTC_CAL 13 + +#define JH7110_AONCLK_END 14 + #endif /* __DT_BINDINGS_CLOCK_STARFIVE_JH7110_CRG_H__ */ diff --git a/include/dt-bindings/reset/starfive,jh7110-crg.h b/include/dt-bindings/reset/starfive,jh7110-crg.h index b88216a4fe40a..d78e38690ceb3 100644 --- a/include/dt-bindings/reset/starfive,jh7110-crg.h +++ b/include/dt-bindings/reset/starfive,jh7110-crg.h @@ -139,4 +139,16 @@ #define JH7110_SYSRST_END 126 +/* AONCRG resets */ +#define JH7110_AONRST_GMAC0_AXI 0 +#define JH7110_AONRST_GMAC0_AHB 1 +#define JH7110_AONRST_IOMUX 2 +#define JH7110_AONRST_PMU_APB 3 +#define JH7110_AONRST_PMU_WKUP 4 +#define JH7110_AONRST_RTC_APB 5 +#define JH7110_AONRST_RTC_CAL 6 +#define JH7110_AONRST_RTC_32K 7 + +#define JH7110_AONRST_END 8 + #endif /* __DT_BINDINGS_RESET_STARFIVE_JH7110_CRG_H__ */ From 5e91e891da0009ac00ff1668c6f97fcf2b5fb4b5 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sat, 9 Jul 2022 23:57:57 +0200 Subject: [PATCH 13/95] clk: starfive: Add StarFive JH7110 system clock driver Add driver for the StarFive JH7110 system clock controller and register an auxiliary device for system reset controller which is named as "reset-sys". Tested-by: Tommaso Merciai Signed-off-by: Emil Renner Berthing Co-developed-by: Hal Feng Signed-off-by: Hal Feng --- drivers/clk/starfive/Kconfig | 11 + drivers/clk/starfive/Makefile | 2 + .../clk/starfive/clk-starfive-jh7110-sys.c | 450 ++++++++++++++++++ drivers/clk/starfive/clk-starfive-jh71x0.c | 50 ++ drivers/clk/starfive/clk-starfive-jh71x0.h | 8 + 5 files changed, 521 insertions(+) create mode 100644 drivers/clk/starfive/clk-starfive-jh7110-sys.c diff --git a/drivers/clk/starfive/Kconfig b/drivers/clk/starfive/Kconfig index 3ceeb19b5edad..670c5084aeb82 100644 --- a/drivers/clk/starfive/Kconfig +++ b/drivers/clk/starfive/Kconfig @@ -20,3 +20,14 @@ config CLK_STARFIVE_JH7100_AUDIO help Say Y or M here to support the audio clocks on the StarFive JH7100 SoC. + +config CLK_STARFIVE_JH7110_SYS + bool "StarFive JH7110 system clock support" + depends on ARCH_STARFIVE || COMPILE_TEST + select AUXILIARY_BUS + select CLK_STARFIVE_JH71X0 + select RESET_STARFIVE_JH7110 + default ARCH_STARFIVE + help + Say yes here to support the system clock controller on the + StarFive JH7110 SoC. diff --git a/drivers/clk/starfive/Makefile b/drivers/clk/starfive/Makefile index 82edfa9f9cb81..5ca4e887fb9cc 100644 --- a/drivers/clk/starfive/Makefile +++ b/drivers/clk/starfive/Makefile @@ -3,3 +3,5 @@ obj-$(CONFIG_CLK_STARFIVE_JH71X0) += clk-starfive-jh71x0.o obj-$(CONFIG_CLK_STARFIVE_JH7100) += clk-starfive-jh7100.o obj-$(CONFIG_CLK_STARFIVE_JH7100_AUDIO) += clk-starfive-jh7100-audio.o + +obj-$(CONFIG_CLK_STARFIVE_JH7110_SYS) += clk-starfive-jh7110-sys.o diff --git a/drivers/clk/starfive/clk-starfive-jh7110-sys.c b/drivers/clk/starfive/clk-starfive-jh7110-sys.c new file mode 100644 index 0000000000000..f98a508969c3b --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh7110-sys.c @@ -0,0 +1,450 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JH7110 System Clock Driver + * + * Copyright (C) 2022 Emil Renner Berthing + * Copyright (C) 2022 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include + +#include + +#include "clk-starfive-jh71x0.h" + +/* external clocks */ +#define JH7110_SYSCLK_OSC (JH7110_SYSCLK_END + 0) +#define JH7110_SYSCLK_GMAC1_RMII_REFIN (JH7110_SYSCLK_END + 1) +#define JH7110_SYSCLK_GMAC1_RGMII_RXIN (JH7110_SYSCLK_END + 2) +#define JH7110_SYSCLK_I2STX_BCLK_EXT (JH7110_SYSCLK_END + 3) +#define JH7110_SYSCLK_I2STX_LRCK_EXT (JH7110_SYSCLK_END + 4) +#define JH7110_SYSCLK_I2SRX_BCLK_EXT (JH7110_SYSCLK_END + 5) +#define JH7110_SYSCLK_I2SRX_LRCK_EXT (JH7110_SYSCLK_END + 6) +#define JH7110_SYSCLK_TDM_EXT (JH7110_SYSCLK_END + 7) +#define JH7110_SYSCLK_MCLK_EXT (JH7110_SYSCLK_END + 8) +#define JH7110_SYSCLK_PLL0_OUT (JH7110_SYSCLK_END + 9) +#define JH7110_SYSCLK_PLL1_OUT (JH7110_SYSCLK_END + 10) +#define JH7110_SYSCLK_PLL2_OUT (JH7110_SYSCLK_END + 11) + +static const struct jh71x0_clk_data jh7110_sysclk_data[] __initconst = { + /* root */ + JH71X0__MUX(JH7110_SYSCLK_CPU_ROOT, "cpu_root", 2, + JH7110_SYSCLK_OSC, + JH7110_SYSCLK_PLL0_OUT), + JH71X0__DIV(JH7110_SYSCLK_CPU_CORE, "cpu_core", 7, JH7110_SYSCLK_CPU_ROOT), + JH71X0__DIV(JH7110_SYSCLK_CPU_BUS, "cpu_bus", 2, JH7110_SYSCLK_CPU_CORE), + JH71X0__MUX(JH7110_SYSCLK_GPU_ROOT, "gpu_root", 2, + JH7110_SYSCLK_PLL2_OUT, + JH7110_SYSCLK_PLL1_OUT), + JH71X0_MDIV(JH7110_SYSCLK_PERH_ROOT, "perh_root", 2, 2, + JH7110_SYSCLK_PLL0_OUT, + JH7110_SYSCLK_PLL2_OUT), + JH71X0__MUX(JH7110_SYSCLK_BUS_ROOT, "bus_root", 2, + JH7110_SYSCLK_OSC, + JH7110_SYSCLK_PLL2_OUT), + JH71X0__DIV(JH7110_SYSCLK_NOCSTG_BUS, "nocstg_bus", 3, JH7110_SYSCLK_BUS_ROOT), + JH71X0__DIV(JH7110_SYSCLK_AXI_CFG0, "axi_cfg0", 3, JH7110_SYSCLK_BUS_ROOT), + JH71X0__DIV(JH7110_SYSCLK_STG_AXIAHB, "stg_axiahb", 2, JH7110_SYSCLK_AXI_CFG0), + JH71X0_GATE(JH7110_SYSCLK_AHB0, "ahb0", CLK_IS_CRITICAL, JH7110_SYSCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_SYSCLK_AHB1, "ahb1", CLK_IS_CRITICAL, JH7110_SYSCLK_STG_AXIAHB), + JH71X0__DIV(JH7110_SYSCLK_APB_BUS, "apb_bus", 8, JH7110_SYSCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_SYSCLK_APB0, "apb0", CLK_IS_CRITICAL, JH7110_SYSCLK_APB_BUS), + JH71X0__DIV(JH7110_SYSCLK_PLL0_DIV2, "pll0_div2", 2, JH7110_SYSCLK_PLL0_OUT), + JH71X0__DIV(JH7110_SYSCLK_PLL1_DIV2, "pll1_div2", 2, JH7110_SYSCLK_PLL1_OUT), + JH71X0__DIV(JH7110_SYSCLK_PLL2_DIV2, "pll2_div2", 2, JH7110_SYSCLK_PLL2_OUT), + JH71X0__DIV(JH7110_SYSCLK_AUDIO_ROOT, "audio_root", 8, JH7110_SYSCLK_PLL2_OUT), + JH71X0__DIV(JH7110_SYSCLK_MCLK_INNER, "mclk_inner", 64, JH7110_SYSCLK_AUDIO_ROOT), + JH71X0__MUX(JH7110_SYSCLK_MCLK, "mclk", 2, + JH7110_SYSCLK_MCLK_INNER, + JH7110_SYSCLK_MCLK_EXT), + JH71X0_GATE(JH7110_SYSCLK_MCLK_OUT, "mclk_out", 0, JH7110_SYSCLK_MCLK_INNER), + JH71X0_MDIV(JH7110_SYSCLK_ISP_2X, "isp_2x", 8, 2, + JH7110_SYSCLK_PLL2_OUT, + JH7110_SYSCLK_PLL1_OUT), + JH71X0__DIV(JH7110_SYSCLK_ISP_AXI, "isp_axi", 4, JH7110_SYSCLK_ISP_2X), + JH71X0_GDIV(JH7110_SYSCLK_GCLK0, "gclk0", 0, 62, JH7110_SYSCLK_PLL0_DIV2), + JH71X0_GDIV(JH7110_SYSCLK_GCLK1, "gclk1", 0, 62, JH7110_SYSCLK_PLL1_DIV2), + JH71X0_GDIV(JH7110_SYSCLK_GCLK2, "gclk2", 0, 62, JH7110_SYSCLK_PLL2_DIV2), + /* cores */ + JH71X0_GATE(JH7110_SYSCLK_CORE, "core", CLK_IS_CRITICAL, JH7110_SYSCLK_CPU_CORE), + JH71X0_GATE(JH7110_SYSCLK_CORE1, "core1", CLK_IS_CRITICAL, JH7110_SYSCLK_CPU_CORE), + JH71X0_GATE(JH7110_SYSCLK_CORE2, "core2", CLK_IS_CRITICAL, JH7110_SYSCLK_CPU_CORE), + JH71X0_GATE(JH7110_SYSCLK_CORE3, "core3", CLK_IS_CRITICAL, JH7110_SYSCLK_CPU_CORE), + JH71X0_GATE(JH7110_SYSCLK_CORE4, "core4", CLK_IS_CRITICAL, JH7110_SYSCLK_CPU_CORE), + JH71X0_GATE(JH7110_SYSCLK_DEBUG, "debug", 0, JH7110_SYSCLK_CPU_BUS), + JH71X0__DIV(JH7110_SYSCLK_RTC_TOGGLE, "rtc_toggle", 6, JH7110_SYSCLK_OSC), + JH71X0_GATE(JH7110_SYSCLK_TRACE0, "trace0", 0, JH7110_SYSCLK_CPU_CORE), + JH71X0_GATE(JH7110_SYSCLK_TRACE1, "trace1", 0, JH7110_SYSCLK_CPU_CORE), + JH71X0_GATE(JH7110_SYSCLK_TRACE2, "trace2", 0, JH7110_SYSCLK_CPU_CORE), + JH71X0_GATE(JH7110_SYSCLK_TRACE3, "trace3", 0, JH7110_SYSCLK_CPU_CORE), + JH71X0_GATE(JH7110_SYSCLK_TRACE4, "trace4", 0, JH7110_SYSCLK_CPU_CORE), + JH71X0_GATE(JH7110_SYSCLK_TRACE_COM, "trace_com", 0, JH7110_SYSCLK_CPU_BUS), + /* noc */ + JH71X0_GATE(JH7110_SYSCLK_NOC_BUS_CPU_AXI, "noc_bus_cpu_axi", CLK_IS_CRITICAL, + JH7110_SYSCLK_CPU_BUS), + JH71X0_GATE(JH7110_SYSCLK_NOC_BUS_AXICFG0_AXI, "noc_bus_axicfg0_axi", CLK_IS_CRITICAL, + JH7110_SYSCLK_AXI_CFG0), + /* ddr */ + JH71X0__DIV(JH7110_SYSCLK_OSC_DIV2, "osc_div2", 2, JH7110_SYSCLK_OSC), + JH71X0__DIV(JH7110_SYSCLK_PLL1_DIV4, "pll1_div4", 2, JH7110_SYSCLK_PLL1_DIV2), + JH71X0__DIV(JH7110_SYSCLK_PLL1_DIV8, "pll1_div8", 2, JH7110_SYSCLK_PLL1_DIV4), + JH71X0__MUX(JH7110_SYSCLK_DDR_BUS, "ddr_bus", 4, + JH7110_SYSCLK_OSC_DIV2, + JH7110_SYSCLK_PLL1_DIV2, + JH7110_SYSCLK_PLL1_DIV4, + JH7110_SYSCLK_PLL1_DIV8), + JH71X0_GATE(JH7110_SYSCLK_DDR_AXI, "ddr_axi", CLK_IS_CRITICAL, JH7110_SYSCLK_DDR_BUS), + /* gpu */ + JH71X0__DIV(JH7110_SYSCLK_GPU_CORE, "gpu_core", 7, JH7110_SYSCLK_GPU_ROOT), + JH71X0_GATE(JH7110_SYSCLK_GPU_CORE_CLK, "gpu_core_clk", 0, JH7110_SYSCLK_GPU_CORE), + JH71X0_GATE(JH7110_SYSCLK_GPU_SYS_CLK, "gpu_sys_clk", 0, JH7110_SYSCLK_ISP_AXI), + JH71X0_GATE(JH7110_SYSCLK_GPU_APB, "gpu_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GDIV(JH7110_SYSCLK_GPU_RTC_TOGGLE, "gpu_rtc_toggle", 0, 12, JH7110_SYSCLK_OSC), + JH71X0_GATE(JH7110_SYSCLK_NOC_BUS_GPU_AXI, "noc_bus_gpu_axi", 0, JH7110_SYSCLK_GPU_CORE), + /* isp */ + JH71X0_GATE(JH7110_SYSCLK_ISP_TOP_CORE, "isp_top_core", 0, JH7110_SYSCLK_ISP_2X), + JH71X0_GATE(JH7110_SYSCLK_ISP_TOP_AXI, "isp_top_axi", 0, JH7110_SYSCLK_ISP_AXI), + JH71X0_GATE(JH7110_SYSCLK_NOC_BUS_ISP_AXI, "noc_bus_isp_axi", CLK_IS_CRITICAL, + JH7110_SYSCLK_ISP_AXI), + /* hifi4 */ + JH71X0__DIV(JH7110_SYSCLK_HIFI4_CORE, "hifi4_core", 15, JH7110_SYSCLK_BUS_ROOT), + JH71X0__DIV(JH7110_SYSCLK_HIFI4_AXI, "hifi4_axi", 2, JH7110_SYSCLK_HIFI4_CORE), + /* axi_cfg1 */ + JH71X0_GATE(JH7110_SYSCLK_AXI_CFG1_MAIN, "axi_cfg1_main", CLK_IS_CRITICAL, + JH7110_SYSCLK_ISP_AXI), + JH71X0_GATE(JH7110_SYSCLK_AXI_CFG1_AHB, "axi_cfg1_ahb", CLK_IS_CRITICAL, + JH7110_SYSCLK_AHB0), + /* vout */ + JH71X0_GATE(JH7110_SYSCLK_VOUT_SRC, "vout_src", 0, JH7110_SYSCLK_PLL2_OUT), + JH71X0__DIV(JH7110_SYSCLK_VOUT_AXI, "vout_axi", 7, JH7110_SYSCLK_PLL2_OUT), + JH71X0_GATE(JH7110_SYSCLK_NOC_BUS_DISP_AXI, "noc_bus_disp_axi", 0, JH7110_SYSCLK_VOUT_AXI), + JH71X0_GATE(JH7110_SYSCLK_VOUT_TOP_AHB, "vout_top_ahb", 0, JH7110_SYSCLK_AHB1), + JH71X0_GATE(JH7110_SYSCLK_VOUT_TOP_AXI, "vout_top_axi", 0, JH7110_SYSCLK_VOUT_AXI), + JH71X0_GATE(JH7110_SYSCLK_VOUT_TOP_HDMITX0_MCLK, "vout_top_hdmitx0_mclk", 0, + JH7110_SYSCLK_MCLK), + JH71X0__DIV(JH7110_SYSCLK_VOUT_TOP_MIPIPHY_REF, "vout_top_mipiphy_ref", 2, + JH7110_SYSCLK_OSC), + /* jpegc */ + JH71X0__DIV(JH7110_SYSCLK_JPEGC_AXI, "jpegc_axi", 16, JH7110_SYSCLK_PLL2_OUT), + JH71X0_GATE(JH7110_SYSCLK_CODAJ12_AXI, "codaj12_axi", 0, JH7110_SYSCLK_JPEGC_AXI), + JH71X0_GDIV(JH7110_SYSCLK_CODAJ12_CORE, "codaj12_core", 0, 16, JH7110_SYSCLK_PLL2_OUT), + JH71X0_GATE(JH7110_SYSCLK_CODAJ12_APB, "codaj12_apb", 0, JH7110_SYSCLK_APB_BUS), + /* vdec */ + JH71X0__DIV(JH7110_SYSCLK_VDEC_AXI, "vdec_axi", 7, JH7110_SYSCLK_BUS_ROOT), + JH71X0_GATE(JH7110_SYSCLK_WAVE511_AXI, "wave511_axi", 0, JH7110_SYSCLK_VDEC_AXI), + JH71X0_GDIV(JH7110_SYSCLK_WAVE511_BPU, "wave511_bpu", 0, 7, JH7110_SYSCLK_BUS_ROOT), + JH71X0_GDIV(JH7110_SYSCLK_WAVE511_VCE, "wave511_vce", 0, 7, JH7110_SYSCLK_PLL0_OUT), + JH71X0_GATE(JH7110_SYSCLK_WAVE511_APB, "wave511_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GATE(JH7110_SYSCLK_VDEC_JPG, "vdec_jpg", 0, JH7110_SYSCLK_JPEGC_AXI), + JH71X0_GATE(JH7110_SYSCLK_VDEC_MAIN, "vdec_main", 0, JH7110_SYSCLK_VDEC_AXI), + JH71X0_GATE(JH7110_SYSCLK_NOC_BUS_VDEC_AXI, "noc_bus_vdec_axi", 0, JH7110_SYSCLK_VDEC_AXI), + /* venc */ + JH71X0__DIV(JH7110_SYSCLK_VENC_AXI, "venc_axi", 15, JH7110_SYSCLK_PLL2_OUT), + JH71X0_GATE(JH7110_SYSCLK_WAVE420L_AXI, "wave420l_axi", 0, JH7110_SYSCLK_VENC_AXI), + JH71X0_GDIV(JH7110_SYSCLK_WAVE420L_BPU, "wave420l_bpu", 0, 15, JH7110_SYSCLK_PLL2_OUT), + JH71X0_GDIV(JH7110_SYSCLK_WAVE420L_VCE, "wave420l_vce", 0, 15, JH7110_SYSCLK_PLL2_OUT), + JH71X0_GATE(JH7110_SYSCLK_WAVE420L_APB, "wave420l_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GATE(JH7110_SYSCLK_NOC_BUS_VENC_AXI, "noc_bus_venc_axi", 0, JH7110_SYSCLK_VENC_AXI), + /* axi_cfg0 */ + JH71X0_GATE(JH7110_SYSCLK_AXI_CFG0_MAIN_DIV, "axi_cfg0_main_div", CLK_IS_CRITICAL, + JH7110_SYSCLK_AHB1), + JH71X0_GATE(JH7110_SYSCLK_AXI_CFG0_MAIN, "axi_cfg0_main", CLK_IS_CRITICAL, + JH7110_SYSCLK_AXI_CFG0), + JH71X0_GATE(JH7110_SYSCLK_AXI_CFG0_HIFI4, "axi_cfg0_hifi4", CLK_IS_CRITICAL, + JH7110_SYSCLK_HIFI4_AXI), + /* intmem */ + JH71X0_GATE(JH7110_SYSCLK_AXIMEM2_AXI, "aximem2_axi", 0, JH7110_SYSCLK_AXI_CFG0), + /* qspi */ + JH71X0_GATE(JH7110_SYSCLK_QSPI_AHB, "qspi_ahb", CLK_IGNORE_UNUSED, JH7110_SYSCLK_AHB1), + JH71X0_GATE(JH7110_SYSCLK_QSPI_APB, "qspi_apb", CLK_IGNORE_UNUSED, JH7110_SYSCLK_APB_BUS), + JH71X0__DIV(JH7110_SYSCLK_QSPI_REF_SRC, "qspi_ref_src", 16, JH7110_SYSCLK_PLL0_OUT), + JH71X0_GMUX(JH7110_SYSCLK_QSPI_REF, "qspi_ref", 0, 2, + JH7110_SYSCLK_OSC, + JH7110_SYSCLK_QSPI_REF_SRC), + /* sdio */ + JH71X0_GATE(JH7110_SYSCLK_SDIO0_AHB, "sdio0_ahb", 0, JH7110_SYSCLK_AHB0), + JH71X0_GATE(JH7110_SYSCLK_SDIO1_AHB, "sdio1_ahb", 0, JH7110_SYSCLK_AHB0), + JH71X0_GDIV(JH7110_SYSCLK_SDIO0_SDCARD, "sdio0_sdcard", 0, 15, JH7110_SYSCLK_AXI_CFG0), + JH71X0_GDIV(JH7110_SYSCLK_SDIO1_SDCARD, "sdio1_sdcard", 0, 15, JH7110_SYSCLK_AXI_CFG0), + /* stg */ + JH71X0__DIV(JH7110_SYSCLK_USB_125M, "usb_125m", 15, JH7110_SYSCLK_PLL0_OUT), + JH71X0_GATE(JH7110_SYSCLK_NOC_BUS_STG_AXI, "noc_bus_stg_axi", CLK_IS_CRITICAL, + JH7110_SYSCLK_NOCSTG_BUS), + /* gmac1 */ + JH71X0_GATE(JH7110_SYSCLK_GMAC1_AHB, "gmac1_ahb", 0, JH7110_SYSCLK_AHB0), + JH71X0_GATE(JH7110_SYSCLK_GMAC1_AXI, "gmac1_axi", 0, JH7110_SYSCLK_STG_AXIAHB), + JH71X0__DIV(JH7110_SYSCLK_GMAC_SRC, "gmac_src", 7, JH7110_SYSCLK_PLL0_OUT), + JH71X0__DIV(JH7110_SYSCLK_GMAC1_GTXCLK, "gmac1_gtxclk", 15, JH7110_SYSCLK_PLL0_OUT), + JH71X0__DIV(JH7110_SYSCLK_GMAC1_RMII_RTX, "gmac1_rmii_rtx", 30, + JH7110_SYSCLK_GMAC1_RMII_REFIN), + JH71X0_GDIV(JH7110_SYSCLK_GMAC1_PTP, "gmac1_ptp", 0, 31, JH7110_SYSCLK_GMAC_SRC), + JH71X0__MUX(JH7110_SYSCLK_GMAC1_RX, "gmac1_rx", 2, + JH7110_SYSCLK_GMAC1_RGMII_RXIN, + JH7110_SYSCLK_GMAC1_RMII_RTX), + JH71X0__INV(JH7110_SYSCLK_GMAC1_RX_INV, "gmac1_rx_inv", JH7110_SYSCLK_GMAC1_RX), + JH71X0_GMUX(JH7110_SYSCLK_GMAC1_TX, "gmac1_tx", CLK_SET_RATE_PARENT, 2, + JH7110_SYSCLK_GMAC1_GTXCLK, + JH7110_SYSCLK_GMAC1_RMII_RTX), + JH71X0__INV(JH7110_SYSCLK_GMAC1_TX_INV, "gmac1_tx_inv", JH7110_SYSCLK_GMAC1_TX), + JH71X0_GATE(JH7110_SYSCLK_GMAC1_GTXC, "gmac1_gtxc", CLK_SET_RATE_PARENT, + JH7110_SYSCLK_GMAC1_GTXCLK), + /* gmac0 */ + JH71X0_GDIV(JH7110_SYSCLK_GMAC0_GTXCLK, "gmac0_gtxclk", 0, 15, JH7110_SYSCLK_PLL0_OUT), + JH71X0_GDIV(JH7110_SYSCLK_GMAC0_PTP, "gmac0_ptp", 0, 31, JH7110_SYSCLK_GMAC_SRC), + JH71X0_GDIV(JH7110_SYSCLK_GMAC_PHY, "gmac_phy", 0, 31, JH7110_SYSCLK_GMAC_SRC), + JH71X0_GATE(JH7110_SYSCLK_GMAC0_GTXC, "gmac0_gtxc", CLK_SET_RATE_PARENT, + JH7110_SYSCLK_GMAC0_GTXCLK), + /* apb misc */ + JH71X0_GATE(JH7110_SYSCLK_IOMUX_APB, "iomux_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GATE(JH7110_SYSCLK_MAILBOX_APB, "mailbox_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GATE(JH7110_SYSCLK_INT_CTRL_APB, "int_ctrl_apb", 0, JH7110_SYSCLK_APB_BUS), + /* can0 */ + JH71X0_GATE(JH7110_SYSCLK_CAN0_APB, "can0_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GDIV(JH7110_SYSCLK_CAN0_TIMER, "can0_timer", 0, 24, JH7110_SYSCLK_OSC), + JH71X0_GDIV(JH7110_SYSCLK_CAN0_CAN, "can0_can", 0, 63, JH7110_SYSCLK_PERH_ROOT), + /* can1 */ + JH71X0_GATE(JH7110_SYSCLK_CAN1_APB, "can1_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GDIV(JH7110_SYSCLK_CAN1_TIMER, "can1_timer", 0, 24, JH7110_SYSCLK_OSC), + JH71X0_GDIV(JH7110_SYSCLK_CAN1_CAN, "can1_can", 0, 63, JH7110_SYSCLK_PERH_ROOT), + /* pwm */ + JH71X0_GATE(JH7110_SYSCLK_PWM_APB, "pwm_apb", 0, JH7110_SYSCLK_APB_BUS), + /* wdt */ + JH71X0_GATE(JH7110_SYSCLK_WDT_APB, "wdt_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GATE(JH7110_SYSCLK_WDT_CORE, "wdt_core", 0, JH7110_SYSCLK_OSC), + /* timer */ + JH71X0_GATE(JH7110_SYSCLK_TIMER_APB, "timer_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GATE(JH7110_SYSCLK_TIMER0, "timer0", 0, JH7110_SYSCLK_OSC), + JH71X0_GATE(JH7110_SYSCLK_TIMER1, "timer1", 0, JH7110_SYSCLK_OSC), + JH71X0_GATE(JH7110_SYSCLK_TIMER2, "timer2", 0, JH7110_SYSCLK_OSC), + JH71X0_GATE(JH7110_SYSCLK_TIMER3, "timer3", 0, JH7110_SYSCLK_OSC), + /* temp sensor */ + JH71X0_GATE(JH7110_SYSCLK_TEMP_APB, "temp_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GDIV(JH7110_SYSCLK_TEMP_CORE, "temp_core", 0, 24, JH7110_SYSCLK_OSC), + /* spi */ + JH71X0_GATE(JH7110_SYSCLK_SPI0_APB, "spi0_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GATE(JH7110_SYSCLK_SPI1_APB, "spi1_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GATE(JH7110_SYSCLK_SPI2_APB, "spi2_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GATE(JH7110_SYSCLK_SPI3_APB, "spi3_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GATE(JH7110_SYSCLK_SPI4_APB, "spi4_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GATE(JH7110_SYSCLK_SPI5_APB, "spi5_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GATE(JH7110_SYSCLK_SPI6_APB, "spi6_apb", 0, JH7110_SYSCLK_APB_BUS), + /* i2c */ + JH71X0_GATE(JH7110_SYSCLK_I2C0_APB, "i2c0_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GATE(JH7110_SYSCLK_I2C1_APB, "i2c1_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GATE(JH7110_SYSCLK_I2C2_APB, "i2c2_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GATE(JH7110_SYSCLK_I2C3_APB, "i2c3_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GATE(JH7110_SYSCLK_I2C4_APB, "i2c4_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GATE(JH7110_SYSCLK_I2C5_APB, "i2c5_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GATE(JH7110_SYSCLK_I2C6_APB, "i2c6_apb", 0, JH7110_SYSCLK_APB_BUS), + /* uart */ + JH71X0_GATE(JH7110_SYSCLK_UART0_APB, "uart0_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GATE(JH7110_SYSCLK_UART0_CORE, "uart0_core", 0, JH7110_SYSCLK_OSC), + JH71X0_GATE(JH7110_SYSCLK_UART1_APB, "uart1_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GATE(JH7110_SYSCLK_UART1_CORE, "uart1_core", 0, JH7110_SYSCLK_OSC), + JH71X0_GATE(JH7110_SYSCLK_UART2_APB, "uart2_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GATE(JH7110_SYSCLK_UART2_CORE, "uart2_core", 0, JH7110_SYSCLK_OSC), + JH71X0_GATE(JH7110_SYSCLK_UART3_APB, "uart3_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GDIV(JH7110_SYSCLK_UART3_CORE, "uart3_core", 0, 10, JH7110_SYSCLK_PERH_ROOT), + JH71X0_GATE(JH7110_SYSCLK_UART4_APB, "uart4_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GDIV(JH7110_SYSCLK_UART4_CORE, "uart4_core", 0, 10, JH7110_SYSCLK_PERH_ROOT), + JH71X0_GATE(JH7110_SYSCLK_UART5_APB, "uart5_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GDIV(JH7110_SYSCLK_UART5_CORE, "uart5_core", 0, 10, JH7110_SYSCLK_PERH_ROOT), + /* pwmdac */ + JH71X0_GATE(JH7110_SYSCLK_PWMDAC_APB, "pwmdac_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GDIV(JH7110_SYSCLK_PWMDAC_CORE, "pwmdac_core", 0, 256, JH7110_SYSCLK_AUDIO_ROOT), + /* spdif */ + JH71X0_GATE(JH7110_SYSCLK_SPDIF_APB, "spdif_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GATE(JH7110_SYSCLK_SPDIF_CORE, "spdif_core", 0, JH7110_SYSCLK_MCLK), + /* i2stx0 */ + JH71X0_GATE(JH7110_SYSCLK_I2STX0_APB, "i2stx0_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GDIV(JH7110_SYSCLK_I2STX0_BCLK_MST, "i2stx0_bclk_mst", 0, 32, JH7110_SYSCLK_MCLK), + JH71X0__INV(JH7110_SYSCLK_I2STX0_BCLK_MST_INV, "i2stx0_bclk_mst_inv", + JH7110_SYSCLK_I2STX0_BCLK_MST), + JH71X0_MDIV(JH7110_SYSCLK_I2STX0_LRCK_MST, "i2stx0_lrck_mst", 64, 2, + JH7110_SYSCLK_I2STX0_BCLK_MST_INV, + JH7110_SYSCLK_I2STX0_BCLK_MST), + JH71X0__MUX(JH7110_SYSCLK_I2STX0_BCLK, "i2stx0_bclk", 2, + JH7110_SYSCLK_I2STX0_BCLK_MST, + JH7110_SYSCLK_I2STX_BCLK_EXT), + JH71X0__INV(JH7110_SYSCLK_I2STX0_BCLK_INV, "i2stx0_bclk_inv", JH7110_SYSCLK_I2STX0_BCLK), + JH71X0__MUX(JH7110_SYSCLK_I2STX0_LRCK, "i2stx0_lrck", 2, + JH7110_SYSCLK_I2STX0_LRCK_MST, + JH7110_SYSCLK_I2STX_LRCK_EXT), + /* i2stx1 */ + JH71X0_GATE(JH7110_SYSCLK_I2STX1_APB, "i2stx1_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GDIV(JH7110_SYSCLK_I2STX1_BCLK_MST, "i2stx1_bclk_mst", 0, 32, JH7110_SYSCLK_MCLK), + JH71X0__INV(JH7110_SYSCLK_I2STX1_BCLK_MST_INV, "i2stx1_bclk_mst_inv", + JH7110_SYSCLK_I2STX1_BCLK_MST), + JH71X0_MDIV(JH7110_SYSCLK_I2STX1_LRCK_MST, "i2stx1_lrck_mst", 64, 2, + JH7110_SYSCLK_I2STX1_BCLK_MST_INV, + JH7110_SYSCLK_I2STX1_BCLK_MST), + JH71X0__MUX(JH7110_SYSCLK_I2STX1_BCLK, "i2stx1_bclk", 2, + JH7110_SYSCLK_I2STX1_BCLK_MST, + JH7110_SYSCLK_I2STX_BCLK_EXT), + JH71X0__INV(JH7110_SYSCLK_I2STX1_BCLK_INV, "i2stx1_bclk_inv", JH7110_SYSCLK_I2STX1_BCLK), + JH71X0__MUX(JH7110_SYSCLK_I2STX1_LRCK, "i2stx1_lrck", 2, + JH7110_SYSCLK_I2STX1_LRCK_MST, + JH7110_SYSCLK_I2STX_LRCK_EXT), + /* i2srx */ + JH71X0_GATE(JH7110_SYSCLK_I2SRX_APB, "i2srx_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GDIV(JH7110_SYSCLK_I2SRX_BCLK_MST, "i2srx_bclk_mst", 0, 32, JH7110_SYSCLK_MCLK), + JH71X0__INV(JH7110_SYSCLK_I2SRX_BCLK_MST_INV, "i2srx_bclk_mst_inv", + JH7110_SYSCLK_I2SRX_BCLK_MST), + JH71X0_MDIV(JH7110_SYSCLK_I2SRX_LRCK_MST, "i2srx_lrck_mst", 64, 2, + JH7110_SYSCLK_I2SRX_BCLK_MST_INV, + JH7110_SYSCLK_I2SRX_BCLK_MST), + JH71X0__MUX(JH7110_SYSCLK_I2SRX_BCLK, "i2srx_bclk", 2, + JH7110_SYSCLK_I2SRX_BCLK_MST, + JH7110_SYSCLK_I2SRX_BCLK_EXT), + JH71X0__INV(JH7110_SYSCLK_I2SRX_BCLK_INV, "i2srx_bclk_inv", JH7110_SYSCLK_I2SRX_BCLK), + JH71X0__MUX(JH7110_SYSCLK_I2SRX_LRCK, "i2srx_lrck", 2, + JH7110_SYSCLK_I2SRX_LRCK_MST, + JH7110_SYSCLK_I2SRX_LRCK_EXT), + /* pdm */ + JH71X0_GDIV(JH7110_SYSCLK_PDM_DMIC, "pdm_dmic", 0, 64, JH7110_SYSCLK_MCLK), + JH71X0_GATE(JH7110_SYSCLK_PDM_APB, "pdm_apb", 0, JH7110_SYSCLK_APB0), + /* tdm */ + JH71X0_GATE(JH7110_SYSCLK_TDM_AHB, "tdm_ahb", 0, JH7110_SYSCLK_AHB0), + JH71X0_GATE(JH7110_SYSCLK_TDM_APB, "tdm_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GDIV(JH7110_SYSCLK_TDM_INTERNAL, "tdm_internal", 0, 64, JH7110_SYSCLK_MCLK), + JH71X0__MUX(JH7110_SYSCLK_TDM_TDM, "tdm_tdm", 2, + JH7110_SYSCLK_TDM_INTERNAL, + JH7110_SYSCLK_TDM_EXT), + JH71X0__INV(JH7110_SYSCLK_TDM_TDM_INV, "tdm_tdm_inv", JH7110_SYSCLK_TDM_TDM), + /* jtag */ + JH71X0__DIV(JH7110_SYSCLK_JTAG_CERTIFICATION_TRNG, "jtag_certification_trng", 4, + JH7110_SYSCLK_OSC), +}; + +static struct clk_hw *jh7110_sysclk_get(struct of_phandle_args *clkspec, void *data) +{ + struct jh71x0_clk_priv *priv = data; + unsigned int idx = clkspec->args[0]; + + if (idx < JH7110_SYSCLK_END) + return &priv->reg[idx].hw; + + if (idx >= JH7110_SYSCLK_PLL0_OUT && idx <= JH7110_SYSCLK_PLL2_OUT) + return priv->pll[idx - JH7110_SYSCLK_PLL0_OUT]; + + return ERR_PTR(-EINVAL); +} + +static int __init jh7110_syscrg_probe(struct platform_device *pdev) +{ + struct jh71x0_clk_priv *priv; + unsigned int idx; + int ret; + + priv = devm_kzalloc(&pdev->dev, + struct_size(priv, reg, JH7110_SYSCLK_END), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spin_lock_init(&priv->rmw_lock); + priv->dev = &pdev->dev; + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + dev_set_drvdata(priv->dev, (void *)(&priv->base)); + + /* + * These PLL clocks are not actually fixed factor clocks and can be + * controlled by the syscon registers of JH7110. They will be dropped + * and registered in the PLL clock driver instead. + */ + /* 24MHz -> 1000.0MHz */ + priv->pll[0] = devm_clk_hw_register_fixed_factor(priv->dev, "pll0_out", + "osc", 0, 125, 3); + if (IS_ERR(priv->pll[0])) + return PTR_ERR(priv->pll[0]); + + /* 24MHz -> 1066.0MHz */ + priv->pll[1] = devm_clk_hw_register_fixed_factor(priv->dev, "pll1_out", + "osc", 0, 533, 12); + if (IS_ERR(priv->pll[1])) + return PTR_ERR(priv->pll[1]); + + /* 24MHz -> 1188.0MHz */ + priv->pll[2] = devm_clk_hw_register_fixed_factor(priv->dev, "pll2_out", + "osc", 0, 99, 2); + if (IS_ERR(priv->pll[2])) + return PTR_ERR(priv->pll[2]); + + for (idx = 0; idx < JH7110_SYSCLK_END; idx++) { + u32 max = jh7110_sysclk_data[idx].max; + struct clk_parent_data parents[4] = {}; + struct clk_init_data init = { + .name = jh7110_sysclk_data[idx].name, + .ops = starfive_jh71x0_clk_ops(max), + .parent_data = parents, + .num_parents = + ((max & JH71X0_CLK_MUX_MASK) >> JH71X0_CLK_MUX_SHIFT) + 1, + .flags = jh7110_sysclk_data[idx].flags, + }; + struct jh71x0_clk *clk = &priv->reg[idx]; + unsigned int i; + + for (i = 0; i < init.num_parents; i++) { + unsigned int pidx = jh7110_sysclk_data[idx].parents[i]; + + if (pidx < JH7110_SYSCLK_END) + parents[i].hw = &priv->reg[pidx].hw; + else if (pidx == JH7110_SYSCLK_OSC) + parents[i].fw_name = "osc"; + else if (pidx == JH7110_SYSCLK_GMAC1_RMII_REFIN) + parents[i].fw_name = "gmac1_rmii_refin"; + else if (pidx == JH7110_SYSCLK_GMAC1_RGMII_RXIN) + parents[i].fw_name = "gmac1_rgmii_rxin"; + else if (pidx == JH7110_SYSCLK_I2STX_BCLK_EXT) + parents[i].fw_name = "i2stx_bclk_ext"; + else if (pidx == JH7110_SYSCLK_I2STX_LRCK_EXT) + parents[i].fw_name = "i2stx_lrck_ext"; + else if (pidx == JH7110_SYSCLK_I2SRX_BCLK_EXT) + parents[i].fw_name = "i2srx_bclk_ext"; + else if (pidx == JH7110_SYSCLK_I2SRX_LRCK_EXT) + parents[i].fw_name = "i2srx_lrck_ext"; + else if (pidx == JH7110_SYSCLK_TDM_EXT) + parents[i].fw_name = "tdm_ext"; + else if (pidx == JH7110_SYSCLK_MCLK_EXT) + parents[i].fw_name = "mclk_ext"; + else if (pidx >= JH7110_SYSCLK_PLL0_OUT && + pidx <= JH7110_SYSCLK_PLL2_OUT) + parents[i].hw = priv->pll[pidx - JH7110_SYSCLK_PLL0_OUT]; + } + + clk->hw.init = &init; + clk->idx = idx; + clk->max_div = max & JH71X0_CLK_DIV_MASK; + + ret = devm_clk_hw_register(&pdev->dev, &clk->hw); + if (ret) + return ret; + } + + ret = devm_of_clk_add_hw_provider(&pdev->dev, jh7110_sysclk_get, priv); + if (ret) + return ret; + + return jh7110_reset_controller_register(priv, "reset-sys", 0); +} + +static const struct of_device_id jh7110_syscrg_match[] = { + { .compatible = "starfive,jh7110-syscrg" }, + { /* sentinel */ } +}; + +static struct platform_driver jh7110_syscrg_driver = { + .driver = { + .name = "clk-starfive-jh7110-sys", + .of_match_table = jh7110_syscrg_match, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver_probe(jh7110_syscrg_driver, jh7110_syscrg_probe); diff --git a/drivers/clk/starfive/clk-starfive-jh71x0.c b/drivers/clk/starfive/clk-starfive-jh71x0.c index b372083d11c34..a8c60630ae731 100644 --- a/drivers/clk/starfive/clk-starfive-jh71x0.c +++ b/drivers/clk/starfive/clk-starfive-jh71x0.c @@ -5,6 +5,7 @@ * Copyright (C) 2021-2022 Emil Renner Berthing */ +#include #include #include #include @@ -331,3 +332,52 @@ const struct clk_ops *starfive_jh71x0_clk_ops(u32 max) return &jh71x0_clk_inv_ops; } EXPORT_SYMBOL_GPL(starfive_jh71x0_clk_ops); + +#if IS_ENABLED(CONFIG_CLK_STARFIVE_JH7110_SYS) + +static void jh7110_reset_unregister_adev(void *_adev) +{ + struct auxiliary_device *adev = _adev; + + auxiliary_device_delete(adev); +} + +static void jh7110_reset_adev_release(struct device *dev) +{ + struct auxiliary_device *adev = to_auxiliary_dev(dev); + + auxiliary_device_uninit(adev); +} + +int jh7110_reset_controller_register(struct jh71x0_clk_priv *priv, + const char *adev_name, + u32 adev_id) +{ + struct auxiliary_device *adev; + int ret; + + adev = devm_kzalloc(priv->dev, sizeof(*adev), GFP_KERNEL); + if (!adev) + return -ENOMEM; + + adev->name = adev_name; + adev->dev.parent = priv->dev; + adev->dev.release = jh7110_reset_adev_release; + adev->id = adev_id; + + ret = auxiliary_device_init(adev); + if (ret) + return ret; + + ret = auxiliary_device_add(adev); + if (ret) { + auxiliary_device_uninit(adev); + return ret; + } + + return devm_add_action_or_reset(priv->dev, + jh7110_reset_unregister_adev, adev); +} +EXPORT_SYMBOL_GPL(jh7110_reset_controller_register); + +#endif diff --git a/drivers/clk/starfive/clk-starfive-jh71x0.h b/drivers/clk/starfive/clk-starfive-jh71x0.h index 34bb11c72eb73..cfe69aabd65a3 100644 --- a/drivers/clk/starfive/clk-starfive-jh71x0.h +++ b/drivers/clk/starfive/clk-starfive-jh71x0.h @@ -120,4 +120,12 @@ struct jh71x0_clk_priv { const struct clk_ops *starfive_jh71x0_clk_ops(u32 max); +#if IS_ENABLED(CONFIG_CLK_STARFIVE_JH7110_SYS) + +int jh7110_reset_controller_register(struct jh71x0_clk_priv *priv, + const char *adev_name, + u32 adev_id); + +#endif + #endif From 5f489583edd1c78d27cd455ea5ffba9d081ac4d2 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sun, 24 Jul 2022 19:43:38 +0200 Subject: [PATCH 14/95] clk: starfive: Add StarFive JH7110 always-on clock driver Add driver for the StarFive JH7110 always-on clock controller and register an auxiliary device for always-on reset controller which is named as "reset-aon". Tested-by: Tommaso Merciai Signed-off-by: Emil Renner Berthing Co-developed-by: Hal Feng Signed-off-by: Hal Feng --- drivers/clk/starfive/Kconfig | 11 ++ drivers/clk/starfive/Makefile | 1 + .../clk/starfive/clk-starfive-jh7110-aon.c | 156 ++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 drivers/clk/starfive/clk-starfive-jh7110-aon.c diff --git a/drivers/clk/starfive/Kconfig b/drivers/clk/starfive/Kconfig index 670c5084aeb82..71c1148ee5f66 100644 --- a/drivers/clk/starfive/Kconfig +++ b/drivers/clk/starfive/Kconfig @@ -31,3 +31,14 @@ config CLK_STARFIVE_JH7110_SYS help Say yes here to support the system clock controller on the StarFive JH7110 SoC. + +config CLK_STARFIVE_JH7110_AON + tristate "StarFive JH7110 always-on clock support" + depends on CLK_STARFIVE_JH7110_SYS + select AUXILIARY_BUS + select CLK_STARFIVE_JH71X0 + select RESET_STARFIVE_JH7110 + default m if ARCH_STARFIVE + help + Say yes here to support the always-on clock controller on the + StarFive JH7110 SoC. diff --git a/drivers/clk/starfive/Makefile b/drivers/clk/starfive/Makefile index 5ca4e887fb9cc..f3df7d957b1e5 100644 --- a/drivers/clk/starfive/Makefile +++ b/drivers/clk/starfive/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_CLK_STARFIVE_JH7100) += clk-starfive-jh7100.o obj-$(CONFIG_CLK_STARFIVE_JH7100_AUDIO) += clk-starfive-jh7100-audio.o obj-$(CONFIG_CLK_STARFIVE_JH7110_SYS) += clk-starfive-jh7110-sys.o +obj-$(CONFIG_CLK_STARFIVE_JH7110_AON) += clk-starfive-jh7110-aon.o diff --git a/drivers/clk/starfive/clk-starfive-jh7110-aon.c b/drivers/clk/starfive/clk-starfive-jh7110-aon.c new file mode 100644 index 0000000000000..941f1189fe5da --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh7110-aon.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JH7110 Always-On Clock Driver + * + * Copyright (C) 2022 Emil Renner Berthing + * Copyright (C) 2022 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include + +#include + +#include "clk-starfive-jh71x0.h" + +/* external clocks */ +#define JH7110_AONCLK_OSC (JH7110_AONCLK_END + 0) +#define JH7110_AONCLK_GMAC0_RMII_REFIN (JH7110_AONCLK_END + 1) +#define JH7110_AONCLK_GMAC0_RGMII_RXIN (JH7110_AONCLK_END + 2) +#define JH7110_AONCLK_STG_AXIAHB (JH7110_AONCLK_END + 3) +#define JH7110_AONCLK_APB_BUS (JH7110_AONCLK_END + 4) +#define JH7110_AONCLK_GMAC0_GTXCLK (JH7110_AONCLK_END + 5) +#define JH7110_AONCLK_RTC_OSC (JH7110_AONCLK_END + 6) + +static const struct jh71x0_clk_data jh7110_aonclk_data[] = { + /* source */ + JH71X0__DIV(JH7110_AONCLK_OSC_DIV4, "osc_div4", 4, JH7110_AONCLK_OSC), + JH71X0__MUX(JH7110_AONCLK_APB_FUNC, "apb_func", 2, + JH7110_AONCLK_OSC_DIV4, + JH7110_AONCLK_OSC), + /* gmac0 */ + JH71X0_GATE(JH7110_AONCLK_GMAC0_AHB, "gmac0_ahb", 0, JH7110_AONCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_AONCLK_GMAC0_AXI, "gmac0_axi", 0, JH7110_AONCLK_STG_AXIAHB), + JH71X0__DIV(JH7110_AONCLK_GMAC0_RMII_RTX, "gmac0_rmii_rtx", 30, + JH7110_AONCLK_GMAC0_RMII_REFIN), + JH71X0_GMUX(JH7110_AONCLK_GMAC0_TX, "gmac0_tx", CLK_SET_RATE_PARENT, 2, + JH7110_AONCLK_GMAC0_GTXCLK, + JH7110_AONCLK_GMAC0_RMII_RTX), + JH71X0__INV(JH7110_AONCLK_GMAC0_TX_INV, "gmac0_tx_inv", JH7110_AONCLK_GMAC0_TX), + JH71X0__MUX(JH7110_AONCLK_GMAC0_RX, "gmac0_rx", 2, + JH7110_AONCLK_GMAC0_RGMII_RXIN, + JH7110_AONCLK_GMAC0_RMII_RTX), + JH71X0__INV(JH7110_AONCLK_GMAC0_RX_INV, "gmac0_rx_inv", JH7110_AONCLK_GMAC0_RX), + /* otpc */ + JH71X0_GATE(JH7110_AONCLK_OTPC_APB, "otpc_apb", 0, JH7110_AONCLK_APB_BUS), + /* rtc */ + JH71X0_GATE(JH7110_AONCLK_RTC_APB, "rtc_apb", 0, JH7110_AONCLK_APB_BUS), + JH71X0__DIV(JH7110_AONCLK_RTC_INTERNAL, "rtc_internal", 1022, JH7110_AONCLK_OSC), + JH71X0__MUX(JH7110_AONCLK_RTC_32K, "rtc_32k", 2, + JH7110_AONCLK_RTC_OSC, + JH7110_AONCLK_RTC_INTERNAL), + JH71X0_GATE(JH7110_AONCLK_RTC_CAL, "rtc_cal", 0, JH7110_AONCLK_OSC), +}; + +static struct clk_hw *jh7110_aonclk_get(struct of_phandle_args *clkspec, void *data) +{ + struct jh71x0_clk_priv *priv = data; + unsigned int idx = clkspec->args[0]; + + if (idx < JH7110_AONCLK_END) + return &priv->reg[idx].hw; + + return ERR_PTR(-EINVAL); +} + +static int jh7110_aoncrg_probe(struct platform_device *pdev) +{ + struct jh71x0_clk_priv *priv; + unsigned int idx; + int ret; + + priv = devm_kzalloc(&pdev->dev, + struct_size(priv, reg, JH7110_AONCLK_END), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spin_lock_init(&priv->rmw_lock); + priv->dev = &pdev->dev; + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + dev_set_drvdata(priv->dev, (void *)(&priv->base)); + + for (idx = 0; idx < JH7110_AONCLK_END; idx++) { + u32 max = jh7110_aonclk_data[idx].max; + struct clk_parent_data parents[4] = {}; + struct clk_init_data init = { + .name = jh7110_aonclk_data[idx].name, + .ops = starfive_jh71x0_clk_ops(max), + .parent_data = parents, + .num_parents = + ((max & JH71X0_CLK_MUX_MASK) >> JH71X0_CLK_MUX_SHIFT) + 1, + .flags = jh7110_aonclk_data[idx].flags, + }; + struct jh71x0_clk *clk = &priv->reg[idx]; + unsigned int i; + + for (i = 0; i < init.num_parents; i++) { + unsigned int pidx = jh7110_aonclk_data[idx].parents[i]; + + if (pidx < JH7110_AONCLK_END) + parents[i].hw = &priv->reg[pidx].hw; + else if (pidx == JH7110_AONCLK_OSC) + parents[i].fw_name = "osc"; + else if (pidx == JH7110_AONCLK_GMAC0_RMII_REFIN) + parents[i].fw_name = "gmac0_rmii_refin"; + else if (pidx == JH7110_AONCLK_GMAC0_RGMII_RXIN) + parents[i].fw_name = "gmac0_rgmii_rxin"; + else if (pidx == JH7110_AONCLK_STG_AXIAHB) + parents[i].fw_name = "stg_axiahb"; + else if (pidx == JH7110_AONCLK_APB_BUS) + parents[i].fw_name = "apb_bus"; + else if (pidx == JH7110_AONCLK_GMAC0_GTXCLK) + parents[i].fw_name = "gmac0_gtxclk"; + else if (pidx == JH7110_AONCLK_RTC_OSC) + parents[i].fw_name = "rtc_osc"; + } + + clk->hw.init = &init; + clk->idx = idx; + clk->max_div = max & JH71X0_CLK_DIV_MASK; + + ret = devm_clk_hw_register(&pdev->dev, &clk->hw); + if (ret) + return ret; + } + + ret = devm_of_clk_add_hw_provider(&pdev->dev, jh7110_aonclk_get, priv); + if (ret) + return ret; + + return jh7110_reset_controller_register(priv, "reset-aon", 1); +} + +static const struct of_device_id jh7110_aoncrg_match[] = { + { .compatible = "starfive,jh7110-aoncrg" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jh7110_aoncrg_match); + +static struct platform_driver jh7110_aoncrg_driver = { + .probe = jh7110_aoncrg_probe, + .driver = { + .name = "clk-starfive-jh7110-aon", + .of_match_table = jh7110_aoncrg_match, + }, +}; +module_platform_driver(jh7110_aoncrg_driver); + +MODULE_AUTHOR("Emil Renner Berthing"); +MODULE_DESCRIPTION("StarFive JH7110 always-on clock driver"); +MODULE_LICENSE("GPL"); From 4e1b69a2405b7630a52b5a7840c20c2fd15e3616 Mon Sep 17 00:00:00 2001 From: Hal Feng Date: Sat, 12 Nov 2022 23:39:59 +0800 Subject: [PATCH 15/95] reset: starfive: Add StarFive JH7110 reset driver Add auxiliary driver to support StarFive JH7110 system and always-on resets. Tested-by: Tommaso Merciai Signed-off-by: Hal Feng --- drivers/reset/starfive/Kconfig | 8 +++ drivers/reset/starfive/Makefile | 1 + .../reset/starfive/reset-starfive-jh7110.c | 70 +++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 drivers/reset/starfive/reset-starfive-jh7110.c diff --git a/drivers/reset/starfive/Kconfig b/drivers/reset/starfive/Kconfig index 1927a5a3b53a3..1fa706a2c3dca 100644 --- a/drivers/reset/starfive/Kconfig +++ b/drivers/reset/starfive/Kconfig @@ -10,3 +10,11 @@ config RESET_STARFIVE_JH7100 default ARCH_STARFIVE help This enables the reset controller driver for the StarFive JH7100 SoC. + +config RESET_STARFIVE_JH7110 + bool "StarFive JH7110 Reset Driver" + depends on AUXILIARY_BUS && CLK_STARFIVE_JH7110_SYS + select RESET_STARFIVE_JH71X0 + default ARCH_STARFIVE + help + This enables the reset controller driver for the StarFive JH7110 SoC. diff --git a/drivers/reset/starfive/Makefile b/drivers/reset/starfive/Makefile index f6aa12466fad9..7a44b66fb9d5d 100644 --- a/drivers/reset/starfive/Makefile +++ b/drivers/reset/starfive/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_RESET_STARFIVE_JH71X0) += reset-starfive-jh71x0.o obj-$(CONFIG_RESET_STARFIVE_JH7100) += reset-starfive-jh7100.o +obj-$(CONFIG_RESET_STARFIVE_JH7110) += reset-starfive-jh7110.o diff --git a/drivers/reset/starfive/reset-starfive-jh7110.c b/drivers/reset/starfive/reset-starfive-jh7110.c new file mode 100644 index 0000000000000..4ccb3f72ca838 --- /dev/null +++ b/drivers/reset/starfive/reset-starfive-jh7110.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Reset driver for the StarFive JH7110 SoC + * + * Copyright (C) 2022 StarFive Technology Co., Ltd. + */ + +#include + +#include "reset-starfive-jh71x0.h" + +#include + +struct jh7110_reset_info { + unsigned int nr_resets; + unsigned int assert_offset; + unsigned int status_offset; +}; + +static const struct jh7110_reset_info jh7110_sys_info = { + .nr_resets = JH7110_SYSRST_END, + .assert_offset = 0x2F8, + .status_offset = 0x308, +}; + +static const struct jh7110_reset_info jh7110_aon_info = { + .nr_resets = JH7110_AONRST_END, + .assert_offset = 0x38, + .status_offset = 0x3C, +}; + +static int jh7110_reset_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct jh7110_reset_info *info = (struct jh7110_reset_info *)(id->driver_data); + void __iomem **base = (void __iomem **)dev_get_drvdata(adev->dev.parent); + + if (!info || !base) + return -ENODEV; + + return reset_starfive_jh71x0_register(&adev->dev, adev->dev.parent->of_node, + *base + info->assert_offset, + *base + info->status_offset, + NULL, + info->nr_resets, + NULL); +} + +static const struct auxiliary_device_id jh7110_reset_ids[] = { + { + .name = "clk_starfive_jh71x0.reset-sys", + .driver_data = (kernel_ulong_t)&jh7110_sys_info, + }, + { + .name = "clk_starfive_jh71x0.reset-aon", + .driver_data = (kernel_ulong_t)&jh7110_aon_info, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(auxiliary, jh7110_reset_ids); + +static struct auxiliary_driver jh7110_reset_driver = { + .probe = jh7110_reset_probe, + .id_table = jh7110_reset_ids, +}; +module_auxiliary_driver(jh7110_reset_driver); + +MODULE_AUTHOR("Hal Feng "); +MODULE_DESCRIPTION("StarFive JH7110 reset driver"); +MODULE_LICENSE("GPL"); From 0bdb16834d56b7acb9e8e6b4d339e63b7048735a Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sun, 10 Jul 2022 22:10:44 +0200 Subject: [PATCH 16/95] dt-bindings: timer: Add StarFive JH7110 clint Add compatible string for the StarFive JH7110 clint. Reviewed-by: Conor Dooley Acked-by: Krzysztof Kozlowski Signed-off-by: Emil Renner Berthing Signed-off-by: Hal Feng --- Documentation/devicetree/bindings/timer/sifive,clint.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/timer/sifive,clint.yaml b/Documentation/devicetree/bindings/timer/sifive,clint.yaml index aada6957216c3..94bef9424df1b 100644 --- a/Documentation/devicetree/bindings/timer/sifive,clint.yaml +++ b/Documentation/devicetree/bindings/timer/sifive,clint.yaml @@ -31,6 +31,7 @@ properties: - enum: - sifive,fu540-c000-clint - starfive,jh7100-clint + - starfive,jh7110-clint - canaan,k210-clint - const: sifive,clint0 - items: From c048fd4f8afba3a260559ac11339f6804686e639 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sun, 10 Jul 2022 22:12:44 +0200 Subject: [PATCH 17/95] dt-bindings: interrupt-controller: Add StarFive JH7110 plic Add compatible string for StarFive JH7110 plic. Reviewed-by: Conor Dooley Acked-by: Krzysztof Kozlowski Signed-off-by: Emil Renner Berthing Signed-off-by: Hal Feng --- .../bindings/interrupt-controller/sifive,plic-1.0.0.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml b/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml index 63bc89e134801..4e98261f29489 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml @@ -59,6 +59,7 @@ properties: - enum: - sifive,fu540-c000-plic - starfive,jh7100-plic + - starfive,jh7110-plic - canaan,k210-plic - const: sifive,plic-1.0.0 - items: From 6c4261bef1d2fd05a175b6b8bd72fe115773b29d Mon Sep 17 00:00:00 2001 From: Hal Feng Date: Sun, 19 Feb 2023 22:47:33 +0800 Subject: [PATCH 18/95] dt-bindings: riscv: Add SiFive S7 compatible Add a new compatible string in cpu.yaml for SiFive S7 CPU core which is used on SiFive U74-MC core complex etc. Reviewed-by: Conor Dooley Acked-by: Krzysztof Kozlowski Signed-off-by: Hal Feng --- Documentation/devicetree/bindings/riscv/cpus.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/riscv/cpus.yaml b/Documentation/devicetree/bindings/riscv/cpus.yaml index 001931d526ec7..14b5b7ea0ce0f 100644 --- a/Documentation/devicetree/bindings/riscv/cpus.yaml +++ b/Documentation/devicetree/bindings/riscv/cpus.yaml @@ -35,6 +35,7 @@ properties: - sifive,e7 - sifive,e71 - sifive,rocket0 + - sifive,s7 - sifive,u5 - sifive,u54 - sifive,u7 From eb1d449e236003a3f84f0cac267a93e6dfe0097a Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sat, 9 Jul 2022 14:37:38 +0200 Subject: [PATCH 19/95] riscv: dts: starfive: Add initial StarFive JH7110 device tree Add initial device tree for the JH7110 RISC-V SoC by StarFive Technology Ltd. Tested-by: Tommaso Merciai Reviewed-by: Conor Dooley Signed-off-by: Emil Renner Berthing Co-developed-by: Jianlong Huang Signed-off-by: Jianlong Huang Co-developed-by: Hal Feng Signed-off-by: Hal Feng --- arch/riscv/boot/dts/starfive/jh7110.dtsi | 509 +++++++++++++++++++++++ 1 file changed, 509 insertions(+) create mode 100644 arch/riscv/boot/dts/starfive/jh7110.dtsi diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi new file mode 100644 index 0000000000000..d484ecdf93f73 --- /dev/null +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2022 StarFive Technology Co., Ltd. + * Copyright (C) 2022 Emil Renner Berthing + */ + +/dts-v1/; +#include +#include + +/ { + compatible = "starfive,jh7110"; + #address-cells = <2>; + #size-cells = <2>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + S7_0: cpu@0 { + compatible = "sifive,s7", "riscv"; + reg = <0>; + d-cache-block-size = <64>; + d-cache-sets = <64>; + d-cache-size = <8192>; + d-tlb-sets = <1>; + d-tlb-size = <40>; + device_type = "cpu"; + i-cache-block-size = <64>; + i-cache-sets = <64>; + i-cache-size = <16384>; + i-tlb-sets = <1>; + i-tlb-size = <40>; + mmu-type = "riscv,sv39"; + next-level-cache = <&ccache>; + riscv,isa = "rv64imac_zba_zbb"; + tlb-split; + status = "disabled"; + + cpu0_intc: interrupt-controller { + compatible = "riscv,cpu-intc"; + interrupt-controller; + #interrupt-cells = <1>; + }; + }; + + U74_1: cpu@1 { + compatible = "sifive,u74-mc", "riscv"; + reg = <1>; + d-cache-block-size = <64>; + d-cache-sets = <64>; + d-cache-size = <32768>; + d-tlb-sets = <1>; + d-tlb-size = <40>; + device_type = "cpu"; + i-cache-block-size = <64>; + i-cache-sets = <64>; + i-cache-size = <32768>; + i-tlb-sets = <1>; + i-tlb-size = <40>; + mmu-type = "riscv,sv39"; + next-level-cache = <&ccache>; + riscv,isa = "rv64imafdc_zba_zbb"; + tlb-split; + + cpu1_intc: interrupt-controller { + compatible = "riscv,cpu-intc"; + interrupt-controller; + #interrupt-cells = <1>; + }; + }; + + U74_2: cpu@2 { + compatible = "sifive,u74-mc", "riscv"; + reg = <2>; + d-cache-block-size = <64>; + d-cache-sets = <64>; + d-cache-size = <32768>; + d-tlb-sets = <1>; + d-tlb-size = <40>; + device_type = "cpu"; + i-cache-block-size = <64>; + i-cache-sets = <64>; + i-cache-size = <32768>; + i-tlb-sets = <1>; + i-tlb-size = <40>; + mmu-type = "riscv,sv39"; + next-level-cache = <&ccache>; + riscv,isa = "rv64imafdc_zba_zbb"; + tlb-split; + + cpu2_intc: interrupt-controller { + compatible = "riscv,cpu-intc"; + interrupt-controller; + #interrupt-cells = <1>; + }; + }; + + U74_3: cpu@3 { + compatible = "sifive,u74-mc", "riscv"; + reg = <3>; + d-cache-block-size = <64>; + d-cache-sets = <64>; + d-cache-size = <32768>; + d-tlb-sets = <1>; + d-tlb-size = <40>; + device_type = "cpu"; + i-cache-block-size = <64>; + i-cache-sets = <64>; + i-cache-size = <32768>; + i-tlb-sets = <1>; + i-tlb-size = <40>; + mmu-type = "riscv,sv39"; + next-level-cache = <&ccache>; + riscv,isa = "rv64imafdc_zba_zbb"; + tlb-split; + + cpu3_intc: interrupt-controller { + compatible = "riscv,cpu-intc"; + interrupt-controller; + #interrupt-cells = <1>; + }; + }; + + U74_4: cpu@4 { + compatible = "sifive,u74-mc", "riscv"; + reg = <4>; + d-cache-block-size = <64>; + d-cache-sets = <64>; + d-cache-size = <32768>; + d-tlb-sets = <1>; + d-tlb-size = <40>; + device_type = "cpu"; + i-cache-block-size = <64>; + i-cache-sets = <64>; + i-cache-size = <32768>; + i-tlb-sets = <1>; + i-tlb-size = <40>; + mmu-type = "riscv,sv39"; + next-level-cache = <&ccache>; + riscv,isa = "rv64imafdc_zba_zbb"; + tlb-split; + + cpu4_intc: interrupt-controller { + compatible = "riscv,cpu-intc"; + interrupt-controller; + #interrupt-cells = <1>; + }; + }; + + cpu-map { + cluster0 { + core0 { + cpu = <&S7_0>; + }; + + core1 { + cpu = <&U74_1>; + }; + + core2 { + cpu = <&U74_2>; + }; + + core3 { + cpu = <&U74_3>; + }; + + core4 { + cpu = <&U74_4>; + }; + }; + }; + }; + + gmac0_rgmii_rxin: gmac0-rgmii-rxin-clock { + compatible = "fixed-clock"; + clock-output-names = "gmac0_rgmii_rxin"; + #clock-cells = <0>; + }; + + gmac0_rmii_refin: gmac0-rmii-refin-clock { + compatible = "fixed-clock"; + clock-output-names = "gmac0_rmii_refin"; + #clock-cells = <0>; + }; + + gmac1_rgmii_rxin: gmac1-rgmii-rxin-clock { + compatible = "fixed-clock"; + clock-output-names = "gmac1_rgmii_rxin"; + #clock-cells = <0>; + }; + + gmac1_rmii_refin: gmac1-rmii-refin-clock { + compatible = "fixed-clock"; + clock-output-names = "gmac1_rmii_refin"; + #clock-cells = <0>; + }; + + i2srx_bclk_ext: i2srx-bclk-ext-clock { + compatible = "fixed-clock"; + clock-output-names = "i2srx_bclk_ext"; + #clock-cells = <0>; + }; + + i2srx_lrck_ext: i2srx-lrck-ext-clock { + compatible = "fixed-clock"; + clock-output-names = "i2srx_lrck_ext"; + #clock-cells = <0>; + }; + + i2stx_bclk_ext: i2stx-bclk-ext-clock { + compatible = "fixed-clock"; + clock-output-names = "i2stx_bclk_ext"; + #clock-cells = <0>; + }; + + i2stx_lrck_ext: i2stx-lrck-ext-clock { + compatible = "fixed-clock"; + clock-output-names = "i2stx_lrck_ext"; + #clock-cells = <0>; + }; + + mclk_ext: mclk-ext-clock { + compatible = "fixed-clock"; + clock-output-names = "mclk_ext"; + #clock-cells = <0>; + }; + + osc: oscillator { + compatible = "fixed-clock"; + clock-output-names = "osc"; + #clock-cells = <0>; + }; + + rtc_osc: rtc-oscillator { + compatible = "fixed-clock"; + clock-output-names = "rtc_osc"; + #clock-cells = <0>; + }; + + tdm_ext: tdm-ext-clock { + compatible = "fixed-clock"; + clock-output-names = "tdm_ext"; + #clock-cells = <0>; + }; + + soc { + compatible = "simple-bus"; + interrupt-parent = <&plic>; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + clint: timer@2000000 { + compatible = "starfive,jh7110-clint", "sifive,clint0"; + reg = <0x0 0x2000000 0x0 0x10000>; + interrupts-extended = <&cpu0_intc 3>, <&cpu0_intc 7>, + <&cpu1_intc 3>, <&cpu1_intc 7>, + <&cpu2_intc 3>, <&cpu2_intc 7>, + <&cpu3_intc 3>, <&cpu3_intc 7>, + <&cpu4_intc 3>, <&cpu4_intc 7>; + }; + + ccache: cache-controller@2010000 { + compatible = "starfive,jh7110-ccache", "sifive,ccache0", "cache"; + reg = <0x0 0x2010000 0x0 0x4000>; + interrupts = <1>, <3>, <4>, <2>; + cache-block-size = <64>; + cache-level = <2>; + cache-sets = <2048>; + cache-size = <2097152>; + cache-unified; + }; + + plic: interrupt-controller@c000000 { + compatible = "starfive,jh7110-plic", "sifive,plic-1.0.0"; + reg = <0x0 0xc000000 0x0 0x4000000>; + interrupts-extended = <&cpu0_intc 11>, + <&cpu1_intc 11>, <&cpu1_intc 9>, + <&cpu2_intc 11>, <&cpu2_intc 9>, + <&cpu3_intc 11>, <&cpu3_intc 9>, + <&cpu4_intc 11>, <&cpu4_intc 9>; + interrupt-controller; + #interrupt-cells = <1>; + #address-cells = <0>; + riscv,ndev = <136>; + }; + + uart0: serial@10000000 { + compatible = "snps,dw-apb-uart"; + reg = <0x0 0x10000000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_UART0_CORE>, + <&syscrg JH7110_SYSCLK_UART0_APB>; + clock-names = "baudclk", "apb_pclk"; + resets = <&syscrg JH7110_SYSRST_UART0_APB>; + interrupts = <32>; + reg-io-width = <4>; + reg-shift = <2>; + status = "disabled"; + }; + + uart1: serial@10010000 { + compatible = "snps,dw-apb-uart"; + reg = <0x0 0x10010000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_UART1_CORE>, + <&syscrg JH7110_SYSCLK_UART1_APB>; + clock-names = "baudclk", "apb_pclk"; + resets = <&syscrg JH7110_SYSRST_UART1_APB>; + interrupts = <33>; + reg-io-width = <4>; + reg-shift = <2>; + status = "disabled"; + }; + + uart2: serial@10020000 { + compatible = "snps,dw-apb-uart"; + reg = <0x0 0x10020000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_UART2_CORE>, + <&syscrg JH7110_SYSCLK_UART2_APB>; + clock-names = "baudclk", "apb_pclk"; + resets = <&syscrg JH7110_SYSRST_UART2_APB>; + interrupts = <34>; + reg-io-width = <4>; + reg-shift = <2>; + status = "disabled"; + }; + + i2c0: i2c@10030000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x10030000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_I2C0_APB>; + clock-names = "ref"; + resets = <&syscrg JH7110_SYSRST_I2C0_APB>; + interrupts = <35>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c1: i2c@10040000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x10040000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_I2C1_APB>; + clock-names = "ref"; + resets = <&syscrg JH7110_SYSRST_I2C1_APB>; + interrupts = <36>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c2: i2c@10050000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x10050000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_I2C2_APB>; + clock-names = "ref"; + resets = <&syscrg JH7110_SYSRST_I2C2_APB>; + interrupts = <37>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + uart3: serial@12000000 { + compatible = "snps,dw-apb-uart"; + reg = <0x0 0x12000000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_UART3_CORE>, + <&syscrg JH7110_SYSCLK_UART3_APB>; + clock-names = "baudclk", "apb_pclk"; + resets = <&syscrg JH7110_SYSRST_UART3_APB>; + interrupts = <45>; + reg-io-width = <4>; + reg-shift = <2>; + status = "disabled"; + }; + + uart4: serial@12010000 { + compatible = "snps,dw-apb-uart"; + reg = <0x0 0x12010000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_UART4_CORE>, + <&syscrg JH7110_SYSCLK_UART4_APB>; + clock-names = "baudclk", "apb_pclk"; + resets = <&syscrg JH7110_SYSRST_UART4_APB>; + interrupts = <46>; + reg-io-width = <4>; + reg-shift = <2>; + status = "disabled"; + }; + + uart5: serial@12020000 { + compatible = "snps,dw-apb-uart"; + reg = <0x0 0x12020000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_UART5_CORE>, + <&syscrg JH7110_SYSCLK_UART5_APB>; + clock-names = "baudclk", "apb_pclk"; + resets = <&syscrg JH7110_SYSRST_UART5_APB>; + interrupts = <47>; + reg-io-width = <4>; + reg-shift = <2>; + status = "disabled"; + }; + + i2c3: i2c@12030000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x12030000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_I2C3_APB>; + clock-names = "ref"; + resets = <&syscrg JH7110_SYSRST_I2C3_APB>; + interrupts = <48>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c4: i2c@12040000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x12040000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_I2C4_APB>; + clock-names = "ref"; + resets = <&syscrg JH7110_SYSRST_I2C4_APB>; + interrupts = <49>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c5: i2c@12050000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x12050000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_I2C5_APB>; + clock-names = "ref"; + resets = <&syscrg JH7110_SYSRST_I2C5_APB>; + interrupts = <50>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c6: i2c@12060000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x12060000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_I2C6_APB>; + clock-names = "ref"; + resets = <&syscrg JH7110_SYSRST_I2C6_APB>; + interrupts = <51>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + syscrg: clock-controller@13020000 { + compatible = "starfive,jh7110-syscrg"; + reg = <0x0 0x13020000 0x0 0x10000>; + clocks = <&osc>, <&gmac1_rmii_refin>, + <&gmac1_rgmii_rxin>, + <&i2stx_bclk_ext>, <&i2stx_lrck_ext>, + <&i2srx_bclk_ext>, <&i2srx_lrck_ext>, + <&tdm_ext>, <&mclk_ext>; + clock-names = "osc", "gmac1_rmii_refin", + "gmac1_rgmii_rxin", + "i2stx_bclk_ext", "i2stx_lrck_ext", + "i2srx_bclk_ext", "i2srx_lrck_ext", + "tdm_ext", "mclk_ext"; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + sysgpio: pinctrl@13040000 { + compatible = "starfive,jh7110-sys-pinctrl"; + reg = <0x0 0x13040000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_IOMUX_APB>; + resets = <&syscrg JH7110_SYSRST_IOMUX_APB>; + interrupts = <86>; + interrupt-controller; + #interrupt-cells = <2>; + gpio-controller; + #gpio-cells = <2>; + }; + + aoncrg: clock-controller@17000000 { + compatible = "starfive,jh7110-aoncrg"; + reg = <0x0 0x17000000 0x0 0x10000>; + clocks = <&osc>, <&gmac0_rmii_refin>, + <&gmac0_rgmii_rxin>, + <&syscrg JH7110_SYSCLK_STG_AXIAHB>, + <&syscrg JH7110_SYSCLK_APB_BUS>, + <&syscrg JH7110_SYSCLK_GMAC0_GTXCLK>, + <&rtc_osc>; + clock-names = "osc", "gmac0_rmii_refin", + "gmac0_rgmii_rxin", "stg_axiahb", + "apb_bus", "gmac0_gtxclk", + "rtc_osc"; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + aongpio: pinctrl@17020000 { + compatible = "starfive,jh7110-aon-pinctrl"; + reg = <0x0 0x17020000 0x0 0x10000>; + resets = <&aoncrg JH7110_AONRST_IOMUX>; + interrupts = <85>; + interrupt-controller; + #interrupt-cells = <2>; + gpio-controller; + #gpio-cells = <2>; + }; + }; +}; From 66e0703a8db5af10660a1fffdf4fe41cf6f8ec6b Mon Sep 17 00:00:00 2001 From: Jianlong Huang Date: Fri, 9 Sep 2022 09:41:38 +0800 Subject: [PATCH 20/95] riscv: dts: starfive: Add StarFive JH7110 pin function definitions Add pin function definitions for StarFive JH7110 SoC. Tested-by: Tommaso Merciai Co-developed-by: Emil Renner Berthing Signed-off-by: Emil Renner Berthing Signed-off-by: Jianlong Huang Signed-off-by: Hal Feng --- arch/riscv/boot/dts/starfive/jh7110-pinfunc.h | 308 ++++++++++++++++++ 1 file changed, 308 insertions(+) create mode 100644 arch/riscv/boot/dts/starfive/jh7110-pinfunc.h diff --git a/arch/riscv/boot/dts/starfive/jh7110-pinfunc.h b/arch/riscv/boot/dts/starfive/jh7110-pinfunc.h new file mode 100644 index 0000000000000..fb0139b56723a --- /dev/null +++ b/arch/riscv/boot/dts/starfive/jh7110-pinfunc.h @@ -0,0 +1,308 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright (C) 2022 Emil Renner Berthing + * Copyright (C) 2022 StarFive Technology Co., Ltd. + */ + +#ifndef __JH7110_PINFUNC_H__ +#define __JH7110_PINFUNC_H__ + +/* + * mux bits: + * | 31 - 24 | 23 - 16 | 15 - 10 | 9 - 8 | 7 - 0 | + * | din | dout | doen | function | gpio nr | + * + * dout: output signal + * doen: output enable signal + * din: optional input signal, 0xff = none + * function: function selector + * gpio nr: gpio number, 0 - 63 + */ +#define GPIOMUX(n, dout, doen, din) ( \ + (((din) & 0xff) << 24) | \ + (((dout) & 0xff) << 16) | \ + (((doen) & 0x3f) << 10) | \ + ((n) & 0x3f)) + +#define PINMUX(n, func) ((1 << 10) | (((func) & 0x3) << 8) | ((n) & 0xff)) + +/* sys_iomux dout */ +#define GPOUT_LOW 0 +#define GPOUT_HIGH 1 +#define GPOUT_SYS_WAVE511_UART_TX 2 +#define GPOUT_SYS_CAN0_STBY 3 +#define GPOUT_SYS_CAN0_TST_NEXT_BIT 4 +#define GPOUT_SYS_CAN0_TST_SAMPLE_POINT 5 +#define GPOUT_SYS_CAN0_TXD 6 +#define GPOUT_SYS_USB_DRIVE_VBUS 7 +#define GPOUT_SYS_QSPI_CS1 8 +#define GPOUT_SYS_SPDIF 9 +#define GPOUT_SYS_HDMI_CEC_SDA 10 +#define GPOUT_SYS_HDMI_DDC_SCL 11 +#define GPOUT_SYS_HDMI_DDC_SDA 12 +#define GPOUT_SYS_WATCHDOG 13 +#define GPOUT_SYS_I2C0_CLK 14 +#define GPOUT_SYS_I2C0_DATA 15 +#define GPOUT_SYS_SDIO0_BACK_END_POWER 16 +#define GPOUT_SYS_SDIO0_CARD_POWER_EN 17 +#define GPOUT_SYS_SDIO0_CCMD_OD_PULLUP_EN 18 +#define GPOUT_SYS_SDIO0_RST 19 +#define GPOUT_SYS_UART0_TX 20 +#define GPOUT_SYS_HIFI4_JTAG_TDO 21 +#define GPOUT_SYS_JTAG_TDO 22 +#define GPOUT_SYS_PDM_MCLK 23 +#define GPOUT_SYS_PWM_CHANNEL0 24 +#define GPOUT_SYS_PWM_CHANNEL1 25 +#define GPOUT_SYS_PWM_CHANNEL2 26 +#define GPOUT_SYS_PWM_CHANNEL3 27 +#define GPOUT_SYS_PWMDAC_LEFT 28 +#define GPOUT_SYS_PWMDAC_RIGHT 29 +#define GPOUT_SYS_SPI0_CLK 30 +#define GPOUT_SYS_SPI0_FSS 31 +#define GPOUT_SYS_SPI0_TXD 32 +#define GPOUT_SYS_GMAC_PHYCLK 33 +#define GPOUT_SYS_I2SRX_BCLK 34 +#define GPOUT_SYS_I2SRX_LRCK 35 +#define GPOUT_SYS_I2STX0_BCLK 36 +#define GPOUT_SYS_I2STX0_LRCK 37 +#define GPOUT_SYS_MCLK 38 +#define GPOUT_SYS_TDM_CLK 39 +#define GPOUT_SYS_TDM_SYNC 40 +#define GPOUT_SYS_TDM_TXD 41 +#define GPOUT_SYS_TRACE_DATA0 42 +#define GPOUT_SYS_TRACE_DATA1 43 +#define GPOUT_SYS_TRACE_DATA2 44 +#define GPOUT_SYS_TRACE_DATA3 45 +#define GPOUT_SYS_TRACE_REF 46 +#define GPOUT_SYS_CAN1_STBY 47 +#define GPOUT_SYS_CAN1_TST_NEXT_BIT 48 +#define GPOUT_SYS_CAN1_TST_SAMPLE_POINT 49 +#define GPOUT_SYS_CAN1_TXD 50 +#define GPOUT_SYS_I2C1_CLK 51 +#define GPOUT_SYS_I2C1_DATA 52 +#define GPOUT_SYS_SDIO1_BACK_END_POWER 53 +#define GPOUT_SYS_SDIO1_CARD_POWER_EN 54 +#define GPOUT_SYS_SDIO1_CLK 55 +#define GPOUT_SYS_SDIO1_CMD_OD_PULLUP_EN 56 +#define GPOUT_SYS_SDIO1_CMD 57 +#define GPOUT_SYS_SDIO1_DATA0 58 +#define GPOUT_SYS_SDIO1_DATA1 59 +#define GPOUT_SYS_SDIO1_DATA2 60 +#define GPOUT_SYS_SDIO1_DATA3 61 +#define GPOUT_SYS_SDIO1_DATA4 63 +#define GPOUT_SYS_SDIO1_DATA5 63 +#define GPOUT_SYS_SDIO1_DATA6 64 +#define GPOUT_SYS_SDIO1_DATA7 65 +#define GPOUT_SYS_SDIO1_RST 66 +#define GPOUT_SYS_UART1_RTS 67 +#define GPOUT_SYS_UART1_TX 68 +#define GPOUT_SYS_I2STX1_SDO0 69 +#define GPOUT_SYS_I2STX1_SDO1 70 +#define GPOUT_SYS_I2STX1_SDO2 71 +#define GPOUT_SYS_I2STX1_SDO3 72 +#define GPOUT_SYS_SPI1_CLK 73 +#define GPOUT_SYS_SPI1_FSS 74 +#define GPOUT_SYS_SPI1_TXD 75 +#define GPOUT_SYS_I2C2_CLK 76 +#define GPOUT_SYS_I2C2_DATA 77 +#define GPOUT_SYS_UART2_RTS 78 +#define GPOUT_SYS_UART2_TX 79 +#define GPOUT_SYS_SPI2_CLK 80 +#define GPOUT_SYS_SPI2_FSS 81 +#define GPOUT_SYS_SPI2_TXD 82 +#define GPOUT_SYS_I2C3_CLK 83 +#define GPOUT_SYS_I2C3_DATA 84 +#define GPOUT_SYS_UART3_TX 85 +#define GPOUT_SYS_SPI3_CLK 86 +#define GPOUT_SYS_SPI3_FSS 87 +#define GPOUT_SYS_SPI3_TXD 88 +#define GPOUT_SYS_I2C4_CLK 89 +#define GPOUT_SYS_I2C4_DATA 90 +#define GPOUT_SYS_UART4_RTS 91 +#define GPOUT_SYS_UART4_TX 92 +#define GPOUT_SYS_SPI4_CLK 93 +#define GPOUT_SYS_SPI4_FSS 94 +#define GPOUT_SYS_SPI4_TXD 95 +#define GPOUT_SYS_I2C5_CLK 96 +#define GPOUT_SYS_I2C5_DATA 97 +#define GPOUT_SYS_UART5_RTS 98 +#define GPOUT_SYS_UART5_TX 99 +#define GPOUT_SYS_SPI5_CLK 100 +#define GPOUT_SYS_SPI5_FSS 101 +#define GPOUT_SYS_SPI5_TXD 102 +#define GPOUT_SYS_I2C6_CLK 103 +#define GPOUT_SYS_I2C6_DATA 104 +#define GPOUT_SYS_SPI6_CLK 105 +#define GPOUT_SYS_SPI6_FSS 106 +#define GPOUT_SYS_SPI6_TXD 107 + +/* aon_iomux dout */ +#define GPOUT_AON_CLK_32K_OUT 2 +#define GPOUT_AON_PTC0_PWM4 3 +#define GPOUT_AON_PTC0_PWM5 4 +#define GPOUT_AON_PTC0_PWM6 5 +#define GPOUT_AON_PTC0_PWM7 6 +#define GPOUT_AON_CLK_GCLK0 7 +#define GPOUT_AON_CLK_GCLK1 8 +#define GPOUT_AON_CLK_GCLK2 9 + +/* sys_iomux doen */ +#define GPOEN_ENABLE 0 +#define GPOEN_DISABLE 1 +#define GPOEN_SYS_HDMI_CEC_SDA 2 +#define GPOEN_SYS_HDMI_DDC_SCL 3 +#define GPOEN_SYS_HDMI_DDC_SDA 4 +#define GPOEN_SYS_I2C0_CLK 5 +#define GPOEN_SYS_I2C0_DATA 6 +#define GPOEN_SYS_HIFI4_JTAG_TDO 7 +#define GPOEN_SYS_JTAG_TDO 8 +#define GPOEN_SYS_PWM0_CHANNEL0 9 +#define GPOEN_SYS_PWM0_CHANNEL1 10 +#define GPOEN_SYS_PWM0_CHANNEL2 11 +#define GPOEN_SYS_PWM0_CHANNEL3 12 +#define GPOEN_SYS_SPI0_NSSPCTL 13 +#define GPOEN_SYS_SPI0_NSSP 14 +#define GPOEN_SYS_TDM_SYNC 15 +#define GPOEN_SYS_TDM_TXD 16 +#define GPOEN_SYS_I2C1_CLK 17 +#define GPOEN_SYS_I2C1_DATA 18 +#define GPOEN_SYS_SDIO1_CMD 19 +#define GPOEN_SYS_SDIO1_DATA0 20 +#define GPOEN_SYS_SDIO1_DATA1 21 +#define GPOEN_SYS_SDIO1_DATA2 22 +#define GPOEN_SYS_SDIO1_DATA3 23 +#define GPOEN_SYS_SDIO1_DATA4 24 +#define GPOEN_SYS_SDIO1_DATA5 25 +#define GPOEN_SYS_SDIO1_DATA6 26 +#define GPOEN_SYS_SDIO1_DATA7 27 +#define GPOEN_SYS_SPI1_NSSPCTL 28 +#define GPOEN_SYS_SPI1_NSSP 29 +#define GPOEN_SYS_I2C2_CLK 30 +#define GPOEN_SYS_I2C2_DATA 31 +#define GPOEN_SYS_SPI2_NSSPCTL 32 +#define GPOEN_SYS_SPI2_NSSP 33 +#define GPOEN_SYS_I2C3_CLK 34 +#define GPOEN_SYS_I2C3_DATA 35 +#define GPOEN_SYS_SPI3_NSSPCTL 36 +#define GPOEN_SYS_SPI3_NSSP 37 +#define GPOEN_SYS_I2C4_CLK 38 +#define GPOEN_SYS_I2C4_DATA 39 +#define GPOEN_SYS_SPI4_NSSPCTL 40 +#define GPOEN_SYS_SPI4_NSSP 41 +#define GPOEN_SYS_I2C5_CLK 42 +#define GPOEN_SYS_I2C5_DATA 43 +#define GPOEN_SYS_SPI5_NSSPCTL 44 +#define GPOEN_SYS_SPI5_NSSP 45 +#define GPOEN_SYS_I2C6_CLK 46 +#define GPOEN_SYS_I2C6_DATA 47 +#define GPOEN_SYS_SPI6_NSSPCTL 48 +#define GPOEN_SYS_SPI6_NSSP 49 + +/* aon_iomux doen */ +#define GPOEN_AON_PTC0_OE_N_4 2 +#define GPOEN_AON_PTC0_OE_N_5 3 +#define GPOEN_AON_PTC0_OE_N_6 4 +#define GPOEN_AON_PTC0_OE_N_7 5 + +/* sys_iomux gin */ +#define GPI_NONE 255 + +#define GPI_SYS_WAVE511_UART_RX 0 +#define GPI_SYS_CAN0_RXD 1 +#define GPI_SYS_USB_OVERCURRENT 2 +#define GPI_SYS_SPDIF 3 +#define GPI_SYS_JTAG_RST 4 +#define GPI_SYS_HDMI_CEC_SDA 5 +#define GPI_SYS_HDMI_DDC_SCL 6 +#define GPI_SYS_HDMI_DDC_SDA 7 +#define GPI_SYS_HDMI_HPD 8 +#define GPI_SYS_I2C0_CLK 9 +#define GPI_SYS_I2C0_DATA 10 +#define GPI_SYS_SDIO0_CD 11 +#define GPI_SYS_SDIO0_INT 12 +#define GPI_SYS_SDIO0_WP 13 +#define GPI_SYS_UART0_RX 14 +#define GPI_SYS_HIFI4_JTAG_TCK 15 +#define GPI_SYS_HIFI4_JTAG_TDI 16 +#define GPI_SYS_HIFI4_JTAG_TMS 17 +#define GPI_SYS_HIFI4_JTAG_RST 18 +#define GPI_SYS_JTAG_TDI 19 +#define GPI_SYS_JTAG_TMS 20 +#define GPI_SYS_PDM_DMIC0 21 +#define GPI_SYS_PDM_DMIC1 22 +#define GPI_SYS_I2SRX_SDIN0 23 +#define GPI_SYS_I2SRX_SDIN1 24 +#define GPI_SYS_I2SRX_SDIN2 25 +#define GPI_SYS_SPI0_CLK 26 +#define GPI_SYS_SPI0_FSS 27 +#define GPI_SYS_SPI0_RXD 28 +#define GPI_SYS_JTAG_TCK 29 +#define GPI_SYS_MCLK_EXT 30 +#define GPI_SYS_I2SRX_BCLK 31 +#define GPI_SYS_I2SRX_LRCK 32 +#define GPI_SYS_I2STX0_BCLK 33 +#define GPI_SYS_I2STX0_LRCK 34 +#define GPI_SYS_TDM_CLK 35 +#define GPI_SYS_TDM_RXD 36 +#define GPI_SYS_TDM_SYNC 37 +#define GPI_SYS_CAN1_RXD 38 +#define GPI_SYS_I2C1_CLK 39 +#define GPI_SYS_I2C1_DATA 40 +#define GPI_SYS_SDIO1_CD 41 +#define GPI_SYS_SDIO1_INT 42 +#define GPI_SYS_SDIO1_WP 43 +#define GPI_SYS_SDIO1_CMD 44 +#define GPI_SYS_SDIO1_DATA0 45 +#define GPI_SYS_SDIO1_DATA1 46 +#define GPI_SYS_SDIO1_DATA2 47 +#define GPI_SYS_SDIO1_DATA3 48 +#define GPI_SYS_SDIO1_DATA4 49 +#define GPI_SYS_SDIO1_DATA5 50 +#define GPI_SYS_SDIO1_DATA6 51 +#define GPI_SYS_SDIO1_DATA7 52 +#define GPI_SYS_SDIO1_STRB 53 +#define GPI_SYS_UART1_CTS 54 +#define GPI_SYS_UART1_RX 55 +#define GPI_SYS_SPI1_CLK 56 +#define GPI_SYS_SPI1_FSS 57 +#define GPI_SYS_SPI1_RXD 58 +#define GPI_SYS_I2C2_CLK 59 +#define GPI_SYS_I2C2_DATA 60 +#define GPI_SYS_UART2_CTS 61 +#define GPI_SYS_UART2_RX 62 +#define GPI_SYS_SPI2_CLK 63 +#define GPI_SYS_SPI2_FSS 64 +#define GPI_SYS_SPI2_RXD 65 +#define GPI_SYS_I2C3_CLK 66 +#define GPI_SYS_I2C3_DATA 67 +#define GPI_SYS_UART3_RX 68 +#define GPI_SYS_SPI3_CLK 69 +#define GPI_SYS_SPI3_FSS 70 +#define GPI_SYS_SPI3_RXD 71 +#define GPI_SYS_I2C4_CLK 72 +#define GPI_SYS_I2C4_DATA 73 +#define GPI_SYS_UART4_CTS 74 +#define GPI_SYS_UART4_RX 75 +#define GPI_SYS_SPI4_CLK 76 +#define GPI_SYS_SPI4_FSS 77 +#define GPI_SYS_SPI4_RXD 78 +#define GPI_SYS_I2C5_CLK 79 +#define GPI_SYS_I2C5_DATA 80 +#define GPI_SYS_UART5_CTS 81 +#define GPI_SYS_UART5_RX 82 +#define GPI_SYS_SPI5_CLK 83 +#define GPI_SYS_SPI5_FSS 84 +#define GPI_SYS_SPI5_RXD 85 +#define GPI_SYS_I2C6_CLK 86 +#define GPI_SYS_I2C6_DATA 87 +#define GPI_SYS_SPI6_CLK 88 +#define GPI_SYS_SPI6_FSS 89 +#define GPI_SYS_SPI6_RXD 90 + +/* aon_iomux gin */ +#define GPI_AON_PMU_GPIO_WAKEUP_0 0 +#define GPI_AON_PMU_GPIO_WAKEUP_1 1 +#define GPI_AON_PMU_GPIO_WAKEUP_2 2 +#define GPI_AON_PMU_GPIO_WAKEUP_3 3 + +#endif From e280d0c18b0d7203deac44d432a22873ad5fb222 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sat, 9 Jul 2022 14:37:38 +0200 Subject: [PATCH 21/95] riscv: dts: starfive: Add StarFive JH7110 VisionFive 2 board device tree Add a minimal device tree for StarFive JH7110 VisionFive 2 board which has version A and version B. Support booting and basic clock/reset/pinctrl/uart drivers. Tested-by: Tommaso Merciai Reviewed-by: Conor Dooley Acked-by: Conor Dooley Signed-off-by: Emil Renner Berthing Co-developed-by: Jianlong Huang Signed-off-by: Jianlong Huang Co-developed-by: Hal Feng Signed-off-by: Hal Feng --- arch/riscv/boot/dts/starfive/Makefile | 6 +- .../jh7110-starfive-visionfive-2-v1.2a.dts | 13 ++ .../jh7110-starfive-visionfive-2-v1.3b.dts | 13 ++ .../jh7110-starfive-visionfive-2.dtsi | 215 ++++++++++++++++++ 4 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.2a.dts create mode 100644 arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.3b.dts create mode 100644 arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi diff --git a/arch/riscv/boot/dts/starfive/Makefile b/arch/riscv/boot/dts/starfive/Makefile index 7b00a48580ca0..170956846d494 100644 --- a/arch/riscv/boot/dts/starfive/Makefile +++ b/arch/riscv/boot/dts/starfive/Makefile @@ -1,2 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-beaglev-starlight.dtb jh7100-starfive-visionfive-v1.dtb +dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-beaglev-starlight.dtb +dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-starfive-visionfive-v1.dtb + +dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.2a.dtb +dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.3b.dtb diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.2a.dts b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.2a.dts new file mode 100644 index 0000000000000..4af3300f3cf31 --- /dev/null +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.2a.dts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2022 StarFive Technology Co., Ltd. + * Copyright (C) 2022 Emil Renner Berthing + */ + +/dts-v1/; +#include "jh7110-starfive-visionfive-2.dtsi" + +/ { + model = "StarFive VisionFive 2 v1.2A"; + compatible = "starfive,visionfive-2-v1.2a", "starfive,jh7110"; +}; diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.3b.dts b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.3b.dts new file mode 100644 index 0000000000000..9230cc3d89466 --- /dev/null +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.3b.dts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2022 StarFive Technology Co., Ltd. + * Copyright (C) 2022 Emil Renner Berthing + */ + +/dts-v1/; +#include "jh7110-starfive-visionfive-2.dtsi" + +/ { + model = "StarFive VisionFive 2 v1.3B"; + compatible = "starfive,visionfive-2-v1.3b", "starfive,jh7110"; +}; diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi new file mode 100644 index 0000000000000..2a6d81609284c --- /dev/null +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2022 StarFive Technology Co., Ltd. + * Copyright (C) 2022 Emil Renner Berthing + */ + +/dts-v1/; +#include "jh7110.dtsi" +#include "jh7110-pinfunc.h" +#include + +/ { + aliases { + i2c0 = &i2c0; + i2c2 = &i2c2; + i2c5 = &i2c5; + i2c6 = &i2c6; + serial0 = &uart0; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + cpus { + timebase-frequency = <4000000>; + }; + + memory@40000000 { + device_type = "memory"; + reg = <0x0 0x40000000 0x1 0x0>; + }; + + gpio-restart { + compatible = "gpio-restart"; + gpios = <&sysgpio 35 GPIO_ACTIVE_HIGH>; + priority = <224>; + }; +}; + +&gmac0_rgmii_rxin { + clock-frequency = <125000000>; +}; + +&gmac0_rmii_refin { + clock-frequency = <50000000>; +}; + +&gmac1_rgmii_rxin { + clock-frequency = <125000000>; +}; + +&gmac1_rmii_refin { + clock-frequency = <50000000>; +}; + +&i2srx_bclk_ext { + clock-frequency = <12288000>; +}; + +&i2srx_lrck_ext { + clock-frequency = <192000>; +}; + +&i2stx_bclk_ext { + clock-frequency = <12288000>; +}; + +&i2stx_lrck_ext { + clock-frequency = <192000>; +}; + +&mclk_ext { + clock-frequency = <12288000>; +}; + +&osc { + clock-frequency = <24000000>; +}; + +&rtc_osc { + clock-frequency = <32768>; +}; + +&tdm_ext { + clock-frequency = <49152000>; +}; + +&i2c0 { + clock-frequency = <100000>; + i2c-sda-hold-time-ns = <300>; + i2c-sda-falling-time-ns = <510>; + i2c-scl-falling-time-ns = <510>; + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins>; + status = "okay"; +}; + +&i2c2 { + clock-frequency = <100000>; + i2c-sda-hold-time-ns = <300>; + i2c-sda-falling-time-ns = <510>; + i2c-scl-falling-time-ns = <510>; + pinctrl-names = "default"; + pinctrl-0 = <&i2c2_pins>; + status = "okay"; +}; + +&i2c5 { + clock-frequency = <100000>; + i2c-sda-hold-time-ns = <300>; + i2c-sda-falling-time-ns = <510>; + i2c-scl-falling-time-ns = <510>; + pinctrl-names = "default"; + pinctrl-0 = <&i2c5_pins>; + status = "okay"; +}; + +&i2c6 { + clock-frequency = <100000>; + i2c-sda-hold-time-ns = <300>; + i2c-sda-falling-time-ns = <510>; + i2c-scl-falling-time-ns = <510>; + pinctrl-names = "default"; + pinctrl-0 = <&i2c6_pins>; + status = "okay"; +}; + +&sysgpio { + i2c0_pins: i2c0-0 { + i2c-pins { + pinmux = , + ; + bias-disable; /* external pull-up */ + input-enable; + input-schmitt-enable; + }; + }; + + i2c2_pins: i2c2-0 { + i2c-pins { + pinmux = , + ; + bias-disable; /* external pull-up */ + input-enable; + input-schmitt-enable; + }; + }; + + i2c5_pins: i2c5-0 { + i2c-pins { + pinmux = , + ; + bias-disable; /* external pull-up */ + input-enable; + input-schmitt-enable; + }; + }; + + i2c6_pins: i2c6-0 { + i2c-pins { + pinmux = , + ; + bias-disable; /* external pull-up */ + input-enable; + input-schmitt-enable; + }; + }; + + uart0_pins: uart0-0 { + tx-pins { + pinmux = ; + bias-disable; + drive-strength = <12>; + input-disable; + input-schmitt-disable; + slew-rate = <0>; + }; + + rx-pins { + pinmux = ; + bias-disable; /* external pull-up */ + drive-strength = <2>; + input-enable; + input-schmitt-enable; + slew-rate = <0>; + }; + }; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pins>; + status = "okay"; +}; From d0f1483e0ff273c2ac7811fa4970e371e49954f1 Mon Sep 17 00:00:00 2001 From: Walker Chen Date: Mon, 16 Jan 2023 15:14:52 +0800 Subject: [PATCH 22/95] riscv: dts: starfive: add pmu controller node Add the pmu controller node for the Starfive JH7110 SoC. The PMU needs to be used by other modules such as VPU, ISP, etc. Signed-off-by: Walker Chen --- arch/riscv/boot/dts/starfive/jh7110.dtsi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi index d484ecdf93f73..417f7f51c219f 100644 --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi @@ -505,5 +505,12 @@ gpio-controller; #gpio-cells = <2>; }; + + pwrc: power-controller@17030000 { + compatible = "starfive,jh7110-pmu"; + reg = <0x0 0x17030000 0x0 0x10000>; + interrupts = <111>; + #power-domain-cells = <1>; + }; }; }; From 3748bac63c39a1eb7e4b0ca5cab059e6758990dc Mon Sep 17 00:00:00 2001 From: William Qiu Date: Wed, 15 Mar 2023 13:58:12 +0800 Subject: [PATCH 23/95] dt-bindings: soc: starfive: Add StarFive syscon doc Add documentation to describe StarFive System Controller Registers. Signed-off-by: William Qiu Reviewed-by: Conor Dooley --- .../soc/starfive/starfive,jh7110-syscon.yaml | 41 +++++++++++++++++++ MAINTAINERS | 5 +++ 2 files changed, 46 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml diff --git a/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml b/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml new file mode 100644 index 0000000000000..ae7f1d6916af4 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/starfive/starfive,jh7110-syscon.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 SoC system controller + +maintainers: + - William Qiu + +description: | + The StarFive JH7110 SoC system controller provides register information such + as offset, mask and shift to configure related modules such as MMC and PCIe. + +properties: + compatible: + items: + - enum: + - starfive,jh7110-aon-syscon + - starfive,jh7110-stg-syscon + - starfive,jh7110-sys-syscon + - const: syscon + + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + syscon@10240000 { + compatible = "starfive,jh7110-stg-syscon", "syscon"; + reg = <0x10240000 0x1000>; + }; + +... diff --git a/MAINTAINERS b/MAINTAINERS index 9b66eaad0547f..60b1bee91d121 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19911,6 +19911,11 @@ S: Supported F: Documentation/devicetree/bindings/mmc/starfive* F: drivers/mmc/host/dw_mmc-starfive.c +STARFIVE JH7110 SYSCON +M: William Qiu +S: Supported +F: Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml + STARFIVE JH71X0 CLOCK DRIVERS M: Emil Renner Berthing M: Hal Feng From 4eb32e1e2696e2c6b6dd485f83c3005fc95021e0 Mon Sep 17 00:00:00 2001 From: William Qiu Date: Wed, 15 Mar 2023 13:58:13 +0800 Subject: [PATCH 24/95] riscv: dts: starfive: Add syscon node Add stg_syscon/sys_syscon/aon_syscon node for JH7110 Soc. Signed-off-by: William Qiu Reviewed-by: Conor Dooley Reviewed-by: Emil Renner Berthing --- arch/riscv/boot/dts/starfive/jh7110.dtsi | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi index 417f7f51c219f..cd7a6a1196f89 100644 --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi @@ -362,6 +362,11 @@ status = "disabled"; }; + stg_syscon: syscon@10240000 { + compatible = "starfive,jh7110-stg-syscon", "syscon"; + reg = <0x0 0x10240000 0x0 0x1000>; + }; + uart3: serial@12000000 { compatible = "snps,dw-apb-uart"; reg = <0x0 0x12000000 0x0 0x10000>; @@ -466,6 +471,11 @@ #reset-cells = <1>; }; + sys_syscon: syscon@13030000 { + compatible = "starfive,jh7110-sys-syscon", "syscon"; + reg = <0x0 0x13030000 0x0 0x1000>; + }; + sysgpio: pinctrl@13040000 { compatible = "starfive,jh7110-sys-pinctrl"; reg = <0x0 0x13040000 0x0 0x10000>; @@ -495,6 +505,11 @@ #reset-cells = <1>; }; + aon_syscon: syscon@17010000 { + compatible = "starfive,jh7110-aon-syscon", "syscon"; + reg = <0x0 0x17010000 0x0 0x1000>; + }; + aongpio: pinctrl@17020000 { compatible = "starfive,jh7110-aon-pinctrl"; reg = <0x0 0x17020000 0x0 0x10000>; From 41707913500fc90db0965796b83ab2aeec112f47 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Tue, 14 Mar 2023 20:43:54 +0800 Subject: [PATCH 25/95] dt-bindings: clock: Add StarFive JH7110 System-Top-Group clock and reset generator Add bindings for the System-Top-Group clock and reset generator (STGCRG) on the JH7110 RISC-V SoC by StarFive Ltd. Signed-off-by: Xingyu Wu Reviewed-by: Krzysztof Kozlowski --- .../clock/starfive,jh7110-stgcrg.yaml | 82 +++++++++++++++++++ .../dt-bindings/clock/starfive,jh7110-crg.h | 34 ++++++++ .../dt-bindings/reset/starfive,jh7110-crg.h | 28 +++++++ 3 files changed, 144 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/starfive,jh7110-stgcrg.yaml diff --git a/Documentation/devicetree/bindings/clock/starfive,jh7110-stgcrg.yaml b/Documentation/devicetree/bindings/clock/starfive,jh7110-stgcrg.yaml new file mode 100644 index 0000000000000..b64ccd84200ac --- /dev/null +++ b/Documentation/devicetree/bindings/clock/starfive,jh7110-stgcrg.yaml @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/starfive,jh7110-stgcrg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 System-Top-Group Clock and Reset Generator + +maintainers: + - Xingyu Wu + +properties: + compatible: + const: starfive,jh7110-stgcrg + + reg: + maxItems: 1 + + clocks: + items: + - description: Main Oscillator (24 MHz) + - description: HIFI4 core + - description: STG AXI/AHB + - description: USB (125 MHz) + - description: CPU Bus + - description: HIFI4 Axi + - description: NOC STG Bus + - description: APB Bus + + clock-names: + items: + - const: osc + - const: hifi4_core + - const: stg_axiahb + - const: usb_125m + - const: cpu_bus + - const: hifi4_axi + - const: nocstg_bus + - const: apb_bus + + '#clock-cells': + const: 1 + description: + See for valid indices. + + '#reset-cells': + const: 1 + description: + See for valid indices. + +required: + - compatible + - reg + - clocks + - clock-names + - '#clock-cells' + - '#reset-cells' + +additionalProperties: false + +examples: + - | + #include + + stgcrg: clock-controller@10230000 { + compatible = "starfive,jh7110-stgcrg"; + reg = <0x10230000 0x10000>; + clocks = <&osc>, + <&syscrg JH7110_SYSCLK_HIFI4_CORE>, + <&syscrg JH7110_SYSCLK_STG_AXIAHB>, + <&syscrg JH7110_SYSCLK_USB_125M>, + <&syscrg JH7110_SYSCLK_CPU_BUS>, + <&syscrg JH7110_SYSCLK_HIFI4_AXI>, + <&syscrg JH7110_SYSCLK_NOCSTG_BUS>, + <&syscrg JH7110_SYSCLK_APB_BUS>; + clock-names = "osc", "hifi4_core", + "stg_axiahb", "usb_125m", + "cpu_bus", "hifi4_axi", + "nocstg_bus", "apb_bus"; + #clock-cells = <1>; + #reset-cells = <1>; + }; diff --git a/include/dt-bindings/clock/starfive,jh7110-crg.h b/include/dt-bindings/clock/starfive,jh7110-crg.h index 06257bfd9ac1e..6c8e8b4cf1f6c 100644 --- a/include/dt-bindings/clock/starfive,jh7110-crg.h +++ b/include/dt-bindings/clock/starfive,jh7110-crg.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 OR MIT */ /* * Copyright 2022 Emil Renner Berthing + * Copyright 2022 StarFive Technology Co., Ltd. */ #ifndef __DT_BINDINGS_CLOCK_STARFIVE_JH7110_CRG_H__ @@ -218,4 +219,37 @@ #define JH7110_AONCLK_END 14 +/* STGCRG clocks */ +#define JH7110_STGCLK_HIFI4_CLK_CORE 0 +#define JH7110_STGCLK_USB0_APB 1 +#define JH7110_STGCLK_USB0_UTMI_APB 2 +#define JH7110_STGCLK_USB0_AXI 3 +#define JH7110_STGCLK_USB0_LPM 4 +#define JH7110_STGCLK_USB0_STB 5 +#define JH7110_STGCLK_USB0_APP_125 6 +#define JH7110_STGCLK_USB0_REFCLK 7 +#define JH7110_STGCLK_PCIE0_AXI_MST0 8 +#define JH7110_STGCLK_PCIE0_APB 9 +#define JH7110_STGCLK_PCIE0_TL 10 +#define JH7110_STGCLK_PCIE1_AXI_MST0 11 +#define JH7110_STGCLK_PCIE1_APB 12 +#define JH7110_STGCLK_PCIE1_TL 13 +#define JH7110_STGCLK_PCIE_SLV_MAIN 14 +#define JH7110_STGCLK_SEC_AHB 15 +#define JH7110_STGCLK_SEC_MISC_AHB 16 +#define JH7110_STGCLK_GRP0_MAIN 17 +#define JH7110_STGCLK_GRP0_BUS 18 +#define JH7110_STGCLK_GRP0_STG 19 +#define JH7110_STGCLK_GRP1_MAIN 20 +#define JH7110_STGCLK_GRP1_BUS 21 +#define JH7110_STGCLK_GRP1_STG 22 +#define JH7110_STGCLK_GRP1_HIFI 23 +#define JH7110_STGCLK_E2_RTC 24 +#define JH7110_STGCLK_E2_CORE 25 +#define JH7110_STGCLK_E2_DBG 26 +#define JH7110_STGCLK_DMA1P_AXI 27 +#define JH7110_STGCLK_DMA1P_AHB 28 + +#define JH7110_STGCLK_END 29 + #endif /* __DT_BINDINGS_CLOCK_STARFIVE_JH7110_CRG_H__ */ diff --git a/include/dt-bindings/reset/starfive,jh7110-crg.h b/include/dt-bindings/reset/starfive,jh7110-crg.h index d78e38690ceb3..4e96ab81dd8ec 100644 --- a/include/dt-bindings/reset/starfive,jh7110-crg.h +++ b/include/dt-bindings/reset/starfive,jh7110-crg.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 OR MIT */ /* * Copyright (C) 2022 Emil Renner Berthing + * Copyright (C) 2022 StarFive Technology Co., Ltd. */ #ifndef __DT_BINDINGS_RESET_STARFIVE_JH7110_CRG_H__ @@ -151,4 +152,31 @@ #define JH7110_AONRST_END 8 +/* STGCRG resets */ +#define JH7110_STGRST_SYSCON 0 +#define JH7110_STGRST_HIFI4_CORE 1 +#define JH7110_STGRST_HIFI4_AXI 2 +#define JH7110_STGRST_SEC_AHB 3 +#define JH7110_STGRST_E24_CORE 4 +#define JH7110_STGRST_DMA1P_AXI 5 +#define JH7110_STGRST_DMA1P_AHB 6 +#define JH7110_STGRST_USB0_AXI 7 +#define JH7110_STGRST_USB0_APB 8 +#define JH7110_STGRST_USB0_UTMI_APB 9 +#define JH7110_STGRST_USB0_PWRUP 10 +#define JH7110_STGRST_PCIE0_AXI_MST0 11 +#define JH7110_STGRST_PCIE0_AXI_SLV0 12 +#define JH7110_STGRST_PCIE0_AXI_SLV 13 +#define JH7110_STGRST_PCIE0_BRG 14 +#define JH7110_STGRST_PCIE0_CORE 15 +#define JH7110_STGRST_PCIE0_APB 16 +#define JH7110_STGRST_PCIE1_AXI_MST0 17 +#define JH7110_STGRST_PCIE1_AXI_SLV0 18 +#define JH7110_STGRST_PCIE1_AXI_SLV 19 +#define JH7110_STGRST_PCIE1_BRG 20 +#define JH7110_STGRST_PCIE1_CORE 21 +#define JH7110_STGRST_PCIE1_APB 22 + +#define JH7110_STGRST_END 23 + #endif /* __DT_BINDINGS_RESET_STARFIVE_JH7110_CRG_H__ */ From c04924625670e471dcafe0b188235c4d48296ac0 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Tue, 14 Mar 2023 20:43:55 +0800 Subject: [PATCH 26/95] reset: starfive: jh7110: Add StarFive System-Top-Group reset support Add auxiliary_device_id to support StarFive JH7110 System-Top-Group resets of which the auxiliary device name is "clk_starfive_jh71x0.reset-stg". Signed-off-by: Xingyu Wu --- drivers/reset/starfive/reset-starfive-jh7110.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/reset/starfive/reset-starfive-jh7110.c b/drivers/reset/starfive/reset-starfive-jh7110.c index 4ccb3f72ca838..2dc46d91a244d 100644 --- a/drivers/reset/starfive/reset-starfive-jh7110.c +++ b/drivers/reset/starfive/reset-starfive-jh7110.c @@ -29,6 +29,12 @@ static const struct jh7110_reset_info jh7110_aon_info = { .status_offset = 0x3C, }; +static const struct jh7110_reset_info jh7110_stg_info = { + .nr_resets = JH7110_STGRST_END, + .assert_offset = 0x74, + .status_offset = 0x78, +}; + static int jh7110_reset_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id) { @@ -55,6 +61,10 @@ static const struct auxiliary_device_id jh7110_reset_ids[] = { .name = "clk_starfive_jh71x0.reset-aon", .driver_data = (kernel_ulong_t)&jh7110_aon_info, }, + { + .name = "clk_starfive_jh71x0.reset-stg", + .driver_data = (kernel_ulong_t)&jh7110_stg_info, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(auxiliary, jh7110_reset_ids); From 99f2ae66010c73c6e68ec1f3ce983c82adaca00b Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Tue, 14 Mar 2023 20:43:56 +0800 Subject: [PATCH 27/95] clk: starfive: Add StarFive JH7110 System-Top-Group clock driver Add driver for the StarFive JH7110 System-Top-Group clock controller. Signed-off-by: Emil Renner Berthing Co-developed-by: Xingyu Wu Signed-off-by: Xingyu Wu --- drivers/clk/starfive/Kconfig | 11 ++ drivers/clk/starfive/Makefile | 1 + .../clk/starfive/clk-starfive-jh7110-stg.c | 176 ++++++++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 drivers/clk/starfive/clk-starfive-jh7110-stg.c diff --git a/drivers/clk/starfive/Kconfig b/drivers/clk/starfive/Kconfig index 71c1148ee5f66..a60abed216508 100644 --- a/drivers/clk/starfive/Kconfig +++ b/drivers/clk/starfive/Kconfig @@ -42,3 +42,14 @@ config CLK_STARFIVE_JH7110_AON help Say yes here to support the always-on clock controller on the StarFive JH7110 SoC. + +config CLK_STARFIVE_JH7110_STG + tristate "StarFive JH7110 System-Top-Group clock support" + depends on CLK_STARFIVE_JH7110_SYS + select AUXILIARY_BUS + select CLK_STARFIVE_JH71X0 + select RESET_STARFIVE_JH7110 + default m if ARCH_STARFIVE + help + Say yes here to support the System-Top-Group clock controller + on the StarFive JH7110 SoC. diff --git a/drivers/clk/starfive/Makefile b/drivers/clk/starfive/Makefile index f3df7d957b1e5..b81e97ee2659e 100644 --- a/drivers/clk/starfive/Makefile +++ b/drivers/clk/starfive/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_CLK_STARFIVE_JH7100_AUDIO) += clk-starfive-jh7100-audio.o obj-$(CONFIG_CLK_STARFIVE_JH7110_SYS) += clk-starfive-jh7110-sys.o obj-$(CONFIG_CLK_STARFIVE_JH7110_AON) += clk-starfive-jh7110-aon.o +obj-$(CONFIG_CLK_STARFIVE_JH7110_STG) += clk-starfive-jh7110-stg.o diff --git a/drivers/clk/starfive/clk-starfive-jh7110-stg.c b/drivers/clk/starfive/clk-starfive-jh7110-stg.c new file mode 100644 index 0000000000000..547f213a22b00 --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh7110-stg.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JH7110 System-Top-Group Clock Driver + * + * Copyright (C) 2022 Emil Renner Berthing + * Copyright (C) 2022 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include + +#include + +#include "clk-starfive-jh71x0.h" + +/* external clocks */ +#define JH7110_STGCLK_OSC (JH7110_STGCLK_END + 0) +#define JH7110_STGCLK_HIFI4_CORE (JH7110_STGCLK_END + 1) +#define JH7110_STGCLK_STG_AXIAHB (JH7110_STGCLK_END + 2) +#define JH7110_STGCLK_USB_125M (JH7110_STGCLK_END + 3) +#define JH7110_STGCLK_CPU_BUS (JH7110_STGCLK_END + 4) +#define JH7110_STGCLK_HIFI4_AXI (JH7110_STGCLK_END + 5) +#define JH7110_STGCLK_NOCSTG_BUS (JH7110_STGCLK_END + 6) +#define JH7110_STGCLK_APB_BUS (JH7110_STGCLK_END + 7) +#define JH7110_STGCLK_EXT_END (JH7110_STGCLK_END + 8) + +static const struct jh71x0_clk_data jh7110_stgclk_data[] = { + /* hifi4 */ + JH71X0_GATE(JH7110_STGCLK_HIFI4_CLK_CORE, "hifi4_clk_core", 0, + JH7110_STGCLK_HIFI4_CORE), + /* usb */ + JH71X0_GATE(JH7110_STGCLK_USB0_APB, "usb0_apb", 0, JH7110_STGCLK_APB_BUS), + JH71X0_GATE(JH7110_STGCLK_USB0_UTMI_APB, "usb0_utmi_apb", 0, JH7110_STGCLK_APB_BUS), + JH71X0_GATE(JH7110_STGCLK_USB0_AXI, "usb0_axi", 0, JH7110_STGCLK_STG_AXIAHB), + JH71X0_GDIV(JH7110_STGCLK_USB0_LPM, "usb0_lpm", 0, 2, JH7110_STGCLK_OSC), + JH71X0_GDIV(JH7110_STGCLK_USB0_STB, "usb0_stb", 0, 4, JH7110_STGCLK_OSC), + JH71X0_GATE(JH7110_STGCLK_USB0_APP_125, "usb0_app_125", 0, JH7110_STGCLK_USB_125M), + JH71X0__DIV(JH7110_STGCLK_USB0_REFCLK, "usb0_refclk", 2, JH7110_STGCLK_OSC), + /* pci-e */ + JH71X0_GATE(JH7110_STGCLK_PCIE0_AXI_MST0, "pcie0_axi_mst0", 0, + JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_PCIE0_APB, "pcie0_apb", 0, JH7110_STGCLK_APB_BUS), + JH71X0_GATE(JH7110_STGCLK_PCIE0_TL, "pcie0_tl", 0, JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_PCIE1_AXI_MST0, "pcie1_axi_mst0", 0, + JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_PCIE1_APB, "pcie1_apb", 0, JH7110_STGCLK_APB_BUS), + JH71X0_GATE(JH7110_STGCLK_PCIE1_TL, "pcie1_tl", 0, JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_PCIE_SLV_MAIN, "pcie_slv_main", CLK_IS_CRITICAL, + JH7110_STGCLK_STG_AXIAHB), + /* security */ + JH71X0_GATE(JH7110_STGCLK_SEC_AHB, "sec_ahb", 0, JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_SEC_MISC_AHB, "sec_misc_ahb", 0, JH7110_STGCLK_STG_AXIAHB), + /* stg mtrx */ + JH71X0_GATE(JH7110_STGCLK_GRP0_MAIN, "mtrx_grp0_main", CLK_IS_CRITICAL, + JH7110_STGCLK_CPU_BUS), + JH71X0_GATE(JH7110_STGCLK_GRP0_BUS, "mtrx_grp0_bus", CLK_IS_CRITICAL, + JH7110_STGCLK_NOCSTG_BUS), + JH71X0_GATE(JH7110_STGCLK_GRP0_STG, "mtrx_grp0_stg", CLK_IS_CRITICAL, + JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_GRP1_MAIN, "mtrx_grp1_main", CLK_IS_CRITICAL, + JH7110_STGCLK_CPU_BUS), + JH71X0_GATE(JH7110_STGCLK_GRP1_BUS, "mtrx_grp1_bus", CLK_IS_CRITICAL, + JH7110_STGCLK_NOCSTG_BUS), + JH71X0_GATE(JH7110_STGCLK_GRP1_STG, "mtrx_grp1_stg", CLK_IS_CRITICAL, + JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_GRP1_HIFI, "mtrx_grp1_hifi", CLK_IS_CRITICAL, + JH7110_STGCLK_HIFI4_AXI), + /* e24_rvpi */ + JH71X0_GDIV(JH7110_STGCLK_E2_RTC, "e2_rtc", 0, 24, JH7110_STGCLK_OSC), + JH71X0_GATE(JH7110_STGCLK_E2_CORE, "e2_core", 0, JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_E2_DBG, "e2_dbg", 0, JH7110_STGCLK_STG_AXIAHB), + /* dw_sgdma1p */ + JH71X0_GATE(JH7110_STGCLK_DMA1P_AXI, "dma1p_axi", 0, JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_DMA1P_AHB, "dma1p_ahb", 0, JH7110_STGCLK_STG_AXIAHB), +}; + +static struct clk_hw *jh7110_stgclk_get(struct of_phandle_args *clkspec, void *data) +{ + struct jh71x0_clk_priv *priv = data; + unsigned int idx = clkspec->args[0]; + + if (idx < JH7110_STGCLK_END) + return &priv->reg[idx].hw; + + return ERR_PTR(-EINVAL); +} + +static int jh7110_stgcrg_probe(struct platform_device *pdev) +{ + struct jh71x0_clk_priv *priv; + unsigned int idx; + int ret; + + priv = devm_kzalloc(&pdev->dev, struct_size(priv, reg, JH7110_STGCLK_END), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spin_lock_init(&priv->rmw_lock); + priv->dev = &pdev->dev; + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + dev_set_drvdata(priv->dev, (void *)(&priv->base)); + + for (idx = 0; idx < JH7110_STGCLK_END; idx++) { + u32 max = jh7110_stgclk_data[idx].max; + struct clk_parent_data parents[4] = {}; + struct clk_init_data init = { + .name = jh7110_stgclk_data[idx].name, + .ops = starfive_jh71x0_clk_ops(max), + .parent_data = parents, + .num_parents = + ((max & JH71X0_CLK_MUX_MASK) >> JH71X0_CLK_MUX_SHIFT) + 1, + .flags = jh7110_stgclk_data[idx].flags, + }; + struct jh71x0_clk *clk = &priv->reg[idx]; + const char *fw_name[JH7110_STGCLK_EXT_END - JH7110_STGCLK_END] = { + "osc", + "hifi4_core", + "stg_axiahb", + "usb_125m", + "cpu_bus", + "hifi4_axi", + "nocstg_bus", + "apb_bus" + }; + unsigned int i; + + for (i = 0; i < init.num_parents; i++) { + unsigned int pidx = jh7110_stgclk_data[idx].parents[i]; + + if (pidx < JH7110_STGCLK_END) + parents[i].hw = &priv->reg[pidx].hw; + else if (pidx < JH7110_STGCLK_EXT_END) + parents[i].fw_name = fw_name[pidx - JH7110_STGCLK_END]; + } + + clk->hw.init = &init; + clk->idx = idx; + clk->max_div = max & JH71X0_CLK_DIV_MASK; + + ret = devm_clk_hw_register(&pdev->dev, &clk->hw); + if (ret) + return ret; + } + + ret = devm_of_clk_add_hw_provider(&pdev->dev, jh7110_stgclk_get, priv); + if (ret) + return ret; + + return jh7110_reset_controller_register(priv, "reset-stg", 2); +} + +static const struct of_device_id jh7110_stgcrg_match[] = { + { .compatible = "starfive,jh7110-stgcrg" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jh7110_stgcrg_match); + +static struct platform_driver jh7110_stgcrg_driver = { + .probe = jh7110_stgcrg_probe, + .driver = { + .name = "clk-starfive-jh7110-stg", + .of_match_table = jh7110_stgcrg_match, + }, +}; +module_platform_driver(jh7110_stgcrg_driver); + +MODULE_AUTHOR("Xingyu Wu "); +MODULE_AUTHOR("Emil Renner Berthing "); +MODULE_DESCRIPTION("StarFive JH7110 System-Top-Group clock driver"); +MODULE_LICENSE("GPL"); From 16d1466fd5a726c9ab96f19d58b335b1a2100aab Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Tue, 14 Mar 2023 20:43:57 +0800 Subject: [PATCH 28/95] dt-bindings: clock: Add StarFive JH7110 Image-Signal-Process clock and reset generator Add bindings for the Image-Signal-Process clock and reset generator (ISPCRG) on the JH7110 RISC-V SoC by StarFive Ltd. Signed-off-by: Xingyu Wu Reviewed-by: Krzysztof Kozlowski --- .../clock/starfive,jh7110-ispcrg.yaml | 87 +++++++++++++++++++ .../dt-bindings/clock/starfive,jh7110-crg.h | 18 ++++ .../dt-bindings/reset/starfive,jh7110-crg.h | 16 ++++ 3 files changed, 121 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/starfive,jh7110-ispcrg.yaml diff --git a/Documentation/devicetree/bindings/clock/starfive,jh7110-ispcrg.yaml b/Documentation/devicetree/bindings/clock/starfive,jh7110-ispcrg.yaml new file mode 100644 index 0000000000000..3b8b85be5cd05 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/starfive,jh7110-ispcrg.yaml @@ -0,0 +1,87 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/starfive,jh7110-ispcrg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 Image-Signal-Process Clock and Reset Generator + +maintainers: + - Xingyu Wu + +properties: + compatible: + const: starfive,jh7110-ispcrg + + reg: + maxItems: 1 + + clocks: + items: + - description: ISP Top core + - description: ISP Top Axi + - description: NOC ISP Bus + - description: external DVP + + clock-names: + items: + - const: isp_top_core + - const: isp_top_axi + - const: noc_bus_isp_axi + - const: dvp_clk + + resets: + items: + - description: ISP Top core + - description: ISP Top Axi + - description: NOC ISP Bus + + '#clock-cells': + const: 1 + description: + See for valid indices. + + '#reset-cells': + const: 1 + description: + See for valid indices. + + power-domains: + maxItems: 1 + description: + ISP domain power + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - '#clock-cells' + - '#reset-cells' + - power-domains + +additionalProperties: false + +examples: + - | + #include + #include + #include + + ispcrg: clock-controller@19810000 { + compatible = "starfive,jh7110-ispcrg"; + reg = <0x19810000 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_ISP_TOP_CORE>, + <&syscrg JH7110_SYSCLK_ISP_TOP_AXI>, + <&syscrg JH7110_SYSCLK_NOC_BUS_ISP_AXI>, + <&dvp_clk>; + clock-names = "isp_top_core", "isp_top_axi", + "noc_bus_isp_axi", "dvp_clk"; + resets = <&syscrg JH7110_SYSRST_ISP_TOP>, + <&syscrg JH7110_SYSRST_ISP_TOP_AXI>, + <&syscrg JH7110_SYSRST_NOC_BUS_ISP_AXI>; + #clock-cells = <1>; + #reset-cells = <1>; + power-domains = <&pwrc JH7110_PD_ISP>; + }; diff --git a/include/dt-bindings/clock/starfive,jh7110-crg.h b/include/dt-bindings/clock/starfive,jh7110-crg.h index 6c8e8b4cf1f6c..a329836f6b53a 100644 --- a/include/dt-bindings/clock/starfive,jh7110-crg.h +++ b/include/dt-bindings/clock/starfive,jh7110-crg.h @@ -252,4 +252,22 @@ #define JH7110_STGCLK_END 29 +/* ISPCRG clocks */ +#define JH7110_ISPCLK_DOM4_APB_FUNC 0 +#define JH7110_ISPCLK_MIPI_RX0_PXL 1 +#define JH7110_ISPCLK_DVP_INV 2 +#define JH7110_ISPCLK_M31DPHY_CFG_IN 3 +#define JH7110_ISPCLK_M31DPHY_REF_IN 4 +#define JH7110_ISPCLK_M31DPHY_TX_ESC_LAN0 5 +#define JH7110_ISPCLK_VIN_ABP 6 +#define JH7110_ISPCLK_VIN_SYS 7 +#define JH7110_ISPCLK_VIN_PIXEL_IF0 8 +#define JH7110_ISPCLK_VIN_PIXEL_IF1 9 +#define JH7110_ISPCLK_VIN_PIXEL_IF2 10 +#define JH7110_ISPCLK_VIN_PIXEL_IF3 11 +#define JH7110_ISPCLK_VIN_P_AXI_WR 12 +#define JH7110_ISPCLK_ISPV2_TOP_WRAPPER_C 13 + +#define JH7110_ISPCLK_END 14 + #endif /* __DT_BINDINGS_CLOCK_STARFIVE_JH7110_CRG_H__ */ diff --git a/include/dt-bindings/reset/starfive,jh7110-crg.h b/include/dt-bindings/reset/starfive,jh7110-crg.h index 4e96ab81dd8ec..3ed5e0b1cebda 100644 --- a/include/dt-bindings/reset/starfive,jh7110-crg.h +++ b/include/dt-bindings/reset/starfive,jh7110-crg.h @@ -179,4 +179,20 @@ #define JH7110_STGRST_END 23 +/* ISPCRG resets */ +#define JH7110_ISPRST_ISPV2_TOP_WRAPPER_P 0 +#define JH7110_ISPRST_ISPV2_TOP_WRAPPER_C 1 +#define JH7110_ISPRST_M31DPHY_HW 2 +#define JH7110_ISPRST_M31DPHY_B09_AON 3 +#define JH7110_ISPRST_VIN_ABP 4 +#define JH7110_ISPRST_VIN_PIXEL_IF0 5 +#define JH7110_ISPRST_VIN_PIXEL_IF1 6 +#define JH7110_ISPRST_VIN_PIXEL_IF2 7 +#define JH7110_ISPRST_VIN_PIXEL_IF3 8 +#define JH7110_ISPRST_VIN_SYS 9 +#define JH7110_ISPRST_VIN_P_AXI_RD 10 +#define JH7110_ISPRST_VIN_P_AXI_WR 11 + +#define JH7110_ISPRST_END 12 + #endif /* __DT_BINDINGS_RESET_STARFIVE_JH7110_CRG_H__ */ From d6a5d692e3dee584048db07671e2a63ea6c286ac Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Tue, 14 Mar 2023 20:43:58 +0800 Subject: [PATCH 29/95] reset: starfive: jh7110: Add StarFive Image-Signal-Process reset support Add auxiliary_device_id to support StarFive JH7110 Image-Signal-Process resets of which the auxiliary device name is "clk_starfive_jh71x0.reset-isp". Signed-off-by: Xingyu Wu --- drivers/reset/starfive/reset-starfive-jh7110.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/reset/starfive/reset-starfive-jh7110.c b/drivers/reset/starfive/reset-starfive-jh7110.c index 2dc46d91a244d..3aeeea8ee1edf 100644 --- a/drivers/reset/starfive/reset-starfive-jh7110.c +++ b/drivers/reset/starfive/reset-starfive-jh7110.c @@ -35,6 +35,12 @@ static const struct jh7110_reset_info jh7110_stg_info = { .status_offset = 0x78, }; +static const struct jh7110_reset_info jh7110_isp_info = { + .nr_resets = JH7110_ISPRST_END, + .assert_offset = 0x38, + .status_offset = 0x3C, +}; + static int jh7110_reset_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id) { @@ -65,6 +71,10 @@ static const struct auxiliary_device_id jh7110_reset_ids[] = { .name = "clk_starfive_jh71x0.reset-stg", .driver_data = (kernel_ulong_t)&jh7110_stg_info, }, + { + .name = "clk_starfive_jh71x0.reset-isp", + .driver_data = (kernel_ulong_t)&jh7110_isp_info, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(auxiliary, jh7110_reset_ids); From cb5792e06fe01675b2bebe902b43544156088ae3 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Tue, 14 Mar 2023 20:43:59 +0800 Subject: [PATCH 30/95] clk: starfive: Add StarFive JH7110 Image-Signal-Process clock driver Add driver for the StarFive JH7110 Image-Signal-Process clock controller. Signed-off-by: Xingyu Wu --- drivers/clk/starfive/Kconfig | 11 + drivers/clk/starfive/Makefile | 1 + .../clk/starfive/clk-starfive-jh7110-isp.c | 233 ++++++++++++++++++ 3 files changed, 245 insertions(+) create mode 100644 drivers/clk/starfive/clk-starfive-jh7110-isp.c diff --git a/drivers/clk/starfive/Kconfig b/drivers/clk/starfive/Kconfig index a60abed216508..be02eabebeff1 100644 --- a/drivers/clk/starfive/Kconfig +++ b/drivers/clk/starfive/Kconfig @@ -53,3 +53,14 @@ config CLK_STARFIVE_JH7110_STG help Say yes here to support the System-Top-Group clock controller on the StarFive JH7110 SoC. + +config CLK_STARFIVE_JH7110_ISP + tristate "StarFive JH7110 Image-Signal-Process clock support" + depends on CLK_STARFIVE_JH7110_SYS && JH71XX_PMU + select AUXILIARY_BUS + select CLK_STARFIVE_JH71X0 + select RESET_STARFIVE_JH7110 + default m if ARCH_STARFIVE + help + Say yes here to support the Image-Signal-Process clock controller + on the StarFive JH7110 SoC. diff --git a/drivers/clk/starfive/Makefile b/drivers/clk/starfive/Makefile index b81e97ee2659e..76fb9f8d628b1 100644 --- a/drivers/clk/starfive/Makefile +++ b/drivers/clk/starfive/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_CLK_STARFIVE_JH7100_AUDIO) += clk-starfive-jh7100-audio.o obj-$(CONFIG_CLK_STARFIVE_JH7110_SYS) += clk-starfive-jh7110-sys.o obj-$(CONFIG_CLK_STARFIVE_JH7110_AON) += clk-starfive-jh7110-aon.o obj-$(CONFIG_CLK_STARFIVE_JH7110_STG) += clk-starfive-jh7110-stg.o +obj-$(CONFIG_CLK_STARFIVE_JH7110_ISP) += clk-starfive-jh7110-isp.o diff --git a/drivers/clk/starfive/clk-starfive-jh7110-isp.c b/drivers/clk/starfive/clk-starfive-jh7110-isp.c new file mode 100644 index 0000000000000..8e372dc423d8b --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh7110-isp.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JH7110 Image-Signal-Process Clock Driver + * + * Copyright (C) 2022 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "clk-starfive-jh71x0.h" + +/* external clocks */ +#define JH7110_ISPCLK_ISP_TOP_CORE (JH7110_ISPCLK_END + 0) +#define JH7110_ISPCLK_ISP_TOP_AXI (JH7110_ISPCLK_END + 1) +#define JH7110_ISPCLK_NOC_BUS_ISP_AXI (JH7110_ISPCLK_END + 2) +#define JH7110_ISPCLK_DVP_CLK (JH7110_ISPCLK_END + 3) +#define JH7110_ISPCLK_EXT_END (JH7110_ISPCLK_END + 4) + +/* ISP domian clocks */ +struct isp_top_crg { + struct clk_bulk_data *top_clks; + int top_clks_num; + void __iomem *base; +}; + +static struct clk_bulk_data jh7110_isp_top_clks[] = { + { .id = "isp_top_core" }, + { .id = "isp_top_axi" } +}; + +static const struct jh71x0_clk_data jh7110_ispclk_data[] = { + /* syscon */ + JH71X0__DIV(JH7110_ISPCLK_DOM4_APB_FUNC, "dom4_apb_func", 15, + JH7110_ISPCLK_ISP_TOP_AXI), + JH71X0__DIV(JH7110_ISPCLK_MIPI_RX0_PXL, "mipi_rx0_pxl", 8, + JH7110_ISPCLK_ISP_TOP_CORE), + JH71X0__INV(JH7110_ISPCLK_DVP_INV, "dvp_inv", JH7110_ISPCLK_DVP_CLK), + /* vin */ + JH71X0__DIV(JH7110_ISPCLK_M31DPHY_CFG_IN, "m31dphy_cfg_in", 16, + JH7110_ISPCLK_ISP_TOP_CORE), + JH71X0__DIV(JH7110_ISPCLK_M31DPHY_REF_IN, "m31dphy_ref_in", 16, + JH7110_ISPCLK_ISP_TOP_CORE), + JH71X0__DIV(JH7110_ISPCLK_M31DPHY_TX_ESC_LAN0, "m31dphy_tx_esc_lan0", 60, + JH7110_ISPCLK_ISP_TOP_CORE), + JH71X0_GATE(JH7110_ISPCLK_VIN_ABP, "vin_abp", 0, + JH7110_ISPCLK_DOM4_APB_FUNC), + JH71X0__DIV(JH7110_ISPCLK_VIN_SYS, "vin_sys", 8, JH7110_ISPCLK_ISP_TOP_CORE), + JH71X0_GATE(JH7110_ISPCLK_VIN_PIXEL_IF0, "vin_pixel_if0", 0, + JH7110_ISPCLK_MIPI_RX0_PXL), + JH71X0_GATE(JH7110_ISPCLK_VIN_PIXEL_IF1, "vin_pixel_if1", 0, + JH7110_ISPCLK_MIPI_RX0_PXL), + JH71X0_GATE(JH7110_ISPCLK_VIN_PIXEL_IF2, "vin_pixel_if2", 0, + JH7110_ISPCLK_MIPI_RX0_PXL), + JH71X0_GATE(JH7110_ISPCLK_VIN_PIXEL_IF3, "vin_pixel_if3", 0, + JH7110_ISPCLK_MIPI_RX0_PXL), + JH71X0__MUX(JH7110_ISPCLK_VIN_P_AXI_WR, "vin_p_axi_wr", 2, + JH7110_ISPCLK_MIPI_RX0_PXL, + JH7110_ISPCLK_DVP_INV), + /* ispv2_top_wrapper */ + JH71X0_GMUX(JH7110_ISPCLK_ISPV2_TOP_WRAPPER_C, "ispv2_top_wrapper_c", 0, 2, + JH7110_ISPCLK_MIPI_RX0_PXL, + JH7110_ISPCLK_DVP_INV), +}; + +static struct isp_top_crg *top_crg_from(void __iomem **base) +{ + return container_of(base, struct isp_top_crg, base); +} + +static inline int jh7110_isp_top_crg_init(struct jh71x0_clk_priv *priv, struct isp_top_crg *top) +{ + struct reset_control *top_rsts; + int ret; + + top->top_clks = jh7110_isp_top_clks; + top->top_clks_num = ARRAY_SIZE(jh7110_isp_top_clks); + ret = devm_clk_bulk_get(priv->dev, top->top_clks_num, top->top_clks); + if (ret) + return dev_err_probe(priv->dev, ret, "failed to get top clocks\n"); + + /* The resets should be shared and other ISP modules will use its. */ + top_rsts = devm_reset_control_array_get_shared(priv->dev); + if (IS_ERR(top_rsts)) + return dev_err_probe(priv->dev, PTR_ERR(top_rsts), + "failed to get top resets\n"); + + ret = clk_bulk_prepare_enable(top->top_clks_num, top->top_clks); + if (ret) + return dev_err_probe(priv->dev, ret, "failed to enable top clocks\n"); + + return reset_control_deassert(top_rsts); +} + +static struct clk_hw *jh7110_ispclk_get(struct of_phandle_args *clkspec, void *data) +{ + struct jh71x0_clk_priv *priv = data; + unsigned int idx = clkspec->args[0]; + + if (idx < JH7110_ISPCLK_END) + return &priv->reg[idx].hw; + + return ERR_PTR(-EINVAL); +} + +static int jh7110_ispcrg_probe(struct platform_device *pdev) +{ + struct jh71x0_clk_priv *priv; + struct isp_top_crg *top; + unsigned int idx; + int ret; + + priv = devm_kzalloc(&pdev->dev, + struct_size(priv, reg, JH7110_ISPCLK_END), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + top = devm_kzalloc(&pdev->dev, sizeof(*top), GFP_KERNEL); + if (!top) + return -ENOMEM; + + spin_lock_init(&priv->rmw_lock); + priv->dev = &pdev->dev; + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + pm_runtime_enable(priv->dev); + ret = pm_runtime_get_sync(priv->dev); + if (ret < 0) + return dev_err_probe(priv->dev, ret, "failed to turn on power\n"); + + ret = jh7110_isp_top_crg_init(priv, top); + if (ret) + goto err_clk; + + top->base = priv->base; + dev_set_drvdata(priv->dev, (void *)(&top->base)); + + for (idx = 0; idx < JH7110_ISPCLK_END; idx++) { + u32 max = jh7110_ispclk_data[idx].max; + struct clk_parent_data parents[4] = {}; + struct clk_init_data init = { + .name = jh7110_ispclk_data[idx].name, + .ops = starfive_jh71x0_clk_ops(max), + .parent_data = parents, + .num_parents = + ((max & JH71X0_CLK_MUX_MASK) >> JH71X0_CLK_MUX_SHIFT) + 1, + .flags = jh7110_ispclk_data[idx].flags, + }; + struct jh71x0_clk *clk = &priv->reg[idx]; + unsigned int i; + const char *fw_name[JH7110_ISPCLK_EXT_END - JH7110_ISPCLK_END] = { + "isp_top_core", + "isp_top_axi", + "noc_bus_isp_axi", + "dvp_clk" + }; + + for (i = 0; i < init.num_parents; i++) { + unsigned int pidx = jh7110_ispclk_data[idx].parents[i]; + + if (pidx < JH7110_ISPCLK_END) + parents[i].hw = &priv->reg[pidx].hw; + else + parents[i].fw_name = fw_name[pidx - JH7110_ISPCLK_END]; + } + + clk->hw.init = &init; + clk->idx = idx; + clk->max_div = max & JH71X0_CLK_DIV_MASK; + + ret = devm_clk_hw_register(&pdev->dev, &clk->hw); + if (ret) + goto err_exit; + } + + ret = devm_of_clk_add_hw_provider(&pdev->dev, jh7110_ispclk_get, priv); + if (ret) + goto err_exit; + + ret = jh7110_reset_controller_register(priv, "reset-isp", 3); + if (ret) + goto err_exit; + + return 0; + +err_exit: + clk_bulk_disable_unprepare(top->top_clks_num, top->top_clks); +err_clk: + pm_runtime_put_sync(priv->dev); + pm_runtime_disable(priv->dev); + return ret; +} + +static int jh7110_ispcrg_remove(struct platform_device *pdev) +{ + void __iomem **base = dev_get_drvdata(&pdev->dev); + struct isp_top_crg *top = top_crg_from(base); + + clk_bulk_disable_unprepare(top->top_clks_num, top->top_clks); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct of_device_id jh7110_ispcrg_match[] = { + { .compatible = "starfive,jh7110-ispcrg" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jh7110_ispcrg_match); + +static struct platform_driver jh7110_ispcrg_driver = { + .probe = jh7110_ispcrg_probe, + .remove = jh7110_ispcrg_remove, + .driver = { + .name = "clk-starfive-jh7110-isp", + .of_match_table = jh7110_ispcrg_match, + }, +}; +module_platform_driver(jh7110_ispcrg_driver); + +MODULE_AUTHOR("Xingyu Wu "); +MODULE_DESCRIPTION("StarFive JH7110 Image-Signal-Process clock driver"); +MODULE_LICENSE("GPL"); From 87db461a6feb5adfa4a07c8f0d7e058cbd68cc9b Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Tue, 14 Mar 2023 20:44:00 +0800 Subject: [PATCH 31/95] dt-bindings: clock: Add StarFive JH7110 Video-Output clock and reset generator Add bindings for the Video-Output clock and reset generator (VOUTCRG) on the JH7110 RISC-V SoC by StarFive Ltd. Signed-off-by: Xingyu Wu Reviewed-by: Krzysztof Kozlowski --- .../clock/starfive,jh7110-voutcrg.yaml | 90 +++++++++++++++++++ .../dt-bindings/clock/starfive,jh7110-crg.h | 22 +++++ .../dt-bindings/reset/starfive,jh7110-crg.h | 16 ++++ 3 files changed, 128 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/starfive,jh7110-voutcrg.yaml diff --git a/Documentation/devicetree/bindings/clock/starfive,jh7110-voutcrg.yaml b/Documentation/devicetree/bindings/clock/starfive,jh7110-voutcrg.yaml new file mode 100644 index 0000000000000..af77bd8c86b12 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/starfive,jh7110-voutcrg.yaml @@ -0,0 +1,90 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/starfive,jh7110-voutcrg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 Video-Output Clock and Reset Generator + +maintainers: + - Xingyu Wu + +properties: + compatible: + const: starfive,jh7110-voutcrg + + reg: + maxItems: 1 + + clocks: + items: + - description: Vout Top core + - description: Vout Top Ahb + - description: Vout Top Axi + - description: Vout Top HDMI MCLK + - description: I2STX0 BCLK + - description: external HDMI pixel + + clock-names: + items: + - const: vout_src + - const: vout_top_ahb + - const: vout_top_axi + - const: vout_top_hdmitx0_mclk + - const: i2stx0_bclk + - const: hdmitx0_pixelclk + + resets: + maxItems: 1 + description: Vout Top core + + '#clock-cells': + const: 1 + description: + See for valid indices. + + '#reset-cells': + const: 1 + description: + See for valid indices. + + power-domains: + maxItems: 1 + description: + Vout domain power + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - '#clock-cells' + - '#reset-cells' + - power-domains + +additionalProperties: false + +examples: + - | + #include + #include + #include + + voutcrg: clock-controller@295C0000 { + compatible = "starfive,jh7110-voutcrg"; + reg = <0x295C0000 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_VOUT_SRC>, + <&syscrg JH7110_SYSCLK_VOUT_TOP_AHB>, + <&syscrg JH7110_SYSCLK_VOUT_TOP_AXI>, + <&syscrg JH7110_SYSCLK_VOUT_TOP_HDMITX0_MCLK>, + <&syscrg JH7110_SYSCLK_I2STX0_BCLK>, + <&hdmitx0_pixelclk>; + clock-names = "vout_src", "vout_top_ahb", + "vout_top_axi", "vout_top_hdmitx0_mclk", + "i2stx0_bclk", "hdmitx0_pixelclk"; + resets = <&syscrg JH7110_SYSRST_VOUT_TOP_SRC>; + #clock-cells = <1>; + #reset-cells = <1>; + power-domains = <&pwrc JH7110_PD_VOUT>; + }; diff --git a/include/dt-bindings/clock/starfive,jh7110-crg.h b/include/dt-bindings/clock/starfive,jh7110-crg.h index a329836f6b53a..0cd963a3a39c5 100644 --- a/include/dt-bindings/clock/starfive,jh7110-crg.h +++ b/include/dt-bindings/clock/starfive,jh7110-crg.h @@ -270,4 +270,26 @@ #define JH7110_ISPCLK_END 14 +/* VOUTCRG clocks */ +#define JH7110_VOUTCLK_APB 0 +#define JH7110_VOUTCLK_DC8200_PIX 1 +#define JH7110_VOUTCLK_DSI_SYS 2 +#define JH7110_VOUTCLK_TX_ESC 3 +#define JH7110_VOUTCLK_DC8200_AXI 4 +#define JH7110_VOUTCLK_DC8200_CORE 5 +#define JH7110_VOUTCLK_DC8200_AHB 6 +#define JH7110_VOUTCLK_DC8200_PIX0 7 +#define JH7110_VOUTCLK_DC8200_PIX1 8 +#define JH7110_VOUTCLK_DOM_VOUT_TOP_LCD 9 +#define JH7110_VOUTCLK_DSITX_APB 10 +#define JH7110_VOUTCLK_DSITX_SYS 11 +#define JH7110_VOUTCLK_DSITX_DPI 12 +#define JH7110_VOUTCLK_DSITX_TXESC 13 +#define JH7110_VOUTCLK_MIPITX_DPHY_TXESC 14 +#define JH7110_VOUTCLK_HDMI_TX_MCLK 15 +#define JH7110_VOUTCLK_HDMI_TX_BCLK 16 +#define JH7110_VOUTCLK_HDMI_TX_SYS 17 + +#define JH7110_VOUTCLK_END 18 + #endif /* __DT_BINDINGS_CLOCK_STARFIVE_JH7110_CRG_H__ */ diff --git a/include/dt-bindings/reset/starfive,jh7110-crg.h b/include/dt-bindings/reset/starfive,jh7110-crg.h index 3ed5e0b1cebda..57857a4ac7b2f 100644 --- a/include/dt-bindings/reset/starfive,jh7110-crg.h +++ b/include/dt-bindings/reset/starfive,jh7110-crg.h @@ -195,4 +195,20 @@ #define JH7110_ISPRST_END 12 +/* VOUTCRG resets */ +#define JH7110_VOUTRST_DC8200_AXI 0 +#define JH7110_VOUTRST_DC8200_AHB 1 +#define JH7110_VOUTRST_DC8200_CORE 2 +#define JH7110_VOUTRST_DSITX_DPI 3 +#define JH7110_VOUTRST_DSITX_APB 4 +#define JH7110_VOUTRST_DSITX_RXESC 5 +#define JH7110_VOUTRST_DSITX_SYS 6 +#define JH7110_VOUTRST_DSITX_TXBYTEHS 7 +#define JH7110_VOUTRST_DSITX_TXESC 8 +#define JH7110_VOUTRST_HDMI_TX_HDMI 9 +#define JH7110_VOUTRST_MIPITX_DPHY_SYS 10 +#define JH7110_VOUTRST_MIPITX_DPHY_TXBYTEHS 11 + +#define JH7110_VOUTRST_END 12 + #endif /* __DT_BINDINGS_RESET_STARFIVE_JH7110_CRG_H__ */ From e612495ca70f3e6cddd9185a35931b23de79444d Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Tue, 14 Mar 2023 20:44:01 +0800 Subject: [PATCH 32/95] reset: starfive: jh7110: Add StarFive Video-Output reset support Add auxiliary_device_id to support StarFive JH7110 Video-Output resets of which the auxiliary device name is "clk_starfive_jh71x0.reset-vout". Signed-off-by: Xingyu Wu --- drivers/reset/starfive/reset-starfive-jh7110.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/reset/starfive/reset-starfive-jh7110.c b/drivers/reset/starfive/reset-starfive-jh7110.c index 3aeeea8ee1edf..6ad535c41087c 100644 --- a/drivers/reset/starfive/reset-starfive-jh7110.c +++ b/drivers/reset/starfive/reset-starfive-jh7110.c @@ -41,6 +41,12 @@ static const struct jh7110_reset_info jh7110_isp_info = { .status_offset = 0x3C, }; +static const struct jh7110_reset_info jh7110_vout_info = { + .nr_resets = JH7110_VOUTRST_END, + .assert_offset = 0x48, + .status_offset = 0x4C, +}; + static int jh7110_reset_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id) { @@ -75,6 +81,10 @@ static const struct auxiliary_device_id jh7110_reset_ids[] = { .name = "clk_starfive_jh71x0.reset-isp", .driver_data = (kernel_ulong_t)&jh7110_isp_info, }, + { + .name = "clk_starfive_jh71x0.reset-vout", + .driver_data = (kernel_ulong_t)&jh7110_vout_info, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(auxiliary, jh7110_reset_ids); From b111ca337607d82995b428e16836ccf5e60d88f5 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Tue, 14 Mar 2023 20:44:02 +0800 Subject: [PATCH 33/95] clk: starfive: Add StarFive JH7110 Video-Output clock driver Add driver for the StarFive JH7110 Video-Output clock controller. Signed-off-by: Xingyu Wu --- drivers/clk/starfive/Kconfig | 11 + drivers/clk/starfive/Makefile | 1 + .../clk/starfive/clk-starfive-jh7110-vout.c | 240 ++++++++++++++++++ 3 files changed, 252 insertions(+) create mode 100644 drivers/clk/starfive/clk-starfive-jh7110-vout.c diff --git a/drivers/clk/starfive/Kconfig b/drivers/clk/starfive/Kconfig index be02eabebeff1..b0c7744965d7b 100644 --- a/drivers/clk/starfive/Kconfig +++ b/drivers/clk/starfive/Kconfig @@ -64,3 +64,14 @@ config CLK_STARFIVE_JH7110_ISP help Say yes here to support the Image-Signal-Process clock controller on the StarFive JH7110 SoC. + +config CLK_STARFIVE_JH7110_VOUT + tristate "StarFive JH7110 Video-Output clock support" + depends on CLK_STARFIVE_JH7110_SYS && JH71XX_PMU + select AUXILIARY_BUS + select CLK_STARFIVE_JH71X0 + select RESET_STARFIVE_JH7110 + default m if ARCH_STARFIVE + help + Say yes here to support the Video-Output clock controller + on the StarFive JH7110 SoC. diff --git a/drivers/clk/starfive/Makefile b/drivers/clk/starfive/Makefile index 76fb9f8d628b1..841377e45bb6f 100644 --- a/drivers/clk/starfive/Makefile +++ b/drivers/clk/starfive/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_CLK_STARFIVE_JH7110_SYS) += clk-starfive-jh7110-sys.o obj-$(CONFIG_CLK_STARFIVE_JH7110_AON) += clk-starfive-jh7110-aon.o obj-$(CONFIG_CLK_STARFIVE_JH7110_STG) += clk-starfive-jh7110-stg.o obj-$(CONFIG_CLK_STARFIVE_JH7110_ISP) += clk-starfive-jh7110-isp.o +obj-$(CONFIG_CLK_STARFIVE_JH7110_VOUT) += clk-starfive-jh7110-vout.o diff --git a/drivers/clk/starfive/clk-starfive-jh7110-vout.c b/drivers/clk/starfive/clk-starfive-jh7110-vout.c new file mode 100644 index 0000000000000..02a6c98b1bcda --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh7110-vout.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JH7110 Video-Output Clock Driver + * + * Copyright (C) 2022 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "clk-starfive-jh71x0.h" + +/* external clocks */ +#define JH7110_VOUTCLK_VOUT_SRC (JH7110_VOUTCLK_END + 0) +#define JH7110_VOUTCLK_VOUT_TOP_AHB (JH7110_VOUTCLK_END + 1) +#define JH7110_VOUTCLK_VOUT_TOP_AXI (JH7110_VOUTCLK_END + 2) +#define JH7110_VOUTCLK_VOUT_TOP_HDMITX0_MCLK (JH7110_VOUTCLK_END + 3) +#define JH7110_VOUTCLK_I2STX0_BCLK (JH7110_VOUTCLK_END + 4) +#define JH7110_VOUTCLK_HDMITX0_PIXELCLK (JH7110_VOUTCLK_END + 5) +#define JH7110_VOUTCLK_EXT_END (JH7110_VOUTCLK_END + 6) + +/* VOUT domian clocks */ +struct vout_top_crg { + struct clk_bulk_data *top_clks; + int top_clks_num; + void __iomem *base; +}; + +static struct clk_bulk_data jh7110_vout_top_clks[] = { + { .id = "vout_src" }, + { .id = "vout_top_ahb" } +}; + +static const struct jh71x0_clk_data jh7110_voutclk_data[] = { + /* divider */ + JH71X0__DIV(JH7110_VOUTCLK_APB, "apb", 8, JH7110_VOUTCLK_VOUT_TOP_AHB), + JH71X0__DIV(JH7110_VOUTCLK_DC8200_PIX, "dc8200_pix", 63, JH7110_VOUTCLK_VOUT_SRC), + JH71X0__DIV(JH7110_VOUTCLK_DSI_SYS, "dsi_sys", 31, JH7110_VOUTCLK_VOUT_SRC), + JH71X0__DIV(JH7110_VOUTCLK_TX_ESC, "tx_esc", 31, JH7110_VOUTCLK_VOUT_TOP_AHB), + /* dc8200 */ + JH71X0_GATE(JH7110_VOUTCLK_DC8200_AXI, "dc8200_axi", 0, JH7110_VOUTCLK_VOUT_TOP_AXI), + JH71X0_GATE(JH7110_VOUTCLK_DC8200_CORE, "dc8200_core", 0, JH7110_VOUTCLK_VOUT_TOP_AXI), + JH71X0_GATE(JH7110_VOUTCLK_DC8200_AHB, "dc8200_ahb", 0, JH7110_VOUTCLK_VOUT_TOP_AHB), + JH71X0_GMUX(JH7110_VOUTCLK_DC8200_PIX0, "dc8200_pix0", 0, 2, + JH7110_VOUTCLK_DC8200_PIX, + JH7110_VOUTCLK_HDMITX0_PIXELCLK), + JH71X0_GMUX(JH7110_VOUTCLK_DC8200_PIX1, "dc8200_pix1", 0, 2, + JH7110_VOUTCLK_DC8200_PIX, + JH7110_VOUTCLK_HDMITX0_PIXELCLK), + /* LCD */ + JH71X0_GMUX(JH7110_VOUTCLK_DOM_VOUT_TOP_LCD, "dom_vout_top_lcd", 0, 2, + JH7110_VOUTCLK_DC8200_PIX0, + JH7110_VOUTCLK_DC8200_PIX1), + /* dsiTx */ + JH71X0_GATE(JH7110_VOUTCLK_DSITX_APB, "dsiTx_apb", 0, JH7110_VOUTCLK_DSI_SYS), + JH71X0_GATE(JH7110_VOUTCLK_DSITX_SYS, "dsiTx_sys", 0, JH7110_VOUTCLK_DSI_SYS), + JH71X0_GMUX(JH7110_VOUTCLK_DSITX_DPI, "dsiTx_dpi", 0, 2, + JH7110_VOUTCLK_DC8200_PIX, + JH7110_VOUTCLK_HDMITX0_PIXELCLK), + JH71X0_GATE(JH7110_VOUTCLK_DSITX_TXESC, "dsiTx_txesc", 0, JH7110_VOUTCLK_TX_ESC), + /* mipitx DPHY */ + JH71X0_GATE(JH7110_VOUTCLK_MIPITX_DPHY_TXESC, "mipitx_dphy_txesc", 0, + JH7110_VOUTCLK_TX_ESC), + /* hdmi */ + JH71X0_GATE(JH7110_VOUTCLK_HDMI_TX_MCLK, "hdmi_tx_mclk", 0, + JH7110_VOUTCLK_VOUT_TOP_HDMITX0_MCLK), + JH71X0_GATE(JH7110_VOUTCLK_HDMI_TX_BCLK, "hdmi_tx_bclk", 0, + JH7110_VOUTCLK_I2STX0_BCLK), + JH71X0_GATE(JH7110_VOUTCLK_HDMI_TX_SYS, "hdmi_tx_sys", 0, JH7110_VOUTCLK_APB), +}; + +static struct vout_top_crg *top_crg_from(void __iomem **base) +{ + return container_of(base, struct vout_top_crg, base); +} + +static int jh7110_vout_top_crg_init(struct jh71x0_clk_priv *priv, struct vout_top_crg *top) +{ + struct reset_control *top_rst; + int ret; + + top->top_clks = jh7110_vout_top_clks; + top->top_clks_num = ARRAY_SIZE(jh7110_vout_top_clks); + ret = devm_clk_bulk_get(priv->dev, top->top_clks_num, top->top_clks); + if (ret) + return dev_err_probe(priv->dev, ret, "failed to get top clocks\n"); + + /* The reset should be shared and other Vout modules will use its. */ + top_rst = devm_reset_control_get_shared(priv->dev, NULL); + if (IS_ERR(top_rst)) + return dev_err_probe(priv->dev, PTR_ERR(top_rst), "failed to get top reset\n"); + + ret = clk_bulk_prepare_enable(top->top_clks_num, top->top_clks); + if (ret) + return dev_err_probe(priv->dev, ret, "failed to enable top clocks\n"); + + return reset_control_deassert(top_rst); +} + +static struct clk_hw *jh7110_voutclk_get(struct of_phandle_args *clkspec, void *data) +{ + struct jh71x0_clk_priv *priv = data; + unsigned int idx = clkspec->args[0]; + + if (idx < JH7110_VOUTCLK_END) + return &priv->reg[idx].hw; + + return ERR_PTR(-EINVAL); +} + +static int jh7110_voutcrg_probe(struct platform_device *pdev) +{ + struct jh71x0_clk_priv *priv; + struct vout_top_crg *top; + unsigned int idx; + int ret; + + priv = devm_kzalloc(&pdev->dev, + struct_size(priv, reg, JH7110_VOUTCLK_END), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + top = devm_kzalloc(&pdev->dev, sizeof(*top), GFP_KERNEL); + if (!top) + return -ENOMEM; + + spin_lock_init(&priv->rmw_lock); + priv->dev = &pdev->dev; + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + pm_runtime_enable(priv->dev); + ret = pm_runtime_get_sync(priv->dev); + if (ret < 0) + return dev_err_probe(priv->dev, ret, "failed to turn on power\n"); + + ret = jh7110_vout_top_crg_init(priv, top); + if (ret) + goto err_clk; + + top->base = priv->base; + dev_set_drvdata(priv->dev, (void *)(&top->base)); + + for (idx = 0; idx < JH7110_VOUTCLK_END; idx++) { + u32 max = jh7110_voutclk_data[idx].max; + struct clk_parent_data parents[4] = {}; + struct clk_init_data init = { + .name = jh7110_voutclk_data[idx].name, + .ops = starfive_jh71x0_clk_ops(max), + .parent_data = parents, + .num_parents = + ((max & JH71X0_CLK_MUX_MASK) >> JH71X0_CLK_MUX_SHIFT) + 1, + .flags = jh7110_voutclk_data[idx].flags, + }; + struct jh71x0_clk *clk = &priv->reg[idx]; + unsigned int i; + const char *fw_name[JH7110_VOUTCLK_EXT_END - JH7110_VOUTCLK_END] = { + "vout_src", + "vout_top_ahb", + "vout_top_axi", + "vout_top_hdmitx0_mclk", + "i2stx0_bclk", + "hdmitx0_pixelclk" + }; + + for (i = 0; i < init.num_parents; i++) { + unsigned int pidx = jh7110_voutclk_data[idx].parents[i]; + + if (pidx < JH7110_VOUTCLK_END) + parents[i].hw = &priv->reg[pidx].hw; + else if (pidx < JH7110_VOUTCLK_EXT_END) + parents[i].fw_name = fw_name[pidx - JH7110_VOUTCLK_END]; + } + + clk->hw.init = &init; + clk->idx = idx; + clk->max_div = max & JH71X0_CLK_DIV_MASK; + + ret = devm_clk_hw_register(&pdev->dev, &clk->hw); + if (ret) + goto err_exit; + } + + ret = devm_of_clk_add_hw_provider(&pdev->dev, jh7110_voutclk_get, priv); + if (ret) + goto err_exit; + + ret = jh7110_reset_controller_register(priv, "reset-vout", 4); + if (ret) + goto err_exit; + + return 0; + +err_exit: + clk_bulk_disable_unprepare(top->top_clks_num, top->top_clks); +err_clk: + pm_runtime_put_sync(priv->dev); + pm_runtime_disable(priv->dev); + return ret; +} + +static int jh7110_voutcrg_remove(struct platform_device *pdev) +{ + void __iomem **base = dev_get_drvdata(&pdev->dev); + struct vout_top_crg *top = top_crg_from(base); + + clk_bulk_disable_unprepare(top->top_clks_num, top->top_clks); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct of_device_id jh7110_voutcrg_match[] = { + { .compatible = "starfive,jh7110-voutcrg" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jh7110_voutcrg_match); + +static struct platform_driver jh7110_voutcrg_driver = { + .probe = jh7110_voutcrg_probe, + .remove = jh7110_voutcrg_remove, + .driver = { + .name = "clk-starfive-jh7110-vout", + .of_match_table = jh7110_voutcrg_match, + }, +}; +module_platform_driver(jh7110_voutcrg_driver); + +MODULE_AUTHOR("Xingyu Wu "); +MODULE_DESCRIPTION("StarFive JH7110 Video-Output clock driver"); +MODULE_LICENSE("GPL"); From 51232aa3ed6af78257378e2f616a2e0b6c0382d7 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Tue, 14 Mar 2023 20:44:03 +0800 Subject: [PATCH 34/95] riscv: dts: starfive: jh7110: Add DVP and HDMI TX pixel external clocks Add DVP and HDMI TX pixel external fixed clocks and the rates are 74.25MHz and 297MHz. Signed-off-by: Xingyu Wu --- .../dts/starfive/jh7110-starfive-visionfive-2.dtsi | 8 ++++++++ arch/riscv/boot/dts/starfive/jh7110.dtsi | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi index 2a6d81609284c..1155b97b593df 100644 --- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi @@ -38,6 +38,10 @@ }; }; +&dvp_clk { + clock-frequency = <74250000>; +}; + &gmac0_rgmii_rxin { clock-frequency = <125000000>; }; @@ -54,6 +58,10 @@ clock-frequency = <50000000>; }; +&hdmitx0_pixelclk { + clock-frequency = <297000000>; +}; + &i2srx_bclk_ext { clock-frequency = <12288000>; }; diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi index cd7a6a1196f89..46c468536266a 100644 --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi @@ -173,6 +173,12 @@ }; }; + dvp_clk: dvp-clock { + compatible = "fixed-clock"; + clock-output-names = "dvp_clk"; + #clock-cells = <0>; + }; + gmac0_rgmii_rxin: gmac0-rgmii-rxin-clock { compatible = "fixed-clock"; clock-output-names = "gmac0_rgmii_rxin"; @@ -197,6 +203,12 @@ #clock-cells = <0>; }; + hdmitx0_pixelclk: hdmitx0-pixel-clock { + compatible = "fixed-clock"; + clock-output-names = "hdmitx0_pixelclk"; + #clock-cells = <0>; + }; + i2srx_bclk_ext: i2srx-bclk-ext-clock { compatible = "fixed-clock"; clock-output-names = "i2srx_bclk_ext"; From 2e2604464b7145ad7cad818723919ddf3073ccd4 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Tue, 25 Oct 2022 14:48:25 +0800 Subject: [PATCH 35/95] riscv: dts: starfive: jh7110: Add STGCRG/ISPCRG/VOUTCRG nodes Add STGCRG/ISPCRG/VOUTCRG new node to support JH7110 System-Top-Group, Image-Signal-Process and Video-Output clock and reset drivers for the JH7110 RISC-V SoC. Signed-off-by: Xingyu Wu Reviewed-by: Conor Dooley --- arch/riscv/boot/dts/starfive/jh7110.dtsi | 55 ++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi index 46c468536266a..196dc14c14d4d 100644 --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi @@ -6,6 +6,7 @@ /dts-v1/; #include +#include #include / { @@ -374,6 +375,25 @@ status = "disabled"; }; + stgcrg: clock-controller@10230000 { + compatible = "starfive,jh7110-stgcrg"; + reg = <0x0 0x10230000 0x0 0x10000>; + clocks = <&osc>, + <&syscrg JH7110_SYSCLK_HIFI4_CORE>, + <&syscrg JH7110_SYSCLK_STG_AXIAHB>, + <&syscrg JH7110_SYSCLK_USB_125M>, + <&syscrg JH7110_SYSCLK_CPU_BUS>, + <&syscrg JH7110_SYSCLK_HIFI4_AXI>, + <&syscrg JH7110_SYSCLK_NOCSTG_BUS>, + <&syscrg JH7110_SYSCLK_APB_BUS>; + clock-names = "osc", "hifi4_core", + "stg_axiahb", "usb_125m", + "cpu_bus", "hifi4_axi", + "nocstg_bus", "apb_bus"; + #clock-cells = <1>; + #reset-cells = <1>; + }; + stg_syscon: syscon@10240000 { compatible = "starfive,jh7110-stg-syscon", "syscon"; reg = <0x0 0x10240000 0x0 0x1000>; @@ -539,5 +559,40 @@ interrupts = <111>; #power-domain-cells = <1>; }; + + ispcrg: clock-controller@19810000 { + compatible = "starfive,jh7110-ispcrg"; + reg = <0x0 0x19810000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_ISP_TOP_CORE>, + <&syscrg JH7110_SYSCLK_ISP_TOP_AXI>, + <&syscrg JH7110_SYSCLK_NOC_BUS_ISP_AXI>, + <&dvp_clk>; + clock-names = "isp_top_core", "isp_top_axi", + "noc_bus_isp_axi", "dvp_clk"; + resets = <&syscrg JH7110_SYSRST_ISP_TOP>, + <&syscrg JH7110_SYSRST_ISP_TOP_AXI>, + <&syscrg JH7110_SYSRST_NOC_BUS_ISP_AXI>; + #clock-cells = <1>; + #reset-cells = <1>; + power-domains = <&pwrc JH7110_PD_ISP>; + }; + + voutcrg: clock-controller@295c0000 { + compatible = "starfive,jh7110-voutcrg"; + reg = <0x0 0x295c0000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_VOUT_SRC>, + <&syscrg JH7110_SYSCLK_VOUT_TOP_AHB>, + <&syscrg JH7110_SYSCLK_VOUT_TOP_AXI>, + <&syscrg JH7110_SYSCLK_VOUT_TOP_HDMITX0_MCLK>, + <&syscrg JH7110_SYSCLK_I2STX0_BCLK>, + <&hdmitx0_pixelclk>; + clock-names = "vout_src", "vout_top_ahb", + "vout_top_axi", "vout_top_hdmitx0_mclk", + "i2stx0_bclk", "hdmitx0_pixelclk"; + resets = <&syscrg JH7110_SYSRST_VOUT_TOP_SRC>; + #clock-cells = <1>; + #reset-cells = <1>; + power-domains = <&pwrc JH7110_PD_VOUT>; + }; }; }; From 496b3dcaafe8f0c7296fb22900926857d8ff974c Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Thu, 16 Mar 2023 11:05:09 +0800 Subject: [PATCH 36/95] dt-bindings: clock: Add StarFive JH7110 PLL clock generator Add bindings for the PLL clock generator on the JH7110 RISC-V SoC. Signed-off-by: Xingyu Wu Reviewed-by: Krzysztof Kozlowski --- .../bindings/clock/starfive,jh7110-pll.yaml | 46 +++++++++++++++++++ .../dt-bindings/clock/starfive,jh7110-crg.h | 6 +++ 2 files changed, 52 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/starfive,jh7110-pll.yaml diff --git a/Documentation/devicetree/bindings/clock/starfive,jh7110-pll.yaml b/Documentation/devicetree/bindings/clock/starfive,jh7110-pll.yaml new file mode 100644 index 0000000000000..9397516f60ba6 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/starfive,jh7110-pll.yaml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/starfive,jh7110-pll.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 PLL Clock Generator + +description: + This PLL are high speed, low jitter frequency synthesizers in JH7110. + Each PLL clocks work in integer mode or fraction mode by some dividers, + and the configuration registers and dividers are set in several syscon + registers. So pll node should be a child of SYS-SYSCON node. + The formula for calculating frequency is that, + Fvco = Fref * (NI + NF) / M / Q1 + +maintainers: + - Xingyu Wu + +properties: + compatible: + const: starfive,jh7110-pll + + clocks: + maxItems: 1 + description: Main Oscillator (24 MHz) + + '#clock-cells': + const: 1 + description: + See for valid indices. + +required: + - compatible + - clocks + - '#clock-cells' + +additionalProperties: false + +examples: + - | + pllclk: pll-clock-controller { + compatible = "starfive,jh7110-pll"; + clocks = <&osc>; + #clock-cells = <1>; + }; diff --git a/include/dt-bindings/clock/starfive,jh7110-crg.h b/include/dt-bindings/clock/starfive,jh7110-crg.h index 0cd963a3a39c5..06f41cd9fc8a3 100644 --- a/include/dt-bindings/clock/starfive,jh7110-crg.h +++ b/include/dt-bindings/clock/starfive,jh7110-crg.h @@ -7,6 +7,12 @@ #ifndef __DT_BINDINGS_CLOCK_STARFIVE_JH7110_CRG_H__ #define __DT_BINDINGS_CLOCK_STARFIVE_JH7110_CRG_H__ +/* PLL clocks */ +#define JH7110_CLK_PLL0_OUT 0 +#define JH7110_CLK_PLL1_OUT 1 +#define JH7110_CLK_PLL2_OUT 2 +#define JH7110_PLLCLK_END 3 + /* SYSCRG clocks */ #define JH7110_SYSCLK_CPU_ROOT 0 #define JH7110_SYSCLK_CPU_CORE 1 From 770a9bd572a34189286b9a38e3b405096582eb51 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Thu, 16 Mar 2023 11:05:10 +0800 Subject: [PATCH 37/95] clk: starfive: Add StarFive JH7110 PLL clock driver Add driver for the StarFive JH7110 PLL clock controller. Signed-off-by: Xingyu Wu --- drivers/clk/starfive/Kconfig | 8 + drivers/clk/starfive/Makefile | 1 + .../clk/starfive/clk-starfive-jh7110-pll.c | 420 ++++++++++++++++++ .../clk/starfive/clk-starfive-jh7110-pll.h | 293 ++++++++++++ 4 files changed, 722 insertions(+) create mode 100644 drivers/clk/starfive/clk-starfive-jh7110-pll.c create mode 100644 drivers/clk/starfive/clk-starfive-jh7110-pll.h diff --git a/drivers/clk/starfive/Kconfig b/drivers/clk/starfive/Kconfig index b0c7744965d7b..abe1de1eafa78 100644 --- a/drivers/clk/starfive/Kconfig +++ b/drivers/clk/starfive/Kconfig @@ -21,6 +21,14 @@ config CLK_STARFIVE_JH7100_AUDIO Say Y or M here to support the audio clocks on the StarFive JH7100 SoC. +config CLK_STARFIVE_JH7110_PLL + bool "StarFive JH7110 PLL clock support" + depends on ARCH_STARFIVE || COMPILE_TEST + default ARCH_STARFIVE + help + Say yes here to support the PLL clock controller on the + StarFive JH7110 SoC. + config CLK_STARFIVE_JH7110_SYS bool "StarFive JH7110 system clock support" depends on ARCH_STARFIVE || COMPILE_TEST diff --git a/drivers/clk/starfive/Makefile b/drivers/clk/starfive/Makefile index 841377e45bb6f..199ac0f37a2f6 100644 --- a/drivers/clk/starfive/Makefile +++ b/drivers/clk/starfive/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_CLK_STARFIVE_JH71X0) += clk-starfive-jh71x0.o obj-$(CONFIG_CLK_STARFIVE_JH7100) += clk-starfive-jh7100.o obj-$(CONFIG_CLK_STARFIVE_JH7100_AUDIO) += clk-starfive-jh7100-audio.o +obj-$(CONFIG_CLK_STARFIVE_JH7110_PLL) += clk-starfive-jh7110-pll.o obj-$(CONFIG_CLK_STARFIVE_JH7110_SYS) += clk-starfive-jh7110-sys.o obj-$(CONFIG_CLK_STARFIVE_JH7110_AON) += clk-starfive-jh7110-aon.o obj-$(CONFIG_CLK_STARFIVE_JH7110_STG) += clk-starfive-jh7110-stg.o diff --git a/drivers/clk/starfive/clk-starfive-jh7110-pll.c b/drivers/clk/starfive/clk-starfive-jh7110-pll.c new file mode 100644 index 0000000000000..b947861065db5 --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh7110-pll.c @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JH7110 PLL Clock Generator Driver + * + * Copyright (C) 2022 StarFive Technology Co., Ltd. + * Copyright (C) 2022 Xingyu Wu + * + * This driver is about to register JH7110 PLL clock generator and support ops. + * The JH7110 have three PLL clock, PLL0, PLL1 and PLL2. + * Each PLL clocks work in integer mode or fraction mode by some dividers, + * and the configuration registers and dividers are set in several syscon registers. + * The formula for calculating frequency is: + * Fvco = Fref * (NI + NF) / M / Q1 + * Fref: OSC source clock rate + * NI: integer frequency dividing ratio of feedback divider, set by fbdiv[11:0]. + * NF: fractional frequency dividing ratio, set by frac[23:0]. NF = frac[23:0] / 2^24 = 0 ~ 0.999. + * M: frequency dividing ratio of pre-divider, set by prediv[5:0]. + * Q1: frequency dividing ratio of post divider, set by postdiv1[1:0], Q1= 1,2,4,8. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "clk-starfive-jh7110-pll.h" + +static struct jh7110_clk_pll_data *jh7110_pll_data_from(struct clk_hw *hw) +{ + return container_of(hw, struct jh7110_clk_pll_data, hw); +} + +static struct jh7110_clk_pll_priv *jh7110_pll_priv_from(struct jh7110_clk_pll_data *data) +{ + return container_of(data, struct jh7110_clk_pll_priv, data[data->idx]); +} + +/* Read register value from syscon and calculate PLL(x) frequency */ +static unsigned long jh7110_pll_get_freq(struct jh7110_clk_pll_data *data, + unsigned long parent_rate) +{ + struct jh7110_clk_pll_priv *priv = jh7110_pll_priv_from(data); + struct jh7110_pll_syscon_offset *offset = &data->offset; + struct jh7110_pll_syscon_mask *mask = &data->mask; + struct jh7110_pll_syscon_shift *shift = &data->shift; + unsigned long freq = 0; + unsigned long frac_cal; + u32 dacpd; + u32 dsmpd; + u32 fbdiv; + u32 prediv; + u32 postdiv1; + u32 frac; + u32 reg_value; + + if (regmap_read(priv->syscon_regmap, offset->dacpd, ®_value)) + goto read_register_error; + dacpd = (reg_value & mask->dacpd) >> shift->dacpd; + + if (regmap_read(priv->syscon_regmap, offset->dsmpd, ®_value)) + goto read_register_error; + dsmpd = (reg_value & mask->dsmpd) >> shift->dsmpd; + + if (regmap_read(priv->syscon_regmap, offset->fbdiv, ®_value)) + goto read_register_error; + fbdiv = (reg_value & mask->fbdiv) >> shift->fbdiv; + /* fbdiv value should be 8 to 4095 */ + if (fbdiv < 8) + goto read_register_error; + + if (regmap_read(priv->syscon_regmap, offset->prediv, ®_value)) + goto read_register_error; + prediv = (reg_value & mask->prediv) >> shift->prediv; + + if (regmap_read(priv->syscon_regmap, offset->postdiv1, ®_value)) + goto read_register_error; + /* postdiv1 = 2 ^ reg_value */ + postdiv1 = 1 << ((reg_value & mask->postdiv1) >> shift->postdiv1); + + if (regmap_read(priv->syscon_regmap, offset->frac, ®_value)) + goto read_register_error; + frac = (reg_value & mask->frac) >> shift->frac; + + /* + * Integer Mode (Both 1) or Fraction Mode (Both 0). + * And the decimal places are counted by expanding them by + * a factor of STARFIVE_PLL_FRAC_PATR_SIZE. + */ + if (dacpd == 1 && dsmpd == 1) + frac_cal = 0; + else if (dacpd == 0 && dsmpd == 0) + frac_cal = (unsigned long)frac * STARFIVE_PLL_FRAC_PATR_SIZE / (1 << 24); + else + goto read_register_error; + + if (frac_cal) + /* fraction mode: Fvco = Fref * (NI + NF) / M / Q1 */ + freq = parent_rate / STARFIVE_PLL_FRAC_PATR_SIZE * + (fbdiv * STARFIVE_PLL_FRAC_PATR_SIZE + frac_cal) / prediv / postdiv1; + else + /* integer mode: Fvco = Fref * NI / M / Q1 */ + freq = parent_rate * fbdiv / prediv / postdiv1; + +read_register_error: + return freq; +} + +/* Select the appropriate frequency from the already configured registers value */ +static int jh7110_pll_select_freq_syscon(struct jh7110_clk_pll_data *data, + unsigned long target_rate) +{ + struct jh7110_clk_pll_priv *priv = jh7110_pll_priv_from(data); + const struct starfive_pll_syscon_value *syscon_value; + unsigned int id; + unsigned int pll_arry_size; + + if (data->idx == JH7110_CLK_PLL0_OUT) + pll_arry_size = ARRAY_SIZE(jh7110_pll0_syscon_freq); + else if (data->idx == JH7110_CLK_PLL1_OUT) + pll_arry_size = ARRAY_SIZE(jh7110_pll1_syscon_freq); + else + pll_arry_size = ARRAY_SIZE(jh7110_pll2_syscon_freq); + + for (id = 0; id < pll_arry_size; id++) { + if (data->idx == JH7110_CLK_PLL0_OUT) + syscon_value = &jh7110_pll0_syscon_freq[id]; + else if (data->idx == JH7110_CLK_PLL1_OUT) + syscon_value = &jh7110_pll1_syscon_freq[id]; + else + syscon_value = &jh7110_pll2_syscon_freq[id]; + + if (target_rate == syscon_value->freq) + goto select_succeed; + } + + dev_err(priv->dev, "pll%d frequency:%ld do not match, please check it.\n", + data->idx, target_rate); + return -EINVAL; + +select_succeed: + data->freq_select_idx = id; + return 0; +} + +static int jh7110_pll_set_freq_syscon(struct jh7110_clk_pll_data *data) +{ + struct jh7110_clk_pll_priv *priv = jh7110_pll_priv_from(data); + struct jh7110_pll_syscon_offset *offset = &data->offset; + struct jh7110_pll_syscon_mask *mask = &data->mask; + struct jh7110_pll_syscon_shift *shift = &data->shift; + unsigned int freq_idx = data->freq_select_idx; + const struct starfive_pll_syscon_value *syscon_value; + int ret; + + if (data->idx == JH7110_CLK_PLL0_OUT) + syscon_value = &jh7110_pll0_syscon_freq[freq_idx]; + else if (data->idx == JH7110_CLK_PLL1_OUT) + syscon_value = &jh7110_pll1_syscon_freq[freq_idx]; + else + syscon_value = &jh7110_pll2_syscon_freq[freq_idx]; + + ret = regmap_update_bits(priv->syscon_regmap, offset->dacpd, mask->dacpd, + (syscon_value->dacpd << shift->dacpd)); + if (ret) + goto set_failed; + + ret = regmap_update_bits(priv->syscon_regmap, offset->dsmpd, mask->dsmpd, + (syscon_value->dsmpd << shift->dsmpd)); + if (ret) + goto set_failed; + + ret = regmap_update_bits(priv->syscon_regmap, offset->prediv, mask->prediv, + (syscon_value->prediv << shift->prediv)); + if (ret) + goto set_failed; + + ret = regmap_update_bits(priv->syscon_regmap, offset->fbdiv, mask->fbdiv, + (syscon_value->fbdiv << shift->fbdiv)); + if (ret) + goto set_failed; + + ret = regmap_update_bits(priv->syscon_regmap, offset->postdiv1, mask->postdiv1, + ((syscon_value->postdiv1 >> 1) << shift->postdiv1)); + if (ret) + goto set_failed; + + /* frac: Integer Mode (Both 1) or Fraction Mode (Both 0) */ + if (syscon_value->dacpd == 0 && syscon_value->dsmpd == 0) + ret = regmap_update_bits(priv->syscon_regmap, offset->frac, mask->frac, + (syscon_value->frac << shift->frac)); + else if (syscon_value->dacpd != syscon_value->dsmpd) + ret = -EINVAL; + +set_failed: + return ret; +} + +static unsigned long jh7110_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct jh7110_clk_pll_data *data = jh7110_pll_data_from(hw); + + return jh7110_pll_get_freq(data, parent_rate); +} + +static int jh7110_pll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) +{ + struct jh7110_clk_pll_data *data = jh7110_pll_data_from(hw); + int ret = jh7110_pll_select_freq_syscon(data, req->rate); + + if (ret) + return ret; + + if (data->idx == JH7110_CLK_PLL0_OUT) + req->rate = jh7110_pll0_syscon_freq[data->freq_select_idx].freq; + else if (data->idx == JH7110_CLK_PLL1_OUT) + req->rate = jh7110_pll1_syscon_freq[data->freq_select_idx].freq; + else + req->rate = jh7110_pll2_syscon_freq[data->freq_select_idx].freq; + + return 0; +} + +static int jh7110_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct jh7110_clk_pll_data *data = jh7110_pll_data_from(hw); + + return jh7110_pll_set_freq_syscon(data); +} + +#ifdef CONFIG_DEBUG_FS +static void jh7110_pll_debug_init(struct clk_hw *hw, struct dentry *dentry) +{ + static const struct debugfs_reg32 jh7110_clk_pll_reg = { + .name = "CTRL", + .offset = 0, + }; + struct jh7110_clk_pll_data *data = jh7110_pll_data_from(hw); + struct jh7110_clk_pll_priv *priv = jh7110_pll_priv_from(data); + struct debugfs_regset32 *regset; + + regset = devm_kzalloc(priv->dev, sizeof(*regset), GFP_KERNEL); + if (!regset) + return; + + regset->regs = &jh7110_clk_pll_reg; + regset->nregs = 1; + + debugfs_create_regset32("registers", 0400, dentry, regset); +} +#else +#define jh7110_pll_debug_init NULL +#endif + +static const struct clk_ops jh7110_pll_ops = { + .recalc_rate = jh7110_pll_recalc_rate, + .determine_rate = jh7110_pll_determine_rate, + .set_rate = jh7110_pll_set_rate, + .debug_init = jh7110_pll_debug_init, +}; + +/* get offset, mask and shift of PLL(x) syscon */ +static int jh7110_pll_data_get(struct jh7110_clk_pll_data *data, int index) +{ + struct jh7110_pll_syscon_offset *offset = &data->offset; + struct jh7110_pll_syscon_mask *mask = &data->mask; + struct jh7110_pll_syscon_shift *shift = &data->shift; + + if (index == JH7110_CLK_PLL0_OUT) { + offset->dacpd = STARFIVE_JH7110_PLL0_DACPD_OFFSET; + offset->dsmpd = STARFIVE_JH7110_PLL0_DSMPD_OFFSET; + offset->fbdiv = STARFIVE_JH7110_PLL0_FBDIV_OFFSET; + offset->frac = STARFIVE_JH7110_PLL0_FRAC_OFFSET; + offset->prediv = STARFIVE_JH7110_PLL0_PREDIV_OFFSET; + offset->postdiv1 = STARFIVE_JH7110_PLL0_POSTDIV1_OFFSET; + + mask->dacpd = STARFIVE_JH7110_PLL0_DACPD_MASK; + mask->dsmpd = STARFIVE_JH7110_PLL0_DSMPD_MASK; + mask->fbdiv = STARFIVE_JH7110_PLL0_FBDIV_MASK; + mask->frac = STARFIVE_JH7110_PLL0_FRAC_MASK; + mask->prediv = STARFIVE_JH7110_PLL0_PREDIV_MASK; + mask->postdiv1 = STARFIVE_JH7110_PLL0_POSTDIV1_MASK; + + shift->dacpd = STARFIVE_JH7110_PLL0_DACPD_SHIFT; + shift->dsmpd = STARFIVE_JH7110_PLL0_DSMPD_SHIFT; + shift->fbdiv = STARFIVE_JH7110_PLL0_FBDIV_SHIFT; + shift->frac = STARFIVE_JH7110_PLL0_FRAC_SHIFT; + shift->prediv = STARFIVE_JH7110_PLL0_PREDIV_SHIFT; + shift->postdiv1 = STARFIVE_JH7110_PLL0_POSTDIV1_SHIFT; + + } else if (index == JH7110_CLK_PLL1_OUT) { + offset->dacpd = STARFIVE_JH7110_PLL1_DACPD_OFFSET; + offset->dsmpd = STARFIVE_JH7110_PLL1_DSMPD_OFFSET; + offset->fbdiv = STARFIVE_JH7110_PLL1_FBDIV_OFFSET; + offset->frac = STARFIVE_JH7110_PLL1_FRAC_OFFSET; + offset->prediv = STARFIVE_JH7110_PLL1_PREDIV_OFFSET; + offset->postdiv1 = STARFIVE_JH7110_PLL1_POSTDIV1_OFFSET; + + mask->dacpd = STARFIVE_JH7110_PLL1_DACPD_MASK; + mask->dsmpd = STARFIVE_JH7110_PLL1_DSMPD_MASK; + mask->fbdiv = STARFIVE_JH7110_PLL1_FBDIV_MASK; + mask->frac = STARFIVE_JH7110_PLL1_FRAC_MASK; + mask->prediv = STARFIVE_JH7110_PLL1_PREDIV_MASK; + mask->postdiv1 = STARFIVE_JH7110_PLL1_POSTDIV1_MASK; + + shift->dacpd = STARFIVE_JH7110_PLL1_DACPD_SHIFT; + shift->dsmpd = STARFIVE_JH7110_PLL1_DSMPD_SHIFT; + shift->fbdiv = STARFIVE_JH7110_PLL1_FBDIV_SHIFT; + shift->frac = STARFIVE_JH7110_PLL1_FRAC_SHIFT; + shift->prediv = STARFIVE_JH7110_PLL1_PREDIV_SHIFT; + shift->postdiv1 = STARFIVE_JH7110_PLL1_POSTDIV1_SHIFT; + + } else if (index == JH7110_CLK_PLL2_OUT) { + offset->dacpd = STARFIVE_JH7110_PLL2_DACPD_OFFSET; + offset->dsmpd = STARFIVE_JH7110_PLL2_DSMPD_OFFSET; + offset->fbdiv = STARFIVE_JH7110_PLL2_FBDIV_OFFSET; + offset->frac = STARFIVE_JH7110_PLL2_FRAC_OFFSET; + offset->prediv = STARFIVE_JH7110_PLL2_PREDIV_OFFSET; + offset->postdiv1 = STARFIVE_JH7110_PLL2_POSTDIV1_OFFSET; + + mask->dacpd = STARFIVE_JH7110_PLL2_DACPD_MASK; + mask->dsmpd = STARFIVE_JH7110_PLL2_DSMPD_MASK; + mask->fbdiv = STARFIVE_JH7110_PLL2_FBDIV_MASK; + mask->frac = STARFIVE_JH7110_PLL2_FRAC_MASK; + mask->prediv = STARFIVE_JH7110_PLL2_PREDIV_MASK; + mask->postdiv1 = STARFIVE_JH7110_PLL2_POSTDIV1_MASK; + + shift->dacpd = STARFIVE_JH7110_PLL2_DACPD_SHIFT; + shift->dsmpd = STARFIVE_JH7110_PLL2_DSMPD_SHIFT; + shift->fbdiv = STARFIVE_JH7110_PLL2_FBDIV_SHIFT; + shift->frac = STARFIVE_JH7110_PLL2_FRAC_SHIFT; + shift->prediv = STARFIVE_JH7110_PLL2_PREDIV_SHIFT; + shift->postdiv1 = STARFIVE_JH7110_PLL2_POSTDIV1_SHIFT; + + } else { + return -ENOENT; + } + + return 0; +} + +static struct clk_hw *jh7110_pll_get(struct of_phandle_args *clkspec, void *data) +{ + struct jh7110_clk_pll_priv *priv = data; + unsigned int idx = clkspec->args[0]; + + if (idx < JH7110_PLLCLK_END) + return &priv->data[idx].hw; + + return ERR_PTR(-EINVAL); +} + +static int jh7110_pll_probe(struct platform_device *pdev) +{ + const char *pll_name[JH7110_PLLCLK_END] = { + "pll0_out", + "pll1_out", + "pll2_out" + }; + struct jh7110_clk_pll_priv *priv; + struct jh7110_clk_pll_data *data; + int ret; + unsigned int idx; + + priv = devm_kzalloc(&pdev->dev, struct_size(priv, data, JH7110_PLLCLK_END), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &pdev->dev; + priv->syscon_regmap = syscon_node_to_regmap(priv->dev->of_node->parent); + if (IS_ERR(priv->syscon_regmap)) + return PTR_ERR(priv->syscon_regmap); + + for (idx = 0; idx < JH7110_PLLCLK_END; idx++) { + struct clk_parent_data parents = { + .index = 0, + }; + struct clk_init_data init = { + .name = pll_name[idx], + .ops = &jh7110_pll_ops, + .parent_data = &parents, + .num_parents = 1, + .flags = 0, + }; + + data = &priv->data[idx]; + + ret = jh7110_pll_data_get(data, idx); + if (ret) + return ret; + + data->hw.init = &init; + data->idx = idx; + + ret = devm_clk_hw_register(&pdev->dev, &data->hw); + if (ret) + return ret; + } + + return devm_of_clk_add_hw_provider(&pdev->dev, jh7110_pll_get, priv); +} + +static const struct of_device_id jh7110_pll_match[] = { + { .compatible = "starfive,jh7110-pll" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jh7110_pll_match); + +static struct platform_driver jh7110_pll_driver = { + .driver = { + .name = "clk-starfive-jh7110-pll", + .of_match_table = jh7110_pll_match, + }, +}; +builtin_platform_driver_probe(jh7110_pll_driver, jh7110_pll_probe); diff --git a/drivers/clk/starfive/clk-starfive-jh7110-pll.h b/drivers/clk/starfive/clk-starfive-jh7110-pll.h new file mode 100644 index 0000000000000..3deb35f144dc1 --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh7110-pll.h @@ -0,0 +1,293 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * StarFive JH7110 PLL Clock Generator Driver + * + * Copyright (C) 2022 Xingyu Wu + */ + +#ifndef _CLK_STARFIVE_JH7110_PLL_H_ +#define _CLK_STARFIVE_JH7110_PLL_H_ + +#include + +/* The decimal places are counted by expanding them by a factor of STARFIVE_PLL_FRAC_PATR_SIZE. */ +#define STARFIVE_PLL_FRAC_PATR_SIZE 1000 + +#define STARFIVE_JH7110_PLL0_DACPD_OFFSET 0x18 +#define STARFIVE_JH7110_PLL0_DACPD_SHIFT 24 +#define STARFIVE_JH7110_PLL0_DACPD_MASK BIT(24) +#define STARFIVE_JH7110_PLL0_DSMPD_OFFSET 0x18 +#define STARFIVE_JH7110_PLL0_DSMPD_SHIFT 25 +#define STARFIVE_JH7110_PLL0_DSMPD_MASK BIT(25) +#define STARFIVE_JH7110_PLL0_FBDIV_OFFSET 0x1c +#define STARFIVE_JH7110_PLL0_FBDIV_SHIFT 0 +#define STARFIVE_JH7110_PLL0_FBDIV_MASK GENMASK(11, 0) +#define STARFIVE_JH7110_PLL0_FRAC_OFFSET 0x20 +#define STARFIVE_JH7110_PLL0_FRAC_SHIFT 0 +#define STARFIVE_JH7110_PLL0_FRAC_MASK GENMASK(23, 0) +#define STARFIVE_JH7110_PLL0_POSTDIV1_OFFSET 0x20 +#define STARFIVE_JH7110_PLL0_POSTDIV1_SHIFT 28 +#define STARFIVE_JH7110_PLL0_POSTDIV1_MASK GENMASK(29, 28) +#define STARFIVE_JH7110_PLL0_PREDIV_OFFSET 0x24 +#define STARFIVE_JH7110_PLL0_PREDIV_SHIFT 0 +#define STARFIVE_JH7110_PLL0_PREDIV_MASK GENMASK(5, 0) + +#define STARFIVE_JH7110_PLL1_DACPD_OFFSET 0x24 +#define STARFIVE_JH7110_PLL1_DACPD_SHIFT 15 +#define STARFIVE_JH7110_PLL1_DACPD_MASK BIT(15) +#define STARFIVE_JH7110_PLL1_DSMPD_OFFSET 0x24 +#define STARFIVE_JH7110_PLL1_DSMPD_SHIFT 16 +#define STARFIVE_JH7110_PLL1_DSMPD_MASK BIT(16) +#define STARFIVE_JH7110_PLL1_FBDIV_OFFSET 0x24 +#define STARFIVE_JH7110_PLL1_FBDIV_SHIFT 17 +#define STARFIVE_JH7110_PLL1_FBDIV_MASK GENMASK(28, 17) +#define STARFIVE_JH7110_PLL1_FRAC_OFFSET 0x28 +#define STARFIVE_JH7110_PLL1_FRAC_SHIFT 0 +#define STARFIVE_JH7110_PLL1_FRAC_MASK GENMASK(23, 0) +#define STARFIVE_JH7110_PLL1_POSTDIV1_OFFSET 0x28 +#define STARFIVE_JH7110_PLL1_POSTDIV1_SHIFT 28 +#define STARFIVE_JH7110_PLL1_POSTDIV1_MASK GENMASK(29, 28) +#define STARFIVE_JH7110_PLL1_PREDIV_OFFSET 0x2c +#define STARFIVE_JH7110_PLL1_PREDIV_SHIFT 0 +#define STARFIVE_JH7110_PLL1_PREDIV_MASK GENMASK(5, 0) + +#define STARFIVE_JH7110_PLL2_DACPD_OFFSET 0x2c +#define STARFIVE_JH7110_PLL2_DACPD_SHIFT 15 +#define STARFIVE_JH7110_PLL2_DACPD_MASK BIT(15) +#define STARFIVE_JH7110_PLL2_DSMPD_OFFSET 0x2c +#define STARFIVE_JH7110_PLL2_DSMPD_SHIFT 16 +#define STARFIVE_JH7110_PLL2_DSMPD_MASK BIT(16) +#define STARFIVE_JH7110_PLL2_FBDIV_OFFSET 0x2c +#define STARFIVE_JH7110_PLL2_FBDIV_SHIFT 17 +#define STARFIVE_JH7110_PLL2_FBDIV_MASK GENMASK(28, 17) +#define STARFIVE_JH7110_PLL2_FRAC_OFFSET 0x30 +#define STARFIVE_JH7110_PLL2_FRAC_SHIFT 0 +#define STARFIVE_JH7110_PLL2_FRAC_MASK GENMASK(23, 0) +#define STARFIVE_JH7110_PLL2_POSTDIV1_OFFSET 0x30 +#define STARFIVE_JH7110_PLL2_POSTDIV1_SHIFT 28 +#define STARFIVE_JH7110_PLL2_POSTDIV1_MASK GENMASK(29, 28) +#define STARFIVE_JH7110_PLL2_PREDIV_OFFSET 0x34 +#define STARFIVE_JH7110_PLL2_PREDIV_SHIFT 0 +#define STARFIVE_JH7110_PLL2_PREDIV_MASK GENMASK(5, 0) + +struct jh7110_pll_syscon_offset { + unsigned int dacpd; + unsigned int dsmpd; + unsigned int fbdiv; + unsigned int frac; + unsigned int prediv; + unsigned int postdiv1; +}; + +struct jh7110_pll_syscon_mask { + u32 dacpd; + u32 dsmpd; + u32 fbdiv; + u32 frac; + u32 prediv; + u32 postdiv1; +}; + +struct jh7110_pll_syscon_shift { + char dacpd; + char dsmpd; + char fbdiv; + char frac; + char prediv; + char postdiv1; +}; + +struct jh7110_clk_pll_data { + struct clk_hw hw; + unsigned int idx; + unsigned int freq_select_idx; + + struct jh7110_pll_syscon_offset offset; + struct jh7110_pll_syscon_mask mask; + struct jh7110_pll_syscon_shift shift; +}; + +struct jh7110_clk_pll_priv { + struct device *dev; + struct regmap *syscon_regmap; + struct jh7110_clk_pll_data data[]; +}; + +struct starfive_pll_syscon_value { + unsigned long freq; + u32 prediv; + u32 fbdiv; + u32 postdiv1; +/* Both daxpd and dsmpd set 1 while integer multiple mode */ +/* Both daxpd and dsmpd set 0 while fraction multiple mode */ + u32 dacpd; + u32 dsmpd; +/* frac value should be decimals multiplied by 2^24 */ + u32 frac; +}; + +enum starfive_pll0_freq_index { + PLL0_FREQ_375 = 0, + PLL0_FREQ_500, + PLL0_FREQ_625, + PLL0_FREQ_750, + PLL0_FREQ_875, + PLL0_FREQ_1000, + PLL0_FREQ_1250, + PLL0_FREQ_1375, + PLL0_FREQ_1500, + PLL0_FREQ_MAX +}; + +enum starfive_pll1_freq_index { + PLL1_FREQ_1066 = 0, + PLL1_FREQ_1200, + PLL1_FREQ_1400, + PLL1_FREQ_1600, + PLL1_FREQ_MAX +}; + +enum starfive_pll2_freq_index { + PLL2_FREQ_1188 = 0, + PLL2_FREQ_12288, + PLL2_FREQ_MAX +}; + +/* + * Because the pll frequency is relatively fixed, + * it cannot be set arbitrarily, so it needs a specific configuration. + * PLL0 frequency should be multiple of 125MHz (USB frequency). + */ +static const struct starfive_pll_syscon_value + jh7110_pll0_syscon_freq[] = { + [PLL0_FREQ_375] = { + .freq = 375000000, + .prediv = 8, + .fbdiv = 125, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, + [PLL0_FREQ_500] = { + .freq = 500000000, + .prediv = 6, + .fbdiv = 125, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, + [PLL0_FREQ_625] = { + .freq = 625000000, + .prediv = 24, + .fbdiv = 625, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, + [PLL0_FREQ_750] = { + .freq = 750000000, + .prediv = 4, + .fbdiv = 125, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, + [PLL0_FREQ_875] = { + .freq = 875000000, + .prediv = 24, + .fbdiv = 875, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, + [PLL0_FREQ_1000] = { + .freq = 1000000000, + .prediv = 3, + .fbdiv = 125, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, + [PLL0_FREQ_1250] = { + .freq = 1250000000, + .prediv = 12, + .fbdiv = 625, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, + [PLL0_FREQ_1375] = { + .freq = 1375000000, + .prediv = 24, + .fbdiv = 1375, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, + [PLL0_FREQ_1500] = { + .freq = 1500000000, + .prediv = 2, + .fbdiv = 125, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, +}; + +static const struct starfive_pll_syscon_value + jh7110_pll1_syscon_freq[] = { + [PLL1_FREQ_1066] = { + .freq = 1066000000, + .prediv = 12, + .fbdiv = 533, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, + [PLL1_FREQ_1200] = { + .freq = 1200000000, + .prediv = 1, + .fbdiv = 50, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, + [PLL1_FREQ_1400] = { + .freq = 1400000000, + .prediv = 6, + .fbdiv = 350, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, + [PLL1_FREQ_1600] = { + .freq = 1600000000, + .prediv = 3, + .fbdiv = 200, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, +}; + +static const struct starfive_pll_syscon_value + jh7110_pll2_syscon_freq[] = { + [PLL2_FREQ_1188] = { + .freq = 1188000000, + .prediv = 2, + .fbdiv = 99, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, + [PLL2_FREQ_12288] = { + .freq = 1228800000, + .prediv = 5, + .fbdiv = 256, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, +}; + +#endif From a15b8dbc657f4726e36a443eaf2174d2809cff26 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Thu, 16 Mar 2023 11:05:11 +0800 Subject: [PATCH 38/95] dt-bindings: soc: starfive: syscon: Add optional patternProperties Add optional compatible and patternProperties. Signed-off-by: Xingyu Wu --- .../soc/starfive/starfive,jh7110-syscon.yaml | 39 ++++++++++++++++--- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml b/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml index ae7f1d6916af4..b61d8921ef42d 100644 --- a/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml +++ b/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml @@ -15,16 +15,31 @@ description: | properties: compatible: - items: - - enum: - - starfive,jh7110-aon-syscon - - starfive,jh7110-stg-syscon - - starfive,jh7110-sys-syscon - - const: syscon + oneOf: + - items: + - enum: + - starfive,jh7110-aon-syscon + - starfive,jh7110-stg-syscon + - starfive,jh7110-sys-syscon + - const: syscon + - items: + - enum: + - starfive,jh7110-aon-syscon + - starfive,jh7110-stg-syscon + - starfive,jh7110-sys-syscon + - const: syscon + - const: simple-mfd reg: maxItems: 1 +patternProperties: + # Optional children + "pll-clock-controller": + type: object + $ref: /schemas/clock/starfive,jh7110-pll.yaml# + description: Clock provider for PLL. + required: - compatible - reg @@ -38,4 +53,16 @@ examples: reg = <0x10240000 0x1000>; }; + - | + syscon@13030000 { + compatible = "starfive,jh7110-sys-syscon", "syscon", "simple-mfd"; + reg = <0x13030000 0x1000>; + + pll-clock-controller { + compatible = "starfive,jh7110-pll"; + clocks = <&osc>; + #clock-cells = <1>; + }; + }; + ... From d4c14c17c2e402ced5caeb10fa779077b2f4b456 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Thu, 16 Mar 2023 11:05:12 +0800 Subject: [PATCH 39/95] dt-bindings: clock: jh7110-syscrg: Add PLL clock inputs Add PLL clock inputs from PLL clock generator. Signed-off-by: Xingyu Wu Acked-by: Krzysztof Kozlowski --- .../clock/starfive,jh7110-syscrg.yaml | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/clock/starfive,jh7110-syscrg.yaml b/Documentation/devicetree/bindings/clock/starfive,jh7110-syscrg.yaml index 84373ae31644e..55d4e7f09cd5a 100644 --- a/Documentation/devicetree/bindings/clock/starfive,jh7110-syscrg.yaml +++ b/Documentation/devicetree/bindings/clock/starfive,jh7110-syscrg.yaml @@ -27,6 +27,9 @@ properties: - description: External I2S RX left/right channel clock - description: External TDM clock - description: External audio master clock + - description: PLL0 + - description: PLL1 + - description: PLL2 - items: - description: Main Oscillator (24 MHz) @@ -38,6 +41,9 @@ properties: - description: External I2S RX left/right channel clock - description: External TDM clock - description: External audio master clock + - description: PLL0 + - description: PLL1 + - description: PLL2 clock-names: oneOf: @@ -52,6 +58,9 @@ properties: - const: i2srx_lrck_ext - const: tdm_ext - const: mclk_ext + - const: pll0_out + - const: pll1_out + - const: pll2_out - items: - const: osc @@ -63,6 +72,9 @@ properties: - const: i2srx_lrck_ext - const: tdm_ext - const: mclk_ext + - const: pll0_out + - const: pll1_out + - const: pll2_out '#clock-cells': const: 1 @@ -93,12 +105,16 @@ examples: <&gmac1_rgmii_rxin>, <&i2stx_bclk_ext>, <&i2stx_lrck_ext>, <&i2srx_bclk_ext>, <&i2srx_lrck_ext>, - <&tdm_ext>, <&mclk_ext>; + <&tdm_ext>, <&mclk_ext>, + <&pllclk JH7110_CLK_PLL0_OUT>, + <&pllclk JH7110_CLK_PLL1_OUT>, + <&pllclk JH7110_CLK_PLL2_OUT>; clock-names = "osc", "gmac1_rmii_refin", "gmac1_rgmii_rxin", "i2stx_bclk_ext", "i2stx_lrck_ext", "i2srx_bclk_ext", "i2srx_lrck_ext", - "tdm_ext", "mclk_ext"; + "tdm_ext", "mclk_ext", + "pll0_out", "pll1_out", "pll2_out"; #clock-cells = <1>; #reset-cells = <1>; }; From 3503fa9acc9c1b88a85f6bef41d4c2d6ed81e4d7 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Thu, 16 Mar 2023 11:05:13 +0800 Subject: [PATCH 40/95] clk: starfive: jh7110-sys: Modify PLL clocks source Modify PLL clocks source to be got from dts instead of the fixed factor clocks. Signed-off-by: Xingyu Wu --- drivers/clk/starfive/Kconfig | 1 + .../clk/starfive/clk-starfive-jh7110-sys.c | 35 ++++--------------- 2 files changed, 7 insertions(+), 29 deletions(-) diff --git a/drivers/clk/starfive/Kconfig b/drivers/clk/starfive/Kconfig index abe1de1eafa78..6818be51c0675 100644 --- a/drivers/clk/starfive/Kconfig +++ b/drivers/clk/starfive/Kconfig @@ -35,6 +35,7 @@ config CLK_STARFIVE_JH7110_SYS select AUXILIARY_BUS select CLK_STARFIVE_JH71X0 select RESET_STARFIVE_JH7110 + select CLK_STARFIVE_JH7110_PLL default ARCH_STARFIVE help Say yes here to support the system clock controller on the diff --git a/drivers/clk/starfive/clk-starfive-jh7110-sys.c b/drivers/clk/starfive/clk-starfive-jh7110-sys.c index f98a508969c3b..ab82a49179086 100644 --- a/drivers/clk/starfive/clk-starfive-jh7110-sys.c +++ b/drivers/clk/starfive/clk-starfive-jh7110-sys.c @@ -328,9 +328,6 @@ static struct clk_hw *jh7110_sysclk_get(struct of_phandle_args *clkspec, void *d if (idx < JH7110_SYSCLK_END) return &priv->reg[idx].hw; - if (idx >= JH7110_SYSCLK_PLL0_OUT && idx <= JH7110_SYSCLK_PLL2_OUT) - return priv->pll[idx - JH7110_SYSCLK_PLL0_OUT]; - return ERR_PTR(-EINVAL); } @@ -354,29 +351,6 @@ static int __init jh7110_syscrg_probe(struct platform_device *pdev) dev_set_drvdata(priv->dev, (void *)(&priv->base)); - /* - * These PLL clocks are not actually fixed factor clocks and can be - * controlled by the syscon registers of JH7110. They will be dropped - * and registered in the PLL clock driver instead. - */ - /* 24MHz -> 1000.0MHz */ - priv->pll[0] = devm_clk_hw_register_fixed_factor(priv->dev, "pll0_out", - "osc", 0, 125, 3); - if (IS_ERR(priv->pll[0])) - return PTR_ERR(priv->pll[0]); - - /* 24MHz -> 1066.0MHz */ - priv->pll[1] = devm_clk_hw_register_fixed_factor(priv->dev, "pll1_out", - "osc", 0, 533, 12); - if (IS_ERR(priv->pll[1])) - return PTR_ERR(priv->pll[1]); - - /* 24MHz -> 1188.0MHz */ - priv->pll[2] = devm_clk_hw_register_fixed_factor(priv->dev, "pll2_out", - "osc", 0, 99, 2); - if (IS_ERR(priv->pll[2])) - return PTR_ERR(priv->pll[2]); - for (idx = 0; idx < JH7110_SYSCLK_END; idx++) { u32 max = jh7110_sysclk_data[idx].max; struct clk_parent_data parents[4] = {}; @@ -414,9 +388,12 @@ static int __init jh7110_syscrg_probe(struct platform_device *pdev) parents[i].fw_name = "tdm_ext"; else if (pidx == JH7110_SYSCLK_MCLK_EXT) parents[i].fw_name = "mclk_ext"; - else if (pidx >= JH7110_SYSCLK_PLL0_OUT && - pidx <= JH7110_SYSCLK_PLL2_OUT) - parents[i].hw = priv->pll[pidx - JH7110_SYSCLK_PLL0_OUT]; + else if (pidx == JH7110_SYSCLK_PLL0_OUT) + parents[i].fw_name = "pll0_out"; + else if (pidx == JH7110_SYSCLK_PLL1_OUT) + parents[i].fw_name = "pll1_out"; + else if (pidx == JH7110_SYSCLK_PLL2_OUT) + parents[i].fw_name = "pll2_out"; } clk->hw.init = &init; From e7f175ccafdb386aeb71fad05e8bcf15fe7fae40 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Thu, 16 Mar 2023 11:05:14 +0800 Subject: [PATCH 41/95] riscv: dts: starfive: jh7110: Add PLL clock node and modify syscrg node Add the PLL clock node for the Starfive JH7110 SoC and modify the SYSCRG node to add PLL clocks. Signed-off-by: Xingyu Wu --- arch/riscv/boot/dts/starfive/jh7110.dtsi | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi index 196dc14c14d4d..7e0de0ab59628 100644 --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi @@ -493,19 +493,29 @@ <&gmac1_rgmii_rxin>, <&i2stx_bclk_ext>, <&i2stx_lrck_ext>, <&i2srx_bclk_ext>, <&i2srx_lrck_ext>, - <&tdm_ext>, <&mclk_ext>; + <&tdm_ext>, <&mclk_ext>, + <&pllclk JH7110_CLK_PLL0_OUT>, + <&pllclk JH7110_CLK_PLL1_OUT>, + <&pllclk JH7110_CLK_PLL2_OUT>; clock-names = "osc", "gmac1_rmii_refin", "gmac1_rgmii_rxin", "i2stx_bclk_ext", "i2stx_lrck_ext", "i2srx_bclk_ext", "i2srx_lrck_ext", - "tdm_ext", "mclk_ext"; + "tdm_ext", "mclk_ext", + "pll0_out", "pll1_out", "pll2_out"; #clock-cells = <1>; #reset-cells = <1>; }; sys_syscon: syscon@13030000 { - compatible = "starfive,jh7110-sys-syscon", "syscon"; + compatible = "starfive,jh7110-sys-syscon", "syscon", "simple-mfd"; reg = <0x0 0x13030000 0x0 0x1000>; + + pllclk: pll-clock-controller { + compatible = "starfive,jh7110-pll"; + clocks = <&osc>; + #clock-cells = <1>; + }; }; sysgpio: pinctrl@13040000 { From ab783659b35c5a5bb122c5da32578667c8c4ee3f Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Wed, 2 Nov 2022 16:48:26 +0800 Subject: [PATCH 42/95] dt-bindings: watchdog: Add watchdog for StarFive JH7100 and JH7110 Add bindings to describe the watchdog for the StarFive JH7100/JH7110 SoC. And Use JH7100 as first StarFive SoC with watchdog. Signed-off-by: Xingyu Wu Reviewed-by: Krzysztof Kozlowski --- .../watchdog/starfive,jh7100-wdt.yaml | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 Documentation/devicetree/bindings/watchdog/starfive,jh7100-wdt.yaml diff --git a/Documentation/devicetree/bindings/watchdog/starfive,jh7100-wdt.yaml b/Documentation/devicetree/bindings/watchdog/starfive,jh7100-wdt.yaml new file mode 100644 index 0000000000000..68f3f6fd08a62 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/starfive,jh7100-wdt.yaml @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/watchdog/starfive,jh7100-wdt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive Watchdog for JH7100 and JH7110 SoC + +maintainers: + - Xingyu Wu + - Samin Guo + +description: + The JH7100 and JH7110 watchdog both are 32 bit counters. JH7100 watchdog + has only one timeout phase and reboots. And JH7110 watchdog has two + timeout phases. At the first phase, the signal of watchdog interrupt + output(WDOGINT) will rise when counter is 0. The counter will reload + the timeout value. And then, if counter decreases to 0 again and WDOGINT + isn't cleared, the watchdog will reset the system unless the watchdog + reset is disabled. + +allOf: + - $ref: watchdog.yaml# + +properties: + compatible: + enum: + - starfive,jh7100-wdt + - starfive,jh7110-wdt + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: APB clock + - description: Core clock + + clock-names: + items: + - const: apb + - const: core + + resets: + items: + - description: APB reset + - description: Core reset + +required: + - compatible + - reg + - clocks + - clock-names + - resets + +unevaluatedProperties: false + +examples: + - | + watchdog@12480000 { + compatible = "starfive,jh7100-wdt"; + reg = <0x12480000 0x10000>; + clocks = <&clk 171>, + <&clk 172>; + clock-names = "apb", "core"; + resets = <&rst 99>, + <&rst 100>; + }; From 213dc21062c0540c64881a4d1fe9bf4a89aa9711 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Thu, 3 Nov 2022 10:29:12 +0800 Subject: [PATCH 43/95] drivers: watchdog: Add StarFive Watchdog driver Add watchdog driver for the StarFive JH7100 and JH7110 SoC. Signed-off-by: Xingyu Wu --- MAINTAINERS | 7 + drivers/watchdog/Kconfig | 11 + drivers/watchdog/Makefile | 3 + drivers/watchdog/starfive-wdt.c | 606 ++++++++++++++++++++++++++++++++ 4 files changed, 627 insertions(+) create mode 100644 drivers/watchdog/starfive-wdt.c diff --git a/MAINTAINERS b/MAINTAINERS index 60b1bee91d121..0ff645e7754d3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19961,6 +19961,13 @@ S: Supported F: Documentation/devicetree/bindings/rng/starfive* F: drivers/char/hw_random/jh7110-trng.c +STARFIVE WATCHDOG DRIVER +M: Xingyu Wu +M: Samin Guo +S: Supported +F: Documentation/devicetree/bindings/watchdog/starfive* +F: drivers/watchdog/starfive-wdt.c + STATIC BRANCH/CALL M: Peter Zijlstra M: Josh Poimboeuf diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index f0872970daf9a..f22138709bf5a 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1999,6 +1999,17 @@ config WATCHDOG_RTAS To compile this driver as a module, choose M here. The module will be called wdrtas. +# RISC-V Architecture + +config STARFIVE_WATCHDOG + tristate "StarFive Watchdog support" + depends on ARCH_STARFIVE || COMPILE_TEST + select WATCHDOG_CORE + default ARCH_STARFIVE + help + Say Y here to support the watchdog of StarFive JH7100 and JH7110 + SoC. This driver can also be built as a module if choose M. + # S390 Architecture config DIAG288_WATCHDOG diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 9cbf6580f16c9..b4c4ccf2d7038 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -192,6 +192,9 @@ obj-$(CONFIG_MEN_A21_WDT) += mena21_wdt.o obj-$(CONFIG_PSERIES_WDT) += pseries-wdt.o obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o +# RISC-V Architecture +obj-$(CONFIG_STARFIVE_WATCHDOG) += starfive-wdt.o + # S390 Architecture obj-$(CONFIG_DIAG288_WATCHDOG) += diag288_wdt.o diff --git a/drivers/watchdog/starfive-wdt.c b/drivers/watchdog/starfive-wdt.c new file mode 100644 index 0000000000000..1995cceca51e4 --- /dev/null +++ b/drivers/watchdog/starfive-wdt.c @@ -0,0 +1,606 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Starfive Watchdog driver + * + * Copyright (C) 2022 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* JH7100 Watchdog register define */ +#define STARFIVE_WDT_JH7100_INTSTAUS 0x000 +#define STARFIVE_WDT_JH7100_CONTROL 0x104 +#define STARFIVE_WDT_JH7100_LOAD 0x108 +#define STARFIVE_WDT_JH7100_EN 0x110 +#define STARFIVE_WDT_JH7100_RELOAD 0x114 /* Write 0 or 1 to reload preset value */ +#define STARFIVE_WDT_JH7100_VALUE 0x118 +#define STARFIVE_WDT_JH7100_INTCLR 0x120 /* + * [0]: Write 1 to clear interrupt + * [1]: 1 mean clearing and 0 mean complete + * [31:2]: reserved. + */ +#define STARFIVE_WDT_JH7100_LOCK 0x13c /* write 0x378f0765 to unlock */ + +/* JH7110 Watchdog register define */ +#define STARFIVE_WDT_JH7110_LOAD 0x000 +#define STARFIVE_WDT_JH7110_VALUE 0x004 +#define STARFIVE_WDT_JH7110_CONTROL 0x008 /* + * [0]: reset enable; + * [1]: interrupt enable && watchdog enable + * [31:2]: reserved. + */ +#define STARFIVE_WDT_JH7110_INTCLR 0x00c /* clear intterupt and reload the counter */ +#define STARFIVE_WDT_JH7110_IMS 0x014 +#define STARFIVE_WDT_JH7110_LOCK 0xc00 /* write 0x1ACCE551 to unlock */ + +/* WDOGCONTROL */ +#define STARFIVE_WDT_ENABLE 0x1 +#define STARFIVE_WDT_EN_SHIFT 0 +#define STARFIVE_WDT_RESET_EN 0x1 +#define STARFIVE_WDT_JH7100_RST_EN_SHIFT 0 +#define STARFIVE_WDT_JH7110_RST_EN_SHIFT 1 + +/* WDOGLOCK */ +#define STARFIVE_WDT_JH7100_UNLOCK_KEY 0x378f0765 +#define STARFIVE_WDT_JH7110_UNLOCK_KEY 0x1acce551 + +/* WDOGINTCLR */ +#define STARFIVE_WDT_INTCLR 0x1 +#define STARFIVE_WDT_JH7100_INTCLR_AVA_SHIFT 1 /* Watchdog can clear interrupt when 0 */ + +#define STARFIVE_WDT_MAXCNT 0xffffffff +#define STARFIVE_WDT_DEFAULT_TIME (15) +#define STARFIVE_WDT_DELAY_US 0 +#define STARFIVE_WDT_TIMEOUT_US 10000 + +/* module parameter */ +#define STARFIVE_WDT_EARLY_ENA 0 + +static bool nowayout = WATCHDOG_NOWAYOUT; +static int heartbeat; +static bool early_enable = STARFIVE_WDT_EARLY_ENA; + +module_param(heartbeat, int, 0); +module_param(early_enable, bool, 0); +module_param(nowayout, bool, 0); + +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (default=" + __MODULE_STRING(STARFIVE_WDT_DEFAULT_TIME) ")"); +MODULE_PARM_DESC(early_enable, + "Watchdog is started at boot time if set to 1, default=" + __MODULE_STRING(STARFIVE_WDT_EARLY_ENA)); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +struct starfive_wdt_variant { + unsigned int control; /* Watchdog Control Resgister for reset enable */ + unsigned int load; /* Watchdog Load register */ + unsigned int reload; /* Watchdog Reload Control register */ + unsigned int enable; /* Watchdog Enable Register */ + unsigned int value; /* Watchdog Counter Value Register */ + unsigned int int_clr; /* Watchdog Interrupt Clear Register */ + unsigned int unlock; /* Watchdog Lock Register */ + unsigned int int_status; /* Watchdog Interrupt Status Register */ + + u32 unlock_key; + char enrst_shift; + char en_shift; + bool intclr_check; /* whether need to check it before clearing interrupt */ + char intclr_ava_shift; + bool double_timeout; /* The watchdog need twice timeout to reboot */ +}; + +struct starfive_wdt { + struct watchdog_device wdd; + spinlock_t lock; /* spinlock for register handling */ + void __iomem *base; + struct clk *core_clk; + struct clk *apb_clk; + const struct starfive_wdt_variant *variant; + unsigned long freq; + u32 count; /* count of timeout */ + u32 reload; /* restore the count */ +}; + +/* Register layout and configuration for the JH7100 */ +static const struct starfive_wdt_variant starfive_wdt_jh7100_variant = { + .control = STARFIVE_WDT_JH7100_CONTROL, + .load = STARFIVE_WDT_JH7100_LOAD, + .reload = STARFIVE_WDT_JH7100_RELOAD, + .enable = STARFIVE_WDT_JH7100_EN, + .value = STARFIVE_WDT_JH7100_VALUE, + .int_clr = STARFIVE_WDT_JH7100_INTCLR, + .unlock = STARFIVE_WDT_JH7100_LOCK, + .unlock_key = STARFIVE_WDT_JH7100_UNLOCK_KEY, + .int_status = STARFIVE_WDT_JH7100_INTSTAUS, + .enrst_shift = STARFIVE_WDT_JH7100_RST_EN_SHIFT, + .en_shift = STARFIVE_WDT_EN_SHIFT, + .intclr_check = true, + .intclr_ava_shift = STARFIVE_WDT_JH7100_INTCLR_AVA_SHIFT, + .double_timeout = false, +}; + +/* Register layout and configuration for the JH7110 */ +static const struct starfive_wdt_variant starfive_wdt_jh7110_variant = { + .control = STARFIVE_WDT_JH7110_CONTROL, + .load = STARFIVE_WDT_JH7110_LOAD, + .enable = STARFIVE_WDT_JH7110_CONTROL, + .value = STARFIVE_WDT_JH7110_VALUE, + .int_clr = STARFIVE_WDT_JH7110_INTCLR, + .unlock = STARFIVE_WDT_JH7110_LOCK, + .unlock_key = STARFIVE_WDT_JH7110_UNLOCK_KEY, + .int_status = STARFIVE_WDT_JH7110_IMS, + .enrst_shift = STARFIVE_WDT_JH7110_RST_EN_SHIFT, + .en_shift = STARFIVE_WDT_EN_SHIFT, + .intclr_check = false, + .double_timeout = true, +}; + +static int starfive_wdt_enable_clock(struct starfive_wdt *wdt) +{ + int ret; + + ret = clk_prepare_enable(wdt->apb_clk); + if (ret) + return dev_err_probe(wdt->wdd.parent, ret, "failed to enable apb clock\n"); + + ret = clk_prepare_enable(wdt->core_clk); + if (ret) + return dev_err_probe(wdt->wdd.parent, ret, "failed to enable core clock\n"); + + return 0; +} + +static void starfive_wdt_disable_clock(struct starfive_wdt *wdt) +{ + clk_disable_unprepare(wdt->core_clk); + clk_disable_unprepare(wdt->apb_clk); +} + +static inline int starfive_wdt_get_clock(struct starfive_wdt *wdt) +{ + struct device *dev = wdt->wdd.parent; + + wdt->apb_clk = devm_clk_get(dev, "apb"); + if (IS_ERR(wdt->apb_clk)) + return dev_err_probe(dev, PTR_ERR(wdt->apb_clk), "failed to get apb clock\n"); + + wdt->core_clk = devm_clk_get(dev, "core"); + if (IS_ERR(wdt->core_clk)) + return dev_err_probe(dev, PTR_ERR(wdt->core_clk), "failed to get core clock\n"); + + return 0; +} + +static inline int starfive_wdt_reset_init(struct device *dev) +{ + struct reset_control *rsts; + int ret; + + rsts = devm_reset_control_array_get_exclusive(dev); + if (IS_ERR(rsts)) + return dev_err_probe(dev, PTR_ERR(rsts), "failed to get resets\n"); + + ret = reset_control_deassert(rsts); + if (ret) + return dev_err_probe(dev, ret, "failed to deassert resets\n"); + + return 0; +} + +static u32 starfive_wdt_ticks_to_sec(struct starfive_wdt *wdt, u32 ticks) +{ + return DIV_ROUND_CLOSEST(ticks, wdt->freq); +} + +/* Write unlock-key to unlock. Write other value to lock. */ +static void starfive_wdt_unlock(struct starfive_wdt *wdt) +{ + spin_lock(&wdt->lock); + writel(wdt->variant->unlock_key, wdt->base + wdt->variant->unlock); +} + +static void starfive_wdt_lock(struct starfive_wdt *wdt) +{ + writel(~wdt->variant->unlock_key, wdt->base + wdt->variant->unlock); + spin_unlock(&wdt->lock); +} + +/* enable watchdog interrupt to reset/reboot */ +static void starfive_wdt_enable_reset(struct starfive_wdt *wdt) +{ + u32 val; + + val = readl(wdt->base + wdt->variant->control); + val |= STARFIVE_WDT_RESET_EN << wdt->variant->enrst_shift; + writel(val, wdt->base + wdt->variant->control); +} + +/* interrupt status whether has been raised from the counter */ +static bool starfive_wdt_raise_irq_status(struct starfive_wdt *wdt) +{ + return !!readl(wdt->base + wdt->variant->int_status); +} + +/* waiting interrupt can be free to clear */ +static int starfive_wdt_wait_int_free(struct starfive_wdt *wdt) +{ + u32 value; + + return readl_poll_timeout_atomic(wdt->base + wdt->variant->int_clr, value, + !(value & BIT(wdt->variant->intclr_ava_shift)), + STARFIVE_WDT_DELAY_US, STARFIVE_WDT_TIMEOUT_US); +} + +/* clear interrupt signal before initialization or reload */ +static int starfive_wdt_int_clr(struct starfive_wdt *wdt) +{ + int ret; + + if (wdt->variant->intclr_check) { + ret = starfive_wdt_wait_int_free(wdt); + if (ret) + return dev_err_probe(wdt->wdd.parent, ret, + "watchdog is not ready to clear interrupt.\n"); + } + writel(STARFIVE_WDT_INTCLR, wdt->base + wdt->variant->int_clr); + + return 0; +} + +static inline void starfive_wdt_set_count(struct starfive_wdt *wdt, u32 val) +{ + writel(val, wdt->base + wdt->variant->load); +} + +static inline u32 starfive_wdt_get_count(struct starfive_wdt *wdt) +{ + return readl(wdt->base + wdt->variant->value); +} + +/* enable watchdog */ +static inline void starfive_wdt_enable(struct starfive_wdt *wdt) +{ + u32 val; + + val = readl(wdt->base + wdt->variant->enable); + val |= STARFIVE_WDT_ENABLE << wdt->variant->en_shift; + writel(val, wdt->base + wdt->variant->enable); +} + +/* disable watchdog */ +static inline void starfive_wdt_disable(struct starfive_wdt *wdt) +{ + u32 val; + + val = readl(wdt->base + wdt->variant->enable); + val &= ~(STARFIVE_WDT_ENABLE << wdt->variant->en_shift); + writel(val, wdt->base + wdt->variant->enable); +} + +static inline void starfive_wdt_set_reload_count(struct starfive_wdt *wdt, u32 count) +{ + starfive_wdt_set_count(wdt, count); + + /* 7100 need set any value to reload register and could reload value to counter */ + if (wdt->variant->reload) + writel(0x1, wdt->base + wdt->variant->reload); +} + +static unsigned int starfive_wdt_max_timeout(struct starfive_wdt *wdt) +{ + if (wdt->variant->double_timeout) + return DIV_ROUND_UP(STARFIVE_WDT_MAXCNT, (wdt->freq / 2)) - 1; + + return DIV_ROUND_UP(STARFIVE_WDT_MAXCNT, wdt->freq) - 1; +} + +static unsigned int starfive_wdt_get_timeleft(struct watchdog_device *wdd) +{ + struct starfive_wdt *wdt = watchdog_get_drvdata(wdd); + u32 count; + + /* + * If the watchdog takes twice timeout and set half count value, + * timeleft value should add the count value before first timeout. + */ + count = starfive_wdt_get_count(wdt); + if (wdt->variant->double_timeout && !starfive_wdt_raise_irq_status(wdt)) + count += wdt->count; + + return starfive_wdt_ticks_to_sec(wdt, count); +} + +static int starfive_wdt_keepalive(struct watchdog_device *wdd) +{ + struct starfive_wdt *wdt = watchdog_get_drvdata(wdd); + int ret; + + starfive_wdt_unlock(wdt); + ret = starfive_wdt_int_clr(wdt); + if (ret) + goto exit; + + starfive_wdt_set_reload_count(wdt, wdt->count); + +exit: + /* exit with releasing spinlock and locking registers */ + starfive_wdt_lock(wdt); + return ret; +} + +static int starfive_wdt_start(struct starfive_wdt *wdt) +{ + int ret; + + starfive_wdt_unlock(wdt); + /* disable watchdog, to be safe */ + starfive_wdt_disable(wdt); + + starfive_wdt_enable_reset(wdt); + ret = starfive_wdt_int_clr(wdt); + if (ret) + goto exit; + + starfive_wdt_set_count(wdt, wdt->count); + starfive_wdt_enable(wdt); + +exit: + starfive_wdt_lock(wdt); + return ret; +} + +static void starfive_wdt_stop(struct starfive_wdt *wdt) +{ + starfive_wdt_unlock(wdt); + starfive_wdt_disable(wdt); + starfive_wdt_lock(wdt); +} + +static int starfive_wdt_pm_start(struct watchdog_device *wdd) +{ + struct starfive_wdt *wdt = watchdog_get_drvdata(wdd); + int ret = pm_runtime_get_sync(wdd->parent); + + if (ret < 0) + return ret; + + return starfive_wdt_start(wdt); +} + +static int starfive_wdt_pm_stop(struct watchdog_device *wdd) +{ + struct starfive_wdt *wdt = watchdog_get_drvdata(wdd); + + starfive_wdt_stop(wdt); + return pm_runtime_put_sync(wdd->parent); +} + +static int starfive_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + struct starfive_wdt *wdt = watchdog_get_drvdata(wdd); + unsigned long count = timeout * wdt->freq; + + /* some watchdogs take two timeouts to reset */ + if (wdt->variant->double_timeout) + count /= 2; + + wdt->count = count; + wdd->timeout = timeout; + + starfive_wdt_unlock(wdt); + starfive_wdt_disable(wdt); + starfive_wdt_set_reload_count(wdt, wdt->count); + starfive_wdt_enable(wdt); + starfive_wdt_lock(wdt); + + return 0; +} + +#define STARFIVE_WDT_OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE) + +static const struct watchdog_info starfive_wdt_info = { + .options = STARFIVE_WDT_OPTIONS, + .identity = "StarFive Watchdog", +}; + +static const struct watchdog_ops starfive_wdt_ops = { + .owner = THIS_MODULE, + .start = starfive_wdt_pm_start, + .stop = starfive_wdt_pm_stop, + .ping = starfive_wdt_keepalive, + .set_timeout = starfive_wdt_set_timeout, + .get_timeleft = starfive_wdt_get_timeleft, +}; + +static int starfive_wdt_probe(struct platform_device *pdev) +{ + struct starfive_wdt *wdt; + int ret; + + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + wdt->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(wdt->base)) + return dev_err_probe(&pdev->dev, PTR_ERR(wdt->base), "error mapping registers\n"); + + wdt->wdd.parent = &pdev->dev; + ret = starfive_wdt_get_clock(wdt); + if (ret) + return ret; + + platform_set_drvdata(pdev, wdt); + pm_runtime_enable(&pdev->dev); + if (pm_runtime_enabled(&pdev->dev)) { + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) + return ret; + } else { + /* runtime PM is disabled but clocks need to be enabled */ + ret = starfive_wdt_enable_clock(wdt); + if (ret) + return ret; + } + + ret = starfive_wdt_reset_init(&pdev->dev); + if (ret) + goto err_exit; + + watchdog_set_drvdata(&wdt->wdd, wdt); + wdt->wdd.info = &starfive_wdt_info; + wdt->wdd.ops = &starfive_wdt_ops; + wdt->variant = of_device_get_match_data(&pdev->dev); + spin_lock_init(&wdt->lock); + + wdt->freq = clk_get_rate(wdt->core_clk); + if (!wdt->freq) { + dev_err(&pdev->dev, "get clock rate failed.\n"); + ret = -EINVAL; + goto err_exit; + } + + wdt->wdd.min_timeout = 1; + wdt->wdd.max_timeout = starfive_wdt_max_timeout(wdt); + wdt->wdd.timeout = STARFIVE_WDT_DEFAULT_TIME; + watchdog_init_timeout(&wdt->wdd, heartbeat, &pdev->dev); + starfive_wdt_set_timeout(&wdt->wdd, wdt->wdd.timeout); + + watchdog_set_nowayout(&wdt->wdd, nowayout); + watchdog_stop_on_reboot(&wdt->wdd); + watchdog_stop_on_unregister(&wdt->wdd); + + if (early_enable) { + ret = starfive_wdt_start(wdt); + if (ret) + goto err_exit; + set_bit(WDOG_HW_RUNNING, &wdt->wdd.status); + } else { + starfive_wdt_stop(wdt); + } + + ret = watchdog_register_device(&wdt->wdd); + if (ret) + goto err_exit; + + if (!early_enable) + return pm_runtime_put_sync(&pdev->dev); + + return 0; + +err_exit: + starfive_wdt_disable_clock(wdt); + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static int starfive_wdt_remove(struct platform_device *pdev) +{ + struct starfive_wdt *wdt = platform_get_drvdata(pdev); + + starfive_wdt_stop(wdt); + watchdog_unregister_device(&wdt->wdd); + + if (pm_runtime_enabled(&pdev->dev)) + pm_runtime_disable(&pdev->dev); + else + /* disable clock without PM */ + starfive_wdt_disable_clock(wdt); + + return 0; +} + +static void starfive_wdt_shutdown(struct platform_device *pdev) +{ + struct starfive_wdt *wdt = platform_get_drvdata(pdev); + + starfive_wdt_pm_stop(&wdt->wdd); +} + +#ifdef CONFIG_PM_SLEEP +static int starfive_wdt_suspend(struct device *dev) +{ + struct starfive_wdt *wdt = dev_get_drvdata(dev); + + /* Save watchdog state, and turn it off. */ + wdt->reload = starfive_wdt_get_count(wdt); + + /* Note that WTCNT doesn't need to be saved. */ + starfive_wdt_stop(wdt); + + return pm_runtime_force_suspend(dev); +} + +static int starfive_wdt_resume(struct device *dev) +{ + struct starfive_wdt *wdt = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + + starfive_wdt_unlock(wdt); + /* Restore watchdog state. */ + starfive_wdt_set_reload_count(wdt, wdt->reload); + starfive_wdt_lock(wdt); + + return starfive_wdt_start(wdt); +} +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_PM +static int starfive_wdt_runtime_suspend(struct device *dev) +{ + struct starfive_wdt *wdt = dev_get_drvdata(dev); + + starfive_wdt_disable_clock(wdt); + + return 0; +} + +static int starfive_wdt_runtime_resume(struct device *dev) +{ + struct starfive_wdt *wdt = dev_get_drvdata(dev); + + return starfive_wdt_enable_clock(wdt); +} +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops starfive_wdt_pm_ops = { + SET_RUNTIME_PM_OPS(starfive_wdt_runtime_suspend, starfive_wdt_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(starfive_wdt_suspend, starfive_wdt_resume) +}; + +static const struct of_device_id starfive_wdt_match[] = { + { .compatible = "starfive,jh7100-wdt", .data = &starfive_wdt_jh7100_variant }, + { .compatible = "starfive,jh7110-wdt", .data = &starfive_wdt_jh7110_variant }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, starfive_wdt_match); + +static struct platform_driver starfive_wdt_driver = { + .probe = starfive_wdt_probe, + .remove = starfive_wdt_remove, + .shutdown = starfive_wdt_shutdown, + .driver = { + .name = "starfive-wdt", + .pm = &starfive_wdt_pm_ops, + .of_match_table = of_match_ptr(starfive_wdt_match), + }, +}; +module_platform_driver(starfive_wdt_driver); + +MODULE_AUTHOR("Xingyu Wu "); +MODULE_AUTHOR("Samin Guo "); +MODULE_DESCRIPTION("StarFive Watchdog Device Driver"); +MODULE_LICENSE("GPL"); From e95a0a470b1231ca7a180ba5f180bae1a1452a68 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Mon, 6 Mar 2023 10:42:07 +0800 Subject: [PATCH 44/95] riscv: dts: starfive: jh7100: Add watchdog node Add watchdog node for the StarFive JH7100 RISC-V SoC. Signed-off-by: Xingyu Wu Reviewed-by: Emil Renner Berthing --- arch/riscv/boot/dts/starfive/jh7100.dtsi | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/riscv/boot/dts/starfive/jh7100.dtsi b/arch/riscv/boot/dts/starfive/jh7100.dtsi index 000447482acaa..4218621ea3b92 100644 --- a/arch/riscv/boot/dts/starfive/jh7100.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7100.dtsi @@ -238,5 +238,15 @@ #size-cells = <0>; status = "disabled"; }; + + watchdog@12480000 { + compatible = "starfive,jh7100-wdt"; + reg = <0x0 0x12480000 0x0 0x10000>; + clocks = <&clkgen JH7100_CLK_WDTIMER_APB>, + <&clkgen JH7100_CLK_WDT_CORE>; + clock-names = "apb", "core"; + resets = <&rstgen JH7100_RSTN_WDTIMER_APB>, + <&rstgen JH7100_RSTN_WDT>; + }; }; }; From 7224e0cce0a94fc8c86ff72d716451233be348b6 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Thu, 3 Nov 2022 10:37:08 +0800 Subject: [PATCH 45/95] riscv: dts: starfive: jh7110: Add watchdog node Add the watchdog node for the Starfive JH7110 SoC. Signed-off-by: Xingyu Wu --- arch/riscv/boot/dts/starfive/jh7110.dtsi | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi index 7e0de0ab59628..3190bcb0ab21c 100644 --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi @@ -530,6 +530,16 @@ #gpio-cells = <2>; }; + wdog: watchdog@13070000 { + compatible = "starfive,jh7110-wdt"; + reg = <0x0 0x13070000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_WDT_APB>, + <&syscrg JH7110_SYSCLK_WDT_CORE>; + clock-names = "apb", "core"; + resets = <&syscrg JH7110_SYSRST_WDT_APB>, + <&syscrg JH7110_SYSRST_WDT_CORE>; + }; + aoncrg: clock-controller@17000000 { compatible = "starfive,jh7110-aoncrg"; reg = <0x0 0x17000000 0x0 0x10000>; From e9efa9983bb60ed838a7e83d88e109e9adae3856 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Tue, 1 Nov 2022 16:50:47 +0800 Subject: [PATCH 46/95] dt-bindings: timer: Add timer for StarFive JH7110 SoC Add bindings for the timer on the JH7110 RISC-V SoC by StarFive Technology Ltd. Signed-off-by: Xingyu Wu --- .../bindings/timer/starfive,jh7110-timer.yaml | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml diff --git a/Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml b/Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml new file mode 100644 index 0000000000000..24b34618f2c8e --- /dev/null +++ b/Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/timer/starfive,jh7110-timer.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 Timer +maintainers: + - Xingyu Wu + - Samin Guo + +description: + This timer has four free-running 32 bit counters in StarFive JH7110 SoC. + And each channel(counter) triggers an interrupt when timeout. They support + one-shot mode and continuous-run mode. + +properties: + compatible: + const: starfive,jh7110-timer + + reg: + maxItems: 1 + + interrupts: + items: + - description: channel 0 + - description: channel 1 + - description: channel 2 + - description: channel 3 + + clocks: + items: + - description: timer APB + - description: channel 0 + - description: channel 1 + - description: channel 2 + - description: channel 3 + + clock-names: + items: + - const: apb + - const: ch0 + - const: ch1 + - const: ch2 + - const: ch3 + + resets: + items: + - description: timer APB + - description: channel 0 + - description: channel 1 + - description: channel 2 + - description: channel 3 + + reset-names: + items: + - const: apb + - const: ch0 + - const: ch1 + - const: ch2 + - const: ch3 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - resets + - reset-names + +additionalProperties: false + +examples: + - | + timer@13050000 { + compatible = "starfive,jh7110-timer"; + reg = <0x13050000 0x10000>; + interrupts = <69>, <70>, <71> ,<72>; + clocks = <&clk 124>, + <&clk 125>, + <&clk 126>, + <&clk 127>, + <&clk 128>; + clock-names = "apb", "ch0", "ch1", + "ch2", "ch3"; + resets = <&rst 117>, + <&rst 118>, + <&rst 119>, + <&rst 120>, + <&rst 121>; + reset-names = "apb", "ch0", "ch1", + "ch2", "ch3"; + }; + From 13eefc428af9d5295929bc0a2cc83958f5dafd29 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Tue, 1 Nov 2022 21:45:06 +0800 Subject: [PATCH 47/95] clocksource: Add StarFive timer driver Add timer driver for the StarFive JH7110 SoC. Signed-off-by: Xingyu Wu --- MAINTAINERS | 7 + drivers/clocksource/Kconfig | 12 + drivers/clocksource/Makefile | 1 + drivers/clocksource/timer-starfive.c | 390 +++++++++++++++++++++++++++ drivers/clocksource/timer-starfive.h | 96 +++++++ 5 files changed, 506 insertions(+) create mode 100644 drivers/clocksource/timer-starfive.c create mode 100644 drivers/clocksource/timer-starfive.h diff --git a/MAINTAINERS b/MAINTAINERS index 0ff645e7754d3..e63c1c7a37050 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19955,6 +19955,13 @@ S: Maintained T: git https://git.kernel.org/pub/scm/linux/kernel/git/conor/linux.git/ F: drivers/soc/starfive/ +STARFIVE TIMER DRIVER +M: Samin Guo +M: Xingyu Wu +S: Supported +F: Documentation/devicetree/bindings/timer/starfive* +F: drivers/clocksource/timer-starfive* + STARFIVE TRNG DRIVER M: Jia Jie Ho S: Supported diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 5fc8f0e7fb382..35daf07d4db7b 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -630,6 +630,18 @@ config RISCV_TIMER is accessed via both the SBI and the rdcycle instruction. This is required for all RISC-V systems. +config STARFIVE_TIMER + bool "Timer for the STARFIVE SoCs" + depends on ARCH_STARFIVE || COMPILE_TEST + select TIMER_OF + select CLKSRC_MMIO + default ARCH_STARFIVE + help + This enables the timer for StarFive SoCs. On RISC-V platform, + the system has started RISCV_TIMER. But you can also use this timer + to do a lot more on StarFive SoCs. This timer can provide high + precision and four channels to use in JH7110 SoC. + config CLINT_TIMER bool "CLINT Timer for the RISC-V platform" if COMPILE_TEST depends on GENERIC_SCHED_CLOCK && RISCV diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 64ab547de97b9..276695d95cdc3 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_INGENIC_TIMER) += ingenic-timer.o obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o obj-$(CONFIG_X86_NUMACHIP) += numachip.o obj-$(CONFIG_RISCV_TIMER) += timer-riscv.o +obj-$(CONFIG_STARFIVE_TIMER) += timer-starfive.o obj-$(CONFIG_CLINT_TIMER) += timer-clint.o obj-$(CONFIG_CSKY_MP_TIMER) += timer-mp-csky.o obj-$(CONFIG_GX6605S_TIMER) += timer-gx6605s.o diff --git a/drivers/clocksource/timer-starfive.c b/drivers/clocksource/timer-starfive.c new file mode 100644 index 0000000000000..53163948ed868 --- /dev/null +++ b/drivers/clocksource/timer-starfive.c @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Starfive Timer driver + * + * Copyright (C) 2022 StarFive Technology Co., Ltd. + * + * Author: + * Xingyu Wu + * Samin Guo + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "timer-starfive.h" + +static const struct starfive_timer_chan_base starfive_timer_jh7110_base = { + .ctrl = STARFIVE_TIMER_JH7110_CTL, + .load = STARFIVE_TIMER_JH7110_LOAD, + .enable = STARFIVE_TIMER_JH7110_ENABLE, + .reload = STARFIVE_TIMER_JH7110_RELOAD, + .value = STARFIVE_TIMER_JH7110_VALUE, + .intclr = STARFIVE_TIMER_JH7110_INT_CLR, + .intmask = STARFIVE_TIMER_JH7110_INT_MASK, + .channel_num = STARFIVE_TIMER_CH_4, + .channel_base = {STARFIVE_TIMER_CH_BASE(0), STARFIVE_TIMER_CH_BASE(1), + STARFIVE_TIMER_CH_BASE(2), STARFIVE_TIMER_CH_BASE(3)}, +}; + +static inline struct starfive_clkevt *to_starfive_clkevt(struct clock_event_device *evt) +{ + return container_of(evt, struct starfive_clkevt, evt); +} + +/* 0:continuous-run mode, 1:single-run mode */ +static inline void starfive_timer_set_mod(struct starfive_clkevt *clkevt, int mod) +{ + writel(mod, clkevt->ctrl); +} + +/* Interrupt Mask Register, 0:Unmask, 1:Mask */ +static inline void starfive_timer_int_enable(struct starfive_clkevt *clkevt) +{ + writel(STARFIVE_TIMER_INTMASK_DIS, clkevt->intmask); +} + +static inline void starfive_timer_int_disable(struct starfive_clkevt *clkevt) +{ + writel(STARFIVE_TIMER_INTMASK_ENA, clkevt->intmask); +} + +/* + * BIT(0): Read value represent channel intr status. + * Write 1 to this bit to clear interrupt. Write 0 has no effects. + * BIT(1): "1" means that it is clearing interrupt. BIT(0) can not be written. + */ +static inline int starfive_timer_int_clear(struct starfive_clkevt *clkevt) +{ + u32 value; + int ret; + + /* waiting interrupt can be to clearing */ + ret = readl_poll_timeout_atomic(clkevt->intclr, value, + !(value & STARFIVE_TIMER_JH7110_INT_CLR_AVA_MASK), + STARFIVE_DELAY_US, STARFIVE_TIMEOUT_US); + if (!ret) + writel(0x1, clkevt->intclr); + + return ret; +} + +/* + * The initial value to be loaded into the + * counter and is also used as the reload value. + * val = clock rate --> 1s + */ +static inline void starfive_timer_set_load(struct starfive_clkevt *clkevt, u32 val) +{ + writel(val, clkevt->load); +} + +static inline u32 starfive_timer_get_val(struct starfive_clkevt *clkevt) +{ + return readl(clkevt->value); +} + +/* + * Write RELOAD register to reload preset value to counter. + * (Write 0 and write 1 are both ok) + */ +static inline void starfive_timer_set_reload(struct starfive_clkevt *clkevt) +{ + writel(0, clkevt->reload); +} + +static inline void starfive_timer_enable(struct starfive_clkevt *clkevt) +{ + writel(STARFIVE_TIMER_ENA, clkevt->enable); +} + +static inline void starfive_timer_disable(struct starfive_clkevt *clkevt) +{ + writel(STARFIVE_TIMER_DIS, clkevt->enable); +} + +static int starfive_timer_int_init_enable(struct starfive_clkevt *clkevt) +{ + int ret; + + starfive_timer_int_disable(clkevt); + ret = starfive_timer_int_clear(clkevt); + if (ret) + return ret; + + starfive_timer_int_enable(clkevt); + starfive_timer_enable(clkevt); + + return 0; +} + +static int starfive_timer_shutdown(struct clock_event_device *evt) +{ + struct starfive_clkevt *clkevt = to_starfive_clkevt(evt); + + starfive_timer_disable(clkevt); + return starfive_timer_int_clear(clkevt); +} + +static void starfive_timer_suspend(struct clock_event_device *evt) +{ + struct starfive_clkevt *clkevt = to_starfive_clkevt(evt); + + clkevt->reload_val = starfive_timer_get_val(clkevt); + starfive_timer_shutdown(evt); +} + +static void starfive_timer_resume(struct clock_event_device *evt) +{ + struct starfive_clkevt *clkevt = to_starfive_clkevt(evt); + + starfive_timer_set_load(clkevt, clkevt->reload_val); + starfive_timer_set_reload(clkevt); + starfive_timer_int_enable(clkevt); + starfive_timer_enable(clkevt); +} + +static int starfive_timer_tick_resume(struct clock_event_device *evt) +{ + starfive_timer_resume(evt); + + return 0; +} + +static int starfive_clocksource_init(struct starfive_clkevt *clkevt) +{ + int ret; + + starfive_timer_set_mod(clkevt, STARFIVE_TIMER_MOD_CONTIN); + starfive_timer_set_load(clkevt, STARFIVE_TIMER_MAX_TICKS); + ret = starfive_timer_int_init_enable(clkevt); + if (ret) + return ret; + + return clocksource_mmio_init(clkevt->value, clkevt->name, clkevt->rate, + STARFIVE_CLOCK_SOURCE_RATING, STARFIVE_VALID_BITS, + clocksource_mmio_readl_down); +} + +/* IRQ handler for the timer */ +static irqreturn_t starfive_timer_interrupt(int irq, void *priv) +{ + struct clock_event_device *evt = (struct clock_event_device *)priv; + struct starfive_clkevt *clkevt = to_starfive_clkevt(evt); + + if (starfive_timer_int_clear(clkevt)) + return IRQ_NONE; + + if (evt->event_handler) + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static int starfive_timer_set_periodic(struct clock_event_device *evt) +{ + struct starfive_clkevt *clkevt = to_starfive_clkevt(evt); + + starfive_timer_disable(clkevt); + starfive_timer_set_mod(clkevt, STARFIVE_TIMER_MOD_CONTIN); + starfive_timer_set_load(clkevt, clkevt->periodic); + + return starfive_timer_int_init_enable(clkevt); +} + +static int starfive_timer_set_oneshot(struct clock_event_device *evt) +{ + struct starfive_clkevt *clkevt = to_starfive_clkevt(evt); + + starfive_timer_disable(clkevt); + starfive_timer_set_mod(clkevt, STARFIVE_TIMER_MOD_SINGLE); + starfive_timer_set_load(clkevt, STARFIVE_TIMER_MAX_TICKS); + + return starfive_timer_int_init_enable(clkevt); +} + +static int starfive_timer_set_next_event(unsigned long next, + struct clock_event_device *evt) +{ + struct starfive_clkevt *clkevt = to_starfive_clkevt(evt); + + starfive_timer_disable(clkevt); + starfive_timer_set_mod(clkevt, STARFIVE_TIMER_MOD_SINGLE); + starfive_timer_set_load(clkevt, next); + starfive_timer_enable(clkevt); + + return 0; +} + +static void starfive_set_clockevent(struct clock_event_device *evt) +{ + evt->features = CLOCK_EVT_FEAT_PERIODIC | + CLOCK_EVT_FEAT_ONESHOT | + CLOCK_EVT_FEAT_DYNIRQ; + evt->set_state_shutdown = starfive_timer_shutdown; + evt->set_state_periodic = starfive_timer_set_periodic; + evt->set_state_oneshot = starfive_timer_set_oneshot; + evt->set_state_oneshot_stopped = starfive_timer_shutdown; + evt->tick_resume = starfive_timer_tick_resume; + evt->set_next_event = starfive_timer_set_next_event; + evt->suspend = starfive_timer_suspend; + evt->resume = starfive_timer_resume; + evt->rating = STARFIVE_CLOCKEVENT_RATING; +} + +static void starfive_clockevents_register(struct starfive_clkevt *clkevt) +{ + clkevt->rate = clk_get_rate(clkevt->clk); + clkevt->periodic = DIV_ROUND_CLOSEST(clkevt->rate, HZ); + + starfive_set_clockevent(&clkevt->evt); + clkevt->evt.name = clkevt->name; + clkevt->evt.irq = clkevt->irq; + clkevt->evt.cpumask = cpu_possible_mask; + + clockevents_config_and_register(&clkevt->evt, clkevt->rate, + STARFIVE_TIMER_MIN_TICKS, STARFIVE_TIMER_MAX_TICKS); +} + +static void __init starfive_clkevt_base_init(const struct starfive_timer_chan_base *timer, + struct starfive_clkevt *clkevt, + void __iomem *base, int ch) +{ + void __iomem *channel_base; + + channel_base = base + timer->channel_base[ch]; + clkevt->base = channel_base; + clkevt->ctrl = channel_base + timer->ctrl; + clkevt->load = channel_base + timer->load; + clkevt->enable = channel_base + timer->enable; + clkevt->reload = channel_base + timer->reload; + clkevt->value = channel_base + timer->value; + clkevt->intclr = channel_base + timer->intclr; + clkevt->intmask = channel_base + timer->intmask; +} + +static int __init starfive_timer_probe(struct platform_device *pdev) +{ + const struct starfive_timer_chan_base *timer_base = of_device_get_match_data(&pdev->dev); + char name[10]; + struct starfive_timer_priv *priv; + struct starfive_clkevt *clkevt; + struct clk *pclk; + struct reset_control *rst; + int ch; + int ret; + + priv = devm_kzalloc(&pdev->dev, struct_size(priv, clkevt, timer_base->channel_num), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->base), + "failed to map registers\n"); + + rst = devm_reset_control_get_exclusive(&pdev->dev, "apb"); + if (IS_ERR(rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(rst), "failed to get apb reset\n"); + + pclk = devm_clk_get_enabled(&pdev->dev, "apb"); + if (IS_ERR(pclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(pclk), + "failed to get & enable apb clock\n"); + + ret = reset_control_deassert(rst); + if (ret) + goto err; + + priv->dev = &pdev->dev; + platform_set_drvdata(pdev, priv); + + for (ch = 0; ch < timer_base->channel_num; ch++) { + clkevt = &priv->clkevt[ch]; + snprintf(name, sizeof(name), "ch%d", ch); + + starfive_clkevt_base_init(timer_base, clkevt, priv->base, ch); + /* Ensure timers are disabled */ + starfive_timer_disable(clkevt); + + rst = devm_reset_control_get_exclusive(&pdev->dev, name); + if (IS_ERR(rst)) { + ret = PTR_ERR(rst); + goto err; + } + + clkevt->clk = devm_clk_get_enabled(&pdev->dev, name); + if (IS_ERR(clkevt->clk)) { + ret = PTR_ERR(clkevt->clk); + goto err; + } + + ret = reset_control_deassert(rst); + if (ret) + goto ch_err; + + clkevt->irq = platform_get_irq(pdev, ch); + if (clkevt->irq < 0) { + ret = clkevt->irq; + goto ch_err; + } + + snprintf(clkevt->name, sizeof(clkevt->name), "%s.ch%d", pdev->name, ch); + starfive_clockevents_register(clkevt); + + ret = devm_request_irq(&pdev->dev, clkevt->irq, starfive_timer_interrupt, + IRQF_TIMER | IRQF_IRQPOLL, + clkevt->name, &clkevt->evt); + if (ret) + goto ch_err; + + ret = starfive_clocksource_init(clkevt); + if (ret) + goto ch_err; + } + + return 0; + +ch_err: + /* Only unregister the failed channel and the rest timer channels continue to work. */ + clk_disable_unprepare(clkevt->clk); +err: + /* If no other channel successfully registers, pclk should be disabled. */ + if (!ch) + clk_disable_unprepare(pclk); + + return ret; +} + +static const struct of_device_id starfive_timer_match[] = { + { .compatible = "starfive,jh7110-timer", .data = &starfive_timer_jh7110_base }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, starfive_timer_match); + +static struct platform_driver starfive_timer_driver = { + .probe = starfive_timer_probe, + .driver = { + .name = "starfive-timer", + .of_match_table = starfive_timer_match, + }, +}; +module_platform_driver(starfive_timer_driver); + +MODULE_AUTHOR("Xingyu Wu "); +MODULE_DESCRIPTION("StarFive timer driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clocksource/timer-starfive.h b/drivers/clocksource/timer-starfive.h new file mode 100644 index 0000000000000..62eb630cc98d6 --- /dev/null +++ b/drivers/clocksource/timer-starfive.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2022 StarFive Technology Co., Ltd. + */ + +#ifndef __STARFIVE_TIMER_H__ +#define __STARFIVE_TIMER_H__ + +/* Bias: Ch0-0x0, Ch1-0x40, Ch2-0x80, and so on. */ +#define STARFIVE_TIMER_CH_LEN 0x40 +#define STARFIVE_TIMER_CH_BASE(x) ((STARFIVE_TIMER_CH_##x) * STARFIVE_TIMER_CH_LEN) + +#define STARFIVE_CLOCK_SOURCE_RATING 200 +#define STARFIVE_VALID_BITS 32 +#define STARFIVE_DELAY_US 0 +#define STARFIVE_TIMEOUT_US 10000 +#define STARFIVE_CLOCKEVENT_RATING 300 +#define STARFIVE_TIMER_MAX_TICKS 0xffffffff +#define STARFIVE_TIMER_MIN_TICKS 0xf + +#define STARFIVE_TIMER_JH7110_INT_STATUS 0x00 /* RO[0:4]: Interrupt Status for channel0~4 */ +#define STARFIVE_TIMER_JH7110_CTL 0x04 /* RW[0]: 0-continuous run, 1-single run */ +#define STARFIVE_TIMER_JH7110_LOAD 0x08 /* RW: load value to counter */ +#define STARFIVE_TIMER_JH7110_ENABLE 0x10 /* RW[0]: timer enable register */ +#define STARFIVE_TIMER_JH7110_RELOAD 0x14 /* RW: write 1 or 0 both reload counter */ +#define STARFIVE_TIMER_JH7110_VALUE 0x18 /* RO: timer value register */ +#define STARFIVE_TIMER_JH7110_INT_CLR 0x20 /* RW: timer interrupt clear register */ +#define STARFIVE_TIMER_JH7110_INT_MASK 0x24 /* RW[0]: timer interrupt mask register */ +#define STARFIVE_TIMER_JH7110_INT_CLR_AVA_MASK BIT(1) + +enum STARFIVE_TIMER_CH { + STARFIVE_TIMER_CH_0 = 0, + STARFIVE_TIMER_CH_1, + STARFIVE_TIMER_CH_2, + STARFIVE_TIMER_CH_3, + STARFIVE_TIMER_CH_4, + STARFIVE_TIMER_CH_5, + STARFIVE_TIMER_CH_6, + STARFIVE_TIMER_CH_7, + STARFIVE_TIMER_CH_MAX +}; + +enum STARFIVE_TIMER_INTMASK { + STARFIVE_TIMER_INTMASK_DIS = 0, + STARFIVE_TIMER_INTMASK_ENA = 1 +}; + +enum STARFIVE_TIMER_MOD { + STARFIVE_TIMER_MOD_CONTIN = 0, + STARFIVE_TIMER_MOD_SINGLE = 1 +}; + +enum STARFIVE_TIMER_CTL_EN { + STARFIVE_TIMER_DIS = 0, + STARFIVE_TIMER_ENA = 1 +}; + +struct starfive_timer_chan_base { + /* Resgister */ + unsigned int ctrl; + unsigned int load; + unsigned int enable; + unsigned int reload; + unsigned int value; + unsigned int intclr; + unsigned int intmask; + + unsigned int channel_num; /* timer channel numbers */ + unsigned int channel_base[]; +}; + +struct starfive_clkevt { + struct clock_event_device evt; + struct clk *clk; + char name[20]; + int irq; + u32 periodic; + u32 rate; + u32 reload_val; + void __iomem *base; + void __iomem *ctrl; + void __iomem *load; + void __iomem *enable; + void __iomem *reload; + void __iomem *value; + void __iomem *intclr; + void __iomem *intmask; +}; + +struct starfive_timer_priv { + struct device *dev; + void __iomem *base; + struct starfive_clkevt clkevt[]; +}; + +#endif /* __STARFIVE_TIMER_H__ */ From 112004fa7ee1ca6638f113e3a952da86030015c0 Mon Sep 17 00:00:00 2001 From: Xingyu Wu Date: Tue, 1 Nov 2022 21:54:04 +0800 Subject: [PATCH 48/95] riscv: dts: jh7110: starfive: Add timer node Add the timer node for the Starfive JH7110 SoC. Signed-off-by: Xingyu Wu --- arch/riscv/boot/dts/starfive/jh7110.dtsi | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi index 3190bcb0ab21c..413ee6130d427 100644 --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi @@ -530,6 +530,26 @@ #gpio-cells = <2>; }; + timer@13050000 { + compatible = "starfive,jh7110-timer"; + reg = <0x0 0x13050000 0x0 0x10000>; + interrupts = <69>, <70>, <71> ,<72>; + clocks = <&syscrg JH7110_SYSCLK_TIMER_APB>, + <&syscrg JH7110_SYSCLK_TIMER0>, + <&syscrg JH7110_SYSCLK_TIMER1>, + <&syscrg JH7110_SYSCLK_TIMER2>, + <&syscrg JH7110_SYSCLK_TIMER3>; + clock-names = "apb", "ch0", "ch1", + "ch2", "ch3"; + resets = <&syscrg JH7110_SYSRST_TIMER_APB>, + <&syscrg JH7110_SYSRST_TIMER0>, + <&syscrg JH7110_SYSRST_TIMER1>, + <&syscrg JH7110_SYSRST_TIMER2>, + <&syscrg JH7110_SYSRST_TIMER3>; + reset-names = "apb", "ch0", "ch1", + "ch2", "ch3"; + }; + wdog: watchdog@13070000 { compatible = "starfive,jh7110-wdt"; reg = <0x0 0x13070000 0x0 0x10000>; From 1565d665d05e652720cf803e30f8e54d7deb79b1 Mon Sep 17 00:00:00 2001 From: William Qiu Date: Wed, 15 Feb 2023 17:51:55 +0800 Subject: [PATCH 49/95] riscv: dts: starfive: Add mmc node Adds the mmc node for the StarFive JH7110 SoC. Set mmco node to emmc and set mmc1 node to sd. Signed-off-by: William Qiu --- .../jh7110-starfive-visionfive-2.dtsi | 23 +++++++++++++ arch/riscv/boot/dts/starfive/jh7110.dtsi | 32 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi index 1155b97b593df..7684bf7290780 100644 --- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi @@ -134,6 +134,29 @@ status = "okay"; }; +&mmc0 { + max-frequency = <100000000>; + bus-width = <8>; + cap-mmc-highspeed; + mmc-ddr-1_8v; + mmc-hs200-1_8v; + non-removable; + cap-mmc-hw-reset; + post-power-on-delay-ms = <200>; + status = "okay"; +}; + +&mmc1 { + max-frequency = <100000000>; + bus-width = <4>; + no-sdio; + no-mmc; + broken-cd; + cap-sd-highspeed; + post-power-on-delay-ms = <200>; + status = "okay"; +}; + &sysgpio { i2c0_pins: i2c0-0 { i2c-pins { diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi index 413ee6130d427..1a16f3bc4cbc5 100644 --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi @@ -560,6 +560,38 @@ <&syscrg JH7110_SYSRST_WDT_CORE>; }; + mmc0: mmc@16010000 { + compatible = "starfive,jh7110-mmc"; + reg = <0x0 0x16010000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_SDIO0_AHB>, + <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>; + clock-names = "biu","ciu"; + resets = <&syscrg JH7110_SYSRST_SDIO0_AHB>; + reset-names = "reset"; + interrupts = <74>; + fifo-depth = <32>; + fifo-watermark-aligned; + data-addr = <0>; + starfive,sysreg = <&sys_syscon 0x14 0x1a 0x7c000000>; + status = "disabled"; + }; + + mmc1: mmc@16020000 { + compatible = "starfive,jh7110-mmc"; + reg = <0x0 0x16020000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_SDIO1_AHB>, + <&syscrg JH7110_SYSCLK_SDIO1_SDCARD>; + clock-names = "biu","ciu"; + resets = <&syscrg JH7110_SYSRST_SDIO1_AHB>; + reset-names = "reset"; + interrupts = <75>; + fifo-depth = <32>; + fifo-watermark-aligned; + data-addr = <0>; + starfive,sysreg = <&sys_syscon 0x9c 0x1 0x3e>; + status = "disabled"; + }; + aoncrg: clock-controller@17000000 { compatible = "starfive,jh7110-aoncrg"; reg = <0x0 0x17000000 0x0 0x10000>; From 04907f5fc7b2a0a6d5abd2b56c4d681a24d18260 Mon Sep 17 00:00:00 2001 From: William Qiu Date: Thu, 2 Mar 2023 18:52:20 +0800 Subject: [PATCH 50/95] dt-bindings: qspi: cdns,qspi-nor: constrain minItems/maxItems of resets The QSPI controller needs three reset items to work properly on JH7110 SoC, so there is need to change the maxItems's value to 3 and add minItems whose value is equal to 2. Other platforms do not have this constraint. Signed-off-by: William Qiu Link: https://lore.kernel.org/r/20230302105221.197421-2-william.qiu@starfivetech.com Signed-off-by: Mark Brown --- .../bindings/spi/cdns,qspi-nor.yaml | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/spi/cdns,qspi-nor.yaml b/Documentation/devicetree/bindings/spi/cdns,qspi-nor.yaml index 5c01db128be0f..b310069762dd5 100644 --- a/Documentation/devicetree/bindings/spi/cdns,qspi-nor.yaml +++ b/Documentation/devicetree/bindings/spi/cdns,qspi-nor.yaml @@ -19,6 +19,33 @@ allOf: then: required: - power-domains + - if: + properties: + compatible: + contains: + const: starfive,jh7110-qspi + then: + properties: + resets: + minItems: 2 + maxItems: 3 + + reset-names: + minItems: 2 + maxItems: 3 + items: + enum: [ qspi, qspi-ocp, rstc_ref ] + + else: + properties: + resets: + maxItems: 2 + + reset-names: + minItems: 1 + maxItems: 2 + items: + enum: [ qspi, qspi-ocp ] properties: compatible: @@ -30,6 +57,7 @@ properties: - intel,lgm-qspi - xlnx,versal-ospi-1.0 - intel,socfpga-qspi + - starfive,jh7110-qspi - const: cdns,qspi-nor - const: cdns,qspi-nor @@ -79,13 +107,14 @@ properties: maxItems: 1 resets: - maxItems: 2 + minItems: 2 + maxItems: 3 reset-names: - minItems: 1 - maxItems: 2 + minItems: 2 + maxItems: 3 items: - enum: [ qspi, qspi-ocp ] + enum: [ qspi, qspi-ocp, rstc_ref ] required: - compatible From a02af446ac8c73990b4464aaad272dc6bb5a0abb Mon Sep 17 00:00:00 2001 From: William Qiu Date: Thu, 2 Mar 2023 18:52:21 +0800 Subject: [PATCH 51/95] spi: cadence-quadspi: Add support for StarFive JH7110 QSPI Add QSPI reset operation in device probe and add RISCV support to QUAD SPI Kconfig. Co-developed-by: Ziv Xu Signed-off-by: Ziv Xu Signed-off-by: William Qiu Link: https://lore.kernel.org/r/20230302105221.197421-3-william.qiu@starfivetech.com Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 2 +- drivers/spi/spi-cadence-quadspi.c | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 47bbba04fe3ae..839d426a741c0 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -239,7 +239,7 @@ config SPI_CADENCE config SPI_CADENCE_QUADSPI tristate "Cadence Quad SPI controller" - depends on OF && (ARM || ARM64 || X86 || COMPILE_TEST) + depends on OF && (ARM || ARM64 || X86 || RISCV || COMPILE_TEST) help Enable support for the Cadence Quad SPI Flash controller. diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index 64b6a460d739b..c789fa9e2177f 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -1615,7 +1615,7 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi) static int cqspi_probe(struct platform_device *pdev) { const struct cqspi_driver_platdata *ddata; - struct reset_control *rstc, *rstc_ocp; + struct reset_control *rstc, *rstc_ocp, *rstc_ref; struct device *dev = &pdev->dev; struct spi_master *master; struct resource *res_ahb; @@ -1705,6 +1705,17 @@ static int cqspi_probe(struct platform_device *pdev) goto probe_reset_failed; } + if (of_device_is_compatible(pdev->dev.of_node, "starfive,jh7110-qspi")) { + rstc_ref = devm_reset_control_get_optional_exclusive(dev, "rstc_ref"); + if (IS_ERR(rstc_ref)) { + ret = PTR_ERR(rstc_ref); + dev_err(dev, "Cannot get QSPI REF reset.\n"); + goto probe_reset_failed; + } + reset_control_assert(rstc_ref); + reset_control_deassert(rstc_ref); + } + reset_control_assert(rstc); reset_control_deassert(rstc); @@ -1859,6 +1870,10 @@ static const struct cqspi_driver_platdata versal_ospi = { .get_dma_status = cqspi_get_versal_dma_status, }; +static const struct cqspi_driver_platdata jh7110_qspi = { + .quirks = CQSPI_DISABLE_DAC_MODE, +}; + static const struct of_device_id cqspi_dt_ids[] = { { .compatible = "cdns,qspi-nor", @@ -1884,6 +1899,10 @@ static const struct of_device_id cqspi_dt_ids[] = { .compatible = "intel,socfpga-qspi", .data = &socfpga_qspi, }, + { + .compatible = "starfive,jh7110-qspi", + .data = &jh7110_qspi, + }, { /* end of table */ } }; From 78b7ec3ca8bc81c480bdb347ae9df1ad997b67a7 Mon Sep 17 00:00:00 2001 From: William Qiu Date: Thu, 2 Mar 2023 16:42:57 +0800 Subject: [PATCH 52/95] riscv: dts: starfive: jh7110: Add qspi controller node Add the quad spi controller node for the Starfive JH7110 SoC. Signed-off-by: William Qiu --- arch/riscv/boot/dts/starfive/jh7110.dtsi | 44 ++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi index 1a16f3bc4cbc5..5ab159813ad59 100644 --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi @@ -486,6 +486,50 @@ status = "disabled"; }; + qspi: spi@13010000 { + compatible = "starfive,jh7110-qspi", "cdns,qspi-nor"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x13010000 0x0 0x10000 + 0x0 0x21000000 0x0 0x400000>; + interrupts = <25>; + clocks = <&syscrg JH7110_SYSCLK_QSPI_REF>; + resets = <&syscrg JH7110_SYSRST_QSPI_APB>, + <&syscrg JH7110_SYSRST_QSPI_AHB>, + <&syscrg JH7110_SYSRST_QSPI_REF>; + reset-names = "qspi", "qspi-ocp", "rstc_ref"; + cdns,fifo-depth = <256>; + cdns,fifo-width = <4>; + cdns,trigger-address = <0x0>; + + nor_flash: nor-flash@0 { + compatible = "jedec,spi-nor"; + reg=<0>; + cdns,read-delay = <5>; + spi-max-frequency = <12000000>; + cdns,tshsl-ns = <1>; + cdns,tsd2d-ns = <1>; + cdns,tchsh-ns = <1>; + cdns,tslch-ns = <1>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + spl@0 { + reg = <0x0 0x20000>; + }; + uboot@100000 { + reg = <0x100000 0x300000>; + }; + data@f00000 { + reg = <0xf00000 0x100000>; + }; + }; + }; + }; + syscrg: clock-controller@13020000 { compatible = "starfive,jh7110-syscrg"; reg = <0x0 0x13020000 0x0 0x10000>; From 5f55e7d4e0fbd45d653db253eaccbd9325cf0da4 Mon Sep 17 00:00:00 2001 From: William Qiu Date: Tue, 21 Mar 2023 13:52:27 +0800 Subject: [PATCH 53/95] dt-bindings: PWM: Add StarFive PWM module Add documentation to describe StarFive Pulse Width Modulation controller driver. Signed-off-by: William Qiu Reviewed-by: Krzysztof Kozlowski --- .../bindings/pwm/starfive,jh7110-pwm.yaml | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/starfive,jh7110-pwm.yaml diff --git a/Documentation/devicetree/bindings/pwm/starfive,jh7110-pwm.yaml b/Documentation/devicetree/bindings/pwm/starfive,jh7110-pwm.yaml new file mode 100644 index 0000000000000..082b3779fa614 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/starfive,jh7110-pwm.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/starfive,jh7110-pwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive PWM controller + +maintainers: + - William Qiu + +description: + StarFive SoCs contain PWM and when operating in PWM mode, the PTC core generates + binary signal with user-programmable low and high periods. Clock source for the + PWM can be either system clockor external clock. Each PWM timer block provides 8 + PWM channels. + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + const: starfive,jh7110-pwm + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + + "#pwm-cells": + const: 3 + +required: + - compatible + - reg + - clocks + - resets + +additionalProperties: false + +examples: + - | + pwm@120d0000 { + compatible = "starfive,jh7110-pwm"; + reg = <0x120d0000 0x10000>; + clocks = <&syscrg 121>; + resets = <&syscrg 108>; + #pwm-cells = <3>; + }; From 50d9b8298615ee6faa13a715f712f949d0468be9 Mon Sep 17 00:00:00 2001 From: William Qiu Date: Tue, 21 Mar 2023 13:52:28 +0800 Subject: [PATCH 54/95] pwm: starfive: Add PWM driver support Add Pulse Width Modulation driver support for StarFive JH7110 soc. Signed-off-by: Hal Feng Signed-off-by: William Qiu --- MAINTAINERS | 7 + drivers/pwm/Kconfig | 10 ++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-starfive-ptc.c | 245 +++++++++++++++++++++++++++++++++ 4 files changed, 263 insertions(+) create mode 100644 drivers/pwm/pwm-starfive-ptc.c diff --git a/MAINTAINERS b/MAINTAINERS index e63c1c7a37050..38ea1bd27419f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19934,6 +19934,13 @@ F: drivers/pinctrl/starfive/pinctrl-starfive-jh71* F: include/dt-bindings/pinctrl/pinctrl-starfive-jh7100.h F: include/dt-bindings/pinctrl/starfive,jh7110-pinctrl.h +STARFIVE JH71X0 PWM DRIVERS +M: William Qiu +M: Hal Feng +S: Supported +F: Documentation/devicetree/bindings/pwm/starfive,jh7110-pwm.yaml +F: drivers/pwm/pwm-starfive-ptc.c + STARFIVE JH71X0 RESET CONTROLLER DRIVERS M: Emil Renner Berthing M: Hal Feng diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index dae023d783a22..2307a0099994e 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -536,6 +536,16 @@ config PWM_SPRD To compile this driver as a module, choose M here: the module will be called pwm-sprd. +config PWM_STARFIVE_PTC + tristate "StarFive PWM PTC support" + depends on OF + depends on COMMON_CLK + help + Generic PWM framework driver for StarFive SoCs. + + To compile this driver as a module, choose M here: the module + will be called pwm-starfive-ptc. + config PWM_STI tristate "STiH4xx PWM support" depends on ARCH_STI || COMPILE_TEST diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 7bf1a29f02b84..577f69904baa6 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_PWM_SIFIVE) += pwm-sifive.o obj-$(CONFIG_PWM_SL28CPLD) += pwm-sl28cpld.o obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o obj-$(CONFIG_PWM_SPRD) += pwm-sprd.o +obj-$(CONFIG_PWM_STARFIVE_PTC) += pwm-starfive-ptc.o obj-$(CONFIG_PWM_STI) += pwm-sti.o obj-$(CONFIG_PWM_STM32) += pwm-stm32.o obj-$(CONFIG_PWM_STM32_LP) += pwm-stm32-lp.o diff --git a/drivers/pwm/pwm-starfive-ptc.c b/drivers/pwm/pwm-starfive-ptc.c new file mode 100644 index 0000000000000..239df796d240c --- /dev/null +++ b/drivers/pwm/pwm-starfive-ptc.c @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PWM driver for the StarFive JH7110 SoC + * + * Copyright (C) 2018 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* how many parameters can be transferred to ptc */ +#define OF_PWM_N_CELLS 3 + +/* PTC Register offsets */ +#define REG_RPTC_CNTR 0x0 +#define REG_RPTC_HRC 0x4 +#define REG_RPTC_LRC 0x8 +#define REG_RPTC_CTRL 0xC + +/* Bit for PWM clock */ +#define BIT_PWM_CLOCK_EN 31 + +/* Bit for clock gen soft reset */ +#define BIT_CLK_GEN_SOFT_RESET 13 + +#define NS_PER_SECOND 1000000000 + +/* + * Access PTC register (cntr hrc lrc and ctrl), + * need to replace PWM_BASE_ADDR + */ +#define REG_PTC_BASE_ADDR_SUB(base, N) \ +((base) + (((N) > 3) ? (((N) % 4) * 0x10 + (1 << 15)) : ((N) * 0x10))) +#define REG_PTC_RPTC_CNTR(base, N) (REG_PTC_BASE_ADDR_SUB(base, N)) +#define REG_PTC_RPTC_HRC(base, N) (REG_PTC_BASE_ADDR_SUB(base, N) + 0x4) +#define REG_PTC_RPTC_LRC(base, N) (REG_PTC_BASE_ADDR_SUB(base, N) + 0x8) +#define REG_PTC_RPTC_CTRL(base, N) (REG_PTC_BASE_ADDR_SUB(base, N) + 0xC) + +/* PTC_RPTC_CTRL */ +#define PTC_EN BIT(0) +#define PTC_ECLK BIT(1) +#define PTC_NEC BIT(2) +#define PTC_OE BIT(3) +#define PTC_SIGNLE BIT(4) +#define PTC_INTE BIT(5) +#define PTC_INT BIT(6) +#define PTC_CNTRRST BIT(7) +#define PTC_CAPTE BIT(8) + +struct starfive_pwm_ptc_device { + struct pwm_chip chip; + struct clk *clk; + struct reset_control *rst; + void __iomem *regs; + int irq; + unsigned int approx_freq;/*pwm apb clock frequency*/ +}; + +static inline +struct starfive_pwm_ptc_device *chip_to_starfive_ptc(struct pwm_chip *c) +{ + return container_of(c, struct starfive_pwm_ptc_device, chip); +} + +static int starfive_pwm_ptc_get_state(struct pwm_chip *chip, + struct pwm_device *dev, + struct pwm_state *state) +{ + struct starfive_pwm_ptc_device *pwm = chip_to_starfive_ptc(chip); + u32 data_lrc, data_hrc; + u32 pwm_clk_ns = 0; + + data_lrc = ioread32(REG_PTC_RPTC_LRC(pwm->regs, dev->hwpwm)); + data_hrc = ioread32(REG_PTC_RPTC_HRC(pwm->regs, dev->hwpwm)); + + pwm_clk_ns = NS_PER_SECOND / pwm->approx_freq; + + state->period = data_lrc * pwm_clk_ns; + state->duty_cycle = data_hrc * pwm_clk_ns; + state->polarity = PWM_POLARITY_NORMAL; + state->enabled = 1; + + return 0; +} + +static int starfive_pwm_ptc_apply(struct pwm_chip *chip, + struct pwm_device *dev, + struct pwm_state *state) +{ + struct starfive_pwm_ptc_device *pwm = chip_to_starfive_ptc(chip); + u32 data_hrc = 0; + u32 data_lrc = 0; + u32 period_data = 0; + u32 duty_data = 0; + s64 multi = pwm->approx_freq; + s64 div = NS_PER_SECOND; + void __iomem *reg_addr; + + if (state->duty_cycle > state->period) + state->duty_cycle = state->period; + + while (multi % 10 == 0 && div % 10 == 0 && multi > 0 && div > 0) { + multi /= 10; + div /= 10; + } + + period_data = (u32)(state->period * multi / div); + if (abs(period_data * div / multi - state->period) + > abs((period_data + 1) * div / multi - state->period) || + (state->period > 0 && period_data == 0)) + period_data += 1; + + if (state->enabled) { + duty_data = (u32)(state->duty_cycle * multi / div); + if (abs(duty_data * div / multi - state->duty_cycle) + > abs((duty_data + 1) * div / multi - state->duty_cycle) || + (state->duty_cycle > 0 && duty_data == 0)) + duty_data += 1; + } else { + duty_data = 0; + } + + if (state->polarity == PWM_POLARITY_NORMAL) + data_hrc = period_data - duty_data; + else + data_hrc = duty_data; + + data_lrc = period_data; + + reg_addr = REG_PTC_RPTC_HRC(pwm->regs, dev->hwpwm); + iowrite32(data_hrc, reg_addr); + + reg_addr = REG_PTC_RPTC_LRC(pwm->regs, dev->hwpwm); + iowrite32(data_lrc, reg_addr); + + reg_addr = REG_PTC_RPTC_CNTR(pwm->regs, dev->hwpwm); + iowrite32(0, reg_addr); + + reg_addr = REG_PTC_RPTC_CTRL(pwm->regs, dev->hwpwm); + iowrite32(PTC_EN | PTC_OE, reg_addr); + + return 0; +} + +static const struct pwm_ops starfive_pwm_ptc_ops = { + .get_state = starfive_pwm_ptc_get_state, + .apply = (void *)starfive_pwm_ptc_apply, + .owner = THIS_MODULE, +}; + +static int starfive_pwm_ptc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct starfive_pwm_ptc_device *pwm; + struct pwm_chip *chip; + int ret; + + pwm = devm_kzalloc(dev, sizeof(*pwm), GFP_KERNEL); + if (!pwm) + return -ENOMEM; + + chip = &pwm->chip; + chip->dev = dev; + chip->ops = &starfive_pwm_ptc_ops; + chip->npwm = 8; + + chip->of_pwm_n_cells = OF_PWM_N_CELLS; + chip->base = -1; + + pwm->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pwm->regs)) + return dev_err_probe(dev, PTR_ERR(pwm->regs), + "Unable to map IO resources\n"); + + pwm->clk = devm_clk_get(dev, NULL); + if (IS_ERR(pwm->clk)) + return dev_err_probe(dev, PTR_ERR(pwm->clk), + "Unable to get pwm clock\n"); + + pwm->rst = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(pwm->rst)) + return dev_err_probe(dev, PTR_ERR(pwm->rst), + "Unable to get pwm reset\n"); + + ret = clk_prepare_enable(pwm->clk); + if (ret) { + dev_err(dev, + "Failed to enable pwm clock, %d\n", ret); + return ret; + } + + reset_control_deassert(pwm->rst); + + pwm->approx_freq = (unsigned int)clk_get_rate(pwm->clk); + if (!pwm->approx_freq) + dev_err(dev, "get pwm apb clock rate failed.\n"); + + ret = devm_pwmchip_add(dev, chip); + if (ret < 0) { + dev_err(dev, "cannot register PTC: %d\n", ret); + clk_disable_unprepare(pwm->clk); + return ret; + } + + platform_set_drvdata(pdev, pwm); + + return 0; +} + +static int starfive_pwm_ptc_remove(struct platform_device *dev) +{ + struct starfive_pwm_ptc_device *pwm = platform_get_drvdata(dev); + struct pwm_chip *chip = &pwm->chip; + + pwmchip_remove(chip); + + return 0; +} + +static const struct of_device_id starfive_pwm_ptc_of_match[] = { + { .compatible = "starfive,jh7110-pwm" }, + {}, +}; +MODULE_DEVICE_TABLE(of, starfive_pwm_ptc_of_match); + +static struct platform_driver starfive_pwm_ptc_driver = { + .probe = starfive_pwm_ptc_probe, + .remove = starfive_pwm_ptc_remove, + .driver = { + .name = "pwm-starfive-ptc", + .of_match_table = starfive_pwm_ptc_of_match, + }, +}; +module_platform_driver(starfive_pwm_ptc_driver); + +MODULE_AUTHOR("Jenny Zhang "); +MODULE_AUTHOR("Hal Feng "); +MODULE_DESCRIPTION("StarFive PWM PTC driver"); +MODULE_LICENSE("GPL"); From a81b07940d40355a0b25bf7bc7b950fb367dbdcf Mon Sep 17 00:00:00 2001 From: William Qiu Date: Wed, 1 Mar 2023 16:45:11 +0800 Subject: [PATCH 55/95] riscv: dts: starfive: Add PWM node Adding StarFive PWM controller node to VisionFive 2 SoC. Signed-off-by: William Qiu --- .../jh7110-starfive-visionfive-2.dtsi | 20 +++++++++++++++++++ arch/riscv/boot/dts/starfive/jh7110.dtsi | 9 +++++++++ 2 files changed, 29 insertions(+) diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi index 7684bf7290780..4633a2c431153 100644 --- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi @@ -157,6 +157,12 @@ status = "okay"; }; +&ptc { + pinctrl-names = "default"; + pinctrl-0 = <&pwm_pins>; + status = "okay"; +}; + &sysgpio { i2c0_pins: i2c0-0 { i2c-pins { @@ -214,6 +220,20 @@ }; }; + pwm_pins: pwm-0 { + pwm-pins { + pinmux = , + ; + bias-disable; + drive-strength = <12>; + input-disable; + input-schmitt-disable; + slew-rate = <0>; + }; + }; + uart0_pins: uart0-0 { tx-pins { pinmux = ; + clocks = <&syscrg JH7110_SYSCLK_PWM_APB>; + resets = <&syscrg JH7110_SYSRST_PWM_APB>; + #pwm-cells=<3>; + status = "disabled"; + }; + qspi: spi@13010000 { compatible = "starfive,jh7110-qspi", "cdns,qspi-nor"; #address-cells = <1>; From 7f13b47ff94bb4b3adf079ae396257dac2a2e20d Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Mon, 8 Aug 2022 17:13:34 +0200 Subject: [PATCH 56/95] dt-bindings: net: snps,dwmac: Add dwmac-5.20 version Add dwmac-5.20 IP version to snps.dwmac.yaml Signed-off-by: Emil Renner Berthing Signed-off-by: Samin Guo Acked-by: Krzysztof Kozlowski Tested-by: Tommaso Merciai --- Documentation/devicetree/bindings/net/snps,dwmac.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/net/snps,dwmac.yaml b/Documentation/devicetree/bindings/net/snps,dwmac.yaml index 16b7d29046964..01b056ab71f7e 100644 --- a/Documentation/devicetree/bindings/net/snps,dwmac.yaml +++ b/Documentation/devicetree/bindings/net/snps,dwmac.yaml @@ -30,6 +30,7 @@ select: - snps,dwmac-4.10a - snps,dwmac-4.20a - snps,dwmac-5.10a + - snps,dwmac-5.20 - snps,dwxgmac - snps,dwxgmac-2.10 @@ -87,6 +88,7 @@ properties: - snps,dwmac-4.10a - snps,dwmac-4.20a - snps,dwmac-5.10a + - snps,dwmac-5.20 - snps,dwxgmac - snps,dwxgmac-2.10 @@ -575,6 +577,7 @@ allOf: - snps,dwmac-3.50a - snps,dwmac-4.10a - snps,dwmac-4.20a + - snps,dwmac-5.20 - snps,dwxgmac - snps,dwxgmac-2.10 - st,spear600-gmac @@ -629,6 +632,7 @@ allOf: - snps,dwmac-4.10a - snps,dwmac-4.20a - snps,dwmac-5.10a + - snps,dwmac-5.20 - snps,dwxgmac - snps,dwxgmac-2.10 - st,spear600-gmac From 87ec2312c67586711ed30d3102e2236ff18cea45 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sun, 7 Aug 2022 22:26:00 +0200 Subject: [PATCH 57/95] net: stmmac: platform: Add snps,dwmac-5.20 IP compatible string Add "snps,dwmac-5.20" compatible string for 5.20 version that can avoid to define some platform data in the glue layer. Signed-off-by: Emil Renner Berthing Signed-off-by: Samin Guo Tested-by: Tommaso Merciai --- drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 067a40fe0a23f..eb0b2898daa3d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -519,7 +519,8 @@ stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac) if (of_device_is_compatible(np, "snps,dwmac-4.00") || of_device_is_compatible(np, "snps,dwmac-4.10a") || of_device_is_compatible(np, "snps,dwmac-4.20a") || - of_device_is_compatible(np, "snps,dwmac-5.10a")) { + of_device_is_compatible(np, "snps,dwmac-5.10a") || + of_device_is_compatible(np, "snps,dwmac-5.20")) { plat->has_gmac4 = 1; plat->has_gmac = 0; plat->pmt = 1; From d457a2abda2178e1ca9c3cdd5f6144f79916e6e5 Mon Sep 17 00:00:00 2001 From: Samin Guo Date: Mon, 27 Feb 2023 18:26:04 +0800 Subject: [PATCH 58/95] dt-bindings: net: snps,dwmac: Add 'ahb' reset/reset-name According to: stmmac_platform.c: stmmac_probe_config_dt stmmac_main.c: stmmac_dvr_probe dwmac controller may require one (stmmaceth) or two (stmmaceth+ahb) reset signals, and the maxItems of resets/reset-names is going to be 2. The gmac of Starfive Jh7110 SOC must have two resets. it uses snps,dwmac-5.20 IP. Signed-off-by: Samin Guo Tested-by: Tommaso Merciai --- .../devicetree/bindings/net/snps,dwmac.yaml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/net/snps,dwmac.yaml b/Documentation/devicetree/bindings/net/snps,dwmac.yaml index 01b056ab71f7e..e4519cf722ab7 100644 --- a/Documentation/devicetree/bindings/net/snps,dwmac.yaml +++ b/Documentation/devicetree/bindings/net/snps,dwmac.yaml @@ -133,12 +133,16 @@ properties: - ptp_ref resets: - maxItems: 1 - description: - MAC Reset signal. + minItems: 1 + items: + - description: GMAC stmmaceth reset + - description: AHB reset reset-names: - const: stmmaceth + minItems: 1 + items: + - const: stmmaceth + - const: ahb power-domains: maxItems: 1 From 072bb0f73731aac86e200226723617380108f8b9 Mon Sep 17 00:00:00 2001 From: Yanhong Wang Date: Mon, 31 Oct 2022 18:08:15 +0800 Subject: [PATCH 59/95] dt-bindings: net: Add support StarFive dwmac Add documentation to describe StarFive dwmac driver(GMAC). Signed-off-by: Yanhong Wang Signed-off-by: Samin Guo Tested-by: Tommaso Merciai --- .../devicetree/bindings/net/snps,dwmac.yaml | 1 + .../bindings/net/starfive,jh7110-dwmac.yaml | 130 ++++++++++++++++++ MAINTAINERS | 6 + 3 files changed, 137 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml diff --git a/Documentation/devicetree/bindings/net/snps,dwmac.yaml b/Documentation/devicetree/bindings/net/snps,dwmac.yaml index e4519cf722ab7..245f7d713261f 100644 --- a/Documentation/devicetree/bindings/net/snps,dwmac.yaml +++ b/Documentation/devicetree/bindings/net/snps,dwmac.yaml @@ -91,6 +91,7 @@ properties: - snps,dwmac-5.20 - snps,dwxgmac - snps,dwxgmac-2.10 + - starfive,jh7110-dwmac reg: minItems: 1 diff --git a/Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml b/Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml new file mode 100644 index 0000000000000..b59e6bd8201f2 --- /dev/null +++ b/Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml @@ -0,0 +1,130 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2022 StarFive Technology Co., Ltd. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/starfive,jh7110-dwmac.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 DWMAC glue layer + +maintainers: + - Emil Renner Berthing + - Samin Guo + +select: + properties: + compatible: + contains: + enum: + - starfive,jh7110-dwmac + required: + - compatible + +properties: + compatible: + items: + - enum: + - starfive,jh7110-dwmac + - const: snps,dwmac-5.20 + + clocks: + items: + - description: GMAC main clock + - description: GMAC AHB clock + - description: PTP clock + - description: TX clock + - description: GTX clock + + clock-names: + items: + - const: stmmaceth + - const: pclk + - const: ptp_ref + - const: tx + - const: gtx + + resets: + items: + - description: MAC Reset signal. + - description: AHB Reset signal. + + reset-names: + items: + - const: stmmaceth + - const: ahb + + starfive,tx-use-rgmii-clk: + description: + Tx clock is provided by external rgmii clock. + type: boolean + + starfive,syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to syscon that configures phy mode + - description: Offset of phy mode selection + - description: Shift of phy mode selection + description: + A phandle to syscon with two arguments that configure phy mode. + The argument one is the offset of phy mode selection, the + argument two is the shift of phy mode selection. + +allOf: + - $ref: snps,dwmac.yaml# + +unevaluatedProperties: false + +required: + - compatible + - clocks + - clock-names + - resets + - reset-names + +examples: + - | + ethernet@16030000 { + compatible = "starfive,jh7110-dwmac", "snps,dwmac-5.20"; + reg = <0x16030000 0x10000>; + clocks = <&clk 3>, <&clk 2>, <&clk 109>, + <&clk 6>, <&clk 111>; + clock-names = "stmmaceth", "pclk", "ptp_ref", + "tx", "gtx"; + resets = <&rst 1>, <&rst 2>; + reset-names = "stmmaceth", "ahb"; + interrupts = <7>, <6>, <5>; + interrupt-names = "macirq", "eth_wake_irq", "eth_lpi"; + phy-mode = "rgmii-id"; + snps,multicast-filter-bins = <64>; + snps,perfect-filter-entries = <8>; + rx-fifo-depth = <2048>; + tx-fifo-depth = <2048>; + snps,fixed-burst; + snps,no-pbl-x8; + snps,tso; + snps,force_thresh_dma_mode; + snps,axi-config = <&stmmac_axi_setup>; + snps,en-tx-lpi-clockgating; + snps,txpbl = <16>; + snps,rxpbl = <16>; + starfive,syscon = <&aon_syscon 0xc 0x12>; + phy-handle = <&phy0>; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + compatible = "snps,dwmac-mdio"; + + phy0: ethernet-phy@0 { + reg = <0>; + }; + }; + + stmmac_axi_setup: stmmac-axi-config { + snps,lpi_en; + snps,wr_osr_lmt = <4>; + snps,rd_osr_lmt = <4>; + snps,blen = <256 128 64 32 0 0 0>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 38ea1bd27419f..a13a1ee7f0cab 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19905,6 +19905,12 @@ M: Emil Renner Berthing S: Maintained F: arch/riscv/boot/dts/starfive/ +STARFIVE DWMAC GLUE LAYER +M: Emil Renner Berthing +M: Samin Guo +S: Maintained +F: Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml + STARFIVE JH7110 MMC/SD/SDIO DRIVER M: William Qiu S: Supported From 4cdcc13879b91f1ef4d953f69c9577153dc9b408 Mon Sep 17 00:00:00 2001 From: Samin Guo Date: Fri, 3 Mar 2023 16:50:58 +0800 Subject: [PATCH 60/95] net: stmmac: Add glue layer for StarFive JH7110 SoC This adds StarFive dwmac driver support on the StarFive JH7110 SoC. Co-developed-by: Emil Renner Berthing Signed-off-by: Emil Renner Berthing Signed-off-by: Samin Guo Tested-by: Tommaso Merciai --- MAINTAINERS | 1 + drivers/net/ethernet/stmicro/stmmac/Kconfig | 12 ++ drivers/net/ethernet/stmicro/stmmac/Makefile | 1 + .../ethernet/stmicro/stmmac/dwmac-starfive.c | 121 ++++++++++++++++++ 4 files changed, 135 insertions(+) create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c diff --git a/MAINTAINERS b/MAINTAINERS index a13a1ee7f0cab..4df9628b3c855 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19909,6 +19909,7 @@ STARFIVE DWMAC GLUE LAYER M: Emil Renner Berthing M: Samin Guo S: Maintained +F: Documentation/devicetree/bindings/net/dwmac-starfive.c F: Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml STARFIVE JH7110 MMC/SD/SDIO DRIVER diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index f77511fe4e873..5f5a997f21f36 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -165,6 +165,18 @@ config DWMAC_SOCFPGA for the stmmac device driver. This driver is used for arria5 and cyclone5 FPGA SoCs. +config DWMAC_STARFIVE + tristate "StarFive dwmac support" + depends on OF && (ARCH_STARFIVE || COMPILE_TEST) + select MFD_SYSCON + default m if ARCH_STARFIVE + help + Support for ethernet controllers on StarFive RISC-V SoCs + + This selects the StarFive platform specific glue layer support for + the stmmac device driver. This driver is used for StarFive JH7110 + ethernet controller. + config DWMAC_STI tristate "STi GMAC support" default ARCH_STI diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index 057e4bab5c08e..8738fdbb4b2dc 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_DWMAC_OXNAS) += dwmac-oxnas.o obj-$(CONFIG_DWMAC_QCOM_ETHQOS) += dwmac-qcom-ethqos.o obj-$(CONFIG_DWMAC_ROCKCHIP) += dwmac-rk.o obj-$(CONFIG_DWMAC_SOCFPGA) += dwmac-altr-socfpga.o +obj-$(CONFIG_DWMAC_STARFIVE) += dwmac-starfive.o obj-$(CONFIG_DWMAC_STI) += dwmac-sti.o obj-$(CONFIG_DWMAC_STM32) += dwmac-stm32.o obj-$(CONFIG_DWMAC_SUNXI) += dwmac-sunxi.o diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c new file mode 100644 index 0000000000000..ef5a769b1c754 --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * StarFive DWMAC platform driver + * + * Copyright (C) 2021 Emil Renner Berthing + * Copyright (C) 2022 StarFive Technology Co., Ltd. + * + */ + +#include +#include +#include + +#include "stmmac_platform.h" + +struct starfive_dwmac { + struct device *dev; + struct clk *clk_tx; +}; + +static void starfive_dwmac_fix_mac_speed(void *priv, unsigned int speed) +{ + struct starfive_dwmac *dwmac = priv; + unsigned long rate; + int err; + + switch (speed) { + case SPEED_1000: + rate = 125000000; + break; + case SPEED_100: + rate = 25000000; + break; + case SPEED_10: + rate = 2500000; + break; + default: + dev_err(dwmac->dev, "invalid speed %u\n", speed); + break; + } + + err = clk_set_rate(dwmac->clk_tx, rate); + if (err) + dev_err(dwmac->dev, "failed to set tx rate %lu\n", rate); +} + +static int starfive_dwmac_probe(struct platform_device *pdev) +{ + struct plat_stmmacenet_data *plat_dat; + struct stmmac_resources stmmac_res; + struct starfive_dwmac *dwmac; + struct clk *clk_gtx; + int err; + + err = stmmac_get_platform_resources(pdev, &stmmac_res); + if (err) + return err; + + plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac); + if (IS_ERR(plat_dat)) { + dev_err(&pdev->dev, "dt configuration failed\n"); + return PTR_ERR(plat_dat); + } + + dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL); + if (!dwmac) + return -ENOMEM; + + dwmac->clk_tx = devm_clk_get_enabled(&pdev->dev, "tx"); + if (IS_ERR(dwmac->clk_tx)) + return dev_err_probe(&pdev->dev, PTR_ERR(dwmac->clk_tx), + "error getting tx clock\n"); + + clk_gtx = devm_clk_get_enabled(&pdev->dev, "gtx"); + if (IS_ERR(clk_gtx)) + return dev_err_probe(&pdev->dev, PTR_ERR(clk_gtx), + "error getting gtx clock\n"); + + /* Generally, the rgmii_tx clock is provided by the internal clock, + * which needs to match the corresponding clock frequency according + * to different speeds. If the rgmii_tx clock is provided by the + * external rgmii_rxin, there is no need to configure the clock + * internally, because rgmii_rxin will be adaptively adjusted. + */ + if (!device_property_read_bool(&pdev->dev, "starfive,tx-use-rgmii-clk")) + plat_dat->fix_mac_speed = starfive_dwmac_fix_mac_speed; + + dwmac->dev = &pdev->dev; + plat_dat->bsp_priv = dwmac; + plat_dat->dma_cfg->dche = true; + + err = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); + if (err) { + stmmac_remove_config_dt(pdev, plat_dat); + return err; + } + + return 0; +} + +static const struct of_device_id starfive_dwmac_match[] = { + { .compatible = "starfive,jh7110-dwmac" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, starfive_dwmac_match); + +static struct platform_driver starfive_dwmac_driver = { + .probe = starfive_dwmac_probe, + .remove = stmmac_pltfr_remove, + .driver = { + .name = "starfive-dwmac", + .pm = &stmmac_pltfr_pm_ops, + .of_match_table = starfive_dwmac_match, + }, +}; +module_platform_driver(starfive_dwmac_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("StarFive DWMAC platform driver"); +MODULE_AUTHOR("Emil Renner Berthing "); +MODULE_AUTHOR("Samin Guo "); From 6e39c575279baf42e5138de603f728b5dc3a5a8e Mon Sep 17 00:00:00 2001 From: Samin Guo Date: Thu, 2 Mar 2023 19:52:37 +0800 Subject: [PATCH 61/95] net: stmmac: starfive_dmac: Add phy interface settings dwmac supports multiple modess. When working under rmii and rgmii, you need to set different phy interfaces. According to the dwmac document, when working in rmii, it needs to be set to 0x4, and rgmii needs to be set to 0x1. The phy interface needs to be set in syscon, the format is as follows: starfive,syscon: <&syscon, offset, shift> Signed-off-by: Samin Guo Tested-by: Tommaso Merciai --- .../ethernet/stmicro/stmmac/dwmac-starfive.c | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c index ef5a769b1c754..84690c8f0250e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c @@ -13,6 +13,10 @@ #include "stmmac_platform.h" +#define STARFIVE_DWMAC_PHY_INFT_RGMII 0x1 +#define STARFIVE_DWMAC_PHY_INFT_RMII 0x4 +#define STARFIVE_DWMAC_PHY_INFT_FIELD 0x7U + struct starfive_dwmac { struct device *dev; struct clk *clk_tx; @@ -44,6 +48,43 @@ static void starfive_dwmac_fix_mac_speed(void *priv, unsigned int speed) dev_err(dwmac->dev, "failed to set tx rate %lu\n", rate); } +static int starfive_dwmac_set_mode(struct plat_stmmacenet_data *plat_dat) +{ + struct starfive_dwmac *dwmac = plat_dat->bsp_priv; + struct regmap *regmap; + unsigned int args[2]; + unsigned int mode; + + switch (plat_dat->interface) { + case PHY_INTERFACE_MODE_RMII: + mode = STARFIVE_DWMAC_PHY_INFT_RMII; + break; + + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + mode = STARFIVE_DWMAC_PHY_INFT_RGMII; + break; + + default: + dev_err(dwmac->dev, "unsupported interface %d\n", + plat_dat->interface); + return -EINVAL; + } + + regmap = syscon_regmap_lookup_by_phandle_args(dwmac->dev->of_node, + "starfive,syscon", + 2, args); + if (IS_ERR(regmap)) { + dev_err(dwmac->dev, "syscon regmap failed.\n"); + return -ENXIO; + } + + /* args[0]:offset args[1]: shift */ + return regmap_update_bits(regmap, args[0], + STARFIVE_DWMAC_PHY_INFT_FIELD << args[1], + mode << args[1]); +} + static int starfive_dwmac_probe(struct platform_device *pdev) { struct plat_stmmacenet_data *plat_dat; @@ -89,6 +130,12 @@ static int starfive_dwmac_probe(struct platform_device *pdev) plat_dat->bsp_priv = dwmac; plat_dat->dma_cfg->dche = true; + err = starfive_dwmac_set_mode(plat_dat); + if (err) { + dev_err(&pdev->dev, "dwmac set mode failed.\n"); + return err; + } + err = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); if (err) { stmmac_remove_config_dt(pdev, plat_dat); From 1e3ef8f7612510f10806822ca74a3cddf6de87c8 Mon Sep 17 00:00:00 2001 From: Samin Guo Date: Fri, 3 Mar 2023 16:49:31 +0800 Subject: [PATCH 62/95] riscv: dts: starfive: jh7110: Add ethernet device nodes Add JH7110 ethernet device node to support gmac driver for the JH7110 RISC-V SoC. Signed-off-by: Yanhong Wang Signed-off-by: Samin Guo Tested-by: Tommaso Merciai --- arch/riscv/boot/dts/starfive/jh7110.dtsi | 69 ++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi index 5634c77314a4b..8066076053020 100644 --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi @@ -252,6 +252,13 @@ #clock-cells = <0>; }; + stmmac_axi_setup: stmmac-axi-config { + snps,lpi_en; + snps,wr_osr_lmt = <4>; + snps,rd_osr_lmt = <4>; + snps,blen = <256 128 64 32 0 0 0>; + }; + tdm_ext: tdm-ext-clock { compatible = "fixed-clock"; clock-output-names = "tdm_ext"; @@ -645,6 +652,68 @@ status = "disabled"; }; + gmac0: ethernet@16030000 { + compatible = "starfive,jh7110-dwmac", "snps,dwmac-5.20"; + reg = <0x0 0x16030000 0x0 0x10000>; + clocks = <&aoncrg JH7110_AONCLK_GMAC0_AXI>, + <&aoncrg JH7110_AONCLK_GMAC0_AHB>, + <&syscrg JH7110_SYSCLK_GMAC0_PTP>, + <&aoncrg JH7110_AONCLK_GMAC0_TX_INV>, + <&syscrg JH7110_SYSCLK_GMAC0_GTXC>; + clock-names = "stmmaceth", "pclk", "ptp_ref", + "tx", "gtx"; + resets = <&aoncrg JH7110_AONRST_GMAC0_AXI>, + <&aoncrg JH7110_AONRST_GMAC0_AHB>; + reset-names = "stmmaceth", "ahb"; + interrupts = <7>, <6>, <5>; + interrupt-names = "macirq", "eth_wake_irq", "eth_lpi"; + snps,multicast-filter-bins = <64>; + snps,perfect-filter-entries = <8>; + rx-fifo-depth = <2048>; + tx-fifo-depth = <2048>; + snps,fixed-burst; + snps,no-pbl-x8; + snps,force_thresh_dma_mode; + snps,axi-config = <&stmmac_axi_setup>; + snps,tso; + snps,en-tx-lpi-clockgating; + snps,txpbl = <16>; + snps,rxpbl = <16>; + starfive,syscon = <&aon_syscon 0xc 0x12>; + status = "disabled"; + }; + + gmac1: ethernet@16040000 { + compatible = "starfive,jh7110-dwmac", "snps,dwmac-5.20"; + reg = <0x0 0x16040000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_GMAC1_AXI>, + <&syscrg JH7110_SYSCLK_GMAC1_AHB>, + <&syscrg JH7110_SYSCLK_GMAC1_PTP>, + <&syscrg JH7110_SYSCLK_GMAC1_TX_INV>, + <&syscrg JH7110_SYSCLK_GMAC1_GTXC>; + clock-names = "stmmaceth", "pclk", "ptp_ref", + "tx", "gtx"; + resets = <&syscrg JH7110_SYSRST_GMAC1_AXI>, + <&syscrg JH7110_SYSRST_GMAC1_AHB>; + reset-names = "stmmaceth", "ahb"; + interrupts = <78>, <77>, <76>; + interrupt-names = "macirq", "eth_wake_irq", "eth_lpi"; + snps,multicast-filter-bins = <64>; + snps,perfect-filter-entries = <8>; + rx-fifo-depth = <2048>; + tx-fifo-depth = <2048>; + snps,fixed-burst; + snps,no-pbl-x8; + snps,force_thresh_dma_mode; + snps,axi-config = <&stmmac_axi_setup>; + snps,tso; + snps,en-tx-lpi-clockgating; + snps,txpbl = <16>; + snps,rxpbl = <16>; + starfive,syscon = <&sys_syscon 0x90 0x2>; + status = "disabled"; + }; + aoncrg: clock-controller@17000000 { compatible = "starfive,jh7110-aoncrg"; reg = <0x0 0x17000000 0x0 0x10000>; From 754b157a79bbdc4955b503edd4d54c6e910a5671 Mon Sep 17 00:00:00 2001 From: Samin Guo Date: Tue, 1 Nov 2022 18:11:02 +0800 Subject: [PATCH 63/95] riscv: dts: starfive: visionfive 2: Add configuration of gmac and phy v1.3B: v1.3B uses motorcomm YT8531(rgmii-id phy) x2, need delay and inverse configurations. The tx_clk of v1.3B uses an external clock and needs to be switched to an external clock source. v1.2A: v1.2A gmac0 uses motorcomm YT8531(rgmii-id) PHY, and needs delay configurations. v1.2A gmac1 uses motorcomm YT8512(rmii) PHY, and needs to switch rx and rx to external clock sources. Signed-off-by: Samin Guo Tested-by: Tommaso Merciai --- .../jh7110-starfive-visionfive-2-v1.2a.dts | 13 +++++++ .../jh7110-starfive-visionfive-2-v1.3b.dts | 27 +++++++++++++++ .../jh7110-starfive-visionfive-2.dtsi | 34 +++++++++++++++++++ 3 files changed, 74 insertions(+) diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.2a.dts b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.2a.dts index 4af3300f3cf31..205a13d8c8b14 100644 --- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.2a.dts +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.2a.dts @@ -11,3 +11,16 @@ model = "StarFive VisionFive 2 v1.2A"; compatible = "starfive,visionfive-2-v1.2a", "starfive,jh7110"; }; + +&gmac1 { + phy-mode = "rmii"; + assigned-clocks = <&syscrg JH7110_SYSCLK_GMAC1_TX>, + <&syscrg JH7110_SYSCLK_GMAC1_RX>; + assigned-clock-parents = <&syscrg JH7110_SYSCLK_GMAC1_RMII_RTX>, + <&syscrg JH7110_SYSCLK_GMAC1_RMII_RTX>; +}; + +&phy0 { + rx-internal-delay-ps = <1900>; + tx-internal-delay-ps = <1350>; +}; diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.3b.dts b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.3b.dts index 9230cc3d89466..32fae0de9a444 100644 --- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.3b.dts +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.3b.dts @@ -11,3 +11,30 @@ model = "StarFive VisionFive 2 v1.3B"; compatible = "starfive,visionfive-2-v1.3b", "starfive,jh7110"; }; + +&gmac0 { + starfive,tx-use-rgmii-clk; + assigned-clocks = <&aoncrg JH7110_AONCLK_GMAC0_TX>; + assigned-clock-parents = <&aoncrg JH7110_AONCLK_GMAC0_RMII_RTX>; +}; + +&gmac1 { + starfive,tx-use-rgmii-clk; + assigned-clocks = <&syscrg JH7110_SYSCLK_GMAC1_TX>; + assigned-clock-parents = <&syscrg JH7110_SYSCLK_GMAC1_RMII_RTX>; +}; + +&phy0 { + motorcomm,tx-clk-adj-enabled; + motorcomm,tx-clk-100-inverted; + motorcomm,tx-clk-1000-inverted; + rx-internal-delay-ps = <1900>; + tx-internal-delay-ps = <1500>; +}; + +&phy1 { + motorcomm,tx-clk-adj-enabled; + motorcomm,tx-clk-100-inverted; + rx-internal-delay-ps = <0>; + tx-internal-delay-ps = <0>; +}; diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi index 4633a2c431153..c26cc769b02d3 100644 --- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi @@ -11,6 +11,8 @@ / { aliases { + ethernet0 = &gmac0; + ethernet1 = &gmac1; i2c0 = &i2c0; i2c2 = &i2c2; i2c5 = &i2c5; @@ -94,6 +96,38 @@ clock-frequency = <49152000>; }; +&gmac0 { + phy-handle = <&phy0>; + phy-mode = "rgmii-id"; + status = "okay"; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + compatible = "snps,dwmac-mdio"; + + phy0: ethernet-phy@0 { + reg = <0>; + }; + }; +}; + +&gmac1 { + phy-handle = <&phy1>; + phy-mode = "rgmii-id"; + status = "okay"; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + compatible = "snps,dwmac-mdio"; + + phy1: ethernet-phy@1 { + reg = <0>; + }; + }; +}; + &i2c0 { clock-frequency = <100000>; i2c-sda-hold-time-ns = <300>; From d7afd3df9eba8d0f172475ca727e0ef9f327dabb Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sun, 6 Jun 2021 22:15:22 +0200 Subject: [PATCH 64/95] dt-bindings: hwmon: Add starfive,jh71x0-temp Add bindings for the temperature sensor on the StarFive JH7100 and JH7110 SoCs. Signed-off-by: Emil Renner Berthing Signed-off-by: Hal Feng Reviewed-by: Rob Herring --- .../bindings/hwmon/starfive,jh71x0-temp.yaml | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/starfive,jh71x0-temp.yaml diff --git a/Documentation/devicetree/bindings/hwmon/starfive,jh71x0-temp.yaml b/Documentation/devicetree/bindings/hwmon/starfive,jh71x0-temp.yaml new file mode 100644 index 0000000000000..f5b34528928dd --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/starfive,jh71x0-temp.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/starfive,jh71x0-temp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH71x0 Temperature Sensor + +maintainers: + - Emil Renner Berthing + +description: | + StarFive Technology Co. JH71x0 embedded temperature sensor + +properties: + compatible: + enum: + - starfive,jh7100-temp + - starfive,jh7110-temp + + reg: + maxItems: 1 + + clocks: + minItems: 2 + maxItems: 2 + + clock-names: + items: + - const: "sense" + - const: "bus" + + '#thermal-sensor-cells': + const: 0 + + resets: + minItems: 2 + maxItems: 2 + + reset-names: + items: + - const: "sense" + - const: "bus" + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - reset-names + +additionalProperties: false + +examples: + - | + #include + #include + + temperature-sensor@124a0000 { + compatible = "starfive,jh7100-temp"; + reg = <0x124a0000 0x10000>; + clocks = <&clkgen JH7100_CLK_TEMP_SENSE>, + <&clkgen JH7100_CLK_TEMP_APB>; + clock-names = "sense", "bus"; + #thermal-sensor-cells = <0>; + resets = <&rstgen JH7100_RSTN_TEMP_SENSE>, + <&rstgen JH7100_RSTN_TEMP_APB>; + reset-names = "sense", "bus"; + }; From 8a4c5449a5ee616e92a5853eeea3247e9d67be70 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sun, 6 Jun 2021 22:31:18 +0200 Subject: [PATCH 65/95] hwmon: (sfctemp) Add StarFive JH71x0 temperature sensor Add driver for the StarFive JH71x0 temperature sensor. You can enable/disable it and read temperature in milli Celcius through sysfs. Signed-off-by: Emil Renner Berthing Co-developed-by: Samin Guo Signed-off-by: Samin Guo Signed-off-by: Hal Feng --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/sfctemp.rst | 33 ++++ MAINTAINERS | 8 + drivers/hwmon/Kconfig | 10 + drivers/hwmon/Makefile | 1 + drivers/hwmon/sfctemp.c | 331 ++++++++++++++++++++++++++++++++ 6 files changed, 384 insertions(+) create mode 100644 Documentation/hwmon/sfctemp.rst create mode 100644 drivers/hwmon/sfctemp.c diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index f1fe75f596a59..d72bb1df64319 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -184,6 +184,7 @@ Hardware Monitoring Kernel Drivers sch5627 sch5636 scpi-hwmon + sfctemp sht15 sht21 sht3x diff --git a/Documentation/hwmon/sfctemp.rst b/Documentation/hwmon/sfctemp.rst new file mode 100644 index 0000000000000..9fbd5bb1f356d --- /dev/null +++ b/Documentation/hwmon/sfctemp.rst @@ -0,0 +1,33 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver sfctemp +===================== + +Supported chips: + - StarFive JH7100 + - StarFive JH7110 + +Authors: + - Emil Renner Berthing + +Description +----------- + +This driver adds support for reading the built-in temperature sensor on the +JH7100 and JH7110 RISC-V SoCs by StarFive Technology Co. Ltd. + +``sysfs`` interface +------------------- + +The temperature sensor can be enabled, disabled and queried via the standard +hwmon interface in sysfs under ``/sys/class/hwmon/hwmonX`` for some value of +``X``: + +================ ==== ============================================= +Name Perm Description +================ ==== ============================================= +temp1_enable RW Enable or disable temperature sensor. + Automatically enabled by the driver, + but may be disabled to save power. +temp1_input RO Temperature reading in milli-degrees Celsius. +================ ==== ============================================= diff --git a/MAINTAINERS b/MAINTAINERS index 4df9628b3c855..5e581e1bcd750 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18911,6 +18911,14 @@ S: Supported F: Documentation/networking/devlink/sfc.rst F: drivers/net/ethernet/sfc/ +SFCTEMP HWMON DRIVER +M: Emil Renner Berthing +L: linux-hwmon@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/hwmon/starfive,jh71x0-temp.yaml +F: Documentation/hwmon/sfctemp.rst +F: drivers/hwmon/sfctemp.c + SFF/SFP/SFP+ MODULE SUPPORT M: Russell King L: netdev@vger.kernel.org diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5b3b76477b0e3..abb82a73dbafe 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1929,6 +1929,16 @@ config SENSORS_STTS751 This driver can also be built as a module. If so, the module will be called stts751. +config SENSORS_SFCTEMP + tristate "Starfive JH71x0 temperature sensor" + depends on ARCH_STARFIVE || COMPILE_TEST + help + If you say yes here you get support for temperature sensor + on the Starfive JH71x0 SoCs. + + This driver can also be built as a module. If so, the module + will be called sfctemp. + config SENSORS_SMM665 tristate "Summit Microelectronics SMM665" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 88712b5031c87..386f4debe68ba 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -181,6 +181,7 @@ obj-$(CONFIG_SENSORS_SBRMI) += sbrmi.o obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o +obj-$(CONFIG_SENSORS_SFCTEMP) += sfctemp.o obj-$(CONFIG_SENSORS_SL28CPLD) += sl28cpld-hwmon.o obj-$(CONFIG_SENSORS_SHT15) += sht15.o obj-$(CONFIG_SENSORS_SHT21) += sht21.o diff --git a/drivers/hwmon/sfctemp.c b/drivers/hwmon/sfctemp.c new file mode 100644 index 0000000000000..d7484e2b81005 --- /dev/null +++ b/drivers/hwmon/sfctemp.c @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Emil Renner Berthing + * Copyright (C) 2021 Samin Guo + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * TempSensor reset. The RSTN can be de-asserted once the analog core has + * powered up. Trst(min 100ns) + * 0:reset 1:de-assert + */ +#define SFCTEMP_RSTN BIT(0) + +/* + * TempSensor analog core power down. The analog core will be powered up + * Tpu(min 50us) after PD is de-asserted. RSTN should be held low until the + * analog core is powered up. + * 0:power up 1:power down + */ +#define SFCTEMP_PD BIT(1) + +/* + * TempSensor start conversion enable. + * 0:disable 1:enable + */ +#define SFCTEMP_RUN BIT(2) + +/* + * TempSensor conversion value output. + * Temp(C)=DOUT*Y/4094 - K + */ +#define SFCTEMP_DOUT_POS 16 +#define SFCTEMP_DOUT_MSK GENMASK(27, 16) + +/* DOUT to Celcius conversion constants */ +#define SFCTEMP_Y1000 237500L +#define SFCTEMP_Z 4094L +#define SFCTEMP_K1000 81100L + +struct sfctemp { + /* serialize access to hardware register and enabled below */ + struct mutex lock; + void __iomem *regs; + struct clk *clk_sense; + struct clk *clk_bus; + struct reset_control *rst_sense; + struct reset_control *rst_bus; + bool enabled; +}; + +static void sfctemp_power_up(struct sfctemp *sfctemp) +{ + /* make sure we're powered down first */ + writel(SFCTEMP_PD, sfctemp->regs); + udelay(1); + + writel(0, sfctemp->regs); + /* wait t_pu(50us) + t_rst(100ns) */ + usleep_range(60, 200); + + /* de-assert reset */ + writel(SFCTEMP_RSTN, sfctemp->regs); + udelay(1); /* wait t_su(500ps) */ +} + +static void sfctemp_power_down(struct sfctemp *sfctemp) +{ + writel(SFCTEMP_PD, sfctemp->regs); +} + +static void sfctemp_run(struct sfctemp *sfctemp) +{ + writel(SFCTEMP_RSTN | SFCTEMP_RUN, sfctemp->regs); + udelay(1); +} + +static void sfctemp_stop(struct sfctemp *sfctemp) +{ + writel(SFCTEMP_RSTN, sfctemp->regs); +} + +static int sfctemp_enable(struct sfctemp *sfctemp) +{ + int ret = 0; + + mutex_lock(&sfctemp->lock); + if (sfctemp->enabled) + goto done; + + ret = clk_prepare_enable(sfctemp->clk_bus); + if (ret) + goto err; + ret = reset_control_deassert(sfctemp->rst_bus); + if (ret) + goto err_disable_bus; + + ret = clk_prepare_enable(sfctemp->clk_sense); + if (ret) + goto err_assert_bus; + ret = reset_control_deassert(sfctemp->rst_sense); + if (ret) + goto err_disable_sense; + + sfctemp_power_up(sfctemp); + sfctemp_run(sfctemp); + sfctemp->enabled = true; +done: + mutex_unlock(&sfctemp->lock); + return ret; + +err_disable_sense: + clk_disable_unprepare(sfctemp->clk_sense); +err_assert_bus: + reset_control_assert(sfctemp->rst_bus); +err_disable_bus: + clk_disable_unprepare(sfctemp->clk_bus); +err: + mutex_unlock(&sfctemp->lock); + return ret; +} + +static int sfctemp_disable(struct sfctemp *sfctemp) +{ + mutex_lock(&sfctemp->lock); + if (!sfctemp->enabled) + goto done; + + sfctemp_stop(sfctemp); + sfctemp_power_down(sfctemp); + reset_control_assert(sfctemp->rst_sense); + clk_disable_unprepare(sfctemp->clk_sense); + reset_control_assert(sfctemp->rst_bus); + clk_disable_unprepare(sfctemp->clk_bus); + sfctemp->enabled = false; +done: + mutex_unlock(&sfctemp->lock); + return 0; +} + +static void sfctemp_disable_action(void *data) +{ + sfctemp_disable(data); +} + +static int sfctemp_convert(struct sfctemp *sfctemp, long *val) +{ + int ret; + + mutex_lock(&sfctemp->lock); + if (!sfctemp->enabled) { + ret = -ENODATA; + goto out; + } + + /* calculate temperature in milli Celcius */ + *val = (long)((readl(sfctemp->regs) & SFCTEMP_DOUT_MSK) >> SFCTEMP_DOUT_POS) + * SFCTEMP_Y1000 / SFCTEMP_Z - SFCTEMP_K1000; + + ret = 0; +out: + mutex_unlock(&sfctemp->lock); + return ret; +} + +static umode_t sfctemp_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_enable: + return 0644; + case hwmon_temp_input: + return 0444; + default: + return 0; + } + default: + return 0; + } +} + +static int sfctemp_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct sfctemp *sfctemp = dev_get_drvdata(dev); + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_enable: + *val = sfctemp->enabled; + return 0; + case hwmon_temp_input: + return sfctemp_convert(sfctemp, val); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int sfctemp_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct sfctemp *sfctemp = dev_get_drvdata(dev); + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_enable: + if (val == 0) + return sfctemp_disable(sfctemp); + if (val == 1) + return sfctemp_enable(sfctemp); + return -EINVAL; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static const struct hwmon_channel_info *sfctemp_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, HWMON_T_ENABLE | HWMON_T_INPUT), + NULL +}; + +static const struct hwmon_ops sfctemp_hwmon_ops = { + .is_visible = sfctemp_is_visible, + .read = sfctemp_read, + .write = sfctemp_write, +}; + +static const struct hwmon_chip_info sfctemp_chip_info = { + .ops = &sfctemp_hwmon_ops, + .info = sfctemp_info, +}; + +static int sfctemp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device *hwmon_dev; + struct sfctemp *sfctemp; + int ret; + + sfctemp = devm_kzalloc(dev, sizeof(*sfctemp), GFP_KERNEL); + if (!sfctemp) + return -ENOMEM; + + dev_set_drvdata(dev, sfctemp); + mutex_init(&sfctemp->lock); + + sfctemp->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(sfctemp->regs)) + return PTR_ERR(sfctemp->regs); + + sfctemp->clk_sense = devm_clk_get(dev, "sense"); + if (IS_ERR(sfctemp->clk_sense)) + return dev_err_probe(dev, PTR_ERR(sfctemp->clk_sense), + "error getting sense clock\n"); + + sfctemp->clk_bus = devm_clk_get(dev, "bus"); + if (IS_ERR(sfctemp->clk_bus)) + return dev_err_probe(dev, PTR_ERR(sfctemp->clk_bus), + "error getting bus clock\n"); + + sfctemp->rst_sense = devm_reset_control_get_exclusive(dev, "sense"); + if (IS_ERR(sfctemp->rst_sense)) + return dev_err_probe(dev, PTR_ERR(sfctemp->rst_sense), + "error getting sense reset\n"); + + sfctemp->rst_bus = devm_reset_control_get_exclusive(dev, "bus"); + if (IS_ERR(sfctemp->rst_bus)) + return dev_err_probe(dev, PTR_ERR(sfctemp->rst_bus), + "error getting busreset\n"); + + ret = reset_control_assert(sfctemp->rst_sense); + if (ret) + return dev_err_probe(dev, ret, "error asserting sense reset\n"); + + ret = reset_control_assert(sfctemp->rst_bus); + if (ret) + return dev_err_probe(dev, ret, "error asserting bus reset\n"); + + ret = devm_add_action(dev, sfctemp_disable_action, sfctemp); + if (ret) + return ret; + + ret = sfctemp_enable(sfctemp); + if (ret) + return dev_err_probe(dev, ret, "error enabling temperature sensor: %d\n", ret); + + hwmon_dev = devm_hwmon_device_register_with_info(dev, "sfctemp", sfctemp, + &sfctemp_chip_info, NULL); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct of_device_id sfctemp_of_match[] = { + { .compatible = "starfive,jh7100-temp" }, + { .compatible = "starfive,jh7110-temp" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sfctemp_of_match); + +static struct platform_driver sfctemp_driver = { + .probe = sfctemp_probe, + .driver = { + .name = "sfctemp", + .of_match_table = sfctemp_of_match, + }, +}; +module_platform_driver(sfctemp_driver); + +MODULE_AUTHOR("Emil Renner Berthing"); +MODULE_DESCRIPTION("StarFive JH71x0 temperature sensor driver"); +MODULE_LICENSE("GPL"); From 06ade2d16d3942a40df0cb89577b490d92c8427c Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sun, 6 Jun 2021 22:31:18 +0200 Subject: [PATCH 66/95] riscv: dts: starfive: jh7110: Add temperature sensor node Add temperature sensor support for StarFive JH7110 SoC. Signed-off-by: Emil Renner Berthing Signed-off-by: Hal Feng --- arch/riscv/boot/dts/starfive/jh7110.dtsi | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi index 8066076053020..04d0583ad9c19 100644 --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi @@ -502,6 +502,18 @@ status = "disabled"; }; + sfctemp: temperature-sensor@120e0000 { + compatible = "starfive,jh7110-temp"; + reg = <0x0 0x120e0000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_TEMP_CORE>, + <&syscrg JH7110_SYSCLK_TEMP_APB>; + clock-names = "sense", "bus"; + resets = <&syscrg JH7110_SYSRST_TEMP_CORE>, + <&syscrg JH7110_SYSRST_TEMP_APB>; + reset-names = "sense", "bus"; + #thermal-sensor-cells = <0>; + }; + qspi: spi@13010000 { compatible = "starfive,jh7110-qspi", "cdns,qspi-nor"; #address-cells = <1>; From aba4738bba936a901409b8e76cf8cb3fdd35b2b9 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Sun, 6 Jun 2021 22:31:18 +0200 Subject: [PATCH 67/95] riscv: dts: starfive: visionfive-2: Add thermal-zones Add thermal-zones for StarFive VisionFive 2 board. Signed-off-by: Emil Renner Berthing Signed-off-by: Hal Feng --- .../jh7110-starfive-visionfive-2.dtsi | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi index c26cc769b02d3..b4808fe6d013a 100644 --- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi @@ -33,6 +33,34 @@ reg = <0x0 0x40000000 0x1 0x0>; }; + thermal-zones { + cpu-thermal { + polling-delay-passive = <250>; + polling-delay = <15000>; + + thermal-sensors = <&sfctemp>; + + cooling-maps { + }; + + trips { + cpu_alert0: cpu_alert0 { + /* milliCelsius */ + temperature = <75000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu_crit: cpu_crit { + /* milliCelsius */ + temperature = <90000>; + hysteresis = <2000>; + type = "critical"; + }; + }; + }; + }; + gpio-restart { compatible = "gpio-restart"; gpios = <&sysgpio 35 GPIO_ACTIVE_HIGH>; From 9539bcf7ef4aabe72e4b3333b24a7db7753a44b2 Mon Sep 17 00:00:00 2001 From: Walker Chen Date: Tue, 14 Mar 2023 16:35:35 +0800 Subject: [PATCH 68/95] dt-bindings: dma: snps,dw-axi-dmac: constrain the items of resets for JH7110 dma The DMA controller needs two reset items to work properly on JH7110 SoC, so there is need to constrain the items' value to 2, other platforms have 1 reset item at most. Signed-off-by: Walker Chen Reviewed-by: Rob Herring --- .../bindings/dma/snps,dw-axi-dmac.yaml | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml b/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml index 5c81194e2300f..363cf8bd150da 100644 --- a/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml +++ b/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml @@ -20,6 +20,7 @@ properties: enum: - snps,axi-dma-1.01a - intel,kmb-axi-dma + - starfive,jh7110-axi-dma reg: minItems: 1 @@ -58,7 +59,8 @@ properties: maximum: 8 resets: - maxItems: 1 + minItems: 1 + maxItems: 2 snps,dma-masters: description: | @@ -109,6 +111,25 @@ required: - snps,priority - snps,block-size +if: + properties: + compatible: + contains: + enum: + - starfive,jh7110-axi-dma +then: + properties: + resets: + minItems: 2 + items: + - description: AXI reset line + - description: AHB reset line + - description: module reset +else: + properties: + resets: + maxItems: 1 + additionalProperties: false examples: From a8a131e43289a0da263d5522a6bb3cc9860b8ba2 Mon Sep 17 00:00:00 2001 From: Walker Chen Date: Tue, 14 Mar 2023 16:35:36 +0800 Subject: [PATCH 69/95] dmaengine: dw-axi-dmac: Add support for StarFive JH7110 DMA Add DMA reset operation in device probe and use different configuration on CH_CFG registers according to match data. Update all uses of of_device_is_compatible with of_device_get_match_data. Signed-off-by: Walker Chen --- .../dma/dw-axi-dmac/dw-axi-dmac-platform.c | 40 ++++++++++++++++--- drivers/dma/dw-axi-dmac/dw-axi-dmac.h | 1 + 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c index 4169e1d7d5ca5..4390c91af522f 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c @@ -21,10 +21,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -46,6 +48,10 @@ DMA_SLAVE_BUSWIDTH_32_BYTES | \ DMA_SLAVE_BUSWIDTH_64_BYTES) +#define AXI_DMA_FLAG_HAS_APB_REGS BIT(0) +#define AXI_DMA_FLAG_HAS_RESETS BIT(1) +#define AXI_DMA_FLAG_USE_CFG2 BIT(2) + static inline void axi_dma_iowrite32(struct axi_dma_chip *chip, u32 reg, u32 val) { @@ -86,7 +92,8 @@ static inline void axi_chan_config_write(struct axi_dma_chan *chan, cfg_lo = (config->dst_multblk_type << CH_CFG_L_DST_MULTBLK_TYPE_POS | config->src_multblk_type << CH_CFG_L_SRC_MULTBLK_TYPE_POS); - if (chan->chip->dw->hdata->reg_map_8_channels) { + if (chan->chip->dw->hdata->reg_map_8_channels && + !chan->chip->dw->hdata->use_cfg2) { cfg_hi = config->tt_fc << CH_CFG_H_TT_FC_POS | config->hs_sel_src << CH_CFG_H_HS_SEL_SRC_POS | config->hs_sel_dst << CH_CFG_H_HS_SEL_DST_POS | @@ -1140,7 +1147,7 @@ static int dma_chan_terminate_all(struct dma_chan *dchan) axi_chan_disable(chan); ret = readl_poll_timeout_atomic(chan->chip->regs + DMAC_CHEN, val, - !(val & chan_active), 1000, 10000); + !(val & chan_active), 1000, 200000); if (ret == -ETIMEDOUT) dev_warn(dchan2dev(dchan), "%s failed to stop\n", axi_chan_name(chan)); @@ -1367,10 +1374,11 @@ static int parse_device_properties(struct axi_dma_chip *chip) static int dw_probe(struct platform_device *pdev) { - struct device_node *node = pdev->dev.of_node; struct axi_dma_chip *chip; struct dw_axi_dma *dw; struct dw_axi_dma_hcfg *hdata; + struct reset_control *resets; + unsigned int flags; u32 i; int ret; @@ -1398,12 +1406,25 @@ static int dw_probe(struct platform_device *pdev) if (IS_ERR(chip->regs)) return PTR_ERR(chip->regs); - if (of_device_is_compatible(node, "intel,kmb-axi-dma")) { + flags = (uintptr_t)of_device_get_match_data(&pdev->dev); + if (flags & AXI_DMA_FLAG_HAS_APB_REGS) { chip->apb_regs = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(chip->apb_regs)) return PTR_ERR(chip->apb_regs); } + if (flags & AXI_DMA_FLAG_HAS_RESETS) { + resets = devm_reset_control_array_get_exclusive(&pdev->dev); + if (IS_ERR(resets)) + return PTR_ERR(resets); + + ret = reset_control_deassert(resets); + if (ret) + return ret; + } + + chip->dw->hdata->use_cfg2 = !!(flags & AXI_DMA_FLAG_USE_CFG2); + chip->core_clk = devm_clk_get(chip->dev, "core-clk"); if (IS_ERR(chip->core_clk)) return PTR_ERR(chip->core_clk); @@ -1554,8 +1575,15 @@ static const struct dev_pm_ops dw_axi_dma_pm_ops = { }; static const struct of_device_id dw_dma_of_id_table[] = { - { .compatible = "snps,axi-dma-1.01a" }, - { .compatible = "intel,kmb-axi-dma" }, + { + .compatible = "snps,axi-dma-1.01a" + }, { + .compatible = "intel,kmb-axi-dma", + .data = (void *)AXI_DMA_FLAG_HAS_APB_REGS, + }, { + .compatible = "starfive,jh7110-axi-dma", + .data = (void *)(AXI_DMA_FLAG_HAS_RESETS | AXI_DMA_FLAG_USE_CFG2), + }, {} }; MODULE_DEVICE_TABLE(of, dw_dma_of_id_table); diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h index e9d5eb0fd5948..eb267cb24f670 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h @@ -33,6 +33,7 @@ struct dw_axi_dma_hcfg { /* Register map for DMAX_NUM_CHANNELS <= 8 */ bool reg_map_8_channels; bool restrict_axi_burst_len; + bool use_cfg2; }; struct axi_dma_chan { From 62d31038f559915f21f455a40c7553aa06b93a5f Mon Sep 17 00:00:00 2001 From: Walker Chen Date: Mon, 27 Feb 2023 20:51:36 +0800 Subject: [PATCH 70/95] riscv: dts: starfive: add dma controller node Add the dma controller node for the Starfive JH7110 SoC. Signed-off-by: Walker Chen --- arch/riscv/boot/dts/starfive/jh7110.dtsi | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi index 04d0583ad9c19..0f42549b4570c 100644 --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi @@ -726,6 +726,24 @@ status = "disabled"; }; + dma: dma-controller@16050000 { + compatible = "starfive,jh7110-axi-dma"; + reg = <0x0 0x16050000 0x0 0x10000>; + clocks = <&stgcrg JH7110_STGCLK_DMA1P_AXI>, + <&stgcrg JH7110_STGCLK_DMA1P_AHB>; + clock-names = "core-clk", "cfgr-clk"; + resets = <&stgcrg JH7110_STGRST_DMA1P_AXI>, + <&stgcrg JH7110_STGRST_DMA1P_AHB>; + interrupts = <73>; + #dma-cells = <1>; + dma-channels = <4>; + snps,dma-masters = <1>; + snps,data-width = <3>; + snps,block-size = <65536 65536 65536 65536>; + snps,priority = <0 1 2 3>; + snps,axi-max-burst-len = <16>; + }; + aoncrg: clock-controller@17000000 { compatible = "starfive,jh7110-aoncrg"; reg = <0x0 0x17000000 0x0 0x10000>; From fd6ede56fdaabfc93d05e33225f69e94a38768ea Mon Sep 17 00:00:00 2001 From: Jia Jie Ho Date: Fri, 2 Dec 2022 14:25:38 +0800 Subject: [PATCH 71/95] riscv: dts: starfive: Add TRNG node for VisionFive 2 Adding StarFive TRNG controller node to VisionFive 2 board. Co-developed-by: Jenny Zhang Signed-off-by: Jenny Zhang Signed-off-by: Jia Jie Ho --- arch/riscv/boot/dts/starfive/jh7110.dtsi | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi index 0f42549b4570c..1f9fd1e1da613 100644 --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi @@ -632,6 +632,16 @@ <&syscrg JH7110_SYSRST_WDT_CORE>; }; + rng: rng@1600c000 { + compatible = "starfive,jh7110-trng"; + reg = <0x0 0x1600C000 0x0 0x4000>; + clocks = <&stgcrg JH7110_STGCLK_SEC_AHB>, + <&stgcrg JH7110_STGCLK_SEC_MISC_AHB>; + clock-names = "hclk", "ahb"; + resets = <&stgcrg JH7110_STGRST_SEC_AHB>; + interrupts = <30>; + }; + mmc0: mmc@16010000 { compatible = "starfive,jh7110-mmc"; reg = <0x0 0x16010000 0x0 0x10000>; From b30df28fdc5d5b13a558a4963a56623681333d5f Mon Sep 17 00:00:00 2001 From: Jia Jie Ho Date: Mon, 13 Mar 2023 21:56:43 +0800 Subject: [PATCH 72/95] dt-bindings: crypto: Add StarFive crypto module Add documentation to describe StarFive cryptographic engine. Co-developed-by: Huan Feng Signed-off-by: Huan Feng Signed-off-by: Jia Jie Ho Reviewed-by: Rob Herring --- .../crypto/starfive,jh7110-crypto.yaml | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 Documentation/devicetree/bindings/crypto/starfive,jh7110-crypto.yaml diff --git a/Documentation/devicetree/bindings/crypto/starfive,jh7110-crypto.yaml b/Documentation/devicetree/bindings/crypto/starfive,jh7110-crypto.yaml new file mode 100644 index 0000000000000..71a2876bd6e49 --- /dev/null +++ b/Documentation/devicetree/bindings/crypto/starfive,jh7110-crypto.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/crypto/starfive,jh7110-crypto.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive Cryptographic Module + +maintainers: + - Jia Jie Ho + - William Qiu + +properties: + compatible: + const: starfive,jh7110-crypto + + reg: + maxItems: 1 + + clocks: + items: + - description: Hardware reference clock + - description: AHB reference clock + + clock-names: + items: + - const: hclk + - const: ahb + + interrupts: + maxItems: 1 + + resets: + maxItems: 1 + + dmas: + items: + - description: TX DMA channel + - description: RX DMA channel + + dma-names: + items: + - const: tx + - const: rx + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - dmas + - dma-names + +additionalProperties: false + +examples: + - | + crypto: crypto@16000000 { + compatible = "starfive,jh7110-crypto"; + reg = <0x16000000 0x4000>; + clocks = <&clk 15>, <&clk 16>; + clock-names = "hclk", "ahb"; + interrupts = <28>; + resets = <&reset 3>; + dmas = <&dma 1 2>, + <&dma 0 2>; + dma-names = "tx", "rx"; + }; +... From 63a7e88a967c72e3d70e16a3cf32a352601e2ef3 Mon Sep 17 00:00:00 2001 From: Jia Jie Ho Date: Mon, 13 Mar 2023 21:56:44 +0800 Subject: [PATCH 73/95] crypto: starfive - Add crypto engine support Adding device probe and DMA init for StarFive cryptographic module. Co-developed-by: Huan Feng Signed-off-by: Huan Feng Signed-off-by: Jia Jie Ho --- MAINTAINERS | 7 + drivers/crypto/Kconfig | 1 + drivers/crypto/Makefile | 1 + drivers/crypto/starfive/Kconfig | 17 +++ drivers/crypto/starfive/Makefile | 4 + drivers/crypto/starfive/jh7110-cryp.c | 202 ++++++++++++++++++++++++++ drivers/crypto/starfive/jh7110-cryp.h | 67 +++++++++ 7 files changed, 299 insertions(+) create mode 100644 drivers/crypto/starfive/Kconfig create mode 100644 drivers/crypto/starfive/Makefile create mode 100644 drivers/crypto/starfive/jh7110-cryp.c create mode 100644 drivers/crypto/starfive/jh7110-cryp.h diff --git a/MAINTAINERS b/MAINTAINERS index 5e581e1bcd750..3cc4e0eb7d217 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19908,6 +19908,13 @@ M: Ion Badulescu S: Odd Fixes F: drivers/net/ethernet/adaptec/starfire* +STARFIVE CRYPTO DRIVER +M: Jia Jie Ho +M: William Qiu +S: Supported +F: Documentation/devicetree/bindings/crypto/starfive* +F: drivers/crypto/starfive/ + STARFIVE DEVICETREES M: Emil Renner Berthing S: Maintained diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index 3b2516d1433f7..c9b6a8747fa12 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -822,5 +822,6 @@ config CRYPTO_DEV_SA2UL source "drivers/crypto/keembay/Kconfig" source "drivers/crypto/aspeed/Kconfig" +source "drivers/crypto/starfive/Kconfig" endif # CRYPTO_HW diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile index 476f1a25ca326..5ff5242a18244 100644 --- a/drivers/crypto/Makefile +++ b/drivers/crypto/Makefile @@ -52,3 +52,4 @@ obj-y += xilinx/ obj-y += hisilicon/ obj-$(CONFIG_CRYPTO_DEV_AMLOGIC_GXL) += amlogic/ obj-y += keembay/ +obj-y += starfive/ diff --git a/drivers/crypto/starfive/Kconfig b/drivers/crypto/starfive/Kconfig new file mode 100644 index 0000000000000..73f39b6bc09fb --- /dev/null +++ b/drivers/crypto/starfive/Kconfig @@ -0,0 +1,17 @@ +# +# StarFive crypto drivers configuration +# + +config CRYPTO_DEV_JH7110 + tristate "StarFive JH7110 cryptographic engine driver" + depends on SOC_STARFIVE + select CRYPTO_ENGINE + select ARM_AMBA + select DMADEVICES + select AMBA_PL08X + help + Support for StarFive JH7110 crypto hardware acceleration engine. + This module provides acceleration for public key algo, + skciphers, AEAD and hash functions. + + If you choose 'M' here, this module will be called starfive-crypto. diff --git a/drivers/crypto/starfive/Makefile b/drivers/crypto/starfive/Makefile new file mode 100644 index 0000000000000..071a4872fb5fd --- /dev/null +++ b/drivers/crypto/starfive/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_CRYPTO_DEV_JH7110) += jh7110-crypto.o +starfive-crypto-objs := jh7110-cryp.o diff --git a/drivers/crypto/starfive/jh7110-cryp.c b/drivers/crypto/starfive/jh7110-cryp.c new file mode 100644 index 0000000000000..abd500f3c1f35 --- /dev/null +++ b/drivers/crypto/starfive/jh7110-cryp.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cryptographic API. + * + * Support for StarFive hardware cryptographic engine. + * Copyright (c) 2022 StarFive Technology + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "jh7110-cryp.h" + +#define DRIVER_NAME "starfive-crypto" + +struct starfive_dev_list { + struct list_head dev_list; + spinlock_t lock; /* protect dev_list */ +}; + +static struct starfive_dev_list dev_list = { + .dev_list = LIST_HEAD_INIT(dev_list.dev_list), + .lock = __SPIN_LOCK_UNLOCKED(dev_list.lock), +}; + +struct starfive_cryp_dev *starfive_cryp_find_dev(struct starfive_cryp_ctx *ctx) +{ + struct starfive_cryp_dev *cryp = NULL, *tmp; + + spin_lock_bh(&dev_list.lock); + if (!ctx->cryp) { + list_for_each_entry(tmp, &dev_list.dev_list, list) { + cryp = tmp; + break; + } + ctx->cryp = cryp; + } else { + cryp = ctx->cryp; + } + + spin_unlock_bh(&dev_list.lock); + + return cryp; +} + +static int starfive_dma_init(struct starfive_cryp_dev *cryp) +{ + dma_cap_mask_t mask; + + cryp->tx = NULL; + cryp->rx = NULL; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + cryp->tx = dma_request_chan(cryp->dev, "tx"); + if (IS_ERR(cryp->tx)) + return dev_err_probe(cryp->dev, PTR_ERR(cryp->tx), + "Error requesting tx dma channel.\n"); + + cryp->rx = dma_request_chan(cryp->dev, "rx"); + if (IS_ERR(cryp->rx)) { + dma_release_channel(cryp->tx); + return dev_err_probe(cryp->dev, PTR_ERR(cryp->rx), + "Error requesting rx dma channel.\n"); + } + + init_completion(&cryp->tx_comp); + init_completion(&cryp->rx_comp); + + return 0; +} + +static void starfive_dma_cleanup(struct starfive_cryp_dev *cryp) +{ + dma_release_channel(cryp->tx); + dma_release_channel(cryp->rx); +} + +static int starfive_cryp_probe(struct platform_device *pdev) +{ + struct starfive_cryp_dev *cryp; + struct resource *res; + int ret; + + cryp = devm_kzalloc(&pdev->dev, sizeof(*cryp), GFP_KERNEL); + if (!cryp) + return -ENOMEM; + + platform_set_drvdata(pdev, cryp); + cryp->dev = &pdev->dev; + + cryp->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(cryp->base)) + return dev_err_probe(&pdev->dev, PTR_ERR(cryp->base), + "Error remapping memory for platform device\n"); + + cryp->phys_base = res->start; + cryp->dma_maxburst = 32; + + cryp->hclk = devm_clk_get(&pdev->dev, "hclk"); + if (IS_ERR(cryp->hclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(cryp->hclk), + "Error getting hardware reference clock\n"); + + cryp->ahb = devm_clk_get(&pdev->dev, "ahb"); + if (IS_ERR(cryp->ahb)) + return dev_err_probe(&pdev->dev, PTR_ERR(cryp->ahb), + "Error getting ahb reference clock\n"); + + cryp->rst = devm_reset_control_get_shared(cryp->dev, NULL); + if (IS_ERR(cryp->rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(cryp->rst), + "Error getting hardware reset line\n"); + + clk_prepare_enable(cryp->hclk); + clk_prepare_enable(cryp->ahb); + reset_control_deassert(cryp->rst); + + spin_lock(&dev_list.lock); + list_add(&cryp->list, &dev_list.dev_list); + spin_unlock(&dev_list.lock); + + ret = starfive_dma_init(cryp); + if (ret) + goto err_dma_init; + + /* Initialize crypto engine */ + cryp->engine = crypto_engine_alloc_init(&pdev->dev, 1); + if (!cryp->engine) { + ret = -ENOMEM; + goto err_engine; + } + + ret = crypto_engine_start(cryp->engine); + if (ret) + goto err_engine_start; + + return 0; + +err_engine_start: + crypto_engine_exit(cryp->engine); +err_engine: + starfive_dma_cleanup(cryp); +err_dma_init: + spin_lock(&dev_list.lock); + list_del(&cryp->list); + spin_unlock(&dev_list.lock); + + return ret; +} + +static int starfive_cryp_remove(struct platform_device *pdev) +{ + struct starfive_cryp_dev *cryp = platform_get_drvdata(pdev); + + if (!cryp) + return -ENODEV; + + crypto_engine_stop(cryp->engine); + crypto_engine_exit(cryp->engine); + + starfive_dma_cleanup(cryp); + + spin_lock(&dev_list.lock); + list_del(&cryp->list); + spin_unlock(&dev_list.lock); + + clk_disable_unprepare(cryp->hclk); + clk_disable_unprepare(cryp->ahb); + reset_control_assert(cryp->rst); + + return 0; +} + +static const struct of_device_id starfive_dt_ids[] __maybe_unused = { + { .compatible = "starfive,jh7110-crypto", .data = NULL}, + {}, +}; +MODULE_DEVICE_TABLE(of, starfive_dt_ids); + +static struct platform_driver starfive_cryp_driver = { + .probe = starfive_cryp_probe, + .remove = starfive_cryp_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = starfive_dt_ids, + }, +}; + +module_platform_driver(starfive_cryp_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("StarFive Cryptographic Module"); diff --git a/drivers/crypto/starfive/jh7110-cryp.h b/drivers/crypto/starfive/jh7110-cryp.h new file mode 100644 index 0000000000000..2ac87ed3fb037 --- /dev/null +++ b/drivers/crypto/starfive/jh7110-cryp.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __STARFIVE_STR_H__ +#define __STARFIVE_STR_H__ + +#include +#include +#include + +#include + +#define STARFIVE_ALG_CR_OFFSET 0x0 +#define STARFIVE_ALG_FIFO_OFFSET 0x4 +#define STARFIVE_IE_MASK_OFFSET 0x8 +#define STARFIVE_IE_FLAG_OFFSET 0xc +#define STARFIVE_DMA_IN_LEN_OFFSET 0x10 +#define STARFIVE_DMA_OUT_LEN_OFFSET 0x14 + +#define STARFIVE_MSG_BUFFER_SIZE SZ_16K + +union starfive_alg_cr { + u32 v; + struct { + u32 start :1; + u32 aes_dma_en :1; + u32 rsvd_0 :1; + u32 hash_dma_en :1; + u32 alg_done :1; + u32 rsvd_1 :3; + u32 clear :1; + u32 rsvd_2 :23; + }; +}; + +struct starfive_cryp_ctx { + struct crypto_engine_ctx enginectx; + struct starfive_cryp_dev *cryp; + + u8 *buffer; +}; + +struct starfive_cryp_dev { + struct list_head list; + struct device *dev; + + struct clk *hclk; + struct clk *ahb; + struct reset_control *rst; + + void __iomem *base; + phys_addr_t phys_base; + + u32 dma_maxburst; + struct dma_chan *tx; + struct dma_chan *rx; + struct dma_slave_config cfg_in; + struct dma_slave_config cfg_out; + struct completion tx_comp; + struct completion rx_comp; + + struct crypto_engine *engine; + + union starfive_alg_cr alg_cr; +}; + +struct starfive_cryp_dev *starfive_cryp_find_dev(struct starfive_cryp_ctx *ctx); + +#endif From 5702ddea73748b7680809fb4114458927708f64a Mon Sep 17 00:00:00 2001 From: Jia Jie Ho Date: Tue, 22 Nov 2022 13:56:50 +0800 Subject: [PATCH 74/95] riscv: dts: starfive: Add crypto and DMA node for VisionFive 2 Add StarFive cryptographic module and dedicated DMA controller node to VisionFive 2 SoCs. Co-developed-by: Huan Feng Signed-off-by: Huan Feng Signed-off-by: Jia Jie Ho Acked-by: Palmer Dabbelt --- arch/riscv/boot/dts/starfive/jh7110.dtsi | 27 ++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi index 1f9fd1e1da613..cdf7fa9e94c14 100644 --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi @@ -632,6 +632,33 @@ <&syscrg JH7110_SYSRST_WDT_CORE>; }; + crypto: crypto@16000000 { + compatible = "starfive,jh7110-crypto"; + reg = <0x0 0x16000000 0x0 0x4000>; + clocks = <&stgcrg JH7110_STGCLK_SEC_AHB>, + <&stgcrg JH7110_STGCLK_SEC_MISC_AHB>; + clock-names = "hclk", "ahb"; + resets = <&stgcrg JH7110_STGRST_SEC_AHB>; + dmas = <&sdma 1 2>, <&sdma 0 2>; + dma-names = "tx", "rx"; + }; + + sdma: dma@16008000 { + compatible = "arm,pl080", "arm,primecell"; + arm,primecell-periphid = <0x00041080>; + reg = <0x0 0x16008000 0x0 0x4000>; + interrupts = <29>; + clocks = <&stgcrg JH7110_STGCLK_SEC_AHB>, + <&stgcrg JH7110_STGCLK_SEC_MISC_AHB>; + clock-names = "hclk", "apb_pclk"; + resets = <&stgcrg JH7110_STGRST_SEC_AHB>; + lli-bus-interface-ahb1; + mem-bus-interface-ahb1; + memcpy-burst-size = <256>; + memcpy-bus-width = <32>; + #dma-cells = <2>; + }; + rng: rng@1600c000 { compatible = "starfive,jh7110-trng"; reg = <0x0 0x1600C000 0x0 0x4000>; From bd03e8a32adf0e6c99d2a3fdda9fafb6a8ccfcf5 Mon Sep 17 00:00:00 2001 From: Jia Jie Ho Date: Mon, 13 Mar 2023 21:56:46 +0800 Subject: [PATCH 75/95] crypto: starfive - Add hash and HMAC support Adding hash/HMAC support for SHA-2 and SM3 to StarFive cryptographic module. Co-developed-by: Huan Feng Signed-off-by: Huan Feng Signed-off-by: Jia Jie Ho --- drivers/crypto/starfive/Kconfig | 4 + drivers/crypto/starfive/Makefile | 2 +- drivers/crypto/starfive/jh7110-cryp.c | 35 + drivers/crypto/starfive/jh7110-cryp.h | 69 +- drivers/crypto/starfive/jh7110-hash.c | 1041 +++++++++++++++++++++++++ 5 files changed, 1147 insertions(+), 4 deletions(-) create mode 100644 drivers/crypto/starfive/jh7110-hash.c diff --git a/drivers/crypto/starfive/Kconfig b/drivers/crypto/starfive/Kconfig index 73f39b6bc09fb..cde485910f884 100644 --- a/drivers/crypto/starfive/Kconfig +++ b/drivers/crypto/starfive/Kconfig @@ -6,6 +6,10 @@ config CRYPTO_DEV_JH7110 tristate "StarFive JH7110 cryptographic engine driver" depends on SOC_STARFIVE select CRYPTO_ENGINE + select CRYPTO_HMAC + select CRYPTO_SHA256 + select CRYPTO_SHA512 + select CRYPTO_SM3_GENERIC select ARM_AMBA select DMADEVICES select AMBA_PL08X diff --git a/drivers/crypto/starfive/Makefile b/drivers/crypto/starfive/Makefile index 071a4872fb5fd..2af49062e36d9 100644 --- a/drivers/crypto/starfive/Makefile +++ b/drivers/crypto/starfive/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_CRYPTO_DEV_JH7110) += jh7110-crypto.o -starfive-crypto-objs := jh7110-cryp.o +jh7110-crypto-objs := jh7110-cryp.o jh7110-hash.o diff --git a/drivers/crypto/starfive/jh7110-cryp.c b/drivers/crypto/starfive/jh7110-cryp.c index abd500f3c1f35..94c99aab308f7 100644 --- a/drivers/crypto/starfive/jh7110-cryp.c +++ b/drivers/crypto/starfive/jh7110-cryp.c @@ -85,10 +85,25 @@ static void starfive_dma_cleanup(struct starfive_cryp_dev *cryp) dma_release_channel(cryp->rx); } +static irqreturn_t starfive_cryp_irq(int irq, void *priv) +{ + u32 status; + struct starfive_cryp_dev *cryp = (struct starfive_cryp_dev *)priv; + + status = readl(cryp->base + STARFIVE_IE_FLAG_OFFSET); + if (status & STARFIVE_IE_FLAG_HASH_DONE) { + writel(STARFIVE_IE_MASK_HASH_DONE, cryp->base + STARFIVE_IE_MASK_OFFSET); + complete(&cryp->hash_done); + } + + return IRQ_HANDLED; +} + static int starfive_cryp_probe(struct platform_device *pdev) { struct starfive_cryp_dev *cryp; struct resource *res; + int irq; int ret; cryp = devm_kzalloc(&pdev->dev, sizeof(*cryp), GFP_KERNEL); @@ -121,6 +136,18 @@ static int starfive_cryp_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, PTR_ERR(cryp->rst), "Error getting hardware reset line\n"); + init_completion(&cryp->hash_done); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(&pdev->dev, irq, starfive_cryp_irq, 0, pdev->name, + (void *)cryp); + if (ret) + return dev_err_probe(&pdev->dev, irq, + "Failed to register interrupt handler\n"); + clk_prepare_enable(cryp->hclk); clk_prepare_enable(cryp->ahb); reset_control_deassert(cryp->rst); @@ -144,8 +171,14 @@ static int starfive_cryp_probe(struct platform_device *pdev) if (ret) goto err_engine_start; + ret = starfive_hash_register_algs(); + if (ret) + goto err_algs_hash; + return 0; +err_algs_hash: + crypto_engine_stop(cryp->engine); err_engine_start: crypto_engine_exit(cryp->engine); err_engine: @@ -165,6 +198,8 @@ static int starfive_cryp_remove(struct platform_device *pdev) if (!cryp) return -ENODEV; + starfive_hash_unregister_algs(); + crypto_engine_stop(cryp->engine); crypto_engine_exit(cryp->engine); diff --git a/drivers/crypto/starfive/jh7110-cryp.h b/drivers/crypto/starfive/jh7110-cryp.h index 2ac87ed3fb037..28cc73308031d 100644 --- a/drivers/crypto/starfive/jh7110-cryp.h +++ b/drivers/crypto/starfive/jh7110-cryp.h @@ -7,6 +7,8 @@ #include #include +#include +#include #define STARFIVE_ALG_CR_OFFSET 0x0 #define STARFIVE_ALG_FIFO_OFFSET 0x4 @@ -15,7 +17,44 @@ #define STARFIVE_DMA_IN_LEN_OFFSET 0x10 #define STARFIVE_DMA_OUT_LEN_OFFSET 0x14 +#define STARFIVE_IE_MASK_HASH_DONE BIT(2) +#define STARFIVE_IE_FLAG_HASH_DONE BIT(2) + #define STARFIVE_MSG_BUFFER_SIZE SZ_16K +#define MAX_KEY_SIZE SHA512_BLOCK_SIZE + +union starfive_hash_csr { + u32 v; + struct { + u32 start :1; + u32 reset :1; + u32 ie :1; + u32 firstb :1; +#define STARFIVE_HASH_SM3 0x0 +#define STARFIVE_HASH_SHA224 0x3 +#define STARFIVE_HASH_SHA256 0x4 +#define STARFIVE_HASH_SHA384 0x5 +#define STARFIVE_HASH_SHA512 0x6 +#define STARFIVE_HASH_MODE_MASK 0x7 + u32 mode :3; + u32 rsvd_1 :1; + u32 final :1; + u32 rsvd_2 :2; +#define STARFIVE_HASH_HMAC_FLAGS 0x800 + u32 hmac :1; + u32 rsvd_3 :1; +#define STARFIVE_HASH_KEY_DONE BIT(13) + u32 key_done :1; + u32 key_flag :1; +#define STARFIVE_HASH_HMAC_DONE BIT(15) + u32 hmac_done :1; +#define STARFIVE_HASH_BUSY BIT(16) + u32 busy :1; + u32 hashdone :1; + u32 rsvd_4 :14; + }; +}; + union starfive_alg_cr { u32 v; @@ -34,14 +73,17 @@ union starfive_alg_cr { struct starfive_cryp_ctx { struct crypto_engine_ctx enginectx; struct starfive_cryp_dev *cryp; + struct starfive_cryp_request_ctx *rctx; - u8 *buffer; + unsigned int hash_mode; + u8 key[MAX_KEY_SIZE]; + int keylen; + struct crypto_ahash *ahash_fbk; }; struct starfive_cryp_dev { struct list_head list; struct device *dev; - struct clk *hclk; struct clk *ahb; struct reset_control *rst; @@ -56,12 +98,33 @@ struct starfive_cryp_dev { struct dma_slave_config cfg_out; struct completion tx_comp; struct completion rx_comp; - + struct completion hash_done; struct crypto_engine *engine; union starfive_alg_cr alg_cr; }; +struct starfive_cryp_request_ctx { + union { + struct ahash_request *hreq; + } req; + union { + union starfive_hash_csr hash; + } csr; + + struct scatterlist *in_sg; + struct scatterlist in_sgl; + struct ahash_request ahash_fbk_req; + size_t total; + unsigned int blksize; + unsigned int digsize; + unsigned long in_sg_len; + bool sgs_copied; +}; + struct starfive_cryp_dev *starfive_cryp_find_dev(struct starfive_cryp_ctx *ctx); +int starfive_hash_register_algs(void); +void starfive_hash_unregister_algs(void); + #endif diff --git a/drivers/crypto/starfive/jh7110-hash.c b/drivers/crypto/starfive/jh7110-hash.c new file mode 100644 index 0000000000000..39570f517ccca --- /dev/null +++ b/drivers/crypto/starfive/jh7110-hash.c @@ -0,0 +1,1041 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hash function and HMAC support for StarFive driver + * + * Copyright (c) 2022 StarFive Technology + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "jh7110-cryp.h" + +#define STARFIVE_HASH_REGS_OFFSET 0x300 +#define STARFIVE_HASH_SHACSR (STARFIVE_HASH_REGS_OFFSET + 0x0) +#define STARFIVE_HASH_SHAWDR (STARFIVE_HASH_REGS_OFFSET + 0x4) +#define STARFIVE_HASH_SHARDR (STARFIVE_HASH_REGS_OFFSET + 0x8) +#define STARFIVE_HASH_SHAWSR (STARFIVE_HASH_REGS_OFFSET + 0xC) +#define STARFIVE_HASH_SHAWLEN3 (STARFIVE_HASH_REGS_OFFSET + 0x10) +#define STARFIVE_HASH_SHAWLEN2 (STARFIVE_HASH_REGS_OFFSET + 0x14) +#define STARFIVE_HASH_SHAWLEN1 (STARFIVE_HASH_REGS_OFFSET + 0x18) +#define STARFIVE_HASH_SHAWLEN0 (STARFIVE_HASH_REGS_OFFSET + 0x1C) +#define STARFIVE_HASH_SHAWKR (STARFIVE_HASH_REGS_OFFSET + 0x20) +#define STARFIVE_HASH_SHAWKLEN (STARFIVE_HASH_REGS_OFFSET + 0x24) + +#define STARFIVE_HASH_BUFLEN SHA512_BLOCK_SIZE + +static inline int starfive_hash_wait_busy(struct starfive_cryp_ctx *ctx) +{ + struct starfive_cryp_dev *cryp = ctx->cryp; + u32 status; + + return readl_relaxed_poll_timeout(cryp->base + STARFIVE_HASH_SHACSR, status, + !(status & STARFIVE_HASH_BUSY), 10, 100000); +} + +static inline int starfive_hash_wait_key_done(struct starfive_cryp_ctx *ctx) +{ + struct starfive_cryp_dev *cryp = ctx->cryp; + u32 status; + + return readl_relaxed_poll_timeout(cryp->base + STARFIVE_HASH_SHACSR, status, + (status & STARFIVE_HASH_KEY_DONE), 10, 100000); +} + +static void starfive_hash_start(struct starfive_cryp_ctx *ctx) +{ + struct starfive_cryp_request_ctx *rctx = ctx->rctx; + struct starfive_cryp_dev *cryp = ctx->cryp; + + rctx->csr.hash.v = readl(cryp->base + STARFIVE_HASH_SHACSR); + rctx->csr.hash.firstb = 0; + rctx->csr.hash.final = 1; + + writel(rctx->csr.hash.v, cryp->base + STARFIVE_HASH_SHACSR); +} + +static int starfive_hash_hmac_key(struct starfive_cryp_ctx *ctx) +{ + struct starfive_cryp_request_ctx *rctx = ctx->rctx; + struct starfive_cryp_dev *cryp = ctx->cryp; + int klen = ctx->keylen, loop; + unsigned int *key = (unsigned int *)ctx->key; + unsigned char *cl; + + writel(ctx->keylen, cryp->base + STARFIVE_HASH_SHAWKLEN); + + rctx->csr.hash.hmac = !!(ctx->hash_mode & STARFIVE_HASH_HMAC_FLAGS); + rctx->csr.hash.key_flag = 1; + + writel(rctx->csr.hash.v, cryp->base + STARFIVE_HASH_SHACSR); + + for (loop = 0; loop < klen / sizeof(unsigned int); loop++, key++) + writel(*key, cryp->base + STARFIVE_HASH_SHAWKR); + + if (klen & 0x3) { + cl = (unsigned char *)key; + for (loop = 0; loop < (klen & 0x3); loop++, cl++) + writeb(*cl, cryp->base + STARFIVE_HASH_SHAWKR); + } + + if (starfive_hash_wait_key_done(ctx)) + return dev_err_probe(cryp->dev, -ETIMEDOUT, "starfive_hash_wait_key_done error\n"); + + return 0; +} + +static void starfive_hash_dma_callback(void *param) +{ + struct starfive_cryp_dev *cryp = param; + + complete(&cryp->tx_comp); +} + +static int starfive_hash_xmit_dma(struct starfive_cryp_ctx *ctx) +{ + struct starfive_cryp_request_ctx *rctx = ctx->rctx; + struct starfive_cryp_dev *cryp = ctx->cryp; + struct dma_async_tx_descriptor *in_desc; + dma_cookie_t cookie; + union starfive_alg_cr alg_cr; + int total_len; + int ret; + + if (!rctx->total) + return 0; + + writel(rctx->total, cryp->base + STARFIVE_DMA_IN_LEN_OFFSET); + + total_len = rctx->total; + total_len = (total_len & 0x3) ? (((total_len >> 2) + 1) << 2) : total_len; + sg_dma_len(rctx->in_sg) = total_len; + + alg_cr.v = 0; + alg_cr.start = 1; + alg_cr.hash_dma_en = 1; + + writel(alg_cr.v, cryp->base + STARFIVE_ALG_CR_OFFSET); + + ret = dma_map_sg(cryp->dev, rctx->in_sg, rctx->in_sg_len, DMA_TO_DEVICE); + if (!ret) + return dev_err_probe(cryp->dev, -EINVAL, "dma_map_sg() error\n"); + + cryp->cfg_in.direction = DMA_MEM_TO_DEV; + cryp->cfg_in.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cryp->cfg_in.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cryp->cfg_in.src_maxburst = cryp->dma_maxburst; + cryp->cfg_in.dst_maxburst = cryp->dma_maxburst; + cryp->cfg_in.dst_addr = cryp->phys_base + STARFIVE_ALG_FIFO_OFFSET; + + dmaengine_slave_config(cryp->tx, &cryp->cfg_in); + + in_desc = dmaengine_prep_slave_sg(cryp->tx, rctx->in_sg, + rctx->in_sg_len, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + + if (!in_desc) + return -EINVAL; + + reinit_completion(&cryp->tx_comp); + + in_desc->callback = starfive_hash_dma_callback; + in_desc->callback_param = cryp; + + cookie = dmaengine_submit(in_desc); + dma_async_issue_pending(cryp->tx); + + if (!wait_for_completion_timeout(&cryp->tx_comp, + msecs_to_jiffies(10000))) { + dev_err(cryp->dev, "wait_for_completion_timeout error, cookie = %x\n", + dma_async_is_tx_complete(cryp->rx, cookie, + NULL, NULL)); + } + + dma_unmap_sg(cryp->dev, rctx->in_sg, rctx->in_sg_len, DMA_TO_DEVICE); + + alg_cr.v = 0; + alg_cr.clear = 1; + + writel(alg_cr.v, cryp->base + STARFIVE_ALG_CR_OFFSET); + + return 0; +} + +static int starfive_hash_xmit(struct starfive_cryp_ctx *ctx) +{ + struct starfive_cryp_request_ctx *rctx = ctx->rctx; + struct starfive_cryp_dev *cryp = ctx->cryp; + int ret; + + rctx->csr.hash.v = 0; + rctx->csr.hash.reset = 1; + writel(rctx->csr.hash.v, cryp->base + STARFIVE_HASH_SHACSR); + + if (starfive_hash_wait_busy(ctx)) + return dev_err_probe(cryp->dev, -ETIMEDOUT, "Error resetting engine.\n"); + + rctx->csr.hash.v = 0; + rctx->csr.hash.mode = ctx->hash_mode & STARFIVE_HASH_MODE_MASK; + rctx->csr.hash.ie = 1; + + if (ctx->hash_mode & STARFIVE_HASH_HMAC_FLAGS) { + ret = starfive_hash_hmac_key(ctx); + if (ret) + return ret; + } else { + rctx->csr.hash.start = 1; + rctx->csr.hash.firstb = 1; + writel(rctx->csr.hash.v, cryp->base + STARFIVE_HASH_SHACSR); + } + + ret = starfive_hash_xmit_dma(ctx); + if (ret) + return ret; + + reinit_completion(&cryp->hash_done); + writel(~STARFIVE_IE_MASK_HASH_DONE, cryp->base + STARFIVE_IE_MASK_OFFSET); + + starfive_hash_start(ctx); + + if (!wait_for_completion_timeout(&cryp->hash_done, usecs_to_jiffies(1000))) + return dev_err_probe(cryp->dev, -ETIMEDOUT, "Timeout waiting for hash done\n"); + + return 0; +} + +static int starfive_hash_copy_hash(struct ahash_request *req) +{ + struct starfive_cryp_request_ctx *rctx = ahash_request_ctx(req); + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + int count, *data; + int mlen; + + if (!req->result) + return 0; + + mlen = rctx->digsize / sizeof(u32); + data = (u32 *)req->result; + + for (count = 0; count < mlen; count++) + data[count] = readl(ctx->cryp->base + STARFIVE_HASH_SHARDR); + + return 0; +} + +static void starfive_hash_finish_req(struct ahash_request *req, int err) +{ + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + struct starfive_cryp_dev *cryp = ctx->cryp; + struct starfive_cryp_request_ctx *rctx = ahash_request_ctx(req); + + if (!err) + err = starfive_hash_copy_hash(req); + + if (rctx->sgs_copied) { + void *buf_in; + int pages, len; + + buf_in = sg_virt(&rctx->in_sgl); + len = ALIGN(rctx->total, rctx->blksize); + pages = len ? get_order(len) : 1; + free_pages((unsigned long)buf_in, pages); + } + + crypto_finalize_hash_request(cryp->engine, req, err); +} + +static int starfive_hash_check_aligned(struct scatterlist *sg, size_t total, size_t align) +{ + int len = 0; + + if (!total) + return 0; + + if (!IS_ALIGNED(total, align)) + return -EINVAL; + + while (sg) { + if (!IS_ALIGNED(sg->offset, sizeof(u32))) + return -EINVAL; + + if (!IS_ALIGNED(sg->length, align)) + return -EINVAL; + + len += sg->length; + sg = sg_next(sg); + } + + if (len != total) + return -EINVAL; + + return 0; +} + +static int starfive_hash_check_io_aligned(struct starfive_cryp_request_ctx *rctx) +{ + return starfive_hash_check_aligned(rctx->in_sg, rctx->total, rctx->blksize); +} + +static void sg_copy_buf(void *buf, struct scatterlist *sg, + unsigned int start, unsigned int nbytes, int out) +{ + struct scatter_walk walk; + + if (!nbytes) + return; + + scatterwalk_start(&walk, sg); + scatterwalk_advance(&walk, start); + scatterwalk_copychunks(buf, &walk, nbytes, out); + scatterwalk_done(&walk, out, 0); +} + +static int starfive_hash_copy_sgs(struct starfive_cryp_request_ctx *rctx) +{ + void *buf_in; + int pages, total_in; + + if (!starfive_hash_check_io_aligned(rctx)) { + rctx->sgs_copied = 0; + return 0; + } + + total_in = ALIGN(rctx->total, rctx->blksize); + pages = total_in ? get_order(total_in) : 1; + buf_in = (void *)__get_free_pages(GFP_ATOMIC, pages); + if (!buf_in) { + rctx->sgs_copied = 0; + return -EFAULT; + } + + sg_copy_buf(buf_in, rctx->in_sg, 0, rctx->total, 0); + sg_init_one(&rctx->in_sgl, buf_in, total_in); + + rctx->in_sg = &rctx->in_sgl; + rctx->in_sg_len = 1; + rctx->sgs_copied = 1; + + return 0; +} + +static int starfive_hash_prepare_req(struct crypto_engine *engine, void *areq) +{ + struct ahash_request *req = container_of(areq, struct ahash_request, + base); + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + struct starfive_cryp_dev *cryp = ctx->cryp; + struct starfive_cryp_request_ctx *rctx = ahash_request_ctx(req); + + if (!cryp) + return -ENODEV; + + rctx->req.hreq = req; + + return starfive_hash_copy_sgs(rctx); +} + +static int starfive_hash_one_request(struct crypto_engine *engine, void *areq) +{ + struct ahash_request *req = container_of(areq, struct ahash_request, + base); + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + struct starfive_cryp_dev *cryp = ctx->cryp; + int err; + + if (!cryp) + return -ENODEV; + + err = starfive_hash_xmit(ctx); + if (err) + return err; + + starfive_hash_finish_req(req, err); + + return 0; +} + +static int starfive_hash_init(struct ahash_request *req) +{ + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct starfive_cryp_request_ctx *rctx = ahash_request_ctx(req); + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm); + + ahash_request_set_tfm(&rctx->ahash_fbk_req, ctx->ahash_fbk); + rctx->ahash_fbk_req.base.flags = req->base.flags + & CRYPTO_TFM_REQ_MAY_SLEEP; + + return crypto_ahash_init(&rctx->ahash_fbk_req); +} + +static int starfive_hash_update(struct ahash_request *req) +{ + struct starfive_cryp_request_ctx *rctx = ahash_request_ctx(req); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm); + + ahash_request_set_tfm(&rctx->ahash_fbk_req, ctx->ahash_fbk); + rctx->ahash_fbk_req.base.flags = req->base.flags + & CRYPTO_TFM_REQ_MAY_SLEEP; + rctx->ahash_fbk_req.nbytes = req->nbytes; + rctx->ahash_fbk_req.src = req->src; + + return crypto_ahash_update(&rctx->ahash_fbk_req); +} + +static int starfive_hash_final(struct ahash_request *req) +{ + struct starfive_cryp_request_ctx *rctx = ahash_request_ctx(req); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm); + + ahash_request_set_tfm(&rctx->ahash_fbk_req, ctx->ahash_fbk); + rctx->ahash_fbk_req.base.flags = req->base.flags + & CRYPTO_TFM_REQ_MAY_SLEEP; + rctx->ahash_fbk_req.result = req->result; + + return crypto_ahash_final(&rctx->ahash_fbk_req); +} + +static int starfive_hash_finup(struct ahash_request *req) +{ + struct starfive_cryp_request_ctx *rctx = ahash_request_ctx(req); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm); + + ahash_request_set_tfm(&rctx->ahash_fbk_req, ctx->ahash_fbk); + rctx->ahash_fbk_req.base.flags = req->base.flags + & CRYPTO_TFM_REQ_MAY_SLEEP; + rctx->ahash_fbk_req.nbytes = req->nbytes; + rctx->ahash_fbk_req.src = req->src; + rctx->ahash_fbk_req.result = req->result; + + return crypto_ahash_finup(&rctx->ahash_fbk_req); +} + +static int starfive_hash_digest(struct ahash_request *req) +{ + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm); + struct starfive_cryp_request_ctx *rctx = ahash_request_ctx(req); + struct starfive_cryp_dev *cryp = ctx->cryp; + + memset(rctx, 0, sizeof(struct starfive_cryp_request_ctx)); + + rctx->req.hreq = req; + rctx->total = req->nbytes; + rctx->in_sg = req->src; + rctx->blksize = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm)); + rctx->digsize = crypto_ahash_digestsize(tfm); + rctx->in_sg_len = sg_nents_for_len(rctx->in_sg, rctx->total); + ctx->rctx = rctx; + + return crypto_transfer_hash_request_to_engine(cryp->engine, req); +} + +static int starfive_hash_export(struct ahash_request *req, void *out) +{ + struct starfive_cryp_request_ctx *rctx = ahash_request_ctx(req); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm); + + ahash_request_set_tfm(&rctx->ahash_fbk_req, ctx->ahash_fbk); + rctx->ahash_fbk_req.base.flags = req->base.flags + & CRYPTO_TFM_REQ_MAY_SLEEP; + + return crypto_ahash_export(&rctx->ahash_fbk_req, out); +} + +static int starfive_hash_import(struct ahash_request *req, const void *in) +{ + struct starfive_cryp_request_ctx *rctx = ahash_request_ctx(req); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm); + + ahash_request_set_tfm(&rctx->ahash_fbk_req, ctx->ahash_fbk); + rctx->ahash_fbk_req.base.flags = req->base.flags + & CRYPTO_TFM_REQ_MAY_SLEEP; + + return crypto_ahash_import(&rctx->ahash_fbk_req, in); +} + +static int starfive_hash_cra_init_algs(struct crypto_tfm *tfm, + const char *algs_hmac_name, + unsigned int mode, + const char *alg_name) +{ + struct starfive_cryp_ctx *ctx = crypto_tfm_ctx(tfm); + struct crypto_ahash *ahash = __crypto_ahash_cast(tfm); + + ctx->cryp = starfive_cryp_find_dev(ctx); + + if (!ctx->cryp) + return -ENODEV; + + ctx->ahash_fbk = crypto_alloc_ahash(alg_name, 0, + CRYPTO_ALG_NEED_FALLBACK); + + if (IS_ERR(ctx->ahash_fbk)) + return dev_err_probe(ctx->cryp->dev, PTR_ERR(ctx->ahash_fbk), + "starfive_hash: Could not load fallback driver.\n"); + + crypto_hash_alg_common(ahash)->statesize = crypto_ahash_statesize(ctx->ahash_fbk); + crypto_ahash_set_reqsize(ahash, sizeof(struct starfive_cryp_request_ctx) + + crypto_ahash_reqsize(ctx->ahash_fbk)); + + ctx->keylen = 0; + ctx->hash_mode = mode; + + if (algs_hmac_name) + ctx->hash_mode |= STARFIVE_HASH_HMAC_FLAGS; + + ctx->enginectx.op.do_one_request = starfive_hash_one_request; + ctx->enginectx.op.prepare_request = starfive_hash_prepare_req; + ctx->enginectx.op.unprepare_request = NULL; + + return 0; +} + +static void starfive_hash_cra_exit(struct crypto_tfm *tfm) +{ + struct starfive_cryp_ctx *ctx = crypto_tfm_ctx(tfm); + + crypto_free_ahash(ctx->ahash_fbk); + + ctx->ahash_fbk = NULL; + ctx->enginectx.op.do_one_request = NULL; + ctx->enginectx.op.prepare_request = NULL; + ctx->enginectx.op.unprepare_request = NULL; +} + +static int starfive_hash_long_setkey(struct starfive_cryp_ctx *ctx, + const u8 *key, unsigned int keylen, + const char *alg_name) +{ + struct crypto_wait wait; + struct ahash_request *req; + struct scatterlist sg; + struct crypto_ahash *ahash_tfm; + u8 *buf; + int ret; + + ahash_tfm = crypto_alloc_ahash(alg_name, 0, 0); + if (IS_ERR(ahash_tfm)) + return PTR_ERR(ahash_tfm); + + req = ahash_request_alloc(ahash_tfm, GFP_KERNEL); + if (!req) { + ret = -ENOMEM; + goto err_free_ahash; + } + + crypto_init_wait(&wait); + ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + crypto_req_done, &wait); + crypto_ahash_clear_flags(ahash_tfm, ~0); + + buf = kzalloc(keylen + STARFIVE_HASH_BUFLEN, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto err_free_req; + } + + memcpy(buf, key, keylen); + sg_init_one(&sg, buf, keylen); + ahash_request_set_crypt(req, &sg, ctx->key, keylen); + + ret = crypto_wait_req(crypto_ahash_digest(req), &wait); + + kfree(buf); +err_free_req: + ahash_request_free(req); +err_free_ahash: + crypto_free_ahash(ahash_tfm); + return ret; +} + +static int starfive_hash224_setkey(struct crypto_ahash *tfm, + const u8 *key, unsigned int keylen) +{ + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm); + unsigned int digestsize = crypto_ahash_digestsize(tfm); + unsigned int blocksize; + int ret = 0; + + blocksize = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm)); + crypto_ahash_setkey(ctx->ahash_fbk, key, keylen); + + if (keylen <= blocksize) { + memcpy(ctx->key, key, keylen); + ctx->keylen = keylen; + } else { + ctx->keylen = digestsize; + ret = starfive_hash_long_setkey(ctx, key, keylen, "starfive-sha224"); + } + + return ret; +} + +static int starfive_hash256_setkey(struct crypto_ahash *tfm, + const u8 *key, unsigned int keylen) +{ + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm); + unsigned int digestsize = crypto_ahash_digestsize(tfm); + unsigned int blocksize; + int ret = 0; + + blocksize = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm)); + crypto_ahash_setkey(ctx->ahash_fbk, key, keylen); + + if (keylen <= blocksize) { + memcpy(ctx->key, key, keylen); + ctx->keylen = keylen; + } else { + ctx->keylen = digestsize; + ret = starfive_hash_long_setkey(ctx, key, keylen, "starfive-sha256"); + } + + return ret; +} + +static int starfive_hash384_setkey(struct crypto_ahash *tfm, + const u8 *key, unsigned int keylen) +{ + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm); + unsigned int digestsize = crypto_ahash_digestsize(tfm); + unsigned int blocksize; + int ret = 0; + + blocksize = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm)); + crypto_ahash_setkey(ctx->ahash_fbk, key, keylen); + + if (keylen <= blocksize) { + memcpy(ctx->key, key, keylen); + ctx->keylen = keylen; + } else { + ctx->keylen = digestsize; + ret = starfive_hash_long_setkey(ctx, key, keylen, "starfive-sha384"); + } + + return ret; +} + +static int starfive_hash512_setkey(struct crypto_ahash *tfm, + const u8 *key, unsigned int keylen) +{ + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm); + unsigned int digestsize = crypto_ahash_digestsize(tfm); + unsigned int blocksize; + int ret = 0; + + blocksize = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm)); + crypto_ahash_setkey(ctx->ahash_fbk, key, keylen); + + if (keylen <= blocksize) { + memcpy(ctx->key, key, keylen); + ctx->keylen = keylen; + } else { + ctx->keylen = digestsize; + ret = starfive_hash_long_setkey(ctx, key, keylen, "starfive-sha512"); + } + + return ret; +} + +static int starfive_sm3_setkey(struct crypto_ahash *tfm, + const u8 *key, unsigned int keylen) +{ + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm); + unsigned int digestsize = crypto_ahash_digestsize(tfm); + unsigned int blocksize; + int ret = 0; + + blocksize = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm)); + crypto_ahash_setkey(ctx->ahash_fbk, key, keylen); + + if (keylen <= blocksize) { + memcpy(ctx->key, key, keylen); + ctx->keylen = keylen; + } else { + ctx->keylen = digestsize; + ret = starfive_hash_long_setkey(ctx, key, keylen, "starfive-sm3"); + } + + return ret; +} + +static int starfive_hash_cra_sha224_init(struct crypto_tfm *tfm) +{ + return starfive_hash_cra_init_algs(tfm, NULL, + STARFIVE_HASH_SHA224, + "sha224-generic"); +} + +static int starfive_hash_cra_sha256_init(struct crypto_tfm *tfm) +{ + return starfive_hash_cra_init_algs(tfm, NULL, + STARFIVE_HASH_SHA256, + "sha256-generic"); +} + +static int starfive_hash_cra_sha384_init(struct crypto_tfm *tfm) +{ + return starfive_hash_cra_init_algs(tfm, NULL, + STARFIVE_HASH_SHA384, + "sha384-generic"); +} + +static int starfive_hash_cra_sha512_init(struct crypto_tfm *tfm) +{ + return starfive_hash_cra_init_algs(tfm, NULL, + STARFIVE_HASH_SHA512, + "sha512-generic"); +} + +static int starfive_hash_cra_sm3_init(struct crypto_tfm *tfm) +{ + return starfive_hash_cra_init_algs(tfm, NULL, + STARFIVE_HASH_SM3, + "sm3-generic"); +} + +static int starfive_hash_cra_hmac_sha224_init(struct crypto_tfm *tfm) +{ + return starfive_hash_cra_init_algs(tfm, "sha224", + STARFIVE_HASH_SHA224, + "hmac(sha224-generic)"); +} + +static int starfive_hash_cra_hmac_sha256_init(struct crypto_tfm *tfm) +{ + return starfive_hash_cra_init_algs(tfm, "sha256", + STARFIVE_HASH_SHA256, + "hmac(sha256-generic)"); +} + +static int starfive_hash_cra_hmac_sha384_init(struct crypto_tfm *tfm) +{ + return starfive_hash_cra_init_algs(tfm, "sha384", + STARFIVE_HASH_SHA384, + "hmac(sha384-generic)"); +} + +static int starfive_hash_cra_hmac_sha512_init(struct crypto_tfm *tfm) +{ + return starfive_hash_cra_init_algs(tfm, "sha512", + STARFIVE_HASH_SHA512, + "hmac(sha512-generic)"); +} + +static int starfive_hash_cra_hmac_sm3_init(struct crypto_tfm *tfm) +{ + return starfive_hash_cra_init_algs(tfm, "sm3", + STARFIVE_HASH_SM3, + "hmac(sm3-generic)"); +} + +static struct ahash_alg algs_sha2_sm3[] = { +{ + .init = starfive_hash_init, + .update = starfive_hash_update, + .final = starfive_hash_final, + .finup = starfive_hash_finup, + .digest = starfive_hash_digest, + .export = starfive_hash_export, + .import = starfive_hash_import, + .halg = { + .digestsize = SHA224_DIGEST_SIZE, + .statesize = sizeof(struct sha256_state), + .base = { + .cra_name = "sha224", + .cra_driver_name = "starfive-sha224", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA224_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 3, + .cra_init = starfive_hash_cra_sha224_init, + .cra_exit = starfive_hash_cra_exit, + .cra_module = THIS_MODULE, + } + } +}, +{ + .init = starfive_hash_init, + .update = starfive_hash_update, + .final = starfive_hash_final, + .finup = starfive_hash_finup, + .digest = starfive_hash_digest, + .export = starfive_hash_export, + .import = starfive_hash_import, + .setkey = starfive_hash224_setkey, + .halg = { + .digestsize = SHA224_DIGEST_SIZE, + .statesize = sizeof(struct sha256_state), + .base = { + .cra_name = "hmac(sha224)", + .cra_driver_name = "starfive-hmac-sha224", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA224_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 3, + .cra_init = starfive_hash_cra_hmac_sha224_init, + .cra_exit = starfive_hash_cra_exit, + .cra_module = THIS_MODULE, + } + } +}, +{ + .init = starfive_hash_init, + .update = starfive_hash_update, + .final = starfive_hash_final, + .finup = starfive_hash_finup, + .digest = starfive_hash_digest, + .export = starfive_hash_export, + .import = starfive_hash_import, + .halg = { + .digestsize = SHA256_DIGEST_SIZE, + .statesize = sizeof(struct sha256_state), + .base = { + .cra_name = "sha256", + .cra_driver_name = "starfive-sha256", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 3, + .cra_init = starfive_hash_cra_sha256_init, + .cra_exit = starfive_hash_cra_exit, + .cra_module = THIS_MODULE, + } + } +}, +{ + .init = starfive_hash_init, + .update = starfive_hash_update, + .final = starfive_hash_final, + .finup = starfive_hash_finup, + .digest = starfive_hash_digest, + .export = starfive_hash_export, + .import = starfive_hash_import, + .setkey = starfive_hash256_setkey, + .halg = { + .digestsize = SHA256_DIGEST_SIZE, + .statesize = sizeof(struct sha256_state), + .base = { + .cra_name = "hmac(sha256)", + .cra_driver_name = "starfive-hmac-sha256", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 3, + .cra_init = starfive_hash_cra_hmac_sha256_init, + .cra_exit = starfive_hash_cra_exit, + .cra_module = THIS_MODULE, + } + } +}, +{ + .init = starfive_hash_init, + .update = starfive_hash_update, + .final = starfive_hash_final, + .finup = starfive_hash_finup, + .digest = starfive_hash_digest, + .export = starfive_hash_export, + .import = starfive_hash_import, + .halg = { + .digestsize = SHA384_DIGEST_SIZE, + .statesize = sizeof(struct sha512_state), + .base = { + .cra_name = "sha384", + .cra_driver_name = "starfive-sha384", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA384_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 3, + .cra_init = starfive_hash_cra_sha384_init, + .cra_exit = starfive_hash_cra_exit, + .cra_module = THIS_MODULE, + } + } +}, +{ + .init = starfive_hash_init, + .update = starfive_hash_update, + .final = starfive_hash_final, + .finup = starfive_hash_finup, + .digest = starfive_hash_digest, + .setkey = starfive_hash384_setkey, + .export = starfive_hash_export, + .import = starfive_hash_import, + .halg = { + .digestsize = SHA384_DIGEST_SIZE, + .statesize = sizeof(struct sha512_state), + .base = { + .cra_name = "hmac(sha384)", + .cra_driver_name = "starfive-hmac-sha384", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA384_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 3, + .cra_init = starfive_hash_cra_hmac_sha384_init, + .cra_exit = starfive_hash_cra_exit, + .cra_module = THIS_MODULE, + } + } +}, +{ + .init = starfive_hash_init, + .update = starfive_hash_update, + .final = starfive_hash_final, + .finup = starfive_hash_finup, + .digest = starfive_hash_digest, + .export = starfive_hash_export, + .import = starfive_hash_import, + .halg = { + .digestsize = SHA512_DIGEST_SIZE, + .statesize = sizeof(struct sha512_state), + .base = { + .cra_name = "sha512", + .cra_driver_name = "starfive-sha512", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_TYPE_AHASH, + .cra_blocksize = SHA512_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 3, + .cra_init = starfive_hash_cra_sha512_init, + .cra_exit = starfive_hash_cra_exit, + .cra_module = THIS_MODULE, + } + } +}, +{ + .init = starfive_hash_init, + .update = starfive_hash_update, + .final = starfive_hash_final, + .finup = starfive_hash_finup, + .digest = starfive_hash_digest, + .setkey = starfive_hash512_setkey, + .export = starfive_hash_export, + .import = starfive_hash_import, + .halg = { + .digestsize = SHA512_DIGEST_SIZE, + .statesize = sizeof(struct sha512_state), + .base = { + .cra_name = "hmac(sha512)", + .cra_driver_name = "starfive-hmac-sha512", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA512_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 3, + .cra_init = starfive_hash_cra_hmac_sha512_init, + .cra_exit = starfive_hash_cra_exit, + .cra_module = THIS_MODULE, + } + } +}, +{ + .init = starfive_hash_init, + .update = starfive_hash_update, + .final = starfive_hash_final, + .finup = starfive_hash_finup, + .digest = starfive_hash_digest, + .export = starfive_hash_export, + .import = starfive_hash_import, + .halg = { + .digestsize = SM3_DIGEST_SIZE, + .statesize = sizeof(struct sm3_state), + .base = { + .cra_name = "sm3", + .cra_driver_name = "starfive-sm3", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SM3_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 3, + .cra_init = starfive_hash_cra_sm3_init, + .cra_exit = starfive_hash_cra_exit, + .cra_module = THIS_MODULE, + } + } +}, +{ + .init = starfive_hash_init, + .update = starfive_hash_update, + .final = starfive_hash_final, + .finup = starfive_hash_finup, + .digest = starfive_hash_digest, + .setkey = starfive_sm3_setkey, + .export = starfive_hash_export, + .import = starfive_hash_import, + .halg = { + .digestsize = SM3_DIGEST_SIZE, + .statesize = sizeof(struct sm3_state), + .base = { + .cra_name = "hmac(sm3)", + .cra_driver_name = "starfive-hmac-sm3", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SM3_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 3, + .cra_init = starfive_hash_cra_hmac_sm3_init, + .cra_exit = starfive_hash_cra_exit, + .cra_module = THIS_MODULE, + } + } +}, +}; + +int starfive_hash_register_algs(void) +{ + int ret = 0; + + ret = crypto_register_ahashes(algs_sha2_sm3, ARRAY_SIZE(algs_sha2_sm3)); + + return ret; +} + +void starfive_hash_unregister_algs(void) +{ + crypto_unregister_ahashes(algs_sha2_sm3, ARRAY_SIZE(algs_sha2_sm3)); +} From 3970ccd3a51766fa9b085b0bb91327f67108f093 Mon Sep 17 00:00:00 2001 From: Changhuang Liang Date: Wed, 15 Mar 2023 03:04:19 -0700 Subject: [PATCH 76/95] dt-bindings: phy: Add starfive,jh7110-dphy-rx StarFive SoCs like the jh7110 use a MIPI D-PHY RX controller based on a M31 IP. Add a binding for it. Signed-off-by: Changhuang Liang --- .../bindings/phy/starfive,jh7110-dphy-rx.yaml | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/starfive,jh7110-dphy-rx.yaml diff --git a/Documentation/devicetree/bindings/phy/starfive,jh7110-dphy-rx.yaml b/Documentation/devicetree/bindings/phy/starfive,jh7110-dphy-rx.yaml new file mode 100644 index 0000000000000..b72ac44bc29d3 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/starfive,jh7110-dphy-rx.yaml @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/starfive,jh7110-dphy-rx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive SoC MIPI D-PHY Rx Controller + +maintainers: + - Jack Zhu + - Changhuang Liang + +description: + The StarFive SoC uses the MIPI CSI D-PHY based on M31 IP to transfer + CSI camera data. + +properties: + compatible: + const: starfive,jh7110-dphy-rx + + reg: + maxItems: 1 + + clocks: + items: + - description: config clock + - description: reference clock + - description: escape mode transmit clock + + clock-names: + items: + - const: cfg + - const: ref + - const: tx + + resets: + items: + - description: DPHY_HW reset + - description: DPHY_B09_ALWAYS_ON reset + + starfive,aon-syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle of AON SYSCON + - description: register offset + description: The power of dphy rx is configured by AON SYSCON + in this property. + + "#phy-cells": + const: 0 + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - starfive,aon-syscon + - "#phy-cells" + +additionalProperties: false + +examples: + - | + phy@19820000 { + compatible = "starfive,jh7110-dphy-rx"; + reg = <0x19820000 0x10000>; + clocks = <&ispcrg 3>, + <&ispcrg 4>, + <&ispcrg 5>; + clock-names = "cfg", "ref", "tx"; + resets = <&ispcrg 2>, + <&ispcrg 3>; + starfive,aon-syscon = <&aon_syscon 0x00>; + #phy-cells = <0>; + }; From a62573f4e1131f642dcbd29fc7a825de34713323 Mon Sep 17 00:00:00 2001 From: Changhuang Liang Date: Wed, 15 Mar 2023 03:04:20 -0700 Subject: [PATCH 77/95] phy: starfive: Add mipi dphy rx support Add mipi dphy rx support for the StarFive JH7110 SoC. It is used to transfer CSI camera data. Signed-off-by: Changhuang Liang --- MAINTAINERS | 7 + drivers/phy/Kconfig | 1 + drivers/phy/Makefile | 1 + drivers/phy/starfive/Kconfig | 13 + drivers/phy/starfive/Makefile | 2 + drivers/phy/starfive/phy-starfive-dphy-rx.c | 362 ++++++++++++++++++++ 6 files changed, 386 insertions(+) create mode 100644 drivers/phy/starfive/Kconfig create mode 100644 drivers/phy/starfive/Makefile create mode 100644 drivers/phy/starfive/phy-starfive-dphy-rx.c diff --git a/MAINTAINERS b/MAINTAINERS index 3cc4e0eb7d217..f734d3169fcba 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19927,6 +19927,13 @@ S: Maintained F: Documentation/devicetree/bindings/net/dwmac-starfive.c F: Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml +STARFIVE JH7110 DPHY RX DRIVER +M: Jack Zhu +M: Changhuang Liang +S: Supported +F: Documentation/devicetree/bindings/phy/starfive,jh7110-dphy-rx.yaml +F: drivers/phy/starfive/phy-starfive-dphy-rx.c + STARFIVE JH7110 MMC/SD/SDIO DRIVER M: William Qiu S: Supported diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 7bd00a11d074b..c4b2a86e2afba 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -91,6 +91,7 @@ source "drivers/phy/rockchip/Kconfig" source "drivers/phy/samsung/Kconfig" source "drivers/phy/socionext/Kconfig" source "drivers/phy/st/Kconfig" +source "drivers/phy/starfive/Kconfig" source "drivers/phy/sunplus/Kconfig" source "drivers/phy/tegra/Kconfig" source "drivers/phy/ti/Kconfig" diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 54f312c10a408..fb3dc9de61115 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -31,6 +31,7 @@ obj-y += allwinner/ \ samsung/ \ socionext/ \ st/ \ + starfive/ \ sunplus/ \ tegra/ \ ti/ \ diff --git a/drivers/phy/starfive/Kconfig b/drivers/phy/starfive/Kconfig new file mode 100644 index 0000000000000..f989b8ff8bcbe --- /dev/null +++ b/drivers/phy/starfive/Kconfig @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Phy drivers for StarFive platforms +# + +config PHY_STARFIVE_DPHY_RX + tristate "StarFive D-PHY RX Support" + select GENERIC_PHY + select GENERIC_PHY_MIPI_DPHY + help + Choose this option if you have a StarFive D-PHY in your + system. If M is selected, the module will be called + phy-starfive-dphy-rx. diff --git a/drivers/phy/starfive/Makefile b/drivers/phy/starfive/Makefile new file mode 100644 index 0000000000000..7ec576cb30aea --- /dev/null +++ b/drivers/phy/starfive/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_PHY_STARFIVE_DPHY_RX) += phy-starfive-dphy-rx.o diff --git a/drivers/phy/starfive/phy-starfive-dphy-rx.c b/drivers/phy/starfive/phy-starfive-dphy-rx.c new file mode 100644 index 0000000000000..57071ec4cca86 --- /dev/null +++ b/drivers/phy/starfive/phy-starfive-dphy-rx.c @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * DPHY driver for the StarFive JH7110 SoC + * + * Copyright (C) 2023 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STF_DPHY_APBCFGSAIF__SYSCFG(x) (x) + +#define STF_DPHY_DA_CDPHY_R100_CTRL0_2D1C_EFUSE_EN BIT(6) +#define STF_DPHY_DA_CDPHY_R100_CTRL0_2D1C_EFUSE_IN GENMASK(12, 7) +#define STF_DPHY_DA_CDPHY_R100_CTRL1_2D1C_EFUSE_EN BIT(19) +#define STF_DPHY_DA_CDPHY_R100_CTRL1_2D1C_EFUSE_IN GENMASK(25, 20) + +#define STF_DPHY_DATA_BUS16_8 BIT(8) +#define STF_DPHY_DEBUG_MODE_SEL GENMASK(15, 9) + +#define STF_DPHY_ENABLE_CLK BIT(6) +#define STF_DPHY_ENABLE_CLK1 BIT(7) +#define STF_DPHY_ENABLE_LAN0 BIT(8) +#define STF_DPHY_ENABLE_LAN1 BIT(9) +#define STF_DPHY_ENABLE_LAN2 BIT(10) +#define STF_DPHY_ENABLE_LAN3 BIT(11) +#define STF_DPHY_GPI_EN GENMASK(17, 12) +#define STF_DPHY_HS_FREQ_CHANGE_CLK BIT(18) +#define STF_DPHY_HS_FREQ_CHANGE_CLK1 BIT(19) +#define STF_DPHY_LANE_SWAP_CLK GENMASK(22, 20) +#define STF_DPHY_LANE_SWAP_CLK1 GENMASK(25, 23) +#define STF_DPHY_LANE_SWAP_LAN0 GENMASK(28, 26) +#define STF_DPHY_LANE_SWAP_LAN1 GENMASK(31, 29) + +#define STF_DPHY_LANE_SWAP_LAN2 GENMASK(2, 0) +#define STF_DPHY_LANE_SWAP_LAN3 GENMASK(5, 3) +#define STF_DPHY_MP_TEST_EN BIT(6) +#define STF_DPHY_MP_TEST_MODE_SEL GENMASK(11, 7) +#define STF_DPHY_PLL_CLK_SEL GENMASK(21, 12) +#define STF_DPHY_PRECOUNTER_IN_CLK GENMASK(29, 22) + +#define STF_DPHY_PRECOUNTER_IN_CLK1 GENMASK(7, 0) +#define STF_DPHY_PRECOUNTER_IN_LAN0 GENMASK(15, 8) +#define STF_DPHY_PRECOUNTER_IN_LAN1 GENMASK(23, 16) +#define STF_DPHY_PRECOUNTER_IN_LAN2 GENMASK(31, 24) + +#define STF_DPHY_PRECOUNTER_IN_LAN3 GENMASK(7, 0) +#define STF_DPHY_RX_1C2C_SEL BIT(8) + +struct regval_t { + u32 addr; + u32 val; +}; + +struct stf_dphy { + struct device *dev; + void __iomem *regs; + struct clk *cfg_clk; + struct clk *ref_clk; + struct clk *tx_clk; + struct reset_control *rstc; + struct regulator *mipi_0p9; + struct phy *phy; + struct regmap *stf_aon_syscon; + unsigned int aon_gp_reg; +}; + +struct stf_dphy_info { + bool external_support; + int (*external_get)(struct stf_dphy *dphy); + void (*external_init)(struct stf_dphy *dphy); + void (*external_exit)(struct stf_dphy *dphy); +}; + +static const struct stf_dphy_info *stf_dphy_info; + +static const struct regval_t stf_dphy_init_list[] = { + { STF_DPHY_APBCFGSAIF__SYSCFG(4), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(8), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(12), 0x0000fff0 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(16), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(20), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(24), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(28), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(32), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(36), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(40), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(40), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(48), 0x24000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(52), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(56), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(60), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(64), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(68), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(72), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(76), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(80), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(84), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(88), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(92), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(96), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(100), 0x02000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(104), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(108), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(112), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(116), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(120), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(124), 0x0000000c }, + { STF_DPHY_APBCFGSAIF__SYSCFG(128), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(132), 0xcc500000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(136), 0x000000cc }, + { STF_DPHY_APBCFGSAIF__SYSCFG(140), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(144), 0x00000000 }, +}; + +static int stf_dphy_configure(struct phy *phy, union phy_configure_opts *opts) +{ + struct stf_dphy *dphy = phy_get_drvdata(phy); + int map[6] = {4, 0, 1, 2, 3, 5}; + int i; + + for (i = 0; i < ARRAY_SIZE(stf_dphy_init_list); i++) + writel(stf_dphy_init_list[i].val, + dphy->regs + stf_dphy_init_list[i].addr); + + writel(FIELD_PREP(STF_DPHY_DA_CDPHY_R100_CTRL0_2D1C_EFUSE_EN, 1) | + FIELD_PREP(STF_DPHY_DA_CDPHY_R100_CTRL0_2D1C_EFUSE_IN, 0x1b) | + FIELD_PREP(STF_DPHY_DA_CDPHY_R100_CTRL1_2D1C_EFUSE_EN, 1) | + FIELD_PREP(STF_DPHY_DA_CDPHY_R100_CTRL1_2D1C_EFUSE_IN, 0x1b), + dphy->regs + STF_DPHY_APBCFGSAIF__SYSCFG(0)); + + writel(FIELD_PREP(STF_DPHY_DATA_BUS16_8, 0) | + FIELD_PREP(STF_DPHY_DEBUG_MODE_SEL, 0x5a), + dphy->regs + STF_DPHY_APBCFGSAIF__SYSCFG(184)); + + writel(FIELD_PREP(STF_DPHY_ENABLE_CLK, 1) | + FIELD_PREP(STF_DPHY_ENABLE_CLK1, 1) | + FIELD_PREP(STF_DPHY_ENABLE_LAN0, 1) | + FIELD_PREP(STF_DPHY_ENABLE_LAN1, 1) | + FIELD_PREP(STF_DPHY_ENABLE_LAN2, 1) | + FIELD_PREP(STF_DPHY_ENABLE_LAN3, 1) | + FIELD_PREP(STF_DPHY_GPI_EN, 0) | + FIELD_PREP(STF_DPHY_HS_FREQ_CHANGE_CLK, 0) | + FIELD_PREP(STF_DPHY_HS_FREQ_CHANGE_CLK1, 0) | + FIELD_PREP(STF_DPHY_LANE_SWAP_CLK, map[0]) | + FIELD_PREP(STF_DPHY_LANE_SWAP_CLK1, map[5]) | + FIELD_PREP(STF_DPHY_LANE_SWAP_LAN0, map[1]) | + FIELD_PREP(STF_DPHY_LANE_SWAP_LAN1, map[2]), + dphy->regs + STF_DPHY_APBCFGSAIF__SYSCFG(188)); + + writel(FIELD_PREP(STF_DPHY_LANE_SWAP_LAN2, map[3]) | + FIELD_PREP(STF_DPHY_LANE_SWAP_LAN3, map[4]) | + FIELD_PREP(STF_DPHY_MP_TEST_EN, 0) | + FIELD_PREP(STF_DPHY_MP_TEST_MODE_SEL, 0) | + FIELD_PREP(STF_DPHY_PLL_CLK_SEL, 0x37c) | + FIELD_PREP(STF_DPHY_PRECOUNTER_IN_CLK, 8), + dphy->regs + STF_DPHY_APBCFGSAIF__SYSCFG(192)); + + writel(FIELD_PREP(STF_DPHY_PRECOUNTER_IN_CLK1, 8) | + FIELD_PREP(STF_DPHY_PRECOUNTER_IN_LAN0, 7) | + FIELD_PREP(STF_DPHY_PRECOUNTER_IN_LAN1, 7) | + FIELD_PREP(STF_DPHY_PRECOUNTER_IN_LAN2, 7), + dphy->regs + STF_DPHY_APBCFGSAIF__SYSCFG(196)); + + writel(FIELD_PREP(STF_DPHY_PRECOUNTER_IN_LAN3, 7) | + FIELD_PREP(STF_DPHY_RX_1C2C_SEL, 0), + dphy->regs + STF_DPHY_APBCFGSAIF__SYSCFG(200)); + + return 0; +} + +static int stf_dphy_init(struct phy *phy) +{ + struct stf_dphy *dphy = phy_get_drvdata(phy); + int ret; + + ret = regulator_enable(dphy->mipi_0p9); + if (ret) + goto err_0p9; + + if (stf_dphy_info->external_support && stf_dphy_info->external_init) + stf_dphy_info->external_init(dphy); + + return 0; + +err_0p9: + return ret; +} + +static int stf_dphy_exit(struct phy *phy) +{ + struct stf_dphy *dphy = phy_get_drvdata(phy); + + if (stf_dphy_info->external_support && stf_dphy_info->external_exit) + stf_dphy_info->external_exit(dphy); + + regulator_disable(dphy->mipi_0p9); + + return 0; +} + +static int stf_dphy_power_on(struct phy *phy) +{ + struct stf_dphy *dphy = phy_get_drvdata(phy); + + clk_set_rate(dphy->cfg_clk, 99000000); + clk_set_rate(dphy->ref_clk, 49500000); + clk_set_rate(dphy->tx_clk, 19800000); + reset_control_deassert(dphy->rstc); + + return 0; +} + +static int stf_dphy_power_off(struct phy *phy) +{ + struct stf_dphy *dphy = phy_get_drvdata(phy); + + reset_control_assert(dphy->rstc); + + return 0; +} + +static const struct phy_ops stf_dphy_ops = { + .init = stf_dphy_init, + .exit = stf_dphy_exit, + .configure = stf_dphy_configure, + .power_on = stf_dphy_power_on, + .power_off = stf_dphy_power_off, +}; + +static int stf_dphy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct stf_dphy *dphy; + int ret; + + dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL); + if (!dphy) + return -ENOMEM; + stf_dphy_info = of_device_get_match_data(&pdev->dev); + dev_set_drvdata(&pdev->dev, dphy); + dphy->dev = &pdev->dev; + + dphy->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dphy->regs)) + return PTR_ERR(dphy->regs); + + dphy->cfg_clk = devm_clk_get(&pdev->dev, "cfg"); + if (IS_ERR(dphy->cfg_clk)) + return PTR_ERR(dphy->cfg_clk); + + dphy->ref_clk = devm_clk_get(&pdev->dev, "ref"); + if (IS_ERR(dphy->ref_clk)) + return PTR_ERR(dphy->ref_clk); + + dphy->tx_clk = devm_clk_get(&pdev->dev, "tx"); + if (IS_ERR(dphy->tx_clk)) + return PTR_ERR(dphy->tx_clk); + + dphy->rstc = devm_reset_control_array_get_exclusive(&pdev->dev); + if (IS_ERR(dphy->rstc)) + return PTR_ERR(dphy->rstc); + + dphy->mipi_0p9 = devm_regulator_get(&pdev->dev, "mipi_0p9"); + if (IS_ERR(dphy->mipi_0p9)) + return PTR_ERR(dphy->mipi_0p9); + + if (stf_dphy_info->external_support && stf_dphy_info->external_get) { + ret = stf_dphy_info->external_get(dphy); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to get PHY external info\n"); + return ret; + } + } + + dphy->phy = devm_phy_create(&pdev->dev, NULL, &stf_dphy_ops); + if (IS_ERR(dphy->phy)) { + dev_err(&pdev->dev, "Failed to create PHY\n"); + return PTR_ERR(dphy->phy); + } + + phy_set_drvdata(dphy->phy, dphy); + phy_provider = devm_of_phy_provider_register(&pdev->dev, + of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static int stf_external_get(struct stf_dphy *dphy) +{ + struct of_phandle_args args; + int ret; + + ret = of_parse_phandle_with_fixed_args(dphy->dev->of_node, + "starfive,aon-syscon", + 1, 0, &args); + if (ret < 0) { + dev_err(dphy->dev, "Failed to parse starfive,aon-syscon\n"); + return -EINVAL; + } + + dphy->stf_aon_syscon = syscon_node_to_regmap(args.np); + of_node_put(args.np); + if (IS_ERR(dphy->stf_aon_syscon)) + return PTR_ERR(dphy->stf_aon_syscon); + + dphy->aon_gp_reg = args.args[0]; + + return 0; +} + +static void stf_external_init(struct stf_dphy *dphy) +{ + regmap_update_bits(dphy->stf_aon_syscon, dphy->aon_gp_reg, + BIT(31), BIT(31)); +} + +static void stf_external_exit(struct stf_dphy *dphy) +{ + regmap_update_bits(dphy->stf_aon_syscon, dphy->aon_gp_reg, + BIT(31), 0); +} + +static const struct stf_dphy_info starfive_dphy_info = { + .external_support = true, + .external_get = stf_external_get, + .external_init = stf_external_init, + .external_exit = stf_external_exit, +}; + +static const struct of_device_id stf_dphy_dt_ids[] = { + { + .compatible = "starfive,jh7110-dphy-rx", + .data = &starfive_dphy_info, + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, stf_dphy_dt_ids); + +static struct platform_driver stf_dphy_driver = { + .probe = stf_dphy_probe, + .driver = { + .name = "starfive-dphy-rx", + .of_match_table = stf_dphy_dt_ids, + }, +}; +module_platform_driver(stf_dphy_driver); + +MODULE_AUTHOR("Jack Zhu "); +MODULE_AUTHOR("Changhuang Liang "); +MODULE_DESCRIPTION("StarFive DPHY RX driver"); +MODULE_LICENSE("GPL"); From d94aef162d44e62a957894f1303a5023b8cd9033 Mon Sep 17 00:00:00 2001 From: Changhuang Liang Date: Wed, 15 Mar 2023 03:04:21 -0700 Subject: [PATCH 78/95] riscv: dts: starfive: Add dphy rx node Add dphy rx node for the StarFive JH7110 SoC. It is used to transfer CSI camera data. Signed-off-by: Changhuang Liang --- arch/riscv/boot/dts/starfive/jh7110.dtsi | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi index cdf7fa9e94c14..61f4de8644512 100644 --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi @@ -838,6 +838,19 @@ power-domains = <&pwrc JH7110_PD_ISP>; }; + csi_phy: phy@19820000 { + compatible = "starfive,jh7110-dphy-rx"; + reg = <0x0 0x19820000 0x0 0x10000>; + clocks = <&ispcrg JH7110_ISPCLK_M31DPHY_CFG_IN>, + <&ispcrg JH7110_ISPCLK_M31DPHY_REF_IN>, + <&ispcrg JH7110_ISPCLK_M31DPHY_TX_ESC_LAN0>; + clock-names = "cfg", "ref", "tx"; + resets = <&ispcrg JH7110_ISPRST_M31DPHY_HW>, + <&ispcrg JH7110_ISPRST_M31DPHY_B09_AON>; + starfive,aon-syscon = <&aon_syscon 0x00>; + #phy-cells = <0>; + }; + voutcrg: clock-controller@295c0000 { compatible = "starfive,jh7110-voutcrg"; reg = <0x0 0x295c0000 0x0 0x10000>; From c9962396092c50c851103f211e693e31946f82bd Mon Sep 17 00:00:00 2001 From: Jack Zhu Date: Fri, 10 Mar 2023 20:05:48 +0800 Subject: [PATCH 79/95] media: dt-bindings: Add bindings for JH7110 Camera Subsystem Add the bindings documentation for Starfive JH7110 Camera Subsystem which is used for handing image sensor data. Signed-off-by: Jack Zhu --- .../bindings/media/starfive,jh7110-camss.yaml | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml diff --git a/Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml b/Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml new file mode 100644 index 0000000000000..3f348dd53441f --- /dev/null +++ b/Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml @@ -0,0 +1,144 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) + +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/starfive,jh7110-camss.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Starfive SoC CAMSS ISP + +maintainers: + - Jack Zhu + - Changhuang Liang + +description: + The Starfive CAMSS ISP is a Camera interface for Starfive JH7110 SoC.It + consists of a VIN controller(Video In Controller, a top-level control until) + and an ISP. + +properties: + compatible: + const: starfive,jh7110-camss + + reg: + maxItems: 2 + + reg-names: + items: + - const: syscon + - const: isp + + clocks: + maxItems: 7 + + clock-names: + items: + - const: apb_func + - const: wrapper_clk_c + - const: dvp_inv + - const: axiwr + - const: mipi_rx0_pxl + - const: ispcore_2x + - const: isp_axi + + resets: + maxItems: 6 + + reset-names: + items: + - const: wrapper_p + - const: wrapper_c + - const: axird + - const: axiwr + - const: isp_top_n + - const: isp_top_axi + + power-domains: + items: + - description: JH7110 ISP Power Domain Switch Controller. + + interrupts: + maxItems: 4 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@1: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: + Input port for receiving CSI data. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + required: + - port@1 + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + - resets + - reset-names + - power-domains + - interrupts + +additionalProperties: false + +examples: + - | + stfcamss: isp@19840000 { + compatible = "starfive,jh7110-camss"; + reg = <0x19840000 0x10000>, + <0x19870000 0x30000>; + reg-names = "syscon", "isp"; + clocks = <&ispcrg 0>, + <&ispcrg 13>, + <&ispcrg 2>, + <&ispcrg 12>, + <&ispcrg 1>, + <&syscrg 51>, + <&syscrg 52>; + clock-names = "apb_func", + "wrapper_clk_c", + "dvp_inv", + "axiwr", + "mipi_rx0_pxl", + "ispcore_2x", + "isp_axi"; + resets = <&ispcrg 0>, + <&ispcrg 1>, + <&ispcrg 10>, + <&ispcrg 11>, + <&syscrg 41>, + <&syscrg 42>; + reset-names = "wrapper_p", + "wrapper_c", + "axird", + "axiwr", + "isp_top_n", + "isp_top_axi"; + power-domains = <&pwrc 5>; + interrupts = <92>, <87>, <88>, <90>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + vin_from_csi2rx: endpoint { + remote-endpoint = <&csi2rx_to_vin>; + }; + }; + }; + }; From 618208af144107e6f90914e776b8d8fc31881aec Mon Sep 17 00:00:00 2001 From: Jack Zhu Date: Fri, 10 Mar 2023 20:05:49 +0800 Subject: [PATCH 80/95] media: dt-bindings: cadence-csi2rx: Convert to DT schema Convert DT bindings document for Cadence MIPI-CSI2 RX controller to DT schema format and add new properties. Signed-off-by: Jack Zhu --- .../devicetree/bindings/media/cdns,csi2rx.txt | 100 ----------- .../bindings/media/cdns,csi2rx.yaml | 163 ++++++++++++++++++ MAINTAINERS | 1 + 3 files changed, 164 insertions(+), 100 deletions(-) delete mode 100644 Documentation/devicetree/bindings/media/cdns,csi2rx.txt create mode 100644 Documentation/devicetree/bindings/media/cdns,csi2rx.yaml diff --git a/Documentation/devicetree/bindings/media/cdns,csi2rx.txt b/Documentation/devicetree/bindings/media/cdns,csi2rx.txt deleted file mode 100644 index 6b02a0657ad97..0000000000000 --- a/Documentation/devicetree/bindings/media/cdns,csi2rx.txt +++ /dev/null @@ -1,100 +0,0 @@ -Cadence MIPI-CSI2 RX controller -=============================== - -The Cadence MIPI-CSI2 RX controller is a CSI-2 bridge supporting up to 4 CSI -lanes in input, and 4 different pixel streams in output. - -Required properties: - - compatible: must be set to "cdns,csi2rx" and an SoC-specific compatible - - reg: base address and size of the memory mapped region - - clocks: phandles to the clocks driving the controller - - clock-names: must contain: - * sys_clk: main clock - * p_clk: register bank clock - * pixel_if[0-3]_clk: pixel stream output clock, one for each stream - implemented in hardware, between 0 and 3 - -Optional properties: - - phys: phandle to the external D-PHY, phy-names must be provided - - phy-names: must contain "dphy", if the implementation uses an - external D-PHY - -Required subnodes: - - ports: A ports node with one port child node per device input and output - port, in accordance with the video interface bindings defined in - Documentation/devicetree/bindings/media/video-interfaces.txt. The - port nodes are numbered as follows: - - Port Description - ----------------------------- - 0 CSI-2 input - 1 Stream 0 output - 2 Stream 1 output - 3 Stream 2 output - 4 Stream 3 output - - The stream output port nodes are optional if they are not - connected to anything at the hardware level or implemented - in the design.Since there is only one endpoint per port, - the endpoints are not numbered. - - -Example: - -csi2rx: csi-bridge@0d060000 { - compatible = "cdns,csi2rx"; - reg = <0x0d060000 0x1000>; - clocks = <&byteclock>, <&byteclock> - <&coreclock>, <&coreclock>, - <&coreclock>, <&coreclock>; - clock-names = "sys_clk", "p_clk", - "pixel_if0_clk", "pixel_if1_clk", - "pixel_if2_clk", "pixel_if3_clk"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - - csi2rx_in_sensor: endpoint { - remote-endpoint = <&sensor_out_csi2rx>; - clock-lanes = <0>; - data-lanes = <1 2>; - }; - }; - - port@1 { - reg = <1>; - - csi2rx_out_grabber0: endpoint { - remote-endpoint = <&grabber0_in_csi2rx>; - }; - }; - - port@2 { - reg = <2>; - - csi2rx_out_grabber1: endpoint { - remote-endpoint = <&grabber1_in_csi2rx>; - }; - }; - - port@3 { - reg = <3>; - - csi2rx_out_grabber2: endpoint { - remote-endpoint = <&grabber2_in_csi2rx>; - }; - }; - - port@4 { - reg = <4>; - - csi2rx_out_grabber3: endpoint { - remote-endpoint = <&grabber3_in_csi2rx>; - }; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/media/cdns,csi2rx.yaml b/Documentation/devicetree/bindings/media/cdns,csi2rx.yaml new file mode 100644 index 0000000000000..ed573a67f93e7 --- /dev/null +++ b/Documentation/devicetree/bindings/media/cdns,csi2rx.yaml @@ -0,0 +1,163 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/cdns,csi2rx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cadence MIPI-CSI2 RX controller + +maintainers: + - Maxime Ripard + +description: + The Cadence MIPI-CSI2 RX controller is a CSI-2 bridge supporting up to 4 CSI + lanes in input, and 4 different pixel streams in output. + +properties: + compatible: + enum: + - cdns,csi2rx + + reg: + maxItems: 1 + + clocks: + items: + - description: CSI2Rx system clock + - description: Gated Register bank clock for APB interface + - description: pixel Clock for Stream interface 0 + - description: pixel Clock for Stream interface 1 + - description: pixel Clock for Stream interface 2 + - description: pixel Clock for Stream interface 3 + + clock-names: + items: + - const: sys + - const: reg_bank + - const: pixel_if0 + - const: pixel_if1 + - const: pixel_if2 + - const: pixel_if3 + + resets: + items: + - description: CSI2Rx system reset + - description: Gated Register bank reset for APB interface + - description: pixel reset for Stream interface 0 + - description: pixel reset for Stream interface 1 + - description: pixel reset for Stream interface 2 + - description: pixel reset for Stream interface 3 + + reset-names: + items: + - const: sys + - const: reg_bank + - const: pixel_if0 + - const: pixel_if1 + - const: pixel_if2 + - const: pixel_if3 + + phys: + maxItems: 1 + description: MIPI D-PHY + + phy-names: + items: + - const: dphy + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: + Input port node, single endpoint describing the CSI-2 transmitter. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + properties: + bus-type: + enum: + - 4 + + clock-lanes: + maximum: 4 + + data-lanes: + minItems: 1 + maxItems: 4 + items: + maximum: 4 + + required: + - clock-lanes + - data-lanes + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: + Output port node + + required: + - port@0 + - port@1 + +required: + - compatible + - reg + - clocks + - clock-names + - ports + +additionalProperties: false + +examples: + - | + csi2rx: csi@0d060000 { + compatible = "cdns,csi2rx"; + reg = <0x0d060000 0x1000>; + clocks = <&byteclock 7>, <&byteclock 6>, + <&coreclock 8>, <&coreclock 9>, + <&coreclock 10>, <&coreclock 11>; + clock-names = "sys", "reg_bank", + "pixel_if0", "pixel_if1", + "pixel_if2", "pixel_if3"; + resets = <&bytereset 9>, <&bytereset 4>, + <&corereset 5>, <&corereset 6>, + <&corereset 7>, <&corereset 8>; + reset-names = "sys", "reg_bank", + "pixel_if0", "pixel_if1", + "pixel_if2", "pixel_if3"; + phys = <&csi_phy>; + phy-names = "dphy"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + csi2rx_in_sensor: endpoint { + remote-endpoint = <&sensor_out_csi2rx>; + clock-lanes = <0>; + data-lanes = <1 2>; + }; + }; + + port@1 { + reg = <1>; + + csi2rx_out_grabber0: endpoint { + remote-endpoint = <&grabber0_in_csi2rx>; + }; + }; + }; + }; + +... diff --git a/MAINTAINERS b/MAINTAINERS index f734d3169fcba..8378c9c062903 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4495,6 +4495,7 @@ M: Maxime Ripard L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/cdns,*.txt +F: Documentation/devicetree/bindings/media/cdns,csi2rx.yaml F: drivers/media/platform/cadence/cdns-csi2* CADENCE NAND DRIVER From aedbdd65d9ffc5ded9029b9557dca94a1cdf6770 Mon Sep 17 00:00:00 2001 From: Jack Zhu Date: Fri, 10 Mar 2023 20:05:50 +0800 Subject: [PATCH 81/95] media: admin-guide: Add starfive_camss.rst for Starfive Camera Subsystem Add the file 'starfive_camss.rst' that documents the Starfive Camera Subsystem driver which is used for handing image sensor data. Signed-off-by: Jack Zhu --- .../admin-guide/media/starfive_camss.rst | 68 +++++++++++++++++++ .../media/starfive_camss_graph.dot | 28 ++++++++ .../admin-guide/media/v4l-drivers.rst | 1 + 3 files changed, 97 insertions(+) create mode 100644 Documentation/admin-guide/media/starfive_camss.rst create mode 100644 Documentation/admin-guide/media/starfive_camss_graph.dot diff --git a/Documentation/admin-guide/media/starfive_camss.rst b/Documentation/admin-guide/media/starfive_camss.rst new file mode 100644 index 0000000000000..12c5ebf794b8a --- /dev/null +++ b/Documentation/admin-guide/media/starfive_camss.rst @@ -0,0 +1,68 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: + +================================ +Starfive Camera Subsystem driver +================================ + +Introduction +------------ + +This file documents the driver for the Starfive Camera Subsystem found on +Starfive JH7110 SoC. The driver is located under drivers/media/platform/starfive. + +The driver implements V4L2, Media controller and v4l2_subdev interfaces. +Camera sensor using V4L2 subdev interface in the kernel is supported. + +The driver has been successfully used on the Gstreamer 1.18.5 with +v4l2src plugin. + + +Starfive Camera Subsystem hardware +---------------------------------- + +The Starfive Camera Subsystem hardware consists of: + +- MIPI DPHY Receiver: receives mipi data from a MIPI camera sensor. +- MIPI CSIRx Controller: is responsible for handling and decoding CSI2 protocol + based camera sensor data stream. +- ISP: handles the image data streams from the MIPI CSIRx Controller. +- VIN(Video In): a top-level module, is responsible for controlling power + and clocks to other modules, dumps the input data to memory or transfers the + input data to ISP. + + +Topology +-------- + +The media controller pipeline graph is as follows: + +.. _starfive_camss_graph: + +.. kernel-figure:: starfive_camss_graph.dot + :alt: starfive_camss_graph.dot + :align: center + +The driver has 5 video devices: + +- stf_vin0_wr_video0: capture device for images directly from the VIN module. +- stf_vin0_isp0_video1: capture device for images without scaling. +- stf_vin0_isp0_ss0_video2: capture device for images with adjustable + scale-down factor. +- stf_vin0_isp0_ss1_video3: capture device for images with adjustable + scale-down factor. +- stf_vin0_isp0_raw_video4: capture device for RAW images. + +The driver has 6 subdevices: + +- stf_isp0: is responsible for all the isp operations. +- stf_vin0_wr: used to dump RAW images to memory. +- stf_vin0_isp0: used to capture unscaled images for the stf_vin0_isp0_video1 + device. +- stf_vin0_isp0_ss0: used to resize and downsample frames for + the stf_vin0_isp0_ss0_video2 capture device. +- stf_vin0_isp0_ss1: used to resize and downsample frames for + the stf_vin0_isp0_ss1_video3 capture device. +- stf_vin0_isp0_raw: used to configure the camss ISP to get RAW + images for stf_vin0_isp0_raw_video4 capture device. diff --git a/Documentation/admin-guide/media/starfive_camss_graph.dot b/Documentation/admin-guide/media/starfive_camss_graph.dot new file mode 100644 index 0000000000000..c35dec8de2542 --- /dev/null +++ b/Documentation/admin-guide/media/starfive_camss_graph.dot @@ -0,0 +1,28 @@ +digraph board { + rankdir=TB + n00000001 [label="{{ 0} | stf_isp0\n/dev/v4l-subdev0 | { 1 | 2 | 3 | 4}}", shape=Mrecord, style=filled, fillcolor=green] + n00000001:port1 -> n00000010:port0 + n00000001:port2 -> n00000019:port0 [style=dashed] + n00000001:port3 -> n00000022:port0 [style=dashed] + n00000001:port4 -> n0000002b:port0 [style=dashed] + n00000007 [label="{{ 0} | stf_vin0_wr\n/dev/v4l-subdev1 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000007:port1 -> n0000000a [style=bold] + n0000000a [label="stf_vin0_wr_video0\n/dev/video0", shape=box, style=filled, fillcolor=yellow] + n00000010 [label="{{ 0} | stf_vin0_isp0\n/dev/v4l-subdev2 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000010:port1 -> n00000013 [style=bold] + n00000013 [label="stf_vin0_isp0_video1\n/dev/video1", shape=box, style=filled, fillcolor=yellow] + n00000019 [label="{{ 0} | stf_vin0_isp0_ss0\n/dev/v4l-subdev3 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000019:port1 -> n0000001c [style=bold] + n0000001c [label="stf_vin0_isp0_ss0_video2\n/dev/video2", shape=box, style=filled, fillcolor=yellow] + n00000022 [label="{{ 0} | stf_vin0_isp0_ss1\n/dev/v4l-subdev4 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000022:port1 -> n00000025 [style=bold] + n00000025 [label="stf_vin0_isp0_ss1_video3\n/dev/video3", shape=box, style=filled, fillcolor=yellow] + n0000002b [label="{{ 0} | stf_vin0_isp0_raw\n/dev/v4l-subdev5 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n0000002b:port1 -> n0000002e [style=bold] + n0000002e [label="stf_vin0_isp0_raw_video4\n/dev/video4", shape=box, style=filled, fillcolor=yellow] + n0000003c [label="{{ 0} | cdns_csi2rx.19800000.csi-bridge\n/dev/v4l-subdev6 | { 1 | 2 | 3 | 4}}", shape=Mrecord, style=filled, fillcolor=green] + n0000003c:port1 -> n00000007:port0 [style=dashed] + n0000003c:port1 -> n00000001:port0 + n00000054 [label="{{} | imx219 6-0010\n/dev/v4l-subdev7 | { 0}}", shape=Mrecord, style=filled, fillcolor=green] + n00000054:port0 -> n0000003c:port0 [style=bold] +} diff --git a/Documentation/admin-guide/media/v4l-drivers.rst b/Documentation/admin-guide/media/v4l-drivers.rst index 1c41f87c39171..3005d637199a6 100644 --- a/Documentation/admin-guide/media/v4l-drivers.rst +++ b/Documentation/admin-guide/media/v4l-drivers.rst @@ -27,6 +27,7 @@ Video4Linux (V4L) driver-specific documentation si470x si4713 si476x + starfive vimc visl vivid From 8bfe75887d8dad4fe73271f7d36a5e578d550605 Mon Sep 17 00:00:00 2001 From: Jack Zhu Date: Fri, 10 Mar 2023 20:05:51 +0800 Subject: [PATCH 82/95] media: cadence: Add support for external dphy and JH7110 SoC Add support for external MIPI D-PHY and Starfive JH7110 SoC which has the cadence csi2 receiver. Signed-off-by: Jack Zhu --- drivers/media/platform/cadence/cdns-csi2rx.c | 273 ++++++++++++++++++- 1 file changed, 263 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index cc3ebb0d96f66..7e7b096869fcf 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -10,9 +10,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -30,6 +32,12 @@ #define CSI2RX_STATIC_CFG_DLANE_MAP(llane, plane) ((plane) << (16 + (llane) * 4)) #define CSI2RX_STATIC_CFG_LANES_MASK GENMASK(11, 8) +#define CSI2RX_DPHY_LANE_CTRL_REG 0x40 +#define CSI2RX_DPHY_CL_RST BIT(16) +#define CSI2RX_DPHY_DL_RST(i) BIT((i) + 12) +#define CSI2RX_DPHY_CL_EN BIT(4) +#define CSI2RX_DPHY_DL_EN(i) BIT(i) + #define CSI2RX_STREAM_BASE(n) (((n) + 1) * 0x100) #define CSI2RX_STREAM_CTRL_REG(n) (CSI2RX_STREAM_BASE(n) + 0x000) @@ -37,6 +45,7 @@ #define CSI2RX_STREAM_DATA_CFG_REG(n) (CSI2RX_STREAM_BASE(n) + 0x008) #define CSI2RX_STREAM_DATA_CFG_EN_VC_SELECT BIT(31) +#define CSI2RX_STREAM_DATA_CFG_EN_DATA_TYPE_0 BIT(7) #define CSI2RX_STREAM_DATA_CFG_VC_SELECT(n) BIT((n) + 16) #define CSI2RX_STREAM_CFG_REG(n) (CSI2RX_STREAM_BASE(n) + 0x00c) @@ -54,8 +63,19 @@ enum csi2rx_pads { CSI2RX_PAD_MAX, }; +struct csi2rx_fmt { + u32 code; + u8 bpp; + u32 dt; +}; + +struct csi2rx_platform_info { + unsigned long sys_clk_rate; +}; + struct csi2rx_priv { struct device *dev; + unsigned int power_count; unsigned int count; /* @@ -68,6 +88,9 @@ struct csi2rx_priv { struct clk *sys_clk; struct clk *p_clk; struct clk *pixel_clk[CSI2RX_STREAMS_MAX]; + struct reset_control *sys_rst; + struct reset_control *p_rst; + struct reset_control *pixel_rst[CSI2RX_STREAMS_MAX]; struct phy *dphy; u8 lanes[CSI2RX_LANES_MAX]; @@ -83,14 +106,100 @@ struct csi2rx_priv { /* Remote source */ struct v4l2_subdev *source_subdev; int source_pad; + + const struct csi2rx_platform_info *platform_info; +}; + +static const struct csi2rx_fmt formats[] = { + { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .bpp = 10, + .dt = 0x2b, + }, + { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .bpp = 10, + .dt = 0x2b, + }, + { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .bpp = 10, + .dt = 0x2b, + }, + { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .bpp = 10, + .dt = 0x2b, + }, }; +static u8 csi2rx_get_bpp(u32 code) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].code == code) + return formats[i].bpp; + } + + return 0; +} + +static u32 csi2rx_get_dt(u32 code) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].code == code) + return formats[i].dt; + } + + return 0; +} + +static s64 csi2rx_get_pixel_rate(struct csi2rx_priv *csi2rx) +{ + struct v4l2_ctrl *ctrl; + + ctrl = v4l2_ctrl_find(csi2rx->source_subdev->ctrl_handler, + V4L2_CID_PIXEL_RATE); + if (!ctrl) { + dev_err(csi2rx->dev, "no pixel rate control in subdev: %s\n", + csi2rx->source_subdev->name); + return -EINVAL; + } + + return v4l2_ctrl_g_ctrl_int64(ctrl); +} + static inline struct csi2rx_priv *v4l2_subdev_to_csi2rx(struct v4l2_subdev *subdev) { return container_of(subdev, struct csi2rx_priv, subdev); } +static int csi2rx_s_power(struct v4l2_subdev *subdev, int on) +{ + struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); + + mutex_lock(&csi2rx->lock); + + if (on) { + if (!csi2rx->power_count) + phy_init(csi2rx->dphy); + + csi2rx->power_count++; + } else { + csi2rx->power_count--; + + if (!csi2rx->power_count) + phy_exit(csi2rx->dphy); + } + + mutex_unlock(&csi2rx->lock); + return 0; +} + static void csi2rx_reset(struct csi2rx_priv *csi2rx) { writel(CSI2RX_SOFT_RESET_PROTOCOL | CSI2RX_SOFT_RESET_FRONT, @@ -101,17 +210,70 @@ static void csi2rx_reset(struct csi2rx_priv *csi2rx) writel(0, csi2rx->base + CSI2RX_SOFT_RESET_REG); } +static int csi2rx_configure_ext_dphy(struct csi2rx_priv *csi2rx) +{ + union phy_configure_opts opts = { }; + struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy; + struct v4l2_subdev_format sd_fmt; + s64 pixel_rate; + int ret; + u8 bpp; + + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sd_fmt.pad = 0; + + ret = v4l2_subdev_call(csi2rx->source_subdev, pad, get_fmt, NULL, + &sd_fmt); + if (ret) + return ret; + + bpp = csi2rx_get_bpp(sd_fmt.format.code); + if (!bpp) + return -EINVAL; + + pixel_rate = csi2rx_get_pixel_rate(csi2rx); + if (pixel_rate < 0) + return pixel_rate; + + ret = phy_mipi_dphy_get_default_config(pixel_rate, bpp, + csi2rx->num_lanes, cfg); + if (ret) + return ret; + + phy_pm_runtime_get_sync(csi2rx->dphy); + + ret = phy_power_on(csi2rx->dphy); + if (ret) + goto out; + + ret = phy_configure(csi2rx->dphy, &opts); + if (ret) { + /* Can't do anything if it fails. Ignore the return value. */ + phy_power_off(csi2rx->dphy); + goto out; + } + +out: + phy_pm_runtime_put_sync(csi2rx->dphy); + + return ret; +} + static int csi2rx_start(struct csi2rx_priv *csi2rx) { + struct v4l2_subdev_format sd_fmt; unsigned int i; unsigned long lanes_used = 0; u32 reg; + u32 dt = 0; int ret; ret = clk_prepare_enable(csi2rx->p_clk); if (ret) return ret; + reset_control_deassert(csi2rx->p_rst); + csi2rx_reset(csi2rx); reg = csi2rx->num_lanes << 8; @@ -139,6 +301,29 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) if (ret) goto err_disable_pclk; + /* Enable DPHY clk and data lanes. */ + if (csi2rx->dphy) { + reg = CSI2RX_DPHY_CL_EN | CSI2RX_DPHY_CL_RST; + for (i = 0; i < csi2rx->num_lanes; i++) { + reg |= CSI2RX_DPHY_DL_EN(csi2rx->lanes[i] - 1); + reg |= CSI2RX_DPHY_DL_RST(csi2rx->lanes[i] - 1); + } + + writel(reg, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); + } + + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sd_fmt.pad = 0; + + ret = v4l2_subdev_call(csi2rx->source_subdev, pad, get_fmt, NULL, + &sd_fmt); + if (ret) + dev_warn(csi2rx->dev, "Couldn't get format\n"); + + dt = csi2rx_get_dt(sd_fmt.format.code); + if (!dt) + dev_warn(csi2rx->dev, "Couldn't get dt\n"); + /* * Create a static mapping between the CSI virtual channels * and the output stream. @@ -154,6 +339,8 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) if (ret) goto err_disable_pixclk; + reset_control_deassert(csi2rx->pixel_rst[i]); + writel(CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF, csi2rx->base + CSI2RX_STREAM_CFG_REG(i)); @@ -161,6 +348,11 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) CSI2RX_STREAM_DATA_CFG_VC_SELECT(i), csi2rx->base + CSI2RX_STREAM_DATA_CFG_REG(i)); + if (dt) + writel(readl(csi2rx->base + CSI2RX_STREAM_DATA_CFG_REG(i)) | + CSI2RX_STREAM_DATA_CFG_EN_DATA_TYPE_0 | dt, + csi2rx->base + CSI2RX_STREAM_DATA_CFG_REG(i)); + writel(CSI2RX_STREAM_CTRL_START, csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); } @@ -169,10 +361,27 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) if (ret) goto err_disable_pixclk; + if (csi2rx->platform_info && csi2rx->platform_info->sys_clk_rate > 0) + clk_set_rate(csi2rx->sys_clk, + csi2rx->platform_info->sys_clk_rate); + + reset_control_deassert(csi2rx->sys_rst); + + if (csi2rx->dphy) { + ret = csi2rx_configure_ext_dphy(csi2rx); + if (ret) { + dev_err(csi2rx->dev, + "Failed to configure external DPHY: %d\n", ret); + goto err_disable_sysclk; + } + } + clk_disable_unprepare(csi2rx->p_clk); return 0; +err_disable_sysclk: + clk_disable_unprepare(csi2rx->sys_clk); err_disable_pixclk: for (; i > 0; i--) clk_disable_unprepare(csi2rx->pixel_clk[i - 1]); @@ -188,18 +397,28 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx) unsigned int i; clk_prepare_enable(csi2rx->p_clk); + reset_control_assert(csi2rx->sys_rst); clk_disable_unprepare(csi2rx->sys_clk); for (i = 0; i < csi2rx->max_streams; i++) { writel(0, csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); + reset_control_assert(csi2rx->pixel_rst[i]); clk_disable_unprepare(csi2rx->pixel_clk[i]); } + reset_control_assert(csi2rx->p_rst); clk_disable_unprepare(csi2rx->p_clk); if (v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, false)) dev_warn(csi2rx->dev, "Couldn't disable our subdev\n"); + + if (csi2rx->dphy) { + writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); + + if (phy_power_off(csi2rx->dphy)) + dev_warn(csi2rx->dev, "Couldn't power off DPHY\n"); + } } static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable) @@ -236,11 +455,16 @@ static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable) return ret; } +static const struct v4l2_subdev_core_ops csi2rx_core_ops = { + .s_power = csi2rx_s_power, +}; + static const struct v4l2_subdev_video_ops csi2rx_video_ops = { .s_stream = csi2rx_s_stream, }; static const struct v4l2_subdev_ops csi2rx_subdev_ops = { + .core = &csi2rx_core_ops, .video = &csi2rx_video_ops, }; @@ -250,6 +474,8 @@ static int csi2rx_async_bound(struct v4l2_async_notifier *notifier, { struct v4l2_subdev *subdev = notifier->sd; struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); + struct v4l2_device *v4l2_dev; + int ret; csi2rx->source_pad = media_entity_get_fwnode_pad(&s_subdev->entity, s_subdev->fwnode, @@ -265,6 +491,15 @@ static int csi2rx_async_bound(struct v4l2_async_notifier *notifier, dev_dbg(csi2rx->dev, "Bound %s pad: %d\n", s_subdev->name, csi2rx->source_pad); + /* ensure source subdev register subdev node */ + v4l2_dev = notifier->v4l2_dev ? notifier->v4l2_dev : + notifier->parent->v4l2_dev; + if (v4l2_dev) { + ret = v4l2_device_register_subdev_nodes(v4l2_dev); + if (ret < 0) + return ret; + } + return media_create_pad_link(&csi2rx->source_subdev->entity, csi2rx->source_pad, &csi2rx->subdev.entity, 0, @@ -299,21 +534,23 @@ static int csi2rx_get_resources(struct csi2rx_priv *csi2rx, return PTR_ERR(csi2rx->p_clk); } + csi2rx->sys_rst = + devm_reset_control_get_optional_exclusive(&pdev->dev, + "sys_rst"); + if (IS_ERR(csi2rx->sys_rst)) + return PTR_ERR(csi2rx->sys_rst); + + csi2rx->p_rst = + devm_reset_control_get_optional_exclusive(&pdev->dev, "p_rst"); + if (IS_ERR(csi2rx->p_rst)) + return PTR_ERR(csi2rx->p_rst); + csi2rx->dphy = devm_phy_optional_get(&pdev->dev, "dphy"); if (IS_ERR(csi2rx->dphy)) { dev_err(&pdev->dev, "Couldn't get external D-PHY\n"); return PTR_ERR(csi2rx->dphy); } - /* - * FIXME: Once we'll have external D-PHY support, the check - * will need to be removed. - */ - if (csi2rx->dphy) { - dev_err(&pdev->dev, "External D-PHY not supported yet\n"); - return -EINVAL; - } - ret = clk_prepare_enable(csi2rx->p_clk); if (ret) { dev_err(&pdev->dev, "Couldn't prepare and enable P clock\n"); @@ -343,13 +580,14 @@ static int csi2rx_get_resources(struct csi2rx_priv *csi2rx, * FIXME: Once we'll have internal D-PHY support, the check * will need to be removed. */ - if (csi2rx->has_internal_dphy) { + if (!csi2rx->dphy && csi2rx->has_internal_dphy) { dev_err(&pdev->dev, "Internal D-PHY not supported yet\n"); return -EINVAL; } for (i = 0; i < csi2rx->max_streams; i++) { char clk_name[16]; + char rst_name[16]; snprintf(clk_name, sizeof(clk_name), "pixel_if%u_clk", i); csi2rx->pixel_clk[i] = devm_clk_get(&pdev->dev, clk_name); @@ -357,6 +595,13 @@ static int csi2rx_get_resources(struct csi2rx_priv *csi2rx, dev_err(&pdev->dev, "Couldn't get clock %s\n", clk_name); return PTR_ERR(csi2rx->pixel_clk[i]); } + + snprintf(rst_name, sizeof(rst_name), "pixel_if%u_rst", i); + csi2rx->pixel_rst[i] = + devm_reset_control_get_optional_exclusive(&pdev->dev, + rst_name); + if (IS_ERR(csi2rx->pixel_rst[i])) + return PTR_ERR(csi2rx->pixel_rst[i]); } return 0; @@ -425,6 +670,7 @@ static int csi2rx_probe(struct platform_device *pdev) csi2rx = kzalloc(sizeof(*csi2rx), GFP_KERNEL); if (!csi2rx) return -ENOMEM; + csi2rx->platform_info = of_device_get_match_data(&pdev->dev); platform_set_drvdata(pdev, csi2rx); csi2rx->dev = &pdev->dev; mutex_init(&csi2rx->lock); @@ -441,6 +687,7 @@ static int csi2rx_probe(struct platform_device *pdev) csi2rx->subdev.dev = &pdev->dev; v4l2_subdev_init(&csi2rx->subdev, &csi2rx_subdev_ops); v4l2_set_subdevdata(&csi2rx->subdev, &pdev->dev); + csi2rx->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(csi2rx->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.%s", KBUILD_MODNAME, dev_name(&pdev->dev)); @@ -462,6 +709,7 @@ static int csi2rx_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Probed CSI2RX with %u/%u lanes, %u streams, %s D-PHY\n", csi2rx->num_lanes, csi2rx->max_lanes, csi2rx->max_streams, + csi2rx->dphy ? "external" : csi2rx->has_internal_dphy ? "internal" : "no"); return 0; @@ -483,8 +731,13 @@ static int csi2rx_remove(struct platform_device *pdev) return 0; } +static const struct csi2rx_platform_info stf_jh7110_info = { + .sys_clk_rate = 297000000, +}; + static const struct of_device_id csi2rx_of_table[] = { { .compatible = "cdns,csi2rx" }, + { .compatible = "starfive,jh7110-csi2rx", .data = &stf_jh7110_info }, { }, }; MODULE_DEVICE_TABLE(of, csi2rx_of_table); From 106091ce2a88f0bd377c00c30dadf8288d20ca05 Mon Sep 17 00:00:00 2001 From: Jack Zhu Date: Thu, 9 Mar 2023 18:48:44 +0800 Subject: [PATCH 83/95] MAINTAINERS: Add Starfive Camera Subsystem driver Add an entry for Starfive Camera Subsystem driver. Signed-off-by: Jack Zhu --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 8378c9c062903..d7d23446c2c81 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19909,6 +19909,15 @@ M: Ion Badulescu S: Odd Fixes F: drivers/net/ethernet/adaptec/starfire* +STARFIVE CAMERA SUBSYSTEM DRIVER +M: Jack Zhu +M: Changhuang Liang +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/admin-guide/media/starfive_camss.rst +F: Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml +F: drivers/media/platform/starfive/ + STARFIVE CRYPTO DRIVER M: Jia Jie Ho M: William Qiu From 5cf8a5f02c759326d376c32168dde90f010878b5 Mon Sep 17 00:00:00 2001 From: Jack Zhu Date: Fri, 10 Mar 2023 20:05:53 +0800 Subject: [PATCH 84/95] media: starfive: Add Starfive Camera Subsystem driver Add the driver for Starfive Camera Subsystem found on Starfive JH7110 SoC. It is used for handing image sensor data. Signed-off-by: Jack Zhu --- drivers/media/platform/Kconfig | 1 + drivers/media/platform/Makefile | 1 + drivers/media/platform/starfive/Kconfig | 18 + drivers/media/platform/starfive/Makefile | 14 + drivers/media/platform/starfive/stf_camss.c | 728 +++++++++ drivers/media/platform/starfive/stf_camss.h | 104 ++ drivers/media/platform/starfive/stf_common.h | 149 ++ drivers/media/platform/starfive/stf_isp.c | 1079 ++++++++++++++ drivers/media/platform/starfive/stf_isp.h | 183 +++ .../media/platform/starfive/stf_isp_hw_ops.c | 1286 ++++++++++++++++ drivers/media/platform/starfive/stf_video.c | 1286 ++++++++++++++++ drivers/media/platform/starfive/stf_video.h | 89 ++ drivers/media/platform/starfive/stf_vin.c | 1314 +++++++++++++++++ drivers/media/platform/starfive/stf_vin.h | 194 +++ .../media/platform/starfive/stf_vin_hw_ops.c | 357 +++++ include/uapi/linux/stf_isp_ioctl.h | 127 ++ 16 files changed, 6930 insertions(+) create mode 100644 drivers/media/platform/starfive/Kconfig create mode 100644 drivers/media/platform/starfive/Makefile create mode 100644 drivers/media/platform/starfive/stf_camss.c create mode 100644 drivers/media/platform/starfive/stf_camss.h create mode 100644 drivers/media/platform/starfive/stf_common.h create mode 100644 drivers/media/platform/starfive/stf_isp.c create mode 100644 drivers/media/platform/starfive/stf_isp.h create mode 100644 drivers/media/platform/starfive/stf_isp_hw_ops.c create mode 100644 drivers/media/platform/starfive/stf_video.c create mode 100644 drivers/media/platform/starfive/stf_video.h create mode 100644 drivers/media/platform/starfive/stf_vin.c create mode 100644 drivers/media/platform/starfive/stf_vin.h create mode 100644 drivers/media/platform/starfive/stf_vin_hw_ops.c create mode 100644 include/uapi/linux/stf_isp_ioctl.h diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index ee579916f8744..627eaa0ab3eef 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -80,6 +80,7 @@ source "drivers/media/platform/renesas/Kconfig" source "drivers/media/platform/rockchip/Kconfig" source "drivers/media/platform/samsung/Kconfig" source "drivers/media/platform/st/Kconfig" +source "drivers/media/platform/starfive/Kconfig" source "drivers/media/platform/sunxi/Kconfig" source "drivers/media/platform/ti/Kconfig" source "drivers/media/platform/verisilicon/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 5453bb868e679..5a038498a370d 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -23,6 +23,7 @@ obj-y += renesas/ obj-y += rockchip/ obj-y += samsung/ obj-y += st/ +obj-y += starfive/ obj-y += sunxi/ obj-y += ti/ obj-y += verisilicon/ diff --git a/drivers/media/platform/starfive/Kconfig b/drivers/media/platform/starfive/Kconfig new file mode 100644 index 0000000000000..4bb1e25b66ca6 --- /dev/null +++ b/drivers/media/platform/starfive/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "Starfive media platform drivers" + +config VIDEO_STARFIVE_CAMSS + tristate "Starfive Camera Subsystem driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && OF + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_DMA_CONTIG + select V4L2_FWNODE + help + Enable this to support for the Starfive Camera subsystem + found on Starfive JH7110 SoC. + + To compile this driver as a module, choose M here: the + module will be called stf-camss. diff --git a/drivers/media/platform/starfive/Makefile b/drivers/media/platform/starfive/Makefile new file mode 100644 index 0000000000000..bf497dd24bbbc --- /dev/null +++ b/drivers/media/platform/starfive/Makefile @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for StarFive camera subsystem driver. +# + +starfive-camss-objs += \ + stf_camss.o \ + stf_isp.o \ + stf_isp_hw_ops.o \ + stf_video.o \ + stf_vin.o \ + stf_vin_hw_ops.o + +obj-$(CONFIG_VIDEO_STARFIVE_CAMSS) += starfive-camss.o \ diff --git a/drivers/media/platform/starfive/stf_camss.c b/drivers/media/platform/starfive/stf_camss.c new file mode 100644 index 0000000000000..525f2d80c5eb7 --- /dev/null +++ b/drivers/media/platform/starfive/stf_camss.c @@ -0,0 +1,728 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * stf_camss.c + * + * Starfive Camera Subsystem driver + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "stf_camss.h" + +static struct clk_bulk_data stfcamss_clocks[] = { + { .id = "clk_apb_func" }, + { .id = "clk_wrapper_clk_c" }, + { .id = "clk_dvp_inv" }, + { .id = "clk_axiwr" }, + { .id = "clk_mipi_rx0_pxl" }, + { .id = "clk_ispcore_2x" }, + { .id = "clk_isp_axi" }, +}; + +static struct reset_control_bulk_data stfcamss_resets[] = { + { .id = "rst_wrapper_p" }, + { .id = "rst_wrapper_c" }, + { .id = "rst_axird" }, + { .id = "rst_axiwr" }, + { .id = "rst_isp_top_n" }, + { .id = "rst_isp_top_axi" }, +}; + +int stfcamss_get_mem_res(struct platform_device *pdev, + struct stfcamss *stfcamss) +{ + stfcamss->syscon_base = + devm_platform_ioremap_resource_byname(pdev, "syscon"); + if (IS_ERR(stfcamss->syscon_base)) + return PTR_ERR(stfcamss->syscon_base); + + stfcamss->isp_base = + devm_platform_ioremap_resource_byname(pdev, "isp"); + if (IS_ERR(stfcamss->isp_base)) + return PTR_ERR(stfcamss->isp_base); + + return 0; +} + +/* + * stfcamss_find_sensor - Find a linked media entity which represents a sensor + * @entity: Media entity to start searching from + * + * Return a pointer to sensor media entity or NULL if not found + */ +struct media_entity *stfcamss_find_sensor(struct media_entity *entity) +{ + struct media_pad *pad; + + while (1) { + if (!entity->pads) + return NULL; + + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + return NULL; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + return NULL; + + entity = pad->entity; + + if (entity->function == MEDIA_ENT_F_CAM_SENSOR) + return entity; + } +} + +/* + * stfcamss_of_parse_endpoint_node - Parse port endpoint node + * @dev: Device + * @node: Device node to be parsed + * @csd: Parsed data from port endpoint node + * + * Return 0 on success or a negative error code on failure + */ +static int stfcamss_of_parse_endpoint_node(struct device *dev, + struct device_node *node, + struct stfcamss_async_subdev *csd) +{ + struct v4l2_fwnode_endpoint vep = { { 0 } }; + + v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep); + st_debug(ST_CAMSS, "%s: vep.base.port = 0x%x, id = 0x%x\n", + __func__, vep.base.port, vep.base.id); + + csd->port = vep.base.port; + switch (csd->port) { + case PORT_NUMBER_DVP_SENSOR: + break; + case PORT_NUMBER_CSI2RX: + break; + default: + break; + }; + + return 0; +} + +/* + * stfcamss_of_parse_ports - Parse ports node + * @stfcamss: STFCAMSS device + * + * Return number of "port" nodes found in "ports" node + */ +static int stfcamss_of_parse_ports(struct stfcamss *stfcamss) +{ + struct device *dev = stfcamss->dev; + struct device_node *node = NULL; + struct device_node *remote = NULL; + int ret, num_subdevs = 0; + + for_each_endpoint_of_node(dev->of_node, node) { + struct stfcamss_async_subdev *csd; + + if (!of_device_is_available(node)) + continue; + + remote = of_graph_get_remote_port_parent(node); + if (!remote) { + st_err(ST_CAMSS, "Cannot get remote parent\n"); + ret = -EINVAL; + goto err_cleanup; + } + + csd = v4l2_async_nf_add_fwnode(&stfcamss->notifier, + of_fwnode_handle(remote), + struct stfcamss_async_subdev); + of_node_put(remote); + if (IS_ERR(csd)) { + ret = PTR_ERR(csd); + goto err_cleanup; + } + + ret = stfcamss_of_parse_endpoint_node(dev, node, csd); + if (ret < 0) + goto err_cleanup; + + num_subdevs++; + } + + return num_subdevs; + +err_cleanup: + of_node_put(node); + return ret; +} + +/* + * stfcamss_init_subdevices - Initialize subdev structures and resources + * @stfcamss: STFCAMSS device + * + * Return 0 on success or a negative error code on failure + */ +static int stfcamss_init_subdevices(struct stfcamss *stfcamss) +{ + int ret; + + ret = stf_isp_subdev_init(stfcamss); + if (ret < 0) { + st_err(ST_CAMSS, + "Failed to init stf_isp sub-device: %d\n", ret); + return ret; + } + + ret = stf_vin_subdev_init(stfcamss); + if (ret < 0) { + st_err(ST_CAMSS, + "Failed to init stf_vin sub-device: %d\n", ret); + return ret; + } + return ret; +} + +static int stfcamss_register_subdevices(struct stfcamss *stfcamss) +{ + int ret; + struct stf_vin_dev *vin_dev = stfcamss->vin_dev; + struct stf_isp_dev *isp_dev = stfcamss->isp_dev; + + ret = stf_isp_register(isp_dev, &stfcamss->v4l2_dev); + if (ret < 0) { + st_err(ST_CAMSS, + "Failed to register stf isp%d entity: %d\n", 0, ret); + goto err_reg_isp; + } + + ret = stf_vin_register(vin_dev, &stfcamss->v4l2_dev); + if (ret < 0) { + st_err(ST_CAMSS, + "Failed to register vin entity: %d\n", ret); + goto err_reg_vin; + } + + ret = media_create_pad_link(&isp_dev->subdev.entity, + STF_ISP_PAD_SRC, + &vin_dev->line[VIN_LINE_ISP].subdev.entity, + STF_VIN_PAD_SINK, + 0); + if (ret < 0) { + st_err(ST_CAMSS, + "Failed to link %s->%s entities: %d\n", + isp_dev->subdev.entity.name, + vin_dev->line[VIN_LINE_ISP].subdev.entity.name, + ret); + goto err_link; + } + + ret = media_create_pad_link( + &isp_dev->subdev.entity, + STF_ISP_PAD_SRC_SS0, + &vin_dev->line[VIN_LINE_ISP_SS0].subdev.entity, + STF_VIN_PAD_SINK, + 0); + + if (ret < 0) { + st_err(ST_CAMSS, + "Failed to link %s->%s entities: %d\n", + isp_dev->subdev.entity.name, + vin_dev->line[VIN_LINE_ISP_SS0].subdev.entity.name, + ret); + goto err_link; + } + + ret = media_create_pad_link( + &isp_dev->subdev.entity, + STF_ISP_PAD_SRC_SS1, + &vin_dev->line[VIN_LINE_ISP_SS1].subdev.entity, + STF_VIN_PAD_SINK, + 0); + if (ret < 0) { + st_err(ST_CAMSS, + "Failed to link %s->%s entities: %d\n", + isp_dev->subdev.entity.name, + vin_dev->line[VIN_LINE_ISP_SS1].subdev.entity.name, + ret); + goto err_link; + } + + ret = media_create_pad_link( + &isp_dev->subdev.entity, + STF_ISP_PAD_SRC_RAW, + &vin_dev->line[VIN_LINE_ISP_RAW].subdev.entity, + STF_VIN_PAD_SINK, + 0); + if (ret < 0) { + st_err(ST_CAMSS, + "Failed to link %s->%s entities: %d\n", + isp_dev->subdev.entity.name, + vin_dev->line[VIN_LINE_ISP_RAW].subdev.entity.name, + ret); + goto err_link; + } + + return ret; + +err_link: + stf_vin_unregister(stfcamss->vin_dev); +err_reg_vin: + stf_isp_unregister(stfcamss->isp_dev); +err_reg_isp: + return ret; +} + +static void stfcamss_unregister_subdevices(struct stfcamss *stfcamss) +{ + stf_isp_unregister(stfcamss->isp_dev); + stf_vin_unregister(stfcamss->vin_dev); +} + +static int stfcamss_register_media_subdev_nod(struct v4l2_async_notifier *async, + struct v4l2_subdev *sd) +{ + struct stfcamss *stfcamss = + container_of(async, struct stfcamss, notifier); + struct host_data *host_data = v4l2_get_subdev_hostdata(sd); + struct media_entity *sensor; + struct media_entity *input; + int ret; + int i, j; + + for (i = 0; host_data->host_entity[i] && (i < HOST_ENTITY_MAX); i++) { + sensor = &sd->entity; + input = host_data->host_entity[i]; + + for (j = 0; j < sensor->num_pads; j++) { + if (sensor->pads[j].flags & MEDIA_PAD_FL_SOURCE) + break; + } + + if (j == sensor->num_pads) { + st_err(ST_CAMSS, "No source pad in external entity\n"); + return -EINVAL; + } + + ret = media_create_pad_link( + sensor, + j, + input, + STF_PAD_SINK, + sensor->function == MEDIA_ENT_F_CAM_SENSOR ? + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED : + 0); + if (ret < 0) { + st_err(ST_CAMSS, "Failed to link %s->%s entities: %d\n", + sensor->name, input->name, ret); + return ret; + } + } + + ret = v4l2_device_register_subdev_nodes(&stfcamss->v4l2_dev); + if (ret < 0) + return ret; + + if (stfcamss->media_dev.devnode) + return ret; + + st_debug(ST_CAMSS, "stfcamss register media device\n"); + return media_device_register(&stfcamss->media_dev); +} + +static int stfcamss_subdev_notifier_bound(struct v4l2_async_notifier *async, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct stfcamss *stfcamss = + container_of(async, struct stfcamss, notifier); + struct stfcamss_async_subdev *csd = + container_of(asd, struct stfcamss_async_subdev, asd); + enum port_num port = csd->port; + struct stf_isp_dev *isp_dev = stfcamss->isp_dev; + struct stf_vin_dev *vin_dev = stfcamss->vin_dev; + struct host_data *host_data = &stfcamss->host_data; + + switch (port) { + case PORT_NUMBER_DVP_SENSOR: + host_data->host_entity[0] = NULL; + host_data->host_entity[1] = NULL; + /* not support DVP sensor */ + break; + case PORT_NUMBER_CSI2RX: + host_data->host_entity[0] = + &vin_dev->line[VIN_LINE_WR].subdev.entity; + host_data->host_entity[1] = &isp_dev->subdev.entity; + break; + default: + break; + }; + + v4l2_set_subdev_hostdata(subdev, host_data); + stfcamss_register_media_subdev_nod(async, subdev); + + return 0; +} + +static const struct v4l2_async_notifier_operations +stfcamss_subdev_notifier_ops = { + .bound = stfcamss_subdev_notifier_bound, +}; + +static const struct media_device_ops stfcamss_media_ops = { + .link_notify = v4l2_pipeline_link_notify, +}; + +/* + * stfcamss_probe - Probe STFCAMSS platform device + * @pdev: Pointer to STFCAMSS platform device + * + * Return 0 on success or a negative error code on failure + */ +static int stfcamss_probe(struct platform_device *pdev) +{ + struct stfcamss *stfcamss; + struct device *dev = &pdev->dev; + int ret = 0, num_subdevs; + + stfcamss = devm_kzalloc(dev, sizeof(struct stfcamss), GFP_KERNEL); + if (!stfcamss) + return -ENOMEM; + + stfcamss->isp_dev = devm_kzalloc(dev, sizeof(*stfcamss->isp_dev), + GFP_KERNEL); + if (!stfcamss->isp_dev) { + ret = -ENOMEM; + goto err_cam; + } + + stfcamss->vin_dev = devm_kzalloc(dev, sizeof(*stfcamss->vin_dev), + GFP_KERNEL); + if (!stfcamss->vin_dev) { + ret = -ENOMEM; + goto err_cam; + } + + stfcamss->irq = platform_get_irq(pdev, 0); + if (stfcamss->irq <= 0) { + st_err(ST_CAMSS, "Could not get irq\n"); + goto err_cam; + } + + stfcamss->isp_irq = platform_get_irq(pdev, 1); + if (stfcamss->isp_irq <= 0) { + st_err(ST_CAMSS, "Could not get isp irq\n"); + goto err_cam; + } + + stfcamss->isp_irq_csi = platform_get_irq(pdev, 2); + if (stfcamss->isp_irq_csi <= 0) { + st_err(ST_CAMSS, "Could not get isp csi irq\n"); + goto err_cam; + } + + stfcamss->isp_irq_csiline = platform_get_irq(pdev, 3); + if (stfcamss->isp_irq_csiline <= 0) { + st_err(ST_CAMSS, "Could not get isp irq csiline\n"); + goto err_cam; + } + + pm_runtime_enable(dev); + + stfcamss->nclks = ARRAY_SIZE(stfcamss_clocks); + stfcamss->sys_clk = stfcamss_clocks; + + ret = devm_clk_bulk_get(dev, stfcamss->nclks, stfcamss->sys_clk); + if (ret) { + st_err(ST_CAMSS, "Failed to get clk controls\n"); + return ret; + } + + stfcamss->nrsts = ARRAY_SIZE(stfcamss_resets); + stfcamss->sys_rst = stfcamss_resets; + + ret = devm_reset_control_bulk_get_shared(dev, stfcamss->nrsts, + stfcamss->sys_rst); + if (ret) { + st_err(ST_CAMSS, "Failed to get reset controls\n"); + return ret; + } + + ret = stfcamss_get_mem_res(pdev, stfcamss); + if (ret) { + st_err(ST_CAMSS, "Could not map registers\n"); + goto err_cam; + } + + stfcamss->dev = dev; + platform_set_drvdata(pdev, stfcamss); + + v4l2_async_nf_init(&stfcamss->notifier); + + num_subdevs = stfcamss_of_parse_ports(stfcamss); + if (num_subdevs < 0) { + ret = num_subdevs; + goto err_cam_noti; + } + + ret = stfcamss_init_subdevices(stfcamss); + if (ret < 0) { + st_err(ST_CAMSS, "Failed to init subdevice: %d\n", ret); + goto err_cam_noti; + } + + stfcamss->media_dev.dev = stfcamss->dev; + strscpy(stfcamss->media_dev.model, "Starfive Camera Subsystem", + sizeof(stfcamss->media_dev.model)); + strscpy(stfcamss->media_dev.serial, "0123456789ABCDEF", + sizeof(stfcamss->media_dev.serial)); + snprintf(stfcamss->media_dev.bus_info, + sizeof(stfcamss->media_dev.bus_info), + "%s:%s", dev_bus_name(dev), pdev->name); + stfcamss->media_dev.hw_revision = 0x01; + stfcamss->media_dev.ops = &stfcamss_media_ops; + media_device_init(&stfcamss->media_dev); + + stfcamss->v4l2_dev.mdev = &stfcamss->media_dev; + + ret = v4l2_device_register(stfcamss->dev, &stfcamss->v4l2_dev); + if (ret < 0) { + st_err(ST_CAMSS, "Failed to register V4L2 device: %d\n", ret); + goto err_cam_noti_med; + } + + ret = stfcamss_register_subdevices(stfcamss); + if (ret < 0) { + st_err(ST_CAMSS, "Failed to register subdevice: %d\n", ret); + goto err_cam_noti_med_vreg; + } + + if (num_subdevs) { + stfcamss->notifier.ops = &stfcamss_subdev_notifier_ops; + ret = v4l2_async_nf_register(&stfcamss->v4l2_dev, + &stfcamss->notifier); + if (ret) { + st_err(ST_CAMSS, + "Failed to register async subdev nodes: %d\n", + ret); + goto err_cam_noti_med_vreg_sub; + } + } else { + ret = v4l2_device_register_subdev_nodes(&stfcamss->v4l2_dev); + if (ret < 0) { + st_err(ST_CAMSS, + "Failed to register subdev nodes: %d\n", + ret); + goto err_cam_noti_med_vreg_sub; + } + + ret = media_device_register(&stfcamss->media_dev); + if (ret < 0) { + st_err(ST_CAMSS, + "Failed to register media device: %d\n", + ret); + goto err_cam_noti_med_vreg_sub_medreg; + } + } + + dev_info(dev, "stfcamss probe success!\n"); + return 0; + +err_cam_noti_med_vreg_sub_medreg: +err_cam_noti_med_vreg_sub: + stfcamss_unregister_subdevices(stfcamss); +err_cam_noti_med_vreg: + v4l2_device_unregister(&stfcamss->v4l2_dev); +err_cam_noti_med: + media_device_cleanup(&stfcamss->media_dev); +err_cam_noti: + v4l2_async_nf_cleanup(&stfcamss->notifier); +err_cam: + return ret; +} + +/* + * stfcamss_remove - Remove STFCAMSS platform device + * @pdev: Pointer to STFCAMSS platform device + * + * Always returns 0. + */ +static int stfcamss_remove(struct platform_device *pdev) +{ + struct stfcamss *stfcamss = platform_get_drvdata(pdev); + + dev_info(&pdev->dev, "remove done\n"); + + stfcamss_unregister_subdevices(stfcamss); + v4l2_device_unregister(&stfcamss->v4l2_dev); + media_device_cleanup(&stfcamss->media_dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct of_device_id stfcamss_of_match[] = { + { .compatible = "starfive,jh7110-camss" }, + { /* end node */ }, +}; + +MODULE_DEVICE_TABLE(of, stfcamss_of_match); + +#ifdef CONFIG_PM_SLEEP +static int stfcamss_suspend(struct device *dev) +{ + struct stfcamss *stfcamss = dev_get_drvdata(dev); + struct stf_vin_dev *vin_dev = stfcamss->vin_dev; + struct media_entity *entity; + struct media_pad *pad; + struct v4l2_subdev *subdev; + struct stfcamss_video *video; + struct video_device *vdev; + int i = 0; + + for (i = 0; i < VIN_LINE_MAX; i++) { + if (vin_dev->line[i].stream_count) { + vin_dev->line[i].stream_count++; + video = &vin_dev->line[i].video_out; + vdev = &vin_dev->line[i].video_out.vdev; + entity = &vdev->entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || + !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + v4l2_subdev_call(subdev, video, s_stream, 0); + } + video_device_pipeline_stop(vdev); + video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR); + v4l2_pipeline_pm_put(&vdev->entity); + } + } + + return pm_runtime_force_suspend(dev); +} + +static int stfcamss_resume(struct device *dev) +{ + struct stfcamss *stfcamss = dev_get_drvdata(dev); + struct stf_vin_dev *vin_dev = stfcamss->vin_dev; + struct media_entity *entity; + struct media_pad *pad; + struct v4l2_subdev *subdev; + struct stfcamss_video *video; + struct video_device *vdev; + int i = 0; + int ret = 0; + + pm_runtime_force_resume(dev); + + for (i = 0; i < VIN_LINE_MAX; i++) { + if (vin_dev->line[i].stream_count) { + vin_dev->line[i].stream_count--; + video = &vin_dev->line[i].video_out; + vdev = &vin_dev->line[i].video_out.vdev; + + ret = v4l2_pipeline_pm_get(&vdev->entity); + if (ret < 0) + goto err; + + ret = video_device_pipeline_start( + vdev, &video->stfcamss->pipe); + if (ret < 0) + goto err_pm_put; + + entity = &vdev->entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || + !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, video, + s_stream, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) + goto err_pipeline_stop; + } + } + } + + return 0; + +err_pipeline_stop: + video_device_pipeline_stop(vdev); +err_pm_put: + v4l2_pipeline_pm_put(&vdev->entity); +err: + return ret; +} +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_PM +static int stfcamss_runtime_suspend(struct device *dev) +{ + struct stfcamss *stfcamss = dev_get_drvdata(dev); + + reset_control_assert(stfcamss->sys_rst[STF_RST_ISP_TOP_AXI].rstc); + reset_control_assert(stfcamss->sys_rst[STF_RST_ISP_TOP_N].rstc); + clk_disable_unprepare(stfcamss->sys_clk[STF_CLK_ISP_AXI].clk); + clk_disable_unprepare(stfcamss->sys_clk[STF_CLK_ISPCORE_2X].clk); + + return 0; +} + +static int stfcamss_runtime_resume(struct device *dev) +{ + struct stfcamss *stfcamss = dev_get_drvdata(dev); + + clk_prepare_enable(stfcamss->sys_clk[STF_CLK_ISPCORE_2X].clk); + clk_prepare_enable(stfcamss->sys_clk[STF_CLK_ISP_AXI].clk); + reset_control_deassert(stfcamss->sys_rst[STF_RST_ISP_TOP_N].rstc); + reset_control_deassert(stfcamss->sys_rst[STF_RST_ISP_TOP_AXI].rstc); + + return 0; +} +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops stfcamss_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(stfcamss_suspend, stfcamss_resume) + SET_RUNTIME_PM_OPS(stfcamss_runtime_suspend, + stfcamss_runtime_resume, + NULL) +}; + +static struct platform_driver stfcamss_driver = { + .probe = stfcamss_probe, + .remove = stfcamss_remove, + .driver = { + .name = DRV_NAME, + .pm = &stfcamss_pm_ops, + .of_match_table = of_match_ptr(stfcamss_of_match), + }, +}; + +module_platform_driver(stfcamss_driver); + +MODULE_AUTHOR("StarFive Corporation"); +MODULE_DESCRIPTION("StarFive Camera Subsystem driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/starfive/stf_camss.h b/drivers/media/platform/starfive/stf_camss.h new file mode 100644 index 0000000000000..a8c6579d62ed6 --- /dev/null +++ b/drivers/media/platform/starfive/stf_camss.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * stf_camss.h + * + * Starfive Camera Subsystem driver + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ + +#ifndef STF_CAMSS_H +#define STF_CAMSS_H + +#include +#include +#include + +enum sensor_type { + SENSOR_VIN, + /* need replace sensor */ + SENSOR_ISP, +}; + +enum subdev_type { + SUBDEV_TYPE_VIN, + SUBDEV_TYPE_ISP, +}; + +#include "stf_common.h" +#include "stf_isp.h" +#include "stf_vin.h" + +#define DRV_NAME "starfive-camss" +#define STF_DVP_NAME "stf_dvp" +#define STF_CSI_NAME "cdns_csi2rx" +#define STF_ISP_NAME "stf_isp" +#define STF_VIN_NAME "stf_vin" + +#define STF_PAD_SINK 0 +#define STF_PAD_SRC 1 +#define STF_PADS_NUM 2 + +enum port_num { + PORT_NUMBER_DVP_SENSOR = 0, + PORT_NUMBER_CSI2RX +}; + +enum stf_clk { + STF_CLK_APB_FUNC = 0, + STF_CLK_WRAPPER_CLK_C, + STF_CLK_DVP_INV, + STF_CLK_AXIWR, + STF_CLK_MIPI_RX0_PXL, + STF_CLK_ISPCORE_2X, + STF_CLK_ISP_AXI, + STF_CLK_NUM +}; + +enum stf_rst { + STF_RST_WRAPPER_P = 0, + STF_RST_WRAPPER_C, + STF_RST_AXIRD, + STF_RST_AXIWR, + STF_RST_ISP_TOP_N, + STF_RST_ISP_TOP_AXI, + STF_RST_NUM +}; + +#define HOST_ENTITY_MAX 2 + +struct host_data { + struct media_entity *host_entity[HOST_ENTITY_MAX]; +}; + +struct stfcamss { + struct v4l2_device v4l2_dev; + struct media_device media_dev; + struct media_pipeline pipe; + struct device *dev; + struct stf_vin_dev *vin_dev; + struct stf_isp_dev *isp_dev; + struct v4l2_async_notifier notifier; + struct host_data host_data; + void __iomem *syscon_base; + void __iomem *isp_base; + int irq; + int isp_irq; + int isp_irq_csi; + int isp_irq_csiline; + struct clk_bulk_data *sys_clk; + int nclks; + struct reset_control_bulk_data *sys_rst; + int nrsts; + struct regmap *stf_aon_syscon; + u32 aon_gp_reg; +}; + +struct stfcamss_async_subdev { + struct v4l2_async_subdev asd; /* must be first */ + enum port_num port; +}; + +struct media_entity *stfcamss_find_sensor(struct media_entity *entity); + +#endif /* STF_CAMSS_H */ diff --git a/drivers/media/platform/starfive/stf_common.h b/drivers/media/platform/starfive/stf_common.h new file mode 100644 index 0000000000000..90f5e4c3061ca --- /dev/null +++ b/drivers/media/platform/starfive/stf_common.h @@ -0,0 +1,149 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * stf_common.h + * + * StarFive Camera Subsystem - Common definitions + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ + +#ifndef STF_COMMON_H +#define STF_COMMON_H + +#include +#include + +enum { + ST_DVP = 0x0001, + ST_ISP = 0x0002, + ST_VIN = 0x0004, + ST_VIDEO = 0x0008, + ST_CAMSS = 0x0010, + ST_SENSOR = 0x0020, +}; + +enum { + ST_NONE = 0x00, + ST_ERR = 0x01, + ST_WARN = 0x02, + ST_INFO = 0x03, + ST_DEBUG = 0x04, +}; + +#ifdef STF_DEBUG +#define STFDBG_LEVEL ST_DEBUG +#define STFDBG_MASK 0x7F +#else +#define STFDBG_LEVEL ST_ERR +#define STFDBG_MASK 0x7F +#endif + +#define ST_MODULE2STRING(__module) ({ \ + char *__str; \ + \ + switch (__module) { \ + case ST_DVP: \ + __str = "st_dvp"; \ + break; \ + case ST_ISP: \ + __str = "st_isp"; \ + break; \ + case ST_VIN: \ + __str = "st_vin"; \ + break; \ + case ST_VIDEO: \ + __str = "st_video"; \ + break; \ + case ST_CAMSS: \ + __str = "st_camss"; \ + break; \ + case ST_SENSOR: \ + __str = "st_sensor"; \ + break; \ + default: \ + __str = "unknown"; \ + break; \ + } \ + \ + __str; \ + }) + +#define st_debug(module, __fmt, arg...) \ + do { \ + if (STFDBG_LEVEL > ST_INFO) { \ + if (STFDBG_MASK & (module)) \ + pr_err("[%s] debug: " __fmt, \ + ST_MODULE2STRING((module)), \ + ## arg); \ + } \ + } while (0) + +#define st_info(module, __fmt, arg...) \ + do { \ + if (STFDBG_LEVEL > ST_WARN) { \ + if (STFDBG_MASK & (module)) \ + pr_err("[%s] info: " __fmt, \ + ST_MODULE2STRING((module)), \ + ## arg); \ + } \ + } while (0) + +#define st_warn(module, __fmt, arg...) \ + do { \ + if (STFDBG_LEVEL > ST_ERR) { \ + if (STFDBG_MASK & (module)) \ + pr_err("[%s] warn: " __fmt, \ + ST_MODULE2STRING((module)), \ + ## arg); \ + } \ + } while (0) + +#define st_err(module, __fmt, arg...) \ + do { \ + if (STFDBG_LEVEL > ST_NONE) { \ + if (STFDBG_MASK & (module)) \ + pr_err("[%s] error: " __fmt, \ + ST_MODULE2STRING((module)), \ + ## arg); \ + } \ + } while (0) + +#define st_err_ratelimited(module, fmt, ...) \ + do { \ + static DEFINE_RATELIMIT_STATE(_rs, \ + DEFAULT_RATELIMIT_INTERVAL, \ + DEFAULT_RATELIMIT_BURST); \ + if (__ratelimit(&_rs) && STFDBG_LEVEL > ST_NONE) { \ + if (STFDBG_MASK & (module)) \ + pr_err("[%s] error: " fmt, \ + ST_MODULE2STRING((module)), \ + ##__VA_ARGS__); \ + } \ + } while (0) + +static inline u32 reg_read(void __iomem *base, u32 reg) +{ + return ioread32(base + reg); +} + +static inline void reg_write(void __iomem *base, u32 reg, u32 val) +{ + iowrite32(val, base + reg); +} + +static inline void reg_set_bit(void __iomem *base, u32 reg, u32 mask, u32 val) +{ + u32 value; + + value = ioread32(base + reg) & ~mask; + val &= mask; + val |= value; + iowrite32(val, base + reg); +} + +static inline void reg_set(void __iomem *base, u32 reg, u32 mask) +{ + iowrite32(ioread32(base + reg) | mask, base + reg); +} + +#endif /* STF_COMMON_H */ diff --git a/drivers/media/platform/starfive/stf_isp.c b/drivers/media/platform/starfive/stf_isp.c new file mode 100644 index 0000000000000..0c67d1d06e036 --- /dev/null +++ b/drivers/media/platform/starfive/stf_isp.c @@ -0,0 +1,1079 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * stf_isp.c + * + * StarFive Camera Subsystem - ISP Module + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ +#include +#include +#include + +#include "stf_camss.h" + +#define SINK_FORMATS_INDEX 0 +#define UO_FORMATS_INDEX 1 +#define RAW_FORMATS_INDEX 2 + +static int user_config_isp; + +static int isp_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel); + +static struct v4l2_rect * +__isp_get_compose(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which); + +static struct v4l2_rect * +__isp_get_crop(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which); + +static struct v4l2_rect * +__isp_get_scale(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel); + +static const struct isp_format isp_formats_sink[] = { + { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, +}; + +static const struct isp_format isp_formats_raw[] = { + { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, +}; + +static const struct isp_format isp_formats_compat_raw10[] = { + { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, +}; + +static const struct isp_format isp_formats_compat_raw8[] = { + { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, +}; + +static const struct isp_format isp_formats_uo[] = { + { MEDIA_BUS_FMT_Y12_1X12, 8 }, +}; + +static const struct isp_format_table isp_formats_st7110[] = { + { isp_formats_sink, ARRAY_SIZE(isp_formats_sink) }, + { isp_formats_uo, ARRAY_SIZE(isp_formats_uo) }, + { isp_formats_raw, ARRAY_SIZE(isp_formats_raw) }, +}; + +int stf_isp_subdev_init(struct stfcamss *stfcamss) +{ + struct stf_isp_dev *isp_dev = stfcamss->isp_dev; + struct device *dev = stfcamss->dev; + + isp_dev->sdev_type = SUBDEV_TYPE_ISP; + isp_dev->hw_ops = &isp_ops; + isp_dev->stfcamss = stfcamss; + isp_dev->formats = isp_formats_st7110; + isp_dev->nformats = ARRAY_SIZE(isp_formats_st7110); + isp_dev->reg_buf = devm_kzalloc(dev, sizeof(*isp_dev->reg_buf), + GFP_KERNEL); + if (!isp_dev->reg_buf) + return -ENOMEM; + + mutex_init(&isp_dev->stream_lock); + mutex_init(&isp_dev->power_lock); + mutex_init(&isp_dev->setfile_lock); + atomic_set(&isp_dev->shadow_count, 0); + return 0; +} + +static int isp_set_power(struct v4l2_subdev *sd, int on) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + + mutex_lock(&isp_dev->power_lock); + if (on) { + if (isp_dev->power_count == 0) + st_debug(ST_ISP, "turn on isp\n"); + isp_dev->power_count++; + } else { + if (isp_dev->power_count == 0) + goto exit; + isp_dev->power_count--; + } +exit: + mutex_unlock(&isp_dev->power_lock); + + return 0; +} + +static struct v4l2_mbus_framefmt * +__isp_get_format(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(&isp_dev->subdev, state, pad); + + return &isp_dev->fmt[pad]; +} + +static int isp_get_interface_type(struct media_entity *entity) +{ + struct v4l2_subdev *subdev; + struct media_pad *pad = &entity->pads[0]; + + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + return -EINVAL; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + return -EINVAL; + + subdev = media_entity_to_v4l2_subdev(pad->entity); + + st_debug(ST_ISP, "interface subdev name %s\n", subdev->name); + if (!strncmp(subdev->name, STF_CSI_NAME, strlen(STF_CSI_NAME))) + return INTERFACE_CSI; + + return -EINVAL; +} + +static int isp_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + int ret = 0, interface_type; + struct v4l2_mbus_framefmt *fmt; + struct v4l2_event src_ch = { 0 }; + + fmt = __isp_get_format(isp_dev, NULL, STF_ISP_PAD_SINK, + V4L2_SUBDEV_FORMAT_ACTIVE); + mutex_lock(&isp_dev->stream_lock); + if (enable) { + if (isp_dev->stream_count == 0) { + isp_dev->hw_ops->isp_clk_enable(isp_dev); + if (!user_config_isp) + isp_dev->hw_ops->isp_config_set(isp_dev); + interface_type = isp_get_interface_type(&sd->entity); + if (interface_type < 0) { + st_err(ST_ISP, + "%s, pipeline not config\n", __func__); + goto exit; + } + isp_dev->hw_ops->isp_set_format(isp_dev, + isp_dev->rect, + fmt->code, + interface_type); + isp_dev->hw_ops->isp_reset(isp_dev); + isp_dev->hw_ops->isp_stream_set(isp_dev, enable); + user_config_isp = 0; + } + isp_dev->stream_count++; + } else { + if (isp_dev->stream_count == 0) + goto exit; + if (isp_dev->stream_count == 1) { + isp_dev->hw_ops->isp_stream_set(isp_dev, enable); + isp_dev->hw_ops->isp_clk_disable(isp_dev); + } + isp_dev->stream_count--; + } + src_ch.type = V4L2_EVENT_SOURCE_CHANGE, + src_ch.u.src_change.changes = isp_dev->stream_count, + + v4l2_subdev_notify_event(sd, &src_ch); +exit: + mutex_unlock(&isp_dev->stream_lock); + + return ret; +} + +static int isp_sensor_fmt_to_index(struct stf_isp_dev *isp_dev) +{ + int ret, idx; + struct media_entity *sensor; + struct v4l2_subdev *subdev; + struct v4l2_subdev_format fmt; + const struct isp_format_table *formats; + + if (!isp_dev) + return -EINVAL; + + sensor = stfcamss_find_sensor(&isp_dev->subdev.entity); + if (!sensor) + return -EINVAL; + + subdev = media_entity_to_v4l2_subdev(sensor); + st_debug(ST_ISP, "Found sensor = %s\n", sensor->name); + + fmt.pad = 0; + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret) { + st_warn(ST_ISP, "Sonser get format failed !!\n"); + return -EINVAL; + } + + st_debug(ST_ISP, "Got sensor format 0x%x !!\n", fmt.format.code); + + formats = &isp_dev->formats[SINK_FORMATS_INDEX]; + for (idx = 0; idx < formats->nfmts; idx++) { + if (formats->fmts[idx].code == fmt.format.code) { + st_info(ST_ISP, "Match sensor format to index %d\n", + idx); + return idx; + } + } + return -ERANGE; +} + +static int isp_fmt_to_index(const struct isp_format_table *f_table, + __u32 mbus_code, unsigned int pad) +{ + int i; + + for (i = 0; i < f_table->nfmts; i++) { + if (mbus_code == f_table->fmts[i].code) + break; + + if (pad == STF_ISP_PAD_SRC_RAW) { + if (mbus_code == isp_formats_compat_raw10[i].code || + mbus_code == isp_formats_compat_raw8[i].code) + break; + } + } + + return i; +} + +static void isp_try_format(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + unsigned int pad, + struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + const struct isp_format_table *formats; + unsigned int i; + u32 code = fmt->code; + u32 bpp; + + switch (pad) { + case STF_ISP_PAD_SINK: + /* Set format on sink pad */ + formats = &isp_dev->formats[SINK_FORMATS_INDEX]; + fmt->width = clamp_t(u32, + fmt->width, STFCAMSS_FRAME_MIN_WIDTH, + STFCAMSS_FRAME_MAX_WIDTH); + fmt->height = clamp_t(u32, + fmt->height, STFCAMSS_FRAME_MIN_HEIGHT, + STFCAMSS_FRAME_MAX_HEIGHT); + fmt->height &= ~0x1; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->flags = 0; + break; + + case STF_ISP_PAD_SRC: + case STF_ISP_PAD_SRC_SS0: + case STF_ISP_PAD_SRC_SS1: + formats = &isp_dev->formats[UO_FORMATS_INDEX]; + break; + + case STF_ISP_PAD_SRC_RAW: + formats = &isp_dev->formats[RAW_FORMATS_INDEX]; + break; + } + + i = isp_fmt_to_index(formats, fmt->code, pad); + st_debug(ST_ISP, "%s pad=%d, code=%x format to index = %d\n", + __func__, pad, code, i); + + if (i >= formats->nfmts && pad == STF_ISP_PAD_SRC_RAW) { + int sensor_idx; + + sensor_idx = isp_sensor_fmt_to_index(isp_dev); + if (sensor_idx) + i = sensor_idx; + } + + if (pad != STF_ISP_PAD_SINK) + *fmt = *__isp_get_format(isp_dev, state, + STF_ISP_PAD_SINK, which); + + if (i >= formats->nfmts) { + fmt->code = formats->fmts[0].code; + bpp = formats->fmts[0].bpp; + st_info(ST_ISP, "Use default index 0 format = 0x%x\n", + fmt->code); + } else { + if (pad == STF_ISP_PAD_SRC_RAW) { + fmt->code = formats->fmts[i].code; + bpp = formats->fmts[i].bpp; + st_info(ST_ISP, + "use format from sink index %d = 0x%x\n", + i, fmt->code); + } else { + fmt->code = code; + bpp = formats->fmts[i].bpp; + st_info(ST_ISP, "Use input format = 0x%x\n", fmt->code); + } + } + + switch (pad) { + case STF_ISP_PAD_SINK: + break; + + case STF_ISP_PAD_SRC: + isp_dev->rect[ISP_COMPOSE].bpp = bpp; + break; + + case STF_ISP_PAD_SRC_SS0: + isp_dev->rect[ISP_SCALE_SS0].bpp = bpp; + break; + + case STF_ISP_PAD_SRC_SS1: + isp_dev->rect[ISP_SCALE_SS1].bpp = bpp; + break; + + case STF_ISP_PAD_SRC_RAW: + isp_dev->rect[ISP_CROP].bpp = bpp; + break; + } +} + +static int isp_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + const struct isp_format_table *formats; + + if (code->index >= isp_dev->nformats) + return -EINVAL; + if (code->pad == STF_ISP_PAD_SINK) { + formats = &isp_dev->formats[SINK_FORMATS_INDEX]; + code->code = formats->fmts[code->index].code; + } else { + struct v4l2_mbus_framefmt *sink_fmt; + + sink_fmt = __isp_get_format(isp_dev, state, STF_ISP_PAD_SINK, + code->which); + + code->code = sink_fmt->code; + if (!code->code) + return -EINVAL; + } + code->flags = 0; + + return 0; +} + +static int isp_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + isp_try_format(isp_dev, state, fse->pad, &format, fse->which); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + isp_try_format(isp_dev, state, fse->pad, &format, fse->which); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +static int isp_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __isp_get_format(isp_dev, state, fmt->pad, fmt->which); + if (!format) + return -EINVAL; + + fmt->format = *format; + + return 0; +} + +static int isp_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + struct v4l2_subdev_selection sel = { 0 }; + struct v4l2_rect *rect = NULL; + int ret; + + st_debug(ST_ISP, "%s pad=%d, code=%x, which=%d\n", + __func__, fmt->reserved[0], fmt->format.code, fmt->which); + format = __isp_get_format(isp_dev, state, fmt->pad, fmt->which); + if (!format) + return -EINVAL; + + mutex_lock(&isp_dev->stream_lock); + if (isp_dev->stream_count) { + fmt->format = *format; + if (fmt->reserved[0] != 0) { + sel.which = fmt->which; + sel.pad = fmt->reserved[0]; + + switch (fmt->reserved[0]) { + case STF_ISP_PAD_SRC: + rect = __isp_get_compose(isp_dev, + state, fmt->which); + break; + + case STF_ISP_PAD_SRC_SS0: + case STF_ISP_PAD_SRC_SS1: + rect = __isp_get_scale(isp_dev, state, &sel); + break; + + case STF_ISP_PAD_SRC_RAW: + rect = __isp_get_crop(isp_dev, + state, fmt->which); + break; + + default: + break; + } + if (rect) { + fmt->format.width = rect->width; + fmt->format.height = rect->height; + } + } + mutex_unlock(&isp_dev->stream_lock); + goto out; + } else { + isp_try_format(isp_dev, state, fmt->pad, + &fmt->format, fmt->which); + *format = fmt->format; + } + mutex_unlock(&isp_dev->stream_lock); + + /* Propagate the format from sink to source */ + if (fmt->pad == STF_ISP_PAD_SINK) { + /* Reset sink pad compose selection */ + sel.which = fmt->which; + sel.pad = STF_ISP_PAD_SINK; + sel.target = V4L2_SEL_TGT_CROP; + sel.r.width = fmt->format.width; + sel.r.height = fmt->format.height; + ret = isp_set_selection(sd, state, &sel); + if (ret < 0) + return ret; + } + +out: + return 0; +} + +static struct v4l2_rect * +__isp_get_compose(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_compose(&isp_dev->subdev, state, + STF_ISP_PAD_SINK); + + return &isp_dev->rect[ISP_COMPOSE].rect; +} + +static struct v4l2_rect * +__isp_get_crop(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_crop(&isp_dev->subdev, state, + STF_ISP_PAD_SINK); + + return &isp_dev->rect[ISP_CROP].rect; +} + +static struct v4l2_rect * +__isp_get_scale(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + int pad; + + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_compose(&isp_dev->subdev, state, + STF_ISP_PAD_SINK); + if (sel->pad != STF_ISP_PAD_SRC_SS0 && sel->pad != STF_ISP_PAD_SRC_SS1) + return NULL; + + pad = sel->pad == STF_ISP_PAD_SRC_SS0 ? ISP_SCALE_SS0 : ISP_SCALE_SS1; + return &isp_dev->rect[pad].rect; +} + +static void isp_try_crop(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + struct v4l2_rect *rect, + enum v4l2_subdev_format_whence which) +{ + struct v4l2_mbus_framefmt *fmt; + + fmt = __isp_get_format(isp_dev, state, STF_ISP_PAD_SINK, which); + + if (rect->width > fmt->width) + rect->width = fmt->width; + + if (rect->width + rect->left > fmt->width) + rect->left = fmt->width - rect->width; + + if (rect->height > fmt->height) + rect->height = fmt->height; + + if (rect->height + rect->top > fmt->height) + rect->top = fmt->height - rect->height; + + if (rect->width < STFCAMSS_FRAME_MIN_WIDTH) { + rect->left = 0; + rect->width = STFCAMSS_FRAME_MIN_WIDTH; + } + + if (rect->height < STFCAMSS_FRAME_MIN_HEIGHT) { + rect->top = 0; + rect->height = STFCAMSS_FRAME_MIN_HEIGHT; + } + rect->height &= ~0x1; +} + +static void isp_try_compose(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + struct v4l2_rect *rect, + enum v4l2_subdev_format_whence which) +{ + struct v4l2_rect *crop; + + crop = __isp_get_crop(isp_dev, state, which); + + if (rect->width > crop->width) + rect->width = crop->width; + + if (rect->height > crop->height) + rect->height = crop->height; + + if (crop->width > rect->width * SCALER_RATIO_MAX) + rect->width = + (crop->width + SCALER_RATIO_MAX - 1) / SCALER_RATIO_MAX; + + if (crop->height > rect->height * SCALER_RATIO_MAX) + rect->height = + (crop->height + SCALER_RATIO_MAX - 1) / SCALER_RATIO_MAX; + + if (rect->width < STFCAMSS_FRAME_MIN_WIDTH) + rect->width = STFCAMSS_FRAME_MIN_WIDTH; + + if (rect->height < STFCAMSS_FRAME_MIN_HEIGHT) + rect->height = STFCAMSS_FRAME_MIN_HEIGHT; + rect->height &= ~0x1; +} + +static void isp_try_scale(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + struct v4l2_rect *rect, + enum v4l2_subdev_format_whence which) +{ + struct v4l2_rect *compose; + + compose = __isp_get_compose(isp_dev, state, which); + + if (rect->width > compose->width) + rect->width = compose->width; + + if (rect->width + rect->left > compose->width) + rect->left = compose->width - rect->width; + + if (rect->height > compose->height) + rect->height = compose->height; + + if (rect->height + rect->top > compose->height) + rect->top = compose->height - rect->height; + + if (rect->width < STFCAMSS_FRAME_MIN_WIDTH) { + rect->left = 0; + rect->width = STFCAMSS_FRAME_MIN_WIDTH; + } + + if (rect->height < STFCAMSS_FRAME_MIN_HEIGHT) { + rect->top = 0; + rect->height = STFCAMSS_FRAME_MIN_HEIGHT; + } + + rect->height &= ~0x1; +} + +static int isp_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + struct v4l2_subdev_format fmt = { 0 }; + struct v4l2_rect *rect; + int ret; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + fmt.pad = sel->pad; + fmt.which = sel->which; + ret = isp_get_format(sd, state, &fmt); + if (ret < 0) + return ret; + + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = fmt.format.width; + sel->r.height = fmt.format.height; + break; + + case V4L2_SEL_TGT_CROP: + rect = __isp_get_crop(isp_dev, state, sel->which); + if (!rect) + return -EINVAL; + + sel->r = *rect; + break; + + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + if (sel->pad > STF_ISP_PAD_SRC_SS1) + return -EINVAL; + rect = __isp_get_crop(isp_dev, state, sel->which); + if (!rect) + return -EINVAL; + + sel->r.left = rect->left; + sel->r.top = rect->top; + sel->r.width = rect->width; + sel->r.height = rect->height; + break; + + case V4L2_SEL_TGT_COMPOSE: + if (sel->pad > STF_ISP_PAD_SRC_SS1) + return -EINVAL; + if (sel->pad == STF_ISP_PAD_SRC_SS0 || + sel->pad == STF_ISP_PAD_SRC_SS1) { + rect = __isp_get_scale(isp_dev, state, sel); + if (!rect) + return -EINVAL; + } else { + rect = __isp_get_compose(isp_dev, state, sel->which); + if (!rect) + return -EINVAL; + } + sel->r = *rect; + break; + + default: + return -EINVAL; + } + + st_info(ST_ISP, "%s pad = %d, left = %d, %d, %d, %d\n", + __func__, sel->pad, sel->r.left, + sel->r.top, sel->r.width, sel->r.height); + return 0; +} + +static int isp_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + struct v4l2_rect *rect; + int ret = 0; + + if (sel->target == V4L2_SEL_TGT_COMPOSE && + (sel->pad == STF_ISP_PAD_SINK || sel->pad == STF_ISP_PAD_SRC)) { + struct v4l2_subdev_format fmt = { 0 }; + int i; + + rect = __isp_get_compose(isp_dev, state, sel->which); + if (!rect) + return -EINVAL; + + mutex_lock(&isp_dev->stream_lock); + if (isp_dev->stream_count) { + sel->r = *rect; + mutex_unlock(&isp_dev->stream_lock); + ret = 0; + goto out; + } else { + isp_try_compose(isp_dev, state, &sel->r, sel->which); + *rect = sel->r; + } + mutex_unlock(&isp_dev->stream_lock); + + /* Reset source pad format width and height */ + fmt.which = sel->which; + fmt.pad = STF_ISP_PAD_SRC; + ret = isp_get_format(sd, state, &fmt); + if (ret < 0) + return ret; + + fmt.format.width = rect->width; + fmt.format.height = rect->height; + ret = isp_set_format(sd, state, &fmt); + + /* Reset scale */ + for (i = STF_ISP_PAD_SRC_SS0; i <= STF_ISP_PAD_SRC_SS1; i++) { + struct v4l2_subdev_selection scale = { 0 }; + + scale.which = sel->which; + scale.target = V4L2_SEL_TGT_COMPOSE; + scale.r = *rect; + scale.pad = i; + ret = isp_set_selection(sd, state, &scale); + } + } else if (sel->target == V4L2_SEL_TGT_COMPOSE && + ((sel->pad == STF_ISP_PAD_SRC_SS0) || + (sel->pad == STF_ISP_PAD_SRC_SS1))) { + struct v4l2_subdev_format fmt = { 0 }; + + rect = __isp_get_scale(isp_dev, state, sel); + if (!rect) + return -EINVAL; + + mutex_lock(&isp_dev->stream_lock); + if (isp_dev->stream_count) { + sel->r = *rect; + mutex_unlock(&isp_dev->stream_lock); + ret = 0; + goto out; + } else { + isp_try_scale(isp_dev, state, &sel->r, sel->which); + *rect = sel->r; + } + mutex_unlock(&isp_dev->stream_lock); + + /* Reset source pad format width and height */ + fmt.which = sel->which; + fmt.pad = sel->pad; + ret = isp_get_format(sd, state, &fmt); + if (ret < 0) + return ret; + + fmt.format.width = rect->width; + fmt.format.height = rect->height; + ret = isp_set_format(sd, state, &fmt); + } else if (sel->target == V4L2_SEL_TGT_CROP) { + struct v4l2_subdev_selection compose = { 0 }; + int i; + + rect = __isp_get_crop(isp_dev, state, sel->which); + if (!rect) + return -EINVAL; + + mutex_lock(&isp_dev->stream_lock); + if (isp_dev->stream_count) { + sel->r = *rect; + mutex_unlock(&isp_dev->stream_lock); + ret = 0; + goto out; + } else { + isp_try_crop(isp_dev, state, &sel->r, sel->which); + *rect = sel->r; + } + mutex_unlock(&isp_dev->stream_lock); + + /* Reset source compose selection */ + compose.which = sel->which; + compose.target = V4L2_SEL_TGT_COMPOSE; + compose.r.width = rect->width; + compose.r.height = rect->height; + compose.pad = STF_ISP_PAD_SINK; + ret = isp_set_selection(sd, state, &compose); + + /* Reset source pad format width and height */ + for (i = STF_ISP_PAD_SRC_RAW; i < STF_ISP_PAD_MAX; i++) { + struct v4l2_subdev_format fmt = { 0 }; + + fmt.which = sel->which; + fmt.pad = i; + ret = isp_get_format(sd, state, &fmt); + if (ret < 0) + return ret; + + fmt.format.width = rect->width; + fmt.format.height = rect->height; + ret = isp_set_format(sd, state, &fmt); + } + } else { + ret = -EINVAL; + } + + st_info(ST_ISP, "%s pad = %d, left = %d, %d, %d, %d\n", + __func__, sel->pad, sel->r.left, + sel->r.top, sel->r.width, sel->r.height); +out: + return ret; +} + +static int isp_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format = { + .pad = STF_ISP_PAD_SINK, + .which = + fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE, + .format = { + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .width = 1920, + .height = 1080 + } + }; + + return isp_set_format(sd, fh ? fh->state : NULL, &format); +} + +static int isp_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + if (flags & MEDIA_LNK_FL_ENABLED) + if (media_pad_remote_pad_first(local)) + return -EBUSY; + return 0; +} + +static int stf_isp_load_setfile(struct stf_isp_dev *isp_dev, char *file_name) +{ + struct device *dev = isp_dev->stfcamss->dev; + const struct firmware *fw; + u8 *buf = NULL; + int *regval_num; + int ret; + + st_debug(ST_ISP, "%s, file_name %s\n", __func__, file_name); + ret = request_firmware(&fw, file_name, dev); + if (ret < 0) { + st_err(ST_ISP, "firmware request failed (%d)\n", ret); + return ret; + } + buf = devm_kzalloc(dev, fw->size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + memcpy(buf, fw->data, fw->size); + + mutex_lock(&isp_dev->setfile_lock); + if (isp_dev->setfile.state == 1) + devm_kfree(dev, isp_dev->setfile.data); + isp_dev->setfile.data = buf; + isp_dev->setfile.size = fw->size; + isp_dev->setfile.state = 1; + regval_num = (int *)&buf[fw->size - sizeof(unsigned int)]; + isp_dev->setfile.settings.regval_num = *regval_num; + isp_dev->setfile.settings.regval = (struct regval_t *)buf; + mutex_unlock(&isp_dev->setfile_lock); + + st_debug(ST_ISP, "stf_isp setfile loaded size: %zu B, reg_nul: %d\n", + fw->size, isp_dev->setfile.settings.regval_num); + + release_firmware(fw); + return ret; +} + +static long stf_isp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + struct stfisp_fw_info *fw_info = arg; + int ret = -ENOIOCTLCMD; + + switch (cmd) { + case VIDIOC_STFISP_LOAD_FW: + if (IS_ERR(fw_info)) { + st_err(ST_ISP, "fw_info failed, params invalid\n"); + return -EINVAL; + } + + ret = stf_isp_load_setfile(isp_dev, fw_info->filename); + break; + + case VIDIOC_STFISP_GET_REG: + ret = isp_dev->hw_ops->isp_reg_read(isp_dev, arg); + break; + + case VIDIOC_STFISP_SET_REG: + ret = isp_dev->hw_ops->isp_reg_write(isp_dev, arg); + break; + + case VIDIOC_STFISP_SHADOW_LOCK: + if (atomic_add_unless(&isp_dev->shadow_count, 1, 1)) + ret = 0; + else + ret = -EBUSY; + break; + + case VIDIOC_STFISP_SHADOW_UNLOCK: + if (atomic_dec_if_positive(&isp_dev->shadow_count) < 0) + ret = -EINVAL; + else + ret = 0; + break; + + case VIDIOC_STFISP_SHADOW_UNLOCK_N_TRIGGER: + isp_dev->hw_ops->isp_shadow_trigger(isp_dev); + if (atomic_dec_if_positive(&isp_dev->shadow_count) < 0) + ret = -EINVAL; + else + ret = 0; + break; + + case VIDIOC_STFISP_SET_USER_CONFIG_ISP: + st_debug(ST_ISP, "Set user config isp\n"); + user_config_isp = 1; + break; + + default: + break; + } + return ret; +} + +int isp_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + + while (atomic_dec_if_positive(&isp_dev->shadow_count) > 0) + st_warn(ST_ISP, "unlock the shadow lock!\n"); + + return 0; +} + +static int stf_isp_subscribe_event(struct v4l2_subdev *sd, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); + default: + st_debug(ST_ISP, "unspport subscribe_event\n"); + return -EINVAL; + } +} + +static const struct v4l2_subdev_core_ops isp_core_ops = { + .s_power = isp_set_power, + .ioctl = stf_isp_ioctl, + .subscribe_event = stf_isp_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops isp_video_ops = { + .s_stream = isp_set_stream, +}; + +static const struct v4l2_subdev_pad_ops isp_pad_ops = { + .enum_mbus_code = isp_enum_mbus_code, + .enum_frame_size = isp_enum_frame_size, + .get_fmt = isp_get_format, + .set_fmt = isp_set_format, + .get_selection = isp_get_selection, + .set_selection = isp_set_selection, +}; + +static const struct v4l2_subdev_ops isp_v4l2_ops = { + .core = &isp_core_ops, + .video = &isp_video_ops, + .pad = &isp_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops isp_v4l2_internal_ops = { + .open = isp_init_formats, + .close = isp_close, +}; + +static const struct media_entity_operations isp_media_ops = { + .link_setup = isp_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; + +int stf_isp_register(struct stf_isp_dev *isp_dev, struct v4l2_device *v4l2_dev) +{ + struct v4l2_subdev *sd = &isp_dev->subdev; + struct media_pad *pads = isp_dev->pads; + int ret; + + v4l2_subdev_init(sd, &isp_v4l2_ops); + sd->internal_ops = &isp_v4l2_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", STF_ISP_NAME, 0); + v4l2_set_subdevdata(sd, isp_dev); + + ret = isp_init_formats(sd, NULL); + if (ret < 0) { + st_err(ST_ISP, "Failed to init format: %d\n", ret); + return ret; + } + + pads[STF_ISP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[STF_ISP_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; + pads[STF_ISP_PAD_SRC_SS0].flags = MEDIA_PAD_FL_SOURCE; + pads[STF_ISP_PAD_SRC_SS1].flags = MEDIA_PAD_FL_SOURCE; + pads[STF_ISP_PAD_SRC_RAW].flags = MEDIA_PAD_FL_SOURCE; + + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; + sd->entity.ops = &isp_media_ops; + ret = media_entity_pads_init(&sd->entity, STF_ISP_PAD_MAX, pads); + if (ret < 0) { + st_err(ST_ISP, "Failed to init media entity: %d\n", ret); + return ret; + } + + ret = v4l2_device_register_subdev(v4l2_dev, sd); + if (ret < 0) { + st_err(ST_ISP, "Failed to register subdev: %d\n", ret); + goto err_sreg; + } + + return 0; + +err_sreg: + media_entity_cleanup(&sd->entity); + return ret; +} + +int stf_isp_unregister(struct stf_isp_dev *isp_dev) +{ + v4l2_device_unregister_subdev(&isp_dev->subdev); + media_entity_cleanup(&isp_dev->subdev.entity); + mutex_destroy(&isp_dev->stream_lock); + mutex_destroy(&isp_dev->power_lock); + mutex_destroy(&isp_dev->setfile_lock); + return 0; +} diff --git a/drivers/media/platform/starfive/stf_isp.h b/drivers/media/platform/starfive/stf_isp.h new file mode 100644 index 0000000000000..ca80b8d1f1cc1 --- /dev/null +++ b/drivers/media/platform/starfive/stf_isp.h @@ -0,0 +1,183 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * stf_isp.h + * + * StarFive Camera Subsystem - ISP Module + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ + +#ifndef STF_ISP_H +#define STF_ISP_H + +#include +#include + +#define STF_ISP_SETFILE "stf_isp0_fw.bin" + +#define ISP_RAW_DATA_BITS 12 +#define SCALER_RATIO_MAX 1 +#define STF_ISP_REG_OFFSET_MAX 0x0FFF +#define STF_ISP_REG_DELAY_MAX 100 + +/* isp registers */ +#define ISP_REG_CSI_INPUT_EN_AND_STATUS 0x00000000 +#define ISP_REG_CSIINTS_ADDR 0x00000008 +#define ISP_REG_CSI_MODULE_CFG 0x00000010 +#define ISP_REG_SENSOR 0x00000014 +#define ISP_REG_RAW_FORMAT_CFG 0x00000018 +#define ISP_REG_PIC_CAPTURE_START_CFG 0x0000001C +#define ISP_REG_PIC_CAPTURE_END_CFG 0x00000020 +#define ISP_REG_DUMP_CFG_0 0x00000024 +#define ISP_REG_DUMP_CFG_1 0x00000028 +#define ISP_REG_SCD_CFG_0 0x00000098 +#define ISP_REG_SCD_CFG_1 0x0000009C +#define ISP_REG_SC_CFG_1 0x000000BC +#define ISP_REG_ISP_CTRL_0 0x00000A00 +#define ISP_REG_ISP_CTRL_1 0x00000A08 +#define ISP_REG_PIPELINE_XY_SIZE 0x00000A0C +#define ISP_REG_IESHD_ADDR 0x00000A50 +#define ISP_REG_Y_PLANE_START_ADDR 0x00000A80 +#define ISP_REG_UV_PLANE_START_ADDR 0x00000A84 +#define ISP_REG_STRIDE 0x00000A88 +#define ISP_REG_PIXEL_COORDINATE_GEN 0x00000A8C +#define ISP_REG_SS0AY 0x00000A94 +#define ISP_REG_SS0AUV 0x00000A98 +#define ISP_REG_SS0S 0x00000A9C +#define ISP_REG_SS0IW 0x00000AA8 +#define ISP_REG_SS1AY 0x00000AAC +#define ISP_REG_SS1AUV 0x00000AB0 +#define ISP_REG_SS1S 0x00000AB4 +#define ISP_REG_SS1IW 0x00000AC0 +#define ISP_REG_YHIST_CFG_4 0x00000CD8 +#define ISP_REG_ITIIWSR 0x00000B20 +#define ISP_REG_ITIDWLSR 0x00000B24 +#define ISP_REG_ITIDWYSAR 0x00000B28 +#define ISP_REG_ITIDWUSAR 0x00000B2C +#define ISP_REG_ITIDRYSAR 0x00000B30 +#define ISP_REG_ITIDRUSAR 0x00000B34 +#define ISP_REG_ITIPDFR 0x00000B38 +#define ISP_REG_ITIDRLSR 0x00000B3C +#define ISP_REG_ITIBSR 0x00000B40 +#define ISP_REG_ITIAIR 0x00000B44 +#define ISP_REG_ITIDPSR 0x00000B48 + +/* The output line of ISP */ +enum isp_line_id { + STF_ISP_LINE_INVALID = -1, + STF_ISP_LINE_SRC = 1, + STF_ISP_LINE_SRC_SS0, + STF_ISP_LINE_SRC_SS1, + STF_ISP_LINE_SRC_RAW, + STF_ISP_LINE_MAX = STF_ISP_LINE_SRC_RAW +}; + +/* pad id for media framework */ +enum isp_pad_id { + STF_ISP_PAD_SINK = 0, + STF_ISP_PAD_SRC, + STF_ISP_PAD_SRC_SS0, + STF_ISP_PAD_SRC_SS1, + STF_ISP_PAD_SRC_RAW, + STF_ISP_PAD_MAX +}; + +enum { + EN_INT_NONE = 0, + EN_INT_ISP_DONE = (0x1 << 24), + EN_INT_CSI_DONE = (0x1 << 25), + EN_INT_SC_DONE = (0x1 << 26), + EN_INT_LINE_INT = (0x1 << 27), + EN_INT_ALL = (0xF << 24), +}; + +enum { + INTERFACE_DVP = 0, + INTERFACE_CSI, +}; + +struct isp_format { + u32 code; + u8 bpp; +}; + +struct isp_format_table { + const struct isp_format *fmts; + int nfmts; +}; + +struct regval_t { + u32 addr; + u32 val; + u32 mask; + u32 delay_ms; +}; + +struct reg_table { + const struct regval_t *regval; + int regval_num; +}; + +struct isp_stream_format { + struct v4l2_rect rect; + u32 bpp; +}; + +struct isp_setfile { + struct reg_table settings; + const u8 *data; + unsigned int size; + unsigned int state; +}; + +enum { + ISP_CROP = 0, + ISP_COMPOSE, + ISP_SCALE_SS0, + ISP_SCALE_SS1, + ISP_RECT_MAX +}; + +struct stf_isp_dev { + enum subdev_type sdev_type; /* This member must be first */ + struct stfcamss *stfcamss; + struct v4l2_subdev subdev; + struct media_pad pads[STF_ISP_PAD_MAX]; + struct v4l2_mbus_framefmt fmt[STF_ISP_PAD_MAX]; + struct isp_stream_format rect[ISP_RECT_MAX]; + const struct isp_format_table *formats; + unsigned int nformats; + const struct isp_hw_ops *hw_ops; + struct mutex power_lock; /* serialize power control*/ + int power_count; + struct mutex stream_lock; /* serialize stream control */ + int stream_count; + atomic_t shadow_count; + + struct mutex setfile_lock; /* protects setting files */ + struct isp_setfile setfile; + + union reg_buf *reg_buf; +}; + +struct isp_hw_ops { + int (*isp_clk_enable)(struct stf_isp_dev *isp_dev); + int (*isp_clk_disable)(struct stf_isp_dev *isp_dev); + int (*isp_reset)(struct stf_isp_dev *isp_dev); + int (*isp_config_set)(struct stf_isp_dev *isp_dev); + int (*isp_set_format)(struct stf_isp_dev *isp_dev, + struct isp_stream_format *crop, + u32 mcode, int type); + int (*isp_stream_set)(struct stf_isp_dev *isp_dev, int on); + int (*isp_reg_read)(struct stf_isp_dev *isp_dev, void *arg); + int (*isp_reg_write)(struct stf_isp_dev *isp_dev, void *arg); + int (*isp_shadow_trigger)(struct stf_isp_dev *isp_dev); +}; + +extern const struct isp_hw_ops isp_ops; + +int stf_isp_subdev_init(struct stfcamss *stfcamss); +int stf_isp_register(struct stf_isp_dev *isp_dev, struct v4l2_device *v4l2_dev); +int stf_isp_unregister(struct stf_isp_dev *isp_dev); + +#endif /* STF_ISP_H */ diff --git a/drivers/media/platform/starfive/stf_isp_hw_ops.c b/drivers/media/platform/starfive/stf_isp_hw_ops.c new file mode 100644 index 0000000000000..b2aee00a78ad3 --- /dev/null +++ b/drivers/media/platform/starfive/stf_isp_hw_ops.c @@ -0,0 +1,1286 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * stf_isp_hw_ops.c + * + * Register interface file for StarFive ISP driver + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + * + */ +#include +#include + +#include "stf_camss.h" + +static const struct regval_t isp_reg_init_config_list[] = { + /* config DC */ + {0x00000044, 0x00000000, 0, 0}, + /* config DEC */ + {0x00000030, 0x00000000, 0, 0}, + /* config OBC */ + {0x00000034, 0x000000BB, 0, 0}, + {0x000002E0, 0x40404040, 0, 0}, + {0x000002E4, 0x40404040, 0, 0}, + {0x000002E8, 0x40404040, 0, 0}, + {0x000002EC, 0x40404040, 0, 0}, + {0x000002F0, 0x00000000, 0, 0}, + {0x000002F4, 0x00000000, 0, 0}, + {0x000002F8, 0x00000000, 0, 0}, + {0x000002FC, 0x00000000, 0, 0}, + /* config LCBQ */ + {0x00000074, 0x00009900, 0, 0}, + {0x0000007C, 0x01E40040, 0, 0}, + {0x00000300, 0x01000100, 0, 0}, + {0x00000304, 0x01000100, 0, 0}, + {0x00000308, 0x01000100, 0, 0}, + {0x0000030C, 0x01000100, 0, 0}, + {0x00000310, 0x01000100, 0, 0}, + {0x00000314, 0x01000100, 0, 0}, + {0x00000318, 0x01000100, 0, 0}, + {0x0000031C, 0x01000100, 0, 0}, + {0x00000320, 0x01000100, 0, 0}, + {0x00000324, 0x01000100, 0, 0}, + {0x00000328, 0x01000100, 0, 0}, + {0x0000032C, 0x01000100, 0, 0}, + {0x00000330, 0x00000100, 0, 0}, + {0x00000334, 0x01000100, 0, 0}, + {0x00000338, 0x01000100, 0, 0}, + {0x0000033C, 0x01000100, 0, 0}, + {0x00000340, 0x01000100, 0, 0}, + {0x00000344, 0x01000100, 0, 0}, + {0x00000348, 0x01000100, 0, 0}, + {0x0000034C, 0x01000100, 0, 0}, + {0x00000350, 0x01000100, 0, 0}, + {0x00000354, 0x01000100, 0, 0}, + {0x00000358, 0x01000100, 0, 0}, + {0x0000035C, 0x01000100, 0, 0}, + {0x00000360, 0x01000100, 0, 0}, + {0x00000364, 0x00000100, 0, 0}, + {0x00000368, 0x01000100, 0, 0}, + {0x0000036C, 0x01000100, 0, 0}, + {0x00000370, 0x01000100, 0, 0}, + {0x00000374, 0x01000100, 0, 0}, + {0x00000378, 0x01000100, 0, 0}, + {0x0000037C, 0x01000100, 0, 0}, + {0x00000380, 0x01000100, 0, 0}, + {0x00000384, 0x01000100, 0, 0}, + {0x00000388, 0x01000100, 0, 0}, + {0x0000038C, 0x01000100, 0, 0}, + {0x00000390, 0x01000100, 0, 0}, + {0x00000394, 0x01000100, 0, 0}, + {0x00000398, 0x00000100, 0, 0}, + {0x0000039C, 0x01000100, 0, 0}, + {0x000003A0, 0x01000100, 0, 0}, + {0x000003A4, 0x01000100, 0, 0}, + {0x000003A8, 0x01000100, 0, 0}, + {0x000003AC, 0x01000100, 0, 0}, + {0x000003B0, 0x01000100, 0, 0}, + {0x000003B4, 0x01000100, 0, 0}, + {0x000003B8, 0x01000100, 0, 0}, + {0x000003BC, 0x01000100, 0, 0}, + {0x000003C0, 0x01000100, 0, 0}, + {0x000003C4, 0x01000100, 0, 0}, + {0x000003C8, 0x01000100, 0, 0}, + {0x000003CC, 0x00000100, 0, 0}, + {0x00000400, 0x00000000, 0, 0}, + {0x00000404, 0x00000000, 0, 0}, + {0x00000408, 0x00000000, 0, 0}, + {0x0000040C, 0x00000000, 0, 0}, + {0x00000410, 0x00000000, 0, 0}, + {0x00000414, 0x00000000, 0, 0}, + {0x00000418, 0x00000000, 0, 0}, + {0x0000041C, 0x00000000, 0, 0}, + {0x00000420, 0x00000000, 0, 0}, + {0x00000424, 0x00000000, 0, 0}, + {0x00000428, 0x00000000, 0, 0}, + {0x0000042C, 0x00000000, 0, 0}, + {0x00000430, 0x00000000, 0, 0}, + {0x00000434, 0x00000000, 0, 0}, + {0x00000438, 0x00000000, 0, 0}, + {0x0000043C, 0x00000000, 0, 0}, + {0x00000440, 0x00000000, 0, 0}, + {0x00000444, 0x00000000, 0, 0}, + {0x00000448, 0x00000000, 0, 0}, + {0x0000044C, 0x00000000, 0, 0}, + {0x00000450, 0x00000000, 0, 0}, + {0x00000454, 0x00000000, 0, 0}, + {0x00000458, 0x00000000, 0, 0}, + {0x0000045C, 0x00000000, 0, 0}, + {0x00000460, 0x00000000, 0, 0}, + {0x00000464, 0x00000000, 0, 0}, + {0x00000468, 0x00000000, 0, 0}, + {0x0000046C, 0x00000000, 0, 0}, + {0x00000470, 0x00000000, 0, 0}, + {0x00000474, 0x00000000, 0, 0}, + {0x00000478, 0x00000000, 0, 0}, + {0x0000047C, 0x00000000, 0, 0}, + {0x00000480, 0x00000000, 0, 0}, + {0x00000484, 0x00000000, 0, 0}, + {0x00000488, 0x00000000, 0, 0}, + {0x0000048C, 0x00000000, 0, 0}, + {0x00000490, 0x00000000, 0, 0}, + {0x00000494, 0x00000000, 0, 0}, + {0x00000498, 0x00000000, 0, 0}, + {0x0000049C, 0x00000000, 0, 0}, + {0x000004A0, 0x00000000, 0, 0}, + {0x000004A4, 0x00000000, 0, 0}, + {0x000004A8, 0x00000000, 0, 0}, + {0x000004AC, 0x00000000, 0, 0}, + {0x000004B0, 0x00000000, 0, 0}, + {0x000004B4, 0x00000000, 0, 0}, + {0x000004B8, 0x00000000, 0, 0}, + {0x000004BC, 0x00000000, 0, 0}, + {0x000004C0, 0x00000000, 0, 0}, + {0x000004C4, 0x00000000, 0, 0}, + {0x000004C8, 0x00000000, 0, 0}, + {0x000004CC, 0x00000000, 0, 0}, + /* config OECF */ + {0x00000100, 0x00100000, 0, 0}, + {0x00000104, 0x00400020, 0, 0}, + {0x00000108, 0x00800060, 0, 0}, + {0x0000010C, 0x00C000A0, 0, 0}, + {0x00000110, 0x010000E0, 0, 0}, + {0x00000114, 0x02000180, 0, 0}, + {0x00000118, 0x03000280, 0, 0}, + {0x0000011C, 0x03FE0380, 0, 0}, + {0x00000120, 0x00100000, 0, 0}, + {0x00000124, 0x00400020, 0, 0}, + {0x00000128, 0x00800060, 0, 0}, + {0x0000012C, 0x00C000A0, 0, 0}, + {0x00000130, 0x010000E0, 0, 0}, + {0x00000134, 0x02000180, 0, 0}, + {0x00000138, 0x03000280, 0, 0}, + {0x0000013C, 0x03FE0380, 0, 0}, + {0x00000140, 0x00100000, 0, 0}, + {0x00000144, 0x00400020, 0, 0}, + {0x00000148, 0x00800060, 0, 0}, + {0x0000014C, 0x00C000A0, 0, 0}, + {0x00000150, 0x010000E0, 0, 0}, + {0x00000154, 0x02000180, 0, 0}, + {0x00000158, 0x03000280, 0, 0}, + {0x0000015C, 0x03FE0380, 0, 0}, + {0x00000160, 0x00100000, 0, 0}, + {0x00000164, 0x00400020, 0, 0}, + {0x00000168, 0x00800060, 0, 0}, + {0x0000016C, 0x00C000A0, 0, 0}, + {0x00000170, 0x010000E0, 0, 0}, + {0x00000174, 0x02000180, 0, 0}, + {0x00000178, 0x03000280, 0, 0}, + {0x0000017C, 0x03FE0380, 0, 0}, + {0x00000180, 0x00100000, 0, 0}, + {0x00000184, 0x00400020, 0, 0}, + {0x00000188, 0x00800060, 0, 0}, + {0x0000018C, 0x00C000A0, 0, 0}, + {0x00000190, 0x010000E0, 0, 0}, + {0x00000194, 0x02000180, 0, 0}, + {0x00000198, 0x03000280, 0, 0}, + {0x0000019C, 0x03FE0380, 0, 0}, + {0x000001A0, 0x00100000, 0, 0}, + {0x000001A4, 0x00400020, 0, 0}, + {0x000001A8, 0x00800060, 0, 0}, + {0x000001AC, 0x00C000A0, 0, 0}, + {0x000001B0, 0x010000E0, 0, 0}, + {0x000001B4, 0x02000180, 0, 0}, + {0x000001B8, 0x03000280, 0, 0}, + {0x000001BC, 0x03FE0380, 0, 0}, + {0x000001C0, 0x00100000, 0, 0}, + {0x000001C4, 0x00400020, 0, 0}, + {0x000001C8, 0x00800060, 0, 0}, + {0x000001CC, 0x00C000A0, 0, 0}, + {0x000001D0, 0x010000E0, 0, 0}, + {0x000001D4, 0x02000180, 0, 0}, + {0x000001D8, 0x03000280, 0, 0}, + {0x000001DC, 0x03FE0380, 0, 0}, + {0x000001E0, 0x00100000, 0, 0}, + {0x000001E4, 0x00400020, 0, 0}, + {0x000001E8, 0x00800060, 0, 0}, + {0x000001EC, 0x00C000A0, 0, 0}, + {0x000001F0, 0x010000E0, 0, 0}, + {0x000001F4, 0x02000180, 0, 0}, + {0x000001F8, 0x03000280, 0, 0}, + {0x000001FC, 0x03FE0380, 0, 0}, + {0x00000200, 0x00800080, 0, 0}, + {0x00000204, 0x00800080, 0, 0}, + {0x00000208, 0x00800080, 0, 0}, + {0x0000020C, 0x00800080, 0, 0}, + {0x00000210, 0x00800080, 0, 0}, + {0x00000214, 0x00800080, 0, 0}, + {0x00000218, 0x00800080, 0, 0}, + {0x0000021C, 0x00800080, 0, 0}, + {0x00000220, 0x00800080, 0, 0}, + {0x00000224, 0x00800080, 0, 0}, + {0x00000228, 0x00800080, 0, 0}, + {0x0000022C, 0x00800080, 0, 0}, + {0x00000230, 0x00800080, 0, 0}, + {0x00000234, 0x00800080, 0, 0}, + {0x00000238, 0x00800080, 0, 0}, + {0x0000023C, 0x00800080, 0, 0}, + {0x00000240, 0x00800080, 0, 0}, + {0x00000244, 0x00800080, 0, 0}, + {0x00000248, 0x00800080, 0, 0}, + {0x0000024C, 0x00800080, 0, 0}, + {0x00000250, 0x00800080, 0, 0}, + {0x00000254, 0x00800080, 0, 0}, + {0x00000258, 0x00800080, 0, 0}, + {0x0000025C, 0x00800080, 0, 0}, + {0x00000260, 0x00800080, 0, 0}, + {0x00000264, 0x00800080, 0, 0}, + {0x00000268, 0x00800080, 0, 0}, + {0x0000026C, 0x00800080, 0, 0}, + {0x00000270, 0x00800080, 0, 0}, + {0x00000274, 0x00800080, 0, 0}, + {0x00000278, 0x00800080, 0, 0}, + {0x0000027C, 0x00800080, 0, 0}, + /* config OECFHM */ + {0x000003D0, 0x04000000, 0, 0}, + {0x000003D4, 0x0C000800, 0, 0}, + {0x000003D8, 0x00000FFF, 0, 0}, + {0x000003DC, 0x08000800, 0, 0}, + {0x000003E0, 0x08000800, 0, 0}, + {0x000003E4, 0x00000800, 0, 0}, + /* config LCCF */ + {0x00000050, 0x021C03C0, 0, 0}, + {0x00000058, 0x0000000B, 0, 0}, + {0x000000E0, 0x00000000, 0, 0}, + {0x000000E4, 0x00000000, 0, 0}, + {0x000000E8, 0x00000000, 0, 0}, + {0x000000EC, 0x00000000, 0, 0}, + /* config AWB */ + {0x00000280, 0x00000000, 0, 0}, + {0x00000284, 0x00000000, 0, 0}, + {0x00000288, 0x00000000, 0, 0}, + {0x0000028C, 0x00000000, 0, 0}, + {0x00000290, 0x00000000, 0, 0}, + {0x00000294, 0x00000000, 0, 0}, + {0x00000298, 0x00000000, 0, 0}, + {0x0000029C, 0x00000000, 0, 0}, + {0x000002A0, 0x00000000, 0, 0}, + {0x000002A4, 0x00000000, 0, 0}, + {0x000002A8, 0x00000000, 0, 0}, + {0x000002AC, 0x00000000, 0, 0}, + {0x000002B0, 0x00000000, 0, 0}, + {0x000002B4, 0x00000000, 0, 0}, + {0x000002B8, 0x00000000, 0, 0}, + {0x000002BC, 0x00000000, 0, 0}, + {0x000002C0, 0x00800080, 0, 0}, + {0x000002C4, 0x00800080, 0, 0}, + {0x000002C8, 0x00800080, 0, 0}, + {0x000002CC, 0x00800080, 0, 0}, + {0x000002D0, 0x00800080, 0, 0}, + {0x000002D4, 0x00800080, 0, 0}, + {0x000002D8, 0x00800080, 0, 0}, + {0x000002DC, 0x00800080, 0, 0}, + /* config CTC and DBC filter */ + {0x00000A10, 0x41400040, 0, 0}, + {0x00000A14, 0x02000200, 0, 0}, + /* config CFA */ + {0x00000018, 0x000011BB, 0, 0}, + {0x00000A1C, 0x00000032, 0, 0}, + /* config CCM */ + {0x00000C40, 0x00060000, 0, 0}, + {0x00000C44, 0x00000000, 0, 0}, + {0x00000C48, 0x00000000, 0, 0}, + {0x00000C4C, 0x00000000, 0, 0}, + {0x00000C50, 0x00000000, 0, 0}, + {0x00000C54, 0x00000000, 0, 0}, + {0x00000C58, 0x00000000, 0, 0}, + {0x00000C5C, 0x00000000, 0, 0}, + {0x00000C60, 0x00000000, 0, 0}, + {0x00000C64, 0x00000000, 0, 0}, + {0x00000C68, 0x00000000, 0, 0}, + {0x00000C6C, 0x00000000, 0, 0}, + {0x00000C70, 0x00000080, 0, 0}, + {0x00000C74, 0x00000000, 0, 0}, + {0x00000C78, 0x00000000, 0, 0}, + {0x00000C7C, 0x00000000, 0, 0}, + {0x00000C80, 0x00000080, 0, 0}, + {0x00000C84, 0x00000000, 0, 0}, + {0x00000C88, 0x00000000, 0, 0}, + {0x00000C8C, 0x00000000, 0, 0}, + {0x00000C90, 0x00000080, 0, 0}, + {0x00000C94, 0x00000000, 0, 0}, + {0x00000C98, 0x00000000, 0, 0}, + {0x00000C9C, 0x00000000, 0, 0}, + {0x00000CA0, 0x00000700, 0, 0}, + {0x00000CA4, 0x00000200, 0, 0}, + /* config GMARGB */ + {0x00000E00, 0x24000000, 0, 0}, + {0x00000E04, 0x08000020, 0, 0}, + {0x00000E08, 0x08000040, 0, 0}, + {0x00000E0C, 0x08000060, 0, 0}, + {0x00000E10, 0x08000080, 0, 0}, + {0x00000E14, 0x080000A0, 0, 0}, + {0x00000E18, 0x080000C0, 0, 0}, + {0x00000E1C, 0x080000E0, 0, 0}, + {0x00000E20, 0x08000100, 0, 0}, + {0x00000E24, 0x08000180, 0, 0}, + {0x00000E28, 0x08000200, 0, 0}, + {0x00000E2C, 0x08000280, 0, 0}, + {0x00000E30, 0x08000300, 0, 0}, + {0x00000E34, 0x08000380, 0, 0}, + {0x00000E38, 0x080003FE, 0, 0}, + /* config R2Y */ + {0x00000E40, 0x0000004C, 0, 0}, + {0x00000E44, 0x00000097, 0, 0}, + {0x00000E48, 0x0000001D, 0, 0}, + {0x00000E4C, 0x000001D5, 0, 0}, + {0x00000E50, 0x000001AC, 0, 0}, + {0x00000E54, 0x00000080, 0, 0}, + {0x00000E58, 0x00000080, 0, 0}, + {0x00000E5C, 0x00000194, 0, 0}, + {0x00000E60, 0x000001EC, 0, 0}, + /* config YCRV */ + {0x00000F00, 0x00000000, 0, 0}, + {0x00000F04, 0x00000010, 0, 0}, + {0x00000F08, 0x00000020, 0, 0}, + {0x00000F0C, 0x00000030, 0, 0}, + {0x00000F10, 0x00000040, 0, 0}, + {0x00000F14, 0x00000050, 0, 0}, + {0x00000F18, 0x00000060, 0, 0}, + {0x00000F1C, 0x00000070, 0, 0}, + {0x00000F20, 0x00000080, 0, 0}, + {0x00000F24, 0x00000090, 0, 0}, + {0x00000F28, 0x000000A0, 0, 0}, + {0x00000F2C, 0x000000B0, 0, 0}, + {0x00000F30, 0x000000C0, 0, 0}, + {0x00000F34, 0x000000D0, 0, 0}, + {0x00000F38, 0x000000E0, 0, 0}, + {0x00000F3C, 0x000000F0, 0, 0}, + {0x00000F40, 0x00000100, 0, 0}, + {0x00000F44, 0x00000110, 0, 0}, + {0x00000F48, 0x00000120, 0, 0}, + {0x00000F4C, 0x00000130, 0, 0}, + {0x00000F50, 0x00000140, 0, 0}, + {0x00000F54, 0x00000150, 0, 0}, + {0x00000F58, 0x00000160, 0, 0}, + {0x00000F5C, 0x00000170, 0, 0}, + {0x00000F60, 0x00000180, 0, 0}, + {0x00000F64, 0x00000190, 0, 0}, + {0x00000F68, 0x000001A0, 0, 0}, + {0x00000F6C, 0x000001B0, 0, 0}, + {0x00000F70, 0x000001C0, 0, 0}, + {0x00000F74, 0x000001D0, 0, 0}, + {0x00000F78, 0x000001E0, 0, 0}, + {0x00000F7C, 0x000001F0, 0, 0}, + {0x00000F80, 0x00000200, 0, 0}, + {0x00000F84, 0x00000210, 0, 0}, + {0x00000F88, 0x00000220, 0, 0}, + {0x00000F8C, 0x00000230, 0, 0}, + {0x00000F90, 0x00000240, 0, 0}, + {0x00000F94, 0x00000250, 0, 0}, + {0x00000F98, 0x00000260, 0, 0}, + {0x00000F9C, 0x00000270, 0, 0}, + {0x00000FA0, 0x00000280, 0, 0}, + {0x00000FA4, 0x00000290, 0, 0}, + {0x00000FA8, 0x000002A0, 0, 0}, + {0x00000FAC, 0x000002B0, 0, 0}, + {0x00000FB0, 0x000002C0, 0, 0}, + {0x00000FB4, 0x000002D0, 0, 0}, + {0x00000FB8, 0x000002E0, 0, 0}, + {0x00000FBC, 0x000002F0, 0, 0}, + {0x00000FC0, 0x00000300, 0, 0}, + {0x00000FC4, 0x00000310, 0, 0}, + {0x00000FC8, 0x00000320, 0, 0}, + {0x00000FCC, 0x00000330, 0, 0}, + {0x00000FD0, 0x00000340, 0, 0}, + {0x00000FD4, 0x00000350, 0, 0}, + {0x00000FD8, 0x00000360, 0, 0}, + {0x00000FDC, 0x00000370, 0, 0}, + {0x00000FE0, 0x00000380, 0, 0}, + {0x00000FE4, 0x00000390, 0, 0}, + {0x00000FE8, 0x000003A0, 0, 0}, + {0x00000FEC, 0x000003B0, 0, 0}, + {0x00000FF0, 0x000003C0, 0, 0}, + {0x00000FF4, 0x000003D0, 0, 0}, + {0x00000FF8, 0x000003E0, 0, 0}, + {0x00000FFC, 0x000003F0, 0, 0}, + /* config Sharp */ + {0x00000E80, 0x00070F00, 0, 0}, + {0x00000E84, 0x00180F00, 0, 0}, + {0x00000E88, 0x00800F00, 0, 0}, + {0x00000E8C, 0x01000F00, 0, 0}, + {0x00000E90, 0x00100F00, 0, 0}, + {0x00000E94, 0x00600F00, 0, 0}, + {0x00000E98, 0x01000F00, 0, 0}, + {0x00000E9C, 0x01900F00, 0, 0}, + {0x00000EA0, 0x00000F00, 0, 0}, + {0x00000EA4, 0x00000F00, 0, 0}, + {0x00000EA8, 0x00000F00, 0, 0}, + {0x00000EAC, 0x00000F00, 0, 0}, + {0x00000EB0, 0x00000F00, 0, 0}, + {0x00000EB4, 0x00000F00, 0, 0}, + {0x00000EB8, 0x00000F00, 0, 0}, + {0x00000EBC, 0x10000000, 0, 0}, + {0x00000EC0, 0x10000000, 0, 0}, + {0x00000EC4, 0x10000000, 0, 0}, + {0x00000EC8, 0x10000000, 0, 0}, + {0x00000ECC, 0x10000000, 0, 0}, + {0x00000ED0, 0x10000000, 0, 0}, + {0x00000ED4, 0x88000D7C, 0, 0}, + {0x00000ED8, 0x00C00040, 0, 0}, + {0x00000EDC, 0xFF000000, 0, 0}, + {0x00000EE0, 0x00A00040, 0, 0}, + {0x00000EE4, 0x00000000, 0, 0}, + {0x00000EE8, 0x00000000, 0, 0}, + /* config DNYUV */ + {0x00000C00, 0x00777777, 0, 0}, + {0x00000C04, 0x00007777, 0, 0}, + {0x00000C08, 0x00777777, 0, 0}, + {0x00000C0C, 0x00007777, 0, 0}, + {0x00000C10, 0x00600040, 0, 0}, + {0x00000C14, 0x00D80090, 0, 0}, + {0x00000C18, 0x01E60144, 0, 0}, + {0x00000C1C, 0x00600040, 0, 0}, + {0x00000C20, 0x00D80090, 0, 0}, + {0x00000C24, 0x01E60144, 0, 0}, + /* config SAT */ + {0x00000A30, 0x00000100, 0, 0}, + {0x00000A34, 0x001F0001, 0, 0}, + {0x00000A38, 0x00000000, 0, 0}, + {0x00000A3C, 0x00000100, 0, 0}, + {0x00000A40, 0x00000008, 0, 0}, + {0x00000A54, 0x04010001, 0, 0}, + {0x00000A58, 0x03FF0001, 0, 0}, + /* config OBA */ + {0x00000090, 0x04380000, 0, 0}, + {0x00000094, 0x04390780, 0, 0}, + /* config SC */ + {0x0000009C, 0x01000000, 0, 0}, + {0x000000B8, 0x000C0000, 0, 0}, + {0x000000BC, 0xC010151D, 0, 0}, + {0x000000C0, 0x01F1BF08, 0, 0}, + {0x000000C4, 0xFF00FF00, 0, 0}, + {0x000000C8, 0xFF00FF00, 0, 0}, + {0x000000CC, 0xFFFF0000, 0, 0}, + {0x000000D0, 0xFFFF0000, 0, 0}, + {0x000000D4, 0xFFFF0000, 0, 0}, + {0x000000D8, 0x01050107, 0, 0}, + {0x000004D0, 0x00000000, 0, 0}, + {0x000004D4, 0x00000000, 0, 0}, + {0x000004D8, 0x00000000, 0, 0}, + {0x000004DC, 0x00000000, 0, 0}, + {0x000004E0, 0x00000000, 0, 0}, + {0x000004E4, 0x00000000, 0, 0}, + {0x000004E8, 0x00000000, 0, 0}, + {0x000004EC, 0x00000000, 0, 0}, + {0x000004F0, 0x00100000, 0, 0}, + {0x000004F4, 0x00000000, 0, 0}, + {0x000004F8, 0x03D20000, 0, 0}, + {0x000004FC, 0x00000000, 0, 0}, + {0x00000500, 0x00950000, 0, 0}, + {0x00000504, 0x00000000, 0, 0}, + {0x00000508, 0x00253000, 0, 0}, + {0x0000050C, 0x00000000, 0, 0}, + {0x00000510, 0x00000000, 0, 0}, + {0x00000514, 0x00000000, 0, 0}, + {0x00000518, 0x00000000, 0, 0}, + {0x0000051C, 0x00000000, 0, 0}, + {0x00000520, 0x00000000, 0, 0}, + {0x00000524, 0x00000000, 0, 0}, + {0x00000528, 0x00000000, 0, 0}, + {0x0000052C, 0x00000000, 0, 0}, + {0x00000530, 0x00000000, 0, 0}, + {0x00000534, 0x00000000, 0, 0}, + {0x00000538, 0xFFFFFFF0, 0, 0}, + {0x0000053C, 0x8FFFFFFF, 0, 0}, + {0x00000540, 0x0000001E, 0, 0}, + {0x00000544, 0x00000000, 0, 0}, + {0x00000548, 0x00000000, 0, 0}, + {0x0000054C, 0xF0F20000, 0, 0}, + {0x000005D0, 0xFF00FF00, 0, 0}, + {0x000005D4, 0xFF00FF00, 0, 0}, + /* config YHIST */ + {0x00000CC8, 0x00000000, 0, 0}, + {0x00000CCC, 0x0437077F, 0, 0}, + {0x00000CD0, 0x00010002, 0, 0}, + {0x00000CD4, 0x00000000, 0, 0}, + /* config CBAR */ + {0x00000600, 0x043E0782, 0, 0}, + {0x00000604, 0x00000000, 0, 0}, + {0x00000608, 0x0437077F, 0, 0}, + {0x0000060C, 0x00443150, 0, 0}, + {0x00000610, 0x00000000, 0, 0}, + {0x00000614, 0x08880888, 0, 0}, + {0x00000618, 0x02220222, 0, 0}, + {0x0000061C, 0x04440444, 0, 0}, + {0x00000620, 0x08880888, 0, 0}, + {0x00000624, 0x0AAA0AAA, 0, 0}, + {0x00000628, 0x0CCC0CCC, 0, 0}, + {0x0000062C, 0x0EEE0EEE, 0, 0}, + {0x00000630, 0x0FFF0FFF, 0, 0}, + {0x00000634, 0x08880888, 0, 0}, + {0x00000638, 0x02220222, 0, 0}, + {0x0000063C, 0x04440444, 0, 0}, + {0x00000640, 0x08880888, 0, 0}, + {0x00000644, 0x0AAA0AAA, 0, 0}, + {0x00000648, 0x0CCC0CCC, 0, 0}, + {0x0000064C, 0x0EEE0EEE, 0, 0}, + {0x00000650, 0x0FFF0FFF, 0, 0}, + /* config sensor */ + {0x00000014, 0x0000000c, 0, 0}, + /* config CROP */ + {0x0000001C, 0x00000000, 0, 0}, + {0x00000020, 0x0437077F, 0, 0}, + /* config isp pileline X/Y size */ + {0x00000A0C, 0x04380780, 0, 0}, + /* config CSI dump */ + {0x00000028, 0x00030B80, 0, 0}, + /* config UO */ + {0x00000A88, 0x00000780, 0, 0}, + /* NV12 */ + {0x00000A8C, 0x00000000, 0, 0}, + /* NV21 + *{0x00000A8C, 0x00000020, 0, 0}, + */ + {0x00000A90, 0x00000000, 0, 0}, + {0x00000A9C, 0x00000780, 0, 0}, + {0x00000AA0, 0x00000002, 0, 0}, + {0x00000AA4, 0x00000002, 0, 0}, + {0x00000AA8, 0x07800438, 0, 0}, + {0x00000AB4, 0x00000780, 0, 0}, + {0x00000AB8, 0x00000002, 0, 0}, + {0x00000ABC, 0x00000002, 0, 0}, + {0x00000AC0, 0x07800438, 0, 0}, + {0x00000AC4, 0x00000000, 0, 0}, + /* config TIL */ + {0x00000B20, 0x04380780, 0, 0}, + {0x00000B24, 0x00000960, 0, 0}, + {0x00000B38, 0x00030003, 0, 0}, + {0x00000B3C, 0x00000960, 0, 0}, + {0x00000B44, 0x00000000, 0, 0}, + {0x00000B48, 0x00000000, 0, 0}, + /* Enable DEC/OBC/OECF/LCCF/AWB/SC/DUMP */ + {0x00000010, 0x000A00D6, 0x00000000, 0x00}, + /* Enable CFA/CAR/CCM/GMARGB/R2Y/SHRP/SAT/DNYUV/YCRV/YHIST/CTC/DBC */ + {0x00000A08, 0x107A01BE, 0x00000000, 0x00}, +}; + +const struct reg_table isp_reg_init_settings[] = { + {isp_reg_init_config_list, ARRAY_SIZE(isp_reg_init_config_list)}, +}; + +static const struct regval_t isp_reg_start_config_list[] = { + /* ENABLE UO/Multi-Frame and Reset ISP */ + {0x00000A00, 0x00120002, 0x00000000, 0x0A}, + /* ENABLE UO/Multi-Frame and Leave ISP reset */ + {0x00000A00, 0x00120000, 0x00000000, 0x0A}, + /* Config ISP shadow mode as next-vsync */ + {0x00000A50, 0x00000002, 0x00000000, 0x00}, + /* ENABLE UO/Multi-Frame and Enable ISP */ + {0x00000A00, 0x00120001, 0x00000000, 0x0A}, + /* Config CSI shadow mode as immediate to fetch current setting */ + {0x00000008, 0x00010004, 0x00000000, 0x0A}, + /* Config CSI shadow mode as next-vsync */ + {0x00000008, 0x00020004, 0x00000000, 0x00}, + /* Enable CSI */ + {0x00000000, 0x00000001, 0x00000000, 0x0A}, +}; + +const struct reg_table isp_reg_start_settings[] = { + {isp_reg_start_config_list, ARRAY_SIZE(isp_reg_start_config_list)}, +}; + +static struct regval_t isp_format_reg_list[] = { + {0x0000001C, 0x00000000, 0x00000000, 0}, + {0x00000020, 0x0437077F, 0x00000000, 0}, + {0x00000A0C, 0x04380780, 0x00000000, 0}, + {0x00000A88, 0x00000780, 0x00000000, 0}, + {0x00000018, 0x000011BB, 0x00000000, 0}, + {0x00000A08, 0x10000000, 0xF0000000, 0}, + {0x00000028, 0x00030B80, 0x0003FFFF, 0}, + {0x00000AA8, 0x07800438, 0x00000000, 0}, + {0x00000A9C, 0x00000780, 0x00000000, 0}, + {0x00000AC0, 0x07800438, 0x00000000, 0}, + {0x00000AB4, 0x00000780, 0x00000000, 0}, + {0x00000B20, 0x04380780, 0x00000000, 0}, + {0x00000B24, 0x00000960, 0x00000000, 0}, + {0x00000B3C, 0x00000960, 0x00000000, 0}, + {0x00000014, 0x00000008, 0x00000000, 0}, +}; + +const struct reg_table isp_format_settings[] = { + {isp_format_reg_list, ARRAY_SIZE(isp_format_reg_list)}, +}; + +static void isp_load_regs(void __iomem *ispbase, const struct reg_table *table) +{ + int j; + u32 delay_ms, reg_addr, mask, val; + + for (j = 0; j < table->regval_num; j++) { + delay_ms = table->regval[j].delay_ms; + reg_addr = table->regval[j].addr; + val = table->regval[j].val; + mask = table->regval[j].mask; + + if (reg_addr % 4 || + reg_addr > STF_ISP_REG_OFFSET_MAX || + delay_ms > STF_ISP_REG_DELAY_MAX) + continue; + + if (mask) + reg_set_bit(ispbase, reg_addr, mask, val); + else + reg_write(ispbase, reg_addr, val); + + if (delay_ms) + usleep_range(1000 * delay_ms, 1000 * delay_ms + 100); + } +} + +static void isp_load_regs_exclude_csi_isp_enable(void __iomem *ispbase, + const struct reg_table *table) +{ + int j; + u32 delay_ms, reg_addr, mask, val; + + for (j = 0; j < table->regval_num; j++) { + delay_ms = table->regval[j].delay_ms; + reg_addr = table->regval[j].addr; + val = table->regval[j].val; + mask = table->regval[j].mask; + + if (reg_addr % 4 || + reg_addr > STF_ISP_REG_OFFSET_MAX || + delay_ms > STF_ISP_REG_DELAY_MAX || + (reg_addr == ISP_REG_CSI_INPUT_EN_AND_STATUS && (val & 0x01)) || + (reg_addr == ISP_REG_ISP_CTRL_0 && (val & 0x01))) + continue; + + if (mask) + reg_set_bit(ispbase, reg_addr, mask, val); + else + reg_write(ispbase, reg_addr, val); + if (delay_ms) + usleep_range(1000 * delay_ms, 1000 * delay_ms + 100); + } +} + +static int stf_isp_clk_enable(struct stf_isp_dev *isp_dev) +{ + struct stfcamss *stfcamss = isp_dev->stfcamss; + + clk_prepare_enable(stfcamss->sys_clk[STF_CLK_WRAPPER_CLK_C].clk); + reset_control_deassert(stfcamss->sys_rst[STF_RST_WRAPPER_C].rstc); + reset_control_deassert(stfcamss->sys_rst[STF_RST_WRAPPER_P].rstc); + + return 0; +} + +static int stf_isp_clk_disable(struct stf_isp_dev *isp_dev) +{ + struct stfcamss *stfcamss = isp_dev->stfcamss; + + reset_control_assert(stfcamss->sys_rst[STF_RST_WRAPPER_C].rstc); + reset_control_assert(stfcamss->sys_rst[STF_RST_WRAPPER_P].rstc); + clk_disable_unprepare(stfcamss->sys_clk[STF_CLK_WRAPPER_CLK_C].clk); + + return 0; +} + +static void __iomem *stf_isp_get_ispbase(struct stf_isp_dev *isp_dev) +{ + void __iomem *base = isp_dev->stfcamss->isp_base; + + return base; +} + +static int stf_isp_reset(struct stf_isp_dev *isp_dev) +{ + void __iomem *ispbase = stf_isp_get_ispbase(isp_dev); + + reg_set_bit(ispbase, ISP_REG_ISP_CTRL_0, BIT(1), BIT(1)); + reg_set_bit(ispbase, ISP_REG_ISP_CTRL_0, BIT(1), 0); + + return 0; +} + +static int stf_isp_config_set(struct stf_isp_dev *isp_dev) +{ + void __iomem *ispbase = stf_isp_get_ispbase(isp_dev); + + mutex_lock(&isp_dev->setfile_lock); + isp_load_regs(ispbase, isp_reg_init_settings); + if (isp_dev->setfile.state) { + st_info(ST_ISP, "%s, Program extra ISP setting!\n", __func__); + isp_load_regs_exclude_csi_isp_enable(ispbase, + &isp_dev->setfile.settings); + } + + mutex_unlock(&isp_dev->setfile_lock); + + return 0; +} + +static int stf_isp_set_format(struct stf_isp_dev *isp_dev, + struct isp_stream_format *crop_array, + u32 mcode, + int type) +{ + struct v4l2_rect *crop = &crop_array[ISP_COMPOSE].rect; + u32 bpp = crop_array[ISP_COMPOSE].bpp; + void __iomem *ispbase = stf_isp_get_ispbase(isp_dev); + u32 val, val1; + + st_debug(ST_ISP, "interface type is %d(%s)\n", + type, type == INTERFACE_CSI ? "CSI" : "DVP"); + + val = crop->left + (crop->top << 16); + isp_format_reg_list[0].addr = ISP_REG_PIC_CAPTURE_START_CFG; + isp_format_reg_list[0].val = val; + + val = (crop->width + crop->left - 1) + + ((crop->height + crop->top - 1) << 16); + isp_format_reg_list[1].addr = ISP_REG_PIC_CAPTURE_END_CFG; + isp_format_reg_list[1].val = val; + + val = crop->width + (crop->height << 16); + isp_format_reg_list[2].addr = ISP_REG_PIPELINE_XY_SIZE; + isp_format_reg_list[2].val = val; + + isp_format_reg_list[3].addr = ISP_REG_STRIDE; + isp_format_reg_list[3].val = ALIGN(crop->width * bpp / 8, + STFCAMSS_FRAME_WIDTH_ALIGN_8); + + switch (mcode) { + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SRGGB8_1X8: + /* 3 2 3 2 1 0 1 0 B Gb B Gb Gr R Gr R */ + val = 0x0000EE44; + val1 = 0x00000000; + break; + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGRBG8_1X8: + /* 2 3 2 3 0 1 0 1, Gb B Gb B R Gr R Gr */ + val = 0x0000BB11; + val1 = 0x20000000; + break; + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGBRG8_1X8: + /* 1 0 1 0 3 2 3 2, Gr R Gr R B Gb B Gb */ + val = 0x000044EE; + val1 = 0x30000000; + break; + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SBGGR8_1X8: + /* 0 1 0 1 2 3 2 3 R Gr R Gr Gb B Gb B */ + val = 0x000011BB; + val1 = 0x10000000; + break; + default: + st_err(ST_ISP, "UNKNOWN format\n"); + val = 0x000011BB; + val1 = 0x10000000; + break; + } + + isp_format_reg_list[4].addr = ISP_REG_RAW_FORMAT_CFG; + isp_format_reg_list[4].val = val; + + isp_format_reg_list[5].addr = ISP_REG_ISP_CTRL_1; + isp_format_reg_list[5].val = val1; + isp_format_reg_list[5].mask = 0xF0000000; + + st_info(ST_ISP, + "src left: %d, top: %d, width = %d, height = %d, bpp = %d\n", + crop->left, crop->top, crop->width, crop->height, bpp); + + crop = &crop_array[ISP_CROP].rect; + bpp = crop_array[ISP_CROP].bpp; + val = ALIGN(crop->width * bpp / 8, STFCAMSS_FRAME_WIDTH_ALIGN_128); + isp_format_reg_list[6].addr = ISP_REG_DUMP_CFG_1; + isp_format_reg_list[6].val = val | 3 << 16; + isp_format_reg_list[6].mask = 0x0003FFFF; + + st_info(ST_ISP, + "raw left: %d, top: %d, width = %d, height = %d, bpp = %d\n", + crop->left, crop->top, crop->width, crop->height, bpp); + + crop = &crop_array[ISP_SCALE_SS0].rect; + bpp = crop_array[ISP_SCALE_SS0].bpp; + isp_format_reg_list[7].addr = ISP_REG_SS0IW; + isp_format_reg_list[7].val = (crop->width << 16) + crop->height; + isp_format_reg_list[8].addr = ISP_REG_SS0S; + isp_format_reg_list[8].val = + ALIGN(crop->width * bpp / 8, STFCAMSS_FRAME_WIDTH_ALIGN_8); + + st_info(ST_ISP, + "ss0 left: %d, top: %d, width = %d, height = %d, bpp = %d\n", + crop->left, crop->top, crop->width, crop->height, bpp); + + crop = &crop_array[ISP_SCALE_SS1].rect; + bpp = crop_array[ISP_SCALE_SS1].bpp; + isp_format_reg_list[9].addr = ISP_REG_SS1IW; + isp_format_reg_list[9].val = (crop->width << 16) + crop->height; + isp_format_reg_list[10].addr = ISP_REG_SS1S; + isp_format_reg_list[10].val = + ALIGN(crop->width * bpp / 8, STFCAMSS_FRAME_WIDTH_ALIGN_8); + + isp_format_reg_list[14].addr = ISP_REG_SENSOR; + isp_format_reg_list[14].val = 0x00000000; + if (type == INTERFACE_CSI) + isp_format_reg_list[14].val |= 0x01; + + isp_load_regs(ispbase, isp_format_settings); + return 0; +} + +static int stf_isp_stream_set(struct stf_isp_dev *isp_dev, int on) +{ + void __iomem *ispbase = stf_isp_get_ispbase(isp_dev); + + if (on) + isp_load_regs(ispbase, isp_reg_start_settings); + + return 0; +} + +static int stf_isp_reg_read(struct stf_isp_dev *isp_dev, void *arg) +{ + void __iomem *ispbase = stf_isp_get_ispbase(isp_dev); + struct isp_reg_param *reg_param = arg; + union reg_buf *reg_buf = isp_dev->reg_buf; + u32 size; + unsigned long r; + + if (!reg_param->reg_buf) { + st_err(ST_ISP, + "Failed to access register.The pointer is NULL!\n"); + return -EINVAL; + } + + size = 0; + switch (reg_param->reg_info.method) { + case STF_ISP_REG_METHOD_ONE_REG: + break; + + case STF_ISP_REG_METHOD_SERIES: + if (reg_param->reg_info.length > STF_ISP_REG_BUF_SIZE) { + st_err(ST_ISP, + "The length = 0x%08X is out of size!\n", + reg_param->reg_info.length); + return -EINVAL; + } + break; + + case STF_ISP_REG_METHOD_MODULE: + st_err(ST_ISP, "The method = %d is not supported!\n", + reg_param->reg_info.method); + return -ENOTTY; + + case STF_ISP_REG_METHOD_TABLE: + if (reg_param->reg_info.length > STF_ISP_REG_TBL_BUF_SIZE) { + st_err(ST_ISP, + "The length = 0x%08X is out of size!\n", + reg_param->reg_info.length); + return -EINVAL; + } + size = sizeof(u32) * reg_param->reg_info.length * 2; + break; + + case STF_ISP_REG_METHOD_TABLE_2: + if (reg_param->reg_info.length > STF_ISP_REG_TBL_2_BUF_SIZE) { + st_err(ST_ISP, + "The length = 0x%08X is out of size!\n", + reg_param->reg_info.length); + return -EINVAL; + } + size = sizeof(u32) * reg_param->reg_info.length * 3; + break; + + case STF_ISP_REG_METHOD_TABLE_3: + if (reg_param->reg_info.length > STF_ISP_REG_TBL_3_BUF_SIZE) { + st_err(ST_ISP, + "The length = 0x%08X is out of size!\n", + reg_param->reg_info.length); + return -EINVAL; + } + size = sizeof(u32) * reg_param->reg_info.length * 4; + break; + + case STF_ISP_REG_METHOD_SMPL_PACK: + st_err(ST_ISP, "The method = %d is not supported!\n", + reg_param->reg_info.method); + return -ENOTTY; + + case STF_ISP_REG_METHOD_SOFT_RDMA: + st_err(ST_ISP, "The method = %d is not supported!\n", + reg_param->reg_info.method); + return -ENOTTY; + + default: + st_err(ST_ISP, "The method = %d is not supported!\n", + reg_param->reg_info.method); + return -ENOTTY; + } + + memset(reg_buf, 0, sizeof(union reg_buf)); + if (size) { + r = copy_from_user((u8 *)reg_buf->buffer, + (u8 *)reg_param->reg_buf->buffer, size); + if (r) { + st_err(ST_ISP, "Failed to copy buffer data\n"); + return -EIO; + } + } + + size = 0; + switch (reg_param->reg_info.method) { + case STF_ISP_REG_METHOD_ONE_REG: + reg_buf->buffer[0] = reg_read(ispbase, + reg_param->reg_info.offset); + size = sizeof(u32); + break; + + case STF_ISP_REG_METHOD_SERIES: + for (r = 0; r < reg_param->reg_info.length; r++) + reg_buf->buffer[r] = + reg_read(ispbase, + reg_param->reg_info.offset + (r * 4)); + + size = sizeof(u32) * reg_param->reg_info.length; + break; + + case STF_ISP_REG_METHOD_MODULE: + break; + + case STF_ISP_REG_METHOD_TABLE: + for (r = 0; r < reg_param->reg_info.length; r++) + reg_buf->reg_tbl[r].value = + reg_read(ispbase, reg_buf->reg_tbl[r].offset); + + size = sizeof(u32) * reg_param->reg_info.length * 2; + break; + + case STF_ISP_REG_METHOD_TABLE_2: + for (r = 0; r < reg_param->reg_info.length; r++) { + if (reg_buf->reg_tbl2[r].mask) + reg_buf->reg_tbl2[r].value = + reg_read(ispbase, reg_buf->reg_tbl2[r].offset) & + reg_buf->reg_tbl2[r].mask; + else + reg_buf->reg_tbl2[r].value = + reg_read(ispbase, + reg_buf->reg_tbl2[r].offset); + } + + size = sizeof(u32) * reg_param->reg_info.length * 3; + break; + + case STF_ISP_REG_METHOD_TABLE_3: + for (r = 0; r < reg_param->reg_info.length; r++) { + if (reg_buf->reg_tbl3[r].mask) + reg_buf->reg_tbl3[r].value = + reg_read(ispbase, reg_buf->reg_tbl3[r].offset) & + reg_buf->reg_tbl3[r].mask; + else + reg_buf->reg_tbl3[r].value = + reg_read(ispbase, + reg_buf->reg_tbl3[r].offset); + + if (reg_buf->reg_tbl3[r].delay_ms) + usleep_range(1000 * reg_buf->reg_tbl3[r].delay_ms, + 1000 * reg_buf->reg_tbl3[r].delay_ms + 100); + } + + size = sizeof(u32) * reg_param->reg_info.length * 4; + break; + + case STF_ISP_REG_METHOD_SMPL_PACK: + break; + + case STF_ISP_REG_METHOD_SOFT_RDMA: + break; + + default: + break; + } + + r = copy_to_user((u8 *)reg_param->reg_buf->buffer, + (u8 *)reg_buf->buffer, size); + if (r) { + st_err(ST_ISP, + "Failed to copy the reg_param->buffer value\n"); + return -EIO; + } + + return 0; +} + +static int stf_isp_soft_rdma(struct stf_isp_dev *isp_dev, u32 rdma_addr) +{ + void __iomem *ispbase = stf_isp_get_ispbase(isp_dev); + struct isp_rdma_info *rdma_info = NULL; + s32 len; + u32 offset; + int ret = 0; + + rdma_info = phys_to_virt(rdma_addr); + while (1) { + if (rdma_info->tag == RDMA_END) + break; + + if (rdma_info->tag == RDMA_WR_ONE) { + reg_write(ispbase, rdma_info->offset, rdma_info->param); + rdma_info++; + } else if (rdma_info->tag == RDMA_WR_SRL) { + offset = rdma_info->offset; + len = rdma_info->param; + rdma_info++; + while (len > 0) { + reg_write(ispbase, offset, rdma_info->param); + offset += 4; + len--; + if (len > 0) { + reg_write(ispbase, + offset, rdma_info->value); + len--; + } + offset += 4; + rdma_info++; + } + } else if (rdma_info->tag == RDMA_LINK) { + rdma_info = phys_to_virt(rdma_info->param); + } else if (rdma_info->tag == RDMA_SINT) { + /* not support this command. */ + rdma_info++; + } else { + rdma_info++; + } + } + + return ret; +} + +static int stf_isp_reg_write(struct stf_isp_dev *isp_dev, void *arg) +{ + void __iomem *ispbase = stf_isp_get_ispbase(isp_dev); + struct isp_reg_param *reg_param = arg; + struct isp_rdma_info *rdma_info = NULL; + union reg_buf *reg_buf = isp_dev->reg_buf; + s32 len; + u32 offset; + u32 size; + unsigned long r; + int ret = 0; + + if (!reg_param->reg_buf && + reg_param->reg_info.method != STF_ISP_REG_METHOD_SOFT_RDMA) { + st_err(ST_ISP, "Failed to access register.\n"); + return -EINVAL; + } + + size = 0; + switch (reg_param->reg_info.method) { + case STF_ISP_REG_METHOD_ONE_REG: + size = sizeof(u32); + break; + + case STF_ISP_REG_METHOD_SERIES: + if (reg_param->reg_info.length > STF_ISP_REG_BUF_SIZE) { + st_err(ST_ISP, + "The length = 0x%08X is out of size!\n", + reg_param->reg_info.length); + return -EINVAL; + } + size = sizeof(u32) * reg_param->reg_info.length; + break; + + case STF_ISP_REG_METHOD_MODULE: + st_err(ST_ISP, "The method = %d is not supported!\n", + reg_param->reg_info.method); + return -ENOTTY; + + case STF_ISP_REG_METHOD_TABLE: + if (reg_param->reg_info.length > STF_ISP_REG_TBL_BUF_SIZE) { + st_err(ST_ISP, + "The length = 0x%08X is out of size!\n", + reg_param->reg_info.length); + return -EINVAL; + } + size = sizeof(u32) * reg_param->reg_info.length * 2; + break; + + case STF_ISP_REG_METHOD_TABLE_2: + if (reg_param->reg_info.length > STF_ISP_REG_TBL_2_BUF_SIZE) { + st_err(ST_ISP, + "The length = 0x%08X is out of size!\n", + reg_param->reg_info.length); + return -EINVAL; + } + size = sizeof(u32) * reg_param->reg_info.length * 3; + break; + + case STF_ISP_REG_METHOD_TABLE_3: + if (reg_param->reg_info.length > STF_ISP_REG_TBL_3_BUF_SIZE) { + st_err(ST_ISP, + "The length = 0x%08X is out of size!\n", + reg_param->reg_info.length); + return -EINVAL; + } + size = sizeof(u32) * reg_param->reg_info.length * 4; + break; + + case STF_ISP_REG_METHOD_SMPL_PACK: + if (reg_param->reg_info.length > + STF_ISP_REG_SMPL_PACK_BUF_SIZE) { + st_err(ST_ISP, + "The length = 0x%08X is out of size!\n", + reg_param->reg_info.length); + return -EINVAL; + } + size = sizeof(u32) * reg_param->reg_info.length * 2; + break; + + case STF_ISP_REG_METHOD_SOFT_RDMA: + break; + + default: + st_err(ST_ISP, "The method = %d is not supported!\n", + reg_param->reg_info.method); + return -ENOTTY; + } + + memset(reg_buf, 0, sizeof(union reg_buf)); + if (size) { + r = copy_from_user((u8 *)reg_buf->buffer, + (u8 *)reg_param->reg_buf->buffer, size); + if (r) { + st_err(ST_ISP, "Failed to copy buffer data\n"); + return -EIO; + } + } + + switch (reg_param->reg_info.method) { + case STF_ISP_REG_METHOD_ONE_REG: + reg_write(ispbase, + reg_param->reg_info.offset, reg_buf->buffer[0]); + break; + + case STF_ISP_REG_METHOD_SERIES: + for (r = 0; r < reg_param->reg_info.length; r++) { + reg_write(ispbase, + reg_param->reg_info.offset + (r * 4), + reg_buf->buffer[r]); + } + break; + + case STF_ISP_REG_METHOD_MODULE: + /* This mode is not supported.*/ + break; + + case STF_ISP_REG_METHOD_TABLE: + for (r = 0; r < reg_param->reg_info.length; r++) { + reg_write(ispbase, reg_buf->reg_tbl[r].offset, + reg_buf->reg_tbl[r].value); + } + break; + + case STF_ISP_REG_METHOD_TABLE_2: + for (r = 0; r < reg_param->reg_info.length; r++) { + if (reg_buf->reg_tbl2[r].mask) { + reg_set_bit(ispbase, + reg_buf->reg_tbl2[r].offset, + reg_buf->reg_tbl2[r].mask, + reg_buf->reg_tbl2[r].value); + } else { + reg_write(ispbase, reg_buf->reg_tbl2[r].offset, + reg_buf->reg_tbl2[r].value); + } + } + break; + + case STF_ISP_REG_METHOD_TABLE_3: + for (r = 0; r < reg_param->reg_info.length; r++) { + if (reg_buf->reg_tbl3[r].mask) + reg_set_bit(ispbase, + reg_buf->reg_tbl3[r].offset, + reg_buf->reg_tbl3[r].mask, + reg_buf->reg_tbl3[r].value); + else + reg_write(ispbase, reg_buf->reg_tbl3[r].offset, + reg_buf->reg_tbl3[r].value); + + if (reg_buf->reg_tbl3[r].delay_ms) + usleep_range(1000 * reg_buf->reg_tbl3[r].delay_ms, + 1000 * reg_buf->reg_tbl3[r].delay_ms + 100); + } + break; + + case STF_ISP_REG_METHOD_SMPL_PACK: + size = reg_param->reg_info.length; + rdma_info = ®_buf->rdma_cmd[0]; + while (size) { + if (rdma_info->tag == RDMA_END) + break; + + if (rdma_info->tag == RDMA_WR_ONE) { + reg_write(ispbase, + rdma_info->offset, rdma_info->param); + rdma_info++; + size--; + } else if (rdma_info->tag == RDMA_WR_SRL) { + offset = rdma_info->offset; + len = rdma_info->param; + rdma_info++; + size--; + while (size && (len > 0)) { + reg_write(ispbase, + offset, rdma_info->param); + offset += 4; + len--; + if (len > 0) { + reg_write(ispbase, + offset, + rdma_info->value); + len--; + } + offset += 4; + rdma_info++; + size--; + } + } else { + rdma_info++; + size--; + } + } + break; + + case STF_ISP_REG_METHOD_SOFT_RDMA: + /* + * Simulation the hardware RDMA behavior to debug and verify + * the RDMA chain. + */ + ret = stf_isp_soft_rdma(isp_dev, reg_param->reg_info.offset); + break; + + default: + break; + } + + return ret; +} + +static int stf_isp_shadow_trigger(struct stf_isp_dev *isp_dev) +{ + void __iomem *ispbase = stf_isp_get_ispbase(isp_dev); + + /* shadow update */ + reg_set_bit(ispbase, + ISP_REG_CSIINTS_ADDR, (BIT(17) | BIT(16)), 0x30000); + reg_set_bit(ispbase, ISP_REG_IESHD_ADDR, (BIT(1) | BIT(0)), 0x3); + + return 0; +} + +const struct isp_hw_ops isp_ops = { + .isp_clk_enable = stf_isp_clk_enable, + .isp_clk_disable = stf_isp_clk_disable, + .isp_reset = stf_isp_reset, + .isp_config_set = stf_isp_config_set, + .isp_set_format = stf_isp_set_format, + .isp_stream_set = stf_isp_stream_set, + .isp_reg_read = stf_isp_reg_read, + .isp_reg_write = stf_isp_reg_write, + .isp_shadow_trigger = stf_isp_shadow_trigger, +}; diff --git a/drivers/media/platform/starfive/stf_video.c b/drivers/media/platform/starfive/stf_video.c new file mode 100644 index 0000000000000..23d393e6fd78b --- /dev/null +++ b/drivers/media/platform/starfive/stf_video.c @@ -0,0 +1,1286 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * stf_video.c + * + * StarFive Camera Subsystem - V4L2 device node + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include + +#include "stf_camss.h" +#include "stf_video.h" + +static const struct stfcamss_format_info formats_pix_wr[] = { + { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10, 1, + { { 1, 1 } }, { { 1, 1 } }, { 10 } }, + { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10, 1, + { { 1, 1 } }, { { 1, 1 } }, { 10 } }, + { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10, 1, + { { 1, 1 } }, { { 1, 1 } }, { 10 } }, + { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10, 1, + { { 1, 1 } }, { { 1, 1 } }, { 10 } }, +}; + +static const struct stfcamss_format_info formats_raw_isp[] = { + { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12, 1, + { { 1, 1 } }, { { 1, 1 } }, { 12 } }, + { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12, 1, + { { 1, 1 } }, { { 1, 1 } }, { 12 } }, + { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12, 1, + { { 1, 1 } }, { { 1, 1 } }, { 12 } }, + { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12, 1, + { { 1, 1 } }, { { 1, 1 } }, { 12 } }, +}; + +static const struct stfcamss_format_info formats_pix_isp[] = { + { MEDIA_BUS_FMT_Y12_1X12, V4L2_PIX_FMT_NV12, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, +}; + +/* ----------------------------------------------------------------------------- + * Helper functions + */ + +static int video_find_format(u32 code, u32 pixelformat, + const struct stfcamss_format_info *formats, + unsigned int nformats) +{ + int i; + + for (i = 0; i < nformats; i++) { + if (formats[i].code == code && + formats[i].pixelformat == pixelformat) + return i; + } + + for (i = 0; i < nformats; i++) + if (formats[i].code == code) + return i; + + for (i = 0; i < nformats; i++) + if (formats[i].pixelformat == pixelformat) + return i; + + return -EINVAL; +} + +static int __video_try_fmt(struct stfcamss_video *video, struct v4l2_format *f) +{ + struct v4l2_pix_format *pix; + const struct stfcamss_format_info *fi; + u32 width, height; + u32 bpl; + int i; + + st_debug(ST_VIDEO, "%s, fmt.type = 0x%x\n", __func__, f->type); + pix = &f->fmt.pix; + + for (i = 0; i < video->nformats; i++) + if (pix->pixelformat == video->formats[i].pixelformat) + break; + + if (i == video->nformats) + i = 0; /* default format */ + + fi = &video->formats[i]; + width = pix->width; + height = pix->height; + + memset(pix, 0, sizeof(*pix)); + + pix->pixelformat = fi->pixelformat; + pix->width = clamp_t(u32, width, STFCAMSS_FRAME_MIN_WIDTH, + STFCAMSS_FRAME_MAX_WIDTH); + pix->height = clamp_t(u32, height, STFCAMSS_FRAME_MIN_HEIGHT, + STFCAMSS_FRAME_MAX_HEIGHT); + bpl = pix->width / fi->hsub[0].numerator * + fi->hsub[0].denominator * fi->bpp[0] / 8; + bpl = ALIGN(bpl, video->bpl_alignment); + pix->bytesperline = bpl; + pix->sizeimage = pix->height / fi->vsub[0].numerator * + fi->vsub[0].denominator * bpl; + + pix->field = V4L2_FIELD_NONE; + pix->colorspace = V4L2_COLORSPACE_SRGB; + pix->flags = 0; + pix->ycbcr_enc = + V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace); + pix->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, + pix->colorspace, + pix->ycbcr_enc); + pix->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace); + + st_info(ST_VIDEO, "w, h = %d, %d, bpp = %d\n", + pix->width, pix->height, fi->bpp[0]); + st_info(ST_VIDEO, "i = %d, s = 0x%x\n", i, pix->sizeimage); + + return 0; +} + +static int stf_video_init_format(struct stfcamss_video *video) +{ + int ret; + struct v4l2_format format = { + .type = video->type, + .fmt.pix = { + .width = 1920, + .height = 1080, + .pixelformat = V4L2_PIX_FMT_RGB565, + }, + }; + + ret = __video_try_fmt(video, &format); + + if (ret < 0) + return ret; + + video->active_fmt = format; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Video queue operations + */ + +static int video_queue_setup(struct vb2_queue *q, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct stfcamss_video *video = vb2_get_drv_priv(q); + const struct v4l2_pix_format *format = &video->active_fmt.fmt.pix; + + st_debug(ST_VIDEO, "%s, planes = %d\n", __func__, *num_planes); + + if (*num_planes) { + if (*num_planes != 1) + return -EINVAL; + + if (sizes[0] < format->sizeimage) + return -EINVAL; + } + + *num_planes = 1; + sizes[0] = format->sizeimage; + if (!sizes[0]) + st_err(ST_VIDEO, "%s: error size is zero!!!\n", __func__); + + st_info(ST_VIDEO, "%s, planes = %d, size = %d\n", + __func__, *num_planes, sizes[0]); + + return 0; +} + +static int video_buf_init(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct stfcamss_video *video = vb2_get_drv_priv(vb->vb2_queue); + struct stfcamss_buffer *buffer = + container_of(vbuf, struct stfcamss_buffer, vb); + const struct v4l2_pix_format *fmt = &video->active_fmt.fmt.pix; + dma_addr_t *paddr; + + buffer->sizeimage = 0; + + paddr = vb2_plane_cookie(vb, 0); + buffer->sizeimage = vb2_plane_size(vb, 0); + buffer->addr[0] = *paddr; + if (fmt->pixelformat == V4L2_PIX_FMT_NV12 || + fmt->pixelformat == V4L2_PIX_FMT_NV21 || + fmt->pixelformat == V4L2_PIX_FMT_NV16 || + fmt->pixelformat == V4L2_PIX_FMT_NV61) + buffer->addr[1] = + buffer->addr[0] + fmt->bytesperline * fmt->height; + + return 0; +} + +static int video_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct stfcamss_video *video = vb2_get_drv_priv(vb->vb2_queue); + const struct v4l2_pix_format *fmt = &video->active_fmt.fmt.pix; + + if (fmt->sizeimage > vb2_plane_size(vb, 0)) { + st_err(ST_VIDEO, "sizeimage = %d, plane size = %d\n", + fmt->sizeimage, (unsigned int)vb2_plane_size(vb, 0)); + return -EINVAL; + } + vb2_set_plane_payload(vb, 0, fmt->sizeimage); + + vbuf->field = V4L2_FIELD_NONE; + + return 0; +} + +static void video_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct stfcamss_video *video = vb2_get_drv_priv(vb->vb2_queue); + struct stfcamss_buffer *buffer = + container_of(vbuf, struct stfcamss_buffer, vb); + + video->ops->queue_buffer(video, buffer); +} + +/* + * video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format + * @mbus: v4l2_mbus_framefmt format (input) + * @pix: v4l2_pix_format_mplane format (output) + * @f: a pointer to formats array element to be used for the conversion + * @alignment: bytesperline alignment value + * + * Fill the output pix structure with information from the input mbus format. + * + * Return 0 on success or a negative error code otherwise + */ +static int video_mbus_to_pix(const struct v4l2_mbus_framefmt *mbus, + struct v4l2_pix_format *pix, + const struct stfcamss_format_info *f, + unsigned int alignment) +{ + u32 bytesperline; + + memset(pix, 0, sizeof(*pix)); + v4l2_fill_pix_format(pix, mbus); + pix->pixelformat = f->pixelformat; + bytesperline = pix->width / f->hsub[0].numerator * + f->hsub[0].denominator * f->bpp[0] / 8; + bytesperline = ALIGN(bytesperline, alignment); + pix->bytesperline = bytesperline; + pix->sizeimage = pix->height / f->vsub[0].numerator * + f->vsub[0].denominator * bytesperline; + return 0; +} + +static struct v4l2_subdev *video_remote_subdev(struct stfcamss_video *video, + u32 *pad) +{ + struct media_pad *remote; + + remote = media_pad_remote_pad_first(&video->pad); + + if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) + return NULL; + + if (pad) + *pad = remote->index; + + return media_entity_to_v4l2_subdev(remote->entity); +} + +static int video_get_subdev_format(struct stfcamss_video *video, + struct v4l2_format *format) +{ + struct v4l2_pix_format *pix = &video->active_fmt.fmt.pix; + struct v4l2_subdev_format fmt; + struct v4l2_subdev *subdev; + u32 pixelformat; + u32 pad; + int ret; + + subdev = video_remote_subdev(video, &pad); + if (!subdev) + return -EPIPE; + + fmt.pad = pad; + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret) + return ret; + + pixelformat = pix->pixelformat; + ret = video_find_format(fmt.format.code, pixelformat, + video->formats, video->nformats); + if (ret < 0) + return ret; + + format->type = video->type; + + return video_mbus_to_pix(&fmt.format, &format->fmt.pix, + &video->formats[ret], video->bpl_alignment); +} + +static int video_check_format(struct stfcamss_video *video) +{ + struct v4l2_pix_format *pix = &video->active_fmt.fmt.pix; + struct v4l2_format format; + struct v4l2_pix_format *sd_pix = &format.fmt.pix; + int ret; + + sd_pix->pixelformat = pix->pixelformat; + ret = video_get_subdev_format(video, &format); + if (ret < 0) + return ret; + + if (pix->pixelformat != sd_pix->pixelformat || + pix->height > sd_pix->height || + pix->width > sd_pix->width || + pix->field != format.fmt.pix.field) { + st_err(ST_VIDEO, + "%s, not match:\n" + "0x%x 0x%x\n0x%x 0x%x\n0x%x 0x%x\n", + __func__, + pix->pixelformat, sd_pix->pixelformat, + pix->height, sd_pix->height, + pix->field, format.fmt.pix.field); + return -EPIPE; + } + + return 0; +} + +static int video_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct stfcamss_video *video = vb2_get_drv_priv(q); + struct video_device *vdev = &video->vdev; + struct media_entity *entity; + struct media_pad *pad; + struct v4l2_subdev *subdev; + int ret; + + ret = video_device_pipeline_start(vdev, &video->stfcamss->pipe); + if (ret < 0) { + st_err(ST_VIDEO, "Failed to media_pipeline_start: %d\n", ret); + return ret; + } + + ret = video_check_format(video); + if (ret < 0) + goto error; + entity = &vdev->entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, video, s_stream, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) + goto error; + } + return 0; + +error: + video_device_pipeline_stop(vdev); + video->ops->flush_buffers(video, VB2_BUF_STATE_QUEUED); + return ret; +} + +static void video_stop_streaming(struct vb2_queue *q) +{ + struct stfcamss_video *video = vb2_get_drv_priv(q); + struct video_device *vdev = &video->vdev; + struct media_entity *entity; + struct media_pad *pad; + struct v4l2_subdev *subdev; + + entity = &vdev->entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + v4l2_subdev_call(subdev, video, s_stream, 0); + } + + video_device_pipeline_stop(vdev); + video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR); +} + +static const struct vb2_ops stf_video_vb2_q_ops = { + .queue_setup = video_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_init = video_buf_init, + .buf_prepare = video_buf_prepare, + .buf_queue = video_buf_queue, + .start_streaming = video_start_streaming, + .stop_streaming = video_stop_streaming, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 ioctls + */ + +static int video_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct stfcamss_video *video = video_drvdata(file); + + strscpy(cap->driver, "stf camss", sizeof(cap->driver)); + strscpy(cap->card, "Starfive Camera Subsystem", sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(video->stfcamss->dev)); + return 0; +} + +static int video_get_pfmt_by_index(struct stfcamss_video *video, int ndx) +{ + int i, j, k; + + /* find index "i" of "k"th unique pixelformat in formats array */ + k = -1; + for (i = 0; i < video->nformats; i++) { + for (j = 0; j < i; j++) { + if (video->formats[i].pixelformat == + video->formats[j].pixelformat) + break; + } + + if (j == i) + k++; + + if (k == ndx) + return i; + } + + return -EINVAL; +} + +static int video_get_pfmt_by_mcode(struct stfcamss_video *video, u32 mcode) +{ + int i; + + for (i = 0; i < video->nformats; i++) { + if (video->formats[i].code == mcode) + return i; + } + + return -EINVAL; +} + +static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct stfcamss_video *video = video_drvdata(file); + int i; + + st_debug(ST_VIDEO, "%s:\n0x%x 0x%x\n 0x%x, 0x%x\n0x%x\n", + __func__, + f->type, video->type, + f->index, video->nformats, + f->mbus_code); + + if (f->type != video->type) + return -EINVAL; + if (f->index >= video->nformats) + return -EINVAL; + + if (f->mbus_code) { + /* Each entry in formats[] table has unique mbus_code */ + if (f->index > 0) + return -EINVAL; + + i = video_get_pfmt_by_mcode(video, f->mbus_code); + } else { + i = video_get_pfmt_by_index(video, f->index); + } + + if (i < 0) + return -EINVAL; + + f->pixelformat = video->formats[i].pixelformat; + + return 0; +} + +static int video_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct v4l2_subdev_frame_size_enum fse = {0}; + struct v4l2_subdev_mbus_code_enum code = {0}; + struct stfcamss_video *video = video_drvdata(file); + struct video_device *vdev = &video->vdev; + struct media_entity *entity = &vdev->entity; + struct media_entity *sensor; + struct v4l2_subdev *subdev; + struct media_pad *pad; + bool support_selection = false; + int i; + int ret; + + for (i = 0; i < video->nformats; i++) { + if (video->formats[i].pixelformat == fsize->pixel_format) + break; + } + + if (i == video->nformats) + return -EINVAL; + + entity = &vdev->entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + if (v4l2_subdev_has_op(subdev, pad, set_selection)) { + support_selection = true; + break; + } + } + + if (support_selection) { + if (fsize->index) + return -EINVAL; + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = STFCAMSS_FRAME_MIN_WIDTH; + fsize->stepwise.max_width = STFCAMSS_FRAME_MAX_WIDTH; + fsize->stepwise.min_height = STFCAMSS_FRAME_MIN_HEIGHT; + fsize->stepwise.max_height = STFCAMSS_FRAME_MAX_HEIGHT; + fsize->stepwise.step_width = 1; + fsize->stepwise.step_height = 1; + } else { + entity = &vdev->entity; + sensor = stfcamss_find_sensor(entity); + if (!sensor) + return -ENOTTY; + + subdev = media_entity_to_v4l2_subdev(sensor); + code.index = 0; + code.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, + enum_mbus_code, NULL, &code); + if (ret < 0) + return -EINVAL; + fse.index = fsize->index; + fse.code = code.code; + fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, + enum_frame_size, NULL, &fse); + if (ret < 0) + return -EINVAL; + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = fse.min_width; + fsize->discrete.height = fse.min_height; + } + + return 0; +} + +static int video_enum_frameintervals(struct file *file, void *fh, + struct v4l2_frmivalenum *fival) +{ + int ret = 0; + struct stfcamss_video *video = video_drvdata(file); + struct video_device *vdev = &video->vdev; + struct media_entity *entity = &vdev->entity; + struct media_entity *sensor; + struct v4l2_subdev *subdev; + struct v4l2_subdev_mbus_code_enum code = {0}; + struct v4l2_subdev_frame_interval_enum fie = {0}; + + sensor = stfcamss_find_sensor(entity); + if (!sensor) + return -ENOTTY; + fie.index = fival->index; + fie.width = fival->width; + fie.height = fival->height; + fie.which = V4L2_SUBDEV_FORMAT_ACTIVE; + subdev = media_entity_to_v4l2_subdev(sensor); + + code.index = 0; + code.which = V4L2_SUBDEV_FORMAT_ACTIVE; + + ret = video_find_format(0, fival->pixel_format, + video->formats, video->nformats); + if (ret < 0) + return -EINVAL; + + ret = v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &code); + if (ret < 0) + return -EINVAL; + + fie.code = code.code; + ret = v4l2_subdev_call(subdev, pad, enum_frame_interval, NULL, &fie); + if (ret < 0) + return ret; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = fie.interval; + + return 0; +} + +static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct stfcamss_video *video = video_drvdata(file); + + st_debug(ST_VIDEO, "%s, fmt.type = 0x%x\n", __func__, f->type); + st_debug(ST_VIDEO, "%s, active_fmt.type = 0x%x,0x%x\n", + __func__, video->active_fmt.type, + video->active_fmt.fmt.pix.pixelformat); + *f = video->active_fmt; + return 0; +} + +static int video_entity_s_fmt(struct stfcamss_video *video, + struct media_entity *entity, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt, + u32 dst_code) +{ + struct v4l2_subdev *subdev; + struct media_pad *pad; + struct v4l2_mbus_framefmt *mf = &fmt->format; + u32 width, height, code; + int ret, index = 0; + + code = mf->code; + width = mf->width; + height = mf->height; + subdev = media_entity_to_v4l2_subdev(entity); + while (1) { + if (index >= entity->num_pads) + break; + pad = &entity->pads[index]; + pad = media_pad_remote_pad_first(pad); + if (pad && is_media_entity_v4l2_subdev(pad->entity)) { + fmt->pad = index; + if (index) + mf->code = dst_code; + ret = v4l2_subdev_call(subdev, pad, + set_fmt, state, fmt); + st_warn(ST_VIDEO, + "\"%s\":%d pad fmt set to 0x%x %ux%u\n", + subdev->name, fmt->pad, mf->code, + mf->width, mf->height); + if (mf->code != code || + mf->width != width || + mf->height != height) { + st_warn(ST_VIDEO, + "\"%s\":%d,changed fmt to 0x%x %ux%u\n", + subdev->name, fmt->pad, mf->code, + mf->width, mf->height); + } + if (index) + ret = video_entity_s_fmt(video, pad->entity, + state, fmt, dst_code); + } + + if (ret < 0 && ret != -ENOIOCTLCMD) + break; + index++; + } + return ret; +} + +static int video_getcrop_pad_id(int video_id) +{ + return stf_vin_map_isp_pad(video_id, STF_ISP_PAD_SRC); +} + +static int video_pipeline_s_fmt(struct stfcamss_video *video, + struct v4l2_subdev_state *state, + struct v4l2_format *f) +{ + struct video_device *vdev = &video->vdev; + struct media_entity *entity = &vdev->entity; + struct v4l2_subdev *subdev; + int ret, index; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .reserved = {video_getcrop_pad_id(video->id)} + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct media_entity *sensor; + u32 width, height, code; + struct media_pad *pad; + + /* pix to mbus format */ + index = video_find_format(mf->code, + pix->pixelformat, + video->formats, video->nformats); + if (index < 0) + return index; + v4l2_fill_mbus_format(mf, pix, video->formats[index].code); + + code = mf->code; + width = mf->width; + height = mf->height; + sensor = stfcamss_find_sensor(entity); + if (sensor) { + subdev = media_entity_to_v4l2_subdev(sensor); + ret = v4l2_subdev_call(subdev, pad, set_fmt, state, &fmt); + st_warn(ST_VIDEO, + "\"%s\":%d pad fmt set to 0x%x %ux%u\n", + subdev->name, fmt.pad, mf->code, + mf->width, mf->height); + if (mf->code != code || + mf->width != width || + mf->height != height) { + st_warn(ST_VIDEO, + "\"%s\":%d, changed fmt to 0x%x %ux%u\n", + subdev->name, fmt.pad, mf->code, + mf->width, mf->height); + } + } else { + st_err(ST_VIDEO, "Can't find sensor\n"); + return -ENOTTY; + } + /* + * Starting from sensor subdevice, walk within + * pipeline and set format on each subdevice + */ + sensor = stfcamss_find_sensor(entity); + pad = media_pad_remote_pad_first(&sensor->pads[0]); + ret = video_entity_s_fmt(video, pad->entity, state, &fmt, code); + + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + + index = video_find_format(mf->code, + video->formats[index].pixelformat, + video->formats, video->nformats); + st_debug(ST_VIDEO, "%s, code=%x, index=%d\n", + __func__, mf->code, index); + + if (index < 0) + return index; + + video_mbus_to_pix(mf, pix, &video->formats[index], + video->bpl_alignment); + + ret = __video_try_fmt(video, f); + if (ret < 0) + return ret; + + return 0; +} + +static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct stfcamss_video *video = video_drvdata(file); + int ret; + + st_debug(ST_VIDEO, "%s, fmt.type = 0x%x, v4l2fmt=%x\n", + __func__, f->type, f->fmt.pix.pixelformat); + + if (vb2_is_busy(&video->vb2_q)) + return -EBUSY; + + ret = __video_try_fmt(video, f); + if (ret < 0) + return ret; + + ret = video_pipeline_s_fmt(video, NULL, f); + + st_debug(ST_VIDEO, "%s, pixelformat=0x%x, ret=%d\n", + __func__, f->fmt.pix.pixelformat, ret); + if (ret < 0) + return ret; + + video->active_fmt = *f; + + return 0; +} + +static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct stfcamss_video *video = video_drvdata(file); + + return __video_try_fmt(video, f); +} + +static int video_enum_input(struct file *file, void *fh, + struct v4l2_input *input) +{ + if (input->index > 0) + return -EINVAL; + + strscpy(input->name, "camera", sizeof(input->name)); + input->type = V4L2_INPUT_TYPE_CAMERA; + + return 0; +} + +static int video_g_input(struct file *file, void *fh, unsigned int *input) +{ + *input = 0; + + return 0; +} + +static int video_s_input(struct file *file, void *fh, unsigned int input) +{ + return input == 0 ? 0 : -EINVAL; +} + +static int video_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *p) +{ + struct stfcamss_video *video = video_drvdata(file); + struct video_device *vdev = &video->vdev; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_pad *pad; + int ret, is_support = 0; + + entity = &vdev->entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_g_parm_cap(vdev, subdev, p); + if (ret < 0 && ret != -ENOIOCTLCMD) + break; + if (!ret) + is_support = 1; + } + + return is_support ? 0 : ret; +} + +static int video_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *p) +{ + struct stfcamss_video *video = video_drvdata(file); + struct video_device *vdev = &video->vdev; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_pad *pad; + struct v4l2_streamparm tmp_p; + int ret, is_support = 0; + + entity = &vdev->entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + tmp_p = *p; + ret = v4l2_s_parm_cap(vdev, subdev, &tmp_p); + if (ret < 0 && ret != -ENOIOCTLCMD) + break; + if (!ret) { + is_support = 1; + *p = tmp_p; + } + } + + return is_support ? 0 : ret; +} + +static int video_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct stfcamss_video *video = video_drvdata(file); + struct video_device *vdev = &video->vdev; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_pad *pad; + struct v4l2_subdev_selection sel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = video_getcrop_pad_id(video->id), + .target = s->target, + .r = s->r, + .flags = s->flags, + }; + int ret; + + st_debug(ST_VIDEO, "%s, target = 0x%x, 0x%x\n", + __func__, sel.target, s->target); + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + + entity = &vdev->entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, pad, get_selection, NULL, &sel); + if (!ret) { + s->r = sel.r; + s->flags = sel.flags; + break; + } + if (ret != -ENOIOCTLCMD) + break; + } + + return ret; +} + +static int video_s_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct stfcamss_video *video = video_drvdata(file); + struct video_device *vdev = &video->vdev; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_pad *pad; + struct v4l2_subdev_selection sel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = video_getcrop_pad_id(video->id), + .target = s->target, + .r = s->r, + .flags = s->flags, + }; + struct v4l2_pix_format *format = &video->active_fmt.fmt.pix; + int ret; + + st_debug(ST_VIDEO, "%s, target = 0x%x, 0x%x\n", + __func__, sel.target, s->target); + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + + entity = &vdev->entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, pad, set_selection, NULL, &sel); + if (!ret) { + s->r = sel.r; + s->flags = sel.flags; + format->width = s->r.width; + format->height = s->r.height; + ret = __video_try_fmt(video, &video->active_fmt); + if (ret < 0) + return ret; + break; + } + if (ret != -ENOIOCTLCMD) + break; + } + + st_debug(ST_VIDEO, "ret = 0x%x, -EINVAL = 0x%x\n", ret, -EINVAL); + + return ret; +} + +static int video_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + struct v4l2_subdev *subdev = NULL; + struct media_entity *entity = &fh->vdev->entity; + struct media_entity *sensor; + + sensor = stfcamss_find_sensor(entity); + if (sensor) + subdev = media_entity_to_v4l2_subdev(sensor); + + if (!subdev) + return -ENOTTY; + + fh->ctrl_handler = subdev->ctrl_handler; + return v4l2_ctrl_subscribe_event(fh, sub); +} + +static int video_unsubscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + struct v4l2_subdev *subdev = NULL; + struct media_entity *entity = &fh->vdev->entity; + struct media_entity *sensor; + + sensor = stfcamss_find_sensor(entity); + if (sensor) + subdev = media_entity_to_v4l2_subdev(sensor); + + if (!subdev) + return -ENOTTY; + + fh->ctrl_handler = subdev->ctrl_handler; + return v4l2_event_unsubscribe(fh, sub); +} + +static const struct v4l2_ioctl_ops stf_vid_vin_ioctl_ops = { + .vidioc_querycap = video_querycap, + .vidioc_enum_fmt_vid_cap = video_enum_fmt, + .vidioc_enum_fmt_vid_out = video_enum_fmt, + .vidioc_enum_framesizes = video_enum_framesizes, + .vidioc_enum_frameintervals = video_enum_frameintervals, + .vidioc_g_fmt_vid_cap = video_g_fmt, + .vidioc_s_fmt_vid_cap = video_s_fmt, + .vidioc_try_fmt_vid_cap = video_try_fmt, + .vidioc_g_fmt_vid_out = video_g_fmt, + .vidioc_s_fmt_vid_out = video_s_fmt, + .vidioc_try_fmt_vid_out = video_try_fmt, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_enum_input = video_enum_input, + .vidioc_g_input = video_g_input, + .vidioc_s_input = video_s_input, + .vidioc_g_parm = video_g_parm, + .vidioc_s_parm = video_s_parm, + .vidioc_subscribe_event = video_subscribe_event, + .vidioc_unsubscribe_event = video_unsubscribe_event, +}; + +static const struct v4l2_ioctl_ops stf_vid_isp_ioctl_ops = { + .vidioc_querycap = video_querycap, + .vidioc_enum_fmt_vid_cap = video_enum_fmt, + .vidioc_enum_fmt_vid_out = video_enum_fmt, + .vidioc_enum_framesizes = video_enum_framesizes, + .vidioc_enum_frameintervals = video_enum_frameintervals, + .vidioc_g_fmt_vid_cap = video_g_fmt, + .vidioc_s_fmt_vid_cap = video_s_fmt, + .vidioc_try_fmt_vid_cap = video_try_fmt, + .vidioc_g_fmt_vid_out = video_g_fmt, + .vidioc_s_fmt_vid_out = video_s_fmt, + .vidioc_try_fmt_vid_out = video_try_fmt, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_enum_input = video_enum_input, + .vidioc_g_input = video_g_input, + .vidioc_s_input = video_s_input, + .vidioc_g_parm = video_g_parm, + .vidioc_s_parm = video_s_parm, + .vidioc_s_selection = video_s_selection, + .vidioc_g_selection = video_g_selection, + .vidioc_subscribe_event = video_subscribe_event, + .vidioc_unsubscribe_event = video_unsubscribe_event, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 file operations + */ + +static int video_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct stfcamss_video *video = video_drvdata(file); + struct v4l2_fh *vfh; + int ret; + + mutex_lock(&video->lock); + + vfh = kzalloc(sizeof(*vfh), GFP_KERNEL); + if (!vfh) { + ret = -ENOMEM; + goto error_alloc; + } + + v4l2_fh_init(vfh, vdev); + v4l2_fh_add(vfh); + + file->private_data = vfh; + + ret = v4l2_pipeline_pm_get(&vdev->entity); + if (ret < 0) { + st_err(ST_VIDEO, + "Failed to power up pipeline: %d\n", ret); + goto error_pm_use; + } + mutex_unlock(&video->lock); + + return 0; + +error_pm_use: + v4l2_fh_release(file); +error_alloc: + mutex_unlock(&video->lock); + return ret; +} + +static int video_release(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + + vb2_fop_release(file); + v4l2_pipeline_pm_put(&vdev->entity); + file->private_data = NULL; + + return 0; +} + +static const struct v4l2_file_operations stf_vid_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = video_open, + .release = video_release, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .read = vb2_fop_read, +}; + +/* ----------------------------------------------------------------------------- + * STFCAMSS video core + */ + +static void stf_video_release(struct video_device *vdev) +{ + struct stfcamss_video *video = video_get_drvdata(vdev); + + media_entity_cleanup(&vdev->entity); + + mutex_destroy(&video->q_lock); + mutex_destroy(&video->lock); +} + +int stf_video_register(struct stfcamss_video *video, + struct v4l2_device *v4l2_dev, const char *name) +{ + struct video_device *vdev; + struct vb2_queue *q; + struct media_pad *pad = &video->pad; + int ret; + enum isp_pad_id isp_pad; + + vdev = &video->vdev; + + mutex_init(&video->q_lock); + + q = &video->vb2_q; + q->drv_priv = video; + q->mem_ops = &vb2_dma_contig_memops; + q->ops = &stf_video_vb2_q_ops; + q->type = video->type; + q->io_modes = VB2_DMABUF | VB2_MMAP | VB2_READ; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->buf_struct_size = sizeof(struct stfcamss_buffer); + q->dev = video->stfcamss->dev; + q->lock = &video->q_lock; + q->min_buffers_needed = STFCAMSS_MIN_BUFFERS; + ret = vb2_queue_init(q); + if (ret < 0) { + st_err(ST_VIDEO, "Failed to init vb2 queue: %d\n", ret); + goto err_vb2_init; + } + + pad->flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&vdev->entity, 1, pad); + if (ret < 0) { + st_err(ST_VIDEO, "Failed to init video entity: %d\n", ret); + goto err_vb2_init; + } + + mutex_init(&video->lock); + + isp_pad = stf_vin_map_isp_pad(video->id, STF_ISP_PAD_SRC); + if (video->id == VIN_LINE_WR) { + video->formats = formats_pix_wr; + video->nformats = ARRAY_SIZE(formats_pix_wr); + video->bpl_alignment = STFCAMSS_FRAME_WIDTH_ALIGN_8; + vdev->ioctl_ops = &stf_vid_vin_ioctl_ops; + } else if (isp_pad == STF_ISP_PAD_SRC || + isp_pad == STF_ISP_PAD_SRC_SS0 || + isp_pad == STF_ISP_PAD_SRC_SS1) { + video->formats = formats_pix_isp; + video->nformats = ARRAY_SIZE(formats_pix_isp); + video->bpl_alignment = STFCAMSS_FRAME_WIDTH_ALIGN_8; + vdev->ioctl_ops = &stf_vid_isp_ioctl_ops; + } else { + video->formats = formats_raw_isp; + video->nformats = ARRAY_SIZE(formats_raw_isp); + video->bpl_alignment = STFCAMSS_FRAME_WIDTH_ALIGN_128; + vdev->ioctl_ops = &stf_vid_isp_ioctl_ops; + } + + ret = stf_video_init_format(video); + if (ret < 0) { + st_err(ST_VIDEO, "Failed to init format: %d\n", ret); + goto err_vid_init_format; + } + + vdev->fops = &stf_vid_fops; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE; + vdev->vfl_dir = VFL_DIR_RX; + vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + vdev->release = stf_video_release; + vdev->v4l2_dev = v4l2_dev; + vdev->queue = &video->vb2_q; + vdev->lock = &video->lock; + strscpy(vdev->name, name, sizeof(vdev->name)); + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, video->id); + if (ret < 0) { + st_err(ST_VIDEO, + "Failed to register video device: %d\n", + ret); + goto err_vid_reg; + } + + video_set_drvdata(vdev, video); + return 0; + +err_vid_reg: +err_vid_init_format: + media_entity_cleanup(&vdev->entity); + mutex_destroy(&video->lock); +err_vb2_init: + mutex_destroy(&video->q_lock); + return ret; +} + +void stf_video_unregister(struct stfcamss_video *video) +{ + vb2_video_unregister_device(&video->vdev); +} diff --git a/drivers/media/platform/starfive/stf_video.h b/drivers/media/platform/starfive/stf_video.h new file mode 100644 index 0000000000000..b41257cde09a5 --- /dev/null +++ b/drivers/media/platform/starfive/stf_video.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * stf_video.h + * + * StarFive Camera Subsystem - V4L2 device node + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ + +#ifndef STF_VIDEO_H +#define STF_VIDEO_H + +#include +#include +#include +#include +#include +#include + +#define STFCAMSS_FRAME_MIN_WIDTH 64 +#define STFCAMSS_FRAME_MAX_WIDTH 1920 +#define STFCAMSS_FRAME_MIN_HEIGHT 64 +#define STFCAMSS_FRAME_MAX_HEIGHT 1080 +#define STFCAMSS_FRAME_WIDTH_ALIGN_8 8 +#define STFCAMSS_FRAME_WIDTH_ALIGN_128 128 +#define STFCAMSS_MIN_BUFFERS 2 + +#define STFCAMSS_MAX_ENTITY_NAME_LEN 27 + +struct stfcamss_buffer { + struct vb2_v4l2_buffer vb; + dma_addr_t addr[3]; + struct list_head queue; + int sizeimage; +}; + +struct fract { + u8 numerator; + u8 denominator; +}; + +/* + * struct stfcamss_format_info - ISP media bus format information + * @code: V4L2 media bus format code + * @pixelformat: V4L2 pixel format FCC identifier + * @planes: Number of planes + * @hsub: Horizontal subsampling (for each plane) + * @vsub: Vertical subsampling (for each plane) + * @bpp: Bits per pixel when stored in memory (for each plane) + */ +struct stfcamss_format_info { + u32 code; + u32 pixelformat; + u8 planes; + struct fract hsub[3]; + struct fract vsub[3]; + u8 bpp[3]; +}; + +struct stfcamss_video { + struct stfcamss *stfcamss; + u8 id; + struct vb2_queue vb2_q; + struct video_device vdev; + struct media_pad pad; + struct media_pipeline pipe; + struct v4l2_format active_fmt; + enum v4l2_buf_type type; + const struct stfcamss_video_ops *ops; + struct mutex lock; /* serialize device access */ + struct mutex q_lock; /* protects the queue */ + unsigned int bpl_alignment; + const struct stfcamss_format_info *formats; + unsigned int nformats; +}; + +struct stfcamss_video_ops { + int (*queue_buffer)(struct stfcamss_video *vid, + struct stfcamss_buffer *buf); + int (*flush_buffers)(struct stfcamss_video *vid, + enum vb2_buffer_state state); +}; + +int stf_video_register(struct stfcamss_video *video, + struct v4l2_device *v4l2_dev, const char *name); + +void stf_video_unregister(struct stfcamss_video *video); + +#endif /* STF_VIDEO_H */ diff --git a/drivers/media/platform/starfive/stf_vin.c b/drivers/media/platform/starfive/stf_vin.c new file mode 100644 index 0000000000000..38d0e2d74280b --- /dev/null +++ b/drivers/media/platform/starfive/stf_vin.c @@ -0,0 +1,1314 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * stf_vin.c + * + * StarFive Camera Subsystem - VIN Module + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ +#include + +#include "stf_camss.h" + +#define vin_line_array(ptr_line) \ + ((const struct vin_line (*)[]) &(ptr_line)[-((ptr_line)->id)]) + +#define line_to_vin_dev(ptr_line) \ + container_of(vin_line_array(ptr_line), struct stf_vin_dev, line) + +#define VIN_FRAME_DROP_MAX_VAL 90 +#define VIN_FRAME_DROP_MIN_VAL 4 +#define VIN_FRAME_PER_SEC_MAX_VAL 90 + +/* ISP ctrl need 1 sec to let frames become stable. */ +#define VIN_FRAME_DROP_SEC_FOR_ISP_CTRL 1 + +static const struct vin_format vin_formats_wr[] = { + { MEDIA_BUS_FMT_SRGGB10_1X10, 10}, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10}, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10}, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10}, +}; + +static const struct vin_format vin_formats_raw[] = { + { MEDIA_BUS_FMT_SBGGR12_1X12, 12}, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12}, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12}, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12}, +}; + +static const struct vin_format vin_formats_uo[] = { + { MEDIA_BUS_FMT_Y12_1X12, 8}, +}; + +static const struct vin_format_table vin_formats_table[] = { + /* VIN_LINE_WR */ + { vin_formats_wr, ARRAY_SIZE(vin_formats_wr) }, + /* VIN_LINE_ISP */ + { vin_formats_uo, ARRAY_SIZE(vin_formats_uo) }, + /* VIN_LINE_ISP_SS0 */ + { vin_formats_uo, ARRAY_SIZE(vin_formats_uo) }, + /* VIN_LINE_ISP_SS1 */ + { vin_formats_uo, ARRAY_SIZE(vin_formats_uo) }, + /* VIN_LINE_ISP_RAW */ + { vin_formats_raw, ARRAY_SIZE(vin_formats_raw) }, +}; + +static void vin_buffer_done(struct vin_line *line); +static void vin_change_buffer(struct vin_line *line); +static struct stfcamss_buffer *vin_buf_get_pending(struct vin_output *output); +static void vin_output_init_addrs(struct vin_line *line); +static void vin_init_outputs(struct vin_line *line); +static struct v4l2_mbus_framefmt * +__vin_get_format(struct vin_line *line, + struct v4l2_subdev_state *state, + unsigned int pad, + enum v4l2_subdev_format_whence which); + +static char *vin_get_line_subdevname(int line_id) +{ + char *name = NULL; + + switch (line_id) { + case VIN_LINE_WR: + name = "wr"; + break; + case VIN_LINE_ISP: + name = "isp0"; + break; + case VIN_LINE_ISP_SS0: + name = "isp0_ss0"; + break; + case VIN_LINE_ISP_SS1: + name = "isp0_ss1"; + break; + case VIN_LINE_ISP_RAW: + name = "isp0_raw"; + break; + default: + name = "unknown"; + break; + } + return name; +} + +static enum isp_line_id vin_map_isp_line(enum vin_line_id line) +{ + enum isp_line_id line_id; + + if (line > VIN_LINE_WR && line < VIN_LINE_MAX) { + line_id = line % STF_ISP_LINE_MAX; + if (line_id == 0) + line_id = STF_ISP_LINE_SRC_RAW; + } else { + line_id = STF_ISP_LINE_INVALID; + } + + return line_id; +} + +enum isp_pad_id stf_vin_map_isp_pad(enum vin_line_id line, enum isp_pad_id def) +{ + enum isp_pad_id pad_id; + + if (line == VIN_LINE_WR) + pad_id = STF_ISP_PAD_SINK; + else if ((line > VIN_LINE_WR) && (line < VIN_LINE_MAX)) + pad_id = vin_map_isp_line(line); + else + pad_id = def; + + return pad_id; +} + +int stf_vin_subdev_init(struct stfcamss *stfcamss) +{ + struct device *dev = stfcamss->dev; + struct stf_vin_dev *vin_dev = stfcamss->vin_dev; + int i, ret = 0; + + vin_dev->stfcamss = stfcamss; + vin_dev->hw_ops = &vin_ops; + + vin_dev->isr_ops = devm_kzalloc(dev, sizeof(*vin_dev->isr_ops), + GFP_KERNEL); + if (!vin_dev->isr_ops) + return -ENOMEM; + vin_dev->isr_ops->isr_buffer_done = vin_buffer_done; + vin_dev->isr_ops->isr_change_buffer = vin_change_buffer; + + atomic_set(&vin_dev->ref_count, 0); + + ret = devm_request_irq(dev, + stfcamss->irq, + vin_dev->hw_ops->vin_wr_irq_handler, + 0, "vin_axiwr_irq", vin_dev); + if (ret) { + st_err(ST_VIN, "failed to request irq\n"); + goto out; + } + + ret = devm_request_irq(dev, + stfcamss->isp_irq, + vin_dev->hw_ops->vin_isp_irq_handler, + 0, "vin_isp_irq", vin_dev); + if (ret) { + st_err(ST_VIN, "failed to request isp irq\n"); + goto out; + } + + ret = devm_request_irq(dev, + stfcamss->isp_irq_csi, + vin_dev->hw_ops->vin_isp_irq_csi_handler, + 0, "vin_isp_irq_csi", vin_dev); + if (ret) { + st_err(ST_VIN, "failed to request isp raw irq\n"); + goto out; + } + + ret = devm_request_irq(dev, + stfcamss->isp_irq_csiline, + vin_dev->hw_ops->vin_isp_irq_csiline_handler, + 0, "vin_isp_irq_csiline", vin_dev); + if (ret) { + st_err(ST_VIN, "failed to request isp irq csiline\n"); + goto out; + } + + mutex_init(&vin_dev->power_lock); + vin_dev->power_count = 0; + + for (i = 0; i < STF_DUMMY_MODULE_NUMS; i++) { + struct dummy_buffer *dummy_buffer = &vin_dev->dummy_buffer[i]; + + mutex_init(&dummy_buffer->stream_lock); + dummy_buffer->nums = + i == 0 ? VIN_DUMMY_BUFFER_NUMS : ISP_DUMMY_BUFFER_NUMS; + dummy_buffer->stream_count = 0; + dummy_buffer->buffer = + devm_kzalloc(dev, + dummy_buffer->nums * sizeof(struct vin_dummy_buffer), + GFP_KERNEL); + atomic_set(&dummy_buffer->frame_skip, 0); + } + + for (i = VIN_LINE_WR; + i < STF_ISP_LINE_MAX + 1; i++) { + struct vin_line *l = &vin_dev->line[i]; + + l->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + l->video_out.stfcamss = stfcamss; + l->id = i; + l->sdev_type = SUBDEV_TYPE_VIN; + l->formats = vin_formats_table[i].fmts; + l->nformats = vin_formats_table[i].nfmts; + spin_lock_init(&l->output_lock); + + mutex_init(&l->stream_lock); + l->stream_count = 0; + mutex_init(&l->power_lock); + l->power_count = 0; + } + + return 0; +out: + return ret; +} + +static enum link vin_get_link(struct media_entity *entity) +{ + struct v4l2_subdev *subdev; + struct media_pad *pad; + bool isp = false; + + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + return LINK_ERROR; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + return LINK_ERROR; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + if (!strncmp(subdev->name, STF_CSI_NAME, + strlen(STF_CSI_NAME))) { + if (isp) + return LINK_CSI_TO_ISP; + else + return LINK_CSI_TO_WR; + } else if (!strncmp(subdev->name, STF_DVP_NAME, + strlen(STF_DVP_NAME))) { + if (isp) + return LINK_DVP_TO_ISP; + else + return LINK_DVP_TO_WR; + } else if (!strncmp(subdev->name, STF_ISP_NAME, + strlen(STF_ISP_NAME))) { + isp = true; + } else { + return LINK_ERROR; + } + } +} + +static int vin_set_power(struct v4l2_subdev *sd, int on) +{ + struct vin_line *line = v4l2_get_subdevdata(sd); + struct stf_vin_dev *vin_dev = line_to_vin_dev(line); + struct stfcamss *stfcamss = vin_dev->stfcamss; + enum link link; + + mutex_lock(&line->power_lock); + if (on) { + if (line->power_count == 0) + vin_init_outputs(line); + line->power_count++; + } else { + if (line->power_count == 0) { + st_err(ST_VIN, "line power off on power_count = 0\n"); + goto exit_line; + } + line->power_count--; + } +exit_line: + mutex_unlock(&line->power_lock); + + mutex_lock(&vin_dev->power_lock); + link = vin_get_link(&sd->entity); + if (link == LINK_ERROR) + goto exit; + + if (on) { + if (vin_dev->power_count == 0) { + pm_runtime_get_sync(stfcamss->dev); + vin_dev->hw_ops->vin_clk_enable(vin_dev, link); + } + vin_dev->power_count++; + } else { + if (vin_dev->power_count == 0) { + st_err(ST_VIN, "vin_dev power off on power_count=0\n"); + goto exit; + } + if (vin_dev->power_count == 1) { + vin_dev->hw_ops->vin_clk_disable(vin_dev, link); + pm_runtime_put_sync(stfcamss->dev); + } + vin_dev->power_count--; + } +exit: + + mutex_unlock(&vin_dev->power_lock); + + return 0; +} + +static unsigned int vin_get_frame_skip(struct vin_line *line) +{ + unsigned int frame_skip = 0; + unsigned int isp_ctrl_skip_frames = 0; + struct media_entity *sensor; + struct v4l2_subdev_frame_interval fi = { + .pad = 0, + }; + + sensor = stfcamss_find_sensor(&line->subdev.entity); + if (sensor) { + int fps = 0; + struct v4l2_subdev *subdev = + media_entity_to_v4l2_subdev(sensor); + + v4l2_subdev_call(subdev, video, g_frame_interval, &fi); + fps = fi.interval.denominator; + + if (fps > 0 && fps <= 90) + isp_ctrl_skip_frames = + fps * VIN_FRAME_DROP_SEC_FOR_ISP_CTRL; + + if (!fps) + st_debug(ST_VIN, + "%s, Failed to get sensor fps !\n", __func__); + + if (isp_ctrl_skip_frames <= VIN_FRAME_DROP_MIN_VAL) + isp_ctrl_skip_frames = VIN_FRAME_DROP_MIN_VAL; + + v4l2_subdev_call(subdev, sensor, g_skip_frames, &frame_skip); + + frame_skip += isp_ctrl_skip_frames; + + if (frame_skip > VIN_FRAME_DROP_MAX_VAL) + frame_skip = VIN_FRAME_DROP_MAX_VAL; + st_debug(ST_VIN, "%s, frame_skip %d\n", __func__, frame_skip); + } + + return frame_skip; +} + +static int vin_enable_output(struct vin_line *line) +{ + struct vin_output *output = &line->output; + unsigned long flags; + + spin_lock_irqsave(&line->output_lock, flags); + + output->state = VIN_OUTPUT_IDLE; + + output->buf[0] = vin_buf_get_pending(output); + + if (!output->buf[0] && output->buf[1]) { + output->buf[0] = output->buf[1]; + output->buf[1] = NULL; + } + + if (output->buf[0]) + output->state = VIN_OUTPUT_SINGLE; + + output->sequence = 0; + + vin_output_init_addrs(line); + spin_unlock_irqrestore(&line->output_lock, flags); + return 0; +} + +static int vin_disable_output(struct vin_line *line) +{ + struct vin_output *output = &line->output; + unsigned long flags; + + spin_lock_irqsave(&line->output_lock, flags); + + output->state = VIN_OUTPUT_OFF; + + spin_unlock_irqrestore(&line->output_lock, flags); + return 0; +} + +static u32 vin_line_to_dummy_module(struct vin_line *line) +{ + u32 dummy_module = 0; + + switch (line->id) { + case VIN_LINE_WR: + dummy_module = STF_DUMMY_VIN; + break; + case VIN_LINE_ISP: + case VIN_LINE_ISP_SS0: + case VIN_LINE_ISP_SS1: + case VIN_LINE_ISP_RAW: + dummy_module = STF_DUMMY_ISP; + break; + default: + dummy_module = STF_DUMMY_VIN; + break; + } + + return dummy_module; +} + +static int vin_alloc_dummy_buffer(struct stf_vin_dev *vin_dev, + struct v4l2_mbus_framefmt *fmt, + int dummy_module) +{ + struct device *dev = vin_dev->stfcamss->dev; + struct dummy_buffer *dummy_buffer = + &vin_dev->dummy_buffer[dummy_module]; + struct vin_dummy_buffer *buffer = NULL; + int ret = 0, i; + u32 aligns; + + for (i = 0; i < dummy_buffer->nums; i++) { + buffer = &vin_dev->dummy_buffer[dummy_module].buffer[i]; + buffer->width = fmt->width; + buffer->height = fmt->height; + buffer->mcode = fmt->code; + if (i == STF_VIN_PAD_SINK) { + aligns = ALIGN(fmt->width * 4, + STFCAMSS_FRAME_WIDTH_ALIGN_8); + buffer->buffer_size = PAGE_ALIGN(aligns * fmt->height); + } else if (i == STF_ISP_PAD_SRC || + i == STF_ISP_PAD_SRC_SS0 || + i == STF_ISP_PAD_SRC_SS1) { + aligns = ALIGN(fmt->width, + STFCAMSS_FRAME_WIDTH_ALIGN_8); + buffer->buffer_size = + PAGE_ALIGN(aligns * fmt->height * 3 / 2); + } else if (i == STF_ISP_PAD_SRC_RAW) { + aligns = ALIGN(fmt->width * ISP_RAW_DATA_BITS / 8, + STFCAMSS_FRAME_WIDTH_ALIGN_128); + buffer->buffer_size = PAGE_ALIGN(aligns * fmt->height); + } else { + continue; + } + + buffer->vaddr = dma_alloc_coherent(dev, + buffer->buffer_size, + &buffer->paddr[0], + GFP_DMA | GFP_KERNEL); + + if (buffer->vaddr) { + if (i == STF_ISP_PAD_SRC || + i == STF_ISP_PAD_SRC_SS0 || + i == STF_ISP_PAD_SRC_SS1) + buffer->paddr[1] = + (dma_addr_t)(buffer->paddr[0] + aligns * fmt->height); + else + st_debug(ST_VIN, "signal plane\n"); + } + } + + return ret; +} + +static void vin_free_dummy_buffer(struct stf_vin_dev *vin_dev, int dummy_module) +{ + struct device *dev = vin_dev->stfcamss->dev; + struct dummy_buffer *dummy_buffer = + &vin_dev->dummy_buffer[dummy_module]; + struct vin_dummy_buffer *buffer = NULL; + int i; + + for (i = 0; i < dummy_buffer->nums; i++) { + buffer = &dummy_buffer->buffer[i]; + if (buffer->vaddr) + dma_free_coherent(dev, buffer->buffer_size, + buffer->vaddr, buffer->paddr[0]); + memset(buffer, 0, sizeof(struct vin_dummy_buffer)); + } +} + +static void vin_set_dummy_buffer(struct vin_line *line, u32 pad) +{ + struct stf_vin_dev *vin_dev = line_to_vin_dev(line); + int dummy_module = vin_line_to_dummy_module(line); + struct dummy_buffer *dummy_buffer = + &vin_dev->dummy_buffer[dummy_module]; + struct vin_dummy_buffer *buffer = NULL; + + switch (pad) { + case STF_VIN_PAD_SINK: + if (line->id == VIN_LINE_WR) { + buffer = &dummy_buffer->buffer[STF_VIN_PAD_SINK]; + vin_dev->hw_ops->vin_wr_set_ping_addr(vin_dev, + buffer->paddr[0]); + vin_dev->hw_ops->vin_wr_set_pong_addr(vin_dev, + buffer->paddr[0]); + } else { + buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC]; + vin_dev->hw_ops->vin_isp_set_yuv_addr(vin_dev, + buffer->paddr[0], + buffer->paddr[1]); + + buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_SS0]; + vin_dev->hw_ops->vin_isp_set_ss0_addr(vin_dev, + buffer->paddr[0], + buffer->paddr[1]); + + buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_SS1]; + vin_dev->hw_ops->vin_isp_set_ss1_addr(vin_dev, + buffer->paddr[0], + buffer->paddr[1]); + + buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_RAW]; + vin_dev->hw_ops->vin_isp_set_raw_addr(vin_dev, + buffer->paddr[0]); + } + break; + case STF_ISP_PAD_SRC: + buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC]; + vin_dev->hw_ops->vin_isp_set_yuv_addr(vin_dev, + buffer->paddr[0], + buffer->paddr[1]); + break; + case STF_ISP_PAD_SRC_SS0: + buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_SS0]; + vin_dev->hw_ops->vin_isp_set_ss0_addr(vin_dev, + buffer->paddr[0], + buffer->paddr[1]); + break; + case STF_ISP_PAD_SRC_SS1: + buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_SS1]; + vin_dev->hw_ops->vin_isp_set_ss1_addr(vin_dev, + buffer->paddr[0], + buffer->paddr[1]); + break; + case STF_ISP_PAD_SRC_RAW: + buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_RAW]; + vin_dev->hw_ops->vin_isp_set_raw_addr(vin_dev, + buffer->paddr[0]); + break; + default: + break; + } +} + +static int vin_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vin_line *line = v4l2_get_subdevdata(sd); + struct stf_vin_dev *vin_dev = line_to_vin_dev(line); + int dummy_module = vin_line_to_dummy_module(line); + struct dummy_buffer *dummy_buffer = + &vin_dev->dummy_buffer[dummy_module]; + struct v4l2_mbus_framefmt *fmt; + struct media_entity *sensor; + struct v4l2_subdev *sensor_sd; + struct v4l2_subdev_format sensor_fmt; + enum link link; + int ret; + + sensor = stfcamss_find_sensor(&line->subdev.entity); + if (!sensor) + return -EINVAL; + + sensor_sd = media_entity_to_v4l2_subdev(sensor); + + sensor_fmt.pad = 0; + sensor_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(sensor_sd, pad, get_fmt, NULL, &sensor_fmt); + if (ret) { + st_warn(ST_VIN, "Sensor get format failed !!\n"); + return -EINVAL; + } + + fmt = __vin_get_format(line, NULL, + STF_VIN_PAD_SINK, V4L2_SUBDEV_FORMAT_ACTIVE); + mutex_lock(&dummy_buffer->stream_lock); + if (enable) { + if (dummy_buffer->stream_count == 0) { + vin_alloc_dummy_buffer(vin_dev, fmt, dummy_module); + vin_set_dummy_buffer(line, STF_VIN_PAD_SINK); + atomic_set(&dummy_buffer->frame_skip, + vin_get_frame_skip(line)); + } + dummy_buffer->stream_count++; + } else { + if (dummy_buffer->stream_count == 1) { + vin_free_dummy_buffer(vin_dev, dummy_module); + /* set buffer addr to zero */ + vin_set_dummy_buffer(line, STF_VIN_PAD_SINK); + } else { + vin_set_dummy_buffer(line, + stf_vin_map_isp_pad(line->id, STF_ISP_PAD_SINK)); + } + + dummy_buffer->stream_count--; + } + mutex_unlock(&dummy_buffer->stream_lock); + + mutex_lock(&line->stream_lock); + link = vin_get_link(&sd->entity); + if (link == LINK_ERROR) + goto exit; + + if (enable) { + if (line->stream_count == 0) { + vin_dev->hw_ops->vin_stream_set(vin_dev, 1, + sensor_fmt, link); + if (line->id == VIN_LINE_WR) { + vin_dev->hw_ops->vin_wr_irq_enable(vin_dev, 1); + vin_dev->hw_ops->vin_wr_stream_set(vin_dev, 1); + } + } + line->stream_count++; + } else { + if (line->stream_count == 1) { + if (line->id == VIN_LINE_WR) { + vin_dev->hw_ops->vin_wr_irq_enable(vin_dev, 0); + vin_dev->hw_ops->vin_wr_stream_set(vin_dev, 0); + } + vin_dev->hw_ops->vin_stream_set(vin_dev, 0, + sensor_fmt, link); + } + line->stream_count--; + } +exit: + mutex_unlock(&line->stream_lock); + + if (enable) + vin_enable_output(line); + else + vin_disable_output(line); + + return 0; +} + +static struct v4l2_mbus_framefmt * +__vin_get_format(struct vin_line *line, + struct v4l2_subdev_state *state, + unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(&line->subdev, state, pad); + return &line->fmt[pad]; +} + +static void vin_try_format(struct vin_line *line, + struct v4l2_subdev_state *state, + unsigned int pad, + struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + unsigned int i; + + switch (pad) { + case STF_VIN_PAD_SINK: + /* Set format on sink pad */ + for (i = 0; i < line->nformats; i++) + if (fmt->code == line->formats[i].code) + break; + + /* If not found, use UYVY as default */ + if (i >= line->nformats) + fmt->code = line->formats[0].code; + + fmt->width = clamp_t(u32, fmt->width, + STFCAMSS_FRAME_MIN_WIDTH, + STFCAMSS_FRAME_MAX_WIDTH); + fmt->height = clamp_t(u32, fmt->height, + STFCAMSS_FRAME_MIN_HEIGHT, + STFCAMSS_FRAME_MAX_HEIGHT); + + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->flags = 0; + break; + + case STF_VIN_PAD_SRC: + /* Set and return a format same as sink pad */ + *fmt = *__vin_get_format(line, state, STF_VIN_PAD_SINK, which); + break; + } + + fmt->colorspace = V4L2_COLORSPACE_SRGB; +} + +static int vin_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct vin_line *line = v4l2_get_subdevdata(sd); + + if (code->index >= line->nformats) + return -EINVAL; + if (code->pad == STF_VIN_PAD_SINK) { + code->code = line->formats[code->index].code; + } else { + struct v4l2_mbus_framefmt *sink_fmt; + + sink_fmt = __vin_get_format(line, state, STF_VIN_PAD_SINK, + code->which); + + code->code = sink_fmt->code; + if (!code->code) + return -EINVAL; + } + code->flags = 0; + + return 0; +} + +static int vin_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct vin_line *line = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + vin_try_format(line, state, fse->pad, &format, fse->which); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + vin_try_format(line, state, fse->pad, &format, fse->which); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +static int vin_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct vin_line *line = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __vin_get_format(line, state, fmt->pad, fmt->which); + if (!format) + return -EINVAL; + + fmt->format = *format; + + return 0; +} + +static int vin_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct vin_line *line = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + st_debug(ST_VIDEO, "%s, pad %d, fmt code %x\n", + __func__, fmt->pad, fmt->format.code); + + format = __vin_get_format(line, state, fmt->pad, fmt->which); + if (!format) + return -EINVAL; + + mutex_lock(&line->stream_lock); + if (line->stream_count) { + fmt->format = *format; + mutex_unlock(&line->stream_lock); + goto out; + } else { + vin_try_format(line, state, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + } + mutex_unlock(&line->stream_lock); + + if (fmt->pad == STF_VIN_PAD_SINK) { + /* Propagate the format from sink to source */ + format = __vin_get_format(line, state, STF_VIN_PAD_SRC, + fmt->which); + + *format = fmt->format; + vin_try_format(line, state, STF_VIN_PAD_SRC, format, + fmt->which); + } + +out: + return 0; +} + +static int vin_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format = { + .pad = STF_VIN_PAD_SINK, + .which = fh ? + V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE, + .format = { + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .width = 1920, + .height = 1080 + } + }; + + return vin_set_format(sd, fh ? fh->state : NULL, &format); +} + +static void vin_output_init_addrs(struct vin_line *line) +{ + struct vin_output *output = &line->output; + struct stf_vin_dev *vin_dev = line_to_vin_dev(line); + dma_addr_t ping_addr; + dma_addr_t pong_addr; + dma_addr_t y_addr, uv_addr; + + output->active_buf = 0; + + if (output->buf[0]) { + ping_addr = output->buf[0]->addr[0]; + y_addr = output->buf[0]->addr[0]; + uv_addr = output->buf[0]->addr[1]; + } else { + return; + } + + if (output->buf[1]) + pong_addr = output->buf[1]->addr[0]; + else + pong_addr = ping_addr; + + switch (vin_map_isp_line(line->id)) { + case STF_ISP_LINE_SRC: + vin_dev->hw_ops->vin_isp_set_yuv_addr(vin_dev, y_addr, uv_addr); + break; + case STF_ISP_LINE_SRC_SS0: + vin_dev->hw_ops->vin_isp_set_ss0_addr(vin_dev, y_addr, uv_addr); + break; + case STF_ISP_LINE_SRC_SS1: + vin_dev->hw_ops->vin_isp_set_ss1_addr(vin_dev, y_addr, uv_addr); + break; + case STF_ISP_LINE_SRC_RAW: + vin_dev->hw_ops->vin_isp_set_raw_addr(vin_dev, y_addr); + break; + default: + if (line->id == VIN_LINE_WR) { + vin_dev->hw_ops->vin_wr_set_ping_addr(vin_dev, + ping_addr); + vin_dev->hw_ops->vin_wr_set_pong_addr(vin_dev, + ping_addr); + } + break; + } +} + +static void vin_init_outputs(struct vin_line *line) +{ + struct vin_output *output = &line->output; + + output->state = VIN_OUTPUT_OFF; + output->buf[0] = NULL; + output->buf[1] = NULL; + output->active_buf = 0; + INIT_LIST_HEAD(&output->pending_bufs); + INIT_LIST_HEAD(&output->ready_bufs); +} + +static void vin_buf_add_ready(struct vin_output *output, + struct stfcamss_buffer *buffer) +{ + INIT_LIST_HEAD(&buffer->queue); + list_add_tail(&buffer->queue, &output->ready_bufs); +} + +static struct stfcamss_buffer *vin_buf_get_ready(struct vin_output *output) +{ + struct stfcamss_buffer *buffer = NULL; + + if (!list_empty(&output->ready_bufs)) { + buffer = list_first_entry(&output->ready_bufs, + struct stfcamss_buffer, + queue); + list_del(&buffer->queue); + } + + return buffer; +} + +static void vin_buf_add_pending(struct vin_output *output, + struct stfcamss_buffer *buffer) +{ + INIT_LIST_HEAD(&buffer->queue); + list_add_tail(&buffer->queue, &output->pending_bufs); +} + +static struct stfcamss_buffer *vin_buf_get_pending(struct vin_output *output) +{ + struct stfcamss_buffer *buffer = NULL; + + if (!list_empty(&output->pending_bufs)) { + buffer = list_first_entry(&output->pending_bufs, + struct stfcamss_buffer, + queue); + list_del(&buffer->queue); + } + + return buffer; +} + +static void vin_buf_update_on_last(struct vin_line *line) +{ + struct vin_output *output = &line->output; + + switch (output->state) { + case VIN_OUTPUT_CONTINUOUS: + output->state = VIN_OUTPUT_SINGLE; + output->active_buf = !output->active_buf; + break; + case VIN_OUTPUT_SINGLE: + output->state = VIN_OUTPUT_STOPPING; + break; + default: + st_err_ratelimited(ST_VIN, "Last buff in wrong state! %d\n", + output->state); + break; + } +} + +static void vin_buf_update_on_next(struct vin_line *line) +{ + struct vin_output *output = &line->output; + + switch (output->state) { + case VIN_OUTPUT_CONTINUOUS: + output->active_buf = !output->active_buf; + break; + case VIN_OUTPUT_SINGLE: + default: + break; + } +} + +static void vin_buf_update_on_new(struct vin_line *line, + struct vin_output *output, + struct stfcamss_buffer *new_buf) +{ + switch (output->state) { + case VIN_OUTPUT_SINGLE: + vin_buf_add_pending(output, new_buf); + break; + case VIN_OUTPUT_IDLE: + st_warn(ST_VIN, "Output idle buffer set!\n"); + if (!output->buf[0]) { + output->buf[0] = new_buf; + vin_output_init_addrs(line); + output->state = VIN_OUTPUT_SINGLE; + } else { + vin_buf_add_pending(output, new_buf); + st_warn(ST_VIN, "Output idle with buffer set!\n"); + } + break; + case VIN_OUTPUT_STOPPING: + if (output->last_buffer) { + output->buf[output->active_buf] = output->last_buffer; + output->last_buffer = NULL; + } else { + st_err(ST_VIN, "stop state lost lastbuffer!\n"); + } + output->state = VIN_OUTPUT_SINGLE; + vin_buf_add_pending(output, new_buf); + break; + case VIN_OUTPUT_CONTINUOUS: + default: + vin_buf_add_pending(output, new_buf); + break; + } +} + +static void vin_buf_flush(struct vin_output *output, + enum vb2_buffer_state state) +{ + struct stfcamss_buffer *buf; + struct stfcamss_buffer *t; + + list_for_each_entry_safe(buf, t, &output->pending_bufs, queue) { + vb2_buffer_done(&buf->vb.vb2_buf, state); + list_del(&buf->queue); + } + list_for_each_entry_safe(buf, t, &output->ready_bufs, queue) { + vb2_buffer_done(&buf->vb.vb2_buf, state); + list_del(&buf->queue); + } +} + +static void vin_buffer_done(struct vin_line *line) +{ + struct stfcamss_buffer *ready_buf; + struct vin_output *output = &line->output; + unsigned long flags; + u64 ts = ktime_get_ns(); + + if (output->state == VIN_OUTPUT_OFF || + output->state == VIN_OUTPUT_RESERVED) + return; + + spin_lock_irqsave(&line->output_lock, flags); + + while ((ready_buf = vin_buf_get_ready(output))) { + ready_buf->vb.vb2_buf.timestamp = ts; + ready_buf->vb.sequence = output->sequence++; + + vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + } + + spin_unlock_irqrestore(&line->output_lock, flags); +} + +static void vin_change_buffer(struct vin_line *line) +{ + struct stfcamss_buffer *ready_buf; + struct vin_output *output = &line->output; + struct stf_vin_dev *vin_dev = line_to_vin_dev(line); + dma_addr_t *new_addr; + unsigned long flags; + u32 active_index; + + if (output->state == VIN_OUTPUT_OFF || + output->state == VIN_OUTPUT_STOPPING || + output->state == VIN_OUTPUT_RESERVED || + output->state == VIN_OUTPUT_IDLE) + return; + + spin_lock_irqsave(&line->output_lock, flags); + + active_index = output->active_buf; + + ready_buf = output->buf[active_index]; + if (!ready_buf) { + st_err_ratelimited(ST_VIN, "Missing ready buf %d %d!\n", + active_index, output->state); + active_index = !active_index; + ready_buf = output->buf[active_index]; + if (!ready_buf) { + st_err_ratelimited(ST_VIN, "Missing ready buf2 %d %d!\n", + active_index, output->state); + goto out_unlock; + } + } + + /* Get next buffer */ + output->buf[active_index] = vin_buf_get_pending(output); + if (!output->buf[active_index]) { + /* No next buffer - set same address */ + new_addr = ready_buf->addr; + vin_buf_update_on_last(line); + } else { + new_addr = output->buf[active_index]->addr; + vin_buf_update_on_next(line); + } + + if (output->state == VIN_OUTPUT_STOPPING) { + output->last_buffer = ready_buf; + } else { + switch (vin_map_isp_line(line->id)) { + case STF_ISP_LINE_SRC: + vin_dev->hw_ops->vin_isp_set_yuv_addr(vin_dev, + new_addr[0], + new_addr[1]); + break; + case STF_ISP_LINE_SRC_SS0: + vin_dev->hw_ops->vin_isp_set_ss0_addr(vin_dev, + new_addr[0], + new_addr[1]); + break; + case STF_ISP_LINE_SRC_SS1: + vin_dev->hw_ops->vin_isp_set_ss1_addr(vin_dev, + new_addr[0], + new_addr[1]); + break; + case STF_ISP_LINE_SRC_RAW: + vin_dev->hw_ops->vin_isp_set_raw_addr(vin_dev, + new_addr[0]); + break; + default: + if (line->id == VIN_LINE_WR) { + vin_dev->hw_ops->vin_wr_set_ping_addr(vin_dev, + new_addr[0]); + vin_dev->hw_ops->vin_wr_set_pong_addr(vin_dev, + new_addr[0]); + } + break; + } + + vin_buf_add_ready(output, ready_buf); + } + + spin_unlock_irqrestore(&line->output_lock, flags); + return; + +out_unlock: + spin_unlock_irqrestore(&line->output_lock, flags); +} + +static int vin_queue_buffer(struct stfcamss_video *vid, + struct stfcamss_buffer *buf) +{ + struct vin_line *line = container_of(vid, struct vin_line, video_out); + struct vin_output *output; + unsigned long flags; + + output = &line->output; + + spin_lock_irqsave(&line->output_lock, flags); + + vin_buf_update_on_new(line, output, buf); + + spin_unlock_irqrestore(&line->output_lock, flags); + + return 0; +} + +static int vin_flush_buffers(struct stfcamss_video *vid, + enum vb2_buffer_state state) +{ + struct vin_line *line = container_of(vid, struct vin_line, video_out); + struct vin_output *output = &line->output; + unsigned long flags; + + spin_lock_irqsave(&line->output_lock, flags); + + vin_buf_flush(output, state); + if (output->buf[0]) + vb2_buffer_done(&output->buf[0]->vb.vb2_buf, state); + + if (output->buf[1]) + vb2_buffer_done(&output->buf[1]->vb.vb2_buf, state); + + if (output->last_buffer) { + vb2_buffer_done(&output->last_buffer->vb.vb2_buf, state); + output->last_buffer = NULL; + } + output->buf[0] = NULL; + output->buf[1] = NULL; + + spin_unlock_irqrestore(&line->output_lock, flags); + return 0; +} + +static int vin_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + if (flags & MEDIA_LNK_FL_ENABLED) + if (media_pad_remote_pad_first(local)) + return -EBUSY; + return 0; +} + +static const struct v4l2_subdev_core_ops vin_core_ops = { + .s_power = vin_set_power, +}; + +static const struct v4l2_subdev_video_ops vin_video_ops = { + .s_stream = vin_set_stream, +}; + +static const struct v4l2_subdev_pad_ops vin_pad_ops = { + .enum_mbus_code = vin_enum_mbus_code, + .enum_frame_size = vin_enum_frame_size, + .get_fmt = vin_get_format, + .set_fmt = vin_set_format, +}; + +static const struct v4l2_subdev_ops vin_v4l2_ops = { + .core = &vin_core_ops, + .video = &vin_video_ops, + .pad = &vin_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops vin_v4l2_internal_ops = { + .open = vin_init_formats, +}; + +static const struct stfcamss_video_ops stfcamss_vin_video_ops = { + .queue_buffer = vin_queue_buffer, + .flush_buffers = vin_flush_buffers, +}; + +static const struct media_entity_operations vin_media_ops = { + .link_setup = vin_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; + +int stf_vin_register(struct stf_vin_dev *vin_dev, struct v4l2_device *v4l2_dev) +{ + struct v4l2_subdev *sd; + struct stfcamss_video *video_out; + struct media_pad *pads; + int ret; + int i; + + for (i = 0; i < STF_ISP_LINE_MAX + 1; i++) { + char name[32]; + char *sub_name = vin_get_line_subdevname(i); + + sd = &vin_dev->line[i].subdev; + pads = vin_dev->line[i].pads; + video_out = &vin_dev->line[i].video_out; + video_out->id = i; + + v4l2_subdev_init(sd, &vin_v4l2_ops); + sd->internal_ops = &vin_v4l2_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s", + STF_VIN_NAME, 0, sub_name); + v4l2_set_subdevdata(sd, &vin_dev->line[i]); + + ret = vin_init_formats(sd, NULL); + if (ret < 0) { + st_err(ST_VIN, "Failed to init format: %d\n", ret); + goto err_init; + } + + pads[STF_VIN_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[STF_VIN_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; + + sd->entity.function = + MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; + sd->entity.ops = &vin_media_ops; + ret = media_entity_pads_init(&sd->entity, + STF_VIN_PADS_NUM, pads); + if (ret < 0) { + st_err(ST_VIN, "Failed to init media entity: %d\n", + ret); + goto err_init; + } + + ret = v4l2_device_register_subdev(v4l2_dev, sd); + if (ret < 0) { + st_err(ST_VIN, "Failed to register subdev: %d\n", ret); + goto err_reg_subdev; + } + + video_out->ops = &stfcamss_vin_video_ops; + video_out->bpl_alignment = 16 * 8; + + snprintf(name, ARRAY_SIZE(name), "%s_%s%d", + sd->name, "video", i); + ret = stf_video_register(video_out, v4l2_dev, name); + if (ret < 0) { + st_err(ST_VIN, "Failed to register video node: %d\n", + ret); + goto err_vid_reg; + } + + ret = media_create_pad_link( + &sd->entity, STF_VIN_PAD_SRC, + &video_out->vdev.entity, 0, + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + if (ret < 0) { + st_err(ST_VIN, "Failed to link %s->%s entities: %d\n", + sd->entity.name, video_out->vdev.entity.name, + ret); + goto err_create_link; + } + } + + return 0; + +err_create_link: + stf_video_unregister(video_out); +err_vid_reg: + v4l2_device_unregister_subdev(sd); +err_reg_subdev: + media_entity_cleanup(&sd->entity); +err_init: + for (i--; i >= 0; i--) { + sd = &vin_dev->line[i].subdev; + video_out = &vin_dev->line[i].video_out; + + stf_video_unregister(video_out); + v4l2_device_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + } + return ret; +} + +int stf_vin_unregister(struct stf_vin_dev *vin_dev) +{ + struct v4l2_subdev *sd; + struct stfcamss_video *video_out; + int i; + + mutex_destroy(&vin_dev->power_lock); + for (i = 0; i < STF_DUMMY_MODULE_NUMS; i++) + mutex_destroy(&vin_dev->dummy_buffer[i].stream_lock); + + for (i = 0; i < STF_ISP_LINE_MAX + 1; i++) { + sd = &vin_dev->line[i].subdev; + video_out = &vin_dev->line[i].video_out; + + stf_video_unregister(video_out); + v4l2_device_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + mutex_destroy(&vin_dev->line[i].stream_lock); + mutex_destroy(&vin_dev->line[i].power_lock); + } + return 0; +} diff --git a/drivers/media/platform/starfive/stf_vin.h b/drivers/media/platform/starfive/stf_vin.h new file mode 100644 index 0000000000000..7a0f39e6d2b9b --- /dev/null +++ b/drivers/media/platform/starfive/stf_vin.h @@ -0,0 +1,194 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * stf_vin.h + * + * StarFive Camera Subsystem - VIN Module + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ + +#ifndef STF_VIN_H +#define STF_VIN_H + +#include +#include +#include + +#include "stf_video.h" + +#define SYSCONSAIF_SYSCFG(x) (x) + +/* syscon offset 0 */ +#define U0_VIN_CNFG_AXI_DVP_EN BIT(2) + +/* syscon offset 20 */ +#define U0_VIN_CNFG_AXIWR0_CHANNEL_SEL GENMASK(3, 0) +#define U0_VIN_CNFG_AXIWR0_EN BIT(4) + +/* syscon offset 32 */ +#define U0_VIN_CNFG_AXIWR0_INTR_CLEAN BIT(0) +#define U0_VIN_CNFG_AXIWR0_INTR_MASK BIT(1) +#define U0_VIN_CNFG_AXIWR0_PIX_CNT_END GENMASK(12, 2) +#define U0_VIN_CNFG_AXIWR0_PIX_CT GENMASK(14, 13) +#define U0_VIN_CNFG_AXIWR0_PIXEL_HEIGH_BIT_SEL GENMASK(16, 15) + +/* syscon offset 36 */ +#define U0_VIN_CNFG_DVP_HS_POS BIT(1) +#define U0_VIN_CNFG_DVP_SWAP_EN BIT(2) +#define U0_VIN_CNFG_DVP_VS_POS BIT(3) +#define U0_VIN_CNFG_GEN_EN_AXIRD BIT(4) +#define U0_VIN_CNFG_ISP_DVP_EN0 BIT(5) +#define U0_VIN_CNFG_MIPI_BYTE_EN_ISP0 GENMASK(7, 6) +#define U0_VIN_CNFG_MIPI_CHANNEL_SEL0 GENMASK(11, 8) +#define U0_VIN_CNFG_P_I_MIPI_HAEDER_EN0 BIT(12) +#define U0_VIN_CNFG_PIX_NUM GENMASK(16, 13) + +#define STF_VIN_PAD_SINK 0 +#define STF_VIN_PAD_SRC 1 +#define STF_VIN_PADS_NUM 2 + +#define ISP_DUMMY_BUFFER_NUMS STF_ISP_PAD_MAX +#define VIN_DUMMY_BUFFER_NUMS 1 + +enum { + STF_DUMMY_VIN, + STF_DUMMY_ISP, + STF_DUMMY_MODULE_NUMS, +}; + +enum link { + LINK_ERROR = -1, + LINK_DVP_TO_WR, + LINK_DVP_TO_ISP, + LINK_CSI_TO_WR, + LINK_CSI_TO_ISP, +}; + +struct vin_format { + u32 code; + u8 bpp; +}; + +struct vin_format_table { + const struct vin_format *fmts; + int nfmts; +}; + +enum vin_output_state { + VIN_OUTPUT_OFF, + VIN_OUTPUT_RESERVED, + VIN_OUTPUT_SINGLE, + VIN_OUTPUT_CONTINUOUS, + VIN_OUTPUT_IDLE, + VIN_OUTPUT_STOPPING +}; + +struct vin_output { + int active_buf; + struct stfcamss_buffer *buf[2]; + struct stfcamss_buffer *last_buffer; + struct list_head pending_bufs; + struct list_head ready_bufs; + enum vin_output_state state; + unsigned int sequence; + unsigned int frame_skip; +}; + +/* + * The vin output lines include all isp controller lines, + * and one vin_wr output line. + */ +enum vin_line_id { + VIN_LINE_NONE = -1, + VIN_LINE_WR = 0, + VIN_LINE_ISP, + VIN_LINE_ISP_SS0, + VIN_LINE_ISP_SS1, + VIN_LINE_ISP_RAW, + VIN_LINE_MAX, +}; + +struct vin_line { + enum subdev_type sdev_type; /* must be frist */ + enum vin_line_id id; + struct v4l2_subdev subdev; + struct media_pad pads[STF_VIN_PADS_NUM]; + struct v4l2_mbus_framefmt fmt[STF_VIN_PADS_NUM]; + struct stfcamss_video video_out; + struct mutex stream_lock; /* serialize stream control */ + int stream_count; + struct mutex power_lock; /* serialize pipeline control in power process*/ + int power_count; + struct vin_output output; /* pipeline and stream states */ + spinlock_t output_lock; + const struct vin_format *formats; + unsigned int nformats; +}; + +struct vin_dummy_buffer { + dma_addr_t paddr[3]; + void *vaddr; + u32 buffer_size; + u32 width; + u32 height; + u32 mcode; +}; + +struct dummy_buffer { + struct vin_dummy_buffer *buffer; + u32 nums; + struct mutex stream_lock; /* protects buffer data */ + int stream_count; + atomic_t frame_skip; +}; + +struct vin_isr_ops { + void (*isr_buffer_done)(struct vin_line *line); + void (*isr_change_buffer)(struct vin_line *line); +}; + +struct stf_vin_dev { + struct stfcamss *stfcamss; + struct vin_line line[VIN_LINE_MAX]; + struct dummy_buffer dummy_buffer[STF_DUMMY_MODULE_NUMS]; + const struct vin_hw_ops *hw_ops; + struct vin_isr_ops *isr_ops; + atomic_t ref_count; + struct mutex power_lock; /* serialize power control*/ + int power_count; +}; + +struct vin_hw_ops { + int (*vin_clk_enable)(struct stf_vin_dev *vin_dev, enum link link); + int (*vin_clk_disable)(struct stf_vin_dev *vin_dev, enum link link); + int (*vin_wr_stream_set)(struct stf_vin_dev *vin_dev, int on); + int (*vin_stream_set)(struct stf_vin_dev *vin_dev, int on, + struct v4l2_subdev_format sensor_fmt, + enum link link); + void (*vin_wr_irq_enable)(struct stf_vin_dev *vin_dev, int enable); + void (*vin_wr_set_ping_addr)(struct stf_vin_dev *vin_dev, + dma_addr_t addr); + void (*vin_wr_set_pong_addr)(struct stf_vin_dev *vin_dev, + dma_addr_t addr); + void (*vin_isp_set_yuv_addr)(struct stf_vin_dev *vin_dev, + dma_addr_t y_addr, dma_addr_t uv_addr); + void (*vin_isp_set_raw_addr)(struct stf_vin_dev *vin_dev, + dma_addr_t raw_addr); + void (*vin_isp_set_ss0_addr)(struct stf_vin_dev *vin_dev, + dma_addr_t y_addr, dma_addr_t uv_addr); + void (*vin_isp_set_ss1_addr)(struct stf_vin_dev *vin_dev, + dma_addr_t y_addr, dma_addr_t uv_addr); + irqreturn_t (*vin_wr_irq_handler)(int irq, void *priv); + irqreturn_t (*vin_isp_irq_handler)(int irq, void *priv); + irqreturn_t (*vin_isp_irq_csi_handler)(int irq, void *priv); + irqreturn_t (*vin_isp_irq_csiline_handler)(int irq, void *priv); +}; + +extern const struct vin_hw_ops vin_ops; + +int stf_vin_subdev_init(struct stfcamss *stfcamss); +int stf_vin_register(struct stf_vin_dev *vin_dev, struct v4l2_device *v4l2_dev); +int stf_vin_unregister(struct stf_vin_dev *vin_dev); +enum isp_pad_id stf_vin_map_isp_pad(enum vin_line_id line, enum isp_pad_id def); + +#endif /* STF_VIN_H */ diff --git a/drivers/media/platform/starfive/stf_vin_hw_ops.c b/drivers/media/platform/starfive/stf_vin_hw_ops.c new file mode 100644 index 0000000000000..98306c4d4d892 --- /dev/null +++ b/drivers/media/platform/starfive/stf_vin_hw_ops.c @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * stf_vin_hw_ops.c + * + * Register interface file for StarFive VIN module driver + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ +#include "stf_camss.h" + +static void vin_intr_clear(void __iomem *syscon_base) +{ + reg_set_bit(syscon_base, SYSCONSAIF_SYSCFG(28), + U0_VIN_CNFG_AXIWR0_INTR_CLEAN, 0x1); + reg_set_bit(syscon_base, SYSCONSAIF_SYSCFG(28), + U0_VIN_CNFG_AXIWR0_INTR_CLEAN, 0x0); +} + +static irqreturn_t stf_vin_wr_irq_handler(int irq, void *priv) +{ + struct stf_vin_dev *vin_dev = priv; + struct stfcamss *stfcamss = vin_dev->stfcamss; + struct dummy_buffer *dummy_buffer = + &vin_dev->dummy_buffer[STF_DUMMY_VIN]; + + if (atomic_dec_if_positive(&dummy_buffer->frame_skip) < 0) { + vin_dev->isr_ops->isr_change_buffer(&vin_dev->line[VIN_LINE_WR]); + vin_dev->isr_ops->isr_buffer_done(&vin_dev->line[VIN_LINE_WR]); + } + + vin_intr_clear(stfcamss->syscon_base); + + return IRQ_HANDLED; +} + +static void __iomem *stf_vin_get_ispbase(struct stf_vin_dev *vin_dev) +{ + void __iomem *base = vin_dev->stfcamss->isp_base; + + return base; +} + +static irqreturn_t stf_vin_isp_irq_handler(int irq, void *priv) +{ + struct stf_vin_dev *vin_dev = priv; + void __iomem *ispbase = stf_vin_get_ispbase(vin_dev); + u32 int_status; + + int_status = reg_read(ispbase, ISP_REG_ISP_CTRL_0); + + if (int_status & BIT(24)) { + if ((int_status & BIT(11))) + vin_dev->isr_ops->isr_buffer_done( + &vin_dev->line[VIN_LINE_ISP_SS0]); + + if ((int_status & BIT(12))) + vin_dev->isr_ops->isr_buffer_done( + &vin_dev->line[VIN_LINE_ISP_SS1]); + + if ((int_status & BIT(20))) + vin_dev->isr_ops->isr_buffer_done( + &vin_dev->line[VIN_LINE_ISP]); + + if (int_status & BIT(25)) + vin_dev->isr_ops->isr_buffer_done( + &vin_dev->line[VIN_LINE_ISP_RAW]); + + /* clear interrupt */ + reg_write(ispbase, + ISP_REG_ISP_CTRL_0, + (int_status & ~EN_INT_ALL) | + EN_INT_ISP_DONE | + EN_INT_CSI_DONE | + EN_INT_SC_DONE); + + } else { + st_debug(ST_VIN, "%s, Unknown interrupt!\n", __func__); + } + + return IRQ_HANDLED; +} + +static irqreturn_t stf_vin_isp_irq_csi_handler(int irq, void *priv) +{ + struct stf_vin_dev *vin_dev = priv; + void __iomem *ispbase = stf_vin_get_ispbase(vin_dev); + u32 int_status; + + int_status = reg_read(ispbase, ISP_REG_ISP_CTRL_0); + + if (int_status & BIT(25)) { + vin_dev->isr_ops->isr_buffer_done( + &vin_dev->line[VIN_LINE_ISP_RAW]); + + /* clear interrupt */ + reg_write(ispbase, + ISP_REG_ISP_CTRL_0, + (int_status & ~EN_INT_ALL) | EN_INT_CSI_DONE); + } else { + st_debug(ST_VIN, "%s, Unknown interrupt!!!\n", __func__); + } + + return IRQ_HANDLED; +} + +static irqreturn_t stf_vin_isp_irq_csiline_handler(int irq, void *priv) +{ + struct stf_vin_dev *vin_dev = priv; + struct stf_isp_dev *isp_dev; + void __iomem *ispbase = stf_vin_get_ispbase(vin_dev); + u32 int_status, value; + + isp_dev = vin_dev->stfcamss->isp_dev; + + int_status = reg_read(ispbase, ISP_REG_ISP_CTRL_0); + if (int_status & BIT(27)) { + struct dummy_buffer *dummy_buffer = + &vin_dev->dummy_buffer[STF_DUMMY_ISP]; + + if (!atomic_read(&isp_dev->shadow_count)) { + if (atomic_dec_if_positive(&dummy_buffer->frame_skip) < 0) { + if ((int_status & BIT(11))) + vin_dev->isr_ops->isr_change_buffer( + &vin_dev->line[VIN_LINE_ISP_SS0]); + if ((int_status & BIT(12))) + vin_dev->isr_ops->isr_change_buffer( + &vin_dev->line[VIN_LINE_ISP_SS1]); + if ((int_status & BIT(20))) + vin_dev->isr_ops->isr_change_buffer( + &vin_dev->line[VIN_LINE_ISP]); + value = reg_read(ispbase, + ISP_REG_CSI_MODULE_CFG); + if ((value & BIT(19))) + vin_dev->isr_ops->isr_change_buffer( + &vin_dev->line[VIN_LINE_ISP_RAW]); + } + + /* shadow update */ + reg_set_bit(ispbase, ISP_REG_CSIINTS_ADDR, + 0x30000, 0x30000); + reg_set_bit(ispbase, ISP_REG_IESHD_ADDR, + BIT(1) | BIT(0), 0x3); + } else { + st_err_ratelimited(ST_VIN, "skip this frame\n"); + } + + /* clear interrupt */ + reg_write(ispbase, ISP_REG_ISP_CTRL_0, + (int_status & ~EN_INT_ALL) | EN_INT_LINE_INT); + } else { + st_debug(ST_VIN, "%s, Unknown interrupt!!!\n", __func__); + } + + return IRQ_HANDLED; +} + +static int stf_vin_clk_enable(struct stf_vin_dev *vin_dev, enum link link) +{ + struct stfcamss *stfcamss = vin_dev->stfcamss; + + clk_set_rate(stfcamss->sys_clk[STF_CLK_APB_FUNC].clk, 49500000); + + switch (link) { + case LINK_DVP_TO_WR: + break; + case LINK_DVP_TO_ISP: + break; + case LINK_CSI_TO_WR: + clk_set_rate(stfcamss->sys_clk[STF_CLK_MIPI_RX0_PXL].clk, + 198000000); + reset_control_deassert(stfcamss->sys_rst[STF_RST_AXIWR].rstc); + clk_set_parent(stfcamss->sys_clk[STF_CLK_AXIWR].clk, + stfcamss->sys_clk[STF_CLK_MIPI_RX0_PXL].clk); + break; + case LINK_CSI_TO_ISP: + clk_set_rate(stfcamss->sys_clk[STF_CLK_MIPI_RX0_PXL].clk, + 198000000); + clk_set_parent(stfcamss->sys_clk[STF_CLK_WRAPPER_CLK_C].clk, + stfcamss->sys_clk[STF_CLK_MIPI_RX0_PXL].clk); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int stf_vin_clk_disable(struct stf_vin_dev *vin_dev, enum link link) +{ + struct stfcamss *stfcamss = vin_dev->stfcamss; + + switch (link) { + case LINK_DVP_TO_WR: + break; + case LINK_DVP_TO_ISP: + break; + case LINK_CSI_TO_WR: + reset_control_assert(stfcamss->sys_rst[STF_RST_AXIWR].rstc); + break; + case LINK_CSI_TO_ISP: + break; + default: + return -EINVAL; + } + + return 0; +} + +static int stf_vin_wr_stream_set(struct stf_vin_dev *vin_dev, int on) +{ + struct stfcamss *stfcamss = vin_dev->stfcamss; + + /* make the axiwr alway on */ + if (on) + reg_set(stfcamss->syscon_base, + SYSCONSAIF_SYSCFG(20), U0_VIN_CNFG_AXIWR0_EN); + + return 0; +} + +static int stf_vin_stream_set(struct stf_vin_dev *vin_dev, int on, + struct v4l2_subdev_format sensor_fmt, + enum link link) +{ + struct stfcamss *stfcamss = vin_dev->stfcamss; + + switch (link) { + case LINK_DVP_TO_WR: + break; + case LINK_DVP_TO_ISP: + break; + case LINK_CSI_TO_WR: + reg_set_bit(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(20), + U0_VIN_CNFG_AXIWR0_CHANNEL_SEL, 0 << 0); + reg_set_bit(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(28), + U0_VIN_CNFG_AXIWR0_PIX_CT, 1 << 13); + reg_set_bit(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(28), + U0_VIN_CNFG_AXIWR0_PIXEL_HEIGH_BIT_SEL, 0 << 15); + reg_set_bit(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(28), + U0_VIN_CNFG_AXIWR0_PIX_CNT_END, + (sensor_fmt.format.width / 4 - 1) << 2); + break; + case LINK_CSI_TO_ISP: + reg_set_bit(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(36), + U0_VIN_CNFG_MIPI_BYTE_EN_ISP0, 0 << 6); + reg_set_bit(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(36), + U0_VIN_CNFG_MIPI_CHANNEL_SEL0, 0 << 8); + reg_set_bit(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(36), + U0_VIN_CNFG_PIX_NUM, 0 << 13); + + if (sensor_fmt.format.code == MEDIA_BUS_FMT_SRGGB10_1X10 || + sensor_fmt.format.code == MEDIA_BUS_FMT_SGRBG10_1X10 || + sensor_fmt.format.code == MEDIA_BUS_FMT_SGBRG10_1X10 || + sensor_fmt.format.code == MEDIA_BUS_FMT_SBGGR10_1X10) + reg_set_bit(stfcamss->syscon_base, + SYSCONSAIF_SYSCFG(36), + U0_VIN_CNFG_P_I_MIPI_HAEDER_EN0, + 1 << 12); + break; + default: + return -EINVAL; + } + + return 0; +} + +static void stf_vin_wr_irq_enable(struct stf_vin_dev *vin_dev, int enable) +{ + struct stfcamss *stfcamss = vin_dev->stfcamss; + unsigned int value = 0; + + if (enable) { + value = ~(0x1 << 1); + reg_set_bit(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(28), + U0_VIN_CNFG_AXIWR0_INTR_MASK, value); + } else { + /* clear vin interrupt */ + value = 0x1 << 1; + reg_set_bit(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(28), + U0_VIN_CNFG_AXIWR0_INTR_CLEAN, 0x1); + reg_set_bit(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(28), + U0_VIN_CNFG_AXIWR0_INTR_CLEAN, 0x0); + reg_set_bit(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(28), + U0_VIN_CNFG_AXIWR0_INTR_MASK, value); + } +} + +static void stf_vin_wr_set_ping_addr(struct stf_vin_dev *vin_dev, + dma_addr_t addr) +{ + struct stfcamss *stfcamss = vin_dev->stfcamss; + + /* set the start address */ + reg_write(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(32), (long)addr); +} + +static void stf_vin_wr_set_pong_addr(struct stf_vin_dev *vin_dev, + dma_addr_t addr) +{ + struct stfcamss *stfcamss = vin_dev->stfcamss; + + /* set the start address */ + reg_write(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(24), (long)addr); +} + +static void stf_vin_isp_set_yuv_addr(struct stf_vin_dev *vin_dev, + dma_addr_t y_addr, dma_addr_t uv_addr) +{ + void __iomem *ispbase = stf_vin_get_ispbase(vin_dev); + + reg_write(ispbase, ISP_REG_Y_PLANE_START_ADDR, y_addr); + reg_write(ispbase, ISP_REG_UV_PLANE_START_ADDR, uv_addr); +} + +static void stf_vin_isp_set_raw_addr(struct stf_vin_dev *vin_dev, + dma_addr_t raw_addr) +{ + void __iomem *ispbase = stf_vin_get_ispbase(vin_dev); + + reg_write(ispbase, ISP_REG_DUMP_CFG_0, raw_addr); +} + +static void stf_vin_isp_set_ss0_addr(struct stf_vin_dev *vin_dev, + dma_addr_t y_addr, dma_addr_t uv_addr) +{ + void __iomem *ispbase = stf_vin_get_ispbase(vin_dev); + + reg_write(ispbase, ISP_REG_SS0AY, y_addr); + reg_write(ispbase, ISP_REG_SS0AUV, uv_addr); +} + +static void stf_vin_isp_set_ss1_addr(struct stf_vin_dev *vin_dev, + dma_addr_t y_addr, dma_addr_t uv_addr) +{ + void __iomem *ispbase = stf_vin_get_ispbase(vin_dev); + + reg_write(ispbase, ISP_REG_SS1AY, y_addr); + reg_write(ispbase, ISP_REG_SS1AUV, uv_addr); +} + +const struct vin_hw_ops vin_ops = { + .vin_clk_enable = stf_vin_clk_enable, + .vin_clk_disable = stf_vin_clk_disable, + .vin_wr_stream_set = stf_vin_wr_stream_set, + .vin_wr_irq_enable = stf_vin_wr_irq_enable, + .vin_stream_set = stf_vin_stream_set, + .vin_wr_set_ping_addr = stf_vin_wr_set_ping_addr, + .vin_wr_set_pong_addr = stf_vin_wr_set_pong_addr, + .vin_isp_set_yuv_addr = stf_vin_isp_set_yuv_addr, + .vin_isp_set_raw_addr = stf_vin_isp_set_raw_addr, + .vin_isp_set_ss0_addr = stf_vin_isp_set_ss0_addr, + .vin_isp_set_ss1_addr = stf_vin_isp_set_ss1_addr, + .vin_wr_irq_handler = stf_vin_wr_irq_handler, + .vin_isp_irq_handler = stf_vin_isp_irq_handler, + .vin_isp_irq_csi_handler = stf_vin_isp_irq_csi_handler, + .vin_isp_irq_csiline_handler = stf_vin_isp_irq_csiline_handler, +}; diff --git a/include/uapi/linux/stf_isp_ioctl.h b/include/uapi/linux/stf_isp_ioctl.h new file mode 100644 index 0000000000000..3f302ef235d2c --- /dev/null +++ b/include/uapi/linux/stf_isp_ioctl.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * stf_isp_ioctl.h + * + * StarFive Camera Subsystem - ISP Module + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ + +#ifndef STF_ISP_IOCTL_H +#define STF_ISP_IOCTL_H + +#include + +#define FILENAME_MAX_LEN 30 + +#define ISP_IOC ('V') +#define STF_ISP_REG_BUF_SIZE (768) +#define STF_ISP_REG_TBL_BUF_SIZE (STF_ISP_REG_BUF_SIZE / 2) +#define STF_ISP_REG_TBL_2_BUF_SIZE (STF_ISP_REG_BUF_SIZE / 3) +#define STF_ISP_REG_TBL_3_BUF_SIZE (STF_ISP_REG_BUF_SIZE / 4) +#define STF_ISP_REG_SMPL_PACK_BUF_SIZE (STF_ISP_REG_BUF_SIZE / 2) +#define RDMA_WR_ONE (0xA0) +#define RDMA_WR_SRL (0xA1) +#define RDMA_LINK (0xA2) +#define RDMA_SINT (0xA3) +#define RDMA_END (0xAF) +#define ENABLE_SS0_SS1 + +enum _STF_ISP_IOCTL { + STF_ISP_IOCTL_LOAD_FW = BASE_VIDIOC_PRIVATE + 1, + STF_ISP_IOCTL_DMABUF_ALLOC, + STF_ISP_IOCTL_DMABUF_FREE, + STF_ISP_IOCTL_GET_HW_VER, + STF_ISP_IOCTL_REG, + STF_ISP_IOCTL_SHADOW_LOCK, + STF_ISP_IOCTL_SHADOW_UNLOCK, + STF_ISP_IOCTL_SHADOW_UNLOCK_N_TRIGGER, + STF_ISP_IOCTL_SET_USER_CONFIG_ISP, + STF_ISP_IOCTL_MAX +}; + +enum _STF_ISP_REG_METHOD { + STF_ISP_REG_METHOD_ONE_REG = 0, + STF_ISP_REG_METHOD_SERIES, + STF_ISP_REG_METHOD_MODULE, + STF_ISP_REG_METHOD_TABLE, + STF_ISP_REG_METHOD_TABLE_2, + STF_ISP_REG_METHOD_TABLE_3, + STF_ISP_REG_METHOD_SMPL_PACK, + STF_ISP_REG_METHOD_SOFT_RDMA, + STF_ISP_REG_METHOD_MAX +}; + +struct stfisp_fw_info { + char __user filename[FILENAME_MAX_LEN]; +}; + +struct dmabuf_create { + __u32 fd; + __u32 size; + __u32 paddr; +}; + +struct isp_rdma_info { + u32 param; + union { + u32 value; + struct { + u32 offset : 24; + u32 tag : 8; + }; + }; +}; + +struct isp_reg_info { + /* access method of register */ + u8 method; + /* offset indicated which register will be read/write */ + u32 offset; + /* length for indicated how much register will be read/write */ + u32 length; +}; + +union reg_buf { + u32 buffer[STF_ISP_REG_BUF_SIZE]; + struct { + u32 offset; + u32 value; + } reg_tbl[STF_ISP_REG_TBL_BUF_SIZE]; + struct { + u32 offset; + u32 value; + u32 mask; + } reg_tbl2[STF_ISP_REG_TBL_2_BUF_SIZE]; + struct { + u32 offset; + u32 value; + u32 mask; + u32 delay_ms; + } reg_tbl3[STF_ISP_REG_TBL_3_BUF_SIZE]; + struct isp_rdma_info rdma_cmd[STF_ISP_REG_SMPL_PACK_BUF_SIZE]; +}; + +struct isp_reg_param { + /* register read/write information */ + struct isp_reg_info reg_info; + /* buffer */ + union reg_buf *reg_buf; +}; + +#define VIDIOC_STFISP_LOAD_FW \ + _IOW(ISP_IOC, STF_ISP_IOCTL_LOAD_FW, struct stfisp_fw_info) +#define VIDIOC_STFISP_GET_REG \ + _IOWR(ISP_IOC, STF_ISP_IOCTL_REG, struct isp_reg_param) +#define VIDIOC_STFISP_SET_REG \ + _IOW(ISP_IOC, STF_ISP_IOCTL_REG, struct isp_reg_param) +#define VIDIOC_STFISP_SHADOW_LOCK \ + _IO(ISP_IOC, STF_ISP_IOCTL_SHADOW_LOCK) +#define VIDIOC_STFISP_SHADOW_UNLOCK \ + _IO(ISP_IOC, STF_ISP_IOCTL_SHADOW_UNLOCK) +#define VIDIOC_STFISP_SHADOW_UNLOCK_N_TRIGGER \ + _IO(ISP_IOC, STF_ISP_IOCTL_SHADOW_UNLOCK_N_TRIGGER) +#define VIDIOC_STFISP_SET_USER_CONFIG_ISP \ + _IO(ISP_IOC, STF_ISP_IOCTL_SET_USER_CONFIG_ISP) + +#endif /* STF_ISP_IOCTL_H */ From e1c46d7ff5ab768d382d737c4b27c8f329d7e07c Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Wed, 15 Mar 2023 18:44:07 +0800 Subject: [PATCH 85/95] dt-bindings: phy: Add StarFive JH7110 USB/PCIe document Add StarFive JH7110 SoC USB 2.0/3.0 and PCIe 2.0 PHY dt-binding. PCIe 2.0 phy can use as USB 3.0 PHY. Signed-off-by: Minda Chen --- .../phy/starfive,jh7110-usb-pcie-phy.yaml | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/starfive,jh7110-usb-pcie-phy.yaml diff --git a/Documentation/devicetree/bindings/phy/starfive,jh7110-usb-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/starfive,jh7110-usb-pcie-phy.yaml new file mode 100644 index 0000000000000..aa1c3fe93100f --- /dev/null +++ b/Documentation/devicetree/bindings/phy/starfive,jh7110-usb-pcie-phy.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/starfive,jh7110-usb-pcie-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive USB 2.0 and PCIe 2.0 PHY + +maintainers: + - Minda Chen + +properties: + compatible: + enum: + - starfive,jh7110-usb-phy + - starfive,jh7110-pcie-phy + + reg: + maxItems: 1 + + "#phy-cells": + const: 0 + + clocks: + items: + - description: usb 125m clock + - description: app 125m clock + + clock-names: + items: + - const: 125m + - const: app_125 + +required: + - compatible + - reg + - "#phy-cells" + +additionalProperties: false + +examples: + - | + phy@10200000 { + compatible = "starfive,jh7110-usb-phy"; + reg = <0x10200000 0x10000>; + clocks = <&syscrg 95>, + <&stgcrg 6>; + clock-names = "125m", "app_125"; + #phy-cells = <0>; + }; + + phy@10210000 { + compatible = "starfive,jh7110-pcie-phy"; + reg = <0x10210000 0x10000>; + #phy-cells = <0>; + }; + + phy@10220000 { + compatible = "starfive,jh7110-pcie-phy"; + reg = <0x10220000 0x10000>; + #phy-cells = <0>; + }; From 0b65149d1f391327757997cb9abb58b466b3eccb Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Wed, 15 Mar 2023 18:44:08 +0800 Subject: [PATCH 86/95] phy: starfive: add JH7110 PCIE 2.0 and USB 2.0 PHY driver. Add Starfive JH7110 SoC PCIe 2.0 and USB 2.0 PHY driver support. PCIe 2.0 PHY can used as USB 3.0 PHY Signed-off-by: Minda Chen --- MAINTAINERS | 8 ++ drivers/phy/starfive/Kconfig | 22 ++++ drivers/phy/starfive/Makefile | 2 + drivers/phy/starfive/phy-jh7110-pcie.c | 136 ++++++++++++++++++++ drivers/phy/starfive/phy-jh7110-usb.c | 167 +++++++++++++++++++++++++ 5 files changed, 335 insertions(+) create mode 100644 drivers/phy/starfive/phy-jh7110-pcie.c create mode 100644 drivers/phy/starfive/phy-jh7110-usb.c diff --git a/MAINTAINERS b/MAINTAINERS index d7d23446c2c81..a3ba5eb7d3168 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19963,6 +19963,14 @@ F: Documentation/devicetree/bindings/clock/starfive,jh71*.yaml F: drivers/clk/starfive/clk-starfive-jh71* F: include/dt-bindings/clock/starfive?jh71*.h +STARFIVE JH71X0 PCIE AND USB PHY DRIVER +M: Emil Renner Berthing +M: Minda Chen +S: Supported +F: Documentation/devicetree/bindings/phy/starfive,jh7110-usb-pcie-phy.yaml +F: drivers/phy/starfive/phy-jh7110-pcie.c +F: drivers/phy/starfive/phy-jh7110-usb.c + STARFIVE JH71X0 PINCTRL DRIVERS M: Emil Renner Berthing M: Jianlong Huang diff --git a/drivers/phy/starfive/Kconfig b/drivers/phy/starfive/Kconfig index f989b8ff8bcbe..df4b22b3aa8cd 100644 --- a/drivers/phy/starfive/Kconfig +++ b/drivers/phy/starfive/Kconfig @@ -11,3 +11,25 @@ config PHY_STARFIVE_DPHY_RX Choose this option if you have a StarFive D-PHY in your system. If M is selected, the module will be called phy-starfive-dphy-rx. + +config PHY_STARFIVE_JH7110_USB + tristate "Starfive JH7110 USB 2.0 PHY support" + depends on USB_SUPPORT + select GENERIC_PHY + select USB_PHY + help + Enable this to support the StarFive USB 2.0 PHY, + used with the Cadence USB controller. + If M is selected, the module will be called + phy-jh7110-usb.ko. + +config PHY_STARFIVE_JH7110_PCIE + tristate "Starfive JH7110 PCIE 2.0/USB 3.0 PHY support" + depends on USB_SUPPORT + select GENERIC_PHY + select USB_PHY + help + Enable this to support the StarFive PCIe 2.0 PHY, + or used as USB 3.0 PHY. + If M is selected, the module will be called + phy-jh7110-pcie.ko. diff --git a/drivers/phy/starfive/Makefile b/drivers/phy/starfive/Makefile index 7ec576cb30aea..c3eaf1b34cbba 100644 --- a/drivers/phy/starfive/Makefile +++ b/drivers/phy/starfive/Makefile @@ -1,2 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_PHY_STARFIVE_DPHY_RX) += phy-starfive-dphy-rx.o +obj-$(CONFIG_PHY_STARFIVE_JH7110_USB) += phy-jh7110-usb.o +obj-$(CONFIG_PHY_STARFIVE_JH7110_PCIE) += phy-jh7110-pcie.o diff --git a/drivers/phy/starfive/phy-jh7110-pcie.c b/drivers/phy/starfive/phy-jh7110-pcie.c new file mode 100644 index 0000000000000..30a8fa1f580d7 --- /dev/null +++ b/drivers/phy/starfive/phy-jh7110-pcie.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * StarFive JH7110 PCIe 2.0 PHY driver + * + * Copyright (C) 2023 Minda Chen + */ + +#include +#include +#include +#include +#include +#include +#include + +#define PCIE_KVCO_LEVEL_OFF (0x28) +#define PCIE_USB3_PHY_PLL_CTL_OFF (0x7c) +#define PCIE_KVCO_TUNE_SIGNAL_OFF (0x80) +#define PCIE_USB3_PHY_ENABLE BIT(4) +#define PHY_KVCO_FINE_TUNE_LEVEL 0x91 +#define PHY_KVCO_FINE_TUNE_SIGNALS 0xc + +struct jh7110_pcie_phy { + struct phy *phy; + void __iomem *regs; + enum phy_mode mode; +}; + +static void jh7110_usb3_mode_set(struct jh7110_pcie_phy *phy) +{ + /* Configuare spread-spectrum mode: down-spread-spectrum */ + writel(PCIE_USB3_PHY_ENABLE, phy->regs + PCIE_USB3_PHY_PLL_CTL_OFF); +} + +static void jh7110_pcie_mode_set(struct jh7110_pcie_phy *phy) +{ + /* PCIe Multi-PHY PLL KVCO Gain fine tune settings: */ + writel(PHY_KVCO_FINE_TUNE_LEVEL, phy->regs + PCIE_KVCO_LEVEL_OFF); + writel(PHY_KVCO_FINE_TUNE_SIGNALS, phy->regs + PCIE_KVCO_TUNE_SIGNAL_OFF); +} + +static int jh7110_pcie_phy_set_mode(struct phy *_phy, + enum phy_mode mode, int submode) +{ + struct jh7110_pcie_phy *phy = phy_get_drvdata(_phy); + + if (mode != phy->mode) { + switch (mode) { + case PHY_MODE_USB_HOST: + case PHY_MODE_USB_DEVICE: + case PHY_MODE_USB_OTG: + jh7110_usb3_mode_set(phy); + break; + case PHY_MODE_PCIE: + jh7110_pcie_mode_set(phy); + break; + default: + return -EINVAL; + } + + dev_info(&_phy->dev, "Changing phy mode to %d\n", mode); + phy->mode = mode; + } + + return 0; +} + +static int jh7110_pcie_phy_init(struct phy *_phy) +{ + return 0; +} + +static int jh7110_pcie_phy_exit(struct phy *_phy) +{ + return 0; +} + +static const struct phy_ops jh7110_pcie_phy_ops = { + .init = jh7110_pcie_phy_init, + .exit = jh7110_pcie_phy_exit, + .set_mode = jh7110_pcie_phy_set_mode, + .owner = THIS_MODULE, +}; + +static int jh7110_pcie_phy_probe(struct platform_device *pdev) +{ + struct jh7110_pcie_phy *phy; + struct device *dev = &pdev->dev; + struct phy_provider *phy_provider; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(phy->regs)) + return PTR_ERR(phy->regs); + + phy->phy = devm_phy_create(dev, NULL, &jh7110_pcie_phy_ops); + if (IS_ERR(phy->phy)) + return dev_err_probe(dev, PTR_ERR(phy->regs), + "Failed to map phy base\n"); + + platform_set_drvdata(pdev, phy); + phy_set_drvdata(phy->phy, phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static int jh7110_pcie_phy_remove(struct platform_device *pdev) +{ + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static const struct of_device_id jh7110_pcie_phy_of_match[] = { + { .compatible = "starfive,jh7110-pcie-phy" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, jh7110_pcie_phy_of_match); + +static struct platform_driver jh7110_pcie_phy_driver = { + .probe = jh7110_pcie_phy_probe, + .remove = jh7110_pcie_phy_remove, + .driver = { + .of_match_table = jh7110_pcie_phy_of_match, + .name = "jh7110-pcie-phy", + } +}; +module_platform_driver(jh7110_pcie_phy_driver); + +MODULE_DESCRIPTION("StarFive JH7110 PCIe 2.0 PHY driver"); +MODULE_AUTHOR("Minda Chen "); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/starfive/phy-jh7110-usb.c b/drivers/phy/starfive/phy-jh7110-usb.c new file mode 100644 index 0000000000000..89db0b7b12240 --- /dev/null +++ b/drivers/phy/starfive/phy-jh7110-usb.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * StarFive JH7110 USB 2.0 PHY driver + * + * Copyright (C) 2023 Minda Chen + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define USB_125M_CLK_RATE 125000000 +#define USB_LS_KEEPALIVE_OFF 0x4 +#define USB_LS_KEEPALIVE_ENABLE BIT(4) + +struct jh7110_usb2_phy { + struct phy *phy; + void __iomem *regs; + struct clk *usb_125m_clk; + struct clk *app_125; + enum usb_dr_mode dr_mode; +}; + +static void jh7110_usb2_mode_set(struct jh7110_usb2_phy *phy) +{ + unsigned int val; + + if (phy->dr_mode != USB_DR_MODE_PERIPHERAL) { + /* Enable the LS speed keep-alive signal */ + val = readl(phy->regs + USB_LS_KEEPALIVE_OFF); + val |= USB_LS_KEEPALIVE_ENABLE; + writel(val, phy->regs + USB_LS_KEEPALIVE_OFF); + } +} + +static int jh7110_usb2_phy_set_mode(struct phy *_phy, + enum phy_mode mode, int submode) +{ + struct jh7110_usb2_phy *phy = phy_get_drvdata(_phy); + int new_mode; + + switch (mode) { + case PHY_MODE_USB_HOST: + new_mode = USB_DR_MODE_HOST; + break; + case PHY_MODE_USB_DEVICE: + new_mode = USB_DR_MODE_PERIPHERAL; + break; + case PHY_MODE_USB_OTG: + new_mode = USB_DR_MODE_OTG; + break; + default: + return -EINVAL; + } + + if (new_mode != phy->dr_mode) { + dev_info(&_phy->dev, "Changing dr_mode to %d\n", new_mode); + phy->dr_mode = new_mode; + jh7110_usb2_mode_set(phy); + } + + return 0; +} + +static int jh7110_usb2_phy_init(struct phy *_phy) +{ + struct jh7110_usb2_phy *phy = phy_get_drvdata(_phy); + int ret; + + ret = clk_set_rate(phy->usb_125m_clk, USB_125M_CLK_RATE); + if (ret) + return ret; + + ret = clk_prepare_enable(phy->app_125); + if (ret) + return ret; + + return 0; +} + +static int jh7110_usb2_phy_exit(struct phy *_phy) +{ + struct jh7110_usb2_phy *phy = phy_get_drvdata(_phy); + + clk_disable_unprepare(phy->app_125); + + return 0; +} + +static const struct phy_ops jh7110_usb2_phy_ops = { + .init = jh7110_usb2_phy_init, + .exit = jh7110_usb2_phy_exit, + .set_mode = jh7110_usb2_phy_set_mode, + .owner = THIS_MODULE, +}; + +static int jh7110_usb_phy_probe(struct platform_device *pdev) +{ + struct jh7110_usb2_phy *phy; + struct device *dev = &pdev->dev; + struct phy_provider *phy_provider; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->usb_125m_clk = devm_clk_get(dev, "125m"); + if (IS_ERR(phy->usb_125m_clk)) + return dev_err_probe(dev, PTR_ERR(phy->usb_125m_clk), + "Failed to get 125m clock\n"); + + phy->app_125 = devm_clk_get(dev, "app_125"); + if (IS_ERR(phy->app_125)) + return dev_err_probe(dev, PTR_ERR(phy->app_125), + "Failed to get app 125m clock\n"); + + phy->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(phy->regs)) + return dev_err_probe(dev, PTR_ERR(phy->regs), + "Failed to map phy base\n"); + + phy->phy = devm_phy_create(dev, NULL, &jh7110_usb2_phy_ops); + if (IS_ERR(phy->phy)) + return dev_err_probe(dev, PTR_ERR(phy->phy), + "Failed to create phy\n"); + + platform_set_drvdata(pdev, phy); + phy_set_drvdata(phy->phy, phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static int jh7110_usb_phy_remove(struct platform_device *pdev) +{ + struct jh7110_usb2_phy *phy = platform_get_drvdata(pdev); + + clk_disable_unprepare(phy->app_125); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static const struct of_device_id jh7110_usb_phy_of_match[] = { + { .compatible = "starfive,jh7110-usb-phy" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, jh7110_usb_phy_of_match); + +static struct platform_driver jh7110_usb_phy_driver = { + .probe = jh7110_usb_phy_probe, + .remove = jh7110_usb_phy_remove, + .driver = { + .of_match_table = jh7110_usb_phy_of_match, + .name = "jh7110-usb-phy", + } +}; +module_platform_driver(jh7110_usb_phy_driver); + +MODULE_DESCRIPTION("StarFive JH7110 USB 2.0 PHY driver"); +MODULE_AUTHOR("Minda Chen "); +MODULE_LICENSE("GPL"); From a20f8309c77c78f5edb2901c6cf075ba329363fa Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Wed, 15 Mar 2023 18:44:09 +0800 Subject: [PATCH 87/95] dt-binding: Add JH7110 USB wrapper layer doc. The dt-binding doc of Cadence USBSS-DRD controller wrapper layer. Signed-off-by: Minda Chen Reviewed-by: Peter Chen --- .../bindings/usb/starfive,jh7110-usb.yaml | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/starfive,jh7110-usb.yaml diff --git a/Documentation/devicetree/bindings/usb/starfive,jh7110-usb.yaml b/Documentation/devicetree/bindings/usb/starfive,jh7110-usb.yaml new file mode 100644 index 0000000000000..b1a8dc6d7b4b7 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/starfive,jh7110-usb.yaml @@ -0,0 +1,119 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/starfive,jh7110-usb.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 wrapper module for the Cadence USBSS-DRD controller + +maintainers: + - Minda Chen + +properties: + compatible: + const: starfive,jh7110-usb + + clocks: + items: + - description: lpm clock + - description: stb clock + - description: apb clock + - description: axi clock + - description: utmi apb clock + + clock-names: + items: + - const: lpm + - const: stb + - const: apb + - const: axi + - const: utmi_apb + + resets: + items: + - description: PWRUP reset + - description: APB reset + - description: AXI reset + - description: UTMI_APB reset + + starfive,sys-syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + items: + - description: phandle to System Register Controller sys_syscon node. + - description: offset of SYS_SYSCONSAIF__SYSCFG register for USB. + description: + The phandle to System Register Controller syscon node and the offset + of SYS_SYSCONSAIF__SYSCFG register for USB. + + starfive,stg-syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + items: + - description: phandle to System Register Controller stg_syscon node. + - description: register0 offset of STG_SYSCONSAIF__SYSCFG register for USB. + - description: register1 offset of STG_SYSCONSAIF__SYSCFG register for USB. + - description: register2 offset of STG_SYSCONSAIF__SYSCFG register for USB. + - description: register3 offset of STG_SYSCONSAIF__SYSCFG register for USB. + description: + The phandle to System Register Controller syscon node and the offset + of STG_SYSCONSAIF__SYSCFG register for USB. Total 4 regsisters offset + for USB. + + "#address-cells": + maximum: 2 + + "#size-cells": + maximum: 2 + + ranges: true + +patternProperties: + "^usb@[0-9a-f]+$": + type: object + +required: + - compatible + - clocks + - clock-names + - resets + - starfive,sys-syscon + - starfive,stg-syscon + - "#address-cells" + - "#size-cells" + - ranges + +additionalProperties: false + +examples: + - | + usb@10100000 { + compatible = "starfive,jh7110-usb"; + clocks = <&syscrg 4>, + <&stgcrg 5>, + <&stgcrg 1>, + <&stgcrg 3>, + <&stgcrg 2>; + clock-names = "lpm", "stb", "apb", "axi", "utmi_apb"; + resets = <&stgcrg 10>, + <&stgcrg 8>, + <&stgcrg 7>, + <&stgcrg 9>; + starfive,stg-syscon = <&stg_syscon 0x4 0xc4 0x148 0x1f4>; + starfive,sys-syscon = <&sys_syscon 0x18>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x10100000 0x100000>; + + usb@0 { + compatible = "cdns,usb3"; + reg = <0x0 0x10000>, + <0x10000 0x10000>, + <0x20000 0x10000>; + reg-names = "otg", "xhci", "dev"; + interrupts = <100>, <108>, <110>; + interrupt-names = "host", "peripheral", "otg"; + maximum-speed = "super-speed"; + dr_mode = "host"; + }; + }; From b61c783de195f07ca8aabd3eb37fe98df3f4f22f Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Wed, 15 Mar 2023 18:44:10 +0800 Subject: [PATCH 88/95] usb: cdns3: add StarFive JH7110 USB driver. There is a Cadence USB3 core for JH7110 SoCs, the cdns core is the child of this USB wrapper module device. Signed-off-by: Minda Chen --- MAINTAINERS | 7 + drivers/usb/cdns3/Kconfig | 11 ++ drivers/usb/cdns3/Makefile | 1 + drivers/usb/cdns3/cdns3-starfive.c | 305 +++++++++++++++++++++++++++++ 4 files changed, 324 insertions(+) create mode 100644 drivers/usb/cdns3/cdns3-starfive.c diff --git a/MAINTAINERS b/MAINTAINERS index a3ba5eb7d3168..07439527eb028 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19996,6 +19996,13 @@ F: Documentation/devicetree/bindings/reset/starfive,jh7100-reset.yaml F: drivers/reset/starfive/reset-starfive-jh71* F: include/dt-bindings/reset/starfive?jh71*.h +STARFIVE JH71X0 USB DRIVERS +M: Emil Renner Berthing +M: Minda Chen +S: Maintained +F: Documentation/devicetree/bindings/usb/starfive,jh7110-usb.yaml +F: drivers/usb/cdns3/cdns3-starfive.c + STARFIVE JH71XX PMU CONTROLLER DRIVER M: Walker Chen S: Supported diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig index b98ca0a1352a2..0a514b5915272 100644 --- a/drivers/usb/cdns3/Kconfig +++ b/drivers/usb/cdns3/Kconfig @@ -78,6 +78,17 @@ config USB_CDNS3_IMX For example, imx8qm and imx8qxp. +config USB_CDNS3_STARFIVE + tristate "Cadence USB3 support on StarFive SoC platforms" + depends on ARCH_STARFIVE || COMPILE_TEST + help + Say 'Y' or 'M' here if you are building for StarFive SoCs + platforms that contain Cadence USB3 controller core. + + e.g. JH7110. + + If you choose to build this driver as module it will + be dynamically linked and module will be called cdns3-starfive.ko endif if USB_CDNS_SUPPORT diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile index 61edb2f892764..48dfae75b5aaf 100644 --- a/drivers/usb/cdns3/Makefile +++ b/drivers/usb/cdns3/Makefile @@ -24,6 +24,7 @@ endif obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci-wrap.o obj-$(CONFIG_USB_CDNS3_TI) += cdns3-ti.o obj-$(CONFIG_USB_CDNS3_IMX) += cdns3-imx.o +obj-$(CONFIG_USB_CDNS3_STARFIVE) += cdns3-starfive.o cdnsp-udc-pci-y := cdnsp-pci.o diff --git a/drivers/usb/cdns3/cdns3-starfive.c b/drivers/usb/cdns3/cdns3-starfive.c new file mode 100644 index 0000000000000..a99f98f852358 --- /dev/null +++ b/drivers/usb/cdns3/cdns3-starfive.c @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * cdns3-starfive.c - StarFive specific Glue layer for Cadence USB Controller + * + * Copyright (C) 2022 Starfive, Inc. + * Author: Yanhong Wang + * Author: Mason Huo + * Author: Minda Chen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core.h" + +#define USB_STRAP_HOST BIT(17) +#define USB_STRAP_DEVICE BIT(18) +#define USB_STRAP_MASK GENMASK(18, 16) + +#define USB_SUSPENDM_HOST BIT(19) +#define USB_SUSPENDM_MASK BIT(19) + +#define USB_SUSPENDM_BYPS BIT(20) +#define USB_REFCLK_MODE BIT(23) +#define USB_PLL_EN BIT(22) +#define USB_PDRSTN_SPLIT BIT(17) + +#define PCIE_CKREF_SRC_MASK GENMASK(19, 18) +#define PCIE_CLK_SEL_MASK GENMASK(21, 20) +#define PCIE_PHY_MODE BIT(20) +#define PCIE_PHY_MODE_MASK GENMASK(21, 20) +#define PCIE_USB3_BUS_WIDTH_MASK GENMASK(3, 2) +#define PCIE_USB3_RATE_MASK GENMASK(6, 5) +#define PCIE_USB3_RX_STANDBY_MASK BIT(7) +#define PCIE_USB3_PHY_ENABLE BIT(4) + +struct cdns_starfive { + struct device *dev; + struct regmap *stg_syscon; + struct regmap *sys_syscon; + struct reset_control *resets; + struct clk_bulk_data *clks; + int num_clks; + u32 sys_offset; + u32 stg_offset_4; + u32 stg_offset_196; + u32 stg_offset_328; + u32 stg_offset_500; + bool usb2_only; +}; + +static int cdns_mode_init(struct platform_device *pdev, + struct cdns_starfive *data, const char **out_mode) +{ + struct device_node *child; + const char *dr_mode = NULL; + + child = of_get_compatible_child(pdev->dev.of_node, "cdns,usb3"); + if (!child) { + return dev_err_probe(&pdev->dev, -ENODEV, + "Failed to find child node\n"); + } + + /* Init usb 2.0 utmi phy */ + regmap_update_bits(data->stg_syscon, data->stg_offset_4, + USB_SUSPENDM_BYPS, USB_SUSPENDM_BYPS); + regmap_update_bits(data->stg_syscon, data->stg_offset_4, + USB_PLL_EN, USB_PLL_EN); + regmap_update_bits(data->stg_syscon, data->stg_offset_4, + USB_REFCLK_MODE, USB_REFCLK_MODE); + + if (!of_find_property(child, "cdns3,usb3-phy", NULL)) { + /* Disconnect usb 3.0 phy mode */ + regmap_update_bits(data->sys_syscon, data->sys_offset, + USB_PDRSTN_SPLIT, USB_PDRSTN_SPLIT); + data->usb2_only = 1; + } else { + /* Config usb 3.0 pipe phy */ + regmap_update_bits(data->stg_syscon, data->stg_offset_196, + PCIE_CKREF_SRC_MASK, 0); + regmap_update_bits(data->stg_syscon, data->stg_offset_196, + PCIE_CLK_SEL_MASK, 0); + regmap_update_bits(data->stg_syscon, data->stg_offset_328, + PCIE_PHY_MODE_MASK, PCIE_PHY_MODE); + regmap_update_bits(data->stg_syscon, data->stg_offset_500, + PCIE_USB3_BUS_WIDTH_MASK, 0); + regmap_update_bits(data->stg_syscon, data->stg_offset_500, + PCIE_USB3_RATE_MASK, 0); + regmap_update_bits(data->stg_syscon, data->stg_offset_500, + PCIE_USB3_RX_STANDBY_MASK, 0); + regmap_update_bits(data->stg_syscon, data->stg_offset_500, + PCIE_USB3_PHY_ENABLE, PCIE_USB3_PHY_ENABLE); + + /* Connect usb 3.0 phy mode */ + regmap_update_bits(data->sys_syscon, data->sys_offset, + USB_PDRSTN_SPLIT, 0); + } + + if (!of_property_read_string(child, "dr_mode", &dr_mode)) { + if (!strcmp(dr_mode, "host")) { + regmap_update_bits(data->stg_syscon, + data->stg_offset_4, + USB_STRAP_MASK, + USB_STRAP_HOST); + regmap_update_bits(data->stg_syscon, + data->stg_offset_4, + USB_SUSPENDM_MASK, + USB_SUSPENDM_HOST); + } else if (!strcmp(dr_mode, "peripheral")) { + regmap_update_bits(data->stg_syscon, data->stg_offset_4, + USB_STRAP_MASK, USB_STRAP_DEVICE); + regmap_update_bits(data->stg_syscon, data->stg_offset_4, + USB_SUSPENDM_MASK, 0); + } + } + + if (out_mode) + *out_mode = dr_mode; + + return 0; +} + +static int cdns_clk_rst_init(struct cdns_starfive *data) +{ + int ret; + + data->num_clks = devm_clk_bulk_get_all(data->dev, &data->clks); + if (data->num_clks < 0) + return dev_err_probe(data->dev, -ENODEV, + "Failed to get clocks\n"); + + ret = clk_bulk_prepare_enable(data->num_clks, data->clks); + if (ret) + return dev_err_probe(data->dev, ret, + "failed to enable clocks\n"); + + data->resets = devm_reset_control_array_get_exclusive(data->dev); + if (IS_ERR(data->resets)) { + ret = dev_err_probe(data->dev, PTR_ERR(data->resets), + "Failed to get resets"); + goto err_clk_init; + } + + ret = reset_control_deassert(data->resets); + if (ret) { + ret = dev_err_probe(data->dev, ret, + "failed to reset clocks\n"); + goto err_clk_init; + } + + return ret; + +err_clk_init: + clk_bulk_disable_unprepare(data->num_clks, data->clks); + return ret; +} + +static int cdns_starfive_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = pdev->dev.of_node; + struct cdns_starfive *data; + unsigned int args[4]; + const char *dr_mode; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + platform_set_drvdata(pdev, data); + + data->dev = dev; + + data->stg_syscon = syscon_regmap_lookup_by_phandle_args(pdev->dev.of_node, + "starfive,stg-syscon", 4, args); + + if (IS_ERR(data->stg_syscon)) + return dev_err_probe(dev, PTR_ERR(data->stg_syscon), + "Failed to parse starfive,stg-syscon\n"); + + data->stg_offset_4 = args[0]; + data->stg_offset_196 = args[1]; + data->stg_offset_328 = args[2]; + data->stg_offset_500 = args[3]; + + data->sys_syscon = syscon_regmap_lookup_by_phandle_args(pdev->dev.of_node, + "starfive,sys-syscon", 1, args); + if (IS_ERR(data->sys_syscon)) + return dev_err_probe(dev, PTR_ERR(data->sys_syscon), + "Failed to parse starfive,sys-syscon\n"); + + data->sys_offset = args[0]; + + ret = cdns_mode_init(pdev, data, &dr_mode); + if (ret) + return ret; + + ret = cdns_clk_rst_init(data); + if (ret) + return ret; + + ret = of_platform_populate(node, NULL, NULL, dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to create children\n"); + + device_set_wakeup_capable(dev, true); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + dev_info(dev, "usb mode %s %s probe success\n", + dr_mode ? dr_mode : "unknown", data->usb2_only ? "2.0" : "3.0"); + + return 0; +} + +static int cdns_starfive_remove_core(struct device *dev, void *c) +{ + struct platform_device *pdev = to_platform_device(dev); + + platform_device_unregister(pdev); + + return 0; +} + +static int cdns_starfive_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cdns_starfive *data = dev_get_drvdata(dev); + + pm_runtime_get_sync(dev); + device_for_each_child(dev, NULL, cdns_starfive_remove_core); + + reset_control_assert(data->resets); + clk_bulk_disable_unprepare(data->num_clks, data->clks); + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int cdns_starfive_resume(struct device *dev) +{ + struct cdns_starfive *data = dev_get_drvdata(dev); + int ret; + + ret = clk_bulk_prepare_enable(data->num_clks, data->clks); + if (ret) + return ret; + + ret = reset_control_deassert(data->resets); + if (ret) + return ret; + + return 0; +} + +static int cdns_starfive_suspend(struct device *dev) +{ + struct cdns_starfive *data = dev_get_drvdata(dev); + + clk_bulk_disable_unprepare(data->num_clks, data->clks); + reset_control_assert(data->resets); + + return 0; +} +#endif + +static const struct dev_pm_ops cdns_starfive_pm_ops = { + SET_RUNTIME_PM_OPS(cdns_starfive_suspend, cdns_starfive_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(cdns_starfive_suspend, cdns_starfive_resume) +}; + +static const struct of_device_id cdns_starfive_of_match[] = { + { .compatible = "starfive,jh7110-usb", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, cdns_starfive_of_match); + +static struct platform_driver cdns_starfive_driver = { + .probe = cdns_starfive_probe, + .remove = cdns_starfive_remove, + .driver = { + .name = "cdns3-starfive", + .of_match_table = cdns_starfive_of_match, + .pm = &cdns_starfive_pm_ops, + }, +}; +module_platform_driver(cdns_starfive_driver); + +MODULE_ALIAS("platform:cdns3-starfive"); +MODULE_AUTHOR("YanHong Wang "); +MODULE_AUTHOR("Mason Huo "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Cadence USB3 StarFive Glue Layer"); From d87fc092b92aa0bc638152d91f5eb6c8922b05be Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Wed, 15 Mar 2023 18:44:11 +0800 Subject: [PATCH 89/95] dts: usb: add StarFive JH7110 USB dts configuration. USB Glue layer and Cadence USB subnode configuration, also includes USB and PCIe phy dts configuration. Signed-off-by: Minda Chen --- .../jh7110-starfive-visionfive-2.dtsi | 7 +++ arch/riscv/boot/dts/starfive/jh7110.dtsi | 54 +++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi index b4808fe6d013a..752bb0b6fd002 100644 --- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi @@ -326,3 +326,10 @@ pinctrl-0 = <&uart0_pins>; status = "okay"; }; + +&usb0 { + status = "okay"; + usbdrd_cdns3: usb@0 { + dr_mode = "peripheral"; + }; +}; diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi index 61f4de8644512..95abd5f6ca23d 100644 --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi @@ -382,6 +382,60 @@ status = "disabled"; }; + usb0: usb@10100000 { + compatible = "starfive,jh7110-usb"; + clocks = <&stgcrg JH7110_STGCLK_USB0_LPM>, + <&stgcrg JH7110_STGCLK_USB0_STB>, + <&stgcrg JH7110_STGCLK_USB0_APB>, + <&stgcrg JH7110_STGCLK_USB0_AXI>, + <&stgcrg JH7110_STGCLK_USB0_UTMI_APB>; + clock-names = "lpm", "stb", "apb", "axi", "utmi_apb"; + resets = <&stgcrg JH7110_STGRST_USB0_PWRUP>, + <&stgcrg JH7110_STGRST_USB0_APB>, + <&stgcrg JH7110_STGRST_USB0_AXI>, + <&stgcrg JH7110_STGRST_USB0_UTMI_APB>; + starfive,stg-syscon = <&stg_syscon 0x4 0xc4 0x148 0x1f4>; + starfive,sys-syscon = <&sys_syscon 0x18>; + status = "disabled"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x0 0x10100000 0x100000>; + + usbdrd_cdns3: usb@0 { + compatible = "cdns,usb3"; + reg = <0x0 0x10000>, + <0x10000 0x10000>, + <0x20000 0x10000>; + reg-names = "otg", "xhci", "dev"; + interrupts = <100>, <108>, <110>; + interrupt-names = "host", "peripheral", "otg"; + phys = <&usbphy0>; + phy-names = "cdns3,usb2-phy"; + maximum-speed = "super-speed"; + }; + }; + + usbphy0: phy@10200000 { + compatible = "starfive,jh7110-usb-phy"; + reg = <0x0 0x10200000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_USB_125M>, + <&stgcrg JH7110_STGCLK_USB0_APP_125>; + clock-names = "125m", "app_125"; + #phy-cells = <0>; + }; + + pciephy0: phy@10210000 { + compatible = "starfive,jh7110-pcie-phy"; + reg = <0x0 0x10210000 0x0 0x10000>; + #phy-cells = <0>; + }; + + pciephy1: phy@10220000 { + compatible = "starfive,jh7110-pcie-phy"; + reg = <0x0 0x10220000 0x0 0x10000>; + #phy-cells = <0>; + }; + stgcrg: clock-controller@10230000 { compatible = "starfive,jh7110-stgcrg"; reg = <0x0 0x10230000 0x0 0x10000>; From 32b615cd2f46ac8f60709e74e7f0bf4d2b8670c8 Mon Sep 17 00:00:00 2001 From: Sia Jee Heng Date: Tue, 14 Mar 2023 13:03:13 +0800 Subject: [PATCH 90/95] RISC-V: Change suspend_save_csrs and suspend_restore_csrs to public function Currently suspend_save_csrs() and suspend_restore_csrs() functions are statically defined in the suspend.c. Change the function's attribute to public so that the functions can be used by hibernation as well. Signed-off-by: Sia Jee Heng Reviewed-by: Ley Foon Tan Reviewed-by: Mason Huo Reviewed-by: Conor Dooley Reviewed-by: Andrew Jones --- arch/riscv/include/asm/suspend.h | 3 +++ arch/riscv/kernel/suspend.c | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/riscv/include/asm/suspend.h b/arch/riscv/include/asm/suspend.h index 8be391c2aecb8..67e047445662a 100644 --- a/arch/riscv/include/asm/suspend.h +++ b/arch/riscv/include/asm/suspend.h @@ -33,4 +33,7 @@ int cpu_suspend(unsigned long arg, /* Low-level CPU resume entry function */ int __cpu_resume_enter(unsigned long hartid, unsigned long context); +/* Used to save and restore the CSRs */ +void suspend_save_csrs(struct suspend_context *context); +void suspend_restore_csrs(struct suspend_context *context); #endif diff --git a/arch/riscv/kernel/suspend.c b/arch/riscv/kernel/suspend.c index 9ba24fb8cc932..3c89b8ec69c49 100644 --- a/arch/riscv/kernel/suspend.c +++ b/arch/riscv/kernel/suspend.c @@ -8,7 +8,7 @@ #include #include -static void suspend_save_csrs(struct suspend_context *context) +void suspend_save_csrs(struct suspend_context *context) { context->scratch = csr_read(CSR_SCRATCH); context->tvec = csr_read(CSR_TVEC); @@ -29,7 +29,7 @@ static void suspend_save_csrs(struct suspend_context *context) #endif } -static void suspend_restore_csrs(struct suspend_context *context) +void suspend_restore_csrs(struct suspend_context *context) { csr_write(CSR_SCRATCH, context->scratch); csr_write(CSR_TVEC, context->tvec); From 2c651b1ed7af56eb6d778edfcade1e54dc3127bd Mon Sep 17 00:00:00 2001 From: Sia Jee Heng Date: Tue, 14 Mar 2023 13:03:14 +0800 Subject: [PATCH 91/95] RISC-V: Factor out common code of __cpu_resume_enter() The cpu_resume() function is very similar for the suspend to disk and suspend to ram cases. Factor out the common code into suspend_restore_csrs macro and suspend_restore_regs macro. Signed-off-by: Sia Jee Heng Reviewed-by: Andrew Jones Reviewed-by: Conor Dooley --- arch/riscv/include/asm/assembler.h | 62 ++++++++++++++++++++++++++++++ arch/riscv/kernel/suspend_entry.S | 34 ++-------------- 2 files changed, 65 insertions(+), 31 deletions(-) create mode 100644 arch/riscv/include/asm/assembler.h diff --git a/arch/riscv/include/asm/assembler.h b/arch/riscv/include/asm/assembler.h new file mode 100644 index 0000000000000..ba59d38f89372 --- /dev/null +++ b/arch/riscv/include/asm/assembler.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2023 StarFive Technology Co., Ltd. + * + * Author: Jee Heng Sia + */ + +#ifndef __ASSEMBLY__ +#error "Only include this from assembly code" +#endif + +#ifndef __ASM_ASSEMBLER_H +#define __ASM_ASSEMBLER_H + +#include +#include +#include + +/* + * suspend_restore_csrs - restore CSRs + */ + .macro suspend_restore_csrs + REG_L t0, (SUSPEND_CONTEXT_REGS + PT_EPC)(a0) + csrw CSR_EPC, t0 + REG_L t0, (SUSPEND_CONTEXT_REGS + PT_STATUS)(a0) + csrw CSR_STATUS, t0 + REG_L t0, (SUSPEND_CONTEXT_REGS + PT_BADADDR)(a0) + csrw CSR_TVAL, t0 + REG_L t0, (SUSPEND_CONTEXT_REGS + PT_CAUSE)(a0) + csrw CSR_CAUSE, t0 + .endm + +/* + * suspend_restore_regs - Restore registers (except A0 and T0-T6) + */ + .macro suspend_restore_regs + REG_L ra, (SUSPEND_CONTEXT_REGS + PT_RA)(a0) + REG_L sp, (SUSPEND_CONTEXT_REGS + PT_SP)(a0) + REG_L gp, (SUSPEND_CONTEXT_REGS + PT_GP)(a0) + REG_L tp, (SUSPEND_CONTEXT_REGS + PT_TP)(a0) + REG_L s0, (SUSPEND_CONTEXT_REGS + PT_S0)(a0) + REG_L s1, (SUSPEND_CONTEXT_REGS + PT_S1)(a0) + REG_L a1, (SUSPEND_CONTEXT_REGS + PT_A1)(a0) + REG_L a2, (SUSPEND_CONTEXT_REGS + PT_A2)(a0) + REG_L a3, (SUSPEND_CONTEXT_REGS + PT_A3)(a0) + REG_L a4, (SUSPEND_CONTEXT_REGS + PT_A4)(a0) + REG_L a5, (SUSPEND_CONTEXT_REGS + PT_A5)(a0) + REG_L a6, (SUSPEND_CONTEXT_REGS + PT_A6)(a0) + REG_L a7, (SUSPEND_CONTEXT_REGS + PT_A7)(a0) + REG_L s2, (SUSPEND_CONTEXT_REGS + PT_S2)(a0) + REG_L s3, (SUSPEND_CONTEXT_REGS + PT_S3)(a0) + REG_L s4, (SUSPEND_CONTEXT_REGS + PT_S4)(a0) + REG_L s5, (SUSPEND_CONTEXT_REGS + PT_S5)(a0) + REG_L s6, (SUSPEND_CONTEXT_REGS + PT_S6)(a0) + REG_L s7, (SUSPEND_CONTEXT_REGS + PT_S7)(a0) + REG_L s8, (SUSPEND_CONTEXT_REGS + PT_S8)(a0) + REG_L s9, (SUSPEND_CONTEXT_REGS + PT_S9)(a0) + REG_L s10, (SUSPEND_CONTEXT_REGS + PT_S10)(a0) + REG_L s11, (SUSPEND_CONTEXT_REGS + PT_S11)(a0) + .endm + +#endif /* __ASM_ASSEMBLER_H */ diff --git a/arch/riscv/kernel/suspend_entry.S b/arch/riscv/kernel/suspend_entry.S index aafcca58c19de..12b52afe09a42 100644 --- a/arch/riscv/kernel/suspend_entry.S +++ b/arch/riscv/kernel/suspend_entry.S @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -83,39 +84,10 @@ ENTRY(__cpu_resume_enter) add a0, a1, zero /* Restore CSRs */ - REG_L t0, (SUSPEND_CONTEXT_REGS + PT_EPC)(a0) - csrw CSR_EPC, t0 - REG_L t0, (SUSPEND_CONTEXT_REGS + PT_STATUS)(a0) - csrw CSR_STATUS, t0 - REG_L t0, (SUSPEND_CONTEXT_REGS + PT_BADADDR)(a0) - csrw CSR_TVAL, t0 - REG_L t0, (SUSPEND_CONTEXT_REGS + PT_CAUSE)(a0) - csrw CSR_CAUSE, t0 + suspend_restore_csrs /* Restore registers (except A0 and T0-T6) */ - REG_L ra, (SUSPEND_CONTEXT_REGS + PT_RA)(a0) - REG_L sp, (SUSPEND_CONTEXT_REGS + PT_SP)(a0) - REG_L gp, (SUSPEND_CONTEXT_REGS + PT_GP)(a0) - REG_L tp, (SUSPEND_CONTEXT_REGS + PT_TP)(a0) - REG_L s0, (SUSPEND_CONTEXT_REGS + PT_S0)(a0) - REG_L s1, (SUSPEND_CONTEXT_REGS + PT_S1)(a0) - REG_L a1, (SUSPEND_CONTEXT_REGS + PT_A1)(a0) - REG_L a2, (SUSPEND_CONTEXT_REGS + PT_A2)(a0) - REG_L a3, (SUSPEND_CONTEXT_REGS + PT_A3)(a0) - REG_L a4, (SUSPEND_CONTEXT_REGS + PT_A4)(a0) - REG_L a5, (SUSPEND_CONTEXT_REGS + PT_A5)(a0) - REG_L a6, (SUSPEND_CONTEXT_REGS + PT_A6)(a0) - REG_L a7, (SUSPEND_CONTEXT_REGS + PT_A7)(a0) - REG_L s2, (SUSPEND_CONTEXT_REGS + PT_S2)(a0) - REG_L s3, (SUSPEND_CONTEXT_REGS + PT_S3)(a0) - REG_L s4, (SUSPEND_CONTEXT_REGS + PT_S4)(a0) - REG_L s5, (SUSPEND_CONTEXT_REGS + PT_S5)(a0) - REG_L s6, (SUSPEND_CONTEXT_REGS + PT_S6)(a0) - REG_L s7, (SUSPEND_CONTEXT_REGS + PT_S7)(a0) - REG_L s8, (SUSPEND_CONTEXT_REGS + PT_S8)(a0) - REG_L s9, (SUSPEND_CONTEXT_REGS + PT_S9)(a0) - REG_L s10, (SUSPEND_CONTEXT_REGS + PT_S10)(a0) - REG_L s11, (SUSPEND_CONTEXT_REGS + PT_S11)(a0) + suspend_restore_regs /* Return zero value */ add a0, zero, zero From 7c7f2a36bae1daa59de3c54234aaa8a3cb7ebeb2 Mon Sep 17 00:00:00 2001 From: Sia Jee Heng Date: Tue, 14 Mar 2023 13:03:15 +0800 Subject: [PATCH 92/95] RISC-V: mm: Enable huge page support to kernel_page_present() function Currently kernel_page_present() function doesn't support huge page detection causes the function to mistakenly return false to the hibernation core. Add huge page detection to the function to solve the problem. Fixes: 9e953cda5cdf ("riscv: Introduce huge page support for 32/64bit kernel") Signed-off-by: Sia Jee Heng Reviewed-by: Ley Foon Tan Reviewed-by: Mason Huo Reviewed-by: Andrew Jones Reviewed-by: Alexandre Ghiti --- arch/riscv/mm/pageattr.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/riscv/mm/pageattr.c b/arch/riscv/mm/pageattr.c index 86c56616e5dea..ea3d61de065b3 100644 --- a/arch/riscv/mm/pageattr.c +++ b/arch/riscv/mm/pageattr.c @@ -217,18 +217,26 @@ bool kernel_page_present(struct page *page) pgd = pgd_offset_k(addr); if (!pgd_present(*pgd)) return false; + if (pgd_leaf(*pgd)) + return true; p4d = p4d_offset(pgd, addr); if (!p4d_present(*p4d)) return false; + if (p4d_leaf(*p4d)) + return true; pud = pud_offset(p4d, addr); if (!pud_present(*pud)) return false; + if (pud_leaf(*pud)) + return true; pmd = pmd_offset(pud, addr); if (!pmd_present(*pmd)) return false; + if (pmd_leaf(*pmd)) + return true; pte = pte_offset_kernel(pmd, addr); return pte_present(*pte); From 197b5065817108769c144df9d8149492f6503a37 Mon Sep 17 00:00:00 2001 From: Sia Jee Heng Date: Tue, 14 Mar 2023 13:03:16 +0800 Subject: [PATCH 93/95] RISC-V: Add arch functions to support hibernation/suspend-to-disk Low level Arch functions were created to support hibernation. swsusp_arch_suspend() relies code from __cpu_suspend_enter() to write cpu state onto the stack, then calling swsusp_save() to save the memory image. Arch specific hibernation header is implemented and is utilized by the arch_hibernation_header_restore() and arch_hibernation_header_save() functions. The arch specific hibernation header consists of satp, hartid, and the cpu_resume address. The kernel built version is also need to be saved into the hibernation image header to making sure only the same kernel is restore when resume. swsusp_arch_resume() creates a temporary page table that covering only the linear map. It copies the restore code to a 'safe' page, then start to restore the memory image. Once completed, it restores the original kernel's page table. It then calls into __hibernate_cpu_resume() to restore the CPU context. Finally, it follows the normal hibernation path back to the hibernation core. To enable hibernation/suspend to disk into RISCV, the below config need to be enabled: - CONFIG_ARCH_HIBERNATION_HEADER - CONFIG_ARCH_HIBERNATION_POSSIBLE Signed-off-by: Sia Jee Heng Reviewed-by: Ley Foon Tan Reviewed-by: Mason Huo Reviewed-by: Conor Dooley --- arch/riscv/Kconfig | 6 + arch/riscv/include/asm/assembler.h | 20 ++ arch/riscv/include/asm/suspend.h | 19 ++ arch/riscv/kernel/Makefile | 1 + arch/riscv/kernel/asm-offsets.c | 5 + arch/riscv/kernel/hibernate-asm.S | 77 ++++++ arch/riscv/kernel/hibernate.c | 427 +++++++++++++++++++++++++++++ 7 files changed, 555 insertions(+) create mode 100644 arch/riscv/kernel/hibernate-asm.S create mode 100644 arch/riscv/kernel/hibernate.c diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index c5e42cc376048..473c2a1a68848 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -707,6 +707,12 @@ menu "Power management options" source "kernel/power/Kconfig" +config ARCH_HIBERNATION_POSSIBLE + def_bool y + +config ARCH_HIBERNATION_HEADER + def_bool HIBERNATION + endmenu # "Power management options" menu "CPU Power Management" diff --git a/arch/riscv/include/asm/assembler.h b/arch/riscv/include/asm/assembler.h index ba59d38f89372..44b1457d3e956 100644 --- a/arch/riscv/include/asm/assembler.h +++ b/arch/riscv/include/asm/assembler.h @@ -59,4 +59,24 @@ REG_L s11, (SUSPEND_CONTEXT_REGS + PT_S11)(a0) .endm +/* + * copy_page - copy 1 page (4KB) of data from source to destination + * @a0 - destination + * @a1 - source + */ + .macro copy_page a0, a1 + lui a2, 0x1 + add a2, a2, a0 +1 : + REG_L t0, 0(a1) + REG_L t1, SZREG(a1) + + REG_S t0, 0(a0) + REG_S t1, SZREG(a0) + + addi a0, a0, 2 * SZREG + addi a1, a1, 2 * SZREG + bne a2, a0, 1b + .endm + #endif /* __ASM_ASSEMBLER_H */ diff --git a/arch/riscv/include/asm/suspend.h b/arch/riscv/include/asm/suspend.h index 67e047445662a..02f87867389a9 100644 --- a/arch/riscv/include/asm/suspend.h +++ b/arch/riscv/include/asm/suspend.h @@ -21,6 +21,11 @@ struct suspend_context { #endif }; +/* + * Used by hibernation core and cleared during resume sequence + */ +extern int in_suspend; + /* Low-level CPU suspend entry function */ int __cpu_suspend_enter(struct suspend_context *context); @@ -36,4 +41,18 @@ int __cpu_resume_enter(unsigned long hartid, unsigned long context); /* Used to save and restore the CSRs */ void suspend_save_csrs(struct suspend_context *context); void suspend_restore_csrs(struct suspend_context *context); + +/* Low-level API to support hibernation */ +int swsusp_arch_suspend(void); +int swsusp_arch_resume(void); +int arch_hibernation_header_save(void *addr, unsigned int max_size); +int arch_hibernation_header_restore(void *addr); +int __hibernate_cpu_resume(void); + +/* Used to resume on the CPU we hibernated on */ +int hibernate_resume_nonboot_cpu_disable(void); + +asmlinkage void hibernate_restore_image(unsigned long resume_satp, unsigned long satp_temp, + unsigned long cpu_resume); +asmlinkage int hibernate_core_restore_code(void); #endif diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile index 4cf303a779ab9..daab341d55e43 100644 --- a/arch/riscv/kernel/Makefile +++ b/arch/riscv/kernel/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_MODULE_SECTIONS) += module-sections.o obj-$(CONFIG_CPU_PM) += suspend_entry.o suspend.o +obj-$(CONFIG_HIBERNATION) += hibernate.o hibernate-asm.o obj-$(CONFIG_FUNCTION_TRACER) += mcount.o ftrace.o obj-$(CONFIG_DYNAMIC_FTRACE) += mcount-dyn.o diff --git a/arch/riscv/kernel/asm-offsets.c b/arch/riscv/kernel/asm-offsets.c index df9444397908d..d6a75aac1d27a 100644 --- a/arch/riscv/kernel/asm-offsets.c +++ b/arch/riscv/kernel/asm-offsets.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -116,6 +117,10 @@ void asm_offsets(void) OFFSET(SUSPEND_CONTEXT_REGS, suspend_context, regs); + OFFSET(HIBERN_PBE_ADDR, pbe, address); + OFFSET(HIBERN_PBE_ORIG, pbe, orig_address); + OFFSET(HIBERN_PBE_NEXT, pbe, next); + OFFSET(KVM_ARCH_GUEST_ZERO, kvm_vcpu_arch, guest_context.zero); OFFSET(KVM_ARCH_GUEST_RA, kvm_vcpu_arch, guest_context.ra); OFFSET(KVM_ARCH_GUEST_SP, kvm_vcpu_arch, guest_context.sp); diff --git a/arch/riscv/kernel/hibernate-asm.S b/arch/riscv/kernel/hibernate-asm.S new file mode 100644 index 0000000000000..effaf5ca5da0e --- /dev/null +++ b/arch/riscv/kernel/hibernate-asm.S @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Hibernation low level support for RISCV. + * + * Copyright (C) 2023 StarFive Technology Co., Ltd. + * + * Author: Jee Heng Sia + */ + +#include +#include +#include +#include + +#include + +/* + * int __hibernate_cpu_resume(void) + * Switch back to the hibernated image's page table prior to restoring the CPU + * context. + * + * Always returns 0 + */ +ENTRY(__hibernate_cpu_resume) + /* switch to hibernated image's page table. */ + csrw CSR_SATP, s0 + sfence.vma + + REG_L a0, hibernate_cpu_context + + suspend_restore_csrs + suspend_restore_regs + + /* Return zero value. */ + mv a0, zero + + ret +END(__hibernate_cpu_resume) + +/* + * Prepare to restore the image. + * a0: satp of saved page tables. + * a1: satp of temporary page tables. + * a2: cpu_resume. + */ +ENTRY(hibernate_restore_image) + mv s0, a0 + mv s1, a1 + mv s2, a2 + REG_L s4, restore_pblist + REG_L a1, relocated_restore_code + + jalr a1 +END(hibernate_restore_image) + +/* + * The below code will be executed from a 'safe' page. + * It first switches to the temporary page table, then starts to copy the pages + * back to the original memory location. Finally, it jumps to __hibernate_cpu_resume() + * to restore the CPU context. + */ +ENTRY(hibernate_core_restore_code) + /* switch to temp page table. */ + csrw satp, s1 + sfence.vma +.Lcopy: + /* The below code will restore the hibernated image. */ + REG_L a1, HIBERN_PBE_ADDR(s4) + REG_L a0, HIBERN_PBE_ORIG(s4) + + copy_page a0, a1 + + REG_L s4, HIBERN_PBE_NEXT(s4) + bnez s4, .Lcopy + + jalr s2 +END(hibernate_core_restore_code) diff --git a/arch/riscv/kernel/hibernate.c b/arch/riscv/kernel/hibernate.c new file mode 100644 index 0000000000000..f11be60b06686 --- /dev/null +++ b/arch/riscv/kernel/hibernate.c @@ -0,0 +1,427 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Hibernation support for RISCV + * + * Copyright (C) 2023 StarFive Technology Co., Ltd. + * + * Author: Jee Heng Sia + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* The logical cpu number we should resume on, initialised to a non-cpu number. */ +static int sleep_cpu = -EINVAL; + +/* Pointer to the temporary resume page table. */ +static pgd_t *resume_pg_dir; + +/* CPU context to be saved. */ +struct suspend_context *hibernate_cpu_context; +EXPORT_SYMBOL_GPL(hibernate_cpu_context); + +unsigned long relocated_restore_code; +EXPORT_SYMBOL_GPL(relocated_restore_code); + +/** + * struct arch_hibernate_hdr_invariants - container to store kernel build version. + * @uts_version: to save the build number and date so that the we do not resume with + * a different kernel. + */ +struct arch_hibernate_hdr_invariants { + char uts_version[__NEW_UTS_LEN + 1]; +}; + +/** + * struct arch_hibernate_hdr - helper parameters that help us to restore the image. + * @invariants: container to store kernel build version. + * @hartid: to make sure same boot_cpu executes the hibernate/restore code. + * @saved_satp: original page table used by the hibernated image. + * @restore_cpu_addr: the kernel's image address to restore the CPU context. + */ +static struct arch_hibernate_hdr { + struct arch_hibernate_hdr_invariants invariants; + unsigned long hartid; + unsigned long saved_satp; + unsigned long restore_cpu_addr; +} resume_hdr; + +static void arch_hdr_invariants(struct arch_hibernate_hdr_invariants *i) +{ + memset(i, 0, sizeof(*i)); + memcpy(i->uts_version, init_utsname()->version, sizeof(i->uts_version)); +} + +/* + * Check if the given pfn is in the 'nosave' section. + */ +int pfn_is_nosave(unsigned long pfn) +{ + unsigned long nosave_begin_pfn = sym_to_pfn(&__nosave_begin); + unsigned long nosave_end_pfn = sym_to_pfn(&__nosave_end - 1); + + return ((pfn >= nosave_begin_pfn) && (pfn <= nosave_end_pfn)); +} + +void notrace save_processor_state(void) +{ + WARN_ON(num_online_cpus() != 1); +} + +void notrace restore_processor_state(void) +{ +} + +/* + * Helper parameters need to be saved to the hibernation image header. + */ +int arch_hibernation_header_save(void *addr, unsigned int max_size) +{ + struct arch_hibernate_hdr *hdr = addr; + + if (max_size < sizeof(*hdr)) + return -EOVERFLOW; + + arch_hdr_invariants(&hdr->invariants); + + hdr->hartid = cpuid_to_hartid_map(sleep_cpu); + hdr->saved_satp = csr_read(CSR_SATP); + hdr->restore_cpu_addr = (unsigned long)__hibernate_cpu_resume; + + return 0; +} +EXPORT_SYMBOL_GPL(arch_hibernation_header_save); + +/* + * Retrieve the helper parameters from the hibernation image header. + */ +int arch_hibernation_header_restore(void *addr) +{ + struct arch_hibernate_hdr_invariants invariants; + struct arch_hibernate_hdr *hdr = addr; + int ret = 0; + + arch_hdr_invariants(&invariants); + + if (memcmp(&hdr->invariants, &invariants, sizeof(invariants))) { + pr_crit("Hibernate image not generated by this kernel!\n"); + return -EINVAL; + } + + sleep_cpu = riscv_hartid_to_cpuid(hdr->hartid); + if (sleep_cpu < 0) { + pr_crit("Hibernated on a CPU not known to this kernel!\n"); + sleep_cpu = -EINVAL; + return -EINVAL; + } + +#ifdef CONFIG_SMP + ret = bringup_hibernate_cpu(sleep_cpu); + if (ret) { + sleep_cpu = -EINVAL; + return ret; + } +#endif + resume_hdr = *hdr; + + return ret; +} +EXPORT_SYMBOL_GPL(arch_hibernation_header_restore); + +int swsusp_arch_suspend(void) +{ + int ret = 0; + + if (__cpu_suspend_enter(hibernate_cpu_context)) { + sleep_cpu = smp_processor_id(); + suspend_save_csrs(hibernate_cpu_context); + ret = swsusp_save(); + } else { + suspend_restore_csrs(hibernate_cpu_context); + flush_tlb_all(); + flush_icache_all(); + + /* + * Tell the hibernation core that we've just restored the memory. + */ + in_suspend = 0; + sleep_cpu = -EINVAL; + } + + return ret; +} + +static int temp_pgtable_map_pte(pmd_t *dst_pmdp, pmd_t *src_pmdp, unsigned long start, + unsigned long end, pgprot_t prot) +{ + pte_t *src_ptep; + pte_t *dst_ptep; + + if (pmd_none(READ_ONCE(*dst_pmdp))) { + dst_ptep = (pte_t *)get_safe_page(GFP_ATOMIC); + if (!dst_ptep) + return -ENOMEM; + + pmd_populate_kernel(NULL, dst_pmdp, dst_ptep); + } + + dst_ptep = pte_offset_kernel(dst_pmdp, start); + src_ptep = pte_offset_kernel(src_pmdp, start); + + do { + pte_t pte = READ_ONCE(*src_ptep); + + if (pte_present(pte)) + set_pte(dst_ptep, __pte(pte_val(pte) | pgprot_val(prot))); + } while (dst_ptep++, src_ptep++, start += PAGE_SIZE, start < end); + + return 0; +} + +static int temp_pgtable_map_pmd(pud_t *dst_pudp, pud_t *src_pudp, unsigned long start, + unsigned long end, pgprot_t prot) +{ + unsigned long next; + unsigned long ret; + pmd_t *src_pmdp; + pmd_t *dst_pmdp; + + if (pud_none(READ_ONCE(*dst_pudp))) { + dst_pmdp = (pmd_t *)get_safe_page(GFP_ATOMIC); + if (!dst_pmdp) + return -ENOMEM; + + pud_populate(NULL, dst_pudp, dst_pmdp); + } + + dst_pmdp = pmd_offset(dst_pudp, start); + src_pmdp = pmd_offset(src_pudp, start); + + do { + pmd_t pmd = READ_ONCE(*src_pmdp); + + next = pmd_addr_end(start, end); + + if (pmd_none(pmd)) + continue; + + if (pmd_leaf(pmd)) { + set_pmd(dst_pmdp, __pmd(pmd_val(pmd) | pgprot_val(prot))); + } else { + ret = temp_pgtable_map_pte(dst_pmdp, src_pmdp, start, next, prot); + if (ret) + return -ENOMEM; + } + } while (dst_pmdp++, src_pmdp++, start = next, start != end); + + return 0; +} + +static int temp_pgtable_map_pud(p4d_t *dst_p4dp, p4d_t *src_p4dp, unsigned long start, + unsigned long end, pgprot_t prot) +{ + unsigned long next; + unsigned long ret; + pud_t *dst_pudp; + pud_t *src_pudp; + + if (p4d_none(READ_ONCE(*dst_p4dp))) { + dst_pudp = (pud_t *)get_safe_page(GFP_ATOMIC); + if (!dst_pudp) + return -ENOMEM; + + p4d_populate(NULL, dst_p4dp, dst_pudp); + } + + dst_pudp = pud_offset(dst_p4dp, start); + src_pudp = pud_offset(src_p4dp, start); + + do { + pud_t pud = READ_ONCE(*src_pudp); + + next = pud_addr_end(start, end); + + if (pud_none(pud)) + continue; + + if (pud_leaf(pud)) { + set_pud(dst_pudp, __pud(pud_val(pud) | pgprot_val(prot))); + } else { + ret = temp_pgtable_map_pmd(dst_pudp, src_pudp, start, next, prot); + if (ret) + return -ENOMEM; + } + } while (dst_pudp++, src_pudp++, start = next, start != end); + + return 0; +} + +static int temp_pgtable_map_p4d(pgd_t *dst_pgdp, pgd_t *src_pgdp, unsigned long start, + unsigned long end, pgprot_t prot) +{ + unsigned long next; + unsigned long ret; + p4d_t *dst_p4dp; + p4d_t *src_p4dp; + + if (pgd_none(READ_ONCE(*dst_pgdp))) { + dst_p4dp = (p4d_t *)get_safe_page(GFP_ATOMIC); + if (!dst_p4dp) + return -ENOMEM; + + pgd_populate(NULL, dst_pgdp, dst_p4dp); + } + + dst_p4dp = p4d_offset(dst_pgdp, start); + src_p4dp = p4d_offset(src_pgdp, start); + + do { + p4d_t p4d = READ_ONCE(*src_p4dp); + + next = p4d_addr_end(start, end); + + if (p4d_none(p4d)) + continue; + + if (p4d_leaf(p4d)) { + set_p4d(dst_p4dp, __p4d(p4d_val(p4d) | pgprot_val(prot))); + } else { + ret = temp_pgtable_map_pud(dst_p4dp, src_p4dp, start, next, prot); + if (ret) + return -ENOMEM; + } + } while (dst_p4dp++, src_p4dp++, start = next, start != end); + + return 0; +} + +static int temp_pgtable_mapping(pgd_t *pgdp, unsigned long start, unsigned long end, pgprot_t prot) +{ + pgd_t *dst_pgdp = pgd_offset_pgd(pgdp, start); + pgd_t *src_pgdp = pgd_offset_k(start); + unsigned long next; + unsigned long ret; + + do { + pgd_t pgd = READ_ONCE(*src_pgdp); + + next = pgd_addr_end(start, end); + + if (pgd_none(pgd)) + continue; + + if (pgd_leaf(pgd)) { + set_pgd(dst_pgdp, __pgd(pgd_val(pgd) | pgprot_val(prot))); + } else { + ret = temp_pgtable_map_p4d(dst_pgdp, src_pgdp, start, next, prot); + if (ret) + return -ENOMEM; + } + } while (dst_pgdp++, src_pgdp++, start = next, start != end); + + return 0; +} + +static unsigned long relocate_restore_code(void) +{ + void *page = (void *)get_safe_page(GFP_ATOMIC); + + if (!page) + return -ENOMEM; + + copy_page(page, hibernate_core_restore_code); + + /* Make the page containing the relocated code executable. */ + set_memory_x((unsigned long)page, 1); + + return (unsigned long)page; +} + +int swsusp_arch_resume(void) +{ + unsigned long end = (unsigned long)pfn_to_virt(max_low_pfn); + unsigned long start = PAGE_OFFSET; + int ret; + + /* + * Memory allocated by get_safe_page() will be dealt with by the hibernation core, + * we don't need to free it here. + */ + resume_pg_dir = (pgd_t *)get_safe_page(GFP_ATOMIC); + if (!resume_pg_dir) + return -ENOMEM; + + /* + * Create a temporary page table and map the whole linear region as executable and + * writable. + */ + ret = temp_pgtable_mapping(resume_pg_dir, start, end, __pgprot(_PAGE_WRITE | _PAGE_EXEC)); + if (ret) + return ret; + + /* Move the restore code to a new page so that it doesn't get overwritten by itself. */ + relocated_restore_code = relocate_restore_code(); + if (relocated_restore_code == -ENOMEM) + return -ENOMEM; + + /* + * Map the __hibernate_cpu_resume() address to the temporary page table so that the + * restore code can jumps to it after finished restore the image. The next execution + * code doesn't find itself in a different address space after switching over to the + * original page table used by the hibernated image. + * The __hibernate_cpu_resume() mapping is unnecessary for RV32 since the kernel and + * linear addresses are identical, but different for RV64. To ensure consistency, we + * map it for both RV32 and RV64 kernels. + * Additionally, we should ensure that the page is writable before restoring the image. + */ + start = (unsigned long)resume_hdr.restore_cpu_addr; + end = start + PAGE_SIZE; + + ret = temp_pgtable_mapping(resume_pg_dir, start, end, __pgprot(_PAGE_WRITE)); + if (ret) + return ret; + + hibernate_restore_image(resume_hdr.saved_satp, (PFN_DOWN(__pa(resume_pg_dir)) | satp_mode), + resume_hdr.restore_cpu_addr); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP_SMP +int hibernate_resume_nonboot_cpu_disable(void) +{ + if (sleep_cpu < 0) { + pr_err("Failing to resume from hibernate on an unknown CPU\n"); + return -ENODEV; + } + + return freeze_secondary_cpus(sleep_cpu); +} +#endif + +static int __init riscv_hibernate_init(void) +{ + hibernate_cpu_context = kzalloc(sizeof(*hibernate_cpu_context), GFP_KERNEL); + + if (WARN_ON(!hibernate_cpu_context)) + return -ENOMEM; + + return 0; +} + +early_initcall(riscv_hibernate_init); From cea31b2516edef9908d43ca7d0b4c34db45cc5ac Mon Sep 17 00:00:00 2001 From: Hal Feng <87058983+hal-feng@users.noreply.github.com> Date: Tue, 20 Dec 2022 16:18:35 +0800 Subject: [PATCH 94/95] [NOT-FOR-UPSTREAM] Add readme --- README.md | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000000000..75060cf81f297 --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +## JH7110 Upstream Status ## + +To get the latest status of each upstreaming patch series, please visit +our RVspace. + +https://rvspace.org/en/project/JH7110_Upstream_Plan + +## Build Instructions ## + +1. Configure Kconfig options + +```shell +# Use default selections +make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- defconfig +``` + +or + +```shell +# Select options manually +make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- nconfig +``` + +To boot up the VisionFive 2 board, please make sure **SOC_STARFIVE**, +**CLK_STARFIVE_JH7110_SYS**, **PINCTRL_STARFIVE_JH7110_SYS**, +**SERIAL_8250_DW** are selected. +> If you need MMC and GMAC drivers, you should also select +**MMC_DW_STARFIVE** and **DWMAC_STARFIVE**. + +2. Build +```shell +make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- +``` + +## How to Run on VisionFive 2 Board via Network ## + +1. Power on, enter u-boot and set enviroment parameters +``` +setenv fdt_high 0xffffffffffffffff; setenv initrd_high 0xffffffffffffffff; +setenv scriptaddr 0x88100000; setenv script_offset_f 0x1fff000; setenv script_size_f 0x1000; +setenv kernel_addr_r 0x84000000; setenv kernel_comp_addr_r 0x90000000; setenv kernel_comp_size 0x10000000; +setenv fdt_addr_r 0x88000000; setenv ramdisk_addr_r 0x88300000; +``` +2. Set IP addresses for the board and your tftp server +``` +setenv serverip 192.168.w.x; setenv gatewayip 192.168.w.y; setenv ipaddr 192.168.w.z; setenv hostname starfive; setenv netdev eth0; +``` +3. Upload dtb, image and file system to DDR from your tftp server +``` +tftpboot ${fdt_addr_r} jh7110-starfive-visionfive-2-v1.3b.dtb; tftpboot ${kernel_addr_r} Image.gz; tftpboot ${ramdisk_addr_r} initramfs.cpio.gz; +``` +> If your VisionFive 2 is v1.2A, you should upload jh7110-starfive-visionfive-2-v1.2a.dtb instead. +4. Load and boot the kernel +``` +booti ${kernel_addr_r} ${ramdisk_addr_r}:${filesize} ${fdt_addr_r}; +``` +When you see the message "buildroot login:", the launch was successful. +You can just input the following accout and password, then continue. +``` +buildroot login: root +Password: starfive +``` From 90ef854c1b3b4f2fd44929e7ea5d427c44e4ec5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Stelmach?= Date: Wed, 29 Mar 2023 14:28:23 +0200 Subject: [PATCH 95/95] media: starfive: add "WITH Linux-syscall-note" to SPDX tag of uapi headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UAPI headers licensed under GPL are supposed to have exception "WITH Linux-syscall-note" so that they can be included into non-GPL user space application code. Signed-off-by: Ɓukasz Stelmach --- include/uapi/linux/stf_isp_ioctl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/linux/stf_isp_ioctl.h b/include/uapi/linux/stf_isp_ioctl.h index 3f302ef235d2c..84a584af3ab8f 100644 --- a/include/uapi/linux/stf_isp_ioctl.h +++ b/include/uapi/linux/stf_isp_ioctl.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * stf_isp_ioctl.h *