diff -drupN a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c --- a/drivers/cpuidle/cpuidle.c 2018-08-06 17:23:04.000000000 +0300 +++ b/drivers/cpuidle/cpuidle.c 2022-06-12 05:28:14.000000000 +0300 @@ -160,6 +160,60 @@ int cpuidle_enter_freeze(struct cpuidle_ } #endif /* CONFIG_SUSPEND */ +#ifdef CONFIG_ARM_SUNXI_CPUIDLE_TIMESTAMP +static inline u64 arch_counter_get_cntpct(void) +{ + u64 cval; + + isb(); + asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (cval)); + return cval; +} + +/* + * 0xc0020800~0xc0021000 is reserved in dts, + * just follow standby space. + * this space is used to record the timestamps + * of each stage in cpuidle. each cpu occupy + * 48 bytes. + */ +#define CPUIDLE_TIMESTAMPS_BASE (phys_to_virt((const volatile void *)0x40020800)) +#define DIV24(x) (((x) * 85) >> 11) + +typedef struct cpuidle_timestamps { + /* Linux record when cpuidle enter */ + u64 cpux_idle_enter_time; + /* optee record before wfi */ + u64 cpux_idle_finish_time; + /* cpus record when core down */ + u64 cpux_core_down_time; + /* cpus record when gicout */ + u64 cpus_wake_up_time; + /* optee record when core up */ + u64 cpux_start_wake_time; + /* Linux record when cpuidle exit */ + u64 cpux_wakeup_finish_time; +} cpuidle_timestamps_t; + +#define software_core_close_time(cpu) \ + (cpuidle_time[cpu].cpux_idle_finish_time -\ + cpuidle_time[cpu].cpux_idle_enter_time) +#define hardware_core_close_time(cpu) \ + (cpuidle_time[cpu].cpux_core_down_time -\ + cpuidle_time[cpu].cpux_idle_finish_time) + +#define software_core_open_time(cpu) \ + (cpuidle_time[cpu].cpux_wakeup_finish_time -\ + cpuidle_time[cpu].cpux_start_wake_time) +#define hardware_core_open_time(cpu) \ + (cpuidle_time[cpu].cpux_start_wake_time -\ + cpuidle_time[cpu].cpus_wake_up_time) + +#define resident_core_down_time(cpu) \ + (cpuidle_time[cpu].cpux_wakeup_finish_time -\ + cpuidle_time[cpu].cpux_idle_enter_time) + +#endif /** * cpuidle_enter_state - enter the state and update stats * @dev: cpuidle device for this cpu @@ -175,7 +229,11 @@ int cpuidle_enter_state(struct cpuidle_d bool broadcast = !!(target_state->flags & CPUIDLE_FLAG_TIMER_STOP); ktime_t time_start, time_end; s64 diff; - +#ifdef CONFIG_ARM_SUNXI_CPUIDLE_TIMESTAMP + int cpu = smp_processor_id(); + cpuidle_timestamps_t *cpuidle_time = + (cpuidle_timestamps_t *)CPUIDLE_TIMESTAMPS_BASE; +#endif /* * Tell the time framework to switch to a broadcast timer because our * local timer will be shut down. If a local timer is used from another @@ -193,20 +251,37 @@ int cpuidle_enter_state(struct cpuidle_d } /* Take note of the planned idle state. */ - sched_idle_set_state(target_state); + sched_idle_set_state(target_state, index); trace_cpu_idle_rcuidle(index, dev->cpu); time_start = ns_to_ktime(local_clock()); - stop_critical_timings(); + +#ifdef CONFIG_ARM_SUNXI_CPUIDLE_TIMESTAMP + if ((index > 0) && (cpu != 0)) + cpuidle_time[cpu].cpux_idle_enter_time = + arch_counter_get_cntpct(); +#endif entered_state = target_state->enter(dev, drv, index); +#ifdef CONFIG_ARM_SUNXI_CPUIDLE_TIMESTAMP + if ((index > 0) && (cpu != 0) && (cpu == smp_processor_id())) { + cpuidle_time[cpu].cpux_wakeup_finish_time = + arch_counter_get_cntpct(); + pr_err("c%d, es:%lld, eh:%lld, xh:%lld, xs:%lld, r:%lld\n", cpu, + DIV24(software_core_close_time(cpu)), + DIV24(hardware_core_close_time(cpu)), + DIV24(hardware_core_open_time(cpu)), + DIV24(software_core_open_time(cpu)), + DIV24(resident_core_down_time(cpu))); + } +#endif start_critical_timings(); time_end = ns_to_ktime(local_clock()); trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu); /* The cpu is no longer idle or about to enter idle. */ - sched_idle_set_state(NULL); + sched_idle_set_state(NULL, -1); if (broadcast) { if (WARN_ON_ONCE(!irqs_disabled()))