static int devfreq_watermark_target_freq(struct devfreq *df, unsigned long *freq) { struct wmark_gov_info *wmarkinfo = df->data; struct devfreq_dev_status dev_stat; int err; err = df->profile->get_dev_status(df->dev.parent, &dev_stat); if (err < 0) return err; switch (wmarkinfo->event) { case HIGH_WATERMARK_EVENT: *freq = freqlist_up(wmarkinfo, dev_stat.current_frequency); /* always enable low watermark */ df->profile->set_low_wmark(df->dev.parent, wmarkinfo->p_low_wmark); /* disable high watermark if no change */ if (*freq == wmarkinfo->last_request) df->profile->set_high_wmark(df->dev.parent, 1000); break; case LOW_WATERMARK_EVENT: *freq = freqlist_down(wmarkinfo, dev_stat.current_frequency); /* always enable high watermark */ df->profile->set_high_wmark(df->dev.parent, wmarkinfo->p_high_wmark); /* disable low watermark if no change */ if (*freq == wmarkinfo->last_request) df->profile->set_low_wmark(df->dev.parent, 0); break; default: break; } /* Mark that you handled event */ wmarkinfo->event = NO_WATERMARK_EVENT; wmarkinfo->last_request = *freq; return 0; }
static void update_watermarks(struct devfreq *df, unsigned long current_frequency, unsigned long ideal_frequency) { struct wmark_gov_info *wmarkinfo = df->data; unsigned long long relation = 0, next_freq = 0; unsigned long long current_frequency_khz = current_frequency / 1000; if (ideal_frequency == wmarkinfo->freqlist[0]) { /* disable the low watermark if we are at lowest clock */ df->profile->set_low_wmark(df->dev.parent, 0); } else { /* calculate the low threshold; what is the load value * at which we would go into lower frequency given the * that we are running at the new frequency? */ next_freq = freqlist_down(wmarkinfo, ideal_frequency); relation = ((next_freq / current_frequency_khz) * wmarkinfo->p_load_target) / 1000; df->profile->set_low_wmark(df->dev.parent, relation); } if (ideal_frequency == wmarkinfo->freqlist[wmarkinfo->freq_count - 1]) { /* disable the high watermark if we are at highest clock */ df->profile->set_high_wmark(df->dev.parent, 1000); } else { /* calculate the high threshold; what is the load value * at which we would go into highest frequency given the * that we are running at the new frequency? */ next_freq = freqlist_up(wmarkinfo, ideal_frequency); relation = ((next_freq / current_frequency_khz) * wmarkinfo->p_load_target) / 1000; relation = min((unsigned long long)wmarkinfo->p_load_max, relation); df->profile->set_high_wmark(df->dev.parent, relation); } }
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_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; }