If the gpc device is removed the platform_devices for its imx-pgc-power-domains are still registered and trying to probe gpc again results in an error.
Fix this by iterating children inside imx_gpc_remove and calling platfrom_device_unregister. Signed-off-by: Leonard Crestez <leonard.cres...@nxp.com> --- drivers/soc/imx/gpc.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) This was prompted by the rejection of the following series: https://lkml.org/lkml/2018/7/2/534 Tested glxgears after rebinding gpc and etnaviv by echo to /sys/bus/platform/drivers/{imx-gpc,etnaviv-gpu}/{unbind,bind} When doing probe again it triggers a warning inside driver_links_drivers_bound: WARN_ON(link->status != DL_STATE_CONSUMER_PROBE); This seems to be because devicelink code does not expect consumers to bind before suppliers but GPC code adds PGC children during its probe function and they register a device-link to their still-probing parent. The warning doesn't trigger on normal boot because the pgc driver is registered after gpc and probing is not nested. The warning can be made to reproduce in a normal boot by making imx_gpc_probe return -EPROBE_DEFER once (no other changes required). This does not seem to be a valid scenario for device links so fix this by adding a check in imx_pgc_power_domain_probe to defer if parent is not probed. This check is not very nice, device_is_bound seems like something that should be internal to the driver core. diff --git a/drivers/soc/imx/gpc.c b/drivers/soc/imx/gpc.c index 32f0748fd067..b4acdfd3cffd 100644 --- a/drivers/soc/imx/gpc.c +++ b/drivers/soc/imx/gpc.c @@ -182,10 +182,13 @@ static int imx_pgc_power_domain_probe(struct platform_device *pdev) { struct imx_pm_domain *domain = pdev->dev.platform_data; struct device *dev = &pdev->dev; int ret; + if (!device_is_bound(dev->parent)) + return -EPROBE_DEFER; + /* if this PD is associated with a DT node try to parse it */ if (dev->of_node) { ret = imx_pgc_parse_dt(dev, domain); if (ret) return ret; @@ -475,10 +478,19 @@ static int imx_gpc_probe(struct platform_device *pdev) } return 0; } +static int __imx_gpc_unregister_child(struct device *dev, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + + platform_device_unregister(pdev); + + return 0; +} + static int imx_gpc_remove(struct platform_device *pdev) { struct device_node *pgc_node; int ret; @@ -502,10 +514,13 @@ static int imx_gpc_remove(struct platform_device *pdev) imx_pgc_put_clocks(&imx_gpc_domains[GPC_PGC_DOMAIN_PU]); ret = pm_genpd_remove(&imx_gpc_domains[GPC_PGC_DOMAIN_ARM].base); if (ret) return ret; + } else { + device_for_each_child(&pdev->dev, NULL, + __imx_gpc_unregister_child); } return 0; } -- 2.17.1