xref: /rk3399_ARM-atf/plat/mediatek/mt8195/drivers/apusys/apupll.c (revision 296b590206aa6db51e5c82b1a97a4f9707b49c4d)
1*296b5902SFlora Fu /*
2*296b5902SFlora Fu  * Copyright (c) 2021, ARM Limited and Contributors. All rights reserved.
3*296b5902SFlora Fu  *
4*296b5902SFlora Fu  * SPDX-License-Identifier: BSD-3-Clause
5*296b5902SFlora Fu  */
6*296b5902SFlora Fu 
7*296b5902SFlora Fu #include <errno.h>
8*296b5902SFlora Fu 
9*296b5902SFlora Fu #include <arch_helpers.h>
10*296b5902SFlora Fu #include <common/debug.h>
11*296b5902SFlora Fu #include <drivers/delay_timer.h>
12*296b5902SFlora Fu #include <lib/spinlock.h>
13*296b5902SFlora Fu 
14*296b5902SFlora Fu #include <apupwr_clkctl.h>
15*296b5902SFlora Fu #include <apupwr_clkctl_def.h>
16*296b5902SFlora Fu #include <mtk_plat_common.h>
17*296b5902SFlora Fu #include <platform_def.h>
18*296b5902SFlora Fu 
19*296b5902SFlora Fu uint32_t mixed_con0_addr[APUPLL_MAX] = {
20*296b5902SFlora Fu 	APU_PLL4H_PLL1_CON0,
21*296b5902SFlora Fu 	APU_PLL4H_PLL2_CON0,
22*296b5902SFlora Fu 	APU_PLL4H_PLL3_CON0,
23*296b5902SFlora Fu 	APU_PLL4H_PLL4_CON0,
24*296b5902SFlora Fu };
25*296b5902SFlora Fu 
26*296b5902SFlora Fu uint32_t mixed_con1_addr[APUPLL_MAX] = {
27*296b5902SFlora Fu 	APU_PLL4H_PLL1_CON1,
28*296b5902SFlora Fu 	APU_PLL4H_PLL2_CON1,
29*296b5902SFlora Fu 	APU_PLL4H_PLL3_CON1,
30*296b5902SFlora Fu 	APU_PLL4H_PLL4_CON1,
31*296b5902SFlora Fu };
32*296b5902SFlora Fu 
33*296b5902SFlora Fu uint32_t mixed_con3_addr[APUPLL_MAX] = {
34*296b5902SFlora Fu 	APU_PLL4H_PLL1_CON3,
35*296b5902SFlora Fu 	APU_PLL4H_PLL2_CON3,
36*296b5902SFlora Fu 	APU_PLL4H_PLL3_CON3,
37*296b5902SFlora Fu 	APU_PLL4H_PLL4_CON3,
38*296b5902SFlora Fu };
39*296b5902SFlora Fu 
40*296b5902SFlora Fu uint32_t fhctl_dds_addr[APUPLL_MAX] = {
41*296b5902SFlora Fu 	APU_PLL4H_FHCTL0_DDS,
42*296b5902SFlora Fu 	APU_PLL4H_FHCTL1_DDS,
43*296b5902SFlora Fu 	APU_PLL4H_FHCTL2_DDS,
44*296b5902SFlora Fu 	APU_PLL4H_FHCTL3_DDS,
45*296b5902SFlora Fu };
46*296b5902SFlora Fu 
47*296b5902SFlora Fu uint32_t fhctl_dvfs_addr[APUPLL_MAX] = {
48*296b5902SFlora Fu 	APU_PLL4H_FHCTL0_DVFS,
49*296b5902SFlora Fu 	APU_PLL4H_FHCTL1_DVFS,
50*296b5902SFlora Fu 	APU_PLL4H_FHCTL2_DVFS,
51*296b5902SFlora Fu 	APU_PLL4H_FHCTL3_DVFS,
52*296b5902SFlora Fu };
53*296b5902SFlora Fu 
54*296b5902SFlora Fu uint32_t fhctl_mon_addr[APUPLL_MAX] = {
55*296b5902SFlora Fu 	APU_PLL4H_FHCTL0_MON,
56*296b5902SFlora Fu 	APU_PLL4H_FHCTL1_MON,
57*296b5902SFlora Fu 	APU_PLL4H_FHCTL2_MON,
58*296b5902SFlora Fu 	APU_PLL4H_FHCTL3_MON,
59*296b5902SFlora Fu };
60*296b5902SFlora Fu 
61*296b5902SFlora Fu uint32_t fhctl_cfg_addr[APUPLL_MAX] = {
62*296b5902SFlora Fu 	APU_PLL4H_FHCTL0_CFG,
63*296b5902SFlora Fu 	APU_PLL4H_FHCTL1_CFG,
64*296b5902SFlora Fu 	APU_PLL4H_FHCTL2_CFG,
65*296b5902SFlora Fu 	APU_PLL4H_FHCTL3_CFG,
66*296b5902SFlora Fu };
67*296b5902SFlora Fu 
68*296b5902SFlora Fu static spinlock_t apupll_lock;
69*296b5902SFlora Fu static spinlock_t npupll_lock;
70*296b5902SFlora Fu static spinlock_t apupll_1_lock;
71*296b5902SFlora Fu static spinlock_t apupll_2_lock;
72*296b5902SFlora Fu static uint32_t pll_cnt[APUPLL_MAX];
73*296b5902SFlora Fu /**
74*296b5902SFlora Fu  * vd2pllidx() - voltage domain to pll idx.
75*296b5902SFlora Fu  * @domain: the voltage domain for getting pll index.
76*296b5902SFlora Fu  *
77*296b5902SFlora Fu  * Caller will get correspond pll index by different voltage domain.
78*296b5902SFlora Fu  * pll_idx[0] --> APUPLL (MDLA0/1)
79*296b5902SFlora Fu  * pll_idx[1] --> NPUPLL (VPU0/1)
80*296b5902SFlora Fu  * pll_idx[2] --> APUPLL1(CONN)
81*296b5902SFlora Fu  * pll_idx[3] --> APUPLL2(IOMMU)
82*296b5902SFlora Fu  * The longer description may have multiple paragraphs.
83*296b5902SFlora Fu  *
84*296b5902SFlora Fu  * Context: Any context.
85*296b5902SFlora Fu  * Return:
86*296b5902SFlora Fu  * * 0 ~ 3	- return the corresponding pll index
87*296b5902SFlora Fu  * * -EEXIST	- cannot find pll idex of the specific voltage domain
88*296b5902SFlora Fu  *
89*296b5902SFlora Fu  */
90*296b5902SFlora Fu static int32_t vd2pllidx(enum dvfs_voltage_domain domain)
91*296b5902SFlora Fu {
92*296b5902SFlora Fu 	int32_t ret;
93*296b5902SFlora Fu 
94*296b5902SFlora Fu 	switch (domain) {
95*296b5902SFlora Fu 	case V_VPU0:
96*296b5902SFlora Fu 	case V_VPU1:
97*296b5902SFlora Fu 		ret = NPUPLL;
98*296b5902SFlora Fu 		break;
99*296b5902SFlora Fu 	case V_MDLA0:
100*296b5902SFlora Fu 	case V_MDLA1:
101*296b5902SFlora Fu 		ret = APUPLL;
102*296b5902SFlora Fu 		break;
103*296b5902SFlora Fu 	case V_TOP_IOMMU:
104*296b5902SFlora Fu 		ret = APUPLL2;
105*296b5902SFlora Fu 		break;
106*296b5902SFlora Fu 	case V_APU_CONN:
107*296b5902SFlora Fu 		ret = APUPLL1;
108*296b5902SFlora Fu 		break;
109*296b5902SFlora Fu 	default:
110*296b5902SFlora Fu 		ERROR("%s wrong voltage domain: %d\n", __func__, domain);
111*296b5902SFlora Fu 		ret = -EEXIST; /* non-exist */
112*296b5902SFlora Fu 		break;
113*296b5902SFlora Fu 	}
114*296b5902SFlora Fu 
115*296b5902SFlora Fu 	return ret;
116*296b5902SFlora Fu }
117*296b5902SFlora Fu 
118*296b5902SFlora Fu /**
119*296b5902SFlora Fu  * pllidx2name() - return names of specific pll index.
120*296b5902SFlora Fu  * @pll_idx: input for specific pll index.
121*296b5902SFlora Fu  *
122*296b5902SFlora Fu  * Given pll index, this function will return name of it.
123*296b5902SFlora Fu  *
124*296b5902SFlora Fu  * Context: Any context.
125*296b5902SFlora Fu  * Return: Names of pll_idx, if found, otherwise will return "NULL"
126*296b5902SFlora Fu  */
127*296b5902SFlora Fu static const char *pllidx2name(int32_t pll_idx)
128*296b5902SFlora Fu {
129*296b5902SFlora Fu 	static const char *const names[] = {
130*296b5902SFlora Fu 		[APUPLL] = "PLL4H_PLL1",
131*296b5902SFlora Fu 		[NPUPLL] = "PLL4H_PLL2",
132*296b5902SFlora Fu 		[APUPLL1] = "PLL4H_PLL3",
133*296b5902SFlora Fu 		[APUPLL2] = "PLL4H_PLL4",
134*296b5902SFlora Fu 		[APUPLL_MAX] = "NULL",
135*296b5902SFlora Fu 	};
136*296b5902SFlora Fu 
137*296b5902SFlora Fu 	if (pll_idx >= APUPLL_MAX) {
138*296b5902SFlora Fu 		pll_idx = APUPLL_MAX;
139*296b5902SFlora Fu 	}
140*296b5902SFlora Fu 
141*296b5902SFlora Fu 	return names[pll_idx];
142*296b5902SFlora Fu }
143*296b5902SFlora Fu 
144*296b5902SFlora Fu /**
145*296b5902SFlora Fu  * _fhctl_mon_done() - poll whether fhctl HW mode is done.
146*296b5902SFlora Fu  * @pll_idx: input for specific pll index.
147*296b5902SFlora Fu  * @tar_dds: target dds for fhctl_mon to be.
148*296b5902SFlora Fu  *
149*296b5902SFlora Fu  * Given pll index, this function will continue to poll whether fhctl_mon
150*296b5902SFlora Fu  * has reached the expected value within 80us.
151*296b5902SFlora Fu  *
152*296b5902SFlora Fu  * Context: Any context.
153*296b5902SFlora Fu  * Return:
154*296b5902SFlora Fu  * * 0 - OK for fhctl_mon == tar_dds
155*296b5902SFlora Fu  * * -ETIMEDOUT - fhctl_mon not reach tar_dds
156*296b5902SFlora Fu  */
157*296b5902SFlora Fu static int32_t _fhctl_mon_done(uint32_t pll_idx, unsigned long tar_dds)
158*296b5902SFlora Fu {
159*296b5902SFlora Fu 	unsigned long mon_dds;
160*296b5902SFlora Fu 	uint64_t timeout = timeout_init_us(PLL_READY_TIME_20US);
161*296b5902SFlora Fu 	int32_t ret = 0;
162*296b5902SFlora Fu 
163*296b5902SFlora Fu 	tar_dds &= DDS_MASK;
164*296b5902SFlora Fu 	do {
165*296b5902SFlora Fu 		mon_dds = apupwr_readl(fhctl_mon_addr[pll_idx]) & DDS_MASK;
166*296b5902SFlora Fu 		if (mon_dds == tar_dds) {
167*296b5902SFlora Fu 			break;
168*296b5902SFlora Fu 		}
169*296b5902SFlora Fu 
170*296b5902SFlora Fu 		if (timeout_elapsed(timeout)) {
171*296b5902SFlora Fu 			ERROR("%s monitor DDS 0x%08lx != expect 0x%08lx\n",
172*296b5902SFlora Fu 				  pllidx2name(pll_idx), mon_dds, tar_dds);
173*296b5902SFlora Fu 			ret = -ETIMEDOUT;
174*296b5902SFlora Fu 			break;
175*296b5902SFlora Fu 		}
176*296b5902SFlora Fu 	} while (mon_dds != tar_dds);
177*296b5902SFlora Fu 
178*296b5902SFlora Fu 	return ret;
179*296b5902SFlora Fu }
180*296b5902SFlora Fu 
181*296b5902SFlora Fu /**
182*296b5902SFlora Fu  * _pll_get_postdiv_reg() - return current post dividor of pll_idx
183*296b5902SFlora Fu  * @pll_idx: input for specific pll index.
184*296b5902SFlora Fu  *
185*296b5902SFlora Fu  * Given pll index, this function will return its current post dividor.
186*296b5902SFlora Fu  *
187*296b5902SFlora Fu  * Context: Any context.
188*296b5902SFlora Fu  * Return: post dividor of current pll_idx.
189*296b5902SFlora Fu  *
190*296b5902SFlora Fu  */
191*296b5902SFlora Fu static uint32_t _pll_get_postdiv_reg(uint32_t pll_idx)
192*296b5902SFlora Fu {
193*296b5902SFlora Fu 	int32_t pll_postdiv_reg = 0;
194*296b5902SFlora Fu 	uint32_t val;
195*296b5902SFlora Fu 
196*296b5902SFlora Fu 	val = apupwr_readl(mixed_con1_addr[pll_idx]);
197*296b5902SFlora Fu 	pll_postdiv_reg = (val >> POSDIV_SHIFT) & POSDIV_MASK;
198*296b5902SFlora Fu 	return pll_postdiv_reg;
199*296b5902SFlora Fu }
200*296b5902SFlora Fu 
201*296b5902SFlora Fu /**
202*296b5902SFlora Fu  * _set_postdiv_reg() - set pll_idx's post dividor.
203*296b5902SFlora Fu  * @pll_idx: Which PLL to enable/disable
204*296b5902SFlora Fu  * @post_div: the register value of post dividor to be wrtten.
205*296b5902SFlora Fu  *
206*296b5902SFlora Fu  * Below are lists of post dividor register value and its meaning:
207*296b5902SFlora Fu  * [31]     APUPLL_SDM_PCW_CHG
208*296b5902SFlora Fu  * [26:24]  APUPLL_POSDIV
209*296b5902SFlora Fu  * [21:0]   APUPLL_SDM_PCW (8bit integer + 14bit fraction)
210*296b5902SFlora Fu  * expected freq range ----- divider-------post divider in reg:
211*296b5902SFlora Fu  * >1500M   (1500/ 1)         -> 1        -> 0(2 to the zero power)
212*296b5902SFlora Fu  * > 750M   (1500/ 2)         -> 2        -> 1(2 to the 1st  power)
213*296b5902SFlora Fu  * > 375M   (1500/ 4)         -> 4        -> 2(2 to the 2nd  power)
214*296b5902SFlora Fu  * > 187.5M (1500/ 8)         -> 8        -> 3(2 to the 3rd  power)
215*296b5902SFlora Fu  * > 93.75M (1500/16)         -> 16       -> 4(2 to the 4th  power)
216*296b5902SFlora Fu  *
217*296b5902SFlora Fu  * Context: Any context.
218*296b5902SFlora Fu  */
219*296b5902SFlora Fu static void _set_postdiv_reg(uint32_t pll_idx, uint32_t post_div)
220*296b5902SFlora Fu {
221*296b5902SFlora Fu 	apupwr_clrbits(POSDIV_MASK << POSDIV_SHIFT, mixed_con1_addr[pll_idx]);
222*296b5902SFlora Fu 	apupwr_setbits((post_div & POSDIV_MASK) << POSDIV_SHIFT,
223*296b5902SFlora Fu 			mixed_con1_addr[pll_idx]);
224*296b5902SFlora Fu }
225*296b5902SFlora Fu 
226*296b5902SFlora Fu /**
227*296b5902SFlora Fu  * _cal_pll_data() - input freq, calculate correspond post dividor and dds.
228*296b5902SFlora Fu  * @pd: address of output post dividor.
229*296b5902SFlora Fu  * @dds: address of output dds.
230*296b5902SFlora Fu  * @freq: input frequency.
231*296b5902SFlora Fu  *
232*296b5902SFlora Fu  * Given freq, this function will calculate correspond post dividor and dds.
233*296b5902SFlora Fu  *
234*296b5902SFlora Fu  * Context: Any context.
235*296b5902SFlora Fu  * Return:
236*296b5902SFlora Fu  * * 0 - done for calculating post dividor and dds.
237*296b5902SFlora Fu  */
238*296b5902SFlora Fu static int32_t _cal_pll_data(uint32_t *pd, uint32_t *dds, uint32_t freq)
239*296b5902SFlora Fu {
240*296b5902SFlora Fu 	uint32_t vco, postdiv_val = 1, postdiv_reg = 0;
241*296b5902SFlora Fu 	uint32_t pcw_val;
242*296b5902SFlora Fu 
243*296b5902SFlora Fu 	vco = freq;
244*296b5902SFlora Fu 	postdiv_val = 1;
245*296b5902SFlora Fu 	postdiv_reg = 0;
246*296b5902SFlora Fu 	while (vco <= FREQ_VCO_MIN) {
247*296b5902SFlora Fu 		postdiv_val = postdiv_val << 1;
248*296b5902SFlora Fu 		postdiv_reg = postdiv_reg + 1;
249*296b5902SFlora Fu 		vco = vco << 1;
250*296b5902SFlora Fu 	}
251*296b5902SFlora Fu 
252*296b5902SFlora Fu 	pcw_val = vco * (1 << PCW_FRACTIONAL_SHIFT);
253*296b5902SFlora Fu 	pcw_val = pcw_val / FREQ_FIN;
254*296b5902SFlora Fu 
255*296b5902SFlora Fu 	if (postdiv_reg == 0) { /* Fvco * 2 with post_divider = 2 */
256*296b5902SFlora Fu 		pcw_val = pcw_val * 2;
257*296b5902SFlora Fu 		postdiv_val = postdiv_val << 1;
258*296b5902SFlora Fu 		postdiv_reg = postdiv_reg + 1;
259*296b5902SFlora Fu 	} /* Post divider is 1 is not available */
260*296b5902SFlora Fu 	*pd = postdiv_reg;
261*296b5902SFlora Fu 	*dds = pcw_val | RG_PLL_SDM_PCW_CHG;
262*296b5902SFlora Fu 
263*296b5902SFlora Fu 	return 0;
264*296b5902SFlora Fu }
265*296b5902SFlora Fu 
266*296b5902SFlora Fu /**
267*296b5902SFlora Fu  * _pll_en() - enable/disable RG_PLL_EN of CON1 for pll[pll_idx]
268*296b5902SFlora Fu  * @pll_idx: Which PLL to enable/disable
269*296b5902SFlora Fu  * @on: 1 -> enable, 0 -> disable.
270*296b5902SFlora Fu  *
271*296b5902SFlora Fu  * This funciton will only change RG_PLL_EN of CON1 for pll[pll_idx].
272*296b5902SFlora Fu  *
273*296b5902SFlora Fu  * Context: Any context.
274*296b5902SFlora Fu  */
275*296b5902SFlora Fu static void _pll_en(uint32_t pll_idx, bool on)
276*296b5902SFlora Fu {
277*296b5902SFlora Fu 	if (on) {
278*296b5902SFlora Fu 		apupwr_setbits(RG_PLL_EN, mixed_con0_addr[pll_idx]);
279*296b5902SFlora Fu 	} else {
280*296b5902SFlora Fu 		apupwr_clrbits(RG_PLL_EN, mixed_con0_addr[pll_idx]);
281*296b5902SFlora Fu 	}
282*296b5902SFlora Fu }
283*296b5902SFlora Fu 
284*296b5902SFlora Fu /**
285*296b5902SFlora Fu  * _pll_pwr() - enable/disable PLL_SDM_PWR_ON of CON3 for pll[pll_idx]
286*296b5902SFlora Fu  * @pll_idx: Which PLL to enable/disable
287*296b5902SFlora Fu  * @on: 1 -> enable, 0 -> disable.
288*296b5902SFlora Fu  *
289*296b5902SFlora Fu  * This funciton will only change PLL_SDM_PWR_ON of CON3 for pll[pll_idx].
290*296b5902SFlora Fu  *
291*296b5902SFlora Fu  * Context: Any context.
292*296b5902SFlora Fu  */
293*296b5902SFlora Fu static void _pll_pwr(uint32_t pll_idx, bool on)
294*296b5902SFlora Fu {
295*296b5902SFlora Fu 	if (on) {
296*296b5902SFlora Fu 		apupwr_setbits(DA_PLL_SDM_PWR_ON, mixed_con3_addr[pll_idx]);
297*296b5902SFlora Fu 	} else {
298*296b5902SFlora Fu 		apupwr_clrbits(DA_PLL_SDM_PWR_ON, mixed_con3_addr[pll_idx]);
299*296b5902SFlora Fu 	}
300*296b5902SFlora Fu }
301*296b5902SFlora Fu 
302*296b5902SFlora Fu /**
303*296b5902SFlora Fu  * _pll_iso() - enable/disable PLL_SDM_ISO_EN of CON3 for pll[pll_idx]
304*296b5902SFlora Fu  * @pll_idx: Which PLL to enable/disable
305*296b5902SFlora Fu  * @enable: 1 -> turn on isolation, 0 -> turn off isolation.
306*296b5902SFlora Fu  *
307*296b5902SFlora Fu  * This funciton will turn on/off pll isolation by
308*296b5902SFlora Fu  * changing PLL_SDM_PWR_ON of CON3 for pll[pll_idx].
309*296b5902SFlora Fu  *
310*296b5902SFlora Fu  * Context: Any context.
311*296b5902SFlora Fu  */
312*296b5902SFlora Fu static void _pll_iso(uint32_t pll_idx, bool enable)
313*296b5902SFlora Fu {
314*296b5902SFlora Fu 	if (enable) {
315*296b5902SFlora Fu 		apupwr_setbits(DA_PLL_SDM_ISO_EN, mixed_con3_addr[pll_idx]);
316*296b5902SFlora Fu 	} else {
317*296b5902SFlora Fu 		apupwr_clrbits(DA_PLL_SDM_ISO_EN, mixed_con3_addr[pll_idx]);
318*296b5902SFlora Fu 	}
319*296b5902SFlora Fu }
320*296b5902SFlora Fu 
321*296b5902SFlora Fu /**
322*296b5902SFlora Fu  * _pll_switch() - entry point to turn whole PLL on/off
323*296b5902SFlora Fu  * @pll_idx: Which PLL to enable/disable
324*296b5902SFlora Fu  * @on: 1 -> enable, 0 -> disable.
325*296b5902SFlora Fu  * @fhctl_en: enable or disable fhctl function
326*296b5902SFlora Fu  *
327*296b5902SFlora Fu  * This is the entry poing for controlling pll and fhctl funciton on/off.
328*296b5902SFlora Fu  * Caller can chose only enable pll instead of fhctl function.
329*296b5902SFlora Fu  *
330*296b5902SFlora Fu  * Context: Any context.
331*296b5902SFlora Fu  * Return:
332*296b5902SFlora Fu  * * 0		- done for enable pll or fhctl as well.
333*296b5902SFlora Fu  */
334*296b5902SFlora Fu static int32_t _pll_switch(uint32_t pll_idx, bool on, bool fhctl_en)
335*296b5902SFlora Fu {
336*296b5902SFlora Fu 	int32_t ret = 0;
337*296b5902SFlora Fu 
338*296b5902SFlora Fu 	if (pll_idx >= APUPLL_MAX) {
339*296b5902SFlora Fu 		ERROR("%s wrong pll_idx: %d\n", __func__, pll_idx);
340*296b5902SFlora Fu 		ret = -EINVAL;
341*296b5902SFlora Fu 		goto err;
342*296b5902SFlora Fu 	}
343*296b5902SFlora Fu 
344*296b5902SFlora Fu 	if (on) {
345*296b5902SFlora Fu 		_pll_pwr(pll_idx, true);
346*296b5902SFlora Fu 		udelay(PLL_CMD_READY_TIME_1US);
347*296b5902SFlora Fu 		_pll_iso(pll_idx, false);
348*296b5902SFlora Fu 		udelay(PLL_CMD_READY_TIME_1US);
349*296b5902SFlora Fu 		_pll_en(pll_idx, true);
350*296b5902SFlora Fu 		udelay(PLL_READY_TIME_20US);
351*296b5902SFlora Fu 	} else {
352*296b5902SFlora Fu 		_pll_en(pll_idx, false);
353*296b5902SFlora Fu 		_pll_iso(pll_idx, true);
354*296b5902SFlora Fu 		_pll_pwr(pll_idx, false);
355*296b5902SFlora Fu 	}
356*296b5902SFlora Fu 
357*296b5902SFlora Fu err:
358*296b5902SFlora Fu 	return ret;
359*296b5902SFlora Fu }
360*296b5902SFlora Fu 
361*296b5902SFlora Fu /**
362*296b5902SFlora Fu  * apu_pll_enable() - API for smc function to enable/disable pll
363*296b5902SFlora Fu  * @pll_idx: Which pll to enable/disable.
364*296b5902SFlora Fu  * @enable: 1 -> enable, 0 -> disable.
365*296b5902SFlora Fu  * @fhctl_en: enable or disable fhctl function
366*296b5902SFlora Fu  *
367*296b5902SFlora Fu  * pll_idx[0] --> APUPLL (MDLA0/1)
368*296b5902SFlora Fu  * pll_idx[1] --> NPUPLL (VPU0/1)
369*296b5902SFlora Fu  * pll_idx[2] --> APUPLL1(CONN)
370*296b5902SFlora Fu  * pll_idx[3] --> APUPLL2(IOMMU)
371*296b5902SFlora Fu  * The differences between _pll_switch are:
372*296b5902SFlora Fu  * 1. Atomic update pll reference cnt to protect double enable pll &
373*296b5902SFlora Fu  * close pll during user is not zero.
374*296b5902SFlora Fu  *
375*296b5902SFlora Fu  * Context: Any context.
376*296b5902SFlora Fu  * Return:
377*296b5902SFlora Fu  * * 0 - done for enable pll or fhctl as well.
378*296b5902SFlora Fu  */
379*296b5902SFlora Fu int32_t apu_pll_enable(int32_t pll_idx, bool enable, bool fhctl_en)
380*296b5902SFlora Fu {
381*296b5902SFlora Fu 	int32_t ret = 0;
382*296b5902SFlora Fu 
383*296b5902SFlora Fu 	if (pll_idx >= APUPLL_MAX) {
384*296b5902SFlora Fu 		ERROR("%s wrong pll_idx: %d\n", __func__, pll_idx);
385*296b5902SFlora Fu 		ret = -EINVAL;
386*296b5902SFlora Fu 		goto err;
387*296b5902SFlora Fu 	}
388*296b5902SFlora Fu 
389*296b5902SFlora Fu 	if (enable) {
390*296b5902SFlora Fu 		switch (pll_idx) {
391*296b5902SFlora Fu 		case APUPLL:
392*296b5902SFlora Fu 			spin_lock(&apupll_lock);
393*296b5902SFlora Fu 			if (pll_cnt[APUPLL] == 0) {
394*296b5902SFlora Fu 				_pll_switch(pll_idx, enable, fhctl_en);
395*296b5902SFlora Fu 			}
396*296b5902SFlora Fu 			pll_cnt[APUPLL]++;
397*296b5902SFlora Fu 			spin_unlock(&apupll_lock);
398*296b5902SFlora Fu 			break;
399*296b5902SFlora Fu 		case NPUPLL:
400*296b5902SFlora Fu 			spin_lock(&npupll_lock);
401*296b5902SFlora Fu 			if (pll_cnt[NPUPLL] == 0) {
402*296b5902SFlora Fu 				_pll_switch(pll_idx, enable, fhctl_en);
403*296b5902SFlora Fu 			}
404*296b5902SFlora Fu 			pll_cnt[NPUPLL]++;
405*296b5902SFlora Fu 			spin_unlock(&npupll_lock);
406*296b5902SFlora Fu 			break;
407*296b5902SFlora Fu 		case APUPLL1:
408*296b5902SFlora Fu 			spin_lock(&apupll_1_lock);
409*296b5902SFlora Fu 			if (pll_cnt[APUPLL1] == 0) {
410*296b5902SFlora Fu 				_pll_switch(pll_idx, enable, fhctl_en);
411*296b5902SFlora Fu 			}
412*296b5902SFlora Fu 			pll_cnt[APUPLL1]++;
413*296b5902SFlora Fu 			spin_unlock(&apupll_1_lock);
414*296b5902SFlora Fu 			break;
415*296b5902SFlora Fu 		case APUPLL2:
416*296b5902SFlora Fu 			spin_lock(&apupll_2_lock);
417*296b5902SFlora Fu 			if (pll_cnt[APUPLL2] == 0) {
418*296b5902SFlora Fu 				_pll_switch(pll_idx, enable, fhctl_en);
419*296b5902SFlora Fu 			}
420*296b5902SFlora Fu 			pll_cnt[APUPLL2]++;
421*296b5902SFlora Fu 			spin_unlock(&apupll_2_lock);
422*296b5902SFlora Fu 			break;
423*296b5902SFlora Fu 		default:
424*296b5902SFlora Fu 			ERROR("%s invalid pll_idx: %d\n", __func__, pll_idx);
425*296b5902SFlora Fu 			ret = -EINVAL;
426*296b5902SFlora Fu 			break;
427*296b5902SFlora Fu 		}
428*296b5902SFlora Fu 	} else {
429*296b5902SFlora Fu 		switch (pll_idx) {
430*296b5902SFlora Fu 		case APUPLL:
431*296b5902SFlora Fu 			spin_lock(&apupll_lock);
432*296b5902SFlora Fu 			if (pll_cnt[APUPLL]) {
433*296b5902SFlora Fu 				pll_cnt[APUPLL]--;
434*296b5902SFlora Fu 			}
435*296b5902SFlora Fu 			if (pll_cnt[APUPLL] == 0) {
436*296b5902SFlora Fu 				_pll_switch(pll_idx, enable, fhctl_en);
437*296b5902SFlora Fu 			}
438*296b5902SFlora Fu 			spin_unlock(&apupll_lock);
439*296b5902SFlora Fu 			break;
440*296b5902SFlora Fu 		case NPUPLL:
441*296b5902SFlora Fu 			spin_lock(&npupll_lock);
442*296b5902SFlora Fu 			if (pll_cnt[NPUPLL]) {
443*296b5902SFlora Fu 				pll_cnt[NPUPLL]--;
444*296b5902SFlora Fu 			}
445*296b5902SFlora Fu 			if (pll_cnt[NPUPLL] == 0) {
446*296b5902SFlora Fu 				_pll_switch(pll_idx, enable, fhctl_en);
447*296b5902SFlora Fu 			}
448*296b5902SFlora Fu 			spin_unlock(&npupll_lock);
449*296b5902SFlora Fu 			break;
450*296b5902SFlora Fu 		case APUPLL1:
451*296b5902SFlora Fu 			spin_lock(&apupll_1_lock);
452*296b5902SFlora Fu 			if (pll_cnt[APUPLL1]) {
453*296b5902SFlora Fu 				pll_cnt[APUPLL1]--;
454*296b5902SFlora Fu 			}
455*296b5902SFlora Fu 			if (pll_cnt[APUPLL1] == 0) {
456*296b5902SFlora Fu 				_pll_switch(pll_idx, enable, fhctl_en);
457*296b5902SFlora Fu 			}
458*296b5902SFlora Fu 			spin_unlock(&apupll_1_lock);
459*296b5902SFlora Fu 			break;
460*296b5902SFlora Fu 		case APUPLL2:
461*296b5902SFlora Fu 			spin_lock(&apupll_2_lock);
462*296b5902SFlora Fu 			if (pll_cnt[APUPLL2]) {
463*296b5902SFlora Fu 				pll_cnt[APUPLL2]--;
464*296b5902SFlora Fu 			}
465*296b5902SFlora Fu 			if (pll_cnt[APUPLL2] == 0) {
466*296b5902SFlora Fu 				_pll_switch(pll_idx, enable, fhctl_en);
467*296b5902SFlora Fu 			}
468*296b5902SFlora Fu 			spin_unlock(&apupll_2_lock);
469*296b5902SFlora Fu 			break;
470*296b5902SFlora Fu 		default:
471*296b5902SFlora Fu 			ERROR("%s invalid pll_idx: %d\n", __func__, pll_idx);
472*296b5902SFlora Fu 			ret = -EINVAL;
473*296b5902SFlora Fu 			break;
474*296b5902SFlora Fu 		}
475*296b5902SFlora Fu 	}
476*296b5902SFlora Fu 
477*296b5902SFlora Fu err:
478*296b5902SFlora Fu 	return ret;
479*296b5902SFlora Fu }
480*296b5902SFlora Fu 
481*296b5902SFlora Fu /**
482*296b5902SFlora Fu  * anpu_pll_set_rate() - API for smc function to set rate of voltage domain.
483*296b5902SFlora Fu  * @domain: Which pll of correspond voltage domain to change rate.
484*296b5902SFlora Fu  * @mode: which mode to use when set_rate
485*296b5902SFlora Fu  * @freq: which frequency to set.
486*296b5902SFlora Fu  *
487*296b5902SFlora Fu  * For V_VPU0/1, it will only allow 1 of them to modify NPUPLL
488*296b5902SFlora Fu  * such that there will be no race condition happen.
489*296b5902SFlora Fu  *
490*296b5902SFlora Fu  * For V_MDLA0/1, it will only allow 1 of them to modify APUPLL1
491*296b5902SFlora Fu  * such that there will be no race condition happen.
492*296b5902SFlora Fu  *
493*296b5902SFlora Fu  * There are 3 kinds of modes to set pll's rate.
494*296b5902SFlora Fu  * 1. pure sw mode: (CON0_PCW)
495*296b5902SFlora Fu  *        fhctl function is off and change rate by programming CON1_PCW.
496*296b5902SFlora Fu  * 2. fhctl sw mode: (FHCTL_SW)
497*296b5902SFlora Fu  *        fhctl function is on and change rate by programming fhctl_dds.
498*296b5902SFlora Fu  *        (post dividor is still need to program CON1_PCW)
499*296b5902SFlora Fu  * 3. fhctl hw mode: (FHCTL_HW)
500*296b5902SFlora Fu  *        fhctl function is on and change rate by programming fhctl_dvfs.
501*296b5902SFlora Fu  *        (post dividor is still need to program CON1_PCW)
502*296b5902SFlora Fu  *
503*296b5902SFlora Fu  * Context: Any context.
504*296b5902SFlora Fu  * Return:
505*296b5902SFlora Fu  * * 0 - done for set rate of voltage domain.
506*296b5902SFlora Fu  */
507*296b5902SFlora Fu int32_t anpu_pll_set_rate(enum dvfs_voltage_domain domain,
508*296b5902SFlora Fu 		      enum pll_set_rate_mode mode, int32_t freq)
509*296b5902SFlora Fu {
510*296b5902SFlora Fu 	uint32_t pd, old_pd, dds;
511*296b5902SFlora Fu 	int32_t pll_idx, ret = 0;
512*296b5902SFlora Fu 
513*296b5902SFlora Fu 	pll_idx = vd2pllidx(domain);
514*296b5902SFlora Fu 	if (pll_idx < 0) {
515*296b5902SFlora Fu 		ret = pll_idx;
516*296b5902SFlora Fu 		goto err;
517*296b5902SFlora Fu 	}
518*296b5902SFlora Fu 
519*296b5902SFlora Fu 	_cal_pll_data(&pd, &dds, freq / 1000);
520*296b5902SFlora Fu 
521*296b5902SFlora Fu 	INFO("%s %s new post_div=%d, target dds=0x%08x(%dMhz) mode = %d\n",
522*296b5902SFlora Fu 	     __func__, pllidx2name(pll_idx), pd, dds, freq / 1000, mode);
523*296b5902SFlora Fu 
524*296b5902SFlora Fu 	/* spin_lock for NPULL, since vpu0/1 share npupll */
525*296b5902SFlora Fu 	if (domain == V_VPU0 || domain == V_VPU1) {
526*296b5902SFlora Fu 		spin_lock(&npupll_lock);
527*296b5902SFlora Fu 	}
528*296b5902SFlora Fu 
529*296b5902SFlora Fu 	/* spin_lock for APUPLL, since mdla0/1 shate apupll */
530*296b5902SFlora Fu 	if (domain == V_MDLA0 || domain == V_MDLA1) {
531*296b5902SFlora Fu 		spin_lock(&apupll_lock);
532*296b5902SFlora Fu 	}
533*296b5902SFlora Fu 
534*296b5902SFlora Fu 	switch (mode) {
535*296b5902SFlora Fu 	case CON0_PCW:
536*296b5902SFlora Fu 		pd = RG_PLL_SDM_PCW_CHG |
537*296b5902SFlora Fu 		     (pd & POSDIV_MASK) << POSDIV_SHIFT | dds;
538*296b5902SFlora Fu 		apupwr_writel(pd, mixed_con1_addr[pll_idx]);
539*296b5902SFlora Fu 		udelay(PLL_READY_TIME_20US);
540*296b5902SFlora Fu 		break;
541*296b5902SFlora Fu 	case FHCTL_SW:
542*296b5902SFlora Fu 		/* pll con0 disable */
543*296b5902SFlora Fu 		_pll_en(pll_idx, false);
544*296b5902SFlora Fu 		apupwr_writel(dds, fhctl_dds_addr[pll_idx]);
545*296b5902SFlora Fu 		_set_postdiv_reg(pll_idx, pd);
546*296b5902SFlora Fu 		apupwr_setbits(PLL_TGL_ORG, fhctl_dds_addr[pll_idx]);
547*296b5902SFlora Fu 		udelay(PLL_CMD_READY_TIME_1US);
548*296b5902SFlora Fu 		/* pll con0 enable */
549*296b5902SFlora Fu 		_pll_en(pll_idx, true);
550*296b5902SFlora Fu 		udelay(PLL_READY_TIME_20US);
551*296b5902SFlora Fu 		break;
552*296b5902SFlora Fu 	case FHCTL_HW:
553*296b5902SFlora Fu 		old_pd = _pll_get_postdiv_reg(pll_idx);
554*296b5902SFlora Fu 		if (pd > old_pd) {
555*296b5902SFlora Fu 			_set_postdiv_reg(pll_idx, pd);
556*296b5902SFlora Fu 			apupwr_writel(dds, fhctl_dvfs_addr[pll_idx]);
557*296b5902SFlora Fu 		} else {
558*296b5902SFlora Fu 			apupwr_writel(dds, fhctl_dvfs_addr[pll_idx]);
559*296b5902SFlora Fu 			_set_postdiv_reg(pll_idx, pd);
560*296b5902SFlora Fu 		}
561*296b5902SFlora Fu 		ret = _fhctl_mon_done(pll_idx, dds);
562*296b5902SFlora Fu 		break;
563*296b5902SFlora Fu 	default:
564*296b5902SFlora Fu 		ERROR("%s input wrong mode: %d\n", __func__, mode);
565*296b5902SFlora Fu 		ret = -EINVAL;
566*296b5902SFlora Fu 		break;
567*296b5902SFlora Fu 	}
568*296b5902SFlora Fu 
569*296b5902SFlora Fu 	/* spin_lock for NPULL, since vpu0/1 share npupll */
570*296b5902SFlora Fu 	if (domain == V_VPU0 || domain == V_VPU1) {
571*296b5902SFlora Fu 		spin_unlock(&npupll_lock);
572*296b5902SFlora Fu 	}
573*296b5902SFlora Fu 
574*296b5902SFlora Fu 	/* spin_lock for APUPLL, since mdla0/1 share apupll */
575*296b5902SFlora Fu 	if (domain == V_MDLA0 || domain == V_MDLA1) {
576*296b5902SFlora Fu 		spin_unlock(&apupll_lock);
577*296b5902SFlora Fu 	}
578*296b5902SFlora Fu 
579*296b5902SFlora Fu err:
580*296b5902SFlora Fu 	return ret;
581*296b5902SFlora Fu }
582