apupll.c 14 KB

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