summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/clk/mediatek/clk-mtk.c69
-rw-r--r--drivers/clk/mediatek/clk-mtk.h43
2 files changed, 91 insertions, 21 deletions
diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c
index 1d5ae8f236..592f8062e5 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 8dde9e56e8..4423689803 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;