diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 744d6585a949..543e566425c1 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -5678,6 +5678,24 @@ static int mvneta_probe(struct platform_device *pdev) "use SW buffer management\n"); mvneta_bm_put(pp->bm_priv); pp->bm_priv = NULL; + } else if (!device_link_add(&pdev->dev, + &pp->bm_priv->pdev->dev, + DL_FLAG_AUTOREMOVE_CONSUMER)) { + /* + * Link guarantees BM resumes before mvneta. + * Without it, BM may not be ready when + * mvneta_bm_port_init() runs on resume, + * causing stale buffer addresses and a crash. + * Fall back to SW management to be safe. + */ + dev_warn(&pdev->dev, + "failed to link to BM, use SW buffer management\n"); + mvneta_bm_pool_destroy(pp->bm_priv, + pp->pool_long, 1 << pp->id); + mvneta_bm_pool_destroy(pp->bm_priv, + pp->pool_short, 1 << pp->id); + mvneta_bm_put(pp->bm_priv); + pp->bm_priv = NULL; } } /* Set RX packet offset correction for platforms, whose diff --git a/drivers/net/ethernet/marvell/mvneta_bm.c b/drivers/net/ethernet/marvell/mvneta_bm.c index 6bb380494919..e0c693c0a910 100644 --- a/drivers/net/ethernet/marvell/mvneta_bm.c +++ b/drivers/net/ethernet/marvell/mvneta_bm.c @@ -129,6 +129,7 @@ static int mvneta_bm_pool_create(struct mvneta_bm *priv, if (!IS_ALIGNED((u32)bm_pool->virt_addr, MVNETA_BM_POOL_PTR_ALIGN)) { dma_free_coherent(&pdev->dev, size_bytes, bm_pool->virt_addr, bm_pool->phys_addr); + bm_pool->virt_addr = NULL; dev_err(&pdev->dev, "BM pool %d is not %d bytes aligned\n", bm_pool->id, MVNETA_BM_POOL_PTR_ALIGN); return -ENOMEM; @@ -139,6 +140,7 @@ static int mvneta_bm_pool_create(struct mvneta_bm *priv, if (err < 0) { dma_free_coherent(&pdev->dev, size_bytes, bm_pool->virt_addr, bm_pool->phys_addr); + bm_pool->virt_addr = NULL; return err; } @@ -477,6 +479,75 @@ static void mvneta_bm_remove(struct platform_device *pdev) clk_disable_unprepare(priv->clk); } +static int mvneta_bm_suspend(struct device *dev) +{ + struct mvneta_bm *priv = dev_get_drvdata(dev); + int i; + + /* Drain buffers and free pool resources while BM is still clocked */ + for (i = 0; i < MVNETA_BM_POOLS_NUM; i++) { + struct mvneta_bm_pool *bm_pool = &priv->bm_pools[i]; + int size_bytes; + + if (bm_pool->type == MVNETA_BM_FREE) + continue; + + mvneta_bm_bufs_free(priv, bm_pool, bm_pool->port_map); + if (bm_pool->hwbm_pool.buf_num) + dev_warn(&priv->pdev->dev, + "pool %d: %d buffers not freed\n", + bm_pool->id, bm_pool->hwbm_pool.buf_num); + + mvneta_bm_pool_disable(priv, bm_pool->id); + + if (bm_pool->virt_addr) { + size_bytes = sizeof(u32) * bm_pool->hwbm_pool.size; + dma_free_coherent(&priv->pdev->dev, size_bytes, + bm_pool->virt_addr, + bm_pool->phys_addr); + bm_pool->virt_addr = NULL; + } + /* + * Safe to destroy: device_link guarantees all mvneta ports + * have already suspended, so no hwbm_pool_add() can be in + * progress holding buf_lock. Pairs with mutex_init() in + * mvneta_bm_pool_use() on resume. + */ + mutex_destroy(&bm_pool->hwbm_pool.buf_lock); + bm_pool->type = MVNETA_BM_FREE; + } + + mvneta_bm_write(priv, MVNETA_BM_COMMAND_REG, MVNETA_BM_STOP_MASK); + clk_disable_unprepare(priv->clk); + return 0; +} + +static int mvneta_bm_resume(struct device *dev) +{ + struct mvneta_bm *priv = dev_get_drvdata(dev); + int i, err; + + err = clk_prepare_enable(priv->clk); + if (err) + return err; + + /* Reinitialize BM hardware; pools are refilled by mvneta_resume() */ + mvneta_bm_default_set(priv); + + /* Restore pool registers lost during clock gating */ + for (i = 0; i < MVNETA_BM_POOLS_NUM; i++) { + mvneta_bm_write(priv, MVNETA_BM_POOL_READ_PTR_REG(i), 0); + mvneta_bm_write(priv, MVNETA_BM_POOL_WRITE_PTR_REG(i), 0); + mvneta_bm_write(priv, MVNETA_BM_POOL_SIZE_REG(i), + priv->bm_pools[i].hwbm_pool.size); + } + + mvneta_bm_write(priv, MVNETA_BM_COMMAND_REG, MVNETA_BM_START_MASK); + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(mvneta_bm_pm_ops, mvneta_bm_suspend, mvneta_bm_resume); + static const struct of_device_id mvneta_bm_match[] = { { .compatible = "marvell,armada-380-neta-bm" }, { } @@ -489,6 +560,7 @@ static struct platform_driver mvneta_bm_driver = { .driver = { .name = MVNETA_BM_DRIVER_NAME, .of_match_table = mvneta_bm_match, + .pm = pm_sleep_ptr(&mvneta_bm_pm_ops), }, };