WIP: Initial support for Samsung Exynos 5250 ARM CPU

Samsung SoC files, including Exynos5 (a Cortex-A15
implementation). Since this is an SoC we'll forego the x86-style
{north,south}bridge and cpu distinction. We may try to split some
stuff out before the final version if prudent.

Change-Id: Ie068e9dc3dd836c83d90e282b10d5202e7a4ba9b
Signed-off-by: David Hendricks <dhendrix@chromium.org>
Signed-off-by: Stefan Reinauer <reinauer@google.com>
Signed-off-by: Ronald G. Minnich <rminnich@gmail.com>
Reviewed-on: http://review.coreboot.org/2005
Tested-by: build bot (Jenkins)
diff --git a/src/cpu/Kconfig b/src/cpu/Kconfig
index 986c68a..383ba79 100644
--- a/src/cpu/Kconfig
+++ b/src/cpu/Kconfig
@@ -1,3 +1,12 @@
+# Warning: This file is included whether or not the if is here.
+# The if controls how the evaluation occurs.
+# (See also src/Kconfig)
+if ARCH_ARMV7
+
+source src/cpu/samsung/Kconfig
+
+endif	# ARCH_ARM
+
 if ARCH_X86
 
 source src/cpu/amd/Kconfig
diff --git a/src/cpu/Makefile.inc b/src/cpu/Makefile.inc
index 938a8df..93b16ae 100644
--- a/src/cpu/Makefile.inc
+++ b/src/cpu/Makefile.inc
@@ -3,6 +3,7 @@
 ################################################################################
 subdirs-y += amd
 subdirs-y += intel
+subdirs-y += samsung
 subdirs-y += via
 
 ################################################################################
diff --git a/src/cpu/samsung/Kconfig b/src/cpu/samsung/Kconfig
new file mode 100644
index 0000000..3a14ab8
--- /dev/null
+++ b/src/cpu/samsung/Kconfig
@@ -0,0 +1,46 @@
+config CPU_SAMSUNG_EXYNOS
+	bool
+	default n
+
+config CPU_SAMSUNG_EXYNOS5
+	depends on ARCH_ARMV7
+	select CPU_SAMSUNG_EXYNOS
+	bool
+	default n
+
+config SKIP_LOWLEVEL_INIT
+	bool "Skip low-level init"
+	default n
+	help
+	  Certain functions (ie PLL init) and processor features may already be
+	  handled by masked ROM code.
+
+config IRAM_BOTTOM
+	hex
+	default 0x02020000
+
+# FIXME(dhendrix): 0x02050000 was in the u-boot sources, but the docs say the
+# iRAM range is 0x0202_0000 - 0x0207_7fff (352KB).
+#config IRAM_TOP
+#	hex
+#	default 0x02050000
+config IRAM_TOP
+	hex
+	default 0x02077fff
+
+config SYS_INIT_SP_ADDR
+	hex
+	default 0x0204F800
+
+config IRAM_STACK
+	hex
+	default SYS_INIT_SP_ADDR
+
+# FIXME(dhendrix): what should this really be?
+config XIP_ROM_SIZE
+	hex
+	default 0x20000
+
+if CPU_SAMSUNG_EXYNOS5
+source src/cpu/samsung/exynos5250/Kconfig
+endif
diff --git a/src/cpu/samsung/Makefile.inc b/src/cpu/samsung/Makefile.inc
new file mode 100644
index 0000000..b6fcd4e
--- /dev/null
+++ b/src/cpu/samsung/Makefile.inc
@@ -0,0 +1,5 @@
+subdirs-$(CONFIG_CPU_SAMSUNG_EXYNOS5) += exynos5-common
+subdirs-$(CONFIG_CPU_SAMSUNG_EXYNOS5) += exynos5250
+
+# S5P is a predecessor to Exynos
+subdirs-$(CONFIG_CPU_SAMSUNG_EXYNOS) += s5p-common
diff --git a/src/cpu/samsung/exynos5-common/Makefile.inc b/src/cpu/samsung/exynos5-common/Makefile.inc
new file mode 100644
index 0000000..cad6cb3
--- /dev/null
+++ b/src/cpu/samsung/exynos5-common/Makefile.inc
@@ -0,0 +1,2 @@
+#romstage-y += soc.c
+romstage-y += spl_boot.c
diff --git a/src/cpu/samsung/exynos5-common/soc.c b/src/cpu/samsung/exynos5-common/soc.c
new file mode 100644
index 0000000..e73fd33
--- /dev/null
+++ b/src/cpu/samsung/exynos5-common/soc.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2010 Samsung Electronics.
+ * Minkyu Kang <mk7.kang@samsung.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <arch/io.h>
+
+void reset_cpu(unsigned long addr)
+{
+	writel(0x1, samsung_get_base_swreset());
+}
diff --git a/src/cpu/samsung/exynos5-common/spl_boot.c b/src/cpu/samsung/exynos5-common/spl_boot.c
new file mode 100644
index 0000000..8ae0511
--- /dev/null
+++ b/src/cpu/samsung/exynos5-common/spl_boot.c
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) 2011 Samsung Electronics
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+/* FIXME(dhendrix): pulled in a lot of extra crap such as partition and string
+   libs*/
+#include <assert.h>
+#include <common.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <console/console.h>
+#include <console/loglevel.h>
+//#include <asm/arch/board.h>
+
+#include <config.h>
+#include <spi.h>
+#if 0
+#include <asm/arch/clock.h>
+#include <asm/arch-exynos/spi.h>
+#include <asm/arch/pinmux.h>
+#include <asm/arch/power.h>
+#endif
+#include <arch/hlt.h>
+#include <cpu/samsung/exynos5250/clk.h>
+#include <cpu/samsung/exynos5250/cpu.h>
+#include <cpu/samsung/exynos5250/periph.h>
+#include <cpu/samsung/exynos5250/pinmux.h>
+#include <cpu/samsung/exynos5250/power.h>
+#include <cpu/samsung/exynos5250/spi.h>
+
+#include <cpu/samsung/exynos5-common/cpu.h>
+#include <cpu/samsung/exynos5-common/exynos5-common.h>
+
+//#include <asm/system.h>
+#include <system.h>
+
+#include <arch/io.h>
+#include <arch/types.h>
+
+/* FIXME(dhendrix): clean out u-boot global data stuff */
+//DECLARE_GLOBAL_DATA_PTR;
+
+#define OM_STAT		(0x1f << 1)
+
+/**
+ * Copy data from SD or MMC device to RAM.
+ *
+ * @param offset	Block offset of the data
+ * @param nblock	Number of blocks
+ * @param dst		Destination address
+ * @return 1 = True or 0 = False
+ */
+typedef u32 (*mmc_copy_func_t)(u32 offset, u32 nblock, u32 dst);
+
+/**
+ * Copy data from SPI flash to RAM.
+ *
+ * @param offset	Block offset of the data
+ * @param nblock	Number of blocks
+ * @param dst		Destination address
+ * @return 1 = True or 0 = False
+ */
+typedef u32 (*spi_copy_func_t)(u32 offset, u32 nblock, u32 dst);
+
+
+/**
+ * Copy data through USB.
+ *
+ * @return 1 = True or 0 = False
+ */
+typedef u32 (*usb_copy_func_t)(void);
+
+/*
+ * Set/clear program flow prediction and return the previous state.
+ */
+static int config_branch_prediction(int set_cr_z)
+{
+	unsigned int cr;
+
+	/* System Control Register: 11th bit Z Branch prediction enable */
+	cr = get_cr();
+	set_cr(set_cr_z ? cr | CR_Z : cr & ~CR_Z);
+
+	return cr & CR_Z;
+}
+
+#if 0
+static void spi_rx_tx(struct exynos_spi *regs, int todo,
+			void *dinp, void const *doutp, int i)
+{
+	uint *rxp = (uint *)(dinp + (i * (32 * 1024)));
+	int rx_lvl, tx_lvl;
+	uint out_bytes, in_bytes;
+
+	out_bytes = in_bytes = todo;
+	setbits_le32(&regs->ch_cfg, SPI_CH_RST);
+	clrbits_le32(&regs->ch_cfg, SPI_CH_RST);
+	writel(((todo * 8) / 32) | SPI_PACKET_CNT_EN, &regs->pkt_cnt);
+
+	while (in_bytes) {
+		uint32_t spi_sts;
+		int temp;
+
+		spi_sts = readl(&regs->spi_sts);
+		rx_lvl = ((spi_sts >> 15) & 0x7f);
+		tx_lvl = ((spi_sts >> 6) & 0x7f);
+		while (tx_lvl < 32 && out_bytes) {
+			temp = 0xffffffff;
+			writel(temp, &regs->tx_data);
+			out_bytes -= 4;
+			tx_lvl += 4;
+		}
+		while (rx_lvl >= 4 && in_bytes) {
+			temp = readl(&regs->rx_data);
+			if (rxp)
+				*rxp++ = temp;
+			in_bytes -= 4;
+			rx_lvl -= 4;
+		}
+	}
+}
+#endif
+
+/* FIXME(dhendrix): feels like exynos_spi_copy should go somewhere else... */
+#if 0
+/**
+ * Copy uboot from spi flash to RAM
+ *
+ * @parma uboot_size	size of u-boot to copy
+ */
+static void exynos_spi_copy(unsigned int uboot_size)
+{
+	int upto, todo;
+	int i;
+//	struct exynos_spi *regs = (struct exynos_spi *)samsung_get_base_spi1();
+	struct exynos_spi *regs = (struct exynos_spi *)0x12d30000;
+
+	clock_set_rate(PERIPH_ID_SPI1, 50000000); /* set spi clock to 50Mhz */
+	/* set the spi1 GPIO */
+	exynos_pinmux_config(PERIPH_ID_SPI1, PINMUX_FLAG_NONE);
+
+	/* set pktcnt and enable it */
+	writel(4 | SPI_PACKET_CNT_EN, &regs->pkt_cnt);
+	/* set FB_CLK_SEL */
+	writel(SPI_FB_DELAY_180, &regs->fb_clk);
+	/* set CH_WIDTH and BUS_WIDTH as word */
+	setbits_le32(&regs->mode_cfg, SPI_MODE_CH_WIDTH_WORD |
+					SPI_MODE_BUS_WIDTH_WORD);
+	clrbits_le32(&regs->ch_cfg, SPI_CH_CPOL_L); /* CPOL: active high */
+
+	/* clear rx and tx channel if set priveously */
+	clrbits_le32(&regs->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON);
+
+	setbits_le32(&regs->swap_cfg, SPI_RX_SWAP_EN |
+		SPI_RX_BYTE_SWAP |
+		SPI_RX_HWORD_SWAP);
+
+	/* do a soft reset */
+	setbits_le32(&regs->ch_cfg, SPI_CH_RST);
+	clrbits_le32(&regs->ch_cfg, SPI_CH_RST);
+
+	/* now set rx and tx channel ON */
+	setbits_le32(&regs->ch_cfg, SPI_RX_CH_ON | SPI_TX_CH_ON | SPI_CH_HS_EN);
+	clrbits_le32(&regs->cs_reg, SPI_SLAVE_SIG_INACT); /* CS low */
+
+	/* Send read instruction (0x3h) followed by a 24 bit addr */
+	writel((SF_READ_DATA_CMD << 24) | SPI_FLASH_UBOOT_POS, &regs->tx_data);
+
+	/* waiting for TX done */
+	while (!(readl(&regs->spi_sts) & SPI_ST_TX_DONE));
+
+	for (upto = 0, i = 0; upto < uboot_size; upto += todo, i++) {
+		todo = MIN(uboot_size - upto, (1 << 15));
+		spi_rx_tx(regs, todo, (void *)(CONFIG_SYS_TEXT_BASE),
+					(void *)(SPI_FLASH_UBOOT_POS), i);
+	}
+
+	setbits_le32(&regs->cs_reg, SPI_SLAVE_SIG_INACT);/* make the CS high */
+
+	/*
+	 * Let put controller mode to BYTE as
+	 * SPI driver does not support WORD mode yet
+	 */
+	clrbits_le32(&regs->mode_cfg, SPI_MODE_CH_WIDTH_WORD |
+					SPI_MODE_BUS_WIDTH_WORD);
+	writel(0, &regs->swap_cfg);
+
+	/*
+	 * Flush spi tx, rx fifos and reset the SPI controller
+	 * and clear rx/tx channel
+	 */
+	clrsetbits_le32(&regs->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST);
+	clrbits_le32(&regs->ch_cfg, SPI_CH_RST);
+	clrbits_le32(&regs->ch_cfg, SPI_TX_CH_ON | SPI_RX_CH_ON);
+}
+#endif
+
+/* Copy U-Boot image to RAM */
+static void copy_uboot_to_ram(void)
+{
+	unsigned int sec_boot_check;
+	unsigned int uboot_size = CONFIG_COREBOOT_ROMSIZE_KB_4096;
+	int is_cr_z_set;
+	enum boot_mode boot_mode = BOOT_MODE_OM;
+	mmc_copy_func_t mmc_copy;
+
+	usb_copy_func_t usb_copy;
+
+#if 0
+	uboot_size = exynos_get_uboot_size();
+	boot_mode = exynos_get_boot_device();
+#endif
+
+	if (boot_mode == BOOT_MODE_OM) {
+		/* Read iRAM location to check for secondary USB boot mode */
+		sec_boot_check = readl(EXYNOS_IRAM_SECONDARY_BASE);
+		if (sec_boot_check == EXYNOS_USB_SECONDARY_BOOT)
+			boot_mode = BOOT_MODE_USB;
+	}
+	debug("U-Boot size %u\n", uboot_size);
+
+	if (boot_mode == BOOT_MODE_OM)
+		boot_mode = readl(EXYNOS_POWER_BASE) & OM_STAT;
+
+	switch (boot_mode) {
+#if defined(CONFIG_EXYNOS_SPI_BOOT)
+	case BOOT_MODE_SERIAL:
+		/* let us our own function to copy u-boot from SF */
+		exynos_spi_copy(uboot_size);
+		break;
+#endif
+	case BOOT_MODE_MMC:
+		mmc_copy = *(mmc_copy_func_t *)EXYNOS_COPY_MMC_FNPTR_ADDR;
+		assert(!(uboot_size & 511));
+		mmc_copy(BL2_START_OFFSET, uboot_size / 512,
+				CONFIG_SYS_TEXT_BASE);
+		break;
+	case BOOT_MODE_USB:
+		/*
+		 * iROM needs program flow prediction to be disabled
+		 * before copy from USB device to RAM
+		 */
+		is_cr_z_set = config_branch_prediction(0);
+		usb_copy = *(usb_copy_func_t *)
+				EXYNOS_COPY_USB_FNPTR_ADDR;
+		usb_copy();
+		config_branch_prediction(is_cr_z_set);
+		break;
+	default:
+		printk(BIOS_ERR, "Invalid boot mode selection\n");
+		hlt();
+		break;
+	}
+	debug("U-Boot copied\n");
+}
+
+#if 0
+/**
+ * Set up the U-Boot global_data pointer
+ *
+ * This sets the address of the global data, and sets up basic values.
+ *
+ * @param gdp	Value to give to gd
+ */
+static void setup_global_data(gd_t *gdp)
+{
+	gd = gdp;
+	memzero((void *)gd, sizeof(gd_t));
+	gd->flags |= GD_FLG_RELOC;
+	gd->baudrate = CONFIG_BAUDRATE;
+	gd->have_console = 1;
+}
+#endif
+
+#if 0
+/* Tell the loaded U-Boot that it was loaded from SPL */
+static void exynos5_set_spl_marker(void)
+{
+	uint32_t *marker = (uint32_t *)CONFIG_SPL_MARKER;
+
+	*marker = EXYNOS5_SPL_MARKER;
+}
+#endif
+
+/* Board-specific call to see if wakeup is allowed. */
+static int __def_board_wakeup_permitted(void)
+{
+	return 1;
+}
+int board_wakeup_permitted(void)
+	__attribute__((weak, alias("__def_board_wakeup_permitted")));
+
+void board_init_f(unsigned long bootflag)
+{
+	/*
+	 * The gd struct is only needed for serial initialization. Since this
+	 * function is called in SPL u-boot. We store the gd struct in the
+	 * stack instead of the default memory region which may not be
+	 * initialized.
+	 */
+//	__attribute__((aligned(8))) gd_t local_gd;
+//	__attribute__((noreturn)) void (*uboot)(void);
+
+//	exynos5_set_spl_marker();
+//	setup_global_data(&local_gd);
+
+	/*
+	 * Init subsystems, and resume if required. For a normal boot this
+	 * will set up the UART and display a message.
+	 */
+	if (lowlevel_init_subsystems()) {
+		if (!board_wakeup_permitted())
+			power_reset();
+		power_exit_wakeup();
+	}
+
+//	printk(BIOS_INFO, "\n\nU-Boot SPL, board rev %u\n", board_get_revision());
+
+	copy_uboot_to_ram();
+	/* Jump to U-Boot image */
+//	uboot = (void *)CONFIG_SYS_TEXT_BASE;
+//	uboot();
+	/* Never returns Here */
+//	printk(BIOS_ERR, "%s: u-boot jump failed", __func__);
+	printk(BIOS_INFO, "%s: we should not be here...", __func__);
+	hlt();
+}
+
+/* Place Holders */
+void board_init_r(gd_t *id, ulong dest_addr)
+{
+	/* Function attribute is no-return */
+	/* This Function never executes */
+	while (1)
+		;
+}
+
+//void save_boot_params(u32 r0, u32 r1, u32 r2, u32 r3) {}
+
+#if 0
+/*
+ * The following functions are required when linking console library to SPL.
+ *
+ * Enabling UART in SPL u-boot requires console library. But some
+ * functions we needed in the console library depends on a bunch
+ * of library in libgeneric, like lib/ctype.o, lib/div64.o, lib/string.o,
+ * and lib/vsprintf.o. Adding them makes the SPL u-boot too large and not
+ * fit into the expected size.
+ *
+ * So we mock these functions in SPL, i.e. vsprintf(), panic(), etc.,
+ * in order to cut its dependency.
+ */
+int vsprintf(char *buf, const char *fmt, va_list args)
+{
+	char *str = buf, *s;
+	ulong u;
+
+	/*
+	 * We won't implement all full functions of vsprintf().
+	 * We only implement %s and %u, and ignore others and directly use
+	 * the original format string as its result.
+	 */
+
+	while (*fmt) {
+		if (*fmt != '%') {
+			*str++ = *fmt++;
+			continue;
+		}
+		fmt++;
+		switch (*fmt) {
+		case '%':
+			*str++ = *fmt++;
+			break;
+		case 's':
+			fmt++;
+			s = va_arg(args, char *);
+			while (*s)
+				*str++ = *s++;
+			break;
+		case 'u':
+			fmt++;
+			u = va_arg(args, ulong);
+			s = simple_itoa(u);
+			while (*s)
+				*str++ = *s++;
+			break;
+		default:
+			/* Print the original string for unsupported formats */
+			*str++ = '%';
+			*str++ = *fmt++;
+		}
+	}
+	*str = '\0';
+	return str - buf;
+}
+#endif
+
+#if 0
+void panic(const char *fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+	vprintf(fmt, args);
+	putc('\n');
+	va_end(args);
+#if defined(CONFIG_PANIC_HANG)
+	hang();
+#else
+	udelay(100000);		/* allow messages to go out */
+	do_reset(NULL, 0, 0, NULL);
+#endif
+	while (1)
+		;
+}
+#endif
+
+#if 0
+void __assert_fail(const char *assertion, const char *file, unsigned line,
+		const char *function)
+{
+	/* This will not return */
+	panic("%s:%u: %s: Assertion `%s' failed.", file, line, function,
+			assertion);
+}
+#endif
+
+#if 0
+char *simple_itoa(ulong i)
+{
+	/* 21 digits plus null terminator, good for 64-bit or smaller ints */
+	static char local[22] __attribute__((section(".data")));
+	char *p = &local[21];
+
+	*p-- = '\0';
+	do {
+		*p-- = '0' + i % 10;
+		i /= 10;
+	} while (i > 0);
+	return p + 1;
+}
+#endif
diff --git a/src/cpu/samsung/exynos5250/Kconfig b/src/cpu/samsung/exynos5250/Kconfig
new file mode 100644
index 0000000..8d4ba2dd0
--- /dev/null
+++ b/src/cpu/samsung/exynos5250/Kconfig
@@ -0,0 +1,28 @@
+config EXYNOS_ACE_SHA
+	bool
+	default n
+
+config SATA_AHCI
+	bool
+	default n
+
+config SPL_BUILD
+	bool
+	default n
+
+config SYS_TEXT_BASE
+	hex "Executable code section"
+	default 0x43e00000
+
+config SYS_SDRAM_BASE
+	hex "SDRAM base address"
+	default 0x40000000
+
+#FIXME(dhendrix, reinauer): re-visit this RAMBASE/RAMTOP stuff...
+config RAMBASE
+	hex
+	default SYS_SDRAM_BASE
+# according to stefan, this is RAMBASE + 1M.
+config RAMTOP
+	hex
+	default 0x40100000
diff --git a/src/cpu/samsung/exynos5250/Makefile.inc b/src/cpu/samsung/exynos5250/Makefile.inc
new file mode 100644
index 0000000..556631a
--- /dev/null
+++ b/src/cpu/samsung/exynos5250/Makefile.inc
@@ -0,0 +1,32 @@
+romstage-y += clock.c
+romstage-y += clock_init.c
+romstage-y += exynos_cache.c
+romstage-y += lowlevel_init.S
+romstage-y += lowlevel_init_c.c
+romstage-y += pinmux.c
+romstage-y += power.c
+romstage-y += soc.c
+romstage-y += uart.c
+
+#ramstage-y += clock.c
+#ramstage-y += clock_init.c
+#ramstage-y += power.c
+#ramstage-y += uart.c
+##ramstage-y += spl.c
+#ramstage-y += pinmux.c
+##ramstage-y += tzpc_init.c
+ramstage-y += clock.c
+ramstage-y += clock_init.c
+ramstage-y += exynos_cache.c
+ramstage-y += lowlevel_init.S
+ramstage-y += lowlevel_init_c.c
+ramstage-y += pinmux.c
+ramstage-y += power.c
+ramstage-y += soc.c
+ramstage-y += uart.c
+
+#ramstage-$(CONFIG_EXYNOS_ACE_SHA) += ace_sha.c
+#ramstage-$(CONFIG_SATA_AHCI) += sata.c
+ramstage-$(CONFIG_SPL_BUILD) += lowlevel_init_c.c
+ramstage-$(CONFIG_SPL_BUILD) += dmc_common.c
+ramstage-$(CONFIG_SPL_BUILD) += dmc_init_ddr3.c
diff --git a/src/cpu/samsung/exynos5250/ace_sha.c b/src/cpu/samsung/exynos5250/ace_sha.c
new file mode 100644
index 0000000..2715a03
--- /dev/null
+++ b/src/cpu/samsung/exynos5250/ace_sha.c
@@ -0,0 +1,118 @@
+/*
+ * Advanced Crypto Engine - SHA Firmware
+ *
+ * Copyright (c) 2012  Samsung Electronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#include <common.h>
+#include <asm/arch/ace_sha.h>
+#include <asm/arch/ace_sfr.h>
+
+/* Maximum input data size is 8 MB. Timeout observed for data size above 8MB */
+#define TIMEOUT_MS		100
+
+#define SHA1_DIGEST_LEN		20
+#define SHA256_DIGEST_LEN	32
+
+/* SHA1 value for the message of zero length */
+static const unsigned char sha1_digest_emptymsg[SHA1_DIGEST_LEN] = {
+	0xDA, 0x39, 0xA3, 0xEE, 0x5E, 0x6B, 0x4B, 0x0D,
+	0x32, 0x55, 0xBF, 0xFF, 0x95, 0x60, 0x18, 0x90,
+	0xAF, 0xD8, 0x07, 0x09};
+
+/* SHA256 value for the message of zero length */
+static const unsigned char sha256_digest_emptymsg[SHA256_DIGEST_LEN] = {
+	0xE3, 0xB0, 0xC4, 0x42, 0x98, 0xFC, 0x1C, 0x14,
+	0x9A, 0xFB, 0xF4, 0xC8, 0x99, 0x6F, 0xB9, 0x24,
+	0x27, 0xAE, 0x41, 0xE4, 0x64, 0x9B, 0x93, 0x4C,
+	0xA4, 0x95, 0x99, 0x1B, 0x78, 0x52, 0xB8, 0x55};
+
+int ace_sha_hash_digest(
+	unsigned char *pout, unsigned char *pbuf,
+	unsigned int buf_len, unsigned int hash_type)
+{
+	unsigned int i, reg, len;
+	unsigned int *pdigest;
+	ulong start;
+	struct exynos_ace_sfr *ace_sha_reg =
+		(struct exynos_ace_sfr *) samsung_get_base_ace_sfr();
+
+	if (buf_len == 0) {
+		/* ACE H/W cannot compute hash value for empty string */
+		if (hash_type == ACE_SHA_TYPE_SHA1)
+			memcpy(pout, sha1_digest_emptymsg, SHA1_DIGEST_LEN);
+		else
+			memcpy(pout, sha256_digest_emptymsg, SHA256_DIGEST_LEN);
+		return 0;
+	}
+
+	/* Flush HRDMA */
+	writel(ACE_FC_HRDMACFLUSH_ON, &ace_sha_reg->fc_hrdmac);
+	writel(ACE_FC_HRDMACFLUSH_OFF, &ace_sha_reg->fc_hrdmac);
+
+	/* Set byte swap of data in */
+	writel(ACE_HASH_SWAPDI_ON | ACE_HASH_SWAPDO_ON | ACE_HASH_SWAPIV_ON,
+			&ace_sha_reg->hash_byteswap);
+
+	/* Select Hash input mux as external source */
+	reg = readl(&ace_sha_reg->fc_fifoctrl);
+	reg = (reg & ~ACE_FC_SELHASH_MASK) | ACE_FC_SELHASH_EXOUT;
+	writel(reg, &ace_sha_reg->fc_fifoctrl);
+
+	/* Set Hash as SHA1 or SHA256 and start Hash engine */
+	reg = (hash_type == ACE_SHA_TYPE_SHA1) ?
+		ACE_HASH_ENGSEL_SHA1HASH : ACE_HASH_ENGSEL_SHA256HASH;
+	reg |= ACE_HASH_STARTBIT_ON;
+	writel(reg, &ace_sha_reg->hash_control);
+
+	/* Enable FIFO mode */
+	writel(ACE_HASH_FIFO_ON, &ace_sha_reg->hash_fifo_mode);
+
+	/* Set message length */
+	writel(buf_len, &ace_sha_reg->hash_msgsize_low);
+	writel(0, &ace_sha_reg->hash_msgsize_high);
+
+	/* Set HRDMA */
+	writel((unsigned int)pbuf, &ace_sha_reg->fc_hrdmas);
+	writel(buf_len, &ace_sha_reg->fc_hrdmal);
+
+	start = get_timer(0);
+
+	while ((readl(&ace_sha_reg->hash_status) & ACE_HASH_MSGDONE_MASK) ==
+		ACE_HASH_MSGDONE_OFF) {
+
+		if (get_timer(start) > TIMEOUT_MS) {
+			debug("%s: Timeout waiting for ACE\n", __func__);
+			return -1;
+		}
+	}
+
+	/* Clear MSG_DONE bit */
+	writel(ACE_HASH_MSGDONE_ON, &ace_sha_reg->hash_status);
+
+	/* Read hash result */
+	pdigest = (unsigned int *)pout;
+	len = (hash_type == ACE_SHA_TYPE_SHA1) ? 5 : 8;
+
+	for (i = 0; i < len ; i++)
+		pdigest[i] = readl(&ace_sha_reg->hash_result[i]);
+
+	/* Clear HRDMA pending bit */
+	writel(ACE_FC_HRDMA, &ace_sha_reg->fc_intpend);
+
+	return 0;
+}
diff --git a/src/cpu/samsung/exynos5250/clock.c b/src/cpu/samsung/exynos5250/clock.c
new file mode 100644
index 0000000..0250d77
--- /dev/null
+++ b/src/cpu/samsung/exynos5250/clock.c
@@ -0,0 +1,610 @@
+/*
+ * Copyright (C) 2010 Samsung Electronics
+ * Minkyu Kang <mk7.kang@samsung.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <console/console.h>
+#include <stdlib.h>
+//#include <fdtdec.h>
+#include <arch/io.h>
+//#include <asm/arch/clock.h>
+//#include <asm/arch/clk.h>
+#include <cpu/samsung/exynos5-common/clk.h>
+#include <cpu/samsung/exynos5250/clk.h>
+#include <cpu/samsung/exynos5250/cpu.h>
+#include <cpu/samsung/exynos5250/periph.h>
+#include <cpu/samsung/s5p-common/clk.h>
+
+/* input clock of PLL: SMDK5250 has 24MHz input clock */
+#define CONFIG_SYS_CLK_FREQ            24000000
+
+
+/* src_bit div_bit prediv_bit */
+static struct clk_bit_info clk_bit_info[PERIPH_ID_COUNT] = {
+	{0,	4,	0,	-1},
+	{4,	4,	4,	-1},
+	{8,	4,	8,	-1},
+	{12,	4,	12,	-1},
+	{0,	4,	0,	8},
+	{4,	4,	16,	24},
+	{8,	4,	0,	8},
+	{12,	4,	16,	24},
+	{-1,	-1,	-1,	-1},
+	{16,	4,	0,	8}, /* PERIPH_ID_SROMC */
+	{20,	4,	16,	24},
+	{24,	4,	0,	8},
+	{0,	4,	0,	4},
+	{4,	4,	12,	16},
+	{-1,	4,	-1,	-1},
+	{-1,	4,	-1,	-1},
+	{-1,	4,	24,	0},
+	{-1,	4,	24,	0},
+	{-1,	4,	24,	0},
+	{-1,	4,	24,	0},
+	{-1,	4,	24,	0},
+	{-1,	4,	24,	0},
+	{-1,	4,	24,	0},
+	{-1,	4,	24,	0},
+	{24,	4,	0,	-1},
+	{24,	4,	0,	-1},
+	{24,	4,	0,	-1},
+	{24,	4,	0,	-1},
+	{24,	4,	0,	-1},
+	{-1,	-1,	-1,	-1},
+	{-1,	-1,	-1,	-1},
+	{-1,	-1,	-1,	-1}, /* PERIPH_ID_I2S1 */
+	{24,	1,	20,	-1}, /* PERIPH_ID_SATA */
+};
+
+/* Epll Clock division values to achive different frequency output */
+static struct st_epll_con_val epll_div[] = {
+	{ 192000000, 0, 48, 3, 1, 0 },
+	{ 180000000, 0, 45, 3, 1, 0 },
+	{  73728000, 1, 73, 3, 3, 47710 },
+	{  67737600, 1, 90, 4, 3, 20762 },
+	{  49152000, 0, 49, 3, 3, 9961 },
+	{  45158400, 0, 45, 3, 3, 10381 },
+	{ 180633600, 0, 45, 3, 1, 10381 }
+};
+
+/* exynos5: return pll clock frequency */
+unsigned long get_pll_clk(int pllreg)
+{
+	struct exynos5_clock *clk =
+		(struct exynos5_clock *)samsung_get_base_clock();
+	unsigned long r, m, p, s, k = 0, mask, fout;
+	unsigned int freq;
+
+	switch (pllreg) {
+	case APLL:
+		r = readl(&clk->apll_con0);
+		break;
+	case BPLL:
+		r = readl(&clk->bpll_con0);
+		break;
+	case MPLL:
+		r = readl(&clk->mpll_con0);
+		break;
+	case EPLL:
+		r = readl(&clk->epll_con0);
+		k = readl(&clk->epll_con1);
+		break;
+	case VPLL:
+		r = readl(&clk->vpll_con0);
+		k = readl(&clk->vpll_con1);
+		break;
+	default:
+		printk(BIOS_DEBUG, "Unsupported PLL (%d)\n", pllreg);
+		return 0;
+	}
+
+	/*
+	 * APLL_CON: MIDV [25:16]
+	 * MPLL_CON: MIDV [25:16]
+	 * EPLL_CON: MIDV [24:16]
+	 * VPLL_CON: MIDV [24:16]
+	 */
+	if (pllreg == APLL || pllreg == BPLL || pllreg == MPLL)
+		mask = 0x3ff;
+	else
+		mask = 0x1ff;
+
+	m = (r >> 16) & mask;
+
+	/* PDIV [13:8] */
+	p = (r >> 8) & 0x3f;
+	/* SDIV [2:0] */
+	s = r & 0x7;
+
+	freq = CONFIG_SYS_CLK_FREQ;
+
+	if (pllreg == EPLL) {
+		k = k & 0xffff;
+		/* FOUT = (MDIV + K / 65536) * FIN / (PDIV * 2^SDIV) */
+		fout = (m + k / 65536) * (freq / (p * (1 << s)));
+	} else if (pllreg == VPLL) {
+		k = k & 0xfff;
+		/* FOUT = (MDIV + K / 1024) * FIN / (PDIV * 2^SDIV) */
+		fout = (m + k / 1024) * (freq / (p * (1 << s)));
+	} else {
+		/* FOUT = MDIV * FIN / (PDIV * 2^SDIV) */
+		fout = m * (freq / (p * (1 << s)));
+	}
+
+	return fout;
+}
+
+unsigned long clock_get_periph_rate(enum periph_id peripheral)
+{
+	struct exynos5_clock *clk =
+		(struct exynos5_clock *)samsung_get_base_clock();
+	struct clk_bit_info *bit_info = &clk_bit_info[peripheral];
+	unsigned long sclk, sub_clk;
+	unsigned int src, div, sub_div;
+
+	switch (peripheral) {
+	case PERIPH_ID_UART0:
+	case PERIPH_ID_UART1:
+	case PERIPH_ID_UART2:
+	case PERIPH_ID_UART3:
+		src = readl(&clk->src_peric0);
+		div = readl(&clk->div_peric0);
+		break;
+	case PERIPH_ID_PWM0:
+	case PERIPH_ID_PWM1:
+	case PERIPH_ID_PWM2:
+	case PERIPH_ID_PWM3:
+	case PERIPH_ID_PWM4:
+		src = readl(&clk->src_peric0);
+		div = readl(&clk->div_peric3);
+		break;
+	case PERIPH_ID_SPI0:
+	case PERIPH_ID_SPI1:
+		src = readl(&clk->src_peric1);
+		div = readl(&clk->div_peric1);
+		break;
+	case PERIPH_ID_SPI2:
+		src = readl(&clk->src_peric1);
+		div = readl(&clk->div_peric2);
+		break;
+	case PERIPH_ID_SPI3:
+	case PERIPH_ID_SPI4:
+		src = readl(&clk->sclk_src_isp);
+		div = readl(&clk->sclk_div_isp);
+		break;
+	case PERIPH_ID_SATA:
+		src = readl(&clk->src_fsys);
+		div = readl(&clk->div_fsys0);
+		break;
+	case PERIPH_ID_SDMMC0:
+	case PERIPH_ID_SDMMC1:
+	case PERIPH_ID_SDMMC2:
+	case PERIPH_ID_SDMMC3:
+		src = readl(&clk->src_fsys);
+		div = readl(&clk->div_fsys1);
+		break;
+	case PERIPH_ID_I2C0:
+	case PERIPH_ID_I2C1:
+	case PERIPH_ID_I2C2:
+	case PERIPH_ID_I2C3:
+	case PERIPH_ID_I2C4:
+	case PERIPH_ID_I2C5:
+	case PERIPH_ID_I2C6:
+	case PERIPH_ID_I2C7:
+		sclk = get_pll_clk(MPLL);
+		sub_div = ((readl(&clk->div_top1) >> bit_info->div_bit) & 0x7) + 1;
+		div = ((readl(&clk->div_top0) >> bit_info->prediv_bit) & 0x7) + 1;
+		return (sclk / sub_div) / div;
+	default:
+		printk(BIOS_DEBUG, "%s: invalid peripheral %d", __func__, peripheral);
+		return -1;
+	};
+
+	src = (src >> bit_info->src_bit) & ((1 << bit_info->n_src_bits) - 1);
+	if (peripheral == PERIPH_ID_SATA) {
+		if (src)
+			sclk = get_pll_clk(BPLL);
+		else
+			sclk = get_pll_clk(MPLL);
+	} else {
+		if (src == SRC_MPLL)
+			sclk = get_pll_clk(MPLL);
+		else if (src == SRC_EPLL)
+			sclk = get_pll_clk(EPLL);
+		else if (src == SRC_VPLL)
+			sclk = get_pll_clk(VPLL);
+		else
+			return 0;
+	}
+
+	sub_div = (div >> bit_info->div_bit) & 0xf;
+	sub_clk = sclk / (sub_div + 1);
+
+	if (peripheral == PERIPH_ID_SDMMC0 || peripheral == PERIPH_ID_SDMMC2) {
+		div = (div >> bit_info->prediv_bit) & 0xff;
+		return sub_clk / (div + 1);
+	}
+
+	return sub_clk;
+}
+
+/* exynos5: return ARM clock frequency */
+unsigned long get_arm_clk(void)
+{
+	struct exynos5_clock *clk =
+		(struct exynos5_clock *)samsung_get_base_clock();
+	unsigned long div;
+	unsigned long armclk;
+	unsigned int arm_ratio;
+	unsigned int arm2_ratio;
+
+	div = readl(&clk->div_cpu0);
+
+	/* ARM_RATIO: [2:0], ARM2_RATIO: [30:28] */
+	arm_ratio = (div >> 0) & 0x7;
+	arm2_ratio = (div >> 28) & 0x7;
+
+	armclk = get_pll_clk(APLL) / (arm_ratio + 1);
+	armclk /= (arm2_ratio + 1);
+
+	return armclk;
+}
+
+/* exynos5: set the mmc clock */
+void set_mmc_clk(int dev_index, unsigned int div)
+{
+	struct exynos5_clock *clk =
+		(struct exynos5_clock *)samsung_get_base_clock();
+	unsigned int addr;
+	unsigned int val;
+
+	/*
+	 * CLK_DIV_FSYS1
+	 * MMC0_PRE_RATIO [15:8], MMC1_PRE_RATIO [31:24]
+	 * CLK_DIV_FSYS2
+	 * MMC2_PRE_RATIO [15:8], MMC3_PRE_RATIO [31:24]
+	 */
+	if (dev_index < 2) {
+		addr = (unsigned int)&clk->div_fsys1;
+	} else {
+		addr = (unsigned int)&clk->div_fsys2;
+		dev_index -= 2;
+	}
+
+	val = readl(addr);
+	val &= ~(0xff << ((dev_index << 4) + 8));
+	val |= (div & 0xff) << ((dev_index << 4) + 8);
+	writel(val, addr);
+}
+
+void clock_ll_set_pre_ratio(enum periph_id periph_id, unsigned divisor)
+{
+	struct exynos5_clock *clk =
+		(struct exynos5_clock *)samsung_get_base_clock();
+	unsigned shift;
+	unsigned mask = 0xff;
+	u32 *reg;
+
+	/*
+	 * For now we only handle a very small subset of peipherals here.
+	 * Others will need to (and do) mangle the clock registers
+	 * themselves, At some point it is hoped that this function can work
+	 * from a table or calculated register offset / mask. For now this
+	 * is at least better than spreading clock control code around
+	 * U-Boot.
+	 */
+	switch (periph_id) {
+	case PERIPH_ID_SPI0:
+		reg = &clk->div_peric1;
+		shift = 8;
+		break;
+	case PERIPH_ID_SPI1:
+		reg = &clk->div_peric1;
+		shift = 24;
+		break;
+	case PERIPH_ID_SPI2:
+		reg = &clk->div_peric2;
+		shift = 8;
+		break;
+	case PERIPH_ID_SPI3:
+		reg = &clk->sclk_div_isp;
+		shift = 4;
+		break;
+	case PERIPH_ID_SPI4:
+		reg = &clk->sclk_div_isp;
+		shift = 16;
+		break;
+	default:
+		debug("%s: Unsupported peripheral ID %d\n", __func__,
+		      periph_id);
+		return;
+	}
+	clrsetbits_le32(reg, mask << shift, (divisor & mask) << shift);
+}
+
+void clock_ll_set_ratio(enum periph_id periph_id, unsigned divisor)
+{
+	struct exynos5_clock *clk =
+		(struct exynos5_clock *)samsung_get_base_clock();
+	unsigned shift;
+	unsigned mask = 0xff;
+	u32 *reg;
+
+	switch (periph_id) {
+	case PERIPH_ID_SPI0:
+		reg = &clk->div_peric1;
+		shift = 0;
+		break;
+	case PERIPH_ID_SPI1:
+		reg = &clk->div_peric1;
+		shift = 16;
+		break;
+	case PERIPH_ID_SPI2:
+		reg = &clk->div_peric2;
+		shift = 0;
+		break;
+	case PERIPH_ID_SPI3:
+		reg = &clk->sclk_div_isp;
+		shift = 0;
+		break;
+	case PERIPH_ID_SPI4:
+		reg = &clk->sclk_div_isp;
+		shift = 12;
+		break;
+	default:
+		debug("%s: Unsupported peripheral ID %d\n", __func__,
+		      periph_id);
+		return;
+	}
+	clrsetbits_le32(reg, mask << shift, (divisor & mask) << shift);
+}
+
+/**
+ * Linearly searches for the most accurate main and fine stage clock scalars
+ * (divisors) for a specified target frequency and scalar bit sizes by checking
+ * all multiples of main_scalar_bits values. Will always return scalars up to or
+ * slower than target.
+ *
+ * @param main_scalar_bits	Number of main scalar bits, must be > 0 and < 32
+ * @param fine_scalar_bits	Number of fine scalar bits, must be > 0 and < 32
+ * @param input_freq		Clock frequency to be scaled in Hz
+ * @param target_freq		Desired clock frequency in Hz
+ * @param best_fine_scalar	Pointer to store the fine stage divisor
+ *
+ * @return best_main_scalar	Main scalar for desired frequency or -1 if none
+ * found
+ */
+static int clock_calc_best_scalar(unsigned int main_scaler_bits,
+	unsigned int fine_scalar_bits, unsigned int input_rate,
+	unsigned int target_rate, unsigned int *best_fine_scalar)
+{
+	int i;
+	int best_main_scalar = -1;
+	unsigned int best_error = target_rate;
+	const unsigned int cap = (1 << fine_scalar_bits) - 1;
+	const unsigned int loops = 1 << main_scaler_bits;
+
+	debug("Input Rate is %u, Target is %u, Cap is %u\n", input_rate,
+			target_rate, cap);
+
+	assert(best_fine_scalar != NULL);
+	assert(main_scaler_bits <= fine_scalar_bits);
+
+	*best_fine_scalar = 1;
+
+	if (input_rate == 0 || target_rate == 0)
+		return -1;
+
+	if (target_rate >= input_rate)
+		return 1;
+
+	for (i = 1; i <= loops; i++) {
+		const unsigned int effective_div = MAX(MIN(input_rate / i /
+							target_rate, cap), 1);
+		const unsigned int effective_rate = input_rate / i /
+							effective_div;
+		const int error = target_rate - effective_rate;
+
+		debug("%d|effdiv:%u, effrate:%u, error:%d\n", i, effective_div,
+				effective_rate, error);
+
+		if (error >= 0 && error <= best_error) {
+			best_error = error;
+			best_main_scalar = i;
+			*best_fine_scalar = effective_div;
+		}
+	}
+
+	return best_main_scalar;
+}
+
+int clock_set_rate(enum periph_id periph_id, unsigned int rate)
+{
+	int main;
+	unsigned int fine;
+
+	switch (periph_id) {
+	case PERIPH_ID_SPI0:
+	case PERIPH_ID_SPI1:
+	case PERIPH_ID_SPI2:
+	case PERIPH_ID_SPI3:
+	case PERIPH_ID_SPI4:
+		main = clock_calc_best_scalar(4, 8, 400000000, rate, &fine);
+		if (main < 0) {
+			debug("%s: Cannot set clock rate for periph %d",
+					__func__, periph_id);
+			return -1;
+		}
+		clock_ll_set_ratio(periph_id, main - 1);
+		clock_ll_set_pre_ratio(periph_id, fine - 1);
+		break;
+	default:
+		debug("%s: Unsupported peripheral ID %d\n", __func__,
+		      periph_id);
+		return -1;
+	}
+
+	return 0;
+}
+
+int clock_set_mshci(enum periph_id peripheral)
+{
+	struct exynos5_clock *clk =
+		(struct exynos5_clock *)samsung_get_base_clock();
+	u32 *addr;
+	unsigned int clock;
+	unsigned int tmp;
+	unsigned int i;
+
+	/* get mpll clock */
+	clock = get_pll_clk(MPLL) / 1000000;
+
+	/*
+	 * CLK_DIV_FSYS1
+	 * MMC0_PRE_RATIO [15:8], MMC0_RATIO [3:0]
+	 * CLK_DIV_FSYS2
+	 * MMC2_PRE_RATIO [15:8], MMC2_RATIO [3:0]
+	 */
+	switch (peripheral) {
+	case PERIPH_ID_SDMMC0:
+		addr = &clk->div_fsys1;
+		break;
+	case PERIPH_ID_SDMMC2:
+		addr = &clk->div_fsys2;
+		break;
+	default:
+		debug("invalid peripheral\n");
+		return -1;
+	}
+	tmp = readl(addr) & ~0xff0f;
+	for (i = 0; i <= 0xf; i++) {
+		if ((clock / (i + 1)) <= 400) {
+			writel(tmp | i << 0, addr);
+			break;
+		}
+	}
+	return 0;
+}
+
+#ifdef CONFIG_OF_CONTROL
+int clock_decode_periph_id(const void *blob, int node)
+{
+	enum periph_id id;
+
+	/*
+	 * For now the peripheral ID is directly encoded. Once we have clock
+	 * support in the fdt and properly in exynos U-Boot we may have
+	 * another way of changing the clock.
+	 */
+	id = fdtdec_get_int(blob, node, "samsung,periph-id", -1);
+	assert(id != PERIPH_ID_NONE);
+	assert(id >= 0 && id < PERIPH_ID_COUNT);
+
+	return id;
+}
+#endif
+
+int clock_epll_set_rate(unsigned long rate)
+{
+	unsigned int epll_con, epll_con_k;
+	unsigned int i;
+	unsigned int lockcnt;
+	unsigned int start;
+	struct exynos5_clock *clk =
+		(struct exynos5_clock *)samsung_get_base_clock();
+
+	epll_con = readl(&clk->epll_con0);
+	epll_con &= ~((EPLL_CON0_LOCK_DET_EN_MASK <<
+			EPLL_CON0_LOCK_DET_EN_SHIFT) |
+		EPLL_CON0_MDIV_MASK << EPLL_CON0_MDIV_SHIFT |
+		EPLL_CON0_PDIV_MASK << EPLL_CON0_PDIV_SHIFT |
+		EPLL_CON0_SDIV_MASK << EPLL_CON0_SDIV_SHIFT);
+
+	for (i = 0; i < ARRAY_SIZE(epll_div); i++) {
+		if (epll_div[i].freq_out == rate)
+			break;
+	}
+
+	if (i == ARRAY_SIZE(epll_div))
+		return -1;
+
+	epll_con_k = epll_div[i].k_dsm << 0;
+	epll_con |= epll_div[i].en_lock_det << EPLL_CON0_LOCK_DET_EN_SHIFT;
+	epll_con |= epll_div[i].m_div << EPLL_CON0_MDIV_SHIFT;
+	epll_con |= epll_div[i].p_div << EPLL_CON0_PDIV_SHIFT;
+	epll_con |= epll_div[i].s_div << EPLL_CON0_SDIV_SHIFT;
+
+	/*
+	 * Required period ( in cycles) to genarate a stable clock output.
+	 * The maximum clock time can be up to 3000 * PDIV cycles of PLLs
+	 * frequency input (as per spec)
+	 */
+	lockcnt = 3000 * epll_div[i].p_div;
+
+	writel(lockcnt, &clk->epll_lock);
+	writel(epll_con, &clk->epll_con0);
+	writel(epll_con_k, &clk->epll_con1);
+
+	start = get_timer(0);
+
+	 while (!(readl(&clk->epll_con0) &
+			(0x1 << EXYNOS5_EPLLCON0_LOCKED_SHIFT))) {
+		if (get_timer(start) > TIMEOUT_EPLL_LOCK) {
+			printk(BIOS_DEBUG, "%s: Timeout waiting for EPLL lock\n", __func__);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+void clock_select_i2s_clk_source(void)
+{
+	struct exynos5_clock *clk =
+		(struct exynos5_clock *)samsung_get_base_clock();
+
+	clrsetbits_le32(&clk->src_peric1, AUDIO1_SEL_MASK,
+			(CLK_SRC_SCLK_EPLL));
+}
+
+int clock_set_i2s_clk_prescaler(unsigned int src_frq, unsigned int dst_frq)
+{
+	struct exynos5_clock *clk =
+		(struct exynos5_clock *)samsung_get_base_clock();
+	unsigned int div ;
+
+	if ((dst_frq == 0) || (src_frq == 0)) {
+		debug("%s: Invalid requency input for prescaler\n", __func__);
+		debug("src frq = %d des frq = %d ", src_frq, dst_frq);
+		return -1;
+	}
+
+	div = (src_frq / dst_frq);
+	if (div > AUDIO_1_RATIO_MASK) {
+		debug("%s: Frequency ratio is out of range\n", __func__);
+		debug("src frq = %d des frq = %d ", src_frq, dst_frq);
+		return -1;
+	}
+	clrsetbits_le32(&clk->div_peric4, AUDIO_1_RATIO_MASK,
+				(div & AUDIO_1_RATIO_MASK));
+	return 0;
+}
diff --git a/src/cpu/samsung/exynos5250/clock_init.c b/src/cpu/samsung/exynos5250/clock_init.c
new file mode 100644
index 0000000..fd2a90f
--- /dev/null
+++ b/src/cpu/samsung/exynos5250/clock_init.c
@@ -0,0 +1,1189 @@
+/*
+ * Clock setup for SMDK5250 board based on EXYNOS5
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#if 0
+#include <common.h>
+#include <config.h>
+//#include <fdtdec.h>
+#include <arch/io.h>
+#include <asm/arch/board.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/clock.h>
+#include <cpu/samsung/exynos5250/cpu.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch-exynos/spl.h>
+#endif
+#include <delay.h>
+#include <stdlib.h>
+#include <types.h>
+#include <system.h>
+
+#include <console/console.h>
+
+#include <cpu/samsung/exynos5-common/spl.h>
+#include <cpu/samsung/exynos5250/clk.h>
+#include <cpu/samsung/exynos5250/cpu.h>
+#include <cpu/samsung/exynos5250/dmc.h>
+#include <cpu/samsung/exynos5250/s5p-dp.h>
+#include <cpu/samsung/s5p-common/clk.h>
+
+#include "clock_init.h"
+#include "setup.h"
+
+//DECLARE_GLOBAL_DATA_PTR;
+
+struct arm_clk_ratios arm_clk_ratios[] = {
+	{
+		.arm_freq_mhz = 600,
+
+		.apll_mdiv = 0xc8,
+		.apll_pdiv = 0x4,
+		.apll_sdiv = 0x1,
+
+		.arm2_ratio = 0x0,
+		.apll_ratio = 0x1,
+		.pclk_dbg_ratio = 0x1,
+		.atb_ratio = 0x2,
+		.periph_ratio = 0x7,
+		.acp_ratio = 0x7,
+		.cpud_ratio = 0x1,
+		.arm_ratio = 0x0,
+	}, {
+		.arm_freq_mhz = 800,
+
+		.apll_mdiv = 0x64,
+		.apll_pdiv = 0x3,
+		.apll_sdiv = 0x0,
+
+		.arm2_ratio = 0x0,
+		.apll_ratio = 0x1,
+		.pclk_dbg_ratio = 0x1,
+		.atb_ratio = 0x3,
+		.periph_ratio = 0x7,
+		.acp_ratio = 0x7,
+		.cpud_ratio = 0x2,
+		.arm_ratio = 0x0,
+	}, {
+		.arm_freq_mhz = 1000,
+
+		.apll_mdiv = 0x7d,
+		.apll_pdiv = 0x3,
+		.apll_sdiv = 0x0,
+
+		.arm2_ratio = 0x0,
+		.apll_ratio = 0x1,
+		.pclk_dbg_ratio = 0x1,
+		.atb_ratio = 0x4,
+		.periph_ratio = 0x7,
+		.acp_ratio = 0x7,
+		.cpud_ratio = 0x2,
+		.arm_ratio = 0x0,
+	}, {
+		.arm_freq_mhz = 1200,
+
+		.apll_mdiv = 0x96,
+		.apll_pdiv = 0x3,
+		.apll_sdiv = 0x0,
+
+		.arm2_ratio = 0x0,
+		.apll_ratio = 0x3,
+		.pclk_dbg_ratio = 0x1,
+		.atb_ratio = 0x5,
+		.periph_ratio = 0x7,
+		.acp_ratio = 0x7,
+		.cpud_ratio = 0x3,
+		.arm_ratio = 0x0,
+	}, {
+		.arm_freq_mhz = 1400,
+
+		.apll_mdiv = 0xaf,
+		.apll_pdiv = 0x3,
+		.apll_sdiv = 0x0,
+
+		.arm2_ratio = 0x0,
+		.apll_ratio = 0x3,
+		.pclk_dbg_ratio = 0x1,
+		.atb_ratio = 0x6,
+		.periph_ratio = 0x7,
+		.acp_ratio = 0x7,
+		.cpud_ratio = 0x3,
+		.arm_ratio = 0x0,
+	}, {
+		.arm_freq_mhz = 1700,
+
+		.apll_mdiv = 0x1a9,
+		.apll_pdiv = 0x6,
+		.apll_sdiv = 0x0,
+
+		.arm2_ratio = 0x0,
+		.apll_ratio = 0x3,
+		.pclk_dbg_ratio = 0x1,
+		.atb_ratio = 0x6,
+		.periph_ratio = 0x7,
+		.acp_ratio = 0x7,
+		.cpud_ratio = 0x3,
+		.arm_ratio = 0x0,
+	}
+};
+
+struct mem_timings mem_timings[] = {
+	{
+		.mem_manuf = MEM_MANUF_ELPIDA,
+		.mem_type = DDR_MODE_DDR3,
+		.frequency_mhz = 800,
+		.mpll_mdiv = 0x64,
+		.mpll_pdiv = 0x3,
+		.mpll_sdiv = 0x0,
+		.cpll_mdiv = 0xde,
+		.cpll_pdiv = 0x4,
+		.cpll_sdiv = 0x2,
+		.gpll_mdiv = 0x215,
+		.gpll_pdiv = 0xc,
+		.gpll_sdiv = 0x1,
+		.epll_mdiv = 0x60,
+		.epll_pdiv = 0x3,
+		.epll_sdiv = 0x3,
+		.vpll_mdiv = 0x96,
+		.vpll_pdiv = 0x3,
+		.vpll_sdiv = 0x2,
+
+		.bpll_mdiv = 0x64,
+		.bpll_pdiv = 0x3,
+		.bpll_sdiv = 0x0,
+		.use_bpll = 0,
+		.pclk_cdrex_ratio = 0x5,
+		.direct_cmd_msr = {
+			0x00020018, 0x00030000, 0x00010042, 0x00000d70
+		},
+		.timing_ref = 0x000000bb,
+		.timing_row = 0x8c36660f,
+		.timing_data = 0x3630580b,
+		.timing_power = 0x41000a44,
+		.phy0_dqs = 0x08080808,
+		.phy1_dqs = 0x08080808,
+		.phy0_dq = 0x08080808,
+		.phy1_dq = 0x08080808,
+		.phy0_tFS = 0x4,
+		.phy1_tFS = 0x4,
+		.phy0_pulld_dqs = 0xf,
+		.phy1_pulld_dqs = 0xf,
+
+		.lpddr3_ctrl_phy_reset = 0x1,
+		.ctrl_start_point = 0x10,
+		.ctrl_inc = 0x10,
+		.ctrl_start = 0x1,
+		.ctrl_dll_on = 0x1,
+		.ctrl_ref = 0x8,
+
+		.ctrl_force = 0x1a,
+		.ctrl_rdlat = 0x0b,
+		.ctrl_bstlen = 0x08,
+
+		.fp_resync = 0x8,
+		.iv_size = 0x7,
+		.dfi_init_start = 1,
+		.aref_en = 1,
+
+		.rd_fetch = 0x3,
+
+		.zq_mode_dds = 0x7,
+		.zq_mode_term = 0x1,
+		.zq_mode_noterm = 0,
+
+		/*
+		* Dynamic Clock: Always Running
+		* Memory Burst length: 8
+		* Number of chips: 1
+		* Memory Bus width: 32 bit
+		* Memory Type: DDR3
+		* Additional Latancy for PLL: 0 Cycle
+		*/
+		.memcontrol = DMC_MEMCONTROL_CLK_STOP_DISABLE |
+			DMC_MEMCONTROL_DPWRDN_DISABLE |
+			DMC_MEMCONTROL_DPWRDN_ACTIVE_PRECHARGE |
+			DMC_MEMCONTROL_TP_DISABLE |
+			DMC_MEMCONTROL_DSREF_ENABLE |
+			DMC_MEMCONTROL_ADD_LAT_PALL_CYCLE(0) |
+			DMC_MEMCONTROL_MEM_TYPE_DDR3 |
+			DMC_MEMCONTROL_MEM_WIDTH_32BIT |
+			DMC_MEMCONTROL_NUM_CHIP_1 |
+			DMC_MEMCONTROL_BL_8 |
+			DMC_MEMCONTROL_PZQ_DISABLE |
+			DMC_MEMCONTROL_MRR_BYTE_7_0,
+		.memconfig = DMC_MEMCONFIGx_CHIP_MAP_INTERLEAVED |
+			DMC_MEMCONFIGx_CHIP_COL_10 |
+			DMC_MEMCONFIGx_CHIP_ROW_15 |
+			DMC_MEMCONFIGx_CHIP_BANK_8,
+		.membaseconfig0 = DMC_MEMBASECONFIG_VAL(0x40),
+		.membaseconfig1 = DMC_MEMBASECONFIG_VAL(0x80),
+		.prechconfig_tp_cnt = 0xff,
+		.dpwrdn_cyc = 0xff,
+		.dsref_cyc = 0xffff,
+		.concontrol = DMC_CONCONTROL_DFI_INIT_START_DISABLE |
+			DMC_CONCONTROL_TIMEOUT_LEVEL0 |
+			DMC_CONCONTROL_RD_FETCH_DISABLE |
+			DMC_CONCONTROL_EMPTY_DISABLE |
+			DMC_CONCONTROL_AREF_EN_DISABLE |
+			DMC_CONCONTROL_IO_PD_CON_DISABLE,
+		.dmc_channels = 2,
+		.chips_per_channel = 2,
+		.chips_to_configure = 1,
+		.send_zq_init = 1,
+		.impedance = IMP_OUTPUT_DRV_30_OHM,
+		.gate_leveling_enable = 0,
+	}, {
+		.mem_manuf = MEM_MANUF_SAMSUNG,
+		.mem_type = DDR_MODE_DDR3,
+		.frequency_mhz = 800,
+		.mpll_mdiv = 0x64,
+		.mpll_pdiv = 0x3,
+		.mpll_sdiv = 0x0,
+		.cpll_mdiv = 0xde,
+		.cpll_pdiv = 0x4,
+		.cpll_sdiv = 0x2,
+		.gpll_mdiv = 0x215,
+		.gpll_pdiv = 0xc,
+		.gpll_sdiv = 0x1,
+		.epll_mdiv = 0x60,
+		.epll_pdiv = 0x3,
+		.epll_sdiv = 0x3,
+		.vpll_mdiv = 0x96,
+		.vpll_pdiv = 0x3,
+		.vpll_sdiv = 0x2,
+
+		.bpll_mdiv = 0x64,
+		.bpll_pdiv = 0x3,
+		.bpll_sdiv = 0x0,
+		.use_bpll = 0,
+		.pclk_cdrex_ratio = 0x5,
+		.direct_cmd_msr = {
+			0x00020018, 0x00030000, 0x00010000, 0x00000d70
+		},
+		.timing_ref = 0x000000bb,
+		.timing_row = 0x8c36660f,
+		.timing_data = 0x3630580b,
+		.timing_power = 0x41000a44,
+		.phy0_dqs = 0x08080808,
+		.phy1_dqs = 0x08080808,
+		.phy0_dq = 0x08080808,
+		.phy1_dq = 0x08080808,
+		.phy0_tFS = 0x8,
+		.phy1_tFS = 0x8,
+		.phy0_pulld_dqs = 0xf,
+		.phy1_pulld_dqs = 0xf,
+
+		.lpddr3_ctrl_phy_reset = 0x1,
+		.ctrl_start_point = 0x10,
+		.ctrl_inc = 0x10,
+		.ctrl_start = 0x1,
+		.ctrl_dll_on = 0x1,
+		.ctrl_ref = 0x8,
+
+		.ctrl_force = 0x1a,
+		.ctrl_rdlat = 0x0b,
+		.ctrl_bstlen = 0x08,
+
+		.fp_resync = 0x8,
+		.iv_size = 0x7,
+		.dfi_init_start = 1,
+		.aref_en = 1,
+
+		.rd_fetch = 0x3,
+
+		.zq_mode_dds = 0x5,
+		.zq_mode_term = 0x1,
+		.zq_mode_noterm = 1,
+
+		/*
+		* Dynamic Clock: Always Running
+		* Memory Burst length: 8
+		* Number of chips: 1
+		* Memory Bus width: 32 bit
+		* Memory Type: DDR3
+		* Additional Latancy for PLL: 0 Cycle
+		*/
+		.memcontrol = DMC_MEMCONTROL_CLK_STOP_DISABLE |
+			DMC_MEMCONTROL_DPWRDN_DISABLE |
+			DMC_MEMCONTROL_DPWRDN_ACTIVE_PRECHARGE |
+			DMC_MEMCONTROL_TP_DISABLE |
+			DMC_MEMCONTROL_DSREF_ENABLE |
+			DMC_MEMCONTROL_ADD_LAT_PALL_CYCLE(0) |
+			DMC_MEMCONTROL_MEM_TYPE_DDR3 |
+			DMC_MEMCONTROL_MEM_WIDTH_32BIT |
+			DMC_MEMCONTROL_NUM_CHIP_1 |
+			DMC_MEMCONTROL_BL_8 |
+			DMC_MEMCONTROL_PZQ_DISABLE |
+			DMC_MEMCONTROL_MRR_BYTE_7_0,
+		.memconfig = DMC_MEMCONFIGx_CHIP_MAP_INTERLEAVED |
+			DMC_MEMCONFIGx_CHIP_COL_10 |
+			DMC_MEMCONFIGx_CHIP_ROW_15 |
+			DMC_MEMCONFIGx_CHIP_BANK_8,
+		.membaseconfig0 = DMC_MEMBASECONFIG_VAL(0x40),
+		.membaseconfig1 = DMC_MEMBASECONFIG_VAL(0x80),
+		.prechconfig_tp_cnt = 0xff,
+		.dpwrdn_cyc = 0xff,
+		.dsref_cyc = 0xffff,
+		.concontrol = DMC_CONCONTROL_DFI_INIT_START_DISABLE |
+			DMC_CONCONTROL_TIMEOUT_LEVEL0 |
+			DMC_CONCONTROL_RD_FETCH_DISABLE |
+			DMC_CONCONTROL_EMPTY_DISABLE |
+			DMC_CONCONTROL_AREF_EN_DISABLE |
+			DMC_CONCONTROL_IO_PD_CON_DISABLE,
+		.dmc_channels = 2,
+		.chips_per_channel = 2,
+		.chips_to_configure = 1,
+		.send_zq_init = 1,
+		.impedance = IMP_OUTPUT_DRV_40_OHM,
+		.gate_leveling_enable = 1,
+	},
+	{
+		.mem_manuf = MEM_MANUF_ELPIDA,
+		.mem_type = DDR_MODE_DDR3,
+		.frequency_mhz = 780,
+		.mpll_mdiv = 0x64,
+		.mpll_pdiv = 0x3,
+		.mpll_sdiv = 0x0,
+		.cpll_mdiv = 0xde,
+		.cpll_pdiv = 0x4,
+		.cpll_sdiv = 0x2,
+		.gpll_mdiv = 0x215,
+		.gpll_pdiv = 0xc,
+		.gpll_sdiv = 0x1,
+		.epll_mdiv = 0x60,
+		.epll_pdiv = 0x3,
+		.epll_sdiv = 0x3,
+		.vpll_mdiv = 0x96,
+		.vpll_pdiv = 0x3,
+		.vpll_sdiv = 0x2,
+
+		.bpll_mdiv = 0x82,
+		.bpll_pdiv = 0x4,
+		.bpll_sdiv = 0x0,
+		.use_bpll = 1,
+		.pclk_cdrex_ratio = 0x5,
+		.direct_cmd_msr = {
+			0x00020018, 0x00030000, 0x00010042, 0x00000d70
+		},
+		.timing_ref = 0x000000bb,
+		.timing_row = 0x8c36660f,
+		.timing_data = 0x3630580b,
+		.timing_power = 0x41000a44,
+		.phy0_dqs = 0x08080808,
+		.phy1_dqs = 0x08080808,
+		.phy0_dq = 0x08080808,
+		.phy1_dq = 0x08080808,
+		.phy0_tFS = 0x4,
+		.phy1_tFS = 0x4,
+		.phy0_pulld_dqs = 0xf,
+		.phy1_pulld_dqs = 0xf,
+
+		.lpddr3_ctrl_phy_reset = 0x1,
+		.ctrl_start_point = 0x10,
+		.ctrl_inc = 0x10,
+		.ctrl_start = 0x1,
+		.ctrl_dll_on = 0x1,
+		.ctrl_ref = 0x8,
+
+		.ctrl_force = 0x1a,
+		.ctrl_rdlat = 0x0b,
+		.ctrl_bstlen = 0x08,
+
+		.fp_resync = 0x8,
+		.iv_size = 0x7,
+		.dfi_init_start = 1,
+		.aref_en = 1,
+
+		.rd_fetch = 0x3,
+
+		.zq_mode_dds = 0x7,
+		.zq_mode_term = 0x1,
+		.zq_mode_noterm = 0,
+
+		/*
+		* Dynamic Clock: Always Running
+		* Memory Burst length: 8
+		* Number of chips: 1
+		* Memory Bus width: 32 bit
+		* Memory Type: DDR3
+		* Additional Latancy for PLL: 0 Cycle
+		*/
+		.memcontrol = DMC_MEMCONTROL_CLK_STOP_DISABLE |
+			DMC_MEMCONTROL_DPWRDN_DISABLE |
+			DMC_MEMCONTROL_DPWRDN_ACTIVE_PRECHARGE |
+			DMC_MEMCONTROL_TP_DISABLE |
+			DMC_MEMCONTROL_DSREF_ENABLE |
+			DMC_MEMCONTROL_ADD_LAT_PALL_CYCLE(0) |
+			DMC_MEMCONTROL_MEM_TYPE_DDR3 |
+			DMC_MEMCONTROL_MEM_WIDTH_32BIT |
+			DMC_MEMCONTROL_NUM_CHIP_1 |
+			DMC_MEMCONTROL_BL_8 |
+			DMC_MEMCONTROL_PZQ_DISABLE |
+			DMC_MEMCONTROL_MRR_BYTE_7_0,
+		.memconfig = DMC_MEMCONFIGx_CHIP_MAP_INTERLEAVED |
+			DMC_MEMCONFIGx_CHIP_COL_10 |
+			DMC_MEMCONFIGx_CHIP_ROW_15 |
+			DMC_MEMCONFIGx_CHIP_BANK_8,
+		.membaseconfig0 = DMC_MEMBASECONFIG_VAL(0x40),
+		.membaseconfig1 = DMC_MEMBASECONFIG_VAL(0x80),
+		.prechconfig_tp_cnt = 0xff,
+		.dpwrdn_cyc = 0xff,
+		.dsref_cyc = 0xffff,
+		.concontrol = DMC_CONCONTROL_DFI_INIT_START_DISABLE |
+			DMC_CONCONTROL_TIMEOUT_LEVEL0 |
+			DMC_CONCONTROL_RD_FETCH_DISABLE |
+			DMC_CONCONTROL_EMPTY_DISABLE |
+			DMC_CONCONTROL_AREF_EN_DISABLE |
+			DMC_CONCONTROL_IO_PD_CON_DISABLE,
+		.dmc_channels = 2,
+		.chips_per_channel = 2,
+		.chips_to_configure = 1,
+		.send_zq_init = 1,
+		.impedance = IMP_OUTPUT_DRV_30_OHM,
+		.gate_leveling_enable = 0,
+	}, {
+		.mem_manuf = MEM_MANUF_SAMSUNG,
+		.mem_type = DDR_MODE_DDR3,
+		.frequency_mhz = 780,
+		.mpll_mdiv = 0x64,
+		.mpll_pdiv = 0x3,
+		.mpll_sdiv = 0x0,
+		.cpll_mdiv = 0xde,
+		.cpll_pdiv = 0x4,
+		.cpll_sdiv = 0x2,
+		.gpll_mdiv = 0x215,
+		.gpll_pdiv = 0xc,
+		.gpll_sdiv = 0x1,
+		.epll_mdiv = 0x60,
+		.epll_pdiv = 0x3,
+		.epll_sdiv = 0x3,
+		.vpll_mdiv = 0x96,
+		.vpll_pdiv = 0x3,
+		.vpll_sdiv = 0x2,
+
+		.bpll_mdiv = 0x82,
+		.bpll_pdiv = 0x4,
+		.bpll_sdiv = 0x0,
+		.use_bpll = 1,
+		.pclk_cdrex_ratio = 0x5,
+		.direct_cmd_msr = {
+			0x00020018, 0x00030000, 0x00010000, 0x00000d70
+		},
+		.timing_ref = 0x000000bb,
+		.timing_row = 0x8c36660f,
+		.timing_data = 0x3630580b,
+		.timing_power = 0x41000a44,
+		.phy0_dqs = 0x08080808,
+		.phy1_dqs = 0x08080808,
+		.phy0_dq = 0x08080808,
+		.phy1_dq = 0x08080808,
+		.phy0_tFS = 0x8,
+		.phy1_tFS = 0x8,
+		.phy0_pulld_dqs = 0xf,
+		.phy1_pulld_dqs = 0xf,
+
+		.lpddr3_ctrl_phy_reset = 0x1,
+		.ctrl_start_point = 0x10,
+		.ctrl_inc = 0x10,
+		.ctrl_start = 0x1,
+		.ctrl_dll_on = 0x1,
+		.ctrl_ref = 0x8,
+
+		.ctrl_force = 0x1a,
+		.ctrl_rdlat = 0x0b,
+		.ctrl_bstlen = 0x08,
+
+		.fp_resync = 0x8,
+		.iv_size = 0x7,
+		.dfi_init_start = 1,
+		.aref_en = 1,
+
+		.rd_fetch = 0x3,
+
+		.zq_mode_dds = 0x5,
+		.zq_mode_term = 0x1,
+		.zq_mode_noterm = 1,
+
+		/*
+		* Dynamic Clock: Always Running
+		* Memory Burst length: 8
+		* Number of chips: 1
+		* Memory Bus width: 32 bit
+		* Memory Type: DDR3
+		* Additional Latancy for PLL: 0 Cycle
+		*/
+		.memcontrol = DMC_MEMCONTROL_CLK_STOP_DISABLE |
+			DMC_MEMCONTROL_DPWRDN_DISABLE |
+			DMC_MEMCONTROL_DPWRDN_ACTIVE_PRECHARGE |
+			DMC_MEMCONTROL_TP_DISABLE |
+			DMC_MEMCONTROL_DSREF_ENABLE |
+			DMC_MEMCONTROL_ADD_LAT_PALL_CYCLE(0) |
+			DMC_MEMCONTROL_MEM_TYPE_DDR3 |
+			DMC_MEMCONTROL_MEM_WIDTH_32BIT |
+			DMC_MEMCONTROL_NUM_CHIP_1 |
+			DMC_MEMCONTROL_BL_8 |
+			DMC_MEMCONTROL_PZQ_DISABLE |
+			DMC_MEMCONTROL_MRR_BYTE_7_0,
+		.memconfig = DMC_MEMCONFIGx_CHIP_MAP_INTERLEAVED |
+			DMC_MEMCONFIGx_CHIP_COL_10 |
+			DMC_MEMCONFIGx_CHIP_ROW_15 |
+			DMC_MEMCONFIGx_CHIP_BANK_8,
+		.membaseconfig0 = DMC_MEMBASECONFIG_VAL(0x40),
+		.membaseconfig1 = DMC_MEMBASECONFIG_VAL(0x80),
+		.prechconfig_tp_cnt = 0xff,
+		.dpwrdn_cyc = 0xff,
+		.dsref_cyc = 0xffff,
+		.concontrol = DMC_CONCONTROL_DFI_INIT_START_DISABLE |
+			DMC_CONCONTROL_TIMEOUT_LEVEL0 |
+			DMC_CONCONTROL_RD_FETCH_DISABLE |
+			DMC_CONCONTROL_EMPTY_DISABLE |
+			DMC_CONCONTROL_AREF_EN_DISABLE |
+			DMC_CONCONTROL_IO_PD_CON_DISABLE,
+		.dmc_channels = 2,
+		.chips_per_channel = 2,
+		.chips_to_configure = 1,
+		.send_zq_init = 1,
+		.impedance = IMP_OUTPUT_DRV_40_OHM,
+		.gate_leveling_enable = 1,
+	}
+};
+
+/**
+ * Detect what memory is present based on board strappings
+ *
+ * Boards have various resistor stuff options that are supposed to match
+ * which SDRAM is present (and which revision of the board this is).  This
+ * uses the resistor stuff options to figure out what memory manufacturer
+ * to use for matching in the memory tables.
+ *
+ * @return A MEM_MANUF_XXX constant, or -1 if an error occurred.
+ */
+/*
+ * FIXME(dhendrix): This unwinds into a mess of board-specific code. The
+ * board's romstage.c file should detect the memory type and pass in
+ * appropriate parameters to whatever calls this.
+ */
+#define BOARD_REV_ELPIDA_MEMORY		3
+#define BOARD_REV_SAMSUNG_MEMORY	4
+static int autodetect_memory(void)
+{
+//	int board_rev = board_get_revision();
+	int board_rev = BOARD_REV_ELPIDA_MEMORY;
+
+	if (board_rev == -1)
+		return -1;
+
+	switch (board_rev) {
+	case BOARD_REV_SAMSUNG_MEMORY:
+		return MEM_MANUF_SAMSUNG;
+	case BOARD_REV_ELPIDA_MEMORY:
+		return MEM_MANUF_ELPIDA;
+	}
+
+	return -1;
+}
+
+#ifdef CONFIG_SPL_BUILD
+
+/**
+ * Get the required memory type and speed (SPL version).
+ *
+ * In SPL we have no device tree, so we use the machine parameters
+ */
+int clock_get_mem_selection(enum ddr_mode *mem_type,
+		unsigned *frequency_mhz, unsigned *arm_freq,
+		enum mem_manuf *mem_manuf)
+{
+	struct spl_machine_param *params;
+
+	params = spl_get_machine_params();
+	*mem_type = params->mem_type;
+	*frequency_mhz = params->frequency_mhz;
+	*arm_freq = params->arm_freq_mhz;
+	if (params->mem_manuf == MEM_MANUF_AUTODETECT) {
+		*mem_manuf = autodetect_memory();
+		if (*mem_manuf == -1)
+			return -1;
+	} else {
+		*mem_manuf = params->mem_manuf;
+	}
+
+	return 0;
+}
+
+#else
+
+static const char *mem_types[DDR_MODE_COUNT] = {
+	"DDR2", "DDR3", "LPDDR2", "LPDDR3"
+};
+
+static const char *mem_manufs[MEM_MANUF_COUNT] = {
+	"autodetect", "Elpida", "Samsung"
+};
+
+int clock_get_mem_selection(enum ddr_mode *mem_type,
+		unsigned *frequency_mhz, unsigned *arm_freq,
+		enum mem_manuf *mem_manuf)
+{
+	const char *typestr;
+	int node, i;
+
+	node = fdtdec_next_compatible(gd->fdt_blob, 0,
+				      COMPAT_SAMSUNG_EXYNOS_DMC);
+	if (node < 0)
+		die("No memory information available in device tree");
+	typestr = fdt_getprop(gd->fdt_blob, node, "mem-type", NULL);
+	for (i = 0; i < DDR_MODE_COUNT; i++) {
+		if (!stricmp(typestr, mem_types[i]))
+			break;
+	}
+	if (i == DDR_MODE_COUNT)
+		die("Invalid memory type in device tree");
+	*mem_type = i;
+
+	typestr = fdt_getprop(gd->fdt_blob, node, "mem-manuf", NULL);
+	for (i = 0; i < MEM_MANUF_COUNT; i++) {
+		if (!stricmp(typestr, mem_manufs[i]))
+			break;
+	}
+	if (i == MEM_MANUF_COUNT)
+		die("Invalid memory manufacturer in device tree");
+
+	if (i == MEM_MANUF_AUTODETECT) {
+		*mem_manuf = autodetect_memory();
+		if (*mem_manuf == -1)
+			return -1;
+	} else {
+		*mem_manuf = i;
+	}
+
+	*frequency_mhz = fdtdec_get_int(gd->fdt_blob, node, "clock-frequency",
+					0);
+	if (!*frequency_mhz)
+		die("Invalid memory frequency in device tree");
+
+	*arm_freq = fdtdec_get_int(gd->fdt_blob, node, "arm-frequency", 0);
+	/* TODO: Remove all these panics/dies, and just return an error code */
+	if (!*arm_freq)
+		die("Invalid ARM frequency in device tree");
+
+	return 0;
+}
+
+const char *clock_get_mem_type_name(enum ddr_mode mem_type)
+{
+	if (mem_type >= 0 && mem_type < DDR_MODE_COUNT)
+		return mem_types[mem_type];
+
+	return "<unknown>";
+}
+
+const char *clock_get_mem_manuf_name(enum mem_manuf mem_manuf)
+{
+	if (mem_manuf >= 0 && mem_manuf < MEM_MANUF_COUNT)
+		return mem_manufs[mem_manuf];
+
+	return "<unknown>";
+}
+#endif
+
+/* Get the ratios for setting ARM clock */
+struct arm_clk_ratios *get_arm_ratios(void);	/* FIXME: silence compiler... */
+struct arm_clk_ratios *get_arm_ratios(void)
+{
+	struct arm_clk_ratios *arm_ratio;
+	enum ddr_mode mem_type;
+	enum mem_manuf mem_manuf;
+	unsigned frequency_mhz, arm_freq;
+	int i;
+
+	/* TODO(sjg@chromium.org): Return NULL and have caller deal with it */
+	if (clock_get_mem_selection(&mem_type, &frequency_mhz,
+					&arm_freq, &mem_manuf))
+		;
+	for (i = 0, arm_ratio = arm_clk_ratios; i < ARRAY_SIZE(arm_clk_ratios);
+		i++, arm_ratio++) {
+		if (arm_ratio->arm_freq_mhz == arm_freq)
+			return arm_ratio;
+	}
+
+	die("get_arm_ratios: Failed to find ratio\n");
+	return NULL;
+}
+
+struct mem_timings *clock_get_mem_timings(void)
+{
+	struct mem_timings *mem;
+	enum ddr_mode mem_type;
+	enum mem_manuf mem_manuf;
+	unsigned frequency_mhz, arm_freq;
+	int i;
+
+	if (!clock_get_mem_selection(&mem_type, &frequency_mhz,
+						&arm_freq, &mem_manuf)) {
+		for (i = 0, mem = mem_timings; i < ARRAY_SIZE(mem_timings);
+				i++, mem++) {
+			if (mem->mem_type == mem_type &&
+					mem->frequency_mhz == frequency_mhz &&
+					mem->mem_manuf == mem_manuf)
+				return mem;
+		}
+	}
+	/* TODO: Call panic() here */
+	while (1)
+		;
+	return NULL;
+}
+
+void system_clock_init()
+{
+	struct exynos5_clock *clk = (struct exynos5_clock *)EXYNOS5_CLOCK_BASE;
+	struct exynos5_mct_regs *mct_regs =
+		(struct exynos5_mct_regs *)EXYNOS5_MULTI_CORE_TIMER_BASE;
+	struct mem_timings *mem;
+	struct arm_clk_ratios *arm_clk_ratio;
+	u32 val, tmp;
+
+	/* Turn on the MCT as early as possible. */
+	mct_regs->g_tcon |= (1 << 8);
+
+	mem = clock_get_mem_timings();
+	arm_clk_ratio = get_arm_ratios();
+
+	clrbits_le32(&clk->src_cpu, MUX_APLL_SEL_MASK);
+	do {
+		val = readl(&clk->mux_stat_cpu);
+	} while ((val | MUX_APLL_SEL_MASK) != val);
+
+	clrbits_le32(&clk->src_core1, MUX_MPLL_SEL_MASK);
+	do {
+		val = readl(&clk->mux_stat_core1);
+	} while ((val | MUX_MPLL_SEL_MASK) != val);
+
+	clrbits_le32(&clk->src_top2, MUX_CPLL_SEL_MASK);
+	clrbits_le32(&clk->src_top2, MUX_EPLL_SEL_MASK);
+	clrbits_le32(&clk->src_top2, MUX_VPLL_SEL_MASK);
+	clrbits_le32(&clk->src_top2, MUX_GPLL_SEL_MASK);
+	tmp = MUX_CPLL_SEL_MASK | MUX_EPLL_SEL_MASK | MUX_VPLL_SEL_MASK
+		| MUX_GPLL_SEL_MASK;
+	do {
+		val = readl(&clk->mux_stat_top2);
+	} while ((val | tmp) != val);
+
+	clrbits_le32(&clk->src_cdrex, MUX_BPLL_SEL_MASK);
+	do {
+		val = readl(&clk->mux_stat_cdrex);
+	} while ((val | MUX_BPLL_SEL_MASK) != val);
+
+	/* PLL locktime */
+	writel(APLL_LOCK_VAL, &clk->apll_lock);
+
+	writel(MPLL_LOCK_VAL, &clk->mpll_lock);
+
+	writel(BPLL_LOCK_VAL, &clk->bpll_lock);
+
+	writel(CPLL_LOCK_VAL, &clk->cpll_lock);
+
+	writel(GPLL_LOCK_VAL, &clk->gpll_lock);
+
+	writel(EPLL_LOCK_VAL, &clk->epll_lock);
+
+	writel(VPLL_LOCK_VAL, &clk->vpll_lock);
+
+	writel(CLK_REG_DISABLE, &clk->pll_div2_sel);
+
+	writel(MUX_HPM_SEL_MASK, &clk->src_cpu);
+	do {
+		val = readl(&clk->mux_stat_cpu);
+	} while ((val | HPM_SEL_SCLK_MPLL) != val);
+
+	val = arm_clk_ratio->arm2_ratio << 28
+		| arm_clk_ratio->apll_ratio << 24
+		| arm_clk_ratio->pclk_dbg_ratio << 20
+		| arm_clk_ratio->atb_ratio << 16
+		| arm_clk_ratio->periph_ratio << 12
+		| arm_clk_ratio->acp_ratio << 8
+		| arm_clk_ratio->cpud_ratio << 4
+		| arm_clk_ratio->arm_ratio;
+	writel(val, &clk->div_cpu0);
+	do {
+		val = readl(&clk->div_stat_cpu0);
+	} while (0 != val);
+
+	writel(CLK_DIV_CPU1_VAL, &clk->div_cpu1);
+	do {
+		val = readl(&clk->div_stat_cpu1);
+	} while (0 != val);
+
+	/* Set APLL */
+	writel(APLL_CON1_VAL, &clk->apll_con1);
+	val = set_pll(arm_clk_ratio->apll_mdiv, arm_clk_ratio->apll_pdiv,
+			arm_clk_ratio->apll_sdiv);
+	writel(val, &clk->apll_con0);
+	while ((readl(&clk->apll_con0) & APLL_CON0_LOCKED) == 0)
+		;
+
+	/* Set MPLL */
+	writel(MPLL_CON1_VAL, &clk->mpll_con1);
+	val = set_pll(mem->mpll_mdiv, mem->mpll_pdiv, mem->mpll_sdiv);
+	writel(val, &clk->mpll_con0);
+	while ((readl(&clk->mpll_con0) & MPLL_CON0_LOCKED) == 0)
+		;
+
+	/*
+	 * Configure MUX_MPLL_FOUT to choose the direct clock source
+	 * path and avoid the fixed DIV/2 block to save power
+	 */
+	setbits_le32(&clk->pll_div2_sel, MUX_MPLL_FOUT_SEL);
+
+	/* Set BPLL */
+	if (mem->use_bpll) {
+		writel(BPLL_CON1_VAL, &clk->bpll_con1);
+		val = set_pll(mem->bpll_mdiv, mem->bpll_pdiv, mem->bpll_sdiv);
+		writel(val, &clk->bpll_con0);
+		while ((readl(&clk->bpll_con0) & BPLL_CON0_LOCKED) == 0)
+			;
+
+		setbits_le32(&clk->pll_div2_sel, MUX_BPLL_FOUT_SEL);
+	}
+
+	/* Set CPLL */
+	writel(CPLL_CON1_VAL, &clk->cpll_con1);
+	val = set_pll(mem->cpll_mdiv, mem->cpll_pdiv, mem->cpll_sdiv);
+	writel(val, &clk->cpll_con0);
+	while ((readl(&clk->cpll_con0) & CPLL_CON0_LOCKED) == 0)
+		;
+
+	/* Set GPLL */
+	writel(GPLL_CON1_VAL, &clk->gpll_con1);
+	val = set_pll(mem->gpll_mdiv, mem->gpll_pdiv, mem->gpll_sdiv);
+	writel(val, &clk->gpll_con0);
+	while ((readl(&clk->gpll_con0) & GPLL_CON0_LOCKED) == 0)
+		;
+
+	/* Set EPLL */
+	writel(EPLL_CON2_VAL, &clk->epll_con2);
+	writel(EPLL_CON1_VAL, &clk->epll_con1);
+	val = set_pll(mem->epll_mdiv, mem->epll_pdiv, mem->epll_sdiv);
+	writel(val, &clk->epll_con0);
+	while ((readl(&clk->epll_con0) & EPLL_CON0_LOCKED) == 0)
+		;
+
+	/* Set VPLL */
+	writel(VPLL_CON2_VAL, &clk->vpll_con2);
+	writel(VPLL_CON1_VAL, &clk->vpll_con1);
+	val = set_pll(mem->vpll_mdiv, mem->vpll_pdiv, mem->vpll_sdiv);
+	writel(val, &clk->vpll_con0);
+	while ((readl(&clk->vpll_con0) & VPLL_CON0_LOCKED) == 0)
+		;
+
+	writel(CLK_SRC_CORE0_VAL, &clk->src_core0);
+	writel(CLK_DIV_CORE0_VAL, &clk->div_core0);
+	while (readl(&clk->div_stat_core0) != 0)
+		;
+
+	writel(CLK_DIV_CORE1_VAL, &clk->div_core1);
+	while (readl(&clk->div_stat_core1) != 0)
+		;
+
+	writel(CLK_DIV_SYSRGT_VAL, &clk->div_sysrgt);
+	while (readl(&clk->div_stat_sysrgt) != 0)
+		;
+
+	writel(CLK_DIV_ACP_VAL, &clk->div_acp);
+	while (readl(&clk->div_stat_acp) != 0)
+		;
+
+	writel(CLK_DIV_SYSLFT_VAL, &clk->div_syslft);
+	while (readl(&clk->div_stat_syslft) != 0)
+		;
+
+	writel(CLK_SRC_TOP0_VAL, &clk->src_top0);
+	writel(CLK_SRC_TOP1_VAL, &clk->src_top1);
+	writel(TOP2_VAL, &clk->src_top2);
+	writel(CLK_SRC_TOP3_VAL, &clk->src_top3);
+
+	writel(CLK_DIV_TOP0_VAL, &clk->div_top0);
+	while (readl(&clk->div_stat_top0))
+		;
+
+	writel(CLK_DIV_TOP1_VAL, &clk->div_top1);
+	while (readl(&clk->div_stat_top1))
+		;
+
+	writel(CLK_SRC_LEX_VAL, &clk->src_lex);
+	while (1) {
+		val = readl(&clk->mux_stat_lex);
+		if (val == (val | 1))
+			break;
+	}
+
+	writel(CLK_DIV_LEX_VAL, &clk->div_lex);
+	while (readl(&clk->div_stat_lex))
+		;
+
+	writel(CLK_DIV_R0X_VAL, &clk->div_r0x);
+	while (readl(&clk->div_stat_r0x))
+		;
+
+	writel(CLK_DIV_R0X_VAL, &clk->div_r0x);
+	while (readl(&clk->div_stat_r0x))
+		;
+
+	writel(CLK_DIV_R1X_VAL, &clk->div_r1x);
+	while (readl(&clk->div_stat_r1x))
+		;
+
+	if (mem->use_bpll) {
+		writel(MUX_BPLL_SEL_MASK | MUX_MCLK_CDREX_SEL |
+			MUX_MCLK_DPHY_SEL, &clk->src_cdrex);
+	} else {
+		writel(CLK_REG_DISABLE, &clk->src_cdrex);
+	}
+
+	writel(CLK_DIV_CDREX_VAL, &clk->div_cdrex);
+	while (readl(&clk->div_stat_cdrex))
+		;
+
+	val = readl(&clk->src_cpu);
+	val |= CLK_SRC_CPU_VAL;
+	writel(val, &clk->src_cpu);
+
+	val = readl(&clk->src_top2);
+	val |= CLK_SRC_TOP2_VAL;
+	writel(val, &clk->src_top2);
+
+	val = readl(&clk->src_core1);
+	val |= CLK_SRC_CORE1_VAL;
+	writel(val, &clk->src_core1);
+
+	writel(CLK_SRC_FSYS0_VAL, &clk->src_fsys);
+	writel(CLK_DIV_FSYS0_VAL, &clk->div_fsys0);
+	while (readl(&clk->div_stat_fsys0))
+		;
+
+	writel(CLK_REG_DISABLE, &clk->clkout_cmu_cpu);
+	writel(CLK_REG_DISABLE, &clk->clkout_cmu_core);
+	writel(CLK_REG_DISABLE, &clk->clkout_cmu_acp);
+	writel(CLK_REG_DISABLE, &clk->clkout_cmu_top);
+	writel(CLK_REG_DISABLE, &clk->clkout_cmu_lex);
+	writel(CLK_REG_DISABLE, &clk->clkout_cmu_r0x);
+	writel(CLK_REG_DISABLE, &clk->clkout_cmu_r1x);
+	writel(CLK_REG_DISABLE, &clk->clkout_cmu_cdrex);
+
+	writel(CLK_SRC_PERIC0_VAL, &clk->src_peric0);
+	writel(CLK_DIV_PERIC0_VAL, &clk->div_peric0);
+
+	writel(CLK_SRC_PERIC1_VAL, &clk->src_peric1);
+	writel(CLK_DIV_PERIC1_VAL, &clk->div_peric1);
+	writel(CLK_DIV_PERIC2_VAL, &clk->div_peric2);
+	writel(SCLK_SRC_ISP_VAL, &clk->sclk_src_isp);
+	writel(SCLK_DIV_ISP_VAL, &clk->sclk_div_isp);
+	writel(CLK_DIV_ISP0_VAL, &clk->div_isp0);
+	writel(CLK_DIV_ISP1_VAL, &clk->div_isp1);
+	writel(CLK_DIV_ISP2_VAL, &clk->div_isp2);
+
+	/* FIMD1 SRC CLK SELECTION */
+	writel(CLK_SRC_DISP1_0_VAL, &clk->src_disp1_0);
+
+	val = MMC2_PRE_RATIO_VAL << MMC2_PRE_RATIO_OFFSET
+		| MMC2_RATIO_VAL << MMC2_RATIO_OFFSET
+		| MMC3_PRE_RATIO_VAL << MMC3_PRE_RATIO_OFFSET
+		| MMC3_RATIO_VAL << MMC3_RATIO_OFFSET;
+	writel(val, &clk->div_fsys2);
+}
+
+void clock_gate(void)
+{
+	struct exynos5_clock *clk = (struct exynos5_clock *)EXYNOS5_CLOCK_BASE;
+
+	/* CLK_GATE_IP_SYSRGT */
+	clrbits_le32(&clk->gate_ip_sysrgt, CLK_C2C_MASK);
+
+	/* CLK_GATE_IP_ACP */
+	clrbits_le32(&clk->gate_ip_acp, CLK_SMMUG2D_MASK |
+					CLK_SMMUSSS_MASK |
+					CLK_SMMUMDMA_MASK |
+					CLK_ID_REMAPPER_MASK |
+					CLK_G2D_MASK |
+					CLK_SSS_MASK |
+					CLK_MDMA_MASK |
+					CLK_SECJTAG_MASK);
+
+	/* CLK_GATE_BUS_SYSLFT */
+	clrbits_le32(&clk->gate_bus_syslft, CLK_EFCLK_MASK);
+
+	/* CLK_GATE_IP_ISP0 */
+	clrbits_le32(&clk->gate_ip_isp0, CLK_UART_ISP_MASK |
+					 CLK_WDT_ISP_MASK |
+					 CLK_PWM_ISP_MASK |
+					 CLK_MTCADC_ISP_MASK |
+					 CLK_I2C1_ISP_MASK |
+					 CLK_I2C0_ISP_MASK |
+					 CLK_MPWM_ISP_MASK |
+					 CLK_MCUCTL_ISP_MASK |
+					 CLK_INT_COMB_ISP_MASK |
+					 CLK_SMMU_MCUISP_MASK |
+					 CLK_SMMU_SCALERP_MASK |
+					 CLK_SMMU_SCALERC_MASK |
+					 CLK_SMMU_FD_MASK |
+					 CLK_SMMU_DRC_MASK |
+					 CLK_SMMU_ISP_MASK |
+					 CLK_GICISP_MASK |
+					 CLK_ARM9S_MASK |
+					 CLK_MCUISP_MASK |
+					 CLK_SCALERP_MASK |
+					 CLK_SCALERC_MASK |
+					 CLK_FD_MASK |
+					 CLK_DRC_MASK |
+					 CLK_ISP_MASK);
+
+	/* CLK_GATE_IP_ISP1 */
+	clrbits_le32(&clk->gate_ip_isp1, CLK_SPI1_ISP_MASK |
+					 CLK_SPI0_ISP_MASK |
+					 CLK_SMMU3DNR_MASK |
+					 CLK_SMMUDIS1_MASK |
+					 CLK_SMMUDIS0_MASK |
+					 CLK_SMMUODC_MASK |
+					 CLK_3DNR_MASK |
+					 CLK_DIS_MASK |
+					 CLK_ODC_MASK);
+
+	/* CLK_GATE_SCLK_ISP */
+	clrbits_le32(&clk->gate_sclk_isp, SCLK_MPWM_ISP_MASK);
+
+	/* CLK_GATE_IP_GSCL */
+	clrbits_le32(&clk->gate_ip_gscl, CLK_SMMUFIMC_LITE2_MASK |
+					 CLK_SMMUFIMC_LITE1_MASK |
+					 CLK_SMMUFIMC_LITE0_MASK |
+					 CLK_SMMUGSCL3_MASK |
+					 CLK_SMMUGSCL2_MASK |
+					 CLK_SMMUGSCL1_MASK |
+					 CLK_SMMUGSCL0_MASK |
+					 CLK_GSCL_WRAP_B_MASK |
+					 CLK_GSCL_WRAP_A_MASK |
+					 CLK_CAMIF_TOP_MASK |
+					 CLK_GSCL3_MASK |
+					 CLK_GSCL2_MASK |
+					 CLK_GSCL1_MASK |
+					 CLK_GSCL0_MASK);
+
+	/* CLK_GATE_IP_DISP1 */
+	clrbits_le32(&clk->gate_ip_disp1, CLK_SMMUTVX_MASK |
+					  CLK_ASYNCTVX_MASK |
+					  CLK_HDMI_MASK |
+					  CLK_MIXER_MASK |
+					  CLK_DSIM1_MASK);
+
+	/* CLK_GATE_IP_MFC */
+	clrbits_le32(&clk->gate_ip_mfc, CLK_SMMUMFCR_MASK |
+					CLK_SMMUMFCL_MASK |
+					CLK_MFC_MASK);
+
+	/* CLK_GATE_IP_GEN */
+	clrbits_le32(&clk->gate_ip_gen, CLK_SMMUMDMA1_MASK |
+					CLK_SMMUJPEG_MASK |
+					CLK_SMMUROTATOR_MASK |
+					CLK_MDMA1_MASK |
+					CLK_JPEG_MASK |
+					CLK_ROTATOR_MASK);
+
+	/* CLK_GATE_IP_FSYS */
+	clrbits_le32(&clk->gate_ip_fsys, CLK_WDT_IOP_MASK |
+					 CLK_SMMUMCU_IOP_MASK |
+					 CLK_SATA_PHY_I2C_MASK |
+					 CLK_SATA_PHY_CTRL_MASK |
+					 CLK_MCUCTL_MASK |
+					 CLK_NFCON_MASK |
+					 CLK_SMMURTIC_MASK |
+					 CLK_RTIC_MASK |
+					 CLK_MIPI_HSI_MASK |
+					 CLK_USBOTG_MASK |
+					 CLK_SATA_MASK |
+					 CLK_PDMA1_MASK |
+					 CLK_PDMA0_MASK |
+					 CLK_MCU_IOP_MASK);
+
+	/* CLK_GATE_IP_PERIC */
+	clrbits_le32(&clk->gate_ip_peric, CLK_HS_I2C3_MASK |
+					  CLK_HS_I2C2_MASK |
+					  CLK_HS_I2C1_MASK |
+					  CLK_HS_I2C0_MASK |
+					  CLK_AC97_MASK |
+					  CLK_SPDIF_MASK |
+					  CLK_PCM2_MASK |
+					  CLK_PCM1_MASK |
+					  CLK_I2S2_MASK |
+					  CLK_SPI2_MASK |
+					  CLK_SPI0_MASK);
+
+	/* CLK_GATE_IP_PERIS */
+	clrbits_le32(&clk->gate_ip_peris, CLK_RTC_MASK |
+					  CLK_TZPC9_MASK |
+					  CLK_TZPC8_MASK |
+					  CLK_TZPC7_MASK |
+					  CLK_TZPC6_MASK |
+					  CLK_TZPC5_MASK |
+					  CLK_TZPC4_MASK |
+					  CLK_TZPC3_MASK |
+					  CLK_TZPC2_MASK |
+					  CLK_TZPC1_MASK |
+					  CLK_TZPC0_MASK |
+					  CLK_CHIPID_MASK);
+
+	/* CLK_GATE_BLOCK */
+	clrbits_le32(&clk->gate_block, CLK_ACP_MASK);
+
+	/* CLK_GATE_IP_CDREX */
+	clrbits_le32(&clk->gate_ip_cdrex, CLK_DPHY0_MASK |
+					  CLK_DPHY1_MASK |
+					  CLK_TZASC_DRBXR_MASK);
+
+}
+
+void clock_init_dp_clock(void)
+{
+	struct exynos5_clock *clk = (struct exynos5_clock *)EXYNOS5_CLOCK_BASE;
+
+	/* DP clock enable */
+	setbits_le32(&clk->gate_ip_disp1, CLK_GATE_DP1_ALLOW);
+
+	/* We run DP at 267 Mhz */
+	setbits_le32(&clk->div_disp1_0, CLK_DIV_DISP1_0_FIMD1);
+}
+
+#ifdef CONFIG_SPL_BUILD
+/*
+ * This is a custom implementation for the udelay(), as we do not the timer
+ * initialise during the SPL boot. We are assuming the cpu takes 3 instruction
+ * pre cycle. This is based on the implementation of sdelay() function.
+ */
+void udelay(unsigned usec)
+{
+	unsigned long count;
+
+	/* TODO(alim.akhtar@samsung.com): Comment on why divided by 30000000 */
+	count = usec * (get_pll_clk(APLL) / (3 * 10000000));
+	sdelay(count);
+}
+#endif
diff --git a/src/cpu/samsung/exynos5250/dmc_common.c b/src/cpu/samsung/exynos5250/dmc_common.c
new file mode 100644
index 0000000..35a13ac
--- /dev/null
+++ b/src/cpu/samsung/exynos5250/dmc_common.c
@@ -0,0 +1,200 @@
+/*
+ * Mem setup common file for different types of DDR present on SMDK5250 boards.
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <assert.h>
+#include <common.h>
+#include <console/console.h>
+#include <cpu/samsung/exynos5250/setup.h>
+#include <cpu/samsung/exynos5-common/spl.h>
+#include <system.h>
+
+#include "clock_init.h"
+#include "setup.h"
+
+#define ZQ_INIT_TIMEOUT	10000
+
+int dmc_config_zq(struct mem_timings *mem,
+		  struct exynos5_phy_control *phy0_ctrl,
+		  struct exynos5_phy_control *phy1_ctrl)
+{
+	unsigned long val = 0;
+	int i;
+
+	/*
+	 * ZQ Calibration:
+	 * Select Driver Strength,
+	 * long calibration for manual calibration
+	 */
+	val = PHY_CON16_RESET_VAL;
+	val |= mem->zq_mode_dds << PHY_CON16_ZQ_MODE_DDS_SHIFT;
+	val |= mem->zq_mode_term << PHY_CON16_ZQ_MODE_TERM_SHIFT;
+	val |= ZQ_CLK_DIV_EN;
+	writel(val, &phy0_ctrl->phy_con16);
+	writel(val, &phy1_ctrl->phy_con16);
+
+	/* Disable termination */
+	if (mem->zq_mode_noterm)
+		val |= PHY_CON16_ZQ_MODE_NOTERM_MASK;
+	writel(val, &phy0_ctrl->phy_con16);
+	writel(val, &phy1_ctrl->phy_con16);
+
+	/* ZQ_MANUAL_START: Enable */
+	val |= ZQ_MANUAL_STR;
+	writel(val, &phy0_ctrl->phy_con16);
+	writel(val, &phy1_ctrl->phy_con16);
+
+	/* ZQ_MANUAL_START: Disable */
+	val &= ~ZQ_MANUAL_STR;
+
+	/*
+	 * Since we are manaully calibrating the ZQ values,
+	 * we are looping for the ZQ_init to complete.
+	 */
+	i = ZQ_INIT_TIMEOUT;
+	while ((readl(&phy0_ctrl->phy_con17) & ZQ_DONE) != ZQ_DONE && i > 0) {
+		sdelay(100);
+		i--;
+	}
+	if (!i)
+		return -1;
+	writel(val, &phy0_ctrl->phy_con16);
+
+	i = ZQ_INIT_TIMEOUT;
+	while ((readl(&phy1_ctrl->phy_con17) & ZQ_DONE) != ZQ_DONE && i > 0) {
+		sdelay(100);
+		i--;
+	}
+	if (!i)
+		return -1;
+	writel(val, &phy1_ctrl->phy_con16);
+
+	return 0;
+}
+
+void update_reset_dll(struct exynos5_dmc *dmc, enum ddr_mode mode)
+{
+	unsigned long val;
+
+	if (mode == DDR_MODE_DDR3) {
+		val = MEM_TERM_EN | PHY_TERM_EN | DMC_CTRL_SHGATE;
+		writel(val, &dmc->phycontrol0);
+	}
+
+	/* Update DLL Information: Force DLL Resyncronization */
+	val = readl(&dmc->phycontrol0);
+	val |= FP_RSYNC;
+	writel(val, &dmc->phycontrol0);
+
+	/* Reset Force DLL Resyncronization */
+	val = readl(&dmc->phycontrol0);
+	val &= ~FP_RSYNC;
+	writel(val, &dmc->phycontrol0);
+}
+
+void dmc_config_mrs(struct mem_timings *mem, struct exynos5_dmc *dmc)
+{
+	int channel, chip;
+
+	for (channel = 0; channel < mem->dmc_channels; channel++) {
+		unsigned long mask;
+
+		mask = channel << DIRECT_CMD_CHANNEL_SHIFT;
+		for (chip = 0; chip < mem->chips_to_configure; chip++) {
+			int i;
+
+			mask |= chip << DIRECT_CMD_CHIP_SHIFT;
+
+			/* Sending NOP command */
+			writel(DIRECT_CMD_NOP | mask, &dmc->directcmd);
+
+			/*
+			 * TODO(alim.akhtar@samsung.com): Do we need these
+			 * delays? This one and the next were not there for
+			 * DDR3.
+			 */
+			sdelay(0x10000);
+
+			/* Sending EMRS/MRS commands */
+			for (i = 0; i < MEM_TIMINGS_MSR_COUNT; i++) {
+				writel(mem->direct_cmd_msr[i] | mask,
+				       &dmc->directcmd);
+				sdelay(0x10000);
+			}
+
+			if (mem->send_zq_init) {
+				/* Sending ZQINIT command */
+				writel(DIRECT_CMD_ZQINIT | mask,
+				       &dmc->directcmd);
+
+				sdelay(10000);
+			}
+		}
+	}
+}
+
+void dmc_config_prech(struct mem_timings *mem, struct exynos5_dmc *dmc)
+{
+	int channel, chip;
+
+	for (channel = 0; channel < mem->dmc_channels; channel++) {
+		unsigned long mask;
+
+		mask = channel << DIRECT_CMD_CHANNEL_SHIFT;
+		for (chip = 0; chip < mem->chips_per_channel; chip++) {
+			mask |= chip << DIRECT_CMD_CHIP_SHIFT;
+
+			/* PALL (all banks precharge) CMD */
+			writel(DIRECT_CMD_PALL | mask, &dmc->directcmd);
+			sdelay(0x10000);
+		}
+	}
+}
+
+void dmc_config_memory(struct mem_timings *mem, struct exynos5_dmc *dmc)
+{
+	writel(mem->memconfig, &dmc->memconfig0);
+	writel(mem->memconfig, &dmc->memconfig1);
+	writel(DMC_MEMBASECONFIG0_VAL, &dmc->membaseconfig0);
+	writel(DMC_MEMBASECONFIG1_VAL, &dmc->membaseconfig1);
+}
+
+void mem_ctrl_init()
+{
+	struct spl_machine_param *param = spl_get_machine_params();
+	struct mem_timings *mem;
+	int ret;
+
+	mem = clock_get_mem_timings();
+
+	/* If there are any other memory variant, add their init call below */
+	if (param->mem_type == DDR_MODE_DDR3) {
+		ret = ddr3_mem_ctrl_init(mem, param->mem_iv_size);
+		if (ret) {
+			printk(BIOS_ERR, "Memory controller init failed, err: %u", ret);
+			BUG();
+		}
+	} else {
+		die("Unknown memory type");
+	}
+}
diff --git a/src/cpu/samsung/exynos5250/dmc_init_ddr3.c b/src/cpu/samsung/exynos5250/dmc_init_ddr3.c
new file mode 100644
index 0000000..6ef10bf
--- /dev/null
+++ b/src/cpu/samsung/exynos5250/dmc_init_ddr3.c
@@ -0,0 +1,249 @@
+/*
+ * DDR3 mem setup file for SMDK5250 board based on EXYNOS5
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <config.h>
+#include <arch/io.h>
+//#include "clock.h"
+/* FIXME(dhendrix): untangle clock/clk ... */
+#include <cpu/samsung/s5p-common/clock.h>
+#include <system.h>
+#include "clk.h"
+#include "cpu.h"
+#include "dmc.h"
+#include "setup.h"
+#include "clock_init.h"
+
+#define RDLVL_COMPLETE_TIMEOUT	10000
+
+static void reset_phy_ctrl(void)
+{
+	struct exynos5_clock *clk = (struct exynos5_clock *)EXYNOS5_CLOCK_BASE;
+
+	writel(LPDDR3PHY_CTRL_PHY_RESET_OFF, &clk->lpddr3phy_ctrl);
+	writel(LPDDR3PHY_CTRL_PHY_RESET, &clk->lpddr3phy_ctrl);
+
+	/*
+	 * For proper memory initialization there should be a minimum delay of
+	 * 500us after the LPDDR3PHY_CTRL_PHY_RESET signal.
+	 * The below value is an approximate value whose calculation in done
+	 * considering that sdelay takes 2 instruction for every 1 delay cycle.
+	 * And assuming each instruction takes 1 clock cycle i.e 1/(1.7 Ghz)sec
+	 * So for 500 usec, the number of delay cycle should be
+	 * (500 * 10^-6) * (1.7 * 10^9) / 2 = 425000
+	 *
+	 * TODO(hatim.rv@samsung.com): Implement the delay using timer/counter
+	 */
+	sdelay(425000);
+}
+
+int ddr3_mem_ctrl_init(struct mem_timings *mem, unsigned long mem_iv_size)
+{
+	unsigned int val;
+	struct exynos5_phy_control *phy0_ctrl, *phy1_ctrl;
+	struct exynos5_dmc *dmc;
+	int i;
+
+	phy0_ctrl = (struct exynos5_phy_control *)EXYNOS5_DMC_PHY0_BASE;
+	phy1_ctrl = (struct exynos5_phy_control *)EXYNOS5_DMC_PHY1_BASE;
+	dmc = (struct exynos5_dmc *)EXYNOS5_DMC_CTRL_BASE;
+
+	reset_phy_ctrl();
+
+	/* Set Impedance Output Driver */
+	val = (mem->impedance << CA_CK_DRVR_DS_OFFSET) |
+		(mem->impedance << CA_CKE_DRVR_DS_OFFSET) |
+		(mem->impedance << CA_CS_DRVR_DS_OFFSET) |
+		(mem->impedance << CA_ADR_DRVR_DS_OFFSET);
+	writel(val, &phy0_ctrl->phy_con39);
+	writel(val, &phy1_ctrl->phy_con39);
+
+	/* Set Read Latency and Burst Length for PHY0 and PHY1 */
+	val = (mem->ctrl_bstlen << PHY_CON42_CTRL_BSTLEN_SHIFT) |
+		(mem->ctrl_rdlat << PHY_CON42_CTRL_RDLAT_SHIFT);
+	writel(val, &phy0_ctrl->phy_con42);
+	writel(val, &phy1_ctrl->phy_con42);
+
+	/* ZQ Calibration */
+	if (dmc_config_zq(mem, phy0_ctrl, phy1_ctrl))
+		return SETUP_ERR_ZQ_CALIBRATION_FAILURE;
+
+	/* DQ Signal */
+	writel(mem->phy0_pulld_dqs, &phy0_ctrl->phy_con14);
+	writel(mem->phy1_pulld_dqs, &phy1_ctrl->phy_con14);
+
+	writel(mem->concontrol | (mem->rd_fetch << CONCONTROL_RD_FETCH_SHIFT)
+		| (mem->dfi_init_start << CONCONTROL_DFI_INIT_START_SHIFT),
+		&dmc->concontrol);
+
+	update_reset_dll(dmc, DDR_MODE_DDR3);
+
+	/* DQS Signal */
+	writel(mem->phy0_dqs, &phy0_ctrl->phy_con4);
+	writel(mem->phy1_dqs, &phy1_ctrl->phy_con4);
+
+	writel(mem->phy0_dq, &phy0_ctrl->phy_con6);
+	writel(mem->phy1_dq, &phy1_ctrl->phy_con6);
+
+	writel(mem->phy0_tFS, &phy0_ctrl->phy_con10);
+	writel(mem->phy1_tFS, &phy1_ctrl->phy_con10);
+
+	val = (mem->ctrl_start_point << PHY_CON12_CTRL_START_POINT_SHIFT) |
+		(mem->ctrl_inc << PHY_CON12_CTRL_INC_SHIFT) |
+		(mem->ctrl_dll_on << PHY_CON12_CTRL_DLL_ON_SHIFT) |
+		(mem->ctrl_ref << PHY_CON12_CTRL_REF_SHIFT);
+	writel(val, &phy0_ctrl->phy_con12);
+	writel(val, &phy1_ctrl->phy_con12);
+
+	/* Start DLL locking */
+	writel(val | (mem->ctrl_start << PHY_CON12_CTRL_START_SHIFT),
+		&phy0_ctrl->phy_con12);
+	writel(val | (mem->ctrl_start << PHY_CON12_CTRL_START_SHIFT),
+		&phy1_ctrl->phy_con12);
+
+	update_reset_dll(dmc, DDR_MODE_DDR3);
+
+	writel(mem->concontrol | (mem->rd_fetch << CONCONTROL_RD_FETCH_SHIFT),
+		&dmc->concontrol);
+
+	/* Memory Channel Inteleaving Size */
+	writel(mem->iv_size, &dmc->ivcontrol);
+
+	/* Set DMC MEMCONTROL register */
+	val = mem->memcontrol & ~DMC_MEMCONTROL_DSREF_ENABLE;
+	writel(val, &dmc->memcontrol);
+
+	writel(mem->memconfig, &dmc->memconfig0);
+	writel(mem->memconfig, &dmc->memconfig1);
+	writel(mem->membaseconfig0, &dmc->membaseconfig0);
+	writel(mem->membaseconfig1, &dmc->membaseconfig1);
+
+	/* Precharge Configuration */
+	writel(mem->prechconfig_tp_cnt << PRECHCONFIG_TP_CNT_SHIFT,
+		&dmc->prechconfig);
+
+	/* Power Down mode Configuration */
+	writel(mem->dpwrdn_cyc << PWRDNCONFIG_DPWRDN_CYC_SHIFT |
+		mem->dsref_cyc << PWRDNCONFIG_DSREF_CYC_SHIFT,
+		&dmc->pwrdnconfig);
+
+	/* TimingRow, TimingData, TimingPower and Timingaref
+	 * values as per Memory AC parameters
+	 */
+	writel(mem->timing_ref, &dmc->timingref);
+	writel(mem->timing_row, &dmc->timingrow);
+	writel(mem->timing_data, &dmc->timingdata);
+	writel(mem->timing_power, &dmc->timingpower);
+
+	/* Send PALL command */
+	dmc_config_prech(mem, dmc);
+
+	/* Send NOP, MRS and ZQINIT commands */
+	dmc_config_mrs(mem, dmc);
+
+	if (mem->gate_leveling_enable) {
+		val = PHY_CON0_RESET_VAL;
+		val |= P0_CMD_EN;
+		writel(val, &phy0_ctrl->phy_con0);
+		writel(val, &phy1_ctrl->phy_con0);
+
+		val = PHY_CON2_RESET_VAL;
+		val |= INIT_DESKEW_EN;
+		writel(val, &phy0_ctrl->phy_con2);
+		writel(val, &phy1_ctrl->phy_con2);
+
+		val = PHY_CON0_RESET_VAL;
+		val |= P0_CMD_EN;
+		val |= BYTE_RDLVL_EN;
+		writel(val, &phy0_ctrl->phy_con0);
+		writel(val, &phy1_ctrl->phy_con0);
+
+		val = (mem->ctrl_start_point <<
+				PHY_CON12_CTRL_START_POINT_SHIFT) |
+			(mem->ctrl_inc << PHY_CON12_CTRL_INC_SHIFT) |
+			(mem->ctrl_force << PHY_CON12_CTRL_FORCE_SHIFT) |
+			(mem->ctrl_start << PHY_CON12_CTRL_START_SHIFT) |
+			(mem->ctrl_ref << PHY_CON12_CTRL_REF_SHIFT);
+		writel(val, &phy0_ctrl->phy_con12);
+		writel(val, &phy1_ctrl->phy_con12);
+
+		val = PHY_CON2_RESET_VAL;
+		val |= INIT_DESKEW_EN;
+		val |= RDLVL_GATE_EN;
+		writel(val, &phy0_ctrl->phy_con2);
+		writel(val, &phy1_ctrl->phy_con2);
+
+		val = PHY_CON0_RESET_VAL;
+		val |= P0_CMD_EN;
+		val |= BYTE_RDLVL_EN;
+		val |= CTRL_SHGATE;
+		writel(val, &phy0_ctrl->phy_con0);
+		writel(val, &phy1_ctrl->phy_con0);
+
+		val = PHY_CON1_RESET_VAL;
+		val &= ~(CTRL_GATEDURADJ_MASK);
+		writel(val, &phy0_ctrl->phy_con1);
+		writel(val, &phy1_ctrl->phy_con1);
+
+		writel(CTRL_RDLVL_GATE_ENABLE, &dmc->rdlvl_config);
+		i = RDLVL_COMPLETE_TIMEOUT;
+		while ((readl(&dmc->phystatus) &
+			(RDLVL_COMPLETE_CHO | RDLVL_COMPLETE_CH1)) !=
+			(RDLVL_COMPLETE_CHO | RDLVL_COMPLETE_CH1) && i > 0) {
+			/*
+			 * TODO(waihong): Comment on how long this take to
+			 * timeout
+			 */
+			sdelay(100);
+			i--;
+		}
+		if (!i)
+			return SETUP_ERR_RDLV_COMPLETE_TIMEOUT;
+		writel(CTRL_RDLVL_GATE_DISABLE, &dmc->rdlvl_config);
+
+		writel(0, &phy0_ctrl->phy_con14);
+		writel(0, &phy1_ctrl->phy_con14);
+
+		val = (mem->ctrl_start_point <<
+				PHY_CON12_CTRL_START_POINT_SHIFT) |
+			(mem->ctrl_inc << PHY_CON12_CTRL_INC_SHIFT) |
+			(mem->ctrl_force << PHY_CON12_CTRL_FORCE_SHIFT) |
+			(mem->ctrl_start << PHY_CON12_CTRL_START_SHIFT) |
+			(mem->ctrl_dll_on << PHY_CON12_CTRL_DLL_ON_SHIFT) |
+			(mem->ctrl_ref << PHY_CON12_CTRL_REF_SHIFT);
+		writel(val, &phy0_ctrl->phy_con12);
+		writel(val, &phy1_ctrl->phy_con12);
+
+		update_reset_dll(dmc, DDR_MODE_DDR3);
+	}
+
+	/* Send PALL command */
+	dmc_config_prech(mem, dmc);
+
+	writel(mem->memcontrol, &dmc->memcontrol);
+
+	/* Set DMC Concontrol and enable auto-refresh counter */
+	writel(mem->concontrol | (mem->rd_fetch << CONCONTROL_RD_FETCH_SHIFT)
+		| (mem->aref_en << CONCONTROL_AREF_EN_SHIFT), &dmc->concontrol);
+	return 0;
+}
diff --git a/src/cpu/samsung/exynos5250/exynos_cache.c b/src/cpu/samsung/exynos5250/exynos_cache.c
new file mode 100644
index 0000000..ec858d1
--- /dev/null
+++ b/src/cpu/samsung/exynos5250/exynos_cache.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2012 Samsung Electronics.
+ * Arun Mankuzhi <arun.m@samsung.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <system.h>
+
+#include <armv7.h>
+
+enum l2_cache_params {
+	CACHE_TAG_RAM_SETUP = (1<<9),
+	CACHE_DATA_RAM_SETUP = (1<<5),
+	CACHE_TAG_RAM_LATENCY = (2<<6),
+	CACHE_DATA_RAM_LATENCY = (2<<0)
+};
+
+
+/* FIXME(dhendrix): maybe move this to a romstage-specific file? */
+#ifdef __PRE_RAM__
+void enable_caches(void)
+{
+	/* Enable D-cache. I-cache is already enabled in start.S */
+	dcache_enable();
+}
+#endif
+
+/*
+ * Set L2 cache parameters
+ */
+static void exynos5_set_l2cache_params(void)
+{
+	unsigned int val = 0;
+
+	asm volatile("mrc p15, 1, %0, c9, c0, 2\n" : "=r"(val));
+
+	val |= CACHE_TAG_RAM_SETUP |
+		CACHE_DATA_RAM_SETUP |
+		CACHE_TAG_RAM_LATENCY |
+		CACHE_DATA_RAM_LATENCY;
+
+	asm volatile("mcr p15, 1, %0, c9, c0, 2\n" : : "r"(val));
+}
+
+/*
+ * Sets L2 cache related parameters before enabling data cache
+ */
+void v7_outer_cache_enable(void)
+{
+	exynos5_set_l2cache_params();
+}
+
+/* stubs so we don't need weak symbols in cache_v7.c */
+void v7_outer_cache_disable(void)
+{
+}
+
+void v7_outer_cache_flush_all(void)
+{
+}
+
+void v7_outer_cache_inval_all(void)
+{
+}
+
+void v7_outer_cache_flush_range(u32 start, u32 end)
+{
+}
+
+void v7_outer_cache_inval_range(u32 start, u32 end)
+{
+}
diff --git a/src/cpu/samsung/exynos5250/lowlevel_init.S b/src/cpu/samsung/exynos5250/lowlevel_init.S
new file mode 100644
index 0000000..883db9d
--- /dev/null
+++ b/src/cpu/samsung/exynos5250/lowlevel_init.S
@@ -0,0 +1,32 @@
+/*
+ * Lowlevel setup for SMDK5250 board based on S5PC520
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+	.globl lowlevel_init
+lowlevel_init:
+	/*
+	 * Set the stack pointer, although it will be overwriten by the caller
+	 * It seems we will not boot if this function is empty.
+	 */
+	ldr	sp, =CONFIG_IRAM_STACK
+	mov	pc, lr
diff --git a/src/cpu/samsung/exynos5250/lowlevel_init_c.c b/src/cpu/samsung/exynos5250/lowlevel_init_c.c
new file mode 100644
index 0000000..2215fb7
--- /dev/null
+++ b/src/cpu/samsung/exynos5250/lowlevel_init_c.c
@@ -0,0 +1,120 @@
+/*
+ * Lowlevel setup for SMDK5250 board based on S5PC520
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Copyright (c) 2012 The Chromium OS Authors.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <config.h>
+#include <cpu/samsung/exynos5-common/exynos5-common.h>
+#include <cpu/samsung/exynos5-common/spl.h>
+#include <cpu/samsung/exynos5250/clock_init.h>
+#include <cpu/samsung/exynos5250/cpu.h>
+#include <cpu/samsung/exynos5250/dmc.h>
+#include <cpu/samsung/exynos5250/pinmux.h>
+#include <cpu/samsung/exynos5250/power.h>
+#include <cpu/samsung/exynos5250/setup.h>
+#include <cpu/samsung/exynos5250/tzpc.h>
+#include "setup.h"
+
+
+void do_barriers(void);	/* FIXME: make gcc shut up about "no previous prototype" */
+
+void do_barriers(void)
+{
+	/*
+	 * The reason we don't write out the instructions dsb/isb/sev:
+	 * While ARM Cortex-A8 supports ARM v7 instruction set (-march=armv7a),
+	 * we compile with -march=armv5 to allow more compilers to work.
+	 * For U-Boot code this has no performance impact.
+	 */
+	__asm__ __volatile__(
+#if defined(__thumb__)
+	".hword 0xF3BF, 0x8F4F\n"  /* dsb; darn -march=armv5 */
+	".hword 0xF3BF, 0x8F6F\n"  /* isb; darn -march=armv5 */
+	".hword 0xBF40\n"          /* sev; darn -march=armv5 */
+#else
+	".word  0xF57FF04F\n"      /* dsb; darn -march=armv5 */
+	".word  0xF57FF06F\n"      /* isb; darn -march=armv5 */
+	".word  0xE320F004\n"      /* sev; darn -march=armv5 */
+#endif
+	);
+}
+
+/* These are the things we can do during low-level init */
+enum {
+	DO_WAKEUP	= 1 << 0,
+	DO_UART		= 1 << 1,
+	DO_CLOCKS	= 1 << 2,
+	DO_POWER	= 1 << 3,
+};
+
+int lowlevel_init_subsystems(void)
+{
+	uint32_t reset_status;
+	int actions = 0;
+
+	do_barriers();
+
+	/* Setup cpu info which is needed to select correct register offsets */
+	cpu_info_init();
+
+	reset_status = power_read_reset_status();
+
+	switch (reset_status) {
+	case S5P_CHECK_SLEEP:
+		actions = DO_CLOCKS | DO_WAKEUP;
+		break;
+	case S5P_CHECK_DIDLE:
+	case S5P_CHECK_LPA:
+		actions = DO_WAKEUP;
+	default:
+		/* This is a normal boot (not a wake from sleep) */
+		actions = DO_UART | DO_CLOCKS | DO_POWER;
+	}
+
+	if (actions & DO_POWER)
+		power_init();
+	if (actions & DO_CLOCKS)
+		system_clock_init();
+	if (actions & DO_UART) {
+		/* Set up serial UART so we can printf() */
+//		exynos_pinmux_config(EXYNOS_UART, PINMUX_FLAG_NONE);
+		/* FIXME(dhendrix): add a function for mapping
+		   CONFIG_CONSOLE_SERIAL_UART_ADDRESS to PERIPH_ID_UARTn */
+		exynos_pinmux_config(PERIPH_ID_UART3, PINMUX_FLAG_NONE);
+		/* FIXME(dhendrix): serial_init() does not seem to
+		   actually do anything !?!? */
+//		serial_init();
+		init_timer();	/* FIXME(dhendrix): was timer_init() */
+	}
+
+/* FIXME(dhendrix): place this somewhere for ramstage... */
+#if 0
+	if (actions & DO_CLOCKS) {
+		mem_ctrl_init();
+		tzpc_init();
+	}
+#endif
+
+	return actions & DO_WAKEUP;
+}
diff --git a/src/cpu/samsung/exynos5250/pinmux.c b/src/cpu/samsung/exynos5250/pinmux.c
new file mode 100644
index 0000000..becced2
--- /dev/null
+++ b/src/cpu/samsung/exynos5250/pinmux.c
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2012 Samsung Electronics.
+ * Abhilash Kesavan <a.kesavan@samsung.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <arch/gpio.h>
+#include <cpu/samsung/exynos5250/gpio.h>
+#include <cpu/samsung/exynos5250/cpu.h>
+#include <cpu/samsung/exynos5250/pinmux.h>
+#include <cpu/samsung/exynos5-common/sromc.h>
+
+int exynos_pinmux_config(enum periph_id peripheral, int flags)
+{
+	int i, start, count, start_ext, pin_ext, pin, drv;
+
+	switch (peripheral) {
+	case PERIPH_ID_UART0:
+	case PERIPH_ID_UART1:
+	case PERIPH_ID_UART2:
+	case PERIPH_ID_UART3:
+		switch (peripheral) {
+		default:
+		case PERIPH_ID_UART0:
+			start = GPIO_A00; count = 4;
+			break;
+		case PERIPH_ID_UART1:
+			start = GPIO_A04; count = 4;
+			break;
+		case PERIPH_ID_UART2:
+			start = GPIO_A10; count = 4;
+			break;
+		case PERIPH_ID_UART3:
+			start = GPIO_A14; count = 2;
+			break;
+		}
+		for (i = start; i < start + count; i++) {
+			gpio_set_pull(i, EXYNOS_GPIO_PULL_NONE);
+			gpio_cfg_pin(i, EXYNOS_GPIO_FUNC(0x2));
+		}
+		break;
+	case PERIPH_ID_SDMMC0:
+	case PERIPH_ID_SDMMC1:
+	case PERIPH_ID_SDMMC2:
+	case PERIPH_ID_SDMMC3:
+		pin = EXYNOS_GPIO_FUNC(0x2);
+		pin_ext = EXYNOS_GPIO_FUNC(0x2);
+		drv = EXYNOS_GPIO_DRV_4X;
+		switch (peripheral) {
+		default:
+		case PERIPH_ID_SDMMC0:
+			start = GPIO_C00;
+			start_ext = GPIO_C10;
+			break;
+		case PERIPH_ID_SDMMC1:
+			start = GPIO_C20;
+			start_ext = 0;
+			break;
+		case PERIPH_ID_SDMMC2:
+			start = GPIO_C30;
+			/*
+			 * TODO: (alim.akhtar@samsung.com)
+			 * add support for 8 bit mode (needs to be a per-board
+			 * option, so in the FDT).
+			 */
+			start_ext = 0;
+			break;
+		case PERIPH_ID_SDMMC3:
+			/*
+			 * TODO: Need to add defintions for GPC4 before
+			 * enabling this.
+			 */
+			debug("SDMMC3 not supported yet");
+			return -1;
+		}
+		if ((flags & PINMUX_FLAG_8BIT_MODE) && !start_ext) {
+			debug("SDMMC device %d does not support 8bit mode",
+					peripheral);
+			return -1;
+		}
+		if (flags & PINMUX_FLAG_8BIT_MODE) {
+			assert(peripheral == PERIPH_ID_SDMMC0);
+			for (i = 0; i <= 3; i++) {
+				gpio_cfg_pin(start_ext + i, pin_ext);
+				gpio_set_pull(start_ext + i,
+					      EXYNOS_GPIO_PULL_UP);
+				gpio_set_drv(start_ext + i, drv);
+			}
+		}
+		for (i = 0; i < 2; i++) {
+			gpio_cfg_pin(start + i, pin);
+			gpio_set_pull(start + i, EXYNOS_GPIO_PULL_NONE);
+			gpio_set_drv(start + i, drv);
+		}
+		for (i = 2; i <= 6; i++) {
+			gpio_cfg_pin(start + i, pin);
+			gpio_set_pull(start + i, EXYNOS_GPIO_PULL_UP);
+			gpio_set_drv(start + i, drv);
+		}
+		break;
+	case PERIPH_ID_SROMC:
+		/*
+		 * SROM:CS1 and EBI
+		 *
+		 * GPY0[0]	SROM_CSn[0]
+		 * GPY0[1]	SROM_CSn[1](2)
+		 * GPY0[2]	SROM_CSn[2]
+		 * GPY0[3]	SROM_CSn[3]
+		 * GPY0[4]	EBI_OEn(2)
+		 * GPY0[5]	EBI_EEn(2)
+		 *
+		 * GPY1[0]	EBI_BEn[0](2)
+		 * GPY1[1]	EBI_BEn[1](2)
+		 * GPY1[2]	SROM_WAIT(2)
+		 * GPY1[3]	EBI_DATA_RDn(2)
+		 */
+		gpio_cfg_pin(GPIO_Y00 + (flags & PINMUX_FLAG_BANK),
+				EXYNOS_GPIO_FUNC(2));
+		gpio_cfg_pin(GPIO_Y04, EXYNOS_GPIO_FUNC(2));
+		gpio_cfg_pin(GPIO_Y05, EXYNOS_GPIO_FUNC(2));
+
+		for (i = 2; i < 4; i++)
+			gpio_cfg_pin(GPIO_Y10 + i, EXYNOS_GPIO_FUNC(2));
+
+		/*
+		 * EBI: 8 Addrss Lines
+		 *
+		 * GPY3[0]	EBI_ADDR[0](2)
+		 * GPY3[1]	EBI_ADDR[1](2)
+		 * GPY3[2]	EBI_ADDR[2](2)
+		 * GPY3[3]	EBI_ADDR[3](2)
+		 * GPY3[4]	EBI_ADDR[4](2)
+		 * GPY3[5]	EBI_ADDR[5](2)
+		 * GPY3[6]	EBI_ADDR[6](2)
+		 * GPY3[7]	EBI_ADDR[7](2)
+		 *
+		 * EBI: 16 Data Lines
+		 *
+		 * GPY5[0]	EBI_DATA[0](2)
+		 * GPY5[1]	EBI_DATA[1](2)
+		 * GPY5[2]	EBI_DATA[2](2)
+		 * GPY5[3]	EBI_DATA[3](2)
+		 * GPY5[4]	EBI_DATA[4](2)
+		 * GPY5[5]	EBI_DATA[5](2)
+		 * GPY5[6]	EBI_DATA[6](2)
+		 * GPY5[7]	EBI_DATA[7](2)
+		 *
+		 * GPY6[0]	EBI_DATA[8](2)
+		 * GPY6[1]	EBI_DATA[9](2)
+		 * GPY6[2]	EBI_DATA[10](2)
+		 * GPY6[3]	EBI_DATA[11](2)
+		 * GPY6[4]	EBI_DATA[12](2)
+		 * GPY6[5]	EBI_DATA[13](2)
+		 * GPY6[6]	EBI_DATA[14](2)
+		 * GPY6[7]	EBI_DATA[15](2)
+		 */
+		for (i = 0; i < 8; i++) {
+			gpio_cfg_pin(GPIO_Y30 + i, EXYNOS_GPIO_FUNC(2));
+			gpio_set_pull(GPIO_Y30 + i, EXYNOS_GPIO_PULL_UP);
+
+			gpio_cfg_pin(GPIO_Y50 + i, EXYNOS_GPIO_FUNC(2));
+			gpio_set_pull(GPIO_Y50 + i, EXYNOS_GPIO_PULL_UP);
+
+			if (flags & PINMUX_FLAG_16BIT) {
+				gpio_cfg_pin(GPIO_Y60 + i, EXYNOS_GPIO_FUNC(2));
+				gpio_set_pull(GPIO_Y60 + i,
+					      EXYNOS_GPIO_PULL_UP);
+			}
+		}
+		break;
+	case PERIPH_ID_SPI0:
+	case PERIPH_ID_SPI1:
+	case PERIPH_ID_SPI2:
+	case PERIPH_ID_SPI3: {
+		int cfg;
+
+		switch (peripheral) {
+		default:
+		case PERIPH_ID_SPI0:
+			start = GPIO_A20;
+			cfg = 0x2;
+			break;
+		case PERIPH_ID_SPI1:
+			start = GPIO_A24;
+			cfg = 0x2;
+			break;
+		case PERIPH_ID_SPI2:
+			start = GPIO_B11;
+			cfg = 0x5;
+			break;
+		case PERIPH_ID_SPI3:
+			start = GPIO_E00;
+			cfg = 0x2;
+			break;
+		}
+
+		for (i = 0; i < 4; i++)
+			gpio_cfg_pin(start + i, EXYNOS_GPIO_FUNC(cfg));
+		break;
+	}
+	case PERIPH_ID_SPI4:
+		for (i = 0; i < 2; i++)
+			gpio_cfg_pin(GPIO_F02 + i, EXYNOS_GPIO_FUNC(0x4));
+		for (i = 2; i < 4; i++)
+			gpio_cfg_pin(GPIO_E02 + i, EXYNOS_GPIO_FUNC(0x4));
+		break;
+	case PERIPH_ID_BACKLIGHT:
+		gpio_cfg_pin(GPIO_B20, EXYNOS_GPIO_OUTPUT);
+		gpio_set_value(GPIO_B20, 1);
+		break;
+	case PERIPH_ID_LCD:
+		gpio_cfg_pin(GPIO_Y25, EXYNOS_GPIO_OUTPUT);
+		gpio_set_value(GPIO_Y25, 1);
+		gpio_cfg_pin(GPIO_X15, EXYNOS_GPIO_OUTPUT);
+		gpio_set_value(GPIO_X15, 1);
+		gpio_cfg_pin(GPIO_X30, EXYNOS_GPIO_OUTPUT);
+		gpio_set_value(GPIO_X30, 1);
+		break;
+	case PERIPH_ID_I2C0:
+		gpio_cfg_pin(GPIO_B30, EXYNOS_GPIO_FUNC(0x2));
+		gpio_cfg_pin(GPIO_B31, EXYNOS_GPIO_FUNC(0x2));
+		gpio_set_pull(GPIO_B30, EXYNOS_GPIO_PULL_NONE);
+		gpio_set_pull(GPIO_B31, EXYNOS_GPIO_PULL_NONE);
+		break;
+	case PERIPH_ID_I2C1:
+		gpio_cfg_pin(GPIO_B32, EXYNOS_GPIO_FUNC(0x2));
+		gpio_cfg_pin(GPIO_B33, EXYNOS_GPIO_FUNC(0x2));
+		gpio_set_pull(GPIO_B32, EXYNOS_GPIO_PULL_NONE);
+		gpio_set_pull(GPIO_B33, EXYNOS_GPIO_PULL_NONE);
+		break;
+	case PERIPH_ID_I2C2:
+		gpio_cfg_pin(GPIO_A06, EXYNOS_GPIO_FUNC(0x3));
+		gpio_cfg_pin(GPIO_A07, EXYNOS_GPIO_FUNC(0x3));
+		gpio_set_pull(GPIO_A06, EXYNOS_GPIO_PULL_NONE);
+		gpio_set_pull(GPIO_A07, EXYNOS_GPIO_PULL_NONE);
+		break;
+	case PERIPH_ID_I2C3:
+		gpio_cfg_pin(GPIO_A12, EXYNOS_GPIO_FUNC(0x3));
+		gpio_cfg_pin(GPIO_A13, EXYNOS_GPIO_FUNC(0x3));
+		gpio_set_pull(GPIO_A12, EXYNOS_GPIO_PULL_NONE);
+		gpio_set_pull(GPIO_A13, EXYNOS_GPIO_PULL_NONE);
+		break;
+	case PERIPH_ID_I2C4:
+		gpio_cfg_pin(GPIO_A20, EXYNOS_GPIO_FUNC(0x3));
+		gpio_cfg_pin(GPIO_A21, EXYNOS_GPIO_FUNC(0x3));
+		gpio_set_pull(GPIO_A20, EXYNOS_GPIO_PULL_NONE);
+		gpio_set_pull(GPIO_A21, EXYNOS_GPIO_PULL_NONE);
+		break;
+	case PERIPH_ID_I2C5:
+		gpio_cfg_pin(GPIO_A22, EXYNOS_GPIO_FUNC(0x3));
+		gpio_cfg_pin(GPIO_A23, EXYNOS_GPIO_FUNC(0x3));
+		gpio_set_pull(GPIO_A22, EXYNOS_GPIO_PULL_NONE);
+		gpio_set_pull(GPIO_A23, EXYNOS_GPIO_PULL_NONE);
+		break;
+	case PERIPH_ID_I2C6:
+		gpio_cfg_pin(GPIO_B13, EXYNOS_GPIO_FUNC(0x4));
+		gpio_cfg_pin(GPIO_B14, EXYNOS_GPIO_FUNC(0x4));
+		break;
+	case PERIPH_ID_I2C7:
+		gpio_cfg_pin(GPIO_B22, EXYNOS_GPIO_FUNC(0x3));
+		gpio_cfg_pin(GPIO_B23, EXYNOS_GPIO_FUNC(0x3));
+		gpio_set_pull(GPIO_B22, EXYNOS_GPIO_PULL_NONE);
+		gpio_set_pull(GPIO_B23, EXYNOS_GPIO_PULL_NONE);
+		break;
+	case PERIPH_ID_DPHPD:
+		/* Set Hotplug detect for DP */
+		gpio_cfg_pin(GPIO_X07, EXYNOS_GPIO_FUNC(0x3));
+
+		/*
+		 * Hotplug detect should have an external pullup; disable the
+		 * internal pulldown so they don't fight.
+		 */
+		gpio_set_pull(GPIO_X07, EXYNOS_GPIO_PULL_NONE);
+		break;
+	case PERIPH_ID_I2S1:
+		for (i = 0; i < 5; i++)
+			gpio_cfg_pin(GPIO_B00 + i, EXYNOS_GPIO_FUNC(0x02));
+		break;
+	default:
+		debug("%s: invalid peripheral %d", __func__, peripheral);
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/src/cpu/samsung/exynos5250/power.c b/src/cpu/samsung/exynos5250/power.c
new file mode 100644
index 0000000..2000fee
--- /dev/null
+++ b/src/cpu/samsung/exynos5250/power.c
@@ -0,0 +1,202 @@
+/*
+ * Power setup code for EXYNOS5
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <arch/io.h>
+#include <console/console.h>
+#include <cpu/samsung/exynos5250/cpu.h>
+#include <cpu/samsung/exynos5250/power.h>
+#include <cpu/samsung/exynos5250/sysreg.h>
+#include <cpu/samsung/exynos5-common/spl.h>
+#include <device/i2c.h>
+#include <device/power/max77686.h>
+
+static void ps_hold_setup(void)
+{
+	struct exynos5_power *power =
+		(struct exynos5_power *)samsung_get_base_power();
+
+	/* Set PS-Hold high */
+	setbits_le32(&power->ps_hold_ctrl, POWER_PS_HOLD_CONTROL_DATA_HIGH);
+}
+
+void power_reset(void)
+{
+	struct exynos5_power *power =
+		(struct exynos5_power *)samsung_get_base_power();
+
+	/* Clear inform1 so there's no change we think we've got a wake reset */
+	power->inform1 = 0;
+
+	setbits_le32(&power->sw_reset, 1);
+}
+
+/* This function never returns */
+void power_shutdown(void)
+{
+	struct exynos5_power *power =
+		(struct exynos5_power *)samsung_get_base_power();
+
+	clrbits_le32(&power->ps_hold_ctrl, POWER_PS_HOLD_CONTROL_DATA_HIGH);
+
+	hang();
+}
+
+void power_enable_dp_phy(void)
+{
+	struct exynos5_power *power =
+		(struct exynos5_power *)samsung_get_base_power();
+
+	setbits_le32(&power->dptx_phy_control, DPTX_PHY_ENABLE);
+}
+
+void power_enable_usb_phy(void)
+{
+	struct exynos5_sysreg *sysreg =
+		(struct exynos5_sysreg *)samsung_get_base_sysreg();
+	struct exynos5_power *power =
+		(struct exynos5_power *)samsung_get_base_power();
+	unsigned int phy_cfg;
+
+	/* Setting USB20PHY_CONFIG register to USB 2.0 HOST link */
+	phy_cfg = readl(&sysreg->usb20_phy_cfg);
+	if (phy_cfg & USB20_PHY_CFG_EN) {
+		debug("USB 2.0 HOST link already selected\n");
+	} else {
+		phy_cfg |= USB20_PHY_CFG_EN;
+		writel(phy_cfg, &sysreg->usb20_phy_cfg);
+	}
+
+	/* Enabling USBHOST_PHY */
+	setbits_le32(&power->usb_host_phy_ctrl, POWER_USB_HOST_PHY_CTRL_EN);
+}
+
+void power_disable_usb_phy(void)
+{
+	struct exynos5_power *power =
+		(struct exynos5_power *)samsung_get_base_power();
+
+	/* Disabling USBHost_PHY */
+	clrbits_le32(&power->usb_host_phy_ctrl, POWER_USB_HOST_PHY_CTRL_EN);
+}
+
+void power_enable_hw_thermal_trip(void)
+{
+	struct exynos5_power *power =
+		(struct exynos5_power *)samsung_get_base_power();
+
+	/* Enable HW thermal trip */
+	setbits_le32(&power->ps_hold_ctrl, POWER_ENABLE_HW_TRIP);
+}
+
+uint32_t power_read_reset_status(void)
+{
+	struct exynos5_power *power =
+		(struct exynos5_power *)samsung_get_base_power();
+
+	return power->inform1;
+}
+
+void power_exit_wakeup(void)
+{
+	struct exynos5_power *power =
+		(struct exynos5_power *)samsung_get_base_power();
+	typedef void (*resume_func)(void);
+
+	((resume_func)power->inform0)();
+}
+
+/**
+ * Initialize the pmic voltages to power up the system
+ * This also calls i2c_init so that we can program the pmic
+ *
+ * REG_ENABLE = 0, needed to set the buck/ldo enable bit ON
+ *
+ * @return	Return 0 if ok, else -1
+ */
+int power_init(void)
+{
+	int error = 0;
+
+	/* FIXME(dhendrix): not necessary for initial bringup... */
+#if 0
+#ifdef CONFIG_SPL_BUILD
+	struct spl_machine_param *param = spl_get_machine_params();
+
+	/* Set the i2c register address base so i2c works before FDT */
+	i2c_set_early_reg(param->i2c_base);
+#endif
+#endif
+
+	ps_hold_setup();
+
+	/* FIXME(dhendrix): not necessary for initial bringup... */
+#if 0
+	/* init the i2c so that we can program pmic chip */
+	i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
+
+	/*
+	 * We're using CR1616 coin cell battery that is non-rechargeable
+	 * battery. But, BBCHOSTEN bit of the BBAT Charger Register in
+	 * MAX77686 is enabled by default for charging coin cell.
+	 *
+	 * Also, we cannot meet the coin cell reverse current spec. in UL
+	 * standard if BBCHOSTEN bit is enabled.
+	 *
+	 * Disable Coin BATT Charging
+	 */
+	error = max77686_disable_backup_batt();
+
+	error |= max77686_volsetting(PMIC_BUCK2, CONFIG_VDD_ARM_MV,
+						REG_ENABLE, MAX77686_MV);
+	error |= max77686_volsetting(PMIC_BUCK3, CONFIG_VDD_INT_UV,
+						REG_ENABLE, MAX77686_UV);
+	error |= max77686_volsetting(PMIC_BUCK1, CONFIG_VDD_MIF_MV,
+						REG_ENABLE, MAX77686_MV);
+	error |= max77686_volsetting(PMIC_BUCK4, CONFIG_VDD_G3D_MV,
+						REG_ENABLE, MAX77686_MV);
+	error |= max77686_volsetting(PMIC_LDO2, CONFIG_VDD_LDO2_MV,
+						REG_ENABLE, MAX77686_MV);
+	error |= max77686_volsetting(PMIC_LDO3, CONFIG_VDD_LDO3_MV,
+						REG_ENABLE, MAX77686_MV);
+	error |= max77686_volsetting(PMIC_LDO5, CONFIG_VDD_LDO5_MV,
+						REG_ENABLE, MAX77686_MV);
+	error |= max77686_volsetting(PMIC_LDO10, CONFIG_VDD_LDO10_MV,
+						REG_ENABLE, MAX77686_MV);
+#endif
+	if (error != 0)
+		printk(BIOS_ERR, "power init failed\n");
+
+	return error;
+}
+
+void power_enable_xclkout(void)
+{
+	struct exynos5_power *power =
+		(struct exynos5_power *)samsung_get_base_power();
+
+	/* use xxti for xclk out */
+	clrsetbits_le32(&power->pmu_debug, PMU_DEBUG_CLKOUT_SEL_MASK,
+				PMU_DEBUG_XXTI);
+}
diff --git a/src/cpu/samsung/exynos5250/sata.c b/src/cpu/samsung/exynos5250/sata.c
new file mode 100644
index 0000000..b3e464e
--- /dev/null
+++ b/src/cpu/samsung/exynos5250/sata.c
@@ -0,0 +1,431 @@
+/*
+ * Copyright (c) 2012 The Chromium OS Authors.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+#include <ahci.h>
+#include <common.h>
+#include <fdtdec.h>
+#include <scsi.h>
+#include <asm/arch-exynos5/sata.h>
+#include <asm/arch/pinmux.h>
+#include <asm/errno.h>
+#include <asm/gpio.h>
+#include <asm/types.h>
+
+#define SATA_AHCI_AXI			0x122f0000
+#define SATA_PHCTRL_APB			0x12170000
+#define SATA_PHY_I2C_ABP		0x121d0000
+#define EXYNOS5_SATA_PHY_CONTROL	(0x10040000 + 0x724)
+#define S5P_PMU_SATA_PHY_CONTROL_EN	0x1
+
+void * const phy_ctrl = (void *)SATA_PHCTRL_APB;
+void * const phy_i2c_base = (void *)SATA_PHY_I2C_ABP;
+
+typedef unsigned char bool;
+#define true 1
+#define false 0
+
+
+#define SATA_TIME_LIMIT			10000
+#define SATA_PHY_I2C_SLAVE_ADDRS	0x70
+
+#define SATA_RESET			0x4
+#define RESET_CMN_RST_N			(1 << 1)
+#define LINK_RESET			0xF0000
+
+#define SATA_MODE0			0x10
+
+#define SATA_CTRL0			0x14
+#define CTRL0_P0_PHY_CALIBRATED_SEL	(1 << 9)
+#define CTRL0_P0_PHY_CALIBRATED		(1 << 8)
+
+#define SATA_PHSATA_CTRLM		0xE0
+#define PHCTRLM_REF_RATE		(1 << 1)
+#define PHCTRLM_HIGH_SPEED		(1 << 0)
+
+#define SATA_PHSATA_STATM		0xF0
+#define PHSTATM_PLL_LOCKED		(1 << 0)
+
+
+/********************** I2C**************/
+#define SATA_I2C_CON			0x00
+#define SATA_I2C_STAT			0x04
+#define SATA_I2C_ADDR			0x08
+#define SATA_I2C_DS			0x0C
+#define SATA_I2C_LC			0x10
+
+/* I2CCON reg */
+#define CON_ACKEN			(1 << 7)
+#define CON_CLK512			(1 << 6)
+#define CON_CLK16			(~CON_CLK512)
+#define CON_INTEN			(1 << 5)
+#define CON_INTPND			(1 << 4)
+#define CON_TXCLK_PS			(0xF)
+
+/* I2CSTAT reg */
+#define STAT_MSTT			(0x3 << 6)
+#define STAT_BSYST			(1 << 5)
+#define STAT_RTEN			(1 << 4)
+#define STAT_LAST			(1 << 0)
+
+#define LC_FLTR_EN			(1 << 2)
+
+#define SATA_PHY_CON_RESET		0xF003F
+
+#define SCLK_SATA_FREQ			(66 * MHZ)
+
+
+
+enum {
+	SATA_GENERATION1,
+	SATA_GENERATION2,
+	SATA_GENERATION3,
+};
+
+static bool sata_is_reg(void __iomem *base, u32 reg, u32 checkbit, u32 Status)
+{
+	if ((__raw_readl(base + reg) & checkbit) == Status)
+		return true;
+	else
+		return false;
+}
+
+static bool wait_for_reg_status(void __iomem *base, u32 reg, u32 checkbit,
+		u32 Status)
+{
+	u32 time_limit_cnt = 0;
+	while (!sata_is_reg(base, reg, checkbit, Status)) {
+		if (time_limit_cnt == SATA_TIME_LIMIT) {
+			return false;
+		}
+		udelay(1000);
+		time_limit_cnt++;
+	}
+	return true;
+}
+
+
+static void sata_set_gen(u8 gen)
+{
+	__raw_writel(gen, phy_ctrl + SATA_MODE0);
+}
+
+/* Address :I2C Address */
+static void sata_i2c_write_addrs(u8 data)
+{
+	__raw_writeb((data & 0xFE), phy_i2c_base + SATA_I2C_DS);
+}
+
+static void sata_i2c_write_data(u8 data)
+{
+	__raw_writeb((data), phy_i2c_base + SATA_I2C_DS);
+}
+
+static void sata_i2c_start(void)
+{
+	u32 val;
+	val = __raw_readl(phy_i2c_base + SATA_I2C_STAT);
+	val |= STAT_BSYST;
+	__raw_writel(val, phy_i2c_base + SATA_I2C_STAT);
+}
+
+static void sata_i2c_stop(void)
+{
+	u32 val;
+	val = __raw_readl(phy_i2c_base + SATA_I2C_STAT);
+	val &= ~STAT_BSYST;
+	__raw_writel(val, phy_i2c_base + SATA_I2C_STAT);
+}
+
+static bool sata_i2c_get_int_status(void)
+{
+	if ((__raw_readl(phy_i2c_base + SATA_I2C_CON)) & CON_INTPND)
+		return true;
+	else
+		return false;
+}
+
+static bool sata_i2c_is_tx_ack(void)
+{
+	if ((__raw_readl(phy_i2c_base + SATA_I2C_STAT)) & STAT_LAST)
+		return false;
+	else
+		return true;
+}
+
+static bool sata_i2c_is_bus_ready(void)
+{
+	if ((__raw_readl(phy_i2c_base + SATA_I2C_STAT)) & STAT_BSYST)
+		return false;
+	else
+		return true;
+}
+
+static bool sata_i2c_wait_for_busready(u32 time_out)
+{
+	while (--time_out) {
+		if (sata_i2c_is_bus_ready())
+			return true;
+		udelay(100);
+	}
+	return false;
+}
+
+static bool sata_i2c_wait_for_tx_ack(u32 time_out)
+{
+	while (--time_out) {
+		if (sata_i2c_get_int_status()) {
+			if (sata_i2c_is_tx_ack())
+				return true;
+		}
+		udelay(100);
+	}
+	return false;
+}
+
+static void sata_i2c_clear_int_status(void)
+{
+	u32 val;
+	val = __raw_readl(phy_i2c_base + SATA_I2C_CON);
+	val &= ~CON_INTPND;
+	__raw_writel(val, phy_i2c_base + SATA_I2C_CON);
+}
+
+
+static void sata_i2c_set_ack_gen(bool enable)
+{
+	u32 val;
+	if (enable) {
+		val = (__raw_readl(phy_i2c_base + SATA_I2C_CON)) | CON_ACKEN;
+		__raw_writel(val, phy_i2c_base + SATA_I2C_CON);
+	} else {
+		val = __raw_readl(phy_i2c_base + SATA_I2C_CON);
+		val &= ~CON_ACKEN;
+		__raw_writel(val, phy_i2c_base + SATA_I2C_CON);
+	}
+
+}
+
+static void sata_i2c_set_master_tx(void)
+{
+	u32 val;
+	/* Disable I2C */
+	val = __raw_readl(phy_i2c_base + SATA_I2C_STAT);
+	val &= ~STAT_RTEN;
+	__raw_writel(val, phy_i2c_base + SATA_I2C_STAT);
+	/* Clear Mode */
+	val = __raw_readl(phy_i2c_base + SATA_I2C_STAT);
+	val &= ~STAT_MSTT;
+	__raw_writel(val, phy_i2c_base + SATA_I2C_STAT);
+
+	sata_i2c_clear_int_status();
+	/* interrupt disable */
+	val = __raw_readl(phy_i2c_base + SATA_I2C_CON);
+	val &= ~CON_INTEN;
+	__raw_writel(val, phy_i2c_base + SATA_I2C_CON);
+
+	/* Master, Send mode */
+	val = __raw_readl(phy_i2c_base + SATA_I2C_STAT);
+	val |=	STAT_MSTT;
+	__raw_writel(val, phy_i2c_base + SATA_I2C_STAT);
+
+	/* interrupt enable */
+	val = __raw_readl(phy_i2c_base + SATA_I2C_CON);
+	val |=	CON_INTEN;
+	__raw_writel(val, phy_i2c_base + SATA_I2C_CON);
+
+	/* Enable I2C */
+	val = __raw_readl(phy_i2c_base + SATA_I2C_STAT);
+	val |= STAT_RTEN;
+	__raw_writel(val, phy_i2c_base + SATA_I2C_STAT);
+}
+
+static void sata_i2c_init(void)
+{
+	u32 val;
+
+	val = __raw_readl(phy_i2c_base + SATA_I2C_CON);
+	val &= CON_CLK16;
+	__raw_writel(val, phy_i2c_base + SATA_I2C_CON);
+
+	val = __raw_readl(phy_i2c_base + SATA_I2C_CON);
+	val &= ~(CON_TXCLK_PS);
+	__raw_writel(val, phy_i2c_base + SATA_I2C_CON);
+
+	val = __raw_readl(phy_i2c_base + SATA_I2C_CON);
+	val |= (2 & CON_TXCLK_PS);
+	__raw_writel(val, phy_i2c_base + SATA_I2C_CON);
+
+	val = __raw_readl(phy_i2c_base + SATA_I2C_LC);
+	val &= ~(LC_FLTR_EN);
+	__raw_writel(val, phy_i2c_base + SATA_I2C_LC);
+
+	sata_i2c_set_ack_gen(false);
+}
+static bool sata_i2c_send(u8 slave_addrs, u8 addrs, u8 ucData)
+{
+	s32 ret = 0;
+	if (!sata_i2c_wait_for_busready(SATA_TIME_LIMIT))
+		return false;
+
+	sata_i2c_init();
+	sata_i2c_set_master_tx();
+
+	__raw_writel(SATA_PHY_CON_RESET, phy_ctrl + SATA_RESET);
+	sata_i2c_write_addrs(slave_addrs);
+	sata_i2c_start();
+	if (!sata_i2c_wait_for_tx_ack(SATA_TIME_LIMIT)) {
+		ret = false;
+		goto STOP;
+	}
+	sata_i2c_write_data(addrs);
+	sata_i2c_clear_int_status();
+	if (!sata_i2c_wait_for_tx_ack(SATA_TIME_LIMIT)) {
+		ret = false;
+		goto STOP;
+	}
+	sata_i2c_write_data(ucData);
+	sata_i2c_clear_int_status();
+	if (!sata_i2c_wait_for_tx_ack(SATA_TIME_LIMIT)) {
+		ret = false;
+		goto STOP;
+	}
+	ret = true;
+
+STOP:
+	sata_i2c_stop();
+	sata_i2c_clear_int_status();
+	sata_i2c_wait_for_busready(SATA_TIME_LIMIT);
+
+	return ret;
+}
+
+static bool ahci_phy_init(void __iomem *mmio)
+{
+	u8 uCount, i = 0;
+	/* 0x3A for 40bit I/F */
+	u8 reg_addrs[] = {0x22, 0x21, 0x3A};
+	/* 0x0B for 40bit I/F */
+	u8 default_setting_value[] = {0x30, 0x4f, 0x0B};
+
+	uCount = sizeof(reg_addrs)/sizeof(u8);
+	while (i < uCount) {
+		if (!sata_i2c_send(SATA_PHY_I2C_SLAVE_ADDRS, reg_addrs[i],
+					default_setting_value[i]))
+			return false;
+		i++;
+	}
+	return true;
+}
+
+static int exynos5_ahci_init(void __iomem *mmio)
+{
+	int val, ret;
+
+	__raw_writel(S5P_PMU_SATA_PHY_CONTROL_EN, EXYNOS5_SATA_PHY_CONTROL);
+
+	val = 0;
+	__raw_writel(val, phy_ctrl + SATA_RESET);
+	val = __raw_readl(phy_ctrl + SATA_RESET);
+	val |= 0x3D;
+	__raw_writel(val, phy_ctrl + SATA_RESET);
+
+	val = __raw_readl(phy_ctrl + SATA_RESET);
+	val |= LINK_RESET;
+	__raw_writel(val, phy_ctrl + SATA_RESET);
+
+	val = __raw_readl(phy_ctrl + SATA_RESET);
+	val |= RESET_CMN_RST_N;
+	__raw_writel(val, phy_ctrl + SATA_RESET);
+
+	val = __raw_readl(phy_ctrl + SATA_PHSATA_CTRLM);
+	val &= ~PHCTRLM_REF_RATE;
+	__raw_writel(val, phy_ctrl + SATA_PHSATA_CTRLM);
+
+	/* High speed enable for Gen3 */
+	val = __raw_readl(phy_ctrl + SATA_PHSATA_CTRLM);
+	val |= PHCTRLM_HIGH_SPEED;
+	__raw_writel(val, phy_ctrl + SATA_PHSATA_CTRLM);
+
+	/* Port0 is available */
+	__raw_writel(0x1, mmio + HOST_PORTS_IMPL);
+
+	ret = ahci_phy_init(mmio);
+
+	val = __raw_readl(phy_ctrl + SATA_CTRL0);
+	val |= CTRL0_P0_PHY_CALIBRATED_SEL|CTRL0_P0_PHY_CALIBRATED;
+	__raw_writel(val, phy_ctrl + SATA_CTRL0);
+	sata_set_gen(SATA_GENERATION3);
+
+	/* release cmu reset */
+	val = __raw_readl(phy_ctrl + SATA_RESET);
+	val &= ~RESET_CMN_RST_N;
+	__raw_writel(val, phy_ctrl + SATA_RESET);
+
+	val = __raw_readl(phy_ctrl + SATA_RESET);
+	val |= RESET_CMN_RST_N;
+	__raw_writel(val, phy_ctrl + SATA_RESET);
+
+	if (wait_for_reg_status(phy_ctrl, SATA_PHSATA_STATM,
+				PHSTATM_PLL_LOCKED, 1)) {
+		return ret;
+	}
+	return 0;
+}
+
+static int exynos5_sata_enable_power(const void *blob)
+{
+	int node;
+	struct fdt_gpio_state gpio;
+
+	node = fdtdec_next_compatible(blob, 0, COMPAT_GOOGLE_SATA);
+	if (node >= 0 &&
+	    fdtdec_decode_gpio(blob, node, "enable-gpios", &gpio) == 0) {
+		gpio_cfg_pin(gpio.gpio, EXYNOS_GPIO_OUTPUT);
+		gpio_set_value(gpio.gpio, 1);
+		return 0;
+	}
+	return -ENODEV;
+}
+
+static void exynos5_enable_clock_gates(void)
+{
+	/* Turn on all SATA clock gates & DMA gates.  */
+	const unsigned cmu_toppart	= 0x10020000;
+	const unsigned addr		= cmu_toppart + 0x944;
+	const unsigned sata_clocks	= (1 << 25) | (1 << 24) | (1 << 6);
+	const unsigned dma_clocks	= (2 << 1) | (1 << 1);
+	const unsigned clk_gate_ip_fsys = readl(addr);
+	writel(clk_gate_ip_fsys | sata_clocks | dma_clocks, addr);
+}
+
+int exynos5_sata_init(const void *blob)
+{
+	if (exynos5_sata_enable_power(blob) == 0) {
+		exynos5_enable_clock_gates();
+
+		if (exynos5_ahci_init((void *)SATA_AHCI_AXI)) {
+			ahci_init(SATA_AHCI_AXI);
+			scsi_scan(1);
+			return 0;
+		}
+	}
+	return -ENODEV;
+}
diff --git a/src/cpu/samsung/exynos5250/setup.h b/src/cpu/samsung/exynos5250/setup.h
index 5e59232..bb438c5 100644
--- a/src/cpu/samsung/exynos5250/setup.h
+++ b/src/cpu/samsung/exynos5250/setup.h
@@ -25,6 +25,10 @@
 #ifndef _SMDK5250_SETUP_H
 #define _SMDK5250_SETUP_H
 
+struct exynos5_dmc;
+enum ddr_mode;
+struct exynos5_phy_control;
+
 /* TZPC : Register Offsets */
 #define TZPC0_BASE		0x10100000
 #define TZPC1_BASE		0x10110000
@@ -683,7 +687,8 @@
 };
 
 /* Functions common between LPDDR2 and DDR3 */
-void sdelay(unsigned long);
+/* FIXME(dhendrix): conflicts with arch system.h version of sdelay()... */
+//void sdelay(unsigned long);
 
 /* CPU info initialization code */
 void cpu_info_init(void);
diff --git a/src/cpu/samsung/exynos5250/soc.c b/src/cpu/samsung/exynos5250/soc.c
new file mode 100644
index 0000000..be28d8c
--- /dev/null
+++ b/src/cpu/samsung/exynos5250/soc.c
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2011 The Chromium OS Authors. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <cpu/samsung/exynos5250/cpu.h>
+#include <cpu/samsung/exynos5250/periph.h>
+
+#include <cpu/samsung/exynos5250/uart.h>
+
+enum periph_id exynos5_get_periph_id(unsigned base_addr)
+{
+	enum periph_id id = PERIPH_ID_NONE;
+
+	switch (base_addr) {
+	case EXYNOS5_UART0_BASE:
+		id = PERIPH_ID_UART0;
+		break;
+	case EXYNOS5_UART1_BASE:
+		id = PERIPH_ID_UART1;
+		break;
+	case EXYNOS5_UART2_BASE:
+		id = PERIPH_ID_UART2;
+		break;
+	case EXYNOS5_UART3_BASE:
+		id = PERIPH_ID_UART3;
+		break;
+	default:
+		break;
+	}
+
+	return id;
+}
diff --git a/src/cpu/samsung/exynos5250/spl.c b/src/cpu/samsung/exynos5250/spl.c
new file mode 100644
index 0000000..00b584a
--- /dev/null
+++ b/src/cpu/samsung/exynos5250/spl.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+/* FIXME(dhendrix): file unneeded? */
+#if 0
+#include <asm/types.h>
+#include <asm/arch-exynos/cpu.h>
+#include <asm/arch-exynos/spl.h>
+
+/* Get the u-boot size from the SPL parameter table */
+unsigned int exynos_get_uboot_size(void)
+{
+	struct spl_machine_param *param = spl_get_machine_params();
+
+	return param->uboot_size;
+}
+
+/* Get the boot device from the SPL parameter table */
+enum boot_mode exynos_get_boot_device(void)
+{
+	struct spl_machine_param *param = spl_get_machine_params();
+
+	return param->boot_source;
+}
+#endif
diff --git a/src/cpu/samsung/exynos5250/tzpc_init.c b/src/cpu/samsung/exynos5250/tzpc_init.c
new file mode 100644
index 0000000..370c2ed
--- /dev/null
+++ b/src/cpu/samsung/exynos5250/tzpc_init.c
@@ -0,0 +1,57 @@
+/*
+ * Lowlevel setup for SMDK5250 board based on S5PC520
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <cpu/samsung/exynos5250/cpu.h>
+#include <asm/arch/dmc.h>
+#include <asm/arch/tzpc.h>
+#include"setup.h"
+
+/* Setting TZPC[TrustZone Protection Controller] */
+void tzpc_init(void)
+{
+	struct exynos5_tzpc *tzpc;
+	unsigned int addr;
+
+	for (addr = TZPC0_BASE; addr <= TZPC9_BASE; addr += TZPC_BASE_OFFSET) {
+		tzpc = (struct exynos5_tzpc *)addr;
+
+		if (addr == TZPC0_BASE)
+			writel(R0SIZE, &tzpc->r0size);
+
+		writel(DECPROTXSET, &tzpc->decprot0set);
+		writel(DECPROTXSET, &tzpc->decprot1set);
+
+		if (addr == TZPC9_BASE) {
+
+		/* TODO: Add comment here describing the numerical values
+		 * used below.
+		 */
+			writel(0xf0, &tzpc->decprot2set);
+			writel(0x50, &tzpc->decprot3set);
+		} else {
+			writel(DECPROTXSET, &tzpc->decprot2set);
+			writel(DECPROTXSET, &tzpc->decprot3set);
+		}
+	}
+}
diff --git a/src/cpu/samsung/exynos5250/uart.c b/src/cpu/samsung/exynos5250/uart.c
new file mode 100644
index 0000000..3d43976
--- /dev/null
+++ b/src/cpu/samsung/exynos5250/uart.c
@@ -0,0 +1,235 @@
+/*
+ * (C) Copyright 2009 SAMSUNG Electronics
+ * Minkyu Kang <mk7.kang@samsung.com>
+ * Heungjun Kim <riverful.kim@samsung.com>
+ *
+ * based on drivers/serial/s3c64xx.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+//#include <common.h>
+//#include <linux/compiler.h>
+#include <uart.h>
+#include <arch/io.h>
+//#include <asm/arch-exynos/spl.h>
+//#include <asm/global_data.h>
+//#include <fdtdec.h>
+//#include <serial.h>
+
+#include <console/console.h>	/* for __console definition */
+
+#include <cpu/samsung/exynos5-common/exynos5-common.h>
+#include <cpu/samsung/exynos5250/clk.h>
+#include <cpu/samsung/exynos5250/uart.h>
+
+#define RX_FIFO_COUNT_MASK	0xff
+#define RX_FIFO_FULL_MASK	(1 << 8)
+#define TX_FIFO_FULL_MASK	(1 << 24)
+
+/* FIXME(dhendrix): exynos5 has 4 UARTs and its functions in u-boot take a
+   base_port argument. However console_driver functions do not. */
+static uint32_t base_port = CONFIG_CONSOLE_SERIAL_UART_ADDRESS;
+#if 0
+/* Information about a serial port */
+struct fdt_serial {
+	u32 base_addr;	/* address of registers in physical memory */
+	u8 port_id;	/* uart port number */
+	u8 enabled;	/* 1 if enabled, 0 if disabled */
+} config = {
+	-1U
+};
+#endif
+
+#if 0
+static inline struct s5p_uart *s5p_get_base_uart(int dev_index)
+{
+	/* FIXME: there should be an assertion here if dev_index is >3 */
+	return (struct s5p_uart *)(EXYNOS5_UART0_BASE + (0x10000 * dev_index));
+}
+#endif
+
+/*
+ * The coefficient, used to calculate the baudrate on S5P UARTs is
+ * calculated as
+ * C = UBRDIV * 16 + number_of_set_bits_in_UDIVSLOT
+ * however, section 31.6.11 of the datasheet doesn't recomment using 1 for 1,
+ * 3 for 2, ... (2^n - 1) for n, instead, they suggest using these constants:
+ */
+static const int udivslot[] = {
+	0,
+	0x0080,
+	0x0808,
+	0x0888,
+	0x2222,
+	0x4924,
+	0x4a52,
+	0x54aa,
+	0x5555,
+	0xd555,
+	0xd5d5,
+	0xddd5,
+	0xdddd,
+	0xdfdd,
+	0xdfdf,
+	0xffdf,
+};
+
+static void serial_setbrg_dev(void)
+{
+//	struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
+	struct s5p_uart *uart = (struct s5p_uart *)base_port;
+	u32 uclk;
+	u32 baudrate = CONFIG_TTYS0_BAUD;
+	u32 val;
+	enum periph_id periph;
+
+	periph = exynos5_get_periph_id(base_port);
+	uclk = clock_get_periph_rate(periph);
+	val = uclk / baudrate;
+
+	writel(val / 16 - 1, &uart->ubrdiv);
+
+	/*
+	 * FIXME(dhendrix): the original uart.h had a "br_rest" value which
+	 * does not seem relevant to the exynos5250... not entirely sure
+	 * where/if we need to worry about it here
+	 */
+#if 0
+	if (s5p_uart_divslot())
+		writew(udivslot[val % 16], &uart->rest.slot);
+	else
+		writeb(val % 16, &uart->rest.value);
+#endif
+}
+
+/*
+ * Initialise the serial port with the given baudrate. The settings
+ * are always 8 data bits, no parity, 1 stop bit, no start bits.
+ */
+static void exynos5_init_dev(void)
+{
+//	struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
+	struct s5p_uart *uart = (struct s5p_uart *)base_port;
+
+	/* enable FIFOs */
+	writel(0x1, &uart->ufcon);
+	writel(0, &uart->umcon);
+	/* 8N1 */
+	writel(0x3, &uart->ulcon);
+	/* No interrupts, no DMA, pure polling */
+	writel(0x245, &uart->ucon);
+
+	serial_setbrg_dev();
+}
+
+static int exynos5_uart_err_check(int op)
+{
+	//struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
+	struct s5p_uart *uart = (struct s5p_uart *)base_port;
+	unsigned int mask;
+
+	/*
+	 * UERSTAT
+	 * Break Detect	[3]
+	 * Frame Err	[2] : receive operation
+	 * Parity Err	[1] : receive operation
+	 * Overrun Err	[0] : receive operation
+	 */
+	if (op)
+		mask = 0x8;
+	else
+		mask = 0xf;
+
+	return readl(&uart->uerstat) & mask;
+}
+
+/*
+ * Read a single byte from the serial port. Returns 1 on success, 0
+ * otherwise. When the function is succesfull, the character read is
+ * written into its argument c.
+ */
+static unsigned char exynos5_uart_rx_byte(void)
+{
+//	struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
+	struct s5p_uart *uart = (struct s5p_uart *)base_port;
+
+	/* wait for character to arrive */
+	while (!(readl(&uart->ufstat) & (RX_FIFO_COUNT_MASK |
+					 RX_FIFO_FULL_MASK))) {
+		if (exynos5_uart_err_check(0))
+			return 0;
+	}
+
+	return readb(&uart->urxh) & 0xff;
+}
+
+/*
+ * Output a single byte to the serial port.
+ */
+/* FIXME: ordering of arguments for coreboot v. u-boot for tx_byte */
+//static void exynos5_tx_byte(const char c, const int dev_index)
+static void exynos5_uart_tx_byte(unsigned char data)
+{
+//	struct s5p_uart *const uart = s5p_get_base_uart(dev_index);
+	struct s5p_uart *uart = (struct s5p_uart *)base_port;
+
+	/* wait for room in the tx FIFO */
+	while ((readl(uart->ufstat) & TX_FIFO_FULL_MASK)) {
+		if (exynos5_uart_err_check(1))
+			return;
+	}
+
+	writeb(data, &uart->utxh);
+}
+
+#ifndef __PRE_RAM__
+static const struct console_driver exynos5_uart_console __console = {
+//static const struct console_driver exynos5_uart_console __console = {
+#if 0
+	void (*init)(void);
+	void (*tx_byte)(unsigned char byte);
+	void (*tx_flush)(void);
+	unsigned char (*rx_byte)(void);
+	int (*tst_byte)(void);
+#endif
+	.init     = exynos5_init_dev,
+	.tx_byte  = exynos5_uart_tx_byte,
+//	.tx_flush = exynos5_uart_tx_flush,
+	.rx_byte  = exynos5_uart_rx_byte,
+//	.tst_byte = exynos5_uart_tst_byte,
+};
+#else
+/* for romstage_console... */
+//void (*uart_init)(void) = exynos5_init_dev;
+//unsigned char (*uart_rx_byte)(unsigned base_port) = exynos5_uart_rx_byte;
+//void (*uart_tx_byte)(unsigned base_port, unsigned char data) = exynos5_uart_tx_byte;
+/* FIXME: trivial wrappers */
+void uart_init()
+{
+	exynos5_init_dev();
+}
+
+unsigned char uart_rx_byte()
+{
+	return exynos5_uart_rx_byte();
+}
+
+void uart_tx_byte(unsigned char data)
+{
+	exynos5_uart_tx_byte(data);
+}
+#endif
diff --git a/src/cpu/samsung/s5p-common/Makefile.inc b/src/cpu/samsung/s5p-common/Makefile.inc
new file mode 100644
index 0000000..9747f0d
--- /dev/null
+++ b/src/cpu/samsung/s5p-common/Makefile.inc
@@ -0,0 +1,12 @@
+romstage-y += cpu_info.c
+romstage-y += pwm.c	# needed by timer.c
+romstage-y += s5p_gpio.c
+romstage-y += timer.c
+
+#romstage-y += sromc.c
+#romstage-y += wdt.c
+
+ramstage-y += cpu_info.c
+ramstage-y += pwm.c	# needed by timer.c
+ramstage-y += timer.c
+ramstage-y += s5p_gpio.c
diff --git a/src/cpu/samsung/s5p-common/Makefile.uboot b/src/cpu/samsung/s5p-common/Makefile.uboot
new file mode 100644
index 0000000..f975f3f
--- /dev/null
+++ b/src/cpu/samsung/s5p-common/Makefile.uboot
@@ -0,0 +1,49 @@
+#
+# Copyright (C) 2009 Samsung Electronics
+# Minkyu Kang <mk7.kang@samsung.com>
+#
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+
+include $(TOPDIR)/config.mk
+
+LIB	= $(obj)libs5p-common.o
+
+COBJS-y		+= cpu_info.o
+COBJS-y		+= timer.o
+COBJS-y		+= sromc.o
+COBJS-y		+= wdt.o
+COBJS-$(CONFIG_PWM)	+= pwm.o
+
+SRCS	:= $(SOBJS:.o=.S) $(COBJS:.o=.c)
+OBJS	:= $(addprefix $(obj),$(COBJS-y) $(SOBJS))
+
+all:	 $(obj).depend $(LIB)
+
+$(LIB):	$(OBJS)
+	$(call cmd_link_o_target, $(OBJS))
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/src/cpu/samsung/s5p-common/cpu_info.c b/src/cpu/samsung/s5p-common/cpu_info.c
new file mode 100644
index 0000000..6b4742e
--- /dev/null
+++ b/src/cpu/samsung/s5p-common/cpu_info.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2009 Samsung Electronics
+ * Minkyu Kang <mk7.kang@samsung.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+#include <common.h>
+#if 0
+#include <asm/io.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/dmc.h>
+#endif
+#include <arch/io.h>
+
+#include <cpu/samsung/s5p-common/clk.h>
+#include <cpu/samsung/s5p-common/clock.h>
+#include <cpu/samsung/s5p-common/cpu.h>
+
+#include <cpu/samsung/exynos5250/dmc.h>
+#include <cpu/samsung/exynos5-common/cpu.h>	/* for EXYNOS_PRO_ID */
+
+/* FIXME(dhendrix): consolidate samsung ID code/#defines to a common location */
+#include <cpu/samsung/exynos5250/setup.h>	/* cpu_info_init() prototype */
+
+/*
+ * The following CPU infos are initialized in lowlevel_init(). They should be
+ * put in the .data section. Otherwise, a compile will put them in the .bss
+ * section since they don't have initial values. The relocation code which
+ * runs after lowlevel_init() will reset them to zero.
+ */
+unsigned int s5p_cpu_id __attribute__((section(".data")));
+unsigned int s5p_cpu_rev __attribute__((section(".data")));
+
+void cpu_info_init(void)
+{
+	s5p_set_cpu_id();
+}
+
+int s5p_get_cpu_id(void)
+{
+	return s5p_cpu_id;
+}
+
+int s5p_get_cpu_rev(void)
+{
+	return s5p_cpu_rev;
+}
+
+void s5p_set_cpu_id(void)
+{
+	s5p_cpu_id = readl(EXYNOS_PRO_ID);
+	s5p_cpu_id = (0xC000 | ((s5p_cpu_id & 0x00FFF000) >> 12));
+
+	/*
+	 * 0xC200: EXYNOS4210 EVT0
+	 * 0xC210: EXYNOS4210 EVT1
+	 */
+	if (s5p_cpu_id == 0xC200) {
+		s5p_cpu_id |= 0x10;
+		s5p_cpu_rev = 0;
+	} else if (s5p_cpu_id == 0xC210) {
+		s5p_cpu_rev = 1;
+	}
+}
+
+#ifdef CONFIG_DISPLAY_CPUINFO
+int print_cpuinfo(void)
+{
+	char buf[32];
+
+	printf("CPU:   S5P%X @ %sMHz\n",
+			s5p_cpu_id, strmhz(buf, get_arm_clk()));
+
+	return 0;
+}
+#endif
+
+#ifndef CONFIG_SPL_BUILD
+void board_show_dram(ulong size)
+{
+	enum ddr_mode mem_type;
+	unsigned frequency_mhz;
+	unsigned arm_freq;
+	enum mem_manuf mem_manuf;
+	char buf[32];
+	int ret;
+
+	/* Get settings from the fdt */
+	ret = clock_get_mem_selection(&mem_type, &frequency_mhz,
+				       &arm_freq, &mem_manuf);
+	if (ret)
+		panic("Invalid DRAM information");
+
+	puts("DRAM:  ");
+	print_size(size, " ");
+	printf("%s %s @ %sMHz",
+	       clock_get_mem_manuf_name(mem_manuf),
+	       clock_get_mem_type_name(mem_type),
+	       strmhz(buf, frequency_mhz));
+	putc('\n');
+}
+#endif
+
+#ifdef CONFIG_ARCH_CPU_INIT
+int arch_cpu_init(void)
+{
+	cpu_info_init();
+
+	return 0;
+}
+#endif
diff --git a/src/cpu/samsung/s5p-common/pwm.c b/src/cpu/samsung/s5p-common/pwm.c
new file mode 100644
index 0000000..5300420
--- /dev/null
+++ b/src/cpu/samsung/s5p-common/pwm.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2011 Samsung Electronics
+ *
+ * Donghwa Lee <dh09.lee@samsung.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+//#include <pwm.h>
+#include <arch/io.h>
+//#include <arch/pwm.h>
+//#include <arch/clk.h>
+/* FIXME(dhendrix): this is a godawful mess of similar-but-different includes... */
+#include <cpu/samsung/exynos5-common/clk.h>
+#include <cpu/samsung/exynos5250/cpu.h>
+#include <cpu/samsung/exynos5250/periph.h>
+#include <cpu/samsung/s5p-common/pwm.h>
+#include <cpu/samsung/s5p-common/clk.h>
+//#include <arch/periph.h>
+
+int pwm_enable(int pwm_id)
+{
+	const struct s5p_timer *pwm =
+			(struct s5p_timer *)samsung_get_base_timer();
+	unsigned long tcon;
+
+	tcon = readl(&pwm->tcon);
+	tcon |= TCON_START(pwm_id);
+
+	writel(tcon, &pwm->tcon);
+
+	return 0;
+}
+
+int pwm_check_enabled(int pwm_id)
+{
+	const struct s5p_timer *pwm =
+			(struct s5p_timer *)samsung_get_base_timer();
+	const unsigned long tcon = readl(&pwm->tcon);
+
+	return tcon & TCON_START(pwm_id);
+}
+
+void pwm_disable(int pwm_id)
+{
+	const struct s5p_timer *pwm =
+			(struct s5p_timer *)samsung_get_base_timer();
+	unsigned long tcon;
+
+	tcon = readl(&pwm->tcon);
+	tcon &= ~TCON_START(pwm_id);
+
+	writel(tcon, &pwm->tcon);
+}
+
+static unsigned long pwm_calc_tin(int pwm_id, unsigned long freq)
+{
+	unsigned long tin_parent_rate;
+	unsigned int div;
+
+	tin_parent_rate = clock_get_periph_rate(PERIPH_ID_PWM0);
+
+	for (div = 2; div <= 16; div *= 2) {
+		if ((tin_parent_rate / (div << 16)) < freq)
+			return tin_parent_rate / div;
+	}
+
+	return tin_parent_rate / 16;
+}
+
+#define NS_IN_SEC 1000000000UL
+
+int pwm_config(int pwm_id, int duty_ns, int period_ns)
+{
+	const struct s5p_timer *pwm =
+			(struct s5p_timer *)samsung_get_base_timer();
+	unsigned int offset;
+	unsigned long tin_rate;
+	unsigned long tin_ns;
+	unsigned long frequency;
+	unsigned long tcon;
+	unsigned long tcnt;
+	unsigned long tcmp;
+
+	/*
+	 * We currently avoid using 64bit arithmetic by using the
+	 * fact that anything faster than 1GHz is easily representable
+	 * by 32bits.
+	 */
+	if (period_ns > NS_IN_SEC || duty_ns > NS_IN_SEC || period_ns == 0)
+		return -1;
+//		return -ERANGE;
+
+	if (duty_ns > period_ns)
+		return -1;
+//		return -EINVAL;
+
+	frequency = NS_IN_SEC / period_ns;
+
+	/* Check to see if we are changing the clock rate of the PWM */
+	tin_rate = pwm_calc_tin(pwm_id, frequency);
+
+	tin_ns = NS_IN_SEC / tin_rate;
+	tcnt = period_ns / tin_ns;
+
+	/* Note, counters count down */
+	tcmp = duty_ns / tin_ns;
+	tcmp = tcnt - tcmp;
+
+	/* Update the PWM register block. */
+	offset = pwm_id * 3;
+	if (pwm_id < 4) {
+		writel(tcnt, &pwm->tcntb0 + offset);
+		writel(tcmp, &pwm->tcmpb0 + offset);
+	}
+
+	tcon = readl(&pwm->tcon);
+	tcon |= TCON_UPDATE(pwm_id);
+	if (pwm_id < 4)
+		tcon |= TCON_AUTO_RELOAD(pwm_id);
+	else
+		tcon |= TCON4_AUTO_RELOAD;
+	writel(tcon, &pwm->tcon);
+
+	tcon &= ~TCON_UPDATE(pwm_id);
+	writel(tcon, &pwm->tcon);
+
+	return 0;
+}
+
+int pwm_init(int pwm_id, int div, int invert)
+{
+	u32 val;
+	const struct s5p_timer *pwm =
+			(struct s5p_timer *)samsung_get_base_timer();
+	unsigned long ticks_per_period;
+	unsigned int offset, prescaler;
+
+	/*
+	 * Timer Freq(HZ) =
+	 *	PWM_CLK / { (prescaler_value + 1) * (divider_value) }
+	 */
+
+	val = readl(&pwm->tcfg0);
+	if (pwm_id < 2) {
+		prescaler = PRESCALER_0;
+		val &= ~0xff;
+		val |= (prescaler & 0xff);
+	} else {
+		prescaler = PRESCALER_1;
+		val &= ~(0xff << 8);
+		val |= (prescaler & 0xff) << 8;
+	}
+	writel(val, &pwm->tcfg0);
+	val = readl(&pwm->tcfg1);
+	val &= ~(0xf << MUX_DIV_SHIFT(pwm_id));
+	val |= (div & 0xf) << MUX_DIV_SHIFT(pwm_id);
+	writel(val, &pwm->tcfg1);
+
+
+	if (pwm_id == 4) {
+		/*
+		 * TODO(sjg): Use this as a countdown timer for now. We count
+		 * down from the maximum value to 0, then reset.
+		 */
+		ticks_per_period = -1UL;
+	} else {
+		const unsigned long pwm_hz = 1000;
+		unsigned long timer_rate_hz = clock_get_periph_rate(
+			PERIPH_ID_PWM0) / ((prescaler + 1) * (1 << div));
+
+		ticks_per_period = timer_rate_hz / pwm_hz;
+	}
+
+	/* set count value */
+	offset = pwm_id * 3;
+
+	writel(ticks_per_period, &pwm->tcntb0 + offset);
+
+	val = readl(&pwm->tcon) & ~(0xf << TCON_OFFSET(pwm_id));
+	if (invert && (pwm_id < 4))
+		val |= TCON_INVERTER(pwm_id);
+	writel(val, &pwm->tcon);
+
+	pwm_enable(pwm_id);
+
+	return 0;
+}
diff --git a/src/cpu/samsung/s5p-common/s5p_gpio.c b/src/cpu/samsung/s5p-common/s5p_gpio.c
new file mode 100644
index 0000000..50c4519
--- /dev/null
+++ b/src/cpu/samsung/s5p-common/s5p_gpio.c
@@ -0,0 +1,490 @@
+/*
+ * (C) Copyright 2009 Samsung Electronics
+ * Minkyu Kang <mk7.kang@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+/* FIXME(dhendrix): fix this up so it doesn't require a bunch of #ifdefs... */
+#include <common.h>
+//#include <arch/io.h>
+#include <arch/gpio.h>
+#include <console/console.h>
+#include <cpu/samsung/s5p-common/gpio.h>
+#include <cpu/samsung/exynos5-common/gpio.h>
+#include <cpu/samsung/exynos5250/gpio.h>	/* FIXME: for gpio_decode_number prototype */
+
+#define CON_MASK(x)		(0xf << ((x) << 2))
+#define CON_SFR(x, v)		((v) << ((x) << 2))
+
+#define DAT_MASK(x)		(0x1 << (x))
+#define DAT_SET(x)		(0x1 << (x))
+
+#define PULL_MASK(x)		(0x3 << ((x) << 1))
+#define PULL_MODE(x, v)		((v) << ((x) << 1))
+
+#define DRV_MASK(x)		(0x3 << ((x) << 1))
+#define DRV_SET(x, m)		((m) << ((x) << 1))
+#define RATE_MASK(x)		(0x1 << (x + 16))
+#define RATE_SET(x)		(0x1 << (x + 16))
+
+struct gpio_info {
+	unsigned int reg_addr;	/* Address of register for this part */
+	unsigned int max_gpio;	/* Maximum GPIO in this part */
+};
+
+#ifdef CONFIG_CPU_SAMSUNG_EXYNOS5
+
+#include <cpu/samsung/exynos5250/cpu.h>
+static const struct gpio_info gpio_data[EXYNOS_GPIO_NUM_PARTS] = {
+	{ EXYNOS5_GPIO_PART1_BASE, GPIO_MAX_PORT_PART_1 },
+	{ EXYNOS5_GPIO_PART2_BASE, GPIO_MAX_PORT_PART_2 },
+	{ EXYNOS5_GPIO_PART3_BASE, GPIO_MAX_PORT_PART_3 },
+	{ EXYNOS5_GPIO_PART4_BASE, GPIO_MAX_PORT_PART_4 },
+	{ EXYNOS5_GPIO_PART5_BASE, GPIO_MAX_PORT_PART_5 },
+	{ EXYNOS5_GPIO_PART6_BASE, GPIO_MAX_PORT },
+};
+
+#define HAVE_GENERIC_GPIO
+
+#elif defined(CONFIG_CPU_SAMSUNG_EXYNOS4)
+
+static const struct gpio_info gpio_data[EXYNOS_GPIO_NUM_PARTS] = {
+	{ EXYNOS4_GPIO_PART1_BASE, GPIO_MAX_PORT_PART_1 },
+	{ EXYNOS4_GPIO_PART2_BASE, GPIO_MAX_PORT_PART_2 },
+	{ EXYNOS4_GPIO_PART3_BASE, GPIO_MAX_PORT_PART_3 },
+};
+
+#define HAVE_GENERIC_GPIO
+
+#endif
+
+/* This macro gets gpio pin offset from 0..7 */
+#define GPIO_BIT(x)     ((x) & 0x7)
+
+//#ifdef HAVE_GENERIC_GPIO
+static struct s5p_gpio_bank *gpio_get_bank(unsigned int gpio)
+{
+	const struct gpio_info *data;
+	unsigned int upto;
+	int i;
+
+	for (i = upto = 0, data = gpio_data; i < EXYNOS_GPIO_NUM_PARTS;
+			i++, upto = data->max_gpio, data++) {
+		if (gpio < data->max_gpio) {
+			struct s5p_gpio_bank *bank;
+
+			bank = (struct s5p_gpio_bank *)data->reg_addr;
+			bank += (gpio - upto) / GPIO_PER_BANK;
+			return bank;
+		}
+	}
+
+#ifndef CONFIG_SPL_BUILD
+	assert(gpio < GPIO_MAX_PORT);	/* ...which it will not be */
+#endif
+	return NULL;
+}
+//#endif
+
+/* TODO: Deprecation this interface in favour of asm-generic/gpio.h */
+void s5p_gpio_cfg_pin(struct s5p_gpio_bank *bank, int gpio, int cfg)
+{
+	unsigned int value;
+
+	value = readl(&bank->con);
+	value &= ~CON_MASK(gpio);
+	value |= CON_SFR(gpio, cfg);
+	writel(value, &bank->con);
+}
+
+void s5p_gpio_direction_output(struct s5p_gpio_bank *bank, int gpio, int en)
+{
+	unsigned int value;
+
+	s5p_gpio_cfg_pin(bank, gpio, EXYNOS_GPIO_OUTPUT);
+
+	value = readl(&bank->dat);
+	value &= ~DAT_MASK(gpio);
+	if (en)
+		value |= DAT_SET(gpio);
+	writel(value, &bank->dat);
+}
+
+void s5p_gpio_direction_input(struct s5p_gpio_bank *bank, int gpio)
+{
+	s5p_gpio_cfg_pin(bank, gpio, EXYNOS_GPIO_INPUT);
+}
+
+void s5p_gpio_set_value(struct s5p_gpio_bank *bank, int gpio, int en)
+{
+	unsigned int value;
+
+	value = readl(&bank->dat);
+	value &= ~DAT_MASK(gpio);
+	if (en)
+		value |= DAT_SET(gpio);
+	writel(value, &bank->dat);
+}
+
+unsigned int s5p_gpio_get_value(struct s5p_gpio_bank *bank, int gpio)
+{
+	unsigned int value;
+
+	value = readl(&bank->dat);
+	return !!(value & DAT_MASK(gpio));
+}
+
+void s5p_gpio_set_pull(struct s5p_gpio_bank *bank, int gpio, int mode)
+{
+	unsigned int value;
+
+	value = readl(&bank->pull);
+	value &= ~PULL_MASK(gpio);
+
+	switch (mode) {
+	case EXYNOS_GPIO_PULL_DOWN:
+	case EXYNOS_GPIO_PULL_UP:
+		value |= PULL_MODE(gpio, mode);
+		break;
+	default:
+		break;
+	}
+
+	writel(value, &bank->pull);
+}
+
+void s5p_gpio_set_drv(struct s5p_gpio_bank *bank, int gpio, int mode)
+{
+	unsigned int value;
+
+	value = readl(&bank->drv);
+	value &= ~DRV_MASK(gpio);
+
+	switch (mode) {
+	case EXYNOS_GPIO_DRV_1X:
+	case EXYNOS_GPIO_DRV_2X:
+	case EXYNOS_GPIO_DRV_3X:
+	case EXYNOS_GPIO_DRV_4X:
+		value |= DRV_SET(gpio, mode);
+		break;
+	default:
+		return;
+	}
+
+	writel(value, &bank->drv);
+}
+
+void s5p_gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode)
+{
+	unsigned int value;
+
+	value = readl(&bank->drv);
+	value &= ~RATE_MASK(gpio);
+
+	switch (mode) {
+	case EXYNOS_GPIO_DRV_FAST:
+	case EXYNOS_GPIO_DRV_SLOW:
+		value |= RATE_SET(gpio);
+		break;
+	default:
+		return;
+	}
+
+	writel(value, &bank->drv);
+}
+
+/* Common GPIO API - only available on Exynos5 */
+/* FIXME(dhendrix): If this stuff is really only applicable to exynos5,
+   move it to a more sensible location. */
+#ifdef HAVE_GENERIC_GPIO
+
+void gpio_cfg_pin(int gpio, int cfg)
+{
+	unsigned int value;
+	struct s5p_gpio_bank *bank = gpio_get_bank(gpio);
+
+	value = readl(&bank->con);
+	value &= ~CON_MASK(GPIO_BIT(gpio));
+	value |= CON_SFR(GPIO_BIT(gpio), cfg);
+	writel(value, &bank->con);
+}
+
+static int gpio_get_cfg(int gpio)
+{
+	struct s5p_gpio_bank *bank = gpio_get_bank(gpio);
+	int shift = GPIO_BIT(gpio) << 2;
+
+	return (readl(&bank->con) & CON_MASK(GPIO_BIT(gpio))) >> shift;
+}
+
+void gpio_set_pull(int gpio, int mode)
+{
+	unsigned int value;
+	struct s5p_gpio_bank *bank = gpio_get_bank(gpio);
+
+	value = readl(&bank->pull);
+	value &= ~PULL_MASK(GPIO_BIT(gpio));
+
+	switch (mode) {
+	case EXYNOS_GPIO_PULL_DOWN:
+	case EXYNOS_GPIO_PULL_UP:
+		value |= PULL_MODE(GPIO_BIT(gpio), mode);
+		break;
+	default:
+		break;
+	}
+
+	writel(value, &bank->pull);
+}
+
+void gpio_set_drv(int gpio, int mode)
+{
+	unsigned int value;
+	struct s5p_gpio_bank *bank = gpio_get_bank(gpio);
+
+	value = readl(&bank->drv);
+	value &= ~DRV_MASK(GPIO_BIT(gpio));
+
+	switch (mode) {
+	case EXYNOS_GPIO_DRV_1X:
+	case EXYNOS_GPIO_DRV_2X:
+	case EXYNOS_GPIO_DRV_3X:
+	case EXYNOS_GPIO_DRV_4X:
+		value |= DRV_SET(GPIO_BIT(gpio), mode);
+		break;
+	default:
+		return;
+	}
+
+	writel(value, &bank->drv);
+}
+
+void gpio_set_rate(int gpio, int mode)
+{
+	unsigned int value;
+	struct s5p_gpio_bank *bank = gpio_get_bank(gpio);
+
+	value = readl(&bank->drv);
+	value &= ~RATE_MASK(GPIO_BIT(gpio));
+
+	switch (mode) {
+	case EXYNOS_GPIO_DRV_FAST:
+	case EXYNOS_GPIO_DRV_SLOW:
+		value |= RATE_SET(GPIO_BIT(gpio));
+		break;
+	default:
+		return;
+	}
+
+	writel(value, &bank->drv);
+}
+
+int gpio_request(unsigned gpio, const char *label)
+{
+	return 0;
+}
+
+int gpio_free(unsigned gpio)
+{
+	return 0;
+}
+
+int gpio_direction_input(unsigned gpio)
+{
+	gpio_cfg_pin(gpio, EXYNOS_GPIO_INPUT);
+
+	return 0;
+}
+
+int gpio_direction_output(unsigned gpio, int value)
+{
+	unsigned int val;
+	struct s5p_gpio_bank *bank = gpio_get_bank(gpio);
+
+	gpio_cfg_pin(gpio, EXYNOS_GPIO_OUTPUT);
+
+	val = readl(&bank->dat);
+	val &= ~DAT_MASK(GPIO_BIT(gpio));
+	if (value)
+		val |= DAT_SET(GPIO_BIT(gpio));
+	writel(val, &bank->dat);
+
+	return 0;
+}
+
+int gpio_get_value(unsigned gpio)
+{
+	unsigned int value;
+	struct s5p_gpio_bank *bank = gpio_get_bank(gpio);
+
+	value = readl(&bank->dat);
+	return !!(value & DAT_MASK(GPIO_BIT(gpio)));
+}
+
+int gpio_set_value(unsigned gpio, int value)
+{
+	unsigned int val;
+	struct s5p_gpio_bank *bank = gpio_get_bank(gpio);
+
+	val = readl(&bank->dat);
+	val &= ~DAT_MASK(GPIO_BIT(gpio));
+	if (value)
+		val |= DAT_SET(GPIO_BIT(gpio));
+	writel(val, &bank->dat);
+
+	return 0;
+}
+#else
+
+static int s5p_gpio_get_pin(unsigned gpio)
+{
+	return gpio % GPIO_PER_BANK;
+}
+
+/*
+ * If we have the old-style GPIO numbering setup, use these functions
+ * which don't necessary provide sequentially increasing GPIO numbers.
+ */
+static struct s5p_gpio_bank *s5p_gpio_get_bank(unsigned gpio)
+{
+	int bank = gpio / GPIO_PER_BANK;
+	bank *= sizeof(struct s5p_gpio_bank);
+
+	return (struct s5p_gpio_bank *) (s5p_gpio_base(gpio) + bank);
+}
+
+int gpio_request(unsigned gpio, const char *label)
+{
+	return 0;
+}
+
+int gpio_free(unsigned gpio)
+{
+	return 0;
+}
+
+int gpio_direction_input(unsigned gpio)
+{
+	s5p_gpio_direction_input(s5p_gpio_get_bank(gpio),
+				s5p_gpio_get_pin(gpio));
+	return 0;
+}
+
+int gpio_direction_output(unsigned gpio, int value)
+{
+	s5p_gpio_direction_output(s5p_gpio_get_bank(gpio),
+				 s5p_gpio_get_pin(gpio), value);
+	return 0;
+}
+
+int gpio_get_value(unsigned gpio)
+{
+	return (int) s5p_gpio_get_value(s5p_gpio_get_bank(gpio),
+				       s5p_gpio_get_pin(gpio));
+}
+
+int gpio_set_value(unsigned gpio, int value)
+{
+	s5p_gpio_set_value(s5p_gpio_get_bank(gpio),
+			  s5p_gpio_get_pin(gpio), value);
+
+	return 0;
+}
+
+#endif /* HAVE_GENERIC_GPIO */
+
+/*
+ * Add a delay here to give the lines time to settle
+ * TODO(sjg): 1us does not always work, 2 is stable, so use 5 to be safe
+ * Come back to this and sort out what the datasheet says
+ */
+#define GPIO_DELAY_US 5
+
+/* FIXME(dhendrix): this should probably go to a more generic location */
+int gpio_decode_number(unsigned gpio_list[], int count)
+{
+	int result = 0;
+	int multiplier = 1;
+	int value, high, low;
+	int gpio, i;
+
+	for (i = 0; i < count; i++) {
+		gpio = gpio_list[i];
+		if (gpio >= GPIO_MAX_PORT)
+			return -1;
+		gpio_direction_input(gpio);
+		gpio_set_pull(gpio, EXYNOS_GPIO_PULL_UP);
+		udelay(GPIO_DELAY_US);
+		high = gpio_get_value(gpio);
+		gpio_set_pull(gpio, EXYNOS_GPIO_PULL_DOWN);
+		udelay(GPIO_DELAY_US);
+		low = gpio_get_value(gpio);
+
+		if (high && low) /* external pullup */
+			value = 2;
+		else if (!high && !low) /* external pulldown */
+			value = 1;
+		else /* floating */
+			value = 0;
+
+		/*
+		 * Check if line is externally pulled high and
+		 * configure the internal pullup to match.  For
+		 * floating and pulldowns, the GPIO is already
+		 * configured with an internal pulldown from the
+		 * above test.
+		 */
+		if (value == 2)
+			gpio_set_pull(gpio, EXYNOS_GPIO_PULL_UP);
+
+		result += value * multiplier;
+		multiplier *= 3;
+	}
+
+	return result;
+}
+
+static const char *get_cfg_name(int cfg)
+{
+	static char name[8];
+
+	if (cfg == EXYNOS_GPIO_INPUT)
+		return "input";
+	else if (cfg == EXYNOS_GPIO_OUTPUT)
+		return "output";
+	printk(BIOS_INFO, "func %d", cfg);
+//	sprintf(name, "func %d", cfg);
+
+	return name;
+}
+
+/*
+ * Display Exynos GPIO information
+ */
+void gpio_info(void)
+{
+	unsigned gpio;
+
+	for (gpio = 0; gpio < GPIO_MAX_PORT; gpio++) {
+		int cfg = gpio_get_cfg(gpio);
+
+		printk(BIOS_INFO, "GPIO_%-3d: %s", gpio, get_cfg_name(cfg));
+		if (cfg == EXYNOS_GPIO_INPUT || cfg == EXYNOS_GPIO_OUTPUT)
+			printk(BIOS_INFO, ", value = %d", gpio_get_value(gpio));
+		printk(BIOS_INFO, "\n");
+	}
+}
diff --git a/src/cpu/samsung/s5p-common/sromc.c b/src/cpu/samsung/s5p-common/sromc.c
new file mode 100644
index 0000000..091e8d1
--- /dev/null
+++ b/src/cpu/samsung/s5p-common/sromc.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010 Samsung Electronics
+ * Naveen Krishna Ch <ch.naveen@samsung.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch/sromc.h>
+
+/*
+ * s5p_config_sromc() - select the proper SROMC Bank and configure the
+ * band width control and bank control registers
+ * srom_bank	- SROM
+ * srom_bw_conf  - SMC Band witdh reg configuration value
+ * srom_bc_conf  - SMC Bank Control reg configuration value
+ */
+void s5p_config_sromc(u32 srom_bank, u32 srom_bw_conf, u32 srom_bc_conf)
+{
+	u32 tmp;
+	struct s5p_sromc *srom =
+		(struct s5p_sromc *)samsung_get_base_sromc();
+
+	/* Configure SMC_BW register to handle proper SROMC bank */
+	tmp = srom->bw;
+	tmp &= ~(0xF << (srom_bank * 4));
+	tmp |= srom_bw_conf;
+	srom->bw = tmp;
+
+	/* Configure SMC_BC register */
+	srom->bc[srom_bank] = srom_bc_conf;
+}
diff --git a/src/cpu/samsung/s5p-common/timer.c b/src/cpu/samsung/s5p-common/timer.c
new file mode 100644
index 0000000..373c374
--- /dev/null
+++ b/src/cpu/samsung/s5p-common/timer.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2009 Samsung Electronics
+ * Heungjun Kim <riverful.kim@samsung.com>
+ * Inki Dae <inki.dae@samsung.com>
+ * Minkyu Kang <mk7.kang@samsung.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <arch/io.h>
+//#include <cpu/samsung/exynos5250/pwm.h>
+//#include <cpu/samsung/exynos5250/clk.h>
+#include <cpu/samsung/s5p-common/pwm.h>
+#include <cpu/samsung/s5p-common/clk.h>
+#include <cpu/samsung/exynos5250/cpu.h>
+#include <cpu/samsung/exynos5-common/exynos5-common.h>
+
+//#include <pwm.h>
+
+//DECLARE_GLOBAL_DATA_PTR;
+static unsigned long long timer_reset_value;
+static unsigned long lastinc;
+
+/* macro to read the 16 bit timer */
+static inline struct s5p_timer *s5p_get_base_timer(void)
+{
+	return (struct s5p_timer *)samsung_get_base_timer();
+}
+
+/**
+ * Read the countdown timer.
+ *
+ * This operates at 1MHz and counts downwards. It will wrap about every
+ * hour (2^32 microseconds).
+ *
+ * @return current value of timer
+ */
+static unsigned long timer_get_us_down(void)
+{
+	struct s5p_timer *const timer = s5p_get_base_timer();
+
+	return readl(&timer->tcnto4);
+}
+
+int init_timer(void)
+{
+	/* Timer may have been enabled in SPL */
+	if (!pwm_check_enabled(4)) {
+		/* PWM Timer 4 */
+		pwm_init(4, MUX_DIV_4, 0);
+		pwm_config(4, 100000, 100000);
+		pwm_enable(4);
+#ifndef CONFIG_SPL_BUILD
+		/* Use this as the current monotonic time in us */
+		//gd->timer_reset_value = 0;
+		timer_reset_value = 0;
+
+		/* Use this as the last timer value we saw */
+		//gd->lastinc = timer_get_us_down();
+		lastinc = timer_get_us_down();
+#endif
+	}
+
+	return 0;
+}
+
+/*
+ * timer without interrupts
+ */
+unsigned long get_timer(unsigned long base)
+{
+	ulong now = timer_get_us_down();
+
+	/*
+	 * Increment the time by the amount elapsed since the last read.
+	 * The timer may have wrapped around, but it makes no difference to
+	 * our arithmetic here.
+	 */
+#if 0
+	gd->timer_reset_value += gd->lastinc - now;
+	gd->lastinc = now;
+
+	/* Divide by 1000 to convert from us to ms */
+	return gd->timer_reset_value / 1000 - base;
+#endif
+	timer_reset_value += lastinc - now;
+	lastinc = now;
+
+	/* Divide by 1000 to convert from us to ms */
+	return timer_reset_value / 1000 - base;
+}
+
+unsigned long timer_get_us(void)
+{
+	struct s5p_timer *const timer = s5p_get_base_timer();
+	unsigned long now_downward_us = readl(&timer->tcnto4);
+
+	/*
+	 * Note that this timer counts downward. The pre-SPL process (BL1)
+	 * takes about 100ms, so add this in here.
+	 */
+	return CONFIG_SPL_TIME_US - now_downward_us;
+}
+
+/* delay x useconds */
+void __udelay(unsigned long usec)
+{
+	unsigned long count_value;
+
+	count_value = timer_get_us_down();
+	while ((int)(count_value - timer_get_us_down()) < (int)usec)
+		;
+}
+
+/*
+ * This function is derived from PowerPC code (read timebase as long long).
+ * On ARM it just returns the timer value.
+ */
+unsigned long long get_ticks(void)
+{
+	return get_timer(0);
+}
+
+/*
+ * This function is derived from PowerPC code (timebase clock frequency).
+ * On ARM it returns the number of timer ticks per second.
+ */
+unsigned long get_tbclk(void)
+{
+	return CONFIG_SYS_HZ;
+}
+
+unsigned long timer_get_boot_us(void)
+{
+	return timer_get_us();
+}
diff --git a/src/cpu/samsung/s5p-common/wdt.c b/src/cpu/samsung/s5p-common/wdt.c
new file mode 100644
index 0000000..94acc1e
--- /dev/null
+++ b/src/cpu/samsung/s5p-common/wdt.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics
+ * Minkyu Kang <mk7.kang@samsung.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch/watchdog.h>
+
+#define PRESCALER_VAL 255
+
+void wdt_stop(void)
+{
+	struct s5p_watchdog *wdt =
+		(struct s5p_watchdog *)samsung_get_base_watchdog();
+	unsigned int wtcon;
+
+	wtcon = readl(&wdt->wtcon);
+	wtcon &= ~(WTCON_EN | WTCON_INT | WTCON_RESET);
+
+	writel(wtcon, &wdt->wtcon);
+}
+
+void wdt_start(unsigned int timeout)
+{
+	struct s5p_watchdog *wdt =
+		(struct s5p_watchdog *)samsung_get_base_watchdog();
+	unsigned int wtcon;
+
+	wdt_stop();
+
+	wtcon = readl(&wdt->wtcon);
+	wtcon |= (WTCON_EN | WTCON_CLK(WTCON_CLK_128));
+	wtcon &= ~WTCON_INT;
+	wtcon |= WTCON_RESET;
+	wtcon |= WTCON_PRESCALER(PRESCALER_VAL);
+
+	writel(timeout, &wdt->wtdat);
+	writel(timeout, &wdt->wtcnt);
+	writel(wtcon, &wdt->wtcon);
+}