Re: [PATCH net-next v3 3/6] net: lan966x: add port module support

Horatiu Vultur
2021-11-24T14:56:13Z

The 11/24/2021 10:20, Russell King (Oracle) wrote:
>
> Hi,

Hi Russel,

>
> 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.

Yes, I will move this in mac_prepare()

>
> > +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.)

I can change the order as you suggested.
Regarding the interrupts, shouldn't they be first disable and then do
all the teardown?

>
> Don't you need to free the netdev as well at some point?

It is not needed because they are allocated using devm_alloc_etherdev_mqs

>
> > 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.

I will remove it.

>
> > 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.

The purpose was to use portmode to configure the MAC and the phy_mode
to configure the serdes. There are small issues regarding this which
will be fix in the next series also I will add some comments just to
make it clear.

Actually, port->config.phy_mode will not get zeroed. Because right after
the memset it follows: 'config = port->config'.

>
> > 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.)

I will use state->interface.

>
> > +
> > + 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.

It was the case for autoneg disabled.

>
> > +
> > + /* 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?

That was a silly mistake from my side.

>
> > +
> > + 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().

Actually, like you mentioned it needs to be link partner's advertisement
so that code can be simplified more:

if (DEV_PCS1G_ANEG_STATUS_ANEG_COMPLETE_GET(val)) {
state->an_complete = true;

bmsr |= state->link ? BMSR_LSTATUS : 0;
bmsr |= BMSR_ANEGCOMPLETE;

lp_adv = DEV_PCS1G_ANEG_STATUS_LP_ADV_GET(val);
phylink_mii_c22_pcs_decode_state(state, bmsr, lp_adv);
}

Because inside phylink_mii_c22_pcs_decode_state, more precisely in
phylink_decode_c37_work, state->advertising will have the local
advertising.

>
> > +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.

Actually, the "config" will still have the phy_mode because config will
get teh value of port->config and after that some fields are changed.

>
> Thanks.
>
> --
> RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
> FTTP is here! 40Mbps down 10Mbps up. Decent connectivity at last!

--
/Horatiu