From 4dc4d99fdddffdeb3e14e722e935c76c74ff9a15 Mon Sep 17 00:00:00 2001 From: Marti Bolivar Date: Fri, 27 Jan 2012 21:31:30 -0500 Subject: 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 . - 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 --- libmaple/include/libmaple/rcc.h | 59 +++++++++++------ libmaple/rcc.c | 116 ++++++++++++++++++++++++++++++++++ libmaple/stm32f1/include/series/rcc.h | 31 ++++++++- libmaple/stm32f1/rcc.c | 26 +++----- libmaple/stm32f2/include/series/rcc.h | 35 ++++++++-- libmaple/stm32f2/rcc.c | 6 -- 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 + +/* 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 /* 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 + /* * 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. -- cgit v1.2.3