77940d8531
Newer versions of Clang tend to apply heavier optimizations than GCC, especially if -mcpu is set. Because we are applying optimizations to the LITTLE core (see b92e1e70898f515646142736df8d72c43e97e251) kernel panics during boot, citing inability to access memory regions on fvmap_init. <6>[ 0.664609] [0: swapper/0: 1] fvmap_init:fvmap initialize 0000000000000000 <0>[ 0.664625] [0: swapper/0: 1] Unable to handle kernel paging request at virtual address ffffff800b2f2402 <2>[ 0.664640] [0: swapper/0: 1] sec_debug_set_extra_info_fault = KERN / 0xffffff800b2f2402 <6>[ 0.664657] [0: swapper/0: 1] search_item_by_key: (FTYPE) extra_info is not ready <2>[ 0.664666] [0: swapper/0: 1] set_item_val: fail to find FTYPE <6>[ 0.664680] [0: swapper/0: 1] search_item_by_key: (FAULT) extra_info is not ready <2>[ 0.664688] [0: swapper/0: 1] set_item_val: fail to find FAULT <6>[ 0.664702] [0: swapper/0: 1] search_item_by_key: (PC) extra_info is not ready <2>[ 0.664710] [0: swapper/0: 1] set_item_val: fail to find PC <6>[ 0.664724] [0: swapper/0: 1] search_item_by_key: (LR) extra_info is not ready <2>[ 0.664732] [0: swapper/0: 1] set_item_val: fail to find LR <1>[ 0.664746] [0: swapper/0: 1] Mem abort info: <1>[ 0.664760] [0: swapper/0: 1] Exception class = DABT (current EL), IL = 32 bits <1>[ 0.664774] [0: swapper/0: 1] SET = 0, FnV = 0 <1>[ 0.664787] [0: swapper/0: 1] EA = 0, S1PTW = 0 <1>[ 0.664799] [0: swapper/0: 1] Data abort info: <1>[ 0.664814] [0: swapper/0: 1] ISV = 0, ISS = 0x00000021 <1>[ 0.664828] [0: swapper/0: 1] CM = 0, WnR = 0 <1>[ 0.664842] [0: swapper/0: 1] swapper pgtable: 4k pages, 39-bit VAs, pgd = ffffff800a739000 <1>[ 0.664856] [0: swapper/0: 1] [ffffff800b2f2402] *pgd=000000097cdfe003, *pud=000000097cdfe003, *pmd=00000009742a9003, *pte=00e800000204b707 <0>[ 0.664884] [0: swapper/0: 1] Internal error: Oops: 96000021 [#1] PREEMPT SMP <4>[ 0.664899] [0: swapper/0: 1] Modules linked in: <0>[ 0.664916] [0: swapper/0: 1] Process swapper/0 (pid: 1, stack limit = 0xffffff80081a8000) <0>[ 0.664936] [0: swapper/0: 1] debug-snapshot: core register saved(CPU:0) <0>[ 0.664950] [0: swapper/0: 1] L2ECTLR_EL1: 0000000000000007 <0>[ 0.664959] [0: swapper/0: 1] L2ECTLR_EL1 valid_bit(30) is NOT set (0x0) <0>[ 0.664978] [0: swapper/0: 1] CPUMERRSR: 0000000008000001, L2MERRSR: 0000000010200c00 <0>[ 0.664992] [0: swapper/0: 1] CPUMERRSR valid_bit(31) is NOT set (0x0) <0>[ 0.665006] [0: swapper/0: 1] L2MERRSR valid_bit(31) is NOT set (0x0) <0>[ 0.665020] [0: swapper/0: 1] debug-snapshot: context saved(CPU:0) <6>[ 0.665088] [0: swapper/0: 1] debug-snapshot: item - log_kevents is disabled <6>[ 0.665112] [0: swapper/0: 1] TIF_FOREIGN_FPSTATE: 0, FP/SIMD depth 0, cpu: 0 <4>[ 0.665130] [0: swapper/0: 1] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.14.113 - Fresh Core-user #1 <4>[ 0.665144] [0: swapper/0: 1] Hardware name: Samsung A50 LTN OPEN rev04 board based on Exynos9610 (DT) <4>[ 0.665160] [0: swapper/0: 1] task: ffffffc8f4ce8000 task.stack: ffffff80081a8000 <4>[ 0.665180] [0: swapper/0: 1] PC is at fvmap_init+0xac/0x2c8 <4>[ 0.665195] [0: swapper/0: 1] LR is at fvmap_init+0x70/0x2c8 <4>[ 0.665211] [0: swapper/0: 1] pc : [<ffffff80085e12b8>] lr : [<ffffff80085e127c>] pstate: 20400145 <4>[ 0.665225] [0: swapper/0: 1] sp : ffffff80081abb80 <4>[ 0.665238] [0: swapper/0: 1] x29: ffffff80081abbb0 x28: 0000000000000000 <4>[ 0.665256] [0: swapper/0: 1] x27: ffffff8009efc000 x26: 0000000000000000 <4>[ 0.665273] [0: swapper/0: 1] x25: ffffff800b2e5970 x24: ffffff8008fa7222 <4>[ 0.665291] [0: swapper/0: 1] x23: ffffff800b2e5a60 x22: ffffff8009efb000 <4>[ 0.665307] [0: swapper/0: 1] x21: ffffffc8f3480000 x20: ffffff800b2e0000 <4>[ 0.665324] [0: swapper/0: 1] x19: ffffff800b2f2400 x18: 0000000000000000 <4>[ 0.665341] [0: swapper/0: 1] x17: ffffff8009bff23c x16: 0000000000000000 <4>[ 0.665358] [0: swapper/0: 1] x15: 00000000000000c6 x14: 0000000000000054 <4>[ 0.665375] [0: swapper/0: 1] x13: 000000000000d7b8 x12: 0000000000000000 <4>[ 0.665392] [0: swapper/0: 1] x11: 0000000000000000 x10: ffffffc8f3480000 <4>[ 0.665409] [0: swapper/0: 1] x9 : ffffff800b2f2400 x8 : 0000000000000000 <4>[ 0.665426] [0: swapper/0: 1] x7 : 5b20205d39303634 x6 : ffffffc0117d09b7 <4>[ 0.665443] [0: swapper/0: 1] x5 : 0000000000000001 x4 : 000000000000000c <4>[ 0.665459] [0: swapper/0: 1] x3 : 0000000000000a30 x2 : ffffffffffffffce <4>[ 0.665476] [0: swapper/0: 1] x1 : 000000000b040000 x0 : 000000000000000a Since we need these optimizations for performance reasons, the only way to resolve this is to solve the issue. Samsung already did something similar to cal-if before. We only needed to make fvmap_header/header volatile and has been tested to work. Signed-off-by: John Vincent <git@tensevntysevn.cf> Change-Id: Ic419135d4a80cbe15f0fa71dc59cc6efa73d6141
510 lines
15 KiB
C
Executable file
510 lines
15 KiB
C
Executable file
#include <linux/types.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/io.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/kobject.h>
|
|
#include <soc/samsung/cal-if.h>
|
|
#include <linux/module.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <soc/samsung/fvmap.h>
|
|
|
|
#include "cmucal.h"
|
|
#include "vclk.h"
|
|
#include "ra.h"
|
|
|
|
#define FVMAP_SIZE (SZ_8K)
|
|
#define STEP_UV (6250)
|
|
|
|
void __iomem *fvmap_base;
|
|
void __iomem *sram_fvmap_base;
|
|
|
|
static int init_margin_table[MAX_MARGIN_ID];
|
|
static int percent_margin_table[MAX_MARGIN_ID];
|
|
|
|
static int margin_mif;
|
|
static int margin_int;
|
|
static int margin_cpucl0;
|
|
static int margin_cpucl1;
|
|
static int margin_cpucl2;
|
|
static int margin_npu;
|
|
static int margin_dsu;
|
|
static int margin_disp;
|
|
static int margin_aud;
|
|
static int margin_cp_cpu;
|
|
static int margin_cp;
|
|
static int margin_cp_em;
|
|
static int margin_cp_mcw;
|
|
static int margin_g3d;
|
|
static int margin_intcam;
|
|
static int margin_cam;
|
|
static int margin_csis;
|
|
static int margin_isp;
|
|
static int margin_mfc0;
|
|
static int margin_mfc1;
|
|
static int margin_intsci;
|
|
static int margin_dsp;
|
|
static int margin_dnc;
|
|
static int margin_gnss;
|
|
static int margin_alive;
|
|
static int margin_chub;
|
|
static int margin_vts;
|
|
static int margin_hsi0;
|
|
|
|
static int margin_intg3d;
|
|
static int margin_wlbt;
|
|
|
|
static int volt_offset_percent;
|
|
|
|
module_param(margin_mif, int, 0);
|
|
module_param(margin_int, int, 0);
|
|
module_param(margin_cpucl0, int, 0);
|
|
module_param(margin_cpucl1, int, 0);
|
|
module_param(margin_cpucl2, int, 0);
|
|
module_param(margin_npu, int, 0);
|
|
module_param(margin_dsu, int, 0);
|
|
module_param(margin_disp, int, 0);
|
|
module_param(margin_aud, int, 0);
|
|
module_param(margin_cp_cpu, int, 0);
|
|
module_param(margin_cp, int, 0);
|
|
module_param(margin_cp_em, int, 0);
|
|
module_param(margin_cp_mcw, int, 0);
|
|
module_param(margin_g3d, int, 0);
|
|
module_param(margin_intcam, int, 0);
|
|
module_param(margin_cam, int, 0);
|
|
module_param(margin_csis, int, 0);
|
|
module_param(margin_isp, int, 0);
|
|
module_param(margin_mfc0, int, 0);
|
|
module_param(margin_mfc1, int, 0);
|
|
module_param(margin_intsci, int, 0);
|
|
module_param(margin_dsp, int, 0);
|
|
module_param(margin_dnc, int, 0);
|
|
module_param(margin_gnss, int, 0);
|
|
module_param(margin_alive, int, 0);
|
|
module_param(margin_chub, int, 0);
|
|
module_param(margin_vts, int, 0);
|
|
module_param(margin_hsi0, int, 0);
|
|
|
|
module_param(margin_intg3d, int, 0);
|
|
module_param(margin_wlbt, int, 0);
|
|
module_param(volt_offset_percent, int, 0);
|
|
|
|
void margin_table_init(void)
|
|
{
|
|
init_margin_table[MARGIN_MIF] = margin_mif;
|
|
init_margin_table[MARGIN_INT] = margin_int;
|
|
init_margin_table[MARGIN_CPUCL0] = margin_cpucl0;
|
|
init_margin_table[MARGIN_CPUCL1] = margin_cpucl1;
|
|
init_margin_table[MARGIN_CPUCL2] = margin_cpucl2;
|
|
init_margin_table[MARGIN_NPU] = margin_npu;
|
|
init_margin_table[MARGIN_DSU] = margin_dsu;
|
|
init_margin_table[MARGIN_DISP] = margin_disp;
|
|
init_margin_table[MARGIN_AUD] = margin_aud;
|
|
init_margin_table[MARGIN_CP_CPU] = margin_cp_cpu;
|
|
init_margin_table[MARGIN_CP] = margin_cp;
|
|
init_margin_table[MARGIN_CP_EM] = margin_cp_em;
|
|
init_margin_table[MARGIN_CP_MCW] = margin_cp_mcw;
|
|
init_margin_table[MARGIN_G3D] = margin_g3d;
|
|
init_margin_table[MARGIN_INTCAM] = margin_intcam;
|
|
init_margin_table[MARGIN_CAM] = margin_cam;
|
|
init_margin_table[MARGIN_CSIS] = margin_csis;
|
|
init_margin_table[MARGIN_ISP] = margin_isp;
|
|
init_margin_table[MARGIN_MFC0] = margin_mfc0;
|
|
init_margin_table[MARGIN_MFC1] = margin_mfc1;
|
|
init_margin_table[MARGIN_INTSCI] = margin_intsci;
|
|
init_margin_table[MARGIN_DSP] = margin_dsp;
|
|
init_margin_table[MARGIN_DNC] = margin_dnc;
|
|
init_margin_table[MARGIN_GNSS] = margin_gnss;
|
|
init_margin_table[MARGIN_ALIVE] = margin_alive;
|
|
init_margin_table[MARGIN_CHUB] = margin_chub;
|
|
init_margin_table[MARGIN_VTS] = margin_vts;
|
|
init_margin_table[MARGIN_HSI0] = margin_hsi0;
|
|
|
|
init_margin_table[MARGIN_INTG3D] = margin_intg3d;
|
|
init_margin_table[MARGIN_WLBT] = margin_wlbt;
|
|
}
|
|
|
|
int fvmap_set_raw_voltage_table(unsigned int id, int uV)
|
|
{
|
|
struct fvmap_header *fvmap_header;
|
|
struct rate_volt_header *fv_table;
|
|
int num_of_lv;
|
|
int idx, i;
|
|
|
|
idx = GET_IDX(id);
|
|
|
|
fvmap_header = sram_fvmap_base;
|
|
fv_table = sram_fvmap_base + fvmap_header[idx].o_ratevolt;
|
|
num_of_lv = fvmap_header[idx].num_of_lv;
|
|
|
|
for (i = 0; i < num_of_lv; i++)
|
|
fv_table->table[i].volt += uV / STEP_UV;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int get_vclk_id_from_margin_id(int margin_id)
|
|
{
|
|
int size = cmucal_get_list_size(ACPM_VCLK_TYPE);
|
|
int i;
|
|
struct vclk *vclk;
|
|
|
|
for (i = 0; i < size; i++) {
|
|
vclk = cmucal_get_node(ACPM_VCLK_TYPE | i);
|
|
|
|
if (vclk->margin_id == margin_id)
|
|
return i;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
#define attr_percent(margin_id, type) \
|
|
static ssize_t show_##type##_percent \
|
|
(struct kobject *kobj, struct kobj_attribute *attr, char *buf) \
|
|
{ \
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", percent_margin_table[margin_id]); \
|
|
} \
|
|
\
|
|
static ssize_t store_##type##_percent \
|
|
(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) \
|
|
{ \
|
|
int input, vclk_id; \
|
|
\
|
|
if (!sscanf(buf, "%d", &input)) \
|
|
return -EINVAL; \
|
|
\
|
|
if (input < -100 || input > 100) \
|
|
return -EINVAL; \
|
|
\
|
|
vclk_id = get_vclk_id_from_margin_id(margin_id); \
|
|
if (vclk_id == -EINVAL) \
|
|
return vclk_id; \
|
|
percent_margin_table[margin_id] = input; \
|
|
cal_dfs_set_volt_margin(vclk_id | ACPM_VCLK_TYPE, input); \
|
|
\
|
|
return count; \
|
|
} \
|
|
\
|
|
static struct kobj_attribute type##_percent = \
|
|
__ATTR(type##_percent, 0600, \
|
|
show_##type##_percent, store_##type##_percent)
|
|
|
|
attr_percent(MARGIN_MIF, mif_margin);
|
|
attr_percent(MARGIN_INT, int_margin);
|
|
attr_percent(MARGIN_CPUCL0, cpucl0_margin);
|
|
attr_percent(MARGIN_CPUCL1, cpucl1_margin);
|
|
attr_percent(MARGIN_CPUCL2, cpucl2_margin);
|
|
attr_percent(MARGIN_NPU, npu_margin);
|
|
attr_percent(MARGIN_DSU, dsu_margin);
|
|
attr_percent(MARGIN_DISP, disp_margin);
|
|
attr_percent(MARGIN_AUD, aud_margin);
|
|
attr_percent(MARGIN_CP_CPU, cp_cpu_margin);
|
|
attr_percent(MARGIN_CP, cp_margin);
|
|
attr_percent(MARGIN_CP_EM, cp_em_margin);
|
|
attr_percent(MARGIN_CP_MCW, cp_mcw_margin);
|
|
attr_percent(MARGIN_G3D, g3d_margin);
|
|
attr_percent(MARGIN_INTCAM, intcam_margin);
|
|
attr_percent(MARGIN_CAM, cam_margin);
|
|
attr_percent(MARGIN_CSIS, csis_margin);
|
|
attr_percent(MARGIN_ISP, isp_margin);
|
|
attr_percent(MARGIN_MFC0, mfc0_margin);
|
|
attr_percent(MARGIN_MFC1, mfc1_margin);
|
|
attr_percent(MARGIN_INTSCI, intsci_margin);
|
|
attr_percent(MARGIN_DSP, dsp_margin);
|
|
attr_percent(MARGIN_DNC, dnc_margin);
|
|
attr_percent(MARGIN_GNSS, gnss_margin);
|
|
attr_percent(MARGIN_ALIVE, alive_margin);
|
|
attr_percent(MARGIN_CHUB, chub_margin);
|
|
attr_percent(MARGIN_VTS, vts_margin);
|
|
attr_percent(MARGIN_HSI0, hsi0_margin);
|
|
|
|
attr_percent(MARGIN_INTG3D, intg3d_margin);
|
|
attr_percent(MARGIN_WLBT, wlbt_margin);
|
|
|
|
static struct attribute *percent_margin_attrs[] = {
|
|
&mif_margin_percent.attr,
|
|
&int_margin_percent.attr,
|
|
&cpucl0_margin_percent.attr,
|
|
&cpucl1_margin_percent.attr,
|
|
&cpucl2_margin_percent.attr,
|
|
&npu_margin_percent.attr,
|
|
&dsu_margin_percent.attr,
|
|
&disp_margin_percent.attr,
|
|
&aud_margin_percent.attr,
|
|
&cp_cpu_margin_percent.attr,
|
|
&cp_margin_percent.attr,
|
|
&cp_em_margin_percent.attr,
|
|
&cp_mcw_margin_percent.attr,
|
|
&g3d_margin_percent.attr,
|
|
&intcam_margin_percent.attr,
|
|
&cam_margin_percent.attr,
|
|
&csis_margin_percent.attr,
|
|
&isp_margin_percent.attr,
|
|
&mfc0_margin_percent.attr,
|
|
&mfc1_margin_percent.attr,
|
|
&intsci_margin_percent.attr,
|
|
&dsp_margin_percent.attr,
|
|
&dnc_margin_percent.attr,
|
|
&gnss_margin_percent.attr,
|
|
&alive_margin_percent.attr,
|
|
&chub_margin_percent.attr,
|
|
&vts_margin_percent.attr,
|
|
&hsi0_margin_percent.attr,
|
|
|
|
&intg3d_margin_percent.attr,
|
|
&wlbt_margin_percent.attr,
|
|
NULL,
|
|
};
|
|
|
|
static const struct attribute_group percent_margin_group = {
|
|
.attrs = percent_margin_attrs,
|
|
};
|
|
|
|
unsigned int dvfs_calibrate_voltage(unsigned int rate_target, unsigned int rate_up,
|
|
unsigned int rate_down, unsigned int volt_up, unsigned int volt_down)
|
|
{
|
|
unsigned int volt_diff_step;
|
|
unsigned int rate_diff;
|
|
unsigned int rate_per_step;
|
|
unsigned int ret;
|
|
|
|
if (rate_up < 0x100)
|
|
return volt_down * STEP_UV;
|
|
|
|
if (rate_down == 0)
|
|
return volt_up * STEP_UV;
|
|
|
|
if ((rate_up == rate_down) || (volt_up == volt_down))
|
|
return volt_up * STEP_UV;
|
|
|
|
volt_diff_step = (volt_up - volt_down);
|
|
rate_diff = rate_up - rate_down;
|
|
rate_per_step = rate_diff / volt_diff_step;
|
|
ret = (unsigned int)((volt_up - ((rate_up - rate_target) / rate_per_step)) + 0) * STEP_UV;
|
|
|
|
return ret;
|
|
}
|
|
|
|
int fvmap_get_freq_volt_table(unsigned int id, void *freq_volt_table, unsigned int table_size)
|
|
{
|
|
struct fvmap_header *fvmap_header = fvmap_base;
|
|
struct rate_volt_header *fv_table;
|
|
int idx, i, j;
|
|
int num_of_lv;
|
|
unsigned int target, rate_up, rate_down, volt_up, volt_down;
|
|
struct freq_volt *table = (struct freq_volt *)freq_volt_table;
|
|
|
|
if (!IS_ACPM_VCLK(id))
|
|
return -EINVAL;
|
|
|
|
if (!table)
|
|
return -ENOMEM;
|
|
|
|
idx = GET_IDX(id);
|
|
|
|
fvmap_header = fvmap_base;
|
|
fv_table = fvmap_base + fvmap_header[idx].o_ratevolt;
|
|
num_of_lv = fvmap_header[idx].num_of_lv;
|
|
|
|
for (i = 0; i < table_size; i++) {
|
|
for (j = 1; j < num_of_lv; j++) {
|
|
rate_up = fv_table->table[j - 1].rate;
|
|
rate_down = fv_table->table[j].rate;
|
|
if ((table[i].rate <= rate_up) && (table[i].rate >= rate_down)) {
|
|
volt_up = fv_table->table[j - 1].volt;
|
|
volt_down = fv_table->table[j].volt;
|
|
target = table[i].rate;
|
|
table[i].volt = dvfs_calibrate_voltage(target,
|
|
rate_up, rate_down, volt_up, volt_down);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (table[i].volt == 0) {
|
|
if (table[i].rate > fv_table->table[0].rate)
|
|
table[i].volt = fv_table->table[0].volt * STEP_UV;
|
|
else if (table[i].rate < fv_table->table[num_of_lv - 1].rate)
|
|
table[i].volt = fv_table->table[num_of_lv - 1].volt * STEP_UV;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(fvmap_get_freq_volt_table);
|
|
|
|
int fvmap_get_voltage_table(unsigned int id, unsigned int *table)
|
|
{
|
|
struct fvmap_header *fvmap_header = fvmap_base;
|
|
struct rate_volt_header *fv_table;
|
|
int idx, i;
|
|
int num_of_lv;
|
|
|
|
if (!IS_ACPM_VCLK(id))
|
|
return 0;
|
|
|
|
idx = GET_IDX(id);
|
|
|
|
fvmap_header = fvmap_base;
|
|
fv_table = fvmap_base + fvmap_header[idx].o_ratevolt;
|
|
num_of_lv = fvmap_header[idx].num_of_lv;
|
|
|
|
for (i = 0; i < num_of_lv; i++)
|
|
table[i] = fv_table->table[i].volt * STEP_UV;
|
|
|
|
return num_of_lv;
|
|
|
|
}
|
|
EXPORT_SYMBOL_GPL(fvmap_get_voltage_table);
|
|
|
|
int fvmap_get_raw_voltage_table(unsigned int id)
|
|
{
|
|
struct fvmap_header *fvmap_header;
|
|
struct rate_volt_header *fv_table;
|
|
int idx, i;
|
|
int num_of_lv;
|
|
unsigned int table[20];
|
|
|
|
idx = GET_IDX(id);
|
|
|
|
fvmap_header = sram_fvmap_base;
|
|
fv_table = sram_fvmap_base + fvmap_header[idx].o_ratevolt;
|
|
num_of_lv = fvmap_header[idx].num_of_lv;
|
|
|
|
for (i = 0; i < num_of_lv; i++)
|
|
table[i] = fv_table->table[i].volt * STEP_UV;
|
|
|
|
for (i = 0; i < num_of_lv; i++)
|
|
printk("dvfs id : %d %d Khz : %d uv\n", ACPM_VCLK_TYPE | id, fv_table->table[i].rate, table[i]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void fvmap_copy_from_sram(void __iomem *map_base, void __iomem *sram_base)
|
|
{
|
|
volatile struct fvmap_header *fvmap_header, *header;
|
|
struct rate_volt_header *old, *new;
|
|
struct clocks *clks;
|
|
struct pll_header *plls;
|
|
struct vclk *vclk;
|
|
unsigned int member_addr;
|
|
unsigned int blk_idx;
|
|
int size, margin;
|
|
int i, j;
|
|
|
|
fvmap_header = map_base;
|
|
header = sram_base;
|
|
|
|
size = cmucal_get_list_size(ACPM_VCLK_TYPE);
|
|
|
|
for (i = 0; i < size; i++) {
|
|
/* load fvmap info */
|
|
fvmap_header[i].domain_id = header[i].domain_id;
|
|
fvmap_header[i].num_of_lv = header[i].num_of_lv;
|
|
fvmap_header[i].num_of_members = header[i].num_of_members;
|
|
fvmap_header[i].num_of_pll = header[i].num_of_pll;
|
|
fvmap_header[i].num_of_mux = header[i].num_of_mux;
|
|
fvmap_header[i].num_of_div = header[i].num_of_div;
|
|
fvmap_header[i].o_famrate = header[i].o_famrate;
|
|
fvmap_header[i].init_lv = header[i].init_lv;
|
|
fvmap_header[i].num_of_child = header[i].num_of_child;
|
|
fvmap_header[i].parent_id = header[i].parent_id;
|
|
fvmap_header[i].parent_offset = header[i].parent_offset;
|
|
fvmap_header[i].block_addr[0] = header[i].block_addr[0];
|
|
fvmap_header[i].block_addr[1] = header[i].block_addr[1];
|
|
fvmap_header[i].block_addr[2] = header[i].block_addr[2];
|
|
fvmap_header[i].o_members = header[i].o_members;
|
|
fvmap_header[i].o_ratevolt = header[i].o_ratevolt;
|
|
fvmap_header[i].o_tables = header[i].o_tables;
|
|
|
|
vclk = cmucal_get_node(ACPM_VCLK_TYPE | i);
|
|
if (vclk == NULL)
|
|
continue;
|
|
#ifdef CONFIG_EXYNOS_DEBUG_INFO
|
|
pr_info("domain_id : %s - id : %x\n",
|
|
vclk->name, fvmap_header[i].domain_id);
|
|
pr_info(" num_of_lv : %d\n", fvmap_header[i].num_of_lv);
|
|
pr_info(" num_of_members : %d\n", fvmap_header[i].num_of_members);
|
|
#endif
|
|
old = sram_base + fvmap_header[i].o_ratevolt;
|
|
new = map_base + fvmap_header[i].o_ratevolt;
|
|
|
|
margin = init_margin_table[vclk->margin_id];
|
|
if (margin)
|
|
cal_dfs_set_volt_margin(i | ACPM_VCLK_TYPE, margin);
|
|
|
|
if (volt_offset_percent) {
|
|
if ((volt_offset_percent < 100) && (volt_offset_percent > -100)) {
|
|
percent_margin_table[i] = volt_offset_percent;
|
|
cal_dfs_set_volt_margin(i | ACPM_VCLK_TYPE, volt_offset_percent);
|
|
}
|
|
}
|
|
|
|
for (j = 0; j < fvmap_header[i].num_of_members; j++) {
|
|
clks = sram_base + fvmap_header[i].o_members;
|
|
|
|
if (j < fvmap_header[i].num_of_pll) {
|
|
plls = sram_base + clks->addr[j];
|
|
member_addr = plls->addr - 0x90000000;
|
|
} else {
|
|
|
|
member_addr = (clks->addr[j] & ~0x3) & 0xffff;
|
|
blk_idx = clks->addr[j] & 0x3;
|
|
|
|
member_addr |= ((fvmap_header[i].block_addr[blk_idx]) << 16) - 0x90000000;
|
|
}
|
|
|
|
vclk->list[j] = cmucal_get_id_by_addr(member_addr);
|
|
#ifdef CONFIG_EXYNOS_DEBUG_INFO
|
|
if (vclk->list[j] == INVALID_CLK_ID)
|
|
pr_info(" Invalid addr :0x%x\n", member_addr);
|
|
else
|
|
pr_info(" DVFS CMU addr:0x%x\n", member_addr);
|
|
#endif
|
|
}
|
|
#ifdef CONFIG_EXYNOS_DEBUG_INFO
|
|
for (j = 0; j < fvmap_header[i].num_of_lv; j++) {
|
|
new->table[j].rate = old->table[j].rate;
|
|
new->table[j].volt = old->table[j].volt;
|
|
pr_info(" lv : [%7d], volt = %d uV (%d %%) \n",
|
|
new->table[j].rate, new->table[j].volt * STEP_UV,
|
|
volt_offset_percent);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
int fvmap_init(void __iomem *sram_base)
|
|
{
|
|
void __iomem *map_base;
|
|
struct kobject *kobj;
|
|
|
|
map_base = kzalloc(FVMAP_SIZE, GFP_KERNEL);
|
|
|
|
fvmap_base = map_base;
|
|
sram_fvmap_base = sram_base;
|
|
pr_info("%s:fvmap initialize %p\n", __func__, sram_base);
|
|
margin_table_init();
|
|
fvmap_copy_from_sram(map_base, sram_base);
|
|
|
|
/* percent margin for each doamin at runtime */
|
|
kobj = kobject_create_and_add("percent_margin", kernel_kobj);
|
|
if (!kobj)
|
|
pr_err("Fail to create percent_margin kboject\n");
|
|
|
|
if (sysfs_create_group(kobj, &percent_margin_group))
|
|
pr_err("Fail to create percent_margin group\n");
|
|
|
|
kobj = kobject_create_and_add("asv-g", kernel_kobj);
|
|
if (!kobj)
|
|
pr_err("Fail to create asv-g kboject\n");
|
|
|
|
if (sysfs_create_group(kobj, &asv_g_spec_grp))
|
|
pr_err("Fail to create asv_g_spec group\n");
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(fvmap_init);
|
|
|
|
MODULE_LICENSE("GPL");
|