Commit 1b597e1c authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull pmdomain fixes from Ulf Hansson:
 "pmdomain core:
   - Add GENPD_FLAG_DEV_NAME_FW flag to generate unique names

  pmdomain providers:
   - arm: Use FLAG_DEV_NAME_FW to ensure unique names
   - imx93-blk-ctrl: Fix the remove path

  arm_scmi/qcom-cpucp:
   - Report duplicate OPPs as firmware bugs for arm_scmi
   - Skip OPP duplicates for arm_scmi
   - Mark the qcom-cpucp mailbox irq with IRQF_NO_SUSPEND flag"

* tag 'pmdomain-v6.12-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/linux-pm:
  mailbox: qcom-cpucp: Mark the irq with IRQF_NO_SUSPEND flag
  firmware: arm_scmi: Report duplicate opps as firmware bugs
  firmware: arm_scmi: Skip opp duplicates
  pmdomain: imx93-blk-ctrl: correct remove path
  pmdomain: arm: Use FLAG_DEV_NAME_FW to ensure unique names
  pmdomain: core: Add GENPD_FLAG_DEV_NAME_FW flag
parents aa35f544 d2fab3fc
Loading
Loading
Loading
Loading
+32 −12
Original line number Diff line number Diff line
@@ -373,7 +373,7 @@ static int iter_perf_levels_update_state(struct scmi_iterator_state *st,
	return 0;
}

static inline void
static inline int
process_response_opp(struct device *dev, struct perf_dom_info *dom,
		     struct scmi_opp *opp, unsigned int loop_idx,
		     const struct scmi_msg_resp_perf_describe_levels *r)
@@ -386,12 +386,16 @@ process_response_opp(struct device *dev, struct perf_dom_info *dom,
		le16_to_cpu(r->opp[loop_idx].transition_latency_us);

	ret = xa_insert(&dom->opps_by_lvl, opp->perf, opp, GFP_KERNEL);
	if (ret)
		dev_warn(dev, "Failed to add opps_by_lvl at %d for %s - ret:%d\n",
	if (ret) {
		dev_info(dev, FW_BUG "Failed to add opps_by_lvl at %d for %s - ret:%d\n",
			 opp->perf, dom->info.name, ret);
		return ret;
	}

	return 0;
}

static inline void
static inline int
process_response_opp_v4(struct device *dev, struct perf_dom_info *dom,
			struct scmi_opp *opp, unsigned int loop_idx,
			const struct scmi_msg_resp_perf_describe_levels_v4 *r)
@@ -404,9 +408,11 @@ process_response_opp_v4(struct device *dev, struct perf_dom_info *dom,
		le16_to_cpu(r->opp[loop_idx].transition_latency_us);

	ret = xa_insert(&dom->opps_by_lvl, opp->perf, opp, GFP_KERNEL);
	if (ret)
		dev_warn(dev, "Failed to add opps_by_lvl at %d for %s - ret:%d\n",
	if (ret) {
		dev_info(dev, FW_BUG "Failed to add opps_by_lvl at %d for %s - ret:%d\n",
			 opp->perf, dom->info.name, ret);
		return ret;
	}

	/* Note that PERF v4 reports always five 32-bit words */
	opp->indicative_freq = le32_to_cpu(r->opp[loop_idx].indicative_freq);
@@ -415,13 +421,21 @@ process_response_opp_v4(struct device *dev, struct perf_dom_info *dom,

		ret = xa_insert(&dom->opps_by_idx, opp->level_index, opp,
				GFP_KERNEL);
		if (ret)
		if (ret) {
			dev_warn(dev,
				 "Failed to add opps_by_idx at %d for %s - ret:%d\n",
				 opp->level_index, dom->info.name, ret);

			/* Cleanup by_lvl too */
			xa_erase(&dom->opps_by_lvl, opp->perf);

			return ret;
		}

		hash_add(dom->opps_by_freq, &opp->hash, opp->indicative_freq);
	}

	return 0;
}

static int
@@ -429,16 +443,22 @@ iter_perf_levels_process_response(const struct scmi_protocol_handle *ph,
				  const void *response,
				  struct scmi_iterator_state *st, void *priv)
{
	int ret;
	struct scmi_opp *opp;
	struct scmi_perf_ipriv *p = priv;

	opp = &p->perf_dom->opp[st->desc_index + st->loop_idx];
	opp = &p->perf_dom->opp[p->perf_dom->opp_count];
	if (PROTOCOL_REV_MAJOR(p->version) <= 0x3)
		process_response_opp(ph->dev, p->perf_dom, opp, st->loop_idx,
				     response);
		ret = process_response_opp(ph->dev, p->perf_dom, opp,
					   st->loop_idx, response);
	else
		process_response_opp_v4(ph->dev, p->perf_dom, opp, st->loop_idx,
					response);
		ret = process_response_opp_v4(ph->dev, p->perf_dom, opp,
					      st->loop_idx, response);

	/* Skip BAD duplicates received from firmware */
	if (ret)
		return ret == -EBUSY ? 0 : ret;

	p->perf_dom->opp_count++;

	dev_dbg(ph->dev, "Level %d Power %d Latency %dus Ifreq %d Index %d\n",
+1 −1
Original line number Diff line number Diff line
@@ -138,7 +138,7 @@ static int qcom_cpucp_mbox_probe(struct platform_device *pdev)
		return irq;

	ret = devm_request_irq(dev, irq, qcom_cpucp_mbox_irq_fn,
			       IRQF_TRIGGER_HIGH, "apss_cpucp_mbox", cpucp);
			       IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND, "apss_cpucp_mbox", cpucp);
	if (ret < 0)
		return dev_err_probe(dev, ret, "Failed to register irq: %d\n", irq);

+2 −1
Original line number Diff line number Diff line
@@ -125,7 +125,8 @@ static int scmi_perf_domain_probe(struct scmi_device *sdev)
		scmi_pd->ph = ph;
		scmi_pd->genpd.name = scmi_pd->info->name;
		scmi_pd->genpd.flags = GENPD_FLAG_ALWAYS_ON |
				       GENPD_FLAG_OPP_TABLE_FW;
				       GENPD_FLAG_OPP_TABLE_FW |
				       GENPD_FLAG_DEV_NAME_FW;
		scmi_pd->genpd.set_performance_state = scmi_pd_set_perf_state;
		scmi_pd->genpd.attach_dev = scmi_pd_attach_dev;
		scmi_pd->genpd.detach_dev = scmi_pd_detach_dev;
+34 −15
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@
#define pr_fmt(fmt) "PM: " fmt

#include <linux/delay.h>
#include <linux/idr.h>
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/platform_device.h>
@@ -23,6 +24,9 @@
#include <linux/cpu.h>
#include <linux/debugfs.h>

/* Provides a unique ID for each genpd device */
static DEFINE_IDA(genpd_ida);

#define GENPD_RETRY_MAX_MS	250		/* Approximate */

#define GENPD_DEV_CALLBACK(genpd, type, callback, dev)		\
@@ -171,6 +175,7 @@ static const struct genpd_lock_ops genpd_raw_spin_ops = {
#define genpd_is_cpu_domain(genpd)	(genpd->flags & GENPD_FLAG_CPU_DOMAIN)
#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)

static inline bool irq_safe_dev_in_sleep_domain(struct device *dev,
		const struct generic_pm_domain *genpd)
@@ -189,7 +194,7 @@ static inline bool irq_safe_dev_in_sleep_domain(struct device *dev,

	if (ret)
		dev_warn_once(dev, "PM domain %s will not be powered off\n",
				genpd->name);
			      dev_name(&genpd->dev));

	return ret;
}
@@ -274,7 +279,7 @@ static void genpd_debug_remove(struct generic_pm_domain *genpd)
	if (!genpd_debugfs_dir)
		return;

	debugfs_lookup_and_remove(genpd->name, genpd_debugfs_dir);
	debugfs_lookup_and_remove(dev_name(&genpd->dev), genpd_debugfs_dir);
}

static void genpd_update_accounting(struct generic_pm_domain *genpd)
@@ -731,7 +736,7 @@ static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)
	genpd->states[state_idx].power_on_latency_ns = elapsed_ns;
	genpd->gd->max_off_time_changed = true;
	pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n",
		 genpd->name, "on", elapsed_ns);
		 dev_name(&genpd->dev), "on", elapsed_ns);

out:
	raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_ON, NULL);
@@ -782,7 +787,7 @@ static int _genpd_power_off(struct generic_pm_domain *genpd, bool timed)
	genpd->states[state_idx].power_off_latency_ns = elapsed_ns;
	genpd->gd->max_off_time_changed = true;
	pr_debug("%s: Power-%s latency exceeded, new value %lld ns\n",
		 genpd->name, "off", elapsed_ns);
		 dev_name(&genpd->dev), "off", elapsed_ns);

out:
	raw_notifier_call_chain(&genpd->power_notifiers, GENPD_NOTIFY_OFF,
@@ -1940,7 +1945,7 @@ int dev_pm_genpd_add_notifier(struct device *dev, struct notifier_block *nb)

	if (ret) {
		dev_warn(dev, "failed to add notifier for PM domain %s\n",
			 genpd->name);
			 dev_name(&genpd->dev));
		return ret;
	}

@@ -1987,7 +1992,7 @@ int dev_pm_genpd_remove_notifier(struct device *dev)

	if (ret) {
		dev_warn(dev, "failed to remove notifier for PM domain %s\n",
			 genpd->name);
			 dev_name(&genpd->dev));
		return ret;
	}

@@ -2013,7 +2018,7 @@ static int genpd_add_subdomain(struct generic_pm_domain *genpd,
	 */
	if (!genpd_is_irq_safe(genpd) && genpd_is_irq_safe(subdomain)) {
		WARN(1, "Parent %s of subdomain %s must be IRQ safe\n",
				genpd->name, subdomain->name);
		     dev_name(&genpd->dev), subdomain->name);
		return -EINVAL;
	}

@@ -2088,7 +2093,7 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,

	if (!list_empty(&subdomain->parent_links) || subdomain->device_count) {
		pr_warn("%s: unable to remove subdomain %s\n",
			genpd->name, subdomain->name);
			dev_name(&genpd->dev), subdomain->name);
		ret = -EBUSY;
		goto out;
	}
@@ -2225,6 +2230,7 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
	genpd->status = is_off ? GENPD_STATE_OFF : GENPD_STATE_ON;
	genpd->device_count = 0;
	genpd->provider = NULL;
	genpd->device_id = -ENXIO;
	genpd->has_provider = false;
	genpd->accounting_time = ktime_get_mono_fast_ns();
	genpd->domain.ops.runtime_suspend = genpd_runtime_suspend;
@@ -2265,7 +2271,18 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
		return ret;

	device_initialize(&genpd->dev);

	if (!genpd_is_dev_name_fw(genpd)) {
		dev_set_name(&genpd->dev, "%s", genpd->name);
	} else {
		ret = ida_alloc(&genpd_ida, GFP_KERNEL);
		if (ret < 0) {
			put_device(&genpd->dev);
			return ret;
		}
		genpd->device_id = ret;
		dev_set_name(&genpd->dev, "%s_%u", genpd->name, genpd->device_id);
	}

	mutex_lock(&gpd_list_lock);
	list_add(&genpd->gpd_list_node, &gpd_list);
@@ -2287,13 +2304,13 @@ static int genpd_remove(struct generic_pm_domain *genpd)

	if (genpd->has_provider) {
		genpd_unlock(genpd);
		pr_err("Provider present, unable to remove %s\n", genpd->name);
		pr_err("Provider present, unable to remove %s\n", dev_name(&genpd->dev));
		return -EBUSY;
	}

	if (!list_empty(&genpd->parent_links) || genpd->device_count) {
		genpd_unlock(genpd);
		pr_err("%s: unable to remove %s\n", __func__, genpd->name);
		pr_err("%s: unable to remove %s\n", __func__, dev_name(&genpd->dev));
		return -EBUSY;
	}

@@ -2307,9 +2324,11 @@ static int genpd_remove(struct generic_pm_domain *genpd)
	genpd_unlock(genpd);
	genpd_debug_remove(genpd);
	cancel_work_sync(&genpd->power_off_work);
	if (genpd->device_id != -ENXIO)
		ida_free(&genpd_ida, genpd->device_id);
	genpd_free_data(genpd);

	pr_debug("%s: removed %s\n", __func__, genpd->name);
	pr_debug("%s: removed %s\n", __func__, dev_name(&genpd->dev));

	return 0;
}
@@ -3272,12 +3291,12 @@ static int genpd_summary_one(struct seq_file *s,
	else
		snprintf(state, sizeof(state), "%s",
			 status_lookup[genpd->status]);
	seq_printf(s, "%-30s  %-30s  %u", genpd->name, state, genpd->performance_state);
	seq_printf(s, "%-30s  %-30s  %u", dev_name(&genpd->dev), state, genpd->performance_state);

	/*
	 * Modifications on the list require holding locks on both
	 * parent and child, so we are safe.
	 * Also genpd->name is immutable.
	 * Also the device name is immutable.
	 */
	list_for_each_entry(link, &genpd->parent_links, parent_node) {
		if (list_is_first(&link->parent_node, &genpd->parent_links))
@@ -3502,7 +3521,7 @@ static void genpd_debug_add(struct generic_pm_domain *genpd)
	if (!genpd_debugfs_dir)
		return;

	d = debugfs_create_dir(genpd->name, genpd_debugfs_dir);
	d = debugfs_create_dir(dev_name(&genpd->dev), genpd_debugfs_dir);

	debugfs_create_file("current_state", 0444,
			    d, genpd, &status_fops);
+3 −1
Original line number Diff line number Diff line
@@ -313,7 +313,9 @@ static void imx93_blk_ctrl_remove(struct platform_device *pdev)

	of_genpd_del_provider(pdev->dev.of_node);

	for (i = 0; bc->onecell_data.num_domains; i++) {
	pm_runtime_disable(&pdev->dev);

	for (i = 0; i < bc->onecell_data.num_domains; i++) {
		struct imx93_blk_ctrl_domain *domain = &bc->domains[i];

		pm_genpd_remove(&domain->genpd);
Loading