diff options
| author | Sanjay Chandrashekara <sanjayc@nvidia.com> | 2018-06-21 19:15:49 -0400 |
|---|---|---|
| committer | mobile promotions <svcmobile_promotions@nvidia.com> | 2018-07-24 03:54:38 -0400 |
| commit | 27c214726d8d5e76cece3deda271003090ad2c03 (patch) | |
| tree | 274a9afe04d9edf793dbeb800fe2a970864a49a8 | |
| parent | e6210538bed3cde7ae968b1b2ba9fd0fc726ec3e (diff) | |
t19x: cpuidle: c6 exit latency measurement
Add support to measure C6 exit latency. This patch implements some
of the helper functions required for this functionality.
Measurement technique
1. Force cores 1 to 7 to enter C6 state.
2. Send an IPI from core 0 to wake all other cores
3. Measure exit latency based on trace printks
4. Check C6 entry count before and after "wfi" to make sure C6 was
entered.
JIRA: TPM-1217
Change-Id: I1d9943b09cf880631ad17f915bbf84959b899d2f
Signed-off-by: Sanjay Chandrashekara <sanjayc@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/1757458
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
| -rw-r--r-- | drivers/cpuidle/cpuidle-tegra19x.c | 61 | ||||
| -rw-r--r-- | include/linux/platform/tegra/t19x-cpuidle.h | 8 |
2 files changed, 66 insertions, 3 deletions
diff --git a/drivers/cpuidle/cpuidle-tegra19x.c b/drivers/cpuidle/cpuidle-tegra19x.c index 58ad5ac05..2166abbb7 100644 --- a/drivers/cpuidle/cpuidle-tegra19x.c +++ b/drivers/cpuidle/cpuidle-tegra19x.c | |||
| @@ -34,6 +34,8 @@ | |||
| 34 | #include <linux/psci.h> | 34 | #include <linux/psci.h> |
| 35 | #include <linux/version.h> | 35 | #include <linux/version.h> |
| 36 | #include <linux/cpuhotplug.h> | 36 | #include <linux/cpuhotplug.h> |
| 37 | #include <linux/atomic.h> | ||
| 38 | #include <linux/platform/tegra/t19x-cpuidle.h> | ||
| 37 | 39 | ||
| 38 | #include <linux/of_gpio.h> | 40 | #include <linux/of_gpio.h> |
| 39 | #include <asm/cpuidle.h> | 41 | #include <asm/cpuidle.h> |
| @@ -49,12 +51,15 @@ | |||
| 49 | #define CORE_WAKE_MASK 0x180C | 51 | #define CORE_WAKE_MASK 0x180C |
| 50 | #define T19x_CPUIDLE_C7_STATE 2 | 52 | #define T19x_CPUIDLE_C7_STATE 2 |
| 51 | #define T19x_CPUIDLE_C6_STATE 1 | 53 | #define T19x_CPUIDLE_C6_STATE 1 |
| 54 | #define MCE_STAT_ID_SHIFT 16UL | ||
| 52 | 55 | ||
| 53 | static u32 read_cluster_info(struct device_node *of_states); | 56 | static u32 read_cluster_info(struct device_node *of_states); |
| 54 | static u32 deepest_cc_state; | 57 | static u32 deepest_cc_state; |
| 55 | static u32 deepest_cg_state; | 58 | static u32 deepest_cg_state; |
| 56 | static u64 forced_idle_state; | 59 | static u64 forced_idle_state; |
| 57 | static u64 forced_cluster_idle_state; | 60 | static u64 forced_cluster_idle_state; |
| 61 | static u64 test_c6_exit_latency; | ||
| 62 | atomic_t entered_c6_cpu_count = ATOMIC_INIT(0); | ||
| 58 | static u32 testmode; | 63 | static u32 testmode; |
| 59 | static struct cpuidle_driver t19x_cpu_idle_driver; | 64 | static struct cpuidle_driver t19x_cpu_idle_driver; |
| 60 | static int crossover_init(void); | 65 | static int crossover_init(void); |
| @@ -88,11 +93,45 @@ static bool check_mce_version(void) | |||
| 88 | return false; | 93 | return false; |
| 89 | } | 94 | } |
| 90 | 95 | ||
| 96 | int read_cpu_counter(void) | ||
| 97 | { | ||
| 98 | return atomic_read(&entered_c6_cpu_count); | ||
| 99 | } | ||
| 100 | EXPORT_SYMBOL(read_cpu_counter); | ||
| 101 | |||
| 102 | void clear_cpu_counter(void) | ||
| 103 | { | ||
| 104 | atomic_set(&entered_c6_cpu_count, 0); | ||
| 105 | } | ||
| 106 | EXPORT_SYMBOL(clear_cpu_counter); | ||
| 107 | |||
| 91 | static void t19x_cpu_enter_c6(u32 wake_time) | 108 | static void t19x_cpu_enter_c6(u32 wake_time) |
| 92 | { | 109 | { |
| 93 | arm_cpuidle_suspend(T19x_CPUIDLE_C6_STATE); | 110 | arm_cpuidle_suspend(T19x_CPUIDLE_C6_STATE); |
| 94 | } | 111 | } |
| 95 | 112 | ||
| 113 | /*enter C6 function used in measuring C6 latency*/ | ||
| 114 | static void test_t19x_cpu_enter_c6(u32 wake_time) | ||
| 115 | { | ||
| 116 | int cpu; | ||
| 117 | u64 val; | ||
| 118 | u32 mce_index; | ||
| 119 | |||
| 120 | cpu = smp_processor_id(); | ||
| 121 | mce_index = (NVG_STAT_QUERY_C6_ENTRIES << MCE_STAT_ID_SHIFT) | ||
| 122 | + (u32)cpu; | ||
| 123 | tegra_mce_read_cstate_stats(mce_index, &val); | ||
| 124 | trace_printk("cpu = %d C6_COUNT_BEFORE = %llu\n", cpu, val); | ||
| 125 | |||
| 126 | atomic_inc(&entered_c6_cpu_count); | ||
| 127 | |||
| 128 | t19x_cpu_enter_c6(0); | ||
| 129 | |||
| 130 | trace_printk("Exiting C6\n"); | ||
| 131 | tegra_mce_read_cstate_stats(mce_index, &val); | ||
| 132 | trace_printk("cpu = %d C6_COUNT_AFTER = %llu\n", cpu, val); | ||
| 133 | } | ||
| 134 | |||
| 96 | static void t19x_cpu_enter_c7(u32 wake_time) | 135 | static void t19x_cpu_enter_c7(u32 wake_time) |
| 97 | { | 136 | { |
| 98 | cpu_pm_enter(); /* power down notifier */ | 137 | cpu_pm_enter(); /* power down notifier */ |
| @@ -147,7 +186,7 @@ static u32 t19x_make_power_state(u32 state) | |||
| 147 | t = ktime_to_timespec(tick_nohz_get_sleep_length()); | 186 | t = ktime_to_timespec(tick_nohz_get_sleep_length()); |
| 148 | wake_time = t.tv_sec * tsc_per_sec + t.tv_nsec / nsec_per_tsc_tick; | 187 | wake_time = t.tv_sec * tsc_per_sec + t.tv_nsec / nsec_per_tsc_tick; |
| 149 | 188 | ||
| 150 | if (testmode) | 189 | if (testmode || test_c6_exit_latency) |
| 151 | wake_time = 0xFFFFEEEE; | 190 | wake_time = 0xFFFFEEEE; |
| 152 | 191 | ||
| 153 | /* The 8-LSB bits of wake time is lost and only 24 MSB bits | 192 | /* The 8-LSB bits of wake time is lost and only 24 MSB bits |
| @@ -267,8 +306,12 @@ static int forced_idle_write(void *data, u64 val) | |||
| 267 | 306 | ||
| 268 | if (pmstate == T19x_CPUIDLE_C7_STATE) | 307 | if (pmstate == T19x_CPUIDLE_C7_STATE) |
| 269 | t19x_cpu_enter_c7(wake_time); | 308 | t19x_cpu_enter_c7(wake_time); |
| 270 | else if (pmstate == T19x_CPUIDLE_C6_STATE) | 309 | else if (pmstate == T19x_CPUIDLE_C6_STATE) { |
| 271 | t19x_cpu_enter_c6(wake_time); | 310 | if (test_c6_exit_latency) |
| 311 | test_t19x_cpu_enter_c6(wake_time); | ||
| 312 | else | ||
| 313 | t19x_cpu_enter_c6(wake_time); | ||
| 314 | } | ||
| 272 | else | 315 | else |
| 273 | asm volatile("wfi\n"); | 316 | asm volatile("wfi\n"); |
| 274 | 317 | ||
| @@ -293,6 +336,12 @@ static int forced_idle_write(void *data, u64 val) | |||
| 293 | return 0; | 336 | return 0; |
| 294 | } | 337 | } |
| 295 | 338 | ||
| 339 | void force_idle_c6(u64 delay) | ||
| 340 | { | ||
| 341 | forced_idle_write(NULL, delay); | ||
| 342 | } | ||
| 343 | EXPORT_SYMBOL(force_idle_c6); | ||
| 344 | |||
| 296 | struct xover_smp_call_data { | 345 | struct xover_smp_call_data { |
| 297 | int index; | 346 | int index; |
| 298 | int value; | 347 | int value; |
| @@ -407,6 +456,12 @@ static int cpuidle_debugfs_init(void) | |||
| 407 | if (!dfs_file) | 456 | if (!dfs_file) |
| 408 | goto err_out; | 457 | goto err_out; |
| 409 | 458 | ||
| 459 | dfs_file = debugfs_create_u64("test_c6_exit_latency", 0644, | ||
| 460 | cpuidle_debugfs_node, &test_c6_exit_latency); | ||
| 461 | |||
| 462 | if (!dfs_file) | ||
| 463 | goto err_out; | ||
| 464 | |||
| 410 | dfs_file = debugfs_create_u64("forced_cluster_idle_state", 0644, | 465 | dfs_file = debugfs_create_u64("forced_cluster_idle_state", 0644, |
| 411 | cpuidle_debugfs_node, &forced_cluster_idle_state); | 466 | cpuidle_debugfs_node, &forced_cluster_idle_state); |
| 412 | 467 | ||
diff --git a/include/linux/platform/tegra/t19x-cpuidle.h b/include/linux/platform/tegra/t19x-cpuidle.h new file mode 100644 index 000000000..5c9275d78 --- /dev/null +++ b/include/linux/platform/tegra/t19x-cpuidle.h | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #ifndef __T19x_CPUIDLE_C6_LATENCY__ | ||
| 2 | #define __T19x_CPUIDLE_C6_LATENCY__ | ||
| 3 | |||
| 4 | void force_idle_c6(u64 delay); | ||
| 5 | int read_cpu_counter(void); | ||
| 6 | void clear_cpu_counter(void); | ||
| 7 | #endif | ||
| 8 | |||
