MALI: midgard: rockchip: add sysfs files to get GPU utilisation
authorchenzhen <chenzhen@rock-chips.com>
Wed, 11 May 2016 10:18:04 +0000 (18:18 +0800)
committerGerrit Code Review <gerrit@rock-chips.com>
Tue, 2 Aug 2016 12:01:37 +0000 (20:01 +0800)
Change-Id: I758369bdd9ef945a89fd87fd7a69bd9f391f0880
Signed-off-by: chenzhen <chenzhen@rock-chips.com>
drivers/gpu/arm/midgard/platform/rk/mali_kbase_config_rk.c
drivers/gpu/arm/midgard/platform/rk/mali_kbase_rk.h

index 62d477dd961e153ba469713b3b5ea4ccaaac0ac5..f7c0a66e3879b43bf4229a048fbcdcf56821797c 100644 (file)
 #include <mali_kbase.h>
 #include <mali_kbase_defs.h>
 #include <mali_kbase_config.h>
+#include <backend/gpu/mali_kbase_pm_internal.h>
 
 #include <linux/pm_runtime.h>
 #include <linux/suspend.h>
 #include <linux/of.h>
+#include <linux/delay.h>
 
 #include "mali_kbase_rk.h"
 
@@ -48,6 +50,10 @@ static int rk_pm_enable_clk(struct kbase_device *kbdev);
 
 static void rk_pm_disable_clk(struct kbase_device *kbdev);
 
+static int kbase_platform_rk_create_sysfs_files(struct device *dev);
+
+static void kbase_platform_rk_remove_sysfs_files(struct device *dev);
+
 /*---------------------------------------------------------------------------*/
 
 static void rk_pm_power_off_delay_work(struct work_struct *work)
@@ -74,6 +80,7 @@ static void rk_pm_power_off_delay_work(struct work_struct *work)
 
 static int kbase_platform_rk_init(struct kbase_device *kbdev)
 {
+       int ret = 0;
        struct rk_context *platform;
 
        platform = kzalloc(sizeof(*platform), GFP_KERNEL);
@@ -96,11 +103,19 @@ static int kbase_platform_rk_init(struct kbase_device *kbdev)
                return -ENOMEM;
        }
        INIT_DEFERRABLE_WORK(&platform->work, rk_pm_power_off_delay_work);
+       platform->utilisation_period = DEFAULT_UTILISATION_PERIOD_IN_MS;
+
+       ret = kbase_platform_rk_create_sysfs_files(kbdev->dev);
+       if (ret) {
+               E("fail to create sysfs_files. ret = %d.", ret);
+               goto EXIT;
+       }
 
        kbdev->platform_context = (void *)platform;
        pm_runtime_enable(kbdev->dev);
 
-       return 0;
+EXIT:
+       return ret;
 }
 
 static void kbase_platform_rk_term(struct kbase_device *kbdev)
@@ -117,6 +132,7 @@ static void kbase_platform_rk_term(struct kbase_device *kbdev)
                platform->kbdev = NULL;
                kfree(platform);
        }
+       kbase_platform_rk_remove_sysfs_files(kbdev->dev);
 }
 
 struct kbase_platform_funcs_conf platform_funcs = {
@@ -139,8 +155,7 @@ static int rk_pm_callback_power_on(struct kbase_device *kbdev)
 {
        int ret = 1; /* Assume GPU has been powered off */
        int err = 0;
-       struct rk_context *platform =
-               (struct rk_context *)kbdev->platform_context;
+       struct rk_context *platform = get_rk_context(kbdev);
 
        cancel_delayed_work_sync(&platform->work);
 
@@ -186,8 +201,7 @@ static int rk_pm_callback_power_on(struct kbase_device *kbdev)
 
 static void rk_pm_callback_power_off(struct kbase_device *kbdev)
 {
-       struct rk_context *platform =
-               (struct rk_context *)kbdev->platform_context;
+       struct rk_context *platform = get_rk_context(kbdev);
 
        rk_pm_disable_clk(kbdev);
        queue_delayed_work(platform->power_off_wq, &platform->work,
@@ -292,3 +306,97 @@ static void rk_pm_disable_clk(struct kbase_device *kbdev)
        }
 }
 
+/*---------------------------------------------------------------------------*/
+
+static ssize_t utilisation_period_show(struct device *dev,
+                                      struct device_attribute *attr,
+                                      char *buf)
+{
+       struct kbase_device *kbdev = dev_get_drvdata(dev);
+       struct rk_context *platform = get_rk_context(kbdev);
+       ssize_t ret = 0;
+
+       ret += snprintf(buf, PAGE_SIZE, "%u\n", platform->utilisation_period);
+
+       return ret;
+}
+
+static ssize_t utilisation_period_store(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf,
+                                       size_t count)
+{
+       struct kbase_device *kbdev = dev_get_drvdata(dev);
+       struct rk_context *platform = get_rk_context(kbdev);
+       int ret = 0;
+
+       ret = kstrtouint(buf, 0, &platform->utilisation_period);
+       if (ret) {
+               E("invalid input period : %s.", buf);
+               return ret;
+       }
+       D("set utilisation_period to '%d'.", platform->utilisation_period);
+
+       return count;
+}
+
+static ssize_t utilisation_show(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       struct kbase_device *kbdev = dev_get_drvdata(dev);
+       struct rk_context *platform = get_rk_context(kbdev);
+       ssize_t ret = 0;
+       unsigned long period_in_us = platform->utilisation_period * 1000;
+       unsigned long total_time;
+       unsigned long busy_time;
+       unsigned long utilisation;
+
+       kbase_pm_reset_dvfs_utilisation(kbdev);
+       usleep_range(period_in_us, period_in_us + 100);
+       kbase_pm_get_dvfs_utilisation(kbdev, &total_time, &busy_time);
+       /* 'devfreq_dev_profile' instance registered to devfreq
+        * also uses kbase_pm_reset_dvfs_utilisation
+        * and kbase_pm_get_dvfs_utilisation.
+        * it's better to cat this file when DVFS is disabled.
+        */
+       D("total_time : %lu, busy_time : %lu.", total_time, busy_time);
+
+       utilisation = busy_time * 100 / total_time;
+       ret += snprintf(buf, PAGE_SIZE, "%ld\n", utilisation);
+
+       return ret;
+}
+
+static DEVICE_ATTR_RW(utilisation_period);
+static DEVICE_ATTR_RO(utilisation);
+
+static int kbase_platform_rk_create_sysfs_files(struct device *dev)
+{
+       int ret = 0;
+
+       ret = device_create_file(dev, &dev_attr_utilisation_period);
+       if (ret) {
+               E("fail to create sysfs file 'utilisation_period'.");
+               goto out;
+       }
+
+       ret = device_create_file(dev, &dev_attr_utilisation);
+       if (ret) {
+               E("fail to create sysfs file 'utilisation'.");
+               goto remove_utilisation_period;
+       }
+
+       return 0;
+
+remove_utilisation_period:
+       device_remove_file(dev, &dev_attr_utilisation_period);
+out:
+       return ret;
+}
+
+static void kbase_platform_rk_remove_sysfs_files(struct device *dev)
+{
+       device_remove_file(dev, &dev_attr_utilisation_period);
+       device_remove_file(dev, &dev_attr_utilisation);
+}
index d6db811d1fce092ebf87fa6a8e8ea41b477527d3..e1623806abe8b07d84bff60d6a5c6cd6675489b5 100644 (file)
 
 /*---------------------------------------------------------------------------*/
 
-/**
+#define DEFAULT_UTILISATION_PERIOD_IN_MS (100)
+
+/*---------------------------------------------------------------------------*/
+
+/*
  * struct rk_context - work_context of platform_dependent_part_of_rk.
- * @is_powered: record the status
- *      of common_parts calling 'power_on_callback' and 'power_off_callback'.
  */
 struct rk_context {
+       /*
+        * record the status of common_parts calling 'power_on_callback'
+        * and 'power_off_callback'.
+        */
        bool is_powered;
        struct kbase_device *kbdev;
        struct workqueue_struct *power_off_wq;
        struct delayed_work work;
        unsigned int delay_ms;
+       /* debug only, the period in ms to count gpu_utilisation. */
+       unsigned int utilisation_period;
 };
 
+/*---------------------------------------------------------------------------*/
+
+static inline struct rk_context *get_rk_context(
+               const struct kbase_device *kbdev)
+{
+       return (struct rk_context *)(kbdev->platform_context);
+}
+
 #endif                         /* _MALI_KBASE_RK_H_ */