static bool tegra30_cpu_cluster_power_down(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { /* All CPUs entering LP2 is not working. * Don't let CPU0 enter LP2 when any secondary CPU is online. */ if (num_online_cpus() > 1 || !tegra_cpu_rail_off_ready()) { cpu_do_idle(); return false; } clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu); tegra_idle_lp2_last(); clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu); return true; }
static bool tegra30_cpu_cluster_power_down(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { /* All CPUs entering LP2 is not working. * Don't let CPU0 enter LP2 when any secondary CPU is online. */ if (num_online_cpus() > 1 || !tegra_cpu_rail_off_ready()) { cpu_do_idle(); return false; } tick_broadcast_enter(); tegra_idle_lp2_last(); tick_broadcast_exit(); return true; }
static bool tegra20_cpu_cluster_power_down(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { while (tegra20_cpu_is_resettable_soon()) cpu_relax(); if (tegra20_reset_cpu_1() || !tegra_cpu_rail_off_ready()) return false; clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu); tegra_idle_lp2_last(); clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu); if (cpu_online(1)) tegra20_wake_cpu1_from_reset(); return true; }
static bool tegra30_cpu_cluster_power_down(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { struct cpuidle_state *state = &drv->states[index]; u32 cpu_on_time = state->exit_latency; u32 cpu_off_time = state->target_residency - state->exit_latency; /* All CPUs entering LP2 is not working. * Don't let CPU0 enter LP2 when any secondary CPU is online. */ if (num_online_cpus() > 1 || !tegra_cpu_rail_off_ready()) { cpu_do_idle(); return false; } clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu); tegra_idle_lp2_last(cpu_on_time, cpu_off_time); clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu); return true; }
static int tegra2_idle_lp2_cpu_0(struct cpuidle_device *dev, struct cpuidle_state *state, s64 request) { ktime_t entry_time; ktime_t exit_time; s64 wake_time; bool sleep_completed = false; int bin; int i; while (tegra2_cpu_is_resettable_soon()) cpu_relax(); if (tegra2_reset_other_cpus(dev->cpu)) return 0; idle_stats.both_idle_count++; if (request < state->target_residency) { tegra2_lp3_fall_back(dev); return -EBUSY; } /* LP2 entry time */ entry_time = ktime_get(); /* LP2 initial targeted wake time */ wake_time = ktime_to_us(entry_time) + request; /* CPU0 must wake up before CPU1. */ smp_rmb(); wake_time = min_t(s64, wake_time, tegra_cpu1_wake_by_time); /* LP2 actual targeted wake time */ request = wake_time - ktime_to_us(entry_time); BUG_ON(wake_time < 0LL); idle_stats.tear_down_count++; entry_time = ktime_get(); if (request > state->target_residency) { s64 sleep_time = request - tegra_lp2_exit_latency; bin = time_to_bin((u32)request / 1000); idle_stats.lp2_count++; idle_stats.lp2_count_bin[bin]++; clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu); if (tegra_idle_lp2_last(sleep_time, 0) == 0) sleep_completed = true; else { int irq = tegra_gic_pending_interrupt(); idle_stats.lp2_int_count[irq]++; } clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu); } for_each_online_cpu(i) { if (i != dev->cpu) tegra2_wake_reset_cpu(i); } exit_time = ktime_get(); if (sleep_completed) { /* * Stayed in LP2 for the full time until the next tick, * adjust the exit latency based on measurement */ s64 actual_time = ktime_to_us(ktime_sub(exit_time, entry_time)); long offset = (long)(actual_time - request); int latency = tegra_lp2_exit_latency + offset / 16; latency = clamp(latency, 0, 10000); tegra_lp2_exit_latency = latency; smp_wmb(); idle_stats.lp2_completed_count++; idle_stats.lp2_completed_count_bin[bin]++; idle_stats.in_lp2_time += actual_time; pr_debug("%lld %lld %ld %d\n", request, actual_time, offset, bin); } return 0; }