summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/hwtracing/coresight/coresight-core.c93
-rw-r--r--include/linux/coresight.h2
2 files changed, 95 insertions, 0 deletions
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index fc4855c3dfaa..309f3a844e8f 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -6,6 +6,7 @@
#include <linux/acpi.h>
#include <linux/bitfield.h>
#include <linux/build_bug.h>
+#include <linux/cpu_pm.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
@@ -1741,6 +1742,91 @@ static void coresight_release_device_list(void)
}
}
+static struct coresight_device *coresight_cpu_get_active_source(void)
+{
+ struct coresight_device *source;
+ bool is_active = false;
+
+ source = coresight_get_percpu_source_ref(smp_processor_id());
+ if (!source)
+ return NULL;
+
+ if (coresight_get_mode(source) != CS_MODE_DISABLED)
+ is_active = true;
+
+ coresight_put_percpu_source_ref(source);
+
+ /*
+ * It is expected to run in atomic context, so it cannot be preempted
+ * to disable the source. Here returns the active source pointer
+ * without concern that its state may change. Since the build path has
+ * taken a reference on the component, the source can be safely used
+ * by the caller.
+ */
+ return is_active ? source : NULL;
+}
+
+static int coresight_pm_is_needed(struct coresight_device *csdev)
+{
+ if (!csdev)
+ return 0;
+
+ /* pm_save_disable() and pm_restore_enable() must be paired */
+ if (coresight_ops(csdev)->pm_save_disable &&
+ coresight_ops(csdev)->pm_restore_enable)
+ return 1;
+
+ return 0;
+}
+
+static int coresight_pm_device_save(struct coresight_device *csdev)
+{
+ return coresight_ops(csdev)->pm_save_disable(csdev);
+}
+
+static void coresight_pm_device_restore(struct coresight_device *csdev)
+{
+ coresight_ops(csdev)->pm_restore_enable(csdev);
+}
+
+static int coresight_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd,
+ void *v)
+{
+ struct coresight_device *csdev = coresight_cpu_get_active_source();
+
+ if (!coresight_pm_is_needed(csdev))
+ return NOTIFY_DONE;
+
+ switch (cmd) {
+ case CPU_PM_ENTER:
+ if (coresight_pm_device_save(csdev))
+ return NOTIFY_BAD;
+ break;
+ case CPU_PM_EXIT:
+ case CPU_PM_ENTER_FAILED:
+ coresight_pm_device_restore(csdev);
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block coresight_cpu_pm_nb = {
+ .notifier_call = coresight_cpu_pm_notify,
+};
+
+static int __init coresight_pm_setup(void)
+{
+ return cpu_pm_register_notifier(&coresight_cpu_pm_nb);
+}
+
+static void coresight_pm_cleanup(void)
+{
+ cpu_pm_unregister_notifier(&coresight_cpu_pm_nb);
+}
+
const struct bus_type coresight_bustype = {
.name = "coresight",
};
@@ -1795,9 +1881,15 @@ static int __init coresight_init(void)
/* initialise the coresight syscfg API */
ret = cscfg_init();
+ if (ret)
+ goto exit_notifier;
+
+ ret = coresight_pm_setup();
if (!ret)
return 0;
+ cscfg_exit();
+exit_notifier:
atomic_notifier_chain_unregister(&panic_notifier_list,
&coresight_notifier);
exit_perf:
@@ -1809,6 +1901,7 @@ exit_bus_unregister:
static void __exit coresight_exit(void)
{
+ coresight_pm_cleanup();
cscfg_exit();
atomic_notifier_chain_unregister(&panic_notifier_list,
&coresight_notifier);
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index e9c20ceb9016..5f9d7ea9f594 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -438,6 +438,8 @@ struct coresight_ops_panic {
struct coresight_ops {
int (*trace_id)(struct coresight_device *csdev, enum cs_mode mode,
struct coresight_device *sink);
+ int (*pm_save_disable)(struct coresight_device *csdev);
+ void (*pm_restore_enable)(struct coresight_device *csdev);
const struct coresight_ops_sink *sink_ops;
const struct coresight_ops_link *link_ops;
const struct coresight_ops_source *source_ops;