devfreq: Bring up to exynos devfreq.
authorChungwoo Park <cww.park@samsung.com>
Mon, 14 May 2018 09:26:03 +0000 (18:26 +0900)
committerChungwoo Park <cww.park@samsung.com>
Mon, 21 May 2018 08:26:25 +0000 (17:26 +0900)
Change-Id: I5f8e4a0b203e4046fd3378111faf24cd400caa50
Signed-off-by: Chungwoo Park <cww.park@samsung.com>
12 files changed:
drivers/devfreq/Kconfig
drivers/devfreq/Makefile
drivers/devfreq/exynos/Makefile [new file with mode: 0644]
drivers/devfreq/exynos/exynos-devfreq.c [new file with mode: 0644]
drivers/devfreq/exynos/exynos_ppmu.c [new file with mode: 0644]
drivers/devfreq/exynos/exynos_ppmu.h [new file with mode: 0644]
drivers/devfreq/governor_simpleexynos.c [new file with mode: 0644]
drivers/devfreq/governor_simpleinteractive.c [new file with mode: 0644]
drivers/devfreq/governor_simpleusage.c [new file with mode: 0644]
include/dt-bindings/soc/samsung/exynos9610-devfreq.h [new file with mode: 0644]
include/linux/devfreq.h
include/soc/samsung/exynos-devfreq.h [new file with mode: 0644]

index 6a172d338f6dcc387bfd4644f7d46709a375231a..0df5d5609feaf4cffe9a52d22dc1625365433891 100644 (file)
@@ -39,6 +39,21 @@ config DEVFREQ_GOV_SIMPLE_ONDEMAND
          Simple-Ondemand should be able to provide busy/total counter
          values that imply the usage rate. A device may provide tuned
          values to the governor with data field at devfreq_add_device().
+config DEVFREQ_GOV_SIMPLE_USAGE
+       tristate "Simple Usage"
+       help
+         Chooses frequency based on the percentage of target device.
+
+config DEVFREQ_GOV_SIMPLE_EXYNOS
+       tristate "Simple Exynos"
+       help
+         Chooses frequency based on the threshold of target device.
+
+config DEVFREQ_GOV_SIMPLE_INTERACTIVE
+       tristate "Simple Interactive"
+       help
+         Chooses frequency based on the requested PM QoS from target device.
+         And This governor uses timer when change frequency.
 
 config DEVFREQ_GOV_PERFORMANCE
        tristate "Performance"
@@ -75,6 +90,18 @@ config DEVFREQ_GOV_PASSIVE
 
 comment "DEVFREQ Drivers"
 
+config ARM_EXYNOS_DEVFREQ
+       bool "ARM Exynos Memory Bus DEVFREQ Common driver"
+       depends on ARCH_EXYNOS
+       select ARCH_HAS_OPP
+       select PM_OPP
+       help
+         This adds the DEVFREQ driver for Exynos series common bus.
+
+config ARM_EXYNOS_DEVFREQ_DEBUG
+       bool "ARM Exynos DEVFREQ sysfs for Debug"
+       depends on ARM_EXYNOS_DEVFREQ
+
 config ARM_EXYNOS_BUS_DEVFREQ
        tristate "ARM EXYNOS Generic Memory Bus DEVFREQ Driver"
        depends on ARCH_EXYNOS || COMPILE_TEST
@@ -115,4 +142,13 @@ config ARM_RK3399_DMC_DEVFREQ
 
 source "drivers/devfreq/event/Kconfig"
 
+config ARM_EXYNOS9610_BUS_DEVFREQ
+       bool "ARM Exynos9610 Memory Bus DEVFREQ Driver"
+       default y
+       depends on ARM_EXYNOS_DEVFREQ
+       depends on SOC_EXYNOS9610
+       select DEVFREQ_GOV_SIMPLE_INTERACTIVE
+       help
+         This adds the DEVFREQ driver for Exynos9610 memory and int.
+
 endif # PM_DEVFREQ
index 32b8d4d3f12c742a56c00ed74618fe9c90172da4..2d55dab951e438ac00c098e6c569fcd22009fe8c 100644 (file)
@@ -2,6 +2,9 @@
 obj-$(CONFIG_PM_DEVFREQ)       += devfreq.o
 obj-$(CONFIG_PM_DEVFREQ_EVENT) += devfreq-event.o
 obj-$(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND)      += governor_simpleondemand.o
+obj-$(CONFIG_DEVFREQ_GOV_SIMPLE_EXYNOS)        += governor_simpleexynos.o
+obj-$(CONFIG_DEVFREQ_GOV_SIMPLE_INTERACTIVE)   += governor_simpleinteractive.o
+obj-$(CONFIG_DEVFREQ_GOV_SIMPLE_USAGE) += governor_simpleusage.o
 obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE)  += governor_performance.o
 obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE)    += governor_powersave.o
 obj-$(CONFIG_DEVFREQ_GOV_USERSPACE)    += governor_userspace.o
@@ -14,3 +17,4 @@ obj-$(CONFIG_ARM_TEGRA_DEVFREQ)               += tegra-devfreq.o
 
 # DEVFREQ Event Drivers
 obj-$(CONFIG_PM_DEVFREQ_EVENT)         += event/
+obj-$(CONFIG_ARM_EXYNOS_DEVFREQ)        += exynos/
diff --git a/drivers/devfreq/exynos/Makefile b/drivers/devfreq/exynos/Makefile
new file mode 100644 (file)
index 0000000..d9df799
--- /dev/null
@@ -0,0 +1,3 @@
+# Exynos DEVFREQ Drivers
+obj-$(CONFIG_ARM_EXYNOS_DEVFREQ)       += exynos-devfreq.o
+obj-$(CONFIG_EXYNOS_WD_DVFS)   += exynos_ppmu.o
diff --git a/drivers/devfreq/exynos/exynos-devfreq.c b/drivers/devfreq/exynos/exynos-devfreq.c
new file mode 100644 (file)
index 0000000..ea4f05f
--- /dev/null
@@ -0,0 +1,1596 @@
+/* linux/drivers/devfreq/exynos-devfreq.c
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * Samsung EXYNOS SoC series devfreq common driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation, either version 2 of the License,
+ * or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/reboot.h>
+#include <linux/suspend.h>
+#include <linux/io.h>
+#include <linux/sched/clock.h>
+#include <linux/clk.h>
+#include <soc/samsung/cal-if.h>
+#include <soc/samsung/bts.h>
+#include <linux/of_platform.h>
+#include <dt-bindings/soc/samsung/exynos9610-devfreq.h>
+#include "../../soc/samsung/cal-if/acpm_dvfs.h"
+#include <soc/samsung/exynos-pd.h>
+
+#include <soc/samsung/exynos-devfreq.h>
+#include <soc/samsung/ect_parser.h>
+#ifdef CONFIG_EXYNOS_DVFS_MANAGER
+#include <soc/samsung/exynos-dm.h>
+#endif
+#ifdef CONFIG_EXYNOS_ACPM
+#include "../../soc/samsung/acpm/acpm.h"
+#include "../../soc/samsung/acpm/acpm_ipc.h"
+#endif
+
+#include "../governor.h"
+
+static struct exynos_devfreq_data **devfreq_data;
+
+static u32 freq_array[6];
+static u32 boot_array[2];
+
+#ifdef CONFIG_EXYNOS_DVFS_MANAGER
+static unsigned int ect_find_constraint_freq(struct ect_minlock_domain *ect_domain,
+                                       unsigned int freq)
+{
+       unsigned int i;
+
+       for (i = 0; i < ect_domain->num_of_level; i++)
+               if (ect_domain->level[i].main_frequencies == freq)
+                       break;
+
+       return ect_domain->level[i].sub_frequencies;
+}
+#endif
+
+static int exynos_constraint_parse(struct exynos_devfreq_data *data,
+               unsigned int min_freq, unsigned int max_freq)
+{
+       struct device_node *np, *child;
+       u32 num_child, constraint_dm_type, constraint_type;
+       const char *devfreq_domain_name;
+       int i = 0, j, const_flag = 1;
+       void *min_block, *dvfs_block;
+       struct ect_dvfs_domain *dvfs_domain;
+       struct ect_minlock_domain *ect_domain;
+#ifdef CONFIG_EXYNOS_DVFS_MANAGER
+       struct exynos_dm_freq *const_table;
+#endif
+       np = of_get_child_by_name(data->dev->of_node, "skew");
+       if (!np)
+               return 0;
+       num_child = of_get_child_count(np);
+#ifdef CONFIG_EXYNOS_DVFS_MANAGER
+       data->nr_constraint = num_child;
+       data->constraint = kzalloc(sizeof(struct exynos_dm_constraint *) * num_child, GFP_KERNEL);
+#endif
+       if (of_property_read_string(data->dev->of_node, "devfreq_domain_name", &devfreq_domain_name))
+               return -ENODEV;
+
+       dvfs_block = ect_get_block(BLOCK_DVFS);
+       if (dvfs_block == NULL)
+               return -ENODEV;
+
+       dvfs_domain = ect_dvfs_get_domain(dvfs_block, (char *)devfreq_domain_name);
+       if (dvfs_domain == NULL)
+               return -ENODEV;
+
+       /* Although there is not any constraint, MIF table should be sent to FVP */
+       min_block = ect_get_block(BLOCK_MINLOCK);
+       if (min_block == NULL) {
+               dev_info(data->dev, "There is not a min block in ECT\n");
+               const_flag = 0;
+       }
+
+       ect_domain = ect_minlock_get_domain(min_block, (char *)devfreq_domain_name);
+       if (ect_domain == NULL) {
+               dev_info(data->dev, "There is not a domain in min block\n");
+               const_flag = 0;
+       }
+
+       for_each_available_child_of_node(np, child) {
+               int use_level = 0;
+
+               if (of_property_read_u32(child, "constraint_dm_type", &constraint_dm_type))
+                       return -ENODEV;
+               if (of_property_read_u32(child, "constraint_type", &constraint_type))
+                       return -ENODEV;
+#ifdef CONFIG_EXYNOS_DVFS_MANAGER
+               if (const_flag) {
+                       data->constraint[i] =
+                               kzalloc(sizeof(struct exynos_dm_constraint), GFP_KERNEL);
+                       if (data->constraint[i] == NULL) {
+                               dev_err(data->dev, "failed to allocate constraint\n");
+                               return -ENOMEM;
+                       }
+
+                       const_table = kzalloc(sizeof(struct exynos_dm_freq) * ect_domain->num_of_level, GFP_KERNEL);
+                       if (const_table == NULL) {
+                               dev_err(data->dev, "failed to allocate constraint\n");
+                               kfree(data->constraint[i]);
+                               return -ENOMEM;
+                       }
+
+                       data->constraint[i]->guidance = true;
+                       data->constraint[i]->constraint_type = constraint_type;
+                       data->constraint[i]->constraint_dm_type = constraint_dm_type;
+                       data->constraint[i]->table_length = ect_domain->num_of_level;
+                       data->constraint[i]->freq_table = const_table;
+               }
+#endif
+               for (j = 0; j < dvfs_domain->num_of_level; j++) {
+                       if (data->opp_list[j].freq > max_freq ||
+                                       data->opp_list[j].freq < min_freq)
+                               continue;
+
+#ifdef CONFIG_EXYNOS_DVFS_MANAGER
+                       if (const_flag) {
+                               const_table[use_level].master_freq = data->opp_list[j].freq;
+                               const_table[use_level].constraint_freq
+                                       = ect_find_constraint_freq(ect_domain, data->opp_list[j].freq);
+                       }
+#endif
+                       use_level++;
+               }
+               i++;
+       }
+       return 0;
+}
+
+static int exynos_devfreq_update_fvp(struct exynos_devfreq_data *data, u32 min_freq, u32 max_freq)
+{
+       int ret, ch_num, size, i, use_level = 0;
+       u32 cmd[4];
+       struct ipc_config config;
+       int nr_constraint = 0;
+#ifdef CONFIG_EXYNOS_DVFS_MANAGER
+       int j;
+       struct exynos_dm_constraint *constraint;
+
+       nr_constraint = data->nr_constraint;
+#endif
+       ret = acpm_ipc_request_channel(data->dev->of_node, NULL, &ch_num, &size);
+       if (ret) {
+               dev_err(data->dev, "acpm request channel is failed, id:%u, size:%u\n", ch_num, size);
+               return -EINVAL;
+       }
+       config.cmd = cmd;
+       config.response = true;
+       config.indirection = false;
+
+       /* constraint info update */
+       if (nr_constraint == 0) {
+               for (i = 0; i < data->max_state; i++) {
+                       if (data->opp_list[i].freq > max_freq ||
+                                       data->opp_list[i].freq < min_freq)
+                               continue;
+
+                       config.cmd[0] = use_level;
+                       config.cmd[1] = data->opp_list[i].freq;
+                       config.cmd[2] = DATA_INIT;
+                       config.cmd[3] = 0;
+                       ret = acpm_ipc_send_data(ch_num, &config);
+                       if (ret) {
+                               dev_err(data->dev, "make constraint table is failed");
+                               return -EINVAL;
+                       }
+                       use_level++;
+               }
+       }
+#ifdef CONFIG_EXYNOS_DVFS_MANAGER
+       else {
+               for (i = 0; i < data->nr_constraint; i++) {
+                       constraint = data->constraint[i];
+                       for (j = 0; j < data->max_state; j++) {
+                               if (data->opp_list[j].freq > max_freq ||
+                                               data->opp_list[j].freq < min_freq)
+                                       continue;
+
+                               config.cmd[0] = use_level;
+                               config.cmd[1] = data->opp_list[j].freq;
+                               config.cmd[2] = DATA_INIT;
+                               config.cmd[3] = constraint->freq_table[use_level].constraint_freq;
+                               ret = acpm_ipc_send_data(ch_num, &config);
+                               if (ret) {
+                                       dev_err(data->dev, "make constraint table is failed");
+                                       return -EINVAL;
+                               }
+                               use_level++;
+                       }
+               }
+               /* Send MIF initial freq and the number of constraint data to FVP */
+               config.cmd[0] = use_level;
+               config.cmd[1] = (unsigned int)data->devfreq_profile.initial_freq;
+               config.cmd[2] = DATA_INIT;
+               config.cmd[3] = SET_CONST;
+
+               ret = acpm_ipc_send_data(ch_num, &config);
+               if (ret) {
+                       dev_err(data->dev, "failed to send nr_constraint and init freq");
+                       return -EINVAL;
+               }
+       }
+#endif
+
+       return 0;
+}
+
+static int exynos_devfreq_reboot(struct exynos_devfreq_data *data)
+{
+       if (pm_qos_request_active(&data->default_pm_qos_max))
+               pm_qos_update_request(&data->default_pm_qos_max,
+                               data->reboot_freq);
+       return 0;
+
+}
+
+static int exynos_devfreq_get_freq(struct device *dev, u32 *cur_freq,
+               struct clk *clk, struct exynos_devfreq_data *data)
+{
+       if (data->pm_domain) {
+               if (!exynos_pd_status(data->pm_domain)) {
+                       dev_err(dev, "power domain %s is offed\n", data->pm_domain->name);
+                       *cur_freq = 0;
+                       return -EINVAL;
+               }
+       }
+
+       *cur_freq = (u32)cal_dfs_get_rate(data->dfs_id);
+       if (*cur_freq == 0) {
+               dev_err(dev, "failed get frequency from CAL\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int exynos_devfreq_set_freq(struct device *dev, u32 new_freq,
+               struct clk *clk, struct exynos_devfreq_data *data)
+{
+#ifdef CONFIG_EXYNOS_BTS
+       if (data->bts_update) {
+               if (data->new_freq < data->old_freq)
+                       bts_update_scen(BS_MIF_CHANGE, data->new_freq);
+       }
+#endif
+       if (data->pm_domain) {
+               if (!exynos_pd_status(data->pm_domain)) {
+                       dev_err(dev, "power domain %s is offed\n", data->pm_domain->name);
+                       return -EINVAL;
+               }
+       }
+
+       if (cal_dfs_set_rate(data->dfs_id, (unsigned long)new_freq)) {
+               dev_err(dev, "failed set frequency to CAL (%uKhz)\n",
+                               new_freq);
+               return -EINVAL;
+       }
+
+#ifdef CONFIG_EXYNOS_BTS
+       if (data->bts_update) {
+               if (data->new_freq > data->old_freq)
+                       bts_update_scen(BS_MIF_CHANGE, data->new_freq);
+       }
+#endif
+
+       return 0;
+}
+
+static int exynos_devfreq_init_freq_table(struct exynos_devfreq_data *data)
+{
+       u32 max_freq, min_freq;
+       unsigned long tmp_max, tmp_min;
+       struct dev_pm_opp *target_opp;
+       u32 flags = 0;
+       int i, ret;
+
+       max_freq = (u32)cal_dfs_get_max_freq(data->dfs_id);
+       if (!max_freq) {
+               dev_err(data->dev, "failed get max frequency\n");
+               return -EINVAL;
+       }
+
+       dev_info(data->dev, "max_freq: %uKhz, get_max_freq: %uKhz\n",
+                       data->max_freq, max_freq);
+
+       if (max_freq < data->max_freq) {
+               flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND;
+               tmp_max = (unsigned long)max_freq;
+               target_opp = devfreq_recommended_opp(data->dev, &tmp_max, flags);
+               if (IS_ERR(target_opp)) {
+                       dev_err(data->dev, "not found valid OPP for max_freq\n");
+                       return PTR_ERR(target_opp);
+               }
+
+               data->max_freq = (u32)dev_pm_opp_get_freq(target_opp);
+               dev_pm_opp_put(target_opp);
+       }
+
+       /* min ferquency must be equal or under max frequency */
+       if (data->min_freq > data->max_freq)
+               data->min_freq = data->max_freq;
+
+       min_freq = (u32)cal_dfs_get_min_freq(data->dfs_id);
+       if (!min_freq) {
+               dev_err(data->dev, "failed get min frequency\n");
+               return -EINVAL;
+       }
+
+       dev_info(data->dev, "min_freq: %uKhz, get_min_freq: %uKhz\n",
+                       data->min_freq, min_freq);
+
+       if (min_freq > data->min_freq) {
+               flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND;
+               tmp_min = (unsigned long)min_freq;
+               target_opp = devfreq_recommended_opp(data->dev, &tmp_min, flags);
+               if (IS_ERR(target_opp)) {
+                       dev_err(data->dev, "not found valid OPP for min_freq\n");
+                       return PTR_ERR(target_opp);
+               }
+
+               data->min_freq = (u32)dev_pm_opp_get_freq(target_opp);
+               dev_pm_opp_put(target_opp);
+       }
+
+       dev_info(data->dev, "min_freq: %uKhz, max_freq: %uKhz\n",
+                       data->min_freq, data->max_freq);
+
+       for (i = 0; i < data->max_state; i++) {
+               if (data->opp_list[i].freq > data->max_freq ||
+                       data->opp_list[i].freq < data->min_freq)
+                       dev_pm_opp_disable(data->dev, (unsigned long)data->opp_list[i].freq);
+       }
+
+       data->devfreq_profile.initial_freq = cal_dfs_get_boot_freq(data->dfs_id);
+       data->devfreq_profile.suspend_freq = cal_dfs_get_resume_freq(data->dfs_id);
+
+       ret = exynos_constraint_parse(data, min_freq, max_freq);
+       if (ret) {
+               dev_err(data->dev, "failed to parse constraint table\n");
+               return -EINVAL;
+       }
+
+       if (data->update_fvp)
+               exynos_devfreq_update_fvp(data, min_freq, max_freq);
+
+       if (data->use_acpm) {
+               ret = exynos_acpm_set_init_freq(data->dfs_id, data->devfreq_profile.initial_freq);
+               if (ret) {
+                       dev_err(data->dev, "failed to set init freq\n");
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_ARM_EXYNOS_DEVFREQ_DEBUG
+static ssize_t show_exynos_devfreq_info(struct device *dev,
+                                       struct device_attribute *attr, char *buf)
+{
+       struct device *parent = dev->parent;
+       struct platform_device *pdev = container_of(parent, struct platform_device, dev);
+       struct exynos_devfreq_data *data = platform_get_drvdata(pdev);
+       ssize_t count = 0;
+       int i;
+
+       count = snprintf(buf, PAGE_SIZE, "[Exynos DEVFREQ Data]\n"
+                        "devfreq dev name : %20s\n"
+                        "devfreq type     : %20d\n"
+                        "Exynos SS flag   : %20u\n",
+                        dev_name(data->dev), data->devfreq_type, data->ess_flag);
+
+       count += snprintf(buf + count, PAGE_SIZE, "\n<Frequency data>\n"
+                         "OPP list length  : %20u\n", data->max_state);
+       count += snprintf(buf + count, PAGE_SIZE, "freq opp table\n");
+       count += snprintf(buf + count, PAGE_SIZE, "\t  idx      freq       volt\n");
+
+       for (i = 0; i < data->max_state; i++)
+               count += snprintf(buf + count, PAGE_SIZE, "\t%5u %10u %10u\n",
+                                 data->opp_list[i].idx, data->opp_list[i].freq,
+                                 data->opp_list[i].volt);
+
+       count += snprintf(buf + count, PAGE_SIZE,
+                         "default_qos     : %20u\n" "initial_freq    : %20lu\n"
+                         "min_freq        : %20u\n" "max_freq        : %20u\n"
+                         "boot_timeout(s) : %20u\n" "max_state       : %20u\n",
+                         data->default_qos, data->devfreq_profile.initial_freq,
+                         data->min_freq, data->max_freq, data->boot_qos_timeout, data->max_state);
+
+       count += snprintf(buf + count, PAGE_SIZE, "\n<Governor data>\n");
+       count += snprintf(buf + count, PAGE_SIZE,
+                         "governor_name   : %20s\n",
+                         data->governor_name);
+       return count;
+}
+
+static ssize_t show_exynos_devfreq_get_freq(struct device *dev,
+                                           struct device_attribute *attr, char *buf)
+{
+       struct device *parent = dev->parent;
+       struct platform_device *pdev = container_of(parent, struct platform_device, dev);
+       struct exynos_devfreq_data *data = platform_get_drvdata(pdev);
+       ssize_t count = 0;
+       u32 get_freq = 0;
+
+       if (exynos_devfreq_get_freq(data->dev, &get_freq, data->clk, data))
+               dev_err(data->dev, "failed get freq\n");
+
+       count = snprintf(buf, PAGE_SIZE, "%10u Khz\n", get_freq);
+
+       return count;
+}
+
+static int exynos_devfreq_cmu_dump(struct exynos_devfreq_data *data)
+{
+       mutex_lock(&data->devfreq->lock);
+       cal_vclk_dbg_info(data->dfs_id);
+       mutex_unlock(&data->devfreq->lock);
+
+       return 0;
+}
+
+static ssize_t show_exynos_devfreq_cmu_dump(struct device *dev,
+                                           struct device_attribute *attr, char *buf)
+{
+       struct device *parent = dev->parent;
+       struct platform_device *pdev = container_of(parent, struct platform_device, dev);
+       struct exynos_devfreq_data *data = platform_get_drvdata(pdev);
+       ssize_t count = 0;
+
+       mutex_lock(&data->lock);
+       if (exynos_devfreq_cmu_dump(data))
+               dev_err(data->dev, "failed CMU Dump\n");
+       mutex_unlock(&data->lock);
+
+       count = snprintf(buf, PAGE_SIZE, "Done\n");
+
+       return count;
+}
+
+static ssize_t show_debug_scaling_devfreq_max(struct device *dev,
+                                       struct device_attribute *attr, char *buf)
+{
+       struct device *parent = dev->parent;
+       struct platform_device *pdev = container_of(parent, struct platform_device, dev);
+       struct exynos_devfreq_data *data = platform_get_drvdata(pdev);
+       ssize_t count = 0;
+       int val;
+
+       if (data->pm_qos_class_max) {
+               val = pm_qos_read_req_value(data->pm_qos_class_max, &data->debug_pm_qos_max);
+               if (val < 0) {
+                       dev_err(dev, "failed to read requested value\n");
+                       return count;
+               }
+               count += snprintf(buf, PAGE_SIZE, "%d\n", val);
+       }
+
+       return count;
+}
+
+static ssize_t store_debug_scaling_devfreq_max(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t count)
+{
+       struct device *parent = dev->parent;
+       struct platform_device *pdev = container_of(parent, struct platform_device, dev);
+       struct exynos_devfreq_data *data = platform_get_drvdata(pdev);
+       int ret;
+       u32 qos_value;
+
+       ret = sscanf(buf, "%u", &qos_value);
+       if (ret != 1)
+               return -EINVAL;
+
+       if (data->pm_qos_class_max) {
+               if (pm_qos_request_active(&data->debug_pm_qos_max))
+                       pm_qos_update_request(&data->debug_pm_qos_max, qos_value);
+       }
+
+       return count;
+}
+
+static ssize_t show_debug_scaling_devfreq_min(struct device *dev,
+                                       struct device_attribute *attr, char *buf)
+{
+       struct device *parent = dev->parent;
+       struct platform_device *pdev = container_of(parent, struct platform_device, dev);
+       struct exynos_devfreq_data *data = platform_get_drvdata(pdev);
+       ssize_t count = 0;
+       int val;
+
+       val = pm_qos_read_req_value(data->pm_qos_class, &data->debug_pm_qos_min);
+       if (val < 0) {
+               dev_err(dev, "failed to read requested value\n");
+               return count;
+       }
+
+       count += snprintf(buf, PAGE_SIZE, "%d\n", val);
+
+       return count;
+}
+
+static ssize_t store_debug_scaling_devfreq_min(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t count)
+{
+       struct device *parent = dev->parent;
+       struct platform_device *pdev = container_of(parent, struct platform_device, dev);
+       struct exynos_devfreq_data *data = platform_get_drvdata(pdev);
+       int ret;
+       u32 qos_value;
+
+       ret = sscanf(buf, "%u", &qos_value);
+       if (ret != 1)
+               return -EINVAL;
+
+       if (pm_qos_request_active(&data->debug_pm_qos_min))
+               pm_qos_update_request(&data->debug_pm_qos_min, qos_value);
+
+       return count;
+}
+
+static ssize_t show_exynos_devfreq_disable_pm_qos(struct device *dev,
+                                                 struct device_attribute *attr, char *buf)
+{
+       struct device *parent = dev->parent;
+       struct platform_device *pdev = container_of(parent, struct platform_device, dev);
+       struct exynos_devfreq_data *data = platform_get_drvdata(pdev);
+       ssize_t count = 0;
+
+       count += snprintf(buf, PAGE_SIZE, "%s\n",
+                         data->devfreq->disabled_pm_qos ? "disabled" : "enabled");
+
+       return count;
+}
+
+static ssize_t store_exynos_devfreq_disable_pm_qos(struct device *dev,
+                                                  struct device_attribute *attr,
+                                                  const char *buf, size_t count)
+{
+       struct device *parent = dev->parent;
+       struct platform_device *pdev = container_of(parent, struct platform_device, dev);
+       struct exynos_devfreq_data *data = platform_get_drvdata(pdev);
+       int ret;
+       u32 disable;
+
+       ret = sscanf(buf, "%u", &disable);
+       if (ret != 1)
+               return -EINVAL;
+
+       if (disable)
+               data->devfreq->disabled_pm_qos = true;
+       else
+               data->devfreq->disabled_pm_qos = false;
+
+       return count;
+}
+static DEVICE_ATTR(exynos_devfreq_info, 0640, show_exynos_devfreq_info, NULL);
+static DEVICE_ATTR(exynos_devfreq_get_freq, 0640, show_exynos_devfreq_get_freq, NULL);
+static DEVICE_ATTR(exynos_devfreq_cmu_dump, 0640, show_exynos_devfreq_cmu_dump, NULL);
+static DEVICE_ATTR(debug_scaling_devfreq_min, 0640, show_debug_scaling_devfreq_min, store_debug_scaling_devfreq_min);
+static DEVICE_ATTR(debug_scaling_devfreq_max, 0640, show_debug_scaling_devfreq_max,
+                                               store_debug_scaling_devfreq_max);
+static DEVICE_ATTR(disable_pm_qos, 0640, show_exynos_devfreq_disable_pm_qos,
+                  store_exynos_devfreq_disable_pm_qos);
+
+static struct attribute *exynos_devfreq_sysfs_entries[] = {
+       &dev_attr_exynos_devfreq_info.attr,
+       &dev_attr_exynos_devfreq_get_freq.attr,
+       &dev_attr_exynos_devfreq_cmu_dump.attr,
+       &dev_attr_debug_scaling_devfreq_min.attr,
+       &dev_attr_debug_scaling_devfreq_max.attr,
+       &dev_attr_disable_pm_qos.attr,
+       NULL,
+};
+
+static struct attribute_group exynos_devfreq_attr_group = {
+       .name = "exynos_data",
+       .attrs = exynos_devfreq_sysfs_entries,
+};
+#endif
+
+static ssize_t show_scaling_devfreq_min(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct device *parent = dev->parent;
+       struct platform_device *pdev = container_of(parent, struct platform_device, dev);
+       struct exynos_devfreq_data *data = platform_get_drvdata(pdev);
+       ssize_t count = 0;
+       int val;
+
+       val = pm_qos_read_req_value(data->pm_qos_class, &data->sys_pm_qos_min);
+       if (val < 0) {
+               dev_err(dev, "failed to read requested value\n");
+               return count;
+       }
+
+       count += snprintf(buf, PAGE_SIZE, "%d\n", val);
+
+       return count;
+}
+
+static ssize_t store_scaling_devfreq_min(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t count)
+{
+       struct device *parent = dev->parent;
+       struct platform_device *pdev = container_of(parent, struct platform_device, dev);
+       struct exynos_devfreq_data *data = platform_get_drvdata(pdev);
+       int ret;
+       u32 qos_value;
+
+       ret = sscanf(buf, "%u", &qos_value);
+       if (ret != 1)
+               return -EINVAL;
+
+       if (pm_qos_request_active(&data->sys_pm_qos_min))
+               pm_qos_update_request(&data->sys_pm_qos_min, qos_value);
+
+       return count;
+}
+
+static DEVICE_ATTR(scaling_devfreq_min, 0640, show_scaling_devfreq_min, store_scaling_devfreq_min);
+
+/* get frequency and delay time data from string */
+static unsigned int *get_tokenized_data(const char *buf, int *num_tokens)
+{
+       const char *cp;
+       int i;
+       int ntokens = 1;
+       unsigned int *tokenized_data;
+       int err = -EINVAL;
+
+       cp = buf;
+       while ((cp = strpbrk(cp + 1, " :")))
+               ntokens++;
+
+       if (!(ntokens & 0x1))
+               goto err;
+
+       tokenized_data = kmalloc(ntokens * sizeof(unsigned int), GFP_KERNEL);
+       if (!tokenized_data) {
+               err = -ENOMEM;
+               goto err;
+       }
+
+       cp = buf;
+       i = 0;
+       while (i < ntokens) {
+               if (sscanf(cp, "%u", &tokenized_data[i++]) != 1)
+                       goto err_kfree;
+
+               cp = strpbrk(cp, " :");
+               if (!cp)
+                       break;
+               cp++;
+       }
+
+       if (i != ntokens)
+               goto err_kfree;
+
+       *num_tokens = ntokens;
+       return tokenized_data;
+
+err_kfree:
+       kfree(tokenized_data);
+err:
+       return ERR_PTR(err);
+}
+
+static ssize_t show_use_delay_time(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct device *parent = dev->parent;
+       struct platform_device *pdev = container_of(parent, struct platform_device, dev);
+       struct exynos_devfreq_data *data = platform_get_drvdata(pdev);
+       ssize_t count = 0;
+
+       mutex_lock(&data->devfreq->lock);
+       count += snprintf(buf, PAGE_SIZE, "%s\n",
+                         (data->simple_interactive_data.use_delay_time) ? "true" : "false");
+       mutex_unlock(&data->devfreq->lock);
+       return count;
+}
+
+static ssize_t store_use_delay_time(struct device *dev,
+                                   struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct device *parent = dev->parent;
+       struct platform_device *pdev = container_of(parent, struct platform_device, dev);
+       struct exynos_devfreq_data *data = platform_get_drvdata(pdev);
+       int ret, use_delay_time;
+
+       ret = sscanf(buf, "%d", &use_delay_time);
+
+       if (ret != 1)
+               return -EINVAL;
+
+       if (use_delay_time == 0 || use_delay_time == 1) {
+               mutex_lock(&data->devfreq->lock);
+               data->simple_interactive_data.use_delay_time = use_delay_time ? true : false;
+               mutex_unlock(&data->devfreq->lock);
+       } else {
+               dev_info(data->dev, "This is invalid value: %d\n", use_delay_time);
+       }
+
+       return count;
+}
+
+static ssize_t show_delay_time(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct device *parent = dev->parent;
+       struct platform_device *pdev = container_of(parent, struct platform_device, dev);
+       struct exynos_devfreq_data *data = platform_get_drvdata(pdev);
+       ssize_t count = 0;
+       int i;
+
+       mutex_lock(&data->devfreq->lock);
+       for (i = 0; i < data->simple_interactive_data.ndelay_time; i++) {
+               count += snprintf(buf + count, PAGE_SIZE, "%d%s",
+                               data->simple_interactive_data.delay_time[i],
+                               (i == data->simple_interactive_data.ndelay_time - 1) ?
+                               "" : (i % 2) ? ":" : " ");
+       }
+       count += snprintf(buf + count, PAGE_SIZE, "\n");
+       mutex_unlock(&data->devfreq->lock);
+       return count;
+}
+
+static ssize_t store_delay_time(struct device *dev,
+                               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct device *parent = dev->parent;
+       struct platform_device *pdev = container_of(parent, struct platform_device, dev);
+       struct exynos_devfreq_data *data = platform_get_drvdata(pdev);
+       int ntokens;
+       int *new_delay_time = NULL;
+
+       new_delay_time = get_tokenized_data(buf , &ntokens);
+       if (IS_ERR(new_delay_time))
+               return PTR_RET(new_delay_time);
+
+       mutex_lock(&data->devfreq->lock);
+       kfree(data->simple_interactive_data.delay_time);
+       data->simple_interactive_data.delay_time = new_delay_time;
+       data->simple_interactive_data.ndelay_time = ntokens;
+       mutex_unlock(&data->devfreq->lock);
+
+       return count;
+}
+
+static DEVICE_ATTR(use_delay_time, 0640, show_use_delay_time, store_use_delay_time);
+static DEVICE_ATTR(delay_time, 0640, show_delay_time, store_delay_time);
+
+static struct attribute *devfreq_interactive_sysfs_entries[] = {
+       &dev_attr_use_delay_time.attr,
+       &dev_attr_delay_time.attr,
+       NULL,
+};
+
+static struct attribute_group devfreq_delay_time_attr_group = {
+       .name = "interactive",
+       .attrs = devfreq_interactive_sysfs_entries,
+};
+
+#ifdef CONFIG_EXYNOS_DVFS_MANAGER
+int find_exynos_devfreq_dm_type(struct device *dev, int *dm_type)
+{
+       struct platform_device *pdev = container_of(dev, struct platform_device, dev);
+       struct exynos_devfreq_data *data = platform_get_drvdata(pdev);
+
+       *dm_type = data->dm_type;
+
+       return 0;
+}
+
+struct device *find_exynos_devfreq_device(void *devdata)
+{
+       struct exynos_devfreq_data *data = devdata;
+
+       if (!devdata) {
+               pr_err("%s: failed get Devfreq type\n", __func__);
+               return ERR_PTR(-EINVAL);
+       }
+
+       return data->dev;
+}
+#endif
+
+#ifdef CONFIG_OF
+#if defined(CONFIG_ECT)
+static int exynos_devfreq_parse_ect(struct exynos_devfreq_data *data, const char *dvfs_domain_name)
+{
+       int i;
+       void *dvfs_block;
+       struct ect_dvfs_domain *dvfs_domain;
+
+       dvfs_block = ect_get_block(BLOCK_DVFS);
+       if (dvfs_block == NULL)
+               return -ENODEV;
+
+       dvfs_domain = ect_dvfs_get_domain(dvfs_block, (char *)dvfs_domain_name);
+       if (dvfs_domain == NULL)
+               return -ENODEV;
+
+       data->max_state = dvfs_domain->num_of_level;
+       data->opp_list = kzalloc(sizeof(struct exynos_devfreq_opp_table) * data->max_state, GFP_KERNEL);
+       if (!data->opp_list) {
+               pr_err("%s: failed to allocate opp_list\n", __func__);
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < dvfs_domain->num_of_level; ++i) {
+               data->opp_list[i].idx = i;
+               data->opp_list[i].freq = dvfs_domain->list_level[i].level;
+               data->opp_list[i].volt = 0;
+       }
+
+       return 0;
+}
+#endif
+
+static int exynos_devfreq_parse_dt(struct device_node *np, struct exynos_devfreq_data *data)
+{
+       const char *use_acpm, *bts_update;
+#if defined(CONFIG_ECT)
+       const char *devfreq_domain_name;
+#endif
+       const char *buf;
+       const char *use_delay_time;
+       const char *pd_name;
+       const char *update_fvp;
+       int ntokens;
+       int not_using_ect = true;
+
+       if (!np)
+               return -ENODEV;
+
+       if (of_property_read_u32(np, "devfreq_type", &data->devfreq_type))
+               return -ENODEV;
+       if (of_property_read_u32(np, "pm_qos_class", &data->pm_qos_class))
+               return -ENODEV;
+       if (of_property_read_u32(np, "pm_qos_class_max", &data->pm_qos_class_max))
+               return -ENODEV;
+       if (of_property_read_u32(np, "ess_flag", &data->ess_flag))
+               return -ENODEV;
+
+#if defined(CONFIG_ECT)
+       if (of_property_read_string(np, "devfreq_domain_name", &devfreq_domain_name))
+               return -ENODEV;
+       not_using_ect = exynos_devfreq_parse_ect(data, devfreq_domain_name);
+#endif
+       if (not_using_ect) {
+               dev_err(data->dev, "cannot parse the DVFS info in ECT");
+               return -ENODEV;
+       }
+
+       if (of_property_read_string(np, "pd_name", &pd_name)) {
+               dev_info(data->dev, "no power domain\n");
+               data->pm_domain = NULL;
+       } else {
+               dev_info(data->dev, "power domain: %s\n", pd_name);
+               data->pm_domain = exynos_pd_lookup_name(pd_name);
+       }
+
+
+       if (of_property_read_u32_array(np, "freq_info", (u32 *)&freq_array,
+                                      (size_t)(ARRAY_SIZE(freq_array))))
+               return -ENODEV;
+
+       data->devfreq_profile.initial_freq = freq_array[0];
+       data->default_qos = freq_array[1];
+       data->devfreq_profile.suspend_freq = freq_array[2];
+       data->min_freq = freq_array[3];
+       data->max_freq = freq_array[4];
+       data->reboot_freq = freq_array[5];
+
+       if (of_property_read_u32_array(np, "boot_info", (u32 *)&boot_array,
+                                      (size_t)(ARRAY_SIZE(boot_array)))) {
+               data->boot_qos_timeout = 0;
+               data->boot_freq = 0;
+               dev_info(data->dev, "This doesn't use boot value\n");
+       } else {
+               data->boot_qos_timeout = boot_array[0];
+               data->boot_freq = boot_array[1];
+       }
+
+       if (of_property_read_u32(np, "governor", &data->gov_type))
+               return -ENODEV;
+       if (data->gov_type == SIMPLE_INTERACTIVE)
+               data->governor_name = "interactive";
+       else {
+               dev_err(data->dev, "invalid governor name (%s)\n", data->governor_name);
+               return -EINVAL;
+       }
+
+       if (!of_property_read_string(np, "use_acpm", &use_acpm)) {
+               if (!strcmp(use_acpm, "true")) {
+                       data->use_acpm = true;
+               } else {
+                       data->use_acpm = false;
+                       dev_info(data->dev, "This does not use acpm\n");
+               }
+       } else {
+               dev_info(data->dev, "This does not use acpm\n");
+               data->use_acpm = false;
+       }
+
+       if (!of_property_read_string(np, "bts_update", &bts_update)) {
+               if (!strcmp(bts_update, "true")) {
+                       data->bts_update = true;
+               } else {
+                       data->bts_update = false;
+                       dev_info(data->dev, "This does not bts update\n");
+               }
+       } else {
+               dev_info(data->dev, "This does not bts update\n");
+               data->bts_update = false;
+       }
+
+       if (!of_property_read_string(np, "update_fvp", &update_fvp)) {
+               if (!strcmp(update_fvp, "true")) {
+                       data->update_fvp = true;
+               } else {
+                       data->update_fvp = false;
+                       dev_info(data->dev, "This does not update fvp\n");
+               }
+       } else {
+               dev_info(data->dev, "This does not update fvp\n");
+               data->update_fvp = false;
+       }
+
+       if (of_property_read_u32(np, "dfs_id", &data->dfs_id) &&
+                       of_property_match_string(np, "clock-names", buf))
+               return -ENODEV;
+
+       if (data->gov_type == SIMPLE_INTERACTIVE) {
+               if (of_property_read_string(np, "use_delay_time", &use_delay_time))
+                       return -ENODEV;
+
+               if (!strcmp(use_delay_time, "true")) {
+                       data->simple_interactive_data.use_delay_time = true;
+               } else if (!strcmp(use_delay_time, "false")) {
+                       data->simple_interactive_data.use_delay_time = false;
+               } else {
+                       dev_err(data->dev, "invalid use_delay_time : (%s)\n", use_delay_time);
+                       return -EINVAL;
+               }
+
+               if (data->simple_interactive_data.use_delay_time) {
+                       if (of_property_read_string(np, "delay_time_list", &buf)) {
+                               /*
+                                * If there is not delay time list,
+                                * delay time will be filled with default time
+                                */
+                               data->simple_interactive_data.delay_time =
+                                       kmalloc(sizeof(unsigned int), GFP_KERNEL);
+                               if (!data->simple_interactive_data.delay_time) {
+                                       dev_err(data->dev, "Fail to allocate delay_time memory\n");
+                                       return -ENOMEM;
+                               }
+                               *(data->simple_interactive_data.delay_time)
+                                       = DEFAULT_DELAY_TIME;
+                               data->simple_interactive_data.ndelay_time =
+                                       DEFAULT_NDELAY_TIME;
+                               dev_info(data->dev, "set default delay time %d ms\n",
+                                               DEFAULT_DELAY_TIME);
+                       } else {
+                               data->simple_interactive_data.delay_time =
+                                       get_tokenized_data(buf, &ntokens);
+                               data->simple_interactive_data.ndelay_time = ntokens;
+                       }
+               }
+       } else {
+               dev_err(data->dev, "not support governor type %u\n", data->gov_type);
+               return -EINVAL;
+       }
+
+#ifdef CONFIG_EXYNOS_DVFS_MANAGER
+       if (of_property_read_u32(np, "dm-index", &data->dm_type)) {
+               dev_err(data->dev, "not support dvfs manager\n");
+               return -ENODEV;
+       }
+#endif
+       return 0;
+}
+#else
+static int exynos_devfreq_parse_dt(struct device_node *np, struct exynos_devfrq_data *data)
+{
+       return -EINVAL;
+}
+#endif
+
+s32 exynos_devfreq_get_opp_idx(struct exynos_devfreq_opp_table *table, unsigned int size, u32 freq)
+{
+       int i;
+
+       for (i = 0; i < size; ++i) {
+               if (table[i].freq == freq)
+                       return i;
+       }
+
+       return -ENODEV;
+}
+
+static int exynos_init_freq_table(struct exynos_devfreq_data *data)
+{
+       int i, ret;
+       u32 freq, volt;
+
+       for (i = 0; i < data->max_state; i++) {
+               freq = data->opp_list[i].freq;
+               volt = data->opp_list[i].volt;
+
+               data->devfreq_profile.freq_table[i] = freq;
+
+               ret = dev_pm_opp_add(data->dev, freq, volt);
+               if (ret) {
+                       dev_err(data->dev, "failed to add opp entries %uKhz\n", freq);
+                       return ret;
+               } else {
+                       dev_info(data->dev, "DEVFREQ : %8uKhz, %8uuV\n", freq, volt);
+               }
+       }
+
+       ret = exynos_devfreq_init_freq_table(data);
+       if (ret) {
+               dev_err(data->dev, "failed init frequency table\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int exynos_devfreq_reboot_notifier(struct notifier_block *nb, unsigned long val, void *v)
+{
+       struct exynos_devfreq_data *data = container_of(nb, struct exynos_devfreq_data,
+                                                       reboot_notifier);
+
+       if (pm_qos_request_active(&data->default_pm_qos_min))
+               pm_qos_update_request(&data->default_pm_qos_min, data->reboot_freq);
+
+       if (exynos_devfreq_reboot(data)) {
+               dev_err(data->dev, "failed reboot\n");
+               return NOTIFY_BAD;
+       }
+
+       return NOTIFY_OK;
+}
+
+static int exynos_devfreq_target(struct device *dev, unsigned long *target_freq, u32 flags)
+{
+       struct platform_device *pdev = container_of(dev, struct platform_device, dev);
+       struct exynos_devfreq_data *data = platform_get_drvdata(pdev);
+       struct timeval before_target, after_target, before_setfreq, after_setfreq;
+       struct dev_pm_opp *target_opp;
+       u32 target_volt;
+       s32 target_idx;
+       s32 target_time = 0;
+       int ret = 0;
+
+       if (data->devfreq_disabled)
+               return -EAGAIN;
+
+       do_gettimeofday(&before_target);
+
+       mutex_lock(&data->lock);
+
+       target_opp = devfreq_recommended_opp(dev, target_freq, flags);
+       if (IS_ERR(target_opp)) {
+               dev_err(dev, "not found valid OPP table\n");
+               ret = PTR_ERR(target_opp);
+               goto out;
+       }
+
+       *target_freq = dev_pm_opp_get_freq(target_opp);
+       target_volt = (u32)dev_pm_opp_get_voltage(target_opp);
+       dev_pm_opp_put(target_opp);
+
+       target_idx = exynos_devfreq_get_opp_idx(data->opp_list, data->max_state, *target_freq);
+       if (target_idx < 0) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       data->new_freq = (u32)(*target_freq);
+       data->new_idx = target_idx;
+       data->new_volt = target_volt;
+
+       if (data->old_freq == data->new_freq)
+               goto out;
+
+       dev_dbg(dev, "LV_%d, %uKhz, %uuV ======> LV_%d, %uKhz, %uuV\n",
+               data->old_idx, data->old_freq, data->old_volt,
+               data->new_idx, data->new_freq, data->new_volt);
+#ifdef CONFIG_DEBUG_SNAPSHOT_FREQ
+       dbg_snapshot_freq(data->ess_flag, data->old_freq, data->new_freq, DSS_FLAG_IN);
+#endif
+       do_gettimeofday(&before_setfreq);
+
+       ret = exynos_devfreq_set_freq(dev, data->new_freq, data->clk, data);
+       if (ret) {
+               dev_err(dev, "failed set frequency (%uKhz --> %uKhz)\n",
+                       data->old_freq, data->new_freq);
+               goto out;
+       }
+
+       do_gettimeofday(&after_setfreq);
+#ifdef CONFIG_DEBUG_SNAPSHOT_FREQ
+       dbg_snapshot_freq(data->ess_flag, data->old_freq, data->new_freq, DSS_FLAG_OUT);
+#endif
+
+       data->old_freq = data->new_freq;
+       data->old_idx = data->new_idx;
+       data->old_volt = data->new_volt;
+
+out:
+       mutex_unlock(&data->lock);
+
+       do_gettimeofday(&after_target);
+
+       target_time = (after_target.tv_sec - before_target.tv_sec) * USEC_PER_SEC +
+           (after_target.tv_usec - before_target.tv_usec);
+
+       data->target_delay = target_time;
+
+       dev_dbg(dev, "target time: %d usec\n", target_time);
+
+       return ret;
+}
+
+static int exynos_devfreq_suspend(struct device *dev)
+{
+       struct platform_device *pdev = container_of(dev, struct platform_device, dev);
+       struct exynos_devfreq_data *data = platform_get_drvdata(pdev);
+       int ret = 0;
+#ifdef CONFIG_EXYNOS_DVFS_MANAGER
+#ifdef CONFIG_EXYNOS_ACPM
+       int size, ch_num;
+       unsigned int cmd[4];
+       struct ipc_config config;
+#endif
+#endif
+       u32 get_freq = 0;
+
+#ifdef CONFIG_EXYNOS_DVFS_MANAGER
+       if (data->use_acpm) {
+               mutex_lock(&data->devfreq->lock);
+               //send flag
+#ifdef CONFIG_EXYNOS_ACPM
+               ret = acpm_ipc_request_channel(dev->of_node, NULL, &ch_num, &size);
+               if (ret) {
+                       dev_err(dev, "acpm request channel is failed, id:%u, size:%u\n", ch_num, size);
+                       mutex_unlock(&data->devfreq->lock);
+                       return -EINVAL;
+               }
+               /* Initial value of release flag is true.
+                * "true" means state of AP is running
+                * "false means state of AP is sleep.
+                */
+               config.cmd = cmd;
+               config.response = true;
+               config.indirection = false;
+               config.cmd[0] = data->devfreq_type;
+               config.cmd[1] = false;
+               config.cmd[2] = DATA_INIT;
+               config.cmd[3] = RELEASE;
+
+               ret = acpm_ipc_send_data(ch_num, &config);
+               if (ret) {
+                       dev_err(dev, "failed to send release infomation to FVP");
+                       mutex_unlock(&data->devfreq->lock);
+                       return -EINVAL;
+               }
+#endif
+               data->devfreq->str_freq = data->devfreq_profile.suspend_freq;
+               ret = update_devfreq(data->devfreq);
+               if (ret && ret != -EAGAIN) {
+                       dev_err(&data->devfreq->dev, "devfreq failed with (%d) error\n", ret);
+                       mutex_unlock(&data->devfreq->lock);
+                       return NOTIFY_BAD;
+               }
+               mutex_unlock(&data->devfreq->lock);
+       }
+#endif
+       if (!data->use_acpm && pm_qos_request_active(&data->default_pm_qos_min))
+               pm_qos_update_request(&data->default_pm_qos_min,
+                               data->devfreq_profile.suspend_freq);
+       if (exynos_devfreq_get_freq(data->dev, &get_freq, data->clk, data))
+               dev_err(data->dev, "failed get freq\n");
+
+       dev_info(data->dev, "Suspend_frequency is %u\n", get_freq);
+
+       return ret;
+}
+
+static int exynos_devfreq_resume(struct device *dev)
+{
+       struct platform_device *pdev = container_of(dev, struct platform_device, dev);
+       struct exynos_devfreq_data *data = platform_get_drvdata(pdev);
+#ifdef CONFIG_EXYNOS_DVFS_MANAGER
+#ifdef CONFIG_EXYNOS_ACPM
+       int size, ch_num;
+       unsigned int cmd[4];
+       struct ipc_config config;
+#endif
+#endif
+       int ret = 0;
+       u32 cur_freq;
+
+       if (!exynos_devfreq_get_freq(data->dev, &cur_freq, data->clk, data))
+               dev_info(data->dev, "Resume frequency is %u\n", cur_freq);
+#ifdef CONFIG_EXYNOS_DVFS_MANAGER
+       if (data->use_acpm) {
+               mutex_lock(&data->devfreq->lock);
+               //send flag
+#ifdef CONFIG_EXYNOS_ACPM
+               ret = acpm_ipc_request_channel(dev->of_node, NULL, &ch_num, &size);
+               if (ret) {
+                       dev_err(dev, "acpm request channel is failed, id:%u, size:%u\n", ch_num, size);
+                       mutex_unlock(&data->devfreq->lock);
+                       return -EINVAL;
+               }
+
+               config.cmd = cmd;
+               config.response = true;
+               config.indirection = false;
+               config.cmd[0] = data->devfreq_type;
+               config.cmd[1] = true;
+               config.cmd[2] = DATA_INIT;
+               config.cmd[3] = RELEASE;
+
+               ret = acpm_ipc_send_data(ch_num, &config);
+               if (ret) {
+                       dev_err(dev, "failed to send release infomation to FVP");
+                       mutex_unlock(&data->devfreq->lock);
+                       return -EINVAL;
+               }
+#endif
+               data->devfreq->str_freq = 0;
+               ret = update_devfreq(data->devfreq);
+               if (ret && ret != -EAGAIN) {
+                       dev_err(&data->devfreq->dev, "devfreq failed with (%d) error\n", ret);
+                       mutex_unlock(&data->devfreq->lock);
+                       return NOTIFY_BAD;
+               }
+               mutex_unlock(&data->devfreq->lock);
+       }
+#endif
+       if (!data->use_acpm && pm_qos_request_active(&data->default_pm_qos_min))
+               pm_qos_update_request(&data->default_pm_qos_min, data->default_qos);
+
+       return ret;
+}
+
+static int exynos_devfreq_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+       struct exynos_devfreq_data *data;
+       struct dev_pm_opp *init_opp;
+       unsigned long init_freq = 0;
+#ifdef CONFIG_EXYNOS_DVFS_MANAGER
+       int nr_constraint;
+#endif
+       data = kzalloc(sizeof(struct exynos_devfreq_data), GFP_KERNEL);
+       if (data == NULL) {
+               dev_err(&pdev->dev, "failed to allocate devfreq data\n");
+               ret = -ENOMEM;
+               goto err_data;
+       }
+
+       data->dev = &pdev->dev;
+
+       mutex_init(&data->lock);
+
+       /* parsing devfreq dts data for exynos */
+       ret = exynos_devfreq_parse_dt(data->dev->of_node, data);
+       if (ret) {
+               dev_err(data->dev, "failed to parse private data\n");
+               goto err_parse_dt;
+       }
+
+       data->devfreq_profile.max_state = data->max_state;
+       data->devfreq_profile.target = exynos_devfreq_target;
+       if (data->gov_type == SIMPLE_INTERACTIVE) {
+               data->simple_interactive_data.pm_qos_class = data->pm_qos_class;
+               data->simple_interactive_data.pm_qos_class_max = data->pm_qos_class_max;
+               data->governor_data = &data->simple_interactive_data;
+       }
+
+       data->devfreq_profile.freq_table = kzalloc(sizeof(*(data->devfreq_profile.freq_table)) * data->max_state, GFP_KERNEL);
+       if (data->devfreq_profile.freq_table == NULL) {
+               dev_err(data->dev, "failed to allocate for freq_table\n");
+               ret = -ENOMEM;
+               goto err_freqtable;
+       }
+
+       ret = exynos_init_freq_table(data);
+       if (ret) {
+               dev_err(data->dev, "failed initailize freq_table\n");
+               goto err_init_table;
+       }
+
+       devfreq_data[data->devfreq_type] = data;
+       platform_set_drvdata(pdev, data);
+
+       data->old_freq = (u32)data->devfreq_profile.initial_freq;
+       data->old_idx = exynos_devfreq_get_opp_idx(data->opp_list, data->max_state, data->old_freq);
+       if (data->old_idx < 0) {
+               ret = -EINVAL;
+               goto err_old_idx;
+       }
+
+       init_freq = (unsigned long)data->old_freq;
+       init_opp = devfreq_recommended_opp(data->dev, &init_freq, 0);
+       if (IS_ERR(init_opp)) {
+               dev_err(data->dev, "not found valid OPP table for sync\n");
+               ret = PTR_ERR(init_opp);
+               goto err_get_opp;
+       }
+       data->new_volt = (u32)dev_pm_opp_get_voltage(init_opp);
+       dev_pm_opp_put(init_opp);
+
+       dev_info(data->dev, "Initial Frequency: %ld, Initial Voltage: %d\n", init_freq,
+                data->new_volt);
+
+       data->old_volt = data->new_volt;
+#ifdef CONFIG_EXYNOS_DVFS_MANAGER
+       ret = exynos_dm_data_init(data->dm_type, data, data->min_freq, data->max_freq, data->old_freq);
+       if (ret) {
+               dev_err(data->dev, "failed DVFS Manager data init\n");
+               goto err_dm_data_init;
+       }
+
+       for (nr_constraint = 0; nr_constraint < data->nr_constraint; nr_constraint++) {
+               if(data->constraint[nr_constraint]) {
+                       ret = register_exynos_dm_constraint_table(data->dm_type,
+                               data->constraint[nr_constraint]);
+                       if (ret) {
+                               dev_err(data->dev,"failed registration constraint table(%d)\n",
+                                       nr_constraint);
+                               goto err_dm_table;
+                       }
+               }
+       }
+#endif
+       /* This flag guarantees initial frequency during boot time */
+       data->devfreq_disabled = true;
+
+       data->devfreq = devfreq_add_device(data->dev, &data->devfreq_profile,
+                                          data->governor_name, data->governor_data);
+       if (IS_ERR(data->devfreq)) {
+               dev_err(data->dev, "failed devfreq device added\n");
+               ret = -EINVAL;
+               goto err_devfreq;
+       }
+
+       data->devfreq->min_freq = data->min_freq;
+       data->devfreq->max_freq = data->max_freq;
+
+       pm_qos_add_request(&data->sys_pm_qos_min, (int)data->pm_qos_class, data->min_freq);
+#ifdef CONFIG_ARM_EXYNOS_DEVFREQ_DEBUG
+       pm_qos_add_request(&data->debug_pm_qos_min, (int)data->pm_qos_class, data->min_freq);
+       pm_qos_add_request(&data->debug_pm_qos_max, (int)data->pm_qos_class_max, data->max_freq);
+#endif
+       if (data->pm_qos_class_max)
+               pm_qos_add_request(&data->default_pm_qos_max, (int)data->pm_qos_class_max,
+                                  data->max_freq);
+       pm_qos_add_request(&data->default_pm_qos_min, (int)data->pm_qos_class, data->default_qos);
+       pm_qos_add_request(&data->boot_pm_qos, (int)data->pm_qos_class,
+                          data->devfreq_profile.initial_freq);
+
+       ret = devfreq_register_opp_notifier(data->dev, data->devfreq);
+       if (ret) {
+               dev_err(data->dev, "failed register opp notifier\n");
+               goto err_opp_noti;
+       }
+
+       data->reboot_notifier.notifier_call = exynos_devfreq_reboot_notifier;
+       ret = register_reboot_notifier(&data->reboot_notifier);
+       if (ret) {
+               dev_err(data->dev, "failed register reboot notifier\n");
+               goto err_reboot_noti;
+       }
+
+       ret = sysfs_create_file(&data->devfreq->dev.kobj, &dev_attr_scaling_devfreq_min.attr);
+       if (ret)
+               dev_warn(data->dev, "failed create sysfs for devfreq pm_qos_min\n");
+
+#ifdef CONFIG_ARM_EXYNOS_DEVFREQ_DEBUG
+       ret = sysfs_create_group(&data->devfreq->dev.kobj, &exynos_devfreq_attr_group);
+       if (ret)
+               dev_warn(data->dev, "failed create sysfs for devfreq data\n");
+#endif
+       ret = sysfs_create_group(&data->devfreq->dev.kobj, &devfreq_delay_time_attr_group);
+       if (ret)
+               dev_warn(data->dev, "failed create sysfs for devfreq data\n");
+
+       data->devfreq_disabled = false;
+
+       if (!data->pm_domain) {
+               /* set booting frequency during booting time */
+               pm_qos_update_request_timeout(&data->boot_pm_qos, data->boot_freq,
+                                       data->boot_qos_timeout * USEC_PER_SEC);
+       } else {
+               pm_runtime_enable(&pdev->dev);
+               pm_runtime_get_sync(&pdev->dev);
+               pm_qos_update_request(&data->boot_pm_qos, data->default_qos);
+               pm_runtime_put_sync(&pdev->dev);
+       }
+
+       dev_info(data->dev, "devfreq is initialized!!\n");
+
+       return 0;
+
+err_reboot_noti:
+       devfreq_unregister_opp_notifier(data->dev, data->devfreq);
+err_opp_noti:
+       pm_qos_remove_request(&data->boot_pm_qos);
+       pm_qos_remove_request(&data->default_pm_qos_min);
+       if (data->pm_qos_class_max)
+               pm_qos_remove_request(&data->default_pm_qos_max);
+#ifdef CONFIG_ARM_EXYNOS_DEVFREQ_DEBUG
+       pm_qos_remove_request(&data->debug_pm_qos_min);
+       pm_qos_remove_request(&data->debug_pm_qos_max);
+#endif
+       pm_qos_remove_request(&data->sys_pm_qos_min);
+       devfreq_remove_device(data->devfreq);
+err_devfreq:
+#ifdef CONFIG_EXYNOS_DVFS_MANAGER
+       for (; nr_constraint >= 0; nr_constraint--) {
+               if (data->constraint[nr_constraint])
+                       unregister_exynos_dm_constraint_table(data->dm_type,
+                               data->constraint[nr_constraint]);
+       }
+err_dm_table:
+err_dm_data_init:
+#endif
+err_get_opp:
+err_old_idx:
+       platform_set_drvdata(pdev, NULL);
+err_init_table:
+       kfree(data->devfreq_profile.freq_table);
+err_freqtable:
+err_parse_dt:
+       mutex_destroy(&data->lock);
+       kfree(data);
+err_data:
+
+       return ret;
+}
+
+static int exynos_devfreq_remove(struct platform_device *pdev)
+{
+       struct exynos_devfreq_data *data = platform_get_drvdata(pdev);
+#ifdef CONFIG_EXYNOS_DVFS_MANAGER
+       int nr_constraint;
+#endif
+       sysfs_remove_file(&data->devfreq->dev.kobj, &dev_attr_scaling_devfreq_min.attr);
+#ifdef CONFIG_ARM_EXYNOS_DEVFREQ_DEBUG
+       sysfs_remove_group(&data->devfreq->dev.kobj, &exynos_devfreq_attr_group);
+#endif
+       sysfs_remove_group(&data->devfreq->dev.kobj, &devfreq_delay_time_attr_group);
+
+       unregister_reboot_notifier(&data->reboot_notifier);
+       devfreq_unregister_opp_notifier(data->dev, data->devfreq);
+
+       pm_qos_remove_request(&data->boot_pm_qos);
+       pm_qos_remove_request(&data->default_pm_qos_min);
+       if (data->pm_qos_class_max)
+               pm_qos_remove_request(&data->default_pm_qos_max);
+#ifdef CONFIG_ARM_EXYNOS_DEVFREQ_DEBUG
+       pm_qos_remove_request(&data->debug_pm_qos_min);
+       pm_qos_remove_request(&data->debug_pm_qos_max);
+#endif
+       pm_qos_remove_request(&data->sys_pm_qos_min);
+       devfreq_remove_device(data->devfreq);
+#ifdef CONFIG_EXYNOS_DVFS_MANAGER
+       for (nr_constraint = 0; nr_constraint < data->nr_constraint; nr_constraint++) {
+               if (data->constraint[nr_constraint])
+                       unregister_exynos_dm_constraint_table(data->dm_type,
+                               data->constraint[nr_constraint]);
+       }
+#endif
+       platform_set_drvdata(pdev, NULL);
+       kfree(data->devfreq_profile.freq_table);
+       mutex_destroy(&data->lock);
+       kfree(data);
+
+       return 0;
+}
+
+static struct platform_device_id exynos_devfreq_driver_ids[] = {
+       {
+        .name = EXYNOS_DEVFREQ_MODULE_NAME,
+        },
+       {},
+};
+
+MODULE_DEVICE_TABLE(platform, exynos_devfreq_driver_ids);
+
+static const struct of_device_id exynos_devfreq_match[] = {
+       {
+        .compatible = "samsung,exynos-devfreq",
+        },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, exynos_devfreq_match);
+
+static const struct dev_pm_ops exynos_devfreq_pm_ops = {
+       .suspend_late = exynos_devfreq_suspend,
+       .resume_early = exynos_devfreq_resume,
+};
+
+static struct platform_driver exynos_devfreq_driver = {
+       .probe = exynos_devfreq_probe,
+       .remove = exynos_devfreq_remove,
+       .id_table = exynos_devfreq_driver_ids,
+       .driver = {
+               .name = EXYNOS_DEVFREQ_MODULE_NAME,
+               .owner = THIS_MODULE,
+               .pm = &exynos_devfreq_pm_ops,
+               .of_match_table = exynos_devfreq_match,
+       },
+};
+
+static int exynos_devfreq_root_probe(struct platform_device *pdev)
+{
+       struct device_node *np;
+       int num_domains;
+
+       np = pdev->dev.of_node;
+
+       platform_driver_register(&exynos_devfreq_driver);
+
+       /* alloc memory for devfreq data structure */
+       num_domains = of_get_child_count(np);
+       devfreq_data = (struct exynos_devfreq_data **)kzalloc(sizeof(struct exynos_devfreq_data *)
+                       * num_domains, GFP_KERNEL);
+
+       /* probe each devfreq node */
+       of_platform_populate(np, NULL, NULL, NULL);
+
+       return 0;
+}
+
+static const struct of_device_id exynos_devfreq_root_match[] = {
+       {
+               .compatible = "samsung,exynos-devfreq-root",
+       },
+       {},
+       };
+
+static struct platform_driver exynos_devfreq_root_driver = {
+       .probe = exynos_devfreq_root_probe,
+       .driver = {
+               .name = "exynos-devfreq-root",
+               .owner = THIS_MODULE,
+               .of_match_table = exynos_devfreq_root_match,
+       },
+};
+
+module_platform_driver(exynos_devfreq_root_driver);
+MODULE_AUTHOR("Taekki Kim <taekki.kim@samsung.com>");
+MODULE_DESCRIPTION("Samsung EXYNOS Soc series devfreq common driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/devfreq/exynos/exynos_ppmu.c b/drivers/devfreq/exynos/exynos_ppmu.c
new file mode 100644 (file)
index 0000000..551373c
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com/
+ *
+ * EXYNOS - PPMU support
+ *
+ * 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/kernel.h>
+#include <linux/types.h>
+#include <linux/io.h>
+
+#include "exynos_ppmu.h"
+
+#define PPMU_PMNC                               0x4
+#define PPMU_CNTENS                             0x8
+#define PPMU_CNT_AUTO                           0x30
+#define PPMU_PMCNT0_HIGH                       0x004C
+#define PPMU_PMCNT1_HIGH                       0x0050
+#define PPMU_PMCNT2_HIGH                       0x0054
+#define PPMU_PMCNT3_HIGH                       0x0044
+#define PPMU_CCNT_HIGH                         0x0058
+#define PPMU_PMCNT0_LOW                                0x0034
+#define PPMU_PMCNT1_LOW                                0x0038
+#define PPMU_PMCNT2_LOW                                0x003C
+#define PPMU_PMCNT3_LOW                                0x0040
+#define PPMU_CCNT_LOW                          0x0048
+#define PPMU_CH_EV0_TYPE                        0x200
+#define PPMU_CH_EV1_TYPE                        0x204
+#define PPMU_CH_EV2_TYPE                        0x208
+#define PPMU_CH_EV3_TYPE                        0x20c
+#define PPMU_SM_ID_V                            0x220
+#define PPMU_SM_ID_A                            0x224
+
+#define EVENT_RD_ACTIVATED                     0x0
+#define EVENT_WR_ACTIVATED                     0x1
+#define EVENT_RD_DATA                          0x4
+#define EVENT3_RD_DATA                         0x4
+#define EVENT3_WR_DATA                         0x5
+#define EVENT3_RW_DATA                         0x22
+
+
+/* 0x1: disable Q channel */
+/* auto mode */
+#define BIT_REGVALUE           ((0x1<<24) | (0x1<<20))
+#define BIT_CH_CCNT            (0x1<<31)
+#define BIT_CH_PMCNT0          (0x1<<0)
+#define BIT_CH_PMCNT1          (0x1<<1)
+#define BIT_CH_PMCNT2          (0x1<<2)
+#define BIT_CH_PMCNT3          (0x1<<3)
+#define BIT_CH_ALL             (BIT_CH_CCNT | BIT_CH_PMCNT0 | \
+                                BIT_CH_PMCNT1 | BIT_CH_PMCNT2 | \
+                                BIT_CH_PMCNT3)
+
+void exynos_read_ppmu(struct ppmu_data *ppmu, void __iomem *ppmu_base,
+                     u32 channel)
+{
+       if (!channel)
+               channel = BIT_CH_ALL;
+       if (channel & BIT_CH_CCNT)
+               ppmu->ccnt = __raw_readl(ppmu_base + PPMU_CCNT_LOW);
+       if (channel & BIT_CH_PMCNT0)
+               ppmu->pmcnt0 = __raw_readl(ppmu_base + PPMU_PMCNT0_LOW);
+       if (channel & BIT_CH_PMCNT1)
+               ppmu->pmcnt1 = __raw_readl(ppmu_base + PPMU_PMCNT1_LOW);
+       if (channel & BIT_CH_PMCNT2)
+               ppmu->pmcnt2 = __raw_readl(ppmu_base + PPMU_PMCNT2_LOW);
+       if (channel & BIT_CH_PMCNT3)
+               ppmu->pmcnt3 = __raw_readl(ppmu_base + PPMU_PMCNT3_LOW);
+}
+
+void exynos_init_ppmu(void __iomem *ppmu_base, u32 mask_v, u32 mask_a)
+{
+
+       __raw_writel(BIT_REGVALUE | 0x6, ppmu_base + PPMU_PMNC);
+       /* Count Enable CCNT, PMCNTTx */
+       __raw_writel(BIT_CH_ALL, ppmu_base + PPMU_CNTENS);
+       __raw_writel(EVENT_RD_ACTIVATED, ppmu_base + PPMU_CH_EV0_TYPE);
+       __raw_writel(EVENT_WR_ACTIVATED, ppmu_base + PPMU_CH_EV1_TYPE);
+       __raw_writel(EVENT_RD_DATA, ppmu_base + PPMU_CH_EV2_TYPE);
+       __raw_writel(EVENT3_WR_DATA, ppmu_base + PPMU_CH_EV3_TYPE);
+       if (mask_v) {
+               __raw_writel(mask_v, ppmu_base + PPMU_SM_ID_V);
+               __raw_writel(mask_a, ppmu_base + PPMU_SM_ID_A);
+       }
+}
+
+void exynos_reset_ppmu(void __iomem *ppmu_base, u32 channel)
+{
+       if (!channel)
+               channel = BIT_CH_ALL;
+       __raw_writel(channel, ppmu_base + PPMU_CNT_AUTO);
+}
+
+void exynos_start_ppmu(void __iomem *ppmu_base)
+{
+       __raw_writel(BIT_REGVALUE | 0x1, ppmu_base + PPMU_PMNC);
+}
+
+void exynos_stop_ppmu(void __iomem *ppmu_base)
+{
+       __raw_writel(BIT_REGVALUE, ppmu_base + PPMU_PMNC);
+}
+
+void exynos_exit_ppmu(void __iomem *ppmu_base)
+{
+       __raw_writel(BIT_REGVALUE, ppmu_base + PPMU_PMNC);
+}
diff --git a/drivers/devfreq/exynos/exynos_ppmu.h b/drivers/devfreq/exynos/exynos_ppmu.h
new file mode 100644 (file)
index 0000000..92550ef
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com/
+ *
+ * EXYNOS PPMU header
+ *
+ * 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.
+*/
+
+#ifndef __DEVFREQ_EXYNOS_PPMU_H
+#define __DEVFREQ_EXYNOS_PPMU_H __FILE__
+
+#include <linux/ktime.h>
+
+struct ppmu_data {
+       u64 ccnt;
+       u64 pmcnt0;
+       u64 pmcnt1;
+       u64 pmcnt2;
+       u64 pmcnt3;
+};
+
+#define exynos_read_ppmu(a, ...) do {} while(0)
+#define exynos_init_ppmu(a, ...) do {} while(0)
+#define exynos_exit_ppmu(a, ...) do {} while(0)
+#define exynos_reset_ppmu(a, ...) do {} while(0)
+#define exynos_start_ppmu(a, ...) do {} while(0)
+#define exynos_stop_ppmu(a, ...) do {} while(0)
+
+#endif /* __DEVFREQ_EXYNOS_PPMU_H */
diff --git a/drivers/devfreq/governor_simpleexynos.c b/drivers/devfreq/governor_simpleexynos.c
new file mode 100644 (file)
index 0000000..9a03f88
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ *  linux/drivers/devfreq/governor_simpleexynos.c
+ *
+ *  Copyright (C) 2011 Samsung Electronics
+ *     MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * 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/pm_qos.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "governor.h"
+
+static int gov_simple_exynos = 0;
+
+static int devfreq_simple_exynos_notifier(struct notifier_block *nb, unsigned long val,
+                                               void *v)
+{
+       struct devfreq_notifier_block *devfreq_nb;
+
+       devfreq_nb = container_of(nb, struct devfreq_notifier_block, nb);
+
+       mutex_lock(&devfreq_nb->df->lock);
+       update_devfreq(devfreq_nb->df);
+       mutex_unlock(&devfreq_nb->df->lock);
+
+       return NOTIFY_OK;
+}
+
+/* Default constants for DevFreq-Simple-Exynos(DFE) */
+#define DFE_URGENTTHRESHOLD    (65)
+#define DFE_UPTHRESHOLD                (60)
+#define DFE_DOWNTHRESHOLD      (45)
+#define DFE_IDLETHRESHOLD      (30)
+
+static int devfreq_simple_exynos_func(struct devfreq *df,
+                                       unsigned long *freq)
+{
+       struct devfreq_dev_status stat;
+       unsigned int dfe_urgentthreshold = DFE_URGENTTHRESHOLD;
+       unsigned int dfe_upthreshold = DFE_UPTHRESHOLD;
+       unsigned int dfe_downthreshold = DFE_DOWNTHRESHOLD;
+       unsigned int dfe_idlethreshold = DFE_IDLETHRESHOLD;
+       struct devfreq_simple_exynos_data *data = df->data;
+       unsigned long max = (df->max_freq) ? df->max_freq : UINT_MAX;
+       unsigned long pm_qos_min = 0, cal_qos_max = 0;
+       unsigned long pm_qos_max = 0;
+       unsigned long usage_rate;
+       int err;
+
+       if (data) {
+               if (!df->disabled_pm_qos) {
+                       pm_qos_min = pm_qos_request(data->pm_qos_class);
+                       if (data->pm_qos_class_max)
+                               pm_qos_max = pm_qos_request(data->pm_qos_class_max);
+                       if (unlikely(gov_simple_exynos))
+                               printk("pm_qos: %lu\n", pm_qos_min);
+               }
+
+               if (data->urgentthreshold)
+                       dfe_urgentthreshold = data->urgentthreshold;
+               if (data->upthreshold)
+                       dfe_upthreshold = data->upthreshold;
+               if (data->downthreshold)
+                       dfe_downthreshold = data->downthreshold;
+               if (data->idlethreshold)
+                       dfe_idlethreshold = data->idlethreshold;
+
+               if (data->cal_qos_max) {
+                       cal_qos_max = data->cal_qos_max;
+                       max = (df->max_freq) ? df->max_freq : 0;
+               } else {
+                       cal_qos_max = df->max_freq;
+               }
+       }
+
+       if (df->profile->get_dev_status) {
+               err = df->profile->get_dev_status(df->dev.parent, &stat);
+               if (err)
+                       return err;
+       } else {
+               *freq = pm_qos_min;
+               if (pm_qos_max)
+                       *freq = min(pm_qos_max, *freq);
+               return 0;
+       }
+
+       /* Assume MAX if it is going to be divided by zero */
+       if (stat.total_time == 0) {
+               *freq = max3(max, cal_qos_max, pm_qos_min);
+               if (pm_qos_max)
+                       *freq = min(pm_qos_max, *freq);
+               return 0;
+       }
+
+       /* Set MAX if we do not know the initial frequency */
+       if (stat.current_frequency == 0) {
+               *freq = max3(max, cal_qos_max, pm_qos_min);
+               if (pm_qos_max)
+                       *freq = min(pm_qos_max, *freq);
+               return 0;
+       }
+
+       usage_rate = div64_u64(stat.busy_time * 100, stat.total_time);
+
+       /* Set MAX if it's busy enough */
+       if (usage_rate > dfe_urgentthreshold)
+               *freq = max3(max, cal_qos_max, pm_qos_min);
+       else if (usage_rate >= dfe_upthreshold) {
+               if (data)
+                       *freq = data->above_freq;
+       } else if (usage_rate > dfe_downthreshold)
+               *freq = stat.current_frequency;
+       else if (usage_rate > dfe_idlethreshold) {
+               if (data)
+                       *freq = data->below_freq;
+       } else
+               *freq = 0;
+
+       if (*freq > cal_qos_max)
+               *freq = cal_qos_max;
+
+       if (pm_qos_min)
+               *freq = max(pm_qos_min, *freq);
+
+       if (pm_qos_max)
+               *freq = min(pm_qos_max, *freq);
+
+       if (unlikely(gov_simple_exynos))
+               printk("Usage: %lu, freq: %lu, old: %lu\n", usage_rate, *freq, stat.current_frequency);
+
+       return 0;
+}
+
+static int devfreq_simple_exynos_register_notifier(struct devfreq *df)
+{
+       int ret;
+       struct devfreq_simple_exynos_data *data = df->data;
+
+       if (!data)
+               return -EINVAL;
+
+       data->nb.df = df;
+       data->nb.nb.notifier_call = devfreq_simple_exynos_notifier;
+
+       ret = pm_qos_add_notifier(data->pm_qos_class, &data->nb.nb);
+       if (ret < 0)
+               goto err1;
+
+       if (data->pm_qos_class_max) {
+               data->nb_max.df = df;
+               data->nb_max.nb.notifier_call = devfreq_simple_exynos_notifier;
+
+               ret = pm_qos_add_notifier(data->pm_qos_class_max, &data->nb_max.nb);
+               if (ret < 0) {
+                       pm_qos_remove_notifier(data->pm_qos_class, &data->nb.nb);
+                       goto err2;
+               }
+       }
+
+       return 0;
+err2:
+       kfree((void *)&data->nb_max.nb);
+
+err1:
+       kfree((void *)&data->nb.nb);
+
+       return ret;
+}
+
+static int devfreq_simple_exynos_unregister_notifier(struct devfreq *df)
+{
+       int ret;
+       struct devfreq_simple_exynos_data *data = df->data;
+
+       if (!data)
+               return -EINVAL;
+
+       if (data->pm_qos_class_max) {
+               ret = pm_qos_remove_notifier(data->pm_qos_class_max, &data->nb_max.nb);
+               if (ret < 0)
+                       goto err;
+       }
+
+       ret = pm_qos_remove_notifier(data->pm_qos_class, &data->nb.nb);
+
+err:
+       return ret;
+}
+
+static int devfreq_simple_exynos_handler(struct devfreq *devfreq,
+                               unsigned int event, void *data)
+{
+       int ret;
+
+       switch (event) {
+       case DEVFREQ_GOV_START:
+               ret = devfreq_simple_exynos_register_notifier(devfreq);
+               if (ret)
+                       return ret;
+               devfreq_monitor_start(devfreq);
+               break;
+
+       case DEVFREQ_GOV_STOP:
+               devfreq_monitor_stop(devfreq);
+               ret = devfreq_simple_exynos_unregister_notifier(devfreq);
+               if (ret)
+                       return ret;
+               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 0;
+}
+
+static struct devfreq_governor devfreq_simple_exynos = {
+       .name = "simple_exynos",
+       .get_target_freq = devfreq_simple_exynos_func,
+       .event_handler = devfreq_simple_exynos_handler,
+};
+
+static int __init devfreq_simple_exynos_init(void)
+{
+       return devfreq_add_governor(&devfreq_simple_exynos);
+}
+subsys_initcall(devfreq_simple_exynos_init);
+
+static void __exit devfreq_simple_exynos_exit(void)
+{
+       int ret;
+
+       ret = devfreq_remove_governor(&devfreq_simple_exynos);
+       if (ret)
+               pr_err("%s: failed remove governor %d\n", __func__, ret);
+
+       return;
+}
+module_exit(devfreq_simple_exynos_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/devfreq/governor_simpleinteractive.c b/drivers/devfreq/governor_simpleinteractive.c
new file mode 100644 (file)
index 0000000..211d21b
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * 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/pm_qos.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <linux/time.h>
+#include <linux/timer.h>
+#include <linux/kthread.h>
+#include <linux/pm_opp.h>
+
+#include "governor.h"
+
+static int devfreq_simple_interactive_notifier(struct notifier_block *nb, unsigned long val,
+                                               void *v)
+{
+       struct devfreq_notifier_block *devfreq_nb;
+
+       devfreq_nb = container_of(nb, struct devfreq_notifier_block, nb);
+
+       mutex_lock(&devfreq_nb->df->lock);
+       update_devfreq(devfreq_nb->df);
+       mutex_unlock(&devfreq_nb->df->lock);
+
+       return NOTIFY_OK;
+}
+
+static int devfreq_simple_interactive_func(struct devfreq *df,
+                                       unsigned long *freq)
+{
+       struct devfreq_simple_interactive_data *data = df->data;
+       unsigned long pm_qos_min = 0;
+       unsigned long pm_qos_max = INT_MAX;
+       int delay_check = 0;
+       int delay_time = 0;
+       int i = 0;
+       struct dev_pm_opp *limit_opp;
+
+       if (!data) {
+               pr_err("%s: failed to find governor data\n", __func__);
+               return -ENODATA;
+       }
+
+       if (!df->disabled_pm_qos) {
+               pm_qos_min = pm_qos_request(data->pm_qos_class);
+               if (data->pm_qos_class_max) {
+                       pm_qos_max = pm_qos_request(data->pm_qos_class_max);
+                       limit_opp = devfreq_recommended_opp(df->dev.parent, &pm_qos_max,
+                                       DEVFREQ_FLAG_LEAST_UPPER_BOUND);
+                       if (IS_ERR(limit_opp)) {
+                               pr_err("%s: failed to limit by max frequency\n", __func__);
+                               return PTR_ERR(limit_opp);
+                       }
+                       dev_pm_opp_put(limit_opp);
+               }
+       }
+
+       *freq = pm_qos_min;
+
+       if (!data->use_delay_time)
+               goto out;
+
+       if (data->prev_freq != df->previous_freq) {
+               for (i = 0; i < data->ndelay_time - 1 &&
+                               *freq >= data->delay_time[i + 1]; i += 2)
+                       ;
+
+               /* unit of delay time should be 10msec */
+               delay_check = data->delay_time[i] % DELAY_TIME_RANGE;
+               delay_time = delay_check ?
+                               data->delay_time[i] - delay_check + DELAY_TIME_RANGE :
+                               data->delay_time[i];
+
+               data->freq_timer.expires = data->changed_time +
+                       msecs_to_jiffies(delay_time);
+       }
+
+       if (pm_qos_max > df->previous_freq && *freq < df->previous_freq &&
+                       data->freq_timer.expires > jiffies) {
+               *freq = df->previous_freq;
+               if (!timer_pending(&data->freq_timer))
+                       /* timer is bound to cpu0 */
+                       add_timer_on(&data->freq_timer, BOUND_CPU_NUM);
+
+               goto out;
+       } else if (timer_pending(&data->freq_timer)) {
+               del_timer_sync(&data->freq_timer);
+       }
+
+       data->changed_time = jiffies;
+
+out:
+       /*
+        * save current frequency and time
+        * to use when update_devfreq is called next
+        */
+       data->prev_freq = df->previous_freq;
+       *freq = min(pm_qos_max, *freq);
+
+       return 0;
+}
+
+static int devfreq_change_freq_task(void *data)
+{
+       struct devfreq *df = data;
+
+       while (!kthread_should_stop()) {
+               set_current_state(TASK_INTERRUPTIBLE);
+
+               schedule();
+
+               set_current_state(TASK_RUNNING);
+
+               mutex_lock(&df->lock);
+               update_devfreq(df);
+               mutex_unlock(&df->lock);
+       }
+
+       return 0;
+}
+
+/*timer callback function send a signal */
+static void simple_interactive_timer(unsigned long data)
+{
+       struct devfreq_simple_interactive_data *gov_data = (void *)data;
+
+       wake_up_process(gov_data->change_freq_task);
+}
+
+static int devfreq_simple_interactive_register_notifier(struct devfreq *df)
+{
+       int ret;
+       struct devfreq_simple_interactive_data *data = df->data;
+
+       if (!data)
+               return -EINVAL;
+
+       data->nb.df = df;
+       data->nb.nb.notifier_call = devfreq_simple_interactive_notifier;
+
+       ret = pm_qos_add_notifier(data->pm_qos_class, &data->nb.nb);
+       if (ret < 0)
+               goto err1;
+
+       if (data->pm_qos_class_max) {
+               data->nb_max.df = df;
+               data->nb_max.nb.notifier_call = devfreq_simple_interactive_notifier;
+
+               ret = pm_qos_add_notifier(data->pm_qos_class_max, &data->nb_max.nb);
+               if (ret < 0) {
+                       pm_qos_remove_notifier(data->pm_qos_class, &data->nb.nb);
+                       goto err2;
+               }
+       }
+
+       /* timer of governor for delay time initialize */
+       data->freq_timer.data = (unsigned long)data;
+       data->freq_timer.function = simple_interactive_timer;
+       init_timer(&data->freq_timer);
+
+       data->change_freq_task = kthread_create(devfreq_change_freq_task, df, "simpleinteractive");
+
+       if (IS_ERR(data->change_freq_task)) {
+               pr_err("%s: failed kthread_create for simpleinteractive governor\n", __func__);
+               ret = PTR_ERR(data->change_freq_task);
+
+               destroy_timer_on_stack(&data->freq_timer);
+               pm_qos_remove_notifier(data->pm_qos_class, &data->nb.nb);
+               if (data->pm_qos_class_max)
+                       pm_qos_remove_notifier(data->pm_qos_class_max, &data->nb_max.nb);
+
+               goto err2;
+       }
+
+       kthread_bind(data->change_freq_task, BOUND_CPU_NUM);
+
+       return 0;
+
+err2:
+       kfree((void *)&data->nb_max.nb);
+
+err1:
+       kfree((void *)&data->nb.nb);
+
+       return ret;
+}
+
+static int devfreq_simple_interactive_unregister_notifier(struct devfreq *df)
+{
+       int ret;
+       struct devfreq_simple_interactive_data *data = df->data;
+
+       if (!data)
+               return -EINVAL;
+
+       if (data->pm_qos_class_max) {
+               ret = pm_qos_remove_notifier(data->pm_qos_class_max, &data->nb_max.nb);
+               if (ret < 0)
+                       goto err;
+       }
+
+       ret = pm_qos_remove_notifier(data->pm_qos_class, &data->nb.nb);
+
+       destroy_timer_on_stack(&data->freq_timer);
+       kthread_stop(data->change_freq_task);
+
+err:
+       return ret;
+}
+
+static int devfreq_simple_interactive_handler(struct devfreq *devfreq,
+                               unsigned int event, void *data)
+{
+       int ret;
+
+       switch (event) {
+       case DEVFREQ_GOV_START:
+               ret = devfreq_simple_interactive_register_notifier(devfreq);
+               if (ret)
+                       return ret;
+               break;
+
+       case DEVFREQ_GOV_STOP:
+               ret = devfreq_simple_interactive_unregister_notifier(devfreq);
+               if (ret)
+                       return ret;
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static struct devfreq_governor devfreq_simple_interactive = {
+       .name = "interactive",
+       .get_target_freq = devfreq_simple_interactive_func,
+       .event_handler = devfreq_simple_interactive_handler,
+};
+
+static int __init devfreq_simple_interactive_init(void)
+{
+       return devfreq_add_governor(&devfreq_simple_interactive);
+}
+subsys_initcall(devfreq_simple_interactive_init);
+
+static void __exit devfreq_simple_interactive_exit(void)
+{
+       int ret;
+
+       ret = devfreq_remove_governor(&devfreq_simple_interactive);
+       if (ret)
+               pr_err("%s: failed remove governor %d\n", __func__, ret);
+
+       return;
+}
+module_exit(devfreq_simple_interactive_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/devfreq/governor_simpleusage.c b/drivers/devfreq/governor_simpleusage.c
new file mode 100644 (file)
index 0000000..e180cbe
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * 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/devfreq.h>
+#include <linux/math64.h>
+#include <linux/pm_qos.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include "governor.h"
+
+/* Default constants for DevFreq-Simple-Ondemand (DFSO) */
+#define DFSO_UPTHRESHOLD       (50)
+#define DFSO_TARGET_PERCENTAGE (20)
+#define DFSO_PROPORTIONAL      (120)
+#define DFSO_WEIGHT            (100)
+
+static int devfreq_simple_usage_notifier(struct notifier_block *nb, unsigned long val, void *data)
+{
+       struct devfreq_notifier_block *devfreq_nb;
+       struct devfreq_simple_usage_data *simple_usage_data;
+
+       devfreq_nb = container_of(nb, struct devfreq_notifier_block, nb);
+       simple_usage_data = container_of(devfreq_nb, struct devfreq_simple_usage_data, nb);
+       devfreq_nb = &simple_usage_data->nb;
+
+       mutex_lock(&devfreq_nb->df->lock);
+       update_devfreq(devfreq_nb->df);
+       mutex_unlock(&devfreq_nb->df->lock);
+
+       return NOTIFY_OK;
+}
+
+static int devfreq_simple_usage_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 = DFSO_UPTHRESHOLD;
+       unsigned int dfso_target_percentage = DFSO_TARGET_PERCENTAGE;
+       unsigned int dfso_proportional = DFSO_PROPORTIONAL;
+       unsigned int dfso_multiplication_weight = DFSO_WEIGHT;
+       struct devfreq_simple_usage_data *data = df->data;
+       unsigned long max = (df->max_freq) ? df->max_freq : 0;
+       unsigned long pm_qos_min;
+
+       if (!data)
+               return -EINVAL;
+
+       if (!df->disabled_pm_qos)
+               pm_qos_min = pm_qos_request(data->pm_qos_class);
+
+       if (err)
+               return err;
+
+       if (data->upthreshold)
+               dfso_upthreshold = data->upthreshold;
+       if (data->target_percentage)
+               dfso_target_percentage = data->target_percentage;
+       if (data->proportional)
+               dfso_proportional = data->proportional;
+       if (data->multiplication_weight)
+               dfso_multiplication_weight = data->multiplication_weight;
+
+       a = stat.busy_time * dfso_multiplication_weight;
+       a = div64_u64(a, 100);
+       a = a * dfso_proportional;
+       b = div64_u64(a, stat.total_time);
+
+       /* If percentage is larger than upthreshold, set with max freq */
+       if (b >= data->upthreshold) {
+               max = max(data->cal_qos_max, pm_qos_min);
+               *freq = max;
+
+               if (*freq > df->max_freq)
+                       *freq = df->max_freq;
+
+               return 0;
+       }
+
+       b *= stat.current_frequency;
+
+       a = div64_u64(b, dfso_target_percentage);
+
+       if (a > data->cal_qos_max)
+               a = data->cal_qos_max;
+
+       *freq = (unsigned long) a;
+
+       if (pm_qos_min && *freq < pm_qos_min)
+               *freq = pm_qos_min;
+
+       return 0;
+}
+
+static int devfreq_simple_usage_register_notifier(struct devfreq *df)
+{
+       int ret;
+       struct devfreq_simple_usage_data *data = df->data;
+
+       if (!data)
+               return -EINVAL;
+
+       data->nb.df = df;
+       data->nb.nb.notifier_call = devfreq_simple_usage_notifier;
+
+       ret = pm_qos_add_notifier(data->pm_qos_class, &data->nb.nb);
+       if (ret < 0)
+               goto err;
+
+       return 0;
+err:
+       kfree((void *)&data->nb.nb);
+
+       return ret;
+}
+
+static int devfreq_simple_usage_unregister_notifier(struct devfreq *df)
+{
+       struct devfreq_simple_usage_data *data = df->data;
+
+       return pm_qos_remove_notifier(data->pm_qos_class, &data->nb.nb);
+}
+
+static int devfreq_simple_usage_handler(struct devfreq *devfreq,
+                               unsigned int event, void *data)
+{
+       int ret;
+
+       switch (event) {
+       case DEVFREQ_GOV_START:
+               ret = devfreq_simple_usage_register_notifier(devfreq);
+               if (ret)
+                       return ret;
+               devfreq_monitor_start(devfreq);
+               break;
+
+       case DEVFREQ_GOV_STOP:
+               devfreq_monitor_stop(devfreq);
+               ret = devfreq_simple_usage_unregister_notifier(devfreq);
+               if (ret)
+                       return ret;
+               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 0;
+}
+
+static struct devfreq_governor devfreq_simple_usage = {
+       .name = "simple_usage",
+       .get_target_freq = devfreq_simple_usage_func,
+       .event_handler = devfreq_simple_usage_handler,
+};
+
+static int __init devfreq_simple_usage_init(void)
+{
+       return devfreq_add_governor(&devfreq_simple_usage);
+}
+subsys_initcall(devfreq_simple_usage_init);
+
+static void __exit devfreq_simple_usage_exit(void)
+{
+       int ret;
+
+       ret = devfreq_remove_governor(&devfreq_simple_usage);
+       if (ret)
+               pr_err("%s: failed remove governor %d\n", __func__, ret);
+
+       return;
+}
+module_exit(devfreq_simple_usage_exit);
+MODULE_LICENSE("GPL");
diff --git a/include/dt-bindings/soc/samsung/exynos9610-devfreq.h b/include/dt-bindings/soc/samsung/exynos9610-devfreq.h
new file mode 100644 (file)
index 0000000..b25726a
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ * Device Tree binding constants for Exynos9610 devfreq
+ */
+
+#ifndef _DT_BINDINGS_EXYNOS_9610_DEVFREQ_H
+#define _DT_BINDINGS_EXYNOS_9610_DEVFREQ_H
+/* DEVFREQ TYPE LIST */
+#define DEVFREQ_MIF            0
+#define DEVFREQ_INT            1
+#define DEVFREQ_DISP           2
+#define DEVFREQ_CAM            3
+#define DEVFREQ_INTCAM         4
+#define DEVFREQ_AUD            5
+#define DEVFREQ_TYPE_END       9
+
+/* ESS FLAG LIST */
+#define ESS_FLAG_INT   2
+#define ESS_FLAG_MIF   3
+#define ESS_FLAG_ISP   4
+#define ESS_FLAG_DISP  5
+#define ESS_FLAG_INTCAM        6
+#define ESS_FLAG_AUD   7
+
+/* DEVFREQ GOV TYPE */
+#define SIMPLE_INTERACTIVE 0
+
+#endif
index cca31812cc83cb847ff0151be31ce6da643695f5..1b0c1059a5f90ec4a5171b6545dca231ee50142b 100644 (file)
@@ -16,6 +16,8 @@
 #include <linux/device.h>
 #include <linux/notifier.h>
 #include <linux/pm_opp.h>
+#include <linux/kthread.h>
+#include <linux/timer.h>
 
 #define DEVFREQ_NAME_LEN 16
 
@@ -46,8 +48,9 @@ struct devfreq_governor;
  */
 struct devfreq_dev_status {
        /* both since the last measure */
-       unsigned long total_time;
-       unsigned long busy_time;
+       unsigned long long total_time;
+       unsigned long long busy_time;
+       unsigned long long delta_time;
        unsigned long current_frequency;
        void *private_data;
 };
@@ -89,6 +92,7 @@ struct devfreq_dev_status {
  */
 struct devfreq_dev_profile {
        unsigned long initial_freq;
+       unsigned long suspend_freq;
        unsigned int polling_ms;
 
        int (*target)(struct device *dev, unsigned long *freq, u32 flags);
@@ -153,6 +157,7 @@ struct devfreq {
 
        unsigned long min_freq;
        unsigned long max_freq;
+       unsigned long str_freq;
        bool stop_polling;
 
        /* information for device frequency transition */
@@ -162,6 +167,8 @@ struct devfreq {
        unsigned long last_stat_updated;
 
        struct srcu_notifier_head transition_notifier_list;
+
+       bool disabled_pm_qos;
 };
 
 struct devfreq_freqs {
@@ -214,6 +221,14 @@ extern void devm_devfreq_unregister_notifier(struct device *dev,
 extern struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev,
                                                int index);
 
+#if IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND) || IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_USAGE)\
+       || IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_INTERACTIVE)
+struct devfreq_notifier_block {
+       struct notifier_block nb;
+       struct devfreq *df;
+};
+#endif
+
 #if IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND)
 /**
  * struct devfreq_simple_ondemand_data - void *data fed to struct devfreq
@@ -229,6 +244,7 @@ extern struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev,
  * the governor uses the default values.
  */
 struct devfreq_simple_ondemand_data {
+       unsigned int multiplication_weight;
        unsigned int upthreshold;
        unsigned int downdifferential;
        unsigned long cal_qos_max;
@@ -267,6 +283,28 @@ struct devfreq_simple_exynos_data {
 };
 #endif
 
+#if IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_INTERACTIVE)
+#define DEFAULT_DELAY_TIME             10 /* msec */
+#define DEFAULT_NDELAY_TIME            1
+#define DELAY_TIME_RANGE               10
+#define BOUND_CPU_NUM                  0
+
+struct devfreq_simple_interactive_data {
+       bool use_delay_time;
+       int *delay_time;
+       int ndelay_time;
+       unsigned long prev_freq;
+       u64 changed_time;
+       struct timer_list freq_timer;
+       struct timer_list freq_slack_timer;
+       struct task_struct *change_freq_task;
+       int pm_qos_class;
+       int pm_qos_class_max;
+       struct devfreq_notifier_block nb;
+       struct devfreq_notifier_block nb_max;
+};
+#endif
+
 #if IS_ENABLED(CONFIG_DEVFREQ_GOV_PASSIVE)
 /**
  * struct devfreq_passive_data - void *data fed to struct devfreq
diff --git a/include/soc/samsung/exynos-devfreq.h b/include/soc/samsung/exynos-devfreq.h
new file mode 100644 (file)
index 0000000..77cdf95
--- /dev/null
@@ -0,0 +1,112 @@
+/* linux/arch/arm64/mach-exynos/include/mach/exynos-devfreq.h
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *              http://www.samsung.com
+ *
+ * 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.
+ */
+#ifndef __EXYNOS_DEVFREQ_H_
+#define __EXYNOS_DEVFREQ_H_
+
+#include <linux/devfreq.h>
+#include <linux/pm_qos.h>
+#include <linux/clk.h>
+#include <soc/samsung/exynos-devfreq-dep.h>
+#ifdef CONFIG_EXYNOS_DVFS_MANAGER
+#include <soc/samsung/exynos-dm.h>
+#endif
+
+#define EXYNOS_DEVFREQ_MODULE_NAME     "exynos-devfreq"
+#define VOLT_STEP                      25000
+#define MAX_NR_CONSTRAINT              DM_TYPE_END
+#define DATA_INIT                      5
+#define SET_CONST                      1
+#define RELEASE                                2
+
+/* DEVFREQ GOV TYPE */
+#define SIMPLE_INTERACTIVE 0
+
+struct exynos_devfreq_opp_table {
+       u32 idx;
+       u32 freq;
+       u32 volt;
+};
+
+struct exynos_devfreq_data {
+       struct device                           *dev;
+       struct devfreq                          *devfreq;
+       struct mutex                            lock;
+       struct clk                              *clk;
+
+       bool                                    devfreq_disabled;
+
+       u32             devfreq_type;
+
+       struct exynos_devfreq_opp_table         *opp_list;
+
+       u32                                     default_qos;
+
+       u32                                     max_state;
+       struct devfreq_dev_profile              devfreq_profile;
+
+       u32             gov_type;
+       const char                              *governor_name;
+       u32                                     cal_qos_max;
+       void                                    *governor_data;
+#if IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_INTERACTIVE)
+       struct devfreq_simple_interactive_data  simple_interactive_data;
+#endif
+       u32                                     dfs_id;
+       s32                                     old_idx;
+       s32                                     new_idx;
+       u32                                     old_freq;
+       u32                                     new_freq;
+       u32                                     min_freq;
+       u32                                     max_freq;
+       u32                                     reboot_freq;
+       u32                                     boot_freq;
+
+       u32                                     old_volt;
+       u32                                     new_volt;
+
+       u32                                     pm_qos_class;
+       u32                                     pm_qos_class_max;
+       struct pm_qos_request                   sys_pm_qos_min;
+#ifdef CONFIG_ARM_EXYNOS_DEVFREQ_DEBUG
+       struct pm_qos_request                   debug_pm_qos_min;
+       struct pm_qos_request                   debug_pm_qos_max;
+#endif
+       struct pm_qos_request                   default_pm_qos_min;
+       struct pm_qos_request                   default_pm_qos_max;
+       struct pm_qos_request                   boot_pm_qos;
+       u32                                     boot_qos_timeout;
+
+       struct notifier_block                   reboot_notifier;
+
+       u32                                     ess_flag;
+
+       s32                                     target_delay;
+
+#ifdef CONFIG_EXYNOS_DVFS_MANAGER
+       u32             dm_type;
+       u32             nr_constraint;
+       struct exynos_dm_constraint             **constraint;
+#endif
+       void                                    *private_data;
+       bool                                    use_acpm;
+       bool                                    bts_update;
+       bool                                    update_fvp;
+       struct exynos_pm_domain *pm_domain;
+};
+
+s32 exynos_devfreq_get_opp_idx(struct exynos_devfreq_opp_table *table,
+                               unsigned int size, u32 freq);
+#if defined(CONFIG_ARM_EXYNOS_DEVFREQ) && defined(CONFIG_EXYNOS_DVFS_MANAGER)
+u32 exynos_devfreq_get_dm_type(u32 devfreq_type);
+u32 exynos_devfreq_get_devfreq_type(int dm_type);
+struct device *find_exynos_devfreq_device(void *devdata);
+int find_exynos_devfreq_dm_type(struct device *dev, int *dm_type);
+#endif
+#endif /* __EXYNOS_DEVFREQ_H_ */