From: Wolfgang Denk Date: Sat, 24 Sep 2005 22:53:22 +0000 (+0200) Subject: Add support for AMD's Pb1x00 eval board; X-Git-Url: http://git.dujemihanovic.xyz/?a=commitdiff_plain;h=265817c7e6e55f1c2d05b8aa2080145291968b2e;p=u-boot.git Add support for AMD's Pb1x00 eval board; add MII routines to the au1x00 ethernet driver; add USB ohci driver (work in progress) Patch by Thomas Sailer, 20 Jan 2005 --- diff --git a/CHANGELOG b/CHANGELOG index b5a1918c65..6569616d57 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,11 @@ Changes for U-Boot 1.1.4: ====================================================================== +* Add support for AMD's Pb1x00 eval board; + add MII routines to the au1x00 ethernet driver; + add USB ohci driver (work in progress) + Patch by Thomas Sailer, 20 Jan 2005 + * Update omap5912osk board Use drivers/cfi_flash.c instead of private flash driver; Remove hardcoded personalized settings from omap5912osk.h; diff --git a/Makefile b/Makefile index 7f7e114eb7..c43cb2e834 100644 --- a/Makefile +++ b/Makefile @@ -1646,6 +1646,11 @@ dbau1550_el_config : unconfig @echo "#define CONFIG_DBAU1550 1" >>include/config.h @./mkconfig -a dbau1x00 mips mips dbau1x00 +pb1000_config : unconfig + @ >include/config.h + @echo "#define CONFIG_PB1000 1" >>include/config.h + @./mkconfig -a pb1x00 mips mips pb1x00 + ######################################################################### ## MIPS64 5Kc ######################################################################### diff --git a/board/esd/cpci2dp/Makefile b/board/esd/cpci2dp/Makefile index b067432728..a60495a59a 100644 --- a/board/esd/cpci2dp/Makefile +++ b/board/esd/cpci2dp/Makefile @@ -25,7 +25,7 @@ include $(TOPDIR)/config.mk LIB = lib$(BOARD).a -OBJS = $(BOARD).o flash.o ../common/misc.o +OBJS = $(BOARD).o flash.o ../common/misc.o $(LIB): $(OBJS) $(SOBJS) $(AR) crv $@ $(OBJS) diff --git a/board/pb1x00/Makefile b/board/pb1x00/Makefile new file mode 100644 index 0000000000..d1cdc6b924 --- /dev/null +++ b/board/pb1x00/Makefile @@ -0,0 +1,41 @@ +# +# (C) Copyright 2003 +# 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$(BOARD).a + +OBJS = $(BOARD).o flash.o +SOBJS = memsetup.o + +$(LIB): .depend $(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/board/pb1x00/README b/board/pb1x00/README new file mode 100644 index 0000000000..b37ff3616b --- /dev/null +++ b/board/pb1x00/README @@ -0,0 +1,63 @@ +By Thomas.Lange@corelatus.se 2004-Oct-05 +---------------------------------------- +DbAu1xx0 are development boards from AMD containing +an Alchemy AU1xx0 series cpu with mips32 core. +Existing cpu:s are Au1000, Au1100, Au1500 and Au1550 + +Limitations & comments +---------------------- +Support was originally big endian only. +I have not tested, but several u-boot users report working +configurations in little endian mode. + +I named the board dbau1x00, to allow +support for all three development boards +( dbau1000, dbau1100 and dbau1500 ). +Now there is a new board called dbau1550 also, which +should be supported RSN. + +I only have a dbau1000, so my testing is limited +to this board. + +The board has two different flash banks, that can +be selected via dip switch. This makes it possible +to test new bootloaders without thrashing the YAMON +boot loader delivered with board. + +NOTE! When you switch between the two boot flashes, the +base addresses will be swapped. +Have this in mind when you compile u-boot. TEXT_BASE has +to match the address where u-boot is located when you +actually launch. + +Ethernet only supported for mac0. + +PCMCIA only supported for slot 0, only 3.3V. + +PCMCIA IDE tested with Sandisk Compact Flash and +IBM microdrive. + +################################### +######## NOTE!!!!!! ######### +################################### +If you partition a disk on another system (e.g. laptop), +all bytes will be swapped on 16bit level when using +PCMCIA and running cpu in big endian mode!!!! + +This is probably due to an error in Au1000 chip. + +Solution: + +a) Boot via network and partition disk directly from +dbau1x00. The endian will then be correct. + +b) Partition disk on "laptop" and fill it with all files +you need. Then write a simple program that endian swaps +whole disk, + +Example: +Original "laptop" byte order: +B0 B1 B2 B3 B4 B5 B6 B7 B8 B9... + +Dbau1000 byte order will then be: +B1 B0 B3 B2 B5 B4 B7 B6 B9 B8... diff --git a/board/pb1x00/config.mk b/board/pb1x00/config.mk new file mode 100644 index 0000000000..396a0454ad --- /dev/null +++ b/board/pb1x00/config.mk @@ -0,0 +1,32 @@ +# +# (C) Copyright 2003 +# 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 +# + +# +# AMD development board AMD Alchemy Pb1x00, MIPS32 core +# + +# ROM version +#TEXT_BASE = 0xbfc00000 + +# SDRAM version +TEXT_BASE = 0x83800000 diff --git a/board/pb1x00/flash.c b/board/pb1x00/flash.c new file mode 100644 index 0000000000..3cf29e844b --- /dev/null +++ b/board/pb1x00/flash.c @@ -0,0 +1,43 @@ +/* + * (C) Copyright 2003 + * 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 + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +/*----------------------------------------------------------------------- + * flash_init() + * + * sets up flash_info and returns size of FLASH (bytes) + */ +unsigned long flash_init (void) +{ + printf ("Skipping flash_init\n"); + return (0); +} + +int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt) +{ + printf ("write_buff not implemented\n"); + return (-1); +} diff --git a/board/pb1x00/memsetup.S b/board/pb1x00/memsetup.S new file mode 100644 index 0000000000..44f02b9555 --- /dev/null +++ b/board/pb1x00/memsetup.S @@ -0,0 +1,392 @@ +/* Memory sub-system initialization code */ + +#include +#include +#include +#include +#include + +#define AU1500_SYS_ADDR 0xB1900000 +#define sys_endian 0x0038 +#define CP0_Config0 $16 +#define MEM_1MS ((396000000/1000000) * 1000) + + .text + .set noreorder + .set mips32 + + .globl memsetup +memsetup: + /* + * Step 1) Establish CPU endian mode. + * NOTE: A fair amount of code is necessary on the Pb1000 to + * obtain the value of Switch S8.1 which is used to determine + * endian at run-time. + */ + + /* RCE1 */ + li t0, MEM_STCFG1 + li t1, 0x00000083 + sw t1, 0(t0) + + li t0, MEM_STTIME1 + li t1, 0x33030A10 + sw t1, 0(t0) + + li t0, MEM_STADDR1 + li t1, 0x11803E40 + sw t1, 0(t0) + + /* Set DSTRB bits so switch will read correctly */ + li t1, 0xBE00000C + lw t2, 0(t1) + or t2, t2, 0x00000300 + sw t2, 0(t1) + + /* Check switch setting */ + li t1, 0xBE000014 + lw t2, 0(t1) + and t2, t2, 0x00000100 + bne t2, zero, big_endian + nop + +little_endian: + + /* Change Au1 core to little endian */ + li t0, AU1500_SYS_ADDR + li t1, 1 + sw t1, sys_endian(t0) + mfc0 t2, CP0_CONFIG + mtc0 t2, CP0_CONFIG + nop + nop + + /* Big Endian is default so nothing to do but fall through */ + +big_endian: + + /* + * Step 2) Establish Status Register + * (set BEV, clear ERL, clear EXL, clear IE) + */ + li t1, 0x00400000 + mtc0 t1, CP0_STATUS + + /* + * Step 3) Establish CP0 Config0 + * (set OD, set K0=3) + */ + li t1, 0x00080003 + mtc0 t1, CP0_CONFIG + + /* + * Step 4) Disable Watchpoint facilities + */ + li t1, 0x00000000 + mtc0 t1, CP0_WATCHLO + mtc0 t1, CP0_IWATCHLO + /* + * Step 5) Disable the performance counters + */ + mtc0 zero, CP0_PERFORMANCE + nop + + /* + * Step 6) Establish EJTAG Debug register + */ + mtc0 zero, CP0_DEBUG + nop + + /* + * Step 7) Establish Cause + * (set IV bit) + */ + li t1, 0x00800000 + mtc0 t1, CP0_CAUSE + + /* Establish Wired (and Random) */ + mtc0 zero, CP0_WIRED + nop + + /* First setup pll:s to make serial work ok */ + /* We have a 12 MHz crystal */ + li t0, SYS_CPUPLL + li t1, 0x21 /* 396 MHz */ + sw t1, 0(t0) + sync + nop + nop + + /* wait 1mS for clocks to settle */ + li t1, MEM_1MS +1: add t1, -1 + bne t1, zero, 1b + nop + /* Setup AUX PLL */ + li t0, SYS_AUXPLL + li t1, 8 /* 96 MHz */ + sw t1, 0(t0) /* aux pll */ + sync + + /* Static memory controller */ + + /* RCE0 8MB AMD29D323 Flash */ + li t0, MEM_STCFG0 + li t1, 0x00001403 + sw t1, 0(t0) + + li t0, MEM_STTIME0 + li t1, 0xFFFFFFDD + sw t1, 0(t0) + + li t0, MEM_STADDR0 + li t1, 0x11F83FE0 + sw t1, 0(t0) + + /* RCE1 CPLD Board Logic */ + li t0, MEM_STCFG1 + li t1, 0x00000083 + sw t1, 0(t0) + + li t0, MEM_STTIME1 + li t1, 0x33030A10 + sw t1, 0(t0) + + li t0, MEM_STADDR1 + li t1, 0x11803E40 + sw t1, 0(t0) + + /* RCE2 CPLD Board Logic */ + li t0, MEM_STCFG2 + li t1, 0x00000004 + sw t1, 0(t0) + + li t0, MEM_STTIME2 + li t1, 0x08061908 + sw t1, 0(t0) + + li t0, MEM_STADDR2 + li t1, 0x12A03FC0 + sw t1, 0(t0) + + /* RCE3 PCMCIA 250ns */ + li t0, MEM_STCFG3 + li t1, 0x00000002 + sw t1, 0(t0) + + li t0, MEM_STTIME3 + li t1, 0x280E3E07 + sw t1, 0(t0) + + li t0, MEM_STADDR3 + li t1, 0x10000000 + sw t1, 0(t0) + + sync + + /* Set peripherals to a known state */ + li t0, IC0_CFG0CLR + li t1, 0xFFFFFFFF + sw t1, 0(t0) + + li t0, IC0_CFG0CLR + sw t1, 0(t0) + + li t0, IC0_CFG1CLR + sw t1, 0(t0) + + li t0, IC0_CFG2CLR + sw t1, 0(t0) + + li t0, IC0_SRCSET + sw t1, 0(t0) + + li t0, IC0_ASSIGNSET + sw t1, 0(t0) + + li t0, IC0_WAKECLR + sw t1, 0(t0) + + li t0, IC0_RISINGCLR + sw t1, 0(t0) + + li t0, IC0_FALLINGCLR + sw t1, 0(t0) + + li t0, IC0_TESTBIT + li t1, 0x00000000 + sw t1, 0(t0) + sync + + li t0, IC1_CFG0CLR + li t1, 0xFFFFFFFF + sw t1, 0(t0) + + li t0, IC1_CFG0CLR + sw t1, 0(t0) + + li t0, IC1_CFG1CLR + sw t1, 0(t0) + + li t0, IC1_CFG2CLR + sw t1, 0(t0) + + li t0, IC1_SRCSET + sw t1, 0(t0) + + li t0, IC1_ASSIGNSET + sw t1, 0(t0) + + li t0, IC1_WAKECLR + sw t1, 0(t0) + + li t0, IC1_RISINGCLR + sw t1, 0(t0) + + li t0, IC1_FALLINGCLR + sw t1, 0(t0) + + li t0, IC1_TESTBIT + li t1, 0x00000000 + sw t1, 0(t0) + sync + + li t0, SYS_FREQCTRL0 + li t1, 0x00000000 + sw t1, 0(t0) + + li t0, SYS_FREQCTRL1 + li t1, 0x00000000 + sw t1, 0(t0) + + li t0, SYS_CLKSRC + li t1, 0x00000000 + sw t1, 0(t0) + + li t0, SYS_PININPUTEN + li t1, 0x00000000 + sw t1, 0(t0) + sync + + li t0, 0xB1100100 + li t1, 0x00000000 + sw t1, 0(t0) + + li t0, 0xB1400100 + li t1, 0x00000000 + sw t1, 0(t0) + + + li t0, SYS_WAKEMSK + li t1, 0x00000000 + sw t1, 0(t0) + + li t0, SYS_WAKESRC + li t1, 0x00000000 + sw t1, 0(t0) + + /* wait 1mS before setup */ + li t1, MEM_1MS +1: add t1, -1 + bne t1, zero, 1b + nop + + /* + * Skip memory setup if we are running from memory + */ + li t0, 0x90000000 + sub t0, ra, t0 + bltz t0, skip_memsetup + nop + + /* + * SDCS0 - Not used, for SMROM + * SDCS1 - 32MB Micron 48LCBM16A2 + * SDCS2 - 32MB Micron 48LCBM16A2 + */ + li t0, MEM_SDMODE0 + li t1, 0x00000000 + sw t1, 0(t0) + + li t0, MEM_SDMODE1 + li t1, 0x00552229 + sw t1, 0(t0) + + li t0, MEM_SDMODE2 + li t1, 0x00552229 + sw t1, 0(t0) + + li t0, MEM_SDADDR0 + li t1, 0x00000000 + sw t1, 0(t0) + + li t0, MEM_SDADDR1 + li t1, 0x001003F8 + sw t1, 0(t0) + + li t0, MEM_SDADDR2 + li t1, 0x001023F8 + sw t1, 0(t0) + + sync + + li t0, MEM_SDREFCFG + li t1, 0x74000c30 /* Disable */ + sw t1, 0(t0) + sync + + li t0, MEM_SDPRECMD + sw zero, 0(t0) + sync + + li t0, MEM_SDAUTOREF + sw zero, 0(t0) + sync + sw zero, 0(t0) + sync + + li t0, MEM_SDREFCFG + li t1, 0x76000c30 /* Enable */ + sw t1, 0(t0) + sync + + li t0, MEM_SDWRMD0 + li t1, 0x00000023 + sw t1, 0(t0) + sync + + li t0, MEM_SDWRMD1 + li t1, 0x00000023 + sw t1, 0(t0) + sync + + li t0, MEM_SDWRMD2 + li t1, 0x00000023 + sw t1, 0(t0) + sync + + /* wait 1mS after setup */ + li t1, MEM_1MS +1: add t1, -1 + bne t1, zero, 1b + nop + +skip_memsetup: + + li t0, SYS_PINFUNC + li t1, 0/*0x00008080*/ + sw t1, 0(t0) + + /* + li t0, SYS_TRIOUTCLR + li t1, 0x00001FFF + sw t1, 0(t0) + + li t0, SYS_OUTPUTCLR + li t1, 0x00008000 + sw t1, 0(t0) + */ + sync + + j ra + nop diff --git a/board/pb1x00/pb1x00.c b/board/pb1x00/pb1x00.c new file mode 100644 index 0000000000..40ac2a4d7c --- /dev/null +++ b/board/pb1x00/pb1x00.c @@ -0,0 +1,115 @@ +/* + * (C) Copyright 2003 + * Thomas.Lange@corelatus.se + * + * 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 +#include +#include +#include + +long int initdram(int board_type) +{ + /* Sdram is setup by assembler code */ + /* If memory could be changed, we should return the true value here */ + return 64*1024*1024; +} + +#define BCSR_PCMCIA_PC0DRVEN 0x0010 +#define BCSR_PCMCIA_PC0RST 0x0080 + +/* In cpu/mips/cpu.c */ +void write_one_tlb( int index, u32 pagemask, u32 hi, u32 low0, u32 low1 ); + +int checkboard (void) +{ + u16 status; + /* volatile u32 *pcmcia_bcsr = (u32*)(DB1000_BCSR_ADDR+0x10); */ + volatile u32 *sys_counter = (volatile u32*)SYS_COUNTER_CNTRL; + u32 proc_id; + + *sys_counter = 0x100; /* Enable 32 kHz oscillator for RTC/TOY */ + + proc_id = read_32bit_cp0_register(CP0_PRID); + + switch (proc_id >> 24) { + case 0: + puts ("Board: Pb1000\n"); + printf ("CPU: Au1000 396 MHz, id: 0x%02x, rev: 0x%02x\n", + (proc_id >> 8) & 0xFF, proc_id & 0xFF); + break; + case 1: + puts ("Board: Pb1500\n"); + printf ("CPU: Au1500, id: 0x%02x, rev: 0x%02x\n", + (proc_id >> 8) & 0xFF, proc_id & 0xFF); + break; + case 2: + puts ("Board: Pb1100\n"); + printf ("CPU: Au1100, id: 0x%02x, rev: 0x%02x\n", + (proc_id >> 8) & 0xFF, proc_id & 0xFF); + break; + default: + printf ("Unsupported cpu %d, proc_id=0x%x\n", proc_id >> 24, proc_id); + } +#if defined(CONFIG_IDE_PCMCIA) && 0 + /* Enable 3.3 V on slot 0 ( VCC ) + No 5V */ + status = 4; + *pcmcia_bcsr = status; + + status |= BCSR_PCMCIA_PC0DRVEN; + *pcmcia_bcsr = status; + au_sync(); + + udelay(300*1000); + + status |= BCSR_PCMCIA_PC0RST; + *pcmcia_bcsr = status; + au_sync(); + + udelay(100*1000); + + /* PCMCIA is on a 36 bit physical address. + We need to map it into a 32 bit addresses */ + +#if 0 + /* We dont need theese unless we run whole pcmcia package */ + write_one_tlb(20, /* index */ + 0x01ffe000, /* Pagemask, 16 MB pages */ + CFG_PCMCIA_IO_BASE, /* Hi */ + 0x3C000017, /* Lo0 */ + 0x3C200017); /* Lo1 */ + + write_one_tlb(21, /* index */ + 0x01ffe000, /* Pagemask, 16 MB pages */ + CFG_PCMCIA_ATTR_BASE, /* Hi */ + 0x3D000017, /* Lo0 */ + 0x3D200017); /* Lo1 */ +#endif /* 0 */ + write_one_tlb(22, /* index */ + 0x01ffe000, /* Pagemask, 16 MB pages */ + CFG_PCMCIA_MEM_ADDR, /* Hi */ + 0x3E000017, /* Lo0 */ + 0x3E200017); /* Lo1 */ +#endif /* CONFIG_IDE_PCMCIA */ + + return 0; +} diff --git a/board/pb1x00/u-boot.lds b/board/pb1x00/u-boot.lds new file mode 100644 index 0000000000..a2d19a84c9 --- /dev/null +++ b/board/pb1x00/u-boot.lds @@ -0,0 +1,68 @@ +/* + * (C) Copyright 2003 + * Wolfgang Denk Engineering, + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* +OUTPUT_FORMAT("elf32-bigmips", "elf32-bigmips", "elf32-bigmips") +*/ +OUTPUT_FORMAT("elf32-tradbigmips", "elf32-tradbigmips", "elf32-tradbigmips") +OUTPUT_ARCH(mips) +ENTRY(_start) +SECTIONS +{ + . = 0x00000000; + + . = ALIGN(4); + .text : + { + *(.text) + } + + . = ALIGN(4); + .rodata : { *(.rodata) } + + . = ALIGN(4); + .data : { *(.data) } + + . = ALIGN(4); + .sdata : { *(.sdata) } + + _gp = ALIGN(16); + + __got_start = .; + .got : { *(.got) } + __got_end = .; + + .sdata : { *(.sdata) } + + __u_boot_cmd_start = .; + .u_boot_cmd : { *(.u_boot_cmd) } + __u_boot_cmd_end = .; + + uboot_end_data = .; + num_got_entries = (__got_end - __got_start) >> 2; + + . = ALIGN(4); + .sbss : { *(.sbss) } + .bss : { *(.bss) } + uboot_end = .; +} diff --git a/common/virtex2.c b/common/virtex2.c index 8405069423..b5dc366aad 100644 --- a/common/virtex2.c +++ b/common/virtex2.c @@ -35,7 +35,7 @@ #if 0 #define FPGA_DEBUG -#endif +#endif #ifdef FPGA_DEBUG #define PRINTF(fmt,args...) printf (fmt ,##args) diff --git a/cpu/mips/Makefile b/cpu/mips/Makefile index 1182266159..c8b30c7589 100644 --- a/cpu/mips/Makefile +++ b/cpu/mips/Makefile @@ -26,7 +26,7 @@ include $(TOPDIR)/config.mk LIB = lib$(CPU).a START = start.o -OBJS = asc_serial.o au1x00_serial.o au1x00_eth.o \ +OBJS = asc_serial.o au1x00_serial.o au1x00_eth.o au1x00_usb_ohci.o \ cpu.o interrupts.o incaip_clock.o SOBJS = incaip_wdt.o cache.o diff --git a/cpu/mips/au1x00_eth.c b/cpu/mips/au1x00_eth.c index 35c07b1461..b7a76521db 100644 --- a/cpu/mips/au1x00_eth.c +++ b/cpu/mips/au1x00_eth.c @@ -13,7 +13,7 @@ * * 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 + * 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 @@ -25,8 +25,8 @@ #ifdef CONFIG_AU1X00 -#if defined(CFG_DISCOVER_PHY) || (CONFIG_COMMANDS & CFG_CMD_MII) -#error "PHY and MII not supported yet" +#if defined(CFG_DISCOVER_PHY) +#error "PHY not supported yet" /* We just assume that we are running 100FD for now */ /* We all use switches, right? ;-) */ #endif @@ -193,9 +193,9 @@ static int au1x00_init(struct eth_device* dev, bd_t * bd){ /* Put mac addr in little endian */ #define ea eth_get_dev()->enetaddr - *mac_addr_high = (ea[5] << 8) | (ea[4] ) ; - *mac_addr_low = (ea[3] << 24) | (ea[2] << 16) | - (ea[1] << 8) | (ea[0] ) ; + *mac_addr_high = (ea[5] << 8) | (ea[4] ) ; + *mac_addr_low = (ea[3] << 24) | (ea[2] << 16) | + (ea[1] << 8) | (ea[0] ) ; #undef ea *mac_mcast_low = 0; *mac_mcast_high = 0; @@ -236,4 +236,61 @@ int au1x00_enet_initialize(bd_t *bis){ return 1; } +#if (CONFIG_COMMANDS & CFG_CMD_MII) +int miiphy_read(unsigned char addr, unsigned char reg, unsigned short * value) +{ + volatile u32 *mii_control_reg = (volatile u32*)(ETH0_BASE+MAC_MII_CNTRL); + volatile u32 *mii_data_reg = (volatile u32*)(ETH0_BASE+MAC_MII_DATA); + u32 mii_control; + unsigned int timedout = 20; + + while (*mii_control_reg & MAC_MII_BUSY) { + udelay(1000); + if (--timedout == 0) { + printf("au1x00_eth: miiphy_read busy timeout!!\n"); + return -1; + } + } + + mii_control = MAC_SET_MII_SELECT_REG(reg) | + MAC_SET_MII_SELECT_PHY(addr) | MAC_MII_READ; + + *mii_control_reg = mii_control; + + timedout = 20; + while (*mii_control_reg & MAC_MII_BUSY) { + udelay(1000); + if (--timedout == 0) { + printf("au1x00_eth: miiphy_read busy timeout!!\n"); + return -1; + } + } + *value = *mii_data_reg; + return 0; +} + +int miiphy_write(unsigned char addr, unsigned char reg, unsigned short value) +{ + volatile u32 *mii_control_reg = (volatile u32*)(ETH0_BASE+MAC_MII_CNTRL); + volatile u32 *mii_data_reg = (volatile u32*)(ETH0_BASE+MAC_MII_DATA); + u32 mii_control; + unsigned int timedout = 20; + + while (*mii_control_reg & MAC_MII_BUSY) { + udelay(1000); + if (--timedout == 0) { + printf("au1x00_eth: miiphy_write busy timeout!!\n"); + return; + } + } + + mii_control = MAC_SET_MII_SELECT_REG(reg) | + MAC_SET_MII_SELECT_PHY(addr) | MAC_MII_WRITE; + + *mii_data_reg = value; + *mii_control_reg = mii_control; + return 0; +} +#endif /* CONFIG_COMMANDS & CFG_CMD_MII */ + #endif /* CONFIG_AU1X00 */ diff --git a/cpu/mips/au1x00_usb_ohci.c b/cpu/mips/au1x00_usb_ohci.c new file mode 100644 index 0000000000..dbf72dc6f8 --- /dev/null +++ b/cpu/mips/au1x00_usb_ohci.c @@ -0,0 +1,1727 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB on the AU1x00. + * + * (C) Copyright 2003 + * Gary Jennejohn, DENX Software Engineering + * + * 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 + * + * Note: Part of this code has been derived from linux + * + */ +/* + * 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! + */ + +#include + +#if defined(CONFIG_AU1X00) && defined(CONFIG_USB_OHCI) + +/* #include no PCI on the AU1x00 */ + +#include +#include +#include +#include +#include +#include "au1x00_usb_ohci.h" + +#define OHCI_USE_NPS /* force NoPowerSwitching mode */ +#define OHCI_VERBOSE_DEBUG /* not always helpful */ +#define OHCI_FILL_TRACE + +#define USBH_ENABLE_BE (1<<0) +#define USBH_ENABLE_C (1<<1) +#define USBH_ENABLE_E (1<<2) +#define USBH_ENABLE_CE (1<<3) +#define USBH_ENABLE_RD (1<<4) + +#ifdef LITTLEENDIAN +#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C) +#else +#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C | USBH_ENABLE_BE) +#endif + + +/* For initializing controller (mask in an HCFS mode too) */ +#define OHCI_CONTROL_INIT \ + (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE + +#undef readl +#undef writel + +#define readl(a) au_readl((long)(a)) +#define writel(v,a) au_writel((v),(int)(a)) + +#define min_t(type,x,y) ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) + +#define 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) +#define 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 (®s->revision) & 0xff; + if (temp != 0x10) + dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f)); + + temp = readl (®s->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 (®s->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 (®s->intrstatus)); + ohci_dump_intr_mask ("intrenable", readl (®s->intrenable)); + + maybe_print_eds ("ed_periodcurrent", readl (®s->ed_periodcurrent)); + + maybe_print_eds ("ed_controlhead", readl (®s->ed_controlhead)); + maybe_print_eds ("ed_controlcurrent", readl (®s->ed_controlcurrent)); + + maybe_print_eds ("ed_bulkhead", readl (®s->ed_bulkhead)); + maybe_print_eds ("ed_bulkcurrent", readl (®s->ed_bulkcurrent)); + + maybe_print_eds ("donehead", readl (®s->donehead)); +} + +static void ohci_dump_roothub (ohci_t *controller, int verbose) +{ + __u32 temp, ndp, i; + + temp = roothub_a (controller); + ndp = (temp & RH_A_NDP); + + 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 ((long)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 ((long)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 (1 || ((usb_pipetype(urb_priv->pipe) == PIPE_BULK) && usb_pipeout(urb_priv->pipe))) { + for (i = 0; i < len; i++) + printf("td->data[%d] %#2x\n",i, ((unsigned char *)(td->data+0x80000000))[i]); + } +#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 */ + +#define kseg_to_phys(x) ((void *)((__u32)(x) - 0x80000000)) + +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 = kseg_to_phys(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, kseg_to_phys(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); + 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; + 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; + + timeout *= 4; + /* 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) { + udelay(250); /* 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 */ + + 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 ;", + 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 (®s->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, ®s->intrdisable); + stat = dl_done_list (&gohci, dl_reverse_done_list (&gohci)); + writel (OHCI_INTR_WDH, ®s->intrenable); + } + + if (ints & OHCI_INTR_SO) { + dbg("USB Schedule overrun\n"); + writel (OHCI_INTR_SO, ®s->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, ®s->intrdisable); + if (ohci->ed_rm_list[frame] != NULL) + writel (OHCI_INTR_SF, ®s->intrenable); + stat = 0xff; + } + + writel (ints, ®s->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); +} + +/*-------------------------------------------------------------------------*/ + +#define __read_32bit_c0_register(source, sel) \ +({ int __res; \ + if (sel == 0) \ + __asm__ __volatile__( \ + "mfc0\t%0, " #source "\n\t" \ + : "=r" (__res)); \ + else \ + __asm__ __volatile__( \ + ".set\tmips32\n\t" \ + "mfc0\t%0, " #source ", " #sel "\n\t" \ + ".set\tmips0\n\t" \ + : "=r" (__res)); \ + __res; \ +}) + +#define read_c0_prid() __read_32bit_c0_register($15, 0) + +/* + * low level initalisation routine, called from usb.c + */ +static char ohci_inited = 0; + +int usb_lowlevel_init(void) +{ + u32 pin_func; + u32 sys_freqctrl, sys_clksrc; + u32 prid = read_c0_prid(); + + dbg("in usb_lowlevel_init\n"); + + /* zero and disable FREQ2 */ + sys_freqctrl = au_readl(SYS_FREQCTRL0); + sys_freqctrl &= ~0xFFF00000; + au_writel(sys_freqctrl, SYS_FREQCTRL0); + + /* zero and disable USBH/USBD clocks */ + sys_clksrc = au_readl(SYS_CLKSRC); + sys_clksrc &= ~0x00007FE0; + au_writel(sys_clksrc, SYS_CLKSRC); + + sys_freqctrl = au_readl(SYS_FREQCTRL0); + sys_freqctrl &= ~0xFFF00000; + + sys_clksrc = au_readl(SYS_CLKSRC); + sys_clksrc &= ~0x00007FE0; + + switch (prid & 0x000000FF) { + case 0x00: /* DA */ + case 0x01: /* HA */ + case 0x02: /* HB */ + /* CPU core freq to 48MHz to slow it way down... */ + au_writel(4, SYS_CPUPLL); + + /* + * Setup 48MHz FREQ2 from CPUPLL for USB Host + */ + /* FRDIV2=3 -> div by 8 of 384MHz -> 48MHz */ + sys_freqctrl |= ((3<<22) | (1<<21) | (0<<20)); + au_writel(sys_freqctrl, SYS_FREQCTRL0); + + /* CPU core freq to 384MHz */ + au_writel(0x20, SYS_CPUPLL); + + printf("Au1000: 48MHz OHCI workaround enabled\n"); + break; + + default: /* HC and newer */ + /* FREQ2 = aux/2 = 48 MHz */ + sys_freqctrl |= ((0<<22) | (1<<21) | (1<<20)); + au_writel(sys_freqctrl, SYS_FREQCTRL0); + break; + } + + /* + * Route 48MHz FREQ2 into USB Host and/or Device + */ + sys_clksrc |= ((4<<12) | (0<<11) | (0<<10)); + au_writel(sys_clksrc, SYS_CLKSRC); + + /* configure pins GPIO[14:9] as GPIO */ + pin_func = au_readl(SYS_PINFUNC) & (u32)(~0x8080); + + au_writel(pin_func, SYS_PINFUNC); + au_writel(0x2800, SYS_TRIOUTCLR); + au_writel(0x0030, SYS_OUTPUTCLR); + + dbg("OHCI board setup complete\n"); + + /* enable host controller */ + au_writel(USBH_ENABLE_CE, USB_HOST_CONFIG); + udelay(1000); + au_writel(USBH_ENABLE_INIT, USB_HOST_CONFIG); + udelay(1000); + + /* wait for reset complete (read register twice; see au1500 errata) */ + while (au_readl(USB_HOST_CONFIG), + !(au_readl(USB_HOST_CONFIG) & USBH_ENABLE_RD)) + udelay(1000); + + dbg("OHCI clock running\n"); + + 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 *)(USB_OHCI_BASE | 0xA0000000); + + gohci.flags = 0; + gohci.slot_name = "au1x00"; + + dbg("OHCI revision: 0x%08x\n" + " RH: a: 0x%08x b: 0x%08x\n", + readl(&gohci.regs->revision), + readl(&gohci.regs->roothub.a), readl(&gohci.regs->roothub.b)); + + if (hc_reset (&gohci) < 0) + goto errout; + + /* 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) + goto errout; + +#ifdef DEBUG + ohci_dump (&gohci, 1); +#else + wait_ms(1); +#endif + ohci_inited = 1; + return 0; + + errout: + err("OHCI initialization error\n"); + hc_release_ohci (&gohci); + /* Initialization failed */ + au_writel(readl(USB_HOST_CONFIG) & ~USBH_ENABLE_CE, USB_HOST_CONFIG); + return -1; +} + +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 */ + /* Disable clock */ + au_writel(readl(USB_HOST_CONFIG) & ~USBH_ENABLE_CE, USB_HOST_CONFIG); + return 0; +} + +#endif /* CONFIG_USB_OHCI */ diff --git a/cpu/mips/au1x00_usb_ohci.h b/cpu/mips/au1x00_usb_ohci.h new file mode 100644 index 0000000000..4ef06ffdeb --- /dev/null +++ b/cpu/mips/au1x00_usb_ohci.h @@ -0,0 +1,416 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2001 David Brownell + * + * 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; +} diff --git a/cpu/ppc4xx/4xx_enet.c b/cpu/ppc4xx/4xx_enet.c index 4809026134..34f6e973f4 100644 --- a/cpu/ppc4xx/4xx_enet.c +++ b/cpu/ppc4xx/4xx_enet.c @@ -1,80 +1,80 @@ /*-----------------------------------------------------------------------------+ * - * This source code has been made available to you by IBM on an AS-IS - * basis. Anyone receiving this source is licensed under IBM - * copyrights to use it in any way he or she deems fit, including - * copying it, modifying it, compiling it, and redistributing it either - * with or without modifications. No license under IBM patents or - * patent applications is to be implied by the copyright license. + * This source code has been made available to you by IBM on an AS-IS + * basis. Anyone receiving this source is licensed under IBM + * copyrights to use it in any way he or she deems fit, including + * copying it, modifying it, compiling it, and redistributing it either + * with or without modifications. No license under IBM patents or + * patent applications is to be implied by the copyright license. * - * Any user of this software should understand that IBM cannot provide - * technical support for this software and will not be responsible for - * any consequences resulting from the use of this software. + * Any user of this software should understand that IBM cannot provide + * technical support for this software and will not be responsible for + * any consequences resulting from the use of this software. * - * Any person who transfers this source code or any derivative work - * must include the IBM copyright notice, this paragraph, and the - * preceding two paragraphs in the transferred software. + * Any person who transfers this source code or any derivative work + * must include the IBM copyright notice, this paragraph, and the + * preceding two paragraphs in the transferred software. * - * COPYRIGHT I B M CORPORATION 1995 - * LICENSED MATERIAL - PROGRAM PROPERTY OF I B M + * COPYRIGHT I B M CORPORATION 1995 + * LICENSED MATERIAL - PROGRAM PROPERTY OF I B M *-----------------------------------------------------------------------------*/ /*-----------------------------------------------------------------------------+ * - * File Name: enetemac.c + * File Name: enetemac.c * - * Function: Device driver for the ethernet EMAC3 macro on the 405GP. + * Function: Device driver for the ethernet EMAC3 macro on the 405GP. * - * Author: Mark Wisner + * Author: Mark Wisner * * Change Activity- * - * Date Description of Change BY - * --------- --------------------- --- - * 05-May-99 Created MKW - * 27-Jun-99 Clean up JWB - * 16-Jul-99 Added MAL error recovery and better IP packet handling MKW - * 29-Jul-99 Added Full duplex support MKW - * 06-Aug-99 Changed names for Mal CR reg MKW - * 23-Aug-99 Turned off SYE when running at 10Mbs MKW - * 24-Aug-99 Marked descriptor empty after call_xlc MKW - * 07-Sep-99 Set MAL RX buffer size reg to ENET_MAX_MTU_ALIGNED / 16 MCG - * to avoid chaining maximum sized packets. Push starting - * RX descriptor address up to the next cache line boundary. - * 16-Jan-00 Added support for booting with IP of 0x0 MKW - * 15-Mar-00 Updated enetInit() to enable broadcast addresses in the - * EMAC_RXM register. JWB - * 12-Mar-01 anne-sophie.harnois@nextream.fr - * - Variables are compatible with those already defined in - * include/net.h - * - Receive buffer descriptor ring is used to send buffers - * to the user - * - Info print about send/received/handled packet number if - * INFO_405_ENET is set - * 17-Apr-01 stefan.roese@esd-electronics.com - * - MAL reset in "eth_halt" included - * - Enet speed and duplex output now in one line - * 08-May-01 stefan.roese@esd-electronics.com - * - MAL error handling added (eth_init called again) - * 13-Nov-01 stefan.roese@esd-electronics.com - * - Set IST bit in EMAC_M1 reg upon 100MBit or full duplex - * 04-Jan-02 stefan.roese@esd-electronics.com - * - Wait for PHY auto negotiation to complete added - * 06-Feb-02 stefan.roese@esd-electronics.com - * - Bug fixed in waiting for auto negotiation to complete - * 26-Feb-02 stefan.roese@esd-electronics.com - * - rx and tx buffer descriptors now allocated (no fixed address - * used anymore) - * 17-Jun-02 stefan.roese@esd-electronics.com - * - MAL error debug printf 'M' removed (rx de interrupt may - * occur upon many incoming packets with only 4 rx buffers). + * Date Description of Change BY + * --------- --------------------- --- + * 05-May-99 Created MKW + * 27-Jun-99 Clean up JWB + * 16-Jul-99 Added MAL error recovery and better IP packet handling MKW + * 29-Jul-99 Added Full duplex support MKW + * 06-Aug-99 Changed names for Mal CR reg MKW + * 23-Aug-99 Turned off SYE when running at 10Mbs MKW + * 24-Aug-99 Marked descriptor empty after call_xlc MKW + * 07-Sep-99 Set MAL RX buffer size reg to ENET_MAX_MTU_ALIGNED / 16 MCG + * to avoid chaining maximum sized packets. Push starting + * RX descriptor address up to the next cache line boundary. + * 16-Jan-00 Added support for booting with IP of 0x0 MKW + * 15-Mar-00 Updated enetInit() to enable broadcast addresses in the + * EMAC_RXM register. JWB + * 12-Mar-01 anne-sophie.harnois@nextream.fr + * - Variables are compatible with those already defined in + * include/net.h + * - Receive buffer descriptor ring is used to send buffers + * to the user + * - Info print about send/received/handled packet number if + * INFO_405_ENET is set + * 17-Apr-01 stefan.roese@esd-electronics.com + * - MAL reset in "eth_halt" included + * - Enet speed and duplex output now in one line + * 08-May-01 stefan.roese@esd-electronics.com + * - MAL error handling added (eth_init called again) + * 13-Nov-01 stefan.roese@esd-electronics.com + * - Set IST bit in EMAC_M1 reg upon 100MBit or full duplex + * 04-Jan-02 stefan.roese@esd-electronics.com + * - Wait for PHY auto negotiation to complete added + * 06-Feb-02 stefan.roese@esd-electronics.com + * - Bug fixed in waiting for auto negotiation to complete + * 26-Feb-02 stefan.roese@esd-electronics.com + * - rx and tx buffer descriptors now allocated (no fixed address + * used anymore) + * 17-Jun-02 stefan.roese@esd-electronics.com + * - MAL error debug printf 'M' removed (rx de interrupt may + * occur upon many incoming packets with only 4 rx buffers). *-----------------------------------------------------------------------------* - * 17-Nov-03 travis.sawyer@sandburst.com - * - ported from 405gp_enet.c to utilized upto 4 EMAC ports - * in the 440GX. This port should work with the 440GP - * (2 EMACs) also - * 15-Aug-05 sr@denx.de - * - merged 405gp_enet.c and 440gx_enet.c to generic 4xx_enet.c - now handling all 4xx cpu's. + * 17-Nov-03 travis.sawyer@sandburst.com + * - ported from 405gp_enet.c to utilized upto 4 EMAC ports + * in the 440GX. This port should work with the 440GP + * (2 EMACs) also + * 15-Aug-05 sr@denx.de + * - merged 405gp_enet.c and 440gx_enet.c to generic 4xx_enet.c + now handling all 4xx cpu's. *-----------------------------------------------------------------------------*/ #include @@ -100,7 +100,7 @@ #error "CONFIG_MII has to be defined!" #endif -#define EMAC_RESET_TIMEOUT 1000 /* 1000 ms reset timeout */ +#define EMAC_RESET_TIMEOUT 1000 /* 1000 ms reset timeout */ #define PHY_AUTONEGOTIATE_TIMEOUT 4000 /* 4000 ms autonegotiate timeout */ /* Ethernet Transmit and Receive Buffers */ @@ -108,12 +108,12 @@ * In the same way ENET_MAX_MTU and ENET_MAX_MTU_ALIGNED are set from * PKTSIZE and PKTSIZE_ALIGN (include/net.h) */ -#define ENET_MAX_MTU PKTSIZE +#define ENET_MAX_MTU PKTSIZE #define ENET_MAX_MTU_ALIGNED PKTSIZE_ALIGN /* define the number of channels implemented */ -#define EMAC_RXCHL EMAC_NUM_DEV -#define EMAC_TXCHL EMAC_NUM_DEV +#define EMAC_RXCHL EMAC_NUM_DEV +#define EMAC_TXCHL EMAC_NUM_DEV /*-----------------------------------------------------------------------------+ * Defines for MAL/EMAC interrupt conditions as reported in the UIC (Universal @@ -127,8 +127,8 @@ #undef INFO_4XX_ENET -#define BI_PHYMODE_NONE 0 -#define BI_PHYMODE_ZMII 1 +#define BI_PHYMODE_NONE 0 +#define BI_PHYMODE_ZMII 1 #define BI_PHYMODE_RGMII 2 @@ -322,7 +322,7 @@ static int ppc_4xx_eth_init (struct eth_device *dev, bd_t * bis) #ifdef INFO_4XX_ENET /* AS.HARNOIS * We should have : - * hw_p->stats.pkts_handled <= hw_p->stats.pkts_rx <= hw_p->stats.pkts_handled+PKTBUFSRX + * hw_p->stats.pkts_handled <= hw_p->stats.pkts_rx <= hw_p->stats.pkts_handled+PKTBUFSRX * In the most cases hw_p->stats.pkts_handled = hw_p->stats.pkts_rx, but it * is possible that new packets (without relationship with * current transfer) have got the time to arrived before @@ -341,8 +341,8 @@ static int ppc_4xx_eth_init (struct eth_device *dev, bd_t * bis) hw_p->stats.pkts_handled = 0; #endif - hw_p->tx_err_index = 0; /* Transmit Error Index for tx_err_log */ - hw_p->rx_err_index = 0; /* Receive Error Index for rx_err_log */ + hw_p->tx_err_index = 0; /* Transmit Error Index for tx_err_log */ + hw_p->rx_err_index = 0; /* Receive Error Index for rx_err_log */ hw_p->rx_slot = 0; /* MAL Receive Slot */ hw_p->rx_i_index = 0; /* Receive Interrupt Queue Index */ @@ -362,7 +362,7 @@ static int ppc_4xx_eth_init (struct eth_device *dev, bd_t * bis) udelay (100); #if defined(CONFIG_440EP) || defined(CONFIG_440GR) - out32 (ZMII_FER, (ZMII_FER_RMII | ZMII_FER_MDI) << ZMII_FER_V (devnum)); + out32 (ZMII_FER, (ZMII_FER_RMII | ZMII_FER_MDI) << ZMII_FER_V (devnum)); #elif defined(CONFIG_440GX) ethgroup = ppc_4xx_eth_setup_bridge(devnum, bis); #elif defined(CONFIG_440GP) @@ -641,7 +641,7 @@ static int ppc_4xx_eth_init (struct eth_device *dev, bd_t * bis) for (i = 0; i < NUM_RX_BUFF; i++) { hw_p->rx[i].ctrl = 0; hw_p->rx[i].data_len = 0; - /* rx[i].data_ptr = (char *) &rx_buff[i]; */ + /* rx[i].data_ptr = (char *) &rx_buff[i]; */ hw_p->rx[i].data_ptr = (char *) NetRxPackets[i]; if ((NUM_RX_BUFF - 1) == i) hw_p->rx[i].ctrl |= MAL_RX_CTRL_WRAP; @@ -757,7 +757,7 @@ static int ppc_4xx_eth_init (struct eth_device *dev, bd_t * bis) /* set transmit request threshold register */ out32 (EMAC_TRTR + hw_p->hw_addr, 0x18000000); /* 256 byte threshold */ - /* set receive low/high water mark register */ + /* set receive low/high water mark register */ #if defined(CONFIG_440) /* 440GP has a 64 byte burst length */ out32 (EMAC_RX_HI_LO_WMARK + hw_p->hw_addr, 0x80009000); @@ -1012,7 +1012,7 @@ int enetInt (struct eth_device *dev) /* check for EOB on valid channels */ if (my_uic0msr & UIC_MRE) { mal_rx_eob = mfdcr (malrxeobisr); - if ((mal_rx_eob & (0x80000000 >> hw_p->devnum)) != 0) { /* call emac routine for channel x */ + if ((mal_rx_eob & (0x80000000 >> hw_p->devnum)) != 0) { /* call emac routine for channel x */ /* clear EOB mtdcr(malrxeobisr, mal_rx_eob); */ enet_rcv (dev, emac_isr); @@ -1118,7 +1118,7 @@ int enetInt (struct eth_device *dev) if (my_uicmsr & UIC_MAL_RXEOB) { mal_rx_eob = mfdcr (malrxeobisr); - if ((mal_rx_eob & (0x80000000 >> hw_p->devnum)) != 0) { /* call emac routine for channel x */ + if ((mal_rx_eob & (0x80000000 >> hw_p->devnum)) != 0) { /* call emac routine for channel x */ /* clear EOB mtdcr(malrxeobisr, mal_rx_eob); */ enet_rcv (dev, emac_isr); @@ -1152,7 +1152,7 @@ static void mal_err (struct eth_device *dev, unsigned long isr, mtdcr (malrxdeir, 0x80000000); #ifdef INFO_4XX_ENET - printf ("\nMAL error occured.... ISR = %lx UIC = = %lx MAL_DEF = %lx MAL_ERR= %lx \n", isr, uic, maldef, mal_errr); + printf ("\nMAL error occured.... ISR = %lx UIC = = %lx MAL_DEF = %lx MAL_ERR= %lx \n", isr, uic, maldef, mal_errr); #endif eth_init (hw_p->bis); /* start again... */ @@ -1266,7 +1266,7 @@ static int ppc_4xx_eth_rx (struct eth_device *dev) unsigned long msr; EMAC_4XX_HW_PST hw_p = dev->priv; - hw_p->is_receiving = 1; /* tell driver */ + hw_p->is_receiving = 1; /* tell driver */ for (;;) { /* AS.HARNOIS @@ -1285,8 +1285,8 @@ static int ppc_4xx_eth_rx (struct eth_device *dev) length = hw_p->rx[user_index].data_len; /* Pass the packet up to the protocol layers. */ - /* NetReceive(NetRxPackets[rxIdx], length - 4); */ - /* NetReceive(NetRxPackets[i], length); */ + /* NetReceive(NetRxPackets[rxIdx], length - 4); */ + /* NetReceive(NetRxPackets[i], length); */ NetReceive (NetRxPackets[user_index], length - 4); /* Free Recv Buffer */ hw_p->rx[user_index].ctrl |= MAL_RX_CTRL_EMPTY; @@ -1303,7 +1303,7 @@ static int ppc_4xx_eth_rx (struct eth_device *dev) mtmsr (msr); /* Enable IRQ's */ } - hw_p->is_receiving = 0; /* tell driver */ + hw_p->is_receiving = 0; /* tell driver */ return length; } diff --git a/include/configs/pb1x00.h b/include/configs/pb1x00.h new file mode 100644 index 0000000000..f7de99b464 --- /dev/null +++ b/include/configs/pb1x00.h @@ -0,0 +1,189 @@ +/* + * (C) Copyright 2003 + * 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 + */ + +/* + * This file contains the configuration parameters for the dbau1x00 board. + */ + +#ifndef __CONFIG_H +#define __CONFIG_H + +#define CONFIG_MIPS32 1 /* MIPS32 CPU core */ +#define CONFIG_PB1X00 1 +#define CONFIG_AU1X00 1 /* alchemy series cpu */ + +#ifdef CONFIG_PB1000 +#define CONFIG_AU1000 1 +#else +#ifdef CONFIG_PB1100 +#define CONFIG_AU1100 1 +#else +#ifdef CONFIG_PB1500 +#define CONFIG_AU1500 1 +#else +#error "No valid board set" +#endif +#endif +#endif + +#define CONFIG_ETHADDR DE:AD:BE:EF:01:01 /* Ethernet address */ + +#define CONFIG_BOOTDELAY 2 /* autoboot after 2 seconds */ + +#define CONFIG_BAUDRATE 115200 + +/* valid baudrates */ +#define CFG_BAUDRATE_TABLE { 9600, 19200, 38400, 57600, 115200 } + +#define CONFIG_TIMESTAMP /* Print image info with timestamp */ +#undef CONFIG_BOOTARGS + +#define CONFIG_EXTRA_ENV_SETTINGS \ + "addmisc=setenv bootargs $(bootargs) " \ + "console=ttyS0,$(baudrate) " \ + "panic=1\0" \ + "bootfile=/vmlinux.img\0" \ + "load=tftp 80500000 $(u-boot)\0" \ + "" +/* Boot from NFS root */ +#define CONFIG_BOOTCOMMAND "bootp; setenv bootargs root=/dev/nfs rw nfsroot=$(serverip):$(rootpath) ip=$(ipaddr):$(serverip):$(gatewayip):$(netmask):$(hostname)::off; bootm" + +/* + * Miscellaneous configurable options + */ +#define CFG_LONGHELP /* undef to save memory */ +#define CFG_PROMPT "Pb1x00 # " /* Monitor Command Prompt */ +#define CFG_CBSIZE 256 /* Console I/O Buffer Size */ +#define CFG_PBSIZE (CFG_CBSIZE+sizeof(CFG_PROMPT)+16) /* Print Buffer Size */ +#define CFG_MAXARGS 16 /* max number of command args*/ + +#define CFG_MALLOC_LEN 128*1024 + +#define CFG_BOOTPARAMS_LEN 128*1024 + +#define CFG_HZ 396000000 /* FIXME causes overflow in net.c */ + +#define CFG_SDRAM_BASE 0x80000000 /* Cached addr */ + +#define CFG_LOAD_ADDR 0x81000000 /* default load address */ + +#define CFG_MEMTEST_START 0x80100000 +#undef CFG_MEMTEST_START +#define CFG_MEMTEST_START 0x80200000 +#define CFG_MEMTEST_END 0x83800000 + +/*----------------------------------------------------------------------- + * FLASH and environment organization + */ +#define CFG_MAX_FLASH_BANKS 2 /* max number of memory banks */ +#define CFG_MAX_FLASH_SECT (128) /* max number of sectors on one chip */ + +#define PHYS_FLASH_1 0xbec00000 /* Flash Bank #1 */ +#define PHYS_FLASH_2 0xbfc00000 /* Flash Bank #2 */ + +/* The following #defines are needed to get flash environment right */ +#define CFG_MONITOR_BASE TEXT_BASE +#define CFG_MONITOR_LEN (192 << 10) + +#define CFG_INIT_SP_OFFSET 0x4000000 + +/* We boot from this flash, selected with dip switch */ +#define CFG_FLASH_BASE PHYS_FLASH_2 + +/* timeout values are in ticks */ +#define CFG_FLASH_ERASE_TOUT (2 * CFG_HZ) /* Timeout for Flash Erase */ +#define CFG_FLASH_WRITE_TOUT (2 * CFG_HZ) /* Timeout for Flash Write */ + +#define CFG_ENV_IS_NOWHERE 1 + +/* Address and size of Primary Environment Sector */ +#define CFG_ENV_ADDR 0xB0030000 +#define CFG_ENV_SIZE 0x10000 + +#define CONFIG_FLASH_16BIT + +#define CONFIG_NR_DRAM_BANKS 2 + +#define CONFIG_NET_MULTI + +#define CONFIG_MEMSIZE_IN_BYTES + + +/*---USB -------------------------------------------*/ +#if 0 +#define CONFIG_USB_OHCI +#define ADD_USB_CMD CFG_CMD_USB | CFG_CMD_FAT +#define CONFIG_USB_STORAGE +#define CONFIG_DOS_PARTITION +#else +#define ADD_USB_CMD 0 +#endif + +/*---ATA PCMCIA ------------------------------------*/ +#if 0 +#define CFG_PCMCIA_MEM_SIZE 0x4000000 /* Offset to slot 1 FIXME!!! */ +#define CFG_PCMCIA_MEM_ADDR 0x20000000 +#define CONFIG_PCMCIA_SLOT_A + +#define CONFIG_ATAPI 1 +#define CONFIG_MAC_PARTITION 1 + +/* We run CF in "true ide" mode or a harddrive via pcmcia */ +#define CONFIG_IDE_PCMCIA 1 + +/* We only support one slot for now */ +#define CFG_IDE_MAXBUS 1 /* max. 1 IDE bus */ +#define CFG_IDE_MAXDEVICE 1 /* max. 1 drive per IDE bus */ + +#undef CONFIG_IDE_LED /* LED for ide not supported */ +#undef CONFIG_IDE_RESET /* reset for ide not supported */ + +#define CFG_ATA_IDE0_OFFSET 0x0000 + +#define CFG_ATA_BASE_ADDR CFG_PCMCIA_MEM_ADDR + +/* Offset for data I/O */ +#define CFG_ATA_DATA_OFFSET 8 + +/* Offset for normal register accesses */ +#define CFG_ATA_REG_OFFSET 0 + +/* Offset for alternate registers */ +#define CFG_ATA_ALT_OFFSET 0x0100 + +#endif +/*----------------------------------------------------------------------- + * Cache Configuration + */ +#define CFG_DCACHE_SIZE 16384 +#define CFG_ICACHE_SIZE 16384 +#define CFG_CACHELINE_SIZE 32 + +#define CONFIG_COMMANDS \ + (((CONFIG_CMD_DFL | CFG_CMD_DHCP | CFG_CMD_ELF | CFG_CMD_MII | CFG_CMD_PING) & \ + ~(CFG_CMD_ENV | CFG_CMD_FAT | CFG_CMD_FLASH | CFG_CMD_FPGA | CFG_CMD_IDE | \ + CFG_CMD_LOADS | CFG_CMD_RUN | CFG_CMD_LOADB | CFG_CMD_ELF | \ + CFG_CMD_BDI | CFG_CMD_BEDBUG)) | ADD_USB_CMD) +#include + +#endif /* __CONFIG_H */