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

clk: mediatek: clk-mux: Add ops for mux gates with HW voter and FENC

MT8196 use a HW voter for mux gate enable/disable control, along with a
FENC status bit to check the status. Voting is performed using
set/clr/upd registers, with a status bit used to verify the vote state.
Add new set of mux gate clock operations with support for voting via
set/clr/upd regs and FENC status logic.

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
be899992 516edf79

+114 -1
+2
drivers/clk/mediatek/clk-mtk.h
··· 20 20 21 21 #define MHZ (1000 * 1000) 22 22 23 + #define MTK_WAIT_HWV_DONE_US 30 24 + 23 25 struct platform_device; 24 26 25 27 /*
+70 -1
drivers/clk/mediatek/clk-mux.c
··· 8 8 #include <linux/clk-provider.h> 9 9 #include <linux/compiler_types.h> 10 10 #include <linux/container_of.h> 11 + #include <linux/dev_printk.h> 11 12 #include <linux/err.h> 12 13 #include <linux/mfd/syscon.h> 13 14 #include <linux/module.h> ··· 16 15 #include <linux/spinlock.h> 17 16 #include <linux/slab.h> 18 17 18 + #include "clk-mtk.h" 19 19 #include "clk-mux.h" 20 20 21 21 #define MTK_WAIT_FENC_DONE_US 30 ··· 24 22 struct mtk_clk_mux { 25 23 struct clk_hw hw; 26 24 struct regmap *regmap; 25 + struct regmap *regmap_hwv; 27 26 const struct mtk_mux *data; 28 27 spinlock_t *lock; 29 28 bool reparent; ··· 122 119 return (val & BIT(mux->data->gate_shift)) == 0; 123 120 } 124 121 122 + static int mtk_clk_mux_hwv_fenc_enable(struct clk_hw *hw) 123 + { 124 + struct mtk_clk_mux *mux = to_mtk_clk_mux(hw); 125 + u32 val; 126 + int ret; 127 + 128 + regmap_write(mux->regmap_hwv, mux->data->hwv_set_ofs, 129 + BIT(mux->data->gate_shift)); 130 + 131 + ret = regmap_read_poll_timeout_atomic(mux->regmap_hwv, mux->data->hwv_sta_ofs, 132 + val, val & BIT(mux->data->gate_shift), 0, 133 + MTK_WAIT_HWV_DONE_US); 134 + if (ret) 135 + return ret; 136 + 137 + ret = regmap_read_poll_timeout_atomic(mux->regmap, mux->data->fenc_sta_mon_ofs, 138 + val, val & BIT(mux->data->fenc_shift), 1, 139 + MTK_WAIT_FENC_DONE_US); 140 + 141 + return ret; 142 + } 143 + 144 + static void mtk_clk_mux_hwv_disable(struct clk_hw *hw) 145 + { 146 + struct mtk_clk_mux *mux = to_mtk_clk_mux(hw); 147 + u32 val; 148 + 149 + regmap_write(mux->regmap_hwv, mux->data->hwv_clr_ofs, 150 + BIT(mux->data->gate_shift)); 151 + 152 + regmap_read_poll_timeout_atomic(mux->regmap_hwv, mux->data->hwv_sta_ofs, 153 + val, (val & BIT(mux->data->gate_shift)), 154 + 0, MTK_WAIT_HWV_DONE_US); 155 + } 156 + 125 157 static u8 mtk_clk_mux_get_parent(struct clk_hw *hw) 126 158 { 127 159 struct mtk_clk_mux *mux = to_mtk_clk_mux(hw); ··· 226 188 return clk_mux_determine_rate_flags(hw, req, 0); 227 189 } 228 190 191 + static bool mtk_clk_mux_uses_hwv(const struct clk_ops *ops) 192 + { 193 + if (ops == &mtk_mux_gate_hwv_fenc_clr_set_upd_ops) 194 + return true; 195 + 196 + return false; 197 + } 198 + 229 199 const struct clk_ops mtk_mux_clr_set_upd_ops = { 230 200 .get_parent = mtk_clk_mux_get_parent, 231 201 .set_parent = mtk_clk_mux_set_parent_setclr_lock, ··· 261 215 }; 262 216 EXPORT_SYMBOL_GPL(mtk_mux_gate_fenc_clr_set_upd_ops); 263 217 218 + const struct clk_ops mtk_mux_gate_hwv_fenc_clr_set_upd_ops = { 219 + .enable = mtk_clk_mux_hwv_fenc_enable, 220 + .disable = mtk_clk_mux_hwv_disable, 221 + .is_enabled = mtk_clk_mux_fenc_is_enabled, 222 + .get_parent = mtk_clk_mux_get_parent, 223 + .set_parent = mtk_clk_mux_set_parent_setclr_lock, 224 + .determine_rate = mtk_clk_mux_determine_rate, 225 + }; 226 + EXPORT_SYMBOL_GPL(mtk_mux_gate_hwv_fenc_clr_set_upd_ops); 227 + 264 228 static struct clk_hw *mtk_clk_register_mux(struct device *dev, 265 229 const struct mtk_mux *mux, 266 230 struct regmap *regmap, 231 + struct regmap *regmap_hwv, 267 232 spinlock_t *lock) 268 233 { 269 234 struct mtk_clk_mux *clk_mux; ··· 290 233 init.parent_names = mux->parent_names; 291 234 init.num_parents = mux->num_parents; 292 235 init.ops = mux->ops; 236 + if (mtk_clk_mux_uses_hwv(init.ops) && !regmap_hwv) 237 + return dev_err_ptr_probe( 238 + dev, -ENXIO, 239 + "regmap not found for hardware voter clocks\n"); 293 240 294 241 clk_mux->regmap = regmap; 242 + clk_mux->regmap_hwv = regmap_hwv; 295 243 clk_mux->data = mux; 296 244 clk_mux->lock = lock; 297 245 clk_mux->hw.init = &init; ··· 329 267 struct clk_hw_onecell_data *clk_data) 330 268 { 331 269 struct regmap *regmap; 270 + struct regmap *regmap_hwv; 332 271 struct clk_hw *hw; 333 272 int i; 334 273 ··· 338 275 pr_err("Cannot find regmap for %pOF: %pe\n", node, regmap); 339 276 return PTR_ERR(regmap); 340 277 } 278 + 279 + regmap_hwv = mtk_clk_get_hwv_regmap(node); 280 + if (IS_ERR(regmap_hwv)) 281 + return dev_err_probe( 282 + dev, PTR_ERR(regmap_hwv), 283 + "Cannot find hardware voter regmap for %pOF\n", node); 341 284 342 285 for (i = 0; i < num; i++) { 343 286 const struct mtk_mux *mux = &muxes[i]; ··· 354 285 continue; 355 286 } 356 287 357 - hw = mtk_clk_register_mux(dev, mux, regmap, lock); 288 + hw = mtk_clk_register_mux(dev, mux, regmap, regmap_hwv, lock); 358 289 359 290 if (IS_ERR(hw)) { 360 291 pr_err("Failed to register clk %s: %pe\n", mux->name,
+42
drivers/clk/mediatek/clk-mux.h
··· 28 28 u32 set_ofs; 29 29 u32 clr_ofs; 30 30 u32 upd_ofs; 31 + 32 + u32 hwv_set_ofs; 33 + u32 hwv_clr_ofs; 34 + u32 hwv_sta_ofs; 31 35 u32 fenc_sta_mon_ofs; 32 36 33 37 u8 mux_shift; ··· 84 80 extern const struct clk_ops mtk_mux_clr_set_upd_ops; 85 81 extern const struct clk_ops mtk_mux_gate_clr_set_upd_ops; 86 82 extern const struct clk_ops mtk_mux_gate_fenc_clr_set_upd_ops; 83 + extern const struct clk_ops mtk_mux_gate_hwv_fenc_clr_set_upd_ops; 87 84 88 85 #define MUX_GATE_CLR_SET_UPD_FLAGS(_id, _name, _parents, _mux_ofs, \ 89 86 _mux_set_ofs, _mux_clr_ofs, _shift, _width, \ ··· 125 120 _mux_set_ofs, _mux_clr_ofs, _shift, _width, \ 126 121 0, _upd_ofs, _upd, CLK_SET_RATE_PARENT, \ 127 122 mtk_mux_clr_set_upd_ops) 123 + 124 + #define MUX_GATE_HWV_FENC_CLR_SET_UPD_FLAGS(_id, _name, _parents, \ 125 + _mux_ofs, _mux_set_ofs, _mux_clr_ofs, \ 126 + _hwv_sta_ofs, _hwv_set_ofs, _hwv_clr_ofs, \ 127 + _shift, _width, _gate, _upd_ofs, _upd, \ 128 + _fenc_sta_mon_ofs, _fenc, _flags) { \ 129 + .id = _id, \ 130 + .name = _name, \ 131 + .mux_ofs = _mux_ofs, \ 132 + .set_ofs = _mux_set_ofs, \ 133 + .clr_ofs = _mux_clr_ofs, \ 134 + .hwv_sta_ofs = _hwv_sta_ofs, \ 135 + .hwv_set_ofs = _hwv_set_ofs, \ 136 + .hwv_clr_ofs = _hwv_clr_ofs, \ 137 + .upd_ofs = _upd_ofs, \ 138 + .fenc_sta_mon_ofs = _fenc_sta_mon_ofs, \ 139 + .mux_shift = _shift, \ 140 + .mux_width = _width, \ 141 + .gate_shift = _gate, \ 142 + .upd_shift = _upd, \ 143 + .fenc_shift = _fenc, \ 144 + .parent_names = _parents, \ 145 + .num_parents = ARRAY_SIZE(_parents), \ 146 + .flags = _flags, \ 147 + .ops = &mtk_mux_gate_hwv_fenc_clr_set_upd_ops, \ 148 + } 149 + 150 + #define MUX_GATE_HWV_FENC_CLR_SET_UPD(_id, _name, _parents, \ 151 + _mux_ofs, _mux_set_ofs, _mux_clr_ofs, \ 152 + _hwv_sta_ofs, _hwv_set_ofs, _hwv_clr_ofs, \ 153 + _shift, _width, _gate, _upd_ofs, _upd, \ 154 + _fenc_sta_mon_ofs, _fenc) \ 155 + MUX_GATE_HWV_FENC_CLR_SET_UPD_FLAGS(_id, _name, _parents, \ 156 + _mux_ofs, _mux_set_ofs, _mux_clr_ofs, \ 157 + _hwv_sta_ofs, _hwv_set_ofs, _hwv_clr_ofs, \ 158 + _shift, _width, _gate, _upd_ofs, _upd, \ 159 + _fenc_sta_mon_ofs, _fenc, 0) 128 160 129 161 #define MUX_GATE_FENC_CLR_SET_UPD_FLAGS(_id, _name, _parents, _paridx, \ 130 162 _num_parents, _mux_ofs, _mux_set_ofs, _mux_clr_ofs, \