summaryrefslogtreecommitdiff
path: root/nuttx/arch/arm/src/samd
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2014-02-15 10:18:05 -0600
committerGregory Nutt <gnutt@nuttx.org>2014-02-15 10:18:05 -0600
commit863c05abb3dfcd2ed6ee86298799f9ef9424a907 (patch)
tree4e205dcb12a9ae2824ec9be7c3da0ccf282b16e9 /nuttx/arch/arm/src/samd
parent873a20d5a2669918f3fa82489f1322f82a9619a2 (diff)
downloadnuttx-863c05abb3dfcd2ed6ee86298799f9ef9424a907.tar.gz
nuttx-863c05abb3dfcd2ed6ee86298799f9ef9424a907.tar.bz2
nuttx-863c05abb3dfcd2ed6ee86298799f9ef9424a907.zip
SAMD20: Finishes basic clock configuration logic
Diffstat (limited to 'nuttx/arch/arm/src/samd')
-rw-r--r--nuttx/arch/arm/src/samd/sam_clockconfig.c410
1 files changed, 397 insertions, 13 deletions
diff --git a/nuttx/arch/arm/src/samd/sam_clockconfig.c b/nuttx/arch/arm/src/samd/sam_clockconfig.c
index f7126c895..136b3f63d 100644
--- a/nuttx/arch/arm/src/samd/sam_clockconfig.c
+++ b/nuttx/arch/arm/src/samd/sam_clockconfig.c
@@ -49,6 +49,7 @@
#include <nuttx/config.h>
#include <stdint.h>
+#include <stdbool.h>
#include <assert.h>
#include "up_arch.h"
@@ -68,8 +69,166 @@
****************************************************************************/
/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This structure describes the configuration of on GCLK */
+
+#ifdef BOARD_GCLK_ENABLE
+struct sam_gclkconfig_s
+{
+ uint8_t gclk; /* Clock generator */
+ bool runstandby; /* Run clock in standby */
+ bool output; /* Output enable */
+ uint8_t clksrc; /* Encoded clock source */
+ uint16_t prescaler; /* Prescaler value */
+};
+#endif
+
+/****************************************************************************
* Private Data
****************************************************************************/
+/* This structure describes the configuration of every enabled GCLK */
+
+#ifdef BOARD_GCLK_ENABLE
+static const struct sam_gclkconfig_s g_gclkconfig[] =
+{
+ /* GCLK generator 0 (Main Clock) */
+
+ {
+ .gclk = 0,
+#ifdef BOARD_GCLK0_RUN_IN_STANDBY
+ .runstandby = true,
+#endif
+#ifdef BOARD_GCLK0_OUTPUT_ENABLE
+ .output = true,
+#endif
+ .prescaler = BOARD_GCLK0_PRESCALER,
+ .clksrc = (uint8_t)(BOARD_GCLK0_CLOCK_SOURCE >> GCLK_GENCTRL_SRC_SHIFT),
+ }
+
+ /* GCLK generator 1 */
+
+#ifdef BOARD_GCLK1_ENABLE
+ ,
+ {
+ .gclk = 1,
+#ifdef BOARD_GCLK1_RUN_IN_STANDBY
+ .runstandby = true;
+#endif
+#ifdef BOARD_GCLK1_OUTPUT_ENABLE
+ .output = true;
+#endif
+ .prescaler = BOARD_GCLK1_PRESCALER,
+ .clksrc = (uint8_t)(BOARD_GCLK1_CLOCK_SOURCE >> GCLK_GENCTRL_SRC_SHIFT),
+ }
+#endif
+
+ /* GCLK generator 2 (RTC) */
+
+#ifdef BOARD_GCLK2_ENABLE
+ ,
+ {
+ .gclk = 2,
+#ifdef BOARD_GCLK2_RUN_IN_STANDBY
+ .runstandby = true;
+#endif
+#ifdef BOARD_GCLK2_OUTPUT_ENABLE
+ .output = true;
+#endif
+ .prescaler = BOARD_GCLK2_PRESCALER,
+ .clksrc = (uint8_t)(BOARD_GCLK2_CLOCK_SOURCE >> GCLK_GENCTRL_SRC_SHIFT),
+ }
+#endif
+
+ /* GCLK generator 3 */
+
+#ifdef BOARD_GCLK3_ENABLE
+ ,
+ {
+ .gclk = 3,
+#ifdef BOARD_GCLK3_RUN_IN_STANDBY
+ .runstandby = true;
+#endif
+#ifdef BOARD_GCLK3_OUTPUT_ENABLE
+ .output = true;
+#endif
+ .prescaler = BOARD_GCLK3_PRESCALER,
+ .clksrc = (uint8_t)(BOARD_GCLK3_CLOCK_SOURCE >> GCLK_GENCTRL_SRC_SHIFT),
+ }
+#endif
+
+ /* GCLK generator 4 */
+
+#ifdef BOARD_GCLK4_ENABLE
+ ,
+ {
+ .gclk = 4,
+#ifdef BOARD_GCLK4_RUN_IN_STANDBY
+ .runstandby = true;
+#endif
+#ifdef BOARD_GCLK4_OUTPUT_ENABLE
+ .output = true;
+#endif
+ .prescaler = BOARD_GCLK4_PRESCALER,
+ .clksrc = (uint8_t)(BOARD_GCLK4_CLOCK_SOURCE >> GCLK_GENCTRL_SRC_SHIFT),
+ }
+#endif
+
+ /* GCLK generator 5 */
+
+#ifdef BOARD_GCLK5_ENABLE
+ ,
+ {
+ .gclk = 5,
+#ifdef BOARD_GCLK5_RUN_IN_STANDBY
+ .runstandby = true;
+#endif
+#ifdef BOARD_GCLK5_OUTPUT_ENABLE
+ .output = true;
+#endif
+ .prescaler = BOARD_GCLK5_PRESCALER,
+ .clksrc = (uint8_t)(BOARD_GCLK5_CLOCK_SOURCE >> GCLK_GENCTRL_SRC_SHIFT),
+ }
+#endif
+
+ /* GCLK generator 6 */
+
+#ifdef BOARD_GCLK6_ENABLE
+ ,
+ {
+ .gclk = 6,
+#ifdef BOARD_GCLK6_RUN_IN_STANDBY
+ .runstandby = true;
+#endif
+#ifdef BOARD_GCLK6_OUTPUT_ENABLE
+ .output = true;
+#endif
+ .prescaler = BOARD_GCLK6_PRESCALER,
+ .clksrc = (uint8_t)(BOARD_GCLK6_CLOCK_SOURCE >> GCLK_GENCTRL_SRC_SHIFT),
+ }
+#endif
+
+ /* GCLK generator 7 */
+
+#ifdef BOARD_GCLK7_ENABLE
+ ,
+ {
+ .gclk = 7,
+#ifdef BOARD_GCLK7_RUN_IN_STANDBY
+ .runstandby = true;
+#endif
+#ifdef BOARD_GCLK7_OUTPUT_ENABLE
+ .output = true;
+#endif
+ .prescaler = BOARD_GCLK7_PRESCALER,
+ .clksrc = (uint8_t)(BOARD_GCLK7_CLOCK_SOURCE >> GCLK_GENCTRL_SRC_SHIFT),
+ }
+#endif
+};
+
+#define NGCLKS_ENABLED (sizeof(g_gclkconfig) / sizeof(struct sam_gclkconfig_s))
+#endif
/****************************************************************************
* Private Functions
@@ -125,7 +284,7 @@ static inline void sam_flash_waitstates(void)
*
****************************************************************************/
-#if defined(CONFIG_SAMD_XOSC) || defined(BOARD_XOSC_ENABLE)
+#ifdef BOARD_XOSC_ENABLE
static inline void sam_xosc_config(void)
{
uint16_t regval;
@@ -207,7 +366,7 @@ static inline void sam_xosc_config(void)
*
****************************************************************************/
-#if defined(CONFIG_SAMD_XOSC32K) || defined(BOARD_XOSC32K_ENABLE)
+#ifdef BOARD_XOSC32K_ENABLE
static inline void sam_xosc32k_config(void)
{
uint16_t regval;
@@ -274,7 +433,7 @@ static inline void sam_xosc32k_config(void)
*
****************************************************************************/
-#if defined(CONFIG_SAMD_OSC32K) || defined(BOARD_OSC32K_ENABLE)
+#ifdef BOARD_OSC32K_ENABLE
static inline void sam_osc32k_config(void)
{
uint32_t regval;
@@ -376,10 +535,15 @@ static inline void sam_osc8m_config(void)
* BOARD_DFLL_COARSEVALUE - Value
* BOARD_DFLL_FINEVALUE - Value
*
- * Closed Loop mode only:
+ * Open Loop mode only:
+ * BOARD_DFLL_COARSEVALUE - Value
+ * BOARD_DFLL_FINEVALUE - Value
+ *
+ * Closed loop mode only:
+ * BOARD_DFLL_SRCGCLKGEN - See GCLK_CLKCTRL_GEN* definitions
+ * BOARD_DFLL_MULTIPLIER - Value
* BOARD_DFLL_MAXCOARSESTEP - Value
* BOARD_DFLL_MAXFINESTEP - Value
- * BOARD_DFLL_MULTIPLIER - Value
*
* Input Parameters:
* None
@@ -389,7 +553,7 @@ static inline void sam_osc8m_config(void)
*
****************************************************************************/
-#if defined(CONFIG_SAMD_DFLL) || defined(BOARD_DFLL_ENABLE)
+#ifdef BOARD_DFLL_ENABLE
static inline void sam_dfll_config(void)
{
uint16_t control;
@@ -456,13 +620,199 @@ static inline void sam_dfll_config(void)
#endif
/****************************************************************************
- * Name: sam_gclk_config
+ * Name: sam_dfll_reference
+ *
+ * Description:
+ * Enable DFLL reference clock if in closed loop mode.
+ * Depends on:
+ *
+ * BOARD_DFLL_SRCGCLKGEN - See GCLK_CLKCTRL_GEN* definitions
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#if defined(BOARD_GCLK_ENABLE) && defined(BOARD_DFLL_ENABLE) && \
+ !defined(BOARD_DFLL_OPENLOOP)
+static inline void sam_dfll_reference(void)
+{
+ uint16_t regval;
+
+ /* Disabled the generic clock */
+
+ regval = GCLK_CLKCTRL_GEN0;
+ putreg16(regval, SAM_GCLK_CLKCTRL);
+
+ /* Wait for the clock to become disabled */
+
+ while ((getreg16(SAM_GCLK_CLKCTRL) & GCLK_CLKCTRL_CLKEN) != 0);
+
+ /* Select the configured clock generator and configure the GCLK output
+ * (always Generic clock generator 0)
+ *
+ * NOTE: We could enable write lock here to prevent further modification
+ */
+
+ regval = (GCLK_CLKCTRL_GEN0 | BOARD_DFLL_SRCGCLKGEN);
+ putreg16(regval, SAM_GCLK_CLKCTRL);
+
+ /* Enable the generic clock */
+
+ regval |= GCLK_CLKCTRL_CLKEN;
+ putreg16(regval, SAM_GCLK_CLKCTRL);
+}
+#else
+# define sam_dfll_reference()
+#endif
+
+/****************************************************************************
+ * Name: sam_config_gclks
+ *
+ * Description:
+ * Configure a single GCLK(s) based on settings in the board.h header file.
+ * Depends on:
+ *
+ * BOARD_GCLKn_RUN_IN_STANDBY - Boolean (defined / not defined)
+ * BOARD_GCLKn_CLOCK_SOURCE - See GCLK_GENCTRL_SRC_* definitions
+ * BOARD_GCLKn_PRESCALER - Value
+ * BOARD_GCLKn_OUTPUT_ENABLE - Boolean (defined / not defined)
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#ifdef BOARD_GCLK_ENABLE
+static inline void sam_gclk_config(FAR const struct sam_gclkconfig_s *config)
+{
+ uint32_t genctrl;
+ uint32_t gendiv;
+
+ /* Select the requested source clock for the generator */
+
+ genctrl = ((uint32_t)config->gclk << GCLK_GENCTRL_ID_SHIFT) |
+ ((uint32_t)config->clksrc << GCLK_GENCTRL_SRC_SHIFT);
+ gendiv = ((uint32_t)config->gclk << GCLK_GENDIV_ID_SHIFT);
+
+#if 0 /* Not yet supported */
+ /* Configure the clock to be either high or low when disabled */
+
+ if (config->level)
+ {
+ genctrl |= GCLK_GENCTRL_OOV;
+ }
+#endif
+
+ /* Configure if the clock output to I/O pin should be enabled */
+
+ if (config->output)
+ {
+ genctrl |= GCLK_GENCTRL_OE;
+ }
+
+ /* Set the prescaler division factor */
+
+ if (config->prescaler > 1)
+ {
+ /* Check if division is a power of two */
+
+ if (((config->prescaler & (config->prescaler - 1)) == 0))
+ {
+ /* Determine the index of the highest bit set to get the
+ * division factor that must be loaded into the division
+ * register.
+ */
+
+ uint32_t count = 0;
+ uint32_t mask;
+
+ for (mask = 2; mask < (uint32_t)config->prescaler; mask <<= 1)
+ {
+ count++;
+ }
+
+ /* Set binary divider power of 2 division factor */
+
+ gendiv |= count << GCLK_GENDIV_DIV_SHIFT;
+ genctrl |= GCLK_GENCTRL_DIVSEL;
+ }
+ else
+ {
+ /* Set integer division factor */
+
+ gendiv |= GCLK_GENDIV_DIV((uint32_t)config->prescaler);
+
+ /* Enable non-binary division with increased duty cycle accuracy */
+
+ genctrl |= GCLK_GENCTRL_IDC;
+ }
+ }
+
+ /* Enable or disable the clock in standby mode */
+
+ if (config->runstandby)
+ {
+ genctrl |= GCLK_GENCTRL_RUNSTDBY;
+ }
+
+ /* Wait for synchronization */
+
+ while ((getreg8(SAM_GCLK_STATUS) & GCLK_STATUS_SYNCBUSY) != 0);
+
+ /* Select the generator */
+
+ putreg32(((uint32_t)config->gclk << GCLK_GENDIV_ID_SHIFT),
+ SAM_GCLK_GENDIV);
+
+ /* Wait for synchronization */
+
+ while ((getreg8(SAM_GCLK_STATUS) & GCLK_STATUS_SYNCBUSY) != 0);
+
+ /* Write the new generator configuration */
+
+ putreg32(gendiv, SAM_GCLK_GENDIV);
+
+ /* Wait for synchronization */
+
+ while ((getreg8(SAM_GCLK_STATUS) & GCLK_STATUS_SYNCBUSY) != 0);
+
+ /* Enable the clock generator */
+
+ genctrl |= GCLK_GENCTRL_GENEN;
+ putreg16(genctrl, SAM_GCLK_GENCTRL);
+
+ /* Wait for synchronization */
+
+ while ((getreg8(SAM_GCLK_STATUS) & GCLK_STATUS_SYNCBUSY) != 0);
+}
+#endif
+
+/****************************************************************************
+ * Name: sam_config_gclks
*
* Description:
* Configure GCLK(s) based on settings in the board.h header file.
* Depends on:
*
- *
+ * Global enable/disable.
+ *
+ * BOARD_GCLK_ENABLE - Boolean (defined / not defined)
+ *
+ * For n=1-7:
+ * BOARD_GCLKn_ENABLE - Boolean (defined / not defined)
+ *
+ * For n=0-8:
+ * BOARD_GCLKn_RUN_IN_STANDBY - Boolean (defined / not defined)
+ * BOARD_GCLKn_CLOCK_SOURCE - See GCLK_GENCTRL_SRC_* definitions
+ * BOARD_GCLKn_PRESCALER - Value
+ * BOARD_GCLKn_OUTPUT_ENABLE - Boolean (defined / not defined)
*
* Input Parameters:
* None
@@ -472,13 +822,47 @@ static inline void sam_dfll_config(void)
*
****************************************************************************/
-#if defined(CONFIG_SAMD_GCLK) || defined(BOARD_GCLK_ENABLE)
-static inline void sam_gclk_config(void)
+#ifdef BOARD_GCLK_ENABLE
+static inline void sam_config_gclks(void)
{
-#warning Missing logic
+ uint32_t regval;
+ int i;
+
+ /* Turn on the GCLK interface clock */
+
+ regval = getreg32(SAM_PM_APBAMASK);
+ regval |= PM_APBAMASK_GCLK;
+ putreg32(regval, SAM_PM_APBAMASK);
+
+ /* Reset the GCLK module */
+
+ putreg8(GCLK_CTRL_SWRST, SAM_GCLK_CTRL);
+
+ /* Wait for the reset to complete */
+
+ while ((getreg8(SAM_GCLK_CTRL) & GCLK_CTRL_SWRST) != 0);
+
+ /* Configure all GCLK generators, skipping GLCK_MAIN which is configured
+ * below.
+ */
+
+ for (i = 1; i < NGCLKS_ENABLED; i++)
+ {
+ sam_gclk_config(&g_gclkconfig[i]);
+ }
+
+ /* Enable DFLL reference clock if the DFLL is enabled in closed loop mode */
+
+ sam_dfll_reference();
+
+ /* Configure the GCLK_MAIN last as it may depend on the DFLL or other
+ * generators
+ */
+
+ sam_gclk_config(&g_gclkconfig[0]);
}
#else
-# define sam_gclk_config()
+# define sam_config_gclks()
#endif
/****************************************************************************
@@ -579,7 +963,7 @@ void sam_clockconfig(void)
/* Configure GCLK(s) */
- sam_gclk_config();
+ sam_config_gclks();
/* Set CPU and BUS clock dividers */