summaryrefslogtreecommitdiff
path: root/src/soc/nvidia/tegra124/clock.c
diff options
context:
space:
mode:
authorJulius Werner <jwerner@chromium.org>2014-04-11 18:23:12 -0700
committerMarc Jones <marc.jones@se-eng.com>2014-12-16 23:29:16 +0100
commite57c30315309a82de64da21dd83bba79864033c9 (patch)
tree1d312f055a981d5a9c5e077b2a783e5525b0cfda /src/soc/nvidia/tegra124/clock.c
parentd712ec47d48f765dbc9008d94b58842d6c24b544 (diff)
tegra124: clock: Enforce PLL constraints for VCO and CF
This patch adds some documentation to the additional PLL divisor constraints on the intermediary VCO and CF values that we just found out about. PLLC divisors for some oscillators had to be adjusted accordingly. It also adds a new clock_get_pll_input_khz() function to replace clock_get_osc_khz() in cases where you want to factor in the built-in predivider for 38.4 and 48 MHz oscillators. BUG=None TEST=Still boots. Original-Change-Id: Ib6e026dbab9fcc50d6d81a884774ad07c7b0dbc3 Original-Signed-off-by: Julius Werner <jwerner@chromium.org> Original-Reviewed-on: https://chromium-review.googlesource.com/194474 Original-Reviewed-by: Hung-Te Lin <hungte@chromium.org> (cherry picked from commit 3f1f565baf100edcd486055e4317c675c882396f) Signed-off-by: Marc Jones <marc.jones@se-eng.com> Change-Id: I091f42bf952a4b58ef2c30586baa5bf7496fa599 Reviewed-on: http://review.coreboot.org/7768 Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net> Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org> Tested-by: build bot (Jenkins)
Diffstat (limited to 'src/soc/nvidia/tegra124/clock.c')
-rw-r--r--src/soc/nvidia/tegra124/clock.c90
1 files changed, 45 insertions, 45 deletions
diff --git a/src/soc/nvidia/tegra124/clock.c b/src/soc/nvidia/tegra124/clock.c
index d12c4b2e89..765e447ecb 100644
--- a/src/soc/nvidia/tegra124/clock.c
+++ b/src/soc/nvidia/tegra124/clock.c
@@ -81,75 +81,77 @@ union __attribute__((transparent_union)) pll_fields {
/* 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).
- * Yes, it really is one equation with three unknowns ... */
+ * 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 */
- /* Based on T124 TRM (to be updatd), PLLP is set to 408MHz in HW.
- * Unless configuring PLLP to a frequency other than 408MHz,
- * software configuration on PLLP is unneeded. */
+ /* PLLDP treats p differently (OUT = VCO / (p + 1) for p < 6). */
} static const osc_table[16] = {
- [OSC_FREQ_OSC12]{
+ [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_OSC13]{
+ [OSC_FREQ_13]{
.khz = 13000,
.pllx = {.n = TEGRA_PLLX_KHZ / 13000, .m = 1, .p = 0},
- .pllc = {.n = 231, .m = 5, .p = 0}, /* 600.6 MHz */
+ .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.75 MHz */
+ .plldp = {.n = 83, .m = 1, .p = 3}, /* 269.8 MHz */
},
- [OSC_FREQ_OSC16P8]{
+ [OSC_FREQ_16P8]{
.khz = 16800,
.pllx = {.n = TEGRA_PLLX_KHZ / 16800, .m = 1, .p = 0},
- .pllc = {.n = 250, .m = 7, .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 */
+ .plldp = {.n = 64, .m = 1, .p = 3}, /* 268.8 MHz */
},
- [OSC_FREQ_OSC19P2]{
+ [OSC_FREQ_19P2]{
.khz = 19200,
.pllx = {.n = TEGRA_PLLX_KHZ / 19200, .m = 1, .p = 0},
- .pllc = {.n = 125, .m = 4, .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}, /* 270.75 MHz */
+ .plldp = {.n = 56, .m = 1, .p = 3}, /* 268.8 MHz */
},
- [OSC_FREQ_OSC26]{
+ [OSC_FREQ_26]{
.khz = 26000,
.pllx = {.n = TEGRA_PLLX_KHZ / 26000, .m = 1, .p = 0},
- .pllc = {.n = 23, .m = 1, .p = 0}, /* 598 MHz */
+ .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}, /* 266.50 MHz */
+ .plldp = {.n = 83, .m = 2, .p = 3}, /* 269.8 MHz */
},
- [OSC_FREQ_OSC38P4]{
+ /* 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,
- /*
- * There is a predivide by 2 before this PLL. Its values
- * should match the 19.2MHz values.
- */
.pllx = {.n = TEGRA_PLLX_KHZ / 19200, .m = 1, .p = 0},
- .pllc = {.n = 125, .m = 4, .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 = 2, .p = 3}, /* 268 MHz */
+ .plldp = {.n = 56, .m = 1, .p = 3}, /* 268.8 MHz */
},
- [OSC_FREQ_OSC48]{
+ [OSC_FREQ_48]{
.khz = 48000,
- /*
- * There is a predivide by 4 before this PLL. Its values
- * should match the 12MHz values.
- */
.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 = 4, .p = 3}, /* 264 MHz */
+ .plldp = {.n = 90, .m = 1, .p = 3},
},
};
@@ -159,7 +161,7 @@ struct {
*/
static u32 clock_get_osc_bits(void)
{
- return readl(&clk_rst->osc_ctrl) >> OSC_CTRL_OSC_FREQ_SHIFT;
+ return (readl(&clk_rst->osc_ctrl) & OSC_FREQ_MASK) >> OSC_FREQ_SHIFT;
}
int clock_get_osc_khz(void)
@@ -167,6 +169,14 @@ int clock_get_osc_khz(void)
return osc_table[clock_get_osc_bits()].khz;
}
+int clock_get_pll_input_khz(void)
+{
+ u32 osc_ctrl = readl(&clk_rst->osc_ctrl);
+ u32 osc_bits = (osc_ctrl & OSC_FREQ_MASK) >> OSC_FREQ_SHIFT;
+ u32 pll_ref_div = (osc_ctrl & OSC_PREDIV_MASK) >> OSC_PREDIV_SHIFT;
+ return osc_table[osc_bits].khz >> pll_ref_div;
+}
+
void clock_init_arm_generic_timer(void)
{
uint32_t freq = clock_get_osc_khz() * 1000;
@@ -225,7 +235,7 @@ static void init_pll(u32 *base, u32 *misc, const union pll_fields pll, u32 lock)
static void init_utmip_pll(void)
{
- int khz = clock_get_osc_khz();
+ int khz = clock_get_pll_input_khz();
/* Shut off PLL crystal clock while we mess with it */
clrbits_le32(&clk_rst->utmip_pll_cfg2, 1 << 30); /* PHY_XTAL_CLKEN */
@@ -304,22 +314,12 @@ clock_display(u32 frequency)
*/
struct pllpad_dividers plld = { 0 };
- u32 ref = clock_get_osc_khz() * 1000, m, n;
+ u32 ref = clock_get_pll_input_khz() * 1000, m, n;
u32 cf, vco = frequency;
const u32 max_m = 1 << 5, max_n = 1 << 10, mhz = 1000 * 1000,
min_vco = 500 * mhz, max_vco = 1000 * mhz,
min_cf = 1 * mhz, max_cf = 6 * mhz;
- /* TODO(hungte) Replace this by clock_get_pll_input_khz */
- switch (clock_get_osc_bits()) {
- case OSC_FREQ_OSC48:
- ref /= 4;
- break;
- case OSC_FREQ_OSC38P4:
- ref /= 2;
- break;
- }
-
if (vco < min_vco || vco > max_vco) {
printk(BIOS_ERR, "%s: VCO (%d) out of range. Cannot support.\n",
__func__, vco);