162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2022 MediaTek Inc. 462306a36Sopenharmony_ci * Author: Edward-JW Yang <edward-jw.yang@mediatek.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/of.h> 862306a36Sopenharmony_ci#include <linux/of_address.h> 962306a36Sopenharmony_ci#include <linux/io.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/clkdev.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "clk-mtk.h" 1562306a36Sopenharmony_ci#include "clk-pllfh.h" 1662306a36Sopenharmony_ci#include "clk-fhctl.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(pllfh_lock); 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ciinline struct mtk_fh *to_mtk_fh(struct clk_hw *hw) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci return container_of(pll, struct mtk_fh, clk_pll); 2562306a36Sopenharmony_ci} 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic int mtk_fhctl_set_rate(struct clk_hw *hw, unsigned long rate, 2862306a36Sopenharmony_ci unsigned long parent_rate) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); 3162306a36Sopenharmony_ci struct mtk_fh *fh = to_mtk_fh(hw); 3262306a36Sopenharmony_ci u32 pcw = 0; 3362306a36Sopenharmony_ci u32 postdiv; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci mtk_pll_calc_values(pll, &pcw, &postdiv, rate, parent_rate); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci return fh->ops->hopping(fh, pcw, postdiv); 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic const struct clk_ops mtk_pllfh_ops = { 4162306a36Sopenharmony_ci .is_prepared = mtk_pll_is_prepared, 4262306a36Sopenharmony_ci .prepare = mtk_pll_prepare, 4362306a36Sopenharmony_ci .unprepare = mtk_pll_unprepare, 4462306a36Sopenharmony_ci .recalc_rate = mtk_pll_recalc_rate, 4562306a36Sopenharmony_ci .round_rate = mtk_pll_round_rate, 4662306a36Sopenharmony_ci .set_rate = mtk_fhctl_set_rate, 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic struct mtk_pllfh_data *get_pllfh_by_id(struct mtk_pllfh_data *pllfhs, 5062306a36Sopenharmony_ci int num_fhs, int pll_id) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci int i; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci for (i = 0; i < num_fhs; i++) 5562306a36Sopenharmony_ci if (pllfhs[i].data.pll_id == pll_id) 5662306a36Sopenharmony_ci return &pllfhs[i]; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci return NULL; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_civoid fhctl_parse_dt(const u8 *compatible_node, struct mtk_pllfh_data *pllfhs, 6262306a36Sopenharmony_ci int num_fhs) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci void __iomem *base; 6562306a36Sopenharmony_ci struct device_node *node; 6662306a36Sopenharmony_ci u32 num_clocks, pll_id, ssc_rate; 6762306a36Sopenharmony_ci int offset, i; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci node = of_find_compatible_node(NULL, NULL, compatible_node); 7062306a36Sopenharmony_ci if (!node) { 7162306a36Sopenharmony_ci pr_err("cannot find \"%s\"\n", compatible_node); 7262306a36Sopenharmony_ci return; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci base = of_iomap(node, 0); 7662306a36Sopenharmony_ci if (!base) { 7762306a36Sopenharmony_ci pr_err("%s(): ioremap failed\n", __func__); 7862306a36Sopenharmony_ci goto out_node_put; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci num_clocks = of_clk_get_parent_count(node); 8262306a36Sopenharmony_ci if (!num_clocks) { 8362306a36Sopenharmony_ci pr_err("%s(): failed to get clocks property\n", __func__); 8462306a36Sopenharmony_ci goto err; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci for (i = 0; i < num_clocks; i++) { 8862306a36Sopenharmony_ci struct mtk_pllfh_data *pllfh; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci offset = i * 2; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci of_property_read_u32_index(node, "clocks", offset + 1, &pll_id); 9362306a36Sopenharmony_ci of_property_read_u32_index(node, 9462306a36Sopenharmony_ci "mediatek,hopping-ssc-percent", 9562306a36Sopenharmony_ci i, &ssc_rate); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci pllfh = get_pllfh_by_id(pllfhs, num_fhs, pll_id); 9862306a36Sopenharmony_ci if (!pllfh) 9962306a36Sopenharmony_ci continue; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci pllfh->state.fh_enable = 1; 10262306a36Sopenharmony_ci pllfh->state.ssc_rate = ssc_rate; 10362306a36Sopenharmony_ci pllfh->state.base = base; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ciout_node_put: 10762306a36Sopenharmony_ci of_node_put(node); 10862306a36Sopenharmony_ci return; 10962306a36Sopenharmony_cierr: 11062306a36Sopenharmony_ci iounmap(base); 11162306a36Sopenharmony_ci goto out_node_put; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fhctl_parse_dt); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int pllfh_init(struct mtk_fh *fh, struct mtk_pllfh_data *pllfh_data) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct fh_pll_regs *regs = &fh->regs; 11862306a36Sopenharmony_ci const struct fhctl_offset *offset; 11962306a36Sopenharmony_ci void __iomem *base = pllfh_data->state.base; 12062306a36Sopenharmony_ci void __iomem *fhx_base = base + pllfh_data->data.fhx_offset; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci offset = fhctl_get_offset_table(pllfh_data->data.fh_ver); 12362306a36Sopenharmony_ci if (IS_ERR(offset)) 12462306a36Sopenharmony_ci return PTR_ERR(offset); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci regs->reg_hp_en = base + offset->offset_hp_en; 12762306a36Sopenharmony_ci regs->reg_clk_con = base + offset->offset_clk_con; 12862306a36Sopenharmony_ci regs->reg_rst_con = base + offset->offset_rst_con; 12962306a36Sopenharmony_ci regs->reg_slope0 = base + offset->offset_slope0; 13062306a36Sopenharmony_ci regs->reg_slope1 = base + offset->offset_slope1; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci regs->reg_cfg = fhx_base + offset->offset_cfg; 13362306a36Sopenharmony_ci regs->reg_updnlmt = fhx_base + offset->offset_updnlmt; 13462306a36Sopenharmony_ci regs->reg_dds = fhx_base + offset->offset_dds; 13562306a36Sopenharmony_ci regs->reg_dvfs = fhx_base + offset->offset_dvfs; 13662306a36Sopenharmony_ci regs->reg_mon = fhx_base + offset->offset_mon; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci fh->pllfh_data = pllfh_data; 13962306a36Sopenharmony_ci fh->lock = &pllfh_lock; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci fh->ops = fhctl_get_ops(); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci return 0; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic bool fhctl_is_supported_and_enabled(const struct mtk_pllfh_data *pllfh) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci return pllfh && (pllfh->state.fh_enable == 1); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic struct clk_hw * 15262306a36Sopenharmony_cimtk_clk_register_pllfh(const struct mtk_pll_data *pll_data, 15362306a36Sopenharmony_ci struct mtk_pllfh_data *pllfh_data, void __iomem *base) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci struct clk_hw *hw; 15662306a36Sopenharmony_ci struct mtk_fh *fh; 15762306a36Sopenharmony_ci int ret; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci fh = kzalloc(sizeof(*fh), GFP_KERNEL); 16062306a36Sopenharmony_ci if (!fh) 16162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci ret = pllfh_init(fh, pllfh_data); 16462306a36Sopenharmony_ci if (ret) { 16562306a36Sopenharmony_ci hw = ERR_PTR(ret); 16662306a36Sopenharmony_ci goto out; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci hw = mtk_clk_register_pll_ops(&fh->clk_pll, pll_data, base, 17062306a36Sopenharmony_ci &mtk_pllfh_ops); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (IS_ERR(hw)) 17362306a36Sopenharmony_ci goto out; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci fhctl_hw_init(fh); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ciout: 17862306a36Sopenharmony_ci if (IS_ERR(hw)) 17962306a36Sopenharmony_ci kfree(fh); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci return hw; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic void mtk_clk_unregister_pllfh(struct clk_hw *hw) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct mtk_fh *fh; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (!hw) 18962306a36Sopenharmony_ci return; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci fh = to_mtk_fh(hw); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci clk_hw_unregister(hw); 19462306a36Sopenharmony_ci kfree(fh); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ciint mtk_clk_register_pllfhs(struct device_node *node, 19862306a36Sopenharmony_ci const struct mtk_pll_data *plls, int num_plls, 19962306a36Sopenharmony_ci struct mtk_pllfh_data *pllfhs, int num_fhs, 20062306a36Sopenharmony_ci struct clk_hw_onecell_data *clk_data) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci void __iomem *base; 20362306a36Sopenharmony_ci int i; 20462306a36Sopenharmony_ci struct clk_hw *hw; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci base = of_iomap(node, 0); 20762306a36Sopenharmony_ci if (!base) { 20862306a36Sopenharmony_ci pr_err("%s(): ioremap failed\n", __func__); 20962306a36Sopenharmony_ci return -EINVAL; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci for (i = 0; i < num_plls; i++) { 21362306a36Sopenharmony_ci const struct mtk_pll_data *pll = &plls[i]; 21462306a36Sopenharmony_ci struct mtk_pllfh_data *pllfh; 21562306a36Sopenharmony_ci bool use_fhctl; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci pllfh = get_pllfh_by_id(pllfhs, num_fhs, pll->id); 21862306a36Sopenharmony_ci use_fhctl = fhctl_is_supported_and_enabled(pllfh); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (use_fhctl) 22162306a36Sopenharmony_ci hw = mtk_clk_register_pllfh(pll, pllfh, base); 22262306a36Sopenharmony_ci else 22362306a36Sopenharmony_ci hw = mtk_clk_register_pll(pll, base); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (IS_ERR(hw)) { 22662306a36Sopenharmony_ci pr_err("Failed to register %s clk %s: %ld\n", 22762306a36Sopenharmony_ci use_fhctl ? "fhpll" : "pll", pll->name, 22862306a36Sopenharmony_ci PTR_ERR(hw)); 22962306a36Sopenharmony_ci goto err; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci clk_data->hws[pll->id] = hw; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci return 0; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cierr: 23862306a36Sopenharmony_ci while (--i >= 0) { 23962306a36Sopenharmony_ci const struct mtk_pll_data *pll = &plls[i]; 24062306a36Sopenharmony_ci struct mtk_pllfh_data *pllfh; 24162306a36Sopenharmony_ci bool use_fhctl; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci pllfh = get_pllfh_by_id(pllfhs, num_fhs, pll->id); 24462306a36Sopenharmony_ci use_fhctl = fhctl_is_supported_and_enabled(pllfh); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (use_fhctl) 24762306a36Sopenharmony_ci mtk_clk_unregister_pllfh(clk_data->hws[pll->id]); 24862306a36Sopenharmony_ci else 24962306a36Sopenharmony_ci mtk_clk_unregister_pll(clk_data->hws[pll->id]); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci clk_data->hws[pll->id] = ERR_PTR(-ENOENT); 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci iounmap(base); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return PTR_ERR(hw); 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mtk_clk_register_pllfhs); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_civoid mtk_clk_unregister_pllfhs(const struct mtk_pll_data *plls, int num_plls, 26162306a36Sopenharmony_ci struct mtk_pllfh_data *pllfhs, int num_fhs, 26262306a36Sopenharmony_ci struct clk_hw_onecell_data *clk_data) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci void __iomem *base = NULL, *fhctl_base = NULL; 26562306a36Sopenharmony_ci int i; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (!clk_data) 26862306a36Sopenharmony_ci return; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci for (i = num_plls; i > 0; i--) { 27162306a36Sopenharmony_ci const struct mtk_pll_data *pll = &plls[i - 1]; 27262306a36Sopenharmony_ci struct mtk_pllfh_data *pllfh; 27362306a36Sopenharmony_ci bool use_fhctl; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (IS_ERR_OR_NULL(clk_data->hws[pll->id])) 27662306a36Sopenharmony_ci continue; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci pllfh = get_pllfh_by_id(pllfhs, num_fhs, pll->id); 27962306a36Sopenharmony_ci use_fhctl = fhctl_is_supported_and_enabled(pllfh); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (use_fhctl) { 28262306a36Sopenharmony_ci fhctl_base = pllfh->state.base; 28362306a36Sopenharmony_ci mtk_clk_unregister_pllfh(clk_data->hws[pll->id]); 28462306a36Sopenharmony_ci } else { 28562306a36Sopenharmony_ci base = mtk_clk_pll_get_base(clk_data->hws[pll->id], 28662306a36Sopenharmony_ci pll); 28762306a36Sopenharmony_ci mtk_clk_unregister_pll(clk_data->hws[pll->id]); 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci clk_data->hws[pll->id] = ERR_PTR(-ENOENT); 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (fhctl_base) 29462306a36Sopenharmony_ci iounmap(fhctl_base); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci iounmap(base); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mtk_clk_unregister_pllfhs); 299