aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarti Bolivar <mbolivar@leaflabs.com>2012-01-27 21:31:30 -0500
committerMarti Bolivar <mbolivar@leaflabs.com>2012-04-11 16:56:54 -0400
commit4dc4d99fdddffdeb3e14e722e935c76c74ff9a15 (patch)
tree639198c2e597d44f03739134f71e2fb59c9a0c29
parentb5a8e0386d5134839bf23e82110d2f1926201202 (diff)
downloadlibrambutan-4dc4d99fdddffdeb3e14e722e935c76c74ff9a15.tar.gz
librambutan-4dc4d99fdddffdeb3e14e722e935c76c74ff9a15.zip
RCC: Clean up and sanitize interfaces across F1, F2 series.
Additions: - rcc_switch_sysclk(): For changing the clock used as SYSCLK's source. - enum rcc_clk: One for each system and secondary clock source (e.g. HSE, LSE). These are defined on a per-series basis in each of the <series/rcc.h>. - rcc_turn_on_clk(), rcc_turn_off_clk(), rcc_is_clk_ready(): For turning on system and secondary clock sources, and checking whether or not they're ready. Uses enum rcc_clk. Removals: - rcc_clk_init(): There's no way to port this to F2. Move it to the F1 header. This also means we can remove the empty implementation and enum rcc_pll_multiplier from the F2 RCC header, where it doesn't make any sense. Also fix up some includes, and rewrite rcc_clk_init() in terms of the new clock source management functions. Signed-off-by: Marti Bolivar <mbolivar@leaflabs.com>
-rw-r--r--libmaple/include/libmaple/rcc.h59
-rw-r--r--libmaple/rcc.c116
-rw-r--r--libmaple/stm32f1/include/series/rcc.h31
-rw-r--r--libmaple/stm32f1/rcc.c26
-rw-r--r--libmaple/stm32f2/include/series/rcc.h35
-rw-r--r--libmaple/stm32f2/rcc.c6
6 files changed, 224 insertions, 49 deletions
diff --git a/libmaple/include/libmaple/rcc.h b/libmaple/include/libmaple/rcc.h
index 08f7c7e..842800b 100644
--- a/libmaple/include/libmaple/rcc.h
+++ b/libmaple/include/libmaple/rcc.h
@@ -37,42 +37,63 @@ extern "C"{
#endif
#include <libmaple/libmaple_types.h>
+
+/* Put the SYSCLK sources before the series header is included, as it
+ * might need them. */
+/**
+ * @brief SYSCLK sources
+ * @see rcc_clk_init()
+ */
+typedef enum rcc_sysclk_src {
+ RCC_CLKSRC_HSI = 0x0,
+ RCC_CLKSRC_HSE = 0x1,
+ RCC_CLKSRC_PLL = 0x2,
+} rcc_sysclk_src;
+
#include <series/rcc.h>
/* Note: Beyond the usual (registers, etc.), it's up to the series
* header to define the following types:
*
- * - rcc_pllsrc: For each PLL source (passed to rcc_clk_init()).
+ * - enum rcc_clk: Available system and secondary clock sources,
+ * e.g. RCC_CLK_HSE, RCC_CLK_PLL, RCC_CLK_LSE.
+ *
+ * Note that the inclusion of secondary clock sources (like LSI and
+ * LSE) makes enum rcc_clk different from the SYSCLK sources, which
+ * are defined in this header as enum rcc_sysclk_src.
*
- * - rcc_pll_multiplier: If appropriate (TODO verify this makes sense).
+ * IMPORTANT NOTE TO IMPLEMENTORS: If you are adding support for a
+ * new STM32 series, see the comment near rcc_clk_reg() in
+ * libmaple/rcc.c for information on how to choose these values so
+ * that rcc_turn_on_clk() etc. will work on your series.
*
- * - rcc_clk_id: For each available peripheral. These are widely used
+ * - enum rcc_clk_id: For each available peripheral. These are widely used
* as unique IDs (TODO extricate from RCC?). Peripherals which are
- * common across families should use the same token for their
+ * common across STM32 series should use the same token for their
* rcc_clk_id in each series header.
*
- * - rcc_clk_domain: For each clock domain (returned by rcc_dev_clk()).
+ * - enum rcc_clk_domain: For each clock domain. This is returned by
+ * rcc_dev_clk(). For instance, each AHB and APB is a clock domain.
*
- * - rcc_prescaler (and a suitable set of dividers): for rcc_set_prescaler().
+ * - enum rcc_prescaler: And a suitable set of dividers for
+ * rcc_set_prescaler().
*/
-/**
- * SYSCLK sources
- * @see rcc_clk_init()
- */
-typedef enum rcc_sysclk_src {
- RCC_CLKSRC_HSI = 0x0,
- RCC_CLKSRC_HSE = 0x1,
- RCC_CLKSRC_PLL = 0x2,
-} rcc_sysclk_src;
+/* Clock prescaler management. */
+void rcc_set_prescaler(rcc_prescaler prescaler, uint32 divider);
+
+/* SYSCLK. */
+void rcc_switch_sysclk(rcc_sysclk_src sysclk_src);
-void rcc_clk_init(rcc_sysclk_src sysclk_src,
- rcc_pllsrc pll_src,
- rcc_pll_multiplier pll_mul);
+/* System and secondary clock sources. */
+void rcc_turn_on_clk(rcc_clk clock);
+void rcc_turn_off_clk(rcc_clk clock);
+int rcc_is_clk_ready(rcc_clk clock);
+
+/* Peripheral clock lines and clock domains. */
void rcc_clk_enable(rcc_clk_id device);
void rcc_reset_dev(rcc_clk_id device);
rcc_clk_domain rcc_dev_clk(rcc_clk_id device);
-void rcc_set_prescaler(rcc_prescaler prescaler, uint32 divider);
#ifdef __cplusplus
} // extern "C"
diff --git a/libmaple/rcc.c b/libmaple/rcc.c
index d6ed2d6..d42bbf1 100644
--- a/libmaple/rcc.c
+++ b/libmaple/rcc.c
@@ -42,3 +42,119 @@
rcc_clk_domain rcc_dev_clk(rcc_clk_id id) {
return rcc_dev_table[id].clk_domain;
}
+
+/**
+ * @brief Switch the clock used as the source of the system clock.
+ *
+ * After switching the source, this function blocks until the new
+ * clock source is in use.
+ *
+ * @param sysclk_src New system clock source.
+ * @see rcc_sysclk_src
+ */
+void rcc_switch_sysclk(rcc_sysclk_src sysclk_src) {
+ uint32 cfgr = RCC_BASE->CFGR;
+ cfgr &= ~RCC_CFGR_SW;
+ cfgr |= sysclk_src;
+
+ /* Switch SYSCLK source. */
+ RCC_BASE->CFGR = cfgr;
+
+ /* Wait for new source to come into use. */
+ while ((RCC_BASE->CFGR & RCC_CFGR_SWS) != (sysclk_src << 2))
+ ;
+}
+
+/*
+ * Turning clocks off and on, querying their status.
+ */
+
+/* IMPORTANT NOTE FOR IMPLEMENTORS:
+ *
+ * libmaple assumes that enum rcc_clk enumerators are two-byte
+ * values, stored in a uint16, in the following way:
+ *
+ * - The high-order byte is the byte offset (from RCC_BASE) of the register
+ * to touch when turning on or off the given clock.
+ *
+ * - The low-order byte is the bit in that register that turns the
+ * clock on or off.
+ *
+ * Example for STM32F1: Turning on the high-speed external clock (HSE)
+ * involves setting HSEON, bit 16, of RCC_CR. The high-order byte is
+ * then offsetof(struct rcc_reg_map, CR) = 0, and the low-order byte
+ * is 16.
+ *
+ * The corresponding value of RCC_CLK_HSE is thus (0 << 8) | 16 = 16.
+ *
+ * On all known STM32 series, this encoding has the property that
+ * adding one to the low byte also gives the bit to check to determine
+ * if the clock is ready. For example, on STM32F1, RCC_CR_HSERDY is
+ * bit 17. If that's not the case on your ser ies, rcc_is_clk_ready()
+ * won't work for you. */
+
+/* Returns the RCC register which controls the clock source. */
+static inline __io uint32* rcc_clk_reg(rcc_clk clock) {
+ return (__io uint32*)((__io uint8*)RCC_BASE + (clock >> 8));
+}
+
+/* Returns a mask in rcc_clk_reg(clock) to be used for turning the
+ * clock on and off */
+static inline uint32 rcc_clk_on_mask(rcc_clk clock) {
+ return 1 << (clock & 0xFF);
+}
+
+/* Returns a mask in rcc_clk_reg(clock) to be used when checking the
+ * readiness of the clock. */
+static inline uint32 rcc_clk_ready_mask(rcc_clk clock) {
+ return rcc_clk_on_mask(clock) << 1;
+}
+
+/**
+ * @brief Turn on a clock source.
+ *
+ * After this routine exists, callers should ensure that the clock
+ * source is ready by waiting until rcc_is_clk_ready(clock) returns
+ * true.
+ *
+ * @param clock Clock to turn on.
+ * @see rcc_turn_off_clk()
+ * @see rcc_is_clk_ready()
+ */
+void rcc_turn_on_clk(rcc_clk clock) {
+ *rcc_clk_reg(clock) |= rcc_clk_on_mask(clock);
+}
+
+/**
+ * @brief Turn off a clock source.
+ *
+ * In certain configurations, certain clock sources cannot be safely
+ * turned off. (For example, the main PLL on STM32F1 devices cannot be
+ * turned off if it has been selected as the SYSCLK source). Consult
+ * the reference material for your MCU to ensure it is safe to call
+ * this function.
+ *
+ * @param clock Clock to turn off.
+ * @see rcc_turn_on_clk()
+ * @see rcc_is_clk_ready()
+ */
+void rcc_turn_off_clk(rcc_clk clock) {
+ *rcc_clk_reg(clock) &= ~rcc_clk_on_mask(clock);
+}
+
+/**
+ * @brief Check if a clock source is ready.
+ *
+ * In general, it is not safe to rely on a clock source unless this
+ * function returns nonzero. Also note that this function may return
+ * nonzero for a short period of time after a clock has been turned
+ * off. Consult the reference material for your MCU for more details.
+ *
+ * @param clock Clock whose readiness to check for.
+ * @return Nonzero if the clock is ready, zero otherwise.
+ * @see rcc_turn_on_clk()
+ * @see rcc_turn_off_clk()
+ */
+int rcc_is_clk_ready(rcc_clk clock) {
+ return (int)(*rcc_clk_reg(clock) & rcc_clk_ready_mask(clock));
+}
diff --git a/libmaple/stm32f1/include/series/rcc.h b/libmaple/stm32f1/include/series/rcc.h
index 261dc5d..474aaf7 100644
--- a/libmaple/stm32f1/include/series/rcc.h
+++ b/libmaple/stm32f1/include/series/rcc.h
@@ -37,6 +37,8 @@
extern "C"{
#endif
+#include <libmaple/libmaple.h>
+
/*
* Register map
*/
@@ -383,7 +385,7 @@ typedef struct rcc_reg_map {
#define RCC_CSR_LSION BIT(RCC_CSR_LSION_BIT)
/*
- * Other types
+ * libmaple-mandated enumeration types.
*/
/**
@@ -546,6 +548,33 @@ typedef enum rcc_ahb_divider {
RCC_AHB_SYSCLK_DIV_512 = 0xF << 4,
} rcc_ahb_divider;
+/**
+ * @brief Available clock sources.
+ */
+typedef enum rcc_clk {
+ RCC_CLK_PLL = (uint16)((offsetof(struct rcc_reg_map, CR) << 8) |
+ RCC_CR_PLLON_BIT), /**< Main PLL, clocked by
+ HSI or HSE. */
+ RCC_CLK_HSE = (uint16)((offsetof(struct rcc_reg_map, CR) << 8) |
+ RCC_CR_HSEON_BIT), /**< High speed external. */
+ RCC_CLK_HSI = (uint16)((offsetof(struct rcc_reg_map, CR) << 8) |
+ RCC_CR_HSION_BIT), /**< High speed internal. */
+ RCC_CLK_LSE = (uint16)((offsetof(struct rcc_reg_map, BDCR) << 8) |
+ RCC_BDCR_LSEON_BIT), /**< Low-speed external
+ * (32.768 KHz). */
+ RCC_CLK_LSI = (uint16)((offsetof(struct rcc_reg_map, CSR) << 8) |
+ RCC_CSR_LSION_BIT), /**< Low-speed internal
+ * (approximately 32 KHz). */
+} rcc_clk;
+
+/*
+ * Series-specific functionality.
+ */
+
+void rcc_clk_init(rcc_sysclk_src sysclk_src,
+ rcc_pllsrc pll_src,
+ rcc_pll_multiplier pll_mul);
+
#ifdef __cplusplus
}
#endif
diff --git a/libmaple/stm32f1/rcc.c b/libmaple/stm32f1/rcc.c
index 2b78e89..2d31482 100644
--- a/libmaple/stm32f1/rcc.c
+++ b/libmaple/stm32f1/rcc.c
@@ -105,9 +105,6 @@ const struct rcc_dev_info rcc_dev_table[] = {
void rcc_clk_init(rcc_sysclk_src sysclk_src,
rcc_pllsrc pll_src,
rcc_pll_multiplier pll_mul) {
- uint32 cfgr = 0;
- uint32 cr;
-
/* Assume that we're going to clock the chip off the PLL, fed by
* the HSE */
ASSERT(sysclk_src == RCC_CLKSRC_PLL &&
@@ -115,25 +112,18 @@ void rcc_clk_init(rcc_sysclk_src sysclk_src,
RCC_BASE->CFGR = pll_src | pll_mul;
- /* Turn on the HSE */
- cr = RCC_BASE->CR;
- cr |= RCC_CR_HSEON;
- RCC_BASE->CR = cr;
- while (!(RCC_BASE->CR & RCC_CR_HSERDY))
+ /* Turn on, and wait for, HSE. */
+ rcc_turn_on_clk(RCC_CLK_HSE);
+ while (!rcc_is_clk_ready(RCC_CLK_HSE))
;
- /* Now the PLL */
- cr |= RCC_CR_PLLON;
- RCC_BASE->CR = cr;
- while (!(RCC_BASE->CR & RCC_CR_PLLRDY))
+ /* Do the same for the main PLL. */
+ rcc_turn_on_clk(RCC_CLK_PLL);
+ while(!rcc_is_clk_ready(RCC_CLK_PLL))
;
- /* Finally, let's switch over to the PLL */
- cfgr &= ~RCC_CFGR_SW;
- cfgr |= RCC_CFGR_SW_PLL;
- RCC_BASE->CFGR = cfgr;
- while ((RCC_BASE->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL)
- ;
+ /* Finally, switch over to the PLL. */
+ rcc_switch_sysclk(RCC_CLKSRC_PLL);
}
/**
diff --git a/libmaple/stm32f2/include/series/rcc.h b/libmaple/stm32f2/include/series/rcc.h
index 1557f2d..89fed1d 100644
--- a/libmaple/stm32f2/include/series/rcc.h
+++ b/libmaple/stm32f2/include/series/rcc.h
@@ -722,7 +722,6 @@ typedef struct rcc_reg_map {
/* Spread spectrum clock generation register */
-
#define RCC_SSCGR_SSCGEN_BIT 31
#define RCC_SSCGR_SPREADSEL_BIT 30
@@ -742,6 +741,10 @@ typedef struct rcc_reg_map {
* Other types
*/
+/*
+ * Clock sources, domains, and peripheral clock IDs.
+ */
+
/**
* @brief Identifies bus and clock line for a peripheral or peripheral
* clock.
@@ -819,10 +822,6 @@ typedef enum rcc_pllsrc {
RCC_PLLSRC_HSE = RCC_PLLCFGR_PLLSRC,
} rcc_pllsrc;
-typedef enum rcc_pll_multiplier { /* TODO -- does this make sense anymore? */
- RCC_PLLMUL_XXX,
-} rcc_pll_multiplier;
-
/**
* @brief Peripheral clock domains.
*/
@@ -834,6 +833,10 @@ typedef enum rcc_clk_domain {
RCC_AHB3,
} rcc_clk_domain;
+/*
+ * Prescalers and dividers.
+ */
+
/**
* @brief Prescaler identifiers.
*/
@@ -912,6 +915,28 @@ typedef enum rcc_ahb_divider {
RCC_AHB_SYSCLK_DIV_512 = RCC_CFGR_HPRE_SYSCLK_DIV_512,
} rcc_ahb_divider;
+/**
+ * @brief Available clock sources.
+ */
+typedef enum rcc_clk {
+ RCC_CLK_PLLI2S = (uint16)((offsetof(struct rcc_reg_map, CR) << 8) |
+ RCC_CR_PLLI2SON_BIT), /**< Dedicated PLL
+ for I2S. */
+ RCC_CLK_PLL = (uint16)((offsetof(struct rcc_reg_map, CR) << 8) |
+ RCC_CR_PLLON_BIT), /**< Main PLL, clocked by
+ HSI or HSE. */
+ RCC_CLK_HSE = (uint16)((offsetof(struct rcc_reg_map, CR) << 8) |
+ RCC_CR_HSEON_BIT), /**< High speed external. */
+ RCC_CLK_HSI = (uint16)((offsetof(struct rcc_reg_map, CR) << 8) |
+ RCC_CR_HSION_BIT), /**< High speed internal. */
+ RCC_CLK_LSE = (uint16)((offsetof(struct rcc_reg_map, BDCR) << 8) |
+ RCC_BDCR_LSEON_BIT), /**< Low-speed external
+ * (32.768 KHz). */
+ RCC_CLK_LSI = (uint16)((offsetof(struct rcc_reg_map, CSR) << 8) |
+ RCC_CSR_LSION_BIT), /**< Low-speed internal
+ * (approximately 32 KHz). */
+} rcc_clk;
+
#ifdef __cplusplus
}
#endif
diff --git a/libmaple/stm32f2/rcc.c b/libmaple/stm32f2/rcc.c
index 268a9b1..7bc220d 100644
--- a/libmaple/stm32f2/rcc.c
+++ b/libmaple/stm32f2/rcc.c
@@ -110,12 +110,6 @@ const struct rcc_dev_info rcc_dev_table[] = {
[RCC_TIM1] = DEV_ENTRY(APB2, TIM1),
};
-void rcc_clk_init(rcc_sysclk_src sysclk_src,
- rcc_pllsrc pll_src,
- rcc_pll_multiplier pll_mul) {
- ASSERT(0); /* FIXME */
-}
-
/**
* @brief Turn on the clock line on a peripheral
* @param id Clock ID of the peripheral to turn on.