diff options
author | Don Fry <brazilnut@us.ibm.com> | 2006-06-29 16:55:02 -0400 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2006-07-05 14:07:15 -0400 |
commit | df27f4a610e22e8c8c740286368cc13e0600f22c (patch) | |
tree | 399422c705273fd6e3f736aec399399de62aea68 | |
parent | 06c878500893c315795fcf944ecbd85c3d023040 (diff) |
[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: Don Fry <brazilnut@us.ibm.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
-rw-r--r-- | drivers/net/pcnet32.c | 95 |
1 files changed, 68 insertions, 27 deletions
diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index e79c3b6bee13..fe0558242835 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c | |||
@@ -1062,6 +1062,43 @@ static int pcnet32_phys_id(struct net_device *dev, u32 data) | |||
1062 | return 0; | 1062 | return 0; |
1063 | } | 1063 | } |
1064 | 1064 | ||
1065 | /* | ||
1066 | * lp->lock must be held. | ||
1067 | */ | ||
1068 | static int pcnet32_suspend(struct net_device *dev, unsigned long *flags, | ||
1069 | int can_sleep) | ||
1070 | { | ||
1071 | int csr5; | ||
1072 | struct pcnet32_private *lp = dev->priv; | ||
1073 | struct pcnet32_access *a = &lp->a; | ||
1074 | ulong ioaddr = dev->base_addr; | ||
1075 | int ticks; | ||
1076 | |||
1077 | /* set SUSPEND (SPND) - CSR5 bit 0 */ | ||
1078 | csr5 = a->read_csr(ioaddr, CSR5); | ||
1079 | a->write_csr(ioaddr, CSR5, csr5 | CSR5_SUSPEND); | ||
1080 | |||
1081 | /* poll waiting for bit to be set */ | ||
1082 | ticks = 0; | ||
1083 | while (!(a->read_csr(ioaddr, CSR5) & CSR5_SUSPEND)) { | ||
1084 | spin_unlock_irqrestore(&lp->lock, *flags); | ||
1085 | if (can_sleep) | ||
1086 | msleep(1); | ||
1087 | else | ||
1088 | mdelay(1); | ||
1089 | spin_lock_irqsave(&lp->lock, *flags); | ||
1090 | ticks++; | ||
1091 | if (ticks > 200) { | ||
1092 | if (netif_msg_hw(lp)) | ||
1093 | printk(KERN_DEBUG | ||
1094 | "%s: Error getting into suspend!\n", | ||
1095 | dev->name); | ||
1096 | return 0; | ||
1097 | } | ||
1098 | } | ||
1099 | return 1; | ||
1100 | } | ||
1101 | |||
1065 | #define PCNET32_REGS_PER_PHY 32 | 1102 | #define PCNET32_REGS_PER_PHY 32 |
1066 | #define PCNET32_MAX_PHYS 32 | 1103 | #define PCNET32_MAX_PHYS 32 |
1067 | static int pcnet32_get_regs_len(struct net_device *dev) | 1104 | 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, | |||
1080 | struct pcnet32_private *lp = dev->priv; | 1117 | struct pcnet32_private *lp = dev->priv; |
1081 | struct pcnet32_access *a = &lp->a; | 1118 | struct pcnet32_access *a = &lp->a; |
1082 | ulong ioaddr = dev->base_addr; | 1119 | ulong ioaddr = dev->base_addr; |
1083 | int ticks; | ||
1084 | unsigned long flags; | 1120 | unsigned long flags; |
1085 | 1121 | ||
1086 | spin_lock_irqsave(&lp->lock, flags); | 1122 | spin_lock_irqsave(&lp->lock, flags); |
1087 | 1123 | ||
1088 | csr0 = a->read_csr(ioaddr, 0); | 1124 | csr0 = a->read_csr(ioaddr, CSR0); |
1089 | if (!(csr0 & 0x0004)) { /* If not stopped */ | 1125 | if (!(csr0 & CSR0_STOP)) /* If not stopped */ |
1090 | /* set SUSPEND (SPND) - CSR5 bit 0 */ | 1126 | pcnet32_suspend(dev, &flags, 1); |
1091 | a->write_csr(ioaddr, 5, 0x0001); | ||
1092 | |||
1093 | /* poll waiting for bit to be set */ | ||
1094 | ticks = 0; | ||
1095 | while (!(a->read_csr(ioaddr, 5) & 0x0001)) { | ||
1096 | spin_unlock_irqrestore(&lp->lock, flags); | ||
1097 | mdelay(1); | ||
1098 | spin_lock_irqsave(&lp->lock, flags); | ||
1099 | ticks++; | ||
1100 | if (ticks > 200) { | ||
1101 | if (netif_msg_hw(lp)) | ||
1102 | printk(KERN_DEBUG | ||
1103 | "%s: Error getting into suspend!\n", | ||
1104 | dev->name); | ||
1105 | break; | ||
1106 | } | ||
1107 | } | ||
1108 | } | ||
1109 | 1127 | ||
1110 | /* read address PROM */ | 1128 | /* read address PROM */ |
1111 | for (i = 0; i < 16; i += 2) | 1129 | for (i = 0; i < 16; i += 2) |
@@ -1142,9 +1160,12 @@ static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs, | |||
1142 | } | 1160 | } |
1143 | } | 1161 | } |
1144 | 1162 | ||
1145 | if (!(csr0 & 0x0004)) { /* If not stopped */ | 1163 | if (!(csr0 & CSR0_STOP)) { /* If not stopped */ |
1164 | int csr5; | ||
1165 | |||
1146 | /* clear SUSPEND (SPND) - CSR5 bit 0 */ | 1166 | /* clear SUSPEND (SPND) - CSR5 bit 0 */ |
1147 | a->write_csr(ioaddr, 5, 0x0000); | 1167 | csr5 = a->read_csr(ioaddr, CSR5); |
1168 | a->write_csr(ioaddr, CSR5, csr5 & (~CSR5_SUSPEND)); | ||
1148 | } | 1169 | } |
1149 | 1170 | ||
1150 | spin_unlock_irqrestore(&lp->lock, flags); | 1171 | spin_unlock_irqrestore(&lp->lock, flags); |
@@ -2652,6 +2673,7 @@ static void pcnet32_load_multicast(struct net_device *dev) | |||
2652 | volatile struct pcnet32_init_block *ib = &lp->init_block; | 2673 | volatile struct pcnet32_init_block *ib = &lp->init_block; |
2653 | volatile u16 *mcast_table = (u16 *) & ib->filter; | 2674 | volatile u16 *mcast_table = (u16 *) & ib->filter; |
2654 | struct dev_mc_list *dmi = dev->mc_list; | 2675 | struct dev_mc_list *dmi = dev->mc_list; |
2676 | unsigned long ioaddr = dev->base_addr; | ||
2655 | char *addrs; | 2677 | char *addrs; |
2656 | int i; | 2678 | int i; |
2657 | u32 crc; | 2679 | u32 crc; |
@@ -2660,6 +2682,10 @@ static void pcnet32_load_multicast(struct net_device *dev) | |||
2660 | if (dev->flags & IFF_ALLMULTI) { | 2682 | if (dev->flags & IFF_ALLMULTI) { |
2661 | ib->filter[0] = 0xffffffff; | 2683 | ib->filter[0] = 0xffffffff; |
2662 | ib->filter[1] = 0xffffffff; | 2684 | ib->filter[1] = 0xffffffff; |
2685 | lp->a.write_csr(ioaddr, PCNET32_MC_FILTER, 0xffff); | ||
2686 | lp->a.write_csr(ioaddr, PCNET32_MC_FILTER+1, 0xffff); | ||
2687 | lp->a.write_csr(ioaddr, PCNET32_MC_FILTER+2, 0xffff); | ||
2688 | lp->a.write_csr(ioaddr, PCNET32_MC_FILTER+3, 0xffff); | ||
2663 | return; | 2689 | return; |
2664 | } | 2690 | } |
2665 | /* clear the multicast filter */ | 2691 | /* clear the multicast filter */ |
@@ -2681,6 +2707,9 @@ static void pcnet32_load_multicast(struct net_device *dev) | |||
2681 | le16_to_cpu(le16_to_cpu(mcast_table[crc >> 4]) | | 2707 | le16_to_cpu(le16_to_cpu(mcast_table[crc >> 4]) | |
2682 | (1 << (crc & 0xf))); | 2708 | (1 << (crc & 0xf))); |
2683 | } | 2709 | } |
2710 | for (i = 0; i < 4; i++) | ||
2711 | lp->a.write_csr(ioaddr, PCNET32_MC_FILTER + i, | ||
2712 | le16_to_cpu(mcast_table[i])); | ||
2684 | return; | 2713 | return; |
2685 | } | 2714 | } |
2686 | 2715 | ||
@@ -2691,8 +2720,11 @@ static void pcnet32_set_multicast_list(struct net_device *dev) | |||
2691 | { | 2720 | { |
2692 | unsigned long ioaddr = dev->base_addr, flags; | 2721 | unsigned long ioaddr = dev->base_addr, flags; |
2693 | struct pcnet32_private *lp = dev->priv; | 2722 | struct pcnet32_private *lp = dev->priv; |
2723 | int csr15, suspended; | ||
2694 | 2724 | ||
2695 | spin_lock_irqsave(&lp->lock, flags); | 2725 | spin_lock_irqsave(&lp->lock, flags); |
2726 | suspended = pcnet32_suspend(dev, &flags, 0); | ||
2727 | csr15 = lp->a.read_csr(ioaddr, CSR15); | ||
2696 | if (dev->flags & IFF_PROMISC) { | 2728 | if (dev->flags & IFF_PROMISC) { |
2697 | /* Log any net taps. */ | 2729 | /* Log any net taps. */ |
2698 | if (netif_msg_hw(lp)) | 2730 | if (netif_msg_hw(lp)) |
@@ -2701,15 +2733,24 @@ static void pcnet32_set_multicast_list(struct net_device *dev) | |||
2701 | lp->init_block.mode = | 2733 | lp->init_block.mode = |
2702 | le16_to_cpu(0x8000 | (lp->options & PCNET32_PORT_PORTSEL) << | 2734 | le16_to_cpu(0x8000 | (lp->options & PCNET32_PORT_PORTSEL) << |
2703 | 7); | 2735 | 7); |
2736 | lp->a.write_csr(ioaddr, CSR15, csr15 | 0x8000); | ||
2704 | } else { | 2737 | } else { |
2705 | lp->init_block.mode = | 2738 | lp->init_block.mode = |
2706 | le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7); | 2739 | le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7); |
2740 | lp->a.write_csr(ioaddr, CSR15, csr15 & 0x7fff); | ||
2707 | pcnet32_load_multicast(dev); | 2741 | pcnet32_load_multicast(dev); |
2708 | } | 2742 | } |
2709 | 2743 | ||
2710 | lp->a.write_csr(ioaddr, 0, 0x0004); /* Temporarily stop the lance. */ | 2744 | if (suspended) { |
2711 | pcnet32_restart(dev, 0x0042); /* Resume normal operation */ | 2745 | int csr5; |
2712 | netif_wake_queue(dev); | 2746 | /* clear SUSPEND (SPND) - CSR5 bit 0 */ |
2747 | csr5 = lp->a.read_csr(ioaddr, CSR5); | ||
2748 | lp->a.write_csr(ioaddr, CSR5, csr5 & (~CSR5_SUSPEND)); | ||
2749 | } else { | ||
2750 | lp->a.write_csr(ioaddr, CSR0, CSR0_STOP); | ||
2751 | pcnet32_restart(dev, CSR0_NORMAL); | ||
2752 | netif_wake_queue(dev); | ||
2753 | } | ||
2713 | 2754 | ||
2714 | spin_unlock_irqrestore(&lp->lock, flags); | 2755 | spin_unlock_irqrestore(&lp->lock, flags); |
2715 | } | 2756 | } |