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);
