blob: 30495e2bb06a1200a57a8d16f6cf0dfc9638bdbf [file] [log] [blame]
#include "pwrcal.h"
#include "pwrcal-env.h"
#include "pwrcal-pmu.h"
#include "pwrcal-dfs.h"
#include "pwrcal-clk.h"
#include "pwrcal-vclk.h"
#include "pwrcal-rae.h"
#include "pwrcal-asv.h"
#include <linux/exynos-ss.h>
#define MARGIN_UNIT 6250
int offset_percent;
int set_big_volt;
int set_lit_volt;
int set_int_volt;
int set_mif_volt;
int set_g3d_volt;
int set_disp_volt;
int set_cam_volt;
static int __init get_big_volt(char *str)
{
get_option(&str, &set_big_volt);
return 0;
}
early_param("big", get_big_volt);
static int __init get_lit_volt(char *str)
{
get_option(&str, &set_lit_volt);
return 0;
}
early_param("lit", get_lit_volt);
static int __init get_int_volt(char *str)
{
get_option(&str, &set_int_volt);
return 0;
}
early_param("int", get_int_volt);
static int __init get_mif_volt(char *str)
{
get_option(&str, &set_mif_volt);
return 0;
}
early_param("mif", get_mif_volt);
static int __init get_g3d_volt(char *str)
{
get_option(&str, &set_g3d_volt);
return 0;
}
early_param("g3d", get_g3d_volt);
static int __init get_disp_volt(char *str)
{
get_option(&str, &set_disp_volt);
return 0;
}
early_param("disp", get_disp_volt);
static int __init get_cam_volt(char *str)
{
get_option(&str, &set_cam_volt);
return 0;
}
early_param("cam", get_cam_volt);
static int __init get_offset_volt(char *str)
{
get_option(&str, &offset_percent);
return 0;
}
early_param("volt_offset_percent", get_offset_volt);
static int set_percent_offset(int actual_volt)
{
int margin_volt = 0;
if (offset_percent) {
margin_volt = actual_volt + ((actual_volt * offset_percent)/100);
if (offset_percent < -5) {
if (((actual_volt * offset_percent)/100) % MARGIN_UNIT != 0)
margin_volt -= ((actual_volt * offset_percent)/100) % MARGIN_UNIT;
} else if (offset_percent < 0) {
if (((actual_volt * offset_percent)/100) % MARGIN_UNIT != 0)
margin_volt -= MARGIN_UNIT + ((actual_volt * offset_percent)/100) % MARGIN_UNIT;
} else if (offset_percent <= 5) {
if ((actual_volt * offset_percent/100) % MARGIN_UNIT != 0)
margin_volt += MARGIN_UNIT - ((actual_volt * offset_percent)/100) % MARGIN_UNIT;
} else {
if (((actual_volt * offset_percent)/100) % MARGIN_UNIT != 0)
margin_volt -= ((actual_volt * offset_percent)/100) % MARGIN_UNIT;
}
return margin_volt;
} else {
return actual_volt;
}
}
unsigned int cal_clk_get(char *name)
{
unsigned int ret;
ret = _cal_vclk_get(name);
if (!ret)
ret = _cal_clk_get(name);
return ret;
}
unsigned int cal_clk_is_enabled(unsigned int id)
{
return 0;
}
int cal_clk_setrate(unsigned int id, unsigned long rate)
{
struct vclk *vclk;
struct pwrcal_clk *clk;
vclk = cal_get_vclk(id);
if (vclk)
return vclk_setrate(vclk, rate);
clk = cal_get_clk(id);
if (clk)
return pwrcal_clk_set_rate(clk, (unsigned long long)rate);
return -1;
}
unsigned long cal_clk_getrate(unsigned int id)
{
struct vclk *vclk;
struct pwrcal_clk *clk;
vclk = cal_get_vclk(id);
if (vclk)
return vclk_getrate(vclk);
clk = cal_get_clk(id);
if (clk)
return pwrcal_clk_get_rate(clk);
return 0;
}
int cal_clk_enable(unsigned int id)
{
struct vclk *vclk;
struct pwrcal_clk *clk;
vclk = cal_get_vclk(id);
if (vclk)
return vclk_enable(vclk);
clk = cal_get_clk(id);
if (clk)
return pwrcal_clk_enable(clk);
return -1;
}
int cal_clk_disable(unsigned int id)
{
struct vclk *vclk;
struct pwrcal_clk *clk;
vclk = cal_get_vclk(id);
if (vclk)
return vclk_disable(vclk);
clk = cal_get_clk(id);
if (clk)
return pwrcal_clk_disable(clk);
return -1;
}
int cal_pd_control(unsigned int id, int on)
{
struct cal_pd *pd;
unsigned int index;
if ((id & 0xFFFF0000) != BLKPWR_MAGIC)
return -1;
index = id & 0x0000FFFF;
if (index >= pwrcal_blkpwr_size)
return -1;
pd = pwrcal_blkpwr_list[index];
if (cal_pd_ops.pd_control)
return cal_pd_ops.pd_control(pd, on);
return -1;
}
int cal_pd_status(unsigned int id)
{
struct cal_pd *pd;
unsigned int index;
if ((id & 0xFFFF0000) != BLKPWR_MAGIC)
return -1;
index = id & 0x0000FFFF;
if (index >= pwrcal_blkpwr_size)
return -1;
pd = pwrcal_blkpwr_list[index];
if (cal_pd_ops.pd_status)
return cal_pd_ops.pd_status(pd);
return -1;
}
int cal_pm_enter(int mode)
{
if (cal_pm_ops.pm_enter)
cal_pm_ops.pm_enter(mode);
return 0;
}
int cal_pm_exit(int mode)
{
if (cal_pm_ops.pm_exit)
cal_pm_ops.pm_exit(mode);
return 0;
}
int cal_pm_earlywakeup(int mode)
{
if (cal_pm_ops.pm_earlywakeup)
cal_pm_ops.pm_earlywakeup(mode);
return 0;
}
unsigned int cal_dfs_get(char *name)
{
int id;
for (id = 0; id < vclk_dfs_list_size; id++)
if (!strcmp(vclk_dfs_list[id]->vclk.name, name))
return vclk_group_dfs + id;
return 0xFFFFFFFF;
}
unsigned long cal_dfs_get_max_freq(unsigned int id)
{
struct vclk *vclk;
vclk = cal_get_vclk(id);
if (!vclk)
return 0;
return dfs_get_max_freq(vclk);
}
unsigned long cal_dfs_get_min_freq(unsigned int id)
{
struct vclk *vclk;
vclk = cal_get_vclk(id);
if (!vclk)
return 0;
return dfs_get_min_freq(vclk);
}
int cal_dfs_set_rate(unsigned int id, unsigned long rate)
{
struct pwrcal_vclk_dfs *dfs;
struct vclk *vclk;
unsigned long flag;
int ret = 0;
#ifdef CONFIG_EXYNOS_SNAPSHOT_CLK
const char *name = "cal_dfs_set_rate";
#endif
vclk = cal_get_vclk(id);
if (!vclk)
return -1;
dfs = to_dfs(vclk);
spin_lock_irqsave(dfs->lock, flag);
if (!vclk->ref_count) {
vclk->vfreq = rate;
goto out;
}
exynos_ss_clk(vclk, name, ESS_FLAG_IN);
if (dfs->table->private_trans)
ret = dfs->table->private_trans(vclk->vfreq, rate, dfs->table);
else if (vclk->ops->set_rate)
ret = vclk->ops->set_rate(vclk, rate);
else
ret = -1;
if (!ret) {
vclk->vfreq = rate;
exynos_ss_clk(vclk, name, ESS_FLAG_OUT);
} else
exynos_ss_clk(vclk, name, ESS_FLAG_ON);
out:
spin_unlock_irqrestore(dfs->lock, flag);
return ret;
}
int cal_dfs_set_rate_switch(unsigned int id, unsigned long switch_rate)
{
struct pwrcal_vclk_dfs *dfs;
struct vclk *vclk;
unsigned long flag;
int ret = 0;
vclk = cal_get_vclk(id);
if (!vclk)
return -1;
dfs = to_dfs(vclk);
spin_lock_irqsave(dfs->lock, flag);
if (!vclk->ref_count) {
ret = -1;
goto out;
}
if (dfs->table->private_switch)
ret = dfs->table->private_switch(vclk->vfreq, switch_rate, dfs->table);
else if (vclk->ops->set_rate)
ret = vclk->ops->set_rate(vclk, switch_rate);
else
ret = -1;
if (!ret)
vclk->vfreq = switch_rate;
out:
spin_unlock_irqrestore(dfs->lock, flag);
return ret;
}
unsigned long cal_dfs_cached_get_rate(unsigned int id)
{
struct pwrcal_vclk_dfs *dfs;
struct vclk *vclk;
unsigned long flag;
unsigned long ret = 0;
#ifdef CONFIG_EXYNOS_SNAPSHOT_CLK
const char *name = "cal_dfs_get_rate";
#endif
vclk = cal_get_vclk(id);
if (!vclk)
return 0;
dfs = to_dfs(vclk);
spin_lock_irqsave(dfs->lock, flag);
exynos_ss_clk(vclk, name, ESS_FLAG_IN);
if (!vclk->ref_count) {
pr_err("%s : %s reference count is zero \n", __func__, vclk->name);
exynos_ss_clk(vclk, name, ESS_FLAG_ON);
goto out;
}
ret = vclk->vfreq;
exynos_ss_clk(vclk, name, ESS_FLAG_OUT);
out:
spin_unlock_irqrestore(dfs->lock, flag);
return ret;
}
unsigned long cal_dfs_get_rate(unsigned int id)
{
struct pwrcal_vclk_dfs *dfs;
struct vclk *vclk;
unsigned long flag;
unsigned long ret = 0;
#ifdef CONFIG_EXYNOS_SNAPSHOT_CLK
const char *name = "cal_dfs_get_rate";
#endif
vclk = cal_get_vclk(id);
if (!vclk)
return 0;
dfs = to_dfs(vclk);
spin_lock_irqsave(dfs->lock, flag);
exynos_ss_clk(vclk, name, ESS_FLAG_IN);
if (!vclk->ref_count) {
pr_err("%s : %s reference count is zero \n", __func__, vclk->name);
exynos_ss_clk(vclk, name, ESS_FLAG_ON);
goto out;
}
if (dfs->table->private_getrate)
ret = dfs->table->private_getrate(dfs->table);
else
ret = vclk->ops->get_rate(vclk);
if (ret > 0) {
vclk->vfreq = (unsigned long)ret;
exynos_ss_clk(vclk, name, ESS_FLAG_OUT);
} else
exynos_ss_clk(vclk, name, ESS_FLAG_ON);
out:
spin_unlock_irqrestore(dfs->lock, flag);
return ret;
}
static struct vclk_dfs_ops *get_dfsops(unsigned int id)
{
struct vclk *vclk;
vclk = cal_get_vclk(id);
if (!vclk)
return NULL;
return (to_dfs(vclk))->dfsops;
}
int cal_dfs_get_rate_table(unsigned int id, unsigned long *table)
{
struct vclk_dfs_ops *dfsops = get_dfsops(id);
if (dfsops)
if (dfsops->get_rate_table)
return dfsops->get_rate_table(table);
return 0;
}
int cal_dfs_get_asv_table(unsigned int id, unsigned int *table)
{
struct vclk_dfs_ops *dfsops = get_dfsops(id);
int num_of_entry, i;
int volt_offset = 0;
int org_volt, percent_volt;
if (dfsops->get_margin_param)
volt_offset = dfsops->get_margin_param(id);
if (dfsops->get_asv_table) {
num_of_entry = dfsops->get_asv_table(table);
for (i = 0; i < num_of_entry; i++) {
org_volt = (int)table[i];
percent_volt = set_percent_offset(org_volt);
table[i] = (unsigned int)(percent_volt + volt_offset);
pr_info("L%2d: %7d uV, percent_offset(%d)-> %7d uV, volt_offset(%d uV)-> %7duV\n",
i, org_volt, offset_percent,
percent_volt, volt_offset, table[i]);
}
return num_of_entry;
}
return 0;
}
void cal_dfs_set_volt_margin(unsigned int id, int volt)
{
struct vclk *vclk;
struct pwrcal_vclk_dfs *dfs;
vclk = cal_get_vclk(id);
dfs = to_dfs(vclk);
dfs->volt_margin = volt;
}
unsigned long cal_dfs_get_rate_by_member(unsigned int id,
char *member,
unsigned long rate)
{
struct vclk_dfs_ops *dfsops = get_dfsops(id);
if (dfsops)
if (dfsops->get_target_rate)
return dfsops->get_target_rate(member, rate);
return 0;
}
int cal_dfs_set_ema(unsigned int id, unsigned int volt)
{
struct vclk_dfs_ops *dfsops = get_dfsops(id);
if (dfsops)
if (dfsops->set_ema)
return dfsops->set_ema(volt);
return -1;
}
int cal_dfs_ext_ctrl(unsigned int id,
enum cal_dfs_ext_ops ops,
int para)
{
struct vclk_dfs_ops *dfsops = get_dfsops(id);
if (dfsops) {
switch (ops) {
case cal_dfs_initsmpl:
if (dfsops->init_smpl)
return dfsops->init_smpl();
break;
case cal_dfs_deinitsmpl:
if (dfsops->deinit_smpl)
return dfsops->deinit_smpl();
break;
case cal_dfs_setsmpl:
if (dfsops->set_smpl)
return dfsops->set_smpl();
break;
case cal_dfs_get_smplstatus:
if (dfsops->get_smpl)
return dfsops->get_smpl();
break;
case cal_dfs_dvs:
if (dfsops->dvs)
return dfsops->dvs(para);
break;
case cal_dfs_mif_is_dll_on:
if (dfsops->is_dll_on)
return dfsops->is_dll_on();
break;
case cal_dfs_cpu_idle_clock_down:
if (dfsops->cpu_idle_clock_down)
return dfsops->cpu_idle_clock_down(para);
break;
case cal_dfs_ctrl_clk_gate:
if (dfsops->ctrl_clk_gate)
return dfsops->ctrl_clk_gate(para);
break;
default:
return -1;
}
}
return -1;
}
int cal_dfs_get_rate_asv_table(unsigned int id,
struct dvfs_rate_volt *table)
{
struct vclk *vclk;
struct pwrcal_vclk_dfs *dfs;
int idx;
int num_of_entry;
unsigned long rate[48];
unsigned int volt[48];
int tmp;
vclk = cal_get_vclk(id);
if (!vclk)
return 0;
dfs = to_dfs(vclk);
num_of_entry = cal_dfs_get_rate_table(id, rate);
if (num_of_entry == 0)
return 0;
if (num_of_entry != cal_dfs_get_asv_table(id, volt))
return 0;
for (idx = 0; idx < num_of_entry; idx++) {
table[idx].rate = rate[idx];
tmp = (int)(volt[idx]) + dfs->volt_margin;
table[idx].volt = (unsigned int)tmp;
}
return num_of_entry;
}
unsigned int cal_asv_pmic_info(void)
{
if (cal_asv_ops.asv_pmic_info)
return cal_asv_ops.asv_pmic_info();
return -1;
}
void cal_asv_print_info(void)
{
if (cal_asv_ops.print_asv_info)
cal_asv_ops.print_asv_info();
}
void cal_rcc_print_info(void)
{
if (cal_asv_ops.print_rcc_info)
cal_asv_ops.print_rcc_info();
}
int cal_asv_set_rcc_table(void)
{
if (cal_asv_ops.set_rcc_table)
return cal_asv_ops.set_rcc_table();
return -1;
}
void cal_asv_set_grp(unsigned int id, unsigned int asvgrp)
{
if (cal_asv_ops.set_grp)
cal_asv_ops.set_grp(id, asvgrp);
}
int cal_asv_get_grp(unsigned int id, unsigned int lv)
{
if (cal_asv_ops.get_grp)
return cal_asv_ops.get_grp(id, lv);
return -1;
}
void cal_asv_set_tablever(unsigned int version)
{
if (cal_asv_ops.set_tablever)
cal_asv_ops.set_tablever(version);
}
int cal_asv_get_tablever(void)
{
if (cal_asv_ops.get_tablever)
return cal_asv_ops.get_tablever();
return -1;
}
void cal_asv_set_ssa0(unsigned int id, unsigned int ssa0)
{
if (cal_asv_ops.set_ssa0)
cal_asv_ops.set_ssa0(id, ssa0);
}
int cal_asv_get_ids_info(unsigned int domain)
{
if (cal_asv_ops.get_ids_info)
return cal_asv_ops.get_ids_info(domain);
return -1;
}
int cal_init(void)
{
static int pwrcal_vclk_initialized;
if (pwrcal_vclk_initialized == 1)
return 0;
cal_rae_init();
clk_init();
dfs_init();
vclk_init();
if (cal_asv_ops.asv_init) {
if (cal_asv_ops.asv_init())
return -1;
#ifdef PWRCAL_TARGET_LINUX
if (cal_asv_ops.print_asv_info)
cal_asv_ops.print_asv_info();
#endif
}
if (cal_pm_ops.pm_init)
cal_pm_ops.pm_init();
if (cal_pd_ops.pd_init)
if (cal_pd_ops.pd_init())
return -1;
vclk_unused_disable();
pwrcal_vclk_initialized = 1;
return 0;
}