Actually, it's two patches... (the required
PCI patch follows).
Description: Nation Semiconductor DP83815 support
Card: NetGear FA311
Card: NetGear FA312 (untested)
Requires: PCI 'pcibios_read_config_dword' patch
Requires: Etherboot-5.0.1
diff --exclude=*~ --recursive --new-file -u etherboot-5.0.1/src/Makefile etherboot-5.0.1.new/src/Makefile
--- etherboot-5.0.1/src/Makefile Thu May 3 11:56:23 2001
+++ etherboot-5.0.1.new/src/Makefile Thu Jun 7 09:10:03 2001
@@ -120,6 +120,7 @@
EPIC100FLAGS= -DINCLUDE_EPIC100
EXOS205FLAGS= -DINCLUDE_EXOS205
LANCEFLAGS= -DINCLUDE_LANCE # Lance/PCI!
+NATSEMIFLAGS= -DINCLUDE_NATSEMI # Netgear FA311/FA312
NE2100FLAGS= -DINCLUDE_NE2100
NEFLAGS= -DINCLUDE_NE -DNE_SCAN=0x300,0x280,0x320,0x340,0x380
NS8390FLAGS= -DINCLUDE_NS8390 # NE2000/PCI!
diff --exclude=*~ --recursive --new-file -u etherboot-5.0.1/src/NIC etherboot-5.0.1.new/src/NIC
--- etherboot-5.0.1/src/NIC Thu Apr 26 10:03:19 2001
+++ etherboot-5.0.1.new/src/NIC Thu Jun 7 09:08:45 2001
@@ -162,6 +162,8 @@
ne ns8390
# 3Com503, aka Etherlink II, also /16 model
3c503 ns8390
+# Netgear FA311 and FA312
+fa311 natsemi 0x100b,0x0020
# SMC 83c170 EPIC/100
epic100 epic100 0x10b8,0x0005
# 3c509, ISA/EISA
diff --exclude=*~ --recursive --new-file -u etherboot-5.0.1/src/cards.h etherboot-5.0.1.new/src/cards.h
--- etherboot-5.0.1/src/cards.h Tue Feb 27 18:46:10 2001
+++ etherboot-5.0.1.new/src/cards.h Thu Jun 7 10:02:34 2001
@@ -170,4 +170,9 @@
PCI_ARG(struct pci_device *));
#endif
+#ifdef INCLUDE_NATSEMI
+extern struct nic *natsemi_probe(struct nic *, unsigned short *
+ PCI_ARG(struct pci_device *));
+#endif
+
#endif /* CARDS_H */
diff --exclude=*~ --recursive --new-file -u etherboot-5.0.1/src/config.c etherboot-5.0.1.new/src/config.c
--- etherboot-5.0.1/src/config.c Wed Apr 25 05:34:08 2001
+++ etherboot-5.0.1.new/src/config.c Thu Jun 7 10:01:24 2001
@@ -9,7 +9,7 @@
#include "nic.h"
#undef INCLUDE_PCI
-#if defined(INCLUDE_NS8390) || defined(INCLUDE_EEPRO100) || defined(INCLUDE_LANCE) || defined(INCLUDE_EPIC100) || defined(INCLUDE_TULIP) || defined(INCLUDE_OTULIP) || defined(INCLUDE_3C90X) || defined(INCLUDE_3C595) || defined(INCLUDE_RTL8139) || defined(INCLUDE_VIA_RHINE) || defined(INCLUDE_W89C840) || defined(INCLUDE_DAVICOM) || defined(INCLUDE_SIS900)
+#if defined(INCLUDE_NS8390) || defined(INCLUDE_EEPRO100) || defined(INCLUDE_LANCE) || defined(INCLUDE_EPIC100) || defined(INCLUDE_TULIP) || defined(INCLUDE_OTULIP) || defined(INCLUDE_3C90X) || defined(INCLUDE_3C595) || defined(INCLUDE_RTL8139) || defined(INCLUDE_VIA_RHINE) || defined(INCLUDE_W89C840) || defined(INCLUDE_DAVICOM) || defined(INCLUDE_SIS900) || defined(INCLUDE_NATSEMI)
/* || others later */
#define INCLUDE_PCI
#include "pci.h"
@@ -170,6 +170,10 @@
{ PCI_VENDOR_ID_SIS, PCI_DEVICE_ID_SIS7016,
"SIS7016", 0, 0, 0, 0},
#endif
+#ifdef INCLUDE_NATSEMI
+ { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_DP83815,
+ "National Semiconductor DP83815", 0, 0, 0, 0},
+#endif
/* other PCI NICs go here */
{0, 0, NULL, 0, 0, 0, 0}
@@ -287,6 +291,9 @@
#endif
#ifdef INCLUDE_W89C840
{ "W89C840F", w89c840_probe, pci_ioaddrs },
+#endif
+#ifdef INCLUDE_NATSEMI
+ { "NS DP83815", natsemi_probe, pci_ioaddrs },
#endif
/* this entry must always be last to mark the end of list */
{ 0, 0, 0 }
diff --exclude=*~ --recursive --new-file -u etherboot-5.0.1/src/natsemi.c etherboot-5.0.1.new/src/natsemi.c
--- etherboot-5.0.1/src/natsemi.c Wed Dec 31 19:00:00 1969
+++ etherboot-5.0.1.new/src/natsemi.c Fri Jun 8 12:49:01 2001
@@ -0,0 +1,864 @@
+/* natsemi.c: A Linux PCI Ethernet driver for the NatSemi DP8381x series. */
+/*
+ Written/copyright 1999-2001 by Donald Becker.
+ Modified for Etherboot 2001, Jason McMullan <jmc...@li...>
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License (GPL), incorporated herein by reference.
+ Drivers based on or derived from this code fall under the GPL and must
+ retain the authorship, copyright and license notice. This file is not
+ a complete program and may only be used when the entire operating
+ system is licensed under the GPL. License for under other terms may be
+ available. Contact the original author for details.
+
+ The original author may be reached as be...@sc..., or at
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+ Support information and updates available at
+ http://www.scyld.com/network/netsemi.html
+
+
+ Linux kernel modifications:
+
+ Version 1.0.1.e: (Jason McMullan <jmc...@li...> )
+ - Modified from Linux driver for etherboot
+ Version 1.0.1:
+ - Spinlock fixes
+ - Bug fixes and better intr performance (Tjeerd)
+ Version 1.0.2:
+ - Now reads correct MAC address from eeprom
+ Version 1.0.3:
+ - Eliminate redundant priv->tx_full flag
+ - Call netif_start_queue from dev->tx_timeout
+ - wmb() in start_tx() to flush data
+ - Update Tx locking
+ - Clean up PCI enable (davej)
+ Version 1.0.4:
+ - Merge Donald Becker's natsemi.c version 1.07
+ Version 1.0.5:
+ - { fill me in }
+ Version 1.0.6:
+ * ethtool support (jgarzik)
+ * Proper initialization of the card (which sometimes
+ fails to occur and leaves the card in a non-functional
+ state). (uzi)
+
+ * Some documented register settings to optimize some
+ of the 100Mbit autodetection circuitry in rev C cards. (uzi)
+
+ * Polling of the PHY intr for stuff like link state
+ change and auto- negotiation to finally work properly. (uzi)
+
+ * One-liner removal of a duplicate declaration of
+ natsemi_error(). (uzi)
+
+*/
+
+#define DRV_NAME "natsemi"
+#define DRV_VERSION "1.07+LK1.0.6"
+#define DRV_RELDATE "May 18, 2001"
+
+
+/* Updated to recommendations in pci-skeleton v2.03. */
+
+/* Automatically extracted configuration info:
+probe-func: natsemi_probe
+config-in: tristate 'National Semiconductor DP8381x series PCI Ethernet support' CONFIG_NATSEMI
+
+c-help-name: National Semiconductor DP8381x series PCI Ethernet support
+c-help-symbol: CONFIG_NATSEMI
+c-help: This driver is for the National Semiconductor DP8381x series,
+c-help: including the 83815 chip.
+c-help: More specific information and updates are available from
+c-help: http://www.scyld.com/network/natsemi.html
+*/
+
+/* The user-configurable values.
+ These may be modified when a driver module is loaded.*/
+
+static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+static int max_interrupt_work = 20;
+static int mtu;
+/* Maximum number of multicast addresses to filter (vs. rx-all-multicast).
+ This chip uses a 512 element hash table based on the Ethernet CRC. */
+static int multicast_filter_limit = 100;
+
+/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
+ Setting to > 1518 effectively disables this feature. */
+static int rx_copybreak;
+
+/* Operational parameters that are set at compile time. */
+
+/* Keep the ring sizes a power of two for compile efficiency.
+ The compiler will convert <unsigned>'%'<2^N> into a bit mask.
+ Making the Tx ring too large decreases the effectiveness of channel
+ bonding and packet priority.
+ There are no ill effects from too-large receive rings. */
+#define TX_RING_SIZE 4
+#define RX_RING_SIZE 4
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT (2*HZ)
+
+#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/
+
+#define DEBUGMSG(level, format, args...) { if (debug >= level) printf(format , ##args); }
+
+#if !defined(__OPTIMIZE__)
+#warning You must compile this file with the correct options!
+#warning See the last lines of the source file.
+#error You must compile this driver with "-O".
+#endif
+
+#include "etherboot.h"
+#include "nic.h"
+#include "pci.h"
+#include "cards.h"
+
+#ifdef __KERNEL__
+/* These identify the driver base version and may not be removed. */
+static char version[] __devinitdata =
+KERN_INFO DRV_NAME ".c:v1.07 1/9/2001 Written by Donald Becker <be...@sc...>\n"
+KERN_INFO " http://www.scyld.com/network/natsemi.html\n"
+KERN_INFO " (unofficial 2.4.x kernel port, version " DRV_VERSION ", " DRV_RELDATE " Jeff Garzik, Tjeerd Mulder)\n";
+#endif
+
+/* Condensed operations for readability. */
+#define cpu_to_le32(x) ((unsigned long)(x))
+#define le32_to_cpu(x) ((unsigned long)(x))
+#define bus_to_virt(x) ((unsigned long)(x))
+#define virt_to_le32desc(addr) cpu_to_le32(addr)
+#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr))
+
+static int eeprom_read(long addr, int location);
+static int mdio_read(struct nic *dev, int phy_id, int location);
+static void natsemi_reset(struct nic *dev);
+static void check_duplex(struct nic *dev);
+static void init_ring(struct nic *dev);
+static void natsemi_tx(struct nic *dev, const char *dest, unsigned int type, unsigned int size, const char *pack);
+static int natsemi_poll(struct nic *dev);
+static int natsemi_rx(struct nic *dev);
+static void natsemi_error(struct nic *dev, int intr_status);
+static void set_rx_mode(struct nic *dev);
+static void natsemi_disable(struct nic *dev);
+
+/*
+ Theory of Operation
+
+I. Board Compatibility
+
+This driver is designed for National Semiconductor DP83815 PCI Ethernet NIC.
+It also works with other chips in in the DP83810 series.
+
+II. Board-specific settings
+
+This driver requires the PCI interrupt line to be valid.
+It honors the EEPROM-set values.
+
+III. Driver operation
+
+IIIa. Ring buffers
+
+This driver uses two statically allocated fixed-size descriptor lists
+formed into rings by a branch from the final descriptor to the beginning of
+the list. The ring sizes are set at compile time by RX/TX_RING_SIZE.
+The NatSemi design uses a 'next descriptor' pointer that the driver forms
+into a list.
+
+IIIb/c. Transmit/Receive Structure
+
+This driver uses a zero-copy receive and transmit scheme.
+The driver allocates full frame size skbuffs for the Rx ring buffers at
+open() time and passes the skb->data field to the chip as receive data
+buffers. When an incoming frame is less than RX_COPYBREAK bytes long,
+a fresh skbuff is allocated and the frame is copied to the new skbuff.
+When the incoming frame is larger, the skbuff is passed directly up the
+protocol stack. Buffers consumed this way are replaced by newly allocated
+skbuffs in a later phase of receives.
+
+The RX_COPYBREAK value is chosen to trade-off the memory wasted by
+using a full-sized skbuff for small frames vs. the copying costs of larger
+frames. New boards are typically used in generously configured machines
+and the underfilled buffers have negligible impact compared to the benefit of
+a single allocation size, so the default value of zero results in never
+copying packets. When copying is done, the cost is usually mitigated by using
+a combined copy/checksum routine. Copying also preloads the cache, which is
+most useful with small frames.
+
+A subtle aspect of the operation is that unaligned buffers are not permitted
+by the hardware. Thus the IP header at offset 14 in an ethernet frame isn't
+longword aligned for further processing. On copies frames are put into the
+skbuff at an offset of "+2", 16-byte aligning the IP header.
+
+IIId. Synchronization
+
+The driver runs as two independent, single-threaded flows of control. One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag. The other thread is the interrupt handler, which is single
+threaded by the hardware and interrupt handling software.
+
+The send packet thread has partial control over the Tx ring and 'dev->tbusy'
+flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next
+queue slot is empty, it clears the tbusy flag when finished otherwise it sets
+the 'lp->tx_full' flag.
+
+The interrupt handler has exclusive control over the Rx ring and records stats
+from the Tx ring. After reaping the stats, it marks the Tx queue entry as
+empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it
+clears both the tx_full and tbusy flags.
+
+IV. Notes
+
+NatSemi PCI network controllers are very uncommon.
+
+IVb. References
+
+http://www.scyld.com/expert/100mbps.html
+http://www.scyld.com/expert/NWay.html
+Datasheet is available from:
+http://www.national.com/pf/DP/DP83815.html
+
+IVc. Errata
+
+None characterised.
+*/
+
+
+
+enum pcistuff {
+ PCI_USES_IO = 0x01,
+ PCI_USES_MEM = 0x02,
+ PCI_USES_MASTER = 0x04,
+ PCI_ADDR0 = 0x08,
+ PCI_ADDR1 = 0x10,
+};
+
+/* MMIO operations required */
+#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR1)
+
+/* Offsets to the device registers.
+ Unlike software-only systems, device drivers interact with complex hardware.
+ It's not useful to define symbolic names for every register bit in the
+ device.
+*/
+enum register_offsets {
+ ChipCmd=0x00, ChipConfig=0x04, EECtrl=0x08, PCIBusCfg=0x0C,
+ IntrStatus=0x10, IntrMask=0x14, IntrEnable=0x18,
+ TxRingPtr=0x20, TxConfig=0x24,
+ RxRingPtr=0x30, RxConfig=0x34, ClkRun=0x3C,
+ WOLCmd=0x40, PauseCmd=0x44, RxFilterAddr=0x48, RxFilterData=0x4C,
+ BootRomAddr=0x50, BootRomData=0x54, SiliconRev=0x58, StatsCtrl=0x5C,
+ StatsData=0x60, RxPktErrs=0x60, RxMissed=0x68, RxCRCErrs=0x64,
+ PCIPM=0x44, PhyStatus=0xC0, MIntrCtrl=0xC4, MIntrStatus=0xC8,
+
+ /* These are from the spec, around page 78... on a separate table. */
+ PGSEL=0xCC, PMDCSR=0xE4, TSTDAT=0xFC, DSPCFG=0xF4, SDCFG=0x8C
+};
+
+/* Bit in ChipCmd. */
+enum ChipCmdBits {
+ ChipReset=0x100, RxReset=0x20, TxReset=0x10, RxOff=0x08, RxOn=0x04,
+ TxOff=0x02, TxOn=0x01,
+};
+
+/* Bits in the interrupt status/mask registers. */
+enum intr_status_bits {
+ IntrRxDone=0x0001, IntrRxIntr=0x0002, IntrRxErr=0x0004, IntrRxEarly=0x0008,
+ IntrRxIdle=0x0010, IntrRxOverrun=0x0020,
+ IntrTxDone=0x0040, IntrTxIntr=0x0080, IntrTxErr=0x0100,
+ IntrTxIdle=0x0200, IntrTxUnderrun=0x0400,
+ StatsMax=0x0800, LinkChange=0x4000,
+ WOLPkt=0x2000,
+ RxResetDone=0x1000000, TxResetDone=0x2000000,
+ IntrPCIErr=0x00f00000,
+ IntrNormalSummary=0x0251, IntrAbnormalSummary=0xED20,
+};
+
+/* Bits in the RxMode register. */
+enum rx_mode_bits {
+ AcceptErr=0x20, AcceptRunt=0x10,
+ AcceptBroadcast=0xC0000000,
+ AcceptMulticast=0x00200000, AcceptAllMulticast=0x20000000,
+ AcceptAllPhys=0x10000000, AcceptMyPhys=0x08000000,
+};
+
+/* The Rx and Tx buffer descriptors. */
+/* Note that using only 32 bit fields simplifies conversion to big-endian
+ architectures. */
+struct natsemi_desc {
+ unsigned long next_desc;
+ signed long cmd_status;
+ unsigned long addr;
+ unsigned long software_use;
+};
+
+typedef char packet[PKT_BUF_SZ];
+
+struct natsemi_queue {
+ struct natsemi_desc desc[RX_RING_SIZE];
+ packet buff[RX_RING_SIZE]; /* Packet buffers */
+ int curr; /* Head of queue */
+};
+
+/* Bits in network_desc.status */
+enum desc_status_bits {
+ DescOwn=0x80000000, DescMore=0x40000000, DescIntr=0x20000000,
+ DescNoCRC=0x10000000,
+ DescPktOK=0x08000000, RxTooLong=0x00400000,
+};
+
+#define PRIV_ALIGN 15 /* Required alignment mask */
+struct natsemi_private {
+ /* Descriptor rings first for alignment. */
+ struct natsemi_queue rx_ring;
+ struct natsemi_queue tx_ring;
+
+ /* Frequently used values: keep some adjacent for cache effect. */
+ unsigned int rx_buf_sz; /* Based on MTU+slack. */
+ /* These values are keep track of the transceiver/media in use. */
+ unsigned int medialock:1; /* Do not sense media. */
+ unsigned int default_port:4; /* Last dev->if_port value. */
+ /* Rx filter. */
+ unsigned long cur_rx_mode;
+ unsigned long rx_filter[16];
+ /* FIFO and PCI burst thresholds. */
+ unsigned long tx_config, rx_config;
+ /* original contents of ClkRun register */
+ unsigned long SavedClkRun;
+ /* MII transceiver section. */
+ unsigned short advertising; /* NWay media advertisement */
+
+ unsigned long ioaddr;
+} npx;
+
+
+struct nic *natsemi_probe(struct nic *dev, unsigned short *io_addrs, struct pci_device *pdev)
+{
+ struct natsemi_private *np;
+ int i, option;
+ unsigned long ioaddr;
+ const int pcibar = 1; /* PCI base address register */
+ int prev_eedata;
+ int tmp;
+
+ if (pdev == 0 || pdev->membase == 0)
+ return NULL;
+
+ ioaddr = pdev->membase;
+
+ printf("%s: MMIO region %X\n", DRV_NAME, ioaddr);
+
+ /* point to private storage */
+ np = &npx;
+ np->ioaddr = ioaddr;
+
+ /* natsemi has a non-standard PM control register
+ * in PCI config space. Some boards apparently need
+ * to be brought to D0 in this manner.
+ */
+ pcibios_read_config_dword(pdev->bus, pdev->devfn, PCIPM, &tmp);
+ if (tmp & (0x03|0x100)) {
+ /* D0 state, disable PME assertion */
+ unsigned long newtmp = tmp & ~(0x03|0x100);
+ pcibios_write_config_dword(pdev->bus, pdev->devfn, PCIPM, newtmp);
+ }
+
+ /* Work around the dropped serial bit. */
+ prev_eedata = eeprom_read(ioaddr, 6);
+ for (i = 0; i < 3; i++) {
+ int eedata = eeprom_read(ioaddr, i + 7);
+ dev->node_addr[i*2] = (eedata << 1) + (prev_eedata >> 15);
+ dev->node_addr[i*2+1] = eedata >> 7;
+ prev_eedata = eedata;
+ }
+
+ /* Reset the chip to erase previous misconfiguration. */
+ writel(ChipReset, ioaddr + ChipCmd);
+
+ /* The chip-specific entries in the device structure. */
+ dev->reset=natsemi_reset;
+ dev->poll=natsemi_poll;
+ dev->transmit=natsemi_tx;
+ dev->disable=natsemi_disable;
+
+ printf("%s: %x:%x, ioaddr %x, MAC: %b:%b:%b:%b:%b:%b ",
+ DRV_NAME, pdev->bus, pdev->devfn, ioaddr,
+ dev->node_addr[0],dev->node_addr[1],dev->node_addr[2],
+ dev->node_addr[3],dev->node_addr[4],dev->node_addr[5]);
+
+ np->advertising = mdio_read(dev, 1, 4);
+ if ((readl(ioaddr + ChipConfig) & 0xe000) != 0xe000) {
+ unsigned long chip_config = readl(ioaddr + ChipConfig);
+ printf("%s: Transceiver default autonegotiation %s "
+ "10%s %s duplex.\n",
+ DRV_NAME,
+ chip_config & 0x2000 ? "enabled, advertise" : "disabled, force",
+ chip_config & 0x4000 ? "0" : "",
+ chip_config & 0x8000 ? "full" : "half");
+ }
+ printf("%s: Transceiver status 0x%x advertising %x.\n",
+ DRV_NAME, (int)readl(ioaddr + 0x84), np->advertising);
+
+ natsemi_reset(dev);
+
+ return dev;
+}
+
+
+/* Read the EEPROM and MII Management Data I/O (MDIO) interfaces.
+ The EEPROM code is for the common 93c06/46 EEPROMs with 6 bit addresses. */
+
+/* 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=0x04, EE_DataIn=0x01, EE_ChipSelect=0x08, EE_DataOut=0x02,
+};
+#define EE_Write0 (EE_ChipSelect)
+#define EE_Write1 (EE_ChipSelect | EE_DataIn)
+
+/* 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_Write0, 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);
+ eeprom_delay(ee_addr);
+
+ for (i = 0; i < 16; i++) {
+ writel(EE_ChipSelect | EE_ShiftClk, ee_addr);
+ eeprom_delay(ee_addr);
+ retval |= (readl(ee_addr) & EE_DataOut) ? 1 << i : 0;
+ writel(EE_ChipSelect, ee_addr);
+ eeprom_delay(ee_addr);
+ }
+
+ /* Terminate the EEPROM access. */
+ writel(EE_Write0, ee_addr);
+ writel(0, ee_addr);
+ return retval;
+}
+
+/* MII transceiver control section.
+ The 83815 series has an internal transceiver, and we present the
+ management registers as if they were MII connected. */
+
+static int mdio_read(struct nic *dev, int phy_id, int location)
+{
+ if (phy_id == 1 && location < 32)
+ return readl(npx.ioaddr + 0x80 + (location<<2)) & 0xffff;
+ else
+ return 0xffff;
+}
+
+
+static void natsemi_reset(struct nic *dev)
+{
+ struct natsemi_private *np = &npx;
+ long ioaddr = npx.ioaddr;
+ int i;
+
+ /* Reset the chip, just in case. */
+ writel(ChipReset, ioaddr + ChipCmd);
+
+ /* On page 78 of the spec, they recommend some settings for "optimum
+ performance" to be done in sequence. These settings optimize some
+ of the 100Mbit autodetection circuitry. Also, we only want to do
+ this for rev C of the chip.
+ */
+ if (readl(ioaddr + SiliconRev) == 0x302) {
+ writew(0x0001, ioaddr + PGSEL);
+ writew(0x189C, ioaddr + PMDCSR);
+ writew(0x0000, ioaddr + TSTDAT);
+ writew(0x5040, ioaddr + DSPCFG);
+ writew(0x008C, ioaddr + SDCFG);
+ }
+
+ /* Enable PHY Specific event based interrupts. Link state change
+ and Auto-Negotiation Completion are among the affected.
+ */
+ writew(0x0002, ioaddr + MIntrCtrl);
+
+ init_ring(dev);
+
+ writel(virt_to_le32desc(np->rx_ring.desc), ioaddr + RxRingPtr);
+ writel(virt_to_le32desc(np->tx_ring.desc), ioaddr + TxRingPtr);
+
+ for (i = 0; i < ETH_ALEN; i += 2) {
+ writel(i, ioaddr + RxFilterAddr);
+ writew(dev->node_addr[i] + (dev->node_addr[i+1] << 8),
+ ioaddr + RxFilterData);
+ }
+
+ /* Initialize other registers. */
+ /* Configure the PCI bus bursts and FIFO thresholds. */
+ /* Configure for standard, in-spec Ethernet. */
+
+ if (readl(ioaddr + ChipConfig) & 0x20000000) { /* Full duplex */
+ np->tx_config = 0xD0801002;
+ np->rx_config = 0x10000020;
+ } else {
+ np->tx_config = 0x10801002;
+ np->rx_config = 0x0020;
+ }
+ writel(np->tx_config, ioaddr + TxConfig);
+ writel(np->rx_config, ioaddr + RxConfig);
+
+ /* Disable PME:
+ * The PME bit is initialized from the EEPROM contents.
+ * PCI cards probably have PME disabled, but motherboard
+ * implementations may have PME set to enable WakeOnLan.
+ * With PME set the chip will scan incoming packets but
+ * nothing will be written to memory. */
+ np->SavedClkRun = readl(ioaddr + ClkRun);
+ writel(np->SavedClkRun & ~0x100, ioaddr + ClkRun);
+
+ check_duplex(dev);
+ set_rx_mode(dev);
+
+ /* Enable interrupts by setting the interrupt mask. */
+ writel(IntrNormalSummary | IntrAbnormalSummary | 0x1f, ioaddr + IntrMask);
+ writel(1, ioaddr + IntrEnable);
+
+ writel(RxOn | TxOn, ioaddr + ChipCmd);
+ writel(4, ioaddr + StatsCtrl); /* Clear Stats */
+
+ DEBUGMSG(2,"%s: Done natsemi_reset(), status: %x.\n",
+ DRV_NAME, (int)readl(ioaddr + ChipCmd));
+}
+
+static void check_duplex(struct nic *dev)
+{
+ struct natsemi_private *np = &npx;
+ long ioaddr = npx.ioaddr;
+ int duplex;
+
+ duplex = readl(ioaddr + ChipConfig) & 0x20000000 ? 1 : 0;
+ DEBUGMSG(1,"%s: Setting %s-duplex based on negotiated link"
+ " capability.\n", DRV_NAME,
+ duplex ? "full" : "half");
+ if (duplex) {
+ np->rx_config |= 0x10000000;
+ np->tx_config |= 0xC0000000;
+ } else {
+ np->rx_config &= ~0x10000000;
+ np->tx_config &= ~0xC0000000;
+ }
+ writel(np->tx_config, ioaddr + TxConfig);
+ writel(np->rx_config, ioaddr + RxConfig);
+}
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void init_ring(struct nic *dev)
+{
+ struct natsemi_private *np = &npx;
+ int i;
+
+ np->rx_buf_sz = PKT_BUF_SZ;
+
+ /* Initialize all Rx descriptors. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ np->rx_ring.desc[i].addr = virt_to_le32desc(&np->rx_ring.buff[i]);
+ np->rx_ring.desc[i].next_desc = virt_to_le32desc(&np->rx_ring.desc[i+1]);
+ np->rx_ring.desc[i].cmd_status = cpu_to_le32(DescIntr | np->rx_buf_sz);
+ }
+ /* Mark the last entry as wrapping the ring. */
+ np->rx_ring.desc[i-1].next_desc = virt_to_le32desc(&np->rx_ring.desc[0]);
+ np->rx_ring.curr=0;
+
+ /* Fill in the Tx buffers. */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ np->tx_ring.desc[i].addr = virt_to_le32desc(&np->tx_ring.buff[i]);
+ np->tx_ring.desc[i].next_desc = virt_to_le32desc(&np->tx_ring.desc[i+1]);
+ np->tx_ring.desc[i].cmd_status = 0;
+ }
+ np->tx_ring.desc[i-1].next_desc = virt_to_le32desc(&np->tx_ring.desc[0]);
+ np->tx_ring.curr=0;
+
+ return;
+}
+
+static void natsemi_tx(struct nic *dev, const char *dest, unsigned int type, unsigned int size, const char *pack)
+{
+ struct natsemi_private *np = &npx;
+ unsigned entry;
+ unsigned int i;
+ char *p;
+
+ /* Note: Ordering is important here, set the field with the
+ "ownership" bit last, and only then increment cur_tx. */
+ entry = np->tx_ring.curr;
+
+ DEBUGMSG(3, "%s: TX (packet length %d), ring #%d\n", DRV_NAME, size, entry);
+
+ /* Calculate the next Tx descriptor entry. */
+ p=np->tx_ring.buff[entry];
+ for (i=0;i<ETH_ALEN;i++,p++) {
+ *p = dest[i];
+ *(p+ETH_ALEN)=dev->node_addr[i];
+ }
+ p += ETH_ALEN;
+ *((unsigned short *)p)=htons(type);
+ p += 2;
+ for (i=0;i<size;i++,p++)
+ *p=pack[i];
+
+ size += 2+(ETH_ALEN*2);
+ np->tx_ring.desc[entry].cmd_status = cpu_to_le32(DescOwn|DescIntr | size);
+
+ /* Wake the potentially-idle transmit channel. */
+ writel(TxOn, npx.ioaddr + ChipCmd);
+
+ /* Wait for TX to complete */
+ natsemi_poll(dev);
+
+ DEBUGMSG(3, "%s: TX complete\n", DRV_NAME);
+
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+ after the Tx thread. */
+static int natsemi_poll(struct nic *dev)
+{
+ struct natsemi_private *np;
+ long ioaddr;
+ int entry;
+ unsigned long intr_status, imr_status;
+
+ ioaddr = npx.ioaddr;
+ np = &npx;
+
+wait_some_more:
+ intr_status = readl(ioaddr + IntrStatus);
+
+ /* Acknowledge all of the current interrupt sources ASAP. */
+// writel(intr_status & 0x000ffff, ioaddr + IntrStatus);
+
+ if (intr_status != 0)
+ DEBUGMSG(4, "%s: poll, status %x.\n",
+ DRV_NAME, intr_status);
+
+ entry=np->tx_ring.curr;
+
+ /* Abnormal error summary/uncommon events handlers. */
+ if (intr_status & IntrAbnormalSummary)
+ natsemi_error(dev, intr_status);
+
+ /* Only do RX if there is no TX pending
+ */
+ if ((intr_status & IntrRxDone) &&
+ (np->tx_ring.desc[entry].cmd_status == 0))
+ return natsemi_rx(dev);
+
+ /* Still waiting for TX? */
+ if ((intr_status & IntrTxDone) == 0 &&
+ np->tx_ring.desc[entry].cmd_status != 0)
+ goto wait_some_more;
+
+ if (intr_status & IntrTxDone) {
+ if (np->tx_ring.desc[entry].cmd_status & cpu_to_le32(0x08000000)) {
+ DEBUGMSG(5, "%s: TX done\n", DRV_NAME);
+ /* TX went ok! */
+ } else { /* Various Tx errors */
+ int tx_status = le32_to_cpu(np->tx_ring.desc[entry].cmd_status);
+ if (tx_status & 0x04010000)
+ printf("%s: TX aborted\n", DRV_NAME);
+ if (tx_status & 0x02000000)
+ printf("%s: TX FIFO error\n", DRV_NAME);
+ if (tx_status & 0x01000000)
+ printf("%s: TX carrier error\n", DRV_NAME);
+ if (tx_status & 0x00200000)
+ printf("%s: TX window error\n");
+ printf("%s: TX status error %X\n", DRV_NAME, tx_status);
+ }
+ np->tx_ring.desc[entry].cmd_status = 0;
+ np->tx_ring.curr = (++np->tx_ring.curr) % TX_RING_SIZE;
+ }
+
+exit_ok:
+ return 0;
+}
+
+/* This routine is logically part of the interrupt handler, but separated
+ for clarity and better register allocation. */
+static int natsemi_rx(struct nic *dev)
+{
+ struct natsemi_private *np = &npx;
+ int entry, gotone=0;
+ unsigned long desc_status;
+
+ entry = np->rx_ring.curr;
+ desc_status = le32_to_cpu(np->rx_ring.desc[entry].cmd_status);
+
+ DEBUGMSG(3, "%s: RX enter\n", DRV_NAME);
+
+ /* If the driver owns the next entry it's a new packet. Send it up. */
+ if (desc_status & DescOwn) {
+ DEBUGMSG(4, " In natsemi_rx() entry %d status was %X.\n",
+ entry, desc_status);
+ if ((desc_status & (DescMore|DescPktOK|RxTooLong)) != DescPktOK) {
+ if (desc_status & DescMore) {
+ printf("%s: Oversized(?) Ethernet frame spanned "
+ "multiple buffers, status %x.\n",
+ DRV_NAME, desc_status);
+ } else {
+ /* There was a error. */
+ DEBUGMSG(4, " natsemi_rx() Rx error was %X.\n",
+ desc_status);
+ if (desc_status & 0x06000000)
+ printf("%s: RX overrun\n", DRV_NAME);
+ if (desc_status & 0x00600000)
+ printf("%s: RX length error\n", DRV_NAME);
+ if (desc_status & 0x00140000)
+ printf("%s: RX frame error\n", DRV_NAME);
+ if (desc_status & 0x00080000)
+ printf("%s: RX CRC error\n", DRV_NAME);
+ }
+ } else {
+ int pkt_len = (desc_status & 0x0fff);
+ memcpy(dev->packet, np->rx_ring.buff[entry], pkt_len);
+ dev->packetlen=pkt_len;
+ DEBUGMSG(4,"%s: RX %d bytes\n",DRV_NAME, pkt_len);
+ gotone=1;
+ }
+ /* Refill the Rx ring buffers. */
+ np->rx_ring.desc[entry].cmd_status = cpu_to_le32(DescIntr | np->rx_buf_sz);
+ np->rx_ring.curr = (++np->rx_ring.curr) % RX_RING_SIZE;
+ }
+
+
+ /* Restart Rx engine if stopped. */
+ writel(RxOn, npx.ioaddr + ChipCmd);
+
+ DEBUGMSG(3, "%s: RX done\n", DRV_NAME);
+
+ return gotone;
+}
+
+static void natsemi_error(struct nic *dev, int intr_status)
+{
+ struct natsemi_private *np = &npx;
+ long ioaddr = npx.ioaddr;
+
+ if (intr_status & LinkChange) {
+ printf("%s: Link changed: Autonegotiation advertising"
+ " %x partner %x.\n", DRV_NAME,
+ (int)readl(ioaddr + 0x90), (int)readl(ioaddr + 0x94));
+ /* read MII int status to clear the flag */
+ readw(ioaddr + MIntrStatus);
+ check_duplex(dev);
+ }
+ if (intr_status & IntrTxUnderrun) {
+ if ((np->tx_config & 0x3f) < 62)
+ np->tx_config += 2;
+ writel(np->tx_config, ioaddr + TxConfig);
+ }
+ if (intr_status & WOLPkt) {
+ int wol_status = readl(ioaddr + WOLCmd);
+ printf("%s: Link wake-up event %X",
+ DRV_NAME, wol_status);
+ }
+ if (intr_status & ~(LinkChange|StatsMax|RxResetDone|TxResetDone|0xA7ff))
+ DEBUGMSG(1, "%s: Something Wicked happened! %x.\n",
+ DRV_NAME, intr_status);
+ /* Hmmmmm, it's not clear how to recover from PCI faults. */
+ if (intr_status & IntrPCIErr) {
+ printf("%s: Ack! PCI fault!\n", DRV_NAME);
+ }
+}
+
+/* The little-endian AUTODIN II ethernet CRC calculations.
+ A big-endian version is also available.
+ This is slow but compact code. Do not use this routine for bulk data,
+ use a table-based routine instead.
+ This is common code and should be moved to net/core/crc.c.
+ Chips may use the upper or lower CRC bits, and may reverse and/or invert
+ them. Select the endian-ness that results in minimal calculations.
+*/
+static unsigned const ethernet_polynomial_le = 0xedb88320U;
+static inline unsigned ether_crc_le(int length, unsigned char *data)
+{
+ unsigned int crc = 0xffffffff; /* Initial value. */
+ while(--length >= 0) {
+ unsigned char current_octet = *data++;
+ int bit;
+ for (bit = 8; --bit >= 0; current_octet >>= 1) {
+ if ((crc ^ current_octet) & 1) {
+ crc >>= 1;
+ crc ^= ethernet_polynomial_le;
+ } else
+ crc >>= 1;
+ }
+ }
+ return crc;
+}
+
+static void set_rx_mode(struct nic *dev)
+{
+ long ioaddr = npx.ioaddr;
+ struct natsemi_private *np = &npx;
+ unsigned long rx_mode;
+
+ rx_mode = AcceptBroadcast | AcceptAllMulticast | AcceptMyPhys;
+ writel(rx_mode, ioaddr + RxFilterAddr);
+ np->cur_rx_mode = rx_mode;
+}
+
+static void natsemi_disable(struct nic *dev)
+{
+ long ioaddr = npx.ioaddr;
+ struct natsemi_private *np = &npx;
+ int i;
+
+ DEBUGMSG(2, "%s: Shutting down ethercard, status was %x "
+ "Int %x.\n",
+ DRV_NAME, (int)readl(ioaddr + ChipCmd),
+ (int)readl(ioaddr + IntrStatus));
+
+ /* Disable interrupts using the mask. */
+ writel(0, ioaddr + IntrMask);
+ writel(0, ioaddr + IntrEnable);
+ writel(2, ioaddr + StatsCtrl); /* Freeze Stats */
+
+ /* Stop the chip's Tx and Rx processes. */
+ writel(RxOff | TxOff, ioaddr + ChipCmd);
+
+ /* Free all the skbuffs in the Rx queue. */
+ i=0;
+ np->rx_ring.desc[i].cmd_status = 0;
+ np->rx_ring.desc[i].addr = 0xBADF00D0; /* An invalid address. */
+
+ /* Restore PME enable bit */
+ writel(np->SavedClkRun, ioaddr + ClkRun);
+#if 0
+ writel(0x0200, ioaddr + ChipConfig); /* Power down Xcvr. */
+#endif
+
+ return;
+}
diff --exclude=*~ --recursive --new-file -u etherboot-5.0.1/src/pci.h etherboot-5.0.1.new/src/pci.h
--- etherboot-5.0.1/src/pci.h Wed Apr 18 11:22:51 2001
+++ etherboot-5.0.1.new/src/pci.h Thu Jun 7 09:59:07 2001
@@ -158,6 +158,8 @@
#define PCI_DEVICE_ID_SIS7016 0x7016
#define PCI_VENDOR_ID_DLINK 0x1186
#define PCI_DEVICE_ID_DFE530TXP 0x1300
+#define PCI_VENDOR_ID_NS 0x100b
+#define PCI_DEVICE_ID_NS_DP83815 0x0020
struct pci_device {
unsigned short vendor, dev_id;
--
Jason McMullan, Senior Linux Consultant
Linuxcare, Inc. 412.432.6457 tel, 412.656.3519 cell
jmc...@li..., http://www.linuxcare.com/
Linuxcare. Putting open source to work.
|