blob: 2bd90858b562c9f75f8dc72addf50cea6e6f2e6f [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001/*
2 * Common function for power&freq support
3 *
4 * Copyright (C) 2013 Marvell International Ltd. All rights reserved.
5 *
6 * Author: Lucao <lucao@marvell.com>
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software Foundation,
19 * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21#include <power/pxa_common.h>
22
23static void __clk_wait_fc_done(void *address, unsigned long fcreq_bit)
24{
25 /* fc done should be asserted in several clk cycles */
26 unsigned int i = 50;
27 udelay(2);
28 while ((__raw_readl(address) & fcreq_bit) && i) {
29 udelay(10);
30 i--;
31 }
32 if (i == 0) {
33 printf("CLK fc req failed! reg = 0x%x, fc_req = 0x%x\n",
34 (unsigned int)__raw_readl(address),
35 (unsigned int)fcreq_bit);
36 }
37}
38
39unsigned int peri_get_parent_index(const char *parent,
40 struct periph_parents *table, int size)
41{
42 int i;
43 for (i = 0; i < size; i++) {
44 if (!strcmp(parent, table[i].name))
45 break;
46 }
47 if (i == size) {
48 printf("no supported source parent found\n");
49 return 0;
50 }
51 return i;
52}
53
54void peri_set_mux_div(struct peri_reg_info *reg_info,
55 unsigned long mux, unsigned long div)
56{
57 unsigned int regval;
58 unsigned int muxmask, divmask;
59 unsigned int muxval, divval, fcreqmsk = 0xFFFFFFFF;
60 muxval = mux & reg_info->src_sel_mask;
61
62 divval = div & reg_info->div_mask;
63 muxval = muxval << reg_info->src_sel_shift;
64 divval = divval << reg_info->div_shift;
65
66 muxmask = reg_info->src_sel_mask << reg_info->src_sel_shift;
67 divmask = reg_info->div_mask << reg_info->div_shift;
68
69 regval = __raw_readl(reg_info->reg_addr);
70 regval &= ~(muxmask | divmask);
71 regval |= (muxval | divval);
72
73 if (reg_info->fcreq_mask) {
74 fcreqmsk = reg_info->fcreq_mask << reg_info->fcreq_shift;
75 regval |= fcreqmsk;
76 }
77 __raw_writel(regval, reg_info->reg_addr);
78
79 if (reg_info->fcreq_mask)
80 __clk_wait_fc_done(reg_info->reg_addr, fcreqmsk);
81
82 regval = __raw_readl(reg_info->reg_addr);
83 printf("regval[%lx] = %x\n", (unsigned long)reg_info->reg_addr, regval);
84}
85
86int peri_set_clk(unsigned long freq, struct periph_clk_tbl *tbl,
87 int tbl_size, struct peri_reg_info *clk_reg_info,
88 struct periph_parents *periph_parents, int parent_size)
89{
90 unsigned long rate, mux, div;
91 unsigned int parent, i;
92 rate = freq * MHZ;
93
94 for (i = 0; i < tbl_size; i++) {
95 if (rate <= tbl[i].fclk_rate) {
96 printf("i = %d\n", i);
97 printf("set clk rate @ %lu\n", tbl[i].fclk_rate);
98 break;
99 }
100 }
101 if (i == tbl_size) {
102 printf("no supported rate\n");
103 return -1;
104 }
105
106 parent = peri_get_parent_index(tbl[i].fclk_parent, periph_parents,
107 parent_size);
108 mux = periph_parents[parent].hw_sel;
109 div = (periph_parents[parent].rate / tbl[i].fclk_rate) - 1;
110 peri_set_mux_div(clk_reg_info, mux, div);
111 return 0;
112}
113
114int peri_setrate(unsigned long freq, struct periph_clk_tbl *tbl,
115 int tbl_size, struct peri_reg_info *aclk_reg_info,
116 struct peri_reg_info *fclk_reg_info,
117 struct periph_parents *periph_aparents, int aparent_size,
118 struct periph_parents *periph_fparents, int fparent_size)
119{
120 unsigned long rate, fmux, amux, fdiv, adiv;
121 unsigned int fparent, aparent, i;
122 rate = freq * MHZ;
123
124 for (i = 0; i < tbl_size; i++) {
125 if (rate <= tbl[i].fclk_rate) {
126 printf("i = %d\n", i);
127 printf("set fclk rate @ %lu aclk rate @ %lu\n",
128 tbl[i].fclk_rate, tbl[i].aclk_rate);
129 break;
130 }
131 }
132 if (i == tbl_size) {
133 printf("no supported rate\n");
134 return -1;
135 }
136
137 aparent = peri_get_parent_index(tbl[i].aclk_parent, periph_aparents,
138 aparent_size);
139 amux = periph_aparents[aparent].hw_sel;
140 adiv = (periph_aparents[aparent].rate / tbl[i].aclk_rate) - 1;
141 peri_set_mux_div(aclk_reg_info, amux, adiv);
142
143 fparent = peri_get_parent_index(tbl[i].fclk_parent, periph_fparents,
144 fparent_size);
145 fmux = periph_fparents[fparent].hw_sel;
146 fdiv = (periph_fparents[fparent].rate / tbl[i].fclk_rate) - 1;
147 peri_set_mux_div(fclk_reg_info, fmux, fdiv);
148 return 0;
149}
150
151/* power domain related */
152enum pd_island {
153 GC3D,
154 GC2D,
155 VPU,
156 ISP,
157 ISLAND_MAX
158};
159
160struct pd_common_data {
161 u32 *reg_clk_res_ctrl;
162 u32 bit_hw_mode;
163 u32 bit_auto_pwr_on;
164 u32 bit_pwr_stat;
165};
166
167struct pd_common_data pd_data[ISLAND_MAX] = {
168 [GC3D] = {
169 .reg_clk_res_ctrl = (u32 *)0xd42828cc,
170 .bit_hw_mode = 11,
171 .bit_auto_pwr_on = 0,
172 .bit_pwr_stat = 0,
173 },
174 [GC2D] = {
175 .reg_clk_res_ctrl = (u32 *)0xd42828f4,
176 .bit_hw_mode = 11,
177 .bit_auto_pwr_on = 6,
178 .bit_pwr_stat = 6,
179 },
180 [VPU] = {
181 .reg_clk_res_ctrl = (u32 *)0xd42828a4,
182 .bit_hw_mode = 19,
183 .bit_auto_pwr_on = 2,
184 .bit_pwr_stat = 2,
185 },
186 [ISP] = {
187 .reg_clk_res_ctrl = (u32 *)0xd4282838,
188 .bit_hw_mode = 15,
189 .bit_auto_pwr_on = 4,
190 .bit_pwr_stat = 4,
191 },
192};
193
194
195/* always use hw control */
196static int island_poweron(enum pd_island island)
197{
198 u32 val;
199 int loop = 5000;
200 struct pd_common_data *data = &pd_data[island];
201
202 /* set HW on/off mode */
203 val = __raw_readl(data->reg_clk_res_ctrl);
204 val |= (1 << data->bit_hw_mode);
205 __raw_writel(val, data->reg_clk_res_ctrl);
206
207 /* on1, on2, off timer */
208 __raw_writel(0x20001fff, APMU_PWR_BLK_TMR_REG);
209
210 /* auto power on */
211 val = __raw_readl(APMU_PWR_CTRL_REG);
212 val |= (1 << data->bit_auto_pwr_on);
213 __raw_writel(val, APMU_PWR_CTRL_REG);
214
215 /*
216 * power on takes 316us
217 */
218 udelay(320);
219
220 /* polling PWR_STAT bit */
221 for (loop = 5000; loop > 0; loop--) {
222 val = __raw_readl(APMU_PWR_STATUS_REG);
223 if (val & (1 << data->bit_pwr_stat))
224 break;
225 udelay(1);
226 }
227
228 if (loop <= 0) {
229 printf("island %d failed to power on!\n", island);
230 return -1;
231 }
232
233 return 0;
234}
235
236/* always use hw control */
237static int island_poweroff(enum pd_island island)
238{
239 u32 val;
240 int loop = 5000;
241 struct pd_common_data *data = &pd_data[island];
242
243 /* auto power off */
244 val = __raw_readl(APMU_PWR_CTRL_REG);
245 val &= ~(1 << data->bit_auto_pwr_on);
246 __raw_writel(val, APMU_PWR_CTRL_REG);
247
248 /*
249 * power off takes 23us, add a pre-delay to reduce the
250 * number of polling
251 */
252 udelay(20);
253
254 /* polling PWR_STAT bit */
255 for (loop = 5000; loop > 0; loop--) {
256 val = __raw_readl(APMU_PWR_STATUS_REG);
257 if (!(val & (1 << data->bit_pwr_stat)))
258 break;
259 udelay(1);
260 }
261 if (loop <= 0) {
262 printf("island %d failed to power off!\n", island);
263 return -1;
264 }
265
266 return 0;
267}
268
269static int island_pwr(enum pd_island island, u32 onoff)
270{
271 int ret = 0;
272 if (onoff)
273 ret = island_poweron(island);
274 else
275 ret = island_poweroff(island);
276 return ret;
277};
278
279static int do_gcpwr(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
280{
281 ulong onoff;
282 int res = -1;
283
284 if (argc != 2) {
285 printf("usage: gcpwr 1/0\n");
286 return -1;
287 }
288
289 res = strict_strtoul(argv[1], 0, &onoff);
290 if (res == 0)
291 res = island_pwr(GC3D, onoff);
292
293 return res;
294}
295
296U_BOOT_CMD(
297 gc_pwr, 6, 1, do_gcpwr,
298 "Gc power on/off",
299 ""
300);
301
302static int do_gc2dpwr(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
303{
304 ulong onoff;
305 int res = -1;
306
307 if (argc != 2) {
308 printf("usage: gc2d_pwr 1/0\n");
309 return -1;
310 }
311
312 res = strict_strtoul(argv[1], 0, &onoff);
313 if (res == 0)
314 res = island_pwr(GC2D, onoff);
315
316 return res;
317}
318
319U_BOOT_CMD(
320 gc2d_pwr, 6, 1, do_gc2dpwr,
321 "Gc2D power on/off",
322 ""
323);
324
325static int do_vpupwr(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
326{
327 ulong onoff;
328 int res = -1;
329
330 if (argc != 2) {
331 printf("usage: vpu_pwr 1/0\n");
332 return -1;
333 }
334
335 res = strict_strtoul(argv[1], 0, &onoff);
336 if (res == 0)
337 res = island_pwr(VPU, onoff);
338
339 return res;
340}
341
342U_BOOT_CMD(
343 vpu_pwr, 6, 1, do_vpupwr,
344 "VPU power on/off",
345 ""
346);
347
348static int do_isppwr(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
349{
350 ulong onoff;
351 int res = -1;
352
353 if (argc != 2) {
354 printf("usage: isp_pwr 1/0\n");
355 return -1;
356 }
357
358 res = strict_strtoul(argv[1], 0, &onoff);
359 if (res == 0)
360 res = island_pwr(ISP, onoff);
361
362 return res;
363}
364
365U_BOOT_CMD(
366 isp_pwr, 6, 1, do_isppwr,
367 "ISP power on/off",
368 ""
369);
370
371/* GC internal scaling and clock gating feature support */
372enum GC_COMP {
373 GC3D_CTR,
374 GC2D_CTR,
375};
376
377#define GC3D_BASE (0xc0400000)
378#define GC2D_BASE (0xd420c000)
379#define REG_GCPULSEEATER (GC3D_BASE + 0x10c)
380#define AQHICLKCTR_OFF (0x000)
381#define POWERCTR_OFF (0x100)
382
383#define FSCALE_VALMASK (0x1fc)
384#define SHADSCALE_VALMASK (0xfe)
385
386u32 *gcctr_base[] = {
387 (u32 *)GC3D_BASE, (u32 *)GC2D_BASE
388};
389
390static void gc_fs_on(enum GC_COMP comp)
391{
392 u32 val;
393 /* switch to manual mode, only 3D has this extra step */
394 if (GC3D_CTR == comp) {
395 val = __raw_readl(REG_GCPULSEEATER);
396 val &= ~(1 << 16);
397 val |= (1 << 17);
398 __raw_writel(val, REG_GCPULSEEATER);
399 /* shader scaling to 1/64 */
400 val |= (1 << 0);
401 val &= ~(SHADSCALE_VALMASK);
402 val |= (1 << 1);
403 __raw_writel(val, REG_GCPULSEEATER);
404 val &= ~(1 << 0);
405 __raw_writel(val, REG_GCPULSEEATER);
406 }
407
408 val = __raw_readl(gcctr_base[comp] + AQHICLKCTR_OFF);
409 val &= ~(FSCALE_VALMASK);
410 /* set 1/64 scaling divider */
411 val |= (1 << 2) | (1 << 9);
412 __raw_writel(val, gcctr_base[comp] + AQHICLKCTR_OFF);
413 /* make scaling work */
414 val &= ~(1 << 9);
415 __raw_writel(val, gcctr_base[comp] + AQHICLKCTR_OFF);
416}
417
418static void gc_fs_off(enum GC_COMP comp)
419{
420 u32 val;
421
422 /* switch to auto mode, only 3D has this extra step */
423 if (GC3D_CTR == comp) {
424 val = __raw_readl(REG_GCPULSEEATER);
425 val &= ~(1 << 16);
426 val |= (1 << 17);
427 __raw_writel(val, REG_GCPULSEEATER);
428 /* shader scaling to 64/64 */
429 val |= (1 << 0);
430 val &= ~(SHADSCALE_VALMASK);
431 val |= (0x40 << 1);
432 __raw_writel(val, REG_GCPULSEEATER);
433 val &= ~(1 << 0);
434 __raw_writel(val, REG_GCPULSEEATER);
435 }
436
437 val = __raw_readl(gcctr_base[comp] + AQHICLKCTR_OFF);
438 val &= ~(FSCALE_VALMASK);
439 /* set 64/64 scaling divider */
440 val |= (64 << 2) | (1 << 9);
441 __raw_writel(val, gcctr_base[comp] + AQHICLKCTR_OFF);
442 /* make scaling work */
443 val &= ~(1 << 9);
444 __raw_writel(val, gcctr_base[comp] + AQHICLKCTR_OFF);
445}
446
447static void gc_cg_ctrl(enum GC_COMP comp, u32 onoff)
448{
449 u32 val;
450
451 val = __raw_readl(gcctr_base[comp] + POWERCTR_OFF);
452 val &= ~(1 << 0);
453 val |= (!!onoff);
454 __raw_writel(val, gcctr_base[comp] + POWERCTR_OFF);
455}
456
457static int do_enable_3dgc_fs(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
458{
459 ulong onoff;
460 int res = -1;
461
462 if (argc != 2) {
463 printf("usage: enable_3dgc_fs 1/0\n");
464 return res;
465 }
466
467 res = strict_strtoul(argv[1], 0, &onoff);
468 if (res == 0) {
469 if (onoff)
470 gc_fs_on(GC3D_CTR);
471 else
472 gc_fs_off(GC3D_CTR);
473 }
474 return res;
475}
476
477U_BOOT_CMD(
478 enable_3dgc_fs, 6, 1, do_enable_3dgc_fs,
479 "Enable GC3D frequency scaling",
480 ""
481);
482
483static int do_enable_3dgc_cg(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
484{
485 ulong onoff;
486 int res = -1;
487
488 if (argc != 2) {
489 printf("usage: enable_3dgc_cg 1/0\n");
490 return res;
491 }
492
493 res = strict_strtoul(argv[1], 0, &onoff);
494 if (res == 0)
495 gc_cg_ctrl(GC3D_CTR, onoff);
496 return res;
497}
498
499U_BOOT_CMD(
500 enable_3dgc_cg, 6, 1, do_enable_3dgc_cg,
501 "Enable GC3D clock gating",
502 ""
503);
504
505static int do_enable_2dgc_fs(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
506{
507 ulong onoff;
508 int res = -1;
509
510 if (argc != 2) {
511 printf("usage: enable_2dgc_fs 1/0\n");
512 return res;
513 }
514
515 res = strict_strtoul(argv[1], 0, &onoff);
516 if (res == 0) {
517 if (onoff)
518 gc_fs_on(GC2D_CTR);
519 else
520 gc_fs_off(GC2D_CTR);
521 }
522 return res;
523}
524
525U_BOOT_CMD(
526 enable_2dgc_fs, 6, 1, do_enable_2dgc_fs,
527 "Enable GC2D frequency scaling",
528 ""
529);
530
531static int do_enable_2dgc_cg(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
532{
533 ulong onoff;
534 int res = -1;
535
536 if (argc != 2) {
537 printf("usage: enable_2dgc_cg 1/0\n");
538 return res;
539 }
540
541 res = strict_strtoul(argv[1], 0, &onoff);
542 if (res == 0)
543 gc_cg_ctrl(GC2D_CTR, onoff);
544 return res;
545}
546
547U_BOOT_CMD(
548 enable_2dgc_cg, 6, 1, do_enable_2dgc_cg,
549 "Enable GC2D clock gating",
550 ""
551);