summaryrefslogtreecommitdiff
path: root/cpu/arm920t/at91rm9200
diff options
context:
space:
mode:
Diffstat (limited to 'cpu/arm920t/at91rm9200')
-rwxr-xr-xcpu/arm920t/at91rm9200/Makefile44
-rwxr-xr-xcpu/arm920t/at91rm9200/arm920t/Makefile43
-rwxr-xr-xcpu/arm920t/at91rm9200/arm920t/config.mk34
-rwxr-xr-xcpu/arm920t/at91rm9200/arm920t/cpu.c186
-rwxr-xr-xcpu/arm920t/at91rm9200/arm920t/interrupts.c174
-rwxr-xr-xcpu/arm920t/at91rm9200/arm920t/start.S441
-rwxr-xr-xcpu/arm920t/at91rm9200/bcm5221.c232
-rwxr-xr-xcpu/arm920t/at91rm9200/dm9161.c225
-rwxr-xr-xcpu/arm920t/at91rm9200/ether.c299
-rwxr-xr-xcpu/arm920t/at91rm9200/i2c.c206
-rwxr-xr-xcpu/arm920t/at91rm9200/interrupts.c210
-rwxr-xr-xcpu/arm920t/at91rm9200/lowlevel_init.S205
-rwxr-xr-xcpu/arm920t/at91rm9200/lxt972.c191
-rwxr-xr-xcpu/arm920t/at91rm9200/serial.c112
-rwxr-xr-xcpu/arm920t/at91rm9200/usb_ohci.c1635
-rwxr-xr-xcpu/arm920t/at91rm9200/usb_ohci.h419
16 files changed, 4656 insertions, 0 deletions
diff --git a/cpu/arm920t/at91rm9200/Makefile b/cpu/arm920t/at91rm9200/Makefile
new file mode 100755
index 0000000..aec9cb6
--- /dev/null
+++ b/cpu/arm920t/at91rm9200/Makefile
@@ -0,0 +1,44 @@
+#
+# (C) Copyright 2000-2005
+# 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., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+
+include $(TOPDIR)/config.mk
+
+LIB = lib$(SOC).a
+
+OBJS = bcm5221.o dm9161.o ether.o i2c.o interrupts.o \
+ lxt972.o serial.o usb_ohci.o
+SOBJS = lowlevel_init.o
+
+all: .depend $(LIB)
+
+$(LIB): $(OBJS) $(SOBJS)
+ $(AR) crv $@ $(OBJS) $(SOBJS)
+
+#########################################################################
+
+.depend: Makefile $(SOBJS:.o=.S) $(OBJS:.o=.c)
+ $(CC) -M $(CFLAGS) $(SOBJS:.o=.S) $(OBJS:.o=.c) > $@
+
+sinclude .depend
+
+#########################################################################
diff --git a/cpu/arm920t/at91rm9200/arm920t/Makefile b/cpu/arm920t/at91rm9200/arm920t/Makefile
new file mode 100755
index 0000000..0a5169f
--- /dev/null
+++ b/cpu/arm920t/at91rm9200/arm920t/Makefile
@@ -0,0 +1,43 @@
+#
+# (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.
+#
+# Some descriptions of such software. Copyright (c) 2008 WonderMedia Technologies, Inc.
+#
+# 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, see <http://www.gnu.org/licenses/>.
+#
+# WonderMedia Technologies, Inc.
+# 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C.
+#
+
+include $(TOPDIR)/config.mk
+
+LIB = lib$(CPU).a
+
+START = start.o
+OBJS = cpu.o interrupts.o
+
+all: .depend $(START) $(LIB)
+
+$(LIB): $(OBJS)
+ $(AR) crv $@ $(OBJS)
+
+#########################################################################
+
+.depend: Makefile $(START:.o=.S) $(OBJS:.o=.c)
+ $(CC) -M $(CFLAGS) $(START:.o=.S) $(OBJS:.o=.c) > $@
+
+sinclude .depend
+
+#########################################################################
diff --git a/cpu/arm920t/at91rm9200/arm920t/config.mk b/cpu/arm920t/at91rm9200/arm920t/config.mk
new file mode 100755
index 0000000..35d5369
--- /dev/null
+++ b/cpu/arm920t/at91rm9200/arm920t/config.mk
@@ -0,0 +1,34 @@
+#
+# (C) Copyright 2002
+# Gary Jennejohn, DENX Software Engineering, <gj@denx.de>
+#
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# Some descriptions of such software. Copyright (c) 2008 WonderMedia Technologies, Inc.
+#
+# 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, see <http://www.gnu.org/licenses/>.
+#
+# WonderMedia Technologies, Inc.
+# 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C.
+#
+
+PLATFORM_RELFLAGS += -fno-strict-aliasing -fno-common -ffixed-r8 \
+ -msoft-float
+
+PLATFORM_CPPFLAGS += -march=armv6
+# =========================================================================
+#
+# Supply options according to compiler version
+#
+# =========================================================================
+PLATFORM_CPPFLAGS +=$(call cc-option,-mapcs-32)
+PLATFORM_RELFLAGS +=$(call cc-option,-mshort-load-bytes,$(call cc-option,-malignment-traps,))
diff --git a/cpu/arm920t/at91rm9200/arm920t/cpu.c b/cpu/arm920t/at91rm9200/arm920t/cpu.c
new file mode 100755
index 0000000..b39587e
--- /dev/null
+++ b/cpu/arm920t/at91rm9200/arm920t/cpu.c
@@ -0,0 +1,186 @@
+/*
+ * (C) Copyright 2002
+ * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Marius Groeger <mgroeger@sysgo.de>
+ *
+ * (C) Copyright 2002
+ * Gary Jennejohn, DENX Software Engineering, <gj@denx.de>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * Copyright (c) 2008 WonderMedia Technologies, Inc.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C.
+ */
+
+/*
+ * CPU specific code
+ */
+
+#include <common.h>
+#include <command.h>
+#include <arm920t.h>
+
+#ifdef CONFIG_USE_IRQ
+DECLARE_GLOBAL_DATA_PTR;
+#endif
+
+/* read co-processor 15, register #1 (control register) */
+static unsigned long read_p15_c1(void)
+{
+ unsigned long value;
+
+ __asm__ __volatile__(
+ "mrc p15, 0, %0, c1, c0, 0 @ read control reg\n"
+ : "=r" (value)
+ :
+ : "memory");
+
+#ifdef MMU_DEBUG
+ printf("p15/c1 is = %08lx\n", value);
+#endif
+ return value;
+}
+
+/* write to co-processor 15, register #1 (control register) */
+static void write_p15_c1(unsigned long value)
+{
+#ifdef MMU_DEBUG
+ printf("write %08lx to p15/c1\n", value);
+#endif
+ __asm__ __volatile__(
+ "mcr p15, 0, %0, c1, c0, 0 @ write it back\n"
+ :
+ : "r" (value)
+ : "memory");
+
+ read_p15_c1();
+}
+
+static void cp_delay(void)
+{
+ volatile int i;
+
+ /* copro seems to need some delay between reading and writing */
+ for (i = 0; i < 100; i++)
+ ;
+}
+
+/* See also ARM920T Technical reference Manual */
+#define C1_MMU (1<<0) /* mmu off/on */
+#define C1_ALIGN (1<<1) /* alignment faults off/on */
+#define C1_DC (1<<2) /* dcache off/on */
+
+#define C1_BIG_ENDIAN (1<<7) /* big endian off/on */
+#define C1_SYS_PROT (1<<8) /* system protection */
+#define C1_ROM_PROT (1<<9) /* ROM protection */
+#define C1_IC (1<<12) /* icache off/on */
+#define C1_HIGH_VECTORS (1<<13) /* location of vectors: low/high addresses */
+
+
+int cpu_init(void)
+{
+ /*
+ * setup up stacks if necessary
+ */
+#ifdef CONFIG_USE_IRQ
+ IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;
+ FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;
+#endif
+ return 0;
+}
+
+int cleanup_before_linux(void)
+{
+ /*
+ * this function is called just before we call linux
+ * it prepares the processor for linux
+ *
+ * we turn off caches etc ...
+ */
+
+ unsigned long i;
+
+ disable_interrupts();
+
+ /* turn off I/D-cache */
+ asm ("mrc p15, 0, %0, c1, c0, 0" : "=r" (i));
+ i &= ~(C1_DC | C1_IC);
+ asm ("mcr p15, 0, %0, c1, c0, 0" : : "r" (i));
+
+ /* flush I/D-cache */
+ i = 0;
+ asm ("mcr p15, 0, %0, c7, c7, 0" : : "r" (i));
+
+ return 0;
+}
+
+int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+ disable_interrupts();
+ reset_cpu(0);
+ /*NOTREACHED*/
+ return 0;
+}
+
+void icache_enable(void)
+{
+ ulong reg;
+
+ reg = read_p15_c1(); /* get control reg. */
+ cp_delay();
+ write_p15_c1(reg | C1_IC);
+}
+
+void icache_disable(void)
+{
+ ulong reg;
+
+ reg = read_p15_c1();
+ cp_delay();
+ write_p15_c1(reg & ~C1_IC);
+}
+
+int icache_status(void)
+{
+ return (read_p15_c1() & C1_IC) != 0;
+}
+
+#ifdef USE_920T_MMU
+/* It makes no sense to use the dcache if the MMU is not enabled */
+void dcache_enable(void)
+{
+ ulong reg;
+
+ reg = read_p15_c1();
+ cp_delay();
+ write_p15_c1(reg | C1_DC);
+}
+
+void dcache_disable(void)
+{
+ ulong reg;
+
+ reg = read_p15_c1();
+ cp_delay();
+ reg &= ~C1_DC;
+ write_p15_c1(reg);
+}
+
+int dcache_status(void)
+{
+ return (read_p15_c1() & C1_DC) != 0;
+}
+#endif
diff --git a/cpu/arm920t/at91rm9200/arm920t/interrupts.c b/cpu/arm920t/at91rm9200/arm920t/interrupts.c
new file mode 100755
index 0000000..e90e385
--- /dev/null
+++ b/cpu/arm920t/at91rm9200/arm920t/interrupts.c
@@ -0,0 +1,174 @@
+/*
+ * (C) Copyright 2002
+ * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Marius Groeger <mgroeger@sysgo.de>
+ *
+ * (C) Copyright 2002
+ * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Alex Zuepke <azu@sysgo.de>
+ *
+ * (C) Copyright 2002
+ * Gary Jennejohn, DENX Software Engineering, <gj@denx.de>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * Copyright (c) 2008 WonderMedia Technologies, Inc.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C.
+ */
+
+#include <common.h>
+#include <arm920t.h>
+#include <asm/proc-armv/ptrace.h>
+
+#ifdef CONFIG_USE_IRQ
+/* enable IRQ interrupts */
+void enable_interrupts(void)
+{
+ unsigned long temp;
+ __asm__ __volatile__("mrs %0, cpsr\n"
+ "bic %0, %0, #0x80\n"
+ "msr cpsr_c, %0"
+ : "=r" (temp)
+ :
+ : "memory");
+}
+
+
+/*
+ * disable IRQ/FIQ interrupts
+ * returns true if interrupts had been enabled before we disabled them
+ */
+int disable_interrupts(void)
+{
+ unsigned long old, temp;
+ __asm__ __volatile__("mrs %0, cpsr\n"
+ "orr %1, %0, #0xc0\n"
+ "msr cpsr_c, %1"
+ : "=r" (old), "=r" (temp)
+ :
+ : "memory");
+ return (old & 0x80) == 0;
+}
+#else
+void enable_interrupts(void)
+{
+ return;
+}
+int disable_interrupts(void)
+{
+ return 0;
+}
+#endif
+
+
+void bad_mode(void)
+{
+ panic("Resetting CPU ...\n");
+ reset_cpu(0);
+}
+
+void show_regs(struct pt_regs *regs)
+{
+ unsigned long flags;
+ const char *processor_modes[] = {
+ "USER_26", "FIQ_26", "IRQ_26", "SVC_26",
+ "UK4_26", "UK5_26", "UK6_26", "UK7_26",
+ "UK8_26", "UK9_26", "UK10_26", "UK11_26",
+ "UK12_26", "UK13_26", "UK14_26", "UK15_26",
+ "USER_32", "FIQ_32", "IRQ_32", "SVC_32",
+ "UK4_32", "UK5_32", "UK6_32", "ABT_32",
+ "UK8_32", "UK9_32", "UK10_32", "UND_32",
+ "UK12_32", "UK13_32", "UK14_32", "SYS_32",
+ };
+
+ flags = condition_codes(regs);
+
+ printf("pc : [<%08lx>] lr : [<%08lx>]\n"
+ "sp : %08lx ip : %08lx fp : %08lx\n",
+ instruction_pointer(regs),
+ regs->ARM_lr, regs->ARM_sp, regs->ARM_ip, regs->ARM_fp);
+ printf("r10: %08lx r9 : %08lx r8 : %08lx\n",
+ regs->ARM_r10, regs->ARM_r9, regs->ARM_r8);
+ printf("r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n",
+ regs->ARM_r7, regs->ARM_r6, regs->ARM_r5, regs->ARM_r4);
+ printf("r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n",
+ regs->ARM_r3, regs->ARM_r2, regs->ARM_r1, regs->ARM_r0);
+ printf("Flags: %c%c%c%c",
+ flags & CC_N_BIT ? 'N' : 'n',
+ flags & CC_Z_BIT ? 'Z' : 'z',
+ flags & CC_C_BIT ? 'C' : 'c', flags & CC_V_BIT ? 'V' : 'v');
+ printf(" IRQs %s FIQs %s Mode %s%s\n",
+ interrupts_enabled(regs) ? "on" : "off",
+ fast_interrupts_enabled(regs) ? "on" : "off",
+ processor_modes[processor_mode(regs)],
+ thumb_mode(regs) ? " (T)" : "");
+}
+
+void do_undefined_instruction(struct pt_regs *pt_regs)
+{
+ printf("undefined instruction\n");
+ show_regs(pt_regs);
+ bad_mode();
+}
+
+void do_software_interrupt(struct pt_regs *pt_regs)
+{
+ printf("software interrupt\n");
+ show_regs(pt_regs);
+ bad_mode();
+}
+
+void do_prefetch_abort(struct pt_regs *pt_regs)
+{
+ printf("prefetch abort\n");
+ show_regs(pt_regs);
+ bad_mode();
+}
+
+void do_data_abort(struct pt_regs *pt_regs)
+{
+ printf("data abort\n");
+ show_regs(pt_regs);
+ bad_mode();
+}
+
+void do_not_used(struct pt_regs *pt_regs)
+{
+ printf("not used\n");
+ show_regs(pt_regs);
+ bad_mode();
+}
+
+void do_fiq(struct pt_regs *pt_regs)
+{
+ printf("fast interrupt request\n");
+ show_regs(pt_regs);
+ bad_mode();
+}
+
+void do_irq(struct pt_regs *pt_regs)
+{
+#if defined(CONFIG_USE_IRQ) && defined(CONFIG_ARCH_INTEGRATOR)
+ /* ASSUMED to be a timer interrupt */
+ /* Just clear it - count handled in */
+ /* integratorap.c */
+ *(volatile ulong*)(CFG_TIMERBASE + 0x0C) = 0;
+#else
+ printf("interrupt request\n");
+ show_regs(pt_regs);
+ bad_mode();
+#endif
+}
diff --git a/cpu/arm920t/at91rm9200/arm920t/start.S b/cpu/arm920t/at91rm9200/arm920t/start.S
new file mode 100755
index 0000000..66c12bf
--- /dev/null
+++ b/cpu/arm920t/at91rm9200/arm920t/start.S
@@ -0,0 +1,441 @@
+/*
+ * armboot - Startup Code for ARM920 CPU-core
+ *
+ * Copyright (c) 2001 Marius Gröger <mag@sysgo.de>
+ * Copyright (c) 2002 Alex Züpke <azu@sysgo.de>
+ * Copyright (c) 2002 Gary Jennejohn <gj@denx.de>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * Copyright (c) 2008 WonderMedia Technologies, Inc.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C.
+ */
+
+
+#include <config.h>
+#include <version.h>
+
+
+/*
+ *************************************************************************
+ *
+ * Jump vector table as in table 3.1 in [1]
+ *
+ *************************************************************************
+ */
+
+
+.globl _start
+_start: b reset
+ ldr pc, _undefined_instruction
+ ldr pc, _software_interrupt
+ ldr pc, _prefetch_abort
+ ldr pc, _data_abort
+ ldr pc, _not_used
+ ldr pc, _irq
+ ldr pc, _fiq
+
+_undefined_instruction: .word undefined_instruction
+_software_interrupt: .word software_interrupt
+_prefetch_abort: .word prefetch_abort
+_data_abort: .word data_abort
+_not_used: .word not_used
+_irq: .word irq
+_fiq: .word fiq
+
+ .balignl 16,0xdeadbeef
+
+
+/*
+ *************************************************************************
+ *
+ * Startup Code (reset vector)
+ *
+ * do important init only if we don't start from memory!
+ * relocate armboot to ram
+ * setup stack
+ * jump to second stage
+ *
+ *************************************************************************
+ */
+
+_TEXT_BASE:
+ .word TEXT_BASE
+
+.globl _armboot_start
+_armboot_start:
+ .word _start
+
+/*
+ * These are defined in the board-specific linker script.
+ */
+.globl _bss_start
+_bss_start:
+ .word __bss_start
+
+.globl _bss_end
+_bss_end:
+ .word _end
+
+#ifdef CONFIG_USE_IRQ
+/* IRQ stack memory (calculated at run-time) */
+.globl IRQ_STACK_START
+IRQ_STACK_START:
+ .word 0x0badc0de
+
+/* IRQ stack memory (calculated at run-time) */
+.globl FIQ_STACK_START
+FIQ_STACK_START:
+ .word 0x0badc0de
+#endif
+
+
+/*
+ * the actual reset code
+ */
+
+reset:
+ /*
+ * set the cpu to SVC32 mode
+ */
+ mrs r0,cpsr
+ bic r0,r0,#0x1f
+ orr r0,r0,#0xd3
+ msr cpsr,r0
+
+/* turn off the watchdog */
+#if defined(CONFIG_S3C2400)
+# define pWTCON 0x15300000
+# define INTMSK 0x14400008 /* Interupt-Controller base addresses */
+# define CLKDIVN 0x14800014 /* clock divisor register */
+#elif defined(CONFIG_S3C2410)
+# define pWTCON 0x53000000
+# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
+# define INTSUBMSK 0x4A00001C
+# define CLKDIVN 0x4C000014 /* clock divisor register */
+#endif
+
+#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
+ ldr r0, =pWTCON
+ mov r1, #0x0
+ str r1, [r0]
+
+ /*
+ * mask all IRQs by setting all bits in the INTMR - default
+ */
+ mov r1, #0xffffffff
+ ldr r0, =INTMSK
+ str r1, [r0]
+# if defined(CONFIG_S3C2410)
+ ldr r1, =0x3ff
+ ldr r0, =INTSUBMSK
+ str r1, [r0]
+# endif
+
+ /* FCLK:HCLK:PCLK = 1:2:4 */
+ /* default FCLK is 120 MHz ! */
+ ldr r0, =CLKDIVN
+ mov r1, #3
+ str r1, [r0]
+#endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */
+
+ /*
+ * we do sys-critical inits only at reboot,
+ * not when booting from ram!
+ */
+#ifndef CONFIG_SKIP_LOWLEVEL_INIT
+ bl cpu_init_crit
+#endif
+
+#ifndef CONFIG_SKIP_RELOCATE_UBOOT
+relocate: /* relocate U-Boot to RAM */
+ adr r0, _start /* r0 <- current position of code */
+ ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
+ cmp r0, r1 /* don't reloc during debug */
+ beq stack_setup
+
+ ldr r2, _armboot_start
+ ldr r3, _bss_start
+ sub r2, r3, r2 /* r2 <- size of armboot */
+ add r2, r0, r2 /* r2 <- source end address */
+
+copy_loop:
+ ldmia r0!, {r3-r10} /* copy from source address [r0] */
+ stmia r1!, {r3-r10} /* copy to target address [r1] */
+ cmp r0, r2 /* until source end addreee [r2] */
+ ble copy_loop
+#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
+
+ /* Set up the stack */
+stack_setup:
+ ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
+ sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
+ sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
+#ifdef CONFIG_USE_IRQ
+ sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
+#endif
+ sub sp, r0, #12 /* leave 3 words for abort-stack */
+
+clear_bss:
+ ldr r0, _bss_start /* find start of bss segment */
+ ldr r1, _bss_end /* stop here */
+ mov r2, #0x00000000 /* clear */
+
+clbss_l:str r2, [r0] /* clear loop... */
+ add r0, r0, #4
+ cmp r0, r1
+ ble clbss_l
+
+#if 0
+ /* try doing this stuff after the relocation */
+ ldr r0, =pWTCON
+ mov r1, #0x0
+ str r1, [r0]
+
+ /*
+ * mask all IRQs by setting all bits in the INTMR - default
+ */
+ mov r1, #0xffffffff
+ ldr r0, =INTMR
+ str r1, [r0]
+
+ /* FCLK:HCLK:PCLK = 1:2:4 */
+ /* default FCLK is 120 MHz ! */
+ ldr r0, =CLKDIVN
+ mov r1, #3
+ str r1, [r0]
+ /* END stuff after relocation */
+#endif
+
+ ldr pc, _start_armboot
+
+_start_armboot: .word start_armboot
+
+
+/*
+ *************************************************************************
+ *
+ * CPU_init_critical registers
+ *
+ * setup important registers
+ * setup memory timing
+ *
+ *************************************************************************
+ */
+
+
+cpu_init_crit:
+
+ /*
+ * flush v4 I/D caches
+ */
+ mov r0, #0
+ mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
+ mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
+
+ /*
+ * disable MMU stuff and caches
+ */
+ mrc p15, 0, r0, c1, c0, 0
+
+#if defined (CONFIG_WMT)
+ bic r0, r0, #0x00000300 @ clear bits 9:8 (---- --RS)
+ bic r0, r0, #0x0000000F @ clear bits 3:0 (---- WCAM)
+ orr r0, r0, #0x00002000 @ set bit 13 (--V--)
+#else
+ bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
+ bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
+#endif
+ orr r0, r0, #0x00000002 @ set bit 2 (A) Align
+ orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
+ mcr p15, 0, r0, c1, c0, 0
+
+ /*
+ * before relocating, we have to setup RAM timing
+ * because memory timing is board-dependend, you will
+ * find a lowlevel_init.S in your board directory.
+ */
+ mov ip, lr
+ bl lowlevel_init
+ mov lr, ip
+ mov pc, lr
+
+
+/*
+ *************************************************************************
+ *
+ * Interrupt handling
+ *
+ *************************************************************************
+ */
+
+@
+@ IRQ stack frame.
+@
+#define S_FRAME_SIZE 72
+
+#define S_OLD_R0 68
+#define S_PSR 64
+#define S_PC 60
+#define S_LR 56
+#define S_SP 52
+
+#define S_IP 48
+#define S_FP 44
+#define S_R10 40
+#define S_R9 36
+#define S_R8 32
+#define S_R7 28
+#define S_R6 24
+#define S_R5 20
+#define S_R4 16
+#define S_R3 12
+#define S_R2 8
+#define S_R1 4
+#define S_R0 0
+
+#define MODE_SVC 0x13
+#define I_BIT 0x80
+
+/*
+ * use bad_save_user_regs for abort/prefetch/undef/swi ...
+ * use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling
+ */
+
+ .macro bad_save_user_regs
+ sub sp, sp, #S_FRAME_SIZE
+ stmia sp, {r0 - r12} @ Calling r0-r12
+ ldr r2, _armboot_start
+ sub r2, r2, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
+ sub r2, r2, #(CFG_GBL_DATA_SIZE+8) @ set base 2 words into abort stack
+ ldmia r2, {r2 - r3} @ get pc, cpsr
+ add r0, sp, #S_FRAME_SIZE @ restore sp_SVC
+
+ add r5, sp, #S_SP
+ mov r1, lr
+ stmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr
+ mov r0, sp
+ .endm
+
+ .macro irq_save_user_regs
+ sub sp, sp, #S_FRAME_SIZE
+ stmia sp, {r0 - r12} @ Calling r0-r12
+ add r8, sp, #S_PC
+ stmdb r8, {sp, lr}^ @ Calling SP, LR
+ str lr, [r8, #0] @ Save calling PC
+ mrs r6, spsr
+ str r6, [r8, #4] @ Save CPSR
+ str r0, [r8, #8] @ Save OLD_R0
+ mov r0, sp
+ .endm
+
+ .macro irq_restore_user_regs
+ ldmia sp, {r0 - lr}^ @ Calling r0 - lr
+ mov r0, r0
+ ldr lr, [sp, #S_PC] @ Get PC
+ add sp, sp, #S_FRAME_SIZE
+ subs pc, lr, #4 @ return & move spsr_svc into cpsr
+ .endm
+
+ .macro get_bad_stack
+ ldr r13, _armboot_start @ setup our mode stack
+ sub r13, r13, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
+ sub r13, r13, #(CFG_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stack
+
+ str lr, [r13] @ save caller lr / spsr
+ mrs lr, spsr
+ str lr, [r13, #4]
+
+ mov r13, #MODE_SVC @ prepare SVC-Mode
+ @ msr spsr_c, r13
+ msr spsr, r13
+ mov lr, pc
+ movs pc, lr
+ .endm
+
+ .macro get_irq_stack @ setup IRQ stack
+ ldr sp, IRQ_STACK_START
+ .endm
+
+ .macro get_fiq_stack @ setup FIQ stack
+ ldr sp, FIQ_STACK_START
+ .endm
+
+/*
+ * exception handlers
+ */
+ .align 5
+undefined_instruction:
+ get_bad_stack
+ bad_save_user_regs
+ bl do_undefined_instruction
+
+ .align 5
+software_interrupt:
+ get_bad_stack
+ bad_save_user_regs
+ bl do_software_interrupt
+
+ .align 5
+prefetch_abort:
+ get_bad_stack
+ bad_save_user_regs
+ bl do_prefetch_abort
+
+ .align 5
+data_abort:
+ get_bad_stack
+ bad_save_user_regs
+ bl do_data_abort
+
+ .align 5
+not_used:
+ get_bad_stack
+ bad_save_user_regs
+ bl do_not_used
+
+#ifdef CONFIG_USE_IRQ
+
+ .align 5
+irq:
+ get_irq_stack
+ irq_save_user_regs
+ bl do_irq
+ irq_restore_user_regs
+
+ .align 5
+fiq:
+ get_fiq_stack
+ /* someone ought to write a more effiction fiq_save_user_regs */
+ irq_save_user_regs
+ bl do_fiq
+ irq_restore_user_regs
+
+#else
+
+ .align 5
+irq:
+ get_bad_stack
+ bad_save_user_regs
+ bl do_irq
+
+ .align 5
+fiq:
+ get_bad_stack
+ bad_save_user_regs
+ bl do_fiq
+
+#endif
diff --git a/cpu/arm920t/at91rm9200/bcm5221.c b/cpu/arm920t/at91rm9200/bcm5221.c
new file mode 100755
index 0000000..6db1435
--- /dev/null
+++ b/cpu/arm920t/at91rm9200/bcm5221.c
@@ -0,0 +1,232 @@
+/*
+ * Broadcom BCM5221 Ethernet PHY
+ *
+ * (C) Copyright 2005 REA Elektronik GmbH <www.rea.de>
+ * Anders Larsen <alarsen@rea.de>
+ *
+ * (C) Copyright 2003
+ * Author : Hamid Ikdoumi (Atmel)
+ *
+ * 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 <at91rm9200_net.h>
+#include <net.h>
+#include <bcm5221.h>
+
+#ifdef CONFIG_DRIVER_ETHER
+
+#if (CONFIG_COMMANDS & CFG_CMD_NET)
+
+/*
+ * Name:
+ * bcm5221_IsPhyConnected
+ * Description:
+ * Reads the 2 PHY ID registers
+ * Arguments:
+ * p_mac - pointer to AT91S_EMAC struct
+ * Return value:
+ * TRUE - if id read successfully
+ * FALSE- if error
+ */
+unsigned int bcm5221_IsPhyConnected (AT91PS_EMAC p_mac)
+{
+ unsigned short Id1, Id2;
+
+ at91rm9200_EmacEnableMDIO (p_mac);
+ at91rm9200_EmacReadPhy (p_mac, BCM5221_PHYID1, &Id1);
+ at91rm9200_EmacReadPhy (p_mac, BCM5221_PHYID2, &Id2);
+ at91rm9200_EmacDisableMDIO (p_mac);
+
+ if ((Id1 == (BCM5221_PHYID1_OUI >> 6)) &&
+ ((Id2 >> 10) == (BCM5221_PHYID1_OUI & BCM5221_LSB_MASK)))
+ return TRUE;
+
+ return FALSE;
+}
+
+/*
+ * Name:
+ * bcm5221_GetLinkSpeed
+ * Description:
+ * Link parallel detection status of MAC is checked and set in the
+ * MAC configuration registers
+ * Arguments:
+ * p_mac - pointer to MAC
+ * Return value:
+ * TRUE - if link status set succesfully
+ * FALSE - if link status not set
+ */
+unsigned char bcm5221_GetLinkSpeed (AT91PS_EMAC p_mac)
+{
+ unsigned short stat1, stat2;
+
+ if (!at91rm9200_EmacReadPhy (p_mac, BCM5221_BMSR, &stat1))
+ return FALSE;
+
+ if (!(stat1 & BCM5221_LINK_STATUS)) /* link status up? */
+ return FALSE;
+
+ if (!at91rm9200_EmacReadPhy (p_mac, BCM5221_ACSR, &stat2))
+ return FALSE;
+
+ if ((stat1 & BCM5221_100BASE_TX_FD) && (stat2 & BCM5221_100) && (stat2 & BCM5221_FDX)) {
+ /*set Emac for 100BaseTX and Full Duplex */
+ p_mac->EMAC_CFG |= AT91C_EMAC_SPD | AT91C_EMAC_FD;
+ return TRUE;
+ }
+
+ if ((stat1 & BCM5221_10BASE_T_FD) && !(stat2 & BCM5221_100) && (stat2 & BCM5221_FDX)) {
+ /*set MII for 10BaseT and Full Duplex */
+ p_mac->EMAC_CFG = (p_mac->EMAC_CFG &
+ ~(AT91C_EMAC_SPD | AT91C_EMAC_FD))
+ | AT91C_EMAC_FD;
+ return TRUE;
+ }
+
+ if ((stat1 & BCM5221_100BASE_TX_HD) && (stat2 & BCM5221_100) && !(stat2 & BCM5221_FDX)) {
+ /*set MII for 100BaseTX and Half Duplex */
+ p_mac->EMAC_CFG = (p_mac->EMAC_CFG &
+ ~(AT91C_EMAC_SPD | AT91C_EMAC_FD))
+ | AT91C_EMAC_SPD;
+ return TRUE;
+ }
+
+ if ((stat1 & BCM5221_10BASE_T_HD) && !(stat2 & BCM5221_100) && !(stat2 & BCM5221_FDX)) {
+ /*set MII for 10BaseT and Half Duplex */
+ p_mac->EMAC_CFG &= ~(AT91C_EMAC_SPD | AT91C_EMAC_FD);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/*
+ * Name:
+ * bcm5221_InitPhy
+ * Description:
+ * MAC starts checking its link by using parallel detection and
+ * Autonegotiation and the same is set in the MAC configuration registers
+ * Arguments:
+ * p_mac - pointer to struct AT91S_EMAC
+ * Return value:
+ * TRUE - if link status set succesfully
+ * FALSE - if link status not set
+ */
+unsigned char bcm5221_InitPhy (AT91PS_EMAC p_mac)
+{
+ unsigned char ret = TRUE;
+ unsigned short IntValue;
+
+ at91rm9200_EmacEnableMDIO (p_mac);
+
+ if (!bcm5221_GetLinkSpeed (p_mac)) {
+ /* Try another time */
+ ret = bcm5221_GetLinkSpeed (p_mac);
+ }
+
+ /* Disable PHY Interrupts */
+ at91rm9200_EmacReadPhy (p_mac, BCM5221_INTR, &IntValue);
+ /* clear FDX LED and INTR Enable */
+ IntValue &= ~(BCM5221_FDX_LED | BCM5221_INTR_ENABLE);
+ /* set FDX, SPD, Link, INTR masks */
+ IntValue |= (BCM5221_FDX_MASK | BCM5221_SPD_MASK |
+ BCM5221_LINK_MASK | BCM5221_INTR_MASK);
+ at91rm9200_EmacWritePhy (p_mac, BCM5221_INTR, &IntValue);
+ at91rm9200_EmacDisableMDIO (p_mac);
+
+ return (ret);
+}
+
+
+/*
+ * Name:
+ * bcm5221_AutoNegotiate
+ * Description:
+ * MAC Autonegotiates with the partner status of same is set in the
+ * MAC configuration registers
+ * Arguments:
+ * dev - pointer to struct net_device
+ * Return value:
+ * TRUE - if link status set successfully
+ * FALSE - if link status not set
+ */
+unsigned char bcm5221_AutoNegotiate (AT91PS_EMAC p_mac, int *status)
+{
+ unsigned short value;
+ unsigned short PhyAnar;
+ unsigned short PhyAnalpar;
+
+ /* Set bcm5221 control register */
+ if (!at91rm9200_EmacReadPhy (p_mac, BCM5221_BMCR, &value))
+ return FALSE;
+ value &= ~BCM5221_AUTONEG; /* remove autonegotiation enable */
+ value |= BCM5221_ISOLATE; /* Electrically isolate PHY */
+ if (!at91rm9200_EmacWritePhy (p_mac, BCM5221_BMCR, &value))
+ return FALSE;
+
+ /* Set the Auto_negotiation Advertisement Register */
+ /* MII advertising for 100BaseTxFD and HD, 10BaseTFD and HD, IEEE 802.3 */
+ PhyAnar = BCM5221_TX_FDX | BCM5221_TX_HDX |
+ BCM5221_10_FDX | BCM5221_10_HDX | BCM5221_AN_IEEE_802_3;
+ if (!at91rm9200_EmacWritePhy (p_mac, BCM5221_ANAR, &PhyAnar))
+ return FALSE;
+
+ /* Read the Control Register */
+ if (!at91rm9200_EmacReadPhy (p_mac, BCM5221_BMCR, &value))
+ return FALSE;
+
+ value |= BCM5221_SPEED_SELECT | BCM5221_AUTONEG | BCM5221_DUPLEX_MODE;
+ if (!at91rm9200_EmacWritePhy (p_mac, BCM5221_BMCR, &value))
+ return FALSE;
+ /* Restart Auto_negotiation */
+ value |= BCM5221_RESTART_AUTONEG;
+ value &= ~BCM5221_ISOLATE;
+ if (!at91rm9200_EmacWritePhy (p_mac, BCM5221_BMCR, &value))
+ return FALSE;
+
+ /*check AutoNegotiate complete */
+ udelay (10000);
+ at91rm9200_EmacReadPhy (p_mac, BCM5221_BMSR, &value);
+ if (!(value & BCM5221_AUTONEG_COMP))
+ return FALSE;
+
+ /* Get the AutoNeg Link partner base page */
+ if (!at91rm9200_EmacReadPhy (p_mac, BCM5221_ANLPAR, &PhyAnalpar))
+ return FALSE;
+
+ if ((PhyAnar & BCM5221_TX_FDX) && (PhyAnalpar & BCM5221_TX_FDX)) {
+ /*set MII for 100BaseTX and Full Duplex */
+ p_mac->EMAC_CFG |= AT91C_EMAC_SPD | AT91C_EMAC_FD;
+ return TRUE;
+ }
+
+ if ((PhyAnar & BCM5221_10_FDX) && (PhyAnalpar & BCM5221_10_FDX)) {
+ /*set MII for 10BaseT and Full Duplex */
+ p_mac->EMAC_CFG = (p_mac->EMAC_CFG &
+ ~(AT91C_EMAC_SPD | AT91C_EMAC_FD))
+ | AT91C_EMAC_FD;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+#endif /* CONFIG_COMMANDS & CFG_CMD_NET */
+
+#endif /* CONFIG_DRIVER_ETHER */
diff --git a/cpu/arm920t/at91rm9200/dm9161.c b/cpu/arm920t/at91rm9200/dm9161.c
new file mode 100755
index 0000000..4b13c23
--- /dev/null
+++ b/cpu/arm920t/at91rm9200/dm9161.c
@@ -0,0 +1,225 @@
+/*
+ * (C) Copyright 2003
+ * Author : Hamid Ikdoumi (Atmel)
+ *
+ * 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 <at91rm9200_net.h>
+#include <net.h>
+#include <dm9161.h>
+
+#ifdef CONFIG_DRIVER_ETHER
+
+#if (CONFIG_COMMANDS & CFG_CMD_NET)
+
+/*
+ * Name:
+ * dm9161_IsPhyConnected
+ * Description:
+ * Reads the 2 PHY ID registers
+ * Arguments:
+ * p_mac - pointer to AT91S_EMAC struct
+ * Return value:
+ * TRUE - if id read successfully
+ * FALSE- if error
+ */
+unsigned int dm9161_IsPhyConnected (AT91PS_EMAC p_mac)
+{
+ unsigned short Id1, Id2;
+
+ at91rm9200_EmacEnableMDIO (p_mac);
+ at91rm9200_EmacReadPhy (p_mac, DM9161_PHYID1, &Id1);
+ at91rm9200_EmacReadPhy (p_mac, DM9161_PHYID2, &Id2);
+ at91rm9200_EmacDisableMDIO (p_mac);
+
+ if ((Id1 == (DM9161_PHYID1_OUI >> 6)) &&
+ ((Id2 >> 10) == (DM9161_PHYID1_OUI & DM9161_LSB_MASK)))
+ return TRUE;
+
+ return FALSE;
+}
+
+/*
+ * Name:
+ * dm9161_GetLinkSpeed
+ * Description:
+ * Link parallel detection status of MAC is checked and set in the
+ * MAC configuration registers
+ * Arguments:
+ * p_mac - pointer to MAC
+ * Return value:
+ * TRUE - if link status set succesfully
+ * FALSE - if link status not set
+ */
+UCHAR dm9161_GetLinkSpeed (AT91PS_EMAC p_mac)
+{
+ unsigned short stat1, stat2;
+
+ if (!at91rm9200_EmacReadPhy (p_mac, DM9161_BMSR, &stat1))
+ return FALSE;
+
+ if (!(stat1 & DM9161_LINK_STATUS)) /* link status up? */
+ return FALSE;
+
+ if (!at91rm9200_EmacReadPhy (p_mac, DM9161_DSCSR, &stat2))
+ return FALSE;
+
+ if ((stat1 & DM9161_100BASE_TX_FD) && (stat2 & DM9161_100FDX)) {
+ /*set Emac for 100BaseTX and Full Duplex */
+ p_mac->EMAC_CFG |= AT91C_EMAC_SPD | AT91C_EMAC_FD;
+ return TRUE;
+ }
+
+ if ((stat1 & DM9161_10BASE_T_FD) && (stat2 & DM9161_10FDX)) {
+ /*set MII for 10BaseT and Full Duplex */
+ p_mac->EMAC_CFG = (p_mac->EMAC_CFG &
+ ~(AT91C_EMAC_SPD | AT91C_EMAC_FD))
+ | AT91C_EMAC_FD;
+ return TRUE;
+ }
+
+ if ((stat1 & DM9161_100BASE_T4_HD) && (stat2 & DM9161_100HDX)) {
+ /*set MII for 100BaseTX and Half Duplex */
+ p_mac->EMAC_CFG = (p_mac->EMAC_CFG &
+ ~(AT91C_EMAC_SPD | AT91C_EMAC_FD))
+ | AT91C_EMAC_SPD;
+ return TRUE;
+ }
+
+ if ((stat1 & DM9161_10BASE_T_HD) && (stat2 & DM9161_10HDX)) {
+ /*set MII for 10BaseT and Half Duplex */
+ p_mac->EMAC_CFG &= ~(AT91C_EMAC_SPD | AT91C_EMAC_FD);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/*
+ * Name:
+ * dm9161_InitPhy
+ * Description:
+ * MAC starts checking its link by using parallel detection and
+ * Autonegotiation and the same is set in the MAC configuration registers
+ * Arguments:
+ * p_mac - pointer to struct AT91S_EMAC
+ * Return value:
+ * TRUE - if link status set succesfully
+ * FALSE - if link status not set
+ */
+UCHAR dm9161_InitPhy (AT91PS_EMAC p_mac)
+{
+ UCHAR ret = TRUE;
+ unsigned short IntValue;
+
+ at91rm9200_EmacEnableMDIO (p_mac);
+
+ if (!dm9161_GetLinkSpeed (p_mac)) {
+ /* Try another time */
+ ret = dm9161_GetLinkSpeed (p_mac);
+ }
+
+ /* Disable PHY Interrupts */
+ at91rm9200_EmacReadPhy (p_mac, DM9161_MDINTR, &IntValue);
+ /* set FDX, SPD, Link, INTR masks */
+ IntValue |= (DM9161_FDX_MASK | DM9161_SPD_MASK |
+ DM9161_LINK_MASK | DM9161_INTR_MASK);
+ at91rm9200_EmacWritePhy (p_mac, DM9161_MDINTR, &IntValue);
+ at91rm9200_EmacDisableMDIO (p_mac);
+
+ return (ret);
+}
+
+
+/*
+ * Name:
+ * dm9161_AutoNegotiate
+ * Description:
+ * MAC Autonegotiates with the partner status of same is set in the
+ * MAC configuration registers
+ * Arguments:
+ * dev - pointer to struct net_device
+ * Return value:
+ * TRUE - if link status set successfully
+ * FALSE - if link status not set
+ */
+UCHAR dm9161_AutoNegotiate (AT91PS_EMAC p_mac, int *status)
+{
+ unsigned short value;
+ unsigned short PhyAnar;
+ unsigned short PhyAnalpar;
+
+ /* Set dm9161 control register */
+ if (!at91rm9200_EmacReadPhy (p_mac, DM9161_BMCR, &value))
+ return FALSE;
+ value &= ~DM9161_AUTONEG; /* remove autonegotiation enable */
+ value |= DM9161_ISOLATE; /* Electrically isolate PHY */
+ if (!at91rm9200_EmacWritePhy (p_mac, DM9161_BMCR, &value))
+ return FALSE;
+
+ /* Set the Auto_negotiation Advertisement Register */
+ /* MII advertising for Next page, 100BaseTxFD and HD, 10BaseTFD and HD, IEEE 802.3 */
+ PhyAnar = DM9161_NP | DM9161_TX_FDX | DM9161_TX_HDX |
+ DM9161_10_FDX | DM9161_10_HDX | DM9161_AN_IEEE_802_3;
+ if (!at91rm9200_EmacWritePhy (p_mac, DM9161_ANAR, &PhyAnar))
+ return FALSE;
+
+ /* Read the Control Register */
+ if (!at91rm9200_EmacReadPhy (p_mac, DM9161_BMCR, &value))
+ return FALSE;
+
+ value |= DM9161_SPEED_SELECT | DM9161_AUTONEG | DM9161_DUPLEX_MODE;
+ if (!at91rm9200_EmacWritePhy (p_mac, DM9161_BMCR, &value))
+ return FALSE;
+ /* Restart Auto_negotiation */
+ value |= DM9161_RESTART_AUTONEG;
+ value &= ~DM9161_ISOLATE;
+ if (!at91rm9200_EmacWritePhy (p_mac, DM9161_BMCR, &value))
+ return FALSE;
+
+ /*check AutoNegotiate complete */
+ udelay (10000);
+ at91rm9200_EmacReadPhy (p_mac, DM9161_BMSR, &value);
+ if (!(value & DM9161_AUTONEG_COMP))
+ return FALSE;
+
+ /* Get the AutoNeg Link partner base page */
+ if (!at91rm9200_EmacReadPhy (p_mac, DM9161_ANLPAR, &PhyAnalpar))
+ return FALSE;
+
+ if ((PhyAnar & DM9161_TX_FDX) && (PhyAnalpar & DM9161_TX_FDX)) {
+ /*set MII for 100BaseTX and Full Duplex */
+ p_mac->EMAC_CFG |= AT91C_EMAC_SPD | AT91C_EMAC_FD;
+ return TRUE;
+ }
+
+ if ((PhyAnar & DM9161_10_FDX) && (PhyAnalpar & DM9161_10_FDX)) {
+ /*set MII for 10BaseT and Full Duplex */
+ p_mac->EMAC_CFG = (p_mac->EMAC_CFG &
+ ~(AT91C_EMAC_SPD | AT91C_EMAC_FD))
+ | AT91C_EMAC_FD;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+#endif /* CONFIG_COMMANDS & CFG_CMD_NET */
+
+#endif /* CONFIG_DRIVER_ETHER */
diff --git a/cpu/arm920t/at91rm9200/ether.c b/cpu/arm920t/at91rm9200/ether.c
new file mode 100755
index 0000000..67008d0
--- /dev/null
+++ b/cpu/arm920t/at91rm9200/ether.c
@@ -0,0 +1,299 @@
+/*
+ * (C) Copyright 2003
+ * Author : Hamid Ikdoumi (Atmel)
+ *
+ * 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 <at91rm9200_net.h>
+#include <net.h>
+#include <miiphy.h>
+
+/* ----- Ethernet Buffer definitions ----- */
+
+typedef struct {
+ unsigned long addr, size;
+} rbf_t;
+
+#define RBF_ADDR 0xfffffffc
+#define RBF_OWNER (1<<0)
+#define RBF_WRAP (1<<1)
+#define RBF_BROADCAST (1<<31)
+#define RBF_MULTICAST (1<<30)
+#define RBF_UNICAST (1<<29)
+#define RBF_EXTERNAL (1<<28)
+#define RBF_UNKOWN (1<<27)
+#define RBF_SIZE 0x07ff
+#define RBF_LOCAL4 (1<<26)
+#define RBF_LOCAL3 (1<<25)
+#define RBF_LOCAL2 (1<<24)
+#define RBF_LOCAL1 (1<<23)
+
+#define RBF_FRAMEMAX 64
+#define RBF_FRAMELEN 0x600
+
+#ifdef CONFIG_DRIVER_ETHER
+
+#if (CONFIG_COMMANDS & CFG_CMD_NET)
+
+/* alignment as per Errata #11 (64 bytes) is insufficient! */
+rbf_t rbfdt[RBF_FRAMEMAX] __attribute((aligned(512)));
+rbf_t *rbfp;
+
+unsigned char rbf_framebuf[RBF_FRAMEMAX][RBF_FRAMELEN] __attribute((aligned(4)));
+
+/* structure to interface the PHY */
+AT91S_PhyOps PhyOps;
+
+AT91PS_EMAC p_mac;
+
+/*********** EMAC Phy layer Management functions *************************/
+/*
+ * Name:
+ * at91rm9200_EmacEnableMDIO
+ * Description:
+ * Enables the MDIO bit in MAC control register
+ * Arguments:
+ * p_mac - pointer to struct AT91S_EMAC
+ * Return value:
+ * none
+ */
+void at91rm9200_EmacEnableMDIO (AT91PS_EMAC p_mac)
+{
+ /* Mac CTRL reg set for MDIO enable */
+ p_mac->EMAC_CTL |= AT91C_EMAC_MPE; /* Management port enable */
+}
+
+/*
+ * Name:
+ * at91rm9200_EmacDisableMDIO
+ * Description:
+ * Disables the MDIO bit in MAC control register
+ * Arguments:
+ * p_mac - pointer to struct AT91S_EMAC
+ * Return value:
+ * none
+ */
+void at91rm9200_EmacDisableMDIO (AT91PS_EMAC p_mac)
+{
+ /* Mac CTRL reg set for MDIO disable */
+ p_mac->EMAC_CTL &= ~AT91C_EMAC_MPE; /* Management port disable */
+}
+
+
+/*
+ * Name:
+ * at91rm9200_EmacReadPhy
+ * Description:
+ * Reads data from the PHY register
+ * Arguments:
+ * dev - pointer to struct net_device
+ * RegisterAddress - unsigned char
+ * pInput - pointer to value read from register
+ * Return value:
+ * TRUE - if data read successfully
+ */
+UCHAR at91rm9200_EmacReadPhy (AT91PS_EMAC p_mac,
+ unsigned char RegisterAddress,
+ unsigned short *pInput)
+{
+ p_mac->EMAC_MAN = (AT91C_EMAC_HIGH & ~AT91C_EMAC_LOW) |
+ (AT91C_EMAC_RW_R) |
+ (RegisterAddress << 18) |
+ (AT91C_EMAC_CODE_802_3);
+
+ udelay (10000);
+
+ *pInput = (unsigned short) p_mac->EMAC_MAN;
+
+ return TRUE;
+}
+
+
+/*
+ * Name:
+ * at91rm9200_EmacWritePhy
+ * Description:
+ * Writes data to the PHY register
+ * Arguments:
+ * dev - pointer to struct net_device
+ * RegisterAddress - unsigned char
+ * pOutput - pointer to value to be written in the register
+ * Return value:
+ * TRUE - if data read successfully
+ */
+UCHAR at91rm9200_EmacWritePhy (AT91PS_EMAC p_mac,
+ unsigned char RegisterAddress,
+ unsigned short *pOutput)
+{
+ p_mac->EMAC_MAN = (AT91C_EMAC_HIGH & ~AT91C_EMAC_LOW) |
+ AT91C_EMAC_CODE_802_3 | AT91C_EMAC_RW_W |
+ (RegisterAddress << 18) | *pOutput;
+
+ udelay (10000);
+
+ return TRUE;
+}
+
+int eth_init (bd_t * bd)
+{
+ int ret;
+ int i;
+
+ p_mac = AT91C_BASE_EMAC;
+
+ /* PIO Disable Register */
+ *AT91C_PIOA_PDR = AT91C_PA16_EMDIO | AT91C_PA15_EMDC | AT91C_PA14_ERXER |
+ AT91C_PA13_ERX1 | AT91C_PA12_ERX0 | AT91C_PA11_ECRS_ECRSDV |
+ AT91C_PA10_ETX1 | AT91C_PA9_ETX0 | AT91C_PA8_ETXEN |
+ AT91C_PA7_ETXCK_EREFCK;
+
+#ifdef CONFIG_AT91C_USE_RMII
+ *AT91C_PIOB_PDR = AT91C_PB19_ERXCK;
+ *AT91C_PIOB_BSR = AT91C_PB19_ERXCK;
+#else
+ *AT91C_PIOB_PDR = AT91C_PB19_ERXCK | AT91C_PB18_ECOL | AT91C_PB17_ERXDV |
+ AT91C_PB16_ERX3 | AT91C_PB15_ERX2 | AT91C_PB14_ETXER |
+ AT91C_PB13_ETX3 | AT91C_PB12_ETX2;
+
+ /* Select B Register */
+ *AT91C_PIOB_BSR = AT91C_PB19_ERXCK | AT91C_PB18_ECOL |
+ AT91C_PB17_ERXDV | AT91C_PB16_ERX3 | AT91C_PB15_ERX2 |
+ AT91C_PB14_ETXER | AT91C_PB13_ETX3 | AT91C_PB12_ETX2;
+#endif
+
+ *AT91C_PMC_PCER = 1 << AT91C_ID_EMAC; /* Peripheral Clock Enable Register */
+
+ p_mac->EMAC_CFG |= AT91C_EMAC_CSR; /* Clear statistics */
+
+ /* Init Ehternet buffers */
+ for (i = 0; i < RBF_FRAMEMAX; i++) {
+ rbfdt[i].addr = (unsigned long)rbf_framebuf[i];
+ rbfdt[i].size = 0;
+ }
+ rbfdt[RBF_FRAMEMAX - 1].addr |= RBF_WRAP;
+ rbfp = &rbfdt[0];
+
+ p_mac->EMAC_SA2L = (bd->bi_enetaddr[3] << 24) | (bd->bi_enetaddr[2] << 16)
+ | (bd->bi_enetaddr[1] << 8) | (bd->bi_enetaddr[0]);
+ p_mac->EMAC_SA2H = (bd->bi_enetaddr[5] << 8) | (bd->bi_enetaddr[4]);
+
+ p_mac->EMAC_RBQP = (long) (&rbfdt[0]);
+ p_mac->EMAC_RSR &= ~(AT91C_EMAC_RSR_OVR | AT91C_EMAC_REC | AT91C_EMAC_BNA);
+
+ p_mac->EMAC_CFG = (p_mac->EMAC_CFG | AT91C_EMAC_CAF | AT91C_EMAC_NBC)
+ & ~AT91C_EMAC_CLK;
+
+#ifdef CONFIG_AT91C_USE_RMII
+ p_mac->EMAC_CFG |= AT91C_EMAC_RMII;
+#endif
+
+#if (AT91C_MASTER_CLOCK > 40000000)
+ /* MDIO clock must not exceed 2.5 MHz, so enable MCK divider */
+ p_mac->EMAC_CFG |= AT91C_EMAC_CLK_HCLK_64;
+#endif
+
+ p_mac->EMAC_CTL |= AT91C_EMAC_TE | AT91C_EMAC_RE;
+
+ at91rm9200_GetPhyInterface (& PhyOps);
+
+ if (!PhyOps.IsPhyConnected (p_mac))
+ printf ("PHY not connected!!\n\r");
+
+ /* MII management start from here */
+ if (!(p_mac->EMAC_SR & AT91C_EMAC_LINK)) {
+ if (!(ret = PhyOps.Init (p_mac))) {
+ printf ("MAC: error during MII initialization\n");
+ return 0;
+ }
+ } else {
+ printf ("No link\n\r");
+ return 0;
+ }
+
+ return 0;
+}
+
+int eth_send (volatile void *packet, int length)
+{
+ while (!(p_mac->EMAC_TSR & AT91C_EMAC_BNQ));
+ p_mac->EMAC_TAR = (long) packet;
+ p_mac->EMAC_TCR = length;
+ while (p_mac->EMAC_TCR & 0x7ff);
+ p_mac->EMAC_TSR |= AT91C_EMAC_COMP;
+ return 0;
+}
+
+int eth_rx (void)
+{
+ int size;
+
+ if (!(rbfp->addr & RBF_OWNER))
+ return 0;
+
+ size = rbfp->size & RBF_SIZE;
+ NetReceive ((volatile uchar *) (rbfp->addr & RBF_ADDR), size);
+
+ rbfp->addr &= ~RBF_OWNER;
+ if (rbfp->addr & RBF_WRAP)
+ rbfp = &rbfdt[0];
+ else
+ rbfp++;
+
+ p_mac->EMAC_RSR |= AT91C_EMAC_REC;
+
+ return size;
+}
+
+void eth_halt (void)
+{
+};
+
+#if defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII)
+int at91rm9200_miiphy_read(char *devname, unsigned char addr,
+ unsigned char reg, unsigned short * value)
+{
+ at91rm9200_EmacEnableMDIO (p_mac);
+ at91rm9200_EmacReadPhy (p_mac, reg, value);
+ at91rm9200_EmacDisableMDIO (p_mac);
+ return 0;
+}
+
+int at91rm9200_miiphy_write(char *devname, unsigned char addr,
+ unsigned char reg, unsigned short value)
+{
+ at91rm9200_EmacEnableMDIO (p_mac);
+ at91rm9200_EmacWritePhy (p_mac, reg, &value);
+ at91rm9200_EmacDisableMDIO (p_mac);
+ return 0;
+}
+
+#endif /* defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII) */
+
+int at91rm9200_miiphy_initialize(bd_t *bis)
+{
+#if defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII)
+ miiphy_register("at91rm9200phy", at91rm9200_miiphy_read, at91rm9200_miiphy_write);
+#endif
+ return 0;
+}
+
+#endif /* CONFIG_COMMANDS & CFG_CMD_NET */
+
+#endif /* CONFIG_DRIVER_ETHER */
diff --git a/cpu/arm920t/at91rm9200/i2c.c b/cpu/arm920t/at91rm9200/i2c.c
new file mode 100755
index 0000000..2565998
--- /dev/null
+++ b/cpu/arm920t/at91rm9200/i2c.c
@@ -0,0 +1,206 @@
+/*
+ * i2c Support for Atmel's AT91RM9200 Two-Wire Interface
+ *
+ * (c) Rick Bronson
+ *
+ * Borrowed heavily from original work by:
+ * Copyright (c) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
+ *
+ * Modified to work with u-boot by (C) 2004 Gary Jennejohn garyj@denx.de
+ *
+ * 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>
+
+#ifdef CONFIG_HARD_I2C
+
+#include <i2c.h>
+#include <asm/io.h>
+#include <asm/arch/hardware.h>
+
+#include <at91rm9200_i2c.h>
+
+/* define DEBUG */
+
+/*
+ * Poll the i2c status register until the specified bit is set.
+ * Returns 0 if timed out (100 msec)
+ */
+static short at91_poll_status(AT91PS_TWI twi, unsigned long bit) {
+ int loop_cntr = 10000;
+ do {
+ udelay(10);
+ } while (!(twi->TWI_SR & bit) && (--loop_cntr > 0));
+
+ return (loop_cntr > 0);
+}
+
+/*
+ * Generic i2c master transfer entrypoint
+ *
+ * rw == 1 means that this is a read
+ */
+static int
+at91_xfer(unsigned char chip, unsigned int addr, int alen,
+ unsigned char *buffer, int len, int rw)
+{
+ AT91PS_TWI twi = (AT91PS_TWI) AT91_TWI_BASE;
+ int length;
+ unsigned char *buf;
+ /* Set the TWI Master Mode Register */
+ twi->TWI_MMR = (chip << 16) | (alen << 8)
+ | ((rw == 1) ? AT91C_TWI_MREAD : 0);
+
+ /* Set TWI Internal Address Register with first messages data field */
+ if (alen > 0)
+ twi->TWI_IADR = addr;
+
+ length = len;
+ buf = buffer;
+ if (length && buf) { /* sanity check */
+ if (rw) {
+ twi->TWI_CR = AT91C_TWI_START;
+ while (length--) {
+ if (!length)
+ twi->TWI_CR = AT91C_TWI_STOP;
+ /* Wait until transfer is finished */
+ if (!at91_poll_status(twi, AT91C_TWI_RXRDY)) {
+ debug ("at91_i2c: timeout 1\n");
+ return 1;
+ }
+ *buf++ = twi->TWI_RHR;
+ }
+ if (!at91_poll_status(twi, AT91C_TWI_TXCOMP)) {
+ debug ("at91_i2c: timeout 2\n");
+ return 1;
+ }
+ } else {
+ twi->TWI_CR = AT91C_TWI_START;
+ while (length--) {
+ twi->TWI_THR = *buf++;
+ if (!length)
+ twi->TWI_CR = AT91C_TWI_STOP;
+ if (!at91_poll_status(twi, AT91C_TWI_TXRDY)) {
+ debug ("at91_i2c: timeout 3\n");
+ return 1;
+ }
+ }
+ /* Wait until transfer is finished */
+ if (!at91_poll_status(twi, AT91C_TWI_TXCOMP)) {
+ debug ("at91_i2c: timeout 4\n");
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+int
+i2c_probe(unsigned char chip)
+{
+ char buffer[1];
+
+ return at91_xfer(chip, 0, 0, buffer, 1, 1);
+}
+
+int
+i2c_read (unsigned char chip, unsigned int addr, int alen,
+ unsigned char *buffer, int len)
+{
+#ifdef CFG_I2C_EEPROM_ADDR_OVERFLOW
+ /* we only allow one address byte */
+ if (alen > 1)
+ return 1;
+ /* XXX assume an ATMEL AT24C16 */
+ if (alen == 1) {
+#if 0 /* EEPROM code already sets this correctly */
+ chip |= (addr >> 8) & 0xff;
+#endif
+ addr = addr & 0xff;
+ }
+#endif
+ return at91_xfer(chip, addr, alen, buffer, len, 1);
+}
+
+int
+i2c_write(unsigned char chip, unsigned int addr, int alen,
+ unsigned char *buffer, int len)
+{
+#ifdef CFG_I2C_EEPROM_ADDR_OVERFLOW
+ int i;
+ unsigned char *buf;
+
+ /* we only allow one address byte */
+ if (alen > 1)
+ return 1;
+ /* XXX assume an ATMEL AT24C16 */
+ if (alen == 1) {
+ buf = buffer;
+ /* do single byte writes */
+ for (i = 0; i < len; i++) {
+#if 0 /* EEPROM code already sets this correctly */
+ chip |= (addr >> 8) & 0xff;
+#endif
+ addr = addr & 0xff;
+ if (at91_xfer(chip, addr, alen, buf++, 1, 0))
+ return 1;
+ addr++;
+ }
+ return 0;
+ }
+#endif
+ return at91_xfer(chip, addr, alen, buffer, len, 0);
+}
+
+/*
+ * Main initialization routine
+ */
+void
+i2c_init(int speed, int slaveaddr)
+{
+ AT91PS_TWI twi = (AT91PS_TWI) AT91_TWI_BASE;
+
+ *AT91C_PIOA_PDR = AT91C_PA25_TWD | AT91C_PA26_TWCK;
+ *AT91C_PIOA_ASR = AT91C_PA25_TWD | AT91C_PA26_TWCK;
+ *AT91C_PIOA_MDER = AT91C_PA25_TWD | AT91C_PA26_TWCK;
+ *AT91C_PMC_PCER = 1 << AT91C_ID_TWI; /* enable peripheral clock */
+
+ twi->TWI_IDR = 0x3ff; /* Disable all interrupts */
+ twi->TWI_CR = AT91C_TWI_SWRST; /* Reset peripheral */
+ twi->TWI_CR = AT91C_TWI_MSEN | AT91C_TWI_SVDIS; /* Set Master mode */
+
+ /* Here, CKDIV = 1 and CHDIV=CLDIV ==> CLDIV = CHDIV = 1/4*((Fmclk/FTWI) -6) */
+ twi->TWI_CWGR = AT91C_TWI_CKDIV1 | AT91C_TWI_CLDIV3 | (AT91C_TWI_CLDIV3 << 8);
+
+ debug ("Found AT91 i2c\n");
+ return;
+}
+
+uchar i2c_reg_read(uchar i2c_addr, uchar reg)
+{
+ char buf;
+
+ i2c_read(i2c_addr, reg, 1, &buf, 1);
+
+ return(buf);
+}
+
+void i2c_reg_write(uchar i2c_addr, uchar reg, uchar val)
+{
+ i2c_write(i2c_addr, reg, 1, &val, 1);
+}
+
+#endif /* CONFIG_HARD_I2C */
diff --git a/cpu/arm920t/at91rm9200/interrupts.c b/cpu/arm920t/at91rm9200/interrupts.c
new file mode 100755
index 0000000..1054602
--- /dev/null
+++ b/cpu/arm920t/at91rm9200/interrupts.c
@@ -0,0 +1,210 @@
+/*
+ * (C) Copyright 2002
+ * Lineo, Inc. <www.lineo.com>
+ * Bernhard Kuhn <bkuhn@lineo.com>
+ *
+ * (C) Copyright 2002
+ * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Marius Groeger <mgroeger@sysgo.de>
+ *
+ * (C) Copyright 2002
+ * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Alex Zuepke <azu@sysgo.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 <common.h>
+/*#include <asm/io.h>*/
+#include <asm/arch/hardware.h>
+/*#include <asm/proc/ptrace.h>*/
+
+/* the number of clocks per CFG_HZ */
+#define TIMER_LOAD_VAL (CFG_HZ_CLOCK/CFG_HZ)
+
+/* macro to read the 16 bit timer */
+#define READ_TIMER (tmr->TC_CV & 0x0000ffff)
+AT91PS_TC tmr;
+
+static ulong timestamp;
+static ulong lastinc;
+
+int interrupt_init (void)
+{
+ tmr = AT91C_BASE_TC0;
+
+ /* enables TC1.0 clock */
+ *AT91C_PMC_PCER = 1 << AT91C_ID_TC0; /* enable clock */
+
+ *AT91C_TCB0_BCR = 0;
+ *AT91C_TCB0_BMR = AT91C_TCB_TC0XC0S_NONE | AT91C_TCB_TC1XC1S_NONE | AT91C_TCB_TC2XC2S_NONE;
+ tmr->TC_CCR = AT91C_TC_CLKDIS;
+#define AT91C_TC_CMR_CPCTRG (1 << 14)
+ /* set to MCLK/2 and restart the timer when the vlaue in TC_RC is reached */
+ tmr->TC_CMR = AT91C_TC_TIMER_DIV1_CLOCK | AT91C_TC_CMR_CPCTRG;
+
+ tmr->TC_IDR = ~0ul;
+ tmr->TC_RC = TIMER_LOAD_VAL;
+ lastinc = 0;
+ tmr->TC_CCR = AT91C_TC_SWTRG | AT91C_TC_CLKEN;
+ timestamp = 0;
+
+ return (0);
+}
+
+/*
+ * timer without interrupts
+ */
+
+void reset_timer (void)
+{
+ reset_timer_masked ();
+}
+
+ulong get_timer (ulong base)
+{
+ return get_timer_masked () - base;
+}
+
+void set_timer (ulong t)
+{
+ timestamp = t;
+}
+
+void udelay (unsigned long usec)
+{
+ udelay_masked(usec);
+}
+
+void reset_timer_masked (void)
+{
+ /* reset time */
+ lastinc = READ_TIMER;
+ timestamp = 0;
+}
+
+ulong get_timer_raw (void)
+{
+ ulong now = READ_TIMER;
+
+ if (now >= lastinc) {
+ /* normal mode */
+ timestamp += now - lastinc;
+ } else {
+ /* we have an overflow ... */
+ timestamp += now + TIMER_LOAD_VAL - lastinc;
+ }
+ lastinc = now;
+
+ return timestamp;
+}
+
+ulong get_timer_masked (void)
+{
+ return get_timer_raw()/TIMER_LOAD_VAL;
+}
+
+void udelay_masked (unsigned long usec)
+{
+ ulong tmo;
+ ulong endtime;
+ signed long diff;
+
+ tmo = CFG_HZ_CLOCK / 1000;
+ tmo *= usec;
+ tmo /= 1000;
+
+ endtime = get_timer_raw () + tmo;
+
+ do {
+ ulong now = get_timer_raw ();
+ diff = endtime - now;
+ } while (diff >= 0);
+}
+
+/*
+ * 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.
+ */
+ulong get_tbclk (void)
+{
+ ulong tbclk;
+
+ tbclk = CFG_HZ;
+ return tbclk;
+}
+
+/*
+ * Reset the cpu by setting up the watchdog timer and let him time out
+ * or toggle a GPIO pin on the AT91RM9200DK board
+ */
+void reset_cpu (ulong ignored)
+{
+
+#ifdef CONFIG_DBGU
+ AT91PS_USART us = (AT91PS_USART) AT91C_BASE_DBGU;
+#endif
+#ifdef CONFIG_USART0
+ AT91PS_USART us = AT91C_BASE_US0;
+#endif
+#ifdef CONFIG_USART1
+ AT91PS_USART us = AT91C_BASE_US1;
+#endif
+#ifdef CONFIG_AT91RM9200DK
+ AT91PS_PIO pio = AT91C_BASE_PIOA;
+#endif
+
+ /*shutdown the console to avoid strange chars during reset */
+ us->US_CR = (AT91C_US_RSTRX | AT91C_US_RSTTX);
+
+#ifdef CONFIG_AT91RM9200DK
+ /* Clear PA19 to trigger the hard reset */
+ pio->PIO_CODR = 0x00080000;
+ pio->PIO_OER = 0x00080000;
+ pio->PIO_PER = 0x00080000;
+#endif
+
+ /* this is the way Linux does it */
+
+ /* FIXME:
+ * These defines should be moved into
+ * include/asm-arm/arch-at91rm9200/AT91RM9200.h
+ * as soon as the whitespace fix gets applied.
+ */
+ #define AT91C_ST_RSTEN (0x1 << 16)
+ #define AT91C_ST_EXTEN (0x1 << 17)
+ #define AT91C_ST_WDRST (0x1 << 0)
+ #define ST_WDMR *((unsigned long *)0xfffffd08) /* watchdog mode register */
+ #define ST_CR *((unsigned long *)0xfffffd00) /* system clock control register */
+
+ ST_WDMR = AT91C_ST_RSTEN | AT91C_ST_EXTEN | 1 ;
+ ST_CR = AT91C_ST_WDRST;
+
+ while (1);
+ /* Never reached */
+}
diff --git a/cpu/arm920t/at91rm9200/lowlevel_init.S b/cpu/arm920t/at91rm9200/lowlevel_init.S
new file mode 100755
index 0000000..1902bd0
--- /dev/null
+++ b/cpu/arm920t/at91rm9200/lowlevel_init.S
@@ -0,0 +1,205 @@
+/*
+ * Memory Setup stuff - taken from blob memsetup.S
+ *
+ * Copyright (C) 1999 2000 2001 Erik Mouw (J.A.K.Mouw@its.tudelft.nl) and
+ * Jan-Derk Bakker (J.D.Bakker@its.tudelft.nl)
+ *
+ * Modified for the at91rm9200dk board by
+ * (C) Copyright 2004
+ * Gary Jennejohn, DENX Software Engineering, <garyj@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
+ */
+
+#include <config.h>
+#include <version.h>
+
+#ifndef CONFIG_SKIP_LOWLEVEL_INIT
+/*
+ * some parameters for the board
+ *
+ * This is based on rm9200dk.cfg for the BDI2000 from ABATRON which in
+ * turn is based on the boot.bin code from ATMEL
+ *
+ */
+
+/* flash */
+#define MC_PUIA 0xFFFFFF10
+#define MC_PUP 0xFFFFFF50
+#define MC_PUER 0xFFFFFF54
+#define MC_ASR 0xFFFFFF04
+#define MC_AASR 0xFFFFFF08
+#define EBI_CFGR 0xFFFFFF64
+#define SMC2_CSR 0xFFFFFF70
+
+/* clocks */
+#define PLLAR 0xFFFFFC28
+#define PLLBR 0xFFFFFC2C
+#define MCKR 0xFFFFFC30
+
+#define AT91C_BASE_CKGR 0xFFFFFC20
+#define CKGR_MOR 0
+
+/* sdram */
+#define PIOC_ASR 0xFFFFF870
+#define PIOC_BSR 0xFFFFF874
+#define PIOC_PDR 0xFFFFF804
+#define EBI_CSA 0xFFFFFF60
+#define SDRC_CR 0xFFFFFF98
+#define SDRC_MR 0xFFFFFF90
+#define SDRC_TR 0xFFFFFF94
+
+
+_MTEXT_BASE:
+#undef START_FROM_MEM
+#ifdef START_FROM_MEM
+ .word TEXT_BASE-PHYS_FLASH_1
+#else
+ .word TEXT_BASE
+#endif
+
+.globl lowlevel_init
+lowlevel_init:
+ /* Get the CKGR Base Address */
+ ldr r1, =AT91C_BASE_CKGR
+ /* Main oscillator Enable register */
+#ifdef CFG_USE_MAIN_OSCILLATOR
+ ldr r0, =0x0000FF01 /* Enable main oscillator, OSCOUNT = 0xFF */
+#else
+ ldr r0, =0x0000FF00 /* Disable main oscillator, OSCOUNT = 0xFF */
+#endif
+ str r0, [r1, #CKGR_MOR]
+ /* Add loop to compensate Main Oscillator startup time */
+ ldr r0, =0x00000010
+LoopOsc:
+ subs r0, r0, #1
+ bhi LoopOsc
+
+ /* memory control configuration */
+ /* this isn't very elegant, but what the heck */
+ ldr r0, =SMRDATA
+ ldr r1, _MTEXT_BASE
+ sub r0, r0, r1
+ add r2, r0, #80
+0:
+ /* the address */
+ ldr r1, [r0], #4
+ /* the value */
+ ldr r3, [r0], #4
+ str r3, [r1]
+ cmp r2, r0
+ bne 0b
+ /* delay - this is all done by guess */
+ ldr r0, =0x00010000
+1:
+ subs r0, r0, #1
+ bhi 1b
+ ldr r0, =SMRDATA1
+ ldr r1, _MTEXT_BASE
+ sub r0, r0, r1
+ add r2, r0, #176
+2:
+ /* the address */
+ ldr r1, [r0], #4
+ /* the value */
+ ldr r3, [r0], #4
+ str r3, [r1]
+ cmp r2, r0
+ bne 2b
+
+ /* switch from FastBus to Asynchronous clock mode */
+ mrc p15, 0, r0, c1, c0, 0
+ orr r0, r0, #0xC0000000 @ set bit 31 (iA) and 30 (nF)
+ mcr p15, 0, r0, c1, c0, 0
+
+ /* everything is fine now */
+ mov pc, lr
+
+ .ltorg
+
+SMRDATA:
+ .word MC_PUIA
+ .word MC_PUIA_VAL
+ .word MC_PUP
+ .word MC_PUP_VAL
+ .word MC_PUER
+ .word MC_PUER_VAL
+ .word MC_ASR
+ .word MC_ASR_VAL
+ .word MC_AASR
+ .word MC_AASR_VAL
+ .word EBI_CFGR
+ .word EBI_CFGR_VAL
+ .word SMC2_CSR
+ .word SMC2_CSR_VAL
+ .word PLLAR
+ .word PLLAR_VAL
+ .word PLLBR
+ .word PLLBR_VAL
+ .word MCKR
+ .word MCKR_VAL
+ /* SMRDATA is 80 bytes long */
+ /* here there's a delay of 100 */
+SMRDATA1:
+ .word PIOC_ASR
+ .word PIOC_ASR_VAL
+ .word PIOC_BSR
+ .word PIOC_BSR_VAL
+ .word PIOC_PDR
+ .word PIOC_PDR_VAL
+ .word EBI_CSA
+ .word EBI_CSA_VAL
+ .word SDRC_CR
+ .word SDRC_CR_VAL
+ .word SDRC_MR
+ .word SDRC_MR_VAL
+ .word SDRAM
+ .word SDRAM_VAL
+ .word SDRC_MR
+ .word SDRC_MR_VAL1
+ .word SDRAM
+ .word SDRAM_VAL
+ .word SDRAM
+ .word SDRAM_VAL
+ .word SDRAM
+ .word SDRAM_VAL
+ .word SDRAM
+ .word SDRAM_VAL
+ .word SDRAM
+ .word SDRAM_VAL
+ .word SDRAM
+ .word SDRAM_VAL
+ .word SDRAM
+ .word SDRAM_VAL
+ .word SDRAM
+ .word SDRAM_VAL
+ .word SDRC_MR
+ .word SDRC_MR_VAL2
+ .word SDRAM1
+ .word SDRAM_VAL
+ .word SDRC_TR
+ .word SDRC_TR_VAL
+ .word SDRAM
+ .word SDRAM_VAL
+ .word SDRC_MR
+ .word SDRC_MR_VAL3
+ .word SDRAM
+ .word SDRAM_VAL
+ /* SMRDATA1 is 176 bytes long */
+#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
diff --git a/cpu/arm920t/at91rm9200/lxt972.c b/cpu/arm920t/at91rm9200/lxt972.c
new file mode 100755
index 0000000..f12c59c
--- /dev/null
+++ b/cpu/arm920t/at91rm9200/lxt972.c
@@ -0,0 +1,191 @@
+/*
+ *
+ * (C) Copyright 2003
+ * Author : Hamid Ikdoumi (Atmel)
+ *
+ * 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
+ */
+
+/*
+ * Adapted for KwikByte KB920x board: 22APR2005
+ */
+
+#include <common.h>
+#include <at91rm9200_net.h>
+#include <net.h>
+#include <lxt971a.h>
+
+#ifdef CONFIG_DRIVER_ETHER
+
+#if (CONFIG_COMMANDS & CFG_CMD_NET)
+
+/*
+ * Name:
+ * lxt972_IsPhyConnected
+ * Description:
+ * Reads the 2 PHY ID registers
+ * Arguments:
+ * p_mac - pointer to AT91S_EMAC struct
+ * Return value:
+ * TRUE - if id read successfully
+ * FALSE- if error
+ */
+unsigned int lxt972_IsPhyConnected (AT91PS_EMAC p_mac)
+{
+ unsigned short Id1, Id2;
+
+ at91rm9200_EmacEnableMDIO (p_mac);
+ at91rm9200_EmacReadPhy (p_mac, PHY_COMMON_ID1, &Id1);
+ at91rm9200_EmacReadPhy (p_mac, PHY_COMMON_ID2, &Id2);
+ at91rm9200_EmacDisableMDIO (p_mac);
+
+ if ((Id1 == (0x0013)) && ((Id2 & 0xFFF0) == 0x78E0))
+ return TRUE;
+
+ return FALSE;
+}
+
+/*
+ * Name:
+ * lxt972_GetLinkSpeed
+ * Description:
+ * Link parallel detection status of MAC is checked and set in the
+ * MAC configuration registers
+ * Arguments:
+ * p_mac - pointer to MAC
+ * Return value:
+ * TRUE - if link status set succesfully
+ * FALSE - if link status not set
+ */
+UCHAR lxt972_GetLinkSpeed (AT91PS_EMAC p_mac)
+{
+ unsigned short stat1;
+
+ if (!at91rm9200_EmacReadPhy (p_mac, PHY_LXT971_STAT2, &stat1))
+ return FALSE;
+
+ if (!(stat1 & PHY_LXT971_STAT2_LINK)) /* link status up? */
+ return FALSE;
+
+ if (stat1 & PHY_LXT971_STAT2_100BTX) {
+
+ if (stat1 & PHY_LXT971_STAT2_DUPLEX_MODE) {
+
+ /*set Emac for 100BaseTX and Full Duplex */
+ p_mac->EMAC_CFG |= AT91C_EMAC_SPD | AT91C_EMAC_FD;
+ } else {
+
+ /*set Emac for 100BaseTX and Half Duplex */
+ p_mac->EMAC_CFG = (p_mac->EMAC_CFG &
+ ~(AT91C_EMAC_SPD | AT91C_EMAC_FD))
+ | AT91C_EMAC_SPD;
+ }
+
+ return TRUE;
+
+ } else {
+
+ if (stat1 & PHY_LXT971_STAT2_DUPLEX_MODE) {
+
+ /*set MII for 10BaseT and Full Duplex */
+ p_mac->EMAC_CFG = (p_mac->EMAC_CFG &
+ ~(AT91C_EMAC_SPD | AT91C_EMAC_FD))
+ | AT91C_EMAC_FD;
+ } else {
+
+ /*set MII for 10BaseT and Half Duplex */
+ p_mac->EMAC_CFG &= ~(AT91C_EMAC_SPD | AT91C_EMAC_FD);
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/*
+ * Name:
+ * lxt972_InitPhy
+ * Description:
+ * MAC starts checking its link by using parallel detection and
+ * Autonegotiation and the same is set in the MAC configuration registers
+ * Arguments:
+ * p_mac - pointer to struct AT91S_EMAC
+ * Return value:
+ * TRUE - if link status set succesfully
+ * FALSE - if link status not set
+ */
+UCHAR lxt972_InitPhy (AT91PS_EMAC p_mac)
+{
+ UCHAR ret = TRUE;
+
+ at91rm9200_EmacEnableMDIO (p_mac);
+
+ if (!lxt972_GetLinkSpeed (p_mac)) {
+ /* Try another time */
+ ret = lxt972_GetLinkSpeed (p_mac);
+ }
+
+ /* Disable PHY Interrupts */
+ at91rm9200_EmacWritePhy (p_mac, PHY_LXT971_INT_ENABLE, 0);
+
+ at91rm9200_EmacDisableMDIO (p_mac);
+
+ return (ret);
+}
+
+
+/*
+ * Name:
+ * lxt972_AutoNegotiate
+ * Description:
+ * MAC Autonegotiates with the partner status of same is set in the
+ * MAC configuration registers
+ * Arguments:
+ * dev - pointer to struct net_device
+ * Return value:
+ * TRUE - if link status set successfully
+ * FALSE - if link status not set
+ */
+UCHAR lxt972_AutoNegotiate (AT91PS_EMAC p_mac, int *status)
+{
+ unsigned short value;
+
+ /* Set lxt972 control register */
+ if (!at91rm9200_EmacReadPhy (p_mac, PHY_COMMON_CTRL, &value))
+ return FALSE;
+
+ /* Restart Auto_negotiation */
+ value |= PHY_COMMON_CTRL_RES_AUTO;
+ if (!at91rm9200_EmacWritePhy (p_mac, PHY_COMMON_CTRL, &value))
+ return FALSE;
+
+ /*check AutoNegotiate complete */
+ udelay (10000);
+ at91rm9200_EmacReadPhy (p_mac, PHY_COMMON_STAT, &value);
+ if (!(value & PHY_COMMON_STAT_AN_COMP))
+ return FALSE;
+
+ return (lxt972_GetLinkSpeed (p_mac));
+}
+
+#endif /* CONFIG_COMMANDS & CFG_CMD_NET */
+
+#endif /* CONFIG_DRIVER_ETHER */
diff --git a/cpu/arm920t/at91rm9200/serial.c b/cpu/arm920t/at91rm9200/serial.c
new file mode 100755
index 0000000..a281932
--- /dev/null
+++ b/cpu/arm920t/at91rm9200/serial.c
@@ -0,0 +1,112 @@
+/*
+ * (C) Copyright 2002
+ * Lineo, Inc <www.lineo.com>
+ * Bernhard Kuhn <bkuhn@lineo.com>
+ *
+ * (C) Copyright 2002
+ * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Marius Groeger <mgroeger@sysgo.de>
+ *
+ * (C) Copyright 2002
+ * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Alex Zuepke <azu@sysgo.de>
+ *
+ * Copyright (C) 1999 2000 2001 Erik Mouw (J.A.K.Mouw@its.tudelft.nl)
+ *
+ * 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/hardware.h>
+
+#if !defined(CONFIG_DBGU) && !defined(CONFIG_USART0) && !defined(CONFIG_USART1)
+#error must define one of CONFIG_DBGU or CONFIG_USART0 or CONFIG_USART1
+#endif
+
+/* ggi thunder */
+#ifdef CONFIG_DBGU
+AT91PS_USART us = (AT91PS_USART) AT91C_BASE_DBGU;
+#endif
+#ifdef CONFIG_USART0
+AT91PS_USART us = (AT91PS_USART) AT91C_BASE_US0;
+#endif
+#ifdef CONFIG_USART1
+AT91PS_USART us = (AT91PS_USART) AT91C_BASE_US1;
+#endif
+
+void serial_setbrg (void)
+{
+ DECLARE_GLOBAL_DATA_PTR;
+ int baudrate;
+
+ if ((baudrate = gd->baudrate) <= 0)
+ baudrate = CONFIG_BAUDRATE;
+ /* MASTER_CLOCK/(16 * baudrate) */
+ us->US_BRGR = (AT91C_MASTER_CLOCK >> 4) / (unsigned)baudrate;
+}
+
+int serial_init (void)
+{
+ /* make any port initializations specific to this port */
+#ifdef CONFIG_DBGU
+ *AT91C_PIOA_PDR = AT91C_PA31_DTXD | AT91C_PA30_DRXD; /* PA 31 & 30 */
+ *AT91C_PMC_PCER = 1 << AT91C_ID_SYS; /* enable clock */
+#endif
+#ifdef CONFIG_USART0
+ *AT91C_PIOA_PDR = AT91C_PA17_TXD0 | AT91C_PA18_RXD0;
+ *AT91C_PMC_PCER |= 1 << AT91C_ID_USART0; /* enable clock */
+#endif
+#ifdef CONFIG_USART1
+ *AT91C_PIOB_PDR = AT91C_PB21_TXD1 | AT91C_PB20_RXD1;
+ *AT91C_PMC_PCER |= 1 << AT91C_ID_USART1; /* enable clock */
+#endif
+ serial_setbrg ();
+
+ us->US_CR = AT91C_US_RSTRX | AT91C_US_RSTTX;
+ us->US_CR = AT91C_US_RXEN | AT91C_US_TXEN;
+ us->US_MR =
+ (AT91C_US_CLKS_CLOCK | AT91C_US_CHRL_8_BITS |
+ AT91C_US_PAR_NONE | AT91C_US_NBSTOP_1_BIT);
+ us->US_IMR = ~0ul;
+ return (0);
+}
+
+void serial_putc (const char c)
+{
+ if (c == '\n')
+ serial_putc ('\r');
+ while ((us->US_CSR & AT91C_US_TXRDY) == 0);
+ us->US_THR = c;
+}
+
+void serial_puts (const char *s)
+{
+ while (*s) {
+ serial_putc (*s++);
+ }
+}
+
+int serial_getc (void)
+{
+ while ((us->US_CSR & AT91C_US_RXRDY) == 0);
+ return us->US_RHR;
+}
+
+int serial_tstc (void)
+{
+ return ((us->US_CSR & AT91C_US_RXRDY) == AT91C_US_RXRDY);
+}
diff --git a/cpu/arm920t/at91rm9200/usb_ohci.c b/cpu/arm920t/at91rm9200/usb_ohci.c
new file mode 100755
index 0000000..5b2c56c
--- /dev/null
+++ b/cpu/arm920t/at91rm9200/usb_ohci.c
@@ -0,0 +1,1635 @@
+/*
+ * URB OHCI HCD (Host Controller Driver) for USB on the AT91RM9200.
+ *
+ * (C) Copyright 2003
+ * Gary Jennejohn, DENX Software Engineering <gj@denx.de>
+ *
+ * Note: Much of this code has been derived from Linux 2.4
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2002 David Brownell
+ *
+ * Modified for the MP2USB by (C) Copyright 2005 Eric Benard
+ * ebenard@eukrea.com - based on s3c24x0's driver
+ *
+ * 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
+ *
+ */
+/*
+ * IMPORTANT NOTES
+ * 1 - you MUST define LITTLEENDIAN in the configuration file for the
+ * board or this driver will NOT work!
+ * 2 - this driver is intended for use with USB Mass Storage Devices
+ * (BBB) ONLY. There is NO support for Interrupt or Isochronous pipes!
+ * 3 - when running on a PQFP208 AT91RM9200, define CONFIG_AT91C_PQFP_UHPBUG
+ * to activate workaround for bug #41 or this driver will NOT work!
+ */
+
+#include <common.h>
+/* #include <pci.h> no PCI on the S3C24X0 */
+
+#ifdef CONFIG_USB_OHCI
+
+#include <asm/arch/hardware.h>
+
+#include <malloc.h>
+#include <usb.h>
+#include "usb_ohci.h"
+
+#define OHCI_USE_NPS /* force NoPowerSwitching mode */
+#undef OHCI_VERBOSE_DEBUG /* not always helpful */
+
+/* For initializing controller (mask in an HCFS mode too) */
+#define OHCI_CONTROL_INIT \
+ (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE
+
+#define readl(a) (*((vu_long *)(a)))
+#define writel(a, b) (*((vu_long *)(b)) = ((vu_long)a))
+
+#define min_t(type,x,y) ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
+
+#undef DEBUG
+#ifdef DEBUG
+#define dbg(format, arg...) printf("DEBUG: " format "\n", ## arg)
+#else
+#define dbg(format, arg...) do {} while(0)
+#endif /* DEBUG */
+#define err(format, arg...) printf("ERROR: " format "\n", ## arg)
+#undef SHOW_INFO
+#ifdef SHOW_INFO
+#define info(format, arg...) printf("INFO: " format "\n", ## arg)
+#else
+#define info(format, arg...) do {} while(0)
+#endif
+
+#define m16_swap(x) swap_16(x)
+#define m32_swap(x) swap_32(x)
+
+/* global ohci_t */
+static ohci_t gohci;
+/* this must be aligned to a 256 byte boundary */
+struct ohci_hcca ghcca[1];
+/* a pointer to the aligned storage */
+struct ohci_hcca *phcca;
+/* this allocates EDs for all possible endpoints */
+struct ohci_device ohci_dev;
+/* urb_priv */
+urb_priv_t urb_priv;
+/* RHSC flag */
+int got_rhsc;
+/* device which was disconnected */
+struct usb_device *devgone;
+
+/*-------------------------------------------------------------------------*/
+
+/* AMD-756 (D2 rev) reports corrupt register contents in some cases.
+ * The erratum (#4) description is incorrect. AMD's workaround waits
+ * till some bits (mostly reserved) are clear; ok for all revs.
+ */
+#define OHCI_QUIRK_AMD756 0xabcd
+#define read_roothub(hc, register, mask) ({ \
+ u32 temp = readl (&hc->regs->roothub.register); \
+ if (hc->flags & OHCI_QUIRK_AMD756) \
+ while (temp & mask) \
+ temp = readl (&hc->regs->roothub.register); \
+ temp; })
+
+static u32 roothub_a (struct ohci *hc)
+ { return read_roothub (hc, a, 0xfc0fe000); }
+static inline u32 roothub_b (struct ohci *hc)
+ { return readl (&hc->regs->roothub.b); }
+static inline u32 roothub_status (struct ohci *hc)
+ { return readl (&hc->regs->roothub.status); }
+static u32 roothub_portstatus (struct ohci *hc, int i)
+ { return read_roothub (hc, portstatus [i], 0xffe0fce0); }
+
+
+/* forward declaration */
+static int hc_interrupt (void);
+static void
+td_submit_job (struct usb_device * dev, unsigned long pipe, void * buffer,
+ int transfer_len, struct devrequest * setup, urb_priv_t * urb, int interval);
+
+/*-------------------------------------------------------------------------*
+ * URB support functions
+ *-------------------------------------------------------------------------*/
+
+/* free HCD-private data associated with this URB */
+
+static void urb_free_priv (urb_priv_t * urb)
+{
+ int i;
+ int last;
+ struct td * td;
+
+ last = urb->length - 1;
+ if (last >= 0) {
+ for (i = 0; i <= last; i++) {
+ td = urb->td[i];
+ if (td) {
+ td->usb_dev = NULL;
+ urb->td[i] = NULL;
+ }
+ }
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+static int sohci_get_current_frame_number (struct usb_device * dev);
+
+/* debug| print the main components of an URB
+ * small: 0) header + data packets 1) just header */
+
+static void pkt_print (struct usb_device * dev, unsigned long pipe, void * buffer,
+ int transfer_len, struct devrequest * setup, char * str, int small)
+{
+ urb_priv_t * purb = &urb_priv;
+
+ dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,len:%d/%d stat:%#lx",
+ str,
+ sohci_get_current_frame_number (dev),
+ usb_pipedevice (pipe),
+ usb_pipeendpoint (pipe),
+ usb_pipeout (pipe)? 'O': 'I',
+ usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"):
+ (usb_pipecontrol (pipe)? "CTRL": "BULK"),
+ purb->actual_length,
+ transfer_len, dev->status);
+#ifdef OHCI_VERBOSE_DEBUG
+ if (!small) {
+ int i, len;
+
+ if (usb_pipecontrol (pipe)) {
+ printf (__FILE__ ": cmd(8):");
+ for (i = 0; i < 8 ; i++)
+ printf (" %02x", ((__u8 *) setup) [i]);
+ printf ("\n");
+ }
+ if (transfer_len > 0 && buffer) {
+ printf (__FILE__ ": data(%d/%d):",
+ purb->actual_length,
+ transfer_len);
+ len = usb_pipeout (pipe)?
+ transfer_len: purb->actual_length;
+ for (i = 0; i < 16 && i < len; i++)
+ printf (" %02x", ((__u8 *) buffer) [i]);
+ printf ("%s\n", i < len? "...": "");
+ }
+ }
+#endif
+}
+
+/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/
+void ep_print_int_eds (ohci_t *ohci, char * str) {
+ int i, j;
+ __u32 * ed_p;
+ for (i= 0; i < 32; i++) {
+ j = 5;
+ ed_p = &(ohci->hcca->int_table [i]);
+ if (*ed_p == 0)
+ continue;
+ printf (__FILE__ ": %s branch int %2d(%2x):", str, i, i);
+ while (*ed_p != 0 && j--) {
+ ed_t *ed = (ed_t *)m32_swap(ed_p);
+ printf (" ed: %4x;", ed->hwINFO);
+ ed_p = &ed->hwNextED;
+ }
+ printf ("\n");
+ }
+}
+
+static void ohci_dump_intr_mask (char *label, __u32 mask)
+{
+ dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s",
+ label,
+ mask,
+ (mask & OHCI_INTR_MIE) ? " MIE" : "",
+ (mask & OHCI_INTR_OC) ? " OC" : "",
+ (mask & OHCI_INTR_RHSC) ? " RHSC" : "",
+ (mask & OHCI_INTR_FNO) ? " FNO" : "",
+ (mask & OHCI_INTR_UE) ? " UE" : "",
+ (mask & OHCI_INTR_RD) ? " RD" : "",
+ (mask & OHCI_INTR_SF) ? " SF" : "",
+ (mask & OHCI_INTR_WDH) ? " WDH" : "",
+ (mask & OHCI_INTR_SO) ? " SO" : ""
+ );
+}
+
+static void maybe_print_eds (char *label, __u32 value)
+{
+ ed_t *edp = (ed_t *)value;
+
+ if (value) {
+ dbg ("%s %08x", label, value);
+ dbg ("%08x", edp->hwINFO);
+ dbg ("%08x", edp->hwTailP);
+ dbg ("%08x", edp->hwHeadP);
+ dbg ("%08x", edp->hwNextED);
+ }
+}
+
+static char * hcfs2string (int state)
+{
+ switch (state) {
+ case OHCI_USB_RESET: return "reset";
+ case OHCI_USB_RESUME: return "resume";
+ case OHCI_USB_OPER: return "operational";
+ case OHCI_USB_SUSPEND: return "suspend";
+ }
+ return "?";
+}
+
+/* dump control and status registers */
+static void ohci_dump_status (ohci_t *controller)
+{
+ struct ohci_regs *regs = controller->regs;
+ __u32 temp;
+
+ temp = readl (&regs->revision) & 0xff;
+ if (temp != 0x10)
+ dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f));
+
+ temp = readl (&regs->control);
+ dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp,
+ (temp & OHCI_CTRL_RWE) ? " RWE" : "",
+ (temp & OHCI_CTRL_RWC) ? " RWC" : "",
+ (temp & OHCI_CTRL_IR) ? " IR" : "",
+ hcfs2string (temp & OHCI_CTRL_HCFS),
+ (temp & OHCI_CTRL_BLE) ? " BLE" : "",
+ (temp & OHCI_CTRL_CLE) ? " CLE" : "",
+ (temp & OHCI_CTRL_IE) ? " IE" : "",
+ (temp & OHCI_CTRL_PLE) ? " PLE" : "",
+ temp & OHCI_CTRL_CBSR
+ );
+
+ temp = readl (&regs->cmdstatus);
+ dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp,
+ (temp & OHCI_SOC) >> 16,
+ (temp & OHCI_OCR) ? " OCR" : "",
+ (temp & OHCI_BLF) ? " BLF" : "",
+ (temp & OHCI_CLF) ? " CLF" : "",
+ (temp & OHCI_HCR) ? " HCR" : ""
+ );
+
+ ohci_dump_intr_mask ("intrstatus", readl (&regs->intrstatus));
+ ohci_dump_intr_mask ("intrenable", readl (&regs->intrenable));
+
+ maybe_print_eds ("ed_periodcurrent", readl (&regs->ed_periodcurrent));
+
+ maybe_print_eds ("ed_controlhead", readl (&regs->ed_controlhead));
+ maybe_print_eds ("ed_controlcurrent", readl (&regs->ed_controlcurrent));
+
+ maybe_print_eds ("ed_bulkhead", readl (&regs->ed_bulkhead));
+ maybe_print_eds ("ed_bulkcurrent", readl (&regs->ed_bulkcurrent));
+
+ maybe_print_eds ("donehead", readl (&regs->donehead));
+}
+
+static void ohci_dump_roothub (ohci_t *controller, int verbose)
+{
+ __u32 temp, ndp, i;
+
+ temp = roothub_a (controller);
+ ndp = (temp & RH_A_NDP);
+#ifdef CONFIG_AT91C_PQFP_UHPBUG
+ ndp = (ndp == 2) ? 1:0;
+#endif
+ if (verbose) {
+ dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp,
+ ((temp & RH_A_POTPGT) >> 24) & 0xff,
+ (temp & RH_A_NOCP) ? " NOCP" : "",
+ (temp & RH_A_OCPM) ? " OCPM" : "",
+ (temp & RH_A_DT) ? " DT" : "",
+ (temp & RH_A_NPS) ? " NPS" : "",
+ (temp & RH_A_PSM) ? " PSM" : "",
+ ndp
+ );
+ temp = roothub_b (controller);
+ dbg ("roothub.b: %08x PPCM=%04x DR=%04x",
+ temp,
+ (temp & RH_B_PPCM) >> 16,
+ (temp & RH_B_DR)
+ );
+ temp = roothub_status (controller);
+ dbg ("roothub.status: %08x%s%s%s%s%s%s",
+ temp,
+ (temp & RH_HS_CRWE) ? " CRWE" : "",
+ (temp & RH_HS_OCIC) ? " OCIC" : "",
+ (temp & RH_HS_LPSC) ? " LPSC" : "",
+ (temp & RH_HS_DRWE) ? " DRWE" : "",
+ (temp & RH_HS_OCI) ? " OCI" : "",
+ (temp & RH_HS_LPS) ? " LPS" : ""
+ );
+ }
+
+ for (i = 0; i < ndp; i++) {
+ temp = roothub_portstatus (controller, i);
+ dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s",
+ i,
+ temp,
+ (temp & RH_PS_PRSC) ? " PRSC" : "",
+ (temp & RH_PS_OCIC) ? " OCIC" : "",
+ (temp & RH_PS_PSSC) ? " PSSC" : "",
+ (temp & RH_PS_PESC) ? " PESC" : "",
+ (temp & RH_PS_CSC) ? " CSC" : "",
+
+ (temp & RH_PS_LSDA) ? " LSDA" : "",
+ (temp & RH_PS_PPS) ? " PPS" : "",
+ (temp & RH_PS_PRS) ? " PRS" : "",
+ (temp & RH_PS_POCI) ? " POCI" : "",
+ (temp & RH_PS_PSS) ? " PSS" : "",
+
+ (temp & RH_PS_PES) ? " PES" : "",
+ (temp & RH_PS_CCS) ? " CCS" : ""
+ );
+ }
+}
+
+static void ohci_dump (ohci_t *controller, int verbose)
+{
+ dbg ("OHCI controller usb-%s state", controller->slot_name);
+
+ /* dumps some of the state we know about */
+ ohci_dump_status (controller);
+ if (verbose)
+ ep_print_int_eds (controller, "hcca");
+ dbg ("hcca frame #%04x", controller->hcca->frame_no);
+ ohci_dump_roothub (controller, 1);
+}
+
+
+#endif /* DEBUG */
+
+/*-------------------------------------------------------------------------*
+ * Interface functions (URB)
+ *-------------------------------------------------------------------------*/
+
+/* get a transfer request */
+
+int sohci_submit_job(struct usb_device *dev, unsigned long pipe, void *buffer,
+ int transfer_len, struct devrequest *setup, int interval)
+{
+ ohci_t *ohci;
+ ed_t * ed;
+ urb_priv_t *purb_priv;
+ int i, size = 0;
+
+ ohci = &gohci;
+
+ /* when controller's hung, permit only roothub cleanup attempts
+ * such as powering down ports */
+ if (ohci->disabled) {
+ err("sohci_submit_job: EPIPE");
+ return -1;
+ }
+
+ /* every endpoint has a ed, locate and fill it */
+ if (!(ed = ep_add_ed (dev, pipe))) {
+ err("sohci_submit_job: ENOMEM");
+ return -1;
+ }
+
+ /* for the private part of the URB we need the number of TDs (size) */
+ switch (usb_pipetype (pipe)) {
+ case PIPE_BULK: /* one TD for every 4096 Byte */
+ size = (transfer_len - 1) / 4096 + 1;
+ break;
+ case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */
+ size = (transfer_len == 0)? 2:
+ (transfer_len - 1) / 4096 + 3;
+ break;
+ }
+
+ if (size >= (N_URB_TD - 1)) {
+ err("need %d TDs, only have %d", size, N_URB_TD);
+ return -1;
+ }
+ purb_priv = &urb_priv;
+ purb_priv->pipe = pipe;
+
+ /* fill the private part of the URB */
+ purb_priv->length = size;
+ purb_priv->ed = ed;
+ purb_priv->actual_length = 0;
+
+ /* allocate the TDs */
+ /* note that td[0] was allocated in ep_add_ed */
+ for (i = 0; i < size; i++) {
+ purb_priv->td[i] = td_alloc (dev);
+ if (!purb_priv->td[i]) {
+ purb_priv->length = i;
+ urb_free_priv (purb_priv);
+ err("sohci_submit_job: ENOMEM");
+ return -1;
+ }
+ }
+
+ if (ed->state == ED_NEW || (ed->state & ED_DEL)) {
+ urb_free_priv (purb_priv);
+ err("sohci_submit_job: EINVAL");
+ return -1;
+ }
+
+ /* link the ed into a chain if is not already */
+ if (ed->state != ED_OPER)
+ ep_link (ohci, ed);
+
+ /* fill the TDs and link it to the ed */
+ td_submit_job(dev, pipe, buffer, transfer_len, setup, purb_priv, interval);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef DEBUG
+/* tell us the current USB frame number */
+
+static int sohci_get_current_frame_number (struct usb_device *usb_dev)
+{
+ ohci_t *ohci = &gohci;
+
+ return m16_swap (ohci->hcca->frame_no);
+}
+#endif
+
+/*-------------------------------------------------------------------------*
+ * ED handling functions
+ *-------------------------------------------------------------------------*/
+
+/* link an ed into one of the HC chains */
+
+static int ep_link (ohci_t *ohci, ed_t *edi)
+{
+ volatile ed_t *ed = edi;
+
+ ed->state = ED_OPER;
+
+ switch (ed->type) {
+ case PIPE_CONTROL:
+ ed->hwNextED = 0;
+ if (ohci->ed_controltail == NULL) {
+ writel (ed, &ohci->regs->ed_controlhead);
+ } else {
+ ohci->ed_controltail->hwNextED = m32_swap (ed);
+ }
+ ed->ed_prev = ohci->ed_controltail;
+ if (!ohci->ed_controltail && !ohci->ed_rm_list[0] &&
+ !ohci->ed_rm_list[1] && !ohci->sleeping) {
+ ohci->hc_control |= OHCI_CTRL_CLE;
+ writel (ohci->hc_control, &ohci->regs->control);
+ }
+ ohci->ed_controltail = edi;
+ break;
+
+ case PIPE_BULK:
+ ed->hwNextED = 0;
+ if (ohci->ed_bulktail == NULL) {
+ writel (ed, &ohci->regs->ed_bulkhead);
+ } else {
+ ohci->ed_bulktail->hwNextED = m32_swap (ed);
+ }
+ ed->ed_prev = ohci->ed_bulktail;
+ if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] &&
+ !ohci->ed_rm_list[1] && !ohci->sleeping) {
+ ohci->hc_control |= OHCI_CTRL_BLE;
+ writel (ohci->hc_control, &ohci->regs->control);
+ }
+ ohci->ed_bulktail = edi;
+ break;
+ }
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* unlink an ed from one of the HC chains.
+ * just the link to the ed is unlinked.
+ * the link from the ed still points to another operational ed or 0
+ * so the HC can eventually finish the processing of the unlinked ed */
+
+static int ep_unlink (ohci_t *ohci, ed_t *ed)
+{
+ ed->hwINFO |= m32_swap (OHCI_ED_SKIP);
+
+ switch (ed->type) {
+ case PIPE_CONTROL:
+ if (ed->ed_prev == NULL) {
+ if (!ed->hwNextED) {
+ ohci->hc_control &= ~OHCI_CTRL_CLE;
+ writel (ohci->hc_control, &ohci->regs->control);
+ }
+ writel (m32_swap (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_controlhead);
+ } else {
+ ed->ed_prev->hwNextED = ed->hwNextED;
+ }
+ if (ohci->ed_controltail == ed) {
+ ohci->ed_controltail = ed->ed_prev;
+ } else {
+ ((ed_t *)m32_swap (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev;
+ }
+ break;
+
+ case PIPE_BULK:
+ if (ed->ed_prev == NULL) {
+ if (!ed->hwNextED) {
+ ohci->hc_control &= ~OHCI_CTRL_BLE;
+ writel (ohci->hc_control, &ohci->regs->control);
+ }
+ writel (m32_swap (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_bulkhead);
+ } else {
+ ed->ed_prev->hwNextED = ed->hwNextED;
+ }
+ if (ohci->ed_bulktail == ed) {
+ ohci->ed_bulktail = ed->ed_prev;
+ } else {
+ ((ed_t *)m32_swap (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev;
+ }
+ break;
+ }
+ ed->state = ED_UNLINK;
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* add/reinit an endpoint; this should be done once at the usb_set_configuration command,
+ * but the USB stack is a little bit stateless so we do it at every transaction
+ * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK
+ * in all other cases the state is left unchanged
+ * the ed info fields are setted anyway even though most of them should not change */
+
+static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe)
+{
+ td_t *td;
+ ed_t *ed_ret;
+ volatile ed_t *ed;
+
+ ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint (pipe) << 1) |
+ (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))];
+
+ if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) {
+ err("ep_add_ed: pending delete");
+ /* pending delete request */
+ return NULL;
+ }
+
+ if (ed->state == ED_NEW) {
+ ed->hwINFO = m32_swap (OHCI_ED_SKIP); /* skip ed */
+ /* dummy td; end of td list for ed */
+ td = td_alloc (usb_dev);
+ ed->hwTailP = m32_swap (td);
+ ed->hwHeadP = ed->hwTailP;
+ ed->state = ED_UNLINK;
+ ed->type = usb_pipetype (pipe);
+ ohci_dev.ed_cnt++;
+ }
+
+ ed->hwINFO = m32_swap (usb_pipedevice (pipe)
+ | usb_pipeendpoint (pipe) << 7
+ | (usb_pipeisoc (pipe)? 0x8000: 0)
+ | (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000))
+ | usb_pipeslow (pipe) << 13
+ | usb_maxpacket (usb_dev, pipe) << 16);
+
+ return ed_ret;
+}
+
+/*-------------------------------------------------------------------------*
+ * TD handling functions
+ *-------------------------------------------------------------------------*/
+
+/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */
+
+static void td_fill (ohci_t *ohci, unsigned int info,
+ void *data, int len,
+ struct usb_device *dev, int index, urb_priv_t *urb_priv)
+{
+ volatile td_t *td, *td_pt;
+#ifdef OHCI_FILL_TRACE
+ int i;
+#endif
+
+ if (index > urb_priv->length) {
+ err("index > length");
+ return;
+ }
+ /* use this td as the next dummy */
+ td_pt = urb_priv->td [index];
+ td_pt->hwNextTD = 0;
+
+ /* fill the old dummy TD */
+ td = urb_priv->td [index] = (td_t *)(m32_swap (urb_priv->ed->hwTailP) & ~0xf);
+
+ td->ed = urb_priv->ed;
+ td->next_dl_td = NULL;
+ td->index = index;
+ td->data = (__u32)data;
+#ifdef OHCI_FILL_TRACE
+ if ((usb_pipetype(urb_priv->pipe) == PIPE_BULK) && usb_pipeout(urb_priv->pipe)) {
+ for (i = 0; i < len; i++)
+ printf("td->data[%d] %#2x ",i, ((unsigned char *)td->data)[i]);
+ printf("\n");
+ }
+#endif
+ if (!len)
+ data = 0;
+
+ td->hwINFO = m32_swap (info);
+ td->hwCBP = m32_swap (data);
+ if (data)
+ td->hwBE = m32_swap (data + len - 1);
+ else
+ td->hwBE = 0;
+ td->hwNextTD = m32_swap (td_pt);
+ td->hwPSW [0] = m16_swap (((__u32)data & 0x0FFF) | 0xE000);
+
+ /* append to queue */
+ td->ed->hwTailP = td->hwNextTD;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* prepare all TDs of a transfer */
+
+static void td_submit_job (struct usb_device *dev, unsigned long pipe, void *buffer,
+ int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval)
+{
+ ohci_t *ohci = &gohci;
+ int data_len = transfer_len;
+ void *data;
+ int cnt = 0;
+ __u32 info = 0;
+ unsigned int toggle = 0;
+
+ /* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */
+ if(usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) {
+ toggle = TD_T_TOGGLE;
+ } else {
+ toggle = TD_T_DATA0;
+ usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 1);
+ }
+ urb->td_cnt = 0;
+ if (data_len)
+ data = buffer;
+ else
+ data = 0;
+
+ switch (usb_pipetype (pipe)) {
+ case PIPE_BULK:
+ info = usb_pipeout (pipe)?
+ TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ;
+ while(data_len > 4096) {
+ td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, dev, cnt, urb);
+ data += 4096; data_len -= 4096; cnt++;
+ }
+ info = usb_pipeout (pipe)?
+ TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ;
+ td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, dev, cnt, urb);
+ cnt++;
+
+ if (!ohci->sleeping)
+ writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */
+ break;
+
+ case PIPE_CONTROL:
+ info = TD_CC | TD_DP_SETUP | TD_T_DATA0;
+ td_fill (ohci, info, setup, 8, dev, cnt++, urb);
+ if (data_len > 0) {
+ info = usb_pipeout (pipe)?
+ TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1;
+ /* NOTE: mishandles transfers >8K, some >4K */
+ td_fill (ohci, info, data, data_len, dev, cnt++, urb);
+ }
+ info = usb_pipeout (pipe)?
+ TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1;
+ td_fill (ohci, info, data, 0, dev, cnt++, urb);
+ if (!ohci->sleeping)
+ writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */
+ break;
+ }
+ if (urb->length != cnt)
+ dbg("TD LENGTH %d != CNT %d", urb->length, cnt);
+}
+
+/*-------------------------------------------------------------------------*
+ * Done List handling functions
+ *-------------------------------------------------------------------------*/
+
+
+/* calculate the transfer length and update the urb */
+
+static void dl_transfer_length(td_t * td)
+{
+ __u32 tdINFO, tdBE, tdCBP;
+ urb_priv_t *lurb_priv = &urb_priv;
+
+ tdINFO = m32_swap (td->hwINFO);
+ tdBE = m32_swap (td->hwBE);
+ tdCBP = m32_swap (td->hwCBP);
+
+
+ if (!(usb_pipetype (lurb_priv->pipe) == PIPE_CONTROL &&
+ ((td->index == 0) || (td->index == lurb_priv->length - 1)))) {
+ if (tdBE != 0) {
+ if (td->hwCBP == 0)
+ lurb_priv->actual_length += tdBE - td->data + 1;
+ else
+ lurb_priv->actual_length += tdCBP - td->data;
+ }
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* replies to the request have to be on a FIFO basis so
+ * we reverse the reversed done-list */
+
+static td_t * dl_reverse_done_list (ohci_t *ohci)
+{
+ __u32 td_list_hc;
+ td_t *td_rev = NULL;
+ td_t *td_list = NULL;
+ urb_priv_t *lurb_priv = NULL;
+
+ td_list_hc = m32_swap (ohci->hcca->done_head) & 0xfffffff0;
+ ohci->hcca->done_head = 0;
+
+ while (td_list_hc) {
+ td_list = (td_t *)td_list_hc;
+
+ if (TD_CC_GET (m32_swap (td_list->hwINFO))) {
+ lurb_priv = &urb_priv;
+ dbg(" USB-error/status: %x : %p",
+ TD_CC_GET (m32_swap (td_list->hwINFO)), td_list);
+ if (td_list->ed->hwHeadP & m32_swap (0x1)) {
+ if (lurb_priv && ((td_list->index + 1) < lurb_priv->length)) {
+ td_list->ed->hwHeadP =
+ (lurb_priv->td[lurb_priv->length - 1]->hwNextTD & m32_swap (0xfffffff0)) |
+ (td_list->ed->hwHeadP & m32_swap (0x2));
+ lurb_priv->td_cnt += lurb_priv->length - td_list->index - 1;
+ } else
+ td_list->ed->hwHeadP &= m32_swap (0xfffffff2);
+ }
+ }
+
+ td_list->next_dl_td = td_rev;
+ td_rev = td_list;
+ td_list_hc = m32_swap (td_list->hwNextTD) & 0xfffffff0;
+ }
+ return td_list;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* td done list */
+static int dl_done_list (ohci_t *ohci, td_t *td_list)
+{
+ td_t *td_list_next = NULL;
+ ed_t *ed;
+ int cc = 0;
+ int stat = 0;
+ /* urb_t *urb; */
+ urb_priv_t *lurb_priv;
+ __u32 tdINFO, edHeadP, edTailP;
+
+ while (td_list) {
+ td_list_next = td_list->next_dl_td;
+
+ lurb_priv = &urb_priv;
+ tdINFO = m32_swap (td_list->hwINFO);
+
+ ed = td_list->ed;
+
+ dl_transfer_length(td_list);
+
+ /* error code of transfer */
+ cc = TD_CC_GET (tdINFO);
+ if (cc != 0) {
+ dbg("ConditionCode %#x", cc);
+ stat = cc_to_error[cc];
+ }
+
+ if (ed->state != ED_NEW) {
+ edHeadP = m32_swap (ed->hwHeadP) & 0xfffffff0;
+ edTailP = m32_swap (ed->hwTailP);
+
+ /* unlink eds if they are not busy */
+ if ((edHeadP == edTailP) && (ed->state == ED_OPER))
+ ep_unlink (ohci, ed);
+ }
+
+ td_list = td_list_next;
+ }
+ return stat;
+}
+
+/*-------------------------------------------------------------------------*
+ * Virtual Root Hub
+ *-------------------------------------------------------------------------*/
+
+/* Device descriptor */
+static __u8 root_hub_dev_des[] =
+{
+ 0x12, /* __u8 bLength; */
+ 0x01, /* __u8 bDescriptorType; Device */
+ 0x10, /* __u16 bcdUSB; v1.1 */
+ 0x01,
+ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
+ 0x00, /* __u8 bDeviceSubClass; */
+ 0x00, /* __u8 bDeviceProtocol; */
+ 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
+ 0x00, /* __u16 idVendor; */
+ 0x00,
+ 0x00, /* __u16 idProduct; */
+ 0x00,
+ 0x00, /* __u16 bcdDevice; */
+ 0x00,
+ 0x00, /* __u8 iManufacturer; */
+ 0x01, /* __u8 iProduct; */
+ 0x00, /* __u8 iSerialNumber; */
+ 0x01 /* __u8 bNumConfigurations; */
+};
+
+
+/* Configuration descriptor */
+static __u8 root_hub_config_des[] =
+{
+ 0x09, /* __u8 bLength; */
+ 0x02, /* __u8 bDescriptorType; Configuration */
+ 0x19, /* __u16 wTotalLength; */
+ 0x00,
+ 0x01, /* __u8 bNumInterfaces; */
+ 0x01, /* __u8 bConfigurationValue; */
+ 0x00, /* __u8 iConfiguration; */
+ 0x40, /* __u8 bmAttributes;
+ Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */
+ 0x00, /* __u8 MaxPower; */
+
+ /* interface */
+ 0x09, /* __u8 if_bLength; */
+ 0x04, /* __u8 if_bDescriptorType; Interface */
+ 0x00, /* __u8 if_bInterfaceNumber; */
+ 0x00, /* __u8 if_bAlternateSetting; */
+ 0x01, /* __u8 if_bNumEndpoints; */
+ 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
+ 0x00, /* __u8 if_bInterfaceSubClass; */
+ 0x00, /* __u8 if_bInterfaceProtocol; */
+ 0x00, /* __u8 if_iInterface; */
+
+ /* endpoint */
+ 0x07, /* __u8 ep_bLength; */
+ 0x05, /* __u8 ep_bDescriptorType; Endpoint */
+ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x03, /* __u8 ep_bmAttributes; Interrupt */
+ 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */
+ 0x00,
+ 0xff /* __u8 ep_bInterval; 255 ms */
+};
+
+static unsigned char root_hub_str_index0[] =
+{
+ 0x04, /* __u8 bLength; */
+ 0x03, /* __u8 bDescriptorType; String-descriptor */
+ 0x09, /* __u8 lang ID */
+ 0x04, /* __u8 lang ID */
+};
+
+static unsigned char root_hub_str_index1[] =
+{
+ 28, /* __u8 bLength; */
+ 0x03, /* __u8 bDescriptorType; String-descriptor */
+ 'O', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'H', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'C', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'I', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ ' ', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'R', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'o', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'o', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 't', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ ' ', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'H', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'u', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+ 'b', /* __u8 Unicode */
+ 0, /* __u8 Unicode */
+};
+
+/* Hub class-specific descriptor is constructed dynamically */
+
+
+/*-------------------------------------------------------------------------*/
+
+#define OK(x) len = (x); break
+#ifdef DEBUG
+#define WR_RH_STAT(x) {info("WR:status %#8x", (x));writel((x), &gohci.regs->roothub.status);}
+#define WR_RH_PORTSTAT(x) {info("WR:portstatus[%d] %#8x", wIndex-1, (x));writel((x), &gohci.regs->roothub.portstatus[wIndex-1]);}
+#else
+#define WR_RH_STAT(x) writel((x), &gohci.regs->roothub.status)
+#define WR_RH_PORTSTAT(x) writel((x), &gohci.regs->roothub.portstatus[wIndex-1])
+#endif
+#define RD_RH_STAT roothub_status(&gohci)
+#define RD_RH_PORTSTAT roothub_portstatus(&gohci,wIndex-1)
+
+/* request to virtual root hub */
+
+int rh_check_port_status(ohci_t *controller)
+{
+ __u32 temp, ndp, i;
+ int res;
+
+ res = -1;
+ temp = roothub_a (controller);
+ ndp = (temp & RH_A_NDP);
+#ifdef CONFIG_AT91C_PQFP_UHPBUG
+ ndp = (ndp == 2) ? 1:0;
+#endif
+
+ for (i = 0; i < ndp; i++) {
+ temp = roothub_portstatus (controller, i);
+ /* check for a device disconnect */
+ if (((temp & (RH_PS_PESC | RH_PS_CSC)) ==
+ (RH_PS_PESC | RH_PS_CSC)) &&
+ ((temp & RH_PS_CCS) == 0)) {
+ res = i;
+ break;
+ }
+ }
+ return res;
+}
+
+static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe,
+ void *buffer, int transfer_len, struct devrequest *cmd)
+{
+ void * data = buffer;
+ int leni = transfer_len;
+ int len = 0;
+ int stat = 0;
+ __u32 datab[4];
+ __u8 *data_buf = (__u8 *)datab;
+ __u16 bmRType_bReq;
+ __u16 wValue;
+ __u16 wIndex;
+ __u16 wLength;
+
+#ifdef DEBUG
+urb_priv.actual_length = 0;
+pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe));
+#else
+ wait_ms(1);
+#endif
+ if ((pipe & PIPE_INTERRUPT) == PIPE_INTERRUPT) {
+ info("Root-Hub submit IRQ: NOT implemented");
+ return 0;
+ }
+
+ bmRType_bReq = cmd->requesttype | (cmd->request << 8);
+ wValue = m16_swap (cmd->value);
+ wIndex = m16_swap (cmd->index);
+ wLength = m16_swap (cmd->length);
+
+ info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x",
+ dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength);
+
+ switch (bmRType_bReq) {
+ /* Request Destination:
+ without flags: Device,
+ RH_INTERFACE: interface,
+ RH_ENDPOINT: endpoint,
+ RH_CLASS means HUB here,
+ RH_OTHER | RH_CLASS almost ever means HUB_PORT here
+ */
+
+ case RH_GET_STATUS:
+ *(__u16 *) data_buf = m16_swap (1); OK (2);
+ case RH_GET_STATUS | RH_INTERFACE:
+ *(__u16 *) data_buf = m16_swap (0); OK (2);
+ case RH_GET_STATUS | RH_ENDPOINT:
+ *(__u16 *) data_buf = m16_swap (0); OK (2);
+ case RH_GET_STATUS | RH_CLASS:
+ *(__u32 *) data_buf = m32_swap (
+ RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE));
+ OK (4);
+ case RH_GET_STATUS | RH_OTHER | RH_CLASS:
+ *(__u32 *) data_buf = m32_swap (RD_RH_PORTSTAT); OK (4);
+
+ case RH_CLEAR_FEATURE | RH_ENDPOINT:
+ switch (wValue) {
+ case (RH_ENDPOINT_STALL): OK (0);
+ }
+ break;
+
+ case RH_CLEAR_FEATURE | RH_CLASS:
+ switch (wValue) {
+ case RH_C_HUB_LOCAL_POWER:
+ OK(0);
+ case (RH_C_HUB_OVER_CURRENT):
+ WR_RH_STAT(RH_HS_OCIC); OK (0);
+ }
+ break;
+
+ case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+ switch (wValue) {
+ case (RH_PORT_ENABLE):
+ WR_RH_PORTSTAT (RH_PS_CCS ); OK (0);
+ case (RH_PORT_SUSPEND):
+ WR_RH_PORTSTAT (RH_PS_POCI); OK (0);
+ case (RH_PORT_POWER):
+ WR_RH_PORTSTAT (RH_PS_LSDA); OK (0);
+ case (RH_C_PORT_CONNECTION):
+ WR_RH_PORTSTAT (RH_PS_CSC ); OK (0);
+ case (RH_C_PORT_ENABLE):
+ WR_RH_PORTSTAT (RH_PS_PESC); OK (0);
+ case (RH_C_PORT_SUSPEND):
+ WR_RH_PORTSTAT (RH_PS_PSSC); OK (0);
+ case (RH_C_PORT_OVER_CURRENT):
+ WR_RH_PORTSTAT (RH_PS_OCIC); OK (0);
+ case (RH_C_PORT_RESET):
+ WR_RH_PORTSTAT (RH_PS_PRSC); OK (0);
+ }
+ break;
+
+ case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+ switch (wValue) {
+ case (RH_PORT_SUSPEND):
+ WR_RH_PORTSTAT (RH_PS_PSS ); OK (0);
+ case (RH_PORT_RESET): /* BUG IN HUP CODE *********/
+ if (RD_RH_PORTSTAT & RH_PS_CCS)
+ WR_RH_PORTSTAT (RH_PS_PRS);
+ OK (0);
+ case (RH_PORT_POWER):
+ WR_RH_PORTSTAT (RH_PS_PPS ); OK (0);
+ case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/
+ if (RD_RH_PORTSTAT & RH_PS_CCS)
+ WR_RH_PORTSTAT (RH_PS_PES );
+ OK (0);
+ }
+ break;
+
+ case RH_SET_ADDRESS: gohci.rh.devnum = wValue; OK(0);
+
+ case RH_GET_DESCRIPTOR:
+ switch ((wValue & 0xff00) >> 8) {
+ case (0x01): /* device descriptor */
+ len = min_t(unsigned int,
+ leni,
+ min_t(unsigned int,
+ sizeof (root_hub_dev_des),
+ wLength));
+ data_buf = root_hub_dev_des; OK(len);
+ case (0x02): /* configuration descriptor */
+ len = min_t(unsigned int,
+ leni,
+ min_t(unsigned int,
+ sizeof (root_hub_config_des),
+ wLength));
+ data_buf = root_hub_config_des; OK(len);
+ case (0x03): /* string descriptors */
+ if(wValue==0x0300) {
+ len = min_t(unsigned int,
+ leni,
+ min_t(unsigned int,
+ sizeof (root_hub_str_index0),
+ wLength));
+ data_buf = root_hub_str_index0;
+ OK(len);
+ }
+ if(wValue==0x0301) {
+ len = min_t(unsigned int,
+ leni,
+ min_t(unsigned int,
+ sizeof (root_hub_str_index1),
+ wLength));
+ data_buf = root_hub_str_index1;
+ OK(len);
+ }
+ default:
+ stat = USB_ST_STALLED;
+ }
+ break;
+
+ case RH_GET_DESCRIPTOR | RH_CLASS:
+ {
+ __u32 temp = roothub_a (&gohci);
+
+ data_buf [0] = 9; /* min length; */
+ data_buf [1] = 0x29;
+ data_buf [2] = temp & RH_A_NDP;
+#ifdef CONFIG_AT91C_PQFP_UHPBUG
+ data_buf [2] = (data_buf [2] == 2) ? 1:0;
+#endif
+ data_buf [3] = 0;
+ if (temp & RH_A_PSM) /* per-port power switching? */
+ data_buf [3] |= 0x1;
+ if (temp & RH_A_NOCP) /* no overcurrent reporting? */
+ data_buf [3] |= 0x10;
+ else if (temp & RH_A_OCPM) /* per-port overcurrent reporting? */
+ data_buf [3] |= 0x8;
+
+ /* corresponds to data_buf[4-7] */
+ datab [1] = 0;
+ data_buf [5] = (temp & RH_A_POTPGT) >> 24;
+ temp = roothub_b (&gohci);
+ data_buf [7] = temp & RH_B_DR;
+ if (data_buf [2] < 7) {
+ data_buf [8] = 0xff;
+ } else {
+ data_buf [0] += 2;
+ data_buf [8] = (temp & RH_B_DR) >> 8;
+ data_buf [10] = data_buf [9] = 0xff;
+ }
+
+ len = min_t(unsigned int, leni,
+ min_t(unsigned int, data_buf [0], wLength));
+ OK (len);
+ }
+
+ case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK (1);
+
+ case RH_SET_CONFIGURATION: WR_RH_STAT (0x10000); OK (0);
+
+ default:
+ dbg ("unsupported root hub command");
+ stat = USB_ST_STALLED;
+ }
+
+#ifdef DEBUG
+ ohci_dump_roothub (&gohci, 1);
+#else
+ wait_ms(1);
+#endif
+
+ len = min_t(int, len, leni);
+ if (data != data_buf)
+ memcpy (data, data_buf, len);
+ dev->act_len = len;
+ dev->status = stat;
+
+#ifdef DEBUG
+ if (transfer_len)
+ urb_priv.actual_length = transfer_len;
+ pkt_print(dev, pipe, buffer, transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/);
+#else
+ wait_ms(1);
+#endif
+
+ return stat;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* common code for handling submit messages - used for all but root hub */
+/* accesses. */
+int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+ int transfer_len, struct devrequest *setup, int interval)
+{
+ int stat = 0;
+ int maxsize = usb_maxpacket(dev, pipe);
+ int timeout;
+
+ /* device pulled? Shortcut the action. */
+ if (devgone == dev) {
+ dev->status = USB_ST_CRC_ERR;
+ return 0;
+ }
+
+#ifdef DEBUG
+ urb_priv.actual_length = 0;
+ pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe));
+#else
+ wait_ms(1);
+#endif
+ if (!maxsize) {
+ err("submit_common_message: pipesize for pipe %lx is zero",
+ pipe);
+ return -1;
+ }
+
+ if (sohci_submit_job(dev, pipe, buffer, transfer_len, setup, interval) < 0) {
+ err("sohci_submit_job failed");
+ return -1;
+ }
+
+ wait_ms(10);
+ /* ohci_dump_status(&gohci); */
+
+ /* allow more time for a BULK device to react - some are slow */
+#define BULK_TO 5000 /* timeout in milliseconds */
+ if (usb_pipetype (pipe) == PIPE_BULK)
+ timeout = BULK_TO;
+ else
+ timeout = 100;
+
+ /* wait for it to complete */
+ for (;;) {
+ /* check whether the controller is done */
+ stat = hc_interrupt();
+ if (stat < 0) {
+ stat = USB_ST_CRC_ERR;
+ break;
+ }
+ if (stat >= 0 && stat != 0xff) {
+ /* 0xff is returned for an SF-interrupt */
+ break;
+ }
+ if (--timeout) {
+ wait_ms(1);
+ } else {
+ err("CTL:TIMEOUT ");
+ stat = USB_ST_CRC_ERR;
+ break;
+ }
+ }
+ /* we got an Root Hub Status Change interrupt */
+ if (got_rhsc) {
+#ifdef DEBUG
+ ohci_dump_roothub (&gohci, 1);
+#endif
+ got_rhsc = 0;
+ /* abuse timeout */
+ timeout = rh_check_port_status(&gohci);
+ if (timeout >= 0) {
+#if 0 /* this does nothing useful, but leave it here in case that changes */
+ /* the called routine adds 1 to the passed value */
+ usb_hub_port_connect_change(gohci.rh.dev, timeout - 1);
+#endif
+ /*
+ * XXX
+ * This is potentially dangerous because it assumes
+ * that only one device is ever plugged in!
+ */
+ devgone = dev;
+ }
+ }
+
+ dev->status = stat;
+ dev->act_len = transfer_len;
+
+#ifdef DEBUG
+ pkt_print(dev, pipe, buffer, transfer_len, setup, "RET(ctlr)", usb_pipein(pipe));
+#else
+ wait_ms(1);
+#endif
+
+ /* free TDs in urb_priv */
+ urb_free_priv (&urb_priv);
+ return 0;
+}
+
+/* submit routines called from usb.c */
+int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+ int transfer_len)
+{
+ info("submit_bulk_msg");
+ return submit_common_msg(dev, pipe, buffer, transfer_len, NULL, 0);
+}
+
+int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+ int transfer_len, struct devrequest *setup)
+{
+ int maxsize = usb_maxpacket(dev, pipe);
+
+ info("submit_control_msg");
+#ifdef DEBUG
+ urb_priv.actual_length = 0;
+ pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe));
+#else
+ wait_ms(1);
+#endif
+ if (!maxsize) {
+ err("submit_control_message: pipesize for pipe %lx is zero",
+ pipe);
+ return -1;
+ }
+ if (((pipe >> 8) & 0x7f) == gohci.rh.devnum) {
+ gohci.rh.dev = dev;
+ /* root hub - redirect */
+ return ohci_submit_rh_msg(dev, pipe, buffer, transfer_len,
+ setup);
+ }
+
+ return submit_common_msg(dev, pipe, buffer, transfer_len, setup, 0);
+}
+
+int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,
+ int transfer_len, int interval)
+{
+ info("submit_int_msg");
+ return -1;
+}
+
+/*-------------------------------------------------------------------------*
+ * HC functions
+ *-------------------------------------------------------------------------*/
+
+/* reset the HC and BUS */
+
+static int hc_reset (ohci_t *ohci)
+{
+ int timeout = 30;
+ int smm_timeout = 50; /* 0,5 sec */
+
+ dbg("%s\n", __FUNCTION__);
+
+ if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */
+ writel (OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership */
+ info("USB HC TakeOver from SMM");
+ while (readl (&ohci->regs->control) & OHCI_CTRL_IR) {
+ wait_ms (10);
+ if (--smm_timeout == 0) {
+ err("USB HC TakeOver failed!");
+ return -1;
+ }
+ }
+ }
+
+ /* Disable HC interrupts */
+ writel (OHCI_INTR_MIE, &ohci->regs->intrdisable);
+
+ dbg("USB HC reset_hc usb-%s: ctrl = 0x%X ;\n",
+ ohci->slot_name,
+ readl(&ohci->regs->control));
+
+ /* Reset USB (needed by some controllers) */
+ writel (0, &ohci->regs->control);
+
+ /* HC Reset requires max 10 us delay */
+ writel (OHCI_HCR, &ohci->regs->cmdstatus);
+ while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
+ if (--timeout == 0) {
+ err("USB HC reset timed out!");
+ return -1;
+ }
+ udelay (1);
+ }
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Start an OHCI controller, set the BUS operational
+ * enable interrupts
+ * connect the virtual root hub */
+
+static int hc_start (ohci_t * ohci)
+{
+ __u32 mask;
+ unsigned int fminterval;
+
+ ohci->disabled = 1;
+
+ /* Tell the controller where the control and bulk lists are
+ * The lists are empty now. */
+
+ writel (0, &ohci->regs->ed_controlhead);
+ writel (0, &ohci->regs->ed_bulkhead);
+
+ writel ((__u32)ohci->hcca, &ohci->regs->hcca); /* a reset clears this */
+
+ fminterval = 0x2edf;
+ writel ((fminterval * 9) / 10, &ohci->regs->periodicstart);
+ fminterval |= ((((fminterval - 210) * 6) / 7) << 16);
+ writel (fminterval, &ohci->regs->fminterval);
+ writel (0x628, &ohci->regs->lsthresh);
+
+ /* start controller operations */
+ ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER;
+ ohci->disabled = 0;
+ writel (ohci->hc_control, &ohci->regs->control);
+
+ /* disable all interrupts */
+ mask = (OHCI_INTR_SO | OHCI_INTR_WDH | OHCI_INTR_SF | OHCI_INTR_RD |
+ OHCI_INTR_UE | OHCI_INTR_FNO | OHCI_INTR_RHSC |
+ OHCI_INTR_OC | OHCI_INTR_MIE);
+ writel (mask, &ohci->regs->intrdisable);
+ /* clear all interrupts */
+ mask &= ~OHCI_INTR_MIE;
+ writel (mask, &ohci->regs->intrstatus);
+ /* Choose the interrupts we care about now - but w/o MIE */
+ mask = OHCI_INTR_RHSC | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO;
+ writel (mask, &ohci->regs->intrenable);
+
+#ifdef OHCI_USE_NPS
+ /* required for AMD-756 and some Mac platforms */
+ writel ((roothub_a (ohci) | RH_A_NPS) & ~RH_A_PSM,
+ &ohci->regs->roothub.a);
+ writel (RH_HS_LPSC, &ohci->regs->roothub.status);
+#endif /* OHCI_USE_NPS */
+
+#define mdelay(n) ({unsigned long msec=(n); while (msec--) udelay(1000);})
+ /* POTPGT delay is bits 24-31, in 2 ms units. */
+ mdelay ((roothub_a (ohci) >> 23) & 0x1fe);
+
+ /* connect the virtual root hub */
+ ohci->rh.devnum = 0;
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* an interrupt happens */
+
+static int
+hc_interrupt (void)
+{
+ ohci_t *ohci = &gohci;
+ struct ohci_regs *regs = ohci->regs;
+ int ints;
+ int stat = -1;
+
+ if ((ohci->hcca->done_head != 0) && !(m32_swap (ohci->hcca->done_head) & 0x01)) {
+ ints = OHCI_INTR_WDH;
+ } else {
+ ints = readl (&regs->intrstatus);
+ }
+
+ /* dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); */
+
+ if (ints & OHCI_INTR_RHSC) {
+ got_rhsc = 1;
+ }
+
+ if (ints & OHCI_INTR_UE) {
+ ohci->disabled++;
+ err ("OHCI Unrecoverable Error, controller usb-%s disabled",
+ ohci->slot_name);
+ /* e.g. due to PCI Master/Target Abort */
+
+#ifdef DEBUG
+ ohci_dump (ohci, 1);
+#else
+ wait_ms(1);
+#endif
+ /* FIXME: be optimistic, hope that bug won't repeat often. */
+ /* Make some non-interrupt context restart the controller. */
+ /* Count and limit the retries though; either hardware or */
+ /* software errors can go forever... */
+ hc_reset (ohci);
+ return -1;
+ }
+
+ if (ints & OHCI_INTR_WDH) {
+ wait_ms(1);
+ writel (OHCI_INTR_WDH, &regs->intrdisable);
+ stat = dl_done_list (&gohci, dl_reverse_done_list (&gohci));
+ writel (OHCI_INTR_WDH, &regs->intrenable);
+ }
+
+ if (ints & OHCI_INTR_SO) {
+ dbg("USB Schedule overrun\n");
+ writel (OHCI_INTR_SO, &regs->intrenable);
+ stat = -1;
+ }
+
+ /* FIXME: this assumes SOF (1/ms) interrupts don't get lost... */
+ if (ints & OHCI_INTR_SF) {
+ unsigned int frame = m16_swap (ohci->hcca->frame_no) & 1;
+ wait_ms(1);
+ writel (OHCI_INTR_SF, &regs->intrdisable);
+ if (ohci->ed_rm_list[frame] != NULL)
+ writel (OHCI_INTR_SF, &regs->intrenable);
+ stat = 0xff;
+ }
+
+ writel (ints, &regs->intrstatus);
+ return stat;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+
+/* De-allocate all resources.. */
+
+static void hc_release_ohci (ohci_t *ohci)
+{
+ dbg ("USB HC release ohci usb-%s", ohci->slot_name);
+
+ if (!ohci->disabled)
+ hc_reset (ohci);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * low level initalisation routine, called from usb.c
+ */
+static char ohci_inited = 0;
+
+int usb_lowlevel_init(void)
+{
+ /*
+ * Enable USB host clock.
+ */
+ *AT91C_PMC_SCER = AT91C_PMC_UHP; /* 48MHz clock enabled for UHP */
+ *AT91C_PMC_PCER = 1 << AT91C_ID_UHP; /* Peripheral Clock Enable Register */
+
+ memset (&gohci, 0, sizeof (ohci_t));
+ memset (&urb_priv, 0, sizeof (urb_priv_t));
+
+ /* align the storage */
+ if ((__u32)&ghcca[0] & 0xff) {
+ err("HCCA not aligned!!");
+ return -1;
+ }
+ phcca = &ghcca[0];
+ info("aligned ghcca %p", phcca);
+ memset(&ohci_dev, 0, sizeof(struct ohci_device));
+ if ((__u32)&ohci_dev.ed[0] & 0x7) {
+ err("EDs not aligned!!");
+ return -1;
+ }
+ memset(gtd, 0, sizeof(td_t) * (NUM_TD + 1));
+ if ((__u32)gtd & 0x7) {
+ err("TDs not aligned!!");
+ return -1;
+ }
+ ptd = gtd;
+ gohci.hcca = phcca;
+ memset (phcca, 0, sizeof (struct ohci_hcca));
+
+ gohci.disabled = 1;
+ gohci.sleeping = 0;
+ gohci.irq = -1;
+ gohci.regs = (struct ohci_regs *)AT91_USB_HOST_BASE;
+
+ gohci.flags = 0;
+ gohci.slot_name = "at91rm9200";
+
+ if (hc_reset (&gohci) < 0) {
+ hc_release_ohci (&gohci);
+ /* Initialization failed */
+ *AT91C_PMC_PCER = AT91C_ID_UHP;
+ *AT91C_PMC_SCDR = 1 << AT91C_PMC_UHP; /* 48MHz clock disabled for UHP */
+ return -1;
+ }
+
+ /* FIXME this is a second HC reset; why?? */
+/* writel (gohci.hc_control = OHCI_USB_RESET, &gohci.regs->control);
+ wait_ms (10);*/
+
+ if (hc_start (&gohci) < 0) {
+ err ("can't start usb-%s", gohci.slot_name);
+ hc_release_ohci (&gohci);
+ /* Initialization failed */
+ *AT91C_PMC_PCER = AT91C_ID_UHP;
+ *AT91C_PMC_SCDR = 1 << AT91C_PMC_UHP; /* 48MHz clock disabled for UHP */
+ return -1;
+ }
+
+#ifdef DEBUG
+ ohci_dump (&gohci, 1);
+#else
+ wait_ms(1);
+#endif
+ ohci_inited = 1;
+ return 0;
+}
+
+int usb_lowlevel_stop(void)
+{
+ /* this gets called really early - before the controller has */
+ /* even been initialized! */
+ if (!ohci_inited)
+ return 0;
+ /* TODO release any interrupts, etc. */
+ /* call hc_release_ohci() here ? */
+ hc_reset (&gohci);
+ /* may not want to do this */
+ *AT91C_PMC_PCER = 1 << AT91C_ID_UHP;
+ *AT91C_PMC_SCDR = 1 << AT91C_PMC_UHP; /* 48MHz clock disabled for UHP */
+ return 0;
+}
+
+#endif /* CONFIG_USB_OHCI */
diff --git a/cpu/arm920t/at91rm9200/usb_ohci.h b/cpu/arm920t/at91rm9200/usb_ohci.h
new file mode 100755
index 0000000..ecb4e93
--- /dev/null
+++ b/cpu/arm920t/at91rm9200/usb_ohci.h
@@ -0,0 +1,419 @@
+/*
+ * URB OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
+ * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net>
+ *
+ * usb-ohci.h
+ */
+
+
+static int cc_to_error[16] = {
+
+/* mapping of the OHCI CC status to error codes */
+ /* No Error */ 0,
+ /* CRC Error */ USB_ST_CRC_ERR,
+ /* Bit Stuff */ USB_ST_BIT_ERR,
+ /* Data Togg */ USB_ST_CRC_ERR,
+ /* Stall */ USB_ST_STALLED,
+ /* DevNotResp */ -1,
+ /* PIDCheck */ USB_ST_BIT_ERR,
+ /* UnExpPID */ USB_ST_BIT_ERR,
+ /* DataOver */ USB_ST_BUF_ERR,
+ /* DataUnder */ USB_ST_BUF_ERR,
+ /* reservd */ -1,
+ /* reservd */ -1,
+ /* BufferOver */ USB_ST_BUF_ERR,
+ /* BuffUnder */ USB_ST_BUF_ERR,
+ /* Not Access */ -1,
+ /* Not Access */ -1
+};
+
+/* ED States */
+
+#define ED_NEW 0x00
+#define ED_UNLINK 0x01
+#define ED_OPER 0x02
+#define ED_DEL 0x04
+#define ED_URB_DEL 0x08
+
+/* usb_ohci_ed */
+struct ed {
+ __u32 hwINFO;
+ __u32 hwTailP;
+ __u32 hwHeadP;
+ __u32 hwNextED;
+
+ struct ed *ed_prev;
+ __u8 int_period;
+ __u8 int_branch;
+ __u8 int_load;
+ __u8 int_interval;
+ __u8 state;
+ __u8 type;
+ __u16 last_iso;
+ struct ed *ed_rm_list;
+
+ struct usb_device *usb_dev;
+ __u32 unused[3];
+} __attribute((aligned(16)));
+typedef struct ed ed_t;
+
+
+/* TD info field */
+#define TD_CC 0xf0000000
+#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f)
+#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28)
+#define TD_EC 0x0C000000
+#define TD_T 0x03000000
+#define TD_T_DATA0 0x02000000
+#define TD_T_DATA1 0x03000000
+#define TD_T_TOGGLE 0x00000000
+#define TD_R 0x00040000
+#define TD_DI 0x00E00000
+#define TD_DI_SET(X) (((X) & 0x07)<< 21)
+#define TD_DP 0x00180000
+#define TD_DP_SETUP 0x00000000
+#define TD_DP_IN 0x00100000
+#define TD_DP_OUT 0x00080000
+
+#define TD_ISO 0x00010000
+#define TD_DEL 0x00020000
+
+/* CC Codes */
+#define TD_CC_NOERROR 0x00
+#define TD_CC_CRC 0x01
+#define TD_CC_BITSTUFFING 0x02
+#define TD_CC_DATATOGGLEM 0x03
+#define TD_CC_STALL 0x04
+#define TD_DEVNOTRESP 0x05
+#define TD_PIDCHECKFAIL 0x06
+#define TD_UNEXPECTEDPID 0x07
+#define TD_DATAOVERRUN 0x08
+#define TD_DATAUNDERRUN 0x09
+#define TD_BUFFEROVERRUN 0x0C
+#define TD_BUFFERUNDERRUN 0x0D
+#define TD_NOTACCESSED 0x0F
+
+
+#define MAXPSW 1
+
+struct td {
+ __u32 hwINFO;
+ __u32 hwCBP; /* Current Buffer Pointer */
+ __u32 hwNextTD; /* Next TD Pointer */
+ __u32 hwBE; /* Memory Buffer End Pointer */
+
+ __u16 hwPSW[MAXPSW];
+ __u8 unused;
+ __u8 index;
+ struct ed *ed;
+ struct td *next_dl_td;
+ struct usb_device *usb_dev;
+ int transfer_len;
+ __u32 data;
+
+ __u32 unused2[2];
+} __attribute((aligned(32)));
+typedef struct td td_t;
+
+#define OHCI_ED_SKIP (1 << 14)
+
+/*
+ * The HCCA (Host Controller Communications Area) is a 256 byte
+ * structure defined in the OHCI spec. that the host controller is
+ * told the base address of. It must be 256-byte aligned.
+ */
+
+#define NUM_INTS 32 /* part of the OHCI standard */
+struct ohci_hcca {
+ __u32 int_table[NUM_INTS]; /* Interrupt ED table */
+ __u16 frame_no; /* current frame number */
+ __u16 pad1; /* set to 0 on each frame_no change */
+ __u32 done_head; /* info returned for an interrupt */
+ u8 reserved_for_hc[116];
+} __attribute((aligned(256)));
+
+
+/*
+ * Maximum number of root hub ports.
+ */
+#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */
+
+/*
+ * This is the structure of the OHCI controller's memory mapped I/O
+ * region. This is Memory Mapped I/O. You must use the readl() and
+ * writel() macros defined in asm/io.h to access these!!
+ */
+struct ohci_regs {
+ /* control and status registers */
+ __u32 revision;
+ __u32 control;
+ __u32 cmdstatus;
+ __u32 intrstatus;
+ __u32 intrenable;
+ __u32 intrdisable;
+ /* memory pointers */
+ __u32 hcca;
+ __u32 ed_periodcurrent;
+ __u32 ed_controlhead;
+ __u32 ed_controlcurrent;
+ __u32 ed_bulkhead;
+ __u32 ed_bulkcurrent;
+ __u32 donehead;
+ /* frame counters */
+ __u32 fminterval;
+ __u32 fmremaining;
+ __u32 fmnumber;
+ __u32 periodicstart;
+ __u32 lsthresh;
+ /* Root hub ports */
+ struct ohci_roothub_regs {
+ __u32 a;
+ __u32 b;
+ __u32 status;
+ __u32 portstatus[MAX_ROOT_PORTS];
+ } roothub;
+} __attribute((aligned(32)));
+
+
+/* OHCI CONTROL AND STATUS REGISTER MASKS */
+
+/*
+ * HcControl (control) register masks
+ */
+#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */
+#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */
+#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */
+#define OHCI_CTRL_CLE (1 << 4) /* control list enable */
+#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */
+#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */
+#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */
+#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
+#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */
+
+/* pre-shifted values for HCFS */
+# define OHCI_USB_RESET (0 << 6)
+# define OHCI_USB_RESUME (1 << 6)
+# define OHCI_USB_OPER (2 << 6)
+# define OHCI_USB_SUSPEND (3 << 6)
+
+/*
+ * HcCommandStatus (cmdstatus) register masks
+ */
+#define OHCI_HCR (1 << 0) /* host controller reset */
+#define OHCI_CLF (1 << 1) /* control list filled */
+#define OHCI_BLF (1 << 2) /* bulk list filled */
+#define OHCI_OCR (1 << 3) /* ownership change request */
+#define OHCI_SOC (3 << 16) /* scheduling overrun count */
+
+/*
+ * masks used with interrupt registers:
+ * HcInterruptStatus (intrstatus)
+ * HcInterruptEnable (intrenable)
+ * HcInterruptDisable (intrdisable)
+ */
+#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */
+#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */
+#define OHCI_INTR_SF (1 << 2) /* start frame */
+#define OHCI_INTR_RD (1 << 3) /* resume detect */
+#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */
+#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */
+#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */
+#define OHCI_INTR_OC (1 << 30) /* ownership change */
+#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */
+
+
+/* Virtual Root HUB */
+struct virt_root_hub {
+ int devnum; /* Address of Root Hub endpoint */
+ void *dev; /* was urb */
+ void *int_addr;
+ int send;
+ int interval;
+};
+
+/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */
+
+/* destination of request */
+#define RH_INTERFACE 0x01
+#define RH_ENDPOINT 0x02
+#define RH_OTHER 0x03
+
+#define RH_CLASS 0x20
+#define RH_VENDOR 0x40
+
+/* Requests: bRequest << 8 | bmRequestType */
+#define RH_GET_STATUS 0x0080
+#define RH_CLEAR_FEATURE 0x0100
+#define RH_SET_FEATURE 0x0300
+#define RH_SET_ADDRESS 0x0500
+#define RH_GET_DESCRIPTOR 0x0680
+#define RH_SET_DESCRIPTOR 0x0700
+#define RH_GET_CONFIGURATION 0x0880
+#define RH_SET_CONFIGURATION 0x0900
+#define RH_GET_STATE 0x0280
+#define RH_GET_INTERFACE 0x0A80
+#define RH_SET_INTERFACE 0x0B00
+#define RH_SYNC_FRAME 0x0C80
+/* Our Vendor Specific Request */
+#define RH_SET_EP 0x2000
+
+
+/* Hub port features */
+#define RH_PORT_CONNECTION 0x00
+#define RH_PORT_ENABLE 0x01
+#define RH_PORT_SUSPEND 0x02
+#define RH_PORT_OVER_CURRENT 0x03
+#define RH_PORT_RESET 0x04
+#define RH_PORT_POWER 0x08
+#define RH_PORT_LOW_SPEED 0x09
+
+#define RH_C_PORT_CONNECTION 0x10
+#define RH_C_PORT_ENABLE 0x11
+#define RH_C_PORT_SUSPEND 0x12
+#define RH_C_PORT_OVER_CURRENT 0x13
+#define RH_C_PORT_RESET 0x14
+
+/* Hub features */
+#define RH_C_HUB_LOCAL_POWER 0x00
+#define RH_C_HUB_OVER_CURRENT 0x01
+
+#define RH_DEVICE_REMOTE_WAKEUP 0x00
+#define RH_ENDPOINT_STALL 0x01
+
+#define RH_ACK 0x01
+#define RH_REQ_ERR -1
+#define RH_NACK 0x00
+
+
+/* OHCI ROOT HUB REGISTER MASKS */
+
+/* roothub.portstatus [i] bits */
+#define RH_PS_CCS 0x00000001 /* current connect status */
+#define RH_PS_PES 0x00000002 /* port enable status*/
+#define RH_PS_PSS 0x00000004 /* port suspend status */
+#define RH_PS_POCI 0x00000008 /* port over current indicator */
+#define RH_PS_PRS 0x00000010 /* port reset status */
+#define RH_PS_PPS 0x00000100 /* port power status */
+#define RH_PS_LSDA 0x00000200 /* low speed device attached */
+#define RH_PS_CSC 0x00010000 /* connect status change */
+#define RH_PS_PESC 0x00020000 /* port enable status change */
+#define RH_PS_PSSC 0x00040000 /* port suspend status change */
+#define RH_PS_OCIC 0x00080000 /* over current indicator change */
+#define RH_PS_PRSC 0x00100000 /* port reset status change */
+
+/* roothub.status bits */
+#define RH_HS_LPS 0x00000001 /* local power status */
+#define RH_HS_OCI 0x00000002 /* over current indicator */
+#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */
+#define RH_HS_LPSC 0x00010000 /* local power status change */
+#define RH_HS_OCIC 0x00020000 /* over current indicator change */
+#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */
+
+/* roothub.b masks */
+#define RH_B_DR 0x0000ffff /* device removable flags */
+#define RH_B_PPCM 0xffff0000 /* port power control mask */
+
+/* roothub.a masks */
+#define RH_A_NDP (0xff << 0) /* number of downstream ports */
+#define RH_A_PSM (1 << 8) /* power switching mode */
+#define RH_A_NPS (1 << 9) /* no power switching */
+#define RH_A_DT (1 << 10) /* device type (mbz) */
+#define RH_A_OCPM (1 << 11) /* over current protection mode */
+#define RH_A_NOCP (1 << 12) /* no over current protection */
+#define RH_A_POTPGT (0xff << 24) /* power on to power good time */
+
+/* urb */
+#define N_URB_TD 48
+typedef struct
+{
+ ed_t *ed;
+ __u16 length; /* number of tds associated with this request */
+ __u16 td_cnt; /* number of tds already serviced */
+ int state;
+ unsigned long pipe;
+ int actual_length;
+ td_t *td[N_URB_TD]; /* list pointer to all corresponding TDs associated with this request */
+} urb_priv_t;
+#define URB_DEL 1
+
+/*
+ * This is the full ohci controller description
+ *
+ * Note how the "proper" USB information is just
+ * a subset of what the full implementation needs. (Linus)
+ */
+
+
+typedef struct ohci {
+ struct ohci_hcca *hcca; /* hcca */
+ /*dma_addr_t hcca_dma;*/
+
+ int irq;
+ int disabled; /* e.g. got a UE, we're hung */
+ int sleeping;
+ unsigned long flags; /* for HC bugs */
+
+ struct ohci_regs *regs; /* OHCI controller's memory */
+
+ ed_t *ed_rm_list[2]; /* lists of all endpoints to be removed */
+ ed_t *ed_bulktail; /* last endpoint of bulk list */
+ ed_t *ed_controltail; /* last endpoint of control list */
+ int intrstatus;
+ __u32 hc_control; /* copy of the hc control reg */
+ struct usb_device *dev[32];
+ struct virt_root_hub rh;
+
+ const char *slot_name;
+} ohci_t;
+
+#define NUM_EDS 8 /* num of preallocated endpoint descriptors */
+
+struct ohci_device {
+ ed_t ed[NUM_EDS];
+ int ed_cnt;
+};
+
+/* hcd */
+/* endpoint */
+static int ep_link(ohci_t * ohci, ed_t * ed);
+static int ep_unlink(ohci_t * ohci, ed_t * ed);
+static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned long pipe);
+
+/*-------------------------------------------------------------------------*/
+
+/* we need more TDs than EDs */
+#define NUM_TD 64
+
+/* +1 so we can align the storage */
+td_t gtd[NUM_TD+1];
+/* pointers to aligned storage */
+td_t *ptd;
+
+/* TDs ... */
+static inline struct td *
+td_alloc (struct usb_device *usb_dev)
+{
+ int i;
+ struct td *td;
+
+ td = NULL;
+ for (i = 0; i < NUM_TD; i++)
+ {
+ if (ptd[i].usb_dev == NULL)
+ {
+ td = &ptd[i];
+ td->usb_dev = usb_dev;
+ break;
+ }
+ }
+
+ return td;
+}
+
+static inline void
+ed_free (struct ed *ed)
+{
+ ed->usb_dev = NULL;
+}