/* dgelin.c: Base on Level One Communications LXT1001 network driver for linux. */ /* Maintained by Beny Chen (beny_chen@dlink.com.tw */ /* jt1lin.c: A Level One Communications LXT1001 network driver for linux. */ /* * NOTICE: this version tested with kernels 2.0.x and later only! * * Written 1998 by Douglas Greiman. * Maintained by Antonio Torrini (atorrini@level1.com) * * Copyright 1998 Level One Communications, Inc * * This software may be used and distributed according to the terms * of the GNU Public License, incorporated herein by reference. * * The maintainer may be reached as atorrini@level1.com, or C/O * Level One Communications, Inc., Austin Design Center * 505 E. Huntland Drive * Suite 550 * Austin, TX 78752 * (512) 407-2138 * * This file is a device driver for the Level One Commuincations * NetCelerator family of Gigabit Ethernet controllers. * * This code uses 'word' to mean a 16 bit value, * and 'dword' to mean a 32 bit value. */ static const char *version = "dgelin.c:v1.00 09/01/99 D-Link (www.dlink.com)\n"; /* Theory of Operation I. Board Compatibility This device driver is designed for Level One Communications NetCelerator Family of Gigabit Ethernet adapters. II. Board-specific settings PCI bus devices are configured by the system at boot time, so no jumpers need to be set on the board. The driver will function correctly if the device's interrupt is shared with another device. For maximum performance, the system BIOS preferably should assign the PCI INTA signal to an otherwise unused system IRQ line. III. Driver operation IIIa. I/O Modes The NetCelerator architecture supports three different mechanisms for I/O: 1. Programmed I/O (PIO) mode, commonly used by low-end adapters. Note that this mode is no longer mantained and supported. 2. Packet Descriptor List (PDL) mode, a DMA descriptor-based mechanism similar to that used by the Intel EtherExpress Pro and other adapters. 3. Packet Descriptor List (PDC) mode, aka Packet Propulsion. This driver supports three receive modes: PIO, PDL, and PDC. Each mode is mutually exclusive. That is, all receives are done using only PIO, or only PDL, or only PDC. This driver supports two transmit modes: PIO, and BusMaster. The BusMaster transmit mode dynamically switches between PDC and PDL based on the size of the packet to transmit. Small packets are copied into PDC buffers, while large packets are indirectly referenced by PDLs. IIIb. Queues This driver makes use of fixed-size circular queues (the JT_QUEUE struct) for both PDL and PDC I/O modes. Each queue contains a fixed number of elements. Each element is a pointer to either a PDL or a PDC buffer, depending on the queue and the current mode of operation. Elements are always queued at the tail and dequeued from the head. For receive, there are two queues, RxPDxAvailableQueue and RxPDxInProgressQueue. The In Progress queue contains pointers to PDL/PDC's that have been given to the controller as receive commands. The Available queue contains PDL/PDC's which are not currently in use by the controller. For transmit, there are three queues, TxPDCAvailableQueue, TxPDLAvailableQueue and TxPDxInProgressQueue. The In Progress queue contains pointers to PDL/PDC's that have been given to the controller as transmit commands. The Available queues contain either PDLs or PDCs which are not currently in use by the controller. Since the In Progress queue contains a heterogeneous mixture of PDC and PDL pointers, some means is needed to distinguish whether a given pointer points to a PDC or a PDL. Thus, the least significant bit of the pointer is set to 1 for PDL pointers, and left at 0 for PDC pointers. Since PDLs and PDCs are always at least 32 byte aligned, the least significant bit would otherwise always be 0. IIIc. Synchronization The driver can be compiled either in standard mode or in performance mode, and the synchronization method depends on which one is used. In standard mode, the driver runs as two independent, single-threaded flows of control. One is the send-packet routine, which enforces single-threaded use by the 'pDev->tbusy' flag. The other thread is the interrupt handler, which is single threaded by the hardware and other software. The send packet thread has partial control over the Tx ring and 'pDev->tbusy' flag. It sets the tbusy flag whenever it's queuing a Tx packet. While queueing the packet for transmit, the send packet thread sets the 'pDevInstance->TxBusy' flag if it determines that the controller cannot queue any more transmits, for any reason. Once a packet is queued, the send packet thread clears the 'pDev->tbusy' flag unless the pDevInstance->TxFull flag has been set. The interrupt handler has exclusive control over the Rx ring and partial control over the Tx ring. After being notified that a transmit(s) has completed, the interrupt handler removes PDL/PDCs from the In Progress queue and adds them to the PDC and PDL Available queues. If the 'pDevInstance->TxFull' flag is set, it clears both the 'TxFull' and 'tbusy' flags. In performance mode, the driver is multi-threaded and synchronization is achieved by means of spin-locks inside of the send routine and the interrupt handler. IIId. PDL Receive Call Tree: jtDevOpen +--jtRxPDLCreate +--jtRxPDLInitialize | +--jtRxPDLReplenish +--jtRxStart jtDevInterrupt +--jtRxPDLReceive +--jtRxPDLReplenish jtDevClose +--jtRxStop +--jtRxPDLFlush +--jtRxPDLDestroy jtRxPDLCreate first allocates and initializes the queues RxPDxAvailableQueue and RxPDxInProgressQueue. Then the function allocates a single contiguous block of memory large enough to hold all receive PDLs. jtRxPDLInitialize divides the block into individual PDLs, which are aligned at cache line boundaries so that the controller can use PCI Memory Write and Invalidate commands. Pointers to each PDL are inserted into the Available queue. jtRxPDLReplenish sends PDL receive commands to the controller. The commands are sent one at a time until the controller indicates that it can't accept more commands. Each PDL to be sent is dequeued from the Available queue and initialized with a header indicating that the PDL owns a single fragment. An sk_buff large enough for a maximum size packet is allocated. The physical address of the sk_buff's data area is stored in the first fragment entry of the PDL. For bookkeeping purposes, the virtual address of the sk_buff structure itself is stored in the PDL's second fragment entry. This fragment entry is ignored by the controller. Once initialized, the PDL is inserted into the InProgress queue. Finally, the address of the PDL receive command is sent to the controller. jtRxPDLReceive is called by the interrupt handler each time the controller receives one or more packets. Since the controller receives packets in order, the function knows that the packet(s) received have been placed into the PDLs at the head of the InProgress queue. The function repeatedly removes the PDL from the head of the InProgress queue and processes it, until each received packet has been processed. First, the address of the sk_buff associated with a particular PDL is extracted from the second fragment entry of that PDL. The sk_buff's length is adjusted to match the actual packet length indicated by the PDL. The packet is then given to the upper layers for processing. Finally, the PDL is returned to the Available queue. Once all of the received packets have been processed, jtRxPDLReplenish is called to recycle PDL receive commands back into service. IIIe. PDC Receive Call Tree: jtDevOpen +--jtRxPDCCreate +--jtRxPDCInitialize | +--jtRxPDCReplenish +--jtRxStart jtDevInterrupt +--jtRxPDCReceive +--jtRxPDCReplenish jtDevClose +--jtRxStop +--jtRxPDCFlush +--jtRxPDCDestroy jtRxPDCCreate first allocates and initializes the queues RxPDxAvailableQueue and RxPDxInProgressQueue. Then the function allocates a block of memory for each receive PDC. Each PDC is at least as large as the maximum packet size. Currently, the memory is allocated as a block of 1, 2 or 4 contiguous pages, considerably larger than the normal Ethernet MTU of 1500. A pointer to the PDC is inserted into the Available queue. jtRxPDCInitialize iterates through each PDC in the Available queue. The address of each PDC buffer is registered in the controller's PDC table in ascending order, starting from 0. jtRxPDCReplenish sends PDC receive commands to the controller. The commands are sent one at a time until the controller indicates that it can't accept more commands. Each PDC to be sent is dequeued from the Available queue and inserted into the In Progress queue. Then the table index of the PDC is sent to the controller as a receive command. The driver does not explicitly keep track of the PDC table index of each PDC buffer. Instead, the driver relies on the fact that PDCs are always removed and inserted from the queues in order. So the driver simply keeps track of the table index of the PDC at the head of the Available queue, and increments the index every time a PDC is dequeued from the Available queue. jtRxPDCReceive is called by the interrupt handler each time the controller receives one or more packets. Since the controller receives packets in order, the function knows that the packet(s) received have been placed into the PDCs at the head of the InProgress queue. The function repeatedly removes the PDC from the head of the InProgress queue and processes it, until all received packets have been processed. Each PDC may have one or more packets stored inside it. Each packet is preceded by a PDC receive header. The code iterates over the PDC until it encounters a null PDC header. For each packet found, the packet is checked for errors. Then, an sk_buff of the same size as the current packet is allocated, and the packet data is copied from PDC buffer to sk_buff. The packet is then given to the upper layers for processing. Once all the packets of a PDC have been processed, the PDC is returned to the Available queue. Once all of the PDCs have been processed, jtRxPDCReplenish is called to recycle PDC receive commands back into service. IIIf. PDx Transmit The following description is completely accurate only for standard mode. The differences when the driver is in performance mode will be descripted later. Call Tree: jtDevOpen +--jtTxPDxCreate +--jtTxPDCCreate +--jtTxPDLCreate +--jtTxPDxInitialize +--jtTxPDCInitialize +--jtTxPDLInitialize +--jtTxStart jtDevStartTransmit +--jtTxPDxTransmit +--jtTxPDLTransmit +--jtTxPDCGetNextAvailable +--jtTxPDCAppendPacket +--jtTxPDCSend jtDevInterrupt +--jtTxPDxDone jtDevClose +--jtTxStop +--jtTxPDxFlush +--jtTxPDxDestroy jtTxPDxCreate first allocates and initializes the queues TxPDCAvailableQueue, TxPDLAvailableQueue and TxPDxInProgressQueue. jtTxPDCCreate allocates a block of memory for each transmit PDC. Each PDC is at least as large as the maximum packet size. Currently, the memory is allocated as a block of 1, 2 or 4 contiguous pages, considerably larger than the normal Ethernet MTU of 1500. A pointer to the PDC is inserted into the PDC Available queue. jtTxPDLCreate allocates a single contiguous block of memory large enough to hold all transmit PDLs. jtTxPDCInitialize iterates through each PDC in the Available queue. The address of each PDC buffer is registered in the controller's PDC table in ascending order, starting from 0. jtRxPDLInitialize divides the PDL block into individual PDLs, which are aligned at cache line boundaries. Pointers to each PDL are inserted into the PDL Available queue. jtTxPDxTransmit tries to transmit as many packets as it can find. The first packet is passed as a parameter to the function, as per the standard Linux API. To find subsequent packets, the function dredges through the packet queues inside the interface's device structure. The queue dredging code is only active when the driver is compiled for kernel versions 2.0.x, since the structure of the device queues changed substantially in kernel versions 2.1.x. jtTxPDxTransmit loops over each packet until it runs out of packets or until the controller can't take any more transmit commands. If the current packet is larger than the specified threshold, jtTxPDLTransmit is called to send the packet via PDL. Otherwise, the packet will be sent via PDC. Sending the packet via PDC is a multistep operation. First, the code looks for a partially built PDC from a previous iteration. If there isn't a PDC, jtTxPDCGetNextAvailable is called to get an empty PDC. Next, jtTxPDCAppend is called to copy the packet into the PDC. Finally, the code peeks at the next packet. If no more packets are going into the PDC, either because there are no more packets, or the next packet is too large, the code calls jtTxPDCSend to actually send the PDC to the controller. jtTxPDxDone is called by the interrupt handler each time the controller completes transmit of one or more packets. Since the controller transmits packets in order, the function knows that the packet(s) transmitted were from the PDCs and PDLs at the head of the InProgress queue. The function repeatedly removes the head element of the InProgress queue and processes it, until each transmit has been processed. For each element taken from the queue, he driver determines whether the element is a PDL or PDC. If the element is a PDL, the function retrieves the address of the sk_buff associated with this PDL from the second fragment entry of the PDL. The sk_buff is freed, and the PDL is inserted into the PDL Available queue. If the element is a PDC, the PDC is simply inserted into the PDC available queue. When performance mode is enabled, jtTxPDxTransmit sends the packets and then checks the completion status. The completion status is also checked when the driver is out of PDCs. This implies that jtTxPDxDone can now be called not only from within the interrupt handler. Second the driver doesn't ask for an interrupt on transmit completion, and sets a chip timer when there are PDL(s) pending. The timer will be removed when there are no more packets to be processed. IV. References Level One Communications NetCelerator Micro-Architecture Specification, Level One Communications, Inc. */ /***************************************************************************** * Module Include Files ****************************************************************************/ #include #ifdef MODULE #ifdef MODVERSIONS #include #endif #include #include #else #define MOD_INC_USE_COUNT #define MOD_DEC_USE_COUNT #endif /***************************************************************************** * Include Files ****************************************************************************/ #include // u8, u16, etc. #include // inb()/outb() etc #include // SA_SHIRQ etc #include // udelay() #include // struct enet_statistics #include // release_region() etc #include // kmalloc, kfree #include // struct device and dev_xxx() #include // PCI_XXX #include // offsetof #include // eth_xxx() #include // Version compatibility #if (LINUX_VERSION_CODE < 0x20123) #define test_and_set_bit(val, addr) set_bit(val, addr) #define test_and_clear_bit(val, addr) clear_bit(val, addr) #endif #if (LINUX_VERSION_CODE < 0x20100) #include // pcibios_xxx() #else #include #endif #if (LINUX_VERSION_CODE < 0x20118) #define EXPORT_NO_SYMBOLS register_symtab(NULL) #endif #if (LINUX_VERSION_CODE < 0x20100) #define DEV_KFREE_SKB(pSkBuff, mode) dev_kfree_skb(pSkBuff, mode) #define GET_FREE_PAGES(gfp, order) __get_free_pages(gfp, order, 0) #else #define DEV_KFREE_SKB(pSkBuff, mode) dev_kfree_skb(pSkBuff); #define GET_FREE_PAGES(gfp, order) __get_free_pages(gfp, order); #endif #if (LINUX_VERSION_CODE < 0x20100) #define ioremap vremap #define iounmap vfree #endif #if (LINUX_VERSION_CODE < 0x20100) #define spinlock_t int #define spin_lock_init(a) {*(a) = 0;} #define spin_lock_irqsave(a,b) {save_flags(b); cli();} #define spin_unlock_irqrestore(a,b) restore_flags(b) #endif #if LINUX_VERSION_CODE < 0x20200 #define spin_lock(lock) spin_lock_irqsave(lock, Flags) #define spin_unlock(lock) spin_unlock_irqrestore(lock, Flags) #define atomic_read(v) (*v) #define atomic_set(v, a) (*v=a) #endif // Why isn't this in a header file? #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #define min(a,b) (((a) < (b)) ? (a) : (b)) /***************************************************************************** * Defined Constants ****************************************************************************/ typedef enum { JT_STATUS_SUCCESS, JT_STATUS_FAILURE, JT_STATUS_RESOURCES, JT_STATUS_PENDING, JT_STATUS_SWRESET_FAILED, JT_STATUS_DEVICE_NOT_FOUND, JT_STATUS_NOT_SUPPORTED, JT_STATUS_DIAGS_NOT_BUILT, JT_STATUS_INVALID_ADAPTER, JT_STATUS_INVALID_PARAMETER, JT_STATUS_MISSING_PARAMETER, JT_STATUS_INVALID_COMMAND, JT_STATUS_BUFFER_TOO_SMALL, JT_STATUS_NO_DATA, JT_STATUS_FILE_IO_FAILED, JT_STATUS_INVALID_STATE, JT_STATUS_IOCTL_FAILED, JT_STATUS_TIMEOUT } JT_STATUS; typedef enum { JT_PHY_STATUS_LINK, JT_PHY_STATUS_FAILURE, JT_PHY_STATUS_RESTART, } JT_PHY_STATUS; //---------------------------------------------------------------------------- // // Constants for the card as a whole // //---------------------------------------------------------------------------- #if !defined(PCI_VENDOR_ID_LEVELONE2) #define PCI_VENDOR_ID_LEVELONE2 0x1308 #endif #if !defined(PCI_VENDOR_ID_LEVELONE3) #define PCI_VENDOR_ID_LEVELONE3 0x1394 #endif #if !defined(PCI_DEVICE_ID_JT1001) #define PCI_DEVICE_ID_JT1001 0x0001 #endif // Sizes #define JT_MAX_MTU 16*PAGE_SIZE #define TX_PDC_TABLE_COUNT 64 #define RX_PDC_TABLE_COUNT 64 #define RX_FIFO_SIZE 64*1024/4 // If you change this, also need to change initializers for // tx_threshold, tx_flowcontrol, rx_flowcontrol etc. #define JT_MAX_CONTROLLER_COUNT 8 // Constants for driver control structures #define JT_MAX_LOOP_COUNT 4 //---------------------------------------------------------------------------- // // CSR REGISTER DEFINES // // The Mercury device's CSRs are arranged as a contiguous sequence of // mostly 32 bit registers. Some of the registers are 64 bits wide. The 32 // bit registers are DWORD aligned and the 64 bit registers are QWORD // aligned (assuming the device's base address is QWORD aligned). The // register indices are given as ordinal values (e.g. for use as array // indices). // //---------------------------------------------------------------------------- #define CSR_REG_SIZE 0x04 // All CSR reside on DWORD boundaries #define CSR00 (CSR_REG_SIZE*0x00) // Mode Register - 1 #define CSR01 (CSR_REG_SIZE*0x01) // Mode Register - 2 #define CSR02 (CSR_REG_SIZE*0x02) // Transmit PDC Buffer Address Table Index #define CSR03 (CSR_REG_SIZE*0x03) // Reserved #define CSR04 (CSR_REG_SIZE*0x04) // Transmit PDC Buffer Address LSD #define CSR05 (CSR_REG_SIZE*0x05) // Transmit PDC Buffer Address MSD #define CSR06 (CSR_REG_SIZE*0x06) // Receive PDC Buffer Address Table Index #define CSR07 (CSR_REG_SIZE*0x07) // Reserved #define CSR08 (CSR_REG_SIZE*0x08) // Receive PDC Buffer Address LSD #define CSR09 (CSR_REG_SIZE*0x09) // Receive PDC Buffer Address MSD #define CSR10 (CSR_REG_SIZE*0x0a) // E2PROM #define CSR11 (CSR_REG_SIZE*0x0b) // Bus Master Status Register #define CSR12 (CSR_REG_SIZE*0x0c) // Transmit PDL Address Register LSD #define CSR13 (CSR_REG_SIZE*0x0d) // Transmit PDL Address Register MSD #define CSR14 (CSR_REG_SIZE*0x0e) // Receive PDL Address Register LSD #define CSR15 (CSR_REG_SIZE*0x0f) // Receive PDL Address Register MSD #define CSR16 (CSR_REG_SIZE*0x10) // Transmit PDC Register #define CSR17 (CSR_REG_SIZE*0x11) // Receive PDC Register #define CSR18 (CSR_REG_SIZE*0x12) // Interrupt Period Register #define CSR19 (CSR_REG_SIZE*0x13) // Transmit FIFO Packet Count Register #define CSR20 (CSR_REG_SIZE*0x14) // Transmit FIFO Watermark Register #define CSR21 (CSR_REG_SIZE*0x15) // Transmit FIFO DWORDs Free Register #define CSR22 (CSR_REG_SIZE*0x16) // Transmit FIFO Write Register #define CSR23 (CSR_REG_SIZE*0x17) // Reserved #define CSR24 (CSR_REG_SIZE*0x18) // Receive FIFO Read Register #define CSR25 (CSR_REG_SIZE*0x19) // Reserved #define CSR26 (CSR_REG_SIZE*0x1a) // Receive FIFO DWORD Count Register #define CSR27 (CSR_REG_SIZE*0x1b) // Receive FIFO Watermark Register #define CSR28 (CSR_REG_SIZE*0x1c) // Receive FIFO Packet Count Register #define CSR29 (CSR_REG_SIZE*0x1d) // Command Register #define CSR30 (CSR_REG_SIZE*0x1e) // Interrupt Mask Register #define CSR31 (CSR_REG_SIZE*0x1f) // Reserved #define CSR32 (CSR_REG_SIZE*0x20) // Interrupt Status Register #define CSR33 (CSR_REG_SIZE*0x21) // Reserved #define CSR34 (CSR_REG_SIZE*0x22) // Multicast Hash Table Register LSD #define CSR35 (CSR_REG_SIZE*0x23) // Multicast Hash Table Register MSD #define CSR36 (CSR_REG_SIZE*0x24) // LED 0 Configuration Register #define CSR37 (CSR_REG_SIZE*0x25) // LED 1 Configuration Register #define CSR38 (CSR_REG_SIZE*0x26) // LED 2 Configuration Register #define CSR39 (CSR_REG_SIZE*0x27) // LED 3 Configuration Register #define CSR40 (CSR_REG_SIZE*0x28) // LED Signal Latch Register #define CSR41 (CSR_REG_SIZE*0x29) // E2PROM Data Register #define CSR42 (CSR_REG_SIZE*0x2a) // LAN Physical Address Register LSD #define CSR43 (CSR_REG_SIZE*0x2b) // LAN Physical Address Register MSW #define CSR44 (CSR_REG_SIZE*0x2c) // G/MII Address Register #define CSR45 (CSR_REG_SIZE*0x2d) // G/MII Data Register #define CSR46 (CSR_REG_SIZE*0x2e) // Dropped Packet Count Register #define CSR47 (CSR_REG_SIZE*0x2f) // Errored Packet Count Register #define CSR48 (CSR_REG_SIZE*0x30) // Late Collision Count Register #define CSR49 (CSR_REG_SIZE*0x31) // aMACID #define CSR50 (CSR_REG_SIZE*0x32) // aFramesTransmittedOK #define CSR51 (CSR_REG_SIZE*0x33) // aSingleCollisionFrames #define CSR52 (CSR_REG_SIZE*0x34) // aMultipleCollisionFrames #define CSR53 (CSR_REG_SIZE*0x35) // aFramesReceivedOK #define CSR54 (CSR_REG_SIZE*0x36) // aFrameCheckSequenceErrors #define CSR55 (CSR_REG_SIZE*0x37) // aAlignmentErrors #define CSR56 (CSR_REG_SIZE*0x38) // Reserved #define CSR57 (CSR_REG_SIZE*0x39) // Reserved #define CSR58 (CSR_REG_SIZE*0x3a) // Timer 0 Count Register #define CSR59 (CSR_REG_SIZE*0x3b) // Timer 0 Interrupt Trigger Register #define CSR60 (CSR_REG_SIZE*0x3c) // Timer 1 Count Register #define CSR61 (CSR_REG_SIZE*0x3d) // Timer 1 Interrupt Trigger Register #define CSR62 (CSR_REG_SIZE*0x3e) // Debug Address Register #define CSR63 (CSR_REG_SIZE*0x3f) // Debug Data Register // Set/Reset control bits #define SERECL0 0x00000001L #define SERECL1 0x00000100L #define SERECL2 0x00010000L #define SERECL3 0x01000000L // Mode register offset and bit positions. #define MODE_REG_1 (CSR_REG_SIZE*0x00) #define SWRE 0x00000002L #define DEBTOD 0x00000004L #define TXFLCTEN 0x00000008L #define RXTRPR 0x00000010L #define GMSTPOEN 0x00000020L #define TXPPEN 0x00000040L #define RMPPEN 0x00000080L #define TXEN 0x00000200L #define RXEN 0x00000400L #define MCEN 0x00000800L #define BCEN 0x00001000L #define POEN 0x00002000L #define UCEN 0x00004000L #define LGPKEN 0x00008000L #define PACREN 0x00020000L #define PAERPKEN 0x00040000L #define TXCREN 0x00080000L #define DBMDEN 0x00100000L #define MGPKEN 0x00200000L #define MGMCBCEN 0x00400000L #define RXFLCTEN 0x00800000L #define VLEN 0x02000000L #define VLTBEN 0x04000000L #define VLRMID 0x08000000L #define VLISGB 0x10000000L #define USPIMD0 0x20000000L #define USPIMD1 0x40000000L #define MODE_REG_2 (CSR_REG_SIZE*0x01) #define LPBKMD_MASK 0x000000E0L #define LPBKMD_MAC 0x00000040L #define LPBKMD_PHY 0x00000080L #define TXIPCKEN 0x00000200L #define TXTPCKEN 0x00000400L #define TXUPCKEN 0x00000800L #define RXIPCKEN 0x00001000L #define RXTPCKEN 0x00002000L #define RXUPCKEN 0x00004000L #define PACKEREN 0x00008000L #define TX_PDC_BUF_ADR_TBL_IX (CSR_REG_SIZE*0x02) #define TX_TBIX_MASK 0x0000003fL #define PRODUCT_ID (CSR_REG_SIZE*0x03) #define TX_PDC_BUF_ADR_LO (CSR_REG_SIZE*0x04) #define TX_PDC_BUF_ADR_HI (CSR_REG_SIZE*0x05) #define RX_PDC_BUF_ADR_TBL_IX (CSR_REG_SIZE*0x06) #define RX_TBIX_MASK 0x0000003fL #define RESERVED07 (CSR_REG_SIZE*0x07) #define RX_PDC_BUF_ADR_LO (CSR_REG_SIZE*0x08) #define RX_PDC_BUF_ADR_HI (CSR_REG_SIZE*0x09) #define EE_PROM (CSR_REG_SIZE*0x0a) #define EEPMPN 0x00000001L #define EERDCM 0x00000002L #define EEWTCM 0x00000004L #define EECKSME 0x00000010L #define EEMU 0x00000020L #define EESL 0x00000040L #define EEAD 0x00001f00L #define EXRMTM 0x000f0000L #define FLPN 0x00100000L #define FLWTEN 0x00200000L #define CHIP_STATUS_REG (CSR_REG_SIZE*0x0b) #define TXPKAC 0x00000001L #define RXPKAV 0x00000002L #define FLCTST 0x00000004L #define UPIST0 0x00000008L #define UPIST1 0x00000010L #define TXIL 0x00000020L #define RXIL 0x00000040L #define TX_PDL_ADDR_REG_LO (CSR_REG_SIZE*0x0c) #define TX_PDL_ADDR_REG_HI (CSR_REG_SIZE*0x0d) #define IPCKIS 0x00200000L #define TPCKIS 0x00400000L #define UPCKIS 0x00800000L #define VLIS 0x10000000L #define DMDNINRQ 0x80000000L #define TX_PDL_FGCN_SHIFT 16 #define TX_PDL_VLTBIX_SHIFT 24 #define PDL_FGCN_MASK 0x001f0000L #define RX_PDL_ADDR_REG_LO (CSR_REG_SIZE*0x0e) #define RX_PDL_ADDR_REG_HI (CSR_REG_SIZE*0x0f) #define RX_PDL_FGCN_SHIFT 16 #define TX_PDC_REG (CSR_REG_SIZE*0x10) #define XFDNINRQ 0x01000000L #define TX_PDC_REG_BFID_SHIFT 16 #define TX_PDC_HDTYPE 0x00020000L #define RX_PDC_REG (CSR_REG_SIZE*0x11) #define RXINRQ 0x80000000L #define RX_PDC_REG_BFID_SHIFT 16 #define RX_PDC_HDTYPE 0x00010000L #define INTR_PERIOD_REG (CSR_REG_SIZE*0x12) #define TX_FIFO_PKT_CNT_REG (CSR_REG_SIZE*0x13) #define TX_FIFO_WTR_MRK_REG (CSR_REG_SIZE*0x14) #define TX_FIFO_DWORDS_FREE_REG (CSR_REG_SIZE*0x15) #define TX_FIFO_WRITE_REG (CSR_REG_SIZE*0x16) #define RESERVED23 (CSR_REG_SIZE*0x17) #define RX_FIFO_READ_REG (CSR_REG_SIZE*0x18) // Status Bits for the first 32 bits of Rx FIFO Header #define PKT_HDR_LENGTH_MASK 0x0000ffffL #define PKT_HDR_TYPE_MASK 0x001f0000L #define ERLN 0x00400000L #define PHAD 0x00800000L #define BCAD 0x01000000L #define MCAD 0x02000000L #define LGPK 0x04000000L #define EROV 0x08000000L #define ERCR 0x10000000L #define ERRU 0x20000000L #define ERAL 0x40000000L #define RXER 0x80000000L // Status Bits for the second 32 bits of Rx FIFO Header #define VLTBIX_RX_MASK 0x0000000fL #define VLHT 0x00000010L #define IPCKER 0x00000100L #define TPCKER 0x00000200L #define UPCKER 0x00000400L #define IPHDPN 0x00000800L #define TPHDPN 0x00001000L #define UPHDPN 0x00002000L #define RX_HDR1_ERROR_MASK ( ERAL | ERRU | ERCR | EROV | ERLN ) #define RX_HDR2_ERROR_MASK ( UPCKER | TPCKER | IPCKER ) #define RX_HDR1_STATUS_MASK ( LGPK ) #define RX_HDR2_STATUS_MASK ( VLTBIX_RX_MASK | VLHT | IPHDPN | TPHDPN | UPHDPN ) #define RESERVED25 (CSR_REG_SIZE*0x19) #define RX_FIFO_DWORD_CNT_REG (CSR_REG_SIZE*0x1a) #define RX_FIFO_WTR_MRK_REG (CSR_REG_SIZE*0x1b) #define RX_FIFO_PKT_CNT_REG (CSR_REG_SIZE*0x1c) // Command register offset and bit positions. #define COMMAND_REG (CSR_REG_SIZE*0x1d) #define SLMDTXCM 0x00000002L #define RXFISKPK 0x00000004L #define DLINRQ 0x00000008L #define PEINRQ 0x00000010L #define TMEN0 0x00000020L #define TMEN1 0x00000040L // Interrupt mask register offset and bit positions. #define INTR_MASK_REG (CSR_REG_SIZE*0x1e) #define TXCMEMMS 0x00000002L #define TXFIWMMS 0x00000004L #define TXDMDNMS 0x00000008L #define TMEXMS 0x00000040L #define INENMS 0x00000080L #define RXCMEMMS 0x00000200L #define RXFIWMMS 0x00000400L #define RXMS 0x00000800L #define RXPDMS 0x00001000L #define PHLASTMS 0x00002000L #define RXMGPKMS 0x00004000L #define USPIMS0 0x00020000L #define USPIMS1 0x00040000L #define TMMS0 0x00080000L #define TMMS1 0x00100000L #define DMDNMSK 0xff000000L #define RESERVED31 (CSR_REG_SIZE*0x1f) #define INTR_STATUS_REG (CSR_REG_SIZE*0x20) #define TXCMEMIN 0x00000002L #define TXFIWMIN 0x00000004L #define TXDMDNIN 0x00000008L #define TMEXIN 0x00000040L #define RXCMEMIN 0x00000200L #define RXFIWMIN 0x00000400L #define RXIN 0x00000800L #define RXPDIN 0x00001000L #define PHLASTIN 0x00002000L #define RXMGPKIN 0x00004000L #define USPIIN0 0x00020000L #define USPIIN1 0x00040000L #define TMIN0 0x00080000L #define TMIN1 0x00100000L #define INTR_STATUS_RXDMDNCN 0xff000000L #define INTR_STATUS_RXDMDNCN_SHIFT 24 #define RESERVED33 (CSR_REG_SIZE*0x21) #define MCAST_HASH_TBL_REG_LO (CSR_REG_SIZE*0x22) #define MCAST_HASH_TBL_REG_HI (CSR_REG_SIZE*0x23) // LED Register Defines #define LED_00_REG (CSR_REG_SIZE*0x24) #define LED_LDPL 0x00000002L #define LED_LDEN 0x00000004L #define LED_PUXP 0x00000008L #define LED_10MB 0x00000010L #define LED_100MB 0x00000020L #define LED_1000MB 0x00000040L #define LED_FD 0x00000100L #define LED_AN 0x00000200L #define LED_LKST 0x00000400L #define LED_ADMA 0x00000800L #define LED_TX 0x00001000L #define LED_RX 0x00002000L #define LED_JA 0x00004000L #define LED_CO 0x00008000L #define LED_CA 0x00010000L // Selection of events that light the LEDs. #define JT_LED_0_MODE LED_LDEN|LED_PUXP|LED_LKST #define JT_LED_1_MODE LED_LDEN|LED_PUXP|LED_TX #define JT_LED_2_MODE LED_LDEN|LED_PUXP|LED_RX #define JT_LED_3_MODE LED_LDEN|LED_PUXP|LED_CO #define LED_01_REG (CSR_REG_SIZE*0x25) #define LED_02_REG (CSR_REG_SIZE*0x26) #define LED_03_REG (CSR_REG_SIZE*0x27) #define RESERVED40 (CSR_REG_SIZE*0x21) #define EEPROM_DATA_REG (CSR_REG_SIZE*0x29) #define LAN_PHY_ADR_LO (CSR_REG_SIZE*0x2a) #define LAN_PHY_ADR_HI (CSR_REG_SIZE*0x2b) #define GMII_ACCESS_REG (CSR_REG_SIZE*0x2c) #define GMRRIX_MASK 0x0000001fL #define GMCM 0x00000080L #define GMPHAD_MASK 0x00001f00L #define GMST 0x00008000L #define GMDA 0xffff0000L #define GMII_MODE_REG (CSR_REG_SIZE*0x2d) #define GMWRSP_MASK 0x00000003L #define GMWRSP_RESERVED 0x00000003L #define GMWRSP_1000MB 0x00000002L #define GMWRSP_100MB 0x00000001L #define GMWRSP_10MB 0x00000000L #define GMFD 0x00000004L #define GMIFPR 0x00000100L #define GMPCEN 0x00000200L #define GMRPP_MASK 0x60000000L #define RPEN 0x80000000L #define STAT_INDEX_REG (CSR_REG_SIZE*0x2e) #define STAT_ID_TX_OK 0 #define STAT_ID_SINGLE_COL 1 #define STAT_ID_MULTI_COL 2 #define STAT_ID_RX_OK 3 #define STAT_ID_CRC_ERROR 4 #define STAT_ID_ALIGN_ERROR 5 #define STAT_ID_DROPPED 6 #define STAT_ID_RX_ERROR 7 #define STAT_ID_TX_ERROR 8 #define STAT_ID_LATE_COL 9 #define STAT_ID_RUNT 10 #define STAT_ID_TOO_LONG 11 #define STAT_ID_VLAN_OK 12 #define STAT_ID_VLAN_DISCARD 13 #define STAT_ID_IP_CKSUM_ERR 14 #define STAT_ID_UDP_CKSUM_ERR 15 #define STAT_ID_PKT_LENGTH_ERR 16 #define STAT_ID_TCP_CKSUM_ERR 17 #define STAT_ID_IP_NOT_V4 18 #define STAT_ID_EXCESS_COL 19 #define STAT_ID_UNICAST_OK 20 #define STAT_ID_MULTICAST_OK 21 #define STAT_ID_BROADCAST_OK 22 #define STAT_ID_PAUSE_RX 23 #define STAT_ID_PAUSE_TX 24 #define STAT_ID_CONTROL_RX 25 #define STAT_ID_DEFERED 26 #define STAT_ID_EXCESS_DEFERED 27 #define STAT_ID_CARRIER_SENSE 28 #define STAT_DATA_REG (CSR_REG_SIZE*0x2f) #define VLAN_TCI_TBL_REG (CSR_REG_SIZE*0x30) #define VLTBCM 0x00200000L #define VLID_TBL_MASK 0x00000fffL #define VLUSPR_TBL_MASK 0x0000e000L #define VLTBIX_TBL_MASK 0x000f0000L #define VLAN_TAG_PID_REG (CSR_REG_SIZE*0x31) #define RESERVED50 (CSR_REG_SIZE*0x32) #define CMD_STATUS_REG (CSR_REG_SIZE*0x33) #define FLOW_CTL_WTR_MRK_REG (CSR_REG_SIZE*0x34) #define RESERVED53 (CSR_REG_SIZE*0x35) #define RESERVED54 (CSR_REG_SIZE*0x36) #define RESERVED55 (CSR_REG_SIZE*0x37) #define RESERVED56 (CSR_REG_SIZE*0x38) #define RESERVED57 (CSR_REG_SIZE*0x39) #define TIMER0_COUNT_REG (CSR_REG_SIZE*0x3a) #define TIMER0_INT_TRIG_REG (CSR_REG_SIZE*0x3b) #define TIMER1_COUNT_REG (CSR_REG_SIZE*0x3c) #define TIMER1_INT_TRIG_REG (CSR_REG_SIZE*0x3d) #define DEBUG_COMMAND_REG (CSR_REG_SIZE*0x3e) #define DEBUG_BIST 0x00000001L #define DEBUG_DBST 0x80000000L #define DEBUG_DATA_REG (CSR_REG_SIZE*0x3f) //---------------------------------------------------------------------------- // End of CSRs //---------------------------------------------------------------------------- // Magic packet constants #define MAGIC_PACKET_ENABLED 1 #define MAGIC_PACKET_DISABLED 0 // VLAN Constants #define VLAN_TABLE_ENTRIES 16 #define VLAN_ENTRY_IN_USE 0x80000000 #define VLAN_ID_MASK 0x00000fff #define VLAN_PRIORITY_MASK 0x0000e000 #define VLAN_PRIORITY_SHIFT 13 #define VLAN_FILTER_ENABLE 0x00000004 #define VLAN_STRIP 0x00000008 #define VLAN_INSERT 0x00000010 // Transmit/Receive Modes #define TX_IOMODE_PIO 1 #define TX_IOMODE_BUSMASTER 2 #define RX_IOMODE_PIO 1 #define RX_IOMODE_PDL 2 #define RX_IOMODE_PDC 4 // Link parameters #define DUPLEX_MODE_AUTO 1 #define DUPLEX_MODE_HALF 2 #define DUPLEX_MODE_FULL 4 #define LINK_SPEED_AUTO 1 #define LINK_SPEED_10 2 #define LINK_SPEED_100 4 #define LINK_SPEED_1000 8 //---------------------------------------------------------------------------- // PHY Indentification Values //---------------------------------------------------------------------------- // Phy Type #define PHY_TYPE_AUTO 1 #define PHY_TYPE_PCS 2 #define PHY_TYPE_MII 4 #define PHY_TYPE_GMII 8 // Level One PCS Phy ID #define HG1_PCS_PHY_ID_1 0x0382 #define HG1_PCS_PHY_ID_2 0x0c00 // Level One LXT970 10/100 #define LEVEL1_PHY_ID_1 0x7810 #define LEVEL1_PHY_ID_2 0x0000 // Level One LXT970 10/100 (Second version) #define LEVEL1_PHY_ID_1_B 0x7810 #define LEVEL1_PHY_ID_2_B 0x0003 // National 10/100 Phy #define NATIONAL_PHY_ID_1 0x2000 #define NATIONAL_PHY_ID_2 0x5c00 typedef enum { PHY_LEVEL1_PCS, PHY_NATIONAL, PHY_LEVEL1 } PHY_ID; //---------------------------------------------------------------------------- // PCS/GMII Phy Register Definitions //---------------------------------------------------------------------------- #define PHY_CONTROL_REG 0 // One PCS/GMII Specific Bit Follows #define PHY_CTL_GSPSEL 0x0040 #define PHY_CTL_COLTST 0x0080 #define PHY_CTL_FDMD 0x0100 #define PHY_CTL_RSAN 0x0200 #define PHY_CTL_ISLT 0x0400 #define PHY_CTL_PWRDN 0x0800 #define PHY_CTL_ANEN 0x1000 // SPSEL is PCS/GMII Specific if GSPEL = 1 #define PHY_CTL_SPSEL 0x2000 #define PHY_CTL_LPBK 0x4000 #define PHY_CTL_RST 0x8000 #define PHY_STATUS_REG 1 #define PHY_STAT_EXT_CAP 0x0001 #define PHY_STAT_JAB_DET 0x0002 #define PHY_STAT_LINK_OK 0x0004 #define PHY_STAT_AN_ABLE 0x0008 #define PHY_STAT_RFLT 0x0010 #define PHY_STAT_AN_DONE 0x0020 #define PHY_STAT_MFPRESUP 0x0040 // One PCS/GMII Specific Bit Follows #define PHY_STAT_EXTSTS 0x0100 #define PHY_STAT_100T2_HD 0x0200 #define PHY_STAT_100T2_FD 0x0400 #define PHY_STAT_10_HD 0x0800 #define PHY_STAT_10_FD 0x1000 #define PHY_STAT_100X_HD 0x2000 #define PHY_STAT_100X_FD 0x4000 #define PHY_STAT_100T4 0x8000 #define PHY_ID_1_REG 2 #define PHY_ID_2_REG 3 #define PHY_AUTO_NEG_AD_REG 4 #define PHY_PCS_AD_FD 0x0020 #define PHY_PCS_AD_HD 0x0040 #define PHY_PCS_AD_PAUSE 0x0180 #define PHY_PCS_AD_PAUSE_NO 0x0000 #define PHY_PCS_AD_PAUSE_SYM 0x0080 #define PHY_PCS_AD_PAUSE_ASYM 0x0100 #define PHY_PCS_AD_PAUSE_LOCAL 0x0180 #define PHY_PCS_AD_RFLT 0x3000 #define PHY_PCS_AD_RFLT_OK 0x0000 #define PHY_PCS_AD_RFLT_LNK_FAIL 0x1000 #define PHY_PCS_AD_RFLT_OFFLINE 0x2000 #define PHY_PCS_AD_RFLT_AN_ERROR 0x3000 #define PHY_PCS_AD_NXPG 0x8000 #define PHY_MII_SEL_MASK 0x001f #define PHY_MII_SEL_8023 0x0001 #define PHY_MII_AD_10T 0x0020 #define PHY_MII_AD_10T_FD 0x0040 #define PHY_MII_AD_100T 0x0080 #define PHY_MII_AD_100T_FD 0x0100 #define PHY_MII_AD_100T4 0x0200 #define PHY_MII_AD_RFLT 0x2000 #define PHY_MII_AD_NXPG 0x8000 #define PHY_AUTO_NEG_PART_BASE_REG 5 // See PHY_AUTO_NEG_AD_REG #define PHY_AUTO_NEG_EXP_REG 6 #define PHY_PCS_AN_EXP_NP_ABLE 0x0002 #define PHY_PCS_AN_EXP_PG_RCVD 0x0004 #define PHY_AUTO_NEG_NEXT_PAGE_REG 7 #define PHY_PCS_AN_NP_MUCF 0x03ff #define PHY_PCS_AN_NP_TOG 0x0800 #define PHY_PCS_AN_NP_ACK2 0x1000 #define PHY_PCS_AN_NP_MSPG 0x2000 #define PHY_PCS_AN_NP_NXPG 0x8000 #define PHY_AUTO_NEG_PART_NEXT_PAGE_REG 8 // See PHY_AUTO_NEG_NEXT_PAGE_REG #define PHY_100T_CONTROL_REG 9 #define PHY_100T_STATUS_REG 10 #define PHY_PCS_EXT_STATUS_REG 11 #define PHY_PCS_EXT_STAT_1000T_HD 0x1000 #define PHY_PCS_EXT_STAT_1000T_FD 0x2000 #define PHY_PCS_EXT_STAT_1000X_HD 0x4000 #define PHY_PCS_EXT_STAT_1000X_FD 0x8000 #define PHY_LVL1_CHIP_STAT_REG 20 #define PHY_LVL1_CHIP_STAT_ANDONE 0x0200 #define PHY_LVL1_CHIP_STAT_100M 0x0800 #define PHY_LVL1_CHIP_STAT_DUP 0x1000 #define PHY_LVL1_CHIP_STAT_LINK 0x2000 #define PHY_NAT_CHIP_STAT_REG 0x19 #define PHY_NAT_CHIP_STAT_10M 0x0040 #define PHY_NAT_CHIP_STAT_DUP 0x0080 #define MII_TIMEOUT_MILLISECONDS 100 /***************************************************************************** * Type Definitions ****************************************************************************/ // Standard Level One type names typedef u64 UINT64, *PUINT64; typedef u32 UINT32, *PUINT32; typedef u16 UINT16, *PUINT16; typedef u8 UINT8, *PUINT8; typedef void VOID, *PVOID; typedef UINT8 BOOLEAN, *PBOOLEAN; // Queue Management Structures typedef struct { void ** Elements; volatile UINT32 Head; volatile UINT32 Tail; UINT32 Capacity; } JT_QUEUE; // PDL Structure #define MAXIMUM_PDL_FRAGMENTS 31 typedef struct { UINT32 FragmentAddressLow; UINT32 FragmentAddressHigh; UINT16 FragmentLength; UINT16 FragLengthReserved; UINT32 FragmentReserved; } PDL_FRAGMENT, *PPDL_FRAGMENT; typedef struct { UINT32 Header; UINT32 Header2; PDL_FRAGMENT Fragment[1]; } PDL, *PPDL; // Each PDL has room for headers, 1 fragment, and 8 bytes of // free space at the end (used for the skbuff pointer). #define PDL_MIN_ALIGNMENT 32 #define PDL_FLAG 0x01 // PDC Header Structures typedef struct { UINT32 Header; UINT32 Data[0]; } TX_PDC, *PTX_PDC; typedef struct { UINT32 Header; UINT32 Header2; UINT32 Data[0]; } RX_PDC, *PRX_PDC; // PHY Structure typedef struct { BOOLEAN Connected; PHY_ID PhyId; UINT8 PhyType; UINT8 PhyAddress; } PHY_ELEMENT, *PPHY_ELEMENT; // Controller instance typedef struct DEV_INSTANCE { // Controller location UINT16 IORange; UINT32 ROMBaseLow; UINT32 ROMBaseHigh; UINT32 ROMRange; UINT8 PciBus; UINT8 PciDeviceFunction; #if defined(USE_MEMORY_BASE) && LINUX_VERSION_CODE > 0x20200 UINT32 MapAddr; #endif #if defined(WORKAROUND_WRITE_COMBINE) UINT32 LastPortOffset; #endif // Phy fields PHY_ELEMENT CurrentPhy; UINT16 OverrideSpeed; UINT8 OverrideDuplexMode; UINT16 Speed; UINT8 DuplexMode; // Controller options UINT8 NodeAddress[ETH_ALEN]; UINT16 CacheLineSize; UINT32 MulticastHashHigh; UINT32 MulticastHashLow; UINT8 MagicPacketMode; // Transmit modes UINT8 TxIOMode; UINT32 TxPacketHeader; BOOLEAN TxFlowControl; // Transmit methods JT_STATUS (* pTxCreate) ( struct DEV_INSTANCE * ); JT_STATUS (* pTxInitialize)( struct DEV_INSTANCE * ); int (* pTxTransmit) ( struct DEV_INSTANCE *, struct sk_buff * ); JT_STATUS (* pTxFlush) ( struct DEV_INSTANCE * ); JT_STATUS (* pTxDestroy) ( struct DEV_INSTANCE * ); // PIO transmit mode UINT16 TxFifoDwordFreeCount; // PDC transmit UINT32 TxPDCCount; UINT32 TxPDCLength; UINT32 TxPDCOrder; UINT32 TxPDCTableCount; UINT32 TxPDCNextTableIndex; JT_QUEUE TxPDCAvailableQueue; // PDL transmit UINT32 TxPDLCount; void * pTxPDLBase; UINT32 TxPDLOrder; JT_QUEUE TxPDLAvailableQueue; UINT32 TxPDLAlignment; #if defined (PERFORMANCE_TX) unsigned long TxPDLTimerOn; atomic_t TxPDLPending; spinlock_t DriverLock; /* Normal IRQ lock */ #endif // Busmaster transmit mode unsigned long TxFull; // unsigned long for atomic bitops UINT32 TxPDxThreshold; UINT8 TxPDxCommandFreeCount; JT_QUEUE TxPDxInProgressQueue; // Receive modes UINT8 RxIOMode; BOOLEAN RxFlowControl; // Receive methods JT_STATUS (* pRxCreate) ( struct DEV_INSTANCE * ); JT_STATUS (* pRxInitialize)( struct DEV_INSTANCE * ); JT_STATUS (* pRxReplenish) ( struct DEV_INSTANCE * ); JT_STATUS (* pRxReceive) ( struct DEV_INSTANCE *, UINT32 ); JT_STATUS (* pRxFlush) ( struct DEV_INSTANCE * ); JT_STATUS (* pRxDestroy) ( struct DEV_INSTANCE * ); // PDC receive mode UINT32 RxPDCCount; UINT32 RxPDCLength; UINT32 RxPDCOrder; UINT32 RxPDCTableCount; UINT32 RxPDCNextTableIndex; // PDL receive mode UINT32 RxPDLCount; void * pRxPDLBase; UINT32 RxPDLOrder; UINT32 RxPDLAlignment; // PDC/PDL receive modes UINT8 RxPDxCommandFreeCount; JT_QUEUE RxPDxAvailableQueue; JT_QUEUE RxPDxInProgressQueue; // CRC check #if defined(WORKAROUND_CRC_CHECK) UINT32 RxCrcCheck; #endif #if defined(EMULATION) // Emulator physical memory handles UINT64 TxPDLBaseEmuAddress; UINT64 RxPDLBaseEmuAddress; #endif // Linux bookkeeping struct device * pDev; struct enet_statistics Statistics; } DEV_INSTANCE, *PDEV_INSTANCE; /***************************************************************************** * Global and Static Variables ****************************************************************************/ #if defined(JT_DEBUG) #define STATIC #define INLINE #else #define STATIC static #define INLINE extern inline #endif // List of controllers detected int jtControllerCount = 0; struct device * jtControllers[JT_MAX_CONTROLLER_COUNT] = {0,}; const char jtProductName[] = "Level One NetCelerator Family Gigabit Ethernet driver"; // User-configurable options STATIC int mtu [JT_MAX_CONTROLLER_COUNT] = {0,}; STATIC int tx_mode [JT_MAX_CONTROLLER_COUNT] = {2, 0,}; STATIC int tx_ringsize [JT_MAX_CONTROLLER_COUNT] = {0,}; STATIC int tx_threshold [JT_MAX_CONTROLLER_COUNT] = {-1, -1, -1, -1, -1, -1, -1, -1,}; STATIC int tx_flowcontrol[JT_MAX_CONTROLLER_COUNT] = {-1, -1, -1, -1, -1, -1, -1, -1,}; STATIC int rx_mode [JT_MAX_CONTROLLER_COUNT] = {4, 0,}; STATIC int rx_ringsize [JT_MAX_CONTROLLER_COUNT] = {0,}; STATIC int rx_flowcontrol[JT_MAX_CONTROLLER_COUNT] = {-1, -1, -1, -1, -1, -1, -1, -1,}; STATIC int speed [JT_MAX_CONTROLLER_COUNT] = {0, 0,}; STATIC char * duplex [JT_MAX_CONTROLLER_COUNT] = {0, 0,}; #if defined(WORKAROUND_CRC_CHECK) STATIC int rx_crc [JT_MAX_CONTROLLER_COUNT] = {0,}; #endif #if LINUX_VERSION_CODE > 0x20115 MODULE_DESCRIPTION("Level One NetCelerator Family Gigabit Ethernet driver"); MODULE_PARM(mtu, "1-" __MODULE_STRING(JT_MAX_CONTROLLER_COUNT) "i"); MODULE_PARM(tx_mode, "1-" __MODULE_STRING(JT_MAX_CONTROLLER_COUNT) "i"); MODULE_PARM(tx_ringsize, "1-" __MODULE_STRING(JT_MAX_CONTROLLER_COUNT) "i"); MODULE_PARM(tx_threshold, "1-" __MODULE_STRING(JT_MAX_CONTROLLER_COUNT) "i"); MODULE_PARM(tx_flowcontrol, "1-" __MODULE_STRING(JT_MAX_CONTROLLER_COUNT) "i"); MODULE_PARM(rx_mode, "1-" __MODULE_STRING(JT_MAX_CONTROLLER_COUNT) "i"); MODULE_PARM(rx_ringsize, "1-" __MODULE_STRING(JT_MAX_CONTROLLER_COUNT) "i"); MODULE_PARM(rx_flowcontrol, "1-" __MODULE_STRING(JT_MAX_CONTROLLER_COUNT) "i"); MODULE_PARM(speed, "1-" __MODULE_STRING(JT_MAX_CONTROLLER_COUNT) "i"); MODULE_PARM(duplex, "1-" __MODULE_STRING(JT_MAX_CONTROLLER_COUNT) "s"); #if defined(WORKAROUND_CRC_CHECK) MODULE_PARM(rx_crc, "1-" __MODULE_STRING(JT_MAX_CONTROLLER_COUNT) "i"); #endif #endif // Debugging options #if defined(JT_DEBUG) int break_init = 0; // If 1, execute breakpoint in init_module int sleep_init = 0; // If non-0, sleep in init_module int verbose = 1; // If 1, enable verbose debugging info #endif // Software breakpoints // Assertion checking // Debugging output #if defined(JT_DEBUG) #define BreakPoint() \ { printk(KERN_CRIT "Breakpoint at %s function %s line %d!\n", __FILE__, __FUNCTION__, __LINE__ ); } #define JT_ASSERT( Condition ) if( ! (Condition) ) { jtQueuePrintAll(); BreakPoint(); } #define JT_ENTER if( verbose ) { printk( KERN_CRIT "Entering %s line %d\n", __FUNCTION__, __LINE__ ); } #define JT_LEAVE if( verbose ) { printk( KERN_CRIT "Leaving %s line %d\n", __FUNCTION__, __LINE__ ); } #define JT_DPRINTK( fmt , arg... ) if( verbose ) { printk( KERN_CRIT fmt, ##arg ); } #else #define BreakPoint() #define JT_ASSERT( Condition ) #define JT_ENTER #define JT_LEAVE #define JT_DPRINTK( fmt , arg... ) #endif /***************************************************************************** * Function Prototypes ****************************************************************************/ // Initialization int init_module(void); void cleanup_module(void); int jt_init(struct device *); STATIC int jtProbe(struct device *); STATIC struct device * jtInitDevInstance( struct device * pDev, UINT8 PciBus, UINT8 PciDeviceFunction ); STATIC JT_STATUS jtDevReset(PDEV_INSTANCE pDevInstance); // Queue management functions INLINE JT_STATUS jtQueueCreate( JT_QUEUE * pQueue, int ElementCount ); INLINE BOOLEAN jtQueueIsEmpty( JT_QUEUE * pQueue ); INLINE BOOLEAN jtQueueIsFull( JT_QUEUE * pQueue ); INLINE void jtQueueEnqueue( JT_QUEUE * pQueue, void * pElement ); INLINE void jtQueueDequeue( JT_QUEUE * pQueue, void ** ppElement ); INLINE void jtQueueDestroy( JT_QUEUE * pQueue ); // Standard device methods STATIC int jtDevOpen(struct device * pDev); STATIC int jtDevStartTransmit(struct sk_buff *skb, struct device * pDev); STATIC void jtDevInterrupt(int irq, void * pDev, struct pt_regs *regs); STATIC int jtDevClose(struct device * pDev); STATIC struct enet_statistics * jtDevGetStatistics(struct device * pDev); STATIC int jtDevIoctl(struct device *pDev, struct ifreq *pIfr, int cmd); STATIC void jtDevSetMulticastList(struct device * pDev); STATIC int jtDevSetMacAddress(struct device * pDev, void * pNewAddr); STATIC int jtDevChangeMtu(struct device *pDev, int NewMtu); // Other Device related functions STATIC void jtAddAddressToMulticastHash( DEV_INSTANCE * pDevInstance, UINT8 * pNetworkAddress ); INLINE void jtDevEnableInterrupts( PDEV_INSTANCE pDevInstance ); INLINE void jtDevDisableInterrupts( PDEV_INSTANCE pDevInstance ); STATIC void jtCsrWriteMacAddress( PDEV_INSTANCE pDevInstance, const UINT8 * pMacAddress); STATIC void jtCsrReadMacAddress( PDEV_INSTANCE pDevInstance, UINT8 * pMacAddress); // Phy functions STATIC JT_STATUS jtPhyFindPhy( PDEV_INSTANCE pDevInstance ); STATIC JT_STATUS jtPhyInitialize( PDEV_INSTANCE pDevInstance ); INLINE void jtPhyDisableInterrupts( PDEV_INSTANCE pDevInstance ); INLINE void jtPhyEnableInterrupts( PDEV_INSTANCE pDevInstance ); STATIC JT_STATUS jtPhyReadRegister( PDEV_INSTANCE pDevInstance, UINT8 PHYAddress, UINT8 PhyRegister, UINT16 * pData ); STATIC JT_STATUS jtPhyWriteRegister( PDEV_INSTANCE pDevInstance, UINT8 PHYAddress, UINT8 PhyRegister, UINT16 Data ); STATIC void jtPhySetSpeed( PDEV_INSTANCE pDevInstance, UINT16 PhySpeed, PUINT16 pPhyControlReg ); STATIC JT_STATUS jtPhyHandleStatusChange( PDEV_INSTANCE pDevInstance ); STATIC JT_STATUS jtPhyGetLinkStatus( PDEV_INSTANCE pDevInstance, JT_PHY_STATUS *pCurrentlyConnected ); STATIC JT_STATUS jtPhyUpdateSpeedDuplex( PDEV_INSTANCE pDevInstance ); STATIC JT_STATUS jtPhyReadStatusRegister( PDEV_INSTANCE pDevInstance, UINT8 PHYAddress, PUINT16 pData ); // RX methods INLINE UINT32 jtRxGetFreeCount( PDEV_INSTANCE pDevInstance ); INLINE void jtRxDecrementFreeCount( PDEV_INSTANCE pDevInstance ); STATIC JT_STATUS jtRxCreateQueues( PDEV_INSTANCE pDevInstance, int ElementCount ); STATIC JT_STATUS jtRxPDCCreate( PDEV_INSTANCE pDevInstance ); STATIC JT_STATUS jtRxPDLCreate( PDEV_INSTANCE pDevInstance ); STATIC JT_STATUS jtRxPDCInitialize( PDEV_INSTANCE pDevInstance ); STATIC JT_STATUS jtRxPDLInitialize( PDEV_INSTANCE pDevInstance ); STATIC JT_STATUS jtRxPDCReplenish( PDEV_INSTANCE pDevInstance ); STATIC JT_STATUS jtRxPDLReplenish( PDEV_INSTANCE pDevInstance ); STATIC void jtRxStart( PDEV_INSTANCE pDevInstance ); INLINE JT_STATUS jtRxValidatePacketLength( PDEV_INSTANCE pDevInstance, UINT32 PacketByteCount ); INLINE JT_STATUS jtRxValidatePacketIntegrity( PDEV_INSTANCE pDevInstance, UINT32 PacketHeader1, UINT32 PacketHeader2 ); INLINE void jtRxHandOffPacket( DEV_INSTANCE * pDevInstance, struct sk_buff * pSkBuff, UINT32 PacketHeader2 ); STATIC JT_STATUS jtRxPDCReceive( PDEV_INSTANCE pDevInstance, UINT32 EventStatus ); STATIC JT_STATUS jtRxPDLReceive( PDEV_INSTANCE pDevInstance, UINT32 EventStatus ); STATIC JT_STATUS jtRxPIOReceive( PDEV_INSTANCE pDevInstance, UINT32 EventStatus ); STATIC void jtRxStop( PDEV_INSTANCE pDevInstance ); STATIC JT_STATUS jtRxPDCFlush( PDEV_INSTANCE pDevInstance ); STATIC JT_STATUS jtRxPDLFlush( PDEV_INSTANCE pDevInstance ); STATIC JT_STATUS jtRxPDCDestroy( PDEV_INSTANCE pDevInstance ); STATIC JT_STATUS jtRxPDLDestroy( PDEV_INSTANCE pDevInstance ); // TX methods INLINE UINT32 jtTxGetFreeCount( PDEV_INSTANCE pDevInstance ); INLINE void jtTxDecrementFreeCount( PDEV_INSTANCE pDevInstance ); STATIC JT_STATUS jtTxPDxCreate( PDEV_INSTANCE pDevInstance) ; STATIC JT_STATUS jtTxPDCCreate( PDEV_INSTANCE pDevInstance) ; STATIC JT_STATUS jtTxPDLCreate( PDEV_INSTANCE pDevInstance) ; STATIC JT_STATUS jtTxPDxInitialize( PDEV_INSTANCE pDevInstance ); STATIC JT_STATUS jtTxPDCInitialize( PDEV_INSTANCE pDevInstance ); STATIC JT_STATUS jtTxPDLInitialize( PDEV_INSTANCE pDevInstance ); STATIC void jtTxStart( PDEV_INSTANCE pDevInstance ); STATIC int jtTxPDxTransmit( PDEV_INSTANCE pDevInstance, struct sk_buff * pSkBuff ); STATIC void jtTxPDCGetNextAvailable( DEV_INSTANCE * pDevInstance, void ** pPDC ); STATIC void jtTxPDCAppendPacket( DEV_INSTANCE * pDevInstance, void * pPDC, UINT32 * pPDCLength, struct sk_buff * pSkBuff ); STATIC void jtTxPDCSend( DEV_INSTANCE * pDevInstance, TX_PDC * pPDC, UINT32 PDCLength ); STATIC void jtTxPDLTransmit( DEV_INSTANCE * pDevInstance, struct sk_buff * pSkBuff ); STATIC int jtTxPIOTransmit( PDEV_INSTANCE pDevInstance, struct sk_buff * pSkBuff ); STATIC JT_STATUS jtTxPDxDone( PDEV_INSTANCE pDevInstance, UINT32 TransmitDoneCount ); STATIC void jtTxStop( PDEV_INSTANCE pDevInstance ); STATIC JT_STATUS jtTxPDxFlush( PDEV_INSTANCE pDevInstance ); STATIC JT_STATUS jtTxPDxDestroy( PDEV_INSTANCE pDevInstance ); // Misc Methods #if 0 STATIC void jtPrintMacAddress( const char * Message, const UINT8 * pAddress ); #endif STATIC void jtSizeToOrder( UINT32 ByteCount, UINT32 * pOrder, UINT32 * pActualByteCount ); STATIC JT_STATUS jtNop( PDEV_INSTANCE pDevInstance ); // Workaround Methods #if defined WORKAROUND_RX_FIFO_OVERFLOW STATIC void jtDevWorkaroundReset( PDEV_INSTANCE pDevInstance ) ; #endif UINT32 crc32buf(char *buf, long len); /**************************************************************************** * Low Level Routines ****************************************************************************/ #if !defined(EMULATION) #define jtFindNextPCIDevice( VendorId, DeviceId, Index, Bus, DevFunction ) \ pcibios_find_device( VendorId, DeviceId, Index, Bus, DevFunction ) #define jtReadPCIConfiguration8( Bus, DevFunction, Offset, pValue ) \ pcibios_read_config_byte( Bus, DevFunction, Offset, pValue ) #define jtReadPCIConfiguration16( Bus, DevFunction, Offset, pValue ) \ pcibios_read_config_word( Bus, DevFunction, Offset, pValue ) #define jtReadPCIConfiguration32( Bus, DevFunction, Offset, pValue ) \ pcibios_read_config_dword( Bus, DevFunction, Offset, pValue ) #define jtWritePCIConfiguration8( Bus, DevFunction, Offset, Value ) \ pcibios_write_config_byte( Bus, DevFunction, Offset, Value ) #define jtWritePCIConfiguration16( Bus, DevFunction, Offset, Value ) \ pcibios_write_config_word( Bus, DevFunction, Offset, Value ) #define jtWritePCIConfiguration32( Bus, DevFunction, Offset, Value ) \ pcibios_write_config_dword( Bus, DevFunction, Offset, Value ) // This is IO mode #if !defined(USE_MEMORY_BASE) || LINUX_VERSION_CODE < 0x20200 #define jtCsrIn8( pDevInstance, PortOffset, pValue ) \ (*pValue = inb( pDevInstance->pDev->base_addr + PortOffset )) #define jtCsrIn16( pDevInstance, PortOffset, pValue ) \ (*pValue = inw( pDevInstance->pDev->base_addr + PortOffset )) #define jtCsrIn32( pDevInstance, PortOffset, pValue ) \ (*pValue = inl( pDevInstance->pDev->base_addr + PortOffset )) #define jtCsrOut8( pDevInstance, PortOffset, Value ) \ (outb( Value, pDevInstance->pDev->base_addr + PortOffset )) #define jtCsrOut16( pDevInstance, PortOffset, Value ) \ (outw( Value, pDevInstance->pDev->base_addr + PortOffset )) #define jtCsrOut32( pDevInstance, PortOffset, Value ) \ (outl( Value, pDevInstance->pDev->base_addr + PortOffset )) // memory mode #else // ideal case #if TRUE //!defined(WORKAROUND_WRITE_COMBINE) #define jtCsrIn8( pDevInstance, PortOffset, pValue ) \ (*pValue = readb( pDevInstance->MapAddr + PortOffset )) #define jtCsrIn16( pDevInstance, PortOffset, pValue ) \ (*pValue = readw( pDevInstance->MapAddr + PortOffset )) #define jtCsrIn32( pDevInstance, PortOffset, pValue ) \ (*pValue = readl( pDevInstance->MapAddr + PortOffset )) #define jtCsrOut8( pDevInstance, PortOffset, Value ) \ (writeb( Value, pDevInstance->MapAddr + PortOffset )) #define jtCsrOut16( pDevInstance, PortOffset, Value ) \ (writew( Value, pDevInstance->MapAddr + PortOffset )) #define jtCsrOut32( pDevInstance, PortOffset, Value ) \ (writel( Value, pDevInstance->MapAddr + PortOffset)) // workaround for write combine #else #define jtCsrIn8( pDevInstance, PortOffset, pValue ) \ {*pValue = readb( pDevInstance->MapAddr + PortOffset ); pDevInstance->LastPortOffset = 0xffff;} #define jtCsrIn16( pDevInstance, PortOffset, pValue ) \ {*pValue = readw( pDevInstance->MapAddr + PortOffset ); pDevInstance->LastPortOffset = 0xffff;} #define jtCsrIn32( pDevInstance, PortOffset, pValue ) \ {*pValue = readl( pDevInstance->MapAddr + PortOffset ); pDevInstance->LastPortOffset = 0xffff;} #define jtCsrOut8( pDevInstance, PortOffset, Value ) \ {if ( (PortOffset&~0x3) != pDevInstance->LastPortOffset && (PortOffset&~0x3) != pDevInstance->LastPortOffset+4) { \ writeb( Value, pDevInstance->MapAddr + PortOffset ); \ pDevInstance->LastPortOffset = PortOffset&~0x3;} \ else { \ outb( Value, pDevInstance->pDev->base_addr + PortOffset ); \ pDevInstance->LastPortOffset = 0xffff; } } #define jtCsrOut16( pDevInstance, PortOffset, Value ) \ {if ( (PortOffset&~0x3) != pDevInstance->LastPortOffset && (PortOffset&~0x3) != pDevInstance->LastPortOffset+4) { \ writew( Value, pDevInstance->MapAddr + PortOffset ); \ pDevInstance->LastPortOffset = PortOffset&~0x3;} \ else { \ outw( Value, pDevInstance->pDev->base_addr + PortOffset ); \ pDevInstance->LastPortOffset = 0xffff; } } #define jtCsrOut32( pDevInstance, PortOffset, Value ) \ {if (PortOffset != pDevInstance->LastPortOffset && PortOffset != pDevInstance->LastPortOffset+4) { \ writel( Value, pDevInstance->MapAddr + PortOffset ); \ pDevInstance->LastPortOffset = PortOffset; } \ else { \ outl( Value, pDevInstance->pDev->base_addr + PortOffset ); \ pDevInstance->LastPortOffset = 0xffff; } } #endif #endif #define jtCsrIn32ToBuffer( pDevInstance, PortOffset, pBuffer, DwordCount ) \ (insl( pDevInstance->pDev->base_addr + PortOffset, pBuffer, DwordCount )) #define jtCsrOut32FromBuffer( pDevInstance, PortOffset, pBuffer, DwordCount ) \ (outsl( pDevInstance->pDev->base_addr + PortOffset, pBuffer, DwordCount )) #else // defined(EMULATION) #include "jt1emu.h" #endif /***************************************************************************** * * Queue management functions * ****************************************************************************/ #if defined(JT_DEBUG) //---------------------------------------------------------------------------- // // FUNCTION: jtQueuePrint // // DESCRIPTION: Debugging only. // Print the fields of a queue. // // PARAMETERS: // pQueue An queue structure to be initialized. // Name Arbitrary string to prefix the printout. // //---------------------------------------------------------------------------- void jtQueuePrint( JT_QUEUE * pQueue, const char * Name ) { printk( KERN_CRIT "%s: Head %4d, Tail %4d, Capacity %4d, Elements %p\n", Name, pQueue->Head, pQueue->Tail, pQueue->Capacity, pQueue->Elements ); } //---------------------------------------------------------------------------- // // FUNCTION: jtQueuePrintAll // // DESCRIPTION: Debugging only. // Print the fields of all of the driver queues for the first // device. // //---------------------------------------------------------------------------- void jtQueuePrintAll(void) { DEV_INSTANCE * pDevInstance = (DEV_INSTANCE*) (jtControllers[0]->priv); unsigned long flags = 0; save_flags(flags); cli(); printk(KERN_CRIT "Dumping all queues:\n"); jtQueuePrint( &pDevInstance->TxPDCAvailableQueue, "Tx PDC Av" ); jtQueuePrint( &pDevInstance->TxPDLAvailableQueue, "Tx PDL Av" ); jtQueuePrint( &pDevInstance->TxPDxInProgressQueue,"Tx PDx Pr" ); jtQueuePrint( &pDevInstance->RxPDxAvailableQueue, "Rx PDx Av" ); jtQueuePrint( &pDevInstance->RxPDxInProgressQueue,"Rx PDx Pr" ); restore_flags(flags); } #endif //---------------------------------------------------------------------------- // // FUNCTION: jtQueueCreate // // DESCRIPTION: Initialize a queue structure, allocating space for its // elements. // // PARAMETERS: // pQueue An queue structure to be initialized. // MaxElementCount The maximum number of elements that can be stored in // the queue at one time. // //---------------------------------------------------------------------------- INLINE JT_STATUS jtQueueCreate( JT_QUEUE * pQueue, int MaxElementCount ) { JT_ASSERT( MaxElementCount > 0 ); JT_ASSERT( pQueue != 0 ); // Initialize fields pQueue->Elements = 0; pQueue->Head = 0; pQueue->Tail = 0; pQueue->Capacity = MaxElementCount+1; // Allocate space for element pointers pQueue->Elements = (void **) kmalloc( sizeof(void *) * pQueue->Capacity, GFP_KERNEL ); if( pQueue->Elements == NULL ) { return JT_STATUS_RESOURCES; } return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtQueueIsEmpty // // DESCRIPTION: Query whether the queue is currently empty or not. // // PARAMETERS: // pQueue The queue to inspect. // // RETURNS: TRUE if the queue is empty. // //---------------------------------------------------------------------------- INLINE BOOLEAN jtQueueIsEmpty( JT_QUEUE * pQueue ) { barrier(); JT_ASSERT( pQueue != 0 ); JT_ASSERT( (pQueue->Head >= 0) && (pQueue->Head < pQueue->Capacity) ); JT_ASSERT( (pQueue->Tail >= 0) && (pQueue->Tail < pQueue->Capacity) ); return (BOOLEAN) (pQueue->Head == pQueue->Tail); } //---------------------------------------------------------------------------- // // FUNCTION: jtQueueIsFull // // DESCRIPTION: Query whether the queue is currently full or not. // // PARAMETERS: // pQueue The queue to inspect. // // RETURNS: TRUE if the queue is full. // //---------------------------------------------------------------------------- INLINE BOOLEAN jtQueueIsFull( JT_QUEUE * pQueue ) { barrier(); JT_ASSERT( pQueue != 0 ); JT_ASSERT( (pQueue->Head >= 0) && (pQueue->Head < pQueue->Capacity) ); JT_ASSERT( (pQueue->Tail >= 0) && (pQueue->Tail < pQueue->Capacity) ); return (BOOLEAN) (pQueue->Head == ( (pQueue->Tail + 1) % pQueue->Capacity ) ); } //---------------------------------------------------------------------------- // // FUNCTION: jtQueueEnqueue // // DESCRIPTION: Add an element to the tail of a queue. // // PARAMETERS: // pQueue The queue to store the element in. // pElement The element to add. // //---------------------------------------------------------------------------- INLINE void jtQueueEnqueue( JT_QUEUE * pQueue, void * pElement ) { barrier(); JT_ASSERT( pQueue != 0 ); JT_ASSERT( ! jtQueueIsFull(pQueue) ); JT_ASSERT( (pQueue->Head >= 0) && (pQueue->Head < pQueue->Capacity) ); JT_ASSERT( (pQueue->Tail >= 0) && (pQueue->Tail < pQueue->Capacity) ); pQueue->Elements[ pQueue->Tail ] = pElement; pQueue->Tail = (pQueue->Tail + 1) % pQueue->Capacity; } //---------------------------------------------------------------------------- // // FUNCTION: jtQueueDequeue // // DESCRIPTION: Remove an element from the head of a queue. // // PARAMETERS: // pQueue The queue to get the element from. // // RETURNS: // ppElement The element removed. // //---------------------------------------------------------------------------- INLINE void jtQueueDequeue( JT_QUEUE * pQueue, void ** ppElement ) { barrier(); JT_ASSERT( pQueue != 0 ); JT_ASSERT( ! jtQueueIsEmpty(pQueue) ); JT_ASSERT( (pQueue->Head >= 0) && (pQueue->Head < pQueue->Capacity) ); JT_ASSERT( (pQueue->Tail >= 0) && (pQueue->Tail < pQueue->Capacity) ); *ppElement = pQueue->Elements[ pQueue->Head ]; pQueue->Head = (pQueue->Head + 1) % pQueue->Capacity; } //---------------------------------------------------------------------------- // // FUNCTION: jtQueueDestroy // // DESCRIPTION: Deallocate space allocated for a queue. // // PARAMETERS: // pQueue An queue structure to be destroyed. // //---------------------------------------------------------------------------- INLINE void jtQueueDestroy( JT_QUEUE * pQueue ) { JT_ASSERT( pQueue != 0 ); kfree( pQueue->Elements ); } /***************************************************************************** * * Inline routines which must come before other code * ****************************************************************************/ //---------------------------------------------------------------------------- // // FUNCTION: jtDevEnableInterrupts // // DESCRIPTION: This function will enable chip interrupts (Master Interrupts) // from the controller. // //---------------------------------------------------------------------------- INLINE void jtDevEnableInterrupts ( PDEV_INSTANCE pDevInstance ) { // Set Interrupt Mask Register: Bit #7 Interrupt Enable Mask jtCsrOut32 ( pDevInstance, INTR_MASK_REG, SERECL0 | INENMS ); } //---------------------------------------------------------------------------- // // FUNCTION: jtDevDisableInterrupts // // DESCRIPTION: This function will disable chip interrupts (Master Interrupts) // from the controller. // //---------------------------------------------------------------------------- INLINE void jtDevDisableInterrupts ( PDEV_INSTANCE pDevInstance ) { // Clear Interrupt Mask Register: Bit #7 Interrupt Enable Mask jtCsrOut32 ( pDevInstance, INTR_MASK_REG, INENMS ); } //---------------------------------------------------------------------------- // // FUNCTION: jtPhyEnableInterrupts // // DESCRIPTION: Enable phy status interrupts from the controller. // //---------------------------------------------------------------------------- INLINE void jtPhyEnableInterrupts ( PDEV_INSTANCE pDevInstance ) { // Set Interrupt Mask Register: Bit #13 Phy Status Interrupt jtCsrOut32 ( pDevInstance, INTR_MASK_REG, SERECL1 | PHLASTMS ); } //---------------------------------------------------------------------------- // // FUNCTION: jtPhyDisableInterrupts // // DESCRIPTION: Disable phy status interrupts from the controller. // //---------------------------------------------------------------------------- INLINE void jtPhyDisableInterrupts ( PDEV_INSTANCE pDevInstance ) { // Clear Interrupt Mask Register: Bit #13 Phy Status Interrupt jtCsrOut32 ( pDevInstance, INTR_MASK_REG, PHLASTMS ); } //---------------------------------------------------------------------------- // // FUNCTION: jtRxGetFreeCount // // DESCRIPTION: Return the number of free receive command queue entries on // the controller. The count of entries is cached in the // device instance, and refreshed as needed from the controller. // Thus this will usually be an undercount of the actual value. // // PARAMETERS: // pDevInstance The pertinent controller instance. // // RETURNS: The count of free entries. At least this many receive // commands may be sent to the controller. // //---------------------------------------------------------------------------- INLINE UINT32 jtRxGetFreeCount( PDEV_INSTANCE pDevInstance ) { // When the cached count reaches zero if( pDevInstance->RxPDxCommandFreeCount == 0 ) { UINT8 CommandFreeCount = 0; // Reread the controller's count of how many more commands it can take jtCsrIn8( pDevInstance, CMD_STATUS_REG+3, &CommandFreeCount ); CommandFreeCount = CommandFreeCount & 0x3f; #if defined WORKAROUND_RX_DMA_DONE // Free commands are decremented if ( CommandFreeCount ) CommandFreeCount = CommandFreeCount - 1; #endif pDevInstance->RxPDxCommandFreeCount = CommandFreeCount; } return pDevInstance->RxPDxCommandFreeCount; } //---------------------------------------------------------------------------- // // FUNCTION: jtRxDecrementFreeCount // // DESCRIPTION: Decrement the cached value of the number of free receive queue // entries. The cache value will be refreshed from the controller // when it reaches zero. // // PARAMETERS: // pDevInstance The pertinent controller instance. // // RETURNS: The count of free entries. At least this many receive // commands may be sent to the controller. // //---------------------------------------------------------------------------- INLINE void jtRxDecrementFreeCount( PDEV_INSTANCE pDevInstance ) { pDevInstance->RxPDxCommandFreeCount--; // Refresh the count if zero (void) jtRxGetFreeCount( pDevInstance ); } //---------------------------------------------------------------------------- // // FUNCTION: jtTxGetFreeCount // // DESCRIPTION: Return the number of free transmit command queue entries on // the controller. The count of entries is cached in the // device instance, and refreshed as needed from the controller. // Thus this will usually be an undercount of the actual value. // // PARAMETERS: // pDevInstance The pertinent controller instance. // // RETURNS: The count of free entries. At least this many transmit // commands may be sent to the controller. // //---------------------------------------------------------------------------- INLINE UINT32 jtTxGetFreeCount( PDEV_INSTANCE pDevInstance ) { // When the cached count reaches zero if( pDevInstance->TxPDxCommandFreeCount == 0 ) { UINT8 CommandFreeCount = 0; // Reread the controller's count of how many more commands it can take jtCsrIn8( pDevInstance, CMD_STATUS_REG+2, &CommandFreeCount ); CommandFreeCount = CommandFreeCount & 0x3f; #if defined WORKAROUND_TX_DMA_DONE // Decrement the value just read if( CommandFreeCount ) CommandFreeCount = CommandFreeCount-1; #endif pDevInstance->TxPDxCommandFreeCount = CommandFreeCount; // If controller is still full if( !CommandFreeCount ) { // Signal that the transmitter is now busy set_bit( 0, (void *) &pDevInstance->TxFull ); JT_DPRINTK ("Out of commands\n"); //printk ( KERN_CRIT "Out of commands\n"); } } return pDevInstance->TxPDxCommandFreeCount; } //---------------------------------------------------------------------------- // // FUNCTION: jtTxDecrementFreeCount // // DESCRIPTION: Decrement the cached value of the number of free transmit queue // entries. The cache value will be refreshed from the controller // when it reaches zero. // // PARAMETERS: // pDevInstance The pertinent controller instance. // // RETURNS: The count of free entries. At least this many transmit // commands may be sent to the controller. // //---------------------------------------------------------------------------- INLINE void jtTxDecrementFreeCount( PDEV_INSTANCE pDevInstance ) { pDevInstance->TxPDxCommandFreeCount--; // Refresh the count if zero (void) jtTxGetFreeCount( pDevInstance ); } /***************************************************************************** * * Initialization/Cleanup * ****************************************************************************/ #ifdef MODULE //---------------------------------------------------------------------------- // // FUNCTION: init_module // // DESCRIPTION: This function called when the module is loaded by insmod. // Find and initialize installed NetCelerator controllers. // // RETURNS: 0 if any NetCelerator controllers were found, or // -ENODEV is no controllers were found. // //-------------------------------------------------------------------------- int init_module(void) { // Export no symbols #if !defined(JT_DEBUG) EXPORT_NO_SYMBOLS; #endif // Scan for controllers jtControllerCount = 0; jtProbe(NULL); // If found any controllers if (jtControllerCount > 0) { return 0; } // Otherwise return error code return -ENODEV; } //---------------------------------------------------------------------------- // // FUNCTION: cleanup_module // // DESCRIPTION: This function is called when the module is unloaded by rmmod. // Shutdown all NetCelerator controllers and release all resources. // //---------------------------------------------------------------------------- void cleanup_module(void) { int Index; // Iterate over each installed controller for( Index=0; Indexpriv; // Release IO region release_region( pDev->base_addr, pDevInstance->IORange ); // Unlink device from devlist unregister_netdev(pDev); #if defined(USE_MEMORY_BASE) && LINUX_VERSION_CODE > 0x20200 iounmap((PVOID)pDevInstance->MapAddr); #endif // Release memory for private data kfree(pDevInstance); } #if defined(EMULATION) if( jtEmuHandle != NULL ) { emuDestroyInstance(jtEmuHandle); } #endif } #else // !defined(MODULE) //---------------------------------------------------------------------------- // // FUNCTION: jt_init // // DESCRIPTION: Scan the PCI bus for NetCelerator controllers. // // PARAMETERS: // pFirstDev The device structure to use for the first controller. // // RETURNS: 0 if any NetCelerator controllers were found, or // -ENODEV is no controllers were found. // //---------------------------------------------------------------------------- int jt_init(struct device * pFirstDev) { // Scan for controllers jtControllerCount = 0; jtProbe(pFirstDev); // If found any controllers if (jtControllerCount > 0) { return 0; } // Otherwise return error code return -ENODEV; } #endif // !defined(MODULE) //---------------------------------------------------------------------------- // // FUNCTION: jtProbe // // DESCRIPTION: Scan the PCI bus for all NetCelerator controllers. // // PARAMETERS: // pFirstDev This is the device structure to use for the first // controller detected when the driver is part of the kernel. // When the driver is a module, this is always NULL. // RETURNS: The number of controller detected. // //---------------------------------------------------------------------------- STATIC int jtProbe(struct device * pFirstDev) { #if defined(JT_DEBUG) // Should we sleep? #if (LINUX_VERSION_CODE < 0x20100) if( sleep_init ) { // Sleep for 'sleep_init' seconds unsigned long j = jiffies + sleep_init*HZ; current->timeout = j; current->state = TASK_INTERRUPTIBLE; schedule(); current->timeout = 0; } #endif // Should we stop? if( break_init ) { BreakPoint(); } #endif #if defined(EMULATION) if( emuCreateInstance(NULL, NULL, &jtEmuHandle) != JT_STATUS_SUCCESS ) { printk(KERN_ERR __FILE__ ": Couldn't initialize emulator!\n"); return -ENODEV; } #endif #if defined(CONFIG_PCI) if (pcibios_present()) { int Index = 0, R3Adapters = JT_MAX_CONTROLLER_COUNT; UINT8 PciBus = 0; UINT8 PciDeviceFunction = 0; // Iterate over all possible NetCelerator controllers (new ID) for (Index = 0; Index < JT_MAX_CONTROLLER_COUNT; ++Index) { struct device * pDev; int Result; // Look for the Index-th controller Result = jtFindNextPCIDevice( PCI_VENDOR_ID_LEVELONE3, PCI_DEVICE_ID_JT1001, Index, &PciBus, &PciDeviceFunction ); // If no more controllers are present if (Result == PCIBIOS_DEVICE_NOT_FOUND) { R3Adapters = Index; break; } // Or if an error occurred else if (Result != PCIBIOS_SUCCESSFUL) { printk( KERN_ERR __FILE__ ": Pci error probing device" #if (LINUX_VERSION_CODE < 0x20100) ": %s\n", pcibios_strerror(Result) #endif ); break; } // If this is the first device detected pDev = NULL; if (Index == 0) { pDev = pFirstDev; } // Initialize device structure pDev = jtInitDevInstance(pDev, PciBus, PciDeviceFunction); // Increment count of controllers found jtControllers[jtControllerCount] = pDev; jtControllerCount++; } // Iterate over all possible NetCelerator controllers (old ID) for (Index = R3Adapters; Index < JT_MAX_CONTROLLER_COUNT; ++Index) { struct device * pDev; int Result; // Look for the Index-th controller Result = jtFindNextPCIDevice( PCI_VENDOR_ID_LEVELONE2, PCI_DEVICE_ID_JT1001, Index-R3Adapters, &PciBus, &PciDeviceFunction ); // If no more controllers are present if (Result == PCIBIOS_DEVICE_NOT_FOUND) { break; } // Or if an error occurred else if (Result != PCIBIOS_SUCCESSFUL) { printk( KERN_ERR __FILE__ ": Pci error probing device" #if (LINUX_VERSION_CODE < 0x20100) ": %s\n", pcibios_strerror(Result) #endif ); break; } // If this is the first device detected pDev = NULL; if (Index == 0) { pDev = pFirstDev; } // Initialize device structure pDev = jtInitDevInstance(pDev, PciBus, PciDeviceFunction); // Increment count of controllers found jtControllers[jtControllerCount] = pDev; jtControllerCount++; } } // if pcibios_present #endif // defined(CONFIG_PCI) return jtControllerCount; } //---------------------------------------------------------------------------- // // FUNCTION: jtInitDevInstance // // DESCRIPTION: Initialize a device object for a newly detected controller. // // PARAMETERS: // pDev A preallocated device structure, or NULL. // PciBus The pci bus that the controller is attached to. // PciDeviceFunction The pci device and function number of the controller. // // RETURNS: The allocated device object for the controller. // //---------------------------------------------------------------------------- STATIC struct device * jtInitDevInstance( struct device * pDev, UINT8 PciBus, UINT8 PciDeviceFunction ) { DEV_INSTANCE * pDevInstance = NULL; UINT32 IOBase = 0; UINT8 Irq = 0; UINT32 MemoryBaseDword = 0; UINT64 MemoryBase = 0; UINT8 CacheLineSize = 0; UINT16 PciCommand = 0; int Result = JT_STATUS_SUCCESS; #if defined(WORKAROUND_LATENCY) UINT8 PciLatency; #endif #if defined(EMULATION) Result = emuConfirmResources( jtEmuHandle ); if (Result != 0) { printk( KERN_ERR "%s: emuConfirmResources failed!\n", pDev->name ); return NULL; } #endif // Find/Allocate device structure. // If pDev is not NULL, it will use the given // pDev device structure. // If pDev is NULL, it will look for an existing, // correctly named device in the device chain. // If this fails, it will allocate a new device structure // and insert it into the device chain. // // The name of the device will be set to the next available ethN name. pDev = init_etherdev(pDev, 0); // Allocate controller private data pDev->priv = kmalloc(sizeof(DEV_INSTANCE), GFP_KERNEL); if (pDev->priv == NULL) { return NULL; } memset(pDev->priv, 0, sizeof(DEV_INSTANCE)); pDevInstance = (DEV_INSTANCE *) pDev->priv; pDevInstance->pDev = pDev; // Setup controller hardware parameters #if defined(EMULATION) pDevInstance->IORange = 128; #else pDevInstance->IORange = 256; #endif pDevInstance->ROMBaseLow = 0; pDevInstance->ROMBaseHigh = 0; pDevInstance->ROMRange = 0; pDevInstance->PciBus = PciBus; pDevInstance->PciDeviceFunction = PciDeviceFunction; // Setup Phy parameters to default pDevInstance->OverrideSpeed = LINK_SPEED_AUTO; pDevInstance->OverrideDuplexMode = DUPLEX_MODE_AUTO; pDevInstance->Speed = 0; pDevInstance->DuplexMode = 0; // Setup Tx parameters to default pDevInstance->TxIOMode = TX_IOMODE_BUSMASTER; pDevInstance->TxFlowControl = TRUE; pDevInstance->TxPDxThreshold = 800; pDevInstance->TxPDCTableCount = TX_PDC_TABLE_COUNT; pDevInstance->TxPDCCount = TX_PDC_TABLE_COUNT; pDevInstance->TxPDLCount = TX_PDC_TABLE_COUNT; pDevInstance->TxPDLAlignment = PDL_MIN_ALIGNMENT; // Setup Rx parameters to default pDevInstance->RxIOMode = RX_IOMODE_PDC; pDevInstance->RxFlowControl = TRUE; pDevInstance->RxPDCTableCount = RX_PDC_TABLE_COUNT; pDevInstance->RxPDCCount = RX_PDC_TABLE_COUNT; pDevInstance->RxPDLCount = RX_PDC_TABLE_COUNT; pDevInstance->RxPDLAlignment = PDL_MIN_ALIGNMENT; // Setup misc pDevInstance->MagicPacketMode = MAGIC_PACKET_DISABLED; // pDevInstance->Task.routine = &jtDevInterruptBH; // pDevInstance->Task.data = pDev; // Now read any user configured parameters // Maximum Tranfer Unit if( mtu[jtControllerCount] > 0 ) { jtDevChangeMtu( pDev, mtu[jtControllerCount] ); } // PIO or PDL/PDC Transmit mode switch( tx_mode[jtControllerCount] ) { case TX_IOMODE_PIO: case TX_IOMODE_BUSMASTER: pDevInstance->TxIOMode = tx_mode[jtControllerCount]; break; } // CRC software check #if defined(WORKAROUND_CRC_CHECK) pDevInstance->RxCrcCheck = rx_crc[jtControllerCount]; #endif // PDC/PDL ring size if( (tx_ringsize[jtControllerCount] > 0) && (tx_ringsize[jtControllerCount] <= pDevInstance->TxPDCTableCount) ) { pDevInstance->TxPDCCount = tx_ringsize[jtControllerCount]; pDevInstance->TxPDLCount = tx_ringsize[jtControllerCount]; } // PDC/PDL Threshold if( tx_threshold[jtControllerCount] >= 0 ) { pDevInstance->TxPDxThreshold = tx_threshold[jtControllerCount]; } // Whether to send PAUSE frames switch( tx_flowcontrol[jtControllerCount] ) { case 0: case 1: pDevInstance->TxFlowControl = tx_flowcontrol[jtControllerCount]; } // PIO, PDL or PDC Receive mode switch( rx_mode[jtControllerCount] ) { case RX_IOMODE_PIO: case RX_IOMODE_PDC: case RX_IOMODE_PDL: pDevInstance->RxIOMode = rx_mode[jtControllerCount]; break; } // PDC/PDL ring size if( (rx_ringsize[jtControllerCount] > 0) && (rx_ringsize[jtControllerCount] <= pDevInstance->RxPDCTableCount) ) { pDevInstance->RxPDCCount = rx_ringsize[jtControllerCount]; pDevInstance->RxPDLCount = rx_ringsize[jtControllerCount]; } // Whether to honor PAUSE frames send by link partner switch( rx_flowcontrol[jtControllerCount] ) { case 0: case 1: pDevInstance->RxFlowControl = rx_flowcontrol[jtControllerCount]; } // Link speed switch( speed[jtControllerCount] ) { case 0: pDevInstance->OverrideSpeed = LINK_SPEED_AUTO; break; case 10: pDevInstance->OverrideSpeed = LINK_SPEED_10; break; case 100: pDevInstance->OverrideSpeed = LINK_SPEED_100; break; case 1000: pDevInstance->OverrideSpeed = LINK_SPEED_1000; break; } // Link duplex mode if( duplex[jtControllerCount] != NULL ) { if( strcmp("half", duplex[jtControllerCount]) == 0 ) { pDevInstance->OverrideDuplexMode = DUPLEX_MODE_HALF; } else if ( strcmp("full", duplex[jtControllerCount]) == 0 ) { pDevInstance->OverrideDuplexMode = DUPLEX_MODE_FULL; } } #if defined(__SMP__) && defined(PERFORMANCE_TX) spin_lock_init(&pDevInstance->DriverLock); #endif // Read the pci interrupt used by the controller (void) jtReadPCIConfiguration8( PciBus, PciDeviceFunction, PCI_INTERRUPT_LINE, &Irq); pDev->irq = Irq; //Read the PCI IO base address and mask off the flag bits (void) jtReadPCIConfiguration32( PciBus, PciDeviceFunction, PCI_BASE_ADDRESS_0, &IOBase); pDev->base_addr = IOBase & ~0x03; (void) jtReadPCIConfiguration32(PciBus, PciDeviceFunction, PCI_BASE_ADDRESS_1, &MemoryBaseDword); // We do a request_region() only to register /proc/ioports info. request_region(pDev->base_addr, pDevInstance->IORange, pDev->name); // Read the 64 bit PCI memory base address one dword at a time, // mash the dwords together, and mask off the flag bits (void) jtReadPCIConfiguration32( PciBus, PciDeviceFunction, PCI_BASE_ADDRESS_1, &MemoryBaseDword); MemoryBase = MemoryBaseDword & ~0x07; (void) jtReadPCIConfiguration32( PciBus, PciDeviceFunction, PCI_BASE_ADDRESS_2, &MemoryBaseDword); MemoryBase |= ((UINT64) (MemoryBaseDword & ~0x07)) << 32; pDev->mem_start = MemoryBase; pDev->mem_end = pDev->mem_start + pDevInstance->IORange; #if defined(USE_MEMORY_BASE) && LINUX_VERSION_CODE > 0x20200 pDevInstance->MapAddr = (UINT32)ioremap(MemoryBase & ~0xf, pDevInstance->IORange); #endif // Get the Cache Line Size parameter (void) jtReadPCIConfiguration8 ( PciBus, PciDeviceFunction, PCI_CACHE_LINE_SIZE, &CacheLineSize ); if ( CacheLineSize == 0 ) { // Set the default to 4 DWORDS pDevInstance->CacheLineSize = 32; // printk(KERN_INFO "Using default cache line size\n"); } else { // Cache Line Size value from the PCI Configuration space is in DWORDS pDevInstance->CacheLineSize = CacheLineSize * 4; // Enable Memory Write and Invalidate for the controller (void) jtReadPCIConfiguration16 ( PciBus, PciDeviceFunction, PCI_COMMAND, &PciCommand ); if ((PciCommand & PCI_COMMAND_INVALIDATE) != PCI_COMMAND_INVALIDATE) { PciCommand |= PCI_COMMAND_INVALIDATE; (void) jtWritePCIConfiguration16 ( PciBus, PciDeviceFunction, PCI_COMMAND, PciCommand ); } } // Set the PDL alignment to cache line length if( pDevInstance->CacheLineSize > PDL_MIN_ALIGNMENT ) { pDevInstance->TxPDLAlignment = pDevInstance->CacheLineSize; pDevInstance->RxPDLAlignment = pDevInstance->CacheLineSize; } // Read the permanent MAC address jtCsrReadMacAddress(pDevInstance, pDevInstance->NodeAddress); memcpy(pDev->dev_addr, (const UINT8 *) (pDevInstance->NodeAddress), ETH_ALEN); // Reset and initialize the device Result = jtDevReset ( pDevInstance ); if ( Result != 0 ) { printk(KERN_WARNING "%s: Error resetting controller.\n", pDev->name); } // Read the ROM Address #if !defined(EMULATION) (void) jtReadPCIConfiguration32( PciBus, PciDeviceFunction, PCI_ROM_ADDRESS, &pDevInstance->ROMBaseLow ); pDevInstance->ROMRange = 0x0100000; #endif // Install device methods pDev->open = &jtDevOpen; pDev->hard_start_xmit = &jtDevStartTransmit; pDev->stop = &jtDevClose; pDev->get_stats = &jtDevGetStatistics; pDev->do_ioctl = &jtDevIoctl; pDev->set_multicast_list = &jtDevSetMulticastList; pDev->set_mac_address = &jtDevSetMacAddress; pDev->change_mtu = &jtDevChangeMtu; switch( pDevInstance->RxIOMode ) { case RX_IOMODE_PDC: pDevInstance->pRxCreate = &jtRxPDCCreate; pDevInstance->pRxInitialize= &jtRxPDCInitialize; pDevInstance->pRxReplenish = &jtRxPDCReplenish; pDevInstance->pRxReceive = &jtRxPDCReceive; pDevInstance->pRxFlush = &jtRxPDCFlush; pDevInstance->pRxDestroy = &jtRxPDCDestroy; break; case RX_IOMODE_PDL: pDevInstance->pRxCreate = &jtRxPDLCreate; pDevInstance->pRxInitialize= &jtRxPDLInitialize; pDevInstance->pRxReplenish = &jtRxPDLReplenish; pDevInstance->pRxReceive = &jtRxPDLReceive; pDevInstance->pRxFlush = &jtRxPDLFlush; pDevInstance->pRxDestroy = &jtRxPDLDestroy; break; case RX_IOMODE_PIO: pDevInstance->pRxCreate = &jtNop; pDevInstance->pRxInitialize= &jtNop; pDevInstance->pRxReplenish = &jtNop; pDevInstance->pRxReceive = &jtRxPIOReceive; pDevInstance->pRxFlush = &jtNop; pDevInstance->pRxDestroy = &jtNop; break; } switch( pDevInstance->TxIOMode ) { case TX_IOMODE_BUSMASTER: pDevInstance->pTxCreate = &jtTxPDxCreate; pDevInstance->pTxInitialize= &jtTxPDxInitialize; pDevInstance->pTxTransmit = &jtTxPDxTransmit; pDevInstance->pTxFlush = &jtTxPDxFlush; pDevInstance->pTxDestroy = &jtTxPDxDestroy; break; case TX_IOMODE_PIO: pDevInstance->pTxCreate = &jtNop; pDevInstance->pTxInitialize= &jtNop; pDevInstance->pTxTransmit = &jtTxPIOTransmit; pDevInstance->pTxFlush = &jtNop; pDevInstance->pTxDestroy = &jtNop; break; } // Register the device register_netdev(pDev); // Print message announcing controller detection #if !defined JT_DEBUG // Low priority if not in debug printk(KERN_INFO "%s", version); printk( KERN_INFO "%s: %s at IO %#3x, Mem %#lx, IRQ %d.\n", pDev->name, jtProductName, (int) pDev->base_addr, (long) pDev->mem_start, (int) pDev->irq ); printk( KERN_INFO "%s: Tx %s Mode, Rx %s Mode, Tx Threshold %d bytes.\n", pDev->name, pDevInstance->TxIOMode == TX_IOMODE_PIO ? "PIO" : "PDx", pDevInstance->RxIOMode == RX_IOMODE_PIO ? "PIO" : pDevInstance->RxIOMode == RX_IOMODE_PDL ? "PDL" : "PDC", pDevInstance->TxPDxThreshold ); #else JT_DPRINTK("%s", version); JT_DPRINTK( "%s: %s at IO %#3x, Mem %#lx, IRQ %d.\n", pDev->name, jtProductName, (int) pDev->base_addr, (long) pDev->mem_start, (int) pDev->irq ); JT_DPRINTK( "%s: Tx %s Mode, Rx %s Mode, Tx Threshold %d bytes.\n", pDev->name, pDevInstance->TxIOMode == TX_IOMODE_PIO ? "PIO" : "PDx", pDevInstance->RxIOMode == RX_IOMODE_PIO ? "PIO" : pDevInstance->RxIOMode == RX_IOMODE_PDL ? "PDL" : "PDC", pDevInstance->TxPDxThreshold ); if( (pDevInstance->TxIOMode == TX_IOMODE_BUSMASTER) || (pDevInstance->RxIOMode == RX_IOMODE_PDL) ) { JT_DPRINTK( "%s: Cache Line size %d bytes.\n", pDev->name, (int) pDevInstance->CacheLineSize ); } #endif #if defined(WORKAROUND_LATENCY) jtReadPCIConfiguration8( PciBus, PciDeviceFunction, PCI_LATENCY_TIMER, &PciLatency ); if (PciLatency < 0x40) { #if defined(JT_DEBUG) JT_DPRINTK( "%s: PCI latency timer (CFLT) is %d. Setting to %d clocks.\n", pDev->name, PciLatency, 0x40 ); #else printk( KERN_INFO "%s: PCI latency timer (CFLT) is %d. Setting to %d clocks.\n", pDev->name, PciLatency, 0x40 ); #endif jtWritePCIConfiguration8( PciBus, PciDeviceFunction, PCI_LATENCY_TIMER, 0x40 ); } else #if defined(JT_DEBUG) JT_DPRINTK( "%s: PCI latency timer (CFLT) is %#x.\n", pDev->name, PciLatency ); #else printk( KERN_INFO "%s: PCI latency timer (CFLT) is %#x.\n", pDev->name, PciLatency ); #endif #endif /*jtWritePCIConfiguration8( PciBus, PciDeviceFunction, PCI_LATENCY_TIMER, 0x20 );*/ return pDev; } //---------------------------------------------------------------------------- // // FUNCTION: jtDevReset // // DESCRIPTION: Do a software reset of the controller, and update all // local bookkeeping appropriately. // //---------------------------------------------------------------------------- static const int RESET_MAX_LOOP_COUNT = 999; JT_STATUS jtDevReset ( PDEV_INSTANCE pDevInstance ) { UINT32 ModeRegister1; UINT16 i; // Reset our local copy of the Multicast Hast Table pDevInstance->MulticastHashLow = 0; pDevInstance->MulticastHashHigh = 0; // Set Set/Reset(bit #0) Control bit for Bits(7:1) and Soft Reset (bit #1) // in ModeRegister-1 jtCsrOut32 ( pDevInstance, MODE_REG_1, SERECL0 | SWRE ); // Delay feature is needed on reset // Wait for 20 millisecond for reset to complete udelay ( 20*1000 ); // Poll device, waiting for for ( i = 0; i < RESET_MAX_LOOP_COUNT; i++ ) { // Read Bit #1 to check reset complete jtCsrIn32 ( pDevInstance, MODE_REG_1, &ModeRegister1 ); if ( ( ModeRegister1 & SWRE ) == 0 ) { return JT_STATUS_SUCCESS; } // Delay to give the device a chance to do work udelay ( 1*1000 ); // One millisecond } return ( JT_STATUS_FAILURE ); } /***************************************************************************** * * Standard Device Methods * ****************************************************************************/ //---------------------------------------------------------------------------- // // FUNCTION: jtDevOpen // // DESCRIPTION: Standard device method to bring the controller to a fully // operational state. // //---------------------------------------------------------------------------- STATIC int jtDevOpen(struct device * pDev) { DEV_INSTANCE * pDevInstance = (DEV_INSTANCE *) pDev->priv; JT_STATUS Result; int RetCode = 0; // Request shared access to the controller's interrupt. RetCode = request_irq( pDev->irq, &jtDevInterrupt, SA_SHIRQ, // Slow, shared interrupt pDev->name, pDev ); if( RetCode != 0 ) { printk( KERN_ERR "%s: Unable to acquire interrupt %d!\n", pDev->name, pDev->irq ); return -EAGAIN; } // Setup various options - Rx Flow Control, Tx CRC generation, // Tx Min Padding, Phy Status Polling, most are defaults but just in case... jtCsrOut32 ( pDevInstance, MODE_REG_1, (SERECL0 | TXPPEN | GMSTPOEN) | (SERECL1 | UCEN) | (SERECL2 | RXFLCTEN | TXCREN) ); #if defined (WORKAROUND_PACKET_PADDING) // Disable Remove Packet Padding jtCsrOut32( pDevInstance, MODE_REG_1, RMPPEN ); #endif // Don't receive errored packets jtCsrOut32( pDevInstance, MODE_REG_1, PAERPKEN ); // If the Maximum Packet Size is greater than 1500, enable large packet support if ( pDev->mtu > ETH_DATA_LEN ) { jtCsrOut32 ( pDevInstance, MODE_REG_1, SERECL1 | LGPKEN ); } else { jtCsrOut32 ( pDevInstance, MODE_REG_1, LGPKEN ); } // Enable/Disable Transmit flow control #if defined(WORKAROUND_FLOW_CONTROL) jtCsrOut32( pDevInstance, MODE_REG_1, TXFLCTEN ); jtCsrOut32( pDevInstance, MODE_REG_1, RXFLCTEN ); #else if ( pDevInstance->TxFlowControl ) { jtCsrOut32 ( pDevInstance, MODE_REG_1, SERECL0 | TXFLCTEN ); } else { jtCsrOut32 ( pDevInstance, MODE_REG_1, TXFLCTEN ); } // Enable/Disable Receive flow control if ( pDevInstance->RxFlowControl ) { jtCsrOut32 ( pDevInstance, MODE_REG_1, SERECL2 | RXFLCTEN ); } else { jtCsrOut32 ( pDevInstance, MODE_REG_1, RXFLCTEN ); } #endif // Workaround for CRC check #if defined(WORKAROUND_CRC_CHECK) if (pDevInstance->RxCrcCheck) jtCsrOut32 ( pDevInstance, MODE_REG_1, SERECL2 | PACREN ); #endif // Disable Magic Packet Mode jtCsrOut32( pDevInstance, MODE_REG_1, MGPKEN ); // Make sure all VLAN options are disabled jtCsrOut32 ( pDevInstance, MODE_REG_1, (VLEN | VLTBEN | VLRMID | VLISGB) ); // Turn on receive acceleration jtCsrOut8 ( pDevInstance, (MODE_REG_2 + 1), (SERECL1 | (RXIPCKEN | RXTPCKEN | RXUPCKEN)) >> 8 ); #if defined WORKAROUND_TX_ACCELERATION // Turn off transmit acceleration since first part does not support TX acceleration jtCsrOut8 ( pDevInstance, (MODE_REG_2 + 1), (TXIPCKEN | TXTPCKEN | TXUPCKEN) >> 8 ); #endif // Set the MAC address jtCsrWriteMacAddress(pDevInstance, pDev->dev_addr); // Read the size of the Tx fifo jtCsrIn16( pDevInstance, TX_FIFO_DWORDS_FREE_REG, &(pDevInstance->TxFifoDwordFreeCount) ); // Decide the default options for packet transmit pDevInstance->TxPacketHeader = 0; #if defined WORKAROUND_RX_FIFO_OVERFLOW // Use the receive watermark to detect the potential overflow condition jtCsrOut16( pDevInstance, RX_FIFO_WTR_MRK_REG, // workaround for R3 0x3fff // workaround for R2 //RX_FIFO_SIZE - ( pDev -> mtu + ETH_HLEN + 3 + 4 )/4 - 1 ); jtCsrOut32( pDevInstance, INTR_MASK_REG, SERECL1 | RXFIWMMS ); #endif // Look for the attached PHY Result = jtPhyFindPhy ( pDevInstance ); if( Result != JT_STATUS_SUCCESS ) { printk( KERN_ERR "%s: Error: Did not detect PHY layer.\n", pDev->name ); return -EAGAIN; } // Initialize the PHY - link type, speed, duplex Result = jtPhyInitialize ( pDevInstance ); if( Result != JT_STATUS_SUCCESS ) { printk( KERN_ERR "%s: Error: Unable to initialize PHY layer.\n", pDev->name ); return -EAGAIN; } // Create the Receive Instance data Result = (*pDevInstance->pRxCreate)( pDevInstance ); if( Result != JT_STATUS_SUCCESS ) { printk( KERN_ERR "%s: Out of memory: Unable to create receive buffers.\n", pDev->name ); // Clean up (*pDevInstance->pRxDestroy)( pDevInstance ); return -EAGAIN; } // Create the Transmit Instance data Result = (*pDevInstance->pTxCreate)( pDevInstance ); if( Result != JT_STATUS_SUCCESS ) { printk( KERN_ERR "%s: Out of memory: Unable to create transmit buffers.\n", pDev->name ); // Clean up (void) (*pDevInstance->pRxDestroy)( pDevInstance ); (void) (*pDevInstance->pTxDestroy)( pDevInstance ); return -EAGAIN; } // Initialize receive and transmit buffers (void) (*pDevInstance->pRxInitialize)( pDevInstance ); (void) (*pDevInstance->pTxInitialize)( pDevInstance ); // Start the Transmitter and Receiver jtTxStart( pDevInstance ); jtRxStart( pDevInstance ); // Read the counts of PDx commands available (void) jtRxGetFreeCount( pDevInstance ); (void) jtTxGetFreeCount( pDevInstance ); // Enable Master Interrupts jtDevEnableInterrupts( pDevInstance ); // Flag device as open pDev->start = 1; pDev->tbusy = 0; pDevInstance->TxFull = 0; // Everything worked MOD_INC_USE_COUNT; return 0; } //---------------------------------------------------------------------------- // // FUNCTION: jtDevStartTransmit // // DESCRIPTION: Standard device method to transmit a single packet. // pSkBuff The packet to transmit. // pDev The controller to transmit from. // //---------------------------------------------------------------------------- STATIC int jtDevStartTransmit(struct sk_buff * pSkBuff, struct device * pDev) { DEV_INSTANCE * pDevInstance = (DEV_INSTANCE *) pDev->priv; int Result; // Validate arguments #if defined(JT_DEBUG) if ( (pSkBuff == NULL) || (pSkBuff->len <= 0) ) { printk( KERN_WARNING "%s: Obsolete driver layer request made: skbuff==NULL.\n", pDev->name ); // Notify the higher layers to try again return 0; } #endif // Block multiple transmits from overlapping. if ( test_and_set_bit(0, (void *) &pDev->tbusy ) ) { //JT_DPRINTK("Busy, packet rejected\n"); printk( KERN_CRIT "Busy, packet rejected\n"); jtCsrOut32( pDevInstance, CSR57, 0 ); return -EBUSY; } // Choose the proper transmit mechanism Result = (*pDevInstance->pTxTransmit)( pDevInstance, pSkBuff ); // Is the controller still busy? if( !test_bit(0, (void*) &pDevInstance->TxFull) ) clear_bit(0, (void*) &pDev->tbusy); #if defined(JT_DEBUG) else { printk(KERN_CRIT "Transmitter still busy!\n"); printk(KERN_CRIT "Tx PDC Available = %d, PDL Available %d, In Progress = %d\n", (pDevInstance->TxPDCAvailableQueue.Tail - pDevInstance->TxPDCAvailableQueue.Head + pDevInstance->TxPDCAvailableQueue.Capacity)%pDevInstance->TxPDCAvailableQueue.Capacity, (pDevInstance->TxPDLAvailableQueue.Tail - pDevInstance->TxPDLAvailableQueue.Head + pDevInstance->TxPDLAvailableQueue.Capacity)%pDevInstance->TxPDLAvailableQueue.Capacity, (pDevInstance->TxPDxInProgressQueue.Tail - pDevInstance->TxPDxInProgressQueue.Head + pDevInstance->TxPDxInProgressQueue.Capacity)%pDevInstance->TxPDxInProgressQueue.Capacity ); } #endif // Mark beginning of transmit pDev->trans_start = jiffies; return Result; } #if defined WORKAROUND_RX_FIFO_OVERFLOW //---------------------------------------------------------------------------- // // FUNCTION: jtDevWorkaroundReset // // DESCRIPTION: The interrupt service routine for the controller. // Irq The number of the interrupt raised. // pDevHandle The controller which raised the interrupt. // pReg Saved state of the cpu. // //---------------------------------------------------------------------------- STATIC void jtDevWorkaroundReset( PDEV_INSTANCE pDevInstance ) { struct device * pDev = pDevInstance->pDev; UINT16 PhyStatus; // Disable Transmitter and Receiver jtTxStop( pDevInstance ); jtRxStop( pDevInstance ); //temporary udelay(1000); // Flush receive and transmit buffers (void) (*pDevInstance->pRxFlush)( pDevInstance ); (void) (*pDevInstance->pTxFlush)( pDevInstance ); // Read statistics before we reset the device (void) jtDevGetStatistics( pDev ); // Reset the device jtDevReset ( pDevInstance ); // Setup various options - Rx Flow Control, Tx CRC generation, // Tx Min Padding, Phy Status Polling, most are defaults but just in case... jtCsrOut32 ( pDevInstance, MODE_REG_1, (SERECL0 | TXPPEN | GMSTPOEN) | (SERECL1 | UCEN) | (SERECL2 | RXFLCTEN | TXCREN) ); #if defined (WORKAROUND_PACKET_PADDING) // Disable Remove Packet Padding jtCsrOut32( pDevInstance, MODE_REG_1, RMPPEN ); #endif // Don't receive errored packets jtCsrOut32( pDevInstance, MODE_REG_1, PAERPKEN ); // If the Maximum Packet Size is greater than 1500, enable large packet support if ( pDev->mtu > ETH_DATA_LEN ) { jtCsrOut32 ( pDevInstance, MODE_REG_1, SERECL1 | LGPKEN ); } else { jtCsrOut32 ( pDevInstance, MODE_REG_1, LGPKEN ); } // Enable/Disable Transmit/Receive flow control #if defined(WORKAROUND_FLOW_CONTROL) jtCsrOut32( pDevInstance, MODE_REG_1, TXFLCTEN ); jtCsrOut32( pDevInstance, MODE_REG_1, RXFLCTEN ); #else if ( pDevInstance->TxFlowControl ) { jtCsrOut32 ( pDevInstance, MODE_REG_1, SERECL0 | TXFLCTEN ); } else { jtCsrOut32 ( pDevInstance, MODE_REG_1, TXFLCTEN ); } if ( pDevInstance->RxFlowControl ) { jtCsrOut32 ( pDevInstance, MODE_REG_1, SERECL2 | RXFLCTEN ); } else { jtCsrOut32 ( pDevInstance, MODE_REG_1, RXFLCTEN ); } #endif // CRC check #if defined(WORKAROUND_CRC_CHECK) if (pDevInstance->RxCrcCheck) jtCsrOut32 ( pDevInstance, MODE_REG_1, SERECL2 | PACREN ); #endif // Disable Magic Packet Mode jtCsrOut32( pDevInstance, MODE_REG_1, MGPKEN ); // Make sure all VLAN options are disabled jtCsrOut32 ( pDevInstance, MODE_REG_1, (VLEN | VLTBEN | VLRMID | VLISGB) ); // Turn on receive acceleration jtCsrOut8 ( pDevInstance, (MODE_REG_2 + 1), (SERECL1 | (RXIPCKEN | RXTPCKEN | RXUPCKEN)) >> 8 ); #if defined WORKAROUND_TX_ACCELERATION // Turn off transmit acceleration since first part does not support TX acceleration jtCsrOut8 ( pDevInstance, (MODE_REG_2 + 1), (TXIPCKEN | TXTPCKEN | TXUPCKEN) >> 8 ); #endif // Set the MAC address jtCsrWriteMacAddress(pDevInstance, pDev->dev_addr); // Use the receive watermark to detect the potential overflow condition jtCsrOut16( pDevInstance, RX_FIFO_WTR_MRK_REG, // workaround for R3 0x3fff // workaround for R2 //RX_FIFO_SIZE - ( pDev -> mtu + ETH_HLEN + 3 + 4 )/4 - 1 ); jtCsrOut32( pDevInstance, INTR_MASK_REG, SERECL1 | RXFIWMMS ); // Dummy Phy call to turn on the LEDs jtPhyReadStatusRegister ( pDevInstance, pDevInstance->CurrentPhy.PhyAddress, &PhyStatus ); // Re-initialize receive and transmit buffers (void) (*pDevInstance->pRxInitialize)( pDevInstance ); (void) (*pDevInstance->pTxInitialize)( pDevInstance ); // Enable Transmitter and Receiver jtTxStart( pDevInstance ); jtRxStart( pDevInstance ); // Redo multicast list jtDevSetMulticastList(pDev); } #endif //---------------------------------------------------------------------------- // // FUNCTION: jtDevInterrupt // // DESCRIPTION: The interrupt service routine for the controller. // Irq The number of the interrupt raised. // pDevHandle The controller which raised the interrupt. // pReg Saved state of the cpu. // //---------------------------------------------------------------------------- STATIC void jtDevInterrupt(int Irq, void * pDevHandle, struct pt_regs * pRegs) { struct device * pDev = (struct device *) pDevHandle; DEV_INSTANCE * pDevInstance = NULL; UINT32 InterruptMask = 0; UINT32 EventStatus = 0; UINT8 LoopCount = 0; #if defined(PERFORMANCE_TX) BOOLEAN TimerPopped = FALSE; #endif #if (LINUX_VERSION_CODE < 0x20200) && defined(PERFORMANCE_TX) && defined(SERIALIZE_TX) unsigned int Flags; #endif // Validate arguments if ( pDev == NULL) { printk(KERN_WARNING __FILE__ ": irq %d for unknown device.\n", Irq); return; } // Get the device private data pDevInstance = (DEV_INSTANCE *) pDev->priv; // Check whether this is our interrupt by reading the interrupt mask. jtCsrIn32( pDevInstance, INTR_MASK_REG, &InterruptMask ); InterruptMask |= DMDNMSK; // JT_DPRINTK("Interrupt Mask %#010x.\n", InterruptMask); if( (InterruptMask & INENMS) == 0 ) { // This isn't our interrupt return; } // Mark interrupt-in-progress flag set_bit(0, (void *) &pDev->interrupt ); // Read the event status jtCsrIn32( pDevInstance, INTR_STATUS_REG, &EventStatus ); //JT_DPRINTK( "Event Status %#010x.\n", EventStatus); // Handle the active events, until no more or we get tired while ( (EventStatus & InterruptMask) != 0 ) { #if defined WORKAROUND_RX_FIFO_OVERFLOW // Handle Controller Reset if( EventStatus & RXFIWMIN ) { JT_DPRINTK( "Fifo Overflow\n" ); //printk ( KERN_CRIT "Fifo Overflow\n" ); jtDevWorkaroundReset( pDevInstance ); break; } #endif (void) (*pDevInstance->pRxReceive)( pDevInstance, EventStatus ); // Acknowledge Bus Master transmit complete #if defined(STANDARD_TX) if( EventStatus & TXDMDNIN ) { UINT8 TransmitDoneCount = 0; // Read number of commands done transmitting jtCsrIn8( pDevInstance, CMD_STATUS_REG, &TransmitDoneCount ); // Cleanup buffers used by transmit jtTxPDxDone( pDevInstance, TransmitDoneCount ); } #elif defined(PERFORMANCE_TX) // See if the timer has expired if( EventStatus & TMEXMS ) TimerPopped = TRUE; // See if there are any packets to process if( atomic_read( &pDevInstance->TxPDLPending ) || test_bit(0, (void*) &pDevInstance->TxFull) ) { UINT8 TransmitDoneCount = 0; // Read number of commands done transmitting jtCsrIn8( pDevInstance, CMD_STATUS_REG, &TransmitDoneCount ); // Cleanup buffers used by transmit jtTxPDxDone( pDevInstance, TransmitDoneCount ); } #endif // Handle PHY interrupt if( EventStatus & PHLASTIN ) { (void) jtPhyHandleStatusChange(pDevInstance); } // Check whether we're allowed to loop again ++LoopCount; if( LoopCount >= JT_MAX_LOOP_COUNT ) { // Don't check for more events, just exit // JT_DPRINTK( "Max Loop Count reached.\n" ); break; } // Read the event status jtCsrIn32( pDevInstance, INTR_STATUS_REG, &EventStatus ); // JT_DPRINTK( "Event Status %#010x.\n", EventStatus); } #if defined(PERFORMANCE_TX) #if defined(SERIALIZE_TX) spin_lock(&pDevInstance->DriverLock); #endif // See if we need another timer if( TimerPopped ) { if( atomic_read(&pDevInstance->TxPDLPending) || test_bit(0, (void*) &pDevInstance->TxFull) ) { #if TRUE //!defined(WORKAROUND_WRITE_COMBINE) jtCsrOut32( pDevInstance, COMMAND_REG, SERECL0 | DLINRQ ); #else outl( SERECL0 | DLINRQ, pDevInstance->pDev->base_addr + COMMAND_REG ); #endif } else clear_bit(0, (void*) &pDevInstance->TxPDLTimerOn); } else { // See if we can stop the timer if( test_bit(0, (void*) &pDevInstance->TxPDLTimerOn) ) { if( !atomic_read(&pDevInstance->TxPDLPending) && !test_bit(0, (void*) &pDevInstance->TxFull) ) { clear_bit(0, (void*) &pDevInstance->TxPDLTimerOn); #if TRUE //!defined(WORKAROUND_WRITE_COMBINE) jtCsrOut32( pDevInstance, COMMAND_REG, DLINRQ ); #else outl( DLINRQ, pDevInstance->pDev->base_addr + COMMAND_REG ); #endif } } } #if defined(SERIALIZE_TX) spin_unlock(&pDevInstance->DriverLock); #endif #endif // Interrupt done clear_bit(0, (void *) &pDev->interrupt ); // Re-enable interrupts from controller jtDevEnableInterrupts( pDevInstance ); } //---------------------------------------------------------------------------- // // FUNCTION: jtDevClose // // DESCRIPTION: Standard device method to shutdown controller and cease all // operations. // //---------------------------------------------------------------------------- STATIC int jtDevClose(struct device * pDev) { PDEV_INSTANCE pDevInstance = (PDEV_INSTANCE) pDev->priv; // Decrement module use count MOD_DEC_USE_COUNT; // Disable Device Interrupts jtDevDisableInterrupts ( pDevInstance ); // Release interrupt free_irq( pDev->irq, pDev ); // Flag device as closed pDev->start = 0; pDev->tbusy = 1; pDevInstance->TxFull = 1; // Disable Transmitter and Receiver jtTxStop( pDevInstance ); jtRxStop( pDevInstance ); // Flush receive and transmit buffers (void) (*pDevInstance->pRxFlush)( pDevInstance ); (void) (*pDevInstance->pTxFlush)( pDevInstance ); // Destroy all RX and TX buffers (*pDevInstance->pRxDestroy)( pDevInstance ); (*pDevInstance->pTxDestroy)( pDevInstance ); // Reset the device to make sure the state is known jtDevReset ( pDevInstance ); return 0; } //---------------------------------------------------------------------------- // // FUNCTION: jtDevGetStatistics // // DESCRIPTION: Standard device method to get statistical detail from // the controller. // //---------------------------------------------------------------------------- STATIC struct enet_statistics * jtDevGetStatistics(struct device * pDev) { PDEV_INSTANCE pDevInstance = (PDEV_INSTANCE) pDev->priv; UINT32 StatValue = 0; // Read statistics from the controller and add to local statistics // The statistic are read in the order they occur in // the enet_statistics structure. // rx_packets maintained locally by the driver jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_TX_OK ); jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue ); pDevInstance->Statistics.tx_packets += StatValue; jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_RX_ERROR ); jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue ); pDevInstance->Statistics.rx_errors += StatValue; jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_TX_ERROR ); jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue ); pDevInstance->Statistics.tx_errors += StatValue; jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_DROPPED ); jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue ); pDevInstance->Statistics.rx_dropped += StatValue; // tx_dropped is maintained locally by the driver jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_MULTICAST_OK ); jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue ); pDevInstance->Statistics.multicast += StatValue; // Add together the number of collisioned packets jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_SINGLE_COL ); jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue ); pDevInstance->Statistics.collisions += StatValue; jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_MULTI_COL ); jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue ); pDevInstance->Statistics.collisions += StatValue; jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_LATE_COL ); jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue ); pDevInstance->Statistics.collisions += StatValue; // detailed rx_errors jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_PKT_LENGTH_ERR ); jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue ); pDevInstance->Statistics.rx_length_errors += StatValue; // rx_over_errors is maintained locally by the driver // Add together FCS, IP, TCP UDP CRC errors jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_CRC_ERROR ); jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue ); pDevInstance->Statistics.rx_crc_errors += StatValue; jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_IP_CKSUM_ERR ); jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue ); pDevInstance->Statistics.rx_crc_errors += StatValue; jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_UDP_CKSUM_ERR ); jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue ); pDevInstance->Statistics.rx_crc_errors += StatValue; jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_TCP_CKSUM_ERR ); jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue ); pDevInstance->Statistics.rx_crc_errors += StatValue; jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_ALIGN_ERROR ); jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue ); pDevInstance->Statistics.rx_frame_errors += StatValue; jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_DROPPED ); jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue ); pDevInstance->Statistics.rx_fifo_errors += StatValue; // rx_missed_errors has no corresponding controller statistic // detailed tx_errors jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_EXCESS_COL ); jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue ); pDevInstance->Statistics.tx_aborted_errors += StatValue; jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_CARRIER_SENSE ); jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue ); pDevInstance->Statistics.tx_carrier_errors += StatValue; // tx_fifo_errors, tx_heartbeat_errors, tx_window_errors // have no corresponding controller statistic return &(pDevInstance->Statistics); } //---------------------------------------------------------------------------- // // FUNCTION: jtDevSetMulticastList // // DESCRIPTION: Standard device method to set device modes. // //---------------------------------------------------------------------------- STATIC void jtDevSetMulticastList(struct device * pDev) { PDEV_INSTANCE pDevInstance = (PDEV_INSTANCE) pDev->priv; // If promiscuous mode is requested if( pDev->flags & IFF_PROMISC ) { // Enable controller promisc mode jtCsrOut32( pDevInstance, MODE_REG_1, POEN|SERECL1 ); } else { // Disable controller promisc mode jtCsrOut32( pDevInstance, MODE_REG_1, POEN ); } // If all multicast requested if( pDev->flags & IFF_ALLMULTI ) { // Set the multicast hash filter to accept all packets pDevInstance->MulticastHashLow = ~0; pDevInstance->MulticastHashHigh = ~0; // Enable multicast reception jtCsrOut32( pDevInstance, MCAST_HASH_TBL_REG_LO, pDevInstance->MulticastHashLow ); jtCsrOut32( pDevInstance, MCAST_HASH_TBL_REG_HI, pDevInstance->MulticastHashHigh ); jtCsrOut32( pDevInstance, MODE_REG_1, SERECL1 | MCEN ); } // Or if a multicast list is indicated else if( pDev->mc_count > 0 ) { struct dev_mc_list * pMCEntry = pDev->mc_list; // Initialize the multicast hash filter to empty pDevInstance->MulticastHashLow = 0; pDevInstance->MulticastHashHigh = 0; // Walk the list of multicast entries, adding each one to the hash filter for( ; pMCEntry != NULL; pMCEntry = pMCEntry->next ) { jtAddAddressToMulticastHash( pDevInstance, pMCEntry->dmi_addr ); } // Write hash filter to the controller, and activate multicast filtering jtCsrOut32( pDevInstance, MCAST_HASH_TBL_REG_LO, pDevInstance->MulticastHashLow ); jtCsrOut32( pDevInstance, MCAST_HASH_TBL_REG_HI, pDevInstance->MulticastHashHigh ); jtCsrOut32( pDevInstance, MODE_REG_1, SERECL1 | MCEN ); } else { // Disable controller multicast mode jtCsrOut32( pDevInstance, MODE_REG_1, MCEN ); } // If broadcast address valid if( pDev->flags & IFF_BROADCAST ) { // Enable controller broadcast mode jtCsrOut32( pDevInstance, MODE_REG_1, SERECL1 | BCEN ); } else { // Disable controller broadcast mode jtCsrOut32( pDevInstance, MODE_REG_1, BCEN ); } #if defined(JT_DEBUG) /* if( verbose ) { UINT32 Mode1 = 0; UINT32 Mode2 = 0; jtCsrIn32( pDevInstance, MODE_REG_1, &Mode1 ); jtCsrIn32( pDevInstance, MODE_REG_2, &Mode2 ); JT_DPRINTK( "Mode Reg 1: %#08x, Mode Reg 2: %#08x\n", Mode1, Mode2 ); } */ #endif } //---------------------------------------------------------------------------- // // FUNCTION: jtDevIoctl // // DESCRIPTION: Standard device method for misc operations. // pDev The pertinent controller. // pIfr Interface request structure. // cmd The command to perform. // //---------------------------------------------------------------------------- STATIC int jtDevIoctl(struct device *pDev, struct ifreq *pIfr, int cmd) { // No ioctls currently supported return -EINVAL; } //---------------------------------------------------------------------------- // // FUNCTION: jtDevSetMacAddress // // DESCRIPTION: Standard device method to set the MAC address. // pDev The pertinent controller. // pNewSockAddr Untyped pointer to the new address (type is sockaddr). // //---------------------------------------------------------------------------- STATIC int jtDevSetMacAddress(struct device * pDev, void * pNewSockAddr ) { UINT8 * pNewAddr = ((struct sockaddr *) pNewSockAddr)->sa_data; // Copy the new mac address into the device struct memcpy(pDev->dev_addr, (const UINT8 *) pNewAddr, ETH_ALEN); // Set the controller's mac address jtCsrWriteMacAddress( (PDEV_INSTANCE) pDev->priv, pDev->dev_addr); return 0; } //---------------------------------------------------------------------------- // // FUNCTION: jtDevChangeMtu // // DESCRIPTION: Standard device method to change the MTU value. // //---------------------------------------------------------------------------- STATIC int jtDevChangeMtu(struct device *pDev, int NewMtu) { // Can only change MTU before device is opened if( pDev->start ) { return -EBUSY; } // Validate that the new MTU is reasonable if ( NewMtu < 60 || NewMtu > JT_MAX_MTU ) { return -EINVAL; } // Set the device's mtu pDev->mtu = NewMtu; return 0; } /****************************************************************************** * * Other device methods * ******************************************************************************/ //---------------------------------------------------------------------------- // // FUNCTION: jtAddAddressToMulticastHash // // DESCRIPTION: Compute the hash value of a network address, and OR it into // the multicast hash. Note that this routine does not update // the controller's registers, only the saved hash in the device // structure. // // PARAMETERS: // pDevInstance The pertinent controller instance. // pNewAddress The multicast address to add the the controller's hash. // //---------------------------------------------------------------------------- STATIC void jtAddAddressToMulticastHash( DEV_INSTANCE * pDevInstance, UINT8 * pNewAddress ) { UINT32 CRC = -1; UINT8 HashTableIndex = 0; UINT8 ByteIndex; UINT32 c = 0; UINT8 BitIndex; const UINT32 CRC32_POLY = 0xedb88320; // Ethernet CRC generator polynomial #if defined(EMULATION) emuAddMulticastListEntry( jtEmuHandle, pNewAddress ); #endif // Calculate the CRC on the address to be added to the multicast list for ( ByteIndex = 0; ByteIndex < ETH_ALEN; ByteIndex++ ) { c = ( ( CRC & 0x0ff ) ^ pNewAddress[ByteIndex] ); for ( BitIndex = 0; BitIndex < 8; BitIndex++ ) { ( c & 1 ) ? ( c = ( ( c >> 1 ) ^ CRC32_POLY ) ) : ( c >>= 1 ); } CRC = ( CRC >> 8 ) ^ c; } // The Bit Offset into the Multicast hash table is the // reverse order of the last 6 bits of the CRC for ( BitIndex = 0; BitIndex < 6; BitIndex++ ) { HashTableIndex <<= 1; HashTableIndex |= (CRC & 1); CRC >>= 1; } // Check to see if this bit is in the MSD or LSD of the 64 bit hash if ( HashTableIndex >= 32 ) { pDevInstance->MulticastHashHigh |= (1 << ( HashTableIndex - 32 )); } else { pDevInstance->MulticastHashLow |= (1 << HashTableIndex); } } //---------------------------------------------------------------------------- // // FUNCTION: jtCsrWriteMacAddress // // DESCRIPTION: Set the controller's current MAC address. // // PARAMETERS: // pDevInstance The pertinent controller instance. // pMacAddress The MAC address (in big endian format). // //---------------------------------------------------------------------------- STATIC void jtCsrWriteMacAddress( PDEV_INSTANCE pDevInstance, const UINT8 * pMacAddress ) { UINT32 MacDword0; UINT32 MacDword1; // Write the address one dword at a time MacDword0 = pMacAddress[0] | ((pMacAddress[1]) << 8) | ((pMacAddress[2]) << 16) | ((pMacAddress[3]) << 24); MacDword1 = pMacAddress[4] | ((pMacAddress[5] << 8)); jtCsrOut32(pDevInstance, LAN_PHY_ADR_LO, MacDword0); jtCsrOut32(pDevInstance, LAN_PHY_ADR_HI, MacDword1); } //---------------------------------------------------------------------------- // // FUNCTION: jtCsrReadMacAddress // // DESCRIPTION: Get the controller's current MAC address. // // PARAMETERS: // pDevInstance The pertinent controller instance. // // RETURNS: // pMacAddress The MAC address (stored in big endian format). // //---------------------------------------------------------------------------- STATIC void jtCsrReadMacAddress( PDEV_INSTANCE pDevInstance, UINT8 * pMacAddress ) { UINT32 MacDword0; UINT32 MacDword1; // Read the address one dword at a time jtCsrIn32(pDevInstance, LAN_PHY_ADR_LO, &MacDword0); jtCsrIn32(pDevInstance, LAN_PHY_ADR_HI, &MacDword1); pMacAddress[0] = (UINT8) (MacDword0); pMacAddress[1] = (UINT8) (MacDword0 >> 8); pMacAddress[2] = (UINT8) (MacDword0 >> 16); pMacAddress[3] = (UINT8) (MacDword0 >> 24); pMacAddress[4] = (UINT8) (MacDword1); pMacAddress[5] = (UINT8) (MacDword1 >> 8); } /****************************************************************************** * * Phy methods * ******************************************************************************/ //---------------------------------------------------------------------------- // // FUNCTION: jtPhyFindPhy // // DESCRIPTION: Detect all Phys connected to the controller. // Choose the first, isolating all others (if any). // //---------------------------------------------------------------------------- STATIC JT_STATUS jtPhyFindPhy ( PDEV_INSTANCE pDevInstance ) { struct device * pDev = pDevInstance->pDev; JT_STATUS Result = JT_STATUS_SUCCESS; UINT32 GMIIMode = 0; UINT8 MaxPhyAddresses; BOOLEAN Found = FALSE; UINT8 PhyAddress; PHY_ID FoundPhyId = 0; UINT8 FoundPhyType = 0; UINT8 FoundPhyAddress = 0; // Determine if a PCS device is attached to this configuration jtCsrIn32 ( pDevInstance, GMII_MODE_REG, &GMIIMode ); if (GMIIMode & GMPCEN) { MaxPhyAddresses = 1; } else { MaxPhyAddresses = 32; } // Iterate over each possible phy for (PhyAddress=0; PhyAddress < MaxPhyAddresses ; PhyAddress++) { UINT16 PhyId1 = 0; UINT16 PhyId2 = 0; // Read the first Phy identification register Result = jtPhyReadRegister ( pDevInstance, PhyAddress, PHY_ID_1_REG, &PhyId1 ); if (Result != JT_STATUS_SUCCESS) { continue; } // Read the second Phy identification register Result = jtPhyReadRegister ( pDevInstance, PhyAddress, PHY_ID_2_REG, &PhyId2 ); if (Result != JT_STATUS_SUCCESS) { continue; } // Look for a hardcoded list of PHY's we recognize. // This may be extended in the future to handle the general case if ((PhyId1 == HG1_PCS_PHY_ID_1) && (PhyId2 == HG1_PCS_PHY_ID_2)) { Found = TRUE; FoundPhyType = PHY_TYPE_PCS; FoundPhyId = PHY_LEVEL1_PCS; FoundPhyAddress = 0; break; } else if (((PhyId1 == LEVEL1_PHY_ID_1) && ((PhyId2 & 0xfffe) == LEVEL1_PHY_ID_2)) || ((PhyId1 == LEVEL1_PHY_ID_1_B) && (PhyId2 == LEVEL1_PHY_ID_2_B))) { Found = TRUE; FoundPhyType = PHY_TYPE_MII; FoundPhyId = PHY_LEVEL1; FoundPhyAddress = PhyAddress; break; } else if ((PhyId1 == NATIONAL_PHY_ID_1) && ((PhyId2 & 0xfffe) == NATIONAL_PHY_ID_2)) { Found = TRUE; FoundPhyType = PHY_TYPE_MII; FoundPhyId = PHY_NATIONAL; FoundPhyAddress = PhyAddress; break; } } // If didn't find a Phy if ( !Found ) { printk( KERN_ERR "%s: No PHY detected.\n", pDev->name ); return(JT_STATUS_DEVICE_NOT_FOUND); } // Announce detection #if !defined JT_DEBUG printk( KERN_INFO "%s: %s %s PHY detected at address %d\n", pDev->name, FoundPhyId== PHY_LEVEL1_PCS ? "Level One" : FoundPhyId == PHY_LEVEL1 ? "Level One" : "National", FoundPhyType == PHY_TYPE_GMII ? "GMII" : FoundPhyType == PHY_TYPE_MII ? "MII" : "PCS", FoundPhyAddress ); #else JT_DPRINTK( "%s: %s %s PHY detected at address %d\n", pDev->name, FoundPhyId== PHY_LEVEL1_PCS ? "Level One" : FoundPhyId == PHY_LEVEL1 ? "Level One" : "National", FoundPhyType == PHY_TYPE_GMII ? "GMII" : FoundPhyType == PHY_TYPE_MII ? "MII" : "PCS", FoundPhyAddress ); #endif // Save PHY info pDevInstance->CurrentPhy.PhyId = FoundPhyId; pDevInstance->CurrentPhy.PhyType = FoundPhyType; pDevInstance->CurrentPhy.PhyAddress = FoundPhyAddress; // Isolate all other G/MII Phys except for the first for ( ; PhyAddress < MaxPhyAddresses ; PhyAddress++) { UINT16 PhyReg = 0; // Isolate the PHY Result = jtPhyReadRegister ( pDevInstance, PhyAddress, PHY_CONTROL_REG, &PhyReg ); if (Result != JT_STATUS_SUCCESS) { continue; } // Turn on the Isolate Bit and write back to the phy PhyReg |= PHY_CTL_ISLT | PHY_CTL_ANEN | PHY_CTL_RSAN; (void) jtPhyWriteRegister ( pDevInstance, PhyAddress, PHY_CONTROL_REG, PhyReg ); } return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtPhyUpdateSpeedDuplex // // DESCRIPTION: Read the link speed and duplex from the Phy, then // update the controller's and our local copy of same. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtPhyUpdateSpeedDuplex( PDEV_INSTANCE pDevInstance ) { struct device * pDev = pDevInstance->pDev; JT_STATUS Result = JT_STATUS_SUCCESS; UINT16 PhyReg = 0; UINT32 GMIIMode; // If the Phy is the NetCelerator PCS interface if (pDevInstance->CurrentPhy.PhyId == PHY_LEVEL1_PCS) { // Read the Phy Status Register Result = jtPhyReadRegister ( pDevInstance, pDevInstance->CurrentPhy.PhyAddress, PHY_CONTROL_REG, &PhyReg ); if (Result != JT_STATUS_SUCCESS) { return Result; } // Decode the duplex switch (PhyReg & PHY_CTL_FDMD) { case PHY_CTL_FDMD: pDevInstance->DuplexMode = DUPLEX_MODE_FULL; break; default: pDevInstance->DuplexMode = DUPLEX_MODE_HALF; } // Decode the link speed switch (PhyReg & ( PHY_CTL_GSPSEL | PHY_CTL_SPSEL )) { case (PHY_CTL_GSPSEL | PHY_CTL_SPSEL): case PHY_CTL_GSPSEL: pDevInstance->Speed = LINK_SPEED_1000; break; case PHY_CTL_SPSEL: pDevInstance->Speed = LINK_SPEED_100; break; default: pDevInstance->Speed = LINK_SPEED_10; break; } } // Or if the Phy is the Level One MII Phy else if (pDevInstance->CurrentPhy.PhyId == PHY_LEVEL1) { // Read the Phy Status Register Result = jtPhyReadRegister ( pDevInstance, pDevInstance->CurrentPhy.PhyAddress, PHY_LVL1_CHIP_STAT_REG, &PhyReg ); if (Result != JT_STATUS_SUCCESS) { return Result; } // Decode the duplex switch (PhyReg & PHY_LVL1_CHIP_STAT_DUP) { case PHY_LVL1_CHIP_STAT_DUP: pDevInstance->DuplexMode = DUPLEX_MODE_FULL; break; default: pDevInstance->DuplexMode = DUPLEX_MODE_HALF; } // Decode the speed switch (PhyReg & PHY_LVL1_CHIP_STAT_100M) { case PHY_LVL1_CHIP_STAT_100M: pDevInstance->Speed = LINK_SPEED_100; break; default: pDevInstance->Speed = LINK_SPEED_10; } } // Or if the phy is National Phy else if (pDevInstance->CurrentPhy.PhyId == PHY_NATIONAL) { // Read the Phy Status Register Result = jtPhyReadRegister ( pDevInstance, pDevInstance->CurrentPhy.PhyAddress, PHY_NAT_CHIP_STAT_REG, &PhyReg ); if (Result != JT_STATUS_SUCCESS) { return Result; } // Decode the duplex switch (PhyReg & PHY_NAT_CHIP_STAT_DUP) { case PHY_NAT_CHIP_STAT_DUP: pDevInstance->DuplexMode = DUPLEX_MODE_FULL; break; default: pDevInstance->DuplexMode = DUPLEX_MODE_HALF; } // Decode the speed switch (PhyReg & PHY_NAT_CHIP_STAT_10M) { case PHY_NAT_CHIP_STAT_10M: pDevInstance->Speed = LINK_SPEED_10; break; default: pDevInstance->Speed = LINK_SPEED_100; } } // Update the GMIIMode Register to reflect the PHY's parameters // .. First read the GMIIMode Register jtCsrIn32 ( pDevInstance, GMII_MODE_REG, &GMIIMode ); GMIIMode &= (UINT32)~(GMWRSP_MASK | GMFD); // Set duplex to half or full if (pDevInstance->DuplexMode == DUPLEX_MODE_FULL) { GMIIMode |= GMFD; } // Set speed to 10, 100 or 1000 switch (pDevInstance->Speed) { case LINK_SPEED_100: GMIIMode |= GMWRSP_100MB; break; case LINK_SPEED_1000: GMIIMode |= GMWRSP_1000MB; break; } // Write the Mode Register to set the proper speed/duplex jtCsrOut32 ( pDevInstance, GMII_MODE_REG, GMIIMode ); // Announce speed #if !defined JT_DEBUG printk(KERN_INFO "%s: Speed %s Mb, %s Duplex\n", pDev->name, pDevInstance->Speed == LINK_SPEED_10 ? "10" : pDevInstance->Speed == LINK_SPEED_100 ? "100" : "1000", pDevInstance->DuplexMode == DUPLEX_MODE_HALF ? "Half" : "Full" ); #else JT_DPRINTK( "%s: Speed %s Mb, %s Duplex\n", pDev->name, pDevInstance->Speed == LINK_SPEED_10 ? "10" : pDevInstance->Speed == LINK_SPEED_100 ? "100" : "1000", pDevInstance->DuplexMode == DUPLEX_MODE_HALF ? "Half" : "Full" ); #endif return(Result); } //---------------------------------------------------------------------------- // // FUNCTION: jtPhyGetLinkStatus // // DESCRIPTION: Read the link status (connected/not connected) from // the currently active Phy // // PARAMETERS: // pDevInstance The pertinent controller instance. // // RETURNS: // pConnected TRUE if the link is connected, FALSE if not. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtPhyGetLinkStatus( PDEV_INSTANCE pDevInstance, JT_PHY_STATUS *pConnected ) { JT_STATUS Result = JT_STATUS_SUCCESS; UINT16 PhyStatus = 0; UINT16 LoopCount; // Initially assume link is not connected *pConnected = JT_PHY_STATUS_FAILURE; #if defined(EMULATION) // Wait 30 seconds for AN complete or Link, AN does not always complete for (LoopCount=0; LoopCount < 300 ; LoopCount++ ) { Result = jtPhyReadStatusRegister ( pDevInstance, pDevInstance->CurrentPhy.PhyAddress, &PhyStatus ); if ((Result != JT_STATUS_SUCCESS) || (PhyStatus & PHY_STAT_AN_DONE) || (PhyStatus & PHY_STAT_LINK_OK)) { break; } // Wait and try again udelay( 100*1000 ); // 100 milliseconds } #endif // If the Phy is the Level One PCS Phy if (pDevInstance->CurrentPhy.PhyId == PHY_LEVEL1_PCS) { // Wait 1 second for AN complete or Link, AN does not always complete for (LoopCount=0; LoopCount < 10 ; LoopCount++ ) { // Read the Phy Status Register Result = jtPhyReadStatusRegister ( pDevInstance, pDevInstance->CurrentPhy.PhyAddress, &PhyStatus ); if ((Result != JT_STATUS_SUCCESS) || (PhyStatus & PHY_STAT_AN_DONE)) { break; } // Wait and try again udelay( 100*1000 ); } if ( Result != JT_STATUS_SUCCESS ) { return Result; } // Let's get the result if ( PhyStatus & PHY_STAT_AN_DONE ) { Result = jtPhyReadRegister ( pDevInstance, pDevInstance->CurrentPhy.PhyAddress, PHY_AUTO_NEG_PART_BASE_REG, &PhyStatus ); // Analize and return the proper result if ( Result == JT_STATUS_SUCCESS ) { PhyStatus = PhyStatus & PHY_PCS_AD_RFLT; switch ( PhyStatus ) { case PHY_PCS_AD_RFLT_LNK_FAIL: PhyStatus = (PhyStatus & (UINT16)~PHY_PCS_AD_RFLT); Result = jtPhyWriteRegister ( pDevInstance, pDevInstance->CurrentPhy.PhyAddress, PHY_AUTO_NEG_AD_REG, PhyStatus ); case PHY_PCS_AD_RFLT_OK: if ( Result == JT_STATUS_SUCCESS ) *pConnected = JT_PHY_STATUS_LINK; else *pConnected = JT_PHY_STATUS_FAILURE; break; case PHY_PCS_AD_RFLT_OFFLINE: *pConnected = JT_PHY_STATUS_FAILURE; break; case PHY_PCS_AD_RFLT_AN_ERROR: *pConnected = JT_PHY_STATUS_RESTART; break; } } else *pConnected = JT_PHY_STATUS_FAILURE; } else *pConnected = JT_PHY_STATUS_FAILURE; } // Or if the Phy is the Level One Phy else if (pDevInstance->CurrentPhy.PhyId == PHY_LEVEL1) { // Wait 2 seconds for AN complete or Link, AN does not always complete for (LoopCount=0; LoopCount < 20 ; LoopCount++ ) { Result = jtPhyReadStatusRegister ( pDevInstance, pDevInstance->CurrentPhy.PhyAddress, &PhyStatus ); if ((Result != JT_STATUS_SUCCESS) || (PhyStatus & PHY_STAT_AN_DONE) || (PhyStatus & PHY_STAT_LINK_OK)) { break; } // Wait and try again udelay( 100*1000 ); } if (Result != JT_STATUS_SUCCESS) { return Result; } // Read the Phy Status Register again to make sure everything's OK Result = jtPhyReadStatusRegister ( pDevInstance, pDevInstance->CurrentPhy.PhyAddress, &PhyStatus ); if (Result != JT_STATUS_SUCCESS) { return Result; } if (PhyStatus & PHY_STAT_LINK_OK) { *pConnected = JT_PHY_STATUS_LINK; } } // Or if the Phy is the National Phy else if (pDevInstance->CurrentPhy.PhyId == PHY_NATIONAL) { // Wait 3 seconds for AN complete or Link, AN does not always complete for (LoopCount=0; LoopCount < 30 ; LoopCount++ ) { Result = jtPhyReadStatusRegister ( pDevInstance, pDevInstance->CurrentPhy.PhyAddress, &PhyStatus ); if ((Result != JT_STATUS_SUCCESS) || (PhyStatus & PHY_STAT_AN_DONE) || (PhyStatus & PHY_STAT_LINK_OK)) { break; } // Wait and try again udelay( 100*1000 ); } if (Result != JT_STATUS_SUCCESS) { return Result; } // Read the Phy Status Register again to make sure everything's OK Result = jtPhyReadStatusRegister ( pDevInstance, pDevInstance->CurrentPhy.PhyAddress, &PhyStatus ); if (Result != JT_STATUS_SUCCESS) { return Result; } if (PhyStatus & PHY_STAT_LINK_OK) { *pConnected = JT_PHY_STATUS_LINK; } } return(Result); } //---------------------------------------------------------------------------- // // FUNCTION: jtPhyHandleStatusChange // // DESCRIPTION: Process a change in the status of the Phy (link change, etc). // //---------------------------------------------------------------------------- STATIC JT_STATUS jtPhyHandleStatusChange( PDEV_INSTANCE pDevInstance ) { struct device * pDev = pDevInstance->pDev; JT_STATUS Result = JT_STATUS_SUCCESS; JT_PHY_STATUS CurrentlyConnected; UINT16 PhyReg; JT_ENTER; // Find out if the Phy is connected Result = jtPhyGetLinkStatus( pDevInstance, &CurrentlyConnected ); if (Result != JT_STATUS_SUCCESS) { JT_LEAVE; return Result; } // And process the result switch (CurrentlyConnected) { case JT_PHY_STATUS_LINK: pDevInstance->CurrentPhy.Connected = TRUE; Result = jtPhyUpdateSpeedDuplex( pDevInstance ); Result = JT_STATUS_SUCCESS; break; case JT_PHY_STATUS_FAILURE: pDevInstance->CurrentPhy.Connected = FALSE; #if !defined JT_DEBUG printk(KERN_INFO "%s: Not Connected.\n", pDev->name); #else JT_DPRINTK("%s: Not Connected.\n", pDev->name); #endif Result = JT_STATUS_SUCCESS; break; case JT_PHY_STATUS_RESTART: pDevInstance->CurrentPhy.Connected = FALSE; // Let's read the Control register Result = jtPhyReadRegister ( pDevInstance, pDevInstance->CurrentPhy.PhyAddress, PHY_CONTROL_REG, &PhyReg ); PhyReg |= (PHY_CTL_RSAN); Result = jtPhyWriteRegister ( pDevInstance, pDevInstance->CurrentPhy.PhyAddress, PHY_CONTROL_REG, PhyReg ); break; } JT_LEAVE; return Result; } //---------------------------------------------------------------------------- // // FUNCTION: jtPhySetSpeed // // DESCRIPTION: Update the Phy control register to request a new speed. // // PARAMETERS: // pDevInstance The pertinent controller instance. // PhySpeed The requested speed (LINKS_SPEED_10, 100 or 1000) // pPhyControlReg The current value of the Phy control register. // // RETURNS: // pPhyControlReg The updated value of the Phy control register. // //---------------------------------------------------------------------------- STATIC void jtPhySetSpeed( PDEV_INSTANCE pDevInstance, UINT16 PhySpeed, PUINT16 pPhyControlReg ) { // If the phy is PCS or GMII if ((pDevInstance->CurrentPhy.PhyType == PHY_TYPE_PCS) || (pDevInstance->CurrentPhy.PhyType == PHY_TYPE_GMII)) { // Use GMII Definitions *pPhyControlReg &= ~(UINT16)(PHY_CTL_GSPSEL | PHY_CTL_SPSEL); switch (PhySpeed) { case LINK_SPEED_10: // Do nothing break; case LINK_SPEED_100: *pPhyControlReg |= PHY_CTL_SPSEL; break; default: *pPhyControlReg |= PHY_CTL_GSPSEL; } } // or it must be MII else { if (PhySpeed == LINK_SPEED_10) { *pPhyControlReg &= ~(UINT16)PHY_CTL_SPSEL; } else { *pPhyControlReg |= PHY_CTL_SPSEL; } } } //---------------------------------------------------------------------------- // // FUNCTION: jtPhyInitialize // // DESCRIPTION: Setup the phy interface based on user supplied value for // duplex and speed, if any. Otherwise, use auto-negotiation. // //---------------------------------------------------------------------------- JT_STATUS jtPhyInitialize ( PDEV_INSTANCE pDevInstance ) { struct device * pDev = pDevInstance->pDev; JT_STATUS Result; UINT16 PhyReg; UINT16 ANReg; UINT16 SavedPhyReg = 0; UINT16 SavedANReg = 0; JT_PHY_STATUS CurrentlyConnected=JT_PHY_STATUS_FAILURE; UINT32 EventStatus; // Disable Phy Interrupts jtPhyDisableInterrupts ( pDevInstance ); // Let's read the Control register Result = jtPhyReadRegister ( pDevInstance, pDevInstance->CurrentPhy.PhyAddress, PHY_CONTROL_REG, &PhyReg ); if ( Result != JT_STATUS_SUCCESS ) { return Result; } SavedPhyReg = PhyReg; // "Un-Isolate" the Phy if (PhyReg & PHY_CTL_ISLT) { PhyReg &= (UINT16)~PHY_CTL_ISLT; } // Is the Speed and/or the Duplex forced? if ((pDevInstance->OverrideSpeed != LINK_SPEED_AUTO) || (pDevInstance->OverrideDuplexMode != DUPLEX_MODE_AUTO)) { // Announce override #if !defined JT_DEBUG printk(KERN_INFO "%s: Manual link override: Speed %s Mb, %s Duplex.\n", pDev->name, pDevInstance->OverrideSpeed == LINK_SPEED_10 ? "10" : pDevInstance->OverrideSpeed == LINK_SPEED_100 ? "100" : "1000", pDevInstance->OverrideDuplexMode == DUPLEX_MODE_HALF ? "Half" : "Full" ); #else JT_DPRINTK("%s: Manual link override: Speed %s Mb, %s Duplex.\n", pDev->name, pDevInstance->OverrideSpeed == LINK_SPEED_10 ? "10" : pDevInstance->OverrideSpeed == LINK_SPEED_100 ? "100" : "1000", pDevInstance->OverrideDuplexMode == DUPLEX_MODE_HALF ? "Half" : "Full" ); #endif // Is AN Enabled if (PhyReg & PHY_CTL_ANEN) { // Disable AN PhyReg &= (UINT16)~PHY_CTL_ANEN; // Write to the phy, AN must be disabled independently of setting speed/duplex Result = jtPhyWriteRegister ( pDevInstance, pDevInstance->CurrentPhy.PhyAddress, PHY_CONTROL_REG, PhyReg ); SavedPhyReg = PhyReg; if (Result != JT_STATUS_SUCCESS) { return Result; } } // Is the Duplex set properly if ((pDevInstance->OverrideDuplexMode & (DUPLEX_MODE_AUTO|DUPLEX_MODE_HALF|DUPLEX_MODE_FULL)) == DUPLEX_MODE_HALF) { // Force Half PhyReg &= (UINT16)~PHY_CTL_FDMD; } else { // Force Full PhyReg |= PHY_CTL_FDMD; } // Set the Speed jtPhySetSpeed( pDevInstance, pDevInstance->OverrideSpeed, &PhyReg ); //Modify the control register if necessary if ( SavedPhyReg != PhyReg ) { Result = jtPhyWriteRegister ( pDevInstance, pDevInstance->CurrentPhy.PhyAddress, PHY_CONTROL_REG, PhyReg ); if (Result != JT_STATUS_SUCCESS) { return Result; } } } else // Use autonegotiation { // Enable AN PhyReg |= (UINT16)PHY_CTL_ANEN; // If using MII phy if (pDevInstance->CurrentPhy.PhyType == PHY_TYPE_MII) { // Auto Negotiate with advertisement set to Link Online Result = jtPhyReadRegister ( pDevInstance, pDevInstance->CurrentPhy.PhyAddress, PHY_AUTO_NEG_AD_REG, &ANReg ); if (Result != JT_STATUS_SUCCESS) { return Result; } SavedANReg=ANReg; // Set the AN Advertisement Register to indicate all capabilities ANReg |= PHY_MII_AD_10T | PHY_MII_AD_10T_FD | PHY_MII_AD_100T | PHY_MII_AD_100T_FD; if ( SavedANReg != ANReg ) { Result = jtPhyWriteRegister ( pDevInstance, pDevInstance->CurrentPhy.PhyAddress, PHY_AUTO_NEG_AD_REG, ANReg ); } } // If not MII, Is this Phy PCS? else if (pDevInstance->CurrentPhy.PhyType == PHY_TYPE_PCS ) { // Auto Negotiate with advertisement set to Link Online Result = jtPhyReadRegister ( pDevInstance, pDevInstance->CurrentPhy.PhyAddress, PHY_AUTO_NEG_AD_REG, &ANReg ); if (Result != JT_STATUS_SUCCESS) { return Result; } #if defined WORKAROUND_FLOW_CONTROL // Turn off the Offline Bit and the Pause Capability ANReg = (ANReg & (UINT16)(~PHY_PCS_AD_RFLT_OFFLINE | PHY_PCS_AD_PAUSE_NO)); #else // Turn off the Offline Bit ANReg = (ANReg & (UINT16)~PHY_PCS_AD_RFLT_OFFLINE); #endif // Now let's write to the Phy Result = jtPhyWriteRegister ( pDevInstance, pDevInstance->CurrentPhy.PhyAddress, PHY_AUTO_NEG_AD_REG, ANReg ); if (Result != JT_STATUS_SUCCESS) { return Result; } } //If necessary we restart the autonegotiation if ( SavedPhyReg != PhyReg || SavedANReg != ANReg) { // Enable AN #if !defined JT_DEBUG printk(KERN_INFO "%s: AutoNegotiate link.\n", pDev->name); #else JT_DPRINTK("%s: AutoNegotiate link.\n", pDev->name); #endif PhyReg |= (PHY_CTL_RSAN); Result = jtPhyWriteRegister ( pDevInstance, pDevInstance->CurrentPhy.PhyAddress, PHY_CONTROL_REG, PhyReg ); if (Result != JT_STATUS_SUCCESS) { return Result; } } } // Is the link Active? Result = jtPhyGetLinkStatus( pDevInstance, &CurrentlyConnected ); if (Result != JT_STATUS_SUCCESS) { return Result; } // And process the result switch (CurrentlyConnected) { case JT_PHY_STATUS_LINK: pDevInstance->CurrentPhy.Connected = TRUE; Result = jtPhyUpdateSpeedDuplex( pDevInstance ); break; case JT_PHY_STATUS_FAILURE: pDevInstance->CurrentPhy.Connected = FALSE; #if !defined JT_DEBUG printk(KERN_INFO "%s: Not Connected.\n", pDev->name); #else JT_DPRINTK("%s: Not Connected.\n", pDev->name); #endif Result = JT_STATUS_SUCCESS; break; case JT_PHY_STATUS_RESTART: pDevInstance->CurrentPhy.Connected = FALSE; PhyReg |= (PHY_CTL_RSAN); Result = jtPhyWriteRegister ( pDevInstance, pDevInstance->CurrentPhy.PhyAddress, PHY_CONTROL_REG, PhyReg ); break; } // We need this not to generate an interrupt jtCsrIn32( pDevInstance, INTR_STATUS_REG, &EventStatus ); // Enable Phy Interrupts jtPhyEnableInterrupts ( pDevInstance ); return(Result); } //---------------------------------------------------------------------------- // // FUNCTION: jtPhyWriteRegister // // DESCRIPTION: Write a 16 bit value to the PHY register specified. // // PARAMETERS: // pDevInstance The pertinent controller instance. // PhyAddress The address of the Phy to affect. // PhyRegister The particular register to write to. // Data The value to write to the register. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtPhyWriteRegister ( PDEV_INSTANCE pDevInstance, UINT8 PHYAddress, UINT8 PHYRegister, UINT16 Data ) { UINT32 GMIIStatus; UINT8 Milliseconds; UINT32 GMIIRegisterData = 0; // Assemble a Phy write command GMIIRegisterData = ( Data << 16 ) | ( ( PHYAddress & 0x1f ) << 8 ) | GMST | GMCM | ( PHYRegister & 0x1f ); // Indicate a Phy write to the controller jtCsrOut32 ( pDevInstance, GMII_ACCESS_REG, GMIIRegisterData ); // Wait for ack from controller for (Milliseconds=0; Milliseconds < MII_TIMEOUT_MILLISECONDS ; Milliseconds++) { jtCsrIn32 ( pDevInstance, GMII_ACCESS_REG, &GMIIStatus ); if ( (GMIIStatus & GMST) == 0 ) { return JT_STATUS_SUCCESS; } // Wait a while longer udelay( 1*1000 ); // 1 millisecond } JT_DPRINTK( "jyPhyWriteRegister returns JT_STATUS_TIMEOUT\n"); return(JT_STATUS_TIMEOUT); } //---------------------------------------------------------------------------- // // FUNCTION: jtPhyReadRegister // // DESCRIPTION: Read a 16 bit value from the PHY register specified. // // PARAMETERS: // pDevInstance The pertinent controller instance. // PhyAddress The address of the Phy to affect. // PhyRegister The particular register to read from. // // RETURNS: // pData Updated with the value read from the register. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtPhyReadRegister ( PDEV_INSTANCE pDevInstance, UINT8 PHYAddress, UINT8 PHYRegister, PUINT16 pData ) { UINT32 GMIIStatus; UINT8 Milliseconds; UINT32 GMIIRegisterData; // Assemble a Phy read command GMIIRegisterData = ( ( PHYAddress & 0x1f ) << 8 ) | GMST | ( PHYRegister & 0x1f ); // Indicate a Phy read to the controller jtCsrOut32 ( pDevInstance, GMII_ACCESS_REG, GMIIRegisterData ); // Wait for ack for (Milliseconds=0; Milliseconds < MII_TIMEOUT_MILLISECONDS ; Milliseconds++) { jtCsrIn32 ( pDevInstance, GMII_ACCESS_REG, &GMIIStatus ); if ( (GMIIStatus & GMST) == 0 ) { *pData = (UINT16)(GMIIStatus>>16); return JT_STATUS_SUCCESS; } // Wait a while longer udelay( 1*1000 ); // 1 millisecond } JT_DPRINTK( "jyPhyReadRegister returns JT_STATUS_TIMEOUT\n"); return(JT_STATUS_TIMEOUT); } //---------------------------------------------------------------------------- // // FUNCTION: jtPhyReadStatusRegister // // DESCRIPTION: Read the value of the PHY's Status register. // Because of a bug/quirk in certain PHY's, the value is // read multiple times until two consecutive reads yield the // same value. // // PARAMETERS: // pDevInstance The pertinent controller instance. // PhyAddress The address of the Phy to affect. // // RETURNS: // pPhyStatus Updated with the value read from the status register. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtPhyReadStatusRegister ( PDEV_INSTANCE pDevInstance, UINT8 PhyAddress, PUINT16 pPhyStatus ) { JT_STATUS Result = JT_STATUS_SUCCESS; UINT8 Milliseconds; UINT16 PreviousPhyStatus = -1; // If the last two values read don't match, loop waiting for it to stabilize for( Milliseconds = 0; Milliseconds < MII_TIMEOUT_MILLISECONDS ; Milliseconds++) { // Read the status again Result = jtPhyReadRegister ( pDevInstance, PhyAddress, PHY_STATUS_REG, pPhyStatus ); if ( Result != JT_STATUS_SUCCESS ) { JT_DPRINTK( "jtPhyReadStatusRegister returns %d\n", Result); return Result; } // Has value stabilized? // JT_DPRINTK( "Phy Status = %10x, Previous Status = %10x\n", *pPhyStatus, PreviousPhyStatus ); if ( *pPhyStatus == PreviousPhyStatus ) { return JT_STATUS_SUCCESS; } // Save the last read status PreviousPhyStatus = *pPhyStatus; // Wait a while longer udelay( 1*1000 ); // 1 millisecond } JT_DPRINTK( "jtPhyReadStatusRegister returns JT_STATUS_TIMEOUT\n"); return(JT_STATUS_TIMEOUT); } /***************************************************************************** * RX Routines ****************************************************************************/ //---------------------------------------------------------------------------- // // FUNCTION: jtRxCreateQueues // // DESCRIPTION: Allocate the queues required for PDL and PDC receive modes. // // PARAMETERS: // pDevInstance The pertinent controller instance. // ElementCount The maximum number of elements each queue will hold. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtRxCreateQueues( PDEV_INSTANCE pDevInstance, int ElementCount ) { JT_STATUS Result1, Result2; JT_ASSERT( pDevInstance->RxIOMode != RX_IOMODE_PIO ); // Initialize the Available and InProgress queues Result1 = jtQueueCreate( &pDevInstance->RxPDxAvailableQueue, ElementCount ); Result2 = jtQueueCreate( &pDevInstance->RxPDxInProgressQueue, ElementCount ); if( Result1 != JT_STATUS_SUCCESS || Result2 != JT_STATUS_SUCCESS ) { return JT_STATUS_RESOURCES; } return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtRxPDCCreate // // DESCRIPTION: Allocate the PDC buffers required for PDC receive mode. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtRxPDCCreate(PDEV_INSTANCE pDevInstance) { JT_STATUS Result = JT_STATUS_SUCCESS; int PDCIndex; int MinPDCSize; #if !defined WORKAROUND_RECEIVE_BUFFERS struct device * pDev = pDevInstance->pDev; #endif JT_ASSERT( pDevInstance->RxIOMode == RX_IOMODE_PDC ); // Create the queues Result = jtRxCreateQueues( pDevInstance, pDevInstance->RxPDCCount ); if( Result != JT_STATUS_SUCCESS ) { return Result; } // Each PDC must be at least large enough to hold one Rx PDC header, // one full-size packet, and a trailing null header. #if defined (WORKAROUND_RECEIVE_BUFFERS) MinPDCSize = 64 * 1024; #else MinPDCSize = pDev->mtu + ETH_HLEN + 3 * sizeof(UINT32); #if defined(WORKAROUND_CRC_CHECK) if (pDevInstance->RxCrcCheck) MinPDCSize += ( 4*ETH_HLEN/60); #endif #endif jtSizeToOrder( MinPDCSize, &pDevInstance->RxPDCOrder, &pDevInstance->RxPDCLength ); // Interate over each available Rx PDC table entry. for( PDCIndex = 0; PDCIndex < pDevInstance->RxPDCCount; ++PDCIndex ) { RX_PDC * pPDC; // Allocate PDC buffer pPDC = (RX_PDC *) GET_FREE_PAGES( GFP_KERNEL, pDevInstance->RxPDCOrder ); // pPDC = (RX_PDC *) kmalloc( pDevInstance->RxPDCLength, GFP_KERNEL ); if( pPDC == NULL ) { return JT_STATUS_RESOURCES; } // Put buffer on the Available queue jtQueueEnqueue( &pDevInstance->RxPDxAvailableQueue, pPDC ); } return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtRxPDLCreate // // DESCRIPTION: Allocate the PDL buffers required for PDL receive mode. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtRxPDLCreate(PDEV_INSTANCE pDevInstance) { JT_STATUS Result = JT_STATUS_SUCCESS; int MinPDLSize; int ActualPDLSize = 0; int PDLIndex; JT_ASSERT( pDevInstance->RxIOMode == RX_IOMODE_PDL ); // Create the queues Result = jtRxCreateQueues( pDevInstance, pDevInstance->RxPDLCount ); if( Result != JT_STATUS_SUCCESS ) { return Result; } // Block must be at least large enough to hold all the PDLs // with cache-line alignment MinPDLSize = pDevInstance->RxPDLCount * pDevInstance->RxPDLAlignment; jtSizeToOrder( MinPDLSize, &pDevInstance->RxPDLOrder, &ActualPDLSize ); // First allocate a big contiguous block pDevInstance->pRxPDLBase = (void *) GET_FREE_PAGES( GFP_KERNEL, pDevInstance->RxPDLOrder ); if( pDevInstance->pRxPDLBase == NULL ) { return JT_STATUS_RESOURCES; } #if defined(EMULATION) pDevInstance->RxPDLBaseEmuAddress = virt_to_bus( pDevInstance->pRxPDLBase ); jtMapPhysicalAddress( pDevInstance->pRxPDLBase, &pDevInstance->RxPDLBaseEmuAddress, PAGE_SIZE ); #endif // Carve the block into individual PDL buffers and put them on the queue for( PDLIndex = 0; PDLIndex < pDevInstance->RxPDLCount; ++PDLIndex ) { // Each PDL buffer has room for exactly one fragment // plus 8 bytes free at the end void * pPDL = pDevInstance->pRxPDLBase + pDevInstance->RxPDLAlignment * PDLIndex; // Put buffer on the Available queue jtQueueEnqueue( &pDevInstance->RxPDxAvailableQueue, pPDL ); } return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtRxPDCInitialize // // DESCRIPTION: Initialize the PDC buffers required for PDC receive mode. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtRxPDCInitialize(PDEV_INSTANCE pDevInstance) { int PDCIndex; JT_ASSERT( pDevInstance->RxIOMode == RX_IOMODE_PDC ); // Initialize Rx PDC Table Index pDevInstance->RxPDCNextTableIndex = 0; // Interate over each PDC in the Rx Available queue. for( PDCIndex = 0; PDCIndex < pDevInstance->RxPDCCount; ++PDCIndex ) { RX_PDC * pPDC; UINT64 BusAddr; jtCsrOut32( pDevInstance, RX_PDC_BUF_ADR_TBL_IX, PDCIndex ); // Take PDC from the head the Available queue jtQueueDequeue( &pDevInstance->RxPDxAvailableQueue, (void**) &pPDC ); // Register the buffer with the controller BusAddr = virt_to_bus( pPDC ); #if defined(EMULATION) jtMapPhysicalAddress( pPDC, &BusAddr, pDevInstance->RxPDCLength); #endif // Order dependency! The high dword must be transferred first! jtCsrOut32( pDevInstance, RX_PDC_BUF_ADR_HI, (UINT32) (BusAddr >> 32) ); jtCsrOut32( pDevInstance, RX_PDC_BUF_ADR_LO, (UINT32) (BusAddr) ); // Put PDC back on the tail the Available queue jtQueueEnqueue( &pDevInstance->RxPDxAvailableQueue, pPDC ); } // Now send receive commands to controller jtRxPDCReplenish( pDevInstance ); return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtRxPDLInitialize // // DESCRIPTION: Initialize the PDL buffers required for PDL receive mode. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtRxPDLInitialize(PDEV_INSTANCE pDevInstance) { JT_ASSERT( pDevInstance->RxIOMode == RX_IOMODE_PDL ); // Now send receive commands to controller (void) jtRxPDLReplenish( pDevInstance ); return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtRxPDCReplenish // // DESCRIPTION: Send down PDC receive commands to the controller until // can take no more. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtRxPDCReplenish( PDEV_INSTANCE pDevInstance ) { JT_ASSERT( pDevInstance->RxIOMode == RX_IOMODE_PDC ); // Send receive commands to the controller until it's full while( !jtQueueIsEmpty( &pDevInstance->RxPDxAvailableQueue ) && (jtRxGetFreeCount( pDevInstance ) > 0) ) { void * pPDC = NULL; UINT32 RxPDCCommand; // Get buffer from the Available queue jtQueueDequeue( &pDevInstance->RxPDxAvailableQueue, (void**) &pPDC ); // Put buffer on the In Progress queue jtQueueEnqueue( &pDevInstance->RxPDxInProgressQueue, pPDC ); // Signal controller to use this buffer for receive RxPDCCommand = pDevInstance->RxPDCLength | ( pDevInstance->RxPDCNextTableIndex << RX_PDC_REG_BFID_SHIFT ) | RXINRQ ; #if !defined(WORKAROUND_WRITE_COMBINE) jtCsrOut32( pDevInstance, RX_PDC_REG, RxPDCCommand ); #else outl( RxPDCCommand, pDevInstance->pDev->base_addr + RX_PDC_REG ); #endif // Advance to next table entry pDevInstance->RxPDCNextTableIndex = (pDevInstance->RxPDCNextTableIndex + 1) % pDevInstance->RxPDCCount; // Update count jtRxDecrementFreeCount( pDevInstance ); } return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtRxPDLReplenish // // DESCRIPTION: Send down PDL receive commands to the controller until // can take no more. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtRxPDLReplenish( PDEV_INSTANCE pDevInstance ) { struct device * pDev = pDevInstance->pDev; JT_ASSERT( pDevInstance->RxIOMode == RX_IOMODE_PDL ); // Send receive commands to the controller until it's full while( !jtQueueIsEmpty( &pDevInstance->RxPDxAvailableQueue ) && (jtRxGetFreeCount( pDevInstance ) > 0) ) { PDL * pPDL = NULL; struct sk_buff * pSkBuff = NULL; UINT64 SkBuffBusAddr; UINT64 SkBuffVirtAddr; UINT64 PDLBusAddr; UINT32 PacketLength; // Set the packet length to the maximum frame size #if defined(WORKAROUND_RECEIVE_BUFFERS) PacketLength = 64 * 1024 - 2; #else PacketLength = pDev->mtu + ETH_HLEN; #if defined(WORKAROUND_CRC_CHECK) if (pDevInstance->RxCrcCheck) PacketLength += 4; #endif #endif // Allocate receive packet pSkBuff = dev_alloc_skb( PacketLength + 2 ); if( pSkBuff == NULL ) { JT_DPRINTK( "%s: Out of memory.\n", pDev->name ); // Exit without trying to construct any more receive commands. return JT_STATUS_RESOURCES; } // Align the IP header on a 16 byte boundary skb_reserve(pSkBuff, 2); // Get PDL buffer from the Available queue jtQueueDequeue( &pDevInstance->RxPDxAvailableQueue, (void**) &pPDL ); // Create header in PDL buffer pPDL->Header = PacketLength | (1 << RX_PDL_FGCN_SHIFT) // Fragment count | RXINRQ; // Point first PDL fragment to the skbuff SkBuffBusAddr = virt_to_bus( pSkBuff->data ); #if defined(EMULATION) jtMapPhysicalAddress( pSkBuff->data, &SkBuffBusAddr, PacketLength ); #endif pPDL->Fragment[0].FragmentAddressLow = (UINT32) (SkBuffBusAddr); pPDL->Fragment[0].FragmentAddressHigh = (UINT32) (SkBuffBusAddr >> 32); pPDL->Fragment[0].FragmentLength = PacketLength; // Save away the sk_buff address in second fragment SkBuffVirtAddr = (unsigned long) pSkBuff; pPDL->Fragment[1].FragmentAddressLow = (UINT32) (SkBuffVirtAddr); pPDL->Fragment[1].FragmentAddressHigh = (UINT32) (SkBuffVirtAddr >> 32); // Put buffer on the In Progress queue jtQueueEnqueue( &pDevInstance->RxPDxInProgressQueue, pPDL ); // Send receive command to the controller PDLBusAddr = virt_to_bus( pPDL ); #if defined(EMULATION) PDLBusAddr = pDevInstance->RxPDLBaseEmuAddress + ((void *) pPDL - (void *) pDevInstance->pRxPDLBase); #endif // Order dependency! The high dword must be transferred first! if ( (UINT32)(PDLBusAddr >> 32) ) jtCsrOut32( pDevInstance, RX_PDL_ADDR_REG_HI, (UINT32) (PDLBusAddr >> 32) ); jtCsrOut32( pDevInstance, RX_PDL_ADDR_REG_LO, (UINT32) (PDLBusAddr) ); // Update count jtRxDecrementFreeCount( pDevInstance ); } return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtRxStart // // DESCRIPTION: Direct the controller to begin receiving packets and // to raise interrupts for packets received. // //---------------------------------------------------------------------------- STATIC void jtRxStart( PDEV_INSTANCE pDevInstance) { // If in PIO receive mode if ( pDevInstance->RxIOMode == RX_IOMODE_PIO ) { // Enable interrupts for packet received jtCsrOut32( pDevInstance, INTR_MASK_REG, SERECL1 | RXMS ); } else // .. in Bus Master mode { jtCsrOut32( pDevInstance, RX_PDL_ADDR_REG_HI, 0 ); // Enable interrupts for PDC or PDL DMA complete jtCsrOut32( pDevInstance, INTR_MASK_REG, SERECL1 | RXPDMS ); } // Enable packet reception on the controller jtCsrOut32 ( pDevInstance, MODE_REG_1, SERECL1 | RXEN ); } //---------------------------------------------------------------------------- // // FUNCTION: jtRxValidatePacketLength // // DESCRIPTION: Verify that a receive packet has an acceptable length. // // PARAMETERS: // pDevInstance The pertinent controller instance. // PacketByteCount Length of the received packet. // //---------------------------------------------------------------------------- INLINE JT_STATUS jtRxValidatePacketLength( PDEV_INSTANCE pDevInstance, UINT32 PacketByteCount ) { struct device * pDev = pDevInstance->pDev; // Validate the length if( (PacketByteCount == 0) || (PacketByteCount > (pDev->mtu + ETH_HLEN)) ) { // Discard this packet pDevInstance->Statistics.rx_errors++; JT_DPRINTK( "Discarding %s packet %d\n", PacketByteCount ? "oversize" : "zero-length", PacketByteCount ); return JT_STATUS_FAILURE; } return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtRxValidatePacketIntegrity // // DESCRIPTION: Verify that a receive packet has no errors. // // PARAMETERS: // pDevInstance The pertinent controller instance. // PacketHeader1 The first dword of packet headers from the controller. // PacketHeader2 The second dword. // //---------------------------------------------------------------------------- INLINE JT_STATUS jtRxValidatePacketIntegrity( PDEV_INSTANCE pDevInstance, UINT32 PacketHeader1, UINT32 PacketHeader2 ) { // Check for errors in the packet #if defined WORKAROUND_ERROR_HEADERS if( (PacketHeader1 & RXER) != 0 || (PacketHeader2 & (IPCKER | IPHDPN)) == (IPCKER | IPHDPN) || (PacketHeader2 & (TPCKER | TPHDPN)) == (TPCKER | TPHDPN) || (PacketHeader2 & (UPCKER | UPHDPN)) == (UPCKER | UPHDPN) ) #else if( (PacketHeader1 & RXER) != 0 || (PacketHeader2 & (IPCKER | TPCKER | UPCKER)) ) #endif { // Discard this packet pDevInstance->Statistics.rx_errors++; JT_DPRINTK( "Discarding errored packet %X.\n", PacketHeader1); return JT_STATUS_FAILURE; } return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtRxHandOffPacket // // DESCRIPTION: Give a received packet to higher protocol layers. // // PARAMETERS: // pDevInstance The pertinent controller instance. // pSkBuff The received packet. // PacketHeader2 The second dword of the packet headers from the controller. // //---------------------------------------------------------------------------- INLINE void jtRxHandOffPacket( DEV_INSTANCE * pDevInstance, struct sk_buff * pSkBuff, UINT32 PacketHeader2 ) { struct device * pDev = pDevInstance->pDev; // Setup skbuff fields pSkBuff->dev = pDev; pSkBuff->protocol = eth_type_trans(pSkBuff, pDev); // Hardware checksumming for IP/TCP/UDP packets if( PacketHeader2 & IPHDPN ) { if ( (PacketHeader2 & TPHDPN) || (PacketHeader2 & UPHDPN) ) pSkBuff->ip_summed = CHECKSUM_UNNECESSARY; else pSkBuff->ip_summed = CHECKSUM_HW; } // Update statistics pDevInstance->Statistics.rx_packets++; #if LINUX_VERSION_CODE >= 0x20125 pDevInstance->Statistics.rx_bytes += pSkBuff->len; #endif // Hand off packet to protocol stack netif_rx(pSkBuff); } //---------------------------------------------------------------------------- // // FUNCTION: jtRxPDCReceive // // DESCRIPTION: Process packets received by the controller. // Reclaim buffers used by packets that have been received. // // PARAMETERS: // pDevInstance The pertinent controller instance. // ReceiveDoneCount The number of PDC receive commands that have completed. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtRxPDCReceive( PDEV_INSTANCE pDevInstance, UINT32 EventStatus ) { struct device * pDev = pDevInstance->pDev; UINT32 ReceiveDoneCount = 0; int ReceiveIndex; JT_ASSERT( pDevInstance->RxIOMode == RX_IOMODE_PDC ); // Update RXDMDNCN ReceiveDoneCount = (EventStatus & INTR_STATUS_RXDMDNCN) >> INTR_STATUS_RXDMDNCN_SHIFT; // Iterate over each completed receive for( ReceiveIndex = 0; ReceiveIndex < ReceiveDoneCount; ++ReceiveIndex ) { void * pPDC; RX_PDC * pPDCCurrPacket; // If no buffers are on the In Progress queue if( jtQueueIsEmpty( &pDevInstance->RxPDxInProgressQueue ) ) { // Serious error in our bookeeping printk( KERN_ERR "%s: Controller indicates nonexistent receives.\n", pDev->name ); #if defined(JT_DEBUG) BreakPoint(); #endif return JT_STATUS_FAILURE; } // Take the receive buffer off the In Progress queue jtQueueDequeue( &pDevInstance->RxPDxInProgressQueue, (void **) &pPDC ); // Iterate over each packet in the buffer pPDCCurrPacket = (RX_PDC *) pPDC; while( TRUE ) { UINT32 PacketHeader1 = 0; UINT32 PacketHeader2 = 0; UINT32 PacketByteCount; JT_STATUS Result; struct sk_buff * pSkBuff; void * pPacketData; // Check for NULL header PacketHeader1 = pPDCCurrPacket->Header; PacketHeader2 = pPDCCurrPacket->Header2; if( (PacketHeader1 & RX_PDC_HDTYPE) == 0 ) { break; } // Extract length of packet and round up to integral number of dwords PacketByteCount = (PacketHeader1 & PKT_HDR_LENGTH_MASK); #if defined(WORKAROUND_CRC_CHECK) if (pDevInstance->RxCrcCheck) { UINT32 SoftCrc, HardCrc; PacketByteCount -= 4; SoftCrc = crc32buf( (char *)pPDCCurrPacket->Data, PacketByteCount ); HardCrc = *(PUINT32)(((char *)pPDCCurrPacket->Data+PacketByteCount)); if( SoftCrc != HardCrc ) { //JT_DPRINTK("Mismatch: Hard CRC %x, Soft CRC %x\n", HardCrc, SoftCrc); printk(KERN_CRIT "Mismatch: Hard CRC %x, Soft CRC %x\n", HardCrc, SoftCrc); break; } } #endif // Validate the length Result = jtRxValidatePacketLength( pDevInstance, PacketByteCount ); if( Result != JT_STATUS_SUCCESS ) { break; } // Check for errors in the packet Result = jtRxValidatePacketIntegrity( pDevInstance, PacketHeader1, PacketHeader2 ); if( Result != JT_STATUS_SUCCESS ) { // Continue to next packet #if defined(WORKAROUND_CRC_CHECK) if (pDevInstance->RxCrcCheck) pPDCCurrPacket += (PacketByteCount + 4 - 1)/8 + 2; else #endif pPDCCurrPacket += (PacketByteCount - 1)/8 + 2; continue; } // Allocate buffer for packet pSkBuff = dev_alloc_skb( PacketByteCount + 2 ); if( pSkBuff == NULL ) { // Drop this packet pDevInstance->Statistics.rx_dropped++; printk( KERN_NOTICE "%s: Out of memory. Dropping packet.\n", pDev->name ); // Exit without trying to receive any more packets break; } // Align the IP header on a 16 byte boundary skb_reserve(pSkBuff, 2); // Copy packet data to skbuff pPacketData = skb_put( pSkBuff, PacketByteCount ); memcpy( pPacketData, (const void *) pPDCCurrPacket->Data, PacketByteCount ); // Give packet to next layer jtRxHandOffPacket( pDevInstance, pSkBuff, PacketHeader2 ); pSkBuff = NULL; // Advance to next packet #if defined(WORKAROUND_CRC_CHECK) if (pDevInstance->RxCrcCheck) pPDCCurrPacket += (PacketByteCount + 4 - 1)/8 + 2; else #endif pPDCCurrPacket += (PacketByteCount - 1)/8 + 2; } // Put the receive buffer on the Available queue jtQueueEnqueue( &pDevInstance->RxPDxAvailableQueue, pPDC ); } jtRxPDCReplenish( pDevInstance ); return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtRxPDLReceive // // DESCRIPTION: Process packets received by the controller. // Reclaim buffers used by packets that have been received. // // PARAMETERS: // pDevInstance The pertinent controller instance. // ReceiveDoneCount The number of PDL receive commands that have completed. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtRxPDLReceive( PDEV_INSTANCE pDevInstance, UINT32 EventStatus ) { struct device * pDev = pDevInstance->pDev; UINT32 ReceiveDoneCount = 0; int ReceiveIndex; JT_ASSERT( pDevInstance->RxIOMode == RX_IOMODE_PDL ); // Update RXDMDNCN ReceiveDoneCount = (EventStatus & INTR_STATUS_RXDMDNCN) >> INTR_STATUS_RXDMDNCN_SHIFT; // Iterate over each completed receive for( ReceiveIndex = 0; ReceiveIndex < ReceiveDoneCount; ++ReceiveIndex ) { PDL * pPDL = NULL; struct sk_buff * pSkBuff = NULL; UINT64 SkBuffVirtAddr; UINT32 PacketHeader1 = 0; UINT32 PacketHeader2 = 0; UINT32 PacketByteCount; JT_STATUS Result; // If no buffers are on the In Progress queue if( jtQueueIsEmpty( &pDevInstance->RxPDxInProgressQueue ) ) { // Serious error in our bookeeping printk( KERN_ERR "%s: Controller indicates nonexistent receives.\n", pDev->name ); return JT_STATUS_FAILURE; } // Take the receive buffer off the In Progress queue jtQueueDequeue( &pDevInstance->RxPDxInProgressQueue, (void **) &pPDL ); //pDevInstance->RxPDxCommandFreeCount ++; // Get the sk_buff packet SkBuffVirtAddr = (((UINT64) pPDL->Fragment[1].FragmentAddressHigh) << 32) | pPDL->Fragment[1].FragmentAddressLow; pSkBuff = (void *) (unsigned long) SkBuffVirtAddr; #if defined(EMULATION) // Release memory handle for skbuff emuUnMapPhysicalAddress( jtEmuHandle, &pPDL->Fragment[0].FragmentAddressLow, &pPDL->Fragment[0].FragmentAddressHigh ); #endif #if defined(JT_DEBUG) pPDL->Fragment[1].FragmentAddressLow = 0; pPDL->Fragment[1].FragmentAddressHigh = 0; #endif // Get packet headers PacketHeader1 = pPDL->Header; PacketHeader2 = pPDL->Header2; // Extract length of packet and round up PacketByteCount = (PacketHeader1 & PKT_HDR_LENGTH_MASK); #if defined(WORKAROUND_CRC_CHECK) if (pDevInstance->RxCrcCheck) { UINT32 SoftCrc, HardCrc; UINT32 Offset; PacketByteCount -= 4; SoftCrc = crc32buf( pSkBuff->data, PacketByteCount ); HardCrc = (UINT32)*(PUINT32)((pSkBuff->data+PacketByteCount)); //if ( HardCrc == (UINT32)*(PUINT32)(pSkBuff->data+PacketByteCount-8) ) if( SoftCrc != HardCrc ) { SoftCrc = crc32buf( pSkBuff->data, PacketByteCount ); //JT_DPRINTK("Mismatch: Hard CRC %x, Soft CRC %x\n", HardCrc, SoftCrc); printk(KERN_CRIT "Mismatch: Hard CRC %x, Soft CRC %x\n", HardCrc, SoftCrc); switch ( (PacketByteCount+6) % 8 ) { case 0: Offset=6; break; case 7: Offset=5; break; case 6: Offset=4; break; case 5: Offset=3; break; case 4: Offset=2; break; default: Offset=1; break; } //Offset = min(6, (PacketByteCount+6) % 8 ); printk(KERN_CRIT "Offset %d, Packet %d\n", Offset, PacketByteCount); if ( !memcmp( pSkBuff->data+PacketByteCount+4-Offset-8, pSkBuff->data+PacketByteCount+4-Offset, Offset ) ) printk(KERN_CRIT "Got it!\n"); else jtCsrOut32( pDevInstance, CSR57, (UINT32)pSkBuff->data ); DEV_KFREE_SKB( pSkBuff, FREE_WRITE ); // Put the PDL back on the available queue jtQueueEnqueue( &pDevInstance->RxPDxAvailableQueue, pPDL ); continue; } } #endif // Validate the length Result = jtRxValidatePacketLength( pDevInstance, PacketByteCount ); if( Result != JT_STATUS_SUCCESS ) { DEV_KFREE_SKB( pSkBuff, FREE_WRITE ); // Put the PDL back on the available queue jtQueueEnqueue( &pDevInstance->RxPDxAvailableQueue, pPDL ); JT_DPRINTK("Packet has wrong length!\n"); continue; } // Check for errors in the packet Result = jtRxValidatePacketIntegrity( pDevInstance, PacketHeader1, PacketHeader2 ); if( Result != JT_STATUS_SUCCESS ) { DEV_KFREE_SKB( pSkBuff, FREE_WRITE ); // Put the PDL back on the available queue jtQueueEnqueue( &pDevInstance->RxPDxAvailableQueue, pPDL ); JT_DPRINTK("Packet has errors!\n"); continue; } // Setup skbuff fields skb_put( pSkBuff, PacketByteCount ); // Give packet to next layer jtRxHandOffPacket( pDevInstance, pSkBuff, PacketHeader2 ); pSkBuff = NULL; // Put the buffer back on the available queue jtQueueEnqueue( &pDevInstance->RxPDxAvailableQueue, pPDL ); } (void) jtRxPDLReplenish( pDevInstance ); return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtRxPIOReceive // // DESCRIPTION: Obtain a received packet from the controller using // the PIO mechanism. // This function expects to be called from the interrupt handler. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtRxPIOReceive( DEV_INSTANCE * pDevInstance, UINT32 EventStatus ) { struct device * pDev = pDevInstance->pDev; UINT16 PacketAvailCount = 0; UINT16 PacketDoneCount = 0; // Any packets received? if( !(EventStatus & RXIN) ) { return JT_STATUS_SUCCESS; } // Read the number of receive packets available jtCsrIn16( pDevInstance, RX_FIFO_PKT_CNT_REG, &PacketAvailCount ); // Iterate over each available packet for( ; PacketAvailCount > 0; --PacketAvailCount, ++PacketDoneCount ) { UINT32 PacketHeader1 = 0; UINT32 PacketHeader2 = 0; UINT32 PacketByteCount; UINT32 PacketFullDwordCount; JT_STATUS Result; struct sk_buff * pSkBuff; void * pPacketData; // Read the 8 bytes of packet headers jtCsrIn32( pDevInstance, RX_FIFO_READ_REG, &PacketHeader1 ); jtCsrIn32( pDevInstance, RX_FIFO_READ_REG, &PacketHeader2 ); // Extract length of packet and number of complete dwords PacketByteCount = (PacketHeader1 & PKT_HDR_LENGTH_MASK); PacketFullDwordCount = PacketByteCount / 4; // Validate the length Result = jtRxValidatePacketLength( pDevInstance, PacketByteCount ); if( Result != JT_STATUS_SUCCESS ) { // Discard this packet jtCsrOut32( pDevInstance, COMMAND_REG, SERECL0 | RXFISKPK ); continue; } // Check for errors in the packet Result = jtRxValidatePacketIntegrity( pDevInstance, PacketHeader1, PacketHeader2 ); if( Result != JT_STATUS_SUCCESS ) { // Discard this packet jtCsrOut32( pDevInstance, COMMAND_REG, SERECL0 | RXFISKPK ); continue; } // Allocate buffer for packet pSkBuff = dev_alloc_skb( PacketByteCount + 2 ); if( pSkBuff == NULL ) { // Drop this packet jtCsrOut32( pDevInstance, COMMAND_REG, SERECL0 | RXFISKPK ); pDevInstance->Statistics.rx_dropped++; printk( KERN_NOTICE "%s: Out of memory. Dropping packet.\n", pDev->name ); // Exit without trying to receive any more packets return JT_STATUS_RESOURCES; } // Align the IP header on a 16 byte boundary skb_reserve(pSkBuff, 2); // Read each dword of packet data pPacketData = skb_put( pSkBuff, PacketByteCount ); jtCsrIn32ToBuffer( pDevInstance, RX_FIFO_READ_REG, pPacketData, PacketFullDwordCount ); pPacketData += PacketFullDwordCount * sizeof(UINT32); // Receive remaining halfword, if any if( (PacketByteCount & 2) == 2 ) { jtCsrIn16( pDevInstance, RX_FIFO_READ_REG, (UINT16 *) pPacketData ); pPacketData += sizeof(UINT16); } // Receive remaining byte, if any if( (PacketByteCount & 1) == 1 ) { jtCsrIn8( pDevInstance, RX_FIFO_READ_REG, (UINT8 *) pPacketData ); pPacketData += sizeof(UINT8); } // Give packet to next layer jtRxHandOffPacket( pDevInstance, pSkBuff, PacketHeader2 ); pSkBuff = NULL; // Notify controller to advance to next packet jtCsrOut32( pDevInstance, COMMAND_REG, SERECL0 | RXFISKPK ); } return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtRxStop // // DESCRIPTION: Direct the controller to cease receiving packets and // to not raise interrupts for packets received. // //---------------------------------------------------------------------------- STATIC void jtRxStop( PDEV_INSTANCE pDevInstance ) { // Disable packet reception on the controller jtCsrOut32( pDevInstance, MODE_REG_1, RXEN ); // Disable interrupts for any receive events jtCsrOut32( pDevInstance, INTR_MASK_REG, RXCMEMMS | RXFIWMMS | RXMS | RXPDMS ); } //---------------------------------------------------------------------------- // // FUNCTION: jtRxPDCFlush // // DESCRIPTION: Flush all PDCs currently in progress. // jtRxStop must have been called before this function. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtRxPDCFlush( PDEV_INSTANCE pDevInstance ) { // Drain the InProgress queue while( !jtQueueIsEmpty( &pDevInstance->RxPDxInProgressQueue ) ) { void * pPDC = NULL; jtQueueDequeue( &pDevInstance->RxPDxInProgressQueue, &pPDC ); jtQueueEnqueue( &pDevInstance->RxPDxAvailableQueue, pPDC ); } return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtRxPDLFlush // // DESCRIPTION: Flush all PDLs currently in progress. // jtRxStop must have been called before this function. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtRxPDLFlush( PDEV_INSTANCE pDevInstance ) { // Drain the InProgress queue while( !jtQueueIsEmpty( &pDevInstance->RxPDxInProgressQueue ) ) { PDL * pPDL = NULL; UINT64 SkBuffVirtAddr; struct sk_buff * pSkBuff = NULL; // Get the PDL off the InProgress queue jtQueueDequeue( &pDevInstance->RxPDxInProgressQueue, (void**) &pPDL ); // Get the sk_buff packet SkBuffVirtAddr = (((UINT64) pPDL->Fragment[1].FragmentAddressHigh) << 32) | pPDL->Fragment[1].FragmentAddressLow; pSkBuff = (void *) (unsigned long) SkBuffVirtAddr; #if defined(EMULATION) // Release memory handle for skbuff emuUnMapPhysicalAddress( jtEmuHandle, &pPDL->Fragment[0].FragmentAddressLow, &pPDL->Fragment[0].FragmentAddressHigh ); #endif #if defined(JT_DEBUG) pPDL->Fragment[1].FragmentAddressLow = 0; pPDL->Fragment[1].FragmentAddressHigh = 0; #endif // Send PDL back to available queue jtQueueEnqueue( &pDevInstance->RxPDxAvailableQueue, pPDL ); } return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtRxDestroy // // DESCRIPTION: Deallocate all queues and buffers required for PDC // receive mode. // jtRxStop must have been called before this function. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtRxPDCDestroy( PDEV_INSTANCE pDevInstance ) { JT_ASSERT( pDevInstance->RxIOMode == RX_IOMODE_PDC ); // Interate over each used Rx PDC table entry. while( ! jtQueueIsEmpty( &pDevInstance->RxPDxAvailableQueue ) ) { RX_PDC * pPDC; // Dequeue buffer on the Available queue jtQueueDequeue( &pDevInstance->RxPDxAvailableQueue, (void **) &pPDC ); #if defined(EMULATION) // Release the emulator memory handle emuUnMapVirtualAddress( jtEmuHandle, pPDC ); #endif // Deallocate PDC buffer free_pages( (unsigned long) pPDC, pDevInstance->RxPDCOrder ); } // Deallocate Rx queues jtQueueDestroy( &pDevInstance->RxPDxAvailableQueue ); jtQueueDestroy( &pDevInstance->RxPDxInProgressQueue ); return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtRxPDLDestroy // // DESCRIPTION: Deallocate all queues and buffers required for busmaster // receive modes. // jtRxStop must have been called before this function. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtRxPDLDestroy( PDEV_INSTANCE pDevInstance ) { if( pDevInstance->pRxPDLBase != NULL ) { #if defined(EMULATION) // Release the emulator handle for the PDL buffers block emuUnMapVirtualAddress( jtEmuHandle, pDevInstance->pRxPDLBase ); #endif // Deallocate PDL buffers block free_pages( (unsigned long) pDevInstance->pRxPDLBase, pDevInstance->RxPDLOrder ); } // Deallocate Rx queues jtQueueDestroy( &pDevInstance->RxPDxAvailableQueue ); jtQueueDestroy( &pDevInstance->RxPDxInProgressQueue ); return JT_STATUS_SUCCESS; } /***************************************************************************** * TX Routines ****************************************************************************/ //---------------------------------------------------------------------------- // // FUNCTION: jtTxPDxCreate // // DESCRIPTION: Allocate and initialize all queues and buffers required // for busmaster transmit modes. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtTxPDxCreate(PDEV_INSTANCE pDevInstance) { JT_STATUS Result, Result1, Result2, Result3; JT_ASSERT( pDevInstance->TxIOMode == TX_IOMODE_BUSMASTER ); // Initialize Tx queues Result1 = jtQueueCreate( &pDevInstance->TxPDCAvailableQueue, pDevInstance->TxPDCCount ); Result2 = jtQueueCreate( &pDevInstance->TxPDLAvailableQueue, pDevInstance->TxPDLCount ); Result3 = jtQueueCreate( &pDevInstance->TxPDxInProgressQueue, pDevInstance->TxPDCCount + pDevInstance->TxPDLCount ); if( Result1 != JT_STATUS_SUCCESS || Result2 != JT_STATUS_SUCCESS || Result3 != JT_STATUS_SUCCESS ) { return JT_STATUS_RESOURCES; } // Create PDC buffers Result = jtTxPDCCreate( pDevInstance ); if( Result != JT_STATUS_SUCCESS ) { return Result; } // Create PDL buffers Result = jtTxPDLCreate( pDevInstance ); if( Result != JT_STATUS_SUCCESS ) { return Result; } #if defined(PERFORMANCE_TX) atomic_set ( &pDevInstance->TxPDLPending, 0 ); #endif return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtTxPDCCreate // // DESCRIPTION: Allocate and initialize all queues and buffers required // for PDC transmit. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtTxPDCCreate(PDEV_INSTANCE pDevInstance) { struct device * pDev = pDevInstance->pDev; int MinPDCSize; int PDCIndex; JT_ASSERT( pDevInstance->TxIOMode == TX_IOMODE_BUSMASTER ); // PDC must be at least large enough to hold one Tx PDC header // and one full-size packet. MinPDCSize = pDev->mtu + ETH_HLEN + sizeof(UINT32); jtSizeToOrder( MinPDCSize, &pDevInstance->TxPDCOrder, &pDevInstance->TxPDCLength ); // Interate over each Tx PDC buffer to create. for( PDCIndex = 0; PDCIndex < pDevInstance->TxPDCCount; ++PDCIndex ) { TX_PDC * pPDC; // Allocate PDC buffer pPDC = (TX_PDC *) GET_FREE_PAGES( GFP_KERNEL, pDevInstance->TxPDCOrder ); // pPDC = (TX_PDC *) kmalloc( pDevInstance->TxPDCLength, GFP_KERNEL ); if( pPDC == NULL ) { return JT_STATUS_RESOURCES; } // Put buffer on the Available queue jtQueueEnqueue( &pDevInstance->TxPDCAvailableQueue, pPDC ); } return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtTxPDLCreate // // DESCRIPTION: Allocate and initialize all queues and buffers required // for PDL transmit. // //---------------------------------------------------------------------------- JT_STATUS jtTxPDLCreate( DEV_INSTANCE * pDevInstance ) { int MinPDLSize; int ActualPDLSize = 0; int PDLIndex; JT_ASSERT( pDevInstance->TxIOMode == TX_IOMODE_BUSMASTER ); // Block must be at least large enough to hold all the PDLs // with cache-line alignment MinPDLSize = pDevInstance->TxPDLCount * pDevInstance->TxPDLAlignment; jtSizeToOrder( MinPDLSize, &pDevInstance->TxPDLOrder, &ActualPDLSize ); // First allocate a big contiguous block pDevInstance->pTxPDLBase = (void *) GET_FREE_PAGES( GFP_KERNEL, pDevInstance->TxPDLOrder ); if( pDevInstance->pTxPDLBase == NULL ) { return JT_STATUS_RESOURCES; } #if defined(EMULATION) pDevInstance->TxPDLBaseEmuAddress = virt_to_bus( pDevInstance->pTxPDLBase ); jtMapPhysicalAddress( pDevInstance->pTxPDLBase, &pDevInstance->TxPDLBaseEmuAddress, PAGE_SIZE ); #endif // Carve the block into individual PDL buffers and put them on the queue for( PDLIndex = 0; PDLIndex < pDevInstance->TxPDLCount; ++PDLIndex ) { // Each PDL buffer has room for exactly one fragment // plus 8 bytes free at the end void * pPDL = pDevInstance->pTxPDLBase + pDevInstance->TxPDLAlignment * PDLIndex; // Put buffer on the Available queue jtQueueEnqueue( &pDevInstance->TxPDLAvailableQueue, pPDL ); } return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtTxPDxInitialize // // DESCRIPTION: Initialize all buffers required // for busmaster transmit modes. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtTxPDxInitialize(PDEV_INSTANCE pDevInstance) { JT_ASSERT( pDevInstance->TxIOMode == TX_IOMODE_BUSMASTER ); // Initialize PDC buffers (void) jtTxPDCInitialize( pDevInstance ); // Initialize PDL buffers (void) jtTxPDLInitialize( pDevInstance ); return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtTxPDCInitialize // // DESCRIPTION: Allocate and initialize all queues and buffers required // for PDC transmit. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtTxPDCInitialize(PDEV_INSTANCE pDevInstance) { int PDCIndex; JT_ASSERT( pDevInstance->TxIOMode == TX_IOMODE_BUSMASTER ); // Initialize Tx PDC Table Index pDevInstance->TxPDCNextTableIndex = 0; // Interate over each Tx PDC buffer to create. for( PDCIndex = 0; PDCIndex < pDevInstance->TxPDCCount; ++PDCIndex ) { TX_PDC * pPDC; UINT64 BusAddr; jtCsrOut32( pDevInstance, TX_PDC_BUF_ADR_TBL_IX, PDCIndex ); // Take PDC from the Available queue jtQueueDequeue( &pDevInstance->TxPDCAvailableQueue, (void **) &pPDC ); // Register the buffer with the controller BusAddr = virt_to_bus( pPDC ); #if defined(EMULATION) jtMapPhysicalAddress( pPDC, &BusAddr, pDevInstance->TxPDCLength ); #endif // Order dependency! The high dword must be transferred first! jtCsrOut32( pDevInstance, TX_PDC_BUF_ADR_HI, (UINT32) (BusAddr >> 32) ); jtCsrOut32( pDevInstance, TX_PDC_BUF_ADR_LO, (UINT32) (BusAddr) ); // Put PDC back on tail of Available queue jtQueueEnqueue( &pDevInstance->TxPDCAvailableQueue, pPDC ); } return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtTxPDLInitialize // // DESCRIPTION: Allocate and initialize all queues and buffers required // for PDL transmit. // //---------------------------------------------------------------------------- JT_STATUS jtTxPDLInitialize( DEV_INSTANCE * pDevInstance ) { JT_ASSERT( pDevInstance->TxIOMode == TX_IOMODE_BUSMASTER ); return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtTxStart // // DESCRIPTION: Direct the controller to begin transmitting packets and // to raise interrupts for PDC/PDL packets transferred to // the controller. // //---------------------------------------------------------------------------- STATIC void jtTxStart(PDEV_INSTANCE pDevInstance) { jtCsrOut32( pDevInstance, TX_PDL_ADDR_REG_HI, 0 ); // Enable interrupts for PDC or PDL DMA complete jtCsrOut32( pDevInstance, INTR_MASK_REG, SERECL0 | TXDMDNMS | TMEXMS ); // Enable packet transmission on the controller jtCsrOut32 ( pDevInstance, MODE_REG_1, SERECL1 | TXEN ); #if defined(PERFORMANCE_TX) pDevInstance->TxPDLTimerOn = 0; jtCsrOut32( pDevInstance, INTR_PERIOD_REG, 24000 ); #endif } //---------------------------------------------------------------------------- // // FUNCTION: jtTxPDxTransmit // // DESCRIPTION: Give the controller a packet to transmit, // using the PDC/PDL mechanism. // // PARAMETERS: // pDevInstance The pertinent controller instance. // pSkBuff The packet to transmit. // //---------------------------------------------------------------------------- STATIC int jtTxPDxTransmit( PDEV_INSTANCE pDevInstance, struct sk_buff * pSkBuff ) { void * pPartialPDC; UINT32 PartialPDCLength; struct sk_buff * pCurrentSkBuff, * pNextSkBuff = NULL; int Done; #if defined(PERFORMANCE_TX) && defined(SERIALIZE_TX) unsigned long Flags; #endif #if ( (LINUX_VERSION_CODE < 0x20100) && MULTIPLE_PACKETS) struct device * pDev = pDevInstance->pDev; struct sk_buff_head * pSkBuffQueue = pDev->buffs; #endif // Hold partially filled PDC pPartialPDC = NULL; PartialPDCLength = 0; // While there remain packets in the device queue pCurrentSkBuff = pSkBuff; Done = FALSE; while( (!Done) && (!test_bit(0, (void*) &pDevInstance->TxFull)) ) { #if ( (LINUX_VERSION_CODE < 0x20100) && MULTIPLE_PACKETS) // Want to stuff multiple packets into a single PDC, if possible // Scan the device packet queues until we find a packet while( skb_queue_empty( pSkBuffQueue ) ) { // Look in the next queue pSkBuffQueue++; // If there are no more queues if( pSkBuffQueue == &pDev->buffs[DEV_NUMBUFFS] ) { // No more packets Done = TRUE; break; } } // Get the next packet to process if( ! Done ) { pNextSkBuff = skb_dequeue( pSkBuffQueue ); JT_DPRINTK("Taking packet from device queue.\n"); } #else Done = TRUE; #endif // If a partially filled PDC exists and we're done with it if( (pPartialPDC != NULL) && ( Done || (pCurrentSkBuff->len >= pDevInstance->TxPDxThreshold) || (pCurrentSkBuff->len > pDevInstance->TxPDCLength - PartialPDCLength - 8) ) ) { // Send it jtTxPDCSend( pDevInstance, pPartialPDC, PartialPDCLength ); pPartialPDC = NULL; PartialPDCLength = 0; } // If this packet is small enough to transfer using PDC mode if( pCurrentSkBuff->len < pDevInstance->TxPDxThreshold ) { // If no partially filled PDC buffer exists if( pPartialPDC == NULL ) { // Start a new one jtTxPDCGetNextAvailable( pDevInstance, &pPartialPDC ); PartialPDCLength = 0; } // Append packet into current PDC buffer jtTxPDCAppendPacket( pDevInstance, pPartialPDC, &PartialPDCLength, pCurrentSkBuff ); } else { // Transmit packet using PDL jtTxPDLTransmit( pDevInstance, pCurrentSkBuff ); } // Done with this packet pCurrentSkBuff = pNextSkBuff; } // end while // If a partially filled PDC exists if( (pPartialPDC != NULL) ) { // Send it jtTxPDCSend( pDevInstance, pPartialPDC, PartialPDCLength ); pPartialPDC = NULL; PartialPDCLength = 0; } JT_ASSERT( pPartialPDC == NULL ); #if defined(PERFORMANCE_TX) // Look for outstanding an exinting timer #if defined(SERIALIZE_TX) spin_lock_irqsave(&pDevInstance->DriverLock, Flags); #endif if( !test_bit(0, (void*) &pDevInstance->TxPDLTimerOn) ) { // Is there outstanding stuff if( atomic_read( &pDevInstance->TxPDLPending ) || test_bit(0, (void*) &pDevInstance->TxFull) ) { // We need to fire a timer jtCsrOut32( pDevInstance, COMMAND_REG, SERECL0 | DLINRQ ); set_bit(0, (void*) &pDevInstance->TxPDLTimerOn); } } #if defined(SERIALIZE_TX) spin_unlock_irqrestore(&pDevInstance->DriverLock, Flags); #endif #endif return 0; } //---------------------------------------------------------------------------- // // FUNCTION: jtTxPDGetNextAvailable // // DESCRIPTION: Take the next available Tx PDC buffer from the queue, // and check for queue exhaustion. // // PARAMETERS: // pDevInstance The pertinent controller instance. // RETURNS: // ppPDC The buffer taken off the available queue. // //---------------------------------------------------------------------------- STATIC void jtTxPDCGetNextAvailable( DEV_INSTANCE * pDevInstance, void ** ppPDC ) { // Take a PDC buffer from the available queue jtQueueDequeue( &pDevInstance->TxPDCAvailableQueue, ppPDC ); // If the available queue is now exhausted if( jtQueueIsEmpty( &pDevInstance->TxPDCAvailableQueue ) ) { // Signal that the transmitter is busy set_bit( 0, (void *) &pDevInstance->TxFull ); JT_DPRINTK("Out of PDCs\n"); //jtCsrOut32( pDevInstance, CSR56, 1 ); } } //---------------------------------------------------------------------------- // // FUNCTION: jtTxPDCAppendPacket // // DESCRIPTION: Append a packet to a PDC buffer. The buffer is assumed to // be large enough to fit in the PDC. // // PARAMETERS: // pDevInstance The pertinent controller instance. // pPDC The buffer to append to. // PDCLength Number of bytes currently used in the PDC buffer. // pSkBuff The packet to append // RETURNS: // PDCLength Number of bytes used, including newly appended packet. // //---------------------------------------------------------------------------- STATIC void jtTxPDCAppendPacket( DEV_INSTANCE * pDevInstance, void * pPDC, UINT32 * pPDCLength, struct sk_buff * pSkBuff ) { TX_PDC * pPDCCurrPacket; // Round packet length up to next 8 byte boundary *pPDCLength = (*pPDCLength + 7) & ~7U; // Create packet header in PDC buffer pPDCCurrPacket = (TX_PDC *)(pPDC + *pPDCLength); pPDCCurrPacket->Header = pSkBuff->len | ( 0x20000 ) | // Make this a #define pDevInstance->TxPacketHeader ; *pPDCLength += sizeof(UINT32); // Copy packet into current PDC buffer memcpy( pPDCCurrPacket->Data, (const void *) pSkBuff->data, pSkBuff->len ); *pPDCLength += pSkBuff->len; // Increment stats #if defined(EMULATION) pDevInstance->Statistics.tx_packets++; #endif #if LINUX_VERSION_CODE >= 0x20125 pDevInstance->Statistics.tx_bytes += pSkBuff->len; #endif // Release packet DEV_KFREE_SKB( pSkBuff, FREE_WRITE ); } //---------------------------------------------------------------------------- // // FUNCTION: jtTxPDCSend // // DESCRIPTION: Signal the controller to transmit a PDC buffer. // The PDC buffer is assumed to be fully constructed, with // one or more packets. // // PARAMETERS: // pDevInstance The pertinent controller instance. // pPDC The buffer to transmit. // PDCLength Number of bytes used in the PDC buffer. // //---------------------------------------------------------------------------- STATIC void jtTxPDCSend( DEV_INSTANCE * pDevInstance, TX_PDC * pPDC, UINT32 PDCLength ) { UINT32 TxPDCCommand; JT_ASSERT( pPDC != NULL ); JT_ASSERT( PDCLength > 0 ); // Initiate transmit of existing PDC // Put PDC buffer on the In Progress queue jtQueueEnqueue( &pDevInstance->TxPDxInProgressQueue, pPDC ); // Decrement count of PDx commands available jtTxDecrementFreeCount( pDevInstance ); // Signal controller to begin transmit #if defined(STANDARD_TX) TxPDCCommand = PDCLength | ( pDevInstance->TxPDCNextTableIndex << TX_PDC_REG_BFID_SHIFT ) | XFDNINRQ ; #else TxPDCCommand = PDCLength | ( pDevInstance->TxPDCNextTableIndex << TX_PDC_REG_BFID_SHIFT ); #endif //#if !defined(WORKAROUND_WRITE_COMBINE) jtCsrOut32( pDevInstance, TX_PDC_REG, TxPDCCommand ); //#else // outl( TxPDCCommand, pDevInstance->pDev->base_addr + TX_PDC_REG ); //#endif // Advance to next table entry pDevInstance->TxPDCNextTableIndex = (pDevInstance->TxPDCNextTableIndex + 1) % pDevInstance->TxPDCCount; } //---------------------------------------------------------------------------- // // FUNCTION: jtTxPDLTransmit // // DESCRIPTION: Give the controller a packet to transmit, // using the PDL mechanism. // // PARAMETERS: // pDevInstance The pertinent controller instance. // pSkBuff The packet to transmit. // //---------------------------------------------------------------------------- STATIC void jtTxPDLTransmit( DEV_INSTANCE * pDevInstance, struct sk_buff * pSkBuff ) { PDL * pPDL = NULL; void * pPDx = NULL; UINT64 SkBuffBusAddr; UINT64 SkBuffVirtAddr; UINT64 PDLBusAddr; // Take PDL buffer off the Available queue jtQueueDequeue( &pDevInstance->TxPDLAvailableQueue, (void**) &pPDL ); // If PDL available queue is now exhausted if( jtQueueIsEmpty( &pDevInstance->TxPDLAvailableQueue ) ) { // Signal that the transmitter is busy JT_DPRINTK("Out of PDLs\n"); set_bit( 0, (void *) &pDevInstance->TxFull ); } // Create packet header in PDL buffer #if defined(STANDARD_TX) pPDL->Header = pSkBuff->len | ( 1 << TX_PDL_FGCN_SHIFT ) // Fragment count | pDevInstance->TxPacketHeader | DMDNINRQ; #elif defined(PERFORMANCE_TX) pPDL->Header = pSkBuff->len | ( 1 << TX_PDL_FGCN_SHIFT ) // Fragment count | pDevInstance->TxPacketHeader; #endif // Point PDL fragment 1 to the skbuff SkBuffBusAddr = virt_to_bus( pSkBuff->data ); #if defined(EMULATION) jtMapPhysicalAddress( pSkBuff->data, &SkBuffBusAddr, pSkBuff->len ); #endif pPDL->Fragment[0].FragmentAddressLow = (UINT32) (SkBuffBusAddr); pPDL->Fragment[0].FragmentAddressHigh = (UINT32) (SkBuffBusAddr >> 32); pPDL->Fragment[0].FragmentLength = pSkBuff->len; // Save away the sk_buff address SkBuffVirtAddr = (unsigned long) pSkBuff; pPDL->Fragment[1].FragmentAddressLow = (UINT32) (SkBuffVirtAddr); pPDL->Fragment[1].FragmentAddressHigh = (UINT32) (SkBuffVirtAddr >> 32); // Mark as PDL type and put buffer on the In Progress queue pPDx = (void *) ((unsigned long) pPDL | PDL_FLAG); jtQueueEnqueue( &pDevInstance->TxPDxInProgressQueue, pPDx ); // Decrement count of PDx commands available jtTxDecrementFreeCount( pDevInstance ); // Signal controller to begin transmit PDLBusAddr = virt_to_bus( pPDL ); #if defined(EMULATION) PDLBusAddr = pDevInstance->TxPDLBaseEmuAddress + ((void *) pPDL - (void *) pDevInstance->pTxPDLBase); #endif // Order dependency! The high dword must be transferred first! if ( (UINT32)(PDLBusAddr >> 32) ) jtCsrOut32( pDevInstance, TX_PDL_ADDR_REG_HI, (UINT32) (PDLBusAddr >> 32) ); jtCsrOut32( pDevInstance, TX_PDL_ADDR_REG_LO, (UINT32) (PDLBusAddr) ); #if defined(PERFORMANCE_TX) atomic_inc ( &pDevInstance->TxPDLPending ); #endif // Update statistics #if defined(EMULATION) pDevInstance->Statistics.tx_packets++; #endif #if LINUX_VERSION_CODE >= 0x20125 pDevInstance->Statistics.tx_bytes += pSkBuff->len; #endif } //---------------------------------------------------------------------------- // // FUNCTION: jtTxPIOTransmit // // DESCRIPTION: Give the controller a packet to transmit, // using the PIO mechanism. // // PARAMETERS: // pDevInstance The pertinent controller instance. // pSkBuff The packet to transmit. // //---------------------------------------------------------------------------- STATIC int jtTxPIOTransmit( PDEV_INSTANCE pDevInstance, struct sk_buff * pSkBuff ) { struct device * pDev = pDevInstance->pDev; // Number of bytes of data, not including Tx header UINT32 PacketByteCount = pSkBuff->len; // Number of 4 byte dwords, rounded up (not including one for Tx header) UINT32 PacketDwordCount = (PacketByteCount - 1) / 4 + 1; // Number of complete 4 byte dwords in packet UINT32 PacketFullDwordCount = PacketByteCount / 4; // Transmit header for the controller UINT32 PacketHeader = pDevInstance->TxPacketHeader | PacketByteCount; // Moving pointer to current data element void * pPacketData = pSkBuff->data; // Validate packet length if( PacketByteCount > (pDev->mtu + ETH_HLEN) ) { // The packet is too large - discard JT_DPRINTK( "Discarding oversize packet.\n"); // Release buffer DEV_KFREE_SKB( pSkBuff, FREE_WRITE ); // Update statistics pDevInstance->Statistics.tx_errors++; return 0; } // Check whether there is room in the Transmit FIFO for this packet // First compare against the saved value if( pDevInstance->TxFifoDwordFreeCount < (PacketDwordCount + 1) ) { // Update our saved value and check again jtCsrIn16( pDevInstance, TX_FIFO_DWORDS_FREE_REG, &(pDevInstance->TxFifoDwordFreeCount) ); // If there is still not enough space available if( pDevInstance->TxFifoDwordFreeCount < (PacketDwordCount + 1) ) { // Drop the packet printk( KERN_NOTICE "%s: Transmit buffer is full. Dropping packet.\n", pDev->name ); // Release buffer DEV_KFREE_SKB( pSkBuff, FREE_WRITE ); // Update statistics pDevInstance->Statistics.tx_dropped++; return 0; } } // Update saved value of Fifo usage pDevInstance->TxFifoDwordFreeCount -= (PacketDwordCount + 1); // Transmit the header jtCsrOut32( pDevInstance, TX_FIFO_WRITE_REG, PacketHeader ); // Transmit the packet itself // Write each dword of packet data pPacketData = pSkBuff->data; jtCsrOut32FromBuffer( pDevInstance, TX_FIFO_WRITE_REG, pPacketData, PacketFullDwordCount ); pPacketData += PacketFullDwordCount * sizeof(UINT32); // Transmit remaining halfword, if any if( (PacketByteCount & 2) == 2 ) { jtCsrOut16( pDevInstance, TX_FIFO_WRITE_REG, * (UINT16 *) pPacketData ); pPacketData += sizeof(UINT16); } // Transmit remaining byte, if any if( (PacketByteCount & 1) == 1 ) { jtCsrOut16( pDevInstance, TX_FIFO_WRITE_REG, * (UINT8 *) pPacketData ); pPacketData += sizeof(UINT8); } // Notify the controller to begin transmit jtCsrOut32( pDevInstance, COMMAND_REG, SERECL0 | SLMDTXCM ); // Update statistics #if defined(EMULATION) pDevInstance->Statistics.tx_packets++; #endif #if LINUX_VERSION_CODE >= 0x20125 pDevInstance->Statistics.tx_bytes += pSkBuff->len; #endif // Release buffer DEV_KFREE_SKB( pSkBuff, FREE_WRITE ); return 0; } //---------------------------------------------------------------------------- // // FUNCTION: jtTxPDxDone // // DESCRIPTION: Reclaim buffers used by packets that have completed transmit. // // PARAMETERS: // pDevInstance The pertinent controller instance. // TransmitDoneCount The number of PDC/PDL transmits that have completed. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtTxPDxDone( PDEV_INSTANCE pDevInstance, UINT32 TransmitDoneCount ) { struct device * pDev = pDevInstance->pDev; int TransmitIndex; // Iterate over each completed transmit for( TransmitIndex = 0; TransmitIndex < TransmitDoneCount; ++TransmitIndex ) { void * pPDx = NULL; // If no buffers are on the In Progress queue if( jtQueueIsEmpty( &pDevInstance->TxPDxInProgressQueue ) ) { // Serious error in our bookeeping printk( KERN_ERR "%s: Controller indicates nonexistent transmits.\n", pDev->name ); return JT_STATUS_FAILURE; } // Take the transmit buffer off the In Progress queue jtQueueDequeue( &pDevInstance->TxPDxInProgressQueue, (void **) &pPDx ); // Use least significant bit to determine PDC or PDL buffer if( ((unsigned long) pPDx) & PDL_FLAG ) { // The buffer is a PDL PDL * pPDL = (void *) ((unsigned long) pPDx & ~PDL_FLAG); UINT64 SkBuffVirtAddr = 0; struct sk_buff * pSkBuff = NULL; JT_ASSERT( ((pPDL->Header & PDL_FGCN_MASK) >> TX_PDL_FGCN_SHIFT) == 1 ); // Release the skbuff holding the packet data #if defined(EMULATION) emuUnMapPhysicalAddress( jtEmuHandle, &pPDL->Fragment[0].FragmentAddressLow, &pPDL->Fragment[0].FragmentAddressHigh ); #endif SkBuffVirtAddr = (((UINT64) pPDL->Fragment[1].FragmentAddressHigh) << 32) | pPDL->Fragment[1].FragmentAddressLow; pSkBuff = (void *) (unsigned long) SkBuffVirtAddr; DEV_KFREE_SKB( pSkBuff, FREE_WRITE ); #if defined(JT_DEBUG) pPDL->Fragment[1].FragmentAddressLow = 0; pPDL->Fragment[1].FragmentAddressHigh = 0; #endif // Put the buffer back on the available queue jtQueueEnqueue( &pDevInstance->TxPDLAvailableQueue, pPDL ); #if defined(PERFORMANCE_TX) atomic_dec ( &pDevInstance->TxPDLPending ); #endif } else // PDC { JT_ASSERT( ((((TX_PDC *)pPDx)->Header & PDL_FGCN_MASK) >> TX_PDL_FGCN_SHIFT) == 2 ); // Put the buffer back on the available queue jtQueueEnqueue( &pDevInstance->TxPDCAvailableQueue, pPDx ); } } // Decide whether the transmitter is still busy if( test_bit(0, (void *) &pDevInstance->TxFull) ) { // Have a PDC available BOOLEAN PDCNotFull = (!jtQueueIsEmpty( &pDevInstance->TxPDCAvailableQueue )); // Likewise, have a PDL available BOOLEAN PDLNotFull = (!jtQueueIsEmpty( &pDevInstance->TxPDLAvailableQueue )); if( PDCNotFull && PDLNotFull ) { // Re-read the number of commands that the chip can accept // If the chip is ready for more commands if( jtTxGetFreeCount( pDevInstance ) != 0 ) { // Mark the controller as not busy clear_bit(0, (void *) &pDevInstance->TxFull); clear_bit(0, (void *) &pDev->tbusy ); mark_bh(NET_BH); } } } return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtTxStop // // DESCRIPTION: Direct the controller to cease transmitting packets. // //---------------------------------------------------------------------------- STATIC void jtTxStop(PDEV_INSTANCE pDevInstance) { // Disable packet reception on the controller jtCsrOut32( pDevInstance, MODE_REG_1, TXEN ); // Disable Timer #if defined(PERFORMANCE_TX) pDevInstance->TxPDLTimerOn = 0; jtCsrOut32( pDevInstance, INTR_PERIOD_REG, 0 ); jtCsrOut32( pDevInstance, COMMAND_REG, DLINRQ ); #endif // Disable interrupts for any transmit events jtCsrOut32( pDevInstance, INTR_MASK_REG, TXDMDNMS | TXCMEMMS | TXFIWMMS | TMEXMS ); } //---------------------------------------------------------------------------- // // FUNCTION: jtTxPDxFlush // // DESCRIPTION: Flush all PDCs and PDLs in progress. // jtTxStop must have been called prior to this function. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtTxPDxFlush(PDEV_INSTANCE pDevInstance) { JT_ASSERT( pDevInstance->TxIOMode == TX_IOMODE_BUSMASTER ); // Drain the InProgress queue while( !jtQueueIsEmpty( &pDevInstance->TxPDxInProgressQueue ) ) { jtTxPDxDone( pDevInstance, 1 ); } #if defined(PERFORMANCE_TX) atomic_set ( &pDevInstance->TxPDLPending, 0 ); #endif return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtTxPDxDestroy // // DESCRIPTION: Deallocate all queues and buffers required // for busmaster transmit modes. // jtTxStop must have been called prior to this function. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtTxPDxDestroy(PDEV_INSTANCE pDevInstance) { JT_ASSERT( pDevInstance->TxIOMode == TX_IOMODE_BUSMASTER ); // Interate over each allocated Tx PDC buffer. while( ! jtQueueIsEmpty( &pDevInstance->TxPDCAvailableQueue ) ) { void * pPDC = NULL; // Dequeue buffer on the Available queue jtQueueDequeue( &pDevInstance->TxPDCAvailableQueue, (void **) &pPDC ); #if defined(EMULATION) // Release the emulator memory handle emuUnMapVirtualAddress( jtEmuHandle, pPDC ); #endif // Deallocate PDC buffer free_pages( (unsigned long) pPDC, pDevInstance->TxPDCOrder ); } // Deallocate Tx PDLs if( pDevInstance->pTxPDLBase != NULL ) { #if defined(EMULATION) // Release the emulator memory handle jtUnMapPhysicalAddress( &pDevInstance->TxPDLBaseEmuAddress ); #endif // Deallocate PDL buffer block free_pages( (unsigned long) pDevInstance->pTxPDLBase, pDevInstance->TxPDLOrder ); } // Deallocate Tx queues jtQueueDestroy( &pDevInstance->TxPDCAvailableQueue ); jtQueueDestroy( &pDevInstance->TxPDLAvailableQueue ); jtQueueDestroy( &pDevInstance->TxPDxInProgressQueue ); return JT_STATUS_SUCCESS; } /***************************************************************************** * Misc Routines ****************************************************************************/ #if 0 //---------------------------------------------------------------------------- // // FUNCTION: jtPrintMacAddress // // DESCRIPTION: Print out the colon-separated octets of a mac address. // // PARAMETERS: // Message A string to print before the address. // pAddress The Mac address to print. // //---------------------------------------------------------------------------- STATIC void jtPrintMacAddress( const char * Message, const UINT8 * pAddress ) { printk( KERN_INFO "%s %02x:%02x:%02x:%02x:%02x:%02x\n", Message, (int) pAddress[0], (int) pAddress[1], (int) pAddress[2], (int) pAddress[3], (int) pAddress[4], (int) pAddress[5] ); } #endif //---------------------------------------------------------------------------- // // FUNCTION: jtNop // // DESCRIPTION: Do absolutely nothing. // //---------------------------------------------------------------------------- STATIC JT_STATUS jtNop( DEV_INSTANCE * pDevInstance ) { return JT_STATUS_SUCCESS; } //---------------------------------------------------------------------------- // // FUNCTION: jtSizeToOrder // // DESCRIPTION: Compute the binary logarithm of an (byte count / PAGE_SIZE). // We want to allocate 'ByteCount' bytes, but the function // __get_free_pages requires an 'Order' parameter, where // 2**Order is the number of pages that will be allocated // // PARAMETERS: // ByteCount Just that // // RETURNS: // pOrder The smallest X such that (2**X) * PAGE_SIZE >= ByteCount // pActualByteCount The number (2**Order) * PAGE_SIZE // //---------------------------------------------------------------------------- STATIC void jtSizeToOrder( UINT32 ByteCount, UINT32 * pOrder, UINT32 * pActualByteCount ) { // Find actual number of pages *pOrder = 0; *pActualByteCount = PAGE_SIZE; while( *pActualByteCount < ByteCount ) { (*pActualByteCount) *= 2; (*pOrder)++; } } //------------------------------------------------------------------------------ // First, the polynomial itself and its table of feedback terms. The // polynomial is // X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 // Note that we take it "backwards" and put the highest-order term in // the lowest-order bit. The X^32 term is "implied"; the LSB is the // X^31 term, etc. The X^0 term (usually shown as "+1") results in // the MSB being 1. // // Note that the usual hardware shift register implementation, which // is what we're using (we're merely optimizing it by doing eight-bit // chunks at a time) shifts bits into the lowest-order term. In our // implementation, that means shifting towards the right. Why do we // do it this way? Because the calculated CRC must be transmitted in // order from highest-order term to lowest-order term. UARTs transmit // characters in order from LSB to MSB. By storing the CRC this way, // we hand it to the UART in the order low-byte to high-byte; the UART // sends each low-bit to hight-bit; and the result is transmission bit // by bit from highest- to lowest-order term without requiring any bit // shuffling on our part. Reception works similarly. // // The feedback terms table consists of 256, 32-bit entries. Notes: // // 1. The table can be generated at runtime if desired; code to do so // is shown later. It might not be obvious, but the feedback // terms simply represent the results of eight shift/xor opera- // tions for all combinations of data and CRC register values. // // 2. The CRC accumulation logic is the same for all CRC polynomials, // be they sixteen or thirty-two bits wide. You simply choose the // appropriate table. Alternatively, because the table can be // generated at runtime, you can start by generating the table for // the polynomial in question and use exactly the same "updcrc", // if your application needn't simultaneously handle two CRC // polynomials. (Note, however, that XMODEM is strange.) // // 3. For 16-bit CRCs, the table entries need be only 16 bits wide; // of course, 32-bit entries work OK if the high 16 bits are zero. // // 4. The values must be right-shifted by eight bits by the "updcrc" // logic; the shift must be unsigned (bring in zeroes). On some // hardware you could probably optimize the shift in assembler by // using byte-swap instructions. //------------------------------------------------------------------------------ static unsigned long crc_32_tab[] = { // CRC polynomial 0xedb88320 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; #define UPDC32(octet,crc) (crc_32_tab[((crc) ^ ((unsigned char)octet)) & 0xff] ^ ((crc) >> 8)) UINT32 crc32buf(char *buf, long len) { register unsigned long oldcrc32; oldcrc32 = 0xFFFFFFFF; for ( ; len; --len, ++buf) { oldcrc32 = UPDC32(*buf, oldcrc32); } return ~oldcrc32; } /* * Local Variables: * compile-command: "gcc -DMODULE -D__KERNEL__ -O2 -Wall -I../../include -c dgelin.c" * End: */