diff options
| author | Peter Tyser <ptyser@xes-inc.com> | 2010-04-12 22:28:14 -0500 | 
|---|---|---|
| committer | Wolfgang Denk <wd@denx.de> | 2010-04-13 09:13:25 +0200 | 
| commit | 1e3827d9cf9442e188604fd1099ac38375135125 (patch) | |
| tree | 4d5f6b1b99cd486c00c4c04145b31a2091ce392d /arch/mips/cpu | |
| parent | c6fb83d21729321426308c3acff2a3dfb20d250b (diff) | |
mips: Move cpu/mips/* to arch/mips/cpu/*
Signed-off-by: Peter Tyser <ptyser@xes-inc.com>
Diffstat (limited to 'arch/mips/cpu')
| -rw-r--r-- | arch/mips/cpu/Makefile | 53 | ||||
| -rw-r--r-- | arch/mips/cpu/asc_serial.c | 368 | ||||
| -rw-r--r-- | arch/mips/cpu/asc_serial.h | 177 | ||||
| -rw-r--r-- | arch/mips/cpu/au1x00_eth.c | 311 | ||||
| -rw-r--r-- | arch/mips/cpu/au1x00_serial.c | 131 | ||||
| -rw-r--r-- | arch/mips/cpu/au1x00_usb_ohci.c | 1726 | ||||
| -rw-r--r-- | arch/mips/cpu/au1x00_usb_ohci.h | 416 | ||||
| -rw-r--r-- | arch/mips/cpu/cache.S | 331 | ||||
| -rw-r--r-- | arch/mips/cpu/config.mk | 39 | ||||
| -rw-r--r-- | arch/mips/cpu/cpu.c | 112 | ||||
| -rw-r--r-- | arch/mips/cpu/incaip_clock.c | 116 | ||||
| -rw-r--r-- | arch/mips/cpu/incaip_wdt.S | 71 | ||||
| -rw-r--r-- | arch/mips/cpu/interrupts.c | 33 | ||||
| -rw-r--r-- | arch/mips/cpu/start.S | 421 | 
14 files changed, 4305 insertions, 0 deletions
| diff --git a/arch/mips/cpu/Makefile b/arch/mips/cpu/Makefile new file mode 100644 index 00000000000..28a1cbb1042 --- /dev/null +++ b/arch/mips/cpu/Makefile @@ -0,0 +1,53 @@ +# +# (C) Copyright 2003-2006 +# 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	= $(obj)lib$(CPU).a + +START	= start.o +SOBJS-y	= cache.o +COBJS-y	= cpu.o interrupts.o + +SOBJS-$(CONFIG_INCA_IP)	+= incaip_wdt.o +COBJS-$(CONFIG_INCA_IP)	+= asc_serial.o incaip_clock.o +COBJS-$(CONFIG_PURPLE)	+= asc_serial.o +COBJS-$(CONFIG_SOC_AU1X00) += au1x00_eth.o au1x00_serial.o au1x00_usb_ohci.o + +SRCS	:= $(START:.o=.S) $(SOBJS-y:.o=.S) $(COBJS-y:.o=.c) +OBJS	:= $(addprefix $(obj),$(SOBJS-y) $(COBJS-y)) +START	:= $(addprefix $(obj),$(START)) + +all:	$(obj).depend $(START) $(LIB) + +$(LIB):	$(OBJS) +	$(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/arch/mips/cpu/asc_serial.c b/arch/mips/cpu/asc_serial.c new file mode 100644 index 00000000000..be686c2ae8d --- /dev/null +++ b/arch/mips/cpu/asc_serial.c @@ -0,0 +1,368 @@ +/* + * (INCA) ASC UART support + */ + +#include <config.h> + +#ifdef CONFIG_PURPLE +#define	serial_init	asc_serial_init +#define	serial_putc	asc_serial_putc +#define	serial_puts	asc_serial_puts +#define	serial_getc	asc_serial_getc +#define	serial_tstc	asc_serial_tstc +#define	serial_setbrg	asc_serial_setbrg +#endif + +#include <common.h> +#include <asm/inca-ip.h> +#include "asc_serial.h" + +#ifdef CONFIG_PURPLE + +#undef ASC_FIFO_PRESENT +#define TOUT_LOOP	100000 + +/* Set base address for second FPI interrupt control register bank */ +#define SFPI_INTCON_BASEADDR	0xBF0F0000 + +/* Register offset from base address */ +#define FBS_ISR		0x00000000	/* Interrupt status register */ +#define FBS_IMR		0x00000008	/* Interrupt mask register */ +#define FBS_IDIS	0x00000010	/* Interrupt disable register */ + +/* Interrupt status register bits */ +#define FBS_ISR_AT	0x00000040	/* ASC transmit interrupt */ +#define FBS_ISR_AR	0x00000020	/* ASC receive interrupt */ +#define FBS_ISR_AE	0x00000010	/* ASC error interrupt */ +#define FBS_ISR_AB	0x00000008	/* ASC transmit buffer interrupt */ +#define FBS_ISR_AS      0x00000004	/* ASC start of autobaud detection interrupt */ +#define FBS_ISR_AF	0x00000002	/* ASC end of autobaud detection interrupt */ + +#else + +#define ASC_FIFO_PRESENT + +#endif + + +#define SET_BIT(reg, mask)                  reg |= (mask) +#define CLEAR_BIT(reg, mask)                reg &= (~mask) +#define CLEAR_BITS(reg, mask)               CLEAR_BIT(reg, mask) +#define SET_BITS(reg, mask)                 SET_BIT(reg, mask) +#define SET_BITFIELD(reg, mask, off, val)   {reg &= (~mask); reg |= (val << off);} + +extern uint incaip_get_fpiclk(void); + +static int serial_setopt (void); + +/* pointer to ASC register base address */ +static volatile incaAsc_t *pAsc = (incaAsc_t *)INCA_IP_ASC; + +/****************************************************************************** +* +* serial_init - initialize a INCAASC channel +* +* This routine initializes the number of data bits, parity +* and set the selected baud rate. Interrupts are disabled. +* Set the modem control signals if the option is selected. +* +* RETURNS: N/A +*/ + +int serial_init (void) +{ +#ifdef CONFIG_INCA_IP +    /* we have to set PMU.EN13 bit to enable an ASC device*/ +    INCAASC_PMU_ENABLE(13); +#endif + +    /* and we have to set CLC register*/ +    CLEAR_BIT(pAsc->asc_clc, ASCCLC_DISS); +    SET_BITFIELD(pAsc->asc_clc, ASCCLC_RMCMASK, ASCCLC_RMCOFFSET, 0x0001); + +    /* initialy we are in async mode */ +    pAsc->asc_con = ASCCON_M_8ASYNC; + +    /* select input port */ +    pAsc->asc_pisel = (CONSOLE_TTY & 0x1); + +#ifdef ASC_FIFO_PRESENT +    /* TXFIFO's filling level */ +    SET_BITFIELD(pAsc->asc_txfcon, ASCTXFCON_TXFITLMASK, +		    ASCTXFCON_TXFITLOFF, INCAASC_TXFIFO_FL); +    /* enable TXFIFO */ +    SET_BIT(pAsc->asc_txfcon, ASCTXFCON_TXFEN); + +    /* RXFIFO's filling level */ +    SET_BITFIELD(pAsc->asc_txfcon, ASCRXFCON_RXFITLMASK, +		    ASCRXFCON_RXFITLOFF, INCAASC_RXFIFO_FL); +    /* enable RXFIFO */ +    SET_BIT(pAsc->asc_rxfcon, ASCRXFCON_RXFEN); +#endif + +    /* enable error signals */ +    SET_BIT(pAsc->asc_con, ASCCON_FEN); +    SET_BIT(pAsc->asc_con, ASCCON_OEN); + +#ifdef CONFIG_INCA_IP +    /* acknowledge ASC interrupts */ +    ASC_INTERRUPTS_CLEAR(INCAASC_IRQ_LINE_ALL); + +    /* disable ASC interrupts */ +    ASC_INTERRUPTS_DISABLE(INCAASC_IRQ_LINE_ALL); +#endif + +#ifdef ASC_FIFO_PRESENT +    /* set FIFOs into the transparent mode */ +    SET_BIT(pAsc->asc_txfcon, ASCTXFCON_TXTMEN); +    SET_BIT(pAsc->asc_rxfcon, ASCRXFCON_RXTMEN); +#endif + +    /* set baud rate */ +    serial_setbrg(); + +    /* set the options */ +    serial_setopt(); + +    return 0; +} + +void serial_setbrg (void) +{ +    ulong      uiReloadValue, fdv; +    ulong      f_ASC; + +#ifdef CONFIG_INCA_IP +    f_ASC = incaip_get_fpiclk(); +#else +    f_ASC = ASC_CLOCK_RATE; +#endif + +#ifndef INCAASC_USE_FDV +    fdv = 2; +    uiReloadValue = (f_ASC / (fdv * 16 * CONFIG_BAUDRATE)) - 1; +#else +    fdv = INCAASC_FDV_HIGH_BAUDRATE; +    uiReloadValue = (f_ASC / (8192 * CONFIG_BAUDRATE / fdv)) - 1; +#endif /* INCAASC_USE_FDV */ + +    if ( (uiReloadValue < 0) || (uiReloadValue > 8191) ) +    { +#ifndef INCAASC_USE_FDV +	fdv = 3; +	uiReloadValue = (f_ASC / (fdv * 16 * CONFIG_BAUDRATE)) - 1; +#else +	fdv = INCAASC_FDV_LOW_BAUDRATE; +	uiReloadValue = (f_ASC / (8192 * CONFIG_BAUDRATE / fdv)) - 1; +#endif /* INCAASC_USE_FDV */ + +	if ( (uiReloadValue < 0) || (uiReloadValue > 8191) ) +	{ +	    return;    /* can't impossibly generate that baud rate */ +	} +    } + +    /* Disable Baud Rate Generator; BG should only be written when R=0 */ +    CLEAR_BIT(pAsc->asc_con, ASCCON_R); + +#ifndef INCAASC_USE_FDV +    /* +     * Disable Fractional Divider (FDE) +     * Divide clock by reload-value + constant (BRS) +     */ +    /* FDE = 0 */ +    CLEAR_BIT(pAsc->asc_con, ASCCON_FDE); + +    if ( fdv == 2 ) +	CLEAR_BIT(pAsc->asc_con, ASCCON_BRS);   /* BRS = 0 */ +    else +	SET_BIT(pAsc->asc_con, ASCCON_BRS); /* BRS = 1 */ + +#else /* INCAASC_USE_FDV */ + +    /* Enable Fractional Divider */ +    SET_BIT(pAsc->asc_con, ASCCON_FDE); /* FDE = 1 */ + +    /* Set fractional divider value */ +    pAsc->asc_fdv = fdv & ASCFDV_VALUE_MASK; + +#endif /* INCAASC_USE_FDV */ + +    /* Set reload value in BG */ +    pAsc->asc_bg = uiReloadValue; + +    /* Enable Baud Rate Generator */ +    SET_BIT(pAsc->asc_con, ASCCON_R);           /* R = 1 */ +} + +/******************************************************************************* +* +* serial_setopt - set the serial options +* +* Set the channel operating mode to that specified. Following options +* are supported: CREAD, CSIZE, PARENB, and PARODD. +* +* Note, this routine disables the transmitter.  The calling routine +* may have to re-enable it. +* +* RETURNS: +* Returns 0 to indicate success, otherwise -1 is returned +*/ + +static int serial_setopt (void) +{ +    ulong  con; + +    switch ( ASC_OPTIONS & ASCOPT_CSIZE ) +    { +    /* 7-bit-data */ +    case ASCOPT_CS7: +	con = ASCCON_M_7ASYNCPAR;   /* 7-bit-data and parity bit */ +	break; + +    /* 8-bit-data */ +    case ASCOPT_CS8: +	if ( ASC_OPTIONS & ASCOPT_PARENB ) +	    con = ASCCON_M_8ASYNCPAR;   /* 8-bit-data and parity bit */ +	else +	    con = ASCCON_M_8ASYNC;      /* 8-bit-data no parity */ +	break; + +    /* +     *  only 7 and 8-bit frames are supported +     *  if we don't use IOCTL extensions +     */ +    default: +	return -1; +    } + +    if ( ASC_OPTIONS & ASCOPT_STOPB ) +	SET_BIT(con, ASCCON_STP);       /* 2 stop bits */ +    else +	CLEAR_BIT(con, ASCCON_STP);     /* 1 stop bit */ + +    if ( ASC_OPTIONS & ASCOPT_PARENB ) +	SET_BIT(con, ASCCON_PEN);           /* enable parity checking */ +    else +	CLEAR_BIT(con, ASCCON_PEN);         /* disable parity checking */ + +    if ( ASC_OPTIONS & ASCOPT_PARODD ) +	SET_BIT(con, ASCCON_ODD);       /* odd parity */ +    else +	CLEAR_BIT(con, ASCCON_ODD);     /* even parity */ + +    if ( ASC_OPTIONS & ASCOPT_CREAD ) +	SET_BIT(pAsc->asc_whbcon, ASCWHBCON_SETREN); /* Receiver enable */ + +    pAsc->asc_con |= con; + +    return 0; +} + +void serial_putc (const char c) +{ +#ifdef ASC_FIFO_PRESENT +    uint txFl = 0; +#else +    uint timeout = 0; +#endif + +    if (c == '\n') serial_putc ('\r'); + +#ifdef ASC_FIFO_PRESENT +    /* check do we have a free space in the TX FIFO */ +    /* get current filling level */ +    do +    { +	txFl = ( pAsc->asc_fstat & ASCFSTAT_TXFFLMASK ) >> ASCFSTAT_TXFFLOFF; +    } +    while ( txFl == INCAASC_TXFIFO_FULL ); +#else + +    while(!(*(volatile unsigned long*)(SFPI_INTCON_BASEADDR + FBS_ISR) & +			   FBS_ISR_AB)) +    { +	    if (timeout++ > TOUT_LOOP) +	    { +		    break; +	    } +    } +#endif + +    pAsc->asc_tbuf = c; /* write char to Transmit Buffer Register */ + +#ifndef ASC_FIFO_PRESENT +    *(volatile unsigned long*)(SFPI_INTCON_BASEADDR + FBS_ISR) = FBS_ISR_AB | +								 FBS_ISR_AT; +#endif + +    /* check for errors */ +    if ( pAsc->asc_con & ASCCON_OE ) +    { +	SET_BIT(pAsc->asc_whbcon, ASCWHBCON_CLROE); +	return; +    } +} + +void serial_puts (const char *s) +{ +    while (*s) +    { +	serial_putc (*s++); +    } +} + +int serial_getc (void) +{ +    ulong symbol_mask; +    char c; + +    while (!serial_tstc()); + +    symbol_mask = +	((ASC_OPTIONS & ASCOPT_CSIZE) == ASCOPT_CS7) ? (0x7f) : (0xff); + +    c = (char)(pAsc->asc_rbuf & symbol_mask); + +#ifndef ASC_FIFO_PRESENT +    *(volatile unsigned long*)(SFPI_INTCON_BASEADDR + FBS_ISR) = FBS_ISR_AR; +#endif + +    return c; +} + +int serial_tstc (void) +{ +    int res = 1; + +#ifdef ASC_FIFO_PRESENT +    if ( (pAsc->asc_fstat & ASCFSTAT_RXFFLMASK) == 0 ) +    { +	res = 0; +    } +#else +    if (!(*(volatile unsigned long*)(SFPI_INTCON_BASEADDR + FBS_ISR) & +								FBS_ISR_AR)) + +    { +	res = 0; +    } +#endif +    else if ( pAsc->asc_con & ASCCON_FE ) +    { +	SET_BIT(pAsc->asc_whbcon, ASCWHBCON_CLRFE); +	res = 0; +    } +    else if ( pAsc->asc_con & ASCCON_PE ) +    { +	SET_BIT(pAsc->asc_whbcon, ASCWHBCON_CLRPE); +	res = 0; +    } +    else if ( pAsc->asc_con & ASCCON_OE ) +    { +	SET_BIT(pAsc->asc_whbcon, ASCWHBCON_CLROE); +	res = 0; +    } + +    return res; +} diff --git a/arch/mips/cpu/asc_serial.h b/arch/mips/cpu/asc_serial.h new file mode 100644 index 00000000000..7ffdcfaf8b7 --- /dev/null +++ b/arch/mips/cpu/asc_serial.h @@ -0,0 +1,177 @@ +/* incaAscSio.h - (INCA) ASC UART tty driver header */ + +#ifndef __INCincaAscSioh +#define __INCincaAscSioh + +#include <asm/inca-ip.h> + +/* channel operating modes */ +#define	ASCOPT_CSIZE	0x00000003 +#define	ASCOPT_CS7	0x00000001 +#define	ASCOPT_CS8	0x00000002 +#define	ASCOPT_PARENB	0x00000004 +#define	ASCOPT_STOPB	0x00000008 +#define	ASCOPT_PARODD	0x00000010 +#define	ASCOPT_CREAD	0x00000020 + +#define ASC_OPTIONS		(ASCOPT_CREAD | ASCOPT_CS8) + +/* ASC input select (0 or 1) */ +#define CONSOLE_TTY	0 + +/* use fractional divider for baudrate settings */ +#define INCAASC_USE_FDV + +#ifdef INCAASC_USE_FDV +   #define INCAASC_FDV_LOW_BAUDRATE        71 +   #define INCAASC_FDV_HIGH_BAUDRATE       453 +#endif /*INCAASC_USE_FDV*/ + + +#define INCAASC_TXFIFO_FL       1 +#define INCAASC_RXFIFO_FL       1 +#define INCAASC_TXFIFO_FULL     16 + +/* interrupt lines masks for the ASC device interrupts*/ +/* change these macroses if it's necessary */ +#define INCAASC_IRQ_LINE_ALL        0x000F0000  /* all IRQs */ + +#define INCAASC_IRQ_LINE_TIR            0x00010000      /* TIR - Tx */ +#define INCAASC_IRQ_LINE_RIR            0x00020000      /* RIR - Rx */ +#define INCAASC_IRQ_LINE_EIR            0x00040000      /* EIR - Err */ +#define INCAASC_IRQ_LINE_TBIR           0x00080000      /* TBIR - Tx Buf*/ + +/* interrupt controller access macros */ +#define ASC_INTERRUPTS_ENABLE(X)  \ +   *((volatile unsigned int*) INCA_IP_ICU_IM2_IER) |= X; +#define ASC_INTERRUPTS_DISABLE(X) \ +   *((volatile unsigned int*) INCA_IP_ICU_IM2_IER) &= ~X; +#define ASC_INTERRUPTS_CLEAR(X)   \ +   *((volatile unsigned int*) INCA_IP_ICU_IM2_ISR) = X; + +/* CLC register's bits and bitfields */ +#define ASCCLC_DISR        0x00000001 +#define ASCCLC_DISS        0x00000002 +#define ASCCLC_RMCMASK     0x0000FF00 +#define ASCCLC_RMCOFFSET   8 + +/* CON register's bits and bitfields */ +#define ASCCON_MODEMASK 0x0007 +    #define ASCCON_M_8SYNC          0x0 +    #define ASCCON_M_8ASYNC         0x1 +    #define ASCCON_M_8IRDAASYNC     0x2 +    #define ASCCON_M_7ASYNCPAR      0x3 +    #define ASCCON_M_9ASYNC         0x4 +    #define ASCCON_M_8WAKEUPASYNC   0x5 +    #define ASCCON_M_8ASYNCPAR      0x7 +#define ASCCON_STP      0x0008 +#define ASCCON_REN      0x0010 +#define ASCCON_PEN      0x0020 +#define ASCCON_FEN      0x0040 +#define ASCCON_OEN      0x0080 +#define ASCCON_PE       0x0100 +#define ASCCON_FE       0x0200 +#define ASCCON_OE       0x0400 +#define ASCCON_FDE      0x0800 +#define ASCCON_ODD      0x1000 +#define ASCCON_BRS      0x2000 +#define ASCCON_LB       0x4000 +#define ASCCON_R        0x8000 + +/* WHBCON register's bits and bitfields */ +#define ASCWHBCON_CLRREN    0x0010 +#define ASCWHBCON_SETREN    0x0020 +#define ASCWHBCON_CLRPE     0x0100 +#define ASCWHBCON_CLRFE     0x0200 +#define ASCWHBCON_CLROE     0x0400 +#define ASCWHBCON_SETPE     0x0800 +#define ASCWHBCON_SETFE     0x1000 +#define ASCWHBCON_SETOE     0x2000 + +/* ABCON register's bits and bitfields */ +#define ASCABCON_ABEN       0x0001 +#define ASCABCON_AUREN      0x0002 +#define ASCABCON_ABSTEN     0x0004 +#define ASCABCON_ABDETEN    0x0008 +#define ASCABCON_FCDETEN    0x0010 +#define ASCABCON_EMMASK     0x0300 +    #define ASCABCON_EMOFF          8 +	#define ASCABCON_EM_DISAB       0x0 +	#define ASCABCON_EM_DURAB       0x1 +	#define ASCABCON_EM_ALWAYS      0x2 +#define ASCABCON_TXINV      0x0400 +#define ASCABCON_RXINV      0x0800 + +/* FDV register mask, offset and bitfields*/ +#define ASCFDV_VALUE_MASK     0x000001FF + +/* WHBABCON register's bits and bitfields */ +#define ASCWHBABCON_SETABEN     0x0001 +#define ASCWHBABCON_CLRABEN     0x0002 + +/* ABSTAT register's bits and bitfields */ +#define ASCABSTAT_FCSDET    0x0001 +#define ASCABSTAT_FCCDET    0x0002 +#define ASCABSTAT_SCSDET    0x0004 +#define ASCABSTAT_SCCDET    0x0008 +#define ASCABSTAT_DETWAIT   0x0010 + +/* WHBABSTAT register's bits and bitfields */ +#define ASCWHBABSTAT_CLRFCSDET  0x0001 +#define ASCWHBABSTAT_SETFCSDET  0x0002 +#define ASCWHBABSTAT_CLRFCCDET  0x0004 +#define ASCWHBABSTAT_SETFCCDET  0x0008 +#define ASCWHBABSTAT_CLRSCSDET  0x0010 +#define ASCWHBABSTAT_SETSCSDET  0x0020 +#define ASCWHBABSTAT_SETSCCDET  0x0040 +#define ASCWHBABSTAT_CLRSCCDET  0x0080 +#define ASCWHBABSTAT_CLRDETWAIT 0x0100 +#define ASCWHBABSTAT_SETDETWAIT 0x0200 + +/* TXFCON register's bits and bitfields */ +#define ASCTXFCON_TXFEN         0x0001 +#define ASCTXFCON_TXFFLU        0x0002 +#define ASCTXFCON_TXTMEN        0x0004 +#define ASCTXFCON_TXFITLMASK    0x3F00 +#define ASCTXFCON_TXFITLOFF     8 + +/* RXFCON register's bits and bitfields */ +#define ASCRXFCON_RXFEN         0x0001 +#define ASCRXFCON_RXFFLU        0x0002 +#define ASCRXFCON_RXTMEN        0x0004 +#define ASCRXFCON_RXFITLMASK    0x3F00 +#define ASCRXFCON_RXFITLOFF     8 + +/* FSTAT register's bits and bitfields */ +#define ASCFSTAT_RXFFLMASK      0x003F +#define ASCFSTAT_TXFFLMASK      0x3F00 +#define ASCFSTAT_TXFFLOFF       8 + +#define INCAASC_PMU_ENABLE(BIT) *((volatile ulong*)0xBF102000) |= (0x1 << BIT); + +typedef  struct         /* incaAsc_t */ +{ +    volatile unsigned long  asc_clc;                            /*0x0000*/ +    volatile unsigned long  asc_pisel;                          /*0x0004*/ +    volatile unsigned long  asc_rsvd1[2];   /* for mapping */   /*0x0008*/ +    volatile unsigned long  asc_con;                            /*0x0010*/ +    volatile unsigned long  asc_bg;                             /*0x0014*/ +    volatile unsigned long  asc_fdv;                            /*0x0018*/ +    volatile unsigned long  asc_pmw;        /* not used */      /*0x001C*/ +    volatile unsigned long  asc_tbuf;                           /*0x0020*/ +    volatile unsigned long  asc_rbuf;                           /*0x0024*/ +    volatile unsigned long  asc_rsvd2[2];   /* for mapping */   /*0x0028*/ +    volatile unsigned long  asc_abcon;                          /*0x0030*/ +    volatile unsigned long  asc_abstat;     /* not used */      /*0x0034*/ +    volatile unsigned long  asc_rsvd3[2];   /* for mapping */   /*0x0038*/ +    volatile unsigned long  asc_rxfcon;                         /*0x0040*/ +    volatile unsigned long  asc_txfcon;                         /*0x0044*/ +    volatile unsigned long  asc_fstat;                          /*0x0048*/ +    volatile unsigned long  asc_rsvd4;      /* for mapping */   /*0x004C*/ +    volatile unsigned long  asc_whbcon;                         /*0x0050*/ +    volatile unsigned long  asc_whbabcon;                       /*0x0054*/ +    volatile unsigned long  asc_whbabstat;  /* not used */      /*0x0058*/ + +} incaAsc_t; + +#endif /* __INCincaAscSioh */ diff --git a/arch/mips/cpu/au1x00_eth.c b/arch/mips/cpu/au1x00_eth.c new file mode 100644 index 00000000000..5074997a29b --- /dev/null +++ b/arch/mips/cpu/au1x00_eth.c @@ -0,0 +1,311 @@ +/* Only eth0 supported for now + * + * (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 <config.h> + +#if defined(CONFIG_SYS_DISCOVER_PHY) +#error "PHY not supported yet" +/* We just assume that we are running 100FD for now */ +/* We all use switches, right? ;-) */ +#endif + +/* I assume ethernet behaves like au1000 */ + +#ifdef CONFIG_SOC_AU1000 +/* Base address differ between cpu:s */ +#define ETH0_BASE AU1000_ETH0_BASE +#define MAC0_ENABLE AU1000_MAC0_ENABLE +#else +#ifdef CONFIG_SOC_AU1100 +#define ETH0_BASE AU1100_ETH0_BASE +#define MAC0_ENABLE AU1100_MAC0_ENABLE +#else +#ifdef CONFIG_SOC_AU1500 +#define ETH0_BASE AU1500_ETH0_BASE +#define MAC0_ENABLE AU1500_MAC0_ENABLE +#else +#ifdef CONFIG_SOC_AU1550 +#define ETH0_BASE AU1550_ETH0_BASE +#define MAC0_ENABLE AU1550_MAC0_ENABLE +#else +#error "No valid cpu set" +#endif +#endif +#endif +#endif + +#include <common.h> +#include <malloc.h> +#include <net.h> +#include <command.h> +#include <asm/io.h> +#include <asm/au1x00.h> + +#if defined(CONFIG_CMD_MII) +#include <miiphy.h> +#endif + +/* Ethernet Transmit and Receive Buffers */ +#define DBUF_LENGTH  1520 +#define PKT_MAXBUF_SIZE		1518 + +static char txbuf[DBUF_LENGTH]; + +static int next_tx; +static int next_rx; + +/* 4 rx and 4 tx fifos */ +#define NO_OF_FIFOS 4 + +typedef struct{ +	u32 status; +	u32 addr; +	u32 len; /* Only used for tx */ +	u32 not_used; +} mac_fifo_t; + +mac_fifo_t mac_fifo[NO_OF_FIFOS]; + +#define MAX_WAIT 1000 + +#if defined(CONFIG_CMD_MII) +int  au1x00_miiphy_read(char *devname, 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  au1x00_miiphy_write(char *devname, 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 -1; +		} +	} + +	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 + +static int au1x00_send(struct eth_device* dev, volatile void *packet, int length){ +	volatile mac_fifo_t *fifo_tx = +		(volatile mac_fifo_t*)(MAC0_TX_DMA_ADDR+MAC_TX_BUFF0_STATUS); +	int i; +	int res; + +	/* tx fifo should always be idle */ +	fifo_tx[next_tx].len = length; +	fifo_tx[next_tx].addr = (virt_to_phys(packet))|TX_DMA_ENABLE; +	au_sync(); + +	udelay(1); +	i=0; +	while(!(fifo_tx[next_tx].addr&TX_T_DONE)){ +		if(i>MAX_WAIT){ +			printf("TX timeout\n"); +			break; +		} +		udelay(1); +		i++; +	} + +	/* Clear done bit */ +	fifo_tx[next_tx].addr = 0; +	fifo_tx[next_tx].len = 0; +	au_sync(); + +	res = fifo_tx[next_tx].status; + +	next_tx++; +	if(next_tx>=NO_OF_FIFOS){ +		next_tx=0; +	} +	return(res); +} + +static int au1x00_recv(struct eth_device* dev){ +	volatile mac_fifo_t *fifo_rx = +		(volatile mac_fifo_t*)(MAC0_RX_DMA_ADDR+MAC_RX_BUFF0_STATUS); + +	int length; +	u32 status; + +	for(;;){ +		if(!(fifo_rx[next_rx].addr&RX_T_DONE)){ +			/* Nothing has been received */ +			return(-1); +		} + +		status = fifo_rx[next_rx].status; + +		length = status&0x3FFF; + +		if(status&RX_ERROR){ +			printf("Rx error 0x%x\n", status); +		} +		else{ +			/* Pass the packet up to the protocol layers. */ +			NetReceive(NetRxPackets[next_rx], length - 4); +		} + +		fifo_rx[next_rx].addr = (virt_to_phys(NetRxPackets[next_rx]))|RX_DMA_ENABLE; + +		next_rx++; +		if(next_rx>=NO_OF_FIFOS){ +			next_rx=0; +		} +	} /* for */ + +	return(0); /* Does anyone use this? */ +} + +static int au1x00_init(struct eth_device* dev, bd_t * bd){ + +	volatile u32 *macen = (volatile u32*)MAC0_ENABLE; +	volatile u32 *mac_ctrl = (volatile u32*)(ETH0_BASE+MAC_CONTROL); +	volatile u32 *mac_addr_high = (volatile u32*)(ETH0_BASE+MAC_ADDRESS_HIGH); +	volatile u32 *mac_addr_low = (volatile u32*)(ETH0_BASE+MAC_ADDRESS_LOW); +	volatile u32 *mac_mcast_high = (volatile u32*)(ETH0_BASE+MAC_MCAST_HIGH); +	volatile u32 *mac_mcast_low = (volatile u32*)(ETH0_BASE+MAC_MCAST_LOW); +	volatile mac_fifo_t *fifo_tx = +		(volatile mac_fifo_t*)(MAC0_TX_DMA_ADDR+MAC_TX_BUFF0_STATUS); +	volatile mac_fifo_t *fifo_rx = +		(volatile mac_fifo_t*)(MAC0_RX_DMA_ADDR+MAC_RX_BUFF0_STATUS); +	int i; + +	next_tx = TX_GET_DMA_BUFFER(fifo_tx[0].addr); +	next_rx = RX_GET_DMA_BUFFER(fifo_rx[0].addr); + +	/* We have to enable clocks before releasing reset */ +	*macen = MAC_EN_CLOCK_ENABLE; +	udelay(10); + +	/* Enable MAC0 */ +	/* We have to release reset before accessing registers */ +	*macen = MAC_EN_CLOCK_ENABLE|MAC_EN_RESET0| +		MAC_EN_RESET1|MAC_EN_RESET2; +	udelay(10); + +	for(i=0;i<NO_OF_FIFOS;i++){ +		fifo_tx[i].len = 0; +		fifo_tx[i].addr = virt_to_phys(&txbuf[0]); +		fifo_rx[i].addr = (virt_to_phys(NetRxPackets[i]))|RX_DMA_ENABLE; +	} + +	/* 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]	    ) ; +#undef ea +	*mac_mcast_low = 0; +	*mac_mcast_high = 0; + +	/* Make sure the MAC buffer is in the correct endian mode */ +#ifdef __LITTLE_ENDIAN +	*mac_ctrl = MAC_FULL_DUPLEX; +	udelay(1); +	*mac_ctrl = MAC_FULL_DUPLEX|MAC_RX_ENABLE|MAC_TX_ENABLE; +#else +	*mac_ctrl = MAC_BIG_ENDIAN|MAC_FULL_DUPLEX; +	udelay(1); +	*mac_ctrl = MAC_BIG_ENDIAN|MAC_FULL_DUPLEX|MAC_RX_ENABLE|MAC_TX_ENABLE; +#endif + +	return(1); +} + +static void au1x00_halt(struct eth_device* dev){ +	volatile u32 *macen = (volatile u32*)MAC0_ENABLE; + +	/* Put MAC0 in reset */ +	*macen = 0; +} + +int au1x00_enet_initialize(bd_t *bis){ +	struct eth_device* dev; + +	if ((dev = (struct eth_device*)malloc(sizeof *dev)) == NULL) { +		puts ("malloc failed\n"); +		return -1; +	} + +	memset(dev, 0, sizeof *dev); + +	sprintf(dev->name, "Au1X00 ethernet"); +	dev->iobase = 0; +	dev->priv   = 0; +	dev->init   = au1x00_init; +	dev->halt   = au1x00_halt; +	dev->send   = au1x00_send; +	dev->recv   = au1x00_recv; + +	eth_register(dev); + +#if defined(CONFIG_CMD_MII) +	miiphy_register(dev->name, +		au1x00_miiphy_read, au1x00_miiphy_write); +#endif + +	return 1; +} diff --git a/arch/mips/cpu/au1x00_serial.c b/arch/mips/cpu/au1x00_serial.c new file mode 100644 index 00000000000..c25ba5a5b3f --- /dev/null +++ b/arch/mips/cpu/au1x00_serial.c @@ -0,0 +1,131 @@ +/* + * AU1X00 UART support + * + * Hardcoded to UART 0 for now + * Speed and options also hardcoded to 115200 8N1 + * + *  Copyright (c) 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 <config.h> +#include <common.h> +#include <asm/au1x00.h> + +/****************************************************************************** +* +* serial_init - initialize a channel +* +* This routine initializes the number of data bits, parity +* and set the selected baud rate. Interrupts are disabled. +* Set the modem control signals if the option is selected. +* +* RETURNS: N/A +*/ + +int serial_init (void) +{ +	volatile u32 *uart_fifoctl = (volatile u32*)(UART0_ADDR+UART_FCR); +	volatile u32 *uart_enable = (volatile u32*)(UART0_ADDR+UART_ENABLE); + +	/* Enable clocks first */ +	*uart_enable = UART_EN_CE; + +	/* Then release reset */ +	/* Must release reset before setting other regs */ +	*uart_enable = UART_EN_CE|UART_EN_E; + +	/* Activate fifos, reset tx and rx */ +	/* Set tx trigger level to 12 */ +	*uart_fifoctl = UART_FCR_ENABLE_FIFO|UART_FCR_CLEAR_RCVR| +		UART_FCR_CLEAR_XMIT|UART_FCR_T_TRIGGER_12; + +	serial_setbrg(); + +	return 0; +} + + +void serial_setbrg (void) +{ +	volatile u32 *uart_clk = (volatile u32*)(UART0_ADDR+UART_CLK); +	volatile u32 *uart_lcr = (volatile u32*)(UART0_ADDR+UART_LCR); +	volatile u32 *sys_powerctrl = (u32 *)SYS_POWERCTRL; +	int sd; +	int divisorx2; + +	/* sd is system clock divisor			*/ +	/* see section 10.4.5 in au1550 datasheet	*/ +	sd = (*sys_powerctrl & 0x03) + 2; + +	/* calulate 2x baudrate and round */ +	divisorx2 = ((CONFIG_SYS_MIPS_TIMER_FREQ/(sd * 16 * CONFIG_BAUDRATE))); + +	if (divisorx2 & 0x01) +		divisorx2 = divisorx2 + 1; + +	*uart_clk = divisorx2 / 2; + +	/* Set parity, stop bits and word length to 8N1 */ +	*uart_lcr = UART_LCR_WLEN8; +} + +void serial_putc (const char c) +{ +	volatile u32 *uart_lsr = (volatile u32*)(UART0_ADDR+UART_LSR); +	volatile u32 *uart_tx = (volatile u32*)(UART0_ADDR+UART_TX); + +	if (c == '\n') serial_putc ('\r'); + +	/* Wait for fifo to shift out some bytes */ +	while((*uart_lsr&UART_LSR_THRE)==0); + +	*uart_tx = (u32)c; +} + +void serial_puts (const char *s) +{ +	while (*s) +	{ +		serial_putc (*s++); +	} +} + +int serial_getc (void) +{ +	volatile u32 *uart_rx = (volatile u32*)(UART0_ADDR+UART_RX); +	char c; + +	while (!serial_tstc()); + +	c = (*uart_rx&0xFF); +	return c; +} + +int serial_tstc (void) +{ +	volatile u32 *uart_lsr = (volatile u32*)(UART0_ADDR+UART_LSR); + +	if(*uart_lsr&UART_LSR_DR){ +		/* Data in rfifo */ +		return(1); +	} +	return 0; +} diff --git a/arch/mips/cpu/au1x00_usb_ohci.c b/arch/mips/cpu/au1x00_usb_ohci.c new file mode 100644 index 00000000000..0bc2305d878 --- /dev/null +++ b/arch/mips/cpu/au1x00_usb_ohci.c @@ -0,0 +1,1726 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB on the AU1x00. + * + * (C) Copyright 2003 + * Gary Jennejohn, DENX Software Engineering <garyj@denx.de> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Note: Part of this code has been derived from linux + * + */ +/* + * IMPORTANT NOTES + * 1 - this driver is intended for use with USB Mass Storage Devices + *     (BBB) ONLY. There is NO support for Interrupt or Isochronous pipes! + */ + +#include <config.h> + +#ifdef CONFIG_USB_OHCI + +/* #include <pci.h> no PCI on the AU1x00 */ + +#include <common.h> +#include <malloc.h> +#include <asm/io.h> +#include <asm/au1x00.h> +#include <usb.h> +#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 __LITTLE_ENDIAN +#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_pipebulk(urb_priv->pipe) && +				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_pipecontrol(lurb_priv->pipe) && +	    ((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 (usb_pipeint(pipe)) { +		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_pipebulk(pipe)) +		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/arch/mips/cpu/au1x00_usb_ohci.h b/arch/mips/cpu/au1x00_usb_ohci.h new file mode 100644 index 00000000000..bb9f351099e --- /dev/null +++ b/arch/mips/cpu/au1x00_usb_ohci.h @@ -0,0 +1,416 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> + * (C) Copyright 2000-2001 David Brownell <dbrownell@users.sourceforge.net> + * + * usb-ohci.h + */ + + +static int cc_to_error[16] = { + +/* mapping of the OHCI CC status to error codes */ +	/* No  Error  */	0, +	/* CRC Error  */	USB_ST_CRC_ERR, +	/* Bit Stuff  */	USB_ST_BIT_ERR, +	/* Data Togg  */	USB_ST_CRC_ERR, +	/* Stall      */	USB_ST_STALLED, +	/* DevNotResp */	-1, +	/* PIDCheck   */	USB_ST_BIT_ERR, +	/* UnExpPID   */	USB_ST_BIT_ERR, +	/* DataOver   */	USB_ST_BUF_ERR, +	/* DataUnder  */	USB_ST_BUF_ERR, +	/* reservd    */	-1, +	/* reservd    */	-1, +	/* BufferOver */	USB_ST_BUF_ERR, +	/* BuffUnder  */	USB_ST_BUF_ERR, +	/* Not Access */	-1, +	/* Not Access */	-1 +}; + +/* ED States */ + +#define ED_NEW		0x00 +#define ED_UNLINK	0x01 +#define ED_OPER		0x02 +#define ED_DEL		0x04 +#define ED_URB_DEL	0x08 + +/* usb_ohci_ed */ +struct ed { +	__u32 hwINFO; +	__u32 hwTailP; +	__u32 hwHeadP; +	__u32 hwNextED; + +	struct ed *ed_prev; +	__u8 int_period; +	__u8 int_branch; +	__u8 int_load; +	__u8 int_interval; +	__u8 state; +	__u8 type; +	__u16 last_iso; +	struct ed *ed_rm_list; + +	struct usb_device *usb_dev; +	__u32 unused[3]; +} __attribute__((aligned(16))); +typedef struct ed ed_t; + + +/* TD info field */ +#define TD_CC			0xf0000000 +#define TD_CC_GET(td_p)		((td_p >>28) & 0x0f) +#define TD_CC_SET(td_p, cc)	(td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28) +#define TD_EC			0x0C000000 +#define TD_T			0x03000000 +#define TD_T_DATA0		0x02000000 +#define TD_T_DATA1		0x03000000 +#define TD_T_TOGGLE		0x00000000 +#define TD_R			0x00040000 +#define TD_DI			0x00E00000 +#define TD_DI_SET(X)		(((X) & 0x07)<< 21) +#define TD_DP			0x00180000 +#define TD_DP_SETUP		0x00000000 +#define TD_DP_IN		0x00100000 +#define TD_DP_OUT		0x00080000 + +#define TD_ISO			0x00010000 +#define TD_DEL			0x00020000 + +/* CC Codes */ +#define TD_CC_NOERROR		0x00 +#define TD_CC_CRC		0x01 +#define TD_CC_BITSTUFFING	0x02 +#define TD_CC_DATATOGGLEM	0x03 +#define TD_CC_STALL		0x04 +#define TD_DEVNOTRESP		0x05 +#define TD_PIDCHECKFAIL		0x06 +#define TD_UNEXPECTEDPID	0x07 +#define TD_DATAOVERRUN		0x08 +#define TD_DATAUNDERRUN		0x09 +#define TD_BUFFEROVERRUN	0x0C +#define TD_BUFFERUNDERRUN	0x0D +#define TD_NOTACCESSED		0x0F + + +#define MAXPSW 1 + +struct td { +	__u32 hwINFO; +	__u32 hwCBP;		/* Current Buffer Pointer */ +	__u32 hwNextTD;		/* Next TD Pointer */ +	__u32 hwBE;		/* Memory Buffer End Pointer */ + +	__u16 hwPSW[MAXPSW]; +	__u8 unused; +	__u8 index; +	struct ed *ed; +	struct td *next_dl_td; +	struct usb_device *usb_dev; +	int transfer_len; +	__u32 data; + +	__u32 unused2[2]; +} __attribute__((aligned(32))); +typedef struct td td_t; + +#define OHCI_ED_SKIP	(1 << 14) + +/* + * The HCCA (Host Controller Communications Area) is a 256 byte + * structure defined in the OHCI spec. that the host controller is + * told the base address of.  It must be 256-byte aligned. + */ + +#define NUM_INTS 32	/* part of the OHCI standard */ +struct ohci_hcca { +	__u32	int_table[NUM_INTS];	/* Interrupt ED table */ +	__u16	frame_no;		/* current frame number */ +	__u16	pad1;			/* set to 0 on each frame_no change */ +	__u32	done_head;		/* info returned for an interrupt */ +	u8		reserved_for_hc[116]; +} __attribute__((aligned(256))); + + +/* + * Maximum number of root hub ports. + */ +#define MAX_ROOT_PORTS	15	/* maximum OHCI root hub ports */ + +/* + * This is the structure of the OHCI controller's memory mapped I/O + * region.  This is Memory Mapped I/O.	You must use the readl() and + * writel() macros defined in asm/io.h to access these!! + */ +struct ohci_regs { +	/* control and status registers */ +	__u32	revision; +	__u32	control; +	__u32	cmdstatus; +	__u32	intrstatus; +	__u32	intrenable; +	__u32	intrdisable; +	/* memory pointers */ +	__u32	hcca; +	__u32	ed_periodcurrent; +	__u32	ed_controlhead; +	__u32	ed_controlcurrent; +	__u32	ed_bulkhead; +	__u32	ed_bulkcurrent; +	__u32	donehead; +	/* frame counters */ +	__u32	fminterval; +	__u32	fmremaining; +	__u32	fmnumber; +	__u32	periodicstart; +	__u32	lsthresh; +	/* Root hub ports */ +	struct	ohci_roothub_regs { +		__u32	a; +		__u32	b; +		__u32	status; +		__u32	portstatus[MAX_ROOT_PORTS]; +	} roothub; +} __attribute__((aligned(32))); + + +/* OHCI CONTROL AND STATUS REGISTER MASKS */ + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_CBSR	(3 << 0)	/* control/bulk service ratio */ +#define OHCI_CTRL_PLE	(1 << 2)	/* periodic list enable */ +#define OHCI_CTRL_IE	(1 << 3)	/* isochronous enable */ +#define OHCI_CTRL_CLE	(1 << 4)	/* control list enable */ +#define OHCI_CTRL_BLE	(1 << 5)	/* bulk list enable */ +#define OHCI_CTRL_HCFS	(3 << 6)	/* host controller functional state */ +#define OHCI_CTRL_IR	(1 << 8)	/* interrupt routing */ +#define OHCI_CTRL_RWC	(1 << 9)	/* remote wakeup connected */ +#define OHCI_CTRL_RWE	(1 << 10)	/* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +#	define OHCI_USB_RESET	(0 << 6) +#	define OHCI_USB_RESUME	(1 << 6) +#	define OHCI_USB_OPER	(2 << 6) +#	define OHCI_USB_SUSPEND	(3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR	(1 << 0)	/* host controller reset */ +#define OHCI_CLF	(1 << 1)	/* control list filled */ +#define OHCI_BLF	(1 << 2)	/* bulk list filled */ +#define OHCI_OCR	(1 << 3)	/* ownership change request */ +#define OHCI_SOC	(3 << 16)	/* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO	(1 << 0)	/* scheduling overrun */ +#define OHCI_INTR_WDH	(1 << 1)	/* writeback of done_head */ +#define OHCI_INTR_SF	(1 << 2)	/* start frame */ +#define OHCI_INTR_RD	(1 << 3)	/* resume detect */ +#define OHCI_INTR_UE	(1 << 4)	/* unrecoverable error */ +#define OHCI_INTR_FNO	(1 << 5)	/* frame number overflow */ +#define OHCI_INTR_RHSC	(1 << 6)	/* root hub status change */ +#define OHCI_INTR_OC	(1 << 30)	/* ownership change */ +#define OHCI_INTR_MIE	(1 << 31)	/* master interrupt enable */ + + +/* Virtual Root HUB */ +struct virt_root_hub { +	int devnum; /* Address of Root Hub endpoint */ +	void *dev;  /* was urb */ +	void *int_addr; +	int send; +	int interval; +}; + +/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */ + +/* destination of request */ +#define RH_INTERFACE		0x01 +#define RH_ENDPOINT		0x02 +#define RH_OTHER		0x03 + +#define RH_CLASS		0x20 +#define RH_VENDOR		0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS		0x0080 +#define RH_CLEAR_FEATURE	0x0100 +#define RH_SET_FEATURE		0x0300 +#define RH_SET_ADDRESS		0x0500 +#define RH_GET_DESCRIPTOR	0x0680 +#define RH_SET_DESCRIPTOR	0x0700 +#define RH_GET_CONFIGURATION	0x0880 +#define RH_SET_CONFIGURATION	0x0900 +#define RH_GET_STATE		0x0280 +#define RH_GET_INTERFACE	0x0A80 +#define RH_SET_INTERFACE	0x0B00 +#define RH_SYNC_FRAME		0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP		0x2000 + + +/* Hub port features */ +#define RH_PORT_CONNECTION	0x00 +#define RH_PORT_ENABLE		0x01 +#define RH_PORT_SUSPEND		0x02 +#define RH_PORT_OVER_CURRENT	0x03 +#define RH_PORT_RESET		0x04 +#define RH_PORT_POWER		0x08 +#define RH_PORT_LOW_SPEED	0x09 + +#define RH_C_PORT_CONNECTION	0x10 +#define RH_C_PORT_ENABLE	0x11 +#define RH_C_PORT_SUSPEND	0x12 +#define RH_C_PORT_OVER_CURRENT	0x13 +#define RH_C_PORT_RESET		0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER	0x00 +#define RH_C_HUB_OVER_CURRENT	0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL	0x01 + +#define RH_ACK			0x01 +#define RH_REQ_ERR		-1 +#define RH_NACK			0x00 + + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS		0x00000001	/* current connect status */ +#define RH_PS_PES		0x00000002	/* port enable status*/ +#define RH_PS_PSS		0x00000004	/* port suspend status */ +#define RH_PS_POCI		0x00000008	/* port over current indicator */ +#define RH_PS_PRS		0x00000010	/* port reset status */ +#define RH_PS_PPS		0x00000100	/* port power status */ +#define RH_PS_LSDA		0x00000200	/* low speed device attached */ +#define RH_PS_CSC		0x00010000	/* connect status change */ +#define RH_PS_PESC		0x00020000	/* port enable status change */ +#define RH_PS_PSSC		0x00040000	/* port suspend status change */ +#define RH_PS_OCIC		0x00080000	/* over current indicator change */ +#define RH_PS_PRSC		0x00100000	/* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS		0x00000001	/* local power status */ +#define RH_HS_OCI		0x00000002	/* over current indicator */ +#define RH_HS_DRWE		0x00008000	/* device remote wakeup enable */ +#define RH_HS_LPSC		0x00010000	/* local power status change */ +#define RH_HS_OCIC		0x00020000	/* over current indicator change */ +#define RH_HS_CRWE		0x80000000	/* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR			0x0000ffff	/* device removable flags */ +#define RH_B_PPCM		0xffff0000	/* port power control mask */ + +/* roothub.a masks */ +#define	RH_A_NDP		(0xff << 0)	/* number of downstream ports */ +#define	RH_A_PSM		(1 << 8)	/* power switching mode */ +#define	RH_A_NPS		(1 << 9)	/* no power switching */ +#define	RH_A_DT			(1 << 10)	/* device type (mbz) */ +#define	RH_A_OCPM		(1 << 11)	/* over current protection mode */ +#define	RH_A_NOCP		(1 << 12)	/* no over current protection */ +#define	RH_A_POTPGT		(0xff << 24)	/* power on to power good time */ + +/* urb */ +#define N_URB_TD 48 +typedef struct +{ +	ed_t *ed; +	__u16 length;		/* number of tds associated with this request */ +	__u16 td_cnt;		/* number of tds already serviced */ +	int   state; +	unsigned long pipe; +	int actual_length; +	td_t *td[N_URB_TD];	/* list pointer to all corresponding TDs associated with this request */ +} urb_priv_t; +#define URB_DEL 1 + +/* + * This is the full ohci controller description + * + * Note how the "proper" USB information is just + * a subset of what the full implementation needs. (Linus) + */ + + +typedef struct ohci { +	struct ohci_hcca *hcca;		/* hcca */ +	/*dma_addr_t hcca_dma;*/ + +	int irq; +	int disabled;			/* e.g. got a UE, we're hung */ +	int sleeping; +	unsigned long flags;		/* for HC bugs */ + +	struct ohci_regs *regs;		/* OHCI controller's memory */ + +	ed_t *ed_rm_list[2];		/* lists of all endpoints to be removed */ +	ed_t *ed_bulktail;		/* last endpoint of bulk list */ +	ed_t *ed_controltail;		/* last endpoint of control list */ +	int intrstatus; +	__u32 hc_control;		/* copy of the hc control reg */ +	struct usb_device *dev[32]; +	struct virt_root_hub rh; + +	const char	*slot_name; +} ohci_t; + +#define NUM_EDS 8		/* num of preallocated endpoint descriptors */ + +struct ohci_device { +	ed_t	ed[NUM_EDS]; +	int ed_cnt; +}; + +/* hcd */ +/* endpoint */ +static int ep_link(ohci_t * ohci, ed_t * ed); +static int ep_unlink(ohci_t * ohci, ed_t * ed); +static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned long pipe); + +/*-------------------------------------------------------------------------*/ + +/* we need more TDs than EDs */ +#define NUM_TD 64 + +/* +1 so we can align the storage */ +td_t gtd[NUM_TD+1]; +/* pointers to aligned storage */ +td_t *ptd; + +/* TDs ... */ +static inline struct td * +td_alloc (struct usb_device *usb_dev) +{ +	int i; +	struct td	*td; + +	td = NULL; +	for (i = 0; i < NUM_TD; i++) { +		if (ptd[i].usb_dev == NULL) { +			td = &ptd[i]; +			td->usb_dev = usb_dev; +			break; +		} +	} +	return td; +} + +static inline void +ed_free (struct ed *ed) +{ +	ed->usb_dev = NULL; +} diff --git a/arch/mips/cpu/cache.S b/arch/mips/cpu/cache.S new file mode 100644 index 00000000000..ff4f11cf787 --- /dev/null +++ b/arch/mips/cpu/cache.S @@ -0,0 +1,331 @@ +/* + *  Cache-handling routined for MIPS CPUs + * + *  Copyright (c) 2003	Wolfgang Denk <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 <config.h> +#include <asm/asm.h> +#include <asm/regdef.h> +#include <asm/mipsregs.h> +#include <asm/addrspace.h> +#include <asm/cacheops.h> + +#define RA		t8 + +/* + * 16kB is the maximum size of instruction and data caches on MIPS 4K, + * 64kB is on 4KE, 24K, 5K, etc. Set bigger size for convenience. + * + * Note that the above size is the maximum size of primary cache. U-Boot + * doesn't have L2 cache support for now. + */ +#define MIPS_MAX_CACHE_SIZE	0x10000 + +#define INDEX_BASE	CKSEG0 + +	.macro	cache_op op addr +	.set	push +	.set	noreorder +	.set	mips3 +	cache	\op, 0(\addr) +	.set	pop +	.endm + +/* + * cacheop macro to automate cache operations + * first some helpers... + */ +#define _mincache(size, maxsize) \ +   bltu  size,maxsize,9f ; \ +   move  size,maxsize ;    \ +9: + +#define _align(minaddr, maxaddr, linesize) \ +   .set noat ; \ +   subu  AT,linesize,1 ;   \ +   not   AT ;        \ +   and   minaddr,AT ;      \ +   addu  maxaddr,-1 ;      \ +   and   maxaddr,AT ;      \ +   .set at + +/* general operations */ +#define doop1(op1) \ +   cache op1,0(a0) +#define doop2(op1, op2) \ +   cache op1,0(a0) ;    \ +   nop ;          \ +   cache op2,0(a0) + +/* specials for cache initialisation */ +#define doop1lw(op1) \ +   lw zero,0(a0) +#define doop1lw1(op1) \ +   cache op1,0(a0) ;    \ +   lw zero,0(a0) ;      \ +   cache op1,0(a0) +#define doop121(op1,op2) \ +   cache op1,0(a0) ;    \ +   nop;           \ +   cache op2,0(a0) ;    \ +   nop;           \ +   cache op1,0(a0) + +#define _oploopn(minaddr, maxaddr, linesize, tag, ops) \ +   .set  noreorder ;    \ +10:   doop##tag##ops ;  \ +   bne     minaddr,maxaddr,10b ; \ +   add      minaddr,linesize ;   \ +   .set  reorder + +/* finally the cache operation macros */ +#define vcacheopn(kva, n, cacheSize, cacheLineSize, tag, ops) \ +   blez  n,11f ;        \ +   addu  n,kva ;        \ +   _align(kva, n, cacheLineSize) ; \ +   _oploopn(kva, n, cacheLineSize, tag, ops) ; \ +11: + +#define icacheopn(kva, n, cacheSize, cacheLineSize, tag, ops) \ +   _mincache(n, cacheSize);   \ +   blez  n,11f ;        \ +   addu  n,kva ;        \ +   _align(kva, n, cacheLineSize) ; \ +   _oploopn(kva, n, cacheLineSize, tag, ops) ; \ +11: + +#define vcacheop(kva, n, cacheSize, cacheLineSize, op) \ +   vcacheopn(kva, n, cacheSize, cacheLineSize, 1, (op)) + +#define icacheop(kva, n, cacheSize, cacheLineSize, op) \ +   icacheopn(kva, n, cacheSize, cacheLineSize, 1, (op)) + +	.macro	f_fill64 dst, offset, val +	LONG_S	\val, (\offset +  0 * LONGSIZE)(\dst) +	LONG_S	\val, (\offset +  1 * LONGSIZE)(\dst) +	LONG_S	\val, (\offset +  2 * LONGSIZE)(\dst) +	LONG_S	\val, (\offset +  3 * LONGSIZE)(\dst) +	LONG_S	\val, (\offset +  4 * LONGSIZE)(\dst) +	LONG_S	\val, (\offset +  5 * LONGSIZE)(\dst) +	LONG_S	\val, (\offset +  6 * LONGSIZE)(\dst) +	LONG_S	\val, (\offset +  7 * LONGSIZE)(\dst) +#if LONGSIZE == 4 +	LONG_S	\val, (\offset +  8 * LONGSIZE)(\dst) +	LONG_S	\val, (\offset +  9 * LONGSIZE)(\dst) +	LONG_S	\val, (\offset + 10 * LONGSIZE)(\dst) +	LONG_S	\val, (\offset + 11 * LONGSIZE)(\dst) +	LONG_S	\val, (\offset + 12 * LONGSIZE)(\dst) +	LONG_S	\val, (\offset + 13 * LONGSIZE)(\dst) +	LONG_S	\val, (\offset + 14 * LONGSIZE)(\dst) +	LONG_S	\val, (\offset + 15 * LONGSIZE)(\dst) +#endif +	.endm + +/* + * mips_init_icache(uint PRId, ulong icache_size, unchar icache_linesz) + */ +LEAF(mips_init_icache) +	blez	a1, 9f +	mtc0	zero, CP0_TAGLO +	/* clear tag to invalidate */ +	PTR_LI		t0, INDEX_BASE +	PTR_ADDU	t1, t0, a1 +1:	cache_op	Index_Store_Tag_I t0 +	PTR_ADDU	t0, a2 +	bne		t0, t1, 1b +	/* fill once, so data field parity is correct */ +	PTR_LI		t0, INDEX_BASE +2:	cache_op	Fill t0 +	PTR_ADDU	t0, a2 +	bne		t0, t1, 2b +	/* invalidate again - prudent but not strictly neccessary */ +	PTR_LI		t0, INDEX_BASE +1:	cache_op	Index_Store_Tag_I t0 +	PTR_ADDU	t0, a2 +	bne		t0, t1, 1b +9:	jr	ra +	END(mips_init_icache) + +/* + * mips_init_dcache(uint PRId, ulong dcache_size, unchar dcache_linesz) + */ +LEAF(mips_init_dcache) +	blez	a1, 9f +	mtc0	zero, CP0_TAGLO +	/* clear all tags */ +	PTR_LI		t0, INDEX_BASE +	PTR_ADDU	t1, t0, a1 +1:	cache_op	Index_Store_Tag_D t0 +	PTR_ADDU	t0, a2 +	bne		t0, t1, 1b +	/* load from each line (in cached space) */ +	PTR_LI		t0, INDEX_BASE +2:	LONG_L		zero, 0(t0) +	PTR_ADDU	t0, a2 +	bne		t0, t1, 2b +	/* clear all tags */ +	PTR_LI		t0, INDEX_BASE +1:	cache_op	Index_Store_Tag_D t0 +	PTR_ADDU	t0, a2 +	bne		t0, t1, 1b +9:	jr	ra +	END(mips_init_dcache) + +/******************************************************************************* +* +* mips_cache_reset - low level initialisation of the primary caches +* +* This routine initialises the primary caches to ensure that they +* have good parity.  It must be called by the ROM before any cached locations +* are used to prevent the possibility of data with bad parity being written to +* memory. +* To initialise the instruction cache it is essential that a source of data +* with good parity is available. This routine +* will initialise an area of memory starting at location zero to be used as +* a source of parity. +* +* RETURNS: N/A +* +*/ +NESTED(mips_cache_reset, 0, ra) +	move	RA, ra +	li	t2, CONFIG_SYS_ICACHE_SIZE +	li	t3, CONFIG_SYS_DCACHE_SIZE +	li	t4, CONFIG_SYS_CACHELINE_SIZE +	move	t5, t4 + +	li	v0, MIPS_MAX_CACHE_SIZE + +	/* +	 * Now clear that much memory starting from zero. +	 */ +	PTR_LI		a0, CKSEG1 +	PTR_ADDU	a1, a0, v0 +2:	PTR_ADDIU	a0, 64 +	f_fill64	a0, -64, zero +	bne		a0, a1, 2b + +	/* +	 * The caches are probably in an indeterminate state, +	 * so we force good parity into them by doing an +	 * invalidate, load/fill, invalidate for each line. +	 */ + +	/* +	 * Assume bottom of RAM will generate good parity for the cache. +	 */ + +	/* +	 * Initialize the I-cache first, +	 */ +	move	a1, t2 +	move	a2, t4 +	PTR_LA	t7, mips_init_icache +	jalr	t7 + +	/* +	 * then initialize D-cache. +	 */ +	move	a1, t3 +	move	a2, t5 +	PTR_LA	t7, mips_init_dcache +	jalr	t7 + +	jr	RA +	END(mips_cache_reset) + +/******************************************************************************* +* +* dcache_status - get cache status +* +* RETURNS: 0 - cache disabled; 1 - cache enabled +* +*/ +LEAF(dcache_status) +	mfc0	t0, CP0_CONFIG +	li	t1, CONF_CM_UNCACHED +	andi	t0, t0, CONF_CM_CMASK +	move	v0, zero +	beq	t0, t1, 2f +	li	v0, 1 +2:	jr	ra +	END(dcache_status) + +/******************************************************************************* +* +* dcache_disable - disable cache +* +* RETURNS: N/A +* +*/ +LEAF(dcache_disable) +	mfc0	t0, CP0_CONFIG +	li	t1, -8 +	and	t0, t0, t1 +	ori	t0, t0, CONF_CM_UNCACHED +	mtc0	t0, CP0_CONFIG +	jr	ra +	END(dcache_disable) + +/******************************************************************************* +* +* dcache_enable - enable cache +* +* RETURNS: N/A +* +*/ +LEAF(dcache_enable) +	mfc0	t0, CP0_CONFIG +	ori	t0, CONF_CM_CMASK +	xori	t0, CONF_CM_CMASK +	ori	t0, CONF_CM_CACHABLE_NONCOHERENT +	mtc0	t0, CP0_CONFIG +	jr	ra +	END(dcache_enable) + +#ifdef CONFIG_SYS_INIT_RAM_LOCK_MIPS +/******************************************************************************* +* +* mips_cache_lock - lock RAM area pointed to by a0 in cache. +* +* RETURNS: N/A +* +*/ +#if defined(CONFIG_PURPLE) +# define	CACHE_LOCK_SIZE	(CONFIG_SYS_DCACHE_SIZE/2) +#else +# define	CACHE_LOCK_SIZE	(CONFIG_SYS_DCACHE_SIZE) +#endif +	.globl	mips_cache_lock +	.ent	mips_cache_lock +mips_cache_lock: +	li	a1, CKSEG0 - CACHE_LOCK_SIZE +	addu	a0, a1 +	li	a2, CACHE_LOCK_SIZE +	li	a3, CONFIG_SYS_CACHELINE_SIZE +	move	a1, a2 +	icacheop(a0,a1,a2,a3,0x1d) + +	jr	ra + +	.end	mips_cache_lock +#endif /* CONFIG_SYS_INIT_RAM_LOCK_MIPS */ diff --git a/arch/mips/cpu/config.mk b/arch/mips/cpu/config.mk new file mode 100644 index 00000000000..a173c5480c0 --- /dev/null +++ b/arch/mips/cpu/config.mk @@ -0,0 +1,39 @@ +# +# (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 +# +v=$(shell $(AS) --version | grep 'GNU assembler' | egrep -o '2\.[0-9\.]+' | cut -d. -f2) +MIPSFLAGS:=$(shell \ +if [ "$v" -lt "14" ]; then \ +	echo "-mcpu=4kc"; \ +else \ +	echo "-march=4kc -mtune=4kc"; \ +fi) + +ifneq (,$(findstring 4KCle,$(CROSS_COMPILE))) +ENDIANNESS = -EL +else +ENDIANNESS = -EB +endif + +MIPSFLAGS += $(ENDIANNESS) + +PLATFORM_CPPFLAGS += $(MIPSFLAGS) diff --git a/arch/mips/cpu/cpu.c b/arch/mips/cpu/cpu.c new file mode 100644 index 00000000000..d5a16047de9 --- /dev/null +++ b/arch/mips/cpu/cpu.c @@ -0,0 +1,112 @@ +/* + * (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 <common.h> +#include <command.h> +#include <netdev.h> +#include <asm/mipsregs.h> +#include <asm/cacheops.h> +#include <asm/reboot.h> + +#define cache_op(op,addr)						\ +	__asm__ __volatile__(						\ +	"	.set	push					\n"	\ +	"	.set	noreorder				\n"	\ +	"	.set	mips3\n\t				\n"	\ +	"	cache	%0, %1					\n"	\ +	"	.set	pop					\n"	\ +	:								\ +	: "i" (op), "R" (*(unsigned char *)(addr))) + +void __attribute__((weak)) _machine_restart(void) +{ +} + +int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ +	_machine_restart(); + +	fprintf(stderr, "*** reset failed ***\n"); +	return 0; +} + +void flush_cache(ulong start_addr, ulong size) +{ +	unsigned long lsize = CONFIG_SYS_CACHELINE_SIZE; +	unsigned long addr = start_addr & ~(lsize - 1); +	unsigned long aend = (start_addr + size - 1) & ~(lsize - 1); + +	while (1) { +		cache_op(Hit_Writeback_Inv_D, addr); +		cache_op(Hit_Invalidate_I, addr); +		if (addr == aend) +			break; +		addr += lsize; +	} +} + +void flush_dcache_range(ulong start_addr, ulong stop) +{ +	unsigned long lsize = CONFIG_SYS_CACHELINE_SIZE; +	unsigned long addr = start_addr & ~(lsize - 1); +	unsigned long aend = (stop - 1) & ~(lsize - 1); + +	while (1) { +		cache_op(Hit_Writeback_Inv_D, addr); +		if (addr == aend) +			break; +		addr += lsize; +	} +} + +void invalidate_dcache_range(ulong start_addr, ulong stop) +{ +	unsigned long lsize = CONFIG_SYS_CACHELINE_SIZE; +	unsigned long addr = start_addr & ~(lsize - 1); +	unsigned long aend = (stop - 1) & ~(lsize - 1); + +	while (1) { +		cache_op(Hit_Invalidate_D, addr); +		if (addr == aend) +			break; +		addr += lsize; +	} +} + +void write_one_tlb(int index, u32 pagemask, u32 hi, u32 low0, u32 low1) +{ +	write_c0_entrylo0(low0); +	write_c0_pagemask(pagemask); +	write_c0_entrylo1(low1); +	write_c0_entryhi(hi); +	write_c0_index(index); +	tlb_write_indexed(); +} + +int cpu_eth_init(bd_t *bis) +{ +#ifdef CONFIG_SOC_AU1X00 +	au1x00_enet_initialize(bis); +#endif +	return 0; +} diff --git a/arch/mips/cpu/incaip_clock.c b/arch/mips/cpu/incaip_clock.c new file mode 100644 index 00000000000..fc2c62180b3 --- /dev/null +++ b/arch/mips/cpu/incaip_clock.c @@ -0,0 +1,116 @@ +/* + * (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 <common.h> +#include <asm/inca-ip.h> + + +/******************************************************************************* +* +* get_cpuclk - returns the frequency of the CPU. +* +* Gets the value directly from the INCA-IP hardware. +* +* RETURNS: +*          150.000.000 for 150 MHz +*          133.333.333 for 133 MHz (= 400MHz/3) +*          100.000.000 for 100 MHz (= 400MHz/4) +* NOTE: +*   This functions should be used by the hardware driver to get the correct +*   frequency of the CPU. Don't use the macros, which are set to init the CPU +*   frequency in the ROM code. +*/ +uint incaip_get_cpuclk (void) +{ +	/*-------------------------------------------------------------------------*/ +	/* CPU Clock Input Multiplexer (MUX I)                                     */ +	/* Multiplexer MUX I selects the maximum input clock to the CPU.           */ +	/*-------------------------------------------------------------------------*/ +	if (*((volatile ulong *) INCA_IP_CGU_CGU_MUXCR) & +	    INCA_IP_CGU_CGU_MUXCR_MUXI) { +		/* MUX I set to 150 MHz clock */ +		return 150000000; +	} else { +		/* MUX I set to 100/133 MHz clock */ +		if (*((volatile ulong *) INCA_IP_CGU_CGU_DIVCR) & 0x40) { +			/* Division value is 1/3, maximum CPU operating */ +			/* frequency is 133.3 MHz                       */ +			return 133333333; +		} else { +			/* Division value is 1/4, maximum CPU operating */ +			/* frequency is 100 MHz                         */ +			return 100000000; +		} +	} +} + +/******************************************************************************* +* +* get_fpiclk - returns the frequency of the FPI bus. +* +* Gets the value directly from the INCA-IP hardware. +* +* RETURNS: Frquency in Hz +* +* NOTE: +*   This functions should be used by the hardware driver to get the correct +*   frequency of the CPU. Don't use the macros, which are set to init the CPU +*   frequency in the ROM code. +*   The calculation for the +*/ +uint incaip_get_fpiclk (void) +{ +	uint clkCPU; + +	clkCPU = incaip_get_cpuclk (); + +	switch (*((volatile ulong *) INCA_IP_CGU_CGU_DIVCR) & 0xC) { +	case 0x4: +		return clkCPU >> 1;	/* devided by 2 */ +		break; +	case 0x8: +		return clkCPU >> 2;	/* devided by 4 */ +		break; +	default: +		return clkCPU; +		break; +	} +} + +int incaip_set_cpuclk (void) +{ +	extern void ebu_init(long); +	extern void cgu_init(long); +	extern void sdram_init(long); +	char tmp[64]; +	ulong cpuclk; + +	if (getenv_r ("cpuclk", tmp, sizeof (tmp)) > 0) { +		cpuclk = simple_strtoul (tmp, NULL, 10) * 1000000; +		cgu_init (cpuclk); +		ebu_init (cpuclk); +		sdram_init (cpuclk); +	} + +	return 0; +} diff --git a/arch/mips/cpu/incaip_wdt.S b/arch/mips/cpu/incaip_wdt.S new file mode 100644 index 00000000000..3ade3cd6fdc --- /dev/null +++ b/arch/mips/cpu/incaip_wdt.S @@ -0,0 +1,71 @@ +/* + *  INCA-IP Watchdog timer management code. + * + *  Copyright (c) 2003	Wolfgang Denk <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 <config.h> +#include <asm/regdef.h> + + +#define WD_BASE            0xb8000000 +#define WD_CON0(value)     0x0020(value) +#define WD_CON1(value)     0x0024(value) +#define WD_DISABLE         0x00000008 +#define WD_ENABLE          0x00000000 +#define WD_WRITE_PW        0xFFFC00F8 +#define WD_WRITE_ENDINIT   0xFFFC00F3 +#define WD_WRITE_INIT      0xFFFC00F2 + + +	.globl	disable_incaip_wdt +disable_incaip_wdt: +	li	t0, WD_BASE + +	/* Calculate password. +	 */ +	lw	t2, WD_CON1(t0) +	and	t2, 0xC + +	lw	t3, WD_CON0(t0) +	and	t3, 0xFFFFFF01 + +	or	t3, t2 +	or	t3, 0xF0 + +	sw	t3, WD_CON0(t0)		/* write password */ + +	/* Clear ENDINIT. +	 */ +	li	t1, WD_WRITE_INIT +	sw	t1, WD_CON0(t0) + + +	li	t1, WD_DISABLE +	sw	t1, WD_CON1(t0)		/* disable watchdog */ +	li	t1, WD_WRITE_PW +	sw	t1, WD_CON0(t0)		/* write password */ +	li	t1, WD_WRITE_ENDINIT +	sw	t1, WD_CON0(t0)		/* end command */ + +	jr	ra +	nop diff --git a/arch/mips/cpu/interrupts.c b/arch/mips/cpu/interrupts.c new file mode 100644 index 00000000000..87f7a9f7e6b --- /dev/null +++ b/arch/mips/cpu/interrupts.c @@ -0,0 +1,33 @@ +/* + * (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 <common.h> + +void enable_interrupts(void) +{ +} + +int disable_interrupts(void) +{ +	return 0; +} diff --git a/arch/mips/cpu/start.S b/arch/mips/cpu/start.S new file mode 100644 index 00000000000..57db589b94f --- /dev/null +++ b/arch/mips/cpu/start.S @@ -0,0 +1,421 @@ +/* + *  Startup Code for MIPS32 CPU-core + * + *  Copyright (c) 2003	Wolfgang Denk <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 <config.h> +#include <asm/regdef.h> +#include <asm/mipsregs.h> + +	/* +	 * For the moment disable interrupts, mark the kernel mode and +	 * set ST0_KX so that the CPU does not spit fire when using +	 * 64-bit addresses. +	 */ +	.macro	setup_c0_status set clr +	.set	push +	mfc0	t0, CP0_STATUS +	or	t0, ST0_CU0 | \set | 0x1f | \clr +	xor	t0, 0x1f | \clr +	mtc0	t0, CP0_STATUS +	.set	noreorder +	sll	zero, 3				# ehb +	.set	pop +	.endm + +	.macro	setup_c0_status_reset +#ifdef CONFIG_64BIT +	setup_c0_status ST0_KX 0 +#else +	setup_c0_status 0 0 +#endif +	.endm + +#define RVECENT(f,n) \ +   b f; nop +#define XVECENT(f,bev) \ +   b f     ;           \ +   li k0,bev + +	.set noreorder + +	.globl _start +	.text +_start: +	RVECENT(reset,0)	/* U-boot entry point */ +	RVECENT(reset,1)	/* software reboot */ +#if defined(CONFIG_INCA_IP) +	.word INFINEON_EBU_BOOTCFG /* EBU init code, fetched during booting */ +	.word 0x00000000           /* phase of the flash                    */ +#elif defined(CONFIG_PURPLE) +	.word INFINEON_EBU_BOOTCFG /* EBU init code, fetched during booting */ +	.word INFINEON_EBU_BOOTCFG /* EBU init code, fetched during booting */ +#else +	RVECENT(romReserved,2) +#endif +	RVECENT(romReserved,3) +	RVECENT(romReserved,4) +	RVECENT(romReserved,5) +	RVECENT(romReserved,6) +	RVECENT(romReserved,7) +	RVECENT(romReserved,8) +	RVECENT(romReserved,9) +	RVECENT(romReserved,10) +	RVECENT(romReserved,11) +	RVECENT(romReserved,12) +	RVECENT(romReserved,13) +	RVECENT(romReserved,14) +	RVECENT(romReserved,15) +	RVECENT(romReserved,16) +	RVECENT(romReserved,17) +	RVECENT(romReserved,18) +	RVECENT(romReserved,19) +	RVECENT(romReserved,20) +	RVECENT(romReserved,21) +	RVECENT(romReserved,22) +	RVECENT(romReserved,23) +	RVECENT(romReserved,24) +	RVECENT(romReserved,25) +	RVECENT(romReserved,26) +	RVECENT(romReserved,27) +	RVECENT(romReserved,28) +	RVECENT(romReserved,29) +	RVECENT(romReserved,30) +	RVECENT(romReserved,31) +	RVECENT(romReserved,32) +	RVECENT(romReserved,33) +	RVECENT(romReserved,34) +	RVECENT(romReserved,35) +	RVECENT(romReserved,36) +	RVECENT(romReserved,37) +	RVECENT(romReserved,38) +	RVECENT(romReserved,39) +	RVECENT(romReserved,40) +	RVECENT(romReserved,41) +	RVECENT(romReserved,42) +	RVECENT(romReserved,43) +	RVECENT(romReserved,44) +	RVECENT(romReserved,45) +	RVECENT(romReserved,46) +	RVECENT(romReserved,47) +	RVECENT(romReserved,48) +	RVECENT(romReserved,49) +	RVECENT(romReserved,50) +	RVECENT(romReserved,51) +	RVECENT(romReserved,52) +	RVECENT(romReserved,53) +	RVECENT(romReserved,54) +	RVECENT(romReserved,55) +	RVECENT(romReserved,56) +	RVECENT(romReserved,57) +	RVECENT(romReserved,58) +	RVECENT(romReserved,59) +	RVECENT(romReserved,60) +	RVECENT(romReserved,61) +	RVECENT(romReserved,62) +	RVECENT(romReserved,63) +	XVECENT(romExcHandle,0x200)	/* bfc00200: R4000 tlbmiss vector */ +	RVECENT(romReserved,65) +	RVECENT(romReserved,66) +	RVECENT(romReserved,67) +	RVECENT(romReserved,68) +	RVECENT(romReserved,69) +	RVECENT(romReserved,70) +	RVECENT(romReserved,71) +	RVECENT(romReserved,72) +	RVECENT(romReserved,73) +	RVECENT(romReserved,74) +	RVECENT(romReserved,75) +	RVECENT(romReserved,76) +	RVECENT(romReserved,77) +	RVECENT(romReserved,78) +	RVECENT(romReserved,79) +	XVECENT(romExcHandle,0x280)	/* bfc00280: R4000 xtlbmiss vector */ +	RVECENT(romReserved,81) +	RVECENT(romReserved,82) +	RVECENT(romReserved,83) +	RVECENT(romReserved,84) +	RVECENT(romReserved,85) +	RVECENT(romReserved,86) +	RVECENT(romReserved,87) +	RVECENT(romReserved,88) +	RVECENT(romReserved,89) +	RVECENT(romReserved,90) +	RVECENT(romReserved,91) +	RVECENT(romReserved,92) +	RVECENT(romReserved,93) +	RVECENT(romReserved,94) +	RVECENT(romReserved,95) +	XVECENT(romExcHandle,0x300)	/* bfc00300: R4000 cache vector */ +	RVECENT(romReserved,97) +	RVECENT(romReserved,98) +	RVECENT(romReserved,99) +	RVECENT(romReserved,100) +	RVECENT(romReserved,101) +	RVECENT(romReserved,102) +	RVECENT(romReserved,103) +	RVECENT(romReserved,104) +	RVECENT(romReserved,105) +	RVECENT(romReserved,106) +	RVECENT(romReserved,107) +	RVECENT(romReserved,108) +	RVECENT(romReserved,109) +	RVECENT(romReserved,110) +	RVECENT(romReserved,111) +	XVECENT(romExcHandle,0x380)	/* bfc00380: R4000 general vector */ +	RVECENT(romReserved,113) +	RVECENT(romReserved,114) +	RVECENT(romReserved,115) +	RVECENT(romReserved,116) +	RVECENT(romReserved,116) +	RVECENT(romReserved,118) +	RVECENT(romReserved,119) +	RVECENT(romReserved,120) +	RVECENT(romReserved,121) +	RVECENT(romReserved,122) +	RVECENT(romReserved,123) +	RVECENT(romReserved,124) +	RVECENT(romReserved,125) +	RVECENT(romReserved,126) +	RVECENT(romReserved,127) + +	/* We hope there are no more reserved vectors! +	 * 128 * 8 == 1024 == 0x400 +	 * so this is address R_VEC+0x400 == 0xbfc00400 +	 */ +#ifdef CONFIG_PURPLE +/* 0xbfc00400 */ +	.word	0xdc870000 +	.word	0xfca70000 +	.word	0x20840008 +	.word	0x20a50008 +	.word	0x20c6ffff +	.word	0x14c0fffa +	.word	0x00000000 +	.word	0x03e00008 +	.word	0x00000000 +	.word	0x00000000 +/* 0xbfc00428 */ +	.word	0xdc870000 +	.word	0xfca70000 +	.word	0x20840008 +	.word	0x20a50008 +	.word	0x20c6ffff +	.word	0x14c0fffa +	.word	0x00000000 +	.word	0x03e00008 +	.word	0x00000000 +	.word	0x00000000 +#endif /* CONFIG_PURPLE */ +	.align 4 +reset: + +	/* Clear watch registers. +	 */ +	mtc0	zero, CP0_WATCHLO +	mtc0	zero, CP0_WATCHHI + +	/* WP(Watch Pending), SW0/1 should be cleared. */ +	mtc0	zero, CP0_CAUSE + +	setup_c0_status_reset + +	/* Init Timer */ +	mtc0	zero, CP0_COUNT +	mtc0	zero, CP0_COMPARE + +#if !defined(CONFIG_SKIP_LOWLEVEL_INIT) +	/* CONFIG0 register */ +	li	t0, CONF_CM_UNCACHED +	mtc0	t0, CP0_CONFIG +#endif /* !CONFIG_SKIP_LOWLEVEL_INIT */ + +	/* Initialize $gp. +	 */ +	bal	1f +	nop +	.word	_gp +1: +	lw	gp, 0(ra) + +#if !defined(CONFIG_SKIP_LOWLEVEL_INIT) +	/* Initialize any external memory. +	 */ +	la	t9, lowlevel_init +	jalr	t9 +	nop + +	/* Initialize caches... +	 */ +	la	t9, mips_cache_reset +	jalr	t9 +	nop + +	/* ... and enable them. +	 */ +	li	t0, CONF_CM_CACHABLE_NONCOHERENT +	mtc0	t0, CP0_CONFIG +#endif /* !CONFIG_SKIP_LOWLEVEL_INIT */ + +	/* Set up temporary stack. +	 */ +#ifdef CONFIG_SYS_INIT_RAM_LOCK_MIPS +	li	a0, CONFIG_SYS_INIT_SP_OFFSET +	la	t9, mips_cache_lock +	jalr	t9 +	nop +#endif + +	li	t0, CONFIG_SYS_SDRAM_BASE + CONFIG_SYS_INIT_SP_OFFSET +	la	sp, 0(t0) + +	la	t9, board_init_f +	jr	t9 +	nop + +/* + * void relocate_code (addr_sp, gd, addr_moni) + * + * This "function" does not return, instead it continues in RAM + * after relocating the monitor code. + * + * a0 = addr_sp + * a1 = gd + * a2 = destination address + */ +	.globl	relocate_code +	.ent	relocate_code +relocate_code: +	move	sp, a0		/* Set new stack pointer	*/ + +	li	t0, CONFIG_SYS_MONITOR_BASE +	la	t3, in_ram +	lw	t2, -12(t3)	/* t2 <-- uboot_end_data	*/ +	move	t1, a2 +	move	s2, a2		/* s2 <-- destination address	*/ + +	/* +	 * Fix $gp: +	 * +	 * New $gp = (Old $gp - CONFIG_SYS_MONITOR_BASE) + Destination Address +	 */ +	move	t6, gp +	sub	gp, CONFIG_SYS_MONITOR_BASE +	add	gp, a2		/* gp now adjusted		*/ +	sub	s1, gp, t6	/* s1 <-- relocation offset	*/ + +	/* +	 * t0 = source address +	 * t1 = target address +	 * t2 = source end address +	 */ + +	/* +	 * Save destination address and size for later usage in flush_cache() +	 */ +	move	s0, a1		/* save gd in s0		*/ +	move	a0, t1		/* a0 <-- destination addr	*/ +	sub	a1, t2, t0	/* a1 <-- size			*/ + +	/* On the purple board we copy the code earlier in a special way +	 * in order to solve flash problems +	 */ +#ifndef CONFIG_PURPLE +1: +	lw	t3, 0(t0) +	sw	t3, 0(t1) +	addu	t0, 4 +	ble	t0, t2, 1b +	addu	t1, 4		/* delay slot			*/ +#endif + +	/* If caches were enabled, we would have to flush them here. +	 */ + +	/* a0 & a1 are already set up for flush_cache(start, size) */ +	la	t9, flush_cache +	jalr	t9 +	nop + +	/* Jump to where we've relocated ourselves. +	 */ +	addi	t0, s2, in_ram - _start +	jr	t0 +	nop + +	.word	_gp +	.word	_GLOBAL_OFFSET_TABLE_ +	.word	uboot_end_data +	.word	uboot_end +	.word	num_got_entries + +in_ram: +	/* +	 * Now we want to update GOT. +	 * +	 * GOT[0] is reserved. GOT[1] is also reserved for the dynamic object +	 * generated by GNU ld. Skip these reserved entries from relocation. +	 */ +	lw	t3, -4(t0)	/* t3 <-- num_got_entries	*/ +	lw	t4, -16(t0)	/* t4 <-- _GLOBAL_OFFSET_TABLE_	*/ +	lw	t5, -20(t0)	/* t5 <-- _gp	*/ +	sub	t4, t5		/* compute offset*/ +	add	t4, t4, gp	/* t4 now holds relocated _GLOBAL_OFFSET_TABLE_	*/ +	addi	t4, t4, 8	/* Skipping first two entries.	*/ +	li	t2, 2 +1: +	lw	t1, 0(t4) +	beqz	t1, 2f +	add	t1, s1 +	sw	t1, 0(t4) +2: +	addi	t2, 1 +	blt	t2, t3, 1b +	addi	t4, 4		/* delay slot			*/ + +	/* Clear BSS. +	 */ +	lw	t1, -12(t0)	/* t1 <-- uboot_end_data	*/ +	lw	t2, -8(t0)	/* t2 <-- uboot_end		*/ +	add	t1, s1		/* adjust pointers		*/ +	add	t2, s1 + +	sub	t1, 4 +1: +	addi	t1, 4 +	bltl	t1, t2, 1b +	sw	zero, 0(t1)	/* delay slot			*/ + +	move	a0, s0		/* a0 <-- gd			*/ +	la	t9, board_init_r +	jr	t9 +	move	a1, s2		/* delay slot			*/ + +	.end	relocate_code + +	/* Exception handlers. +	 */ +romReserved: +	b	romReserved + +romExcHandle: +	b	romExcHandle | 
