Commit 3b7b8aca authored by Ulf Hansson's avatar Ulf Hansson
Browse files

pmdomain: core: Add common ->sync_state() support for genpd providers



If the genpd provider's fwnode doesn't have an associated struct device
with it, we can make use of the generic genpd->dev and it corresponding
driver internally in genpd to manage ->sync_state().

More precisely, while adding a genpd OF provider let's check if the fwnode
has a device and if not, make the preparation to handle ->sync_state()
internally through the genpd_provider_driver and the genpd_provider_bus.

Note that, genpd providers may opt out from this behaviour by setting the
GENPD_FLAG_NO_SYNC_STATE config options for the genpds in question.

Suggested-by: default avatarSaravana Kannan <saravanak@google.com>
Tested-by: Hiago De Franco <hiago.franco@toradex.com> # Colibri iMX8X
Tested-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> # TI AM62A,Xilinx ZynqMP ZCU106
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
Link: https://lore.kernel.org/r/20250701114733.636510-19-ulf.hansson@linaro.org
parent 9a4681a4
Loading
Loading
Loading
Loading
+50 −2
Original line number Diff line number Diff line
@@ -186,6 +186,7 @@ static const struct genpd_lock_ops genpd_raw_spin_ops = {
#define genpd_is_rpm_always_on(genpd)	(genpd->flags & GENPD_FLAG_RPM_ALWAYS_ON)
#define genpd_is_opp_table_fw(genpd)	(genpd->flags & GENPD_FLAG_OPP_TABLE_FW)
#define genpd_is_dev_name_fw(genpd)	(genpd->flags & GENPD_FLAG_DEV_NAME_FW)
#define genpd_is_no_sync_state(genpd)	(genpd->flags & GENPD_FLAG_NO_SYNC_STATE)

static inline bool irq_safe_dev_in_sleep_domain(struct device *dev,
		const struct generic_pm_domain *genpd)
@@ -2351,6 +2352,7 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
	atomic_set(&genpd->sd_count, 0);
	genpd->status = is_off ? GENPD_STATE_OFF : GENPD_STATE_ON;
	genpd->sync_state = GENPD_SYNC_STATE_OFF;
	genpd->device_count = 0;
	genpd->provider = NULL;
	genpd->device_id = -ENXIO;
@@ -2606,6 +2608,8 @@ static bool genpd_present(const struct generic_pm_domain *genpd)
int of_genpd_add_provider_simple(struct device_node *np,
				 struct generic_pm_domain *genpd)
{
	struct fwnode_handle *fwnode;
	struct device *dev;
	int ret;

	if (!np || !genpd)
@@ -2619,6 +2623,15 @@ int of_genpd_add_provider_simple(struct device_node *np,

	genpd->dev.of_node = np;

	fwnode = of_fwnode_handle(np);
	dev = get_dev_from_fwnode(fwnode);
	if (!dev && !genpd_is_no_sync_state(genpd)) {
		genpd->sync_state = GENPD_SYNC_STATE_SIMPLE;
		device_set_node(&genpd->dev, fwnode);
	}

	put_device(dev);

	ret = device_add(&genpd->dev);
	if (ret)
		return ret;
@@ -2643,7 +2656,7 @@ int of_genpd_add_provider_simple(struct device_node *np,
	if (ret)
		goto err_opp;

	genpd->provider = &np->fwnode;
	genpd->provider = fwnode;
	genpd->has_provider = true;

	return 0;
@@ -2668,8 +2681,11 @@ int of_genpd_add_provider_onecell(struct device_node *np,
				  struct genpd_onecell_data *data)
{
	struct generic_pm_domain *genpd;
	struct fwnode_handle *fwnode;
	struct device *dev;
	unsigned int i;
	int ret = -EINVAL;
	bool sync_state = false;

	if (!np || !data)
		return -EINVAL;
@@ -2680,6 +2696,13 @@ int of_genpd_add_provider_onecell(struct device_node *np,
	if (!data->xlate)
		data->xlate = genpd_xlate_onecell;

	fwnode = of_fwnode_handle(np);
	dev = get_dev_from_fwnode(fwnode);
	if (!dev)
		sync_state = true;

	put_device(dev);

	for (i = 0; i < data->num_domains; i++) {
		genpd = data->domains[i];

@@ -2690,6 +2713,12 @@ int of_genpd_add_provider_onecell(struct device_node *np,

		genpd->dev.of_node = np;

		if (sync_state && !genpd_is_no_sync_state(genpd)) {
			genpd->sync_state = GENPD_SYNC_STATE_ONECELL;
			device_set_node(&genpd->dev, fwnode);
			sync_state = false;
		}

		ret = device_add(&genpd->dev);
		if (ret)
			goto error;
@@ -2712,7 +2741,7 @@ int of_genpd_add_provider_onecell(struct device_node *np,
			WARN_ON(IS_ERR(genpd->opp_table));
		}

		genpd->provider = &np->fwnode;
		genpd->provider = fwnode;
		genpd->has_provider = true;
	}

@@ -3430,6 +3459,25 @@ static int genpd_provider_probe(struct device *dev)

static void genpd_provider_sync_state(struct device *dev)
{
	struct generic_pm_domain *genpd = container_of(dev, struct generic_pm_domain, dev);

	switch (genpd->sync_state) {
	case GENPD_SYNC_STATE_OFF:
		break;

	case GENPD_SYNC_STATE_ONECELL:
		of_genpd_sync_state(dev->of_node);
		break;

	case GENPD_SYNC_STATE_SIMPLE:
		genpd_lock(genpd);
		genpd_power_off(genpd, false, 0);
		genpd_unlock(genpd);
		break;

	default:
		break;
	}
}

static struct device_driver genpd_provider_drv = {
+7 −0
Original line number Diff line number Diff line
@@ -133,6 +133,12 @@ enum genpd_notication {
	GENPD_NOTIFY_ON,
};

enum genpd_sync_state {
	GENPD_SYNC_STATE_OFF = 0,
	GENPD_SYNC_STATE_SIMPLE,
	GENPD_SYNC_STATE_ONECELL,
};

struct dev_power_governor {
	bool (*power_down_ok)(struct dev_pm_domain *domain);
	bool (*suspend_ok)(struct device *dev);
@@ -193,6 +199,7 @@ struct generic_pm_domain {
	unsigned int performance_state;	/* Aggregated max performance state */
	cpumask_var_t cpus;		/* A cpumask of the attached CPUs */
	bool synced_poweroff;		/* A consumer needs a synced poweroff */
	enum genpd_sync_state sync_state; /* How sync_state is managed. */
	int (*power_off)(struct generic_pm_domain *domain);
	int (*power_on)(struct generic_pm_domain *domain);
	struct raw_notifier_head power_notifiers; /* Power on/off notifiers */