/* * Xircom Realport PCMCIA card support. * * Documentation sources: * 1. X1601-3 External Reference Specification, Revision C. Xircom Inc., * Available for download from http://www.xircom.com/ * 2. ML6692 100BASE-TX Physical Layer with MII specification. MicroLinear * Corp, Available for download from http://www.microlinear.com/ * 3. Scott Mitchell's if_xe driver for FreeBSD. * 4. Werner Koch's xirc2ps driver for Linux. * 5. Plan9 drivers ethersmc, etherec2t, and ether82557. * * The mii routines were derived from those in the Linux driver, see * the mii section for the license. */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/error.h" #include "../port/netif.h" #include "etherif.h" #define PageSelect(c, x) (outb((c)->port+Pr, (x))) #define csr8r(c, r) (inb((c)->port+(r))) #define csr16r(c, r) (ins((c)->port+(r))) #define csr8sr(c, r, b, l) (insb((c)->port+(r), (b), (int)(l))) #define csr16sr(c, r, w, l) (inss((c)->port+(r), (w), (int)(l))) #define csr8w(c, r, b) (outb((c)->port+(r), (int)(b))) #define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w))) #define csr8sw(c, r, b, l) (outsb((c)->port+(r), (b), (int)(l))) #define csr16sw(c, r, w, l) (outss((c)->port+(r), (w), (int)(l))) typedef struct { Lock; ushort rev; int attached; Block *txbp; /* transmit buffer */ int srev; int port; ulong ccrport; int slot; int mohawk; int dingo; int speed; int duplex; ulong interrupts; ulong txover; ulong rxreject; ulong macintr; ulong rxbad; ulong longpacket; ulong rxwrap; ulong rxbadlen; ulong rxoklen; } Ctlr; static uint plength(Ether* ether); static void config_dingo(Ether* ether); static void config_mohawk(Ether* ether); static void enable_intr(Ctlr* ctlr); static void disable_intr(Ctlr* ctlr); /* reset */ static void chipreset(Ctlr* ctlr); static void hardreset(Ctlr* ctlr); static void softreset(Ctlr* ctlr); static void chipenable(Ether* ether); static void mediaselect(Ctlr* ctrl); /* CIS */ static void findmodel(Ether *ether,char *type); static void findspeed(Ether *ether); static int getslot(Ether *ether, char **type); static int readnodeid(Ether* ether); static void cis_scan(Ether* ether); /* debug */ void printbits(uint x); static void rxreg_dump(Ctlr *ctlr); static void txreg_dump(Ctlr *ctlr); static void reg_dump(Ctlr *ctlr); /* MII */ static void mii_idle(Ctlr *ctlr); static void mii_putbit(Ctlr *ctlr, uint data); static int mii_getbit(Ctlr *ctlr); static void mii_wbits(Ctlr *ctlr, uint data, int len); static uint mii_rd(Ctlr *ctlr, uchar phyaddr, uchar phyreg); static void mii_wr(Ctlr *ctlr, uchar phyaddr, uchar phyreg, uint data, int len); static int mii_init(Ctlr *ctlr); static char* xircompcmcia[] = { "CEM56", /* dingo/mohawk (not supported yet...) */ "CE3-10/100", /* mohawk */ "R2E-100BTX", "PRO/100 M16A", nil, }; static char* mediatable[4] = { "10BASE-T", "10BASE-TFD", "100BASE-TX", "100BASE-TXFD", }; enum { DEBUG = 0, IDEBUG = 0, RXDEBUG = 0, TXDEBUG = 0, MIIDEBUG = 0, }; enum { /* CIS bits (from P9 ethersmc driver) */ TupleFunce = 0x22, TfNodeId = 0x04, }; enum { /* Phys bits */ Phy_Bmcr = 0x00, /* Basic mode control register */ Phy_Bmsr = 0x01, /* Basic mode status register */ Phy_Id1 = 0x02, /* Phy Id 1 */ Phy_Id2 = 0x03, /* Phy Id 2 */ }; enum { /* Attribute memory registers (Xircom pg 28-35) */ Dingo_Cis = 0x0000, /* Start of Card Info Stucture (CIS) tuples */ Dingo_Ecor = 0x0800, /* Ethernet configuration option register */ Dingo_Ecsr = 0x0802, /* Ethernet configuration status register */ Dingo_Ebar0 = 0x080a, /* Ethernet base address register */ Dingo_Ebar1 = 0x080c, Dingo_Dcor0 = 0x0820, /* Configuration options registers */ Dingo_Dcor1 = 0x0822, Dingo_Dcor2 = 0x0824, Dingo_Dcor3 = 0x0826, Dingo_Dcor4 = 0x0828, Dingo_Sfcor = 0x0840, /* 2nd function configuration option register */ }; enum { /* Ecor bits (Xircom pg 28) */ Dingo_Ecor_EthEnable = 0x01, Dingo_Ecor_IobEnable = 0x02, Dingo_Ecor_IntEnable = 0x04, Dingo_Ecor_IrqStschg = 0x20, Dingo_Ecor_IrqLevel = 0x40, Dingo_Ecor_Sreset = 0x80, }; enum { /* Group A (Xircom pg 36) */ Cr = 0x00, /* Command register */ Esr = 0x00, /* Ethernet status register */ Pr = 0x01, /* Page select register */ Edp = 0x04, /* Ethernet data port */ Isr0 = 0x06, /* Ethernet interrupt status register */ Gir = 0x07, /* Global interrupt register */ }; enum {/* Cr bits (Xircom pg 41) */ Cr_DisableIntr = 0x00, Cr_TxPacket = 0x01, Cr_SoftReset = 0x02, Cr_EnableIntr = 0x04, Cr_ForceIntr = 0x08, Cr_ClearFifo = 0x10, Cr_ClearOverrun = 0x20, Cr_RestartTx = 0x40, }; enum {/* Esr bits (Xircom pg 42) */ Esr_RxFull = 0x01, Esr_RxPartial = 0x02, Esr_RxReject = 0x04, Esr_TxPending = 0x08, Esr_BadPolarity = 0x10, Esr_MediaSelect = 0x20, }; enum {/* Isr bits (Xircom pg 44) */ Isr_TxOverflow = 0x01, Isr_TxPacket = 0x02, Isr_MacIntr = 0x04, Isr_RxEarly = 0x10, Isr_RxFull = 0x20, Isr_RxReject = 0x40, Isr_ForceIntr = 0x80, }; enum { /* Gir bits (Xircom pg 45) */ Gir_EthIrq = 0x01, Gir_ErhMask = 0x02, Gir_SfIrq = 0x04, Gir_SfMask = 0x08, }; enum { /* page 0 (Xircom pg 36) */ Tso0 = 0x08, /* Transmit space open */ Tso1 = 0x09, Tso2 = 0x0a, Do0 = 0x0c, /* Data offset register */ Do1 = 0x0d, Rsr = 0x0c, /* Rx status register */ Tpr = 0x0d, /* Tx packets register */ Rbc0 = 0x0e, /* Rx byte count */ Rbc1 = 0x0f, }; enum { /* Do bits (Xircom pg 47) */ Do_Offset = 0x1fff, Do_ChangeOffset = 0x2000, Do_Smem = 0x4000, Do_SkipPacket = 0x8000, }; enum { /* Rsr bits (Xircom pg 48) */ Rsr_PhyPacket = 0x01, Rsr_BcastPacket = 0x02, Rsr_LongPacket = 0x04, Rsr_AddrMatch = 0x08, Rsr_AlignError = 0x10, /* CE2 only */ Rsr_CrcError = 0x20, Rsr_RxOk = 0x80, }; enum { /* page 1 (Xircom pg 36) */ Imr0 = 0x0c, /* Interrupt mask register 0 */ Imr1 = 0x0d, /* Interrupt mask register 1 (CE2 only) */ Ecr = 0x0e, /* Ethernet configuration register */ }; enum { /* Imr bits (Xircom pg 46) */ Imr_TxOverflow = 0x01, Imr_TxPacket = 0x02, Imr_MacIntr = 0x04, Imr_RxEarly = 0x10, Imr_RxFull = 0x20, Imr_RxReject = 0x40, Imr_ForceIntr = 0x80 }; enum { /* Ecr bits (Xircom pg 51) */ Ecr_EarlyTx = 0x01, Ecr_EarlyRx = 0x02, Ecr_FullDuplex = 0x04, Ecr_LongTpCable = 0x08, /* CE2 only */ Ecr_NoPolCol = 0x10, /* CE2 only */ Ecr_NoLinkPulse = 0x20, Ecr_NoAutoTx = 0x40, /* CE2 only */ Ecr_CompatMode = 0x80, /* Map ESR bits 2:0 to RBC bits 15:13 */ }; enum { /* page 2 (Xircom pg 37) */ Rbs0 = 0x08, /* Receive buffer start*/ Rbs1 = 0x09, Led = 0x0a, /* LED control register */ Led3 = 0x0b, /* LED3 control register */ Msr = 0x0c, /* Misc. setup register */ Gp2 = 0x0d, /* General purpose register 2 */ }; enum { /* Led bits (Xircom pg 52) */ Led_Led0Mask = 0x07, Led_Led0Shift = 0x00, Led_Led1Mask = 0x38, Led_Led1Shift = 0x03, Led_Led0Rx = 0x40, Led_Led1Rx = 0x80, }; enum { /* Led3 bits (Xircom pg 53) */ Led3_Mask = 0x07, Led3_Shift = 0x00, Led3_Rx = 0x40, }; enum { /* Msr bits (Xircom pg 55) */ Msr_128K = 0x01, Msr_RbsBit16 = 0x02, Msr_MiiSelect = 0x08, Msr_HashTable = 0x20, }; enum {/* Gp2 bits (Xircom pg 57) */ Gp2_Gp3Out = 0x01, Gp2_Gp4Out = 0x02, Gp2_Gp3Select = 0x04, Gp2_Gp4Select = 0x08, Gp2_Gp3In = 0x10, Gp2_Gp4In = 0x20, }; enum { /* page 3 (Xircom pg 37) */ Tpt0 = 0x0a, /* Transmit packet threshold */ Tpt1 = 0x0b, }; enum { /* page 4 (Xircom pg 37) */ Gp0 = 0x08, /* General purpose register 0 */ Gp1 = 0x09, /* General purpose register 1 */ Bv = 0x0a, /* Bonding version register */ Ees = 0x0b, /* EEPROM control register */ Lma = 0x0c, /* Local memory address (CE2 only) */ Lmd = 0x0e, /* Local memory data (CE2 only) */ }; /* Gp0 bits (Xircom pg 56) */ enum { Gp0_Gp1Out = 0x01, Gp0_Gp2Out = 0x02, Gp0_Gp1Select = 0x04, Gp0_Gp2Select = 0x08, Gp0_Gp1In = 0x10, Gp0_Gp2In = 0x20, }; enum { /* page 5 (Xircom pg 38) */ Crha0 = 0x08, /* Current Rx host address */ Crha1 = 0x09, Rhsa0 = 0x0a, /* Rx host start address */ Rhsa1 = 0x0b, Rnsa0 = 0x0c, /* Rx network start address */ Rnsa1 = 0x0d, Crna0 = 0x0e, /* Current Rx network address */ Crna1 = 0x0f, }; enum { /* page 6 (Xircom pg 38) */ Ctha0 = 0x08, /* Current Tx host address */ Ctha1 = 0x09, Thsa0 = 0x0a, /* Tx host start address */ Thsa1 = 0x0b, Tnsa0 = 0x0c, /* Tx network start address */ Tnsa1 = 0x0d, Ctna0 = 0x0e, /* Current Tx network address */ Ctna1 = 0x0f, }; enum { /* page 8 (Xircom pg 39) */ Thbc0 = 0x08, /* Tx host byte count */ Thbc1 = 0x09, Thps0 = 0x0a, /* Tx host packet size */ Thps1 = 0x0b, Tnbc0 = 0x0c, /* Tx network byte count */ Tnbc1 = 0x0d, Tnps0 = 0x0e, /* Tx network packet size */ Tnps1 = 0x0f, }; enum { /* page 0x10 (Xircom pg 39, 58-59) */ DingoID = 0x08, /* Dingo ID register */ RevID = 0x0a, /* Dingo revision ID */ VendorID = 0x0c, /* Dingo vendor ID */ }; enum { /* page 0x40 (Xircom pg 40) */ Cmd0 = 0x08, /* Command register */ Rst0 = 0x09, /* Receive status register */ Txst0 = 0x0b, /* Transmit status register 0 */ Txst1 = 0x0c, /* Transmit status register 1 */ Rx0Msk = 0x0d, /* Receive status mask register */ Tx0Msk = 0x0e, /* Transmit status 0 mask register */ Tx1Msk = 0x0f, /* Transmit status 1 mask register */ }; enum { /* Cmd0 bits (Xircom pg 68)*/ Cmd0_Tx = 0x01, /* CE2 only */ Cmd0_RxEnable = 0x04, Cmd0_RxDisable = 0x08, Cmd0_Abort = 0x10, /* CE2 only */ Cmd0_Online = 0x20, Cmd0_AckIntr = 0x40, /* CE2 only */ Cmd0_Offline = 0x80, }; enum { /* page 0x42 (Xircom pg 40) */ Swc0 = 0x08, /* Software configuration 0 */ Swc1 = 0x09, /* Software configuration 1 */ Boc = 0x0a, /* Back-off configuration */ Tcd = 0x0b, /* Transmit collision deferral */ }; enum { /* Swc0 bits (Xircom pg 73) */ Swc0_LBEnable = 0x01, Swc0_LBSource = 0x02, Swc0_AcceptError = 0x04, Swc0_AcceprShort = 0x08, Swc0_NoCrc = 0x40, }; enum { /* Swc1 bits (Xircom pg 74) */ Swc1_AddrEnable = 0x01, /* Enable individual address filters */ Swc1_PromiscuousMulti = 0x02, /* Accept all multicast packets */ Swc1_Promiscuous = 0x04, /* Accept all non-multicast packets */ Swc1_BcastDisable = 0x08, /* Reject broadcast packets */ Swc1_MediaSelect = 0x40, /* media select (Mohawk only) */ Swc1_AutoMedia = 0x80, /* Auto media select (Mohawk only) */ }; enum { /* page 50 bits */ Etheraddr_start = 0x08, Etheraddr_end = 0x0d, }; static void attach(Ether *ether) { Ctlr* ctlr; ctlr = ether->ctlr; ilock(ctlr); if (DEBUG) print("xircom: attach\n"); if (ctlr->attached) { iunlock(ctlr); return; } chipenable(ether); mediaselect(ctlr); ctlr->attached = 1; iunlock(ctlr); } static void txstart(Ether* ether) { Ctlr* ctlr; Block *bp; long len, frees; ctlr = ether->ctlr; if (DEBUG) print("xircom: txstart\n"); if (ctlr->txbp) { bp = ctlr->txbp; ctlr->txbp = 0; } else { bp = qget(ether->oq); if (bp == 0) return; } len = BLEN(bp); /* Check transmit buffer space */ PageSelect(ctlr,0); csr16w(ctlr, Tso2, len+2); frees = csr16r(ctlr, Tso0) & 0x7fff; if (frees <= len + 2) print("no buffer space! (%ld)\n",frees); /* Send packet length to card */ csr16w(ctlr, Edp, len); /* Write packet to card */ csr8sw(ctlr, Edp, bp->rp, len); if(TXDEBUG > 1) txreg_dump(ctlr); /* mohawk only */ csr8w(ctlr, Cr, Cr_TxPacket|Cr_EnableIntr); ether->outpackets++; if(TXDEBUG > 1) txreg_dump(ctlr); freeb(bp); } static void receive(Ether* ether) { int len; Ctlr* ctlr; Block *bp; ctlr = ether->ctlr; if (RXDEBUG) print("xircom: receive\n"); PageSelect(ctlr,0); if (csr8r(ctlr, Rsr) & Rsr_RxOk) { len = plength(ether); bp = iallocb(len); if (RXDEBUG > 1) rxreg_dump(ctlr); csr8sr(ctlr, Edp, bp->rp, len); bp->wp = bp->rp + len; etheriq(ether, bp, 1); ether->inpackets++; PageSelect(ctlr,0); csr16w(ctlr, Do0, Do_SkipPacket); delay(1); /* need to wait at least 500ns */ } else { ctlr->rxbad++; } if ( csr8r(ctlr, Rsr) & Rsr_CrcError ) { ether->crcs++; } if ( csr8r(ctlr, Rsr) & Rsr_LongPacket ) { ctlr->longpacket++; } } static void txerror(Ether* ether) { USED(ether); print("xircom: txerror\n"); } static void transmit(Ether* ether) { Ctlr *ctlr; if(TXDEBUG) print("xircom: transmit\n"); ctlr = ether->ctlr; ilock(ctlr); txstart(ether); iunlock(ctlr); } static void interrupt(Ureg*, void *arg) { uint int_status, rx_status, tx_status; Ctlr* ctlr; Ether* ether; ether = arg; ctlr = ether->ctlr; int_status = csr8r(ctlr, Isr0); if (IDEBUG) print("xircom: interrupt %2.2ux\n",int_status); while(int_status) { ctlr->interrupts++; if(ctlr->mohawk) csr8w(ctlr, Cr, Cr_DisableIntr); PageSelect(ctlr,0x40); rx_status = csr8r(ctlr, Rst0); USED(rx_status); //csr8w(ctlr, Rst0, (~rx_status & 0xff)); tx_status = csr8r(ctlr, Txst0); tx_status |= csr8r(ctlr, Txst1) << 8; USED(tx_status); //csr8w(ctrl, Txst0, 0); //csr8w(ctrl, Txst1, 0); /* transmit */ if (int_status & Isr_TxPacket) { if(TXDEBUG) print("transmit intr\n"); } /* receive */ while(csr8r(ctlr, Esr) & Esr_RxFull ) receive(ether); if (int_status & Isr_TxOverflow) { ctlr->txover++; } if (int_status & Isr_RxReject) { ctlr->rxreject++; } if (int_status & Isr_MacIntr) { ctlr->macintr++; } if(ctlr->mohawk) csr8w(ctlr, Cr, Cr_EnableIntr); int_status = csr8r(ctlr, Isr0); } } static void promiscuous(void* arg, int on) { USED(arg); USED(on); print("xircom: promiscuous\n"); } static void multicast(void* arg, uchar *addr, int on) { USED(arg); USED(addr); USED(on); print("xircom: multicast\n"); } static void statistics(Ether* ether) { USED(ether); } static long ifstat(Ether* ether, void* a, long n, ulong offset) { char *p; int len; Ctlr *ctlr; if(n == 0) return 0; ctlr = ether->ctlr; ilock(ctlr); statistics(ether); iunlock(ctlr); p = malloc(READSTR); len = snprint(p, READSTR, "SiliconRev: %d\n", ctlr->srev); len += snprint(p+len, READSTR-len, "Port: %2.2ux\n", ctlr->port); PageSelect(ctlr,4); len += snprint(p+len, READSTR-len, "Bv: %2.2ux\n", csr8r(ctlr, Bv)); PageSelect(ctlr, 0x10); len += snprint(p+len, READSTR-len, "DingoID: %2.2ux\n", csr16r(ctlr, DingoID)); len += snprint(p+len, READSTR-len, "RevID: %2.2ux\n",csr16r(ctlr, RevID)); len += snprint(p+len, READSTR-len, "VendorID: %2.2ux\n",csr8r(ctlr, VendorID)); len += snprint(p+len, READSTR-len, "Speed: %d\n", ctlr->speed); len += snprint(p+len, READSTR-len, "Duplex: %d\n", ctlr->duplex); len += snprint(p+len, READSTR-len, "Interrupts: %uld\n", ctlr->interrupts); len += snprint(p+len, READSTR-len, "TxPackets: %d\n", ether->outpackets); len += snprint(p+len, READSTR-len, "RxPackets: %d\n", ether->inpackets); len += snprint(p+len, READSTR-len, "TxOverrun: %uld\n", ctlr->txover); len += snprint(p+len, READSTR-len, "RxReject: %uld\n", ctlr->rxreject); len += snprint(p+len, READSTR-len, "RxBad : %uld\n", ctlr->rxbad); len += snprint(p+len, READSTR-len, "MacIntr: %uld\n", ctlr->macintr); len += snprint(p+len, READSTR-len, "CrcErrors: %d\n", ether->crcs); len += snprint(p+len, READSTR-len, "RxLongPacket: %uld\n", ctlr->longpacket); len += snprint(p+len, READSTR-len, "RxBufferWrap: %uld\n", ctlr->rxwrap); len += snprint(p+len, READSTR-len, "RxBadLength: %uld\n", ctlr->rxbadlen); len += snprint(p+len, READSTR-len, "RxOkLength: %uld\n", ctlr->rxoklen); USED(len); n = readstr(offset, a, n, p); free(p); return n; } static int reset(Ether* ether) { int port, slot; char *type; Ctlr* ctlr; uchar ea[Eaddrlen]; if (ether->irq == 0) ether->irq = 9; if (ether->port == 0) ether->port = 0x300; ether->mem = 0x0; if (ether->size== 0) ether->size = 0x1000; slot = getslot(ether, &type); ether->ctlr = malloc(sizeof(Ctlr)); ctlr = ether->ctlr; port = ether->port; ctlr->port = ether->port; ctlr->slot = slot; findmodel(ether, type); findspeed(ether); if(ioalloc(port, 0x10, 0, "xircom") < 0) return -1; if(slot < 0){ print(" no slot!\n"); iofree(port); return -1; } if (ctlr == 0) { print(" no ctlr!\n"); iofree(ether->port); pcmspecialclose(slot); } ilock(ctlr); memset(ea, 0, Eaddrlen); if (memcmp(ea, ether->ea, Eaddrlen) == 0) { if (readnodeid(ether) < 0) { print("xircom: cannot find ethernet address\n"); iunlock(ctlr); iofree(port); pcmspecialclose(slot); return -1; } } chipreset(ctlr); /* extra stuff for dingo (someday...) */ if (ctlr->dingo) { config_dingo(ether); } else { config_mohawk(ether); } ctlr->interrupts = 0; ctlr->txover = 0; ctlr->rxreject = 0; ctlr->macintr = 0; ctlr->rxbad= 0; ctlr->longpacket = 0; ctlr->rxwrap = 0; ctlr->rxbadlen = 0; ctlr->rxoklen = 0; ether->inpackets = 0; ether->outpackets = 0; ether->crcs = 0; ether->attach = attach; ether->transmit = transmit; ether->interrupt = interrupt; ether->ifstat = ifstat; ether->promiscuous = promiscuous; ether->multicast = multicast; ether->arg = ether; ether->mbps = ctlr->speed; iunlock(ctlr); return 0; } void etherxircomlink(void) { addethercard("xircom", reset); } /*============== support functions ===============*/ static void config_mohawk(Ether *ether) { Ctlr *ctlr; PCMmap *m; uchar *p; ctlr = ether->ctlr; m = pcmmap(ctlr->slot, Dingo_Ecor, 1 ,1); p = (uchar *) KADDR(m->isa + Dingo_Ecor - m->ca); *p = Dingo_Ecor_IrqLevel | Dingo_Ecor_IntEnable | Dingo_Ecor_IobEnable | Dingo_Ecor_EthEnable; *(p+2) = 0x0; pcmunmap(ctlr->slot, m); } static void config_dingo(Ether *ether) { Ctlr *ctlr; PCMmap *m; uchar *p; ctlr = ether->ctlr; m = pcmmap(ctlr->slot, 0xff80, 1 ,1); p = (uchar *) KADDR(m->isa + 0xff80 - m->ca); print("mio %2.2ux\n",*(p+0x0a)); print("mio2 %2.2ux\n",*(p+0x0c)); *(p+0x0a) = ctlr->port & 0xff; *(p+0x0c) = (ctlr->port >> 8) & 0xff; *p = 0x47; pcmunmap(ctlr->slot, m); m = pcmmap(ctlr->slot, Dingo_Ecor, 1 ,1); p = (uchar *) KADDR(m->isa + Dingo_Ecor - m->ca); *p = Dingo_Ecor_IrqLevel | Dingo_Ecor_IntEnable | Dingo_Ecor_IobEnable | Dingo_Ecor_EthEnable; print("ECOR %2.2ux\n",*p); *(p+2) = 0x0; print("ECSR %2.2ux\n",*(p+2)); print("io %2.2ux\n",*(p+0x0a)); print("io2 %2.2ux\n",*(p+0x0c)); *(p+0x0a) = ctlr->port & 0xff; *(p+0x0c) = (ctlr->port >> 8) & 0xff; print("io %2.2ux\n",*(p+0x0a)); print("io2 %2.2ux\n",*(p+0x0c)); pcmunmap(ctlr->slot, m); m = pcmmap(ctlr->slot, Dingo_Dcor0, 1 ,1); p = (uchar *) KADDR(m->isa + Dingo_Dcor0 - m->ca); *p = 0x01; *(p+2) = 0x0c; *(p+4) = 0x0; *(p+6) = 0x0; *(p+8) = 0x0; pcmunmap(ctlr->slot, m); softreset(ctlr); } /* this is a horrible hack because Rbc1 is sometimes bogus */ static uint plength(Ether* ether) { int len; Ctlr *ctlr; ctlr = ether->ctlr; PageSelect(ctlr, 0); if( csr8r(ctlr, Rbc1) && 0xff) { /* high byte is garbage, try sick workaround */ PageSelect(ctlr, 5); /* use Rnsa0 and Rhsa0 pointers to find length */ len = csr16r(ctlr, Rnsa0) - csr16r(ctlr, Rhsa0) - 3; PageSelect(ctlr, 0); /* ick, try to deal with wraparound */ if ( len < 0) { ctlr->rxwrap++; len = 1024*24+len; /* ugly we set 24k for rx buffer */ if (RXDEBUG) print("bad packet length, trying %d\n",len); } if ( len < 0 || len > 8*1024) { ctlr->rxbadlen++; len = csr8r(ctlr, Rbc0); /* really ugly */ if (RXDEBUG) print("double bad packet length, trying %d\n",len); } } else { ctlr->rxoklen++; len = csr8r(ctlr, Rbc0); len |= (csr8r(ctlr, Rbc1) & 0x1f) << 8; } return(len); } /*============== intr enable/disable functions ===============*/ static void enable_intr(Ctlr* ctlr) { PageSelect(ctlr,1); csr8w(ctlr, Imr0, 0xff); /* Unmask everything */ csr8w(ctlr, Imr1, 0x01); /* Unmask TX underrun detection */ delay(1); PageSelect(ctlr,0); csr8w(ctlr, Cr, Cr_EnableIntr); /* Enable interrupts */ } static void disable_intr(Ctlr* ctlr) { PageSelect(ctlr, 0); csr8w(ctlr, Cr, Cr_DisableIntr); /* disable intrrupts */ PageSelect(ctlr,1); csr8w(ctlr, Imr0, 0); /* forbid interrupts */ csr8w(ctlr, Imr1, 0); PageSelect(ctlr,0); } /*============== chip reset/enable functions ===============*/ static void chipreset(Ctlr* ctlr) { hardreset(ctlr); softreset(ctlr); if (DEBUG) reg_dump(ctlr); } static void hardreset(Ctlr* ctlr) { PageSelect(ctlr, 4); delay(1); csr8w(ctlr, Gp1, 0); /* clear bit 0: power down */ delay(40); csr8w(ctlr, Gp1, 1); /* set bit 0: power up */ delay(20); PageSelect(ctlr, 0); } static void softreset(Ctlr* ctlr) { PageSelect(ctlr, 0); csr8w(ctlr, Cr, Cr_SoftReset); delay(20); csr8w(ctlr, Cr, 0); /* clear reset */ delay(40); PageSelect(ctlr, 4); if (ctlr->mohawk) { /* set GP1 and GP2 as outputs * set GP1 low to power on the ML6692 * set GP2 high to power on the 10Mhz chip */ csr8w(ctlr, Gp0, Gp0_Gp1Select|Gp0_Gp2Select|Gp0_Gp2Out); } delay(500); /* circuits need time to power up */ /* get rev info */ if (ctlr->mohawk) ctlr->srev = (csr8r(ctlr, Bv) & 0x70) >> 4; else ctlr->srev = (csr8r(ctlr, Bv) & 0x30) >> 4; /* turn off 2nd function interrupt */ csr8w(ctlr, Gir, Gir_SfMask); /* disable_intr(ctlr); why? */ PageSelect(ctlr, 0); } /* very simple, no auto detection yet */ static void mediaselect(Ctlr* ctlr) { if (ctlr->speed == 10) { print("media: 10BT "); PageSelect(ctlr, 0x42); csr8w(ctlr, Swc1, Swc1_MediaSelect); //enable 10BT } else if ( ctlr->speed == 100) { print("media: 100BT "); } else { print("bad media"); } delay(40); if (ctlr->duplex) { PageSelect(ctlr, 1); print("full duplex\n"); csr8w(ctlr, Ecr, csr8r(ctlr, Ecr | Ecr_FullDuplex)); delay(40); } else { print("half duplex\n"); } PageSelect(ctlr, 0); } static void setaddrs(Ether* ether) { int i, j = 0; Ctlr *ctlr; ctlr = ether->ctlr; PageSelect(ctlr,0x50); for(i = Etheraddr_end; i >= Etheraddr_start; i--) { csr8w(ctlr,i,ether->ea[j]); j++; } } static void chipenable(Ether* ether) { Ctlr *ctlr; ctlr = ether->ctlr; #ifdef _ONLYTEST_ /* compat mode hack */ PageSelect(ctlr, 1); csr8w(ctlr, Ecr, Ecr_CompatMode); print("Ecr register "); printbits(csr8r(ctlr, Ecr)); print("\n"); #endif PageSelect(ctlr, 0x42); csr8w(ctlr, Swc0, 0x20); /* Disable source insertion */ /* * Set the 'local memory dividing line' -- splits the 32K card memory into * 8K for transmit buffers and 24K for receive. This is done automatically * on newer revision cards. */ if (ctlr->srev != 1) { PageSelect(ctlr, 2); csr16w(ctlr, Rbs0, 0x2000); } setaddrs(ether); /* Fix the data offset register -- reset leaves it off-by-one */ PageSelect(ctlr, 0); csr16w(ctlr, Do0, Do_ChangeOffset); /* Set MAC interrupt masks and clear status regs */ PageSelect(ctlr, 0x40); csr8w(ctlr, Rx0Msk, 0xff); csr8w(ctlr, Tx0Msk, 0xff); if (!(ctlr->dingo)) csr8w(ctlr, Tx1Msk, 0xb0); csr8w(ctlr, Rst0, 0x00); csr8w(ctlr, Txst0, 0x00); csr8w(ctlr, Txst1, 0x00); if(mii_init(ctlr)) { if (MIIDEBUG) print("MII detected\n"); PageSelect(ctlr, 2); csr8w(ctlr, Msr, csr8r(ctlr, Msr) | Msr_MiiSelect); /* use mii */ delay(20); } else { if (MIIDEBUG) print("no MII detected\n"); if (ctlr->mohawk) { print("MII error: trying to continue\n"); PageSelect(ctlr, 2); csr8w(ctlr, Msr, Msr_MiiSelect); /* try to use mii anyhow */ delay(20); //PageSelect(ctlr, 0x42); //csr8w(ctlr,Swc1, Swc1_AutoMedia); /* auto media detect: not working yet */ } else { print("card not supported\n"); } delay(50); } if (ctlr->dingo) { PageSelect(ctlr,2); csr8w(ctlr, Led, 0x3b); csr8w(ctlr, Led3, 0x04); /* dingo 100 Mbit LED */ } /* Enable receiver, put MAC online */ PageSelect(ctlr, 0x40); csr8w(ctlr, Cmd0, Cmd0_RxEnable|Cmd0_Online); enable_intr(ctlr); PageSelect(ctlr, 0); /* dingo modem magic */ if(ctlr->dingo) { if(!(csr8r(ctlr,0x10) & 0x01)) { print("dingo magic\n"); csr8w(ctlr, 0x10, 0x11); } } PageSelect(ctlr, 0); } /*============== PCMCIA/CIS functions ===============*/ static void findmodel(Ether *ether, char *type) { Ctlr* ctlr; ctlr = ether->ctlr; if(cistrcmp(type, "CEM56") == 0){ print("type: dingo, watch out for baby\n"); ctlr->dingo = 1; ctlr->mohawk = 1; } else { print("type: mohawk\n"); ctlr->dingo = 0; ctlr->mohawk = 1; } } static void findspeed(Ether *ether) { Ctlr* ctlr; int i, k, media; char *p; ctlr = ether->ctlr; media = 2; /* 100BT half duplex default */ for(i = 0; i < ether->nopt; i++) { if(cistrncmp(ether->opt[i], "media=", 6) != 0) continue; p = ether->opt[i]+6; for(k = 0; k< nelem(mediatable); k++) { if(cistrcmp(p, mediatable[k]) == 0) media = k; } } print("media type: %d\n",media); switch(media){ default: break; case 0x00: /* 10BASE-T */ ctlr->speed=10; ctlr->duplex=0; break; case 0x01: /* 10BASE-TFD */ ctlr->speed=10; ctlr->duplex=1; break; case 0x02: /* 100BASE-TX */ ctlr->speed=100; ctlr->duplex=0; break; case 0x03: /* 100BASE-TXFD */ ctlr->speed=100; ctlr->duplex=1; break; } } static int getslot(Ether *ether, char **type) { int i, slot; slot = -1; *type = nil; for(i = 0; xircompcmcia[i] != nil; i++){ *type = xircompcmcia[i]; if((slot = pcmspecial(*type, ether)) >= 0) break; } if(xircompcmcia[i] == nil){ for(i = 0; i < ether->nopt; i++){ if(cistrncmp(ether->opt[i], "id=", 3)) continue; *type = ðer->opt[i][3]; if((slot = pcmspecial(*type, ether)) >= 0) break; } } return slot; } /* get MAC addr from CIS */ static int readnodeid(Ether* ether) { Ctlr* ctlr; uchar data[Eaddrlen + 1]; int len; ctlr = ether->ctlr; len = sizeof(data); if (pcmcistuple(ctlr->slot, TupleFunce, 0x4, data, len) != len) return -1; if (data[0] != Eaddrlen) return -1; memmove(ether->ea, &data[1], Eaddrlen); return 0; } /*============== Debugging functions ===============*/ void printbits(uint x) { int i; for(i = 7; i >=0 ; i--) { print("%d",(x>>i) & ~(~0<<1)); } } static void reg_dump(Ctlr *ctlr) { print("port=%2.2ux\n",ctlr->port); PageSelect(ctlr,4); print("Bv=%2.2ux (should be 0x45/0x55) ",csr8r(ctlr, Bv)); printbits(csr8r(ctlr, Bv)); print("\n"); print("page=%2.2ux (should be 4)\n",csr8r(ctlr, Pr)); PageSelect(ctlr, 0x10); print("DingoID=%2.2ux (should be 0x444B)\n",csr16r(ctlr, DingoID)); print("page=%2.2ux (should be 10)\n",csr8r(ctlr, Pr)); print("RevID=%2.2ux (should be 0x0001)\n",csr16r(ctlr, RevID)); print("VendorID=%2.2ux (0 for X1601-2)\n",csr8r(ctlr, VendorID)); } static void txreg_dump(Ctlr *ctlr) { PageSelect(ctlr, 6); print("Ctha=%4.4ux ", csr16r(ctlr, Ctha0)); print("Thsa=%4.4ux ", csr16r(ctlr, Thsa0)); print("Tnsa=%4.4ux ", csr16r(ctlr, Tnsa0)); print("Ctna=%4.4ux\n", csr16r(ctlr, Ctna0)); PageSelect(ctlr, 8); print("Thbc=%4.4ux ", csr16r(ctlr, Thbc0)); print("Thps=%4.4ux ", csr16r(ctlr, Thps0)); print("Tnbc=%4.4ux ", csr16r(ctlr, Tnbc0)); print("Tnps=%4.4ux\n", csr16r(ctlr, Tnps0)); PageSelect(ctlr, 0); } static void rxreg_dump(Ctlr *ctlr) { PageSelect(ctlr, 5); print("Crha=%4.4ux ", csr16r(ctlr, Crha0)); print("Rhsa=%4.4ux ", csr16r(ctlr, Rhsa0)); print("Rnsa=%4.4ux ", csr16r(ctlr, Rnsa0)); print("Crna=%4.4ux ", csr16r(ctlr, Crna0)); print("Rnsa-Rhsa=%d ", csr16r(ctlr, Rnsa0)-csr16r(ctlr, Rhsa0)); print("Rnsa-Crha=%d\n", csr16r(ctlr, Rnsa0)-csr16r(ctlr, Crha0)); PageSelect(ctlr, 0); } /*============== MII Management functions ===============*/ /* * The mmi functions were derived from Werner Koch's xirc2ps driver * for Linux under the following license: * * Copyright (c) 1997, 1998 by Werner Koch (dd9jn) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, and the entire permission notice in its entirety, * including the disclaimer of warranties. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ static void mii_idle(Ctlr *ctlr) { PageSelect(ctlr, 2); csr8w(ctlr, Gp2, 0x04|0); /* drive MDCK low */ delay(1); csr8w(ctlr, Gp2, 0x04|1); /* and drive MDCK high */ delay(1); } static void mii_putbit(Ctlr *ctlr, uint data) { PageSelect(ctlr, 2); if (data) { csr8w(ctlr, Gp2, 0x0c|2|0); /* set MDIO */ delay(1); csr8w(ctlr, Gp2, 0x0c|2|1); /* and drive MDCK high */ delay(1); } else { csr8w(ctlr, Gp2, 0x0c|0|0); /* clear MDIO */ delay(1); csr8w(ctlr, Gp2, 0x0c|0|1); /* and drive MDCK high */ delay(1); } } static int mii_getbit(Ctlr *ctlr) { uint d; PageSelect(ctlr, 2); csr8w(ctlr, Gp2, 4|0); /* drive MDCK low */ delay(1); d = csr8r(ctlr, Gp2); /* read MDIO */ csr8w(ctlr, Gp2, 4|1); /* drive MDCK high again */ delay(1); return d & 0x20; /* read MDIO */ } static void mii_wbits(Ctlr *ctlr, uint data, int len) { uint m = 1 << (len-1); for (; m; m >>= 1) mii_putbit(ctlr, data & m); } static uint mii_rd(Ctlr *ctlr, uchar phyaddr, uchar phyreg) { int i; uint data = 0, m; PageSelect(ctlr, 2); for (i=0; i < 32; i++) /* 32 bit preamble */ mii_putbit(ctlr, 1); mii_wbits(ctlr, 0x06, 4); /* Start and opcode for read */ mii_wbits(ctlr, phyaddr, 5); /* PHY address to be accessed */ mii_wbits(ctlr, phyreg, 5); /* PHY register to read */ mii_idle(ctlr); /* turn around */ mii_getbit(ctlr); for (m = 1<<15; m; m >>= 1) if (mii_getbit(ctlr)) data |= m; mii_idle(ctlr); return data; } static void mii_wr(Ctlr *ctlr, uchar phyaddr, uchar phyreg, uint data, int len) { int i; PageSelect(ctlr, 2); for (i=0; i < 32; i++) /* 32 bit preamble */ mii_putbit(ctlr, 1); mii_wbits(ctlr, 0x05, 4); /* Start and opcode for write */ mii_wbits(ctlr, phyaddr, 5); /* PHY address to be accessed */ mii_wbits(ctlr, phyreg, 5); /* PHY Register to write */ mii_putbit(ctlr, 1); /* turn around */ mii_putbit(ctlr, 0); mii_wbits(ctlr, data, len); /* And write the data */ mii_idle(ctlr); } static int mii_init(Ctlr *ctlr) { uint val = 1; uint new_mii, control, status; status = mii_rd(ctlr, 0, 1); if ((status & 0xff00) != 0x7800) { print("%ux no MII!\n",(status & 0xff00)); val = 0; /* No MII */ } new_mii = (mii_rd(ctlr, 0, 2) != 0xffff); print("new mii %ud\n",new_mii); if (ctlr->speed == 10) { control = 0x0000; /* no auto neg, 10mbs mode */ } else if (ctlr->speed == 100) { control = 0x2000; /* no auto neg, 100mbs mode */ } else { control = 0x0000; print("bad media\n"); } mii_wr(ctlr, 0, 0, control, 16); delay(100); control = mii_rd(ctlr, 0, 0); if (control & 0x0400) { print("can't take PHY out of isolation mode %ux\n",control); val = 0; } PageSelect(ctlr, 0); return val; }