diff --git a/drivers/gpu/msm/Kconfig b/drivers/gpu/msm/Kconfig index ba63fbcbbb4..515d7e91384 100644 --- a/drivers/gpu/msm/Kconfig +++ b/drivers/gpu/msm/Kconfig @@ -4,6 +4,9 @@ config MSM_KGSL depends on ARCH_MSM && !ARCH_MSM7X00A && !ARCH_MSM7X25 select GENERIC_ALLOCATOR select FW_LOADER + select PM_DEVFREQ + select DEVFREQ_GOV_SIMPLE_ONDEMAND + select DEVFREQ_GOV_PERFORMANCE ---help--- 3D graphics driver. Required to use hardware accelerated OpenGL ES 2.0 and 1.1. @@ -60,6 +63,16 @@ config MSM_KGSL_2D default y depends on MSM_KGSL && !ARCH_MSM7X27 && !ARCH_MSM7X27A && !(ARCH_QSD8X50 && !MSM_SOC_REV_A) +config MSM_ADRENO_DEFAULT_GOVERNOR + string "devfreq governor for the adreno core" + default "simple_ondemand" + depends on MSM_KGSL + +config MSM_Z180_DEFAULT_GOVERNOR + string "devfreq governor for the z180 core(s)" + default "performance" + depends on MSM_KGSL_2D + config MSM_KGSL_DRM bool "Build a DRM interface for the MSM_KGSL driver" depends on MSM_KGSL && DRM diff --git a/drivers/gpu/msm/Makefile b/drivers/gpu/msm/Makefile index aac183b91ab..14e07e57ed2 100644 --- a/drivers/gpu/msm/Makefile +++ b/drivers/gpu/msm/Makefile @@ -15,8 +15,6 @@ msm_kgsl_core-y = \ msm_kgsl_core-$(CONFIG_DEBUG_FS) += kgsl_debugfs.o msm_kgsl_core-$(CONFIG_MSM_KGSL_CFF_DUMP) += kgsl_cffdump.o msm_kgsl_core-$(CONFIG_MSM_KGSL_DRM) += kgsl_drm.o -msm_kgsl_core-$(CONFIG_MSM_SCM) += kgsl_pwrscale_trustzone.o -msm_kgsl_core-$(CONFIG_MSM_SLEEP_STATS_DEVICE) += kgsl_pwrscale_idlestats.o msm_kgsl_core-$(CONFIG_SYNC) += kgsl_sync.o msm_adreno-y += \ diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index a271388cda6..701a5446825 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -77,11 +77,27 @@ #define KGSL_LOG_LEVEL_DEFAULT 3 +/* + * The default values for the simpleondemand governor are 90 and 5, + * we use different values here. + * They have to be tuned and compare with the tz governor anyway. + */ +static struct devfreq_simple_ondemand_data adreno_ondemand_data = { + .upthreshold = 80, + .downdifferential = 20, +}; + +static const struct devfreq_governor_data adreno_governors[] = { + { .name = "simple_ondemand", .data = &adreno_ondemand_data }, +}; + static const struct kgsl_functable adreno_functable; static struct adreno_device device_3d0 = { .dev = { KGSL_DEVICE_COMMON_INIT(device_3d0.dev), + .pwrscale = KGSL_PWRSCALE_INIT(adreno_governors, + ARRAY_SIZE(adreno_governors)), .name = DEVICE_3D0_NAME, .id = KGSL_DEVICE_3D0, .mh = { @@ -1635,8 +1651,7 @@ adreno_probe(struct platform_device *pdev) adreno_ft_init_sysfs(device); - kgsl_pwrscale_init(device); - kgsl_pwrscale_attach_policy(device, ADRENO_DEFAULT_PWRSCALE_POLICY); + kgsl_pwrscale_init(&pdev->dev, CONFIG_MSM_ADRENO_DEFAULT_GOVERNOR); device->flags &= ~KGSL_FLAGS_SOFT_RESET; pdata = kgsl_device_get_drvdata(device); @@ -1666,7 +1681,6 @@ static int __devexit adreno_remove(struct platform_device *pdev) adreno_coresight_remove(pdev); adreno_profile_close(device); - kgsl_pwrscale_detach_policy(device); kgsl_pwrscale_close(device); adreno_dispatcher_close(adreno_dev); @@ -2855,6 +2869,7 @@ static void adreno_power_stats(struct kgsl_device *device, struct kgsl_pwrctrl *pwr = &device->pwrctrl; unsigned int cycles = 0; + memset(stats, 0, sizeof(*stats)); /* * Get the busy cycles counted since the counter was last reset. * If we're not currently active, there shouldn't have been @@ -2863,22 +2878,8 @@ static void adreno_power_stats(struct kgsl_device *device, if (device->state == KGSL_STATE_ACTIVE) cycles = adreno_dev->gpudev->busy_cycles(adreno_dev); - /* - * In order to calculate idle you have to have run the algorithm - * at least once to get a start time. - */ - if (pwr->time != 0) { - s64 tmp = ktime_to_us(ktime_get()); - stats->total_time = tmp - pwr->time; - pwr->time = tmp; - stats->busy_time = adreno_ticks_to_us(cycles, device->pwrctrl. - pwrlevels[device->pwrctrl.active_pwrlevel]. - gpu_freq); - } else { - stats->total_time = 0; - stats->busy_time = 0; - pwr->time = ktime_to_us(ktime_get()); - } + stats->busy_time = adreno_ticks_to_us(cycles, + kgsl_pwrctrl_active_freq(pwr)); } void adreno_irqctrl(struct kgsl_device *device, int state) diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h index 418d2308aef..5498766e756 100644 --- a/drivers/gpu/msm/adreno.h +++ b/drivers/gpu/msm/adreno.h @@ -62,6 +62,7 @@ #define ADRENO_DEFAULT_PWRSCALE_POLICY NULL #endif +void adreno_debugfs_init(struct kgsl_device *device); #define ADRENO_ISTORE_START 0x5000 /* Istore offset */ diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index fe6b34c11dd..6c1ed92b90d 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -605,7 +605,6 @@ EXPORT_SYMBOL(kgsl_check_timestamp); static int kgsl_suspend_device(struct kgsl_device *device, pm_message_t state) { int status = -EINVAL; - struct kgsl_pwrscale_policy *policy_saved; if (!device) return -EINVAL; @@ -613,8 +612,6 @@ static int kgsl_suspend_device(struct kgsl_device *device, pm_message_t state) KGSL_PWR_WARN(device, "suspend start\n"); mutex_lock(&device->mutex); - policy_saved = device->pwrscale.policy; - device->pwrscale.policy = NULL; kgsl_pwrctrl_request_state(device, KGSL_STATE_SUSPEND); /* Tell the device to drain the submission queue */ @@ -659,7 +656,7 @@ static int kgsl_suspend_device(struct kgsl_device *device, pm_message_t state) goto end; } kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); - device->pwrscale.policy = policy_saved; + kgsl_pwrscale_sleep(device); status = 0; end: diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index f87c64ca278..b76e9ff9c51 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -292,7 +292,6 @@ struct kgsl_device { struct list_head events; struct list_head events_pending_list; unsigned int events_last_timestamp; - s64 on_time; /* Postmortem Control switches */ int pm_regs_enabled; @@ -418,11 +417,6 @@ struct kgsl_device_private { struct kgsl_process_private *process_priv; }; -struct kgsl_power_stats { - s64 total_time; - s64 busy_time; -}; - struct kgsl_device *kgsl_get_device(int dev_idx); int kgsl_add_event(struct kgsl_device *device, u32 id, u32 ts, diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 8ed29fbda48..769abfcc0b9 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -226,11 +226,8 @@ static int kgsl_pwrctrl_thermal_pwrlevel_store(struct device *dev, * a policy only change the active clock if it is higher then the new * thermal level */ - - if (device->pwrscale.policy == NULL || - pwr->thermal_pwrlevel > pwr->active_pwrlevel) + if (pwr->thermal_pwrlevel > pwr->active_pwrlevel) kgsl_pwrctrl_pwrlevel_change(device, pwr->thermal_pwrlevel); - mutex_unlock(&device->mutex); return count; @@ -285,11 +282,8 @@ static int kgsl_pwrctrl_max_pwrlevel_store(struct device *dev, * If there is no policy then move to max by default. Otherwise only * move max if the current level happens to be higher then the new max */ - - if (device->pwrscale.policy == NULL || - (max_level > pwr->active_pwrlevel)) + if (max_level > pwr->active_pwrlevel) kgsl_pwrctrl_pwrlevel_change(device, max_level); - mutex_unlock(&device->mutex); return count; @@ -479,8 +473,7 @@ static int kgsl_pwrctrl_gpuclk_show(struct device *dev, if (device == NULL) return 0; pwr = &device->pwrctrl; - return snprintf(buf, PAGE_SIZE, "%d\n", - pwr->pwrlevels[pwr->active_pwrlevel].gpu_freq); + return snprintf(buf, PAGE_SIZE, "%ld\n", kgsl_pwrctrl_active_freq(pwr)); } static int kgsl_pwrctrl_idle_timer_store(struct device *dev, @@ -1179,7 +1172,7 @@ void kgsl_idle_check(struct work_struct *work) mutex_lock(&device->mutex); - kgsl_pwrscale_idle(device); + kgsl_pwrscale_update(device); if (device->state == KGSL_STATE_ACTIVE || device->state == KGSL_STATE_NAP) { @@ -1275,12 +1268,25 @@ EXPORT_SYMBOL(kgsl_pre_hwaccess); static int _nap(struct kgsl_device *device) { + struct kgsl_power_stats stats; + switch (device->state) { case KGSL_STATE_ACTIVE: if (!device->ftbl->isidle(device)) { kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); return -EBUSY; } + + /* + * Read HW busy counters before going to NAP state. + * The data might be used by power scale govenors + * independently of the HW activity. For example + * the simple-on-demand governer will get the latest + * busy_time data even if the gpu isn't active. + */ + device->ftbl->power_stats(device, &stats); + device->pwrscale.accum_stats.busy_time += stats.busy_time; + kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_OFF, KGSL_STATE_NAP); kgsl_pwrctrl_set_state(device, KGSL_STATE_NAP); @@ -1300,7 +1306,7 @@ _sleep_accounting(struct kgsl_device *device) { kgsl_pwrctrl_busy_time(device, false); device->pwrctrl.clk_stats.start = ktime_set(0, 0); - device->pwrctrl.time = 0; + kgsl_pwrscale_sleep(device); } @@ -1465,7 +1471,7 @@ void kgsl_pwrctrl_enable(struct kgsl_device *device) struct kgsl_pwrctrl *pwr = &device->pwrctrl; /* Order pwrrail/clk sequence based upon platform */ kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_ON); - kgsl_pwrctrl_pwrlevel_change(device, pwr->default_pwrlevel); + kgsl_pwrctrl_pwrlevel_change(device, pwr->active_pwrlevel); kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_ON, KGSL_STATE_ACTIVE); kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_ON); } @@ -1591,8 +1597,6 @@ void kgsl_active_count_put(struct kgsl_device *device) BUG_ON(!mutex_is_locked(&device->mutex)); BUG_ON(atomic_read(&device->active_cnt) == 0); - kgsl_pwrscale_idle(device); - if (atomic_dec_and_test(&device->active_cnt)) { if (device->state == KGSL_STATE_ACTIVE && device->requested_state == KGSL_STATE_NONE) { diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index 9f181606bd8..ba5ccd6d3bf 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -88,7 +88,6 @@ struct kgsl_pwrctrl { uint32_t pcl; unsigned int idle_needed; const char *irq_name; - s64 time; struct kgsl_clk_stats clk_stats; struct pm_qos_request pm_qos_req_dma; unsigned int pm_qos_latency; @@ -117,6 +116,18 @@ static inline unsigned long kgsl_get_clkrate(struct clk *clk) return (clk != NULL) ? clk_get_rate(clk) : 0; } +/* + * kgsl_pwrctrl_active_freq - get currently configured frequency + * @pwr: kgsl_pwrctrl structure for the device + * + * Returns the currently configured frequency for the device. + */ +static inline unsigned long +kgsl_pwrctrl_active_freq(struct kgsl_pwrctrl *pwr) +{ + return pwr->pwrlevels[pwr->active_pwrlevel].gpu_freq; +} + void kgsl_pwrctrl_set_state(struct kgsl_device *device, unsigned int state); void kgsl_pwrctrl_request_state(struct kgsl_device *device, unsigned int state); diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c index 47554c4fad0..96913eaec47 100644 --- a/drivers/gpu/msm/kgsl_pwrscale.c +++ b/drivers/gpu/msm/kgsl_pwrscale.c @@ -14,364 +14,451 @@ #include #include -#include - #include "kgsl.h" #include "kgsl_pwrscale.h" #include "kgsl_device.h" +#include "kgsl_trace.h" -struct kgsl_pwrscale_attribute { - struct attribute attr; - ssize_t (*show)(struct kgsl_device *device, char *buf); - ssize_t (*store)(struct kgsl_device *device, const char *buf, - size_t count); -}; - -#define to_pwrscale(k) container_of(k, struct kgsl_pwrscale, kobj) -#define pwrscale_to_device(p) container_of(p, struct kgsl_device, pwrscale) -#define to_device(k) container_of(k, struct kgsl_device, pwrscale_kobj) -#define to_pwrscale_attr(a) \ -container_of(a, struct kgsl_pwrscale_attribute, attr) -#define to_policy_attr(a) \ -container_of(a, struct kgsl_pwrscale_policy_attribute, attr) - -#define PWRSCALE_ATTR(_name, _mode, _show, _store) \ -struct kgsl_pwrscale_attribute pwrscale_attr_##_name = \ -__ATTR(_name, _mode, _show, _store) - -/* Master list of available policies */ - -static struct kgsl_pwrscale_policy *kgsl_pwrscale_policies[] = { -#ifdef CONFIG_MSM_SCM - &kgsl_pwrscale_policy_tz, -#endif -#ifdef CONFIG_MSM_SLEEP_STATS_DEVICE - &kgsl_pwrscale_policy_idlestats, -#endif - NULL -}; - -static ssize_t pwrscale_policy_store(struct kgsl_device *device, - const char *buf, size_t count) -{ - int i; - struct kgsl_pwrscale_policy *policy = NULL; - - /* The special keyword none allows the user to detach all - policies */ - if (!strncmp("none", buf, 4)) { - kgsl_pwrscale_detach_policy(device); - return count; - } - - for (i = 0; kgsl_pwrscale_policies[i]; i++) { - if (!strncmp(kgsl_pwrscale_policies[i]->name, buf, - strnlen(kgsl_pwrscale_policies[i]->name, - PAGE_SIZE))) { - policy = kgsl_pwrscale_policies[i]; - break; - } - } - - if (policy) - if (kgsl_pwrscale_attach_policy(device, policy)) - return -EIO; - - return count; -} - -static ssize_t pwrscale_policy_show(struct kgsl_device *device, char *buf) -{ - int ret; - - if (device->pwrscale.policy) { - ret = snprintf(buf, PAGE_SIZE, "%s", - device->pwrscale.policy->name); - if (device->pwrscale.enabled == 0) - ret += snprintf(buf + ret, PAGE_SIZE - ret, - " (disabled)"); - ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n"); - } else - ret = snprintf(buf, PAGE_SIZE, "none\n"); - - return ret; -} - -PWRSCALE_ATTR(policy, 0664, pwrscale_policy_show, pwrscale_policy_store); - -static ssize_t pwrscale_avail_policies_show(struct kgsl_device *device, - char *buf) -{ - int i, ret = 0; - - for (i = 0; kgsl_pwrscale_policies[i]; i++) { - ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s ", - kgsl_pwrscale_policies[i]->name); - } - - ret += snprintf(buf + ret, PAGE_SIZE - ret, "none\n"); - return ret; -} -PWRSCALE_ATTR(avail_policies, 0444, pwrscale_avail_policies_show, NULL); - -static struct attribute *pwrscale_attrs[] = { - &pwrscale_attr_policy.attr, - &pwrscale_attr_avail_policies.attr, - NULL -}; - -static ssize_t policy_sysfs_show(struct kobject *kobj, - struct attribute *attr, char *buf) -{ - struct kgsl_pwrscale *pwrscale = to_pwrscale(kobj); - struct kgsl_device *device = pwrscale_to_device(pwrscale); - struct kgsl_pwrscale_policy_attribute *pattr = to_policy_attr(attr); - ssize_t ret; - - if (pattr->show) - ret = pattr->show(device, pwrscale, buf); - else - ret = -EIO; - - return ret; -} - -static ssize_t policy_sysfs_store(struct kobject *kobj, - struct attribute *attr, - const char *buf, size_t count) -{ - struct kgsl_pwrscale *pwrscale = to_pwrscale(kobj); - struct kgsl_device *device = pwrscale_to_device(pwrscale); - struct kgsl_pwrscale_policy_attribute *pattr = to_policy_attr(attr); - ssize_t ret; - - if (pattr->store) - ret = pattr->store(device, pwrscale, buf, count); - else - ret = -EIO; - - return ret; -} - -static void policy_sysfs_release(struct kobject *kobj) -{ -} - -static ssize_t pwrscale_sysfs_show(struct kobject *kobj, - struct attribute *attr, char *buf) -{ - struct kgsl_device *device = to_device(kobj); - struct kgsl_pwrscale_attribute *pattr = to_pwrscale_attr(attr); - ssize_t ret; - - if (pattr->show) - ret = pattr->show(device, buf); - else - ret = -EIO; - - return ret; -} - -static ssize_t pwrscale_sysfs_store(struct kobject *kobj, - struct attribute *attr, - const char *buf, size_t count) -{ - struct kgsl_device *device = to_device(kobj); - struct kgsl_pwrscale_attribute *pattr = to_pwrscale_attr(attr); - ssize_t ret; - - if (pattr->store) - ret = pattr->store(device, buf, count); - else - ret = -EIO; - - return ret; -} - -static void pwrscale_sysfs_release(struct kobject *kobj) -{ -} - -static const struct sysfs_ops policy_sysfs_ops = { - .show = policy_sysfs_show, - .store = policy_sysfs_store -}; - -static const struct sysfs_ops pwrscale_sysfs_ops = { - .show = pwrscale_sysfs_show, - .store = pwrscale_sysfs_store -}; - -static struct kobj_type ktype_pwrscale_policy = { - .sysfs_ops = &policy_sysfs_ops, - .default_attrs = NULL, - .release = policy_sysfs_release -}; - -static struct kobj_type ktype_pwrscale = { - .sysfs_ops = &pwrscale_sysfs_ops, - .default_attrs = pwrscale_attrs, - .release = pwrscale_sysfs_release -}; - -#define PWRSCALE_ACTIVE(_d) \ - ((_d)->pwrscale.policy && (_d)->pwrscale.enabled) +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); +/* + * kgsl_pwrscale_sleep - notify governor that device is going off + * @device: The device + * + * Called shortly after all pending work is completed. + */ void kgsl_pwrscale_sleep(struct kgsl_device *device) { - if (PWRSCALE_ACTIVE(device) && device->pwrscale.policy->sleep) - device->pwrscale.policy->sleep(device, &device->pwrscale); + BUG_ON(!mutex_is_locked(&device->mutex)); + if (!device->pwrscale.enabled) + return; + device->pwrscale.time = device->pwrscale.on_time = 0; + + /* to call devfreq_suspend_device() from a kernel thread */ + queue_work(device->pwrscale.devfreq_wq, + &device->pwrscale.devfreq_suspend_ws); } EXPORT_SYMBOL(kgsl_pwrscale_sleep); +/* + * kgsl_pwrscale_wake - notify governor that device is going on + * @device: The device + * + * Called when the device is returning to an active state. + */ void kgsl_pwrscale_wake(struct kgsl_device *device) { - if (PWRSCALE_ACTIVE(device) && device->pwrscale.policy->wake) - device->pwrscale.policy->wake(device, &device->pwrscale); + struct kgsl_power_stats stats; + BUG_ON(!mutex_is_locked(&device->mutex)); + + if (!device->pwrscale.enabled) + return; + /* clear old stats before waking */ + memset(&device->pwrscale.accum_stats, 0, + sizeof(device->pwrscale.accum_stats)); + + /* and any hw activity from waking up*/ + device->ftbl->power_stats(device, &stats); + + device->pwrscale.time = ktime_to_us(ktime_get()); + + device->pwrscale.next_governor_call = 0; + + /* to call devfreq_resume_device() from a kernel thread */ + queue_work(device->pwrscale.devfreq_wq, + &device->pwrscale.devfreq_resume_ws); } EXPORT_SYMBOL(kgsl_pwrscale_wake); +/* + * kgsl_pwrscale_busy - update pwrscale state for new work + * @device: The device + * + * Called when new work is submitted to the device. + * This function must be called with the device mutex locked. + */ void kgsl_pwrscale_busy(struct kgsl_device *device) { - if (PWRSCALE_ACTIVE(device) && device->pwrscale.policy->busy) - device->pwrscale.policy->busy(device, - &device->pwrscale); + BUG_ON(!mutex_is_locked(&device->mutex)); + if (!device->pwrscale.enabled) + return; + if (device->pwrscale.on_time == 0) + device->pwrscale.on_time = ktime_to_us(ktime_get()); } EXPORT_SYMBOL(kgsl_pwrscale_busy); -void kgsl_pwrscale_idle(struct kgsl_device *device) +/* + * kgsl_pwrscale_update - update device busy statistics + * @device: The device + * + * Read hardware busy counters when the device is likely to be + * on and accumulate the results between devfreq get_dev_status + * calls. This is limits the need to turn on clocks to read these + * values for governers that run independtly of hardware + * activity (for example, by time based polling). + */ +void kgsl_pwrscale_update(struct kgsl_device *device) { - if (PWRSCALE_ACTIVE(device) && device->pwrscale.policy->idle) - if (device->state == KGSL_STATE_ACTIVE) - device->pwrscale.policy->idle(device, - &device->pwrscale); -} -EXPORT_SYMBOL(kgsl_pwrscale_idle); + BUG_ON(!mutex_is_locked(&device->mutex)); + if (!device->pwrscale.enabled) + return; + + if (device->pwrscale.next_governor_call == 0) + device->pwrscale.next_governor_call = jiffies; + + if (time_before(jiffies, device->pwrscale.next_governor_call)) + return; + + device->pwrscale.next_governor_call = jiffies + + msecs_to_jiffies(KGSL_GOVERNOR_CALL_INTERVAL); + + /* to call srcu_notifier_call_chain() from a kernel thread */ + queue_work(device->pwrscale.devfreq_wq, + &device->pwrscale.devfreq_notify_ws); +} +EXPORT_SYMBOL(kgsl_pwrscale_update); + +/* + * kgsl_pwrscale_disable - temporarily disable the governor + * @device: The device + * + * Temporarily disable the governor, to prevent interference + * with profiling tools that expect a fixed clock frequency. + * This function must be called with the device mutex locked. + */ void kgsl_pwrscale_disable(struct kgsl_device *device) { - device->pwrscale.enabled = 0; + BUG_ON(!mutex_is_locked(&device->mutex)); + + if (device->pwrscale.enabled) { + queue_work(device->pwrscale.devfreq_wq, + &device->pwrscale.devfreq_suspend_ws); + device->pwrscale.enabled = false; + kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_TURBO); + } } EXPORT_SYMBOL(kgsl_pwrscale_disable); +/* + * kgsl_pwrscale_enable - re-enable the governor + * @device: The device + * + * Reenable the governor after a kgsl_pwrscale_disable() call. + * This function must be called with the device mutex locked. + */ void kgsl_pwrscale_enable(struct kgsl_device *device) { - device->pwrscale.enabled = 1; + BUG_ON(!mutex_is_locked(&device->mutex)); + + if (!device->pwrscale.enabled) { + device->pwrscale.enabled = true; + queue_work(device->pwrscale.devfreq_wq, + &device->pwrscale.devfreq_resume_ws); + } } EXPORT_SYMBOL(kgsl_pwrscale_enable); -int kgsl_pwrscale_policy_add_files(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale, - struct attribute_group *attr_group) +/* + * kgsl_devfreq_target - devfreq_dev_profile.target callback + * @dev: see devfreq.h + * @freq: see devfreq.h + * @flags: see devfreq.h + * + * This function expects the device mutex to be unlocked. + */ +int kgsl_devfreq_target(struct device *dev, unsigned long *freq, u32 flags) { - int ret; + struct kgsl_device *device = dev_get_drvdata(dev); + struct devfreq_dev_profile *profile; + struct kgsl_pwrctrl *pwr; + int level = -1, i; + unsigned long cur_freq; + int lubflag = 0; - ret = kobject_add(&pwrscale->kobj, &device->pwrscale_kobj, - "%s", pwrscale->policy->name); + if (device == NULL) + return -ENODEV; + if (freq == NULL) + return -EINVAL; + profile = &device->pwrscale.profile; + pwr = &device->pwrctrl; - if (ret) - return ret; + if (flags & DEVFREQ_FLAG_LEAST_UPPER_BOUND) + lubflag = 1; - ret = sysfs_create_group(&pwrscale->kobj, attr_group); - - if (ret) { - kobject_del(&pwrscale->kobj); - kobject_put(&pwrscale->kobj); - } - - return ret; -} - -void kgsl_pwrscale_policy_remove_files(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale, - struct attribute_group *attr_group) -{ - sysfs_remove_group(&pwrscale->kobj, attr_group); - kobject_del(&pwrscale->kobj); - kobject_put(&pwrscale->kobj); -} - -static void _kgsl_pwrscale_detach_policy(struct kgsl_device *device) -{ - if (device->pwrscale.policy != NULL) { - device->pwrscale.policy->close(device, &device->pwrscale); + mutex_lock(&device->mutex); + cur_freq = kgsl_pwrctrl_active_freq(pwr); + if (*freq > cur_freq && pwr->active_pwrlevel > 0) { /* - * Try to set max pwrlevel which will be limited to thermal by - * kgsl_pwrctrl_pwrlevel_change if thermal is indeed lower + * If LUB - Least Upper Bound - is requested + * - move up just one level, + * otherwise - move up until required freq or higher */ - - kgsl_pwrctrl_pwrlevel_change(device, - device->pwrctrl.max_pwrlevel); - device->pwrctrl.default_pwrlevel = - device->pwrctrl.max_pwrlevel; + level = pwr->active_pwrlevel - 1; + if (!lubflag) + while (*freq > pwr->pwrlevels[level].gpu_freq + && level > 0) + level--; + } else if (*freq < cur_freq + && pwr->active_pwrlevel < (pwr->num_pwrlevels - 2)) { + /* + * Move down at least 1 frequency. If we fall out the bottom + * of the loop, use the lowest frequency. + */ + level = (pwr->num_pwrlevels - 1); + for (i = pwr->active_pwrlevel; i < level; i += pwr->step_mul) + if (pwr->pwrlevels[i].gpu_freq <= *freq) { + level = i; + break; + } + } else { + /* already at current freq, min, or max */ + level = pwr->active_pwrlevel; } - device->pwrscale.policy = NULL; -} -void kgsl_pwrscale_detach_policy(struct kgsl_device *device) -{ - mutex_lock(&device->mutex); - _kgsl_pwrscale_detach_policy(device); + if (device->pwrscale.enabled) + kgsl_pwrctrl_pwrlevel_change(device, level); + + *freq = kgsl_pwrctrl_active_freq(pwr); + mutex_unlock(&device->mutex); + return 0; } -EXPORT_SYMBOL(kgsl_pwrscale_detach_policy); +EXPORT_SYMBOL(kgsl_devfreq_target); -int kgsl_pwrscale_attach_policy(struct kgsl_device *device, - struct kgsl_pwrscale_policy *policy) +/* + * kgsl_devfreq_get_dev_status - devfreq_dev_profile.get_dev_status callback + * @dev: see devfreq.h + * @freq: see devfreq.h + * @flags: see devfreq.h + * + * This function expects the device mutex to be unlocked. + */ +int kgsl_devfreq_get_dev_status(struct device *dev, + struct devfreq_dev_status *stat) { - int ret = 0; + struct kgsl_device *device = dev_get_drvdata(dev); + struct kgsl_pwrscale *pwrscale; + s64 tmp; + + if (device == NULL) + return -ENODEV; + if (stat == NULL) + return -EINVAL; + + pwrscale = &device->pwrscale; + memset(stat, 0, sizeof(*stat)); mutex_lock(&device->mutex); - - if (device->pwrscale.policy == policy) - goto done; - - if (device->pwrctrl.num_pwrlevels < 3) { - ret = -EINVAL; - goto done; + /* make sure we don't turn on clocks just to read stats */ + if (device->state == KGSL_STATE_ACTIVE) { + struct kgsl_power_stats extra; + device->ftbl->power_stats(device, &extra); + device->pwrscale.accum_stats.busy_time += extra.busy_time; } - if (device->pwrscale.policy != NULL) - _kgsl_pwrscale_detach_policy(device); + tmp = ktime_to_us(ktime_get()); + stat->total_time = tmp - pwrscale->time; + pwrscale->time = tmp; - device->pwrscale.policy = policy; + stat->busy_time = pwrscale->accum_stats.busy_time; - device->pwrctrl.default_pwrlevel = - device->pwrctrl.init_pwrlevel; - /* Pwrscale is enabled by default at attach time */ - kgsl_pwrscale_enable(device); + stat->current_frequency = kgsl_pwrctrl_active_freq(&device->pwrctrl); - if (policy) { - ret = device->pwrscale.policy->init(device, &device->pwrscale); - if (ret) - device->pwrscale.policy = NULL; - } + trace_kgsl_pwrstats(device, stat->total_time, &pwrscale->accum_stats); + memset(&pwrscale->accum_stats, 0, sizeof(pwrscale->accum_stats)); -done: mutex_unlock(&device->mutex); - return ret; + return 0; } -EXPORT_SYMBOL(kgsl_pwrscale_attach_policy); +EXPORT_SYMBOL(kgsl_devfreq_get_dev_status); -int kgsl_pwrscale_init(struct kgsl_device *device) +/* + * kgsl_devfreq_get_cur_freq - devfreq_dev_profile.get_cur_freq callback + * @dev: see devfreq.h + * @freq: see devfreq.h + * @flags: see devfreq.h + * + * This function expects the device mutex to be unlocked. + */ +int kgsl_devfreq_get_cur_freq(struct device *dev, unsigned long *freq) { + struct kgsl_device *device = dev_get_drvdata(dev); + + if (device == NULL) + return -ENODEV; + if (freq == NULL) + return -EINVAL; + + mutex_lock(&device->mutex); + *freq = kgsl_pwrctrl_active_freq(&device->pwrctrl); + mutex_unlock(&device->mutex); + + return 0; +} +EXPORT_SYMBOL(kgsl_devfreq_get_cur_freq); + +/* + * kgsl_devfreq_add_notifier - add a fine grained notifier. + * @dev: The device + * @nb: Notifier block that will recieve updates. + * + * Add a notifier to recieve ADRENO_DEVFREQ_NOTIFY_* events + * from the device. + */ +int kgsl_devfreq_add_notifier(struct device *dev, struct notifier_block *nb) +{ + struct kgsl_device *device = dev_get_drvdata(dev); + + if (device == NULL) + return -ENODEV; + + if (nb == NULL) + return -EINVAL; + + return srcu_notifier_chain_register(&device->pwrscale.nh, nb); +} + +void kgsl_pwrscale_idle(struct kgsl_device *device) +{ + BUG_ON(!mutex_is_locked(&device->mutex)); + queue_work(device->pwrscale.devfreq_wq, + &device->pwrscale.devfreq_notify_ws); +} +EXPORT_SYMBOL(kgsl_pwrscale_idle); + +/* + * kgsl_devfreq_del_notifier - remove a fine grained notifier. + * @dev: The device + * @nb: The notifier block. + * + * Remove a notifier registered with kgsl_devfreq_add_notifier(). + */ +int kgsl_devfreq_del_notifier(struct device *dev, struct notifier_block *nb) +{ + struct kgsl_device *device = dev_get_drvdata(dev); + + if (device == NULL) + return -ENODEV; + + if (nb == NULL) + return -EINVAL; + + return srcu_notifier_chain_unregister(&device->pwrscale.nh, nb); +} +EXPORT_SYMBOL(kgsl_devfreq_del_notifier); + +/* + * kgsl_pwrscale_init - Initialize pwrscale. + * @dev: The device + * @governor: The initial governor to use. + * + * Initialize devfreq and any non-constant profile data. + */ +int kgsl_pwrscale_init(struct device *dev, const char *governor) +{ + struct kgsl_device *device; + struct kgsl_pwrscale *pwrscale; + struct kgsl_pwrctrl *pwr; + struct devfreq *devfreq; + int i, out = 0; int ret; - ret = kobject_init_and_add(&device->pwrscale_kobj, &ktype_pwrscale, - &device->dev->kobj, "pwrscale"); + device = dev_get_drvdata(dev); + if (device == NULL) + return -ENODEV; - if (ret) - return ret; + pwrscale = &device->pwrscale; + pwr = &device->pwrctrl; - kobject_init(&device->pwrscale.kobj, &ktype_pwrscale_policy); - return ret; + srcu_init_notifier_head(&pwrscale->nh); + + pwrscale->profile.initial_freq = + pwr->pwrlevels[pwr->default_pwrlevel].gpu_freq; + /* Let's start with 10 ms and tune in later */ + pwrscale->profile.polling_ms = 10; + + /* do not include the 'off' level or duplicate freq. levels */ + for (i = 0; i < (pwr->num_pwrlevels - 1); i += pwr->step_mul) + pwrscale->freq_table[out++] = pwr->pwrlevels[i].gpu_freq; + + pwrscale->profile.max_state = out; + /* link storage array to the devfreq profile pointer */ + pwrscale->profile.freq_table = pwrscale->freq_table; + + /* if there is only 1 freq, no point in running a governor */ + if (pwrscale->profile.max_state == 1) + governor = "performance"; + + devfreq = devfreq_add_device(dev, &pwrscale->profile, governor, NULL); + if (IS_ERR(devfreq)) + return PTR_ERR(devfreq); + + pwrscale->devfreq = devfreq; + + ret = sysfs_create_link(&device->dev->kobj, + &devfreq->dev.kobj, "devfreq"); + + pwrscale->devfreq_wq = create_freezable_workqueue("kgsl_devfreq_wq"); + INIT_WORK(&pwrscale->devfreq_suspend_ws, do_devfreq_suspend); + INIT_WORK(&pwrscale->devfreq_resume_ws, do_devfreq_resume); + INIT_WORK(&pwrscale->devfreq_notify_ws, do_devfreq_notify); + + pwrscale->next_governor_call = 0; + + return 0; } EXPORT_SYMBOL(kgsl_pwrscale_init); +/* + * kgsl_pwrscale_close - clean up pwrscale + * @device: the device + * + * This function should be called with the device mutex locked. + */ void kgsl_pwrscale_close(struct kgsl_device *device) { - kobject_put(&device->pwrscale_kobj); + struct kgsl_pwrscale *pwrscale; + + BUG_ON(!mutex_is_locked(&device->mutex)); + + pwrscale = &device->pwrscale; + flush_workqueue(pwrscale->devfreq_wq); + destroy_workqueue(pwrscale->devfreq_wq); + devfreq_remove_device(device->pwrscale.devfreq); + device->pwrscale.devfreq = NULL; + srcu_cleanup_notifier_head(&device->pwrscale.nh); } EXPORT_SYMBOL(kgsl_pwrscale_close); + +static void do_devfreq_suspend(struct work_struct *work) +{ + struct kgsl_pwrscale *pwrscale = container_of(work, + struct kgsl_pwrscale, devfreq_suspend_ws); + struct devfreq *devfreq = pwrscale->devfreq; + + devfreq_suspend_device(devfreq); +} + +static void do_devfreq_resume(struct work_struct *work) +{ + struct kgsl_pwrscale *pwrscale = container_of(work, + struct kgsl_pwrscale, devfreq_resume_ws); + struct devfreq *devfreq = pwrscale->devfreq; + + devfreq_resume_device(devfreq); +} + +static void do_devfreq_notify(struct work_struct *work) +{ + struct kgsl_pwrscale *pwrscale = container_of(work, + struct kgsl_pwrscale, devfreq_notify_ws); + struct devfreq *devfreq = pwrscale->devfreq; + srcu_notifier_call_chain(&pwrscale->nh, + ADRENO_DEVFREQ_NOTIFY_RETIRE, + devfreq); +} diff --git a/drivers/gpu/msm/kgsl_pwrscale.h b/drivers/gpu/msm/kgsl_pwrscale.h index f17b394a505..9737b658e46 100644 --- a/drivers/gpu/msm/kgsl_pwrscale.h +++ b/drivers/gpu/msm/kgsl_pwrscale.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2013, 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 @@ -14,68 +14,56 @@ #ifndef __KGSL_PWRSCALE_H #define __KGSL_PWRSCALE_H -struct kgsl_pwrscale; +#include +#include -struct kgsl_pwrscale_policy { - const char *name; - int (*init)(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale); - void (*close)(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale); - void (*idle)(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale); - void (*busy)(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale); - void (*sleep)(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale); - void (*wake)(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale); +/* devfreq governor call window in msec */ +#define KGSL_GOVERNOR_CALL_INTERVAL 5 + +struct kgsl_power_stats { + u64 busy_time; }; struct kgsl_pwrscale { - struct kgsl_pwrscale_policy *policy; - struct kobject kobj; - void *priv; - int enabled; + struct devfreq *devfreq; + struct devfreq_dev_profile profile; + unsigned int freq_table[KGSL_MAX_PWRLEVELS]; + char last_governor[DEVFREQ_NAME_LEN]; + struct kgsl_power_stats accum_stats; + bool enabled; + s64 time; + s64 on_time; + struct srcu_notifier_head nh; + struct workqueue_struct *devfreq_wq; + struct work_struct devfreq_suspend_ws; + struct work_struct devfreq_resume_ws; + struct work_struct devfreq_notify_ws; + unsigned long next_governor_call; }; -struct kgsl_pwrscale_policy_attribute { - struct attribute attr; - ssize_t (*show)(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale, char *buf); - ssize_t (*store)(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale, const char *buf, - size_t count); -}; - -#define PWRSCALE_POLICY_ATTR(_name, _mode, _show, _store) \ - struct kgsl_pwrscale_policy_attribute policy_attr_##_name = \ - __ATTR(_name, _mode, _show, _store) - -extern struct kgsl_pwrscale_policy kgsl_pwrscale_policy_tz; -extern struct kgsl_pwrscale_policy kgsl_pwrscale_policy_idlestats; -extern struct kgsl_pwrscale_policy kgsl_pwrscale_policy_msm; - -int kgsl_pwrscale_init(struct kgsl_device *device); +int kgsl_pwrscale_init(struct device *dev, const char *governor); void kgsl_pwrscale_close(struct kgsl_device *device); -int kgsl_pwrscale_attach_policy(struct kgsl_device *device, - struct kgsl_pwrscale_policy *policy); -void kgsl_pwrscale_detach_policy(struct kgsl_device *device); - -void kgsl_pwrscale_idle(struct kgsl_device *device); +void kgsl_pwrscale_update(struct kgsl_device *device); void kgsl_pwrscale_busy(struct kgsl_device *device); +void kgsl_pwrscale_idle(struct kgsl_device *device); void kgsl_pwrscale_sleep(struct kgsl_device *device); void kgsl_pwrscale_wake(struct kgsl_device *device); void kgsl_pwrscale_enable(struct kgsl_device *device); void kgsl_pwrscale_disable(struct kgsl_device *device); -int kgsl_pwrscale_policy_add_files(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale, - struct attribute_group *attr_group); +int kgsl_devfreq_target(struct device *dev, unsigned long *freq, u32 flags); +int kgsl_devfreq_get_dev_status(struct device *, struct devfreq_dev_status *); +int kgsl_devfreq_get_cur_freq(struct device *dev, unsigned long *freq); -void kgsl_pwrscale_policy_remove_files(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale, - struct attribute_group *attr_group); +#define KGSL_PWRSCALE_INIT(_gov_list, _num_gov) { \ + .enabled = true, \ + .profile = { \ + .target = kgsl_devfreq_target, \ + .get_dev_status = kgsl_devfreq_get_dev_status, \ + .get_cur_freq = kgsl_devfreq_get_cur_freq, \ + .governor_data = (_gov_list), \ + .num_governor_data = (_num_gov), \ + } } #endif diff --git a/drivers/gpu/msm/kgsl_pwrscale_idlestats.c b/drivers/gpu/msm/kgsl_pwrscale_idlestats.c deleted file mode 100644 index c3188a5f746..00000000000 --- a/drivers/gpu/msm/kgsl_pwrscale_idlestats.c +++ /dev/null @@ -1,232 +0,0 @@ -/* Copyright (c) 2011-2012, 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 - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "kgsl.h" -#include "kgsl_pwrscale.h" -#include "kgsl_device.h" - -#define MAX_CORES 4 -struct _cpu_info { - spinlock_t lock; - struct notifier_block cpu_nb; - u64 start[MAX_CORES]; - u64 end[MAX_CORES]; - int curr_freq[MAX_CORES]; - int max_freq[MAX_CORES]; -}; - -struct idlestats_priv { - char name[32]; - struct msm_idle_stats_device idledev; - struct kgsl_device *device; - struct msm_idle_pulse pulse; - struct _cpu_info cpu_info; -}; - -static int idlestats_cpufreq_notifier( - struct notifier_block *nb, - unsigned long val, void *data) -{ - struct _cpu_info *cpu = container_of(nb, - struct _cpu_info, cpu_nb); - struct cpufreq_freqs *freq = data; - - if (val != CPUFREQ_POSTCHANGE) - return 0; - - spin_lock(&cpu->lock); - if (freq->cpu < num_possible_cpus()) - cpu->curr_freq[freq->cpu] = freq->new / 1000; - spin_unlock(&cpu->lock); - - return 0; -} - -static void idlestats_get_sample(struct msm_idle_stats_device *idledev, - struct msm_idle_pulse *pulse) -{ - struct kgsl_power_stats stats; - struct idlestats_priv *priv = container_of(idledev, - struct idlestats_priv, idledev); - struct kgsl_device *device = priv->device; - struct kgsl_pwrctrl *pwr = &device->pwrctrl; - - mutex_lock(&device->mutex); - /* If the GPU is asleep, don't wake it up - assume that we - are idle */ - - if (device->state == KGSL_STATE_ACTIVE) { - device->ftbl->power_stats(device, &stats); - pulse->busy_start_time = pwr->time - stats.busy_time; - pulse->busy_interval = stats.busy_time; - } else { - pulse->busy_start_time = pwr->time; - pulse->busy_interval = 0; - } - pulse->wait_interval = 0; - mutex_unlock(&device->mutex); -} - -static void idlestats_busy(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale) -{ - struct idlestats_priv *priv = pwrscale->priv; - struct kgsl_power_stats stats; - int i, busy, nr_cpu = 1; - - if (priv->pulse.busy_start_time != 0) { - priv->pulse.wait_interval = 0; - /* Calculate the total CPU busy time for this GPU pulse */ - for (i = 0; i < num_possible_cpus(); i++) { - spin_lock(&priv->cpu_info.lock); - if (cpu_online(i)) { - priv->cpu_info.end[i] = - (u64)ktime_to_us(ktime_get()) - - get_cpu_idle_time_us(i, NULL); - busy = priv->cpu_info.end[i] - - priv->cpu_info.start[i]; - /* Normalize the busy time by frequency */ - busy = priv->cpu_info.curr_freq[i] * - (busy / priv->cpu_info.max_freq[i]); - priv->pulse.wait_interval += busy; - nr_cpu++; - } - spin_unlock(&priv->cpu_info.lock); - } - priv->pulse.wait_interval /= nr_cpu; - - /* This is called from within a mutex protected function, so - no additional locking required */ - device->ftbl->power_stats(device, &stats); - - /* If total_time is zero, then we don't have - any interesting statistics to store */ - if (stats.total_time == 0) { - priv->pulse.busy_start_time = 0; - return; - } - - priv->pulse.busy_interval = stats.busy_time; - msm_idle_stats_idle_end(&priv->idledev, &priv->pulse); - } - priv->pulse.busy_start_time = ktime_to_us(ktime_get()); -} - -static void idlestats_idle(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale) -{ - int i, nr_cpu; - struct idlestats_priv *priv = pwrscale->priv; - - nr_cpu = num_possible_cpus(); - for (i = 0; i < nr_cpu; i++) - if (cpu_online(i)) - priv->cpu_info.start[i] = - (u64)ktime_to_us(ktime_get()) - - get_cpu_idle_time_us(i, NULL); - - msm_idle_stats_idle_start(&priv->idledev); -} - -static void idlestats_sleep(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale) -{ - struct idlestats_priv *priv = pwrscale->priv; - msm_idle_stats_update_event(&priv->idledev, - MSM_IDLE_STATS_EVENT_IDLE_TIMER_EXPIRED); -} - -static void idlestats_wake(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale) -{ - /* Use highest perf level on wake-up from - sleep for better performance */ - kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_TURBO); -} - -static int idlestats_init(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale) -{ - struct idlestats_priv *priv; - struct cpufreq_policy cpu_policy; - int ret, i; - - priv = pwrscale->priv = kzalloc(sizeof(struct idlestats_priv), - GFP_KERNEL); - if (pwrscale->priv == NULL) - return -ENOMEM; - - snprintf(priv->name, sizeof(priv->name), "idle_stats_%s", - device->name); - - priv->device = device; - - priv->idledev.name = (const char *) priv->name; - priv->idledev.get_sample = idlestats_get_sample; - - spin_lock_init(&priv->cpu_info.lock); - priv->cpu_info.cpu_nb.notifier_call = - idlestats_cpufreq_notifier; - ret = cpufreq_register_notifier(&priv->cpu_info.cpu_nb, - CPUFREQ_TRANSITION_NOTIFIER); - if (ret) - goto err; - for (i = 0; i < num_possible_cpus(); i++) { - cpufreq_frequency_table_cpuinfo(&cpu_policy, - cpufreq_frequency_get_table(i)); - priv->cpu_info.max_freq[i] = cpu_policy.max / 1000; - priv->cpu_info.curr_freq[i] = cpu_policy.max / 1000; - } - ret = msm_idle_stats_register_device(&priv->idledev); -err: - if (ret) { - kfree(pwrscale->priv); - pwrscale->priv = NULL; - } - - return ret; -} - -static void idlestats_close(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale) -{ - struct idlestats_priv *priv = pwrscale->priv; - - if (pwrscale->priv == NULL) - return; - - cpufreq_unregister_notifier(&priv->cpu_info.cpu_nb, - CPUFREQ_TRANSITION_NOTIFIER); - msm_idle_stats_deregister_device(&priv->idledev); - - kfree(pwrscale->priv); - pwrscale->priv = NULL; -} - -struct kgsl_pwrscale_policy kgsl_pwrscale_policy_idlestats = { - .name = "idlestats", - .init = idlestats_init, - .idle = idlestats_idle, - .busy = idlestats_busy, - .sleep = idlestats_sleep, - .wake = idlestats_wake, - .close = idlestats_close -}; diff --git a/drivers/gpu/msm/kgsl_pwrscale_trustzone.c b/drivers/gpu/msm/kgsl_pwrscale_trustzone.c deleted file mode 100644 index 7f8a6b1b956..00000000000 --- a/drivers/gpu/msm/kgsl_pwrscale_trustzone.c +++ /dev/null @@ -1,269 +0,0 @@ -/* Copyright (c) 2010-2013, 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 - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "kgsl.h" -#include "kgsl_pwrscale.h" -#include "kgsl_device.h" - -#define TZ_GOVERNOR_PERFORMANCE 0 -#define TZ_GOVERNOR_ONDEMAND 1 - -struct tz_priv { - int governor; - struct kgsl_power_stats bin; - unsigned int idle_dcvs; -}; -spinlock_t tz_lock; - -/* FLOOR is 5msec to capture up to 3 re-draws - * per frame for 60fps content. - */ -#define FLOOR 5000 -/* CEILING is 50msec, larger than any standard - * frame length, but less than the idle timer. - */ -#define CEILING 50000 -#define TZ_RESET_ID 0x3 -#define TZ_UPDATE_ID 0x4 -#define TZ_INIT_ID 0x6 - -/* Trap into the TrustZone, and call funcs there. */ -static int __secure_tz_entry2(u32 cmd, u32 val1, u32 val2) -{ - int ret; - spin_lock(&tz_lock); - /* sync memory before sending the commands to tz*/ - __iowmb(); - ret = scm_call_atomic2(SCM_SVC_IO, cmd, val1, val2); - spin_unlock(&tz_lock); - return ret; -} - -static int __secure_tz_entry3(u32 cmd, u32 val1, u32 val2, - u32 val3) -{ - int ret; - spin_lock(&tz_lock); - /* sync memory before sending the commands to tz*/ - __iowmb(); - ret = scm_call_atomic3(SCM_SVC_IO, cmd, val1, val2, - val3); - spin_unlock(&tz_lock); - return ret; -} - -static ssize_t tz_governor_show(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale, - char *buf) -{ - struct tz_priv *priv = pwrscale->priv; - int ret; - - if (priv->governor == TZ_GOVERNOR_ONDEMAND) - ret = snprintf(buf, 10, "ondemand\n"); - else - ret = snprintf(buf, 13, "performance\n"); - - return ret; -} - -static ssize_t tz_governor_store(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale, - const char *buf, size_t count) -{ - char str[20]; - struct tz_priv *priv = pwrscale->priv; - struct kgsl_pwrctrl *pwr = &device->pwrctrl; - int ret; - - ret = sscanf(buf, "%20s", str); - if (ret != 1) - return -EINVAL; - - mutex_lock(&device->mutex); - - if (!strncmp(str, "ondemand", 8)) - priv->governor = TZ_GOVERNOR_ONDEMAND; - else if (!strncmp(str, "performance", 11)) - priv->governor = TZ_GOVERNOR_PERFORMANCE; - - if (priv->governor == TZ_GOVERNOR_PERFORMANCE) { - kgsl_pwrctrl_pwrlevel_change(device, pwr->max_pwrlevel); - pwr->default_pwrlevel = pwr->max_pwrlevel; - } else { - pwr->default_pwrlevel = pwr->init_pwrlevel; - } - - mutex_unlock(&device->mutex); - return count; -} - -PWRSCALE_POLICY_ATTR(governor, 0644, tz_governor_show, tz_governor_store); - -static struct attribute *tz_attrs[] = { - &policy_attr_governor.attr, - NULL -}; - -static struct attribute_group tz_attr_group = { - .attrs = tz_attrs, -}; - -static void tz_wake(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale) -{ - struct tz_priv *priv = pwrscale->priv; - if (device->state != KGSL_STATE_NAP && - priv->governor == TZ_GOVERNOR_ONDEMAND) - kgsl_pwrctrl_pwrlevel_change(device, - device->pwrctrl.default_pwrlevel); -} - -static void tz_idle(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale) -{ - struct kgsl_pwrctrl *pwr = &device->pwrctrl; - struct tz_priv *priv = pwrscale->priv; - struct kgsl_power_stats stats; - int val, idle; - - /* In "performance" mode the clock speed always stays - the same */ - if (priv->governor == TZ_GOVERNOR_PERFORMANCE) - return; - - device->ftbl->power_stats(device, &stats); - priv->bin.total_time += stats.total_time; - priv->bin.busy_time += stats.busy_time; - /* Do not waste CPU cycles running this algorithm if - * the GPU just started, or if less than FLOOR time - * has passed since the last run. - */ - if ((stats.total_time == 0) || - (priv->bin.total_time < FLOOR)) - return; - - /* If there is an extended block of busy processing, set - * frequency to turbo. Otherwise run the normal algorithm. - */ - if (priv->bin.busy_time > CEILING) { - val = 0; - kgsl_pwrctrl_pwrlevel_change(device, - KGSL_PWRLEVEL_TURBO); - } else if (priv->idle_dcvs) { - idle = priv->bin.total_time - priv->bin.busy_time; - idle = (idle > 0) ? idle : 0; - val = __secure_tz_entry2(TZ_UPDATE_ID, idle, device->id); - } else { - if (pwr->step_mul > 1) - val = __secure_tz_entry3(TZ_UPDATE_ID, - (pwr->active_pwrlevel + 1)/2, - priv->bin.total_time, priv->bin.busy_time); - else - val = __secure_tz_entry3(TZ_UPDATE_ID, - pwr->active_pwrlevel, - priv->bin.total_time, priv->bin.busy_time); - } - - priv->bin.total_time = 0; - priv->bin.busy_time = 0; - - /* If the decision is to move to a lower level, make sure the GPU - * frequency drops. - */ - if (val > 0) - val *= pwr->step_mul; - if (val) - kgsl_pwrctrl_pwrlevel_change(device, - pwr->active_pwrlevel + val); -} - -static void tz_busy(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale) -{ - device->on_time = ktime_to_us(ktime_get()); -} - -static void tz_sleep(struct kgsl_device *device, - struct kgsl_pwrscale *pwrscale) -{ - struct tz_priv *priv = pwrscale->priv; - - __secure_tz_entry2(TZ_RESET_ID, 0, 0); - priv->bin.total_time = 0; - priv->bin.busy_time = 0; -} - -#ifdef CONFIG_MSM_SCM -static int tz_init(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale) -{ - int i = 0, j = 1, ret = 0; - struct tz_priv *priv; - struct kgsl_pwrctrl *pwr = &device->pwrctrl; - unsigned int tz_pwrlevels[KGSL_MAX_PWRLEVELS + 1]; - - priv = pwrscale->priv = kzalloc(sizeof(struct tz_priv), GFP_KERNEL); - if (pwrscale->priv == NULL) - return -ENOMEM; - priv->idle_dcvs = 0; - priv->governor = TZ_GOVERNOR_ONDEMAND; - spin_lock_init(&tz_lock); - kgsl_pwrscale_policy_add_files(device, pwrscale, &tz_attr_group); - for (i = 0; i < pwr->num_pwrlevels - 1; i++) { - if (i == 0) - tz_pwrlevels[j] = pwr->pwrlevels[i].gpu_freq; - else if (pwr->pwrlevels[i].gpu_freq != - pwr->pwrlevels[i - 1].gpu_freq) { - j++; - tz_pwrlevels[j] = pwr->pwrlevels[i].gpu_freq; - } - } - tz_pwrlevels[0] = j; - ret = scm_call(SCM_SVC_DCVS, TZ_INIT_ID, tz_pwrlevels, - sizeof(tz_pwrlevels), NULL, 0); - if (ret) { - KGSL_DRV_ERR(device, "Fall back to idle based GPU DCVS algo"); - priv->idle_dcvs = 1; - } - return 0; -} -#else -static int tz_init(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale) -{ - return -EINVAL; -} -#endif /* CONFIG_MSM_SCM */ - -static void tz_close(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale) -{ - kgsl_pwrscale_policy_remove_files(device, pwrscale, &tz_attr_group); - kfree(pwrscale->priv); - pwrscale->priv = NULL; -} - -struct kgsl_pwrscale_policy kgsl_pwrscale_policy_tz = { - .name = "trustzone", - .init = tz_init, - .busy = tz_busy, - .idle = tz_idle, - .sleep = tz_sleep, - .wake = tz_wake, - .close = tz_close -}; -EXPORT_SYMBOL(kgsl_pwrscale_policy_tz); diff --git a/drivers/gpu/msm/kgsl_trace.h b/drivers/gpu/msm/kgsl_trace.h index 5f39b8bb3ec..1fd555d0ea9 100644 --- a/drivers/gpu/msm/kgsl_trace.h +++ b/drivers/gpu/msm/kgsl_trace.h @@ -287,6 +287,31 @@ TRACE_EVENT(kgsl_gpubusy, ) ); +TRACE_EVENT(kgsl_pwrstats, + TP_PROTO(struct kgsl_device *device, u64 total_time, + struct kgsl_power_stats *pstats), + + TP_ARGS(device, total_time, pstats), + + TP_STRUCT__entry( + __string(device_name, device->name) + __field(u64, total_time) + __field(u64, busy_time) + ), + + TP_fast_assign( + __assign_str(device_name, device->name); + __entry->total_time = total_time; + __entry->busy_time = pstats->busy_time; + ), + + TP_printk( + "d_name=%s total=%lld busy=%lld", + __get_str(device_name), + __entry->total_time, __entry->busy_time + ) +); + DECLARE_EVENT_CLASS(kgsl_pwrstate_template, TP_PROTO(struct kgsl_device *device, unsigned int state), diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c index ae7aee0a0f5..7e5877157a7 100644 --- a/drivers/gpu/msm/z180.c +++ b/drivers/gpu/msm/z180.c @@ -559,8 +559,7 @@ static int __devinit z180_probe(struct platform_device *pdev) if (status) goto error_close_ringbuffer; - kgsl_pwrscale_init(device); - kgsl_pwrscale_attach_policy(device, Z180_DEFAULT_PWRSCALE_POLICY); + kgsl_pwrscale_init(&pdev->dev, CONFIG_MSM_Z180_DEFAULT_GOVERNOR); return status; @@ -955,18 +954,16 @@ z180_drawctxt_destroy(struct kgsl_context *context) static void z180_power_stats(struct kgsl_device *device, struct kgsl_power_stats *stats) { - struct kgsl_pwrctrl *pwr = &device->pwrctrl; + struct kgsl_pwrscale *pwrscale = &device->pwrscale; s64 tmp = ktime_to_us(ktime_get()); - if (pwr->time == 0) { - pwr->time = tmp; - stats->total_time = 0; + memset(stats, 0, sizeof(stats)); + if (pwrscale->on_time == 0) { + pwrscale->on_time = tmp; stats->busy_time = 0; } else { - stats->total_time = tmp - pwr->time; - pwr->time = tmp; - stats->busy_time = tmp - device->on_time; - device->on_time = tmp; + stats->busy_time = tmp - pwrscale->on_time; + pwrscale->on_time = tmp; } } diff --git a/drivers/gpu/msm/z180.h b/drivers/gpu/msm/z180.h index a36e92d864f..5b54445bbdb 100644 --- a/drivers/gpu/msm/z180.h +++ b/drivers/gpu/msm/z180.h @@ -26,8 +26,6 @@ #define Z180_DEVICE(device) \ KGSL_CONTAINER_OF(device, struct z180_device, dev) -#define Z180_DEFAULT_PWRSCALE_POLICY NULL - /* Wait a maximum of 10 seconds when trying to idle the core */ #define Z180_IDLE_TIMEOUT (20 * 1000) diff --git a/include/linux/msm_adreno_devfreq.h b/include/linux/msm_adreno_devfreq.h new file mode 100644 index 00000000000..a84d14c2aa5 --- /dev/null +++ b/include/linux/msm_adreno_devfreq.h @@ -0,0 +1,28 @@ +#ifndef MSM_ADRENO_DEVFREQ_H +#define MSM_ADRENO_DEVFREQ_H + +#include + +#define ADRENO_DEVFREQ_NOTIFY_SUBMIT 1 +#define ADRENO_DEVFREQ_NOTIFY_RETIRE 2 +#define ADRENO_DEVFREQ_NOTIFY_IDLE 3 + +struct device; + +int kgsl_devfreq_add_notifier(struct device *, struct notifier_block *); + +int kgsl_devfreq_del_notifier(struct device *, struct notifier_block *); + +/* same as KGSL_MAX_PWRLEVELS */ +#define MSM_ADRENO_MAX_PWRLEVELS 10 + +struct devfreq_msm_adreno_tz_data { + struct notifier_block nb; + struct { + s64 total_time; + s64 busy_time; + } bin; + unsigned int device_id; +}; + +#endif