libopencm3/lib/stm32/common/rcc_common_all.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