From patchwork Sat Feb 15 11:34:43 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anatolij Gustschin X-Patchwork-Id: 236389 List-Id: U-Boot discussion From: agust at denx.de (Anatolij Gustschin) Date: Sat, 15 Feb 2020 12:34:43 +0100 Subject: [PATCH] power-domain: fix hang in endless loop on i.MX8 Message-ID: <20200215113443.6866-1-agust@denx.de> Currently when booting the kernel on i.MX8 U-Boot hangs in an endless loop when switching off dma, connectivity or lsio power domains during device removal. It hapens first when removing gpio0 (gpio at 5d080000) device, here its power domain device 'lsio_gpio0' is obtained for switching off power. Since the obtained 'lsio_gpio0' device is removed afterwards, its power domain is also switched off and here the parent power domain device 'lsio_power_domain' is optained for switching off the power. Thereafter, when the obtained 'lsio_power_domain' is removed, device_remove() removes its first child 'lsio_gpio0'. During this child removal the 'lsio_power_domain' device is obtained again for switching and when removing it later, the same child removal is repeated, so we are stuck in an endless loop. Below is a snippet from dm tree on i.MX8QXP for better illustration of the DM devices relationship: Class Index Probed Driver Name ----------------------------------------------------------- root 0 [ + ] root_driver root_driver ... simple_bus 0 [ + ] generic_simple_bus |-- imx8qx-pm power_doma 0 [ + ] imx8_power_domain | |-- lsio_power_domain power_doma 1 [ + ] imx8_power_domain | | |-- lsio_gpio0 power_doma 2 [ + ] imx8_power_domain | | |-- lsio_gpio1 Do not remove a power domain device if it is a parent of the currently controlled device. Fixes: 52edfed65de9 ("dm: core: device: switch off power domain after device removal") Signed-off-by: Anatolij Gustschin Reported-by: Oliver Graute Reported-by: Fabio Estevam Reviewed-by: Simon Glass Reviewed-by: Lokesh Vutla --- drivers/power/domain/power-domain-uclass.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/power/domain/power-domain-uclass.c b/drivers/power/domain/power-domain-uclass.c index d9c623b56e..d8fe4d4877 100644 --- a/drivers/power/domain/power-domain-uclass.c +++ b/drivers/power/domain/power-domain-uclass.c @@ -127,6 +127,18 @@ static int dev_power_domain_ctrl(struct udevice *dev, bool on) ret = power_domain_off(&pd); } + /* + * For platforms with parent and child power-domain devices + * we may not run device_remove() on the power-domain parent + * because it will result in removing its children and switching + * off their power-domain parent. So we will get here again and + * again and will be stuck in an endless loop. + */ + if (!on && dev_get_parent(dev) == pd.dev && + device_get_uclass_id(dev) == UCLASS_POWER_DOMAIN) { + return ret; + } + /* * power_domain_get() bound the device, thus * we must remove it again to prevent unbinding