/** @addtogroup scif * * @brief Access functions for the SAM4 System Controf Interface (SCIF) * @ingroup SAM4 * LGPL License Terms @ref lgpl_license * @author @htmlonly © @endhtmlonly 2016 * Maxim Sloyko * */ /* * This file is part of the libopencm3 project. * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . */ #include /** @brief Enable external oscillator. * * @param[in] mode enum osc_mode: Oscillator mode (which pins oscillator connected to). * @param[in] freq uint32_t: External Oscillator frequency, in Hertz. Must be 0.6MHz - 30MHz * @param[in] startup enum osc_startup: Oscillator start time in RCSYS clock cycles. * * @returns zero upon success. */ int scif_osc_enable(enum osc_mode mode, uint32_t freq, enum osc_startup startup) { uint8_t gain; const uint32_t kHz = 1000; const uint32_t MHz = 1000 * kHz; if (freq > 600 * kHz && freq <= 2 * MHz) { gain = 0; } else if (freq > 2 * MHz && freq <= 4 * MHz) { gain = 1; } else if (freq > 4 * MHz && freq <= 8 * MHz) { gain = 2; } else if (freq > 8 * MHz && freq <= 16 * MHz) { gain = 3; } else if (freq > 16 * MHz && freq <= 30 * MHz) { gain = 4; } else { return -1; } SCIF_UNLOCK = SCIF_OSCCTRL0_KEY; SCIF_OSCCTRL0 = mode | SCIF_OSCCTRL_OSCEN | (gain << SCIF_OSCCTRL_GAIN_SHIFT) | (startup << SCIF_OSCCTRL_STARTUP_SHIFT); while (!(SCIF_PCLKSR & SCIF_OSC0RDY)); return 0; } /** @brief Configure and enable PLL clock. * * @param[in] delay uint8_t: Specifies the number of RCSYS clock cycles before * ISR.PLLLOCKn will be set after PLL has been written, or after PLL has * been automatically re-enabled after exiting a sleep mode. * @param[in] mul uint8_t: Multiply factor. * @param[in] div uint8_t: Division factor.These fields determine the ratio of * the PLL output frequency to the source oscillator frequency: * f_vco = (PLLMUL+1)/PLLDIV * f_ref if PLLDIV >0 * f_vco = 2*(PLLMUL+1) * f_ref if PLLDIV = 0 * Note that the PLLMUL field should always be greater than 1 or the * behavior of the PLL will be undefined. * @param[in] pll_opt uint8_t: PLL Options. * * @returns zero upon success. */ int scif_enable_pll(uint8_t delay, uint8_t mul, uint8_t div, uint8_t pll_opt, enum pll_clk_src source_clock) { // First, PLL needs to be disabled, otherwise the configuration register // is unaccessible. uint32_t pll_val = SCIF_PLL0; if (pll_val & SCIF_PLL0_PLLEN) { SCIF_UNLOCK = SCIF_PLL0_KEY; SCIF_PLL0 = pll_val & (~SCIF_PLL0_PLLEN); } if (mul == 0) mul = 1; pll_val = SCIF_PLL0_PLLOSC_MASKED(source_clock) | SCIF_PLL0_PLLOPT_MASKED(pll_opt) | SCIF_PLL0_PLLDIV_MASKED(div) | SCIF_PLL0_PLLMUL_MASKED(mul) | SCIF_PLL0_PLLCOUNT_MASKED(delay); SCIF_UNLOCK = SCIF_PLL0_KEY; SCIF_PLL0 = pll_val; // Now enable TODO: does this really need to be separate operation? SCIF_UNLOCK = SCIF_PLL0_KEY; SCIF_PLL0 = pll_val | SCIF_PLL0_PLLEN; while(!(SCIF_PCLKSR & SCIF_PLL0LOCK)); return 0; } /** @brief Configure and enable Generic Clock * * @param[in] gclk enum generic_clock: Generic Clock to configure and enable. * @param[in] source_clock enum gclk_src: Source Clock for this Generic Clock. * @param[in] div uint16_t: Division Factor. Upper 8 bits only used for Generic Clock 11, * If 0, clock is undivided. */ void scif_enable_gclk(enum generic_clock gclk, enum gclk_src source_clock, uint16_t div) { uint32_t reg_val = SCIF_GCCTRL_CEN | SCIF_GCCTRL_OSCSEL_MASKED(source_clock); if (div) { if (gclk < GENERIC_CLOCK11) { div &= 0xf; } reg_val |= SCIF_GCCTRL_DIV_MASKED(div) | SCIF_GCCTRL_DIVEN; } SCIF_GCTRL(gclk) = reg_val; }