Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <ulf.hansson@linaro.org>
Signed-off-by: Abel Vesa <abel.vesa@linaro.org>
Signed-off-by: Jagadeesh Kona <quic_jkona@quicinc.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
Reviewed-by: Dhruva Gole <d-gole@ti.com>
Reviewed-by: Taniya Das <quic_tdas@quicinc.com>
Link: https://lore.kernel.org/r/20240624044809.17751-2-quic_jkona@quicinc.com

+81
+64
drivers/pmdomain/core.c
··· 588 588 } 589 589 EXPORT_SYMBOL_GPL(dev_pm_genpd_synced_poweroff); 590 590 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 + 591 653 static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed) 592 654 { 593 655 unsigned int state_idx = genpd->state_idx; ··· 1748 1686 return PTR_ERR(gpd_data); 1749 1687 1750 1688 gpd_data->cpu = genpd_get_cpu(genpd, base_dev); 1689 + 1690 + gpd_data->hw_mode = genpd->get_hwmode_dev ? genpd->get_hwmode_dev(genpd, dev) : false; 1751 1691 1752 1692 ret = genpd->attach_dev ? genpd->attach_dev(genpd, dev) : 0; 1753 1693 if (ret)
+17
include/linux/pm_domain.h
··· 175 175 int (*set_performance_state)(struct generic_pm_domain *genpd, 176 176 unsigned int state); 177 177 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); 178 182 int (*attach_dev)(struct generic_pm_domain *domain, 179 183 struct device *dev); 180 184 void (*detach_dev)(struct generic_pm_domain *domain, ··· 241 237 unsigned int performance_state; 242 238 unsigned int default_pstate; 243 239 unsigned int rpm_pstate; 240 + bool hw_mode; 244 241 void *data; 245 242 }; 246 243 ··· 272 267 void dev_pm_genpd_set_next_wakeup(struct device *dev, ktime_t next); 273 268 ktime_t dev_pm_genpd_get_next_hrtimer(struct device *dev); 274 269 void dev_pm_genpd_synced_poweroff(struct device *dev); 270 + int dev_pm_genpd_set_hwmode(struct device *dev, bool enable); 271 + bool dev_pm_genpd_get_hwmode(struct device *dev); 275 272 276 273 extern struct dev_power_governor simple_qos_governor; 277 274 extern struct dev_power_governor pm_domain_always_on_gov; ··· 346 339 } 347 340 static inline void dev_pm_genpd_synced_poweroff(struct device *dev) 348 341 { } 342 + 343 + static inline int dev_pm_genpd_set_hwmode(struct device *dev, bool enable) 344 + { 345 + return -EOPNOTSUPP; 346 + } 347 + 348 + static inline bool dev_pm_genpd_get_hwmode(struct device *dev) 349 + { 350 + return false; 351 + } 349 352 350 353 #define simple_qos_governor (*(struct dev_power_governor *)(NULL)) 351 354 #define pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL))