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 51e2f5d..5574abe 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -31,7 +31,7 @@ #include "smsc95xx.h" #define SMSC_CHIPNAME "smsc95xx" -#define SMSC_DRIVER_VERSION "1.0.3" +#define SMSC_DRIVER_VERSION "1.0.4" #define HS_USB_PKT_SIZE (512) #define FS_USB_PKT_SIZE (64) #define DEFAULT_HS_BURST_CAP_SIZE (16 * 1024 + 5 * HS_USB_PKT_SIZE) @@ -40,15 +40,16 @@ #define MAX_SINGLE_PACKET_SIZE (2048) #define LAN95XX_EEPROM_MAGIC (0x9500) #define EEPROM_MAC_OFFSET (0x01) +#define DEFAULT_TX_CSUM_ENABLE (true) #define DEFAULT_RX_CSUM_ENABLE (true) #define SMSC95XX_INTERNAL_PHY_ID (1) #define SMSC95XX_TX_OVERHEAD (8) -#define FLOW_CTRL_TX (1) -#define FLOW_CTRL_RX (2) +#define SMSC95XX_TX_OVERHEAD_CSUM (12) struct smsc95xx_priv { u32 mac_cr; spinlock_t mac_cr_lock; + bool use_tx_csum; bool use_rx_csum; }; @@ -310,9 +311,10 @@ static void smsc95xx_async_cmd_callback(struct urb *urb, struct pt_regs *regs) { struct usb_context *usb_context = urb->context; struct usbnet *dev = usb_context->dev; + int status = urb->status; - if (urb->status < 0) - devwarn(dev, "async callback failed with %d", urb->status); + if (status < 0) + devwarn(dev, "async callback failed with %d", status); complete(&usb_context->notify); @@ -434,28 +436,6 @@ static void smsc95xx_set_multicast(struct net_device *netdev) smsc95xx_write_reg_async(dev, MAC_CR, &pdata->mac_cr); } -static u8 smsc95xx_resolve_flowctrl_fulldplx(u16 lcladv, u16 rmtadv) -{ - u8 cap = 0; - - if (lcladv & ADVERTISE_PAUSE_CAP) { - if (lcladv & ADVERTISE_PAUSE_ASYM) { - if (rmtadv & LPA_PAUSE_CAP) - cap = FLOW_CTRL_TX | FLOW_CTRL_RX; - else if (rmtadv & LPA_PAUSE_ASYM) - cap = FLOW_CTRL_RX; - } else { - if (rmtadv & LPA_PAUSE_CAP) - cap = FLOW_CTRL_TX | FLOW_CTRL_RX; - } - } else if (lcladv & ADVERTISE_PAUSE_ASYM) { - if ((rmtadv & LPA_PAUSE_CAP) && (rmtadv & LPA_PAUSE_ASYM)) - cap = FLOW_CTRL_TX; - } - - return cap; -} - static void smsc95xx_phy_update_flowcontrol(struct usbnet *dev, u8 duplex, u16 lcladv, u16 rmtadv) { @@ -468,7 +448,7 @@ static void smsc95xx_phy_update_flowcontrol(struct usbnet *dev, u8 duplex, } if (duplex == DUPLEX_FULL) { - u8 cap = smsc95xx_resolve_flowctrl_fulldplx(lcladv, rmtadv); + u8 cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv); if (cap & FLOW_CTRL_RX) flow = 0xFFFF0002; @@ -556,9 +536,10 @@ static void smsc95xx_status(struct usbnet *dev, struct urb *urb) devwarn(dev, "unexpected interrupt, intdata=0x%08X", intdata); } -/* Enable or disable Rx checksum offload engine */ -static int smsc95xx_set_rx_csum(struct usbnet *dev, bool enable) +/* Enable or disable Tx & Rx checksum offload engines */ +static int smsc95xx_set_csums(struct usbnet *dev) { + struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); u32 read_buf; int ret = smsc95xx_read_reg(dev, COE_CR, &read_buf); if (ret < 0) { @@ -566,7 +547,12 @@ static int smsc95xx_set_rx_csum(struct usbnet *dev, bool enable) return ret; } - if (enable) + if (pdata->use_tx_csum) + read_buf |= Tx_COE_EN_; + else + read_buf &= ~Tx_COE_EN_; + + if (pdata->use_rx_csum) read_buf |= Rx_COE_EN_; else read_buf &= ~Rx_COE_EN_; @@ -626,7 +612,26 @@ static int smsc95xx_ethtool_set_rx_csum(struct net_device *netdev, u32 val) pdata->use_rx_csum = !!val; - return smsc95xx_set_rx_csum(dev, pdata->use_rx_csum); + return smsc95xx_set_csums(dev); +} + +static u32 smsc95xx_ethtool_get_tx_csum(struct net_device *netdev) +{ + struct usbnet *dev = netdev_priv(netdev); + struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); + + return pdata->use_tx_csum; +} + +static int smsc95xx_ethtool_set_tx_csum(struct net_device *netdev, u32 val) +{ + struct usbnet *dev = netdev_priv(netdev); + struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); + + pdata->use_tx_csum = !!val; + + ethtool_op_set_tx_hw_csum(netdev, pdata->use_tx_csum); + return smsc95xx_set_csums(dev); } static struct ethtool_ops smsc95xx_ethtool_ops = { @@ -640,6 +645,8 @@ static struct ethtool_ops smsc95xx_ethtool_ops = { .get_eeprom_len = smsc95xx_ethtool_get_eeprom_len, .get_eeprom = smsc95xx_ethtool_get_eeprom, .set_eeprom = smsc95xx_ethtool_set_eeprom, + .get_tx_csum = smsc95xx_ethtool_get_tx_csum, + .set_tx_csum = smsc95xx_ethtool_set_tx_csum, .get_rx_csum = smsc95xx_ethtool_get_rx_csum, .set_rx_csum = smsc95xx_ethtool_set_rx_csum, }; @@ -757,9 +764,9 @@ static int smsc95xx_phy_initialize(struct usbnet *dev) static int smsc95xx_reset(struct usbnet *dev) { struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); + struct net_device *netdev = dev->net; u32 read_buf, write_buf, burst_cap; int ret = 0, timeout; - DECLARE_MAC_BUF(mac); if (netif_msg_ifup(dev)) devdbg(dev, "entering smsc95xx_reset"); @@ -818,8 +825,7 @@ static int smsc95xx_reset(struct usbnet *dev) return ret; if (netif_msg_ifup(dev)) - devdbg(dev, "MAC Address: %s", - print_mac(mac, dev->net->dev_addr)); + devdbg(dev, "MAC Address: %pM", dev->net->dev_addr); ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); if (ret < 0) { @@ -970,10 +976,11 @@ static int smsc95xx_reset(struct usbnet *dev) return ret; } - /* Enable or disable Rx checksum offload engine */ - ret = smsc95xx_set_rx_csum(dev, pdata->use_rx_csum); + /* Enable or disable checksum offload engines */ + ethtool_op_set_tx_hw_csum(netdev, pdata->use_tx_csum); + ret = smsc95xx_set_csums(dev); if (ret < 0) { - devwarn(dev, "Failed to set Rx csum offload: %d", ret); + devwarn(dev, "Failed to set csum offload: %d", ret); return ret; } @@ -1029,6 +1036,7 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf) spin_lock_init(&pdata->mac_cr_lock); + pdata->use_tx_csum = DEFAULT_TX_CSUM_ENABLE; pdata->use_rx_csum = DEFAULT_RX_CSUM_ENABLE; /* Init all registers */ @@ -1148,22 +1156,44 @@ static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb) return 1; } +static u32 smsc95xx_calc_csum_preamble(struct sk_buff *skb) +{ + int len = skb->data - skb->head; + u16 high_16 = (u16)(skb->csum_offset + skb->csum_start - len); + u16 low_16 = (u16)(skb->csum_start - len); + return (high_16 << 16) | low_16; +} + static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) { + struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); + bool csum = pdata->use_tx_csum && (skb->ip_summed == CHECKSUM_PARTIAL); + int overhead = csum ? SMSC95XX_TX_OVERHEAD_CSUM : SMSC95XX_TX_OVERHEAD; u32 tx_cmd_a, tx_cmd_b; - if (skb_headroom(skb) < SMSC95XX_TX_OVERHEAD) { + /* We do not advertise SG, so skbs should be already linearized */ + BUG_ON(skb_shinfo(skb)->nr_frags); + + if (skb_headroom(skb) < overhead) { struct sk_buff *skb2 = skb_copy_expand(skb, - SMSC95XX_TX_OVERHEAD, 0, flags); + overhead, 0, flags); dev_kfree_skb_any(skb); skb = skb2; if (!skb) return NULL; } + if (csum) { + u32 csum_preamble = smsc95xx_calc_csum_preamble(skb); + skb_push(skb, 4); + memcpy(skb->data, &csum_preamble, 4); + } + skb_push(skb, 4); tx_cmd_b = (u32)(skb->len - 4); + if (csum) + tx_cmd_b |= TX_CMD_B_CSUM_ENABLE; cpu_to_le32s(&tx_cmd_b); memcpy(skb->data, &tx_cmd_b, 4); |