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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 function will only change RG_PLL_EN of CON1 for pll[pll_idx]. 272 * 273 * Context: Any context. 274 */ 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 function will only change PLL_SDM_PWR_ON of CON3 for pll[pll_idx]. 290 * 291 * Context: Any context. 292 */ 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 function 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 */ 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 function 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 */ 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 */ 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 */ 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