123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375 |
- /*
- Etherboot DEC Tulip driver
- adapted by Ken Yap from
- FreeBSD netboot DEC 21143 driver
- Author: David Sharp
- date: Nov/98
- Known to work on DEC DE500 using 21143-PC chipset.
- Even on cards with the same chipset there can be
- incompatablity problems with the way media selection
- and status LED settings are done. See comments below.
- Some code fragments were taken from verious places,
- Ken Yap's etherboot, FreeBSD's if_de.c, and various
- Linux related files. DEC's manuals for the 21143 and
- SROM format were very helpful. The Linux de driver
- development page has a number of links to useful
- related information. Have a look at:
- ftp://cesdis.gsfc.nasa.gov/pub/linux/drivers/tulip-devel.html
- */
- #include "etherboot.h"
- #include "nic.h"
- #include "pci.h"
- #include "cards.h"
- #include "otulip.h"
- static unsigned short vendor, dev_id;
- static unsigned short ioaddr;
- static unsigned int *membase;
- static unsigned char srom[1024];
- #define BUFLEN 1536 /* must be longword divisable */
- /* buffers must be longword aligned */
- /* transmit descriptor and buffer */
- static struct txdesc txd;
- /* receive descriptor(s) and buffer(s) */
- #define NRXD 4
- static struct rxdesc rxd[NRXD];
- static int rxd_tail = 0;
- #ifdef USE_LOWMEM_BUFFER
- #define rxb ((char *)0x10000 - NRXD * BUFLEN)
- #define txb ((char *)0x10000 - NRXD * BUFLEN - BUFLEN)
- #else
- static unsigned char rxb[NRXD * BUFLEN];
- static unsigned char txb[BUFLEN];
- #endif
- static unsigned char ehdr[ETH_HLEN]; /* buffer for ethernet header */
- enum tulip_offsets {
- CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,
- CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,
- CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 };
- /***************************************************************************/
- /* 21143 specific stuff */
- /***************************************************************************/
- /* XXX assume 33MHz PCI bus, this is not very accurate and should be
- used only with gross over estimations of required delay times unless
- you tune UADJUST to your specific processor and I/O subsystem */
- #define UADJUST 870
- static void udelay(unsigned long usec) {
- unsigned long i;
- for (i=((usec*UADJUST)/33)+1; i>0; i--) (void) TULIP_CSR_READ(csr_0);
- }
- /* The following srom related code was taken from FreeBSD's if_de.c */
- /* with minor alterations to make it work here. the Linux code is */
- /* better but this was easier to use */
- static void delay_300ns(void)
- {
- int idx;
- for (idx = (300 / 33) + 1; idx > 0; idx--)
- (void) TULIP_CSR_READ(csr_busmode);
- }
- #define EMIT do { TULIP_CSR_WRITE(csr_srom_mii, csr); delay_300ns(); } while (0)
- static void srom_idle(void)
- {
- unsigned bit, csr;
- csr = SROMSEL ; EMIT;
- csr = SROMSEL | SROMRD; EMIT;
- csr ^= SROMCS; EMIT;
- csr ^= SROMCLKON; EMIT;
- /*
- * Write 25 cycles of 0 which will force the SROM to be idle.
- */
- for (bit = 3 + SROM_BITWIDTH + 16; bit > 0; bit--) {
- csr ^= SROMCLKOFF; EMIT; /* clock low; data not valid */
- csr ^= SROMCLKON; EMIT; /* clock high; data valid */
- }
- csr ^= SROMCLKOFF; EMIT;
- csr ^= SROMCS; EMIT;
- csr = 0; EMIT;
- }
- static void srom_read(void)
- {
- unsigned idx;
- const unsigned bitwidth = SROM_BITWIDTH;
- const unsigned cmdmask = (SROMCMD_RD << bitwidth);
- const unsigned msb = 1 << (bitwidth + 3 - 1);
- unsigned lastidx = (1 << bitwidth) - 1;
- srom_idle();
- for (idx = 0; idx <= lastidx; idx++) {
- unsigned lastbit, data, bits, bit, csr;
- csr = SROMSEL ; EMIT;
- csr = SROMSEL | SROMRD; EMIT;
- csr ^= SROMCSON; EMIT;
- csr ^= SROMCLKON; EMIT;
- lastbit = 0;
- for (bits = idx|cmdmask, bit = bitwidth + 3; bit > 0; bit--, bits <<= 1)
- {
- const unsigned thisbit = bits & msb;
- csr ^= SROMCLKOFF; EMIT; /* clock low; data not valid */
- if (thisbit != lastbit) {
- csr ^= SROMDOUT; EMIT; /* clock low; invert data */
- } else {
- EMIT;
- }
- csr ^= SROMCLKON; EMIT; /* clock high; data valid */
- lastbit = thisbit;
- }
- csr ^= SROMCLKOFF; EMIT;
- for (data = 0, bits = 0; bits < 16; bits++) {
- data <<= 1;
- csr ^= SROMCLKON; EMIT; /* clock high; data valid */
- data |= TULIP_CSR_READ(csr_srom_mii) & SROMDIN ? 1 : 0;
- csr ^= SROMCLKOFF; EMIT; /* clock low; data not valid */
- }
- srom[idx*2] = data & 0xFF;
- srom[idx*2+1] = data >> 8;
- csr = SROMSEL | SROMRD; EMIT;
- csr = 0; EMIT;
- }
- srom_idle();
- }
- /**************************************************************************
- ETH_RESET - Reset adapter
- ***************************************************************************/
- static void tulip_reset(struct nic *nic)
- {
- int x,cnt=2;
- outl(0x00000001, ioaddr + CSR0);
- udelay(1000);
- /* turn off reset and set cache align=16lword, burst=unlimit */
- outl(0x01A08000, ioaddr + CSR0);
- /* for some reason the media selection does not take
- the first time se it is repeated. */
- while(cnt--) {
- /* stop TX,RX processes */
- if (cnt == 1)
- outl(0x32404000, ioaddr + CSR6);
- else
- outl(0x32000040, ioaddr + CSR6);
- /* XXX - media selection is vendor specific and hard coded right
- here. This should be fixed to use the hints in the SROM and
- allow media selection by the user at runtime. MII support
- should also be added. Support for chips other than the
- 21143 should be added here as well */
- /* start set to 10Mbps half-duplex */
- /* setup SIA */
- outl(0x0, ioaddr + CSR13); /* reset SIA */
- outl(0x7f3f, ioaddr + CSR14);
- outl(0x8000008, ioaddr + CSR15);
- outl(0x0, ioaddr + CSR13);
- outl(0x1, ioaddr + CSR13);
- outl(0x2404000, ioaddr + CSR6);
- /* initalize GP */
- outl(0x8af0008, ioaddr + CSR15);
- outl(0x50008, ioaddr + CSR15);
- /* end set to 10Mbps half-duplex */
- if (vendor == PCI_VENDOR_ID_MACRONIX && dev_id == PCI_DEVICE_ID_MX987x5) {
- /* do stuff for MX98715 */
- outl(0x01a80000, ioaddr + CSR6);
- outl(0xFFFFFFFF, ioaddr + CSR14);
- outl(0x00001000, ioaddr + CSR12);
- }
- outl(0x0, ioaddr + CSR7); /* disable interrupts */
- /* construct setup packet which is used by the 21143 to
- program its CAM to recognize interesting MAC addresses */
- memset(&txd, 0, sizeof(struct txdesc));
- txd.buf1addr = &txb[0];
- txd.buf2addr = &txb[0]; /* just in case */
- txd.buf1sz = 192; /* setup packet must be 192 bytes */
- txd.buf2sz = 0;
- txd.control = 0x020; /* setup packet */
- txd.status = 0x80000000; /* give ownership to 21143 */
- /* construct perfect filter frame */
- /* with mac address as first match */
- /* and broadcast address for all others */
- for(x=0;x<192;x++) txb[x] = 0xff;
- txb[0] = nic->node_addr[0];
- txb[1] = nic->node_addr[1];
- txb[4] = nic->node_addr[2];
- txb[5] = nic->node_addr[3];
- txb[8] = nic->node_addr[4];
- txb[9] = nic->node_addr[5];
- outl((unsigned long)&txd, ioaddr + CSR4); /* set xmit buf */
- outl(0x2406000, ioaddr + CSR6); /* start transmiter */
- udelay(50000); /* wait for the setup packet to be processed */
- }
- /* setup receive descriptor */
- {
- int x;
- for(x=0;x<NRXD;x++) {
- memset(&rxd[x], 0, sizeof(struct rxdesc));
- rxd[x].buf1addr = &rxb[x * BUFLEN];
- rxd[x].buf2addr = 0; /* not used */
- rxd[x].buf1sz = BUFLEN;
- rxd[x].buf2sz = 0; /* not used */
- rxd[x].control = 0x0;
- rxd[x].status = 0x80000000; /* give ownership it to 21143 */
- }
- rxd[NRXD - 1].control = 0x008; /* Set Receive end of ring on la
- st descriptor */
- rxd_tail = 0;
- }
- /* tell DC211XX where to find rx descriptor list */
- outl((unsigned long)&rxd[0], ioaddr + CSR3);
- /* start the receiver */
- outl(0x2406002, ioaddr + CSR6);
- }
- /**************************************************************************
- ETH_TRANSMIT - Transmit a frame
- ***************************************************************************/
- static const char padmap[] = {
- 0, 3, 2, 1};
- static void tulip_transmit(struct nic *nic, const char *d, unsigned int t, unsigned int s, const char *p)
- {
- unsigned long time;
- /* setup ethernet header */
- memcpy(ehdr, d, ETH_ALEN);
- memcpy(&ehdr[ETH_ALEN], nic->node_addr, ETH_ALEN);
- ehdr[ETH_ALEN*2] = (t >> 8) & 0xff;
- ehdr[ETH_ALEN*2+1] = t & 0xff;
- /* setup the transmit descriptor */
- memset(&txd, 0, sizeof(struct txdesc));
- txd.buf1addr = &ehdr[0]; /* ethernet header */
- txd.buf1sz = ETH_HLEN;
- txd.buf2addr = p; /* packet to transmit */
- txd.buf2sz = s;
- txd.control = 0x188; /* LS+FS+TER */
- txd.status = 0x80000000; /* give it to 21143 */
- outl(inl(ioaddr + CSR6) & ~0x00004000, ioaddr + CSR6);
- outl((unsigned long)&txd, ioaddr + CSR4);
- outl(inl(ioaddr + CSR6) | 0x00004000, ioaddr + CSR6);
- /* Wait for transmit to complete before returning. not well tested.
- time = currticks();
- while(txd.status & 0x80000000) {
- if (currticks() - time > 20) {
- printf("transmit timeout.\n");
- break;
- }
- }
- */
- }
- /**************************************************************************
- ETH_POLL - Wait for a frame
- ***************************************************************************/
- static int tulip_poll(struct nic *nic)
- {
- if (rxd[rxd_tail].status & 0x80000000) return 0;
- nic->packetlen = (rxd[rxd_tail].status & 0x3FFF0000) >> 16;
- /* copy packet to working buffer */
- /* XXX - this copy could be avoided with a little more work
- but for now we are content with it because the optimised
- memcpy(, , ) is quite fast */
- memcpy(nic->packet, rxb + rxd_tail * BUFLEN, nic->packetlen);
- /* return the descriptor and buffer to recieve ring */
- rxd[rxd_tail].status = 0x80000000;
- rxd_tail++;
- if (rxd_tail == NRXD) rxd_tail = 0;
- return 1;
- }
- static void tulip_disable(struct nic *nic)
- {
- /* nothing for the moment */
- }
- /**************************************************************************
- ETH_PROBE - Look for an adapter
- ***************************************************************************/
- struct nic *otulip_probe(struct nic *nic, unsigned short *io_addrs, struct pci_device *pci)
- {
- int i;
- if (io_addrs == 0 || *io_addrs == 0)
- return (0);
- vendor = pci->vendor;
- dev_id = pci->dev_id;
- ioaddr = *io_addrs;
- membase = (unsigned int *)pci->membase;
- /* wakeup chip */
- pcibios_write_config_dword(pci->bus,pci->devfn,0x40,0x00000000);
- /* Stop the chip's Tx and Rx processes. */
- /* outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6); */
- /* Clear the missed-packet counter. */
- /* (volatile int)inl(ioaddr + CSR8); */
- srom_read();
- for (i=0; i < ETH_ALEN; i++)
- nic->node_addr[i] = srom[20+i];
- printf("Tulip %! at ioaddr %#hX\n", nic->node_addr, ioaddr);
- tulip_reset(nic);
- nic->reset = tulip_reset;
- nic->poll = tulip_poll;
- nic->transmit = tulip_transmit;
- nic->disable = tulip_disable;
- return nic;
- }
|