diff options
Diffstat (limited to 'drivers/clk/mediatek/clk-pll.c')
-rw-r--r-- | drivers/clk/mediatek/clk-pll.c | 58 |
1 files changed, 52 insertions, 6 deletions
diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c index ce453e1718e5..cd2b6ce551c6 100644 --- a/drivers/clk/mediatek/clk-pll.c +++ b/drivers/clk/mediatek/clk-pll.c @@ -37,6 +37,13 @@ int mtk_pll_is_prepared(struct clk_hw *hw) return (readl(pll->en_addr) & BIT(pll->data->pll_en_bit)) != 0; } +static int mtk_pll_fenc_is_prepared(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + + return !!(readl(pll->fenc_addr) & BIT(pll->data->fenc_sta_bit)); +} + static unsigned long __mtk_pll_recalc_rate(struct mtk_clk_pll *pll, u32 fin, u32 pcw, int postdiv) { @@ -200,16 +207,19 @@ unsigned long mtk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) return __mtk_pll_recalc_rate(pll, parent_rate, pcw, postdiv); } -long mtk_pll_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *prate) +int mtk_pll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); u32 pcw = 0; int postdiv; - mtk_pll_calc_values(pll, &pcw, &postdiv, rate, *prate); + mtk_pll_calc_values(pll, &pcw, &postdiv, req->rate, + req->best_parent_rate); + + req->rate = __mtk_pll_recalc_rate(pll, req->best_parent_rate, pcw, + postdiv); - return __mtk_pll_recalc_rate(pll, *prate, pcw, postdiv); + return 0; } int mtk_pll_prepare(struct clk_hw *hw) @@ -274,14 +284,43 @@ void mtk_pll_unprepare(struct clk_hw *hw) writel(r, pll->pwr_addr); } +static int mtk_pll_prepare_setclr(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + + writel(BIT(pll->data->pll_en_bit), pll->en_set_addr); + + /* Wait 20us after enable for the PLL to stabilize */ + udelay(20); + + return 0; +} + +static void mtk_pll_unprepare_setclr(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + + writel(BIT(pll->data->pll_en_bit), pll->en_clr_addr); +} + const struct clk_ops mtk_pll_ops = { .is_prepared = mtk_pll_is_prepared, .prepare = mtk_pll_prepare, .unprepare = mtk_pll_unprepare, .recalc_rate = mtk_pll_recalc_rate, - .round_rate = mtk_pll_round_rate, + .determine_rate = mtk_pll_determine_rate, + .set_rate = mtk_pll_set_rate, +}; + +const struct clk_ops mtk_pll_fenc_clr_set_ops = { + .is_prepared = mtk_pll_fenc_is_prepared, + .prepare = mtk_pll_prepare_setclr, + .unprepare = mtk_pll_unprepare_setclr, + .recalc_rate = mtk_pll_recalc_rate, + .determine_rate = mtk_pll_determine_rate, .set_rate = mtk_pll_set_rate, }; +EXPORT_SYMBOL_GPL(mtk_pll_fenc_clr_set_ops); struct clk_hw *mtk_clk_register_pll_ops(struct mtk_clk_pll *pll, const struct mtk_pll_data *data, @@ -308,9 +347,15 @@ struct clk_hw *mtk_clk_register_pll_ops(struct mtk_clk_pll *pll, pll->en_addr = base + data->en_reg; else pll->en_addr = pll->base_addr + REG_CON0; + if (data->en_set_reg) + pll->en_set_addr = base + data->en_set_reg; + if (data->en_clr_reg) + pll->en_clr_addr = base + data->en_clr_reg; pll->hw.init = &init; pll->data = data; + pll->fenc_addr = base + data->fenc_sta_ofs; + init.name = data->name; init.flags = (data->flags & PLL_AO) ? CLK_IS_CRITICAL : 0; init.ops = pll_ops; @@ -333,12 +378,13 @@ struct clk_hw *mtk_clk_register_pll(const struct mtk_pll_data *data, { struct mtk_clk_pll *pll; struct clk_hw *hw; + const struct clk_ops *pll_ops = data->ops ? data->ops : &mtk_pll_ops; pll = kzalloc(sizeof(*pll), GFP_KERNEL); if (!pll) return ERR_PTR(-ENOMEM); - hw = mtk_clk_register_pll_ops(pll, data, base, &mtk_pll_ops); + hw = mtk_clk_register_pll_ops(pll, data, base, pll_ops); if (IS_ERR(hw)) kfree(pll); |