[U-Boot] [PATCH 15/35] arm: socfpga: clock: Add code to read clock configuration

Marek Vasut marex at denx.de
Mon Sep 15 13:06:08 CEST 2014


From: Pavel Machek <pavel at denx.de>

Add the entire bulk of code to read out clock configuration from the SoCFPGA
CPU registers. This is important for MMC, QSPI and UART drivers as otherwise
they cannot determine the frequency of their upstream clock.

Signed-off-by: Pavel Machek <pavel at denx.de>
Signed-off-by: Marek Vasut <marex at denx.de>
Cc: Chin Liang See <clsee at altera.com>
Cc: Dinh Nguyen <dinguyen at altera.com>
Cc: Albert Aribaud <albert.u.boot at aribaud.net>
Cc: Tom Rini <trini at ti.com>
Cc: Wolfgang Denk <wd at denx.de>
Cc: Pavel Machek <pavel at denx.de>
---
 arch/arm/cpu/armv7/socfpga/clock_manager.c        | 226 +++++++++++++++++++++-
 arch/arm/include/asm/arch-socfpga/clock_manager.h |  43 +++-
 include/configs/socfpga_cyclone5.h                |   1 +
 3 files changed, 267 insertions(+), 3 deletions(-)

diff --git a/arch/arm/cpu/armv7/socfpga/clock_manager.c b/arch/arm/cpu/armv7/socfpga/clock_manager.c
index d032bbd..07cf74c 100644
--- a/arch/arm/cpu/armv7/socfpga/clock_manager.c
+++ b/arch/arm/cpu/armv7/socfpga/clock_manager.c
@@ -8,8 +8,10 @@
 #include <asm/io.h>
 #include <asm/arch/clock_manager.h>
 
+DECLARE_GLOBAL_DATA_PTR;
+
 static const struct socfpga_clock_manager *clock_manager_base =
-		(void *)SOCFPGA_CLKMGR_ADDRESS;
+	(struct socfpga_clock_manager *)SOCFPGA_CLKMGR_ADDRESS;
 
 #define CLKMGR_BYPASS_ENABLE	1
 #define CLKMGR_BYPASS_DISABLE	0
@@ -358,3 +360,225 @@ void cm_basic_init(const cm_config_t *cfg)
 	writel(~0, &clock_manager_base->per_pll.en);
 	writel(~0, &clock_manager_base->sdr_pll.en);
 }
+
+unsigned long cm_get_mpu_clk_hz(void)
+{
+	uint32_t reg, clock;
+
+	/* get the main VCO clock */
+	reg = readl(&clock_manager_base->main_pll.vco);
+	clock = CONFIG_HPS_CLK_OSC1_HZ /
+		(CLKMGR_MAINPLLGRP_VCO_DENOM_GET(reg) + 1);
+	clock *= (CLKMGR_MAINPLLGRP_VCO_NUMER_GET(reg) + 1);
+
+	/* get the MPU clock */
+	reg = readl(&clock_manager_base->altera.mpuclk);
+	clock /= (reg + 1);
+	reg = readl(&clock_manager_base->main_pll.mpuclk);
+	clock /= (reg + 1);
+	return clock;
+}
+
+unsigned long cm_get_sdram_clk_hz(void)
+{
+	uint32_t reg, clock = 0;
+
+	/* identify SDRAM PLL clock source */
+	reg = readl(&clock_manager_base->sdr_pll.vco);
+	reg = CLKMGR_SDRPLLGRP_VCO_SSRC_GET(reg);
+	if (reg == CLKMGR_VCO_SSRC_EOSC1)
+		clock = CONFIG_HPS_CLK_OSC1_HZ;
+	else if (reg == CLKMGR_VCO_SSRC_EOSC2)
+		clock = CONFIG_HPS_CLK_OSC2_HZ;
+	else if (reg == CLKMGR_VCO_SSRC_F2S)
+		clock = CONFIG_HPS_CLK_F2S_SDR_REF_HZ;
+
+	/* get the SDRAM VCO clock */
+	reg = readl(&clock_manager_base->sdr_pll.vco);
+	clock /= (CLKMGR_SDRPLLGRP_VCO_DENOM_GET(reg) + 1);
+	clock *= (CLKMGR_SDRPLLGRP_VCO_NUMER_GET(reg) + 1);
+
+	/* get the SDRAM (DDR_DQS) clock */
+	reg = readl(&clock_manager_base->sdr_pll.ddrdqsclk);
+	reg = CLKMGR_SDRPLLGRP_DDRDQSCLK_CNT_GET(reg);
+	clock /= (reg + 1);
+
+	return clock;
+}
+
+unsigned int cm_get_l4_sp_clk_hz(void)
+{
+	uint32_t reg, clock = 0;
+
+	/* identify the source of L4 SP clock */
+	reg = readl(&clock_manager_base->main_pll.l4src);
+	reg = CLKMGR_MAINPLLGRP_L4SRC_L4SP_GET(reg);
+
+	if (reg == CLKMGR_L4_SP_CLK_SRC_MAINPLL) {
+		/* get the main VCO clock */
+		reg = readl(&clock_manager_base->main_pll.vco);
+		clock = CONFIG_HPS_CLK_OSC1_HZ /
+			(CLKMGR_MAINPLLGRP_VCO_DENOM_GET(reg) + 1);
+		clock *= (CLKMGR_MAINPLLGRP_VCO_NUMER_GET(reg) + 1);
+
+		/* get the clock prior L4 SP divider (main clk) */
+		reg = readl(&clock_manager_base->altera.mainclk);
+		clock /= (reg + 1);
+		reg = readl(&clock_manager_base->main_pll.mainclk);
+		clock /= (reg + 1);
+	} else if (reg == CLKMGR_L4_SP_CLK_SRC_PERPLL) {
+		/* identify PER PLL clock source */
+		reg = readl(&clock_manager_base->per_pll.vco);
+		reg = CLKMGR_PERPLLGRP_VCO_SSRC_GET(reg);
+		if (reg == CLKMGR_VCO_SSRC_EOSC1)
+			clock = CONFIG_HPS_CLK_OSC1_HZ;
+		else if (reg == CLKMGR_VCO_SSRC_EOSC2)
+			clock = CONFIG_HPS_CLK_OSC2_HZ;
+		else if (reg == CLKMGR_VCO_SSRC_F2S)
+			clock = CONFIG_HPS_CLK_F2S_PER_REF_HZ;
+
+		/* get the PER VCO clock */
+		reg = readl(&clock_manager_base->per_pll.vco);
+		clock /= (CLKMGR_PERPLLGRP_VCO_DENOM_GET(reg) + 1);
+		clock *= (CLKMGR_PERPLLGRP_VCO_NUMER_GET(reg) + 1);
+
+		/* get the clock prior L4 SP divider (periph_base_clk) */
+		reg = readl(&clock_manager_base->per_pll.perbaseclk);
+		clock /= (reg + 1);
+	}
+
+	/* get the L4 SP clock which supplied to UART */
+	reg = readl(&clock_manager_base->main_pll.maindiv);
+	reg = CLKMGR_MAINPLLGRP_MAINDIV_L4SPCLK_GET(reg);
+	clock = clock / (reg + 1);
+
+	return clock;
+}
+
+unsigned int cm_get_mmc_controller_clk_hz(void)
+{
+	uint32_t reg, clock = 0;
+
+	/* identify the source of MMC clock */
+	reg = readl(&clock_manager_base->per_pll.src);
+	reg = CLKMGR_PERPLLGRP_SRC_SDMMC_GET(reg);
+
+	if (reg == CLKMGR_SDMMC_CLK_SRC_F2S) {
+		clock = CONFIG_HPS_CLK_F2S_PER_REF_HZ;
+	} else if (reg == CLKMGR_SDMMC_CLK_SRC_MAIN) {
+		/* get the main VCO clock */
+		reg = readl(&clock_manager_base->main_pll.vco);
+		clock = CONFIG_HPS_CLK_OSC1_HZ /
+			(CLKMGR_MAINPLLGRP_VCO_DENOM_GET(reg) + 1);
+		clock *= (CLKMGR_MAINPLLGRP_VCO_NUMER_GET(reg) + 1);
+
+		/* get the SDMMC clock */
+		reg = readl(&clock_manager_base->main_pll.mainnandsdmmcclk);
+		clock /= (reg + 1);
+	} else if (reg == CLKMGR_SDMMC_CLK_SRC_PER) {
+		/* identify PER PLL clock source */
+		reg = readl(&clock_manager_base->per_pll.vco);
+		reg = CLKMGR_PERPLLGRP_VCO_SSRC_GET(reg);
+		if (reg == CLKMGR_VCO_SSRC_EOSC1)
+			clock = CONFIG_HPS_CLK_OSC1_HZ;
+		else if (reg == CLKMGR_VCO_SSRC_EOSC2)
+			clock = CONFIG_HPS_CLK_OSC2_HZ;
+		else if (reg == CLKMGR_VCO_SSRC_F2S)
+			clock = CONFIG_HPS_CLK_F2S_PER_REF_HZ;
+
+		/* get the PER VCO clock */
+		reg = readl(&clock_manager_base->per_pll.vco);
+		clock /= (CLKMGR_PERPLLGRP_VCO_DENOM_GET(reg) + 1);
+		clock *= (CLKMGR_PERPLLGRP_VCO_NUMER_GET(reg) + 1);
+
+		/* get the SDMMC clock */
+		reg = readl(&clock_manager_base->per_pll.pernandsdmmcclk);
+		clock /= (reg + 1);
+	}
+
+	/* further divide by 4 as we have fixed divider at wrapper */
+	clock /= 4;
+	return clock;
+}
+
+unsigned int cm_get_qspi_controller_clk_hz(void)
+{
+	uint32_t reg, clock = 0;
+
+	/* identify the source of QSPI clock */
+	reg = readl(&clock_manager_base->per_pll.src);
+	reg = CLKMGR_PERPLLGRP_SRC_QSPI_GET(reg);
+
+	if (reg == CLKMGR_QSPI_CLK_SRC_F2S) {
+		clock = CONFIG_HPS_CLK_F2S_PER_REF_HZ;
+	} else if (reg == CLKMGR_QSPI_CLK_SRC_MAIN) {
+		/* get the main VCO clock */
+		reg = readl(&clock_manager_base->main_pll.vco);
+		clock = CONFIG_HPS_CLK_OSC1_HZ /
+			(CLKMGR_MAINPLLGRP_VCO_DENOM_GET(reg) + 1);
+		clock *= (CLKMGR_MAINPLLGRP_VCO_NUMER_GET(reg) + 1);
+
+		/* get the qspi clock */
+		reg = readl(&clock_manager_base->main_pll.mainqspiclk);
+		clock /= (reg + 1);
+	} else if (reg == CLKMGR_QSPI_CLK_SRC_PER) {
+		/* identify PER PLL clock source */
+		reg = readl(&clock_manager_base->per_pll.vco);
+		reg = CLKMGR_PERPLLGRP_VCO_SSRC_GET(reg);
+		if (reg == CLKMGR_VCO_SSRC_EOSC1)
+			clock = CONFIG_HPS_CLK_OSC1_HZ;
+		else if (reg == CLKMGR_VCO_SSRC_EOSC2)
+			clock = CONFIG_HPS_CLK_OSC2_HZ;
+		else if (reg == CLKMGR_VCO_SSRC_F2S)
+			clock = CONFIG_HPS_CLK_F2S_PER_REF_HZ;
+
+		/* get the PER VCO clock */
+		reg = readl(&clock_manager_base->per_pll.vco);
+		clock /= (CLKMGR_PERPLLGRP_VCO_DENOM_GET(reg) + 1);
+		clock *= (CLKMGR_PERPLLGRP_VCO_NUMER_GET(reg) + 1);
+
+		/* get the qspi clock */
+		reg = readl(&clock_manager_base->per_pll.perqspiclk);
+		clock /= (reg + 1);
+	}
+
+	return clock;
+}
+
+static void cm_print_clock_quick_summary(void)
+{
+	printf("MPU       %10ld kHz\n", cm_get_mpu_clk_hz() / 1000);
+	printf("DDR       %10ld kHz\n", cm_get_sdram_clk_hz() / 1000);
+	printf("EOSC1       %8d kHz\n", CONFIG_HPS_CLK_OSC1_HZ / 1000);
+	printf("EOSC2       %8d kHz\n", CONFIG_HPS_CLK_OSC2_HZ / 1000);
+	printf("F2S_SDR_REF %8d kHz\n", CONFIG_HPS_CLK_F2S_SDR_REF_HZ / 1000);
+	printf("F2S_PER_REF %8d kHz\n", CONFIG_HPS_CLK_F2S_PER_REF_HZ / 1000);
+	printf("MMC         %8d kHz\n", cm_get_mmc_controller_clk_hz() / 1000);
+	printf("QSPI        %8d kHz\n", cm_get_qspi_controller_clk_hz() / 1000);
+	printf("UART        %8d kHz\n", cm_get_l4_sp_clk_hz() / 1000);
+}
+
+int set_cpu_clk_info(void)
+{
+	/* Calculate the clock frequencies required for drivers */
+	cm_get_l4_sp_clk_hz();
+	cm_get_mmc_controller_clk_hz();
+
+	gd->bd->bi_arm_freq = cm_get_mpu_clk_hz() / 1000000;
+	gd->bd->bi_dsp_freq = 0;
+	gd->bd->bi_ddr_freq = cm_get_sdram_clk_hz() / 1000000;
+
+	return 0;
+}
+
+int do_showclocks(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	cm_print_clock_quick_summary();
+	return 0;
+}
+
+U_BOOT_CMD(
+	clocks,	CONFIG_SYS_MAXARGS, 1, do_showclocks,
+	"display clocks",
+	""
+);
diff --git a/arch/arm/include/asm/arch-socfpga/clock_manager.h b/arch/arm/include/asm/arch-socfpga/clock_manager.h
index dea171e..d2a9b48 100644
--- a/arch/arm/include/asm/arch-socfpga/clock_manager.h
+++ b/arch/arm/include/asm/arch-socfpga/clock_manager.h
@@ -7,6 +7,15 @@
 #ifndef	_CLOCK_MANAGER_H_
 #define	_CLOCK_MANAGER_H_
 
+#ifndef __ASSEMBLER__
+/* Clock speed accessors */
+unsigned long cm_get_mpu_clk_hz(void);
+unsigned long cm_get_sdram_clk_hz(void);
+unsigned int cm_get_l4_sp_clk_hz(void);
+unsigned int cm_get_mmc_controller_clk_hz(void);
+unsigned int cm_get_qspi_controller_clk_hz(void);
+#endif
+
 typedef struct {
 	/* main group */
 	uint32_t main_vco_base;
@@ -89,6 +98,11 @@ struct socfpga_clock_manager_sdr_pll {
 	u32	stat;
 };
 
+struct socfpga_clock_manager_altera {
+	u32	mpuclk;
+	u32	mainclk;
+};
+
 struct socfpga_clock_manager {
 	u32	ctrl;
 	u32	bypass;
@@ -100,7 +114,8 @@ struct socfpga_clock_manager {
 	struct socfpga_clock_manager_main_pll main_pll;
 	struct socfpga_clock_manager_per_pll per_pll;
 	struct socfpga_clock_manager_sdr_pll sdr_pll;
-	u32	_pad_0xe0_0x200[72];
+	struct socfpga_clock_manager_altera altera;
+	u32	_pad_0xe8_0x200[70];
 };
 
 #define CLKMGR_CTRL_SAFEMODE_MASK 0x00000001
@@ -118,8 +133,10 @@ struct socfpga_clock_manager {
 
 /* Main PLL */
 #define CLKMGR_MAINPLLGRP_VCO_BGPWRDN_SET(x) (((x) << 0) & 0x00000001)
+#define CLKMGR_MAINPLLGRP_VCO_DENOM_GET(x) (((x) & 0x003f0000) >> 16)
 #define CLKMGR_MAINPLLGRP_VCO_DENOM_SET(x) (((x) << 16) & 0x003f0000)
 #define CLKMGR_MAINPLLGRP_VCO_EN_SET(x) (((x) << 1) & 0x00000002)
+#define CLKMGR_MAINPLLGRP_VCO_NUMER_GET(x) (((x) & 0x0000fff8) >> 3)
 #define CLKMGR_MAINPLLGRP_VCO_NUMER_SET(x) (((x) << 3) & 0x0000fff8)
 #define CLKMGR_MAINPLLGRP_VCO_OUTRESETALL_MASK 0x01000000
 #define CLKMGR_MAINPLLGRP_VCO_PWRDN_SET(x) (((x) << 2) & 0x00000004)
@@ -148,7 +165,8 @@ struct socfpga_clock_manager {
 #define CLKMGR_MAINPLLGRP_MAINDIV_L3MPCLK_SET(x) (((x) << 0) & 0x00000003)
 #define CLKMGR_MAINPLLGRP_MAINDIV_L3SPCLK_SET(x) (((x) << 2) & 0x0000000c)
 #define CLKMGR_MAINPLLGRP_MAINDIV_L4MPCLK_SET(x) (((x) << 4) & 0x00000070)
-#define CLKMGR_MAINPLLGRP_MAINDIV_L4SPCLK_SET(x)  (((x) << 7) & 0x00000380)
+#define CLKMGR_MAINPLLGRP_MAINDIV_L4SPCLK_GET(x) (((x) & 0x00000380) >> 7)
+#define CLKMGR_MAINPLLGRP_MAINDIV_L4SPCLK_SET(x) (((x) << 7) & 0x00000380)
 
 #define CLKMGR_MAINPLLGRP_DBGDIV_DBGATCLK_SET(x) (((x) << 0) & 0x00000003)
 #define CLKMGR_MAINPLLGRP_DBGDIV_DBGCLK_SET(x) (((x) << 2) & 0x0000000c)
@@ -156,16 +174,25 @@ struct socfpga_clock_manager {
 #define CLKMGR_MAINPLLGRP_TRACEDIV_TRACECLK_SET(x) (((x) << 0) & 0x00000007)
 
 #define CLKMGR_MAINPLLGRP_L4SRC_L4MP_SET(x) (((x) << 0) & 0x00000001)
+#define CLKMGR_MAINPLLGRP_L4SRC_L4SP_GET(x) (((x) & 0x00000002) >> 1)
 #define CLKMGR_MAINPLLGRP_L4SRC_L4SP_SET(x) (((x) << 1) & 0x00000002)
 #define CLKMGR_MAINPLLGRP_L4SRC_RESET_VALUE 0x00000000
+#define CLKMGR_L4_SP_CLK_SRC_MAINPLL	0x0
+#define CLKMGR_L4_SP_CLK_SRC_PERPLL	0x1
 
 /* Per PLL */
+#define CLKMGR_PERPLLGRP_VCO_DENOM_GET(x) (((x) & 0x003f0000) >> 16)
 #define CLKMGR_PERPLLGRP_VCO_DENOM_SET(x) (((x) << 16) & 0x003f0000)
+#define CLKMGR_PERPLLGRP_VCO_NUMER_GET(x) (((x) & 0x0000fff8) >> 3)
 #define CLKMGR_PERPLLGRP_VCO_NUMER_SET(x) (((x) << 3) & 0x0000fff8)
 #define CLKMGR_PERPLLGRP_VCO_OUTRESETALL_MASK 0x01000000
 #define CLKMGR_PERPLLGRP_VCO_PSRC_SET(x) (((x) << 22) & 0x00c00000)
 #define CLKMGR_PERPLLGRP_VCO_REGEXTSEL_MASK 0x80000000
 #define CLKMGR_PERPLLGRP_VCO_RESET_VALUE 0x8001000d
+#define CLKMGR_PERPLLGRP_VCO_SSRC_GET(x) (((x) & 0x00c00000) >> 22)
+#define CLKMGR_VCO_SSRC_EOSC1		0x0
+#define CLKMGR_VCO_SSRC_EOSC2		0x1
+#define CLKMGR_VCO_SSRC_F2S		0x2
 
 #define CLKMGR_PERPLLGRP_EMAC0CLK_CNT_SET(x) (((x) << 0) & 0x000001ff)
 
@@ -191,12 +218,22 @@ struct socfpga_clock_manager {
 #define CLKMGR_PERPLLGRP_GPIODIV_GPIODBCLK_SET(x) (((x) << 0) & 0x00ffffff)
 
 #define CLKMGR_PERPLLGRP_SRC_NAND_SET(x) (((x) << 2) & 0x0000000c)
+#define CLKMGR_PERPLLGRP_SRC_QSPI_GET(x) (((x) & 0x00000030) >> 4)
 #define CLKMGR_PERPLLGRP_SRC_QSPI_SET(x) (((x) << 4) & 0x00000030)
 #define CLKMGR_PERPLLGRP_SRC_RESET_VALUE 0x00000015
+#define CLKMGR_PERPLLGRP_SRC_SDMMC_GET(x) (((x) & 0x00000003) >> 0)
 #define CLKMGR_PERPLLGRP_SRC_SDMMC_SET(x) (((x) << 0) & 0x00000003)
+#define CLKMGR_SDMMC_CLK_SRC_F2S	0x0
+#define CLKMGR_SDMMC_CLK_SRC_MAIN	0x1
+#define CLKMGR_SDMMC_CLK_SRC_PER	0x2
+#define CLKMGR_QSPI_CLK_SRC_F2S		0x0
+#define CLKMGR_QSPI_CLK_SRC_MAIN	0x1
+#define CLKMGR_QSPI_CLK_SRC_PER		0x2
 
 /* SDR PLL */
+#define CLKMGR_SDRPLLGRP_VCO_DENOM_GET(x) (((x) & 0x003f0000) >> 16)
 #define CLKMGR_SDRPLLGRP_VCO_DENOM_SET(x) (((x) << 16) & 0x003f0000)
+#define CLKMGR_SDRPLLGRP_VCO_NUMER_GET(x) (((x) & 0x0000fff8) >> 3)
 #define CLKMGR_SDRPLLGRP_VCO_NUMER_SET(x) (((x) << 3) & 0x0000fff8)
 #define CLKMGR_SDRPLLGRP_VCO_OUTRESETALL_SET(x) (((x) << 24) & 0x01000000)
 #define CLKMGR_SDRPLLGRP_VCO_OUTRESETALL_SET(x) (((x) << 24) & 0x01000000)
@@ -204,8 +241,10 @@ struct socfpga_clock_manager {
 #define CLKMGR_SDRPLLGRP_VCO_OUTRESET_SET(x) (((x) << 25) & 0x7e000000)
 #define CLKMGR_SDRPLLGRP_VCO_REGEXTSEL_MASK 0x80000000
 #define CLKMGR_SDRPLLGRP_VCO_RESET_VALUE 0x8001000d
+#define CLKMGR_SDRPLLGRP_VCO_SSRC_GET(x) (((x) & 0x00c00000) >> 22)
 #define CLKMGR_SDRPLLGRP_VCO_SSRC_SET(x) (((x) << 22) & 0x00c00000)
 
+#define CLKMGR_SDRPLLGRP_DDRDQSCLK_CNT_GET(x) (((x) & 0x000001ff) >> 0)
 #define CLKMGR_SDRPLLGRP_DDRDQSCLK_CNT_MASK 0x000001ff
 #define CLKMGR_SDRPLLGRP_DDRDQSCLK_CNT_SET(x) (((x) << 0) & 0x000001ff)
 #define CLKMGR_SDRPLLGRP_DDRDQSCLK_PHASE_MASK 0x001ffe00
diff --git a/include/configs/socfpga_cyclone5.h b/include/configs/socfpga_cyclone5.h
index 5d145cd..3b6cfb4 100644
--- a/include/configs/socfpga_cyclone5.h
+++ b/include/configs/socfpga_cyclone5.h
@@ -24,6 +24,7 @@
 #define CONFIG_MISC_INIT_R
 #define CONFIG_SINGLE_BOOTLOADER
 #define CONFIG_SOCFPGA
+#define CONFIG_CLOCKS
 
 /* base address for .text section */
 #ifdef CONFIG_SOCFPGA_VIRTUAL_TARGET
-- 
2.1.0



More information about the U-Boot mailing list