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_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; }