/*
* Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
#include
#include
#include
#include "ddr_training_impl.h"
#define HI_SYS_CTL_REG SYS_CTRL_REG_BASE
#define OK 0
#define ERROR -1
#define DDR_CA0_OFST 0x24
#define DDR_CA1_OFST 0x28
#define DDR_CA2_OFST 0x2c
#define REG_BASE_DDRC 0x11130000
#define DDRC_CTRL_SREF_OFST 0x8000 + 0x0
#define DDRC_CFG_DDRMODE_OFST 0x8000 + 0X50
#define DDRC_CURR_FUNC_OFST 0x8000 + 0x294
#define DDRC1_CTRL_SREF_OFST 0x9000 + 0x0
#define DDRC1_CFG_DDRMODE_OFST 0x9000 + 0X50
#define DDRC1_CURR_FUNC_OFST 0x9000 + 0x294
#define DDRC2_CTRL_SREF_OFST 0xa000 + 0x0
#define DDRC2_CFG_DDRMODE_OFST 0xa000 + 0X50
#define DDRC2_CURR_FUNC_OFST 0xa000 + 0x294
#define DDRC3_CTRL_SREF_OFST 0xb000 + 0x0
#define DDRC3_CFG_DDRMODE_OFST 0xb000 + 0X50
#define DDRC3_CURR_FUNC_OFST 0xb000 + 0x294
#define DDRC_SELF_REFURBISH_MASK (0x1)
static inline void delay(unsigned int num)
{
volatile unsigned int i;
for (i = 0; i < (100 * num); i++)
__asm__ __volatile__("nop");
}
extern void reset_cpu(unsigned long addr);
static inline void DWB(void) /* drain write buffer */
{
}
static inline unsigned int readl(unsigned addr)
{
unsigned int val;
val = (*(volatile unsigned int *)(uintptr_t)(addr));
return val;
}
static inline void writel(unsigned val, unsigned addr)
{
DWB();
(*(volatile unsigned *)(uintptr_t)(addr)) = val;
DWB();
}
#define REG_BASE_MISC 0x11024000
#ifdef DDR_SCRAMB_ENABLE
#undef reg_get
#define reg_get(addr) (*(volatile unsigned int *)((long)addr))
#undef reg_set
#define reg_set(addr, val) (*(volatile unsigned int *)((long)addr) = (val))
void ddr_scramb_start(unsigned int random1, unsigned int random2)
{
reg_set((unsigned int *)(REG_BASE_MISC + DDR_CA0_OFST), random1);
reg_set((unsigned int *)(REG_BASE_MISC + DDR_CA1_OFST), random2);
reg_set((unsigned int *)(REG_BASE_MISC + DDR_CA2_OFST), 0);
reg_set((unsigned int *)(REG_BASE_MISC + DDR_CA2_OFST), 0x10);
delay(0x100);
reg_set((unsigned int *)(REG_BASE_MISC + DDR_CA0_OFST), 0);
reg_set((unsigned int *)(REG_BASE_MISC + DDR_CA1_OFST), 0);
}
#define REG_BASE_RNG_GEN 0x10130000
#define TRNG_DSTA_FIFO_DATA_OFST 0x204
#define TRNG_DATA_ST_OFST 0x208
#define HISEC_COM_TRNG_CTRL_OFST 0x200
#define BIT_TRNG_FIFO_DATA_CNT 0x8
#define TRNG_FIFO_DATA_CNT_MASK 0xff
#define TRNG_CTRL_DEF_VAL 0xa
#define REG_PERI_CRG_TRNG 0x2d80
#define TRNG_CLK_ENABLE (0x1 << 4)
#define TRNG_DO_SRST (0x1 << 0)
#define TRNG_CLK_DISABLE ~(0x1 << 4)
#define TRNG_UNDO_SRST ~(0x1 << 0)
/* get random number */
int get_random_num(void)
{
unsigned int reg_val = 0;
do {
reg_val = reg_get(REG_BASE_RNG_GEN + TRNG_DATA_ST_OFST);
} while (!((reg_val >> BIT_TRNG_FIFO_DATA_CNT) &
TRNG_FIFO_DATA_CNT_MASK));
reg_val = reg_get(REG_BASE_RNG_GEN + TRNG_DSTA_FIFO_DATA_OFST);
return reg_val;
}
void trng_init(void)
{
unsigned int reg_val = 0;
/* open trng clock */
reg_val = reg_get(CRG_REG_BASE + REG_PERI_CRG_TRNG);
reg_val |= TRNG_CLK_ENABLE;
reg_val &= TRNG_UNDO_SRST;
reg_set(CRG_REG_BASE + REG_PERI_CRG_TRNG, reg_val);
/* set trng ctrl register */
reg_set(REG_BASE_RNG_GEN + HISEC_COM_TRNG_CTRL_OFST,
TRNG_CTRL_DEF_VAL);
}
void trng_deinit(void)
{
unsigned int reg_val = 0;
/* close trng clock */
reg_val = reg_get(CRG_REG_BASE + REG_PERI_CRG_TRNG);
reg_val &= TRNG_CLK_DISABLE;
reg_set(CRG_REG_BASE + REG_PERI_CRG_TRNG, reg_val);
}
int ddr_scramb(void)
{
unsigned int random_num1 = 0;
unsigned int random_num2 = 0;
unsigned int reg_val[4] = {0,0,0,0};
unsigned int ddrca_val[4] = {0, 0, 0, 0};
unsigned int ddrc_isvalid[4] = {0,0,0,0};
/* read ddrc_cfg_ddrmode register,
* if value[3:0] is not 0x0 ,the channel is valid.
*/
ddrc_isvalid[0] = (reg_get(REG_BASE_DDRC + DDRC_CFG_DDRMODE_OFST) & 0xf)?1:0;
ddrc_isvalid[1] = (reg_get(REG_BASE_DDRC + DDRC1_CFG_DDRMODE_OFST)& 0xf)?1:0;
ddrc_isvalid[2] = (reg_get(REG_BASE_DDRC + DDRC2_CFG_DDRMODE_OFST)& 0xf)?1:0;
ddrc_isvalid[3] = (reg_get(REG_BASE_DDRC + DDRC3_CFG_DDRMODE_OFST)& 0xf)?1:0;
/* set ddrc to do self-refurbish */
if(ddrc_isvalid[0])
reg_set(REG_BASE_DDRC + DDRC_CTRL_SREF_OFST, 0x1);
if(ddrc_isvalid[1])
reg_set(REG_BASE_DDRC + DDRC1_CTRL_SREF_OFST, 0x1);
if(ddrc_isvalid[2])
reg_set(REG_BASE_DDRC + DDRC2_CTRL_SREF_OFST, 0x1);
if(ddrc_isvalid[3])
reg_set(REG_BASE_DDRC + DDRC3_CTRL_SREF_OFST, 0x1);
/* wait the status of ddrc to be sef-refurbish */
do {
reg_val[0] = ddrc_isvalid[0]?(reg_get(REG_BASE_DDRC + DDRC_CURR_FUNC_OFST) & 0x1):1;
reg_val[1] = ddrc_isvalid[1]?(reg_get(REG_BASE_DDRC + DDRC1_CURR_FUNC_OFST) & 0x1):1;
reg_val[2] = ddrc_isvalid[2]?(reg_get(REG_BASE_DDRC + DDRC2_CURR_FUNC_OFST) & 0x1):1;
reg_val[3] = ddrc_isvalid[3]?(reg_get(REG_BASE_DDRC + DDRC3_CURR_FUNC_OFST) & 0x1):1;
} while (!(reg_val[0] & reg_val[1] & reg_val[2] & reg_val[3]));
if (ddrc_isvalid[0]) {
ddrca_val[0] = reg_get(REG_BASE_DDRC + 0x8164);
reg_set(REG_BASE_DDRC + 0x8184, ddrca_val[0]);
ddrca_val[1] = reg_get(REG_BASE_DDRC + 0x8168);
reg_set(REG_BASE_DDRC + 0x8188, ddrca_val[1]);
ddrca_val[2] = reg_get(REG_BASE_DDRC + 0x816c);
reg_set(REG_BASE_DDRC + 0x8048, ddrca_val[2]);
}
if (ddrc_isvalid[1]) {
ddrca_val[0] = reg_get(REG_BASE_DDRC + 0x9164);
reg_set(REG_BASE_DDRC + 0x9184, ddrca_val[0]);
ddrca_val[1] = reg_get(REG_BASE_DDRC + 0x9168);
reg_set(REG_BASE_DDRC + 0x9188, ddrca_val[1]);
ddrca_val[2] = reg_get(REG_BASE_DDRC + 0x916c);
reg_set(REG_BASE_DDRC + 0x9048, ddrca_val[2]);
}
if (ddrc_isvalid[2]) {
ddrca_val[0] = reg_get(REG_BASE_DDRC + 0xa164);
reg_set(REG_BASE_DDRC + 0xa184, ddrca_val[0]);
ddrca_val[1] = reg_get(REG_BASE_DDRC + 0xa168);
reg_set(REG_BASE_DDRC + 0xa188, ddrca_val[1]);
ddrca_val[2] = reg_get(REG_BASE_DDRC + 0xa16c);
reg_set(REG_BASE_DDRC + 0xa048, ddrca_val[2]);
}
if (ddrc_isvalid[3]) {
ddrca_val[0] = reg_get(REG_BASE_DDRC + 0xb164);
reg_set(REG_BASE_DDRC + 0xb184, ddrca_val[0]);
ddrca_val[1] = reg_get(REG_BASE_DDRC + 0xb168);
reg_set(REG_BASE_DDRC + 0xb188, ddrca_val[1]);
ddrca_val[2] = reg_get(REG_BASE_DDRC + 0xb16c);
reg_set(REG_BASE_DDRC + 0xb048, ddrca_val[2]);
}
trng_init();
/* get random number */
random_num1 = get_random_num();
random_num2 = get_random_num();
/* start ddr scrambling */
ddr_scramb_start(random_num1, random_num2);
/* clear random number */
(void)get_random_num();
(void)get_random_num();
(void)get_random_num();
(void)get_random_num();
trng_deinit();
/* set ddrc to exit self-refurbish */
if(ddrc_isvalid[0])
reg_set(REG_BASE_DDRC + DDRC_CTRL_SREF_OFST, (0x1<<1));
if(ddrc_isvalid[1])
reg_set(REG_BASE_DDRC + DDRC1_CTRL_SREF_OFST, (0x1<<1));
if(ddrc_isvalid[2])
reg_set(REG_BASE_DDRC + DDRC2_CTRL_SREF_OFST, (0x1<<1));
if(ddrc_isvalid[3])
reg_set(REG_BASE_DDRC + DDRC3_CTRL_SREF_OFST, (0x1<<1));
/* wait the status of ddrc to be normal */
do {
reg_val[0] = ddrc_isvalid[0]?(reg_get(REG_BASE_DDRC + DDRC_CURR_FUNC_OFST) & 0x1):0;
reg_val[1] = ddrc_isvalid[1]?(reg_get(REG_BASE_DDRC + DDRC1_CURR_FUNC_OFST) & 0x1):0;
reg_val[2] = ddrc_isvalid[2]?(reg_get(REG_BASE_DDRC + DDRC2_CURR_FUNC_OFST) & 0x1):0;
reg_val[3] = ddrc_isvalid[3]?(reg_get(REG_BASE_DDRC + DDRC3_CURR_FUNC_OFST) & 0x1):0;
} while (reg_val[0] | reg_val[1] | reg_val[2] | reg_val[3]);
return OK;
}
#endif /* DDR_SCRAMB_ENABLE */
#define HPM_CORE_VOL_REG 0x11029000
#define HPM_CPU_VOL_REG 0x11029004
#define HMP_CLK_REG 0x11014A80
#define CPU_HPM_CTRL0_REG 0x1102B000
#define CORE_HPM_CTRL0_REG 0x1102B010
#define HPM_CPU_REG0 0x1102B008
#define HPM_CPU_REG1 0x1102B00c
#define HPM_CORE_REG0 0x1102B018
#define HPM_CORE_REG1 0x1102B01c
#define CYCLE_NUM 32
#define SVB_RECORD_REG0 0x11020340
#define SVB_RECORD_REG1 0x11020344
#define VOLTAGE_RECOED_REG 0x1102015C
#define SVB_VERSION_REG 0x11020168
#define SVB_VERSION 0x101
#define OTP_HPM_CORE_REG 0x11021504
#define OTP_HPM_CPU_REG 0x11021530
#define OTP_VOLTAGE_DELTA_CORE_REG 0x1102150C
#define OTP_VOLTAGE_DELTA_CPU_REG 0x11021518
#define TSENSOR_STATUS0 0X1102A008
static unsigned hpm_value_avg(unsigned int *val, int num)
{
unsigned int i;
unsigned tmp = 0;
for (i = 0; i < num; i++)
tmp += val[i] >> 2;
return tmp >> 5;
}
static void get_hpm_value(unsigned int *hpm_core, unsigned int *hpm_cpu)
{
int i;
unsigned int temp;
unsigned int core_value[4];
unsigned int cpu_value[4];
core_value[0] = 0;
core_value[1] = 0;
core_value[2] = 0;
core_value[3] = 0;
cpu_value[0] = 0;
cpu_value[1] = 0;
cpu_value[2] = 0;
cpu_value[3] = 0;
for (i = 0; i < CYCLE_NUM; i++) {
delay(24);
/* cpu */
temp = readl(HPM_CPU_REG0);
cpu_value[1] += (temp >> 16) & 0x3ff;
cpu_value[0] += temp & 0x3ff;
temp = readl(HPM_CPU_REG1);
cpu_value[3] += (temp >> 16) & 0x3ff;
cpu_value[2] += temp & 0x3ff;
/* core */
temp = readl(HPM_CORE_REG0);
core_value[1] += (temp >> 16) & 0x3ff;
core_value[0] += temp & 0x3ff;
temp = readl(HPM_CORE_REG1);
core_value[3] += (temp >> 16) & 0x3ff;
core_value[2] += temp & 0x3ff;
}
*hpm_core = hpm_value_avg(core_value, 4);
*hpm_cpu = hpm_value_avg(cpu_value, 4);
}
static void start_hpm(unsigned int *hpm_core, unsigned int *hpm_cpu)
{
/* core */
writel(0x60080001, CORE_HPM_CTRL0_REG);
/* cpu */
writel(0x60080001, CPU_HPM_CTRL0_REG);
delay(240); /*10ms*/
get_hpm_value(hpm_core, hpm_cpu);
}
static void hpm_check(unsigned int *hpm_core, unsigned int *hpm_cpu,
int *temperature, unsigned int hpm_from_otp)
{
union {
struct {
unsigned int sys_hpm_core : 9; /* [8..0]*/
unsigned int reserved_0 : 7; /* [15..9]*/
unsigned int sys_hpm_cpu : 9; /* [24..16]*/
unsigned int reserved_1 : 7; /* [31..25]*/
} bits;
unsigned int u32;
} hpm_reg0; /*SVB_RECORD_REG0 0x11020340*/
union {
struct {
signed char temperature : 8; /* [7..0]*/
unsigned int reserved_0 : 20; /* [27..8]*/
unsigned int temperature_err : 1; /* [28]*/
unsigned int hpm_cpu_err : 1; /* [29]*/
unsigned int hpm_core_err : 1; /* [30]*/
unsigned int from_otp : 1; /* [31]*/
} bits;
unsigned int u32;
} hpm_reg1; /*SVB_RECORD_REG1 0x11020344*/
hpm_reg0.u32 = 0;
hpm_reg1.u32 = 0;
if (*hpm_core < 200) {
*hpm_core = 200;
hpm_reg1.bits.hpm_core_err = 1;
}
if (*hpm_core > 300) {
*hpm_core = 300;
hpm_reg1.bits.hpm_core_err = 1;
}
if (*hpm_cpu < 240) {
*hpm_cpu = 240;
hpm_reg1.bits.hpm_cpu_err = 1;
}
if (*hpm_cpu > 330) {
*hpm_cpu = 330;
hpm_reg1.bits.hpm_cpu_err = 1;
}
if (*temperature < -40) {
*temperature = -40;
hpm_reg1.bits.temperature_err = 1;
}
if (*temperature > 110) {
*temperature = 110;
hpm_reg1.bits.temperature_err = 1;
}
hpm_reg0.bits.sys_hpm_core = *hpm_core;
hpm_reg0.bits.sys_hpm_cpu = *hpm_cpu;
hpm_reg1.bits.temperature = (signed char) *temperature;
hpm_reg1.bits.from_otp = hpm_from_otp;
writel(hpm_reg0.u32, SVB_RECORD_REG0);
writel(hpm_reg1.u32, SVB_RECORD_REG1);
}
static void voltage_check(double *pcore_mv, double *pcpu_mv, double core_delta_mv)
{
short otp_voltage_delta_core;
short otp_voltage_delta_cpu;
union {
struct {
unsigned int voltage_core : 12; /* [11..0]*/
unsigned int voltage_cpu : 12; /* [23..12]*/
unsigned int delta_core : 8; /* [31..24]*/
} bits;
unsigned int u32;
} voltage_reg;
otp_voltage_delta_core = (short)(readl(OTP_VOLTAGE_DELTA_CORE_REG) & 0xffff);
otp_voltage_delta_cpu = (short)(readl(OTP_VOLTAGE_DELTA_CPU_REG) & 0xffff);
*pcore_mv += (double)otp_voltage_delta_core;
*pcpu_mv += (double)otp_voltage_delta_cpu;
voltage_reg.u32 = 0;
voltage_reg.bits.voltage_core = (unsigned int)*pcore_mv;
voltage_reg.bits.voltage_cpu = (unsigned int)*pcpu_mv;
voltage_reg.bits.delta_core = (unsigned int)core_delta_mv;
writel(voltage_reg.u32, VOLTAGE_RECOED_REG);
}
static void set_hpm_core_volt(double mv)
{
unsigned int svb_value;
svb_value = ((unsigned int)((1078000 - mv * 1000) * 84) & 0xffff0000) + 0x19f5;
writel(svb_value, HPM_CORE_VOL_REG);
}
static void set_hpm_cpu_volt(double mv)
{
unsigned int svb_value;
svb_value = ((unsigned int)((1078000 - mv * 1000) * 84) & 0xffff0000) + 0x19f5;
writel(svb_value, HPM_CPU_VOL_REG);
}
static void get_temperature(int *temperature)
{
int value = 0;
value = (int)(readl(TSENSOR_STATUS0) & 0x3ff);
*temperature = (int)(((value - 127) * 165) / 784.0 ) - 40;
}
static void get_ate_hpm(unsigned int *pate_hpm_core, unsigned int *pate_hpm_cpu)
{
*pate_hpm_core = (unsigned int)(readl(OTP_HPM_CORE_REG) & 0xffff);
*pate_hpm_cpu = (unsigned int)(readl(OTP_HPM_CPU_REG) & 0xffff);
}
static void start_svb(double *pcore_mv, double *pcpu_mv)
{
unsigned int ate_hpm_core;
unsigned int ate_hpm_cpu;
unsigned int hpm_core;
unsigned int hpm_cpu;
int temperature_delta;
double core_delta_mv;
int temperature;
unsigned int hpm_from_otp;
unsigned int version;
version = readl(SVB_VERSION_REG);
version = (version & 0x0000FFFF) | (SVB_VERSION << 16);
writel(version, SVB_VERSION_REG);
get_temperature(&temperature);
get_ate_hpm(&ate_hpm_core,&ate_hpm_cpu);
if ((!ate_hpm_core) || (!ate_hpm_cpu)) {
set_hpm_core_volt(900);
set_hpm_cpu_volt(1000);
/* open hmp clock */
writel(0x10, HMP_CLK_REG);
start_hpm(&hpm_core, &hpm_cpu);
hpm_from_otp = 0;
} else {
/*ATE SVB transfer to board SVB*/
hpm_core = ate_hpm_core - 5;
hpm_cpu = ate_hpm_cpu - 12;
hpm_from_otp = 1;
}
hpm_check(&hpm_core, &hpm_cpu, &temperature, hpm_from_otp);
/******************************** CORE ********************************/
*pcore_mv = 1330 - 1.533 * hpm_core;
temperature_delta = 110 - temperature;
if (temperature_delta > 85)
temperature_delta = 85;
core_delta_mv = ((((double)hpm_core - 200) / 3.3) + 30.0) * ((double)temperature_delta / 85.0);
/******************************** CPU *********************************/
*pcpu_mv = 1728.384 - 2.545 * hpm_cpu; /*svb001*/
if (*pcpu_mv > 1050)
*pcpu_mv = 1050;
if (*pcpu_mv < 910)
*pcpu_mv = 910;
voltage_check(pcore_mv, pcpu_mv, core_delta_mv);
set_hpm_core_volt(*pcore_mv - core_delta_mv);
set_hpm_cpu_volt(*pcpu_mv);
/* delay 10ms do not delete */
delay(240);
}
static void end_svb(const double *pcore_mv,const double *pcpu_mv)
{
set_hpm_core_volt(*pcore_mv);
/* delay 10ms do not delete */
delay(120);
}
void start_ddr_training(unsigned int base)
{
double core_mv;
double cpu_mv;
start_svb(&core_mv, &cpu_mv);
/* ddr hw training */
ddr_hw_training_if();
/* ddr sw training */
ddr_sw_training_if();
/*the value should config after trainning, or
it will cause chip compatibility problems */
if ((readl(DDR_REG_BASE_PHY0 + DDR_PHY_DRAMCFG)
& PHY_DRAMCFG_TYPE_MASK) == PHY_DRAMCFG_TYPE_LPDDR4) {
writel(0x401, DDR_REG_BASE_DMC0 + 0x28);
writel(0x401, DDR_REG_BASE_DMC1 + 0x28);
} else
writel(0x401, DDR_REG_BASE_DMC0 + 0x28);
if ((readl(DDR_REG_BASE_PHY1 + DDR_PHY_DRAMCFG)
& PHY_DRAMCFG_TYPE_MASK) == PHY_DRAMCFG_TYPE_LPDDR4) {
writel(0x401, DDR_REG_BASE_DMC2 + 0x28);
writel(0x401, DDR_REG_BASE_DMC3 + 0x28);
} else
writel(0x401, DDR_REG_BASE_DMC1 + 0x28);
#ifdef DDR_SCRAMB_ENABLE
/* enable ddr scramb */
ddr_scramb();
#endif
end_svb(&core_mv, &cpu_mv);
}