blob: 5eec261f3991154a0c9fe091c67eba52057e6f62 [file] [log] [blame]
/*
* common function for clock framework source file
*
* Copyright (C) 2012 Marvell
* Chao Xie <xiechao.mail@gmail.com>
* Zhoujie Wu <zjwu@marvell.com>
* Lu Cao <lucao@marvell.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cpufreq.h>
#include <linux/devfreq.h>
#include <linux/clk/mmpcpdvc.h>
#include "clk.h"
enum comp {
_CORE,
_DDR,
_AXI,
BOOT_DVFS_MAX,
};
static unsigned long minfreq[BOOT_DVFS_MAX] = { 0, 0, 0 };
static unsigned long bootfreq[BOOT_DVFS_MAX];
static struct clk *clk[BOOT_DVFS_MAX];
static char *clk_name[BOOT_DVFS_MAX] = {"cpu", "ddr", "axi"};
static struct pm_qos_request sdh_core_qos_max, sdh_ddr_qos_max;
static struct pm_qos_request sdh_core_qos_min;
static int __init sdh_tuning_init(void)
{
#if defined(CONFIG_CPU_FREQ) && defined(CONFIG_PM_DEVFREQ)
struct cpufreq_frequency_table *cpufreq_table =
cpufreq_frequency_get_table(0);
struct devfreq_frequency_table *ddrfreq_table =
devfreq_frequency_get_table(DEVFREQ_DDR);
if (cpufreq_table)
minfreq[_CORE] = cpufreq_table[0].frequency;
else
pr_err("%s cpufreq_table get failed, use 0 to set!\n", __func__);
if (ddrfreq_table)
minfreq[_DDR] = ddrfreq_table[0].frequency;
else
pr_err("%s ddrfreq_table get failed, use 0 to set!\n", __func__);
#else
pr_info("%s CONFIG_CPU_FREQ & CONFIG_PM_DEVFREQ not defined!\n", __func__);
minfreq[_CORE] = 0;
minfreq[_DDR] = 0;
#endif
sdh_core_qos_max.name = "sdh_tuning_core_max";
pm_qos_add_request(&sdh_core_qos_max,
PM_QOS_CPUFREQ_MAX, PM_QOS_DEFAULT_VALUE);
sdh_core_qos_min.name = "sdh_tuning_core_min";
pm_qos_add_request(&sdh_core_qos_min,
PM_QOS_CPUFREQ_MIN, PM_QOS_DEFAULT_VALUE);
sdh_ddr_qos_max.name = "sdh_tuning_ddr_max";
pm_qos_add_request(&sdh_ddr_qos_max,
PM_QOS_DDR_DEVFREQ_MAX, PM_QOS_DEFAULT_VALUE);
return 0;
}
arch_initcall(sdh_tuning_init);
int sdh_tunning_savefreq(void)
{
int i;
for (i = 0; i < BOOT_DVFS_MAX; i++) {
if (unlikely(!clk[i])) {
clk[i] = clk_get(NULL, clk_name[i]);
if (!clk[i]) {
pr_err("failed to get clk %s\n", clk_name[i]);
return -1;
}
}
if (!bootfreq[i])
bootfreq[i] = clk_get_rate(clk[i]);
}
#if defined(CONFIG_ARM64) && defined(CONFIG_ASR_DVFS)
return 0;
#else
clk_set_rate(clk[_AXI], minfreq[_AXI]*1000);
pm_qos_update_request(&sdh_core_qos_max, minfreq[_CORE]);
pm_qos_update_request(&sdh_ddr_qos_max, minfreq[_DDR]);
#endif
return 0;
}
#if defined(CONFIG_ARM64) && defined(CONFIG_ASR_DVFS)
int sdh_tunning_updatefreq(unsigned int dvfs_level)
{
long freq;
/* request PM_QOS_CPUFREQ_MAX */
freq = get_dvc_freq(0, dvfs_level);
if (freq >= 0) {
pm_qos_update_request(&sdh_core_qos_max, (s32)(freq/1000));
pm_qos_update_request(&sdh_core_qos_min, (s32)(freq/1000));
} else {
pm_qos_update_request(&sdh_core_qos_max, 0);
pr_err("fail to get CORE freq for dvfs level %d\n", dvfs_level);
}
/* request PM_QOS_DDR_DEVFREQ_MAX */
freq = get_dvc_freq(2, dvfs_level);
if (freq >= 0) {
pm_qos_update_request(&sdh_ddr_qos_max, (s32)(freq/1000));
} else {
pm_qos_update_request(&sdh_ddr_qos_max, 0);
pr_err("fail to get DDR freq for dvfs level %d\n", dvfs_level);
}
return 0;
}
#else
int sdh_tunning_updatefreq(unsigned int dvfs_level)
{
return 0;
}
#endif
int sdh_tunning_restorefreq(void)
{
int i;
pm_qos_update_request(&sdh_core_qos_max, PM_QOS_DEFAULT_VALUE);
pm_qos_update_request(&sdh_ddr_qos_max, PM_QOS_DEFAULT_VALUE);
#if defined(CONFIG_ARM64) && defined(CONFIG_ASR_DVFS)
pm_qos_update_request(&sdh_core_qos_min, PM_QOS_DEFAULT_VALUE);
#endif
for (i = 0; i < BOOT_DVFS_MAX; i++) {
if (!clk[i])
continue;
if (i != _CORE)
clk_set_rate(clk[i], bootfreq[i]);
}
return 0;
}
static unsigned int platvl_min, platvl_max;
void plat_set_vl_min(u32 vl_num)
{
platvl_min = vl_num;
}
unsigned int plat_get_vl_min(void)
{
return platvl_min;
}
void plat_set_vl_max(u32 vl_num)
{
platvl_max = vl_num;
}
unsigned int plat_get_vl_max(void)
{
return platvl_max;
}
static struct cpmsa_dvc_info cpmsadvcinfo;
/*
* This interface will be used by different platform to fill CP DVC info
*/
int fillcpdvcinfo(struct cpmsa_dvc_info *dvc_info)
{
if (!dvc_info)
return -EINVAL;
memcpy(&cpmsadvcinfo, dvc_info, sizeof(struct cpmsa_dvc_info));
return 0;
}
/*
* This interface will be used by telephony to get CP DVC info, and
* they will use ACIPC to pass the info to CP
*/
int getcpdvcinfo(struct cpmsa_dvc_info *dvc_info)
{
if (!dvc_info)
return -EINVAL;
memcpy(dvc_info, &cpmsadvcinfo, sizeof(struct cpmsa_dvc_info));
return 0;
}
EXPORT_SYMBOL(getcpdvcinfo);
static struct ddr_dfc_info ddrdfc;
/*
* This interface will be used by different platform to fill CP ddr dfc info
*/
int fillddrdfcinfo(struct ddr_dfc_info *dfc_info)
{
if (!dfc_info)
return -EINVAL;
memcpy(&ddrdfc, dfc_info, sizeof(struct ddr_dfc_info));
return 0;
}
/*
* This interface will be used by telephony to get CP ddr dfc info, and
* they will use ACIPC to pass the info to CP
*/
int getddrdfcinfo(struct ddr_dfc_info *dfc_info)
{
if (!dfc_info)
return -EINVAL;
memcpy(dfc_info, &ddrdfc, sizeof(struct ddr_dfc_info));
return 0;
}
EXPORT_SYMBOL(getddrdfcinfo);