blob: f5e561b61623cfff1579d9feaf7b8a3a1f787061 [file] [edit]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Google GS101 SoC devfreq 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 <linux/thermal.h>
#include <soc/google/cal-if.h>
#include <soc/google/bts.h>
#include <linux/of_platform.h>
#include <trace/events/power.h>
#include <trace/hooks/systrace.h>
#include <dt-bindings/soc/google/gs101-devfreq.h>
#include "../../soc/google/cal-if/acpm_dvfs.h"
#include <soc/google/exynos-pd.h>
#include <soc/google/exynos-devfreq.h>
#include <soc/google/ect_parser.h>
#include <soc/google/acpm_ipc_ctrl.h>
#if IS_ENABLED(CONFIG_EXYNOS_DVFS_MANAGER)
#include <soc/google/exynos-dm.h>
#endif
#if IS_ENABLED(CONFIG_GS_ACPM)
#include "../../soc/google/acpm/acpm.h"
#include "../../soc/google/acpm/acpm_ipc.h"
#endif
#include <soc/google/debug-snapshot.h>
#include "governor.h"
#include "gs-ppc.h"
#include "thermal_core.h"
#define HZ_PER_KHZ 1000
static struct exynos_devfreq_data **devfreq_data;
static u32 freq_array[6];
static u32 boot_array[2];
static struct devfreq *find_exynos_devfreq_device(void *devdata);
static int find_exynos_devfreq_dm_type(struct device *dev, int *dm_type);
static int exynos_devfreq_target(struct device *dev,
unsigned long *target_freq, u32 flags);
static u32 exynos_devfreq_opp_round_freq(
struct exynos_devfreq_opp_table *table,
unsigned int size, u32 freq);
#if IS_ENABLED(CONFIG_EXYNOS_ALT_DVFS)
static struct srcu_notifier_head exynos_alt_notifier;
void exynos_alt_call_chain(void)
{
srcu_notifier_call_chain(&exynos_alt_notifier, 0, NULL);
}
static int exynos_alt_register_notifier(struct notifier_block *nb)
{
return srcu_notifier_chain_register(&exynos_alt_notifier, nb);
}
static int exynos_alt_unregister_notifier(struct notifier_block *nb)
{
return srcu_notifier_chain_unregister(&exynos_alt_notifier, nb);
}
static int init_alt_notifier_list(void)
{
srcu_init_notifier_head(&exynos_alt_notifier);
return 0;
}
#endif
#if IS_ENABLED(CONFIG_EXYNOS_DVFS_MANAGER)
static int exynos_devfreq_dm_call(struct device *parent,
unsigned long *target_freq, u32 flags)
{
int err = 0;
struct platform_device *pdev;
struct exynos_devfreq_data *data;
unsigned long str_freq;
int dm_type;
unsigned long exynos_pm_qos_max;
struct devfreq *devfreq;
struct devfreq_simple_interactive_data *gov_data;
struct dev_pm_opp *max_opp;
pdev = container_of(parent, struct platform_device, dev);
if (!pdev)
return -ENODEV;
data = platform_get_drvdata(pdev);
if (!data || data->devfreq_disabled)
return -EPROBE_DEFER;
str_freq = data->suspend_freq;
devfreq = data->devfreq;
gov_data = devfreq->data;
err = find_exynos_devfreq_dm_type(devfreq->dev.parent, &dm_type);
if (err)
return -EINVAL;
ATRACE_BEGIN(__func__);
exynos_pm_qos_max = (unsigned long)HZ_PER_KHZ * dev_pm_qos_read_value(devfreq->dev.parent,
DEV_PM_QOS_MAX_FREQUENCY);
if (!strcmp(devfreq->governor->name, "interactive") && gov_data->pm_qos_class_max)
exynos_pm_qos_max =
(unsigned long)exynos_pm_qos_request(gov_data->pm_qos_class_max);
max_opp = dev_pm_opp_find_freq_floor(devfreq->dev.parent, &exynos_pm_qos_max);
if (max_opp == ERR_PTR(-ERANGE)) {
exynos_pm_qos_max = data->min_freq;
max_opp = dev_pm_opp_find_freq_ceil(devfreq->dev.parent, &exynos_pm_qos_max);
}
dev_pm_opp_put(max_opp);
if (data->suspend_flag)
policy_update_call_to_DM(dm_type, str_freq,
str_freq);
else
policy_update_call_to_DM(dm_type, *target_freq, exynos_pm_qos_max);
DM_CALL(dm_type, target_freq);
*target_freq = data->previous_freq;
ATRACE_END();
return err;
}
static int exynos_devfreq_get_cur_freq(struct device *dev,
unsigned long *freq)
{
struct platform_device *pdev = container_of(dev, struct platform_device, dev);
struct exynos_devfreq_data *data = platform_get_drvdata(pdev);
int ret = 0;
*freq = data->previous_freq;
return ret;
}
/**
* exynos_devfreq_get_freq_level() - Lookup freq_table for the frequency
* @devfreq: the devfreq instance
* @freq: the target frequency
*/
static int exynos_devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq)
{
int lev;
for (lev = 0; lev < devfreq->profile->max_state; lev++)
if (freq == devfreq->profile->freq_table[lev])
return lev;
return -EINVAL;
}
/**
* exynos_devfreq_update_status() - Update statistics of devfreq behavior
* @devfreq: the devfreq instance
* @freq: the update target frequency
*/
static int exynos_devfreq_update_status(struct exynos_devfreq_data *devdata)
{
int prev_lev, ret = 0;
unsigned long cur_time;
struct devfreq *devfreq;
devfreq = find_exynos_devfreq_device(devdata);
cur_time = jiffies;
/* Immediately exit if previous_freq is not initialized yet. */
if (!devdata->previous_freq)
goto out;
prev_lev = exynos_devfreq_get_freq_level(devfreq, devdata->previous_freq);
if (prev_lev < 0) {
ret = prev_lev;
goto out;
}
devdata->time_in_state[prev_lev] +=
cur_time - devdata->last_stat_updated;
out:
devdata->last_stat_updated = cur_time;
return ret;
}
static int devfreq_frequency_scaler(int dm_type, void *devdata,
u32 target_freq, unsigned int relation)
{
struct devfreq *devfreq;
unsigned long freq = target_freq;
unsigned long max_freq, min_freq;
u32 flags = 0;
int err = 0;
ATRACE_BEGIN(__func__);
devfreq = find_exynos_devfreq_device(devdata);
if (IS_ERR_OR_NULL(devfreq)) {
pr_err("%s: No such devfreq for dm_type(%d)\n", __func__, dm_type);
err = -ENODEV;
goto err_out;
}
/*
* Adjust the freuqency with user freq and QoS.
*
* List from the highest proiority
* max_freq (probably called by thermal when it's too hot)
* min_freq
*/
min_freq = (unsigned long)HZ_PER_KHZ * dev_pm_qos_read_value(devfreq->dev.parent,
DEV_PM_QOS_MIN_FREQUENCY);
if (min_freq && freq < min_freq) {
freq = min_freq;
flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use GLB */
}
max_freq = (unsigned long)HZ_PER_KHZ * dev_pm_qos_read_value(devfreq->dev.parent,
DEV_PM_QOS_MAX_FREQUENCY);
if (max_freq && freq > max_freq) {
freq = max_freq;
flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */
}
err = exynos_devfreq_target(devfreq->dev.parent, &freq, flags);
err_out:
ATRACE_END();
return err;
}
#endif
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;
#if IS_ENABLED(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;
/* 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++;
}
} else {
#if IS_ENABLED(CONFIG_EXYNOS_DVFS_MANAGER)
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 (exynos_pm_qos_request_active(&data->default_pm_qos_max))
exynos_pm_qos_update_request(&data->default_pm_qos_max,
data->reboot_freq);
return 0;
}
static unsigned long _exynos_devfreq_get_freq(unsigned int devfreq_type)
{
struct exynos_devfreq_data *data = NULL;
unsigned long freq;
if (devfreq_data)
data = devfreq_data[devfreq_type];
if (!data) {
pr_err("Fail to get exynos_devfreq_data\n");
return 0;
}
if (data->clk)
freq = data->old_freq;
else
freq = cal_dfs_get_rate(data->dfs_id);
if ((u32)freq == 0) {
if (data->clk)
dev_err(data->dev,
"failed get frequency from clock framework\n");
else
dev_err(data->dev, "failed get frequency from CAL\n");
return 0;
}
return freq;
}
unsigned long exynos_devfreq_get_domain_freq(unsigned int devfreq_type)
{
return _exynos_devfreq_get_freq(devfreq_type);
}
EXPORT_SYMBOL(exynos_devfreq_get_domain_freq);
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)_exynos_devfreq_get_freq(data->devfreq_type);
if (*cur_freq == 0) {
dev_err(dev, "failed get frequency\n");
return -EINVAL;
}
return 0;
}
int exynos_devfreq_get_boundary(unsigned int devfreq_type,
unsigned int *max_freq, unsigned int *min_freq)
{
struct exynos_devfreq_data *data = NULL;
if ((devfreq_type >= DEVFREQ_MIF) && (devfreq_type < DEVFREQ_TYPE_END)) {
data = devfreq_data[devfreq_type];
} else
return -EINVAL;
if (!data) {
pr_err("%s, Fail to get exynos_devfreq_data\n", __func__);
return -ENOMEM;
}
*max_freq = data->max_freq;
*min_freq = data->min_freq;
return 0;
}
EXPORT_SYMBOL(exynos_devfreq_get_boundary);
int exynos_devfreq_lock_freq(unsigned int devfreq_type, unsigned int target_freq)
{
struct exynos_devfreq_data *data = NULL;
int ret;
if ((devfreq_type >= DEVFREQ_MIF) && (devfreq_type < DEVFREQ_TYPE_END)) {
data = devfreq_data[devfreq_type];
} else
return -EINVAL;
if (!data) {
pr_err("%s, Fail to get exynos_devfreq_data\n", __func__);
return -ENOMEM;
}
ret = devfreq_frequency_scaler(devfreq_type, data, target_freq, 0);
return ret;
}
EXPORT_SYMBOL(exynos_devfreq_lock_freq);
static int exynos_devfreq_set_freq(struct device *dev, u32 new_freq,
struct clk *clk,
struct exynos_devfreq_data *data)
{
if (data->bts_update) {
if (data->new_freq < data->old_freq)
bts_update_scen(BS_MIF_CHANGE, data->new_freq);
}
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;
}
trace_clock_set_rate(dev_name(data->dev), new_freq, raw_smp_processor_id());
if (data->bts_update) {
if (data->new_freq > data->old_freq)
bts_update_scen(BS_MIF_CHANGE, data->new_freq);
}
return 0;
}
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->suspend_freq =
cal_dfs_get_resume_freq(data->dfs_id);
data->devfreq_profile.initial_freq =
min(data->devfreq_profile.initial_freq,
(unsigned long)data->max_freq);
data->devfreq_profile.initial_freq =
max(data->devfreq_profile.initial_freq,
(unsigned long)data->min_freq);
data->suspend_freq =
min(data->suspend_freq,
(unsigned long)data->max_freq);
data->suspend_freq =
max(data->suspend_freq,
(unsigned long)data->min_freq);
data->devfreq_profile.initial_freq =
exynos_devfreq_opp_round_freq(data->opp_list, data->max_state,
data->devfreq_profile.initial_freq);
data->max_freq =
exynos_devfreq_opp_round_freq(data->opp_list, data->max_state,
data->max_freq);
data->min_freq =
exynos_devfreq_opp_round_freq(data->opp_list, data->max_state,
data->min_freq);
data->suspend_freq =
exynos_devfreq_opp_round_freq(data->opp_list, data->max_state,
data->suspend_freq);
dev_info(data->dev, "initial_freq: %luKhz, suspend_freq: %luKhz\n",
data->devfreq_profile.initial_freq,
data->suspend_freq);
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;
}
EXPORT_SYMBOL_GPL(exynos_devfreq_init_freq_table);
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 ssize_t show_exynos_devfreq_cmu_dump(struct device *dev,
struct device_attribute *attr,
char *buf)
{
ssize_t count = 0;
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 = exynos_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 (exynos_pm_qos_request_active(&data->debug_pm_qos_max))
exynos_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 = exynos_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 (exynos_pm_qos_request_active(&data->debug_pm_qos_min))
exynos_pm_qos_update_request(&data->debug_pm_qos_min, qos_value);
return count;
}
static ssize_t cancel_boot_freq_store(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;
bool cancel_flag;
ret = kstrtobool(buf, &cancel_flag);
if (ret) {
dev_err(dev, "Failed to store cancel_boot\n");
return ret;
}
if (cancel_flag) {
exynos_pm_qos_update_request_timeout(&data->boot_pm_qos,
data->boot_freq,
0);
}
return count;
}
static ssize_t show_alt_dvfs_info(struct device *dev,
struct device_attribute *attr, char *buf)
{
#if IS_ENABLED(CONFIG_EXYNOS_ALT_DVFS)
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.alt_data.num_target_load;
i++) {
count += snprintf(buf + count, PAGE_SIZE, "%d%s",
data->simple_interactive_data.alt_data.target_load[i],
(i ==
(data->simple_interactive_data.alt_data.num_target_load - 1)) ?
"" : " ");
}
count += snprintf(buf + count, PAGE_SIZE, "\n");
/* Parameters */
count += snprintf(buf + count, PAGE_SIZE, "MIN SAMPLE TIME: %u\n",
data->simple_interactive_data.alt_data.min_sample_time);
count += snprintf(buf + count, PAGE_SIZE, "HOLD SAMPLE TIME: %u\n",
data->simple_interactive_data.alt_data.hold_sample_time);
count += snprintf(buf + count, PAGE_SIZE, "HISPEED LOAD: %u\n",
data->simple_interactive_data.alt_data.hispeed_load);
count += snprintf(buf + count, PAGE_SIZE, "HISPEED FREQ: %u\n",
data->simple_interactive_data.alt_data.hispeed_freq);
mutex_unlock(&data->devfreq->lock);
return count;
#else
return 0;
#endif
}
static DEVICE_ATTR(alt_dvfs_info, 0640, show_alt_dvfs_info, NULL);
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_WO(cancel_boot_freq);
static ssize_t time_in_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct platform_device *pdev = container_of(dev->parent, struct platform_device, dev);
struct exynos_devfreq_data *data = platform_get_drvdata(pdev);
struct devfreq *devfreq = to_devfreq(dev);
ssize_t len = 0;
int i, err;
unsigned int max_state = devfreq->profile->max_state;
mutex_lock(&data->lock);
err = exynos_devfreq_update_status(data);
if (err)
return 0;
for (i = 0; i < max_state; i++) {
len += sprintf(buf + len, "%8lu",
devfreq->profile->freq_table[i]);
len += sprintf(buf + len, "%10u\n",
jiffies_to_msecs(data->time_in_state[i]));
}
mutex_unlock(&data->lock);
return len;
}
static DEVICE_ATTR_RO(time_in_state);
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_alt_dvfs_info.attr,
&dev_attr_cancel_boot_freq.attr,
NULL,
};
static struct attribute_group exynos_devfreq_attr_group = {
.name = "exynos_data",
.attrs = exynos_devfreq_sysfs_entries,
};
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 = exynos_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 (exynos_pm_qos_request_active(&data->sys_pm_qos_min))
exynos_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_array(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_ERR_OR_ZERO(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;
}
#if IS_ENABLED(CONFIG_EXYNOS_ALT_DVFS)
static ssize_t show_target_load(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.alt_data.num_target_load;
i++) {
count += snprintf(buf + count, PAGE_SIZE, "%d%s",
data->simple_interactive_data.alt_data.target_load[i],
(i ==
(data->simple_interactive_data.alt_data.num_target_load - 1) ?
"" : " "));
}
count += snprintf(buf + count, PAGE_SIZE, "\n");
mutex_unlock(&data->devfreq->lock);
return count;
}
static ssize_t store_target_load(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_target_load = NULL;
new_target_load = get_tokenized_data(buf, &ntokens);
if (IS_ERR(new_target_load))
return PTR_ERR_OR_ZERO(new_target_load);
mutex_lock(&data->devfreq->lock);
kfree(data->simple_interactive_data.alt_data.target_load);
data->simple_interactive_data.alt_data.target_load = new_target_load;
data->simple_interactive_data.alt_data.num_target_load = ntokens;
mutex_unlock(&data->devfreq->lock);
return count;
}
static ssize_t show_hold_sample_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, "%u\n",
data->simple_interactive_data.alt_data.hold_sample_time);
mutex_unlock(&data->devfreq->lock);
return count;
}
static ssize_t store_hold_sample_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;
mutex_lock(&data->devfreq->lock);
ret = sscanf(buf, "%u",
&data->simple_interactive_data.alt_data.hold_sample_time);
mutex_unlock(&data->devfreq->lock);
if (ret != 1)
return -EINVAL;
return count;
}
static ssize_t min_sample_time_show(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;
mutex_lock(&data->devfreq->lock);
count = snprintf(buf, PAGE_SIZE, "%u\n",
data->simple_interactive_data.alt_data.min_sample_time);
mutex_unlock(&data->devfreq->lock);
return count;
}
static ssize_t min_sample_time_store(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);
unsigned int min_sample_time;
if (kstrtouint(buf, 10, &min_sample_time) || !min_sample_time)
return -EINVAL;
mutex_lock(&data->devfreq->lock);
data->simple_interactive_data.alt_data.min_sample_time =
min_sample_time;
mutex_unlock(&data->devfreq->lock);
return count;
}
#endif
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);
#if IS_ENABLED(CONFIG_EXYNOS_ALT_DVFS)
static DEVICE_ATTR(target_load, 0640, show_target_load, store_target_load);
static DEVICE_ATTR(hold_sample_time, 0640, show_hold_sample_time,
store_hold_sample_time);
static DEVICE_ATTR_RW(min_sample_time);
#endif
static struct attribute *devfreq_interactive_sysfs_entries[] = {
&dev_attr_use_delay_time.attr,
&dev_attr_delay_time.attr,
#if IS_ENABLED(CONFIG_EXYNOS_ALT_DVFS)
&dev_attr_target_load.attr,
&dev_attr_hold_sample_time.attr,
&dev_attr_min_sample_time.attr,
#endif
NULL,
};
static struct attribute_group devfreq_delay_time_attr_group = {
.name = "interactive",
.attrs = devfreq_interactive_sysfs_entries,
};
#if IS_ENABLED(CONFIG_EXYNOS_DVFS_MANAGER)
static 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;
}
static struct devfreq *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->devfreq;
}
#endif
#ifdef CONFIG_OF
#if IS_ENABLED(CONFIG_ECT)
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)
return -ENODEV;
dvfs_domain = ect_dvfs_get_domain(dvfs_block, (char *)dvfs_domain_name);
if (!dvfs_domain)
return -ENODEV;
data->max_state = dvfs_domain->num_of_level;
data->opp_list = kcalloc(data->max_state,
sizeof(struct exynos_devfreq_opp_table),
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;
}
EXPORT_SYMBOL_GPL(exynos_devfreq_parse_ect);
#endif
#if IS_ENABLED(CONFIG_EXYNOS_ALT_DVFS)
#define NUM_COLS 2
#endif
static int exynos_devfreq_parse_dt(struct device_node *np,
struct exynos_devfreq_data *data)
{
const char *use_acpm, *bts_update;
const char *use_get_dev;
#if IS_ENABLED(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;
u32 count_total = 0;
#if IS_ENABLED(CONFIG_EXYNOS_ALT_DVFS)
struct devfreq_alt_dvfs_data *alt_data;
u32 *of_data_int_array = NULL;
int map_cnt;
#endif
unsigned int i;
unsigned int val;
unsigned int *volt_table;
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 IS_ENABLED(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);
}
data->clk = devm_clk_get(data->dev, "DEVFREQ");
#if IS_ENABLED(CONFIG_ECT)
if (data->clk && !IS_ERR(data->clk))
dev_info(data->dev, "%s clock info exist\n",
devfreq_domain_name);
else
data->clk = NULL;
#endif
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->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(np, "max-volt", &val) &&
!of_property_read_u32(np, "dfs_id", &data->dfs_id)) {
volt_table = kzalloc(sizeof(unsigned int) * data->max_state,
GFP_KERNEL);
if (!volt_table)
return -ENOMEM;
cal_dfs_get_asv_table(data->dfs_id, volt_table);
for (i = 0; i < data->max_state; i++) {
if (volt_table[i] <= val)
break;
}
data->max_freq = min(data->max_freq, data->opp_list[i].freq);
kfree(volt_table);
}
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 (!of_property_read_string(np, "use_get_dev", &use_get_dev)) {
if (!strcmp(use_get_dev, "true")) {
data->use_get_dev = true;
} else if (!strcmp(use_get_dev, "false")) {
data->use_get_dev = false;
} else {
dev_err(data->dev, "invalid use_get_dev string (%s)\n",
use_get_dev);
return -EINVAL;
}
} else {
dev_info(data->dev,
"Operation function get_dev_status will not be registered.\n");
data->use_get_dev = false;
}
of_property_read_u32(np, "polling_ms",
&data->devfreq_profile.polling_ms);
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;
}
}
/* Polling ms must be registered */
/* */
if (data->use_get_dev) {
/* Register um_data and um_list for tracing load */
int i;
data->um_data.um_group =
of_property_count_elems_of_size(np, "um_count",
sizeof(u32));
if (data->um_data.um_group <= 0)
return -ENODEV;
data->um_data.um_count =
devm_kcalloc(data->dev, data->um_data.um_group,
sizeof(u32), GFP_KERNEL);
if (!data->um_data.um_count) {
dev_err(data->dev,
"Failed to allocate memory for um_count\n");
return -ENOMEM;
}
if (of_property_read_u32_array(np, "um_count",
(u32 *)data->um_data.um_count,
(size_t)data->um_data.um_group))
return -ENODEV;
data->um_data.ppc_val =
devm_kcalloc(data->dev, data->um_data.um_group,
sizeof(struct ppc_data),
GFP_KERNEL);
if (data->um_data.ppc_val == NULL) {
dev_err(data->dev,
"failed to allocate memory for PPC val\n");
return -ENOMEM;
}
for (i = 0; i < data->um_data.um_group; i++)
count_total += data->um_data.um_count[i];
data->um_data.um_count_total = count_total;
data->um_data.pa_base =
kcalloc(data->um_data.um_count_total, sizeof(u32),
GFP_KERNEL);
if (!data->um_data.pa_base) {
dev_err(data->dev,
"failed to allocate memory for PA base\n");
return -ENOMEM;
}
if (of_property_read_u32_array(np, "um_list",
(u32 *)data->um_data.pa_base,
(size_t)(data->um_data.um_count_total)))
return -ENODEV;
data->um_data.va_base = kcalloc(data->um_data.um_count_total,
sizeof(void __iomem *),
GFP_KERNEL);
if (!data->um_data.va_base) {
dev_err(data->dev,
"failed to allocate memory for va base");
kfree(data->um_data.pa_base);
return -ENOMEM;
}
for (i = 0; i < data->um_data.um_count_total; i++) {
data->um_data.va_base[i] = ioremap(data->um_data.pa_base[i],
SZ_4K);
}
} else {
dev_info(data->dev, "Not registered UM monitor data\n");
}
#if IS_ENABLED(CONFIG_EXYNOS_ALT_DVFS)
/* Parse ALT-DVFS related parameters */
if (of_property_read_bool(np, "use_alt_dvfs")) {
int i;
alt_data = &data->simple_interactive_data.alt_data;
alt_data->num_target_load =
of_property_count_elems_of_size(np, "target_load",
sizeof(u32));
if (alt_data->num_target_load > 0) {
alt_data->target_load =
kmalloc_array(alt_data->num_target_load,
sizeof(u32), GFP_KERNEL);
if (!alt_data->target_load) {
dev_err(data->dev,
"Failed to allocate memory for target_load\n");
return -ENOMEM;
}
if (of_property_read_u32_array(np, "target_load",
(u32 *)alt_data->target_load,
(size_t)alt_data->num_target_load))
return -ENODEV;
} else {
/* Fix target load as defined ALTDVFS_TARGET_LOAD */
alt_data->target_load = kmalloc(sizeof(unsigned int), GFP_KERNEL);
if (!alt_data->target_load)
return -ENOMEM;
*alt_data->target_load = ALTDVFS_TARGET_LOAD;
alt_data->num_target_load =
ALTDVFS_NUM_TARGET_LOAD;
}
if (of_property_read_u32(np, "min_sample_time",
&alt_data->min_sample_time))
alt_data->min_sample_time =
ALTDVFS_MIN_SAMPLE_TIME;
if (of_property_read_u32(np, "hold_sample_time",
&alt_data->hold_sample_time))
alt_data->hold_sample_time =
ALTDVFS_HOLD_SAMPLE_TIME;
if (of_property_read_u32(np, "hispeed_load",
&alt_data->hispeed_load))
alt_data->hispeed_load = ALTDVFS_HISPEED_LOAD;
if (of_property_read_u32(np, "hispeed_freq",
&alt_data->hispeed_freq))
alt_data->hispeed_freq = ALTDVFS_HISPEED_FREQ;
if (of_property_read_u32(np, "tolerance",
&alt_data->tolerance))
alt_data->tolerance = ALTDVFS_TOLERANCE;
/* Initial buffer and load setup */
alt_data->track_group = data->um_data.um_group;
alt_data->track = devm_kcalloc(data->dev,
alt_data->track_group,
sizeof(struct devfreq_alt_track),
GFP_KERNEL);
if (!alt_data->track) {
dev_err(data->dev,
"Failed to allocate memory\n");
return -ENOMEM;
}
for (i = 0; i < alt_data->track_group; i++) {
alt_data->track[i].front = alt_data->track[i].buffer;
alt_data->track[i].rear = alt_data->track[i].buffer;
alt_data->track[i].min_load = 100;
}
/* Initial governor freq setup */
data->simple_interactive_data.governor_freq = 0;
map_cnt = of_property_count_elems_of_size(np, "mif_int_map",
sizeof(u32));
if (map_cnt <= 0 || map_cnt % NUM_COLS) {
dev_info(data->dev,
"No valid mif_int_map available\n");
goto err_map;
}
of_data_int_array = kcalloc(map_cnt, sizeof(u32), GFP_KERNEL);
if (!of_data_int_array) {
dev_err(data->dev,
"Failed to allocate memory\n");
return -ENOMEM;
}
if (of_property_read_u32_array(np, "mif_int_map",
of_data_int_array,
(size_t)map_cnt)) {
kfree(of_data_int_array);
return -ENODEV;
}
alt_data->map_row_cnt = map_cnt / NUM_COLS;
alt_data->mif_int_tbl =
devm_kcalloc(data->dev, alt_data->map_row_cnt,
sizeof(*alt_data->mif_int_tbl), GFP_KERNEL);
if (!alt_data->mif_int_tbl) {
dev_err(data->dev,
"Failed to allocate memory for mif_int_tbl\n");
kfree(of_data_int_array);
return -ENOMEM;
}
for (i = 0; i < alt_data->map_row_cnt; i++) {
alt_data->mif_int_tbl[i].mif_freq =
of_data_int_array[i * NUM_COLS];
alt_data->mif_int_tbl[i].int_freq =
of_data_int_array[i * NUM_COLS + 1];
}
err_map:
kfree(of_data_int_array);
} else {
dev_info(data->dev,
"ALT-DVFS is not declared by device tree.\n");
}
#endif
} else {
dev_err(data->dev, "not support governor type %u\n",
data->gov_type);
return -EINVAL;
}
#if IS_ENABLED(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;
}
/*
* Round freq to the opp with closest but lower or equal frequency
* If none are found, the lowest opp freq is given
*/
static u32 exynos_devfreq_opp_round_freq(
struct exynos_devfreq_opp_table *table,
unsigned int size, u32 freq)
{
int i;
for (i = 0; i < size; ++i) {
if (table[i].freq > freq)
continue;
return table[i].freq;
}
return table[size - 1].freq;
}
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;
}
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 (exynos_pm_qos_request_active(&data->default_pm_qos_min))
exynos_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;
}
#if IS_ENABLED(CONFIG_EXYNOS_ALT_DVFS)
static int exynos_devfreq_notifier(struct notifier_block *nb, unsigned long val,
void *v)
{
struct devfreq_notifier_block *um_nb =
container_of(nb, struct devfreq_notifier_block, nb);
int err;
mutex_lock(&um_nb->df->lock);
err = update_devfreq(um_nb->df);
if (err && err != -EAGAIN) {
dev_err(&um_nb->df->dev, "devfreq failed with (%d) error\n",
err);
mutex_unlock(&um_nb->df->lock);
return NOTIFY_BAD;
}
mutex_unlock(&um_nb->df->lock);
return NOTIFY_OK;
}
#endif
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 dev_pm_opp *target_opp;
u64 before_target, after_target, before_setfreq, after_setfreq;
u32 target_volt;
s32 target_idx;
s32 target_time = 0;
int ret = 0;
if (data->devfreq_disabled)
return -EAGAIN;
before_target = sched_clock();
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);
#if IS_ENABLED(CONFIG_DEBUG_SNAPSHOT)
dbg_snapshot_freq(data->ess_flag, data->old_freq, data->new_freq,
DSS_FLAG_IN);
#endif
before_setfreq = sched_clock();
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;
}
after_setfreq = sched_clock();
#if IS_ENABLED(CONFIG_DEBUG_SNAPSHOT)
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;
if (data->devfreq->profile->freq_table)
if (exynos_devfreq_update_status(data))
dev_err(dev,
"Couldn't update frequency transition information.\n");
data->previous_freq = data->new_freq;
out:
mutex_unlock(&data->lock);
after_target = sched_clock();
target_time = after_target - before_target;
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;
#if IS_ENABLED(CONFIG_EXYNOS_DVFS_MANAGER)
#if IS_ENABLED(CONFIG_GS_ACPM)
int size, ch_num;
unsigned int cmd[4];
struct ipc_config config;
#endif
#endif
u32 get_freq = 0;
#if IS_ENABLED(CONFIG_EXYNOS_DVFS_MANAGER)
if (data->use_acpm) {
mutex_lock(&data->devfreq->lock);
//send flag
#if IS_ENABLED(CONFIG_GS_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.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 information to FVP");
mutex_unlock(&data->devfreq->lock);
return -EINVAL;
}
#endif
ret = update_devfreq(data->devfreq);
if (ret) {
dev_err(&data->devfreq->dev,
"devfreq failed with (%d) error\n", ret);
mutex_unlock(&data->devfreq->lock);
dev->power.must_resume = true;
return -EINVAL;
}
data->suspend_flag = true;
mutex_unlock(&data->devfreq->lock);
}
#endif
if (!data->use_acpm && exynos_pm_qos_request_active(&data->default_pm_qos_min))
exynos_pm_qos_update_request(&data->default_pm_qos_min,
data->suspend_freq);
#if IS_ENABLED(CONFIG_EXYNOS_ALT_DVFS)
exynos_devfreq_um_exit(data);
#endif
dev->power.must_resume = true;
dev_dbg(data->dev, "Suspend frequency is %u\n",
exynos_devfreq_get_freq(data->dev, &get_freq, data->clk, data) ?
0 : get_freq);
return ret; // 0
}
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);
#if IS_ENABLED(CONFIG_EXYNOS_DVFS_MANAGER)
#if IS_ENABLED(CONFIG_GS_ACPM)
int size, ch_num;
unsigned int cmd[4];
struct ipc_config config;
#endif
#endif
int ret = 0;
u32 cur_freq;
dev_dbg(data->dev, "Resume_frequency is %u\n",
exynos_devfreq_get_freq(data->dev, &cur_freq, data->clk, data) ?
0 : cur_freq);
#if IS_ENABLED(CONFIG_EXYNOS_ALT_DVFS)
ret = exynos_devfreq_um_init(data);
if (ret)
dev_err(data->dev, "failed to restart um\n");
#endif
#if IS_ENABLED(CONFIG_EXYNOS_DVFS_MANAGER)
if (data->use_acpm) {
mutex_lock(&data->devfreq->lock);
data->suspend_flag = false;
//send flag (retry?)
#if IS_ENABLED(CONFIG_GS_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.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 information to FVP");
mutex_unlock(&data->devfreq->lock);
return -EINVAL;
}
#endif
ret = update_devfreq(data->devfreq);
if (ret) {
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 && exynos_pm_qos_request_active(&data->default_pm_qos_min))
exynos_pm_qos_update_request(&data->default_pm_qos_min,
data->default_qos);
return ret;
}
ssize_t
user_vote_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct thermal_cooling_device *cdev = to_cooling_device(dev);
struct exynos_devfreq_data *devfreq_cdev = cdev->devdata;
if (!devfreq_cdev)
return -ENODEV;
return sprintf(buf, "%lu\n", devfreq_cdev->sysfs_req);
}
ssize_t user_vote_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct thermal_cooling_device *cdev = to_cooling_device(dev);
struct exynos_devfreq_data *devfreq_cdev = cdev->devdata;
int ret;
unsigned long state;
if (!devfreq_cdev)
return -ENODEV;
ret = kstrtoul(buf, 0, &state);
if (ret)
return ret;
if (state > devfreq_cdev->max_state)
return -EINVAL;
mutex_lock(&cdev->lock);
devfreq_cdev->sysfs_req = state;
cdev->updated = false;
mutex_unlock(&cdev->lock);
thermal_cdev_update(cdev);
return count;
}
static DEVICE_ATTR_RW(user_vote);
static int exynos_devfreq_get_max_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
struct exynos_devfreq_data *data = cdev->devdata;
*state = data->max_state;
return 0;
}
static int exynos_devfreq_get_cur_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
struct exynos_devfreq_data *data = cdev->devdata;
*state = data->cooling_state;
return 0;
}
static int exynos_devfreq_set_cur_state(struct thermal_cooling_device *cdev,
unsigned long state)
{
struct exynos_devfreq_data *data = cdev->devdata;
/* Request state should be less than max_level */
if (WARN_ON(state > data->max_state))
return -EINVAL;
state = max(data->sysfs_req, state);
/* Check if the old cooling action is same as new cooling action */
if (data->cooling_state == state)
return -EALREADY;
data->cooling_state = state;
dev_info(data->dev, "Set %s cur_state %lu, freq: %8luKhz\n", cdev->type,
state, data->devfreq_profile.freq_table[state]);
if (exynos_pm_qos_request_active(&data->thermal_pm_qos_max))
exynos_pm_qos_update_request(&data->thermal_pm_qos_max,
data->devfreq_profile.freq_table[state]);
return 0;
}
static struct thermal_cooling_device_ops exynos_devfreq_cooling_ops = {
.get_max_state = exynos_devfreq_get_max_state,
.get_cur_state = exynos_devfreq_get_cur_state,
.set_cur_state = exynos_devfreq_set_cur_state,
};
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;
#if IS_ENABLED(CONFIG_EXYNOS_DVFS_MANAGER)
int nr_constraint;
int dm_type;
int err;
#endif
#if IS_ENABLED(CONFIG_ECT)
const char *devfreq_domain_name;
#endif
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
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_dm_call;
data->devfreq_profile.get_cur_freq = exynos_devfreq_get_cur_freq;
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 = kcalloc(data->max_state,
sizeof(*data->devfreq_profile.freq_table),
GFP_KERNEL);
if (!data->devfreq_profile.freq_table) {
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->previous_freq = data->old_freq;
data->last_stat_updated = jiffies;
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;
#if IS_ENABLED(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;
}
/* Register device tracing for ALT-DVFS */
if (data->use_get_dev)
register_get_dev_status(data);
data->time_in_state = devm_kcalloc(data->dev,
data->devfreq->profile->max_state,
sizeof(unsigned long),
GFP_KERNEL);
if (!data->time_in_state) {
err = -ENOMEM;
goto err_devfreq;
}
lockdep_register_key(&data->devfreq_lock_key);
lockdep_set_class(&data->devfreq->lock, &data->devfreq_lock_key);
#if IS_ENABLED(CONFIG_EXYNOS_DVFS_MANAGER)
err = find_exynos_devfreq_dm_type(data->dev, &dm_type);
if (err)
goto err_dm_type;
err = register_exynos_dm_freq_scaler(dm_type, devfreq_frequency_scaler);
if (err)
goto err_dm_scaler;
#endif
if (data->min_freq) {
err = dev_pm_qos_update_request(
&data->devfreq->user_min_freq_req, data->min_freq / HZ_PER_KHZ);
if (err < 0)
goto err_dm_scaler;
}
if (data->max_freq) {
err = dev_pm_qos_update_request(
&data->devfreq->user_max_freq_req, data->max_freq / HZ_PER_KHZ);
if (err < 0)
goto err_dm_scaler;
}
exynos_pm_qos_add_request(&data->sys_pm_qos_min, (int)data->pm_qos_class,
data->min_freq);
exynos_pm_qos_add_request(&data->debug_pm_qos_min, (int)data->pm_qos_class,
data->min_freq);
exynos_pm_qos_add_request(&data->debug_pm_qos_max, (int)data->pm_qos_class_max,
data->max_freq);
exynos_pm_qos_add_request(&data->thermal_pm_qos_max, (int)data->pm_qos_class_max,
data->max_freq);
if (data->pm_qos_class_max)
exynos_pm_qos_add_request(&data->default_pm_qos_max,
(int)data->pm_qos_class_max, data->max_freq);
exynos_pm_qos_add_request(&data->default_pm_qos_min, (int)data->pm_qos_class,
data->default_qos);
exynos_pm_qos_add_request(&data->boot_pm_qos, (int)data->pm_qos_class,
data->devfreq_profile.initial_freq);
/* Initialize ALT-DVFS */
#if IS_ENABLED(CONFIG_EXYNOS_ALT_DVFS)
if (data->use_get_dev) {
init_alt_notifier_list();
/* if polling_ms is 0, update_devfreq function is called by um */
if (data->devfreq_profile.polling_ms == 0) {
data->um_nb =
kzalloc(sizeof(struct devfreq_notifier_block),
GFP_KERNEL);
if (!data->um_nb) {
dev_err(data->dev,
"failed to allocate notifier block\n");
ret = -ENOMEM;
goto err_um_nb;
}
data->um_nb->df = data->devfreq;
data->um_nb->nb.notifier_call = exynos_devfreq_notifier;
exynos_alt_register_notifier(&data->um_nb->nb);
data->last_monitor_time = sched_clock();
}
/*
* UM data should be register.
* And if polling_ms is 0, um notifier should be register in callback.
*/
ret = exynos_devfreq_um_init(data);
if (ret) {
dev_err(data->dev, "failed register um\n");
goto err_um;
}
}
exynos_pm_qos_add_request(&data->bus_pm_qos_min, PM_QOS_DEVICE_THROUGHPUT, 0);
#endif
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");
ret = sysfs_create_file(&data->devfreq->dev.kobj,
&dev_attr_time_in_state.attr);
if (ret)
dev_warn(data->dev,
"failed create sysfs for devfreq time_in_state\n");
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");
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 */
exynos_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);
exynos_pm_qos_update_request(&data->boot_pm_qos, data->default_qos);
pm_runtime_put_sync(&pdev->dev);
}
#if IS_ENABLED(CONFIG_ECT)
if (of_property_read_string(data->dev->of_node, "devfreq_domain_name",
&devfreq_domain_name))
return -ENODEV;
// Register cooling device
data->cooling_dev = devm_thermal_of_cooling_device_register(data->dev,
dev_of_node(data->dev), (char *)devfreq_domain_name,
data, &exynos_devfreq_cooling_ops);
if (IS_ERR(data->cooling_dev)) {
ret = PTR_ERR(data->cooling_dev);
dev_err(data->dev, "%s: failed to register cooling device: %d\n",
devfreq_domain_name, ret);
} else {
dev_info(data->dev, "%s cdev is initialized!!\n", devfreq_domain_name);
ret = device_create_file(&data->cooling_dev->device, &dev_attr_user_vote);
if (ret)
thermal_cooling_device_unregister(data->cooling_dev);
}
#endif
dev_info(data->dev, "devfreq is initialized!!\n");
return ret;
err_reboot_noti:
devfreq_unregister_opp_notifier(data->dev, data->devfreq);
err_opp_noti:
exynos_pm_qos_remove_request(&data->boot_pm_qos);
exynos_pm_qos_remove_request(&data->default_pm_qos_min);
if (data->pm_qos_class_max)
exynos_pm_qos_remove_request(&data->default_pm_qos_max);
exynos_pm_qos_remove_request(&data->debug_pm_qos_min);
exynos_pm_qos_remove_request(&data->debug_pm_qos_max);
exynos_pm_qos_remove_request(&data->thermal_pm_qos_max);
exynos_pm_qos_remove_request(&data->sys_pm_qos_min);
devfreq_remove_device(data->devfreq);
#if IS_ENABLED(CONFIG_EXYNOS_ALT_DVFS)
exynos_pm_qos_remove_request(&data->bus_pm_qos_min);
err_um:
if (data->um_nb) {
exynos_alt_unregister_notifier(&data->um_nb->nb);
kfree(data->um_nb);
}
err_um_nb:
#endif
#if IS_ENABLED(CONFIG_EXYNOS_DVFS_MANAGER)
unregister_exynos_dm_freq_scaler(dm_type);
err_dm_scaler:
err_dm_type:
#endif
lockdep_unregister_key(&data->devfreq_lock_key);
err_devfreq:
#if IS_ENABLED(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);
#if IS_ENABLED(CONFIG_EXYNOS_DVFS_MANAGER)
int nr_constraint;
#endif
sysfs_remove_file(&data->devfreq->dev.kobj,
&dev_attr_scaling_devfreq_min.attr);
sysfs_remove_group(&data->devfreq->dev.kobj,
&exynos_devfreq_attr_group);
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);
exynos_pm_qos_remove_request(&data->boot_pm_qos);
exynos_pm_qos_remove_request(&data->default_pm_qos_min);
if (data->pm_qos_class_max)
exynos_pm_qos_remove_request(&data->default_pm_qos_max);
exynos_pm_qos_remove_request(&data->debug_pm_qos_min);
exynos_pm_qos_remove_request(&data->debug_pm_qos_max);
exynos_pm_qos_remove_request(&data->thermal_pm_qos_max);
exynos_pm_qos_remove_request(&data->sys_pm_qos_min);
#if IS_ENABLED(CONFIG_EXYNOS_ALT_DVFS)
exynos_alt_unregister_notifier(&data->um_nb->nb);
exynos_devfreq_um_exit(data);
#endif
lockdep_unregister_key(&data->devfreq_lock_key);
devfreq_remove_device(data->devfreq);
#if IS_ENABLED(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;
devfreq_simple_interactive_init();
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 = kcalloc(num_domains,
sizeof(struct exynos_devfreq_data *), 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 <[email protected]>");
MODULE_DESCRIPTION("Samsung EXYNOS Soc series devfreq common driver");
MODULE_LICENSE("GPL");