17dd7cddfSDavid du Colombier /*
27dd7cddfSDavid du Colombier * Etherlink III, Fast EtherLink and Fast EtherLink XL adapters.
37dd7cddfSDavid du Colombier * To do:
47dd7cddfSDavid du Colombier * check robustness in the face of errors (e.g. busmaster & rxUnderrun);
57dd7cddfSDavid du Colombier * RxEarly and busmaster;
67dd7cddfSDavid du Colombier * autoSelect;
77dd7cddfSDavid du Colombier * PCI latency timer and master enable;
87dd7cddfSDavid du Colombier * errata list;
99a747e4fSDavid du Colombier * rewrite all initialisation.
107dd7cddfSDavid du Colombier */
117dd7cddfSDavid du Colombier #include "u.h"
127dd7cddfSDavid du Colombier #include "../port/lib.h"
137dd7cddfSDavid du Colombier #include "mem.h"
147dd7cddfSDavid du Colombier #include "dat.h"
157dd7cddfSDavid du Colombier #include "fns.h"
167dd7cddfSDavid du Colombier #include "io.h"
177dd7cddfSDavid du Colombier #include "../port/error.h"
187dd7cddfSDavid du Colombier #include "../port/netif.h"
197dd7cddfSDavid du Colombier
207dd7cddfSDavid du Colombier #include "etherif.h"
217dd7cddfSDavid du Colombier
227dd7cddfSDavid du Colombier #define XCVRDEBUG if(0)print
237dd7cddfSDavid du Colombier
247dd7cddfSDavid du Colombier enum {
257dd7cddfSDavid du Colombier IDport = 0x0110, /* anywhere between 0x0100 and 0x01F0 */
267dd7cddfSDavid du Colombier };
277dd7cddfSDavid du Colombier
287dd7cddfSDavid du Colombier enum { /* all windows */
297dd7cddfSDavid du Colombier CommandR = 0x000E,
307dd7cddfSDavid du Colombier IntStatusR = 0x000E,
317dd7cddfSDavid du Colombier };
327dd7cddfSDavid du Colombier
337dd7cddfSDavid du Colombier enum { /* Commands */
347dd7cddfSDavid du Colombier GlobalReset = 0x0000,
357dd7cddfSDavid du Colombier SelectRegisterWindow = 0x0001,
367dd7cddfSDavid du Colombier EnableDcConverter = 0x0002,
377dd7cddfSDavid du Colombier RxDisable = 0x0003,
387dd7cddfSDavid du Colombier RxEnable = 0x0004,
397dd7cddfSDavid du Colombier RxReset = 0x0005,
407dd7cddfSDavid du Colombier Stall = 0x0006, /* 3C90x */
417dd7cddfSDavid du Colombier TxDone = 0x0007,
427dd7cddfSDavid du Colombier RxDiscard = 0x0008,
437dd7cddfSDavid du Colombier TxEnable = 0x0009,
447dd7cddfSDavid du Colombier TxDisable = 0x000A,
457dd7cddfSDavid du Colombier TxReset = 0x000B,
467dd7cddfSDavid du Colombier RequestInterrupt = 0x000C,
477dd7cddfSDavid du Colombier AcknowledgeInterrupt = 0x000D,
487dd7cddfSDavid du Colombier SetInterruptEnable = 0x000E,
497dd7cddfSDavid du Colombier SetIndicationEnable = 0x000F, /* SetReadZeroMask */
507dd7cddfSDavid du Colombier SetRxFilter = 0x0010,
517dd7cddfSDavid du Colombier SetRxEarlyThresh = 0x0011,
527dd7cddfSDavid du Colombier SetTxAvailableThresh = 0x0012,
537dd7cddfSDavid du Colombier SetTxStartThresh = 0x0013,
547dd7cddfSDavid du Colombier StartDma = 0x0014, /* initiate busmaster operation */
557dd7cddfSDavid du Colombier StatisticsEnable = 0x0015,
567dd7cddfSDavid du Colombier StatisticsDisable = 0x0016,
577dd7cddfSDavid du Colombier DisableDcConverter = 0x0017,
587dd7cddfSDavid du Colombier SetTxReclaimThresh = 0x0018, /* PIO-only adapters */
597dd7cddfSDavid du Colombier PowerUp = 0x001B, /* not all adapters */
607dd7cddfSDavid du Colombier PowerDownFull = 0x001C, /* not all adapters */
617dd7cddfSDavid du Colombier PowerAuto = 0x001D, /* not all adapters */
627dd7cddfSDavid du Colombier };
637dd7cddfSDavid du Colombier
647dd7cddfSDavid du Colombier enum { /* (Global|Rx|Tx)Reset command bits */
657dd7cddfSDavid du Colombier tpAuiReset = 0x0001, /* 10BaseT and AUI transceivers */
667dd7cddfSDavid du Colombier endecReset = 0x0002, /* internal Ethernet encoder/decoder */
677dd7cddfSDavid du Colombier networkReset = 0x0004, /* network interface logic */
687dd7cddfSDavid du Colombier fifoReset = 0x0008, /* FIFO control logic */
697dd7cddfSDavid du Colombier aismReset = 0x0010, /* autoinitialise state-machine logic */
707dd7cddfSDavid du Colombier hostReset = 0x0020, /* bus interface logic */
717dd7cddfSDavid du Colombier dmaReset = 0x0040, /* bus master logic */
727dd7cddfSDavid du Colombier vcoReset = 0x0080, /* on-board 10Mbps VCO */
737dd7cddfSDavid du Colombier updnReset = 0x0100, /* upload/download (Rx/TX) logic */
747dd7cddfSDavid du Colombier
757dd7cddfSDavid du Colombier resetMask = 0x01FF,
767dd7cddfSDavid du Colombier };
777dd7cddfSDavid du Colombier
787dd7cddfSDavid du Colombier enum { /* Stall command bits */
797dd7cddfSDavid du Colombier upStall = 0x0000,
807dd7cddfSDavid du Colombier upUnStall = 0x0001,
817dd7cddfSDavid du Colombier dnStall = 0x0002,
827dd7cddfSDavid du Colombier dnUnStall = 0x0003,
837dd7cddfSDavid du Colombier };
847dd7cddfSDavid du Colombier
857dd7cddfSDavid du Colombier enum { /* SetRxFilter command bits */
867dd7cddfSDavid du Colombier receiveIndividual = 0x0001, /* match station address */
877dd7cddfSDavid du Colombier receiveMulticast = 0x0002,
887dd7cddfSDavid du Colombier receiveBroadcast = 0x0004,
897dd7cddfSDavid du Colombier receiveAllFrames = 0x0008, /* promiscuous */
907dd7cddfSDavid du Colombier };
917dd7cddfSDavid du Colombier
927dd7cddfSDavid du Colombier enum { /* StartDma command bits */
937dd7cddfSDavid du Colombier Upload = 0x0000, /* transfer data from adapter to memory */
947dd7cddfSDavid du Colombier Download = 0x0001, /* transfer data from memory to adapter */
957dd7cddfSDavid du Colombier };
967dd7cddfSDavid du Colombier
977dd7cddfSDavid du Colombier enum { /* IntStatus bits */
987dd7cddfSDavid du Colombier interruptLatch = 0x0001,
997dd7cddfSDavid du Colombier hostError = 0x0002, /* Adapter Failure */
1007dd7cddfSDavid du Colombier txComplete = 0x0004,
1017dd7cddfSDavid du Colombier txAvailable = 0x0008,
1027dd7cddfSDavid du Colombier rxComplete = 0x0010,
1037dd7cddfSDavid du Colombier rxEarly = 0x0020,
1047dd7cddfSDavid du Colombier intRequested = 0x0040,
1057dd7cddfSDavid du Colombier updateStats = 0x0080,
1067dd7cddfSDavid du Colombier transferInt = 0x0100, /* Bus Master Transfer Complete */
1077dd7cddfSDavid du Colombier dnComplete = 0x0200,
1087dd7cddfSDavid du Colombier upComplete = 0x0400,
1097dd7cddfSDavid du Colombier busMasterInProgress = 0x0800,
1107dd7cddfSDavid du Colombier commandInProgress = 0x1000,
1117dd7cddfSDavid du Colombier
1127dd7cddfSDavid du Colombier interruptMask = 0x07FE,
1137dd7cddfSDavid du Colombier };
1147dd7cddfSDavid du Colombier
1157dd7cddfSDavid du Colombier #define COMMAND(port, cmd, a) outs((port)+CommandR, ((cmd)<<11)|(a))
1167dd7cddfSDavid du Colombier #define STATUS(port) ins((port)+IntStatusR)
1177dd7cddfSDavid du Colombier
1187dd7cddfSDavid du Colombier enum { /* Window 0 - setup */
1197dd7cddfSDavid du Colombier Wsetup = 0x0000,
1207dd7cddfSDavid du Colombier /* registers */
1217dd7cddfSDavid du Colombier ManufacturerID = 0x0000, /* 3C5[08]*, 3C59[27] */
1227dd7cddfSDavid du Colombier ProductID = 0x0002, /* 3C5[08]*, 3C59[27] */
1237dd7cddfSDavid du Colombier ConfigControl = 0x0004, /* 3C5[08]*, 3C59[27] */
1247dd7cddfSDavid du Colombier AddressConfig = 0x0006, /* 3C5[08]*, 3C59[27] */
1257dd7cddfSDavid du Colombier ResourceConfig = 0x0008, /* 3C5[08]*, 3C59[27] */
1267dd7cddfSDavid du Colombier EepromCommand = 0x000A,
1277dd7cddfSDavid du Colombier EepromData = 0x000C,
1287dd7cddfSDavid du Colombier /* AddressConfig Bits */
1297dd7cddfSDavid du Colombier autoSelect9 = 0x0080,
1307dd7cddfSDavid du Colombier xcvrMask9 = 0xC000,
1317dd7cddfSDavid du Colombier /* ConfigControl bits */
1327dd7cddfSDavid du Colombier Ena = 0x0001,
1337dd7cddfSDavid du Colombier base10TAvailable9 = 0x0200,
1347dd7cddfSDavid du Colombier coaxAvailable9 = 0x1000,
1357dd7cddfSDavid du Colombier auiAvailable9 = 0x2000,
1367dd7cddfSDavid du Colombier /* EepromCommand bits */
1377dd7cddfSDavid du Colombier EepromReadRegister = 0x0080,
138e31d734fSDavid du Colombier EepromReadOffRegister = 0x00B0,
1399a747e4fSDavid du Colombier EepromRead8bRegister = 0x0230,
1407dd7cddfSDavid du Colombier EepromBusy = 0x8000,
1417dd7cddfSDavid du Colombier };
1427dd7cddfSDavid du Colombier
1437dd7cddfSDavid du Colombier #define EEPROMCMD(port, cmd, a) outs((port)+EepromCommand, (cmd)|(a))
1447dd7cddfSDavid du Colombier #define EEPROMBUSY(port) (ins((port)+EepromCommand) & EepromBusy)
1457dd7cddfSDavid du Colombier #define EEPROMDATA(port) ins((port)+EepromData)
1467dd7cddfSDavid du Colombier
1477dd7cddfSDavid du Colombier enum { /* Window 1 - operating set */
1487dd7cddfSDavid du Colombier Wop = 0x0001,
1497dd7cddfSDavid du Colombier /* registers */
1507dd7cddfSDavid du Colombier Fifo = 0x0000,
1517dd7cddfSDavid du Colombier RxError = 0x0004, /* 3C59[0257] only */
1527dd7cddfSDavid du Colombier RxStatus = 0x0008,
1533ff48bf5SDavid du Colombier TIMER = 0x000A,
1547dd7cddfSDavid du Colombier TxStatus = 0x000B,
1557dd7cddfSDavid du Colombier TxFree = 0x000C,
1567dd7cddfSDavid du Colombier /* RxError bits */
1577dd7cddfSDavid du Colombier rxOverrun = 0x0001,
1587dd7cddfSDavid du Colombier runtFrame = 0x0002,
1597dd7cddfSDavid du Colombier alignmentError = 0x0004, /* Framing */
1607dd7cddfSDavid du Colombier crcError = 0x0008,
1617dd7cddfSDavid du Colombier oversizedFrame = 0x0010,
1627dd7cddfSDavid du Colombier dribbleBits = 0x0080,
1637dd7cddfSDavid du Colombier /* RxStatus bits */
1647dd7cddfSDavid du Colombier rxBytes = 0x1FFF, /* 3C59[0257] mask */
1657dd7cddfSDavid du Colombier rxBytes9 = 0x07FF, /* 3C5[078]9 mask */
1667dd7cddfSDavid du Colombier rxError9 = 0x3800, /* 3C5[078]9 error mask */
1677dd7cddfSDavid du Colombier rxOverrun9 = 0x0000,
1687dd7cddfSDavid du Colombier oversizedFrame9 = 0x0800,
1697dd7cddfSDavid du Colombier dribbleBits9 = 0x1000,
1707dd7cddfSDavid du Colombier runtFrame9 = 0x1800,
1717dd7cddfSDavid du Colombier alignmentError9 = 0x2000, /* Framing */
1727dd7cddfSDavid du Colombier crcError9 = 0x2800,
1737dd7cddfSDavid du Colombier rxError = 0x4000,
1747dd7cddfSDavid du Colombier rxIncomplete = 0x8000,
1757dd7cddfSDavid du Colombier /* TxStatus Bits */
1767dd7cddfSDavid du Colombier txStatusOverflow = 0x0004,
1777dd7cddfSDavid du Colombier maxCollisions = 0x0008,
1787dd7cddfSDavid du Colombier txUnderrun = 0x0010,
1797dd7cddfSDavid du Colombier txJabber = 0x0020,
1807dd7cddfSDavid du Colombier interruptRequested = 0x0040,
1817dd7cddfSDavid du Colombier txStatusComplete = 0x0080,
1827dd7cddfSDavid du Colombier };
1837dd7cddfSDavid du Colombier
1847dd7cddfSDavid du Colombier enum { /* Window 2 - station address */
1857dd7cddfSDavid du Colombier Wstation = 0x0002,
1867dd7cddfSDavid du Colombier
1877dd7cddfSDavid du Colombier ResetOp905B = 0x000C,
1887dd7cddfSDavid du Colombier };
1897dd7cddfSDavid du Colombier
1907dd7cddfSDavid du Colombier enum { /* Window 3 - FIFO management */
1917dd7cddfSDavid du Colombier Wfifo = 0x0003,
1927dd7cddfSDavid du Colombier /* registers */
1937dd7cddfSDavid du Colombier InternalConfig = 0x0000, /* 3C509B, 3C589, 3C59[0257] */
1947dd7cddfSDavid du Colombier OtherInt = 0x0004, /* 3C59[0257] */
1957dd7cddfSDavid du Colombier RomControl = 0x0006, /* 3C509B, 3C59[27] */
1967dd7cddfSDavid du Colombier MacControl = 0x0006, /* 3C59[0257] */
1977dd7cddfSDavid du Colombier ResetOptions = 0x0008, /* 3C59[0257] */
1987dd7cddfSDavid du Colombier MediaOptions = 0x0008, /* 3C905B */
1997dd7cddfSDavid du Colombier RxFree = 0x000A,
2007dd7cddfSDavid du Colombier /* InternalConfig bits */
2017dd7cddfSDavid du Colombier disableBadSsdDetect = 0x00000100,
2027dd7cddfSDavid du Colombier ramLocation = 0x00000200, /* 0 external, 1 internal */
2037dd7cddfSDavid du Colombier ramPartition5to3 = 0x00000000,
2047dd7cddfSDavid du Colombier ramPartition3to1 = 0x00010000,
2057dd7cddfSDavid du Colombier ramPartition1to1 = 0x00020000,
2067dd7cddfSDavid du Colombier ramPartition3to5 = 0x00030000,
2077dd7cddfSDavid du Colombier ramPartitionMask = 0x00030000,
2087dd7cddfSDavid du Colombier xcvr10BaseT = 0x00000000,
2097dd7cddfSDavid du Colombier xcvrAui = 0x00100000, /* 10BASE5 */
2107dd7cddfSDavid du Colombier xcvr10Base2 = 0x00300000,
2117dd7cddfSDavid du Colombier xcvr100BaseTX = 0x00400000,
2127dd7cddfSDavid du Colombier xcvr100BaseFX = 0x00500000,
2137dd7cddfSDavid du Colombier xcvrMii = 0x00600000,
2147dd7cddfSDavid du Colombier xcvrMask = 0x00700000,
2157dd7cddfSDavid du Colombier autoSelect = 0x01000000,
2167dd7cddfSDavid du Colombier /* MacControl bits */
2177dd7cddfSDavid du Colombier deferExtendEnable = 0x0001,
2183ff48bf5SDavid du Colombier deferTIMERSelect = 0x001E, /* mask */
2197dd7cddfSDavid du Colombier fullDuplexEnable = 0x0020,
2207dd7cddfSDavid du Colombier allowLargePackets = 0x0040,
2217dd7cddfSDavid du Colombier extendAfterCollision = 0x0080, /* 3C90xB */
2227dd7cddfSDavid du Colombier flowControlEnable = 0x0100, /* 3C90xB */
2237dd7cddfSDavid du Colombier vltEnable = 0x0200, /* 3C90xB */
2247dd7cddfSDavid du Colombier /* ResetOptions bits */
2257dd7cddfSDavid du Colombier baseT4Available = 0x0001,
2267dd7cddfSDavid du Colombier baseTXAvailable = 0x0002,
2277dd7cddfSDavid du Colombier baseFXAvailable = 0x0004,
2287dd7cddfSDavid du Colombier base10TAvailable = 0x0008,
2297dd7cddfSDavid du Colombier coaxAvailable = 0x0010,
2307dd7cddfSDavid du Colombier auiAvailable = 0x0020,
2317dd7cddfSDavid du Colombier miiConnector = 0x0040,
2327dd7cddfSDavid du Colombier };
2337dd7cddfSDavid du Colombier
2347dd7cddfSDavid du Colombier enum { /* Window 4 - diagnostic */
2357dd7cddfSDavid du Colombier Wdiagnostic = 0x0004,
2367dd7cddfSDavid du Colombier /* registers */
2377dd7cddfSDavid du Colombier VcoDiagnostic = 0x0002,
2387dd7cddfSDavid du Colombier FifoDiagnostic = 0x0004,
2397dd7cddfSDavid du Colombier NetworkDiagnostic = 0x0006,
2407dd7cddfSDavid du Colombier PhysicalMgmt = 0x0008,
2417dd7cddfSDavid du Colombier MediaStatus = 0x000A,
2427dd7cddfSDavid du Colombier BadSSD = 0x000C,
2437dd7cddfSDavid du Colombier UpperBytesOk = 0x000D,
2447dd7cddfSDavid du Colombier /* FifoDiagnostic bits */
2457dd7cddfSDavid du Colombier txOverrun = 0x0400,
2467dd7cddfSDavid du Colombier rxUnderrun = 0x2000,
2477dd7cddfSDavid du Colombier receiving = 0x8000,
2487dd7cddfSDavid du Colombier /* PhysicalMgmt bits */
2497dd7cddfSDavid du Colombier mgmtClk = 0x0001,
2507dd7cddfSDavid du Colombier mgmtData = 0x0002,
2517dd7cddfSDavid du Colombier mgmtDir = 0x0004,
2527dd7cddfSDavid du Colombier cat5LinkTestDefeat = 0x8000,
2537dd7cddfSDavid du Colombier /* MediaStatus bits */
2547dd7cddfSDavid du Colombier dataRate100 = 0x0002,
2557dd7cddfSDavid du Colombier crcStripDisable = 0x0004,
2567dd7cddfSDavid du Colombier enableSqeStats = 0x0008,
2577dd7cddfSDavid du Colombier collisionDetect = 0x0010,
2587dd7cddfSDavid du Colombier carrierSense = 0x0020,
2597dd7cddfSDavid du Colombier jabberGuardEnable = 0x0040,
2607dd7cddfSDavid du Colombier linkBeatEnable = 0x0080,
2617dd7cddfSDavid du Colombier jabberDetect = 0x0200,
2627dd7cddfSDavid du Colombier polarityReversed = 0x0400,
2637dd7cddfSDavid du Colombier linkBeatDetect = 0x0800,
2647dd7cddfSDavid du Colombier txInProg = 0x1000,
2657dd7cddfSDavid du Colombier dcConverterEnabled = 0x4000,
2667dd7cddfSDavid du Colombier auiDisable = 0x8000, /* 10BaseT transceiver selected */
2677dd7cddfSDavid du Colombier };
2687dd7cddfSDavid du Colombier
2697dd7cddfSDavid du Colombier enum { /* Window 5 - internal state */
2707dd7cddfSDavid du Colombier Wstate = 0x0005,
2717dd7cddfSDavid du Colombier /* registers */
2727dd7cddfSDavid du Colombier TxStartThresh = 0x0000,
2737dd7cddfSDavid du Colombier TxAvailableThresh = 0x0002,
2747dd7cddfSDavid du Colombier RxEarlyThresh = 0x0006,
2757dd7cddfSDavid du Colombier RxFilter = 0x0008,
2767dd7cddfSDavid du Colombier InterruptEnable = 0x000A,
2777dd7cddfSDavid du Colombier IndicationEnable = 0x000C,
2787dd7cddfSDavid du Colombier };
2797dd7cddfSDavid du Colombier
2807dd7cddfSDavid du Colombier enum { /* Window 6 - statistics */
2817dd7cddfSDavid du Colombier Wstatistics = 0x0006,
2827dd7cddfSDavid du Colombier /* registers */
2837dd7cddfSDavid du Colombier CarrierLost = 0x0000,
2847dd7cddfSDavid du Colombier SqeErrors = 0x0001,
2857dd7cddfSDavid du Colombier MultipleColls = 0x0002,
2867dd7cddfSDavid du Colombier SingleCollFrames = 0x0003,
2877dd7cddfSDavid du Colombier LateCollisions = 0x0004,
2887dd7cddfSDavid du Colombier RxOverruns = 0x0005,
2897dd7cddfSDavid du Colombier FramesXmittedOk = 0x0006,
2907dd7cddfSDavid du Colombier FramesRcvdOk = 0x0007,
2917dd7cddfSDavid du Colombier FramesDeferred = 0x0008,
2927dd7cddfSDavid du Colombier UpperFramesOk = 0x0009,
2937dd7cddfSDavid du Colombier BytesRcvdOk = 0x000A,
2947dd7cddfSDavid du Colombier BytesXmittedOk = 0x000C,
2957dd7cddfSDavid du Colombier };
2967dd7cddfSDavid du Colombier
2977dd7cddfSDavid du Colombier enum { /* Window 7 - bus master operations */
2987dd7cddfSDavid du Colombier Wmaster = 0x0007,
2997dd7cddfSDavid du Colombier /* registers */
3007dd7cddfSDavid du Colombier MasterAddress = 0x0000,
3017dd7cddfSDavid du Colombier MasterLen = 0x0006,
3027dd7cddfSDavid du Colombier MasterStatus = 0x000C,
3037dd7cddfSDavid du Colombier /* MasterStatus bits */
3047dd7cddfSDavid du Colombier masterAbort = 0x0001,
3057dd7cddfSDavid du Colombier targetAbort = 0x0002,
3067dd7cddfSDavid du Colombier targetRetry = 0x0004,
3077dd7cddfSDavid du Colombier targetDisc = 0x0008,
3087dd7cddfSDavid du Colombier masterDownload = 0x1000,
3097dd7cddfSDavid du Colombier masterUpload = 0x4000,
3107dd7cddfSDavid du Colombier masterInProgress = 0x8000,
3117dd7cddfSDavid du Colombier
3127dd7cddfSDavid du Colombier masterMask = 0xD00F,
3137dd7cddfSDavid du Colombier };
3147dd7cddfSDavid du Colombier
3157dd7cddfSDavid du Colombier enum { /* 3C90x extended register set */
3163ff48bf5SDavid du Colombier TIMER905 = 0x001A, /* 8-bits */
31759cc4ca5SDavid du Colombier TxStatus905 = 0x001B, /* 8-bits */
3187dd7cddfSDavid du Colombier PktStatus = 0x0020, /* 32-bits */
3197dd7cddfSDavid du Colombier DnListPtr = 0x0024, /* 32-bits, 8-byte aligned */
3207dd7cddfSDavid du Colombier FragAddr = 0x0028, /* 32-bits */
3217dd7cddfSDavid du Colombier FragLen = 0x002C, /* 16-bits */
3227dd7cddfSDavid du Colombier ListOffset = 0x002E, /* 8-bits */
3237dd7cddfSDavid du Colombier TxFreeThresh = 0x002F, /* 8-bits */
3247dd7cddfSDavid du Colombier UpPktStatus = 0x0030, /* 32-bits */
3253ff48bf5SDavid du Colombier FreeTIMER = 0x0034, /* 16-bits */
3267dd7cddfSDavid du Colombier UpListPtr = 0x0038, /* 32-bits, 8-byte aligned */
3277dd7cddfSDavid du Colombier
3287dd7cddfSDavid du Colombier /* PktStatus bits */
3297dd7cddfSDavid du Colombier fragLast = 0x00000001,
3307dd7cddfSDavid du Colombier dnCmplReq = 0x00000002,
3317dd7cddfSDavid du Colombier dnStalled = 0x00000004,
3327dd7cddfSDavid du Colombier upCompleteX = 0x00000008,
3337dd7cddfSDavid du Colombier dnCompleteX = 0x00000010,
3347dd7cddfSDavid du Colombier upRxEarlyEnable = 0x00000020,
3357dd7cddfSDavid du Colombier armCountdown = 0x00000040,
3367dd7cddfSDavid du Colombier dnInProg = 0x00000080,
3377dd7cddfSDavid du Colombier counterSpeed = 0x00000010, /* 0 3.2uS, 1 320nS */
3387dd7cddfSDavid du Colombier countdownMode = 0x00000020,
3397dd7cddfSDavid du Colombier /* UpPktStatus bits (dpd->control) */
3407dd7cddfSDavid du Colombier upPktLenMask = 0x00001FFF,
3417dd7cddfSDavid du Colombier upStalled = 0x00002000,
3427dd7cddfSDavid du Colombier upError = 0x00004000,
3437dd7cddfSDavid du Colombier upPktComplete = 0x00008000,
3447dd7cddfSDavid du Colombier upOverrun = 0x00010000, /* RxError<<16 */
3457dd7cddfSDavid du Colombier upRuntFrame = 0x00020000,
3467dd7cddfSDavid du Colombier upAlignmentError = 0x00040000,
3477dd7cddfSDavid du Colombier upCRCError = 0x00080000,
3487dd7cddfSDavid du Colombier upOversizedFrame = 0x00100000,
3497dd7cddfSDavid du Colombier upDribbleBits = 0x00800000,
3507dd7cddfSDavid du Colombier upOverflow = 0x01000000,
3517dd7cddfSDavid du Colombier
3527dd7cddfSDavid du Colombier dnIndicate = 0x80000000, /* FrameStartHeader (dpd->control) */
3537dd7cddfSDavid du Colombier
3547dd7cddfSDavid du Colombier updnLastFrag = 0x80000000, /* (dpd->len) */
3557dd7cddfSDavid du Colombier
3567dd7cddfSDavid du Colombier Nup = 32,
3577dd7cddfSDavid du Colombier Ndn = 64,
3587dd7cddfSDavid du Colombier };
3597dd7cddfSDavid du Colombier
3607dd7cddfSDavid du Colombier /*
3617dd7cddfSDavid du Colombier * Up/Dn Packet Descriptors.
3627dd7cddfSDavid du Colombier * The hardware info (np, control, addr, len) must be 8-byte aligned
3637dd7cddfSDavid du Colombier * and this structure size must be a multiple of 8.
3647dd7cddfSDavid du Colombier */
3657dd7cddfSDavid du Colombier typedef struct Pd Pd;
3667dd7cddfSDavid du Colombier typedef struct Pd {
3677dd7cddfSDavid du Colombier ulong np; /* next pointer */
3687dd7cddfSDavid du Colombier ulong control; /* FSH or UpPktStatus */
3697dd7cddfSDavid du Colombier ulong addr;
3707dd7cddfSDavid du Colombier ulong len;
3717dd7cddfSDavid du Colombier
3727dd7cddfSDavid du Colombier Pd* next;
3737dd7cddfSDavid du Colombier Block* bp;
3747dd7cddfSDavid du Colombier } Pd;
3757dd7cddfSDavid du Colombier
3763ff48bf5SDavid du Colombier typedef struct Ctlr Ctlr;
3773ff48bf5SDavid du Colombier typedef struct Ctlr {
3783ff48bf5SDavid du Colombier int port;
3793ff48bf5SDavid du Colombier Pcidev* pcidev;
3803ff48bf5SDavid du Colombier int irq;
3813ff48bf5SDavid du Colombier Ctlr* next;
3823ff48bf5SDavid du Colombier int active;
3833ff48bf5SDavid du Colombier int did;
3843ff48bf5SDavid du Colombier
3857dd7cddfSDavid du Colombier Lock wlock; /* window access */
3867dd7cddfSDavid du Colombier
3877dd7cddfSDavid du Colombier int attached;
3887dd7cddfSDavid du Colombier int busmaster;
3897dd7cddfSDavid du Colombier Block* rbp; /* receive buffer */
3907dd7cddfSDavid du Colombier
3917dd7cddfSDavid du Colombier Block* txbp; /* FIFO -based transmission */
3927dd7cddfSDavid du Colombier int txthreshold;
3937dd7cddfSDavid du Colombier int txbusy;
3947dd7cddfSDavid du Colombier
3957dd7cddfSDavid du Colombier int nup; /* full-busmaster -based reception */
3967dd7cddfSDavid du Colombier void* upbase;
3977dd7cddfSDavid du Colombier Pd* upr;
3987dd7cddfSDavid du Colombier Pd* uphead;
3997dd7cddfSDavid du Colombier
4007dd7cddfSDavid du Colombier int ndn; /* full-busmaster -based transmission */
4017dd7cddfSDavid du Colombier void* dnbase;
4027dd7cddfSDavid du Colombier Pd* dnr;
4037dd7cddfSDavid du Colombier Pd* dnhead;
4047dd7cddfSDavid du Colombier Pd* dntail;
4057dd7cddfSDavid du Colombier int dnq;
4067dd7cddfSDavid du Colombier
4077dd7cddfSDavid du Colombier long interrupts; /* statistics */
4083ff48bf5SDavid du Colombier long bogusinterrupts;
40959cc4ca5SDavid du Colombier long timer[2];
4107dd7cddfSDavid du Colombier long stats[BytesRcvdOk+3];
4117dd7cddfSDavid du Colombier
4127dd7cddfSDavid du Colombier int upqmax;
4137dd7cddfSDavid du Colombier int upqmaxhw;
4147dd7cddfSDavid du Colombier ulong upinterrupts;
4157dd7cddfSDavid du Colombier ulong upqueued;
4167dd7cddfSDavid du Colombier ulong upstalls;
4177dd7cddfSDavid du Colombier int dnqmax;
4187dd7cddfSDavid du Colombier int dnqmaxhw;
4197dd7cddfSDavid du Colombier ulong dninterrupts;
4207dd7cddfSDavid du Colombier ulong dnqueued;
4217dd7cddfSDavid du Colombier
4227dd7cddfSDavid du Colombier int xcvr; /* transceiver type */
423e31d734fSDavid du Colombier int eepromcmd; /* EEPROM read command */
4247dd7cddfSDavid du Colombier int rxstatus9; /* old-style RxStatus register */
4257dd7cddfSDavid du Colombier int rxearly; /* RxEarlyThreshold */
4267dd7cddfSDavid du Colombier int ts; /* threshold shift */
4277dd7cddfSDavid du Colombier int upenabled;
4287dd7cddfSDavid du Colombier int dnenabled;
429e31d734fSDavid du Colombier ulong cbfnpa; /* CardBus functions */
430e31d734fSDavid du Colombier ulong* cbfn;
4317dd7cddfSDavid du Colombier } Ctlr;
4327dd7cddfSDavid du Colombier
4333ff48bf5SDavid du Colombier static Ctlr* ctlrhead;
4343ff48bf5SDavid du Colombier static Ctlr* ctlrtail;
4353ff48bf5SDavid du Colombier
4367dd7cddfSDavid du Colombier static void
init905(Ctlr * ctlr)4377dd7cddfSDavid du Colombier init905(Ctlr* ctlr)
4387dd7cddfSDavid du Colombier {
4397dd7cddfSDavid du Colombier Block *bp;
4407dd7cddfSDavid du Colombier Pd *pd, *prev;
4417dd7cddfSDavid du Colombier
4427dd7cddfSDavid du Colombier /*
4437dd7cddfSDavid du Colombier * Create rings for the receive and transmit sides.
4447dd7cddfSDavid du Colombier * Take care with alignment:
4457dd7cddfSDavid du Colombier * make sure ring base is 8-byte aligned;
4467dd7cddfSDavid du Colombier * make sure each entry is 8-byte aligned.
4477dd7cddfSDavid du Colombier */
4487dd7cddfSDavid du Colombier ctlr->upbase = malloc((ctlr->nup+1)*sizeof(Pd));
449*aa72973aSDavid du Colombier if(ctlr->upbase == nil)
450*aa72973aSDavid du Colombier error(Enomem);
4517dd7cddfSDavid du Colombier ctlr->upr = (Pd*)ROUNDUP((ulong)ctlr->upbase, 8);
4527dd7cddfSDavid du Colombier
4537dd7cddfSDavid du Colombier prev = ctlr->upr;
4547dd7cddfSDavid du Colombier for(pd = &ctlr->upr[ctlr->nup-1]; pd >= ctlr->upr; pd--){
4557dd7cddfSDavid du Colombier pd->np = PADDR(&prev->np);
4567dd7cddfSDavid du Colombier pd->control = 0;
4579a747e4fSDavid du Colombier bp = iallocb(sizeof(Etherpkt));
4589a747e4fSDavid du Colombier if(bp == nil)
4599a747e4fSDavid du Colombier panic("can't allocate ethernet receive ring");
4607dd7cddfSDavid du Colombier pd->addr = PADDR(bp->rp);
4617dd7cddfSDavid du Colombier pd->len = updnLastFrag|sizeof(Etherpkt);
4627dd7cddfSDavid du Colombier
4637dd7cddfSDavid du Colombier pd->next = prev;
4647dd7cddfSDavid du Colombier prev = pd;
4657dd7cddfSDavid du Colombier pd->bp = bp;
4667dd7cddfSDavid du Colombier }
4677dd7cddfSDavid du Colombier ctlr->uphead = ctlr->upr;
4687dd7cddfSDavid du Colombier
4697dd7cddfSDavid du Colombier ctlr->dnbase = malloc((ctlr->ndn+1)*sizeof(Pd));
470*aa72973aSDavid du Colombier if(ctlr->dnbase == nil) {
471*aa72973aSDavid du Colombier free(ctlr->upbase);
472*aa72973aSDavid du Colombier error(Enomem);
473*aa72973aSDavid du Colombier }
4747dd7cddfSDavid du Colombier ctlr->dnr = (Pd*)ROUNDUP((ulong)ctlr->dnbase, 8);
4757dd7cddfSDavid du Colombier
4767dd7cddfSDavid du Colombier prev = ctlr->dnr;
4777dd7cddfSDavid du Colombier for(pd = &ctlr->dnr[ctlr->ndn-1]; pd >= ctlr->dnr; pd--){
4787dd7cddfSDavid du Colombier pd->next = prev;
4797dd7cddfSDavid du Colombier prev = pd;
4807dd7cddfSDavid du Colombier }
4817dd7cddfSDavid du Colombier ctlr->dnhead = ctlr->dnr;
4827dd7cddfSDavid du Colombier ctlr->dntail = ctlr->dnr;
4837dd7cddfSDavid du Colombier ctlr->dnq = 0;
4847dd7cddfSDavid du Colombier }
4857dd7cddfSDavid du Colombier
4867dd7cddfSDavid du Colombier static Block*
rbpalloc(Block * (* f)(int))4877dd7cddfSDavid du Colombier rbpalloc(Block* (*f)(int))
4887dd7cddfSDavid du Colombier {
4897dd7cddfSDavid du Colombier Block *bp;
4907dd7cddfSDavid du Colombier ulong addr;
4917dd7cddfSDavid du Colombier
4927dd7cddfSDavid du Colombier /*
4937dd7cddfSDavid du Colombier * The receive buffers must be on a 32-byte
4947dd7cddfSDavid du Colombier * boundary for EISA busmastering.
4957dd7cddfSDavid du Colombier */
4967dd7cddfSDavid du Colombier if(bp = f(ROUNDUP(sizeof(Etherpkt), 4) + 31)){
4977dd7cddfSDavid du Colombier addr = (ulong)bp->base;
4987dd7cddfSDavid du Colombier addr = ROUNDUP(addr, 32);
4997dd7cddfSDavid du Colombier bp->rp = (uchar*)addr;
5007dd7cddfSDavid du Colombier }
5017dd7cddfSDavid du Colombier
5027dd7cddfSDavid du Colombier return bp;
5037dd7cddfSDavid du Colombier }
5047dd7cddfSDavid du Colombier
5057dd7cddfSDavid du Colombier static uchar*
startdma(Ether * ether,ulong address)5067dd7cddfSDavid du Colombier startdma(Ether* ether, ulong address)
5077dd7cddfSDavid du Colombier {
5087dd7cddfSDavid du Colombier int port, status, w;
5097dd7cddfSDavid du Colombier uchar *wp;
5107dd7cddfSDavid du Colombier
5117dd7cddfSDavid du Colombier port = ether->port;
5127dd7cddfSDavid du Colombier
5137dd7cddfSDavid du Colombier w = (STATUS(port)>>13) & 0x07;
5147dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wmaster);
5157dd7cddfSDavid du Colombier
5167dd7cddfSDavid du Colombier wp = KADDR(inl(port+MasterAddress));
5177dd7cddfSDavid du Colombier status = ins(port+MasterStatus);
5187dd7cddfSDavid du Colombier if(status & (masterInProgress|targetAbort|masterAbort))
5197dd7cddfSDavid du Colombier print("#l%d: BM status 0x%uX\n", ether->ctlrno, status);
5207dd7cddfSDavid du Colombier outs(port+MasterStatus, masterMask);
5217dd7cddfSDavid du Colombier outl(port+MasterAddress, address);
5227dd7cddfSDavid du Colombier outs(port+MasterLen, sizeof(Etherpkt));
5237dd7cddfSDavid du Colombier COMMAND(port, StartDma, Upload);
5247dd7cddfSDavid du Colombier
5257dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, w);
5267dd7cddfSDavid du Colombier return wp;
5277dd7cddfSDavid du Colombier }
5287dd7cddfSDavid du Colombier
5297dd7cddfSDavid du Colombier static void
promiscuous(void * arg,int on)5307dd7cddfSDavid du Colombier promiscuous(void* arg, int on)
5317dd7cddfSDavid du Colombier {
5327dd7cddfSDavid du Colombier int filter, port;
5337dd7cddfSDavid du Colombier Ether *ether;
5347dd7cddfSDavid du Colombier
5357dd7cddfSDavid du Colombier ether = (Ether*)arg;
5367dd7cddfSDavid du Colombier port = ether->port;
5377dd7cddfSDavid du Colombier
5387dd7cddfSDavid du Colombier filter = receiveBroadcast|receiveIndividual;
5397dd7cddfSDavid du Colombier if(ether->nmaddr)
5407dd7cddfSDavid du Colombier filter |= receiveMulticast;
5417dd7cddfSDavid du Colombier if(on)
5427dd7cddfSDavid du Colombier filter |= receiveAllFrames;
5437dd7cddfSDavid du Colombier COMMAND(port, SetRxFilter, filter);
5447dd7cddfSDavid du Colombier }
5457dd7cddfSDavid du Colombier
5467dd7cddfSDavid du Colombier static void
multicast(void * arg,uchar * addr,int on)5477dd7cddfSDavid du Colombier multicast(void* arg, uchar *addr, int on)
5487dd7cddfSDavid du Colombier {
5497dd7cddfSDavid du Colombier int filter, port;
5507dd7cddfSDavid du Colombier Ether *ether;
5517dd7cddfSDavid du Colombier
5527dd7cddfSDavid du Colombier USED(addr, on);
5537dd7cddfSDavid du Colombier
5547dd7cddfSDavid du Colombier ether = (Ether*)arg;
5557dd7cddfSDavid du Colombier port = ether->port;
5567dd7cddfSDavid du Colombier
5577dd7cddfSDavid du Colombier filter = receiveBroadcast|receiveIndividual;
5587dd7cddfSDavid du Colombier if(ether->nmaddr)
5597dd7cddfSDavid du Colombier filter |= receiveMulticast;
5607dd7cddfSDavid du Colombier if(ether->prom)
5617dd7cddfSDavid du Colombier filter |= receiveAllFrames;
5627dd7cddfSDavid du Colombier COMMAND(port, SetRxFilter, filter);
5637dd7cddfSDavid du Colombier }
5647dd7cddfSDavid du Colombier
5659a747e4fSDavid du Colombier /* On the 575B and C, interrupts need to be acknowledged in CardBus memory space */
5669a747e4fSDavid du Colombier static void
intrackcb(ulong * cbfn)567e31d734fSDavid du Colombier intrackcb(ulong *cbfn)
5689a747e4fSDavid du Colombier {
569e31d734fSDavid du Colombier cbfn[1] = 0x8000;
5709a747e4fSDavid du Colombier }
5719a747e4fSDavid du Colombier
5727dd7cddfSDavid du Colombier static void
attach(Ether * ether)5737dd7cddfSDavid du Colombier attach(Ether* ether)
5747dd7cddfSDavid du Colombier {
5757dd7cddfSDavid du Colombier int port, x;
5767dd7cddfSDavid du Colombier Ctlr *ctlr;
5777dd7cddfSDavid du Colombier
5787dd7cddfSDavid du Colombier ctlr = ether->ctlr;
5797dd7cddfSDavid du Colombier ilock(&ctlr->wlock);
5807dd7cddfSDavid du Colombier if(ctlr->attached){
5817dd7cddfSDavid du Colombier iunlock(&ctlr->wlock);
5827dd7cddfSDavid du Colombier return;
5837dd7cddfSDavid du Colombier }
5847dd7cddfSDavid du Colombier
5857dd7cddfSDavid du Colombier port = ether->port;
5867dd7cddfSDavid du Colombier
5877dd7cddfSDavid du Colombier /*
5887dd7cddfSDavid du Colombier * Set the receiver packet filter for this and broadcast addresses,
5897dd7cddfSDavid du Colombier * set the interrupt masks for all interrupts, enable the receiver
5907dd7cddfSDavid du Colombier * and transmitter.
5917dd7cddfSDavid du Colombier */
5927dd7cddfSDavid du Colombier promiscuous(ether, ether->prom);
5937dd7cddfSDavid du Colombier
5947dd7cddfSDavid du Colombier x = interruptMask;
5957dd7cddfSDavid du Colombier if(ctlr->busmaster == 1)
5967dd7cddfSDavid du Colombier x &= ~(rxEarly|rxComplete);
5977dd7cddfSDavid du Colombier else{
5987dd7cddfSDavid du Colombier if(ctlr->dnenabled)
5997dd7cddfSDavid du Colombier x &= ~transferInt;
6007dd7cddfSDavid du Colombier if(ctlr->upenabled)
6017dd7cddfSDavid du Colombier x &= ~(rxEarly|rxComplete);
6027dd7cddfSDavid du Colombier }
6037dd7cddfSDavid du Colombier COMMAND(port, SetIndicationEnable, x);
6047dd7cddfSDavid du Colombier COMMAND(port, SetInterruptEnable, x);
6057dd7cddfSDavid du Colombier COMMAND(port, RxEnable, 0);
6067dd7cddfSDavid du Colombier COMMAND(port, TxEnable, 0);
6077dd7cddfSDavid du Colombier
6083ff48bf5SDavid du Colombier /*
6093ff48bf5SDavid du Colombier * If this is a CardBus card, acknowledge any interrupts.
6103ff48bf5SDavid du Colombier */
611e31d734fSDavid du Colombier if(ctlr->cbfn != nil)
612e31d734fSDavid du Colombier intrackcb(ctlr->cbfn);
6139a747e4fSDavid du Colombier
6147dd7cddfSDavid du Colombier /*
6157dd7cddfSDavid du Colombier * Prime the busmaster channel for receiving directly into a
6167dd7cddfSDavid du Colombier * receive packet buffer if necessary.
6177dd7cddfSDavid du Colombier */
6187dd7cddfSDavid du Colombier if(ctlr->busmaster == 1)
6197dd7cddfSDavid du Colombier startdma(ether, PADDR(ctlr->rbp->rp));
6207dd7cddfSDavid du Colombier else{
6217dd7cddfSDavid du Colombier if(ctlr->upenabled)
6227dd7cddfSDavid du Colombier outl(port+UpListPtr, PADDR(&ctlr->uphead->np));
6237dd7cddfSDavid du Colombier }
6247dd7cddfSDavid du Colombier
6257dd7cddfSDavid du Colombier ctlr->attached = 1;
6267dd7cddfSDavid du Colombier iunlock(&ctlr->wlock);
6277dd7cddfSDavid du Colombier }
6287dd7cddfSDavid du Colombier
6297dd7cddfSDavid du Colombier static void
statistics(Ether * ether)6307dd7cddfSDavid du Colombier statistics(Ether* ether)
6317dd7cddfSDavid du Colombier {
6327dd7cddfSDavid du Colombier int port, i, u, w;
6337dd7cddfSDavid du Colombier Ctlr *ctlr;
6347dd7cddfSDavid du Colombier
6357dd7cddfSDavid du Colombier port = ether->port;
6367dd7cddfSDavid du Colombier ctlr = ether->ctlr;
6377dd7cddfSDavid du Colombier
6387dd7cddfSDavid du Colombier /*
6397dd7cddfSDavid du Colombier * 3C59[27] require a read between a PIO write and
6407dd7cddfSDavid du Colombier * reading a statistics register.
6417dd7cddfSDavid du Colombier */
6427dd7cddfSDavid du Colombier w = (STATUS(port)>>13) & 0x07;
6437dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wstatistics);
6447dd7cddfSDavid du Colombier STATUS(port);
6457dd7cddfSDavid du Colombier
6467dd7cddfSDavid du Colombier for(i = 0; i < UpperFramesOk; i++)
6477dd7cddfSDavid du Colombier ctlr->stats[i] += inb(port+i) & 0xFF;
6487dd7cddfSDavid du Colombier u = inb(port+UpperFramesOk) & 0xFF;
6497dd7cddfSDavid du Colombier ctlr->stats[FramesXmittedOk] += (u & 0x30)<<4;
6507dd7cddfSDavid du Colombier ctlr->stats[FramesRcvdOk] += (u & 0x03)<<8;
6517dd7cddfSDavid du Colombier ctlr->stats[BytesRcvdOk] += ins(port+BytesRcvdOk) & 0xFFFF;
6527dd7cddfSDavid du Colombier ctlr->stats[BytesRcvdOk+1] += ins(port+BytesXmittedOk) & 0xFFFF;
6537dd7cddfSDavid du Colombier
6547dd7cddfSDavid du Colombier switch(ctlr->xcvr){
6557dd7cddfSDavid du Colombier
6567dd7cddfSDavid du Colombier case xcvrMii:
6577dd7cddfSDavid du Colombier case xcvr100BaseTX:
6587dd7cddfSDavid du Colombier case xcvr100BaseFX:
6597dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wdiagnostic);
6607dd7cddfSDavid du Colombier STATUS(port);
6617dd7cddfSDavid du Colombier ctlr->stats[BytesRcvdOk+2] += inb(port+BadSSD);
6627dd7cddfSDavid du Colombier break;
6637dd7cddfSDavid du Colombier }
6647dd7cddfSDavid du Colombier
6657dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, w);
6667dd7cddfSDavid du Colombier }
6677dd7cddfSDavid du Colombier
6687dd7cddfSDavid du Colombier static void
txstart(Ether * ether)6697dd7cddfSDavid du Colombier txstart(Ether* ether)
6707dd7cddfSDavid du Colombier {
6717dd7cddfSDavid du Colombier int port, len;
6727dd7cddfSDavid du Colombier Ctlr *ctlr;
6737dd7cddfSDavid du Colombier Block *bp;
6747dd7cddfSDavid du Colombier
6757dd7cddfSDavid du Colombier port = ether->port;
6767dd7cddfSDavid du Colombier ctlr = ether->ctlr;
6777dd7cddfSDavid du Colombier
6787dd7cddfSDavid du Colombier /*
6797dd7cddfSDavid du Colombier * Attempt to top-up the transmit FIFO. If there's room simply
6807dd7cddfSDavid du Colombier * stuff in the packet length (unpadded to a dword boundary), the
6817dd7cddfSDavid du Colombier * packet data (padded) and remove the packet from the queue.
6827dd7cddfSDavid du Colombier * If there's no room post an interrupt for when there is.
6837dd7cddfSDavid du Colombier * This routine is called both from the top level and from interrupt
6847dd7cddfSDavid du Colombier * level and expects to be called with ctlr->wlock already locked
6857dd7cddfSDavid du Colombier * and the correct register window (Wop) in place.
6867dd7cddfSDavid du Colombier */
6877dd7cddfSDavid du Colombier for(;;){
6887dd7cddfSDavid du Colombier if(ctlr->txbp){
6897dd7cddfSDavid du Colombier bp = ctlr->txbp;
6907dd7cddfSDavid du Colombier ctlr->txbp = 0;
6917dd7cddfSDavid du Colombier }
6927dd7cddfSDavid du Colombier else{
6937dd7cddfSDavid du Colombier bp = qget(ether->oq);
6947dd7cddfSDavid du Colombier if(bp == nil)
6957dd7cddfSDavid du Colombier break;
6967dd7cddfSDavid du Colombier }
6977dd7cddfSDavid du Colombier
6987dd7cddfSDavid du Colombier len = ROUNDUP(BLEN(bp), 4);
6997dd7cddfSDavid du Colombier if(len+4 <= ins(port+TxFree)){
7007dd7cddfSDavid du Colombier outl(port+Fifo, BLEN(bp));
7017dd7cddfSDavid du Colombier outsl(port+Fifo, bp->rp, len/4);
7027dd7cddfSDavid du Colombier
7037dd7cddfSDavid du Colombier freeb(bp);
7047dd7cddfSDavid du Colombier
7057dd7cddfSDavid du Colombier ether->outpackets++;
7067dd7cddfSDavid du Colombier }
7077dd7cddfSDavid du Colombier else{
7087dd7cddfSDavid du Colombier ctlr->txbp = bp;
7097dd7cddfSDavid du Colombier if(ctlr->txbusy == 0){
7107dd7cddfSDavid du Colombier ctlr->txbusy = 1;
7117dd7cddfSDavid du Colombier COMMAND(port, SetTxAvailableThresh, len>>ctlr->ts);
7127dd7cddfSDavid du Colombier }
7137dd7cddfSDavid du Colombier break;
7147dd7cddfSDavid du Colombier }
7157dd7cddfSDavid du Colombier }
7167dd7cddfSDavid du Colombier }
7177dd7cddfSDavid du Colombier
7187dd7cddfSDavid du Colombier static void
txstart905(Ether * ether)7197dd7cddfSDavid du Colombier txstart905(Ether* ether)
7207dd7cddfSDavid du Colombier {
7217dd7cddfSDavid du Colombier Ctlr *ctlr;
7227dd7cddfSDavid du Colombier int port, stalled, timeo;
7237dd7cddfSDavid du Colombier Block *bp;
7247dd7cddfSDavid du Colombier Pd *pd;
7257dd7cddfSDavid du Colombier
7267dd7cddfSDavid du Colombier ctlr = ether->ctlr;
7277dd7cddfSDavid du Colombier port = ether->port;
7287dd7cddfSDavid du Colombier
7297dd7cddfSDavid du Colombier /*
7307dd7cddfSDavid du Colombier * Free any completed packets.
7317dd7cddfSDavid du Colombier */
7327dd7cddfSDavid du Colombier pd = ctlr->dntail;
7337dd7cddfSDavid du Colombier while(ctlr->dnq){
7347dd7cddfSDavid du Colombier if(PADDR(&pd->np) == inl(port+DnListPtr))
7357dd7cddfSDavid du Colombier break;
7367dd7cddfSDavid du Colombier if(pd->bp){
7377dd7cddfSDavid du Colombier freeb(pd->bp);
7387dd7cddfSDavid du Colombier pd->bp = nil;
7397dd7cddfSDavid du Colombier }
7407dd7cddfSDavid du Colombier ctlr->dnq--;
7417dd7cddfSDavid du Colombier pd = pd->next;
7427dd7cddfSDavid du Colombier }
7437dd7cddfSDavid du Colombier ctlr->dntail = pd;
7447dd7cddfSDavid du Colombier
7457dd7cddfSDavid du Colombier stalled = 0;
7467dd7cddfSDavid du Colombier while(ctlr->dnq < (ctlr->ndn-1)){
7477dd7cddfSDavid du Colombier bp = qget(ether->oq);
7487dd7cddfSDavid du Colombier if(bp == nil)
7497dd7cddfSDavid du Colombier break;
7507dd7cddfSDavid du Colombier
7517dd7cddfSDavid du Colombier pd = ctlr->dnhead->next;
7527dd7cddfSDavid du Colombier pd->np = 0;
7537dd7cddfSDavid du Colombier pd->control = dnIndicate|BLEN(bp);
7547dd7cddfSDavid du Colombier pd->addr = PADDR(bp->rp);
7557dd7cddfSDavid du Colombier pd->len = updnLastFrag|BLEN(bp);
7567dd7cddfSDavid du Colombier pd->bp = bp;
7577dd7cddfSDavid du Colombier
7587dd7cddfSDavid du Colombier if(stalled == 0 && ctlr->dnq && inl(port+DnListPtr)){
7597dd7cddfSDavid du Colombier COMMAND(port, Stall, dnStall);
7607dd7cddfSDavid du Colombier for(timeo = 100; (STATUS(port) & commandInProgress) && timeo; timeo--)
7617dd7cddfSDavid du Colombier ;
7627dd7cddfSDavid du Colombier if(timeo == 0)
7637dd7cddfSDavid du Colombier print("#l%d: dnstall %d\n", ether->ctlrno, timeo);
7647dd7cddfSDavid du Colombier stalled = 1;
7657dd7cddfSDavid du Colombier }
7667dd7cddfSDavid du Colombier
7677dd7cddfSDavid du Colombier coherence();
7687dd7cddfSDavid du Colombier ctlr->dnhead->np = PADDR(&pd->np);
7697dd7cddfSDavid du Colombier ctlr->dnhead->control &= ~dnIndicate;
7707dd7cddfSDavid du Colombier ctlr->dnhead = pd;
7717dd7cddfSDavid du Colombier if(ctlr->dnq == 0)
7727dd7cddfSDavid du Colombier ctlr->dntail = pd;
7737dd7cddfSDavid du Colombier ctlr->dnq++;
7747dd7cddfSDavid du Colombier
7757dd7cddfSDavid du Colombier ctlr->dnqueued++;
7767dd7cddfSDavid du Colombier }
7777dd7cddfSDavid du Colombier
7787dd7cddfSDavid du Colombier if(ctlr->dnq > ctlr->dnqmax)
7797dd7cddfSDavid du Colombier ctlr->dnqmax = ctlr->dnq;
7807dd7cddfSDavid du Colombier
7817dd7cddfSDavid du Colombier /*
7827dd7cddfSDavid du Colombier * If the adapter is not currently processing anything
7837dd7cddfSDavid du Colombier * and there is something on the queue, start it processing.
7847dd7cddfSDavid du Colombier */
7857dd7cddfSDavid du Colombier if(inl(port+DnListPtr) == 0 && ctlr->dnq)
7867dd7cddfSDavid du Colombier outl(port+DnListPtr, PADDR(&ctlr->dnhead->np));
7877dd7cddfSDavid du Colombier if(stalled)
7887dd7cddfSDavid du Colombier COMMAND(port, Stall, dnUnStall);
7897dd7cddfSDavid du Colombier }
7907dd7cddfSDavid du Colombier
7917dd7cddfSDavid du Colombier static void
transmit(Ether * ether)7927dd7cddfSDavid du Colombier transmit(Ether* ether)
7937dd7cddfSDavid du Colombier {
7947dd7cddfSDavid du Colombier Ctlr *ctlr;
7957dd7cddfSDavid du Colombier int port, w;
7967dd7cddfSDavid du Colombier
7977dd7cddfSDavid du Colombier port = ether->port;
7987dd7cddfSDavid du Colombier ctlr = ether->ctlr;
7997dd7cddfSDavid du Colombier
8007dd7cddfSDavid du Colombier ilock(&ctlr->wlock);
8017dd7cddfSDavid du Colombier if(ctlr->dnenabled)
8027dd7cddfSDavid du Colombier txstart905(ether);
8037dd7cddfSDavid du Colombier else{
8047dd7cddfSDavid du Colombier w = (STATUS(port)>>13) & 0x07;
8057dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wop);
8067dd7cddfSDavid du Colombier txstart(ether);
8077dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, w);
8087dd7cddfSDavid du Colombier }
8097dd7cddfSDavid du Colombier iunlock(&ctlr->wlock);
8107dd7cddfSDavid du Colombier }
8117dd7cddfSDavid du Colombier
8127dd7cddfSDavid du Colombier static void
receive905(Ether * ether)8137dd7cddfSDavid du Colombier receive905(Ether* ether)
8147dd7cddfSDavid du Colombier {
8157dd7cddfSDavid du Colombier Ctlr *ctlr;
8167dd7cddfSDavid du Colombier int len, port, q;
8177dd7cddfSDavid du Colombier Pd *pd;
8187dd7cddfSDavid du Colombier Block *bp;
8197dd7cddfSDavid du Colombier
8207dd7cddfSDavid du Colombier ctlr = ether->ctlr;
8217dd7cddfSDavid du Colombier port = ether->port;
8227dd7cddfSDavid du Colombier
8237dd7cddfSDavid du Colombier if(inl(port+UpPktStatus) & upStalled)
8247dd7cddfSDavid du Colombier ctlr->upstalls++;
8257dd7cddfSDavid du Colombier q = 0;
8267dd7cddfSDavid du Colombier for(pd = ctlr->uphead; pd->control & upPktComplete; pd = pd->next){
8277dd7cddfSDavid du Colombier if(pd->control & upError){
8287dd7cddfSDavid du Colombier if(pd->control & upOverrun)
8297dd7cddfSDavid du Colombier ether->overflows++;
8307dd7cddfSDavid du Colombier if(pd->control & (upOversizedFrame|upRuntFrame))
8317dd7cddfSDavid du Colombier ether->buffs++;
8327dd7cddfSDavid du Colombier if(pd->control & upAlignmentError)
8337dd7cddfSDavid du Colombier ether->frames++;
8347dd7cddfSDavid du Colombier if(pd->control & upCRCError)
8357dd7cddfSDavid du Colombier ether->crcs++;
8367dd7cddfSDavid du Colombier }
8377dd7cddfSDavid du Colombier else if(bp = iallocb(sizeof(Etherpkt)+4)){
8387dd7cddfSDavid du Colombier len = pd->control & rxBytes;
8397dd7cddfSDavid du Colombier pd->bp->wp = pd->bp->rp+len;
8407dd7cddfSDavid du Colombier etheriq(ether, pd->bp, 1);
8417dd7cddfSDavid du Colombier pd->bp = bp;
8427dd7cddfSDavid du Colombier pd->addr = PADDR(bp->rp);
8437dd7cddfSDavid du Colombier coherence();
8447dd7cddfSDavid du Colombier }
8457dd7cddfSDavid du Colombier
8467dd7cddfSDavid du Colombier pd->control = 0;
8477dd7cddfSDavid du Colombier COMMAND(port, Stall, upUnStall);
8487dd7cddfSDavid du Colombier
8497dd7cddfSDavid du Colombier q++;
8507dd7cddfSDavid du Colombier }
8517dd7cddfSDavid du Colombier ctlr->uphead = pd;
8527dd7cddfSDavid du Colombier
8537dd7cddfSDavid du Colombier ctlr->upqueued += q;
8547dd7cddfSDavid du Colombier if(q > ctlr->upqmax)
8557dd7cddfSDavid du Colombier ctlr->upqmax = q;
8567dd7cddfSDavid du Colombier }
8577dd7cddfSDavid du Colombier
8587dd7cddfSDavid du Colombier static void
receive(Ether * ether)8597dd7cddfSDavid du Colombier receive(Ether* ether)
8607dd7cddfSDavid du Colombier {
8617dd7cddfSDavid du Colombier int len, port, rxerror, rxstatus;
8627dd7cddfSDavid du Colombier Ctlr *ctlr;
8637dd7cddfSDavid du Colombier Block *bp;
8647dd7cddfSDavid du Colombier
8657dd7cddfSDavid du Colombier port = ether->port;
8667dd7cddfSDavid du Colombier ctlr = ether->ctlr;
8677dd7cddfSDavid du Colombier
8687dd7cddfSDavid du Colombier while(((rxstatus = ins(port+RxStatus)) & rxIncomplete) == 0){
8697dd7cddfSDavid du Colombier if(ctlr->busmaster == 1 && (STATUS(port) & busMasterInProgress))
8707dd7cddfSDavid du Colombier break;
8717dd7cddfSDavid du Colombier
8727dd7cddfSDavid du Colombier /*
8737dd7cddfSDavid du Colombier * If there was an error, log it and continue.
8747dd7cddfSDavid du Colombier * Unfortunately the 3C5[078]9 has the error info in the status register
8757dd7cddfSDavid du Colombier * and the 3C59[0257] implement a separate RxError register.
8767dd7cddfSDavid du Colombier */
8777dd7cddfSDavid du Colombier if(rxstatus & rxError){
8787dd7cddfSDavid du Colombier if(ctlr->rxstatus9){
8797dd7cddfSDavid du Colombier switch(rxstatus & rxError9){
8807dd7cddfSDavid du Colombier
8817dd7cddfSDavid du Colombier case rxOverrun9:
8827dd7cddfSDavid du Colombier ether->overflows++;
8837dd7cddfSDavid du Colombier break;
8847dd7cddfSDavid du Colombier
8857dd7cddfSDavid du Colombier case oversizedFrame9:
8867dd7cddfSDavid du Colombier case runtFrame9:
8877dd7cddfSDavid du Colombier ether->buffs++;
8887dd7cddfSDavid du Colombier break;
8897dd7cddfSDavid du Colombier
8907dd7cddfSDavid du Colombier case alignmentError9:
8917dd7cddfSDavid du Colombier ether->frames++;
8927dd7cddfSDavid du Colombier break;
8937dd7cddfSDavid du Colombier
8947dd7cddfSDavid du Colombier case crcError9:
8957dd7cddfSDavid du Colombier ether->crcs++;
8967dd7cddfSDavid du Colombier break;
8977dd7cddfSDavid du Colombier
8987dd7cddfSDavid du Colombier }
8997dd7cddfSDavid du Colombier }
9007dd7cddfSDavid du Colombier else{
9017dd7cddfSDavid du Colombier rxerror = inb(port+RxError);
9027dd7cddfSDavid du Colombier if(rxerror & rxOverrun)
9037dd7cddfSDavid du Colombier ether->overflows++;
9047dd7cddfSDavid du Colombier if(rxerror & (oversizedFrame|runtFrame))
9057dd7cddfSDavid du Colombier ether->buffs++;
9067dd7cddfSDavid du Colombier if(rxerror & alignmentError)
9077dd7cddfSDavid du Colombier ether->frames++;
9087dd7cddfSDavid du Colombier if(rxerror & crcError)
9097dd7cddfSDavid du Colombier ether->crcs++;
9107dd7cddfSDavid du Colombier }
9117dd7cddfSDavid du Colombier }
9127dd7cddfSDavid du Colombier
9137dd7cddfSDavid du Colombier /*
9147dd7cddfSDavid du Colombier * If there was an error or a new receive buffer can't be
9157dd7cddfSDavid du Colombier * allocated, discard the packet and go on to the next.
9167dd7cddfSDavid du Colombier */
9177dd7cddfSDavid du Colombier if((rxstatus & rxError) || (bp = rbpalloc(iallocb)) == 0){
9187dd7cddfSDavid du Colombier COMMAND(port, RxDiscard, 0);
9197dd7cddfSDavid du Colombier while(STATUS(port) & commandInProgress)
9207dd7cddfSDavid du Colombier ;
9217dd7cddfSDavid du Colombier
9227dd7cddfSDavid du Colombier if(ctlr->busmaster == 1)
9237dd7cddfSDavid du Colombier startdma(ether, PADDR(ctlr->rbp->rp));
9247dd7cddfSDavid du Colombier
9257dd7cddfSDavid du Colombier continue;
9267dd7cddfSDavid du Colombier }
9277dd7cddfSDavid du Colombier
9287dd7cddfSDavid du Colombier /*
9297dd7cddfSDavid du Colombier * A valid receive packet awaits:
9307dd7cddfSDavid du Colombier * if using PIO, read it into the buffer;
9317dd7cddfSDavid du Colombier * discard the packet from the FIFO;
9327dd7cddfSDavid du Colombier * if using busmastering, start a new transfer for
9337dd7cddfSDavid du Colombier * the next packet and as a side-effect get the
9347dd7cddfSDavid du Colombier * end-pointer of the one just received;
9357dd7cddfSDavid du Colombier * pass the packet on to whoever wants it.
9367dd7cddfSDavid du Colombier */
9377dd7cddfSDavid du Colombier if(ctlr->busmaster == 0 || ctlr->busmaster == 2){
9387dd7cddfSDavid du Colombier len = (rxstatus & rxBytes9);
9397dd7cddfSDavid du Colombier ctlr->rbp->wp = ctlr->rbp->rp + len;
9407dd7cddfSDavid du Colombier insl(port+Fifo, ctlr->rbp->rp, HOWMANY(len, 4));
9417dd7cddfSDavid du Colombier }
9427dd7cddfSDavid du Colombier
9437dd7cddfSDavid du Colombier COMMAND(port, RxDiscard, 0);
9447dd7cddfSDavid du Colombier while(STATUS(port) & commandInProgress)
9457dd7cddfSDavid du Colombier ;
9467dd7cddfSDavid du Colombier
9477dd7cddfSDavid du Colombier if(ctlr->busmaster == 1)
9487dd7cddfSDavid du Colombier ctlr->rbp->wp = startdma(ether, PADDR(bp->rp));
9497dd7cddfSDavid du Colombier
9507dd7cddfSDavid du Colombier etheriq(ether, ctlr->rbp, 1);
9517dd7cddfSDavid du Colombier ctlr->rbp = bp;
9527dd7cddfSDavid du Colombier }
9537dd7cddfSDavid du Colombier }
9547dd7cddfSDavid du Colombier
9559a747e4fSDavid du Colombier static int
ejectable(int did)9569a747e4fSDavid du Colombier ejectable(int did)
9579a747e4fSDavid du Colombier {
9589a747e4fSDavid du Colombier switch (did) {
9599a747e4fSDavid du Colombier case 0x5157:
9609a747e4fSDavid du Colombier return 1;
9619a747e4fSDavid du Colombier
9629a747e4fSDavid du Colombier default:
9639a747e4fSDavid du Colombier return 0;
9649a747e4fSDavid du Colombier }
9659a747e4fSDavid du Colombier }
9669a747e4fSDavid du Colombier
9677dd7cddfSDavid du Colombier static void
interrupt(Ureg *,void * arg)9687dd7cddfSDavid du Colombier interrupt(Ureg*, void* arg)
9697dd7cddfSDavid du Colombier {
9707dd7cddfSDavid du Colombier Ether *ether;
97159cc4ca5SDavid du Colombier int port, status, s, txstatus, w, x;
9727dd7cddfSDavid du Colombier Ctlr *ctlr;
9737dd7cddfSDavid du Colombier
9747dd7cddfSDavid du Colombier ether = arg;
9757dd7cddfSDavid du Colombier port = ether->port;
9767dd7cddfSDavid du Colombier ctlr = ether->ctlr;
9777dd7cddfSDavid du Colombier
9787dd7cddfSDavid du Colombier ilock(&ctlr->wlock);
97959cc4ca5SDavid du Colombier status = STATUS(port);
98059cc4ca5SDavid du Colombier if(!(status & (interruptMask|interruptLatch))){
9813ff48bf5SDavid du Colombier ctlr->bogusinterrupts++;
98259cc4ca5SDavid du Colombier iunlock(&ctlr->wlock);
98359cc4ca5SDavid du Colombier return;
98459cc4ca5SDavid du Colombier }
98559cc4ca5SDavid du Colombier w = (status>>13) & 0x07;
9867dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wop);
9877dd7cddfSDavid du Colombier
9887dd7cddfSDavid du Colombier ctlr->interrupts++;
98959cc4ca5SDavid du Colombier if(ctlr->busmaster == 2)
9903ff48bf5SDavid du Colombier ctlr->timer[0] += inb(port+TIMER905) & 0xFF;
99159cc4ca5SDavid du Colombier else
9923ff48bf5SDavid du Colombier ctlr->timer[0] += inb(port+TIMER) & 0xFF;
99359cc4ca5SDavid du Colombier
99459cc4ca5SDavid du Colombier do{
9957dd7cddfSDavid du Colombier if(status & hostError){
9967dd7cddfSDavid du Colombier /*
9977dd7cddfSDavid du Colombier * Adapter failure, try to find out why, reset if
9987dd7cddfSDavid du Colombier * necessary. What happens if Tx is active and a reset
9997dd7cddfSDavid du Colombier * occurs, need to retransmit? This probably isn't right.
10007dd7cddfSDavid du Colombier */
10017dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wdiagnostic);
10027dd7cddfSDavid du Colombier x = ins(port+FifoDiagnostic);
10037dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wop);
10049a747e4fSDavid du Colombier
10059a747e4fSDavid du Colombier if (status == 0xFFFF && x == 0xFFFF && ejectable(ctlr->did)) {
10069a747e4fSDavid du Colombier print("#l%d: Card ejected?\n", ether->ctlrno);
10079a747e4fSDavid du Colombier iunlock(&ctlr->wlock);
10089a747e4fSDavid du Colombier return;
10099a747e4fSDavid du Colombier }
10109a747e4fSDavid du Colombier
10117dd7cddfSDavid du Colombier print("#l%d: status 0x%uX, diag 0x%uX\n",
10127dd7cddfSDavid du Colombier ether->ctlrno, status, x);
10137dd7cddfSDavid du Colombier
10147dd7cddfSDavid du Colombier if(x & txOverrun){
10157dd7cddfSDavid du Colombier if(ctlr->busmaster == 0)
10167dd7cddfSDavid du Colombier COMMAND(port, TxReset, 0);
10177dd7cddfSDavid du Colombier else
10187dd7cddfSDavid du Colombier COMMAND(port, TxReset, (updnReset|dmaReset));
10197dd7cddfSDavid du Colombier COMMAND(port, TxEnable, 0);
10207dd7cddfSDavid du Colombier }
10217dd7cddfSDavid du Colombier
10227dd7cddfSDavid du Colombier if(x & rxUnderrun){
10237dd7cddfSDavid du Colombier /*
10247dd7cddfSDavid du Colombier * This shouldn't happen...
10257dd7cddfSDavid du Colombier * Reset the receiver and restore the filter and RxEarly
10267dd7cddfSDavid du Colombier * threshold before re-enabling.
10277dd7cddfSDavid du Colombier * Need to restart any busmastering?
10287dd7cddfSDavid du Colombier */
10297dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wstate);
10307dd7cddfSDavid du Colombier s = (port+RxFilter) & 0x000F;
10317dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wop);
10327dd7cddfSDavid du Colombier COMMAND(port, RxReset, 0);
10337dd7cddfSDavid du Colombier while(STATUS(port) & commandInProgress)
10347dd7cddfSDavid du Colombier ;
10357dd7cddfSDavid du Colombier COMMAND(port, SetRxFilter, s);
10367dd7cddfSDavid du Colombier COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts);
10377dd7cddfSDavid du Colombier COMMAND(port, RxEnable, 0);
10387dd7cddfSDavid du Colombier }
10397dd7cddfSDavid du Colombier
10407dd7cddfSDavid du Colombier status &= ~hostError;
10417dd7cddfSDavid du Colombier }
10427dd7cddfSDavid du Colombier
10437dd7cddfSDavid du Colombier if(status & (transferInt|rxComplete)){
10447dd7cddfSDavid du Colombier receive(ether);
10457dd7cddfSDavid du Colombier status &= ~(transferInt|rxComplete);
10467dd7cddfSDavid du Colombier }
10477dd7cddfSDavid du Colombier
10487dd7cddfSDavid du Colombier if(status & (upComplete)){
10497dd7cddfSDavid du Colombier COMMAND(port, AcknowledgeInterrupt, upComplete);
10507dd7cddfSDavid du Colombier receive905(ether);
10517dd7cddfSDavid du Colombier status &= ~upComplete;
10527dd7cddfSDavid du Colombier ctlr->upinterrupts++;
10537dd7cddfSDavid du Colombier }
10547dd7cddfSDavid du Colombier
10557dd7cddfSDavid du Colombier if(status & txComplete){
10567dd7cddfSDavid du Colombier /*
10577dd7cddfSDavid du Colombier * Pop the TxStatus stack, accumulating errors.
10587dd7cddfSDavid du Colombier * Adjust the TX start threshold if there was an underrun.
10597dd7cddfSDavid du Colombier * If there was a Jabber or Underrun error, reset
10607dd7cddfSDavid du Colombier * the transmitter, taking care not to reset the dma logic
10617dd7cddfSDavid du Colombier * as a busmaster receive may be in progress.
10627dd7cddfSDavid du Colombier * For all conditions enable the transmitter.
10637dd7cddfSDavid du Colombier */
106459cc4ca5SDavid du Colombier if(ctlr->busmaster == 2)
106559cc4ca5SDavid du Colombier txstatus = port+TxStatus905;
106659cc4ca5SDavid du Colombier else
106759cc4ca5SDavid du Colombier txstatus = port+TxStatus;
10687dd7cddfSDavid du Colombier s = 0;
10697dd7cddfSDavid du Colombier do{
107059cc4ca5SDavid du Colombier if(x = inb(txstatus))
107159cc4ca5SDavid du Colombier outb(txstatus, 0);
10727dd7cddfSDavid du Colombier s |= x;
10737dd7cddfSDavid du Colombier }while(STATUS(port) & txComplete);
10747dd7cddfSDavid du Colombier
10757dd7cddfSDavid du Colombier if(s & txUnderrun){
10767dd7cddfSDavid du Colombier if(ctlr->dnenabled){
10777dd7cddfSDavid du Colombier while(inl(port+PktStatus) & dnInProg)
10787dd7cddfSDavid du Colombier ;
10797dd7cddfSDavid du Colombier }
10807dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wdiagnostic);
10817dd7cddfSDavid du Colombier while(ins(port+MediaStatus) & txInProg)
10827dd7cddfSDavid du Colombier ;
10837dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wop);
10847dd7cddfSDavid du Colombier if(ctlr->txthreshold < ETHERMAXTU)
10857dd7cddfSDavid du Colombier ctlr->txthreshold += ETHERMINTU;
10867dd7cddfSDavid du Colombier }
10877dd7cddfSDavid du Colombier
10887dd7cddfSDavid du Colombier /*
10897dd7cddfSDavid du Colombier * According to the manual, maxCollisions does not require
10907dd7cddfSDavid du Colombier * a TxReset, merely a TxEnable. However, evidence points to
10917dd7cddfSDavid du Colombier * it being necessary on the 3C905. The jury is still out.
10927dd7cddfSDavid du Colombier * On busy or badly configured networks maxCollisions can
10937dd7cddfSDavid du Colombier * happen frequently enough for messages to be annoying so
10947dd7cddfSDavid du Colombier * keep quiet about them by popular request.
10957dd7cddfSDavid du Colombier */
10967dd7cddfSDavid du Colombier if(s & (txJabber|txUnderrun|maxCollisions)){
10977dd7cddfSDavid du Colombier if(ctlr->busmaster == 0)
10987dd7cddfSDavid du Colombier COMMAND(port, TxReset, 0);
10997dd7cddfSDavid du Colombier else
11007dd7cddfSDavid du Colombier COMMAND(port, TxReset, (updnReset|dmaReset));
11017dd7cddfSDavid du Colombier while(STATUS(port) & commandInProgress)
11027dd7cddfSDavid du Colombier ;
11037dd7cddfSDavid du Colombier COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts);
11047dd7cddfSDavid du Colombier if(ctlr->busmaster == 2)
11057dd7cddfSDavid du Colombier outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256));
11067dd7cddfSDavid du Colombier if(ctlr->dnenabled)
11077dd7cddfSDavid du Colombier status |= dnComplete;
11087dd7cddfSDavid du Colombier }
11097dd7cddfSDavid du Colombier
11107dd7cddfSDavid du Colombier if(s & ~(txStatusComplete|maxCollisions))
11117dd7cddfSDavid du Colombier print("#l%d: txstatus 0x%uX, threshold %d\n",
11127dd7cddfSDavid du Colombier ether->ctlrno, s, ctlr->txthreshold);
11137dd7cddfSDavid du Colombier COMMAND(port, TxEnable, 0);
11147dd7cddfSDavid du Colombier ether->oerrs++;
11157dd7cddfSDavid du Colombier status &= ~txComplete;
11167dd7cddfSDavid du Colombier status |= txAvailable;
11177dd7cddfSDavid du Colombier }
11187dd7cddfSDavid du Colombier
11197dd7cddfSDavid du Colombier if(status & txAvailable){
11207dd7cddfSDavid du Colombier COMMAND(port, AcknowledgeInterrupt, txAvailable);
11217dd7cddfSDavid du Colombier ctlr->txbusy = 0;
11227dd7cddfSDavid du Colombier txstart(ether);
11237dd7cddfSDavid du Colombier status &= ~txAvailable;
11247dd7cddfSDavid du Colombier }
11257dd7cddfSDavid du Colombier
11267dd7cddfSDavid du Colombier if(status & dnComplete){
11277dd7cddfSDavid du Colombier COMMAND(port, AcknowledgeInterrupt, dnComplete);
11287dd7cddfSDavid du Colombier txstart905(ether);
11297dd7cddfSDavid du Colombier status &= ~dnComplete;
11307dd7cddfSDavid du Colombier ctlr->dninterrupts++;
11317dd7cddfSDavid du Colombier }
11327dd7cddfSDavid du Colombier
11337dd7cddfSDavid du Colombier if(status & updateStats){
11347dd7cddfSDavid du Colombier statistics(ether);
11357dd7cddfSDavid du Colombier status &= ~updateStats;
11367dd7cddfSDavid du Colombier }
11377dd7cddfSDavid du Colombier
11387dd7cddfSDavid du Colombier /*
11397dd7cddfSDavid du Colombier * Currently, this shouldn't happen.
11407dd7cddfSDavid du Colombier */
11417dd7cddfSDavid du Colombier if(status & rxEarly){
11427dd7cddfSDavid du Colombier COMMAND(port, AcknowledgeInterrupt, rxEarly);
11437dd7cddfSDavid du Colombier status &= ~rxEarly;
11447dd7cddfSDavid du Colombier }
11457dd7cddfSDavid du Colombier
11467dd7cddfSDavid du Colombier /*
11477dd7cddfSDavid du Colombier * Panic if there are any interrupts not dealt with.
11487dd7cddfSDavid du Colombier */
11497dd7cddfSDavid du Colombier if(status & interruptMask)
11507dd7cddfSDavid du Colombier panic("#l%d: interrupt mask 0x%uX\n", ether->ctlrno, status);
11517dd7cddfSDavid du Colombier
11527dd7cddfSDavid du Colombier COMMAND(port, AcknowledgeInterrupt, interruptLatch);
1153e31d734fSDavid du Colombier if(ctlr->cbfn != nil)
1154e31d734fSDavid du Colombier intrackcb(ctlr->cbfn);
11559a747e4fSDavid du Colombier
115659cc4ca5SDavid du Colombier }while((status = STATUS(port)) & (interruptMask|interruptLatch));
115759cc4ca5SDavid du Colombier
115859cc4ca5SDavid du Colombier if(ctlr->busmaster == 2)
11593ff48bf5SDavid du Colombier ctlr->timer[1] += inb(port+TIMER905) & 0xFF;
116059cc4ca5SDavid du Colombier else
11613ff48bf5SDavid du Colombier ctlr->timer[1] += inb(port+TIMER) & 0xFF;
11627dd7cddfSDavid du Colombier
11637dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, w);
11647dd7cddfSDavid du Colombier iunlock(&ctlr->wlock);
11657dd7cddfSDavid du Colombier }
11667dd7cddfSDavid du Colombier
11677dd7cddfSDavid du Colombier static long
ifstat(Ether * ether,void * a,long n,ulong offset)11687dd7cddfSDavid du Colombier ifstat(Ether* ether, void* a, long n, ulong offset)
11697dd7cddfSDavid du Colombier {
11707dd7cddfSDavid du Colombier char *p;
11717dd7cddfSDavid du Colombier int len;
11727dd7cddfSDavid du Colombier Ctlr *ctlr;
11737dd7cddfSDavid du Colombier
11747dd7cddfSDavid du Colombier if(n == 0)
11757dd7cddfSDavid du Colombier return 0;
11767dd7cddfSDavid du Colombier
11777dd7cddfSDavid du Colombier ctlr = ether->ctlr;
11787dd7cddfSDavid du Colombier
11797dd7cddfSDavid du Colombier ilock(&ctlr->wlock);
11807dd7cddfSDavid du Colombier statistics(ether);
11817dd7cddfSDavid du Colombier iunlock(&ctlr->wlock);
11827dd7cddfSDavid du Colombier
11837dd7cddfSDavid du Colombier p = malloc(READSTR);
1184*aa72973aSDavid du Colombier if(p == nil)
1185*aa72973aSDavid du Colombier error(Enomem);
11867dd7cddfSDavid du Colombier len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts);
11873ff48bf5SDavid du Colombier len += snprint(p+len, READSTR-len, "bogusinterrupts: %lud\n", ctlr->bogusinterrupts);
118859cc4ca5SDavid du Colombier len += snprint(p+len, READSTR-len, "timer: %lud %lud\n",
118959cc4ca5SDavid du Colombier ctlr->timer[0], ctlr->timer[1]);
119059cc4ca5SDavid du Colombier len += snprint(p+len, READSTR-len, "carrierlost: %lud\n",
119159cc4ca5SDavid du Colombier ctlr->stats[CarrierLost]);
119259cc4ca5SDavid du Colombier len += snprint(p+len, READSTR-len, "sqeerrors: %lud\n",
119359cc4ca5SDavid du Colombier ctlr->stats[SqeErrors]);
119459cc4ca5SDavid du Colombier len += snprint(p+len, READSTR-len, "multiplecolls: %lud\n",
119559cc4ca5SDavid du Colombier ctlr->stats[MultipleColls]);
119659cc4ca5SDavid du Colombier len += snprint(p+len, READSTR-len, "singlecollframes: %lud\n",
119759cc4ca5SDavid du Colombier ctlr->stats[SingleCollFrames]);
119859cc4ca5SDavid du Colombier len += snprint(p+len, READSTR-len, "latecollisions: %lud\n",
119959cc4ca5SDavid du Colombier ctlr->stats[LateCollisions]);
120059cc4ca5SDavid du Colombier len += snprint(p+len, READSTR-len, "rxoverruns: %lud\n",
120159cc4ca5SDavid du Colombier ctlr->stats[RxOverruns]);
120259cc4ca5SDavid du Colombier len += snprint(p+len, READSTR-len, "framesxmittedok: %lud\n",
120359cc4ca5SDavid du Colombier ctlr->stats[FramesXmittedOk]);
120459cc4ca5SDavid du Colombier len += snprint(p+len, READSTR-len, "framesrcvdok: %lud\n",
120559cc4ca5SDavid du Colombier ctlr->stats[FramesRcvdOk]);
120659cc4ca5SDavid du Colombier len += snprint(p+len, READSTR-len, "framesdeferred: %lud\n",
120759cc4ca5SDavid du Colombier ctlr->stats[FramesDeferred]);
120859cc4ca5SDavid du Colombier len += snprint(p+len, READSTR-len, "bytesrcvdok: %lud\n",
120959cc4ca5SDavid du Colombier ctlr->stats[BytesRcvdOk]);
121059cc4ca5SDavid du Colombier len += snprint(p+len, READSTR-len, "bytesxmittedok: %lud\n",
121159cc4ca5SDavid du Colombier ctlr->stats[BytesRcvdOk+1]);
12127dd7cddfSDavid du Colombier
12137dd7cddfSDavid du Colombier if(ctlr->upenabled){
12147dd7cddfSDavid du Colombier if(ctlr->upqmax > ctlr->upqmaxhw)
12157dd7cddfSDavid du Colombier ctlr->upqmaxhw = ctlr->upqmax;
12167dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "up: q %lud i %lud m %d h %d s %lud\n",
12177dd7cddfSDavid du Colombier ctlr->upqueued, ctlr->upinterrupts,
12187dd7cddfSDavid du Colombier ctlr->upqmax, ctlr->upqmaxhw, ctlr->upstalls);
12197dd7cddfSDavid du Colombier ctlr->upqmax = 0;
12207dd7cddfSDavid du Colombier }
12217dd7cddfSDavid du Colombier if(ctlr->dnenabled){
12227dd7cddfSDavid du Colombier if(ctlr->dnqmax > ctlr->dnqmaxhw)
12237dd7cddfSDavid du Colombier ctlr->dnqmaxhw = ctlr->dnqmax;
12247dd7cddfSDavid du Colombier len += snprint(p+len, READSTR-len, "dn: q %lud i %lud m %d h %d\n",
12257dd7cddfSDavid du Colombier ctlr->dnqueued, ctlr->dninterrupts, ctlr->dnqmax, ctlr->dnqmaxhw);
12267dd7cddfSDavid du Colombier ctlr->dnqmax = 0;
12277dd7cddfSDavid du Colombier }
12287dd7cddfSDavid du Colombier
12297dd7cddfSDavid du Colombier snprint(p+len, READSTR-len, "badssd: %lud\n", ctlr->stats[BytesRcvdOk+2]);
12307dd7cddfSDavid du Colombier
12317dd7cddfSDavid du Colombier n = readstr(offset, a, n, p);
12327dd7cddfSDavid du Colombier free(p);
12337dd7cddfSDavid du Colombier
12347dd7cddfSDavid du Colombier return n;
12357dd7cddfSDavid du Colombier }
12367dd7cddfSDavid du Colombier
12377dd7cddfSDavid du Colombier static void
txrxreset(int port)12387dd7cddfSDavid du Colombier txrxreset(int port)
12397dd7cddfSDavid du Colombier {
12407dd7cddfSDavid du Colombier COMMAND(port, TxReset, 0);
12417dd7cddfSDavid du Colombier while(STATUS(port) & commandInProgress)
12427dd7cddfSDavid du Colombier ;
12437dd7cddfSDavid du Colombier COMMAND(port, RxReset, 0);
12447dd7cddfSDavid du Colombier while(STATUS(port) & commandInProgress)
12457dd7cddfSDavid du Colombier ;
12467dd7cddfSDavid du Colombier }
12477dd7cddfSDavid du Colombier
12483ff48bf5SDavid du Colombier static Ctlr*
tcmadapter(int port,int irq,Pcidev * pcidev)12493ff48bf5SDavid du Colombier tcmadapter(int port, int irq, Pcidev* pcidev)
12507dd7cddfSDavid du Colombier {
12513ff48bf5SDavid du Colombier Ctlr *ctlr;
12527dd7cddfSDavid du Colombier
12533ff48bf5SDavid du Colombier ctlr = malloc(sizeof(Ctlr));
1254*aa72973aSDavid du Colombier if(ctlr == nil)
1255*aa72973aSDavid du Colombier error(Enomem);
12563ff48bf5SDavid du Colombier ctlr->port = port;
12573ff48bf5SDavid du Colombier ctlr->irq = irq;
12583ff48bf5SDavid du Colombier ctlr->pcidev = pcidev;
1259e31d734fSDavid du Colombier ctlr->eepromcmd = EepromReadRegister;
12607dd7cddfSDavid du Colombier
12613ff48bf5SDavid du Colombier if(ctlrhead != nil)
12623ff48bf5SDavid du Colombier ctlrtail->next = ctlr;
12633ff48bf5SDavid du Colombier else
12643ff48bf5SDavid du Colombier ctlrhead = ctlr;
12653ff48bf5SDavid du Colombier ctlrtail = ctlr;
12663ff48bf5SDavid du Colombier
12673ff48bf5SDavid du Colombier return ctlr;
12687dd7cddfSDavid du Colombier }
12697dd7cddfSDavid du Colombier
12707dd7cddfSDavid du Colombier /*
12717dd7cddfSDavid du Colombier * Write two 0 bytes to identify the IDport and then reset the
12727dd7cddfSDavid du Colombier * ID sequence. Then send the ID sequence to the card to get
12737dd7cddfSDavid du Colombier * the card into command state.
12747dd7cddfSDavid du Colombier */
12757dd7cddfSDavid du Colombier static void
idseq(void)12767dd7cddfSDavid du Colombier idseq(void)
12777dd7cddfSDavid du Colombier {
12787dd7cddfSDavid du Colombier int i;
12797dd7cddfSDavid du Colombier uchar al;
12807dd7cddfSDavid du Colombier static int reset, untag;
12817dd7cddfSDavid du Colombier
12827dd7cddfSDavid du Colombier /*
12837dd7cddfSDavid du Colombier * One time only:
12847dd7cddfSDavid du Colombier * reset any adapters listening
12857dd7cddfSDavid du Colombier */
12867dd7cddfSDavid du Colombier if(reset == 0){
12877dd7cddfSDavid du Colombier outb(IDport, 0);
12887dd7cddfSDavid du Colombier outb(IDport, 0);
12897dd7cddfSDavid du Colombier outb(IDport, 0xC0);
12907dd7cddfSDavid du Colombier delay(20);
12917dd7cddfSDavid du Colombier reset = 1;
12927dd7cddfSDavid du Colombier }
12937dd7cddfSDavid du Colombier
12947dd7cddfSDavid du Colombier outb(IDport, 0);
12957dd7cddfSDavid du Colombier outb(IDport, 0);
12967dd7cddfSDavid du Colombier for(al = 0xFF, i = 0; i < 255; i++){
12977dd7cddfSDavid du Colombier outb(IDport, al);
12987dd7cddfSDavid du Colombier if(al & 0x80){
12997dd7cddfSDavid du Colombier al <<= 1;
13007dd7cddfSDavid du Colombier al ^= 0xCF;
13017dd7cddfSDavid du Colombier }
13027dd7cddfSDavid du Colombier else
13037dd7cddfSDavid du Colombier al <<= 1;
13047dd7cddfSDavid du Colombier }
13057dd7cddfSDavid du Colombier
13067dd7cddfSDavid du Colombier /*
13077dd7cddfSDavid du Colombier * One time only:
13087dd7cddfSDavid du Colombier * write ID sequence to get the attention of all adapters;
13097dd7cddfSDavid du Colombier * untag all adapters.
13107dd7cddfSDavid du Colombier * If a global reset is done here on all adapters it will confuse
13117dd7cddfSDavid du Colombier * any ISA cards configured for EISA mode.
13127dd7cddfSDavid du Colombier */
13137dd7cddfSDavid du Colombier if(untag == 0){
13147dd7cddfSDavid du Colombier outb(IDport, 0xD0);
13157dd7cddfSDavid du Colombier untag = 1;
13167dd7cddfSDavid du Colombier }
13177dd7cddfSDavid du Colombier }
13187dd7cddfSDavid du Colombier
13197dd7cddfSDavid du Colombier static ulong
activate(void)13207dd7cddfSDavid du Colombier activate(void)
13217dd7cddfSDavid du Colombier {
13227dd7cddfSDavid du Colombier int i;
13237dd7cddfSDavid du Colombier ushort x, acr;
13247dd7cddfSDavid du Colombier
13257dd7cddfSDavid du Colombier /*
13267dd7cddfSDavid du Colombier * Do the little configuration dance:
13277dd7cddfSDavid du Colombier *
13287dd7cddfSDavid du Colombier * 2. write the ID sequence to get to command state.
13297dd7cddfSDavid du Colombier */
13307dd7cddfSDavid du Colombier idseq();
13317dd7cddfSDavid du Colombier
13327dd7cddfSDavid du Colombier /*
13337dd7cddfSDavid du Colombier * 3. Read the Manufacturer ID from the EEPROM.
13347dd7cddfSDavid du Colombier * This is done by writing the IDPort with 0x87 (0x80
13357dd7cddfSDavid du Colombier * is the 'read EEPROM' command, 0x07 is the offset of
13367dd7cddfSDavid du Colombier * the Manufacturer ID field in the EEPROM).
13377dd7cddfSDavid du Colombier * The data comes back 1 bit at a time.
13387dd7cddfSDavid du Colombier * A delay seems necessary between reading the bits.
13397dd7cddfSDavid du Colombier *
13407dd7cddfSDavid du Colombier * If the ID doesn't match, there are no more adapters.
13417dd7cddfSDavid du Colombier */
13427dd7cddfSDavid du Colombier outb(IDport, 0x87);
13437dd7cddfSDavid du Colombier delay(20);
13447dd7cddfSDavid du Colombier for(x = 0, i = 0; i < 16; i++){
13457dd7cddfSDavid du Colombier delay(20);
13467dd7cddfSDavid du Colombier x <<= 1;
13477dd7cddfSDavid du Colombier x |= inb(IDport) & 0x01;
13487dd7cddfSDavid du Colombier }
13497dd7cddfSDavid du Colombier if(x != 0x6D50)
13507dd7cddfSDavid du Colombier return 0;
13517dd7cddfSDavid du Colombier
13527dd7cddfSDavid du Colombier /*
13537dd7cddfSDavid du Colombier * 3. Read the Address Configuration from the EEPROM.
13547dd7cddfSDavid du Colombier * The Address Configuration field is at offset 0x08 in the EEPROM).
13557dd7cddfSDavid du Colombier */
13567dd7cddfSDavid du Colombier outb(IDport, 0x88);
13577dd7cddfSDavid du Colombier for(acr = 0, i = 0; i < 16; i++){
13587dd7cddfSDavid du Colombier delay(20);
13597dd7cddfSDavid du Colombier acr <<= 1;
13607dd7cddfSDavid du Colombier acr |= inb(IDport) & 0x01;
13617dd7cddfSDavid du Colombier }
13627dd7cddfSDavid du Colombier
13637dd7cddfSDavid du Colombier return (acr & 0x1F)*0x10 + 0x200;
13647dd7cddfSDavid du Colombier }
13657dd7cddfSDavid du Colombier
13667dd7cddfSDavid du Colombier static void
tcm509isa(void)13677dd7cddfSDavid du Colombier tcm509isa(void)
13687dd7cddfSDavid du Colombier {
13697dd7cddfSDavid du Colombier int irq, port;
13707dd7cddfSDavid du Colombier
13717dd7cddfSDavid du Colombier /*
13727dd7cddfSDavid du Colombier * Attempt to activate all adapters. If adapter is set for
13737dd7cddfSDavid du Colombier * EISA mode (0x3F0), tag it and ignore. Otherwise, activate
13747dd7cddfSDavid du Colombier * it fully.
13757dd7cddfSDavid du Colombier */
13767dd7cddfSDavid du Colombier while(port = activate()){
13773ff48bf5SDavid du Colombier if(ioalloc(port, 0x10, 0, "tcm509isa") < 0){
13783ff48bf5SDavid du Colombier print("tcm509isa: port 0x%uX in use\n", port);
13797dd7cddfSDavid du Colombier continue;
13803ff48bf5SDavid du Colombier }
13817dd7cddfSDavid du Colombier
13827dd7cddfSDavid du Colombier /*
13837dd7cddfSDavid du Colombier * 6. Tag the adapter so it won't respond in future.
13847dd7cddfSDavid du Colombier */
13857dd7cddfSDavid du Colombier outb(IDport, 0xD1);
13867dd7cddfSDavid du Colombier if(port == 0x3F0){
13877dd7cddfSDavid du Colombier iofree(port);
13887dd7cddfSDavid du Colombier continue;
13897dd7cddfSDavid du Colombier }
13907dd7cddfSDavid du Colombier
13917dd7cddfSDavid du Colombier /*
13927dd7cddfSDavid du Colombier * 6. Activate the adapter by writing the Activate command
13937dd7cddfSDavid du Colombier * (0xFF).
13947dd7cddfSDavid du Colombier */
13957dd7cddfSDavid du Colombier outb(IDport, 0xFF);
13967dd7cddfSDavid du Colombier delay(20);
13977dd7cddfSDavid du Colombier
13987dd7cddfSDavid du Colombier /*
13997dd7cddfSDavid du Colombier * 8. Can now talk to the adapter's I/O base addresses.
14007dd7cddfSDavid du Colombier * Use the I/O base address from the acr just read.
14017dd7cddfSDavid du Colombier *
14027dd7cddfSDavid du Colombier * Enable the adapter and clear out any lingering status
14037dd7cddfSDavid du Colombier * and interrupts.
14047dd7cddfSDavid du Colombier */
14057dd7cddfSDavid du Colombier while(STATUS(port) & commandInProgress)
14067dd7cddfSDavid du Colombier ;
14077dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wsetup);
14087dd7cddfSDavid du Colombier outs(port+ConfigControl, Ena);
14097dd7cddfSDavid du Colombier
14107dd7cddfSDavid du Colombier txrxreset(port);
14117dd7cddfSDavid du Colombier COMMAND(port, AcknowledgeInterrupt, 0xFF);
14127dd7cddfSDavid du Colombier
14137dd7cddfSDavid du Colombier irq = (ins(port+ResourceConfig)>>12) & 0x0F;
14143ff48bf5SDavid du Colombier tcmadapter(port, irq, nil);
14157dd7cddfSDavid du Colombier }
14167dd7cddfSDavid du Colombier }
14177dd7cddfSDavid du Colombier
14187dd7cddfSDavid du Colombier static void
tcm5XXeisa(void)14197dd7cddfSDavid du Colombier tcm5XXeisa(void)
14207dd7cddfSDavid du Colombier {
14217dd7cddfSDavid du Colombier ushort x;
14227dd7cddfSDavid du Colombier int irq, port, slot;
14237dd7cddfSDavid du Colombier
14247dd7cddfSDavid du Colombier /*
14257dd7cddfSDavid du Colombier * Check if this is an EISA machine.
14267dd7cddfSDavid du Colombier * If not, nothing to do.
14277dd7cddfSDavid du Colombier */
14287dd7cddfSDavid du Colombier if(strncmp((char*)KADDR(0xFFFD9), "EISA", 4))
14297dd7cddfSDavid du Colombier return;
14307dd7cddfSDavid du Colombier
14317dd7cddfSDavid du Colombier /*
14327dd7cddfSDavid du Colombier * Continue through the EISA slots looking for a match on both
14337dd7cddfSDavid du Colombier * 3COM as the manufacturer and 3C579-* or 3C59[27]-* as the product.
14347dd7cddfSDavid du Colombier * If an adapter is found, select window 0, enable it and clear
14357dd7cddfSDavid du Colombier * out any lingering status and interrupts.
14367dd7cddfSDavid du Colombier */
14377dd7cddfSDavid du Colombier for(slot = 1; slot < MaxEISA; slot++){
14387dd7cddfSDavid du Colombier port = slot*0x1000;
14393ff48bf5SDavid du Colombier if(ioalloc(port, 0x1000, 0, "tcm5XXeisa") < 0){
14403ff48bf5SDavid du Colombier print("tcm5XXeisa: port 0x%uX in use\n", port);
14417dd7cddfSDavid du Colombier continue;
14423ff48bf5SDavid du Colombier }
14437dd7cddfSDavid du Colombier if(ins(port+0xC80+ManufacturerID) != 0x6D50){
14447dd7cddfSDavid du Colombier iofree(port);
14457dd7cddfSDavid du Colombier continue;
14467dd7cddfSDavid du Colombier }
14477dd7cddfSDavid du Colombier x = ins(port+0xC80+ProductID);
14487dd7cddfSDavid du Colombier if((x & 0xF0FF) != 0x9050 && (x & 0xFF00) != 0x5900){
14497dd7cddfSDavid du Colombier iofree(port);
14507dd7cddfSDavid du Colombier continue;
14517dd7cddfSDavid du Colombier }
14527dd7cddfSDavid du Colombier
14537dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wsetup);
14547dd7cddfSDavid du Colombier outs(port+ConfigControl, Ena);
14557dd7cddfSDavid du Colombier
14567dd7cddfSDavid du Colombier txrxreset(port);
14577dd7cddfSDavid du Colombier COMMAND(port, AcknowledgeInterrupt, 0xFF);
14587dd7cddfSDavid du Colombier
14597dd7cddfSDavid du Colombier irq = (ins(port+ResourceConfig)>>12) & 0x0F;
14603ff48bf5SDavid du Colombier tcmadapter(port, irq, nil);
14617dd7cddfSDavid du Colombier }
14627dd7cddfSDavid du Colombier }
14637dd7cddfSDavid du Colombier
14647dd7cddfSDavid du Colombier static void
tcm59Xpci(void)14657dd7cddfSDavid du Colombier tcm59Xpci(void)
14667dd7cddfSDavid du Colombier {
14677dd7cddfSDavid du Colombier Pcidev *p;
1468e31d734fSDavid du Colombier Ctlr *ctlr;
14697dd7cddfSDavid du Colombier int irq, port;
14707dd7cddfSDavid du Colombier
14717dd7cddfSDavid du Colombier p = nil;
14727dd7cddfSDavid du Colombier while(p = pcimatch(p, 0x10B7, 0)){
1473e31d734fSDavid du Colombier if(p->ccrb != 0x02 || p->ccru != 0)
1474e31d734fSDavid du Colombier continue;
14759a747e4fSDavid du Colombier /*
14769a747e4fSDavid du Colombier * Not prepared to deal with memory-mapped
14779a747e4fSDavid du Colombier * devices yet.
14789a747e4fSDavid du Colombier */
14799a747e4fSDavid du Colombier if(!(p->mem[0].bar & 0x01))
14807dd7cddfSDavid du Colombier continue;
14819a747e4fSDavid du Colombier port = p->mem[0].bar & ~0x01;
14829a747e4fSDavid du Colombier if((port = ioalloc((port == 0)? -1: port, p->mem[0].size,
14833ff48bf5SDavid du Colombier 0, "tcm59Xpci")) < 0){
14843ff48bf5SDavid du Colombier print("tcm59Xpci: port 0x%uX in use\n", port);
14859a747e4fSDavid du Colombier continue;
14863ff48bf5SDavid du Colombier }
14877dd7cddfSDavid du Colombier irq = p->intl;
14887dd7cddfSDavid du Colombier
1489e31d734fSDavid du Colombier txrxreset(port);
1490e31d734fSDavid du Colombier COMMAND(port, AcknowledgeInterrupt, 0xFF);
1491e31d734fSDavid du Colombier
1492e31d734fSDavid du Colombier ctlr = tcmadapter(port, irq, p);
1493e31d734fSDavid du Colombier switch(p->did){
1494e31d734fSDavid du Colombier default:
1495e31d734fSDavid du Colombier break;
1496e31d734fSDavid du Colombier case 0x5157:
1497e31d734fSDavid du Colombier ctlr->eepromcmd = EepromRead8bRegister;
14984de34a7eSDavid du Colombier ctlr->cbfnpa = p->mem[2].bar&~0x0F;
14994de34a7eSDavid du Colombier ctlr->cbfn = vmap(p->mem[2].bar&~0x0F, p->mem[2].size);
1500e31d734fSDavid du Colombier break;
1501e31d734fSDavid du Colombier case 0x6056:
1502e31d734fSDavid du Colombier ctlr->eepromcmd = EepromReadOffRegister;
15034de34a7eSDavid du Colombier ctlr->cbfnpa = p->mem[2].bar&~0x0F;
15044de34a7eSDavid du Colombier ctlr->cbfn = vmap(p->mem[2].bar&~0x0F, p->mem[2].size);
1505e31d734fSDavid du Colombier break;
1506e31d734fSDavid du Colombier }
15077dd7cddfSDavid du Colombier pcisetbme(p);
15087dd7cddfSDavid du Colombier }
15097dd7cddfSDavid du Colombier }
15107dd7cddfSDavid du Colombier
15117dd7cddfSDavid du Colombier static char* tcmpcmcia[] = {
15127dd7cddfSDavid du Colombier "3C589", /* 3COM 589[ABCD] */
15137dd7cddfSDavid du Colombier "3C562", /* 3COM 562 */
15147dd7cddfSDavid du Colombier "589E", /* 3COM Megahertz 589E */
15157dd7cddfSDavid du Colombier nil,
15167dd7cddfSDavid du Colombier };
15177dd7cddfSDavid du Colombier
15183ff48bf5SDavid du Colombier static Ctlr*
tcm5XXpcmcia(Ether * ether)15197dd7cddfSDavid du Colombier tcm5XXpcmcia(Ether* ether)
15207dd7cddfSDavid du Colombier {
15217dd7cddfSDavid du Colombier int i;
15223ff48bf5SDavid du Colombier Ctlr *ctlr;
15233ff48bf5SDavid du Colombier
15243ff48bf5SDavid du Colombier if(ether->type == nil)
15253ff48bf5SDavid du Colombier return nil;
15267dd7cddfSDavid du Colombier
15277dd7cddfSDavid du Colombier for(i = 0; tcmpcmcia[i] != nil; i++){
15283ff48bf5SDavid du Colombier if(cistrcmp(ether->type, tcmpcmcia[i]))
15293ff48bf5SDavid du Colombier continue;
15303ff48bf5SDavid du Colombier ctlr = tcmadapter(ether->port, ether->irq, nil);
15313ff48bf5SDavid du Colombier ctlr->active = 1;
15323ff48bf5SDavid du Colombier return ctlr;
15337dd7cddfSDavid du Colombier }
1534b985bfb9SDavid du Colombier
15353ff48bf5SDavid du Colombier return nil;
15367dd7cddfSDavid du Colombier }
15377dd7cddfSDavid du Colombier
15387dd7cddfSDavid du Colombier static void
setxcvr(Ctlr * ctlr,int xcvr)15393ff48bf5SDavid du Colombier setxcvr(Ctlr* ctlr, int xcvr)
15407dd7cddfSDavid du Colombier {
15413ff48bf5SDavid du Colombier int port, x;
15427dd7cddfSDavid du Colombier
15433ff48bf5SDavid du Colombier port = ctlr->port;
15443ff48bf5SDavid du Colombier if(ctlr->rxstatus9){
15457dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wsetup);
15467dd7cddfSDavid du Colombier x = ins(port+AddressConfig) & ~xcvrMask9;
15477dd7cddfSDavid du Colombier x |= (xcvr>>20)<<14;
15487dd7cddfSDavid du Colombier outs(port+AddressConfig, x);
15497dd7cddfSDavid du Colombier }
15507dd7cddfSDavid du Colombier else{
15517dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wfifo);
15527dd7cddfSDavid du Colombier x = inl(port+InternalConfig) & ~xcvrMask;
15537dd7cddfSDavid du Colombier x |= xcvr;
15547dd7cddfSDavid du Colombier outl(port+InternalConfig, x);
15557dd7cddfSDavid du Colombier }
15567dd7cddfSDavid du Colombier
15577dd7cddfSDavid du Colombier txrxreset(port);
15587dd7cddfSDavid du Colombier }
15597dd7cddfSDavid du Colombier
15607dd7cddfSDavid du Colombier static void
setfullduplex(int port)15617dd7cddfSDavid du Colombier setfullduplex(int port)
15627dd7cddfSDavid du Colombier {
15637dd7cddfSDavid du Colombier int x;
15647dd7cddfSDavid du Colombier
15657dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wfifo);
15667dd7cddfSDavid du Colombier x = ins(port+MacControl);
15677dd7cddfSDavid du Colombier outs(port+MacControl, fullDuplexEnable|x);
15687dd7cddfSDavid du Colombier
15697dd7cddfSDavid du Colombier txrxreset(port);
15707dd7cddfSDavid du Colombier }
15717dd7cddfSDavid du Colombier
15727dd7cddfSDavid du Colombier static int
miimdi(int port,int n)15737dd7cddfSDavid du Colombier miimdi(int port, int n)
15747dd7cddfSDavid du Colombier {
15757dd7cddfSDavid du Colombier int data, i;
15767dd7cddfSDavid du Colombier
15777dd7cddfSDavid du Colombier /*
15787dd7cddfSDavid du Colombier * Read n bits from the MII Management Register.
15797dd7cddfSDavid du Colombier */
15807dd7cddfSDavid du Colombier data = 0;
15817dd7cddfSDavid du Colombier for(i = n-1; i >= 0; i--){
15827dd7cddfSDavid du Colombier if(ins(port) & mgmtData)
15837dd7cddfSDavid du Colombier data |= (1<<i);
15847dd7cddfSDavid du Colombier microdelay(1);
15857dd7cddfSDavid du Colombier outs(port, mgmtClk);
15867dd7cddfSDavid du Colombier microdelay(1);
15877dd7cddfSDavid du Colombier outs(port, 0);
15887dd7cddfSDavid du Colombier microdelay(1);
15897dd7cddfSDavid du Colombier }
15907dd7cddfSDavid du Colombier
15917dd7cddfSDavid du Colombier return data;
15927dd7cddfSDavid du Colombier }
15937dd7cddfSDavid du Colombier
15947dd7cddfSDavid du Colombier static void
miimdo(int port,int bits,int n)15957dd7cddfSDavid du Colombier miimdo(int port, int bits, int n)
15967dd7cddfSDavid du Colombier {
15977dd7cddfSDavid du Colombier int i, mdo;
15987dd7cddfSDavid du Colombier
15997dd7cddfSDavid du Colombier /*
16007dd7cddfSDavid du Colombier * Write n bits to the MII Management Register.
16017dd7cddfSDavid du Colombier */
16027dd7cddfSDavid du Colombier for(i = n-1; i >= 0; i--){
16037dd7cddfSDavid du Colombier if(bits & (1<<i))
16047dd7cddfSDavid du Colombier mdo = mgmtDir|mgmtData;
16057dd7cddfSDavid du Colombier else
16067dd7cddfSDavid du Colombier mdo = mgmtDir;
16077dd7cddfSDavid du Colombier outs(port, mdo);
16087dd7cddfSDavid du Colombier microdelay(1);
16097dd7cddfSDavid du Colombier outs(port, mdo|mgmtClk);
16107dd7cddfSDavid du Colombier microdelay(1);
16117dd7cddfSDavid du Colombier outs(port, mdo);
16127dd7cddfSDavid du Colombier microdelay(1);
16137dd7cddfSDavid du Colombier }
16147dd7cddfSDavid du Colombier }
16157dd7cddfSDavid du Colombier
16167dd7cddfSDavid du Colombier static int
miir(int port,int phyad,int regad)16177dd7cddfSDavid du Colombier miir(int port, int phyad, int regad)
16187dd7cddfSDavid du Colombier {
16197dd7cddfSDavid du Colombier int data, w;
16207dd7cddfSDavid du Colombier
16217dd7cddfSDavid du Colombier w = (STATUS(port)>>13) & 0x07;
16227dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wdiagnostic);
16237dd7cddfSDavid du Colombier port += PhysicalMgmt;
16247dd7cddfSDavid du Colombier
16257dd7cddfSDavid du Colombier /*
16267dd7cddfSDavid du Colombier * Preamble;
16277dd7cddfSDavid du Colombier * ST+OP+PHYAD+REGAD;
16287dd7cddfSDavid du Colombier * TA + 16 data bits.
16297dd7cddfSDavid du Colombier */
16307dd7cddfSDavid du Colombier miimdo(port, 0xFFFFFFFF, 32);
16317dd7cddfSDavid du Colombier miimdo(port, 0x1800|(phyad<<5)|regad, 14);
16327dd7cddfSDavid du Colombier data = miimdi(port, 18);
16337dd7cddfSDavid du Colombier
16347dd7cddfSDavid du Colombier port -= PhysicalMgmt;
16357dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, w);
16367dd7cddfSDavid du Colombier
16377dd7cddfSDavid du Colombier if(data & 0x10000)
16387dd7cddfSDavid du Colombier return -1;
16397dd7cddfSDavid du Colombier
16407dd7cddfSDavid du Colombier return data & 0xFFFF;
16417dd7cddfSDavid du Colombier }
16427dd7cddfSDavid du Colombier
1643e31d734fSDavid du Colombier static int
scanphy(int port)16447dd7cddfSDavid du Colombier scanphy(int port)
16457dd7cddfSDavid du Colombier {
16467dd7cddfSDavid du Colombier int i, x;
16477dd7cddfSDavid du Colombier
16487dd7cddfSDavid du Colombier for(i = 0; i < 32; i++){
16497dd7cddfSDavid du Colombier if((x = miir(port, i, 2)) == -1 || x == 0)
16507dd7cddfSDavid du Colombier continue;
16517dd7cddfSDavid du Colombier x <<= 6;
16527dd7cddfSDavid du Colombier x |= miir(port, i, 3)>>10;
16537dd7cddfSDavid du Colombier XCVRDEBUG("phy%d: oui %uX reg1 %uX\n", i, x, miir(port, i, 1));
16547dd7cddfSDavid du Colombier USED(x);
1655e31d734fSDavid du Colombier
1656e31d734fSDavid du Colombier return i;
16577dd7cddfSDavid du Colombier }
1658e31d734fSDavid du Colombier return 24;
16597dd7cddfSDavid du Colombier }
16607dd7cddfSDavid du Colombier
16617dd7cddfSDavid du Colombier static struct {
16627dd7cddfSDavid du Colombier char *name;
16637dd7cddfSDavid du Colombier int avail;
16647dd7cddfSDavid du Colombier int xcvr;
16657dd7cddfSDavid du Colombier } media[] = {
16667dd7cddfSDavid du Colombier "10BaseT", base10TAvailable, xcvr10BaseT,
16677dd7cddfSDavid du Colombier "10Base2", coaxAvailable, xcvr10Base2,
16687dd7cddfSDavid du Colombier "100BaseTX", baseTXAvailable, xcvr100BaseTX,
16697dd7cddfSDavid du Colombier "100BaseFX", baseFXAvailable, xcvr100BaseFX,
16707dd7cddfSDavid du Colombier "aui", auiAvailable, xcvrAui,
16717dd7cddfSDavid du Colombier "mii", miiConnector, xcvrMii
16727dd7cddfSDavid du Colombier };
16737dd7cddfSDavid du Colombier
16747dd7cddfSDavid du Colombier static int
autoselect(Ctlr * ctlr)16753ff48bf5SDavid du Colombier autoselect(Ctlr* ctlr)
16767dd7cddfSDavid du Colombier {
16773ff48bf5SDavid du Colombier int media, port, x;
16787dd7cddfSDavid du Colombier
16797dd7cddfSDavid du Colombier /*
16807dd7cddfSDavid du Colombier * Pathetic attempt at automatic media selection.
16817dd7cddfSDavid du Colombier * Really just to get the Fast Etherlink 10BASE-T/100BASE-TX
16827dd7cddfSDavid du Colombier * cards operational.
16837dd7cddfSDavid du Colombier * It's a bonus if it works for anything else.
16847dd7cddfSDavid du Colombier */
16853ff48bf5SDavid du Colombier port = ctlr->port;
16863ff48bf5SDavid du Colombier if(ctlr->rxstatus9){
16877dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wsetup);
16887dd7cddfSDavid du Colombier x = ins(port+ConfigControl);
16897dd7cddfSDavid du Colombier media = 0;
16907dd7cddfSDavid du Colombier if(x & base10TAvailable9)
16917dd7cddfSDavid du Colombier media |= base10TAvailable;
16927dd7cddfSDavid du Colombier if(x & coaxAvailable9)
16937dd7cddfSDavid du Colombier media |= coaxAvailable;
16947dd7cddfSDavid du Colombier if(x & auiAvailable9)
16957dd7cddfSDavid du Colombier media |= auiAvailable;
16967dd7cddfSDavid du Colombier }
16977dd7cddfSDavid du Colombier else{
16987dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wfifo);
16997dd7cddfSDavid du Colombier media = ins(port+ResetOptions);
17007dd7cddfSDavid du Colombier }
17017dd7cddfSDavid du Colombier XCVRDEBUG("autoselect: media %uX\n", media);
17027dd7cddfSDavid du Colombier
17037dd7cddfSDavid du Colombier if(media & miiConnector)
17047dd7cddfSDavid du Colombier return xcvrMii;
17057dd7cddfSDavid du Colombier
17067dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wdiagnostic);
17077dd7cddfSDavid du Colombier XCVRDEBUG("autoselect: media status %uX\n", ins(port+MediaStatus));
17087dd7cddfSDavid du Colombier
17097dd7cddfSDavid du Colombier if(media & baseTXAvailable){
17107dd7cddfSDavid du Colombier /*
17117dd7cddfSDavid du Colombier * Must have InternalConfig register.
17127dd7cddfSDavid du Colombier */
17133ff48bf5SDavid du Colombier setxcvr(ctlr, xcvr100BaseTX);
17147dd7cddfSDavid du Colombier
17157dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wdiagnostic);
17167dd7cddfSDavid du Colombier x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable);
17177dd7cddfSDavid du Colombier outs(port+MediaStatus, linkBeatEnable|x);
17187dd7cddfSDavid du Colombier delay(10);
17197dd7cddfSDavid du Colombier
17207dd7cddfSDavid du Colombier if(ins(port+MediaStatus) & linkBeatDetect)
17217dd7cddfSDavid du Colombier return xcvr100BaseTX;
17227dd7cddfSDavid du Colombier outs(port+MediaStatus, x);
17237dd7cddfSDavid du Colombier }
17247dd7cddfSDavid du Colombier
17257dd7cddfSDavid du Colombier if(media & base10TAvailable){
17263ff48bf5SDavid du Colombier setxcvr(ctlr, xcvr10BaseT);
17277dd7cddfSDavid du Colombier
17287dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wdiagnostic);
17297dd7cddfSDavid du Colombier x = ins(port+MediaStatus) & ~dcConverterEnabled;
17307dd7cddfSDavid du Colombier outs(port+MediaStatus, linkBeatEnable|jabberGuardEnable|x);
17317dd7cddfSDavid du Colombier delay(100);
17327dd7cddfSDavid du Colombier
17337dd7cddfSDavid du Colombier XCVRDEBUG("autoselect: 10BaseT media status %uX\n", ins(port+MediaStatus));
17347dd7cddfSDavid du Colombier if(ins(port+MediaStatus) & linkBeatDetect)
17357dd7cddfSDavid du Colombier return xcvr10BaseT;
17367dd7cddfSDavid du Colombier outs(port+MediaStatus, x);
17377dd7cddfSDavid du Colombier }
17387dd7cddfSDavid du Colombier
17397dd7cddfSDavid du Colombier /*
17407dd7cddfSDavid du Colombier * Botch.
17417dd7cddfSDavid du Colombier */
17427dd7cddfSDavid du Colombier return autoSelect;
17437dd7cddfSDavid du Colombier }
17447dd7cddfSDavid du Colombier
17457dd7cddfSDavid du Colombier static int
eepromdata(Ctlr * ctlr,int offset)17463ff48bf5SDavid du Colombier eepromdata(Ctlr* ctlr, int offset)
17477dd7cddfSDavid du Colombier {
17483ff48bf5SDavid du Colombier int port;
17493ff48bf5SDavid du Colombier
17503ff48bf5SDavid du Colombier port = ctlr->port;
17513ff48bf5SDavid du Colombier
17527dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wsetup);
17537dd7cddfSDavid du Colombier while(EEPROMBUSY(port))
17547dd7cddfSDavid du Colombier ;
1755e31d734fSDavid du Colombier EEPROMCMD(port, ctlr->eepromcmd, offset);
17567dd7cddfSDavid du Colombier while(EEPROMBUSY(port))
17577dd7cddfSDavid du Colombier ;
17587dd7cddfSDavid du Colombier return EEPROMDATA(port);
17597dd7cddfSDavid du Colombier }
17607dd7cddfSDavid du Colombier
17610809e9a7SDavid du Colombier static void
resetctlr(Ctlr * ctlr)17620809e9a7SDavid du Colombier resetctlr(Ctlr *ctlr)
17630809e9a7SDavid du Colombier {
17640809e9a7SDavid du Colombier int x, port = ctlr->port;
17650809e9a7SDavid du Colombier
17660809e9a7SDavid du Colombier txrxreset(port);
17670809e9a7SDavid du Colombier x = ins(port+ResetOp905B);
17680809e9a7SDavid du Colombier XCVRDEBUG("905[BC] reset ops 0x%uX\n", x);
17690809e9a7SDavid du Colombier x &= ~0x4010;
17700809e9a7SDavid du Colombier if(ctlr->did == 0x5157){
17710809e9a7SDavid du Colombier x |= 0x0010; /* Invert LED */
17720809e9a7SDavid du Colombier outs(port+ResetOp905B, x);
17730809e9a7SDavid du Colombier }
17740809e9a7SDavid du Colombier if(ctlr->did == 0x6056){
17750809e9a7SDavid du Colombier x |= 0x4000;
17760809e9a7SDavid du Colombier outs(port+ResetOp905B, x);
17770809e9a7SDavid du Colombier
17780809e9a7SDavid du Colombier COMMAND(port, SelectRegisterWindow, Wsetup);
17790809e9a7SDavid du Colombier outs(port, 0x0800);
17800809e9a7SDavid du Colombier }
17810809e9a7SDavid du Colombier }
17820809e9a7SDavid du Colombier
17830809e9a7SDavid du Colombier static void
shutdown(Ether * ether)17840809e9a7SDavid du Colombier shutdown(Ether *ether)
17850809e9a7SDavid du Colombier {
17860809e9a7SDavid du Colombier print("etherelnk3 shutting down\n");
17870809e9a7SDavid du Colombier resetctlr(ether->ctlr);
17880809e9a7SDavid du Colombier }
17890809e9a7SDavid du Colombier
17907dd7cddfSDavid du Colombier int
etherelnk3reset(Ether * ether)17917dd7cddfSDavid du Colombier etherelnk3reset(Ether* ether)
17927dd7cddfSDavid du Colombier {
17937dd7cddfSDavid du Colombier char *p;
17943ff48bf5SDavid du Colombier Ctlr *ctlr;
17953ff48bf5SDavid du Colombier uchar ea[Eaddrlen];
17963ff48bf5SDavid du Colombier static int scandone;
17973ff48bf5SDavid du Colombier int anar, anlpar, i, j, phyaddr, phystat, port, timeo, x;
17987dd7cddfSDavid du Colombier
17997dd7cddfSDavid du Colombier /*
18007dd7cddfSDavid du Colombier * Scan for adapter on PCI, EISA and finally
18017dd7cddfSDavid du Colombier * using the little ISA configuration dance.
18027dd7cddfSDavid du Colombier */
18037dd7cddfSDavid du Colombier if(scandone == 0){
18047dd7cddfSDavid du Colombier tcm59Xpci();
18057dd7cddfSDavid du Colombier tcm5XXeisa();
18067dd7cddfSDavid du Colombier tcm509isa();
18077dd7cddfSDavid du Colombier scandone = 1;
18087dd7cddfSDavid du Colombier }
18097dd7cddfSDavid du Colombier
18107dd7cddfSDavid du Colombier /*
18117dd7cddfSDavid du Colombier * Any adapter matches if no ether->port is supplied,
18127dd7cddfSDavid du Colombier * otherwise the ports must match.
18137dd7cddfSDavid du Colombier */
18143ff48bf5SDavid du Colombier for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
18153ff48bf5SDavid du Colombier if(ctlr->active)
18169a747e4fSDavid du Colombier continue;
18173ff48bf5SDavid du Colombier if(ether->port == 0 || ether->port == ctlr->port){
18183ff48bf5SDavid du Colombier ctlr->active = 1;
18197dd7cddfSDavid du Colombier break;
18207dd7cddfSDavid du Colombier }
18217dd7cddfSDavid du Colombier }
18223ff48bf5SDavid du Colombier if(ctlr == nil && (ctlr = tcm5XXpcmcia(ether)) == 0)
18237dd7cddfSDavid du Colombier return -1;
18247dd7cddfSDavid du Colombier
18253ff48bf5SDavid du Colombier ether->ctlr = ctlr;
18263ff48bf5SDavid du Colombier port = ctlr->port;
18273ff48bf5SDavid du Colombier ether->port = port;
18283ff48bf5SDavid du Colombier ether->irq = ctlr->irq;
18293ff48bf5SDavid du Colombier if(ctlr->pcidev != nil)
18303ff48bf5SDavid du Colombier ether->tbdf = ctlr->pcidev->tbdf;
18313ff48bf5SDavid du Colombier else
18323ff48bf5SDavid du Colombier ether->tbdf = BUSUNKNOWN;
18333ff48bf5SDavid du Colombier
18347dd7cddfSDavid du Colombier /*
18357dd7cddfSDavid du Colombier * Read the DeviceID from the EEPROM, it's at offset 0x03,
18367dd7cddfSDavid du Colombier * and do something depending on capabilities.
18377dd7cddfSDavid du Colombier */
18383ff48bf5SDavid du Colombier switch(ctlr->did = eepromdata(ctlr, 0x03)){
18399a747e4fSDavid du Colombier case 0x5157: /* 3C575 Cyclone */
1840e31d734fSDavid du Colombier case 0x6056:
18413ff48bf5SDavid du Colombier /*FALLTHROUGH*/
18423ff48bf5SDavid du Colombier case 0x4500: /* 3C450 HomePNA Tornado */
18433ff48bf5SDavid du Colombier case 0x7646: /* 3CSOHO100-TX */
18443ff48bf5SDavid du Colombier case 0x9055: /* 3C905B-TX */
18453ff48bf5SDavid du Colombier case 0x9200: /* 3C905C-TX */
184639734e7eSDavid du Colombier case 0x9201: /* 3C920 */
18471805eb57SDavid du Colombier case 0x9805: /* 3C9805: 3C980-TX Python-T 10/100baseTX */
18483ff48bf5SDavid du Colombier /*FALLTHROUGH*/
18493ff48bf5SDavid du Colombier case 0x9000: /* 3C900-TPO */
18503ff48bf5SDavid du Colombier case 0x9001: /* 3C900-COMBO */
18513ff48bf5SDavid du Colombier case 0x9005: /* 3C900B-COMBO */
18523ff48bf5SDavid du Colombier case 0x9050: /* 3C905-TX */
18533ff48bf5SDavid du Colombier case 0x9051: /* 3C905-T4 */
18547dd7cddfSDavid du Colombier if(BUSTYPE(ether->tbdf) != BusPCI)
18557dd7cddfSDavid du Colombier goto buggery;
18563ff48bf5SDavid du Colombier ctlr->busmaster = 2;
18577dd7cddfSDavid du Colombier goto vortex;
18583ff48bf5SDavid du Colombier case 0x5900: /* 3C590-[TP|COMBO|TPO] */
18593ff48bf5SDavid du Colombier case 0x5920: /* 3C592-[TP|COMBO|TPO] */
18603ff48bf5SDavid du Colombier case 0x5950: /* 3C595-TX */
18613ff48bf5SDavid du Colombier case 0x5951: /* 3C595-T4 */
18623ff48bf5SDavid du Colombier case 0x5952: /* 3C595-MII */
18633ff48bf5SDavid du Colombier case 0x5970: /* 3C597-TX */
18643ff48bf5SDavid du Colombier case 0x5971: /* 3C597-T4 */
18653ff48bf5SDavid du Colombier case 0x5972: /* 3C597-MII */
18663ff48bf5SDavid du Colombier ctlr->busmaster = 1;
18677dd7cddfSDavid du Colombier vortex:
18687dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wfifo);
18693ff48bf5SDavid du Colombier ctlr->xcvr = inl(port+InternalConfig) & (autoSelect|xcvrMask);
18703ff48bf5SDavid du Colombier ctlr->rxearly = 8188;
18713ff48bf5SDavid du Colombier ctlr->rxstatus9 = 0;
18727dd7cddfSDavid du Colombier break;
18737dd7cddfSDavid du Colombier buggery:
18747dd7cddfSDavid du Colombier default:
18753ff48bf5SDavid du Colombier ctlr->busmaster = 0;
18767dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wsetup);
18777dd7cddfSDavid du Colombier x = ins(port+AddressConfig);
18783ff48bf5SDavid du Colombier ctlr->xcvr = ((x & xcvrMask9)>>14)<<20;
18797dd7cddfSDavid du Colombier if(x & autoSelect9)
18803ff48bf5SDavid du Colombier ctlr->xcvr |= autoSelect;
18813ff48bf5SDavid du Colombier ctlr->rxearly = 2044;
18823ff48bf5SDavid du Colombier ctlr->rxstatus9 = 1;
18837dd7cddfSDavid du Colombier break;
18847dd7cddfSDavid du Colombier }
18853ff48bf5SDavid du Colombier if(ctlr->rxearly >= 2048)
18863ff48bf5SDavid du Colombier ctlr->ts = 2;
18877dd7cddfSDavid du Colombier
18887dd7cddfSDavid du Colombier /*
18897dd7cddfSDavid du Colombier * Check if the adapter's station address is to be overridden.
18909a747e4fSDavid du Colombier * If not, read it from the EEPROM and set in ether->ea prior to
18919a747e4fSDavid du Colombier * loading the station address in Wstation.
18929a747e4fSDavid du Colombier * The EEPROM returns 16-bits at a time.
18937dd7cddfSDavid du Colombier */
18947dd7cddfSDavid du Colombier memset(ea, 0, Eaddrlen);
18957dd7cddfSDavid du Colombier if(memcmp(ea, ether->ea, Eaddrlen) == 0){
18967dd7cddfSDavid du Colombier for(i = 0; i < Eaddrlen/2; i++){
18973ff48bf5SDavid du Colombier x = eepromdata(ctlr, i);
18987dd7cddfSDavid du Colombier ether->ea[2*i] = x>>8;
18997dd7cddfSDavid du Colombier ether->ea[2*i+1] = x;
19007dd7cddfSDavid du Colombier }
19017dd7cddfSDavid du Colombier }
19027dd7cddfSDavid du Colombier
19037dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wstation);
19047dd7cddfSDavid du Colombier for(i = 0; i < Eaddrlen; i++)
19057dd7cddfSDavid du Colombier outb(port+i, ether->ea[i]);
19067dd7cddfSDavid du Colombier
19077dd7cddfSDavid du Colombier /*
19087dd7cddfSDavid du Colombier * Enable the transceiver if necessary and determine whether
19097dd7cddfSDavid du Colombier * busmastering can be used. Due to bugs in the first revision
19107dd7cddfSDavid du Colombier * of the 3C59[05], don't use busmastering at 10Mbps.
19117dd7cddfSDavid du Colombier */
19123ff48bf5SDavid du Colombier XCVRDEBUG("reset: xcvr %uX\n", ctlr->xcvr);
19137dd7cddfSDavid du Colombier
19147dd7cddfSDavid du Colombier /*
19157dd7cddfSDavid du Colombier * Allow user to specify desired media in plan9.ini
19167dd7cddfSDavid du Colombier */
19177dd7cddfSDavid du Colombier for(i = 0; i < ether->nopt; i++){
19187dd7cddfSDavid du Colombier if(cistrncmp(ether->opt[i], "media=", 6) != 0)
19197dd7cddfSDavid du Colombier continue;
19207dd7cddfSDavid du Colombier p = ether->opt[i]+6;
19217dd7cddfSDavid du Colombier for(j = 0; j < nelem(media); j++)
19227dd7cddfSDavid du Colombier if(cistrcmp(p, media[j].name) == 0)
19233ff48bf5SDavid du Colombier ctlr->xcvr = media[j].xcvr;
19247dd7cddfSDavid du Colombier }
19257dd7cddfSDavid du Colombier
19267dd7cddfSDavid du Colombier /*
19277dd7cddfSDavid du Colombier * forgive me, but i am weak
19287dd7cddfSDavid du Colombier */
19293ff48bf5SDavid du Colombier switch(ctlr->did){
19303ff48bf5SDavid du Colombier default:
19313ff48bf5SDavid du Colombier if(ctlr->xcvr & autoSelect)
19323ff48bf5SDavid du Colombier ctlr->xcvr = autoselect(ctlr);
19333ff48bf5SDavid du Colombier break;
19343ff48bf5SDavid du Colombier case 0x5157:
19353ff48bf5SDavid du Colombier case 0x6056:
1936e31d734fSDavid du Colombier case 0x4500:
19373ff48bf5SDavid du Colombier case 0x7646:
19383ff48bf5SDavid du Colombier case 0x9055:
19393ff48bf5SDavid du Colombier case 0x9200:
194039734e7eSDavid du Colombier case 0x9201:
19411805eb57SDavid du Colombier case 0x9805:
19423ff48bf5SDavid du Colombier ctlr->xcvr = xcvrMii;
19430809e9a7SDavid du Colombier resetctlr(ctlr);
19443ff48bf5SDavid du Colombier break;
19459a747e4fSDavid du Colombier }
19463ff48bf5SDavid du Colombier XCVRDEBUG("xcvr selected: %uX, did 0x%uX\n", ctlr->xcvr, ctlr->did);
19479a747e4fSDavid du Colombier
19483ff48bf5SDavid du Colombier switch(ctlr->xcvr){
19497dd7cddfSDavid du Colombier case xcvrMii:
19507dd7cddfSDavid du Colombier /*
19517dd7cddfSDavid du Colombier * Quick hack.
19527dd7cddfSDavid du Colombier */
19533ff48bf5SDavid du Colombier if(ctlr->did == 0x5157)
19543ff48bf5SDavid du Colombier phyaddr = 0;
1955e31d734fSDavid du Colombier else if(ctlr->did == 0x6056)
1956e31d734fSDavid du Colombier phyaddr = scanphy(port);
19573ff48bf5SDavid du Colombier else
19583ff48bf5SDavid du Colombier phyaddr = 24;
19597dd7cddfSDavid du Colombier for(i = 0; i < 7; i++)
19607dd7cddfSDavid du Colombier XCVRDEBUG(" %2.2uX", miir(port, phyaddr, i));
19617dd7cddfSDavid du Colombier XCVRDEBUG("\n");
19627dd7cddfSDavid du Colombier
19637dd7cddfSDavid du Colombier for(timeo = 0; timeo < 30; timeo++){
19647dd7cddfSDavid du Colombier phystat = miir(port, phyaddr, 0x01);
19657dd7cddfSDavid du Colombier if(phystat & 0x20)
19667dd7cddfSDavid du Colombier break;
19677dd7cddfSDavid du Colombier XCVRDEBUG(" %2.2uX", phystat);
19687dd7cddfSDavid du Colombier delay(100);
19697dd7cddfSDavid du Colombier }
19707dd7cddfSDavid du Colombier XCVRDEBUG(" %2.2uX", miir(port, phyaddr, 0x01));
19717dd7cddfSDavid du Colombier XCVRDEBUG("\n");
19727dd7cddfSDavid du Colombier
19737dd7cddfSDavid du Colombier anar = miir(port, phyaddr, 0x04);
19747dd7cddfSDavid du Colombier anlpar = miir(port, phyaddr, 0x05) & 0x03E0;
19757dd7cddfSDavid du Colombier anar &= anlpar;
19767dd7cddfSDavid du Colombier miir(port, phyaddr, 0x00);
19777dd7cddfSDavid du Colombier XCVRDEBUG("mii an: %uX anlp: %uX r0:%uX r1:%uX\n",
19787dd7cddfSDavid du Colombier anar, anlpar, miir(port, phyaddr, 0x00),
19797dd7cddfSDavid du Colombier miir(port, phyaddr, 0x01));
19807dd7cddfSDavid du Colombier for(i = 0; i < ether->nopt; i++){
19817dd7cddfSDavid du Colombier if(cistrcmp(ether->opt[i], "fullduplex") == 0)
19827dd7cddfSDavid du Colombier anar |= 0x0100;
19837dd7cddfSDavid du Colombier else if(cistrcmp(ether->opt[i], "100BASE-TXFD") == 0)
19847dd7cddfSDavid du Colombier anar |= 0x0100;
19857dd7cddfSDavid du Colombier else if(cistrcmp(ether->opt[i], "force100") == 0)
19867dd7cddfSDavid du Colombier anar |= 0x0080;
19877dd7cddfSDavid du Colombier }
19887dd7cddfSDavid du Colombier XCVRDEBUG("mii anar: %uX\n", anar);
19897dd7cddfSDavid du Colombier if(anar & 0x0100){ /* 100BASE-TXFD */
19907dd7cddfSDavid du Colombier ether->mbps = 100;
19917dd7cddfSDavid du Colombier setfullduplex(port);
19927dd7cddfSDavid du Colombier }
19939a747e4fSDavid du Colombier else if(anar & 0x0200){ /* 100BASE-T4 */
19949a747e4fSDavid du Colombier /* nothing to do */
19959a747e4fSDavid du Colombier }
19967dd7cddfSDavid du Colombier else if(anar & 0x0080) /* 100BASE-TX */
19977dd7cddfSDavid du Colombier ether->mbps = 100;
19987dd7cddfSDavid du Colombier else if(anar & 0x0040) /* 10BASE-TFD */
19997dd7cddfSDavid du Colombier setfullduplex(port);
20009a747e4fSDavid du Colombier else{ /* 10BASE-T */
20019a747e4fSDavid du Colombier /* nothing to do */
20029a747e4fSDavid du Colombier }
20037dd7cddfSDavid du Colombier break;
20047dd7cddfSDavid du Colombier case xcvr100BaseTX:
20057dd7cddfSDavid du Colombier case xcvr100BaseFX:
20067dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wfifo);
20077dd7cddfSDavid du Colombier x = inl(port+InternalConfig) & ~ramPartitionMask;
20087dd7cddfSDavid du Colombier outl(port+InternalConfig, x|ramPartition1to1);
20097dd7cddfSDavid du Colombier
20107dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wdiagnostic);
20117dd7cddfSDavid du Colombier x = ins(port+MediaStatus) & ~(dcConverterEnabled|jabberGuardEnable);
20127dd7cddfSDavid du Colombier x |= linkBeatEnable;
20137dd7cddfSDavid du Colombier outs(port+MediaStatus, x);
20147dd7cddfSDavid du Colombier
20157dd7cddfSDavid du Colombier if(x & dataRate100)
20167dd7cddfSDavid du Colombier ether->mbps = 100;
20177dd7cddfSDavid du Colombier break;
20187dd7cddfSDavid du Colombier case xcvr10BaseT:
20197dd7cddfSDavid du Colombier /*
20207dd7cddfSDavid du Colombier * Enable Link Beat and Jabber to start the
20217dd7cddfSDavid du Colombier * transceiver.
20227dd7cddfSDavid du Colombier */
20237dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wdiagnostic);
20247dd7cddfSDavid du Colombier x = ins(port+MediaStatus) & ~dcConverterEnabled;
20257dd7cddfSDavid du Colombier x |= linkBeatEnable|jabberGuardEnable;
20267dd7cddfSDavid du Colombier outs(port+MediaStatus, x);
20277dd7cddfSDavid du Colombier
20283ff48bf5SDavid du Colombier if((ctlr->did & 0xFF00) == 0x5900)
20293ff48bf5SDavid du Colombier ctlr->busmaster = 0;
20307dd7cddfSDavid du Colombier break;
20317dd7cddfSDavid du Colombier case xcvr10Base2:
20327dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wdiagnostic);
20337dd7cddfSDavid du Colombier x = ins(port+MediaStatus) & ~(linkBeatEnable|jabberGuardEnable);
20347dd7cddfSDavid du Colombier outs(port+MediaStatus, x);
20357dd7cddfSDavid du Colombier
20367dd7cddfSDavid du Colombier /*
20377dd7cddfSDavid du Colombier * Start the DC-DC converter.
20387dd7cddfSDavid du Colombier * Wait > 800 microseconds.
20397dd7cddfSDavid du Colombier */
20407dd7cddfSDavid du Colombier COMMAND(port, EnableDcConverter, 0);
20417dd7cddfSDavid du Colombier delay(1);
20427dd7cddfSDavid du Colombier break;
20437dd7cddfSDavid du Colombier }
20447dd7cddfSDavid du Colombier
20457dd7cddfSDavid du Colombier /*
20467dd7cddfSDavid du Colombier * Wop is the normal operating register set.
20477dd7cddfSDavid du Colombier * The 3C59[0257] adapters allow access to more than one register window
20487dd7cddfSDavid du Colombier * at a time, but there are situations where switching still needs to be
20497dd7cddfSDavid du Colombier * done, so just do it.
20507dd7cddfSDavid du Colombier * Clear out any lingering Tx status.
20517dd7cddfSDavid du Colombier */
20527dd7cddfSDavid du Colombier COMMAND(port, SelectRegisterWindow, Wop);
20533ff48bf5SDavid du Colombier if(ctlr->busmaster == 2)
205459cc4ca5SDavid du Colombier x = port+TxStatus905;
205559cc4ca5SDavid du Colombier else
205659cc4ca5SDavid du Colombier x = port+TxStatus;
205759cc4ca5SDavid du Colombier while(inb(x))
205859cc4ca5SDavid du Colombier outb(x, 0);
20597dd7cddfSDavid du Colombier
20607dd7cddfSDavid du Colombier /*
20613ff48bf5SDavid du Colombier * Clear out the
20627dd7cddfSDavid du Colombier * adapter statistics, clear the statistics logged into ctlr
20633ff48bf5SDavid du Colombier * and enable statistics collection.
20647dd7cddfSDavid du Colombier */
20657dd7cddfSDavid du Colombier ilock(&ctlr->wlock);
20667dd7cddfSDavid du Colombier statistics(ether);
20677dd7cddfSDavid du Colombier memset(ctlr->stats, 0, sizeof(ctlr->stats));
20687dd7cddfSDavid du Colombier
20697dd7cddfSDavid du Colombier COMMAND(port, StatisticsEnable, 0);
20707dd7cddfSDavid du Colombier
20717dd7cddfSDavid du Colombier /*
20727dd7cddfSDavid du Colombier * Allocate any receive buffers.
20737dd7cddfSDavid du Colombier */
20747dd7cddfSDavid du Colombier switch(ctlr->busmaster){
20757dd7cddfSDavid du Colombier case 2:
20767dd7cddfSDavid du Colombier ctlr->dnenabled = 1;
20777dd7cddfSDavid du Colombier
20787dd7cddfSDavid du Colombier /*
20797dd7cddfSDavid du Colombier * 10MUpldBug.
20807dd7cddfSDavid du Colombier * Disabling is too severe, can use receive busmastering at
20817dd7cddfSDavid du Colombier * 100Mbps OK, but how to tell which rate is actually being used -
20827dd7cddfSDavid du Colombier * the 3c905 always seems to have dataRate100 set?
20837dd7cddfSDavid du Colombier * Believe the bug doesn't apply if upRxEarlyEnable is set
20847dd7cddfSDavid du Colombier * and the threshold is set such that uploads won't start
20857dd7cddfSDavid du Colombier * until the whole packet has been received.
20867dd7cddfSDavid du Colombier */
20877dd7cddfSDavid du Colombier ctlr->upenabled = 1;
20883ff48bf5SDavid du Colombier x = eepromdata(ctlr, 0x0F);
20897dd7cddfSDavid du Colombier if(!(x & 0x01))
20907dd7cddfSDavid du Colombier outl(port+PktStatus, upRxEarlyEnable);
20917dd7cddfSDavid du Colombier
20927dd7cddfSDavid du Colombier if(ctlr->upenabled || ctlr->dnenabled){
20937dd7cddfSDavid du Colombier ctlr->nup = Nup;
20947dd7cddfSDavid du Colombier ctlr->ndn = Ndn;
20957dd7cddfSDavid du Colombier init905(ctlr);
20967dd7cddfSDavid du Colombier }
20979a747e4fSDavid du Colombier else {
20989a747e4fSDavid du Colombier ctlr->rbp = rbpalloc(iallocb);
20999a747e4fSDavid du Colombier if(ctlr->rbp == nil)
21009a747e4fSDavid du Colombier panic("can't reset ethernet: out of memory");
21019a747e4fSDavid du Colombier }
21027dd7cddfSDavid du Colombier outl(port+TxFreeThresh, HOWMANY(ETHERMAXTU, 256));
21037dd7cddfSDavid du Colombier break;
21047dd7cddfSDavid du Colombier default:
21059a747e4fSDavid du Colombier ctlr->rbp = rbpalloc(iallocb);
21069a747e4fSDavid du Colombier if(ctlr->rbp == nil)
21079a747e4fSDavid du Colombier panic("can't reset ethernet: out of memory");
21087dd7cddfSDavid du Colombier break;
21097dd7cddfSDavid du Colombier }
21107dd7cddfSDavid du Colombier
21117dd7cddfSDavid du Colombier /*
21127dd7cddfSDavid du Colombier * Set a base TxStartThresh which will be incremented
21137dd7cddfSDavid du Colombier * if any txUnderrun errors occur and ensure no RxEarly
21147dd7cddfSDavid du Colombier * interrupts happen.
21157dd7cddfSDavid du Colombier */
21167dd7cddfSDavid du Colombier ctlr->txthreshold = ETHERMAXTU/2;
21177dd7cddfSDavid du Colombier COMMAND(port, SetTxStartThresh, ctlr->txthreshold>>ctlr->ts);
21183ff48bf5SDavid du Colombier COMMAND(port, SetRxEarlyThresh, ctlr->rxearly>>ctlr->ts);
21197dd7cddfSDavid du Colombier
21207dd7cddfSDavid du Colombier iunlock(&ctlr->wlock);
21217dd7cddfSDavid du Colombier
21227dd7cddfSDavid du Colombier /*
21237dd7cddfSDavid du Colombier * Linkage to the generic ethernet driver.
21247dd7cddfSDavid du Colombier */
21257dd7cddfSDavid du Colombier ether->attach = attach;
21267dd7cddfSDavid du Colombier ether->transmit = transmit;
21277dd7cddfSDavid du Colombier ether->interrupt = interrupt;
21287dd7cddfSDavid du Colombier ether->ifstat = ifstat;
21297dd7cddfSDavid du Colombier
21307dd7cddfSDavid du Colombier ether->promiscuous = promiscuous;
21317dd7cddfSDavid du Colombier ether->multicast = multicast;
21320809e9a7SDavid du Colombier ether->shutdown = shutdown;
21337dd7cddfSDavid du Colombier ether->arg = ether;
21347dd7cddfSDavid du Colombier
21357dd7cddfSDavid du Colombier return 0;
21367dd7cddfSDavid du Colombier }
21377dd7cddfSDavid du Colombier
21387dd7cddfSDavid du Colombier void
etherelnk3link(void)21397dd7cddfSDavid du Colombier etherelnk3link(void)
21407dd7cddfSDavid du Colombier {
21417dd7cddfSDavid du Colombier addethercard("elnk3", etherelnk3reset);
21427dd7cddfSDavid du Colombier addethercard("3C509", etherelnk3reset);
21439a747e4fSDavid du Colombier addethercard("3C575", etherelnk3reset);
21447dd7cddfSDavid du Colombier }
2145