/* Copyright 2018 Canaan Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "sysctl.h" #define SYSCTRL_CLOCK_FREQ_IN0 (26000000UL) const u8 get_select_pll2[] = { [SYSCTL_SOURCE_IN0] = 0, [SYSCTL_SOURCE_PLL0] = 1, [SYSCTL_SOURCE_PLL1] = 2, }; const u8 get_source_pll2[] = { [0] = SYSCTL_SOURCE_IN0, [1] = SYSCTL_SOURCE_PLL0, [2] = SYSCTL_SOURCE_PLL1, }; const u8 get_select_aclk[] = { [SYSCTL_SOURCE_IN0] = 0, [SYSCTL_SOURCE_PLL0] = 1, }; const u8 get_source_aclk[] = { [0] = SYSCTL_SOURCE_IN0, [1] = SYSCTL_SOURCE_PLL0, }; volatile sysctl_t *const sysctl = (volatile sysctl_t *)SYSCTL_BASE_ADDR; u32 sysctl_get_git_id(void) { return sysctl->git_id.git_id; } u32 sysctl_get_freq(void) { return sysctl->clk_freq.clk_freq; } static void sysctl_reset_ctl(sysctl_reset_t reset, u8 rst_value) { switch (reset) { case SYSCTL_RESET_SOC: sysctl->soft_reset.soft_reset = rst_value; break; case SYSCTL_RESET_ROM: sysctl->peri_reset.rom_reset = rst_value; break; case SYSCTL_RESET_DMA: sysctl->peri_reset.dma_reset = rst_value; break; case SYSCTL_RESET_AI: sysctl->peri_reset.ai_reset = rst_value; break; case SYSCTL_RESET_DVP: sysctl->peri_reset.dvp_reset = rst_value; break; case SYSCTL_RESET_FFT: sysctl->peri_reset.fft_reset = rst_value; break; case SYSCTL_RESET_GPIO: sysctl->peri_reset.gpio_reset = rst_value; break; case SYSCTL_RESET_SPI0: sysctl->peri_reset.spi0_reset = rst_value; break; case SYSCTL_RESET_SPI1: sysctl->peri_reset.spi1_reset = rst_value; break; case SYSCTL_RESET_SPI2: sysctl->peri_reset.spi2_reset = rst_value; break; case SYSCTL_RESET_SPI3: sysctl->peri_reset.spi3_reset = rst_value; break; case SYSCTL_RESET_I2S0: sysctl->peri_reset.i2s0_reset = rst_value; break; case SYSCTL_RESET_I2S1: sysctl->peri_reset.i2s1_reset = rst_value; break; case SYSCTL_RESET_I2S2: sysctl->peri_reset.i2s2_reset = rst_value; break; case SYSCTL_RESET_I2C0: sysctl->peri_reset.i2c0_reset = rst_value; break; case SYSCTL_RESET_I2C1: sysctl->peri_reset.i2c1_reset = rst_value; break; case SYSCTL_RESET_I2C2: sysctl->peri_reset.i2c2_reset = rst_value; break; case SYSCTL_RESET_UART1: sysctl->peri_reset.uart1_reset = rst_value; break; case SYSCTL_RESET_UART2: sysctl->peri_reset.uart2_reset = rst_value; break; case SYSCTL_RESET_UART3: sysctl->peri_reset.uart3_reset = rst_value; break; case SYSCTL_RESET_AES: sysctl->peri_reset.aes_reset = rst_value; break; case SYSCTL_RESET_FPIOA: sysctl->peri_reset.fpioa_reset = rst_value; break; case SYSCTL_RESET_TIMER0: sysctl->peri_reset.timer0_reset = rst_value; break; case SYSCTL_RESET_TIMER1: sysctl->peri_reset.timer1_reset = rst_value; break; case SYSCTL_RESET_TIMER2: sysctl->peri_reset.timer2_reset = rst_value; break; case SYSCTL_RESET_WDT0: sysctl->peri_reset.wdt0_reset = rst_value; break; case SYSCTL_RESET_WDT1: sysctl->peri_reset.wdt1_reset = rst_value; break; case SYSCTL_RESET_SHA: sysctl->peri_reset.sha_reset = rst_value; break; case SYSCTL_RESET_RTC: sysctl->peri_reset.rtc_reset = rst_value; break; default: break; } } void sysctl_reset(sysctl_reset_t reset) { sysctl_reset_ctl(reset, 1); sysctl_reset_ctl(reset, 0); } static int sysctl_clock_bus_en(sysctl_clock_t clock, u8 en) { /* * The timer is under APB0, to prevent apb0_clk_en1 and apb0_clk_en0 * on same register, we split it to peripheral and central two * registers, to protect CPU close apb0 clock accidentally. * * The apb0_clk_en0 and apb0_clk_en1 have same function, * one of them set, the APB0 clock enable. */ /* The APB clock should carefully disable */ if (en) { switch (clock) { /* * These peripheral devices are under APB0 * GPIO, UART1, UART2, UART3, SPI_SLAVE, I2S0, I2S1, * I2S2, I2C0, I2C1, I2C2, FPIOA, SHA256, TIMER0, * TIMER1, TIMER2 */ case SYSCTL_CLOCK_GPIO: case SYSCTL_CLOCK_SPI2: case SYSCTL_CLOCK_I2S0: case SYSCTL_CLOCK_I2S1: case SYSCTL_CLOCK_I2S2: case SYSCTL_CLOCK_I2C0: case SYSCTL_CLOCK_I2C1: case SYSCTL_CLOCK_I2C2: case SYSCTL_CLOCK_UART1: case SYSCTL_CLOCK_UART2: case SYSCTL_CLOCK_UART3: case SYSCTL_CLOCK_FPIOA: case SYSCTL_CLOCK_TIMER0: case SYSCTL_CLOCK_TIMER1: case SYSCTL_CLOCK_TIMER2: case SYSCTL_CLOCK_SHA: sysctl->clk_en_cent.apb0_clk_en = en; break; /* * These peripheral devices are under APB1 * WDT, AES, OTP, DVP, SYSCTL */ case SYSCTL_CLOCK_AES: case SYSCTL_CLOCK_WDT0: case SYSCTL_CLOCK_WDT1: case SYSCTL_CLOCK_OTP: case SYSCTL_CLOCK_RTC: sysctl->clk_en_cent.apb1_clk_en = en; break; /* * These peripheral devices are under APB2 * SPI0, SPI1 */ case SYSCTL_CLOCK_SPI0: case SYSCTL_CLOCK_SPI1: sysctl->clk_en_cent.apb2_clk_en = en; break; default: break; } } return 0; } static int sysctl_clock_device_en(sysctl_clock_t clock, u8 en) { switch (clock) { /* * These devices are PLL */ case SYSCTL_CLOCK_PLL0: sysctl->pll0.pll_out_en0 = en; break; case SYSCTL_CLOCK_PLL1: sysctl->pll1.pll_out_en1 = en; break; case SYSCTL_CLOCK_PLL2: sysctl->pll2.pll_out_en2 = en; break; /* * These devices are CPU, SRAM, APB bus, ROM, DMA, AI */ case SYSCTL_CLOCK_CPU: sysctl->clk_en_cent.cpu_clk_en = en; break; case SYSCTL_CLOCK_SRAM0: sysctl->clk_en_cent.sram0_clk_en = en; break; case SYSCTL_CLOCK_SRAM1: sysctl->clk_en_cent.sram1_clk_en = en; break; case SYSCTL_CLOCK_APB0: sysctl->clk_en_cent.apb0_clk_en = en; break; case SYSCTL_CLOCK_APB1: sysctl->clk_en_cent.apb1_clk_en = en; break; case SYSCTL_CLOCK_APB2: sysctl->clk_en_cent.apb2_clk_en = en; break; case SYSCTL_CLOCK_ROM: sysctl->clk_en_peri.rom_clk_en = en; break; case SYSCTL_CLOCK_DMA: sysctl->clk_en_peri.dma_clk_en = en; break; case SYSCTL_CLOCK_AI: sysctl->clk_en_peri.ai_clk_en = en; break; case SYSCTL_CLOCK_DVP: sysctl->clk_en_peri.dvp_clk_en = en; break; case SYSCTL_CLOCK_FFT: sysctl->clk_en_peri.fft_clk_en = en; break; case SYSCTL_CLOCK_SPI3: sysctl->clk_en_peri.spi3_clk_en = en; break; /* * These peripheral devices are under APB0 * GPIO, UART1, UART2, UART3, SPI_SLAVE, I2S0, I2S1, * I2S2, I2C0, I2C1, I2C2, FPIOA, SHA256, TIMER0, * TIMER1, TIMER2 */ case SYSCTL_CLOCK_GPIO: sysctl->clk_en_peri.gpio_clk_en = en; break; case SYSCTL_CLOCK_SPI2: sysctl->clk_en_peri.spi2_clk_en = en; break; case SYSCTL_CLOCK_I2S0: sysctl->clk_en_peri.i2s0_clk_en = en; break; case SYSCTL_CLOCK_I2S1: sysctl->clk_en_peri.i2s1_clk_en = en; break; case SYSCTL_CLOCK_I2S2: sysctl->clk_en_peri.i2s2_clk_en = en; break; case SYSCTL_CLOCK_I2C0: sysctl->clk_en_peri.i2c0_clk_en = en; break; case SYSCTL_CLOCK_I2C1: sysctl->clk_en_peri.i2c1_clk_en = en; break; case SYSCTL_CLOCK_I2C2: sysctl->clk_en_peri.i2c2_clk_en = en; break; case SYSCTL_CLOCK_UART1: sysctl->clk_en_peri.uart1_clk_en = en; break; case SYSCTL_CLOCK_UART2: sysctl->clk_en_peri.uart2_clk_en = en; break; case SYSCTL_CLOCK_UART3: sysctl->clk_en_peri.uart3_clk_en = en; break; case SYSCTL_CLOCK_FPIOA: sysctl->clk_en_peri.fpioa_clk_en = en; break; case SYSCTL_CLOCK_TIMER0: sysctl->clk_en_peri.timer0_clk_en = en; break; case SYSCTL_CLOCK_TIMER1: sysctl->clk_en_peri.timer1_clk_en = en; break; case SYSCTL_CLOCK_TIMER2: sysctl->clk_en_peri.timer2_clk_en = en; break; case SYSCTL_CLOCK_SHA: sysctl->clk_en_peri.sha_clk_en = en; break; /* * These peripheral devices are under APB1 * WDT, AES, OTP, DVP, SYSCTL */ case SYSCTL_CLOCK_AES: sysctl->clk_en_peri.aes_clk_en = en; break; case SYSCTL_CLOCK_WDT0: sysctl->clk_en_peri.wdt0_clk_en = en; break; case SYSCTL_CLOCK_WDT1: sysctl->clk_en_peri.wdt1_clk_en = en; break; case SYSCTL_CLOCK_OTP: sysctl->clk_en_peri.otp_clk_en = en; break; case SYSCTL_CLOCK_RTC: sysctl->clk_en_peri.rtc_clk_en = en; break; /* * These peripheral devices are under APB2 * SPI0, SPI1 */ case SYSCTL_CLOCK_SPI0: sysctl->clk_en_peri.spi0_clk_en = en; break; case SYSCTL_CLOCK_SPI1: sysctl->clk_en_peri.spi1_clk_en = en; break; default: break; } return 0; } int sysctl_clock_enable(sysctl_clock_t clock) { if (clock >= SYSCTL_CLOCK_MAX) return -1; sysctl_clock_bus_en(clock, 1); sysctl_clock_device_en(clock, 1); return 0; } int sysctl_clock_disable(sysctl_clock_t clock) { if (clock >= SYSCTL_CLOCK_MAX) return -1; sysctl_clock_bus_en(clock, 0); sysctl_clock_device_en(clock, 0); return 0; } int sysctl_clock_set_threshold(sysctl_threshold_t which, int threshold) { int result = 0; switch (which) { /* * These threshold is 2 bit width */ case SYSCTL_THRESHOLD_ACLK: sysctl->clk_sel0.aclk_divider_sel = (u8)threshold & 0x03; break; /* * These threshold is 3 bit width */ case SYSCTL_THRESHOLD_APB0: sysctl->clk_sel0.apb0_clk_sel = (u8)threshold & 0x07; break; case SYSCTL_THRESHOLD_APB1: sysctl->clk_sel0.apb1_clk_sel = (u8)threshold & 0x07; break; case SYSCTL_THRESHOLD_APB2: sysctl->clk_sel0.apb2_clk_sel = (u8)threshold & 0x07; break; /* * These threshold is 4 bit width */ case SYSCTL_THRESHOLD_SRAM0: sysctl->clk_th0.sram0_gclk_threshold = (u8)threshold & 0x0F; break; case SYSCTL_THRESHOLD_SRAM1: sysctl->clk_th0.sram1_gclk_threshold = (u8)threshold & 0x0F; break; case SYSCTL_THRESHOLD_AI: sysctl->clk_th0.ai_gclk_threshold = (u8)threshold & 0x0F; break; case SYSCTL_THRESHOLD_DVP: sysctl->clk_th0.dvp_gclk_threshold = (u8)threshold & 0x0F; break; case SYSCTL_THRESHOLD_ROM: sysctl->clk_th0.rom_gclk_threshold = (u8)threshold & 0x0F; break; /* * These threshold is 8 bit width */ case SYSCTL_THRESHOLD_SPI0: sysctl->clk_th1.spi0_clk_threshold = (u8)threshold; break; case SYSCTL_THRESHOLD_SPI1: sysctl->clk_th1.spi1_clk_threshold = (u8)threshold; break; case SYSCTL_THRESHOLD_SPI2: sysctl->clk_th1.spi2_clk_threshold = (u8)threshold; break; case SYSCTL_THRESHOLD_SPI3: sysctl->clk_th1.spi3_clk_threshold = (u8)threshold; break; case SYSCTL_THRESHOLD_TIMER0: sysctl->clk_th2.timer0_clk_threshold = (u8)threshold; break; case SYSCTL_THRESHOLD_TIMER1: sysctl->clk_th2.timer1_clk_threshold = (u8)threshold; break; case SYSCTL_THRESHOLD_TIMER2: sysctl->clk_th2.timer2_clk_threshold = (u8)threshold; break; case SYSCTL_THRESHOLD_I2S0_M: sysctl->clk_th4.i2s0_mclk_threshold = (u8)threshold; break; case SYSCTL_THRESHOLD_I2S1_M: sysctl->clk_th4.i2s1_mclk_threshold = (u8)threshold; break; case SYSCTL_THRESHOLD_I2S2_M: sysctl->clk_th5.i2s2_mclk_threshold = (u8)threshold; break; case SYSCTL_THRESHOLD_I2C0: sysctl->clk_th5.i2c0_clk_threshold = (u8)threshold; break; case SYSCTL_THRESHOLD_I2C1: sysctl->clk_th5.i2c1_clk_threshold = (u8)threshold; break; case SYSCTL_THRESHOLD_I2C2: sysctl->clk_th5.i2c2_clk_threshold = (u8)threshold; break; case SYSCTL_THRESHOLD_WDT0: sysctl->clk_th6.wdt0_clk_threshold = (u8)threshold; break; case SYSCTL_THRESHOLD_WDT1: sysctl->clk_th6.wdt1_clk_threshold = (u8)threshold; break; /* * These threshold is 16 bit width */ case SYSCTL_THRESHOLD_I2S0: sysctl->clk_th3.i2s0_clk_threshold = (u16)threshold; break; case SYSCTL_THRESHOLD_I2S1: sysctl->clk_th3.i2s1_clk_threshold = (u16)threshold; break; case SYSCTL_THRESHOLD_I2S2: sysctl->clk_th4.i2s2_clk_threshold = (u16)threshold; break; default: result = -1; break; } return result; } int sysctl_clock_get_threshold(sysctl_threshold_t which) { int threshold = 0; switch (which) { /* * Select and get threshold value */ case SYSCTL_THRESHOLD_ACLK: threshold = (int)sysctl->clk_sel0.aclk_divider_sel; break; case SYSCTL_THRESHOLD_APB0: threshold = (int)sysctl->clk_sel0.apb0_clk_sel; break; case SYSCTL_THRESHOLD_APB1: threshold = (int)sysctl->clk_sel0.apb1_clk_sel; break; case SYSCTL_THRESHOLD_APB2: threshold = (int)sysctl->clk_sel0.apb2_clk_sel; break; case SYSCTL_THRESHOLD_SRAM0: threshold = (int)sysctl->clk_th0.sram0_gclk_threshold; break; case SYSCTL_THRESHOLD_SRAM1: threshold = (int)sysctl->clk_th0.sram1_gclk_threshold; break; case SYSCTL_THRESHOLD_AI: threshold = (int)sysctl->clk_th0.ai_gclk_threshold; break; case SYSCTL_THRESHOLD_DVP: threshold = (int)sysctl->clk_th0.dvp_gclk_threshold; break; case SYSCTL_THRESHOLD_ROM: threshold = (int)sysctl->clk_th0.rom_gclk_threshold; break; case SYSCTL_THRESHOLD_SPI0: threshold = (int)sysctl->clk_th1.spi0_clk_threshold; break; case SYSCTL_THRESHOLD_SPI1: threshold = (int)sysctl->clk_th1.spi1_clk_threshold; break; case SYSCTL_THRESHOLD_SPI2: threshold = (int)sysctl->clk_th1.spi2_clk_threshold; break; case SYSCTL_THRESHOLD_SPI3: threshold = (int)sysctl->clk_th1.spi3_clk_threshold; break; case SYSCTL_THRESHOLD_TIMER0: threshold = (int)sysctl->clk_th2.timer0_clk_threshold; break; case SYSCTL_THRESHOLD_TIMER1: threshold = (int)sysctl->clk_th2.timer1_clk_threshold; break; case SYSCTL_THRESHOLD_TIMER2: threshold = (int)sysctl->clk_th2.timer2_clk_threshold; break; case SYSCTL_THRESHOLD_I2S0: threshold = (int)sysctl->clk_th3.i2s0_clk_threshold; break; case SYSCTL_THRESHOLD_I2S1: threshold = (int)sysctl->clk_th3.i2s1_clk_threshold; break; case SYSCTL_THRESHOLD_I2S2: threshold = (int)sysctl->clk_th4.i2s2_clk_threshold; break; case SYSCTL_THRESHOLD_I2S0_M: threshold = (int)sysctl->clk_th4.i2s0_mclk_threshold; break; case SYSCTL_THRESHOLD_I2S1_M: threshold = (int)sysctl->clk_th4.i2s1_mclk_threshold; break; case SYSCTL_THRESHOLD_I2S2_M: threshold = (int)sysctl->clk_th5.i2s2_mclk_threshold; break; case SYSCTL_THRESHOLD_I2C0: threshold = (int)sysctl->clk_th5.i2c0_clk_threshold; break; case SYSCTL_THRESHOLD_I2C1: threshold = (int)sysctl->clk_th5.i2c1_clk_threshold; break; case SYSCTL_THRESHOLD_I2C2: threshold = (int)sysctl->clk_th5.i2c2_clk_threshold; break; case SYSCTL_THRESHOLD_WDT0: threshold = (int)sysctl->clk_th6.wdt0_clk_threshold; break; case SYSCTL_THRESHOLD_WDT1: threshold = (int)sysctl->clk_th6.wdt1_clk_threshold; break; default: break; } return threshold; } int sysctl_clock_set_clock_select(sysctl_clock_select_t which, int select) { int result = 0; switch (which) { /* * These clock select is 1 bit width */ case SYSCTL_CLOCK_SELECT_PLL0_BYPASS: sysctl->pll0.pll_bypass0 = select & 0x01; break; case SYSCTL_CLOCK_SELECT_PLL1_BYPASS: sysctl->pll1.pll_bypass1 = select & 0x01; break; case SYSCTL_CLOCK_SELECT_PLL2_BYPASS: sysctl->pll2.pll_bypass2 = select & 0x01; break; case SYSCTL_CLOCK_SELECT_ACLK: sysctl->clk_sel0.aclk_sel = select & 0x01; break; case SYSCTL_CLOCK_SELECT_SPI3: sysctl->clk_sel0.spi3_clk_sel = select & 0x01; break; case SYSCTL_CLOCK_SELECT_TIMER0: sysctl->clk_sel0.timer0_clk_sel = select & 0x01; break; case SYSCTL_CLOCK_SELECT_TIMER1: sysctl->clk_sel0.timer1_clk_sel = select & 0x01; break; case SYSCTL_CLOCK_SELECT_TIMER2: sysctl->clk_sel0.timer2_clk_sel = select & 0x01; break; case SYSCTL_CLOCK_SELECT_SPI3_SAMPLE: sysctl->clk_sel1.spi3_sample_clk_sel = select & 0x01; break; /* * These clock select is 2 bit width */ case SYSCTL_CLOCK_SELECT_PLL2: sysctl->pll2.pll_ckin_sel2 = select & 0x03; break; default: result = -1; break; } return result; } int sysctl_clock_get_clock_select(sysctl_clock_select_t which) { int clock_select = 0; switch (which) { /* * Select and get clock select value */ case SYSCTL_CLOCK_SELECT_PLL0_BYPASS: clock_select = (int)sysctl->pll0.pll_bypass0; break; case SYSCTL_CLOCK_SELECT_PLL1_BYPASS: clock_select = (int)sysctl->pll1.pll_bypass1; break; case SYSCTL_CLOCK_SELECT_PLL2_BYPASS: clock_select = (int)sysctl->pll2.pll_bypass2; break; case SYSCTL_CLOCK_SELECT_PLL2: clock_select = (int)sysctl->pll2.pll_ckin_sel2; break; case SYSCTL_CLOCK_SELECT_ACLK: clock_select = (int)sysctl->clk_sel0.aclk_sel; break; case SYSCTL_CLOCK_SELECT_SPI3: clock_select = (int)sysctl->clk_sel0.spi3_clk_sel; break; case SYSCTL_CLOCK_SELECT_TIMER0: clock_select = (int)sysctl->clk_sel0.timer0_clk_sel; break; case SYSCTL_CLOCK_SELECT_TIMER1: clock_select = (int)sysctl->clk_sel0.timer1_clk_sel; break; case SYSCTL_CLOCK_SELECT_TIMER2: clock_select = (int)sysctl->clk_sel0.timer2_clk_sel; break; case SYSCTL_CLOCK_SELECT_SPI3_SAMPLE: clock_select = (int)sysctl->clk_sel1.spi3_sample_clk_sel; break; default: break; } return clock_select; } u32 sysctl_clock_source_get_freq(sysctl_clock_source_t input) { u32 result; switch (input) { case SYSCTL_SOURCE_IN0: result = SYSCTRL_CLOCK_FREQ_IN0; break; case SYSCTL_SOURCE_PLL0: result = sysctl_pll_get_freq(SYSCTL_PLL0); break; case SYSCTL_SOURCE_PLL1: result = sysctl_pll_get_freq(SYSCTL_PLL1); break; case SYSCTL_SOURCE_PLL2: result = sysctl_pll_get_freq(SYSCTL_PLL2); break; case SYSCTL_SOURCE_ACLK: result = sysctl_clock_get_freq(SYSCTL_CLOCK_ACLK); break; default: result = 0; break; } return result; } static int sysctl_pll_is_lock(sysctl_pll_t pll) { /* * All bit enable means PLL lock * * struct pll_lock_t * { * u8 overflow : 1; * u8 rfslip : 1; * u8 fbslip : 1; * }; * */ if (pll >= SYSCTL_PLL_MAX) return 0; switch (pll) { case SYSCTL_PLL0: return sysctl->pll_lock.pll_lock0 == 3; case SYSCTL_PLL1: return sysctl->pll_lock.pll_lock1 & 1; case SYSCTL_PLL2: return sysctl->pll_lock.pll_lock2 & 1; default: break; } return 0; } static int sysctl_pll_clear_slip(sysctl_pll_t pll) { if (pll >= SYSCTL_PLL_MAX) return -1; switch (pll) { case SYSCTL_PLL0: sysctl->pll_lock.pll_slip_clear0 = 1; break; case SYSCTL_PLL1: sysctl->pll_lock.pll_slip_clear1 = 1; break; case SYSCTL_PLL2: sysctl->pll_lock.pll_slip_clear2 = 1; break; default: break; } return sysctl_pll_is_lock(pll) ? 0 : -1; } int sysctl_pll_enable(sysctl_pll_t pll) { /* * ---+ * PWRDN | * +------------------------------------------------------------- * ^ * | * | * t1 * +------------------+ * RESET | | * ----------+ +----------------------------------- * ^ ^ ^ * |<----- t_rst ---->|<---------- t_lock ---------->| * | | | * t2 t3 t4 */ if (pll >= SYSCTL_PLL_MAX) return -1; switch (pll) { case SYSCTL_PLL0: /* Do not bypass PLL */ sysctl->pll0.pll_bypass0 = 0; /* * Power on the PLL, negtive from PWRDN * 0 is power off * 1 is power on */ sysctl->pll0.pll_pwrd0 = 1; /* * Reset trigger of the PLL, connected RESET * 0 is free * 1 is reset */ sysctl->pll0.pll_reset0 = 0; sysctl->pll0.pll_reset0 = 1; asm volatile ("nop"); asm volatile ("nop"); sysctl->pll0.pll_reset0 = 0; break; case SYSCTL_PLL1: /* Do not bypass PLL */ sysctl->pll1.pll_bypass1 = 0; /* * Power on the PLL, negtive from PWRDN * 0 is power off * 1 is power on */ sysctl->pll1.pll_pwrd1 = 1; /* * Reset trigger of the PLL, connected RESET * 0 is free * 1 is reset */ sysctl->pll1.pll_reset1 = 0; sysctl->pll1.pll_reset1 = 1; asm volatile ("nop"); asm volatile ("nop"); sysctl->pll1.pll_reset1 = 0; break; case SYSCTL_PLL2: /* Do not bypass PLL */ sysctl->pll2.pll_bypass2 = 0; /* * Power on the PLL, negtive from PWRDN * 0 is power off * 1 is power on */ sysctl->pll2.pll_pwrd2 = 1; /* * Reset trigger of the PLL, connected RESET * 0 is free * 1 is reset */ sysctl->pll2.pll_reset2 = 0; sysctl->pll2.pll_reset2 = 1; asm volatile ("nop"); asm volatile ("nop"); sysctl->pll2.pll_reset2 = 0; break; default: break; } return 0; } int sysctl_pll_disable(sysctl_pll_t pll) { if (pll >= SYSCTL_PLL_MAX) return -1; switch (pll) { case SYSCTL_PLL0: /* Bypass PLL */ sysctl->pll0.pll_bypass0 = 1; /* * Power on the PLL, negtive from PWRDN * 0 is power off * 1 is power on */ sysctl->pll0.pll_pwrd0 = 0; break; case SYSCTL_PLL1: /* Bypass PLL */ sysctl->pll1.pll_bypass1 = 1; /* * Power on the PLL, negtive from PWRDN * 0 is power off * 1 is power on */ sysctl->pll1.pll_pwrd1 = 0; break; case SYSCTL_PLL2: /* Bypass PLL */ sysctl->pll2.pll_bypass2 = 1; /* * Power on the PLL, negtive from PWRDN * 0 is power off * 1 is power on */ sysctl->pll2.pll_pwrd2 = 0; break; default: break; } return 0; } u32 sysctl_pll_get_freq(sysctl_pll_t pll) { u32 freq_in = 0; u32 nr = 0, nf = 0, od = 0; u8 select = 0; if (pll >= SYSCTL_PLL_MAX) return 0; switch (pll) { case SYSCTL_PLL0: freq_in = sysctl_clock_source_get_freq(SYSCTL_SOURCE_IN0); nr = sysctl->pll0.clkr0 + 1; nf = sysctl->pll0.clkf0 + 1; od = sysctl->pll0.clkod0 + 1; break; case SYSCTL_PLL1: freq_in = sysctl_clock_source_get_freq(SYSCTL_SOURCE_IN0); nr = sysctl->pll1.clkr1 + 1; nf = sysctl->pll1.clkf1 + 1; od = sysctl->pll1.clkod1 + 1; break; case SYSCTL_PLL2: /* * Get input freq accroding select register */ select = sysctl->pll2.pll_ckin_sel2; if (select < sizeof(get_source_pll2)) freq_in = sysctl_clock_source_get_freq(get_source_pll2[select]); else return 0; nr = sysctl->pll2.clkr2 + 1; nf = sysctl->pll2.clkf2 + 1; od = sysctl->pll2.clkod2 + 1; break; default: break; } /* * Get final PLL output freq * FOUT = FIN / NR * NF / OD */ return (double)freq_in / (double)nr * (double)nf / (double)od; } static u32 sysctl_pll_source_set_freq(sysctl_pll_t pll, sysctl_clock_source_t source, u32 freq) { const double vco_min = 3.5e+08; const double vco_max = 1.75e+09; const double ref_min = 1.36719e+07; const double ref_max = 1.75e+09; const int nr_min = 1; const int nr_max = 16; const int nf_min = 1; const int nf_max = 64; const int no_min = 1; const int no_max = 16; const int nb_min = 1; const int nb_max = 64; const int max_vco = 1; const int ref_rng = 1; int nr = 0; int nrx = 0; int nf = 0; int nfi = 0; int no = 0; int noe = 0; int not = 0; int nor = 0; int nore = 0; int nb = 0; int first = 0; int firstx = 0; int found = 0; long long nfx = 0; double fin = 0, fout = 0, fvco = 0; double val = 0, nval = 0, err = 0, merr = 0, terr = 0; int x_nrx = 0, x_no = 0, x_nb = 0; long long x_nfx = 0; double x_fvco = 0, x_err = 0; sysctl_pll0_t pll0; sysctl_pll1_t pll1; sysctl_pll2_t pll2; u32 freq_in = 0; if (pll >= SYSCTL_PLL_MAX) return 0; if (source >= SYSCTL_SOURCE_MAX) return 0; switch (pll) { case SYSCTL_PLL0: case SYSCTL_PLL1: /* * Check input clock source */ if (source != SYSCTL_SOURCE_IN0) return 0; freq_in = sysctl_clock_source_get_freq(SYSCTL_SOURCE_IN0); /* * Check input clock freq */ if (freq_in == 0) return 0; break; case SYSCTL_PLL2: /* * Check input clock source */ if (source < sizeof(get_select_pll2)) freq_in = sysctl_clock_source_get_freq(source); /* * Check input clock freq */ if (freq_in == 0) return 0; break; default: return 0; } /* * Begin calculate PLL registers' value */ fin = freq_in; fout = freq; val = fout / fin; terr = 0.5 / ((double)(nf_max / 2)); first = firstx = 1; if (terr != -2) { first = 0; if (terr == 0) terr = 1e-16; merr = fabs(terr); } found = 0; for (nfi = val; nfi < nf_max; ++nfi) { nr = rint(((double)nfi) / val); if (nr == 0) continue; if ((ref_rng) && (nr < nr_min)) continue; if (fin / ((double)nr) > ref_max) continue; nrx = nr; nf = nfx = nfi; nval = ((double)nfx) / ((double)nr); if (nf == 0) nf = 1; err = 1 - nval / val; if ((first) || (fabs(err) < merr * (1 + 1e-6)) || (fabs(err) < 1e-16)) { not = floor(vco_max / fout); for (no = (not > no_max) ? no_max : not; no > no_min; --no) { if ((ref_rng) && ((nr / no) < nr_min)) continue; if ((nr % no) == 0) break; } if ((nr % no) != 0) continue; nor = ((not > no_max) ? no_max : not) / no; nore = nf_max / nf; if (nor > nore) nor = nore; noe = ceil(vco_min / fout); if (!max_vco) { nore = (noe - 1) / no + 1; nor = nore; not = 0; /* force next if to fail */ } if ((((no * nor) < (not >> 1)) || ((no * nor) < noe)) && ((no * nor) < (nf_max / nf))) { no = nf_max / nf; if (no > no_max) no = no_max; if (no > not) no = not; nfx *= no; nf *= no; if ((no > 1) && (!firstx)) continue; /* wait for larger nf in later iterations */ } else { nrx /= no; nfx *= nor; nf *= nor; no *= nor; if (no > no_max) continue; if ((nor > 1) && (!firstx)) continue; /* wait for larger nf in later iterations */ } nb = nfx; if (nb < nb_min) nb = nb_min; if (nb > nb_max) continue; fvco = fin / ((double)nrx) * ((double)nfx); if (fvco < vco_min) continue; if (fvco > vco_max) continue; if (nf < nf_min) continue; if ((ref_rng) && (fin / ((double)nrx) < ref_min)) continue; if ((ref_rng) && (nrx > nr_max)) continue; if (!(((firstx) && (terr < 0)) || (fabs(err) < merr * (1 - 1e-6)) || ((max_vco) && (no > x_no)))) continue; if ((!firstx) && (terr >= 0) && (nrx > x_nrx)) continue; found = 1; x_no = no; x_nrx = nrx; x_nfx = nfx; x_nb = nb; x_fvco = fvco; x_err = err; first = firstx = 0; merr = fabs(err); if (terr != -1) continue; } } if (!found) return 0; nrx = x_nrx; nfx = x_nfx; no = x_no; nb = x_nb; fvco = x_fvco; err = x_err; if ((terr != -2) && (fabs(err) >= terr * (1 - 1e-6))) return 0; /* * Begin write PLL registers' value, * Using atomic write method. */ switch (pll) { case SYSCTL_PLL0: /* Read register from bus */ pll0 = sysctl->pll0; /* Set register temporary value */ pll0.clkr0 = nrx - 1; pll0.clkf0 = nfx - 1; pll0.clkod0 = no - 1; pll0.bwadj0 = nb - 1; /* Write register back to bus */ sysctl->pll0 = pll0; break; case SYSCTL_PLL1: /* Read register from bus */ pll1 = sysctl->pll1; /* Set register temporary value */ pll1.clkr1 = nrx - 1; pll1.clkf1 = nfx - 1; pll1.clkod1 = no - 1; pll1.bwadj1 = nb - 1; /* Write register back to bus */ sysctl->pll1 = pll1; break; case SYSCTL_PLL2: /* Read register from bus */ pll2 = sysctl->pll2; /* Set register temporary value */ if (source < sizeof(get_select_pll2)) pll2.pll_ckin_sel2 = get_select_pll2[source]; pll2.clkr2 = nrx - 1; pll2.clkf2 = nfx - 1; pll2.clkod2 = no - 1; pll2.bwadj2 = nb - 1; /* Write register back to bus */ sysctl->pll2 = pll2; break; default: return 0; } return sysctl_pll_get_freq(pll); } u32 sysctl_clock_get_freq(sysctl_clock_t clock) { u32 source = 0; u32 result = 0; switch (clock) { /* * The clock IN0 */ case SYSCTL_CLOCK_IN0: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_IN0); result = source; break; /* * These clock directly under PLL clock domain * They are using gated divider. */ case SYSCTL_CLOCK_PLL0: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_PLL0); result = source; break; case SYSCTL_CLOCK_PLL1: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_PLL1); result = source; break; case SYSCTL_CLOCK_PLL2: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_PLL2); result = source; break; /* * These clock directly under ACLK clock domain */ case SYSCTL_CLOCK_CPU: switch (sysctl_clock_get_clock_select(SYSCTL_CLOCK_SELECT_ACLK)) { case 0: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_IN0); break; case 1: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_PLL0) / (2ULL << sysctl_clock_get_threshold(SYSCTL_THRESHOLD_ACLK)); break; default: break; } result = source; break; case SYSCTL_CLOCK_DMA: switch (sysctl_clock_get_clock_select(SYSCTL_CLOCK_SELECT_ACLK)) { case 0: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_IN0); break; case 1: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_PLL0) / (2ULL << sysctl_clock_get_threshold(SYSCTL_THRESHOLD_ACLK)); break; default: break; } result = source; break; case SYSCTL_CLOCK_FFT: switch (sysctl_clock_get_clock_select(SYSCTL_CLOCK_SELECT_ACLK)) { case 0: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_IN0); break; case 1: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_PLL0) / (2ULL << sysctl_clock_get_threshold(SYSCTL_THRESHOLD_ACLK)); break; default: break; } result = source; break; case SYSCTL_CLOCK_ACLK: switch (sysctl_clock_get_clock_select(SYSCTL_CLOCK_SELECT_ACLK)) { case 0: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_IN0); break; case 1: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_PLL0) / (2ULL << sysctl_clock_get_threshold(SYSCTL_THRESHOLD_ACLK)); break; default: break; } result = source; break; case SYSCTL_CLOCK_HCLK: switch (sysctl_clock_get_clock_select(SYSCTL_CLOCK_SELECT_ACLK)) { case 0: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_IN0); break; case 1: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_PLL0) / (2ULL << sysctl_clock_get_threshold(SYSCTL_THRESHOLD_ACLK)); break; default: break; } result = source; break; /* * These clock under ACLK clock domain. * They are using gated divider. */ case SYSCTL_CLOCK_SRAM0: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_ACLK); result = source / (sysctl_clock_get_threshold(SYSCTL_THRESHOLD_SRAM0) + 1); break; case SYSCTL_CLOCK_SRAM1: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_ACLK); result = source / (sysctl_clock_get_threshold(SYSCTL_THRESHOLD_SRAM1) + 1); break; case SYSCTL_CLOCK_ROM: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_ACLK); result = source / (sysctl_clock_get_threshold(SYSCTL_THRESHOLD_ROM) + 1); break; case SYSCTL_CLOCK_DVP: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_ACLK); result = source / (sysctl_clock_get_threshold(SYSCTL_THRESHOLD_DVP) + 1); break; /* * These clock under ACLK clock domain. * They are using even divider. */ case SYSCTL_CLOCK_APB0: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_ACLK); result = source / (sysctl_clock_get_threshold(SYSCTL_THRESHOLD_APB0) + 1); break; case SYSCTL_CLOCK_APB1: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_ACLK); result = source / (sysctl_clock_get_threshold(SYSCTL_THRESHOLD_APB1) + 1); break; case SYSCTL_CLOCK_APB2: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_ACLK); result = source / (sysctl_clock_get_threshold(SYSCTL_THRESHOLD_APB2) + 1); break; /* * These clock under AI clock domain. * They are using gated divider. */ case SYSCTL_CLOCK_AI: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_PLL1); result = source / (sysctl_clock_get_threshold(SYSCTL_THRESHOLD_AI) + 1); break; /* * These clock under I2S clock domain. * They are using even divider. */ case SYSCTL_CLOCK_I2S0: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_PLL2); result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_I2S0) + 1) * 2); break; case SYSCTL_CLOCK_I2S1: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_PLL2); result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_I2S1) + 1) * 2); break; case SYSCTL_CLOCK_I2S2: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_PLL2); result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_I2S2) + 1) * 2); break; /* * These clock under WDT clock domain. * They are using even divider. */ case SYSCTL_CLOCK_WDT0: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_IN0); result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_WDT0) + 1) * 2); break; case SYSCTL_CLOCK_WDT1: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_IN0); result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_WDT1) + 1) * 2); break; /* * These clock under PLL0 clock domain. * They are using even divider. */ case SYSCTL_CLOCK_SPI0: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_PLL0); result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_SPI0) + 1) * 2); break; case SYSCTL_CLOCK_SPI1: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_PLL0); result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_SPI1) + 1) * 2); break; case SYSCTL_CLOCK_SPI2: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_PLL0); result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_SPI2) + 1) * 2); break; case SYSCTL_CLOCK_I2C0: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_PLL0); result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_I2C0) + 1) * 2); break; case SYSCTL_CLOCK_I2C1: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_PLL0); result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_I2C1) + 1) * 2); break; case SYSCTL_CLOCK_I2C2: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_PLL0); result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_I2C2) + 1) * 2); break; /* * These clock under PLL0_SEL clock domain. * They are using even divider. */ case SYSCTL_CLOCK_SPI3: switch (sysctl_clock_get_clock_select(SYSCTL_CLOCK_SELECT_SPI3)) { case 0: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_IN0); break; case 1: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_PLL0); break; default: break; } result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_SPI3) + 1) * 2); break; case SYSCTL_CLOCK_TIMER0: switch (sysctl_clock_get_clock_select(SYSCTL_CLOCK_SELECT_TIMER0)) { case 0: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_IN0); break; case 1: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_PLL0); break; default: break; } result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_TIMER0) + 1) * 2); break; case SYSCTL_CLOCK_TIMER1: switch (sysctl_clock_get_clock_select(SYSCTL_CLOCK_SELECT_TIMER1)) { case 0: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_IN0); break; case 1: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_PLL0); break; default: break; } result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_TIMER1) + 1) * 2); break; case SYSCTL_CLOCK_TIMER2: switch (sysctl_clock_get_clock_select(SYSCTL_CLOCK_SELECT_TIMER2)) { case 0: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_IN0); break; case 1: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_PLL0); break; default: break; } result = source / ((sysctl_clock_get_threshold(SYSCTL_THRESHOLD_TIMER2) + 1) * 2); break; /* * These clock under MISC clock domain. * They are using even divider. */ /* * These clock under APB0 clock domain. * They are using even divider. */ case SYSCTL_CLOCK_GPIO: source = sysctl_clock_get_freq(SYSCTL_CLOCK_APB0); result = source; break; case SYSCTL_CLOCK_UART1: source = sysctl_clock_get_freq(SYSCTL_CLOCK_APB0); result = source; break; case SYSCTL_CLOCK_UART2: source = sysctl_clock_get_freq(SYSCTL_CLOCK_APB0); result = source; break; case SYSCTL_CLOCK_UART3: source = sysctl_clock_get_freq(SYSCTL_CLOCK_APB0); result = source; break; case SYSCTL_CLOCK_FPIOA: source = sysctl_clock_get_freq(SYSCTL_CLOCK_APB0); result = source; break; case SYSCTL_CLOCK_SHA: source = sysctl_clock_get_freq(SYSCTL_CLOCK_APB0); result = source; break; /* * These clock under APB1 clock domain. * They are using even divider. */ case SYSCTL_CLOCK_AES: source = sysctl_clock_get_freq(SYSCTL_CLOCK_APB1); result = source; break; case SYSCTL_CLOCK_OTP: source = sysctl_clock_get_freq(SYSCTL_CLOCK_APB1); result = source; break; case SYSCTL_CLOCK_RTC: source = sysctl_clock_source_get_freq(SYSCTL_SOURCE_IN0); result = source; break; /* * These clock under APB2 clock domain. * They are using even divider. */ default: break; } return result; } int sysctl_dma_select(sysctl_dma_channel_t channel, sysctl_dma_select_t select) { sysctl_dma_sel0_t dma_sel0; sysctl_dma_sel1_t dma_sel1; /* Read register from bus */ dma_sel0 = sysctl->dma_sel0; dma_sel1 = sysctl->dma_sel1; switch (channel) { case SYSCTL_DMA_CHANNEL_0: dma_sel0.dma_sel0 = select; break; case SYSCTL_DMA_CHANNEL_1: dma_sel0.dma_sel1 = select; break; case SYSCTL_DMA_CHANNEL_2: dma_sel0.dma_sel2 = select; break; case SYSCTL_DMA_CHANNEL_3: dma_sel0.dma_sel3 = select; break; case SYSCTL_DMA_CHANNEL_4: dma_sel0.dma_sel4 = select; break; case SYSCTL_DMA_CHANNEL_5: dma_sel1.dma_sel5 = select; break; default: return -1; } /* Write register back to bus */ sysctl->dma_sel0 = dma_sel0; sysctl->dma_sel1 = dma_sel1; return 0; } u32 sysctl_pll_fast_enable_pll(void) { sysctl_pll0_t pll0; sysctl_pll1_t pll1; sysctl_pll2_t pll2; /* Read register from bus */ pll0 = sysctl->pll0; pll1 = sysctl->pll1; pll2 = sysctl->pll2; /* * Begin write PLL registers' value, * Using atomic write method. */ /* PLL VCO MAX freq: 1.8GHz */ /* PLL0: 26M reference clk get 793M output clock */ pll0.clkr0 = 0; pll0.clkf0 = 60; pll0.clkod0 = 1; pll0.bwadj0 = 60; /* PLL1: 26M reference clk get 390M output clock */ pll1.clkr1 = 0; pll1.clkf1 = 59; pll1.clkod1 = 3; pll1.bwadj1 = 59; /* PLL2: 26M reference clk get 390M output clock */ pll2.clkr2 = 0; pll2.clkf2 = 59; pll2.clkod2 = 3; pll2.bwadj2 = 59; /* Write register to bus */ sysctl->pll0 = pll0; sysctl->pll1 = pll1; sysctl->pll2 = pll2; sysctl_pll_enable(SYSCTL_PLL0); sysctl_pll_enable(SYSCTL_PLL1); sysctl_pll_enable(SYSCTL_PLL2); while (sysctl_pll_is_lock(SYSCTL_PLL0) == 0) sysctl_pll_clear_slip(SYSCTL_PLL0); while (sysctl_pll_is_lock(SYSCTL_PLL1) == 0) sysctl_pll_clear_slip(SYSCTL_PLL1); while (sysctl_pll_is_lock(SYSCTL_PLL2) == 0) sysctl_pll_clear_slip(SYSCTL_PLL2); sysctl_clock_enable(SYSCTL_CLOCK_PLL0); sysctl_clock_enable(SYSCTL_CLOCK_PLL1); sysctl_clock_enable(SYSCTL_CLOCK_PLL2); /* Set ACLK to PLL0 */ sysctl_clock_set_clock_select(SYSCTL_CLOCK_SELECT_ACLK, SYSCTL_SOURCE_PLL0); return 0; } u32 sysctl_set_spi0_dvp_data(u8 en) { sysctl->misc.spi_dvp_data_enable = en; return 0; } void sysctl_set_power_mode(sysctl_power_bank_t power_bank, sysctl_io_power_mode_t io_power_mode) { if(io_power_mode) *((u32 *)(&sysctl->power_sel)) |= (1 << power_bank); else *((u32 *)(&sysctl->power_sel)) &= ~(1 << power_bank); } u32 sysctl_pll_set_freq(sysctl_pll_t pll, u32 pll_freq) { u32 result; if(pll_freq == 0) return 0; volatile sysctl_general_pll_t *v_pll_t; switch(pll) { case SYSCTL_PLL0: v_pll_t = (sysctl_general_pll_t *)(&sysctl->pll0); break; case SYSCTL_PLL1: v_pll_t = (sysctl_general_pll_t *)(&sysctl->pll1); break; case SYSCTL_PLL2: v_pll_t = (sysctl_general_pll_t *)(&sysctl->pll2); break; default: return 0; break; } /* 1. Change CPU CLK to XTAL */ if (pll == SYSCTL_PLL0) sysctl_clock_set_clock_select(SYSCTL_CLOCK_SELECT_ACLK, SYSCTL_SOURCE_IN0); /* 2. Disable PLL output */ v_pll_t->pll_out_en = 0; /* 3. Turn off PLL */ v_pll_t->pll_pwrd = 0; /* 4. Set PLL new value */ if (pll == SYSCTL_PLL2) result = sysctl_pll_source_set_freq(pll, v_pll_t->pll_ckin_sel, pll_freq); else result = sysctl_pll_source_set_freq(pll, SYSCTL_SOURCE_IN0, pll_freq); /* 5. Power on PLL */ v_pll_t->pll_pwrd = 1; /* wait >100ns */ sysctl_usleep(1); /* 6. Reset PLL then Release Reset*/ v_pll_t->pll_reset = 0; v_pll_t->pll_reset = 1; /* wait >100ns */ sysctl_usleep(1); v_pll_t->pll_reset = 0; /* 7. Get lock status, wait PLL stable */ while (sysctl_pll_is_lock(pll) == 0) sysctl_pll_clear_slip(pll); /* 8. Enable PLL output */ v_pll_t->pll_out_en = 1; /* 9. Change CPU CLK to PLL */ if (pll == SYSCTL_PLL0) sysctl_clock_set_clock_select(SYSCTL_CLOCK_SELECT_ACLK, SYSCTL_SOURCE_PLL0); return result; } u32 sysctl_cpu_set_freq(u32 freq) { if (freq == 0) return 0; return sysctl_pll_set_freq(SYSCTL_PLL0, (sysctl->clk_sel0.aclk_divider_sel + 1) * 2 * freq); } void sysctl_enable_irq(void) { set_csr(mie, MIP_MEIP); set_csr(mstatus, MSTATUS_MIE); } void sysctl_disable_irq(void) { clear_csr(mie, MIP_MEIP); clear_csr(mstatus, MSTATUS_MIE); } u64 sysctl_get_time_us(void) { u64 v_cycle = read_cycle(); return v_cycle * 1000000 / sysctl_clock_get_freq(SYSCTL_CLOCK_CPU); } void sysctl_usleep(u64 usec) { u64 nop_all = usec * sysctl_clock_get_freq(SYSCTL_CLOCK_CPU) / 1000000UL; u64 cycle = read_cycle(); while (read_cycle() - cycle < nop_all); }