Skip to content

Commit

Permalink
cpu/samd5x: fix CPU init
Browse files Browse the repository at this point in the history
There were still some things wrong with samd5x CPU init which only
showed up when used in conjunction with RIOTBOOT, that is cpu_init()
was called twice.

 - gclk_connect() should block until the GCLK is ready.
 - DPLL should be disabled dring configuration.
 - make sure not to use DPLL for MCLK when re-configuring DPLL
 - All APBxMASK bits should be in a defined state.
 - always enable 1kHz oscilator output.
  • Loading branch information
benpicco committed Jun 20, 2019
1 parent c332514 commit f29ca15
Showing 1 changed file with 46 additions and 31 deletions.
77 changes: 46 additions & 31 deletions cpu/samd5x/cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,16 @@

static void xosc32k_init(void)
{
OSC32KCTRL->XOSC32K.reg = OSC32KCTRL_XOSC32K_ENABLE | OSC32KCTRL_XOSC32K_XTALEN
| OSC32KCTRL_XOSC32K_EN32K | OSC32KCTRL_XOSC32K_RUNSTDBY
| OSC32KCTRL_XOSC32K_STARTUP(7);
OSC32KCTRL->XOSC32K.reg = OSC32KCTRL_XOSC32K_ENABLE
| OSC32KCTRL_XOSC32K_EN1K
| OSC32KCTRL_XOSC32K_EN32K
| OSC32KCTRL_XOSC32K_RUNSTDBY
| OSC32KCTRL_XOSC32K_XTALEN
| OSC32KCTRL_XOSC32K_STARTUP(7);

while (!OSC32KCTRL->STATUS.bit.XOSC32KRDY) {}
}

#ifdef MODULE_PERIPH_USBDEV
static void dfll_init(void)
{
uint32_t reg = OSCCTRL_DFLLCTRLB_QLDIS
Expand All @@ -41,30 +43,41 @@ static void dfll_init(void)

OSCCTRL->DFLLCTRLB.reg = reg;
OSCCTRL->DFLLCTRLA.reg = OSCCTRL_DFLLCTRLA_ENABLE;

while (!OSCCTRL->STATUS.bit.DFLLRDY) {}
}
#endif

static void fdpll0_init(uint32_t f_cpu)
{
/* We source the DPLL from 32kHz GCLK1 */
const uint32_t LDR = ((f_cpu << 5) / 32768);

/* disable the DPLL before changing the configuration */
OSCCTRL->Dpll[0].DPLLCTRLA.bit.ENABLE = 0;
while (OSCCTRL->Dpll[0].DPLLSYNCBUSY.reg) {}

/* set DPLL clock source */
GCLK->PCHCTRL[OSCCTRL_GCLK_ID_FDPLL0].reg = GCLK_PCHCTRL_GEN(1) | GCLK_PCHCTRL_CHEN;
while (!(GCLK->PCHCTRL[OSCCTRL_GCLK_ID_FDPLL0].reg & GCLK_PCHCTRL_CHEN)) {}

OSCCTRL->Dpll[0].DPLLRATIO.reg = OSCCTRL_DPLLRATIO_LDRFRAC(LDR & 0x1F)
| OSCCTRL_DPLLRATIO_LDR((LDR >> 5) - 1);
| OSCCTRL_DPLLRATIO_LDR((LDR >> 5) - 1);

/* Without LBYPASS, startup takes very long, see errata section 2.13. */
OSCCTRL->Dpll[0].DPLLCTRLB.reg = OSCCTRL_DPLLCTRLB_REFCLK_GCLK
| OSCCTRL_DPLLCTRLB_WUF
| OSCCTRL_DPLLCTRLB_LBYPASS;
| OSCCTRL_DPLLCTRLB_WUF
| OSCCTRL_DPLLCTRLB_LBYPASS;

OSCCTRL->Dpll[0].DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_ENABLE;

while (OSCCTRL->Dpll[0].DPLLSYNCBUSY.reg) {}
while (!(OSCCTRL->Dpll[0].DPLLSTATUS.bit.CLKRDY &&
OSCCTRL->Dpll[0].DPLLSTATUS.bit.LOCK)) {}
}

static void gclk_connect(uint8_t id, uint8_t src, uint32_t flags) {
GCLK->GENCTRL[id].reg = GCLK_GENCTRL_SRC(src) | GCLK_GENCTRL_GENEN | flags | GCLK_GENCTRL_IDC;
while (GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL(id)) {}
}

/**
Expand All @@ -77,51 +90,53 @@ void cpu_init(void)

/* turn on only needed APB peripherals */
MCLK->APBAMASK.reg = MCLK_APBAMASK_MCLK
| MCLK_APBAMASK_OSCCTRL
| MCLK_APBAMASK_OSC32KCTRL
| MCLK_APBAMASK_GCLK
| MCLK_APBAMASK_OSCCTRL
| MCLK_APBAMASK_OSC32KCTRL
| MCLK_APBAMASK_GCLK
| MCLK_APBAMASK_SUPC
| MCLK_APBAMASK_PAC
#ifdef MODULE_PERIPH_PM
| MCLK_APBAMASK_PM
#endif
#ifdef MODULE_PERIPH_GPIO_IRQ
| MCLK_APBAMASK_EIC
| MCLK_APBAMASK_EIC
#endif
;

MCLK->APBBMASK.reg = 0
#ifdef MODULE_PERIPH_FLASHPAGE
| MCLK_APBBMASK_NVMCTRL
#endif
;
#ifdef MODULE_PERIPH_GPIO
MCLK->APBBMASK.reg = MCLK_APBBMASK_PORT;
| MCLK_APBBMASK_PORT
#endif
;

MCLK->APBCMASK.reg = 0;
MCLK->APBDMASK.reg = 0;

/* enable the Cortex M Cache Controller */
CMCC->CTRL.bit.CEN = 1;

/* Software reset the GCLK module to ensure it is re-initialized correctly */
GCLK->CTRLA.reg = GCLK_CTRLA_SWRST;
while (GCLK->CTRLA.reg & GCLK_CTRLA_SWRST) {}
while (GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_SWRST) {}

xosc32k_init();
gclk_connect(1, GCLK_SOURCE_XOSC32K, 0);

/* make sure main clock is not sourced from DPLL */
dfll_init();
gclk_connect(0, GCLK_SOURCE_DFLL, 0);

fdpll0_init(CLOCK_CORECLOCK);

/* main clock */
/* source main clock from DPLL */
gclk_connect(0, GCLK_SOURCE_DPLL0, 0);

/* clock used by xtimer */
gclk_connect(5, GCLK_SOURCE_DPLL0, GCLK_GENCTRL_DIV(CLOCK_CORECLOCK / 8000000));

#ifdef MODULE_PERIPH_USBDEV
dfll_init();
gclk_connect(6, GCLK_SOURCE_DFLL, 0);
#endif

while (GCLK->SYNCBUSY.reg) {}

#ifdef MODULE_PERIPH_PM
/* enable power managemet module */
MCLK->APBAMASK.reg |= MCLK_APBAMASK_PM;
#endif
#ifdef MODULE_PERIPH_FLASHPAGE
MCLK->APBBMASK.reg |= MCLK_APBBMASK_NVMCTRL;
#endif

/* trigger static peripheral initialization */
periph_init();
}

0 comments on commit f29ca15

Please sign in to comment.