📄 w89c840.c
字号:
nic->packet[4], nic->packet[5], nic->packet[6], nic->packet[7], nic->packet[8], nic->packet[9], nic->packet[10], nic->packet[11], nic->packet[12], nic->packet[13], nic->packet[14], nic->packet[15], nic->packet[16], nic->packet[17]);#endif } entry = (++w840private.cur_rx) % RX_RING_SIZE; w840private.rx_head_desc = &w840private.rx_ring[entry]; } while (0); if (intr_status & (AbnormalIntr | TxFIFOUnderflow | IntrPCIErr |TimerInt | IntrTxStopped)) { handle_intr(intr_status); } return packet_received;}/**************************************************************************w89c840_transmit - Transmit a frame***************************************************************************/static void w89c840_transmit( struct nic *nic, const char *d, /* Destination */ unsigned int t, /* Type */ unsigned int s, /* size */ const char *p) /* Packet */{ /* send the packet to destination */ unsigned entry; int transmit_status; /* Caution: the write order is important here, set the field with the "ownership" bits last. */ /* Fill in our transmit buffer */ entry = w840private.cur_tx % TX_RING_SIZE; memcpy (tx_packet, d, ETH_ALEN); /* dst */ memcpy (tx_packet + ETH_ALEN, nic->node_addr, ETH_ALEN);/* src */ *((char *) tx_packet + 12) = t >> 8; /* type */ *((char *) tx_packet + 13) = t; memcpy (tx_packet + ETH_HLEN, p, s); s += ETH_HLEN; while (s < ETH_ZLEN) *((char *) tx_packet + ETH_HLEN + (s++)) = 0; w840private.tx_ring[entry].buffer1 = virt_to_le32desc(tx_packet); w840private.tx_ring[entry].length = (DescWholePkt | s); if (entry >= TX_RING_SIZE-1) /* Wrap ring */ w840private.tx_ring[entry].length |= (DescIntr | DescEndRing); w840private.tx_ring[entry].status = (DescOwn); w840private.cur_tx++; w840private.tx_q_bytes += s; writel(0, ioaddr + TxStartDemand); /* Work around horrible bug in the chip by marking the queue as full when we do not have FIFO room for a maximum sized packet. */ if ((w840private.drv_flags & HasBrokenTx) && w840private.tx_q_bytes > TX_BUG_FIFO_LIMIT) { /* Actually this is left to help finding error tails later in debugging... * See Linux kernel driver in winbond-840.c for details. */ w840private.tx_full = 1; }#if defined(W89C840_DEBUG) printf("winbond-840 : Transmit frame # %d size %d queued in slot %d.\n", w840private.cur_tx, s, entry);#endif /* Now wait for TX to complete. */ transmit_status = w840private.tx_ring[entry].status; load_timer2(TX_TIMEOUT); { u32 intr_stat = 0; while (1) { intr_stat = readl(ioaddr + IntrStatus);#if defined(W89C840_DEBUG) decode_interrupt(intr_stat);#endif if (intr_stat & (NormalIntr | IntrTxDone)) { while ( (transmit_status & DescOwn) && timer2_running()) { transmit_status = w840private.tx_ring[entry].status; } writel(intr_stat & 0x0001ffff, ioaddr + IntrStatus); break; } } } if ((transmit_status & DescOwn) == 0) {#if defined(W89C840_DEBUG) printf("winbond-840 : transmission complete after %d wait loop iterations, status %X\n", TX_LOOP_COUNT - transmit_loop_counter, w840private.tx_ring[entry].status);#endif return; } /* Transmit timed out... */ printf("winbond-840 : transmission TIMEOUT : status %X\n", w840private.tx_ring[entry].status); return;}/**************************************************************************w89c840_disable - Turn off ethernet interface***************************************************************************/static void w89c840_disable(struct nic *nic){ /* Don't know what to do to disable the board. Is this needed at all? */ /* Yes, a live NIC can corrupt the loaded memory later [Ken] */ /* Stop the chip's Tx and Rx processes. */ writel(w840private.csr6 &= ~0x20FA, ioaddr + NetworkConfig);}/**************************************************************************w89c840_probe - Look for an adapter, this routine's visible to the outside***************************************************************************/struct nic *w89c840_probe(struct nic *nic, unsigned short *probe_addrs, struct pci_device *p){ u16 sum = 0; int i, j, to; unsigned short value; int options; int promisc; if (probe_addrs == 0 || probe_addrs[0] == 0) return 0; ioaddr = probe_addrs[0]; /* Mask the bit that says "this is an io addr" */#if defined(W89C840_DEBUG) printf("winbond-840: PCI bus %hhX device function %hhX: I/O address: %hX\n", p->bus, p->devfn, ioaddr);#endif ioaddr = ioaddr & ~3; /* Mask the bit that says "this is an io addr" */ /* if probe_addrs is 0, then routine can use a hardwired default */ /* From Matt Hortman <mbhortman@acpthinclient.com> */ if (p->vendor == PCI_VENDOR_ID_WINBOND2 && p->dev_id == PCI_DEVICE_ID_WINBOND2_89C840) { /* detected "Winbond W89c840 Fast Ethernet PCI NIC" */ } else if ( p->vendor == PCI_VENDOR_ID_COMPEX && p->dev_id == PCI_DEVICE_ID_COMPEX_RL100ATX) { /* detected "Compex RL100ATX Fast Ethernet PCI NIC" */ } else { /* Gee, guess what? They missed again. */ printf("device ID : %X - is not a Compex RL100ATX NIC.\n", p->dev_id); return 0; } printf(" %s\n", w89c840_version); adjust_pci_device(p); /* Ok. Got one. Read the eeprom. */ for (j = 0, i = 0; i < 0x40; i++) { value = eeprom_read(ioaddr, i); eeprom[i] = value; sum += value; } for (i=0;i<ETH_ALEN;i++) { nic->node_addr[i] = (eeprom[i/2] >> (8*(i&1))) & 0xff; } printf ("Ethernet addr: %!\n", nic->node_addr);#if defined(W89C840_DEBUG) printf("winbond-840: EEPROM checksum %hX, got eeprom", sum);#endif /* Reset the chip to erase previous misconfiguration. No hold time required! */ writel(0x00000001, ioaddr + PCIBusCfg); if (driver_flags & CanHaveMII) { int phy, phy_idx = 0; for (phy = 1; phy < 32 && phy_idx < 4; phy++) { int mii_status = mdio_read(ioaddr, phy, 1); if (mii_status != 0xffff && mii_status != 0x0000) { w840private.phys[phy_idx++] = phy; w840private.advertising = mdio_read(ioaddr, phy, 4);#if defined(W89C840_DEBUG) printf("winbond-840 : MII PHY found at address %d, status " "%X advertising %hX.\n", phy, mii_status, w840private.advertising);#endif } } w840private.mii_cnt = phy_idx; if (phy_idx == 0) { printf("winbond-840 : MII PHY not found -- this device may not operate correctly.\n"); } } /* point to NIC specific routines */ nic->reset = w89c840_reset; nic->poll = w89c840_poll; nic->transmit = w89c840_transmit; nic->disable = w89c840_disable; w89c840_reset(nic); return nic;}/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces. These are often serial bit streams generated by the host processor. The example below is for the common 93c46 EEPROM, 64 16 bit words. *//* Delay between EEPROM clock transitions. No extra delay is needed with 33Mhz PCI, but future 66Mhz access may need a delay. Note that pre-2.0.34 kernels had a cache-alignment bug that made udelay() unreliable. The old method of using an ISA access as a delay, __SLOW_DOWN_IO__, is depricated.*/#define eeprom_delay(ee_addr) readl(ee_addr)enum EEPROM_Ctrl_Bits { EE_ShiftClk=0x02, EE_Write0=0x801, EE_Write1=0x805, EE_ChipSelect=0x801, EE_DataIn=0x08,};/* The EEPROM commands include the alway-set leading bit. */enum EEPROM_Cmds { EE_WriteCmd=(5 << 6), EE_ReadCmd=(6 << 6), EE_EraseCmd=(7 << 6),};static int eeprom_read(long addr, int location){ int i; int retval = 0; int ee_addr = addr + EECtrl; int read_cmd = location | EE_ReadCmd; writel(EE_ChipSelect, ee_addr); /* Shift the read command bits out. */ for (i = 10; i >= 0; i--) { short dataval = (read_cmd & (1 << i)) ? EE_Write1 : EE_Write0; writel(dataval, ee_addr); eeprom_delay(ee_addr); writel(dataval | EE_ShiftClk, ee_addr); eeprom_delay(ee_addr); } writel(EE_ChipSelect, ee_addr); for (i = 16; i > 0; i--) { writel(EE_ChipSelect | EE_ShiftClk, ee_addr); eeprom_delay(ee_addr); retval = (retval << 1) | ((readl(ee_addr) & EE_DataIn) ? 1 : 0); writel(EE_ChipSelect, ee_addr); eeprom_delay(ee_addr); } /* Terminate the EEPROM access. */ writel(0, ee_addr); return retval;}/* MII transceiver control section. Read and write the MII registers using software-generated serial MDIO protocol. See the MII specifications or DP83840A data sheet for details. The maximum data clock rate is 2.5 Mhz. The minimum timing is usually met by back-to-back 33Mhz PCI cycles. */#define mdio_delay(mdio_addr) readl(mdio_addr)/* Set iff a MII transceiver on any interface requires mdio preamble. This only set with older tranceivers, so the extra code size of a per-interface flag is not worthwhile. */static char mii_preamble_required = 1;#define MDIO_WRITE0 (MDIO_EnbOutput)#define MDIO_WRITE1 (MDIO_DataOut | MDIO_EnbOutput)/* Generate the preamble required for initial synchronization and a few older transceivers. */static void mdio_sync(long mdio_addr){ int bits = 32; /* Establish sync by sending at least 32 logic ones. */ while (--bits >= 0) { writel(MDIO_WRITE1, mdio_addr); mdio_delay(mdio_addr); writel(MDIO_WRITE1 | MDIO_ShiftClk, mdio_addr); mdio_delay(mdio_addr); }}static int mdio_read(int base_address, int phy_id, int location){ long mdio_addr = base_address + MIICtrl; int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; int i, retval = 0; if (mii_preamble_required) mdio_sync(mdio_addr); /* Shift the read command bits out. */ for (i = 15; i >= 0; i--) { int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; writel(dataval, mdio_addr); mdio_delay(mdio_addr); writel(dataval | MDIO_ShiftClk, mdio_addr); mdio_delay(mdio_addr); } /* Read the two transition, 16 data, and wire-idle bits. */ for (i = 20; i > 0; i--) { writel(MDIO_EnbIn, mdio_addr); mdio_delay(mdio_addr); retval = (retval << 1) | ((readl(mdio_addr) & MDIO_DataIn) ? 1 : 0); writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); mdio_delay(mdio_addr); } return (retval>>1) & 0xffff;}static void mdio_write(int base_address, int phy_id, int location, int value){ long mdio_addr = base_address + MIICtrl; int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; int i; if (location == 4 && phy_id == w840private.phys[0]) w840private.advertising = value; if (mii_preamble_required) mdio_sync(mdio_addr); /* Shift the command bits out. */ for (i = 31; i >= 0; i--) { int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; writel(dataval, mdio_addr); mdio_delay(mdio_addr); writel(dataval | MDIO_ShiftClk, mdio_addr); mdio_delay(mdio_addr); } /* Clear out extra bits. */ for (i = 2; i > 0; i--) { writel(MDIO_EnbIn, mdio_addr); mdio_delay(mdio_addr); writel(MDIO_EnbIn | MDIO_ShiftClk, mdio_addr); mdio_delay(mdio_addr); } return;}static void check_duplex(void){ int mii_reg5 = mdio_read(ioaddr, w840private.phys[0], 5); int negotiated = mii_reg5 & w840private.advertising; int duplex; if (w840private.duplex_lock || mii_reg5 == 0xffff) return; duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040; if (w840private.full_duplex != duplex) { w840private.full_duplex = duplex; #if defined(W89C840_DEBUG) printf("winbond-840 : Setting %s-duplex based on MII # %d negotiated capability %X\n", duplex ? "full" : "half", w840private.phys[0], negotiated);#endif w840private.csr6 &= ~0x200; w840private.csr6 |= duplex ? 0x200 : 0; }}static void set_rx_mode(void){ u32 mc_filter[2]; /* Multicast hash filter */ u32 rx_mode; /* Accept all multicasts from now on. */ memset(mc_filter, 0xff, sizeof(mc_filter));/* * Actually, should work OK with multicast enabled. -- iko *//* * rx_mode = AcceptBroadcast | AcceptMyPhys | AcceptMulticast; */ rx_mode = AcceptBroadcast | AcceptMyPhys; writel(mc_filter[0], ioaddr + MulticastFilter0); writel(mc_filter[1], ioaddr + MulticastFilter1); w840private.csr6 &= ~0x00F8; w840private.csr6 |= rx_mode; writel(w840private.csr6, ioaddr + NetworkConfig);#if defined(W89C840_DEBUG) printf("winbond-840 : Done setting RX mode.\n");#endif}/* Initialize the Rx and Tx rings, along with various 'dev' bits. */static void init_ring(void){ int i; char * p; w840private.tx_full = 0; w840private.tx_q_bytes = w840private.cur_rx = w840private.cur_tx = 0; w840private.dirty_rx = w840private.dirty_tx = 0; w840private.rx_buf_sz = PKT_BUF_SZ; w840private.rx_head_desc = &w840private.rx_ring[0]; /* Initial all Rx descriptors. Fill in the Rx buffers. */ p = &rx_packet[0]; for (i = 0; i < RX_RING_SIZE; i++) { w840private.rx_ring[i].length = w840private.rx_buf_sz; w840private.rx_ring[i].status = 0; w840private.rx_ring[i].next_desc = virt_to_le32desc(&w840private.rx_ring[i+1]); w840private.rx_ring[i].buffer1 = virt_to_le32desc(p + (PKT_BUF_SZ * i)); w840private.rx_ring[i].status = DescOwn | DescIntr; } /* Mark the last entry as wrapping the ring. */ w840private.rx_ring[i-1].length |= DescEndRing; w840private.rx_ring[i-1].next_desc = virt_to_le32desc(&w840private.rx_ring[0]); w840private.dirty_rx = (unsigned int)(i - RX_RING_SIZE); for (i = 0; i < TX_RING_SIZE; i++) { w840private.tx_ring[i].status = 0; } return;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -