If you load tulip as a module, add "strict_csr6=1" to the options for
tulip. If tulip is builtin, you will have to edit tulip.c by hand to
set strict_csr6 to 1, I did not want to change boot code just for this.
ps. I have the Xircom Realport RBEM56G working with 10 Mb, 100 Mb and
56K modem :)! But please don't ask "how do I get my Xircom RBEM56
working?", a bigger patch including more Xircom code is almost ready
for release.
--- linux-2.2.9/drivers/net/tulip.c Fri Feb 5 09:47:48 1999
+++ linux/drivers/net/tulip.c Mon Jun 7 00:38:00 1999
@@ -15,6 +15,8 @@
Support and updates available at
http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html
+
+ Tweaks to CSR6 by Keith Owens <kaos@ocs.com.au>
*/
#define SMP_CHECK
@@ -431,6 +433,106 @@
static void set_rx_mode(struct device *dev, int num_addrs, void *addrs);
#endif
+/* The 21143 documentation (27807401.pdf from www.intel.com) is a bit vague
+ about when CSR6[13] (Start/Stop transmitter) and CSR6[1] (Start/Stop
+ receiver) can be issued. The Xircom RBEM56 documentation (103-0548-001B.pdf
+ from www.xircom.com) is more explicit and says that these bits should only be
+ set when the transmitter (receiver) is in the stopped state. Bitter
+ experience has shown that this is necessary for the Xircom. If you start the
+ RBEM56 when it is already running, it gets confused about the state of the
+ transmit and receive rings and frames are lost.
+
+ Most lost frames are not a problem, TCP or application retransmission will
+ take care of it. However if the lost frame is a setup packet then the MAC
+ filters are not setup correctly and there is no automatic recovery. Symptoms
+ include no response to ARP and packets being ignored on receive. Mail on
+ linux-net indicates that this *might* be a problem for other tulip cards.
+
+ 27807401.pdf, table 3-76 says that certain bits can only be changed when the
+ transmitter and/or receiver are stopped. 103-0548-001B.pdf contains similar
+ restrictions. I (kaos) have not seen these bits cause problems but, since a
+ workaround is required for bits 13 and 1 on the Xircom, I also enforce the
+ documented rules for all CSR6 bits.
+
+ All output to CSR6 is done with outl_CSR6. If the "strict_csr6" flag is zero
+ then this is equivalent to a normal outl. When "strict_csr6" is nonzero and
+ the new value for CSR6 will change any of the restricted bits then outl_CSR6
+ stops the transmitter and receiver first. Only after CSR5 says that activity
+ has stopped does outl_CSR6 send the new value. Users of RBEM56 (and probably
+ all Xircom Cardbus cards based on tulip chips) should include
+
+ module "tulip_cb" opts "strict_csr6=1"
+
+ in /etc/pcmcia/config.opts.
+
+ Stopping both transmitter and receiver might be overkill. Most of the
+ restricted bits say both must be stopped, a few bits can be changed with one
+ of transmitter or receiver still running. However the Intel and Xircom docs
+ disagree, for example Intel says both must be stopped to change CSR6[22],
+ Xircom says just the transmitter must be stopped. To be safe, always stop
+ both when strict_csr6 is set. In any case, the restricted bits are rarely
+ changed.
+
+ Alas, even checking for change of restricted bits is not quite enough for
+ RBEM56. It can get wedged setting CSR6[13] or CSR6[1], even when they are
+ already set. When trying to outl_CSR6 with those bits set, always stop the
+ transmitter and receiver, even if these bits are already set.
+
+ Messy. Keith Owens <kaos@ocs.com.au>.
+ */
+
+static int strict_csr6 = 0; /* set to 1 for problem builtin cards */
+
+static void outl_CSR6(u32 newcsr6, long ioaddr)
+{
+ const static restricted_bits = ( (1 << 22)
+ | (1 << 21)
+ | (1 << 17)
+ | (1 << 16)
+ | (1 << 15)
+ | (1 << 14)
+ | (1 << 13)
+ | (1 << 12)
+ | (1 << 11)
+ | (1 << 10)
+ | (1 << 9)
+ | (1 << 8)
+ | (1 << 5)
+ | (1 << 1)
+ );
+ int csr5, csr5_22_20, csr5_19_17, currcsr6, attempts = 200;
+ if (!strict_csr6) {
+ outl(newcsr6, ioaddr + CSR6);
+ return;
+ }
+ currcsr6 = inl(ioaddr + CSR6);
+ if ((newcsr6 & restricted_bits) == (currcsr6 & restricted_bits & ~0x2002)) {
+ outl(newcsr6, ioaddr + CSR6); /* safe */
+ return;
+ }
+ /* make sure the transmitter and receiver are stopped first */
+ currcsr6 &= ~0x2002;
+ while (1) {
+ outl(currcsr6, ioaddr + CSR6);
+ csr5 = inl(ioaddr + CSR5);
+ if (csr5 == 0xffffffff)
+ break; /* cannot read csr5, card removed? */
+ csr5_22_20 = csr5 & 0x700000;
+ csr5_19_17 = csr5 & 0x0e0000;
+ if ((csr5_22_20 == 0 || csr5_22_20 == 0x600000) &
+ (csr5_19_17 == 0 || csr5_19_17 == 0x80000 || csr5_19_17 == 0xc0000))
+ break; /* both are stopped or suspended */
+ if (!--attempts) {
+ printk(KERN_INFO
+ "tulip.c: outl_CSR6 too many attempts, csr5=0x%08x\n", csr5);
+ outl(newcsr6, ioaddr + CSR6); /* unsafe but do it anyway */
+ return;
+ }
+ }
+ /* now it is safe to change csr6 */
+ outl(newcsr6, ioaddr + CSR6);
+}
+
/* A list of all installed Tulip devices, for removing the driver module. */
@@ -588,7 +690,7 @@
dev->name, tulip_tbl[chip_id].chip_name, ioaddr);
/* Stop the chip's Tx and Rx processes. */
- outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
+ outl_CSR6(inl(ioaddr + CSR6) & ~0x2002, ioaddr);
/* Clear the missed-packet counter. */
(volatile int)inl(ioaddr + CSR8);
@@ -790,7 +892,7 @@
outl(0x00000000, ioaddr + CSR13);
outl(0xFFFFFFFF, ioaddr + CSR14);
outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */
- outl(inl(ioaddr + CSR6) | 0x0200, ioaddr + CSR6);
+ outl_CSR6(inl(ioaddr + CSR6) | 0x0200, ioaddr);
outl(0x0000EF05, ioaddr + CSR13);
break;
case DC21040:
@@ -802,7 +904,7 @@
outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12);
break;
case DC21142:
- outl(0x82420200, ioaddr + CSR6);
+ outl_CSR6(0x82420200, ioaddr);
outl(0x0001, ioaddr + CSR13);
outl(0x0003FFFF, ioaddr + CSR14);
outl(0x0008, ioaddr + CSR15);
@@ -811,14 +913,14 @@
break;
case LC82C168:
if ( ! tp->mii_cnt) {
- outl(0x00420000, ioaddr + CSR6);
+ outl_CSR6(0x00420000, ioaddr);
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_CSR6(0x00000000, ioaddr);
outl(0x000711C0, ioaddr + CSR14); /* Turn on NWay. */
outl(0x00000001, ioaddr + CSR13);
break;
@@ -1209,7 +1311,7 @@
/* On some chip revs we must set the MII/SYM port before the reset!? */
if (tp->mii_cnt || (tp->mtable && tp->mtable->has_mii))
- outl(0x00040000, ioaddr + CSR6);
+ outl_CSR6(0x00040000, ioaddr);
/* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
outl(0x00000001, ioaddr + CSR0);
@@ -1348,8 +1450,8 @@
select_media(dev, 1);
/* Start the chip's Tx to process setup frame. */
- outl(tp->csr6, ioaddr + CSR6);
- outl(tp->csr6 | 0x2000, ioaddr + CSR6);
+ outl_CSR6(tp->csr6, ioaddr);
+ outl_CSR6(tp->csr6 | 0x2000, ioaddr);
dev->tbusy = 0;
tp->interrupt = 0;
@@ -1358,7 +1460,7 @@
/* Enable interrupts by setting the interrupt mask. */
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_CSR6(tp->csr6 | 0x2002, ioaddr);
outl(0, ioaddr + CSR2); /* Rx poll demand */
if (tulip_debug > 2) {
@@ -1689,8 +1791,8 @@
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);
+ outl_CSR6(tp->csr6 | 0x0002, ioaddr);
+ outl_CSR6(tp->csr6 | 0x2002, ioaddr);
next_tick = (24*HZ)/10;
break;
}
@@ -1729,8 +1831,8 @@
tp->csr6 |= 0x0200;
else
tp->csr6 &= ~0x0200;
- outl(tp->csr6 | 0x0002, ioaddr + CSR6);
- outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ outl_CSR6(tp->csr6 | 0x0002, ioaddr);
+ outl_CSR6(tp->csr6 | 0x2002, ioaddr);
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",
@@ -1771,7 +1873,7 @@
if (dev->if_port == 3) {
if (csr12 & 2) { /* No 100mbps link beat, revert to 10mbps. */
new_csr6 = 0x82420200;
- outl(new_csr6, ioaddr + CSR6);
+ outl_CSR6(new_csr6, ioaddr);
outl(0x0000, ioaddr + CSR13);
outl(0x0003FFFF, ioaddr + CSR14);
outl(0x0008, ioaddr + CSR15);
@@ -1813,8 +1915,8 @@
tp->csr6 &= 0x00D5;
tp->csr6 |= new_csr6;
outl(0x0301, ioaddr + CSR12);
- outl(tp->csr6 | 0x0002, ioaddr + CSR6);
- outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ outl_CSR6(tp->csr6 | 0x0002, ioaddr);
+ outl_CSR6(tp->csr6 | 0x2002, ioaddr);
}
}
tp->timer.expires = RUN_AT(next_tick);
@@ -1834,7 +1936,7 @@
if ((csr12 & 0x7000) == 0x5000) {
if (csr12 & 0x01800000) {
/* Switch to 100mbps mode. */
- outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+ outl_CSR6(tp->csr6 | 0x0002, ioaddr);
if (csr12 & 0x01000000) {
dev->if_port = 5;
tp->csr6 = 0x83860200;
@@ -1842,7 +1944,7 @@
dev->if_port = 3;
tp->csr6 = 0x83860000;
}
- outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ outl_CSR6(tp->csr6 | 0x2002, ioaddr);
} /* Else 10baseT-FD is handled automatically. */
} else if (dev->if_port == 3) {
if (!(csr12 & 2))
@@ -1865,8 +1967,8 @@
tp->csr6 = 0x83860000;
outl(0x0003FF7F, ioaddr + CSR14);
outl(0x0301, ioaddr + CSR12);
- outl(tp->csr6 | 0x0002, ioaddr + CSR6);
- outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ outl_CSR6(tp->csr6 | 0x0002, ioaddr);
+ outl_CSR6(tp->csr6 | 0x2002, ioaddr);
}
}
@@ -1961,8 +2063,8 @@
}
if (tp->csr6 != new_csr6) {
tp->csr6 = new_csr6;
- outl(tp->csr6 | 0x0002, ioaddr + CSR6); /* Restart Tx */
- outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ outl_CSR6(tp->csr6 | 0x0002, ioaddr); /* Restart Tx */
+ outl_CSR6(tp->csr6 | 0x2002, ioaddr);
dev->trans_start = jiffies;
if (tulip_debug > 0) /* Gurppp, should be >1 */
printk(KERN_INFO "%s: Changing PNIC configuration to %s-duplex, "
@@ -2016,7 +2118,7 @@
} else if (tp->chip_id == DC21140 || tp->chip_id == DC21142
|| tp->chip_id == MX98713) {
/* Stop the transmit process. */
- outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+ outl_CSR6(tp->csr6 | 0x0002, ioaddr);
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),
@@ -2030,7 +2132,7 @@
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);
+ outl_CSR6(tp->csr6 | 0x2002, ioaddr);
tp->stats.tx_errors++;
dev->trans_start = jiffies;
return;
@@ -2051,8 +2153,8 @@
/* Perhaps we should reinitialize the hardware here. */
dev->if_port = 0;
/* Stop and restart the chip's Tx processes . */
- outl(tp->csr6 | 0x0002, ioaddr + CSR6);
- outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ outl_CSR6(tp->csr6 | 0x0002, ioaddr);
+ outl_CSR6(tp->csr6 | 0x2002, ioaddr);
/* Trigger an immediate transmit demand. */
outl(0, ioaddr + CSR1);
@@ -2286,8 +2388,8 @@
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);
+ outl_CSR6(tp->csr6 | 0x0002, ioaddr);
+ outl_CSR6(tp->csr6 | 0x2002, ioaddr);
}
}
@@ -2300,8 +2402,8 @@
else
tp->csr6 |= 0x00200000; /* Store-n-forward. */
/* Restart the transmit process. */
- outl(tp->csr6 | 0x0002, ioaddr + CSR6);
- outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+ outl_CSR6(tp->csr6 | 0x0002, ioaddr);
+ outl_CSR6(tp->csr6 | 0x2002, ioaddr);
}
if (csr5 & RxDied) { /* Missed a Rx frame. */
tp->stats.rx_errors++;
@@ -2473,7 +2575,7 @@
/* Disable interrupts by clearing the interrupt mask. */
outl(0x00000000, ioaddr + CSR7);
/* Stop the chip's Tx and Rx processes. */
- outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
+ outl_CSR6(inl(ioaddr + CSR6) & ~0x2002, ioaddr);
/* 21040 -- Leave the card in 10baseT state. */
if (tp->chip_id == DC21040)
outl(0x00000004, ioaddr + CSR13);
@@ -2638,13 +2740,13 @@
tp->csr6 &= ~0x00D5;
if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
- outl(csr6 | 0x00C0, ioaddr + CSR6);
+ outl_CSR6(csr6 | 0x00C0, ioaddr);
/* Unconditionally log net taps. */
printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name);
tp->csr6 |= 0xC0;
} else if ((dev->mc_count > 1000) || (dev->flags & IFF_ALLMULTI)) {
/* Too many to filter perfectly -- accept all multicasts. */
- outl(csr6 | 0x0080, ioaddr + CSR6);
+ outl_CSR6(csr6 | 0x0080, ioaddr);
tp->csr6 |= 0x80;
} else {
u32 *setup_frm = tp->setup_frame;
@@ -2728,7 +2830,7 @@
/* Trigger an immediate transmit demand. */
outl(0, ioaddr + CSR1);
}
- outl(csr6 | 0x0000, ioaddr + CSR6);
+ outl_CSR6(csr6 | 0x0000, ioaddr);
}
}
@@ -2795,6 +2897,7 @@
MODULE_PARM(rx_copybreak, "i");
MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM(strict_csr6, "i");
#endif
/* An additional parameter that may be passed in... */
-
To unsubscribe from this list: send the line "unsubscribe linux-net" in
the body of a message to majordomo@vger.rutgers.edu