/** @addtogroup rcc_file RCC peripheral API * @ingroup peripheral_apis */ /* * This file is part of the libopencm3 project. * * Copyright (C) 2013 Frantisek Burian * .. file is merged from many other copyrighted files of stm32 family * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . */ /**@{*/ #include /*---------------------------------------------------------------------------*/ /** @brief RCC Enable Peripheral Clocks. * * Enable the clock on particular peripherals. There are three registers * involved, each one controlling the enabling of clocks associated with the * AHB, APB1 and APB2 respectively. Several peripherals could be enabled * simultaneously only if they are controlled by the same register. * @sa rcc_periph_clock_enable for a less error prone version, if you only * need to enable a single peripheral. * * @param[in] *reg Unsigned int32. Pointer to a Clock Enable Register * (either RCC_AHBENR, RCC_APB1ENR or RCC_APB2ENR) * * @param[in] en Unsigned int32. Logical OR of all enables to be set * @li If register is RCC_AHBENR, from @ref rcc_ahbenr_en * @li If register is RCC_APB1ENR, from @ref rcc_apb1enr_en * @li If register is RCC_APB2ENR, from @ref rcc_apb2enr_en */ void rcc_peripheral_enable_clock(volatile uint32_t *reg, uint32_t en) { *reg |= en; } /*---------------------------------------------------------------------------*/ /** @brief RCC Disable Peripheral Clocks. * * Disable the clock on particular peripherals. There are three registers * involved, each one controlling the enabling of clocks associated with * the AHB, APB1 and APB2 respectively. Several peripherals could be disabled * simultaneously only if they are controlled by the same register. * @sa rcc_periph_clock_disable for a less error prone version, if you only * need to disable a single peripheral. * * @param[in] *reg Unsigned int32. Pointer to a Clock Enable Register * (either RCC_AHBENR, RCC_APB1ENR or RCC_APB2ENR) * @param[in] en Unsigned int32. Logical OR of all enables to be used for * disabling. * @li If register is RCC_AHBENR, from @ref rcc_ahbenr_en * @li If register is RCC_APB1ENR, from @ref rcc_apb1enr_en * @li If register is RCC_APB2ENR, from @ref rcc_apb2enr_en */ void rcc_peripheral_disable_clock(volatile uint32_t *reg, uint32_t en) { *reg &= ~en; } /*---------------------------------------------------------------------------*/ /** @brief RCC Reset Peripherals. * * Reset particular peripherals. There are three registers involved, each one * controlling reset of peripherals associated with the AHB, APB1 and APB2 * respectively. Several peripherals could be reset simultaneously only if * they are controlled by the same register. * @sa rcc_periph_reset_hold for a less error prone version, if you only * need to reset a single peripheral. * @sa rcc_periph_reset_pulse if you are only going to toggle reset anyway. * * @param[in] *reg Unsigned int32. Pointer to a Reset Register * (either RCC_AHBENR, RCC_APB1ENR or RCC_APB2ENR) * @param[in] reset Unsigned int32. Logical OR of all resets. * @li If register is RCC_AHBRSTR, from @ref rcc_ahbrstr_rst * @li If register is RCC_APB1RSTR, from @ref rcc_apb1rstr_rst * @li If register is RCC_APB2RSTR, from @ref rcc_apb2rstr_rst */ void rcc_peripheral_reset(volatile uint32_t *reg, uint32_t reset) { *reg |= reset; } /*---------------------------------------------------------------------------*/ /** @brief RCC Remove Reset on Peripherals. * * Remove the reset on particular peripherals. There are three registers * involved, each one controlling reset of peripherals associated with the AHB, * APB1 and APB2 respectively. Several peripherals could have the reset removed * simultaneously only if they are controlled by the same register. * @sa rcc_periph_reset_release for a less error prone version, if you only * need to unreset a single peripheral. * @sa rcc_periph_reset_pulse if you are only going to toggle reset anyway. * * @param[in] *reg Unsigned int32. Pointer to a Reset Register * (either RCC_AHBENR, RCC_APB1ENR or RCC_APB2ENR) * @param[in] clear_reset Unsigned int32. Logical OR of all resets to be * removed: * @li If register is RCC_AHBRSTR, from @ref rcc_ahbrstr_rst * @li If register is RCC_APB1RSTR, from @ref rcc_apb1rstr_rst * @li If register is RCC_APB2RSTR, from @ref rcc_apb2rstr_rst */ void rcc_peripheral_clear_reset(volatile uint32_t *reg, uint32_t clear_reset) { *reg &= ~clear_reset; } #define _RCC_REG(i) MMIO32(RCC_BASE + ((i) >> 5)) #define _RCC_BIT(i) (1 << ((i) & 0x1f)) /*---------------------------------------------------------------------------*/ /** @brief Enable Peripheral Clock in running mode. * * Enable the clock on particular peripheral. * * @param[in] clken rcc_periph_clken Peripheral RCC * * For available constants, see #rcc_periph_clken (RCC_UART1 for example) */ void rcc_periph_clock_enable(enum rcc_periph_clken clken) { _RCC_REG(clken) |= _RCC_BIT(clken); } /*---------------------------------------------------------------------------*/ /** @brief Disable Peripheral Clock in running mode. * Disable the clock on particular peripheral. * * @param[in] clken rcc_periph_clken Peripheral RCC * * For available constants, see #rcc_periph_clken (RCC_UART1 for example) */ void rcc_periph_clock_disable(enum rcc_periph_clken clken) { _RCC_REG(clken) &= ~_RCC_BIT(clken); } /*---------------------------------------------------------------------------*/ /** @brief Reset Peripheral, pulsed * * Reset particular peripheral, and restore to working state. * * @param[in] rst rcc_periph_rst Peripheral reset * * For available constants, see #rcc_periph_rst (RST_UART1 for example) */ void rcc_periph_reset_pulse(enum rcc_periph_rst rst) { _RCC_REG(rst) |= _RCC_BIT(rst); _RCC_REG(rst) &= ~_RCC_BIT(rst); } /*---------------------------------------------------------------------------*/ /** @brief Reset Peripheral, hold * * Reset particular peripheral, and hold in reset state. * * @param[in] rst rcc_periph_rst Peripheral reset * * For available constants, see #rcc_periph_rst (RST_UART1 for example) */ void rcc_periph_reset_hold(enum rcc_periph_rst rst) { _RCC_REG(rst) |= _RCC_BIT(rst); } /*---------------------------------------------------------------------------*/ /** @brief Reset Peripheral, release * * Restore peripheral from reset state to working state. * * @param[in] rst rcc_periph_rst Peripheral reset * * For available constants, see #rcc_periph_rst (RST_UART1 for example) */ void rcc_periph_reset_release(enum rcc_periph_rst rst) { _RCC_REG(rst) &= ~_RCC_BIT(rst); } /** @brief Select the source of Microcontroller Clock Output * * Exact sources available depend on your target. On devices with multiple * MCO pins, this function controls MCO1 * * @param[in] mcosrc the unshifted source bits */ void rcc_set_mco(uint32_t mcosrc) { RCC_CFGR = (RCC_CFGR & ~(RCC_CFGR_MCO_MASK << RCC_CFGR_MCO_SHIFT)) | (mcosrc << RCC_CFGR_MCO_SHIFT); } /** * RCC Enable Bypass. * Enable an external clock to bypass the internal clock (high speed and low * speed clocks only). The external clock must be enabled (see @ref rcc_osc_on) * and the internal clock must be disabled (see @ref rcc_osc_off) for this to * have effect. * @note The LSE clock is in the backup domain and cannot be bypassed until the * backup domain write protection has been removed (see @ref * pwr_disable_backup_domain_write_protect). * @param[in] osc Oscillator ID. Only HSE and LSE have effect. */ void rcc_osc_bypass_enable(enum rcc_osc osc) { switch (osc) { case RCC_HSE: RCC_CR |= RCC_CR_HSEBYP; break; case RCC_LSE: #ifdef RCC_CSR_LSEBYP RCC_CSR |= RCC_CSR_LSEBYP; #else RCC_BDCR |= RCC_BDCR_LSEBYP; #endif break; default: /* Do nothing, only HSE/LSE allowed here. */ break; } } /** * RCC Disable Bypass. * Re-enable the internal clock (high speed and low speed clocks only). The * internal clock must be disabled (see @ref rcc_osc_off) for this to have * effect. * @note The LSE clock is in the backup domain and cannot have bypass removed * until the backup domain write protection has been removed (see @ref * pwr_disable_backup_domain_write_protect) or the backup domain has been reset * (see @ref rcc_backupdomain_reset). * @param[in] osc Oscillator ID. Only HSE and LSE have effect. */ void rcc_osc_bypass_disable(enum rcc_osc osc) { switch (osc) { case RCC_HSE: RCC_CR &= ~RCC_CR_HSEBYP; break; case RCC_LSE: #ifdef RCC_CSR_LSEBYP RCC_CSR &= ~RCC_CSR_LSEBYP; #else RCC_BDCR &= ~RCC_BDCR_LSEBYP; #endif break; default: /* Do nothing, only HSE/LSE allowed here. */ break; } } /* This is a helper to calculate dividers that go 2/4/8/16/64/128/256/512. * These dividers also use the top bit as an "enable". This is tyipcally * used for AHB and other system clock prescaler. */ uint16_t rcc_get_div_from_hpre(uint8_t div_val) { if (div_val < 0x8) { return 1; } else if (div_val <= 0x0b /* DIV16 */) { return (1U << (div_val - 7)); } else { return (1U << (div_val - 6)); } } /**@}*/ #undef _RCC_REG #undef _RCC_BIT