Loading drivers/devfreq/governor_msm_adreno_tz.c +1 −15 Original line number Diff line number Diff line /* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved. /* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -358,17 +358,6 @@ static int tz_stop(struct devfreq *devfreq) return 0; } static int tz_resume(struct devfreq *devfreq) { struct devfreq_dev_profile *profile = devfreq->profile; unsigned long freq; freq = profile->initial_freq; return profile->target(devfreq->dev.parent, &freq, 0); } static int tz_suspend(struct devfreq *devfreq) { struct devfreq_msm_adreno_tz_data *priv = devfreq->data; Loading Loading @@ -403,9 +392,6 @@ static int tz_handler(struct devfreq *devfreq, unsigned int event, void *data) break; case DEVFREQ_GOV_RESUME: result = tz_resume(devfreq); break; case DEVFREQ_GOV_INTERVAL: /* ignored, this governor doesn't use polling */ default: Loading drivers/gpu/msm/kgsl_pwrctrl.c +54 −4 Original line number Diff line number Diff line Loading @@ -188,7 +188,8 @@ static void _ab_buslevel_update(struct kgsl_pwrctrl *pwr, * constraint if one exists. */ static unsigned int _adjust_pwrlevel(struct kgsl_pwrctrl *pwr, int level, struct kgsl_pwr_constraint *pwrc) struct kgsl_pwr_constraint *pwrc, int popp) { unsigned int max_pwrlevel = max_t(unsigned int, pwr->thermal_pwrlevel, pwr->max_pwrlevel); Loading @@ -211,6 +212,9 @@ static unsigned int _adjust_pwrlevel(struct kgsl_pwrctrl *pwr, int level, break; } if (popp && (max_pwrlevel < pwr->active_pwrlevel)) max_pwrlevel = pwr->active_pwrlevel; if (level < max_pwrlevel) return max_pwrlevel; if (level > min_pwrlevel) Loading Loading @@ -360,7 +364,8 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, * Adjust the power level if required by thermal, max/min, * constraints, etc */ new_level = _adjust_pwrlevel(pwr, new_level, &pwr->constraint); new_level = _adjust_pwrlevel(pwr, new_level, &pwr->constraint, device->pwrscale.popp_level); /* * If thermal cycling is required and the new level hits the Loading Loading @@ -401,6 +406,9 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, pwrlevel->gpu_freq); /* Change register settings if any AFTER pwrlevel change*/ kgsl_pwrctrl_pwrlevel_change_settings(device, 1, 0); /* Timestamp the frequency change */ device->pwrscale.freq_change_time = ktime_to_ms(ktime_get()); } EXPORT_SYMBOL(kgsl_pwrctrl_pwrlevel_change); Loading @@ -424,7 +432,7 @@ void kgsl_pwrctrl_set_constraint(struct kgsl_device *device, if (device == NULL || pwrc == NULL) return; constraint = _adjust_pwrlevel(&device->pwrctrl, device->pwrctrl.active_pwrlevel, pwrc); device->pwrctrl.active_pwrlevel, pwrc, 0); pwrc_old = &device->pwrctrl.constraint; /* Loading Loading @@ -1040,6 +1048,43 @@ done: return count; } static ssize_t kgsl_popp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned int val = 0; struct kgsl_device *device = kgsl_device_from_dev(dev); int ret; if (device == NULL) return 0; ret = kgsl_sysfs_store(buf, &val); if (ret) return ret; mutex_lock(&device->mutex); if (val) set_bit(POPP_ON, &device->pwrscale.popp_state); else clear_bit(POPP_ON, &device->pwrscale.popp_state); mutex_unlock(&device->mutex); return count; } static ssize_t kgsl_popp_show(struct device *dev, struct device_attribute *attr, char *buf) { struct kgsl_device *device = kgsl_device_from_dev(dev); if (device == NULL) return 0; return snprintf(buf, PAGE_SIZE, "%d\n", test_bit(POPP_ON, &device->pwrscale.popp_state)); } static DEVICE_ATTR(gpuclk, 0644, kgsl_pwrctrl_gpuclk_show, kgsl_pwrctrl_gpuclk_store); static DEVICE_ATTR(max_gpuclk, 0644, kgsl_pwrctrl_max_gpuclk_show, Loading Loading @@ -1084,6 +1129,7 @@ static DEVICE_ATTR(bus_split, 0644, static DEVICE_ATTR(default_pwrlevel, 0644, kgsl_pwrctrl_default_pwrlevel_show, kgsl_pwrctrl_default_pwrlevel_store); static DEVICE_ATTR(popp, 0644, kgsl_popp_show, kgsl_popp_store); static const struct device_attribute *pwrctrl_attr_list[] = { &dev_attr_gpuclk, Loading @@ -1102,6 +1148,7 @@ static const struct device_attribute *pwrctrl_attr_list[] = { &dev_attr_force_rail_on, &dev_attr_bus_split, &dev_attr_default_pwrlevel, &dev_attr_popp, NULL }; Loading Loading @@ -1722,8 +1769,11 @@ static int kgsl_pwrctrl_enable(struct kgsl_device *device) if (pwr->wakeup_maxpwrlevel) { level = pwr->max_pwrlevel; pwr->wakeup_maxpwrlevel = 0; } else } else if (kgsl_popp_check(device)) { level = pwr->active_pwrlevel; } else { level = pwr->default_pwrlevel; } kgsl_pwrctrl_pwrlevel_change(device, level); Loading drivers/gpu/msm/kgsl_pwrscale.c +163 −2 Original line number Diff line number Diff line Loading @@ -22,6 +22,13 @@ #define FAST_BUS 1 #define SLOW_BUS -1 static struct kgsl_popp popp_param[POPP_MAX] = { {0, 0}, {-5, 20}, {-5, 0}, {0, 0}, }; static void do_devfreq_suspend(struct work_struct *work); static void do_devfreq_resume(struct work_struct *work); static void do_devfreq_notify(struct work_struct *work); Loading Loading @@ -108,14 +115,26 @@ EXPORT_SYMBOL(kgsl_pwrscale_busy); */ void kgsl_pwrscale_update_stats(struct kgsl_device *device) { struct kgsl_pwrscale *psc = &device->pwrscale; BUG_ON(!mutex_is_locked(&device->mutex)); if (!device->pwrscale.enabled) if (!psc->enabled) return; if (device->state == KGSL_STATE_ACTIVE) { struct kgsl_power_stats stats; device->ftbl->power_stats(device, &stats); if (psc->popp_level) { u64 x = stats.busy_time; u64 y = stats.ram_time; do_div(x, 100); do_div(y, 100); x *= popp_param[psc->popp_level].gpu_x; y *= popp_param[psc->popp_level].ddr_y; trace_kgsl_popp_mod(device, x, y); stats.busy_time += x; stats.ram_time += y; } device->pwrscale.accum_stats.busy_time += stats.busy_time; device->pwrscale.accum_stats.ram_time += stats.ram_time; device->pwrscale.accum_stats.ram_wait += stats.ram_wait; Loading Loading @@ -213,6 +232,145 @@ static int _thermal_adjust(struct kgsl_pwrctrl *pwr, int level) return level; } /* * Use various metrics including level stability, NAP intervals, and * overall GPU freq / DDR freq combination to decide if POPP should * be activated. */ static bool popp_stable(struct kgsl_device *device) { s64 t; struct kgsl_pwrctrl *pwr = &device->pwrctrl; struct kgsl_pwrscale *psc = &device->pwrscale; if (!test_bit(POPP_ON, &psc->popp_state)) return false; /* If running at turbo, min, or already pushed don't change levels */ if (test_bit(POPP_PUSH, &psc->popp_state) || pwr->active_pwrlevel == 0 || (!psc->popp_level && pwr->active_pwrlevel == pwr->min_pwrlevel)) return false; t = ktime_to_ms(ktime_get()); if ((device->pwrscale.freq_change_time + STABLE_TIME) < t) { device->pwrscale.freq_change_time = t; return true; } return false; } bool kgsl_popp_check(struct kgsl_device *device) { int i; struct kgsl_pwrscale *psc = &device->pwrscale; struct kgsl_pwr_event *e; if (!test_bit(POPP_ON, &psc->popp_state)) return false; if (!test_bit(POPP_PUSH, &psc->popp_state)) return false; if (psc->history[KGSL_PWREVENT_STATE].events == NULL) { clear_bit(POPP_PUSH, &psc->popp_state); return false; } e = &psc->history[KGSL_PWREVENT_STATE]. events[psc->history[KGSL_PWREVENT_STATE].index]; if (e->data == KGSL_STATE_SLUMBER) e->duration = ktime_us_delta(ktime_get(), e->start); /* If there's been a long SLUMBER in recent history, clear the _PUSH */ for (i = 0; i < psc->history[KGSL_PWREVENT_STATE].size; i++) { e = &psc->history[KGSL_PWREVENT_STATE].events[i]; if ((e->data == KGSL_STATE_SLUMBER) && (e->duration > POPP_RESET_TIME)) { clear_bit(POPP_PUSH, &psc->popp_state); return false; } } return true; } /* * The GPU has been running at the current frequency for a while. Attempt * to lower the frequency for boarderline cases. */ static void popp_trans1(struct kgsl_device *device) { struct kgsl_pwrctrl *pwr = &device->pwrctrl; struct kgsl_pwrscale *psc = &device->pwrscale; int old_level = psc->popp_level; switch (old_level) { case 0: psc->popp_level = 2; kgsl_pwrctrl_pwrlevel_change(device, pwr->active_pwrlevel + 1); break; case 1: case 2: psc->popp_level++; break; case 3: set_bit(POPP_PUSH, &psc->popp_state); psc->popp_level = 0; break; case POPP_MAX: default: psc->popp_level = 0; break; } trace_kgsl_popp_level(device, old_level, psc->popp_level); } /* * The GPU DCVS algorithm recommends a level change. Apply any * POPP restrictions and update the level accordingly */ static int popp_trans2(struct kgsl_device *device, int level) { struct kgsl_pwrctrl *pwr = &device->pwrctrl; struct kgsl_pwrscale *psc = &device->pwrscale; int old_level = psc->popp_level; if (!test_bit(POPP_ON, &psc->popp_state)) return level; clear_bit(POPP_PUSH, &psc->popp_state); /* If the governor recommends going down, do it! */ if (pwr->active_pwrlevel < level) { psc->popp_level = 0; trace_kgsl_popp_level(device, old_level, psc->popp_level); return level; } switch (psc->popp_level) { case 0: /* If the feature isn't engaged, go up immediately */ break; case 1: /* Turn off mitigation, and go up a level */ psc->popp_level = 0; break; case 2: case 3: /* Try a more aggressive mitigation */ psc->popp_level--; level++; break; case POPP_MAX: default: psc->popp_level = 0; break; } trace_kgsl_popp_level(device, old_level, psc->popp_level); return level; } /* * kgsl_devfreq_target - devfreq_dev_profile.target callback * @dev: see devfreq.h Loading Loading @@ -259,11 +417,13 @@ int kgsl_devfreq_target(struct device *dev, unsigned long *freq, u32 flags) if (pwr->thermal_cycle == CYCLE_ACTIVE) level = _thermal_adjust(pwr, i); else level = i; level = popp_trans2(device, i); break; } if (level != pwr->active_pwrlevel) kgsl_pwrctrl_pwrlevel_change(device, level); } else if (popp_stable(device)) { popp_trans1(device); } *freq = kgsl_pwrctrl_active_freq(pwr); Loading Loading @@ -617,6 +777,7 @@ int kgsl_pwrscale_init(struct device *dev, const char *governor) sizeof(struct kgsl_pwr_event), GFP_KERNEL); pwrscale->history[i].type = i; } set_bit(POPP_ON, &pwrscale->popp_state); return 0; } Loading drivers/gpu/msm/kgsl_pwrscale.h +49 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,27 @@ #define KGSL_PWREVENT_POPP 3 #define KGSL_PWREVENT_MAX 4 /** * Amount of time running at a level to be considered * "stable" in msec */ #define STABLE_TIME 150 /* Amount of idle time needed to re-set stability in usec */ #define POPP_RESET_TIME 1000000 /* Number of POPP levels */ #define POPP_MAX 4 /* POPP state bits */ #define POPP_ON BIT(0) #define POPP_PUSH BIT(1) struct kgsl_popp { int gpu_x; int ddr_y; }; struct kgsl_power_stats { u64 busy_time; u64 ram_time; Loading @@ -46,6 +67,29 @@ struct kgsl_pwr_history { unsigned int size; }; /** * struct kgsl_pwrscale - Power scaling settings for a KGSL device * @devfreqptr - Pointer to the devfreq device * @gpu_profile - GPU profile data for the devfreq device * @bus_profile - Bus specific data for the bus devfreq device * @freq_table - GPU frequencies for the DCVS algorithm * @last_governor - Prior devfreq governor * @accum_stats - Accumulated statistics for various frequency calculations * @enabled - Whether or not power scaling is enabled * @time - Last submitted sample timestamp * @on_time - Timestamp when gpu busy begins * @freq_change_time - Timestamp of last freq change or popp update * @nh - Notifier for the partner devfreq bus device * @devfreq_wq - Main devfreq workqueue * @devfreq_suspend_ws - Pass device suspension to devfreq * @devfreq_resume_ws - Pass device resume to devfreq * @devfreq_notify_ws - Notify devfreq to update sampling * @next_governor_call - Timestamp after which the governor may be notified of * a new sample * @history - History of power events with timestamps and durations * @popp_level - Current level of POPP mitigation * @popp_state - Control state for POPP, on/off, recently pushed, etc */ struct kgsl_pwrscale { struct devfreq *devfreqptr; struct msm_adreno_extended_profile gpu_profile; Loading @@ -56,6 +100,7 @@ struct kgsl_pwrscale { bool enabled; ktime_t time; s64 on_time; s64 freq_change_time; struct srcu_notifier_head nh; struct workqueue_struct *devfreq_wq; struct work_struct devfreq_suspend_ws; Loading @@ -63,6 +108,8 @@ struct kgsl_pwrscale { struct work_struct devfreq_notify_ws; ktime_t next_governor_call; struct kgsl_pwr_history history[KGSL_PWREVENT_MAX]; int popp_level; unsigned long popp_state; }; int kgsl_pwrscale_init(struct device *dev, const char *governor); Loading @@ -85,6 +132,8 @@ int kgsl_busmon_target(struct device *dev, unsigned long *freq, u32 flags); int kgsl_busmon_get_dev_status(struct device *, struct devfreq_dev_status *); int kgsl_busmon_get_cur_freq(struct device *dev, unsigned long *freq); bool kgsl_popp_check(struct kgsl_device *device); #define KGSL_PWRSCALE_INIT(_priv_data) { \ .enabled = true, \ Loading drivers/gpu/msm/kgsl_trace.h +47 −1 Original line number Diff line number Diff line /* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. /* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -854,6 +854,52 @@ TRACE_EVENT(kgsl_regwrite, ) ); TRACE_EVENT(kgsl_popp_level, TP_PROTO(struct kgsl_device *device, int level1, int level2), TP_ARGS(device, level1, level2), TP_STRUCT__entry( __string(device_name, device->name) __field(int, level1) __field(int, level2) ), TP_fast_assign( __assign_str(device_name, device->name); __entry->level1 = level1; __entry->level2 = level2; ), TP_printk( "d_name=%s old level=%d new level=%d", __get_str(device_name), __entry->level1, __entry->level2) ); TRACE_EVENT(kgsl_popp_mod, TP_PROTO(struct kgsl_device *device, int x, int y), TP_ARGS(device, x, y), TP_STRUCT__entry( __string(device_name, device->name) __field(int, x) __field(int, y) ), TP_fast_assign( __assign_str(device_name, device->name); __entry->x = x; __entry->y = y; ), TP_printk( "d_name=%s GPU busy mod=%d bus busy mod=%d", __get_str(device_name), __entry->x, __entry->y) ); TRACE_EVENT(kgsl_register_event, TP_PROTO(unsigned int id, unsigned int timestamp, void *func), TP_ARGS(id, timestamp, func), Loading Loading
drivers/devfreq/governor_msm_adreno_tz.c +1 −15 Original line number Diff line number Diff line /* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved. /* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -358,17 +358,6 @@ static int tz_stop(struct devfreq *devfreq) return 0; } static int tz_resume(struct devfreq *devfreq) { struct devfreq_dev_profile *profile = devfreq->profile; unsigned long freq; freq = profile->initial_freq; return profile->target(devfreq->dev.parent, &freq, 0); } static int tz_suspend(struct devfreq *devfreq) { struct devfreq_msm_adreno_tz_data *priv = devfreq->data; Loading Loading @@ -403,9 +392,6 @@ static int tz_handler(struct devfreq *devfreq, unsigned int event, void *data) break; case DEVFREQ_GOV_RESUME: result = tz_resume(devfreq); break; case DEVFREQ_GOV_INTERVAL: /* ignored, this governor doesn't use polling */ default: Loading
drivers/gpu/msm/kgsl_pwrctrl.c +54 −4 Original line number Diff line number Diff line Loading @@ -188,7 +188,8 @@ static void _ab_buslevel_update(struct kgsl_pwrctrl *pwr, * constraint if one exists. */ static unsigned int _adjust_pwrlevel(struct kgsl_pwrctrl *pwr, int level, struct kgsl_pwr_constraint *pwrc) struct kgsl_pwr_constraint *pwrc, int popp) { unsigned int max_pwrlevel = max_t(unsigned int, pwr->thermal_pwrlevel, pwr->max_pwrlevel); Loading @@ -211,6 +212,9 @@ static unsigned int _adjust_pwrlevel(struct kgsl_pwrctrl *pwr, int level, break; } if (popp && (max_pwrlevel < pwr->active_pwrlevel)) max_pwrlevel = pwr->active_pwrlevel; if (level < max_pwrlevel) return max_pwrlevel; if (level > min_pwrlevel) Loading Loading @@ -360,7 +364,8 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, * Adjust the power level if required by thermal, max/min, * constraints, etc */ new_level = _adjust_pwrlevel(pwr, new_level, &pwr->constraint); new_level = _adjust_pwrlevel(pwr, new_level, &pwr->constraint, device->pwrscale.popp_level); /* * If thermal cycling is required and the new level hits the Loading Loading @@ -401,6 +406,9 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, pwrlevel->gpu_freq); /* Change register settings if any AFTER pwrlevel change*/ kgsl_pwrctrl_pwrlevel_change_settings(device, 1, 0); /* Timestamp the frequency change */ device->pwrscale.freq_change_time = ktime_to_ms(ktime_get()); } EXPORT_SYMBOL(kgsl_pwrctrl_pwrlevel_change); Loading @@ -424,7 +432,7 @@ void kgsl_pwrctrl_set_constraint(struct kgsl_device *device, if (device == NULL || pwrc == NULL) return; constraint = _adjust_pwrlevel(&device->pwrctrl, device->pwrctrl.active_pwrlevel, pwrc); device->pwrctrl.active_pwrlevel, pwrc, 0); pwrc_old = &device->pwrctrl.constraint; /* Loading Loading @@ -1040,6 +1048,43 @@ done: return count; } static ssize_t kgsl_popp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned int val = 0; struct kgsl_device *device = kgsl_device_from_dev(dev); int ret; if (device == NULL) return 0; ret = kgsl_sysfs_store(buf, &val); if (ret) return ret; mutex_lock(&device->mutex); if (val) set_bit(POPP_ON, &device->pwrscale.popp_state); else clear_bit(POPP_ON, &device->pwrscale.popp_state); mutex_unlock(&device->mutex); return count; } static ssize_t kgsl_popp_show(struct device *dev, struct device_attribute *attr, char *buf) { struct kgsl_device *device = kgsl_device_from_dev(dev); if (device == NULL) return 0; return snprintf(buf, PAGE_SIZE, "%d\n", test_bit(POPP_ON, &device->pwrscale.popp_state)); } static DEVICE_ATTR(gpuclk, 0644, kgsl_pwrctrl_gpuclk_show, kgsl_pwrctrl_gpuclk_store); static DEVICE_ATTR(max_gpuclk, 0644, kgsl_pwrctrl_max_gpuclk_show, Loading Loading @@ -1084,6 +1129,7 @@ static DEVICE_ATTR(bus_split, 0644, static DEVICE_ATTR(default_pwrlevel, 0644, kgsl_pwrctrl_default_pwrlevel_show, kgsl_pwrctrl_default_pwrlevel_store); static DEVICE_ATTR(popp, 0644, kgsl_popp_show, kgsl_popp_store); static const struct device_attribute *pwrctrl_attr_list[] = { &dev_attr_gpuclk, Loading @@ -1102,6 +1148,7 @@ static const struct device_attribute *pwrctrl_attr_list[] = { &dev_attr_force_rail_on, &dev_attr_bus_split, &dev_attr_default_pwrlevel, &dev_attr_popp, NULL }; Loading Loading @@ -1722,8 +1769,11 @@ static int kgsl_pwrctrl_enable(struct kgsl_device *device) if (pwr->wakeup_maxpwrlevel) { level = pwr->max_pwrlevel; pwr->wakeup_maxpwrlevel = 0; } else } else if (kgsl_popp_check(device)) { level = pwr->active_pwrlevel; } else { level = pwr->default_pwrlevel; } kgsl_pwrctrl_pwrlevel_change(device, level); Loading
drivers/gpu/msm/kgsl_pwrscale.c +163 −2 Original line number Diff line number Diff line Loading @@ -22,6 +22,13 @@ #define FAST_BUS 1 #define SLOW_BUS -1 static struct kgsl_popp popp_param[POPP_MAX] = { {0, 0}, {-5, 20}, {-5, 0}, {0, 0}, }; static void do_devfreq_suspend(struct work_struct *work); static void do_devfreq_resume(struct work_struct *work); static void do_devfreq_notify(struct work_struct *work); Loading Loading @@ -108,14 +115,26 @@ EXPORT_SYMBOL(kgsl_pwrscale_busy); */ void kgsl_pwrscale_update_stats(struct kgsl_device *device) { struct kgsl_pwrscale *psc = &device->pwrscale; BUG_ON(!mutex_is_locked(&device->mutex)); if (!device->pwrscale.enabled) if (!psc->enabled) return; if (device->state == KGSL_STATE_ACTIVE) { struct kgsl_power_stats stats; device->ftbl->power_stats(device, &stats); if (psc->popp_level) { u64 x = stats.busy_time; u64 y = stats.ram_time; do_div(x, 100); do_div(y, 100); x *= popp_param[psc->popp_level].gpu_x; y *= popp_param[psc->popp_level].ddr_y; trace_kgsl_popp_mod(device, x, y); stats.busy_time += x; stats.ram_time += y; } device->pwrscale.accum_stats.busy_time += stats.busy_time; device->pwrscale.accum_stats.ram_time += stats.ram_time; device->pwrscale.accum_stats.ram_wait += stats.ram_wait; Loading Loading @@ -213,6 +232,145 @@ static int _thermal_adjust(struct kgsl_pwrctrl *pwr, int level) return level; } /* * Use various metrics including level stability, NAP intervals, and * overall GPU freq / DDR freq combination to decide if POPP should * be activated. */ static bool popp_stable(struct kgsl_device *device) { s64 t; struct kgsl_pwrctrl *pwr = &device->pwrctrl; struct kgsl_pwrscale *psc = &device->pwrscale; if (!test_bit(POPP_ON, &psc->popp_state)) return false; /* If running at turbo, min, or already pushed don't change levels */ if (test_bit(POPP_PUSH, &psc->popp_state) || pwr->active_pwrlevel == 0 || (!psc->popp_level && pwr->active_pwrlevel == pwr->min_pwrlevel)) return false; t = ktime_to_ms(ktime_get()); if ((device->pwrscale.freq_change_time + STABLE_TIME) < t) { device->pwrscale.freq_change_time = t; return true; } return false; } bool kgsl_popp_check(struct kgsl_device *device) { int i; struct kgsl_pwrscale *psc = &device->pwrscale; struct kgsl_pwr_event *e; if (!test_bit(POPP_ON, &psc->popp_state)) return false; if (!test_bit(POPP_PUSH, &psc->popp_state)) return false; if (psc->history[KGSL_PWREVENT_STATE].events == NULL) { clear_bit(POPP_PUSH, &psc->popp_state); return false; } e = &psc->history[KGSL_PWREVENT_STATE]. events[psc->history[KGSL_PWREVENT_STATE].index]; if (e->data == KGSL_STATE_SLUMBER) e->duration = ktime_us_delta(ktime_get(), e->start); /* If there's been a long SLUMBER in recent history, clear the _PUSH */ for (i = 0; i < psc->history[KGSL_PWREVENT_STATE].size; i++) { e = &psc->history[KGSL_PWREVENT_STATE].events[i]; if ((e->data == KGSL_STATE_SLUMBER) && (e->duration > POPP_RESET_TIME)) { clear_bit(POPP_PUSH, &psc->popp_state); return false; } } return true; } /* * The GPU has been running at the current frequency for a while. Attempt * to lower the frequency for boarderline cases. */ static void popp_trans1(struct kgsl_device *device) { struct kgsl_pwrctrl *pwr = &device->pwrctrl; struct kgsl_pwrscale *psc = &device->pwrscale; int old_level = psc->popp_level; switch (old_level) { case 0: psc->popp_level = 2; kgsl_pwrctrl_pwrlevel_change(device, pwr->active_pwrlevel + 1); break; case 1: case 2: psc->popp_level++; break; case 3: set_bit(POPP_PUSH, &psc->popp_state); psc->popp_level = 0; break; case POPP_MAX: default: psc->popp_level = 0; break; } trace_kgsl_popp_level(device, old_level, psc->popp_level); } /* * The GPU DCVS algorithm recommends a level change. Apply any * POPP restrictions and update the level accordingly */ static int popp_trans2(struct kgsl_device *device, int level) { struct kgsl_pwrctrl *pwr = &device->pwrctrl; struct kgsl_pwrscale *psc = &device->pwrscale; int old_level = psc->popp_level; if (!test_bit(POPP_ON, &psc->popp_state)) return level; clear_bit(POPP_PUSH, &psc->popp_state); /* If the governor recommends going down, do it! */ if (pwr->active_pwrlevel < level) { psc->popp_level = 0; trace_kgsl_popp_level(device, old_level, psc->popp_level); return level; } switch (psc->popp_level) { case 0: /* If the feature isn't engaged, go up immediately */ break; case 1: /* Turn off mitigation, and go up a level */ psc->popp_level = 0; break; case 2: case 3: /* Try a more aggressive mitigation */ psc->popp_level--; level++; break; case POPP_MAX: default: psc->popp_level = 0; break; } trace_kgsl_popp_level(device, old_level, psc->popp_level); return level; } /* * kgsl_devfreq_target - devfreq_dev_profile.target callback * @dev: see devfreq.h Loading Loading @@ -259,11 +417,13 @@ int kgsl_devfreq_target(struct device *dev, unsigned long *freq, u32 flags) if (pwr->thermal_cycle == CYCLE_ACTIVE) level = _thermal_adjust(pwr, i); else level = i; level = popp_trans2(device, i); break; } if (level != pwr->active_pwrlevel) kgsl_pwrctrl_pwrlevel_change(device, level); } else if (popp_stable(device)) { popp_trans1(device); } *freq = kgsl_pwrctrl_active_freq(pwr); Loading Loading @@ -617,6 +777,7 @@ int kgsl_pwrscale_init(struct device *dev, const char *governor) sizeof(struct kgsl_pwr_event), GFP_KERNEL); pwrscale->history[i].type = i; } set_bit(POPP_ON, &pwrscale->popp_state); return 0; } Loading
drivers/gpu/msm/kgsl_pwrscale.h +49 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,27 @@ #define KGSL_PWREVENT_POPP 3 #define KGSL_PWREVENT_MAX 4 /** * Amount of time running at a level to be considered * "stable" in msec */ #define STABLE_TIME 150 /* Amount of idle time needed to re-set stability in usec */ #define POPP_RESET_TIME 1000000 /* Number of POPP levels */ #define POPP_MAX 4 /* POPP state bits */ #define POPP_ON BIT(0) #define POPP_PUSH BIT(1) struct kgsl_popp { int gpu_x; int ddr_y; }; struct kgsl_power_stats { u64 busy_time; u64 ram_time; Loading @@ -46,6 +67,29 @@ struct kgsl_pwr_history { unsigned int size; }; /** * struct kgsl_pwrscale - Power scaling settings for a KGSL device * @devfreqptr - Pointer to the devfreq device * @gpu_profile - GPU profile data for the devfreq device * @bus_profile - Bus specific data for the bus devfreq device * @freq_table - GPU frequencies for the DCVS algorithm * @last_governor - Prior devfreq governor * @accum_stats - Accumulated statistics for various frequency calculations * @enabled - Whether or not power scaling is enabled * @time - Last submitted sample timestamp * @on_time - Timestamp when gpu busy begins * @freq_change_time - Timestamp of last freq change or popp update * @nh - Notifier for the partner devfreq bus device * @devfreq_wq - Main devfreq workqueue * @devfreq_suspend_ws - Pass device suspension to devfreq * @devfreq_resume_ws - Pass device resume to devfreq * @devfreq_notify_ws - Notify devfreq to update sampling * @next_governor_call - Timestamp after which the governor may be notified of * a new sample * @history - History of power events with timestamps and durations * @popp_level - Current level of POPP mitigation * @popp_state - Control state for POPP, on/off, recently pushed, etc */ struct kgsl_pwrscale { struct devfreq *devfreqptr; struct msm_adreno_extended_profile gpu_profile; Loading @@ -56,6 +100,7 @@ struct kgsl_pwrscale { bool enabled; ktime_t time; s64 on_time; s64 freq_change_time; struct srcu_notifier_head nh; struct workqueue_struct *devfreq_wq; struct work_struct devfreq_suspend_ws; Loading @@ -63,6 +108,8 @@ struct kgsl_pwrscale { struct work_struct devfreq_notify_ws; ktime_t next_governor_call; struct kgsl_pwr_history history[KGSL_PWREVENT_MAX]; int popp_level; unsigned long popp_state; }; int kgsl_pwrscale_init(struct device *dev, const char *governor); Loading @@ -85,6 +132,8 @@ int kgsl_busmon_target(struct device *dev, unsigned long *freq, u32 flags); int kgsl_busmon_get_dev_status(struct device *, struct devfreq_dev_status *); int kgsl_busmon_get_cur_freq(struct device *dev, unsigned long *freq); bool kgsl_popp_check(struct kgsl_device *device); #define KGSL_PWRSCALE_INIT(_priv_data) { \ .enabled = true, \ Loading
drivers/gpu/msm/kgsl_trace.h +47 −1 Original line number Diff line number Diff line /* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. /* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -854,6 +854,52 @@ TRACE_EVENT(kgsl_regwrite, ) ); TRACE_EVENT(kgsl_popp_level, TP_PROTO(struct kgsl_device *device, int level1, int level2), TP_ARGS(device, level1, level2), TP_STRUCT__entry( __string(device_name, device->name) __field(int, level1) __field(int, level2) ), TP_fast_assign( __assign_str(device_name, device->name); __entry->level1 = level1; __entry->level2 = level2; ), TP_printk( "d_name=%s old level=%d new level=%d", __get_str(device_name), __entry->level1, __entry->level2) ); TRACE_EVENT(kgsl_popp_mod, TP_PROTO(struct kgsl_device *device, int x, int y), TP_ARGS(device, x, y), TP_STRUCT__entry( __string(device_name, device->name) __field(int, x) __field(int, y) ), TP_fast_assign( __assign_str(device_name, device->name); __entry->x = x; __entry->y = y; ), TP_printk( "d_name=%s GPU busy mod=%d bus busy mod=%d", __get_str(device_name), __entry->x, __entry->y) ); TRACE_EVENT(kgsl_register_event, TP_PROTO(unsigned int id, unsigned int timestamp, void *func), TP_ARGS(id, timestamp, func), Loading