[PATCH] IBM Lanstreamer bugfixes (round 2)

Kent Yoder (key@austin.ibm.com)
Thu, 31 Jan 2002 09:29:21 -0600 (CST)


Hopefully issues 1-12 were all addressed with this patch.

Sorry to include something this big inline, but its not available
externally yet. Seeking permission to apply to 2.4...

Changes made:

* No more magic numbers, all come from pci.h
* no more udelay()'s, which covered up PCI posting effects
* got rid of a __u8/__u16 here and there for u8/u16
* ioctl code disabled for kernel >= 2.5
* bounded the number of times we could loop inside the interrupt function
* spin_lock_irqsave/restore in open()/close()
* better formatting

Thanks,
Kent

diff -urN linux-2.4.13-vanilla/drivers/net/tokenring/lanstreamer.c linux-2.4.13/drivers/net/tokenring/lanstreamer.c
--- linux-2.4.13-vanilla/drivers/net/tokenring/lanstreamer.c Sun Sep 30 13:26:07 2001
+++ linux-2.4.13/drivers/net/tokenring/lanstreamer.c Wed Jan 30 16:41:32 2002
@@ -60,6 +60,11 @@
* malloc free checks, reviewed code. <alan@redhat.com>
* 03/13/00 - Added spinlocks for smp
* 03/08/01 - Added support for module_init() and module_exit()
+ * 08/15/01 - Added ioctl() functionality for debugging, changed netif_*_queue
+ * calls and other incorrectness - Kent Yoder <yoder1@us.ibm.com>
+ * 11/05/01 - Restructured the interrupt function, added delays, reduced the
+ * the number of TX descriptors to 1, which together can prevent
+ * the card from locking up the box - <yoder1@us.ibm.com>
*
* To Do:
*
@@ -84,6 +89,14 @@

#define STREAMER_NETWORK_MONITOR 0

+/* #define CONFIG_PROC_FS */
+
+/*
+ * Allow or disallow ioctl's for debugging
+ */
+
+#define STREAMER_IOCTL 0
+
#include <linux/config.h>
#include <linux/module.h>

@@ -121,7 +134,8 @@
* Official releases will only have an a.b.c version number format.
*/

-static char version[] = "LanStreamer.c v0.4.0 03/08/01 - Mike Sullivan";
+static char version[] = "LanStreamer.c v0.4.0 03/08/01 - Mike Sullivan\n"
+ " v0.5.1 11/14/01 - Kent Yoder";

static struct pci_device_id streamer_pci_tbl[] __initdata = {
{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_TR, PCI_ANY_ID, PCI_ANY_ID,},
@@ -175,6 +189,10 @@
MODULE_PARM(message_level,
"1-" __MODULE_STRING(STREAMER_MAX_ADAPTERS) "i");

+#if STREAMER_IOCTL
+static int streamer_ioctl(struct net_device *, struct ifreq *, int);
+#endif
+
static int streamer_reset(struct net_device *dev);
static int streamer_open(struct net_device *dev);
static int streamer_xmit(struct sk_buff *skb, struct net_device *dev);
@@ -206,6 +224,8 @@
__u32 mmio_start, mmio_end, mmio_flags, mmio_len;
int rc=0;
static int card_no=-1;
+ u16 pcr;
+ u8 cls = 0;

#if STREAMER_DEBUG
printk("lanstreamer::streamer_init_one, entry pdev %p\n",pdev);
@@ -281,7 +301,11 @@
dev->hard_start_xmit = &streamer_xmit;
dev->change_mtu = &streamer_change_mtu;
dev->stop = &streamer_close;
+#if STREAMER_IOCTL
+ dev->do_ioctl = &streamer_ioctl;
+#else
dev->do_ioctl = NULL;
+#endif
dev->set_multicast_list = &streamer_set_rx_mode;
dev->get_stats = &streamer_get_stats;
dev->set_mac_address = &streamer_set_mac_address;
@@ -303,6 +327,18 @@

spin_lock_init(&streamer_priv->streamer_lock);

+ pci_write_config_byte (pdev, PCI_CACHE_LINE_SIZE, cls);
+ pci_read_config_word (pdev, PCI_COMMAND, &pcr);
+
+ /* Turn off Fast B2B enable */
+ pcr &= ~PCI_COMMAND_FAST_BACK;
+ /* Turn on SERR# enable and others */
+ pcr |= (PCI_COMMAND_SERR | PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY |
+ PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+
+ pci_write_config_word (pdev, PCI_COMMAND, pcr);
+ pci_read_config_word (pdev, PCI_COMMAND, &pcr);
+
printk("%s \n", version);
printk("%s: %s. I/O at %hx, MMIO at %p, using irq %d\n",dev->name,
streamer_priv->streamer_card_name,
@@ -403,6 +439,7 @@
printk("GPR: %x\n", readw(streamer_mmio + GPR));
printk("SISRMASK: %x\n", readw(streamer_mmio + SISR_MASK));
#endif
+ writew(readw(streamer_mmio + BCTL) | (BCTL_RX_FIFO_8 | BCTL_TX_FIFO_8), streamer_mmio + BCTL );

if (streamer_priv->streamer_ring_speed == 0) { /* Autosense */
writew(readw(streamer_mmio + GPR) | GPR_AUTOSENSE,
@@ -558,8 +595,6 @@
do {
int i;

- save_flags(flags);
- cli();
for (i = 0; i < SRB_COMMAND_SIZE; i += 2) {
writew(0, streamer_mmio + LAPDINC);
}
@@ -599,11 +634,12 @@
}
printk("\n");
#endif
-
+ spin_lock_irqsave(&streamer_priv->streamer_lock, flags);
streamer_priv->srb_queued = 1;

/* signal solo that SRB command has been issued */
writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM);
+ spin_unlock_irqrestore(&streamer_priv->streamer_lock, flags);

while (streamer_priv->srb_queued) {
interruptible_sleep_on_timeout(&streamer_priv->srb_wait, 5 * HZ);
@@ -617,7 +653,6 @@
break;
}
}
- restore_flags(flags);

#if STREAMER_DEBUG
printk("SISR_MASK: %x\n", readw(streamer_mmio + SISR_MASK));
@@ -767,9 +802,12 @@
streamer_priv->streamer_tx_ring[i].bufcnt_framelen = 0;
streamer_priv->streamer_tx_ring[i].buffer = 0;
streamer_priv->streamer_tx_ring[i].buflen = 0;
+ streamer_priv->streamer_tx_ring[i].rsvd1 = 0;
+ streamer_priv->streamer_tx_ring[i].rsvd2 = 0;
+ streamer_priv->streamer_tx_ring[i].rsvd3 = 0;
}
streamer_priv->streamer_tx_ring[STREAMER_TX_RING_SIZE - 1].forward =
- virt_to_bus(&streamer_priv->streamer_tx_ring[0]);;
+ virt_to_bus(&streamer_priv->streamer_tx_ring[0]);

streamer_priv->free_tx_ring_entries = STREAMER_TX_RING_SIZE;
streamer_priv->tx_ring_free = 0; /* next entry in tx ring to use */
@@ -941,37 +979,30 @@
__u8 *streamer_mmio = streamer_priv->streamer_mmio;
__u16 sisr;
__u16 misr;
- __u16 sisrmask;
+ u8 max_intr = MAX_INTR;

- sisrmask = SISR_MI;
- writew(~sisrmask, streamer_mmio + SISR_MASK_RUM);
+ spin_lock(&streamer_priv->streamer_lock);
sisr = readw(streamer_mmio + SISR);
- writew(~sisr, streamer_mmio + SISR_RUM);
- misr = readw(streamer_mmio + MISR_RUM);
- writew(~misr, streamer_mmio + MISR_RUM);

- if (!sisr)
- { /* Interrupt isn't for us */
- writew(~misr,streamer_mmio+MISR_RUM);
- return;
- }
+ while((sisr & (SISR_MI | SISR_SRB_REPLY | SISR_ADAPTER_CHECK | SISR_ASB_FREE |
+ SISR_ARB_CMD | SISR_TRB_REPLY | SISR_PAR_ERR | SISR_SERR_ERR))
+ && (max_intr > 0)) {

- spin_lock(&streamer_priv->streamer_lock);
+ if(sisr & SISR_PAR_ERR) {
+ writew(~SISR_PAR_ERR, streamer_mmio + SISR_RUM);
+ (void)readw(streamer_mmio + SISR_RUM);
+ }

- if ((sisr & (SISR_SRB_REPLY | SISR_ADAPTER_CHECK | SISR_ASB_FREE | SISR_ARB_CMD | SISR_TRB_REPLY))
- || (misr & (MISR_TX2_EOF | MISR_RX_NOBUF | MISR_RX_EOF))) {
- if (sisr & SISR_SRB_REPLY) {
- if (streamer_priv->srb_queued == 1) {
- wake_up_interruptible(&streamer_priv->srb_wait);
- } else if (streamer_priv->srb_queued == 2) {
- streamer_srb_bh(dev);
- }
- streamer_priv->srb_queued = 0;
+ else if(sisr & SISR_SERR_ERR) {
+ writew(~SISR_SERR_ERR, streamer_mmio + SISR_RUM);
+ (void)readw(streamer_mmio + SISR_RUM);
}
- /* SISR_SRB_REPLY */
+
+ else if(sisr & SISR_MI) {
+ misr = readw(streamer_mmio + MISR_RUM);
+
if (misr & MISR_TX2_EOF) {
- while (streamer_priv->streamer_tx_ring[(streamer_priv->tx_ring_last_status + 1) & (STREAMER_TX_RING_SIZE - 1)].status)
- {
+ while(streamer_priv->streamer_tx_ring[(streamer_priv->tx_ring_last_status + 1) & (STREAMER_TX_RING_SIZE - 1)].status) {
streamer_priv->tx_ring_last_status = (streamer_priv->tx_ring_last_status + 1) & (STREAMER_TX_RING_SIZE - 1);
streamer_priv->free_tx_ring_entries++;
streamer_priv->streamer_stats.tx_bytes += streamer_priv->tx_ring_skb[streamer_priv->tx_ring_last_status]->len;
@@ -981,6 +1012,9 @@
streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].status = 0;
streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].bufcnt_framelen = 0;
streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].buflen = 0;
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].rsvd1 = 0;
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].rsvd2 = 0;
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_last_status].rsvd3 = 0;
}
netif_wake_queue(dev);
}
@@ -989,7 +1023,30 @@
streamer_rx(dev);
}
/* MISR_RX_EOF */
- if (sisr & SISR_ADAPTER_CHECK) {
+
+ if (misr & MISR_RX_NOBUF) {
+ /* According to the documentation, we don't have to do anything,
+ * but trapping it keeps it out of /var/log/messages.
+ */
+ } /* SISR_RX_NOBUF */
+
+ writew(~misr, streamer_mmio + MISR_RUM);
+ (void)readw(streamer_mmio + MISR_RUM);
+ }
+
+ else if (sisr & SISR_SRB_REPLY) {
+ if (streamer_priv->srb_queued == 1) {
+ wake_up_interruptible(&streamer_priv->srb_wait);
+ } else if (streamer_priv->srb_queued == 2) {
+ streamer_srb_bh(dev);
+ }
+ streamer_priv->srb_queued = 0;
+
+ writew(~SISR_SRB_REPLY, streamer_mmio + SISR_RUM);
+ (void)readw(streamer_mmio + SISR_RUM);
+ }
+
+ else if (sisr & SISR_ADAPTER_CHECK) {
printk(KERN_WARNING "%s: Adapter Check Interrupt Raised, 8 bytes of information follow:\n", dev->name);
writel(readl(streamer_mmio + LAPWWO), streamer_mmio + LAPA);
printk(KERN_WARNING "%s: Words %x:%x:%x:%x:\n",
@@ -1001,38 +1058,37 @@
}

/* SISR_ADAPTER_CHECK */
- if (sisr & SISR_ASB_FREE) {
+ else if (sisr & SISR_ASB_FREE) {
/* Wake up anything that is waiting for the asb response */
if (streamer_priv->asb_queued) {
streamer_asb_bh(dev);
}
+ writew(~SISR_ASB_FREE, streamer_mmio + SISR_RUM);
+ (void)readw(streamer_mmio + SISR_RUM);
}
/* SISR_ASB_FREE */
- if (sisr & SISR_ARB_CMD) {
+ else if (sisr & SISR_ARB_CMD) {
streamer_arb_cmd(dev);
+ writew(~SISR_ARB_CMD, streamer_mmio + SISR_RUM);
+ (void)readw(streamer_mmio + SISR_RUM);
}
/* SISR_ARB_CMD */
- if (sisr & SISR_TRB_REPLY) {
+ else if (sisr & SISR_TRB_REPLY) {
/* Wake up anything that is waiting for the trb response */
if (streamer_priv->trb_queued) {
wake_up_interruptible(&streamer_priv->
trb_wait);
}
streamer_priv->trb_queued = 0;
+ writew(~SISR_TRB_REPLY, streamer_mmio + SISR_RUM);
+ (void)readw(streamer_mmio + SISR_RUM);
}
/* SISR_TRB_REPLY */
- if (misr & MISR_RX_NOBUF) {
- /* According to the documentation, we don't have to do anything, but trapping it keeps it out of
- /var/log/messages. */
- } /* SISR_RX_NOBUF */
- } else {
- printk(KERN_WARNING "%s: Unexpected interrupt: %x\n",
- dev->name, sisr);
- printk(KERN_WARNING "%s: SISR_MASK: %x\n", dev->name,
- readw(streamer_mmio + SISR_MASK));
- } /* One if the interrupts we want */

- writew(SISR_MI, streamer_mmio + SISR_MASK_SUM);
+ sisr = readw(streamer_mmio + SISR);
+ max_intr--;
+ } /* while() */
+
spin_unlock(&streamer_priv->streamer_lock) ;
}

@@ -1044,13 +1100,16 @@
unsigned long flags ;

spin_lock_irqsave(&streamer_priv->streamer_lock, flags);
- netif_stop_queue(dev);

if (streamer_priv->free_tx_ring_entries) {
streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].status = 0;
- streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].bufcnt_framelen = 0x00010000 | skb->len;
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].bufcnt_framelen = 0x00020000 | skb->len;
streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].buffer = virt_to_bus(skb->data);
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].rsvd1 = skb->len;
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].rsvd2 = 0;
+ streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].rsvd3 = 0;
streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free].buflen = skb->len;
+
streamer_priv->tx_ring_skb[streamer_priv->tx_ring_free] = skb;
streamer_priv->free_tx_ring_entries--;
#if STREAMER_DEBUG_PACKETS
@@ -1067,12 +1126,13 @@
#endif

writel(virt_to_bus (&streamer_priv->streamer_tx_ring[streamer_priv->tx_ring_free]),streamer_mmio + TX2LFDA);
+ (void)readl(streamer_mmio + TX2LFDA);

streamer_priv->tx_ring_free = (streamer_priv->tx_ring_free + 1) & (STREAMER_TX_RING_SIZE - 1);
- netif_wake_queue(dev);
spin_unlock_irqrestore(&streamer_priv->streamer_lock,flags);
return 0;
} else {
+ netif_stop_queue(dev);
spin_unlock_irqrestore(&streamer_priv->streamer_lock,flags);
return 1;
}
@@ -1092,12 +1152,13 @@
writew(htons(SRB_CLOSE_ADAPTER << 8),streamer_mmio+LAPDINC);
writew(htons(STREAMER_CLEAR_RET_CODE << 8), streamer_mmio+LAPDINC);

- save_flags(flags);
- cli();
+ spin_lock_irqsave(&streamer_priv->streamer_lock, flags);

streamer_priv->srb_queued = 1;
writew(LISR_SRB_CMD, streamer_mmio + LISR_SUM);

+ spin_unlock_irqrestore(&streamer_priv->streamer_lock, flags);
+
while (streamer_priv->srb_queued)
{
interruptible_sleep_on_timeout(&streamer_priv->srb_wait,
@@ -1114,7 +1175,6 @@
}
}

- restore_flags(flags);
streamer_priv->rx_ring_last_received = (streamer_priv->rx_ring_last_received + 1) & (STREAMER_RX_RING_SIZE - 1);

for (i = 0; i < STREAMER_RX_RING_SIZE; i++) {
@@ -1806,6 +1866,64 @@
return size;
}
#endif
+#endif
+
+#if STREAMER_IOCTL && (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+static int streamer_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ int i;
+ struct streamer_private *streamer_priv = (struct streamer_private *) dev->priv;
+ u8 *streamer_mmio = streamer_priv->streamer_mmio;
+
+ switch(cmd) {
+ case IOCTL_SISR_MASK:
+ writew(SISR_MI, streamer_mmio + SISR_MASK_SUM);
+ break;
+ case IOCTL_SPIN_LOCK_TEST:
+ printk("spin_lock() called.\n");
+ spin_lock(&streamer_priv->streamer_lock);
+ spin_unlock(&streamer_priv->streamer_lock);
+ printk("spin_unlock() finished.\n");
+ break;
+ case IOCTL_PRINT_BDAS:
+ printk("bdas: RXBDA: %x RXLBDA: %x TX2FDA: %x TX2LFDA: %x\n",
+ readw(streamer_mmio + RXBDA),
+ readw(streamer_mmio + RXLBDA),
+ readw(streamer_mmio + TX2FDA),
+ readw(streamer_mmio + TX2LFDA));
+ break;
+ case IOCTL_PRINT_REGISTERS:
+ printk("registers:\n");
+ printk("SISR: %04x MISR: %04x LISR: %04x BCTL: %04x BMCTL: %04x\nmask %04x mask %04x\n",
+ readw(streamer_mmio + SISR),
+ readw(streamer_mmio + MISR_RUM),
+ readw(streamer_mmio + LISR),
+ readw(streamer_mmio + BCTL),
+ readw(streamer_mmio + BMCTL_SUM),
+ readw(streamer_mmio + SISR_MASK),
+ readw(streamer_mmio + MISR_MASK));
+ break;
+ case IOCTL_PRINT_RX_BUFS:
+ printk("Print rx bufs:\n");
+ for(i=0; i<STREAMER_RX_RING_SIZE; i++)
+ printk("rx_ring %d status: 0x%x\n", i,
+ streamer_priv->streamer_rx_ring[i].status);
+ break;
+ case IOCTL_PRINT_TX_BUFS:
+ printk("Print tx bufs:\n");
+ for(i=0; i<STREAMER_TX_RING_SIZE; i++)
+ printk("tx_ring %d status: 0x%x\n", i,
+ streamer_priv->streamer_tx_ring[i].status);
+ break;
+ case IOCTL_RX_CMD:
+ streamer_rx(dev);
+ printk("Sent rx command.\n");
+ break;
+ default:
+ printk("Bad ioctl!\n");
+ }
+ return 0;
+}
#endif

static struct pci_driver streamer_pci_driver = {
diff -urN linux-2.4.13-vanilla/drivers/net/tokenring/lanstreamer.h linux-2.4.13/drivers/net/tokenring/lanstreamer.h
--- linux-2.4.13-vanilla/drivers/net/tokenring/lanstreamer.h Tue Mar 20 14:04:59 2001
+++ linux-2.4.13/drivers/net/tokenring/lanstreamer.h Wed Jan 30 16:41:47 2002
@@ -56,11 +56,36 @@
*
* 12/10/99 - Alpha Release 0.1.0
* First release to the public
+ * 08/15/01 - Added ioctl() definitions and others - Kent Yoder <yoder1@us.ibm.com>
*
*/

+#if STREAMER_IOCTL && (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+#include <asm/ioctl.h>
+#define IOCTL_PRINT_RX_BUFS SIOCDEVPRIVATE
+#define IOCTL_PRINT_TX_BUFS SIOCDEVPRIVATE+1
+#define IOCTL_RX_CMD SIOCDEVPRIVATE+2
+#define IOCTL_TX_CMD SIOCDEVPRIVATE+3
+#define IOCTL_PRINT_REGISTERS SIOCDEVPRIVATE+4
+#define IOCTL_PRINT_BDAS SIOCDEVPRIVATE+5
+#define IOCTL_SPIN_LOCK_TEST SIOCDEVPRIVATE+6
+#define IOCTL_SISR_MASK SIOCDEVPRIVATE+7
+#endif
+
+/* MAX_INTR - the maximum number of times we can loop
+ * inside the interrupt function before returning
+ * control to the OS (maximum value is 256)
+ */
+#define MAX_INTR 5
+
+#define CLS 0x0C
+#define MLR 0x86
+#define LTR 0x0D
+
#define BCTL 0x60
#define BCTL_SOFTRESET (1<<15)
+#define BCTL_RX_FIFO_8 (1<<1)
+#define BCTL_TX_FIFO_8 (1<<3)

#define GPR 0x4a
#define GPR_AUTOSENSE (1<<2)
@@ -89,6 +114,7 @@
#define SISR_MASK_RUM 0x58

#define SISR_MI (1<<15)
+#define SISR_SERR_ERR (1<<14)
#define SISR_TIMER (1<<11)
#define SISR_LAP_PAR_ERR (1<<10)
#define SISR_LAP_ACC_ERR (1<<9)
@@ -218,7 +244,13 @@
/* Streamer defaults for buffers */

#define STREAMER_RX_RING_SIZE 16 /* should be a power of 2 */
-#define STREAMER_TX_RING_SIZE 8 /* should be a power of 2 */
+/* Setting the number of TX descriptors to 1 is a workaround for an
+ * undocumented hardware problem with the lanstreamer board. Setting
+ * this to something higher may slightly increase the throughput you
+ * can get from the card, but at the risk of locking up the box. -
+ * <yoder1@us.ibm.com>
+ */
+#define STREAMER_TX_RING_SIZE 1 /* should be a power of 2 */

#define PKT_BUF_SZ 4096 /* Default packet size */

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/