[U-Boot] [PATCH v3] dm: core: device: switch off power domain after device removal

Anatolij Gustschin agust at denx.de
Thu Aug 1 07:44:24 UTC 2019


Hi Lokesh,

On Thu, 1 Aug 2019 09:43:39 +0530
Lokesh Vutla lokeshvutla at ti.com wrote:

>On 01/08/19 2:55 AM, Anatolij Gustschin wrote:
>> The power domain associated with a device is enabled when probing,
>> but currently the domain remains enabled when the device is removed.
>> Some boards started to disable power domains for selected devices
>> via custom board_quiesce_devices(), but it doesn't work in many
>> cases, i. e. because devices still can be accessed later in
>> .remove() callback on behalf of dm_remove_devices_flags().
>> 
>> Utilize the DM core to power off the device power domain, but add a
>> device flag to be able to selectively let the power domain enabled
>> after device removal. This might be required for devices that must
>> remain enabled when booting OS, i. e. serial console for debug
>> output, etc.
>> 
>> Signed-off-by: Anatolij Gustschin <agust at denx.de>
>> ---
>> Changes in v3:
>>  - remove 'power-domain' device to fix dm power-domain test (make qcheck)
>>  - don't switch off the power domain for current console device
>> 
>> Changes in v2:
>>  - use CONFIG_IS_ENABLED(POWER_DOMAIN) to reduce code size
>> 
>>  drivers/core/device-remove.c | 18 ++++++++++++++++++
>>  include/dm/device.h          |  6 ++++++
>>  2 files changed, 24 insertions(+)
>> 
>> diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c
>> index 586fadee0a..5a0e48e182 100644
>> --- a/drivers/core/device-remove.c
>> +++ b/drivers/core/device-remove.c
>> @@ -16,6 +16,7 @@
>>  #include <dm/uclass.h>
>>  #include <dm/uclass-internal.h>
>>  #include <dm/util.h>
>> +#include <power-domain.h>
>>  
>>  int device_chld_unbind(struct udevice *dev, struct driver *drv)
>>  {
>> @@ -155,6 +156,7 @@ static bool flags_remove(uint flags, uint drv_flags)
>>  int device_remove(struct udevice *dev, uint flags)
>>  {
>>  	const struct driver *drv;
>> +	struct power_domain pd;
>>  	int ret;
>>  
>>  	if (!dev)
>> @@ -192,6 +194,22 @@ int device_remove(struct udevice *dev, uint flags)
>>  		}
>>  	}
>>  
>> +	if (CONFIG_IS_ENABLED(POWER_DOMAIN) && dev->parent &&
>> +	    device_get_uclass_id(dev) != UCLASS_POWER_DOMAIN &&
>> +	    dev != gd->cur_serial_dev &&
>> +	    !(dev->flags & DM_FLAG_REMOVE_WITH_PD_ON)) {  
>
>I did not realize before but this statement is too hard to read :). Can we drop
>the following two conditions:
>	dev->parent && device_get_uclass_id(dev) != UCLASS_POWER_DOMAIN
>
>power_domain_get() will indirectly take care of above two cases.

I'll test it.

>> +		if (!power_domain_get(dev, &pd)) {
>> +			power_domain_off(&pd);
>> +			/*
>> +			 * power_domain_get() bound the device, thus
>> +			 * we must remove it again to prevent unbinding
>> +			 * active devices (which would result in unbind
>> +			 * error).
>> +			 */
>> +			device_remove(pd.dev, DM_REMOVE_NORMAL);  
>
>Unless I am mistaken, this looks like this is an extra work. Why can't we store
>a PD in struct device, something like below:
>
>diff --git a/drivers/core/device.c b/drivers/core/device.c
>index cded457e2b..efa780a02f 100644
>--- a/drivers/core/device.c
>+++ b/drivers/core/device.c
>@@ -307,7 +307,6 @@ static void *alloc_priv(int size, uint flags)
>
> int device_probe(struct udevice *dev)
> {
>-	struct power_domain pd;
> 	const struct driver *drv;
> 	int size = 0;
> 	int ret;
>@@ -388,10 +387,11 @@ int device_probe(struct udevice *dev)
> 	if (dev->parent && device_get_uclass_id(dev) != UCLASS_PINCTRL)
> 		pinctrl_select_state(dev, "default");
>
>+	dev->pd.dev = NULL;
> 	if (dev->parent && device_get_uclass_id(dev) != UCLASS_POWER_DOMAIN &&
> 	    !(drv->flags & DM_FLAG_DEFAULT_PD_EN_OFF)) {
>-		if (!power_domain_get(dev, &pd))
>-			power_domain_on(&pd);
>+		if (!power_domain_get(dev, &dev->pd))
>+			power_domain_on(&dev->pd);
> 	}
>
> 	ret = uclass_pre_probe_device(dev);
>diff --git a/include/dm/device.h b/include/dm/device.h
>index b892386dc4..26699ca5cc 100644
>--- a/include/dm/device.h
>+++ b/include/dm/device.h
>@@ -12,12 +12,14 @@
>
> #include <dm/ofnode.h>
> #include <dm/uclass-id.h>
>+#include <errno.h>
> #include <fdtdec.h>
> #include <linker_lists.h>
> #include <linux/compat.h>
> #include <linux/kernel.h>
> #include <linux/list.h>
> #include <linux/printk.h>
>+#include <power-domain.h>
>
> struct driver_info;
>
>@@ -126,6 +128,7 @@ enum {
>  *		When CONFIG_DEVRES is enabled, devm_kmalloc() and friends will
>  *		add to this list. Memory so-allocated will be freed
>  *		automatically when the device is removed / unbound
>+ * @pd: Pointer to power_domain controlling this device.
>  */
> struct udevice {
> 	const struct driver *driver;
>@@ -149,6 +152,7 @@ struct udevice {
> #ifdef CONFIG_DEVRES
> 	struct list_head devres_head;
> #endif
>+	struct power_domain pd;
> };
>
> /* Maximum sequence number supported */

I tried similar change, this didn't work yet. When booting Linux I see
infinite recursion in device_remove() for 'lsio_gpio0' on i.mx8qxp.

--
Anatolij


More information about the U-Boot mailing list