summaryrefslogtreecommitdiff
path: root/src/cpu/samsung/s5p-common
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/samsung/s5p-common
parent747127d50545c1fbd0dcc10baacc742d3151ddfe (diff)
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/samsung/s5p-common')
-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
8 files changed, 1145 insertions, 0 deletions
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);
+}