Skip to content

Commit acf1cb1

Browse files
committed
platform/sam3u: initial pmc
Signed-off-by: Rafael Silva <perigoso@riseup.net>
1 parent 1a74c1c commit acf1cb1

File tree

3 files changed

+328
-0
lines changed

3 files changed

+328
-0
lines changed

config/families/sam3u.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ source = [
3131
'startup.c',
3232
'eefc.c',
3333
'wdt.c',
34+
'pmc.c',
3435
]
3536

3637
[dependencies]

src/platform/sam3u/pmc.c

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
/*
2+
* SPDX-License-Identifier: MIT
3+
* SPDX-FileCopyrightText: 2021 Rafael Silva <perigoso@riseup.net>
4+
*/
5+
6+
#include <sam.h>
7+
8+
#include "util/data.h"
9+
10+
#include "platform/sam3u/eefc.h"
11+
#include "platform/sam3u/pmc.h"
12+
13+
static struct pmc_clock_tree_t pmc_clock_tree;
14+
15+
void pmc_init(u32 hfxo_freq, u32 lfxo_freq)
16+
{
17+
if (hfxo_freq != 12000000UL) // this is a preset for 12MHz Crystal
18+
return;
19+
20+
pmc_clock_tree.hfxo_freq = hfxo_freq;
21+
pmc_clock_tree.lfxo_freq = lfxo_freq;
22+
23+
/* Configure flash waitstates */
24+
eefc_config_waitstates(96000000);
25+
26+
/* 12MHz external xtal, maximum possible startup time */
27+
pmc_hfxo_enable(false, 0xFF);
28+
29+
/* Select as main oscillator */
30+
pmc_mainck_source_set(PMC_MAINCK_HFXO);
31+
pmc_mck_source_set(PMC_MCK_MAINCK, 1);
32+
33+
/* Multiply by 8 for 96MHz */
34+
pmc_pllack_enable(8, 1);
35+
pmc_upllck_enable();
36+
37+
/* Select as MCK source */
38+
pmc_mck_source_set(PMC_MCK_PLLACK, 1);
39+
}
40+
41+
void pmc_update_clock_tree()
42+
{
43+
pmc_clock_tree.lfrco_freq = CHIP_FREQ_SLCK_RC;
44+
45+
switch (SUPC->SUPC_SR & SUPC_SR_OSCSEL) {
46+
case SUPC_SR_OSCSEL_RC:
47+
pmc_clock_tree.slck_freq = pmc_clock_tree.lfrco_freq;
48+
break;
49+
case SUPC_SR_OSCSEL_CRYST:
50+
pmc_clock_tree.slck_freq = pmc_clock_tree.lfxo_freq;
51+
break;
52+
}
53+
54+
switch (PMC->CKGR_MOR & CKGR_MOR_MOSCRCF_Msk) {
55+
case CKGR_MOR_MOSCRCF_4_MHz:
56+
pmc_clock_tree.hfrco_freq = CHIP_FREQ_MAINCK_RC_4MHZ;
57+
break;
58+
case CKGR_MOR_MOSCRCF_8_MHz:
59+
pmc_clock_tree.hfrco_freq = CHIP_FREQ_MAINCK_RC_8MHZ;
60+
break;
61+
case CKGR_MOR_MOSCRCF_12_MHz:
62+
pmc_clock_tree.hfrco_freq = CHIP_FREQ_MAINCK_RC_12MHZ;
63+
break;
64+
}
65+
66+
switch (PMC->CKGR_MOR & CKGR_MOR_MOSCSEL) {
67+
case 0:
68+
pmc_clock_tree.mainck_freq = pmc_clock_tree.hfrco_freq;
69+
break;
70+
case CKGR_MOR_MOSCSEL:
71+
pmc_clock_tree.mainck_freq = pmc_clock_tree.hfxo_freq;
72+
break;
73+
}
74+
75+
float plla_div = (PMC->CKGR_PLLAR & CKGR_PLLAR_DIVA_Msk) >> CKGR_PLLAR_DIVA_Pos;
76+
float plla_mul = (PMC->CKGR_PLLAR & CKGR_PLLAR_MULA_Msk) >> CKGR_PLLAR_MULA_Pos;
77+
78+
if (plla_div == 0 || plla_mul == 0)
79+
pmc_clock_tree.pllack_freq = 0;
80+
else
81+
pmc_clock_tree.pllack_freq = (uint32_t) (((float) pmc_clock_tree.mainck_freq / plla_div) * (plla_mul + 1.f));
82+
83+
if (!(PMC->CKGR_UCKR & CKGR_UCKR_UPLLEN))
84+
pmc_clock_tree.upllck_freq = 0;
85+
else
86+
pmc_clock_tree.upllck_freq = CHIP_FREQ_UTMIPLL;
87+
88+
switch (PMC->PMC_MCKR & PMC_MCKR_CSS_Msk) {
89+
case PMC_MCKR_CSS_SLOW_CLK:
90+
pmc_clock_tree.mck_freq = pmc_clock_tree.slck_freq;
91+
break;
92+
case PMC_MCKR_CSS_MAIN_CLK:
93+
pmc_clock_tree.mck_freq = pmc_clock_tree.mainck_freq;
94+
break;
95+
case PMC_MCKR_CSS_UPLL_CLK:
96+
pmc_clock_tree.mck_freq = pmc_clock_tree.upllck_freq / 2;
97+
break;
98+
case PMC_MCKR_CSS_PLLA_CLK:
99+
pmc_clock_tree.mck_freq = pmc_clock_tree.pllack_freq;
100+
break;
101+
}
102+
103+
switch (PMC->PMC_MCKR & PMC_MCKR_PRES_Msk) {
104+
case PMC_MCKR_PRES_CLK_1:
105+
pmc_clock_tree.mck_freq >>= 0;
106+
break;
107+
case PMC_MCKR_PRES_CLK_2:
108+
pmc_clock_tree.mck_freq >>= 1;
109+
break;
110+
case PMC_MCKR_PRES_CLK_4:
111+
pmc_clock_tree.mck_freq >>= 2;
112+
break;
113+
case PMC_MCKR_PRES_CLK_8:
114+
pmc_clock_tree.mck_freq >>= 3;
115+
break;
116+
case PMC_MCKR_PRES_CLK_16:
117+
pmc_clock_tree.mck_freq >>= 4;
118+
break;
119+
case PMC_MCKR_PRES_CLK_32:
120+
pmc_clock_tree.mck_freq >>= 5;
121+
break;
122+
case PMC_MCKR_PRES_CLK_64:
123+
pmc_clock_tree.mck_freq >>= 6;
124+
break;
125+
case PMC_MCKR_PRES_CLK_3:
126+
pmc_clock_tree.mck_freq /= 3;
127+
break;
128+
}
129+
130+
pmc_clock_tree.fclk_freq = pmc_clock_tree.mck_freq;
131+
}
132+
133+
const struct pmc_clock_tree_t *pmc_get_clock_tree()
134+
{
135+
return (const struct pmc_clock_tree_t *) &pmc_clock_tree;
136+
}
137+
138+
void pmc_hfxo_enable(bool bypass, u8 startup_time)
139+
{
140+
if (bypass) {
141+
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~(CKGR_MOR_MOSCXTEN)) | CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCXTBY;
142+
} else {
143+
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~(CKGR_MOR_MOSCXTST_Msk | CKGR_MOR_MOSCXTBY)) | CKGR_MOR_KEY_PASSWD |
144+
CKGR_MOR_MOSCXTEN | CKGR_MOR_MOSCXTST(startup_time);
145+
}
146+
147+
while (!(PMC->PMC_SR & PMC_SR_MOSCXTS)) continue;
148+
}
149+
150+
void pmc_hfxo_disable()
151+
{
152+
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~(CKGR_MOR_MOSCXTBY | CKGR_MOR_MOSCXTEN)) | CKGR_MOR_KEY_PASSWD;
153+
}
154+
155+
void pmc_hfrco_enable(enum pmc_hfrco_freq_t freq)
156+
{
157+
PMC->CKGR_MOR |= (CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCRCEN);
158+
159+
while (!(PMC->PMC_SR & PMC_SR_MOSCRCS)) continue;
160+
161+
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCRCF_Msk) | CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCRCF(freq);
162+
163+
while (!(PMC->PMC_SR & PMC_SR_MOSCRCS)) continue;
164+
}
165+
166+
void pmc_hfrco_disable()
167+
{
168+
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~(CKGR_MOR_MOSCRCEN | CKGR_MOR_MOSCRCF_Msk)) | CKGR_MOR_KEY_PASSWD;
169+
}
170+
171+
void pmc_pllack_enable(u8 mul, u8 div)
172+
{
173+
pmc_pllack_disable();
174+
175+
if (!mul || !div)
176+
mul = div = 1;
177+
178+
PMC->CKGR_PLLAR = CKGR_PLLAR_ONE | CKGR_PLLAR_MULA(mul - 1) | CKGR_PLLAR_DIVA(div) | CKGR_PLLAR_PLLACOUNT(63);
179+
180+
while (!(PMC->PMC_SR & PMC_SR_LOCKA)) continue;
181+
}
182+
183+
void pmc_pllack_disable()
184+
{
185+
PMC->CKGR_PLLAR = CKGR_PLLAR_ONE | CKGR_PLLAR_MULA(0) | CKGR_PLLAR_DIVA(0);
186+
}
187+
188+
void pmc_upllck_enable()
189+
{
190+
pmc_upllck_disable();
191+
192+
PMC->CKGR_UCKR = CKGR_UCKR_UPLLCOUNT(255) | CKGR_UCKR_UPLLEN;
193+
194+
while (!(PMC->PMC_SR & PMC_SR_LOCKU)) continue;
195+
}
196+
197+
void pmc_upllck_disable()
198+
{
199+
PMC->CKGR_UCKR &= ~CKGR_UCKR_UPLLEN;
200+
}
201+
202+
void pmc_slck_source_set(enum pmc_slck_src_t src)
203+
{
204+
if (src == PMC_SCLK_LFXO_BYPASS)
205+
SUPC->SUPC_MR |= (SUPC_MR_KEY_PASSWD | SUPC_MR_OSCBYPASS);
206+
else
207+
SUPC->SUPC_MR = (SUPC->SUPC_MR & ~SUPC_MR_OSCBYPASS) | SUPC_MR_KEY_PASSWD;
208+
209+
if (src == PMC_SCLK_LFRCO)
210+
SUPC->SUPC_CR = (SUPC->SUPC_CR & ~SUPC_CR_XTALSEL) | SUPC_CR_KEY_PASSWD;
211+
else
212+
SUPC->SUPC_CR |= (SUPC_CR_KEY_PASSWD | SUPC_CR_XTALSEL);
213+
}
214+
215+
void pmc_mainck_source_set(enum pmc_mainck_src_t src)
216+
{
217+
u32 ckgr_mor_val = 0;
218+
219+
if (src == PMC_MAINCK_HFXO)
220+
ckgr_mor_val = CKGR_MOR_MOSCSEL;
221+
222+
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCSEL) | CKGR_MOR_KEY_PASSWD | ckgr_mor_val;
223+
}
224+
225+
void pmc_mck_source_set(enum pmc_mck_src_t src, u8 prescaler)
226+
{
227+
if (!prescaler)
228+
prescaler = 1;
229+
230+
u32 pmc_mckr_val = 0;
231+
if (src == PMC_MCK_UPLLCK2)
232+
pmc_mckr_val = PMC_MCKR_UPLLDIV2;
233+
234+
PMC->PMC_MCKR = (PMC->PMC_MCKR & ~(PMC_MCKR_PRES_Msk | PMC_MCKR_CSS_Msk)) | PMC_MCKR_CSS(src) |
235+
PMC_MCKR_PRES(prescaler - 1) | pmc_mckr_val;
236+
237+
while (!(PMC->PMC_SR & PMC_SR_MCKRDY)) continue;
238+
}
239+
240+
void pmc_peripheral_clock_enable(u8 pid)
241+
{
242+
if (pid > 31)
243+
return;
244+
245+
PMC->PMC_PCER0 = 1 << pid;
246+
}
247+
248+
void pmc_peripheral_clock_disable(u8 pid)
249+
{
250+
if (pid > 31)
251+
return;
252+
253+
PMC->PMC_PCDR0 = 1 << pid;
254+
}

src/platform/sam3u/pmc.h

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* SPDX-License-Identifier: MIT
3+
* SPDX-FileCopyrightText: 2021 Rafael Silva <perigoso@riseup.net>
4+
*/
5+
6+
#pragma once
7+
8+
#include "sam.h"
9+
10+
#include <stdbool.h>
11+
12+
#include "util/types.h"
13+
14+
struct pmc_clock_tree_t {
15+
u32 hfxo_freq;
16+
u32 hfrco_freq;
17+
u32 lfxo_freq;
18+
u32 lfrco_freq;
19+
u32 slck_freq;
20+
u32 mainck_freq;
21+
u32 pllack_freq;
22+
u32 upllck_freq;
23+
u32 mck_freq;
24+
u32 fclk_freq;
25+
};
26+
27+
enum pmc_hfrco_freq_t {
28+
PMC_HFRCO_4MHZ = 0x0u,
29+
PMC_HFRCO_8MHZ = 0x1u,
30+
PMC_HFRCO_12MHZ = 0x2u,
31+
};
32+
33+
enum pmc_slck_src_t {
34+
PMC_SCLK_LFRCO = 0,
35+
PMC_SCLK_LFXO = 1,
36+
PMC_SCLK_LFXO_BYPASS = 2,
37+
};
38+
39+
enum pmc_mainck_src_t {
40+
PMC_MAINCK_HFRCO = 0,
41+
PMC_MAINCK_HFXO = 1,
42+
};
43+
44+
enum pmc_mck_src_t {
45+
PMC_MCK_SLCK = 0x0u,
46+
PMC_MCK_MAINCK = 0x1u,
47+
PMC_MCK_PLLACK = 0x2u,
48+
PMC_MCK_UPLLCK2 = 0x3u,
49+
};
50+
51+
void pmc_init(u32 hfxo_freq, u32 lfxo_freq);
52+
53+
void pmc_update_clock_tree();
54+
const struct pmc_clock_tree_t *pmc_get_clock_tree();
55+
56+
void pmc_hfxo_enable(bool bypass, u8 startup_time);
57+
void pmc_hfxo_disable();
58+
59+
void pmc_hfrco_enable(enum pmc_hfrco_freq_t freq);
60+
void pmc_hfrco_disable();
61+
62+
void pmc_pllack_enable(u8 mul, u8 div);
63+
void pmc_pllack_disable();
64+
65+
void pmc_upllck_enable();
66+
void pmc_upllck_disable();
67+
68+
void pmc_slck_source_set(enum pmc_slck_src_t src);
69+
void pmc_mainck_source_set(enum pmc_mainck_src_t src);
70+
void pmc_mck_source_set(enum pmc_mck_src_t src, u8 prescaler);
71+
72+
void pmc_peripheral_clock_enable(u8 pid);
73+
void pmc_peripheral_clock_disable(u8 pid);

0 commit comments

Comments
 (0)