public inbox for netdev@vger.kernel.org
 help / color / mirror / Atom feed
From: Jens Emil Schulz Ostergaard <jensemil.schulzostergaard@microchip.com>
To: "Russell King (Oracle)" <linux@armlinux.org.uk>
Cc: <UNGLinuxDriver@microchip.com>, Andrew Lunn <andrew@lunn.ch>,
	"Vladimir Oltean" <olteanv@gmail.com>,
	"David S. Miller" <davem@davemloft.net>,
	"Eric Dumazet" <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>,
	"Paolo Abeni" <pabeni@redhat.com>,
	Simon Horman <horms@kernel.org>, Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>,
	Woojung Huh <woojung.huh@microchip.com>,
	"Steen Hegelund" <Steen.Hegelund@microchip.com>,
	Daniel Machon <daniel.machon@microchip.com>,
	<linux-kernel@vger.kernel.org>, <netdev@vger.kernel.org>,
	<devicetree@vger.kernel.org>
Subject: Re: [PATCH net-next 4/8] net: dsa: lan9645x: add basic dsa driver for LAN9645X
Date: Thu, 5 Mar 2026 15:24:43 +0100	[thread overview]
Message-ID: <32b7267f84b60a4632eb643fd0b999d03c630a1d.camel@microchip.com> (raw)
In-Reply-To: <aahWu2-3YT92vMuC@shell.armlinux.org.uk>

On Wed, 2026-03-04 at 15:58 +0000, Russell King (Oracle) wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> 
> On Tue, Mar 03, 2026 at 01:22:30PM +0100, Jens Emil Schulz Østergaard wrote:
> > Add the LAN9645X basic DSA driver with initialization, parent regmap
> > requests, port module initialization for NPI, CPU ports and front ports,
> > and phylink integration for MAC side configuration.
> 
> I'm reviewing the phylink implementation only.
> 
> > +static int lan9645x_rgmii_setup(struct lan9645x *lan9645x, int port, int speed,
> > +                             phy_interface_t mode)
> > +{
> > +     bool tx_delay = false, rx_delay = false;
> > +     u32 rx_idx, tx_idx;
> > +     u8 tx_clk;
> > +     int idx;
> > +
> > +     /* Port 4 or 7 is RGMII_0 and port 8 is RGMII_1 */
> > +     idx = port == 8 ? 1 : 0;
> > +
> > +     if (!phy_interface_mode_is_rgmii(mode))
> > +             return 0;
> > +
> > +     tx_clk = speed == SPEED_1000 ? 1 :
> > +              speed == SPEED_100  ? 2 :
> > +              speed == SPEED_10   ? 3 : 0;
> > +
> > +     lan_rmw(HSIO_RGMII_CFG_RGMII_RX_RST_SET(0) |
> > +             HSIO_RGMII_CFG_RGMII_TX_RST_SET(0) |
> > +             HSIO_RGMII_CFG_TX_CLK_CFG_SET(tx_clk),
> 
> What exactly is this clock configuration doing?
> 

This configures the outgoing txc clock. The encoding here is:

0: Disable TX clock generation
1: 125MHz for 1000Mbps operation
2:  25MHz for  100Mbps operation
3: 2.5MHz for   10Mbps operation

We take the RX and TX clock out of reset and configure this tx clock based on speed.


> > +             HSIO_RGMII_CFG_RGMII_RX_RST |
> > +             HSIO_RGMII_CFG_RGMII_TX_RST |
> > +             HSIO_RGMII_CFG_TX_CLK_CFG,
> > +             lan9645x, HSIO_RGMII_CFG(idx));
> > +
> > +     /* We configure delays on the MAC side. When the PHY is not responsible
> > +      * for delays, the MAC is, which is why RGMII_TXID results in
> > +      * rx_delay=true.
> > +      */
> > +     if (mode == PHY_INTERFACE_MODE_RGMII ||
> > +         mode == PHY_INTERFACE_MODE_RGMII_TXID)
> > +             rx_delay = true;
> > +
> > +     if (mode == PHY_INTERFACE_MODE_RGMII ||
> > +         mode == PHY_INTERFACE_MODE_RGMII_RXID)
> > +             tx_delay = true;
> 
> PHY_INTERFACE_MODE_RGMII means that the delays are provided by the
> board layout, so this seems wrong to me. Please review the phylib
> documentation concerning RGMII delays. I'd like Andrew to comment on
> this too.
> 

The idea with this was, if the PHY does not provide internal delay, then
we do it in the MAC. In Documentation/networking/phy.rst I saw this:

* PHY_INTERFACE_MODE_RGMII: the PHY is not responsible for inserting any
  internal delay by itself, it assumes that either the Ethernet MAC (if capable)
  or the PCB traces insert the correct 1.5-2ns delay

This code was intended for the Ethernet MAC case. When this delay configuration
is applied the ~2ns delay is added by the MAC. For instance, the tx_delay
case will add the delay to the txc clock configured above.

I understood the section below:

  For cases where the PHY is not capable of providing this delay, but the
  Ethernet MAC driver is capable of doing so, the correct phy_interface_t value
  should be PHY_INTERFACE_MODE_RGMII, and the Ethernet MAC driver should be
  configured correctly in order to provide the required transmit and/or receive
  side delay from the perspective of the PHY device.

to mean that the mac driver is allowed to configure these delays based
on phy_interface_t. Because the mac delays can be configured assymmetrically
for rx and tx, the modes PHY_INTERFACE_MODE_RGMII_TXID and
PHY_INTERFACE_MODE_RGMII_RXID are also handled.

As Vladimir mentions, there are other drivers who configure this using

rx-internal-delay-ps
tx-internal-delay-ps

Which is also done by
drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_rgmii.c

However, the DLL on lan969x is different. It can configure the delay
amount with CLK_SEL. On lan9645x it is simply ~2ns, on or off, in rx or tx
direction, so accepting a delay amount seemed out of place.

But I can change this to use {rx,tx}-internal-delay-ps, and enable the
delay base on these being 0 or not, if you think this is the better
way to do it.


> > +
> > +     /* Setup DLL configuration. Register layout:
> > +      * 0:        RGMII_0_RX
> > +      * 1:        RGMII_0_TX
> > +      * 2:        RGMII_1_RX
> > +      * 3:        RGMII_1_TX
> > +      * ...
> > +      * (N<<1)    RGMII_N_RX,
> > +      * (N<<1)+1: RGMII_N_TX,
> > +      */
> > +     rx_idx = idx << 1;
> > +     tx_idx = rx_idx + 1;
> 
> Nit: I think the shift is too clever here. It's clearly:
> 
>         rx_idx = idx * 2;
>         tx_idx = idx * 2 + 1;
> 

I will change this.

> > +
> > +     /* Enable DLL in RGMII clock paths, deassert DLL reset, and start the
> > +      * delay tune FSM.
> > +      */
> > +     lan_rmw(HSIO_DLL_CFG_DLL_CLK_ENA_SET(1) |
> > +             HSIO_DLL_CFG_DLL_RST_SET(0) |
> > +             HSIO_DLL_CFG_DLL_ENA_SET(rx_delay) |
> > +             HSIO_DLL_CFG_DELAY_ENA_SET(rx_delay),
> > +             HSIO_DLL_CFG_DLL_CLK_ENA |
> > +             HSIO_DLL_CFG_DLL_RST |
> > +             HSIO_DLL_CFG_DLL_ENA |
> > +             HSIO_DLL_CFG_DELAY_ENA,
> > +             lan9645x, HSIO_DLL_CFG(rx_idx));
> > +
> > +     lan_rmw(HSIO_DLL_CFG_DLL_CLK_ENA_SET(1) |
> > +             HSIO_DLL_CFG_DLL_RST_SET(0) |
> > +             HSIO_DLL_CFG_DLL_ENA_SET(tx_delay) |
> > +             HSIO_DLL_CFG_DELAY_ENA_SET(tx_delay),
> > +             HSIO_DLL_CFG_DLL_CLK_ENA |
> > +             HSIO_DLL_CFG_DLL_RST |
> > +             HSIO_DLL_CFG_DLL_ENA |
> > +             HSIO_DLL_CFG_DELAY_ENA,
> > +             lan9645x, HSIO_DLL_CFG(tx_idx));
> > +
> > +     return 0;
> > +}
> > +
> > +static void lan9645x_phylink_mac_config(struct lan9645x *lan9645x, int port,
> > +                                     unsigned int mode,
> > +                                     const struct phylink_link_state *state)
> > +{
> > +     if (phy_interface_mode_is_rgmii(state->interface))
> > +             lan9645x_rgmii_setup(lan9645x, port, state->speed,
> > +                                  state->interface);
> 
> state->speed will always be SPEED_UNKNOWN here. Please don't use
> state->speed here, it's even documented that drivers should not access
> this member in their mac_config method().
> 

I will change this in the next version.

> > +}
> > +
> > +static int lan9645x_phylink_mac_prepare(struct lan9645x *lan9645x, int port,
> > +                                     unsigned int mode,
> > +                                     phy_interface_t iface)
> > +{
> > +     switch (port) {
> > +     case 0 ... 3:
> > +             lan_rmw(HSIO_HW_CFG_GMII_ENA_SET(BIT(port)),
> > +                     HSIO_HW_CFG_GMII_ENA_SET(BIT(port)), lan9645x,
> > +                     HSIO_HW_CFG);
> > +             break;
> > +     case 4:
> > +             lan_rmw(HSIO_HW_CFG_GMII_ENA_SET(BIT(port)),
> > +                     HSIO_HW_CFG_GMII_ENA_SET(BIT(port)), lan9645x,
> > +                     HSIO_HW_CFG);
> > +
> > +             if (phy_interface_mode_is_rgmii(iface))
> > +                     lan_rmw(HSIO_HW_CFG_RGMII_0_CFG_SET(1),
> > +                             HSIO_HW_CFG_RGMII_0_CFG,
> > +                             lan9645x, HSIO_HW_CFG);
> > +
> > +             break;
> > +     case 7 ... 8:
> > +             lan_rmw(HSIO_HW_CFG_GMII_ENA_SET(BIT(port)),
> > +                     HSIO_HW_CFG_GMII_ENA_SET(BIT(port)), lan9645x,
> > +                     HSIO_HW_CFG);
> > +             break;
> > +     default:
> > +             /* Ports 5-6 are SerDes-only and need PCS support (not yet
> > +              * implemented). They are excluded from phylink_get_caps.
> 
> ... which means phylink_create() will fail, and this function will never
> be called for those ports.
> 
> > +              */
> > +             return -EINVAL;
> > +     }
> 
> As each of the above cases uses the exactly the same preamble, I'm
> wondering whether something like:
> 
>         u32 mask;
> 
>         if (port == 5 || port == 6 || port > 8)
>                 return -EINVAL;
> 
>         mask = HSIO_HW_CFG_GMII_ENA_SET(BIT(port));
>         lan_rmw(mask, mask, lan9645x, HSIO_HW_CFG);
> 
> 
>         if (port == 4 && phy_interface_mode_is_rgmii(iface))
>                 lan_rmw(HSIO_HW_CFG_RGMII_0_CFG_SET(1),
>                         HSIO_HW_CFG_RGMII_0_CFG,
>                         lan9645x, HSIO_HW_CFG);
> 
> would be neater.
> 

Yes this is much simpler. With the serdes ports and qsgmii support,
the cases have a bit more variability, but I will use change this
for now.

> > +
> > +     return 0;
> > +}
> > +
> > +static int lan9645x_port_is_cuphy(struct lan9645x *lan9645x, int port,
> > +                               phy_interface_t interface)
> > +{
> > +     return port >= 0 && port <= 4 && interface == PHY_INTERFACE_MODE_GMII;
> > +}
> > +
> > +static void lan9645x_phylink_mac_link_up(struct lan9645x *lan9645x, int port,
> > +                                      unsigned int link_an_mode,
> > +                                      phy_interface_t interface,
> > +                                      struct phy_device *phydev, int speed,
> > +                                      int duplex, bool tx_pause,
> > +                                      bool rx_pause)
> > +{
> > +     struct lan9645x_port *p = lan9645x_to_port(lan9645x, port);
> > +     int rx_ifg1, rx_ifg2, tx_ifg, gtx_clk = 0;
> > +     struct lan9645x_path_delay *path_delay;
> > +     int gspeed = LAN9645X_SPEED_DISABLED;
> > +     int mode = 0;
> > +     int fc_spd;
> > +
> > +     /* Configure speed for RGMII modules. */
> > +     if (phy_interface_mode_is_rgmii(interface))
> > +             lan9645x_rgmii_setup(lan9645x, port, speed, interface);
> 
> You shouldn't need to do a full setup of the RGMII state here - all
> that should be done is to adjust the transmit clock speed. I would
> prefer to see lan9645x_rgmii_setup() split into a "we're configuring
> the bulk of RGMII state" function which is called from mac_config(),
> and a "we have link up, do the minimal configuration that is dependent
> on the resulting link speed" in mac_link_up().
> 

I will try to split up the RGMII configuration like this.

> > +
> > +     if (duplex == DUPLEX_FULL) {
> > +             mode |= DEV_MAC_MODE_CFG_FDX_ENA_SET(1);
> > +             rx_ifg2 = DEV_MAC_IFG_CFG_RX_IFG2_SET(0x2);
> > +             tx_ifg = DEV_MAC_IFG_CFG_TX_IFG_SET(0x5);
> > +
> > +     } else {
> > +             rx_ifg2 = DEV_MAC_IFG_CFG_RX_IFG2_SET(0x2);
> > +             tx_ifg = DEV_MAC_IFG_CFG_TX_IFG_SET(0x6);
> > +     }
> > +
> > +     switch (speed) {
> > +     case SPEED_10:
> > +             rx_ifg1 = DEV_MAC_IFG_CFG_RX_IFG1_SET(0x2);
> > +             gspeed = LAN9645X_SPEED_10;
> > +             break;
> > +     case SPEED_100:
> > +             rx_ifg1 = DEV_MAC_IFG_CFG_RX_IFG1_SET(0x1);
> > +             gspeed = LAN9645X_SPEED_100;
> > +             break;
> > +     case SPEED_1000:
> > +             gspeed = LAN9645X_SPEED_1000;
> > +             mode |= DEV_MAC_MODE_CFG_GIGA_MODE_ENA_SET(1);
> > +             mode |= DEV_MAC_MODE_CFG_FDX_ENA_SET(1);
> > +             rx_ifg1 = DEV_MAC_IFG_CFG_RX_IFG1_SET(0x1);
> > +             rx_ifg2 = DEV_MAC_IFG_CFG_RX_IFG2_SET(0x2);
> > +             tx_ifg = DEV_MAC_IFG_CFG_TX_IFG_SET(0x6);
> > +             gtx_clk = 1;
> > +             break;
> > +     case SPEED_2500:
> > +             gspeed = LAN9645X_SPEED_2500;
> > +             mode |= DEV_MAC_MODE_CFG_GIGA_MODE_ENA_SET(1);
> > +             mode |= DEV_MAC_MODE_CFG_FDX_ENA_SET(1);
> > +             rx_ifg1 = DEV_MAC_IFG_CFG_RX_IFG1_SET(0x1);
> > +             rx_ifg2 = DEV_MAC_IFG_CFG_RX_IFG2_SET(0x2);
> > +             tx_ifg = DEV_MAC_IFG_CFG_TX_IFG_SET(0x6);
> > +             break;
> > +     default:
> > +             dev_err(lan9645x->dev, "Unsupported speed on port %d: %d\n",
> > +                     p->chip_port, speed);
> > +             return;
> > +     }
> > +
> > +     p->speed = gspeed;
> > +     p->duplex = duplex;
> > +     fc_spd = lan9645x_speed_fc_enc(p->speed);
> > +
> > +     if (phy_interface_num_ports(interface) == 4 ||
> > +         interface == PHY_INTERFACE_MODE_SGMII)
> > +             mode |= DEV_MAC_MODE_CFG_GIGA_MODE_ENA_SET(1);
> 
> PHY_INTERFACE_MODE_SGMII in the kernel means Cisco SGMII, and does not
> include 1000BASE-X. As SGMII/BASE-X support is incomplete, I can't tell
> whether the above is correct.
> 

Yes this is for serdes ports, I will remove this.

> > +
> > +     lan_rmw(mode,
> > +             DEV_MAC_MODE_CFG_FDX_ENA |
> > +             DEV_MAC_MODE_CFG_GIGA_MODE_ENA,
> > +             lan9645x, DEV_MAC_MODE_CFG(p->chip_port));
> > +
> > +     lan_rmw(tx_ifg | rx_ifg1 | rx_ifg2,
> 
> It only makes sense to use seperate variables if you're going to be
> placing them into the appropriate bitfield locations in a different
> part of the code to that which is making decisions about their values.
> However, I'm guessing that DEV_MAC_IFG_CFG_RX_IFG1_SET() et.al. are
> preparing the field values.
> 
> So, I'd prefer to see a single variable called "mac_ifg_cfg" with
> the values for each field accumulated into that:
> 
>         switch ... {
>         case SPEED_1000:
>                 mac_ifg_cfg = DEV_MAC_IFG_CFG_RX_IFG1_SET(0x1) |
>                               DEV_MAC_IFG_CFG_RX_IFG2_SET(0x2) |
>                               DEV_MAC_IFG_CFG_TX_IFG_SET(0x6);
>         }
>         ...
>         lan_rmw(mac_ifg_cfg,
> 
> or separate variables with the creation of the register value at
> the use site:
> 
>         switch ... {
>         case SPEED_1000:
>                 rx_ifg1 = 0x1;
>                 rx_ifg2 = 0x2;
>                 tx_ifg = 0x6;
>         }
>         ...
>         lan_rmw(DEV_MAC_IFG_CFG_RX_IFG1_SET(rx_ifg1) |
>                 DEV_MAC_IFG_CFG_RX_IFG2_SET(rx_ifg2) |
>                 DEV_MAC_IFG_CFG_TX_IFG_SET(tx_ifg),
> 

I will update this for the next version.

> > +             DEV_MAC_IFG_CFG_TX_IFG |
> > +             DEV_MAC_IFG_CFG_RX_IFG1 |
> > +             DEV_MAC_IFG_CFG_RX_IFG2,
> > +             lan9645x, DEV_MAC_IFG_CFG(p->chip_port));
> > +
> > +     lan_rmw(DEV_MAC_HDX_CFG_SEED_SET(p->chip_port) |
> > +             DEV_MAC_HDX_CFG_SEED_LOAD_SET(1),
> > +             DEV_MAC_HDX_CFG_SEED |
> > +             DEV_MAC_HDX_CFG_SEED_LOAD, lan9645x,
> > +             DEV_MAC_HDX_CFG(p->chip_port));
> > +
> > +     if (lan9645x_port_is_cuphy(lan9645x, port, interface)) {
> > +             lan_rmw(CHIP_TOP_CUPHY_PORT_CFG_GTX_CLK_ENA_SET(gtx_clk),
> > +                     CHIP_TOP_CUPHY_PORT_CFG_GTX_CLK_ENA, lan9645x,
> > +                     CHIP_TOP_CUPHY_PORT_CFG(p->chip_port));
> > +     }
> > +
> > +     lan_rmw(DEV_MAC_HDX_CFG_SEED_LOAD_SET(0),
> > +             DEV_MAC_HDX_CFG_SEED_LOAD, lan9645x,
> > +             DEV_MAC_HDX_CFG(p->chip_port));
> > +
> > +     /* Set PFC link speed and enable map */
> > +     lan_rmw(ANA_PFC_CFG_FC_LINK_SPEED_SET(fc_spd) |
> > +             ANA_PFC_CFG_RX_PFC_ENA_SET(0),
> > +             ANA_PFC_CFG_FC_LINK_SPEED |
> > +             ANA_PFC_CFG_RX_PFC_ENA,
> > +             lan9645x, ANA_PFC_CFG(p->chip_port));
> > +
> > +     lan_rmw(DEV_PCS1G_CFG_PCS_ENA_SET(1),
> > +             DEV_PCS1G_CFG_PCS_ENA, lan9645x,
> > +             DEV_PCS1G_CFG(p->chip_port));
> > +
> > +     lan_rmw(DEV_PCS1G_SD_CFG_SD_ENA_SET(0),
> > +             DEV_PCS1G_SD_CFG_SD_ENA,
> > +             lan9645x, DEV_PCS1G_SD_CFG(p->chip_port));
> > +
> > +     lan_rmw(SYS_PAUSE_CFG_PAUSE_ENA_SET(1),
> > +             SYS_PAUSE_CFG_PAUSE_ENA,
> > +             lan9645x, SYS_PAUSE_CFG(p->chip_port));
> > +
> > +     /* Set SMAC of Pause frame (00:00:00:00:00:00) */
> > +     lan_wr(0, lan9645x, DEV_FC_MAC_LOW_CFG(p->chip_port));
> > +     lan_wr(0, lan9645x, DEV_FC_MAC_HIGH_CFG(p->chip_port));
> > +
> > +     /* Flow control */
> > +     lan_rmw(SYS_MAC_FC_CFG_FC_LINK_SPEED_SET(fc_spd) |
> > +             SYS_MAC_FC_CFG_FC_LATENCY_CFG_SET(0x7) |
> > +             SYS_MAC_FC_CFG_ZERO_PAUSE_ENA_SET(1) |
> > +             SYS_MAC_FC_CFG_PAUSE_VAL_CFG_SET(0xffff) |
> > +             SYS_MAC_FC_CFG_RX_FC_ENA_SET(rx_pause ? 1 : 0) |
> > +             SYS_MAC_FC_CFG_TX_FC_ENA_SET(tx_pause ? 1 : 0),
> > +             SYS_MAC_FC_CFG_FC_LINK_SPEED |
> > +             SYS_MAC_FC_CFG_FC_LATENCY_CFG |
> > +             SYS_MAC_FC_CFG_ZERO_PAUSE_ENA |
> > +             SYS_MAC_FC_CFG_PAUSE_VAL_CFG |
> > +             SYS_MAC_FC_CFG_RX_FC_ENA |
> > +             SYS_MAC_FC_CFG_TX_FC_ENA,
> > +             lan9645x, SYS_MAC_FC_CFG(p->chip_port));
> > +
> > +     list_for_each_entry(path_delay, &p->path_delays, list) {
> > +             if (path_delay->speed == speed) {
> > +                     lan_wr(path_delay->rx_delay + p->rx_delay,
> > +                            lan9645x, SYS_PTP_RXDLY_CFG(p->chip_port));
> > +                     lan_wr(path_delay->tx_delay,
> > +                            lan9645x, SYS_PTP_TXDLY_CFG(p->chip_port));
> > +             }
> > +     }
> > +
> > +     /* Enable MAC module */
> > +     lan_wr(DEV_MAC_ENA_CFG_RX_ENA_SET(1) |
> > +            DEV_MAC_ENA_CFG_TX_ENA_SET(1),
> > +            lan9645x, DEV_MAC_ENA_CFG(p->chip_port));
> > +
> > +     /* port _must_ be taken out of reset before MAC. */
> > +     lan_rmw(DEV_CLOCK_CFG_PORT_RST_SET(0),
> > +             DEV_CLOCK_CFG_PORT_RST,
> > +             lan9645x, DEV_CLOCK_CFG(p->chip_port));
> > +
> > +     /* Take out the clock from reset. Note this write will set all these
> > +      * fields to zero:
> > +      *
> > +      * DEV_CLOCK_CFG[*].MAC_TX_RST
> > +      * DEV_CLOCK_CFG[*].MAC_RX_RST
> > +      * DEV_CLOCK_CFG[*].PCS_TX_RST
> > +      * DEV_CLOCK_CFG[*].PCS_RX_RST
> > +      * DEV_CLOCK_CFG[*].PORT_RST
> > +      * DEV_CLOCK_CFG[*].PHY_RST
> > +      *
> > +      * Note link_down will assert PORT_RST, MAC_RX_RST and MAC_TX_RST, so
> > +      * we are effectively taking the mac tx/rx clocks out of reset.
> > +      *
> > +      * This linkspeed field has a slightly different encoding from others:
> > +      *
> > +      * - 0 is no-link
> > +      * - 1 is both 2500/1000
> > +      * - 2 is 100mbit
> > +      * - 3 is 10mbit
> > +      *
> > +      */
> > +     lan_wr(DEV_CLOCK_CFG_LINK_SPEED_SET(fc_spd == 0 ? 1 : fc_spd),
> > +            lan9645x,
> > +            DEV_CLOCK_CFG(p->chip_port));
> > +
> > +     /* Core: Enable port for frame transfer */
> > +     lan_rmw(QSYS_SW_PORT_MODE_PORT_ENA_SET(1) |
> > +             QSYS_SW_PORT_MODE_SCH_NEXT_CFG_SET(1) |
> > +             QSYS_SW_PORT_MODE_INGRESS_DROP_MODE_SET(1) |
> > +             QSYS_SW_PORT_MODE_TX_PFC_ENA_SET(0),
> > +             QSYS_SW_PORT_MODE_PORT_ENA |
> > +             QSYS_SW_PORT_MODE_SCH_NEXT_CFG |
> > +             QSYS_SW_PORT_MODE_INGRESS_DROP_MODE |
> > +             QSYS_SW_PORT_MODE_TX_PFC_ENA,
> > +             lan9645x, QSYS_SW_PORT_MODE(p->chip_port));
> > +
> > +     lan_rmw(AFI_PORT_CFG_FC_SKIP_TTI_INJ_SET(0) |
> > +             AFI_PORT_CFG_FRM_OUT_MAX_SET(16),
> > +             AFI_PORT_CFG_FC_SKIP_TTI_INJ |
> > +             AFI_PORT_CFG_FRM_OUT_MAX,
> > +             lan9645x, AFI_PORT_CFG(p->chip_port));
> 
> This looks overly complicated for link-up events. Please look to see
> how much of this can be moved to the mac_prepare, mac_config and
> mac_finish methods, which are supposed to be doing the bulk of
> configuring the port for operating in the specified PHY interface mode.
> 
> mac_link_up is only supposed to configure for the negotiated parameters
> such as speed, duplex, and pause and enable transmission/reception.
> 
> Conversely, mac_link_down should just stop the transmission/reception
> of packets.
> 
> The PCS state fiddling concerns me greatly (although I do appreciate
> you don't have that implemented fully yet) but given its presence in
> mac_link_up, I think this will cause you future problems, especially
> with inband support - the PCS link needs to remain active so it can
> participate in negotiation, thereby allowing this method to be called.
> 
> You also don't want to be resetting the PCS on the mac_link_down()
> method for the same reason.
> 

I agree it is complicated. This is taken from the sequence in lan966x.
Back then it took some effort, and help from design, to get it right.
So I did not have the courage to change it, but I will try to
rearrange this to be more in the spirit of phylink.

> > +}
> > +
> > +void lan9645x_phylink_port_down(struct lan9645x *lan9645x, int port)
> > +{
> > +     struct lan9645x_port *p = lan9645x_to_port(lan9645x, port);
> > +     u32 val;
> > +
> > +     /* 0.5: Disable any AFI */
> > +     lan_rmw(AFI_PORT_CFG_FC_SKIP_TTI_INJ_SET(1) |
> > +             AFI_PORT_CFG_FRM_OUT_MAX_SET(0),
> > +             AFI_PORT_CFG_FC_SKIP_TTI_INJ |
> > +             AFI_PORT_CFG_FRM_OUT_MAX,
> > +             lan9645x, AFI_PORT_CFG(p->chip_port));
> > +
> > +     /* wait for reg afi_port_frm_out to become 0 for the port */
> > +     if (lan9645x_rd_poll_slow(lan9645x, AFI_PORT_FRM_OUT(p->chip_port),
> > +                               val,
> > +                               !AFI_PORT_FRM_OUT_FRM_OUT_CNT_GET(val)))
> > +             dev_err(lan9645x->dev, "AFI timeout chip port %u",
> > +                     p->chip_port);
> > +
> > +     /* 2: Disable MAC frame reception */
> > +     lan_rmw(DEV_MAC_ENA_CFG_RX_ENA_SET(0),
> > +             DEV_MAC_ENA_CFG_RX_ENA,
> > +             lan9645x, DEV_MAC_ENA_CFG(p->chip_port));
> > +
> > +     /* 1: Reset the PCS Rx clock domain  */
> > +     lan_rmw(DEV_CLOCK_CFG_PCS_RX_RST_SET(1),
> > +             DEV_CLOCK_CFG_PCS_RX_RST,
> > +             lan9645x, DEV_CLOCK_CFG(p->chip_port));
> > +
> > +     p->speed = LAN9645X_SPEED_DISABLED;
> > +     p->duplex = DUPLEX_UNKNOWN;
> > +
> > +     /* 3: Disable traffic being sent to or from switch port */
> > +     lan_rmw(QSYS_SW_PORT_MODE_PORT_ENA_SET(0),
> > +             QSYS_SW_PORT_MODE_PORT_ENA,
> > +             lan9645x, QSYS_SW_PORT_MODE(p->chip_port));
> > +
> > +     /* 4: Disable dequeuing from the egress queues  */
> > +     lan_rmw(QSYS_PORT_MODE_DEQUEUE_DIS_SET(1),
> > +             QSYS_PORT_MODE_DEQUEUE_DIS,
> > +             lan9645x, QSYS_PORT_MODE(p->chip_port));
> > +
> > +     /* 5: Disable Flowcontrol */
> > +     lan_rmw(SYS_PAUSE_CFG_PAUSE_ENA_SET(0),
> > +             SYS_PAUSE_CFG_PAUSE_ENA,
> > +             lan9645x, SYS_PAUSE_CFG(p->chip_port));
> > +
> > +     /* 5.1: Disable PFC */
> > +     lan_rmw(QSYS_SW_PORT_MODE_TX_PFC_ENA_SET(0),
> > +             QSYS_SW_PORT_MODE_TX_PFC_ENA,
> > +             lan9645x, QSYS_SW_PORT_MODE(p->chip_port));
> > +
> > +     /* 6: Wait a worst case time 8ms (10K jumbo/10Mbit) */
> > +     usleep_range(8 * USEC_PER_MSEC, 9 * USEC_PER_MSEC);
> > +
> > +     /* 7: Disable HDX backpressure. */
> > +     lan_rmw(SYS_FRONT_PORT_MODE_HDX_MODE_SET(0),
> > +             SYS_FRONT_PORT_MODE_HDX_MODE,
> > +             lan9645x, SYS_FRONT_PORT_MODE(p->chip_port));
> > +
> > +     /* 8: Flush the queues associated with the port */
> > +     lan_rmw(QSYS_SW_PORT_MODE_AGING_MODE_SET(3),
> > +             QSYS_SW_PORT_MODE_AGING_MODE,
> > +             lan9645x, QSYS_SW_PORT_MODE(p->chip_port));
> > +
> > +     /* 9: Enable dequeuing from the egress queues */
> > +     lan_rmw(QSYS_PORT_MODE_DEQUEUE_DIS_SET(0),
> > +             QSYS_PORT_MODE_DEQUEUE_DIS,
> > +             lan9645x, QSYS_PORT_MODE(p->chip_port));
> > +
> > +     /* 10: Wait until flushing is complete */
> > +     if (lan9645x_rd_poll_slow(lan9645x, QSYS_SW_STATUS(p->chip_port),
> > +                               val, !QSYS_SW_STATUS_EQ_AVAIL_GET(val)))
> > +             dev_err(lan9645x->dev, "Flush timeout chip port %u", port);
> > +
> > +     /* 11: Disable MAC tx */
> > +     lan_rmw(DEV_MAC_ENA_CFG_TX_ENA_SET(0),
> > +             DEV_MAC_ENA_CFG_TX_ENA,
> > +             lan9645x, DEV_MAC_ENA_CFG(p->chip_port));
> > +
> > +     /* 12: Reset the Port and MAC clock domains */
> > +     lan_rmw(DEV_CLOCK_CFG_PORT_RST_SET(1),
> > +             DEV_CLOCK_CFG_PORT_RST,
> > +             lan9645x, DEV_CLOCK_CFG(p->chip_port));
> > +
> > +     /* Wait before resetting MAC clock domains. */
> > +     usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC);
> > +
> > +     lan_rmw(DEV_CLOCK_CFG_MAC_TX_RST_SET(1) |
> > +             DEV_CLOCK_CFG_MAC_RX_RST_SET(1) |
> > +             DEV_CLOCK_CFG_PORT_RST_SET(1),
> > +             DEV_CLOCK_CFG_MAC_TX_RST |
> > +             DEV_CLOCK_CFG_MAC_RX_RST |
> > +             DEV_CLOCK_CFG_PORT_RST,
> > +             lan9645x, DEV_CLOCK_CFG(p->chip_port));
> > +
> > +     /* 13: Clear flushing */
> > +     lan_rmw(QSYS_SW_PORT_MODE_AGING_MODE_SET(1),
> > +             QSYS_SW_PORT_MODE_AGING_MODE,
> > +             lan9645x, QSYS_SW_PORT_MODE(p->chip_port));
> > +}
> > +
> > +static void lan9645x_phylink_mac_link_down(struct lan9645x *lan9645x, int port,
> > +                                        unsigned int link_an_mode,
> > +                                        phy_interface_t interface)
> > +{
> > +     struct lan9645x_port *p = lan9645x_to_port(lan9645x, port);
> > +
> > +     lan9645x_phylink_port_down(lan9645x, port);
> > +
> > +     /* 14: Take PCS out of reset */
> > +     lan_rmw(DEV_CLOCK_CFG_PCS_RX_RST_SET(0) |
> > +             DEV_CLOCK_CFG_PCS_TX_RST_SET(0),
> > +             DEV_CLOCK_CFG_PCS_RX_RST |
> > +             DEV_CLOCK_CFG_PCS_TX_RST,
> > +             lan9645x, DEV_CLOCK_CFG(p->chip_port));
> > +}
> > +
> > +static struct lan9645x_port *
> > +lan9645x_phylink_config_to_port(struct phylink_config *config)
> > +{
> > +     struct dsa_port *dp = dsa_phylink_to_port(config);
> > +
> > +     return lan9645x_to_port(dp->ds->priv, dp->index);
> > +}
> > +
> > +static void
> > +lan9645x_port_phylink_mac_config(struct phylink_config *config,
> > +                              unsigned int mode,
> > +                              const struct phylink_link_state *state)
> > +{
> > +     struct lan9645x_port *p = lan9645x_phylink_config_to_port(config);
> > +
> > +     lan9645x_phylink_mac_config(p->lan9645x, p->chip_port, mode, state);
> > +}
> > +
> > +static void lan9645x_port_phylink_mac_link_up(struct phylink_config *config,
> > +                                           struct phy_device *phydev,
> > +                                           unsigned int link_an_mode,
> > +                                           phy_interface_t interface,
> > +                                           int speed, int duplex,
> > +                                           bool tx_pause, bool rx_pause)
> > +{
> > +     struct lan9645x_port *p = lan9645x_phylink_config_to_port(config);
> > +
> > +     lan9645x_phylink_mac_link_up(p->lan9645x, p->chip_port, link_an_mode,
> > +                                  interface, phydev, speed, duplex, tx_pause,
> > +                                  rx_pause);
> > +}
> > +
> > +static void lan9645x_port_phylink_mac_link_down(struct phylink_config *config,
> > +                                             unsigned int link_an_mode,
> > +                                             phy_interface_t interface)
> > +{
> > +     struct lan9645x_port *p = lan9645x_phylink_config_to_port(config);
> > +
> > +     lan9645x_phylink_mac_link_down(p->lan9645x, p->chip_port, link_an_mode,
> > +                                    interface);
> > +}
> > +
> > +static int lan9645x_port_phylink_mac_prepare(struct phylink_config *config,
> > +                                          unsigned int mode,
> > +                                          phy_interface_t iface)
> > +{
> > +     struct lan9645x_port *p = lan9645x_phylink_config_to_port(config);
> > +
> > +     return lan9645x_phylink_mac_prepare(p->lan9645x, p->chip_port, mode,
> > +                                         iface);
> > +}
> > +
> > +const struct phylink_mac_ops lan9645x_phylink_mac_ops = {
> > +     .mac_config                     = lan9645x_port_phylink_mac_config,
> > +     .mac_link_up                    = lan9645x_port_phylink_mac_link_up,
> > +     .mac_link_down                  = lan9645x_port_phylink_mac_link_down,
> > +     .mac_prepare                    = lan9645x_port_phylink_mac_prepare,
> 
> Please order the functions, and the initialisers here in the same order
> that they appear in struct phylink_mac_ops, which is:
> 
>         mac_prepare (this will be called first)
>         mac_config (this will be called second)
> 
>         mac_link_down
>         mac_link_up (this will be called last when bringing up the link)
> 
> The configuration functions (prepare, config, finish) are specifically
> arranged in the order that they are called which helps make the code
> more readable - and if you arrange the functions in the file in the
> same order, then when one reads through the code, one reads the code in
> the order it will be executed.
> 
> What you have is a bit like a book where the preface is oddly located
> after the main body of the text!
> 

Thank you, I will shuffle this around.

> > +};
> > diff --git a/drivers/net/dsa/microchip/lan9645x/lan9645x_port.c b/drivers/net/dsa/microchip/lan9645x/lan9645x_port.c
> > new file mode 100644
> > index 000000000000..038868ae0a32
> > --- /dev/null
> > +++ b/drivers/net/dsa/microchip/lan9645x/lan9645x_port.c
> > @@ -0,0 +1,289 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/* Copyright (C) 2026 Microchip Technology Inc.
> > + */
> > +
> > +#include "lan9645x_main.h"
> > +
> > +int lan9645x_port_init(struct lan9645x *lan9645x, int port)
> > +{
> > +     struct lan9645x_port *p;
> > +
> > +     p = lan9645x_to_port(lan9645x, port);
> > +
> > +     /* Disable learning on port */
> > +     lan_rmw(ANA_PORT_CFG_LEARN_ENA_SET(0),
> > +             ANA_PORT_CFG_LEARN_ENA,
> > +             lan9645x, ANA_PORT_CFG(p->chip_port));
> > +
> > +     lan9645x_port_set_maxlen(lan9645x, port, ETH_DATA_LEN);
> > +
> > +     lan9645x_phylink_port_down(lan9645x, port);
> > +
> > +     if (phy_interface_num_ports(p->phy_mode) == 4)
> > +             lan_rmw(DEV_CLOCK_CFG_PCS_RX_RST_SET(0) |
> > +                     DEV_CLOCK_CFG_PCS_TX_RST_SET(0),
> > +                     DEV_CLOCK_CFG_PCS_RX_RST |
> > +                     DEV_CLOCK_CFG_PCS_TX_RST,
> > +                     lan9645x, DEV_CLOCK_CFG(p->chip_port));
> 
> Two things I don't like here:
> 
> 1. the use of phy_mode rather than working out another way to handle
>    this.
> 
> 2. I think the above is just a complex way of saying
>         if (p->phy_mode == PHY_INTERFACE_MODE_QSGMII)
> 
>    since you don't support QUSGMII or 10G_QXGMII.
> 
> Thanks.
> 

Indeed, that is for QSGMII. I will remove it now, and use 
PHY_INTERFACE_MODE_QSGMII directly when the serdes ports are implemented.

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

Thank you for the comments,
Emil


  reply	other threads:[~2026-03-05 14:25 UTC|newest]

Thread overview: 69+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-03 12:22 [PATCH net-next 0/8] net: dsa: add DSA support for the LAN9645x switch chip family Jens Emil Schulz Østergaard
2026-03-03 12:22 ` [PATCH net-next 1/8] net: dsa: add tag driver for LAN9645X Jens Emil Schulz Østergaard
2026-03-03 14:13   ` Andrew Lunn
2026-03-03 15:58     ` Jens Emil Schulz Ostergaard
2026-03-04 15:14       ` Andrew Lunn
2026-03-05 12:59         ` Jens Emil Schulz Ostergaard
2026-03-03 16:11   ` Vladimir Oltean
2026-03-05 13:53     ` Jens Emil Schulz Ostergaard
2026-03-04 15:23   ` Andrew Lunn
2026-03-05 13:01     ` Jens Emil Schulz Ostergaard
2026-03-03 12:22 ` [PATCH net-next 2/8] dt-bindings: net: lan9645x: add LAN9645X switch bindings Jens Emil Schulz Østergaard
2026-03-03 13:22   ` Vladimir Oltean
2026-03-03 16:00     ` Jens Emil Schulz Ostergaard
2026-03-03 14:18   ` Andrew Lunn
2026-03-03 19:04     ` Conor Dooley
2026-03-04 15:57       ` Jens Emil Schulz Ostergaard
2026-03-05 12:57       ` Jens Emil Schulz Ostergaard
2026-03-05 18:31         ` Conor Dooley
2026-03-06 15:08           ` Jens Emil Schulz Ostergaard
2026-03-06 15:20             ` Conor Dooley
2026-03-18 14:19               ` Jens Emil Schulz Ostergaard
2026-03-18 17:18                 ` Conor Dooley
2026-03-18 17:20                   ` Conor Dooley
2026-03-18 17:26                     ` Christian Marangi
2026-03-24 10:31                       ` Jens Emil Schulz Ostergaard
2026-03-04 15:55     ` Jens Emil Schulz Ostergaard
2026-03-03 18:49   ` Conor Dooley
2026-03-04 15:58     ` Jens Emil Schulz Ostergaard
2026-03-03 18:56   ` Conor Dooley
2026-03-04 16:10     ` Jens Emil Schulz Ostergaard
2026-03-04 16:14       ` Vladimir Oltean
2026-03-04 19:06         ` Conor Dooley
2026-03-05 13:08           ` Jens Emil Schulz Ostergaard
2026-03-03 12:22 ` [PATCH net-next 3/8] net: dsa: lan9645x: add autogenerated register macros Jens Emil Schulz Østergaard
2026-03-03 12:22 ` [PATCH net-next 4/8] net: dsa: lan9645x: add basic dsa driver for LAN9645X Jens Emil Schulz Østergaard
2026-03-03 14:15   ` Vladimir Oltean
2026-03-04 14:37     ` Jens Emil Schulz Ostergaard
2026-03-04 15:58   ` Russell King (Oracle)
2026-03-05 14:24     ` Jens Emil Schulz Ostergaard [this message]
2026-03-05 14:58       ` Andrew Lunn
2026-03-05 15:10         ` Vladimir Oltean
2026-03-05 16:54         ` Alexander Stein
2026-03-05 17:37           ` Andrew Lunn
2026-03-06 15:03             ` Jens Emil Schulz Ostergaard
2026-03-06 16:33               ` Andrew Lunn
2026-03-09 12:01                 ` Jens Emil Schulz Ostergaard
2026-03-06 14:22         ` Russell King (Oracle)
2026-03-06 21:03           ` Jakub Kicinski
2026-03-03 12:22 ` [PATCH net-next 5/8] net: dsa: lan9645x: add bridge support Jens Emil Schulz Østergaard
2026-03-03 14:20   ` Vladimir Oltean
2026-03-03 16:08     ` Jens Emil Schulz Ostergaard
2026-03-03 16:17       ` Vladimir Oltean
2026-03-05 13:14         ` Jens Emil Schulz Ostergaard
2026-03-03 14:51   ` Vladimir Oltean
2026-03-09 12:09     ` Jens Emil Schulz Ostergaard
2026-03-03 12:22 ` [PATCH net-next 6/8] net: dsa: lan9645x: add vlan support Jens Emil Schulz Østergaard
2026-03-03 14:59   ` Vladimir Oltean
2026-03-04 14:40     ` Jens Emil Schulz Ostergaard
2026-03-04 14:52       ` Vladimir Oltean
2026-03-03 12:22 ` [PATCH net-next 7/8] net: dsa: lan9645x: add mac table integration Jens Emil Schulz Østergaard
2026-03-03 15:27   ` Vladimir Oltean
2026-03-04 15:23     ` Jens Emil Schulz Ostergaard
2026-03-04 15:34       ` Andrew Lunn
2026-03-05 13:17         ` Jens Emil Schulz Ostergaard
2026-03-03 12:22 ` [PATCH net-next 8/8] net: dsa: lan9645x: add port statistics Jens Emil Schulz Østergaard
2026-03-03 16:01   ` Vladimir Oltean
2026-03-03 20:21     ` Andrew Lunn
2026-03-04 15:51       ` Jens Emil Schulz Ostergaard
2026-03-04 15:50     ` Jens Emil Schulz Ostergaard

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=32b7267f84b60a4632eb643fd0b999d03c630a1d.camel@microchip.com \
    --to=jensemil.schulzostergaard@microchip.com \
    --cc=Steen.Hegelund@microchip.com \
    --cc=UNGLinuxDriver@microchip.com \
    --cc=andrew@lunn.ch \
    --cc=conor+dt@kernel.org \
    --cc=daniel.machon@microchip.com \
    --cc=davem@davemloft.net \
    --cc=devicetree@vger.kernel.org \
    --cc=edumazet@google.com \
    --cc=horms@kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux@armlinux.org.uk \
    --cc=netdev@vger.kernel.org \
    --cc=olteanv@gmail.com \
    --cc=pabeni@redhat.com \
    --cc=robh@kernel.org \
    --cc=woojung.huh@microchip.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox