All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [PATCH 0/6] tegra2: Add more clock/pinmux functionality
@ 2011-09-08 22:11 Simon Glass
  2011-09-08 22:11 ` [U-Boot] [PATCH 1/6] tegra2: Rename CLOCK_PLL_ID to CLOCK_ID Simon Glass
                   ` (7 more replies)
  0 siblings, 8 replies; 10+ messages in thread
From: Simon Glass @ 2011-09-08 22:11 UTC (permalink / raw)
  To: u-boot

This adds to the basic clock functionality already available. The concept
of a peripheral ID is introduced, and all peripheral clock access is done
using this ID.

Functions are provided to start, query and adjust peripheral clocks,
including automatic selection of the best available clock based on the
requested rate (this replaces hard-coded divisors).

On the pinmux side we can now select functions for pin groups using the
new pinmux_set_func() function.

Expanded functions are provided to adjust and query PLL clocks.

With a full compliment of clock and pinmux functions, it should no longer
be necessary for board/driver code to directly access clock registers. This
change removes all such accesses.

This functionality will be used for I2C, SPI, LCD, USB, keyboard, NAND and
other drivers for Tegra2.

At then end is a patch to enable MMC on Seaboard, to make it all worthwhile.

Note: These patches include a definition of assert() which I will happily
remove if the one sent upstream is accepted.


Simon Glass (6):
  tegra2: Rename CLOCK_PLL_ID to CLOCK_ID
  tegra2: Clean up board code a little
  tegra2: Add more clock functions
  tegra2: Rename PIN_ to PINGRP_
  tegra2: Add more pinmux functions
  tegra2: Enable MMC for Seaboard

 arch/arm/cpu/armv7/tegra2/ap20.c           |    5 +-
 arch/arm/cpu/armv7/tegra2/clock.c          |  840 +++++++++++++++++++++++++++-
 arch/arm/cpu/armv7/tegra2/pinmux.c         |  540 ++++++++++++++++++-
 arch/arm/include/asm/arch-tegra2/clk_rst.h |   84 +--
 arch/arm/include/asm/arch-tegra2/clock.h   |  149 ++++-
 arch/arm/include/asm/arch-tegra2/pinmux.h  |  444 ++++++++++-----
 board/nvidia/common/board.c                |  243 +++------
 board/nvidia/common/board.h                |    4 +-
 board/nvidia/harmony/harmony.c             |   26 +
 board/nvidia/seaboard/seaboard.c           |   35 ++-
 drivers/mmc/tegra2_mmc.c                   |   94 +--
 drivers/mmc/tegra2_mmc.h                   |    1 +
 12 files changed, 1972 insertions(+), 493 deletions(-)

-- 
1.7.3.1

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [U-Boot] [PATCH 1/6] tegra2: Rename CLOCK_PLL_ID to CLOCK_ID
  2011-09-08 22:11 [U-Boot] [PATCH 0/6] tegra2: Add more clock/pinmux functionality Simon Glass
@ 2011-09-08 22:11 ` Simon Glass
  2011-09-08 22:11 ` [U-Boot] [PATCH 2/6] tegra2: Clean up board code a little Simon Glass
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Simon Glass @ 2011-09-08 22:11 UTC (permalink / raw)
  To: u-boot

Rename CLOCK_PLL_ID to CLOCK_ID which takes account of the fact that the
code now deals with both PLL clocks and source clocks.

This also tidied up the assert() to match the one sent upstream, and fixes
an error in the PWM id.

Signed-off-by: Simon Glass <sjg@chromium.org>
---
 arch/arm/cpu/armv7/tegra2/ap20.c         |    2 +-
 arch/arm/cpu/armv7/tegra2/clock.c        |   21 +++++++++-----
 arch/arm/include/asm/arch-tegra2/clock.h |   43 ++++++++++++++---------------
 board/nvidia/common/board.c              |    2 +-
 4 files changed, 36 insertions(+), 32 deletions(-)

diff --git a/arch/arm/cpu/armv7/tegra2/ap20.c b/arch/arm/cpu/armv7/tegra2/ap20.c
index dc5f984..64d4c69 100644
--- a/arch/arm/cpu/armv7/tegra2/ap20.c
+++ b/arch/arm/cpu/armv7/tegra2/ap20.c
@@ -36,7 +36,7 @@ u32 s_first_boot = 1;
 void init_pllx(void)
 {
 	struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
-	struct clk_pll *pll = &clkrst->crc_pll[CLOCK_PLL_ID_XCPU];
+	struct clk_pll *pll = &clkrst->crc_pll[CLOCK_ID_XCPU];
 	u32 reg;
 
 	/* If PLLX is already enabled, just return */
diff --git a/arch/arm/cpu/armv7/tegra2/clock.c b/arch/arm/cpu/armv7/tegra2/clock.c
index 67eed14..8575e26 100644
--- a/arch/arm/cpu/armv7/tegra2/clock.c
+++ b/arch/arm/cpu/armv7/tegra2/clock.c
@@ -28,14 +28,19 @@
 #include <asm/arch/tegra2.h>
 #include <common.h>
 
-#ifdef DEBUG
-#define assert(x)	\
-	({ if (!(x)) printf("Assertion failure '%s' %s line %d\n", \
-		#x, __FILE__, __LINE__); })
+#if defined(DEBUG)
+#define DEBUG_ASSERT 1
 #else
-#define assert(x)
+#define DEBUG_ASSERT 0
 #endif
 
+#define assert(x)							\
+	({								\
+		if (DEBUG_ASSERT && !(x))				\
+			printf("Assertion failure '%s' %s line %d\n",	\
+			       #x, __FILE__, __LINE__);			\
+	})
+
 /*
  * Get the oscillator frequency, from the corresponding hardware configuration
  * field.
@@ -50,7 +55,7 @@ enum clock_osc_freq clock_get_osc_freq(void)
 	return (reg & OSC_FREQ_MASK) >> OSC_FREQ_SHIFT;
 }
 
-unsigned long clock_start_pll(enum clock_pll_id clkid, u32 divm, u32 divn,
+unsigned long clock_start_pll(enum clock_id clkid, u32 divm, u32 divn,
 		u32 divp, u32 cpcon, u32 lfcon)
 {
 	struct clk_rst_ctlr *clkrst =
@@ -58,7 +63,7 @@ unsigned long clock_start_pll(enum clock_pll_id clkid, u32 divm, u32 divn,
 	u32 data;
 	struct clk_pll *pll;
 
-	assert(clock_pll_id_isvalid(clkid));
+	assert(clock_id_isvalid(clkid));
 	pll = &clkrst->crc_pll[clkid];
 
 	/*
@@ -74,7 +79,7 @@ unsigned long clock_start_pll(enum clock_pll_id clkid, u32 divm, u32 divn,
 	data = (divm << PLL_DIVM_SHIFT) | (divn << PLL_DIVN_SHIFT) |
 			(0 << PLL_BYPASS_SHIFT) | (1 << PLL_ENABLE_SHIFT);
 
-	if (clkid == CLOCK_PLL_ID_USB)
+	if (clkid == CLOCK_ID_USB)
 		data |= divp << PLLU_VCO_FREQ_SHIFT;
 	else
 		data |= divp << PLL_DIVP_SHIFT;
diff --git a/arch/arm/include/asm/arch-tegra2/clock.h b/arch/arm/include/asm/arch-tegra2/clock.h
index d01aec8..8adb23c 100644
--- a/arch/arm/include/asm/arch-tegra2/clock.h
+++ b/arch/arm/include/asm/arch-tegra2/clock.h
@@ -22,7 +22,7 @@
 /* Tegra2 clock control functions */
 
 #ifndef _CLOCK_H
-
+#define _CLOCK_H
 
 /* Set of oscillator frequencies supported in the internal API. */
 enum clock_osc_freq {
@@ -36,22 +36,22 @@ enum clock_osc_freq {
 };
 
 /* The PLLs supported by the hardware */
-enum clock_pll_id {
-	CLOCK_PLL_ID_FIRST,
-	CLOCK_PLL_ID_CGENERAL = CLOCK_PLL_ID_FIRST,
-	CLOCK_PLL_ID_MEMORY,
-	CLOCK_PLL_ID_PERIPH,
-	CLOCK_PLL_ID_AUDIO,
-	CLOCK_PLL_ID_USB,
-	CLOCK_PLL_ID_DISPLAY,
+enum clock_id {
+	CLOCK_ID_FIRST,
+	CLOCK_ID_CGENERAL = CLOCK_ID_FIRST,
+	CLOCK_ID_MEMORY,
+	CLOCK_ID_PERIPH,
+	CLOCK_ID_AUDIO,
+	CLOCK_ID_USB,
+	CLOCK_ID_DISPLAY,
 
 	/* now the simple ones */
-	CLOCK_PLL_ID_FIRST_SIMPLE,
-	CLOCK_PLL_ID_XCPU = CLOCK_PLL_ID_FIRST_SIMPLE,
-	CLOCK_PLL_ID_EPCI,
-	CLOCK_PLL_ID_SFROM32KHZ,
+	CLOCK_ID_FIRST_SIMPLE,
+	CLOCK_ID_XCPU = CLOCK_ID_FIRST_SIMPLE,
+	CLOCK_ID_EPCI,
+	CLOCK_ID_SFROM32KHZ,
 
-	CLOCK_PLL_ID_COUNT,
+	CLOCK_ID_COUNT,
 };
 
 /* The clocks supported by the hardware */
@@ -80,7 +80,7 @@ enum periph_id {
 
 	/* 16 */
 	PERIPH_ID_TWC,
-	PERIPH_ID_PWC,
+	PERIPH_ID_PWM,
 	PERIPH_ID_I2S2,
 	PERIPH_ID_EPP,
 	PERIPH_ID_VI,
@@ -181,8 +181,7 @@ enum periph_id {
 #define PERIPH_MASK(id) (1 << ((id) & 0x1f))
 
 /* return 1 if a PLL ID is in range */
-#define clock_pll_id_isvalid(id) ((id) >= CLOCK_PLL_ID_FIRST && \
-		(id) < CLOCK_PLL_ID_COUNT)
+#define clock_id_isvalid(id) ((id) >= CLOCK_ID_FIRST && (id) < CLOCK_ID_COUNT)
 
 /* return 1 if a peripheral ID is in range */
 #define clock_periph_id_isvalid(id) ((id) >= PERIPH_ID_FIRST && \
@@ -194,7 +193,7 @@ enum periph_id {
 /* return the current oscillator clock frequency */
 enum clock_osc_freq clock_get_osc_freq(void);
 
-/*
+/**
  * Start PLL using the provided configuration parameters.
  *
  * @param id	clock id
@@ -206,7 +205,7 @@ enum clock_osc_freq clock_get_osc_freq(void);
  *
  * @returns monotonic time in us that the PLL will be stable
  */
-unsigned long clock_start_pll(enum clock_pll_id id, u32 divm, u32 divn,
+unsigned long clock_start_pll(enum clock_id id, u32 divm, u32 divn,
 		u32 divp, u32 cpcon, u32 lfcon);
 
 /*
@@ -224,7 +223,7 @@ void clock_enable(enum periph_id clkid);
  */
 void clock_set_enable(enum periph_id clkid, int enable);
 
-/*
+/**
  * Reset a peripheral. This puts it in reset, waits for a delay, then takes
  * it out of reset and waits for th delay again.
  *
@@ -233,7 +232,7 @@ void clock_set_enable(enum periph_id clkid, int enable);
  */
 void reset_periph(enum periph_id periph_id, int us_delay);
 
-/*
+/**
  * Put a peripheral into or out of reset.
  *
  * @param periph_id	peripheral to reset
@@ -251,7 +250,7 @@ enum crc_reset_id {
 	crc_rst_debug = 1 << 4,
 };
 
-/*
+/**
  * Put parts of the CPU complex into or out of reset.\
  *
  * @param cpu		cpu number (0 or 1 on Tegra2)
diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c
index 160dac8..32d3cfb 100644
--- a/board/nvidia/common/board.c
+++ b/board/nvidia/common/board.c
@@ -77,7 +77,7 @@ int timer_init(void)
 static void clock_init_uart(void)
 {
 	struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
-	struct clk_pll *pll = &clkrst->crc_pll[CLOCK_PLL_ID_PERIPH];
+	struct clk_pll *pll = &clkrst->crc_pll[CLOCK_ID_PERIPH];
 	u32 reg;
 
 	reg = readl(&pll->pll_base);
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [U-Boot] [PATCH 2/6] tegra2: Clean up board code a little
  2011-09-08 22:11 [U-Boot] [PATCH 0/6] tegra2: Add more clock/pinmux functionality Simon Glass
  2011-09-08 22:11 ` [U-Boot] [PATCH 1/6] tegra2: Rename CLOCK_PLL_ID to CLOCK_ID Simon Glass
@ 2011-09-08 22:11 ` Simon Glass
  2011-09-08 22:12 ` [U-Boot] [PATCH 3/6] tegra2: Add more clock functions Simon Glass
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Simon Glass @ 2011-09-08 22:11 UTC (permalink / raw)
  To: u-boot

This removes clock_init() and pinmux_init() which are names better suited
to those respective modules. By moving board_init_f() to the bottom of the
file we can remove the need for so many functions in the board.h header file.

The only clock/pinmux/gpio init we need to do prior to relocation is
for the UART.

Signed-off-by: Simon Glass <sjg@chromium.org>
---
 board/nvidia/common/board.c |   65 +++++++++++++-----------------------------
 board/nvidia/common/board.h |    3 --
 2 files changed, 20 insertions(+), 48 deletions(-)

diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c
index 32d3cfb..5e28b4c 100644
--- a/board/nvidia/common/board.c
+++ b/board/nvidia/common/board.c
@@ -43,24 +43,6 @@ const struct tegra2_sysinfo sysinfo = {
 	CONFIG_TEGRA2_BOARD_STRING
 };
 
-#ifdef CONFIG_BOARD_EARLY_INIT_F
-int board_early_init_f(void)
-{
-	/* Initialize periph clocks */
-	clock_init();
-
-	/* Initialize periph pinmuxes */
-	pinmux_init();
-
-	/* Initialize periph GPIOs */
-	gpio_init();
-
-	/* Init UART, scratch regs, and start CPU */
-	tegra2_start();
-	return 0;
-}
-#endif	/* EARLY_INIT */
-
 /*
  * Routine: timer_init
  * Description: init the timestamp and lastinc value
@@ -155,6 +137,7 @@ static void pin_mux_uart(void)
 #endif	/* CONFIG_TEGRA2_ENABLE_UARTD */
 }
 
+#ifdef CONFIG_TEGRA2_MMC
 /*
  * Routine: clock_init_mmc
  * Description: init the PLL and clocks for the SDMMC controllers
@@ -235,33 +218,7 @@ static void pin_mux_mmc(void)
 	pinmux_tristate_disable(PIN_SDD);
 	pinmux_tristate_disable(PIN_SDB);
 }
-
-/*
- * Routine: clock_init
- * Description: Do individual peripheral clock reset/enables
- */
-void clock_init(void)
-{
-	clock_init_uart();
-}
-
-/*
- * Routine: pinmux_init
- * Description: Do individual peripheral pinmux configs
- */
-void pinmux_init(void)
-{
-	pin_mux_uart();
-}
-
-/*
- * Routine: gpio_init
- * Description: Do individual peripheral GPIO configs
- */
-void gpio_init(void)
-{
-	gpio_config_uart();
-}
+#endif
 
 /*
  * Routine: board_init
@@ -307,3 +264,21 @@ int board_mmc_getcd(u8 *cd, struct mmc *mmc)
 	return 0;
 }
 #endif
+
+#ifdef CONFIG_BOARD_EARLY_INIT_F
+int board_early_init_f(void)
+{
+	/* Initialize UART clocks */
+	clock_init_uart();
+
+	/* Initialize periph pinmuxes */
+	pin_mux_uart();
+
+	/* Initialize periph GPIOs */
+	gpio_config_uart();
+
+	/* Init UART, scratch regs, and start CPU */
+	tegra2_start();
+	return 0;
+}
+#endif	/* EARLY_INIT */
diff --git a/board/nvidia/common/board.h b/board/nvidia/common/board.h
index 4334c02..d649eb7 100644
--- a/board/nvidia/common/board.h
+++ b/board/nvidia/common/board.h
@@ -25,9 +25,6 @@
 #define _BOARD_H_
 
 void tegra2_start(void);
-void clock_init(void);
-void pinmux_init(void);
-void gpio_init(void);
 void gpio_config_uart(void);
 int tegra2_mmc_init(int dev_index, int bus_width);
 
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [U-Boot] [PATCH 3/6] tegra2: Add more clock functions
  2011-09-08 22:11 [U-Boot] [PATCH 0/6] tegra2: Add more clock/pinmux functionality Simon Glass
  2011-09-08 22:11 ` [U-Boot] [PATCH 1/6] tegra2: Rename CLOCK_PLL_ID to CLOCK_ID Simon Glass
  2011-09-08 22:11 ` [U-Boot] [PATCH 2/6] tegra2: Clean up board code a little Simon Glass
@ 2011-09-08 22:12 ` Simon Glass
  2011-09-08 22:12 ` [U-Boot] [PATCH 4/6] tegra2: Rename PIN_ to PINGRP_ Simon Glass
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Simon Glass @ 2011-09-08 22:12 UTC (permalink / raw)
  To: u-boot

This adds most of the clock functions required by board and driver code:

-query and adjust peripheral clocks
-query and adjust PLLs
-reset and enable control

These functions are plumbed in as required.

Signed-off-by: Simon Glass <sjg@chromium.org>
---
 arch/arm/cpu/armv7/tegra2/ap20.c           |    3 +-
 arch/arm/cpu/armv7/tegra2/clock.c          |  821 +++++++++++++++++++++++++++-
 arch/arm/include/asm/arch-tegra2/clk_rst.h |   84 +--
 arch/arm/include/asm/arch-tegra2/clock.h   |  108 ++++-
 board/nvidia/common/board.c                |  105 +---
 drivers/mmc/tegra2_mmc.c                   |   92 +--
 drivers/mmc/tegra2_mmc.h                   |    1 +
 7 files changed, 999 insertions(+), 215 deletions(-)

diff --git a/arch/arm/cpu/armv7/tegra2/ap20.c b/arch/arm/cpu/armv7/tegra2/ap20.c
index 64d4c69..5cb4b1b 100644
--- a/arch/arm/cpu/armv7/tegra2/ap20.c
+++ b/arch/arm/cpu/armv7/tegra2/ap20.c
@@ -189,7 +189,6 @@ static void reset_A9_cpu(int reset)
 
 static void clock_enable_coresight(int enable)
 {
-	struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
 	u32 rst, src;
 
 	clock_set_enable(PERIPH_ID_CORESIGHT, enable);
@@ -203,7 +202,7 @@ static void clock_enable_coresight(int enable)
 		 *  (bits 7:0), so 00000001b == 1.5 (n+1 + .5)
 		 */
 		src = CLK_DIVIDER(NVBL_PLLP_KHZ, 144000);
-		writel(src, &clkrst->crc_clk_src_csite);
+		clock_ll_set_source_divisor(PERIPH_ID_CSI, 0, src);
 
 		/* Unlock the CPU CoreSight interfaces */
 		rst = 0xC5ACCE55;
diff --git a/arch/arm/cpu/armv7/tegra2/clock.c b/arch/arm/cpu/armv7/tegra2/clock.c
index 8575e26..f8bedee 100644
--- a/arch/arm/cpu/armv7/tegra2/clock.c
+++ b/arch/arm/cpu/armv7/tegra2/clock.c
@@ -27,6 +27,7 @@
 #include <asm/arch/timer.h>
 #include <asm/arch/tegra2.h>
 #include <common.h>
+#include <div64.h>
 
 #if defined(DEBUG)
 #define DEBUG_ASSERT 1
@@ -42,6 +43,370 @@
 	})
 
 /*
+ * This is our record of the current clock rate of each clock. We don't
+ * fill all of these in since we are only really interested in clocks which
+ * we use as parents.
+ */
+static unsigned pll_rate[CLOCK_ID_COUNT];
+
+/*
+ * The oscillator frequency is fixed to one of four set values. Based on this
+ * the other clocks are set up appropriately.
+ */
+static unsigned osc_freq[CLOCK_OSC_FREQ_COUNT] = {
+	13000000,
+	19200000,
+	12000000,
+	26000000,
+};
+
+/*
+ * Clock types that we can use as a source. The Tegra2 has muxes for the
+ * peripheral clocks, and in most cases there are four options for the clock
+ * source. This gives us a clock 'type' and exploits what commonality exists
+ * in the device.
+ *
+ * Letters are obvious, except for T which means CLK_M, and S which means the
+ * clock derived from 32KHz. Beware that CLK_M (also called OSC in the
+ * datasheet) and PLL_M are different things. The former is the basic
+ * clock supplied to the SOC from an external oscillator. The latter is the
+ * memory clock PLL.
+ *
+ * See definitions in clock_id in the header file.
+ */
+enum clock_type_id {
+	CLOCK_TYPE_AXPT,	/* PLL_A, PLL_X, PLL_P, CLK_M */
+	CLOCK_TYPE_MCPA,	/* and so on */
+	CLOCK_TYPE_MCPT,
+	CLOCK_TYPE_PCM,
+	CLOCK_TYPE_PCMT,
+	CLOCK_TYPE_PCXTS,
+	CLOCK_TYPE_PDCT,
+
+	CLOCK_TYPE_COUNT,
+	CLOCK_TYPE_NONE = -1,	/* invalid clock type */
+};
+
+/* return 1 if a peripheral ID is in range */
+#define clock_type_id_isvalid(id) ((id) >= 0 && \
+		(id) < CLOCK_TYPE_COUNT)
+
+char pllp_valid = 1;	/* PLLP is set up correctly */
+
+enum {
+	CLOCK_MAX_MUX	= 4	/* number of source options for each clock */
+};
+
+/*
+ * Clock source mux for each clock type. This just converts our enum into
+ * a list of mux sources for use by the code. Note that CLOCK_TYPE_PCXTS
+ * is special as it has 5 sources. Since it also has a different number of
+ * bits in its register for the source, we just handle it with a special
+ * case in the code.
+ */
+#define CLK(x) CLOCK_ID_ ## x
+static enum clock_id clock_source[CLOCK_TYPE_COUNT][CLOCK_MAX_MUX] = {
+	{ CLK(AUDIO),	CLK(XCPU),	CLK(PERIPH),	CLK(OSC)	},
+	{ CLK(MEMORY),	CLK(CGENERAL),	CLK(PERIPH),	CLK(AUDIO)	},
+	{ CLK(MEMORY),	CLK(CGENERAL),	CLK(PERIPH),	CLK(OSC)	},
+	{ CLK(PERIPH),	CLK(CGENERAL),	CLK(MEMORY),	CLK(NONE)	},
+	{ CLK(PERIPH),	CLK(CGENERAL),	CLK(MEMORY),	CLK(OSC)	},
+	{ CLK(PERIPH),	CLK(CGENERAL),	CLK(XCPU),	CLK(OSC)	},
+	{ CLK(PERIPH),	CLK(DISPLAY),	CLK(CGENERAL),	CLK(OSC)	},
+};
+
+/*
+ * Clock peripheral IDs which sadly don't match up with PERIPH_ID. This is
+ * not in the header file since it is for purely internal use - we want
+ * callers to use the PERIPH_ID for all access to peripheral clocks to avoid
+ * confusion bewteen PERIPH_ID_... and PERIPHC_...
+ *
+ * We don't call this CLOCK_PERIPH_ID or PERIPH_CLOCK_ID as it would just be
+ * confusing.
+ *
+ * Note to SOC vendors: perhaps define a unified numbering for peripherals and
+ * use it for reset, clock enable, clock source/divider and even pinmuxing
+ * if you can.
+ */
+enum periphc_internal_id {
+	/* 0x00 */
+	PERIPHC_I2S1,
+	PERIPHC_I2S2,
+	PERIPHC_SPDIF_OUT,
+	PERIPHC_SPDIF_IN,
+	PERIPHC_PWM,
+	PERIPHC_SPI1,
+	PERIPHC_SPI2,
+	PERIPHC_SPI3,
+
+	/* 0x08 */
+	PERIPHC_XIO,
+	PERIPHC_I2C1,
+	PERIPHC_DVC_I2C,
+	PERIPHC_TWC,
+	PERIPHC_0c,
+	PERIPHC_10,	/* PERIPHC_SPI1, what is this really? */
+	PERIPHC_DISP1,
+	PERIPHC_DISP2,
+
+	/* 0x10 */
+	PERIPHC_CVE,
+	PERIPHC_IDE0,
+	PERIPHC_VI,
+	PERIPHC_1c,
+	PERIPHC_SDMMC1,
+	PERIPHC_SDMMC2,
+	PERIPHC_G3D,
+	PERIPHC_G2D,
+
+	/* 0x18 */
+	PERIPHC_NDFLASH,
+	PERIPHC_SDMMC4,
+	PERIPHC_VFIR,
+	PERIPHC_EPP,
+	PERIPHC_MPE,
+	PERIPHC_MIPI,
+	PERIPHC_UART1,
+	PERIPHC_UART2,
+
+	/* 0x20 */
+	PERIPHC_HOST1X,
+	PERIPHC_21,
+	PERIPHC_TVO,
+	PERIPHC_HDMI,
+	PERIPHC_24,
+	PERIPHC_TVDAC,
+	PERIPHC_I2C2,
+	PERIPHC_EMC,
+
+	/* 0x28 */
+	PERIPHC_UART3,
+	PERIPHC_29,
+	PERIPHC_VI_SENSOR,
+	PERIPHC_2b,
+	PERIPHC_2c,
+	PERIPHC_SPI4,
+	PERIPHC_I2C3,
+	PERIPHC_SDMMC3,
+
+	/* 0x30 */
+	PERIPHC_UART4,
+	PERIPHC_UART5,
+	PERIPHC_VDE,
+	PERIPHC_OWR,
+	PERIPHC_NOR,
+	PERIPHC_CSITE,
+
+	PERIPHC_COUNT,
+
+	PERIPHC_NONE = -1,
+};
+
+/* return 1 if a periphc_internal_id is in range */
+#define periphc_internal_id_isvalid(id) ((id) >= 0 && \
+		(id) < PERIPHC_COUNT)
+
+/*
+ * Clock type for each peripheral clock source. We put the name in each
+ * record just so it is easy to match things up
+ */
+#define TYPE(name, type) type
+static enum clock_type_id clock_periph_type[PERIPHC_COUNT] = {
+	/* 0x00 */
+	TYPE(PERIPHC_I2S1,	CLOCK_TYPE_AXPT),
+	TYPE(PERIPHC_I2S2,	CLOCK_TYPE_AXPT),
+	TYPE(PERIPHC_SPDIF_OUT,	CLOCK_TYPE_AXPT),
+	TYPE(PERIPHC_SPDIF_IN,	CLOCK_TYPE_PCM),
+	TYPE(PERIPHC_PWM,	CLOCK_TYPE_PCXTS),
+	TYPE(PERIPHC_SPI1,	CLOCK_TYPE_PCMT),
+	TYPE(PERIPHC_SPI22,	CLOCK_TYPE_PCMT),
+	TYPE(PERIPHC_SPI3,	CLOCK_TYPE_PCMT),
+
+	/* 0x08 */
+	TYPE(PERIPHC_XIO,	CLOCK_TYPE_PCMT),
+	TYPE(PERIPHC_I2C1,	CLOCK_TYPE_PCMT),
+	TYPE(PERIPHC_DVC_I2C,	CLOCK_TYPE_PCMT),
+	TYPE(PERIPHC_TWC,	CLOCK_TYPE_PCMT),
+	TYPE(PERIPHC_NONE,	CLOCK_TYPE_NONE),
+	TYPE(PERIPHC_SPI1,	CLOCK_TYPE_PCMT),
+	TYPE(PERIPHC_DISP1,	CLOCK_TYPE_PDCT),
+	TYPE(PERIPHC_DISP2,	CLOCK_TYPE_PDCT),
+
+	/* 0x10 */
+	TYPE(PERIPHC_CVE,	CLOCK_TYPE_PDCT),
+	TYPE(PERIPHC_IDE0,	CLOCK_TYPE_PCMT),
+	TYPE(PERIPHC_VI,	CLOCK_TYPE_MCPA),
+	TYPE(PERIPHC_NONE,	CLOCK_TYPE_NONE),
+	TYPE(PERIPHC_SDMMC1,	CLOCK_TYPE_PCMT),
+	TYPE(PERIPHC_SDMMC2,	CLOCK_TYPE_PCMT),
+	TYPE(PERIPHC_G3D,	CLOCK_TYPE_MCPA),
+	TYPE(PERIPHC_G2D,	CLOCK_TYPE_MCPA),
+
+	/* 0x18 */
+	TYPE(PERIPHC_NDFLASH,	CLOCK_TYPE_PCMT),
+	TYPE(PERIPHC_SDMMC4,	CLOCK_TYPE_PCMT),
+	TYPE(PERIPHC_VFIR,	CLOCK_TYPE_PCMT),
+	TYPE(PERIPHC_EPP,	CLOCK_TYPE_MCPA),
+	TYPE(PERIPHC_MPE,	CLOCK_TYPE_MCPA),
+	TYPE(PERIPHC_MIPI,	CLOCK_TYPE_PCMT),
+	TYPE(PERIPHC_UART1,	CLOCK_TYPE_PCMT),
+	TYPE(PERIPHC_UART2,	CLOCK_TYPE_PCMT),
+
+	/* 0x20 */
+	TYPE(PERIPHC_HOST1X,	CLOCK_TYPE_MCPA),
+	TYPE(PERIPHC_NONE,	CLOCK_TYPE_NONE),
+	TYPE(PERIPHC_TVO,	CLOCK_TYPE_PDCT),
+	TYPE(PERIPHC_HDMI,	CLOCK_TYPE_PDCT),
+	TYPE(PERIPHC_NONE,	CLOCK_TYPE_NONE),
+	TYPE(PERIPHC_TVDAC,	CLOCK_TYPE_PDCT),
+	TYPE(PERIPHC_I2C2,	CLOCK_TYPE_PCMT),
+	TYPE(PERIPHC_EMC,	CLOCK_TYPE_MCPT),
+
+	/* 0x28 */
+	TYPE(PERIPHC_UART3,	CLOCK_TYPE_PCMT),
+	TYPE(PERIPHC_NONE,	CLOCK_TYPE_NONE),
+	TYPE(PERIPHC_VI,	CLOCK_TYPE_MCPA),
+	TYPE(PERIPHC_NONE,	CLOCK_TYPE_NONE),
+	TYPE(PERIPHC_NONE,	CLOCK_TYPE_NONE),
+	TYPE(PERIPHC_SPI4,	CLOCK_TYPE_PCMT),
+	TYPE(PERIPHC_I2C3,	CLOCK_TYPE_PCMT),
+	TYPE(PERIPHC_SDMMC3,	CLOCK_TYPE_PCMT),
+
+	/* 0x30 */
+	TYPE(PERIPHC_UART4,	CLOCK_TYPE_PCMT),
+	TYPE(PERIPHC_UART5,	CLOCK_TYPE_PCMT),
+	TYPE(PERIPHC_VDE,	CLOCK_TYPE_PCMT),
+	TYPE(PERIPHC_OWR,	CLOCK_TYPE_PCMT),
+	TYPE(PERIPHC_NOR,	CLOCK_TYPE_PCMT),
+	TYPE(PERIPHC_CSITE,	CLOCK_TYPE_PCMT),
+};
+
+/*
+ * This array translates a periph_id to a periphc_internal_id
+ *
+ * Not present/matched up:
+ *	uint vi_sensor;	 _VI_SENSOR_0,		0x1A8
+ *	SPDIF - which is both 0x08 and 0x0c
+ *
+ */
+#define NONE(name) (-1)
+#define OFFSET(name, value) PERIPHC_ ## name
+static s8 periph_id_to_internal_id[PERIPH_ID_COUNT] = {
+	/* Low word: 31:0 */
+	NONE(CPU),
+	NONE(RESERVED1),
+	NONE(RESERVED2),
+	NONE(AC97),
+	NONE(RTC),
+	NONE(TMR),
+	PERIPHC_UART1,
+	PERIPHC_UART2,	/* and vfir 0x68 */
+
+	/* 0x08 */
+	NONE(GPIO),
+	PERIPHC_SDMMC2,
+	NONE(SPDIF),		/* 0x08 and 0x0c, unclear which to use */
+	PERIPHC_I2S1,
+	PERIPHC_I2C1,
+	PERIPHC_NDFLASH,
+	PERIPHC_SDMMC1,
+	PERIPHC_SDMMC4,
+
+	/* 0x10 */
+	PERIPHC_TWC,
+	PERIPHC_PWM,
+	PERIPHC_I2S2,
+	PERIPHC_EPP,
+	PERIPHC_VI,
+	PERIPHC_G2D,
+	NONE(USBD),
+	NONE(ISP),
+
+	/* 0x18 */
+	PERIPHC_G3D,
+	PERIPHC_IDE0,
+	PERIPHC_DISP2,
+	PERIPHC_DISP1,
+	PERIPHC_HOST1X,
+	NONE(VCP),
+	NONE(RESERVED30),
+	NONE(CACHE2),
+
+	/* Middle word: 63:32 */
+	NONE(MEM),
+	NONE(AHBDMA),
+	NONE(APBDMA),
+	NONE(RESERVED35),
+	NONE(KBC),
+	NONE(STAT_MON),
+	NONE(PMC),
+	NONE(FUSE),
+
+	/* 0x28 */
+	NONE(KFUSE),
+	NONE(SBC1),	/* SBC1, 0x34, is this SPI1? */
+	PERIPHC_NOR,
+	PERIPHC_SPI1,
+	PERIPHC_SPI2,
+	PERIPHC_XIO,
+	PERIPHC_SPI3,
+	PERIPHC_DVC_I2C,
+
+	/* 0x30 */
+	NONE(DSI),
+	PERIPHC_TVO,	/* also CVE 0x40 */
+	PERIPHC_MIPI,
+	PERIPHC_HDMI,
+	PERIPHC_CSITE,
+	PERIPHC_TVDAC,
+	PERIPHC_I2C2,
+	PERIPHC_UART3,
+
+	/* 0x38 */
+	NONE(RESERVED56),
+	PERIPHC_EMC,
+	NONE(USB2),
+	NONE(USB3),
+	PERIPHC_MPE,
+	PERIPHC_VDE,
+	NONE(BSEA),
+	NONE(BSEV),
+
+	/* Upper word 95:64 */
+	NONE(SPEEDO),
+	PERIPHC_UART4,
+	PERIPHC_UART5,
+	PERIPHC_I2C3,
+	PERIPHC_SPI4,
+	PERIPHC_SDMMC3,
+	NONE(PCIE),
+	PERIPHC_OWR,
+
+	/* 0x48 */
+	NONE(AFI),
+	NONE(CORESIGHT),
+	NONE(RESERVED74),
+	NONE(AVPUCQ),
+	NONE(RESERVED76),
+	NONE(RESERVED77),
+	NONE(RESERVED78),
+	NONE(RESERVED79),
+
+	/* 0x50 */
+	NONE(RESERVED80),
+	NONE(RESERVED81),
+	NONE(RESERVED82),
+	NONE(RESERVED83),
+	NONE(IRAMA),
+	NONE(IRAMB),
+	NONE(IRAMC),
+	NONE(IRAMD),
+
+	/* 0x58 */
+	NONE(CRAM2),
+};
+
+/*
  * Get the oscillator frequency, from the corresponding hardware configuration
  * field.
  */
@@ -55,16 +420,21 @@ enum clock_osc_freq clock_get_osc_freq(void)
 	return (reg & OSC_FREQ_MASK) >> OSC_FREQ_SHIFT;
 }
 
-unsigned long clock_start_pll(enum clock_id clkid, u32 divm, u32 divn,
-		u32 divp, u32 cpcon, u32 lfcon)
+/* Returns a pointer to the registers of the given pll */
+static struct clk_pll *get_pll(enum clock_id clkid)
 {
 	struct clk_rst_ctlr *clkrst =
 			(struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
-	u32 data;
-	struct clk_pll *pll;
 
 	assert(clock_id_isvalid(clkid));
-	pll = &clkrst->crc_pll[clkid];
+	return &clkrst->crc_pll[clkid];
+}
+
+unsigned long clock_start_pll(enum clock_id clkid, u32 divm, u32 divn,
+		u32 divp, u32 cpcon, u32 lfcon)
+{
+	struct clk_pll *pll = get_pll(clkid);
+	u32 data;
 
 	/*
 	 * We cheat by treating all PLL (except PLLU) in the same fashion.
@@ -89,6 +459,294 @@ unsigned long clock_start_pll(enum clock_id clkid, u32 divm, u32 divn,
 	return timer_get_us() + CLOCK_PLL_STABLE_DELAY_US;
 }
 
+/* return 1 if a peripheral ID is in range and valid */
+static int clock_periph_id_isvalid(enum periph_id id)
+{
+	if (id < PERIPH_ID_FIRST || id >= PERIPH_ID_COUNT)
+		printf("Peripheral id %d out of range\n", id);
+	else {
+		switch (id) {
+		case PERIPH_ID_RESERVED1:
+		case PERIPH_ID_RESERVED2:
+		case PERIPH_ID_RESERVED30:
+		case PERIPH_ID_RESERVED35:
+		case PERIPH_ID_RESERVED56:
+		case PERIPH_ID_RESERVED74:
+		case PERIPH_ID_RESERVED76:
+		case PERIPH_ID_RESERVED77:
+		case PERIPH_ID_RESERVED78:
+		case PERIPH_ID_RESERVED79:
+		case PERIPH_ID_RESERVED80:
+		case PERIPH_ID_RESERVED81:
+		case PERIPH_ID_RESERVED82:
+		case PERIPH_ID_RESERVED83:
+			printf("Peripheral id %d is reserved\n", id);
+			break;
+		default:
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/* Returns a pointer to the clock source register for a peripheral */
+static u32 *get_periph_source_reg(enum periph_id periph_id)
+{
+	struct clk_rst_ctlr *clkrst =
+			(struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+	enum periphc_internal_id internal_id;
+
+	assert(clock_periph_id_isvalid(periph_id));
+	internal_id = periph_id_to_internal_id[periph_id];
+	assert(internal_id != -1);
+	return &clkrst->crc_clk_src[internal_id];
+}
+
+void clock_ll_set_source_divisor(enum periph_id periph_id, unsigned source,
+			      unsigned divisor)
+{
+	u32 *reg = get_periph_source_reg(periph_id);
+	u32 value;
+
+	value = readl(reg);
+
+	value &= ~OUT_CLK_SOURCE_MASK;
+	value |= source << OUT_CLK_SOURCE_SHIFT;
+
+	value &= ~OUT_CLK_DIVISOR_MASK;
+	value |= divisor << OUT_CLK_DIVISOR_SHIFT;
+
+	writel(value, reg);
+}
+
+void clock_ll_set_source(enum periph_id periph_id, unsigned source)
+{
+	u32 *reg = get_periph_source_reg(periph_id);
+
+	clrsetbits_le32(reg, OUT_CLK_SOURCE_MASK,
+			source << OUT_CLK_SOURCE_SHIFT);
+}
+
+/**
+ * Given the parent's rate and the required rate for the children, this works
+ * out the peripheral clock divider to use, in 7.1 binary format.
+ *
+ * @param parent_rate	clock rate of parent clock in Hz
+ * @param rate		required clock rate for this clock
+ * @return divider which should be used
+ */
+static int clk_div7_1_get_divider(unsigned long parent_rate,
+				  unsigned long rate)
+{
+	u64 divider = parent_rate * 2;
+
+	divider += rate - 1;
+	do_div(divider, rate);
+
+	if ((s64)divider - 2 < 0)
+		return 0;
+
+	if ((s64)divider - 2 > 255)
+		return -1;
+
+	return divider - 2;
+}
+
+/**
+ * Given the parent's rate and the divider in 7.1 format, this works out the
+ * resulting peripheral clock rate.
+ *
+ * @param parent_rate	clock rate of parent clock in Hz
+ * @param divider which should be used in 7.1 format
+ * @return effective clock rate of peripheral
+ */
+static unsigned long get_rate_from_divider(unsigned long parent_rate,
+					   int divider)
+{
+	u64 rate;
+
+	rate = (u64)parent_rate * 2;
+	do_div(rate, divider + 2);
+	return rate;
+}
+
+unsigned long clock_get_periph_rate(enum periph_id periph_id,
+		enum clock_id parent)
+{
+	u32 *reg = get_periph_source_reg(periph_id);
+
+	return get_rate_from_divider(pll_rate[parent],
+		(readl(reg) & OUT_CLK_DIVISOR_MASK) >> OUT_CLK_DIVISOR_SHIFT);
+}
+
+/**
+ * Find the best available 7.1 format divisor given a parent clock rate and
+ * required child clock rate. This function assumes that a second-stage
+ * divisor is available which can divide by powers of 2 from 1 to 256.
+ *
+ * @param parent_rate	clock rate of parent clock in Hz
+ * @param rate		required clock rate for this clock
+ * @param extra_div	value for the second-stage divisor (not set if this
+ *			function returns -1.
+ * @return divider which should be used, or -1 if nothing is valid
+ *
+ */
+static int find_best_divider(unsigned long parent_rate, unsigned long rate,
+		int *extra_div)
+{
+	int shift;
+	int best_divider = -1;
+	int best_error = rate;
+
+	/* try dividers from 1 to 256 and find closest match */
+	for (shift = 0; shift <= 8 && best_error > 0; shift++) {
+		unsigned divided_parent = parent_rate >> shift;
+		int divider = clk_div7_1_get_divider(divided_parent, rate);
+		unsigned effective_rate = get_rate_from_divider(divided_parent,
+						       divider);
+		int error = rate - effective_rate;
+
+		/* Given a valid divider, look for the lowest error */
+		if (divider != -1 && error < best_error) {
+			best_error = error;
+			*extra_div = 1 << shift;
+			best_divider = divider;
+		}
+	}
+
+	/* return what we found - *extra_div will already be set */
+	return best_divider;
+}
+
+/**
+ * Given a peripheral ID and the required source clock, this returns which
+ * value should be programmed into the source mux for that peripheral.
+ *
+ * There is special code here to handle the one source type with 5 sources.
+ *
+ * @param periph_id	peripheral to start
+ * @param source	PLL id of required parent clock
+ * @param mux_bits	Set to number of bits in mux register: 2 or 4
+ * @return mux value (0-4, or -1 if not found)
+ */
+static int get_periph_clock_source(enum periph_id periph_id,
+		enum clock_id parent, int *mux_bits)
+{
+	enum clock_type_id type;
+	enum periphc_internal_id internal_id;
+	int mux;
+
+	assert(clock_periph_id_isvalid(periph_id));
+
+	internal_id = periph_id_to_internal_id[periph_id];
+	assert(periphc_internal_id_isvalid(internal_id));
+
+	type = clock_periph_type[internal_id];
+	assert(clock_type_id_isvalid(type));
+
+	/* Special case here for the clock with a 4-bit source mux */
+	if (type == CLOCK_TYPE_PCXTS)
+		*mux_bits = 4;
+	else
+		*mux_bits = 2;
+
+	for (mux = 0; mux < CLOCK_MAX_MUX; mux++)
+		if (clock_source[type][mux] == parent)
+			return mux;
+
+	/*
+	 * Not found: it might be looking for the 'S' in CLOCK_TYPE_PCXTS
+	 * which is not in our table. If not, then they are asking for a
+	 * source which this peripheral can't access through its mux.
+	 */
+	assert(type == CLOCK_TYPE_PCXTS);
+	assert(parent == CLOCK_ID_SFROM32KHZ);
+	if (type == CLOCK_TYPE_PCXTS && parent == CLOCK_ID_SFROM32KHZ)
+		return 4;	/* mux value for this clock */
+
+	/* if we get here, either us or the caller has made a mistake */
+	printf("Caller requested bad clock: periph=%d, parent=%d\n", periph_id,
+		parent);
+	return -1;
+}
+
+/**
+ * Adjust peripheral PLL to use the given divider and source.
+ *
+ * @param periph_id	peripheral to adjust
+ * @param parent	Required parent clock (for source mux)
+ * @param divider	Required divider in 7.1 format
+ * @return 0 if ok, -1 on error (requesting a parent clock which is not valid
+ *		for this peripheral)
+ */
+static int adjust_periph_pll(enum periph_id periph_id,
+		enum clock_id parent, unsigned divider)
+{
+	u32 *reg = get_periph_source_reg(periph_id);
+	unsigned source;
+	int mux_bits;
+
+	clrsetbits_le32(reg, OUT_CLK_DIVISOR_MASK,
+			divider << OUT_CLK_DIVISOR_SHIFT);
+	udelay(1);
+
+	/* work out the source clock and set it */
+	source = get_periph_clock_source(periph_id, parent, &mux_bits);
+	if (source < 0)
+		return -1;
+	if (mux_bits == 4) {
+		clrsetbits_le32(reg, OUT_CLK_SOURCE4_MASK,
+			source << OUT_CLK_SOURCE4_SHIFT);
+	} else {
+		clrsetbits_le32(reg, OUT_CLK_SOURCE_MASK,
+			source << OUT_CLK_SOURCE_SHIFT);
+	}
+	udelay(2);
+	return 0;
+}
+
+unsigned clock_adjust_periph_pll_div(enum periph_id periph_id,
+		enum clock_id parent, unsigned rate, int *extra_div)
+{
+	unsigned effective_rate;
+	int divider;
+
+	if (extra_div)
+		divider = find_best_divider(pll_rate[parent], rate, extra_div);
+	else
+		divider = clk_div7_1_get_divider(pll_rate[parent], rate);
+	assert(divider >= 0);
+	if (adjust_periph_pll(periph_id, parent, divider))
+		return -1U;
+	debug("periph %d, rate=%d, reg=%p = %x\n", periph_id, rate,
+		get_periph_source_reg(periph_id),
+		readl(get_periph_source_reg(periph_id)));
+
+	/* Check what we ended up with. This shouldn't matter though */
+	effective_rate = clock_get_periph_rate(periph_id, parent);
+	if (extra_div)
+		effective_rate /= *extra_div;
+	if (rate != effective_rate)
+		debug("Requested clock rate %u not honored (got %u)\n",
+		       rate, effective_rate);
+	return effective_rate;
+}
+
+unsigned clock_start_periph_pll(enum periph_id periph_id,
+		enum clock_id parent, unsigned rate)
+{
+	unsigned effective_rate;
+
+	reset_set_enable(periph_id, 1);
+	clock_enable(periph_id);
+
+	effective_rate = clock_adjust_periph_pll_div(periph_id, parent, rate,
+						 NULL);
+
+	reset_set_enable(periph_id, 0);
+	return effective_rate;
+}
+
 void clock_set_enable(enum periph_id periph_id, int enable)
 {
 	struct clk_rst_ctlr *clkrst =
@@ -161,3 +819,156 @@ void reset_cmplx_set_enable(int cpu, int which, int reset)
 	else
 		writel(mask, &clkrst->crc_cpu_cmplx_clr);
 }
+
+unsigned clock_get_rate(enum clock_id clkid)
+{
+	struct clk_pll *pll;
+	u32 base;
+	u32 divm;
+	u64 parent_rate;
+	u64 rate;
+
+	parent_rate = osc_freq[clock_get_osc_freq()];
+	if (clkid == CLOCK_ID_OSC)
+		return parent_rate;
+
+	pll = get_pll(clkid);
+	base = readl(&pll->pll_base);
+
+	/* Oh for bf_unpack()... */
+	rate = parent_rate * ((base & PLL_DIVN_MASK) >> PLL_DIVN_SHIFT);
+	divm = (base & PLL_DIVM_MASK) >> PLL_DIVM_SHIFT;
+	if (clkid == CLOCK_ID_USB)
+		divm <<= (base & PLLU_VCO_FREQ_MASK) >> PLLU_VCO_FREQ_SHIFT;
+	else
+		divm <<= (base & PLL_DIVP_MASK) >> PLL_DIVP_SHIFT;
+	do_div(rate, divm);
+	return rate;
+}
+
+/**
+ * Set the output frequency you want for each PLL clock.
+ * PLL output frequencies are programmed by setting their N, M and P values.
+ * The governing equations are:
+ *     VCO = (Fi / m) * n, Fo = VCO / (2^p)
+ *     where Fo is the output frequency from the PLL.
+ * Example: Set the output frequency to 216Mhz(Fo) with 12Mhz OSC(Fi)
+ *     216Mhz = ((12Mhz / m) * n) / (2^p) so n=432,m=12,p=1
+ * Please see Tegra TRM section 5.3 to get the detail for PLL Programming
+ *
+ * @param n PLL feedback divider(DIVN)
+ * @param m PLL input divider(DIVN)
+ * @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
+ */
+static int clock_set_rate(enum clock_id clkid, u32 n, u32 m, u32 p, u32 cpcon)
+{
+	u32 base_reg;
+	u32 misc_reg;
+	struct clk_pll *pll;
+
+	pll = get_pll(clkid);
+
+	base_reg = readl(&pll->pll_base);
+
+	/* Set BYPASS, m, n and p to PLL_BASE */
+	base_reg &= ~PLL_DIVM_MASK;
+	base_reg |= m << PLL_DIVM_SHIFT;
+
+	base_reg &= ~PLL_DIVN_MASK;
+	base_reg |= n << PLL_DIVN_SHIFT;
+
+	base_reg &= ~PLL_DIVP_MASK;
+	base_reg |= p << PLL_DIVP_SHIFT;
+
+	if (clkid == CLOCK_ID_PERIPH) {
+		/*
+		 * If the PLL is already set up, check that it is correct
+		 * and record this info for clock_verify() to check.
+		 */
+		if (base_reg & PLL_BASE_OVRRIDE_MASK) {
+			base_reg |= PLL_ENABLE_MASK;
+			if (base_reg != readl(&pll->pll_base))
+				pllp_valid = 0;
+			return pllp_valid ? 1 : -1;
+		}
+		base_reg |= PLL_BASE_OVRRIDE_MASK;
+	}
+
+	base_reg |= PLL_BYPASS_MASK;
+	writel(base_reg, &pll->pll_base);
+
+	/* Set cpcon to PLL_MISC */
+	misc_reg = readl(&pll->pll_misc);
+	misc_reg &= ~PLL_CPCON_MASK;
+	misc_reg |= cpcon << PLL_CPCON_SHIFT;
+	writel(misc_reg, &pll->pll_misc);
+
+	/* Enable PLL */
+	base_reg |= PLL_ENABLE_MASK;
+	writel(base_reg, &pll->pll_base);
+
+	/* Disable BYPASS */
+	base_reg &= ~PLL_BYPASS_MASK;
+	writel(base_reg, &pll->pll_base);
+
+	return 0;
+}
+
+int clock_verify(void)
+{
+	struct clk_pll *pll = get_pll(CLOCK_ID_PERIPH);
+	u32 reg = readl(&pll->pll_base);
+
+	if (!pllp_valid) {
+		printf("Warning: PLLP %x is not correct\n", reg);
+		return -1;
+	}
+	debug("PLLX %x is correct\n", reg);
+	return 0;
+}
+
+void clock_early_init(void)
+{
+	/*
+	 * PLLP output frequency set to 216MHz
+	 * PLLC output frequency set to 600Mhz
+	 *
+	 * TODO: Can we calculate these values instead of hard-coding?
+	 */
+	switch (clock_get_osc_freq()) {
+	case CLOCK_OSC_FREQ_12_0: /* OSC is 12Mhz */
+		clock_set_rate(CLOCK_ID_PERIPH, 432, 12, 1, 8);
+		clock_set_rate(CLOCK_ID_CGENERAL, 600, 12, 0, 8);
+		break;
+
+	case CLOCK_OSC_FREQ_26_0: /* OSC is 26Mhz */
+		clock_set_rate(CLOCK_ID_PERIPH, 432, 26, 1, 8);
+		clock_set_rate(CLOCK_ID_CGENERAL, 600, 26, 0, 8);
+		break;
+
+	case CLOCK_OSC_FREQ_13_0:
+	case CLOCK_OSC_FREQ_19_2:
+	default:
+		/*
+		 * These are not supported. It is too early to print a
+		 * message and the UART likely won't work anyway due to the
+		 * oscillator being wrong.
+		 */
+		break;
+	}
+}
+
+void clock_init(void)
+{
+	pll_rate[CLOCK_ID_MEMORY] = clock_get_rate(CLOCK_ID_MEMORY);
+	pll_rate[CLOCK_ID_PERIPH] = clock_get_rate(CLOCK_ID_PERIPH);
+	pll_rate[CLOCK_ID_CGENERAL] = clock_get_rate(CLOCK_ID_CGENERAL);
+	pll_rate[CLOCK_ID_OSC] = clock_get_rate(CLOCK_ID_OSC);
+	pll_rate[CLOCK_ID_SFROM32KHZ] = 32768;
+	debug("Osc = %d\n", pll_rate[CLOCK_ID_OSC]);
+	debug("PLLM = %d\n", pll_rate[CLOCK_ID_MEMORY]);
+	debug("PLLP = %d\n", pll_rate[CLOCK_ID_PERIPH]);
+}
diff --git a/arch/arm/include/asm/arch-tegra2/clk_rst.h b/arch/arm/include/asm/arch-tegra2/clk_rst.h
index bd9d9ad..0b6e004 100644
--- a/arch/arm/include/asm/arch-tegra2/clk_rst.h
+++ b/arch/arm/include/asm/arch-tegra2/clk_rst.h
@@ -43,9 +43,12 @@ struct clk_pll_simple {
  * structure for which we use clk_pll_simple. The reason for this non-
  * othogonal setup is not stated.
  */
-#define TEGRA_CLK_PLLS		6
-#define TEGRA_CLK_SIMPLE_PLLS	3	/* Number of simple PLLs */
-#define TEGRA_CLK_REGS		3	/* Number of clock enable registers */
+enum {
+	TEGRA_CLK_PLLS		= 6,	/* Number of normal PLLs */
+	TEGRA_CLK_SIMPLE_PLLS	= 3,	/* Number of simple PLLs */
+	TEGRA_CLK_REGS		= 3,	/* Number of clock enable registers */
+	TEGRA_CLK_SOURCES	= 64,	/* Number of peripheral clock sources */
+};
 
 /* Clock/Reset Controller (CLK_RST_CONTROLLER_) regs */
 struct clk_rst_ctlr {
@@ -79,65 +82,10 @@ struct clk_rst_ctlr {
 	uint crc_reserved10;		/* _reserved_10,	0xF8 */
 	uint crc_reserved11;		/* _reserved_11,	0xFC */
 
-	uint crc_clk_src_i2s1;		/*_I2S1_0,		0x100 */
-	uint crc_clk_src_i2s2;		/*_I2S2_0,		0x104 */
-	uint crc_clk_src_spdif_out;	/*_SPDIF_OUT_0,		0x108 */
-	uint crc_clk_src_spdif_in;	/*_SPDIF_IN_0,		0x10C */
-	uint crc_clk_src_pwm;		/*_PWM_0,		0x110 */
-	uint crc_clk_src_spi1;		/*_SPI1_0,		0x114 */
-	uint crc_clk_src_sbc2;		/*_SBC2_0,		0x118 */
-	uint crc_clk_src_sbc3;		/*_SBC3_0,		0x11C */
-	uint crc_clk_src_xio;		/*_XIO_0,		0x120 */
-	uint crc_clk_src_i2c1;		/*_I2C1_0,		0x124 */
-	uint crc_clk_src_dvc_i2c;	/*_DVC_I2C_0,		0x128 */
-	uint crc_clk_src_twc;		/*_TWC_0,		0x12C */
-	uint crc_reserved12;		/*			0x130 */
-	uint crc_clk_src_sbc1;		/*_SBC1_0,		0x134 */
-	uint crc_clk_src_disp1;		/*_DISP1_0,		0x138 */
-	uint crc_clk_src_disp2;		/*_DISP2_0,		0x13C */
-	uint crc_clk_src_cve;		/*_CVE_0,		0x140 */
-	uint crc_clk_src_ide;		/*_IDE_0,		0x144 */
-	uint crc_clk_src_vi;		/*_VI_0,		0x148 */
-	uint crc_reserved13;		/*			0x14C */
-	uint crc_clk_src_sdmmc1;	/*_SDMMC1_0,		0x150 */
-	uint crc_clk_src_sdmmc2;	/*_SDMMC2_0,		0x154 */
-	uint crc_clk_src_g3d;		/*_G3D_0,		0x158 */
-	uint crc_clk_src_g2d;		/*_G2D_0,		0x15C */
-	uint crc_clk_src_ndflash;	/*_NDFLASH_0,		0x160 */
-	uint crc_clk_src_sdmmc4;	/*_SDMMC4_0,		0x164 */
-	uint crc_clk_src_vfir;		/*_VFIR_0,		0x168 */
-	uint crc_clk_src_epp;		/*_EPP_0,		0x16C */
-	uint crc_clk_src_mp3;		/*_MPE_0,		0x170 */
-	uint crc_clk_src_mipi;		/*_MIPI_0,		0x174 */
-	uint crc_clk_src_uarta;		/*_UARTA_0,		0x178 */
-	uint crc_clk_src_uartb;		/*_UARTB_0,		0x17C */
-	uint crc_clk_src_host1x;	/*_HOST1X_0,		0x180 */
-	uint crc_reserved14;		/*			0x184 */
-	uint crc_clk_src_tvo;		/*_TVO_0,		0x188 */
-	uint crc_clk_src_hdmi;		/*_HDMI_0,		0x18C */
-	uint crc_reserved15;		/*			0x190 */
-	uint crc_clk_src_tvdac;		/*_TVDAC_0,		0x194 */
-	uint crc_clk_src_i2c2;		/*_I2C2_0,		0x198 */
-	uint crc_clk_src_emc;		/*_EMC_0,		0x19C */
-	uint crc_clk_src_uartc;		/*_UARTC_0,		0x1A0 */
-	uint crc_reserved16;		/*			0x1A4 */
-	uint crc_clk_src_vi_sensor;	/*_VI_SENSOR_0,		0x1A8 */
-	uint crc_reserved17;		/*			0x1AC */
-	uint crc_reserved18;		/*			0x1B0 */
-	uint crc_clk_src_sbc4;		/*_SBC4_0,		0x1B4 */
-	uint crc_clk_src_i2c3;		/*_I2C3_0,		0x1B8 */
-	uint crc_clk_src_sdmmc3;	/*_SDMMC3_0,		0x1BC */
-	uint crc_clk_src_uartd;		/*_UARTD_0,		0x1C0 */
-	uint crc_clk_src_uarte;		/*_UARTE_0,		0x1C4 */
-	uint crc_clk_src_vde;		/*_VDE_0,		0x1C8 */
-	uint crc_clk_src_owr;		/*_OWR_0,		0x1CC */
-	uint crc_clk_src_nor;		/*_NOR_0,		0x1D0 */
-	uint crc_clk_src_csite;		/*_CSITE_0,		0x1D4 */
-	uint crc_reserved19[9];		/*			0x1D8-1F8 */
-	uint crc_clk_src_osc;		/*_OSC_0,		0x1FC */
+	uint crc_clk_src[TEGRA_CLK_SOURCES]; /*_I2S1_0...	0x100-1fc */
 	uint crc_reserved20[80];	/*			0x200-33C */
-	uint crc_cpu_cmplx_set;		/* _CPU_CMPLX_SET_0,	0x340 */
-	uint crc_cpu_cmplx_clr;		/* _CPU_CMPLX_CLR_0,	0x344 */
+	uint crc_cpu_cmplx_set;		/* _CPU_CMPLX_SET_0,	0x340	  */
+	uint crc_cpu_cmplx_clr;		/* _CPU_CMPLX_CLR_0,	0x344     */
 };
 
 /* CLK_RST_CONTROLLER_CLK_CPU_CMPLX_0 */
@@ -156,10 +104,13 @@ struct clk_rst_ctlr {
 #define PLL_BASE_OVRRIDE_MASK	(1U << 28)
 
 #define PLL_DIVP_SHIFT		20
+#define PLL_DIVP_MASK		(7U << PLL_DIVP_SHIFT)
 
 #define PLL_DIVN_SHIFT		8
+#define PLL_DIVN_MASK		(0x3ffU << PLL_DIVN_SHIFT)
 
 #define PLL_DIVM_SHIFT		0
+#define PLL_DIVM_MASK		(0x1f << PLL_DIVM_SHIFT)
 
 /* CLK_RST_CONTROLLER_PLLx_MISC_0 */
 #define PLL_CPCON_SHIFT		8
@@ -168,9 +119,20 @@ struct clk_rst_ctlr {
 #define PLL_LFCON_SHIFT		4
 
 #define PLLU_VCO_FREQ_SHIFT	20
+#define PLLU_VCO_FREQ_MASK	(1U << PLLU_VCO_FREQ_SHIFT)
 
 /* CLK_RST_CONTROLLER_OSC_CTRL_0 */
 #define OSC_FREQ_SHIFT		30
 #define OSC_FREQ_MASK		(3U << OSC_FREQ_SHIFT)
 
+/* CLK_RST_CONTROLLER_CLK_SOURCE_x_OUT_0 */
+#define OUT_CLK_DIVISOR_SHIFT	0
+#define OUT_CLK_DIVISOR_MASK	(255 << OUT_CLK_DIVISOR_SHIFT)
+
+#define OUT_CLK_SOURCE_SHIFT	30
+#define OUT_CLK_SOURCE_MASK	(3U << OUT_CLK_SOURCE_SHIFT)
+
+#define OUT_CLK_SOURCE4_SHIFT	28
+#define OUT_CLK_SOURCE4_MASK	(15U << OUT_CLK_SOURCE4_SHIFT)
+
 #endif	/* CLK_RST_H */
diff --git a/arch/arm/include/asm/arch-tegra2/clock.h b/arch/arm/include/asm/arch-tegra2/clock.h
index 8adb23c..49e9904 100644
--- a/arch/arm/include/asm/arch-tegra2/clock.h
+++ b/arch/arm/include/asm/arch-tegra2/clock.h
@@ -51,7 +51,12 @@ enum clock_id {
 	CLOCK_ID_EPCI,
 	CLOCK_ID_SFROM32KHZ,
 
-	CLOCK_ID_COUNT,
+	/* These are the base clocks (inputs to the Tegra SOC) */
+	CLOCK_ID_32KHZ,
+	CLOCK_ID_OSC,
+
+	CLOCK_ID_COUNT,	/* number of clocks */
+	CLOCK_ID_NONE = -1,
 };
 
 /* The clocks supported by the hardware */
@@ -183,10 +188,6 @@ enum periph_id {
 /* return 1 if a PLL ID is in range */
 #define clock_id_isvalid(id) ((id) >= CLOCK_ID_FIRST && (id) < CLOCK_ID_COUNT)
 
-/* return 1 if a peripheral ID is in range */
-#define clock_periph_id_isvalid(id) ((id) >= PERIPH_ID_FIRST && \
-		(id) < PERIPH_ID_COUNT)
-
 /* PLL stabilization delay in usec */
 #define CLOCK_PLL_STABLE_DELAY_US 300
 
@@ -216,6 +217,13 @@ unsigned long clock_start_pll(enum clock_id id, u32 divm, u32 divn,
 void clock_enable(enum periph_id clkid);
 
 /*
+ * Disable a clock
+ *
+ * @param id	clock id
+ */
+void clock_disable(enum periph_id clkid);
+
+/*
  * Set whether a clock is enabled or disabled.
  *
  * @param id		clock id
@@ -259,4 +267,94 @@ enum crc_reset_id {
  */
 void reset_cmplx_set_enable(int cpu, int which, int reset);
 
+/**
+ * Set the source for a peripheral clock. This plus the divisor sets the
+ * clock rate. You need to look up the datasheet to see the meaning of the
+ * source parameter as it changes for each peripheral.
+ *
+ * Warning: This function is only for use pre-relocation. Please use
+ * clock_start_periph_pll() instead.
+ *
+ * @param periph_id	peripheral to adjust
+ * @param source	source clock (0, 1, 2 or 3)
+ */
+void clock_ll_set_source(enum periph_id periph_id, unsigned source);
+
+/**
+ * Set the source and divisor for a peripheral clock. This sets the
+ * clock rate. You need to look up the datasheet to see the meaning of the
+ * source parameter as it changes for each peripheral.
+ *
+ * Warning: This function is only for use pre-relocation. Please use
+ * clock_start_periph_pll() instead.
+ *
+ * @param periph_id	peripheral to adjust
+ * @param source	source clock (0, 1, 2 or 3)
+ * @param divisor	divisor value to use
+ */
+void clock_ll_set_source_divisor(enum periph_id periph_id, unsigned source,
+		unsigned divisor);
+
+/**
+ * Start a peripheral PLL clock@the given rate. This also resets the
+ * peripheral.
+ *
+ * @param periph_id	peripheral to start
+ * @param parent	PLL id of required parent clock
+ * @param rate		Required clock rate in Hz
+ * @return rate selected in Hz, or -1U if something went wrong
+ */
+unsigned clock_start_periph_pll(enum periph_id periph_id,
+		enum clock_id parent, unsigned rate);
+
+/**
+ * Returns the rate of a peripheral clock in Hz. Since the caller almost
+ * certainly knows the parent clock (having just set it) we require that
+ * this be passed in so we don't need to work it out.
+ *
+ * @param periph_id	peripheral to start
+ * @param parent	PLL id of parent clock (used to calculate rate, you
+ *			must know this!)
+ * @return clock rate of peripheral in Hz
+ */
+unsigned long clock_get_periph_rate(enum periph_id periph_id,
+		enum clock_id parent);
+
+/**
+ * Adjust peripheral PLL clock to the given rate. This does not reset the
+ * peripheral. If a second stage divisor is not available, pass NULL for
+ * extra_div. If it is available, then this parameter will return the
+ * divisor selected (which will be a power of 2 from 1 to 256).
+ *
+ * @param periph_id	peripheral to start
+ * @param parent	PLL id of required parent clock
+ * @param rate		Required clock rate in Hz
+ * @param extra_div	value for the second-stage divisor (NULL if one is
+			not available)
+ * @return rate selected in Hz, or -1U if something went wrong
+ */
+unsigned clock_adjust_periph_pll_div(enum periph_id periph_id,
+		enum clock_id parent, unsigned rate, int *extra_div);
+
+/**
+ * Returns the clock rate of a specified clock, in Hz.
+ *
+ * @param parent	PLL id of clock to check
+ * @return rate of clock in Hz
+ */
+unsigned clock_get_rate(enum clock_id clkid);
+
+/*
+ * Checks that clocks are valid and prints a warning if not
+ *
+ * @return 0 if ok, -1 on error
+ */
+int clock_verify(void);
+
+/* Initialize the clocks */
+void clock_init(void);
+
+/* Initialize the PLLs */
+void clock_early_init(void);
+
 #endif
diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c
index 5e28b4c..9672c5e 100644
--- a/board/nvidia/common/board.c
+++ b/board/nvidia/common/board.c
@@ -52,62 +52,31 @@ int timer_init(void)
 	return 0;
 }
 
-/*
- * Routine: clock_init_uart
- * Description: init the PLL and clock for the UART(s)
- */
-static void clock_init_uart(void)
+static void enable_uart(enum periph_id pid)
 {
-	struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
-	struct clk_pll *pll = &clkrst->crc_pll[CLOCK_ID_PERIPH];
-	u32 reg;
-
-	reg = readl(&pll->pll_base);
-	if (!(reg & PLL_BASE_OVRRIDE_MASK)) {
-		/* Override pllp setup for 216MHz operation. */
-		reg = PLL_BYPASS_MASK | PLL_BASE_OVRRIDE_MASK |
-			(1 << PLL_DIVP_SHIFT) | (0xc << PLL_DIVM_SHIFT);
-		reg |= (NVRM_PLLP_FIXED_FREQ_KHZ / 500) << PLL_DIVN_SHIFT;
-		writel(reg, &pll->pll_base);
-
-		reg |= PLL_ENABLE_MASK;
-		writel(reg, &pll->pll_base);
-
-		reg &= ~PLL_BYPASS_MASK;
-		writel(reg, &pll->pll_base);
-	}
-
-#if defined(CONFIG_TEGRA2_ENABLE_UARTA)
 	/* Assert UART reset and enable clock */
-	reset_set_enable(PERIPH_ID_UART1, 1);
-	clock_enable(PERIPH_ID_UART1);
-
-	/* Enable pllp_out0 to UART */
-	reg = readl(&clkrst->crc_clk_src_uarta);
-	reg &= 0x3FFFFFFF;	/* UARTA_CLK_SRC = 00, PLLP_OUT0 */
-	writel(reg, &clkrst->crc_clk_src_uarta);
+	reset_set_enable(pid, 1);
+	clock_enable(pid);
+	clock_ll_set_source(pid, 0);	/* UARTx_CLK_SRC = 00, PLLP_OUT0 */
 
 	/* wait for 2us */
 	udelay(2);
 
 	/* De-assert reset to UART */
-	reset_set_enable(PERIPH_ID_UART1, 0);
+	reset_set_enable(pid, 0);
+}
+
+/*
+ * Routine: clock_init_uart
+ * Description: init the PLL and clock for the UART(s)
+ */
+static void clock_init_uart(void)
+{
+#if defined(CONFIG_TEGRA2_ENABLE_UARTA)
+	enable_uart(PERIPH_ID_UART1);
 #endif	/* CONFIG_TEGRA2_ENABLE_UARTA */
 #if defined(CONFIG_TEGRA2_ENABLE_UARTD)
-	/* Assert UART reset and enable clock */
-	reset_set_enable(PERIPH_ID_UART4, 1);
-	clock_enable(PERIPH_ID_UART4);
-
-	/* Enable pllp_out0 to UART */
-	reg = readl(&clkrst->crc_clk_src_uartd);
-	reg &= 0x3FFFFFFF;	/* UARTD_CLK_SRC = 00, PLLP_OUT0 */
-	writel(reg, &clkrst->crc_clk_src_uartd);
-
-	/* wait for 2us */
-	udelay(2);
-
-	/* De-assert reset to UART */
-	reset_set_enable(PERIPH_ID_UART4, 0);
+	enable_uart(PERIPH_ID_UART4);
 #endif	/* CONFIG_TEGRA2_ENABLE_UARTD */
 }
 
@@ -144,40 +113,8 @@ static void pin_mux_uart(void)
  */
 static void clock_init_mmc(void)
 {
-	struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
-	u32 reg;
-
-	/* Do the SDMMC resets/clock enables */
-	reset_set_enable(PERIPH_ID_SDMMC4, 1);
-	clock_enable(PERIPH_ID_SDMMC4);
-
-	/* Enable pllp_out0 to SDMMC4 */
-	reg = readl(&clkrst->crc_clk_src_sdmmc4);
-	reg &= 0x3FFFFF00;	/* SDMMC4_CLK_SRC = 00, PLLP_OUT0 */
-	reg |= (10 << 1);	/* n-1, 11-1 shl 1 */
-	writel(reg, &clkrst->crc_clk_src_sdmmc4);
-
-	/*
-	 * As per the Tegra2 TRM, section 5.3.4:
-	 * 'Wait 2 us for the clock to flush through the pipe/logic'
-	 */
-	udelay(2);
-
-	reset_set_enable(PERIPH_ID_SDMMC4, 1);
-
-	reset_set_enable(PERIPH_ID_SDMMC3, 1);
-	clock_enable(PERIPH_ID_SDMMC3);
-
-	/* Enable pllp_out0 to SDMMC4, set divisor to 11 for 20MHz */
-	reg = readl(&clkrst->crc_clk_src_sdmmc3);
-	reg &= 0x3FFFFF00;	/* SDMMC3_CLK_SRC = 00, PLLP_OUT0 */
-	reg |= (10 << 1);	/* n-1, 11-1 shl 1 */
-	writel(reg, &clkrst->crc_clk_src_sdmmc3);
-
-	/* wait for 2us */
-	udelay(2);
-
-	reset_set_enable(PERIPH_ID_SDMMC3, 0);
+	clock_start_periph_pll(PERIPH_ID_SDMMC4, CLOCK_ID_PERIPH, 20000000);
+	clock_start_periph_pll(PERIPH_ID_SDMMC3, CLOCK_ID_PERIPH, 20000000);
 }
 
 /*
@@ -226,6 +163,9 @@ static void pin_mux_mmc(void)
  */
 int board_init(void)
 {
+	clock_init();
+	clock_verify();
+
 	/* boot param addr */
 	gd->bd->bi_boot_params = (NV_PA_SDRAM_BASE + 0x100);
 
@@ -268,6 +208,9 @@ int board_mmc_getcd(u8 *cd, struct mmc *mmc)
 #ifdef CONFIG_BOARD_EARLY_INIT_F
 int board_early_init_f(void)
 {
+	/* Initialize essential common plls */
+	clock_early_init();
+
 	/* Initialize UART clocks */
 	clock_init_uart();
 
diff --git a/drivers/mmc/tegra2_mmc.c b/drivers/mmc/tegra2_mmc.c
index 8b6f829..a71785c 100644
--- a/drivers/mmc/tegra2_mmc.c
+++ b/drivers/mmc/tegra2_mmc.c
@@ -23,36 +23,46 @@
 #include <mmc.h>
 #include <asm/io.h>
 #include <asm/arch/clk_rst.h>
+#include <asm/arch/clock.h>
 #include "tegra2_mmc.h"
 
 /* support 4 mmc hosts */
 struct mmc mmc_dev[4];
 struct mmc_host mmc_host[4];
 
-static inline struct tegra2_mmc *tegra2_get_base_mmc(int dev_index)
+
+/**
+ * Get the host address and peripheral ID for a device. Devices are numbered
+ * from 0 to 3.
+ *
+ * @param host		Structure to fill in (base, reg, mmc_id)
+ * @param dev_index	Device index (0-3)
+ */
+static void tegra2_get_setup(struct mmc_host *host, int dev_index)
 {
-	unsigned long offset;
 	debug("tegra2_get_base_mmc: dev_index = %d\n", dev_index);
 
 	switch (dev_index) {
-	case 0:
-		offset = TEGRA2_SDMMC4_BASE;
-		break;
 	case 1:
-		offset = TEGRA2_SDMMC3_BASE;
+		host->base = TEGRA2_SDMMC3_BASE;
+		host->mmc_id = PERIPH_ID_SDMMC3;
 		break;
 	case 2:
-		offset = TEGRA2_SDMMC2_BASE;
+		host->base = TEGRA2_SDMMC2_BASE;
+		host->mmc_id = PERIPH_ID_SDMMC2;
 		break;
 	case 3:
-		offset = TEGRA2_SDMMC1_BASE;
+		host->base = TEGRA2_SDMMC1_BASE;
+		host->mmc_id = PERIPH_ID_SDMMC1;
 		break;
+	case 0:
 	default:
-		offset = TEGRA2_SDMMC4_BASE;
+		host->base = TEGRA2_SDMMC4_BASE;
+		host->mmc_id = PERIPH_ID_SDMMC4;
 		break;
 	}
 
-	return (struct tegra2_mmc *)(offset);
+	host->reg = (struct tegra2_mmc *)host->base;
 }
 
 static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data)
@@ -274,62 +284,24 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
 
 static void mmc_change_clock(struct mmc_host *host, uint clock)
 {
-	int div, hw_div;
+	int div;
 	unsigned short clk;
 	unsigned long timeout;
-	unsigned int reg, hostbase;
-	struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+
 	debug(" mmc_change_clock called\n");
 
-	/* Change Tegra2 SDMMCx clock divisor here */
-	/* Source is 216MHz, PLLP_OUT0 */
+	/*
+	 * Change Tegra2 SDMMCx clock divisor here. Source is 216MHz,
+	 * PLLP_OUT0
+	 */
 	if (clock == 0)
 		goto out;
-
-	div = 1;
-	if (clock <= 400000) {
-		hw_div = ((9-1)<<1);		/* Best match is 375KHz */
-		div = 64;
-	} else if (clock <= 20000000)
-		hw_div = ((11-1)<<1);		/* Best match is 19.6MHz */
-	else if (clock <= 26000000)
-		hw_div = ((9-1)<<1);		/* Use 24MHz */
-	else
-		hw_div = ((4-1)<<1) + 1;	/* 4.5 divisor for 48MHz */
-
-	debug("mmc_change_clock: hw_div = %d, card clock div = %d\n",
-		hw_div, div);
-
-	/* Change SDMMCx divisor */
-
-	hostbase = readl(&host->base);
-	debug("mmc_change_clock: hostbase = %08X\n", hostbase);
-
-	if (hostbase == TEGRA2_SDMMC1_BASE) {
-		reg = readl(&clkrst->crc_clk_src_sdmmc1);
-		reg &= 0xFFFFFF00;	/* divisor (7.1) = 00 */
-		reg |= hw_div;		/* n-1 */
-		writel(reg, &clkrst->crc_clk_src_sdmmc1);
-	} else if (hostbase == TEGRA2_SDMMC2_BASE) {
-		reg = readl(&clkrst->crc_clk_src_sdmmc2);
-		reg &= 0xFFFFFF00;	/* divisor (7.1) = 00 */
-		reg |= hw_div;		/* n-1 */
-		writel(reg, &clkrst->crc_clk_src_sdmmc2);
-	} else if (hostbase == TEGRA2_SDMMC3_BASE) {
-		reg = readl(&clkrst->crc_clk_src_sdmmc3);
-		reg &= 0xFFFFFF00;	/* divisor (7.1) = 00 */
-		reg |= hw_div;		/* n-1 */
-		writel(reg, &clkrst->crc_clk_src_sdmmc3);
-	} else {
-		reg = readl(&clkrst->crc_clk_src_sdmmc4);
-		reg &= 0xFFFFFF00;	/* divisor (7.1) = 00 */
-		reg |= hw_div;		/* n-1 */
-		writel(reg, &clkrst->crc_clk_src_sdmmc4);
-	}
+	clock_adjust_periph_pll_div(host->mmc_id, CLOCK_ID_PERIPH, clock,
+				    &div);
+	debug("div = %d\n", div);
 
 	writew(0, &host->reg->clkcon);
 
-	div >>= 1;
 	/*
 	 * CLKCON
 	 * SELFREQ[15:8]	: base clock divided by value
@@ -337,6 +309,7 @@ static void mmc_change_clock(struct mmc_host *host, uint clock)
 	 * STBLINTCLK[1]	: Internal Clock Stable
 	 * ENINTCLK[0]		: Internal Clock Enable
 	 */
+	div >>= 1;
 	clk = (div << 8) | (1 << 0);
 	writew(clk, &host->reg->clkcon);
 
@@ -355,7 +328,6 @@ static void mmc_change_clock(struct mmc_host *host, uint clock)
 	writew(clk, &host->reg->clkcon);
 
 	debug("mmc_change_clock: clkcon = %08X\n", clk);
-	debug("mmc_change_clock: CLK_SOURCE_SDMMCx = %08X\n", reg);
 
 out:
 	host->clock = clock;
@@ -370,7 +342,6 @@ static void mmc_set_ios(struct mmc *mmc)
 	debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock);
 
 	/* Change clock first */
-
 	mmc_change_clock(host, mmc->clock);
 
 	ctrl = readb(&host->reg->hostctl);
@@ -495,8 +466,7 @@ static int tegra2_mmc_initialize(int dev_index, int bus_width)
 	mmc->f_max = 48000000;
 
 	mmc_host[dev_index].clock = 0;
-	mmc_host[dev_index].reg = tegra2_get_base_mmc(dev_index);
-	mmc_host[dev_index].base = (unsigned int)mmc_host[dev_index].reg;
+	tegra2_get_setup(&mmc_host[dev_index], dev_index);
 	mmc_register(mmc);
 
 	return 0;
diff --git a/drivers/mmc/tegra2_mmc.h b/drivers/mmc/tegra2_mmc.h
index 4b80f9f..28698e0 100644
--- a/drivers/mmc/tegra2_mmc.h
+++ b/drivers/mmc/tegra2_mmc.h
@@ -73,6 +73,7 @@ struct mmc_host {
 	unsigned int version;	/* SDHCI spec. version */
 	unsigned int clock;	/* Current clock (MHz) */
 	unsigned int base;	/* Base address, SDMMC1/2/3/4 */
+	enum periph_id mmc_id;	/* Peripheral ID: PERIPH_ID_... */
 };
 
 int tegra2_mmc_init(int dev_index, int bus_width);
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [U-Boot] [PATCH 4/6] tegra2: Rename PIN_ to PINGRP_
  2011-09-08 22:11 [U-Boot] [PATCH 0/6] tegra2: Add more clock/pinmux functionality Simon Glass
                   ` (2 preceding siblings ...)
  2011-09-08 22:12 ` [U-Boot] [PATCH 3/6] tegra2: Add more clock functions Simon Glass
@ 2011-09-08 22:12 ` Simon Glass
  2011-09-08 22:12 ` [U-Boot] [PATCH 5/6] tegra2: Add more pinmux functions Simon Glass
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Simon Glass @ 2011-09-08 22:12 UTC (permalink / raw)
  To: u-boot

The pin groupings are better named PINGRP, since on Tegra2 they refer to
multiple pins.

Sorry about this, but better to get it right now when there is only a small
amount of code affected.

Signed-off-by: Simon Glass <sjg@chromium.org>
---
 arch/arm/cpu/armv7/tegra2/pinmux.c        |    6 +-
 arch/arm/include/asm/arch-tegra2/pinmux.h |  272 ++++++++++++++--------------
 board/nvidia/common/board.c               |   18 +-
 3 files changed, 148 insertions(+), 148 deletions(-)

diff --git a/arch/arm/cpu/armv7/tegra2/pinmux.c b/arch/arm/cpu/armv7/tegra2/pinmux.c
index 5594ab8..01a3d84 100644
--- a/arch/arm/cpu/armv7/tegra2/pinmux.c
+++ b/arch/arm/cpu/armv7/tegra2/pinmux.c
@@ -27,7 +27,7 @@
 #include <common.h>
 
 
-void pinmux_set_tristate(enum pmux_pin pin, int enable)
+void pinmux_set_tristate(enum pmux_pingrp pin, int enable)
 {
 	struct pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE;
 	u32 *tri = &pmt->pmt_tri[TRISTATE_REG(pin)];
@@ -41,12 +41,12 @@ void pinmux_set_tristate(enum pmux_pin pin, int enable)
 	writel(reg, tri);
 }
 
-void pinmux_tristate_enable(enum pmux_pin pin)
+void pinmux_tristate_enable(enum pmux_pingrp pin)
 {
 	pinmux_set_tristate(pin, 1);
 }
 
-void pinmux_tristate_disable(enum pmux_pin pin)
+void pinmux_tristate_disable(enum pmux_pingrp pin)
 {
 	pinmux_set_tristate(pin, 0);
 }
diff --git a/arch/arm/include/asm/arch-tegra2/pinmux.h b/arch/arm/include/asm/arch-tegra2/pinmux.h
index b8a4753..e8ef632 100644
--- a/arch/arm/include/asm/arch-tegra2/pinmux.h
+++ b/arch/arm/include/asm/arch-tegra2/pinmux.h
@@ -24,137 +24,137 @@
 #ifndef _PINMUX_H_
 #define _PINMUX_H_
 
-/* Pins which we can set to tristate or normal */
-enum pmux_pin {
+/* Pin groups which we can set to tristate or normal */
+enum pmux_pingrp {
 	/* APB_MISC_PP_TRISTATE_REG_A_0 */
-	PIN_ATA,
-	PIN_ATB,
-	PIN_ATC,
-	PIN_ATD,
-	PIN_CDEV1,
-	PIN_CDEV2,
-	PIN_CSUS,
-	PIN_DAP1,
-
-	PIN_DAP2,
-	PIN_DAP3,
-	PIN_DAP4,
-	PIN_DTA,
-	PIN_DTB,
-	PIN_DTC,
-	PIN_DTD,
-	PIN_DTE,
-
-	PIN_GPU,
-	PIN_GPV,
-	PIN_I2CP,
-	PIN_IRTX,
-	PIN_IRRX,
-	PIN_KBCB,
-	PIN_KBCA,
-	PIN_PMC,
-
-	PIN_PTA,
-	PIN_RM,
-	PIN_KBCE,
-	PIN_KBCF,
-	PIN_GMA,
-	PIN_GMC,
-	PIN_SDMMC1,
-	PIN_OWC,
+	PINGRP_ATA,
+	PINGRP_ATB,
+	PINGRP_ATC,
+	PINGRP_ATD,
+	PINGRP_CDEV1,
+	PINGRP_CDEV2,
+	PINGRP_CSUS,
+	PINGRP_DAP1,
+
+	PINGRP_DAP2,
+	PINGRP_DAP3,
+	PINGRP_DAP4,
+	PINGRP_DTA,
+	PINGRP_DTB,
+	PINGRP_DTC,
+	PINGRP_DTD,
+	PINGRP_DTE,
+
+	PINGRP_GPU,
+	PINGRP_GPV,
+	PINGRP_I2CP,
+	PINGRP_IRTX,
+	PINGRP_IRRX,
+	PINGRP_KBCB,
+	PINGRP_KBCA,
+	PINGRP_PMC,
+
+	PINGRP_PTA,
+	PINGRP_RM,
+	PINGRP_KBCE,
+	PINGRP_KBCF,
+	PINGRP_GMA,
+	PINGRP_GMC,
+	PINGRP_SDMMC1,
+	PINGRP_OWC,
 
 	/* 32: APB_MISC_PP_TRISTATE_REG_B_0 */
-	PIN_GME,
-	PIN_SDC,
-	PIN_SDD,
-	PIN_RESERVED0,
-	PIN_SLXA,
-	PIN_SLXC,
-	PIN_SLXD,
-	PIN_SLXK,
-
-	PIN_SPDI,
-	PIN_SPDO,
-	PIN_SPIA,
-	PIN_SPIB,
-	PIN_SPIC,
-	PIN_SPID,
-	PIN_SPIE,
-	PIN_SPIF,
-
-	PIN_SPIG,
-	PIN_SPIH,
-	PIN_UAA,
-	PIN_UAB,
-	PIN_UAC,
-	PIN_UAD,
-	PIN_UCA,
-	PIN_UCB,
-
-	PIN_RESERVED1,
-	PIN_ATE,
-	PIN_KBCC,
-	PIN_RESERVED2,
-	PIN_RESERVED3,
-	PIN_GMB,
-	PIN_GMD,
-	PIN_DDC,
+	PINGRP_GME,
+	PINGRP_SDC,
+	PINGRP_SDD,
+	PINGRP_RESERVED0,
+	PINGRP_SLXA,
+	PINGRP_SLXC,
+	PINGRP_SLXD,
+	PINGRP_SLXK,
+
+	PINGRP_SPDI,
+	PINGRP_SPDO,
+	PINGRP_SPIA,
+	PINGRP_SPIB,
+	PINGRP_SPIC,
+	PINGRP_SPID,
+	PINGRP_SPIE,
+	PINGRP_SPIF,
+
+	PINGRP_SPIG,
+	PINGRP_SPIH,
+	PINGRP_UAA,
+	PINGRP_UAB,
+	PINGRP_UAC,
+	PINGRP_UAD,
+	PINGRP_UCA,
+	PINGRP_UCB,
+
+	PINGRP_RESERVED1,
+	PINGRP_ATE,
+	PINGRP_KBCC,
+	PINGRP_RESERVED2,
+	PINGRP_RESERVED3,
+	PINGRP_GMB,
+	PINGRP_GMD,
+	PINGRP_DDC,
 
 	/* 64: APB_MISC_PP_TRISTATE_REG_C_0 */
-	PIN_LD0,
-	PIN_LD1,
-	PIN_LD2,
-	PIN_LD3,
-	PIN_LD4,
-	PIN_LD5,
-	PIN_LD6,
-	PIN_LD7,
-
-	PIN_LD8,
-	PIN_LD9,
-	PIN_LD10,
-	PIN_LD11,
-	PIN_LD12,
-	PIN_LD13,
-	PIN_LD14,
-	PIN_LD15,
-
-	PIN_LD16,
-	PIN_LD17,
-	PIN_LHP0,
-	PIN_LHP1,
-	PIN_LHP2,
-	PIN_LVP0,
-	PIN_LVP1,
-	PIN_HDINT,
-
-	PIN_LM0,
-	PIN_LM1,
-	PIN_LVS,
-	PIN_LSC0,
-	PIN_LSC1,
-	PIN_LSCK,
-	PIN_LDC,
-	PIN_LCSN,
+	PINGRP_LD0,
+	PINGRP_LD1,
+	PINGRP_LD2,
+	PINGRP_LD3,
+	PINGRP_LD4,
+	PINGRP_LD5,
+	PINGRP_LD6,
+	PINGRP_LD7,
+
+	PINGRP_LD8,
+	PINGRP_LD9,
+	PINGRP_LD10,
+	PINGRP_LD11,
+	PINGRP_LD12,
+	PINGRP_LD13,
+	PINGRP_LD14,
+	PINGRP_LD15,
+
+	PINGRP_LD16,
+	PINGRP_LD17,
+	PINGRP_LHP0,
+	PINGRP_LHP1,
+	PINGRP_LHP2,
+	PINGRP_LVP0,
+	PINGRP_LVP1,
+	PINGRP_HDINT,
+
+	PINGRP_LM0,
+	PINGRP_LM1,
+	PINGRP_LVS,
+	PINGRP_LSC0,
+	PINGRP_LSC1,
+	PINGRP_LSCK,
+	PINGRP_LDC,
+	PINGRP_LCSN,
 
 	/* 96: APB_MISC_PP_TRISTATE_REG_D_0 */
-	PIN_LSPI,
-	PIN_LSDA,
-	PIN_LSDI,
-	PIN_LPW0,
-	PIN_LPW1,
-	PIN_LPW2,
-	PIN_LDI,
-	PIN_LHS,
-
-	PIN_LPP,
-	PIN_RESERVED4,
-	PIN_KBCD,
-	PIN_GPU7,
-	PIN_DTF,
-	PIN_UDA,
-	PIN_CRTP,
-	PIN_SDB,
+	PINGRP_LSPI,
+	PINGRP_LSDA,
+	PINGRP_LSDI,
+	PINGRP_LPW0,
+	PINGRP_LPW1,
+	PINGRP_LPW2,
+	PINGRP_LDI,
+	PINGRP_LHS,
+
+	PINGRP_LPP,
+	PINGRP_RESERVED4,
+	PINGRP_KBCD,
+	PINGRP_GPU7,
+	PINGRP_DTF,
+	PINGRP_UDA,
+	PINGRP_CRTP,
+	PINGRP_SDB,
 };
 
 
@@ -172,25 +172,25 @@ struct pmux_tri_ctlr {
 
 	uint pmt_reserved[22];		/* ABP_MISC_PP_ reserved offs 28-7C */
 
-	uint pmt_ctl_a;			/* _PIN_MUX_CTL_A_0, offset 80 */
-	uint pmt_ctl_b;			/* _PIN_MUX_CTL_B_0, offset 84 */
-	uint pmt_ctl_c;			/* _PIN_MUX_CTL_C_0, offset 88 */
-	uint pmt_ctl_d;			/* _PIN_MUX_CTL_D_0, offset 8C */
-	uint pmt_ctl_e;			/* _PIN_MUX_CTL_E_0, offset 90 */
-	uint pmt_ctl_f;			/* _PIN_MUX_CTL_F_0, offset 94 */
-	uint pmt_ctl_g;			/* _PIN_MUX_CTL_G_0, offset 98 */
+	uint pmt_ctl_a;			/* _PINGRP_MUX_CTL_A_0, offset 80 */
+	uint pmt_ctl_b;			/* _PINGRP_MUX_CTL_B_0, offset 84 */
+	uint pmt_ctl_c;			/* _PINGRP_MUX_CTL_C_0, offset 88 */
+	uint pmt_ctl_d;			/* _PINGRP_MUX_CTL_D_0, offset 8C */
+	uint pmt_ctl_e;			/* _PINGRP_MUX_CTL_E_0, offset 90 */
+	uint pmt_ctl_f;			/* _PINGRP_MUX_CTL_F_0, offset 94 */
+	uint pmt_ctl_g;			/* _PINGRP_MUX_CTL_G_0, offset 98 */
 };
 
-/* Converts a pin number to a tristate register: 0=A, 1=B, 2=C, 3=D */
+/* Converts a pin group to a tristate register: 0=A, 1=B, 2=C, 3=D */
 #define TRISTATE_REG(id) ((id) >> 5)
 
 /* Mask value for a tristate (within TRISTATE_REG(id)) */
 #define TRISTATE_MASK(id) (1 << ((id) & 0x1f))
 
-/* Set a pin to tristate */
-void pinmux_tristate_enable(enum pmux_pin pin);
+/* Set a pin group to tristate */
+void pinmux_tristate_enable(enum pmux_pingrp pin);
 
-/* Set a pin to normal (non tristate) */
-void pinmux_tristate_disable(enum pmux_pin pin);
+/* Set a pin group to normal (non tristate) */
+void pinmux_tristate_disable(enum pmux_pingrp pin);
 
 #endif	/* PINMUX_H */
diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c
index 9672c5e..35ff2ef 100644
--- a/board/nvidia/common/board.c
+++ b/board/nvidia/common/board.c
@@ -94,15 +94,15 @@ static void pin_mux_uart(void)
 	reg &= 0xFFF0FFFF;	/* IRRX_/IRTX_SEL [19:16] = 00 UARTA */
 	writel(reg, &pmt->pmt_ctl_c);
 
-	pinmux_tristate_disable(PIN_IRRX);
-	pinmux_tristate_disable(PIN_IRTX);
+	pinmux_tristate_disable(PINGRP_IRRX);
+	pinmux_tristate_disable(PINGRP_IRTX);
 #endif	/* CONFIG_TEGRA2_ENABLE_UARTA */
 #if defined(CONFIG_TEGRA2_ENABLE_UARTD)
 	reg = readl(&pmt->pmt_ctl_b);
 	reg &= 0xFFFFFFF3;	/* GMC_SEL [3:2] = 00, UARTD */
 	writel(reg, &pmt->pmt_ctl_b);
 
-	pinmux_tristate_disable(PIN_GMC);
+	pinmux_tristate_disable(PINGRP_GMC);
 #endif	/* CONFIG_TEGRA2_ENABLE_UARTD */
 }
 
@@ -138,9 +138,9 @@ static void pin_mux_mmc(void)
 	reg |= (3 << 0);	/* GME_SEL [1:0] = 11 SDIO4 */
 	writel(reg, &pmt->pmt_ctl_d);
 
-	pinmux_tristate_disable(PIN_ATB);
-	pinmux_tristate_disable(PIN_GMA);
-	pinmux_tristate_disable(PIN_GME);
+	pinmux_tristate_disable(PINGRP_ATB);
+	pinmux_tristate_disable(PINGRP_GMA);
+	pinmux_tristate_disable(PINGRP_GME);
 
 	/* SDMMC3 */
 	/* SDIO3_CLK, SDIO3_CMD, SDIO3_DAT[3:0] */
@@ -151,9 +151,9 @@ static void pin_mux_mmc(void)
 	reg |= (2 << 14);	/* SDD_SEL [15:14] = 01 SDIO3 */
 	writel(reg, &pmt->pmt_ctl_d);
 
-	pinmux_tristate_disable(PIN_SDC);
-	pinmux_tristate_disable(PIN_SDD);
-	pinmux_tristate_disable(PIN_SDB);
+	pinmux_tristate_disable(PINGRP_SDC);
+	pinmux_tristate_disable(PINGRP_SDD);
+	pinmux_tristate_disable(PINGRP_SDB);
 }
 #endif
 
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [U-Boot] [PATCH 5/6] tegra2: Add more pinmux functions
  2011-09-08 22:11 [U-Boot] [PATCH 0/6] tegra2: Add more clock/pinmux functionality Simon Glass
                   ` (3 preceding siblings ...)
  2011-09-08 22:12 ` [U-Boot] [PATCH 4/6] tegra2: Rename PIN_ to PINGRP_ Simon Glass
@ 2011-09-08 22:12 ` Simon Glass
  2011-09-08 22:12 ` [U-Boot] [PATCH 6/6] tegra2: Enable MMC for Seaboard Simon Glass
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Simon Glass @ 2011-09-08 22:12 UTC (permalink / raw)
  To: u-boot

This adds support for changing pinmux functions of pin groups. This is done
by defining a PMUX_FUNC_... enum which can be used to select the function for
each group using pinmux_set_func(). It is also possible to enable
pullup/pulldown, and the existing tristate functionality is retained.

Also provided is a means of configuring a list of pingroups by providing a
configuration table to pinmux_config_table().

Signed-off-by: Simon Glass <sjg@chromium.org>
---
 arch/arm/cpu/armv7/tegra2/pinmux.c        |  534 ++++++++++++++++++++++++++++-
 arch/arm/include/asm/arch-tegra2/pinmux.h |  192 ++++++++++-
 board/nvidia/common/board.c               |   42 +--
 3 files changed, 719 insertions(+), 49 deletions(-)

diff --git a/arch/arm/cpu/armv7/tegra2/pinmux.c b/arch/arm/cpu/armv7/tegra2/pinmux.c
index 01a3d84..4a5bb54 100644
--- a/arch/arm/cpu/armv7/tegra2/pinmux.c
+++ b/arch/arm/cpu/armv7/tegra2/pinmux.c
@@ -26,10 +26,476 @@
 #include <asm/arch/pinmux.h>
 #include <common.h>
 
+#if defined(DEBUG)
+#define DEBUG_ASSERT 1
+#else
+#define DEBUG_ASSERT 0
+#endif
+
+#define assert(x)							\
+	({								\
+		if (DEBUG_ASSERT && !(x))				\
+			printf("Assertion failure '%s' %s line %d\n",	\
+			       #x, __FILE__, __LINE__);			\
+	})
+
+/*
+ * This defines the order of the pin mux control bits in the registers. For
+ * some reason there is no correspendence between the tristate, pin mux and
+ * pullup/pulldown registers.
+ */
+enum pmux_ctlid {
+	/* 0: APB_MISC_PP_PIN_MUX_CTL_A_0 */
+	MUXCTL_UAA,
+	MUXCTL_UAB,
+	MUXCTL_UAC,
+	MUXCTL_UAD,
+	MUXCTL_UDA,
+	MUXCTL_RESERVED5,
+	MUXCTL_ATE,
+	MUXCTL_RM,
+
+	MUXCTL_ATB,
+	MUXCTL_RESERVED9,
+	MUXCTL_ATD,
+	MUXCTL_ATC,
+	MUXCTL_ATA,
+	MUXCTL_KBCF,
+	MUXCTL_KBCE,
+	MUXCTL_SDMMC1,
+
+	/* 16: APB_MISC_PP_PIN_MUX_CTL_B_0 */
+	MUXCTL_GMA,
+	MUXCTL_GMC,
+	MUXCTL_HDINT,
+	MUXCTL_SLXA,
+	MUXCTL_OWC,
+	MUXCTL_SLXC,
+	MUXCTL_SLXD,
+	MUXCTL_SLXK,
+
+	MUXCTL_UCA,
+	MUXCTL_UCB,
+	MUXCTL_DTA,
+	MUXCTL_DTB,
+	MUXCTL_RESERVED28,
+	MUXCTL_DTC,
+	MUXCTL_DTD,
+	MUXCTL_DTE,
+
+	/* 32: APB_MISC_PP_PIN_MUX_CTL_C_0 */
+	MUXCTL_DDC,
+	MUXCTL_CDEV1,
+	MUXCTL_CDEV2,
+	MUXCTL_CSUS,
+	MUXCTL_I2CP,
+	MUXCTL_KBCA,
+	MUXCTL_KBCB,
+	MUXCTL_KBCC,
+
+	MUXCTL_IRTX,
+	MUXCTL_IRRX,
+	MUXCTL_DAP1,
+	MUXCTL_DAP2,
+	MUXCTL_DAP3,
+	MUXCTL_DAP4,
+	MUXCTL_GMB,
+	MUXCTL_GMD,
+
+	/* 48: APB_MISC_PP_PIN_MUX_CTL_D_0 */
+	MUXCTL_GME,
+	MUXCTL_GPV,
+	MUXCTL_GPU,
+	MUXCTL_SPDO,
+	MUXCTL_SPDI,
+	MUXCTL_SDB,
+	MUXCTL_SDC,
+	MUXCTL_SDD,
+
+	MUXCTL_SPIH,
+	MUXCTL_SPIG,
+	MUXCTL_SPIF,
+	MUXCTL_SPIE,
+	MUXCTL_SPID,
+	MUXCTL_SPIC,
+	MUXCTL_SPIB,
+	MUXCTL_SPIA,
+
+	/* 64: APB_MISC_PP_PIN_MUX_CTL_E_0 */
+	MUXCTL_LPW0,
+	MUXCTL_LPW1,
+	MUXCTL_LPW2,
+	MUXCTL_LSDI,
+	MUXCTL_LSDA,
+	MUXCTL_LSPI,
+	MUXCTL_LCSN,
+	MUXCTL_LDC,
+
+	MUXCTL_LSCK,
+	MUXCTL_LSC0,
+	MUXCTL_LSC1,
+	MUXCTL_LHS,
+	MUXCTL_LVS,
+	MUXCTL_LM0,
+	MUXCTL_LM1,
+	MUXCTL_LVP0,
+
+	/* 80: APB_MISC_PP_PIN_MUX_CTL_F_0 */
+	MUXCTL_LD0,
+	MUXCTL_LD1,
+	MUXCTL_LD2,
+	MUXCTL_LD3,
+	MUXCTL_LD4,
+	MUXCTL_LD5,
+	MUXCTL_LD6,
+	MUXCTL_LD7,
+
+	MUXCTL_LD8,
+	MUXCTL_LD9,
+	MUXCTL_LD10,
+	MUXCTL_LD11,
+	MUXCTL_LD12,
+	MUXCTL_LD13,
+	MUXCTL_LD14,
+	MUXCTL_LD15,
+
+	/* 96: APB_MISC_PP_PIN_MUX_CTL_G_0 */
+	MUXCTL_LD16,
+	MUXCTL_LD17,
+	MUXCTL_LHP1,
+	MUXCTL_LHP2,
+	MUXCTL_LVP1,
+	MUXCTL_LHP0,
+	MUXCTL_RESERVED102,
+	MUXCTL_LPP,
+
+	MUXCTL_LDI,
+	MUXCTL_PMC,
+	MUXCTL_CRTP,
+	MUXCTL_PTA,
+	MUXCTL_RESERVED108,
+	MUXCTL_KBCD,
+	MUXCTL_GPU7,
+	MUXCTL_DTF,
+
+	MUXCTL_NONE = -1,
+};
+
+/*
+ * And this defines the order of the pullup/pulldown controls which are again
+ * in a different order
+ */
+enum pmux_pullid {
+	/* 0: APB_MISC_PP_PULLUPDOWN_REG_A_0 */
+	PUCTL_ATA,
+	PUCTL_ATB,
+	PUCTL_ATC,
+	PUCTL_ATD,
+	PUCTL_ATE,
+	PUCTL_DAP1,
+	PUCTL_DAP2,
+	PUCTL_DAP3,
+
+	PUCTL_DAP4,
+	PUCTL_DTA,
+	PUCTL_DTB,
+	PUCTL_DTC,
+	PUCTL_DTD,
+	PUCTL_DTE,
+	PUCTL_DTF,
+	PUCTL_GPV,
+
+	/* 16: APB_MISC_PP_PULLUPDOWN_REG_B_0 */
+	PUCTL_RM,
+	PUCTL_I2CP,
+	PUCTL_PTA,
+	PUCTL_GPU7,
+	PUCTL_KBCA,
+	PUCTL_KBCB,
+	PUCTL_KBCC,
+	PUCTL_KBCD,
+
+	PUCTL_SPDI,
+	PUCTL_SPDO,
+	PUCTL_GPSLXAU,
+	PUCTL_CRTP,
+	PUCTL_SLXC,
+	PUCTL_SLXD,
+	PUCTL_SLXK,
+
+	/* 32: APB_MISC_PP_PULLUPDOWN_REG_C_0 */
+	PUCTL_CDEV1,
+	PUCTL_CDEV2,
+	PUCTL_SPIA,
+	PUCTL_SPIB,
+	PUCTL_SPIC,
+	PUCTL_SPID,
+	PUCTL_SPIE,
+	PUCTL_SPIF,
+
+	PUCTL_SPIG,
+	PUCTL_SPIH,
+	PUCTL_IRTX,
+	PUCTL_IRRX,
+	PUCTL_GME,
+	PUCTL_RESERVED45,
+	PUCTL_XM2D,
+	PUCTL_XM2C,
+
+	/* 48: APB_MISC_PP_PULLUPDOWN_REG_D_0 */
+	PUCTL_UAA,
+	PUCTL_UAB,
+	PUCTL_UAC,
+	PUCTL_UAD,
+	PUCTL_UCA,
+	PUCTL_UCB,
+	PUCTL_LD17,
+	PUCTL_LD19_18,
+
+	PUCTL_LD21_20,
+	PUCTL_LD23_22,
+	PUCTL_LS,
+	PUCTL_LC,
+	PUCTL_CSUS,
+	PUCTL_DDRC,
+	PUCTL_SDC,
+	PUCTL_SDD,
+
+	/* 64: APB_MISC_PP_PULLUPDOWN_REG_E_0 */
+	PUCTL_KBCF,
+	PUCTL_KBCE,
+	PUCTL_PMCA,
+	PUCTL_PMCB,
+	PUCTL_PMCC,
+	PUCTL_PMCD,
+	PUCTL_PMCE,
+	PUCTL_CK32,
+
+	PUCTL_UDA,
+	PUCTL_SDMMC1,
+	PUCTL_GMA,
+	PUCTL_GMB,
+	PUCTL_GMC,
+	PUCTL_GMD,
+	PUCTL_DDC,
+	PUCTL_OWC,
+
+	PUCTL_NONE = -1
+};
+
+struct tegra_pingroup_desc {
+	const char *name;
+	enum pmux_func funcs[4];
+	enum pmux_func func_safe;
+	enum pmux_vddio vddio;
+	enum pmux_ctlid ctl_id;
+	enum pmux_pullid pull_id;
+};
+
+
+/* Converts a pmux_pingrp number to a tristate register: 0=A, 1=B, 2=C, 3=D */
+#define TRISTATE_REG(pmux_pingrp) ((pmux_pingrp) >> 5)
+
+/* Mask value for a tristate (within TRISTATE_REG(id)) */
+#define TRISTATE_MASK(pmux_pingrp) (1 << ((pmux_pingrp) & 0x1f))
+
+/* Converts a PUCTL id to a pull register: 0=A, 1=B...4=E */
+#define PULL_REG(pmux_pullid) ((pmux_pullid) >> 4)
+
+/* Converts a PUCTL id to a shift position */
+#define PULL_SHIFT(pmux_pullid) ((pmux_pullid << 1) & 0x1f)
+
+/* Converts a MUXCTL id to a ctl register: 0=A, 1=B...6=G */
+#define MUXCTL_REG(pmux_ctlid) ((pmux_ctlid) >> 4)
+
+/* Converts a MUXCTL id to a shift position */
+#define MUXCTL_SHIFT(pmux_ctlid) ((pmux_ctlid << 1) & 0x1f)
+
+/* Convenient macro for defining pin group properties */
+#define PINALL(pg_name, vdd, f0, f1, f2, f3, f_safe, mux, pupd)		\
+	{						\
+		.vddio = PMUX_VDDIO_ ## vdd,		\
+		.funcs = {				\
+			PMUX_FUNC_ ## f0,			\
+			PMUX_FUNC_ ## f1,			\
+			PMUX_FUNC_ ## f2,			\
+			PMUX_FUNC_ ## f3,			\
+		},					\
+		.func_safe = PMUX_FUNC_ ## f_safe,		\
+		.ctl_id = mux,				\
+		.pull_id = pupd				\
+	}
+
+/* A normal pin group where the mux name and pull-up name match */
+#define PIN(pg_name, vdd, f0, f1, f2, f3, f_safe)		\
+		PINALL(pg_name, vdd, f0, f1, f2, f3, f_safe,	\
+			MUXCTL_ ## pg_name, PUCTL_ ## pg_name)
+
+/* A pin group where the pull-up name doesn't have a 1-1 mapping */
+#define PINP(pg_name, vdd, f0, f1, f2, f3, f_safe, pupd)		\
+		PINALL(pg_name, vdd, f0, f1, f2, f3, f_safe,	\
+			MUXCTL_ ## pg_name, PUCTL_ ## pupd)
+
+/* A pin group number which is not used */
+#define PIN_RESERVED \
+	PIN(NONE, NONE, NONE, NONE, NONE, NONE, NONE)
+
+const struct tegra_pingroup_desc tegra_soc_pingroups[PINGRP_COUNT] = {
+	PIN(ATA,  NAND,  IDE,    NAND,   GMI,       RSVD,        IDE),
+	PIN(ATB,  NAND,  IDE,    NAND,   GMI,       SDIO4,       IDE),
+	PIN(ATC,  NAND,  IDE,    NAND,   GMI,       SDIO4,       IDE),
+	PIN(ATD,  NAND,  IDE,    NAND,   GMI,       SDIO4,       IDE),
+	PIN(CDEV1, AUDIO, OSC,   PLLA_OUT, PLLM_OUT1, AUDIO_SYNC, OSC),
+	PIN(CDEV2, AUDIO, OSC,   AHB_CLK, APB_CLK, PLLP_OUT4,    OSC),
+	PIN(CSUS, VI, PLLC_OUT1, PLLP_OUT2, PLLP_OUT3, VI_SENSOR_CLK,
+		PLLC_OUT1),
+	PIN(DAP1, AUDIO, DAP1,   RSVD,   GMI,       SDIO2,       DAP1),
+
+	PIN(DAP2, AUDIO, DAP2,   TWC,    RSVD,      GMI,         DAP2),
+	PIN(DAP3, BB,    DAP3,   RSVD,   RSVD,      RSVD,        DAP3),
+	PIN(DAP4, UART,  DAP4,   RSVD,   GMI,       RSVD,        DAP4),
+	PIN(DTA,  VI,    RSVD,   SDIO2,  VI,        RSVD,        RSVD4),
+	PIN(DTB,  VI,    RSVD,   RSVD,   VI,        SPI1,        RSVD1),
+	PIN(DTC,  VI,    RSVD,   RSVD,   VI,        RSVD,        RSVD1),
+	PIN(DTD,  VI,    RSVD,   SDIO2,  VI,        RSVD,        RSVD1),
+	PIN(DTE,  VI,    RSVD,   RSVD,   VI,        SPI1,        RSVD1),
+
+	PINP(GPU, UART,  PWM,    UARTA,  GMI,       RSVD,        RSVD4,
+		GPSLXAU),
+	PIN(GPV,  SD,    PCIE,   RSVD,   RSVD,      RSVD,        PCIE),
+	PIN(I2CP, SYS,   I2C,    RSVD,   RSVD,      RSVD,        RSVD4),
+	PIN(IRTX, UART,  UARTA,  UARTB,  GMI,       SPI4,        UARTB),
+	PIN(IRRX, UART,  UARTA,  UARTB,  GMI,       SPI4,        UARTB),
+	PIN(KBCB, SYS,   KBC,    NAND,   SDIO2,     MIO,         KBC),
+	PIN(KBCA, SYS,   KBC,    NAND,   SDIO2,     EMC_TEST0_DLL, KBC),
+	PINP(PMC, SYS,   PWR_ON, PWR_INTR, RSVD,    RSVD,        PWR_ON, NONE),
+
+	PIN(PTA,  NAND,  I2C2,   HDMI,   GMI,       RSVD,        RSVD4),
+	PIN(RM,   UART,  I2C,    RSVD,   RSVD,      RSVD,        RSVD4),
+	PIN(KBCE, SYS,   KBC,    NAND,   OWR,       RSVD,        KBC),
+	PIN(KBCF, SYS,   KBC,    NAND,   TRACE,     MIO,         KBC),
+	PIN(GMA,  NAND,  UARTE,  SPI3,   GMI,       SDIO4,       SPI3),
+	PIN(GMC,  NAND,  UARTD,  SPI4,   GMI,       SFLASH,      SPI4),
+	PIN(SDMMC1, BB,  SDIO1,  RSVD,   UARTE,     UARTA,       RSVD2),
+	PIN(OWC,  SYS,   OWR,    RSVD,   RSVD,      RSVD,        OWR),
+
+	PIN(GME,  NAND,  RSVD,   DAP5,   GMI,       SDIO4,       GMI),
+	PIN(SDC,  SD,    PWM,    TWC,    SDIO3,     SPI3,        TWC),
+	PIN(SDD,  SD,    UARTA,  PWM,    SDIO3,     SPI3,        PWM),
+	PIN_RESERVED,
+	PINP(SLXA, SD,   PCIE,   SPI4,   SDIO3,     SPI2,        PCIE, CRTP),
+	PIN(SLXC, SD,    SPDIF,  SPI4,   SDIO3,     SPI2,        SPI4),
+	PIN(SLXD, SD,    SPDIF,  SPI4,   SDIO3,     SPI2,        SPI4),
+	PIN(SLXK, SD,    PCIE,   SPI4,   SDIO3,     SPI2,        PCIE),
+
+	PIN(SPDI, AUDIO, SPDIF,  RSVD,   I2C,       SDIO2,       RSVD2),
+	PIN(SPDO, AUDIO, SPDIF,  RSVD,   I2C,       SDIO2,       RSVD2),
+	PIN(SPIA, AUDIO, SPI1,   SPI2,   SPI3,      GMI,         GMI),
+	PIN(SPIB, AUDIO, SPI1,   SPI2,   SPI3,      GMI,         GMI),
+	PIN(SPIC, AUDIO, SPI1,   SPI2,   SPI3,      GMI,         GMI),
+	PIN(SPID, AUDIO, SPI2,   SPI1,   SPI2_ALT,  GMI,         GMI),
+	PIN(SPIE, AUDIO, SPI2,   SPI1,   SPI2_ALT,  GMI,         GMI),
+	PIN(SPIF, AUDIO, SPI3,   SPI1,   SPI2,      RSVD,        RSVD4),
+
+	PIN(SPIG, AUDIO, SPI3,   SPI2,   SPI2_ALT,  I2C,         SPI2_ALT),
+	PIN(SPIH, AUDIO, SPI3,   SPI2,   SPI2_ALT,  I2C,         SPI2_ALT),
+	PIN(UAA,  BB,    SPI3,   MIPI_HS, UARTA,    ULPI,        MIPI_HS),
+	PIN(UAB,  BB,    SPI2,   MIPI_HS, UARTA,    ULPI,        MIPI_HS),
+	PIN(UAC,  BB,    OWR,    RSVD,   RSVD,      RSVD,        RSVD4),
+	PIN(UAD,  UART,  IRDA,   SPDIF,  UARTA,     SPI4,        SPDIF),
+	PIN(UCA,  UART,  UARTC,  RSVD,   GMI,       RSVD,        RSVD4),
+	PIN(UCB,  UART,  UARTC,  PWM,    GMI,       RSVD,        RSVD4),
+
+	PIN_RESERVED,
+	PIN(ATE,  NAND,  IDE,    NAND,   GMI,       RSVD,        IDE),
+	PIN(KBCC, SYS,   KBC,    NAND,   TRACE,     EMC_TEST1_DLL, KBC),
+	PIN_RESERVED,
+	PIN_RESERVED,
+	PIN(GMB,  NAND,  IDE,    NAND,   GMI,       GMI_INT,     GMI),
+	PIN(GMD,  NAND,  RSVD,   NAND,   GMI,       SFLASH,      GMI),
+	PIN(DDC,  LCD,   I2C2,   RSVD,   RSVD,      RSVD,        RSVD4),
+
+	/* 64 */
+	PINP(LD0,  LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17),
+	PINP(LD1,  LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17),
+	PINP(LD2,  LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17),
+	PINP(LD3,  LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17),
+	PINP(LD4,  LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17),
+	PINP(LD5,  LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17),
+	PINP(LD6,  LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17),
+	PINP(LD7,  LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17),
+
+	PINP(LD8,  LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17),
+	PINP(LD9,  LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17),
+	PINP(LD10, LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17),
+	PINP(LD11, LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17),
+	PINP(LD12, LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17),
+	PINP(LD13, LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17),
+	PINP(LD14, LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17),
+	PINP(LD15, LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17),
+
+	PINP(LD16, LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17),
+	PINP(LD17, LCD,  DISPA,  DISPB,  RSVD,      RSVD,     RSVD4, LD17),
+	PINP(LHP0, LCD,  DISPA,  DISPB,  RSVD,      RSVD,     RSVD4, LD21_20),
+	PINP(LHP1, LCD,  DISPA,  DISPB,  RSVD,      RSVD,     RSVD4, LD19_18),
+	PINP(LHP2, LCD,  DISPA,  DISPB,  RSVD,      RSVD,     RSVD4, LD19_18),
+	PINP(LVP0, LCD,  DISPA,  DISPB,  RSVD,      RSVD,     RSVD4, LC),
+	PINP(LVP1, LCD,  DISPA,  DISPB,  RSVD,      RSVD,     RSVD4, LD21_20),
+	PINP(HDINT, LCD, HDMI,   RSVD,   RSVD,      RSVD,     HDMI , LC),
+
+	PINP(LM0,  LCD,  DISPA,  DISPB,  SPI3,      RSVD,     RSVD4, LC),
+	PINP(LM1,  LCD,  DISPA,  DISPB,  RSVD,      CRT,      RSVD3, LC),
+	PINP(LVS,  LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LC),
+	PINP(LSC0, LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LC),
+	PINP(LSC1, LCD,  DISPA,  DISPB,  SPI3,      HDMI,     DISPA, LS),
+	PINP(LSCK, LCD,  DISPA,  DISPB,  SPI3,      HDMI,     DISPA, LS),
+	PINP(LDC,  LCD,  DISPA,  DISPB,  RSVD,      RSVD,     RSVD4, LS),
+	PINP(LCSN, LCD,  DISPA,  DISPB,  SPI3,      RSVD,     RSVD4, LS),
+
+	/* 96 */
+	PINP(LSPI, LCD,  DISPA,  DISPB,  XIO,       HDMI,     DISPA, LC),
+	PINP(LSDA, LCD,  DISPA,  DISPB,  SPI3,      HDMI,     DISPA, LS),
+	PINP(LSDI, LCD,  DISPA,  DISPB,  SPI3,      RSVD,     DISPA, LS),
+	PINP(LPW0, LCD,  DISPA,  DISPB,  SPI3,      HDMI,     DISPA, LS),
+	PINP(LPW1, LCD,  DISPA,  DISPB,  RSVD,      RSVD,     RSVD4, LS),
+	PINP(LPW2, LCD,  DISPA,  DISPB,  SPI3,      HDMI,     DISPA, LS),
+	PINP(LDI,  LCD,  DISPA,  DISPB,  RSVD,      RSVD,     RSVD4, LD23_22),
+	PINP(LHS,  LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LC),
+
+	PINP(LPP,  LCD,  DISPA,  DISPB,  RSVD,      RSVD,     RSVD4, LD23_22),
+	PIN_RESERVED,
+	PIN(KBCD,  SYS,  KBC,    NAND,   SDIO2,     MIO,      KBC),
+	PIN(GPU7,  SYS,  RTCK,   RSVD,   RSVD,      RSVD,     RTCK),
+	PIN(DTF,   VI,   I2C3,   RSVD,   VI,        RSVD,     RSVD4),
+	PIN(UDA,   BB,   SPI1,   RSVD,   UARTD,     ULPI,     RSVD2),
+	PIN(CRTP,  LCD,  CRT,    RSVD,   RSVD,      RSVD,     RSVD),
+	PINP(SDB,  SD,   UARTA,  PWM,    SDIO3,     SPI2,     PWM,   NONE),
+
+	/* these pin groups only have pullup and pull down control */
+	PINALL(CK32,  SYS,   RSVD, RSVD, RSVD, RSVD,  RSVD, MUXCTL_NONE,
+		PUCTL_NONE),
+	PINALL(DDRC,  DDR,   RSVD, RSVD, RSVD, RSVD,  RSVD, MUXCTL_NONE,
+		PUCTL_NONE),
+	PINALL(PMCA,  SYS,   RSVD, RSVD, RSVD, RSVD,  RSVD, MUXCTL_NONE,
+		PUCTL_NONE),
+	PINALL(PMCB,  SYS,   RSVD, RSVD, RSVD, RSVD,  RSVD, MUXCTL_NONE,
+		PUCTL_NONE),
+	PINALL(PMCC,  SYS,   RSVD, RSVD, RSVD, RSVD,  RSVD, MUXCTL_NONE,
+		PUCTL_NONE),
+	PINALL(PMCD,  SYS,   RSVD, RSVD, RSVD, RSVD,  RSVD, MUXCTL_NONE,
+		PUCTL_NONE),
+	PINALL(PMCE,  SYS,   RSVD, RSVD, RSVD, RSVD,  RSVD, MUXCTL_NONE,
+		PUCTL_NONE),
+	PINALL(XM2C,  DDR,   RSVD, RSVD, RSVD, RSVD,  RSVD, MUXCTL_NONE,
+		PUCTL_NONE),
+	PINALL(XM2D,  DDR,   RSVD, RSVD, RSVD, RSVD,  RSVD, MUXCTL_NONE,
+		PUCTL_NONE),
+};
 
 void pinmux_set_tristate(enum pmux_pingrp pin, int enable)
 {
-	struct pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE;
+	struct pmux_tri_ctlr *pmt =
+			(struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE;
 	u32 *tri = &pmt->pmt_tri[TRISTATE_REG(pin)];
 	u32 reg;
 
@@ -50,3 +516,69 @@ void pinmux_tristate_disable(enum pmux_pingrp pin)
 {
 	pinmux_set_tristate(pin, 0);
 }
+
+void pinmux_set_pullupdown(enum pmux_pingrp pin, enum pmux_pull pupd)
+{
+	struct pmux_tri_ctlr *pmt =
+			(struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE;
+	enum pmux_pullid pull_id = tegra_soc_pingroups[pin].pull_id;
+	u32 *pull = &pmt->pmt_pull[PULL_REG(pull_id)];
+	u32 mask_bit;
+	u32 reg;
+	mask_bit = PULL_SHIFT(pull_id);
+
+	reg = readl(pull);
+	reg &= ~(0x3 << mask_bit);
+	reg |= pupd << mask_bit;
+	writel(reg, pull);
+}
+
+void pinmux_set_func(enum pmux_pingrp pin, enum pmux_func func)
+{
+	struct pmux_tri_ctlr *pmt =
+			(struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE;
+	enum pmux_ctlid mux_id = tegra_soc_pingroups[pin].ctl_id;
+	u32 *muxctl = &pmt->pmt_ctl[MUXCTL_REG(mux_id)];
+	u32 mask_bit;
+	int i, mux = -1;
+	u32 reg;
+
+	assert(pmux_func_isvalid(func));
+
+	/* Handle special values */
+	if (func >= PMUX_FUNC_RSVD1) {
+		mux = (func - PMUX_FUNC_RSVD1) & 0x3;
+	} else {
+		/* Search for the appropriate function */
+		for (i = 0; i < 4; i++) {
+			if (tegra_soc_pingroups[pin].funcs[i] == func) {
+				mux = i;
+				break;
+			}
+		}
+	}
+	assert(mux != -1);
+
+	mask_bit = MUXCTL_SHIFT(mux_id);
+	reg = readl(muxctl);
+	reg &= ~(0x3 << mask_bit);
+	reg |= mux << mask_bit;
+	writel(reg, muxctl);
+}
+
+void pinmux_config_pingroup(struct pingroup_config *config)
+{
+	enum pmux_pingrp pin = config->pingroup;
+
+	pinmux_set_func(pin, config->func);
+	pinmux_set_pullupdown(pin, config->pull);
+	pinmux_set_tristate(pin, config->tristate);
+}
+
+void pinmux_config_table(struct pingroup_config *config, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		pinmux_config_pingroup(&config[i]);
+}
diff --git a/arch/arm/include/asm/arch-tegra2/pinmux.h b/arch/arm/include/asm/arch-tegra2/pinmux.h
index e8ef632..469d742 100644
--- a/arch/arm/include/asm/arch-tegra2/pinmux.h
+++ b/arch/arm/include/asm/arch-tegra2/pinmux.h
@@ -24,7 +24,14 @@
 #ifndef _PINMUX_H_
 #define _PINMUX_H_
 
-/* Pin groups which we can set to tristate or normal */
+/*
+ * Pin groups which we adjust. There are three basic attributes of each pin
+ * group which use this enum:
+ *
+ *	- function
+ *	- pullup / pulldown
+ *	- tristate or normal
+ */
 enum pmux_pingrp {
 	/* APB_MISC_PP_TRISTATE_REG_A_0 */
 	PINGRP_ATA,
@@ -155,37 +162,169 @@ enum pmux_pingrp {
 	PINGRP_UDA,
 	PINGRP_CRTP,
 	PINGRP_SDB,
+
+	/* these pin groups only have pullup and pull down control */
+	PINGRP_FIRST_NO_MUX,
+	PINGRP_CK32 = PINGRP_FIRST_NO_MUX,
+	PINGRP_DDRC,
+	PINGRP_PMCA,
+	PINGRP_PMCB,
+	PINGRP_PMCC,
+	PINGRP_PMCD,
+	PINGRP_PMCE,
+	PINGRP_XM2C,
+	PINGRP_XM2D,
+
+	PINGRP_COUNT,
+};
+
+/*
+ * Functions which can be assigned to each of the pin groups. The values here
+ * bear no relation to the values programmed into pinmux registers and are
+ * purely a convenience. The translation is done through a table search.
+ */
+enum pmux_func {
+	PMUX_FUNC_AHB_CLK,
+	PMUX_FUNC_APB_CLK,
+	PMUX_FUNC_AUDIO_SYNC,
+	PMUX_FUNC_CRT,
+	PMUX_FUNC_DAP1,
+	PMUX_FUNC_DAP2,
+	PMUX_FUNC_DAP3,
+	PMUX_FUNC_DAP4,
+	PMUX_FUNC_DAP5,
+	PMUX_FUNC_DISPA,
+	PMUX_FUNC_DISPB,
+	PMUX_FUNC_EMC_TEST0_DLL,
+	PMUX_FUNC_EMC_TEST1_DLL,
+	PMUX_FUNC_GMI,
+	PMUX_FUNC_GMI_INT,
+	PMUX_FUNC_HDMI,
+	PMUX_FUNC_I2C,
+	PMUX_FUNC_I2C2,
+	PMUX_FUNC_I2C3,
+	PMUX_FUNC_IDE,
+	PMUX_FUNC_IRDA,
+	PMUX_FUNC_KBC,
+	PMUX_FUNC_MIO,
+	PMUX_FUNC_MIPI_HS,
+	PMUX_FUNC_NAND,
+	PMUX_FUNC_OSC,
+	PMUX_FUNC_OWR,
+	PMUX_FUNC_PCIE,
+	PMUX_FUNC_PLLA_OUT,
+	PMUX_FUNC_PLLC_OUT1,
+	PMUX_FUNC_PLLM_OUT1,
+	PMUX_FUNC_PLLP_OUT2,
+	PMUX_FUNC_PLLP_OUT3,
+	PMUX_FUNC_PLLP_OUT4,
+	PMUX_FUNC_PWM,
+	PMUX_FUNC_PWR_INTR,
+	PMUX_FUNC_PWR_ON,
+	PMUX_FUNC_RTCK,
+	PMUX_FUNC_SDIO1,
+	PMUX_FUNC_SDIO2,
+	PMUX_FUNC_SDIO3,
+	PMUX_FUNC_SDIO4,
+	PMUX_FUNC_SFLASH,
+	PMUX_FUNC_SPDIF,
+	PMUX_FUNC_SPI1,
+	PMUX_FUNC_SPI2,
+	PMUX_FUNC_SPI2_ALT,
+	PMUX_FUNC_SPI3,
+	PMUX_FUNC_SPI4,
+	PMUX_FUNC_TRACE,
+	PMUX_FUNC_TWC,
+	PMUX_FUNC_UARTA,
+	PMUX_FUNC_UARTB,
+	PMUX_FUNC_UARTC,
+	PMUX_FUNC_UARTD,
+	PMUX_FUNC_UARTE,
+	PMUX_FUNC_ULPI,
+	PMUX_FUNC_VI,
+	PMUX_FUNC_VI_SENSOR_CLK,
+	PMUX_FUNC_XIO,
+	PMUX_FUNC_SAFE,
+
+	/* These don't have a name, but can be used in the table */
+	PMUX_FUNC_RSVD1,
+	PMUX_FUNC_RSVD2,
+	PMUX_FUNC_RSVD3,
+	PMUX_FUNC_RSVD4,
+	PMUX_FUNC_RSVD,	/* Not valid and should not be used */
+
+	PMUX_FUNC_COUNT,
+
+	PMUX_FUNC_NONE = -1,
 };
 
+/* return 1 if a pmux_func is in range */
+#define pmux_func_isvalid(func) ((func) >= 0 && (func) < PMUX_FUNC_COUNT && \
+		(func) != PMUX_FUNC_RSVD)
+
+/* The pullup/pulldown state of a pin group */
+enum pmux_pull {
+	PMUX_PULL_NORMAL = 0,
+	PMUX_PULL_DOWN,
+	PMUX_PULL_UP,
+};
+
+/* Defines whether a pin group is tristated or in normal operation */
+enum pmux_tristate {
+	PMUX_TRI_NORMAL = 0,
+	PMUX_TRI_TRISTATE = 1,
+};
 
-#define TEGRA_TRISTATE_REGS 4
+/* Available power domains used by pin groups */
+enum pmux_vddio {
+	PMUX_VDDIO_BB = 0,
+	PMUX_VDDIO_LCD,
+	PMUX_VDDIO_VI,
+	PMUX_VDDIO_UART,
+	PMUX_VDDIO_DDR,
+	PMUX_VDDIO_NAND,
+	PMUX_VDDIO_SYS,
+	PMUX_VDDIO_AUDIO,
+	PMUX_VDDIO_SD,
+
+	PMUX_VDDIO_NONE
+};
+
+enum {
+	PMUX_TRISTATE_REGS	= 4,
+	PMUX_MUX_REGS		= 7,
+	PMUX_PULL_REGS		= 5,
+};
 
 /* APB MISC Pin Mux and Tristate (APB_MISC_PP_) registers */
 struct pmux_tri_ctlr {
 	uint pmt_reserved0;		/* ABP_MISC_PP_ reserved offset 00 */
 	uint pmt_reserved1;		/* ABP_MISC_PP_ reserved offset 04 */
-	uint pmt_strap_opt_a;		/* _STRAPPING_OPT_A_0, offset 08 */
+	uint pmt_strap_opt_a;		/* _STRAPPING_OPT_A_0, offset 08   */
 	uint pmt_reserved2;		/* ABP_MISC_PP_ reserved offset 0C */
 	uint pmt_reserved3;		/* ABP_MISC_PP_ reserved offset 10 */
-	uint pmt_tri[TEGRA_TRISTATE_REGS]; /* _TRI_STATE_REG_A/B/C/D_0 14-20 */
-	uint pmt_cfg_ctl;		/* _CONFIG_CTL_0, offset 24 */
+	uint pmt_tri[PMUX_TRISTATE_REGS];/* _TRI_STATE_REG_A/B/C/D_0 14-20 */
+	uint pmt_cfg_ctl;		/* _CONFIG_CTL_0, offset 24        */
 
 	uint pmt_reserved[22];		/* ABP_MISC_PP_ reserved offs 28-7C */
 
-	uint pmt_ctl_a;			/* _PINGRP_MUX_CTL_A_0, offset 80 */
-	uint pmt_ctl_b;			/* _PINGRP_MUX_CTL_B_0, offset 84 */
-	uint pmt_ctl_c;			/* _PINGRP_MUX_CTL_C_0, offset 88 */
-	uint pmt_ctl_d;			/* _PINGRP_MUX_CTL_D_0, offset 8C */
-	uint pmt_ctl_e;			/* _PINGRP_MUX_CTL_E_0, offset 90 */
-	uint pmt_ctl_f;			/* _PINGRP_MUX_CTL_F_0, offset 94 */
-	uint pmt_ctl_g;			/* _PINGRP_MUX_CTL_G_0, offset 98 */
+	uint pmt_ctl[PMUX_MUX_REGS];	/* _PIN_MUX_CTL_A-G_0, offset 80   */
+	uint pmt_reserved4;		/* ABP_MISC_PP_ reserved offset 9c */
+	uint pmt_pull[PMUX_PULL_REGS];	/* APB_MISC_PP_PULLUPDOWN_REG_A-E  */
 };
 
-/* Converts a pin group to a tristate register: 0=A, 1=B, 2=C, 3=D */
-#define TRISTATE_REG(id) ((id) >> 5)
-
-/* Mask value for a tristate (within TRISTATE_REG(id)) */
-#define TRISTATE_MASK(id) (1 << ((id) & 0x1f))
+/*
+ * This defines the configuration for a pin, including the function assigned,
+ * pull up/down settings and tristate settings. Having set up one of these
+ * you can call pinmux_config_pingroup() to configure a pin in one step. Also
+ * available is pinmux_config_table() to configure a list of pins.
+ */
+struct pingroup_config {
+	enum pmux_pingrp pingroup;	/* pin group PINGRP_...             */
+	enum pmux_func func;		/* function to assign FUNC_...      */
+	enum pmux_pull pull;		/* pull up/down/normal PMUX_PULL_...*/
+	enum pmux_tristate tristate;	/* tristate or normal PMUX_TRI_...  */
+};
 
 /* Set a pin group to tristate */
 void pinmux_tristate_enable(enum pmux_pingrp pin);
@@ -193,4 +332,23 @@ void pinmux_tristate_enable(enum pmux_pingrp pin);
 /* Set a pin group to normal (non tristate) */
 void pinmux_tristate_disable(enum pmux_pingrp pin);
 
+/* Set the pull up/down feature for a pin group */
+void pinmux_set_pullupdown(enum pmux_pingrp pin, enum pmux_pull pupd);
+
+/* Set the mux function for a pin group */
+void pinmux_set_func(enum pmux_pingrp pin, enum pmux_func func);
+
+/* Set the complete configuration for a pin group */
+void pinmux_config_pingroup(struct pingroup_config *config);
+
+void pinmux_set_tristate(enum pmux_pingrp pin, int enable);
+
+/**
+ * Configuure a list of pin groups
+ *
+ * @param config	List of config items
+ * @param len		Number of config items in list
+ */
+void pinmux_config_table(struct pingroup_config *config, int len);
+
 #endif	/* PINMUX_H */
diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c
index 35ff2ef..5ba7bda 100644
--- a/board/nvidia/common/board.c
+++ b/board/nvidia/common/board.c
@@ -86,21 +86,15 @@ static void clock_init_uart(void)
  */
 static void pin_mux_uart(void)
 {
-	struct pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE;
-	u32 reg;
-
 #if defined(CONFIG_TEGRA2_ENABLE_UARTA)
-	reg = readl(&pmt->pmt_ctl_c);
-	reg &= 0xFFF0FFFF;	/* IRRX_/IRTX_SEL [19:16] = 00 UARTA */
-	writel(reg, &pmt->pmt_ctl_c);
+	pinmux_set_func(PINGRP_IRRX, PMUX_FUNC_UARTA);
+	pinmux_set_func(PINGRP_IRTX, PMUX_FUNC_UARTA);
 
 	pinmux_tristate_disable(PINGRP_IRRX);
 	pinmux_tristate_disable(PINGRP_IRTX);
 #endif	/* CONFIG_TEGRA2_ENABLE_UARTA */
 #if defined(CONFIG_TEGRA2_ENABLE_UARTD)
-	reg = readl(&pmt->pmt_ctl_b);
-	reg &= 0xFFFFFFF3;	/* GMC_SEL [3:2] = 00, UARTD */
-	writel(reg, &pmt->pmt_ctl_b);
+	pinmux_set_func(PINGRP_GMC, PMUX_FUNC_UARTD);
 
 	pinmux_tristate_disable(PINGRP_GMC);
 #endif	/* CONFIG_TEGRA2_ENABLE_UARTD */
@@ -123,33 +117,19 @@ static void clock_init_mmc(void)
  */
 static void pin_mux_mmc(void)
 {
-	struct pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE;
-	u32 reg;
-
-	/* SDMMC4 */
-	/* config 2, x8 on 2nd set of pins */
-	reg = readl(&pmt->pmt_ctl_a);
-	reg |= (3 << 16);	/* ATB_SEL [17:16] = 11 SDIO4 */
-	writel(reg, &pmt->pmt_ctl_a);
-	reg = readl(&pmt->pmt_ctl_b);
-	reg |= (3 << 0);	/* GMA_SEL [1:0] = 11 SDIO4 */
-	writel(reg, &pmt->pmt_ctl_b);
-	reg = readl(&pmt->pmt_ctl_d);
-	reg |= (3 << 0);	/* GME_SEL [1:0] = 11 SDIO4 */
-	writel(reg, &pmt->pmt_ctl_d);
+	/* SDMMC4: config 3, x8 on 2nd set of pins */
+	pinmux_set_func(PINGRP_ATB, PMUX_FUNC_SDIO4);
+	pinmux_set_func(PINGRP_GMA, PMUX_FUNC_SDIO4);
+	pinmux_set_func(PINGRP_GME, PMUX_FUNC_SDIO4);
 
 	pinmux_tristate_disable(PINGRP_ATB);
 	pinmux_tristate_disable(PINGRP_GMA);
 	pinmux_tristate_disable(PINGRP_GME);
 
-	/* SDMMC3 */
-	/* SDIO3_CLK, SDIO3_CMD, SDIO3_DAT[3:0] */
-	reg = readl(&pmt->pmt_ctl_d);
-	reg &= 0xFFFF03FF;
-	reg |= (2 << 10);	/* SDB_SEL [11:10] = 01 SDIO3 */
-	reg |= (2 << 12);	/* SDC_SEL [13:12] = 01 SDIO3 */
-	reg |= (2 << 14);	/* SDD_SEL [15:14] = 01 SDIO3 */
-	writel(reg, &pmt->pmt_ctl_d);
+	/* SDMMC3: SDIO3_CLK, SDIO3_CMD, SDIO3_DAT[3:0] */
+	pinmux_set_func(PINGRP_SDB, PMUX_FUNC_SDIO3);
+	pinmux_set_func(PINGRP_SDC, PMUX_FUNC_SDIO3);
+	pinmux_set_func(PINGRP_SDD, PMUX_FUNC_SDIO3);
 
 	pinmux_tristate_disable(PINGRP_SDC);
 	pinmux_tristate_disable(PINGRP_SDD);
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [U-Boot] [PATCH 6/6] tegra2: Enable MMC for Seaboard
  2011-09-08 22:11 [U-Boot] [PATCH 0/6] tegra2: Add more clock/pinmux functionality Simon Glass
                   ` (4 preceding siblings ...)
  2011-09-08 22:12 ` [U-Boot] [PATCH 5/6] tegra2: Add more pinmux functions Simon Glass
@ 2011-09-08 22:12 ` Simon Glass
  2011-09-12  4:37 ` [U-Boot] [PATCH 0/6] tegra2: Add more clock/pinmux functionality Simon Glass
  2011-09-21 20:43 ` Tom Warren
  7 siblings, 0 replies; 10+ messages in thread
From: Simon Glass @ 2011-09-08 22:12 UTC (permalink / raw)
  To: u-boot

From: Tom Warren <twarren@nvidia.com>

This adds the required GPIO and pinmux configuration to make eMMC / SD work
on Seaboard.

Signed-off-by: Simon Glass <sjg@chromium.org>
---
 board/nvidia/common/board.c      |   13 +------------
 board/nvidia/common/board.h      |    1 +
 board/nvidia/harmony/harmony.c   |   26 ++++++++++++++++++++++++++
 board/nvidia/seaboard/seaboard.c |   35 ++++++++++++++++++++++++++++++++++-
 drivers/mmc/tegra2_mmc.c         |    2 +-
 5 files changed, 63 insertions(+), 14 deletions(-)

diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c
index 5ba7bda..d13537d 100644
--- a/board/nvidia/common/board.c
+++ b/board/nvidia/common/board.c
@@ -160,6 +160,7 @@ int board_mmc_init(bd_t *bd)
 	/* Enable clocks, muxes, etc. for SDMMC controllers */
 	clock_init_mmc();
 	pin_mux_mmc();
+	gpio_config_mmc();
 
 	debug("board_mmc_init: init eMMC\n");
 	/* init dev 0, eMMC chip, with 4-bit bus */
@@ -171,18 +172,6 @@ int board_mmc_init(bd_t *bd)
 
 	return 0;
 }
-
-/* this is a weak define that we are overriding */
-int board_mmc_getcd(u8 *cd, struct mmc *mmc)
-{
-	debug("board_mmc_getcd called\n");
-	/*
-	 * Hard-code CD presence for now. Need to add GPIO inputs
-	 * for Seaboard & Harmony (& Kaen/Aebl/Wario?)
-	 */
-	*cd = 1;
-	return 0;
-}
 #endif
 
 #ifdef CONFIG_BOARD_EARLY_INIT_F
diff --git a/board/nvidia/common/board.h b/board/nvidia/common/board.h
index d649eb7..344e702 100644
--- a/board/nvidia/common/board.h
+++ b/board/nvidia/common/board.h
@@ -26,6 +26,7 @@
 
 void tegra2_start(void);
 void gpio_config_uart(void);
+void gpio_config_mmc(void);
 int tegra2_mmc_init(int dev_index, int bus_width);
 
 #endif	/* BOARD_H */
diff --git a/board/nvidia/harmony/harmony.c b/board/nvidia/harmony/harmony.c
index f1ab050..cbb30d6 100644
--- a/board/nvidia/harmony/harmony.c
+++ b/board/nvidia/harmony/harmony.c
@@ -24,6 +24,9 @@
 #include <common.h>
 #include <asm/io.h>
 #include <asm/arch/tegra2.h>
+#ifdef CONFIG_TEGRA2_MMC
+#include <mmc.h>
+#endif
 
 /*
  * Routine: gpio_config_uart
@@ -32,3 +35,26 @@
 void gpio_config_uart(void)
 {
 }
+
+#ifdef CONFIG_TEGRA2_MMC
+/*
+ * Routine: gpio_config_mmc
+ * Description: Set GPIOs for SD card
+ */
+void gpio_config_mmc(void)
+{
+	/* Not implemented for now */
+}
+
+/* this is a weak define that we are overriding */
+int board_mmc_getcd(u8 *cd, struct mmc *mmc)
+{
+	debug("board_mmc_getcd called\n");
+	/*
+	 * Hard-code CD presence for now. Need to add GPIO inputs
+	 * for Harmony
+	 */
+	*cd = 1;
+	return 0;
+}
+#endif
diff --git a/board/nvidia/seaboard/seaboard.c b/board/nvidia/seaboard/seaboard.c
index 4b9a8f3..bc67d0f 100644
--- a/board/nvidia/seaboard/seaboard.c
+++ b/board/nvidia/seaboard/seaboard.c
@@ -24,7 +24,10 @@
 #include <common.h>
 #include <asm/io.h>
 #include <asm/arch/tegra2.h>
-#include <asm/arch/gpio.h>
+#include <asm/gpio.h>
+#ifdef CONFIG_TEGRA2_MMC
+#include <mmc.h>
+#endif
 
 /*
  * Routine: gpio_config_uart
@@ -50,3 +53,33 @@ void gpio_config_uart(void)
 	val |= 1 << GPIO_BIT(gp);
 	writel(val, &bank->gpio_dir_out[GPIO_PORT(gp)]);
 }
+
+#ifdef CONFIG_TEGRA2_MMC
+/*
+ * Routine: gpio_config_mmc
+ * Description: Set GPIOs for SDMMC3 SDIO slot.
+ */
+void gpio_config_mmc(void)
+{
+	/* Set EN_VDDIO_SD (GPIO I6) */
+	gpio_direction_output(GPIO_PI6, 1);
+
+	/* Config pin as GPI for Card Detect (GPIO I5) */
+	gpio_direction_input(GPIO_PI5);
+}
+
+/* this is a weak define that we are overriding */
+int board_mmc_getcd(u8 *cd, struct mmc *mmc)
+{
+	debug("board_mmc_getcd called\n");
+	*cd = 1;			/* Assume card is inserted, or eMMC */
+
+	if (IS_SD(mmc)) {
+		/* Seaboard SDMMC3 = SDIO3_CD = GPIO_PI5 */
+		if (gpio_get_value(GPIO_PI5))
+			*cd = 0;
+	}
+
+	return 0;
+}
+#endif
diff --git a/drivers/mmc/tegra2_mmc.c b/drivers/mmc/tegra2_mmc.c
index a71785c..9e741f2 100644
--- a/drivers/mmc/tegra2_mmc.c
+++ b/drivers/mmc/tegra2_mmc.c
@@ -452,7 +452,7 @@ static int tegra2_mmc_initialize(int dev_index, int bus_width)
 		mmc->host_caps = MMC_MODE_8BIT;
 	else
 		mmc->host_caps = MMC_MODE_4BIT;
-	mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
+	mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC;
 
 	/*
 	 * min freq is for card identification, and is the highest
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [U-Boot] [PATCH 0/6] tegra2: Add more clock/pinmux functionality
  2011-09-08 22:11 [U-Boot] [PATCH 0/6] tegra2: Add more clock/pinmux functionality Simon Glass
                   ` (5 preceding siblings ...)
  2011-09-08 22:12 ` [U-Boot] [PATCH 6/6] tegra2: Enable MMC for Seaboard Simon Glass
@ 2011-09-12  4:37 ` Simon Glass
  2011-09-21 20:43 ` Tom Warren
  7 siblings, 0 replies; 10+ messages in thread
From: Simon Glass @ 2011-09-12  4:37 UTC (permalink / raw)
  To: u-boot

Hi,

On Thu, Sep 8, 2011 at 3:11 PM, Simon Glass <sjg@chromium.org> wrote:
> This adds to the basic clock functionality already available. The concept
> of a peripheral ID is introduced, and all peripheral clock access is done
> using this ID.
>
[snip]
> Note: These patches include a definition of assert() which I will happily
> remove if the one sent upstream is accepted.

Now that assert() is in master, this needs to be removed. I will send
a new patch set later in the week to tidy this up - just want to allow
a few more days for any comments to come through.

Regards,
Simon

>
>
> Simon Glass (6):
> ?tegra2: Rename CLOCK_PLL_ID to CLOCK_ID
> ?tegra2: Clean up board code a little
> ?tegra2: Add more clock functions
> ?tegra2: Rename PIN_ to PINGRP_
> ?tegra2: Add more pinmux functions
> ?tegra2: Enable MMC for Seaboard
>
> ?arch/arm/cpu/armv7/tegra2/ap20.c ? ? ? ? ? | ? ?5 +-
> ?arch/arm/cpu/armv7/tegra2/clock.c ? ? ? ? ?| ?840 +++++++++++++++++++++++++++-
> ?arch/arm/cpu/armv7/tegra2/pinmux.c ? ? ? ? | ?540 ++++++++++++++++++-
> ?arch/arm/include/asm/arch-tegra2/clk_rst.h | ? 84 +--
> ?arch/arm/include/asm/arch-tegra2/clock.h ? | ?149 ++++-
> ?arch/arm/include/asm/arch-tegra2/pinmux.h ?| ?444 ++++++++++-----
> ?board/nvidia/common/board.c ? ? ? ? ? ? ? ?| ?243 +++------
> ?board/nvidia/common/board.h ? ? ? ? ? ? ? ?| ? ?4 +-
> ?board/nvidia/harmony/harmony.c ? ? ? ? ? ? | ? 26 +
> ?board/nvidia/seaboard/seaboard.c ? ? ? ? ? | ? 35 ++-
> ?drivers/mmc/tegra2_mmc.c ? ? ? ? ? ? ? ? ? | ? 94 +--
> ?drivers/mmc/tegra2_mmc.h ? ? ? ? ? ? ? ? ? | ? ?1 +
> ?12 files changed, 1972 insertions(+), 493 deletions(-)
>
> --
> 1.7.3.1
>
>

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [U-Boot] [PATCH 0/6] tegra2: Add more clock/pinmux functionality
  2011-09-08 22:11 [U-Boot] [PATCH 0/6] tegra2: Add more clock/pinmux functionality Simon Glass
                   ` (6 preceding siblings ...)
  2011-09-12  4:37 ` [U-Boot] [PATCH 0/6] tegra2: Add more clock/pinmux functionality Simon Glass
@ 2011-09-21 20:43 ` Tom Warren
  2011-09-21 20:58   ` Simon Glass
  7 siblings, 1 reply; 10+ messages in thread
From: Tom Warren @ 2011-09-21 20:43 UTC (permalink / raw)
  To: u-boot

> -----Original Message-----
> From: Simon Glass [mailto:sjg at chromium.org]
> Sent: Thursday, September 08, 2011 3:12 PM
> To: U-Boot Mailing List
> Cc: Albert ARIBAUD; Tom Warren; Simon Glass
> Subject: [PATCH 0/6] tegra2: Add more clock/pinmux functionality
> 
> This adds to the basic clock functionality already available. The concept
> of a peripheral ID is introduced, and all peripheral clock access is done
> using this ID.
> 
> Functions are provided to start, query and adjust peripheral clocks,
> including automatic selection of the best available clock based on the
> requested rate (this replaces hard-coded divisors).
> 
> On the pinmux side we can now select functions for pin groups using the
> new pinmux_set_func() function.
> 
> Expanded functions are provided to adjust and query PLL clocks.
> 
> With a full compliment of clock and pinmux functions, it should no longer
> be necessary for board/driver code to directly access clock registers. This
> change removes all such accesses.
> 
> This functionality will be used for I2C, SPI, LCD, USB, keyboard, NAND and
> other drivers for Tegra2.
> 
> At then end is a patch to enable MMC on Seaboard, to make it all worthwhile.
> 
> Note: These patches include a definition of assert() which I will happily
> remove if the one sent upstream is accepted.
> 
> 
> Simon Glass (6):
>   tegra2: Rename CLOCK_PLL_ID to CLOCK_ID
>   tegra2: Clean up board code a little
>   tegra2: Add more clock functions
>   tegra2: Rename PIN_ to PINGRP_
>   tegra2: Add more pinmux functions
>   tegra2: Enable MMC for Seaboard
> 
>  arch/arm/cpu/armv7/tegra2/ap20.c           |    5 +-
>  arch/arm/cpu/armv7/tegra2/clock.c          |  840
> +++++++++++++++++++++++++++-
>  arch/arm/cpu/armv7/tegra2/pinmux.c         |  540 ++++++++++++++++++-
>  arch/arm/include/asm/arch-tegra2/clk_rst.h |   84 +--
>  arch/arm/include/asm/arch-tegra2/clock.h   |  149 ++++-
>  arch/arm/include/asm/arch-tegra2/pinmux.h  |  444 ++++++++++-----
>  board/nvidia/common/board.c                |  243 +++------
>  board/nvidia/common/board.h                |    4 +-
>  board/nvidia/harmony/harmony.c             |   26 +
>  board/nvidia/seaboard/seaboard.c           |   35 ++-
>  drivers/mmc/tegra2_mmc.c                   |   94 +--
>  drivers/mmc/tegra2_mmc.h                   |    1 +
>  12 files changed, 1972 insertions(+), 493 deletions(-)
> 
> --
> 1.7.3.1

This works fine on my Seaboard (T20-A03). My Harmony is put away, so I didn't test on it.

Tested-by: Tom Warren <twarren@nvidia.com>

Thanks,

Tom

-----------------------------------------------------------------------------------
This email message is for the sole use of the intended recipient(s) and may contain
confidential information.  Any unauthorized review, use, disclosure or distribution
is prohibited.  If you are not the intended recipient, please contact the sender by
reply email and destroy all copies of the original message.
-----------------------------------------------------------------------------------

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [U-Boot] [PATCH 0/6] tegra2: Add more clock/pinmux functionality
  2011-09-21 20:43 ` Tom Warren
@ 2011-09-21 20:58   ` Simon Glass
  0 siblings, 0 replies; 10+ messages in thread
From: Simon Glass @ 2011-09-21 20:58 UTC (permalink / raw)
  To: u-boot

Hi Tom,

On Wed, Sep 21, 2011 at 1:43 PM, Tom Warren <TWarren@nvidia.com> wrote:
>> -----Original Message-----
>> From: Simon Glass [mailto:sjg at chromium.org]
>> Sent: Thursday, September 08, 2011 3:12 PM
>> To: U-Boot Mailing List
>> Cc: Albert ARIBAUD; Tom Warren; Simon Glass
>> Subject: [PATCH 0/6] tegra2: Add more clock/pinmux functionality
>>
>> This adds to the basic clock functionality already available. The concept
>> of a peripheral ID is introduced, and all peripheral clock access is done
>> using this ID.
>>
>> Functions are provided to start, query and adjust peripheral clocks,
>> including automatic selection of the best available clock based on the
>> requested rate (this replaces hard-coded divisors).
>>
>> On the pinmux side we can now select functions for pin groups using the
>> new pinmux_set_func() function.
>>
>> Expanded functions are provided to adjust and query PLL clocks.
>>
>> With a full compliment of clock and pinmux functions, it should no longer
>> be necessary for board/driver code to directly access clock registers. This
>> change removes all such accesses.
>>
>> This functionality will be used for I2C, SPI, LCD, USB, keyboard, NAND and
>> other drivers for Tegra2.
>>
>> At then end is a patch to enable MMC on Seaboard, to make it all worthwhile.
>>
>> Note: These patches include a definition of assert() which I will happily
>> remove if the one sent upstream is accepted.
>>
>>
>> Simon Glass (6):
>> ? tegra2: Rename CLOCK_PLL_ID to CLOCK_ID
>> ? tegra2: Clean up board code a little
>> ? tegra2: Add more clock functions
>> ? tegra2: Rename PIN_ to PINGRP_
>> ? tegra2: Add more pinmux functions
>> ? tegra2: Enable MMC for Seaboard
>>
>> ?arch/arm/cpu/armv7/tegra2/ap20.c ? ? ? ? ? | ? ?5 +-
>> ?arch/arm/cpu/armv7/tegra2/clock.c ? ? ? ? ?| ?840
>> +++++++++++++++++++++++++++-
>> ?arch/arm/cpu/armv7/tegra2/pinmux.c ? ? ? ? | ?540 ++++++++++++++++++-
>> ?arch/arm/include/asm/arch-tegra2/clk_rst.h | ? 84 +--
>> ?arch/arm/include/asm/arch-tegra2/clock.h ? | ?149 ++++-
>> ?arch/arm/include/asm/arch-tegra2/pinmux.h ?| ?444 ++++++++++-----
>> ?board/nvidia/common/board.c ? ? ? ? ? ? ? ?| ?243 +++------
>> ?board/nvidia/common/board.h ? ? ? ? ? ? ? ?| ? ?4 +-
>> ?board/nvidia/harmony/harmony.c ? ? ? ? ? ? | ? 26 +
>> ?board/nvidia/seaboard/seaboard.c ? ? ? ? ? | ? 35 ++-
>> ?drivers/mmc/tegra2_mmc.c ? ? ? ? ? ? ? ? ? | ? 94 +--
>> ?drivers/mmc/tegra2_mmc.h ? ? ? ? ? ? ? ? ? | ? ?1 +
>> ?12 files changed, 1972 insertions(+), 493 deletions(-)
>>
>> --
>> 1.7.3.1
>
> This works fine on my Seaboard (T20-A03). My Harmony is put away, so I didn't test on it.
>
> Tested-by: Tom Warren <twarren@nvidia.com>

Thanks for testing it. I will remove the assert() now that it is in
U-Boot's common.h, and resend.

Regards,
Simon

>
> Thanks,
>
> Tom
>
> -----------------------------------------------------------------------------------
> This email message is for the sole use of the intended recipient(s) and may contain
> confidential information. ?Any unauthorized review, use, disclosure or distribution
> is prohibited. ?If you are not the intended recipient, please contact the sender by
> reply email and destroy all copies of the original message.
> -----------------------------------------------------------------------------------
>

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2011-09-21 20:58 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-09-08 22:11 [U-Boot] [PATCH 0/6] tegra2: Add more clock/pinmux functionality Simon Glass
2011-09-08 22:11 ` [U-Boot] [PATCH 1/6] tegra2: Rename CLOCK_PLL_ID to CLOCK_ID Simon Glass
2011-09-08 22:11 ` [U-Boot] [PATCH 2/6] tegra2: Clean up board code a little Simon Glass
2011-09-08 22:12 ` [U-Boot] [PATCH 3/6] tegra2: Add more clock functions Simon Glass
2011-09-08 22:12 ` [U-Boot] [PATCH 4/6] tegra2: Rename PIN_ to PINGRP_ Simon Glass
2011-09-08 22:12 ` [U-Boot] [PATCH 5/6] tegra2: Add more pinmux functions Simon Glass
2011-09-08 22:12 ` [U-Boot] [PATCH 6/6] tegra2: Enable MMC for Seaboard Simon Glass
2011-09-12  4:37 ` [U-Boot] [PATCH 0/6] tegra2: Add more clock/pinmux functionality Simon Glass
2011-09-21 20:43 ` Tom Warren
2011-09-21 20:58   ` Simon Glass

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.