X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=arch%2Farm%2Fmach-tegra%2Fclock.c;h=e539ad8b30a7bbe532e267cd2eb0679252e04a0c;hb=07798764c26177e4ff40f34f06f6a3741d51b240;hp=c50d56dc888b5f612604832b22045b317cfdc93c;hpb=5b8031ccb4ed6e84457d883198d77efc307085dc;p=oweals%2Fu-boot.git diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c index c50d56dc88..e539ad8b30 100644 --- a/arch/arm/mach-tegra/clock.c +++ b/arch/arm/mach-tegra/clock.c @@ -1,12 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2010-2015, NVIDIA CORPORATION. All rights reserved. - * - * SPDX-License-Identifier: GPL-2.0 + * Copyright (c) 2010-2019, NVIDIA CORPORATION. All rights reserved. */ /* Tegra SoC common clock control functions */ #include +#include +#include #include #include #include @@ -15,8 +16,6 @@ #include #include #include -#include -#include /* * This is our record of the current clock rate of each clock. We don't @@ -206,6 +205,29 @@ int clock_ll_set_source_bits(enum periph_id periph_id, int mux_bits, return 0; } +static int clock_ll_get_source_bits(enum periph_id periph_id, int mux_bits) +{ + u32 *reg = get_periph_source_reg(periph_id); + u32 val = readl(reg); + + switch (mux_bits) { + case MASK_BITS_31_30: + val >>= OUT_CLK_SOURCE_31_30_SHIFT; + val &= OUT_CLK_SOURCE_31_30_MASK; + return val; + case MASK_BITS_31_29: + val >>= OUT_CLK_SOURCE_31_29_SHIFT; + val &= OUT_CLK_SOURCE_31_29_MASK; + return val; + case MASK_BITS_31_28: + val >>= OUT_CLK_SOURCE_31_28_SHIFT; + val &= OUT_CLK_SOURCE_31_28_MASK; + return val; + default: + return -1; + } +} + void clock_ll_set_source(enum periph_id periph_id, unsigned source) { clock_ll_set_source_bits(periph_id, MASK_BITS_31_30, source); @@ -288,9 +310,46 @@ unsigned long clock_get_periph_rate(enum periph_id periph_id, enum clock_id parent) { u32 *reg = get_periph_source_reg(periph_id); + unsigned parent_rate = pll_rate[parent]; + int div = (readl(reg) & OUT_CLK_DIVISOR_MASK) >> OUT_CLK_DIVISOR_SHIFT; + + switch (periph_id) { + case PERIPH_ID_UART1: + case PERIPH_ID_UART2: + case PERIPH_ID_UART3: + case PERIPH_ID_UART4: + case PERIPH_ID_UART5: +#ifdef CONFIG_TEGRA20 + /* There's no divider for these clocks in this SoC. */ + return parent_rate; +#else + /* + * This undoes the +2 in get_rate_from_divider() which I + * believe is incorrect. Ideally we would fix + * get_rate_from_divider(), but... Removing the +2 from + * get_rate_from_divider() would probably require remove the -2 + * from the tail of clk_get_divider() since I believe that's + * only there to invert get_rate_from_divider()'s +2. Observe + * how find_best_divider() uses those two functions together. + * However, doing so breaks other stuff, such as Seaboard's + * display, likely due to clock_set_pllout()'s call to + * clk_get_divider(). Attempting to fix that by making + * clock_set_pllout() subtract 2 from clk_get_divider()'s + * return value doesn't help. In summary this clock driver is + * quite broken but I'm afraid I have no idea how to fix it + * without completely replacing it. + * + * Be careful to avoid a divide by zero error. + */ + if (div >= 1) + div -= 2; + break; +#endif + default: + break; + } - return get_rate_from_divider(pll_rate[parent], - (readl(reg) & OUT_CLK_DIVISOR_MASK) >> OUT_CLK_DIVISOR_SHIFT); + return get_rate_from_divider(parent_rate, div); } /** @@ -363,6 +422,20 @@ static int adjust_periph_pll(enum periph_id periph_id, int source, return 0; } +enum clock_id clock_get_periph_parent(enum periph_id periph_id) +{ + int err, mux_bits, divider_bits, type; + int source; + + err = get_periph_clock_info(periph_id, &mux_bits, ÷r_bits, &type); + if (err) + return CLOCK_ID_NONE; + + source = clock_ll_get_source_bits(periph_id, mux_bits); + + return get_periph_clock_id(periph_id, source); +} + unsigned clock_adjust_periph_pll_div(enum periph_id periph_id, enum clock_id parent, unsigned rate, int *extra_div) { @@ -404,6 +477,7 @@ unsigned clock_start_periph_pll(enum periph_id periph_id, reset_set_enable(periph_id, 1); clock_enable(periph_id); + udelay(2); effective_rate = clock_adjust_periph_pll_div(periph_id, parent, rate, NULL); @@ -510,7 +584,7 @@ unsigned clock_get_rate(enum clock_id clkid) * @param p post divider(DIVP) * @param cpcon base PLL charge pump(CPCON) * @return 0 if ok, -1 on error (the requested PLL is incorrect and cannot - * be overriden), 1 if PLL is already correct + * be overridden), 1 if PLL is already correct */ int clock_set_rate(enum clock_id clkid, u32 n, u32 m, u32 p, u32 cpcon) { @@ -581,14 +655,13 @@ void clock_ll_start_uart(enum periph_id periph_id) } #if CONFIG_IS_ENABLED(OF_CONTROL) -int clock_decode_periph_id(const void *blob, int node) +int clock_decode_periph_id(struct udevice *dev) { enum periph_id id; u32 cell[2]; int err; - err = fdtdec_get_int_array(blob, node, "clocks", cell, - ARRAY_SIZE(cell)); + err = dev_read_u32_array(dev, "clocks", cell, ARRAY_SIZE(cell)); if (err) return -1; id = clk_id_to_periph_id(cell[1]); @@ -612,6 +685,8 @@ int clock_verify(void) void clock_init(void) { + int i; + pll_rate[CLOCK_ID_CGENERAL] = clock_get_rate(CLOCK_ID_CGENERAL); pll_rate[CLOCK_ID_MEMORY] = clock_get_rate(CLOCK_ID_MEMORY); pll_rate[CLOCK_ID_PERIPH] = clock_get_rate(CLOCK_ID_PERIPH); @@ -630,6 +705,19 @@ void clock_init(void) debug("PLLU = %d\n", pll_rate[CLOCK_ID_USB]); debug("PLLD = %d\n", pll_rate[CLOCK_ID_DISPLAY]); debug("PLLX = %d\n", pll_rate[CLOCK_ID_XCPU]); + + for (i = 0; periph_clk_init_table[i].periph_id != -1; i++) { + enum periph_id periph_id; + enum clock_id parent; + int source, mux_bits, divider_bits; + + periph_id = periph_clk_init_table[i].periph_id; + parent = periph_clk_init_table[i].parent_clock_id; + + source = get_periph_clock_source(periph_id, parent, &mux_bits, + ÷r_bits); + clock_ll_set_source_bits(periph_id, mux_bits, source); + } } static void set_avp_clock_source(u32 src) @@ -727,11 +815,16 @@ void tegra30_set_up_pllp(void) int clock_external_output(int clk_id) { - struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE; + u32 val; if (clk_id >= 1 && clk_id <= 3) { - setbits_le32(&pmc->pmc_clk_out_cntrl, - 1 << (2 + (clk_id - 1) * 8)); + val = tegra_pmc_readl(offsetof(struct pmc_ctlr, + pmc_clk_out_cntrl)); + val |= 1 << (2 + (clk_id - 1) * 8); + tegra_pmc_writel(val, + offsetof(struct pmc_ctlr, + pmc_clk_out_cntrl)); + } else { printf("%s: Unknown output clock id %d\n", __func__, clk_id); return -EINVAL; @@ -739,3 +832,8 @@ int clock_external_output(int clk_id) return 0; } + +__weak bool clock_early_init_done(void) +{ + return true; +}