blob: 5372fcc7759fa981c0261266eebbc92b4e2c20c9 [file] [log] [blame]
/****************************************************************************
* arch/arm/src/samd5e5/sam_clockconfig.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
#include "arm_internal.h"
#include "hardware/sam_pm.h"
#include "hardware/sam_supc.h"
#include "hardware/sam_oscctrl.h"
#include "hardware/sam_osc32kctrl.h"
#include "hardware/sam_gclk.h"
#include "hardware/sam_nvmctrl.h"
#include "sam_gclk.h"
#include "sam_periphclks.h"
#include <arch/board/board.h> /* Normally must be included last */
#include "sam_clockconfig.h" /* Needs to be included after board.h */
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Types
****************************************************************************/
/****************************************************************************
* Private Data
****************************************************************************/
/* This describes the power-up clock configuration using data provided in the
* board.h header file.
*/
static const struct sam_clockconfig_s g_initial_clocking =
{
.waitstates = BOARD_FLASH_WAITSTATES,
.cpudiv = BOARD_MCLK_CPUDIV,
.gclkset1 = BOARD_GCLK_SET1,
.gclkset2 = BOARD_GCLK_SET2,
.cpu_frequency = BOARD_CPU_FREQUENCY,
#if BOARD_HAVE_XOSC32K != 0
.xosc32k =
{
.enable = BOARD_XOSC32K_ENABLE,
.highspeed = BOARD_XOSC32K_HIGHSPEED,
.extalen = BOARD_XOSC32K_XTALEN,
.en32k = BOARD_XOSC32K_EN32K,
.en1k = BOARD_XOSC32K_EN1K,
.runstdby = BOARD_XOSC32K_RUNSTDBY,
.ondemand = BOARD_XOSC32K_ONDEMAND,
.cfden = BOARD_XOSC32K_CFDEN,
.cfdeo = BOARD_XOSC32K_CFDEO,
.caliben = BOARD_XOSC32K_CALIBEN,
.startup = BOARD_XOSC32K_STARTUP,
.calib = BOARD_XOSC32K_CALIB,
.rtcsel = BOARD_XOSC32K_RTCSEL,
},
#endif
#if BOARD_HAVE_XOSC0 != 0
.xosc0 =
{
.enable = BOARD_XOSC0_ENABLE,
.extalen = BOARD_XOSC0_XTALEN,
.runstdby = BOARD_XOSC0_RUNSTDBY,
.ondemand = BOARD_XOSC0_ONDEMAND,
.lowgain = BOARD_XOSC0_LOWGAIN,
.enalc = BOARD_XOSC0_ENALC,
.cfden = BOARD_XOSC0_CFDEN,
.startup = BOARD_XOSC0_STARTUP,
.xosc_frequency = BOARD_XOSC0_FREQUENCY,
},
#endif
#if BOARD_HAVE_XOSC1 != 0
.xosc1 =
{
.enable = BOARD_XOSC1_ENABLE,
.extalen = BOARD_XOSC1_XTALEN,
.runstdby = BOARD_XOSC1_RUNSTDBY,
.ondemand = BOARD_XOSC1_ONDEMAND,
.lowgain = BOARD_XOSC1_LOWGAIN,
.enalc = BOARD_XOSC1_ENALC,
.cfden = BOARD_XOSC1_CFDEN,
.startup = BOARD_XOSC1_STARTUP,
.xosc_frequency = BOARD_XOSC1_FREQUENCY,
},
#endif
.dfll =
{
.enable = BOARD_DFLL_ENABLE,
.runstdby = BOARD_DFLL_RUNSTDBY,
.ondemand = BOARD_DFLL_ONDEMAND,
.mode = BOARD_DFLL_MODE,
.stable = BOARD_DFLL_STABLE,
.llaw = BOARD_DFLL_LLAW,
.usbcrm = BOARD_DFLL_USBCRM,
.ccdis = BOARD_DFLL_CCDIS,
.qldis = BOARD_DFLL_QLDIS,
.bplckc = BOARD_DFLL_BPLCKC,
.waitlock = BOARD_DFLL_WAITLOCK,
.caliben = BOARD_DFLL_CALIBEN,
.gclklock = BOARD_DFLL_GCLKLOCK,
.fcalib = BOARD_DFLL_FCALIB,
.ccalib = BOARD_DFLL_CCALIB,
.fstep = BOARD_DFLL_FSTEP,
.cstep = BOARD_DFLL_CSTEP,
.gclk = BOARD_DFLL_GCLK,
.mul = BOARD_DFLL_MUL
},
.dpll =
{
{
.enable = BOARD_DPLL0_ENABLE,
.dcoen = BOARD_DPLL0_DCOEN,
.lbypass = BOARD_DPLL0_LBYPASS,
.wuf = BOARD_DPLL0_WUF,
.runstdby = BOARD_DPLL0_RUNSTDBY,
.ondemand = BOARD_DPLL0_ONDEMAND,
.reflock = BOARD_DPLL0_REFLOCK,
.refclk = BOARD_DPLL0_REFCLK,
.ltime = BOARD_DPLL0_LTIME,
.filter = BOARD_DPLL0_FILTER,
.dcofilter = BOARD_DPLL0_DCOFILTER,
.gclk = BOARD_DPLL0_GCLK,
.ldrfrac = BOARD_DPLL0_LDRFRAC,
.ldrint = BOARD_DPLL0_LDRINT,
.div = BOARD_DPLL0_DIV
},
{
.enable = BOARD_DPLL1_ENABLE,
.dcoen = BOARD_DPLL1_DCOEN,
.lbypass = BOARD_DPLL1_LBYPASS,
.wuf = BOARD_DPLL1_WUF,
.runstdby = BOARD_DPLL1_RUNSTDBY,
.ondemand = BOARD_DPLL1_ONDEMAND,
.reflock = BOARD_DPLL1_REFLOCK,
.refclk = BOARD_DPLL1_REFCLK,
.ltime = BOARD_DPLL1_LTIME,
.filter = BOARD_DPLL1_FILTER,
.dcofilter = BOARD_DPLL1_DCOFILTER,
.gclk = BOARD_DPLL1_GCLK,
.ldrfrac = BOARD_DPLL1_LDRFRAC,
.ldrint = BOARD_DPLL1_LDRINT,
.div = BOARD_DPLL1_DIV
}
},
.gclk =
{
{
.enable = BOARD_GCLK0_ENABLE,
.oov = BOARD_GCLK0_OOV,
.oe = BOARD_GCLK0_OE,
.runstdby = BOARD_GCLK0_RUNSTDBY,
.source = BOARD_GCLK0_SOURCE,
.div = BOARD_GCLK0_DIV
},
{
.enable = BOARD_GCLK1_ENABLE,
.oov = BOARD_GCLK1_OOV,
.oe = BOARD_GCLK1_OE,
.runstdby = BOARD_GCLK1_RUNSTDBY,
.source = BOARD_GCLK1_SOURCE,
.div = BOARD_GCLK1_DIV
},
{
.enable = BOARD_GCLK2_ENABLE,
.oov = BOARD_GCLK2_OOV,
.oe = BOARD_GCLK2_OE,
.runstdby = BOARD_GCLK2_RUNSTDBY,
.source = BOARD_GCLK2_SOURCE,
.div = BOARD_GCLK2_DIV
},
{
.enable = BOARD_GCLK3_ENABLE,
.oov = BOARD_GCLK3_OOV,
.oe = BOARD_GCLK3_OE,
.runstdby = BOARD_GCLK3_RUNSTDBY,
.source = BOARD_GCLK3_SOURCE,
.div = BOARD_GCLK3_DIV
},
{
.enable = BOARD_GCLK4_ENABLE,
.oov = BOARD_GCLK4_OOV,
.oe = BOARD_GCLK4_OE,
.runstdby = BOARD_GCLK4_RUNSTDBY,
.source = BOARD_GCLK4_SOURCE,
.div = BOARD_GCLK4_DIV
},
{
.enable = BOARD_GCLK5_ENABLE,
.oov = BOARD_GCLK5_OOV,
.oe = BOARD_GCLK5_OE,
.runstdby = BOARD_GCLK5_RUNSTDBY,
.source = BOARD_GCLK5_SOURCE,
.div = BOARD_GCLK5_DIV
},
{
.enable = BOARD_GCLK6_ENABLE,
.oov = BOARD_GCLK6_OOV,
.oe = BOARD_GCLK6_OE,
.runstdby = BOARD_GCLK6_RUNSTDBY,
.source = BOARD_GCLK6_SOURCE,
.div = BOARD_GCLK6_DIV
},
{
.enable = BOARD_GCLK7_ENABLE,
.oov = BOARD_GCLK7_OOV,
.oe = BOARD_GCLK7_OE,
.runstdby = BOARD_GCLK7_RUNSTDBY,
.source = BOARD_GCLK7_SOURCE,
.div = BOARD_GCLK7_DIV
},
{
.enable = BOARD_GCLK8_ENABLE,
.oov = BOARD_GCLK8_OOV,
.oe = BOARD_GCLK8_OE,
.runstdby = BOARD_GCLK8_RUNSTDBY,
.source = BOARD_GCLK8_SOURCE,
.div = BOARD_GCLK8_DIV
},
{
.enable = BOARD_GCLK9_ENABLE,
.oov = BOARD_GCLK9_OOV,
.oe = BOARD_GCLK9_OE,
.runstdby = BOARD_GCLK9_RUNSTDBY,
.source = BOARD_GCLK9_SOURCE,
.div = BOARD_GCLK9_DIV
},
{
.enable = BOARD_GCLK10_ENABLE,
.oov = BOARD_GCLK10_OOV,
.oe = BOARD_GCLK10_OE,
.runstdby = BOARD_GCLK10_RUNSTDBY,
.source = BOARD_GCLK10_SOURCE,
.div = BOARD_GCLK10_DIV
},
{
.enable = BOARD_GCLK11_ENABLE,
.oov = BOARD_GCLK11_OOV,
.oe = BOARD_GCLK11_OE,
.runstdby = BOARD_GCLK11_RUNSTDBY,
.source = BOARD_GCLK11_SOURCE,
.div = BOARD_GCLK11_DIV
}
}
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: sam_get_waitstates
*
* Description:
* Set the number of FLASH wait states.
*
****************************************************************************/
static uint16_t sam_get_waitstates(void)
{
uint16_t regval = getreg16(SAM_NVMCTRL_CTRLA);
return (regval & NVMCTRL_CTRLA_RWS_MASK) >> NVMCTRL_CTRLA_RWS_SHIFT;
}
/****************************************************************************
* Name: sam_set_waitstates
*
* Description:
* Set the number of FLASH wait states.
*
****************************************************************************/
static void sam_set_waitstates(const struct sam_clockconfig_s *config)
{
DEBUGASSERT(config->waitstates < 16);
modifyreg16(SAM_NVMCTRL_CTRLA, NVMCTRL_CTRLA_RWS_MASK,
NVMCTRL_CTRLA_RWS(config->waitstates));
}
/****************************************************************************
* Name: sam_xosc32k_configure
*
* Description:
* Configure XOSC32K
*
****************************************************************************/
#if BOARD_HAVE_XOSC32K != 0
static void sam_xosc32k_configure(const struct sam_xosc32_config_s *config)
{
uint32_t regval32;
uint32_t regval16;
uint32_t regval8;
/* Configure the OSC32KCTRL register */
regval16 = OSC32KCTRL_XOSC32K_STARTUP(config->startup);
if (config->enable)
{
regval16 |= OSC32KCTRL_XOSC32K_ENABLE;
}
if (config->highspeed)
{
regval16 |= OSC32KCTRL_XOSC32K_GCM_HS;
}
if (config->extalen)
{
regval16 |= OSC32KCTRL_XOSC32K_XTALEN;
}
if (config->en32k)
{
regval16 |= OSC32KCTRL_XOSC32K_EN32K;
}
if (config->en1k)
{
regval16 |= OSC32KCTRL_XOSC32K_EN1K;
}
if (config->runstdby)
{
regval16 |= OSC32KCTRL_XOSC32K_RUNSTDBY;
}
if (config->ondemand)
{
regval16 |= OSC32KCTRL_XOSC32K_ONDEMAND;
}
putreg16(regval16, SAM_OSC32KCTRL_XOSC32K);
/* Configure the CFDCTRL register */
regval8 = config->cfden ? OSC32KCTRL_CFDCTRL_CFDEN : 0;
putreg8(regval8, SAM_OSC32KCTRL_CFDCTRL);
regval8 = config->cfdeo ? OSC32KCTRL_EVCTRL_CFDEO : 0;
putreg8(regval8, SAM_OSC32KCTRL_EVCTRL);
/* Setup OSCULP32K calibration */
if (config->caliben)
{
regval32 = getreg32(SAM_OSC32KCTRL_OSCULP32K);
regval32 &= ~OSC32KCTRL_OSCULP32K_CALIB_MASK;
regval32 |= OSC32KCTRL_OSCULP32K_CALIB(config->calib);
}
/* Wait for XOSC32 to become ready if it was enabled */
if (config->enable && !config->ondemand)
{
while ((getreg32(SAM_OSC32KCTRL_STATUS) &
OSC32KCTRL_INT_XOSC32KRDY) == 0)
{
}
}
/* Set the RTC clock source */
putreg8(OSC32KCTRL_RTCCTRL_RTCSEL(config->rtcsel),
SAM_OSC32KCTRL_RTCCTRL);
}
#endif
/****************************************************************************
* Name: sam_xoscctrl
*
* Description:
* Get the appropriate settings for the XOSCCTRL register for XOSC0 or 1.
*
****************************************************************************/
#if BOARD_HAVE_XOSC0 != 0 || BOARD_HAVE_XOSC1 != 0
static uint32_t sam_xoscctrl(const struct sam_xosc_config_s *config)
{
uint32_t regval;
uint32_t cfdpresc;
uint32_t imult;
uint8_t iptat = 3;
/* Some settings determined by the crystal frequency */
if (config->xosc_frequency > 32000000)
{
cfdpresc = 0;
imult = 7;
iptat = 3;
}
else if (config->xosc_frequency > 24000000)
{
cfdpresc = 1;
imult = 6;
iptat = 3;
}
else if (config->xosc_frequency > 16000000)
{
cfdpresc = 2;
imult = 5;
iptat = 3;
}
else
{
cfdpresc = 3;
imult = 4;
iptat = 3;
}
/* Get the XOSCTCTL register *configuration */
regval = OSCCTRL_XOSCCTRL_IPTAT(iptat) | OSCCTRL_XOSCCTRL_IMULT(imult) |
OSCCTRL_XOSCCTRL_STARTUP(config->startup) |
OSCCTRL_XOSCCTRL_CFDPRESC(cfdpresc);
if (config->enable)
{
regval |= OSCCTRL_XOSCCTRL_ENABLE;
}
if (config->extalen)
{
regval |= OSCCTRL_XOSCCTRL_XTALEN;
}
if (config->runstdby)
{
regval |= OSCCTRL_XOSCCTRL_RUNSTDBY;
}
if (config->ondemand)
{
regval |= OSCCTRL_XOSCCTRL_ONDEMAND;
}
if (config->lowgain)
{
regval |= OSCCTRL_XOSCCTRL_LOWBUFGAIN;
}
if (config->enalc)
{
regval |= OSCCTRL_XOSCCTRL_ENALC;
}
if (config->cfden)
{
regval |= OSCCTRL_XOSCCTRL_CFDEN;
}
if (config->swben)
{
regval |= OSCCTRL_XOSCCTRL_SWBEN;
}
return regval;
}
#endif
/****************************************************************************
* Name: sam_xosc0_configure
*
* Description:
* Configure XOSC0K
*
****************************************************************************/
#if BOARD_HAVE_XOSC0 != 0
static void sam_xosc0_configure(const struct sam_xosc_config_s *config)
{
uint32_t regval;
/* Configure the XOSCTCTL register */
regval = sam_xoscctrl(config);
putreg32(regval, SAM_OSCCTRL_XOSCCTRL0);
/* Wait for XOSC0 to become ready if it was enabled */
if (config->enable)
{
while ((getreg32(SAM_OSCCTRL_STATUS) & OSCCTRL_INT_XOSCRDY0) == 0)
{
}
}
/* Re-select OnDemand */
if (config->ondemand)
{
regval = getreg32(SAM_OSCCTRL_XOSCCTRL0);
regval |= OSCCTRL_XOSCCTRL_ONDEMAND;
putreg32(regval, SAM_OSCCTRL_XOSCCTRL0);
}
}
#endif
#if BOARD_HAVE_XOSC1 != 0
void sam_xosc1_configure(const struct sam_xosc_config_s *config)
{
uint32_t regval;
/* Configure the XOSCTCTL register */
regval = sam_xoscctrl(config);
putreg32(regval, SAM_OSCCTRL_XOSCCTRL1);
/* Wait for XOSC1 to become ready if it was enabled */
if (config->enable)
{
while ((getreg32(SAM_OSCCTRL_STATUS) & OSCCTRL_INT_XOSCRDY1) == 0)
{
}
}
/* Re-select OnDemand */
if (config->ondemand)
{
regval = getreg32(SAM_OSCCTRL_XOSCCTRL1);
regval |= OSCCTRL_XOSCCTRL_ONDEMAND;
putreg32(regval, SAM_OSCCTRL_XOSCCTRL1);
}
}
#endif
/****************************************************************************
* Name: sam_mclk_configure
*
* Description:
* Configure master clock generator
*
****************************************************************************/
static inline void sam_mclk_configure(uint8_t cpudiv)
{
putreg8(cpudiv, SAM_MCLK_CPUDIV);
}
/****************************************************************************
* Name: sam_gclkset_configure
*
* Description:
* Configure a set of GCLKs
*
****************************************************************************/
static void sam_gclkset_configure(uint16_t gclkset,
const struct sam_gclk_config_s *config)
{
uint16_t mask;
int gclk;
/* Try every GCLK */
for (gclk = 0; gclk < SAM_NGCLK && gclkset != 0; gclk++)
{
/* Check if this one is in the set */
mask = 1 << gclk;
if ((gclkset & mask) != 0)
{
/* Yes.. Remove it from the set and configure it */
gclkset &= ~mask;
sam_gclk_configure(gclk, &config[gclk]);
}
}
}
/****************************************************************************
* Name: sam_dfll_configure, sam_dfll_ready, and sam_dfll_gclkready
*
* Description:
* Configure the DFLL
*
****************************************************************************/
static void sam_dfll_configure(const struct sam_dfll_config_s *config)
{
uint32_t regval32;
uint8_t regval8;
/* Set GCLK0 source to OSCULP32K (temporarily) */
regval32 = getreg32(SAM_GCLK_GENCTRL(0));
regval32 &= ~GCLK_GENCTRL_SRC_MASK;
regval32 |= GCLK_GENCTRL_SRC_OSCULP32K;
putreg32(regval32, SAM_GCLK_GENCTRL(0));
/* Disable the DFLL */
putreg8(0, SAM_OSCCTRL_DFLLCTRLA);
/* If we are running in closed loop mode and we are in USB clock recover
* mode, then set up the input source GCLK channel (unless it has already
* been configured and the configuration is locked).
*/
if (config->usbcrm && config->mode &&
!sam_gclk_chan_locked(GCLK_CHAN_OSCCTRL_DFLL))
{
/* Configure the DFLL GCLK channel to use the GCLK source. */
sam_gclk_chan_enable(GCLK_CHAN_OSCCTRL_DFLL, config->gclk,
(bool)config->gclklock);
}
/* Setup the DFLLMUL register */
regval32 = OSCCTRL_DFLLMUL_MUL(config->mul) |
OSCCTRL_DFLLMUL_FSTEP(config->fstep) |
OSCCTRL_DFLLMUL_CSTEP(config->cstep);
putreg32(regval32, SAM_OSCCTRL_DFLLMUL);
/* Wait until the multiplier is synchronized */
do
{
regval8 = getreg32(SAM_OSCCTRL_DFLLSYNC);
regval8 &= OSCCTRL_DFLLSYNC_DFLLMUL;
}
while (regval8 != 0);
/* Reset the DFLLCTRLB register */
putreg32(0, SAM_OSCCTRL_DFLLCTRLB);
do
{
regval8 = getreg8(SAM_OSCCTRL_DFLLSYNC);
regval8 &= OSCCTRL_DFLLSYNC_DFLLCTRLB;
}
while (regval8 != 0);
/* Set up the DFLLCTRLA register */
regval8 = OSCCTRL_DPLLCTRLA_ENABLE;
if (config->runstdby)
{
regval8 |= OSCCTRL_DPLLCTRLA_RUNSTDBY;
}
putreg8(regval8, SAM_OSCCTRL_DFLLCTRLA);
do
{
regval8 = getreg8(SAM_OSCCTRL_DFLLSYNC);
regval8 &= OSCCTRL_DPLLCTRLA_ENABLE;
}
while (regval8 != 0);
/* Overwrite factory calibration values is so requested */
if (config->caliben)
{
regval32 = OSCCTRL_DFLLVAL_FINE(config->fcalib) |
OSCCTRL_DFLLVAL_COARSE(config->ccalib);
putreg32(regval32, SAM_OSCCTRL_DFLLVAL);
}
else
{
regval32 = getreg32(SAM_OSCCTRL_DFLLVAL);
}
/* Writing to the DFLLVAL will force it to re-synchronize */
putreg32(regval32, SAM_OSCCTRL_DFLLVAL);
do
{
regval8 = getreg8(SAM_OSCCTRL_DFLLSYNC);
regval8 &= OSCCTRL_DFLLSYNC_DFLLVAL;
}
while (regval8 != 0);
/* Setup the DFLLCTRLB register */
regval8 = 0;
if (config->mode)
{
regval8 |= OSCCTRL_DFLLCTRLB_MODE;
}
if (config->stable)
{
regval8 |= OSCCTRL_DFLLCTRLB_STABLE;
}
if (config->llaw)
{
regval8 |= OSCCTRL_DFLLCTRLB_LLAW;
}
if (config->usbcrm)
{
regval8 |= OSCCTRL_DFLLCTRLB_USBCRM;
}
if (config->ccdis)
{
regval8 |= OSCCTRL_DFLLCTRLB_CCDIS;
}
if (config->qldis)
{
regval8 |= OSCCTRL_DFLLCTRLB_QLDIS;
}
if (config->bplckc)
{
regval8 |= OSCCTRL_DFLLCTRLB_BPLCKC;
}
if (config->waitlock)
{
regval8 |= OSCCTRL_DFLLCTRLB_WAITLOCK;
}
putreg8(regval8, SAM_OSCCTRL_DPLL0CTRLB);
do
{
regval8 = getreg8(SAM_OSCCTRL_DFLLSYNC);
regval8 &= OSCCTRL_DFLLSYNC_DFLLCTRLB;
}
while (regval8 != 0);
}
static void sam_dfll_ready(const struct sam_dfll_config_s *config)
{
uint32_t ready;
uint32_t regval32;
uint8_t regval8;
/* Check if the mode bit was set, i.e., we are in closed-loop mode. If so
* wait for the DFLL to be ready for and for the coarse lock to be
* obtained.
*/
regval8 = getreg8(SAM_OSCCTRL_DPLL0CTRLB);
if ((regval8 & OSCCTRL_DFLLCTRLB_MODE) != 0)
{
ready = OSCCTRL_INT_DFLLRDY | OSCCTRL_INT_DFLLLCKC;
}
/* In open-loop mode, wait only for DFLL ready */
else
{
ready = OSCCTRL_INT_DFLLRDY;
}
/* Wait for whichever ready condition */
do
{
regval32 = getreg32(SAM_OSCCTRL_STATUS);
regval32 &= ready;
}
while (regval32 != ready);
/* Now, we can set the OnDemand bit in the DFLLCTRLA */
if (config->ondemand)
{
regval8 = getreg8(SAM_OSCCTRL_DFLLCTRLA);
regval8 |= OSCCTRL_DFLLCTRLA_ONDEMAND;
putreg8(regval8, SAM_OSCCTRL_DFLLCTRLA);
}
}
static void sam_dfll_gclkready(const struct sam_dfll_config_s *config)
{
uint32_t regval32;
/* Wait until all GCLKs are synchronized */
while (getreg32(SAM_GCLK_SYNCHBUSY) != 0)
{
}
/* Set the source of GCLK0 to to the configured source. */
regval32 = getreg32(SAM_GCLK_GENCTRL(0));
regval32 &= ~GCLK_GENCTRL_SRC_MASK;
regval32 |= GCLK_GENCTRL_SRC(config->gclk);
putreg32(regval32, SAM_GCLK_GENCTRL(0));
}
/****************************************************************************
* Name: sam_dpll_configure and sam_dpll_ready
*
* Description:
* Configure a DPLL, DPLL0 or DPLL1
*
****************************************************************************/
static void sam_dpll_gclkchannel(uint8_t chan,
const struct sam_dpll_config_s *config)
{
/* Check if we are using a dedicated GCLK as the reference clock. If so
* configure GCLK unless it has already been configured and configuration
* registers have been locked.
*/
if (config->refclk == 0 && !sam_gclk_chan_locked(chan))
{
/* Yes.. configure the GCLK channel that will be used as
* refclk source.
*/
sam_gclk_chan_enable(chan, config->gclk, (bool)config->reflock);
}
}
static void sam_dpll_configure(uintptr_t base,
const struct sam_dpll_config_s *config)
{
uint32_t regval;
/* Set up the DPLL ratio control register */
regval = OSCCTRL_DPLLRATIO_LDR(config->ldrint) |
OSCCTRL_DPLLRATIO_LDRFRAC(config->ldrfrac);
putreg32(regval, base + SAM_OSCCTRL_DPLLRATIO_OFFSET);
/* Set up the DPLL control B register */
regval = OSCCTRL_DPLLCTRLB_FILTER(config->filter) |
OSCCTRL_DPLLCTRLB_REFLCK(config->refclk) |
OSCCTRL_DPLLCTRLB_LTIME(config->ltime) |
OSCCTRL_DPLLCTRLB_DCOFILTER(config->dcofilter) |
OSCCTRL_DPLLCTRLB_DIV(config->div);
if (config->wuf)
{
regval |= OSCCTRL_DPLLCTRLB_WUF;
}
if (config->lbypass)
{
regval |= OSCCTRL_DPLLCTRLB_LBYPASS;
}
if (config->dcoen)
{
regval |= OSCCTRL_DPLLCTRLB_DCOEN;
}
putreg32(regval, base + SAM_OSCCTRL_DPLLCTRLB_OFFSET);
/* Set up the DPLL control A register */
regval = 0;
if (config->enable)
{
regval |= OSCCTRL_DFLLCTRLA_ENABLE;
}
if (config->runstdby)
{
regval |= OSCCTRL_DFLLCTRLA_RUNSTDBY;
}
putreg32(regval, base + SAM_OSCCTRL_DPLLCTRLA_OFFSET);
}
static void sam_dpll_ready(uintptr_t base,
const struct sam_dpll_config_s *config)
{
uint32_t regval;
/* If the DPLL was enabled, then wait for it to lock and for the clock to
* be ready.
*/
if (config->enable)
{
uint32_t lockready = (OSCCTRL_DPLLSTATUS_LOCK |
OSCCTRL_DPLLSTATUS_CLKRDY);
do
{
regval = getreg32(base + SAM_OSCCTRL_DPLLSTATUS_OFFSET);
regval &= lockready;
}
while (regval != lockready);
}
/* Finally, set the OnDemand bit if selected */
if (config->ondemand)
{
regval = getreg32(base + SAM_OSCCTRL_DPLLCTRLA_OFFSET);
regval |= OSCCTRL_DFLLCTRLA_ONDEMAND;
putreg32(regval, base + SAM_OSCCTRL_DPLLCTRLA_OFFSET);
}
}
/****************************************************************************
* Name: sam_loop_configure
*
* Description:
* Configure all loops: DFLL, DPLL0, and DPLL1
*
****************************************************************************/
static void sam_loop_configure(const struct sam_clockconfig_s *config)
{
/* Configure and enable all loops */
sam_dfll_configure(&config->dfll);
sam_dpll_gclkchannel(GCLK_CHAN_OSCCTRL_DPLL0, &config->dpll[0]);
sam_dpll_configure(SAM_OSCCTRL_DPLL0_BASE, &config->dpll[0]);
sam_dpll_gclkchannel(GCLK_CHAN_OSCCTRL_DPLL1, &config->dpll[1]);
sam_dpll_configure(SAM_OSCCTRL_DPLL1_BASE, &config->dpll[1]);
/* Wait for them to become ready */
sam_dfll_ready(&config->dfll);
sam_dpll_ready(SAM_OSCCTRL_DPLL0_BASE, &config->dpll[0]);
sam_dpll_ready(SAM_OSCCTRL_DPLL1_BASE, &config->dpll[1]);
sam_dfll_gclkready(&config->dfll);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: sam_clock_configure
*
* Description:
* Configure the clock sub-system per the provided configuration data.
*
* This should be called only (1) early in the initialization sequence, or
* (2) later but within a critical section.
*
****************************************************************************/
void sam_clock_configure(const struct sam_clockconfig_s *config)
{
uint16_t waitstates;
/* Check if the number of wait states is increasing or decreasing */
waitstates = sam_get_waitstates();
if (config->waitstates > waitstates)
{
/* Increasing. Set the new number of wait states before configuring
* the clocking.
*/
sam_set_waitstates(config);
}
/* REVISIT: This function is intended to support both the power-up reset
* clock configuration as well as the subsequent re-configuration of the
* clocking. Logic is here dependent upon the initial clock state. In
* order to successfully reconfigure the clocks, I suspect that it will be
* necessary to always restore the power-up clock configuration here before
* setting the new clock configuration.
*/
#if BOARD_HAVE_XOSC32K != 0
/* Configure XOSC32 */
sam_xosc32k_configure(&config->xosc32k);
#endif
#if BOARD_HAVE_XOSC0 != 0
/* Configure XOSC0 */
sam_xosc0_configure(&config->xosc0);
#endif
#if BOARD_HAVE_XOSC1 != 0
/* Configure XOSC1 */
sam_xosc1_configure(&config->xosc1);
#endif
/* Configure master clock generator */
sam_mclk_configure(config->cpudiv);
/* Pre-configure some GCLKs before configuring the DPLLs */
sam_gclkset_configure(config->gclkset1, config->gclk);
/* Configure loops: DFLL, DPLL0, and DPLL1. */
sam_loop_configure(config);
/* Configure the renaming GCLKs before configuring the DPLLs */
sam_gclkset_configure(config->gclkset2, config->gclk);
/* Check if the number of wait states is increasing or decreasing */
if (config->waitstates < waitstates)
{
/* Decreasing. Set the new number of wait states after configuring
* the clocking.
*/
sam_set_waitstates(config);
}
}
/****************************************************************************
* Name: sam_clock_initialize
*
* Description:
* Configure the initial power-up clocking. This function may be called
* only once by the power-up reset logic.
*
****************************************************************************/
void sam_clock_initialize(void)
{
sam_clock_configure(&g_initial_clocking);
}