diff options
author | Steve Glendinning <steve.glendinning@smsc.com> | 2008-11-20 07:19:21 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-11-20 07:19:21 -0500 |
commit | f7b29271c35ed38f990ca3419696ca148349c2d3 (patch) | |
tree | 8f50d875b7e101f75c98f358a9f963ef1c024242 /drivers/net/usb | |
parent | 8761169cf15fdc9336abfddbb9c8dda59d974190 (diff) |
smsc95xx: add tx checksum offload support
LAN9500 supports tx checksum offload, which slightly decreases cpu
utilisation. The benefit isn't very large because we still require
the skb to be linearized, but it does save a few cycles.
This patch adds support for it, and enables it by default.
Signed-off-by: Steve Glendinning <steve.glendinning@smsc.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/usb')
-rw-r--r-- | drivers/net/usb/smsc95xx.c | 75 |
1 files changed, 65 insertions, 10 deletions
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index e0d349f74642..4638a7bb471e 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c | |||
@@ -31,7 +31,7 @@ | |||
31 | #include "smsc95xx.h" | 31 | #include "smsc95xx.h" |
32 | 32 | ||
33 | #define SMSC_CHIPNAME "smsc95xx" | 33 | #define SMSC_CHIPNAME "smsc95xx" |
34 | #define SMSC_DRIVER_VERSION "1.0.3" | 34 | #define SMSC_DRIVER_VERSION "1.0.4" |
35 | #define HS_USB_PKT_SIZE (512) | 35 | #define HS_USB_PKT_SIZE (512) |
36 | #define FS_USB_PKT_SIZE (64) | 36 | #define FS_USB_PKT_SIZE (64) |
37 | #define DEFAULT_HS_BURST_CAP_SIZE (16 * 1024 + 5 * HS_USB_PKT_SIZE) | 37 | #define DEFAULT_HS_BURST_CAP_SIZE (16 * 1024 + 5 * HS_USB_PKT_SIZE) |
@@ -40,15 +40,18 @@ | |||
40 | #define MAX_SINGLE_PACKET_SIZE (2048) | 40 | #define MAX_SINGLE_PACKET_SIZE (2048) |
41 | #define LAN95XX_EEPROM_MAGIC (0x9500) | 41 | #define LAN95XX_EEPROM_MAGIC (0x9500) |
42 | #define EEPROM_MAC_OFFSET (0x01) | 42 | #define EEPROM_MAC_OFFSET (0x01) |
43 | #define DEFAULT_TX_CSUM_ENABLE (true) | ||
43 | #define DEFAULT_RX_CSUM_ENABLE (true) | 44 | #define DEFAULT_RX_CSUM_ENABLE (true) |
44 | #define SMSC95XX_INTERNAL_PHY_ID (1) | 45 | #define SMSC95XX_INTERNAL_PHY_ID (1) |
45 | #define SMSC95XX_TX_OVERHEAD (8) | 46 | #define SMSC95XX_TX_OVERHEAD (8) |
47 | #define SMSC95XX_TX_OVERHEAD_CSUM (12) | ||
46 | #define FLOW_CTRL_TX (1) | 48 | #define FLOW_CTRL_TX (1) |
47 | #define FLOW_CTRL_RX (2) | 49 | #define FLOW_CTRL_RX (2) |
48 | 50 | ||
49 | struct smsc95xx_priv { | 51 | struct smsc95xx_priv { |
50 | u32 mac_cr; | 52 | u32 mac_cr; |
51 | spinlock_t mac_cr_lock; | 53 | spinlock_t mac_cr_lock; |
54 | bool use_tx_csum; | ||
52 | bool use_rx_csum; | 55 | bool use_rx_csum; |
53 | }; | 56 | }; |
54 | 57 | ||
@@ -556,9 +559,10 @@ static void smsc95xx_status(struct usbnet *dev, struct urb *urb) | |||
556 | devwarn(dev, "unexpected interrupt, intdata=0x%08X", intdata); | 559 | devwarn(dev, "unexpected interrupt, intdata=0x%08X", intdata); |
557 | } | 560 | } |
558 | 561 | ||
559 | /* Enable or disable Rx checksum offload engine */ | 562 | /* Enable or disable Tx & Rx checksum offload engines */ |
560 | static int smsc95xx_set_rx_csum(struct usbnet *dev, bool enable) | 563 | static int smsc95xx_set_csums(struct usbnet *dev) |
561 | { | 564 | { |
565 | struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); | ||
562 | u32 read_buf; | 566 | u32 read_buf; |
563 | int ret = smsc95xx_read_reg(dev, COE_CR, &read_buf); | 567 | int ret = smsc95xx_read_reg(dev, COE_CR, &read_buf); |
564 | if (ret < 0) { | 568 | if (ret < 0) { |
@@ -566,7 +570,12 @@ static int smsc95xx_set_rx_csum(struct usbnet *dev, bool enable) | |||
566 | return ret; | 570 | return ret; |
567 | } | 571 | } |
568 | 572 | ||
569 | if (enable) | 573 | if (pdata->use_tx_csum) |
574 | read_buf |= Tx_COE_EN_; | ||
575 | else | ||
576 | read_buf &= ~Tx_COE_EN_; | ||
577 | |||
578 | if (pdata->use_rx_csum) | ||
570 | read_buf |= Rx_COE_EN_; | 579 | read_buf |= Rx_COE_EN_; |
571 | else | 580 | else |
572 | read_buf &= ~Rx_COE_EN_; | 581 | read_buf &= ~Rx_COE_EN_; |
@@ -626,7 +635,26 @@ static int smsc95xx_ethtool_set_rx_csum(struct net_device *netdev, u32 val) | |||
626 | 635 | ||
627 | pdata->use_rx_csum = !!val; | 636 | pdata->use_rx_csum = !!val; |
628 | 637 | ||
629 | return smsc95xx_set_rx_csum(dev, pdata->use_rx_csum); | 638 | return smsc95xx_set_csums(dev); |
639 | } | ||
640 | |||
641 | static u32 smsc95xx_ethtool_get_tx_csum(struct net_device *netdev) | ||
642 | { | ||
643 | struct usbnet *dev = netdev_priv(netdev); | ||
644 | struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); | ||
645 | |||
646 | return pdata->use_tx_csum; | ||
647 | } | ||
648 | |||
649 | static int smsc95xx_ethtool_set_tx_csum(struct net_device *netdev, u32 val) | ||
650 | { | ||
651 | struct usbnet *dev = netdev_priv(netdev); | ||
652 | struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); | ||
653 | |||
654 | pdata->use_tx_csum = !!val; | ||
655 | |||
656 | ethtool_op_set_tx_hw_csum(netdev, pdata->use_tx_csum); | ||
657 | return smsc95xx_set_csums(dev); | ||
630 | } | 658 | } |
631 | 659 | ||
632 | static struct ethtool_ops smsc95xx_ethtool_ops = { | 660 | static struct ethtool_ops smsc95xx_ethtool_ops = { |
@@ -640,6 +668,8 @@ static struct ethtool_ops smsc95xx_ethtool_ops = { | |||
640 | .get_eeprom_len = smsc95xx_ethtool_get_eeprom_len, | 668 | .get_eeprom_len = smsc95xx_ethtool_get_eeprom_len, |
641 | .get_eeprom = smsc95xx_ethtool_get_eeprom, | 669 | .get_eeprom = smsc95xx_ethtool_get_eeprom, |
642 | .set_eeprom = smsc95xx_ethtool_set_eeprom, | 670 | .set_eeprom = smsc95xx_ethtool_set_eeprom, |
671 | .get_tx_csum = smsc95xx_ethtool_get_tx_csum, | ||
672 | .set_tx_csum = smsc95xx_ethtool_set_tx_csum, | ||
643 | .get_rx_csum = smsc95xx_ethtool_get_rx_csum, | 673 | .get_rx_csum = smsc95xx_ethtool_get_rx_csum, |
644 | .set_rx_csum = smsc95xx_ethtool_set_rx_csum, | 674 | .set_rx_csum = smsc95xx_ethtool_set_rx_csum, |
645 | }; | 675 | }; |
@@ -757,6 +787,7 @@ static int smsc95xx_phy_initialize(struct usbnet *dev) | |||
757 | static int smsc95xx_reset(struct usbnet *dev) | 787 | static int smsc95xx_reset(struct usbnet *dev) |
758 | { | 788 | { |
759 | struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); | 789 | struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); |
790 | struct net_device *netdev = dev->net; | ||
760 | u32 read_buf, write_buf, burst_cap; | 791 | u32 read_buf, write_buf, burst_cap; |
761 | int ret = 0, timeout; | 792 | int ret = 0, timeout; |
762 | 793 | ||
@@ -968,10 +999,11 @@ static int smsc95xx_reset(struct usbnet *dev) | |||
968 | return ret; | 999 | return ret; |
969 | } | 1000 | } |
970 | 1001 | ||
971 | /* Enable or disable Rx checksum offload engine */ | 1002 | /* Enable or disable checksum offload engines */ |
972 | ret = smsc95xx_set_rx_csum(dev, pdata->use_rx_csum); | 1003 | ethtool_op_set_tx_hw_csum(netdev, pdata->use_tx_csum); |
1004 | ret = smsc95xx_set_csums(dev); | ||
973 | if (ret < 0) { | 1005 | if (ret < 0) { |
974 | devwarn(dev, "Failed to set Rx csum offload: %d", ret); | 1006 | devwarn(dev, "Failed to set csum offload: %d", ret); |
975 | return ret; | 1007 | return ret; |
976 | } | 1008 | } |
977 | 1009 | ||
@@ -1027,6 +1059,7 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf) | |||
1027 | 1059 | ||
1028 | spin_lock_init(&pdata->mac_cr_lock); | 1060 | spin_lock_init(&pdata->mac_cr_lock); |
1029 | 1061 | ||
1062 | pdata->use_tx_csum = DEFAULT_TX_CSUM_ENABLE; | ||
1030 | pdata->use_rx_csum = DEFAULT_RX_CSUM_ENABLE; | 1063 | pdata->use_rx_csum = DEFAULT_RX_CSUM_ENABLE; |
1031 | 1064 | ||
1032 | /* Init all registers */ | 1065 | /* Init all registers */ |
@@ -1146,22 +1179,44 @@ static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb) | |||
1146 | return 1; | 1179 | return 1; |
1147 | } | 1180 | } |
1148 | 1181 | ||
1182 | static u32 smsc95xx_calc_csum_preamble(struct sk_buff *skb) | ||
1183 | { | ||
1184 | int len = skb->data - skb->head; | ||
1185 | u16 high_16 = (u16)(skb->csum_offset + skb->csum_start - len); | ||
1186 | u16 low_16 = (u16)(skb->csum_start - len); | ||
1187 | return (high_16 << 16) | low_16; | ||
1188 | } | ||
1189 | |||
1149 | static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev, | 1190 | static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev, |
1150 | struct sk_buff *skb, gfp_t flags) | 1191 | struct sk_buff *skb, gfp_t flags) |
1151 | { | 1192 | { |
1193 | struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); | ||
1194 | bool csum = pdata->use_tx_csum && (skb->ip_summed == CHECKSUM_PARTIAL); | ||
1195 | int overhead = csum ? SMSC95XX_TX_OVERHEAD_CSUM : SMSC95XX_TX_OVERHEAD; | ||
1152 | u32 tx_cmd_a, tx_cmd_b; | 1196 | u32 tx_cmd_a, tx_cmd_b; |
1153 | 1197 | ||
1154 | if (skb_headroom(skb) < SMSC95XX_TX_OVERHEAD) { | 1198 | /* We do not advertise SG, so skbs should be already linearized */ |
1199 | BUG_ON(skb_shinfo(skb)->nr_frags); | ||
1200 | |||
1201 | if (skb_headroom(skb) < overhead) { | ||
1155 | struct sk_buff *skb2 = skb_copy_expand(skb, | 1202 | struct sk_buff *skb2 = skb_copy_expand(skb, |
1156 | SMSC95XX_TX_OVERHEAD, 0, flags); | 1203 | overhead, 0, flags); |
1157 | dev_kfree_skb_any(skb); | 1204 | dev_kfree_skb_any(skb); |
1158 | skb = skb2; | 1205 | skb = skb2; |
1159 | if (!skb) | 1206 | if (!skb) |
1160 | return NULL; | 1207 | return NULL; |
1161 | } | 1208 | } |
1162 | 1209 | ||
1210 | if (csum) { | ||
1211 | u32 csum_preamble = smsc95xx_calc_csum_preamble(skb); | ||
1212 | skb_push(skb, 4); | ||
1213 | memcpy(skb->data, &csum_preamble, 4); | ||
1214 | } | ||
1215 | |||
1163 | skb_push(skb, 4); | 1216 | skb_push(skb, 4); |
1164 | tx_cmd_b = (u32)(skb->len - 4); | 1217 | tx_cmd_b = (u32)(skb->len - 4); |
1218 | if (csum) | ||
1219 | tx_cmd_b |= TX_CMD_B_CSUM_ENABLE; | ||
1165 | cpu_to_le32s(&tx_cmd_b); | 1220 | cpu_to_le32s(&tx_cmd_b); |
1166 | memcpy(skb->data, &tx_cmd_b, 4); | 1221 | memcpy(skb->data, &tx_cmd_b, 4); |
1167 | 1222 | ||