diff options
author | shemminger@osdl.org <shemminger@osdl.org> | 2005-09-27 18:02:57 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@pobox.com> | 2005-09-28 11:49:32 -0400 |
commit | d1f1370863f7fa3d76dc7d7779debdda854a5a60 (patch) | |
tree | 9b27703564b30b25f9c772ec3afa4fa1fc22b536 /drivers/net/sky2.c | |
parent | d11c13e752c4e34777d33579ee0378e0178ef52d (diff) |
[PATCH] sky2: add hardware VLAN acceleration support
Use the hardware to do VLAN.
Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
Diffstat (limited to 'drivers/net/sky2.c')
-rw-r--r-- | drivers/net/sky2.c | 101 |
1 files changed, 87 insertions, 14 deletions
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 8527a49e3ff7..6d9c1db8896e 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c | |||
@@ -26,7 +26,6 @@ | |||
26 | /* | 26 | /* |
27 | * TODO | 27 | * TODO |
28 | * - coalescing setting? | 28 | * - coalescing setting? |
29 | * - vlan support | ||
30 | * | 29 | * |
31 | * TOTEST | 30 | * TOTEST |
32 | * - variable ring size | 31 | * - variable ring size |
@@ -48,9 +47,14 @@ | |||
48 | #include <linux/tcp.h> | 47 | #include <linux/tcp.h> |
49 | #include <linux/in.h> | 48 | #include <linux/in.h> |
50 | #include <linux/delay.h> | 49 | #include <linux/delay.h> |
50 | #include <linux/if_vlan.h> | ||
51 | 51 | ||
52 | #include <asm/irq.h> | 52 | #include <asm/irq.h> |
53 | 53 | ||
54 | #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) | ||
55 | #define SKY2_VLAN_TAG_USED 1 | ||
56 | #endif | ||
57 | |||
54 | #include "sky2.h" | 58 | #include "sky2.h" |
55 | 59 | ||
56 | #define DRV_NAME "sky2" | 60 | #define DRV_NAME "sky2" |
@@ -489,7 +493,7 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port) | |||
489 | /* Configure Rx MAC FIFO */ | 493 | /* Configure Rx MAC FIFO */ |
490 | sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_CLR); | 494 | sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_CLR); |
491 | sky2_write16(hw, SK_REG(port, RX_GMF_CTRL_T), | 495 | sky2_write16(hw, SK_REG(port, RX_GMF_CTRL_T), |
492 | GMF_OPER_ON | GMF_RX_F_FL_ON); | 496 | GMF_RX_CTRL_DEF); |
493 | 497 | ||
494 | /* Flush Rx MAC FIFO on any flowcontrol or error */ | 498 | /* Flush Rx MAC FIFO on any flowcontrol or error */ |
495 | reg = GMR_FS_ANY_ERR; | 499 | reg = GMR_FS_ANY_ERR; |
@@ -717,6 +721,41 @@ static void sky2_rx_clean(struct sky2_port *sky2) | |||
717 | } | 721 | } |
718 | } | 722 | } |
719 | 723 | ||
724 | #ifdef SKY2_VLAN_TAG_USED | ||
725 | static void sky2_vlan_rx_register(struct net_device *dev, struct vlan_group *grp) | ||
726 | { | ||
727 | struct sky2_port *sky2 = netdev_priv(dev); | ||
728 | struct sky2_hw *hw = sky2->hw; | ||
729 | u16 port = sky2->port; | ||
730 | unsigned long flags; | ||
731 | |||
732 | spin_lock_irqsave(&sky2->tx_lock, flags); | ||
733 | |||
734 | sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), RX_VLAN_STRIP_ON); | ||
735 | sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_VLAN_TAG_ON); | ||
736 | sky2->vlgrp = grp; | ||
737 | |||
738 | spin_unlock_irqrestore(&sky2->tx_lock, flags); | ||
739 | } | ||
740 | |||
741 | static void sky2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) | ||
742 | { | ||
743 | struct sky2_port *sky2 = netdev_priv(dev); | ||
744 | struct sky2_hw *hw = sky2->hw; | ||
745 | u16 port = sky2->port; | ||
746 | unsigned long flags; | ||
747 | |||
748 | spin_lock_irqsave(&sky2->tx_lock, flags); | ||
749 | |||
750 | sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), RX_VLAN_STRIP_OFF); | ||
751 | sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_VLAN_TAG_OFF); | ||
752 | if (sky2->vlgrp) | ||
753 | sky2->vlgrp->vlan_devices[vid] = NULL; | ||
754 | |||
755 | spin_unlock_irqrestore(&sky2->tx_lock, flags); | ||
756 | } | ||
757 | #endif | ||
758 | |||
720 | #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) | 759 | #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) |
721 | static inline unsigned rx_size(const struct sky2_port *sky2) | 760 | static inline unsigned rx_size(const struct sky2_port *sky2) |
722 | { | 761 | { |
@@ -894,7 +933,7 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) | |||
894 | { | 933 | { |
895 | struct sky2_port *sky2 = netdev_priv(dev); | 934 | struct sky2_port *sky2 = netdev_priv(dev); |
896 | struct sky2_hw *hw = sky2->hw; | 935 | struct sky2_hw *hw = sky2->hw; |
897 | struct sky2_tx_le *le; | 936 | struct sky2_tx_le *le = NULL; |
898 | struct ring_info *re; | 937 | struct ring_info *re; |
899 | unsigned long flags; | 938 | unsigned long flags; |
900 | unsigned i, len; | 939 | unsigned i, len; |
@@ -961,8 +1000,23 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) | |||
961 | sky2->tx_last_mss = mss; | 1000 | sky2->tx_last_mss = mss; |
962 | } | 1001 | } |
963 | 1002 | ||
964 | /* Handle TCP checksum offload */ | ||
965 | ctrl = 0; | 1003 | ctrl = 0; |
1004 | #ifdef SKY2_VLAN_TAG_USED | ||
1005 | /* Add VLAN tag, can piggyback on LRGLEN or ADDR64 */ | ||
1006 | if (sky2->vlgrp && vlan_tx_tag_present(skb)) { | ||
1007 | if (!le) { | ||
1008 | le = get_tx_le(sky2); | ||
1009 | le->tx.addr = 0; | ||
1010 | le->opcode = OP_VLAN|HW_OWNER; | ||
1011 | le->ctrl = 0; | ||
1012 | } else | ||
1013 | le->opcode |= OP_VLAN; | ||
1014 | le->length = cpu_to_be16(vlan_tx_tag_get(skb)); | ||
1015 | ctrl |= INS_VLAN; | ||
1016 | } | ||
1017 | #endif | ||
1018 | |||
1019 | /* Handle TCP checksum offload */ | ||
966 | if (skb->ip_summed == CHECKSUM_HW) { | 1020 | if (skb->ip_summed == CHECKSUM_HW) { |
967 | u16 hdr = skb->h.raw - skb->data; | 1021 | u16 hdr = skb->h.raw - skb->data; |
968 | u16 offset = hdr + skb->csum; | 1022 | u16 offset = hdr + skb->csum; |
@@ -1436,10 +1490,7 @@ static struct sk_buff *sky2_receive(struct sky2_port *sky2, | |||
1436 | 1490 | ||
1437 | sky2->rx_next = (sky2->rx_next + 1) % sky2->rx_pending; | 1491 | sky2->rx_next = (sky2->rx_next + 1) % sky2->rx_pending; |
1438 | 1492 | ||
1439 | if (!(status & GMR_FS_RX_OK) | 1493 | if (!(status & GMR_FS_RX_OK) || (status & GMR_FS_ANY_ERR)) |
1440 | || (status & GMR_FS_ANY_ERR) | ||
1441 | || (length << 16) != (status & GMR_FS_LEN) | ||
1442 | || length > bufsize) | ||
1443 | goto error; | 1494 | goto error; |
1444 | 1495 | ||
1445 | if (length < RX_COPY_THRESHOLD) { | 1496 | if (length < RX_COPY_THRESHOLD) { |
@@ -1539,6 +1590,9 @@ static int sky2_poll(struct net_device *dev0, int *budget) | |||
1539 | u16 length; | 1590 | u16 length; |
1540 | 1591 | ||
1541 | BUG_ON(le->link >= hw->ports); | 1592 | BUG_ON(le->link >= hw->ports); |
1593 | if (!hw->dev[le->link]) | ||
1594 | goto skip; | ||
1595 | |||
1542 | sky2 = netdev_priv(hw->dev[le->link]); | 1596 | sky2 = netdev_priv(hw->dev[le->link]); |
1543 | status = le32_to_cpu(le->status); | 1597 | status = le32_to_cpu(le->status); |
1544 | length = le16_to_cpu(le->length); | 1598 | length = le16_to_cpu(le->length); |
@@ -1546,12 +1600,27 @@ static int sky2_poll(struct net_device *dev0, int *budget) | |||
1546 | switch (le->opcode & ~HW_OWNER) { | 1600 | switch (le->opcode & ~HW_OWNER) { |
1547 | case OP_RXSTAT: | 1601 | case OP_RXSTAT: |
1548 | skb = sky2_receive(sky2, length, status); | 1602 | skb = sky2_receive(sky2, length, status); |
1549 | if (likely(skb)) { | 1603 | if (!skb) |
1604 | break; | ||
1605 | #ifdef SKY2_VLAN_TAG_USED | ||
1606 | if (sky2->vlgrp && (status & GMR_FS_VLAN)) { | ||
1607 | vlan_hwaccel_receive_skb(skb, | ||
1608 | sky2->vlgrp, | ||
1609 | be16_to_cpu(sky2->rx_tag)); | ||
1610 | } else | ||
1611 | #endif | ||
1550 | netif_receive_skb(skb); | 1612 | netif_receive_skb(skb); |
1551 | ++work_done; | ||
1552 | } | ||
1553 | break; | 1613 | break; |
1554 | 1614 | ||
1615 | #ifdef SKY2_VLAN_TAG_USED | ||
1616 | case OP_RXVLAN: | ||
1617 | sky2->rx_tag = length; | ||
1618 | break; | ||
1619 | |||
1620 | case OP_RXCHKSVLAN: | ||
1621 | sky2->rx_tag = length; | ||
1622 | /* fall through */ | ||
1623 | #endif | ||
1555 | case OP_RXCHKS: | 1624 | case OP_RXCHKS: |
1556 | skb = sky2->rx_ring[sky2->rx_next].skb; | 1625 | skb = sky2->rx_ring[sky2->rx_next].skb; |
1557 | skb->ip_summed = CHECKSUM_HW; | 1626 | skb->ip_summed = CHECKSUM_HW; |
@@ -1563,9 +1632,6 @@ static int sky2_poll(struct net_device *dev0, int *budget) | |||
1563 | tx_index(sky2->port, status, length)); | 1632 | tx_index(sky2->port, status, length)); |
1564 | break; | 1633 | break; |
1565 | 1634 | ||
1566 | case OP_RXTIMESTAMP: | ||
1567 | break; | ||
1568 | |||
1569 | default: | 1635 | default: |
1570 | if (net_ratelimit()) | 1636 | if (net_ratelimit()) |
1571 | printk(KERN_WARNING PFX | 1637 | printk(KERN_WARNING PFX |
@@ -1574,6 +1640,7 @@ static int sky2_poll(struct net_device *dev0, int *budget) | |||
1574 | break; | 1640 | break; |
1575 | } | 1641 | } |
1576 | 1642 | ||
1643 | skip: | ||
1577 | hw->st_idx = (hw->st_idx + 1) % STATUS_RING_SIZE; | 1644 | hw->st_idx = (hw->st_idx + 1) % STATUS_RING_SIZE; |
1578 | if (hw->st_idx == hwidx) { | 1645 | if (hw->st_idx == hwidx) { |
1579 | hwidx = sky2_read16(hw, STAT_PUT_IDX); | 1646 | hwidx = sky2_read16(hw, STAT_PUT_IDX); |
@@ -2615,6 +2682,12 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw, | |||
2615 | dev->features |= NETIF_F_HIGHDMA; | 2682 | dev->features |= NETIF_F_HIGHDMA; |
2616 | dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG; | 2683 | dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG; |
2617 | 2684 | ||
2685 | #ifdef SKY2_VLAN_TAG_USED | ||
2686 | dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; | ||
2687 | dev->vlan_rx_register = sky2_vlan_rx_register; | ||
2688 | dev->vlan_rx_kill_vid = sky2_vlan_rx_kill_vid; | ||
2689 | #endif | ||
2690 | |||
2618 | /* read the mac address */ | 2691 | /* read the mac address */ |
2619 | memcpy_fromio(dev->dev_addr, hw->regs + B2_MAC_1 + port * 8, ETH_ALEN); | 2692 | memcpy_fromio(dev->dev_addr, hw->regs + B2_MAC_1 + port * 8, ETH_ALEN); |
2620 | 2693 | ||