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
13struct 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
21struct 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	{ 1300, 104,      1, 1, 7},
36	{ 1350, 108,      1, 1, 7},
37	{ 1400, 112,      1, 1, 7},
38};
39
40uint32_t armpll_config(uint32_t clkmhz)
41{
42	uint32_t freqid;
43	uint32_t ndiv_frac;
44	uint32_t pll;
45	uint32_t status = 1;
46	uint32_t timeout_countdown;
47	int i;
48
49	for (i = 0; i < NELEMS(armpll_clk_tab); i++) {
50		if (armpll_clk_tab[i].mode == clkmhz) {
51			status = 0;
52			break;
53		}
54	}
55
56	if (status) {
57		printf("Error: Clock configuration not supported\n");
58		goto armpll_config_done;
59	}
60
61	/* Enable write access */
62	writel(IPROC_REG_WRITE_ACCESS, IHOST_PROC_CLK_WR_ACCESS);
63
64	if (clkmhz == 25)
65		freqid = 0;
66	else
67		freqid = 2;
68
69	/* Bypass ARM clock and run on sysclk */
70	writel(1 << IHOST_PROC_CLK_POLICY_FREQ__PRIV_ACCESS_MODE |
71	       freqid << IHOST_PROC_CLK_POLICY_FREQ__POLICY3_FREQ_R |
72	       freqid << IHOST_PROC_CLK_POLICY_FREQ__POLICY2_FREQ_R |
73	       freqid << IHOST_PROC_CLK_POLICY_FREQ__POLICY1_FREQ_R |
74	       freqid << IHOST_PROC_CLK_POLICY_FREQ__POLICY0_FREQ_R,
75	       IHOST_PROC_CLK_POLICY_FREQ);
76
77	writel(1 << IHOST_PROC_CLK_POLICY_CTL__GO |
78	       1 << IHOST_PROC_CLK_POLICY_CTL__GO_AC,
79	       IHOST_PROC_CLK_POLICY_CTL);
80
81	/* Poll CCU until operation complete */
82	timeout_countdown = 0x100000;
83	while (readl(IHOST_PROC_CLK_POLICY_CTL) &
84	       (1 << IHOST_PROC_CLK_POLICY_CTL__GO)) {
85		timeout_countdown--;
86		if (timeout_countdown == 0) {
87			printf("CCU polling timedout\n");
88			status = 1;
89			goto armpll_config_done;
90		}
91	}
92
93	if (clkmhz == 25 || clkmhz == 100) {
94		status = 0;
95		goto armpll_config_done;
96	}
97
98	/* Now it is safe to program the PLL */
99	pll = readl(IHOST_PROC_CLK_PLLARMB);
100	pll &= ~((1 << IHOST_PROC_CLK_PLLARMB__PLLARM_NDIV_FRAC_WIDTH) - 1);
101	ndiv_frac =
102		((1 << IHOST_PROC_CLK_PLLARMB__PLLARM_NDIV_FRAC_WIDTH) - 1) &
103		 (armpll_clk_tab[i].ndiv_frac <<
104		 IHOST_PROC_CLK_PLLARMB__PLLARM_NDIV_FRAC_R);
105	pll |= ndiv_frac;
106	writel(pll, IHOST_PROC_CLK_PLLARMB);
107
108	writel(1 << IHOST_PROC_CLK_PLLARMA__PLLARM_LOCK |
109	       armpll_clk_tab[i].ndiv_int <<
110			IHOST_PROC_CLK_PLLARMA__PLLARM_NDIV_INT_R |
111	       armpll_clk_tab[i].pdiv <<
112			IHOST_PROC_CLK_PLLARMA__PLLARM_PDIV_R |
113	       1 << IHOST_PROC_CLK_PLLARMA__PLLARM_SOFT_RESETB,
114	       IHOST_PROC_CLK_PLLARMA);
115
116	/* Poll ARM PLL Lock until operation complete */
117	timeout_countdown = 0x100000;
118	while (readl(IHOST_PROC_CLK_PLLARMA) &
119	       (1 << IHOST_PROC_CLK_PLLARMA__PLLARM_LOCK)) {
120		timeout_countdown--;
121		if (timeout_countdown == 0) {
122			printf("ARM PLL lock failed\n");
123			status = 1;
124			goto armpll_config_done;
125		}
126	}
127
128	pll = readl(IHOST_PROC_CLK_PLLARMA);
129	pll |= (1 << IHOST_PROC_CLK_PLLARMA__PLLARM_SOFT_POST_RESETB);
130	writel(pll, IHOST_PROC_CLK_PLLARMA);
131
132	/* Set the policy */
133	writel(1 << IHOST_PROC_CLK_POLICY_FREQ__PRIV_ACCESS_MODE |
134	       armpll_clk_tab[i].freqid <<
135			IHOST_PROC_CLK_POLICY_FREQ__POLICY3_FREQ_R |
136	       armpll_clk_tab[i].freqid <<
137			IHOST_PROC_CLK_POLICY_FREQ__POLICY2_FREQ_R |
138	       armpll_clk_tab[i].freqid <<
139			IHOST_PROC_CLK_POLICY_FREQ__POLICY1_FREQ_R |
140	       armpll_clk_tab[i+4].freqid <<
141			IHOST_PROC_CLK_POLICY_FREQ__POLICY0_FREQ_R,
142	       IHOST_PROC_CLK_POLICY_FREQ);
143
144	writel(IPROC_CLKCT_HDELAY_SW_EN, IHOST_PROC_CLK_CORE0_CLKGATE);
145	writel(IPROC_CLKCT_HDELAY_SW_EN, IHOST_PROC_CLK_CORE1_CLKGATE);
146	writel(IPROC_CLKCT_HDELAY_SW_EN, IHOST_PROC_CLK_ARM_SWITCH_CLKGATE);
147	writel(IPROC_CLKCT_HDELAY_SW_EN, IHOST_PROC_CLK_ARM_PERIPH_CLKGATE);
148	writel(IPROC_CLKCT_HDELAY_SW_EN, IHOST_PROC_CLK_APB0_CLKGATE);
149
150	writel(1 << IHOST_PROC_CLK_POLICY_CTL__GO |
151	       1 << IHOST_PROC_CLK_POLICY_CTL__GO_AC,
152	       IHOST_PROC_CLK_POLICY_CTL);
153
154	/* Poll CCU until operation complete */
155	timeout_countdown = 0x100000;
156	while (readl(IHOST_PROC_CLK_POLICY_CTL) &
157	       (1 << IHOST_PROC_CLK_POLICY_CTL__GO)) {
158		timeout_countdown--;
159		if (timeout_countdown == 0) {
160			printf("CCU polling failed\n");
161			status = 1;
162			goto armpll_config_done;
163		}
164	}
165
166	status = 0;
167armpll_config_done:
168	/* Disable access to PLL registers */
169	writel(0, IHOST_PROC_CLK_WR_ACCESS);
170
171	return status;
172}
173