diff ced2a9bc7cc247be72bd6915c9a01a7d2d2710a5 uncommitted --- a/sys/src/9/pc/ether8169.c +++ b/sys/src/9/pc/ether8169.c @@ -18,6 +18,7 @@ #include "../port/netif.h" #include "../port/etherif.h" #include "../port/ethermii.h" +//#include "../pc/rtl8125mcode.h" enum { /* registers */ Idr0 = 0x00, /* MAC address */ @@ -43,6 +44,7 @@ Config3 = 0x54, /* Configuration Register 3 */ Config4 = 0x55, /* Configuration Register 4 */ Config5 = 0x56, /* Configuration Register 5 */ + Tdfnr = 0x57, /* ? */ Timerint = 0x58, /* Timer Interrupt */ Mulint = 0x5C, /* Multiple Interrupt Select */ Phyar = 0x60, /* PHY Access */ @@ -51,15 +53,41 @@ Tbilpar = 0x6A, /* TBI Auto-Negotiation Link Partner */ Phystatus = 0x6C, /* PHY Status */ Pmch = 0x6F, /* power management */ + Ephyar = 0x80, /* EPHY Access */ Ldps = 0x82, /* link down power saving */ + Dllpr = 0xD0, /* ? */ + Twicmd = 0xD2, /* ? */ Rms = 0xDA, /* Receive Packet Maximum Size */ Cplusc = 0xE0, /* C+ Command */ Coal = 0xE2, /* Interrupt Mitigation (Coalesce) */ Rdsar = 0xE4, /* Receive Descriptor Start Address */ Etx = 0xEC, /* Early Transmit Threshold */ + + Misc = 0xF0, /* Miscellaneous */ }; +enum { /* RTL8125 Family registers */ + Intcfg0 = 0x34, + Imr8125 = 0x38, + Isr8125 = 0x3c, + Intcfg1 = 0x7a, + Tppoll8125 = 0x90, + Macocp8125 = 0xb0, + Phyar8125 = 0xb8, + Mcu = 0xd3, + Coal8125 = 0xa00, + Phyocp8125 = 0xa40, + Misc8125 = 0xf2, + Rssctl = 0x4500, + Qnumctl = 0x4800, + + Timerint0 = 0x0058, + Timerint1 = 0x005c, + Timerint2 = 0x008c, + Timerint3 = 0x00f4, +}; + enum { /* Dtccr */ Reset = 0x00000001, /* Reset */ Dump = 0x00000008, /* Dump */ @@ -99,8 +127,9 @@ Lbk0 = 0x00020000, /* Loopback Test 0 */ Lbk1 = 0x00040000, /* Loopback Test 1 */ Ifg2 = 0x00080000, /* Interframe Gap 2 */ - HwveridSHIFT = 23, /* Hardware Version ID */ - HwveridMASK = 0x7C800000, + + Hwveridmask1 = 0x7CF00000, + Hwveridmask2 = 0x7C800000, Macv01 = 0x00000000, /* RTL8169 */ Macv02 = 0x00800000, /* RTL8169S/8110S */ Macv03 = 0x04000000, /* RTL8169S/8110S */ @@ -114,7 +143,6 @@ Macv13 = 0x34000000, /* RTL8101E */ Macv14 = 0x30800000, /* RTL8100E */ Macv15 = 0x38800000, /* RTL8100E */ -// Macv19 = 0x3c000000, /* dup Macv12a: RTL8111c-gr */ Macv25 = 0x28000000, /* RTL8168D */ Macv26 = 0x48000000, /* RTL8111/8168B */ Macv27 = 0x2c800000, /* RTL8111e */ @@ -128,7 +156,23 @@ Macv44 = 0x5c800000, /* RTL8411B */ Macv45 = 0x54000000, /* RTL8111HN/8168H */ Macv51 = 0x50000000, /* RTL8168EP */ + Macv61 = 0x60900000, /* RTL8125A (untested) */ + Macv63 = 0x64100000, /* RTL8125B */ + Macv64 = 0x68800000, /* RTL8125D */ + Macv65 = 0x68900000, /* RTL8125D (untested) */ + Macv66 = 0x68100000, /* RTL8125BP (untested) */ + Macv70 = 0x64900000, /* RTL8126A (untested) */ + Macv71 = 0x64a00000, /* RTL8126A (untested) */ + /* Microcode versions */ + Mcuv61 = 0x0b33, + Mcuv63 = 0x0b99, + Mcuv64 = 0x0027, + Mcuv65 = 0x0027, + Mcuv66 = 0x0001, +// Mcuv70 = 0, +// Mcuv71 = 0, + Ifg0 = 0x01000000, /* Interframe Gap 0 */ Ifg1 = 0x02000000, /* Interframe Gap 1 */ }; @@ -145,11 +189,13 @@ MrxdmaMASK = 0x00000700, Mrxdmaunlimited = 0x00000700, RxfthSHIFT = 13, /* Receive Buffer Length */ + Rxpso = 0x00000800, /* RX Pause Slot On */ RxfthMASK = 0x0000E000, Rxfth256 = 0x00008000, Rxfthnone = 0x0000E000, Rer8 = 0x00010000, /* Accept Error Packets > 8 bytes */ MulERINT = 0x01000000, /* Multiple Early Interrupt Select */ + Rx8125fetchdflt = 0x40000000 /* 8125-specific */ }; enum { /* Cr9346 */ @@ -159,6 +205,7 @@ Eecs = 0x08, /* */ Eem0 = 0x40, /* Operating Mode */ Eem1 = 0x80, + Eewc = 0xc0, /* write config */ }; enum { /* Phyar */ @@ -172,14 +219,21 @@ enum { /* Phystatus */ Fd = 0x01, /* Full Duplex */ Linksts = 0x02, /* Link Status */ - Speed10 = 0x04, /* */ - Speed100 = 0x08, /* */ - Speed1000 = 0x10, /* */ - Rxflow = 0x20, /* */ - Txflow = 0x40, /* */ - Entbi = 0x80, /* */ + Speed10 = 0x04, + Speed100 = 0x08, + Speed1000 = 0x10, + Rxflow = 0x20, + Txflow = 0x40, + Entbi = 0x80, + Speed2500 = 0x0400, /* 8125 family only (2-byte accessing) */ + Speed5000 = 0x1000, + }; +enum { /* Mcu */ + Ooben = 0x80 +}; + enum { /* Cplusc */ Txenb = 0x0001, /* enable C+ transmit mode */ Rxenb = 0x0002, /* enable C+ receive mode */ @@ -191,6 +245,17 @@ Endian = 0x0200, /* Endian Mode */ }; +enum { /* Misc/Misc8125 */ + Rxdven = 0x00080000, /* RXDV gate enable */ + Rxdven8125 = 0x00000008 +}; + +enum { + Clkreqen = 0x80, /* Config2 */ + + Pmestatus = 0x01 /* Config5 */ +}; + typedef struct D D; /* Transmit/Receive Descriptor */ struct D { u32int control; @@ -264,6 +329,7 @@ Rtl8100e = (0x8136<<16)|0x10EC, /* RTL810[01]E: pci -e */ Rtl8169c = (0x0116<<16)|0x16EC, /* RTL8169C+ (USR997902) */ Rtl8111b = (0x8161<<16)|0x10EC, /* RTL8111/8168/8411: pci-e */ + Rtl8125 = (0x8125<<16)|0x10EC, /* /RTL8125[BD]?/ */ Rtl8169sc = (0x8167<<16)|0x10EC, /* RTL8169SC */ Rtl8168b = (0x8168<<16)|0x10EC, /* RTL8168B: pci-e */ Rtl8169 = (0x8169<<16)|0x10EC, /* RTL8169 */ @@ -286,6 +352,7 @@ int macv; /* MAC version */ int phyv; /* PHY version */ int pcie; /* flag: pci-express device? */ + int is8125; /* flag: uses RTL8125 family registers? */ uvlong mchash; /* multicast hash */ @@ -339,22 +406,56 @@ #define csr16w(c, r, w) (outs((c)->port+(r), (u16int)(w))) #define csr32w(c, r, l) (outl((c)->port+(r), (u32int)(l))) +static uint +rtl8125macr(Ctlr *ctlr, int r) +{ + csr32w(ctlr, Macocp8125, r << 15); + return csr32r(ctlr, Macocp8125); +} + +static void +rtl8125macw(Ctlr *ctlr, int r, int v) +{ + csr32w(ctlr, Macocp8125, Flag | (r << 15) | v); +} + +static uint +rtl8169miireg(Ctlr *ctlr, int ra, int *phy, int *data) +{ + uint r; + + if(ctlr->is8125){ + *phy = Phyar8125; + r = ((Phyocp8125 + ra/8) << 4) + ((ra % 8) << 1); + r = (r >> 1) << 16; + if(data) + r |= Flag | *data; + }else{ + *phy = Phyar; + r = (ra << 16) & RegaddrMASK; + if(data) + r |= Flag | ((*data<ctlr; - r = (ra<<16) & RegaddrMASK; - csr32w(ctlr, Phyar, r); + ctlr = mii->ctlr; + r = rtl8169miireg(ctlr, ra, &phy, nil); + csr32w(ctlr, phy, r); delay(1); for(timeo = 0; timeo < 2000; timeo++){ - if((r = csr32r(ctlr, Phyar)) & Flag) + if((r = csr32r(ctlr, phy)) & Flag) break; microdelay(100); } @@ -361,31 +462,34 @@ if(!(r & Flag)) return -1; - return (r & DataMASK)>>DataSHIFT; + r &= DataMASK; + if(!ctlr->is8125) + r >>= DataSHIFT; + return r; } static int -rtl8169miimiw(Mii* mii, int pa, int ra, int data) +rtl8169miimiw(Mii *mii, int pa, int ra, int data) { + Ctlr *ctlr; uint r; int timeo; - Ctlr *ctlr; + int phy; if(pa != 1) return -1; - ctlr = mii->ctlr; - r = Flag|((ra<<16) & RegaddrMASK)|((data<ctlr; + r = rtl8169miireg(ctlr, ra, &phy, &data); + csr32w(ctlr, phy, r); delay(1); for(timeo = 0; timeo < 2000; timeo++){ - if(!((r = csr32r(ctlr, Phyar)) & Flag)) + if(!((r = csr32r(ctlr, phy)) & Flag)) break; microdelay(100); } if(r & Flag) return -1; - return 0; } @@ -411,7 +515,14 @@ case Macv28: case Macv29: case Macv30: - csr8w(ctlr, Pmch, csr8r(ctlr, Pmch) | 0x80); + case Macv61: + case Macv63: + case Macv64: + case Macv65: + case Macv66: + case Macv70: + case Macv71: + csr8w(ctlr, Pmch, csr8r(ctlr, Pmch) | 0xc0); break; } rtl8169miimiw(ctlr->mii, 1, 0x1f, 0); @@ -427,9 +538,8 @@ rtl8169miimiw(ctlr->mii, 1, 0x0B, 0x0000); /* magic */ } - if(mii(ctlr->mii, (1<<1)) == 0 || (phy = ctlr->mii->curphy) == nil){ + if(mii(ctlr->mii, (1<<1)) == 0 || (phy = ctlr->mii->curphy) == nil) error("no phy"); - } print("#l%d: rtl8169: oui %#ux phyno %d, macv = %#8.8ux phyv = %#4.4ux\n", edev->ctlrno, phy->oui, phy->phyno, ctlr->macv, ctlr->phyv); @@ -445,7 +555,7 @@ rtl8169promiscuous(void* arg, int on) { Ether *edev; - Ctlr * ctlr; + Ctlr *ctlr; edev = arg; ctlr = edev->ctlr; @@ -471,13 +581,13 @@ ulong c, crc, carry; crc = ~0UL; - for (i = 0; i < len; i++) { + for(i = 0; i < len; i++){ c = addr[i]; - for (j = 0; j < 8; j++) { + for(j = 0; j < 8; j++){ carry = ((crc & (1UL << 31))? 1: 0) ^ (c & 1); crc <<= 1; c >>= 1; - if (carry) + if(carry) crc = (crc ^ Etherpolybe) | carry; } } @@ -497,7 +607,7 @@ Ether *edev; Ctlr *ctlr; - if (!add) + if(!add) return; /* ok to keep receiving on old mcast addrs */ edev = ether; @@ -510,10 +620,10 @@ csr32w(ctlr, Rcr, ctlr->rcr); /* pci-e variants reverse the order of the hash byte registers */ - if (ctlr->pcie) { + if(ctlr->pcie){ csr32w(ctlr, Mar0, swabl(ctlr->mchash>>32)); csr32w(ctlr, Mar0+4, swabl(ctlr->mchash)); - } else { + }else{ csr32w(ctlr, Mar0, ctlr->mchash); csr32w(ctlr, Mar0+4, ctlr->mchash>>32); } @@ -609,13 +719,22 @@ } static void -rtl8169halt(Ctlr* ctlr) +rtl8169setimr(Ctlr *ctlr, int m) { - csr8w(ctlr, Cr, 0); + ctlr->imr = m; + if(ctlr->is8125) + csr32w(ctlr, Imr8125, m); + else + csr16w(ctlr, Imr, m); +} - ctlr->imr = 0; - csr16w(ctlr, Imr, 0); - csr16w(ctlr, Isr, ~0); +static void +rtl8169setisr(Ctlr *ctlr, int s) +{ + if(ctlr->is8125) + csr32w(ctlr, Isr8125, s); + else + csr16w(ctlr, Isr, s); } static int @@ -624,6 +743,24 @@ u32int r; int timeo; + if(ctlr->is8125){ + csr8w(ctlr, Misc8125, csr8r(ctlr, Misc8125) | Rxdven8125); + delay(2); + + for(timeo = 0; timeo < 3000; timeo++){ + microdelay(50); + if((csr8r(ctlr, Mcu) & (0x10|0x20)) == (0x10|0x20)) + break; + } + if(ctlr->macv == Macv63) + for(timeo = 0; timeo < 3000; timeo++){ + microdelay(50); + if((csr16r(ctlr, Coal) & 0x0103) == 0x0103) + break; + } + delay(2); + } + /* * Soft reset the controller. */ @@ -634,8 +771,6 @@ break; delay(1); } - rtl8169halt(ctlr); - if(r & Rst) return -1; return 0; @@ -642,6 +777,28 @@ } static void +rtl8169halt(Ctlr* ctlr) +{ + uint r; + + r = csr32r(ctlr, Rcr); + r &= ~(Aap|Apm|Am|Ab|Ar|Aer); + csr32w(ctlr, Rcr, r); + + rtl8169setimr(ctlr, 0); + rtl8169setisr(ctlr, ~0); + if(ctlr->is8125){ + if(ctlr->macv == Macv70 || ctlr->macv == Macv71) + csr8w(ctlr, Intcfg0, csr8r(ctlr, Intcfg0) & ~0x01); + csr32w(ctlr, Timerint0, 0); + csr32w(ctlr, Timerint1, 0); + csr32w(ctlr, Timerint2, 0); + csr32w(ctlr, Timerint3, 0); + } + rtl8169reset(ctlr); +} + +static void rtl8169replenish(Ctlr* ctlr) { D *d; @@ -667,23 +824,15 @@ } } +/* + * Initialize Rx & Tx descriptors + */ static void -rtl8169init(Ether* edev) +rtl8169trdinit(Ctlr *ctlr) { - int i, timeo; - u32int r; Block *bp; - Ctlr *ctlr; - u64int pa; - u16int cplusc; + int i; - ctlr = edev->ctlr; - ilock(ctlr); - if(rtl8169reset(ctlr) < 0){ - iunlock(ctlr); - error("reset failed"); - } - memset(ctlr->td, 0, sizeof(D)*ctlr->ntd); ctlr->tdh = ctlr->tdt = ctlr->ntq = 0; ctlr->td[ctlr->ntd-1].control = Eor; @@ -692,7 +841,6 @@ ctlr->tb[i] = nil; freeb(bp); } - memset(ctlr->rd, 0, sizeof(D)*ctlr->nrd); ctlr->rdh = ctlr->rdt = ctlr->nrq = 0; ctlr->rd[ctlr->nrd-1].control = Eor; @@ -706,11 +854,422 @@ growbp(&ctlr->pool, ctlr->nrd*4); } rtl8169replenish(ctlr); +} - cplusc = csr16r(ctlr, Cplusc); - cplusc &= ~(Endian|Rxchksum); - cplusc |= Txenb|Mulrw; +static int +rtl8125phyw(Ctlr *ctlr, int r, int v) +{ + int i; + + r = Flag | (r >> 1) << 16 | v; + csr32w(ctlr, Phyar8125, r); + for (i = 0; i < 100; i++){ + delay(1); + if(!(csr32r(ctlr, Phyar8125) & Flag)) + break; + } + if(csr32r(ctlr, Phyar8125) & Flag) + return -1; + return 0; +} + +static int +rtl8125phyr(Ctlr *ctlr, int r) +{ + int i; + + r = (r >> 1) << 16; + csr32w(ctlr, Phyar8125, r); + for (i = 0; i < 100; i++) { + delay(1); + r = csr32r(ctlr, Phyar8125); + if(r & Flag) + break; + } + if(!(r & Flag)) + return -1; + return r & 0xffff; +} + +static void +rtl8125patchmcu(Ctlr *ctlr, int p) +{ + uint r; + + if(ctlr->macv == Macv70 || ctlr->macv == Macv71) + return; + + r = rtl8125phyr(ctlr, 0xb820); + if(p) + r |= 0x0010; + else + r &= ~0x0010; + rtl8125phyw(ctlr, 0xb820, r); +} + +static void +rtl8125phywait(Ctlr *ctlr, int state) +{ + int timeo; + + for(timeo = 0; timeo < 100; timeo++){ + if((rtl8125phyr(ctlr, 0xa420) & 7) == state) + break; + microdelay(1); + } +} + +static void +rtl8125fifowait(Ctlr *ctlr) +{ + int timeo; + + for(timeo = 0; timeo < 10; timeo++){ + microdelay(100); + if(csr16r(ctlr, Twicmd) & 0x0200) + break; + } +} + +/* + * Temporary + */ +static struct { + ushort r; + ushort v; +} macv61bps[] = { + { 0xf800, 0xe008 }, { 0xf802, 0xe01e }, { 0xf804, 0xe02e }, + { 0xf806, 0xe054 }, { 0xf808, 0xe057 }, { 0xf80a, 0xe059 }, + { 0xf80c, 0xe0c2 }, { 0xf80e, 0xe0cb }, { 0xf810, 0x9996 }, + { 0xf812, 0x49d1 }, { 0xf814, 0xf005 }, { 0xf816, 0x49d4 }, + { 0xf818, 0xf10a }, { 0xf81a, 0x49d8 }, { 0xf81c, 0xf108 }, + { 0xf81e, 0xc00f }, { 0xf820, 0x7100 }, { 0xf822, 0x209c }, + { 0xf824, 0x249c }, { 0xf826, 0xc009 }, { 0xf828, 0x9900 }, + { 0xf82a, 0xe004 }, { 0xf82c, 0xc006 }, { 0xf82e, 0x1900 }, + { 0xf830, 0x9900 }, { 0xf832, 0xc602 }, { 0xf834, 0xbe00 }, + { 0xf836, 0x5a48 }, { 0xf838, 0xe0c2 }, { 0xf83a, 0x0004 }, + { 0xf83c, 0xe10a }, { 0xf83e, 0xc60f }, { 0xf840, 0x73c4 }, + { 0xf842, 0x49b3 }, { 0xf844, 0xf106 }, { 0xf846, 0x73c2 }, + { 0xf848, 0xc608 }, { 0xf84a, 0xb406 }, { 0xf84c, 0xc609 }, + { 0xf84e, 0xff80 }, { 0xf850, 0xc605 }, { 0xf852, 0xb406 }, + { 0xf854, 0xc605 }, { 0xf856, 0xff80 }, { 0xf858, 0x0544 }, + { 0xf85a, 0x0568 }, { 0xf85c, 0xe906 }, { 0xf85e, 0xcde8 }, + { 0xf860, 0xc724 }, { 0xf862, 0xc624 }, { 0xf864, 0x9ee2 }, + { 0xf866, 0x1e01 }, { 0xf868, 0x9ee0 }, { 0xf86a, 0x76e0 }, + { 0xf86c, 0x49e0 }, { 0xf86e, 0xf1fe }, { 0xf870, 0x76e6 }, + { 0xf872, 0x486d }, { 0xf874, 0x4868 }, { 0xf876, 0x9ee4 }, + { 0xf878, 0x1e03 }, { 0xf87a, 0x9ee0 }, { 0xf87c, 0x76e0 }, + { 0xf87e, 0x49e0 }, { 0xf880, 0xf1fe }, { 0xf882, 0xc615 }, + { 0xf884, 0x9ee2 }, { 0xf886, 0x1e01 }, { 0xf888, 0x9ee0 }, + { 0xf88a, 0x76e0 }, { 0xf88c, 0x49e0 }, { 0xf88e, 0xf1fe }, + { 0xf890, 0x76e6 }, { 0xf892, 0x486f }, { 0xf894, 0x9ee4 }, + { 0xf896, 0x1e03 }, { 0xf898, 0x9ee0 }, { 0xf89a, 0x76e0 }, + { 0xf89c, 0x49e0 }, { 0xf89e, 0xf1fe }, { 0xf8a0, 0x7196 }, + { 0xf8a2, 0xc702 }, { 0xf8a4, 0xbf00 }, { 0xf8a6, 0x5a44 }, + { 0xf8a8, 0xeb0e }, { 0xf8aa, 0x0070 }, { 0xf8ac, 0x00c3 }, + { 0xf8ae, 0x1bc0 }, { 0xf8b0, 0xc602 }, { 0xf8b2, 0xbe00 }, + { 0xf8b4, 0x0e26 }, { 0xf8b6, 0xc602 }, { 0xf8b8, 0xbe00 }, + { 0xf8ba, 0x0eba }, { 0xf8bc, 0x1501 }, { 0xf8be, 0xf02a }, + { 0xf8c0, 0x1500 }, { 0xf8c2, 0xf15d }, { 0xf8c4, 0xc661 }, + { 0xf8c6, 0x75c8 }, { 0xf8c8, 0x49d5 }, { 0xf8ca, 0xf00a }, + { 0xf8cc, 0x49d6 }, { 0xf8ce, 0xf008 }, { 0xf8d0, 0x49d7 }, + { 0xf8d2, 0xf006 }, { 0xf8d4, 0x49d8 }, { 0xf8d6, 0xf004 }, + { 0xf8d8, 0x75d2 }, { 0xf8da, 0x49d9 }, { 0xf8dc, 0xf150 }, + { 0xf8de, 0xc553 }, { 0xf8e0, 0x77a0 }, { 0xf8e2, 0x75c8 }, + { 0xf8e4, 0x4855 }, { 0xf8e6, 0x4856 }, { 0xf8e8, 0x4857 }, + { 0xf8ea, 0x4858 }, { 0xf8ec, 0x48da }, { 0xf8ee, 0x48db }, + { 0xf8f0, 0x49fe }, { 0xf8f2, 0xf002 }, { 0xf8f4, 0x485a }, + { 0xf8f6, 0x49ff }, { 0xf8f8, 0xf002 }, { 0xf8fa, 0x485b }, + { 0xf8fc, 0x9dc8 }, { 0xf8fe, 0x75d2 }, { 0xf900, 0x4859 }, + { 0xf902, 0x9dd2 }, { 0xf904, 0xc643 }, { 0xf906, 0x75c0 }, + { 0xf908, 0x49d4 }, { 0xf90a, 0xf033 }, { 0xf90c, 0x49d0 }, + { 0xf90e, 0xf137 }, { 0xf910, 0xe030 }, { 0xf912, 0xc63a }, + { 0xf914, 0x75c8 }, { 0xf916, 0x49d5 }, { 0xf918, 0xf00e }, + { 0xf91a, 0x49d6 }, { 0xf91c, 0xf00c }, { 0xf91e, 0x49d7 }, + { 0xf920, 0xf00a }, { 0xf922, 0x49d8 }, { 0xf924, 0xf008 }, + { 0xf926, 0x75d2 }, { 0xf928, 0x49d9 }, { 0xf92a, 0xf005 }, + { 0xf92c, 0xc62e }, { 0xf92e, 0x75c0 }, { 0xf930, 0x49d7 }, + { 0xf932, 0xf125 }, { 0xf934, 0xc528 }, { 0xf936, 0x77a0 }, + { 0xf938, 0xc627 }, { 0xf93a, 0x75c8 }, { 0xf93c, 0x4855 }, + { 0xf93e, 0x4856 }, { 0xf940, 0x4857 }, { 0xf942, 0x4858 }, + { 0xf944, 0x48da }, { 0xf946, 0x48db }, { 0xf948, 0x49fe }, + { 0xf94a, 0xf002 }, { 0xf94c, 0x485a }, { 0xf94e, 0x49ff }, + { 0xf950, 0xf002 }, { 0xf952, 0x485b }, { 0xf954, 0x9dc8 }, + { 0xf956, 0x75d2 }, { 0xf958, 0x4859 }, { 0xf95a, 0x9dd2 }, + { 0xf95c, 0xc616 }, { 0xf95e, 0x75c0 }, { 0xf960, 0x4857 }, + { 0xf962, 0x9dc0 }, { 0xf964, 0xc613 }, { 0xf966, 0x75c0 }, + { 0xf968, 0x49da }, { 0xf96a, 0xf003 }, { 0xf96c, 0x49d0 }, + { 0xf96e, 0xf107 }, { 0xf970, 0xc60b }, { 0xf972, 0xc50e }, + { 0xf974, 0x48d9 }, { 0xf976, 0x9dc0 }, { 0xf978, 0x4859 }, + { 0xf97a, 0x9dc0 }, { 0xf97c, 0xc608 }, { 0xf97e, 0xc702 }, + { 0xf980, 0xbf00 }, { 0xf982, 0x3ae0 }, { 0xf984, 0xe860 }, + { 0xf986, 0xb400 }, { 0xf988, 0xb5d4 }, { 0xf98a, 0xe908 }, + { 0xf98c, 0xe86c }, { 0xf98e, 0x1200 }, { 0xf990, 0xc409 }, + { 0xf992, 0x6780 }, { 0xf994, 0x48f1 }, { 0xf996, 0x8f80 }, + { 0xf998, 0xc404 }, { 0xf99a, 0xc602 }, { 0xf99c, 0xbe00 }, + { 0xf99e, 0x10aa }, { 0xf9a0, 0xc010 }, { 0xf9a2, 0xea7c }, + { 0xf9a4, 0xc602 }, { 0xf9a6, 0xbe00 }, { 0xf9a8, 0x0000 }, + { 0xfc26, 0x8000 }, { 0xfc2a, 0x0540 }, { 0xfc2e, 0x0e24 }, + { 0xfc30, 0x0eb8 }, { 0xfc32, 0x3a5c }, { 0xfc34, 0x10a8 }, + { 0xfc48, 0x007a } +}, macv63bps[] = { + { 0xf800, 0xe008 }, { 0xf802, 0xe013 }, { 0xf804, 0xe01e }, + { 0xf806, 0xe02f }, { 0xf808, 0xe035 }, { 0xf80a, 0xe04f }, + { 0xf80c, 0xe053 }, { 0xf80e, 0xe055 }, { 0xf810, 0x740a }, + { 0xf812, 0x4846 }, { 0xf814, 0x4847 }, { 0xf816, 0x9c0a }, + { 0xf818, 0xc607 }, { 0xf81a, 0x74c0 }, { 0xf81c, 0x48c6 }, + { 0xf81e, 0x9cc0 }, { 0xf820, 0xc602 }, { 0xf822, 0xbe00 }, + { 0xf824, 0x13f0 }, { 0xf826, 0xe054 }, { 0xf828, 0x72ca }, + { 0xf82a, 0x4826 }, { 0xf82c, 0x4827 }, { 0xf82e, 0x9aca }, + { 0xf830, 0xc607 }, { 0xf832, 0x72c0 }, { 0xf834, 0x48a6 }, + { 0xf836, 0x9ac0 }, { 0xf838, 0xc602 }, { 0xf83a, 0xbe00 }, + { 0xf83c, 0x081c }, { 0xf83e, 0xe054 }, { 0xf840, 0xc60f }, + { 0xf842, 0x74c4 }, { 0xf844, 0x49cc }, { 0xf846, 0xf109 }, + { 0xf848, 0xc60c }, { 0xf84a, 0x74ca }, { 0xf84c, 0x48c7 }, + { 0xf84e, 0x9cca }, { 0xf850, 0xc609 }, { 0xf852, 0x74c0 }, + { 0xf854, 0x4846 }, { 0xf856, 0x9cc0 }, { 0xf858, 0xc602 }, + { 0xf85a, 0xbe00 }, { 0xf85c, 0x2494 }, { 0xf85e, 0xe092 }, + { 0xf860, 0xe0c0 }, { 0xf862, 0xe054 }, { 0xf864, 0x7420 }, + { 0xf866, 0x48c0 }, { 0xf868, 0x9c20 }, { 0xf86a, 0x7444 }, + { 0xf86c, 0xc602 }, { 0xf86e, 0xbe00 }, { 0xf870, 0x12dc }, + { 0xf872, 0x733a }, { 0xf874, 0x21b5 }, { 0xf876, 0x25bc }, + { 0xf878, 0x1304 }, { 0xf87a, 0xf111 }, { 0xf87c, 0x1b12 }, + { 0xf87e, 0x1d2a }, { 0xf880, 0x3168 }, { 0xf882, 0x3ada }, + { 0xf884, 0x31ab }, { 0xf886, 0x1a00 }, { 0xf888, 0x9ac0 }, + { 0xf88a, 0x1300 }, { 0xf88c, 0xf1fb }, { 0xf88e, 0x7620 }, + { 0xf890, 0x236e }, { 0xf892, 0x276f }, { 0xf894, 0x1a3c }, + { 0xf896, 0x22a1 }, { 0xf898, 0x41b5 }, { 0xf89a, 0x9ee2 }, + { 0xf89c, 0x76e4 }, { 0xf89e, 0x486f }, { 0xf8a0, 0x9ee4 }, + { 0xf8a2, 0xc602 }, { 0xf8a4, 0xbe00 }, { 0xf8a6, 0x4a26 }, + { 0xf8a8, 0x733a }, { 0xf8aa, 0x49bb }, { 0xf8ac, 0xc602 }, + { 0xf8ae, 0xbe00 }, { 0xf8b0, 0x47a2 }, { 0xf8b2, 0xc602 }, + { 0xf8b4, 0xbe00 }, { 0xf8b6, 0x0000 }, { 0xf8b8, 0xc602 }, + { 0xf8ba, 0xbe00 }, { 0xf8bc, 0x0000 }, { 0xfc26, 0x8000 }, + { 0xfc28, 0x13e6 }, { 0xfc2a, 0x0812 }, { 0xfc2c, 0x248c }, + { 0xfc2e, 0x12da }, { 0xfc30, 0x4a20 }, { 0xfc32, 0x47a0 }, + { 0xfc48, 0x003f } +}; + +static void +rtl8125hwinit(Ctlr *ctlr) +{ + int i; + + csr32w(ctlr, Rcr, csr32r(ctlr, Rcr) & ~(Aap|Apm|Am|Ab|Ar|Aer)); + + /* disable RealWoW support */ + rtl8125macw(ctlr, 0xc0bc, 0x00ff); + + rtl8169reset(ctlr); + + /* disable oob */ + csr8w(ctlr, Mcu, csr8r(ctlr, Mcu) & ~Ooben); + + rtl8125macw(ctlr, 0xe8de, rtl8125macr(ctlr, 0xe8de) & ~0x4000); + + rtl8125fifowait(ctlr); + rtl8125macw(ctlr, 0xc0aa, 0x07d0); + rtl8125macw(ctlr, 0xc0a6, 0x01b5); + rtl8125macw(ctlr, 0xc01e, 0x5555); + rtl8125fifowait(ctlr); + + if(rtl8125macr(ctlr, 0xd42c) & 0x0100){ + rtl8125phywait(ctlr, 2); + rtl8125macw(ctlr, 0xd408, rtl8125macr(ctlr, 0xd408) & ~0x0100); + if(ctlr->macv == Macv63) + rtl8125phyw(ctlr, 0xa466, rtl8125phyr(ctlr, 0xa466) & ~0x0001); + rtl8125phyw(ctlr, 0xa468, rtl8125phyr(ctlr, 0xa468) & ~0x000a); + } + + csr8w(ctlr, Cr9346, csr8r(ctlr, Cr9346) | Eewc); + csr8w(ctlr, Config5, csr8r(ctlr, Config5) & ~Pmestatus); + if(ctlr->macv == Macv70 || ctlr->macv == Macv71) + csr8w(ctlr, Intcfg0, csr8r(ctlr, Intcfg0) & ~0x08); + else + csr8w(ctlr, Config2, csr8r(ctlr, Config2) & ~Clkreqen); + csr8w(ctlr, Cr9346, csr8r(ctlr, Cr9346) & ~Eewc); + + csr8w(ctlr, 0xf1, csr8r(ctlr, 0xf1) & ~0x80); + rtl8125macw(ctlr, 0xd40a, rtl8125macr(ctlr, 0xd40a) & ~0x0010); + rtl8125macw(ctlr, 0xfc38, 0); + for(i = 0xfc28; i < 0xfc38; i += 2) + rtl8125macw(ctlr, i, 0); + delay(3); + rtl8125macw(ctlr, 0xfc26, 0); + + /* + * Only these two have bps(?) configurations. + */ + if(ctlr->macv == Macv61) + for(i = 0; i < nelem(macv61bps); i++) + rtl8125macw(ctlr, macv61bps[i].r, macv61bps[i].v); + else if(ctlr->macv == Macv63) + for(i = 0; i < nelem(macv63bps); i++) + rtl8125macw(ctlr, macv63bps[i].r, macv63bps[i].v); + + /* Disable phy power saving */ + if(ctlr->macv != Macv70 && ctlr->macv != Macv71) + if(rtl8125phyr(ctlr, 0xc416) != 0x0500){ + rtl8125patchmcu(ctlr, 1); + rtl8125phyw(ctlr, 0xc416, 0); + rtl8125phyw(ctlr, 0xc416, 0x0500); + rtl8125patchmcu(ctlr, 0); + } +} + +/* + * Additional RTL8125 family specific initialization + */ +static void +rtl8125init(Ctlr *ctlr) +{ + int timeo; + int v; + uint r; + + csr8w(ctlr, Cr9346, csr8r(ctlr, Cr9346) | Eewc); + + csr8w(ctlr, 0xf1, csr8r(ctlr, 0xf1) & ~0x80); + if(ctlr->macv == Macv70 || ctlr->macv == Macv71) + csr8w(ctlr, Intcfg0, csr8r(ctlr, Intcfg0) & ~0x08); + else + csr8w(ctlr, Config2, csr8r(ctlr, Config2) & ~Clkreqen); + csr8w(ctlr, Config5, csr8r(ctlr, Config5) & ~Pmestatus); + + if(ctlr->macv == Macv70 || ctlr->macv == Macv71) + csr8w(ctlr, Config3, csr8r(ctlr, Config3) & ~0x02); + + /* Disable interrupt coalescing */ + csr8w(ctlr, Intcfg0, 0); + for(r = Coal8125; r < Coal8125+0x80; r += 4) + csr32w(ctlr, r, 0); switch(ctlr->macv){ + case Macv63: + case Macv70: + case Macv71: + csr16w(ctlr, Intcfg1, 0); + break; + case Macv61: + case Macv64: + case Macv65: + case Macv66: + for(; r < Coal8125+0x100; r += 4) + csr32w(ctlr, r, 0); + break; + } + + csr16w(ctlr, 0x0382, 0x221b); + + csr8w(ctlr, Rssctl, 0); + csr16w(ctlr, Qnumctl, 0); + csr8w(ctlr, Config1, csr8r(ctlr, Config1) & ~0x10); + + rtl8125macw(ctlr, 0xc140, 0xffff); + rtl8125macw(ctlr, 0xc142, 0xffff); + + v = rtl8125macr(ctlr, 0xd3e2) & ~0x0fff; + rtl8125macw(ctlr, 0xd3e2, v | 0x03a9); + + rtl8125macw(ctlr, 0xd3e4, rtl8125macr(ctlr, 0xd3e4) & ~0x00ff); + rtl8125macw(ctlr, 0xe860, rtl8125macr(ctlr, 0xe860) | 0x0080); + rtl8125macw(ctlr, 0xeb58, rtl8125macr(ctlr, 0xeb58) | 0x0001); + + if(ctlr->macv == Macv70 || ctlr->macv == Macv71) + csr8w(ctlr, 0xd8, csr8r(ctlr, 0xd8) & ~0x02); + + v = rtl8125macr(ctlr, 0xe614) & ~0x0700; + switch(ctlr->macv){ + case Macv61: + case Macv70: + case Macv71: + rtl8125macw(ctlr, 0xe614, v | 0x0400); + break; + default: + rtl8125macw(ctlr, 0xe614, v | 0x0200); + } + + rtl8125macw(ctlr, 0xe63e, rtl8125macr(ctlr, 0xe63e) & ~0x0c00); + + switch(ctlr->macv){ + case Macv61: + case Macv70: + case Macv71: + rtl8125macw(ctlr, 0xe63e, (rtl8125macr(ctlr, 0xe63e) & ~0x0030) | 0x0020); + break; + default: + rtl8125macw(ctlr, 0xe63e, rtl8125macr(ctlr, 0xe63e) & ~0x0030); + } + + rtl8125macw(ctlr, 0xc0b4, rtl8125macr(ctlr, 0xc0b4) | 0x000c); + + rtl8125macw(ctlr, 0xeb6a, (rtl8125macr(ctlr, 0xeb6a) & ~0x00ff) | 0x0033); + rtl8125macw(ctlr, 0xeb50, (rtl8125macr(ctlr, 0xeb50) & ~0x03e0) | 0x0040); + rtl8125macw(ctlr, 0xe056, (rtl8125macr(ctlr, 0xe056) & ~0x00f0) | 0x0030); + + csr8w(ctlr, Tdfnr, 0x10); + csr8w(ctlr, Dllpr, csr8r(ctlr, Dllpr) | 0x80); + + rtl8125macw(ctlr, 0xe040, rtl8125macr(ctlr, 0xe040) & ~0x1000); + + rtl8125macw(ctlr, 0xea1c, (rtl8125macr(ctlr, 0xea1c) & ~0x0003) | 0x0001); + rtl8125macw(ctlr, 0xe0c0, (rtl8125macr(ctlr, 0xe0c0) & ~0x4f0f) | 0x4403); + rtl8125macw(ctlr, 0xe052, rtl8125macr(ctlr, 0xe052) | 0x0068); + rtl8125macw(ctlr, 0xe052, rtl8125macr(ctlr, 0xe052) & ~0x0080); + rtl8125macw(ctlr, 0xc0ac, (rtl8125macr(ctlr, 0xc0ac) & ~0x0080) | 0x1f00); + rtl8125macw(ctlr, 0xd430, (rtl8125macr(ctlr, 0xd430) & ~0x0fff) | 0x047f); + if(ctlr->macv == Macv61) + rtl8125macw(ctlr, 0xe84c, 0x00c0); + else + rtl8125macw(ctlr, 0xe84c, 0x0080); + + csr8w(ctlr, Dllpr, csr8r(ctlr, Dllpr) | 0x40); + + if(ctlr->macv == Macv61) + csr8w(ctlr, Mcu, csr8r(ctlr, Mcu) | 0x01); + + /* Disable EEE plus. */ + rtl8125macw(ctlr, 0xe080, rtl8125macr(ctlr, 0xe080) & ~0x0002); + + if(ctlr->macv == Macv70 || ctlr->macv == Macv71) + v = ~0x0304; + else + v = ~0x0004; + rtl8125macw(ctlr, 0xea1c, rtl8125macr(ctlr, 0xea1c) & v); + rtl8125macw(ctlr, 0xeb54, rtl8125macr(ctlr, 0xeb54) | 0x0001); + microdelay(1); + rtl8125macw(ctlr, 0xeb54, rtl8125macr(ctlr, 0xeb54) & ~0x0001); + csr32w(ctlr, 0x1880, csr32r(ctlr, 0x1880) & ~0x0030); + rtl8125macw(ctlr, 0xe098, 0xc302); + + for(timeo = 0; timeo < 10; timeo++){ + if(!(rtl8125macr(ctlr, 0xe00e) & 0x2000)) + break; + delay(1); + } +} + +static void +rtl8169init(Ctlr *ctlr) +{ + int timeo; + u32int r; + u64int pa; + u16int cplusc; + + ilock(ctlr); + if(rtl8169reset(ctlr) < 0){ + iunlock(ctlr); + error("reset failed"); + } + + r = csr32r(ctlr, Rcr); + r &= ~(Aap|Apm|Am|Ab|Ar|Aer); + csr32w(ctlr, Rcr, r); + + rtl8169trdinit(ctlr); + + cplusc = csr16r(ctlr, Cplusc) & ~(Endian|Rxchksum); + switch(ctlr->macv){ case Macv40: case Macv42: case Macv44: @@ -722,12 +1281,11 @@ cplusc |= Rxenb; break; } - csr16w(ctlr, Cplusc, cplusc); + csr16w(ctlr, Cplusc, cplusc|Txenb|Mulrw); pa = PCIWADDR(ctlr->td); csr32w(ctlr, Tnpds+4, pa>>32); csr32w(ctlr, Tnpds, pa); - pa = PCIWADDR(ctlr->rd); csr32w(ctlr, Rdsar+4, pa>>32); csr32w(ctlr, Rdsar, pa); @@ -745,7 +1303,12 @@ error("reset failed"); } - /* pre-RTL8168G controllers need TX/RX before configuration */ + if(ctlr->is8125) + rtl8125init(ctlr); + else + /* disable interrupt coalescing */ + csr16w(ctlr, Coal, 0); + switch(ctlr->macv){ case Macv40: case Macv42: @@ -752,24 +1315,43 @@ case Macv44: case Macv45: case Macv51: - /* RXDV gating */ - i = csr32r(ctlr, 0x00F0); - csr32w(ctlr, 0x00F0, i&~0x00080000); + /* disable rxdv gate */ + csr32w(ctlr, Misc, csr32r(ctlr, Misc) & ~Rxdven); break; + case Macv61: + case Macv63: + case Macv64: + case Macv65: + case Macv66: + case Macv70: + case Macv71: + csr32w(ctlr, Misc8125, csr32r(ctlr, Misc8125) & ~Rxdven8125); + break; default: + /* pre-RTL8168G controllers need TX/RX before configuration */ csr8w(ctlr, Cr, Te|Re); } csr32w(ctlr, Tcr, Ifg1|Ifg0|Mtxdmaunlimited); ctlr->tcr = csr32r(ctlr, Tcr); + ctlr->rcr = Ab|Am|Apm|Mrxdmaunlimited; switch(ctlr->macv){ case Macv42: case Macv45: case Macv51: - ctlr->rcr = Rxfth256|Mrxdmaunlimited|Ab|Am|Apm; + ctlr->rcr |= Rxfth256; break; + case Macv61: + case Macv63: + case Macv64: + case Macv65: + case Macv66: + case Macv70: + case Macv71: + ctlr->rcr |= Rx8125fetchdflt|Rxpso; + break; default: - ctlr->rcr = Rxfthnone|Mrxdmaunlimited|Ab|Am|Apm; + ctlr->rcr |= Rxfthnone; break; } ctlr->mchash = 0; @@ -781,14 +1363,11 @@ csr8w(ctlr, Etx, 0x3f); csr16w(ctlr, Rms, 0x3fff); - csr16w(ctlr, Coal, 0); - /* no early rx interrupts */ r = csr16r(ctlr, Mulint) & 0xF000; csr16w(ctlr, Mulint, r); - ctlr->imr = Serr|Fovw|Punlc|Rdu|Ter|Rer|Rok|Tdu; - csr16w(ctlr, Imr, ctlr->imr); + rtl8169setimr(ctlr, Serr|Fovw|Punlc|Rdu|Ter|Rer|Rok|Tdu); csr32w(ctlr, Mpc, 0); @@ -798,6 +1377,13 @@ case Macv44: case Macv45: case Macv51: + case Macv61: + case Macv63: + case Macv64: + case Macv65: + case Macv66: + case Macv70: + case Macv71: csr8w(ctlr, Cr, Te|Re); default: break; @@ -817,7 +1403,7 @@ for(;;){ ctlr = edev->ctlr; sleep(&ctlr->reset, return0, nil); - rtl8169init(edev); + rtl8169init(ctlr); } } @@ -875,8 +1461,14 @@ nexterror(); } - rtl8169init(edev); - rtl8169mii(edev); + if(ctlr->is8125){ + rtl8125hwinit(ctlr); + rtl8169mii(edev); + // rtl8125phyconf(ctlr); + } + rtl8169init(ctlr); + if(!ctlr->is8125) + rtl8169mii(edev); ctlr->init = 1; poperror(); @@ -891,16 +1483,14 @@ static void rtl8169link(Ether* edev) { + Ctlr *ctlr = edev->ctlr; uint r; - Ctlr *ctlr; - ctlr = edev->ctlr; - /* * Maybe the link changed - do we care very much? * Could stall transmits if no link, maybe? */ - r = csr8r(ctlr, Phystatus); + r = ctlr->is8125? csr16r(ctlr, Phystatus): csr8r(ctlr, Phystatus); if(r & Linksts){ if(r & Speed10) ethersetspeed(edev, 10); @@ -908,10 +1498,15 @@ ethersetspeed(edev, 100); else if(r & Speed1000) ethersetspeed(edev, 1000); + else if(ctlr->is8125){ + if(r & Speed2500) + ethersetspeed(edev, 2500); + else if(r & Speed5000) + ethersetspeed(edev, 5000); + } ethersetlink(edev, 1); - } else { + }else ethersetlink(edev, 0); - } } static void @@ -968,7 +1563,10 @@ if(ctlr->ntq > 0){ coherence(); - csr8w(ctlr, Tppoll, Npq); + if(ctlr->is8125) + csr8w(ctlr, Tppoll8125, 1); + else + csr8w(ctlr, Tppoll, Npq); } unlock(ctlr); } @@ -1050,21 +1648,37 @@ wakeup(&ctlr->reset); } +static uint +rtl8169getintr(Ctlr *ctlr) +{ + u32int isr; + + if(ctlr->is8125){ + isr = csr32r(ctlr, Isr8125); + if(isr == 0xFFFFFFFF) + return 0; + }else{ + isr = csr16r(ctlr, Isr); + if(isr == 0xFFFF) + return 0; + } + if((isr & ctlr->imr) == 0) + return 0; + rtl8169setisr(ctlr, isr); + return isr; +} + static void rtl8169interrupt(Ureg*, void* arg) { Ctlr *ctlr; Ether *edev; - u32int isr; + uint isr; edev = arg; ctlr = edev->ctlr; - while((isr = csr16r(ctlr, Isr)) != 0 && isr != 0xFFFF){ - csr16w(ctlr, Isr, isr); - if((isr & ctlr->imr) == 0) - break; - + while(isr = rtl8169getintr(ctlr)){ if(isr & Serr) ctlr->serr++; if(isr & Fovw) @@ -1095,18 +1709,21 @@ static void rtl8169shutdown(Ether *edev) { - Ctlr *ctlr = edev->ctlr; - - rtl8169halt(ctlr); + rtl8169halt(edev->ctlr); } int -vetmacv(Ctlr *ctlr, uint *macv) +vetmacv(Ctlr *ctlr, int *macv) { - *macv = csr32r(ctlr, Tcr) & HwveridMASK; - switch(*macv){ - default: - return -1; + int mask; + + /* + * Try a more restrictive mask first to catch more specific nics. + */ + mask = Hwveridmask1; + ctlr->is8125 = 0; +again: + switch(*macv = (csr32r(ctlr, Tcr) & mask)){ case Macv01: case Macv02: case Macv03: @@ -1134,6 +1751,20 @@ case Macv45: case Macv51: break; + case Macv61: + case Macv63: + case Macv64: + case Macv65: + case Macv66: + case Macv70: + case Macv71: + ctlr->is8125 = 1; + break; + default: + if(mask == Hwveridmask2) + return -1; + mask = Hwveridmask2; + goto again; } return 0; } @@ -1144,7 +1775,6 @@ Pcidev *p; Ctlr *ctlr; int i, port, pcie; - uint macv; p = nil; while(p = pcimatch(p, 0, 0)){ @@ -1158,6 +1788,7 @@ case Rtl8100e: /* RTL810[01]E ? */ case Rtl8168b: /* RTL8168B */ case Rtl8111b: /* RTL8111/8168/8411 */ + case Rtl8125: /* RTL812[56] family */ pcie = 1; break; case Rtl8169c: /* RTL8169C */ @@ -1187,22 +1818,21 @@ ctlr->pcidev = p; ctlr->pciv = i; ctlr->pcie = pcie; - pcienable(p); - if(vetmacv(ctlr, &macv) == -1){ - print("rtl8169: %T: unknown mac %.4ux %.8ux\n", p->tbdf, p->did, macv); + + /* + * Extract the chip hardware version needed to configure each properly. + */ + if(vetmacv(ctlr, &ctlr->macv) == -1){ + print("rtl8169: %T: unknown mac %.4ux %.8ux\n", p->tbdf, p->did, ctlr->macv); pcidisable(p); iofree(port); free(ctlr); continue; } + rtl8169halt(ctlr); - /* - * Extract the chip hardware version, - * needed to configure each properly. - */ - ctlr->macv = macv; if(rtl8169ctlrhead != nil) rtl8169ctlrtail->next = ctlr; else