ARM: Add support for EP93xx SoCs

Add support for the Cirrus EP93xx platform

Signed-off-by: Matthias Kaehlcke <matthias@kaehlcke.net>
Acked-by: Tom <Tom.Rix@windriver.com>
diff --git a/cpu/arm920t/ep93xx/Makefile b/cpu/arm920t/ep93xx/Makefile
new file mode 100644
index 0000000..30e12af
--- /dev/null
+++ b/cpu/arm920t/ep93xx/Makefile
@@ -0,0 +1,56 @@
+#
+# Cirrus Logic EP93xx CPU-specific Makefile
+#
+# Copyright (C) 2009 Matthias Kaehlcke <matthias@kaehlcke.net>
+#
+# Copyright (C) 2004, 2005
+# Cory T. Tusar, Videon Central, Inc., <ctusar@videon-central.com>
+#
+# Copyright (C) 2006
+# Dominic Rath <Dominic.Rath@gmx.de>
+#
+# Based on an original Makefile, which is
+#
+# (C) Copyright 2000, 2001, 2002
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+#
+# 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.,
+# 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+include $(TOPDIR)/config.mk
+
+LIB = $(obj)lib$(SOC).a
+
+COBJS   = cpu.o led.o speed.o timer.o
+SOBJS   = lowlevel_init.o
+
+SRCS    := $(SOBJS:.o=.S) $(COBJS:.o=.c)
+OBJS    := $(addprefix $(obj),$(SOBJS) $(COBJS))
+
+all:    $(obj).depend $(LIB)
+
+$(LIB): $(OBJS)
+	$(AR) $(ARFLAGS) $@ $(OBJS)
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
+
diff --git a/cpu/arm920t/ep93xx/cpu.c b/cpu/arm920t/ep93xx/cpu.c
new file mode 100644
index 0000000..1abb9c6
--- /dev/null
+++ b/cpu/arm920t/ep93xx/cpu.c
@@ -0,0 +1,51 @@
+/*
+ * Cirrus Logic EP93xx CPU-specific support.
+ *
+ * Copyright (C) 2009 Matthias Kaehlcke <matthias@kaehlcke.net>
+ *
+ * Copyright (C) 2004, 2005
+ * Cory T. Tusar, Videon Central, Inc., <ctusar@videon-central.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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <common.h>
+#include <asm/arch/ep93xx.h>
+#include <asm/io.h>
+
+/* We reset the CPU by generating a 1-->0 transition on DeviceCfg bit 31. */
+extern void reset_cpu(ulong addr)
+{
+	struct syscon_regs *syscon = (struct syscon_regs *)SYSCON_BASE;
+	uint32_t value;
+
+	/* Unlock DeviceCfg and set SWRST */
+	writel(0xAA, &syscon->sysswlock);
+	value = readl(&syscon->devicecfg);
+	value |= SYSCON_DEVICECFG_SWRST;
+	writel(value, &syscon->devicecfg);
+
+	/* Unlock DeviceCfg and clear SWRST */
+	writel(0xAA, &syscon->sysswlock);
+	value = readl(&syscon->devicecfg);
+	value &= ~SYSCON_DEVICECFG_SWRST;
+	writel(value, &syscon->devicecfg);
+
+	/* Dying... */
+	while (1)
+		; /* noop */
+}
diff --git a/cpu/arm920t/ep93xx/led.c b/cpu/arm920t/ep93xx/led.c
new file mode 100644
index 0000000..7e2c897
--- /dev/null
+++ b/cpu/arm920t/ep93xx/led.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2010, 2009 Matthias Kaehlcke <matthias@kaehlcke.net>
+ *
+ * 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 <asm/io.h>
+#include <asm/arch/ep93xx.h>
+#include <config.h>
+#include <status_led.h>
+
+static uint8_t saved_state[2] = {STATUS_LED_OFF, STATUS_LED_OFF};
+static uint32_t gpio_pin[2] = {1 << STATUS_LED_GREEN,
+			       1 << STATUS_LED_RED};
+
+inline void switch_LED_on(uint8_t led)
+{
+	register struct gpio_regs *gpio = (struct gpio_regs *)GPIO_BASE;
+
+	writel(readl(&gpio->pedr) | gpio_pin[led], &gpio->pedr);
+	saved_state[led] = STATUS_LED_ON;
+}
+
+inline void switch_LED_off(uint8_t led)
+{
+	register struct gpio_regs *gpio = (struct gpio_regs *)GPIO_BASE;
+
+	writel(readl(&gpio->pedr) & ~gpio_pin[led], &gpio->pedr);
+	saved_state[led] = STATUS_LED_OFF;
+}
+
+void red_LED_on(void)
+{
+	switch_LED_on(STATUS_LED_RED);
+}
+
+void red_LED_off(void)
+{
+	switch_LED_off(STATUS_LED_RED);
+}
+
+void green_LED_on(void)
+{
+	switch_LED_on(STATUS_LED_GREEN);
+}
+
+void green_LED_off(void)
+{
+	switch_LED_off(STATUS_LED_GREEN);
+}
+
+void __led_init(led_id_t mask, int state)
+{
+	__led_set(mask, state);
+}
+
+void __led_toggle(led_id_t mask)
+{
+	if (STATUS_LED_RED == mask) {
+		if (STATUS_LED_ON == saved_state[STATUS_LED_RED])
+			red_LED_off();
+		else
+			red_LED_on();
+	} else if (STATUS_LED_GREEN == mask) {
+		if (STATUS_LED_ON == saved_state[STATUS_LED_GREEN])
+			green_LED_off();
+		else
+			green_LED_on();
+	}
+}
+
+void __led_set(led_id_t mask, int state)
+{
+	if (STATUS_LED_RED == mask) {
+		if (STATUS_LED_ON == state)
+			red_LED_on();
+		else
+			red_LED_off();
+	} else if (STATUS_LED_GREEN == mask) {
+		if (STATUS_LED_ON == state)
+			green_LED_on();
+		else
+			green_LED_off();
+	}
+}
diff --git a/cpu/arm920t/ep93xx/lowlevel_init.S b/cpu/arm920t/ep93xx/lowlevel_init.S
new file mode 100644
index 0000000..a20ec89
--- /dev/null
+++ b/cpu/arm920t/ep93xx/lowlevel_init.S
@@ -0,0 +1,65 @@
+/*
+ * Low-level initialization for EP93xx
+ *
+ * Copyright (C) 2009 Matthias Kaehlcke <matthias@kaehlcke.net>
+ *
+ * Copyright (C) 2006 Dominic Rath <Dominic.Rath@gmx.de>
+ *
+ * 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 <version.h>
+#include <asm/arch/ep93xx.h>
+
+.globl lowlevel_init
+lowlevel_init:
+	/* backup return address */
+	ldr r1, =SYSCON_SCRATCH0
+	str lr, [r1]
+
+	/* Turn on both LEDs */
+	bl red_LED_on
+	bl green_LED_on
+
+	/* Configure flash wait states before we switch to the PLL */
+	bl flash_cfg
+
+	/* Set up PLL */
+	bl pll_cfg
+
+	/* Turn off the Green LED and leave the Red LED on */
+	bl green_LED_off
+
+	/* Setup SDRAM */
+	bl sdram_cfg
+
+	/* Turn on Green LED, Turn off the Red LED */
+	bl green_LED_on
+	bl red_LED_off
+
+	/* FIXME: we use async mode for now */
+	mrc p15, 0, r0, c1, c0, 0
+	orr r0, r0, #0xc0000000
+	mcr p15, 0, r0, c1, c0, 0
+
+	/* restore return address */
+	ldr r1, =SYSCON_SCRATCH0
+	ldr lr, [r1]
+
+	mov pc, lr
diff --git a/cpu/arm920t/ep93xx/speed.c b/cpu/arm920t/ep93xx/speed.c
new file mode 100644
index 0000000..c83a3bb
--- /dev/null
+++ b/cpu/arm920t/ep93xx/speed.c
@@ -0,0 +1,110 @@
+/*
+ * Cirrus Logic EP93xx PLL support.
+ *
+ * Copyright (C) 2009 Matthias Kaehlcke <matthias@kaehlcke.net>
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <common.h>
+#include <asm/arch/ep93xx.h>
+#include <asm/io.h>
+#include <div64.h>
+
+/*
+ * CONFIG_SYS_CLK_FREQ should be defined as the input frequency of the PLL.
+ *
+ * get_FCLK(), get_HCLK(), get_PCLK() and get_UCLK() return the clock of
+ * the specified bus in HZ.
+ */
+
+/*
+ * return the PLL output frequency
+ *
+ * PLL rate = CONFIG_SYS_CLK_FREQ * (X1FBD + 1) * (X2FBD + 1)
+ * / (X2IPD + 1) / 2^PS
+ */
+static ulong get_PLLCLK(uint32_t *pllreg)
+{
+	uint8_t i;
+	const uint32_t clkset = readl(pllreg);
+	uint64_t rate = CONFIG_SYS_CLK_FREQ;
+	rate *= ((clkset >> SYSCON_CLKSET_PLL_X1FBD1_SHIFT) & 0x1f) + 1;
+	rate *= ((clkset >> SYSCON_CLKSET_PLL_X2FBD2_SHIFT) & 0x3f) + 1;
+	do_div(rate, (clkset  & 0x1f) + 1);			/* X2IPD */
+	for (i = 0; i < ((clkset >> SYSCON_CLKSET_PLL_PS_SHIFT) & 3); i++)
+		rate >>= 1;
+
+	return (ulong)rate;
+}
+
+/* return FCLK frequency */
+ulong get_FCLK()
+{
+	const uint8_t fclk_divisors[] = { 1, 2, 4, 8, 16, 1, 1, 1 };
+	struct syscon_regs *syscon = (struct syscon_regs *)SYSCON_BASE;
+
+	const uint32_t clkset1 = readl(&syscon->clkset1);
+	const uint8_t fclk_div =
+		fclk_divisors[(clkset1 >> SYSCON_CLKSET1_FCLK_DIV_SHIFT) & 7];
+	const ulong fclk_rate = get_PLLCLK(&syscon->clkset1) / fclk_div;
+
+	return fclk_rate;
+}
+
+/* return HCLK frequency */
+ulong get_HCLK(void)
+{
+	const uint8_t hclk_divisors[] = { 1, 2, 4, 5, 6, 8, 16, 32 };
+	struct syscon_regs *syscon = (struct syscon_regs *)SYSCON_BASE;
+
+	const uint32_t clkset1 = readl(&syscon->clkset1);
+	const uint8_t hclk_div =
+		hclk_divisors[(clkset1 >> SYSCON_CLKSET1_HCLK_DIV_SHIFT) & 7];
+	const ulong hclk_rate = get_PLLCLK(&syscon->clkset1) / hclk_div;
+
+	return hclk_rate;
+}
+
+/* return PCLK frequency */
+ulong get_PCLK(void)
+{
+	const uint8_t pclk_divisors[] = { 1, 2, 4, 8 };
+	struct syscon_regs *syscon = (struct syscon_regs *)SYSCON_BASE;
+
+	const uint32_t clkset1 = readl(&syscon->clkset1);
+	const uint8_t pclk_div =
+		pclk_divisors[(clkset1 >> SYSCON_CLKSET1_PCLK_DIV_SHIFT) & 3];
+	const ulong pclk_rate = get_HCLK() / pclk_div;
+
+	return pclk_rate;
+}
+
+/* return UCLK frequency */
+ulong get_UCLK(void)
+{
+	struct syscon_regs *syscon = (struct syscon_regs *)SYSCON_BASE;
+	ulong uclk_rate;
+
+	const uint32_t value = readl(&syscon->pwrcnt);
+	if (value & SYSCON_PWRCNT_UART_BAUD)
+		uclk_rate = CONFIG_SYS_CLK_FREQ;
+	else
+		uclk_rate = CONFIG_SYS_CLK_FREQ / 2;
+
+	return uclk_rate;
+}
diff --git a/cpu/arm920t/ep93xx/timer.c b/cpu/arm920t/ep93xx/timer.c
new file mode 100644
index 0000000..6d969d9
--- /dev/null
+++ b/cpu/arm920t/ep93xx/timer.c
@@ -0,0 +1,168 @@
+/*
+ * Cirrus Logic EP93xx timer support.
+ *
+ * Copyright (C) 2009, 2010
+ * Matthias Kaehlcke <matthias@kaehlcke.net>
+ *
+ * Copyright (C) 2004, 2005
+ * Cory T. Tusar, Videon Central, Inc., <ctusar@videon-central.com>
+ *
+ * Based on the original intr.c Cirrus Logic EP93xx Rev D. interrupt support,
+ * author unknown.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <common.h>
+#include <linux/types.h>
+#include <asm/arch/ep93xx.h>
+#include <asm/io.h>
+
+#define TIMER_CLKSEL	(1 << 3)
+#define TIMER_MODE	(1 << 6)
+#define TIMER_ENABLE	(1 << 7)
+
+#define TIMER_FREQ	508469
+#define TIMER_LOAD_VAL	(TIMER_FREQ / CONFIG_SYS_HZ)
+
+static ulong timestamp;
+static ulong lastdec;
+
+static inline unsigned long clk_to_systicks(unsigned long clk_ticks)
+{
+	unsigned long sys_ticks = (clk_ticks * CONFIG_SYS_HZ) / TIMER_FREQ;
+
+	return sys_ticks;
+}
+
+static inline unsigned long usecs_to_ticks(unsigned long usecs)
+{
+	unsigned long ticks;
+
+	if (usecs >= 1000) {
+		ticks = usecs / 1000;
+		ticks *= (TIMER_LOAD_VAL * CONFIG_SYS_HZ);
+		ticks /= 1000;
+	} else {
+		ticks = usecs * TIMER_LOAD_VAL * CONFIG_SYS_HZ;
+		ticks /= (1000 * 1000);
+	}
+
+	return ticks;
+}
+
+static inline unsigned long read_timer(void)
+{
+	struct timer_regs *timer = (struct timer_regs *)TIMER_BASE;
+
+	return readl(&timer->timer3.value);
+}
+
+/*
+ * timer without interrupts
+ */
+unsigned long long get_ticks(void)
+{
+	const unsigned long now = read_timer();
+
+	if (lastdec >= now) {
+		/* normal mode */
+		timestamp += lastdec - now;
+	} else {
+		/* we have an overflow ... */
+		timestamp += lastdec + TIMER_LOAD_VAL - now;
+	}
+
+	lastdec = now;
+
+	return timestamp;
+}
+
+unsigned long get_timer_masked(void)
+{
+	return clk_to_systicks(get_ticks());
+}
+
+unsigned long get_timer(unsigned long base)
+{
+	return get_timer_masked() - base;
+}
+
+void reset_timer_masked(void)
+{
+	lastdec = read_timer();
+	timestamp = 0;
+}
+
+void reset_timer(void)
+{
+	reset_timer_masked();
+}
+
+void set_timer(unsigned long t)
+{
+	timestamp = t;
+}
+
+void __udelay(unsigned long usec)
+{
+	const unsigned long ticks = usecs_to_ticks(usec);
+	const unsigned long target = clk_to_systicks(ticks) + get_timer(0);
+
+	while (get_timer_masked() < target)
+		/* noop */;
+}
+
+void udelay_masked(unsigned long usec)
+{
+	const unsigned long ticks = usecs_to_ticks(usec);
+	const unsigned long target = clk_to_systicks(ticks) + get_timer(0);
+
+	reset_timer_masked();
+
+	while (get_timer_masked() < target)
+		/* noop */;
+}
+
+int timer_init(void)
+{
+	struct timer_regs *timer = (struct timer_regs *)TIMER_BASE;
+
+	/* use timer 3 with 508KHz and free running */
+	writel(TIMER_CLKSEL, &timer->timer3.control);
+
+	/* auto load, manual update of Timer 3 */
+	lastdec = TIMER_LOAD_VAL;
+	writel(TIMER_LOAD_VAL, &timer->timer3.load);
+
+	/* Enable the timer and periodic mode */
+	writel(TIMER_ENABLE | TIMER_MODE | TIMER_CLKSEL,
+		&timer->timer3.control);
+
+	reset_timer_masked();
+
+	return 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;
+}
diff --git a/cpu/arm920t/ep93xx/u-boot.lds b/cpu/arm920t/ep93xx/u-boot.lds
new file mode 100644
index 0000000..737c9d8
--- /dev/null
+++ b/cpu/arm920t/ep93xx/u-boot.lds
@@ -0,0 +1,59 @@
+/*
+ * (C) Copyright 2002
+ * Gary Jennejohn, DENX Software Engineering, <gj@denx.de>
+ *
+ * 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
+ */
+
+OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+SECTIONS
+{
+	. = 0x00000000;
+
+	. = ALIGN(4);
+	.text      :
+	{
+	  cpu/arm920t/start.o	(.text)
+		/* the EP93xx expects to find the pattern 'CRUS' at 0x1000 */
+	  . = 0x1000;
+	  LONG(0x53555243)
+	  *(.text)
+	}
+
+	. = ALIGN(4);
+	.rodata : { *(.rodata) }
+
+	. = ALIGN(4);
+	.data : { *(.data) }
+
+	. = ALIGN(4);
+	.got : { *(.got) }
+
+	. = .;
+	__u_boot_cmd_start = .;
+	.u_boot_cmd : { *(.u_boot_cmd) }
+	__u_boot_cmd_end = .;
+
+	. = ALIGN(4);
+	__bss_start = .;
+	.bss : { *(.bss) }
+	_end = .;
+}