From 3f31a634f69595bcc6a473301d1492c97a767809 Mon Sep 17 00:00:00 2001 From: Julius Werner Date: Fri, 25 Oct 2013 14:12:47 -0700 Subject: [PATCH] tegra124: Revamp clock source/divisor configuration The clock_ll_set_source_divisor() function has loads of issues, its ugly name only being the least of them. This patch replaces it with a nice little macro that allows you to very easily and cleanly specify the clock source and target frequency you want to use for a specific device, while automatically calculating the correct divisor bits at compile time. There's the small catch that Nvidia hardware designers in their great wisdom decided to just encode the same clock sources with different bits for a few devices. There's no easy way around this, so I suggest building this solution for the common case and writing the bits directly for those few outlier devices (right now we only need one of them). Also moves some clock source configuration code from display.c to clock.c so that we can lose the external dependency on clk_rst.h and hide all the ugly clock internals in one file (which I think is a cleaner architecture). This still won't prevent us from splitting the clock_config() function into individual smaller wrappers at a later point. BUG=None TEST=Booted, runs as good as before, dumped and compared all clock source registers to ensure they stayed the same. Change-Id: I2365c9167977eebe36fb1e8e48c7983cdd655f51 Signed-off-by: Julius Werner Reviewed-on: https://chromium-review.googlesource.com/174804 --- src/soc/nvidia/tegra124/clk_rst.h | 20 +---- src/soc/nvidia/tegra124/clock.c | 82 +++++++++------------ src/soc/nvidia/tegra124/display.c | 17 ----- src/soc/nvidia/tegra124/include/soc/clock.h | 14 +++- 4 files changed, 49 insertions(+), 84 deletions(-) diff --git a/src/soc/nvidia/tegra124/clk_rst.h b/src/soc/nvidia/tegra124/clk_rst.h index 4280f3ccd4..c9c595d9e8 100644 --- a/src/soc/nvidia/tegra124/clk_rst.h +++ b/src/soc/nvidia/tegra124/clk_rst.h @@ -425,24 +425,10 @@ enum { #define OSC_XOFS_MASK (0x3F << OSC_XOFS_SHIFT) #define OSC_DRIVE_STRENGTH 7 -/* - * CLK_RST_CONTROLLER_CLK_SOURCE_x_OUT_0 - the mask here is normally 8 bits - * but can be 16. We could use knowledge we have to restrict the mask in - * the 8-bit cases (the divider_bits value returned by - * get_periph_clock_source()) but it does not seem worth it since the code - * already checks the ranges of values it is writing, in clk_get_divider(). - */ -#define CLK_DIVISOR_SHIFT 0 -#define CLK_DIVISOR_MASK (0xffff << CLK_DIVISOR_SHIFT) +#define CLK_DIVISOR_MASK (0xffff) -#define CLK_SOURCE_SHIFT 30 -#define CLK_SOURCE_MASK (3U << CLK_SOURCE_SHIFT) - -#define CLK_SOURCE3_SHIFT 29 -#define CLK_SOURCE3_MASK (7U << CLK_SOURCE3_SHIFT) - -#define CLK_SOURCE4_SHIFT 28 -#define CLK_SOURCE4_MASK (15U << CLK_SOURCE4_SHIFT) +#define CLK_SOURCE_SHIFT 29 +#define CLK_SOURCE_MASK (0x7 << CLK_SOURCE_SHIFT) #define CLK_UART_DIV_OVERRIDE (1 << 24) diff --git a/src/soc/nvidia/tegra124/clock.c b/src/soc/nvidia/tegra124/clock.c index 7be06fb30c..22c0f3897c 100644 --- a/src/soc/nvidia/tegra124/clock.c +++ b/src/soc/nvidia/tegra124/clock.c @@ -23,6 +23,13 @@ #include "flow.h" #include "pmc.h" +/* Warning: Some devices just use different bits for the same sources for no + * apparent reason. *Always* double-check the TRM before trusting this macro. */ +#define clock_configure_source(device, src, freq) \ + clrsetbits_le32(&clk_rst->clk_src_##device, \ + CLK_SOURCE_MASK | CLK_DIVISOR_MASK, \ + src << CLK_SOURCE_SHIFT | CLK_DIVIDER(TEGRA_##src##_KHZ, freq)); + 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; @@ -143,23 +150,6 @@ struct { }, }; -// TODO(hungte) Some clock source are assigned in 3 or 4 bits -// (OUT_CLK_SOURCE*_MASK/SHIFT), not always OUT_CLK_SOURCE_MASK/SHIFT. -void clock_ll_set_source_divisor(u32 *reg, u32 source, u32 divisor) -{ - u32 value; - - value = readl(reg); - - value &= ~CLK_SOURCE_MASK; - value |= source << CLK_SOURCE_SHIFT; - - value &= ~CLK_DIVISOR_MASK; - value |= divisor << CLK_DIVISOR_SHIFT; - - writel(value, reg); -} - /* Get the oscillator frequency, from the corresponding hardware * configuration field. This is actually a per-soc thing. Avoid the * temptation to make it common. @@ -214,8 +204,8 @@ static void init_pll(u32 *base, u32 *misc, const union pll_fields pll) * been determined through trial and error (must lead to div 13 at 24MHz). */ void clock_early_uart(void) { - clock_ll_set_source_divisor(&clk_rst->clk_src_uarta, 3, - CLK_UART_DIV_OVERRIDE | CLK_DIVIDER(clock_get_osc_khz(), 1800)); + write32(CLK_M << CLK_SOURCE_SHIFT | CLK_UART_DIV_OVERRIDE | + CLK_DIVIDER(TEGRA_CLK_M_KHZ, 1800), &clk_rst->clk_src_uarta); setbits_le32(&clk_rst->clk_out_enb_l, CLK_L_UARTA); udelay(2); clrbits_le32(&clk_rst->rst_dev_l, CLK_L_UARTA); @@ -332,7 +322,7 @@ void clock_config(void) /* 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); + CLK_L_SDMMC4 | CLK_L_DISP1 | CLK_L_HOST1X); setbits_le32(&clk_rst->clk_out_enb_h, CLK_H_EMC | CLK_H_I2C2 | CLK_H_I2C5 | CLK_H_SBC1 | CLK_H_PMC | CLK_H_APBDMA | CLK_H_MEM); @@ -341,43 +331,37 @@ void clock_config(void) setbits_le32(&clk_rst->clk_out_enb_v, CLK_V_MSELECT | CLK_V_I2C4); setbits_le32(&clk_rst->clk_out_enb_w, CLK_W_DVFS); - /* - * Set MSELECT clock source as PLLP (00)_REG, and ask for a clock - * divider that would set the MSELECT clock at 102MHz for a - * PLLP base of 408MHz. - */ - clock_ll_set_source_divisor(&clk_rst->clk_src_mselect, 0, - CLK_DIVIDER(TEGRA_PLLP_KHZ, 102000)); - /* Give clock time to stabilize */ udelay(IO_STABILIZATION_DELAY); - /* I2C1 gets CLK_M and a divisor of 17 */ - clock_ll_set_source_divisor(&clk_rst->clk_src_i2c1, 3, 16); - /* I2C2 gets CLK_M and a divisor of 17 */ - clock_ll_set_source_divisor(&clk_rst->clk_src_i2c2, 3, 16); - /* I2C3 (cam) gets CLK_M and a divisor of 17 */ - clock_ll_set_source_divisor(&clk_rst->clk_src_i2c3, 3, 16); - /* I2C4 (ddc) gets CLK_M and a divisor of 17 */ - clock_ll_set_source_divisor(&clk_rst->clk_src_i2c4, 3, 16); - /* I2C5 (PMU) gets CLK_M and a divisor of 17 */ - clock_ll_set_source_divisor(&clk_rst->clk_src_i2c5, 3, 16); + clock_configure_source(mselect, PLLP, 102000); - /* UARTA gets PLLP, deactivate CLK_UART_DIV_OVERRIDE */ - writel(0 << CLK_SOURCE_SHIFT, &clk_rst->clk_src_uarta); + /* TODO: is the 1.333MHz correct? This may have always been bogus... */ + clock_configure_source(i2c1, CLK_M, 1333); + clock_configure_source(i2c2, CLK_M, 1333); + clock_configure_source(i2c3, CLK_M, 1333); + clock_configure_source(i2c4, CLK_M, 1333); + clock_configure_source(i2c5, CLK_M, 1333); + + /* Move UARTA to PLLP and remove the CLK_UART_DIV_OVERRIDE */ + write32(PLLP << CLK_SOURCE_SHIFT, &clk_rst->clk_src_uarta); /* MMC3 and MMC4: Set base clock frequency for SD Clock to Tegra MMC's * maximum speed (48MHz) so we can change SDCLK by second stage divisor * in payloads, without touching base clock. - * - * Note, parent clock source for MMC3/4 is PLLP_OUT0. MMC clock source - * should be specified in 3 bits, but since PLLP_OUT0 is #0, it's OK to - * simply call clock_ll_set_source_divisor (2 bits). */ - clock_ll_set_source_divisor(&clk_rst->clk_src_sdmmc3, 0, - CLK_DIVIDER(TEGRA_PLLP_KHZ, 48000)); - clock_ll_set_source_divisor(&clk_rst->clk_src_sdmmc4, 0, - CLK_DIVIDER(TEGRA_PLLP_KHZ, 48000)); + clock_configure_source(sdmmc3, PLLP, 48000); + clock_configure_source(sdmmc4, PLLP, 48000); + + /* PLLP and PLLM are switched for HOST1x for no apparent reason. */ + write32(4 /* PLLP! */ << CLK_SOURCE_SHIFT | + /* TODO(rminnich): The divisor isn't accurate enough to get to + * 144MHz (it goes to 163 instead). What should we do here? */ + CLK_DIVIDER(TEGRA_PLLP_KHZ, 144000), + &clk_rst->clk_src_host1x); + + /* DISP1 doesn't support a divisor. Use PLLC which runs at 600MHz. */ + clock_configure_source(disp1, PLLC, 600000); /* Give clock time to stabilize. */ udelay(IO_STABILIZATION_DELAY); @@ -386,7 +370,7 @@ void clock_config(void) clrbits_le32(&clk_rst->rst_dev_l, CLK_L_CACHE2 | CLK_L_GPIO | CLK_L_TMR | CLK_L_I2C1 | - CLK_L_SDMMC4); + CLK_L_SDMMC4 | CLK_L_DISP1 | CLK_L_HOST1X); clrbits_le32(&clk_rst->rst_dev_h, CLK_H_EMC | CLK_H_I2C2 | CLK_H_I2C5 | CLK_H_SBC1 | CLK_H_PMC | CLK_H_APBDMA | CLK_H_MEM); diff --git a/src/soc/nvidia/tegra124/display.c b/src/soc/nvidia/tegra124/display.c index b7993008d5..2eb02965b8 100644 --- a/src/soc/nvidia/tegra124/display.c +++ b/src/soc/nvidia/tegra124/display.c @@ -32,12 +32,9 @@ #include #include #include -#include "clk_rst.h" #include "chip.h" #include -static struct clk_rst_ctlr *clk_rst = (void *)TEGRA_CLK_RST_BASE; - static const u32 rgb_enb_tab[PIN_REG_COUNT] = { 0x00000000, 0x00000000, @@ -281,20 +278,6 @@ void display_startup(device_t dev) * The panel_vdd is done in the romstage, so we need only * light things up here once we're sure it's all working. */ - 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(TEGRA_PLLP_KHZ, 144000)); - - /* DISP1 doesn't support a divisor. Use PLLC which runs at 600MHz. */ - val = readl(&clk_rst->clk_src_disp1); - val &= ~CLK_SOURCE3_MASK; - val |= (4 << CLK_SOURCE3_SHIFT); - writel(val, &clk_rst->clk_src_disp1); - - udelay(2); - - clrbits_le32(&clk_rst->rst_dev_l, CLK_L_DISP1|CLK_L_HOST1X); writel(0x00000100, &dc->cmd.gen_incr_syncpt_ctrl); writel(0x0000011a, &dc->cmd.cont_syncpt_vsync); diff --git a/src/soc/nvidia/tegra124/include/soc/clock.h b/src/soc/nvidia/tegra124/include/soc/clock.h index 056a38b2e0..fe4f391724 100644 --- a/src/soc/nvidia/tegra124/include/soc/clock.h +++ b/src/soc/nvidia/tegra124/include/soc/clock.h @@ -155,7 +155,20 @@ enum { /* Calculate clock frequency value from reference and clock divider value */ #define CLK_FREQUENCY(REF, REG) (((REF) * 2) / (REG + 2)) +enum clock_source { /* Careful: Not true for all sources, always check TRM! */ + PLLP = 0, + PLLC2 = 1, + PLLC = 2, + PLLD = 2, + PLLC3 = 3, + PLLA = 3, + PLLM = 4, + PLLD2 = 5, + CLK_M = 6, +}; + /* soc-specific */ +#define TEGRA_CLK_M_KHZ clock_get_osc_khz() #define TEGRA_PLLX_KHZ (1900000) #define TEGRA_PLLP_KHZ (408000) #define TEGRA_PLLC_KHZ (600000) @@ -167,5 +180,4 @@ void clock_early_uart(void); void clock_cpu0_config_and_reset(void * entry); void clock_config(void); void clock_init(void); -void clock_ll_set_source_divisor(u32 *reg, u32 source, u32 divisor); #endif /* __SOC_NVIDIA_TEGRA124_CLOCK_H__ */