msm: kgsl: add gpu busy stats to sysfs
Signed-off-by: Suman Tatiraju <sumant@codeaurora.org>
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index f3fa797..35cf12e 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -19,6 +19,8 @@
#include "kgsl_device.h"
#define GPU_SWFI_LATENCY 3
+#define UPDATE_BUSY_VAL 1000000
+#define UPDATE_BUSY 50
void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device,
unsigned int new_level)
@@ -223,18 +225,37 @@
device->pwrctrl.interval_timeout);
}
+static int kgsl_pwrctrl_gpubusy_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ struct kgsl_busy *b = &device->pwrctrl.busy;
+ ret = snprintf(buf, 17, "%7d %7d\n",
+ b->on_time_old, b->time_old);
+ if (!test_bit(KGSL_PWRFLAGS_AXI_ON, &device->pwrctrl.power_flags)) {
+ b->on_time_old = 0;
+ b->time_old = 0;
+ }
+ return ret;
+}
+
DEVICE_ATTR(gpuclk, 0644, kgsl_pwrctrl_gpuclk_show, kgsl_pwrctrl_gpuclk_store);
DEVICE_ATTR(max_gpuclk, 0644, kgsl_pwrctrl_max_gpuclk_show,
kgsl_pwrctrl_max_gpuclk_store);
DEVICE_ATTR(pwrnap, 0644, kgsl_pwrctrl_pwrnap_show, kgsl_pwrctrl_pwrnap_store);
DEVICE_ATTR(idle_timer, 0644, kgsl_pwrctrl_idle_timer_show,
kgsl_pwrctrl_idle_timer_store);
+DEVICE_ATTR(gpubusy, 0644, kgsl_pwrctrl_gpubusy_show,
+ NULL);
static const struct device_attribute *pwrctrl_attr_list[] = {
&dev_attr_gpuclk,
&dev_attr_max_gpuclk,
&dev_attr_pwrnap,
&dev_attr_idle_timer,
+ &dev_attr_gpubusy,
NULL
};
@@ -248,6 +269,31 @@
kgsl_remove_device_sysfs_files(device->dev, pwrctrl_attr_list);
}
+/* Track the amount of time the gpu is on vs the total system time. *
+ * Regularly update the percentage of busy time displayed by sysfs. */
+static void kgsl_pwrctrl_busy_time(struct kgsl_device *device, bool on_time)
+{
+ struct kgsl_busy *b = &device->pwrctrl.busy;
+ int elapsed;
+ if (b->start.tv_sec == 0)
+ do_gettimeofday(&(b->start));
+ do_gettimeofday(&(b->stop));
+ elapsed = (b->stop.tv_sec - b->start.tv_sec) * 1000000;
+ elapsed += b->stop.tv_usec - b->start.tv_usec;
+ b->time += elapsed;
+ if (on_time)
+ b->on_time += elapsed;
+ /* Update the output regularly and reset the counters. */
+ if ((b->time > UPDATE_BUSY_VAL) ||
+ !test_bit(KGSL_PWRFLAGS_AXI_ON, &device->pwrctrl.power_flags)) {
+ b->on_time_old = b->on_time;
+ b->time_old = b->time;
+ b->on_time = 0;
+ b->time = 0;
+ }
+ do_gettimeofday(&(b->start));
+}
+
void kgsl_pwrctrl_clk(struct kgsl_device *device, int state)
{
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
@@ -265,6 +311,7 @@
clk_set_rate(pwr->grp_clks[0],
pwr->pwrlevels[pwr->num_pwrlevels - 1].
gpu_freq);
+ kgsl_pwrctrl_busy_time(device, true);
}
} else if (state == KGSL_PWRFLAGS_ON) {
if (!test_and_set_bit(KGSL_PWRFLAGS_CLK_ON,
@@ -283,6 +330,7 @@
for (i = KGSL_MAX_CLKS - 1; i > 0; i--)
if (pwr->grp_clks[i])
clk_enable(pwr->grp_clks[i]);
+ kgsl_pwrctrl_busy_time(device, false);
}
}
}
@@ -526,10 +574,18 @@
kgsl_pwrscale_idle(device);
if (device->state & (KGSL_STATE_ACTIVE | KGSL_STATE_NAP)) {
- if (kgsl_pwrctrl_sleep(device) != 0)
+ if (kgsl_pwrctrl_sleep(device) != 0) {
mod_timer(&device->idle_timer,
jiffies +
device->pwrctrl.interval_timeout);
+ /* If the GPU has been too busy to sleep, make sure *
+ * that is acurately reflected in the % busy numbers. */
+ device->pwrctrl.busy.no_nap_cnt++;
+ if (device->pwrctrl.busy.no_nap_cnt > UPDATE_BUSY) {
+ kgsl_pwrctrl_busy_time(device, true);
+ device->pwrctrl.busy.no_nap_cnt = 0;
+ }
+ }
} else if (device->state & (KGSL_STATE_HUNG |
KGSL_STATE_DUMP_AND_RECOVER)) {
device->requested_state = KGSL_STATE_NONE;
@@ -601,6 +657,8 @@
clk_set_rate(pwr->grp_clks[0],
pwr->pwrlevels[pwr->num_pwrlevels - 1].
gpu_freq);
+ kgsl_pwrctrl_busy_time(device, false);
+ pwr->busy.start.tv_sec = 0;
device->pwrctrl.time = 0;
kgsl_pwrscale_sleep(device);