ARM: tegra: clock: fix PLLD/PLLD2 related clock calculations

While PLLD/D2 is the nominal parent clock, all derived clocks are generated
from its single output, plld_out0, which is PLLD/D2 divided by two. Direct
use of PLLD/D2 is absent in peripheral clock configurations. Therefore,
clock derivation formulas must take in account this division.

Signed-off-by: Jonas Schwöbel <jonasschwoebel@yahoo.de>
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
This commit is contained in:
Jonas Schwöbel
2025-03-04 09:02:11 +02:00
committed by Svyatoslav Ryhel
parent 8fb7ed59a8
commit dbc27c2462

View File

@@ -358,6 +358,13 @@ unsigned long clock_get_periph_rate(enum periph_id periph_id,
break; break;
} }
/*
* PLLD/PLLD2 raw clock rate is never used, instead plld_out0 is used
* that is PLLD/PLLD2 halved.
*/
if (parent == CLOCK_ID_DISPLAY || parent == CLOCK_ID_DISPLAY2)
parent_rate /= 2;
return get_rate_from_divider(parent_rate, div); return get_rate_from_divider(parent_rate, div);
} }
@@ -449,6 +456,7 @@ unsigned clock_adjust_periph_pll_div(enum periph_id periph_id,
enum clock_id parent, unsigned rate, int *extra_div) enum clock_id parent, unsigned rate, int *extra_div)
{ {
unsigned effective_rate; unsigned effective_rate;
unsigned int parent_rate;
int mux_bits, divider_bits, source; int mux_bits, divider_bits, source;
int divider; int divider;
int xdiv = 0; int xdiv = 0;
@@ -457,7 +465,17 @@ unsigned clock_adjust_periph_pll_div(enum periph_id periph_id,
source = get_periph_clock_source(periph_id, parent, &mux_bits, source = get_periph_clock_source(periph_id, parent, &mux_bits,
&divider_bits); &divider_bits);
divider = find_best_divider(divider_bits, pll_rate[parent], /*
* Clocks derived from PLLD/D2 are actually sourced from its halved
* output, plld_out0/plld2_out0. No peripheral clocks use the raw
* PLLD/D2 frequency. This halving must be accounted for in derived
* clock calculations.
*/
parent_rate = pll_rate[parent];
if (parent == CLOCK_ID_DISPLAY || parent == CLOCK_ID_DISPLAY2)
parent_rate /= 2;
divider = find_best_divider(divider_bits, parent_rate,
rate, &xdiv); rate, &xdiv);
if (extra_div) if (extra_div)
*extra_div = xdiv; *extra_div = xdiv;
@@ -685,6 +703,16 @@ int clock_set_rate(enum clock_id clkid, u32 n, u32 m, u32 p, u32 cpcon)
else else
writel(base_reg, &simple_pll->pll_base); writel(base_reg, &simple_pll->pll_base);
/*
* Changing clocks was never intended in the U-Boot for Tegra.
* If a clock is changed after clock_init() the parent rate is wrong.
* Usually there is no reason to change peripheral clocks, but Display
* PLLs which needs to generate a precise pixelclock might be adjusted.
* Especially in the case of HDMI display with changing and prior
* unknown resolution.
*/
pll_rate[clkid] = clock_get_rate(clkid);
return 0; return 0;
} }