patch-2.0.34 linux/drivers/net/tulip.c
Next file: linux/drivers/net/yellowfin.c
Previous file: linux/drivers/net/tlan.h
Back to the patch index
Back to the overall index
- Lines: 2411
- Date:
Wed Jun 3 15:17:47 1998
- Orig file:
v2.0.33/linux/drivers/net/tulip.c
- Orig date:
Sun Sep 7 12:28:36 1997
diff -u --recursive --new-file v2.0.33/linux/drivers/net/tulip.c linux/drivers/net/tulip.c
@@ -1,7 +1,6 @@
/* 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.
@@ -17,10 +16,20 @@
http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html
*/
-static const char *version = "tulip.c:v0.79 9/3/97 becker@cesdis.gsfc.nasa.gov\n";
+#define SMP_CHECK
+static const char version[] = "tulip.c:v0.88 4/7/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[MAX_UNITS] = {0, };
+static int options[MAX_UNITS] = {0, };
+static int mtu[MAX_UNITS] = {0, }; /* Jumbo MTU for interfaces. */
+
/* Set if the PCI BIOS detects the chips on a multiport board backwards. */
#ifdef REVERSE_PROBE_ORDER
static int reverse_probe = 1;
@@ -33,10 +42,14 @@
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. */
-static const rx_copybreak = 200;
+#ifdef __alpha__
+static int rx_copybreak = 1518;
+#else
+static int rx_copybreak = 100;
+#endif
/* The following example shows how to always use the 10base2 port. */
#ifdef notdef
@@ -125,6 +138,7 @@
#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)
#endif
@@ -156,7 +170,7 @@
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.
@@ -251,30 +265,61 @@
#ifndef PCI_DEVICE_ID_DEC_TULIP_PLUS
#define PCI_DEVICE_ID_DEC_TULIP_PLUS 0x0014 /* 21041. */
#endif
-
#ifndef PCI_DEVICE_ID_DEC_TULIP_21142
#define PCI_DEVICE_ID_DEC_TULIP_21142 0x0019
#endif
+#ifndef PCI_VENDOR_ID_LITEON
+#define PCI_VENDOR_ID_LITEON 0x11AD
+#endif
+#define PCI_DEVICE_ID_PNIC 0x0002
+#define PCI_DEVICE_ID_PNIC_X 0x0168
+
+#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};
static struct tulip_chip_table {
- int device_id;
- char *chip_name;
- int flags;
- void (*media_timer)(unsigned long data);
+ int 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, "DS21040 Tulip", 0, tulip_timer },
- { PCI_DEVICE_ID_DEC_TULIP_PLUS, "DS21041 Tulip", 0, tulip_timer },
- { PCI_DEVICE_ID_DEC_TULIP_FAST, "DS21140 Tulip", 0, tulip_timer }, /* + 21140A*/
- { PCI_DEVICE_ID_DEC_TULIP_21142, "DS21142 Tulip", 0, tulip_timer }, /* + 21143 */
+ { PCI_DEVICE_ID_DEC_TULIP, "Digital DC21040 Tulip", 128, 0x0001ebef,
+ 0, tulip_timer },
+ { PCI_DEVICE_ID_DEC_TULIP_PLUS, "Digital DC21041 Tulip", 128, 0x0001ebef,
+ HAS_MEDIA_TABLE, tulip_timer },
+ { PCI_DEVICE_ID_DEC_TULIP_FAST, "Digital DS21140 Tulip", 128, 0x0001ebef,
+ HAS_MII | HAS_MEDIA_TABLE, tulip_timer },
+ { PCI_DEVICE_ID_DEC_TULIP_21142, "Digital DS21142/3 Tulip", 256, 0x0801fbff,
+ HAS_MII | HAS_MEDIA_TABLE, t21142_timer },
+ { PCI_DEVICE_ID_PNIC_X, "Lite-On 82c168 PNIC", 256, 0x0001ebef,
+ 0, pnic_timer },
+ { PCI_DEVICE_ID_MX98713, "Macronix 98713 PMAC", 128, 0x0001ebef,
+ HAS_MII | HAS_MEDIA_TABLE, tulip_timer /* Tulip-like! */ },
+ { PCI_DEVICE_ID_MX98715, "Macronix 98715 PMAC", 256, 0x0001ebef,
+ HAS_MEDIA_TABLE, mxic_timer },
+ { PCI_DEVICE_ID_MX98725, "Macronix 98725 PMAC", 256, 0x0001ebef,
+ HAS_MEDIA_TABLE, mxic_timer },
{0, 0, 0, 0},
};
/* This matches the table above. */
-enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, };
+enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, DC21143=3,
+ LC82C168, MX98713, MX98715, MX98725};
static const char * const medianame[] = {
"10baseT", "10base2", "AUI", "100baseTx",
@@ -283,15 +328,17 @@
"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 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, };
static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
-static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0001, 0x0001, };
-static u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x7F3F, 0x7F3D, };
+static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0000, 0x0001, };
+static u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x0000, 0x7F3D, };
static u16 t21142_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
/* Offsets to the Command and Status Registers, "CSRs". All accesses
@@ -304,9 +351,9 @@
/* 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. */
@@ -339,7 +386,6 @@
struct mediainfo *next;
int info_type;
int index;
- struct non_mii { char media; unsigned char csr12val; char bitnum, flags;} non_mii;
unsigned char *info;
};
@@ -354,7 +400,7 @@
/* The addresses of receive-in-place skbuffs. */
struct sk_buff* rx_skbuff[RX_RING_SIZE];
char *rx_buffs; /* Address of temporary Rx buffers. */
- int setup_frame[48]; /* Pseudo-Tx frame to init address table. */
+ u32 setup_frame[48]; /* Pseudo-Tx frame to init address table. */
int chip_id;
int revision;
#if LINUX_VERSION_CODE > 0x20139
@@ -363,32 +409,37 @@
struct enet_statistics stats;
#endif
struct timer_list timer; /* Media selection timer. */
+ 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 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 */
};
-/* Used to pass the full-duplex flag, etc. */
-static int full_duplex[8] = {0, };
-static int options[8] = {0, };
-
-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 ioaddr,
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 void mdio_write(int ioaddr, 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);
@@ -399,18 +450,19 @@
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);
+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
@@ -432,9 +484,9 @@
unsigned char pci_bus, pci_device_fn;
for (;pci_index < 0xff; pci_index++) {
- unsigned char pci_irq_line, pci_latency;
- unsigned short pci_command, vendor, device;
- unsigned int pci_ioaddr, chip_idx = 0;
+ u16 vendor, device, pci_command, new_command;
+ u32 pci_ioaddr;
+ int chip_idx = 0;
if (pcibios_find_class
(PCI_CLASS_NETWORK_ETHERNET << 8,
@@ -448,64 +500,71 @@
PCI_VENDOR_ID, &vendor);
pcibios_read_config_word(pci_bus, pci_device_fn,
PCI_DEVICE_ID, &device);
- 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);
/* Remove I/O space marker in bit 0. */
pci_ioaddr &= ~3;
- if (vendor != PCI_VENDOR_ID_DEC)
+ if (vendor != PCI_VENDOR_ID_DEC
+ && vendor != PCI_VENDOR_ID_LITEON
+ && vendor != PCI_VENDOR_ID_MXIC)
continue;
+ if (vendor == PCI_VENDOR_ID_LITEON)
+ device = PCI_DEVICE_ID_PNIC_X;
+ else if (vendor == PCI_VENDOR_ID_MXIC)
+ device = PCI_DEVICE_ID_MX98713;
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);
+ printk(KERN_INFO "Unknown Tulip-style PCI ethernet chip type"
+ " %4.4x %4.4x"" detected: not configured.\n",
+ vendor, 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);
+ printk(KERN_DEBUG "Found %s at I/O %#x.\n",
+ tulip_tbl[chip_idx].chip_name, pci_ioaddr);
- if (check_region(pci_ioaddr, TULIP_TOTAL_SIZE))
+ if (check_region(pci_ioaddr, tulip_tbl[chip_idx].io_size))
continue;
-#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
+ 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);
+ }
+ dev = tulip_probe1(pci_bus, pci_device_fn, dev,
+ pci_ioaddr, chip_idx, cards_found);
+
+ /* Get and check the bus-master and latency values. */
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);
- 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)
- pcibios_write_config_dword(pci_bus, pci_device_fn,
- 0x40, 0x40000000);
- dev = 0;
- cards_found++;
+ 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, "
+ " 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++;
}
}
}
@@ -513,7 +572,8 @@
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 ioaddr,
int chip_id, int board_idx)
{
static int did_version = 0; /* Already printed version info. */
@@ -521,6 +581,7 @@
/* 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;
+ u8 pci_irq_line;
int i;
unsigned short sum;
@@ -529,13 +590,15 @@
dev = init_etherdev(dev, 0);
- printk(KERN_INFO "%s: DEC %s at %#3x,",
+ pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_INTERRUPT_LINE, &pci_irq_line);
+ printk(KERN_INFO "%s: %s at %#3x,",
dev->name, tulip_tbl[chip_id].chip_name, ioaddr);
/* Stop the chip's Tx and Rx processes. */
outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
/* Clear the missed-packet counter. */
- (volatile)inl(ioaddr + CSR8);
+ (volatile int)inl(ioaddr + CSR8);
if (chip_id == DC21041) {
if (inl(ioaddr + CSR9) & 0x8000) {
@@ -561,6 +624,16 @@
dev->dev_addr[i] = value;
sum += value & 0xff;
}
+ } else if (chip_id == LC82C168) {
+ for (i = 0; i < 3; i++) {
+ int value, boguscnt = 100000;
+ outl(0x600 | i, ioaddr + 0x98);
+ do
+ value = inl(ioaddr + CSR9);
+ while (value < 0 && --boguscnt > 0);
+ ((u16*)dev->dev_addr)[i] = value;
+ sum += value & 0xffff;
+ }
} else { /* Must be a new chip, with a serial EEPROM interface. */
/* We read the whole EEPROM, and sort it out later. DEC has a
specification _Digital Semiconductor 21X4 Serial ROM Format_
@@ -581,6 +654,13 @@
sum += ee_data[i + sa_offset];
}
}
+ /* Lite-On boards have the address byte-swapped. */
+ if (dev->dev_addr[0] == 0xA0 && dev->dev_addr[1] == 0x00)
+ for (i = 0; i < 6; i+=2) {
+ char tmp = dev->dev_addr[i];
+ dev->dev_addr[i] = dev->dev_addr[i+1];
+ dev->dev_addr[i+1] = tmp;
+ }
/* On the Zynx 315 Etherarray and other multiport boards only the
first Tulip has an EEPROM.
The addresses of the subsequent ports are derived from the first.
@@ -591,33 +671,37 @@
for (i = 0; i < 5; i++)
dev->dev_addr[i] = last_phys_addr[i];
dev->dev_addr[i] = last_phys_addr[i] + 1;
- irq = last_irq;
+#if defined(__i386__) /* This BIOS bug doesn't exist on Alphas. */
+ pci_irq_line = 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;
+ printk(", IRQ %d.\n", pci_irq_line);
+ last_irq = pci_irq_line;
/* We do a request_region() only to register /proc/ioports info. */
+ /* Note that proper size is tulip_tbl[chip_id].chip_name, but... */
request_region(ioaddr, TULIP_TOTAL_SIZE, tulip_tbl[chip_id].chip_name);
dev->base_addr = ioaddr;
- dev->irq = irq;
+ dev->irq = pci_irq_line;
/* Make certain the data structures are quadword aligned. */
tp = (void *)(((long)kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA) + 7) & ~7);
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
tp->full_duplex = 1;
+ tp->full_duplex_lock = 1;
#endif
#ifdef TULIP_DEFAULT_MEDIA
tp->default_port = TULIP_DEFAULT_MEDIA;
@@ -627,32 +711,64 @@
#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 (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)
+ 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(ioaddr, phy, 1);
+ if (mii_status != 0xffff && mii_status != 0x0000) {
+ int mii_reg0 = mdio_read(ioaddr, 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(ioaddr, phy, 4));
+ mdio_write(ioaddr, phy, 4, reg4);
+ }
+ /* Enable autonegotiation: some boards default to off. */
+ mdio_write(ioaddr, 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;
@@ -664,8 +780,11 @@
dev->hard_start_xmit = &tulip_start_xmit;
dev->stop = &tulip_close;
dev->get_stats = &tulip_get_stats;
+#ifdef HAVE_PRIVATE_IOCTL
+ dev->do_ioctl = &private_ioctl;
+#endif
#ifdef HAVE_MULTICAST
- dev->set_multicast_list = &set_multicast_list;
+ dev->set_multicast_list = &set_rx_mode;
#endif
/* Reset the xcvr interface and turn on heartbeat. */
@@ -674,17 +793,36 @@
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(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;
}
return dev;
@@ -722,49 +860,50 @@
{0, 0, 0, 0, {}}};
static const char * block_name[] = {"21140 non-MII", "21140 MII PHY",
- "21142 non-MII PHY", "21142 MII PHY", };
+ "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. */
static struct mediatable *last_mediatable = NULL;
static unsigned char *last_ee_data = NULL;
- static controller_index = 0;
+ static int controller_index = 0;
struct tulip_private *tp = (struct tulip_private *)dev->priv;
int ioaddr = dev->base_addr;
unsigned char *ee_data = tp->eeprom;
int i;
- {
- static int done_did_that = 0;
- if (done_did_that++ == 0)
- printk(KERN_INFO" The following verbose information is emitted for\n"
- KERN_INFO" bug reports on media selection.\n");
- }
tp->mtable = 0;
for (i = 0; i < EEPROM_SIZE/2; i++)
- ((u16 *)ee_data)[i] = read_eeprom(ioaddr, i);
+ ((u16 *)ee_data)[i] = read_eeprom(ioaddr, i);
/* Detect an old-style (SA only) EEPROM layout:
memcmp(eedata, eedata+16, 8). */
for (i = 0; i < 8; i ++)
- if (ee_data[i] != ee_data[16+i])
- break;
+ if (ee_data[i] != ee_data[16+i])
+ break;
if (i >= 8) {
- if (ee_data[0] == 0xff) {
- if (last_mediatable) {
- controller_index++;
- printk(KERN_INFO "%s: Controller %d of multiport board.\n",
- dev->name, controller_index);
- tp->mtable = last_mediatable;
- ee_data = last_ee_data;
- goto subsequent_board;
- } else
- printk(KERN_INFO "%s: Missing EEPROM, this device may not work correctly!\n",
+ if (ee_data[0] == 0xff) {
+ if (last_mediatable) {
+ controller_index++;
+ printk(KERN_INFO "%s: Controller %d of multiport board.\n",
+ dev->name, controller_index);
+ tp->mtable = last_mediatable;
+ ee_data = last_ee_data;
+ goto subsequent_board;
+ } else
+ printk(KERN_INFO "%s: Missing EEPROM, this interface may "
+ "not work correctly!\n",
dev->name);
- return;
- }
+ return;
+ }
/* Do a fix-up based on the vendor half of the station address prefix. */
for (i = 0; eeprom_fixups[i].name; i++) {
if (dev->dev_addr[0] == eeprom_fixups[i].addr0
@@ -787,7 +926,7 @@
}
}
if (tulip_debug > 1) {
- printk(KERN_DEBUG "\nread_eeprom:");
+ printk(KERN_DEBUG "read_eeprom:");
for (i = 0; i < 64; i++) {
printk("%s%4.4x", (i & 7) == 0 ? "\n" KERN_DEBUG : " ",
read_eeprom(ioaddr, i));
@@ -801,30 +940,44 @@
}
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];
- unsigned short *csrvals = (unsigned short *)&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];
+ for (i = 0; i < 3; i++) {
+ csrvals[i] = 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;
- short media = *((u16 *)p)++;
+ u16 media = get_u16(p);
- if (tp->chip_id == DC21140)
+ p += 2;
+ if (tp->chip_id == DC21140 || tp->chip_id == MX98713)
csr12dir = *p++;
count = *p++;
mtable = (struct mediatable *)
@@ -847,6 +1000,8 @@
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];
@@ -864,16 +1019,7 @@
"sequences %d/%d long, capabilities %2.2x %2.2x.\n",
dev->name, bp[0], bp[1], bp[1 + bp[1]*2],
bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]);
- if (tulip_debug > 2) {
- int mii_reg;
- printk(KERN_DEBUG "%s: MII xcvr control registers:",
- dev->name);
- for (mii_reg = 0; mii_reg < 32; mii_reg++)
- printk(" %4.4x", mdio_read(ioaddr,bp[0], mii_reg));
- printk(".\n");
- }
}
-
printk(KERN_INFO "%s: Index #%d - Media %s (#%d) described "
"by a %s (%d) block.\n",
dev->name, i, medianame[leaf->media], leaf->media,
@@ -941,6 +1087,16 @@
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. */
@@ -950,17 +1106,12 @@
#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)
{
int i;
int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
- unsigned short retval = 0;
+ int retval = 0;
int mdio_addr = ioaddr + CSR9;
/* Establish sync by sending at least 32 logic ones. */
@@ -971,35 +1122,54 @@
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);
+ outl(MDIO_ENB | dataval, mdio_addr);
mdio_delay();
- outl(dataval | MDIO_SHIFT_CLK, mdio_addr);
+ outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr);
mdio_delay();
- outl(dataval, mdio_addr);
+ }
+ /* Read the two transition, 16 data, and wire-idle bits. */
+ for (i = 19; i > 0; i--) {
+ outl(MDIO_ENB_IN, mdio_addr);
+ mdio_delay();
+ 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_SHIFT_CLK, mdio_addr);
- mdio_delay();
- outl(MDIO_ENB_IN, mdio_addr);
+ return (retval>>1) & 0xffff;
+}
- for (i = 16; i > 0; i--) {
- outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+static void mdio_write(int ioaddr, int phy_id, int location, int value)
+{
+ int i;
+ int cmd = (0x5002 << 16) | (phy_id << 23) | (location<<18) | value;
+ int mdio_addr = ioaddr + CSR9;
+
+ /* Establish sync by sending 32 logic ones. */
+ for (i = 32; i >= 0; i--) {
+ outl(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
mdio_delay();
- retval = (retval << 1) | ((inl(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
- outl(MDIO_ENB_IN, mdio_addr);
+ outl(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
mdio_delay();
}
- /* Clear out extra bits. */
- for (i = 16; i > 0; i--) {
- outl(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+ /* 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 retval;
+ return;
}
@@ -1011,7 +1181,7 @@
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. */
@@ -1033,23 +1203,30 @@
Tx and Rx queues and the address filter list. */
#if defined(__alpha__)
/* ToDo: Alpha setting could be better. */
- outl(0x00200000 | 0xE000, ioaddr + CSR0);
-#else
+ outl(0x01A00000 | 0xE000, ioaddr + CSR0);
+#elif defined(__powerpc__)
+ 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
- outl(0x00200000 | (x86 <= 4 ? 0x4800 : 0x8000), ioaddr + CSR0);
+#if (LINUX_VERSION_CODE > 0x2014c)
+#define x86 boot_cpu_data.x86
+#endif
+ 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(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
@@ -1073,7 +1250,7 @@
/* Fill the whole address filter table with our physical address. */
{
u16 *eaddrs = (u16 *)dev->dev_addr;
- int *setup_frm = tp->setup_frame, i;
+ u32 *setup_frm = tp->setup_frame, i;
/* You must add the broadcast address when doing perfect filtering! */
*setup_frm++ = 0xffff;
@@ -1097,7 +1274,7 @@
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;
@@ -1105,14 +1282,16 @@
/* 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)) {
+ 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)) {
@@ -1120,26 +1299,33 @@
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
+ 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(0x0001fbff, ioaddr + CSR7);
+ outl(tulip_tbl[tp->chip_id].valid_intrs, ioaddr + CSR7);
outl(tp->csr6 | 0x2002, ioaddr + CSR6);
outl(0, ioaddr + CSR2); /* Rx poll demand */
@@ -1151,9 +1337,9 @@
/* 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);
return 0;
@@ -1174,8 +1360,8 @@
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)
@@ -1183,26 +1369,11 @@
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",
@@ -1212,56 +1383,74 @@
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 */
- new_csr6 = 0x02000000;
+ if (mleaf->type == 4)
+ 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(ioaddr, 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",
@@ -1272,7 +1461,33 @@
outl(t21041_csr15[dev->if_port], ioaddr + CSR15);
outl(t21041_csr13[dev->if_port], ioaddr + CSR13);
new_csr6 = 0x80020000;
- } else { /* 21040 */
+ } else if (tp->chip_id == LC82C168) {
+ if (startup)
+ dev->if_port = 0;
+ if (tulip_debug > 1)
+ printk(KERN_DEBUG "%s: LiteOn PHY status is %3.3x, CSR12 %4.4x,"
+ " media %s.\n",
+ dev->name, inl(ioaddr + 0xB8), inl(ioaddr + CSR12),
+ medianame[dev->if_port]);
+ 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)
+ outl(0x0201F868, ioaddr + 0xB8); /* Trigger autonegotiation. */
+ else
+ outl(0x1F868, ioaddr + 0xB8);
+ } else {
+ outl(0x32, ioaddr + CSR12);
+ new_csr6 = 0x00420000;
+ outl(0x1F078, ioaddr + 0xB8);
+ }
+ } else if (tp->chip_id == DC21040) { /* 21040 */
/* Turn on the xcvr interface. */
int csr12 = inl(ioaddr + CSR12);
if (tulip_debug > 1)
@@ -1283,6 +1498,23 @@
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);
@@ -1357,125 +1589,262 @@
break;
}
break;
- case DC21140: case DC21142: {
+ case DC21140: case DC21142: case MX98713: default: {
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;
- } 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");
+ /* 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(ioaddr, tp->phys[0], 1);
+ mii_reg5 = mdio_read(ioaddr, 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(ioaddr, 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);
+#ifdef notyet
+ goto select_next_media;
+#endif
}
+ 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;
}
- 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)
+ case 2: /* 21142 serial block has no link beat. */
+ default:
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:
- {
- int mii_reg5 = mdio_read(ioaddr, tp->phys[0], 5);
- printk(KERN_INFO "%s: MII monitoring tick: CSR12 %2.2x, "
- "Link partner report %4.4x.\n",
- dev->name, csr12, mii_reg5);
- if (mii_reg5 != 0xffff
- && mdio_read(ioaddr, tp->phys[0], 1) & 0x0020) {
- int full_duplex = mii_reg5 & 0x0100 ? 1 : 0;
- if (full_duplex != tp->full_duplex) {
- tp->full_duplex = full_duplex;
- 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 partner capability of %4.4x.\n",
- dev->name, full_duplex ? "full" : "half",
- tp->phys[0], mii_reg5);
- }
- }
+ }
+ }
+ break;
+ }
+ if (next_tick) {
+ tp->timer.expires = RUN_AT(next_tick);
+ add_timer(&tp->timer);
+ }
+}
- /* Hack for D-Link: Full duplex indication is on bit 3. */
- if (dev->dev_addr[0] == 0 && dev->dev_addr[1] == 0x80
- && dev->dev_addr[2] == 0xC8) {
- if (csr12 & 0x08) {
- tp->full_duplex = 0;
- tp->csr6 &= ~0x0200;
- outl(tp->csr6 | 0x0002, ioaddr + CSR6);
- outl(tp->csr6 | 0x2002, ioaddr + CSR6);
- } else {
- tp->full_duplex = 1;
- tp->csr6 |= 0x0200;
- outl(tp->csr6 | 0x0002, ioaddr + CSR6);
- outl(tp->csr6 | 0x2002, ioaddr + CSR6);
- }
- }
- break;
- case 2: /* 21142 non-MII */
- case 3: /* 21142 MII */
- next_tick = (24*HZ)/10;
- break;
- default:
- break;
- }
+/* 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;
+ int 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);
+ }
}
- default: /* Invalid chip type. */
- break;
+ 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;
+ int 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;
+ int 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);
@@ -1483,6 +1852,59 @@
}
}
+static void pnic_timer(unsigned long data)
+{
+ struct device *dev = (struct device *)data;
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int csr12 = inl(ioaddr + CSR12);
+ int phy_reg = inl(ioaddr + 0xB8);
+ int next_tick = 60*HZ;
+ int duplex;
+
+ if (tulip_debug > 1)
+ printk(KERN_DEBUG "%s: LC82C168 phy status %8.8x, CSR5 %8.8x.\n",
+ dev->name, phy_reg, inl(ioaddr + CSR5));
+ if (tp->full_duplex_lock)
+ return; /* Do not bother to set timer. */
+ duplex = phy_reg & 0x30000000 ? 1 : 0;
+ 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"
+ " PNIC PHY report of %8.8x.\n",
+ dev->name, tp->full_duplex ? "full" : "half", phy_reg);
+ }
+ if (phy_reg & 0x04000000) { /* Remote link fault */
+ /*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) {
+ dev->if_port = 3;
+ } else
+ dev->if_port = 0;
+ next_tick = 3*HZ;
+ select_media(dev, 0);
+ outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+ outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ dev->trans_start = jiffies;
+ }
+ 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;
@@ -1502,20 +1924,6 @@
}
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);
@@ -1535,6 +1943,27 @@
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",
@@ -1616,15 +2045,6 @@
int entry;
u32 flag;
-#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
-
/* 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) {
@@ -1681,20 +2101,31 @@
struct device *dev = (struct device *)(irq2dev_map[irq]);
#endif
- struct tulip_private *lp;
- int csr5, ioaddr, boguscnt = 12;
+ struct tulip_private *tp;
+ int csr5, ioaddr, work_budget = max_interrupt_work;
if (dev == NULL) {
- printk ("tulip_interrupt(): irq %d for unknown device.\n", irq);
+ printk (KERN_ERR" tulip_interrupt(): irq %d for unknown device.\n",
+ irq);
return;
}
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);
-
+ return;
+#endif
+ }
dev->interrupt = 1;
+#ifdef SMP_CHECK
+ tp->smp_proc_id = hard_smp_processor_id();
+#endif
do {
csr5 = inl(ioaddr + CSR5);
@@ -1705,23 +2136,24 @@
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 & 0x0040) /* Rx interrupt */
- tulip_rx(dev);
+ if (csr5 & (RxIntr | RxNoBuf))
+ work_budget -= tulip_rx(dev);
- if (csr5 & 0x0007) { /* Tx-done interrupt */
- int dirty_tx;
+ if (csr5 & (TxNoBuf | TxDied | TxIntr)) {
+ 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) {
@@ -1731,74 +2163,96 @@
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
+#if LINUX_VERSION_CODE > 0x20127
+ tp->stats.tx_bytes += tp->tx_ring[entry].length & 0x7ff;
#endif
- lp->stats.collisions += (status >> 3) & 15;
- lp->stats.tx_packets++;
+ tp->stats.collisions += (status >> 3) & 15;
+ tp->stats.tx_packets++;
}
/* Free the original skb. */
- dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE);
- 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;
}
/* Log errors. */
- if (csr5 & 0x8000) { /* Abnormal error summary bit. */
- if (csr5 & 0x0008) lp->stats.tx_errors++; /* Tx babble. */
- if (csr5 & 0x0020) { /* Tx FIFO underflow. */
- 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 (csr5 & 0x0100) { /* Missed a Rx frame. */
- lp->stats.rx_errors++;
- lp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+ if (csr5 & AbnormalIntr) { /* Abnormal error summary bit. */
+ if (csr5 & TxJabber) tp->stats.tx_errors++;
+ if (csr5 & TxFIFOUnderflow) {
+ 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. */
+ tp->stats.rx_errors++;
+ tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
}
- if (csr5 & 0x0800) {
+ 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);
@@ -1807,6 +2261,7 @@
printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n",
dev->name, inl(ioaddr + CSR5));
+#ifdef notdef
/* Code that should never be run! Perhaps remove after testing.. */
{
static int stopit = 10;
@@ -1822,95 +2277,84 @@
#endif
}
}
+#endif
dev->interrupt = 0;
+ clear_bit(0, (void*)&tp->interrupt);
return;
}
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++;
+ 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;
/* 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 = 0; /* No memory, drop the packet. */
- goto memory_squeeze;
- }
- /* Pass up the skb already on the Rx ring. */
- skb = lp->rx_skbuff[entry];
- 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 (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);
+ bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len);
+#else
+#warning Code untested
+ eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1),
+ pkt_len, 0);
+ skb_put(skb, pkt_len);
+#endif
+ 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
}
#if LINUX_VERSION_CODE > 0x10300
skb->protocol = eth_type_trans(skb, dev);
@@ -1918,14 +2362,35 @@
skb->len = pkt_len;
#endif
netif_rx(skb);
- lp->stats.rx_packets++;
+ 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
@@ -1972,12 +2437,20 @@
#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;
}
@@ -1999,6 +2472,71 @@
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(ioaddr, 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(ioaddr, 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
@@ -2025,12 +2563,10 @@
return crc;
}
-
-static void
#ifdef NEW_MULTICAST
-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;
@@ -2057,13 +2593,13 @@
if (dev->mc_count > 14) { /* Must use a multicast hash table. */
u16 hash_table[32];
memset(hash_table, 0, sizeof(hash_table));
+ /* This should work on big-endian machines as well. */
for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
i++, mclist = mclist->next)
- set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff,
- hash_table);
+ set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff,
+ hash_table);
/* Copy the hash table to the setup frame.
- NOTE that only the LOW SHORTWORD of setup_frame[] is valid!
- This code may require tweaking for non-x86 architectures! */
+ NOTE that only the LOW SHORTWORD of setup_frame[] is valid! */
for (i = 0; i < 32; i++)
*setup_frm++ = hash_table[i];
setup_frm += 7;
@@ -2090,9 +2626,9 @@
}
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) {
@@ -2105,19 +2641,19 @@
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;
@@ -2133,13 +2669,69 @@
}
}
+#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, io, 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... */
@@ -2151,8 +2743,12 @@
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
@@ -2160,6 +2756,10 @@
{
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;
@@ -2174,7 +2774,8 @@
/*
* 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
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov