aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLennert Buytenhek <buytenh@marvell.com>2008-11-20 06:58:27 -0500
committerDavid S. Miller <davem@davemloft.net>2008-11-20 06:58:27 -0500
commit66e63ffbc04706568d8789cbb00eaa8ddbcae648 (patch)
tree665aad7d94bc9d97a4413bfd15d65dd7db173388 /drivers
parent66823b928d746df21485deeff6744c77702abf29 (diff)
mv643xx_eth: implement ->set_rx_mode()
Currently, if multiple unicast addresses are programmed into a mv643xx_eth interface, the core networking will resort to enabling promiscuous mode on the interface, as mv643xx_eth does not implement ->set_rx_mode(). This patch switches mv643xx_eth over from ->set_multicast_list() to ->set_rx_mode(), and implements support for secondary unicast addresses. The hardware can handle multiple unicast addresses as long as their first 11 nibbles are the same (i.e. are of the form xx:xx:xx:xx:xx:xy where the x part is the same for all addresses), so if that is the case, we use that mode. If it's not the case, we enable unicast promiscuous mode in the hardware, which is slightly better than enabling promiscuous mode for multicasts as well, which is what would happen before. While we are at it, change the programming sequence so that we don't clear all filter bits first, so we don't lose all incoming packets while the filter is being reprogrammed. Signed-off-by: Lennert Buytenhek <buytenh@marvell.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/mv643xx_eth.c174
1 files changed, 104 insertions, 70 deletions
diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c
index 3326587d8ce3..9f3ee7d6a5bc 100644
--- a/drivers/net/mv643xx_eth.c
+++ b/drivers/net/mv643xx_eth.c
@@ -1448,11 +1448,8 @@ static const struct ethtool_ops mv643xx_eth_ethtool_ops_phyless = {
1448/* address handling *********************************************************/ 1448/* address handling *********************************************************/
1449static void uc_addr_get(struct mv643xx_eth_private *mp, unsigned char *addr) 1449static void uc_addr_get(struct mv643xx_eth_private *mp, unsigned char *addr)
1450{ 1450{
1451 unsigned int mac_h; 1451 unsigned int mac_h = rdlp(mp, MAC_ADDR_HIGH);
1452 unsigned int mac_l; 1452 unsigned int mac_l = rdlp(mp, MAC_ADDR_LOW);
1453
1454 mac_h = rdlp(mp, MAC_ADDR_HIGH);
1455 mac_l = rdlp(mp, MAC_ADDR_LOW);
1456 1453
1457 addr[0] = (mac_h >> 24) & 0xff; 1454 addr[0] = (mac_h >> 24) & 0xff;
1458 addr[1] = (mac_h >> 16) & 0xff; 1455 addr[1] = (mac_h >> 16) & 0xff;
@@ -1462,57 +1459,71 @@ static void uc_addr_get(struct mv643xx_eth_private *mp, unsigned char *addr)
1462 addr[5] = mac_l & 0xff; 1459 addr[5] = mac_l & 0xff;
1463} 1460}
1464 1461
1465static void init_mac_tables(struct mv643xx_eth_private *mp) 1462static void uc_addr_set(struct mv643xx_eth_private *mp, unsigned char *addr)
1466{ 1463{
1467 int i; 1464 wrlp(mp, MAC_ADDR_HIGH,
1468 1465 (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]);
1469 for (i = 0; i < 0x100; i += 4) { 1466 wrlp(mp, MAC_ADDR_LOW, (addr[4] << 8) | addr[5]);
1470 wrl(mp, SPECIAL_MCAST_TABLE(mp->port_num) + i, 0);
1471 wrl(mp, OTHER_MCAST_TABLE(mp->port_num) + i, 0);
1472 }
1473
1474 for (i = 0; i < 0x10; i += 4)
1475 wrl(mp, UNICAST_TABLE(mp->port_num) + i, 0);
1476} 1467}
1477 1468
1478static void set_filter_table_entry(struct mv643xx_eth_private *mp, 1469static u32 uc_addr_filter_mask(struct net_device *dev)
1479 int table, unsigned char entry)
1480{ 1470{
1481 unsigned int table_reg; 1471 struct dev_addr_list *uc_ptr;
1482 1472 u32 nibbles;
1483 /* Set "accepts frame bit" at specified table entry */
1484 table_reg = rdl(mp, table + (entry & 0xfc));
1485 table_reg |= 0x01 << (8 * (entry & 3));
1486 wrl(mp, table + (entry & 0xfc), table_reg);
1487}
1488 1473
1489static void uc_addr_set(struct mv643xx_eth_private *mp, unsigned char *addr) 1474 if (dev->flags & IFF_PROMISC)
1490{ 1475 return 0;
1491 unsigned int mac_h;
1492 unsigned int mac_l;
1493 int table;
1494 1476
1495 mac_l = (addr[4] << 8) | addr[5]; 1477 nibbles = 1 << (dev->dev_addr[5] & 0x0f);
1496 mac_h = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]; 1478 for (uc_ptr = dev->uc_list; uc_ptr != NULL; uc_ptr = uc_ptr->next) {
1479 if (memcmp(dev->dev_addr, uc_ptr->da_addr, 5))
1480 return 0;
1481 if ((dev->dev_addr[5] ^ uc_ptr->da_addr[5]) & 0xf0)
1482 return 0;
1497 1483
1498 wrlp(mp, MAC_ADDR_LOW, mac_l); 1484 nibbles |= 1 << (uc_ptr->da_addr[5] & 0x0f);
1499 wrlp(mp, MAC_ADDR_HIGH, mac_h); 1485 }
1500 1486
1501 table = UNICAST_TABLE(mp->port_num); 1487 return nibbles;
1502 set_filter_table_entry(mp, table, addr[5] & 0x0f);
1503} 1488}
1504 1489
1505static int mv643xx_eth_set_mac_address(struct net_device *dev, void *addr) 1490static void mv643xx_eth_program_unicast_filter(struct net_device *dev)
1506{ 1491{
1507 struct mv643xx_eth_private *mp = netdev_priv(dev); 1492 struct mv643xx_eth_private *mp = netdev_priv(dev);
1493 u32 port_config;
1494 u32 nibbles;
1495 int i;
1508 1496
1509 /* +2 is for the offset of the HW addr type */
1510 memcpy(dev->dev_addr, addr + 2, 6);
1511
1512 init_mac_tables(mp);
1513 uc_addr_set(mp, dev->dev_addr); 1497 uc_addr_set(mp, dev->dev_addr);
1514 1498
1515 return 0; 1499 port_config = rdlp(mp, PORT_CONFIG);
1500 nibbles = uc_addr_filter_mask(dev);
1501 if (!nibbles) {
1502 port_config |= UNICAST_PROMISCUOUS_MODE;
1503 wrlp(mp, PORT_CONFIG, port_config);
1504 return;
1505 }
1506
1507 for (i = 0; i < 16; i += 4) {
1508 int off = UNICAST_TABLE(mp->port_num) + i;
1509 u32 v;
1510
1511 v = 0;
1512 if (nibbles & 1)
1513 v |= 0x00000001;
1514 if (nibbles & 2)
1515 v |= 0x00000100;
1516 if (nibbles & 4)
1517 v |= 0x00010000;
1518 if (nibbles & 8)
1519 v |= 0x01000000;
1520 nibbles >>= 4;
1521
1522 wrl(mp, off, v);
1523 }
1524
1525 port_config &= ~UNICAST_PROMISCUOUS_MODE;
1526 wrlp(mp, PORT_CONFIG, port_config);
1516} 1527}
1517 1528
1518static int addr_crc(unsigned char *addr) 1529static int addr_crc(unsigned char *addr)
@@ -1533,24 +1544,22 @@ static int addr_crc(unsigned char *addr)
1533 return crc; 1544 return crc;
1534} 1545}
1535 1546
1536static void mv643xx_eth_set_rx_mode(struct net_device *dev) 1547static void mv643xx_eth_program_multicast_filter(struct net_device *dev)
1537{ 1548{
1538 struct mv643xx_eth_private *mp = netdev_priv(dev); 1549 struct mv643xx_eth_private *mp = netdev_priv(dev);
1539 u32 port_config; 1550 u32 *mc_spec;
1551 u32 *mc_other;
1540 struct dev_addr_list *addr; 1552 struct dev_addr_list *addr;
1541 int i; 1553 int i;
1542 1554
1543 port_config = rdlp(mp, PORT_CONFIG);
1544 if (dev->flags & IFF_PROMISC)
1545 port_config |= UNICAST_PROMISCUOUS_MODE;
1546 else
1547 port_config &= ~UNICAST_PROMISCUOUS_MODE;
1548 wrlp(mp, PORT_CONFIG, port_config);
1549
1550 if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) { 1555 if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
1551 int port_num = mp->port_num; 1556 int port_num;
1552 u32 accept = 0x01010101; 1557 u32 accept;
1558 int i;
1553 1559
1560oom:
1561 port_num = mp->port_num;
1562 accept = 0x01010101;
1554 for (i = 0; i < 0x100; i += 4) { 1563 for (i = 0; i < 0x100; i += 4) {
1555 wrl(mp, SPECIAL_MCAST_TABLE(port_num) + i, accept); 1564 wrl(mp, SPECIAL_MCAST_TABLE(port_num) + i, accept);
1556 wrl(mp, OTHER_MCAST_TABLE(port_num) + i, accept); 1565 wrl(mp, OTHER_MCAST_TABLE(port_num) + i, accept);
@@ -1558,28 +1567,55 @@ static void mv643xx_eth_set_rx_mode(struct net_device *dev)
1558 return; 1567 return;
1559 } 1568 }
1560 1569
1561 for (i = 0; i < 0x100; i += 4) { 1570 mc_spec = kmalloc(0x200, GFP_KERNEL);
1562 wrl(mp, SPECIAL_MCAST_TABLE(mp->port_num) + i, 0); 1571 if (mc_spec == NULL)
1563 wrl(mp, OTHER_MCAST_TABLE(mp->port_num) + i, 0); 1572 goto oom;
1564 } 1573 mc_other = mc_spec + (0x100 >> 2);
1574
1575 memset(mc_spec, 0, 0x100);
1576 memset(mc_other, 0, 0x100);
1565 1577
1566 for (addr = dev->mc_list; addr != NULL; addr = addr->next) { 1578 for (addr = dev->mc_list; addr != NULL; addr = addr->next) {
1567 u8 *a = addr->da_addr; 1579 u8 *a = addr->da_addr;
1568 int table; 1580 u32 *table;
1569 1581 int entry;
1570 if (addr->da_addrlen != 6)
1571 continue;
1572 1582
1573 if (memcmp(a, "\x01\x00\x5e\x00\x00", 5) == 0) { 1583 if (memcmp(a, "\x01\x00\x5e\x00\x00", 5) == 0) {
1574 table = SPECIAL_MCAST_TABLE(mp->port_num); 1584 table = mc_spec;
1575 set_filter_table_entry(mp, table, a[5]); 1585 entry = a[5];
1576 } else { 1586 } else {
1577 int crc = addr_crc(a); 1587 table = mc_other;
1578 1588 entry = addr_crc(a);
1579 table = OTHER_MCAST_TABLE(mp->port_num);
1580 set_filter_table_entry(mp, table, crc);
1581 } 1589 }
1590
1591 table[entry >> 2] |= 1 << (entry & 3);
1582 } 1592 }
1593
1594 for (i = 0; i < 0x100; i += 4) {
1595 wrl(mp, SPECIAL_MCAST_TABLE(mp->port_num) + i, mc_spec[i >> 2]);
1596 wrl(mp, OTHER_MCAST_TABLE(mp->port_num) + i, mc_other[i >> 2]);
1597 }
1598
1599 kfree(mc_spec);
1600}
1601
1602static void mv643xx_eth_set_rx_mode(struct net_device *dev)
1603{
1604 mv643xx_eth_program_unicast_filter(dev);
1605 mv643xx_eth_program_multicast_filter(dev);
1606}
1607
1608static int mv643xx_eth_set_mac_address(struct net_device *dev, void *addr)
1609{
1610 struct sockaddr *sa = addr;
1611
1612 memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
1613
1614 netif_addr_lock_bh(dev);
1615 mv643xx_eth_program_unicast_filter(dev);
1616 netif_addr_unlock_bh(dev);
1617
1618 return 0;
1583} 1619}
1584 1620
1585 1621
@@ -1988,7 +2024,7 @@ static void port_start(struct mv643xx_eth_private *mp)
1988 /* 2024 /*
1989 * Add configured unicast address to address filter table. 2025 * Add configured unicast address to address filter table.
1990 */ 2026 */
1991 uc_addr_set(mp, mp->dev->dev_addr); 2027 mv643xx_eth_program_unicast_filter(mp->dev);
1992 2028
1993 /* 2029 /*
1994 * Receive all unmatched unicast, TCP, UDP, BPDU and broadcast 2030 * Receive all unmatched unicast, TCP, UDP, BPDU and broadcast
@@ -2084,8 +2120,6 @@ static int mv643xx_eth_open(struct net_device *dev)
2084 return -EAGAIN; 2120 return -EAGAIN;
2085 } 2121 }
2086 2122
2087 init_mac_tables(mp);
2088
2089 mv643xx_eth_recalc_skb_size(mp); 2123 mv643xx_eth_recalc_skb_size(mp);
2090 2124
2091 napi_enable(&mp->napi); 2125 napi_enable(&mp->napi);
@@ -2667,7 +2701,7 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
2667 dev->hard_start_xmit = mv643xx_eth_xmit; 2701 dev->hard_start_xmit = mv643xx_eth_xmit;
2668 dev->open = mv643xx_eth_open; 2702 dev->open = mv643xx_eth_open;
2669 dev->stop = mv643xx_eth_stop; 2703 dev->stop = mv643xx_eth_stop;
2670 dev->set_multicast_list = mv643xx_eth_set_rx_mode; 2704 dev->set_rx_mode = mv643xx_eth_set_rx_mode;
2671 dev->set_mac_address = mv643xx_eth_set_mac_address; 2705 dev->set_mac_address = mv643xx_eth_set_mac_address;
2672 dev->do_ioctl = mv643xx_eth_ioctl; 2706 dev->do_ioctl = mv643xx_eth_ioctl;
2673 dev->change_mtu = mv643xx_eth_change_mtu; 2707 dev->change_mtu = mv643xx_eth_change_mtu;