mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-17 01:34:00 +08:00
clk: qcom: Add gfx3d ping-pong PLL frequency switching
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>
This commit is contained in:
parent
b1e010c073
commit
55213e1ace
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user