diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-02-15 02:15:32 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-02-15 02:15:32 +0000 |
commit | 86464aed71025541805e7b1515541aee89879e33 (patch) | |
tree | e01a457a4912a8553bc65524aa3125d51f29f810 /drivers/net/tulip.c | |
parent | 88f99939ecc6a95a79614574cb7d95ffccfc3466 (diff) |
Merge with Linux 2.2.1.
Diffstat (limited to 'drivers/net/tulip.c')
-rw-r--r-- | drivers/net/tulip.c | 2090 |
1 files changed, 1200 insertions, 890 deletions
diff --git a/drivers/net/tulip.c b/drivers/net/tulip.c index 04ddcef20..555776826 100644 --- a/drivers/net/tulip.c +++ b/drivers/net/tulip.c @@ -1,13 +1,13 @@ -/* tulip.c: A DEC 21040-family ethernet driver for linux. */ +/* tulip.c: A DEC 21040-family ethernet driver for Linux. */ /* - NOTICE: THIS IS THE ALPHA TEST VERSION! - Written 1994-1997 by Donald Becker. + Written 1994-1998 by Donald Becker. This software may be used and distributed according to the terms of the GNU Public License, incorporated herein by reference. - This driver is for the SMC EtherPower PCI ethernet adapter. - It should work with most other DEC 21*40-based ethercards. + This driver is for the Digital "Tulip" ethernet adapter interface. + It should work with most DEC 21*4*-based chips/ethercards, as well as + PNIC and MXIC chips. The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O Center of Excellence in Space Data and Information Sciences @@ -17,14 +17,27 @@ http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html */ -static const char *version = "tulip.c:v0.83 10/19/97 becker@cesdis.gsfc.nasa.gov\n"; +#define SMP_CHECK +static const char version[] = "tulip.c:v0.89H 5/23/98 becker@cesdis.gsfc.nasa.gov\n"; /* A few user-configurable values. */ +/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ +static int max_interrupt_work = 25; + +#define MAX_UNITS 8 /* Used to pass the full-duplex flag, etc. */ -static int full_duplex[8] = {0, }; -static int options[8] = {0, }; -static int mtu[8] = {0, }; /* Jumbo MTU for interfaces. */ +static int full_duplex[MAX_UNITS] = {0, }; +static int options[MAX_UNITS] = {0, }; +static int mtu[MAX_UNITS] = {0, }; /* Jumbo MTU for interfaces. */ + +/* The possible media types that can be set in options[] are: */ +static const char * const medianame[] = { + "10baseT", "10base2", "AUI", "100baseTx", + "10baseT-FD", "100baseTx-FD", "100baseT4", "100baseFx", + "100baseFx-FD", "MII 10baseT", "MII 10baseT-FD", "MII", + "10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FD", "MII 100baseT4", +}; /* Set if the PCI BIOS detects the chips on a multiport board backwards. */ #ifdef REVERSE_PROBE_ORDER @@ -38,40 +51,20 @@ static int reverse_probe = 0; bonding and packet priority. There are no ill effects from too-large receive rings. */ #define TX_RING_SIZE 16 -#define RX_RING_SIZE 16 +#define RX_RING_SIZE 32 /* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ #ifdef __alpha__ -static const int rx_copybreak = 1518; +static int rx_copybreak = 1518; #else -static const int rx_copybreak = 100; +static int rx_copybreak = 100; #endif -/* The following example shows how to always use the 10base2 port. */ -#ifdef notdef -#define TULIP_DEFAULT_MEDIA 1 /* 1 == 10base2 */ -#define TULIP_NO_MEDIA_SWITCH /* Don't switch from this port */ -#endif - -/* Define to force full-duplex operation on all Tulip interfaces. */ -/* #define TULIP_FULL_DUPLEX 1 */ - /* Operational parameters that usually are not changed. */ /* Time in jiffies before concluding the transmitter is hung. */ -#define TX_TIMEOUT ((2000*HZ)/1000) +#define TX_TIMEOUT (4*HZ) -#include <linux/config.h> -#ifdef MODULE -#ifdef MODVERSIONS -#include <linux/modversions.h> -#endif #include <linux/module.h> -#include <linux/version.h> -#else -#define MOD_INC_USE_COUNT -#define MOD_DEC_USE_COUNT -#endif - #include <linux/kernel.h> #include <linux/sched.h> #include <linux/string.h> @@ -82,6 +75,7 @@ static const int rx_copybreak = 100; #include <linux/malloc.h> #include <linux/interrupt.h> #include <linux/pci.h> + #include <asm/processor.h> /* Processor type for cache alignment. */ #include <asm/bitops.h> #include <asm/io.h> @@ -106,28 +100,10 @@ static const int rx_copybreak = 100; #define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2) #endif -#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10338 -#ifdef MODULE -#if !defined(CONFIG_MODVERSIONS) && !defined(__NO_VERSION__) -char kernel_version[] = UTS_RELEASE; -#endif -#else -#undef MOD_INC_USE_COUNT -#define MOD_INC_USE_COUNT -#undef MOD_DEC_USE_COUNT -#define MOD_DEC_USE_COUNT -#endif -#endif /* 1.3.38 */ - #if (LINUX_VERSION_CODE >= 0x10344) #define NEW_MULTICAST #include <linux/delay.h> #endif -#if (LINUX_VERSION_CODE >= 0x20100) -#ifdef MODULE -char kernel_version[] = UTS_RELEASE; -#endif -#endif #ifdef SA_SHIRQ #define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs) #else @@ -135,8 +111,8 @@ char kernel_version[] = UTS_RELEASE; #endif #if (LINUX_VERSION_CODE < 0x20123) +#define hard_smp_processor_id() smp_processor_id() #define test_and_set_bit(val, addr) set_bit(val, addr) -#include <linux/bios32.h> #endif /* This my implementation of shared IRQs, now only used for 1.2.13. */ @@ -167,7 +143,7 @@ I. Board Compatibility This device driver is designed for the DECchip "Tulip", Digital's single-chip ethernet controllers for PCI. Supported members of the family -are the 21040, 21041, 21140, 21140A and 21142. These chips are used on +are the 21040, 21041, 21140, 21140A, 21142, and 21143. These chips are used on many PCI boards including the SMC EtherPower series. @@ -268,44 +244,65 @@ register of the set CSR12-15 written. Hmmm, now how is that possible? */ #ifndef PCI_VENDOR_ID_LITEON #define PCI_VENDOR_ID_LITEON 0x11AD -#define PCI_DEVICE_ID_PNIC 0x0002 -#define PCI_DEVICE_ID_PNIC_X 0x0168 -#else -/* Now PCI_VENDOR_ID_LITEON is defined, but device IDs have different names */ -#define PCI_DEVICE_ID_PNIC PCI_DEVICE_ID_LITEON_LNE100TX -#define PCI_DEVICE_ID_PNIC_X 0x0168 +#endif + +#ifndef PCI_VENDOR_ID_MXIC +#define PCI_VENDOR_ID_MXIC 0x10d9 +#define PCI_DEVICE_ID_MX98713 0x0512 +#define PCI_DEVICE_ID_MX98715 0x0531 +#define PCI_DEVICE_ID_MX98725 0x0531 #endif /* The rest of these values should never change. */ static void tulip_timer(unsigned long data); +static void t21142_timer(unsigned long data); +static void mxic_timer(unsigned long data); +static void pnic_timer(unsigned long data); /* A table describing the chip types. */ +enum tbl_flag { HAS_MII=1, HAS_MEDIA_TABLE = 2, CSR12_IN_SROM = 4,}; static struct tulip_chip_table { - int device_id; - char *chip_name; - int flags; - void (*media_timer)(unsigned long data); + int vendor_id, device_id; + char *chip_name; + int io_size; + int valid_intrs; /* CSR7 interrupt enable settings */ + int flags; + void (*media_timer)(unsigned long data); } tulip_tbl[] = { - { PCI_DEVICE_ID_DEC_TULIP, "Digital DS21040 Tulip", 0, tulip_timer }, - { PCI_DEVICE_ID_DEC_TULIP_PLUS, "Digital DS21041 Tulip", 0, tulip_timer }, - { PCI_DEVICE_ID_DEC_TULIP_FAST, "Digital DS21140 Tulip", 0, tulip_timer }, - { PCI_DEVICE_ID_DEC_TULIP_21142, "Digital DS21142/3 Tulip", 0, tulip_timer }, - { PCI_DEVICE_ID_PNIC_X, "Lite-On 82c168 PNIC", 0, tulip_timer }, + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP, + "Digital DC21040 Tulip", 128, 0x0001ebef, 0, tulip_timer }, + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_PLUS, + "Digital DC21041 Tulip", 128, 0x0001ebef, HAS_MEDIA_TABLE, tulip_timer }, + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_FAST, + "Digital DS21140 Tulip", 128, 0x0001ebef, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, + tulip_timer }, + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_21142, + "Digital DS21142/3 Tulip", 128, 0x0801fbff, + HAS_MII | HAS_MEDIA_TABLE, t21142_timer }, + { PCI_VENDOR_ID_LITEON, 0x0002, + "Lite-On 82c168 PNIC", 256, 0x0001ebef, HAS_MII, pnic_timer }, + { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98713, + "Macronix 98713 PMAC", 128, 0x0001ebef, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer /* Tulip-like! */ }, + { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98715, + "Macronix 98715 PMAC", 256, 0x0001ebef, HAS_MEDIA_TABLE, mxic_timer }, + { PCI_VENDOR_ID_MXIC, PCI_DEVICE_ID_MX98725, + "Macronix 98725 PMAC", 256, 0x0001ebef, HAS_MEDIA_TABLE, mxic_timer }, + { 0x125B, 0x1400, "ASIX AX88140", 128, 0x0001fbff, + HAS_MII | HAS_MEDIA_TABLE | CSR12_IN_SROM, tulip_timer }, {0, 0, 0, 0}, }; /* This matches the table above. */ -enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, LC82C168}; - -static const char * const medianame[] = { - "10baseT", "10base2", "AUI", "100baseTx", - "10baseT-FD", "100baseTx-FD", "100baseT4", "100baseFx", - "100baseFx-FD", "MII 10baseT", "MII 10baseT-FD", "MII", - "10baseT(forced)", "MII 100baseTx", "MII 100baseTx-FD", "MII 100baseT4", -}; -/* A full-duplex map for above. */ -static const char media_fd[] = -{0,0,0,0, 0xff,0xff,0,0, 0xff,0,0xff,0x01, 0,0,0xff,0 }; +enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3, + LC82C168, MX98713, MX98715, MX98725}; + +/* A full-duplex map for media types. */ +enum MediaIs {MediaIsFD = 1, MediaAlwaysFD=2, MediaIsMII=4, MediaIsFx=8, + MediaIs100=16}; +static const char media_cap[] = +{0,0,0,16, 3,19,16,24, 27,4,7,5, 0,20,23,20 }; /* 21041 transceiver register settings: 10-T, 10-2, AUI, 10-T, 10T-FD*/ static u16 t21041_csr13[] = { 0xEF05, 0xEF09, 0xEF09, 0xEF01, 0xEF09, }; static u16 t21041_csr14[] = { 0x7F3F, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, }; @@ -325,9 +322,9 @@ enum tulip_offsets { /* The bits in the CSR5 status registers, mostly interrupt sources. */ enum status_bits { TimerInt=0x800, TPLnkFail=0x1000, TPLnkPass=0x10, + NormalIntr=0x10000, AbnormalIntr=0x8000, RxJabber=0x200, RxDied=0x100, RxNoBuf=0x80, RxIntr=0x40, - TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02, - TxIntr=0x01, + TxFIFOUnderflow=0x20, TxJabber=0x08, TxNoBuf=0x04, TxDied=0x02, TxIntr=0x01, }; /* The Tulip Rx and Tx buffer descriptors. */ @@ -352,7 +349,7 @@ struct medialeaf { struct mediatable { u16 defaultmedia; u8 leafcount, csr12dir; /* General purpose pin directions. */ - unsigned has_mii:1; + unsigned has_mii:1, has_nonmii:1; struct medialeaf mleaf[0]; }; @@ -360,7 +357,6 @@ struct mediainfo { struct mediainfo *next; int info_type; int index; - struct non_mii { char media; unsigned char csr12val; char bitnum, flags;} non_mii; unsigned char *info; }; @@ -384,32 +380,38 @@ struct tulip_private { struct enet_statistics stats; #endif struct timer_list timer; /* Media selection timer. */ -#ifdef CONFIG_NET_HW_FLOWCONTROL - int fc_bit; + int interrupt; /* In-interrupt flag. */ +#ifdef SMP_CHECK + int smp_proc_id; /* Which processor in IRQ handler. */ #endif unsigned int cur_rx, cur_tx; /* The next free ring entry */ unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ unsigned int tx_full:1; /* The Tx queue is full. */ unsigned int full_duplex:1; /* Full-duplex operation requested. */ unsigned int full_duplex_lock:1; + unsigned int fake_addr:1; /* Multiport board faked address. */ unsigned int default_port:4; /* Last dev->if_port value. */ unsigned int media2:4; /* Secondary monitored media port. */ unsigned int medialock:1; /* Don't sense media type. */ unsigned int mediasense:1; /* Media sensing in progress. */ unsigned int csr6; /* Current CSR6 control settings. */ unsigned char eeprom[128]; /* Serial EEPROM contents. */ - signed char phys[4]; /* MII device addresses. */ + u16 to_advertise; /* NWay capabilities advertised. */ + u16 advertising[4]; + signed char phys[4], mii_cnt; /* MII device addresses. */ struct mediatable *mtable; int cur_index; /* Current media index. */ - unsigned char pci_bus, pci_device_fn; + unsigned char pci_bus, pci_dev_fn; int pad0, pad1; /* Used for 8-byte alignment */ }; -static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq, +static struct device *tulip_probe1(int pci_bus, int pci_devfn, + struct device *dev, int chip_id, int options); static void parse_eeprom(struct device *dev); -static int read_eeprom(int ioaddr, int location); -static int mdio_read(int ioaddr, int phy_id, int location); +static int read_eeprom(long ioaddr, int location); +static int mdio_read(struct device *dev, int phy_id, int location); +static void mdio_write(struct device *dev, int phy_id, int location, int value); static void select_media(struct device *dev, int startup); static int tulip_open(struct device *dev); static void tulip_timer(unsigned long data); @@ -420,24 +422,19 @@ static int tulip_rx(struct device *dev); static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs); static int tulip_close(struct device *dev); static struct enet_statistics *tulip_get_stats(struct device *dev); +#ifdef HAVE_PRIVATE_IOCTL +static int private_ioctl(struct device *dev, struct ifreq *rq, int cmd); +#endif #ifdef NEW_MULTICAST -static void set_multicast_list(struct device *dev); +static void set_rx_mode(struct device *dev); #else -static void set_multicast_list(struct device *dev, int num_addrs, void *addrs); -#endif -#ifdef CONFIG_NET_FASTROUTE -#include <linux/if_arp.h> -#include <net/ip.h> - -static int tulip_accept_fastpath(struct device *dev, struct dst_entry *dst); +static void set_rx_mode(struct device *dev, int num_addrs, void *addrs); #endif -#ifdef MODULE /* A list of all installed Tulip devices, for removing the driver module. */ static struct device *root_tulip_dev = NULL; -#endif /* This 21040 probe no longer uses a large fixed contiguous Rx buffer region, but now receives directly into full-sized skbuffs that are allocated @@ -449,121 +446,118 @@ int tulip_probe(struct device *dev) { int cards_found = 0; static int pci_index = 0; /* Static, for multiple probe calls. */ + unsigned char pci_bus, pci_device_fn; /* Ideally we would detect all network cards in slot order. That would be best done a central PCI probe dispatch, which wouldn't work well with the current structure. So instead we detect just the Tulip cards in slot order. */ - if (pci_present()) { - unsigned char pci_bus, pci_device_fn; - - for (;pci_index < 0xff; pci_index++) { - unsigned char pci_latency; #if LINUX_VERSION_CODE >= 0x20155 - unsigned int pci_irq_line; - struct pci_dev *pdev; + if (! pci_present()) + return -ENODEV; #else - unsigned char pci_irq_line; -#endif - unsigned short pci_command, vendor, device; - unsigned int pci_ioaddr, chip_idx = 0; - - if (pcibios_find_class - (PCI_CLASS_NETWORK_ETHERNET << 8, - reverse_probe ? 0xfe - pci_index : pci_index, - &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL) { - if (reverse_probe) - continue; - else - break; - } - pcibios_read_config_word(pci_bus, pci_device_fn, - PCI_VENDOR_ID, &vendor); - pcibios_read_config_word(pci_bus, pci_device_fn, - PCI_DEVICE_ID, &device); + if (! pcibios_present()) + return -ENODEV; +#endif + for (;pci_index < 0xff; pci_index++) { + u16 vendor, device, pci_command, new_command; + unsigned long pci_ioaddr = 0; + int chip_idx = 0; + + if (pcibios_find_class + (PCI_CLASS_NETWORK_ETHERNET << 8, + reverse_probe ? 0xfe - pci_index : pci_index, + &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL) + if (reverse_probe) + continue; + else + break; + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_VENDOR_ID, &vendor); + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_DEVICE_ID, &device); + + for (chip_idx = 0; tulip_tbl[chip_idx].chip_name; chip_idx++) + if (vendor == tulip_tbl[chip_idx].vendor_id && + device == tulip_tbl[chip_idx].device_id) + break; + if (tulip_tbl[chip_idx].chip_name == 0) { + if (vendor == PCI_VENDOR_ID_DEC || + vendor == PCI_VENDOR_ID_LITEON) + printk(KERN_INFO "Unknown Tulip-style PCI ethernet chip type" + " %4.4x %4.4x"" detected: not configured.\n", + vendor, device); + continue; + } #if LINUX_VERSION_CODE >= 0x20155 - pdev = pci_find_slot(pci_bus, pci_device_fn); - pci_irq_line = pdev->irq; - pci_ioaddr = pdev->base_address[0]; + pci_ioaddr = pci_find_slot(pci_bus, pci_device_fn)->base_address[0]; #else - pcibios_read_config_byte(pci_bus, pci_device_fn, - PCI_INTERRUPT_LINE, &pci_irq_line); - pcibios_read_config_dword(pci_bus, pci_device_fn, - PCI_BASE_ADDRESS_0, &pci_ioaddr); + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, + &pci_ioaddr); #endif - /* Remove I/O space marker in bit 0. */ - pci_ioaddr &= ~3; - - if (vendor != PCI_VENDOR_ID_DEC - && vendor != PCI_VENDOR_ID_LITEON) - continue; - if (vendor == PCI_VENDOR_ID_LITEON) - device = PCI_DEVICE_ID_PNIC_X; - - for (chip_idx = 0; tulip_tbl[chip_idx].chip_name; chip_idx++) - if (device == tulip_tbl[chip_idx].device_id) - break; - if (tulip_tbl[chip_idx].chip_name == 0) { - printk(KERN_INFO "Unknown Digital PCI ethernet chip type" - " %4.4x"" detected: not configured.\n", device); - continue; - } - if (tulip_debug > 2) - printk(KERN_DEBUG "Found DEC PCI Tulip at I/O %#x, IRQ %d.\n", - pci_ioaddr, pci_irq_line); + /* Remove I/O space marker in bit 0. */ + pci_ioaddr &= ~3; - if (check_region(pci_ioaddr, TULIP_TOTAL_SIZE)) - continue; + if (tulip_debug > 2) + printk(KERN_DEBUG "Found %s at I/O %#lx.\n", + tulip_tbl[chip_idx].chip_name, pci_ioaddr); + + if (check_region(pci_ioaddr, tulip_tbl[chip_idx].io_size)) + continue; + + pcibios_read_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, &pci_command); + new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO; + if (pci_command != new_command) { + printk(KERN_INFO " The PCI BIOS has not enabled this" + " device! Updating PCI command %4.4x->%4.4x.\n", + pci_command, new_command); + pcibios_write_config_word(pci_bus, pci_device_fn, + PCI_COMMAND, new_command); + } -#ifdef MODULE - dev = tulip_probe1(dev, pci_ioaddr, pci_irq_line, chip_idx, - cards_found); -#else - dev = tulip_probe1(dev, pci_ioaddr, pci_irq_line, chip_idx, -1); -#endif + dev = tulip_probe1(pci_bus, pci_device_fn, dev, chip_idx, cards_found); - if (dev) { - /* Get and check the bus-master and latency values. */ - pcibios_read_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, &pci_command); - if ( ! (pci_command & PCI_COMMAND_MASTER)) { - printk(KERN_INFO " PCI Master Bit has not been set! Setting...\n"); - pci_command |= PCI_COMMAND_MASTER; - pcibios_write_config_word(pci_bus, pci_device_fn, - PCI_COMMAND, pci_command); - } - pcibios_read_config_byte(pci_bus, pci_device_fn, - PCI_LATENCY_TIMER, &pci_latency); - if (pci_latency < 10) { - printk(KERN_INFO " PCI latency timer (CFLT) is unreasonably" - " low at %d. Setting to 64 clocks.\n", pci_latency); + /* Get and check the bus-master and latency values. */ + if (dev) { + unsigned char pci_latency; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_LATENCY_TIMER, &pci_latency); + if (pci_latency < 10) { + printk(KERN_INFO " PCI latency timer (CFLT) is " + "unreasonably low at %d. Setting to 64 clocks.\n", + pci_latency); pcibios_write_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, 64); - } else if (tulip_debug > 1) - printk(KERN_INFO " PCI latency timer (CFLT) is %#x.\n", - pci_latency); - /* Bring the 21143 out power-down mode. */ - if (device == PCI_DEVICE_ID_DEC_TULIP_21142) + } else if (tulip_debug > 1) + printk(KERN_INFO " PCI latency timer (CFLT) is %#x, " + " PCI command is %4.4x.\n", + pci_latency, new_command); + /* Bring the 21143 out power-down mode. */ + if (device == PCI_DEVICE_ID_DEC_TULIP_21142) pcibios_write_config_dword(pci_bus, pci_device_fn, - 0x40, 0x40000000); - dev = 0; - cards_found++; - } + 0x40, 0x40000000); + dev = 0; + cards_found++; } } return cards_found ? 0 : -ENODEV; } -static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq, +static struct device *tulip_probe1(int pci_bus, int pci_device_fn, + struct device *dev, int chip_id, int board_idx) { static int did_version = 0; /* Already printed version info. */ struct tulip_private *tp; + long ioaddr; + int irq; /* See note below on the multiport cards. */ static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'}; static int last_irq = 0; + static int multiport_cnt = 0; /* For four-port boards w/one EEPROM */ int i; unsigned short sum; @@ -572,7 +566,25 @@ static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq, dev = init_etherdev(dev, 0); - printk(KERN_INFO "%s: %s at %#3x,", +#if LINUX_VERSION_CODE >= 0x20155 + irq = pci_find_slot(pci_bus, pci_device_fn)->irq; + ioaddr = pci_find_slot(pci_bus, pci_device_fn)->base_address[0]; +#else + { + u8 pci_irq_line; + u32 pci_ioaddr; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq_line); + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, + &pci_ioaddr); + irq = pci_irq_line; + ioaddr = pci_ioaddr; + } +#endif + /* Remove I/O space marker in bit 0. */ + ioaddr &= ~3; + + printk(KERN_INFO "%s: %s at %#3lx,", dev->name, tulip_tbl[chip_id].chip_name, ioaddr); /* Stop the chip's Tx and Rx processes. */ @@ -629,6 +641,10 @@ static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq, for (i = 0; i < 8; i ++) if (ee_data[i] != ee_data[16+i]) sa_offset = 20; + if (ee_data[0] == 0xff && ee_data[1] == 0xff && ee_data[2] == 0) { + sa_offset = 2; /* Grrr, damn Matrox boards. */ + multiport_cnt = 4; + } for (i = 0; i < 6; i ++) { dev->dev_addr[i] = ee_data[i + sa_offset]; sum += ee_data[i + sa_offset]; @@ -655,13 +671,15 @@ static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq, irq = last_irq; #endif } + for (i = 0; i < 6; i++) printk(" %2.2x", last_phys_addr[i] = dev->dev_addr[i]); printk(", IRQ %d.\n", irq); last_irq = irq; /* We do a request_region() only to register /proc/ioports info. */ - request_region(ioaddr, TULIP_TOTAL_SIZE, tulip_tbl[chip_id].chip_name); + /* Note that proper size is tulip_tbl[chip_id].chip_name, but... */ + request_region(ioaddr, TULIP_TOTAL_SIZE, dev->name); dev->base_addr = ioaddr; dev->irq = irq; @@ -671,11 +689,11 @@ static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq, memset(tp, 0, sizeof(*tp)); dev->priv = tp; -#ifdef MODULE tp->next_module = root_tulip_dev; root_tulip_dev = dev; -#endif + tp->pci_bus = pci_bus; + tp->pci_dev_fn = pci_device_fn; tp->chip_id = chip_id; #ifdef TULIP_FULL_DUPLEX @@ -690,36 +708,64 @@ static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq, #endif /* The lower four bits are the media type. */ - if (board_idx >= 0) { - tp->full_duplex = (options[board_idx]&16) || full_duplex[board_idx]>0; - if (tp->full_duplex) - tp->full_duplex_lock = 1; + if (board_idx >= 0 && board_idx < MAX_UNITS) { tp->default_port = options[board_idx] & 15; - if (tp->default_port) - tp->medialock = 1; + if ((options[board_idx] & 0x90) || full_duplex[board_idx] > 0) + tp->full_duplex = 1; if (mtu[board_idx] > 0) dev->mtu = mtu[board_idx]; } + if (dev->mem_start) + tp->default_port = dev->mem_start; + if (tp->default_port) { + tp->medialock = 1; + if (media_cap[tp->default_port] & MediaAlwaysFD) + tp->full_duplex = 1; + } + if (tp->full_duplex) + tp->full_duplex_lock = 1; /* This is logically part of probe1(), but too complex to write inline. */ - if (chip_id != DC21040 && chip_id != LC82C168) + if (tulip_tbl[chip_id].flags & HAS_MEDIA_TABLE) parse_eeprom(dev); - if (tp->mtable && tp->mtable->has_mii) { + if (media_cap[tp->default_port] & MediaIsMII) { + u16 media2advert[] = { 0x20, 0x40, 0x03e0, 0x60, 0x80, 0x100, 0x200 }; + tp->to_advertise = media2advert[tp->default_port - 9]; + } else + tp->to_advertise = 0x03e1; + + if ((tp->mtable && tp->mtable->has_mii) || + ( ! tp->mtable && (tulip_tbl[tp->chip_id].flags & HAS_MII))) { int phy, phy_idx; /* Find the connected MII xcvrs. Doing this in open() would allow detecting external xcvrs later, but takes much time. */ for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(tp->phys); phy++) { - int mii_status = mdio_read(ioaddr, phy, 0); - if (mii_status != 0xffff && mii_status != 0x0000) { - tp->phys[phy_idx++] = phy; - printk(KERN_INFO "%s: MII transceiver found at MDIO address %d.\n", - dev->name, phy); + int mii_status = mdio_read(dev, phy, 1); + if (mii_status != 0xffff && mii_status != 0x0000) { + int mii_reg0 = mdio_read(dev, phy, 0); + int reg4 = ((mii_status>>6) & tp->to_advertise) | 1; + tp->phys[phy_idx] = phy; + tp->advertising[phy_idx++] = reg4; + printk(KERN_INFO "%s: MII transceiver found at MDIO address " + "%d, config %4.4x status %4.4x.\n", + dev->name, phy, mii_reg0, mii_status); + if (1 || (media_cap[tp->default_port] & MediaIsMII)) { + printk(KERN_DEBUG "%s: Advertising %4.4x on PHY %d," + " previously advertising %4.4x.\n", + dev->name, reg4, phy, mdio_read(dev, phy, 4)); + mdio_write(dev, phy, 4, reg4); + } + /* Enable autonegotiation: some boards default to off. */ + mdio_write(dev, phy, 0, mii_reg0 | + (tp->full_duplex ? 0x1100 : 0x1000) | + (media_cap[tp->default_port]&MediaIs100 ? 0x2000:0)); } } - if (phy_idx == 0) { + tp->mii_cnt = phy_idx; + if (tp->mtable && tp->mtable->has_mii && phy_idx == 0) { printk(KERN_INFO "%s: ***WARNING***: No MII transceiver found!\n", dev->name); tp->phys[0] = 1; @@ -731,11 +777,11 @@ static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq, dev->hard_start_xmit = &tulip_start_xmit; dev->stop = &tulip_close; dev->get_stats = &tulip_get_stats; -#ifdef HAVE_MULTICAST - dev->set_multicast_list = &set_multicast_list; +#ifdef HAVE_PRIVATE_IOCTL + dev->do_ioctl = &private_ioctl; #endif -#ifdef CONFIG_NET_FASTROUTE - dev->accept_fastpath = tulip_accept_fastpath; +#ifdef HAVE_MULTICAST + dev->set_multicast_list = &set_rx_mode; #endif /* Reset the xcvr interface and turn on heartbeat. */ @@ -744,20 +790,37 @@ static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq, outl(0x00000000, ioaddr + CSR13); outl(0xFFFFFFFF, ioaddr + CSR14); outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */ - outl(inl(ioaddr + CSR6) | 0x200, ioaddr + CSR6); + outl(inl(ioaddr + CSR6) | 0x0200, ioaddr + CSR6); outl(0x0000EF05, ioaddr + CSR13); break; - case DC21140: case DC21142: - if (tp->mtable) - outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12); - break; case DC21040: outl(0x00000000, ioaddr + CSR13); outl(0x00000004, ioaddr + CSR13); break; + case DC21140: default: + if (tp->mtable) + outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12); + break; + case DC21142: + outl(0x82420200, ioaddr + CSR6); + outl(0x0001, ioaddr + CSR13); + outl(0x0003FFFF, ioaddr + CSR14); + outl(0x0008, ioaddr + CSR15); + outl(0x0001, ioaddr + CSR13); + outl(0x1301, ioaddr + CSR12); /* Start NWay. */ + break; case LC82C168: - outl(0x33, ioaddr + CSR12); - outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */ + if ( ! tp->mii_cnt) { + outl(0x00420000, ioaddr + CSR6); + outl(0x30, ioaddr + CSR12); + outl(0x0001F078, ioaddr + 0xB8); + outl(0x0201F078, ioaddr + 0xB8); /* Turn on autonegotiation. */ + } + break; + case MX98713: case MX98715: case MX98725: + outl(0x00000000, ioaddr + CSR6); + outl(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */ + outl(0x00000001, ioaddr + CSR13); break; } @@ -782,7 +845,9 @@ static struct fixups { {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x021f, 0x0000, 0x009E, /* 10baseT */ 0x0903, 0x006D, /* 100baseTx */ }}, - {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x013f, + {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x033f, + 0x0107, 0x8021, /* 100baseFx */ + 0x0108, 0x8021, /* 100baseFx-FD */ 0x0103, 0x006D, /* 100baseTx */ }}, {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0313, 0x1001, 0x009E, /* 10base2, CSR12 0x10*/ @@ -796,9 +861,15 @@ static struct fixups { {0, 0, 0, 0, {}}}; static const char * block_name[] = {"21140 non-MII", "21140 MII PHY", - "21142 non-MII PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"}; + "21142 Serial PHY", "21142 MII PHY", "21143 SYM PHY", "21143 reset method"}; #define EEPROM_SIZE 128 +#if defined(__i386__) +#define get_u16(ptr) (*(u16 *)(ptr)) +#else +#define get_u16(ptr) (((u8*)(ptr))[0] + (((u8*)(ptr))[1]<<8)) +#endif + static void parse_eeprom(struct device *dev) { /* The last media info list parsed, for multiport boards. */ @@ -806,7 +877,7 @@ static void parse_eeprom(struct device *dev) static unsigned char *last_ee_data = NULL; static int controller_index = 0; struct tulip_private *tp = (struct tulip_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; unsigned char *ee_data = tp->eeprom; int i; @@ -870,30 +941,45 @@ static void parse_eeprom(struct device *dev) } subsequent_board: - if (tp->chip_id == DC21041) { + if (ee_data[27] == 0) { /* No valid media table. */ + } else if (tp->chip_id == DC21041) { unsigned char *p = (void *)ee_data + ee_data[27 + controller_index*3]; - short media = *(u16 *)p; - int count = p[2]; + short media; + int count; + + media = get_u16(p); + p += 2; + count = *p++; printk(KERN_INFO "%s:21041 Media information at %d, default media " "%4.4x (%s).\n", dev->name, ee_data[27], media, media & 0x0800 ? "Autosense" : medianame[media & 15]); for (i = 0; i < count; i++) { - unsigned char media_code = p[3 + i*7]; - u16 *csrvals = (u16 *)&p[3 + i*7 + 1]; - printk(KERN_INFO "%s: 21041 media %2.2x (%s)," - " csr13 %4.4x csr14 %4.4x csr15 %4.4x.\n", - dev->name, media_code & 15, medianame[media_code & 15], - csrvals[0], csrvals[1], csrvals[2]); + unsigned char media_code = *p++; + u16 csrvals[3]; + int idx; + for (idx = 0; idx < 3; idx++) { + csrvals[idx] = get_u16(p); + p += 2; + } + if (media_code & 0x40) { + printk(KERN_INFO "%s: 21041 media %2.2x (%s)," + " csr13 %4.4x csr14 %4.4x csr15 %4.4x.\n", + dev->name, media_code & 15, medianame[media_code & 15], + csrvals[0], csrvals[1], csrvals[2]); + } else + printk(KERN_INFO "%s: 21041 media #%d, %s.\n", + dev->name, media_code & 15, medianame[media_code & 15]); } } else { unsigned char *p = (void *)ee_data + ee_data[27]; unsigned char csr12dir = 0; int count; struct mediatable *mtable; - u16 media = *((u16 *)p)++; + u16 media = get_u16(p); - if (tp->chip_id == DC21140) + p += 2; + if (tulip_tbl[tp->chip_id].flags & CSR12_IN_SROM) csr12dir = *p++; count = *p++; mtable = (struct mediatable *) @@ -905,7 +991,7 @@ subsequent_board: mtable->defaultmedia = media; mtable->leafcount = count; mtable->csr12dir = csr12dir; - mtable->has_mii = 0; + mtable->has_nonmii = mtable->has_mii = 0; printk(KERN_INFO "%s: EEPROM default media type %s.\n", dev->name, media & 0x0800 ? "Autosense" : medianame[media & 15]); @@ -916,14 +1002,18 @@ subsequent_board: leaf->type = 0; leaf->media = p[0] & 0x3f; leaf->leafdata = p; + if ((p[2] & 0x61) == 0x01) /* Bogus, but Znyx boards do it. */ + mtable->has_mii = 1; p += 4; } else { leaf->type = p[1]; if (p[1] & 1) { mtable->has_mii = 1; leaf->media = 11; - } else + } else { + mtable->has_nonmii = 1; leaf->media = p[2] & 0x0f; + } leaf->leafdata = p + 2; p += (p[0] & 0x3f) + 1; } @@ -966,11 +1056,11 @@ subsequent_board: #define EE_READ_CMD (6 << 6) #define EE_ERASE_CMD (7 << 6) -static int read_eeprom(int ioaddr, int location) +static int read_eeprom(long ioaddr, int location) { int i; unsigned short retval = 0; - int ee_addr = ioaddr + CSR9; + long ee_addr = ioaddr + CSR9; int read_cmd = location | EE_READ_CMD; outl(EE_ENB & ~EE_CS, ee_addr); @@ -1001,6 +1091,16 @@ static int read_eeprom(int ioaddr, int location) 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 PCI I/O cycles, but we insert a delay to avoid + "overclocking" issues or future 66Mhz PCI. */ +#define mdio_delay() inl(mdio_addr) + /* Read and write the MII registers using software-generated serial MDIO protocol. It is just different enough from the EEPROM protocol to not share code. The maxium data clock rate is 2.5 Mhz. */ @@ -1010,18 +1110,24 @@ static int read_eeprom(int ioaddr, int location) #define MDIO_ENB 0x00000 /* Ignore the 0x02000 databook setting. */ #define MDIO_ENB_IN 0x40000 #define MDIO_DATA_READ 0x80000 -#ifdef _LINUX_DELAY_H -#define mdio_delay() udelay(1) -#else -#define mdio_delay() __SLOW_DOWN_IO -#endif -static int mdio_read(int ioaddr, int phy_id, int location) +static int mdio_read(struct device *dev, int phy_id, int location) { + struct tulip_private *tp = (struct tulip_private *)dev->priv; int i; int read_cmd = (0xf6 << 10) | (phy_id << 5) | location; - unsigned short retval = 0; - int mdio_addr = ioaddr + CSR9; + int retval = 0; + long mdio_addr = dev->base_addr + CSR9; + + if (tp->chip_id == LC82C168) { + long ioaddr = dev->base_addr; + int i = 1000; + outl(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0); + while (--i > 0) + if ( ! ((retval = inl(ioaddr + 0xA0)) & 0x80000000)) + return retval & 0xffff; + return 0xffff; + } /* Establish sync by sending at least 32 logic ones. */ for (i = 32; i >= 0; i--) { @@ -1031,60 +1137,78 @@ static int mdio_read(int ioaddr, int phy_id, int location) mdio_delay(); } /* Shift the read command bits out. */ - for (i = 17; i >= 0; i--) { + for (i = 15; i >= 0; i--) { int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; - outl(dataval, mdio_addr); - mdio_delay(); - outl(dataval | MDIO_SHIFT_CLK, mdio_addr); + outl(MDIO_ENB | dataval, mdio_addr); mdio_delay(); - outl(dataval, mdio_addr); + outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); mdio_delay(); } - outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - outl(MDIO_ENB_IN, mdio_addr); - - for (i = 16; i > 0; i--) { - outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); - mdio_delay(); - retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); + /* Read the two transition, 16 data, and wire-idle bits. */ + for (i = 19; i > 0; i--) { outl(MDIO_ENB_IN, mdio_addr); mdio_delay(); - } - /* Clear out extra bits. */ - for (i = 16; i > 0; i--) { + retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0); outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); mdio_delay(); - outl(MDIO_ENB_IN, mdio_addr); - mdio_delay(); } - return retval; + return (retval>>1) & 0xffff; } -#ifdef CONFIG_NET_HW_FLOWCONTROL -/* Enable receiver */ - -void tulip_xon(struct device *dev) +static void mdio_write(struct device *dev, int phy_id, int location, int value) { - struct tulip_private *lp = (struct tulip_private *)dev->priv; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int i; + int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value; + long mdio_addr = dev->base_addr + CSR9; + + if (tp->chip_id == LC82C168) { + long ioaddr = dev->base_addr; + int i = 1000; + outl(cmd, ioaddr + 0xA0); + do + if ( ! (inl(ioaddr + 0xA0) & 0x80000000)) + break; + while (--i > 0); + return; + } - clear_bit(lp->fc_bit, &netdev_fc_xoff); - if (dev->start) - outl(lp->csr6 | 0x2002, dev->base_addr + CSR6); + /* Establish sync by sending 32 logic ones. */ + for (i = 32; i >= 0; i--) { + outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr); + mdio_delay(); + outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Shift the command bits out. */ + for (i = 31; i >= 0; i--) { + int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0; + outl(MDIO_ENB | dataval, mdio_addr); + mdio_delay(); + outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + /* Clear out extra bits. */ + for (i = 2; i > 0; i--) { + outl(MDIO_ENB_IN, mdio_addr); + mdio_delay(); + outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr); + mdio_delay(); + } + return; } -#endif static int tulip_open(struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; int i = 0; /* On some chip revs we must set the MII/SYM port before the reset!? */ - if (tp->mtable && tp->mtable->has_mii) + if (tp->mii_cnt || (tp->mtable && tp->mtable->has_mii)) outl(0x00040000, ioaddr + CSR6); /* Reset the chip, holding bit 0 set at least 50 PCI cycles. */ @@ -1106,31 +1230,30 @@ tulip_open(struct device *dev) Tx and Rx queues and the address filter list. */ #if defined(__alpha__) /* ToDo: Alpha setting could be better. */ - outl(0x00200000 | 0xE000, ioaddr + CSR0); + outl(0x01A00000 | 0xE000, ioaddr + CSR0); #elif defined(__powerpc__) - outl(0x00200080 | 0x8000, ioaddr + CSR0); + outl(0x01A00080 | 0x8000, ioaddr + CSR0); #elif defined(__i386__) #if defined(MODULE) /* When a module we don't have 'x86' to check. */ - outl(0x00200000 | 0x4800, ioaddr + CSR0); + outl(0x01A00000 | 0x4800, ioaddr + CSR0); #else -#ifndef ORIGINAL_TEXT -#define x86 (boot_cpu_data.x86) +#if (LINUX_VERSION_CODE > 0x2014c) +#define x86 boot_cpu_data.x86 #endif - outl(0x00200000 | (x86 <= 4 ? 0x4800 : 0x8000), ioaddr + CSR0); + outl(0x01A00000 | (x86 <= 4 ? 0x4800 : 0x8000), ioaddr + CSR0); if (x86 <= 4) printk(KERN_INFO "%s: This is a 386/486 PCI system, setting cache " "alignment to %x.\n", dev->name, - 0x00200000 | (x86 <= 4 ? 0x4800 : 0x8000)); + 0x01A00000 | (x86 <= 4 ? 0x4800 : 0x8000)); #endif #else - outl(0x00200000 | 0x4800, ioaddr + CSR0); + outl(0x01A00000 | 0x4800, ioaddr + CSR0); #warning Processor architecture undefined! #endif #ifdef SA_SHIRQ - if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, - tulip_tbl[tp->chip_id].chip_name, dev)) { + if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ, dev->name, dev)) { return -EAGAIN; } #else @@ -1178,7 +1301,7 @@ tulip_open(struct device *dev) outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4); if (dev->if_port == 0) - dev->if_port = tp->default_port; + dev->if_port = tp->default_port; if (tp->chip_id == DC21041 && dev->if_port > 4) /* Invalid: Select initial TP, autosense, autonegotiate. */ dev->if_port = 4; @@ -1186,14 +1309,16 @@ tulip_open(struct device *dev) /* Allow selecting a default media. */ if (tp->mtable == NULL) goto media_picked; - if (dev->if_port) + if (dev->if_port) { + int looking_for = media_cap[dev->if_port] & MediaIsMII ? 11 : + (dev->if_port == 12 ? 0 : dev->if_port); for (i = 0; i < tp->mtable->leafcount; i++) - if (tp->mtable->mleaf[i].media == - (dev->if_port == 12 ? 0 : dev->if_port)) { - printk(KERN_INFO "%s: Using user-specified media %s.\n", - dev->name, medianame[dev->if_port]); - goto media_picked; - } + if (tp->mtable->mleaf[i].media == looking_for) { + printk(KERN_INFO "%s: Using user-specified media %s.\n", + dev->name, medianame[dev->if_port]); + goto media_picked; + } + } if ((tp->mtable->defaultmedia & 0x0800) == 0) for (i = 0; i < tp->mtable->leafcount; i++) if (tp->mtable->mleaf[i].media == (tp->mtable->defaultmedia & 15)) { @@ -1201,55 +1326,61 @@ tulip_open(struct device *dev) dev->name, medianame[tp->mtable->mleaf[i].media]); goto media_picked; } + /* Start sensing first non-full-duplex media. */ for (i = tp->mtable->leafcount - 1; - (media_fd[tp->mtable->mleaf[i].media] & 2) && i > 0; i--) + (media_cap[tp->mtable->mleaf[i].media] & MediaAlwaysFD) && i > 0; i--) ; media_picked: - tp->cur_index = i; tp->csr6 = 0; - select_media(dev, 1); + tp->cur_index = i; + if (dev->if_port == 0 && tp->chip_id == DC21142) { + tp->csr6 = 0x82420200; + outl(0x0003FFFF, ioaddr + CSR14); + outl(0x0008, ioaddr + CSR15); + outl(0x0001, ioaddr + CSR13); + outl(0x1301, ioaddr + CSR12); + } else if (tp->chip_id == LC82C168 && tp->mii_cnt && ! tp->medialock) { + dev->if_port = 11; + tp->csr6 = 0x816C0000 | (tp->full_duplex ? 0x0200 : 0); + outl(0x0001, ioaddr + CSR15); + } else + select_media(dev, 1); /* Start the chip's Tx to process setup frame. */ outl(tp->csr6, ioaddr + CSR6); outl(tp->csr6 | 0x2000, ioaddr + CSR6); dev->tbusy = 0; - dev->interrupt = 0; + tp->interrupt = 0; dev->start = 1; - /* Enable interrupts by setting the interrupt mask. */ - outl(0x0001ebef, ioaddr + CSR7); + outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR5); + outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7); outl(tp->csr6 | 0x2002, ioaddr + CSR6); outl(0, ioaddr + CSR2); /* Rx poll demand */ if (tulip_debug > 2) { - printk(KERN_DEBUG "%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR13 %8.8x.\n", + printk(KERN_DEBUG "%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR6 %8.8x.\n", dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR5), - inl(ioaddr + CSR13)); + inl(ioaddr + CSR6)); } /* Set the timer to switch to check for link beat and perhaps switch to an alternate media type. */ init_timer(&tp->timer); - tp->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */ + tp->timer.expires = RUN_AT(5*HZ); tp->timer.data = (unsigned long)dev; - tp->timer.function = &tulip_timer; /* timer handler */ + tp->timer.function = tulip_tbl[tp->chip_id].media_timer; add_timer(&tp->timer); -#ifdef CONFIG_NET_HW_FLOWCONTROL - tp->fc_bit = netdev_register_fc(dev, tulip_xon); -#endif -#ifdef CONFIG_NET_FASTROUTE - dev->tx_semaphore = 1; -#endif return 0; } /* Set up the transceiver control registers for the selected media type. */ static void select_media(struct device *dev, int startup) { - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; struct tulip_private *tp = (struct tulip_private *)dev->priv; struct mediatable *mtable = tp->mtable; u32 new_csr6; @@ -1261,8 +1392,8 @@ static void select_media(struct device *dev, int startup) switch (mleaf->type) { case 0: /* 21140 non-MII xcvr. */ if (tulip_debug > 1) - printk(KERN_DEBUG "%s: Using a 21140 non-MII transceiver with control" - " setting %2.2x.\n", + printk(KERN_DEBUG "%s: Using a 21140 non-MII transceiver" + " with control setting %2.2x.\n", dev->name, p[1]); dev->if_port = p[0]; if (startup) @@ -1270,26 +1401,11 @@ static void select_media(struct device *dev, int startup) outl(p[1], ioaddr + CSR12); new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18); break; - case 1: - if (startup) { - outl(mtable->csr12dir | 0x100, ioaddr + CSR12); - dev->if_port = 11; - if (tulip_debug > 2) - printk(KERN_DEBUG "%s: Doing a reset sequence of length %d.\n", - dev->name, p[2 + p[1]]); - for (i = 0; i < p[2 + p[1]]; i++) - outl(p[3 + p[1] + i], ioaddr + CSR12); - if (tulip_debug > 2) - printk(KERN_DEBUG "%s Doing a transceiver setup sequence of length %d.\n", - dev->name, p[1]); - for (i = 0; i < p[1]; i++) - outl(p[2 + i], ioaddr + CSR12); - } - check_mii = 1; - new_csr6 = 0x020C0000; - break; case 2: case 4: { - u16 *setup = (u16*)&p[1]; + u16 setup[3]; + for (i = 0; i < 3; i++) + setup[i] = get_u16(&p[i*2 + 1]); + dev->if_port = p[0] & 15; if (tulip_debug > 1) printk(KERN_DEBUG "%s: 21142 non-MII %s transceiver control %4.4x/%4.4x.\n", @@ -1299,59 +1415,74 @@ static void select_media(struct device *dev, int startup) outl(setup[1], ioaddr + CSR14); outl(setup[2], ioaddr + CSR15); outl(setup[0], ioaddr + CSR13); - setup += 3; - } else { + for (i = 0; i < 3; i++) /* Re-fill setup[] */ + setup[i] = get_u16(&p[i*2 + 7]); + } else if (dev->if_port <= 4) { outl(0, ioaddr + CSR13); outl(t21142_csr14[dev->if_port], ioaddr + CSR14); outl(t21142_csr15[dev->if_port], ioaddr + CSR15); outl(t21142_csr13[dev->if_port], ioaddr + CSR13); + } else { + outl(0, ioaddr + CSR14); + outl(8, ioaddr + CSR15); + outl(0, ioaddr + CSR13); } outl(setup[0]<<16, ioaddr + CSR15); /* Direction */ outl(setup[1]<<16, ioaddr + CSR15); /* Data */ if (mleaf->type == 4) - new_csr6 = 0x02000000 | ((setup[2] & 0x71) << 18); + new_csr6 = 0x82020000 | ((setup[2] & 0x71) << 18); else new_csr6 = 0x82420000; break; } - case 3: { + case 1: case 3: { + int phy_num = p[0]; int init_length = p[1]; - u16 * init_sequence = (u16*)(p + 2); - int reset_length = p[2 + init_length*2]; - u16 * reset_sequence = (u16*)&p[3 + init_length*2]; + u16 *misc_info; + u16 to_advertise; dev->if_port = 11; - if (startup) { - if (tulip_debug > 2) - printk(KERN_DEBUG "%s: Doing a 21142 reset sequence of length %d.\n", - dev->name, reset_length); - for (i = 0; i < reset_length; i++) - outl(reset_sequence[i] << 16, ioaddr + CSR15); - } - if (tulip_debug > 2) - printk(KERN_DEBUG "%s: Doing a 21142 xcvr setup sequence of length %d.\n", - dev->name, init_length); - for (i = 0; i < init_length; i++) - outl(init_sequence[i] << 16, ioaddr + CSR15); check_mii = 1; - new_csr6 = 0x020C0000; + new_csr6 = 0x020E0000; + if (mleaf->type == 3) { /* 21142 */ + u16 *init_sequence = (u16*)(p+2); + u16 *reset_sequence = &((u16*)(p+3))[init_length]; + int reset_length = p[2 + init_length*2]; + misc_info = reset_sequence + reset_length; + if (startup) + for (i = 0; i < reset_length; i++) + outl(get_u16(&reset_sequence[i]) << 16, ioaddr + CSR15); + for (i = 0; i < init_length; i++) + outl(get_u16(&init_sequence[i]) << 16, ioaddr + CSR15); + } else { + u8 *init_sequence = p + 2; + u8 *reset_sequence = p + 3 + init_length; + int reset_length = p[2 + init_length]; + misc_info = (u16*)(reset_sequence + reset_length); + if (startup) { + outl(mtable->csr12dir | 0x100, ioaddr + CSR12); + for (i = 0; i < reset_length; i++) + outl(reset_sequence[i], ioaddr + CSR12); + } + for (i = 0; i < init_length; i++) + outl(init_sequence[i], ioaddr + CSR12); + } + to_advertise = (get_u16(&misc_info[1]) & tp->to_advertise) | 1; + tp->advertising[phy_num] = to_advertise; + if (tulip_debug > 1 || 1) + printk(KERN_DEBUG "%s: Advertising %4.4x on PHY %d (%d).\n", + dev->name, to_advertise, phy_num, tp->phys[phy_num]); + /* Bogus: put in by a committee? */ + mdio_write(dev, tp->phys[phy_num], 4, to_advertise); break; } default: - new_csr6 = 0x020C0000; + new_csr6 = 0x020E0000; } if (tulip_debug > 1) printk(KERN_DEBUG "%s: Using media type %s, CSR12 is %2.2x.\n", dev->name, medianame[dev->if_port], inl(ioaddr + CSR12) & 0xff); - } else if (tp->chip_id == DC21140) { - /* Set media type to MII @ 100mbps: 0x020C0000 */ - new_csr6 = 0x020C0000; - dev->if_port = 11; - if (tulip_debug > 1) { - printk(KERN_DEBUG "%s: Unknown media control, assuming MII, CSR12 %2.2x.\n", - dev->name, inl(ioaddr + CSR12) & 0xff); - } } else if (tp->chip_id == DC21041) { if (tulip_debug > 1) printk(KERN_DEBUG "%s: 21041 using media %s, CSR12 is %4.4x.\n", @@ -1363,14 +1494,24 @@ static void select_media(struct device *dev, int startup) outl(t21041_csr13[dev->if_port], ioaddr + CSR13); new_csr6 = 0x80020000; } else if (tp->chip_id == LC82C168) { - if (startup) - dev->if_port = 3; + if (startup && ! tp->medialock) + dev->if_port = tp->mii_cnt ? 11 : 0; if (tulip_debug > 1) - printk(KERN_DEBUG "%s: LiteOn PHY status is %3.3x, CSR12 %4.4x," + printk(KERN_DEBUG "%s: PNIC PHY status is %3.3x, CSR12 %4.4x," " media %s.\n", dev->name, inl(ioaddr + 0xB8), inl(ioaddr + CSR12), medianame[dev->if_port]); - if (dev->if_port == 3 || dev->if_port == 5) { + if (tp->mii_cnt) { + new_csr6 = 0x812C0000; + outl(0x0001, ioaddr + CSR15); + outl(0x0201B07A, ioaddr + 0xB8); + } else if (startup) { + /* Start with 10mbps to do autonegotiation. */ + outl(0x32, ioaddr + CSR12); + new_csr6 = 0x00420000; + outl(0x0001B078, ioaddr + 0xB8); + outl(0x0201B078, ioaddr + 0xB8); + } else if (dev->if_port == 3 || dev->if_port == 5) { outl(0x33, ioaddr + CSR12); new_csr6 = 0x01860000; if (startup) @@ -1382,7 +1523,7 @@ static void select_media(struct device *dev, int startup) new_csr6 = 0x00420000; outl(0x1F078, ioaddr + 0xB8); } - } else { /* 21040 */ + } else if (tp->chip_id == DC21040) { /* 21040 */ /* Turn on the xcvr interface. */ int csr12 = inl(ioaddr + CSR12); if (tulip_debug > 1) @@ -1393,6 +1534,23 @@ static void select_media(struct device *dev, int startup) outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11); outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */ outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13); + } else { /* Unknown chip type with no media table. */ + if (tp->default_port == 0) + if (tp->mii_cnt) { + dev->if_port = 11; + } else + dev->if_port = 3; + if (media_cap[dev->if_port] & MediaIsMII) { + new_csr6 = 0x020E0000; + } else if (media_cap[dev->if_port] & MediaIsFx) { + new_csr6 = 0x028600000; + } else + new_csr6 = 0x038600000; + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: No media description table, assuming " + "%s transceiver, CSR12 %2.2x.\n", + dev->name, medianame[dev->if_port], + inl(ioaddr + CSR12)); } tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | (tp->full_duplex ? 0x0200 : 0); @@ -1403,7 +1561,7 @@ static void tulip_timer(unsigned long data) { struct device *dev = (struct device *)data; struct tulip_private *tp = (struct tulip_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; u32 csr12 = inl(ioaddr + CSR12); int next_tick = 0; @@ -1467,167 +1625,366 @@ static void tulip_timer(unsigned long data) break; } break; - case LC82C168: { + case DC21140: case DC21142: case MX98713: default: { + struct medialeaf *mleaf; + unsigned char *p; + if (tp->mtable == NULL) { /* No EEPROM info, use generic code. */ + /* Not much that can be done. + Assume this a generic MII or SYM transceiver. */ + next_tick = 60*HZ; + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: network media monitor CSR6 %8.8x " + "CSR12 0x%2.2x.\n", + dev->name, inl(ioaddr + CSR6), csr12 & 0xff); + break; + } + mleaf = &tp->mtable->mleaf[tp->cur_index]; + p = mleaf->leafdata; + switch (mleaf->type) { + case 0: case 4: { + /* Type 0 serial or 4 SYM transceiver. Check the link beat bit. */ + int offset = mleaf->type == 4 ? 5 : 2; + s8 bitnum = p[offset]; + if (p[offset+1] & 0x80) { + if (tulip_debug > 1) + printk(KERN_DEBUG"%s: Transceiver monitor tick " + "CSR12=%#2.2x, no media sense.\n", + dev->name, csr12); + if (mleaf->type == 4) { + if (mleaf->media == 3 && (csr12 & 0x02)) + goto select_next_media; + } + break; + } + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: Transceiver monitor tick: CSR12=%#2.2x" + " bit %d is %d, expecting %d.\n", + dev->name, csr12, (bitnum >> 1) & 7, + (csr12 & (1 << ((bitnum >> 1) & 7))) != 0, + (bitnum >= 0)); + /* Check that the specified bit has the proper value. */ + if ((bitnum < 0) != + ((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) { + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: Link beat detected for %s.\n", dev->name, + medianame[mleaf->media]); + if ((p[2] & 0x61) == 0x01) /* Bogus Znyx board. */ + goto actually_mii; + break; + } + if (tp->medialock) + break; + select_next_media: + if (--tp->cur_index < 0) { + /* We start again, but should instead look for default. */ + tp->cur_index = tp->mtable->leafcount - 1; + } + dev->if_port = tp->mtable->mleaf[tp->cur_index].media; + if (media_cap[dev->if_port] & MediaIsFD) + goto select_next_media; /* Skip FD entries. */ + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: No link beat on media %s," + " trying transceiver type %s.\n", + dev->name, medianame[mleaf->media & 15], + medianame[tp->mtable->mleaf[tp->cur_index].media]); + select_media(dev, 0); + /* Restart the transmit process. */ + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + next_tick = (24*HZ)/10; + break; + } + case 1: case 3: { /* 21140, 21142 MII */ + int mii_reg1, mii_reg5; + actually_mii: + mii_reg1 = mdio_read(dev, tp->phys[0], 1); + mii_reg5 = mdio_read(dev, tp->phys[0], 5); + if (tulip_debug > 1) + printk(KERN_INFO "%s: MII status %4.4x, Link partner report " + "%4.4x, CSR12 %2.2x, %cD.\n", + dev->name, mii_reg1, mii_reg5, csr12, + tp->full_duplex ? 'F' : 'H'); + if (mii_reg1 != 0xffff && (mii_reg1 & 0x0004) == 0) { + int new_reg1 = mdio_read(dev, tp->phys[0], 1); + if ((new_reg1 & 0x0004) == 0) { + printk(KERN_INFO "%s: No link beat on the MII interface," + " status then %4.4x now %4.4x.\n", + dev->name, mii_reg1, new_reg1); + if (tp->mtable && tp->mtable->has_nonmii) + goto select_next_media; + } + } + if (mii_reg5 == 0xffff || mii_reg5 == 0x0000) + ; /* No MII device or no link partner report */ + else if (tp->full_duplex_lock) + ; + else { + int negotiated = mii_reg5 & tp->advertising[0]; + int duplex = ((negotiated & 0x0100) != 0 + || (negotiated & 0x00C0) == 0x0040); + /* 100baseTx-FD or 10T-FD, but not 100-HD */ + if (tp->full_duplex != duplex) { + tp->full_duplex = duplex; + if (tp->full_duplex) + tp->csr6 |= 0x0200; + else + tp->csr6 &= ~0x0200; + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + if (tulip_debug > 0) /* Gurppp, should be >1 */ + printk(KERN_INFO "%s: Setting %s-duplex based on MII" + " Xcvr #%d parter capability of %4.4x.\n", + dev->name, tp->full_duplex ? "full" : "half", + tp->phys[0], mii_reg5); + } + } + next_tick = 60*HZ; + break; + } + case 2: /* 21142 serial block has no link beat. */ + default: + break; + } + } + break; + } + if (next_tick) { + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); + } +} + +/* Handle the 21143 uniquely: do autoselect with NWay, not the EEPROM list + of available transceivers. */ +static void t21142_timer(unsigned long data) +{ + struct device *dev = (struct device *)data; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + int csr12 = inl(ioaddr + CSR12); + int next_tick = 60*HZ; + int new_csr6 = 0; + + if (tulip_debug > 1) + printk(KERN_INFO"%s: 21142 negotiation status %8.8x, %s.\n", + dev->name, csr12, medianame[dev->if_port]); + if (dev->if_port == 3) { + if (csr12 & 2) { /* No 100mbps link beat, revert to 10mbps. */ + new_csr6 = 0x82420200; + outl(new_csr6, ioaddr + CSR6); + outl(0x0000, ioaddr + CSR13); + outl(0x0003FFFF, ioaddr + CSR14); + outl(0x0008, ioaddr + CSR15); + outl(0x0001, ioaddr + CSR13); + outl(0x1301, ioaddr + CSR12); /* Start NWay. */ + } + } else if ((csr12 & 0x7000) != 0x5000) { + /* Negotiation failed. Search media types. */ + if (tulip_debug > 1) + printk(KERN_INFO"%s: 21142 negotiation failed, status %8.8x.\n", + dev->name, csr12); + if (!(csr12 & 4)) { /* 10mbps link beat good. */ + new_csr6 = 0x82420000; + dev->if_port = 0; + outl(0, ioaddr + CSR13); + outl(0x0003FFFF, ioaddr + CSR14); + outl(t21142_csr15[dev->if_port], ioaddr + CSR15); + outl(t21142_csr13[dev->if_port], ioaddr + CSR13); + } else if (csr12 & 0x100) { + new_csr6 = 0x82420200; + dev->if_port = 2; + outl(0, ioaddr + CSR13); + outl(0x0003FFFF, ioaddr + CSR14); + outl(0x0008, ioaddr + CSR15); + outl(0x0001, ioaddr + CSR13); + } else { + /* Select 100mbps port to check for link beat. */ + new_csr6 = 0x83860000; + dev->if_port = 3; + outl(0, ioaddr + CSR13); + outl(0x0003FF7F, ioaddr + CSR14); + outl(8, ioaddr + CSR15); + outl(1, ioaddr + CSR13); + } + if (tulip_debug > 1) + printk(KERN_INFO"%s: Testing new 21142 media %s.\n", + dev->name, medianame[dev->if_port]); + if (new_csr6 != (tp->csr6 & ~0x00D5)) { + tp->csr6 &= 0x00D5; + tp->csr6 |= new_csr6; + outl(0x0301, ioaddr + CSR12); + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + } + } + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); +} + +static void t21142_lnk_change( struct device *dev) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + int csr12 = inl(ioaddr + CSR12); + + if (tulip_debug > 1) + printk(KERN_INFO"%s: 21142 link status interrupt %8.8x, CSR5 %x.\n", + dev->name, csr12, inl(ioaddr + CSR5)); + + if ((csr12 & 0x7000) == 0x5000) { + if (csr12 & 0x01800000) { + /* Switch to 100mbps mode. */ + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + if (csr12 & 0x01000000) { + dev->if_port = 5; + tp->csr6 = 0x83860200; + } else { + dev->if_port = 3; + tp->csr6 = 0x83860000; + } + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + } /* Else 10baseT-FD is handled automatically. */ + } else if (dev->if_port == 3) { + if (!(csr12 & 2)) + printk(KERN_INFO"%s: 21142 100baseTx link beat good.\n", + dev->name); + else + dev->if_port = 0; + } else if (dev->if_port == 0) { + if (!(csr12 & 4)) + printk(KERN_INFO"%s: 21142 10baseT link beat good.\n", + dev->name); + } else if (!(csr12 & 4)) { /* 10mbps link beat good. */ + printk(KERN_INFO"%s: 21142 10mpbs sensed media.\n", + dev->name); + dev->if_port = 0; + } else { /* 100mbps link beat good. */ + printk(KERN_INFO"%s: 21142 100baseTx sensed media.\n", + dev->name); + dev->if_port = 3; + tp->csr6 = 0x83860000; + outl(0x0003FF7F, ioaddr + CSR14); + outl(0x0301, ioaddr + CSR12); + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + } +} + + +static void mxic_timer(unsigned long data) +{ + struct device *dev = (struct device *)data; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + int next_tick = 60*HZ; + + if (tulip_debug > 3) { + printk(KERN_INFO"%s: MXIC negotiation status %8.8x.\n", dev->name, + inl(ioaddr + CSR12)); + } + if (next_tick) { + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); + } +} + +static void pnic_timer(unsigned long data) +{ + struct device *dev = (struct device *)data; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + int csr12 = inl(ioaddr + CSR12); + int next_tick = 60*HZ; + int new_csr6 = tp->csr6 & ~0x40C40200; + + if (media_cap[dev->if_port] & MediaIsMII) { + int negotiated = mdio_read(dev, tp->phys[0], 5) & tp->advertising[0]; + + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: LC82C168 negotiated capability %8.8x, " + "CSR5 %8.8x.\n", + dev->name, negotiated, inl(ioaddr + CSR5)); + + if (negotiated & 0x0380) /* 10 vs 100mbps */ + new_csr6 |= 0x812E0000; + else + new_csr6 |= 0x816E0000; + if (((negotiated & 0x0300) == 0x0100) /* Duplex */ + || (negotiated & 0x00C0) == 0x0040 + || tp->full_duplex_lock) { + tp->full_duplex = 1; + new_csr6 |= 0x0200; + } + if (tulip_debug > 1) + printk(KERN_DEBUG "%s: LC82C168 MII PHY status %4.4x, Link " + "partner report %4.4x, csr6 %8.8x/%8.8x.\n", + dev->name, mdio_read(dev, tp->phys[0], 1), negotiated, + tp->csr6, inl(ioaddr + CSR6)); + } else { int phy_reg = inl(ioaddr + 0xB8); + int csr5 = inl(ioaddr + CSR5); + if (tulip_debug > 1) printk(KERN_DEBUG "%s: LC82C168 phy status %8.8x, CSR5 %8.8x.\n", - dev->name, phy_reg, inl(ioaddr + CSR5)); + dev->name, phy_reg, csr5); + if (phy_reg & 0x04000000) { /* Remote link fault */ - outl(0x0201F078, ioaddr + 0xB8); - next_tick = (24*HZ)/10; - } else - next_tick = 10*HZ; + /*outl(0x0201F078, ioaddr + 0xB8);*/ + next_tick = 3*HZ; + } if (inl(ioaddr + CSR5) & TPLnkFail) { /* 100baseTx link beat */ if (tulip_debug > 1) printk(KERN_DEBUG "%s: %s link beat failed, CSR12 %4.4x, " "CSR5 %8.8x, PHY %3.3x.\n", dev->name, medianame[dev->if_port], csr12, inl(ioaddr + CSR5), inl(ioaddr + 0xB8)); - if (dev->if_port == 0) { + if (tp->medialock) { + } else if (dev->if_port == 0) { dev->if_port = 3; - } else - dev->if_port = 0; - next_tick = (24*HZ)/10; - select_media(dev, 0); - outl(tp->csr6 | 0x0002, ioaddr + CSR6); - outl(tp->csr6 | 0x2002, ioaddr + CSR6); - dev->trans_start = jiffies; - } - break; - } - case DC21140: case DC21142: { - struct medialeaf *mleaf; - unsigned char *p; - if (tp->mtable == NULL) { /* No EEPROM info, use generic code. */ - /* Assume this is like a SMC card, and check its link beat bit. */ - if ((dev->if_port == 0 && (csr12 & 0x0080)) || - (dev->if_port == 1 && (csr12 & 0x0040) == 0)) { - dev->if_port ^= 1; - /* Stop the transmit process. */ - tp->csr6 = (dev->if_port ? 0x03860000 : 0x02420000); - outl(tp->csr6 | 0x0002, ioaddr + CSR6); - printk(KERN_INFO "%s: link beat timed out, CSR12 is 0x%2.2x, switching to" - " %s media.\n", dev->name, - csr12 & 0xff, - dev->if_port ? "100baseTx" : "10baseT"); - outl(tp->csr6 | 0xA002, ioaddr + CSR6); - dev->trans_start = jiffies; - next_tick = (24*HZ)/10; + outl(0x33, ioaddr + CSR12); + new_csr6 = 0x01860000; + outl(0x1F868, ioaddr + 0xB8); } else { - next_tick = 10*HZ; - if (tulip_debug > 2) - printk(KERN_DEBUG "%s: network media monitor 0x%2.2x, link" - " beat detected as %s.\n", dev->name, - csr12 & 0xff, - dev->if_port ? "100baseTx" : "10baseT"); + dev->if_port = 0; + outl(0x32, ioaddr + CSR12); + new_csr6 = 0x00420000; + outl(0x1F078, ioaddr + 0xB8); } - break; + new_csr6 |= (tp->csr6 & 0xfdff); + next_tick = 3*HZ; + } else + new_csr6 = tp->csr6; + if (tp->full_duplex_lock || (phy_reg & 0x30000000) != 0) { + tp->full_duplex = 1; + new_csr6 |= 0x00000200; } - mleaf = &tp->mtable->mleaf[tp->cur_index]; - p = mleaf->leafdata; - switch (mleaf->type) { - case 0: case 4: { - /* Type 0 non-MII or #4 SYM transceiver. Check the link beat bit. */ - s8 bitnum = p[mleaf->type == 4 ? 5 : 2]; - if (tulip_debug > 2) - printk(KERN_DEBUG "%s: Transceiver monitor tick: CSR12=%#2.2x bit %d is" - " %d, expecting %d.\n", - dev->name, csr12, (bitnum >> 1) & 7, - (csr12 & (1 << ((bitnum >> 1) & 7))) != 0, - (bitnum >= 0)); - /* Check that the specified bit has the proper value. */ - if ((bitnum < 0) != - ((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) { - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: Link beat detected for %s.\n", dev->name, - medianame[mleaf->media]); - break; - } - if (tp->medialock) - break; - select_next_media: - if (--tp->cur_index < 0) { - /* We start again, but should instead look for default. */ - tp->cur_index = tp->mtable->leafcount - 1; - } - dev->if_port = tp->mtable->mleaf[tp->cur_index].media; - if (media_fd[dev->if_port]) - goto select_next_media; /* Skip FD entries. */ - if (tulip_debug > 1) - printk(KERN_DEBUG "%s: No link beat on media %s," - " trying transceiver type %s.\n", - dev->name, medianame[mleaf->media & 15], - medianame[tp->mtable->mleaf[tp->cur_index].media]); - select_media(dev, 0); - /* Restart the transmit process. */ - outl(tp->csr6 | 0x0002, ioaddr + CSR6); - outl(tp->csr6 | 0x2002, ioaddr + CSR6); - next_tick = (24*HZ)/10; - break; - } - case 1: case 3: /* 21140, 21142 MII */ - { - int mii_reg1 = mdio_read(ioaddr, tp->phys[0], 1); - int mii_reg5 = mdio_read(ioaddr, tp->phys[0], 5); - printk(KERN_INFO "%s: MII monitoring tick: CSR12 %2.2x, " - "MII status %4.4x, Link partner report %4.4x.\n", - dev->name, csr12, mii_reg1, mii_reg5); -#ifdef notdef - if (mii_reg1 != 0xffff && (mii_reg1 & 0x0004) == 0) - goto select_next_media; -#else - if (mii_reg1 != 0xffff && (mii_reg1 & 0x0004) == 0) - printk(KERN_INFO "%s: No link beat on the MII interface, " - "status then %4.4x now %4.4x.\n", - dev->name, mii_reg1, - mdio_read(ioaddr, tp->phys[0], 1)); -#endif - if (mii_reg5 == 0xffff || mii_reg5 == 0x0000) - ; /* No MII device or no link partner report */ - else if (tp->full_duplex_lock) - ; - else if ((mii_reg5 & 0x0100) != 0 - || (mii_reg5 & 0x00C0) == 0x0040) { - /* 100baseTx-FD or 10T-FD, but not 100-HD */ - if (tp->full_duplex == 0) { - tp->full_duplex = 1; - tp->csr6 |= 0x0200; - outl(tp->csr6 | 0x0002, ioaddr + CSR6); - outl(tp->csr6 | 0x2002, ioaddr + CSR6); - } - if (tulip_debug > 0) /* Gurppp, should be >1 */ - printk(KERN_INFO "%s: Setting %s-duplex based on MII" - " Xcvr #%d parter capability of %4.4x.\n", - dev->name, full_duplex ? "full" : "half", - tp->phys[0], mii_reg5); - } - } - break; - case 2: /* 21142 serial block has no link beat. */ - default: - break; - } - } - break; - default: /* Invalid chip type. */ - break; } - if (next_tick) { - tp->timer.expires = RUN_AT(next_tick); - add_timer(&tp->timer); + if (tp->csr6 != new_csr6) { + tp->csr6 = new_csr6; + outl(tp->csr6 | 0x0002, ioaddr + CSR6); /* Restart Tx */ + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + dev->trans_start = jiffies; + if (tulip_debug > 0) /* Gurppp, should be >1 */ + printk(KERN_INFO "%s: Changing PNIC configuration to %s-duplex, " + "CSR6 %8.8x.\n", + dev->name, tp->full_duplex ? "full" : "half", new_csr6); } + tp->timer.expires = RUN_AT(next_tick); + add_timer(&tp->timer); } static void tulip_tx_timeout(struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; - if (tp->mtable && tp->mtable->has_mii) { - /* Do nothing -- the media monitor should handle this. */ - if (tulip_debug > 1) - printk(KERN_WARNING "%s: Transmit timeout using MII device.\n", - dev->name); + if (media_cap[dev->if_port] & MediaIsMII) { + /* Do nothing -- the media monitor should handle this. */ + if (tulip_debug > 1) + printk(KERN_WARNING "%s: Transmit timeout using MII device.\n", + dev->name); + dev->trans_start = jiffies; + return; } else if (tp->chip_id == DC21040) { if (inl(ioaddr + CSR12) & 0x0002) { printk(KERN_INFO "%s: transmit timed out, switching to %s media.\n", @@ -1637,20 +1994,6 @@ static void tulip_tx_timeout(struct device *dev) } dev->trans_start = jiffies; return; - } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142) { - /* Stop the transmit process. */ - outl(tp->csr6 | 0x0002, ioaddr + CSR6); - dev->if_port ^= 1; - printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, " - "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n", - dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12), - inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15)); - printk(KERN_WARNING "%s: transmit timed out, switching to %s media.\n", - dev->name, dev->if_port ? "100baseTx" : "10baseT"); - outl(tp->csr6 | 0x2002, ioaddr + CSR6); - tp->stats.tx_errors++; - dev->trans_start = jiffies; - return; } else if (tp->chip_id == DC21041) { u32 csr12 = inl(ioaddr + CSR12); @@ -1659,19 +2002,38 @@ static void tulip_tx_timeout(struct device *dev) dev->name, inl(ioaddr + CSR5), csr12, inl(ioaddr + CSR13), inl(ioaddr + CSR14)); tp->mediasense = 1; - - if (dev->if_port == 1 || dev->if_port == 2) { + if (dev->if_port == 1 || dev->if_port == 2) if (csr12 & 0x0004) { dev->if_port = 2 - dev->if_port; } else dev->if_port = 0; - } else + else dev->if_port = 1; - select_media(dev, 0); tp->stats.tx_errors++; dev->trans_start = jiffies; return; + } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142 + || tp->chip_id == MX98713) { + /* Stop the transmit process. */ + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + printk(KERN_WARNING "%s: 21140 transmit timed out, status %8.8x, " + "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n", + dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12), + inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15)); + if (tp->mtable) { + if (--tp->cur_index < 0) { + /* We start again, but should instead look for default. */ + tp->cur_index = tp->mtable->leafcount - 1; + } + select_media(dev, 0); + printk(KERN_WARNING "%s: transmit timed out, switching to %s media.\n", + dev->name, dev->if_port ? "100baseTx" : "10baseT"); + } + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + tp->stats.tx_errors++; + dev->trans_start = jiffies; + return; } else printk(KERN_WARNING "%s: transmit timed out, status %8.8x, CSR12 %8.8x," " resetting...\n", @@ -1753,40 +2115,12 @@ tulip_start_xmit(struct sk_buff *skb, struct device *dev) int entry; u32 flag; -#ifdef ORIGINAL_TEXT -#ifndef final_version - if (skb == NULL || skb->len <= 0) { - printk(KERN_ERR "%s: Obsolete driver layer request made: skbuff==NULL.\n", - dev->name); - dev_tint(dev); - return 0; - } -#endif -#endif - -#ifdef CONFIG_NET_FASTROUTE - cli(); - if (xchg(&dev->tx_semaphore,0) == 0) { - sti(); - /* With new queueing algorithm returning 1 when dev->tbusy == 0 - should not result in lockups, but I am still not sure. --ANK - */ - if (net_ratelimit()) - printk(KERN_CRIT "Please check: are you still alive?\n"); - return 1; - } -#endif /* Block a timer-based transmit from overlapping. This could better be done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { -#ifdef CONFIG_NET_FASTROUTE - sti(); -#endif - if (jiffies - dev->trans_start >= TX_TIMEOUT) - tulip_tx_timeout(dev); -#ifdef CONFIG_NET_FASTROUTE - dev->tx_semaphore = 1; -#endif + if (jiffies - dev->trans_start < TX_TIMEOUT) + return 1; + tulip_tx_timeout(dev); return 1; } @@ -1816,7 +2150,6 @@ tulip_start_xmit(struct sk_buff *skb, struct device *dev) if (entry == TX_RING_SIZE-1) flag |= 0xe2000000; - tp->stats.tx_bytes += skb->len; tp->tx_ring[entry].length = skb->len | flag; tp->tx_ring[entry].status = 0x80000000; /* Pass ownership to the chip. */ tp->cur_tx++; @@ -1824,10 +2157,6 @@ tulip_start_xmit(struct sk_buff *skb, struct device *dev) outl(0, dev->base_addr + CSR1); dev->trans_start = jiffies; -#ifdef CONFIG_NET_FASTROUTE - dev->tx_semaphore = 1; - sti(); -#endif return 0; } @@ -1842,8 +2171,9 @@ static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *reg struct device *dev = (struct device *)(irq2dev_map[irq]); #endif - struct tulip_private *lp; - int csr5, ioaddr, boguscnt = 12; + struct tulip_private *tp; + long ioaddr; + int csr5, work_budget = max_interrupt_work; if (dev == NULL) { printk (KERN_ERR" tulip_interrupt(): irq %d for unknown device.\n", @@ -1852,11 +2182,21 @@ static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *reg } ioaddr = dev->base_addr; - lp = (struct tulip_private *)dev->priv; - if (dev->interrupt) + tp = (struct tulip_private *)dev->priv; + if (test_and_set_bit(0, (void*)&tp->interrupt)) { +#ifdef SMP_CHECK + printk(KERN_ERR "%s: Re-entering the interrupt handler with proc %d," + " proc %d already handling.\n", dev->name, + tp->smp_proc_id, hard_smp_processor_id()); +#else printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); - +#endif + return; + } dev->interrupt = 1; +#ifdef SMP_CHECK + tp->smp_proc_id = hard_smp_processor_id(); +#endif do { csr5 = inl(ioaddr + CSR5); @@ -1867,23 +2207,24 @@ static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *reg printk(KERN_DEBUG "%s: interrupt csr5=%#8.8x new csr5=%#8.8x.\n", dev->name, csr5, inl(dev->base_addr + CSR5)); - if ((csr5 & 0x00018000) == 0) + if ((csr5 & (NormalIntr|AbnormalIntr)) == 0) break; - if (csr5 & RxIntr) - tulip_rx(dev); + if (csr5 & (RxIntr | RxNoBuf)) + work_budget -= tulip_rx(dev); if (csr5 & (TxNoBuf | TxDied | TxIntr)) { - int dirty_tx; + unsigned int dirty_tx; - for (dirty_tx = lp->dirty_tx; dirty_tx < lp->cur_tx; dirty_tx++) { + for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0; + dirty_tx++) { int entry = dirty_tx % TX_RING_SIZE; - int status = lp->tx_ring[entry].status; + int status = tp->tx_ring[entry].status; if (status < 0) break; /* It still hasn't been Txed */ /* Check for Rx filter setup frames. */ - if (lp->tx_skbuff[entry] == NULL) + if (tp->tx_skbuff[entry] == NULL) continue; if (status & 0x8000) { @@ -1893,74 +2234,104 @@ static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *reg printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", dev->name, status); #endif - lp->stats.tx_errors++; - if (status & 0x4104) lp->stats.tx_aborted_errors++; - if (status & 0x0C00) lp->stats.tx_carrier_errors++; - if (status & 0x0200) lp->stats.tx_window_errors++; - if (status & 0x0002) lp->stats.tx_fifo_errors++; - if ((status & 0x0080) && lp->full_duplex == 0) - lp->stats.tx_heartbeat_errors++; + tp->stats.tx_errors++; + if (status & 0x4104) tp->stats.tx_aborted_errors++; + if (status & 0x0C00) tp->stats.tx_carrier_errors++; + if (status & 0x0200) tp->stats.tx_window_errors++; + if (status & 0x0002) tp->stats.tx_fifo_errors++; + if ((status & 0x0080) && tp->full_duplex == 0) + tp->stats.tx_heartbeat_errors++; #ifdef ETHER_STATS - if (status & 0x0100) lp->stats.collisions16++; + if (status & 0x0100) tp->stats.collisions16++; #endif } else { #ifdef ETHER_STATS - if (status & 0x0001) lp->stats.tx_deferred++; + if (status & 0x0001) tp->stats.tx_deferred++; #endif - lp->stats.collisions += (status >> 3) & 15; - lp->stats.tx_packets++; +#if LINUX_VERSION_CODE > 0x20127 + tp->stats.tx_bytes += tp->tx_ring[entry].length & 0x7ff; +#endif + tp->stats.collisions += (status >> 3) & 15; + tp->stats.tx_packets++; } /* Free the original skb. */ - dev_kfree_skb(lp->tx_skbuff[entry]); - lp->tx_skbuff[entry] = 0; +#if (LINUX_VERSION_CODE > 0x20155) + dev_kfree_skb(tp->tx_skbuff[entry]); +#else + dev_kfree_skb(tp->tx_skbuff[entry], FREE_WRITE); +#endif + tp->tx_skbuff[entry] = 0; } #ifndef final_version - if (lp->cur_tx - dirty_tx > TX_RING_SIZE) { + if (tp->cur_tx - dirty_tx > TX_RING_SIZE) { printk(KERN_ERR "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", - dev->name, dirty_tx, lp->cur_tx, lp->tx_full); + dev->name, dirty_tx, tp->cur_tx, tp->tx_full); dirty_tx += TX_RING_SIZE; } #endif - if (lp->tx_full && dev->tbusy - && dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) { + if (tp->tx_full && dev->tbusy + && tp->cur_tx - dirty_tx < TX_RING_SIZE - 2) { /* The ring is no longer full, clear tbusy. */ - lp->tx_full = 0; + tp->tx_full = 0; dev->tbusy = 0; mark_bh(NET_BH); } - lp->dirty_tx = dirty_tx; + tp->dirty_tx = dirty_tx; + if (csr5 & TxDied) { + if (tulip_debug > 1) + printk(KERN_WARNING "%s: The transmitter stopped!" + " CSR5 is %x, CSR6 %x.\n", + dev->name, csr5, inl(ioaddr + CSR6)); + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); + } } /* Log errors. */ - if (csr5 & 0x8000) { /* Abnormal error summary bit. */ - if (csr5 & TxJabber) lp->stats.tx_errors++; + if (csr5 & AbnormalIntr) { /* Abnormal error summary bit. */ + if (csr5 & TxJabber) tp->stats.tx_errors++; if (csr5 & TxFIFOUnderflow) { - lp->csr6 |= 0x00200000; /* Reconfigure to store-n-forward. */ - /* Restart the transmit process. */ - outl(lp->csr6 | 0x0002, ioaddr + CSR6); - outl(lp->csr6 | 0x2002, ioaddr + CSR6); + if ((tp->csr6 & 0xC000) != 0xC000) + tp->csr6 += 0x4000; /* Bump up the Tx threshold */ + else + tp->csr6 |= 0x00200000; /* Store-n-forward. */ + /* Restart the transmit process. */ + outl(tp->csr6 | 0x0002, ioaddr + CSR6); + outl(tp->csr6 | 0x2002, ioaddr + CSR6); } if (csr5 & RxDied) { /* Missed a Rx frame. */ - lp->stats.rx_errors++; - lp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; + tp->stats.rx_errors++; + tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; } if (csr5 & TimerInt) { printk(KERN_ERR "%s: Something Wicked happened! %8.8x.\n", dev->name, csr5); /* Hmmmmm, it's not clear what to do here. */ } + if (csr5 & (TPLnkPass | TPLnkFail | 0x08000000) + && tp->chip_id == DC21142) { + if (tulip_debug > 1) + printk(KERN_INFO"%s: 21142 link change, CSR5 = %8.8x.\n", + dev->name, csr5); + t21142_lnk_change(dev); + } /* Clear all error sources, included undocumented ones! */ - outl(0x000f7ba, ioaddr + CSR5); + outl(0x0800f7ba, ioaddr + CSR5); } - if (--boguscnt < 0) { - printk(KERN_WARNING "%s: Too much work at interrupt, csr5=0x%8.8x.\n", - dev->name, csr5); - /* Clear all interrupt sources. */ - outl(0x0001ffff, ioaddr + CSR5); + if (--work_budget < 0) { + if (tulip_debug > 1) + printk(KERN_WARNING "%s: Too much work at interrupt, " + "csr5=0x%8.8x.\n", dev->name, csr5); + /* Acknowledge all interrupt sources. */ + outl(0x8001ffff, ioaddr + CSR5); +#ifdef notdef + /* Clear all but standard interrupt sources. */ + outl((~csr5) & 0x0001ebef, ioaddr + CSR7); +#endif break; } } while (1); @@ -1969,271 +2340,82 @@ static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *reg printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n", dev->name, inl(ioaddr + CSR5)); - /* Code that should never be run! Perhaps remove after testing.. */ - { - static int stopit = 10; - if (dev->start == 0 && --stopit < 0) { - printk(KERN_ERR "%s: Emergency stop, looping startup interrupt.\n" - KERN_ERR "%s: Disabling interrupt handler %d to avoid " - "locking up the machine.\n", - dev->name, dev->name, dev->irq); -#ifdef SA_SHIRQ - free_irq(irq, dev); -#else - free_irq(irq); -#endif - } - } - dev->interrupt = 0; + clear_bit(0, (void*)&tp->interrupt); return; } -#ifdef CONFIG_NET_FASTROUTE -/* DMAing cards are the most easy in this respect, - they are able to make fast route to any device. - - Now we allow to make it only to another ethernet card. - */ -static int tulip_accept_fastpath(struct device *dev, struct dst_entry *dst) -{ - struct device *odev = dst->dev; - - if (dst->ops->protocol != __constant_htons(ETH_P_IP)) - return -1; - if (odev->type != ARPHRD_ETHER || odev->accept_fastpath == NULL) - return -1; - - return 0; -} - -/* - Return values: - - 0 - packet has gone by fast path. - 1 - fast path is OK, but device deferred xmit. (semifast path) - - 2 - fast path is hit, but packet is a bit strange. (NI) - 3 - oom - - 4 - fast path miss. - */ - -static int tulip_fast_forward(struct device *dev, int entry, int len) -{ - struct tulip_private *lp = (struct tulip_private *)dev->priv; - struct sk_buff *skb = lp->rx_skbuff[entry]; - struct ethhdr *eth = (void*)skb->data; - - if (eth->h_proto == __constant_htons(ETH_P_IP)) { - struct rtable *rt; - struct iphdr *iph; - unsigned h; - - iph = (struct iphdr*)(skb->data + ETH_HLEN); - h = (*(u8*)&iph->daddr^*(u8*)&iph->saddr)&NETDEV_FASTROUTE_HMASK; - rt = (struct rtable*)(dev->fastpath[h]); - if (rt && - ((u16*)&iph->daddr)[0] == ((u16*)&rt->key.dst)[0] && - ((u16*)&iph->daddr)[1] == ((u16*)&rt->key.dst)[1] && - ((u16*)&iph->saddr)[0] == ((u16*)&rt->key.src)[0] && - ((u16*)&iph->saddr)[1] == ((u16*)&rt->key.src)[1] && - rt->u.dst.obsolete == 0) { - struct device *odev = rt->u.dst.dev; - - dev_fastroute_stat.hits++; - - if (*(u8*)iph != 0x45 || - (eth->h_dest[0]&1) || - !neigh_is_valid(rt->u.dst.neighbour) || - iph->ttl <= 1) - goto alas2; - - ip_decrease_ttl(iph); - - if (1) { - struct sk_buff *skb2 = DEV_ALLOC_SKB(PKT_BUF_SZ); - if (skb2 == NULL) - goto oom; - lp->rx_ring[entry].buffer1 = virt_to_bus(skb2->tail); - skb2->dev = dev; - lp->rx_skbuff[entry] = skb2; - } - - skb_put(skb, len); - - ip_statistics.IpInReceives++; - ip_statistics.IpForwDatagrams++; - - /* Could use hh cache */ - memcpy(eth->h_source, odev->dev_addr, 6); - memcpy(eth->h_dest, rt->u.dst.neighbour->ha, 6); - skb->dev = odev; - -#ifdef FAST_SKB_RECYCLE /* DO NOT DEFINE IT! READ COMMENT */ - /* We could use fast buffer recycling here if odev - is not DMAing. - - The only problem is that we must allocate skb2 - BEFORE we lose skb, otherwise we would make hole in - tulip rx array. Hence, to implement FAST_SKB_RECYCLE - we need always keep at least one skb in a safe place. - */ - atomic_inc(&skb->users); -#endif - - if (odev->tx_semaphore && - odev->tbusy == 0 && - odev->interrupt == 0 && - odev->hard_start_xmit(skb, odev) == 0) { -#ifdef FAST_SKB_RECYCLE - if (atomic_read(&skb->users) == 1) { - skb->tail = skb->data; - skb->len = 0; - } -#endif - dev_fastroute_stat.succeed++; - return 0; - } -#ifdef FAST_SKB_RECYCLE - atomic_dec(&skb->users); -#endif - - /* Otherwise... */ - skb->pkt_type = PACKET_FASTROUTE; - skb->nh.raw = skb->data + ETH_HLEN; - skb->protocol = __constant_htons(ETH_P_IP); - dev_fastroute_stat.deferred++; - return 1; - } - } - return 4; - -oom: - return 3; - -alas2: -#ifdef not_yet - skb->dst = dst_clone(&rt->u.dst); - return 2; -#else - return 4; -#endif -} -#endif - static int tulip_rx(struct device *dev) { - struct tulip_private *lp = (struct tulip_private *)dev->priv; - int entry = lp->cur_rx % RX_RING_SIZE; + struct tulip_private *tp = (struct tulip_private *)dev->priv; + int entry = tp->cur_rx % RX_RING_SIZE; + int rx_work_limit = tp->dirty_rx + RX_RING_SIZE - tp->cur_rx; + int work_done = 0; if (tulip_debug > 4) printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry, - lp->rx_ring[entry].status); + tp->rx_ring[entry].status); /* If we own the next entry, it's a new packet. Send it up. */ - while (lp->rx_ring[entry].status >= 0) { - int status = lp->rx_ring[entry].status; + while (tp->rx_ring[entry].status >= 0) { + s32 status = tp->rx_ring[entry].status; + if (--rx_work_limit < 0) + break; if ((status & 0x0300) != 0x0300) { if ((status & 0xffff) != 0x7fff) { /* Ingore earlier buffers. */ - printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " - "multiple buffers, status %8.8x!\n", dev->name, status); - lp->stats.rx_length_errors++; + if (tulip_debug > 1) + printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " + "multiple buffers, status %8.8x!\n", + dev->name, status); + tp->stats.rx_length_errors++; } } else if (status & 0x8000) { /* There was a fatal error. */ - lp->stats.rx_errors++; /* end of a packet.*/ - if (status & 0x0890) lp->stats.rx_length_errors++; - if (status & 0x0004) lp->stats.rx_frame_errors++; - if (status & 0x0002) lp->stats.rx_crc_errors++; - if (status & 0x0001) lp->stats.rx_fifo_errors++; + if (tulip_debug > 2) + printk(KERN_DEBUG "%s: Receive error, Rx status %8.8x.\n", + dev->name, status); + tp->stats.rx_errors++; /* end of a packet.*/ + if (status & 0x0890) tp->stats.rx_length_errors++; + if (status & 0x0004) tp->stats.rx_frame_errors++; + if (status & 0x0002) tp->stats.rx_crc_errors++; + if (status & 0x0001) tp->stats.rx_fifo_errors++; } else { - /* Malloc up new buffer, compatible with net-2e. */ /* Omit the four octet CRC from the length. */ - short pkt_len = (lp->rx_ring[entry].status >> 16) - 4; + short pkt_len = (status >> 16) - 4; struct sk_buff *skb; - int rx_in_place = 0; -#ifdef CONFIG_NET_HW_FLOWCONTROL - if (netdev_dropping) - goto throttle; -#endif - - skb = lp->rx_skbuff[entry]; - -#ifdef CONFIG_NET_FASTROUTE - switch (tulip_fast_forward(dev, entry, pkt_len)) { - case 0: - goto gone; - case 1: - goto semi_gone; - case 2: - break; - case 3: - skb = NULL; - goto memory_squeeze; - } -#endif /* Check if the packet is long enough to just accept without copying to a properly sized skbuff. */ - if (pkt_len > rx_copybreak) { - struct sk_buff *newskb; - char *temp; - - /* Get a fresh skbuff to replace the filled one. */ - newskb = DEV_ALLOC_SKB(PKT_BUF_SZ); - if (newskb == NULL) { - skb = NULL; /* No memory, drop the packet. */ - goto memory_squeeze; - } - /* Pass up the skb already on the Rx ring. */ - temp = skb_put(skb, pkt_len); - if (bus_to_virt(lp->rx_ring[entry].buffer1) != temp) - printk(KERN_ERR "%s: Internal consistency error -- the " - "skbuff addresses do not match" - " in tulip_rx: %p vs. %p / %p.\n", dev->name, - bus_to_virt(lp->rx_ring[entry].buffer1), - skb->head, temp); - rx_in_place = 1; - lp->rx_skbuff[entry] = newskb; - newskb->dev = dev; - /* Longword alignment required: do not skb_reserve(2)! */ - lp->rx_ring[entry].buffer1 = virt_to_bus(newskb->tail); - } else - skb = DEV_ALLOC_SKB(pkt_len + 2); -memory_squeeze: - if (skb == NULL) { - int i; - printk(KERN_WARNING "%s: Memory squeeze, deferring packet.\n", - dev->name); - /* Check that at least two ring entries are free. - If not, free one and mark stats->rx_dropped++. */ - for (i = 0; i < RX_RING_SIZE; i++) - if (lp->rx_ring[(entry+i) % RX_RING_SIZE].status < 0) - break; - - if (i > RX_RING_SIZE -2) { - lp->stats.rx_dropped++; - lp->rx_ring[entry].status = 0x80000000; - lp->cur_rx++; - } - break; - } - skb->dev = dev; - if (! rx_in_place) { - skb_reserve(skb, 2); /* 16 byte align the data fields */ -#if LINUX_VERSION_CODE < 0x20200 || defined(__alpha__) + if (pkt_len < rx_copybreak + && (skb = DEV_ALLOC_SKB(pkt_len+2)) != NULL) { + skb->dev = dev; + skb_reserve(skb, 2); /* 16 byte align the IP header */ +#if LINUX_VERSION_CODE < 0x10300 + memcpy(skb->data, tp->rx_ring[entry].buffer1, pkt_len); +#elif LINUX_VERSION_CODE < 0x20200 || defined(__alpha__) memcpy(skb_put(skb, pkt_len), - bus_to_virt(lp->rx_ring[entry].buffer1), pkt_len); -#else -#ifdef ORIGINAL_TEXT -#warning Code untested + bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len); #else -#error Code is wrong, and it has nothing to do with 2.2 :-) + eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1), + pkt_len, 0); + skb_put(skb, pkt_len); #endif - eth_copy_and_sum(skb, bus_to_virt(lp->rx_ring[entry].buffer1), - pkt_len, 0); + work_done++; + } else { /* Pass up the skb already on the Rx ring. */ + skb = tp->rx_skbuff[entry]; + tp->rx_skbuff[entry] = NULL; +#ifndef final_version + { + void *temp = skb_put(skb, pkt_len); + if (bus_to_virt(tp->rx_ring[entry].buffer1) != temp) + printk(KERN_ERR "%s: Internal consistency error! The " + "skbuff addresses do not match in tulip_rx:" + " %p vs. %p / %p.\n", dev->name, + bus_to_virt(tp->rx_ring[entry].buffer1), + skb->head, temp); + } +#else skb_put(skb, pkt_len); #endif } @@ -2242,56 +2424,48 @@ memory_squeeze: #else skb->len = pkt_len; #endif -#ifdef CONFIG_NET_FASTROUTE -semi_gone: -#endif netif_rx(skb); -#ifdef CONFIG_NET_HW_FLOWCONTROL - if (netdev_dropping) { -throttle: - if (lp->fc_bit) { - outl(lp->csr6 | 0x2000, dev->base_addr + CSR6); - set_bit(lp->fc_bit, &netdev_fc_xoff); - } - } -#endif -#ifdef CONFIG_NET_FASTROUTE -gone: -#endif - lp->stats.rx_packets++; -#ifndef ORIGINAL_TEXT - lp->stats.rx_bytes += pkt_len; + dev->last_rx = jiffies; + tp->stats.rx_packets++; +#if LINUX_VERSION_CODE > 0x20127 + tp->stats.rx_bytes += pkt_len; #endif } + entry = (++tp->cur_rx) % RX_RING_SIZE; + } - lp->rx_ring[entry].status = 0x80000000; - entry = (++lp->cur_rx) % RX_RING_SIZE; + /* Refill the Rx ring buffers. */ + for (; tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) { + entry = tp->dirty_rx % RX_RING_SIZE; + if (tp->rx_skbuff[entry] == NULL) { + struct sk_buff *skb; + skb = tp->rx_skbuff[entry] = DEV_ALLOC_SKB(PKT_BUF_SZ); + if (skb == NULL) + break; + skb->dev = dev; /* Mark as being used by this device. */ +#if LINUX_VERSION_CODE > 0x10300 + tp->rx_ring[entry].buffer1 = virt_to_bus(skb->tail); +#else + tp->rx_ring[entry].buffer1 = virt_to_bus(skb->data); +#endif + work_done++; + } + tp->rx_ring[entry].status = 0x80000000; } - return 0; + return work_done; } static int tulip_close(struct device *dev) { - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; struct tulip_private *tp = (struct tulip_private *)dev->priv; int i; -#ifdef CONFIG_NET_FASTROUTE - dev->tx_semaphore = 0; -#endif dev->start = 0; dev->tbusy = 1; -#ifdef CONFIG_NET_HW_FLOWCONTROL - if (tp->fc_bit) { - int bit = tp->fc_bit; - tp->fc_bit = 0; - netdev_unregister_fc(bit); - } -#endif - if (tulip_debug > 1) printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n", dev->name, inl(ioaddr + CSR5)); @@ -2326,12 +2500,20 @@ tulip_close(struct device *dev) #if LINUX_VERSION_CODE < 0x20100 skb->free = 1; #endif +#if (LINUX_VERSION_CODE > 0x20155) dev_kfree_skb(skb); +#else + dev_kfree_skb(skb, FREE_WRITE); +#endif } } for (i = 0; i < TX_RING_SIZE; i++) { if (tp->tx_skbuff[i]) +#if (LINUX_VERSION_CODE > 0x20155) dev_kfree_skb(tp->tx_skbuff[i]); +#else + dev_kfree_skb(tp->tx_skbuff[i], FREE_WRITE); +#endif tp->tx_skbuff[i] = 0; } @@ -2345,7 +2527,7 @@ static struct enet_statistics * tulip_get_stats(struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; if (dev->start) tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff; @@ -2353,6 +2535,71 @@ tulip_get_stats(struct device *dev) return &tp->stats; } +#ifdef HAVE_PRIVATE_IOCTL +/* Provide ioctl() calls to examine the MII xcvr state. */ +static int private_ioctl(struct device *dev, struct ifreq *rq, int cmd) +{ + struct tulip_private *tp = (struct tulip_private *)dev->priv; + long ioaddr = dev->base_addr; + u16 *data = (u16 *)&rq->ifr_data; + int phy = tp->phys[0] & 0x1f; + long flags; + + switch(cmd) { + case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ + if (tp->mtable && tp->mtable->has_mii) + data[0] = phy; + else if (tp->chip_id == DC21142) + data[0] = 32; + else + return -ENODEV; + return 0; + case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ + if (data[0] == 32) { /* 21142 pseudo-MII */ + int csr12 = inl(ioaddr + CSR12); + int csr14 = inl(ioaddr + CSR14); + switch (data[1]) { + case 0: { + data[3] = ((csr14<<13)&0x4000) + ((csr14<<5)&0x1000); + break; } + case 1: + data[3] = 0x7848 + ((csr12&0x7000) == 0x5000 ? 0x20 : 0) + + (csr12&0x06 ? 0x04 : 0); + break; + case 4: { + int csr14 = inl(ioaddr + CSR14); + data[3] = ((csr14>>9)&0x0380) + ((csr14>>1)&0x20) + 1; + break; + } + case 5: data[3] = inl(ioaddr + CSR12) >> 16; break; + default: data[3] = 0; break; + } + } else { + save_flags(flags); + cli(); + data[3] = mdio_read(dev, data[0] & 0x1f, data[1] & 0x1f); + restore_flags(flags); + } + return 0; + case SIOCDEVPRIVATE+2: /* Write the specified MII register */ + if (!suser()) + return -EPERM; + if (data[0] == 32) { /* 21142 pseudo-MII */ + } else { + save_flags(flags); + cli(); + mdio_write(dev, data[0] & 0x1f, data[1] & 0x1f, data[2]); + restore_flags(flags); + } + return 0; + default: + return -EOPNOTSUPP; + } + + return -EOPNOTSUPP; +} +#endif /* HAVE_PRIVATE_IOCTL */ + /* Set or clear the multicast filter for this adaptor. Note that we only use exclusion around actually queueing the new frame, not around filling tp->setup_frame. This is non-deterministic @@ -2380,12 +2627,12 @@ static inline unsigned ether_crc_le(int length, unsigned char *data) } #ifdef NEW_MULTICAST -static void set_multicast_list(struct device *dev) +static void set_rx_mode(struct device *dev) #else -static void set_multicast_list(struct device *dev, int num_addrs, void *addrs) +static void set_rx_mode(struct device *dev, int num_addrs, void *addrs) #endif { - int ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; int csr6 = inl(ioaddr + CSR6) & ~0x00D5; struct tulip_private *tp = (struct tulip_private *)dev->priv; @@ -2442,9 +2689,9 @@ static void set_multicast_list(struct device *dev, int num_addrs, void *addrs) } eaddrs = (u16 *)dev->dev_addr; do { - *setup_frm++ = eaddrs[0]; - *setup_frm++ = eaddrs[1]; - *setup_frm++ = eaddrs[2]; + *setup_frm++ = eaddrs[0]; + *setup_frm++ = eaddrs[1]; + *setup_frm++ = eaddrs[2]; } while (++i < 15); /* Now add this frame to the Tx list. */ if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) { @@ -2457,19 +2704,19 @@ static void set_multicast_list(struct device *dev, int num_addrs, void *addrs) entry = tp->cur_tx++ % TX_RING_SIZE; if (entry != 0) { - /* Avoid a chip errata by prefixing a dummy entry. */ - tp->tx_skbuff[entry] = 0; - tp->tx_ring[entry].length = - (entry == TX_RING_SIZE-1) ? 0x02000000 : 0; - tp->tx_ring[entry].buffer1 = 0; - tp->tx_ring[entry].status = 0x80000000; - entry = tp->cur_tx++ % TX_RING_SIZE; + /* Avoid a chip errata by prefixing a dummy entry. */ + tp->tx_skbuff[entry] = 0; + tp->tx_ring[entry].length = + (entry == TX_RING_SIZE-1) ? 0x02000000 : 0; + tp->tx_ring[entry].buffer1 = 0; + tp->tx_ring[entry].status = 0x80000000; + entry = tp->cur_tx++ % TX_RING_SIZE; } tp->tx_skbuff[entry] = 0; /* Put the setup frame on the Tx list. */ if (entry == TX_RING_SIZE-1) - tx_flags |= 0x02000000; /* Wrap ring. */ + tx_flags |= 0x02000000; /* Wrap ring. */ tp->tx_ring[entry].length = tx_flags; tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame); tp->tx_ring[entry].status = 0x80000000; @@ -2485,15 +2732,69 @@ static void set_multicast_list(struct device *dev, int num_addrs, void *addrs) } } +#ifdef CARDBUS + +#include <pcmcia/driver_ops.h> + +static dev_node_t *tulip_attach(dev_locator_t *loc) +{ + u16 dev_id; + u32 io; + u8 bus, devfn; + struct device *dev; + + if (loc->bus != LOC_PCI) return NULL; + bus = loc->b.pci.bus; devfn = loc->b.pci.devfn; + printk(KERN_INFO "tulip_attach(bus %d, function %d)\n", bus, devfn); + pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &io); + pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &dev_id); + io &= ~3; + dev = tulip_probe1(bus, devfn, NULL, DC21142, -1); + if (dev) { + dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL); + strcpy(node->dev_name, dev->name); + node->major = node->minor = 0; + node->next = NULL; + MOD_INC_USE_COUNT; + return node; + } + return NULL; +} + +static void tulip_detach(dev_node_t *node) +{ + struct device **devp, **next; + printk(KERN_INFO "tulip_detach(%s)\n", node->dev_name); + for (devp = &root_tulip_dev; *devp; devp = next) { + next = &((struct tulip_private *)(*devp)->priv)->next_module; + if (strcmp((*devp)->name, node->dev_name) == 0) break; + } + if (*devp) { + unregister_netdev(*devp); + kfree(*devp); + *devp = *next; + kfree(node); + MOD_DEC_USE_COUNT; + } +} + +struct driver_operations tulip_ops = { + "tulip_cb", tulip_attach, NULL, NULL, tulip_detach +}; + +#endif /* Cardbus support */ + + #ifdef MODULE #if LINUX_VERSION_CODE > 0x20118 MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>"); MODULE_DESCRIPTION("Digital 21*4* Tulip ethernet driver"); MODULE_PARM(debug, "i"); +MODULE_PARM(max_interrupt_work, "i"); MODULE_PARM(reverse_probe, "i"); MODULE_PARM(rx_copybreak, "i"); -MODULE_PARM(options, "1-" __MODULE_STRING(8) "i"); -MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i"); +MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); #endif /* An additional parameter that may be passed in... */ @@ -2505,8 +2806,12 @@ init_module(void) if (debug >= 0) tulip_debug = debug; - root_tulip_dev = NULL; +#ifdef CARDBUS + register_driver(&tulip_ops); + return 0; +#else return tulip_probe(NULL); +#endif } void @@ -2514,6 +2819,10 @@ cleanup_module(void) { struct device *next_dev; +#ifdef CARDBUS + unregister_driver(&tulip_ops); +#endif + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ while (root_tulip_dev) { next_dev = ((struct tulip_private *)root_tulip_dev->priv)->next_module; @@ -2528,7 +2837,8 @@ cleanup_module(void) /* * Local variables: - * compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c" + * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" + * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 |