Merge branch 'lan78xx-fixes'
Woojung Huh says: ==================== lan78xx: update and fixes lan78xx: change to use updated phy-ignore-interrupts lan78xx: Add to handle mux control per chip id lan78xx: throttle TX path at slower than SuperSpeed USB ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Коммит
a77ce1bc12
|
@ -36,7 +36,7 @@
|
||||||
#define DRIVER_AUTHOR "WOOJUNG HUH <woojung.huh@microchip.com>"
|
#define DRIVER_AUTHOR "WOOJUNG HUH <woojung.huh@microchip.com>"
|
||||||
#define DRIVER_DESC "LAN78XX USB 3.0 Gigabit Ethernet Devices"
|
#define DRIVER_DESC "LAN78XX USB 3.0 Gigabit Ethernet Devices"
|
||||||
#define DRIVER_NAME "lan78xx"
|
#define DRIVER_NAME "lan78xx"
|
||||||
#define DRIVER_VERSION "1.0.1"
|
#define DRIVER_VERSION "1.0.2"
|
||||||
|
|
||||||
#define TX_TIMEOUT_JIFFIES (5 * HZ)
|
#define TX_TIMEOUT_JIFFIES (5 * HZ)
|
||||||
#define THROTTLE_JIFFIES (HZ / 8)
|
#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 length, u8 *data)
|
||||||
{
|
{
|
||||||
u32 val;
|
u32 val;
|
||||||
|
u32 saved;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
int retval;
|
||||||
|
|
||||||
ret = lan78xx_eeprom_confirm_not_busy(dev);
|
/* depends on chip, some EEPROM pins are muxed with LED function.
|
||||||
if (ret)
|
* disable & restore LED function to access EEPROM.
|
||||||
return ret;
|
*/
|
||||||
|
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++) {
|
for (i = 0; i < length; i++) {
|
||||||
val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_READ_;
|
val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_READ_;
|
||||||
val |= (offset & E2P_CMD_EPC_ADDR_MASK_);
|
val |= (offset & E2P_CMD_EPC_ADDR_MASK_);
|
||||||
ret = lan78xx_write_reg(dev, E2P_CMD, val);
|
ret = lan78xx_write_reg(dev, E2P_CMD, val);
|
||||||
if (unlikely(ret < 0))
|
if (unlikely(ret < 0)) {
|
||||||
return -EIO;
|
retval = -EIO;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
ret = lan78xx_wait_eeprom(dev);
|
retval = lan78xx_wait_eeprom(dev);
|
||||||
if (ret < 0)
|
if (retval < 0)
|
||||||
return ret;
|
goto exit;
|
||||||
|
|
||||||
ret = lan78xx_read_reg(dev, E2P_DATA, &val);
|
ret = lan78xx_read_reg(dev, E2P_DATA, &val);
|
||||||
if (unlikely(ret < 0))
|
if (unlikely(ret < 0)) {
|
||||||
return -EIO;
|
retval = -EIO;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
data[i] = val & 0xFF;
|
data[i] = val & 0xFF;
|
||||||
offset++;
|
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,
|
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 length, u8 *data)
|
||||||
{
|
{
|
||||||
u32 val;
|
u32 val;
|
||||||
|
u32 saved;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
int retval;
|
||||||
|
|
||||||
ret = lan78xx_eeprom_confirm_not_busy(dev);
|
/* depends on chip, some EEPROM pins are muxed with LED function.
|
||||||
if (ret)
|
* disable & restore LED function to access EEPROM.
|
||||||
return ret;
|
*/
|
||||||
|
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 */
|
/* Issue write/erase enable command */
|
||||||
val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_EWEN_;
|
val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_EWEN_;
|
||||||
ret = lan78xx_write_reg(dev, E2P_CMD, val);
|
ret = lan78xx_write_reg(dev, E2P_CMD, val);
|
||||||
if (unlikely(ret < 0))
|
if (unlikely(ret < 0)) {
|
||||||
return -EIO;
|
retval = -EIO;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
ret = lan78xx_wait_eeprom(dev);
|
retval = lan78xx_wait_eeprom(dev);
|
||||||
if (ret < 0)
|
if (retval < 0)
|
||||||
return ret;
|
goto exit;
|
||||||
|
|
||||||
for (i = 0; i < length; i++) {
|
for (i = 0; i < length; i++) {
|
||||||
/* Fill data register */
|
/* Fill data register */
|
||||||
val = data[i];
|
val = data[i];
|
||||||
ret = lan78xx_write_reg(dev, E2P_DATA, val);
|
ret = lan78xx_write_reg(dev, E2P_DATA, val);
|
||||||
if (ret < 0)
|
if (ret < 0) {
|
||||||
return ret;
|
retval = -EIO;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
/* Send "write" command */
|
/* Send "write" command */
|
||||||
val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_WRITE_;
|
val = E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_WRITE_;
|
||||||
val |= (offset & E2P_CMD_EPC_ADDR_MASK_);
|
val |= (offset & E2P_CMD_EPC_ADDR_MASK_);
|
||||||
ret = lan78xx_write_reg(dev, E2P_CMD, val);
|
ret = lan78xx_write_reg(dev, E2P_CMD, val);
|
||||||
if (ret < 0)
|
if (ret < 0) {
|
||||||
return ret;
|
retval = -EIO;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
ret = lan78xx_wait_eeprom(dev);
|
retval = lan78xx_wait_eeprom(dev);
|
||||||
if (ret < 0)
|
if (retval < 0)
|
||||||
return ret;
|
goto exit;
|
||||||
|
|
||||||
offset++;
|
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,
|
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) {
|
if (!phydev->link && dev->link_on) {
|
||||||
dev->link_on = false;
|
dev->link_on = false;
|
||||||
netif_carrier_off(dev->net);
|
|
||||||
|
|
||||||
/* reset MAC */
|
/* reset MAC */
|
||||||
ret = lan78xx_read_reg(dev, MAC_CR, &buf);
|
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);
|
ret = lan78xx_write_reg(dev, MAC_CR, buf);
|
||||||
if (unlikely(ret < 0))
|
if (unlikely(ret < 0))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
|
phy_mac_interrupt(phydev, 0);
|
||||||
} else if (phydev->link && !dev->link_on) {
|
} else if (phydev->link && !dev->link_on) {
|
||||||
dev->link_on = true;
|
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);
|
ethtool_cmd_speed(&ecmd), ecmd.duplex, ladv, radv);
|
||||||
|
|
||||||
ret = lan78xx_update_flowcontrol(dev, 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;
|
return ret;
|
||||||
|
@ -1495,7 +1540,6 @@ done:
|
||||||
static int lan78xx_mdio_init(struct lan78xx_net *dev)
|
static int lan78xx_mdio_init(struct lan78xx_net *dev)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
int i;
|
|
||||||
|
|
||||||
dev->mdiobus = mdiobus_alloc();
|
dev->mdiobus = mdiobus_alloc();
|
||||||
if (!dev->mdiobus) {
|
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",
|
snprintf(dev->mdiobus->id, MII_BUS_ID_SIZE, "usb-%03d:%03d",
|
||||||
dev->udev->bus->busnum, dev->udev->devnum);
|
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_) {
|
switch (dev->devid & ID_REV_CHIP_ID_MASK_) {
|
||||||
case 0x78000000:
|
case 0x78000000:
|
||||||
case 0x78500000:
|
case 0x78500000:
|
||||||
|
@ -1558,6 +1598,16 @@ static int lan78xx_phy_init(struct lan78xx_net *dev)
|
||||||
return -EIO;
|
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,
|
ret = phy_connect_direct(dev->net, phydev,
|
||||||
lan78xx_link_status_change,
|
lan78xx_link_status_change,
|
||||||
PHY_INTERFACE_MODE_GMII);
|
PHY_INTERFACE_MODE_GMII);
|
||||||
|
@ -1580,14 +1630,6 @@ static int lan78xx_phy_init(struct lan78xx_net *dev)
|
||||||
SUPPORTED_Pause | SUPPORTED_Asym_Pause);
|
SUPPORTED_Pause | SUPPORTED_Asym_Pause);
|
||||||
genphy_config_aneg(phydev);
|
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);
|
phy_start(phydev);
|
||||||
|
|
||||||
netif_dbg(dev, ifup, dev->net, "phy initialised successfully");
|
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) {
|
if (skb2) {
|
||||||
skb_queue_tail(&dev->txq_pend, 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);
|
netif_stop_queue(net);
|
||||||
} else {
|
} else {
|
||||||
netif_dbg(dev, tx_err, dev->net,
|
netif_dbg(dev, tx_err, dev->net,
|
||||||
|
|
Загрузка…
Ссылка в новой задаче