/** * @brief Initialize the CPU, set IRQ priorities */ void cpu_init(void) { /* disable the watchdog timer */ WDT->WDT_MR |= WDT_MR_WDDIS; /* initialize the Cortex-M core */ cortexm_init(); /* setup the flash wait states */ EFC0->EEFC_FMR = EEFC_FMR_FWS(CLOCK_FWS); EFC1->EEFC_FMR = EEFC_FMR_FWS(CLOCK_FWS); /* unlock write protect register for PMC module */ PMC->PMC_WPMR = PMC_WPMR_WPKEY(WPKEY); /* activate the external crystal */ PMC->CKGR_MOR = (CKGR_MOR_KEY(MORKEY) | CKGR_MOR_MOSCXTST(XTAL_STARTUP) | CKGR_MOR_MOSCXTEN | CKGR_MOR_MOSCRCEN); /* wait for crystal to be stable */ while (!(PMC->PMC_SR & PMC_SR_MOSCXTS)); /* select crystal to clock the main clock */ PMC->CKGR_MOR = (CKGR_MOR_KEY(MORKEY) | CKGR_MOR_MOSCXTST(XTAL_STARTUP) | CKGR_MOR_MOSCXTEN | CKGR_MOR_MOSCRCEN | CKGR_MOR_MOSCSEL); /* wait for main oscillator selection to be complete */ while (!(PMC->PMC_SR & PMC_SR_MOSCSELS)); /* setup PLLA */ PMC->CKGR_PLLAR = (CKGR_PLLAR_ONE | CKGR_PLLAR_PLLACOUNT(PLL_CNT) | CKGR_PLLAR_MULA(CLOCK_PLL_MUL) | CKGR_PLLAR_DIVA(CLOCK_PLL_DIV)); /* wait for PLL to lock */ while (!(PMC->PMC_SR & PMC_SR_LOCKA)); /* before switching to PLLA, we need to switch to main clock */ PMC->PMC_MCKR = PMC_MCKR_CSS_MAIN_CLK; while (!(PMC->PMC_SR & PMC_SR_MCKRDY)); /* use PLLA as main clock source */ PMC->PMC_MCKR = PMC_MCKR_CSS_PLLA_CLK; /* wait for master clock to be ready */ while (!(PMC->PMC_SR & PMC_SR_MCKRDY)); /* trigger static peripheral initialization */ periph_init(); }
/** * Set new system clock * * @param drv_info - PMC driver info * @param new_clk - clock, encodded as follow: * - bit [1:0] - clock source (MCKR_CSS) * * Details: * * Switching from one clock source and prescaler to another cannot be done in a * single step. And when we change only the source or only the prescaler we must * ensure that the intermediate combination is allowed.\n * Also this function is run from the flash, so we should avoid forbidden * frequencies. For example SAM7S64 series have a bug and they can't work in the * 3-19MHz range. \n * * Algorithm: * - STEP 1. If we have to change the clock source, we switch first to a known * good slow clock. The slow clock can be embedded oscillator (RC, fast RC or * main) and the selection is defined from the application in the PMC_INFO. * */ void sys_SetNewClock(PMC_INFO drv_info, unsigned int new_clk) { unsigned int temp; Pmc* pPMC = drv_info->hw_base; pPMC->PMC_WPMR = PMC_WPMR_WPKEY(0x504D43); // STEP 1: If new source != old source => switch to slow clock //prepare main clock sys_SetMainClock(pPMC, drv_info->CFG_CKGR_MOR); temp = pPMC->PMC_MCKR; if((new_clk ^ temp) & PMC_MCKR_CSS_Msk) { temp &= ~ PMC_MCKR_CSS_Msk; if(new_clk & PMC_MCKR_CSS_Msk) { //switch to main clock temp |= PMC_MCKR_CSS_MAIN_CLK; } else { //switch to slow clock //JTAG can get lost if current prescaler is too high } pPMC->PMC_MCKR = temp; //wait for clock switch while(!(pPMC->PMC_SR & PMC_SR_MCKRDY)); // we are now using main/slow source, so we can set the prescaler temp &= ~PMC_MCKR_PRES_Msk; temp |= new_clk & PMC_MCKR_PRES_Msk; pPMC->PMC_MCKR = temp; //JTAG can get lost now... //prescaller change may not be completed } else { // the source is OK, only the prescaller is about to change.. // If the new clock is lower, we will change the divisor here // before we calculate and change the flash wait states. if(new_clk > temp) { pPMC->PMC_MCKR = new_clk; } } //wait until main clock is stable.... while(!(pPMC->PMC_SR & PMC_SR_MCKRDY)); // STEP 2: We are now running in slow clock or we have reduced the clock // From now on, we will only increase the clock, but before that // we must prepare the hardware (wait states and osc/pll) //calculate the flash wait states <20MHz=0 >30MHz=2 if((new_clk & PMC_MCKR_CSS_Msk) > PMC_MCKR_CSS_MAIN_CLK) temp = EEFC_FMR_FWS(2); else temp = EEFC_FMR_FWS(0); EFC->EEFC_FMR = (EFC->EEFC_FMR & ~EEFC_FMR_FWS_Msk) | temp; //check the source if((new_clk ^ pPMC->PMC_MCKR ) & PMC_MCKR_CSS_Msk) { // start pll if required if((new_clk & PMC_MCKR_CSS_Msk) == PMC_MCKR_CSS_PLLA_CLK) { // Start PLL pPMC->CKGR_PLLAR = drv_info->CFG_CKGR_PLLAR; // Wait PLL while(!(pPMC->PMC_SR & PMC_SR_LOCKA)); } } //STEP 3: set the desired clock and divider if it is not done by now. // here the new source is ok... // and we know that either the prescaler, either the source is OK (or both) pPMC->PMC_MCKR = new_clk; //wait until main clock is stable.... while(!(pPMC->PMC_SR & PMC_SR_MCKRDY)); //some cleanup... temp = sizeof(pPMC->PMC_PCK)/4; while(temp--) { new_clk |= pPMC->PMC_PCK[temp]; } new_clk &= PMC_MCKR_CSS_Msk; //turn off PLL A if(new_clk != PMC_MCKR_CSS_PLLA_CLK) pPMC->CKGR_PLLAR = drv_info->CFG_CKGR_PLLAR & ~CKGR_PLLAR_MULA_Msk; if(new_clk == PMC_MCKR_CSS_SLOW_CLK) pPMC->CKGR_MOR = 0; //turn off oscillator new_clk=pPMC->PMC_MCKR; temp = drv_info->CFG_FREQUENCIES[new_clk & PMC_MCKR_CSS_Msk]; temp >>= (new_clk & PMC_MCKR_PRES_Msk) >> 2; system_clock_frequency = temp; }