📄 pcnet32.c
字号:
#define EB52#ifdef EB50#define __unused __attribute__((unused))#endif/**************************************************************************** pcnet32.c -- Etherboot device driver for the AMD PCnet32* Written 2003-2003 by Timothy Legge <tlegge@rogers.com>** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License, or* (at your option) any later version.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the* GNU General Public License for more details.** You should have received a copy of the GNU General Public License* along with this program; if not, write to the Free Software* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.** Portions of this code based on:* pcnet32.c: An AMD PCnet32 ethernet driver for linux:** (C) 1996-1999 Thomas Bogendoerfer* See Linux Driver for full information* * The transmit and poll functions were written with reference to:* lance.c - LANCE NIC driver for Etherboot written by Ken Yap * * Linux Driver Version 1.27a, 10.02.2002* * * REVISION HISTORY:* ================* v1.0 08-06-2003 timlegge Initial port of Linux driver* v1.1 08-23-2003 timlegge Add multicast support* v1.2 01-17-2004 timlegge Initial driver output cleanup* * Indent Options: indent -kr -i8***************************************************************************//* to get some global routines like printf */#include "etherboot.h"/* to get the interface to the body of the program */#include "nic.h"/* to get the PCI support functions, if this is a PCI NIC */#include "pci.h"/* Include the time functions */#include "timer.h"/* void hex_dump(const char *data, const unsigned int len); *//* Etherboot Specific definations */#define drv_version "v1.2"#define drv_date "01-17-2004"typedef unsigned char u8;typedef signed char s8;typedef unsigned short u16;typedef signed short s16;typedef unsigned int u32;typedef signed int s32;static u32 ioaddr; /* Globally used for the card's io address */#ifdef EB50#define cpu_to_le32(val) (val)#define le16_to_cpu(val) (val)#define le32_to_cpu(val) (val)#define virt_to_bus(x) ((unsigned long)x)#define bus_to_virt(x) ((unsigned long) x)#endif/* Condensed operations for readability. */#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr))#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))/* End Etherboot Specific */int cards_found /* __initdata */ ;#ifdef REMOVE/* FIXME: Remove these they are probably pointless *//* * VLB I/O addresses */static unsigned int pcnet32_portlist[] /*__initdata */ ={ 0x300, 0x320, 0x340, 0x360, 0 };static int pcnet32_debug = 1;static int tx_start = 1; /* Mapping -- 0:20, 1:64, 2:128, 3:~220 (depends on chip vers) */static int pcnet32vlb; /* check for VLB cards ? */static struct net_device *pcnet32_dev;static int max_interrupt_work = 80;static int rx_copybreak = 200;#endif#define PCNET32_PORT_AUI 0x00#define PCNET32_PORT_10BT 0x01#define PCNET32_PORT_GPSI 0x02#define PCNET32_PORT_MII 0x03#define PCNET32_PORT_PORTSEL 0x03#define PCNET32_PORT_ASEL 0x04#define PCNET32_PORT_100 0x40#define PCNET32_PORT_FD 0x80#define PCNET32_DMA_MASK 0xffffffff/* * table to translate option values from tulip * to internal options */static unsigned char options_mapping[] = { PCNET32_PORT_ASEL, /* 0 Auto-select */ PCNET32_PORT_AUI, /* 1 BNC/AUI */ PCNET32_PORT_AUI, /* 2 AUI/BNC */ PCNET32_PORT_ASEL, /* 3 not supported */ PCNET32_PORT_10BT | PCNET32_PORT_FD, /* 4 10baseT-FD */ PCNET32_PORT_ASEL, /* 5 not supported */ PCNET32_PORT_ASEL, /* 6 not supported */ PCNET32_PORT_ASEL, /* 7 not supported */ PCNET32_PORT_ASEL, /* 8 not supported */ PCNET32_PORT_MII, /* 9 MII 10baseT */ PCNET32_PORT_MII | PCNET32_PORT_FD, /* 10 MII 10baseT-FD */ PCNET32_PORT_MII, /* 11 MII (autosel) */ PCNET32_PORT_10BT, /* 12 10BaseT */ PCNET32_PORT_MII | PCNET32_PORT_100, /* 13 MII 100BaseTx */ PCNET32_PORT_MII | PCNET32_PORT_100 | PCNET32_PORT_FD, /* 14 MII 100BaseTx-FD */ PCNET32_PORT_ASEL /* 15 not supported */};#define MAX_UNITS 8 /* More are supported, limit only on options */static int options[MAX_UNITS];static int full_duplex[MAX_UNITS];/* * Theory of Operation * * This driver uses the same software structure as the normal lance * driver. So look for a verbose description in lance.c. The differences * to the normal lance driver is the use of the 32bit mode of PCnet32 * and PCnetPCI chips. Because these chips are 32bit chips, there is no * 16MB limitation and we don't need bounce buffers. *//* * Set the number of Tx and Rx buffers, using Log_2(# buffers). * Reasonable default values are 4 Tx buffers, and 16 Rx buffers. * That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4). */#ifndef PCNET32_LOG_TX_BUFFERS#define PCNET32_LOG_TX_BUFFERS 1#define PCNET32_LOG_RX_BUFFERS 2#endif#define TX_RING_SIZE (1 << (PCNET32_LOG_TX_BUFFERS))#define TX_RING_MOD_MASK (TX_RING_SIZE - 1)/* FIXME: Fix this to allow multiple tx_ring descriptors */#define TX_RING_LEN_BITS 0x0000 /*PCNET32_LOG_TX_BUFFERS) << 12) */#define RX_RING_SIZE (1 << (PCNET32_LOG_RX_BUFFERS))#define RX_RING_MOD_MASK (RX_RING_SIZE - 1)#define RX_RING_LEN_BITS ((PCNET32_LOG_RX_BUFFERS) << 4)#define PKT_BUF_SZ 1544/* Offsets from base I/O address. */#define PCNET32_WIO_RDP 0x10#define PCNET32_WIO_RAP 0x12#define PCNET32_WIO_RESET 0x14#define PCNET32_WIO_BDP 0x16#define PCNET32_DWIO_RDP 0x10#define PCNET32_DWIO_RAP 0x14#define PCNET32_DWIO_RESET 0x18#define PCNET32_DWIO_BDP 0x1C#define PCNET32_TOTAL_SIZE 0x20/* Buffers for the tx and Rx *//* Create a static buffer of size PKT_BUF_SZ for eachTX Descriptor. All descriptors point to apart of this buffer */static unsigned char txb[PKT_BUF_SZ * TX_RING_SIZE] __attribute__ ((aligned(16)));/* Create a static buffer of size PKT_BUF_SZ for eachRX Descriptor All descriptors point to apart of this buffer */static unsigned char rxb[RX_RING_SIZE * PKT_BUF_SZ] __attribute__ ((aligned(16)));/* The PCNET32 Rx and Tx ring descriptors. */struct pcnet32_rx_head { u32 base; s16 buf_length; s16 status; u32 msg_length; u32 reserved;};struct pcnet32_tx_head { u32 base; s16 length; s16 status; u32 misc; u32 reserved;};/* The PCNET32 32-Bit initialization block, described in databook. */struct pcnet32_init_block { u16 mode; u16 tlen_rlen; u8 phys_addr[6]; u16 reserved; u32 filter[2]; /* Receive and transmit ring base, along with extra bits. */ u32 rx_ring; u32 tx_ring;};/* PCnet32 access functions */struct pcnet32_access { u16(*read_csr) (unsigned long, int); void (*write_csr) (unsigned long, int, u16); u16(*read_bcr) (unsigned long, int); void (*write_bcr) (unsigned long, int, u16); u16(*read_rap) (unsigned long); void (*write_rap) (unsigned long, u16); void (*reset) (unsigned long);};/* Define the TX Descriptor */static struct pcnet32_tx_head tx_ring[TX_RING_SIZE] __attribute__ ((aligned(16)));/* Define the RX Descriptor */static struct pcnet32_rx_head rx_ring[RX_RING_SIZE] __attribute__ ((aligned(16)));/* * The first three fields of pcnet32_private are read by the ethernet device * so we allocate the structure should be allocated by pci_alloc_consistent(). */struct pcnet32_private { struct pcnet32_init_block init_block; struct pci_dev *pci_dev; /* Pointer to the associated pci device structure */ const char *name; /* The saved address of a sent-in-place packet/buffer, for skfree(). */ struct sk_buff *tx_skbuff[TX_RING_SIZE]; struct sk_buff *rx_skbuff[RX_RING_SIZE]; struct pcnet32_access a; unsigned int cur_rx, cur_tx; /* The next free ring entry */ unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ char tx_full; int options; int shared_irq:1, /* shared irq possible */ ltint:1, /* enable TxDone-intr inhibitor */ dxsuflo:1, /* disable transmit stop on uflo */ mii:1; /* mii port available */ struct net_device *next; int full_duplex:1;} lpx;static struct pcnet32_private *lp;enum pci_flags_bit { PCI_USES_IO = 1, PCI_USES_MEM = 2, PCI_USES_MASTER = 4, PCI_ADDR0 = 0x10 << 0, PCI_ADDR1 = 0x10 << 1, PCI_ADDR2 = 0x10 << 2, PCI_ADDR3 = 0x10 << 3,};static u16 pcnet32_wio_read_csr(unsigned long addr, int index){ outw(index, addr + PCNET32_WIO_RAP); return inw(addr + PCNET32_WIO_RDP);}static void pcnet32_wio_write_csr(unsigned long addr, int index, u16 val){ outw(index, addr + PCNET32_WIO_RAP); outw(val, addr + PCNET32_WIO_RDP);}static u16 pcnet32_wio_read_bcr(unsigned long addr, int index){ outw(index, addr + PCNET32_WIO_RAP); return inw(addr + PCNET32_WIO_BDP);}static void pcnet32_wio_write_bcr(unsigned long addr, int index, u16 val){ outw(index, addr + PCNET32_WIO_RAP); outw(val, addr + PCNET32_WIO_BDP);}static u16 pcnet32_wio_read_rap(unsigned long addr){ return inw(addr + PCNET32_WIO_RAP);}static void pcnet32_wio_write_rap(unsigned long addr, u16 val){ outw(val, addr + PCNET32_WIO_RAP);}static void pcnet32_wio_reset(unsigned long addr){ inw(addr + PCNET32_WIO_RESET);}static int pcnet32_wio_check(unsigned long addr){ outw(88, addr + PCNET32_WIO_RAP); return (inw(addr + PCNET32_WIO_RAP) == 88);}static struct pcnet32_access pcnet32_wio = { read_csr:pcnet32_wio_read_csr, write_csr:pcnet32_wio_write_csr, read_bcr:pcnet32_wio_read_bcr, write_bcr:pcnet32_wio_write_bcr, read_rap:pcnet32_wio_read_rap, write_rap:pcnet32_wio_write_rap, reset:pcnet32_wio_reset};static u16 pcnet32_dwio_read_csr(unsigned long addr, int index){ outl(index, addr + PCNET32_DWIO_RAP); return (inl(addr + PCNET32_DWIO_RDP) & 0xffff);}static void pcnet32_dwio_write_csr(unsigned long addr, int index, u16 val){ outl(index, addr + PCNET32_DWIO_RAP); outl(val, addr + PCNET32_DWIO_RDP);}static u16 pcnet32_dwio_read_bcr(unsigned long addr, int index){ outl(index, addr + PCNET32_DWIO_RAP); return (inl(addr + PCNET32_DWIO_BDP) & 0xffff);}static void pcnet32_dwio_write_bcr(unsigned long addr, int index, u16 val){ outl(index, addr + PCNET32_DWIO_RAP); outl(val, addr + PCNET32_DWIO_BDP);}static u16 pcnet32_dwio_read_rap(unsigned long addr){ return (inl(addr + PCNET32_DWIO_RAP) & 0xffff);}static void pcnet32_dwio_write_rap(unsigned long addr, u16 val){ outl(val, addr + PCNET32_DWIO_RAP);}static void pcnet32_dwio_reset(unsigned long addr){ inl(addr + PCNET32_DWIO_RESET);}static int pcnet32_dwio_check(unsigned long addr){ outl(88, addr + PCNET32_DWIO_RAP); return ((inl(addr + PCNET32_DWIO_RAP) & 0xffff) == 88);}static struct pcnet32_access pcnet32_dwio = { read_csr:pcnet32_dwio_read_csr, write_csr:pcnet32_dwio_write_csr, read_bcr:pcnet32_dwio_read_bcr, write_bcr:pcnet32_dwio_write_bcr, read_rap:pcnet32_dwio_read_rap, write_rap:pcnet32_dwio_write_rap, reset:pcnet32_dwio_reset};/* Initialize the PCNET32 Rx and Tx rings. */static int pcnet32_init_ring(struct nic *nic){ int i; lp->tx_full = 0; lp->cur_rx = lp->cur_tx = 0; lp->dirty_rx = lp->dirty_tx = 0; for (i = 0; i < RX_RING_SIZE; i++) { rx_ring[i].base = (u32) virt_to_le32desc(&rxb[i]); rx_ring[i].buf_length = le16_to_cpu(-PKT_BUF_SZ); rx_ring[i].status = le16_to_cpu(0x8000); } /* The Tx buffer address is filled in as needed, but we do need to clear the upper ownership bit. */ for (i = 0; i < TX_RING_SIZE; i++) { tx_ring[i].base = 0; tx_ring[i].status = 0; } lp->init_block.tlen_rlen = le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS); for (i = 0; i < 6; i++) lp->init_block.phys_addr[i] = nic->node_addr[i]; lp->init_block.rx_ring = (u32) virt_to_le32desc(&rx_ring[0]); lp->init_block.tx_ring = (u32) virt_to_le32desc(&tx_ring[0]); return 0;}/**************************************************************************RESET - Reset adapter***************************************************************************/static void pcnet32_reset(struct nic *nic){ /* put the card in its initial state */ u16 val; int i; /* Reset the PCNET32 */ lp->a.reset(ioaddr); /* switch pcnet32 to 32bit mode */ lp->a.write_bcr(ioaddr, 20, 2); /* set/reset autoselect bit */ val = lp->a.read_bcr(ioaddr, 2) & ~2; if (lp->options & PCNET32_PORT_ASEL) val |= 2; lp->a.write_bcr(ioaddr, 2, val); /* handle full duplex setting */ if (lp->full_duplex) { val = lp->a.read_bcr(ioaddr, 9) & ~3; if (lp->options & PCNET32_PORT_FD) { val |= 1; if (lp->options == (PCNET32_PORT_FD | PCNET32_PORT_AUI)) val |= 2; } else if (lp->options & PCNET32_PORT_ASEL) { /* workaround of xSeries250, turn on for 79C975 only */ i = ((lp->a. read_csr(ioaddr, 88) | (lp->a.read_csr(ioaddr, 89) << 16)) >> 12) & 0xffff; if (i == 0x2627) val |= 3;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -