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

clk: qcom: rcg2: Stop hardcoding gfx3d pingpong parent numbers

The function clk_gfx3d_determine_rate is selecting different PLLs
to manage the GFX3D clock source in a special way: this one needs
to be ping-pong'ed on different PLLs to ensure stability during
frequency switching (set a PLL rate, let it stabilize, switch the
RCG to the new PLL) and fast frequency transitions.

This technique is currently being used in the MSM8996 SoC and the
function was assuming that the parents were always at a specific
index in the parents list, which is TRUE, if we use this only on
the MSM8996 MMCC.
Unfortunately, MSM8996 is not the only SoC that needs to ping-pong
the graphics RCG, so choices are:
1. Make new special ops just to hardcode *again* other indexes,
creating code duplication for (imo) no reason; or
2. Generalize this function, so that it becomes usable for a range
of SoCs with slightly different ping-pong configuration.

In this commit, the second road was taken: define a new "special"
struct clk_rcg2_gfx3d, containing the ordered list of parents to
ping-pong the graphics clock on, and the "regular" rcg2 clock
structure in order to generalize the clk_gfx3d_determine_rate
function and make it working for other SoCs.

As for the function itself it is left with the assumption that we
need to ping-pong over three parents. The reasons for this are:
1. The initial model was MSM8996, which has 3 parents for the
graphics clock pingpong;
2. The other example that was taken into consideration is the
SDM630/636/660 SoC gpu clock controller, which is ping-ponging
over two dynamic clocked and one fixed clock PLL.

Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>
Link: https://lore.kernel.org/r/20210113183817.447866-6-angelogioacchino.delregno@somainline.org
[sboyd@kernel.org: Grow some local variables, drop do_div() usage in
favor of plain division, we're not dealing with a u64 here]
Signed-off-by: Stephen Boyd <sboyd@kernel.org>

authored by

AngeloGioacchino Del Regno and committed by
Stephen Boyd
7cbb78a9 9502d488

+46 -20
+9
drivers/clk/qcom/clk-rcg.h
··· 153 153 154 154 #define to_clk_rcg2(_hw) container_of(to_clk_regmap(_hw), struct clk_rcg2, clkr) 155 155 156 + struct clk_rcg2_gfx3d { 157 + u8 div; 158 + struct clk_rcg2 rcg; 159 + struct clk_hw **hws; 160 + }; 161 + 162 + #define to_clk_rcg2_gfx3d(_hw) \ 163 + container_of(to_clk_rcg2(_hw), struct clk_rcg2_gfx3d, rcg) 164 + 156 165 extern const struct clk_ops clk_rcg2_ops; 157 166 extern const struct clk_ops clk_rcg2_floor_ops; 158 167 extern const struct clk_ops clk_edp_pixel_ops;
+37 -20
drivers/clk/qcom/clk-rcg2.c
··· 728 728 struct clk_rate_request *req) 729 729 { 730 730 struct clk_rate_request parent_req = { }; 731 - struct clk_hw *p2, *p8, *p9, *xo; 732 - unsigned long p9_rate; 731 + struct clk_rcg2_gfx3d *cgfx = to_clk_rcg2_gfx3d(hw); 732 + struct clk_hw *xo, *p0, *p1, *p2; 733 + unsigned long request, p0_rate; 733 734 int ret; 735 + 736 + p0 = cgfx->hws[0]; 737 + p1 = cgfx->hws[1]; 738 + p2 = cgfx->hws[2]; 739 + /* 740 + * This function does ping-pong the RCG between PLLs: if we don't 741 + * have at least one fixed PLL and two variable ones, 742 + * then it's not going to work correctly. 743 + */ 744 + if (WARN_ON(!p0 || !p1 || !p2)) 745 + return -EINVAL; 734 746 735 747 xo = clk_hw_get_parent_by_index(hw, 0); 736 748 if (req->rate == clk_hw_get_rate(xo)) { ··· 750 738 return 0; 751 739 } 752 740 753 - p9 = clk_hw_get_parent_by_index(hw, 2); 754 - p2 = clk_hw_get_parent_by_index(hw, 3); 755 - p8 = clk_hw_get_parent_by_index(hw, 4); 741 + request = req->rate; 742 + if (cgfx->div > 1) 743 + parent_req.rate = request = request * cgfx->div; 756 744 757 - /* PLL9 is a fixed rate PLL */ 758 - p9_rate = clk_hw_get_rate(p9); 745 + /* This has to be a fixed rate PLL */ 746 + p0_rate = clk_hw_get_rate(p0); 759 747 760 - parent_req.rate = req->rate = min(req->rate, p9_rate); 761 - if (req->rate == p9_rate) { 762 - req->rate = req->best_parent_rate = p9_rate; 763 - req->best_parent_hw = p9; 748 + if (request == p0_rate) { 749 + req->rate = req->best_parent_rate = p0_rate; 750 + req->best_parent_hw = p0; 764 751 return 0; 765 752 } 766 753 767 - if (req->best_parent_hw == p9) { 754 + if (req->best_parent_hw == p0) { 768 755 /* Are we going back to a previously used rate? */ 769 - if (clk_hw_get_rate(p8) == req->rate) 770 - req->best_parent_hw = p8; 771 - else 756 + if (clk_hw_get_rate(p2) == request) 772 757 req->best_parent_hw = p2; 773 - } else if (req->best_parent_hw == p8) { 774 - req->best_parent_hw = p2; 758 + else 759 + req->best_parent_hw = p1; 760 + } else if (req->best_parent_hw == p2) { 761 + req->best_parent_hw = p1; 775 762 } else { 776 - req->best_parent_hw = p8; 763 + req->best_parent_hw = p2; 777 764 } 778 765 779 766 ret = __clk_determine_rate(req->best_parent_hw, &parent_req); ··· 780 769 return ret; 781 770 782 771 req->rate = req->best_parent_rate = parent_req.rate; 772 + if (cgfx->div > 1) 773 + req->rate /= cgfx->div; 783 774 784 775 return 0; 785 776 } ··· 789 776 static int clk_gfx3d_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, 790 777 unsigned long parent_rate, u8 index) 791 778 { 792 - struct clk_rcg2 *rcg = to_clk_rcg2(hw); 779 + struct clk_rcg2_gfx3d *cgfx = to_clk_rcg2_gfx3d(hw); 780 + struct clk_rcg2 *rcg = &cgfx->rcg; 793 781 u32 cfg; 794 782 int ret; 795 783 796 - /* Just mux it, we don't use the division or m/n hardware */ 797 784 cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; 785 + /* On some targets, the GFX3D RCG may need to divide PLL frequency */ 786 + if (cgfx->div > 1) 787 + cfg |= ((2 * cgfx->div) - 1) << CFG_SRC_DIV_SHIFT; 788 + 798 789 ret = regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, cfg); 799 790 if (ret) 800 791 return ret;