From 89eff11485479586f08b3461c74259d1860feb6d Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Fri, 28 Jun 2024 19:40:50 +0200 Subject: clk: mediatek: add support for parent mux from different source There is a current limitation where parents for a mux can be all declared as they are from a common source. This is not true as there are some MUX that can have parent from both infracfg or from topckgen. To handle this, implement a new flag for the mux, CLK_PARENT_MIXED, and a new entry for the mux parent_flags. To use this, CLK_PARENT_MIXED must be used and parent_flags will be used instead of the parent variable. Entry in parent_flags are just a struct of ID and flags where it will be defined where that parent comes from with the usage of CLK_PARENT_INFRASYS or CLK_PARENT_TOPCKGEN. This permits to have MUX with parents from infracfg or topckgen. Notice that with CLK_PARENT_MIXED applied the CLK_BYPASS_XTAL is ignored. With CLK_PARENT_MIXED declare CLK_PARENT_XTAL for the relevant parent instead. Also alias for the CLK_PARENT macro are provided to better clear their usage. CLK_PARENT_MIXED require these alias that describe the clk type to be defined in the clk_tree flags to prevent clk ID clash from different subsystem that may have equal clk ID. Signed-off-by: Christian Marangi --- drivers/clk/mediatek/clk-mtk.c | 69 +++++++++++++++++++++++++++++++----------- drivers/clk/mediatek/clk-mtk.h | 43 ++++++++++++++++++++++++-- 2 files changed, 91 insertions(+), 21 deletions(-) (limited to 'drivers/clk') diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c index 1d5ae8f..592f806 100644 --- a/drivers/clk/mediatek/clk-mtk.c +++ b/drivers/clk/mediatek/clk-mtk.c @@ -55,13 +55,27 @@ static ulong mtk_clk_find_parent_rate(struct clk *clk, int id, } static int mtk_clk_mux_set_parent(void __iomem *base, u32 parent, + u32 parent_type, const struct mtk_composite *mux) { u32 val, index = 0; - while (mux->parent[index] != parent) - if (++index == mux->num_parents) - return -EINVAL; + if (mux->flags & CLK_PARENT_MIXED) { + /* + * Assume parent_type in clk_tree to be always set with + * CLK_PARENT_MIXED implementation. If it's not, assume + * not parent clk ID clash is possible. + */ + while (mux->parent_flags[index].id != parent || + (parent_type && (mux->parent_flags[index].flags & CLK_PARENT_MASK) != + parent_type)) + if (++index == mux->num_parents) + return -EINVAL; + } else { + while (mux->parent[index] != parent) + if (++index == mux->num_parents) + return -EINVAL; + } if (mux->flags & CLK_MUX_SETCLR_UPD) { val = (mux->mux_mask << mux->mux_shift); @@ -353,6 +367,19 @@ static ulong mtk_topckgen_get_mux_rate(struct clk *clk, u32 off) return priv->tree->xtal_rate; } +static ulong mtk_find_parent_rate(struct mtk_clk_priv *priv, struct clk *clk, + const int parent, u16 flags) +{ + switch (flags & CLK_PARENT_MASK) { + case CLK_PARENT_XTAL: + return priv->tree->xtal_rate; + case CLK_PARENT_TOPCKGEN: + return mtk_clk_find_parent_rate(clk, parent, priv->parent); + default: + return mtk_clk_find_parent_rate(clk, parent, NULL); + } +} + static ulong mtk_infrasys_get_mux_rate(struct clk *clk, u32 off) { struct mtk_clk_priv *priv = dev_get_priv(clk->dev); @@ -363,21 +390,21 @@ static ulong mtk_infrasys_get_mux_rate(struct clk *clk, u32 off) index &= mux->mux_mask << mux->mux_shift; index = index >> mux->mux_shift; - if (mux->parent[index] > 0 || - (mux->parent[index] == CLK_XTAL && - priv->tree->flags & CLK_BYPASS_XTAL)) { - switch (mux->flags & CLK_PARENT_MASK) { - case CLK_PARENT_TOPCKGEN: - return mtk_clk_find_parent_rate(clk, mux->parent[index], - priv->parent); - break; - default: - return mtk_clk_find_parent_rate(clk, mux->parent[index], - NULL); - break; - } + /* + * Parents can be either from TOPCKGEN or INFRACFG, + * inspect the mtk_parent struct to check the source + */ + if (mux->flags & CLK_PARENT_MIXED) { + const struct mtk_parent *parent = &mux->parent_flags[index]; + + return mtk_find_parent_rate(priv, clk, parent->id, parent->flags); } - return priv->tree->xtal_rate; + + if (mux->parent[index] == CLK_XTAL && + !(priv->tree->flags & CLK_BYPASS_XTAL)) + return priv->tree->xtal_rate; + + return mtk_find_parent_rate(priv, clk, mux->parent[index], mux->flags); } static ulong mtk_topckgen_get_rate(struct clk *clk) @@ -491,12 +518,18 @@ static int mtk_clk_mux_disable(struct clk *clk) static int mtk_common_clk_set_parent(struct clk *clk, struct clk *parent) { + struct mtk_clk_priv *parent_priv = dev_get_priv(parent->dev); struct mtk_clk_priv *priv = dev_get_priv(clk->dev); + u32 parent_type; if (clk->id < priv->tree->muxes_offs) return 0; - return mtk_clk_mux_set_parent(priv->base, parent->id, + if (!parent_priv) + return 0; + + parent_type = parent_priv->tree->flags & CLK_PARENT_MASK; + return mtk_clk_mux_set_parent(priv->base, parent->id, parent_type, &priv->tree->muxes[clk->id - priv->tree->muxes_offs]); } diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h index 8dde9e5..4423689 100644 --- a/drivers/clk/mediatek/clk-mtk.h +++ b/drivers/clk/mediatek/clk-mtk.h @@ -13,7 +13,11 @@ /* flags in struct mtk_clk_tree */ -/* clk id == 0 doesn't mean it's xtal clk */ +/* clk id == 0 doesn't mean it's xtal clk + * This doesn't apply when CLK_PARENT_MIXED is defined. + * With CLK_PARENT_MIXED declare CLK_PARENT_XTAL for the + * relevant parent. + */ #define CLK_BYPASS_XTAL BIT(0) #define HAVE_RST_BAR BIT(0) @@ -30,7 +34,17 @@ #define CLK_PARENT_TOPCKGEN BIT(5) #define CLK_PARENT_INFRASYS BIT(6) #define CLK_PARENT_XTAL BIT(7) -#define CLK_PARENT_MASK GENMASK(7, 4) +/* + * For CLK_PARENT_MIXED to correctly work, is required to + * define in clk_tree flags the clk type using the alias. + */ +#define CLK_PARENT_MIXED BIT(8) +#define CLK_PARENT_MASK GENMASK(8, 4) + +/* alias to reference clk type */ +#define CLK_APMIXED CLK_PARENT_APMIXED +#define CLK_TOPCKGEN CLK_PARENT_TOPCKGEN +#define CLK_INFRASYS CLK_PARENT_INFRASYS #define ETHSYS_HIFSYS_RST_CTRL_OFS 0x34 @@ -98,10 +112,30 @@ struct mtk_fixed_factor { } /** + * struct mtk_parent - clock parent with flags. Needed for MUX that + * parent with mixed infracfg and topckgen. + * + * @id: index of parent clocks + * @flags: hardware-specific flags (parent location, + * infracfg, topckgen, APMIXED, xtal ...) + */ +struct mtk_parent { + const int id; + u16 flags; +}; + +#define PARENT(_id, _flags) { \ + .id = _id, \ + .flags = _flags, \ + } + +/** * struct mtk_composite - aggregate clock of mux, divider and gate clocks * * @id: index of clocks * @parent: index of parnet clocks + * @parent: index of parnet clocks + * @parent_flags: table of parent clocks with flags * @mux_reg: hardware-specific mux register * @gate_reg: hardware-specific gate register * @mux_mask: mask to the mux bit field @@ -112,7 +146,10 @@ struct mtk_fixed_factor { */ struct mtk_composite { const int id; - const int *parent; + union { + const int *parent; + const struct mtk_parent *parent_flags; + }; u32 mux_reg; u32 mux_set_reg; u32 mux_clr_reg; -- cgit v1.1