blob: ed51dfc7d33eef3b03cf23af4e395644b2ba9509 [file] [log] [blame]
#include <errno.h>
#include <common.h>
#include <command.h>
#include "../../board/Marvell/include/core.h"
#include <asm/io.h>
#include <asm/gpio.h>
#include <asm/arch/cpu.h>
#include <asm/arch/features.h>
#include <asm/gpio.h>
#include <linux/types.h>
#include <power/pxa182x_freq.h>
#include <i2c.h>
#include <power/pmic.h>
#include <power/marvell88pm_pmic.h>
#include "../../board/Marvell/common/mv_cp.h"
//#define PXA182X_FREQ_DEBUG 1
/*
* !!!!!!!!!!!!!!!!!!!!!Note: AP and CP has different copy of the below variables
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!
* Which means AP only has real time value of c_aop,
* need to update/get the c_axiop value when CP not changing them.
* and vice-versa.
*/
#define PXA1826_NUM_PROFILES 16
uint32_t aop_num = 0, axiop_num = 0, csop_num = 0, dop_num = 0;
op_index c_aop=OP_INVALID, c_csop = OP_INVALID, c_dop = OP_INVALID;
op_index c_axiop = OP_INVALID;
uint32_t max_dvc = 0; /* The current max requested DVC level at AP or CP side*/
uint32_t uidro_svt = 0;
uint32_t uiProfile = 0;
#define GET_CHIP_DVL4OP_Ax(p, n) \
do {\
uint32_t j = 0; \
for (j = 0; j <= n; j++) \
(p[j].dvc) = (((p[j].dvcsa) >> (4*uiProfile))&0xF); \
} while(0)
core_freq *ca7_value;
core_freq ca7_value_832M[] = {
/*label pclk,memclk,busclk,pll2,pll2p,dvc,dvcs{profile15..profile0}, dvcsa*/
{"OP0", 416, 208, 104, 0, 0, 0, 0x1111100000000001, 0x0000000000000000},
{"OP1", 312, 156, 156, 0, 0, 0, 0x0000000000000000, 0x0000000000000000},
{"OP2", 416, 208, 208, 0, 0, 0, 0x1111100000000001, 0x0000000000000000},
{"OP3", 624, 312, 156, 0, 0, 1, 0x1111111111111001, 0x1111111111110001},
{"OP4", 832, 416, 208, 0, 0, 2, 0x2222222222222222, 0x3333333333333333},
};
core_freq ca7_value_1057M[] = {
/*label pclk,memclk,busclk,pll2,pll2p,dvc,dvcs{profile15..profile0}, dvcsa*/
{"OP0", 416, 208, 104, 0, 0, 0, 0x1111100000000001, 0x0000000000000000},
{"OP1", 312, 156, 156, 0, 0, 0, 0x0000000000000000, 0x0000000000000000},
{"OP2", 416, 208, 208, 0, 0, 0, 0x1111100000000001, 0x0000000000000000},
{"OP3", 624, 312, 156, 0, 0, 1, 0x1111111111111001, 0x1111111111110001},
{"OP4", 832, 416, 208, 0, 0, 2, 0x2222222222222222, 0x2222222222222222},
{"OP5", 1057,528, 264, 1057,0, 3, 0x3333333333333333, 0x3333333333333333},
};
core_freq ca7_value_1248M[] = {
/*label pclk,memclk,busclk,pll2,pll2p,dvc,dvcs{profile15..profile0}, dvcsa*/
{"OP0", 416, 208, 104, 0, 0, 0, 0x1111100000000001, 0x0000000000000000},
{"OP1", 312, 156, 156, 0, 0, 0, 0x0000000000000000, 0x0000000000000000},
{"OP2", 416, 208, 208, 0, 0, 0, 0x1111100000000001, 0x0000000000000000},
{"OP3", 624, 312, 156, 0, 0, 1, 0x1111111111111001, 0x1111111111110001},
{"OP4", 832, 416, 208, 0, 0, 2, 0x2222222222222222, 0x2222222222222222},
{"OP5", 1248,624, 312, 0, 0, 3, 0x3333333333333333, 0x3333333333333333},
};
dfc_setting *ca7_reg;
/* the ASYNC bits has no use in AP side */
dfc_setting ca7_reg_1248M[] = {
/*pll2ref,pll2fb,apb_spare2,fccr[31:29],0x09FC0000|cc_ap[8:0] mc_reg_table*/
{NA , NA , NA , 0x0<<29, 0xC9, NA}, /* OP0 */
{NA , NA , NA , 0x1<<29, 0x4B, NA}, /* OP1 */
{NA , NA , NA , 0x0<<29, 0x49, NA}, /* OP2 */
{NA , NA , NA , 0x1<<29, 0xC9, NA}, /* OP3 */
{NA , NA , NA , 0x0<<29, 0xC8, NA}, /* OP4 */
{NA , NA , NA , 0x1<<29, 0xC8, NA}, /* OP5 */
};
/* the ASYNC bits has no use in AP side */
dfc_setting ca7_reg_1057M[] = {
/*pll2ref,pll2fb,apb_spare2,fccr[31:29],0x09FC0000|cc_ap[8:0] mc_reg_table*/
{NA , NA , NA , 0x0<<29, 0xC9, NA}, /* OP0 */
{NA , NA , NA , 0x1<<29, 0x4B, NA}, /* OP1 */
{NA , NA , NA , 0x0<<29, 0x49, NA}, /* OP2 */
{NA , NA , NA , 0x1<<29, 0xC9, NA}, /* OP3 */
{NA , NA , NA , 0x0<<29, 0xC8, NA}, /* OP4 */
{3 , 61 , 0x1010E0EB, 0x2<<29, 0xC8, NA}, /* OP5 */
};
ddr_freq *pxa1826_ddr_value;
ddr_freq pxa1826_ddr_value_400op[] = {
/* label dclk, pll2,pll2p,dfl_setting,dvc,dvcs{profile15..profile0}, dvcsa*/
{"OP0", 104, 0, 0, 0x00000090, 0, 0x0000000000000000, 0x0000000000000000},
{"OP1", 156, 0, 0, 0x00000111, 0, 0x0000000000000000, 0x0000000000000000},
{"OP2", 208, 0, 0, 0x00000180, 0, 0x0000000000000000, 0x0000000000000000},
{"OP3", 312, 0, 0, 0x00000201, 0, 0x0000000000000000, 0x0000000000000000},
{"OP4", 398, 797, 0, 0x00000282, 0, 0x0000000000000000, 0x0000000000000000},
};
ddr_freq pxa1826_ddr_value_416op[] = {
/* label dclk, pll2,pll2p,dfl_setting, dvc,dvcs{profile15..profile0}, dvcsa*/
{"OP0", 104, 0, 0, 0x00000090, 0, 0x0000000000000000, 0x0000000000000000},
{"OP1", 156, 0, 0, 0x00000111, 0, 0x0000000000000000, 0x0000000000000000},
{"OP2", 208, 0, 0, 0x00000180, 0, 0x0000000000000000, 0x0000000000000000},
{"OP3", 312, 0, 0, 0x00000201, 0, 0x0000000000000000, 0x0000000000000000},
{"OP4", 416, 0, 0, 0x00000383, 0, 0x0000000000000000, 0x0000000000000000},
};
ddr_freq pxa1826_ddr_value_533op_1057svc[] = {
/* label dclk, pll2,pll2p,dfl_setting, dvc,dvcs{profile15..profile0}, dvcsa*/
{"OP0", 104, 0, 0, 0x00000090, 0, 0x0000000000000000, 0x0000000000000000},
{"OP1", 156, 0, 0, 0x00000111, 0, 0x0000000000000000, 0x0000000000000000},
{"OP2", 208, 0, 0, 0x00000180, 0, 0x0000000000000000, 0x0000000000000000},
{"OP3", 312, 0, 0, 0x00000201, 0, 0x0000000000000000, 0x0000000000000000},
{"OP4", 416, 0, 0, 0x00000383, 0, 0x0000000000000000, 0x0000000000000000},
{"OP5", 528, 1057,0, 0x00000302, 1, 0x1111111111000001, 0x1111111111000001},
};
ddr_freq pxa1826_ddr_value_533op_non1057svc[] = {
/* label dclk, pll2,pll2p,dfl_setting, dvc,dvcs{profile15..profile0}, dvcsa*/
{"OP0", 104, 0, 0, 0x00000090, 0, 0x0000000000000000, 0x0000000000000000},
{"OP1", 156, 0, 0, 0x00000111, 0, 0x0000000000000000, 0x0000000000000000},
{"OP2", 208, 0, 0, 0x00000180, 0, 0x0000000000000000, 0x0000000000000000},
{"OP3", 312, 0, 0, 0x00000201, 0, 0x0000000000000000, 0x0000000000000000},
{"OP4", 416, 0, 0, 0x00000383, 0, 0x0000000000000000, 0x0000000000000000},
{"OP5", 528, 1057,0, 0x00000302, 1, 0x1111111111000001, 0x2221111111000002},
};
dfc_setting *pxa1826_ddr_reg;
dfc_setting pxa1826_ddr_reg_400op[] = {
/*pll2ref,pll2fb,apb_spare2, fccr[27:26], 0x09F80000|cc_cp[8:0] mc_reg_table*/
{NA , NA , NA , 0x0<<23, 0x1<<12, 0x08}, /* OP0 */
{NA , NA , NA , 0x1<<23, 0x1<<12, 0x10}, /* OP1 */
{NA , NA , NA , 0x0<<23, 0x0<<12, 0x18}, /* OP2 */
{NA , NA , NA , 0x1<<23, 0x0<<12, 0x20}, /* OP3 */
{3 , 46, 0x1010D0EB, 0x2<<23, 0x0<<12, 0x28}, /* OP4 */
};
dfc_setting pxa1826_ddr_reg_416op[] = {
/*pll2ref,pll2fb,apb_spare2, fccr[27:26], 0x09F80000|cc_cp[8:0] mc_reg_table*/
{NA , NA , NA , 0x0<<23, 0x1<<12, 0x08}, /* OP0 */
{NA , NA , NA , 0x1<<23, 0x1<<12, 0x10}, /* OP1 */
{NA , NA , NA , 0x0<<23, 0x0<<12, 0x18}, /* OP2 */
{NA , NA , NA , 0x1<<23, 0x0<<12, 0x20}, /* OP3 */
{NA , NA, NA , 0x3<<23, 0x0<<12, 0x38}, /* OP4 */
};
dfc_setting pxa1826_ddr_reg_533op[] = {
/*pll2ref,pll2fb,apb_spare2, fccr[27:26], 0x09F80000|cc_cp[8:0] mc_reg_table*/
{NA , NA , NA , 0x0<<23, 0x1<<12, 0x08}, /* OP0 */
{NA , NA , NA , 0x1<<23, 0x1<<12, 0x10}, /* OP1 */
{NA , NA , NA , 0x0<<23, 0x0<<12, 0x18}, /* OP2 */
{NA , NA , NA , 0x1<<23, 0x0<<12, 0x20}, /* OP3 */
{NA , NA, NA , 0x3<<23, 0x0<<12, 0x38}, /* OP4 */
{3 , 61, 0x1010E0EB, 0x2<<23, 0x0<<12, 0x30}, /* OP5 */
};
axi_freq aclk_value[] = {
/* label aclk, dvc,dvcs{profile15..profile0}, dvcsa*/
{"OP0", 104, 0, 0x0000000000000000, 0x0000000000000000},
{"OP1", 156, 0, 0x0000000000000000, 0x0000000000000000},
{"OP2", 208, 0, 0x0000000000000000, 0x0000000000000000},
{"OP3", 312, 1, 0x1111110000000001, 0x1111111000000001},
};
axi_setting aclk_reg[] = {
/* fccr[25|19], cc_cp[17:15] */
{0x0<<19, 0x3<<15}, /* OP0 */
{0x1<<19, 0x3<<15}, /* OP1 */
{0x0<<19, 0x1<<15}, /* OP2 */
{0x1<<19, 0x1<<15}, /* OP3 */
};
cs_freq cs_value[] = {
/* label atclk, dbg_clk, trace_clk, dvc */
{"OP0", 104, 52, 208, 0, 0x0000000000000000},
{"OP1", 416, 208, 208, 0, 0x0000000000000000},
};
cs_setting cs_reg[] = {
/* |trace_config */
{0x00080103}, /* OP0 */
{0x00080100}, /* OP1 */
};
static struct cpmsa_dvc_info cpmsa_dvc_info_182x[] = {
[0] = {
.cpdvcinfo[0] = {208, VL0},
.cpdvcinfo[1] = {312, VL0},
.cpdvcinfo[2] = {416, VL0},
.cpdvcinfo[3] = {624, VL1},
.cpaxidvcinfo[0] = {104, VL0},
.cpaxidvcinfo[1] = {156, VL0},
.cpaxidvcinfo[2] = {208, VL0},
.lteaxidvcinfo[0] = {104, VL0},
.lteaxidvcinfo[1] = {156, VL0},
.lteaxidvcinfo[2] = {208, VL0},
.lteaxidvcinfo[3] = {312, VL0},
.msadvcvl[0] = {416, VL0},
.msadvcvl[1] = {624, VL2},
},
[1] = {
.cpdvcinfo[0] = {208, VL0},
.cpdvcinfo[1] = {312, VL0},
.cpdvcinfo[2] = {416, VL0},
.cpdvcinfo[3] = {624, VL0},
.cpaxidvcinfo[0] = {104, VL0},
.cpaxidvcinfo[1] = {156, VL0},
.cpaxidvcinfo[2] = {208, VL0},
.lteaxidvcinfo[0] = {104, VL0},
.lteaxidvcinfo[1] = {156, VL0},
.lteaxidvcinfo[2] = {208, VL0},
.lteaxidvcinfo[3] = {312, VL0},
.msadvcvl[0] = {416, VL0},
.msadvcvl[1] = {624, VL1},
},
[2] = {
.cpdvcinfo[0] = {208, VL0},
.cpdvcinfo[1] = {312, VL0},
.cpdvcinfo[2] = {416, VL0},
.cpdvcinfo[3] = {624, VL0},
.cpaxidvcinfo[0] = {104, VL0},
.cpaxidvcinfo[1] = {156, VL0},
.cpaxidvcinfo[2] = {208, VL0},
.lteaxidvcinfo[0] = {104, VL0},
.lteaxidvcinfo[1] = {156, VL0},
.lteaxidvcinfo[2] = {208, VL0},
.lteaxidvcinfo[3] = {312, VL0},
.msadvcvl[0] = {416, VL0},
.msadvcvl[1] = {624, VL2},
},
[3] = {
.cpdvcinfo[0] = {208, VL0},
.cpdvcinfo[1] = {312, VL0},
.cpdvcinfo[2] = {416, VL0},
.cpdvcinfo[3] = {624, VL1},
.cpaxidvcinfo[0] = {104, VL0},
.cpaxidvcinfo[1] = {156, VL0},
.cpaxidvcinfo[2] = {208, VL0},
.lteaxidvcinfo[0] = {104, VL0},
.lteaxidvcinfo[1] = {156, VL0},
.lteaxidvcinfo[2] = {208, VL0},
.lteaxidvcinfo[3] = {312, VL0},
.msadvcvl[0] = {416, VL0},
.msadvcvl[1] = {624, VL2},
},
};
static struct ddr_dfc_info ddrdfcinfo;
#define PM801_SLAVE_ADDR 0x31
#define PM801_VBUCK1_SET0_REG 0x3c /* dvc[1:0] = 0x0 */
#define VOL_BASE 600000
#define VOL_STEP 12500
#define VOL_HIGH 1400000
#define PM801_DVC1 13
#define PM801_DVC2 127
#define PM812_DVC1 79
#define PM812_DVC2 78
static void find_ddr_level(void)
{
int i;
ddrdfcinfo.ddr_idle = 0;
for (i = 0; i <= dop_num; i++) {
if (ddrdfcinfo.ddr_active == 0) {
if (pxa1826_ddr_value[i].dclk >= 312) {
ddrdfcinfo.ddr_active = i;
}
}
if (ddrdfcinfo.ddr_high == 0) {
if (pxa1826_ddr_value[i].dclk >= 398) {
ddrdfcinfo.ddr_high = i;
}
}
if (ddrdfcinfo.ddr_active && ddrdfcinfo.ddr_high)
break;
}
return;
}
static int set_volt(u32 vol)
{
int ret = 0;
unsigned gpio_dvc2, gpio_dvc1;
struct pmic *p_power;
struct pmic_chip_desc *board_pmic_chip;
vol *= 1000;
if ((vol < VOL_BASE) || (vol > VOL_HIGH)) {
printf("out of range when set voltage!\n");
return -1;
}
board_pmic_chip = get_marvell_pmic();
if (!board_pmic_chip)
return -1;
p_power = pmic_get(board_pmic_chip->power_name);
if (!p_power)
return -1;
if (pmic_probe(p_power))
return -1;
ret = marvell88pm_set_buck_vol(p_power, 1, vol, 0);
ret = marvell88pm_set_buck_vol(p_power, 1, vol, 1);
ret = marvell88pm_set_buck_vol(p_power, 1, vol, 2);
ret = marvell88pm_set_buck_vol(p_power, 1, vol, 3);
debug("Set VBUCK1 to %4dmV\n", vol / 1000);
gpio_dvc2 = PM812_DVC2;
gpio_dvc1 = PM812_DVC1;
/* always use DVC[1:0] = 0 in uboot */
/* TODO : make sure DVC pins' function is GPIO */
gpio_direction_output(gpio_dvc1, 0);
gpio_direction_output(gpio_dvc2, 0);
return ret;
}
static u32 get_volt(int buck_num, int level)
{
u32 vol = 0;
struct pmic *p_power;
struct pmic_chip_desc *board_pmic_chip;
board_pmic_chip = get_marvell_pmic();
if (!board_pmic_chip)
return -1;
p_power = pmic_get(board_pmic_chip->power_name);
if (!p_power)
return -1;
if (pmic_probe(p_power))
return -1;
vol = marvell88pm_get_buck_vol(p_power, buck_num, level);
if (vol < 0)
return -1;
return vol / 1000;
}
int do_setvolt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
ulong vol, buck_num = 1, dvl = 0;
int res = 0;
struct pmic *p_power;
struct pmic_chip_desc *board_pmic_chip;
if ((argc < 1) || (argc > 4))
return -1;
board_pmic_chip = get_marvell_pmic();
if (!board_pmic_chip)
return -1;
p_power = pmic_get(board_pmic_chip->power_name);
if (!p_power)
return -1;
if (pmic_probe(p_power))
return -1;
if (argc == 1) {
printf("usage: setvolt x xxxx(unit mV)\n"
"for PM80X, x can be 1 2 ... 5\n"
"for PM80X, xxxx can be 600..1300 for buck1,"
"step 13 or 21\n"
);
goto out;
} else if (argc == 2) {
res |= strict_strtoul(argv[1], 0, &buck_num);
goto out;
} else if (argc == 3) {
res |= strict_strtoul(argv[1], 0, &buck_num);
res |= strict_strtoul(argv[2], 0, &vol);
res |= marvell88pm_set_buck_vol(p_power, buck_num, vol*1000, 0);
res |= marvell88pm_set_buck_vol(p_power, buck_num, vol*1000, 1);
res |= marvell88pm_set_buck_vol(p_power, buck_num, vol*1000, 2);
res |= marvell88pm_set_buck_vol(p_power, buck_num, vol*1000, 3);
} else if (argc == 4) {
res |= strict_strtoul(argv[1], 0, &buck_num);
res |= strict_strtoul(argv[2], 0, &vol);
res |= strict_strtoul(argv[3], 0, &dvl);
res |= marvell88pm_set_buck_vol(p_power, buck_num, vol*1000, dvl);
}
if (res == 0)
printf("Voltage change was successful\n");
else
printf("Voltage change was unsuccessful\n");
out:
if (buck_num == 1 || buck_num == 4) {
printf("DVL00 -- BUCK%ld is %dmV\n",
buck_num, get_volt(buck_num, 0));
printf("DVL01 -- BUCK%ld is %dmV\n",
buck_num, get_volt(buck_num, 1));
printf("DVL10 -- BUCK%ld is %dmV\n",
buck_num, get_volt(buck_num, 2));
printf("DVL11 -- BUCK%ld is %dmV\n",
buck_num, get_volt(buck_num, 3));
} else
printf("BUCK%ld is %dmV in active mode\n",
buck_num, get_volt(buck_num, 0));
return 0;
}
U_BOOT_CMD(
setvolt, 6, 1, do_setvolt,
"Setting voltages",
""
);
static int check_help(char *help_argu)
{
if ((strcmp((const char*)help_argu, "/h") == 0) ||
(strcmp((const char*)help_argu, "--help") == 0))
return 1;
return 0;
}
static int pll2_is_enabled(void)
{
uint32_t val;
val = PMUM->PLL2CR;
/* ctrl = 0(hw enable) or ctrl = 1&&en = 1(sw enable) */
/* ctrl = 1&&en = 0(sw disable) */
if ((val & (0x1 << 9)) && (!(val & (0x1 << 8))))
return 0;
else
return 1;
}
void get_pll2(uint32_t *pll2, uint32_t *pll2p)
{
volatile uint32_t temp;
uint32_t pll2fb, pll2ref, vcodiv_se, vcodiv_diff;
uint32_t refclk = 26;
temp = PMUM->PLL2CR;
if (pll2_is_enabled()) {
pll2fb = (temp>>10)&0x1FF;
pll2ref= (temp>>19)&0x1F;
temp = APBSPARE->PLL2_SW_CTRL;
vcodiv_se = (temp>>20)& 0x7;
vcodiv_se = 1 << vcodiv_se;
*pll2=((pll2fb*refclk*4)/pll2ref)/vcodiv_se;
vcodiv_diff = (temp>>17)& 0x7;
vcodiv_diff = 1 << vcodiv_diff;
*pll2p=((pll2fb*refclk*4)/pll2ref)/vcodiv_diff;
} else {
*pll2=0;
*pll2p=0;
}
}
void hw_setup_pll2(uint32_t pll2ref, uint32_t pll2fb, uint32_t apb_spare2)
{
uint32_t temp, delaytime = 20;
temp = PMUM->PLL2CR;
if (pll2_is_enabled() && (((temp&0xF80000)>>19) == pll2ref)
&& ((temp&0x7FC00)>>10==pll2fb)
&& (APBSPARE->PLL2_SW_CTRL==apb_spare2)) {
printf("PLL2 is already enabled at desired value.\n");
return;
} else {
if (pll2_is_enabled()) {
printf("pll2 is already enabled with not desired value\n");
return;
}
APBSPARE->PLL2_SW_CTRL = apb_spare2;
temp &= (~0xfffc00);
temp |= ((pll2ref<<19)|(pll2fb<<10));
PMUM->PLL2CR = temp;
temp = PMUM->PLL2CR;
temp |= 0x200; /* SW ctrl + en_0 */
PMUM->PLL2CR = temp;
temp &= (~0x200); /* hw ctrl + en_0 */
PMUM->PLL2CR = temp;
udelay(30);
while (!(PMUM->POSR & 0x1 << 29) && delaytime) {
udelay(5);
delaytime--;
}
if (!delaytime) {
printf("pll2 can't be locked\n");
return;
}
}
}
static int pll2_is_inuse(void)
{
uint32_t fccr;
fccr = PMUM->FCCR;
if (((fccr & BIT30)==0) &&
(((fccr & BIT25)!=1)||((fccr & BIT19) !=0))&&
((fccr & BIT27)==0) &&
((fccr & BIT24)==0))
return 0;
else
return 1;
}
static void sw_turnoff_pll2(void)
{
uint32_t temp;
debug("turning off PLL2\n");
temp = PMUM->PLL2CR;
temp |= 0x200;
PMUM->PLL2CR = temp;
temp &= (~0x100);
PMUM->PLL2CR = temp;
}
static uint32_t abs_diff(uint32_t value1, uint32_t value2)
{
if (value1 < value2) {
return (value2 - value1);
} else {
return (value1 - value2);
}
}
/*
* Collect all the AP and CP freqs and print out the infos, not include MSA.
*/
op_index aop_get_dump(core_freq *ap_op)
{
uint32_t pll_dragon, read_reg, temp_reg;
uint32_t i = 0, pll2 = 0, pll2p = 0;
read_reg = PMUA->PLL_SEL_STATUS;
pll_dragon = (read_reg & 0xC) >> 2;
switch (pll_dragon) {
case 0:
pll_dragon = 832;
break;
case 1:
pll_dragon = 1248;
break;
case 2:
get_pll2(&pll_dragon, &pll2p);
break;
case 3:
get_pll2(&pll2, &pll_dragon);
break;
}
read_reg = PMUA->DM_CC_AP;
PMUA->CC_AP |= BIT31;
PMUA->CC_AP &= ~BIT31;
temp_reg = read_reg & 7;
ap_op->pclk =pll_dragon / (temp_reg + 1) ;
temp_reg = (read_reg & 0x00000038) >> 3;
ap_op->memclk =ap_op->pclk / (temp_reg + 1) ;
temp_reg = (read_reg & 0x000001C0) >> 6;
ap_op->busclk =ap_op->pclk / (temp_reg + 1) ;
while (i<aop_num) {
if ((abs_diff(ca7_value[i].pclk, ap_op->pclk)<5)&&
(abs_diff(ca7_value[i].memclk, ap_op->memclk)<5)&&
(abs_diff(ca7_value[i].busclk, ap_op->busclk)<5)) {
ap_op->dvc = ca7_value[i].dvc;
c_aop = (op_index)i;
break;
} else {
i++;
c_aop = OP_INVALID;
}
}
debug("CA7 pclk = %d, memclk= %d, busclk = %d\n",
ap_op->pclk,ap_op->memclk,ap_op->busclk);
return c_aop;
}
static uint32_t check_aop(int32_t top)
{
int32_t aop;
core_freq temp;
aop = aop_get_dump(&temp);
if (aop == OP_INVALID) {
printf("Warning: CA7 freqs are INVALID OP\n");
return OP_INVALID;
}
if (aop == top) {
printf("CA7 OP%d,DVL:%d\n", aop,max_dvc);
c_aop = top;
return MV_OK;
} else {
printf("Warning: CA7 OP changed but freqs are not expected.\n");
c_aop = aop;
return MV_FAIL;
}
}
static void dump_aop( unsigned int op_index)
{
core_freq *md = &(ca7_value[op_index]);
printf("%d(%s)---", op_index, md->label);
printf("pclk:%d memclk:%d busclk:%d"
" pll2:%d pll2p:%d dvc:%d\n",
md->pclk, md->memclk, md->busclk,
md->pll2, md->pll2p, md->dvc);
}
void dump_aop_list(void)
{
unsigned int i;
printf("AP core OP list \n");
for (i = 0; i < aop_num; i++) {
dump_aop(i);
}
}
void dump_cur_aop(void)
{
printf("CA7 @OP%d\n", c_aop);
dump_aop(c_aop);
}
op_index dop_get_dump(ddr_freq *d_op)
{
uint32_t pll_dp, read_reg, temp_reg;
uint32_t i = 0, pll2 = 0, pll2p = 0;
read_reg = PMUA->PLL_SEL_STATUS;
pll_dp = (read_reg >> 4) & 0x3;
switch (pll_dp) {
case 0:
pll_dp = 416;
break;
case 1:
pll_dp = 624;
break;
case 2:
get_pll2(&pll_dp, &pll2p);
break;
case 3:
pll_dp = 832;
break;
}
/* Read the current status */
read_reg = PMUA->DM_CC_AP;
PMUA->CC_AP |= BIT31;
PMUA->CC_AP &= ~BIT31;
temp_reg = (read_reg >> 12) & 7;
d_op->dclk =pll_dp /(temp_reg + 1)/2 ;
while (i < dop_num) {
if ((abs_diff(pxa1826_ddr_value[i].dclk, d_op->dclk)<5)) {
d_op->dvc = pxa1826_ddr_value[i].dvc;
c_dop = (op_index)i;
CIU->SW_SCRATCH = c_dop;
break;
} else {
i++;
c_dop = OP_INVALID;
CIU->SW_SCRATCH = c_dop;
}
}
debug("DDR dclk = %d\n",d_op->dclk);
return c_dop;
}
static uint32_t check_dop(int32_t top)
{
int32_t dop;
ddr_freq temp;
dop = dop_get_dump(&temp);
if (dop == OP_INVALID) {
printf("Warning: DDR freqs are INVALID OP\n");
return OP_INVALID;
}
if (dop == top) {
printf("DDR OP%d,DVL:%d\n", dop,max_dvc);
c_dop = top;
CIU->SW_SCRATCH = c_dop;
return MV_OK;
} else {
printf("ERR: the DDR OP changed but freq is not expected\n");
c_dop = dop;
CIU->SW_SCRATCH = c_dop;
return MV_FAIL;
}
}
static void dump_dop( unsigned int op_index)
{
ddr_freq *md = &(pxa1826_ddr_value[op_index]);
printf("%d(%s)---", op_index, md->label);
printf("dclk:%d pll2:%d pll2p:%d dvc:%d\n",
md->dclk,md->pll2, md->pll2p, md->dvc);
}
void dump_dop_list(void)
{
unsigned int i;
printf("DDR OP list \n");
for (i=0; i < dop_num; i++) {
dump_dop(i);
}
}
void dump_cur_dop(void)
{
printf("DDR @OP%d\n", c_dop);
dump_dop(c_dop);
}
op_index axiop_get_dump(axi_freq *axi_op)
{
uint32_t pll_aclk, read_reg, temp_reg;
uint32_t i = 0, pll2p = 0;
read_reg = PMUA->PLL_SEL_STATUS;
pll_aclk = (read_reg & 0xC0) >> 6 ;
switch (pll_aclk) {
case 0:
pll_aclk = 416;
break;
case 1:
pll_aclk = 624;
break;
case 2:
get_pll2(&pll_aclk, &pll2p);
break;
case 3:
pll_aclk = 312;
break;
}
read_reg = PMUA->DM_CC_AP;
PMUA->CC_AP |= BIT31;
PMUA->CC_AP &= ~BIT31;
temp_reg = (read_reg & 0x00038000) >> 15;
axi_op->aclk = pll_aclk / (temp_reg + 1) ;
while (i<axiop_num) {
if (abs_diff(aclk_value[i].aclk, axi_op->aclk)<5) {
axi_op->dvc = aclk_value[i].dvc;
c_axiop = (op_index)i;
break;
} else {
i++;
c_axiop = OP_INVALID;
}
}
debug("AXI aclk = %d\n",axi_op->aclk);
return c_axiop;
}
static uint32_t check_axiop(int32_t top)
{
int32_t axiop;
axi_freq temp;
axiop = axiop_get_dump(&temp);
if (axiop == OP_INVALID) {
printf("Warning: AXI freq is INVALID OP\n");
return OP_INVALID;
}
if (axiop == top) {
printf("AXI OP%d,DVL:%d\n", axiop,max_dvc);
c_axiop = top;
return MV_OK;
} else {
printf("ERROR: the AXI freq changed but the values"
" are not expected.\n");
c_axiop = axiop;
return MV_FAIL;
}
}
static void dump_axiop( unsigned int op_index)
{
axi_freq *md = &(aclk_value[op_index]);
printf("%d(%s)---", op_index, md->label);
printf("aclk:%d dvc:%d\n", md->aclk, md->dvc);
}
void dump_axiop_list(void)
{
unsigned int i;
printf("AXI OP list \n");
for (i=0; i < axiop_num; i++) {
dump_axiop(i);
}
}
void dump_cur_axiop(void)
{
printf("AXI @OP%d\n", c_axiop);
dump_axiop(c_axiop);
}
/*AP core freq change function*/
static void adfc_set(uint32_t top)
{
uint32_t read_data;
uint32_t FCCR_MASK = ~(0x7 << 29);
/* +++Step4: set up correct PMUA_DEBUG value */
read_data = PMUA->DEBUG_REG;
read_data |= 0x00660001;
if (PMUM->APRR & 0x1) { /* CP is hold in reset */
/* Change the AP freq clk, ignore CP, it is for AP standalone debug */
read_data |= 0x9;
} else {
/* Need to wait CP's ACK on halt and clock off */
read_data &= ~0x9;
}
PMUA->DEBUG_REG = read_data;
PMUM->FCCR = (PMUM->FCCR & FCCR_MASK)| ca7_reg[top].pmum_fccr;
read_data = PMUM->FCCR;
PMUA->AP_IMR |= BIT1; /* BIT1=BIT3|BIT4|BIT5@delay */
PMUA->AP_IRWC &= ~BIT1;
PMUA->AP_ISR &= ~BIT1;
/* +++Step8:Seagull allow freq. change voting */
PMUA->CC_CP |= (0x1 << 27);
PMUA->CC_AP |= (0x1 << 27);
/* +++Step9: AP set for clock dividers and kick off the sm_fc */
PMUA->CC_AP &= ~0x1ff;
PMUA->CC_AP |= ca7_reg[top].pmua_cc_ap_cp;
PMUA->CC_AP |= 0x01000000;
while (!(BIT1 & PMUA->AP_ISR));
PMUA->AP_ISR &= 0xFFFFFFFD;
}
static void ddfc_set(uint32_t top)
{
uint32_t read_data;
uint32_t FCCR_MASK = ~(0x3 << 23);
/* +++Step4: set up correct PMUA_DEBUG value */
read_data = PMUA->DEBUG_REG;
read_data |= 0x00660001;
PMUA->DEBUG_REG = read_data;
PMUM->FCCR = (PMUM->FCCR & FCCR_MASK)| pxa1826_ddr_reg[top].pmum_fccr;
read_data = PMUM->FCCR;
PMUA->CC_CP |= (0x1 << 27);
PMUA->CC_AP |= (0x1 << 27);
PMUA->AP_IMR |= BIT1; /* BIT1=BIT3|BIT4|BIT5@delay */
PMUA->AP_IRWC &= ~BIT1;
PMUA->AP_ISR &= ~BIT1;
/* +++Step9: AP set for clock dividers and kick off the sm_fc */
PMUA->CC_AP &= ~(0x7 << 12);
PMUA->CC_AP |= pxa1826_ddr_reg[top].pmua_cc_ap_cp;
PMUA->MC_HW_SLP_TYPE &= ~0xF8; /* BITS[7:3] */
PMUA->MC_HW_SLP_TYPE |= pxa1826_ddr_reg[top].mc_reg_table;
PMUA->CC_AP |= 0x02000000;
while (!(BIT1 & PMUA->AP_ISR));
PMUA->AP_ISR &= 0xFFFFFFFD;
}
static void axidfc_set(uint32_t top)
{
uint32_t read_data;
PMUA->DEBUG_REG |= 0x00660001;
PMUA->CC_AP |= BIT27;
PMUA->CC_CP |= BIT27;
PMUM->FCCR &= ~(1<<25);
PMUM->FCCR &= ~(1<<19);
PMUM->FCCR |= aclk_reg[top].pmum_fccr;
read_data = PMUM->FCCR;
debug("0x%x.\n", read_data);
PMUA->AP_IMR = 0x2;
PMUA->AP_IRWC &= ~0x2;
PMUA->CC_AP &= ~0x38000;
PMUA->CC_AP |= aclk_reg[top].pmua_cc_ap_cp;
PMUA->CC_AP |= BIT26;
while ((PMUA->AP_ISR & 0x2) == 0);
PMUA->AP_ISR &= ~0x2;
}
/*
* You don’t need release CP from reset state.
* You can let AP write PMU_CC_CP to trigger CP freq change
* Only through this CC_CP dclk can be changed!
*/
static void get_fc_sm(int is_cp)
{
if (!is_cp) {
while (PMUA->DM_CC_AP & BIT24) {
/*if fail to get control once,
*clear the flag to avoid deadlock with CP
*/
PMUA->CC_AP |= (BIT31);
PMUA->CC_AP &= ~(BIT31);
}
}
}
static void put_fc_sm(int is_cp)
{
uint32_t read_data, temp_data;
if (!is_cp) {
read_data = PMUA->CC_AP;
temp_data = (read_data & 0x0AFFFFFF) | BIT31; /* RD_ST clear */
PMUA->CC_AP = temp_data;
PMUA->CC_AP = temp_data & 0x0AFFFFFF;
}
}
static void update_memory_xtc(uint32_t pclk)
{
CIU->CA7_CPU_SRAM_CONF_1 = (pclk <= 832)? 0x1088:0x5088;
if (pclk <= 416)
CIU->CA7_CPU_SRAM_CONF_0 = 0x00111111;
else if (pclk <= 624)
CIU->CA7_CPU_SRAM_CONF_0 = 0x00555555;
else if (pclk <= 1066)
CIU->CA7_CPU_SRAM_CONF_0 = 0x00999999;
else
CIU->CA7_CPU_SRAM_CONF_0 = 0x00AAAAAA;
}
#define VOL_LEVELS 4
static struct svtrng svtrngtb[] = {
{1 , 310, 15},
{311, 322, 14},
{323, 335, 13},
{336, 348, 12},
{349, 360, 11},
{361, 373, 10},
{374, 379, 9},
{380, 386, 8},
{387, 392, 7},
{393, 398, 6},
{399, 405, 5},
{406, 411, 4},
{412, 417, 3},
{418, 424, 2},
{425, 0xfff, 1}
};
static unsigned int convert_svtdro2profile(uint32_t uisvtdro)
{
unsigned int idx;
for (idx = 0; idx < ARRAY_SIZE(svtrngtb); idx++) {
if (uisvtdro >= svtrngtb[idx].min
&& uisvtdro <= svtrngtb[idx].max) {
uiProfile = svtrngtb[idx].profile;
break;
}
}
return uiProfile;
}
static uint32_t pxa1826_get_profile(void)
{
uint32_t uiManFuses_95_64 = GEU->BLOCK7_RESERVED_2;
uint32_t uiManFuses_127_96 = GEU->BLOCK7_RESERVED_3;
uint32_t uiManFuses_63_32, uiblock0_rsv1 = GEU->BLOCK0_RESERVED_1;
uint32_t uifuses = 0, uitemp = 3, uitemp2 = 1, profile = 0;
int i;
uiManFuses_63_32 = GEU->FUSE_MANU_PARA_1;
uiManFuses_95_64 = GEU->FUSE_MANU_PARA_2;
uidro_svt = ((uiManFuses_95_64 & 0xF) << 6)
+ ((uiManFuses_63_32 >> 26) & 0x3f);
uifuses = (uiblock0_rsv1 >> 16) & 0x0000FFFF;
if (uifuses) {
for (i = 1; i < PXA1826_NUM_PROFILES; i++) {
uitemp |= uitemp2 << i;
if (uitemp == uifuses)
profile = i;
}
return profile;
} else {
return convert_svtdro2profile(uidro_svt);
}
}
static uint32_t soc_init_profile(void)
{
uint32_t uiprofile;
uiprofile = pxa1826_get_profile();
if (uiprofile == 15 && !(cpu_is_pxa1826_a0())) {
while(1)
printf("!!!!!!profile 15 chip can't support 1.2G!!!!!!\n");
}
uiProfile = uiprofile;
return uiprofile;
}
static int (*pxa1826_svc_table_a0)[VOL_LEVELS];
static int pxa1826_svc_table_a0_832M[][VOL_LEVELS] = {
{1050, 1075, 1125, 1150}, /* profile 0 */
{ 975, 988, 988, 1025}, /* profile 1 */
{ 975, 1000, 1000, 1038},
{ 975, 1000, 1000, 1038},
{ 975, 988, 1013, 1050},
{ 975, 988, 1013, 1050}, /* profile 5 */
{ 975, 1000, 1025, 1063},
{ 975, 1000, 1025, 1063},
{ 975, 1013, 1038, 1075},
{ 975, 1013, 1050, 1075},
{ 988, 1025, 1063, 1088}, /* profile 10 */
{1000, 1038, 1075, 1100},
{1013, 1050, 1088, 1113},
{1025, 1050, 1100, 1125},
{1038, 1063, 1113, 1138},
{1050, 1075, 1125, 1150} /* profile 15 */
};
static int pxa1826_svc_table_a0_1057M[][VOL_LEVELS] = {
{1050, 1088, 1150, 1225}, /* profile 0 */
{ 975, 988, 1025, 1088}, /* profile 1 */
{ 975, 1000, 1038, 1088},
{ 975, 1000, 1038, 1100},
{ 975, 988, 1050, 1113},
{ 975, 988, 1050, 1113}, /* profile 5 */
{ 975, 1000, 1063, 1125},
{ 975, 1000, 1063, 1125},
{ 975, 1013, 1075, 1138},
{ 975, 1013, 1075, 1138},
{ 988, 1025, 1088, 1163}, /* profile 10 */
{1000, 1038, 1100, 1175},
{1013, 1050, 1113, 1188},
{1025, 1063, 1125, 1200},
{1038, 1075, 1138, 1213},
{1050, 1088, 1150, 1225} /* profile 15 */
};
static int pxa1826_svc_table_a0_1248M[][VOL_LEVELS] = {
{1050, 1075, 1150, 1300}, /* profile 0 */
{ 975, 988, 1025, 1163}, /* profile 1 */
{ 975, 1000, 1038, 1163},
{ 975, 1000, 1038, 1175},
{ 975, 988, 1050, 1188},
{ 975, 988, 1050, 1188}, /* profile 5 */
{ 975, 1000, 1063, 1200},
{ 975, 1000, 1063, 1200},
{ 975, 1013, 1075, 1213},
{ 975, 1013, 1075, 1225},
{ 988, 1025, 1088, 1238}, /* profile 10 */
{1000, 1038, 1100, 1250},
{1013, 1050, 1113, 1263},
{1025, 1050, 1125, 1275},
{1038, 1063, 1138, 1288},
{1050, 1075, 1150, 1300} /* profile 15 */
};
/*
* In case SW DVC case, CP can't adjust the voltage and AP is the owner
* And even the DVL0 should be higher enough to cover all CP/DP PPs.
* That's why we need HW-DVC!!!!
*/
uint32_t * svc_init_table;
u32 get_mvolt_by_dvc(int dvc)
{
u32 profile = uiProfile;
return pxa1826_svc_table_a0[profile][dvc];
}
static void soc_dvc_init(void)
{
uint32_t profile = 0;
profile = soc_init_profile();
printf("profile is %d\n", profile);
svc_init_table = (uint32_t *)&(pxa1826_svc_table_a0[profile][0]);
}
/* init hw dvc for cp boot and these values will be overwritten by kernel*/
static void pxa182x_init_hwdvc(void)
{
int count;
writel(0x200, PMU_DVC_VL01STR_A0);
writel(0x200, PMU_DVC_VL12STR_A0);
writel(0x200, PMU_DVC_VL23STR_A0);
writel(0x1a0, PMU_DVC_EXRA_STR);
writel(0x80, PMU_DVC_APSUB);
writel(0x8080, PMU_DVC_APCHIP);
writel(0x3, PMU_DVC_DVCR);
/* AP self vote dvc*/
writel(0x80 | ((max_dvc & 0x3) << 8), PMU_DVC_AP);
writel(((readl(PMU_DVC_AP)) | (0x1 << 15)), PMU_DVC_AP);
count = 100000;
while((readl(PMU_DVC_AP)) & (0x1 << 15))
count--;
if (count == 0)
printf("!!!!!!AP-DVC failed\n");
}
static void pxa182x_init_hwdfc(void)
{
int i;
for (i = 0; i < dop_num; i++) {
writel(pxa1826_ddr_value[i].dvc | pxa1826_ddr_value[i].dfl_setting,
PMUA_DFC_LEVEL0 + (i << 2));
}
}
static int ddr_hwdfc_seq(int level)
{
u32 status, isr, dfc_ap;
uint32_t pll2=0, pll2p=0;
int max_delay = 100, timeout = 10000000;
if (pxa1826_ddr_value[level].pll2 || pxa1826_ddr_value[level].pll2p) {
/* get the current pll2 and pll2p value */
get_pll2(&pll2, &pll2p);
if (((abs_diff(pxa1826_ddr_value[level].pll2, pll2)>=5)&&
pll2&&pxa1826_ddr_value[level].pll2) ||
((abs_diff(pxa1826_ddr_value[level].pll2p, pll2p)>=5)&&
pll2p&&pxa1826_ddr_value[level].pll2p)) {
printf("FAIL:PLL2 is used at another frequency pll2 ="
" %d, pll2p= %d already!\n", pll2,pll2p);
printf("In decoupled DFC solution, PLLs shoud take"
" fixed values, avoid MFC.\n");
printf("For test purpose, please switch all clock"
" to PLL1 based first\n");
return -1;
} else {
debug("Enable PLL2=%d, PLL2P=%d \n"
, pxa1826_ddr_value[level].pll2
, pxa1826_ddr_value[level].pll2p);
hw_setup_pll2(pxa1826_ddr_reg[level].pll2ref
, pxa1826_ddr_reg[level].pll2fb
, pxa1826_ddr_reg[level].apb_spare2);
}
}
/* wait for DFC triggered by CP/MSA is done */
status = readl(PMUA_DFC_STATUS);
while (max_delay && (status & 0x1)) {
udelay(10);
max_delay--;
status = readl(PMUA_DFC_STATUS);
}
if (max_delay <= 0) {
printf("AP cannot start HWDFC as DFC is in progress!\n");
printf("DFCAP %x, DFCSTATUS %x,\n",
readl(PMUA_DFC_AP),
readl(PMUA_DFC_STATUS));
return -1;
}
/* Check if AP ISR is set, if set, clear it */
isr = readl(PMUA_AP_ISR);
if (isr & (1 << 1)) {
printf("Somebody doesn't clear ISR after FC! ISR: %X\n", isr);
writel(isr & ~(1 << 1), PMUA_AP_ISR);
}
/* trigger AP HWDFC */
dfc_ap = readl(PMUA_DFC_AP);
dfc_ap &= ~(0x7 << 1);
dfc_ap |= ((level << 1) | 0x1);
writel(dfc_ap, PMUA_DFC_AP);
/* polling ISR */
while (!((1 << 1) & readl(PMUA_AP_ISR)) && timeout)
timeout--;
if (timeout <= 0) {
printf("AP frequency change timeout!\n");
printf("APMU_ISR %x, DFC_LVL: 0x%x, DFC_AP %x, DFC_STATUS %x\n",
readl(PMUA_AP_ISR),
readl(PMUA_DFC_LEVEL0 + (level << 2)),
readl(PMUA_DFC_AP),
readl(PMUA_DFC_STATUS));
}
/* clear AP fc done signal */
writel(readl(PMUA_AP_ISR) & ~(1 << 1), PMUA_AP_ISR);
return 0;
}
/*This should be called by PMU_AO init*/
void pxa182x_fc_init(int ddr_mode, int max_corefreq_mode)
{
core_freq temp_aop;
ddr_freq temp_dop;
axi_freq temp_axi_op;
u32 mvolt;
int i;
if(max_corefreq_mode==1) {
aop_num = sizeof(ca7_value_1248M)/sizeof(core_freq);
ca7_value = ca7_value_1248M;
} else if(max_corefreq_mode==2) {
aop_num = sizeof(ca7_value_1057M)/sizeof(core_freq);
ca7_value = ca7_value_1057M;
} else {
aop_num = sizeof(ca7_value_832M)/sizeof(core_freq);
ca7_value = ca7_value_832M;
}
ca7_reg = (max_corefreq_mode == 2) ? ca7_reg_1057M : ca7_reg_1248M;
if(ddr_mode == 0) {
pxa1826_ddr_value = pxa1826_ddr_value_400op;
pxa1826_ddr_reg = pxa1826_ddr_reg_400op;
dop_num = sizeof(pxa1826_ddr_value_400op)/sizeof(ddr_freq);
}
if(ddr_mode == 2) {
pxa1826_ddr_value = pxa1826_ddr_value_416op;
pxa1826_ddr_reg = pxa1826_ddr_reg_416op;
dop_num = sizeof(pxa1826_ddr_value_416op)/sizeof(ddr_freq);
}
if(ddr_mode == 1) {
pxa1826_ddr_value = (max_corefreq_mode == 2) ? pxa1826_ddr_value_533op_1057svc : pxa1826_ddr_value_533op_non1057svc;
pxa1826_ddr_reg = pxa1826_ddr_reg_533op;
dop_num = sizeof(pxa1826_ddr_reg_533op)/sizeof(ddr_freq);
}
debug("aop_num=%d\r\n", aop_num);
pxa1826_svc_table_a0 = (max_corefreq_mode == 1) ? pxa1826_svc_table_a0_1248M :
(max_corefreq_mode ? pxa1826_svc_table_a0_1057M : pxa1826_svc_table_a0_832M);
csop_num = sizeof(cs_value)/sizeof(cs_freq);
axiop_num = sizeof(aclk_value)/sizeof(axi_freq);
axiop_get_dump(&temp_axi_op);
PMUM->SCCR |= 0x1;
/*
* MC_HW_SLP_TYPE:
* for nezha2,
* BIT7: enable table-based DDR clock change
* BIT8: use 2:1 mode for LPDDR2
* BIT9: enable the lpm exit ddr reg table 0
*
* for nezha3,
* BIT_10: 0-enable table-based DDR clock change
* BIT_11: 0- use 2:1 mode for LPDDR2/3, set in BLF file instead
* BIT_8: 0-enable the lpm exit ddr reg table 0
* BIT_9: 0- enable auto DLL update during HW DVC
*/
if (cpu_is_pxa1826()) {
PMUA->MC_HW_SLP_TYPE &= ~(BIT8 | BIT10);
dop_get_dump(&temp_dop);
if (c_dop == OP_INVALID) {
printf("Note:current DDR OP is not official defined\n");
ddfc_set(0);
c_dop = 0;
}
CIU->SW_SCRATCH = c_dop;
max_dvc = pxa1826_ddr_value[c_dop].dvc;
if (max_dvc != 0) {
printf("Warning: the boot up PP DVL "
"assumption is wrong!\n");
}
/* SQU_CTRL_0 */
*(volatile uint32_t *)(0xd42A0000) = 0x10000000;
/* CIU->AP_DMA_AXI_XTC */
*(volatile uint32_t *)(0xd4282CF8) = 0x00000010;
}
/* write initial dvc value */
soc_dvc_init();
if (c_axiop == OP_INVALID) {
printf("Note:current AXI OP is not official defined.\n");
axidfc_set(0);
c_axiop = 0;
}
aop_get_dump(&temp_aop);
if (c_aop == OP_INVALID) {
printf("Note:current CA7 OP is not official defined.\n");
adfc_set(0); /* should not use adfc invoking DVC here */
c_aop = 0;
}
/*Note: we direclty do the CS change to PP0 for simple*/
PMUA->TRACE_CONFIG &= ~0x003E07C7;
PMUA->TRACE_CONFIG |= cs_reg[0].trace_config;
PMUA->TRACE_CONFIG |= 0x00008020;
debug("Coresight OP changed to OP0\n");
c_csop = 0;
GET_CHIP_DVL4OP_Ax(ca7_value, aop_num-1);
GET_CHIP_DVL4OP_Ax(aclk_value, axiop_num-1);
GET_CHIP_DVL4OP_Ax(pxa1826_ddr_value, dop_num-1);
/*Prepare the DVC settings before enable the DVC mechanism.*/
max_dvc = ca7_value[c_aop].dvc;
if (max_dvc < aclk_value[c_axiop].dvc)
max_dvc = aclk_value[c_axiop].dvc;
if (max_dvc < cs_value[c_csop].dvc)
max_dvc = cs_value[c_csop].dvc;
if (max_dvc < pxa1826_ddr_value[c_dop].dvc)
max_dvc = pxa1826_ddr_value[c_dop].dvc;
if (max_dvc < 1)
max_dvc = 1; /* assume CP side boot OP need DVL1 */
*(volatile uint32_t *)(0xd401e2E0) = 0x90C1; /* DVC1, GPIO78 */
*(volatile uint32_t *)(0xd401e2E4) = 0x90C1; /* DVC0, GPIO79 */
mvolt = get_mvolt_by_dvc(max_dvc);
set_volt(mvolt);
udelay(40); /* 40us(40us *12.5mv/us) should be enough */
pxa182x_init_hwdfc();
adfc(aop_num-1);
axidfc(axiop_num-1);
ddfc_set(0);
ddr_hwdfc_seq(dop_num-1);
check_dop(dop_num-1);
pxa182x_init_hwdvc();
find_ddr_level();
}
void update_voltage_dvcl2h(uint32_t initiator, uint32_t top)
{
uint32_t max_dvc_next, mvolt;
if (1 == initiator) { /* ap core */
max_dvc_next = ca7_value[top].dvc;
if (max_dvc_next < aclk_value[c_axiop].dvc)
max_dvc_next = aclk_value[c_axiop].dvc;
if (max_dvc_next < cs_value[c_csop].dvc)
max_dvc_next = cs_value[c_csop].dvc;
if (max_dvc_next < pxa1826_ddr_value[c_dop].dvc)
max_dvc_next = pxa1826_ddr_value[c_dop].dvc;
} else if (2 == initiator) { /* AXI */
max_dvc_next = aclk_value[top].dvc;
if (max_dvc_next < ca7_value[c_aop].dvc)
max_dvc_next = ca7_value[c_aop].dvc;
if (max_dvc_next < cs_value[c_csop].dvc)
max_dvc_next = cs_value[c_csop].dvc;
if (max_dvc_next < pxa1826_ddr_value[c_dop].dvc)
max_dvc_next = pxa1826_ddr_value[c_dop].dvc;
} else if (3 == initiator) { /* Coresight */
max_dvc_next = cs_value[top].dvc;
if (max_dvc_next < ca7_value[c_aop].dvc)
max_dvc_next = ca7_value[c_aop].dvc;
if (max_dvc_next < aclk_value[c_axiop].dvc)
max_dvc_next = aclk_value[c_axiop].dvc;
if (max_dvc_next < pxa1826_ddr_value[c_dop].dvc)
max_dvc_next = pxa1826_ddr_value[c_dop].dvc;
} else if (5 == initiator) { /* ddr */
max_dvc_next = pxa1826_ddr_value[top].dvc;
if (max_dvc_next < ca7_value[c_aop].dvc)
max_dvc_next = ca7_value[c_aop].dvc;
if (max_dvc_next < aclk_value[c_axiop].dvc)
max_dvc_next = aclk_value[c_axiop].dvc;
if (max_dvc_next < cs_value[c_csop].dvc)
max_dvc_next = cs_value[c_csop].dvc;
} else {
printf("Warning: unsuppoted DVC initiator!\n");
max_dvc_next = 3;
}
debug("l2h:The max_dvc_next = %d\n", max_dvc_next);
if (max_dvc_next > (uint32_t) DVC11) {
printf("ERROR:invalid dvc_next\n");
} else if (max_dvc_next < max_dvc) {
printf("ERROR: wrong voltage direction\n");
} else {
mvolt = get_mvolt_by_dvc(max_dvc_next);
set_volt(mvolt);
udelay(20);
max_dvc = max_dvc_next;
}
}
void update_voltage_dvch2l(uint32_t initiator, uint32_t top)
{
uint32_t max_dvc_next, mvolt;
if (1 == initiator) {
max_dvc_next = ca7_value[top].dvc;
if (max_dvc_next < aclk_value[c_axiop].dvc)
max_dvc_next = aclk_value[c_axiop].dvc;
if (max_dvc_next < cs_value[c_csop].dvc)
max_dvc_next = cs_value[c_csop].dvc;
if (max_dvc_next < pxa1826_ddr_value[c_dop].dvc)
max_dvc_next = pxa1826_ddr_value[c_dop].dvc;
} else if (2 == initiator) {
max_dvc_next = aclk_value[top].dvc;
if (max_dvc_next < ca7_value[c_aop].dvc)
max_dvc_next = ca7_value[c_aop].dvc;
if (max_dvc_next < cs_value[c_csop].dvc)
max_dvc_next = cs_value[c_csop].dvc;
if (max_dvc_next < pxa1826_ddr_value[c_dop].dvc)
max_dvc_next = pxa1826_ddr_value[c_dop].dvc;
}
else if (3 == initiator) {
max_dvc_next = cs_value[top].dvc;
if (max_dvc_next < ca7_value[c_aop].dvc)
max_dvc_next = ca7_value[c_aop].dvc;
if (max_dvc_next < aclk_value[c_axiop].dvc)
max_dvc_next = aclk_value[c_axiop].dvc;
if (max_dvc_next < pxa1826_ddr_value[c_dop].dvc)
max_dvc_next = pxa1826_ddr_value[c_dop].dvc;
} else if (5 == initiator) { /* DDR */
max_dvc_next = pxa1826_ddr_value[top].dvc;
if (max_dvc_next < ca7_value[c_aop].dvc)
max_dvc_next = ca7_value[c_aop].dvc;
if (max_dvc_next < aclk_value[c_axiop].dvc)
max_dvc_next = aclk_value[c_axiop].dvc;
if (max_dvc_next < cs_value[c_csop].dvc)
max_dvc_next = cs_value[c_csop].dvc;
} else {
printf("Warning: unsuppoted DVC initiator!\n");
max_dvc_next = 3;
}
debug("h2l:The max_dvc_next = %d\n ", max_dvc_next);
if (max_dvc_next > (uint32_t) DVC11) {
printf("ERROR:invalid dvc_next\n");
} else if (max_dvc_next > max_dvc) {
printf("ERROR: wrong voltage direction\n");
} else {
mvolt = get_mvolt_by_dvc(max_dvc_next);
set_volt(mvolt);
udelay(20);
/* With SW-DVC, we need to make sure CP can work at the lowest DVL */
max_dvc = max_dvc_next;
}
}
/*
*Must make sure there is only one entity of this function is in executing at
*any time,better to be in critial section, return value is OP_INVALID or MV_OK
*/
uint32_t adfc(uint32_t top)
{
uint32_t pll2=0, pll2p=0;
uint32_t flags;
if (top == c_aop) {
debug("CA7 is already at OP %d!\n", c_aop);
dump_cur_aop();
return c_aop;
}
if (ca7_value[top].pll2 || ca7_value[top].pll2p) {
get_pll2(&pll2, &pll2p);
if (((abs_diff(ca7_value[top].pll2, pll2)>=5)&&
pll2&&ca7_value[top].pll2) ||
((abs_diff(ca7_value[top].pll2p, pll2p)>=5)&&
pll2p&&ca7_value[top].pll2p)) {
printf("FAIL:PLL2 is used at another frequency pll2 ="
" %d, pll2p= %d already!\n", pll2,pll2p);
printf("In decoupled DFC solution, PLLs shoud take"
" fixed values, avoid MFC.\n");
printf("For test purpose, please switch all clock to"
" PLL1 based first\n");
return c_aop;
} else {
debug("Enable PLL2=%d, PLL2P=%d \n"
, ca7_value[top].pll2, ca7_value[top].pll2p);
hw_setup_pll2(ca7_reg[top].pll2ref
, ca7_reg[top].pll2fb, ca7_reg[top].apb_spare2);
}
}
get_fc_sm(0);
local_irq_save(flags);
if (ca7_value[top].dvc > ca7_value[c_aop].dvc) {
update_voltage_dvcl2h(1, top);
}
if (ca7_value[top].pclk > ca7_value[c_aop].pclk) {
update_memory_xtc(ca7_value[top].pclk);
}
adfc_set(top);
if (!pll2_is_inuse()) {
sw_turnoff_pll2();
}
if (ca7_value[top].pclk < ca7_value[c_aop].pclk) {
update_memory_xtc(ca7_value[top].pclk);
}
if (ca7_value[top].dvc < ca7_value[c_aop].dvc) {
update_voltage_dvch2l(1, top);
}
put_fc_sm(0);
local_irq_restore(flags);
return (check_aop(top));
}
uint32_t ddfc(uint32_t top)
{
uint32_t pll2=0, pll2p=0;
uint32_t flags;
if (top == c_dop) {
debug("DDR is already at OP %d!\n", c_dop);
dump_cur_dop();
return c_dop;
}
if (pxa1826_ddr_value[top].pll2 || pxa1826_ddr_value[top].pll2p) {
/* get the current pll2 and pll2p value */
get_pll2(&pll2, &pll2p);
if (((abs_diff(pxa1826_ddr_value[top].pll2, pll2)>=5)&&
pll2&&pxa1826_ddr_value[top].pll2) ||
((abs_diff(pxa1826_ddr_value[top].pll2p, pll2p)>=5)&&
pll2p&&pxa1826_ddr_value[top].pll2p)) {
printf("FAIL:PLL2 is used at another frequency pll2 ="
" %d, pll2p= %d already!\n", pll2,pll2p);
printf("In decoupled DFC solution, PLLs shoud take"
" fixed values, avoid MFC.\n");
printf("For test purpose, please switch all clock"
" to PLL1 based first\n");
return c_dop;
} else {
debug("Enable PLL2=%d, PLL2P=%d \n"
, pxa1826_ddr_value[top].pll2
, pxa1826_ddr_value[top].pll2p);
hw_setup_pll2(pxa1826_ddr_reg[top].pll2ref
, pxa1826_ddr_reg[top].pll2fb
, pxa1826_ddr_reg[top].apb_spare2);
}
}
get_fc_sm(0);
local_irq_save(flags);
if (pxa1826_ddr_value[top].dvc > pxa1826_ddr_value[c_dop].dvc) {
update_voltage_dvcl2h(5, top);
}
ddfc_set(top);
if (!pll2_is_inuse()) {
sw_turnoff_pll2();
}
if (pxa1826_ddr_value[top].dvc < pxa1826_ddr_value[c_dop].dvc) {
update_voltage_dvch2l(5, top);
}
put_fc_sm(0);
local_irq_restore(flags);
return (check_dop(top));
}
uint32_t axidfc(uint32_t top)
{
uint32_t flags;
axi_freq temp;
c_axiop = axiop_get_dump(&temp);
if (top == c_axiop) {
debug("ACLK is already at OP %d!\n", c_axiop);
dump_cur_axiop();
return c_axiop;
}
get_fc_sm(0);
local_irq_save(flags);
if (aclk_value[top].dvc > aclk_value[c_axiop].dvc)
update_voltage_dvcl2h(2, top);
axidfc_set(top);
if (aclk_value[top].dvc < aclk_value[c_axiop].dvc)
update_voltage_dvch2l(2, top);
put_fc_sm(0);
local_irq_restore(flags);
return (check_axiop(top));
}
uint32_t aop_test(uint32_t argc, char * const argv[])
{
uint32_t test_mode;
(void)argc;
if ((strcmp((const char*)argv[2], "/c") == 0) ||
(strcmp((const char*)argv[2], "--check") == 0)){
dump_cur_aop();
return MV_OK;
}
test_mode = simple_strtol((char*)argv[2],NULL,10);
if (test_mode < aop_num) {
adfc(test_mode);
return MV_OK;
} else {
printf("unsupported core op\n");
}
return MV_OK;
}
uint32_t dop_test(uint32_t argc, char * const argv[])
{
uint32_t test_mode;
(void)argc;
if ((strcmp((const char*)argv[2], "/c") == 0) ||
(strcmp((const char*)argv[2], "--check") == 0)){
dump_cur_dop();
return MV_OK;
}
test_mode = simple_strtol((char*)argv[2],NULL,10);
if (test_mode < dop_num) {
ddfc(test_mode);
return MV_OK;
} else {
printf("unsupported ddr op\n");
}
return MV_OK;
}
uint32_t axiop_test(uint32_t argc, char * const argv[])
{
uint32_t test_mode;
(void)argc;
if ((strcmp((const char*)argv[2], "/c") == 0) ||
(strcmp((const char*)argv[2], "--check") == 0)) {
dump_cur_axiop();
return MV_OK;
}
test_mode = simple_strtol((char*)argv[2],NULL,10);
if (test_mode < axiop_num) {
axidfc(test_mode);
return MV_OK;
} else {
printf("unsupported axi op\n");
}
return MV_OK;
}
void ppset_help(void)
{
printf(" ppset - change the device OP to defined one.\n");
printf(" ppset /r or ppset --regdump: dump pmu register.\n");
printf(" ppset /h or ppset --help: print the help information\n");
printf(" ==========================================================\n");
printf(" Syntax: ppset OPdevice OPmode <op_count>\n");
printf(" OP_device: core, ddr, axi\n");
printf(" OP_mode: if it's < OP_NUM, then just change OP to OP_mode\n");
return;
}
/*
* ppset <OP_device> <OP_index>
* OP_device: core/axi/cs
* OP_index: 0..n, "/c", "help", "/h"
*/
static int do_ppset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
printf("\n");
switch (argc) {
case 1:
printf("ppset--> No PP specified.\n");
ppset_help();
break;
default:
if (check_help(argv[1])) {
ppset_help();
dump_aop_list();
dump_axiop_list();
dump_dop_list();
} else if ((strcmp((const char*)argv[1], "/c") == 0)) {
dump_cur_aop();
dump_cur_dop();
dump_cur_axiop();
} else {
if ((strcmp((char *)argv[1], "core") == 0)) {
if (MV_OK == aop_test(argc, argv))
return 1;
else
return 0;
} else if ((strcmp((char*)argv[1], "ddr") == 0)) {
if (MV_OK == dop_test(argc, argv))
return 1;
else
return 0;
} else if ((strcmp((char*)argv[1], "axi") == 0)) {
if (MV_OK == axiop_test(argc, argv))
return 1;
else
return 0;
}
}
}
return 0;
}
static unsigned int convert_profile2commdvl(unsigned int uiprofile)
{
unsigned int cmbindex = 0;
if (uiprofile >= 1 && uiprofile <= 3)
cmbindex = 1;
else if (uiprofile == 4)
cmbindex = 2;
else if (uiprofile >= 5 && uiprofile <= 8)
cmbindex = 3;
else
cmbindex = 0;
return cmbindex;
}
int getcpdvcinfo(struct cpmsa_dvc_info *dvc_info)
{
if (!dvc_info) {
printf("%s: dvc_info NULL\n", __func__);
return -1;
}
memcpy(dvc_info,
&cpmsa_dvc_info_182x[convert_profile2commdvl(uiProfile)],
sizeof(struct cpmsa_dvc_info));
return 0;
}
int getddrdfcinfo(struct ddr_dfc_info *dfc_info)
{
if (!dfc_info) {
printf("%s: dvc_info NULL\n", __func__);
return -1;
}
memcpy(dfc_info, &ddrdfcinfo, sizeof(struct ddr_dfc_info));
return 0;
}
U_BOOT_CMD(
ppset, 6, 1, do_ppset,
"change core/ddr/axi op\n",
""
);