static void min_cpus_change(void) { bool g_cluster = false; cputime64_t on_time = 0; if (cpq_state == TEGRA_CPQ_DISABLED) return; mutex_lock(tegra3_cpu_lock); if ((tegra_cpq_min_cpus() >= 2) && is_lp_cluster()) { if (switch_clk_to_gmode()){ pr_err(CPUQUIET_TAG "min_cpus_change - switch_clk_to_gmode failed\n"); mutex_unlock(tegra3_cpu_lock); return; } on_time = ktime_to_ms(ktime_get()) - lp_on_time; show_status("LP -> off - min_cpus_change", on_time, -1); g_cluster = true; } tegra_cpu_set_speed_cap(NULL); mutex_unlock(tegra3_cpu_lock); schedule_work(&minmax_work); if (g_cluster && !manual_hotplug) cpuquiet_device_free(); }
static void enable_callback(struct cpuquiet_attribute *attr) { int disabled = -1; mutex_lock(tegra3_cpu_lock); if (!enable && cpq_state != TEGRA_CPQ_DISABLED) { disabled = 1; cpq_state = TEGRA_CPQ_DISABLED; } else if (enable && cpq_state == TEGRA_CPQ_DISABLED) { disabled = 0; cpq_state = TEGRA_CPQ_IDLE; tegra_cpu_set_speed_cap(NULL); } mutex_unlock(tegra3_cpu_lock); if (disabled == -1) return; pr_info(CPUQUIET_TAG "enable=%d\n", enable); if (disabled == 1) { cancel_delayed_work_sync(&cpuquiet_work); pr_info(CPUQUIET_TAG "enable_callback: clusterswitch disabled\n"); cpuquiet_device_busy(); } else if (!disabled) { pr_info(CPUQUIET_TAG "enable_callback: clusterswitch enabled\n"); cpuquiet_device_free(); } }
int tegra_cpuquiet_force_gmode(void) { cputime64_t on_time = 0; if (no_lp) return -EBUSY; if (!is_g_cluster_present()) return -EBUSY; if (cpq_state == TEGRA_CPQ_DISABLED) return -EBUSY; if (is_lp_cluster()) { mutex_lock(tegra3_cpu_lock); if (switch_clk_to_gmode()) { pr_err(CPUQUIET_TAG "tegra_cpuquiet_force_gmode - switch_clk_to_gmode failed\n"); mutex_unlock(tegra3_cpu_lock); return -EBUSY; } on_time = ktime_to_ms(ktime_get()) - lp_on_time; show_status("LP -> off - force", on_time, -1); mutex_unlock(tegra3_cpu_lock); if (!manual_hotplug) cpuquiet_device_free(); } return 0; }
static int min_cpus_notify(struct notifier_block *nb, unsigned long n, void *p) { bool g_cluster = false; if (cpq_state == TEGRA_CPQ_DISABLED) return NOTIFY_OK; mutex_lock(tegra3_cpu_lock); if ((n >= 1) && is_lp_cluster()) { /* make sure cpu rate is within g-mode * range before switching */ unsigned long speed = max((unsigned long)tegra_getspeed(0), clk_get_min_rate(cpu_g_clk) / 1000); tegra_update_cpu_speed(speed); clk_set_parent(cpu_clk, cpu_g_clk); g_cluster = true; } tegra_cpu_set_speed_cap(NULL); mutex_unlock(tegra3_cpu_lock); schedule_work(&minmax_work); if (g_cluster) cpuquiet_device_free(); return NOTIFY_OK; }
static void tegra_cpuquiet_work_func(struct work_struct *work) { int device_busy = -1; mutex_lock(tegra3_cpu_lock); switch(cpq_state) { case TEGRA_CPQ_DISABLED: case TEGRA_CPQ_IDLE: break; case TEGRA_CPQ_SWITCH_TO_G: if (is_lp_cluster()) { if(!clk_set_parent(cpu_clk, cpu_g_clk)) { /*catch-up with governor target speed */ tegra_cpu_set_speed_cap(NULL); /* process pending core requests*/ device_busy = 0; } } break; case TEGRA_CPQ_SWITCH_TO_LP: if (!is_lp_cluster() && !no_lp && !pm_qos_request(PM_QOS_MIN_ONLINE_CPUS) && num_online_cpus() == 1) { if (!clk_set_parent(cpu_clk, cpu_lp_clk)) { /*catch-up with governor target speed*/ tegra_cpu_set_speed_cap(NULL); device_busy = 1; } } break; default: pr_err("%s: invalid tegra hotplug state %d\n", __func__, cpq_state); } mutex_unlock(tegra3_cpu_lock); if (device_busy == 1) { cpuquiet_device_busy(); } else if (!device_busy) { apply_core_config(); cpuquiet_device_free(); } }
static void set_manual_hotplug(unsigned int mode) { if (manual_hotplug == mode) return; manual_hotplug = mode; pr_info(CPUQUIET_TAG "manual_hotplug=%d\n", manual_hotplug); // stop governor if (manual_hotplug) { cancel_delayed_work_sync(&cpuquiet_work); cpuquiet_device_busy(); schedule_work(&cpu_core_state_work); } else { cpuquiet_device_free(); } }
static void enable_callback(struct cpuquiet_attribute *attr) { mutex_lock(tegra3_cpu_lock); if (!enable && cpq_state != TEGRA_CPQ_DISABLED) { cpq_state = TEGRA_CPQ_DISABLED; mutex_unlock(tegra3_cpu_lock); cancel_delayed_work_sync(&cpuquiet_work); pr_info("Tegra cpuquiet clusterswitch disabled\n"); cpuquiet_device_busy(); mutex_lock(tegra3_cpu_lock); } else if (enable && cpq_state == TEGRA_CPQ_DISABLED) { cpq_state = TEGRA_CPQ_IDLE; pr_info("Tegra cpuquiet clusterswitch enabled\n"); tegra_cpu_set_speed_cap(NULL); cpuquiet_device_free(); } mutex_unlock(tegra3_cpu_lock); }
void tegra_auto_hotplug_governor(unsigned int cpu_freq, bool suspend) { if (!is_g_cluster_present()) return; if (cpq_state == TEGRA_CPQ_DISABLED) return; if (suspend) { cpq_state = TEGRA_CPQ_IDLE; /* Switch to G-mode if suspend rate is high enough */ if (is_lp_cluster() && (cpu_freq >= idle_bottom_freq)) { clk_set_parent(cpu_clk, cpu_g_clk); cpuquiet_device_free(); } return; } if (is_lp_cluster() && pm_qos_request(PM_QOS_MIN_ONLINE_CPUS) >= 2) { if (cpq_state != TEGRA_CPQ_SWITCH_TO_G) { /* Force switch */ cpq_state = TEGRA_CPQ_SWITCH_TO_G; queue_delayed_work( cpuquiet_wq, &cpuquiet_work, up_delay); } return; } if (is_lp_cluster() && (cpu_freq >= idle_top_freq || no_lp)) { cpq_state = TEGRA_CPQ_SWITCH_TO_G; queue_delayed_work(cpuquiet_wq, &cpuquiet_work, up_delay); } else if (!is_lp_cluster() && !no_lp && cpu_freq <= idle_bottom_freq) { cpq_state = TEGRA_CPQ_SWITCH_TO_LP; queue_delayed_work(cpuquiet_wq, &cpuquiet_work, down_delay); } else { cpq_state = TEGRA_CPQ_IDLE; } }
void tegra_cpuquiet_device_free(void) { if (!manual_hotplug) cpuquiet_device_free(); }
static void tegra_cpuquiet_work_func(struct work_struct *work) { int device_busy = -1; cputime64_t on_time = 0; #if CPUQUIET_DEBUG_VERBOSE pr_info(CPUQUIET_TAG "%s\n", __func__); #endif if (!mutex_trylock (&hotplug_lock)){ #if CPUQUIET_DEBUG_VERBOSE pr_info(CPUQUIET_TAG "%s failed to get hotplug_lock\n", __func__); #endif return; } mutex_lock(tegra3_cpu_lock); switch(cpq_state) { case TEGRA_CPQ_DISABLED: case TEGRA_CPQ_IDLE: break; case TEGRA_CPQ_SWITCH_TO_G: if (is_lp_cluster()) { if (!switch_clk_to_gmode()) { on_time = ktime_to_ms(ktime_get()) - lp_on_time; show_status("LP -> off", on_time, -1); /*catch-up with governor target speed */ tegra_cpu_set_speed_cap(NULL); device_busy = 0; } else pr_err(CPUQUIET_TAG "tegra_cpuquiet_work_func - switch_clk_to_gmode failed\n"); } #if CPUQUIET_DEBUG_VERBOSE else pr_info(CPUQUIET_TAG "skipping queued TEGRA_CPQ_SWITCH_TO_G - cond failed\n"); #endif break; case TEGRA_CPQ_SWITCH_TO_LP: if (lp_possible()) { if (!switch_clk_to_lpmode()) { show_status("LP -> on", 0, -1); /*catch-up with governor target speed*/ tegra_cpu_set_speed_cap(NULL); device_busy = 1; lp_on_time = ktime_to_ms(ktime_get()); } #if CPUQUIET_DEBUG_VERBOSE else pr_info(CPUQUIET_TAG "skipping queued TEGRA_CPQ_SWITCH_TO_LP - switch_clk_to_lpmode failed\n"); #endif } #if CPUQUIET_DEBUG_VERBOSE else pr_info(CPUQUIET_TAG "skipping queued TEGRA_CPQ_SWITCH_TO_LP - cond failed\n"); #endif break; default: pr_err(CPUQUIET_TAG "%s: invalid tegra hotplug state %d\n", __func__, cpq_state); } mutex_unlock(tegra3_cpu_lock); mutex_unlock(&hotplug_lock); if (!manual_hotplug){ if (device_busy == 1) { cpuquiet_device_busy(); } else if (!device_busy) { cpuquiet_device_free(); } } }