Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

clk: mediatek: clk-gate: Add ops for gates with HW voter

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>
Reviewed-by: Chen-Yu Tsai <wenst@chromium.org>
Signed-off-by: Laura Nao <laura.nao@collabora.com>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>

authored by

Laura Nao and committed by
Stephen Boyd
e504d3bd 8ceff24a

+71 -3
+68 -3
drivers/clk/mediatek/clk-gate.c
··· 5 5 */ 6 6 7 7 #include <linux/clk-provider.h> 8 + #include <linux/dev_printk.h> 8 9 #include <linux/mfd/syscon.h> 9 10 #include <linux/module.h> 10 11 #include <linux/printk.h> ··· 13 12 #include <linux/slab.h> 14 13 #include <linux/types.h> 15 14 15 + #include "clk-mtk.h" 16 16 #include "clk-gate.h" 17 17 18 18 struct mtk_clk_gate { 19 19 struct clk_hw hw; 20 20 struct regmap *regmap; 21 + struct regmap *regmap_hwv; 21 22 const struct mtk_gate *gate; 22 23 }; 23 24 ··· 102 99 mtk_cg_clr_bit(hw); 103 100 } 104 101 102 + static int mtk_cg_hwv_set_en(struct clk_hw *hw, bool enable) 103 + { 104 + struct mtk_clk_gate *cg = to_mtk_clk_gate(hw); 105 + u32 val; 106 + 107 + regmap_write(cg->regmap_hwv, 108 + enable ? cg->gate->hwv_regs->set_ofs : 109 + cg->gate->hwv_regs->clr_ofs, 110 + BIT(cg->gate->shift)); 111 + 112 + return regmap_read_poll_timeout_atomic(cg->regmap_hwv, 113 + cg->gate->hwv_regs->sta_ofs, val, 114 + val & BIT(cg->gate->shift), 0, 115 + MTK_WAIT_HWV_DONE_US); 116 + } 117 + 118 + static int mtk_cg_hwv_enable(struct clk_hw *hw) 119 + { 120 + return mtk_cg_hwv_set_en(hw, true); 121 + } 122 + 123 + static void mtk_cg_hwv_disable(struct clk_hw *hw) 124 + { 125 + mtk_cg_hwv_set_en(hw, false); 126 + } 127 + 105 128 static int mtk_cg_enable_no_setclr(struct clk_hw *hw) 106 129 { 107 130 mtk_cg_clr_bit_no_setclr(hw); ··· 152 123 mtk_cg_clr_bit_no_setclr(hw); 153 124 } 154 125 126 + static bool mtk_cg_uses_hwv(const struct clk_ops *ops) 127 + { 128 + if (ops == &mtk_clk_gate_hwv_ops_setclr || 129 + ops == &mtk_clk_gate_hwv_ops_setclr_inv) 130 + return true; 131 + 132 + return false; 133 + } 134 + 155 135 const struct clk_ops mtk_clk_gate_ops_setclr = { 156 136 .is_enabled = mtk_cg_bit_is_cleared, 157 137 .enable = mtk_cg_enable, ··· 174 136 .disable = mtk_cg_disable_inv, 175 137 }; 176 138 EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_setclr_inv); 139 + 140 + const struct clk_ops mtk_clk_gate_hwv_ops_setclr = { 141 + .is_enabled = mtk_cg_bit_is_cleared, 142 + .enable = mtk_cg_hwv_enable, 143 + .disable = mtk_cg_hwv_disable, 144 + }; 145 + EXPORT_SYMBOL_GPL(mtk_clk_gate_hwv_ops_setclr); 146 + 147 + const struct clk_ops mtk_clk_gate_hwv_ops_setclr_inv = { 148 + .is_enabled = mtk_cg_bit_is_set, 149 + .enable = mtk_cg_hwv_enable, 150 + .disable = mtk_cg_hwv_disable, 151 + }; 152 + EXPORT_SYMBOL_GPL(mtk_clk_gate_hwv_ops_setclr_inv); 177 153 178 154 const struct clk_ops mtk_clk_gate_ops_no_setclr = { 179 155 .is_enabled = mtk_cg_bit_is_cleared, ··· 204 152 EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_no_setclr_inv); 205 153 206 154 static struct clk_hw *mtk_clk_register_gate(struct device *dev, 207 - const struct mtk_gate *gate, 208 - struct regmap *regmap) 155 + const struct mtk_gate *gate, 156 + struct regmap *regmap, 157 + struct regmap *regmap_hwv) 209 158 { 210 159 struct mtk_clk_gate *cg; 211 160 int ret; ··· 221 168 init.parent_names = gate->parent_name ? &gate->parent_name : NULL; 222 169 init.num_parents = gate->parent_name ? 1 : 0; 223 170 init.ops = gate->ops; 171 + if (mtk_cg_uses_hwv(init.ops) && !regmap_hwv) 172 + return dev_err_ptr_probe( 173 + dev, -ENXIO, 174 + "regmap not found for hardware voter clocks\n"); 224 175 225 176 cg->regmap = regmap; 177 + cg->regmap_hwv = regmap_hwv; 226 178 cg->gate = gate; 227 179 cg->hw.init = &init; 228 180 ··· 259 201 int i; 260 202 struct clk_hw *hw; 261 203 struct regmap *regmap; 204 + struct regmap *regmap_hwv; 262 205 263 206 if (!clk_data) 264 207 return -ENOMEM; ··· 270 211 return PTR_ERR(regmap); 271 212 } 272 213 214 + regmap_hwv = mtk_clk_get_hwv_regmap(node); 215 + if (IS_ERR(regmap_hwv)) 216 + return dev_err_probe( 217 + dev, PTR_ERR(regmap_hwv), 218 + "Cannot find hardware voter regmap for %pOF\n", node); 219 + 273 220 for (i = 0; i < num; i++) { 274 221 const struct mtk_gate *gate = &clks[i]; 275 222 ··· 285 220 continue; 286 221 } 287 222 288 - hw = mtk_clk_register_gate(dev, gate, regmap); 223 + hw = mtk_clk_register_gate(dev, gate, regmap, regmap_hwv); 289 224 290 225 if (IS_ERR(hw)) { 291 226 pr_err("Failed to register clk %s: %pe\n", gate->name,
+3
drivers/clk/mediatek/clk-gate.h
··· 19 19 extern const struct clk_ops mtk_clk_gate_ops_setclr_inv; 20 20 extern const struct clk_ops mtk_clk_gate_ops_no_setclr; 21 21 extern const struct clk_ops mtk_clk_gate_ops_no_setclr_inv; 22 + extern const struct clk_ops mtk_clk_gate_hwv_ops_setclr; 23 + extern const struct clk_ops mtk_clk_gate_hwv_ops_setclr_inv; 22 24 23 25 struct mtk_gate_regs { 24 26 u32 sta_ofs; ··· 33 31 const char *name; 34 32 const char *parent_name; 35 33 const struct mtk_gate_regs *regs; 34 + const struct mtk_gate_regs *hwv_regs; 36 35 int shift; 37 36 const struct clk_ops *ops; 38 37 unsigned long flags;