289 lines
9.6 KiB
C
289 lines
9.6 KiB
C
|
/** @addtogroup rcc_file RCC peripheral API
|
||
|
* @ingroup peripheral_apis
|
||
|
*/
|
||
|
/*
|
||
|
* This file is part of the libopencm3 project.
|
||
|
*
|
||
|
* Copyright (C) 2013 Frantisek Burian <bufran@seznam.cz>
|
||
|
* .. 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 <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
/**@{*/
|
||
|
|
||
|
#include <libopencm3/stm32/rcc.h>
|
||
|
|
||
|
/*---------------------------------------------------------------------------*/
|
||
|
/** @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 <em>only if they are controlled by the same register</em>.
|
||
|
* @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_AHBER, 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.
|
||
|
*
|
||
|
* 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 disabled
|
||
|
* simultaneously <em>only if they are controlled by the same register</em>.
|
||
|
* @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_AHBER, 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 <em>only if
|
||
|
* they are controlled by the same register</em>.
|
||
|
* @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 <em>only if they are controlled by the same register</em>.
|
||
|
* @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
|