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