[RFC] soc: imx: gpc: Unregister pgc children on remove

From: Leonard Crestez
Date: Tue Jul 03 2018 - 15:39:44 EST


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.crestez@xxxxxxx>
---
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