diff options
| -rw-r--r-- | drivers/hwtracing/coresight/coresight-core.c | 93 | ||||
| -rw-r--r-- | include/linux/coresight.h | 2 |
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; |
