Hi, On Wed, Nov 24, 2021 at 09:39:12AM +0100, Horatiu Vultur wrote: > +static int lan966x_port_open(struct net_device *dev) > +{ > + struct lan966x_port *port = netdev_priv(dev); > + struct lan966x *lan966x = port->lan966x; > + int err; > + > + if (port->serdes) { > + err = phy_set_mode_ext(port->serdes, PHY_MODE_ETHERNET, > + port->config.phy_mode); > + if (err) { > + netdev_err(dev, "Could not set mode of SerDes\n"); > + return err; > + } > + } This could be done in the mac_prepare() method. > +static void lan966x_cleanup_ports(struct lan966x *lan966x) > +{ > + struct lan966x_port *port; > + int portno; > + > + for (portno = 0; portno < lan966x->num_phys_ports; portno++) { > + port = lan966x->ports[portno]; > + if (!port) > + continue; > + > + if (port->phylink) { > + rtnl_lock(); > + lan966x_port_stop(port->dev); > + rtnl_unlock(); > + phylink_destroy(port->phylink); > + port->phylink = NULL; > + } > + > + if (port->fwnode) > + fwnode_handle_put(port->fwnode); > + > + if (port->dev) > + unregister_netdev(port->dev); This doesn't look like the correct sequence to me. Shouldn't the net device be unregistered first, which will take the port down by doing so and make it unavailable to userspace to further manipulate. Then we should start tearing other stuff down such as destroying phylink and disabling interrupts (in the caller of this.) Don't you need to free the netdev as well at some point? > static int lan966x_probe_port(struct lan966x *lan966x, u32 p, > - phy_interface_t phy_mode) > + phy_interface_t phy_mode, > + struct fwnode_handle *portnp) > { ... > + port->phylink_config.dev = &port->dev->dev; > + port->phylink_config.type = PHYLINK_NETDEV; > + port->phylink_config.pcs_poll = true; > + port->phylink_pcs.poll = true; You don't need to set both of these - please omit port->phylink_config.pcs_poll. > diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h > index 7a1ff9d19fbf..ce2798db0449 100644 > --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h > +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h ... > @@ -44,15 +58,48 @@ struct lan966x { > void __iomem *regs[NUM_TARGETS]; > > int shared_queue_sz; > + > + /* interrupts */ > + int xtr_irq; > +}; > + > +struct lan966x_port_config { > + phy_interface_t portmode; > + phy_interface_t phy_mode; What is the difference between "portmode" and "phy_mode"? Does it matter if port->config.phy_mode get zeroed when lan966x_port_pcs_set() is called from lan966x_pcs_config()? It looks to me like the first call will clear phy_mode, setting it to PHY_INTERFACE_MODE_NA from that point on. > diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_port.c b/drivers/net/ethernet/microchip/lan966x/lan966x_port.c > new file mode 100644 > index 000000000000..ca1b0c8d1bf5 > --- /dev/null > +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_port.c > @@ -0,0 +1,422 @@ ... > +void lan966x_port_status_get(struct lan966x_port *port, > + struct phylink_link_state *state) > +{ > + struct lan966x *lan966x = port->lan966x; > + u16 lp_adv, ld_adv; > + bool link_down; > + u16 bmsr = 0; > + u32 val; > + > + val = lan_rd(lan966x, DEV_PCS1G_STICKY(port->chip_port)); > + link_down = DEV_PCS1G_STICKY_LINK_DOWN_STICKY_GET(val); > + if (link_down) > + lan_wr(val, lan966x, DEV_PCS1G_STICKY(port->chip_port)); > + > + /* Get both current Link and Sync status */ > + val = lan_rd(lan966x, DEV_PCS1G_LINK_STATUS(port->chip_port)); > + state->link = DEV_PCS1G_LINK_STATUS_LINK_STATUS_GET(val) && > + DEV_PCS1G_LINK_STATUS_SYNC_STATUS_GET(val); > + state->link &= !link_down; > + > + if (port->config.portmode == PHY_INTERFACE_MODE_1000BASEX) > + state->speed = SPEED_1000; > + else if (port->config.portmode == PHY_INTERFACE_MODE_2500BASEX) > + state->speed = SPEED_2500; Why not use state->interface? state->interface will be the currently configured interface mode (which should be the same as your port->config.portmode.) > + > + state->duplex = DUPLEX_FULL; Also, what is the purpose of initialising state->speed and state->duplex here? phylink_mii_c22_pcs_decode_state() will do that for you when decoding the advertisements. If it's to deal with autoneg disabled, then it ought to be conditional on autoneg being disabled and the link being up. > + > + /* Get PCS ANEG status register */ > + val = lan_rd(lan966x, DEV_PCS1G_ANEG_STATUS(port->chip_port)); > + > + /* Aneg complete provides more information */ > + if (DEV_PCS1G_ANEG_STATUS_ANEG_COMPLETE_GET(val)) { > + lp_adv = DEV_PCS1G_ANEG_STATUS_LP_ADV_GET(val); > + state->an_complete = true; > + > + bmsr |= state->link ? BMSR_LSTATUS : 0; > + bmsr |= state->an_complete; Shouldn't this be setting BMSR_ANEGCOMPLETE? > + > + if (port->config.portmode == PHY_INTERFACE_MODE_SGMII) { > + phylink_mii_c22_pcs_decode_state(state, bmsr, lp_adv); > + } else { > + val = lan_rd(lan966x, DEV_PCS1G_ANEG_CFG(port->chip_port)); > + ld_adv = DEV_PCS1G_ANEG_CFG_ADV_ABILITY_GET(val); > + phylink_mii_c22_pcs_decode_state(state, bmsr, ld_adv); > + } This looks like it can be improved: if (DEV_PCS1G_ANEG_STATUS_ANEG_COMPLETE_GET(val)) { state->an_complete = true; bmsr |= state->link ? BMSR_LSTATUS : 0; bmsr |= BMSR_ANEGCOMPLETE; if (state->interface == PHY_INTERFACE_MODE_SGMII) { lp_adv = DEV_PCS1G_ANEG_STATUS_LP_ADV_GET(val); } else { val = lan_rd(lan966x, DEV_PCS1G_ANEG_CFG(port->chip_port)); lp_adv = DEV_PCS1G_ANEG_CFG_ADV_ABILITY_GET(val); } phylink_mii_c22_pcs_decode_state(state, bmsr, lp_adv); } I'm not sure that the non-SGMII code is actually correct though. Which advertisement are you extracting by reading the DEV_PCS1G_ANEG_CFG register and extracting DEV_PCS1G_ANEG_CFG_ADV_ABILITY_GET ? From the code in lan966x_port_pcs_set(), it suggests this is our advertisement, but it's supposed to always be the link partner's advertisement being passed to phylink_mii_c22_pcs_decode_state(). > +int lan966x_port_pcs_set(struct lan966x_port *port, > + struct lan966x_port_config *config) > +{ ... > + port->config = *config; As mentioned elsewhere, "config" won't have phy_mode set, so this clears port->config.phymode to PHY_INTERFACE_MODE_NA, which I think will cause e.g. lan966x_port_link_up() not to behave as intended. Thanks. -- RMK's Patch system: https://www.armlinux.org.uk/developer/patches/ FTTP is here! 40Mbps down 10Mbps up. Decent connectivity at last!