/* * Copyright (c) 2024 hydrogenium2020-offical * Copyright (c) 2018 naehrwert * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "t124.h" #include "clock.h" #include "timer.h" #include "pmc.h" #include "printf.h" #include "util.h" #include "types.h" enum { OSC_FREQ_13 = 0, OSC_FREQ_16P8 = 1, OSC_FREQ_19P2 = 4, OSC_FREQ_38P4 = 5, OSC_FREQ_12 = 8, OSC_FREQ_48 = 9, OSC_FREQ_26 = 12 }; /* This table defines the frequency dividers for every PLL to turn the external * OSC clock into the frequencies defined by TEGRA_PLL*_KHZ in soc/clock.h. * All PLLs have three dividers (n, m and p), with the governing formula for * the output frequency being CF = (IN / m), VCO = CF * n and OUT = VCO / (2^p). * All divisor configurations must meet the PLL's constraints for VCO and CF: * PLLX: 12 MHz < CF < 50 MHz, 700 MHz < VCO < 3000 MHz * PLLC: 12 MHz < CF < 50 MHz, 600 MHz < VCO < 1400 MHz * PLLM: 12 MHz < CF < 50 MHz, 400 MHz < VCO < 1066 MHz * PLLP: 1 MHz < CF < 6 MHz, 200 MHz < VCO < 700 MHz * PLLD: 1 MHz < CF < 6 MHz, 500 MHz < VCO < 1000 MHz * PLLU: 1 MHz < CF < 6 MHz, 480 MHz < VCO < 960 MHz * PLLDP: 12 MHz < CF < 38 MHz, 600 MHz < VCO < 1200 MHz * (values taken from Linux' drivers/clk/tegra/clk-tegra124.c). */ struct { int khz; struct pllcx_dividers pllx; /* target: CONFIG_PLLX_KHZ */ struct pllcx_dividers pllc; /* target: 600 MHz */ /* PLLM is set up dynamically by clock_sdram(). */ /* PLLP is hardwired to 408 MHz in HW (unless we set BASE_OVRD). */ struct pllu_dividers pllu; /* target; 960 MHz */ struct pllcx_dividers plldp; /* target; 270 MHz */ /* PLLDP treats p differently (OUT = VCO / (p + 1) for p < 6). */ } static const osc_table[16] = { [OSC_FREQ_12] = { .khz = 12000, .pllx = {.n = TEGRA_PLLX_KHZ / 12000, .m = 1, .p = 0}, .pllc = {.n = 50, .m = 1, .p = 0}, .pllu = {.n = 960, .m = 12, .p = 0, .cpcon = 12, .lfcon = 2}, .plldp = {.n = 90, .m = 1, .p = 3}, }, [OSC_FREQ_13] = { .khz = 13000, .pllx = {.n = TEGRA_PLLX_KHZ / 13000, .m = 1, .p = 0}, .pllc = {.n = 46, .m = 1, .p = 0}, /* 598.0 MHz */ .pllu = {.n = 960, .m = 13, .p = 0, .cpcon = 12, .lfcon = 2}, .plldp = {.n = 83, .m = 1, .p = 3}, /* 269.8 MHz */ }, [OSC_FREQ_16P8] = { .khz = 16800, .pllx = {.n = TEGRA_PLLX_KHZ / 16800, .m = 1, .p = 0}, .pllc = {.n = 71, .m = 1, .p = 1}, /* 596.4 MHz */ .pllu = {.n = 400, .m = 7, .p = 0, .cpcon = 5, .lfcon = 2}, .plldp = {.n = 64, .m = 1, .p = 3}, /* 268.8 MHz */ }, [OSC_FREQ_19P2] = { .khz = 19200, .pllx = {.n = TEGRA_PLLX_KHZ / 19200, .m = 1, .p = 0}, .pllc = {.n = 62, .m = 1, .p = 1}, /* 595.2 MHz */ .pllu = {.n = 200, .m = 4, .p = 0, .cpcon = 3, .lfcon = 2}, .plldp = {.n = 56, .m = 1, .p = 3}, /* 268.8 MHz */ }, [OSC_FREQ_26] = { .khz = 26000, .pllx = {.n = TEGRA_PLLX_KHZ / 26000, .m = 1, .p = 0}, .pllc = {.n = 23, .m = 1, .p = 0}, /* 598.0 MHz */ .pllu = {.n = 960, .m = 26, .p = 0, .cpcon = 12, .lfcon = 2}, .plldp = {.n = 83, .m = 2, .p = 3}, /* 269.8 MHz */ }, /* These oscillators get predivided as PLL inputs... n/m/p divisors for * 38.4 should always match 19.2, and 48 should always match 12. */ [OSC_FREQ_38P4] = { .khz = 38400, .pllx = {.n = TEGRA_PLLX_KHZ / 19200, .m = 1, .p = 0}, .pllc = {.n = 62, .m = 1, .p = 1}, /* 595.2 MHz */ .pllu = {.n = 200, .m = 4, .p = 0, .cpcon = 3, .lfcon = 2}, .plldp = {.n = 56, .m = 1, .p = 3}, /* 268.8 MHz */ }, [OSC_FREQ_48] = { .khz = 48000, .pllx = {.n = TEGRA_PLLX_KHZ / 12000, .m = 1, .p = 0}, .pllc = {.n = 50, .m = 1, .p = 0}, .pllu = {.n = 960, .m = 12, .p = 0, .cpcon = 12, .lfcon = 2}, .plldp = {.n = 90, .m = 1, .p = 3}, }, }; enum { SCLK_SYS_STATE_STDBY, SCLK_SYS_STATE_IDLE, SCLK_SYS_STATE_RUN, SCLK_SYS_STATE_IRQ = 4U, SCLK_SYS_STATE_FIQ = 8U, }; void clock_enable(const clk_rst_t *clk) { // Put clock into reset. CLOCK(clk->reset) = (CLOCK(clk->reset) & ~BIT(clk->index)) | BIT(clk->index); // Disable. CLOCK(clk->enable) &= ~BIT(clk->index); // Configure clock source if required. if (clk->source) CLOCK(clk->source) = clk->clk_div | (clk->clk_src << 29); // Enable. CLOCK(clk->enable) = (CLOCK(clk->enable) & ~BIT(clk->index)) | BIT(clk->index); sleep(2); // Take clock off reset. CLOCK(clk->reset) &= ~BIT(clk->index); } void clock_disable(const clk_rst_t *clk) { // Put clock into reset. CLOCK(clk->reset) = (CLOCK(clk->reset) & ~BIT(clk->index)) | BIT(clk->index); // Disable. CLOCK(clk->enable) &= ~BIT(clk->index); } static void init_pll(u32 pll_base_offset, u32 pll_misc_offset, const union pll_fields pll, u32 lock) { //TODO: rename u32 dividers = pll.div.n << PLL_BASE_DIVN_SHIFT | pll.div.m << PLL_BASE_DIVM_SHIFT | pll.div.p << PLL_BASE_DIVP_SHIFT; u32 misc_con = pll.div.cpcon << PLL_MISC_CPCON_SHIFT | pll.div.lfcon << PLL_MISC_LFCON_SHIFT; /* Write dividers but BYPASS the PLL while we're messing with it. */ CLOCK(pll_base_offset)=dividers | (1<>CLK_OSC_FREQ_SHIFT; /* Set PLLC dynramp_step A to 0x2b and B to 0xb (from U-Boot -- why? */ CLOCK(CLK_RST_CONTROLLER_PLLC_MISC2)=(0x2b << 17 | 0xb << 9); // static const osc_table[16] = { // [OSC_FREQ_12] = { // .khz = 12000, // .pllx = {.n = TEGRA_PLLX_KHZ / 12000, .m = 1, .p = 0}, // .pllc = {.n = 50, .m = 1, .p = 0}, // .pllu = {.n = 960, .m = 12, .p = 0, .cpcon = 12, .lfcon = 2}, // .plldp = {.n = 90, .m = 1, .p = 3}, // }, /* Max out the AVP clock before everything else (need PLLC for that). */ init_pll(CLK_RST_CONTROLLER_PLLC_BASE,CLK_RST_CONTROLLER_PLLC_MISC,osc_table[clk_osc_bit].pllc,(1 << CLK_RST_CONTROLLER_PLLC_MISC_PLLC_LOCK_ENABLE_SHIFT)); printf_("[x] PLLC setup\n"); /* Typical ratios are 1:2:2 or 1:2:3 sclk:hclk:pclk (See: APB DMA * features section in the TRM). */ CLOCK(CLK_RST_CONTROLLER_CLK_SYSTEM_RATE)= TEGRA_HCLK_RATIO << CLK_RST_CONTROLLER_CLK_SYSTEM_RATE_AHB_RATE_SHIFT | TEGRA_PCLK_RATIO << CLK_RST_CONTROLLER_CLK_SYSTEM_RATE_APB_RATE_SHIFT; CLOCK(CLK_RST_CONTROLLER_PLLC_OUT)=CLK_DIVIDER(TEGRA_PLLC_KHZ, TEGRA_SCLK_KHZ) << CLK_RST_CONTROLLER_PLLM_OUT_PLLM_OUT1_RATIO_SHIFT | (1 << CLK_RST_CONTROLLER_PLLM_OUT_PLLM_OUT1_CLKEN_SHIFT) | (1 << CLK_RST_CONTROLLER_PLLM_OUT_PLLM_OUT1_RSTN_SHIFT); /* sclk = 300 MHz */ CLOCK(CLK_RST_CONTROLLER_SCLK_BURST_POLICY)= CLK_RST_CONTROLLER_SCLK_BURST_POLICY_SYS_STATE_RUN << CLK_RST_CONTROLLER_SCLK_BURST_POLICY_SYS_STATE_SHIFT | CLK_RST_CONTROLLER_SCLK_BURST_POLICY_SWAKEUP_RUN_SOURCE_PLLC_OUT1 <> PMC_XOFS_SHIFT; /*Change the oscillator drive strength (from U-Boot -- why?) */ u32 osc_ctrl = CLOCK(CLK_RST_CONTROLLER_OSC_CTRL); osc_ctrl &= ~CLK_OSC_XOFS_MASK; osc_ctrl |= (xofs << CLK_OSC_XOFS_SHIFT); osc_ctrl |= CLK_OSC_XOE; CLOCK(CLK_RST_CONTROLLER_OSC_CTRL)=osc_ctrl; /* Disable IDDQ for PLLX before we set it up (from U-Boot -- why?) */ CLOCK(CLK_RST_CONTROLLER_PLLX_MISC_3) &= ~PLLX_MISC3_IDDQ; // Disable IDDQ. /* Set up PLLP_OUT(1|2|3|4) divisor to generate (9.6|48|102|204)MHz */ //pll2 ovr shift should be 18? CLOCK(CLK_RST_CONTROLLER_PLLP_OUTA)=((CLK_DIVIDER(TEGRA_PLLP_KHZ, 9600) << (1 << CLK_RST_CONTROLLER_PLLM_OUT_PLLM_OUT1_RATIO_SHIFT) | (1 << CLK_RST_CONTROLLER_PLLP_OUTA_PLLP_OUT1_OVRRIDE_SHIFT) | (1 << CLK_RST_CONTROLLER_PLLM_OUT_PLLM_OUT1_CLKEN_SHIFT) | (1 << CLK_RST_CONTROLLER_PLLM_OUT_PLLM_OUT1_RSTN_SHIFT)) << PLL_OUT1_SHIFT | (CLK_DIVIDER(TEGRA_PLLP_KHZ, 48000) << (1 << CLK_RST_CONTROLLER_PLLM_OUT_PLLM_OUT1_RATIO_SHIFT) | (1 << CLK_RST_CONTROLLER_PLLP_OUTA_PLLP_OUT1_OVRRIDE_SHIFT) | (1 << CLK_RST_CONTROLLER_PLLM_OUT_PLLM_OUT1_CLKEN_SHIFT) | (1 << CLK_RST_CONTROLLER_PLLM_OUT_PLLM_OUT1_RSTN_SHIFT)) << PLL_OUT2_SHIFT); CLOCK(CLK_RST_CONTROLLER_PLLP_OUTB)= ((CLK_DIVIDER(TEGRA_PLLP_KHZ, 102000) << (1 << CLK_RST_CONTROLLER_PLLM_OUT_PLLM_OUT1_RATIO_SHIFT) | (1 << CLK_RST_CONTROLLER_PLLP_OUTA_PLLP_OUT1_OVRRIDE_SHIFT) | (1 << CLK_RST_CONTROLLER_PLLM_OUT_PLLM_OUT1_CLKEN_SHIFT) | (1 << CLK_RST_CONTROLLER_PLLM_OUT_PLLM_OUT1_RSTN_SHIFT)) << PLL_OUT3_SHIFT | (CLK_DIVIDER(TEGRA_PLLP_KHZ, 204000) << (1 << CLK_RST_CONTROLLER_PLLM_OUT_PLLM_OUT1_RATIO_SHIFT) | (1 << CLK_RST_CONTROLLER_PLLP_OUTA_PLLP_OUT1_OVRRIDE_SHIFT) | (1 << CLK_RST_CONTROLLER_PLLM_OUT_PLLM_OUT1_CLKEN_SHIFT) | (1 << CLK_RST_CONTROLLER_PLLM_OUT_PLLM_OUT1_RSTN_SHIFT)) << PLL_OUT4_SHIFT); init_pll(CLK_RST_CONTROLLER_PLLX_BASE,CLK_RST_CONTROLLER_PLLX_MISC,osc_table[clk_osc_bit].pllx,(1 << CLK_RST_CONTROLLER_PLLX_MISC_PLLX_LOCK_ENABLE_SHIFT)); init_pll(CLK_RST_CONTROLLER_PLLU_BASE,CLK_RST_CONTROLLER_PLLU_MISC,osc_table[clk_osc_bit].pllu,(1 << CLK_RST_CONTROLLER_PLLU_MISC_PLLU_LOCK_ENABLE_SHIFT)); //TODO usb device init later /* Graphics just has to be different. There's a few more bits we * need to set in here, but it makes sense just to restrict all the * special bits to this one function. Imported form graphics_pll(void) */ /* the vendor code sets the dither bit (28) * an undocumented bit (24) * and clamp while we mess with it (22) * Dither is pretty important to display port * so we really do need to handle these bits. * I'm not willing to not clamp it, even if * it might "mostly work" with it not set, * I don't want to find out in a few months * that it is needed. */ u32 scfg = (1<<28) | (1<<24) | (1<<22); CLOCK(CLK_RST_CONTROLLER_PLLDP_SS_CFG)=scfg; init_pll(CLK_RST_CONTROLLER_PLLDP_BASE, CLK_RST_CONTROLLER_PLLDP_MISC, osc_table[clk_osc_bit].plldp, (1 << CLK_RST_CONTROLLER_PLLDP_MISC_0_PLLDP_LOCK_ENABLE_SHIFT)); /* leave dither and undoc bits set, release clamp */ scfg = (1<<28) | (1<<24); CLOCK(CLK_RST_CONTROLLER_PLLDP_SS_CFG)=scfg; /* disp1 will be set when panel information (pixel clock) is * retrieved (clock_display). */ printf_("[X] PLLP,PLLX,PLLU,PLLDP setup\n"); } static const clk_rst_t _clock_uart[] = { { CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_CONTROLLER_CLK_SOURCE_UARTA, CLK_L_UARTA, 0, 2 }, { CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_CONTROLLER_CLK_SOURCE_UARTB, CLK_L_UARTB, 0, 2 }, { CLK_RST_CONTROLLER_RST_DEVICES_H, CLK_RST_CONTROLLER_CLK_OUT_ENB_H, CLK_RST_CONTROLLER_CLK_SOURCE_UARTC, CLK_H_UARTC, 0, 2 }, { CLK_RST_CONTROLLER_RST_DEVICES_U, CLK_RST_CONTROLLER_CLK_OUT_ENB_U, CLK_RST_CONTROLLER_CLK_SOURCE_UARTD, CLK_U_UARTD, 0, 2 }, }; void clock_enable_uart(u32 idx) { clock_enable(&_clock_uart[idx]); } #define UART_SRC_CLK_DIV_EN BIT(24) int clock_uart_use_src_div(u32 idx, u32 baud) { u32 clk_src_div = CLOCK(_clock_uart[idx].source) & 0xE0000000; if (baud == 3000000) CLOCK(_clock_uart[idx].source) = clk_src_div | UART_SRC_CLK_DIV_EN | 15; else if (baud == 1000000) CLOCK(_clock_uart[idx].source) = clk_src_div | UART_SRC_CLK_DIV_EN | 49; else { CLOCK(_clock_uart[idx].source) = clk_src_div | 2; return 1; } return 0; }