clk: qcom: Add gfx3d ping-pong PLL frequency switching
authorStephen Boyd <sboyd@codeaurora.org>
Tue, 1 Dec 2015 01:31:41 +0000 (17:31 -0800)
committerStephen Boyd <sboyd@codeaurora.org>
Tue, 1 Dec 2015 02:24:29 +0000 (18:24 -0800)
The GPU clocks on msm8996 have three dedicated PLLs, MMPLL2,
MMPLL8, and MMPLL9. We leave MMPLL9 at the maximum speed (624
MHz), and we use MMPLL2 and MMPLL8 for the other frequencies. To
make switching frequencies faster, we ping-pong between MMPLL2
and MMPLL8 when we're switching between frequencies that aren't
the maximum. Implement custom rcg clk ops for this type of
frequency switching.

Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
drivers/clk/qcom/clk-rcg.h
drivers/clk/qcom/clk-rcg2.c

index 4b1e94bdf29e2b3a585b4c1d64680ff51f71d467..b904c335cda4275945351e3c772aa171a3c11d32 100644 (file)
@@ -178,5 +178,6 @@ extern const struct clk_ops clk_edp_pixel_ops;
 extern const struct clk_ops clk_byte_ops;
 extern const struct clk_ops clk_byte2_ops;
 extern const struct clk_ops clk_pixel_ops;
+extern const struct clk_ops clk_gfx3d_ops;
 
 #endif
index b544bb302f79894c0d3c6599858c7f0c8b113433..a071bba8018c40424d4d7f02ea80c9622c4d1b26 100644 (file)
@@ -723,3 +723,90 @@ const struct clk_ops clk_pixel_ops = {
        .determine_rate = clk_pixel_determine_rate,
 };
 EXPORT_SYMBOL_GPL(clk_pixel_ops);
+
+static int clk_gfx3d_determine_rate(struct clk_hw *hw,
+                                   struct clk_rate_request *req)
+{
+       struct clk_rate_request parent_req = { };
+       struct clk_hw *p2, *p8, *p9, *xo;
+       unsigned long p9_rate;
+       int ret;
+
+       xo = clk_hw_get_parent_by_index(hw, 0);
+       if (req->rate == clk_hw_get_rate(xo)) {
+               req->best_parent_hw = xo;
+               return 0;
+       }
+
+       p9 = clk_hw_get_parent_by_index(hw, 2);
+       p2 = clk_hw_get_parent_by_index(hw, 3);
+       p8 = clk_hw_get_parent_by_index(hw, 4);
+
+       /* PLL9 is a fixed rate PLL */
+       p9_rate = clk_hw_get_rate(p9);
+
+       parent_req.rate = req->rate = min(req->rate, p9_rate);
+       if (req->rate == p9_rate) {
+               req->rate = req->best_parent_rate = p9_rate;
+               req->best_parent_hw = p9;
+               return 0;
+       }
+
+       if (req->best_parent_hw == p9) {
+               /* Are we going back to a previously used rate? */
+               if (clk_hw_get_rate(p8) == req->rate)
+                       req->best_parent_hw = p8;
+               else
+                       req->best_parent_hw = p2;
+       } else if (req->best_parent_hw == p8) {
+               req->best_parent_hw = p2;
+       } else {
+               req->best_parent_hw = p8;
+       }
+
+       ret = __clk_determine_rate(req->best_parent_hw, &parent_req);
+       if (ret)
+               return ret;
+
+       req->rate = req->best_parent_rate = parent_req.rate;
+
+       return 0;
+}
+
+static int clk_gfx3d_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
+               unsigned long parent_rate, u8 index)
+{
+       struct clk_rcg2 *rcg = to_clk_rcg2(hw);
+       u32 cfg;
+       int ret;
+
+       /* Just mux it, we don't use the division or m/n hardware */
+       cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT;
+       ret = regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, cfg);
+       if (ret)
+               return ret;
+
+       return update_config(rcg);
+}
+
+static int clk_gfx3d_set_rate(struct clk_hw *hw, unsigned long rate,
+                             unsigned long parent_rate)
+{
+       /*
+        * We should never get here; clk_gfx3d_determine_rate() should always
+        * make us use a different parent than what we're currently using, so
+        * clk_gfx3d_set_rate_and_parent() should always be called.
+        */
+       return 0;
+}
+
+const struct clk_ops clk_gfx3d_ops = {
+       .is_enabled = clk_rcg2_is_enabled,
+       .get_parent = clk_rcg2_get_parent,
+       .set_parent = clk_rcg2_set_parent,
+       .recalc_rate = clk_rcg2_recalc_rate,
+       .set_rate = clk_gfx3d_set_rate,
+       .set_rate_and_parent = clk_gfx3d_set_rate_and_parent,
+       .determine_rate = clk_gfx3d_determine_rate,
+};
+EXPORT_SYMBOL_GPL(clk_gfx3d_ops);