[coreboot-gerrit] Patch set updated for coreboot: 62465b5 coreboot arm: Define function for setting cntfrq register

Marc Jones (marc.jones@se-eng.com) gerrit at coreboot.org
Wed Feb 25 03:09:36 CET 2015


Marc Jones (marc.jones at se-eng.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/8468

-gerrit

commit 62465b509b1e8d7e58e2a55468c821b2f46c2a9b
Author: Furquan Shaikh <furquan at google.com>
Date:   Tue Jun 24 15:21:03 2014 -0700

    coreboot arm: Define function for setting cntfrq register
    
    Define functions for setting cntfrq register in arm and arm64 arch. This allows
    SoCs to set this register independently of the architecture being used.
    
    BUG=None
    BRANCH=None
    TEST=Compiles successfully for nyan and rush
    
    Original-Change-Id: I93240419b2c012eee29a408deff34a42af943a63
    Original-Signed-off-by: Furquan Shaikh <furquan at google.com>
    Original-Reviewed-on: https://chromium-review.googlesource.com/205580
    Original-Tested-by: Furquan Shaikh <furquan at chromium.org>
    Original-Reviewed-by: Aaron Durbin <adurbin at chromium.org>
    Original-Commit-Queue: Aaron Durbin <adurbin at chromium.org>
    (cherry picked from commit 768463fef5d630dec915aa0b95e7724d4a6f74b6)
    
    armv8: GPL license armv8 lib
    
    BUG=None
    BRANCH=None
    TEST=Compiles successfully.
    
    Original-Change-Id: Ibe0f09ef6704ad808cc482ffec27a4db32d7f6fd
    Original-Signed-off-by: Furquan Shaikh <furquan at google.com>
    Original-Reviewed-on: https://chromium-review.googlesource.com/250950
    Original-Trybot-Ready: Furquan Shaikh <furquan at chromium.org>
    Original-Tested-by: Furquan Shaikh <furquan at chromium.org>
    Original-Reviewed-by: Aaron Durbin <adurbin at chromium.org>
    Original-Commit-Queue: Furquan Shaikh <furquan at chromium.org>
    (cherry picked from commit bc115869bb0bcedbc284677ca5743b9ab40bfc7e)
    
    Signed-off-by: Marc Jones <marc.jones at se-eng.com>
    Change-Id: I298c3e76cb52f0876bce3dd4f54d875f62e9310a
---
 src/arch/arm/Makefile.inc             |   3 +
 src/arch/arm/clock.c                  |  36 ++
 src/arch/arm/include/arch/clock.h     |  25 ++
 src/arch/arm64/armv8/lib/Makefile.inc |   2 +-
 src/arch/arm64/armv8/lib/clock.c      |  32 ++
 src/arch/arm64/include/arch/clock.h   |  25 ++
 src/soc/nvidia/tegra124/clock.c       |   3 +-
 src/soc/nvidia/tegra132/clock.c       | 674 ++++++++++++++++++++++++++++++++++
 8 files changed, 798 insertions(+), 2 deletions(-)

diff --git a/src/arch/arm/Makefile.inc b/src/arch/arm/Makefile.inc
index 96c8b49..0fcbf6c 100644
--- a/src/arch/arm/Makefile.inc
+++ b/src/arch/arm/Makefile.inc
@@ -60,6 +60,7 @@ bootblock-y += memset.S
 bootblock-y += memcpy.S
 bootblock-y += memmove.S
 bootblock-y += div0.c
+bootblock-y += clock.c
 
 $(objcbfs)/bootblock.debug: $(src)/arch/arm/bootblock.ld $(obj)/ldoptions $$(bootblock-objs) $$(VERSTAGE_LIB)
 	@printf "    LINK       $(subst $(obj)/,,$(@))\n"
@@ -91,6 +92,7 @@ romstage-y += eabi_compat.c
 romstage-y += memset.S
 romstage-y += memcpy.S
 romstage-y += memmove.S
+romstage-y += clock.c
 
 VBOOT_STUB_DEPS += $(obj)/arch/arm/eabi_compat.rmodules_arm.o
 
@@ -115,6 +117,7 @@ ramstage-y += tables.c
 ramstage-y += memset.S
 ramstage-y += memcpy.S
 ramstage-y += memmove.S
+ramstage-y += clock.c
 ramstage-srcs += $(wildcard src/mainboard/$(MAINBOARDDIR)/mainboard.c)
 
 $(objcbfs)/ramstage.debug: $$(ramstage-objs) $(src)/arch/arm/ramstage.ld $(obj)/ldoptions
diff --git a/src/arch/arm/clock.c b/src/arch/arm/clock.c
new file mode 100644
index 0000000..5f68e6f
--- /dev/null
+++ b/src/arch/arm/clock.c
@@ -0,0 +1,36 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2014 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <stdint.h>
+#include <arch/clock.h>
+
+void set_cntfrq(uint32_t freq)
+{
+	__asm__ __volatile__("mcr p15, 0, %0, c14, c0, 0\n" :: "r"(freq));
+}
diff --git a/src/arch/arm/include/arch/clock.h b/src/arch/arm/include/arch/clock.h
new file mode 100644
index 0000000..a11fbd4
--- /dev/null
+++ b/src/arch/arm/include/arch/clock.h
@@ -0,0 +1,25 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2014 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __ARM_CLOCK_H_
+#define __ARM_CLOCK_H_
+
+void set_cntfrq(uint32_t);
+
+#endif //__ARM_CLOCK_H_
diff --git a/src/arch/arm64/armv8/lib/Makefile.inc b/src/arch/arm64/armv8/lib/Makefile.inc
index bda203b..3e393bb 100644
--- a/src/arch/arm64/armv8/lib/Makefile.inc
+++ b/src/arch/arm64/armv8/lib/Makefile.inc
@@ -20,7 +20,7 @@
 ##
 ################################################################################
 
-lib_access = pstate.c sysctrl.c cache.c tlb.c misc.c
+lib_access = pstate.c sysctrl.c cache.c tlb.c misc.c clock.c
 
 ifeq ($(CONFIG_ARCH_BOOTBLOCK_ARMV8_64),y)
 bootblock-y += $(lib_access)
diff --git a/src/arch/arm64/armv8/lib/clock.c b/src/arch/arm64/armv8/lib/clock.c
new file mode 100644
index 0000000..00c9208
--- /dev/null
+++ b/src/arch/arm64/armv8/lib/clock.c
@@ -0,0 +1,32 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2014 Google Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of
+ * the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * clock.c: Functions for accessing clock and timer related registers
+ * Reference: ARM Architecture Reference Manual, ARMv8-A edition
+ */
+
+#include <stdint.h>
+
+#include <arch/clock.h>
+
+void set_cntfrq(uint32_t freq)
+{
+	__asm__ __volatile__("msr cntfrq_el0, %0" :: "r"(freq));
+}
diff --git a/src/arch/arm64/include/arch/clock.h b/src/arch/arm64/include/arch/clock.h
new file mode 100644
index 0000000..a11fbd4
--- /dev/null
+++ b/src/arch/arm64/include/arch/clock.h
@@ -0,0 +1,25 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2014 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __ARM_CLOCK_H_
+#define __ARM_CLOCK_H_
+
+void set_cntfrq(uint32_t);
+
+#endif //__ARM_CLOCK_H_
diff --git a/src/soc/nvidia/tegra124/clock.c b/src/soc/nvidia/tegra124/clock.c
index e3c0e71..fa33283 100644
--- a/src/soc/nvidia/tegra124/clock.c
+++ b/src/soc/nvidia/tegra124/clock.c
@@ -20,6 +20,7 @@
 #include <soc/addressmap.h>
 #include <soc/clock.h>
 #include <stdlib.h>
+#include <arch/clock.h>
 #include "clk_rst.h"
 #include "flow.h"
 #include "maincpu.h"
@@ -181,7 +182,7 @@ void clock_init_arm_generic_timer(void)
 {
 	uint32_t freq = clock_get_osc_khz() * 1000;
 	// Set the cntfrq register.
-	__asm__ __volatile__("mcr p15, 0, %0, c14, c0, 0\n" :: "r"(freq));
+	set_cntfrq(freq);
 
 	// Record the system timer frequency.
 	write32(freq, &sysctr->cntfid0);
diff --git a/src/soc/nvidia/tegra132/clock.c b/src/soc/nvidia/tegra132/clock.c
new file mode 100644
index 0000000..83a73f2
--- /dev/null
+++ b/src/soc/nvidia/tegra132/clock.c
@@ -0,0 +1,674 @@
+/*
+ * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright 2014 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <console/console.h>
+#include <delay.h>
+#include <arch/io.h>
+#include <soc/addressmap.h>
+#include <soc/clock.h>
+#include <stdlib.h>
+#include <arch/clock.h>
+#include "clk_rst.h"
+#include "flow.h"
+#include "maincpu.h"
+#include "pmc.h"
+#include "sysctr.h"
+
+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;
+static struct sysctr_regs *sysctr = (void *)TEGRA_SYSCTR0_BASE;
+
+struct pll_dividers {
+	u32	n : 10;
+	u32	m : 8;
+	u32	p : 4;
+	u32	cpcon : 4;
+	u32	lfcon : 4;
+	u32	: 2;
+};
+
+/* 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	lfcon : 4;
+	u32	: 2;
+};
+
+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 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 */
+	/* PLLDP treats p differently (OUT = VCO / (p + 1) for p < 6). */
+} static const osc_table[16] = {
+	[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_13]{
+		.khz = 13000,
+		.pllx = {.n = TEGRA_PLLX_KHZ / 13000, .m =  1, .p = 0},
+		.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.8 MHz */
+	},
+	[OSC_FREQ_16P8]{
+		.khz = 16800,
+		.pllx = {.n = TEGRA_PLLX_KHZ / 16800, .m =  1, .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 */
+	},
+	[OSC_FREQ_19P2]{
+		.khz = 19200,
+		.pllx = {.n = TEGRA_PLLX_KHZ / 19200, .m =  1, .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},		 /* 268.8 MHz */
+	},
+	[OSC_FREQ_26]{
+		.khz = 26000,
+		.pllx = {.n = TEGRA_PLLX_KHZ / 26000, .m =  1, .p = 0},
+		.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},		 /* 269.8 MHz */
+	},
+	/* 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,
+		.pllx = {.n = TEGRA_PLLX_KHZ / 19200, .m =  1, .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},		 /* 268.8 MHz */
+	},
+	[OSC_FREQ_48]{
+		.khz = 48000,
+		.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},
+	},
+};
+
+/* Get the oscillator frequency, from the corresponding hardware
+ * configuration field. This is actually a per-soc thing. Avoid the
+ * temptation to make it common.
+ */
+static u32 clock_get_osc_bits(void)
+{
+	return (readl(&clk_rst->osc_ctrl) & OSC_FREQ_MASK) >> OSC_FREQ_SHIFT;
+}
+
+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;
+	/* Set the cntfrq register. */
+	set_cntfrq(freq);
+
+	/* Record the system timer frequency. */
+	write32(freq, &sysctr->cntfid0);
+	/* Enable the system counter. */
+	uint32_t cntcr = read32(&sysctr->cntcr);
+	cntcr |= SYSCTR_CNTCR_EN | SYSCTR_CNTCR_HDBG;
+	write32(cntcr, &sysctr->cntcr);
+}
+
+#define SOR0_CLK_SEL0			(1 << 14)
+#define SOR0_CLK_SEL1			(1 << 15)
+
+void sor_clock_stop(void)
+{
+	/* The Serial Output Resource clock has to be off
+	 * before we start the plldp. Learned the hard way.
+	 * FIXME: this has to be cleaned up a bit more.
+	 * Waiting on some new info from Nvidia.
+	 */
+	clrbits_le32(&clk_rst->clk_src_sor, SOR0_CLK_SEL0 | SOR0_CLK_SEL1);
+}
+
+void sor_clock_start(void)
+{
+	/* uses PLLP, has a non-standard bit layout. */
+	setbits_le32(&clk_rst->clk_src_sor, SOR0_CLK_SEL0);
+}
+
+static void init_pll(u32 *base, u32 *misc, const union pll_fields pll, u32 lock)
+{
+	u32 dividers =  pll.div.n << PLL_BASE_DIVN_SHIFT |
+			pll.div.m << PLL_BASE_DIVM_SHIFT |
+			pll.div.p << PLL_BASE_DIVP_SHIFT;
+	u32 misc_con = pll.div.cpcon << PLL_MISC_CPCON_SHIFT |
+		       pll.div.lfcon << PLL_MISC_LFCON_SHIFT;
+
+	/* Write dividers but BYPASS the PLL while we're messing with it. */
+	writel(dividers | PLL_BASE_BYPASS, base);
+	/*
+	 * Set Lock bit, CPCON and LFCON fields (default to 0 if it doesn't
+	 * exist for this PLL)
+	 */
+	writel(lock | misc_con, misc);
+
+	/* Enable PLL and take it back out of BYPASS */
+	writel(dividers | PLL_BASE_ENABLE, base);
+
+	/* Wait for lock ready */
+	while (!(readl(base) & PLL_BASE_LOCK));
+}
+
+static void init_utmip_pll(void)
+{
+	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 */
+	udelay(1);
+
+	write32(80 << 16 |			/* (rst) phy_divn */
+		1 << 8 |			/* (rst) phy_divm */
+		0, &clk_rst->utmip_pll_cfg0);	/* 960MHz * 1 / 80 == 12 MHz */
+
+	write32(div_round_up(khz, 8000) << 27 |	/* pllu_enbl_cnt / 8 (1us) */
+		0 << 16 |			/* PLLU pwrdn */
+		0 << 14 |			/* pll_enable pwrdn */
+		0 << 12 |			/* pll_active pwrdn */
+		div_round_up(khz, 102) << 0 |	/* phy_stbl_cnt / 256 (2.5ms) */
+		0, &clk_rst->utmip_pll_cfg1);
+
+	/* TODO: TRM can't decide if actv is 5us or 10us, keep an eye on it */
+	write32(0 << 24 |			/* SAMP_D/XDEV pwrdn */
+		div_round_up(khz, 3200) << 18 |	/* phy_actv_cnt / 16 (5us) */
+		div_round_up(khz, 256) << 6 |	/* pllu_stbl_cnt / 256 (1ms) */
+		0 << 4 |			/* SAMP_C/USB3 pwrdn */
+		0 << 2 |			/* SAMP_B/XHOST pwrdn */
+		0 << 0 |			/* SAMP_A/USBD pwrdn */
+		0, &clk_rst->utmip_pll_cfg2);
+
+	setbits_le32(&clk_rst->utmip_pll_cfg2, 1 << 30); /* PHY_XTAL_CLKEN */
+}
+
+/* Graphics just has to be different. There's a few more bits we
+ * need to set in here, but it makes sense just to restrict all the
+ * special bits to this one function.
+ */
+static void graphics_pll(void)
+{
+	int osc = clock_get_osc_bits();
+	u32 *cfg = &clk_rst->plldp_ss_cfg;
+	/* the vendor code sets the dither bit (28)
+	 * an undocumented bit (24)
+	 * and clamp while we mess with it (22)
+	 * Dither is pretty important to display port
+	 * so we really do need to handle these bits.
+	 * I'm not willing to not clamp it, even if
+	 * it might "mostly work" with it not set,
+	 * I don't want to find out in a few months
+	 * that it is needed.
+	 */
+	u32 scfg = (1 << 28) | (1 << 24) | (1 << 22);
+	writel(scfg, cfg);
+	init_pll(&clk_rst->plldp_base, &clk_rst->plldp_misc,
+		osc_table[osc].plldp, PLLDPD2_MISC_LOCK_ENABLE);
+	/* leave dither and undoc bits set, release clamp */
+	scfg = (1<<28) | (1<<24);
+	writel(scfg, cfg);
+
+	/* disp1 will be set when panel information (pixel clock) is
+	 * retrieved (clock_display).
+	 */
+}
+
+/* Init PLLD clock source. */
+int
+clock_display(u32 frequency)
+{
+	/**
+	 * plld (fo) = vco >> p, where 500MHz < vco < 1000MHz
+	 *           = (cf * n) >> p, where 1MHz < cf < 6MHz
+	 *           = ((ref / m) * n) >> p
+	 *
+	 * Iterate the possible values of p (3 bits, 2^7) to find out a minimum
+	 * safe vco, then find best (m, n). since m has only 5 bits, we can
+	 * iterate all possible values.  Note Tegra 124 supports 11 bits for n,
+	 * but our pll_fields has only 10 bits for n.
+	 *
+	 * Note, values that undershoot or overshoot the target output frequency 
+	 * may not work if the values are not in "safe" range by panel
+	 * specification.
+	 */
+	struct pllpad_dividers plld = { 0 };
+	u32 ref = clock_get_pll_input_khz() * 1000, m, n, p = 0;
+	u32 cf, vco;
+	u32 diff, best_diff;
+	const u32 max_m = 1 << 5, max_n = 1 << 10, max_p = 1 << 3,
+		  mhz = 1000 * 1000, min_vco = 500 * mhz, max_vco = 1000 * mhz,
+		  min_cf = 1 * mhz, max_cf = 6 * mhz;
+
+	for (vco = frequency; vco < min_vco && p < max_p; p++)
+		vco <<= 1;
+
+	if (vco < min_vco || vco > max_vco) {
+		printk(BIOS_ERR, "%s: Cannot find out a supported VCO"
+			" for Frequency (%u).\n", __func__, frequency);
+		return -1;
+	}
+
+	plld.p = p;
+	best_diff = vco;
+
+	for (m = 1; m < max_m && best_diff; m++) {
+		cf = ref / m;
+		if (cf < min_cf)
+			break;
+		if (cf > max_cf)
+			continue;
+
+		n = vco / cf;
+		if (n >= max_n)
+			continue;
+
+		diff = vco - n * cf;
+		if (n + 1 < max_n && diff > cf / 2) {
+			n++;
+			diff = cf - diff;
+		}
+
+		if (diff >= best_diff)
+			continue;
+
+		best_diff = diff;
+		plld.m = m;
+		plld.n = n;
+	}
+
+	if (plld.n < 50)
+		plld.cpcon = 2;
+	else if (plld.n < 300)
+		plld.cpcon = 3;
+	else if (plld.n < 600)
+		plld.cpcon = 8;
+	else
+		plld.cpcon = 12;
+
+	if (best_diff)
+		printk(BIOS_ERR, "%s: Failed to match output frequency %u, "
+		       "best difference is %u.\n", __func__, frequency,
+		       best_diff);
+
+	printk(BIOS_DEBUG, "%s: PLLD=%u ref=%u, m/n/p/cpcon=%u/%u/%u/%u\n",
+	       __func__, (ref / plld.m * plld.n) >> plld.p, ref, plld.m, plld.n,
+	       plld.p, plld.cpcon);
+
+	init_pll(&clk_rst->plld_base, &clk_rst->plld_misc, plld,
+		 (PLLUD_MISC_LOCK_ENABLE | PLLD_MISC_CLK_ENABLE));
+	return 0;
+}
+
+/* Initialize the UART and put it on CLK_M so we can use it during clock_init().
+ * Will later move it to PLLP in clock_config(). The divisor must be very small
+ * to accomodate 12KHz OSCs, so we override the 16.0 UART divider with the 15.1
+ * CLK_SOURCE divider to get more precision. (This might still not be enough for
+ * some OSCs... if you use 13KHz, be prepared to have a bad time.) The 1900 has
+ * been determined through trial and error (must lead to div 13 at 24MHz). */
+void clock_early_uart(void)
+{
+	write32(CLK_M << CLK_SOURCE_SHIFT | CLK_UART_DIV_OVERRIDE |
+		CLK_DIVIDER(TEGRA_CLK_M_KHZ, 1900), &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);
+}
+
+/* Enable output clock (CLK1~3) for external peripherals. */
+void clock_external_output(int clk_id)
+{
+	switch (clk_id) {
+	case 1:
+		setbits_le32(&pmc->clk_out_cntrl, 1 << 2);
+		break;
+	case 2:
+		setbits_le32(&pmc->clk_out_cntrl, 1 << 10);
+		break;
+	case 3:
+		setbits_le32(&pmc->clk_out_cntrl, 1 << 18);
+		break;
+	default:
+		printk(BIOS_CRIT, "ERROR: Unknown output clock id %d\n",
+		       clk_id);
+		break;
+	}
+}
+
+/* Start PLLM for SDRAM. */
+void clock_sdram(u32 m, u32 n, u32 p, u32 setup, u32 ph45, u32 ph90,
+		 u32 ph135, u32 kvco, u32 kcp, u32 stable_time, u32 emc_source,
+		 u32 same_freq)
+{
+	u32 misc1 = ((setup << PLLM_MISC1_SETUP_SHIFT) |
+		     (ph45 << PLLM_MISC1_PD_LSHIFT_PH45_SHIFT) |
+		     (ph90 << PLLM_MISC1_PD_LSHIFT_PH90_SHIFT) |
+		     (ph135 << PLLM_MISC1_PD_LSHIFT_PH135_SHIFT));
+	u32 misc2 = ((kvco << PLLM_MISC2_KVCO_SHIFT) |
+		 (kcp << PLLM_MISC2_KCP_SHIFT));
+	u32 base;
+
+	if (same_freq)
+		emc_source |= CLK_SOURCE_EMC_MC_EMC_SAME_FREQ;
+	else
+		emc_source &= ~CLK_SOURCE_EMC_MC_EMC_SAME_FREQ;
+
+	/*
+	 * Note PLLM_BASE.PLLM_OUT1_RSTN must be in RESET_ENABLE mode, and
+	 * PLLM_BASE.ENABLE must be in DISABLE state (both are the default
+	 * values after coldboot reset).
+	 */
+
+	writel(misc1, &clk_rst->pllm_misc1);
+	writel(misc2, &clk_rst->pllm_misc2);
+
+	/* PLLM.BASE needs BYPASS=0, different from general init_pll */
+	base = readl(&clk_rst->pllm_base);
+	base &= ~(PLLCMX_BASE_DIVN_MASK | PLLCMX_BASE_DIVM_MASK |
+		  PLLM_BASE_DIVP_MASK | PLL_BASE_BYPASS);
+	base |= ((m << PLL_BASE_DIVM_SHIFT) | (n << PLL_BASE_DIVN_SHIFT) |
+		 (p << PLL_BASE_DIVP_SHIFT));
+	writel(base, &clk_rst->pllm_base);
+
+	setbits_le32(&clk_rst->pllm_base, PLL_BASE_ENABLE);
+	/* stable_time is required, before we can start to check lock. */
+	udelay(stable_time);
+
+	while (!(readl(&clk_rst->pllm_base) & PLL_BASE_LOCK))
+		udelay(1);
+
+	/*
+	 * After PLLM reports being locked, we have to delay 10us before
+	 * enabling PLLM_OUT.
+	 */
+	udelay(10);
+
+	/* Put OUT1 out of reset state (start to output). */
+	setbits_le32(&clk_rst->pllm_out, PLLM_OUT1_RSTN_RESET_DISABLE);
+
+	/* Enable and start MEM(MC) and EMC. */
+	clock_enable_clear_reset(0, CLK_H_MEM | CLK_H_EMC, 0, 0, 0, 0);
+	writel(emc_source, &clk_rst->clk_src_emc);
+	udelay(IO_STABILIZATION_DELAY);
+}
+
+void clock_cpu0_config_and_reset(void *entry)
+{
+	void * const evp_cpu_reset = (uint8_t *)TEGRA_EVP_BASE + 0x100;
+
+	write32(CONFIG_STACK_TOP, &maincpu_stack_pointer);
+	write32((uintptr_t)entry, &maincpu_entry_point);
+	write32((uintptr_t)&maincpu_setup, evp_cpu_reset);
+
+	/* Set active CPU cluster to G */
+	clrbits_le32(&flow->cluster_control, 1);
+
+	/* Set up cclk_brst and divider. */
+	write32((CRC_CCLK_BRST_POL_PLLX_OUT0 << 0) |
+		(CRC_CCLK_BRST_POL_PLLX_OUT0 << 4) |
+		(CRC_CCLK_BRST_POL_PLLX_OUT0 << 8) |
+		(CRC_CCLK_BRST_POL_PLLX_OUT0 << 12) |
+		(CRC_CCLK_BRST_POL_CPU_STATE_RUN << 28),
+		&clk_rst->cclk_brst_pol);
+	write32(CRC_SUPER_CCLK_DIVIDER_SUPER_CDIV_ENB,
+		&clk_rst->super_cclk_div);
+
+	/* Enable the clocks for CPUs 0-3. */
+	uint32_t cpu_cmplx_clr = read32(&clk_rst->clk_cpu_cmplx_clr);
+	cpu_cmplx_clr |= CRC_CLK_CLR_CPU0_STP | CRC_CLK_CLR_CPU1_STP |
+			 CRC_CLK_CLR_CPU2_STP | CRC_CLK_CLR_CPU3_STP;
+	write32(cpu_cmplx_clr, &clk_rst->clk_cpu_cmplx_clr);
+
+	/* Enable other CPU related clocks. */
+	setbits_le32(&clk_rst->clk_out_enb_l, CLK_L_CPU);
+	setbits_le32(&clk_rst->clk_out_enb_v, CLK_V_CPUG);
+	setbits_le32(&clk_rst->clk_out_enb_v, CLK_V_CPULP);
+
+	/* Disable the reset on the non-CPU parts of the fast cluster. */
+	write32(CRC_RST_CPUG_CLR_NONCPU,
+		&clk_rst->rst_cpug_cmplx_clr);
+	/* Disable the various resets on the CPUs. */
+	write32(CRC_RST_CPUG_CLR_CPU0 | CRC_RST_CPUG_CLR_CPU1 |
+		CRC_RST_CPUG_CLR_CPU2 | CRC_RST_CPUG_CLR_CPU3 |
+		CRC_RST_CPUG_CLR_DBG0 | CRC_RST_CPUG_CLR_DBG1 |
+		CRC_RST_CPUG_CLR_DBG2 | CRC_RST_CPUG_CLR_DBG3 |
+		CRC_RST_CPUG_CLR_CORE0 | CRC_RST_CPUG_CLR_CORE1 |
+		CRC_RST_CPUG_CLR_CORE2 | CRC_RST_CPUG_CLR_CORE3 |
+		CRC_RST_CPUG_CLR_CX0 | CRC_RST_CPUG_CLR_CX1 |
+		CRC_RST_CPUG_CLR_CX2 | CRC_RST_CPUG_CLR_CX3 |
+		CRC_RST_CPUG_CLR_L2 | CRC_RST_CPUG_CLR_PDBG,
+		&clk_rst->rst_cpug_cmplx_clr);
+
+	/* Disable the reset on the non-CPU parts of the slow cluster. */
+	write32(CRC_RST_CPULP_CLR_NONCPU,
+		&clk_rst->rst_cpulp_cmplx_clr);
+	/* Disable the various resets on the LP CPU.  */
+	write32(CRC_RST_CPULP_CLR_CPU0 | CRC_RST_CPULP_CLR_DBG0 |
+		CRC_RST_CPULP_CLR_CORE0 | CRC_RST_CPULP_CLR_CX0 |
+		CRC_RST_CPULP_CLR_L2 | CRC_RST_CPULP_CLR_PDBG,
+		&clk_rst->rst_cpulp_cmplx_clr);
+}
+
+void clock_halt_avp(void)
+{
+	for (;;)
+		write32(FLOW_EVENT_JTAG | FLOW_EVENT_LIC_IRQ |
+			FLOW_EVENT_GIC_IRQ | FLOW_MODE_WAITEVENT,
+			&flow->halt_cop_events);
+}
+
+void clock_init(void)
+{
+	u32 osc = clock_get_osc_bits();
+
+	/* Set PLLC dynramp_step A to 0x2b and B to 0xb (from U-Boot -- why? */
+	writel(0x2b << 17 | 0xb << 9, &clk_rst->pllc_misc2);
+
+	/* Max out the AVP clock before everything else (need PLLC for that). */
+	init_pll(&clk_rst->pllc_base, &clk_rst->pllc_misc,
+		osc_table[osc].pllc, PLLC_MISC_LOCK_ENABLE);
+
+	/* Typical ratios are 1:2:2 or 1:2:3 sclk:hclk:pclk (See: APB DMA
+	 * features section in the TRM). */
+	write32(1 << HCLK_DIVISOR_SHIFT | 0 << PCLK_DIVISOR_SHIFT,
+		&clk_rst->clk_sys_rate);	/* pclk = hclk = sclk/2 */
+	write32(CLK_DIVIDER(TEGRA_PLLC_KHZ, 300000) << PLL_OUT_RATIO_SHIFT |
+		PLL_OUT_CLKEN | PLL_OUT_RSTN, &clk_rst->pllc_out);
+	write32(SCLK_SYS_STATE_RUN << SCLK_SYS_STATE_SHIFT |
+		SCLK_SOURCE_PLLC_OUT1 << SCLK_RUN_SHIFT,
+		&clk_rst->sclk_brst_pol);		/* sclk = 300 MHz */
+
+	/* Change the oscillator drive strength (from U-Boot -- why?) */
+	clrsetbits_le32(&clk_rst->osc_ctrl, OSC_XOFS_MASK,
+			OSC_DRIVE_STRENGTH << OSC_XOFS_SHIFT);
+
+	/*
+	 * Ambiguous quote from u-boot. TODO: what's this mean?
+	 * "should update same value in PMC_OSC_EDPD_OVER XOFS
+	 * field for warmboot "
+	 */
+	clrsetbits_le32(&pmc->osc_edpd_over, PMC_OSC_EDPD_OVER_XOFS_MASK,
+			OSC_DRIVE_STRENGTH << PMC_OSC_EDPD_OVER_XOFS_SHIFT);
+
+	/* Disable IDDQ for PLLX before we set it up (from U-Boot -- why?) */
+	clrbits_le32(&clk_rst->pllx_misc3, PLLX_IDDQ_MASK);
+
+	/* Set up PLLP_OUT(1|2|3|4) divisor to generate (9.6|48|102|204)MHz */
+	write32((CLK_DIVIDER(TEGRA_PLLP_KHZ, 9600) << PLL_OUT_RATIO_SHIFT |
+		 PLL_OUT_OVR | PLL_OUT_CLKEN | PLL_OUT_RSTN) << PLL_OUT1_SHIFT |
+		(CLK_DIVIDER(TEGRA_PLLP_KHZ, 48000) << PLL_OUT_RATIO_SHIFT |
+		 PLL_OUT_OVR | PLL_OUT_CLKEN | PLL_OUT_RSTN) << PLL_OUT2_SHIFT,
+		&clk_rst->pllp_outa);
+	write32((CLK_DIVIDER(TEGRA_PLLP_KHZ, 102000) << PLL_OUT_RATIO_SHIFT |
+		 PLL_OUT_OVR | PLL_OUT_CLKEN | PLL_OUT_RSTN) << PLL_OUT3_SHIFT |
+		(CLK_DIVIDER(TEGRA_PLLP_KHZ, 204000) << PLL_OUT_RATIO_SHIFT |
+		 PLL_OUT_OVR | PLL_OUT_CLKEN | PLL_OUT_RSTN) << PLL_OUT4_SHIFT,
+		&clk_rst->pllp_outb);
+
+	/* init pllx */
+	init_pll(&clk_rst->pllx_base, &clk_rst->pllx_misc,
+		osc_table[osc].pllx, PLLPAXS_MISC_LOCK_ENABLE);
+
+	/* init pllu */
+	init_pll(&clk_rst->pllu_base, &clk_rst->pllu_misc,
+		osc_table[osc].pllu, PLLUD_MISC_LOCK_ENABLE);
+
+	init_utmip_pll();
+	graphics_pll();
+}
+
+void clock_enable_clear_reset(u32 l, u32 h, u32 u, u32 v, u32 w, u32 x)
+{
+	if (l)
+		writel(l, &clk_rst->clk_enb_l_set);
+	if (h)
+		writel(h, &clk_rst->clk_enb_h_set);
+	if (u)
+		writel(u, &clk_rst->clk_enb_u_set);
+	if (v)
+		writel(v, &clk_rst->clk_enb_v_set);
+	if (w)
+		writel(w, &clk_rst->clk_enb_w_set);
+	if (x)
+		writel(x, &clk_rst->clk_enb_x_set);
+
+	/* Give clocks time to stabilize. */
+	udelay(IO_STABILIZATION_DELAY);
+
+	if (l)
+		writel(l, &clk_rst->rst_dev_l_clr);
+	if (h)
+		writel(h, &clk_rst->rst_dev_h_clr);
+	if (u)
+		writel(u, &clk_rst->rst_dev_u_clr);
+	if (v)
+		writel(v, &clk_rst->rst_dev_v_clr);
+	if (w)
+		writel(w, &clk_rst->rst_dev_w_clr);
+	if (x)
+		writel(x, &clk_rst->rst_dev_x_clr);
+}
+
+void clock_reset_l(u32 bit)
+{
+	writel(bit, &clk_rst->rst_dev_l_set);
+	udelay(1);
+	writel(bit, &clk_rst->rst_dev_l_clr);
+}
+
+void clock_reset_h(u32 bit)
+{
+	writel(bit, &clk_rst->rst_dev_h_set);
+	udelay(1);
+	writel(bit, &clk_rst->rst_dev_h_clr);
+}
+
+void clock_reset_u(u32 bit)
+{
+	writel(bit, &clk_rst->rst_dev_u_set);
+	udelay(1);
+	writel(bit, &clk_rst->rst_dev_u_clr);
+}
+
+void clock_reset_v(u32 bit)
+{
+	writel(bit, &clk_rst->rst_dev_v_set);
+	udelay(1);
+	writel(bit, &clk_rst->rst_dev_v_clr);
+}
+
+void clock_reset_w(u32 bit)
+{
+	writel(bit, &clk_rst->rst_dev_w_set);
+	udelay(1);
+	writel(bit, &clk_rst->rst_dev_w_clr);
+}
+
+void clock_reset_x(u32 bit)
+{
+	writel(bit, &clk_rst->rst_dev_x_set);
+	udelay(1);
+	writel(bit, &clk_rst->rst_dev_x_clr);
+}



More information about the coreboot-gerrit mailing list