Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit df27f4a6 authored by Don Fry's avatar Don Fry Committed by Jeff Garzik
Browse files

[PATCH] pcnet32: Suspend the chip rather than restart when changing multicast/promisc



Suspend the chip if possible rather than stop and discard all tx and rx
frames, when changing the mcast list or entering/leaving promiscuous
mode.  Created common pcnet32_suspend routine.

Tested ia32 and ppc64

Signed-off-by: default avatarDon Fry <brazilnut@us.ibm.com>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent 06c87850
Loading
Loading
Loading
Loading
+68 −27
Original line number Diff line number Diff line
@@ -1062,6 +1062,43 @@ static int pcnet32_phys_id(struct net_device *dev, u32 data)
	return 0;
}

/*
 * lp->lock must be held.
 */
static int pcnet32_suspend(struct net_device *dev, unsigned long *flags,
		int can_sleep)
{
	int csr5;
	struct pcnet32_private *lp = dev->priv;
	struct pcnet32_access *a = &lp->a;
	ulong ioaddr = dev->base_addr;
	int ticks;

	/* set SUSPEND (SPND) - CSR5 bit 0 */
	csr5 = a->read_csr(ioaddr, CSR5);
	a->write_csr(ioaddr, CSR5, csr5 | CSR5_SUSPEND);

	/* poll waiting for bit to be set */
	ticks = 0;
	while (!(a->read_csr(ioaddr, CSR5) & CSR5_SUSPEND)) {
		spin_unlock_irqrestore(&lp->lock, *flags);
		if (can_sleep)
			msleep(1);
		else
			mdelay(1);
		spin_lock_irqsave(&lp->lock, *flags);
		ticks++;
		if (ticks > 200) {
			if (netif_msg_hw(lp))
				printk(KERN_DEBUG
				       "%s: Error getting into suspend!\n",
				       dev->name);
			return 0;
		}
	}
	return 1;
}

#define PCNET32_REGS_PER_PHY	32
#define PCNET32_MAX_PHYS	32
static int pcnet32_get_regs_len(struct net_device *dev)
@@ -1080,32 +1117,13 @@ static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs,
	struct pcnet32_private *lp = dev->priv;
	struct pcnet32_access *a = &lp->a;
	ulong ioaddr = dev->base_addr;
	int ticks;
	unsigned long flags;

	spin_lock_irqsave(&lp->lock, flags);

	csr0 = a->read_csr(ioaddr, 0);
	if (!(csr0 & 0x0004)) {	/* If not stopped */
		/* set SUSPEND (SPND) - CSR5 bit 0 */
		a->write_csr(ioaddr, 5, 0x0001);

		/* poll waiting for bit to be set */
		ticks = 0;
		while (!(a->read_csr(ioaddr, 5) & 0x0001)) {
			spin_unlock_irqrestore(&lp->lock, flags);
			mdelay(1);
			spin_lock_irqsave(&lp->lock, flags);
			ticks++;
			if (ticks > 200) {
				if (netif_msg_hw(lp))
					printk(KERN_DEBUG
					       "%s: Error getting into suspend!\n",
					       dev->name);
				break;
			}
		}
	}
	csr0 = a->read_csr(ioaddr, CSR0);
	if (!(csr0 & CSR0_STOP))	/* If not stopped */
		pcnet32_suspend(dev, &flags, 1);

	/* read address PROM */
	for (i = 0; i < 16; i += 2)
@@ -1142,9 +1160,12 @@ static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs,
		}
	}

	if (!(csr0 & 0x0004)) {	/* If not stopped */
	if (!(csr0 & CSR0_STOP)) {	/* If not stopped */
		int csr5;

		/* clear SUSPEND (SPND) - CSR5 bit 0 */
		a->write_csr(ioaddr, 5, 0x0000);
		csr5 = a->read_csr(ioaddr, CSR5);
		a->write_csr(ioaddr, CSR5, csr5 & (~CSR5_SUSPEND));
	}

	spin_unlock_irqrestore(&lp->lock, flags);
@@ -2652,6 +2673,7 @@ static void pcnet32_load_multicast(struct net_device *dev)
	volatile struct pcnet32_init_block *ib = &lp->init_block;
	volatile u16 *mcast_table = (u16 *) & ib->filter;
	struct dev_mc_list *dmi = dev->mc_list;
	unsigned long ioaddr = dev->base_addr;
	char *addrs;
	int i;
	u32 crc;
@@ -2660,6 +2682,10 @@ static void pcnet32_load_multicast(struct net_device *dev)
	if (dev->flags & IFF_ALLMULTI) {
		ib->filter[0] = 0xffffffff;
		ib->filter[1] = 0xffffffff;
		lp->a.write_csr(ioaddr, PCNET32_MC_FILTER, 0xffff);
		lp->a.write_csr(ioaddr, PCNET32_MC_FILTER+1, 0xffff);
		lp->a.write_csr(ioaddr, PCNET32_MC_FILTER+2, 0xffff);
		lp->a.write_csr(ioaddr, PCNET32_MC_FILTER+3, 0xffff);
		return;
	}
	/* clear the multicast filter */
@@ -2681,6 +2707,9 @@ static void pcnet32_load_multicast(struct net_device *dev)
		    le16_to_cpu(le16_to_cpu(mcast_table[crc >> 4]) |
				(1 << (crc & 0xf)));
	}
	for (i = 0; i < 4; i++)
		lp->a.write_csr(ioaddr, PCNET32_MC_FILTER + i,
				le16_to_cpu(mcast_table[i]));
	return;
}

@@ -2691,8 +2720,11 @@ static void pcnet32_set_multicast_list(struct net_device *dev)
{
	unsigned long ioaddr = dev->base_addr, flags;
	struct pcnet32_private *lp = dev->priv;
	int csr15, suspended;

	spin_lock_irqsave(&lp->lock, flags);
	suspended = pcnet32_suspend(dev, &flags, 0);
	csr15 = lp->a.read_csr(ioaddr, CSR15);
	if (dev->flags & IFF_PROMISC) {
		/* Log any net taps. */
		if (netif_msg_hw(lp))
@@ -2701,15 +2733,24 @@ static void pcnet32_set_multicast_list(struct net_device *dev)
		lp->init_block.mode =
		    le16_to_cpu(0x8000 | (lp->options & PCNET32_PORT_PORTSEL) <<
				7);
		lp->a.write_csr(ioaddr, CSR15, csr15 | 0x8000);
	} else {
		lp->init_block.mode =
		    le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7);
		lp->a.write_csr(ioaddr, CSR15, csr15 & 0x7fff);
		pcnet32_load_multicast(dev);
	}

	lp->a.write_csr(ioaddr, 0, 0x0004);	/* Temporarily stop the lance. */
	pcnet32_restart(dev, 0x0042);	/*  Resume normal operation */
	if (suspended) {
		int csr5;
		/* clear SUSPEND (SPND) - CSR5 bit 0 */
		csr5 = lp->a.read_csr(ioaddr, CSR5);
		lp->a.write_csr(ioaddr, CSR5, csr5 & (~CSR5_SUSPEND));
	} else { 
		lp->a.write_csr(ioaddr, CSR0, CSR0_STOP);
		pcnet32_restart(dev, CSR0_NORMAL);
		netif_wake_queue(dev);
	}

	spin_unlock_irqrestore(&lp->lock, flags);
}