aboutsummaryrefslogtreecommitdiffstats
path: root/libmaple/rcc.c
diff options
context:
space:
mode:
authorMarti Bolivar <mbolivar@leaflabs.com>2012-06-26 18:24:49 -0400
committerMarti Bolivar <mbolivar@leaflabs.com>2012-06-26 18:32:57 -0400
commitf005bd3a5c087e3d5559f2858a1e7898a4f92a8d (patch)
tree0701628a68056f7b5f92d5a5af5f281f58e6a71e /libmaple/rcc.c
parent761e059962e8f53f3cceef61d65bf2bf3025319a (diff)
parentc6073e4886da4606679bc3e9d770c9cff9390597 (diff)
downloadlibrambutan-f005bd3a5c087e3d5559f2858a1e7898a4f92a8d.tar.gz
librambutan-f005bd3a5c087e3d5559f2858a1e7898a4f92a8d.zip
Merge branch 'wip-family-support'
Merge the long-lived (too long; future changes like these will need to proceed more incrementally) development branch of libmaple, containing experimental STM32F2 and STM32F1 value line support, into master. This required many changes to the structure of the library. The most important structural reorganizations occurred in: - 954f9e5: moves public headers to include directories - 3efa313: uses "series" instead of "family" - c0d60e3: adds board files to the build system, to make it easier to add new boards - 096d86c: adds build logic for targeting different STM32 series (e.g. STM32F1, STM32F2) This last commit in particular (096d86c) is the basis for the repartitioning of libmaple into portable sections, which work on all supported MCUs, and nonportable sections, which are segregated into separate directories and contain all series-specific code. Moving existing STM32F1-only code into libmaple/stm32f1 and wirish/stm32f1, along with adding equivalents under .../stm32f2 directories, was the principal project of this branch. Important API changes occur in several places. Existing code is still expected to work on STM32F1 targets, but there have been many deprecations. A detailed changelog explaining the situation needs to be prepared. F2 and F1 value line support is not complete; the merge is proceeding prematurely in this respect. We've been getting more libmaple patches from the community lately, and I'm worried that the merge conflicts with the old tree structure will become painful to manage. Conflicts: Makefile Resolved Makefile conflicts manually; this required propagating -Xlinker usage into support/make/target-config.mk. Signed-off-by: Marti Bolivar <mbolivar@leaflabs.com>
Diffstat (limited to 'libmaple/rcc.c')
-rw-r--r--libmaple/rcc.c269
1 files changed, 116 insertions, 153 deletions
diff --git a/libmaple/rcc.c b/libmaple/rcc.c
index 65abfb6..8e7d1ea 100644
--- a/libmaple/rcc.c
+++ b/libmaple/rcc.c
@@ -2,6 +2,7 @@
* The MIT License
*
* Copyright (c) 2010 Perry Hung.
+ * Copyright (c) 2011 LeafLabs, LLC.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
@@ -25,182 +26,144 @@
*****************************************************************************/
/**
- * @file rcc.c
- * @brief Implements pretty much only the basic clock setup on the
- * stm32, clock enable/disable and peripheral reset commands.
+ * @file libmaple/rcc.c
+ * @brief Portable RCC routines.
*/
-#include "libmaple.h"
-#include "flash.h"
-#include "rcc.h"
-#include "bitband.h"
-
-#define APB1 RCC_APB1
-#define APB2 RCC_APB2
-#define AHB RCC_AHB
-
-struct rcc_dev_info {
- const rcc_clk_domain clk_domain;
- const uint8 line_num;
-};
-
-/* Device descriptor table, maps rcc_clk_id onto bus and enable/reset
- * register bit numbers. */
-static const struct rcc_dev_info rcc_dev_table[] = {
- [RCC_GPIOA] = { .clk_domain = APB2, .line_num = 2 },
- [RCC_GPIOB] = { .clk_domain = APB2, .line_num = 3 },
- [RCC_GPIOC] = { .clk_domain = APB2, .line_num = 4 },
- [RCC_GPIOD] = { .clk_domain = APB2, .line_num = 5 },
- [RCC_AFIO] = { .clk_domain = APB2, .line_num = 0 },
- [RCC_ADC1] = { .clk_domain = APB2, .line_num = 9 },
- [RCC_ADC2] = { .clk_domain = APB2, .line_num = 10 },
- [RCC_ADC3] = { .clk_domain = APB2, .line_num = 15 },
- [RCC_USART1] = { .clk_domain = APB2, .line_num = 14 },
- [RCC_USART2] = { .clk_domain = APB1, .line_num = 17 },
- [RCC_USART3] = { .clk_domain = APB1, .line_num = 18 },
- [RCC_TIMER1] = { .clk_domain = APB2, .line_num = 11 },
- [RCC_TIMER2] = { .clk_domain = APB1, .line_num = 0 },
- [RCC_TIMER3] = { .clk_domain = APB1, .line_num = 1 },
- [RCC_TIMER4] = { .clk_domain = APB1, .line_num = 2 },
- [RCC_SPI1] = { .clk_domain = APB2, .line_num = 12 },
- [RCC_SPI2] = { .clk_domain = APB1, .line_num = 14 },
- [RCC_DMA1] = { .clk_domain = AHB, .line_num = 0 },
- [RCC_PWR] = { .clk_domain = APB1, .line_num = 28},
- [RCC_BKP] = { .clk_domain = APB1, .line_num = 27},
- [RCC_I2C1] = { .clk_domain = APB1, .line_num = 21 },
- [RCC_I2C2] = { .clk_domain = APB1, .line_num = 22 },
- [RCC_CRC] = { .clk_domain = AHB, .line_num = 6},
- [RCC_FLITF] = { .clk_domain = AHB, .line_num = 4},
- [RCC_SRAM] = { .clk_domain = AHB, .line_num = 2},
- [RCC_USB] = { .clk_domain = APB1, .line_num = 23},
-#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
- [RCC_GPIOE] = { .clk_domain = APB2, .line_num = 6 },
- [RCC_GPIOF] = { .clk_domain = APB2, .line_num = 7 },
- [RCC_GPIOG] = { .clk_domain = APB2, .line_num = 8 },
- [RCC_UART4] = { .clk_domain = APB1, .line_num = 19 },
- [RCC_UART5] = { .clk_domain = APB1, .line_num = 20 },
- [RCC_TIMER5] = { .clk_domain = APB1, .line_num = 3 },
- [RCC_TIMER6] = { .clk_domain = APB1, .line_num = 4 },
- [RCC_TIMER7] = { .clk_domain = APB1, .line_num = 5 },
- [RCC_TIMER8] = { .clk_domain = APB2, .line_num = 13 },
- [RCC_FSMC] = { .clk_domain = AHB, .line_num = 8 },
- [RCC_DAC] = { .clk_domain = APB1, .line_num = 29 },
- [RCC_DMA2] = { .clk_domain = AHB, .line_num = 1 },
- [RCC_SDIO] = { .clk_domain = AHB, .line_num = 10 },
- [RCC_SPI3] = { .clk_domain = APB1, .line_num = 15 },
-#endif
-#ifdef STM32_XL_DENSITY
- [RCC_TIMER9] = { .clk_domain = APB2, .line_num = 19 },
- [RCC_TIMER10] = { .clk_domain = APB2, .line_num = 20 },
- [RCC_TIMER11] = { .clk_domain = APB2, .line_num = 21 },
- [RCC_TIMER12] = { .clk_domain = APB1, .line_num = 6 },
- [RCC_TIMER13] = { .clk_domain = APB1, .line_num = 7 },
- [RCC_TIMER14] = { .clk_domain = APB1, .line_num = 8 },
-#endif
-};
+#include <libmaple/rcc.h>
+
+#include "rcc_private.h"
/**
- * @brief Initialize the clock control system. Initializes the system
- * clock source to use the PLL driven by an external oscillator
- * @param sysclk_src system clock source, must be PLL
- * @param pll_src pll clock source, must be HSE
- * @param pll_mul pll multiplier
+ * @brief Get a peripheral's clock domain
+ * @param id Clock ID of the peripheral whose clock domain to return
+ * @return Clock source for the given clock ID
*/
-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 &&
- pll_src == RCC_PLLSRC_HSE);
-
- 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))
- ;
-
- /* Now the PLL */
- cr |= RCC_CR_PLLON;
- RCC_BASE->CR = cr;
- while (!(RCC_BASE->CR & RCC_CR_PLLRDY))
- ;
+rcc_clk_domain rcc_dev_clk(rcc_clk_id id) {
+ return rcc_dev_table[id].clk_domain;
+}
- /* Finally, let's switch over to the PLL */
+/**
+ * @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 |= RCC_CFGR_SW_PLL;
+ cfgr |= sysclk_src;
+
+ /* Switch SYSCLK source. */
RCC_BASE->CFGR = cfgr;
- while ((RCC_BASE->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL)
+
+ /* Wait for new source to come into use. */
+ while ((RCC_BASE->CFGR & RCC_CFGR_SWS) != (sysclk_src << 2))
;
}
-/**
- * @brief Turn on the clock line on a peripheral
- * @param id Clock ID of the peripheral to turn on.
+/*
+ * Turning clocks off and on, querying their status.
*/
-void rcc_clk_enable(rcc_clk_id id) {
- static const __io uint32* enable_regs[] = {
- [APB1] = &RCC_BASE->APB1ENR,
- [APB2] = &RCC_BASE->APB2ENR,
- [AHB] = &RCC_BASE->AHBENR,
- };
-
- rcc_clk_domain clk_domain = rcc_dev_clk(id);
- __io uint32* enr = (__io uint32*)enable_regs[clk_domain];
- uint8 lnum = rcc_dev_table[id].line_num;
-
- bb_peri_set_bit(enr, lnum, 1);
+
+/* 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 series, 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 Reset a peripheral.
- * @param id Clock ID of the peripheral to reset.
+ * @brief Turn on a clock source.
+ *
+ * After this routine exits, 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_reset_dev(rcc_clk_id id) {
- static const __io uint32* reset_regs[] = {
- [APB1] = &RCC_BASE->APB1RSTR,
- [APB2] = &RCC_BASE->APB2RSTR,
- };
-
- rcc_clk_domain clk_domain = rcc_dev_clk(id);
- __io void* addr = (__io void*)reset_regs[clk_domain];
- uint8 lnum = rcc_dev_table[id].line_num;
-
- bb_peri_set_bit(addr, lnum, 1);
- bb_peri_set_bit(addr, lnum, 0);
+void rcc_turn_on_clk(rcc_clk clock) {
+ *rcc_clk_reg(clock) |= rcc_clk_on_mask(clock);
}
/**
- * @brief Get a peripheral's clock domain
- * @param id Clock ID of the peripheral whose clock domain to return
- * @return Clock source for the given clock ID
+ * @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()
*/
-rcc_clk_domain rcc_dev_clk(rcc_clk_id id) {
- return rcc_dev_table[id].clk_domain;
+void rcc_turn_off_clk(rcc_clk clock) {
+ *rcc_clk_reg(clock) &= ~rcc_clk_on_mask(clock);
}
/**
- * @brief Set the divider on a peripheral prescaler
- * @param prescaler prescaler to set
- * @param divider prescaler divider
+ * @brief Check if a clock is on.
+ * @param clock Clock to check.
+ * @return 1 if the clock is on, 0 if the clock is off.
*/
-void rcc_set_prescaler(rcc_prescaler prescaler, uint32 divider) {
- static const uint32 masks[] = {
- [RCC_PRESCALER_AHB] = RCC_CFGR_HPRE,
- [RCC_PRESCALER_APB1] = RCC_CFGR_PPRE1,
- [RCC_PRESCALER_APB2] = RCC_CFGR_PPRE2,
- [RCC_PRESCALER_USB] = RCC_CFGR_USBPRE,
- [RCC_PRESCALER_ADC] = RCC_CFGR_ADCPRE,
- };
+int rcc_is_clk_on(rcc_clk clock) {
+ return !!(*rcc_clk_reg(clock) & rcc_clk_on_mask(clock));
+}
- uint32 cfgr = RCC_BASE->CFGR;
- cfgr &= ~masks[prescaler];
- cfgr |= divider;
- RCC_BASE->CFGR = cfgr;
+/**
+ * @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));
}