summaryrefslogtreecommitdiff
path: root/src/cpu
diff options
context:
space:
mode:
authorStefan Reinauer <stefan.reinauer@coreboot.org>2012-12-07 17:18:43 -0800
committerRonald G. Minnich <rminnich@gmail.com>2012-12-08 06:48:03 +0100
commit9fe20cb3814df88f181648860102a9da249a4da1 (patch)
tree3623451d939c151754dcec0e4f87ec27b5a44614 /src/cpu
parent747127d50545c1fbd0dcc10baacc742d3151ddfe (diff)
downloadcoreboot-9fe20cb3814df88f181648860102a9da249a4da1.tar.xz
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)
Diffstat (limited to 'src/cpu')
-rw-r--r--src/cpu/Kconfig9
-rw-r--r--src/cpu/Makefile.inc1
-rw-r--r--src/cpu/samsung/Kconfig46
-rw-r--r--src/cpu/samsung/Makefile.inc5
-rw-r--r--src/cpu/samsung/exynos5-common/Makefile.inc2
-rw-r--r--src/cpu/samsung/exynos5-common/soc.c30
-rw-r--r--src/cpu/samsung/exynos5-common/spl_boot.c460
-rw-r--r--src/cpu/samsung/exynos5250/Kconfig28
-rw-r--r--src/cpu/samsung/exynos5250/Makefile.inc32
-rw-r--r--src/cpu/samsung/exynos5250/ace_sha.c118
-rw-r--r--src/cpu/samsung/exynos5250/clock.c610
-rw-r--r--src/cpu/samsung/exynos5250/clock_init.c1189
-rw-r--r--src/cpu/samsung/exynos5250/dmc_common.c200
-rw-r--r--src/cpu/samsung/exynos5250/dmc_init_ddr3.c249
-rw-r--r--src/cpu/samsung/exynos5250/exynos_cache.c90
-rw-r--r--src/cpu/samsung/exynos5250/lowlevel_init.S32
-rw-r--r--src/cpu/samsung/exynos5250/lowlevel_init_c.c120
-rw-r--r--src/cpu/samsung/exynos5250/pinmux.c303
-rw-r--r--src/cpu/samsung/exynos5250/power.c202
-rw-r--r--src/cpu/samsung/exynos5250/sata.c431
-rw-r--r--src/cpu/samsung/exynos5250/setup.h7
-rw-r--r--src/cpu/samsung/exynos5250/soc.c47
-rw-r--r--src/cpu/samsung/exynos5250/spl.c41
-rw-r--r--src/cpu/samsung/exynos5250/tzpc_init.c57
-rw-r--r--src/cpu/samsung/exynos5250/uart.c235
-rw-r--r--src/cpu/samsung/s5p-common/Makefile.inc12
-rw-r--r--src/cpu/samsung/s5p-common/Makefile.uboot49
-rw-r--r--src/cpu/samsung/s5p-common/cpu_info.c128
-rw-r--r--src/cpu/samsung/s5p-common/pwm.c205
-rw-r--r--src/cpu/samsung/s5p-common/s5p_gpio.c490
-rw-r--r--src/cpu/samsung/s5p-common/sromc.c49
-rw-r--r--src/cpu/samsung/s5p-common/timer.c153
-rw-r--r--src/cpu/samsung/s5p-common/wdt.c59
33 files changed, 5688 insertions, 1 deletions
diff --git a/src/cpu/Kconfig b/src/cpu/Kconfig
index 986c68afc4..383ba796a2 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 938a8dfef1..93b16ae3bd 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 0000000000..3a14ab86e8
--- /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 0000000000..b6fcd4e9c0
--- /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 0000000000..cad6cb3de7
--- /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 0000000000..e73fd338ba
--- /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 0000000000..8ae051171e
--- /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 0000000000..8d4ba2dd0a
--- /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 0000000000..556631a25a
--- /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 0000000000..2715a03b2d
--- /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 0000000000..0250d77a28
--- /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 0000000000..fd2a90f815
--- /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 0000000000..35a13acc58
--- /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 0000000000..6ef10bfc91
--- /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 0000000000..ec858d1bc3
--- /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 0000000000..883db9dd7c
--- /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 0000000000..2215fb7a95
--- /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 0000000000..becced24d5
--- /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 0000000000..2000feebca
--- /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 0000000000..b3e464ea31
--- /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 5e5923202a..bb438c5fd6 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 @@ enum {
};
/* 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 0000000000..be28d8c38d
--- /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 0000000000..00b584a1d3
--- /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 0000000000..370c2ed9c2
--- /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 0000000000..3d43976b6d
--- /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 0000000000..9747f0d1ab
--- /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 0000000000..f975f3f06c
--- /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 0000000000..6b4742ec21
--- /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 0000000000..53004204c6
--- /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 0000000000..50c451974c
--- /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 0000000000..091e8d18ab
--- /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 0000000000..373c3741d2
--- /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 0000000000..94acc1e4ba
--- /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);
+}