* [PATCH net-next v3 0/7] net: axienet: Fix deferred probe loop
@ 2025-07-28 22:18 Sean Anderson
2025-07-28 22:18 ` [PATCH net-next v3 1/7] net: axienet: Fix resource release ordering Sean Anderson
` (6 more replies)
0 siblings, 7 replies; 12+ messages in thread
From: Sean Anderson @ 2025-07-28 22:18 UTC (permalink / raw)
To: Radhey Shyam Pandey, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, netdev
Cc: linux-kernel, Greg Kroah-Hartman, Michal Simek, linux-arm-kernel,
Leon Romanovsky, Sean Anderson
Fix a deferred-probe loop by splitting the MAC and MDIO functionality
into separate drivers. Please refer to the last patch for an extended
look at the problem this series is attempting to solve.
This is a rather large fix for a rather rare bug, so I have sent it
for net-next. The first patch is a smaller fix, however, and could go
via net.
Changes in v3:
- Use ioread32/iowrite32 directly in MDIO
- Use MDIO bus device in prints
- Use device variable to probe
- Rework to use a separate axienet_common structure, as netdevs cannot
be reused once registered.
- Use ida_alloc for aux id
Changes in v2:
- Fix building as a module
- Expand commit message with much more info on the problem and possible
solutions
Sean Anderson (7):
net: axienet: Fix resource release ordering
net: axienet: Use ioread32/iowrite32 directly
net: axienet: Use MDIO bus device in prints
net: axienet: Simplify axienet_mdio_setup
net: axienet: Use device variable in probe
net: axienet: Rearrange lifetime functions
net: axienet: Split into MAC and MDIO drivers
drivers/net/ethernet/xilinx/Kconfig | 1 +
drivers/net/ethernet/xilinx/xilinx_axienet.h | 45 +-
.../net/ethernet/xilinx/xilinx_axienet_main.c | 383 +++++++++++-------
.../net/ethernet/xilinx/xilinx_axienet_mdio.c | 158 ++++----
4 files changed, 350 insertions(+), 237 deletions(-)
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH net-next v3 1/7] net: axienet: Fix resource release ordering
2025-07-28 22:18 [PATCH net-next v3 0/7] net: axienet: Fix deferred probe loop Sean Anderson
@ 2025-07-28 22:18 ` Sean Anderson
2025-07-30 9:27 ` Gupta, Suraj
2025-07-28 22:18 ` [PATCH net-next v3 2/7] net: axienet: Use ioread32/iowrite32 directly Sean Anderson
` (5 subsequent siblings)
6 siblings, 1 reply; 12+ messages in thread
From: Sean Anderson @ 2025-07-28 22:18 UTC (permalink / raw)
To: Radhey Shyam Pandey, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, netdev
Cc: linux-kernel, Greg Kroah-Hartman, Michal Simek, linux-arm-kernel,
Leon Romanovsky, Sean Anderson
Device-managed resources are released after manually-managed resources.
Therefore, once any manually-managed resource is acquired, all further
resources must be manually-managed too.
Convert all resources before the MDIO bus is created into device-managed
resources. In all cases but one there are already devm variants available.
Fixes: 46aa27df8853 ("net: axienet: Use devm_* calls")
Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
---
(no changes since v1)
.../net/ethernet/xilinx/xilinx_axienet_main.c | 89 ++++++++-----------
1 file changed, 37 insertions(+), 52 deletions(-)
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index 6011d7eae0c7..1f277e5e4a62 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -2744,6 +2744,11 @@ static void axienet_dma_err_handler(struct work_struct *work)
axienet_setoptions(ndev, lp->options);
}
+static void axienet_disable_misc(void *clocks)
+{
+ clk_bulk_disable_unprepare(XAE_NUM_MISC_CLOCKS, clocks);
+}
+
/**
* axienet_probe - Axi Ethernet probe function.
* @pdev: Pointer to platform device structure.
@@ -2767,7 +2772,7 @@ static int axienet_probe(struct platform_device *pdev)
int addr_width = 32;
u32 value;
- ndev = alloc_etherdev(sizeof(*lp));
+ ndev = devm_alloc_etherdev(&pdev->dev, sizeof(*lp));
if (!ndev)
return -ENOMEM;
@@ -2795,22 +2800,17 @@ static int axienet_probe(struct platform_device *pdev)
seqcount_mutex_init(&lp->hw_stats_seqcount, &lp->stats_lock);
INIT_DEFERRABLE_WORK(&lp->stats_work, axienet_refresh_stats);
- lp->axi_clk = devm_clk_get_optional(&pdev->dev, "s_axi_lite_clk");
+ lp->axi_clk = devm_clk_get_optional_enabled(&pdev->dev,
+ "s_axi_lite_clk");
if (!lp->axi_clk) {
/* For backward compatibility, if named AXI clock is not present,
* treat the first clock specified as the AXI clock.
*/
- lp->axi_clk = devm_clk_get_optional(&pdev->dev, NULL);
- }
- if (IS_ERR(lp->axi_clk)) {
- ret = PTR_ERR(lp->axi_clk);
- goto free_netdev;
- }
- ret = clk_prepare_enable(lp->axi_clk);
- if (ret) {
- dev_err(&pdev->dev, "Unable to enable AXI clock: %d\n", ret);
- goto free_netdev;
+ lp->axi_clk = devm_clk_get_optional_enabled(&pdev->dev, NULL);
}
+ if (IS_ERR(lp->axi_clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(lp->axi_clk),
+ "could not get AXI clock\n");
lp->misc_clks[0].id = "axis_clk";
lp->misc_clks[1].id = "ref_clk";
@@ -2818,18 +2818,23 @@ static int axienet_probe(struct platform_device *pdev)
ret = devm_clk_bulk_get_optional(&pdev->dev, XAE_NUM_MISC_CLOCKS, lp->misc_clks);
if (ret)
- goto cleanup_clk;
+ return dev_err_probe(&pdev->dev, ret,
+ "could not get misc. clocks\n");
ret = clk_bulk_prepare_enable(XAE_NUM_MISC_CLOCKS, lp->misc_clks);
if (ret)
- goto cleanup_clk;
+ return dev_err_probe(&pdev->dev, ret,
+ "could not enable misc. clocks\n");
+
+ ret = devm_add_action_or_reset(&pdev->dev, axienet_disable_misc,
+ lp->misc_clks);
+ if (ret)
+ return ret;
/* Map device registers */
lp->regs = devm_platform_get_and_ioremap_resource(pdev, 0, ðres);
- if (IS_ERR(lp->regs)) {
- ret = PTR_ERR(lp->regs);
- goto cleanup_clk;
- }
+ if (IS_ERR(lp->regs))
+ return PTR_ERR(lp->regs);
lp->regs_start = ethres->start;
/* Setup checksum offload, but default to off if not specified */
@@ -2898,19 +2903,17 @@ static int axienet_probe(struct platform_device *pdev)
lp->phy_mode = PHY_INTERFACE_MODE_1000BASEX;
break;
default:
- ret = -EINVAL;
- goto cleanup_clk;
+ return -EINVAL;
}
} else {
ret = of_get_phy_mode(pdev->dev.of_node, &lp->phy_mode);
if (ret)
- goto cleanup_clk;
+ return ret;
}
if (lp->switch_x_sgmii && lp->phy_mode != PHY_INTERFACE_MODE_SGMII &&
lp->phy_mode != PHY_INTERFACE_MODE_1000BASEX) {
dev_err(&pdev->dev, "xlnx,switch-x-sgmii only supported with SGMII or 1000BaseX\n");
- ret = -EINVAL;
- goto cleanup_clk;
+ return -EINVAL;
}
if (!of_property_present(pdev->dev.of_node, "dmas")) {
@@ -2925,7 +2928,7 @@ static int axienet_probe(struct platform_device *pdev)
dev_err(&pdev->dev,
"unable to get DMA resource\n");
of_node_put(np);
- goto cleanup_clk;
+ return ret;
}
lp->dma_regs = devm_ioremap_resource(&pdev->dev,
&dmares);
@@ -2942,19 +2945,17 @@ static int axienet_probe(struct platform_device *pdev)
}
if (IS_ERR(lp->dma_regs)) {
dev_err(&pdev->dev, "could not map DMA regs\n");
- ret = PTR_ERR(lp->dma_regs);
- goto cleanup_clk;
+ return PTR_ERR(lp->dma_regs);
}
if (lp->rx_irq <= 0 || lp->tx_irq <= 0) {
dev_err(&pdev->dev, "could not determine irqs\n");
- ret = -ENOMEM;
- goto cleanup_clk;
+ return -ENOMEM;
}
/* Reset core now that clocks are enabled, prior to accessing MDIO */
ret = __axienet_device_reset(lp);
if (ret)
- goto cleanup_clk;
+ return ret;
/* Autodetect the need for 64-bit DMA pointers.
* When the IP is configured for a bus width bigger than 32 bits,
@@ -2981,14 +2982,13 @@ static int axienet_probe(struct platform_device *pdev)
}
if (!IS_ENABLED(CONFIG_64BIT) && lp->features & XAE_FEATURE_DMA_64BIT) {
dev_err(&pdev->dev, "64-bit addressable DMA is not compatible with 32-bit architecture\n");
- ret = -EINVAL;
- goto cleanup_clk;
+ return -EINVAL;
}
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(addr_width));
if (ret) {
dev_err(&pdev->dev, "No suitable DMA available\n");
- goto cleanup_clk;
+ return ret;
}
netif_napi_add(ndev, &lp->napi_rx, axienet_rx_poll);
netif_napi_add(ndev, &lp->napi_tx, axienet_tx_poll);
@@ -2998,15 +2998,12 @@ static int axienet_probe(struct platform_device *pdev)
lp->eth_irq = platform_get_irq_optional(pdev, 0);
if (lp->eth_irq < 0 && lp->eth_irq != -ENXIO) {
- ret = lp->eth_irq;
- goto cleanup_clk;
+ return lp->eth_irq;
}
tx_chan = dma_request_chan(lp->dev, "tx_chan0");
- if (IS_ERR(tx_chan)) {
- ret = PTR_ERR(tx_chan);
- dev_err_probe(lp->dev, ret, "No Ethernet DMA (TX) channel found\n");
- goto cleanup_clk;
- }
+ if (IS_ERR(tx_chan))
+ return dev_err_probe(lp->dev, PTR_ERR(tx_chan),
+ "No Ethernet DMA (TX) channel found\n");
cfg.reset = 1;
/* As name says VDMA but it has support for DMA channel reset */
@@ -3014,7 +3011,7 @@ static int axienet_probe(struct platform_device *pdev)
if (ret < 0) {
dev_err(&pdev->dev, "Reset channel failed\n");
dma_release_channel(tx_chan);
- goto cleanup_clk;
+ return ret;
}
dma_release_channel(tx_chan);
@@ -3119,13 +3116,6 @@ static int axienet_probe(struct platform_device *pdev)
put_device(&lp->pcs_phy->dev);
if (lp->mii_bus)
axienet_mdio_teardown(lp);
-cleanup_clk:
- clk_bulk_disable_unprepare(XAE_NUM_MISC_CLOCKS, lp->misc_clks);
- clk_disable_unprepare(lp->axi_clk);
-
-free_netdev:
- free_netdev(ndev);
-
return ret;
}
@@ -3143,11 +3133,6 @@ static void axienet_remove(struct platform_device *pdev)
put_device(&lp->pcs_phy->dev);
axienet_mdio_teardown(lp);
-
- clk_bulk_disable_unprepare(XAE_NUM_MISC_CLOCKS, lp->misc_clks);
- clk_disable_unprepare(lp->axi_clk);
-
- free_netdev(ndev);
}
static void axienet_shutdown(struct platform_device *pdev)
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH net-next v3 2/7] net: axienet: Use ioread32/iowrite32 directly
2025-07-28 22:18 [PATCH net-next v3 0/7] net: axienet: Fix deferred probe loop Sean Anderson
2025-07-28 22:18 ` [PATCH net-next v3 1/7] net: axienet: Fix resource release ordering Sean Anderson
@ 2025-07-28 22:18 ` Sean Anderson
2025-07-29 4:28 ` Subbaraya Sundeep
2025-07-28 22:18 ` [PATCH net-next v3 3/7] net: axienet: Use MDIO bus device in prints Sean Anderson
` (4 subsequent siblings)
6 siblings, 1 reply; 12+ messages in thread
From: Sean Anderson @ 2025-07-28 22:18 UTC (permalink / raw)
To: Radhey Shyam Pandey, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, netdev
Cc: linux-kernel, Greg Kroah-Hartman, Michal Simek, linux-arm-kernel,
Leon Romanovsky, Sean Anderson
In preparation for splitting the MDIO bus into a separate driver,
convert all register reads/writes to use ioread32/iowrite32 directly
instead of using the axienet_ior/iow helpers. While we're at it, clean
up the register calculations a bit.
Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
---
Changes in v3:
- New
.../net/ethernet/xilinx/xilinx_axienet_mdio.c | 43 +++++++++----------
1 file changed, 20 insertions(+), 23 deletions(-)
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
index 9ca2643c921e..16f3581390dd 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
@@ -32,7 +32,7 @@ static int axienet_mdio_wait_until_ready(struct axienet_local *lp)
{
u32 val;
- return readx_poll_timeout(axinet_ior_read_mcr, lp,
+ return readx_poll_timeout(ioread32, lp->regs + XAE_MDIO_MCR_OFFSET,
val, val & XAE_MDIO_MCR_READY_MASK,
1, 20000);
}
@@ -45,8 +45,8 @@ static int axienet_mdio_wait_until_ready(struct axienet_local *lp)
*/
static void axienet_mdio_mdc_enable(struct axienet_local *lp)
{
- axienet_iow(lp, XAE_MDIO_MC_OFFSET,
- ((u32)lp->mii_clk_div | XAE_MDIO_MC_MDIOEN_MASK));
+ iowrite32((u32)lp->mii_clk_div | XAE_MDIO_MC_MDIOEN_MASK,
+ lp->regs + XAE_MDIO_MC_OFFSET);
}
/**
@@ -59,9 +59,9 @@ static void axienet_mdio_mdc_disable(struct axienet_local *lp)
{
u32 mc_reg;
- mc_reg = axienet_ior(lp, XAE_MDIO_MC_OFFSET);
- axienet_iow(lp, XAE_MDIO_MC_OFFSET,
- (mc_reg & ~XAE_MDIO_MC_MDIOEN_MASK));
+ mc_reg = ioread32(lp->regs + XAE_MDIO_MC_OFFSET);
+ iowrite32(mc_reg & ~XAE_MDIO_MC_MDIOEN_MASK,
+ lp->regs + XAE_MDIO_MC_OFFSET);
}
/**
@@ -90,13 +90,11 @@ static int axienet_mdio_read(struct mii_bus *bus, int phy_id, int reg)
return ret;
}
- axienet_iow(lp, XAE_MDIO_MCR_OFFSET,
- (((phy_id << XAE_MDIO_MCR_PHYAD_SHIFT) &
- XAE_MDIO_MCR_PHYAD_MASK) |
- ((reg << XAE_MDIO_MCR_REGAD_SHIFT) &
- XAE_MDIO_MCR_REGAD_MASK) |
- XAE_MDIO_MCR_INITIATE_MASK |
- XAE_MDIO_MCR_OP_READ_MASK));
+ rc = FIELD_PREP(XAE_MDIO_MCR_PHYAD_MASK, phy_id) |
+ FIELD_PREP(XAE_MDIO_MCR_REGAD_MASK, reg) |
+ XAE_MDIO_MCR_INITIATE_MASK |
+ XAE_MDIO_MCR_OP_READ_MASK;
+ iowrite32(rc, lp->regs + XAE_MDIO_MCR_OFFSET);
ret = axienet_mdio_wait_until_ready(lp);
if (ret < 0) {
@@ -104,7 +102,7 @@ static int axienet_mdio_read(struct mii_bus *bus, int phy_id, int reg)
return ret;
}
- rc = axienet_ior(lp, XAE_MDIO_MRD_OFFSET) & 0x0000FFFF;
+ rc = ioread32(lp->regs + XAE_MDIO_MRD_OFFSET) & 0x0000FFFF;
dev_dbg(lp->dev, "axienet_mdio_read(phy_id=%i, reg=%x) == %x\n",
phy_id, reg, rc);
@@ -129,8 +127,9 @@ static int axienet_mdio_read(struct mii_bus *bus, int phy_id, int reg)
static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg,
u16 val)
{
- int ret;
struct axienet_local *lp = bus->priv;
+ int ret;
+ u32 mcr;
dev_dbg(lp->dev, "axienet_mdio_write(phy_id=%i, reg=%x, val=%x)\n",
phy_id, reg, val);
@@ -143,14 +142,12 @@ static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg,
return ret;
}
- axienet_iow(lp, XAE_MDIO_MWD_OFFSET, (u32)val);
- axienet_iow(lp, XAE_MDIO_MCR_OFFSET,
- (((phy_id << XAE_MDIO_MCR_PHYAD_SHIFT) &
- XAE_MDIO_MCR_PHYAD_MASK) |
- ((reg << XAE_MDIO_MCR_REGAD_SHIFT) &
- XAE_MDIO_MCR_REGAD_MASK) |
- XAE_MDIO_MCR_INITIATE_MASK |
- XAE_MDIO_MCR_OP_WRITE_MASK));
+ iowrite32(val, lp->regs + XAE_MDIO_MWD_OFFSET);
+ mcr = FIELD_PREP(XAE_MDIO_MCR_PHYAD_MASK, phy_id) |
+ FIELD_PREP(XAE_MDIO_MCR_REGAD_MASK, reg) |
+ XAE_MDIO_MCR_INITIATE_MASK |
+ XAE_MDIO_MCR_OP_WRITE_MASK;
+ iowrite32(mcr, lp->regs + XAE_MDIO_MCR_OFFSET);
ret = axienet_mdio_wait_until_ready(lp);
if (ret < 0) {
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH net-next v3 3/7] net: axienet: Use MDIO bus device in prints
2025-07-28 22:18 [PATCH net-next v3 0/7] net: axienet: Fix deferred probe loop Sean Anderson
2025-07-28 22:18 ` [PATCH net-next v3 1/7] net: axienet: Fix resource release ordering Sean Anderson
2025-07-28 22:18 ` [PATCH net-next v3 2/7] net: axienet: Use ioread32/iowrite32 directly Sean Anderson
@ 2025-07-28 22:18 ` Sean Anderson
2025-07-29 9:07 ` kernel test robot
2025-07-28 22:18 ` [PATCH net-next v3 4/7] net: axienet: Simplify axienet_mdio_setup Sean Anderson
` (3 subsequent siblings)
6 siblings, 1 reply; 12+ messages in thread
From: Sean Anderson @ 2025-07-28 22:18 UTC (permalink / raw)
To: Radhey Shyam Pandey, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, netdev
Cc: linux-kernel, Greg Kroah-Hartman, Michal Simek, linux-arm-kernel,
Leon Romanovsky, Sean Anderson
For clarity and to remove the dependency on the parent netdev, use the
MDIO bus device in print statements.
Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
---
Changes in v3:
- New
.../net/ethernet/xilinx/xilinx_axienet_mdio.c | 34 +++++++++++--------
1 file changed, 19 insertions(+), 15 deletions(-)
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
index 16f3581390dd..cacd5590731d 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
@@ -104,7 +104,7 @@ static int axienet_mdio_read(struct mii_bus *bus, int phy_id, int reg)
rc = ioread32(lp->regs + XAE_MDIO_MRD_OFFSET) & 0x0000FFFF;
- dev_dbg(lp->dev, "axienet_mdio_read(phy_id=%i, reg=%x) == %x\n",
+ dev_dbg(&bus->dev, "%s(phy_id=%i, reg=%x) == %x\n", __func__,
phy_id, reg, rc);
axienet_mdio_mdc_disable(lp);
@@ -131,7 +131,7 @@ static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg,
int ret;
u32 mcr;
- dev_dbg(lp->dev, "axienet_mdio_write(phy_id=%i, reg=%x, val=%x)\n",
+ dev_dbg(&bus->dev, "%s(phy_id=%i, reg=%x, val=%x)\n", __func__,
phy_id, reg, val);
axienet_mdio_mdc_enable(lp);
@@ -169,8 +169,9 @@ static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg,
* Sets up the MDIO interface by initializing the MDIO clock and enabling the
* MDIO interface in hardware.
**/
-static int axienet_mdio_enable(struct axienet_local *lp, struct device_node *np)
+static int axienet_mdio_enable(struct mii_bus *bus, struct device_node *np)
{
+ struct axienet_local *lp = bus->priv;
u32 mdio_freq = DEFAULT_MDIO_FREQ;
u32 host_clock;
u32 clk_div;
@@ -186,28 +187,31 @@ static int axienet_mdio_enable(struct axienet_local *lp, struct device_node *np)
/* Legacy fallback: detect CPU clock frequency and use as AXI
* bus clock frequency. This only works on certain platforms.
*/
- np1 = of_find_node_by_name(NULL, "cpu");
+ np1 = of_find_node_by_name(NULL, "lpu");
if (!np1) {
- netdev_warn(lp->ndev, "Could not find CPU device node.\n");
+ dev_warn(&bus->dev,
+ "Could not find CPU device node.\n");
host_clock = DEFAULT_HOST_CLOCK;
} else {
int ret = of_property_read_u32(np1, "clock-frequency",
&host_clock);
if (ret) {
- netdev_warn(lp->ndev, "CPU clock-frequency property not found.\n");
+ dev_warn(&bus->dev,
+ "CPU clock-frequency property not found.\n");
host_clock = DEFAULT_HOST_CLOCK;
}
of_node_put(np1);
}
- netdev_info(lp->ndev, "Setting assumed host clock to %u\n",
- host_clock);
+ dev_info(&bus->dev,
+ "Setting assumed host clock to %u\n", host_clock);
}
if (np)
of_property_read_u32(np, "clock-frequency", &mdio_freq);
if (mdio_freq != DEFAULT_MDIO_FREQ)
- netdev_info(lp->ndev, "Setting non-standard mdio bus frequency to %u Hz\n",
- mdio_freq);
+ dev_info(&bus->dev,
+ "Setting non-standard mdio bus frequency to %u Hz\n",
+ mdio_freq);
/* clk_div can be calculated by deriving it from the equation:
* fMDIO = fHOST / ((1 + clk_div) * 2)
@@ -245,14 +249,14 @@ static int axienet_mdio_enable(struct axienet_local *lp, struct device_node *np)
/* Check for overflow of mii_clk_div */
if (clk_div & ~XAE_MDIO_MC_CLOCK_DIVIDE_MAX) {
- netdev_warn(lp->ndev, "MDIO clock divisor overflow\n");
+ dev_warn(&bus->dev, "MDIO clock divisor overflow\n");
return -EOVERFLOW;
}
lp->mii_clk_div = (u8)clk_div;
- netdev_dbg(lp->ndev,
- "Setting MDIO clock divisor to %u/%u Hz host clock.\n",
- lp->mii_clk_div, host_clock);
+ dev_dbg(&bus->dev,
+ "Setting MDIO clock divisor to %u/%u Hz host clock.\n",
+ lp->mii_clk_div, host_clock);
axienet_mdio_mdc_enable(lp);
@@ -295,7 +299,7 @@ int axienet_mdio_setup(struct axienet_local *lp)
lp->mii_bus = bus;
mdio_node = of_get_child_by_name(lp->dev->of_node, "mdio");
- ret = axienet_mdio_enable(lp, mdio_node);
+ ret = axienet_mdio_enable(bus, mdio_node);
if (ret < 0)
goto unregister;
ret = of_mdiobus_register(bus, mdio_node);
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH net-next v3 4/7] net: axienet: Simplify axienet_mdio_setup
2025-07-28 22:18 [PATCH net-next v3 0/7] net: axienet: Fix deferred probe loop Sean Anderson
` (2 preceding siblings ...)
2025-07-28 22:18 ` [PATCH net-next v3 3/7] net: axienet: Use MDIO bus device in prints Sean Anderson
@ 2025-07-28 22:18 ` Sean Anderson
2025-07-28 22:18 ` [PATCH net-next v3 5/7] net: axienet: Use device variable in probe Sean Anderson
` (2 subsequent siblings)
6 siblings, 0 replies; 12+ messages in thread
From: Sean Anderson @ 2025-07-28 22:18 UTC (permalink / raw)
To: Radhey Shyam Pandey, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, netdev
Cc: linux-kernel, Greg Kroah-Hartman, Michal Simek, linux-arm-kernel,
Leon Romanovsky, Sean Anderson
We always put the mdio_node and disable the bus after probing, so
perform these steps unconditionally.
Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
---
(no changes since v1)
drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c | 15 +++++----------
1 file changed, 5 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
index cacd5590731d..2799d168ed9d 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
@@ -302,19 +302,14 @@ int axienet_mdio_setup(struct axienet_local *lp)
ret = axienet_mdio_enable(bus, mdio_node);
if (ret < 0)
goto unregister;
+
ret = of_mdiobus_register(bus, mdio_node);
- if (ret)
- goto unregister_mdio_enabled;
of_node_put(mdio_node);
axienet_mdio_mdc_disable(lp);
- return 0;
-
-unregister_mdio_enabled:
- axienet_mdio_mdc_disable(lp);
-unregister:
- of_node_put(mdio_node);
- mdiobus_free(bus);
- lp->mii_bus = NULL;
+ if (ret) {
+ mdiobus_free(bus);
+ lp->mii_bus = NULL;
+ }
return ret;
}
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH net-next v3 5/7] net: axienet: Use device variable in probe
2025-07-28 22:18 [PATCH net-next v3 0/7] net: axienet: Fix deferred probe loop Sean Anderson
` (3 preceding siblings ...)
2025-07-28 22:18 ` [PATCH net-next v3 4/7] net: axienet: Simplify axienet_mdio_setup Sean Anderson
@ 2025-07-28 22:18 ` Sean Anderson
2025-07-28 22:18 ` [PATCH net-next v3 6/7] net: axienet: Rearrange lifetime functions Sean Anderson
2025-07-28 22:18 ` [PATCH net-next v3 7/7] net: axienet: Split into MAC and MDIO drivers Sean Anderson
6 siblings, 0 replies; 12+ messages in thread
From: Sean Anderson @ 2025-07-28 22:18 UTC (permalink / raw)
To: Radhey Shyam Pandey, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, netdev
Cc: linux-kernel, Greg Kroah-Hartman, Michal Simek, linux-arm-kernel,
Leon Romanovsky, Sean Anderson
There are a lot of references to pdev->dev that we can shorten by using
a dev variable.
Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
---
Changes in v3:
- New
.../net/ethernet/xilinx/xilinx_axienet_main.c | 83 +++++++++----------
1 file changed, 41 insertions(+), 42 deletions(-)
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index 1f277e5e4a62..28927c7c6c41 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -2764,6 +2764,7 @@ static void axienet_disable_misc(void *clocks)
static int axienet_probe(struct platform_device *pdev)
{
int ret;
+ struct device *dev = &pdev->dev;
struct device_node *np;
struct axienet_local *lp;
struct net_device *ndev;
@@ -2772,13 +2773,13 @@ static int axienet_probe(struct platform_device *pdev)
int addr_width = 32;
u32 value;
- ndev = devm_alloc_etherdev(&pdev->dev, sizeof(*lp));
+ ndev = devm_alloc_etherdev(dev, sizeof(*lp));
if (!ndev)
return -ENOMEM;
platform_set_drvdata(pdev, ndev);
- SET_NETDEV_DEV(ndev, &pdev->dev);
+ SET_NETDEV_DEV(ndev, dev);
ndev->features = NETIF_F_SG;
ndev->ethtool_ops = &axienet_ethtool_ops;
@@ -2788,7 +2789,7 @@ static int axienet_probe(struct platform_device *pdev)
lp = netdev_priv(ndev);
lp->ndev = ndev;
- lp->dev = &pdev->dev;
+ lp->dev = dev;
lp->options = XAE_OPTION_DEFAULTS;
lp->rx_bd_num = RX_BD_NUM_DEFAULT;
lp->tx_bd_num = TX_BD_NUM_DEFAULT;
@@ -2800,33 +2801,32 @@ static int axienet_probe(struct platform_device *pdev)
seqcount_mutex_init(&lp->hw_stats_seqcount, &lp->stats_lock);
INIT_DEFERRABLE_WORK(&lp->stats_work, axienet_refresh_stats);
- lp->axi_clk = devm_clk_get_optional_enabled(&pdev->dev,
- "s_axi_lite_clk");
+ lp->axi_clk = devm_clk_get_optional_enabled(dev, "s_axi_lite_clk");
if (!lp->axi_clk) {
/* For backward compatibility, if named AXI clock is not present,
* treat the first clock specified as the AXI clock.
*/
- lp->axi_clk = devm_clk_get_optional_enabled(&pdev->dev, NULL);
+ lp->axi_clk = devm_clk_get_optional_enabled(dev, NULL);
}
if (IS_ERR(lp->axi_clk))
- return dev_err_probe(&pdev->dev, PTR_ERR(lp->axi_clk),
+ return dev_err_probe(dev, PTR_ERR(lp->axi_clk),
"could not get AXI clock\n");
lp->misc_clks[0].id = "axis_clk";
lp->misc_clks[1].id = "ref_clk";
lp->misc_clks[2].id = "mgt_clk";
- ret = devm_clk_bulk_get_optional(&pdev->dev, XAE_NUM_MISC_CLOCKS, lp->misc_clks);
+ ret = devm_clk_bulk_get_optional(dev, XAE_NUM_MISC_CLOCKS, lp->misc_clks);
if (ret)
- return dev_err_probe(&pdev->dev, ret,
+ return dev_err_probe(dev, ret,
"could not get misc. clocks\n");
ret = clk_bulk_prepare_enable(XAE_NUM_MISC_CLOCKS, lp->misc_clks);
if (ret)
- return dev_err_probe(&pdev->dev, ret,
+ return dev_err_probe(dev, ret,
"could not enable misc. clocks\n");
- ret = devm_add_action_or_reset(&pdev->dev, axienet_disable_misc,
+ ret = devm_add_action_or_reset(dev, axienet_disable_misc,
lp->misc_clks);
if (ret)
return ret;
@@ -2843,7 +2843,7 @@ static int axienet_probe(struct platform_device *pdev)
if (axienet_ior(lp, XAE_ABILITY_OFFSET) & XAE_ABILITY_STATS)
lp->features |= XAE_FEATURE_STATS;
- ret = of_property_read_u32(pdev->dev.of_node, "xlnx,txcsum", &value);
+ ret = of_property_read_u32(dev->of_node, "xlnx,txcsum", &value);
if (!ret) {
switch (value) {
case 1:
@@ -2858,7 +2858,7 @@ static int axienet_probe(struct platform_device *pdev)
break;
}
}
- ret = of_property_read_u32(pdev->dev.of_node, "xlnx,rxcsum", &value);
+ ret = of_property_read_u32(dev->of_node, "xlnx,rxcsum", &value);
if (!ret) {
switch (value) {
case 1:
@@ -2877,13 +2877,13 @@ static int axienet_probe(struct platform_device *pdev)
* Here we check for memory allocated for Rx/Tx in the hardware from
* the device-tree and accordingly set flags.
*/
- of_property_read_u32(pdev->dev.of_node, "xlnx,rxmem", &lp->rxmem);
+ of_property_read_u32(dev->of_node, "xlnx,rxmem", &lp->rxmem);
- lp->switch_x_sgmii = of_property_read_bool(pdev->dev.of_node,
+ lp->switch_x_sgmii = of_property_read_bool(dev->of_node,
"xlnx,switch-x-sgmii");
/* Start with the proprietary, and broken phy_type */
- ret = of_property_read_u32(pdev->dev.of_node, "xlnx,phy-type", &value);
+ ret = of_property_read_u32(dev->of_node, "xlnx,phy-type", &value);
if (!ret) {
netdev_warn(ndev, "Please upgrade your device tree binary blob to use phy-mode");
switch (value) {
@@ -2906,32 +2906,31 @@ static int axienet_probe(struct platform_device *pdev)
return -EINVAL;
}
} else {
- ret = of_get_phy_mode(pdev->dev.of_node, &lp->phy_mode);
+ ret = of_get_phy_mode(dev->of_node, &lp->phy_mode);
if (ret)
return ret;
}
if (lp->switch_x_sgmii && lp->phy_mode != PHY_INTERFACE_MODE_SGMII &&
lp->phy_mode != PHY_INTERFACE_MODE_1000BASEX) {
- dev_err(&pdev->dev, "xlnx,switch-x-sgmii only supported with SGMII or 1000BaseX\n");
+ dev_err(dev, "xlnx,switch-x-sgmii only supported with SGMII or 1000BaseX\n");
return -EINVAL;
}
- if (!of_property_present(pdev->dev.of_node, "dmas")) {
+ if (!of_property_present(dev->of_node, "dmas")) {
/* Find the DMA node, map the DMA registers, and decode the DMA IRQs */
- np = of_parse_phandle(pdev->dev.of_node, "axistream-connected", 0);
+ np = of_parse_phandle(dev->of_node, "axistream-connected", 0);
if (np) {
struct resource dmares;
ret = of_address_to_resource(np, 0, &dmares);
if (ret) {
- dev_err(&pdev->dev,
+ dev_err(dev,
"unable to get DMA resource\n");
of_node_put(np);
return ret;
}
- lp->dma_regs = devm_ioremap_resource(&pdev->dev,
- &dmares);
+ lp->dma_regs = devm_ioremap_resource(dev, &dmares);
lp->rx_irq = irq_of_parse_and_map(np, 1);
lp->tx_irq = irq_of_parse_and_map(np, 0);
of_node_put(np);
@@ -2944,11 +2943,11 @@ static int axienet_probe(struct platform_device *pdev)
lp->eth_irq = platform_get_irq_optional(pdev, 2);
}
if (IS_ERR(lp->dma_regs)) {
- dev_err(&pdev->dev, "could not map DMA regs\n");
+ dev_err(dev, "could not map DMA regs\n");
return PTR_ERR(lp->dma_regs);
}
if (lp->rx_irq <= 0 || lp->tx_irq <= 0) {
- dev_err(&pdev->dev, "could not determine irqs\n");
+ dev_err(dev, "could not determine irqs\n");
return -ENOMEM;
}
@@ -2974,20 +2973,20 @@ static int axienet_probe(struct platform_device *pdev)
if (ioread32(desc) > 0) {
lp->features |= XAE_FEATURE_DMA_64BIT;
addr_width = 64;
- dev_info(&pdev->dev,
+ dev_info(dev,
"autodetected 64-bit DMA range\n");
}
iowrite32(0x0, desc);
}
}
if (!IS_ENABLED(CONFIG_64BIT) && lp->features & XAE_FEATURE_DMA_64BIT) {
- dev_err(&pdev->dev, "64-bit addressable DMA is not compatible with 32-bit architecture\n");
+ dev_err(dev, "64-bit addressable DMA is not compatible with 32-bit architecture\n");
return -EINVAL;
}
- ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(addr_width));
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(addr_width));
if (ret) {
- dev_err(&pdev->dev, "No suitable DMA available\n");
+ dev_err(dev, "No suitable DMA available\n");
return ret;
}
netif_napi_add(ndev, &lp->napi_rx, axienet_rx_poll);
@@ -3000,16 +2999,16 @@ static int axienet_probe(struct platform_device *pdev)
if (lp->eth_irq < 0 && lp->eth_irq != -ENXIO) {
return lp->eth_irq;
}
- tx_chan = dma_request_chan(lp->dev, "tx_chan0");
+ tx_chan = dma_request_chan(dev, "tx_chan0");
if (IS_ERR(tx_chan))
- return dev_err_probe(lp->dev, PTR_ERR(tx_chan),
+ return dev_err_probe(dev, PTR_ERR(tx_chan),
"No Ethernet DMA (TX) channel found\n");
cfg.reset = 1;
/* As name says VDMA but it has support for DMA channel reset */
ret = xilinx_vdma_channel_set_config(tx_chan, &cfg);
if (ret < 0) {
- dev_err(&pdev->dev, "Reset channel failed\n");
+ dev_err(dev, "Reset channel failed\n");
dma_release_channel(tx_chan);
return ret;
}
@@ -3024,14 +3023,14 @@ static int axienet_probe(struct platform_device *pdev)
ndev->netdev_ops = &axienet_netdev_ops;
/* Check for Ethernet core IRQ (optional) */
if (lp->eth_irq <= 0)
- dev_info(&pdev->dev, "Ethernet core IRQ not defined\n");
+ dev_info(dev, "Ethernet core IRQ not defined\n");
/* Retrieve the MAC address */
- ret = of_get_mac_address(pdev->dev.of_node, mac_addr);
+ ret = of_get_mac_address(dev->of_node, mac_addr);
if (!ret) {
axienet_set_mac_address(ndev, mac_addr);
} else {
- dev_warn(&pdev->dev, "could not find MAC address property: %d\n",
+ dev_warn(dev, "could not find MAC address property: %d\n",
ret);
axienet_set_mac_address(ndev, NULL);
}
@@ -3048,21 +3047,21 @@ static int axienet_probe(struct platform_device *pdev)
ret = axienet_mdio_setup(lp);
if (ret)
- dev_warn(&pdev->dev,
+ dev_warn(dev,
"error registering MDIO bus: %d\n", ret);
if (lp->phy_mode == PHY_INTERFACE_MODE_SGMII ||
lp->phy_mode == PHY_INTERFACE_MODE_1000BASEX) {
- np = of_parse_phandle(pdev->dev.of_node, "pcs-handle", 0);
+ np = of_parse_phandle(dev->of_node, "pcs-handle", 0);
if (!np) {
/* Deprecated: Always use "pcs-handle" for pcs_phy.
* Falling back to "phy-handle" here is only for
* backward compatibility with old device trees.
*/
- np = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0);
+ np = of_parse_phandle(dev->of_node, "phy-handle", 0);
}
if (!np) {
- dev_err(&pdev->dev, "pcs-handle (preferred) or phy-handle required for 1000BaseX/SGMII\n");
+ dev_err(dev, "pcs-handle (preferred) or phy-handle required for 1000BaseX/SGMII\n");
ret = -EINVAL;
goto cleanup_mdio;
}
@@ -3091,18 +3090,18 @@ static int axienet_probe(struct platform_device *pdev)
lp->phylink_config.supported_interfaces);
}
- lp->phylink = phylink_create(&lp->phylink_config, pdev->dev.fwnode,
+ lp->phylink = phylink_create(&lp->phylink_config, dev->fwnode,
lp->phy_mode,
&axienet_phylink_ops);
if (IS_ERR(lp->phylink)) {
ret = PTR_ERR(lp->phylink);
- dev_err(&pdev->dev, "phylink_create error (%i)\n", ret);
+ dev_err(dev, "phylink_create error (%i)\n", ret);
goto cleanup_mdio;
}
ret = register_netdev(lp->ndev);
if (ret) {
- dev_err(lp->dev, "register_netdev() error (%i)\n", ret);
+ dev_err(dev, "register_netdev() error (%i)\n", ret);
goto cleanup_phylink;
}
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH net-next v3 6/7] net: axienet: Rearrange lifetime functions
2025-07-28 22:18 [PATCH net-next v3 0/7] net: axienet: Fix deferred probe loop Sean Anderson
` (4 preceding siblings ...)
2025-07-28 22:18 ` [PATCH net-next v3 5/7] net: axienet: Use device variable in probe Sean Anderson
@ 2025-07-28 22:18 ` Sean Anderson
2025-07-28 22:18 ` [PATCH net-next v3 7/7] net: axienet: Split into MAC and MDIO drivers Sean Anderson
6 siblings, 0 replies; 12+ messages in thread
From: Sean Anderson @ 2025-07-28 22:18 UTC (permalink / raw)
To: Radhey Shyam Pandey, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, netdev
Cc: linux-kernel, Greg Kroah-Hartman, Michal Simek, linux-arm-kernel,
Leon Romanovsky, Sean Anderson
Rearrange the lifetime functions (probe, remove, etc.) in preparation
for the next commit. No functional change intended.
Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
---
Changes in v3:
- Rework to use a separate axienet_common structure
drivers/net/ethernet/xilinx/xilinx_axienet.h | 41 ++++--
.../net/ethernet/xilinx/xilinx_axienet_main.c | 135 ++++++++++--------
.../net/ethernet/xilinx/xilinx_axienet_mdio.c | 45 +++---
3 files changed, 127 insertions(+), 94 deletions(-)
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h
index 5ff742103beb..d7215dd92ce9 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet.h
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h
@@ -467,6 +467,29 @@ struct skbuf_dma_descriptor {
int sg_len;
};
+/**
+ * struct axienet_common - axienet private common data
+ * @pdev: Pointer to common platform device structure
+ * @axi_clk: AXI4-Lite bus clock
+ * @reset_lock: Lock held while resetting the device to prevent register access
+ * @mii_bus: Pointer to MII bus structure
+ * @mii_clk_div: MII bus clock divider value
+ * @regs_start: Resource start for axienet device addresses
+ * @regs: Base address for the axienet_local device address space
+ */
+struct axienet_common {
+ struct platform_device *pdev;
+
+ struct clk *axi_clk;
+
+ struct mutex reset_lock;
+ struct mii_bus *mii_bus;
+ u8 mii_clk_div;
+
+ void __iomem *regs;
+ resource_size_t regs_start;
+};
+
/**
* struct axienet_local - axienet private per device data
* @ndev: Pointer for net_device to which it will be attached.
@@ -549,6 +572,7 @@ struct skbuf_dma_descriptor {
struct axienet_local {
struct net_device *ndev;
struct device *dev;
+ struct axienet_common *cp;
struct phylink *phylink;
struct phylink_config phylink_config;
@@ -558,13 +582,11 @@ struct axienet_local {
bool switch_x_sgmii;
- struct clk *axi_clk;
struct clk_bulk_data misc_clks[XAE_NUM_MISC_CLOCKS];
struct mii_bus *mii_bus;
u8 mii_clk_div;
- resource_size_t regs_start;
void __iomem *regs;
void __iomem *dma_regs;
@@ -654,21 +676,14 @@ static inline u32 axienet_ior(struct axienet_local *lp, off_t offset)
return ioread32(lp->regs + offset);
}
-static inline u32 axinet_ior_read_mcr(struct axienet_local *lp)
-{
- return axienet_ior(lp, XAE_MDIO_MCR_OFFSET);
-}
-
static inline void axienet_lock_mii(struct axienet_local *lp)
{
- if (lp->mii_bus)
- mutex_lock(&lp->mii_bus->mdio_lock);
+ mutex_lock(&lp->cp->reset_lock);
}
static inline void axienet_unlock_mii(struct axienet_local *lp)
{
- if (lp->mii_bus)
- mutex_unlock(&lp->mii_bus->mdio_lock);
+ mutex_unlock(&lp->cp->reset_lock);
}
/**
@@ -738,7 +753,7 @@ static inline void axienet_dma_out_addr(struct axienet_local *lp, off_t reg,
#endif /* CONFIG_64BIT */
/* Function prototypes visible in xilinx_axienet_mdio.c for other files */
-int axienet_mdio_setup(struct axienet_local *lp);
-void axienet_mdio_teardown(struct axienet_local *lp);
+int axienet_mdio_setup(struct axienet_common *lp);
+void axienet_mdio_teardown(struct axienet_common *lp);
#endif /* XILINX_AXI_ENET_H */
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index 28927c7c6c41..f235ef15187c 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -225,8 +225,8 @@ static void axienet_dma_bd_release(struct net_device *ndev)
static u64 axienet_dma_rate(struct axienet_local *lp)
{
- if (lp->axi_clk)
- return clk_get_rate(lp->axi_clk);
+ if (lp->cp->axi_clk)
+ return clk_get_rate(lp->cp->axi_clk);
return 125000000; /* arbitrary guess if no clock rate set */
}
@@ -2749,29 +2749,17 @@ static void axienet_disable_misc(void *clocks)
clk_bulk_disable_unprepare(XAE_NUM_MISC_CLOCKS, clocks);
}
-/**
- * axienet_probe - Axi Ethernet probe function.
- * @pdev: Pointer to platform device structure.
- *
- * Return: 0, on success
- * Non-zero error value on failure.
- *
- * This is the probe routine for Axi Ethernet driver. This is called before
- * any other driver routines are invoked. It allocates and sets up the Ethernet
- * device. Parses through device tree and populates fields of
- * axienet_local. It registers the Ethernet device.
- */
-static int axienet_probe(struct platform_device *pdev)
+static int axienet_mac_probe(struct axienet_common *cp)
{
- int ret;
+ struct platform_device *pdev = cp->pdev;
struct device *dev = &pdev->dev;
- struct device_node *np;
struct axienet_local *lp;
struct net_device *ndev;
- struct resource *ethres;
+ struct device_node *np;
u8 mac_addr[ETH_ALEN];
int addr_width = 32;
u32 value;
+ int ret;
ndev = devm_alloc_etherdev(dev, sizeof(*lp));
if (!ndev)
@@ -2790,6 +2778,8 @@ static int axienet_probe(struct platform_device *pdev)
lp = netdev_priv(ndev);
lp->ndev = ndev;
lp->dev = dev;
+ lp->cp = cp;
+ lp->regs = cp->regs;
lp->options = XAE_OPTION_DEFAULTS;
lp->rx_bd_num = RX_BD_NUM_DEFAULT;
lp->tx_bd_num = TX_BD_NUM_DEFAULT;
@@ -2801,17 +2791,6 @@ static int axienet_probe(struct platform_device *pdev)
seqcount_mutex_init(&lp->hw_stats_seqcount, &lp->stats_lock);
INIT_DEFERRABLE_WORK(&lp->stats_work, axienet_refresh_stats);
- lp->axi_clk = devm_clk_get_optional_enabled(dev, "s_axi_lite_clk");
- if (!lp->axi_clk) {
- /* For backward compatibility, if named AXI clock is not present,
- * treat the first clock specified as the AXI clock.
- */
- lp->axi_clk = devm_clk_get_optional_enabled(dev, NULL);
- }
- if (IS_ERR(lp->axi_clk))
- return dev_err_probe(dev, PTR_ERR(lp->axi_clk),
- "could not get AXI clock\n");
-
lp->misc_clks[0].id = "axis_clk";
lp->misc_clks[1].id = "ref_clk";
lp->misc_clks[2].id = "mgt_clk";
@@ -2831,12 +2810,6 @@ static int axienet_probe(struct platform_device *pdev)
if (ret)
return ret;
- /* Map device registers */
- lp->regs = devm_platform_get_and_ioremap_resource(pdev, 0, ðres);
- if (IS_ERR(lp->regs))
- return PTR_ERR(lp->regs);
- lp->regs_start = ethres->start;
-
/* Setup checksum offload, but default to off if not specified */
lp->features = 0;
@@ -3045,11 +3018,6 @@ static int axienet_probe(struct platform_device *pdev)
lp->tx_dma_cr = axienet_calc_cr(lp, XAXIDMA_DFT_TX_THRESHOLD,
XAXIDMA_DFT_TX_USEC);
- ret = axienet_mdio_setup(lp);
- if (ret)
- dev_warn(dev,
- "error registering MDIO bus: %d\n", ret);
-
if (lp->phy_mode == PHY_INTERFACE_MODE_SGMII ||
lp->phy_mode == PHY_INTERFACE_MODE_1000BASEX) {
np = of_parse_phandle(dev->of_node, "pcs-handle", 0);
@@ -3061,17 +3029,14 @@ static int axienet_probe(struct platform_device *pdev)
np = of_parse_phandle(dev->of_node, "phy-handle", 0);
}
if (!np) {
- dev_err(dev, "pcs-handle (preferred) or phy-handle required for 1000BaseX/SGMII\n");
- ret = -EINVAL;
- goto cleanup_mdio;
+ dev_err(dev,
+ "pcs-handle (preferred) or phy-handle required for 1000BaseX/SGMII\n");
+ return -EINVAL;
}
lp->pcs_phy = of_mdio_find_device(np);
- if (!lp->pcs_phy) {
- ret = -EPROBE_DEFER;
- of_node_put(np);
- goto cleanup_mdio;
- }
of_node_put(np);
+ if (!lp->pcs_phy)
+ return -EPROBE_DEFER;
lp->pcs.ops = &axienet_pcs_ops;
lp->pcs.poll = true;
}
@@ -3096,7 +3061,7 @@ static int axienet_probe(struct platform_device *pdev)
if (IS_ERR(lp->phylink)) {
ret = PTR_ERR(lp->phylink);
dev_err(dev, "phylink_create error (%i)\n", ret);
- goto cleanup_mdio;
+ goto cleanup_pcs;
}
ret = register_netdev(lp->ndev);
@@ -3109,32 +3074,24 @@ static int axienet_probe(struct platform_device *pdev)
cleanup_phylink:
phylink_destroy(lp->phylink);
-
-cleanup_mdio:
+cleanup_pcs:
if (lp->pcs_phy)
put_device(&lp->pcs_phy->dev);
- if (lp->mii_bus)
- axienet_mdio_teardown(lp);
return ret;
}
-static void axienet_remove(struct platform_device *pdev)
+static void axienet_mac_remove(struct platform_device *pdev)
{
struct net_device *ndev = platform_get_drvdata(pdev);
struct axienet_local *lp = netdev_priv(ndev);
unregister_netdev(ndev);
-
- if (lp->phylink)
- phylink_destroy(lp->phylink);
-
+ phylink_destroy(lp->phylink);
if (lp->pcs_phy)
put_device(&lp->pcs_phy->dev);
-
- axienet_mdio_teardown(lp);
}
-static void axienet_shutdown(struct platform_device *pdev)
+static void axienet_mac_shutdown(struct platform_device *pdev)
{
struct net_device *ndev = platform_get_drvdata(pdev);
@@ -3182,10 +3139,64 @@ static int axienet_resume(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(axienet_pm_ops,
axienet_suspend, axienet_resume);
+static int axienet_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct axienet_common *cp;
+ struct resource *ethres;
+ int ret;
+
+ cp = devm_kzalloc(dev, sizeof(*cp), GFP_KERNEL);
+ if (!cp)
+ return -ENOMEM;
+
+ cp->pdev = pdev;
+ mutex_init(&cp->reset_lock);
+
+ cp->axi_clk = devm_clk_get_optional_enabled(dev, "s_axi_lite_clk");
+ if (!cp->axi_clk) {
+ /* For backward compatibility, if named AXI clock is not present,
+ * treat the first clock specified as the AXI clock.
+ */
+ cp->axi_clk = devm_clk_get_optional_enabled(dev, NULL);
+ }
+ if (IS_ERR(cp->axi_clk))
+ return dev_err_probe(dev, PTR_ERR(cp->axi_clk),
+ "could not get AXI clock\n");
+
+ /* Map device registers */
+ cp->regs = devm_platform_get_and_ioremap_resource(pdev, 0, ðres);
+ if (IS_ERR(cp->regs))
+ return PTR_ERR(cp->regs);
+ cp->regs_start = ethres->start;
+
+ ret = axienet_mdio_setup(cp);
+ if (ret)
+ dev_warn(dev, "error registering MDIO bus: %d\n", ret);
+
+ ret = axienet_mac_probe(cp);
+ if (!ret)
+ return 0;
+
+ if (cp->mii_bus)
+ axienet_mdio_teardown(cp);
+ return ret;
+}
+
+static void axienet_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct axienet_local *lp = netdev_priv(ndev);
+
+ axienet_mac_remove(pdev);
+ if (lp->mii_bus)
+ axienet_mdio_teardown(lp->cp);
+}
+
static struct platform_driver axienet_driver = {
.probe = axienet_probe,
.remove = axienet_remove,
- .shutdown = axienet_shutdown,
+ .shutdown = axienet_mac_shutdown,
.driver = {
.name = "xilinx_axienet",
.pm = &axienet_pm_ops,
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
index 2799d168ed9d..d428ce6da639 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
@@ -12,6 +12,7 @@
#include <linux/clk.h>
#include <linux/of_address.h>
#include <linux/of_mdio.h>
+#include <linux/platform_device.h>
#include <linux/jiffies.h>
#include <linux/iopoll.h>
@@ -22,13 +23,13 @@
/**
* axienet_mdio_wait_until_ready - MDIO wait function
- * @lp: Pointer to axienet local data structure.
+ * @lp: Pointer to axienet common data structure.
*
* Return : 0 on success, Negative value on errors
*
* Wait till MDIO interface is ready to accept a new transaction.
*/
-static int axienet_mdio_wait_until_ready(struct axienet_local *lp)
+static int axienet_mdio_wait_until_ready(struct axienet_common *lp)
{
u32 val;
@@ -39,11 +40,11 @@ static int axienet_mdio_wait_until_ready(struct axienet_local *lp)
/**
* axienet_mdio_mdc_enable - MDIO MDC enable function
- * @lp: Pointer to axienet local data structure.
+ * @lp: Pointer to axienet common data structure.
*
* Enable the MDIO MDC. Called prior to a read/write operation
*/
-static void axienet_mdio_mdc_enable(struct axienet_local *lp)
+static void axienet_mdio_mdc_enable(struct axienet_common *lp)
{
iowrite32((u32)lp->mii_clk_div | XAE_MDIO_MC_MDIOEN_MASK,
lp->regs + XAE_MDIO_MC_OFFSET);
@@ -51,11 +52,11 @@ static void axienet_mdio_mdc_enable(struct axienet_local *lp)
/**
* axienet_mdio_mdc_disable - MDIO MDC disable function
- * @lp: Pointer to axienet local data structure.
+ * @lp: Pointer to axienet common data structure.
*
* Disable the MDIO MDC. Called after a read/write operation
*/
-static void axienet_mdio_mdc_disable(struct axienet_local *lp)
+static void axienet_mdio_mdc_disable(struct axienet_common *lp)
{
u32 mc_reg;
@@ -80,8 +81,9 @@ static int axienet_mdio_read(struct mii_bus *bus, int phy_id, int reg)
{
u32 rc;
int ret;
- struct axienet_local *lp = bus->priv;
+ struct axienet_common *lp = bus->priv;
+ guard(mutex)(&lp->reset_lock);
axienet_mdio_mdc_enable(lp);
ret = axienet_mdio_wait_until_ready(lp);
@@ -127,13 +129,14 @@ static int axienet_mdio_read(struct mii_bus *bus, int phy_id, int reg)
static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg,
u16 val)
{
- struct axienet_local *lp = bus->priv;
+ struct axienet_common *lp = bus->priv;
int ret;
u32 mcr;
dev_dbg(&bus->dev, "%s(phy_id=%i, reg=%x, val=%x)\n", __func__,
phy_id, reg, val);
+ guard(mutex)(&lp->reset_lock);
axienet_mdio_mdc_enable(lp);
ret = axienet_mdio_wait_until_ready(lp);
@@ -160,7 +163,7 @@ static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg,
/**
* axienet_mdio_enable - MDIO hardware setup function
- * @lp: Pointer to axienet local data structure.
+ * @lp: Pointer to axienet common data structure.
* @np: Pointer to mdio device tree node.
*
* Return: 0 on success, -ETIMEDOUT on a timeout, -EOVERFLOW on a clock
@@ -171,7 +174,7 @@ static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg,
**/
static int axienet_mdio_enable(struct mii_bus *bus, struct device_node *np)
{
- struct axienet_local *lp = bus->priv;
+ struct axienet_common *lp = bus->priv;
u32 mdio_freq = DEFAULT_MDIO_FREQ;
u32 host_clock;
u32 clk_div;
@@ -187,7 +190,7 @@ static int axienet_mdio_enable(struct mii_bus *bus, struct device_node *np)
/* Legacy fallback: detect CPU clock frequency and use as AXI
* bus clock frequency. This only works on certain platforms.
*/
- np1 = of_find_node_by_name(NULL, "lpu");
+ np1 = of_find_node_by_name(NULL, "cpu");
if (!np1) {
dev_warn(&bus->dev,
"Could not find CPU device node.\n");
@@ -258,6 +261,7 @@ static int axienet_mdio_enable(struct mii_bus *bus, struct device_node *np)
"Setting MDIO clock divisor to %u/%u Hz host clock.\n",
lp->mii_clk_div, host_clock);
+ guard(mutex)(&lp->reset_lock);
axienet_mdio_mdc_enable(lp);
ret = axienet_mdio_wait_until_ready(lp);
@@ -269,7 +273,7 @@ static int axienet_mdio_enable(struct mii_bus *bus, struct device_node *np)
/**
* axienet_mdio_setup - MDIO setup function
- * @lp: Pointer to axienet local data structure.
+ * @lp: Pointer to axienet common data structure.
*
* Return: 0 on success, -ETIMEDOUT on a timeout, -EOVERFLOW on a clock
* divisor overflow, -ENOMEM when mdiobus_alloc (to allocate
@@ -278,7 +282,7 @@ static int axienet_mdio_enable(struct mii_bus *bus, struct device_node *np)
* Sets up the MDIO interface by initializing the MDIO clock.
* Register the MDIO interface.
**/
-int axienet_mdio_setup(struct axienet_local *lp)
+int axienet_mdio_setup(struct axienet_common *lp)
{
struct device_node *mdio_node;
struct mii_bus *bus;
@@ -295,18 +299,21 @@ int axienet_mdio_setup(struct axienet_local *lp)
bus->name = "Xilinx Axi Ethernet MDIO";
bus->read = axienet_mdio_read;
bus->write = axienet_mdio_write;
- bus->parent = lp->dev;
+ bus->parent = &lp->pdev->dev;
lp->mii_bus = bus;
- mdio_node = of_get_child_by_name(lp->dev->of_node, "mdio");
- ret = axienet_mdio_enable(bus, mdio_node);
+ mdio_node = of_get_child_by_name(lp->pdev->dev.of_node, "mdio");
+ scoped_guard(mutex, &lp->reset_lock)
+ ret = axienet_mdio_enable(bus, mdio_node);
if (ret < 0)
goto unregister;
ret = of_mdiobus_register(bus, mdio_node);
of_node_put(mdio_node);
- axienet_mdio_mdc_disable(lp);
+ scoped_guard(mutex, &lp->reset_lock)
+ axienet_mdio_mdc_disable(lp);
if (ret) {
+unregister:
mdiobus_free(bus);
lp->mii_bus = NULL;
}
@@ -315,11 +322,11 @@ int axienet_mdio_setup(struct axienet_local *lp)
/**
* axienet_mdio_teardown - MDIO remove function
- * @lp: Pointer to axienet local data structure.
+ * @lp: Pointer to axienet common data structure.
*
* Unregisters the MDIO and frees any associate memory for mii bus.
*/
-void axienet_mdio_teardown(struct axienet_local *lp)
+void axienet_mdio_teardown(struct axienet_common *lp)
{
mdiobus_unregister(lp->mii_bus);
mdiobus_free(lp->mii_bus);
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH net-next v3 7/7] net: axienet: Split into MAC and MDIO drivers
2025-07-28 22:18 [PATCH net-next v3 0/7] net: axienet: Fix deferred probe loop Sean Anderson
` (5 preceding siblings ...)
2025-07-28 22:18 ` [PATCH net-next v3 6/7] net: axienet: Rearrange lifetime functions Sean Anderson
@ 2025-07-28 22:18 ` Sean Anderson
2025-07-29 4:16 ` Subbaraya Sundeep
6 siblings, 1 reply; 12+ messages in thread
From: Sean Anderson @ 2025-07-28 22:18 UTC (permalink / raw)
To: Radhey Shyam Pandey, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, netdev
Cc: linux-kernel, Greg Kroah-Hartman, Michal Simek, linux-arm-kernel,
Leon Romanovsky, Sean Anderson
Returning EPROBE_DEFER after probing a bus may result in an infinite
probe loop if the EPROBE_DEFER error is never resolved. There are two
mutually-exclusive scenarios (that can both occur in the same system).
First, the PCS can be attached to our own MDIO bus:
MAC
|
+->MDIO
|
+->PCS
+->PHY (etc)
In this scenario, we have to probe the MDIO bus before we can look up
the PCS, since otherwise the PCS will always be missing when we look for
it. But if we do things in the right order then we can't get
EPROBE_DEFER, and so there's no risk of a probe loop.
Second, the PCS can be attached to some other MDIO bus:
MAC MDIO
| |
+->MDIO +->PCS
|
+->PHY (etc)
In this scenario, the MDIO bus might not be present for whatever reason
(module not loaded, error in probe, etc.) and we have the possibility of
an EPROBE_DEFER error. If that happens, we will end up in a probe loop
because the PHY on our own MDIO bus incremented deferred_trigger_count
when it probed successfully:
deferred_probe_work_func()
driver_probe_device(MAC)
axienet_probe(MAC)
mdiobus_register(MDIO)
device_add(PHY)
(probe successful)
driver_bound(PHY)
driver_deferred_probe_trigger()
return -EPROBE_DEFER
driver_deferred_probe_add(MAC)
// deferred_trigger_count changed, so...
driver_deferred_probe_trigger()
As I see it, this problem could be solved in the following four ways:
- Modify the driver core to detect and mitigate this sort of scenario
(NACKed by Greg).
- Split the driver into MAC and MDIO parts (this commit).
- Modify phylink to allow connecting a PCS after phylink_create but
before phylink_start. This is tricky because the PCS can affect the
supported phy interfaces, and phy interfaces are validated in
phylink_create.
- Defer phylink_create to ndo_open. This means that all the
netdev/ethtool ops that use phylink now need to check ip the netdev is
open and fall back to some other implementation. I don't think we can
just return -EINVAL or whatever because using ethtool on a down device
has historically worked. I am wary of breaking userspace because some
tool assumes it can get_ksettings while the netdev is down.
Aside from the first option, the second one (this commit) has the best
UX. With the latter two, you could have a netdev that never comes up and
the user may not have very good insight as to why. For example, it may
not be obvious that the user should try to bring the netdev up again
after the PCS is probed. By waiting to create the netdev until after we
successfully probe the PCS we show up in devices_deferred and the netdev
can be brought up as usual.
Per the second bullet point above, split the MAC and MDIO functionality
into separate auxiliary devices. If the MAC fails with EPROBE_DEFER,
then the MDIO bus will remain bound, preventing a probe loop.
Fixes: 1a02556086fc ("net: axienet: Properly handle PCS/PMA PHY for 1000BaseX mode")
Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
---
Changes in v3:
- Rework to use a separate axienet_common structure, as netdevs cannot
be reused once registered.
- Use ida_alloc for aux id
Changes in v2:
- Fix building as a module
- Expand commit message with much more info on the problem and possible
solutions
drivers/net/ethernet/xilinx/Kconfig | 1 +
drivers/net/ethernet/xilinx/xilinx_axienet.h | 10 +-
.../net/ethernet/xilinx/xilinx_axienet_main.c | 168 ++++++++++++++----
.../net/ethernet/xilinx/xilinx_axienet_mdio.c | 59 +++---
4 files changed, 169 insertions(+), 69 deletions(-)
diff --git a/drivers/net/ethernet/xilinx/Kconfig b/drivers/net/ethernet/xilinx/Kconfig
index 7502214cc7d5..3b940d2d3115 100644
--- a/drivers/net/ethernet/xilinx/Kconfig
+++ b/drivers/net/ethernet/xilinx/Kconfig
@@ -27,6 +27,7 @@ config XILINX_AXI_EMAC
tristate "Xilinx 10/100/1000 AXI Ethernet support"
depends on HAS_IOMEM
depends on XILINX_DMA
+ select AUXILIARY_BUS
select PHYLINK
select DIMLIB
help
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h
index d7215dd92ce9..69665c7f264a 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet.h
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h
@@ -470,6 +470,7 @@ struct skbuf_dma_descriptor {
/**
* struct axienet_common - axienet private common data
* @pdev: Pointer to common platform device structure
+ * @mac: Pointer to MAC (netdev parent) device structure
* @axi_clk: AXI4-Lite bus clock
* @reset_lock: Lock held while resetting the device to prevent register access
* @mii_bus: Pointer to MII bus structure
@@ -479,11 +480,12 @@ struct skbuf_dma_descriptor {
*/
struct axienet_common {
struct platform_device *pdev;
+ struct auxiliary_device mac;
struct clk *axi_clk;
struct mutex reset_lock;
- struct mii_bus *mii_bus;
+ struct auxiliary_device mii_bus;
u8 mii_clk_div;
void __iomem *regs;
@@ -493,7 +495,7 @@ struct axienet_common {
/**
* struct axienet_local - axienet private per device data
* @ndev: Pointer for net_device to which it will be attached.
- * @dev: Pointer to device structure
+ * @dev: Pointer to parent device structure for DMA access
* @phylink: Pointer to phylink instance
* @phylink_config: phylink configuration settings
* @pcs_phy: Reference to PCS/PMA PHY if used
@@ -752,8 +754,6 @@ static inline void axienet_dma_out_addr(struct axienet_local *lp, off_t reg,
#endif /* CONFIG_64BIT */
-/* Function prototypes visible in xilinx_axienet_mdio.c for other files */
-int axienet_mdio_setup(struct axienet_common *lp);
-void axienet_mdio_teardown(struct axienet_common *lp);
+extern struct auxiliary_driver xilinx_axienet_mdio;
#endif /* XILINX_AXI_ENET_H */
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index f235ef15187c..23e5c8090d45 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -22,6 +22,7 @@
* - Add support for extended VLAN support.
*/
+#include <linux/auxiliary_bus.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/etherdevice.h>
@@ -1907,8 +1908,11 @@ static const struct net_device_ops axienet_netdev_dmaengine_ops = {
static void axienet_ethtools_get_drvinfo(struct net_device *ndev,
struct ethtool_drvinfo *ed)
{
+ struct axienet_local *lp = netdev_priv(ndev);
+
strscpy(ed->driver, DRIVER_NAME, sizeof(ed->driver));
strscpy(ed->version, DRIVER_VERSION, sizeof(ed->version));
+ strscpy(ed->bus_info, dev_name(lp->dev), sizeof(ed->bus_info));
}
/**
@@ -2749,10 +2753,12 @@ static void axienet_disable_misc(void *clocks)
clk_bulk_disable_unprepare(XAE_NUM_MISC_CLOCKS, clocks);
}
-static int axienet_mac_probe(struct axienet_common *cp)
+static int axienet_mac_probe(struct auxiliary_device *auxdev,
+ const struct auxiliary_device_id *id)
{
+ struct axienet_common *cp = auxdev->dev.platform_data;
struct platform_device *pdev = cp->pdev;
- struct device *dev = &pdev->dev;
+ struct device *dev = &auxdev->dev;
struct axienet_local *lp;
struct net_device *ndev;
struct device_node *np;
@@ -2765,7 +2771,7 @@ static int axienet_mac_probe(struct axienet_common *cp)
if (!ndev)
return -ENOMEM;
- platform_set_drvdata(pdev, ndev);
+ auxiliary_set_drvdata(auxdev, ndev);
SET_NETDEV_DEV(ndev, dev);
ndev->features = NETIF_F_SG;
@@ -2777,7 +2783,7 @@ static int axienet_mac_probe(struct axienet_common *cp)
lp = netdev_priv(ndev);
lp->ndev = ndev;
- lp->dev = dev;
+ lp->dev = &pdev->dev;
lp->cp = cp;
lp->regs = cp->regs;
lp->options = XAE_OPTION_DEFAULTS;
@@ -2909,8 +2915,11 @@ static int axienet_mac_probe(struct axienet_common *cp)
of_node_put(np);
lp->eth_irq = platform_get_irq_optional(pdev, 0);
} else {
+ struct resource *dmares;
+
/* Check for these resources directly on the Ethernet node. */
- lp->dma_regs = devm_platform_get_and_ioremap_resource(pdev, 1, NULL);
+ dmares = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ lp->dma_regs = devm_ioremap_resource(dev, dmares);
lp->rx_irq = platform_get_irq(pdev, 1);
lp->tx_irq = platform_get_irq(pdev, 0);
lp->eth_irq = platform_get_irq_optional(pdev, 2);
@@ -2925,7 +2934,9 @@ static int axienet_mac_probe(struct axienet_common *cp)
}
/* Reset core now that clocks are enabled, prior to accessing MDIO */
+ axienet_lock_mii(lp);
ret = __axienet_device_reset(lp);
+ axienet_unlock_mii(lp);
if (ret)
return ret;
@@ -2957,7 +2968,8 @@ static int axienet_mac_probe(struct axienet_common *cp)
return -EINVAL;
}
- ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(addr_width));
+ ret = dma_set_mask_and_coherent(lp->dev,
+ DMA_BIT_MASK(addr_width));
if (ret) {
dev_err(dev, "No suitable DMA available\n");
return ret;
@@ -3055,7 +3067,7 @@ static int axienet_mac_probe(struct axienet_common *cp)
lp->phylink_config.supported_interfaces);
}
- lp->phylink = phylink_create(&lp->phylink_config, dev->fwnode,
+ lp->phylink = phylink_create(&lp->phylink_config, dev_fwnode(dev),
lp->phy_mode,
&axienet_phylink_ops);
if (IS_ERR(lp->phylink)) {
@@ -3080,9 +3092,9 @@ static int axienet_mac_probe(struct axienet_common *cp)
return ret;
}
-static void axienet_mac_remove(struct platform_device *pdev)
+static void axienet_mac_remove(struct auxiliary_device *auxdev)
{
- struct net_device *ndev = platform_get_drvdata(pdev);
+ struct net_device *ndev = auxiliary_get_drvdata(auxdev);
struct axienet_local *lp = netdev_priv(ndev);
unregister_netdev(ndev);
@@ -3091,9 +3103,9 @@ static void axienet_mac_remove(struct platform_device *pdev)
put_device(&lp->pcs_phy->dev);
}
-static void axienet_mac_shutdown(struct platform_device *pdev)
+static void axienet_mac_shutdown(struct auxiliary_device *auxdev)
{
- struct net_device *ndev = platform_get_drvdata(pdev);
+ struct net_device *ndev = auxiliary_get_drvdata(auxdev);
rtnl_lock();
netif_device_detach(ndev);
@@ -3139,12 +3151,78 @@ static int axienet_resume(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(axienet_pm_ops,
axienet_suspend, axienet_resume);
+static const struct auxiliary_device_id xilinx_axienet_mac_id_table[] = {
+ { .name = KBUILD_MODNAME ".mac", },
+ { },
+};
+MODULE_DEVICE_TABLE(auxiliary, xilinx_axienet_mac_id_table);
+
+static struct auxiliary_driver xilinx_axienet_mac = {
+ .name = "mac",
+ .id_table = xilinx_axienet_mac_id_table,
+ .probe = axienet_mac_probe,
+ .remove = axienet_mac_remove,
+ .shutdown = axienet_mac_shutdown,
+ .driver = {
+ .pm = &axienet_pm_ops,
+ },
+};
+
+static DEFINE_IDA(axienet_id);
+
+static void axienet_id_free(void *data)
+{
+ int id = (intptr_t)data;
+
+ ida_free(&axienet_id, id);
+}
+
+static void auxenet_aux_release(struct device *dev) { }
+
+static void axienet_aux_destroy(void *data)
+{
+ struct auxiliary_device *auxdev = data;
+
+ auxiliary_device_delete(auxdev);
+ auxiliary_device_uninit(auxdev);
+ fwnode_handle_put(auxdev->dev.fwnode);
+}
+
+static int axienet_aux_create(struct axienet_common *cp,
+ struct auxiliary_device *auxdev, const char *name,
+ int id, struct fwnode_handle *fwnode)
+{
+ struct device *dev = &cp->pdev->dev;
+ int ret;
+
+ auxdev->name = name;
+ auxdev->id = id;
+ auxdev->dev.parent = dev;
+ auxdev->dev.platform_data = cp;
+ auxdev->dev.release = auxenet_aux_release;
+ device_set_node(&auxdev->dev, fwnode);
+ ret = auxiliary_device_init(auxdev);
+ if (ret) {
+ fwnode_handle_put(fwnode);
+ return ret;
+ }
+
+ ret = auxiliary_device_add(auxdev);
+ if (ret) {
+ fwnode_handle_put(fwnode);
+ auxiliary_device_uninit(auxdev);
+ return ret;
+ }
+
+ return devm_add_action_or_reset(dev, axienet_aux_destroy, auxdev);
+}
+
static int axienet_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct axienet_common *cp;
struct resource *ethres;
- int ret;
+ int ret, id;
cp = devm_kzalloc(dev, sizeof(*cp), GFP_KERNEL);
if (!cp)
@@ -3170,33 +3248,31 @@ static int axienet_probe(struct platform_device *pdev)
return PTR_ERR(cp->regs);
cp->regs_start = ethres->start;
- ret = axienet_mdio_setup(cp);
- if (ret)
- dev_warn(dev, "error registering MDIO bus: %d\n", ret);
+ id = ida_alloc(&axienet_id, GFP_KERNEL);
+ if (id < 0)
+ return dev_err_probe(dev, id, "could not allocate id\n");
- ret = axienet_mac_probe(cp);
- if (!ret)
- return 0;
+ ret = devm_add_action_or_reset(dev, axienet_id_free,
+ (void *)(intptr_t)id);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "could not register id free action\n");
- if (cp->mii_bus)
- axienet_mdio_teardown(cp);
- return ret;
-}
+ ret = axienet_aux_create(cp, &cp->mii_bus, "mdio", id,
+ device_get_named_child_node(dev, "mdio"));
+ if (ret)
+ return dev_err_probe(dev, ret, "could not create mdio bus\n");
-static void axienet_remove(struct platform_device *pdev)
-{
- struct net_device *ndev = platform_get_drvdata(pdev);
- struct axienet_local *lp = netdev_priv(ndev);
+ ret = axienet_aux_create(cp, &cp->mac, "mac", id,
+ fwnode_handle_get(dev_fwnode(dev)));
+ if (ret)
+ return dev_err_probe(dev, ret, "could not create MAC\n");
- axienet_mac_remove(pdev);
- if (lp->mii_bus)
- axienet_mdio_teardown(lp->cp);
+ return 0;
}
static struct platform_driver axienet_driver = {
.probe = axienet_probe,
- .remove = axienet_remove,
- .shutdown = axienet_mac_shutdown,
.driver = {
.name = "xilinx_axienet",
.pm = &axienet_pm_ops,
@@ -3204,7 +3280,35 @@ static struct platform_driver axienet_driver = {
},
};
-module_platform_driver(axienet_driver);
+static int __init axienet_init(void)
+{
+ int ret;
+
+ ret = auxiliary_driver_register(&xilinx_axienet_mdio);
+ if (ret)
+ return ret;
+
+ ret = auxiliary_driver_register(&xilinx_axienet_mac);
+ if (ret)
+ goto unregister_mdio;
+
+ ret = platform_driver_register(&axienet_driver);
+ if (ret) {
+ auxiliary_driver_unregister(&xilinx_axienet_mac);
+unregister_mdio:
+ auxiliary_driver_unregister(&xilinx_axienet_mdio);
+ }
+ return ret;
+}
+module_init(axienet_init);
+
+static void __exit axienet_exit(void)
+{
+ platform_driver_register(&axienet_driver);
+ auxiliary_driver_unregister(&xilinx_axienet_mac);
+ auxiliary_driver_unregister(&xilinx_axienet_mdio);
+}
+module_exit(axienet_exit);
MODULE_DESCRIPTION("Xilinx Axi Ethernet driver");
MODULE_AUTHOR("Xilinx");
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
index d428ce6da639..cc8a2a70271b 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
@@ -9,10 +9,10 @@
* Copyright (c) 2010 - 2012 Xilinx, Inc. All rights reserved.
*/
+#include <linux/auxiliary_bus.h>
#include <linux/clk.h>
#include <linux/of_address.h>
#include <linux/of_mdio.h>
-#include <linux/platform_device.h>
#include <linux/jiffies.h>
#include <linux/iopoll.h>
@@ -271,19 +271,10 @@ static int axienet_mdio_enable(struct mii_bus *bus, struct device_node *np)
return ret;
}
-/**
- * axienet_mdio_setup - MDIO setup function
- * @lp: Pointer to axienet common data structure.
- *
- * Return: 0 on success, -ETIMEDOUT on a timeout, -EOVERFLOW on a clock
- * divisor overflow, -ENOMEM when mdiobus_alloc (to allocate
- * memory for mii bus structure) fails.
- *
- * Sets up the MDIO interface by initializing the MDIO clock.
- * Register the MDIO interface.
- **/
-int axienet_mdio_setup(struct axienet_common *lp)
+static int axienet_mdio_probe(struct auxiliary_device *auxdev,
+ const struct auxiliary_device_id *id)
{
+ struct axienet_common *lp = auxdev->dev.platform_data;
struct device_node *mdio_node;
struct mii_bus *bus;
int ret;
@@ -299,36 +290,40 @@ int axienet_mdio_setup(struct axienet_common *lp)
bus->name = "Xilinx Axi Ethernet MDIO";
bus->read = axienet_mdio_read;
bus->write = axienet_mdio_write;
- bus->parent = &lp->pdev->dev;
- lp->mii_bus = bus;
+ bus->parent = &auxdev->dev;
+ auxiliary_set_drvdata(auxdev, bus);
- mdio_node = of_get_child_by_name(lp->pdev->dev.of_node, "mdio");
- scoped_guard(mutex, &lp->reset_lock)
- ret = axienet_mdio_enable(bus, mdio_node);
+ mdio_node = dev_of_node(&auxdev->dev);
+ ret = axienet_mdio_enable(bus, mdio_node);
if (ret < 0)
goto unregister;
ret = of_mdiobus_register(bus, mdio_node);
- of_node_put(mdio_node);
scoped_guard(mutex, &lp->reset_lock)
axienet_mdio_mdc_disable(lp);
- if (ret) {
+ if (ret)
unregister:
mdiobus_free(bus);
- lp->mii_bus = NULL;
- }
return ret;
}
-/**
- * axienet_mdio_teardown - MDIO remove function
- * @lp: Pointer to axienet common data structure.
- *
- * Unregisters the MDIO and frees any associate memory for mii bus.
- */
-void axienet_mdio_teardown(struct axienet_common *lp)
+static void axienet_mdio_remove(struct auxiliary_device *auxdev)
{
- mdiobus_unregister(lp->mii_bus);
- mdiobus_free(lp->mii_bus);
- lp->mii_bus = NULL;
+ struct mii_bus *mii_bus = auxiliary_get_drvdata(auxdev);
+
+ mdiobus_unregister(mii_bus);
+ mdiobus_free(mii_bus);
}
+
+static const struct auxiliary_device_id xilinx_axienet_mdio_id_table[] = {
+ { .name = KBUILD_MODNAME ".mdio", },
+ { },
+};
+MODULE_DEVICE_TABLE(auxiliary, xilinx_axienet_mdio_id_table);
+
+struct auxiliary_driver xilinx_axienet_mdio = {
+ .name = "mdio",
+ .id_table = xilinx_axienet_mdio_id_table,
+ .probe = axienet_mdio_probe,
+ .remove = axienet_mdio_remove,
+};
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH net-next v3 7/7] net: axienet: Split into MAC and MDIO drivers
2025-07-28 22:18 ` [PATCH net-next v3 7/7] net: axienet: Split into MAC and MDIO drivers Sean Anderson
@ 2025-07-29 4:16 ` Subbaraya Sundeep
0 siblings, 0 replies; 12+ messages in thread
From: Subbaraya Sundeep @ 2025-07-29 4:16 UTC (permalink / raw)
To: Sean Anderson
Cc: Radhey Shyam Pandey, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, netdev, linux-kernel,
Greg Kroah-Hartman, Michal Simek, linux-arm-kernel,
Leon Romanovsky
On 2025-07-28 at 22:18:23, Sean Anderson (sean.anderson@linux.dev) wrote:
> Returning EPROBE_DEFER after probing a bus may result in an infinite
> probe loop if the EPROBE_DEFER error is never resolved. There are two
> mutually-exclusive scenarios (that can both occur in the same system).
> First, the PCS can be attached to our own MDIO bus:
>
> MAC
> |
> +->MDIO
> |
> +->PCS
> +->PHY (etc)
>
> In this scenario, we have to probe the MDIO bus before we can look up
> the PCS, since otherwise the PCS will always be missing when we look for
> it. But if we do things in the right order then we can't get
> EPROBE_DEFER, and so there's no risk of a probe loop.
>
> Second, the PCS can be attached to some other MDIO bus:
>
> MAC MDIO
> | |
> +->MDIO +->PCS
> |
> +->PHY (etc)
>
> In this scenario, the MDIO bus might not be present for whatever reason
> (module not loaded, error in probe, etc.) and we have the possibility of
> an EPROBE_DEFER error. If that happens, we will end up in a probe loop
> because the PHY on our own MDIO bus incremented deferred_trigger_count
> when it probed successfully:
>
> deferred_probe_work_func()
> driver_probe_device(MAC)
> axienet_probe(MAC)
> mdiobus_register(MDIO)
> device_add(PHY)
> (probe successful)
> driver_bound(PHY)
> driver_deferred_probe_trigger()
> return -EPROBE_DEFER
> driver_deferred_probe_add(MAC)
> // deferred_trigger_count changed, so...
> driver_deferred_probe_trigger()
>
> As I see it, this problem could be solved in the following four ways:
>
> - Modify the driver core to detect and mitigate this sort of scenario
> (NACKed by Greg).
> - Split the driver into MAC and MDIO parts (this commit).
> - Modify phylink to allow connecting a PCS after phylink_create but
> before phylink_start. This is tricky because the PCS can affect the
> supported phy interfaces, and phy interfaces are validated in
> phylink_create.
> - Defer phylink_create to ndo_open. This means that all the
> netdev/ethtool ops that use phylink now need to check ip the netdev is
> open and fall back to some other implementation. I don't think we can
> just return -EINVAL or whatever because using ethtool on a down device
> has historically worked. I am wary of breaking userspace because some
> tool assumes it can get_ksettings while the netdev is down.
>
> Aside from the first option, the second one (this commit) has the best
> UX. With the latter two, you could have a netdev that never comes up and
> the user may not have very good insight as to why. For example, it may
> not be obvious that the user should try to bring the netdev up again
> after the PCS is probed. By waiting to create the netdev until after we
> successfully probe the PCS we show up in devices_deferred and the netdev
> can be brought up as usual.
>
> Per the second bullet point above, split the MAC and MDIO functionality
> into separate auxiliary devices. If the MAC fails with EPROBE_DEFER,
> then the MDIO bus will remain bound, preventing a probe loop.
>
> Fixes: 1a02556086fc ("net: axienet: Properly handle PCS/PMA PHY for 1000BaseX mode")
> Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
> ---
>
> Changes in v3:
> - Rework to use a separate axienet_common structure, as netdevs cannot
> be reused once registered.
> - Use ida_alloc for aux id
>
> Changes in v2:
> - Fix building as a module
> - Expand commit message with much more info on the problem and possible
> solutions
>
> drivers/net/ethernet/xilinx/Kconfig | 1 +
> drivers/net/ethernet/xilinx/xilinx_axienet.h | 10 +-
> .../net/ethernet/xilinx/xilinx_axienet_main.c | 168 ++++++++++++++----
> .../net/ethernet/xilinx/xilinx_axienet_mdio.c | 59 +++---
> 4 files changed, 169 insertions(+), 69 deletions(-)
>
> diff --git a/drivers/net/ethernet/xilinx/Kconfig b/drivers/net/ethernet/xilinx/Kconfig
> index 7502214cc7d5..3b940d2d3115 100644
> --- a/drivers/net/ethernet/xilinx/Kconfig
> +++ b/drivers/net/ethernet/xilinx/Kconfig
> @@ -27,6 +27,7 @@ config XILINX_AXI_EMAC
> tristate "Xilinx 10/100/1000 AXI Ethernet support"
> depends on HAS_IOMEM
> depends on XILINX_DMA
> + select AUXILIARY_BUS
> select PHYLINK
> select DIMLIB
> help
> diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h
> index d7215dd92ce9..69665c7f264a 100644
> --- a/drivers/net/ethernet/xilinx/xilinx_axienet.h
> +++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h
> @@ -470,6 +470,7 @@ struct skbuf_dma_descriptor {
> /**
> * struct axienet_common - axienet private common data
> * @pdev: Pointer to common platform device structure
> + * @mac: Pointer to MAC (netdev parent) device structure
> * @axi_clk: AXI4-Lite bus clock
> * @reset_lock: Lock held while resetting the device to prevent register access
> * @mii_bus: Pointer to MII bus structure
> @@ -479,11 +480,12 @@ struct skbuf_dma_descriptor {
> */
> struct axienet_common {
> struct platform_device *pdev;
> + struct auxiliary_device mac;
>
> struct clk *axi_clk;
>
> struct mutex reset_lock;
> - struct mii_bus *mii_bus;
> + struct auxiliary_device mii_bus;
> u8 mii_clk_div;
>
> void __iomem *regs;
> @@ -493,7 +495,7 @@ struct axienet_common {
> /**
> * struct axienet_local - axienet private per device data
> * @ndev: Pointer for net_device to which it will be attached.
> - * @dev: Pointer to device structure
> + * @dev: Pointer to parent device structure for DMA access
> * @phylink: Pointer to phylink instance
> * @phylink_config: phylink configuration settings
> * @pcs_phy: Reference to PCS/PMA PHY if used
> @@ -752,8 +754,6 @@ static inline void axienet_dma_out_addr(struct axienet_local *lp, off_t reg,
>
> #endif /* CONFIG_64BIT */
>
> -/* Function prototypes visible in xilinx_axienet_mdio.c for other files */
> -int axienet_mdio_setup(struct axienet_common *lp);
> -void axienet_mdio_teardown(struct axienet_common *lp);
> +extern struct auxiliary_driver xilinx_axienet_mdio;
>
> #endif /* XILINX_AXI_ENET_H */
> diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
> index f235ef15187c..23e5c8090d45 100644
> --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
> +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
> @@ -22,6 +22,7 @@
> * - Add support for extended VLAN support.
> */
>
> +#include <linux/auxiliary_bus.h>
> #include <linux/clk.h>
> #include <linux/delay.h>
> #include <linux/etherdevice.h>
> @@ -1907,8 +1908,11 @@ static const struct net_device_ops axienet_netdev_dmaengine_ops = {
> static void axienet_ethtools_get_drvinfo(struct net_device *ndev,
> struct ethtool_drvinfo *ed)
> {
> + struct axienet_local *lp = netdev_priv(ndev);
> +
> strscpy(ed->driver, DRIVER_NAME, sizeof(ed->driver));
> strscpy(ed->version, DRIVER_VERSION, sizeof(ed->version));
> + strscpy(ed->bus_info, dev_name(lp->dev), sizeof(ed->bus_info));
> }
>
> /**
> @@ -2749,10 +2753,12 @@ static void axienet_disable_misc(void *clocks)
> clk_bulk_disable_unprepare(XAE_NUM_MISC_CLOCKS, clocks);
> }
>
> -static int axienet_mac_probe(struct axienet_common *cp)
> +static int axienet_mac_probe(struct auxiliary_device *auxdev,
> + const struct auxiliary_device_id *id)
> {
> + struct axienet_common *cp = auxdev->dev.platform_data;
> struct platform_device *pdev = cp->pdev;
> - struct device *dev = &pdev->dev;
> + struct device *dev = &auxdev->dev;
> struct axienet_local *lp;
> struct net_device *ndev;
> struct device_node *np;
> @@ -2765,7 +2771,7 @@ static int axienet_mac_probe(struct axienet_common *cp)
> if (!ndev)
> return -ENOMEM;
>
> - platform_set_drvdata(pdev, ndev);
> + auxiliary_set_drvdata(auxdev, ndev);
>
> SET_NETDEV_DEV(ndev, dev);
> ndev->features = NETIF_F_SG;
> @@ -2777,7 +2783,7 @@ static int axienet_mac_probe(struct axienet_common *cp)
>
> lp = netdev_priv(ndev);
> lp->ndev = ndev;
> - lp->dev = dev;
> + lp->dev = &pdev->dev;
> lp->cp = cp;
> lp->regs = cp->regs;
> lp->options = XAE_OPTION_DEFAULTS;
> @@ -2909,8 +2915,11 @@ static int axienet_mac_probe(struct axienet_common *cp)
> of_node_put(np);
> lp->eth_irq = platform_get_irq_optional(pdev, 0);
> } else {
> + struct resource *dmares;
> +
> /* Check for these resources directly on the Ethernet node. */
> - lp->dma_regs = devm_platform_get_and_ioremap_resource(pdev, 1, NULL);
> + dmares = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> + lp->dma_regs = devm_ioremap_resource(dev, dmares);
> lp->rx_irq = platform_get_irq(pdev, 1);
> lp->tx_irq = platform_get_irq(pdev, 0);
> lp->eth_irq = platform_get_irq_optional(pdev, 2);
> @@ -2925,7 +2934,9 @@ static int axienet_mac_probe(struct axienet_common *cp)
> }
>
> /* Reset core now that clocks are enabled, prior to accessing MDIO */
> + axienet_lock_mii(lp);
> ret = __axienet_device_reset(lp);
> + axienet_unlock_mii(lp);
> if (ret)
> return ret;
>
> @@ -2957,7 +2968,8 @@ static int axienet_mac_probe(struct axienet_common *cp)
> return -EINVAL;
> }
>
> - ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(addr_width));
> + ret = dma_set_mask_and_coherent(lp->dev,
> + DMA_BIT_MASK(addr_width));
> if (ret) {
> dev_err(dev, "No suitable DMA available\n");
> return ret;
> @@ -3055,7 +3067,7 @@ static int axienet_mac_probe(struct axienet_common *cp)
> lp->phylink_config.supported_interfaces);
> }
>
> - lp->phylink = phylink_create(&lp->phylink_config, dev->fwnode,
> + lp->phylink = phylink_create(&lp->phylink_config, dev_fwnode(dev),
> lp->phy_mode,
> &axienet_phylink_ops);
> if (IS_ERR(lp->phylink)) {
> @@ -3080,9 +3092,9 @@ static int axienet_mac_probe(struct axienet_common *cp)
> return ret;
> }
>
> -static void axienet_mac_remove(struct platform_device *pdev)
> +static void axienet_mac_remove(struct auxiliary_device *auxdev)
> {
> - struct net_device *ndev = platform_get_drvdata(pdev);
> + struct net_device *ndev = auxiliary_get_drvdata(auxdev);
> struct axienet_local *lp = netdev_priv(ndev);
>
> unregister_netdev(ndev);
> @@ -3091,9 +3103,9 @@ static void axienet_mac_remove(struct platform_device *pdev)
> put_device(&lp->pcs_phy->dev);
> }
>
> -static void axienet_mac_shutdown(struct platform_device *pdev)
> +static void axienet_mac_shutdown(struct auxiliary_device *auxdev)
> {
> - struct net_device *ndev = platform_get_drvdata(pdev);
> + struct net_device *ndev = auxiliary_get_drvdata(auxdev);
>
> rtnl_lock();
> netif_device_detach(ndev);
> @@ -3139,12 +3151,78 @@ static int axienet_resume(struct device *dev)
> static DEFINE_SIMPLE_DEV_PM_OPS(axienet_pm_ops,
> axienet_suspend, axienet_resume);
>
> +static const struct auxiliary_device_id xilinx_axienet_mac_id_table[] = {
> + { .name = KBUILD_MODNAME ".mac", },
> + { },
> +};
> +MODULE_DEVICE_TABLE(auxiliary, xilinx_axienet_mac_id_table);
> +
> +static struct auxiliary_driver xilinx_axienet_mac = {
> + .name = "mac",
> + .id_table = xilinx_axienet_mac_id_table,
> + .probe = axienet_mac_probe,
> + .remove = axienet_mac_remove,
> + .shutdown = axienet_mac_shutdown,
> + .driver = {
> + .pm = &axienet_pm_ops,
> + },
> +};
> +
> +static DEFINE_IDA(axienet_id);
> +
> +static void axienet_id_free(void *data)
> +{
> + int id = (intptr_t)data;
> +
> + ida_free(&axienet_id, id);
> +}
> +
> +static void auxenet_aux_release(struct device *dev) { }
> +
> +static void axienet_aux_destroy(void *data)
> +{
> + struct auxiliary_device *auxdev = data;
> +
> + auxiliary_device_delete(auxdev);
> + auxiliary_device_uninit(auxdev);
> + fwnode_handle_put(auxdev->dev.fwnode);
> +}
> +
> +static int axienet_aux_create(struct axienet_common *cp,
> + struct auxiliary_device *auxdev, const char *name,
> + int id, struct fwnode_handle *fwnode)
> +{
> + struct device *dev = &cp->pdev->dev;
> + int ret;
> +
> + auxdev->name = name;
> + auxdev->id = id;
> + auxdev->dev.parent = dev;
> + auxdev->dev.platform_data = cp;
> + auxdev->dev.release = auxenet_aux_release;
> + device_set_node(&auxdev->dev, fwnode);
> + ret = auxiliary_device_init(auxdev);
> + if (ret) {
> + fwnode_handle_put(fwnode);
> + return ret;
> + }
> +
> + ret = auxiliary_device_add(auxdev);
> + if (ret) {
> + fwnode_handle_put(fwnode);
> + auxiliary_device_uninit(auxdev);
> + return ret;
> + }
> +
> + return devm_add_action_or_reset(dev, axienet_aux_destroy, auxdev);
> +}
> +
> static int axienet_probe(struct platform_device *pdev)
> {
> struct device *dev = &pdev->dev;
> struct axienet_common *cp;
> struct resource *ethres;
> - int ret;
> + int ret, id;
>
> cp = devm_kzalloc(dev, sizeof(*cp), GFP_KERNEL);
> if (!cp)
> @@ -3170,33 +3248,31 @@ static int axienet_probe(struct platform_device *pdev)
> return PTR_ERR(cp->regs);
> cp->regs_start = ethres->start;
>
> - ret = axienet_mdio_setup(cp);
> - if (ret)
> - dev_warn(dev, "error registering MDIO bus: %d\n", ret);
> + id = ida_alloc(&axienet_id, GFP_KERNEL);
> + if (id < 0)
> + return dev_err_probe(dev, id, "could not allocate id\n");
>
> - ret = axienet_mac_probe(cp);
> - if (!ret)
> - return 0;
> + ret = devm_add_action_or_reset(dev, axienet_id_free,
> + (void *)(intptr_t)id);
> + if (ret)
> + return dev_err_probe(dev, ret,
> + "could not register id free action\n");
>
> - if (cp->mii_bus)
> - axienet_mdio_teardown(cp);
> - return ret;
> -}
> + ret = axienet_aux_create(cp, &cp->mii_bus, "mdio", id,
> + device_get_named_child_node(dev, "mdio"));
> + if (ret)
> + return dev_err_probe(dev, ret, "could not create mdio bus\n");
>
> -static void axienet_remove(struct platform_device *pdev)
> -{
> - struct net_device *ndev = platform_get_drvdata(pdev);
> - struct axienet_local *lp = netdev_priv(ndev);
> + ret = axienet_aux_create(cp, &cp->mac, "mac", id,
> + fwnode_handle_get(dev_fwnode(dev)));
> + if (ret)
> + return dev_err_probe(dev, ret, "could not create MAC\n");
>
> - axienet_mac_remove(pdev);
> - if (lp->mii_bus)
> - axienet_mdio_teardown(lp->cp);
> + return 0;
> }
>
> static struct platform_driver axienet_driver = {
> .probe = axienet_probe,
> - .remove = axienet_remove,
> - .shutdown = axienet_mac_shutdown,
> .driver = {
> .name = "xilinx_axienet",
> .pm = &axienet_pm_ops,
> @@ -3204,7 +3280,35 @@ static struct platform_driver axienet_driver = {
> },
> };
>
> -module_platform_driver(axienet_driver);
> +static int __init axienet_init(void)
> +{
> + int ret;
> +
> + ret = auxiliary_driver_register(&xilinx_axienet_mdio);
> + if (ret)
> + return ret;
> +
> + ret = auxiliary_driver_register(&xilinx_axienet_mac);
> + if (ret)
> + goto unregister_mdio;
> +
> + ret = platform_driver_register(&axienet_driver);
> + if (ret) {
> + auxiliary_driver_unregister(&xilinx_axienet_mac);
> +unregister_mdio:
> + auxiliary_driver_unregister(&xilinx_axienet_mdio);
> + }
> + return ret;
> +}
> +module_init(axienet_init);
> +
> +static void __exit axienet_exit(void)
> +{
> + platform_driver_register(&axienet_driver);
platform_driver_unregister
Thanks,
Sundeep
> + auxiliary_driver_unregister(&xilinx_axienet_mac);
> + auxiliary_driver_unregister(&xilinx_axienet_mdio);
> +}
> +module_exit(axienet_exit);
>
> MODULE_DESCRIPTION("Xilinx Axi Ethernet driver");
> MODULE_AUTHOR("Xilinx");
> diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
> index d428ce6da639..cc8a2a70271b 100644
> --- a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
> +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
> @@ -9,10 +9,10 @@
> * Copyright (c) 2010 - 2012 Xilinx, Inc. All rights reserved.
> */
>
> +#include <linux/auxiliary_bus.h>
> #include <linux/clk.h>
> #include <linux/of_address.h>
> #include <linux/of_mdio.h>
> -#include <linux/platform_device.h>
> #include <linux/jiffies.h>
> #include <linux/iopoll.h>
>
> @@ -271,19 +271,10 @@ static int axienet_mdio_enable(struct mii_bus *bus, struct device_node *np)
> return ret;
> }
>
> -/**
> - * axienet_mdio_setup - MDIO setup function
> - * @lp: Pointer to axienet common data structure.
> - *
> - * Return: 0 on success, -ETIMEDOUT on a timeout, -EOVERFLOW on a clock
> - * divisor overflow, -ENOMEM when mdiobus_alloc (to allocate
> - * memory for mii bus structure) fails.
> - *
> - * Sets up the MDIO interface by initializing the MDIO clock.
> - * Register the MDIO interface.
> - **/
> -int axienet_mdio_setup(struct axienet_common *lp)
> +static int axienet_mdio_probe(struct auxiliary_device *auxdev,
> + const struct auxiliary_device_id *id)
> {
> + struct axienet_common *lp = auxdev->dev.platform_data;
> struct device_node *mdio_node;
> struct mii_bus *bus;
> int ret;
> @@ -299,36 +290,40 @@ int axienet_mdio_setup(struct axienet_common *lp)
> bus->name = "Xilinx Axi Ethernet MDIO";
> bus->read = axienet_mdio_read;
> bus->write = axienet_mdio_write;
> - bus->parent = &lp->pdev->dev;
> - lp->mii_bus = bus;
> + bus->parent = &auxdev->dev;
> + auxiliary_set_drvdata(auxdev, bus);
>
> - mdio_node = of_get_child_by_name(lp->pdev->dev.of_node, "mdio");
> - scoped_guard(mutex, &lp->reset_lock)
> - ret = axienet_mdio_enable(bus, mdio_node);
> + mdio_node = dev_of_node(&auxdev->dev);
> + ret = axienet_mdio_enable(bus, mdio_node);
> if (ret < 0)
> goto unregister;
>
> ret = of_mdiobus_register(bus, mdio_node);
> - of_node_put(mdio_node);
> scoped_guard(mutex, &lp->reset_lock)
> axienet_mdio_mdc_disable(lp);
> - if (ret) {
> + if (ret)
> unregister:
> mdiobus_free(bus);
> - lp->mii_bus = NULL;
> - }
> return ret;
> }
>
> -/**
> - * axienet_mdio_teardown - MDIO remove function
> - * @lp: Pointer to axienet common data structure.
> - *
> - * Unregisters the MDIO and frees any associate memory for mii bus.
> - */
> -void axienet_mdio_teardown(struct axienet_common *lp)
> +static void axienet_mdio_remove(struct auxiliary_device *auxdev)
> {
> - mdiobus_unregister(lp->mii_bus);
> - mdiobus_free(lp->mii_bus);
> - lp->mii_bus = NULL;
> + struct mii_bus *mii_bus = auxiliary_get_drvdata(auxdev);
> +
> + mdiobus_unregister(mii_bus);
> + mdiobus_free(mii_bus);
> }
> +
> +static const struct auxiliary_device_id xilinx_axienet_mdio_id_table[] = {
> + { .name = KBUILD_MODNAME ".mdio", },
> + { },
> +};
> +MODULE_DEVICE_TABLE(auxiliary, xilinx_axienet_mdio_id_table);
> +
> +struct auxiliary_driver xilinx_axienet_mdio = {
> + .name = "mdio",
> + .id_table = xilinx_axienet_mdio_id_table,
> + .probe = axienet_mdio_probe,
> + .remove = axienet_mdio_remove,
> +};
> --
> 2.35.1.1320.gc452695387.dirty
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH net-next v3 2/7] net: axienet: Use ioread32/iowrite32 directly
2025-07-28 22:18 ` [PATCH net-next v3 2/7] net: axienet: Use ioread32/iowrite32 directly Sean Anderson
@ 2025-07-29 4:28 ` Subbaraya Sundeep
0 siblings, 0 replies; 12+ messages in thread
From: Subbaraya Sundeep @ 2025-07-29 4:28 UTC (permalink / raw)
To: Sean Anderson
Cc: Radhey Shyam Pandey, Andrew Lunn, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, netdev, linux-kernel,
Greg Kroah-Hartman, Michal Simek, linux-arm-kernel,
Leon Romanovsky
Hi,
On 2025-07-28 at 22:18:18, Sean Anderson (sean.anderson@linux.dev) wrote:
> In preparation for splitting the MDIO bus into a separate driver,
> convert all register reads/writes to use ioread32/iowrite32 directly
> instead of using the axienet_ior/iow helpers. While we're at it, clean
> up the register calculations a bit.
>
> Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
> ---
>
> Changes in v3:
> - New
>
> .../net/ethernet/xilinx/xilinx_axienet_mdio.c | 43 +++++++++----------
> 1 file changed, 20 insertions(+), 23 deletions(-)
>
> diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
> index 9ca2643c921e..16f3581390dd 100644
> --- a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
> +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
> @@ -32,7 +32,7 @@ static int axienet_mdio_wait_until_ready(struct axienet_local *lp)
> {
> u32 val;
>
> - return readx_poll_timeout(axinet_ior_read_mcr, lp,
> + return readx_poll_timeout(ioread32, lp->regs + XAE_MDIO_MCR_OFFSET,
> val, val & XAE_MDIO_MCR_READY_MASK,
> 1, 20000);
> }
> @@ -45,8 +45,8 @@ static int axienet_mdio_wait_until_ready(struct axienet_local *lp)
> */
> static void axienet_mdio_mdc_enable(struct axienet_local *lp)
> {
> - axienet_iow(lp, XAE_MDIO_MC_OFFSET,
> - ((u32)lp->mii_clk_div | XAE_MDIO_MC_MDIOEN_MASK));
> + iowrite32((u32)lp->mii_clk_div | XAE_MDIO_MC_MDIOEN_MASK,
> + lp->regs + XAE_MDIO_MC_OFFSET);
> }
>
> /**
> @@ -59,9 +59,9 @@ static void axienet_mdio_mdc_disable(struct axienet_local *lp)
> {
> u32 mc_reg;
>
> - mc_reg = axienet_ior(lp, XAE_MDIO_MC_OFFSET);
> - axienet_iow(lp, XAE_MDIO_MC_OFFSET,
> - (mc_reg & ~XAE_MDIO_MC_MDIOEN_MASK));
> + mc_reg = ioread32(lp->regs + XAE_MDIO_MC_OFFSET);
> + iowrite32(mc_reg & ~XAE_MDIO_MC_MDIOEN_MASK,
> + lp->regs + XAE_MDIO_MC_OFFSET);
> }
>
> /**
> @@ -90,13 +90,11 @@ static int axienet_mdio_read(struct mii_bus *bus, int phy_id, int reg)
> return ret;
> }
>
> - axienet_iow(lp, XAE_MDIO_MCR_OFFSET,
> - (((phy_id << XAE_MDIO_MCR_PHYAD_SHIFT) &
> - XAE_MDIO_MCR_PHYAD_MASK) |
> - ((reg << XAE_MDIO_MCR_REGAD_SHIFT) &
> - XAE_MDIO_MCR_REGAD_MASK) |
> - XAE_MDIO_MCR_INITIATE_MASK |
> - XAE_MDIO_MCR_OP_READ_MASK));
> + rc = FIELD_PREP(XAE_MDIO_MCR_PHYAD_MASK, phy_id) |
> + FIELD_PREP(XAE_MDIO_MCR_REGAD_MASK, reg) |
> + XAE_MDIO_MCR_INITIATE_MASK |
> + XAE_MDIO_MCR_OP_READ_MASK;
> + iowrite32(rc, lp->regs + XAE_MDIO_MCR_OFFSET);
nit: remove XAE_MDIO_MCR_REGAD_SHIFT macros in header file too in this patch.
Thanks,
Sundeep
>
> ret = axienet_mdio_wait_until_ready(lp);
> if (ret < 0) {
> @@ -104,7 +102,7 @@ static int axienet_mdio_read(struct mii_bus *bus, int phy_id, int reg)
> return ret;
> }
>
> - rc = axienet_ior(lp, XAE_MDIO_MRD_OFFSET) & 0x0000FFFF;
> + rc = ioread32(lp->regs + XAE_MDIO_MRD_OFFSET) & 0x0000FFFF;
>
> dev_dbg(lp->dev, "axienet_mdio_read(phy_id=%i, reg=%x) == %x\n",
> phy_id, reg, rc);
> @@ -129,8 +127,9 @@ static int axienet_mdio_read(struct mii_bus *bus, int phy_id, int reg)
> static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg,
> u16 val)
> {
> - int ret;
> struct axienet_local *lp = bus->priv;
> + int ret;
> + u32 mcr;
>
> dev_dbg(lp->dev, "axienet_mdio_write(phy_id=%i, reg=%x, val=%x)\n",
> phy_id, reg, val);
> @@ -143,14 +142,12 @@ static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg,
> return ret;
> }
>
> - axienet_iow(lp, XAE_MDIO_MWD_OFFSET, (u32)val);
> - axienet_iow(lp, XAE_MDIO_MCR_OFFSET,
> - (((phy_id << XAE_MDIO_MCR_PHYAD_SHIFT) &
> - XAE_MDIO_MCR_PHYAD_MASK) |
> - ((reg << XAE_MDIO_MCR_REGAD_SHIFT) &
> - XAE_MDIO_MCR_REGAD_MASK) |
> - XAE_MDIO_MCR_INITIATE_MASK |
> - XAE_MDIO_MCR_OP_WRITE_MASK));
> + iowrite32(val, lp->regs + XAE_MDIO_MWD_OFFSET);
> + mcr = FIELD_PREP(XAE_MDIO_MCR_PHYAD_MASK, phy_id) |
> + FIELD_PREP(XAE_MDIO_MCR_REGAD_MASK, reg) |
> + XAE_MDIO_MCR_INITIATE_MASK |
> + XAE_MDIO_MCR_OP_WRITE_MASK;
> + iowrite32(mcr, lp->regs + XAE_MDIO_MCR_OFFSET);
>
> ret = axienet_mdio_wait_until_ready(lp);
> if (ret < 0) {
> --
> 2.35.1.1320.gc452695387.dirty
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH net-next v3 3/7] net: axienet: Use MDIO bus device in prints
2025-07-28 22:18 ` [PATCH net-next v3 3/7] net: axienet: Use MDIO bus device in prints Sean Anderson
@ 2025-07-29 9:07 ` kernel test robot
0 siblings, 0 replies; 12+ messages in thread
From: kernel test robot @ 2025-07-29 9:07 UTC (permalink / raw)
To: Sean Anderson, Radhey Shyam Pandey, Andrew Lunn, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, netdev
Cc: llvm, oe-kbuild-all, linux-kernel, Greg Kroah-Hartman,
Michal Simek, linux-arm-kernel, Leon Romanovsky, Sean Anderson
Hi Sean,
kernel test robot noticed the following build warnings:
[auto build test WARNING on net-next/main]
url: https://github.com/intel-lab-lkp/linux/commits/Sean-Anderson/net-axienet-Fix-resource-release-ordering/20250729-062108
base: net-next/main
patch link: https://lore.kernel.org/r/20250728221823.11968-4-sean.anderson%40linux.dev
patch subject: [PATCH net-next v3 3/7] net: axienet: Use MDIO bus device in prints
config: arm-randconfig-002-20250729 (https://download.01.org/0day-ci/archive/20250729/202507291640.Zr1m8oli-lkp@intel.com/config)
compiler: clang version 19.1.7 (https://github.com/llvm/llvm-project cd708029e0b2869e80abe31ddb175f7c35361f90)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250729/202507291640.Zr1m8oli-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202507291640.Zr1m8oli-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> Warning: drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c:172 function parameter 'bus' not described in 'axienet_mdio_enable'
>> Warning: drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c:172 Excess function parameter 'lp' description in 'axienet_mdio_enable'
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 12+ messages in thread
* RE: [PATCH net-next v3 1/7] net: axienet: Fix resource release ordering
2025-07-28 22:18 ` [PATCH net-next v3 1/7] net: axienet: Fix resource release ordering Sean Anderson
@ 2025-07-30 9:27 ` Gupta, Suraj
0 siblings, 0 replies; 12+ messages in thread
From: Gupta, Suraj @ 2025-07-30 9:27 UTC (permalink / raw)
To: Sean Anderson, Pandey, Radhey Shyam, Andrew Lunn,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, Greg Kroah-Hartman, Simek, Michal,
linux-arm-kernel@lists.infradead.org, Leon Romanovsky
[Public]
> -----Original Message-----
> From: Sean Anderson <sean.anderson@linux.dev>
> Sent: Tuesday, July 29, 2025 3:48 AM
> To: Pandey, Radhey Shyam <radhey.shyam.pandey@amd.com>; Andrew Lunn
> <andrew+netdev@lunn.ch>; David S . Miller <davem@davemloft.net>; Eric Dumazet
> <edumazet@google.com>; Jakub Kicinski <kuba@kernel.org>; Paolo Abeni
> <pabeni@redhat.com>; netdev@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org; Greg Kroah-Hartman
> <gregkh@linuxfoundation.org>; Simek, Michal <michal.simek@amd.com>; linux-arm-
> kernel@lists.infradead.org; Leon Romanovsky <leon@kernel.org>; Sean Anderson
> <sean.anderson@linux.dev>
> Subject: [PATCH net-next v3 1/7] net: axienet: Fix resource release ordering
>
> Caution: This message originated from an External Source. Use proper caution when
> opening attachments, clicking links, or responding.
>
>
> Device-managed resources are released after manually-managed resources.
> Therefore, once any manually-managed resource is acquired, all further resources
> must be manually-managed too.
>
> Convert all resources before the MDIO bus is created into device-managed resources.
> In all cases but one there are already devm variants available.
>
> Fixes: 46aa27df8853 ("net: axienet: Use devm_* calls")
> Signed-off-by: Sean Anderson <sean.anderson@linux.dev>
Looks fine to me.
Reviewed-by: Suraj Gupta <suraj.gupta2@amd.com>
> ---
>
> (no changes since v1)
>
> .../net/ethernet/xilinx/xilinx_axienet_main.c | 89 ++++++++-----------
> 1 file changed, 37 insertions(+), 52 deletions(-)
>
> diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
> b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
> index 6011d7eae0c7..1f277e5e4a62 100644
> --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
> +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
> @@ -2744,6 +2744,11 @@ static void axienet_dma_err_handler(struct work_struct
> *work)
> axienet_setoptions(ndev, lp->options); }
>
> +static void axienet_disable_misc(void *clocks) {
> + clk_bulk_disable_unprepare(XAE_NUM_MISC_CLOCKS, clocks); }
> +
> /**
> * axienet_probe - Axi Ethernet probe function.
> * @pdev: Pointer to platform device structure.
> @@ -2767,7 +2772,7 @@ static int axienet_probe(struct platform_device *pdev)
> int addr_width = 32;
> u32 value;
>
> - ndev = alloc_etherdev(sizeof(*lp));
> + ndev = devm_alloc_etherdev(&pdev->dev, sizeof(*lp));
> if (!ndev)
> return -ENOMEM;
>
> @@ -2795,22 +2800,17 @@ static int axienet_probe(struct platform_device *pdev)
> seqcount_mutex_init(&lp->hw_stats_seqcount, &lp->stats_lock);
> INIT_DEFERRABLE_WORK(&lp->stats_work, axienet_refresh_stats);
>
> - lp->axi_clk = devm_clk_get_optional(&pdev->dev, "s_axi_lite_clk");
> + lp->axi_clk = devm_clk_get_optional_enabled(&pdev->dev,
> + "s_axi_lite_clk");
> if (!lp->axi_clk) {
> /* For backward compatibility, if named AXI clock is not present,
> * treat the first clock specified as the AXI clock.
> */
> - lp->axi_clk = devm_clk_get_optional(&pdev->dev, NULL);
> - }
> - if (IS_ERR(lp->axi_clk)) {
> - ret = PTR_ERR(lp->axi_clk);
> - goto free_netdev;
> - }
> - ret = clk_prepare_enable(lp->axi_clk);
> - if (ret) {
> - dev_err(&pdev->dev, "Unable to enable AXI clock: %d\n", ret);
> - goto free_netdev;
> + lp->axi_clk = devm_clk_get_optional_enabled(&pdev->dev,
> + NULL);
> }
> + if (IS_ERR(lp->axi_clk))
> + return dev_err_probe(&pdev->dev, PTR_ERR(lp->axi_clk),
> + "could not get AXI clock\n");
>
> lp->misc_clks[0].id = "axis_clk";
> lp->misc_clks[1].id = "ref_clk"; @@ -2818,18 +2818,23 @@ static int
> axienet_probe(struct platform_device *pdev)
>
> ret = devm_clk_bulk_get_optional(&pdev->dev, XAE_NUM_MISC_CLOCKS, lp-
> >misc_clks);
> if (ret)
> - goto cleanup_clk;
> + return dev_err_probe(&pdev->dev, ret,
> + "could not get misc. clocks\n");
>
> ret = clk_bulk_prepare_enable(XAE_NUM_MISC_CLOCKS, lp->misc_clks);
> if (ret)
> - goto cleanup_clk;
> + return dev_err_probe(&pdev->dev, ret,
> + "could not enable misc. clocks\n");
> +
> + ret = devm_add_action_or_reset(&pdev->dev, axienet_disable_misc,
> + lp->misc_clks);
> + if (ret)
> + return ret;
>
> /* Map device registers */
> lp->regs = devm_platform_get_and_ioremap_resource(pdev, 0, ðres);
> - if (IS_ERR(lp->regs)) {
> - ret = PTR_ERR(lp->regs);
> - goto cleanup_clk;
> - }
> + if (IS_ERR(lp->regs))
> + return PTR_ERR(lp->regs);
> lp->regs_start = ethres->start;
>
> /* Setup checksum offload, but default to off if not specified */ @@ -2898,19
> +2903,17 @@ static int axienet_probe(struct platform_device *pdev)
> lp->phy_mode = PHY_INTERFACE_MODE_1000BASEX;
> break;
> default:
> - ret = -EINVAL;
> - goto cleanup_clk;
> + return -EINVAL;
> }
> } else {
> ret = of_get_phy_mode(pdev->dev.of_node, &lp->phy_mode);
> if (ret)
> - goto cleanup_clk;
> + return ret;
> }
> if (lp->switch_x_sgmii && lp->phy_mode != PHY_INTERFACE_MODE_SGMII
> &&
> lp->phy_mode != PHY_INTERFACE_MODE_1000BASEX) {
> dev_err(&pdev->dev, "xlnx,switch-x-sgmii only supported with SGMII or
> 1000BaseX\n");
> - ret = -EINVAL;
> - goto cleanup_clk;
> + return -EINVAL;
> }
>
> if (!of_property_present(pdev->dev.of_node, "dmas")) { @@ -2925,7 +2928,7
> @@ static int axienet_probe(struct platform_device *pdev)
> dev_err(&pdev->dev,
> "unable to get DMA resource\n");
> of_node_put(np);
> - goto cleanup_clk;
> + return ret;
> }
> lp->dma_regs = devm_ioremap_resource(&pdev->dev,
> &dmares); @@ -2942,19 +2945,17 @@ static
> int axienet_probe(struct platform_device *pdev)
> }
> if (IS_ERR(lp->dma_regs)) {
> dev_err(&pdev->dev, "could not map DMA regs\n");
> - ret = PTR_ERR(lp->dma_regs);
> - goto cleanup_clk;
> + return PTR_ERR(lp->dma_regs);
> }
> if (lp->rx_irq <= 0 || lp->tx_irq <= 0) {
> dev_err(&pdev->dev, "could not determine irqs\n");
> - ret = -ENOMEM;
> - goto cleanup_clk;
> + return -ENOMEM;
> }
>
> /* Reset core now that clocks are enabled, prior to accessing MDIO */
> ret = __axienet_device_reset(lp);
> if (ret)
> - goto cleanup_clk;
> + return ret;
>
> /* Autodetect the need for 64-bit DMA pointers.
> * When the IP is configured for a bus width bigger than 32 bits, @@ -
> 2981,14 +2982,13 @@ static int axienet_probe(struct platform_device *pdev)
> }
> if (!IS_ENABLED(CONFIG_64BIT) && lp->features &
> XAE_FEATURE_DMA_64BIT) {
> dev_err(&pdev->dev, "64-bit addressable DMA is not compatible with
> 32-bit architecture\n");
> - ret = -EINVAL;
> - goto cleanup_clk;
> + return -EINVAL;
> }
>
> ret = dma_set_mask_and_coherent(&pdev->dev,
> DMA_BIT_MASK(addr_width));
> if (ret) {
> dev_err(&pdev->dev, "No suitable DMA available\n");
> - goto cleanup_clk;
> + return ret;
> }
> netif_napi_add(ndev, &lp->napi_rx, axienet_rx_poll);
> netif_napi_add(ndev, &lp->napi_tx, axienet_tx_poll); @@ -2998,15 +2998,12
> @@ static int axienet_probe(struct platform_device *pdev)
>
> lp->eth_irq = platform_get_irq_optional(pdev, 0);
> if (lp->eth_irq < 0 && lp->eth_irq != -ENXIO) {
> - ret = lp->eth_irq;
> - goto cleanup_clk;
> + return lp->eth_irq;
> }
> tx_chan = dma_request_chan(lp->dev, "tx_chan0");
> - if (IS_ERR(tx_chan)) {
> - ret = PTR_ERR(tx_chan);
> - dev_err_probe(lp->dev, ret, "No Ethernet DMA (TX) channel found\n");
> - goto cleanup_clk;
> - }
> + if (IS_ERR(tx_chan))
> + return dev_err_probe(lp->dev, PTR_ERR(tx_chan),
> + "No Ethernet DMA (TX)
> + channel found\n");
>
> cfg.reset = 1;
> /* As name says VDMA but it has support for DMA channel reset */ @@ -
> 3014,7 +3011,7 @@ static int axienet_probe(struct platform_device *pdev)
> if (ret < 0) {
> dev_err(&pdev->dev, "Reset channel failed\n");
> dma_release_channel(tx_chan);
> - goto cleanup_clk;
> + return ret;
> }
>
> dma_release_channel(tx_chan); @@ -3119,13 +3116,6 @@ static int
> axienet_probe(struct platform_device *pdev)
> put_device(&lp->pcs_phy->dev);
> if (lp->mii_bus)
> axienet_mdio_teardown(lp);
> -cleanup_clk:
> - clk_bulk_disable_unprepare(XAE_NUM_MISC_CLOCKS, lp->misc_clks);
> - clk_disable_unprepare(lp->axi_clk);
> -
> -free_netdev:
> - free_netdev(ndev);
> -
> return ret;
> }
>
> @@ -3143,11 +3133,6 @@ static void axienet_remove(struct platform_device *pdev)
> put_device(&lp->pcs_phy->dev);
>
> axienet_mdio_teardown(lp);
> -
> - clk_bulk_disable_unprepare(XAE_NUM_MISC_CLOCKS, lp->misc_clks);
> - clk_disable_unprepare(lp->axi_clk);
> -
> - free_netdev(ndev);
> }
>
> static void axienet_shutdown(struct platform_device *pdev)
> --
> 2.35.1.1320.gc452695387.dirty
>
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2025-07-30 9:30 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-28 22:18 [PATCH net-next v3 0/7] net: axienet: Fix deferred probe loop Sean Anderson
2025-07-28 22:18 ` [PATCH net-next v3 1/7] net: axienet: Fix resource release ordering Sean Anderson
2025-07-30 9:27 ` Gupta, Suraj
2025-07-28 22:18 ` [PATCH net-next v3 2/7] net: axienet: Use ioread32/iowrite32 directly Sean Anderson
2025-07-29 4:28 ` Subbaraya Sundeep
2025-07-28 22:18 ` [PATCH net-next v3 3/7] net: axienet: Use MDIO bus device in prints Sean Anderson
2025-07-29 9:07 ` kernel test robot
2025-07-28 22:18 ` [PATCH net-next v3 4/7] net: axienet: Simplify axienet_mdio_setup Sean Anderson
2025-07-28 22:18 ` [PATCH net-next v3 5/7] net: axienet: Use device variable in probe Sean Anderson
2025-07-28 22:18 ` [PATCH net-next v3 6/7] net: axienet: Rearrange lifetime functions Sean Anderson
2025-07-28 22:18 ` [PATCH net-next v3 7/7] net: axienet: Split into MAC and MDIO drivers Sean Anderson
2025-07-29 4:16 ` Subbaraya Sundeep
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).