diff options
Diffstat (limited to 'drivers/net/usb/smsc95xx.c')
-rw-r--r-- | drivers/net/usb/smsc95xx.c | 110 |
1 files changed, 70 insertions, 40 deletions
diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index 51e2f5d7d14e..5574abe29c73 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,16 @@ | |||
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) |
46 | #define FLOW_CTRL_TX (1) | 47 | #define SMSC95XX_TX_OVERHEAD_CSUM (12) |
47 | #define FLOW_CTRL_RX (2) | ||
48 | 48 | ||
49 | struct smsc95xx_priv { | 49 | struct smsc95xx_priv { |
50 | u32 mac_cr; | 50 | u32 mac_cr; |
51 | spinlock_t mac_cr_lock; | 51 | spinlock_t mac_cr_lock; |
52 | bool use_tx_csum; | ||
52 | bool use_rx_csum; | 53 | bool use_rx_csum; |
53 | }; | 54 | }; |
54 | 55 | ||
@@ -310,9 +311,10 @@ static void smsc95xx_async_cmd_callback(struct urb *urb, struct pt_regs *regs) | |||
310 | { | 311 | { |
311 | struct usb_context *usb_context = urb->context; | 312 | struct usb_context *usb_context = urb->context; |
312 | struct usbnet *dev = usb_context->dev; | 313 | struct usbnet *dev = usb_context->dev; |
314 | int status = urb->status; | ||
313 | 315 | ||
314 | if (urb->status < 0) | 316 | if (status < 0) |
315 | devwarn(dev, "async callback failed with %d", urb->status); | 317 | devwarn(dev, "async callback failed with %d", status); |
316 | 318 | ||
317 | complete(&usb_context->notify); | 319 | complete(&usb_context->notify); |
318 | 320 | ||
@@ -434,28 +436,6 @@ static void smsc95xx_set_multicast(struct net_device *netdev) | |||
434 | smsc95xx_write_reg_async(dev, MAC_CR, &pdata->mac_cr); | 436 | smsc95xx_write_reg_async(dev, MAC_CR, &pdata->mac_cr); |
435 | } | 437 | } |
436 | 438 | ||
437 | static u8 smsc95xx_resolve_flowctrl_fulldplx(u16 lcladv, u16 rmtadv) | ||
438 | { | ||
439 | u8 cap = 0; | ||
440 | |||
441 | if (lcladv & ADVERTISE_PAUSE_CAP) { | ||
442 | if (lcladv & ADVERTISE_PAUSE_ASYM) { | ||
443 | if (rmtadv & LPA_PAUSE_CAP) | ||
444 | cap = FLOW_CTRL_TX | FLOW_CTRL_RX; | ||
445 | else if (rmtadv & LPA_PAUSE_ASYM) | ||
446 | cap = FLOW_CTRL_RX; | ||
447 | } else { | ||
448 | if (rmtadv & LPA_PAUSE_CAP) | ||
449 | cap = FLOW_CTRL_TX | FLOW_CTRL_RX; | ||
450 | } | ||
451 | } else if (lcladv & ADVERTISE_PAUSE_ASYM) { | ||
452 | if ((rmtadv & LPA_PAUSE_CAP) && (rmtadv & LPA_PAUSE_ASYM)) | ||
453 | cap = FLOW_CTRL_TX; | ||
454 | } | ||
455 | |||
456 | return cap; | ||
457 | } | ||
458 | |||
459 | static void smsc95xx_phy_update_flowcontrol(struct usbnet *dev, u8 duplex, | 439 | static void smsc95xx_phy_update_flowcontrol(struct usbnet *dev, u8 duplex, |
460 | u16 lcladv, u16 rmtadv) | 440 | u16 lcladv, u16 rmtadv) |
461 | { | 441 | { |
@@ -468,7 +448,7 @@ static void smsc95xx_phy_update_flowcontrol(struct usbnet *dev, u8 duplex, | |||
468 | } | 448 | } |
469 | 449 | ||
470 | if (duplex == DUPLEX_FULL) { | 450 | if (duplex == DUPLEX_FULL) { |
471 | u8 cap = smsc95xx_resolve_flowctrl_fulldplx(lcladv, rmtadv); | 451 | u8 cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv); |
472 | 452 | ||
473 | if (cap & FLOW_CTRL_RX) | 453 | if (cap & FLOW_CTRL_RX) |
474 | flow = 0xFFFF0002; | 454 | flow = 0xFFFF0002; |
@@ -556,9 +536,10 @@ static void smsc95xx_status(struct usbnet *dev, struct urb *urb) | |||
556 | devwarn(dev, "unexpected interrupt, intdata=0x%08X", intdata); | 536 | devwarn(dev, "unexpected interrupt, intdata=0x%08X", intdata); |
557 | } | 537 | } |
558 | 538 | ||
559 | /* Enable or disable Rx checksum offload engine */ | 539 | /* Enable or disable Tx & Rx checksum offload engines */ |
560 | static int smsc95xx_set_rx_csum(struct usbnet *dev, bool enable) | 540 | static int smsc95xx_set_csums(struct usbnet *dev) |
561 | { | 541 | { |
542 | struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); | ||
562 | u32 read_buf; | 543 | u32 read_buf; |
563 | int ret = smsc95xx_read_reg(dev, COE_CR, &read_buf); | 544 | int ret = smsc95xx_read_reg(dev, COE_CR, &read_buf); |
564 | if (ret < 0) { | 545 | if (ret < 0) { |
@@ -566,7 +547,12 @@ static int smsc95xx_set_rx_csum(struct usbnet *dev, bool enable) | |||
566 | return ret; | 547 | return ret; |
567 | } | 548 | } |
568 | 549 | ||
569 | if (enable) | 550 | if (pdata->use_tx_csum) |
551 | read_buf |= Tx_COE_EN_; | ||
552 | else | ||
553 | read_buf &= ~Tx_COE_EN_; | ||
554 | |||
555 | if (pdata->use_rx_csum) | ||
570 | read_buf |= Rx_COE_EN_; | 556 | read_buf |= Rx_COE_EN_; |
571 | else | 557 | else |
572 | read_buf &= ~Rx_COE_EN_; | 558 | read_buf &= ~Rx_COE_EN_; |
@@ -626,7 +612,26 @@ static int smsc95xx_ethtool_set_rx_csum(struct net_device *netdev, u32 val) | |||
626 | 612 | ||
627 | pdata->use_rx_csum = !!val; | 613 | pdata->use_rx_csum = !!val; |
628 | 614 | ||
629 | return smsc95xx_set_rx_csum(dev, pdata->use_rx_csum); | 615 | return smsc95xx_set_csums(dev); |
616 | } | ||
617 | |||
618 | static u32 smsc95xx_ethtool_get_tx_csum(struct net_device *netdev) | ||
619 | { | ||
620 | struct usbnet *dev = netdev_priv(netdev); | ||
621 | struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); | ||
622 | |||
623 | return pdata->use_tx_csum; | ||
624 | } | ||
625 | |||
626 | static int smsc95xx_ethtool_set_tx_csum(struct net_device *netdev, u32 val) | ||
627 | { | ||
628 | struct usbnet *dev = netdev_priv(netdev); | ||
629 | struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); | ||
630 | |||
631 | pdata->use_tx_csum = !!val; | ||
632 | |||
633 | ethtool_op_set_tx_hw_csum(netdev, pdata->use_tx_csum); | ||
634 | return smsc95xx_set_csums(dev); | ||
630 | } | 635 | } |
631 | 636 | ||
632 | static struct ethtool_ops smsc95xx_ethtool_ops = { | 637 | static struct ethtool_ops smsc95xx_ethtool_ops = { |
@@ -640,6 +645,8 @@ static struct ethtool_ops smsc95xx_ethtool_ops = { | |||
640 | .get_eeprom_len = smsc95xx_ethtool_get_eeprom_len, | 645 | .get_eeprom_len = smsc95xx_ethtool_get_eeprom_len, |
641 | .get_eeprom = smsc95xx_ethtool_get_eeprom, | 646 | .get_eeprom = smsc95xx_ethtool_get_eeprom, |
642 | .set_eeprom = smsc95xx_ethtool_set_eeprom, | 647 | .set_eeprom = smsc95xx_ethtool_set_eeprom, |
648 | .get_tx_csum = smsc95xx_ethtool_get_tx_csum, | ||
649 | .set_tx_csum = smsc95xx_ethtool_set_tx_csum, | ||
643 | .get_rx_csum = smsc95xx_ethtool_get_rx_csum, | 650 | .get_rx_csum = smsc95xx_ethtool_get_rx_csum, |
644 | .set_rx_csum = smsc95xx_ethtool_set_rx_csum, | 651 | .set_rx_csum = smsc95xx_ethtool_set_rx_csum, |
645 | }; | 652 | }; |
@@ -757,9 +764,9 @@ static int smsc95xx_phy_initialize(struct usbnet *dev) | |||
757 | static int smsc95xx_reset(struct usbnet *dev) | 764 | static int smsc95xx_reset(struct usbnet *dev) |
758 | { | 765 | { |
759 | struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); | 766 | struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); |
767 | struct net_device *netdev = dev->net; | ||
760 | u32 read_buf, write_buf, burst_cap; | 768 | u32 read_buf, write_buf, burst_cap; |
761 | int ret = 0, timeout; | 769 | int ret = 0, timeout; |
762 | DECLARE_MAC_BUF(mac); | ||
763 | 770 | ||
764 | if (netif_msg_ifup(dev)) | 771 | if (netif_msg_ifup(dev)) |
765 | devdbg(dev, "entering smsc95xx_reset"); | 772 | devdbg(dev, "entering smsc95xx_reset"); |
@@ -818,8 +825,7 @@ static int smsc95xx_reset(struct usbnet *dev) | |||
818 | return ret; | 825 | return ret; |
819 | 826 | ||
820 | if (netif_msg_ifup(dev)) | 827 | if (netif_msg_ifup(dev)) |
821 | devdbg(dev, "MAC Address: %s", | 828 | devdbg(dev, "MAC Address: %pM", dev->net->dev_addr); |
822 | print_mac(mac, dev->net->dev_addr)); | ||
823 | 829 | ||
824 | ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); | 830 | ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); |
825 | if (ret < 0) { | 831 | if (ret < 0) { |
@@ -970,10 +976,11 @@ static int smsc95xx_reset(struct usbnet *dev) | |||
970 | return ret; | 976 | return ret; |
971 | } | 977 | } |
972 | 978 | ||
973 | /* Enable or disable Rx checksum offload engine */ | 979 | /* Enable or disable checksum offload engines */ |
974 | ret = smsc95xx_set_rx_csum(dev, pdata->use_rx_csum); | 980 | ethtool_op_set_tx_hw_csum(netdev, pdata->use_tx_csum); |
981 | ret = smsc95xx_set_csums(dev); | ||
975 | if (ret < 0) { | 982 | if (ret < 0) { |
976 | devwarn(dev, "Failed to set Rx csum offload: %d", ret); | 983 | devwarn(dev, "Failed to set csum offload: %d", ret); |
977 | return ret; | 984 | return ret; |
978 | } | 985 | } |
979 | 986 | ||
@@ -1029,6 +1036,7 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf) | |||
1029 | 1036 | ||
1030 | spin_lock_init(&pdata->mac_cr_lock); | 1037 | spin_lock_init(&pdata->mac_cr_lock); |
1031 | 1038 | ||
1039 | pdata->use_tx_csum = DEFAULT_TX_CSUM_ENABLE; | ||
1032 | pdata->use_rx_csum = DEFAULT_RX_CSUM_ENABLE; | 1040 | pdata->use_rx_csum = DEFAULT_RX_CSUM_ENABLE; |
1033 | 1041 | ||
1034 | /* Init all registers */ | 1042 | /* Init all registers */ |
@@ -1148,22 +1156,44 @@ static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb) | |||
1148 | return 1; | 1156 | return 1; |
1149 | } | 1157 | } |
1150 | 1158 | ||
1159 | static u32 smsc95xx_calc_csum_preamble(struct sk_buff *skb) | ||
1160 | { | ||
1161 | int len = skb->data - skb->head; | ||
1162 | u16 high_16 = (u16)(skb->csum_offset + skb->csum_start - len); | ||
1163 | u16 low_16 = (u16)(skb->csum_start - len); | ||
1164 | return (high_16 << 16) | low_16; | ||
1165 | } | ||
1166 | |||
1151 | static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev, | 1167 | static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev, |
1152 | struct sk_buff *skb, gfp_t flags) | 1168 | struct sk_buff *skb, gfp_t flags) |
1153 | { | 1169 | { |
1170 | struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); | ||
1171 | bool csum = pdata->use_tx_csum && (skb->ip_summed == CHECKSUM_PARTIAL); | ||
1172 | int overhead = csum ? SMSC95XX_TX_OVERHEAD_CSUM : SMSC95XX_TX_OVERHEAD; | ||
1154 | u32 tx_cmd_a, tx_cmd_b; | 1173 | u32 tx_cmd_a, tx_cmd_b; |
1155 | 1174 | ||
1156 | if (skb_headroom(skb) < SMSC95XX_TX_OVERHEAD) { | 1175 | /* We do not advertise SG, so skbs should be already linearized */ |
1176 | BUG_ON(skb_shinfo(skb)->nr_frags); | ||
1177 | |||
1178 | if (skb_headroom(skb) < overhead) { | ||
1157 | struct sk_buff *skb2 = skb_copy_expand(skb, | 1179 | struct sk_buff *skb2 = skb_copy_expand(skb, |
1158 | SMSC95XX_TX_OVERHEAD, 0, flags); | 1180 | overhead, 0, flags); |
1159 | dev_kfree_skb_any(skb); | 1181 | dev_kfree_skb_any(skb); |
1160 | skb = skb2; | 1182 | skb = skb2; |
1161 | if (!skb) | 1183 | if (!skb) |
1162 | return NULL; | 1184 | return NULL; |
1163 | } | 1185 | } |
1164 | 1186 | ||
1187 | if (csum) { | ||
1188 | u32 csum_preamble = smsc95xx_calc_csum_preamble(skb); | ||
1189 | skb_push(skb, 4); | ||
1190 | memcpy(skb->data, &csum_preamble, 4); | ||
1191 | } | ||
1192 | |||
1165 | skb_push(skb, 4); | 1193 | skb_push(skb, 4); |
1166 | tx_cmd_b = (u32)(skb->len - 4); | 1194 | tx_cmd_b = (u32)(skb->len - 4); |
1195 | if (csum) | ||
1196 | tx_cmd_b |= TX_CMD_B_CSUM_ENABLE; | ||
1167 | cpu_to_le32s(&tx_cmd_b); | 1197 | cpu_to_le32s(&tx_cmd_b); |
1168 | memcpy(skb->data, &tx_cmd_b, 4); | 1198 | memcpy(skb->data, &tx_cmd_b, 4); |
1169 | 1199 | ||