1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2014 Broadcom Corporation.
4  */
5 
6 #include <common.h>
7 #include <asm/io.h>
8 #include <asm/iproc-common/armpll.h>
9 #include <asm/iproc-common/sysmap.h>
10 
11 #define NELEMS(x)	(sizeof(x) / sizeof(x[0]))
12 
13 struct armpll_parameters {
14 	unsigned int mode;
15 	unsigned int ndiv_int;
16 	unsigned int ndiv_frac;
17 	unsigned int pdiv;
18 	unsigned int freqid;
19 };
20 
21 struct armpll_parameters armpll_clk_tab[] = {
22 	{   25, 64,      1, 1, 0},
23 	{  100, 64,      1, 1, 2},
24 	{  400, 64,      1, 1, 6},
25 	{  448, 71, 713050, 1, 6},
26 	{  500, 80,      1, 1, 6},
27 	{  560, 89, 629145, 1, 6},
28 	{  600, 96,      1, 1, 6},
29 	{  800, 64,      1, 1, 7},
30 	{  896, 71, 713050, 1, 7},
31 	{ 1000, 80,      1, 1, 7},
32 	{ 1100, 88,      1, 1, 7},
33 	{ 1120, 89, 629145, 1, 7},
34 	{ 1200, 96,      1, 1, 7},
35 };
36 
37 uint32_t armpll_config(uint32_t clkmhz)
38 {
39 	uint32_t freqid;
40 	uint32_t ndiv_frac;
41 	uint32_t pll;
42 	uint32_t status = 1;
43 	uint32_t timeout_countdown;
44 	int i;
45 
46 	for (i = 0; i < NELEMS(armpll_clk_tab); i++) {
47 		if (armpll_clk_tab[i].mode == clkmhz) {
48 			status = 0;
49 			break;
50 		}
51 	}
52 
53 	if (status) {
54 		printf("Error: Clock configuration not supported\n");
55 		goto armpll_config_done;
56 	}
57 
58 	/* Enable write access */
59 	writel(IPROC_REG_WRITE_ACCESS, IHOST_PROC_CLK_WR_ACCESS);
60 
61 	if (clkmhz == 25)
62 		freqid = 0;
63 	else
64 		freqid = 2;
65 
66 	/* Bypass ARM clock and run on sysclk */
67 	writel(1 << IHOST_PROC_CLK_POLICY_FREQ__PRIV_ACCESS_MODE |
68 	       freqid << IHOST_PROC_CLK_POLICY_FREQ__POLICY3_FREQ_R |
69 	       freqid << IHOST_PROC_CLK_POLICY_FREQ__POLICY2_FREQ_R |
70 	       freqid << IHOST_PROC_CLK_POLICY_FREQ__POLICY1_FREQ_R |
71 	       freqid << IHOST_PROC_CLK_POLICY_FREQ__POLICY0_FREQ_R,
72 	       IHOST_PROC_CLK_POLICY_FREQ);
73 
74 	writel(1 << IHOST_PROC_CLK_POLICY_CTL__GO |
75 	       1 << IHOST_PROC_CLK_POLICY_CTL__GO_AC,
76 	       IHOST_PROC_CLK_POLICY_CTL);
77 
78 	/* Poll CCU until operation complete */
79 	timeout_countdown = 0x100000;
80 	while (readl(IHOST_PROC_CLK_POLICY_CTL) &
81 	       (1 << IHOST_PROC_CLK_POLICY_CTL__GO)) {
82 		timeout_countdown--;
83 		if (timeout_countdown == 0) {
84 			printf("CCU polling timedout\n");
85 			status = 1;
86 			goto armpll_config_done;
87 		}
88 	}
89 
90 	if (clkmhz == 25 || clkmhz == 100) {
91 		status = 0;
92 		goto armpll_config_done;
93 	}
94 
95 	/* Now it is safe to program the PLL */
96 	pll = readl(IHOST_PROC_CLK_PLLARMB);
97 	pll &= ~((1 << IHOST_PROC_CLK_PLLARMB__PLLARM_NDIV_FRAC_WIDTH) - 1);
98 	ndiv_frac =
99 		((1 << IHOST_PROC_CLK_PLLARMB__PLLARM_NDIV_FRAC_WIDTH) - 1) &
100 		 (armpll_clk_tab[i].ndiv_frac <<
101 		 IHOST_PROC_CLK_PLLARMB__PLLARM_NDIV_FRAC_R);
102 	pll |= ndiv_frac;
103 	writel(pll, IHOST_PROC_CLK_PLLARMB);
104 
105 	writel(1 << IHOST_PROC_CLK_PLLARMA__PLLARM_LOCK |
106 	       armpll_clk_tab[i].ndiv_int <<
107 			IHOST_PROC_CLK_PLLARMA__PLLARM_NDIV_INT_R |
108 	       armpll_clk_tab[i].pdiv <<
109 			IHOST_PROC_CLK_PLLARMA__PLLARM_PDIV_R |
110 	       1 << IHOST_PROC_CLK_PLLARMA__PLLARM_SOFT_RESETB,
111 	       IHOST_PROC_CLK_PLLARMA);
112 
113 	/* Poll ARM PLL Lock until operation complete */
114 	timeout_countdown = 0x100000;
115 	while (readl(IHOST_PROC_CLK_PLLARMA) &
116 	       (1 << IHOST_PROC_CLK_PLLARMA__PLLARM_LOCK)) {
117 		timeout_countdown--;
118 		if (timeout_countdown == 0) {
119 			printf("ARM PLL lock failed\n");
120 			status = 1;
121 			goto armpll_config_done;
122 		}
123 	}
124 
125 	pll = readl(IHOST_PROC_CLK_PLLARMA);
126 	pll |= (1 << IHOST_PROC_CLK_PLLARMA__PLLARM_SOFT_POST_RESETB);
127 	writel(pll, IHOST_PROC_CLK_PLLARMA);
128 
129 	/* Set the policy */
130 	writel(1 << IHOST_PROC_CLK_POLICY_FREQ__PRIV_ACCESS_MODE |
131 	       armpll_clk_tab[i].freqid <<
132 			IHOST_PROC_CLK_POLICY_FREQ__POLICY3_FREQ_R |
133 	       armpll_clk_tab[i].freqid <<
134 			IHOST_PROC_CLK_POLICY_FREQ__POLICY2_FREQ_R |
135 	       armpll_clk_tab[i].freqid <<
136 			IHOST_PROC_CLK_POLICY_FREQ__POLICY1_FREQ_R |
137 	       armpll_clk_tab[i+4].freqid <<
138 			IHOST_PROC_CLK_POLICY_FREQ__POLICY0_FREQ_R,
139 	       IHOST_PROC_CLK_POLICY_FREQ);
140 
141 	writel(IPROC_CLKCT_HDELAY_SW_EN, IHOST_PROC_CLK_CORE0_CLKGATE);
142 	writel(IPROC_CLKCT_HDELAY_SW_EN, IHOST_PROC_CLK_CORE1_CLKGATE);
143 	writel(IPROC_CLKCT_HDELAY_SW_EN, IHOST_PROC_CLK_ARM_SWITCH_CLKGATE);
144 	writel(IPROC_CLKCT_HDELAY_SW_EN, IHOST_PROC_CLK_ARM_PERIPH_CLKGATE);
145 	writel(IPROC_CLKCT_HDELAY_SW_EN, IHOST_PROC_CLK_APB0_CLKGATE);
146 
147 	writel(1 << IHOST_PROC_CLK_POLICY_CTL__GO |
148 	       1 << IHOST_PROC_CLK_POLICY_CTL__GO_AC,
149 	       IHOST_PROC_CLK_POLICY_CTL);
150 
151 	/* Poll CCU until operation complete */
152 	timeout_countdown = 0x100000;
153 	while (readl(IHOST_PROC_CLK_POLICY_CTL) &
154 	       (1 << IHOST_PROC_CLK_POLICY_CTL__GO)) {
155 		timeout_countdown--;
156 		if (timeout_countdown == 0) {
157 			printf("CCU polling failed\n");
158 			status = 1;
159 			goto armpll_config_done;
160 		}
161 	}
162 
163 	status = 0;
164 armpll_config_done:
165 	/* Disable access to PLL registers */
166 	writel(0, IHOST_PROC_CLK_WR_ACCESS);
167 
168 	return status;
169 }
170