1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
|
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2011-01-13 weety first version
*/
#include <rtthread.h>
#include "at91sam9g45.h"
static rt_list_t clocks;
struct clk {
char name[32];
rt_uint32_t rate_hz;
struct clk *parent;
rt_list_t node;
};
static struct clk clk32k = {
"clk32k",
AT91C_SLOW_CLOCK,
RT_NULL,
{RT_NULL, RT_NULL},
};
static struct clk main_clk = {
"main",
0,
RT_NULL,
{RT_NULL, RT_NULL},
};
static struct clk plla = {
"plla",
0,
&main_clk,
{RT_NULL, RT_NULL},
};
static struct clk mck = {
"mck",
0,
RT_NULL,
{RT_NULL, RT_NULL},
};
static struct clk upllck = {
"upllck",
480*1000*1000,
&main_clk,
{RT_NULL, RT_NULL},
};
static struct clk *const standard_pmc_clocks[] = {
/* four primary clocks */
&clk32k,
&main_clk,
&plla,
/* MCK */
&mck
};
/* clocks cannot be de-registered no refcounting necessary */
struct clk *clk_get(const char *id)
{
struct clk *clk;
rt_list_t *list;
for (list = (&clocks)->next; list != &clocks; list = list->next)
{
clk = (struct clk *)rt_list_entry(list, struct clk, node);
if (rt_strcmp(id, clk->name) == 0)
return clk;
}
return RT_NULL;
}
rt_uint32_t clk_get_rate(struct clk *clk)
{
rt_uint32_t rate;
for (;;) {
rate = clk->rate_hz;
if (rate || !clk->parent)
break;
clk = clk->parent;
}
return rate;
}
static void at91_upllck_init(rt_uint32_t main_clock)
{
// EHCI USB use fixed 480MHz clock
}
static struct clk *at91_css_to_clk(unsigned long css)
{
switch (css) {
case AT91C_PMC_CSS_SLOW_CLK:
return &clk32k;
case AT91C_PMC_CSS_MAIN_CLK:
return &main_clk;
case AT91C_PMC_CSS_PLLA_CLK:
return &plla;
case AT91C_PMC_CSS_UPLL_CLK:
return &upllck;
}
return RT_NULL;
}
// TODO: how to auto-set register value by OSC and MCK
/* Settings at 400/133MHz */
// In datasheet, ATMEL says 12MHz main crystal startup time less than 2ms, so we
// configure OSC startup timeout to 64*8/32768=15.6ms, should enough
#define BOARD_OSCOUNT (AT91C_CKGR_OSCOUNT & (64 << 8))
// MAINCK => Divider(DIVA) => PLLA(MULA, OUTA) => /1/2 Divider(PLLADIV2) => PLLACK
// pls. refer to doc6438G figure 24-6 on pg294. ICPLLA in reg PMC_PLLICPR
// 12MHz / 3 * (199 + 1) = 800MHz
// OUTA/ICPLLA can as ICPLLA:OUTA[1]:OUTA[0] = (800-PLLAOUT(MHz))/50
// PLLACOUNT field occupy bit[13:8], max value is 0x3F, then about 19.2ms
#define BOARD_CKGR_PLLA (AT91C_CKGR_SRCA | AT91C_CKGR_OUTA_0)
#define BOARD_PLLACOUNT (0x3F << 8)
#define BOARD_MULA (AT91C_CKGR_MULA & (199 << 16))
#define BOARD_DIVA (AT91C_CKGR_DIVA & 3)
// Clock Source => select(CCS) => Prescaler(PRES) => Master Clock Divider(MDIV) => MCK
// => Processor Clock Divider => PCK
// Master clock can refer to doc6438G figure 25-2 on pg298
// PLLADIV2=1(div 2, 400MHz), PRES=0(no div, 400MHz),
// MDIV=3(Master Clock divided by 3, 133MHz), CSS=0(still Slow Clock)
#define BOARD_PRESCALER (0x00001300) //400/133MHz
#define MHz(n) ((n) * 1000 * 1000)
#define OSC_FREQ MHz(12)
#define PLLA_FREQ MHz(800)
static void at91_plla_init(void)
{
rt_uint32_t pllar, mckr;
// Code refer to doc6438G, 25.10 Programming Sequence
/* Initialize main oscillator
****************************/
// enable main OSC and wait OSC startup time timeout.
AT91C_BASE_PMC->PMC_MOR = BOARD_OSCOUNT | AT91C_CKGR_MOSCEN;
while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MOSCS));
/* Initialize PLLA, Set PLL to 800MHz and wait PLL stable */
pllar = (MHz(800) - PLLA_FREQ) / MHz(50); // please refer to Table 46-15 of doc 6438G
AT91C_BASE_PMC->PMC_PLLICPR = (pllar >> 2) & 1; // ICPLLA
pllar = (pllar & 3) << 14; // OUTA
pllar |= BOARD_DIVA; // PLLA input clock as 4MHz
pllar |= BOARD_MULA; // PLLA output clock as 800MHz
pllar |= BOARD_PLLACOUNT;
pllar |= AT91C_CKGR_SRCA; // I don't known what means, but seems must set it
AT91C_BASE_PMC->PMC_PLLAR = pllar;
while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_LOCKA));
/* Wait for the master clock if it was already initialized */
// make sure Master clock in READY status before operate it
while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY));
/* Switch to fast clock
**********************/
/* setup main clock divisor and prescaler, 400MHz/133MHz, but don't switch */
mckr = AT91C_BASE_PMC->PMC_MCKR;
if ((mckr & AT91C_PMC_MDIV) != (BOARD_PRESCALER & AT91C_PMC_MDIV))
{
mckr = (mckr & ~(unsigned int)AT91C_PMC_MDIV) | (BOARD_PRESCALER & AT91C_PMC_MDIV);
AT91C_BASE_PMC->PMC_MCKR = mckr;
while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY));
}
/* Switch to PLL + prescaler, now Switch to PLLA as source, run on the fly */
if ((mckr & AT91C_PMC_CSS) != AT91C_PMC_CSS_PLLA_CLK)
{
mckr = (mckr & ~(unsigned int)AT91C_PMC_CSS) | AT91C_PMC_CSS_PLLA_CLK;
AT91C_BASE_PMC->PMC_MCKR = mckr;
while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY));
}
plla.rate_hz = PLLA_FREQ;
}
#define false 0
#define true 1
int at91_clock_init(rt_uint32_t main_clock)
{
unsigned tmp, freq, mckr, mdiv;
int i;
/*
* When the bootloader initialized the main oscillator correctly,
* there's no problem using the cycle counter. But if it didn't,
* or when using oscillator bypass mode, we must be told the speed
* of the main clock.
*/
if (!main_clock) {
do {
tmp = readl(AT91C_CKGR_MCFR);
} while (!(tmp & AT91C_CKGR_MAINRDY));
main_clock = (tmp & AT91C_CKGR_MAINF) * (AT91C_SLOW_CLOCK / 16);
}
main_clk.rate_hz = main_clock;
at91_plla_init();
at91_upllck_init(main_clock);
/*
* MCK and CPU derive from one of those primary clocks.
* For now, assume this parentage won't change.
*/
mckr = readl(AT91C_PMC_MCKR);
mck.parent = at91_css_to_clk(mckr & AT91C_PMC_CSS);
freq = mck.parent->rate_hz;
freq /= (1 << ((mckr & AT91C_PMC_PRES) >> 2)); /* prescale */
mdiv = 1 << ((mckr & AT91C_PMC_MDIV) >> 8);
if (mdiv == 8) mdiv = 3;
freq /= mdiv; /* mdiv */
if (mckr & AT91C_PMC_PLLADIV2) freq /= 2; /* plla_div2 */
mck.rate_hz = freq;
/* Register the PMC's standard clocks */
rt_list_init(&clocks);
for (i = 0; i < ARRAY_SIZE(standard_pmc_clocks); i++)
rt_list_insert_after(&clocks, &standard_pmc_clocks[i]->node);
rt_list_insert_after(&clocks, &upllck.node);
/* MCK and CPU clock are "always on" */
//clk_enable(&mck);
/*rt_kprintf("Clocks: CPU %u MHz, master %u MHz, main %u.%03u MHz\n",
freq / 1000000, (unsigned) mck.rate_hz / 1000000,
(unsigned) main_clock / 1000000,
((unsigned) main_clock % 1000000) / 1000);*///cause blocked
return 0;
}
/**
* @brief System Clock Configuration
*/
void rt_hw_clock_init(void)
{
at91_clock_init(MHz(12));
}
|