1296b5902SFlora Fu /*
2296b5902SFlora Fu * Copyright (c) 2021, ARM Limited and Contributors. All rights reserved.
3296b5902SFlora Fu *
4296b5902SFlora Fu * SPDX-License-Identifier: BSD-3-Clause
5296b5902SFlora Fu */
6296b5902SFlora Fu
7296b5902SFlora Fu #include <errno.h>
8296b5902SFlora Fu
9296b5902SFlora Fu #include <arch_helpers.h>
10296b5902SFlora Fu #include <common/debug.h>
11296b5902SFlora Fu #include <drivers/delay_timer.h>
12296b5902SFlora Fu #include <lib/spinlock.h>
13296b5902SFlora Fu
14296b5902SFlora Fu #include <apupwr_clkctl.h>
15296b5902SFlora Fu #include <apupwr_clkctl_def.h>
16296b5902SFlora Fu #include <mtk_plat_common.h>
17296b5902SFlora Fu #include <platform_def.h>
18296b5902SFlora Fu
19296b5902SFlora Fu uint32_t mixed_con0_addr[APUPLL_MAX] = {
20296b5902SFlora Fu APU_PLL4H_PLL1_CON0,
21296b5902SFlora Fu APU_PLL4H_PLL2_CON0,
22296b5902SFlora Fu APU_PLL4H_PLL3_CON0,
23296b5902SFlora Fu APU_PLL4H_PLL4_CON0,
24296b5902SFlora Fu };
25296b5902SFlora Fu
26296b5902SFlora Fu uint32_t mixed_con1_addr[APUPLL_MAX] = {
27296b5902SFlora Fu APU_PLL4H_PLL1_CON1,
28296b5902SFlora Fu APU_PLL4H_PLL2_CON1,
29296b5902SFlora Fu APU_PLL4H_PLL3_CON1,
30296b5902SFlora Fu APU_PLL4H_PLL4_CON1,
31296b5902SFlora Fu };
32296b5902SFlora Fu
33296b5902SFlora Fu uint32_t mixed_con3_addr[APUPLL_MAX] = {
34296b5902SFlora Fu APU_PLL4H_PLL1_CON3,
35296b5902SFlora Fu APU_PLL4H_PLL2_CON3,
36296b5902SFlora Fu APU_PLL4H_PLL3_CON3,
37296b5902SFlora Fu APU_PLL4H_PLL4_CON3,
38296b5902SFlora Fu };
39296b5902SFlora Fu
40296b5902SFlora Fu uint32_t fhctl_dds_addr[APUPLL_MAX] = {
41296b5902SFlora Fu APU_PLL4H_FHCTL0_DDS,
42296b5902SFlora Fu APU_PLL4H_FHCTL1_DDS,
43296b5902SFlora Fu APU_PLL4H_FHCTL2_DDS,
44296b5902SFlora Fu APU_PLL4H_FHCTL3_DDS,
45296b5902SFlora Fu };
46296b5902SFlora Fu
47296b5902SFlora Fu uint32_t fhctl_dvfs_addr[APUPLL_MAX] = {
48296b5902SFlora Fu APU_PLL4H_FHCTL0_DVFS,
49296b5902SFlora Fu APU_PLL4H_FHCTL1_DVFS,
50296b5902SFlora Fu APU_PLL4H_FHCTL2_DVFS,
51296b5902SFlora Fu APU_PLL4H_FHCTL3_DVFS,
52296b5902SFlora Fu };
53296b5902SFlora Fu
54296b5902SFlora Fu uint32_t fhctl_mon_addr[APUPLL_MAX] = {
55296b5902SFlora Fu APU_PLL4H_FHCTL0_MON,
56296b5902SFlora Fu APU_PLL4H_FHCTL1_MON,
57296b5902SFlora Fu APU_PLL4H_FHCTL2_MON,
58296b5902SFlora Fu APU_PLL4H_FHCTL3_MON,
59296b5902SFlora Fu };
60296b5902SFlora Fu
61296b5902SFlora Fu uint32_t fhctl_cfg_addr[APUPLL_MAX] = {
62296b5902SFlora Fu APU_PLL4H_FHCTL0_CFG,
63296b5902SFlora Fu APU_PLL4H_FHCTL1_CFG,
64296b5902SFlora Fu APU_PLL4H_FHCTL2_CFG,
65296b5902SFlora Fu APU_PLL4H_FHCTL3_CFG,
66296b5902SFlora Fu };
67296b5902SFlora Fu
68296b5902SFlora Fu static spinlock_t apupll_lock;
69296b5902SFlora Fu static spinlock_t npupll_lock;
70296b5902SFlora Fu static spinlock_t apupll_1_lock;
71296b5902SFlora Fu static spinlock_t apupll_2_lock;
72296b5902SFlora Fu static uint32_t pll_cnt[APUPLL_MAX];
73296b5902SFlora Fu /**
74296b5902SFlora Fu * vd2pllidx() - voltage domain to pll idx.
75296b5902SFlora Fu * @domain: the voltage domain for getting pll index.
76296b5902SFlora Fu *
77296b5902SFlora Fu * Caller will get correspond pll index by different voltage domain.
78296b5902SFlora Fu * pll_idx[0] --> APUPLL (MDLA0/1)
79296b5902SFlora Fu * pll_idx[1] --> NPUPLL (VPU0/1)
80296b5902SFlora Fu * pll_idx[2] --> APUPLL1(CONN)
81296b5902SFlora Fu * pll_idx[3] --> APUPLL2(IOMMU)
82296b5902SFlora Fu * The longer description may have multiple paragraphs.
83296b5902SFlora Fu *
84296b5902SFlora Fu * Context: Any context.
85296b5902SFlora Fu * Return:
86296b5902SFlora Fu * * 0 ~ 3 - return the corresponding pll index
87296b5902SFlora Fu * * -EEXIST - cannot find pll idex of the specific voltage domain
88296b5902SFlora Fu *
89296b5902SFlora Fu */
vd2pllidx(enum dvfs_voltage_domain domain)90296b5902SFlora Fu static int32_t vd2pllidx(enum dvfs_voltage_domain domain)
91296b5902SFlora Fu {
92296b5902SFlora Fu int32_t ret;
93296b5902SFlora Fu
94296b5902SFlora Fu switch (domain) {
95296b5902SFlora Fu case V_VPU0:
96296b5902SFlora Fu case V_VPU1:
97296b5902SFlora Fu ret = NPUPLL;
98296b5902SFlora Fu break;
99296b5902SFlora Fu case V_MDLA0:
100296b5902SFlora Fu case V_MDLA1:
101296b5902SFlora Fu ret = APUPLL;
102296b5902SFlora Fu break;
103296b5902SFlora Fu case V_TOP_IOMMU:
104296b5902SFlora Fu ret = APUPLL2;
105296b5902SFlora Fu break;
106296b5902SFlora Fu case V_APU_CONN:
107296b5902SFlora Fu ret = APUPLL1;
108296b5902SFlora Fu break;
109296b5902SFlora Fu default:
110296b5902SFlora Fu ERROR("%s wrong voltage domain: %d\n", __func__, domain);
111296b5902SFlora Fu ret = -EEXIST; /* non-exist */
112296b5902SFlora Fu break;
113296b5902SFlora Fu }
114296b5902SFlora Fu
115296b5902SFlora Fu return ret;
116296b5902SFlora Fu }
117296b5902SFlora Fu
118296b5902SFlora Fu /**
119296b5902SFlora Fu * pllidx2name() - return names of specific pll index.
120296b5902SFlora Fu * @pll_idx: input for specific pll index.
121296b5902SFlora Fu *
122296b5902SFlora Fu * Given pll index, this function will return name of it.
123296b5902SFlora Fu *
124296b5902SFlora Fu * Context: Any context.
125296b5902SFlora Fu * Return: Names of pll_idx, if found, otherwise will return "NULL"
126296b5902SFlora Fu */
pllidx2name(int32_t pll_idx)127296b5902SFlora Fu static const char *pllidx2name(int32_t pll_idx)
128296b5902SFlora Fu {
129296b5902SFlora Fu static const char *const names[] = {
130296b5902SFlora Fu [APUPLL] = "PLL4H_PLL1",
131296b5902SFlora Fu [NPUPLL] = "PLL4H_PLL2",
132296b5902SFlora Fu [APUPLL1] = "PLL4H_PLL3",
133296b5902SFlora Fu [APUPLL2] = "PLL4H_PLL4",
134296b5902SFlora Fu [APUPLL_MAX] = "NULL",
135296b5902SFlora Fu };
136296b5902SFlora Fu
137296b5902SFlora Fu if (pll_idx >= APUPLL_MAX) {
138296b5902SFlora Fu pll_idx = APUPLL_MAX;
139296b5902SFlora Fu }
140296b5902SFlora Fu
141296b5902SFlora Fu return names[pll_idx];
142296b5902SFlora Fu }
143296b5902SFlora Fu
144296b5902SFlora Fu /**
145296b5902SFlora Fu * _fhctl_mon_done() - poll whether fhctl HW mode is done.
146296b5902SFlora Fu * @pll_idx: input for specific pll index.
147296b5902SFlora Fu * @tar_dds: target dds for fhctl_mon to be.
148296b5902SFlora Fu *
149296b5902SFlora Fu * Given pll index, this function will continue to poll whether fhctl_mon
150296b5902SFlora Fu * has reached the expected value within 80us.
151296b5902SFlora Fu *
152296b5902SFlora Fu * Context: Any context.
153296b5902SFlora Fu * Return:
154296b5902SFlora Fu * * 0 - OK for fhctl_mon == tar_dds
155296b5902SFlora Fu * * -ETIMEDOUT - fhctl_mon not reach tar_dds
156296b5902SFlora Fu */
_fhctl_mon_done(uint32_t pll_idx,unsigned long tar_dds)157296b5902SFlora Fu static int32_t _fhctl_mon_done(uint32_t pll_idx, unsigned long tar_dds)
158296b5902SFlora Fu {
159296b5902SFlora Fu unsigned long mon_dds;
160296b5902SFlora Fu uint64_t timeout = timeout_init_us(PLL_READY_TIME_20US);
161296b5902SFlora Fu int32_t ret = 0;
162296b5902SFlora Fu
163296b5902SFlora Fu tar_dds &= DDS_MASK;
164296b5902SFlora Fu do {
165296b5902SFlora Fu mon_dds = apupwr_readl(fhctl_mon_addr[pll_idx]) & DDS_MASK;
166296b5902SFlora Fu if (mon_dds == tar_dds) {
167296b5902SFlora Fu break;
168296b5902SFlora Fu }
169296b5902SFlora Fu
170296b5902SFlora Fu if (timeout_elapsed(timeout)) {
171296b5902SFlora Fu ERROR("%s monitor DDS 0x%08lx != expect 0x%08lx\n",
172296b5902SFlora Fu pllidx2name(pll_idx), mon_dds, tar_dds);
173296b5902SFlora Fu ret = -ETIMEDOUT;
174296b5902SFlora Fu break;
175296b5902SFlora Fu }
176296b5902SFlora Fu } while (mon_dds != tar_dds);
177296b5902SFlora Fu
178296b5902SFlora Fu return ret;
179296b5902SFlora Fu }
180296b5902SFlora Fu
181296b5902SFlora Fu /**
182296b5902SFlora Fu * _pll_get_postdiv_reg() - return current post dividor of pll_idx
183296b5902SFlora Fu * @pll_idx: input for specific pll index.
184296b5902SFlora Fu *
185296b5902SFlora Fu * Given pll index, this function will return its current post dividor.
186296b5902SFlora Fu *
187296b5902SFlora Fu * Context: Any context.
188296b5902SFlora Fu * Return: post dividor of current pll_idx.
189296b5902SFlora Fu *
190296b5902SFlora Fu */
_pll_get_postdiv_reg(uint32_t pll_idx)191296b5902SFlora Fu static uint32_t _pll_get_postdiv_reg(uint32_t pll_idx)
192296b5902SFlora Fu {
193296b5902SFlora Fu int32_t pll_postdiv_reg = 0;
194296b5902SFlora Fu uint32_t val;
195296b5902SFlora Fu
196296b5902SFlora Fu val = apupwr_readl(mixed_con1_addr[pll_idx]);
197296b5902SFlora Fu pll_postdiv_reg = (val >> POSDIV_SHIFT) & POSDIV_MASK;
198296b5902SFlora Fu return pll_postdiv_reg;
199296b5902SFlora Fu }
200296b5902SFlora Fu
201296b5902SFlora Fu /**
202296b5902SFlora Fu * _set_postdiv_reg() - set pll_idx's post dividor.
203296b5902SFlora Fu * @pll_idx: Which PLL to enable/disable
204296b5902SFlora Fu * @post_div: the register value of post dividor to be wrtten.
205296b5902SFlora Fu *
206296b5902SFlora Fu * Below are lists of post dividor register value and its meaning:
207296b5902SFlora Fu * [31] APUPLL_SDM_PCW_CHG
208296b5902SFlora Fu * [26:24] APUPLL_POSDIV
209296b5902SFlora Fu * [21:0] APUPLL_SDM_PCW (8bit integer + 14bit fraction)
210296b5902SFlora Fu * expected freq range ----- divider-------post divider in reg:
211296b5902SFlora Fu * >1500M (1500/ 1) -> 1 -> 0(2 to the zero power)
212296b5902SFlora Fu * > 750M (1500/ 2) -> 2 -> 1(2 to the 1st power)
213296b5902SFlora Fu * > 375M (1500/ 4) -> 4 -> 2(2 to the 2nd power)
214296b5902SFlora Fu * > 187.5M (1500/ 8) -> 8 -> 3(2 to the 3rd power)
215296b5902SFlora Fu * > 93.75M (1500/16) -> 16 -> 4(2 to the 4th power)
216296b5902SFlora Fu *
217296b5902SFlora Fu * Context: Any context.
218296b5902SFlora Fu */
_set_postdiv_reg(uint32_t pll_idx,uint32_t post_div)219296b5902SFlora Fu static void _set_postdiv_reg(uint32_t pll_idx, uint32_t post_div)
220296b5902SFlora Fu {
221296b5902SFlora Fu apupwr_clrbits(POSDIV_MASK << POSDIV_SHIFT, mixed_con1_addr[pll_idx]);
222296b5902SFlora Fu apupwr_setbits((post_div & POSDIV_MASK) << POSDIV_SHIFT,
223296b5902SFlora Fu mixed_con1_addr[pll_idx]);
224296b5902SFlora Fu }
225296b5902SFlora Fu
226296b5902SFlora Fu /**
227296b5902SFlora Fu * _cal_pll_data() - input freq, calculate correspond post dividor and dds.
228296b5902SFlora Fu * @pd: address of output post dividor.
229296b5902SFlora Fu * @dds: address of output dds.
230296b5902SFlora Fu * @freq: input frequency.
231296b5902SFlora Fu *
232296b5902SFlora Fu * Given freq, this function will calculate correspond post dividor and dds.
233296b5902SFlora Fu *
234296b5902SFlora Fu * Context: Any context.
235296b5902SFlora Fu * Return:
236296b5902SFlora Fu * * 0 - done for calculating post dividor and dds.
237296b5902SFlora Fu */
_cal_pll_data(uint32_t * pd,uint32_t * dds,uint32_t freq)238296b5902SFlora Fu static int32_t _cal_pll_data(uint32_t *pd, uint32_t *dds, uint32_t freq)
239296b5902SFlora Fu {
240296b5902SFlora Fu uint32_t vco, postdiv_val = 1, postdiv_reg = 0;
241296b5902SFlora Fu uint32_t pcw_val;
242296b5902SFlora Fu
243296b5902SFlora Fu vco = freq;
244296b5902SFlora Fu postdiv_val = 1;
245296b5902SFlora Fu postdiv_reg = 0;
246296b5902SFlora Fu while (vco <= FREQ_VCO_MIN) {
247296b5902SFlora Fu postdiv_val = postdiv_val << 1;
248296b5902SFlora Fu postdiv_reg = postdiv_reg + 1;
249296b5902SFlora Fu vco = vco << 1;
250296b5902SFlora Fu }
251296b5902SFlora Fu
252296b5902SFlora Fu pcw_val = vco * (1 << PCW_FRACTIONAL_SHIFT);
253296b5902SFlora Fu pcw_val = pcw_val / FREQ_FIN;
254296b5902SFlora Fu
255296b5902SFlora Fu if (postdiv_reg == 0) { /* Fvco * 2 with post_divider = 2 */
256296b5902SFlora Fu pcw_val = pcw_val * 2;
257296b5902SFlora Fu postdiv_val = postdiv_val << 1;
258296b5902SFlora Fu postdiv_reg = postdiv_reg + 1;
259296b5902SFlora Fu } /* Post divider is 1 is not available */
260296b5902SFlora Fu *pd = postdiv_reg;
261296b5902SFlora Fu *dds = pcw_val | RG_PLL_SDM_PCW_CHG;
262296b5902SFlora Fu
263296b5902SFlora Fu return 0;
264296b5902SFlora Fu }
265296b5902SFlora Fu
266296b5902SFlora Fu /**
267296b5902SFlora Fu * _pll_en() - enable/disable RG_PLL_EN of CON1 for pll[pll_idx]
268296b5902SFlora Fu * @pll_idx: Which PLL to enable/disable
269296b5902SFlora Fu * @on: 1 -> enable, 0 -> disable.
270296b5902SFlora Fu *
271*1b491eeaSElyes Haouas * This function will only change RG_PLL_EN of CON1 for pll[pll_idx].
272296b5902SFlora Fu *
273296b5902SFlora Fu * Context: Any context.
274296b5902SFlora Fu */
_pll_en(uint32_t pll_idx,bool on)275296b5902SFlora Fu static void _pll_en(uint32_t pll_idx, bool on)
276296b5902SFlora Fu {
277296b5902SFlora Fu if (on) {
278296b5902SFlora Fu apupwr_setbits(RG_PLL_EN, mixed_con0_addr[pll_idx]);
279296b5902SFlora Fu } else {
280296b5902SFlora Fu apupwr_clrbits(RG_PLL_EN, mixed_con0_addr[pll_idx]);
281296b5902SFlora Fu }
282296b5902SFlora Fu }
283296b5902SFlora Fu
284296b5902SFlora Fu /**
285296b5902SFlora Fu * _pll_pwr() - enable/disable PLL_SDM_PWR_ON of CON3 for pll[pll_idx]
286296b5902SFlora Fu * @pll_idx: Which PLL to enable/disable
287296b5902SFlora Fu * @on: 1 -> enable, 0 -> disable.
288296b5902SFlora Fu *
289*1b491eeaSElyes Haouas * This function will only change PLL_SDM_PWR_ON of CON3 for pll[pll_idx].
290296b5902SFlora Fu *
291296b5902SFlora Fu * Context: Any context.
292296b5902SFlora Fu */
_pll_pwr(uint32_t pll_idx,bool on)293296b5902SFlora Fu static void _pll_pwr(uint32_t pll_idx, bool on)
294296b5902SFlora Fu {
295296b5902SFlora Fu if (on) {
296296b5902SFlora Fu apupwr_setbits(DA_PLL_SDM_PWR_ON, mixed_con3_addr[pll_idx]);
297296b5902SFlora Fu } else {
298296b5902SFlora Fu apupwr_clrbits(DA_PLL_SDM_PWR_ON, mixed_con3_addr[pll_idx]);
299296b5902SFlora Fu }
300296b5902SFlora Fu }
301296b5902SFlora Fu
302296b5902SFlora Fu /**
303296b5902SFlora Fu * _pll_iso() - enable/disable PLL_SDM_ISO_EN of CON3 for pll[pll_idx]
304296b5902SFlora Fu * @pll_idx: Which PLL to enable/disable
305296b5902SFlora Fu * @enable: 1 -> turn on isolation, 0 -> turn off isolation.
306296b5902SFlora Fu *
307*1b491eeaSElyes Haouas * This function will turn on/off pll isolation by
308296b5902SFlora Fu * changing PLL_SDM_PWR_ON of CON3 for pll[pll_idx].
309296b5902SFlora Fu *
310296b5902SFlora Fu * Context: Any context.
311296b5902SFlora Fu */
_pll_iso(uint32_t pll_idx,bool enable)312296b5902SFlora Fu static void _pll_iso(uint32_t pll_idx, bool enable)
313296b5902SFlora Fu {
314296b5902SFlora Fu if (enable) {
315296b5902SFlora Fu apupwr_setbits(DA_PLL_SDM_ISO_EN, mixed_con3_addr[pll_idx]);
316296b5902SFlora Fu } else {
317296b5902SFlora Fu apupwr_clrbits(DA_PLL_SDM_ISO_EN, mixed_con3_addr[pll_idx]);
318296b5902SFlora Fu }
319296b5902SFlora Fu }
320296b5902SFlora Fu
321296b5902SFlora Fu /**
322296b5902SFlora Fu * _pll_switch() - entry point to turn whole PLL on/off
323296b5902SFlora Fu * @pll_idx: Which PLL to enable/disable
324296b5902SFlora Fu * @on: 1 -> enable, 0 -> disable.
325296b5902SFlora Fu * @fhctl_en: enable or disable fhctl function
326296b5902SFlora Fu *
327*1b491eeaSElyes Haouas * This is the entry poing for controlling pll and fhctl function on/off.
328296b5902SFlora Fu * Caller can chose only enable pll instead of fhctl function.
329296b5902SFlora Fu *
330296b5902SFlora Fu * Context: Any context.
331296b5902SFlora Fu * Return:
332296b5902SFlora Fu * * 0 - done for enable pll or fhctl as well.
333296b5902SFlora Fu */
_pll_switch(uint32_t pll_idx,bool on,bool fhctl_en)334296b5902SFlora Fu static int32_t _pll_switch(uint32_t pll_idx, bool on, bool fhctl_en)
335296b5902SFlora Fu {
336296b5902SFlora Fu int32_t ret = 0;
337296b5902SFlora Fu
338296b5902SFlora Fu if (pll_idx >= APUPLL_MAX) {
339296b5902SFlora Fu ERROR("%s wrong pll_idx: %d\n", __func__, pll_idx);
340296b5902SFlora Fu ret = -EINVAL;
341296b5902SFlora Fu goto err;
342296b5902SFlora Fu }
343296b5902SFlora Fu
344296b5902SFlora Fu if (on) {
345296b5902SFlora Fu _pll_pwr(pll_idx, true);
346296b5902SFlora Fu udelay(PLL_CMD_READY_TIME_1US);
347296b5902SFlora Fu _pll_iso(pll_idx, false);
348296b5902SFlora Fu udelay(PLL_CMD_READY_TIME_1US);
349296b5902SFlora Fu _pll_en(pll_idx, true);
350296b5902SFlora Fu udelay(PLL_READY_TIME_20US);
351296b5902SFlora Fu } else {
352296b5902SFlora Fu _pll_en(pll_idx, false);
353296b5902SFlora Fu _pll_iso(pll_idx, true);
354296b5902SFlora Fu _pll_pwr(pll_idx, false);
355296b5902SFlora Fu }
356296b5902SFlora Fu
357296b5902SFlora Fu err:
358296b5902SFlora Fu return ret;
359296b5902SFlora Fu }
360296b5902SFlora Fu
361296b5902SFlora Fu /**
362296b5902SFlora Fu * apu_pll_enable() - API for smc function to enable/disable pll
363296b5902SFlora Fu * @pll_idx: Which pll to enable/disable.
364296b5902SFlora Fu * @enable: 1 -> enable, 0 -> disable.
365296b5902SFlora Fu * @fhctl_en: enable or disable fhctl function
366296b5902SFlora Fu *
367296b5902SFlora Fu * pll_idx[0] --> APUPLL (MDLA0/1)
368296b5902SFlora Fu * pll_idx[1] --> NPUPLL (VPU0/1)
369296b5902SFlora Fu * pll_idx[2] --> APUPLL1(CONN)
370296b5902SFlora Fu * pll_idx[3] --> APUPLL2(IOMMU)
371296b5902SFlora Fu * The differences between _pll_switch are:
372296b5902SFlora Fu * 1. Atomic update pll reference cnt to protect double enable pll &
373296b5902SFlora Fu * close pll during user is not zero.
374296b5902SFlora Fu *
375296b5902SFlora Fu * Context: Any context.
376296b5902SFlora Fu * Return:
377296b5902SFlora Fu * * 0 - done for enable pll or fhctl as well.
378296b5902SFlora Fu */
apu_pll_enable(int32_t pll_idx,bool enable,bool fhctl_en)379296b5902SFlora Fu int32_t apu_pll_enable(int32_t pll_idx, bool enable, bool fhctl_en)
380296b5902SFlora Fu {
381296b5902SFlora Fu int32_t ret = 0;
382296b5902SFlora Fu
383296b5902SFlora Fu if (pll_idx >= APUPLL_MAX) {
384296b5902SFlora Fu ERROR("%s wrong pll_idx: %d\n", __func__, pll_idx);
385296b5902SFlora Fu ret = -EINVAL;
386296b5902SFlora Fu goto err;
387296b5902SFlora Fu }
388296b5902SFlora Fu
389296b5902SFlora Fu if (enable) {
390296b5902SFlora Fu switch (pll_idx) {
391296b5902SFlora Fu case APUPLL:
392296b5902SFlora Fu spin_lock(&apupll_lock);
393296b5902SFlora Fu if (pll_cnt[APUPLL] == 0) {
394296b5902SFlora Fu _pll_switch(pll_idx, enable, fhctl_en);
395296b5902SFlora Fu }
396296b5902SFlora Fu pll_cnt[APUPLL]++;
397296b5902SFlora Fu spin_unlock(&apupll_lock);
398296b5902SFlora Fu break;
399296b5902SFlora Fu case NPUPLL:
400296b5902SFlora Fu spin_lock(&npupll_lock);
401296b5902SFlora Fu if (pll_cnt[NPUPLL] == 0) {
402296b5902SFlora Fu _pll_switch(pll_idx, enable, fhctl_en);
403296b5902SFlora Fu }
404296b5902SFlora Fu pll_cnt[NPUPLL]++;
405296b5902SFlora Fu spin_unlock(&npupll_lock);
406296b5902SFlora Fu break;
407296b5902SFlora Fu case APUPLL1:
408296b5902SFlora Fu spin_lock(&apupll_1_lock);
409296b5902SFlora Fu if (pll_cnt[APUPLL1] == 0) {
410296b5902SFlora Fu _pll_switch(pll_idx, enable, fhctl_en);
411296b5902SFlora Fu }
412296b5902SFlora Fu pll_cnt[APUPLL1]++;
413296b5902SFlora Fu spin_unlock(&apupll_1_lock);
414296b5902SFlora Fu break;
415296b5902SFlora Fu case APUPLL2:
416296b5902SFlora Fu spin_lock(&apupll_2_lock);
417296b5902SFlora Fu if (pll_cnt[APUPLL2] == 0) {
418296b5902SFlora Fu _pll_switch(pll_idx, enable, fhctl_en);
419296b5902SFlora Fu }
420296b5902SFlora Fu pll_cnt[APUPLL2]++;
421296b5902SFlora Fu spin_unlock(&apupll_2_lock);
422296b5902SFlora Fu break;
423296b5902SFlora Fu default:
424296b5902SFlora Fu ERROR("%s invalid pll_idx: %d\n", __func__, pll_idx);
425296b5902SFlora Fu ret = -EINVAL;
426296b5902SFlora Fu break;
427296b5902SFlora Fu }
428296b5902SFlora Fu } else {
429296b5902SFlora Fu switch (pll_idx) {
430296b5902SFlora Fu case APUPLL:
431296b5902SFlora Fu spin_lock(&apupll_lock);
432296b5902SFlora Fu if (pll_cnt[APUPLL]) {
433296b5902SFlora Fu pll_cnt[APUPLL]--;
434296b5902SFlora Fu }
435296b5902SFlora Fu if (pll_cnt[APUPLL] == 0) {
436296b5902SFlora Fu _pll_switch(pll_idx, enable, fhctl_en);
437296b5902SFlora Fu }
438296b5902SFlora Fu spin_unlock(&apupll_lock);
439296b5902SFlora Fu break;
440296b5902SFlora Fu case NPUPLL:
441296b5902SFlora Fu spin_lock(&npupll_lock);
442296b5902SFlora Fu if (pll_cnt[NPUPLL]) {
443296b5902SFlora Fu pll_cnt[NPUPLL]--;
444296b5902SFlora Fu }
445296b5902SFlora Fu if (pll_cnt[NPUPLL] == 0) {
446296b5902SFlora Fu _pll_switch(pll_idx, enable, fhctl_en);
447296b5902SFlora Fu }
448296b5902SFlora Fu spin_unlock(&npupll_lock);
449296b5902SFlora Fu break;
450296b5902SFlora Fu case APUPLL1:
451296b5902SFlora Fu spin_lock(&apupll_1_lock);
452296b5902SFlora Fu if (pll_cnt[APUPLL1]) {
453296b5902SFlora Fu pll_cnt[APUPLL1]--;
454296b5902SFlora Fu }
455296b5902SFlora Fu if (pll_cnt[APUPLL1] == 0) {
456296b5902SFlora Fu _pll_switch(pll_idx, enable, fhctl_en);
457296b5902SFlora Fu }
458296b5902SFlora Fu spin_unlock(&apupll_1_lock);
459296b5902SFlora Fu break;
460296b5902SFlora Fu case APUPLL2:
461296b5902SFlora Fu spin_lock(&apupll_2_lock);
462296b5902SFlora Fu if (pll_cnt[APUPLL2]) {
463296b5902SFlora Fu pll_cnt[APUPLL2]--;
464296b5902SFlora Fu }
465296b5902SFlora Fu if (pll_cnt[APUPLL2] == 0) {
466296b5902SFlora Fu _pll_switch(pll_idx, enable, fhctl_en);
467296b5902SFlora Fu }
468296b5902SFlora Fu spin_unlock(&apupll_2_lock);
469296b5902SFlora Fu break;
470296b5902SFlora Fu default:
471296b5902SFlora Fu ERROR("%s invalid pll_idx: %d\n", __func__, pll_idx);
472296b5902SFlora Fu ret = -EINVAL;
473296b5902SFlora Fu break;
474296b5902SFlora Fu }
475296b5902SFlora Fu }
476296b5902SFlora Fu
477296b5902SFlora Fu err:
478296b5902SFlora Fu return ret;
479296b5902SFlora Fu }
480296b5902SFlora Fu
481296b5902SFlora Fu /**
482296b5902SFlora Fu * anpu_pll_set_rate() - API for smc function to set rate of voltage domain.
483296b5902SFlora Fu * @domain: Which pll of correspond voltage domain to change rate.
484296b5902SFlora Fu * @mode: which mode to use when set_rate
485296b5902SFlora Fu * @freq: which frequency to set.
486296b5902SFlora Fu *
487296b5902SFlora Fu * For V_VPU0/1, it will only allow 1 of them to modify NPUPLL
488296b5902SFlora Fu * such that there will be no race condition happen.
489296b5902SFlora Fu *
490296b5902SFlora Fu * For V_MDLA0/1, it will only allow 1 of them to modify APUPLL1
491296b5902SFlora Fu * such that there will be no race condition happen.
492296b5902SFlora Fu *
493296b5902SFlora Fu * There are 3 kinds of modes to set pll's rate.
494296b5902SFlora Fu * 1. pure sw mode: (CON0_PCW)
495296b5902SFlora Fu * fhctl function is off and change rate by programming CON1_PCW.
496296b5902SFlora Fu * 2. fhctl sw mode: (FHCTL_SW)
497296b5902SFlora Fu * fhctl function is on and change rate by programming fhctl_dds.
498296b5902SFlora Fu * (post dividor is still need to program CON1_PCW)
499296b5902SFlora Fu * 3. fhctl hw mode: (FHCTL_HW)
500296b5902SFlora Fu * fhctl function is on and change rate by programming fhctl_dvfs.
501296b5902SFlora Fu * (post dividor is still need to program CON1_PCW)
502296b5902SFlora Fu *
503296b5902SFlora Fu * Context: Any context.
504296b5902SFlora Fu * Return:
505296b5902SFlora Fu * * 0 - done for set rate of voltage domain.
506296b5902SFlora Fu */
anpu_pll_set_rate(enum dvfs_voltage_domain domain,enum pll_set_rate_mode mode,int32_t freq)507296b5902SFlora Fu int32_t anpu_pll_set_rate(enum dvfs_voltage_domain domain,
508296b5902SFlora Fu enum pll_set_rate_mode mode, int32_t freq)
509296b5902SFlora Fu {
510296b5902SFlora Fu uint32_t pd, old_pd, dds;
511296b5902SFlora Fu int32_t pll_idx, ret = 0;
512296b5902SFlora Fu
513296b5902SFlora Fu pll_idx = vd2pllidx(domain);
514296b5902SFlora Fu if (pll_idx < 0) {
515296b5902SFlora Fu ret = pll_idx;
516296b5902SFlora Fu goto err;
517296b5902SFlora Fu }
518296b5902SFlora Fu
519296b5902SFlora Fu _cal_pll_data(&pd, &dds, freq / 1000);
520296b5902SFlora Fu
521296b5902SFlora Fu INFO("%s %s new post_div=%d, target dds=0x%08x(%dMhz) mode = %d\n",
522296b5902SFlora Fu __func__, pllidx2name(pll_idx), pd, dds, freq / 1000, mode);
523296b5902SFlora Fu
524296b5902SFlora Fu /* spin_lock for NPULL, since vpu0/1 share npupll */
525296b5902SFlora Fu if (domain == V_VPU0 || domain == V_VPU1) {
526296b5902SFlora Fu spin_lock(&npupll_lock);
527296b5902SFlora Fu }
528296b5902SFlora Fu
529296b5902SFlora Fu /* spin_lock for APUPLL, since mdla0/1 shate apupll */
530296b5902SFlora Fu if (domain == V_MDLA0 || domain == V_MDLA1) {
531296b5902SFlora Fu spin_lock(&apupll_lock);
532296b5902SFlora Fu }
533296b5902SFlora Fu
534296b5902SFlora Fu switch (mode) {
535296b5902SFlora Fu case CON0_PCW:
536296b5902SFlora Fu pd = RG_PLL_SDM_PCW_CHG |
537296b5902SFlora Fu (pd & POSDIV_MASK) << POSDIV_SHIFT | dds;
538296b5902SFlora Fu apupwr_writel(pd, mixed_con1_addr[pll_idx]);
539296b5902SFlora Fu udelay(PLL_READY_TIME_20US);
540296b5902SFlora Fu break;
541296b5902SFlora Fu case FHCTL_SW:
542296b5902SFlora Fu /* pll con0 disable */
543296b5902SFlora Fu _pll_en(pll_idx, false);
544296b5902SFlora Fu apupwr_writel(dds, fhctl_dds_addr[pll_idx]);
545296b5902SFlora Fu _set_postdiv_reg(pll_idx, pd);
546296b5902SFlora Fu apupwr_setbits(PLL_TGL_ORG, fhctl_dds_addr[pll_idx]);
547296b5902SFlora Fu udelay(PLL_CMD_READY_TIME_1US);
548296b5902SFlora Fu /* pll con0 enable */
549296b5902SFlora Fu _pll_en(pll_idx, true);
550296b5902SFlora Fu udelay(PLL_READY_TIME_20US);
551296b5902SFlora Fu break;
552296b5902SFlora Fu case FHCTL_HW:
553296b5902SFlora Fu old_pd = _pll_get_postdiv_reg(pll_idx);
554296b5902SFlora Fu if (pd > old_pd) {
555296b5902SFlora Fu _set_postdiv_reg(pll_idx, pd);
556296b5902SFlora Fu apupwr_writel(dds, fhctl_dvfs_addr[pll_idx]);
557296b5902SFlora Fu } else {
558296b5902SFlora Fu apupwr_writel(dds, fhctl_dvfs_addr[pll_idx]);
559296b5902SFlora Fu _set_postdiv_reg(pll_idx, pd);
560296b5902SFlora Fu }
561296b5902SFlora Fu ret = _fhctl_mon_done(pll_idx, dds);
562296b5902SFlora Fu break;
563296b5902SFlora Fu default:
564296b5902SFlora Fu ERROR("%s input wrong mode: %d\n", __func__, mode);
565296b5902SFlora Fu ret = -EINVAL;
566296b5902SFlora Fu break;
567296b5902SFlora Fu }
568296b5902SFlora Fu
569296b5902SFlora Fu /* spin_lock for NPULL, since vpu0/1 share npupll */
570296b5902SFlora Fu if (domain == V_VPU0 || domain == V_VPU1) {
571296b5902SFlora Fu spin_unlock(&npupll_lock);
572296b5902SFlora Fu }
573296b5902SFlora Fu
574296b5902SFlora Fu /* spin_lock for APUPLL, since mdla0/1 share apupll */
575296b5902SFlora Fu if (domain == V_MDLA0 || domain == V_MDLA1) {
576296b5902SFlora Fu spin_unlock(&apupll_lock);
577296b5902SFlora Fu }
578296b5902SFlora Fu
579296b5902SFlora Fu err:
580296b5902SFlora Fu return ret;
581296b5902SFlora Fu }
582