MT8196 use a HW voter for gate enable/disable control. Voting is
performed using set/clr regs, with a status bit used to verify the vote
state. Add new set of gate clock operations with support for voting via
set/clr regs.
Reviewed-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Laura Nao <laura.nao@collabora.com>
---
drivers/clk/mediatek/clk-gate.c | 77 +++++++++++++++++++++++++++++++--
drivers/clk/mediatek/clk-gate.h | 3 ++
2 files changed, 77 insertions(+), 3 deletions(-)
diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c
index 0375ccad4be3..426f3a25763d 100644
--- a/drivers/clk/mediatek/clk-gate.c
+++ b/drivers/clk/mediatek/clk-gate.c
@@ -5,6 +5,7 @@
*/
#include <linux/clk-provider.h>
+#include <linux/dev_printk.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/printk.h>
@@ -12,14 +13,19 @@
#include <linux/slab.h>
#include <linux/types.h>
+#include "clk-mtk.h"
#include "clk-gate.h"
struct mtk_clk_gate {
struct clk_hw hw;
struct regmap *regmap;
+ struct regmap *regmap_hwv;
int set_ofs;
int clr_ofs;
int sta_ofs;
+ unsigned int hwv_set_ofs;
+ unsigned int hwv_clr_ofs;
+ unsigned int hwv_sta_ofs;
u8 bit;
};
@@ -100,6 +106,28 @@ static void mtk_cg_disable_inv(struct clk_hw *hw)
mtk_cg_clr_bit(hw);
}
+static int mtk_cg_hwv_set_en(struct clk_hw *hw, bool enable)
+{
+ struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
+ u32 val;
+
+ regmap_write(cg->regmap_hwv, enable ? cg->hwv_set_ofs : cg->hwv_clr_ofs, BIT(cg->bit));
+
+ return regmap_read_poll_timeout_atomic(cg->regmap_hwv, cg->hwv_sta_ofs, val,
+ val & BIT(cg->bit),
+ 0, MTK_WAIT_HWV_DONE_US);
+}
+
+static int mtk_cg_hwv_enable(struct clk_hw *hw)
+{
+ return mtk_cg_hwv_set_en(hw, true);
+}
+
+static void mtk_cg_hwv_disable(struct clk_hw *hw)
+{
+ mtk_cg_hwv_set_en(hw, false);
+}
+
static int mtk_cg_enable_no_setclr(struct clk_hw *hw)
{
mtk_cg_clr_bit_no_setclr(hw);
@@ -124,6 +152,15 @@ static void mtk_cg_disable_inv_no_setclr(struct clk_hw *hw)
mtk_cg_clr_bit_no_setclr(hw);
}
+static bool mtk_cg_uses_hwv(const struct clk_ops *ops)
+{
+ if (ops == &mtk_clk_gate_hwv_ops_setclr ||
+ ops == &mtk_clk_gate_hwv_ops_setclr_inv)
+ return true;
+
+ return false;
+}
+
const struct clk_ops mtk_clk_gate_ops_setclr = {
.is_enabled = mtk_cg_bit_is_cleared,
.enable = mtk_cg_enable,
@@ -138,6 +175,20 @@ const struct clk_ops mtk_clk_gate_ops_setclr_inv = {
};
EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_setclr_inv);
+const struct clk_ops mtk_clk_gate_hwv_ops_setclr = {
+ .is_enabled = mtk_cg_bit_is_cleared,
+ .enable = mtk_cg_hwv_enable,
+ .disable = mtk_cg_hwv_disable,
+};
+EXPORT_SYMBOL_GPL(mtk_clk_gate_hwv_ops_setclr);
+
+const struct clk_ops mtk_clk_gate_hwv_ops_setclr_inv = {
+ .is_enabled = mtk_cg_bit_is_set,
+ .enable = mtk_cg_hwv_enable,
+ .disable = mtk_cg_hwv_disable,
+};
+EXPORT_SYMBOL_GPL(mtk_clk_gate_hwv_ops_setclr_inv);
+
const struct clk_ops mtk_clk_gate_ops_no_setclr = {
.is_enabled = mtk_cg_bit_is_cleared,
.enable = mtk_cg_enable_no_setclr,
@@ -153,8 +204,9 @@ const struct clk_ops mtk_clk_gate_ops_no_setclr_inv = {
EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_no_setclr_inv);
static struct clk_hw *mtk_clk_register_gate(struct device *dev,
- const struct mtk_gate *gate,
- struct regmap *regmap)
+ const struct mtk_gate *gate,
+ struct regmap *regmap,
+ struct regmap *regmap_hwv)
{
struct mtk_clk_gate *cg;
int ret;
@@ -169,11 +221,22 @@ static struct clk_hw *mtk_clk_register_gate(struct device *dev,
init.parent_names = gate->parent_name ? &gate->parent_name : NULL;
init.num_parents = gate->parent_name ? 1 : 0;
init.ops = gate->ops;
+ if (mtk_cg_uses_hwv(init.ops) && !regmap_hwv) {
+ dev_err(dev, "regmap not found for hardware voter clocks\n");
+ return ERR_PTR(-ENXIO);
+ }
cg->regmap = regmap;
+ cg->regmap_hwv = regmap_hwv;
cg->set_ofs = gate->regs->set_ofs;
cg->clr_ofs = gate->regs->clr_ofs;
cg->sta_ofs = gate->regs->sta_ofs;
+ if (gate->hwv_regs) {
+ cg->hwv_set_ofs = gate->hwv_regs->set_ofs;
+ cg->hwv_clr_ofs = gate->hwv_regs->clr_ofs;
+ cg->hwv_sta_ofs = gate->hwv_regs->sta_ofs;
+ }
+
cg->bit = gate->shift;
cg->hw.init = &init;
@@ -206,6 +269,7 @@ int mtk_clk_register_gates(struct device *dev, struct device_node *node,
int i;
struct clk_hw *hw;
struct regmap *regmap;
+ struct regmap *regmap_hwv;
if (!clk_data)
return -ENOMEM;
@@ -216,6 +280,13 @@ int mtk_clk_register_gates(struct device *dev, struct device_node *node,
return PTR_ERR(regmap);
}
+ regmap_hwv = mtk_clk_get_hwv_regmap(node);
+ if (IS_ERR(regmap_hwv)) {
+ pr_err("Cannot find hardware voter regmap for %pOF: %pe\n",
+ node, regmap_hwv);
+ return PTR_ERR(regmap_hwv);
+ }
+
for (i = 0; i < num; i++) {
const struct mtk_gate *gate = &clks[i];
@@ -225,7 +296,7 @@ int mtk_clk_register_gates(struct device *dev, struct device_node *node,
continue;
}
- hw = mtk_clk_register_gate(dev, gate, regmap);
+ hw = mtk_clk_register_gate(dev, gate, regmap, regmap_hwv);
if (IS_ERR(hw)) {
pr_err("Failed to register clk %s: %pe\n", gate->name,
diff --git a/drivers/clk/mediatek/clk-gate.h b/drivers/clk/mediatek/clk-gate.h
index 1a46b4c56fc5..4f05b9855dae 100644
--- a/drivers/clk/mediatek/clk-gate.h
+++ b/drivers/clk/mediatek/clk-gate.h
@@ -19,6 +19,8 @@ extern const struct clk_ops mtk_clk_gate_ops_setclr;
extern const struct clk_ops mtk_clk_gate_ops_setclr_inv;
extern const struct clk_ops mtk_clk_gate_ops_no_setclr;
extern const struct clk_ops mtk_clk_gate_ops_no_setclr_inv;
+extern const struct clk_ops mtk_clk_gate_hwv_ops_setclr;
+extern const struct clk_ops mtk_clk_gate_hwv_ops_setclr_inv;
struct mtk_gate_regs {
u32 sta_ofs;
@@ -31,6 +33,7 @@ struct mtk_gate {
const char *name;
const char *parent_name;
const struct mtk_gate_regs *regs;
+ const struct mtk_gate_regs *hwv_regs;
int shift;
const struct clk_ops *ops;
unsigned long flags;
--
2.39.5
On Tue, Aug 5, 2025 at 10:55 PM Laura Nao <laura.nao@collabora.com> wrote: > > MT8196 use a HW voter for gate enable/disable control. Voting is > performed using set/clr regs, with a status bit used to verify the vote > state. Add new set of gate clock operations with support for voting via > set/clr regs. > > Reviewed-by: Nícolas F. R. A. Prado <nfraprado@collabora.com> > Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> > Signed-off-by: Laura Nao <laura.nao@collabora.com> > --- > drivers/clk/mediatek/clk-gate.c | 77 +++++++++++++++++++++++++++++++-- > drivers/clk/mediatek/clk-gate.h | 3 ++ > 2 files changed, 77 insertions(+), 3 deletions(-) > > diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c > index 0375ccad4be3..426f3a25763d 100644 > --- a/drivers/clk/mediatek/clk-gate.c > +++ b/drivers/clk/mediatek/clk-gate.c > @@ -5,6 +5,7 @@ > */ > > #include <linux/clk-provider.h> > +#include <linux/dev_printk.h> > #include <linux/mfd/syscon.h> > #include <linux/module.h> > #include <linux/printk.h> > @@ -12,14 +13,19 @@ > #include <linux/slab.h> > #include <linux/types.h> > > +#include "clk-mtk.h" > #include "clk-gate.h" > > struct mtk_clk_gate { > struct clk_hw hw; > struct regmap *regmap; > + struct regmap *regmap_hwv; > int set_ofs; > int clr_ofs; > int sta_ofs; > + unsigned int hwv_set_ofs; > + unsigned int hwv_clr_ofs; > + unsigned int hwv_sta_ofs; > u8 bit; > }; > > @@ -100,6 +106,28 @@ static void mtk_cg_disable_inv(struct clk_hw *hw) > mtk_cg_clr_bit(hw); > } > > +static int mtk_cg_hwv_set_en(struct clk_hw *hw, bool enable) > +{ > + struct mtk_clk_gate *cg = to_mtk_clk_gate(hw); > + u32 val; > + > + regmap_write(cg->regmap_hwv, enable ? cg->hwv_set_ofs : cg->hwv_clr_ofs, BIT(cg->bit)); > + > + return regmap_read_poll_timeout_atomic(cg->regmap_hwv, cg->hwv_sta_ofs, val, > + val & BIT(cg->bit), > + 0, MTK_WAIT_HWV_DONE_US); > +} > + > +static int mtk_cg_hwv_enable(struct clk_hw *hw) > +{ > + return mtk_cg_hwv_set_en(hw, true); > +} > + > +static void mtk_cg_hwv_disable(struct clk_hw *hw) > +{ > + mtk_cg_hwv_set_en(hw, false); > +} > + > static int mtk_cg_enable_no_setclr(struct clk_hw *hw) > { > mtk_cg_clr_bit_no_setclr(hw); > @@ -124,6 +152,15 @@ static void mtk_cg_disable_inv_no_setclr(struct clk_hw *hw) > mtk_cg_clr_bit_no_setclr(hw); > } > > +static bool mtk_cg_uses_hwv(const struct clk_ops *ops) > +{ > + if (ops == &mtk_clk_gate_hwv_ops_setclr || > + ops == &mtk_clk_gate_hwv_ops_setclr_inv) > + return true; > + > + return false; > +} > + > const struct clk_ops mtk_clk_gate_ops_setclr = { > .is_enabled = mtk_cg_bit_is_cleared, > .enable = mtk_cg_enable, > @@ -138,6 +175,20 @@ const struct clk_ops mtk_clk_gate_ops_setclr_inv = { > }; > EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_setclr_inv); > > +const struct clk_ops mtk_clk_gate_hwv_ops_setclr = { > + .is_enabled = mtk_cg_bit_is_cleared, > + .enable = mtk_cg_hwv_enable, > + .disable = mtk_cg_hwv_disable, > +}; > +EXPORT_SYMBOL_GPL(mtk_clk_gate_hwv_ops_setclr); > + > +const struct clk_ops mtk_clk_gate_hwv_ops_setclr_inv = { > + .is_enabled = mtk_cg_bit_is_set, > + .enable = mtk_cg_hwv_enable, > + .disable = mtk_cg_hwv_disable, > +}; > +EXPORT_SYMBOL_GPL(mtk_clk_gate_hwv_ops_setclr_inv); > + > const struct clk_ops mtk_clk_gate_ops_no_setclr = { > .is_enabled = mtk_cg_bit_is_cleared, > .enable = mtk_cg_enable_no_setclr, > @@ -153,8 +204,9 @@ const struct clk_ops mtk_clk_gate_ops_no_setclr_inv = { > EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_no_setclr_inv); > > static struct clk_hw *mtk_clk_register_gate(struct device *dev, > - const struct mtk_gate *gate, > - struct regmap *regmap) > + const struct mtk_gate *gate, > + struct regmap *regmap, > + struct regmap *regmap_hwv) > { > struct mtk_clk_gate *cg; > int ret; > @@ -169,11 +221,22 @@ static struct clk_hw *mtk_clk_register_gate(struct device *dev, > init.parent_names = gate->parent_name ? &gate->parent_name : NULL; > init.num_parents = gate->parent_name ? 1 : 0; > init.ops = gate->ops; > + if (mtk_cg_uses_hwv(init.ops) && !regmap_hwv) { > + dev_err(dev, "regmap not found for hardware voter clocks\n"); > + return ERR_PTR(-ENXIO); return dev_err_probe()? I believe the same applies to the previous patch. > + } > > cg->regmap = regmap; > + cg->regmap_hwv = regmap_hwv; > cg->set_ofs = gate->regs->set_ofs; > cg->clr_ofs = gate->regs->clr_ofs; > cg->sta_ofs = gate->regs->sta_ofs; > + if (gate->hwv_regs) { > + cg->hwv_set_ofs = gate->hwv_regs->set_ofs; > + cg->hwv_clr_ofs = gate->hwv_regs->clr_ofs; > + cg->hwv_sta_ofs = gate->hwv_regs->sta_ofs; > + } > + > cg->bit = gate->shift; > > cg->hw.init = &init; > @@ -206,6 +269,7 @@ int mtk_clk_register_gates(struct device *dev, struct device_node *node, > int i; > struct clk_hw *hw; > struct regmap *regmap; > + struct regmap *regmap_hwv; > > if (!clk_data) > return -ENOMEM; > @@ -216,6 +280,13 @@ int mtk_clk_register_gates(struct device *dev, struct device_node *node, > return PTR_ERR(regmap); > } > > + regmap_hwv = mtk_clk_get_hwv_regmap(node); > + if (IS_ERR(regmap_hwv)) { > + pr_err("Cannot find hardware voter regmap for %pOF: %pe\n", > + node, regmap_hwv); > + return PTR_ERR(regmap_hwv); return dev_err_probe(); ChenYu > + } > + > for (i = 0; i < num; i++) { > const struct mtk_gate *gate = &clks[i]; > > @@ -225,7 +296,7 @@ int mtk_clk_register_gates(struct device *dev, struct device_node *node, > continue; > } > > - hw = mtk_clk_register_gate(dev, gate, regmap); > + hw = mtk_clk_register_gate(dev, gate, regmap, regmap_hwv); > > if (IS_ERR(hw)) { > pr_err("Failed to register clk %s: %pe\n", gate->name, > diff --git a/drivers/clk/mediatek/clk-gate.h b/drivers/clk/mediatek/clk-gate.h > index 1a46b4c56fc5..4f05b9855dae 100644 > --- a/drivers/clk/mediatek/clk-gate.h > +++ b/drivers/clk/mediatek/clk-gate.h > @@ -19,6 +19,8 @@ extern const struct clk_ops mtk_clk_gate_ops_setclr; > extern const struct clk_ops mtk_clk_gate_ops_setclr_inv; > extern const struct clk_ops mtk_clk_gate_ops_no_setclr; > extern const struct clk_ops mtk_clk_gate_ops_no_setclr_inv; > +extern const struct clk_ops mtk_clk_gate_hwv_ops_setclr; > +extern const struct clk_ops mtk_clk_gate_hwv_ops_setclr_inv; > > struct mtk_gate_regs { > u32 sta_ofs; > @@ -31,6 +33,7 @@ struct mtk_gate { > const char *name; > const char *parent_name; > const struct mtk_gate_regs *regs; > + const struct mtk_gate_regs *hwv_regs; > int shift; > const struct clk_ops *ops; > unsigned long flags; > -- > 2.39.5 >
On 8/15/25 05:37, Chen-Yu Tsai wrote: > On Tue, Aug 5, 2025 at 10:55 PM Laura Nao <laura.nao@collabora.com> wrote: >> >> MT8196 use a HW voter for gate enable/disable control. Voting is >> performed using set/clr regs, with a status bit used to verify the vote >> state. Add new set of gate clock operations with support for voting via >> set/clr regs. >> >> Reviewed-by: Nícolas F. R. A. Prado <nfraprado@collabora.com> >> Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> >> Signed-off-by: Laura Nao <laura.nao@collabora.com> >> --- >> drivers/clk/mediatek/clk-gate.c | 77 +++++++++++++++++++++++++++++++-- >> drivers/clk/mediatek/clk-gate.h | 3 ++ >> 2 files changed, 77 insertions(+), 3 deletions(-) >> >> diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c >> index 0375ccad4be3..426f3a25763d 100644 >> --- a/drivers/clk/mediatek/clk-gate.c >> +++ b/drivers/clk/mediatek/clk-gate.c >> @@ -5,6 +5,7 @@ >> */ >> >> #include <linux/clk-provider.h> >> +#include <linux/dev_printk.h> >> #include <linux/mfd/syscon.h> >> #include <linux/module.h> >> #include <linux/printk.h> >> @@ -12,14 +13,19 @@ >> #include <linux/slab.h> >> #include <linux/types.h> >> >> +#include "clk-mtk.h" >> #include "clk-gate.h" >> >> struct mtk_clk_gate { >> struct clk_hw hw; >> struct regmap *regmap; >> + struct regmap *regmap_hwv; >> int set_ofs; >> int clr_ofs; >> int sta_ofs; >> + unsigned int hwv_set_ofs; >> + unsigned int hwv_clr_ofs; >> + unsigned int hwv_sta_ofs; >> u8 bit; >> }; >> >> @@ -100,6 +106,28 @@ static void mtk_cg_disable_inv(struct clk_hw *hw) >> mtk_cg_clr_bit(hw); >> } >> >> +static int mtk_cg_hwv_set_en(struct clk_hw *hw, bool enable) >> +{ >> + struct mtk_clk_gate *cg = to_mtk_clk_gate(hw); >> + u32 val; >> + >> + regmap_write(cg->regmap_hwv, enable ? cg->hwv_set_ofs : cg->hwv_clr_ofs, BIT(cg->bit)); >> + >> + return regmap_read_poll_timeout_atomic(cg->regmap_hwv, cg->hwv_sta_ofs, val, >> + val & BIT(cg->bit), >> + 0, MTK_WAIT_HWV_DONE_US); >> +} >> + >> +static int mtk_cg_hwv_enable(struct clk_hw *hw) >> +{ >> + return mtk_cg_hwv_set_en(hw, true); >> +} >> + >> +static void mtk_cg_hwv_disable(struct clk_hw *hw) >> +{ >> + mtk_cg_hwv_set_en(hw, false); >> +} >> + >> static int mtk_cg_enable_no_setclr(struct clk_hw *hw) >> { >> mtk_cg_clr_bit_no_setclr(hw); >> @@ -124,6 +152,15 @@ static void mtk_cg_disable_inv_no_setclr(struct clk_hw *hw) >> mtk_cg_clr_bit_no_setclr(hw); >> } >> >> +static bool mtk_cg_uses_hwv(const struct clk_ops *ops) >> +{ >> + if (ops == &mtk_clk_gate_hwv_ops_setclr || >> + ops == &mtk_clk_gate_hwv_ops_setclr_inv) >> + return true; >> + >> + return false; >> +} >> + >> const struct clk_ops mtk_clk_gate_ops_setclr = { >> .is_enabled = mtk_cg_bit_is_cleared, >> .enable = mtk_cg_enable, >> @@ -138,6 +175,20 @@ const struct clk_ops mtk_clk_gate_ops_setclr_inv = { >> }; >> EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_setclr_inv); >> >> +const struct clk_ops mtk_clk_gate_hwv_ops_setclr = { >> + .is_enabled = mtk_cg_bit_is_cleared, >> + .enable = mtk_cg_hwv_enable, >> + .disable = mtk_cg_hwv_disable, >> +}; >> +EXPORT_SYMBOL_GPL(mtk_clk_gate_hwv_ops_setclr); >> + >> +const struct clk_ops mtk_clk_gate_hwv_ops_setclr_inv = { >> + .is_enabled = mtk_cg_bit_is_set, >> + .enable = mtk_cg_hwv_enable, >> + .disable = mtk_cg_hwv_disable, >> +}; >> +EXPORT_SYMBOL_GPL(mtk_clk_gate_hwv_ops_setclr_inv); >> + >> const struct clk_ops mtk_clk_gate_ops_no_setclr = { >> .is_enabled = mtk_cg_bit_is_cleared, >> .enable = mtk_cg_enable_no_setclr, >> @@ -153,8 +204,9 @@ const struct clk_ops mtk_clk_gate_ops_no_setclr_inv = { >> EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_no_setclr_inv); >> >> static struct clk_hw *mtk_clk_register_gate(struct device *dev, >> - const struct mtk_gate *gate, >> - struct regmap *regmap) >> + const struct mtk_gate *gate, >> + struct regmap *regmap, >> + struct regmap *regmap_hwv) >> { >> struct mtk_clk_gate *cg; >> int ret; >> @@ -169,11 +221,22 @@ static struct clk_hw *mtk_clk_register_gate(struct device *dev, >> init.parent_names = gate->parent_name ? &gate->parent_name : NULL; >> init.num_parents = gate->parent_name ? 1 : 0; >> init.ops = gate->ops; >> + if (mtk_cg_uses_hwv(init.ops) && !regmap_hwv) { >> + dev_err(dev, "regmap not found for hardware voter clocks\n"); >> + return ERR_PTR(-ENXIO); > > return dev_err_probe()? > > I believe the same applies to the previous patch. > mtk_clk_register_gate and mtk_clk_register_mux actually both return a struct clk_hw *. >> + } >> >> cg->regmap = regmap; >> + cg->regmap_hwv = regmap_hwv; >> cg->set_ofs = gate->regs->set_ofs; >> cg->clr_ofs = gate->regs->clr_ofs; >> cg->sta_ofs = gate->regs->sta_ofs; >> + if (gate->hwv_regs) { >> + cg->hwv_set_ofs = gate->hwv_regs->set_ofs; >> + cg->hwv_clr_ofs = gate->hwv_regs->clr_ofs; >> + cg->hwv_sta_ofs = gate->hwv_regs->sta_ofs; >> + } >> + >> cg->bit = gate->shift; >> >> cg->hw.init = &init; >> @@ -206,6 +269,7 @@ int mtk_clk_register_gates(struct device *dev, struct device_node *node, >> int i; >> struct clk_hw *hw; >> struct regmap *regmap; >> + struct regmap *regmap_hwv; >> >> if (!clk_data) >> return -ENOMEM; >> @@ -216,6 +280,13 @@ int mtk_clk_register_gates(struct device *dev, struct device_node *node, >> return PTR_ERR(regmap); >> } >> >> + regmap_hwv = mtk_clk_get_hwv_regmap(node); >> + if (IS_ERR(regmap_hwv)) { >> + pr_err("Cannot find hardware voter regmap for %pOF: %pe\n", >> + node, regmap_hwv); >> + return PTR_ERR(regmap_hwv); > > return dev_err_probe(); > Will do. Thanks, Laura > ChenYu > >> + } >> + >> for (i = 0; i < num; i++) { >> const struct mtk_gate *gate = &clks[i]; >> >> @@ -225,7 +296,7 @@ int mtk_clk_register_gates(struct device *dev, struct device_node *node, >> continue; >> } >> >> - hw = mtk_clk_register_gate(dev, gate, regmap); >> + hw = mtk_clk_register_gate(dev, gate, regmap, regmap_hwv); >> >> if (IS_ERR(hw)) { >> pr_err("Failed to register clk %s: %pe\n", gate->name, >> diff --git a/drivers/clk/mediatek/clk-gate.h b/drivers/clk/mediatek/clk-gate.h >> index 1a46b4c56fc5..4f05b9855dae 100644 >> --- a/drivers/clk/mediatek/clk-gate.h >> +++ b/drivers/clk/mediatek/clk-gate.h >> @@ -19,6 +19,8 @@ extern const struct clk_ops mtk_clk_gate_ops_setclr; >> extern const struct clk_ops mtk_clk_gate_ops_setclr_inv; >> extern const struct clk_ops mtk_clk_gate_ops_no_setclr; >> extern const struct clk_ops mtk_clk_gate_ops_no_setclr_inv; >> +extern const struct clk_ops mtk_clk_gate_hwv_ops_setclr; >> +extern const struct clk_ops mtk_clk_gate_hwv_ops_setclr_inv; >> >> struct mtk_gate_regs { >> u32 sta_ofs; >> @@ -31,6 +33,7 @@ struct mtk_gate { >> const char *name; >> const char *parent_name; >> const struct mtk_gate_regs *regs; >> + const struct mtk_gate_regs *hwv_regs; >> int shift; >> const struct clk_ops *ops; >> unsigned long flags; >> -- >> 2.39.5 >>
On Mon, Aug 25, 2025 at 2:52 PM Laura Nao <laura.nao@collabora.com> wrote: > > On 8/15/25 05:37, Chen-Yu Tsai wrote: > > On Tue, Aug 5, 2025 at 10:55 PM Laura Nao <laura.nao@collabora.com> wrote: > >> > >> MT8196 use a HW voter for gate enable/disable control. Voting is > >> performed using set/clr regs, with a status bit used to verify the vote > >> state. Add new set of gate clock operations with support for voting via > >> set/clr regs. > >> > >> Reviewed-by: Nícolas F. R. A. Prado <nfraprado@collabora.com> > >> Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> > >> Signed-off-by: Laura Nao <laura.nao@collabora.com> > >> --- > >> drivers/clk/mediatek/clk-gate.c | 77 +++++++++++++++++++++++++++++++-- > >> drivers/clk/mediatek/clk-gate.h | 3 ++ > >> 2 files changed, 77 insertions(+), 3 deletions(-) > >> > >> diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c > >> index 0375ccad4be3..426f3a25763d 100644 > >> --- a/drivers/clk/mediatek/clk-gate.c > >> +++ b/drivers/clk/mediatek/clk-gate.c > >> @@ -5,6 +5,7 @@ > >> */ > >> > >> #include <linux/clk-provider.h> > >> +#include <linux/dev_printk.h> > >> #include <linux/mfd/syscon.h> > >> #include <linux/module.h> > >> #include <linux/printk.h> > >> @@ -12,14 +13,19 @@ > >> #include <linux/slab.h> > >> #include <linux/types.h> > >> > >> +#include "clk-mtk.h" > >> #include "clk-gate.h" > >> > >> struct mtk_clk_gate { > >> struct clk_hw hw; > >> struct regmap *regmap; > >> + struct regmap *regmap_hwv; > >> int set_ofs; > >> int clr_ofs; > >> int sta_ofs; > >> + unsigned int hwv_set_ofs; > >> + unsigned int hwv_clr_ofs; > >> + unsigned int hwv_sta_ofs; > >> u8 bit; > >> }; > >> > >> @@ -100,6 +106,28 @@ static void mtk_cg_disable_inv(struct clk_hw *hw) > >> mtk_cg_clr_bit(hw); > >> } > >> > >> +static int mtk_cg_hwv_set_en(struct clk_hw *hw, bool enable) > >> +{ > >> + struct mtk_clk_gate *cg = to_mtk_clk_gate(hw); > >> + u32 val; > >> + > >> + regmap_write(cg->regmap_hwv, enable ? cg->hwv_set_ofs : cg->hwv_clr_ofs, BIT(cg->bit)); > >> + > >> + return regmap_read_poll_timeout_atomic(cg->regmap_hwv, cg->hwv_sta_ofs, val, > >> + val & BIT(cg->bit), > >> + 0, MTK_WAIT_HWV_DONE_US); > >> +} > >> + > >> +static int mtk_cg_hwv_enable(struct clk_hw *hw) > >> +{ > >> + return mtk_cg_hwv_set_en(hw, true); > >> +} > >> + > >> +static void mtk_cg_hwv_disable(struct clk_hw *hw) > >> +{ > >> + mtk_cg_hwv_set_en(hw, false); > >> +} > >> + > >> static int mtk_cg_enable_no_setclr(struct clk_hw *hw) > >> { > >> mtk_cg_clr_bit_no_setclr(hw); > >> @@ -124,6 +152,15 @@ static void mtk_cg_disable_inv_no_setclr(struct clk_hw *hw) > >> mtk_cg_clr_bit_no_setclr(hw); > >> } > >> > >> +static bool mtk_cg_uses_hwv(const struct clk_ops *ops) > >> +{ > >> + if (ops == &mtk_clk_gate_hwv_ops_setclr || > >> + ops == &mtk_clk_gate_hwv_ops_setclr_inv) > >> + return true; > >> + > >> + return false; > >> +} > >> + > >> const struct clk_ops mtk_clk_gate_ops_setclr = { > >> .is_enabled = mtk_cg_bit_is_cleared, > >> .enable = mtk_cg_enable, > >> @@ -138,6 +175,20 @@ const struct clk_ops mtk_clk_gate_ops_setclr_inv = { > >> }; > >> EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_setclr_inv); > >> > >> +const struct clk_ops mtk_clk_gate_hwv_ops_setclr = { > >> + .is_enabled = mtk_cg_bit_is_cleared, > >> + .enable = mtk_cg_hwv_enable, > >> + .disable = mtk_cg_hwv_disable, > >> +}; > >> +EXPORT_SYMBOL_GPL(mtk_clk_gate_hwv_ops_setclr); > >> + > >> +const struct clk_ops mtk_clk_gate_hwv_ops_setclr_inv = { > >> + .is_enabled = mtk_cg_bit_is_set, > >> + .enable = mtk_cg_hwv_enable, > >> + .disable = mtk_cg_hwv_disable, > >> +}; > >> +EXPORT_SYMBOL_GPL(mtk_clk_gate_hwv_ops_setclr_inv); > >> + > >> const struct clk_ops mtk_clk_gate_ops_no_setclr = { > >> .is_enabled = mtk_cg_bit_is_cleared, > >> .enable = mtk_cg_enable_no_setclr, > >> @@ -153,8 +204,9 @@ const struct clk_ops mtk_clk_gate_ops_no_setclr_inv = { > >> EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_no_setclr_inv); > >> > >> static struct clk_hw *mtk_clk_register_gate(struct device *dev, > >> - const struct mtk_gate *gate, > >> - struct regmap *regmap) > >> + const struct mtk_gate *gate, > >> + struct regmap *regmap, > >> + struct regmap *regmap_hwv) > >> { > >> struct mtk_clk_gate *cg; > >> int ret; > >> @@ -169,11 +221,22 @@ static struct clk_hw *mtk_clk_register_gate(struct device *dev, > >> init.parent_names = gate->parent_name ? &gate->parent_name : NULL; > >> init.num_parents = gate->parent_name ? 1 : 0; > >> init.ops = gate->ops; > >> + if (mtk_cg_uses_hwv(init.ops) && !regmap_hwv) { > >> + dev_err(dev, "regmap not found for hardware voter clocks\n"); > >> + return ERR_PTR(-ENXIO); > > > > return dev_err_probe()? > > > > I believe the same applies to the previous patch. > > > > mtk_clk_register_gate and mtk_clk_register_mux actually both return a > struct clk_hw *. Oops, you're right. If that case I believe dev_err_ptr_probe() could be used? ChenYu
On 8/25/25 16:50, Chen-Yu Tsai wrote: > On Mon, Aug 25, 2025 at 2:52 PM Laura Nao <laura.nao@collabora.com> wrote: >> >> On 8/15/25 05:37, Chen-Yu Tsai wrote: >>> On Tue, Aug 5, 2025 at 10:55 PM Laura Nao <laura.nao@collabora.com> wrote: >>>> >>>> MT8196 use a HW voter for gate enable/disable control. Voting is >>>> performed using set/clr regs, with a status bit used to verify the vote >>>> state. Add new set of gate clock operations with support for voting via >>>> set/clr regs. >>>> >>>> Reviewed-by: Nícolas F. R. A. Prado <nfraprado@collabora.com> >>>> Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> >>>> Signed-off-by: Laura Nao <laura.nao@collabora.com> >>>> --- >>>> drivers/clk/mediatek/clk-gate.c | 77 +++++++++++++++++++++++++++++++-- >>>> drivers/clk/mediatek/clk-gate.h | 3 ++ >>>> 2 files changed, 77 insertions(+), 3 deletions(-) >>>> >>>> diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c >>>> index 0375ccad4be3..426f3a25763d 100644 >>>> --- a/drivers/clk/mediatek/clk-gate.c >>>> +++ b/drivers/clk/mediatek/clk-gate.c >>>> @@ -5,6 +5,7 @@ >>>> */ >>>> >>>> #include <linux/clk-provider.h> >>>> +#include <linux/dev_printk.h> >>>> #include <linux/mfd/syscon.h> >>>> #include <linux/module.h> >>>> #include <linux/printk.h> >>>> @@ -12,14 +13,19 @@ >>>> #include <linux/slab.h> >>>> #include <linux/types.h> >>>> >>>> +#include "clk-mtk.h" >>>> #include "clk-gate.h" >>>> >>>> struct mtk_clk_gate { >>>> struct clk_hw hw; >>>> struct regmap *regmap; >>>> + struct regmap *regmap_hwv; >>>> int set_ofs; >>>> int clr_ofs; >>>> int sta_ofs; >>>> + unsigned int hwv_set_ofs; >>>> + unsigned int hwv_clr_ofs; >>>> + unsigned int hwv_sta_ofs; >>>> u8 bit; >>>> }; >>>> >>>> @@ -100,6 +106,28 @@ static void mtk_cg_disable_inv(struct clk_hw *hw) >>>> mtk_cg_clr_bit(hw); >>>> } >>>> >>>> +static int mtk_cg_hwv_set_en(struct clk_hw *hw, bool enable) >>>> +{ >>>> + struct mtk_clk_gate *cg = to_mtk_clk_gate(hw); >>>> + u32 val; >>>> + >>>> + regmap_write(cg->regmap_hwv, enable ? cg->hwv_set_ofs : cg->hwv_clr_ofs, BIT(cg->bit)); >>>> + >>>> + return regmap_read_poll_timeout_atomic(cg->regmap_hwv, cg->hwv_sta_ofs, val, >>>> + val & BIT(cg->bit), >>>> + 0, MTK_WAIT_HWV_DONE_US); >>>> +} >>>> + >>>> +static int mtk_cg_hwv_enable(struct clk_hw *hw) >>>> +{ >>>> + return mtk_cg_hwv_set_en(hw, true); >>>> +} >>>> + >>>> +static void mtk_cg_hwv_disable(struct clk_hw *hw) >>>> +{ >>>> + mtk_cg_hwv_set_en(hw, false); >>>> +} >>>> + >>>> static int mtk_cg_enable_no_setclr(struct clk_hw *hw) >>>> { >>>> mtk_cg_clr_bit_no_setclr(hw); >>>> @@ -124,6 +152,15 @@ static void mtk_cg_disable_inv_no_setclr(struct clk_hw *hw) >>>> mtk_cg_clr_bit_no_setclr(hw); >>>> } >>>> >>>> +static bool mtk_cg_uses_hwv(const struct clk_ops *ops) >>>> +{ >>>> + if (ops == &mtk_clk_gate_hwv_ops_setclr || >>>> + ops == &mtk_clk_gate_hwv_ops_setclr_inv) >>>> + return true; >>>> + >>>> + return false; >>>> +} >>>> + >>>> const struct clk_ops mtk_clk_gate_ops_setclr = { >>>> .is_enabled = mtk_cg_bit_is_cleared, >>>> .enable = mtk_cg_enable, >>>> @@ -138,6 +175,20 @@ const struct clk_ops mtk_clk_gate_ops_setclr_inv = { >>>> }; >>>> EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_setclr_inv); >>>> >>>> +const struct clk_ops mtk_clk_gate_hwv_ops_setclr = { >>>> + .is_enabled = mtk_cg_bit_is_cleared, >>>> + .enable = mtk_cg_hwv_enable, >>>> + .disable = mtk_cg_hwv_disable, >>>> +}; >>>> +EXPORT_SYMBOL_GPL(mtk_clk_gate_hwv_ops_setclr); >>>> + >>>> +const struct clk_ops mtk_clk_gate_hwv_ops_setclr_inv = { >>>> + .is_enabled = mtk_cg_bit_is_set, >>>> + .enable = mtk_cg_hwv_enable, >>>> + .disable = mtk_cg_hwv_disable, >>>> +}; >>>> +EXPORT_SYMBOL_GPL(mtk_clk_gate_hwv_ops_setclr_inv); >>>> + >>>> const struct clk_ops mtk_clk_gate_ops_no_setclr = { >>>> .is_enabled = mtk_cg_bit_is_cleared, >>>> .enable = mtk_cg_enable_no_setclr, >>>> @@ -153,8 +204,9 @@ const struct clk_ops mtk_clk_gate_ops_no_setclr_inv = { >>>> EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_no_setclr_inv); >>>> >>>> static struct clk_hw *mtk_clk_register_gate(struct device *dev, >>>> - const struct mtk_gate *gate, >>>> - struct regmap *regmap) >>>> + const struct mtk_gate *gate, >>>> + struct regmap *regmap, >>>> + struct regmap *regmap_hwv) >>>> { >>>> struct mtk_clk_gate *cg; >>>> int ret; >>>> @@ -169,11 +221,22 @@ static struct clk_hw *mtk_clk_register_gate(struct device *dev, >>>> init.parent_names = gate->parent_name ? &gate->parent_name : NULL; >>>> init.num_parents = gate->parent_name ? 1 : 0; >>>> init.ops = gate->ops; >>>> + if (mtk_cg_uses_hwv(init.ops) && !regmap_hwv) { >>>> + dev_err(dev, "regmap not found for hardware voter clocks\n"); >>>> + return ERR_PTR(-ENXIO); >>> >>> return dev_err_probe()? >>> >>> I believe the same applies to the previous patch. >>> >> >> mtk_clk_register_gate and mtk_clk_register_mux actually both return a >> struct clk_hw *. > > Oops, you're right. If that case I believe dev_err_ptr_probe() could be > used? Indeed, thanks for the pointer. I'll fix it in the next revision. Laura
© 2016 - 2025 Red Hat, Inc.