#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 #include #ifdef CONFIG_PMUCAL_MOD #include "./pmucal_mod/pmucal_system.h" #include "./pmucal_mod/pmucal_local.h" #include "./pmucal_mod/pmucal_cpu.h" #include "./pmucal_mod/pmucal_rae.h" #endif #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) { #ifdef CONFIG_PMUCAL_MOD unsigned int index; if ((id & 0xFFFF0000) != BLKPWR_MAGIC) return -1; index = id & 0x0000FFFF; if (on) return pmucal_local_enable(index); else return pmucal_local_disable(index); #else 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; #endif } int cal_pd_status(unsigned int id) { #ifdef CONFIG_PMUCAL_MOD unsigned int index; if ((id & 0xFFFF0000) != BLKPWR_MAGIC) return -1; index = id & 0x0000FFFF; return pmucal_local_is_enabled(index); #else 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; #endif } int cal_pm_enter(int mode) { #ifdef CONFIG_PMUCAL_MOD return pmucal_system_enter(mode); #else if (cal_pm_ops.pm_enter) cal_pm_ops.pm_enter(mode); return 0; #endif } int cal_pm_exit(int mode) { #ifdef CONFIG_PMUCAL_MOD return pmucal_system_exit(mode); #else if (cal_pm_ops.pm_exit) cal_pm_ops.pm_exit(mode); return 0; #endif } int cal_pm_earlywakeup(int mode) { #ifdef CONFIG_PMUCAL_MOD return pmucal_system_earlywakeup(mode); #else if (cal_pm_ops.pm_earlywakeup) cal_pm_ops.pm_earlywakeup(mode); return 0; #endif } #ifdef CONFIG_PMUCAL_MOD int cal_cpu_enable(unsigned int cpu) { return pmucal_cpu_enable(cpu); } int cal_cpu_disable(unsigned int cpu) { return pmucal_cpu_disable(cpu); } int cal_cpu_status(unsigned int cpu) { return pmucal_cpu_is_enabled(cpu); } int cal_cluster_enable(unsigned int cluster) { return pmucal_cpu_cluster_enable(cluster); } int cal_cluster_disable(unsigned int cluster) { return pmucal_cpu_cluster_disable(cluster); } int cal_cluster_status(unsigned int cluster) { return pmucal_cpu_cluster_is_enabled(cluster); } #endif 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); trace_exynos_clk_in(vclk, __func__); 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); trace_exynos_clk_out(vclk, __func__); } else { exynos_ss_clk(vclk, name, ESS_FLAG_ON); trace_exynos_clk_on(vclk, __func__); } 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); trace_exynos_clk_in(vclk, __func__); if (!vclk->ref_count) { pr_err("%s : %s reference count is zero \n", __func__, vclk->name); exynos_ss_clk(vclk, name, ESS_FLAG_ON); trace_exynos_clk_on(vclk, __func__); goto out; } ret = vclk->vfreq; exynos_ss_clk(vclk, name, ESS_FLAG_OUT); trace_exynos_clk_out(vclk, __func__); 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); trace_exynos_clk_in(vclk, __func__); if (!vclk->ref_count) { pr_err("%s : %s reference count is zero \n", __func__, vclk->name); exynos_ss_clk(vclk, name, ESS_FLAG_ON); trace_exynos_clk_on(vclk, __func__); 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); trace_exynos_clk_out(vclk, __func__); } else { exynos_ss_clk(vclk, name, ESS_FLAG_ON); trace_exynos_clk_on(vclk, __func__); } 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) { 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_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; 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; } 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 __init cal_init(void) { static int pwrcal_vclk_initialized; #ifdef CONFIG_PMUCAL_MOD int ret; #endif 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 } #ifdef CONFIG_PMUCAL_MOD ret = pmucal_rae_init(); if (ret < 0) return ret; ret = pmucal_system_init(); if (ret < 0) return ret; #else if (cal_pm_ops.pm_init) cal_pm_ops.pm_init(); #endif vclk_unused_disable(); #ifdef CONFIG_PMUCAL_MOD ret = pmucal_local_init(); if (ret < 0) return ret; ret = pmucal_cpu_init(); if (ret < 0) return ret; #else if (cal_pd_ops.pd_init) if (cal_pd_ops.pd_init()) return -1; #endif pwrcal_vclk_initialized = 1; return 0; }