aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRussell King - ARM Linux <linux@arm.linux.org.uk>2011-06-09 20:52:14 -0400
committerDavid S. Miller <davem@davemloft.net>2011-06-11 18:56:00 -0400
commitbfc6501324427a97814de1587f89d73bf8677057 (patch)
tree4fb3306a4eff794bc2f6c1c48971e328026a334b /drivers
parentd814dee0e1a5d9b2f858b91551a0dd0608f777a1 (diff)
NET: am79c961: ensure multicast filter is correctly set at open
We were clearing out the multicast filter whenever the interface was upped, and not setting the mode bits correctly. This can cause problems if there are any multicast addresses already set at this point, or if ALLMULTI was set. Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/arm/am79c961a.c77
1 files changed, 40 insertions, 37 deletions
diff --git a/drivers/net/arm/am79c961a.c b/drivers/net/arm/am79c961a.c
index 084b67fbe7aa..79d88a0b00a3 100644
--- a/drivers/net/arm/am79c961a.c
+++ b/drivers/net/arm/am79c961a.c
@@ -196,6 +196,42 @@ am79c961_ramtest(struct net_device *dev, unsigned int val)
196 return errorcount; 196 return errorcount;
197} 197}
198 198
199static void am79c961_mc_hash(char *addr, u16 *hash)
200{
201 if (addr[0] & 0x01) {
202 int idx, bit;
203 u32 crc;
204
205 crc = ether_crc_le(ETH_ALEN, addr);
206
207 idx = crc >> 30;
208 bit = (crc >> 26) & 15;
209
210 hash[idx] |= 1 << bit;
211 }
212}
213
214static unsigned int am79c961_get_rx_mode(struct net_device *dev, u16 *hash)
215{
216 unsigned int mode = MODE_PORT_10BT;
217
218 if (dev->flags & IFF_PROMISC) {
219 mode |= MODE_PROMISC;
220 memset(hash, 0xff, 4 * sizeof(*hash));
221 } else if (dev->flags & IFF_ALLMULTI) {
222 memset(hash, 0xff, 4 * sizeof(*hash));
223 } else {
224 struct netdev_hw_addr *ha;
225
226 memset(hash, 0, 4 * sizeof(*hash));
227
228 netdev_for_each_mc_addr(ha, dev)
229 am79c961_mc_hash(ha->addr, hash);
230 }
231
232 return mode;
233}
234
199static void 235static void
200am79c961_init_for_open(struct net_device *dev) 236am79c961_init_for_open(struct net_device *dev)
201{ 237{
@@ -203,6 +239,7 @@ am79c961_init_for_open(struct net_device *dev)
203 unsigned long flags; 239 unsigned long flags;
204 unsigned char *p; 240 unsigned char *p;
205 u_int hdr_addr, first_free_addr; 241 u_int hdr_addr, first_free_addr;
242 u16 multi_hash[4], mode = am79c961_get_rx_mode(dev, multi_hash);
206 int i; 243 int i;
207 244
208 /* 245 /*
@@ -218,16 +255,12 @@ am79c961_init_for_open(struct net_device *dev)
218 write_ireg (dev->base_addr, 2, 0x0000); /* MODE register selects media */ 255 write_ireg (dev->base_addr, 2, 0x0000); /* MODE register selects media */
219 256
220 for (i = LADRL; i <= LADRH; i++) 257 for (i = LADRL; i <= LADRH; i++)
221 write_rreg (dev->base_addr, i, 0); 258 write_rreg (dev->base_addr, i, multi_hash[i - LADRL]);
222 259
223 for (i = PADRL, p = dev->dev_addr; i <= PADRH; i++, p += 2) 260 for (i = PADRL, p = dev->dev_addr; i <= PADRH; i++, p += 2)
224 write_rreg (dev->base_addr, i, p[0] | (p[1] << 8)); 261 write_rreg (dev->base_addr, i, p[0] | (p[1] << 8));
225 262
226 i = MODE_PORT_10BT; 263 write_rreg (dev->base_addr, MODE, mode);
227 if (dev->flags & IFF_PROMISC)
228 i |= MODE_PROMISC;
229
230 write_rreg (dev->base_addr, MODE, i);
231 write_rreg (dev->base_addr, POLLINT, 0); 264 write_rreg (dev->base_addr, POLLINT, 0);
232 write_rreg (dev->base_addr, SIZERXR, -RX_BUFFERS); 265 write_rreg (dev->base_addr, SIZERXR, -RX_BUFFERS);
233 write_rreg (dev->base_addr, SIZETXR, -TX_BUFFERS); 266 write_rreg (dev->base_addr, SIZETXR, -TX_BUFFERS);
@@ -340,21 +373,6 @@ am79c961_close(struct net_device *dev)
340 return 0; 373 return 0;
341} 374}
342 375
343static void am79c961_mc_hash(char *addr, unsigned short *hash)
344{
345 if (addr[0] & 0x01) {
346 int idx, bit;
347 u32 crc;
348
349 crc = ether_crc_le(ETH_ALEN, addr);
350
351 idx = crc >> 30;
352 bit = (crc >> 26) & 15;
353
354 hash[idx] |= 1 << bit;
355 }
356}
357
358/* 376/*
359 * Set or clear promiscuous/multicast mode filter for this adapter. 377 * Set or clear promiscuous/multicast mode filter for this adapter.
360 */ 378 */
@@ -362,24 +380,9 @@ static void am79c961_setmulticastlist (struct net_device *dev)
362{ 380{
363 struct dev_priv *priv = netdev_priv(dev); 381 struct dev_priv *priv = netdev_priv(dev);
364 unsigned long flags; 382 unsigned long flags;
365 unsigned short multi_hash[4], mode; 383 u16 multi_hash[4], mode = am79c961_get_rx_mode(dev, multi_hash);
366 int i, stopped; 384 int i, stopped;
367 385
368 mode = MODE_PORT_10BT;
369
370 if (dev->flags & IFF_PROMISC) {
371 mode |= MODE_PROMISC;
372 } else if (dev->flags & IFF_ALLMULTI) {
373 memset(multi_hash, 0xff, sizeof(multi_hash));
374 } else {
375 struct netdev_hw_addr *ha;
376
377 memset(multi_hash, 0x00, sizeof(multi_hash));
378
379 netdev_for_each_mc_addr(ha, dev)
380 am79c961_mc_hash(ha->addr, multi_hash);
381 }
382
383 spin_lock_irqsave(&priv->chip_lock, flags); 386 spin_lock_irqsave(&priv->chip_lock, flags);
384 387
385 stopped = read_rreg(dev->base_addr, CSR0) & CSR0_STOP; 388 stopped = read_rreg(dev->base_addr, CSR0) & CSR0_STOP;