rp2040/driver/src/clock.c

644 lines
18 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "clock.h"
#include "resets.h"
void clock_rosc_enable(void)
{
uint32_t addr;
uint32_t val;
if (clock_rosc_get_status(CLOCK_ROSC_STATUS_STABLE)) {
return;
}
addr = ROSC_BASE + CLOCK_ROSC_CTRL_OFFSET;
val = getreg32(addr);
val &= ~(CLOCK_ROSC_ENABLE_MASK << CLOCK_ROSC_ENABLE_POS);
val |= (uint32_t)0xFAB << CLOCK_ROSC_ENABLE_POS;
putreg32(val, addr);
while (!clock_rosc_get_status(CLOCK_ROSC_STATUS_STABLE));
}
void clock_rosc_disable(void)
{
uint32_t addr;
uint32_t val;
addr = ROSC_BASE + CLOCK_ROSC_CTRL_OFFSET;
val = getreg32(addr);
val &= ~(CLOCK_ROSC_ENABLE_MASK << CLOCK_ROSC_ENABLE_POS);
val |= (uint32_t)0xD1E << CLOCK_ROSC_ENABLE_POS;
putreg32(val, addr);
}
void clock_rosc_set_freq_range(uint8_t freq_range)
{
uint32_t addr;
uint32_t val;
uint32_t magic;
switch (freq_range) {
case CLOCK_ROSC_FREQ_RANGE_LOW:
magic = 0xFA4;
break;
case CLOCK_ROSC_FREQ_RANGE_MEDIUM:
magic = 0xFA5;
break;
case CLOCK_ROSC_FREQ_RANGE_HIGH:
magic = 0xFA7;
break;
case CLOCK_ROSC_FREQ_RANGE_TOOHIGH:
magic = 0xFA6;
break;
default:
magic = 0xFA4;
break;
}
addr = ROSC_BASE + CLOCK_ROSC_CTRL_OFFSET;
val = getreg32(addr);
val &= ~(CLOCK_ROSC_FREQ_RANGE_MASK << CLOCK_ROSC_FREQ_RANGE_POS);
val |= magic << CLOCK_ROSC_FREQ_RANGE_POS;
putreg32(val, addr);
}
uint8_t clock_rosc_get_freq_range(void)
{
uint32_t addr;
uint32_t val;
uint8_t freq_range;
addr = ROSC_BASE + CLOCK_ROSC_CTRL_OFFSET;
val = getreg32(addr);
val >>= CLOCK_ROSC_FREQ_RANGE_POS;
val &= CLOCK_ROSC_FREQ_RANGE_MASK;
switch (val) {
case 0xFA4:
freq_range = CLOCK_ROSC_FREQ_RANGE_LOW;
break;
case 0xFA5:
freq_range = CLOCK_ROSC_FREQ_RANGE_MEDIUM;
break;
case 0xFA7:
freq_range = CLOCK_ROSC_FREQ_RANGE_HIGH;
break;
case 0xFA6:
freq_range = CLOCK_ROSC_FREQ_RANGE_TOOHIGH;
break;
default:
freq_range = CLOCK_ROSC_FREQ_RANGE_INVALID;
break;
}
return freq_range;
}
void clock_rosc_set_strength(uint8_t stage, uint8_t strength)
{
uint32_t addr;
uint32_t val;
uint8_t pos;
if (stage < 4) {
addr = ROSC_BASE + CLOCK_ROSC_FREQA_OFFSET;
} else {
addr = ROSC_BASE + CLOCK_ROSC_FREQB_OFFSET;
}
pos = (stage & 0x03) << 2;
val = getreg32(addr);
val &= ~(CLOCK_ROSC_STRENGTH_MASK << pos);
val |= strength << pos;
putreg32(val, addr);
}
uint8_t clock_rosc_get_strength(uint8_t stage)
{
uint32_t addr;
uint32_t val;
uint8_t pos;
if (stage < 4) {
addr = ROSC_BASE + CLOCK_ROSC_FREQA_OFFSET;
} else {
addr = ROSC_BASE + CLOCK_ROSC_FREQB_OFFSET;
}
pos = (stage & 0x03) << 2;
val = getreg32(addr);
val >>= pos;
val &= CLOCK_ROSC_STRENGTH_MASK;
return (uint8_t)val;
}
void clock_rosc_dormant(void)
{
putreg32(0x636F6D61, ROSC_BASE + CLOCK_ROSC_DORMANT_OFFSET);
}
void clock_rosc_wake(void)
{
putreg32(0x77616B65, ROSC_BASE + CLOCK_ROSC_DORMANT_OFFSET);
}
void clock_rosc_set_div(uint8_t div)
{
uint32_t val;
div &= 0x1F;
val = 0xAA0 + (uint32_t)div;
putreg32(val, ROSC_BASE + CLOCK_ROSC_DIV_OFFSET);
}
uint8_t clock_rosc_get_div(void)
{
uint32_t val;
val = getreg32(ROSC_BASE + CLOCK_ROSC_DIV_OFFSET);
val -= 0xAA0;
return (uint8_t)val;
}
uint8_t clock_rosc_get_status(uint8_t item)
{
uint32_t val;
val = getreg32(ROSC_BASE + CLOCK_ROSC_STATUS_OFFSET);
val &= (1 << item);
if (val) {
return 1;
} else {
return 0;
}
}
void clock_rosc_clr_badwrite_status(void)
{
putreg32(1 << CLOCK_ROSC_STATUS_BADWRITE, ROSC_BASE + CLOCK_ROSC_STATUS_OFFSET);
}
uint8_t clock_rosc_get_randombit(void)
{
uint32_t val;
val = getreg32(ROSC_BASE + CLOCK_ROSC_RANDOMBIT_OFFSET);
val &= 0x01;
return (uint8_t)val;
}
void clock_rosc_set_count(uint8_t count)
{
putreg32((uint32_t)count, ROSC_BASE + CLOCK_ROSC_COUNT_OFFSET);
}
uint8_t clock_rosc_get_count(void)
{
return (uint8_t)getreg32(ROSC_BASE + CLOCK_ROSC_COUNT_OFFSET);
}
/************************* XOSC function *************************/
void clock_xosc_enable(void)
{
uint32_t addr;
uint32_t val;
if (clock_xosc_get_status(CLOCK_XOSC_STATUS_STABLE)) {
return;
}
addr = XOSC_BASE + CLOCK_XOSC_CTRL_OFFSET;
val = getreg32(addr);
val &= ~(CLOCK_XOSC_ENABLE_MASK << CLOCK_XOSC_ENABLE_POS);
val |= (uint32_t)0xFAB << CLOCK_XOSC_ENABLE_POS;
putreg32(val, addr);
while (!clock_xosc_get_status(CLOCK_XOSC_STATUS_STABLE));
}
void clock_xosc_disable(void)
{
uint32_t addr;
uint32_t val;
addr = XOSC_BASE + CLOCK_XOSC_CTRL_OFFSET;
val = getreg32(addr);
val &= ~(CLOCK_XOSC_ENABLE_MASK << CLOCK_XOSC_ENABLE_POS);
val |= (uint32_t)0xD1E << CLOCK_XOSC_ENABLE_POS;
putreg32(val, addr);
}
uint8_t clock_xosc_get_status(uint8_t item)
{
uint32_t val;
val = getreg32(XOSC_BASE + CLOCK_XOSC_STATUS_OFFSET);
val &= (1 << item);
if (val) {
return 1;
} else {
return 0;
}
}
void clock_xosc_clr_badwrite_status(void)
{
putreg32(1 << CLOCK_XOSC_STATUS_BADWRITE, XOSC_BASE + CLOCK_XOSC_STATUS_OFFSET);
}
void clock_xosc_dormant(void)
{
putreg32(0x636F6D61, XOSC_BASE + CLOCK_XOSC_DORMANT_OFFSET);
}
void clock_xosc_wake(void)
{
putreg32(0x77616B65, XOSC_BASE + CLOCK_XOSC_DORMANT_OFFSET);
}
void clock_xosc_set_startup_delay(uint32_t cycle)
{
uint32_t x4;
uint32_t val;
x4 = 0;
cycle >>= 8; /* in multiples of 256*xtal_period */
if (cycle > CLOCK_XOSC_STARTUP_DELAY_MASK) {
x4 = 1;
cycle >>= 2; /* multiplies the startup_delay by 4 */
if (cycle > CLOCK_XOSC_STARTUP_DELAY_MASK) {
cycle = CLOCK_XOSC_STARTUP_DELAY_MASK;
}
}
val = ((x4 << CLOCK_XOSC_STARTUP_X4_POS) | (cycle << CLOCK_XOSC_STARTUP_DELAY_POS));
putreg32(val, XOSC_BASE + CLOCK_XOSC_STARTUP_OFFSET);
}
void clock_xosc_set_count(uint8_t count)
{
putreg32((uint32_t)count, XOSC_BASE + CLOCK_XOSC_COUNT_OFFSET);
}
uint8_t clock_xosc_get_count(void)
{
return (uint8_t)getreg32(XOSC_BASE + CLOCK_XOSC_COUNT_OFFSET);
}
void clock_pll_init(uint8_t pll, uint8_t refdiv, uint8_t fbdiv, uint8_t postdiv1, uint8_t postdiv2)
{
uint32_t addr;
uint32_t val;
if (pll == CLOCK_PLL_SYSPLL) {
reset_unreset_blocks_wait(RESETS_BLOCK_SYSPLL);
addr = PLL_SYS_BASE;
} else if (pll == CLOCK_PLL_USBPLL) {
reset_unreset_blocks_wait(RESETS_BLOCK_USBPLL);
addr = PLL_USB_BASE;
} else {
return;
}
/* Load VCO-related dividers before starting VCO */
putreg32((uint32_t)refdiv, addr + CLOCK_PLL_CS_OFFSET);
putreg32((uint32_t)fbdiv, addr + CLOCK_PLL_FBDIV_INT_OFFSET);
/* Turn on PLL */
val = (1 << CLOCK_PLL_PD_POS) | (1 << CLOCK_PLL_VCOPD_POS);
putreg32(val, addr + CLOCK_PLL_PWR_OFFSET + REG_ALIAS_CLR_BITS);
/* Wait for PLL to lock */
while (1) {
val = getreg32(addr + CLOCK_PLL_CS_OFFSET);
val &= (1 << CLOCK_PLL_LOCK_POS);
if (val) {
break;
}
}
/* Set up post dividers */
val = (postdiv1 << CLOCK_PLL_POSTDIV1_POS) | (postdiv2 << CLOCK_PLL_POSTDIV2_POS);
putreg32(val, addr + CLOCK_PLL_PRIM_OFFSET);
/* Turn on post divider */
putreg32(1 << CLOCK_PLL_POSTDIVPD_POS, addr + CLOCK_PLL_PWR_OFFSET + REG_ALIAS_CLR_BITS);
}
void clock_pll_deinit(uint8_t pll)
{
if (pll == CLOCK_PLL_SYSPLL) {
reset_unreset_blocks_wait(RESETS_BLOCK_SYSPLL);
putreg32(CLOCK_PLL_PWR_MASK, PLL_SYS_BASE + CLOCK_PLL_PWR_OFFSET);
} else if (pll == CLOCK_PLL_USBPLL) {
reset_unreset_blocks_wait(RESETS_BLOCK_USBPLL);
putreg32(CLOCK_PLL_PWR_MASK, PLL_USB_BASE + CLOCK_PLL_PWR_OFFSET);
}
}
/* id: 0 ~ 3 */
/* div[31:8]: Integer component of the divisor, 0 is for 2^24 */
/* div[ 7:0]: Fractional component of the divisor */
/* phase: 0 ~ 3 */
void clock_gpout_set(uint8_t id, uint8_t enable, uint8_t src, uint32_t div, uint8_t dc50, uint8_t phase, uint8_t nudge)
{
uint32_t addr;
uint32_t val;
/* step1: disable clock */
addr = CLOCKS_BASE + CLOCK_GPOUT0_CTRL_OFFSET + id * 0xC;
putreg32(1 << CLOCK_ENABLE_POS, addr + REG_ALIAS_CLR_BITS);
/* step2: config ctrl register */
val = getreg32(addr);
/* config clock source */
val &= ~(CLOCK_AUXSRC_MASK << CLOCK_AUXSRC_POS);
val |= ((src & CLOCK_AUXSRC_MASK) << CLOCK_AUXSRC_POS);
/* config duty cycle correction for odd divisors */
if (dc50) {
val |= 1 << CLOCK_DC50_POS;
} else {
val &= ~(1 << CLOCK_DC50_POS);
}
/* config delay phase up to 3 cycles of the input clock */
val &= ~(CLOCK_PHASE_MASK << CLOCK_PHASE_POS);
val |= ((phase & CLOCK_PHASE_MASK) << CLOCK_PHASE_POS);
/* config signal shifts the phase of the output by 1 cycle of the input clock */
if (nudge) {
val |= 1 << CLOCK_NUDGE_POS;
} else {
val &= ~(1 << CLOCK_NUDGE_POS);
}
/* disable clock */
val &= ~(1 << CLOCK_ENABLE_POS);
putreg32(val, addr);
/* step3: config div */
putreg32(div, CLOCKS_BASE + CLOCK_GPOUT0_DIV_OFFSET + id * 0xC);
/* step4: config enable */
if (enable) {
putreg32(1 << CLOCK_ENABLE_POS, addr + REG_ALIAS_SET_BITS);
}
}
uint32_t clock_ref_get_selected(void)
{
return getreg32(CLOCKS_BASE + CLOCK_REF_SELECTED_OFFSET);
}
void clock_ref_set_src(uint8_t src)
{
uint32_t addr;
uint32_t val;
addr = CLOCKS_BASE + CLOCK_REF_CTRL_OFFSET;
if (src == CLOCK_REF_SRC_ROSC_PH_GLITCHLESS) {
/* switch to the glitchless mux */
clock_rosc_enable();
/* step1: switch the glitchless mux to an alternate source */
putreg32(0, addr);
/* step2: poll the SELECTED register until the switch is completed */
while (1) {
if (clock_ref_get_selected() & (1 << 0)) {
break;
}
}
} else if (src == CLOCK_REF_SRC_XOSC_GLITCHLESS) {
/* switch to the glitchless mux */
clock_xosc_enable();
/* step1: switch the glitchless mux to an alternate source */
putreg32(2, addr);
/* step2: poll the SELECTED register until the switch is completed */
while (1) {
if (clock_ref_get_selected() & (1 << 2)) {
break;
}
}
} else {
/* switch to the auxiliary mux when the generator has a glitchless mux */
clock_rosc_enable();
/* step1: switch the glitchless mux to a source that isnt the aux mux */
putreg32(0, addr);
/* step2: poll the SELECTED register until the switch is completed */
while (1) {
if (clock_ref_get_selected() & (1 << 0)) {
break;
}
}
/* step3: change the auxiliary mux select control */
val = (uint32_t)src << CLOCK_AUXSRC_POS;
putreg32(val, addr);
/* step4: switch the glitchless mux back to the aux mux */
val |= 1;
putreg32(val, addr);
/* step5: if required, poll the SELECTED register until the switch is completed */
while (1) {
if (clock_ref_get_selected() & (1 << 1)) {
break;
}
}
}
}
/* div[9:8]: Integer component of the divisor, 0 is for 2^2 */
/* div[7:0]: reserved */
void clock_ref_set_div(uint32_t div)
{
putreg32(div, CLOCKS_BASE + CLOCK_REF_DIV_OFFSET);
}
uint32_t clock_sys_get_selected(void)
{
return getreg32(CLOCKS_BASE + CLOCK_SYS_SELECTED_OFFSET);
}
/* must: call clock_ref_set_src brfore call clock_sys_set_src */
void clock_sys_set_src(uint8_t src)
{
uint32_t addr;
uint32_t val;
addr = CLOCKS_BASE + CLOCK_SYS_CTRL_OFFSET;
if (src == CLOCK_SYS_SRC_REF_GLITCHLESS) {
/* switch to the glitchless mux */
/* step1: switch the glitchless mux to an alternate source */
putreg32(0, addr);
/* step2: poll the SELECTED register until the switch is completed */
while (1) {
if (clock_sys_get_selected() & (1 << 0)) {
break;
}
}
} else {
/* switch to the auxiliary mux when the generator has a glitchless mux */
/* step1: switch the glitchless mux to a source that isnt the aux mux */
putreg32(0, addr);
/* step2: poll the SELECTED register until the switch is completed */
while (1) {
if (clock_sys_get_selected() & (1 << 0)) {
break;
}
}
/* step3: change the auxiliary mux select control */
val = (uint32_t)src << CLOCK_AUXSRC_POS;
putreg32(val, addr);
/* step4: switch the glitchless mux back to the aux mux */
val |= 1;
putreg32(val, addr);
/* step5: if required, poll the SELECTED register until the switch is completed */
while (1) {
if (clock_sys_get_selected() & (1 << 1)) {
break;
}
}
}
}
/* div[31:8]: Integer component of the divisor, 0 is for 2^24 */
/* div[ 7:0]: Fractional component of the divisor */
void clock_sys_set_div(uint32_t div)
{
putreg32(div, CLOCKS_BASE + CLOCK_SYS_DIV_OFFSET);
}
void clock_peri_set(uint8_t enable, uint8_t src)
{
uint32_t addr;
uint32_t val;
/* step1: disable clock */
addr = CLOCKS_BASE + CLOCK_PERI_CTRL_OFFSET;
putreg32(1 << CLOCK_ENABLE_POS, addr + REG_ALIAS_CLR_BITS);
/* step2: config ctrl register */
val = getreg32(addr);
/* config clock source */
val &= ~(CLOCK_AUXSRC_MASK << CLOCK_AUXSRC_POS);
val |= ((src & CLOCK_AUXSRC_MASK) << CLOCK_AUXSRC_POS);
/* disable clock */
val &= ~(1 << CLOCK_ENABLE_POS);
putreg32(val, addr);
/* step3: config enable */
if (enable) {
putreg32(1 << CLOCK_ENABLE_POS, addr + REG_ALIAS_SET_BITS);
}
}
/* div[9:8]: Integer component of the divisor, 0 is for 2^2 */
/* div[7:0]: reserved */
/* phase: 0 ~ 3 */
void clock_usb_set(uint8_t enable, uint8_t src, uint32_t div, uint8_t phase, uint8_t nudge)
{
uint32_t addr;
uint32_t val;
/* step1: disable clock */
addr = CLOCKS_BASE + CLOCK_USB_CTRL_OFFSET;
putreg32(1 << CLOCK_ENABLE_POS, addr + REG_ALIAS_CLR_BITS);
/* step2: config ctrl register */
val = getreg32(addr);
/* config clock source */
val &= ~(CLOCK_AUXSRC_MASK << CLOCK_AUXSRC_POS);
val |= ((src & CLOCK_AUXSRC_MASK) << CLOCK_AUXSRC_POS);
/* config delay phase up to 3 cycles of the input clock */
val &= ~(CLOCK_PHASE_MASK << CLOCK_PHASE_POS);
val |= ((phase & CLOCK_PHASE_MASK) << CLOCK_PHASE_POS);
/* config signal shifts the phase of the output by 1 cycle of the input clock */
if (nudge) {
val |= 1 << CLOCK_NUDGE_POS;
} else {
val &= ~(1 << CLOCK_NUDGE_POS);
}
/* disable clock */
val &= ~(1 << CLOCK_ENABLE_POS);
putreg32(val, addr);
/* step3: config div */
putreg32(div, CLOCKS_BASE + CLOCK_USB_DIV_OFFSET);
/* step4: config enable */
if (enable) {
putreg32(1 << CLOCK_ENABLE_POS, addr + REG_ALIAS_SET_BITS);
}
}
/* div[9:8]: Integer component of the divisor, 0 is for 2^2 */
/* div[7:0]: reserved */
/* phase: 0 ~ 3 */
void clock_adc_set(uint8_t enable, uint8_t src, uint32_t div, uint8_t phase, uint8_t nudge)
{
uint32_t addr;
uint32_t val;
/* step1: disable clock */
addr = CLOCKS_BASE + CLOCK_ADC_CTRL_OFFSET;
putreg32(1 << CLOCK_ENABLE_POS, addr + REG_ALIAS_CLR_BITS);
/* step2: config ctrl register */
val = getreg32(addr);
/* config clock source */
val &= ~(CLOCK_AUXSRC_MASK << CLOCK_AUXSRC_POS);
val |= ((src & CLOCK_AUXSRC_MASK) << CLOCK_AUXSRC_POS);
/* config delay phase up to 3 cycles of the input clock */
val &= ~(CLOCK_PHASE_MASK << CLOCK_PHASE_POS);
val |= ((phase & CLOCK_PHASE_MASK) << CLOCK_PHASE_POS);
/* config signal shifts the phase of the output by 1 cycle of the input clock */
if (nudge) {
val |= 1 << CLOCK_NUDGE_POS;
} else {
val &= ~(1 << CLOCK_NUDGE_POS);
}
/* disable clock */
val &= ~(1 << CLOCK_ENABLE_POS);
putreg32(val, addr);
/* step3: config div */
putreg32(div, CLOCKS_BASE + CLOCK_ADC_DIV_OFFSET);
/* step4: config enable */
if (enable) {
putreg32(1 << CLOCK_ENABLE_POS, addr + REG_ALIAS_SET_BITS);
}
}
/* div[31:8]: Integer component of the divisor, 0 is for 2^2 */
/* div[ 7:0]: Fractional component of the divisor */
/* phase: 0 ~ 3 */
void clock_rtc_set(uint8_t enable, uint8_t src, uint32_t div, uint8_t phase, uint8_t nudge)
{
uint32_t addr;
uint32_t val;
/* step1: disable clock */
addr = CLOCKS_BASE + CLOCK_RTC_CTRL_OFFSET;
putreg32(1 << CLOCK_ENABLE_POS, addr + REG_ALIAS_CLR_BITS);
/* step2: config ctrl register */
val = getreg32(addr);
/* config clock source */
val &= ~(CLOCK_AUXSRC_MASK << CLOCK_AUXSRC_POS);
val |= ((src & CLOCK_AUXSRC_MASK) << CLOCK_AUXSRC_POS);
/* config delay phase up to 3 cycles of the input clock */
val &= ~(CLOCK_PHASE_MASK << CLOCK_PHASE_POS);
val |= ((phase & CLOCK_PHASE_MASK) << CLOCK_PHASE_POS);
/* config signal shifts the phase of the output by 1 cycle of the input clock */
if (nudge) {
val |= 1 << CLOCK_NUDGE_POS;
} else {
val &= ~(1 << CLOCK_NUDGE_POS);
}
/* disable clock */
val &= ~(1 << CLOCK_ENABLE_POS);
putreg32(val, addr);
/* step3: config div */
putreg32(div, CLOCKS_BASE + CLOCK_RTC_DIV_OFFSET);
/* step4: config enable */
if (enable) {
putreg32(1 << CLOCK_ENABLE_POS, addr + REG_ALIAS_SET_BITS);
}
}
void clock_enable(uint8_t clock)
{
putreg32(1 << CLOCK_ENABLE_POS, CLOCKS_BASE + CLOCK_CTRL_OFFSET + REG_ALIAS_SET_BITS + (uint32_t)clock * 0xC);
}
void clock_disable(uint8_t clock)
{
putreg32(1 << CLOCK_ENABLE_POS, CLOCKS_BASE + CLOCK_CTRL_OFFSET + REG_ALIAS_CLR_BITS + (uint32_t)clock * 0xC);
}
void clock_kill(uint8_t clock)
{
putreg32(1 << CLOCK_KILL_POS, CLOCKS_BASE + CLOCK_CTRL_OFFSET + REG_ALIAS_SET_BITS + (uint32_t)clock * 0xC);
putreg32(1 << CLOCK_KILL_POS, CLOCKS_BASE + CLOCK_CTRL_OFFSET + REG_ALIAS_CLR_BITS + (uint32_t)clock * 0xC);
}