|
@@ -31,7 +31,7 @@
|
|
#include "smsc95xx.h"
|
|
#include "smsc95xx.h"
|
|
|
|
|
|
#define SMSC_CHIPNAME "smsc95xx"
|
|
#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 HS_USB_PKT_SIZE (512)
|
|
#define FS_USB_PKT_SIZE (64)
|
|
#define FS_USB_PKT_SIZE (64)
|
|
#define DEFAULT_HS_BURST_CAP_SIZE (16 * 1024 + 5 * HS_USB_PKT_SIZE)
|
|
#define DEFAULT_HS_BURST_CAP_SIZE (16 * 1024 + 5 * HS_USB_PKT_SIZE)
|
|
@@ -40,15 +40,18 @@
|
|
#define MAX_SINGLE_PACKET_SIZE (2048)
|
|
#define MAX_SINGLE_PACKET_SIZE (2048)
|
|
#define LAN95XX_EEPROM_MAGIC (0x9500)
|
|
#define LAN95XX_EEPROM_MAGIC (0x9500)
|
|
#define EEPROM_MAC_OFFSET (0x01)
|
|
#define EEPROM_MAC_OFFSET (0x01)
|
|
|
|
+#define DEFAULT_TX_CSUM_ENABLE (true)
|
|
#define DEFAULT_RX_CSUM_ENABLE (true)
|
|
#define DEFAULT_RX_CSUM_ENABLE (true)
|
|
#define SMSC95XX_INTERNAL_PHY_ID (1)
|
|
#define SMSC95XX_INTERNAL_PHY_ID (1)
|
|
#define SMSC95XX_TX_OVERHEAD (8)
|
|
#define SMSC95XX_TX_OVERHEAD (8)
|
|
|
|
+#define SMSC95XX_TX_OVERHEAD_CSUM (12)
|
|
#define FLOW_CTRL_TX (1)
|
|
#define FLOW_CTRL_TX (1)
|
|
#define FLOW_CTRL_RX (2)
|
|
#define FLOW_CTRL_RX (2)
|
|
|
|
|
|
struct smsc95xx_priv {
|
|
struct smsc95xx_priv {
|
|
u32 mac_cr;
|
|
u32 mac_cr;
|
|
spinlock_t mac_cr_lock;
|
|
spinlock_t mac_cr_lock;
|
|
|
|
+ bool use_tx_csum;
|
|
bool use_rx_csum;
|
|
bool use_rx_csum;
|
|
};
|
|
};
|
|
|
|
|
|
@@ -556,9 +559,10 @@ static void smsc95xx_status(struct usbnet *dev, struct urb *urb)
|
|
devwarn(dev, "unexpected interrupt, intdata=0x%08X", intdata);
|
|
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;
|
|
u32 read_buf;
|
|
int ret = smsc95xx_read_reg(dev, COE_CR, &read_buf);
|
|
int ret = smsc95xx_read_reg(dev, COE_CR, &read_buf);
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
@@ -566,7 +570,12 @@ static int smsc95xx_set_rx_csum(struct usbnet *dev, bool enable)
|
|
return ret;
|
|
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_;
|
|
read_buf |= Rx_COE_EN_;
|
|
else
|
|
else
|
|
read_buf &= ~Rx_COE_EN_;
|
|
read_buf &= ~Rx_COE_EN_;
|
|
@@ -626,7 +635,26 @@ static int smsc95xx_ethtool_set_rx_csum(struct net_device *netdev, u32 val)
|
|
|
|
|
|
pdata->use_rx_csum = !!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 = {
|
|
static struct ethtool_ops smsc95xx_ethtool_ops = {
|
|
@@ -640,6 +668,8 @@ static struct ethtool_ops smsc95xx_ethtool_ops = {
|
|
.get_eeprom_len = smsc95xx_ethtool_get_eeprom_len,
|
|
.get_eeprom_len = smsc95xx_ethtool_get_eeprom_len,
|
|
.get_eeprom = smsc95xx_ethtool_get_eeprom,
|
|
.get_eeprom = smsc95xx_ethtool_get_eeprom,
|
|
.set_eeprom = smsc95xx_ethtool_set_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,
|
|
.get_rx_csum = smsc95xx_ethtool_get_rx_csum,
|
|
.set_rx_csum = smsc95xx_ethtool_set_rx_csum,
|
|
.set_rx_csum = smsc95xx_ethtool_set_rx_csum,
|
|
};
|
|
};
|
|
@@ -757,6 +787,7 @@ static int smsc95xx_phy_initialize(struct usbnet *dev)
|
|
static int smsc95xx_reset(struct usbnet *dev)
|
|
static int smsc95xx_reset(struct usbnet *dev)
|
|
{
|
|
{
|
|
struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
|
|
struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
|
|
|
|
+ struct net_device *netdev = dev->net;
|
|
u32 read_buf, write_buf, burst_cap;
|
|
u32 read_buf, write_buf, burst_cap;
|
|
int ret = 0, timeout;
|
|
int ret = 0, timeout;
|
|
|
|
|
|
@@ -968,10 +999,11 @@ static int smsc95xx_reset(struct usbnet *dev)
|
|
return ret;
|
|
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) {
|
|
if (ret < 0) {
|
|
- devwarn(dev, "Failed to set Rx csum offload: %d", ret);
|
|
|
|
|
|
+ devwarn(dev, "Failed to set csum offload: %d", ret);
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1027,6 +1059,7 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
|
|
|
|
|
|
spin_lock_init(&pdata->mac_cr_lock);
|
|
spin_lock_init(&pdata->mac_cr_lock);
|
|
|
|
|
|
|
|
+ pdata->use_tx_csum = DEFAULT_TX_CSUM_ENABLE;
|
|
pdata->use_rx_csum = DEFAULT_RX_CSUM_ENABLE;
|
|
pdata->use_rx_csum = DEFAULT_RX_CSUM_ENABLE;
|
|
|
|
|
|
/* Init all registers */
|
|
/* Init all registers */
|
|
@@ -1146,22 +1179,44 @@ static int smsc95xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
|
|
return 1;
|
|
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,
|
|
static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,
|
|
struct sk_buff *skb, gfp_t flags)
|
|
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;
|
|
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,
|
|
struct sk_buff *skb2 = skb_copy_expand(skb,
|
|
- SMSC95XX_TX_OVERHEAD, 0, flags);
|
|
|
|
|
|
+ overhead, 0, flags);
|
|
dev_kfree_skb_any(skb);
|
|
dev_kfree_skb_any(skb);
|
|
skb = skb2;
|
|
skb = skb2;
|
|
if (!skb)
|
|
if (!skb)
|
|
return NULL;
|
|
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);
|
|
skb_push(skb, 4);
|
|
tx_cmd_b = (u32)(skb->len - 4);
|
|
tx_cmd_b = (u32)(skb->len - 4);
|
|
|
|
+ if (csum)
|
|
|
|
+ tx_cmd_b |= TX_CMD_B_CSUM_ENABLE;
|
|
cpu_to_le32s(&tx_cmd_b);
|
|
cpu_to_le32s(&tx_cmd_b);
|
|
memcpy(skb->data, &tx_cmd_b, 4);
|
|
memcpy(skb->data, &tx_cmd_b, 4);
|
|
|
|
|