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 = .;
+}