diff --git a/src/mainboard/google/nyan/bootblock.c b/src/mainboard/google/nyan/bootblock.c index 4215a78371..49133ca55c 100644 --- a/src/mainboard/google/nyan/bootblock.c +++ b/src/mainboard/google/nyan/bootblock.c @@ -20,8 +20,8 @@ #include #include #include +#include #include -#include #include #include /* FIXME: move back to soc code? */ diff --git a/src/soc/nvidia/tegra124/bootblock.c b/src/soc/nvidia/tegra124/bootblock.c index a264a348d3..2698611fba 100644 --- a/src/soc/nvidia/tegra124/bootblock.c +++ b/src/soc/nvidia/tegra124/bootblock.c @@ -21,8 +21,8 @@ #include #include #include +#include -#include "clock.h" #include "pinmux.h" #include "power.h" diff --git a/src/soc/nvidia/tegra124/clk_rst.h b/src/soc/nvidia/tegra124/clk_rst.h index 7b9c3e8bb6..4280f3ccd4 100644 --- a/src/soc/nvidia/tegra124/clk_rst.h +++ b/src/soc/nvidia/tegra124/clk_rst.h @@ -326,31 +326,32 @@ struct __attribute__ ((__packed__)) clk_rst_ctlr { #define OSC_FREQ_OSC38P4 5 /* 38.4MHz */ #define OSC_FREQ_OSC48 9 /* 48.0MHz */ -/* CLK_RST_CONTROLLER_PLLx_BASE_0 */ -#define PLL_BYPASS_SHIFT 31 -#define PLL_BYPASS_MASK (1U << PLL_BYPASS_SHIFT) +/* CLK_RST_CONTROLLER_PLL*_BASE_0 */ +#define PLL_BASE_BYPASS (1U << 31) +#define PLL_BASE_ENABLE (1U << 30) +#define PLL_BASE_REF_DIS (1U << 29) +#define PLL_BASE_OVRRIDE (1U << 28) +#define PLL_BASE_LOCK (1U << 27) -#define PLL_ENABLE_SHIFT 30 -#define PLL_ENABLE_MASK (1U << PLL_ENABLE_SHIFT) +#define PLL_BASE_DIVP_SHIFT 20 +#define PLL_BASE_DIVP_MASK (7U << PLL_BASE_DIVP_SHIFT) -#define PLL_BASE_OVRRIDE_MASK (1U << 28) -#define PLL_BASE_LOCK_MASK (1U << 27) +#define PLL_BASE_DIVN_SHIFT 8 +#define PLL_BASE_DIVN_MASK (0x3ffU << PLL_BASE_DIVN_SHIFT) -#define PLL_DIVP_SHIFT 20 -#define PLL_DIVP_MASK (7U << PLL_DIVP_SHIFT) - -#define PLL_DIVN_SHIFT 8 -#define PLL_DIVN_MASK (0x3ffU << PLL_DIVN_SHIFT) - -#define PLL_DIVM_SHIFT 0 -#define PLL_DIVM_MASK (0x1f << PLL_DIVM_SHIFT) +#define PLL_BASE_DIVM_SHIFT 0 +#define PLL_BASE_DIVM_MASK (0x1f << PLL_BASE_DIVM_SHIFT) /* SPECIAL CASE: PLLM, PLLC and PLLX use different-sized fields here */ -#define PLLCMX_DIVP_MASK (0xfU << PLL_DIVP_SHIFT) -#define PLLCMX_DIVN_MASK (0xffU << PLL_DIVN_SHIFT) -#define PLLCMX_DIVM_MASK (0xffU << PLL_DIVM_SHIFT) +#define PLLCMX_BASE_DIVP_MASK (0xfU << PLL_BASE_DIVP_SHIFT) +#define PLLCMX_BASE_DIVN_MASK (0xffU << PLL_BASE_DIVN_SHIFT) +#define PLLCMX_BASE_DIVM_MASK (0xffU << PLL_BASE_DIVM_SHIFT) -/* CLK_RST_CONTROLLER_PLLx_OUTx_0 */ +/* Generic, indiscriminate divisor mask. May catch some innocent bystander bits + * on the side that we don't particularly care about. */ +#define PLL_BASE_DIV_MASK (0xffffff) + +/* CLK_RST_CONTROLLER_PLL*_OUT*_0 */ #define PLL_OUT_RSTN (1 << 0) #define PLL_OUT_CLKEN (1 << 1) #define PLL_OUT_OVRRIDE (1 << 2) @@ -365,21 +366,22 @@ struct __attribute__ ((__packed__)) clk_rst_ctlr { #define PLL_OUT2_RATIO_SHIFT 24 #define PLL_OUT2_RATIO_MASK (0xffU << PLL_OUT2_RATIO_SHIFT) -/* CLK_RST_CONTROLLER_PLLx_MISC_0 */ -#define PLL_DCCON_SHIFT 20 -#define PLL_DCCON_MASK (1U << PLL_DCCON_SHIFT) +/* CLK_RST_CONTROLLER_PLL*_MISC_0 */ +#define PLL_MISC_DCCON (1 << 20) -#define PLL_LOCK_ENABLE_SHIFT 18 -#define PLL_LOCK_ENABLE_MASK (1U << PLL_LOCK_ENABLE_SHIFT) +#define PLL_MISC_CPCON_SHIFT 8 +#define PLL_MISC_CPCON_MASK (0xfU << PLL_MISC_CPCON_SHIFT) -#define PLL_CPCON_SHIFT 8 -#define PLL_CPCON_MASK (15U << PLL_CPCON_SHIFT) +#define PLL_MISC_LFCON_SHIFT 4 +#define PLL_MISC_LFCON_MASK (0xfU << PLL_MISC_LFCON_SHIFT) -#define PLL_LFCON_SHIFT 4 -#define PLL_LFCON_MASK (15U << PLL_LFCON_SHIFT) +/* This bit is different all over the place. Oh joy... */ +#define PLLC_MISC_LOCK_ENABLE (1 << 24) +#define PLLUD_MISC_LOCK_ENABLE (1 << 22) +#define PLLPAXS_MISC_LOCK_ENABLE (1 << 18) +#define PLLE_MISC_LOCK_ENABLE (1 << 9) -#define PLLU_VCO_FREQ_SHIFT 20 -#define PLLU_VCO_FREQ_MASK (1U << PLLU_VCO_FREQ_SHIFT) +#define PLLU_MISC_VCO_FREQ (1 << 20) #define PLLP_OUT1_OVR (1 << 2) #define PLLP_OUT2_OVR (1 << 18) @@ -406,19 +408,6 @@ enum { IN_408_OUT_9_6_DIVISOR = 83, }; -/* CRC_PLLP_MISC_0 0xac */ -#define PLLP_MISC_PLLP_CPCON_8 (8 << 8) -#define PLLP_MISC_PLLP_LOCK_ENABLE (1 << 18) - -/* CRC_PLLU_BASE_0 0xc0 */ -#define PLLU_BYPASS_ENABLE (1 << 31) -#define PLLU_ENABLE_ENABLE (1 << 30) -#define PLLU_REF_DIS_REF_DISABLE (1 << 29) -#define PLLU_OVERRIDE_ENABLE (1 << 24) - -/* CRC_PLLU_MISC_0 0xcc */ -#define PLLU_LOCK_ENABLE_ENABLE (1 << 22) - /* PLLX_BASE_0 0xe0 */ #define PLLX_BASE_PLLX_ENABLE (1 << 30) diff --git a/src/soc/nvidia/tegra124/clock.c b/src/soc/nvidia/tegra124/clock.c index 16ce445bdb..9dce86754e 100644 --- a/src/soc/nvidia/tegra124/clock.c +++ b/src/soc/nvidia/tegra124/clock.c @@ -17,8 +17,8 @@ #include #include #include +#include #include "clk_rst.h" -#include "clock.h" #include "cpug.h" #include "flow.h" #include "pmc.h" @@ -27,31 +27,120 @@ static struct clk_rst_ctlr *clk_rst = (void *)TEGRA_CLK_RST_BASE; static struct flow_ctlr *flow = (void *)TEGRA_FLOW_BASE; static struct tegra_pmc_regs *pmc = (void*)TEGRA_PMC_BASE; -/* only needed in this one place. Avoid namespace pollution. Be green .*/ -struct clk_pll_table { - u16 n; - u16 m; - u8 p; +struct pll_dividers { + u32 n : 10; + u32 m : 8; + u32 p : 4; + u32 cpcon: 4; + u32 : 6; }; -/* - * Timing tables - */ -struct clk_pll_table tegra_pll_x_table[16] = { - /* T124: 1.9 GHz */ - /* - * Field Bits Width - * n 15:8 8 - * m 7:0 8 - * p 23:20 4 - */ - [OSC_FREQ_OSC13]{146,1,0}, - [OSC_FREQ_OSC19P2]{98,1,0}, - [OSC_FREQ_OSC12]{157,1,0}, - [OSC_FREQ_OSC26]{73,1,0}, - [OSC_FREQ_OSC16P8]{113,1,0}, - [OSC_FREQ_OSC38P4]{98,2,0}, - [OSC_FREQ_OSC48]{157,4,0}, +/* Some PLLs have more restrictive divider bit lengths or are missing some + * fields. Make sure to use the right struct in the osc_table definition to get + * compile-time checking, but keep the bits aligned with struct pll_dividers so + * they can be used interchangeably at run time. Add new formats as required. */ +struct pllcx_dividers { + u32 n : 8; + u32 : 2; + u32 m : 8; + u32 p : 4; + u32 : 10; +}; +struct pllpad_dividers { + u32 n : 10; + u32 m : 5; + u32 : 3; + u32 p : 3; + u32 : 1; + u32 cpcon : 4; + u32 : 6; +}; +struct pllu_dividers { + u32 n : 10; + u32 m : 5; + u32 : 3; + u32 p : 1; + u32 : 3; + u32 cpcon : 4; + u32 : 6; +}; + +union __attribute__((transparent_union)) pll_fields { + u32 raw; + struct pll_dividers div; + struct pllcx_dividers cx; + struct pllpad_dividers pad; + struct pllu_dividers u; +}; + +/* 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 OUT = (IN / m) * N / (2^P). */ +struct { + int khz; + struct pllcx_dividers pllx; /* target: 1900 MHz */ + struct pllpad_dividers pllp; /* target: 408 MHz */ + struct pllcx_dividers pllc; /* target: 600 MHz */ + struct pllpad_dividers plld; /* target: 925 MHz */ + struct pllu_dividers pllu; /* target; 960 MHz */ +} static const osc_table[16] = { + [OSC_FREQ_OSC12]{ + .khz = 12000, + .pllx = {.n = 158, .m = 1, .p = 0}, /* 1896 MHz */ + .pllp = {.n = 34, .m = 1, .p = 0, .cpcon = 2}, + .pllc = {.n = 50, .m = 1, .p = 0}, + .plld = {.n = 925, .m = 12, .p = 0, .cpcon = 12}, + .pllu = {.n = 80, .m = 1, .p = 0, .cpcon = 3}, + }, + [OSC_FREQ_OSC13]{ + .khz = 13000, + .pllx = {.n = 146, .m = 1, .p = 0}, /* 1898 MHz */ + .pllp = {.n = 408, .m = 13, .p = 0, .cpcon = 8}, + .pllc = {.n = 231, .m = 5, .p = 0}, /* 600.6 MHz */ + .plld = {.n = 925, .m = 13, .p = 0, .cpcon = 12}, + .pllu = {.n = 960, .m = 13, .p = 0, .cpcon = 12}, + }, + [OSC_FREQ_OSC16P8]{ + .khz = 16800, + .pllx = {.n = 113, .m = 1, .p = 0}, /* 1898.4 MHz */ + .pllp = {.n = 170, .m = 7, .p = 0, .cpcon = 4}, + .pllc = {.n = 250, .m = 7, .p = 0}, + .plld = {.n = 936, .m = 17, .p = 0, .cpcon = 12},/* 924.9 MHz */ + .pllu = {.n = 400, .m = 7, .p = 0, .cpcon = 8}, + }, + [OSC_FREQ_OSC19P2]{ + .khz = 19200, + .pllx = {.n = 98, .m = 1, .p = 0}, /* 1881.6 MHz */ + .pllp = {.n = 85, .m = 4, .p = 0, .cpcon = 3}, + .pllc = {.n = 125, .m = 4, .p = 0}, + .plld = {.n = 819, .m = 17, .p = 0, .cpcon = 12},/* 924.9 MHz */ + .pllu = {.n = 50, .m = 1, .p = 0, .cpcon = 2}, + }, + [OSC_FREQ_OSC26]{ + .khz = 26000, + .pllx = {.n = 73, .m = 1, .p = 0}, /* 1898 MHz */ + .pllp = {.n = 204, .m = 13, .p = 0, .cpcon = 5}, + .pllc = {.n = 23, .m = 1, .p = 0}, /* 598 MHz */ + .plld = {.n = 925, .m = 26, .p = 0, .cpcon = 12}, + .pllu = {.n = 480, .m = 13, .p = 0, .cpcon = 8}, + }, + [OSC_FREQ_OSC38P4]{ + .khz = 38400, + .pllx = {.n = 98, .m = 1, .p = 0}, /* 1881.6 MHz */ + .pllp = {.n = 85, .m = 4, .p = 0, .cpcon = 3}, + .pllc = {.n = 125, .m = 4, .p = 0}, + .plld = {.n = 819, .m = 17, .p = 0, .cpcon = 12},/* 924.9 MHz */ + .pllu = {.n = 50, .m = 1, .p = 0, .cpcon = 2}, + }, + [OSC_FREQ_OSC48]{ + .khz = 48000, + .pllx = {.n = 158, .m = 1, .p = 0}, /* 1896 MHz */ + .pllp = {.n = 24, .m = 1, .p = 0, .cpcon = 2}, + .pllc = {.n = 50, .m = 1, .p = 0}, + .plld = {.n = 925, .m = 12, .p = 0, .cpcon = 12}, + .pllu = {.n = 80, .m = 1, .p = 0, .cpcon = 3}, + }, }; void clock_ll_set_source_divisor(u32 *reg, u32 source, u32 divisor) @@ -73,34 +162,14 @@ void clock_ll_set_source_divisor(u32 *reg, u32 source, u32 divisor) * configuration field. This is actually a per-soc thing. Avoid the * temptation to make it common. */ -static int clock_get_osc_freq(void) +static u32 clock_get_osc_bits(void) { - u32 reg; - reg = readl(&clk_rst->osc_ctrl); - reg >>= OSC_CTRL_OSC_FREQ_SHIFT; - return reg; + return readl(&clk_rst->osc_ctrl) >> OSC_CTRL_OSC_FREQ_SHIFT; } int clock_get_osc_khz(void) { - /* Implemented better in the next patch, sorry, hard to split this up */ - switch (clock_get_osc_freq()) { - case OSC_FREQ_OSC13: - return 13000; - case OSC_FREQ_OSC19P2: - return 19200; - default: - case OSC_FREQ_OSC12: - return 12000; - case OSC_FREQ_OSC26: - return 26000; - case OSC_FREQ_OSC16P8: - return 16800; - case OSC_FREQ_OSC38P4: - return 38400; - case OSC_FREQ_OSC48: - return 48000; - } + return osc_table[clock_get_osc_bits()].khz; } static void adjust_pllp_out_freqs(void) @@ -118,54 +187,21 @@ static void adjust_pllp_out_freqs(void) writel(reg, &clk_rst->pllp_outb); } -static int pllx_set_rate(u32 divn, u32 divm, u32 divp) +static void init_pll(u32 *base, u32 *misc, const union pll_fields pll) { - u32 reg; + u32 dividers = pll.div.n << PLL_BASE_DIVN_SHIFT | + pll.div.m << PLL_BASE_DIVM_SHIFT | + pll.div.p << PLL_BASE_DIVP_SHIFT; - /* If PLLX is already enabled, just return */ - if (readl(&clk_rst->pllx_base) & PLL_ENABLE_MASK) - return 0; + /* Write dividers but BYPASS the PLL while we're messing with it. */ + writel(dividers | PLL_BASE_BYPASS, base); - /* Disable IDDQ */ - reg = readl(&clk_rst->pllx_misc3); - reg &= ~PLLX_IDDQ_MASK; - writel(reg, &clk_rst->pllx_misc3); - udelay(2); + /* Set CPCON field (defaults to 0 if it doesn't exist for this PLL) */ + writel(pll.div.cpcon << PLL_MISC_CPCON_SHIFT, misc); - /* Set BYPASS, m, n and p to PLLX_BASE */ - reg = PLL_BYPASS_MASK | (divm << PLL_DIVM_SHIFT); - reg |= ((divn << PLL_DIVN_SHIFT) | (divp << PLL_DIVP_SHIFT)); - writel(reg, &clk_rst->pllx_base); - - /* Disable BYPASS */ - reg = readl(&clk_rst->pllx_base); - reg &= ~PLL_BYPASS_MASK; - writel(reg, &clk_rst->pllx_base); - - /* Set lock_enable to PLLX_MISC */ - reg = readl(&clk_rst->pllx_misc); - reg |= PLL_LOCK_ENABLE_MASK; - writel(reg, &clk_rst->pllx_misc); - - /* Enable PLLX last, as per JZ */ - reg = readl(&clk_rst->pllx_base); - reg |= PLL_ENABLE_MASK; - writel(reg, &clk_rst->pllx_base); - - return 0; -} - -static void init_pllx(void) -{ - int osc = clock_get_osc_freq(); - struct clk_pll_table *sel = &tegra_pll_x_table[osc]; - - if (sel->n == 0) - return; - - pllx_set_rate(sel->n, sel->m, sel->p); - - adjust_pllp_out_freqs(); + /* enable PLL and take it back out of BYPASS (we don't wait for lock + * because we assume that to be done by the time we start using it). */ + writel(dividers | PLL_BASE_ENABLE, base); } /* Initialize the UART and put it on CLK_M so we can use it during clock_init(). @@ -191,10 +227,6 @@ void clock_cpu0_config_and_reset(void *entry) write32((uintptr_t)entry, &cpug_entry_point); write32((uintptr_t)&cpug_setup, evp_cpu_reset); - // Wait for PLLX to lock. - while (!(readl(&clk_rst->pllx_base) & (0x1 << 27))) - ; - // Set up cclk_brst and divider. write32((CRC_CCLK_BRST_POL_PLLX_OUT0 << 0) | (CRC_CCLK_BRST_POL_PLLX_OUT0 << 4) | @@ -238,6 +270,7 @@ void clock_cpu0_config_and_reset(void *entry) void clock_init(void) { u32 val; + u32 osc = clock_get_osc_bits(); /* * On poweron, AVP clock source (also called system clock) is set to @@ -270,7 +303,22 @@ void clock_init(void) val |= (OSC_DRIVE_STRENGTH << PMC_OSC_EDPD_OVER_XOFS_SHIFT); writel(val, &pmc->osc_edpd_over); - init_pllx(); + /* Disable IDDQ for PLLX before we set it up (from U-Boot -- why?) */ + val = readl(&clk_rst->pllx_misc3); + val &= ~PLLX_IDDQ_MASK; + writel(val, &clk_rst->pllx_misc3); + udelay(2); + + /* Set PLLC dynramp_step A to 0x2b and B to 0xb (from U-Boot -- why? */ + writel(0x2b << 17 | 0xb << 9, &clk_rst->pllc_misc2); + + adjust_pllp_out_freqs(); + + init_pll(&clk_rst->pllx_base, &clk_rst->pllx_misc, osc_table[osc].pllx); + init_pll(&clk_rst->pllp_base, &clk_rst->pllp_misc, osc_table[osc].pllp); + init_pll(&clk_rst->pllc_base, &clk_rst->pllc_misc, osc_table[osc].pllc); + init_pll(&clk_rst->plld_base, &clk_rst->plld_misc, osc_table[osc].plld); + init_pll(&clk_rst->pllu_base, &clk_rst->pllu_misc, osc_table[osc].pllu); val = (1 << CLK_SYS_RATE_AHB_RATE_SHIFT); writel(val, &clk_rst->clk_sys_rate); @@ -279,6 +327,7 @@ void clock_init(void) void clock_config(void) { /* Enable clocks for the required peripherals. */ + /* TODO: can (should?) we use the _SET and _CLR registers here? */ setbits_le32(&clk_rst->clk_out_enb_l, CLK_L_CACHE2 | CLK_L_GPIO | CLK_L_TMR | CLK_L_I2C1 | CLK_L_SDMMC4); @@ -296,7 +345,7 @@ void clock_config(void) * PLLP base of 408MHz. */ clock_ll_set_source_divisor(&clk_rst->clk_src_mselect, 0, - CLK_DIVIDER(NVBL_PLLP_KHZ, 102000)); + CLK_DIVIDER(TEGRA_PLLP_KHZ, 102000)); /* Give clock time to stabilize */ udelay(IO_STABILIZATION_DELAY); diff --git a/src/soc/nvidia/tegra124/display.c b/src/soc/nvidia/tegra124/display.c index f744980a6d..b7d3d7f736 100644 --- a/src/soc/nvidia/tegra124/display.c +++ b/src/soc/nvidia/tegra124/display.c @@ -30,9 +30,9 @@ #include #include #include +#include #include #include "clk_rst.h" -#include "clock.h" #include "chip.h" #include @@ -255,12 +255,12 @@ void display_startup(device_t dev) setbits_le32(&clk_rst->rst_dev_l, CLK_L_DISP1 | CLK_L_HOST1X); clock_ll_set_source_divisor(&clk_rst->clk_src_host1x, 4, - CLK_DIVIDER(NVBL_PLLP_KHZ, 144000)); + CLK_DIVIDER(TEGRA_PLLP_KHZ, 144000)); /* u-boot uses PLLC for DISP1. * But the u-boot code does not work and we don't set up PLLC anyway. * PLLP seems quite good enough, so run with that for now. */ clock_ll_set_source_divisor(&clk_rst->clk_src_disp1, 0 /* 4 */, - CLK_DIVIDER(NVBL_PLLP_KHZ, 600000)); + CLK_DIVIDER(TEGRA_PLLP_KHZ, 600000)); udelay(2); diff --git a/src/soc/nvidia/tegra124/clock.h b/src/soc/nvidia/tegra124/include/soc/clock.h similarity index 96% rename from src/soc/nvidia/tegra124/clock.h rename to src/soc/nvidia/tegra124/include/soc/clock.h index d5268fcd44..056a38b2e0 100644 --- a/src/soc/nvidia/tegra124/clock.h +++ b/src/soc/nvidia/tegra124/include/soc/clock.h @@ -156,9 +156,11 @@ enum { #define CLK_FREQUENCY(REF, REG) (((REF) * 2) / (REG + 2)) /* soc-specific */ -#define NVBL_PLLP_KHZ (408000) -#define NVBL_PLLC_KHZ (600000) -#define NVBL_PLLD_KHZ (925000) +#define TEGRA_PLLX_KHZ (1900000) +#define TEGRA_PLLP_KHZ (408000) +#define TEGRA_PLLC_KHZ (600000) +#define TEGRA_PLLD_KHZ (925000) +#define TEGRA_PLLU_KHZ (960000) int clock_get_osc_khz(void); void clock_early_uart(void);