The current implementation only saves and restores the context for ETM sources while ignoring the context of links. However, if funnels or replicators on a linked path resides in a CPU or cluster power domain, the hardware context for the link will be lost after resuming from low power states.
To support context management for links during CPU low power modes, a better way is to implement CPU PM callbacks in the Arm CoreSight core layer. As the core layer has sufficient information for linked paths, from tracers to links, which can be used for power management.
As a first step, this patch registers CPU PM notifier in the core layer. If a source device provides callbacks for saving and restoring context, these callbacks will be invoked in CPU suspend and resume.
Reviewed-by: James Clark james.clark@linaro.org Tested-by: James Clark james.clark@linaro.org Reviewed-by: Yeoreum Yun yeoreum.yun@arm.com Signed-off-by: Leo Yan leo.yan@arm.com --- drivers/hwtracing/coresight/coresight-core.c | 98 ++++++++++++++++++++++++++++ include/linux/coresight.h | 2 + 2 files changed, 100 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 74c9f0dd43784dd735885249c1e50fc86f610582..8384d1466b4cc74f7e463388862c58c86b74be79 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> @@ -431,6 +432,11 @@ void coresight_set_percpu_local_path(struct coresight_path *path) } EXPORT_SYMBOL_GPL(coresight_set_percpu_local_path);
+static struct coresight_path *coresight_get_percpu_local_path(void) +{ + return this_cpu_read(percpu_path); +} + /* * coresight_disable_path_from : Disable components in the given path beyond * @nd in the list. If @nd is NULL, all the components, except the SOURCE are @@ -1647,6 +1653,91 @@ static void coresight_release_device_list(void) } }
+static bool coresight_pm_is_needed(struct coresight_path *path) +{ + struct coresight_device *csdev; + + if (!path) + return false; + + csdev = coresight_get_source(path); + if (!csdev) + return false; + + /* pm_save_disable() and pm_restore_enable() must be paired */ + if (!coresight_ops(csdev)->pm_save_disable || + !coresight_ops(csdev)->pm_restore_enable) + return false; + + /* Save and restore only if the source is active */ + if (coresight_get_mode(csdev)) + return true; + + return false; +} + +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_pm_save(struct coresight_path *path) +{ + struct coresight_device *source = coresight_get_source(path); + + return coresight_pm_device_save(source); +} + +static void coresight_pm_restore(struct coresight_path *path) +{ + struct coresight_device *source = coresight_get_source(path); + + coresight_pm_device_restore(source); +} + +static int coresight_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd, + void *v) +{ + struct coresight_path *path = coresight_get_percpu_local_path(); + + if (!coresight_pm_is_needed(path)) + return NOTIFY_OK; + + switch (cmd) { + case CPU_PM_ENTER: + if (coresight_pm_save(path)) + return NOTIFY_BAD; + break; + case CPU_PM_EXIT: + case CPU_PM_ENTER_FAILED: + coresight_pm_restore(path); + 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", }; @@ -1701,9 +1792,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: @@ -1715,6 +1812,7 @@ static int __init coresight_init(void)
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 e9c20ceb9016fa3db256b8c1147c1fd2027b7b0d..5f9d7ea9f5941ab01eb6a084ca558a9417c7727f 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;