linux-kernel-devfreq-governor_maliondemand.c

/*
 *  linux/drivers/devfreq/governor_maliondemand.c
 *  Copyright (C) *
 * base on:
 *  linux/drivers/devfreq/governor_simpleondemand.c
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/errno.h>
#include <linux/module.h>
#include <linux/devfreq.h>
#include <linux/math64.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/pm.h>
#include <linux/mutex.h>

#include "governor.h"


/* Default constants for DevFreq-Mali-Ondemand (DFMO) */
#define DFMO_VSYNC_UPTHRESHOLD  (90)
#define DFMO_VSYNC_DOWNDIFFERENCTIAL (5)
#define DFMO_NO_VSYNC_UPTHRESHOLD (40)
#define DFMO_NO_VSYNC_DOWNDIFFERENCTIAL (10)
#define DFMO_MAX_UPTHRESHOLD  (100)
#define DFMO_MIN_UPTHRESHOLD  (11)
#define DFMO_MAX_DOWNDIFFERENCTIAL (30)
#define DFMO_MIN_DOWNDIFFERENCTIAL (1)

struct devfreq_mali_ondemand_data {
 unsigned int vsync_upthreshold;
 unsigned int vsync_downdifferential;
 unsigned int no_vsync_upthreshold;
 unsigned int no_vsync_downdifferential;
 int vsync;
 int utilisation;
};

static int devfreq_mali_ondemand_func(struct devfreq *df,
     unsigned long *freq)
{
 struct devfreq_dev_status stat;
 int err = df->profile->get_dev_status(df->dev.parent, &stat);
 unsigned long long a, b;
 unsigned int dfso_upthreshold = DFMO_VSYNC_UPTHRESHOLD;
 unsigned int dfso_downdifferential = DFMO_VSYNC_DOWNDIFFERENCTIAL;
 struct devfreq_mali_ondemand_data *data = df->data;
 unsigned long max = (df->max_freq) ? df->max_freq : UINT_MAX;

 if (err)
  return err;

 if (data) {
  data->vsync = (int)stat.private_data;
  data->utilisation = stat.busy_time * 100 / stat.total_time;

  if (data->vsync) {
   dfso_upthreshold = data->vsync_upthreshold;
   dfso_downdifferential = data->vsync_downdifferential;
  } else {
   dfso_upthreshold = data->no_vsync_upthreshold;
   dfso_downdifferential = data->no_vsync_downdifferential;
  }
 }
 if (dfso_upthreshold > 100 ||
     dfso_upthreshold < dfso_downdifferential) {
  pr_err("%s: invalid performance parameter, upth[%d], diff[%d]\n",
   __func__, dfso_upthreshold, dfso_downdifferential);
  return -EINVAL;
 }

 /* Assume MAX if it is going to be divided by zero */
 if (stat.total_time == 0) {
  *freq = max;
  return 0;
 }

 /* Prevent overflow */
 if (stat.busy_time >= (1 << 24) || stat.total_time >= (1 << 24)) {
  stat.busy_time >>= 7;
  stat.total_time >>= 7;
 }

 /* Set MAX if it's busy enough */
 if (stat.busy_time * 100 >
     stat.total_time * dfso_upthreshold) {
  *freq = max;
  return 0;
 }

 /* Set MAX if we do not know the initial frequency */
 if (stat.current_frequency == 0) {
  *freq = max;
  return 0;
 }

 /* Keep the current frequency */
 if (stat.busy_time * 100 >
     stat.total_time * (dfso_upthreshold - dfso_downdifferential)) {
  *freq = stat.current_frequency;
  return 0;
 }

 /* Set the desired frequency based on the load */
 a = stat.busy_time;
 a *= stat.current_frequency;
 b = div_u64(a, stat.total_time);
 b *= 100;
 b = div_u64(b, (dfso_upthreshold - dfso_downdifferential / 2));
 *freq = (unsigned long) b;

 if (df->min_freq && *freq < df->min_freq)
  *freq = df->min_freq;
 if (df->max_freq && *freq > df->max_freq)
  *freq = df->max_freq;

 return 0;
}


#define store_one(object, min, max)      \
static ssize_t store_##object      \
(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \
{          \
 struct devfreq *devfreq = to_devfreq(dev);    \
 struct devfreq_mali_ondemand_data *data;    \
 unsigned int input;       \
 int ret = 0;        \
 ret = sscanf(buf, "%u", &input);     \
 if (ret != 1 || input > max || input < min)    \
  return -EINVAL;       \
 mutex_lock(&devfreq->lock);      \
 data = devfreq->data;       \
 data->object = input;       \
 ret = update_devfreq(devfreq);      \
 if (ret == 0)        \
  ret = count;       \
 mutex_unlock(&devfreq->lock);      \
 return ret;        \
}

store_one(vsync_upthreshold, DFMO_MIN_UPTHRESHOLD, DFMO_MAX_UPTHRESHOLD)
store_one(vsync_downdifferential, DFMO_MIN_DOWNDIFFERENCTIAL, DFMO_MAX_DOWNDIFFERENCTIAL)
store_one(no_vsync_upthreshold, DFMO_MIN_UPTHRESHOLD, DFMO_MAX_UPTHRESHOLD)
store_one(no_vsync_downdifferential, DFMO_MIN_DOWNDIFFERENCTIAL, DFMO_MAX_DOWNDIFFERENCTIAL)


#define show_one(object)     \
static ssize_t show_##object     \
(struct device *dev, struct device_attribute *attr, char *buf) \
{        \
 struct devfreq *devfreq = to_devfreq(dev);  \
 struct devfreq_mali_ondemand_data *data;  \
 int ret = 0;      \
 mutex_lock(&devfreq->lock);    \
 data = devfreq->data;     \
 ret = sprintf(buf, "%u\n", data->object);  \
 mutex_unlock(&devfreq->lock);    \
 return ret;      \
}

show_one(vsync_upthreshold)
show_one(vsync_downdifferential)
show_one(no_vsync_upthreshold)
show_one(no_vsync_downdifferential)
show_one(vsync)
show_one(utilisation)

#define MALI_ONDEMAND_ATTR_RW(_name) \
 static DEVICE_ATTR(_name, 0644, show_##_name, store_##_name)

MALI_ONDEMAND_ATTR_RW(vsync_upthreshold);
MALI_ONDEMAND_ATTR_RW(vsync_downdifferential);
MALI_ONDEMAND_ATTR_RW(no_vsync_upthreshold);
MALI_ONDEMAND_ATTR_RW(no_vsync_downdifferential);

#define MALI_ONDEMAND_ATTR_RO(_name) \
 static DEVICE_ATTR(_name, 0444, show_##_name, NULL)

MALI_ONDEMAND_ATTR_RO(vsync);
MALI_ONDEMAND_ATTR_RO(utilisation);


static struct attribute *dev_entries[] = {
 &dev_attr_vsync_upthreshold.attr,
 &dev_attr_vsync_downdifferential.attr,
 &dev_attr_no_vsync_upthreshold.attr,
 &dev_attr_no_vsync_downdifferential.attr,
 &dev_attr_vsync.attr,
 &dev_attr_utilisation.attr,
 NULL,
};


static struct attribute_group dev_attr_group = {
 .name = "mali_ondemand",
 .attrs = dev_entries,
};

static int mali_ondemand_init(struct devfreq *devfreq)
{
 int err = 0;
 struct devfreq_mali_ondemand_data *data;

 if (!devfreq->data) {
  data = kzalloc(sizeof(struct devfreq_mali_ondemand_data),
           GFP_KERNEL);
  if (!data) {
   err = -ENOMEM;
   goto out;
  }
  data->vsync_upthreshold = DFMO_VSYNC_UPTHRESHOLD;
  data->vsync_downdifferential = DFMO_VSYNC_DOWNDIFFERENCTIAL;
  data->no_vsync_upthreshold = DFMO_NO_VSYNC_UPTHRESHOLD;
  data->no_vsync_downdifferential = DFMO_NO_VSYNC_DOWNDIFFERENCTIAL;
  devfreq->data = data;
 }

 err = sysfs_create_group(&devfreq->dev.kobj, &dev_attr_group);
out:
 return err;
}

static void mali_ondemand_exit(struct devfreq *devfreq)
{
 sysfs_remove_group(&devfreq->dev.kobj, &dev_attr_group);
 kfree(devfreq->data);
 devfreq->data = NULL;
}


static int devfreq_mali_ondemand_handler(struct devfreq *devfreq,
    unsigned int event, void *data)
{
 int ret = 0;

 switch (event) {
 case DEVFREQ_GOV_START:
  ret = mali_ondemand_init(devfreq);
  if (!ret)
   devfreq_monitor_start(devfreq);
  break;

 case DEVFREQ_GOV_STOP:
  devfreq_monitor_stop(devfreq);
  mali_ondemand_exit(devfreq);
  break;

 case DEVFREQ_GOV_INTERVAL:
  devfreq_interval_update(devfreq, (unsigned int *)data);
  break;

 case DEVFREQ_GOV_SUSPEND:
  devfreq_monitor_suspend(devfreq);
  break;

 case DEVFREQ_GOV_RESUME:
  devfreq_monitor_resume(devfreq);
  break;

 default:
  break;
 }

 return ret;
}

static struct devfreq_governor devfreq_mali_ondemand = {
 .name = "mali_ondemand",
 .get_target_freq = devfreq_mali_ondemand_func,
 .event_handler = devfreq_mali_ondemand_handler,
};

static int __init devfreq_mali_ondemand_init(void)
{
 return devfreq_add_governor(&devfreq_mali_ondemand);
}
subsys_initcall(devfreq_mali_ondemand_init);

static void __exit devfreq_mali_ondemand_exit(void)
{
 int ret;

 ret = devfreq_remove_governor(&devfreq_mali_ondemand);
 if (ret)
  pr_err("%s: failed remove governor %d\n", __func__, ret);

 return;
}
module_exit(devfreq_mali_ondemand_exit);
MODULE_LICENSE("GPL");

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值