diff options
Diffstat (limited to 'drivers/net/usb/lan78xx.c')
-rw-r--r-- | drivers/net/usb/lan78xx.c | 742 |
1 files changed, 354 insertions, 388 deletions
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index f53e255116ea..1ff25f57329a 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -6,6 +6,7 @@ #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/ethtool.h> +#include <linux/phylink.h> #include <linux/usb.h> #include <linux/crc32.h> #include <linux/signal.h> @@ -25,7 +26,6 @@ #include <linux/irq.h> #include <linux/irqchip/chained_irq.h> #include <linux/microchipphy.h> -#include <linux/phy_fixed.h> #include <linux/of_mdio.h> #include <linux/of_net.h> #include "lan78xx.h" @@ -384,7 +384,7 @@ struct skb_data { /* skb->cb is one of these */ #define EVENT_RX_HALT 1 #define EVENT_RX_MEMORY 2 #define EVENT_STS_SPLIT 3 -#define EVENT_LINK_RESET 4 +#define EVENT_PHY_INT_ACK 4 #define EVENT_RX_PAUSED 5 #define EVENT_DEV_WAKING 6 #define EVENT_DEV_ASLEEP 7 @@ -413,7 +413,6 @@ struct lan78xx_net { struct net_device *net; struct usb_device *udev; struct usb_interface *intf; - void *driver_priv; unsigned int tx_pend_data_len; size_t n_tx_urbs; @@ -448,28 +447,24 @@ struct lan78xx_net { unsigned long flags; wait_queue_head_t *wait; - unsigned char suspend_count; unsigned int maxpacket; struct timer_list stat_monitor; unsigned long data[5]; - int link_on; - u8 mdix_ctrl; - u32 chipid; u32 chiprev; struct mii_bus *mdiobus; phy_interface_t interface; - int fc_autoneg; - u8 fc_request_control; - int delta; struct statstage stats; struct irq_domain_data domain_data; + + struct phylink *phylink; + struct phylink_config phylink_config; }; /* use ethtool to change the level for any given device */ @@ -1554,28 +1549,6 @@ static void lan78xx_set_multicast(struct net_device *netdev) schedule_work(&pdata->set_multicast); } -static int lan78xx_configure_flowcontrol(struct lan78xx_net *dev, - bool tx_pause, bool rx_pause); - -static int lan78xx_update_flowcontrol(struct lan78xx_net *dev, u8 duplex, - u16 lcladv, u16 rmtadv) -{ - u8 cap; - - if (dev->fc_autoneg) - cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv); - else - cap = dev->fc_request_control; - - netif_dbg(dev, link, dev->net, "rx pause %s, tx pause %s", - (cap & FLOW_CTRL_RX ? "enabled" : "disabled"), - (cap & FLOW_CTRL_TX ? "enabled" : "disabled")); - - return lan78xx_configure_flowcontrol(dev, - cap & FLOW_CTRL_TX, - cap & FLOW_CTRL_RX); -} - static void lan78xx_rx_urb_submit_all(struct lan78xx_net *dev); static int lan78xx_mac_reset(struct lan78xx_net *dev) @@ -1638,75 +1611,6 @@ static int lan78xx_phy_int_ack(struct lan78xx_net *dev) return lan78xx_write_reg(dev, INT_STS, INT_STS_PHY_INT_); } -static int lan78xx_configure_usb(struct lan78xx_net *dev, int speed); - -static int lan78xx_link_reset(struct lan78xx_net *dev) -{ - struct phy_device *phydev = dev->net->phydev; - struct ethtool_link_ksettings ecmd; - int ladv, radv, ret, link; - - /* clear LAN78xx interrupt status */ - ret = lan78xx_phy_int_ack(dev); - if (unlikely(ret < 0)) - return ret; - - mutex_lock(&phydev->lock); - phy_read_status(phydev); - link = phydev->link; - mutex_unlock(&phydev->lock); - - if (!link && dev->link_on) { - dev->link_on = false; - - /* reset MAC */ - ret = lan78xx_mac_reset(dev); - if (ret < 0) - return ret; - - timer_delete(&dev->stat_monitor); - } else if (link && !dev->link_on) { - dev->link_on = true; - - phy_ethtool_ksettings_get(phydev, &ecmd); - - ret = lan78xx_configure_usb(dev, ecmd.base.speed); - if (ret < 0) - return ret; - - ladv = phy_read(phydev, MII_ADVERTISE); - if (ladv < 0) - return ladv; - - radv = phy_read(phydev, MII_LPA); - if (radv < 0) - return radv; - - netif_dbg(dev, link, dev->net, - "speed: %u duplex: %d anadv: 0x%04x anlpa: 0x%04x", - ecmd.base.speed, ecmd.base.duplex, ladv, radv); - - ret = lan78xx_update_flowcontrol(dev, ecmd.base.duplex, ladv, - radv); - if (ret < 0) - return ret; - - if (!timer_pending(&dev->stat_monitor)) { - dev->delta = 1; - mod_timer(&dev->stat_monitor, - jiffies + STAT_UPDATE_TIMER); - } - - lan78xx_rx_urb_submit_all(dev); - - local_bh_disable(); - napi_schedule(&dev->napi); - local_bh_enable(); - } - - return 0; -} - /* some work can't be done in tasklets, so we use keventd * * NOTE: annoying asymmetry: if it's active, schedule_work() fails, @@ -1733,7 +1637,7 @@ static void lan78xx_status(struct lan78xx_net *dev, struct urb *urb) if (intdata & INT_ENP_PHY_INT) { netif_dbg(dev, link, dev->net, "PHY INTR: 0x%08x\n", intdata); - lan78xx_defer_kevent(dev, EVENT_LINK_RESET); + lan78xx_defer_kevent(dev, EVENT_PHY_INT_ACK); if (dev->domain_data.phyirq > 0) generic_handle_irq_safe(dev->domain_data.phyirq); @@ -1880,66 +1784,15 @@ exit_pm_put: static int lan78xx_get_eee(struct net_device *net, struct ethtool_keee *edata) { struct lan78xx_net *dev = netdev_priv(net); - struct phy_device *phydev = net->phydev; - int ret; - u32 buf; - - ret = usb_autopm_get_interface(dev->intf); - if (ret < 0) - return ret; - ret = phy_ethtool_get_eee(phydev, edata); - if (ret < 0) - goto exit; - - ret = lan78xx_read_reg(dev, MAC_CR, &buf); - if (buf & MAC_CR_EEE_EN_) { - /* EEE_TX_LPI_REQ_DLY & tx_lpi_timer are same uSec unit */ - ret = lan78xx_read_reg(dev, EEE_TX_LPI_REQ_DLY, &buf); - edata->tx_lpi_timer = buf; - } else { - edata->tx_lpi_timer = 0; - } - - ret = 0; -exit: - usb_autopm_put_interface(dev->intf); - - return ret; + return phylink_ethtool_get_eee(dev->phylink, edata); } static int lan78xx_set_eee(struct net_device *net, struct ethtool_keee *edata) { struct lan78xx_net *dev = netdev_priv(net); - int ret; - u32 buf; - - ret = usb_autopm_get_interface(dev->intf); - if (ret < 0) - return ret; - - ret = phy_ethtool_set_eee(net->phydev, edata); - if (ret < 0) - goto out; - - buf = (u32)edata->tx_lpi_timer; - ret = lan78xx_write_reg(dev, EEE_TX_LPI_REQ_DLY, buf); -out: - usb_autopm_put_interface(dev->intf); - - return ret; -} -static u32 lan78xx_get_link(struct net_device *net) -{ - u32 link; - - mutex_lock(&net->phydev->lock); - phy_read_status(net->phydev); - link = net->phydev->link; - mutex_unlock(&net->phydev->lock); - - return link; + return phylink_ethtool_set_eee(dev->phylink, edata); } static void lan78xx_get_drvinfo(struct net_device *net, @@ -1969,109 +1822,32 @@ static int lan78xx_get_link_ksettings(struct net_device *net, struct ethtool_link_ksettings *cmd) { struct lan78xx_net *dev = netdev_priv(net); - struct phy_device *phydev = net->phydev; - int ret; - - ret = usb_autopm_get_interface(dev->intf); - if (ret < 0) - return ret; - - phy_ethtool_ksettings_get(phydev, cmd); - - usb_autopm_put_interface(dev->intf); - return ret; + return phylink_ethtool_ksettings_get(dev->phylink, cmd); } static int lan78xx_set_link_ksettings(struct net_device *net, const struct ethtool_link_ksettings *cmd) { struct lan78xx_net *dev = netdev_priv(net); - struct phy_device *phydev = net->phydev; - int ret = 0; - int temp; - ret = usb_autopm_get_interface(dev->intf); - if (ret < 0) - return ret; - - /* change speed & duplex */ - ret = phy_ethtool_ksettings_set(phydev, cmd); - - if (!cmd->base.autoneg) { - /* force link down */ - temp = phy_read(phydev, MII_BMCR); - phy_write(phydev, MII_BMCR, temp | BMCR_LOOPBACK); - mdelay(1); - phy_write(phydev, MII_BMCR, temp); - } - - usb_autopm_put_interface(dev->intf); - - return ret; + return phylink_ethtool_ksettings_set(dev->phylink, cmd); } static void lan78xx_get_pause(struct net_device *net, struct ethtool_pauseparam *pause) { struct lan78xx_net *dev = netdev_priv(net); - struct phy_device *phydev = net->phydev; - struct ethtool_link_ksettings ecmd; - - phy_ethtool_ksettings_get(phydev, &ecmd); - pause->autoneg = dev->fc_autoneg; - - if (dev->fc_request_control & FLOW_CTRL_TX) - pause->tx_pause = 1; - - if (dev->fc_request_control & FLOW_CTRL_RX) - pause->rx_pause = 1; + phylink_ethtool_get_pauseparam(dev->phylink, pause); } static int lan78xx_set_pause(struct net_device *net, struct ethtool_pauseparam *pause) { struct lan78xx_net *dev = netdev_priv(net); - struct phy_device *phydev = net->phydev; - struct ethtool_link_ksettings ecmd; - int ret; - - phy_ethtool_ksettings_get(phydev, &ecmd); - - if (pause->autoneg && !ecmd.base.autoneg) { - ret = -EINVAL; - goto exit; - } - - dev->fc_request_control = 0; - if (pause->rx_pause) - dev->fc_request_control |= FLOW_CTRL_RX; - - if (pause->tx_pause) - dev->fc_request_control |= FLOW_CTRL_TX; - - if (ecmd.base.autoneg) { - __ETHTOOL_DECLARE_LINK_MODE_MASK(fc) = { 0, }; - u32 mii_adv; - - linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT, - ecmd.link_modes.advertising); - linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, - ecmd.link_modes.advertising); - mii_adv = (u32)mii_advertise_flowctrl(dev->fc_request_control); - mii_adv_to_linkmode_adv_t(fc, mii_adv); - linkmode_or(ecmd.link_modes.advertising, fc, - ecmd.link_modes.advertising); - phy_ethtool_ksettings_set(phydev, &ecmd); - } - - dev->fc_autoneg = pause->autoneg; - - ret = 0; -exit: - return ret; + return phylink_ethtool_set_pauseparam(dev->phylink, pause); } static int lan78xx_get_regs_len(struct net_device *netdev) @@ -2108,7 +1884,7 @@ clean_data: } static const struct ethtool_ops lan78xx_ethtool_ops = { - .get_link = lan78xx_get_link, + .get_link = ethtool_op_get_link, .nway_reset = phy_ethtool_nway_reset, .get_drvinfo = lan78xx_get_drvinfo, .get_msglevel = lan78xx_get_msglevel, @@ -2332,26 +2108,6 @@ static void lan78xx_remove_mdio(struct lan78xx_net *dev) mdiobus_free(dev->mdiobus); } -static void lan78xx_link_status_change(struct net_device *net) -{ - struct lan78xx_net *dev = netdev_priv(net); - struct phy_device *phydev = net->phydev; - u32 data; - int ret; - - ret = lan78xx_read_reg(dev, MAC_CR, &data); - if (ret < 0) - return; - - if (phydev->enable_tx_lpi) - data |= MAC_CR_EEE_EN_; - else - data &= ~MAC_CR_EEE_EN_; - lan78xx_write_reg(dev, MAC_CR, data); - - phy_print_status(phydev); -} - static int irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwirq) { @@ -2448,10 +2204,8 @@ static int lan78xx_setup_irq_domain(struct lan78xx_net *dev) dev->domain_data.irqchip = &lan78xx_irqchip; dev->domain_data.irq_handler = handle_simple_irq; - irqdomain = irq_domain_create_simple(of_fwnode_handle(dev->udev->dev.parent->of_node), - MAX_INT_EP, 0, - &chip_domain_ops, - &dev->domain_data); + irqdomain = irq_domain_create_simple(dev_fwnode(dev->udev->dev.parent), MAX_INT_EP, 0, + &chip_domain_ops, &dev->domain_data); if (irqdomain) { /* create mapping for PHY interrupt */ irqmap = irq_create_mapping(irqdomain, INT_EP_PHY); @@ -2483,6 +2237,77 @@ static void lan78xx_remove_irq_domain(struct lan78xx_net *dev) dev->domain_data.irqdomain = NULL; } +static void lan78xx_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) +{ + struct net_device *net = to_net_dev(config->dev); + struct lan78xx_net *dev = netdev_priv(net); + u32 mac_cr = 0; + int ret; + + /* Check if the mode is supported */ + if (mode != MLO_AN_FIXED && mode != MLO_AN_PHY) { + netdev_err(net, "Unsupported negotiation mode: %u\n", mode); + return; + } + + switch (state->interface) { + case PHY_INTERFACE_MODE_GMII: + mac_cr |= MAC_CR_GMII_EN_; + break; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_TXID: + case PHY_INTERFACE_MODE_RGMII_RXID: + break; + default: + netdev_warn(net, "Unsupported interface mode: %d\n", + state->interface); + return; + } + + ret = lan78xx_update_reg(dev, MAC_CR, MAC_CR_GMII_EN_, mac_cr); + if (ret < 0) + netdev_err(net, "Failed to config MAC with error %pe\n", + ERR_PTR(ret)); +} + +static void lan78xx_mac_link_down(struct phylink_config *config, + unsigned int mode, phy_interface_t interface) +{ + struct net_device *net = to_net_dev(config->dev); + struct lan78xx_net *dev = netdev_priv(net); + int ret; + + netif_stop_queue(net); + + /* MAC reset will not de-assert TXEN/RXEN, we need to stop them + * manually before reset. TX and RX should be disabled before running + * link_up sequence. + */ + ret = lan78xx_stop_tx_path(dev); + if (ret < 0) + goto link_down_fail; + + ret = lan78xx_stop_rx_path(dev); + if (ret < 0) + goto link_down_fail; + + /* MAC reset seems to not affect MAC configuration, no idea if it is + * really needed, but it was done in previous driver version. So, leave + * it here. + */ + ret = lan78xx_mac_reset(dev); + if (ret < 0) + goto link_down_fail; + + return; + +link_down_fail: + netdev_err(dev->net, "Failed to set MAC down with error %pe\n", + ERR_PTR(ret)); +} + /** * lan78xx_configure_usb - Configure USB link power settings * @dev: pointer to the LAN78xx device structure @@ -2618,28 +2443,155 @@ static int lan78xx_configure_flowcontrol(struct lan78xx_net *dev, return lan78xx_write_reg(dev, FLOW, flow); } +static void lan78xx_mac_link_up(struct phylink_config *config, + struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + struct net_device *net = to_net_dev(config->dev); + struct lan78xx_net *dev = netdev_priv(net); + u32 mac_cr = 0; + int ret; + + switch (speed) { + case SPEED_1000: + mac_cr |= MAC_CR_SPEED_1000_; + break; + case SPEED_100: + mac_cr |= MAC_CR_SPEED_100_; + break; + case SPEED_10: + mac_cr |= MAC_CR_SPEED_10_; + break; + default: + netdev_err(dev->net, "Unsupported speed %d\n", speed); + return; + } + + if (duplex == DUPLEX_FULL) + mac_cr |= MAC_CR_FULL_DUPLEX_; + + /* make sure TXEN and RXEN are disabled before reconfiguring MAC */ + ret = lan78xx_update_reg(dev, MAC_CR, MAC_CR_SPEED_MASK_ | + MAC_CR_FULL_DUPLEX_ | MAC_CR_EEE_EN_, mac_cr); + if (ret < 0) + goto link_up_fail; + + ret = lan78xx_configure_flowcontrol(dev, tx_pause, rx_pause); + if (ret < 0) + goto link_up_fail; + + ret = lan78xx_configure_usb(dev, speed); + if (ret < 0) + goto link_up_fail; + + lan78xx_rx_urb_submit_all(dev); + + ret = lan78xx_flush_rx_fifo(dev); + if (ret < 0) + goto link_up_fail; + + ret = lan78xx_flush_tx_fifo(dev); + if (ret < 0) + goto link_up_fail; + + ret = lan78xx_start_tx_path(dev); + if (ret < 0) + goto link_up_fail; + + ret = lan78xx_start_rx_path(dev); + if (ret < 0) + goto link_up_fail; + + netif_start_queue(net); + + return; + +link_up_fail: + netdev_err(dev->net, "Failed to set MAC up with error %pe\n", + ERR_PTR(ret)); +} + /** - * lan78xx_register_fixed_phy() - Register a fallback fixed PHY + * lan78xx_mac_eee_enable - Enable or disable MAC-side EEE support * @dev: LAN78xx device + * @enable: true to enable EEE, false to disable * - * Registers a fixed PHY with 1 Gbps full duplex. This is used in special cases - * like EVB-KSZ9897-1, where LAN7801 acts as a USB-to-Ethernet interface to a - * switch without a visible PHY. + * This function sets or clears the MAC_CR_EEE_EN_ bit to control Energy + * Efficient Ethernet (EEE) operation. According to current understanding + * of the LAN7800 documentation, this bit can be modified while TX and RX + * are enabled. No explicit requirement was found to disable data paths + * before changing this bit. + * + * Return: 0 on success or a negative error code + */ +static int lan78xx_mac_eee_enable(struct lan78xx_net *dev, bool enable) +{ + u32 mac_cr = 0; + + if (enable) + mac_cr |= MAC_CR_EEE_EN_; + + return lan78xx_update_reg(dev, MAC_CR, MAC_CR_EEE_EN_, mac_cr); +} + +static void lan78xx_mac_disable_tx_lpi(struct phylink_config *config) +{ + struct net_device *net = to_net_dev(config->dev); + struct lan78xx_net *dev = netdev_priv(net); + + lan78xx_mac_eee_enable(dev, false); +} + +static int lan78xx_mac_enable_tx_lpi(struct phylink_config *config, u32 timer, + bool tx_clk_stop) +{ + struct net_device *net = to_net_dev(config->dev); + struct lan78xx_net *dev = netdev_priv(net); + int ret; + + /* Software should only change this field when Energy Efficient + * Ethernet Enable (EEEEN) is cleared. We ensure that by clearing + * EEEEN during probe, and phylink itself guarantees that + * mac_disable_tx_lpi() will have been previously called. + */ + ret = lan78xx_write_reg(dev, EEE_TX_LPI_REQ_DLY, timer); + if (ret < 0) + return ret; + + return lan78xx_mac_eee_enable(dev, true); +} + +static const struct phylink_mac_ops lan78xx_phylink_mac_ops = { + .mac_config = lan78xx_mac_config, + .mac_link_down = lan78xx_mac_link_down, + .mac_link_up = lan78xx_mac_link_up, + .mac_disable_tx_lpi = lan78xx_mac_disable_tx_lpi, + .mac_enable_tx_lpi = lan78xx_mac_enable_tx_lpi, +}; + +/** + * lan78xx_set_fixed_link() - Set fixed link configuration for LAN7801 + * @dev: LAN78xx device + * + * Use fixed link configuration with 1 Gbps full duplex. This is used in special + * cases like EVB-KSZ9897-1, where LAN7801 acts as a USB-to-Ethernet interface + * to a switch without a visible PHY. * * Return: pointer to the registered fixed PHY, or ERR_PTR() on error. */ -static struct phy_device *lan78xx_register_fixed_phy(struct lan78xx_net *dev) +static int lan78xx_set_fixed_link(struct lan78xx_net *dev) { - struct fixed_phy_status fphy_status = { - .link = 1, + static const struct phylink_link_state state = { .speed = SPEED_1000, .duplex = DUPLEX_FULL, }; netdev_info(dev->net, - "No PHY found on LAN7801 – registering fixed PHY (e.g. EVB-KSZ9897-1)\n"); + "No PHY found on LAN7801 – using fixed link instead (e.g. EVB-KSZ9897-1)\n"); - return fixed_phy_register(&fphy_status, NULL); + return phylink_set_fixed_link(dev->phylink, &state); } /** @@ -2675,7 +2627,7 @@ static struct phy_device *lan78xx_get_phy(struct lan78xx_net *dev) dev->interface = PHY_INTERFACE_MODE_RGMII; /* No PHY found – fallback to fixed PHY (e.g. KSZ switch board) */ - return lan78xx_register_fixed_phy(dev); + return NULL; case ID_REV_CHIP_ID_7800_: case ID_REV_CHIP_ID_7850_: @@ -2802,20 +2754,101 @@ static int lan78xx_configure_leds_from_dt(struct lan78xx_net *dev, return lan78xx_write_reg(dev, HW_CFG, reg); } +static int lan78xx_phylink_setup(struct lan78xx_net *dev) +{ + struct phylink_config *pc = &dev->phylink_config; + struct phylink *phylink; + + pc->dev = &dev->net->dev; + pc->type = PHYLINK_NETDEV; + pc->mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE | MAC_10 | + MAC_100 | MAC_1000FD; + pc->mac_managed_pm = true; + pc->lpi_capabilities = MAC_100FD | MAC_1000FD; + /* + * Default TX LPI (Low Power Idle) request delay count is set to 50us. + * + * Source: LAN7800 Documentation, DS00001992H, Section 15.1.57, Page 204. + * + * Reasoning: + * According to the application note in the LAN7800 documentation, a + * zero delay may negatively impact the TX data path’s ability to + * support Gigabit operation. A value of 50us is recommended as a + * reasonable default when the part operates at Gigabit speeds, + * balancing stability and power efficiency in EEE mode. This delay can + * be increased based on performance testing, as EEE is designed for + * scenarios with mostly idle links and occasional bursts of full + * bandwidth transmission. The goal is to ensure reliable Gigabit + * performance without overly aggressive power optimization during + * inactive periods. + */ + pc->lpi_timer_default = 50; + pc->eee_enabled_default = true; + + if (dev->chipid == ID_REV_CHIP_ID_7801_) + phy_interface_set_rgmii(pc->supported_interfaces); + else + __set_bit(PHY_INTERFACE_MODE_GMII, pc->supported_interfaces); + + memcpy(dev->phylink_config.lpi_interfaces, + dev->phylink_config.supported_interfaces, + sizeof(dev->phylink_config.lpi_interfaces)); + + phylink = phylink_create(pc, dev->net->dev.fwnode, + dev->interface, &lan78xx_phylink_mac_ops); + if (IS_ERR(phylink)) + return PTR_ERR(phylink); + + dev->phylink = phylink; + + return 0; +} + +static void lan78xx_phy_uninit(struct lan78xx_net *dev) +{ + if (dev->phylink) { + phylink_disconnect_phy(dev->phylink); + phylink_destroy(dev->phylink); + dev->phylink = NULL; + } +} + static int lan78xx_phy_init(struct lan78xx_net *dev) { - __ETHTOOL_DECLARE_LINK_MODE_MASK(fc) = { 0, }; - int ret; - u32 mii_adv; struct phy_device *phydev; + int ret; phydev = lan78xx_get_phy(dev); + /* phydev can be NULL if no PHY is found and the chip is LAN7801, + * which will use a fixed link later. + * If an error occurs, return the error code immediately. + */ if (IS_ERR(phydev)) return PTR_ERR(phydev); + ret = lan78xx_phylink_setup(dev); + if (ret < 0) + return ret; + ret = lan78xx_mac_prepare_for_phy(dev); if (ret < 0) - goto free_phy; + goto phylink_uninit; + + /* If no PHY is found, set up a fixed link. It is very specific to + * the LAN7801 and is used in special cases like EVB-KSZ9897-1 where + * LAN7801 acts as a USB-to-Ethernet interface to a switch without + * a visible PHY. + */ + if (!phydev) { + ret = lan78xx_set_fixed_link(dev); + if (ret < 0) + goto phylink_uninit; + + /* No PHY found, so set up a fixed link and return early. + * No need to configure PHY IRQ or attach to phylink. + */ + return 0; + } /* if phyirq is not set, use polling mode in phylib */ if (dev->domain_data.phyirq > 0) @@ -2824,54 +2857,21 @@ static int lan78xx_phy_init(struct lan78xx_net *dev) phydev->irq = PHY_POLL; netdev_dbg(dev->net, "phydev->irq = %d\n", phydev->irq); - /* set to AUTOMDIX */ - phydev->mdix = ETH_TP_MDI_AUTO; - - ret = phy_connect_direct(dev->net, phydev, - lan78xx_link_status_change, - dev->interface); + ret = phylink_connect_phy(dev->phylink, phydev); if (ret) { - netdev_err(dev->net, "can't attach PHY to %s\n", - dev->mdiobus->id); - if (dev->chipid == ID_REV_CHIP_ID_7801_) { - if (phy_is_pseudo_fixed_link(phydev)) { - fixed_phy_unregister(phydev); - phy_device_free(phydev); - } - } - return -EIO; + netdev_err(dev->net, "can't attach PHY to %s, error %pe\n", + dev->mdiobus->id, ERR_PTR(ret)); + goto phylink_uninit; } - /* MAC doesn't support 1000T Half */ - phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_1000baseT_Half_BIT); - - /* support both flow controls */ - dev->fc_request_control = (FLOW_CTRL_RX | FLOW_CTRL_TX); - linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT, - phydev->advertising); - linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, - phydev->advertising); - mii_adv = (u32)mii_advertise_flowctrl(dev->fc_request_control); - mii_adv_to_linkmode_adv_t(fc, mii_adv); - linkmode_or(phydev->advertising, fc, phydev->advertising); - - phy_support_eee(phydev); - ret = lan78xx_configure_leds_from_dt(dev, phydev); - if (ret) - goto free_phy; - - genphy_config_aneg(phydev); - - dev->fc_autoneg = phydev->autoneg; + if (ret < 0) + goto phylink_uninit; return 0; -free_phy: - if (phy_is_pseudo_fixed_link(phydev)) { - fixed_phy_unregister(phydev); - phy_device_free(phydev); - } +phylink_uninit: + lan78xx_phy_uninit(dev); return ret; } @@ -3212,7 +3212,6 @@ static int lan78xx_reset(struct lan78xx_net *dev) unsigned long timeout; int ret; u32 buf; - u8 sig; ret = lan78xx_read_reg(dev, HW_CFG, &buf); if (ret < 0) @@ -3369,22 +3368,12 @@ static int lan78xx_reset(struct lan78xx_net *dev) if (ret < 0) return ret; + buf &= ~(MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_ | MAC_CR_EEE_EN_); + /* LAN7801 only has RGMII mode */ - if (dev->chipid == ID_REV_CHIP_ID_7801_) { + if (dev->chipid == ID_REV_CHIP_ID_7801_) buf &= ~MAC_CR_GMII_EN_; - /* Enable Auto Duplex and Auto speed */ - buf |= MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_; - } - if (dev->chipid == ID_REV_CHIP_ID_7800_ || - dev->chipid == ID_REV_CHIP_ID_7850_) { - ret = lan78xx_read_raw_eeprom(dev, 0, 1, &sig); - if (!ret && sig != EEPROM_INDICATOR) { - /* Implies there is no external eeprom. Set mac speed */ - netdev_info(dev->net, "No External EEPROM. Setting MAC Speed\n"); - buf |= MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_; - } - } ret = lan78xx_write_reg(dev, MAC_CR, buf); if (ret < 0) return ret; @@ -3434,9 +3423,11 @@ static int lan78xx_open(struct net_device *net) mutex_lock(&dev->dev_mutex); - phy_start(net->phydev); + lan78xx_init_stats(dev); - netif_dbg(dev, ifup, dev->net, "phy initialised successfully"); + napi_enable(&dev->napi); + + set_bit(EVENT_DEV_OPEN, &dev->flags); /* for Link Check */ if (dev->urb_intr) { @@ -3448,31 +3439,8 @@ static int lan78xx_open(struct net_device *net) } } - ret = lan78xx_flush_rx_fifo(dev); - if (ret < 0) - goto done; - ret = lan78xx_flush_tx_fifo(dev); - if (ret < 0) - goto done; + phylink_start(dev->phylink); - ret = lan78xx_start_tx_path(dev); - if (ret < 0) - goto done; - ret = lan78xx_start_rx_path(dev); - if (ret < 0) - goto done; - - lan78xx_init_stats(dev); - - set_bit(EVENT_DEV_OPEN, &dev->flags); - - netif_start_queue(net); - - dev->link_on = false; - - napi_enable(&dev->napi); - - lan78xx_defer_kevent(dev, EVENT_LINK_RESET); done: mutex_unlock(&dev->dev_mutex); @@ -3530,7 +3498,6 @@ static int lan78xx_stop(struct net_device *net) timer_delete_sync(&dev->stat_monitor); clear_bit(EVENT_DEV_OPEN, &dev->flags); - netif_stop_queue(net); napi_disable(&dev->napi); lan78xx_terminate_urbs(dev); @@ -3540,12 +3507,7 @@ static int lan78xx_stop(struct net_device *net) net->stats.rx_packets, net->stats.tx_packets, net->stats.rx_errors, net->stats.tx_errors); - /* ignore errors that occur stopping the Tx and Rx data paths */ - lan78xx_stop_tx_path(dev); - lan78xx_stop_rx_path(dev); - - if (net->phydev) - phy_stop(net->phydev); + phylink_stop(dev->phylink); usb_kill_urb(dev->urb_intr); @@ -3555,7 +3517,7 @@ static int lan78xx_stop(struct net_device *net) */ clear_bit(EVENT_TX_HALT, &dev->flags); clear_bit(EVENT_RX_HALT, &dev->flags); - clear_bit(EVENT_LINK_RESET, &dev->flags); + clear_bit(EVENT_PHY_INT_ACK, &dev->flags); clear_bit(EVENT_STAT_UPDATE, &dev->flags); cancel_delayed_work_sync(&dev->wq); @@ -4479,14 +4441,14 @@ static void lan78xx_delayedwork(struct work_struct *work) } } - if (test_bit(EVENT_LINK_RESET, &dev->flags)) { + if (test_bit(EVENT_PHY_INT_ACK, &dev->flags)) { int ret = 0; - clear_bit(EVENT_LINK_RESET, &dev->flags); - if (lan78xx_link_reset(dev) < 0) { - netdev_info(dev->net, "link reset failed (%d)\n", - ret); - } + clear_bit(EVENT_PHY_INT_ACK, &dev->flags); + ret = lan78xx_phy_int_ack(dev); + if (ret) + netdev_info(dev->net, "PHY INT ack failed (%pe)\n", + ERR_PTR(ret)); } if (test_bit(EVENT_STAT_UPDATE, &dev->flags)) { @@ -4560,32 +4522,29 @@ static void lan78xx_disconnect(struct usb_interface *intf) struct lan78xx_net *dev; struct usb_device *udev; struct net_device *net; - struct phy_device *phydev; dev = usb_get_intfdata(intf); usb_set_intfdata(intf, NULL); if (!dev) return; - netif_napi_del(&dev->napi); - udev = interface_to_usbdev(intf); net = dev->net; + rtnl_lock(); + phylink_stop(dev->phylink); + phylink_disconnect_phy(dev->phylink); + rtnl_unlock(); + + netif_napi_del(&dev->napi); + unregister_netdev(net); timer_shutdown_sync(&dev->stat_monitor); set_bit(EVENT_DEV_DISCONNECT, &dev->flags); cancel_delayed_work_sync(&dev->wq); - phydev = net->phydev; - - phy_disconnect(net->phydev); - - if (phy_is_pseudo_fixed_link(phydev)) { - fixed_phy_unregister(phydev); - phy_device_free(phydev); - } + phylink_destroy(dev->phylink); usb_scuttle_anchored_urbs(&dev->deferred); @@ -4669,7 +4628,6 @@ static int lan78xx_probe(struct usb_interface *intf, goto out1; } - /* netdev_printk() needs this */ SET_NETDEV_DEV(netdev, &intf->dev); dev = netdev_priv(netdev); @@ -4788,7 +4746,7 @@ static int lan78xx_probe(struct usb_interface *intf, ret = register_netdev(netdev); if (ret != 0) { netif_err(dev, probe, netdev, "couldn't register the device\n"); - goto out8; + goto phy_uninit; } usb_set_intfdata(intf, dev); @@ -4803,8 +4761,8 @@ static int lan78xx_probe(struct usb_interface *intf, return 0; -out8: - phy_disconnect(netdev->phydev); +phy_uninit: + lan78xx_phy_uninit(dev); free_urbs: usb_free_urb(dev->urb_intr); out5: @@ -5139,6 +5097,10 @@ static int lan78xx_suspend(struct usb_interface *intf, pm_message_t message) spin_unlock_irq(&dev->txq.lock); } + rtnl_lock(); + phylink_suspend(dev->phylink, false); + rtnl_unlock(); + /* stop RX */ ret = lan78xx_stop_rx_path(dev); if (ret < 0) @@ -5366,11 +5328,15 @@ static int lan78xx_reset_resume(struct usb_interface *intf) if (ret < 0) return ret; - phy_start(dev->net->phydev); - ret = lan78xx_resume(intf); + if (ret < 0) + return ret; - return ret; + rtnl_lock(); + phylink_resume(dev->phylink); + rtnl_unlock(); + + return 0; } static const struct usb_device_id products[] = { |