Home | History | Annotate | Download | only in netboot
      1 /*
      2   Etherboot DEC Tulip driver
      3   adapted by Ken Yap from
      4 
      5   FreeBSD netboot DEC 21143 driver
      6 
      7   Author: David Sharp
      8     date: Nov/98
      9 
     10  Known to work on DEC DE500 using 21143-PC chipset.
     11  Even on cards with the same chipset there can be
     12  incompatablity problems with the way media selection
     13  and status LED settings are done.  See comments below.
     14 
     15  Some code fragments were taken from verious places,
     16  Ken Yap's etherboot, FreeBSD's if_de.c, and various
     17  Linux related files.  DEC's manuals for the 21143 and
     18  SROM format were very helpful.  The Linux de driver
     19  development page has a number of links to useful
     20  related information.  Have a look at:
     21  ftp://cesdis.gsfc.nasa.gov/pub/linux/drivers/tulip-devel.html
     22 
     23 */
     24 
     25 #include "etherboot.h"
     26 #include "nic.h"
     27 #include "pci.h"
     28 #include "cards.h"
     29 #include "otulip.h"
     30 
     31 static unsigned short vendor, dev_id;
     32 static unsigned short ioaddr;
     33 static unsigned int *membase;
     34 static unsigned char srom[1024];
     35 
     36 #define BUFLEN 1536     /* must be longword divisable */
     37                         /* buffers must be longword aligned */
     38 
     39 /* transmit descriptor and buffer */
     40 static struct txdesc txd;
     41 
     42 /* receive descriptor(s) and buffer(s) */
     43 #define NRXD 4
     44 static struct rxdesc rxd[NRXD];
     45 static int rxd_tail = 0;
     46 #ifdef	USE_LOWMEM_BUFFER
     47 #define rxb ((char *)0x10000 - NRXD * BUFLEN)
     48 #define txb ((char *)0x10000 - NRXD * BUFLEN - BUFLEN)
     49 #else
     50 static unsigned char rxb[NRXD * BUFLEN];
     51 static unsigned char txb[BUFLEN];
     52 #endif
     53 
     54 static unsigned char ehdr[ETH_HLEN];    /* buffer for ethernet header */
     55 
     56 enum tulip_offsets {
     57         CSR0=0,    CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,
     58         CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,
     59         CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 };
     60 
     61 
     62 /***************************************************************************/
     63 /* 21143 specific stuff  */
     64 /***************************************************************************/
     65 
     66 /* XXX assume 33MHz PCI bus,  this is not very accurate and should be
     67    used only with gross over estimations of required delay times unless
     68    you tune UADJUST to your specific processor and I/O subsystem */
     69 
     70 #define UADJUST 870
     71 static void udelay(unsigned long usec) {
     72   unsigned long i;
     73   for (i=((usec*UADJUST)/33)+1; i>0; i--) (void) TULIP_CSR_READ(csr_0);
     74 }
     75 
     76 /* The following srom related code was taken from FreeBSD's if_de.c */
     77 /* with minor alterations to make it work here.  the Linux code is */
     78 /* better but this was easier to use */
     79 
     80 static void delay_300ns(void)
     81 {
     82     int idx;
     83     for (idx = (300 / 33) + 1; idx > 0; idx--)
     84         (void) TULIP_CSR_READ(csr_busmode);
     85 }
     86 
     87 #define EMIT do { TULIP_CSR_WRITE(csr_srom_mii, csr); delay_300ns(); } while (0)
     88 
     89 static void srom_idle(void)
     90 {
     91     unsigned bit, csr;
     92 
     93     csr  = SROMSEL ; EMIT;
     94     csr  = SROMSEL | SROMRD; EMIT;
     95     csr ^= SROMCS; EMIT;
     96     csr ^= SROMCLKON; EMIT;
     97     /*
     98      * Write 25 cycles of 0 which will force the SROM to be idle.
     99      */
    100     for (bit = 3 + SROM_BITWIDTH + 16; bit > 0; bit--) {
    101         csr ^= SROMCLKOFF; EMIT;    /* clock low; data not valid */
    102         csr ^= SROMCLKON; EMIT;     /* clock high; data valid */
    103     }
    104     csr ^= SROMCLKOFF; EMIT;
    105     csr ^= SROMCS; EMIT;
    106     csr  = 0; EMIT;
    107 }
    108 
    109 static void srom_read(void)
    110 {
    111     unsigned idx;
    112     const unsigned bitwidth = SROM_BITWIDTH;
    113     const unsigned cmdmask = (SROMCMD_RD << bitwidth);
    114     const unsigned msb = 1 << (bitwidth + 3 - 1);
    115     unsigned lastidx = (1 << bitwidth) - 1;
    116 
    117     srom_idle();
    118 
    119     for (idx = 0; idx <= lastidx; idx++) {
    120         unsigned lastbit, data, bits, bit, csr;
    121         csr  = SROMSEL ;                EMIT;
    122         csr  = SROMSEL | SROMRD;        EMIT;
    123         csr ^= SROMCSON;                EMIT;
    124         csr ^=            SROMCLKON;    EMIT;
    125 
    126         lastbit = 0;
    127         for (bits = idx|cmdmask, bit = bitwidth + 3; bit > 0; bit--, bits <<= 1)
    128  {
    129             const unsigned thisbit = bits & msb;
    130             csr ^= SROMCLKOFF; EMIT;    /* clock low; data not valid */
    131             if (thisbit != lastbit) {
    132                 csr ^= SROMDOUT; EMIT;  /* clock low; invert data */
    133             } else {
    134                 EMIT;
    135             }
    136             csr ^= SROMCLKON; EMIT;     /* clock high; data valid */
    137             lastbit = thisbit;
    138         }
    139         csr ^= SROMCLKOFF; EMIT;
    140 
    141         for (data = 0, bits = 0; bits < 16; bits++) {
    142             data <<= 1;
    143             csr ^= SROMCLKON; EMIT;     /* clock high; data valid */
    144             data |= TULIP_CSR_READ(csr_srom_mii) & SROMDIN ? 1 : 0;
    145             csr ^= SROMCLKOFF; EMIT;    /* clock low; data not valid */
    146         }
    147         srom[idx*2] = data & 0xFF;
    148         srom[idx*2+1] = data >> 8;
    149         csr  = SROMSEL | SROMRD; EMIT;
    150         csr  = 0; EMIT;
    151     }
    152     srom_idle();
    153 }
    154 
    155 /**************************************************************************
    156 ETH_RESET - Reset adapter
    157 ***************************************************************************/
    158 static void tulip_reset(struct nic *nic)
    159 {
    160         int x,cnt=2;
    161 
    162         outl(0x00000001, ioaddr + CSR0);
    163         udelay(1000);
    164         /* turn off reset and set cache align=16lword, burst=unlimit */
    165         outl(0x01A08000, ioaddr + CSR0);
    166 
    167 	/* for some reason the media selection does not take
    168            the first time se it is repeated.  */
    169 
    170         while(cnt--) {
    171         /* stop TX,RX processes */
    172         if (cnt == 1)
    173 		outl(0x32404000, ioaddr + CSR6);
    174         else
    175 		outl(0x32000040, ioaddr + CSR6);
    176 
    177         /* XXX - media selection is vendor specific and hard coded right
    178            here.  This should be fixed to use the hints in the SROM and
    179            allow media selection by the user at runtime.  MII support
    180            should also be added.  Support for chips other than the
    181            21143 should be added here as well  */
    182 
    183         /* start  set to 10Mbps half-duplex */
    184 
    185         /* setup SIA */
    186         outl(0x0, ioaddr + CSR13);              /* reset SIA */
    187         outl(0x7f3f, ioaddr + CSR14);
    188         outl(0x8000008, ioaddr + CSR15);
    189         outl(0x0, ioaddr + CSR13);
    190         outl(0x1, ioaddr + CSR13);
    191         outl(0x2404000, ioaddr + CSR6);
    192 
    193         /* initalize GP */
    194         outl(0x8af0008, ioaddr + CSR15);
    195         outl(0x50008, ioaddr + CSR15);
    196 
    197         /* end  set to 10Mbps half-duplex */
    198 
    199 	if (vendor == PCI_VENDOR_ID_MACRONIX && dev_id == PCI_DEVICE_ID_MX987x5) {
    200 		/* do stuff for MX98715 */
    201 		outl(0x01a80000, ioaddr + CSR6);
    202 		outl(0xFFFFFFFF, ioaddr + CSR14);
    203 		outl(0x00001000, ioaddr + CSR12);
    204 	}
    205 
    206         outl(0x0, ioaddr + CSR7);       /* disable interrupts */
    207 
    208         /* construct setup packet which is used by the 21143 to
    209            program its CAM to recognize interesting MAC addresses */
    210 
    211         memset(&txd, 0, sizeof(struct txdesc));
    212         txd.buf1addr = &txb[0];
    213         txd.buf2addr = &txb[0];         /* just in case */
    214         txd.buf1sz   = 192;             /* setup packet must be 192 bytes */
    215         txd.buf2sz   = 0;
    216         txd.control  = 0x020;           /* setup packet */
    217         txd.status   = 0x80000000;      /* give ownership to 21143 */
    218 
    219         /* construct perfect filter frame */
    220         /* with mac address as first match */
    221         /* and broadcast address for all others */
    222 
    223         for(x=0;x<192;x++) txb[x] = 0xff;
    224         txb[0] = nic->node_addr[0];
    225         txb[1] = nic->node_addr[1];
    226         txb[4] = nic->node_addr[2];
    227         txb[5] = nic->node_addr[3];
    228         txb[8] = nic->node_addr[4];
    229         txb[9] = nic->node_addr[5];
    230         outl((unsigned long)&txd, ioaddr + CSR4);        /* set xmit buf */
    231         outl(0x2406000, ioaddr + CSR6);         /* start transmiter */
    232 
    233         udelay(50000);  /* wait for the setup packet to be processed */
    234 
    235         }
    236 
    237         /* setup receive descriptor */
    238         {
    239           int x;
    240           for(x=0;x<NRXD;x++) {
    241             memset(&rxd[x], 0, sizeof(struct rxdesc));
    242             rxd[x].buf1addr = &rxb[x * BUFLEN];
    243             rxd[x].buf2addr = 0;        /* not used */
    244             rxd[x].buf1sz   = BUFLEN;
    245             rxd[x].buf2sz   = 0;        /* not used */
    246             rxd[x].control  = 0x0;
    247             rxd[x].status   = 0x80000000;       /* give ownership it to 21143 */
    248           }
    249           rxd[NRXD - 1].control  = 0x008;       /* Set Receive end of ring on la
    250 st descriptor */
    251           rxd_tail = 0;
    252         }
    253 
    254         /* tell DC211XX where to find rx descriptor list */
    255         outl((unsigned long)&rxd[0], ioaddr + CSR3);
    256         /* start the receiver */
    257         outl(0x2406002, ioaddr + CSR6);
    258 
    259 }
    260 
    261 /**************************************************************************
    262 ETH_TRANSMIT - Transmit a frame
    263 ***************************************************************************/
    264 static const char padmap[] = {
    265         0, 3, 2, 1};
    266 
    267 static void tulip_transmit(struct nic *nic, const char *d, unsigned int t, unsigned int s, const char *p)
    268 {
    269         unsigned long time;
    270 
    271         /* setup ethernet header */
    272 
    273 	memcpy(ehdr, d, ETH_ALEN);
    274 	memcpy(&ehdr[ETH_ALEN], nic->node_addr, ETH_ALEN);
    275         ehdr[ETH_ALEN*2] = (t >> 8) & 0xff;
    276         ehdr[ETH_ALEN*2+1] = t & 0xff;
    277 
    278         /* setup the transmit descriptor */
    279 
    280         memset(&txd, 0, sizeof(struct txdesc));
    281 
    282         txd.buf1addr = &ehdr[0];        /* ethernet header */
    283         txd.buf1sz   = ETH_HLEN;
    284 
    285         txd.buf2addr = p;               /* packet to transmit */
    286         txd.buf2sz   = s;
    287 
    288         txd.control  = 0x188;           /* LS+FS+TER */
    289 
    290         txd.status   = 0x80000000;      /* give it to 21143 */
    291 
    292         outl(inl(ioaddr + CSR6) & ~0x00004000, ioaddr + CSR6);
    293         outl((unsigned long)&txd, ioaddr + CSR4);
    294         outl(inl(ioaddr + CSR6) | 0x00004000, ioaddr + CSR6);
    295 
    296 /*   Wait for transmit to complete before returning.  not well tested.
    297 
    298         time = currticks();
    299         while(txd.status & 0x80000000) {
    300           if (currticks() - time > 20) {
    301             printf("transmit timeout.\n");
    302             break;
    303           }
    304         }
    305 */
    306 
    307 }
    308 
    309 /**************************************************************************
    310 ETH_POLL - Wait for a frame
    311 ***************************************************************************/
    312 static int tulip_poll(struct nic *nic)
    313 {
    314         if (rxd[rxd_tail].status & 0x80000000) return 0;
    315 
    316         nic->packetlen = (rxd[rxd_tail].status & 0x3FFF0000) >> 16;
    317 
    318         /* copy packet to working buffer */
    319         /* XXX - this copy could be avoided with a little more work
    320            but for now we are content with it because the optimised
    321            memcpy(, , ) is quite fast */
    322 
    323         memcpy(nic->packet, rxb + rxd_tail * BUFLEN, nic->packetlen);
    324 
    325         /* return the descriptor and buffer to recieve ring */
    326         rxd[rxd_tail].status = 0x80000000;
    327         rxd_tail++;
    328         if (rxd_tail == NRXD) rxd_tail = 0;
    329 
    330         return 1;
    331 }
    332 
    333 static void tulip_disable(struct nic *nic)
    334 {
    335 	/* nothing for the moment */
    336 }
    337 
    338 /**************************************************************************
    339 ETH_PROBE - Look for an adapter
    340 ***************************************************************************/
    341 struct nic *otulip_probe(struct nic *nic, unsigned short *io_addrs, struct pci_device *pci)
    342 {
    343         int i;
    344 
    345 	if (io_addrs == 0 || *io_addrs == 0)
    346 		return (0);
    347 	vendor = pci->vendor;
    348 	dev_id = pci->dev_id;
    349 	ioaddr = *io_addrs;
    350 	membase = (unsigned int *)pci->membase;
    351 
    352         /* wakeup chip */
    353         pcibios_write_config_dword(pci->bus,pci->devfn,0x40,0x00000000);
    354 
    355         /* Stop the chip's Tx and Rx processes. */
    356         /* outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6); */
    357         /* Clear the missed-packet counter. */
    358         /* (volatile int)inl(ioaddr + CSR8); */
    359 
    360         srom_read();
    361 
    362 	for (i=0; i < ETH_ALEN; i++)
    363 		nic->node_addr[i] = srom[20+i];
    364 
    365         printf("Tulip %! at ioaddr %#hX\n", nic->node_addr, ioaddr);
    366 
    367         tulip_reset(nic);
    368 
    369 	nic->reset = tulip_reset;
    370 	nic->poll = tulip_poll;
    371 	nic->transmit = tulip_transmit;
    372 	nic->disable = tulip_disable;
    373         return nic;
    374 }
    375