blob: dcc6ac53fbe3711b903fea2c6cbc9e1f1c24755b [file] [log] [blame]
/*
* linux/drivers/clk/mmp/dvfs-asr1901.c
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/mfd/88pm80x.h>
#include <linux/mfd/88pm801.h>
#include <linux/clk/mmp.h>
#include <linux/clk/dvfs-dvc.h>
#include <linux/clk/mmpcpdvc.h>
#include <linux/cputype.h>
#include <soc/asr/addr-map.h>
#ifdef CONFIG_OPTEE
#include <linux/asr_tee_sip.h>
#endif
enum dvfs_comp{
CORE = 0,
DDR,
AXI,
SDH0,
SDH1,
ETH,
VM_RAIL_MAX,
};
#define VL_MAX MAX_PMIC_LEVEL
#define ACTIVE_RAIL_FLAG (AFFECT_RAIL_ACTIVE)
#define ACTIVE_M2_RAIL_FLAG (AFFECT_RAIL_ACTIVE | AFFECT_RAIL_M2)
#define ACTIVE_M2_D1P_RAIL_FLAG \
(AFFECT_RAIL_ACTIVE | AFFECT_RAIL_M2 | AFFECT_RAIL_D1P)
#define APMU_GEU 0x068
#define BANK3_fUSE_31_00 0x280
#define BANK3_fUSE_63_32 0x284
#define BANK3_fUSE_95_64 0x288
#define BANK3_fUSE_127_96 0x28C
#define BANK3_fUSE_159_128 0x290
#define BANK3_fUSE_191_160 0x294
#define BANK3_fUSE_223_192 0x298
#define BANK3_fUSE_255_224 0x29C
#define sdh_dvfs {DUMMY_VL_TO_KHZ(VL0), DUMMY_VL_TO_KHZ(VL1),DUMMY_VL_TO_KHZ(VL2), DUMMY_VL_TO_KHZ(VL3)},\
{DUMMY_VL_TO_KHZ(VL0), DUMMY_VL_TO_KHZ(VL1),DUMMY_VL_TO_KHZ(VL2), DUMMY_VL_TO_KHZ(VL3)}
#define eth_dvfs {DUMMY_VL_TO_KHZ(VL0), DUMMY_VL_TO_KHZ(VL1),DUMMY_VL_TO_KHZ(VL2), DUMMY_VL_TO_KHZ(VL3)}
unsigned long (*freqs_cmb)[VL_MAX];
extern enum max_corefreq_type max_corefreq_mode;
//static bool asr1901_a2plus_flag;
/* components frequency combination for different profile ranges */
static unsigned long freqs_cmb_asr1901_1475M[][VM_RAIL_MAX][VL_MAX] = {
/* VL0 VL1 VL2 VL3 */
[0] = {
{ 1248000, 1475000, 1475000, 1475000 }, /* CORE */
{ 800000, 1066000, 1600000, 2000000 }, /* DDR */
{ 208000, 312000, 312000, 416000 }, /* AXI */
sdh_dvfs,
eth_dvfs,
},
[1] = {
{ 1248000, 1475000, 1475000, 1475000 }, /* CORE */
{ 800000, 1066000, 1600000, 2000000 }, /* DDR */
{ 208000, 312000, 312000, 416000 }, /* AXI */
sdh_dvfs,
eth_dvfs,
},
[2] = {
{ 1248000, 1475000, 1475000, 1475000 }, /* CORE */
{ 800000, 1066000, 1600000, 2000000 }, /* DDR */
{ 208000, 312000, 312000, 416000 }, /* AXI */
sdh_dvfs,
eth_dvfs,
},
[3] = {
{ 1248000, 1475000, 1475000, 1475000 }, /* CORE */
{ 800000, 1066000, 1600000, 2000000 }, /* DDR */
{ 208000, 312000, 312000, 416000 }, /* AXI */
sdh_dvfs,
eth_dvfs,
},
[4] = {
{ 1248000, 1475000, 1475000, 1475000 }, /* CORE */
{ 800000, 1066000, 1600000, 2000000 }, /* DDR */
{ 208000, 312000, 312000, 416000 }, /* AXI */
sdh_dvfs,
eth_dvfs,
},
[5] = {
{ 1248000, 1475000, 1475000, 1475000 }, /* CORE */
{ 800000, 1066000, 1600000, 2000000 }, /* DDR */
{ 208000, 312000, 312000, 416000 }, /* AXI */
sdh_dvfs,
eth_dvfs,
},
[6] = {
{ 1248000, 1475000, 1475000, 1475000 }, /* CORE */
{ 800000, 1066000, 1600000, 2000000 }, /* DDR */
{ 208000, 312000, 312000, 416000 }, /* AXI */
sdh_dvfs,
eth_dvfs,
},
[7] = {
{ 1248000, 1475000, 1475000, 1475000 }, /* CORE */
{ 800000, 1066000, 1600000, 2000000 }, /* DDR */
{ 208000, 312000, 312000, 416000 }, /* AXI */
sdh_dvfs,
eth_dvfs,
},
[8] = {
{ 1248000, 1475000, 1475000, 1475000 }, /* CORE */
{ 800000, 1066000, 1600000, 2000000 }, /* DDR */
{ 208000, 312000, 312000, 416000 }, /* AXI */
sdh_dvfs,
eth_dvfs,
},
};
static struct cpmsa_dvc_info cpmsa_dvc_info_1901_1475SVC[] = {
[0] = {
.cpdvcinfo[0] = {624, VL0},
.cpdvcinfo[1] = {832, VL0},
.cpdvcinfo[2] = {1248,VL1},
.cpdvcinfo[3] = {1500,VL3},
.cpaxidvcinfo[0] = {832, VL0}, /* for bx2 */
.cpaxidvcinfo[1] = {1248, VL1}, /* for bx2 */
.msadvcvl[0] = {416, VL1},
.msadvcvl[1] = {491, VL1},
.msadvcvl[2] = {624, VL3},
},
[1] = {
.cpdvcinfo[0] = {624, VL0},
.cpdvcinfo[1] = {832, VL0},
.cpdvcinfo[2] = {1248,VL1},
.cpdvcinfo[3] = {1500,VL3},
.cpaxidvcinfo[0] = {832, VL0}, /* for bx2 */
.cpaxidvcinfo[1] = {1248, VL1}, /* for bx2 */
.msadvcvl[0] = {416, VL1},
.msadvcvl[1] = {491, VL1},
.msadvcvl[2] = {624, VL3},
},
[2] = {
.cpdvcinfo[0] = {624, VL0},
.cpdvcinfo[1] = {832, VL0},
.cpdvcinfo[2] = {1248,VL1},
.cpdvcinfo[3] = {1500,VL3},
.cpaxidvcinfo[0] = {832, VL0}, /* for bx2 */
.cpaxidvcinfo[1] = {1248, VL1}, /* for bx2 */
.msadvcvl[0] = {416, VL1},
.msadvcvl[1] = {491, VL1},
.msadvcvl[2] = {624, VL3},
},
[3] = {
.cpdvcinfo[0] = {624, VL0},
.cpdvcinfo[1] = {832, VL0},
.cpdvcinfo[2] = {1248,VL1},
.cpdvcinfo[3] = {1500,VL3},
.cpaxidvcinfo[0] = {832, VL0}, /* for bx2 */
.cpaxidvcinfo[1] = {1248, VL1}, /* for bx2 */
.msadvcvl[0] = {416, VL1},
.msadvcvl[1] = {491, VL1},
.msadvcvl[2] = {624, VL3},
},
[4] = {
.cpdvcinfo[0] = {624, VL0},
.cpdvcinfo[1] = {832, VL0},
.cpdvcinfo[2] = {1248,VL1},
.cpdvcinfo[3] = {1500,VL3},
.cpaxidvcinfo[0] = {832, VL0}, /* for bx2 */
.cpaxidvcinfo[1] = {1248, VL1}, /* for bx2 */
.msadvcvl[0] = {416, VL1},
.msadvcvl[1] = {491, VL1},
.msadvcvl[2] = {624, VL3},
},
[5] = {
.cpdvcinfo[0] = {624, VL0},
.cpdvcinfo[1] = {832, VL0},
.cpdvcinfo[2] = {1248,VL1},
.cpdvcinfo[3] = {1500,VL3},
.cpaxidvcinfo[0] = {832, VL0}, /* for bx2 */
.cpaxidvcinfo[1] = {1248, VL1}, /* for bx2 */
.msadvcvl[0] = {416, VL1},
.msadvcvl[1] = {491, VL1},
.msadvcvl[2] = {624, VL3},
},
[6] = {
.cpdvcinfo[0] = {624, VL0},
.cpdvcinfo[1] = {832, VL0},
.cpdvcinfo[2] = {1248,VL1},
.cpdvcinfo[3] = {1500,VL3},
.cpaxidvcinfo[0] = {832, VL0}, /* for bx2 */
.cpaxidvcinfo[1] = {1248, VL1}, /* for bx2 */
.msadvcvl[0] = {416, VL1},
.msadvcvl[1] = {491, VL1},
.msadvcvl[2] = {624, VL3},
},
[7] = {
.cpdvcinfo[0] = {624, VL0},
.cpdvcinfo[1] = {832, VL0},
.cpdvcinfo[2] = {1248,VL1},
.cpdvcinfo[3] = {1500,VL3},
.cpaxidvcinfo[0] = {832, VL0}, /* for bx2 */
.cpaxidvcinfo[1] = {1248, VL1}, /* for bx2 */
.msadvcvl[0] = {416, VL1},
.msadvcvl[1] = {491, VL1},
.msadvcvl[2] = {624, VL3},
},
[8] = {
.cpdvcinfo[0] = {624, VL0},
.cpdvcinfo[1] = {832, VL0},
.cpdvcinfo[2] = {1248,VL1},
.cpdvcinfo[3] = {1500,VL3},
.cpaxidvcinfo[0] = {832, VL0}, /* for bx2 */
.cpaxidvcinfo[1] = {1248, VL1}, /* for bx2 */
.msadvcvl[0] = {416, VL1},
.msadvcvl[1] = {491, VL1},
.msadvcvl[2] = {624, VL3},
},
};
static int vm_mv_asr1901_svc_1475M[][VL_MAX] = {
{820, 850, 870, 920}, /* profile 0 */
{790, 810, 830, 840}, /* profile 1 */
{790, 810, 830, 850},
{800, 820, 840, 860},
{800, 820, 840, 870},
{810, 830, 850, 880}, /* profile 5 */
{810, 830, 850, 900},
{820, 840, 860, 910},
{820, 850, 870, 920}, /* profile 8 */
{820, 850, 870, 920},
{820, 850, 870, 920}, /* profile 10 */
{820, 850, 870, 920},
{820, 850, 870, 920},
{820, 850, 870, 920},
{820, 850, 870, 920},
{820, 850, 870, 920} /* profile 15 */
};
static struct dvfs_rail_component vm_rail_comp_tbl_dvc[] = {
INIT_DVFS("cpu", true, AFFECT_RAIL_ACTIVE, NULL),
INIT_DVFS("ddr", true, ACTIVE_M2_D1P_RAIL_FLAG, NULL),
INIT_DVFS("axi", true, ACTIVE_M2_RAIL_FLAG, NULL),
INIT_DVFS("SDH0", true, ACTIVE_M2_D1P_RAIL_FLAG, NULL),
INIT_DVFS("SDH1", true, ACTIVE_M2_D1P_RAIL_FLAG, NULL),
INIT_DVFS("ETH", true, ACTIVE_M2_D1P_RAIL_FLAG, NULL),
};
static int set_pmic_volt(unsigned int lvl, unsigned int mv)
{
return pm8xx_dvc_setvolt(PM801_ID_BUCK1, lvl, mv * mV2uV);
}
static int get_pmic_volt(unsigned int lvl)
{
int uv = 0, ret = 0;
ret = pm8xx_dvc_getvolt(PM801_ID_BUCK1, lvl, &uv);
if (ret < 0)
return ret;
return DIV_ROUND_UP(uv, mV2uV);
}
static struct dvc_plat_info dvc_asr1901_info = {
.comps = vm_rail_comp_tbl_dvc,
.num_comps = ARRAY_SIZE(vm_rail_comp_tbl_dvc),
.num_volts = VL_MAX,
.cp_pmudvc_lvl = VL0,
.dp_pmudvc_lvl = VL0, /* if dp624, volt vl1 */
.dvc_pin_switch = 0,
.set_vccmain_volt = set_pmic_volt,
.get_vccmain_volt = get_pmic_volt,
.pmic_rampup_step = 10000,
.force_dvc = false,
.dbglvl = 1,
.regname = "BUCK1",
.extra_timer_dlyus = 16,
};
static unsigned int uiprofile;
static unsigned int cmbindex;
unsigned int uisvtdro;
static u16 auxadc_gain_offset, auxadc_gain_error;
/* FIXME: must remove the table after has fuse info !!! */
struct svtrng {
unsigned int min;
unsigned int max;
unsigned int profile;
};
static struct svtrng svtrngtb_asr1901[] = {
{168, 175, 8},
{176, 183, 7},
{184, 191, 6},
{192, 199, 5},
{200, 207, 4},
{208, 215, 3},
{216, 223, 2},
{224, 231, 1}
/* NOTE: by default use profile 0 */
};
static unsigned int convert_profile2comm1475dvl(unsigned int uiprofile)
{
return uiprofile;
}
static unsigned int convert_profile2cmb1475index(unsigned int uiprofile)
{
return uiprofile;
}
static unsigned int convert_svtdro2profile(unsigned int uisvtdro)
{
unsigned int uiprofile = 0, idx, svtrngtb_size;
struct svtrng *svtrngtb;
svtrngtb = svtrngtb_asr1901;
svtrngtb_size = ARRAY_SIZE(svtrngtb_asr1901);
for (idx = 0; idx < svtrngtb_size; idx++) {
if (uisvtdro >= svtrngtb[idx].min &&
uisvtdro <= svtrngtb[idx].max) {
uiprofile = svtrngtb[idx].profile;
break;
}
}
return uiprofile;
}
unsigned int ddr_cmbindex(void)
{
return (unsigned int)DDR;
}
EXPORT_SYMBOL(ddr_cmbindex);
static int __init __init_read_droinfo(void)
{
unsigned int fuse_63_32 = 0;
unsigned int fuse_95_64 = 0;
unsigned int fuse_127_96 = 0;
unsigned int fuse_159_128 = 0;
unsigned int fuse_191_160 = 0;
unsigned int fuse_223_192 = 0;
unsigned int fuse_255_224 = 0;
unsigned long long uilot;
unsigned int uiwafer;
unsigned int uix;
unsigned int uiy;
unsigned int uifoundry;
unsigned int uidev_version;
unsigned int uitinfo;
#ifndef CONFIG_OPTEE
void __iomem *apmu_base, *geu_base;
unsigned int uigeustatus;
apmu_base = ioremap(AXI_PHYS_BASE + 0x82800, SZ_512);
if (apmu_base == NULL) {
pr_err("error to ioremap APMU base\n");
return -1;
}
geu_base = ioremap(AXI_PHYS_BASE + 0x92800, SZ_4K);
if (geu_base == NULL) {
pr_err("error to ioremap GEU base\n");
return -1;
}
/*
* Read out DRO value, need enable GEU clock, if already disable,
* need enable it firstly
*/
uigeustatus = __raw_readl(apmu_base + APMU_GEU);
if ((uigeustatus & 0x30) != 0x30) {
__raw_writel((uigeustatus | 0x30), apmu_base + APMU_GEU);
udelay(10);
}
fuse_63_32 = __raw_readl(geu_base + BANK3_fUSE_63_32);
fuse_95_64 = __raw_readl(geu_base + BANK3_fUSE_95_64);
fuse_127_96 = __raw_readl(geu_base + BANK3_fUSE_127_96);
fuse_159_128 = __raw_readl(geu_base + BANK3_fUSE_159_128);
fuse_191_160 = __raw_readl(geu_base + BANK3_fUSE_191_160);
fuse_223_192 = __raw_readl(geu_base + BANK3_fUSE_223_192);
fuse_255_224 = __raw_readl(geu_base + BANK3_fUSE_255_224);
__raw_writel(uigeustatus, apmu_base + APMU_GEU);
iounmap(geu_base);
iounmap(apmu_base);
#else
fuse_63_32 = asr_ciu_read(BANK3_fUSE_63_32);
fuse_95_64 = asr_ciu_read(BANK3_fUSE_95_64);
fuse_127_96 = asr_ciu_read(BANK3_fUSE_127_96);
fuse_159_128 = asr_ciu_read(BANK3_fUSE_159_128);
fuse_191_160 = asr_ciu_read(BANK3_fUSE_191_160);
fuse_223_192 = asr_ciu_read(BANK3_fUSE_223_192);
fuse_255_224 = asr_ciu_read(BANK3_fUSE_255_224);
#endif
uisvtdro = ((fuse_159_128 >> (137 - 128)) & (0x1ff));
uix = ((fuse_223_192 >> (206 - 192)) & (0x3f));
uiy = ((fuse_223_192 >> (200 - 192)) & (0x3f));
uidev_version = ((fuse_127_96 >> ( 114 - 96)) & (0x7));
uiwafer = ((fuse_223_192 >> ( 212 - 192)) & (0x1f));
uifoundry = ((fuse_255_224 >> (253 - 224)) & (0x1));
uilot = ((fuse_223_192 >> ( 217 - 192)) & (0x7f));
uilot |= (((fuse_255_224 >> ( 224 - 224)) & (0x1fffffff)) << 7);
uitinfo = ((fuse_191_160 >> ( 170 - 160)) & (0x3fffff));
uitinfo |= (((fuse_223_192 >> ( 192 - 192)) & (0xff)) << 22);
auxadc_gain_offset = ((fuse_63_32 >> (44 - 32)) & (0xfff));
auxadc_gain_error = ((fuse_63_32 >> (32 - 32)) & (0xfff));
uiprofile = convert_svtdro2profile(uisvtdro);
cmbindex = convert_profile2cmb1475index(uiprofile);
pr_info("uiprofile = %d\n", uiprofile);
printk(KERN_DEBUG " *********************************\n");
pr_info("ULT1: %08X%08X, ULT0: %08X%08X\n",
fuse_159_128, fuse_191_160,
fuse_223_192, fuse_255_224);
printk(KERN_DEBUG " *********************************\n");
printk(KERN_DEBUG " ULT decoded below\n");
printk(KERN_DEBUG " LOT_ID = 0x%08llx%08llx\n", (uilot >> 32) & 0xffffffff, uilot & 0xffffffff);
printk(KERN_DEBUG " WAFER_ID = 0x%03x\n", uiwafer);
printk(KERN_DEBUG " SVT_DRO = 0x%03x\n", uisvtdro);
printk(KERN_DEBUG " XLOC = 0x%03x\n", uix);
printk(KERN_DEBUG " YLOC = 0x%03x\n", uiy);
printk(KERN_DEBUG " DEV_VER = 0x%03x\n", uidev_version);
printk(KERN_DEBUG " TINFO = 0x%08x\n", uitinfo);
printk(KERN_DEBUG " FOUNDRY = %d\n", uifoundry);
printk(KERN_DEBUG " ADC = 0x%03x 0x%03x\n", auxadc_gain_offset, auxadc_gain_error);
printk(KERN_DEBUG " *********************************\n");
pr_info(" SVT_DRO = %d\n", uisvtdro);
printk(KERN_DEBUG " Profile = %d\n", uiprofile);
printk(KERN_DEBUG " *********************************\n");
return uisvtdro;
}
int get_fuseinfo(u64 *fuseuid, u64 *fuseult, u64 *fusemanu)
{
#ifndef CONFIG_OPTEE
unsigned int uigeustatus;
void __iomem *apmu_base, *geu_base;
geu_base = ioremap(AXI_PHYS_BASE + 0x92800, SZ_4K);
if (geu_base == NULL) {
pr_err("error to ioremap GEU base\n");
return -1;
}
apmu_base = ioremap(AXI_PHYS_BASE + 0x82800, SZ_512);
if (apmu_base == NULL) {
pr_err("error to ioremap APMU base\n");
iounmap(geu_base);
return -1;
}
uigeustatus = __raw_readl(apmu_base + APMU_GEU);
if ((uigeustatus & 0x30) != 0x30) {
__raw_writel((uigeustatus | 0x30), apmu_base + APMU_GEU);
udelay(10);
}
*fuseuid = __raw_readl(geu_base + BANK3_fUSE_255_224);
*fuseuid = (*fuseuid << 32) | __raw_readl(geu_base + BANK3_fUSE_223_192);
*fuseult = __raw_readl(geu_base + BANK3_fUSE_191_160);
*fuseult = (*fuseult << 32) | __raw_readl(geu_base + BANK3_fUSE_159_128);
*fusemanu = __raw_readl(geu_base + BANK3_fUSE_127_96);
*fusemanu = (*fusemanu << 32) | __raw_readl(geu_base + BANK3_fUSE_95_64);
__raw_writel(uigeustatus, apmu_base + APMU_GEU);
iounmap(geu_base);
iounmap(apmu_base);
#else
*fuseuid = asr_ciu_read(BANK3_fUSE_255_224);
*fuseuid = (*fuseuid << 32) | asr_ciu_read(BANK3_fUSE_223_192);
*fuseult = asr_ciu_read(BANK3_fUSE_191_160);
*fuseult = (*fuseult << 32) | asr_ciu_read(BANK3_fUSE_159_128);
*fusemanu = asr_ciu_read(BANK3_fUSE_127_96);
*fusemanu = (*fusemanu << 32) | asr_ciu_read(BANK3_fUSE_95_64);
#endif
return 0;
}
EXPORT_SYMBOL(get_fuseinfo);
int __init setup_asr1901_dvfs_platinfo(void)
{
void __iomem *hwdvc_base;
enum dvfs_comp idx;
struct dvc_plat_info *plat_info = &dvc_asr1901_info;
__init_read_droinfo();
freqs_cmb = freqs_cmb_asr1901_1475M[cmbindex];
dvc_asr1901_info.millivolts = vm_mv_asr1901_svc_1475M[uiprofile];
fillcpdvcinfo(&cpmsa_dvc_info_1901_1475SVC[convert_profile2comm1475dvl(uiprofile)]);
hwdvc_base = ioremap(APB_PHYS_BASE + 0x50000, SZ_4K);
if (hwdvc_base == NULL) {
pr_err("error to ioremap hwdvc base\n");
return -EINVAL;
}
dvc_asr1901_info.dvc_reg_base = hwdvc_base;
plat_set_vl_min(0);
plat_set_vl_max(dvc_asr1901_info.num_volts);
for (idx = CORE; idx < VM_RAIL_MAX; idx++)
plat_info->comps[idx].freqs = freqs_cmb[idx];
return dvfs_setup_dvcplatinfo(&dvc_asr1901_info);
}
int extern_get_auxadc_fusedata(u16 *gain_offset, u16 *gain_error)
{
*gain_offset = auxadc_gain_offset;
*gain_error = auxadc_gain_error;
return 0;
}