#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"
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)
static int kbase_platform_rk_init(struct kbase_device *kbdev)
{
+ int ret = 0;
struct rk_context *platform;
platform = kzalloc(sizeof(*platform), GFP_KERNEL);
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)
platform->kbdev = NULL;
kfree(platform);
}
+ kbase_platform_rk_remove_sysfs_files(kbdev->dev);
}
struct kbase_platform_funcs_conf platform_funcs = {
{
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);
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,
}
}
+/*---------------------------------------------------------------------------*/
+
+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);
+}
/*---------------------------------------------------------------------------*/
-/**
+#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_ */