Skip to content

Commit 95f6454

Browse files
committed
PM: domains: Allow devices attached to genpd to be managed by HW
Some power-domains may be capable of relying on the HW to control the power for a device that's hooked up to it. Typically, for these kinds of configurations the consumer driver should be able to change the behavior of power domain at runtime, control the power domain in SW mode for certain configurations and handover the control to HW mode for other usecases. To allow a consumer driver to change the behaviour of the PM domain for its device, let's provide a new function, dev_pm_genpd_set_hwmode(). Moreover, let's add a corresponding optional genpd callback, ->set_hwmode_dev(), which the genpd provider should implement if it can support switching between HW controlled mode and SW controlled mode. Similarly, add the dev_pm_genpd_get_hwmode() to allow consumers to read the current mode and its corresponding optional genpd callback, ->get_hwmode_dev(), which the genpd provider can also implement to synchronize the initial HW mode state in genpd_add_device() by reading back the mode from the hardware. Signed-off-by: Ulf Hansson <[email protected]> Signed-off-by: Abel Vesa <[email protected]> Signed-off-by: Jagadeesh Kona <[email protected]> Reviewed-by: Dmitry Baryshkov <[email protected]> Reviewed-by: Dhruva Gole <[email protected]> Reviewed-by: Taniya Das <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 8fbed2d commit 95f6454

File tree

2 files changed

+81
-0
lines changed

2 files changed

+81
-0
lines changed

drivers/pmdomain/core.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,68 @@ void dev_pm_genpd_synced_poweroff(struct device *dev)
588588
}
589589
EXPORT_SYMBOL_GPL(dev_pm_genpd_synced_poweroff);
590590

591+
/**
592+
* dev_pm_genpd_set_hwmode() - Set the HW mode for the device and its PM domain.
593+
*
594+
* @dev: Device for which the HW-mode should be changed.
595+
* @enable: Value to set or unset the HW-mode.
596+
*
597+
* Some PM domains can rely on HW signals to control the power for a device. To
598+
* allow a consumer driver to switch the behaviour for its device in runtime,
599+
* which may be beneficial from a latency or energy point of view, this function
600+
* may be called.
601+
*
602+
* It is assumed that the users guarantee that the genpd wouldn't be detached
603+
* while this routine is getting called.
604+
*
605+
* Return: Returns 0 on success and negative error values on failures.
606+
*/
607+
int dev_pm_genpd_set_hwmode(struct device *dev, bool enable)
608+
{
609+
struct generic_pm_domain *genpd;
610+
int ret = 0;
611+
612+
genpd = dev_to_genpd_safe(dev);
613+
if (!genpd)
614+
return -ENODEV;
615+
616+
if (!genpd->set_hwmode_dev)
617+
return -EOPNOTSUPP;
618+
619+
genpd_lock(genpd);
620+
621+
if (dev_gpd_data(dev)->hw_mode == enable)
622+
goto out;
623+
624+
ret = genpd->set_hwmode_dev(genpd, dev, enable);
625+
if (!ret)
626+
dev_gpd_data(dev)->hw_mode = enable;
627+
628+
out:
629+
genpd_unlock(genpd);
630+
return ret;
631+
}
632+
EXPORT_SYMBOL_GPL(dev_pm_genpd_set_hwmode);
633+
634+
/**
635+
* dev_pm_genpd_get_hwmode() - Get the HW mode setting for the device.
636+
*
637+
* @dev: Device for which the current HW-mode setting should be fetched.
638+
*
639+
* This helper function allows consumer drivers to fetch the current HW mode
640+
* setting of its the device.
641+
*
642+
* It is assumed that the users guarantee that the genpd wouldn't be detached
643+
* while this routine is getting called.
644+
*
645+
* Return: Returns the HW mode setting of device from SW cached hw_mode.
646+
*/
647+
bool dev_pm_genpd_get_hwmode(struct device *dev)
648+
{
649+
return dev_gpd_data(dev)->hw_mode;
650+
}
651+
EXPORT_SYMBOL_GPL(dev_pm_genpd_get_hwmode);
652+
591653
static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)
592654
{
593655
unsigned int state_idx = genpd->state_idx;
@@ -1687,6 +1749,8 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
16871749

16881750
gpd_data->cpu = genpd_get_cpu(genpd, base_dev);
16891751

1752+
gpd_data->hw_mode = genpd->get_hwmode_dev ? genpd->get_hwmode_dev(genpd, dev) : false;
1753+
16901754
ret = genpd->attach_dev ? genpd->attach_dev(genpd, dev) : 0;
16911755
if (ret)
16921756
goto out;

include/linux/pm_domain.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@ struct generic_pm_domain {
175175
int (*set_performance_state)(struct generic_pm_domain *genpd,
176176
unsigned int state);
177177
struct gpd_dev_ops dev_ops;
178+
int (*set_hwmode_dev)(struct generic_pm_domain *domain,
179+
struct device *dev, bool enable);
180+
bool (*get_hwmode_dev)(struct generic_pm_domain *domain,
181+
struct device *dev);
178182
int (*attach_dev)(struct generic_pm_domain *domain,
179183
struct device *dev);
180184
void (*detach_dev)(struct generic_pm_domain *domain,
@@ -237,6 +241,7 @@ struct generic_pm_domain_data {
237241
unsigned int performance_state;
238242
unsigned int default_pstate;
239243
unsigned int rpm_pstate;
244+
bool hw_mode;
240245
void *data;
241246
};
242247

@@ -267,6 +272,8 @@ int dev_pm_genpd_remove_notifier(struct device *dev);
267272
void dev_pm_genpd_set_next_wakeup(struct device *dev, ktime_t next);
268273
ktime_t dev_pm_genpd_get_next_hrtimer(struct device *dev);
269274
void dev_pm_genpd_synced_poweroff(struct device *dev);
275+
int dev_pm_genpd_set_hwmode(struct device *dev, bool enable);
276+
bool dev_pm_genpd_get_hwmode(struct device *dev);
270277

271278
extern struct dev_power_governor simple_qos_governor;
272279
extern struct dev_power_governor pm_domain_always_on_gov;
@@ -340,6 +347,16 @@ static inline ktime_t dev_pm_genpd_get_next_hrtimer(struct device *dev)
340347
static inline void dev_pm_genpd_synced_poweroff(struct device *dev)
341348
{ }
342349

350+
static inline int dev_pm_genpd_set_hwmode(struct device *dev, bool enable)
351+
{
352+
return -EOPNOTSUPP;
353+
}
354+
355+
static inline bool dev_pm_genpd_get_hwmode(struct device *dev)
356+
{
357+
return false;
358+
}
359+
343360
#define simple_qos_governor (*(struct dev_power_governor *)(NULL))
344361
#define pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL))
345362
#endif

0 commit comments

Comments
 (0)