static void podgov_clocks_handler(struct work_struct *work) { struct podgov_info_rec *podgov = container_of(work, struct podgov_info_rec, work); struct devfreq *df = podgov->power_manager; unsigned long freq; mutex_lock(&df->lock); if (!podgov->enable) { mutex_unlock(&df->lock); return; } if (podgov->scale == 0) { mutex_unlock(&df->lock); return; } freq = podgov->scale * (df->previous_freq / 100); scaling_limit(df, &freq); if (df->previous_freq != freq) { trace_podgov_clocks_handler(df->previous_freq, freq); podgov->adjustment_frequency = freq; podgov->adjustment_type = ADJUSTMENT_LOCAL; update_devfreq(df); } mutex_unlock(&df->lock); }
static unsigned long scaling_state_check(struct devfreq *df, ktime_t time) { struct podgov_info_rec *podgov = df->data; unsigned long dt; long max_boost, load, damp, freq, boost, res; dt = (unsigned long) ktime_us_delta(time, podgov->last_scale); if (dt < podgov->p_block_window || df->previous_freq == 0) return 0; /* convert to mhz to avoid overflow */ freq = df->previous_freq / 1000000; max_boost = (df->max_freq/3) / 1000000; /* calculate and trace load */ load = 1000 - podgov->idle_avg; trace_podgov_busy(load); damp = podgov->p_damp; if ((1000 - podgov->idle) > podgov->p_load_max) { /* if too busy, scale up max/3, do not damp */ boost = max_boost; damp = 10; } else { /* boost = bias * freq * (load - target)/target */ boost = (load - podgov->p_load_target); boost *= (podgov->p_bias * freq); boost /= (100 * podgov->p_load_target); /* clamp to max boost */ boost = (boost < max_boost) ? boost : max_boost; } /* calculate new request */ res = freq + boost; /* Maintain average request */ podgov->freq_avg = (podgov->freq_avg * podgov->p_smooth) + res; podgov->freq_avg /= (podgov->p_smooth+1); /* Applying damping to frequencies */ res = ((damp * res) + ((10 - damp)*podgov->freq_avg)) / 10; /* Convert to hz, limit, and apply */ res = res * 1000000; scaling_limit(df, &res); trace_podgov_scaling_state_check(df->previous_freq, res); return res; }
static int nvhost_pod_estimate_freq(struct devfreq *df, unsigned long *freq) { struct podgov_info_rec *podgov = df->data; struct devfreq_dev_status dev_stat; struct nvhost_devfreq_ext_stat *ext_stat; long delay; int current_event; int stat; ktime_t now; stat = df->profile->get_dev_status(df->dev.parent, &dev_stat); if (stat < 0) return stat; /* Ensure maximal clock when scaling is disabled */ if (!podgov->enable) { *freq = df->max_freq; return 0; } if (podgov->p_user) { *freq = podgov->p_freq_request; return 0; } current_event = DEVICE_IDLE; stat = 0; now = ktime_get(); /* Local adjustments (i.e. requests from kernel threads) are * handled here */ if (podgov->adjustment_type == ADJUSTMENT_LOCAL) { podgov->adjustment_type = ADJUSTMENT_DEVICE_REQ; /* Do not do unnecessary scaling */ scaling_limit(df, &podgov->adjustment_frequency); if (df->previous_freq == podgov->adjustment_frequency) return GET_TARGET_FREQ_DONTSCALE; trace_podgov_estimate_freq(df->previous_freq, podgov->adjustment_frequency); *freq = podgov->adjustment_frequency; return 0; } /* Retrieve extended data */ ext_stat = dev_stat.private_data; if (!ext_stat) return -EINVAL; current_event = ext_stat->busy; *freq = dev_stat.current_frequency; df->min_freq = ext_stat->min_freq; df->max_freq = ext_stat->max_freq; /* Sustain local variables */ podgov->last_event_type = current_event; podgov->total_idle += (dev_stat.total_time - dev_stat.busy_time); podgov->last_total_idle += (dev_stat.total_time - dev_stat.busy_time); /* update the load estimate based on idle time */ update_load_estimate(df); /* if throughput hint enabled, and last hint is recent enough, return */ if (podgov->p_use_throughput_hint && ktime_us_delta(now, podgov->last_throughput_hint) < 1000000) return GET_TARGET_FREQ_DONTSCALE; switch (current_event) { case DEVICE_IDLE: /* delay idle_max % of 2 * fast_response time (given in * microseconds) */ *freq = scaling_state_check(df, now); delay = (podgov->idle_max * podgov->p_estimation_window) / 500000; schedule_delayed_work(&podgov->idle_timer, msecs_to_jiffies(delay)); break; case DEVICE_BUSY: cancel_delayed_work(&podgov->idle_timer); *freq = scaling_state_check(df, now); break; case DEVICE_UNKNOWN: *freq = scaling_state_check(df, now); break; } if (!(*freq) || (*freq == df->previous_freq)) return GET_TARGET_FREQ_DONTSCALE; trace_podgov_estimate_freq(df->previous_freq, *freq); return 0; }
void nvhost_scale3d_set_throughput_hint(int hint) { struct podgov_info_rec *podgov = local_podgov; struct devfreq *df; long idle; long curr, target; int avg_idle, avg_hint, scale_score; unsigned int smooth; if (!podgov) return; df = podgov->power_manager; if (!df) return; mutex_lock(&podgov->power_manager->lock); podgov->block--; if (!podgov->enable || !podgov->p_use_throughput_hint || podgov->block > 0) { mutex_unlock(&podgov->power_manager->lock); return; } trace_podgov_hint(podgov->idle_estimate, hint); podgov->last_throughput_hint = ktime_get(); curr = podgov->power_manager->previous_freq; idle = podgov->idle_estimate; smooth = podgov->p_smooth; /* compute averages usings exponential-moving-average */ avg_idle = ((smooth*podgov->idle_avg + idle)/(smooth+1)); podgov->idle_avg = avg_idle; avg_hint = ((smooth*podgov->hint_avg + hint)/(smooth+1)); podgov->hint_avg = avg_hint; /* set the target using avg_hint and avg_idle */ if (avg_hint < podgov->p_hint_lo_limit) { target = freqlist_up(podgov, curr, 1); } else if (avg_hint > podgov->p_hint_hi_limit) { target = freqlist_down(podgov, curr, 1); } else { scale_score = avg_idle + avg_hint; if (scale_score > podgov->p_scaledown_limit) target = freqlist_down(podgov, curr, 1); else if (scale_score < podgov->p_scaleup_limit) target = freqlist_up(podgov, curr, 1); else target = curr; } /* clamp and apply target */ scaling_limit(df, &target); if (target != curr) { podgov->block = podgov->p_smooth; trace_podgov_do_scale(df->previous_freq, target); podgov->adjustment_frequency = target; podgov->adjustment_type = ADJUSTMENT_LOCAL; update_devfreq(df); } trace_podgov_print_target(idle, avg_idle, curr / 1000000, target, hint, avg_hint); mutex_unlock(&podgov->power_manager->lock); }
static int nvhost_pod_estimate_freq(struct devfreq *df, unsigned long *freq) { struct podgov_info_rec *podgov = df->data; struct devfreq_dev_status dev_stat; struct nvhost_devfreq_ext_stat *ext_stat; int current_event; int stat; ktime_t now; stat = df->profile->get_dev_status(df->dev.parent, &dev_stat); if (stat < 0) return stat; /* Ensure maximal clock when scaling is disabled */ if (!podgov->enable) { *freq = df->max_freq; return 0; } if (podgov->p_user) { *freq = podgov->p_freq_request; return 0; } current_event = DEVICE_IDLE; stat = 0; now = ktime_get(); /* Local adjustments (i.e. requests from kernel threads) are * handled here */ if (podgov->adjustment_type == ADJUSTMENT_LOCAL) { podgov->adjustment_type = ADJUSTMENT_DEVICE_REQ; /* Do not do unnecessary scaling */ scaling_limit(df, &podgov->adjustment_frequency); /* Round the frequency and check if we're already there */ if (freqlist_up(podgov, podgov->adjustment_frequency, 0) == dev_stat.current_frequency) return GET_TARGET_FREQ_DONTSCALE; trace_podgov_estimate_freq(df->previous_freq, podgov->adjustment_frequency); *freq = podgov->adjustment_frequency; return 0; } /* Retrieve extended data */ ext_stat = dev_stat.private_data; if (!ext_stat) return -EINVAL; current_event = ext_stat->busy; *freq = dev_stat.current_frequency; df->min_freq = ext_stat->min_freq; df->max_freq = ext_stat->max_freq; /* Sustain local variables */ podgov->last_event_type = current_event; podgov->idle = 1000 * (dev_stat.total_time - dev_stat.busy_time); podgov->idle = podgov->idle / dev_stat.total_time; podgov->idle_avg = (podgov->p_smooth * podgov->idle_avg) + podgov->idle; podgov->idle_avg = podgov->idle_avg / (podgov->p_smooth + 1); /* if throughput hint enabled, and last hint is recent enough, return */ if (podgov->p_use_throughput_hint && ktime_us_delta(now, podgov->last_throughput_hint) < 1000000) return GET_TARGET_FREQ_DONTSCALE; switch (current_event) { case DEVICE_IDLE: /* Launch a work to slowdown the gpu */ *freq = scaling_state_check(df, now); schedule_delayed_work(&podgov->idle_timer, msecs_to_jiffies(podgov->p_slowdown_delay)); break; case DEVICE_BUSY: cancel_delayed_work(&podgov->idle_timer); *freq = scaling_state_check(df, now); break; } if (!(*freq) || (freqlist_up(podgov, *freq, 0) == dev_stat.current_frequency)) return GET_TARGET_FREQ_DONTSCALE; podgov->last_scale = now; trace_podgov_estimate_freq(df->previous_freq, *freq); return 0; }
static int nvhost_scale3d_set_throughput_hint(struct notifier_block *nb, unsigned long action, void *data) { struct podgov_info_rec *podgov = container_of(nb, struct podgov_info_rec, throughput_hint_notifier); struct devfreq *df; struct platform_device *pdev; int hint = tegra_throughput_get_hint(); long idle; long curr, target; int avg_idle, avg_hint, scale_score; unsigned int smooth; if (!podgov) return NOTIFY_DONE; df = podgov->power_manager; if (!df) return NOTIFY_DONE; pdev = to_platform_device(df->dev.parent); /* make sure the device is alive before doing any scaling */ nvhost_module_busy_noresume(pdev); if (!pm_runtime_active(&pdev->dev)) { nvhost_module_idle(pdev); return 0; } mutex_lock(&podgov->power_manager->lock); podgov->block--; if (!podgov->enable || !podgov->p_use_throughput_hint || podgov->block > 0) goto exit_unlock; trace_podgov_hint(podgov->idle, hint); podgov->last_throughput_hint = ktime_get(); curr = podgov->power_manager->previous_freq; idle = podgov->idle; avg_idle = podgov->idle_avg; smooth = podgov->p_smooth; /* compute averages usings exponential-moving-average */ avg_hint = ((smooth*podgov->hint_avg + hint)/(smooth+1)); podgov->hint_avg = avg_hint; /* set the target using avg_hint and avg_idle */ target = curr; if (avg_hint < podgov->p_hint_lo_limit) { target = freqlist_up(podgov, curr, 1); } else { scale_score = avg_idle + avg_hint; if (scale_score > podgov->p_scaledown_limit) target = freqlist_down(podgov, curr, 1); else if (scale_score < podgov->p_scaleup_limit && hint < podgov->p_hint_hi_limit) target = freqlist_up(podgov, curr, 1); } /* clamp and apply target */ scaling_limit(df, &target); if (target != curr) { podgov->block = podgov->p_smooth; trace_podgov_do_scale(df->previous_freq, target); podgov->adjustment_frequency = target; podgov->adjustment_type = ADJUSTMENT_LOCAL; update_devfreq(df); } trace_podgov_print_target(idle, avg_idle, curr / 1000000, target, hint, avg_hint); exit_unlock: mutex_unlock(&podgov->power_manager->lock); nvhost_module_idle(pdev); return NOTIFY_OK; }