xref: /openbmc/linux/drivers/clk/mediatek/clk-pll.c (revision 23fe31de)
19741b1a6SJames Liao /*
29741b1a6SJames Liao  * Copyright (c) 2014 MediaTek Inc.
39741b1a6SJames Liao  * Author: James Liao <jamesjj.liao@mediatek.com>
49741b1a6SJames Liao  *
59741b1a6SJames Liao  * This program is free software; you can redistribute it and/or modify
69741b1a6SJames Liao  * it under the terms of the GNU General Public License version 2 as
79741b1a6SJames Liao  * published by the Free Software Foundation.
89741b1a6SJames Liao  *
99741b1a6SJames Liao  * This program is distributed in the hope that it will be useful,
109741b1a6SJames Liao  * but WITHOUT ANY WARRANTY; without even the implied warranty of
119741b1a6SJames Liao  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
129741b1a6SJames Liao  * GNU General Public License for more details.
139741b1a6SJames Liao  */
149741b1a6SJames Liao 
159741b1a6SJames Liao #include <linux/of.h>
169741b1a6SJames Liao #include <linux/of_address.h>
179741b1a6SJames Liao #include <linux/io.h>
189741b1a6SJames Liao #include <linux/slab.h>
199741b1a6SJames Liao #include <linux/clkdev.h>
209741b1a6SJames Liao #include <linux/delay.h>
219741b1a6SJames Liao 
229741b1a6SJames Liao #include "clk-mtk.h"
239741b1a6SJames Liao 
249741b1a6SJames Liao #define REG_CON0		0
259741b1a6SJames Liao #define REG_CON1		4
269741b1a6SJames Liao 
279741b1a6SJames Liao #define CON0_BASE_EN		BIT(0)
289741b1a6SJames Liao #define CON0_PWR_ON		BIT(0)
299741b1a6SJames Liao #define CON0_ISO_EN		BIT(1)
3023fe31deSWeiyi Lu #define PCW_CHG_MASK		BIT(31)
319741b1a6SJames Liao 
329741b1a6SJames Liao #define AUDPLL_TUNER_EN		BIT(31)
339741b1a6SJames Liao 
349741b1a6SJames Liao #define POSTDIV_MASK		0x7
359d7e1a82SOwen Chen 
369d7e1a82SOwen Chen /* default 7 bits integer, can be overridden with pcwibits. */
379741b1a6SJames Liao #define INTEGER_BITS		7
389741b1a6SJames Liao 
399741b1a6SJames Liao /*
409741b1a6SJames Liao  * MediaTek PLLs are configured through their pcw value. The pcw value describes
419741b1a6SJames Liao  * a divider in the PLL feedback loop which consists of 7 bits for the integer
429741b1a6SJames Liao  * part and the remaining bits (if present) for the fractional part. Also they
439741b1a6SJames Liao  * have a 3 bit power-of-two post divider.
449741b1a6SJames Liao  */
459741b1a6SJames Liao 
469741b1a6SJames Liao struct mtk_clk_pll {
479741b1a6SJames Liao 	struct clk_hw	hw;
489741b1a6SJames Liao 	void __iomem	*base_addr;
499741b1a6SJames Liao 	void __iomem	*pd_addr;
509741b1a6SJames Liao 	void __iomem	*pwr_addr;
519741b1a6SJames Liao 	void __iomem	*tuner_addr;
52e2f744a8Sweiyi.lu@mediatek.com 	void __iomem	*tuner_en_addr;
539741b1a6SJames Liao 	void __iomem	*pcw_addr;
5423fe31deSWeiyi Lu 	void __iomem	*pcw_chg_addr;
559741b1a6SJames Liao 	const struct mtk_pll_data *data;
569741b1a6SJames Liao };
579741b1a6SJames Liao 
589741b1a6SJames Liao static inline struct mtk_clk_pll *to_mtk_clk_pll(struct clk_hw *hw)
599741b1a6SJames Liao {
609741b1a6SJames Liao 	return container_of(hw, struct mtk_clk_pll, hw);
619741b1a6SJames Liao }
629741b1a6SJames Liao 
639741b1a6SJames Liao static int mtk_pll_is_prepared(struct clk_hw *hw)
649741b1a6SJames Liao {
659741b1a6SJames Liao 	struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
669741b1a6SJames Liao 
679741b1a6SJames Liao 	return (readl(pll->base_addr + REG_CON0) & CON0_BASE_EN) != 0;
689741b1a6SJames Liao }
699741b1a6SJames Liao 
709741b1a6SJames Liao static unsigned long __mtk_pll_recalc_rate(struct mtk_clk_pll *pll, u32 fin,
719741b1a6SJames Liao 		u32 pcw, int postdiv)
729741b1a6SJames Liao {
739741b1a6SJames Liao 	int pcwbits = pll->data->pcwbits;
749d7e1a82SOwen Chen 	int pcwfbits = 0;
759d7e1a82SOwen Chen 	int ibits;
769741b1a6SJames Liao 	u64 vco;
779741b1a6SJames Liao 	u8 c = 0;
789741b1a6SJames Liao 
799741b1a6SJames Liao 	/* The fractional part of the PLL divider. */
809d7e1a82SOwen Chen 	ibits = pll->data->pcwibits ? pll->data->pcwibits : INTEGER_BITS;
819d7e1a82SOwen Chen 	if (pcwbits > ibits)
829d7e1a82SOwen Chen 		pcwfbits = pcwbits - ibits;
839741b1a6SJames Liao 
849741b1a6SJames Liao 	vco = (u64)fin * pcw;
859741b1a6SJames Liao 
869741b1a6SJames Liao 	if (pcwfbits && (vco & GENMASK(pcwfbits - 1, 0)))
879741b1a6SJames Liao 		c = 1;
889741b1a6SJames Liao 
899741b1a6SJames Liao 	vco >>= pcwfbits;
909741b1a6SJames Liao 
919741b1a6SJames Liao 	if (c)
929741b1a6SJames Liao 		vco++;
939741b1a6SJames Liao 
949741b1a6SJames Liao 	return ((unsigned long)vco + postdiv - 1) / postdiv;
959741b1a6SJames Liao }
969741b1a6SJames Liao 
97be17ca6aSOwen Chen static void __mtk_pll_tuner_enable(struct mtk_clk_pll *pll)
98be17ca6aSOwen Chen {
99be17ca6aSOwen Chen 	u32 r;
100be17ca6aSOwen Chen 
101be17ca6aSOwen Chen 	if (pll->tuner_en_addr) {
102be17ca6aSOwen Chen 		r = readl(pll->tuner_en_addr) | BIT(pll->data->tuner_en_bit);
103be17ca6aSOwen Chen 		writel(r, pll->tuner_en_addr);
104be17ca6aSOwen Chen 	} else if (pll->tuner_addr) {
105be17ca6aSOwen Chen 		r = readl(pll->tuner_addr) | AUDPLL_TUNER_EN;
106be17ca6aSOwen Chen 		writel(r, pll->tuner_addr);
107be17ca6aSOwen Chen 	}
108be17ca6aSOwen Chen }
109be17ca6aSOwen Chen 
110be17ca6aSOwen Chen static void __mtk_pll_tuner_disable(struct mtk_clk_pll *pll)
111be17ca6aSOwen Chen {
112be17ca6aSOwen Chen 	u32 r;
113be17ca6aSOwen Chen 
114be17ca6aSOwen Chen 	if (pll->tuner_en_addr) {
115be17ca6aSOwen Chen 		r = readl(pll->tuner_en_addr) & ~BIT(pll->data->tuner_en_bit);
116be17ca6aSOwen Chen 		writel(r, pll->tuner_en_addr);
117be17ca6aSOwen Chen 	} else if (pll->tuner_addr) {
118be17ca6aSOwen Chen 		r = readl(pll->tuner_addr) & ~AUDPLL_TUNER_EN;
119be17ca6aSOwen Chen 		writel(r, pll->tuner_addr);
120be17ca6aSOwen Chen 	}
121be17ca6aSOwen Chen }
122be17ca6aSOwen Chen 
1239741b1a6SJames Liao static void mtk_pll_set_rate_regs(struct mtk_clk_pll *pll, u32 pcw,
1249741b1a6SJames Liao 		int postdiv)
1259741b1a6SJames Liao {
12623fe31deSWeiyi Lu 	u32 chg, val;
1279741b1a6SJames Liao 	int pll_en;
1289741b1a6SJames Liao 
1299741b1a6SJames Liao 	pll_en = readl(pll->base_addr + REG_CON0) & CON0_BASE_EN;
1309741b1a6SJames Liao 
131be17ca6aSOwen Chen 	/* disable tuner */
132be17ca6aSOwen Chen 	__mtk_pll_tuner_disable(pll);
133be17ca6aSOwen Chen 
134b3be457eSJames Liao 	/* set postdiv */
135b3be457eSJames Liao 	val = readl(pll->pd_addr);
136b3be457eSJames Liao 	val &= ~(POSTDIV_MASK << pll->data->pd_shift);
137b3be457eSJames Liao 	val |= (ffs(postdiv) - 1) << pll->data->pd_shift;
1389741b1a6SJames Liao 
139b3be457eSJames Liao 	/* postdiv and pcw need to set at the same time if on same register */
140b3be457eSJames Liao 	if (pll->pd_addr != pll->pcw_addr) {
141b3be457eSJames Liao 		writel(val, pll->pd_addr);
142b3be457eSJames Liao 		val = readl(pll->pcw_addr);
143b3be457eSJames Liao 	}
144b3be457eSJames Liao 
145b3be457eSJames Liao 	/* set pcw */
1469741b1a6SJames Liao 	val &= ~GENMASK(pll->data->pcw_shift + pll->data->pcwbits - 1,
1479741b1a6SJames Liao 			pll->data->pcw_shift);
1489741b1a6SJames Liao 	val |= pcw << pll->data->pcw_shift;
1499741b1a6SJames Liao 	writel(val, pll->pcw_addr);
1509741b1a6SJames Liao 
15123fe31deSWeiyi Lu 	chg = readl(pll->pcw_chg_addr);
1529741b1a6SJames Liao 
1539741b1a6SJames Liao 	if (pll_en)
15423fe31deSWeiyi Lu 		chg |= PCW_CHG_MASK;
1559741b1a6SJames Liao 
15623fe31deSWeiyi Lu 	writel(chg, pll->pcw_chg_addr);
1579741b1a6SJames Liao 	if (pll->tuner_addr)
15823fe31deSWeiyi Lu 		writel(val + 1, pll->tuner_addr);
1599741b1a6SJames Liao 
160be17ca6aSOwen Chen 	/* restore tuner_en */
161be17ca6aSOwen Chen 	__mtk_pll_tuner_enable(pll);
162be17ca6aSOwen Chen 
1639741b1a6SJames Liao 	if (pll_en)
1649741b1a6SJames Liao 		udelay(20);
1659741b1a6SJames Liao }
1669741b1a6SJames Liao 
1679741b1a6SJames Liao /*
1689741b1a6SJames Liao  * mtk_pll_calc_values - calculate good values for a given input frequency.
1699741b1a6SJames Liao  * @pll:	The pll
1709741b1a6SJames Liao  * @pcw:	The pcw value (output)
1719741b1a6SJames Liao  * @postdiv:	The post divider (output)
1729741b1a6SJames Liao  * @freq:	The desired target frequency
1739741b1a6SJames Liao  * @fin:	The input frequency
1749741b1a6SJames Liao  *
1759741b1a6SJames Liao  */
1769741b1a6SJames Liao static void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32 *postdiv,
1779741b1a6SJames Liao 		u32 freq, u32 fin)
1789741b1a6SJames Liao {
1799d7e1a82SOwen Chen 	unsigned long fmin = pll->data->fmin ? pll->data->fmin : (1000 * MHZ);
18075ce0cdbSJames Liao 	const struct mtk_pll_div_table *div_table = pll->data->div_table;
1819741b1a6SJames Liao 	u64 _pcw;
1829d7e1a82SOwen Chen 	int ibits;
1839741b1a6SJames Liao 	u32 val;
1849741b1a6SJames Liao 
1859741b1a6SJames Liao 	if (freq > pll->data->fmax)
1869741b1a6SJames Liao 		freq = pll->data->fmax;
1879741b1a6SJames Liao 
18875ce0cdbSJames Liao 	if (div_table) {
18975ce0cdbSJames Liao 		if (freq > div_table[0].freq)
19075ce0cdbSJames Liao 			freq = div_table[0].freq;
19175ce0cdbSJames Liao 
19275ce0cdbSJames Liao 		for (val = 0; div_table[val + 1].freq != 0; val++) {
19375ce0cdbSJames Liao 			if (freq > div_table[val + 1].freq)
19475ce0cdbSJames Liao 				break;
19575ce0cdbSJames Liao 		}
19675ce0cdbSJames Liao 		*postdiv = 1 << val;
19775ce0cdbSJames Liao 	} else {
198196de71aSJames Liao 		for (val = 0; val < 5; val++) {
1999741b1a6SJames Liao 			*postdiv = 1 << val;
200196de71aSJames Liao 			if ((u64)freq * *postdiv >= fmin)
2019741b1a6SJames Liao 				break;
2029741b1a6SJames Liao 		}
20375ce0cdbSJames Liao 	}
2049741b1a6SJames Liao 
2059741b1a6SJames Liao 	/* _pcw = freq * postdiv / fin * 2^pcwfbits */
2069d7e1a82SOwen Chen 	ibits = pll->data->pcwibits ? pll->data->pcwibits : INTEGER_BITS;
2079d7e1a82SOwen Chen 	_pcw = ((u64)freq << val) << (pll->data->pcwbits - ibits);
2089741b1a6SJames Liao 	do_div(_pcw, fin);
2099741b1a6SJames Liao 
2109741b1a6SJames Liao 	*pcw = (u32)_pcw;
2119741b1a6SJames Liao }
2129741b1a6SJames Liao 
2139741b1a6SJames Liao static int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
2149741b1a6SJames Liao 		unsigned long parent_rate)
2159741b1a6SJames Liao {
2169741b1a6SJames Liao 	struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
2179741b1a6SJames Liao 	u32 pcw = 0;
2189741b1a6SJames Liao 	u32 postdiv;
2199741b1a6SJames Liao 
2209741b1a6SJames Liao 	mtk_pll_calc_values(pll, &pcw, &postdiv, rate, parent_rate);
2219741b1a6SJames Liao 	mtk_pll_set_rate_regs(pll, pcw, postdiv);
2229741b1a6SJames Liao 
2239741b1a6SJames Liao 	return 0;
2249741b1a6SJames Liao }
2259741b1a6SJames Liao 
2269741b1a6SJames Liao static unsigned long mtk_pll_recalc_rate(struct clk_hw *hw,
2279741b1a6SJames Liao 		unsigned long parent_rate)
2289741b1a6SJames Liao {
2299741b1a6SJames Liao 	struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
2309741b1a6SJames Liao 	u32 postdiv;
2319741b1a6SJames Liao 	u32 pcw;
2329741b1a6SJames Liao 
2339741b1a6SJames Liao 	postdiv = (readl(pll->pd_addr) >> pll->data->pd_shift) & POSTDIV_MASK;
2349741b1a6SJames Liao 	postdiv = 1 << postdiv;
2359741b1a6SJames Liao 
2369741b1a6SJames Liao 	pcw = readl(pll->pcw_addr) >> pll->data->pcw_shift;
2379741b1a6SJames Liao 	pcw &= GENMASK(pll->data->pcwbits - 1, 0);
2389741b1a6SJames Liao 
2399741b1a6SJames Liao 	return __mtk_pll_recalc_rate(pll, parent_rate, pcw, postdiv);
2409741b1a6SJames Liao }
2419741b1a6SJames Liao 
2429741b1a6SJames Liao static long mtk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
2439741b1a6SJames Liao 		unsigned long *prate)
2449741b1a6SJames Liao {
2459741b1a6SJames Liao 	struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
2469741b1a6SJames Liao 	u32 pcw = 0;
2479741b1a6SJames Liao 	int postdiv;
2489741b1a6SJames Liao 
2499741b1a6SJames Liao 	mtk_pll_calc_values(pll, &pcw, &postdiv, rate, *prate);
2509741b1a6SJames Liao 
2519741b1a6SJames Liao 	return __mtk_pll_recalc_rate(pll, *prate, pcw, postdiv);
2529741b1a6SJames Liao }
2539741b1a6SJames Liao 
2549741b1a6SJames Liao static int mtk_pll_prepare(struct clk_hw *hw)
2559741b1a6SJames Liao {
2569741b1a6SJames Liao 	struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
2579741b1a6SJames Liao 	u32 r;
2589741b1a6SJames Liao 
2599741b1a6SJames Liao 	r = readl(pll->pwr_addr) | CON0_PWR_ON;
2609741b1a6SJames Liao 	writel(r, pll->pwr_addr);
2619741b1a6SJames Liao 	udelay(1);
2629741b1a6SJames Liao 
2639741b1a6SJames Liao 	r = readl(pll->pwr_addr) & ~CON0_ISO_EN;
2649741b1a6SJames Liao 	writel(r, pll->pwr_addr);
2659741b1a6SJames Liao 	udelay(1);
2669741b1a6SJames Liao 
2679741b1a6SJames Liao 	r = readl(pll->base_addr + REG_CON0);
2689741b1a6SJames Liao 	r |= pll->data->en_mask;
2699741b1a6SJames Liao 	writel(r, pll->base_addr + REG_CON0);
2709741b1a6SJames Liao 
271be17ca6aSOwen Chen 	__mtk_pll_tuner_enable(pll);
2729741b1a6SJames Liao 
2739741b1a6SJames Liao 	udelay(20);
2749741b1a6SJames Liao 
2759741b1a6SJames Liao 	if (pll->data->flags & HAVE_RST_BAR) {
2769741b1a6SJames Liao 		r = readl(pll->base_addr + REG_CON0);
2779741b1a6SJames Liao 		r |= pll->data->rst_bar_mask;
2789741b1a6SJames Liao 		writel(r, pll->base_addr + REG_CON0);
2799741b1a6SJames Liao 	}
2809741b1a6SJames Liao 
2819741b1a6SJames Liao 	return 0;
2829741b1a6SJames Liao }
2839741b1a6SJames Liao 
2849741b1a6SJames Liao static void mtk_pll_unprepare(struct clk_hw *hw)
2859741b1a6SJames Liao {
2869741b1a6SJames Liao 	struct mtk_clk_pll *pll = to_mtk_clk_pll(hw);
2879741b1a6SJames Liao 	u32 r;
2889741b1a6SJames Liao 
2899741b1a6SJames Liao 	if (pll->data->flags & HAVE_RST_BAR) {
2909741b1a6SJames Liao 		r = readl(pll->base_addr + REG_CON0);
2919741b1a6SJames Liao 		r &= ~pll->data->rst_bar_mask;
2929741b1a6SJames Liao 		writel(r, pll->base_addr + REG_CON0);
2939741b1a6SJames Liao 	}
2949741b1a6SJames Liao 
295be17ca6aSOwen Chen 	__mtk_pll_tuner_disable(pll);
2969741b1a6SJames Liao 
2979741b1a6SJames Liao 	r = readl(pll->base_addr + REG_CON0);
2989741b1a6SJames Liao 	r &= ~CON0_BASE_EN;
2999741b1a6SJames Liao 	writel(r, pll->base_addr + REG_CON0);
3009741b1a6SJames Liao 
3019741b1a6SJames Liao 	r = readl(pll->pwr_addr) | CON0_ISO_EN;
3029741b1a6SJames Liao 	writel(r, pll->pwr_addr);
3039741b1a6SJames Liao 
3049741b1a6SJames Liao 	r = readl(pll->pwr_addr) & ~CON0_PWR_ON;
3059741b1a6SJames Liao 	writel(r, pll->pwr_addr);
3069741b1a6SJames Liao }
3079741b1a6SJames Liao 
3089741b1a6SJames Liao static const struct clk_ops mtk_pll_ops = {
3099741b1a6SJames Liao 	.is_prepared	= mtk_pll_is_prepared,
3109741b1a6SJames Liao 	.prepare	= mtk_pll_prepare,
3119741b1a6SJames Liao 	.unprepare	= mtk_pll_unprepare,
3129741b1a6SJames Liao 	.recalc_rate	= mtk_pll_recalc_rate,
3139741b1a6SJames Liao 	.round_rate	= mtk_pll_round_rate,
3149741b1a6SJames Liao 	.set_rate	= mtk_pll_set_rate,
3159741b1a6SJames Liao };
3169741b1a6SJames Liao 
3179741b1a6SJames Liao static struct clk *mtk_clk_register_pll(const struct mtk_pll_data *data,
3189741b1a6SJames Liao 		void __iomem *base)
3199741b1a6SJames Liao {
3209741b1a6SJames Liao 	struct mtk_clk_pll *pll;
32195f58981SRicky Liang 	struct clk_init_data init = {};
3229741b1a6SJames Liao 	struct clk *clk;
3239741b1a6SJames Liao 	const char *parent_name = "clk26m";
3249741b1a6SJames Liao 
3259741b1a6SJames Liao 	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
3269741b1a6SJames Liao 	if (!pll)
3279741b1a6SJames Liao 		return ERR_PTR(-ENOMEM);
3289741b1a6SJames Liao 
3299741b1a6SJames Liao 	pll->base_addr = base + data->reg;
3309741b1a6SJames Liao 	pll->pwr_addr = base + data->pwr_reg;
3319741b1a6SJames Liao 	pll->pd_addr = base + data->pd_reg;
3329741b1a6SJames Liao 	pll->pcw_addr = base + data->pcw_reg;
33323fe31deSWeiyi Lu 	if (data->pcw_chg_reg)
33423fe31deSWeiyi Lu 		pll->pcw_chg_addr = base + data->pcw_chg_reg;
33523fe31deSWeiyi Lu 	else
33623fe31deSWeiyi Lu 		pll->pcw_chg_addr = pll->base_addr + REG_CON1;
3379741b1a6SJames Liao 	if (data->tuner_reg)
3389741b1a6SJames Liao 		pll->tuner_addr = base + data->tuner_reg;
339e2f744a8Sweiyi.lu@mediatek.com 	if (data->tuner_en_reg)
340e2f744a8Sweiyi.lu@mediatek.com 		pll->tuner_en_addr = base + data->tuner_en_reg;
3419741b1a6SJames Liao 	pll->hw.init = &init;
3429741b1a6SJames Liao 	pll->data = data;
3439741b1a6SJames Liao 
3449741b1a6SJames Liao 	init.name = data->name;
345e9862118SShunli Wang 	init.flags = (data->flags & PLL_AO) ? CLK_IS_CRITICAL : 0;
3469741b1a6SJames Liao 	init.ops = &mtk_pll_ops;
347c955bf39SChen Zhong 	if (data->parent_name)
348c955bf39SChen Zhong 		init.parent_names = &data->parent_name;
349c955bf39SChen Zhong 	else
3509741b1a6SJames Liao 		init.parent_names = &parent_name;
3519741b1a6SJames Liao 	init.num_parents = 1;
3529741b1a6SJames Liao 
3539741b1a6SJames Liao 	clk = clk_register(NULL, &pll->hw);
3549741b1a6SJames Liao 
3559741b1a6SJames Liao 	if (IS_ERR(clk))
3569741b1a6SJames Liao 		kfree(pll);
3579741b1a6SJames Liao 
3589741b1a6SJames Liao 	return clk;
3599741b1a6SJames Liao }
3609741b1a6SJames Liao 
361928f3bfbSJames Liao void mtk_clk_register_plls(struct device_node *node,
3629741b1a6SJames Liao 		const struct mtk_pll_data *plls, int num_plls, struct clk_onecell_data *clk_data)
3639741b1a6SJames Liao {
3649741b1a6SJames Liao 	void __iomem *base;
365cdb2bab7SJames Liao 	int i;
3669741b1a6SJames Liao 	struct clk *clk;
3679741b1a6SJames Liao 
3689741b1a6SJames Liao 	base = of_iomap(node, 0);
3699741b1a6SJames Liao 	if (!base) {
3709741b1a6SJames Liao 		pr_err("%s(): ioremap failed\n", __func__);
3719741b1a6SJames Liao 		return;
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 
3779741b1a6SJames Liao 		clk = mtk_clk_register_pll(pll, base);
3789741b1a6SJames Liao 
3799741b1a6SJames Liao 		if (IS_ERR(clk)) {
3809741b1a6SJames Liao 			pr_err("Failed to register clk %s: %ld\n",
3819741b1a6SJames Liao 					pll->name, PTR_ERR(clk));
3829741b1a6SJames Liao 			continue;
3839741b1a6SJames Liao 		}
3849741b1a6SJames Liao 
3859741b1a6SJames Liao 		clk_data->clks[pll->id] = clk;
3869741b1a6SJames Liao 	}
3879741b1a6SJames Liao }
388