diff options
author | Justin Waters <justin@justin-linux.(none)> | 2008-01-21 13:46:21 -0500 |
---|---|---|
committer | Justin Waters <justin@justin-linux.(none)> | 2008-01-21 13:46:21 -0500 |
commit | 037af3481edfb2b7a3fbed1dffd277df25f677b9 (patch) | |
tree | 2c39b8891b4494298b7ee447c480645d96e13bdb /cpu | |
parent | ba999c531ed16ec749b2b6f4b0133cee38842b91 (diff) |
Add Atmel AT91 patch version 1.5
This patch adds support for the following boards:
AT91RM9200-EK
AT91SAM9260-EK
AT91SAM9261-EK
AT91SAM9263-EK
AT91SAM9RL-EK
It was downloaded from http://www.linux4sam.org/twiki/pub/Linux4SAM/U-Boot/u-boot-1.1.5_atmel_1.5.diff.bz2
Signed-off-by: Justin Waters <justin.waters@timesys.com>
Diffstat (limited to 'cpu')
-rw-r--r-- | cpu/arm920t/at91rm9200/interrupts.c | 4 | ||||
-rw-r--r-- | cpu/arm926ejs/at91sam926x/Makefile | 44 | ||||
-rw-r--r-- | cpu/arm926ejs/at91sam926x/config.mk | 29 | ||||
-rw-r--r-- | cpu/arm926ejs/at91sam926x/ether.c | 454 | ||||
-rw-r--r-- | cpu/arm926ejs/at91sam926x/interrupts.c | 207 | ||||
-rw-r--r-- | cpu/arm926ejs/at91sam926x/lcd.c | 279 | ||||
-rw-r--r-- | cpu/arm926ejs/at91sam926x/lcd_lut.h | 271 | ||||
-rw-r--r-- | cpu/arm926ejs/at91sam926x/serial.c | 116 | ||||
-rw-r--r-- | cpu/arm926ejs/at91sam926x/spi.c | 243 | ||||
-rw-r--r-- | cpu/arm926ejs/at91sam926x/usb_ohci.c | 1709 | ||||
-rw-r--r-- | cpu/arm926ejs/at91sam926x/usb_ohci.h | 417 | ||||
-rw-r--r-- | cpu/arm926ejs/interrupts.c | 4 | ||||
-rw-r--r-- | cpu/arm926ejs/start.S | 8 |
13 files changed, 3779 insertions, 6 deletions
diff --git a/cpu/arm920t/at91rm9200/interrupts.c b/cpu/arm920t/at91rm9200/interrupts.c index 10546027f8d..f0f8e8ec46c 100644 --- a/cpu/arm920t/at91rm9200/interrupts.c +++ b/cpu/arm920t/at91rm9200/interrupts.c @@ -175,14 +175,14 @@ void reset_cpu (ulong ignored) #ifdef CONFIG_USART1 AT91PS_USART us = AT91C_BASE_US1; #endif -#ifdef CONFIG_AT91RM9200DK +#if defined CONFIG_AT91RM9200DK || defined CONFIG_AT91RM9200EK AT91PS_PIO pio = AT91C_BASE_PIOA; #endif /*shutdown the console to avoid strange chars during reset */ us->US_CR = (AT91C_US_RSTRX | AT91C_US_RSTTX); -#ifdef CONFIG_AT91RM9200DK +#if defined CONFIG_AT91RM9200DK || defined CONFIG_AT91RM9200EK /* Clear PA19 to trigger the hard reset */ pio->PIO_CODR = 0x00080000; pio->PIO_OER = 0x00080000; diff --git a/cpu/arm926ejs/at91sam926x/Makefile b/cpu/arm926ejs/at91sam926x/Makefile new file mode 100644 index 00000000000..83a82c42fe5 --- /dev/null +++ b/cpu/arm926ejs/at91sam926x/Makefile @@ -0,0 +1,44 @@ +# +# (C) Copyright 2000-2003 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB = lib$(SOC).a + + +OBJS = serial.o interrupts.o usb_ohci.o lcd.o ether.o spi.o +SOBJS = + +all: .depend $(LIB) + +$(LIB): $(OBJS) $(SOBJS) + $(AR) crv $@ $(OBJS) $(SOBJS) + +######################################################################### + +.depend: Makefile $(OBJS:.o=.c) + $(CC) -M $(CFLAGS) $(OBJS:.o=.c) > $@ + +sinclude .depend + +######################################################################### diff --git a/cpu/arm926ejs/at91sam926x/config.mk b/cpu/arm926ejs/at91sam926x/config.mk new file mode 100644 index 00000000000..8866690cd29 --- /dev/null +++ b/cpu/arm926ejs/at91sam926x/config.mk @@ -0,0 +1,29 @@ +# +# (C) Copyright 2002 +# Gary Jennejohn, DENX Software Engineering, <gj@denx.de> +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +PLATFORM_RELFLAGS += -fno-strict-aliasing -fno-common -ffixed-r8 +PLATFORM_RELFLAGS += $(call cc-option,-msoft-float,) + +PLATFORM_CPPFLAGS += -march=armv5te +PLATFORM_CPPFLAGS += $(call cc-option,-mtune=arm926ejs,) +PLATFORM_CPPFLAGS += $(call cc-option,-mapcs-32,) diff --git a/cpu/arm926ejs/at91sam926x/ether.c b/cpu/arm926ejs/at91sam926x/ether.c new file mode 100644 index 00000000000..46b9b985282 --- /dev/null +++ b/cpu/arm926ejs/at91sam926x/ether.c @@ -0,0 +1,454 @@ +/* + * (C) Copyright 2006 + * Author : Lacressonniere Nicolas (Atmel) + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#ifndef CONFIG_DRIVER_DM9000 /* SAM9261EK uses DM9000 Phy */ + +#ifdef CONFIG_DRIVER_ETHER +#if (CONFIG_COMMANDS & CFG_CMD_NET) + +#include <net.h> +#include <at91_net.h> +#include <miiphy.h> +#include <dm9161.h> + + +/* ----- Ethernet Buffer definitions ----- */ + +/* Receive Transfer descriptor structure */ +typedef struct _AT91S_RxTdDescriptor { + unsigned int addr; + union + { + unsigned int status; + struct { + unsigned int Length:12; + unsigned int Rxbuf_off:2; + unsigned int StartOfFrame:1; + unsigned int EndOfFrame:1; + unsigned int Cfi:1; + unsigned int VlanPriority:3; + unsigned int PriorityTag:1; + unsigned int VlanTag:1; + unsigned int TypeID:1; + unsigned int Sa4Match:1; + unsigned int Sa3Match:1; + unsigned int Sa2Match:1; + unsigned int Sa1Match:1; + unsigned int Res0:1; + unsigned int ExternalAdd:1; + unsigned int UniCast:1; + unsigned int MultiCast:1; + unsigned int BroadCast:1; + } S_Status; + } U_Status; +} AT91S_RxTdDescriptor, *AT91PS_RxTdDescriptor; + + +/* Transmit Transfer descriptor structure */ +typedef struct _AT91S_TxTdDescriptor { + unsigned int addr; + union + { + unsigned int status; + struct { + unsigned int Length:11; + unsigned int Res0:4; + unsigned int LastBuff:1; + unsigned int NoCrc:1; + unsigned int Res1:10; + unsigned int BufExhausted:1; + unsigned int TransmitUnderrun:1; + unsigned int TransmitError:1; + unsigned int Wrap:1; + unsigned int BuffUsed:1; + } S_Status; + } U_Status; +} AT91S_TxTdDescriptor, *AT91PS_TxTdDescriptor; + + +#define RBF_ADDR 0xfffffffc +#define RBF_OWNER (1<<0) +#define RBF_WRAP (1<<1) +#define RBF_BROADCAST (1<<31) +#define RBF_MULTICAST (1<<30) +#define RBF_UNICAST (1<<29) +#define RBF_EXTERNAL (1<<28) +#define RBF_UNKOWN (1<<27) +#define RBF_SIZE 0x07ff +#define RBF_LOCAL4 (1<<26) +#define RBF_LOCAL3 (1<<25) +#define RBF_LOCAL2 (1<<24) +#define RBF_LOCAL1 (1<<23) +#define RBF_EOF (1<<15) +#define RBF_SOF (1<<14) + +#define MAX_ETH_FRAME_LEN 0x600 /* 1536 bytes. Max ethernet frame size */ + +#define RBF_FRAMEMAX 64 +#define RBF_FRAMELEN 128 + +#define TBF_FRAMEMAX 64 +#define TBF_FRAMELEN 0x600 + +/* alignment as per Errata #11 (64 bytes) is insufficient! */ +AT91S_RxTdDescriptor RxtdList[RBF_FRAMEMAX] __attribute((aligned(512))); +AT91S_TxTdDescriptor TxtdList[TBF_FRAMEMAX] __attribute((aligned(512))); + +unsigned char rbf_framebuf[RBF_FRAMEMAX][RBF_FRAMELEN] __attribute((aligned(4))); +unsigned char tbf_framebuf[TBF_FRAMEMAX][TBF_FRAMELEN] __attribute((aligned(4))); + +unsigned int RxBuffIndex = 0; +unsigned int TxBuffIndex = 0; + +static unsigned char stage_rx_buffer[MAX_ETH_FRAME_LEN]; +volatile unsigned char *rx_pkt_tmp = stage_rx_buffer; + +/* structure to interface the PHY */ +AT91S_PhyOps PhyOps; + +AT91PS_EMAC p_mac; + +extern int AT91F_EMAC_Hardware_Init(void); + +/*********** EMAC Phy layer Management functions *************************/ +/* + * Name: + * at91_EmacEnableMDIO + * Description: + * Enables the MDIO bit in MAC control register + * Arguments: + * p_mac - pointer to struct AT91S_EMAC + * Return value: + * none + */ +void at91_EmacEnableMDIO (AT91PS_EMAC p_mac) +{ + /* Mac CTRL reg set for MDIO enable */ + p_mac->EMAC_NCR |= AT91C_EMAC_MPE; /* Management port enable */ +} + +/* + * Name: + * at91_EmacDisableMDIO + * Description: + * Disables the MDIO bit in MAC control register + * Arguments: + * p_mac - pointer to struct AT91S_EMAC + * Return value: + * none + */ +void at91_EmacDisableMDIO (AT91PS_EMAC p_mac) +{ + /* Mac CTRL reg set for MDIO disable */ + p_mac->EMAC_NCR &= ~AT91C_EMAC_MPE; /* Management port disable */ +} + + +/* + * Name: + * at91_EmacReadPhy + * Description: + * Reads data from the PHY register + * Arguments: + * dev - pointer to struct net_device + * RegisterAddress - unsigned char + * pInput - pointer to value read from register + * Return value: + * TRUE - if data read successfully + */ +uchar at91_EmacReadPhy (AT91PS_EMAC p_mac, + unsigned char PhyAddress, + unsigned char RegisterAddress, + unsigned short *pInput) +{ + p_mac->EMAC_MAN = (AT91C_EMAC_SOF & (0x01 << 30)) | + (AT91C_EMAC_RW & (0x2 << 28)) | + ((PhyAddress & 0x1F) << 23) | + (RegisterAddress << 18) | + (AT91C_EMAC_CODE & (0x2 << 16)); + + /* Wait until IDLE bit in Network Status register is cleared */ + while (!(p_mac->EMAC_NSR & AT91C_EMAC_IDLE)); + + *pInput = (unsigned short) (p_mac->EMAC_MAN & 0x0000FFFF); + + return TRUE; +} + + +/* + * Name: + * at91_EmacWritePhy + * Description: + * Writes data to the PHY register + * Arguments: + * dev - pointer to struct net_device + * RegisterAddress - unsigned char + * pOutput - pointer to value to be written in the register + * Return value: + * TRUE - if data read successfully + */ +uchar at91_EmacWritePhy (AT91PS_EMAC p_mac, + unsigned char PhyAddress, + unsigned char RegisterAddress, + unsigned short *pOutput) +{ + p_mac->EMAC_MAN = (AT91C_EMAC_SOF & (0x01 << 30)) | + (AT91C_EMAC_RW & (0x1 << 28)) | + ((PhyAddress & 0x1F) << 23) | + (RegisterAddress << 18) | + (AT91C_EMAC_CODE & (0x2 << 16)) | + *pOutput; + + /* Wait until IDLE bit in Network Status register is cleared */ + while (!(p_mac->EMAC_NSR & AT91C_EMAC_IDLE)); + + return TRUE; +} + +/*---------------------------------------------------------------------------- */ +/* \fn AT91F_EMACInit */ +/* \brief This function initialise the ethernet */ +/* \return Status ( Success = 0) */ +/*---------------------------------------------------------------------------- */ +int AT91F_EMACInit(bd_t * bd, + unsigned int pRxTdList, + unsigned int pTxTdList) +{ + unsigned int tick = 0; + unsigned short status; + + /* Wait for PHY auto negotiation completed */ + at91_EmacEnableMDIO(p_mac); + + do { + at91_EmacReadPhy(p_mac, AT91C_PHY_ADDR, DM9161_BMSR, &status); + at91_EmacReadPhy(p_mac, AT91C_PHY_ADDR, DM9161_BMSR, &status); + + tick++; + } + while (!(status & DM9161_AUTONEG_COMP) && (tick < AT91C_ETH_TIMEOUT)); + + at91_EmacDisableMDIO(p_mac); + + if (tick == AT91C_ETH_TIMEOUT) + { + printf ("-E- Autonegociation Timeout\n\r"); + return 1; + } else + printf ("End of Autonegociation\n\r"); + + /* the sequence write EMAC_SA1L and write EMAC_SA1H must be respected */ + p_mac->EMAC_SA1L = (bd->bi_enetaddr[3] << 24) | (bd->bi_enetaddr[2] << 16) + | (bd->bi_enetaddr[1] << 8) | (bd->bi_enetaddr[0]); + p_mac->EMAC_SA1H = (bd->bi_enetaddr[5] << 8) | (bd->bi_enetaddr[4]); + + p_mac->EMAC_RBQP = pRxTdList; + p_mac->EMAC_TBQP = pTxTdList; + + + p_mac->EMAC_NCFGR |= (AT91C_EMAC_CAF | AT91C_EMAC_NBC ); + p_mac->EMAC_NCFGR &= ~(AT91C_EMAC_CLK); + + p_mac->EMAC_NCR |= (AT91C_EMAC_TE | AT91C_EMAC_RE); + + return 0; +} + +int eth_init (bd_t * bd) +{ + volatile unsigned int uStatus; + unsigned int val, i; + int ret; + static int first_time = 0; + + if (!first_time) + { + /* Init PIOs in peripheral mode */ + AT91F_EMAC_Hardware_Init(); + + *AT91C_PMC_PCER = 1 << AT91C_ID_EMAC; /* Peripheral Clock Enable Register */ + + /* Disable Tx and Rx */ + p_mac->EMAC_NCR = 0; + /* Clear statistics */ + p_mac->EMAC_NCR = AT91C_EMAC_CLRSTAT; + + uStatus = p_mac->EMAC_ISR; + p_mac->EMAC_TSR = 0xFFFFFFFF; + p_mac->EMAC_RSR = 0xFFFFFFFF; + + /* We don't use interrupts. disable them */ + p_mac->EMAC_IDR = 0xFFFFFFFF; + + /* Enable EMAC in MII mode and enable MII Clock (25MHz) */ + p_mac->EMAC_USRIO = AT91C_EMAC_CLKEN; + +#ifdef CONFIG_AT91C_USE_RMII + p_mac->EMAC_USRIO |= AT91C_EMAC_RMII; +#endif + + /* Set MDIO clock */ +#if (AT91C_MASTER_CLOCK > 80000000) + p_mac->EMAC_NCFGR |= AT91C_EMAC_CLK_HCLK_64; +#else + p_mac->EMAC_NCFGR |= AT91C_EMAC_CLK_HCLK_32; +#endif + + /* Init Ethernet buffers */ + RxBuffIndex = 0; + TxBuffIndex = 0; + + /* Initialise RxtdList descriptor */ + for (i = 0; i < RBF_FRAMEMAX; ++i) { + val = (unsigned int)(&rbf_framebuf[i][0]); + RxtdList[i].addr = val & 0xFFFFFFF8; + RxtdList[i].U_Status.status = 0; + } + /* Set the WRAP bit at the end of the list descriptor */ + RxtdList[RBF_FRAMEMAX-1].addr |= RBF_WRAP; + + /* Initialise TxtdList descriptor */ + for (i = 0; i < TBF_FRAMEMAX; ++i) { + val = (unsigned int)(&tbf_framebuf[i][0]); + TxtdList[i].addr = val & 0xFFFFFFF8; + TxtdList[i].U_Status.status = 0; + TxtdList[i].U_Status.S_Status.BuffUsed = 1; + } + TxtdList[0].U_Status.S_Status.BuffUsed = 0; + /* Set the WRAP bit at the end of the list descriptor */ + TxtdList[TBF_FRAMEMAX-1].U_Status.S_Status.Wrap = 1; + + at91_GetPhyInterface (&PhyOps); + + /* Phy Software Reset */ + if (!PhyOps.Reset(p_mac)) + { + printf ("PHY not reset!!\n\r"); + return 0; + } + + if (!PhyOps.IsPhyConnected (p_mac)) + { + printf ("PHY not connected!!\n\r"); + return 0; + } + + /* MII management start from here */ + if (!(ret = PhyOps.Init (p_mac))) + { + printf ("MAC: error during RMII initialization\n"); + return 0; + } + + if (AT91F_EMACInit(bd, (unsigned int)RxtdList, (unsigned int)TxtdList)) + return 0; + + first_time = 1; + } + + /* Initialize Receive Frame Pointer */ + rx_pkt_tmp = stage_rx_buffer; + + return 0; +} + +int eth_send (volatile void *packet, int length) +{ + + if ((TxtdList[TxBuffIndex].U_Status.S_Status.BuffUsed == 0)) + { + TxtdList[TxBuffIndex].addr = (unsigned int)packet; + TxtdList[TxBuffIndex].U_Status.S_Status.Length = length; + TxtdList[TxBuffIndex].U_Status.S_Status.LastBuff = 1; + } + else + return 2; + + p_mac->EMAC_NCR |= AT91C_EMAC_TSTART; + + while (!(p_mac->EMAC_TSR & AT91C_EMAC_COMP)); + + p_mac->EMAC_TSR |= AT91C_EMAC_COMP; + + TxtdList[TxBuffIndex].U_Status.S_Status.Length = 0; + TxtdList[TxBuffIndex].U_Status.S_Status.LastBuff = 0; + + if (TxBuffIndex == (TBF_FRAMEMAX - 1)) + TxBuffIndex = 0; + else + TxBuffIndex ++; + + TxtdList[TxBuffIndex].U_Status.S_Status.BuffUsed = 0; + + if (p_mac->EMAC_TSR & AT91C_EMAC_UBR) + p_mac->EMAC_TSR |= AT91C_EMAC_UBR; + + return 0; +} + +int eth_rx (void) +{ + int size = 0; + + while (!size) + { + while (!(RxtdList[RxBuffIndex].addr & RBF_OWNER)); + + if (RxtdList[RxBuffIndex].U_Status.S_Status.StartOfFrame == 1) + rx_pkt_tmp = stage_rx_buffer; + + memcpy((uchar *)rx_pkt_tmp, (uchar *)(RxtdList[RxBuffIndex].addr & RBF_ADDR), RBF_FRAMELEN); + rx_pkt_tmp += RBF_FRAMELEN; + + if (RxtdList[RxBuffIndex].U_Status.S_Status.EndOfFrame == 1) + { + size = RxtdList[RxBuffIndex].U_Status.S_Status.Length; + + NetReceive ((uchar *)stage_rx_buffer, size); + if (p_mac->EMAC_RSR & AT91C_EMAC_REC) + p_mac->EMAC_RSR |= AT91C_EMAC_REC; + + rx_pkt_tmp = stage_rx_buffer; + } + + RxtdList[RxBuffIndex].U_Status.status = 0; + RxtdList[RxBuffIndex].addr &= ~RBF_OWNER; + + if (RxtdList[RxBuffIndex].addr & RBF_WRAP) + RxBuffIndex = 0; + else + RxBuffIndex++; + } + + return size; +} + +void eth_halt (void) +{ +}; + +#endif /* CONFIG_COMMANDS & CFG_CMD_NET */ +#endif /* CONFIG_DRIVER_DM9000 */ +#endif /* CONFIG_DRIVER_ETHER */ diff --git a/cpu/arm926ejs/at91sam926x/interrupts.c b/cpu/arm926ejs/at91sam926x/interrupts.c new file mode 100644 index 00000000000..85b33f2846b --- /dev/null +++ b/cpu/arm926ejs/at91sam926x/interrupts.c @@ -0,0 +1,207 @@ +/* + * (C) Copyright 2005 + * M. Amine SAYA ATMEL Rousset, France. + * + * (C) Copyright 2003 + * Texas Instruments <www.ti.com> + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Alex Zuepke <azu@sysgo.de> + * + * (C) Copyright 2002-2004 + * Gary Jennejohn, DENX Software Engineering, <gj@denx.de> + * + * (C) Copyright 2004 + * Philippe Robin, ARM Ltd. <philippe.robin@arm.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/arch/hardware.h> + + +#define TIMER_LOAD_VAL (AT91C_MASTER_CLOCK / (16 * CFG_HZ)) + +/* macro to read the 20-bit wide AT91SAM9263 PITC value*/ +#define READ_RESET_TIMER (p_pitc->PITC_PIVR) +#define READ_TIMER (p_pitc->PITC_PIIR) + +#define HIGH_TIMER_VALUE(X) ((((X) & 0xfff00000) >> 20)) +#define TIMER_VALUE(X) ((((X) & 0xfff00000) >> 20) * TIMER_LOAD_VAL + ((X) & 0x000fffff)) + + +ulong get_timer_masked (void); + +AT91PS_PITC p_pitc; + +static ulong timestamp; +static ulong lastinc; + +/* nothing really to do with interrupts, just starts up a counter. */ +int interrupt_init (void) +{ + p_pitc = AT91C_BASE_PITC; + + /* Enable PITC Clock + The clock is already enabled for system controller in boot */ + *AT91C_PMC_PCER = 1 << AT91C_ID_SYS; + + /* Enable PITC */ + p_pitc->PITC_PIMR = AT91C_PITC_PITEN; + + /* Load PITC_PIMR with the right timer value */ + p_pitc->PITC_PIMR |= TIMER_LOAD_VAL; + + reset_timer_masked(); + + return 0; +} + + +/* + * timer without interrupts + */ + +void reset_timer (void) +{ + reset_timer_masked (); +} + +ulong get_timer (ulong base) +{ + return get_timer_masked () - base; +} + +void set_timer (ulong t) +{ + timestamp = t; +} + +ulong get_all_timer(void) +{ + ulong now; + + now = READ_TIMER; + return TIMER_VALUE(now); +} + + +/* delay x useconds AND perserve advance timstamp value */ +void udelay (unsigned long usec) +{ + ulong tmo; + + if(usec >= 1000){ /* if "big" number, spread normalization to seconds */ + tmo = usec / 1000; /* start to normalize for usec to ticks per sec */ + tmo *= (AT91C_MASTER_CLOCK/16); /* find number of "ticks" to wait to achieve target */ + tmo /= 1000; /* finish normalize. */ + }else{ /* else small number, don't kill it prior to HZ multiply */ + tmo = usec * (AT91C_MASTER_CLOCK/16); + tmo /= (1000*1000); + } + + reset_timer_masked(); + tmo += get_all_timer(); /* get current timestamp */ + + while(get_all_timer() < tmo); /* loop till event */ +} + +void reset_timer_masked (void) +{ + /* reset time */ + lastinc = READ_RESET_TIMER; + + /* start "advancing" time stamp from 0 */ + timestamp = 0; +} + +ulong get_timer_masked (void) +{ + ulong now = READ_TIMER; + + now = HIGH_TIMER_VALUE(now); + + timestamp = now; + + lastinc = now; + + return timestamp * CFG_HZ; +} + +/* waits specified delay value and resets timestamp */ +void udelay_masked (unsigned long usec) +{ + ulong tmo; + + if(usec >= 1000){ /* if "big" number, spread normalization to seconds */ + tmo = usec / 1000; /* start to normalize for usec to ticks per sec */ + tmo *= (AT91C_MASTER_CLOCK/16); /* find number of "ticks" to wait to achieve target */ + tmo /= 1000; /* finish normalize. */ + }else{ /* else small number, don't kill it prior to HZ multiply */ + tmo = usec * (AT91C_MASTER_CLOCK/16); + tmo /= (1000*1000); + } + + reset_timer_masked(); /* set "advancing" timestamp to 0, set lastinc value */ + tmo += get_all_timer(); + + while (get_all_timer() < tmo) /* wait for time stamp to overtake tick number.*/ + /*NOP*/; +} + +/* + * This function is derived from PowerPC code (read timebase as long long). + * On ARM it just returns the timer value. + */ +unsigned long long get_ticks(void) +{ + return get_timer(0); +} + +/* + * This function is derived from PowerPC code (timebase clock frequency). + * On ARM it returns the number of timer ticks per second. + */ +ulong get_tbclk (void) +{ + ulong tbclk; + + tbclk = CFG_HZ; + return tbclk; +} + +/* + * Reset the cpu by setting up the Reset Controller + * on the SAM9263EK board + */ +void reset_cpu (ulong ignored) +{ + /* Use Reset Controller */ + + + while (1); + /* Never reached */ +} + diff --git a/cpu/arm926ejs/at91sam926x/lcd.c b/cpu/arm926ejs/at91sam926x/lcd.c new file mode 100644 index 00000000000..affb6be01b0 --- /dev/null +++ b/cpu/arm926ejs/at91sam926x/lcd.c @@ -0,0 +1,279 @@ +/* + * (C) Copyright 2005 + * M. Amine SAYA, ATMEL Rousset, France. + * Added support to AT91SAM9261 LCD Controller + * + * (C) Copyright 2001-2002 + * Wolfgang Denk, DENX Software Engineering -- wd@denx.de + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/************************************************************************/ +/* ** HEADER FILES */ +/************************************************************************/ + +#include <config.h> +#include <common.h> +#include <version.h> +#include <stdarg.h> +#include <linux/types.h> +#include <devices.h> + +#undef CONFIG_LCD_LOGO +#include <lcd.h> +#include "lcd_lut.h" + + +#ifdef CONFIG_LCD +//#include <asm/arch/pio.h> + +#define LCD_BPP LCD_COLOR8 + +/* #define DEBUG */ +#define FB_SYNC_HOR_HIGH_ACT 1 /* horizontal sync high active */ +#define FB_SYNC_VERT_HIGH_ACT 2 /* vertical sync high active */ + +/* More or less configurable parameters for LCDC controller*/ +#define SIDSAFB_FIFO_SIZE 512 +#define SIDSAFB_DMA_BURST_LEN 16 +#define SIDSAFB_CRST_VAL 0xc8 /* 0xda */ + + +AT91PS_SYS AT91_SYS = (AT91PS_SYS)AT91C_BASE_SYS; + +/* 640x480x16 @ 61 Hz */ +vidinfo_t panel_info = { + vl_col: 240, + vl_row: 320, + vl_width: 64, + vl_height: 86, + vl_pixclock: 4965000, + vl_hsp: FB_SYNC_HOR_HIGH_ACT, + vl_vsp: FB_SYNC_VERT_HIGH_ACT, + vl_bpix: 3, + vl_tft: 1, + /* Horizontal control Timing */ + vl_hpw: 5, + vl_blw: 1, + vl_elw: 33, + /* Vertical control Timing */ + vl_vpw: 1, + vl_bfw: 1, + vl_efw: 0, +}; + + + + +/*----------------------------------------------------------------------*/ +void lcd_setcolreg (ushort regno, ushort red, ushort green, ushort blue); +void lcd_ctrl_init (void *lcdbase); +void lcd_enable (void); +void lcd_init_lut(void); + + +int lcd_line_length; +int lcd_color_fg; +int lcd_color_bg; + +void *lcd_base; /* Start of framebuffer memory */ +void *lcd_console_address; /* Start of console buffer */ + +short console_col; +short console_row; + + + +/************************************************************************/ +void lcd_init_lut(void) +{ + unsigned int i; + for(i =0; i <256; i++) { +#if defined(CONFIG_AT91SAM9261EK) || defined(CONFIG_AT91SAM9263EK) + panel_info.controller.lcdc->LCDC_LUT_ENTRY[i] = + ((rgbPalette[i].red) >> 3 | + ((rgbPalette[i].green & 0xf8) << 2) | + ((rgbPalette[i].blue & 0xf8) << 7)); +#else + panel_info.controller.lcdc->LCDC_LUT_ENTRY[i] = + ((rgbPalette[i].blue) >> 3 | + ((rgbPalette[i].green & 0xfc) << 3) | + ((rgbPalette[i].red & 0xf8) << 8)); +#endif + } +} + + +void lcd_setcolreg (ushort regno, ushort red, + ushort green, ushort blue) +{ +#if defined(CONFIG_AT91SAM9261EK) || defined(CONFIG_AT91SAM9263EK) + panel_info.controller.lcdc->LCDC_LUT_ENTRY[regno] = ((red >> 3) | + ((green & 0xf8) << 2) | + ((blue & 0xf8) << 7)); +#else + panel_info.controller.lcdc->LCDC_LUT_ENTRY[regno] = ((blue >> 3) | + ((green & 0xfc) << 3) | + ((red & 0xf8) << 8)); +#endif +} + + +void lcd_ctrl_init (void *lcdbase) +{ + unsigned long value; + + AT91F_LCDC_TFT_CfgPIO(); + +#ifdef CONFIG_AT91SAM9261EK + /* Enable HCLOCK in PMC */ + *AT91C_PMC_SCER = AT91C_PMC_HCK1; +#endif + +#ifdef CONFIG_AT91SAM9263EK + /* Enable LCDC clock in PMC */ + *AT91C_PMC_PCER = 1 << AT91C_ID_LCDC; +#endif +#ifdef CONFIG_AT91SAM9RLEK + /* Enable LCDC clock in PMC */ + *AT91C_PMC_PCER = 1 << AT91C_ID_LCDC; +#endif + /* Init controller field in panel_info */ + panel_info.controller.lcdc = (AT91PS_LCDC) AT91C_BASE_LCDC; + panel_info.controller.frame_buffer = (unsigned long) lcdbase; + + /* Turn off the LCD controller and the DMA controller */ + panel_info.controller.lcdc->LCDC_PWRCON = 0x00; + panel_info.controller.lcdc->LCDC_DMACON = 0; + + /* Reset LCDC DMA */ + panel_info.controller.lcdc->LCDC_DMACON = AT91C_LCDC_DMARST; + + /* ...set frame size and burst length = 8 words (?) */ + value = ((unsigned int)panel_info.vl_row * (unsigned int)panel_info.vl_col * (unsigned int)NBITS(panel_info.vl_bpix)) / 32; + value |= ((512 - 1) << 24); + panel_info.controller.lcdc->LCDC_FRMCFG = value; + + /* Set pixel clock */ + value = AT91C_MASTER_CLOCK / panel_info.vl_pixclock; + + if (AT91C_MASTER_CLOCK % panel_info.vl_pixclock) + value++; + + value = (value / 2) - 1; + + if (!value) { + panel_info.controller.lcdc->LCDC_LCDCON1 = AT91C_LCDC_BYPASS; + } else + panel_info.controller.lcdc->LCDC_LCDCON1 = value << 12; + + /* Initialize control register 2 */ + if (panel_info.vl_tft) + value = ( AT91C_LCDC_MEMOR_LITTLEIND | AT91C_LCDC_DISTYPE_TFT | AT91C_LCDC_CLKMOD); + else + value = ( AT91C_LCDC_MEMOR_LITTLEIND | AT91C_LCDC_CLKMOD); + + if (!((unsigned int)panel_info.vl_hsp & FB_SYNC_HOR_HIGH_ACT)) + value |= 1 << 10; /* INVLINE */ + if (!((unsigned int)panel_info.vl_vsp & FB_SYNC_VERT_HIGH_ACT)) + value |= 1 << 9; /* INVFRAME */ + + value |= (unsigned int)(panel_info.vl_bpix << 5); + + panel_info.controller.lcdc->LCDC_LCDCON2 = value; + + /* Vertical timing */ + value = (unsigned int)((panel_info.vl_vpw - 1) << 16); + value |= (unsigned int)(panel_info.vl_bfw << 8); + value |= (unsigned int)panel_info.vl_efw; + panel_info.controller.lcdc->LCDC_TIM1 = value; + + /* Horizontal timing */ + value = (unsigned int)((panel_info.vl_elw - 1) << 21); + value |= (unsigned int)((panel_info.vl_hpw - 1) << 8); + value |= (unsigned int)(panel_info.vl_blw - 1); + + panel_info.controller.lcdc->LCDC_TIM2 = value; + + value = (unsigned int)((panel_info.vl_col - 1) << 21); + value |= (unsigned int)(panel_info.vl_row - 1); + + panel_info.controller.lcdc->LCDC_LCDFRCFG = value; + + /* FIFO Threshold: Use formula from data sheet */ + value = SIDSAFB_FIFO_SIZE - (2 * SIDSAFB_DMA_BURST_LEN + 3); + panel_info.controller.lcdc->LCDC_FIFO = value; + + /* Toggle LCD_MODE every frame */ + value = 0; + panel_info.controller.lcdc->LCDC_MVAL = value; + + /* Disable all interrupts */ + panel_info.controller.lcdc->LCDC_IDR = ~0UL; + /* Set contrast */ + value = AT91C_LCDC_PS_DIVIDEDBYEIGHT | AT91C_LCDC_POL_POSITIVEPULSE | AT91C_LCDC_ENA_PWMGEMENABLED; + panel_info.controller.lcdc->LCDC_CTRSTCON = value; + panel_info.controller.lcdc->LCDC_CTRSTVAL = 0xDA; /*SIDSAFB_CRST_VAL;*/ + + panel_info.controller.lcdc->LCDC_BA1 = (unsigned int)lcdbase; + panel_info.controller.lcdc->LCDC_FRMCFG = (15 << 24) + ((unsigned int)panel_info.vl_col * (unsigned int)panel_info.vl_row * (unsigned int)NBITS(panel_info.vl_bpix)) / 32; + panel_info.controller.lcdc->LCDC_DMACON = AT91C_LCDC_DMAEN; + panel_info.controller.lcdc->LCDC_PWRCON = AT91C_LCDC_PWR | 0x0C; +} + + +/*----------------------------------------------------------------------*/ +void lcd_enable (void) +{ +#ifdef CONFIG_AT91SAM9261EK + *AT91C_PIOA_SODR = AT91C_PIO_PA12; +#endif + +#ifdef CONFIG_AT91SAM9263EK + *AT91C_PIOD_SODR = AT91C_PIO_PD12; +#endif +} + +/*----------------------------------------------------------------------*/ +void lcd_disable (void) +{ +#ifdef CONFIG_AT91SAM9261EK + *AT91C_PIOA_CODR = AT91C_PIO_PA12; +#endif + +#ifdef CONFIG_AT91SAM9263EK + *AT91C_PIOD_CODR = AT91C_PIO_PD12; +#endif +} + +/*----------------------------------------------------------------------*/ + +ulong calc_fbsize (void) +{ + ulong size; + int line_length = ((unsigned int)panel_info.vl_col * (unsigned int)NBITS(panel_info.vl_bpix)) / 8; + + size = line_length * (unsigned int)panel_info.vl_row; + size += PAGE_SIZE; + + return size; +} + +#endif /* CONFIG_LCD */ diff --git a/cpu/arm926ejs/at91sam926x/lcd_lut.h b/cpu/arm926ejs/at91sam926x/lcd_lut.h new file mode 100644 index 00000000000..5885075920c --- /dev/null +++ b/cpu/arm926ejs/at91sam926x/lcd_lut.h @@ -0,0 +1,271 @@ +#ifndef _LCDLUT_H +#define _LCDLUT_H + +typedef struct _PALETTEENTRY { + unsigned char red; + unsigned char green; + unsigned char blue; + unsigned char peFlags; +} PALETTEENTRY, *PPALETTEENTRY; + +const PALETTEENTRY rgbPalette[256] = +{ + { 0x00, 0x00, 0x00, 0 }, /* 0 Sys Black gray 0 */ + { 0x80, 0x00, 0x00, 0 }, /* 1 Sys Dk Red */ + { 0x00, 0x80, 0x00, 0 }, /* 2 Sys Dk Green */ + { 0x80, 0x80, 0x00, 0 }, /* 3 Sys Dk Yellow */ + { 0x00, 0x00, 0x80, 0 }, /* 4 Sys Dk Blue */ + { 0x80, 0x00, 0x80, 0 }, /* 5 Sys Dk Violet */ + { 0x00, 0x80, 0x80, 0 }, /* 6 Sys Dk Cyan */ + { 0xc0, 0xc0, 0xc0, 0 }, /* 7 Sys Lt Grey gray 192 */ + { 0xc0, 0xdc, 0xc0, 0 }, /* 8 Sys 8 */ + { 0xa6, 0xca, 0xf0, 0 }, /* 9 Sys 9 (the first 10 are fixed by Windows) */ + { 0x04, 0x04, 0x04, 0 }, /* 10 gray 4 */ + { 0x08, 0x08, 0x08, 0 }, /* 11 gray 8 */ + { 0x0c, 0x0c, 0x0c, 0 }, /* 12 gray 12 */ + { 0x11, 0x11, 0x11, 0 }, /* 13 gray 17 */ + { 0x16, 0x16, 0x16, 0 }, /* 14 gray 22 */ + { 0x1c, 0x1c, 0x1c, 0 }, /* 15 gray 28 */ + { 0x22, 0x22, 0x22, 0 }, /* 16 gray 34 */ + { 0x29, 0x29, 0x29, 0 }, /* 17 gray 41 */ + { 0x55, 0x55, 0x55, 0 }, /* 18 swapped so inversions look good gray 85 */ + { 0x4d, 0x4d, 0x4d, 0 }, /* 19 swapped so inversions look good gray 77 */ + { 0x42, 0x42, 0x42, 0 }, /* 20 swapped so inversions look good gray 66 */ + { 0x39, 0x39, 0x39, 0 }, /* 21 swapped so inversions look good gray 57 */ + { 0xFF, 0x7C, 0x80, 0 }, /* 22 R255 G124 B128 */ + { 0xFF, 0x50, 0x50, 0 }, /* 23 R255 G80 B80 */ + { 0xD6, 0x00, 0x93, 0 }, /* 24 R214 G0 B147 */ + { 0xCC, 0xEC, 0xFF, 0 }, /* 25 R204 G236 B255 */ + { 0xEF, 0xD6, 0xC6, 0 }, /* 26 R239 G214 B198 */ + { 0xE7, 0xE7, 0xD6, 0 }, /* 27 R231 G231 B214 */ + { 0xAD, 0xA9, 0x90, 0 }, /* 28 R173 G169 B144 */ + { 0x33, 0x00, 0x00, 0 }, /* 29 */ + { 0x66, 0x00, 0x00, 0 }, /* 30 */ + { 0x99, 0x00, 0x00, 0 }, /* 31 */ + { 0xcc, 0x00, 0x00, 0 }, /* 32 */ + { 0x00, 0x33, 0x00, 0 }, /* 33 */ + { 0x33, 0x33, 0x00, 0 }, /* 34 */ + { 0x66, 0x33, 0x00, 0 }, /* 35 */ + { 0x99, 0x33, 0x00, 0 }, /* 36 */ + { 0xcc, 0x33, 0x00, 0 }, /* 37 */ + { 0xff, 0x33, 0x00, 0 }, /* 38 */ + { 0x00, 0x66, 0x00, 0 }, /* 39 */ + { 0x33, 0x66, 0x00, 0 }, /* 40 */ + { 0x66, 0x66, 0x00, 0 }, /* 41 */ + { 0x99, 0x66, 0x00, 0 }, /* 42 */ + { 0xcc, 0x66, 0x00, 0 }, /* 43 */ + { 0xff, 0x66, 0x00, 0 }, /* 44 */ + { 0x00, 0x99, 0x00, 0 }, /* 45 */ + { 0x33, 0x99, 0x00, 0 }, /* 46 */ + { 0x66, 0x99, 0x00, 0 }, /* 47 */ + { 0x99, 0x99, 0x00, 0 }, /* 48 */ + { 0xcc, 0x99, 0x00, 0 }, /* 49 */ + { 0xff, 0x99, 0x00, 0 }, /* 50 */ + { 0x00, 0xcc, 0x00, 0 }, /* 51 */ + { 0x33, 0xcc, 0x00, 0 }, /* 52 */ + { 0x66, 0xcc, 0x00, 0 }, /* 53 */ + { 0x99, 0xcc, 0x00, 0 }, /* 54 */ + { 0xcc, 0xcc, 0x00, 0 }, /* 55 */ + { 0xff, 0xcc, 0x00, 0 }, /* 56 */ + { 0x66, 0xff, 0x00, 0 }, /* 57 */ + { 0x99, 0xff, 0x00, 0 }, /* 58 */ + { 0xcc, 0xff, 0x00, 0 }, /* 59 */ + { 0x00, 0x00, 0x33, 0 }, /* 60 */ + { 0x33, 0x00, 0x33, 0 }, /* 61 */ + { 0x66, 0x00, 0x33, 0 }, /* 62 */ + { 0x99, 0x00, 0x33, 0 }, /* 63 */ + { 0xcc, 0x00, 0x33, 0 }, /* 64 */ + { 0xff, 0x00, 0x33, 0 }, /* 65 */ + { 0x00, 0x33, 0x33, 0 }, /* 66 */ + { 0x33, 0x33, 0x33, 0 }, /* 67 gray 51 */ + { 0x66, 0x33, 0x33, 0 }, /* 68 */ + { 0x99, 0x33, 0x33, 0 }, /* 69 */ + { 0xcc, 0x33, 0x33, 0 }, /* 70 */ + { 0xff, 0x33, 0x33, 0 }, /* 71 */ + { 0x00, 0x66, 0x33, 0 }, /* 72 */ + { 0x33, 0x66, 0x33, 0 }, /* 73 */ + { 0x66, 0x66, 0x33, 0 }, /* 74 */ + { 0x99, 0x66, 0x33, 0 }, /* 75 */ + { 0xcc, 0x66, 0x33, 0 }, /* 76 */ + { 0xff, 0x66, 0x33, 0 }, /* 77 */ + { 0x00, 0x99, 0x33, 0 }, /* 78 */ + { 0x33, 0x99, 0x33, 0 }, /* 79 */ + { 0x66, 0x99, 0x33, 0 }, /* 80 */ + { 0x99, 0x99, 0x33, 0 }, /* 81 */ + { 0xcc, 0x99, 0x33, 0 }, /* 82 */ + { 0xff, 0x99, 0x33, 0 }, /* 83 */ + { 0x00, 0xcc, 0x33, 0 }, /* 84 */ + { 0x33, 0xcc, 0x33, 0 }, /* 85 */ + { 0x66, 0xcc, 0x33, 0 }, /* 86 */ + { 0x99, 0xcc, 0x33, 0 }, /* 87 */ + { 0xcc, 0xcc, 0x33, 0 }, /* 88 */ + { 0xff, 0xcc, 0x33, 0 }, /* 89 */ + { 0x33, 0xff, 0x33, 0 }, /* 90 */ + { 0x66, 0xff, 0x33, 0 }, /* 91 */ + { 0x99, 0xff, 0x33, 0 }, /* 92 */ + { 0xcc, 0xff, 0x33, 0 }, /* 93 */ + { 0xff, 0xff, 0x33, 0 }, /* 94 */ + { 0x00, 0x00, 0x66, 0 }, /* 95 */ + { 0x33, 0x00, 0x66, 0 }, /* 96 */ + { 0x66, 0x00, 0x66, 0 }, /* 97 */ + { 0x99, 0x00, 0x66, 0 }, /* 98 */ + { 0xcc, 0x00, 0x66, 0 }, /* 99 */ + { 0xff, 0x00, 0x66, 0 }, /* 100 */ + { 0x00, 0x33, 0x66, 0 }, /* 101 */ + { 0x33, 0x33, 0x66, 0 }, /* 102 */ + { 0x66, 0x33, 0x66, 0 }, /* 103 */ + { 0x99, 0x33, 0x66, 0 }, /* 104 */ + { 0xcc, 0x33, 0x66, 0 }, /* 105 */ + { 0xff, 0x33, 0x66, 0 }, /* 106 */ + { 0x00, 0x66, 0x66, 0 }, /* 107 */ + { 0x33, 0x66, 0x66, 0 }, /* 108 */ + { 0x66, 0x66, 0x66, 0 }, /* 109 gray 102 */ + { 0x99, 0x66, 0x66, 0 }, /* 110 */ + { 0xcc, 0x66, 0x66, 0 }, /* 111 */ + { 0x00, 0x99, 0x66, 0 }, /* 112 */ + { 0x33, 0x99, 0x66, 0 }, /* 113 */ + { 0x66, 0x99, 0x66, 0 }, /* 114 */ + { 0x99, 0x99, 0x66, 0 }, /* 115 */ + { 0xcc, 0x99, 0x66, 0 }, /* 116 */ + { 0xff, 0x99, 0x66, 0 }, /* 117 */ + { 0x00, 0xcc, 0x66, 0 }, /* 118 */ + { 0x33, 0xcc, 0x66, 0 }, /* 119 */ + { 0x99, 0xcc, 0x66, 0 }, /* 120 */ + { 0xcc, 0xcc, 0x66, 0 }, /* 121 */ + { 0xff, 0xcc, 0x66, 0 }, /* 122 */ + { 0x00, 0xff, 0x66, 0 }, /* 123 */ + { 0x33, 0xff, 0x66, 0 }, /* 124 */ + { 0x99, 0xff, 0x66, 0 }, /* 125 */ + { 0xcc, 0xff, 0x66, 0 }, /* 126 */ + { 0xff, 0x00, 0xcc, 0 }, /* 127 */ + { 0xcc, 0x00, 0xff, 0 }, /* 128 */ + { 0x00, 0x99, 0x99, 0 }, /* 129 */ + { 0x99, 0x33, 0x99, 0 }, /* 130 */ + { 0x99, 0x00, 0x99, 0 }, /* 131 */ + { 0xcc, 0x00, 0x99, 0 }, /* 132 */ + { 0x00, 0x00, 0x99, 0 }, /* 133 */ + { 0x33, 0x33, 0x99, 0 }, /* 134 */ + { 0x66, 0x00, 0x99, 0 }, /* 135 */ + { 0xcc, 0x33, 0x99, 0 }, /* 136 */ + { 0xff, 0x00, 0x99, 0 }, /* 137 */ + { 0x00, 0x66, 0x99, 0 }, /* 138 */ + { 0x33, 0x66, 0x99, 0 }, /* 139 */ + { 0x66, 0x33, 0x99, 0 }, /* 140 */ + { 0x99, 0x66, 0x99, 0 }, /* 141 */ + { 0xcc, 0x66, 0x99, 0 }, /* 142 */ + { 0xff, 0x33, 0x99, 0 }, /* 143 */ + { 0x33, 0x99, 0x99, 0 }, /* 144 */ + { 0x66, 0x99, 0x99, 0 }, /* 145 */ + { 0x99, 0x99, 0x99, 0 }, /* 146 gray 153 */ + { 0xcc, 0x99, 0x99, 0 }, /* 147 */ + { 0xff, 0x99, 0x99, 0 }, /* 148 */ + { 0x00, 0xcc, 0x99, 0 }, /* 149 */ + { 0x33, 0xcc, 0x99, 0 }, /* 150 */ + { 0x66, 0xcc, 0x66, 0 }, /* 151 */ + { 0x99, 0xcc, 0x99, 0 }, /* 152 */ + { 0xcc, 0xcc, 0x99, 0 }, /* 153 */ + { 0xff, 0xcc, 0x99, 0 }, /* 154 */ + { 0x00, 0xff, 0x99, 0 }, /* 155 */ + { 0x33, 0xff, 0x99, 0 }, /* 156 */ + { 0x66, 0xcc, 0x99, 0 }, /* 157 */ + { 0x99, 0xff, 0x99, 0 }, /* 158 */ + { 0xcc, 0xff, 0x99, 0 }, /* 159 */ + { 0xff, 0xff, 0x99, 0 }, /* 160 */ + { 0x00, 0x00, 0xcc, 0 }, /* 161 */ + { 0x33, 0x00, 0x99, 0 }, /* 162 */ + { 0x66, 0x00, 0xcc, 0 }, /* 163 */ + { 0x99, 0x00, 0xcc, 0 }, /* 164 */ + { 0xcc, 0x00, 0xcc, 0 }, /* 165 */ + { 0x00, 0x33, 0x99, 0 }, /* 166 */ + { 0x33, 0x33, 0xcc, 0 }, /* 167 */ + { 0x66, 0x33, 0xcc, 0 }, /* 168 */ + { 0x99, 0x33, 0xcc, 0 }, /* 169 */ + { 0xcc, 0x33, 0xcc, 0 }, /* 170 */ + { 0xff, 0x33, 0xcc, 0 }, /* 171 */ + { 0x00, 0x66, 0xcc, 0 }, /* 172 */ + { 0x33, 0x66, 0xcc, 0 }, /* 173 */ + { 0x66, 0x66, 0x99, 0 }, /* 174 */ + { 0x99, 0x66, 0xcc, 0 }, /* 175 */ + { 0xcc, 0x66, 0xcc, 0 }, /* 176 */ + { 0xff, 0x66, 0x99, 0 }, /* 177 */ + { 0x00, 0x99, 0xcc, 0 }, /* 178 */ + { 0x33, 0x99, 0xcc, 0 }, /* 179 */ + { 0x66, 0x99, 0xcc, 0 }, /* 180 */ + { 0x99, 0x99, 0xcc, 0 }, /* 181 */ + { 0xcc, 0x99, 0xcc, 0 }, /* 182 */ + { 0xff, 0x99, 0xcc, 0 }, /* 183 */ + { 0x00, 0xcc, 0xcc, 0 }, /* 184 */ + { 0x33, 0xcc, 0xcc, 0 }, /* 185 */ + { 0x66, 0xcc, 0xcc, 0 }, /* 186 */ + { 0x99, 0xcc, 0xcc, 0 }, /* 187 */ + { 0xcc, 0xcc, 0xcc, 0 }, /* 188 gray 204 */ + { 0xff, 0xcc, 0xcc, 0 }, /* 189 */ + { 0x00, 0xff, 0xcc, 0 }, /* 190 */ + { 0x33, 0xff, 0xcc, 0 }, /* 191 */ + { 0x66, 0xff, 0x99, 0 }, /* 192 */ + { 0x99, 0xff, 0xcc, 0 }, /* 193 */ + { 0xcc, 0xff, 0xcc, 0 }, /* 194 */ + { 0xff, 0xff, 0xcc, 0 }, /* 195 */ + { 0x33, 0x00, 0xcc, 0 }, /* 196 */ + { 0x66, 0x00, 0xff, 0 }, /* 197 */ + { 0x99, 0x00, 0xff, 0 }, /* 198 */ + { 0x00, 0x33, 0xcc, 0 }, /* 199 */ + { 0x33, 0x33, 0xff, 0 }, /* 200 */ + { 0x66, 0x33, 0xff, 0 }, /* 201 */ + { 0x99, 0x33, 0xff, 0 }, /* 202 */ + { 0xcc, 0x33, 0xff, 0 }, /* 203 */ + { 0xff, 0x33, 0xff, 0 }, /* 204 */ + { 0x00, 0x66, 0xff, 0 }, /* 205 */ + { 0x33, 0x66, 0xff, 0 }, /* 206 */ + { 0x66, 0x66, 0xcc, 0 }, /* 207 */ + { 0x99, 0x66, 0xff, 0 }, /* 208 */ + { 0xcc, 0x66, 0xff, 0 }, /* 209 */ + { 0xff, 0x66, 0xcc, 0 }, /* 210 */ + { 0x00, 0x99, 0xff, 0 }, /* 211 */ + { 0x33, 0x99, 0xff, 0 }, /* 212 */ + { 0x66, 0x99, 0xff, 0 }, /* 213 */ + { 0x99, 0x99, 0xff, 0 }, /* 214 */ + { 0xcc, 0x99, 0xff, 0 }, /* 215 */ + { 0xff, 0x99, 0xff, 0 }, /* 216 */ + { 0x00, 0xcc, 0xff, 0 }, /* 217 */ + { 0x33, 0xcc, 0xff, 0 }, /* 218 */ + { 0x66, 0xcc, 0xff, 0 }, /* 219 */ + { 0x99, 0xcc, 0xff, 0 }, /* 220 */ + { 0xcc, 0xcc, 0xff, 0 }, /* 221 */ + { 0xff, 0xcc, 0xff, 0 }, /* 222 */ + { 0x33, 0xff, 0xff, 0 }, /* 223 */ + { 0x66, 0xff, 0xcc, 0 }, /* 224 */ + { 0x99, 0xff, 0xff, 0 }, /* 225 */ + { 0xcc, 0xff, 0xff, 0 }, /* 226 */ + { 0xff, 0x66, 0x66, 0 }, /* 227 */ + { 0x66, 0xff, 0x66, 0 }, /* 228 */ + { 0xff, 0xff, 0x66, 0 }, /* 229 */ + { 0x66, 0x66, 0xff, 0 }, /* 230 */ + { 0xff, 0x66, 0xff, 0 }, /* 231 */ + { 0x66, 0xff, 0xff, 0 }, /* 232 */ + { 0xA5, 0x00, 0x21, 0 }, /* 233 R165 G0 B33 */ + { 0x5f, 0x5f, 0x5f, 0 }, /* 234 gray 95 */ + { 0x77, 0x77, 0x77, 0 }, /* 235 gray 119 */ + { 0x86, 0x86, 0x86, 0 }, /* 236 gray 134 */ + { 0x96, 0x96, 0x96, 0 }, /* 237 gray 150 */ + { 0xcb, 0xcb, 0xcb, 0 }, /* 238 gray 203 */ + { 0xb2, 0xb2, 0xb2, 0 }, /* 239 gray 178 */ + { 0xd7, 0xd7, 0xd7, 0 }, /* 240 gray 215 */ + { 0xdd, 0xdd, 0xdd, 0 }, /* 241 gray 221 */ + { 0xe3, 0xe3, 0xe3, 0 }, /* 242 gray 227 */ + { 0xea, 0xea, 0xea, 0 }, /* 243 gray 234 */ + { 0xf1, 0xf1, 0xf1, 0 }, /* 244 gray 241 */ + { 0xf8, 0xf8, 0xf8, 0 }, /* 245 gray 248 */ + { 0xff, 0xfb, 0xf0, 0 }, /* 246 Sys Reserved */ + { 0xa0, 0xa0, 0xa4, 0 }, /* 247 Sys Reserved */ + { 0x80, 0x80, 0x80, 0 }, /* 248 Sys Lt Gray gray 128 */ + { 0xff, 0x00, 0x00, 0 }, /* 249 Sys Red */ + { 0x00, 0xff, 0x00, 0 }, /* 250 Sys Green */ + { 0xff, 0xff, 0x00, 0 }, /* 251 Sys Yellow */ + { 0x00, 0x00, 0xff, 0 }, /* 252 Sys Blue */ + { 0xff, 0x00, 0xff, 0 }, /* 253 Sys Violet */ + { 0x00, 0xff, 0xff, 0 }, /* 254 Sys Cyan */ + { 0xff, 0xff, 0xff, 0 } /* 255 Sys White gray 255 */ +}; + +#endif diff --git a/cpu/arm926ejs/at91sam926x/serial.c b/cpu/arm926ejs/at91sam926x/serial.c new file mode 100644 index 00000000000..8ef91c2e41e --- /dev/null +++ b/cpu/arm926ejs/at91sam926x/serial.c @@ -0,0 +1,116 @@ +/* + * (C) Copyright 2005 + * M. Amine SAYA ATMEL Rousset, France. + * Added AT91SAM9261's serial port support. + * Added NADIA2's serial port support. + * + * (C) Copyright 2002 + * Lineo, Inc <www.lineo.com> + * Bernhard Kuhn <bkuhn@lineo.com> + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Alex Zuepke <azu@sysgo.de> + * + * Copyright (C) 1999 2000 2001 Erik Mouw (J.A.K.Mouw@its.tudelft.nl) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/hardware.h> + +#if !defined(CONFIG_DBGU) && !defined(CONFIG_USART0) && !defined(CONFIG_USART1) && !defined(CONFIG_USART2) +#error must define one of CONFIG_DBGU or CONFIG_USART0 or CONFIG_USART1 or CONFIG_USART2 +#endif + +/* Serial ports for SAM926x */ +#ifdef CONFIG_DBGU +AT91PS_USART us = (AT91PS_USART) AT91C_BASE_DBGU; +#endif + +#ifdef CONFIG_USART0 +AT91PS_USART us = (AT91PS_USART) AT91C_BASE_US0; +#endif + +#ifdef CONFIG_USART1 +AT91PS_USART us = (AT91PS_USART) AT91C_BASE_US1; +#endif + +#ifdef CONFIG_USART2 +AT91PS_USART us = (AT91PS_USART) AT91C_BASE_US2; +#endif + +extern int AT91F_Serial_Hardware_Init(void); + + +void serial_setbrg (void) +{ + DECLARE_GLOBAL_DATA_PTR; + int baudrate; + + if ((baudrate = gd->baudrate) <= 0) + baudrate = CONFIG_BAUDRATE; + if (baudrate == 0 || baudrate == CONFIG_BAUDRATE) + us->US_BRGR = AT91C_MASTER_CLOCK/(baudrate * 16); +} + +int serial_init (void) +{ + /* make any port initializations specific to this port */ + AT91F_Serial_Hardware_Init(); + + serial_setbrg (); + + us->US_CR = AT91C_US_RSTRX | AT91C_US_RSTTX; + us->US_CR = AT91C_US_RXEN | AT91C_US_TXEN; + us->US_MR = + (AT91C_US_CLKS_CLOCK | AT91C_US_CHRL_8_BITS | + AT91C_US_PAR_NONE | AT91C_US_NBSTOP_1_BIT); + us->US_IMR = ~0ul; + return (0); +} + +void serial_putc (const char c) +{ + if (c == '\n') + serial_putc ('\r'); + while ((us->US_CSR & AT91C_US_TXRDY) == 0); + us->US_THR = c; +} + +void serial_puts (const char *s) +{ + while (*s) { + serial_putc (*s++); + } +} + +int serial_getc (void) +{ + while ((us->US_CSR & AT91C_US_RXRDY) == 0); + return us->US_RHR; +} + +int serial_tstc (void) +{ + return ((us->US_CSR & AT91C_US_RXRDY) == AT91C_US_RXRDY); +} diff --git a/cpu/arm926ejs/at91sam926x/spi.c b/cpu/arm926ejs/at91sam926x/spi.c new file mode 100644 index 00000000000..bd855283b92 --- /dev/null +++ b/cpu/arm926ejs/at91sam926x/spi.c @@ -0,0 +1,243 @@ +/* + * (c) Copyright 2005 + * M. Amine SAYA, ATMEL Rousset, France. + * Driver for AT91SAM9261 SPI + * Based on at91rm9200 spi driver from H. Ikdoumi, ATMEL Rousset. + * + * 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> + +#ifdef CONFIG_HAS_DATAFLASH +#include <dataflash.h> +#include <asm/arch/pio.h> + +#ifdef CONFIG_AT91SAM9263EK +#define AT91C_SPI_CS0_CLK 8000000 /* SPI Clock for Dataflash cards -> SAM9263EK */ +#else +#define AT91C_SPI_CS0_CLK 33000000 /* SPI Clock for Dataflash devices */ +#endif + +#define AT91C_SPI_CS1_CLK 33000000 /* SPI Clock for Dataflash devices -> SAM9260EK */ +#define AT91C_SPI_CS3_CLK 8000000 /* SPI Clock for Dataflash cards -> SAM9261EK */ + +#define AT91C_SPI_PCS0_DATAFLASH_CARD 0xE /* Chip Select 0 : NPCS0 %1110 */ +#define AT91C_SPI_PCS1_DATAFLASH_CARD 0xD /* Chip Select 1 : NPCS1 %1101 */ +#define AT91C_SPI_PCS3_DATAFLASH_CARD 0x7 /* Chip Select 3 : NPCS3 %0111 */ + + +/*----------------------------------------------------------------------------*/ +/* \fn AT91F_SpiInit */ +/* \brief SPI Init */ +/*----------------------------------------------------------------------------*/ +void AT91F_SpiInit(void) { + + volatile unsigned int dummy; + +#ifdef CONFIG_AT91SAM9260EK + /* Configure PIO controllers to periph mode */ + AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, // PIO controller base address + ((unsigned int) AT91C_PA1_SPI0_MOSI) | + ((unsigned int) AT91C_PA3_SPI0_NPCS0) | + ((unsigned int) AT91C_PA0_SPI0_MISO) | + ((unsigned int) AT91C_PA2_SPI0_SPCK), /* Peripheral A */ + 0); /* Peripheral B */ + /* Configure PIO controllers to periph mode */ + AT91F_PIO_CfgPeriph(AT91C_BASE_PIOC, /* PIO controller base address */ + 0, /* Peripheral A */ + ((unsigned int) AT91C_PC11_SPI0_NPCS1)); /* Peripheral B */ +#endif + +#ifdef CONFIG_AT91SAM9261EK + /* Configure PIOs for SPI0 (internal DataFlash) */ + AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, + (AT91C_PA0_MISO0 | AT91C_PA1_MOSI0 | + AT91C_PA2_SPCK0 | AT91C_PA3_NPCS00), + 0); +#endif + +#ifdef CONFIG_AT91SAM9263EK + /* Configure PIO controllers to periph mode */ + AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, /* PIO controller base address */ + 0, /* Peripheral A */ + ((unsigned int) AT91C_PA2_SPI0_SPCK) | + ((unsigned int) AT91C_PA1_SPI0_MOSI) | + ((unsigned int) AT91C_PA5_SPI0_NPCS0) | + ((unsigned int) AT91C_PA0_SPI0_MISO)); /* Peripheral B */ +#endif + +#ifdef CONFIG_AT91SAM9RLEK + /* Configure PIOs for SPI0 (internal DataFlash) */ + AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, + ((unsigned int) AT91C_PA25_MISO) | + ((unsigned int) AT91C_PA26_MOSI) | + ((unsigned int) AT91C_PA27_SPCK) | + ((unsigned int) AT91C_PA28_NPCS0), /* Peripheral A */ + 0 /* Peripheral B */); +#endif + + + /* Enable Clock */ + AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_SPI0; + + /* Reset the SPI */ + AT91C_BASE_SPI0->SPI_CR = AT91C_SPI_SWRST; + + /* Configure SPI in Master Mode with No CS selected !!! */ + AT91C_BASE_SPI0->SPI_MR = AT91C_SPI_MSTR | AT91C_SPI_MODFDIS | AT91C_SPI_PCS; + + /* Configure CS0 in Mode 0 */ + /* to avoid pb with DataFlash AT45DB642D */ + AT91C_BASE_SPI0->SPI_CSR[0] = (AT91C_SPI_NCPHA | (AT91C_SPI_DLYBS & DATAFLASH_TCSS) | + (AT91C_SPI_DLYBCT & DATAFLASH_TCHS) | + (AT91C_MASTER_CLOCK / AT91C_SPI_CS0_CLK) << 8); + + +#ifdef CONFIG_AT91SAM9260EK + AT91C_BASE_SPI0->SPI_CSR[1] = (AT91C_SPI_NCPHA | (AT91C_SPI_DLYBS & DATAFLASH_TCSS) | + (AT91C_SPI_DLYBCT & DATAFLASH_TCHS) | + (AT91C_MASTER_CLOCK / AT91C_SPI_CS1_CLK) << 8); +#endif +#ifdef CONFIG_AT91SAM9261EK + AT91C_BASE_SPI0->SPI_CSR[3] = (AT91C_SPI_NCPHA | (AT91C_SPI_DLYBS & DATAFLASH_TCSS) | + (AT91C_SPI_DLYBCT & DATAFLASH_TCHS) | + (AT91C_MASTER_CLOCK / AT91C_SPI_CS3_CLK) << 8); +#endif + + /* SPI_Enable */ + AT91C_BASE_SPI0->SPI_CR = AT91C_SPI_SPIEN; + + while(!(AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_SPIENS)); + + /* Add tempo to get SPI in a safe state. + Should be removed for new silicon (Rev B) + */ + udelay(500000); + dummy = AT91C_BASE_SPI0->SPI_SR; + dummy = AT91C_BASE_SPI0->SPI_RDR; +} + + +/*----------------------------------------------------------------------------*/ +/* \fn AT91F_SpiEnable */ +/* \brief SPI Enable */ +/*----------------------------------------------------------------------------*/ +void AT91F_SpiEnable(int cs) { + AT91C_BASE_SPI0->SPI_MR &= 0xFFF0FFFF; + + switch(cs) { + case 0: + /* Configure SPI CS0 for DataFlash card AT45DBxx */ + AT91C_BASE_SPI0->SPI_MR |= ((AT91C_SPI_PCS0_DATAFLASH_CARD<<16) & AT91C_SPI_PCS); + break; + case 1: + /* Configure SPI CS1 for dataflash card AT45DBxx */ + AT91C_BASE_SPI0->SPI_MR |= ((AT91C_SPI_PCS1_DATAFLASH_CARD<<16) & AT91C_SPI_PCS); + break; + case 2: + case 3: + /* Configure SPI CS3 for dataflash card AT45DBxx */ + AT91C_BASE_SPI0->SPI_MR |= ((AT91C_SPI_PCS3_DATAFLASH_CARD<<16) & AT91C_SPI_PCS); + break; + default: + /* Configure PCS */ + AT91C_BASE_SPI0->SPI_MR |= ((AT91C_SPI_PCS0_DATAFLASH_CARD<<16) & AT91C_SPI_PCS); + break; + } + + /* SPI_Enable */ + AT91C_BASE_SPI0->SPI_CR = AT91C_SPI_SPIEN; +} + + +/*----------------------------------------------------------------------------*/ +/* \fn AT91F_SpiDisablePDC */ +/* \brief Disable PDC registers */ +/*----------------------------------------------------------------------------*/ +void __inline__ AT91F_SpiDisablePDC(AT91PS_SPI spi_ptr) +{ + spi_ptr->SPI_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS; +} + + +/*----------------------------------------------------------------------------*/ +/* \fn AT91F_SpiEnablePDC */ +/* \brief Enable PDC registers */ +/*----------------------------------------------------------------------------*/ +void __inline__ AT91F_SpiEnablePDC(AT91PS_SPI spi_ptr) +{ + spi_ptr->SPI_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN; +} + + +/*----------------------------------------------------------------------------*/ +/* \fn AT91F_SpiWrite */ +/* \brief Set the PDC registers for a transfert */ +/*----------------------------------------------------------------------------*/ +unsigned int AT91F_SpiWrite ( AT91PS_DataflashDesc pDesc ) +{ + unsigned int timeout; + + pDesc->state = BUSY; + + /* Disable PDC TX and RX channels */ + AT91C_BASE_SPI0->SPI_PTCR = AT91C_PDC_TXTDIS + AT91C_PDC_RXTDIS; + + /* Initialize the Transmit and Receive Pointer */ + AT91C_BASE_SPI0->SPI_RPR = (unsigned int)pDesc->rx_cmd_pt ; + AT91C_BASE_SPI0->SPI_TPR = (unsigned int)pDesc->tx_cmd_pt ; + + /* Intialize the Transmit and Receive Counters */ + AT91C_BASE_SPI0->SPI_RCR = pDesc->rx_cmd_size; + AT91C_BASE_SPI0->SPI_TCR = pDesc->tx_cmd_size; + + /* If there is another transfer to perform */ + if(pDesc->tx_data_size){ + /* Initialize the Next Transmit and Next Receive Pointer */ + AT91C_BASE_SPI0->SPI_RNPR = (unsigned int)pDesc->rx_data_pt ; + AT91C_BASE_SPI0->SPI_TNPR = (unsigned int)pDesc->tx_data_pt ; + + /* Initialize the Next Transmit and Next Receive Counters */ + AT91C_BASE_SPI0->SPI_RNCR = pDesc->rx_data_size ; + AT91C_BASE_SPI0->SPI_TNCR = pDesc->tx_data_size ; + } + + /* arm simple, non interrupt dependent timer */ + reset_timer_masked(); + timeout = 0; + + AT91F_SpiEnablePDC(AT91C_BASE_SPI0); + + while(!(AT91C_BASE_SPI0->SPI_SR & AT91C_SPI_RXBUFF) && ((timeout = get_timer_masked() ) < CFG_SPI_WRITE_TOUT)); + + /* Disable PDC TX and RX channels */ + AT91F_SpiDisablePDC(AT91C_BASE_SPI0); + + pDesc->state = IDLE; + + + if (timeout >= CFG_SPI_WRITE_TOUT){ + printf("AT91F_SpiWrite : Error Timeout\n\r"); + return DATAFLASH_ERROR; + } + + return DATAFLASH_OK; +} + +#endif diff --git a/cpu/arm926ejs/at91sam926x/usb_ohci.c b/cpu/arm926ejs/at91sam926x/usb_ohci.c new file mode 100644 index 00000000000..79d0fc008ff --- /dev/null +++ b/cpu/arm926ejs/at91sam926x/usb_ohci.c @@ -0,0 +1,1709 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB on the S3C2400 and SAM9263 from ATMEL. + * + * (C) Copyright 2003 + * Gary Jennejohn, DENX Software Engineering <gj@denx.de> + * + * Note: Much of this code has been derived from Linux 2.4 + * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> + * (C) Copyright 2000-2002 David Brownell + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + */ +/* + * IMPORTANT NOTES + * 1 - you MUST define LITTLEENDIAN in the configuration file for the + * board or this driver will NOT work! + * 2 - this driver is intended for use with USB Mass Storage Devices + * (BBB) ONLY. There is NO support for Interrupt or Isochronous pipes! + */ + +#include <common.h> + +#ifdef CONFIG_USB_OHCI + +#include <asm/hardware.h> + +#include <malloc.h> +#include <usb.h> +#include "usb_ohci.h" + +#define OHCI_USE_NPS /* force NoPowerSwitching mode */ +#undef OHCI_VERBOSE_DEBUG /* not always helpful */ + + +/* For initializing controller (mask in an HCFS mode too) */ +#define OHCI_CONTROL_INIT \ + (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE + +#define readl(a) (*((vu_long *)(a))) +#define writel(a, b) (*((vu_long *)(b)) = ((vu_long)a)) + +#define min_t(type,x,y) ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) + +#undef DEBUG +#ifdef DEBUG +#define dbg(format, arg...) printf("DEBUG: " format "\n", ## arg) +#else +#define dbg(format, arg...) do {} while(0) +#endif /* DEBUG */ +#define err(format, arg...) printf("ERROR: " format "\n", ## arg) +#undef SHOW_INFO +#ifdef SHOW_INFO +#define info(format, arg...) printf("INFO: " format "\n", ## arg) +#else +#define info(format, arg...) do {} while(0) +#endif + +#define m16_swap(x) swap_16(x) +#define m32_swap(x) swap_32(x) + +/* global ohci_t */ +static ohci_t gohci; +/* this must be aligned to a 256 byte boundary */ +struct ohci_hcca ghcca[1]; +/* a pointer to the aligned storage */ +struct ohci_hcca *phcca; +/* this allocates EDs for all possible endpoints */ +struct ohci_device ohci_dev; +/* urb_priv */ +urb_priv_t urb_priv; +/* RHSC flag */ +int got_rhsc; +/* device which was disconnected */ +struct usb_device *devgone; +/* flag guarding URB transation */ +int urb_finished = 0; + +/*-------------------------------------------------------------------------*/ + +/* 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; + } + + /* if we have an unfinished URB from previous transaction let's + * fail and scream as quickly as possible so as not to corrupt + * further communication */ + if (!urb_finished) { + err("sohci_submit_job: URB NOT FINISHED"); + return -1; + } + /* we're about to begin a new transaction here so mark the URB unfinished */ + urb_finished = 0; + + /* every endpoint has a ed, locate and fill it */ + if (!(ed = ep_add_ed (dev, pipe))) { + err("sohci_submit_job: ENOMEM"); + return -1; + } + + /* for the private part of the URB we need the number of TDs (size) */ + switch (usb_pipetype (pipe)) { + case PIPE_BULK: /* one TD for every 4096 Byte */ + size = (transfer_len - 1) / 4096 + 1; + break; + case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ + size = (transfer_len == 0)? 2: + (transfer_len - 1) / 4096 + 3; + break; + } + + if (size >= (N_URB_TD - 1)) { + err("need %d TDs, only have %d", size, N_URB_TD); + return -1; + } + purb_priv = &urb_priv; + purb_priv->pipe = pipe; + + /* fill the private part of the URB */ + purb_priv->length = size; + purb_priv->ed = ed; + purb_priv->actual_length = 0; + + /* allocate the TDs */ + /* note that td[0] was allocated in ep_add_ed */ + for (i = 0; i < size; i++) { + purb_priv->td[i] = td_alloc (dev); + if (!purb_priv->td[i]) { + purb_priv->length = i; + urb_free_priv (purb_priv); + err("sohci_submit_job: ENOMEM"); + return -1; + } + } + + if (ed->state == ED_NEW || (ed->state & ED_DEL)) { + urb_free_priv (purb_priv); + err("sohci_submit_job: EINVAL"); + return -1; + } + + /* link the ed into a chain if is not already */ + if (ed->state != ED_OPER) + ep_link (ohci, ed); + + /* fill the TDs and link it to the ed */ + td_submit_job(dev, pipe, buffer, transfer_len, setup, purb_priv, interval); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +/* tell us the current USB frame number */ + +static int sohci_get_current_frame_number (struct usb_device *usb_dev) +{ + ohci_t *ohci = &gohci; + + return m16_swap (ohci->hcca->frame_no); +} +#endif + +/*-------------------------------------------------------------------------* + * ED handling functions + *-------------------------------------------------------------------------*/ + +/* link an ed into one of the HC chains */ + +static int ep_link (ohci_t *ohci, ed_t *edi) +{ + volatile ed_t *ed = edi; + + ed->state = ED_OPER; + + switch (ed->type) { + case PIPE_CONTROL: + ed->hwNextED = 0; + if (ohci->ed_controltail == NULL) { + writel (ed, &ohci->regs->ed_controlhead); + } else { + ohci->ed_controltail->hwNextED = m32_swap (ed); + } + ed->ed_prev = ohci->ed_controltail; + if (!ohci->ed_controltail && !ohci->ed_rm_list[0] && + !ohci->ed_rm_list[1] && !ohci->sleeping) { + ohci->hc_control |= OHCI_CTRL_CLE; + writel (ohci->hc_control, &ohci->regs->control); + } + ohci->ed_controltail = edi; + break; + + case PIPE_BULK: + ed->hwNextED = 0; + if (ohci->ed_bulktail == NULL) { + writel (ed, &ohci->regs->ed_bulkhead); + } else { + ohci->ed_bulktail->hwNextED = m32_swap (ed); + } + ed->ed_prev = ohci->ed_bulktail; + if (!ohci->ed_bulktail && !ohci->ed_rm_list[0] && + !ohci->ed_rm_list[1] && !ohci->sleeping) { + ohci->hc_control |= OHCI_CTRL_BLE; + writel (ohci->hc_control, &ohci->regs->control); + } + ohci->ed_bulktail = edi; + break; + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* unlink an ed from one of the HC chains. + * just the link to the ed is unlinked. + * the link from the ed still points to another operational ed or 0 + * so the HC can eventually finish the processing of the unlinked ed */ + +static int ep_unlink (ohci_t *ohci, ed_t *ed) +{ + ed->hwINFO |= m32_swap (OHCI_ED_SKIP); + + switch (ed->type) { + case PIPE_CONTROL: + if (ed->ed_prev == NULL) { + if (!ed->hwNextED) { + ohci->hc_control &= ~OHCI_CTRL_CLE; + writel (ohci->hc_control, &ohci->regs->control); + } + writel (m32_swap (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_controlhead); + } else { + ed->ed_prev->hwNextED = ed->hwNextED; + } + if (ohci->ed_controltail == ed) { + ohci->ed_controltail = ed->ed_prev; + } else { + ((ed_t *)m32_swap (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; + } + break; + + case PIPE_BULK: + if (ed->ed_prev == NULL) { + if (!ed->hwNextED) { + ohci->hc_control &= ~OHCI_CTRL_BLE; + writel (ohci->hc_control, &ohci->regs->control); + } + writel (m32_swap (*((__u32 *)&ed->hwNextED)), &ohci->regs->ed_bulkhead); + } else { + ed->ed_prev->hwNextED = ed->hwNextED; + } + if (ohci->ed_bulktail == ed) { + ohci->ed_bulktail = ed->ed_prev; + } else { + ((ed_t *)m32_swap (*((__u32 *)&ed->hwNextED)))->ed_prev = ed->ed_prev; + } + break; + } + ed->state = ED_UNLINK; + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +/* add/reinit an endpoint; this should be done once at the usb_set_configuration command, + * but the USB stack is a little bit stateless so we do it at every transaction + * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK + * in all other cases the state is left unchanged + * the ed info fields are setted anyway even though most of them should not change */ + +static ed_t * ep_add_ed (struct usb_device *usb_dev, unsigned long pipe) +{ + td_t *td; + ed_t *ed_ret; + volatile ed_t *ed; + + ed = ed_ret = &ohci_dev.ed[(usb_pipeendpoint (pipe) << 1) | + (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]; + + if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) { + err("ep_add_ed: pending delete"); + /* pending delete request */ + return NULL; + } + + if (ed->state == ED_NEW) { + ed->hwINFO = m32_swap (OHCI_ED_SKIP); /* skip ed */ + /* dummy td; end of td list for ed */ + td = td_alloc (usb_dev); + ed->hwTailP = m32_swap (td); + ed->hwHeadP = ed->hwTailP; + ed->state = ED_UNLINK; + ed->type = usb_pipetype (pipe); + ohci_dev.ed_cnt++; + } + + ed->hwINFO = m32_swap (usb_pipedevice (pipe) + | usb_pipeendpoint (pipe) << 7 + | (usb_pipeisoc (pipe)? 0x8000: 0) + | (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) + | usb_pipeslow (pipe) << 13 + | usb_maxpacket (usb_dev, pipe) << 16); + + return ed_ret; +} + +/*-------------------------------------------------------------------------* + * TD handling functions + *-------------------------------------------------------------------------*/ + +/* enqueue next TD for this URB (OHCI spec 5.2.8.2) */ + +static void td_fill (ohci_t *ohci, unsigned int info, + void *data, int len, + struct usb_device *dev, int index, urb_priv_t *urb_priv) +{ + volatile td_t *td, *td_pt; +#ifdef OHCI_FILL_TRACE + int i; +#endif + + if (index > urb_priv->length) { + err("index > length"); + return; + } + /* use this td as the next dummy */ + td_pt = urb_priv->td [index]; + td_pt->hwNextTD = 0; + + /* fill the old dummy TD */ + td = urb_priv->td [index] = (td_t *)(m32_swap (urb_priv->ed->hwTailP) & ~0xf); + + td->ed = urb_priv->ed; + td->next_dl_td = NULL; + td->index = index; + td->data = (__u32)data; +#ifdef OHCI_FILL_TRACE + if ((usb_pipetype(urb_priv->pipe) == PIPE_BULK) && usb_pipeout(urb_priv->pipe)) { + for (i = 0; i < len; i++) + printf("td->data[%d] %#2x ",i, ((unsigned char *)td->data)[i]); + printf("\n"); + } +#endif + if (!len) + data = 0; + + td->hwINFO = m32_swap (info); + td->hwCBP = m32_swap (data); + if (data) + td->hwBE = m32_swap (data + len - 1); + else + td->hwBE = 0; + td->hwNextTD = m32_swap (td_pt); + + /* append to queue */ + td->ed->hwTailP = td->hwNextTD; +} + +/*-------------------------------------------------------------------------*/ + +/* prepare all TDs of a transfer */ + +static void td_submit_job (struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, urb_priv_t *urb, int interval) +{ + ohci_t *ohci = &gohci; + int data_len = transfer_len; + void *data; + int cnt = 0; + __u32 info = 0; + unsigned int toggle = 0; + + /* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */ + if(usb_gettoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe))) { + toggle = TD_T_TOGGLE; + } else { + toggle = TD_T_DATA0; + usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 1); + } + urb->td_cnt = 0; + if (data_len) + data = buffer; + else + data = 0; + + switch (usb_pipetype (pipe)) { + case PIPE_BULK: + info = usb_pipeout (pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; + while(data_len > 4096) { + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, 4096, dev, cnt, urb); + data += 4096; data_len -= 4096; cnt++; + } + info = usb_pipeout (pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; + td_fill (ohci, info | (cnt? TD_T_TOGGLE:toggle), data, data_len, dev, cnt, urb); + cnt++; + + if (!ohci->sleeping) + writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ + break; + + case PIPE_CONTROL: + info = TD_CC | TD_DP_SETUP | TD_T_DATA0; + td_fill (ohci, info, setup, 8, dev, cnt++, urb); + if (data_len > 0) { + info = usb_pipeout (pipe)? + TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; + /* NOTE: mishandles transfers >8K, some >4K */ + td_fill (ohci, info, data, data_len, dev, cnt++, urb); + } + info = usb_pipeout (pipe)? + TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1; + td_fill (ohci, info, data, 0, dev, cnt++, urb); + if (!ohci->sleeping) + writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ + break; + } + if (urb->length != cnt) + dbg("TD LENGTH %d != CNT %d", urb->length, cnt); +} + +/*-------------------------------------------------------------------------* + * Done List handling functions + *-------------------------------------------------------------------------*/ + + +/* calculate the transfer length and update the urb */ + +static void dl_transfer_length(td_t * td) +{ + __u32 tdINFO, tdBE, tdCBP; + urb_priv_t *lurb_priv = &urb_priv; + + tdINFO = m32_swap (td->hwINFO); + tdBE = m32_swap (td->hwBE); + tdCBP = m32_swap (td->hwCBP); + + + if (!(usb_pipetype (lurb_priv->pipe) == PIPE_CONTROL && + ((td->index == 0) || (td->index == lurb_priv->length - 1)))) { + if (tdBE != 0) { + if (td->hwCBP == 0) + lurb_priv->actual_length += tdBE - td->data + 1; + else + lurb_priv->actual_length += tdCBP - td->data; + } + } +} + +/*-------------------------------------------------------------------------*/ + +/* replies to the request have to be on a FIFO basis so + * we reverse the reversed done-list */ + +static td_t * dl_reverse_done_list (ohci_t *ohci) +{ + __u32 td_list_hc; + td_t *td_rev = NULL; + td_t *td_list = NULL; + urb_priv_t *lurb_priv = NULL; + + td_list_hc = m32_swap (ohci->hcca->done_head) & 0xfffffff0; + ohci->hcca->done_head = 0; + + while (td_list_hc) { + td_list = (td_t *)td_list_hc; + + if (TD_CC_GET (m32_swap (td_list->hwINFO))) { + lurb_priv = &urb_priv; + dbg(" USB-error/status: %x : %p", + TD_CC_GET (m32_swap (td_list->hwINFO)), td_list); + if (td_list->ed->hwHeadP & m32_swap (0x1)) { + if (lurb_priv && ((td_list->index + 1) < lurb_priv->length)) { + td_list->ed->hwHeadP = + (lurb_priv->td[lurb_priv->length - 1]->hwNextTD & m32_swap (0xfffffff0)) | + (td_list->ed->hwHeadP & m32_swap (0x2)); + lurb_priv->td_cnt += lurb_priv->length - td_list->index - 1; + } else + td_list->ed->hwHeadP &= m32_swap (0xfffffff2); + } + } + + td_list->next_dl_td = td_rev; + td_rev = td_list; + td_list_hc = m32_swap (td_list->hwNextTD) & 0xfffffff0; + } + + return td_list; +} + +/*-------------------------------------------------------------------------*/ + +/* td done list */ +static int dl_done_list (ohci_t *ohci, td_t *td_list) +{ + td_t *td_list_next = NULL; + ed_t *ed; + int cc = 0; + int stat = 0; + /* urb_t *urb; */ + urb_priv_t *lurb_priv; + __u32 tdINFO, edHeadP, edTailP; + + while (td_list) { + td_list_next = td_list->next_dl_td; + + lurb_priv = &urb_priv; + tdINFO = m32_swap (td_list->hwINFO); + + ed = td_list->ed; + + dl_transfer_length(td_list); + + /* error code of transfer */ + cc = TD_CC_GET (tdINFO); + if (cc != 0) { + dbg("ConditionCode %#x", cc); + stat = cc_to_error[cc]; + } + + /* see if this done list makes for all TD's of current URB, + * and mark the URB finished if so */ + if (++(lurb_priv->td_cnt) == lurb_priv->length) { + if ((ed->state & (ED_OPER | ED_UNLINK))) + urb_finished = 1; + else + dbg("dl_done_list: strange.., ED state %x, ed->state\n"); + } else + dbg("dl_done_list: processing TD %x, len %x\n", lurb_priv->td_cnt, + lurb_priv->length); + + if (ed->state != ED_NEW) { + edHeadP = m32_swap (ed->hwHeadP) & 0xfffffff0; + edTailP = m32_swap (ed->hwTailP); + + /* unlink eds if they are not busy */ + if ((edHeadP == edTailP) && (ed->state == ED_OPER)) + ep_unlink (ohci, ed); + } + + td_list = td_list_next; + } + return stat; +} + +/*-------------------------------------------------------------------------* + * Virtual Root Hub + *-------------------------------------------------------------------------*/ + +/* Device descriptor */ +static __u8 root_hub_dev_des[] = +{ + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x10, /* __u16 bcdUSB; v1.1 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x01, /* __u8 iProduct; */ + 0x00, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + + +/* Configuration descriptor */ +static __u8 root_hub_config_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + +static unsigned char root_hub_str_index0[] = +{ + 0x04, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 0x09, /* __u8 lang ID */ + 0x04, /* __u8 lang ID */ +}; + +static unsigned char root_hub_str_index1[] = +{ + 28, /* __u8 bLength; */ + 0x03, /* __u8 bDescriptorType; String-descriptor */ + 'O', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'H', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'C', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'I', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + ' ', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'R', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'o', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 't', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + ' ', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'H', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'u', /* __u8 Unicode */ + 0, /* __u8 Unicode */ + 'b', /* __u8 Unicode */ + 0, /* __u8 Unicode */ +}; + +/* Hub class-specific descriptor is constructed dynamically */ + + +/*-------------------------------------------------------------------------*/ + +#define OK(x) len = (x); break +#ifdef DEBUG +#define WR_RH_STAT(x) {info("WR:status %#8x", (x));writel((x), &gohci.regs->roothub.status);} +#define WR_RH_PORTSTAT(x) {info("WR:portstatus[%d] %#8x", wIndex-1, (x));writel((x), &gohci.regs->roothub.portstatus[wIndex-1]);} +#else +#define WR_RH_STAT(x) writel((x), &gohci.regs->roothub.status) +#define WR_RH_PORTSTAT(x) writel((x), &gohci.regs->roothub.portstatus[wIndex-1]) +#endif +#define RD_RH_STAT roothub_status(&gohci) +#define RD_RH_PORTSTAT roothub_portstatus(&gohci,wIndex-1) + +/* request to virtual root hub */ + +int rh_check_port_status(ohci_t *controller) +{ + __u32 temp, ndp, i; + int res; + + res = -1; + temp = roothub_a (controller); + ndp = (temp & RH_A_NDP); + for (i = 0; i < ndp; i++) { + temp = roothub_portstatus (controller, i); + /* check for a device disconnect */ + if (((temp & (RH_PS_PESC | RH_PS_CSC)) == + (RH_PS_PESC | RH_PS_CSC)) && + ((temp & RH_PS_CCS) == 0)) { + res = i; + break; + } + } + return res; +} + +static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe, + void *buffer, int transfer_len, struct devrequest *cmd) +{ + void * data = buffer; + int leni = transfer_len; + int len = 0; + int stat = 0; + __u32 datab[4]; + __u8 *data_buf = (__u8 *)datab; + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + +#ifdef DEBUG +urb_priv.actual_length = 0; +pkt_print(dev, pipe, buffer, transfer_len, cmd, "SUB(rh)", usb_pipein(pipe)); +#else + wait_ms(1); +#endif + if ((pipe & PIPE_INTERRUPT) == PIPE_INTERRUPT) { + info("Root-Hub submit IRQ: NOT implemented"); + return 0; + } + + bmRType_bReq = cmd->requesttype | (cmd->request << 8); + wValue = m16_swap (cmd->value); + wIndex = m16_swap (cmd->index); + wLength = m16_swap (cmd->length); + + info("Root-Hub: adr: %2x cmd(%1x): %08x %04x %04x %04x", + dev->devnum, 8, bmRType_bReq, wValue, wIndex, wLength); + + switch (bmRType_bReq) { + /* Request Destination: + without flags: Device, + RH_INTERFACE: interface, + RH_ENDPOINT: endpoint, + RH_CLASS means HUB here, + RH_OTHER | RH_CLASS almost ever means HUB_PORT here + */ + + case RH_GET_STATUS: + *(__u16 *) data_buf = m16_swap (1); OK (2); + case RH_GET_STATUS | RH_INTERFACE: + *(__u16 *) data_buf = m16_swap (0); OK (2); + case RH_GET_STATUS | RH_ENDPOINT: + *(__u16 *) data_buf = m16_swap (0); OK (2); + case RH_GET_STATUS | RH_CLASS: + *(__u32 *) data_buf = m32_swap ( + RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); + OK (4); + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + *(__u32 *) data_buf = m32_swap (RD_RH_PORTSTAT); OK (4); + + case RH_CLEAR_FEATURE | RH_ENDPOINT: + switch (wValue) { + case (RH_ENDPOINT_STALL): OK (0); + } + break; + + case RH_CLEAR_FEATURE | RH_CLASS: + switch (wValue) { + case RH_C_HUB_LOCAL_POWER: + OK(0); + case (RH_C_HUB_OVER_CURRENT): + WR_RH_STAT(RH_HS_OCIC); OK (0); + } + break; + + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_ENABLE): + WR_RH_PORTSTAT (RH_PS_CCS ); OK (0); + case (RH_PORT_SUSPEND): + WR_RH_PORTSTAT (RH_PS_POCI); OK (0); + case (RH_PORT_POWER): + WR_RH_PORTSTAT (RH_PS_LSDA); OK (0); + case (RH_C_PORT_CONNECTION): + WR_RH_PORTSTAT (RH_PS_CSC ); OK (0); + case (RH_C_PORT_ENABLE): + WR_RH_PORTSTAT (RH_PS_PESC); OK (0); + case (RH_C_PORT_SUSPEND): + WR_RH_PORTSTAT (RH_PS_PSSC); OK (0); + case (RH_C_PORT_OVER_CURRENT): + WR_RH_PORTSTAT (RH_PS_OCIC); OK (0); + case (RH_C_PORT_RESET): + WR_RH_PORTSTAT (RH_PS_PRSC); OK (0); + } + break; + + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_SUSPEND): + WR_RH_PORTSTAT (RH_PS_PSS ); OK (0); + case (RH_PORT_RESET): /* BUG IN HUP CODE *********/ + if (RD_RH_PORTSTAT & RH_PS_CCS) + WR_RH_PORTSTAT (RH_PS_PRS); + OK (0); + case (RH_PORT_POWER): + WR_RH_PORTSTAT (RH_PS_PPS ); OK (0); + case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/ + if (RD_RH_PORTSTAT & RH_PS_CCS) + WR_RH_PORTSTAT (RH_PS_PES ); + OK (0); + } + break; + + case RH_SET_ADDRESS: gohci.rh.devnum = wValue; OK(0); + + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case (0x01): /* device descriptor */ + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof (root_hub_dev_des), + wLength)); + data_buf = root_hub_dev_des; OK(len); + case (0x02): /* configuration descriptor */ + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof (root_hub_config_des), + wLength)); + data_buf = root_hub_config_des; OK(len); + case (0x03): /* string descriptors */ + if(wValue==0x0300) { + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof (root_hub_str_index0), + wLength)); + data_buf = root_hub_str_index0; + OK(len); + } + if(wValue==0x0301) { + len = min_t(unsigned int, + leni, + min_t(unsigned int, + sizeof (root_hub_str_index1), + wLength)); + data_buf = root_hub_str_index1; + OK(len); + } + default: + stat = USB_ST_STALLED; + } + break; + + case RH_GET_DESCRIPTOR | RH_CLASS: + { + __u32 temp = roothub_a (&gohci); + + data_buf [0] = 9; /* min length; */ + data_buf [1] = 0x29; + data_buf [2] = temp & RH_A_NDP; + data_buf [3] = 0; + if (temp & RH_A_PSM) /* per-port power switching? */ + data_buf [3] |= 0x1; + if (temp & RH_A_NOCP) /* no overcurrent reporting? */ + data_buf [3] |= 0x10; + else if (temp & RH_A_OCPM) /* per-port overcurrent reporting? */ + data_buf [3] |= 0x8; + + /* corresponds to data_buf[4-7] */ + datab [1] = 0; + data_buf [5] = (temp & RH_A_POTPGT) >> 24; + temp = roothub_b (&gohci); + data_buf [7] = temp & RH_B_DR; + if (data_buf [2] < 7) { + data_buf [8] = 0xff; + } else { + data_buf [0] += 2; + data_buf [8] = (temp & RH_B_DR) >> 8; + data_buf [10] = data_buf [9] = 0xff; + } + + len = min_t(unsigned int, leni, + min_t(unsigned int, data_buf [0], wLength)); + OK (len); + } + + case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK (1); + + case RH_SET_CONFIGURATION: WR_RH_STAT (0x10000); OK (0); + + default: + dbg ("unsupported root hub command"); + stat = USB_ST_STALLED; + } + +#ifdef DEBUG + ohci_dump_roothub (&gohci, 1); +#else + wait_ms(1); +#endif + + len = min_t(int, len, leni); + if (data != data_buf) + memcpy (data, data_buf, len); + dev->act_len = len; + dev->status = stat; + +#ifdef DEBUG + if (transfer_len) + urb_priv.actual_length = transfer_len; + pkt_print(dev, pipe, buffer, transfer_len, cmd, "RET(rh)", 0/*usb_pipein(pipe)*/); +#else + wait_ms(1); +#endif + + return stat; +} + +/*-------------------------------------------------------------------------*/ + +/* common code for handling submit messages - used for all but root hub */ +/* accesses. */ +int submit_common_msg(struct usb_device *dev, unsigned long pipe, void *buffer, + int transfer_len, struct devrequest *setup, int interval) +{ + int stat = 0; + int maxsize = usb_maxpacket(dev, pipe); + int timeout; + + /* device pulled? Shortcut the action. */ + if (devgone == dev) { + dev->status = USB_ST_CRC_ERR; + return 0; + } + +#ifdef DEBUG + urb_priv.actual_length = 0; + pkt_print(dev, pipe, buffer, transfer_len, setup, "SUB", usb_pipein(pipe)); +#else + wait_ms(1); +#endif + if (!maxsize) { + err("submit_common_message: pipesize for pipe %lx is zero", + pipe); + return -1; + } + + if (sohci_submit_job(dev, pipe, buffer, transfer_len, setup, interval) < 0) { + err("sohci_submit_job failed"); + return -1; + } + + wait_ms(10); + /* ohci_dump_status(&gohci); */ + + /* allow more time for a BULK device to react - some are slow */ +#define BULK_TO 5000 /* timeout in milliseconds */ + if (usb_pipetype (pipe) == PIPE_BULK) + timeout = BULK_TO; + else + timeout = 100; + + /* wait for it to complete */ + for (;;) { + /* check whether the controller is done */ + stat = hc_interrupt(); + + if (stat < 0) { + stat = USB_ST_CRC_ERR; + break; + } + + /* NOTE: since we are not interrupt driven in U-Boot and always + * handle only one URB at a time, we cannot assume the + * transaction finished on the first successful return from + * hc_interrupt().. unless the flag for current URB is set, + * meaning that all TD's to/from device got actually + * transferred and processed. If the current URB is not + * finished we need to re-iterate this loop so as + * hc_interrupt() gets called again as there needs to be some + * more TD's to process still */ + if ((stat >= 0) && (stat != 0xff) && (urb_finished)) { + /* 0xff is returned for an SF-interrupt */ + break; + } + + if (--timeout) { + wait_ms(1); + if (!urb_finished) + dbg("\%"); + + } else { + err("CTL:TIMEOUT "); + dbg("submit_common_msg: TO status %x\n", stat); + stat = USB_ST_CRC_ERR; + urb_finished = 1; + break; + } + } + +#if 0 + /* 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; + } + } +#endif + + 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 if ((ints = readl (®s->intrstatus)) == ~(u32)0) { + ohci->disabled++; + err ("%s device removed!", ohci->slot_name); + return -1; + + } else if ((ints &= readl (®s->intrenable)) == 0) { + dbg("hc_interrupt: returning..\n"); + return 0xff; + } + + /* dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca->frame_no)); */ + + if (ints & OHCI_INTR_RHSC) { + got_rhsc = 1; + stat = 0xff; + } + + 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); +} + +/*-------------------------------------------------------------------------*/ + +/* + * low level initalisation routine, called from usb.c + */ +static char ohci_inited = 0; + +int usb_lowlevel_init(void) +{ + /* + * Enable USB host clock. + */ + *AT91C_PMC_PCER = 1 << AT91C_ID_UHP; /* Peripheral Clock Enable Register */ +#ifdef CONFIG_AT91SAM9261EK + *AT91C_PMC_SCER = AT91C_PMC_UHP | AT91C_PMC_HCK0; /* UHP System clock enable*/ +#else + *AT91C_PMC_SCER = AT91C_PMC_UHP; /* UHP System clock enable*/ +#endif + +#ifdef CONFIG_AT91SAM9263EK + /* ON SAM9263-EK ONLY !!! */ + + /* Configure PIOA21 & PIOA24 in output */ + /* Used to enable VBus on UHP ports */ + *AT91C_PIOA_PER = AT91C_PIO_PA21 | AT91C_PIO_PA24; + *AT91C_PIOA_OER = AT91C_PIO_PA21 | AT91C_PIO_PA24; + *AT91C_PIOA_CODR = AT91C_PIO_PA21 | AT91C_PIO_PA24; +#endif + + 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 *) AT91C_BASE_UHP; + + gohci.flags = 0; +#if defined CONFIG_AT91SAM9260EK + gohci.slot_name = "sam9260"; +#elif defined CONFIG_AT91SAM9261EK + gohci.slot_name = "sam9261"; +#elif defined CONFIG_AT91SAM9263EK + gohci.slot_name = "sam9263"; +#endif + + if (hc_reset (&gohci) < 0) { + hc_release_ohci (&gohci); + + /* Initialization failed */ + *AT91C_PMC_PCDR = 1 << AT91C_ID_UHP; /* Peripheral Clock Disable Register */ + *AT91C_PMC_SCDR = AT91C_PMC_UHP; /* UHP System clock disable*/ + + return -1; + } + + /* FIXME this is a second HC reset; why?? */ + gohci.hc_control = OHCI_USB_RESET; + writel (gohci.hc_control, &gohci.regs->control); + wait_ms (10); + + if (hc_start (&gohci) < 0) { + err ("can't start usb-%s", gohci.slot_name); + hc_release_ohci (&gohci); + + /* Initialization failed */ + *AT91C_PMC_PCDR = 1 << AT91C_ID_UHP; /* Peripheral Clock Disable Register */ +#ifdef CONFIG_AT91SAM9261EK + *AT91C_PMC_SCDR = AT91C_PMC_UHP | AT91C_PMC_HCK0; /* UHP System clock disable*/ +#else + *AT91C_PMC_SCDR = AT91C_PMC_UHP; /* UHP System clock disable*/ +#endif + return -1; + } + +#ifdef DEBUG + ohci_dump (&gohci, 1); +#else + wait_ms(1); +#endif + ohci_inited = 1; + urb_finished = 1; + + return 0; +} + +int usb_lowlevel_stop(void) +{ + /* this gets called really early - before the controller has */ + /* even been initialized! */ + if (!ohci_inited) + return 0; + /* TODO release any interrupts, etc. */ + /* call hc_release_ohci() here ? */ + hc_reset (&gohci); + + /* may not want to do this */ + *AT91C_PMC_PCDR = 1 << AT91C_ID_UHP; /* Peripheral Clock Disable Register */ +#ifdef CONFIG_AT91SAM9261EK + *AT91C_PMC_SCDR = AT91C_PMC_UHP | AT91C_PMC_HCK0; /* UHP System clock disable*/ +#else + *AT91C_PMC_SCDR = AT91C_PMC_UHP; /* UHP System clock disable*/ +#endif + + return 0; +} + +#endif /* CONFIG_USB_OHCI */ diff --git a/cpu/arm926ejs/at91sam926x/usb_ohci.h b/cpu/arm926ejs/at91sam926x/usb_ohci.h new file mode 100644 index 00000000000..5e9a0fdfc4e --- /dev/null +++ b/cpu/arm926ejs/at91sam926x/usb_ohci.h @@ -0,0 +1,417 @@ +/* + * 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 */ + + __u8 unused; + __u8 index; + struct ed *ed; + struct td *next_dl_td; + struct usb_device *usb_dev; + int transfer_len; + __u32 data; + + __u32 unused2[2]; +} __attribute((aligned(32))); +typedef struct td td_t; + +#define OHCI_ED_SKIP (1 << 14) + +/* + * The HCCA (Host Controller Communications Area) is a 256 byte + * structure defined in the OHCI spec. that the host controller is + * told the base address of. It must be 256-byte aligned. + */ + +#define NUM_INTS 32 /* part of the OHCI standard */ +struct ohci_hcca { + __u32 int_table[NUM_INTS]; /* Interrupt ED table */ + __u16 frame_no; /* current frame number */ + __u16 pad1; /* set to 0 on each frame_no change */ + __u32 done_head; /* info returned for an interrupt */ + u8 reserved_for_hc[116]; +} __attribute((aligned(256))); + + +/* + * Maximum number of root hub ports. + */ +#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */ + +/* + * This is the structure of the OHCI controller's memory mapped I/O + * region. This is Memory Mapped I/O. You must use the readl() and + * writel() macros defined in asm/io.h to access these!! + */ +struct ohci_regs { + /* control and status registers */ + __u32 revision; + __u32 control; + __u32 cmdstatus; + __u32 intrstatus; + __u32 intrenable; + __u32 intrdisable; + /* memory pointers */ + __u32 hcca; + __u32 ed_periodcurrent; + __u32 ed_controlhead; + __u32 ed_controlcurrent; + __u32 ed_bulkhead; + __u32 ed_bulkcurrent; + __u32 donehead; + /* frame counters */ + __u32 fminterval; + __u32 fmremaining; + __u32 fmnumber; + __u32 periodicstart; + __u32 lsthresh; + /* Root hub ports */ + struct ohci_roothub_regs { + __u32 a; + __u32 b; + __u32 status; + __u32 portstatus[MAX_ROOT_PORTS]; + } roothub; +} __attribute((aligned(32))); + + +/* OHCI CONTROL AND STATUS REGISTER MASKS */ + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */ +#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */ +#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */ +#define OHCI_CTRL_CLE (1 << 4) /* control list enable */ +#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */ +#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */ +#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ +#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ +#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +# define OHCI_USB_RESET (0 << 6) +# define OHCI_USB_RESUME (1 << 6) +# define OHCI_USB_OPER (2 << 6) +# define OHCI_USB_SUSPEND (3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR (1 << 0) /* host controller reset */ +#define OHCI_CLF (1 << 1) /* control list filled */ +#define OHCI_BLF (1 << 2) /* bulk list filled */ +#define OHCI_OCR (1 << 3) /* ownership change request */ +#define OHCI_SOC (3 << 16) /* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ +#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */ +#define OHCI_INTR_SF (1 << 2) /* start frame */ +#define OHCI_INTR_RD (1 << 3) /* resume detect */ +#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ +#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ +#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ +#define OHCI_INTR_OC (1 << 30) /* ownership change */ +#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ + + +/* Virtual Root HUB */ +struct virt_root_hub { + int devnum; /* Address of Root Hub endpoint */ + void *dev; /* was urb */ + void *int_addr; + int send; + int interval; +}; + +/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */ + +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 + +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 + + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS 0x00000001 /* current connect status */ +#define RH_PS_PES 0x00000002 /* port enable status*/ +#define RH_PS_PSS 0x00000004 /* port suspend status */ +#define RH_PS_POCI 0x00000008 /* port over current indicator */ +#define RH_PS_PRS 0x00000010 /* port reset status */ +#define RH_PS_PPS 0x00000100 /* port power status */ +#define RH_PS_LSDA 0x00000200 /* low speed device attached */ +#define RH_PS_CSC 0x00010000 /* connect status change */ +#define RH_PS_PESC 0x00020000 /* port enable status change */ +#define RH_PS_PSSC 0x00040000 /* port suspend status change */ +#define RH_PS_OCIC 0x00080000 /* over current indicator change */ +#define RH_PS_PRSC 0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS 0x00000001 /* local power status */ +#define RH_HS_OCI 0x00000002 /* over current indicator */ +#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC 0x00010000 /* local power status change */ +#define RH_HS_OCIC 0x00020000 /* over current indicator change */ +#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + +/* urb */ +#define N_URB_TD 48 +typedef struct +{ + ed_t *ed; + __u16 length; /* number of tds associated with this request */ + __u16 td_cnt; /* number of tds already serviced */ + int state; + unsigned long pipe; + int actual_length; + td_t *td[N_URB_TD]; /* list pointer to all corresponding TDs associated with this request */ +} urb_priv_t; +#define URB_DEL 1 + +/* + * This is the full ohci controller description + * + * Note how the "proper" USB information is just + * a subset of what the full implementation needs. (Linus) + */ + + +typedef struct ohci { + struct ohci_hcca *hcca; /* hcca */ + /*dma_addr_t hcca_dma;*/ + + int irq; + int disabled; /* e.g. got a UE, we're hung */ + int sleeping; + unsigned long flags; /* for HC bugs */ + + struct ohci_regs *regs; /* OHCI controller's memory */ + + ed_t *ed_rm_list[2]; /* lists of all endpoints to be removed */ + ed_t *ed_bulktail; /* last endpoint of bulk list */ + ed_t *ed_controltail; /* last endpoint of control list */ + int intrstatus; + __u32 hc_control; /* copy of the hc control reg */ + struct usb_device *dev[32]; + struct virt_root_hub rh; + + const char *slot_name; +} ohci_t; + +#define NUM_EDS 8 /* num of preallocated endpoint descriptors */ + +struct ohci_device { + ed_t ed[NUM_EDS]; + int ed_cnt; +}; + +/* hcd */ +/* endpoint */ +static int ep_link(ohci_t * ohci, ed_t * ed); +static int ep_unlink(ohci_t * ohci, ed_t * ed); +static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned long pipe); + +/*-------------------------------------------------------------------------*/ + +/* we need more TDs than EDs */ +#define NUM_TD 64 + +/* +1 so we can align the storage */ +td_t gtd[NUM_TD+1]; +/* pointers to aligned storage */ +td_t *ptd; + +/* TDs ... */ +static inline struct td * +td_alloc (struct usb_device *usb_dev) +{ + int i; + struct td *td; + + td = NULL; + for (i = 0; i < NUM_TD; i++) + { + if (ptd[i].usb_dev == NULL) + { + td = &ptd[i]; + td->usb_dev = usb_dev; + break; + } + } + + return td; +} + +static inline void +ed_free (struct ed *ed) +{ + ed->usb_dev = NULL; +} diff --git a/cpu/arm926ejs/interrupts.c b/cpu/arm926ejs/interrupts.c index 9cac969f64a..f4f22c0351c 100644 --- a/cpu/arm926ejs/interrupts.c +++ b/cpu/arm926ejs/interrupts.c @@ -172,7 +172,9 @@ void do_irq (struct pt_regs *pt_regs) bad_mode (); } -#ifdef CONFIG_INTEGRATOR +#if defined(CONFIG_INTEGRATOR) || defined(CONFIG_AT91SAM9261EK) \ +|| defined(CONFIG_AT91SAM9260EK)|| defined(CONFIG_AT91SAM9263EK) \ +|| defined(CONFIG_AT91SAM9RLEK) /* Timer functionality supplied by Integrator board (AP or CP) */ diff --git a/cpu/arm926ejs/start.S b/cpu/arm926ejs/start.S index 725c6639a1e..11b7c3cadfb 100644 --- a/cpu/arm926ejs/start.S +++ b/cpu/arm926ejs/start.S @@ -5,8 +5,8 @@ * * ----- Adapted for OMAP1610 OMAP730 from ARM925t code ------ * - * Copyright (c) 2001 Marius Gröger <mag@sysgo.de> - * Copyright (c) 2002 Alex Züpke <azu@sysgo.de> + * Copyright (c) 2001 Marius Gröger <mag@sysgo.de> + * Copyright (c) 2002 Alex Züpke <azu@sysgo.de> * Copyright (c) 2002 Gary Jennejohn <gj@denx.de> * Copyright (c) 2003 Richard Woodruff <r-woodruff2@ti.com> * Copyright (c) 2003 Kshitij <kshitij@ti.com> @@ -199,7 +199,7 @@ _start_armboot: ************************************************************************* */ - +#ifndef CONFIG_SKIP_LOWLEVEL_INIT cpu_init_crit: /* * flush v4 I/D caches @@ -225,6 +225,8 @@ cpu_init_crit: bl lowlevel_init /* go setup pll,mux,memory */ mov lr, ip /* restore link */ mov pc, lr /* back to my caller */ +#endif + /* ************************************************************************* * |