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