/************************************************************* * File: lib/am79970.c * Purpose: A tftp driver for the Am79C970A Ethernet controller * Author: Phil Bunce (pjb@carmel.com) * Revision History: * 980615 Created from sonic.c * 981114 Functional for big endian. * 981125 Functional for little endian. * 981127 Moved the GETRXREC swapPkt to ether.c * 981216 Omit all of initDA if already done. This fixes the prob * of IDON timeout on second Ether download. * 990318 Updated value in initDA */ #include #include #include #if 0 /* debug */ #define STATIC /* */ #else #define STATIC static #endif /*#define VERBOSE /* turn on verbose mode */ /* * This file contains the low-level driver for the Am79C970A * Ethernet controller. It is called by functions in lib/ether.c. * When used this way, it is used to provide PMON with a method to * support download using tftp via Ethernet. */ /* * Everything on this chip is accessed via 4 registers. RDP, RAP, RESET, * and BDP. RDP and RAP are used to access the CSR regs, RAP and BDP are * used to access the BCR regs. CSR1&2 point to the INIT block. The * rdr and tdr pointers in the INIT block point to the rx and tx descriptor * rings. Note that the byte-count fields hold the 2's complement of the * byte count. Visit www.amd.com for a full technical manual on the Am79C970A. */ #define ETHER_BASE 0xbfe00000 #ifdef CHECKS_ON #include #else #define assert(x) #endif #define inw(a) (*((Ulong *)(a))) #define outw(a,v) (*((Ulong *)(a))=(v)) #define outh(a,v) (*((Ushort *)(a))=(v)) #define swap32(n) ( \ ((((Ulong)(n))>>24)&0x000000ff) | \ ((((Ulong)(n))>>8)&0x0000ff00) | \ ((((Ulong)(n))<<8)&0x00ff0000) | \ ((((Ulong)(n))<<24)&0xff000000) \ ) #ifdef ETHERNET /* skip the entire file if ethernet is not enabled */ #define ALIGNED(n,a) (((Ulong)(a))&(n-1))?0:1 typedef volatile struct RDrec { /* 16-byte align */ Ulong cw1; /* RCC RPC MCNT */ Ulong cw2; /* OWN ...... BCNT */ char *buf; int resv; } RDrec; #define MCNTMASK 0xfff /* RD cw1 */ typedef volatile struct TDrec { /* 16-byte align */ Ulong cw1; /* BUFF .... TRC */ Ulong cw2; /* OWN ...... BCNT */ char *buf; int resv; } TDrec; /* pointed to by CSR2:CSR1 */ typedef volatile struct INITrec { /* 4-byte align */ Ulong cw; /* TLE RLE MODE */ Ulong padr0; /* mac addr - ls byte is 1st byte of adr */ Ulong padr1; /* mac addr - last 2 bytes */ Ulong ladr0; /* multicast only - mbz */ Ulong ladr1; /* multicast only - mbz */ RDrec *rdr; TDrec *tdr; Ulong resv; } INITrec; #define TDRSIZE 3 /* 2^N of TDR size. ie. 2=4 3=8 4=16 5=32 */ #define RDRSIZE 3 /* 2^N of RDR size. ie. 2=4 3=8 4=16 5=32 */ #define RBUFSIZE 1500 #define TBUFSIZE 64 #define ATDRSIZE (1< /****************** Globals **********************************/ extern int vflag; static INITrec *init_ptr; /****************** Forward Declarations *********************/ #define M_RDP (ETHER_BASE+0x10) #define M_RAP (ETHER_BASE+0x14) #define M_RESET (ETHER_BASE+0x18) #define M_BDP (ETHER_BASE+0x1c) /* PCI Configuration Space Definitions */ #define PCI_VENDORID (0x00) #define PCI_DEVICEID (0x02) #define PCI_COMMAND (0x04) #define PCI_STATUS (0x06) #define PCI_REVID (0x08) #define PCI_PIR (0x09) /* Not used on AMD controller */ #define PCI_SUBCLASS (0x0a) #define PCI_BASECLASS (0x0b) #define PCI_CACHELINE (0x0c) #define PCI_LATENCY (0x0d) #define PCI_HEADERTYPE (0x0e) #define PCI_BIST (0x0f) /* Not used on AMD controller */ #define PCI_BASEADDRESS0 (0x10) #define PCI_BASEADDRESS1 (0x14) #define PCI_BASEADDRESS2 (0x18) /* Not used on AMD controller */ #define PCI_BASEADDRESS3 (0x1c) /* Not used on AMD controller */ #define PCI_BASEADDRESS4 (0x20) /* Not used on AMD controller */ #define PCI_BASEADDRESS5 (0x24) /* Not used on AMD controller */ #define PCI_CARDBUSCIS (0x28) /* Not used on AMD controller */ #define PCI_SUBVENDORID (0x2c) /* Not used on AMD controller */ #define PCI_SUBSYSID (0x2e) /* Not used on AMD controller */ #define PCI_EXPANSIONROM (0x30) #define PCI_INTLINE (0x3c) #define PCI_INTPIN (0x3d) #define PCI_MINGNT (0x3e) #define PCI_MAXLAT (0x3f) #define TYPE0 0x00 #define TYPE1 0x01 #define AMDDEVAD 0xbfe90000 /* Ethernet controller has */ /* IDSEL connected to AD16 */ #define AMD_VENDOR_ID 0x20001022 #define CSR0_ERR (1<<15) #define CSR0_BABL (1<<14) #define CSR0_CERR (1<<13) #define CSR0_MISS (1<<12) #define CSR0_MERR (1<<11) #define CSR0_RINT (1<<10) #define CSR0_TINT (1<<9) #define CSR0_IDON (1<<8) #define CSR0_INTR (1<<7) #define CSR0_IENA (1<<6) #define CSR0_RXON (1<<5) #define CSR0_TXON (1<<4) #define CSR0_TDMD (1<<3) #define CSR0_STOP (1<<2) #define CSR0_STRT (1<<1) #define CSR0_INIT (1<<0) #define CSR_STATUS 0 /* 16-bit */ #define CSR_INIT 1 /* 32-bit */ #define CSR_RDRBASE 24 /* 32-bit */ #define CSR_RDRNEXT 26 /* 32-bit */ #define CSR_RDRCUR 28 /* 32-bit */ #define CSR_TDRBASE 30 /* 32-bit */ #define CSR_TDRCUR 34 /* 32-bit */ #define CSR_RDRCNT 72 /* 16-bit */ #define CSR_TDRCNT 74 /* 16-bit */ #define CSR_RDRLEN 76 /* 16-bit */ #define CSR_TDRLEN 78 /* 16-bit */ #define CSR3_BSWP (1<<2) /* byte swap */ #define BCR20_SSIZE32 (1<<8) #define SWSTYLE_3 3 /* csr15 is programmed via word0 of the init block */ #define CSR15_PROM (1<<15) /* enable promiscuous mode */ #define CSR15_DRCVBC (1<<14) /* disable rx broadcast */ #define CSR15_DRCVPA (1<<13) /* disable rx phy adr */ #define CSR15_DLNKTST (1<<12) /* disable link status */ #define CSR15_DAPC (1<<11) /* disable auto pol corr */ #define CSR15_MENDECL (1<<10) /* mendec loopback mode */ #define CSR15_LRT (1<<9) /* low rx threshold (T-MAU) */ #define CSR15_TSEL (1<<9) /* low rx threshold (AUI) */ #define CSR15_PORTSELMSK (3<<7) /* port select bits */ #define CSR15_INTL (1<<6) /* internal loopback */ #define CSR15_DRTY (1<<5) /* disable retry */ #define CSR15_FCOLL (1<<4) /* force collision */ #define CSR15_DXMTFCS (1<<3) /* disable tx crc */ #define CSR15_LOOP (1<<2) /* loopback mode */ #define CSR15_DTX (1<<1) /* disable tx */ #define CSR15_DRX (1<<0) /* disable rx */ /************************************************************* * void *xalloc(int size) * On bdmr4102, can't use std malloc as that allocs from sdram. * PCI Ethernet controller can't access sdram. Use 128KB of sram * at 0xae000000 instead. */ void *xalloc(int size) { static void *bufptr; void *v; if (bufptr == 0) bufptr = (void *)0xae000000; v = bufptr; size = (size+16)&~15; ((char *)bufptr) += size; if (bufptr >= (void *)0xae020000) return(0); return(v); } /************************************************************* */ STATIC Ushort getCSR(int n) { Ushort v; outw(M_RAP,n); v = inw(M_RDP); return(v); } /************************************************************* */ STATIC Ulong getCSRw(int n) { Ulong v; v = (getCSR(n+1)<<16)|getCSR(n); return(v); } /************************************************************* */ STATIC void putCSR(int n,Ulong v) { outw(M_RAP,n); outw(M_RDP,v); } /************************************************************* */ STATIC void putCSRw(int n,Ulong v) { putCSR(n+1,v>>16); putCSR(n,v&0xffff); } /************************************************************* */ STATIC Ushort getBCR(int n) { outw(M_RAP,n); return(inw(M_BDP)); } /************************************************************* */ STATIC void putBCR(int n,Ushort v) { outw(M_RAP,n); outw(M_BDP,v); } #ifdef VERBOSE /************************************************************* */ STATIC void printRD(RDrec *r) { if (((Ulong)r)&0xf) printf("error: RDrec not aligned.\n"); printf("RDrec %08x: ",r); printf("cw1=%08x ",r->cw1); printf("cw2=%08x ",r->cw2); printf("buf=%08x ",r->buf); printf("resv=%x ",r->resv); printf("\n"); } /************************************************************* */ STATIC void printTD(TDrec *r) { if (((Ulong)r)&0xf) printf("error: TDrec not aligned.\n"); printf("TDrec %08x: ",r); printf("cw1=%08x ",r->cw1); printf("cw2=%08x ",r->cw2); printf("buf=%08x ",r->buf); printf("resv=%x ",r->resv); printf("\n"); } /************************************************************* */ STATIC printINIT(INITrec *r) { if (((Ulong)r)&0x3) printf("error: INITrec not aligned.\n"); printf("INITrec %08x:\n\t",r); printf("cw=%08x ",r->cw); printf("mac=%04x%08x ",r->padr1,r->padr0); printf("ladr=%x.%x ",r->ladr1,r->ladr0); printf("rdr=%08x ",r->rdr); printf("tdr=%08x ",r->tdr); printf("\n"); } /************************************************************* */ STATIC void printCSRs() { int i; for (i=0;i<32;i++) { if ((i%8)==0) printf("\n%2d - ",i); printf("%04x ",getCSR(i)); } printf("\n"); } #endif /************************************************************* */ STATIC int initDA(Uchar *macadr) { INITrec *ir; RDrec *rr; TDrec *tr; int i; Ulong v; if (init_ptr) return(1); /* 981216 only do this the first time */ /* # Setup Ethernet PCI Configuration Space # # Since this is an embedded application with no expansion slots this # code forgoes the process of scanning all PCI devices. We do a # detect of the the AMD ethernet controller as Device 20 which has # IDSEL pin hardwired to AD16 on the eval board. The routine then # maps the ethernet registers to 0x1fbb0000 and IO space starting at 0x0, # configures timing, disables the expansion ROM, and configures interrupts. # The routine finishs by configuring the Fbus unit # # Set up Bus Unit for Configuration Transaction */ /* Map all 1fexxxxx memory space to PCI */ outw(M_FBUSCMP,0x000f1fe0); /* map cfg space starting at 0x1e000000 - 0x1effffff */ outw(M_FBUSAC,0x40071fe8); /* Configure FBUS unit. Burst length set to 1 and Ext Ack. Mmaster */ outw(M_FBUSCFG,0x08); // 990318 was 0x0c /* Perform a PCI Device Detect */ v = inw(AMDDEVAD+PCI_VENDORID); if (v != AMD_VENDOR_ID) { printf("failed device detect %08x\n",v); return(0); } /* Disconnect Controller from all but configuration cycles */ outw(AMDDEVAD+PCI_COMMAND,0); /* # Even though there is no IO space in MIPS we will initialize # the register to begin at zero. The bus controller on the 4102 # supports IO space so if somebody wants to use it, it starts at 0x0 */ outw(AMDDEVAD+PCI_BASEADDRESS0,0); outw(AMDDEVAD+PCI_BASEADDRESS0+4,0); /* # Assign AMD Ethernet Controller to 0x1fe00000 # There are 32 bytes of address locations required */ outw(AMDDEVAD+PCI_BASEADDRESS1,ETHER_BASE-0xa0000000); outw(AMDDEVAD+PCI_BASEADDRESS1+4,0); /* # Disable Expansion ROM */ outw(AMDDEVAD+PCI_EXPANSIONROM,0); /* # Configure Latency Timer */ v = inw(AMDDEVAD+PCI_CACHELINE); v &= 0xffff00ff; outw(AMDDEVAD+PCI_CACHELINE,v); /* # Configure Command Register */ outw(AMDDEVAD+PCI_COMMAND,0x07); /* Enable Bus Mastering, Memory Access, IO Access. Disable Parity, * SERR, VGA, etc. */ outw(M_RDP,0); /* switch to DWORD mode */ /* 981216 I used to wrap an if !init_ptr around just this section */ ir = (INITrec *)xalloc(sizeof(INITrec)); if (!ir || !ALIGNED(4,ir)) return(0); rr = (RDrec *)xalloc(sizeof(RDrec)*ARDRSIZE); if (!rr || !ALIGNED(16,rr)) return(0); ir->rdr = (log2phy(rr)); base_RD = next_RD = rr; for (i=0;ibuf = (log2phy(xalloc(RBUFSIZE))); if (!rr->buf) return(0); rr->cw1 = rr->resv = 0; rr->cw2 = (OWN|ONES|((0-RBUFSIZE)&0xfff)); } tr = (TDrec *)xalloc(sizeof(TDrec)*ATDRSIZE); if (!tr || !ALIGNED(16,rr)) return(0); ir->tdr = (log2phy(tr)); base_TD = next_TD = tr; for (i=0;ibuf = (log2phy(xalloc(TBUFSIZE))); if (!tr->buf) return(0); tr->cw1 = tr->resv = 0; tr->cw2 = (ONES|((0-TBUFSIZE)&0xfff)); } init_ptr = ir; putBCR(20,BCR20_SSIZE32|SWSTYLE_3); putBCR(6,1); /* led2 = collisions */ #ifdef MIPSEB putCSR(3,CSR3_BSWP); /* byte swap */ #endif ir->cw = (TDRSIZE<cw |= CSR15_PROM; #endif ir->padr0 = ((macadr[3]<<24)|(macadr[2]<<16)|(macadr[1]<<8)|macadr[0]); ir->padr1 = ((macadr[5]<<8)|macadr[4]); ir->ladr0 = ir->ladr1 = ir->resv = 0; putCSRw(CSR_INIT,(Ulong)log2phy(ir)); putCSR(0,CSR0_STRT|CSR0_INIT); for (i=0;i<1000000;i++) if (getCSR(0)&CSR0_IDON) break; if (i >= 1000000) { printf("timeout waiting for IDON.\n"); putCSR(0,CSR0_STOP); #ifdef VERBOSE printCSRs(); printINIT(ir); #endif return(0); } return(1); } /************************************************************* * void *am79970_driver(int op,void *vp1,void *vp2) * Main entry point for the driver. */ void *am79970_driver(int op,void *vp1,void *vp2) { char *macAddr = vp1; RDrec *rr = vp1; int *pLen = vp2; TDrec *tr; int n; switch (op) { case ETHER_INIT : /* int ether_driver(ETHER_INIT,Uchar *macAddr,void) */ if (!initDA(macAddr)) return(0); return((void *)1); case ETHER_GETTBA : /* char *ether_driver(ETHER_GETTBA,void, int *len) */ tr = next_TD; if (tr->cw2&OWN) return(0); n = *pLen; tr->cw2 = ONES|STP|ENP|((0-n)&0xfff); return(phy2k1(tr->buf)); case ETHER_TBARDY : /* int ether_driver(ETHER_TBRDY,void,void) */ tr = next_TD; tr->cw2 |= OWN; putCSR(CSR_STATUS,getCSR(CSR_STATUS)|CSR0_TDMD); /* inform the chip */ next_TD++; next_TDcnt++; if (next_TDcnt >= ATDRSIZE) { next_TD = base_TD; next_TDcnt = 0; } return((void *)1); case ETHER_GETRXREC : /* RXREC *ether_driver(ETHER_GETRXREC,void,void) */ rr = next_RD; if (rr->cw2&OWN) return(0); return((void *)rr); case ETHER_GETRBA : /* char *ether_driver(ETHER_GETRBA,RXrec *rr,int *len) */ *pLen = rr->cw1&MCNTMASK; return(phy2k1(rr->buf)); case ETHER_RXDONE : /* int ether_driver(ETHER_RXDONE,RXrec *rr,void) */ rr->cw2 |= OWN; next_RD++; next_RDcnt++; if (next_RDcnt >= ARDRSIZE) { next_RD = base_RD; next_RDcnt = 0; } return((void *)1); case ETHER_RXRDY : /* int ether_driver(ETHER_RXRDY,void,void) */ rr = next_RD; if (rr->cw2&OWN) return(0); return((void *)1); default : return(0); } return(0); } #else am79970_foobar() {} #endif /* ETHERNET */