[net-next,10/14] net: dsa: netc: introduce NXP NETC switch driver for i.MX94

Wei Fang wei.fang at nxp.com
Thu Mar 19 13:48:23 AEDT 2026


> > @@ -0,0 +1,698 @@
> > +static int netc_port_create_emdio_bus(struct netc_port *np,
> > +				      struct device_node *node)
> > +{
> > +	struct netc_switch *priv = np->switch_priv;
> > +	struct enetc_mdio_priv *mdio_priv;
> > +	struct device *dev = priv->dev;
> > +	struct enetc_hw *hw;
> > +	struct mii_bus *bus;
> > +	int err;
> > +
> > +	hw = enetc_hw_alloc(dev, np->iobase);
> > +	if (IS_ERR(hw))
> > +		return dev_err_probe(dev, PTR_ERR(hw),
> > +				     "Failed to allocate enetc_hw\n");
> > +
> > +	bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv));
> > +	if (!bus)
> > +		return -ENOMEM;
> > +
> > +	bus->name = "NXP NETC switch external MDIO Bus";
> > +	bus->read = enetc_mdio_read_c22;
> > +	bus->write = enetc_mdio_write_c22;
> > +	bus->read_c45 = enetc_mdio_read_c45;
> > +	bus->write_c45 = enetc_mdio_write_c45;
> > +	bus->parent = dev;
> > +	mdio_priv = bus->priv;
> > +	mdio_priv->hw = hw;
> > +	mdio_priv->mdio_base = NETC_EMDIO_BASE;
> > +	snprintf(bus->id, MII_BUS_ID_SIZE, "%s-p%d-emdio",
> > +		 dev_name(dev), np->dp->index);
> > +
> > +	err = devm_of_mdiobus_register(dev, bus, node);
> > +	if (err)
> > +		return dev_err_probe(dev, err,
> > +				     "Cannot register EMDIO bus\n");
> > +
> > +	np->emdio = bus;
> > +
> > +	return 0;
> > +}
> > +
> > +static int netc_port_create_mdio_bus(struct netc_port *np,
> > +				     struct device_node *node)
> > +{
> > +	struct device_node *mdio_node;
> > +	int err;
> > +
> > +	mdio_node = of_get_child_by_name(node, "mdio");
> > +	if (mdio_node) {
> > +		err = netc_port_create_emdio_bus(np, mdio_node);
> > +		of_node_put(mdio_node);
> > +		if (err)
> > +			return err;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void netc_port_free_mdio_bus(struct netc_port *np)
> > +{
> > +	if (np->emdio)
> > +		mdiobus_unregister(np->emdio);
>                          ^^^^
> 
> Does this cause a double unregister? The MDIO bus is registered with
> devm_of_mdiobus_register() in netc_port_create_emdio_bus(), which
> installs a devres action that automatically calls mdiobus_unregister()
> during device removal.
> 
> However, netc_port_free_mdio_bus() also explicitly calls
> mdiobus_unregister(np->emdio).
> 
> During device removal:
> 
>   netc_switch_remove()
>     -> dsa_unregister_switch()
>       -> dsa_switch_teardown()
>         -> netc_teardown()
>           -> netc_free_mdio_bus()
>             -> netc_port_free_mdio_bus()
>               -> mdiobus_unregister()  // first call
> 
> Then devres cleanup fires:
> 
>   devm_mdiobus_unregister()
>     -> mdiobus_unregister()  // second call
>       -> WARN_ON_ONCE(bus->state != MDIOBUS_REGISTERED)
> 
> The second call triggers a warning at drivers/net/phy/mdio_bus_provider.c:675
> because the bus state is already MDIOBUS_UNREGISTERED. With
> panic_on_warn=1, this causes a kernel panic.

Good catch, I changed mdiobus_register() to devm_of_mdiobus_register(), but I
forgot to remove mdiobus_unregister(). I will fix this issue.

> 
> The same double-unregister occurs on the error path of
> netc_init_all_ports(), where netc_free_mdio_bus() is called, but devres
> will also fire during device teardown.
> 
> > +}
> 
> [ ... ]
> 
> > +static int netc_switch_pci_init(struct pci_dev *pdev)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +	struct netc_switch_regs *regs;
> > +	struct netc_switch *priv;
> > +	int err;
> > +
> > +	pcie_flr(pdev);
> > +	err = pci_enable_device_mem(pdev);
> > +	if (err)
> > +		return dev_err_probe(dev, err, "Failed to enable device\n");
> > +
> > +	/* The command BD rings and NTMP tables need DMA */
> > +	dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
>                                        ^^^^
> 
> Is the return value being checked? The comment states "command BD rings
> and NTMP tables need DMA", confirming DMA is essential for this driver.

No need to check the return value, because dma_set_mask_and_coherent()
never return fail when DMA_BIT_MASK(64).

See https://elixir.bootlin.com/linux/v7.0-rc4/source/Documentation/core-api/dma-api-howto.rst#L269

> 
> The function can fail with -EIO when dma_supported() returns false for
> the 64-bit mask (from dma_set_mask() in kernel/dma/mapping.c:902).
> 
> The sibling enetc driver from the same NXP NETC subsystem checks this
> return value in enetc.c:
> 
>   err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
>   if (err) {
>       ...
>       goto err_dma;
>   }
> 
> If the 64-bit DMA mask setup fails, the DMA mask remains at the default,
> and subsequent DMA operations for NTMP table management (added in later
> commits) may silently fail or produce incorrect results on systems with
> memory above 4GB.
> 



More information about the Linuxppc-dev mailing list