Il 07/03/25 04:26, Guangjie Song ha scritto:
Add data fields, defines and ops to support voting for mux.
The main thing that is missing here is an answer to an obvious question....
...what are the advantages of hardware voting, and why do we need to use
HW voting instead of the refcount that is already kept by the common clock
framework?
As far as I can see here, the only difference is that the enable/disable
is more complex, losing more time for polling after writes and nothing else?
Is this to synchronize the clock voting between SCP and AP or what?!
If this is the answer, I don't see why we should use this HW voter for all
clocks, since it's simply more expensive (so the clock drivers are wrong as
they enable the voter for all clocks).
Signed-off-by: Guangjie Song <guangjie.song@xxxxxxxxxxxx>
---
drivers/clk/mediatek/clk-mux.c | 198 ++++++++++++++++++++++++++++++++-
drivers/clk/mediatek/clk-mux.h | 79 +++++++++++++
2 files changed, 275 insertions(+), 2 deletions(-)
diff --git a/drivers/clk/mediatek/clk-mux.c b/drivers/clk/mediatek/clk-mux.c
index 60990296450b..8a2c89cb3cd5 100644
--- a/drivers/clk/mediatek/clk-mux.c
+++ b/drivers/clk/mediatek/clk-mux.c
@@ -15,11 +15,13 @@
#include <linux/spinlock.h>
#include <linux/slab.h>
+#include "clk-mtk.h"
#include "clk-mux.h"
struct mtk_clk_mux {
struct clk_hw hw;
struct regmap *regmap;
+ struct regmap *vote_regmap;
const struct mtk_mux *data;
spinlock_t *lock;
bool reparent;
@@ -30,6 +32,46 @@ static inline struct mtk_clk_mux *to_mtk_clk_mux(struct clk_hw *hw)
return container_of(hw, struct mtk_clk_mux, hw);
}
+static int mtk_clk_mux_fenc_enable_setclr(struct clk_hw *hw)
+{
+ struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
+ unsigned long flags = 0;
+ u32 val = 0;
+ int i = 0;
+ int ret = 0;
+
+ if (mux->lock)
+ spin_lock_irqsave(mux->lock, flags);
+ else
+ __acquire(mux->lock);
+
+ regmap_write(mux->regmap, mux->data->clr_ofs, BIT(mux->data->gate_shift));
+
+ while (1) {
+ regmap_read(mux->regmap, mux->data->fenc_sta_mon_ofs, &val);
Why are you reinventing the wheel instead of just using regmap_read_poll_timeout()?
+
+ if ((val & BIT(mux->data->fenc_shift)) != 0)
+ break;
+
+ if (i < MTK_WAIT_FENC_DONE_CNT) {
+ udelay(MTK_WAIT_FENC_DONE_US);
+ } else {
+ pr_err("%s wait fenc done timeout\n", clk_hw_get_name(hw));
+ ret = -EBUSY;
+ break;
+ }
+
+ i++;
+ }
+
+ if (mux->lock)
+ spin_unlock_irqrestore(mux->lock, flags);
+ else
+ __release(mux->lock);
+
+ return ret;
+}
+
static int mtk_clk_mux_enable_setclr(struct clk_hw *hw)
{
struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
@@ -70,6 +112,16 @@ static void mtk_clk_mux_disable_setclr(struct clk_hw *hw)
BIT(mux->data->gate_shift));
}
+static int mtk_clk_mux_fenc_is_enabled(struct clk_hw *hw)
+{
+ struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
+ u32 val = 0;
+
+ regmap_read(mux->regmap, mux->data->fenc_sta_mon_ofs, &val);
+
+ return (val & BIT(mux->data->fenc_shift)) != 0;
That's just `return val & BIT(mux->data->fenc_shift);` ...
+}
+
static int mtk_clk_mux_is_enabled(struct clk_hw *hw)
{
struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
@@ -80,6 +132,106 @@ static int mtk_clk_mux_is_enabled(struct clk_hw *hw)
return (val & BIT(mux->data->gate_shift)) == 0;
}
+static int mtk_clk_vote_mux_is_enabled(struct clk_hw *hw)
+{
+ struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
+ u32 val = 0;
+
+ regmap_read(mux->vote_regmap, mux->data->vote_set_ofs, &val);
+
+ return (val & BIT(mux->data->gate_shift)) != 0;
same
+}
+
+static int mtk_clk_vote_mux_is_done(struct clk_hw *hw)
+{
+ struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
+ u32 val = 0;
+
+ regmap_read(mux->vote_regmap, mux->data->vote_sta_ofs, &val);
+
+ return (val & BIT(mux->data->gate_shift)) != 0;
ditto
+}
+
+static int mtk_clk_mux_vote_fenc_enable(struct clk_hw *hw)
+{
+ struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
+ u32 val = 0, val2 = 0;
+ bool is_done = false;
+ int i = 0;
+
+ regmap_write(mux->vote_regmap, mux->data->vote_set_ofs, BIT(mux->data->gate_shift));
+
+ while (!mtk_clk_vote_mux_is_enabled(hw)) {
+ if (i < MTK_WAIT_VOTE_PREPARE_CNT) {
+ udelay(MTK_WAIT_VOTE_PREPARE_US);
regmap_readl_poll_timeout().....
+ } else {
+ pr_err("%s mux prepare timeout(%x)\n", clk_hw_get_name(hw), val);
+ return -EBUSY;
+ }
+
+ i++;
+ }
+
+ i = 0;
+
+ while (1) {
+ if (!is_done)
+ regmap_read(mux->vote_regmap, mux->data->vote_sta_ofs, &val);
+
+ if (((val & BIT(mux->data->gate_shift)) != 0))
+ is_done = true;
+
and again - twice.
+ if (is_done) {
+ regmap_read(mux->regmap, mux->data->fenc_sta_mon_ofs, &val2);
+ if ((val2 & BIT(mux->data->fenc_shift)) != 0)
+ break;
+ }
+
+ if (i < MTK_WAIT_VOTE_DONE_CNT) {
+ udelay(MTK_WAIT_VOTE_DONE_US);
+ } else {
+ pr_err("%s mux enable timeout(%x %x)\n", clk_hw_get_name(hw), val, val2);
+ return -EBUSY;
+ }
+
+ i++;
+ }
+
+ return 0;
+}
+
+static void mtk_clk_mux_vote_disable(struct clk_hw *hw)
+{
+ struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
+ int i = 0;
+
+ regmap_write(mux->vote_regmap, mux->data->vote_clr_ofs, BIT(mux->data->gate_shift));
+
+ while (mtk_clk_vote_mux_is_enabled(hw)) {
+ if (i < MTK_WAIT_VOTE_PREPARE_CNT) {
+ udelay(MTK_WAIT_VOTE_PREPARE_US);
+ } else {
+ pr_err("%s mux unprepare timeout\n", clk_hw_get_name(hw));
+ return;
+ }
+
....and again....
+ i++;
+ }
+
+ i = 0;
+
+ while (!mtk_clk_vote_mux_is_done(hw)) {
+ if (i < MTK_WAIT_VOTE_DONE_CNT) {
+ udelay(MTK_WAIT_VOTE_DONE_US);
+ } else {
+ pr_err("%s mux disable timeout\n", clk_hw_get_name(hw));
+ return;
+ }
+
+ i++;
+ }
+}
+
static u8 mtk_clk_mux_get_parent(struct clk_hw *hw)
{
struct mtk_clk_mux *mux = to_mtk_clk_mux(hw);
@@ -151,6 +303,12 @@ static int mtk_clk_mux_determine_rate(struct clk_hw *hw,
return clk_mux_determine_rate_flags(hw, req, mux->data->flags);
}
+static void mtk_clk_mux_vote_fenc_disable_unused(struct clk_hw *hw)
+{
+ mtk_clk_mux_vote_fenc_enable(hw);
+ mtk_clk_mux_vote_disable(hw);
Why would you need to enable and disable?
If this is not a mistake... this definitely needs a comment in the code.
+}
+
const struct clk_ops mtk_mux_clr_set_upd_ops = {
.get_parent = mtk_clk_mux_get_parent,
.set_parent = mtk_clk_mux_set_parent_setclr_lock,
@@ -168,9 +326,31 @@ const struct clk_ops mtk_mux_gate_clr_set_upd_ops = {
};
EXPORT_SYMBOL_GPL(mtk_mux_gate_clr_set_upd_ops);
+const struct clk_ops mtk_mux_gate_fenc_clr_set_upd_ops = {
+ .enable = mtk_clk_mux_fenc_enable_setclr,
+ .disable = mtk_clk_mux_disable_setclr,
+ .is_enabled = mtk_clk_mux_fenc_is_enabled,
+ .get_parent = mtk_clk_mux_get_parent,
+ .set_parent = mtk_clk_mux_set_parent_setclr_lock,
+ .determine_rate = mtk_clk_mux_determine_rate,
+};
+EXPORT_SYMBOL_GPL(mtk_mux_gate_fenc_clr_set_upd_ops);
+
+const struct clk_ops mtk_mux_vote_fenc_ops = {
+ .enable = mtk_clk_mux_vote_fenc_enable,
+ .disable = mtk_clk_mux_vote_disable,
+ .is_enabled = mtk_clk_mux_fenc_is_enabled,
+ .get_parent = mtk_clk_mux_get_parent,
+ .set_parent = mtk_clk_mux_set_parent_setclr_lock,
+ .determine_rate = mtk_clk_mux_determine_rate,
+ .disable_unused = mtk_clk_mux_vote_fenc_disable_unused,
+};
+EXPORT_SYMBOL_GPL(mtk_mux_vote_fenc_ops);
+
static struct clk_hw *mtk_clk_register_mux(struct device *dev,
const struct mtk_mux *mux,
struct regmap *regmap,
+ struct regmap *vote_regmap,
spinlock_t *lock)
{
struct mtk_clk_mux *clk_mux;
@@ -185,9 +365,17 @@ static struct clk_hw *mtk_clk_register_mux(struct device *dev,
init.flags = mux->flags;
init.parent_names = mux->parent_names;
init.num_parents = mux->num_parents;
- init.ops = mux->ops;
+ if (mux->flags & CLK_USE_VOTE) {
+ if (vote_regmap)
+ init.ops = mux->ops;
+ else
+ init.ops = mux->dma_ops;
Sorry why is this called dma_ops?!
That's at least confusing, if not simply wrong.... please explain.
+ } else {
+ init.ops = mux->ops;
+ }
clk_mux->regmap = regmap;
+ clk_mux->vote_regmap = vote_regmap;
clk_mux->data = mux;
clk_mux->lock = lock;
clk_mux->hw.init = &init;
@@ -220,6 +408,7 @@ int mtk_clk_register_muxes(struct device *dev,
struct clk_hw_onecell_data *clk_data)
{
struct regmap *regmap;
+ struct regmap *vote_regmap = NULL;
struct clk_hw *hw;
int i;
@@ -238,8 +427,13 @@ int mtk_clk_register_muxes(struct device *dev,
continue;
}
- hw = mtk_clk_register_mux(dev, mux, regmap, lock);
+ if (mux->vote_comp) {
+ vote_regmap = syscon_regmap_lookup_by_phandle(node, mux->vote_comp);
+ if (IS_ERR(vote_regmap))
+ vote_regmap = NULL;
+ }
+ hw = mtk_clk_register_mux(dev, mux, regmap, vote_regmap, lock);
...and this change just breaks each and every MediaTek SoC that is currently
supported upstream.
Please test your changes on older platforms before submitting upstream.
Regards,
Angelo