forked from rubenslte/android_kernel_samsung_msm8226
msm: kgsl: convert pwrscale framework to use devfreq
Convert the clock frequency scaling infrastructure to be based on devfreq. Change-Id: I1a60ba339db5715a8836b835bd1b29b46e151af6 Signed-off-by: Jeremy Gebben <jgebben@codeaurora.org> Signed-off-by: Vladimir Razgulin <vrazguli@codeaurora.org>
This commit is contained in:
committed by
Lucille Sylvester
parent
dfd2ca740d
commit
f84c799de3
@@ -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
|
||||
|
||||
@@ -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 += \
|
||||
|
||||
+20
-19
@@ -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)
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
+379
-292
@@ -14,364 +14,451 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include <asm/page.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
@@ -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 <linux/devfreq.h>
|
||||
#include <linux/msm_adreno_devfreq.h>
|
||||
|
||||
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
|
||||
|
||||
@@ -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 <linux/slab.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/idle_stats_device.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/tick.h>
|
||||
|
||||
#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
|
||||
};
|
||||
@@ -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 <linux/export.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <mach/socinfo.h>
|
||||
#include <mach/scm.h>
|
||||
|
||||
#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);
|
||||
@@ -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),
|
||||
|
||||
|
||||
+7
-10
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef MSM_ADRENO_DEVFREQ_H
|
||||
#define MSM_ADRENO_DEVFREQ_H
|
||||
|
||||
#include <linux/notifier.h>
|
||||
|
||||
#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
|
||||
Reference in New Issue
Block a user