summaryrefslogtreecommitdiff
path: root/drivers/net/usb/lan78xx.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-02-01 15:56:08 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2016-02-01 15:56:08 -0800
commit34229b277480f46c1e9a19f027f30b074512e68b (patch)
tree90d8b43ebceb850b0e7852d75283aebbd2abbc00 /drivers/net/usb/lan78xx.c
parent2c923414d3963b959f65a8a6031972402e6a34a5 (diff)
parent53729eb174c1589f9185340ffe8c10b3f39f3ef3 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
Pull networking fixes from David Miller: "This looks like a lot but it's a mixture of regression fixes as well as fixes for longer standing issues. 1) Fix on-channel cancellation in mac80211, from Johannes Berg. 2) Handle CHECKSUM_COMPLETE properly in xt_TCPMSS netfilter xtables module, from Eric Dumazet. 3) Avoid infinite loop in UDP SO_REUSEPORT logic, also from Eric Dumazet. 4) Avoid a NULL deref if we try to set SO_REUSEPORT after a socket is bound, from Craig Gallek. 5) GRO key comparisons don't take lightweight tunnels into account, from Jesse Gross. 6) Fix struct pid leak via SCM credentials in AF_UNIX, from Eric Dumazet. 7) We need to set the rtnl_link_ops of ipv6 SIT tunnels before we register them, otherwise the NEWLINK netlink message is missing the proper attributes. From Thadeu Lima de Souza Cascardo. 8) Several Spectrum chip bug fixes for mlxsw switch driver, from Ido Schimmel 9) Handle fragments properly in ipv4 easly socket demux, from Eric Dumazet. 10) Don't ignore the ifindex key specifier on ipv6 output route lookups, from Paolo Abeni" * git://git.kernel.org/pub/scm/linux/kernel/git/davem/net: (128 commits) tcp: avoid cwnd undo after receiving ECN irda: fix a potential use-after-free in ircomm_param_request net: tg3: avoid uninitialized variable warning net: nb8800: avoid uninitialized variable warning net: vxge: avoid unused function warnings net: bgmac: clarify CONFIG_BCMA dependency net: hp100: remove unnecessary #ifdefs net: davinci_cpdma: use dma_addr_t for DMA address ipv6/udp: use sticky pktinfo egress ifindex on connect() ipv6: enforce flowi6_oif usage in ip6_dst_lookup_tail() netlink: not trim skb for mmaped socket when dump vxlan: fix a out of bounds access in __vxlan_find_mac net: dsa: mv88e6xxx: fix port VLAN maps fib_trie: Fix shift by 32 in fib_table_lookup net: moxart: use correct accessors for DMA memory ipv4: ipconfig: avoid unused ic_proto_used symbol bnxt_en: Fix crash in bnxt_free_tx_skbs() during tx timeout. bnxt_en: Exclude rx_drop_pkts hw counter from the stack's rx_dropped counter. bnxt_en: Ring free response from close path should use completion ring net_sched: drr: check for NULL pointer in drr_dequeue ...
Diffstat (limited to 'drivers/net/usb/lan78xx.c')
-rw-r--r--drivers/net/usb/lan78xx.c132
1 files changed, 88 insertions, 44 deletions
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index 2ed53331bfb2..1c299b8a162d 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -36,7 +36,7 @@
#define DRIVER_AUTHOR "WOOJUNG HUH <woojung.huh@microchip.com>"
#define DRIVER_DESC "LAN78XX USB 3.0 Gigabit Ethernet Devices"
#define DRIVER_NAME "lan78xx"
-#define DRIVER_VERSION "1.0.1"
+#define DRIVER_VERSION "1.0.2"
#define TX_TIMEOUT_JIFFIES (5 * HZ)
#define THROTTLE_JIFFIES (HZ / 8)
@@ -462,32 +462,53 @@ static int lan78xx_read_raw_eeprom(struct lan78xx_net *dev, u32 offset,
u32 length, u8 *data)
{
u32 val;
+ u32 saved;
int i, ret;
+ int retval;
- ret = lan78xx_eeprom_confirm_not_busy(dev);
- if (ret)
- return ret;
+ /* depends on chip, some EEPROM pins are muxed with LED function.
+ * disable & restore LED function to access EEPROM.
+ */
+ ret = lan78xx_read_reg(dev, HW_CFG, &val);
+ saved = val;
+ if ((dev->devid & ID_REV_CHIP_ID_MASK_) == 0x78000000) {
+ val &= ~(HW_CFG_LED1_EN_ | HW_CFG_LED0_EN_);
+ ret = lan78xx_write_reg(dev, HW_CFG, val);
+ }
+
+ retval = lan78xx_eeprom_confirm_not_busy(dev);
+ if (retval)
+ return retval;
for (i = 0; i < length; i++) {
val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_READ_;
val |= (offset & E2P_CMD_EPC_ADDR_MASK_);
ret = lan78xx_write_reg(dev, E2P_CMD, val);
- if (unlikely(ret < 0))
- return -EIO;
+ if (unlikely(ret < 0)) {
+ retval = -EIO;
+ goto exit;
+ }
- ret = lan78xx_wait_eeprom(dev);
- if (ret < 0)
- return ret;
+ retval = lan78xx_wait_eeprom(dev);
+ if (retval < 0)
+ goto exit;
ret = lan78xx_read_reg(dev, E2P_DATA, &val);
- if (unlikely(ret < 0))
- return -EIO;
+ if (unlikely(ret < 0)) {
+ retval = -EIO;
+ goto exit;
+ }
data[i] = val & 0xFF;
offset++;
}
- return 0;
+ retval = 0;
+exit:
+ if ((dev->devid & ID_REV_CHIP_ID_MASK_) == 0x78000000)
+ ret = lan78xx_write_reg(dev, HW_CFG, saved);
+
+ return retval;
}
static int lan78xx_read_eeprom(struct lan78xx_net *dev, u32 offset,
@@ -509,44 +530,67 @@ static int lan78xx_write_raw_eeprom(struct lan78xx_net *dev, u32 offset,
u32 length, u8 *data)
{
u32 val;
+ u32 saved;
int i, ret;
+ int retval;
- ret = lan78xx_eeprom_confirm_not_busy(dev);
- if (ret)
- return ret;
+ /* depends on chip, some EEPROM pins are muxed with LED function.
+ * disable & restore LED function to access EEPROM.
+ */
+ ret = lan78xx_read_reg(dev, HW_CFG, &val);
+ saved = val;
+ if ((dev->devid & ID_REV_CHIP_ID_MASK_) == 0x78000000) {
+ val &= ~(HW_CFG_LED1_EN_ | HW_CFG_LED0_EN_);
+ ret = lan78xx_write_reg(dev, HW_CFG, val);
+ }
+
+ retval = lan78xx_eeprom_confirm_not_busy(dev);
+ if (retval)
+ goto exit;
/* Issue write/erase enable command */
val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_EWEN_;
ret = lan78xx_write_reg(dev, E2P_CMD, val);
- if (unlikely(ret < 0))
- return -EIO;
+ if (unlikely(ret < 0)) {
+ retval = -EIO;
+ goto exit;
+ }
- ret = lan78xx_wait_eeprom(dev);
- if (ret < 0)
- return ret;
+ retval = lan78xx_wait_eeprom(dev);
+ if (retval < 0)
+ goto exit;
for (i = 0; i < length; i++) {
/* Fill data register */
val = data[i];
ret = lan78xx_write_reg(dev, E2P_DATA, val);
- if (ret < 0)
- return ret;
+ if (ret < 0) {
+ retval = -EIO;
+ goto exit;
+ }
/* Send "write" command */
val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_WRITE_;
val |= (offset & E2P_CMD_EPC_ADDR_MASK_);
ret = lan78xx_write_reg(dev, E2P_CMD, val);
- if (ret < 0)
- return ret;
+ if (ret < 0) {
+ retval = -EIO;
+ goto exit;
+ }
- ret = lan78xx_wait_eeprom(dev);
- if (ret < 0)
- return ret;
+ retval = lan78xx_wait_eeprom(dev);
+ if (retval < 0)
+ goto exit;
offset++;
}
- return 0;
+ retval = 0;
+exit:
+ if ((dev->devid & ID_REV_CHIP_ID_MASK_) == 0x78000000)
+ ret = lan78xx_write_reg(dev, HW_CFG, saved);
+
+ return retval;
}
static int lan78xx_read_raw_otp(struct lan78xx_net *dev, u32 offset,
@@ -904,7 +948,6 @@ static int lan78xx_link_reset(struct lan78xx_net *dev)
if (!phydev->link && dev->link_on) {
dev->link_on = false;
- netif_carrier_off(dev->net);
/* reset MAC */
ret = lan78xx_read_reg(dev, MAC_CR, &buf);
@@ -914,6 +957,8 @@ static int lan78xx_link_reset(struct lan78xx_net *dev)
ret = lan78xx_write_reg(dev, MAC_CR, buf);
if (unlikely(ret < 0))
return -EIO;
+
+ phy_mac_interrupt(phydev, 0);
} else if (phydev->link && !dev->link_on) {
dev->link_on = true;
@@ -953,7 +998,7 @@ static int lan78xx_link_reset(struct lan78xx_net *dev)
ethtool_cmd_speed(&ecmd), ecmd.duplex, ladv, radv);
ret = lan78xx_update_flowcontrol(dev, ecmd.duplex, ladv, radv);
- netif_carrier_on(dev->net);
+ phy_mac_interrupt(phydev, 1);
}
return ret;
@@ -1495,7 +1540,6 @@ done:
static int lan78xx_mdio_init(struct lan78xx_net *dev)
{
int ret;
- int i;
dev->mdiobus = mdiobus_alloc();
if (!dev->mdiobus) {
@@ -1511,10 +1555,6 @@ static int lan78xx_mdio_init(struct lan78xx_net *dev)
snprintf(dev->mdiobus->id, MII_BUS_ID_SIZE, "usb-%03d:%03d",
dev->udev->bus->busnum, dev->udev->devnum);
- /* handle our own interrupt */
- for (i = 0; i < PHY_MAX_ADDR; i++)
- dev->mdiobus->irq[i] = PHY_IGNORE_INTERRUPT;
-
switch (dev->devid & ID_REV_CHIP_ID_MASK_) {
case 0x78000000:
case 0x78500000:
@@ -1558,6 +1598,16 @@ static int lan78xx_phy_init(struct lan78xx_net *dev)
return -EIO;
}
+ /* Enable PHY interrupts.
+ * We handle our own interrupt
+ */
+ ret = phy_read(phydev, LAN88XX_INT_STS);
+ ret = phy_write(phydev, LAN88XX_INT_MASK,
+ LAN88XX_INT_MASK_MDINTPIN_EN_ |
+ LAN88XX_INT_MASK_LINK_CHANGE_);
+
+ phydev->irq = PHY_IGNORE_INTERRUPT;
+
ret = phy_connect_direct(dev->net, phydev,
lan78xx_link_status_change,
PHY_INTERFACE_MODE_GMII);
@@ -1580,14 +1630,6 @@ static int lan78xx_phy_init(struct lan78xx_net *dev)
SUPPORTED_Pause | SUPPORTED_Asym_Pause);
genphy_config_aneg(phydev);
- /* Workaround to enable PHY interrupt.
- * phy_start_interrupts() is API for requesting and enabling
- * PHY interrupt. However, USB-to-Ethernet device can't use
- * request_irq() called in phy_start_interrupts().
- * Set PHY to PHY_HALTED and call phy_start()
- * to make a call to phy_enable_interrupts()
- */
- phy_stop(phydev);
phy_start(phydev);
netif_dbg(dev, ifup, dev->net, "phy initialised successfully");
@@ -2221,7 +2263,9 @@ netdev_tx_t lan78xx_start_xmit(struct sk_buff *skb, struct net_device *net)
if (skb2) {
skb_queue_tail(&dev->txq_pend, skb2);
- if (skb_queue_len(&dev->txq_pend) > 10)
+ /* throttle TX patch at slower than SUPER SPEED USB */
+ if ((dev->udev->speed < USB_SPEED_SUPER) &&
+ (skb_queue_len(&dev->txq_pend) > 10))
netif_stop_queue(net);
} else {
netif_dbg(dev, tx_err, dev->net,