void select_clock(const clock_config_t *cfg) { uint32_t new_qspi_div = 0; uint32_t old_qspi_div = SPI0_REG(SPI_REG_SCKDIV); /* Clock based on external escilator */ if (!cfg->xosc || cfg->pll) { /* Turn on internal oscillator */ PRCI_REG(PRCI_HFROSCCFG) = ROSC_DIV(cfg->osc_div) | ROSC_TRIM(MYNEWT_VAL(HFROSC_DEFAULT_TRIM_VAL)) | ROSC_EN(1); while ((PRCI_REG(PRCI_HFROSCCFG) & ROSC_RDY(1)) == 0) { } /* Compute CPU frequency on demand */ cpu_freq = 0; } if (cfg->xosc) { /* Turn on external oscillator if not ready yet */ if ((PRCI_REG(PRCI_HFXOSCCFG) & XOSC_RDY(1)) == 0) { PRCI_REG(PRCI_HFXOSCCFG) = XOSC_EN(1); while ((PRCI_REG(PRCI_HFXOSCCFG) & XOSC_RDY(1)) == 0) { } } cpu_freq = cfg->frq; } /* * If reqested closk is higher then FLASH_MAX_CLOCK change divider so QSPI * clock is in rage. */ if (cfg->frq >= MYNEWT_VAL(FLASH_MAX_CLOCK) * 2) { new_qspi_div = (cfg->frq + MYNEWT_VAL(FLASH_MAX_CLOCK) - 1) / (2 * MYNEWT_VAL(FLASH_MAX_CLOCK)) - 1; } /* New qspi divider is higher, reduce qspi clock before switching to higher clock */ if (new_qspi_div > old_qspi_div) { SPI0_REG(SPI_REG_SCKDIV) = new_qspi_div; } PRCI_REG(PRCI_PLLDIV) = PLL_FINAL_DIV_BY_1(cfg->pll_outdiv1) | PLL_FINAL_DIV(cfg->pll_out_div); if (cfg->pll) { uint32_t now; uint32_t pllcfg = PLL_REFSEL(cfg->xosc) | PLL_R(cfg->pll_div_r) | PLL_F(cfg->pll_mul_f) | PLL_Q(cfg->pll_div_q); PRCI_REG(PRCI_PLLCFG) = PLL_BYPASS(1) | pllcfg; PRCI_REG(PRCI_PLLCFG) ^= PLL_BYPASS(1); /* 100us lock grace period */ now = mtime_lo(); while (mtime_lo() - now < 4) { } /* Now it is safe to check for PLL Lock */ while ((PRCI_REG(PRCI_PLLCFG) & PLL_LOCK(1)) == 0) { } /* Select PLL as clock source */ PRCI_REG(PRCI_PLLCFG) |= PLL_SEL(1) | pllcfg; } else { /* Select bypassed PLL as source signal, it allows to use HFXOSC */ PRCI_REG(PRCI_PLLCFG) = PLL_BYPASS(1) | PLL_REFSEL(cfg->xosc) | PLL_SEL(1); } /* * Old qspi divider was higher, now it's safe to reduce divider, increasing * qspi clock for better performance */ if (new_qspi_div < old_qspi_div) { SPI0_REG(SPI_REG_SCKDIV) = new_qspi_div; } }
void use_pll(int refsel, int bypass, int r, int f, int q) { // Ensure that we aren't running off the PLL before we mess with it. if (PRCI_REG(PRCI_PLLCFG) & PLL_SEL(1)) { // Make sure the HFROSC is running at its default setting use_hfrosc(4, 16); } // Set PLL Source to be HFXOSC if available. uint32_t config_value = 0; config_value |= PLL_REFSEL(refsel); if (bypass) { // Bypass config_value |= PLL_BYPASS(1); PRCI_REG(PRCI_PLLCFG) = config_value; // If we don't have an HFXTAL, this doesn't really matter. // Set our Final output divide to divide-by-1: PRCI_REG(PRCI_PLLDIV) = (PLL_FINAL_DIV_BY_1(1) | PLL_FINAL_DIV(0)); } else { // In case we are executing from QSPI, // (which is quite likely) we need to // set the QSPI clock divider appropriately // before boosting the clock frequency. // Div = f_sck/2 SPI0_REG(SPI_REG_SCKDIV) = 8; // Set DIV Settings for PLL // Both HFROSC and HFXOSC are modeled as ideal // 16MHz sources (assuming dividers are set properly for // HFROSC). // (Legal values of f_REF are 6-48MHz) // Set DIVR to divide-by-2 to get 8MHz frequency // (legal values of f_R are 6-12 MHz) config_value |= PLL_BYPASS(1); config_value |= PLL_R(r); // Set DIVF to get 512Mhz frequncy // There is an implied multiply-by-2, 16Mhz. // So need to write 32-1 // (legal values of f_F are 384-768 MHz) config_value |= PLL_F(f); // Set DIVQ to divide-by-2 to get 256 MHz frequency // (legal values of f_Q are 50-400Mhz) config_value |= PLL_Q(q); // Set our Final output divide to divide-by-1: PRCI_REG(PRCI_PLLDIV) = (PLL_FINAL_DIV_BY_1(1) | PLL_FINAL_DIV(0)); PRCI_REG(PRCI_PLLCFG) = config_value; // Un-Bypass the PLL. PRCI_REG(PRCI_PLLCFG) &= ~PLL_BYPASS(1); // Wait for PLL Lock // Note that the Lock signal can be glitchy. // Need to wait 100 us // RTC is running at 32kHz. // So wait 4 ticks of RTC. uint32_t now = mtime_lo(); while (mtime_lo() - now < 4) ; // Now it is safe to check for PLL Lock while ((PRCI_REG(PRCI_PLLCFG) & PLL_LOCK(1)) == 0) ; } // Switch over to PLL Clock source PRCI_REG(PRCI_PLLCFG) |= PLL_SEL(1); }