Commit 0e2d1af3 authored by David S. Miller's avatar David S. Miller

Merge branch 'cpsw-fixes'

Johan Hovold says:

====================
net: cpsw: fix leaks and probe deferral

This series fixes as number of leaks and issues in the cpsw probe-error
and driver-unbind paths, some which specifically prevented deferred
probing.

v2
 - Keep platform device runtime-resumed throughout probe instead of
   resuming in the probe error path as suggested by Grygorii (patch
   1/7).

 - Runtime-resume platform device before registering any children in
   order to make sure it is synchronously suspended after deregistering
   children in the error path (patch 3/7).
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 06ba3b21 23a09873
...@@ -2375,8 +2375,11 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, ...@@ -2375,8 +2375,11 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
* to the PHY is the Ethernet MAC DT node. * to the PHY is the Ethernet MAC DT node.
*/ */
ret = of_phy_register_fixed_link(slave_node); ret = of_phy_register_fixed_link(slave_node);
if (ret) if (ret) {
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "failed to register fixed-link phy: %d\n", ret);
return ret; return ret;
}
slave_data->phy_node = of_node_get(slave_node); slave_data->phy_node = of_node_get(slave_node);
} else if (parp) { } else if (parp) {
u32 phyid; u32 phyid;
...@@ -2397,6 +2400,7 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, ...@@ -2397,6 +2400,7 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
} }
snprintf(slave_data->phy_id, sizeof(slave_data->phy_id), snprintf(slave_data->phy_id, sizeof(slave_data->phy_id),
PHY_ID_FMT, mdio->name, phyid); PHY_ID_FMT, mdio->name, phyid);
put_device(&mdio->dev);
} else { } else {
dev_err(&pdev->dev, dev_err(&pdev->dev,
"No slave[%d] phy_id, phy-handle, or fixed-link property\n", "No slave[%d] phy_id, phy-handle, or fixed-link property\n",
...@@ -2440,6 +2444,46 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, ...@@ -2440,6 +2444,46 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data,
return 0; return 0;
} }
static void cpsw_remove_dt(struct platform_device *pdev)
{
struct net_device *ndev = platform_get_drvdata(pdev);
struct cpsw_common *cpsw = ndev_to_cpsw(ndev);
struct cpsw_platform_data *data = &cpsw->data;
struct device_node *node = pdev->dev.of_node;
struct device_node *slave_node;
int i = 0;
for_each_available_child_of_node(node, slave_node) {
struct cpsw_slave_data *slave_data = &data->slave_data[i];
if (strcmp(slave_node->name, "slave"))
continue;
if (of_phy_is_fixed_link(slave_node)) {
struct phy_device *phydev;
phydev = of_phy_find_device(slave_node);
if (phydev) {
fixed_phy_unregister(phydev);
/* Put references taken by
* of_phy_find_device() and
* of_phy_register_fixed_link().
*/
phy_device_free(phydev);
phy_device_free(phydev);
}
}
of_node_put(slave_data->phy_node);
i++;
if (i == data->slaves)
break;
}
of_platform_depopulate(&pdev->dev);
}
static int cpsw_probe_dual_emac(struct cpsw_priv *priv) static int cpsw_probe_dual_emac(struct cpsw_priv *priv)
{ {
struct cpsw_common *cpsw = priv->cpsw; struct cpsw_common *cpsw = priv->cpsw;
...@@ -2547,6 +2591,9 @@ static int cpsw_probe(struct platform_device *pdev) ...@@ -2547,6 +2591,9 @@ static int cpsw_probe(struct platform_device *pdev)
int irq; int irq;
cpsw = devm_kzalloc(&pdev->dev, sizeof(struct cpsw_common), GFP_KERNEL); cpsw = devm_kzalloc(&pdev->dev, sizeof(struct cpsw_common), GFP_KERNEL);
if (!cpsw)
return -ENOMEM;
cpsw->dev = &pdev->dev; cpsw->dev = &pdev->dev;
ndev = alloc_etherdev_mq(sizeof(struct cpsw_priv), CPSW_MAX_QUEUES); ndev = alloc_etherdev_mq(sizeof(struct cpsw_priv), CPSW_MAX_QUEUES);
...@@ -2584,11 +2631,19 @@ static int cpsw_probe(struct platform_device *pdev) ...@@ -2584,11 +2631,19 @@ static int cpsw_probe(struct platform_device *pdev)
/* Select default pin state */ /* Select default pin state */
pinctrl_pm_select_default_state(&pdev->dev); pinctrl_pm_select_default_state(&pdev->dev);
if (cpsw_probe_dt(&cpsw->data, pdev)) { /* Need to enable clocks with runtime PM api to access module
dev_err(&pdev->dev, "cpsw: platform data missing\n"); * registers
ret = -ENODEV; */
ret = pm_runtime_get_sync(&pdev->dev);
if (ret < 0) {
pm_runtime_put_noidle(&pdev->dev);
goto clean_runtime_disable_ret; goto clean_runtime_disable_ret;
} }
ret = cpsw_probe_dt(&cpsw->data, pdev);
if (ret)
goto clean_dt_ret;
data = &cpsw->data; data = &cpsw->data;
cpsw->rx_ch_num = 1; cpsw->rx_ch_num = 1;
cpsw->tx_ch_num = 1; cpsw->tx_ch_num = 1;
...@@ -2608,7 +2663,7 @@ static int cpsw_probe(struct platform_device *pdev) ...@@ -2608,7 +2663,7 @@ static int cpsw_probe(struct platform_device *pdev)
GFP_KERNEL); GFP_KERNEL);
if (!cpsw->slaves) { if (!cpsw->slaves) {
ret = -ENOMEM; ret = -ENOMEM;
goto clean_runtime_disable_ret; goto clean_dt_ret;
} }
for (i = 0; i < data->slaves; i++) for (i = 0; i < data->slaves; i++)
cpsw->slaves[i].slave_num = i; cpsw->slaves[i].slave_num = i;
...@@ -2620,7 +2675,7 @@ static int cpsw_probe(struct platform_device *pdev) ...@@ -2620,7 +2675,7 @@ static int cpsw_probe(struct platform_device *pdev)
if (IS_ERR(clk)) { if (IS_ERR(clk)) {
dev_err(priv->dev, "fck is not found\n"); dev_err(priv->dev, "fck is not found\n");
ret = -ENODEV; ret = -ENODEV;
goto clean_runtime_disable_ret; goto clean_dt_ret;
} }
cpsw->bus_freq_mhz = clk_get_rate(clk) / 1000000; cpsw->bus_freq_mhz = clk_get_rate(clk) / 1000000;
...@@ -2628,26 +2683,17 @@ static int cpsw_probe(struct platform_device *pdev) ...@@ -2628,26 +2683,17 @@ static int cpsw_probe(struct platform_device *pdev)
ss_regs = devm_ioremap_resource(&pdev->dev, ss_res); ss_regs = devm_ioremap_resource(&pdev->dev, ss_res);
if (IS_ERR(ss_regs)) { if (IS_ERR(ss_regs)) {
ret = PTR_ERR(ss_regs); ret = PTR_ERR(ss_regs);
goto clean_runtime_disable_ret; goto clean_dt_ret;
} }
cpsw->regs = ss_regs; cpsw->regs = ss_regs;
/* Need to enable clocks with runtime PM api to access module
* registers
*/
ret = pm_runtime_get_sync(&pdev->dev);
if (ret < 0) {
pm_runtime_put_noidle(&pdev->dev);
goto clean_runtime_disable_ret;
}
cpsw->version = readl(&cpsw->regs->id_ver); cpsw->version = readl(&cpsw->regs->id_ver);
pm_runtime_put_sync(&pdev->dev);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1); res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
cpsw->wr_regs = devm_ioremap_resource(&pdev->dev, res); cpsw->wr_regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(cpsw->wr_regs)) { if (IS_ERR(cpsw->wr_regs)) {
ret = PTR_ERR(cpsw->wr_regs); ret = PTR_ERR(cpsw->wr_regs);
goto clean_runtime_disable_ret; goto clean_dt_ret;
} }
memset(&dma_params, 0, sizeof(dma_params)); memset(&dma_params, 0, sizeof(dma_params));
...@@ -2684,7 +2730,7 @@ static int cpsw_probe(struct platform_device *pdev) ...@@ -2684,7 +2730,7 @@ static int cpsw_probe(struct platform_device *pdev)
default: default:
dev_err(priv->dev, "unknown version 0x%08x\n", cpsw->version); dev_err(priv->dev, "unknown version 0x%08x\n", cpsw->version);
ret = -ENODEV; ret = -ENODEV;
goto clean_runtime_disable_ret; goto clean_dt_ret;
} }
for (i = 0; i < cpsw->data.slaves; i++) { for (i = 0; i < cpsw->data.slaves; i++) {
struct cpsw_slave *slave = &cpsw->slaves[i]; struct cpsw_slave *slave = &cpsw->slaves[i];
...@@ -2713,7 +2759,7 @@ static int cpsw_probe(struct platform_device *pdev) ...@@ -2713,7 +2759,7 @@ static int cpsw_probe(struct platform_device *pdev)
if (!cpsw->dma) { if (!cpsw->dma) {
dev_err(priv->dev, "error initializing dma\n"); dev_err(priv->dev, "error initializing dma\n");
ret = -ENOMEM; ret = -ENOMEM;
goto clean_runtime_disable_ret; goto clean_dt_ret;
} }
cpsw->txch[0] = cpdma_chan_create(cpsw->dma, 0, cpsw_tx_handler, 0); cpsw->txch[0] = cpdma_chan_create(cpsw->dma, 0, cpsw_tx_handler, 0);
...@@ -2811,16 +2857,23 @@ static int cpsw_probe(struct platform_device *pdev) ...@@ -2811,16 +2857,23 @@ static int cpsw_probe(struct platform_device *pdev)
ret = cpsw_probe_dual_emac(priv); ret = cpsw_probe_dual_emac(priv);
if (ret) { if (ret) {
cpsw_err(priv, probe, "error probe slave 2 emac interface\n"); cpsw_err(priv, probe, "error probe slave 2 emac interface\n");
goto clean_ale_ret; goto clean_unregister_netdev_ret;
} }
} }
pm_runtime_put(&pdev->dev);
return 0; return 0;
clean_unregister_netdev_ret:
unregister_netdev(ndev);
clean_ale_ret: clean_ale_ret:
cpsw_ale_destroy(cpsw->ale); cpsw_ale_destroy(cpsw->ale);
clean_dma_ret: clean_dma_ret:
cpdma_ctlr_destroy(cpsw->dma); cpdma_ctlr_destroy(cpsw->dma);
clean_dt_ret:
cpsw_remove_dt(pdev);
pm_runtime_put_sync(&pdev->dev);
clean_runtime_disable_ret: clean_runtime_disable_ret:
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
clean_ndev_ret: clean_ndev_ret:
...@@ -2846,7 +2899,7 @@ static int cpsw_remove(struct platform_device *pdev) ...@@ -2846,7 +2899,7 @@ static int cpsw_remove(struct platform_device *pdev)
cpsw_ale_destroy(cpsw->ale); cpsw_ale_destroy(cpsw->ale);
cpdma_ctlr_destroy(cpsw->dma); cpdma_ctlr_destroy(cpsw->dma);
of_platform_depopulate(&pdev->dev); cpsw_remove_dt(pdev);
pm_runtime_put_sync(&pdev->dev); pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
if (cpsw->data.dual_emac) if (cpsw->data.dual_emac)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment