11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29741b1a6SJames Liao /*
39741b1a6SJames Liao * Copyright (c) 2014 MediaTek Inc.
49741b1a6SJames Liao * Author: James Liao <jamesjj.liao@mediatek.com>
59741b1a6SJames Liao */
69741b1a6SJames Liao
710174b50SChen-Yu Tsai #include <linux/clk-provider.h>
810174b50SChen-Yu Tsai #include <linux/container_of.h>
910174b50SChen-Yu Tsai #include <linux/delay.h>
1010174b50SChen-Yu Tsai #include <linux/err.h>
119741b1a6SJames Liao #include <linux/io.h>
1232b028fbSMiles Chen #include <linux/module.h>
1310174b50SChen-Yu Tsai #include <linux/of_address.h>
149741b1a6SJames Liao #include <linux/slab.h>
159741b1a6SJames Liao
1639691fb6SChen-Yu Tsai #include "clk-pll.h"
179741b1a6SJames Liao
1810174b50SChen-Yu Tsai #define MHZ (1000 * 1000)
1910174b50SChen-Yu Tsai
209741b1a6SJames Liao #define REG_CON0 0
219741b1a6SJames Liao #define REG_CON1 4
229741b1a6SJames Liao
239741b1a6SJames Liao #define CON0_BASE_EN BIT(0)
249741b1a6SJames Liao #define CON0_PWR_ON BIT(0)
259741b1a6SJames Liao #define CON0_ISO_EN BIT(1)
2623fe31deSWeiyi Lu #define PCW_CHG_MASK BIT(31)
279741b1a6SJames Liao
289741b1a6SJames Liao #define AUDPLL_TUNER_EN BIT(31)
299741b1a6SJames Liao
309d7e1a82SOwen Chen /* default 7 bits integer, can be overridden with pcwibits. */
319741b1a6SJames Liao #define INTEGER_BITS 7
329741b1a6SJames Liao
mtk_pll_is_prepared(struct clk_hw * hw)33029c936aSJohnson Wang int mtk_pll_is_prepared(struct clk_hw *hw)
349741b1a6SJames Liao {
359741b1a6SJames Liao struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
369741b1a6SJames Liao
37f384c447SChun-Jie Chen return (readl(pll->en_addr) & BIT(pll->data->pll_en_bit)) != 0;
389741b1a6SJames Liao }
399741b1a6SJames Liao
__mtk_pll_recalc_rate(struct mtk_clk_pll * pll,u32 fin,u32 pcw,int postdiv)409741b1a6SJames Liao static unsigned long __mtk_pll_recalc_rate(struct mtk_clk_pll *pll, u32 fin,
419741b1a6SJames Liao u32 pcw, int postdiv)
429741b1a6SJames Liao {
439741b1a6SJames Liao int pcwbits = pll->data->pcwbits;
449d7e1a82SOwen Chen int pcwfbits = 0;
459d7e1a82SOwen Chen int ibits;
469741b1a6SJames Liao u64 vco;
479741b1a6SJames Liao u8 c = 0;
489741b1a6SJames Liao
499741b1a6SJames Liao /* The fractional part of the PLL divider. */
509d7e1a82SOwen Chen ibits = pll->data->pcwibits ? pll->data->pcwibits : INTEGER_BITS;
519d7e1a82SOwen Chen if (pcwbits > ibits)
529d7e1a82SOwen Chen pcwfbits = pcwbits - ibits;
539741b1a6SJames Liao
549741b1a6SJames Liao vco = (u64)fin * pcw;
559741b1a6SJames Liao
569741b1a6SJames Liao if (pcwfbits && (vco & GENMASK(pcwfbits - 1, 0)))
579741b1a6SJames Liao c = 1;
589741b1a6SJames Liao
599741b1a6SJames Liao vco >>= pcwfbits;
609741b1a6SJames Liao
619741b1a6SJames Liao if (c)
629741b1a6SJames Liao vco++;
639741b1a6SJames Liao
649741b1a6SJames Liao return ((unsigned long)vco + postdiv - 1) / postdiv;
659741b1a6SJames Liao }
669741b1a6SJames Liao
__mtk_pll_tuner_enable(struct mtk_clk_pll * pll)67be17ca6aSOwen Chen static void __mtk_pll_tuner_enable(struct mtk_clk_pll *pll)
68be17ca6aSOwen Chen {
69be17ca6aSOwen Chen u32 r;
70be17ca6aSOwen Chen
71be17ca6aSOwen Chen if (pll->tuner_en_addr) {
72be17ca6aSOwen Chen r = readl(pll->tuner_en_addr) | BIT(pll->data->tuner_en_bit);
73be17ca6aSOwen Chen writel(r, pll->tuner_en_addr);
74be17ca6aSOwen Chen } else if (pll->tuner_addr) {
75be17ca6aSOwen Chen r = readl(pll->tuner_addr) | AUDPLL_TUNER_EN;
76be17ca6aSOwen Chen writel(r, pll->tuner_addr);
77be17ca6aSOwen Chen }
78be17ca6aSOwen Chen }
79be17ca6aSOwen Chen
__mtk_pll_tuner_disable(struct mtk_clk_pll * pll)80be17ca6aSOwen Chen static void __mtk_pll_tuner_disable(struct mtk_clk_pll *pll)
81be17ca6aSOwen Chen {
82be17ca6aSOwen Chen u32 r;
83be17ca6aSOwen Chen
84be17ca6aSOwen Chen if (pll->tuner_en_addr) {
85be17ca6aSOwen Chen r = readl(pll->tuner_en_addr) & ~BIT(pll->data->tuner_en_bit);
86be17ca6aSOwen Chen writel(r, pll->tuner_en_addr);
87be17ca6aSOwen Chen } else if (pll->tuner_addr) {
88be17ca6aSOwen Chen r = readl(pll->tuner_addr) & ~AUDPLL_TUNER_EN;
89be17ca6aSOwen Chen writel(r, pll->tuner_addr);
90be17ca6aSOwen Chen }
91be17ca6aSOwen Chen }
92be17ca6aSOwen Chen
mtk_pll_set_rate_regs(struct mtk_clk_pll * pll,u32 pcw,int postdiv)939741b1a6SJames Liao static void mtk_pll_set_rate_regs(struct mtk_clk_pll *pll, u32 pcw,
949741b1a6SJames Liao int postdiv)
959741b1a6SJames Liao {
9623fe31deSWeiyi Lu u32 chg, val;
979741b1a6SJames Liao
98be17ca6aSOwen Chen /* disable tuner */
99be17ca6aSOwen Chen __mtk_pll_tuner_disable(pll);
100be17ca6aSOwen Chen
101b3be457eSJames Liao /* set postdiv */
102b3be457eSJames Liao val = readl(pll->pd_addr);
103b3be457eSJames Liao val &= ~(POSTDIV_MASK << pll->data->pd_shift);
104b3be457eSJames Liao val |= (ffs(postdiv) - 1) << pll->data->pd_shift;
1059741b1a6SJames Liao
106b3be457eSJames Liao /* postdiv and pcw need to set at the same time if on same register */
107b3be457eSJames Liao if (pll->pd_addr != pll->pcw_addr) {
108b3be457eSJames Liao writel(val, pll->pd_addr);
109b3be457eSJames Liao val = readl(pll->pcw_addr);
110b3be457eSJames Liao }
111b3be457eSJames Liao
112b3be457eSJames Liao /* set pcw */
1139741b1a6SJames Liao val &= ~GENMASK(pll->data->pcw_shift + pll->data->pcwbits - 1,
1149741b1a6SJames Liao pll->data->pcw_shift);
1159741b1a6SJames Liao val |= pcw << pll->data->pcw_shift;
1169741b1a6SJames Liao writel(val, pll->pcw_addr);
117dac5d672SJames Liao chg = readl(pll->pcw_chg_addr) | PCW_CHG_MASK;
11823fe31deSWeiyi Lu writel(chg, pll->pcw_chg_addr);
1199741b1a6SJames Liao if (pll->tuner_addr)
12023fe31deSWeiyi Lu writel(val + 1, pll->tuner_addr);
1219741b1a6SJames Liao
122be17ca6aSOwen Chen /* restore tuner_en */
123be17ca6aSOwen Chen __mtk_pll_tuner_enable(pll);
124be17ca6aSOwen Chen
1259741b1a6SJames Liao udelay(20);
1269741b1a6SJames Liao }
1279741b1a6SJames Liao
1289741b1a6SJames Liao /*
1299741b1a6SJames Liao * mtk_pll_calc_values - calculate good values for a given input frequency.
1309741b1a6SJames Liao * @pll: The pll
1319741b1a6SJames Liao * @pcw: The pcw value (output)
1329741b1a6SJames Liao * @postdiv: The post divider (output)
1339741b1a6SJames Liao * @freq: The desired target frequency
1349741b1a6SJames Liao * @fin: The input frequency
1359741b1a6SJames Liao *
1369741b1a6SJames Liao */
mtk_pll_calc_values(struct mtk_clk_pll * pll,u32 * pcw,u32 * postdiv,u32 freq,u32 fin)137029c936aSJohnson Wang void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32 *postdiv,
1389741b1a6SJames Liao u32 freq, u32 fin)
1399741b1a6SJames Liao {
1409d7e1a82SOwen Chen unsigned long fmin = pll->data->fmin ? pll->data->fmin : (1000 * MHZ);
14175ce0cdbSJames Liao const struct mtk_pll_div_table *div_table = pll->data->div_table;
1429741b1a6SJames Liao u64 _pcw;
1439d7e1a82SOwen Chen int ibits;
1449741b1a6SJames Liao u32 val;
1459741b1a6SJames Liao
1469741b1a6SJames Liao if (freq > pll->data->fmax)
1479741b1a6SJames Liao freq = pll->data->fmax;
1489741b1a6SJames Liao
14975ce0cdbSJames Liao if (div_table) {
15075ce0cdbSJames Liao if (freq > div_table[0].freq)
15175ce0cdbSJames Liao freq = div_table[0].freq;
15275ce0cdbSJames Liao
15375ce0cdbSJames Liao for (val = 0; div_table[val + 1].freq != 0; val++) {
15475ce0cdbSJames Liao if (freq > div_table[val + 1].freq)
15575ce0cdbSJames Liao break;
15675ce0cdbSJames Liao }
15775ce0cdbSJames Liao *postdiv = 1 << val;
15875ce0cdbSJames Liao } else {
159196de71aSJames Liao for (val = 0; val < 5; val++) {
1609741b1a6SJames Liao *postdiv = 1 << val;
161196de71aSJames Liao if ((u64)freq * *postdiv >= fmin)
1629741b1a6SJames Liao break;
1639741b1a6SJames Liao }
16475ce0cdbSJames Liao }
1659741b1a6SJames Liao
1669741b1a6SJames Liao /* _pcw = freq * postdiv / fin * 2^pcwfbits */
1679d7e1a82SOwen Chen ibits = pll->data->pcwibits ? pll->data->pcwibits : INTEGER_BITS;
1689d7e1a82SOwen Chen _pcw = ((u64)freq << val) << (pll->data->pcwbits - ibits);
1699741b1a6SJames Liao do_div(_pcw, fin);
1709741b1a6SJames Liao
1719741b1a6SJames Liao *pcw = (u32)_pcw;
1729741b1a6SJames Liao }
1739741b1a6SJames Liao
mtk_pll_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)174029c936aSJohnson Wang int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
1759741b1a6SJames Liao unsigned long parent_rate)
1769741b1a6SJames Liao {
1779741b1a6SJames Liao struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
1789741b1a6SJames Liao u32 pcw = 0;
1799741b1a6SJames Liao u32 postdiv;
1809741b1a6SJames Liao
1819741b1a6SJames Liao mtk_pll_calc_values(pll, &pcw, &postdiv, rate, parent_rate);
1829741b1a6SJames Liao mtk_pll_set_rate_regs(pll, pcw, postdiv);
1839741b1a6SJames Liao
1849741b1a6SJames Liao return 0;
1859741b1a6SJames Liao }
1869741b1a6SJames Liao
mtk_pll_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)187029c936aSJohnson Wang unsigned long mtk_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
1889741b1a6SJames Liao {
1899741b1a6SJames Liao struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
1909741b1a6SJames Liao u32 postdiv;
1919741b1a6SJames Liao u32 pcw;
1929741b1a6SJames Liao
1939741b1a6SJames Liao postdiv = (readl(pll->pd_addr) >> pll->data->pd_shift) & POSTDIV_MASK;
1949741b1a6SJames Liao postdiv = 1 << postdiv;
1959741b1a6SJames Liao
1969741b1a6SJames Liao pcw = readl(pll->pcw_addr) >> pll->data->pcw_shift;
1979741b1a6SJames Liao pcw &= GENMASK(pll->data->pcwbits - 1, 0);
1989741b1a6SJames Liao
1999741b1a6SJames Liao return __mtk_pll_recalc_rate(pll, parent_rate, pcw, postdiv);
2009741b1a6SJames Liao }
2019741b1a6SJames Liao
mtk_pll_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * prate)202029c936aSJohnson Wang long mtk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
2039741b1a6SJames Liao unsigned long *prate)
2049741b1a6SJames Liao {
2059741b1a6SJames Liao struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
2069741b1a6SJames Liao u32 pcw = 0;
2079741b1a6SJames Liao int postdiv;
2089741b1a6SJames Liao
2099741b1a6SJames Liao mtk_pll_calc_values(pll, &pcw, &postdiv, rate, *prate);
2109741b1a6SJames Liao
2119741b1a6SJames Liao return __mtk_pll_recalc_rate(pll, *prate, pcw, postdiv);
2129741b1a6SJames Liao }
2139741b1a6SJames Liao
mtk_pll_prepare(struct clk_hw * hw)214029c936aSJohnson Wang int mtk_pll_prepare(struct clk_hw *hw)
2159741b1a6SJames Liao {
2169741b1a6SJames Liao struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
2179741b1a6SJames Liao u32 r;
2189741b1a6SJames Liao
2199741b1a6SJames Liao r = readl(pll->pwr_addr) | CON0_PWR_ON;
2209741b1a6SJames Liao writel(r, pll->pwr_addr);
2219741b1a6SJames Liao udelay(1);
2229741b1a6SJames Liao
2239741b1a6SJames Liao r = readl(pll->pwr_addr) & ~CON0_ISO_EN;
2249741b1a6SJames Liao writel(r, pll->pwr_addr);
2259741b1a6SJames Liao udelay(1);
2269741b1a6SJames Liao
227f384c447SChun-Jie Chen r = readl(pll->en_addr) | BIT(pll->data->pll_en_bit);
228f384c447SChun-Jie Chen writel(r, pll->en_addr);
2299741b1a6SJames Liao
230e1fd35f5SChun-Jie Chen if (pll->data->en_mask) {
231e1fd35f5SChun-Jie Chen r = readl(pll->base_addr + REG_CON0) | pll->data->en_mask;
2327cc4e1bbSChun-Jie Chen writel(r, pll->base_addr + REG_CON0);
2337cc4e1bbSChun-Jie Chen }
2347cc4e1bbSChun-Jie Chen
235be17ca6aSOwen Chen __mtk_pll_tuner_enable(pll);
2369741b1a6SJames Liao
2379741b1a6SJames Liao udelay(20);
2389741b1a6SJames Liao
2399741b1a6SJames Liao if (pll->data->flags & HAVE_RST_BAR) {
2409741b1a6SJames Liao r = readl(pll->base_addr + REG_CON0);
2419741b1a6SJames Liao r |= pll->data->rst_bar_mask;
2429741b1a6SJames Liao writel(r, pll->base_addr + REG_CON0);
2439741b1a6SJames Liao }
2449741b1a6SJames Liao
2459741b1a6SJames Liao return 0;
2469741b1a6SJames Liao }
2479741b1a6SJames Liao
mtk_pll_unprepare(struct clk_hw * hw)248029c936aSJohnson Wang void mtk_pll_unprepare(struct clk_hw *hw)
2499741b1a6SJames Liao {
2509741b1a6SJames Liao struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
2519741b1a6SJames Liao u32 r;
2529741b1a6SJames Liao
2539741b1a6SJames Liao if (pll->data->flags & HAVE_RST_BAR) {
2549741b1a6SJames Liao r = readl(pll->base_addr + REG_CON0);
2559741b1a6SJames Liao r &= ~pll->data->rst_bar_mask;
2569741b1a6SJames Liao writel(r, pll->base_addr + REG_CON0);
2579741b1a6SJames Liao }
2589741b1a6SJames Liao
259be17ca6aSOwen Chen __mtk_pll_tuner_disable(pll);
2609741b1a6SJames Liao
261e1fd35f5SChun-Jie Chen if (pll->data->en_mask) {
262e1fd35f5SChun-Jie Chen r = readl(pll->base_addr + REG_CON0) & ~pll->data->en_mask;
2637cc4e1bbSChun-Jie Chen writel(r, pll->base_addr + REG_CON0);
2647cc4e1bbSChun-Jie Chen }
2657cc4e1bbSChun-Jie Chen
266f384c447SChun-Jie Chen r = readl(pll->en_addr) & ~BIT(pll->data->pll_en_bit);
267f384c447SChun-Jie Chen writel(r, pll->en_addr);
2689741b1a6SJames Liao
2699741b1a6SJames Liao r = readl(pll->pwr_addr) | CON0_ISO_EN;
2709741b1a6SJames Liao writel(r, pll->pwr_addr);
2719741b1a6SJames Liao
2729741b1a6SJames Liao r = readl(pll->pwr_addr) & ~CON0_PWR_ON;
2739741b1a6SJames Liao writel(r, pll->pwr_addr);
2749741b1a6SJames Liao }
2759741b1a6SJames Liao
276029c936aSJohnson Wang const struct clk_ops mtk_pll_ops = {
2779741b1a6SJames Liao .is_prepared = mtk_pll_is_prepared,
2789741b1a6SJames Liao .prepare = mtk_pll_prepare,
2799741b1a6SJames Liao .unprepare = mtk_pll_unprepare,
2809741b1a6SJames Liao .recalc_rate = mtk_pll_recalc_rate,
2819741b1a6SJames Liao .round_rate = mtk_pll_round_rate,
2829741b1a6SJames Liao .set_rate = mtk_pll_set_rate,
2839741b1a6SJames Liao };
2849741b1a6SJames Liao
mtk_clk_register_pll_ops(struct mtk_clk_pll * pll,const struct mtk_pll_data * data,void __iomem * base,const struct clk_ops * pll_ops)285029c936aSJohnson Wang struct clk_hw *mtk_clk_register_pll_ops(struct mtk_clk_pll *pll,
286029c936aSJohnson Wang const struct mtk_pll_data *data,
287029c936aSJohnson Wang void __iomem *base,
288029c936aSJohnson Wang const struct clk_ops *pll_ops)
2899741b1a6SJames Liao {
29095f58981SRicky Liang struct clk_init_data init = {};
2916f691a58SChen-Yu Tsai int ret;
2929741b1a6SJames Liao const char *parent_name = "clk26m";
2939741b1a6SJames Liao
2949741b1a6SJames Liao pll->base_addr = base + data->reg;
2959741b1a6SJames Liao pll->pwr_addr = base + data->pwr_reg;
2969741b1a6SJames Liao pll->pd_addr = base + data->pd_reg;
2979741b1a6SJames Liao pll->pcw_addr = base + data->pcw_reg;
29823fe31deSWeiyi Lu if (data->pcw_chg_reg)
29923fe31deSWeiyi Lu pll->pcw_chg_addr = base + data->pcw_chg_reg;
30023fe31deSWeiyi Lu else
30123fe31deSWeiyi Lu pll->pcw_chg_addr = pll->base_addr + REG_CON1;
3029741b1a6SJames Liao if (data->tuner_reg)
3039741b1a6SJames Liao pll->tuner_addr = base + data->tuner_reg;
304cb95c169SChun-Jie Chen if (data->tuner_en_reg || data->tuner_en_bit)
305e2f744a8Sweiyi.lu@mediatek.com pll->tuner_en_addr = base + data->tuner_en_reg;
306f384c447SChun-Jie Chen if (data->en_reg)
307f384c447SChun-Jie Chen pll->en_addr = base + data->en_reg;
308f384c447SChun-Jie Chen else
309f384c447SChun-Jie Chen pll->en_addr = pll->base_addr + REG_CON0;
3109741b1a6SJames Liao pll->hw.init = &init;
3119741b1a6SJames Liao pll->data = data;
3129741b1a6SJames Liao
3139741b1a6SJames Liao init.name = data->name;
314e9862118SShunli Wang init.flags = (data->flags & PLL_AO) ? CLK_IS_CRITICAL : 0;
315029c936aSJohnson Wang init.ops = pll_ops;
316c955bf39SChen Zhong if (data->parent_name)
317c955bf39SChen Zhong init.parent_names = &data->parent_name;
318c955bf39SChen Zhong else
3199741b1a6SJames Liao init.parent_names = &parent_name;
3209741b1a6SJames Liao init.num_parents = 1;
3219741b1a6SJames Liao
3226f691a58SChen-Yu Tsai ret = clk_hw_register(NULL, &pll->hw);
3239741b1a6SJames Liao
324*5a938500SDan Carpenter if (ret)
3256f691a58SChen-Yu Tsai return ERR_PTR(ret);
3269741b1a6SJames Liao
3276f691a58SChen-Yu Tsai return &pll->hw;
3286f691a58SChen-Yu Tsai }
3296f691a58SChen-Yu Tsai
mtk_clk_register_pll(const struct mtk_pll_data * data,void __iomem * base)330029c936aSJohnson Wang struct clk_hw *mtk_clk_register_pll(const struct mtk_pll_data *data,
331029c936aSJohnson Wang void __iomem *base)
332029c936aSJohnson Wang {
333029c936aSJohnson Wang struct mtk_clk_pll *pll;
334029c936aSJohnson Wang struct clk_hw *hw;
335029c936aSJohnson Wang
336029c936aSJohnson Wang pll = kzalloc(sizeof(*pll), GFP_KERNEL);
337029c936aSJohnson Wang if (!pll)
338029c936aSJohnson Wang return ERR_PTR(-ENOMEM);
339029c936aSJohnson Wang
340029c936aSJohnson Wang hw = mtk_clk_register_pll_ops(pll, data, base, &mtk_pll_ops);
341*5a938500SDan Carpenter if (IS_ERR(hw))
342*5a938500SDan Carpenter kfree(pll);
343029c936aSJohnson Wang
344029c936aSJohnson Wang return hw;
345029c936aSJohnson Wang }
346029c936aSJohnson Wang
mtk_clk_unregister_pll(struct clk_hw * hw)347029c936aSJohnson Wang void mtk_clk_unregister_pll(struct clk_hw *hw)
3486dd19906SChen-Yu Tsai {
3496dd19906SChen-Yu Tsai struct mtk_clk_pll *pll;
3506dd19906SChen-Yu Tsai
3516dd19906SChen-Yu Tsai if (!hw)
3526dd19906SChen-Yu Tsai return;
3536dd19906SChen-Yu Tsai
3546dd19906SChen-Yu Tsai pll = to_mtk_clk_pll(hw);
3556dd19906SChen-Yu Tsai
3566f691a58SChen-Yu Tsai clk_hw_unregister(hw);
3576dd19906SChen-Yu Tsai kfree(pll);
3586dd19906SChen-Yu Tsai }
3596dd19906SChen-Yu Tsai
mtk_clk_register_plls(struct device_node * node,const struct mtk_pll_data * plls,int num_plls,struct clk_hw_onecell_data * clk_data)3606ae34f2bSChen-Yu Tsai int mtk_clk_register_plls(struct device_node *node,
3616ae34f2bSChen-Yu Tsai const struct mtk_pll_data *plls, int num_plls,
362609cc5e1SChen-Yu Tsai struct clk_hw_onecell_data *clk_data)
3639741b1a6SJames Liao {
3649741b1a6SJames Liao void __iomem *base;
365cdb2bab7SJames Liao int i;
3666f691a58SChen-Yu Tsai struct clk_hw *hw;
3679741b1a6SJames Liao
3689741b1a6SJames Liao base = of_iomap(node, 0);
3699741b1a6SJames Liao if (!base) {
3709741b1a6SJames Liao pr_err("%s(): ioremap failed\n", __func__);
3716ae34f2bSChen-Yu Tsai return -EINVAL;
3729741b1a6SJames Liao }
3739741b1a6SJames Liao
3749741b1a6SJames Liao for (i = 0; i < num_plls; i++) {
3759741b1a6SJames Liao const struct mtk_pll_data *pll = &plls[i];
3769741b1a6SJames Liao
377609cc5e1SChen-Yu Tsai if (!IS_ERR_OR_NULL(clk_data->hws[pll->id])) {
378d54bb86bSChen-Yu Tsai pr_warn("%pOF: Trying to register duplicate clock ID: %d\n",
379d54bb86bSChen-Yu Tsai node, pll->id);
380d54bb86bSChen-Yu Tsai continue;
381d54bb86bSChen-Yu Tsai }
382d54bb86bSChen-Yu Tsai
3836f691a58SChen-Yu Tsai hw = mtk_clk_register_pll(pll, base);
3849741b1a6SJames Liao
3856f691a58SChen-Yu Tsai if (IS_ERR(hw)) {
3866f691a58SChen-Yu Tsai pr_err("Failed to register clk %s: %pe\n", pll->name,
3876f691a58SChen-Yu Tsai hw);
3886ae34f2bSChen-Yu Tsai goto err;
3899741b1a6SJames Liao }
3909741b1a6SJames Liao
3916f691a58SChen-Yu Tsai clk_data->hws[pll->id] = hw;
3929741b1a6SJames Liao }
3936ae34f2bSChen-Yu Tsai
3946ae34f2bSChen-Yu Tsai return 0;
3956ae34f2bSChen-Yu Tsai
3966ae34f2bSChen-Yu Tsai err:
3976ae34f2bSChen-Yu Tsai while (--i >= 0) {
3986ae34f2bSChen-Yu Tsai const struct mtk_pll_data *pll = &plls[i];
3996ae34f2bSChen-Yu Tsai
4006f691a58SChen-Yu Tsai mtk_clk_unregister_pll(clk_data->hws[pll->id]);
401609cc5e1SChen-Yu Tsai clk_data->hws[pll->id] = ERR_PTR(-ENOENT);
4026ae34f2bSChen-Yu Tsai }
4036ae34f2bSChen-Yu Tsai
4046ae34f2bSChen-Yu Tsai iounmap(base);
4056ae34f2bSChen-Yu Tsai
4066f691a58SChen-Yu Tsai return PTR_ERR(hw);
4079741b1a6SJames Liao }
40832b028fbSMiles Chen EXPORT_SYMBOL_GPL(mtk_clk_register_plls);
40932b028fbSMiles Chen
mtk_clk_pll_get_base(struct clk_hw * hw,const struct mtk_pll_data * data)410029c936aSJohnson Wang __iomem void *mtk_clk_pll_get_base(struct clk_hw *hw,
4116dd19906SChen-Yu Tsai const struct mtk_pll_data *data)
4126dd19906SChen-Yu Tsai {
4136dd19906SChen-Yu Tsai struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
4146dd19906SChen-Yu Tsai
4156dd19906SChen-Yu Tsai return pll->base_addr - data->reg;
4166dd19906SChen-Yu Tsai }
4176dd19906SChen-Yu Tsai
mtk_clk_unregister_plls(const struct mtk_pll_data * plls,int num_plls,struct clk_hw_onecell_data * clk_data)4186dd19906SChen-Yu Tsai void mtk_clk_unregister_plls(const struct mtk_pll_data *plls, int num_plls,
419609cc5e1SChen-Yu Tsai struct clk_hw_onecell_data *clk_data)
4206dd19906SChen-Yu Tsai {
4216dd19906SChen-Yu Tsai __iomem void *base = NULL;
4226dd19906SChen-Yu Tsai int i;
4236dd19906SChen-Yu Tsai
4246dd19906SChen-Yu Tsai if (!clk_data)
4256dd19906SChen-Yu Tsai return;
4266dd19906SChen-Yu Tsai
4276dd19906SChen-Yu Tsai for (i = num_plls; i > 0; i--) {
4286dd19906SChen-Yu Tsai const struct mtk_pll_data *pll = &plls[i - 1];
4296dd19906SChen-Yu Tsai
430609cc5e1SChen-Yu Tsai if (IS_ERR_OR_NULL(clk_data->hws[pll->id]))
4316dd19906SChen-Yu Tsai continue;
4326dd19906SChen-Yu Tsai
4336dd19906SChen-Yu Tsai /*
4346dd19906SChen-Yu Tsai * This is quite ugly but unfortunately the clks don't have
4356dd19906SChen-Yu Tsai * any device tied to them, so there's no place to store the
4366dd19906SChen-Yu Tsai * pointer to the I/O region base address. We have to fetch
4376dd19906SChen-Yu Tsai * it from one of the registered clks.
4386dd19906SChen-Yu Tsai */
439609cc5e1SChen-Yu Tsai base = mtk_clk_pll_get_base(clk_data->hws[pll->id], pll);
4406dd19906SChen-Yu Tsai
4416f691a58SChen-Yu Tsai mtk_clk_unregister_pll(clk_data->hws[pll->id]);
442609cc5e1SChen-Yu Tsai clk_data->hws[pll->id] = ERR_PTR(-ENOENT);
4436dd19906SChen-Yu Tsai }
4446dd19906SChen-Yu Tsai
4456dd19906SChen-Yu Tsai iounmap(base);
4466dd19906SChen-Yu Tsai }
4476dd19906SChen-Yu Tsai EXPORT_SYMBOL_GPL(mtk_clk_unregister_plls);
4486dd19906SChen-Yu Tsai
44932b028fbSMiles Chen MODULE_LICENSE("GPL");
450