/* * lowlevel_init_v300.c * * The file is Hardware initialization. * * 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 static inline void delay(unsigned int num) { volatile unsigned int i; for (i = 0; i < (100 * num); i++) /* 100: Cycle */ __asm__ __volatile__("nop"); } 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_RNG_GEN 0x10090000 #define TRNG_DSTA_FIFO_DATA_OFST 0x204 #define TRNG_DATA_ST_OFST 0x208 #define BIT_TRNG_FIFO_DATA_CNT 0x8 #define TRNG_FIFO_DATA_CNT_MASK 0xff #define REG_PERI_CRG104 0x1a0 #define TRNG_CLK_ENABLE (0x1<<3) #define TRNG_CLK_DISABLE ~(0x1<<3) #define TRNG_CTRL_DEF_VAL 0xa #define HISEC_COM_TRNG_CTRL_OFST 0x200 #define REG_BASE_MISC 0x12030000 #define DDR_CA0_OFST 0x28 #define DDR_CA1_OFST 0x2C #define DDR_CA2_OFST 0x30 #define REG_BASE_DDRC 0x120d0000 #define DDRC_CTRL_SREF_OFST (0x8000 + 0x0) #define DDRC_CFG_DDRMODE_OFST (0x8000 + 0x50) #define DDRC_CURR_FUNC_OFST (0x8000 + 0x294) #define DDRC_CHANNEL_VALID_MASK 0xf #define DDRC_SELF_REFURBISH_MASK 0x1 #define DDRC_SELF_REFURBISH_EN 0x1 #define DDRC_SELF_REFURBISH_EXIT (0x1 << 1) #define HPM_CORE_MIN 150 #define HPM_CORE_MAX 350 #define TEMPERATURE_MIN 117 #define TEMPERATURE_MAX 841 #define HPM_CORE_VALUE_MIN 190 #define HPM_CORE_VALUE_MAX 310 #undef reg_get #undef reg_set #define reg_get(addr) readl(addr) #define reg_set(addr, val) writel(val, (unsigned int)(addr)) #define temperature_formula(val) (((((val) - 117) * 212) >> 10) - 40) #define hpm_formula(hpm, temp) ((hpm) + 4 + ((((temp) - 70) * 205) >> 10)) #define volt_formula(val) (1234 - ((1445 * (val)) >> 10)) #define duty_formula(val) ((unsigned int)((1099 - (val)) * 460) >> 10) void trng_init(void) { unsigned int reg_val; /* open rsa and trng clock */ reg_val = reg_get(CRG_REG_BASE + REG_PERI_CRG104); reg_val |= TRNG_CLK_ENABLE; reg_set(CRG_REG_BASE + REG_PERI_CRG104, 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; /* close rsa and trng clock */ reg_val = reg_get(CRG_REG_BASE + REG_PERI_CRG104); reg_val &= TRNG_CLK_DISABLE; reg_set(CRG_REG_BASE + REG_PERI_CRG104, reg_val); } /* svb */ #define SVB_VER_18EV300 0x10 #define CYCLE_NUM 4 #define HPM_CORE_REG0 0x120280d8 #define HPM_CORE_REG1 0x120280dc #define PWM0_REG 0X12080000 #define PWM_REG_OFFSET 0x20 #define PWM_CFG1 0X04 #define PWM_CTRL 0X0C #define SVB_VER_REG 0x12020168 #define HPM_CHECK_REG 0x1202015c #define SYS_CTRL_VOLT_REG 0x12020158 #define SVB_PWM_SEL 0x1202009c #define TSENSOR_STATUS0 0x120280bc #define OTP_HPM_CORE_REG 0x100a002c 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] >> NUM_2; return tmp >> NUM_2; } static void get_hpm_value(unsigned int *hpm_core) { int i; unsigned int temp; unsigned int core_value[NUM_4]; core_value[NUM_0] = 0; core_value[NUM_1] = 0; core_value[NUM_2] = 0; core_value[NUM_3] = 0; for (i = 0; i < CYCLE_NUM; i++) { temp = readl(HPM_CORE_REG0); core_value[NUM_1] += (temp >> 16) & 0x3ff; /* get hight 16 bits */ core_value[NUM_0] += temp & 0x3ff; temp = readl(HPM_CORE_REG1); core_value[NUM_3] += (temp >> 16) & 0x3ff; /* get hight 16 bits */ core_value[NUM_2] += temp & 0x3ff; } *hpm_core = hpm_value_avg(core_value, NUM_4); } static void start_hpm(unsigned int *hpm_core) { get_hpm_value(hpm_core); } static void hpm_check(unsigned int *hpm_core) { union { struct { unsigned int reserved_0 : 16; /* [15..0] */ unsigned int sys_hpm_core : 9; /* [24..16] */ unsigned int reserved_1 : 1; /* [25] */ unsigned int hpm_core_err : 1; /* [26] */ unsigned int reserved_2 : 5; /* [27..31] */ } bits; unsigned int u32; } sysboot10; sysboot10.u32 = readl(HPM_CHECK_REG); sysboot10.bits.sys_hpm_core = 0; sysboot10.bits.hpm_core_err = 0; if (*hpm_core < HPM_CORE_MIN) { *hpm_core = HPM_CORE_MIN; sysboot10.bits.hpm_core_err = 1; } if (*hpm_core > HPM_CORE_MAX) { *hpm_core = HPM_CORE_MAX; sysboot10.bits.hpm_core_err = 1; } sysboot10.bits.sys_hpm_core = *hpm_core; writel(sysboot10.u32, HPM_CHECK_REG); } static void get_temperature(unsigned int *temperature) { unsigned int value; value = readl(TSENSOR_STATUS0); value = value & 0x3ff; if (value <= TEMPERATURE_MIN) *temperature = -40; /* -40: temperature value */ else if (value >= TEMPERATURE_MAX) *temperature = 110; /* 110: temperature value */ else *temperature = temperature_formula(value); } static void adjust_hpm(unsigned int *hpm_core, unsigned int temperature) { /* 283 70: hpm_core and temperature Threshold */ if ((*hpm_core >= 283) && (temperature >= 70)) *hpm_core = hpm_formula(*hpm_core, temperature); /* 222 70: hpm_core and temperature Threshold */ else if ((*hpm_core <= 222) && (temperature >= 70)) *hpm_core = *hpm_core - NUM_4; } static void set_hpm_core_volt(unsigned int hpm_core_value, unsigned int pwm_id) { unsigned int volt; unsigned int duty; unsigned int otp_vmin_core = readl(OTP_HPM_CORE_REG); if (hpm_core_value <= HPM_CORE_VALUE_MIN) volt = 966; /* 966:vole val */ else if (hpm_core_value >= HPM_CORE_VALUE_MAX) volt = 796; /* 796:vole val */ else volt = volt_formula(hpm_core_value); volt = volt + (int)((short int)(otp_vmin_core >> 16)); /* get hight 16 bits */ writel(volt, SYS_CTRL_VOLT_REG); duty = duty_formula(volt); writel(duty, PWM0_REG + pwm_id * PWM_REG_OFFSET + PWM_CFG1); writel(0x5, PWM0_REG + pwm_id * PWM_REG_OFFSET + PWM_CTRL); } void start_svb(void) { unsigned int hpm_core = 0; unsigned int pwm_id; unsigned int temperature = 0; unsigned int tmp_reg = readl(SVB_VER_REG); tmp_reg = (tmp_reg & 0xff00ffff) | (SVB_VER_18EV300 << 16); /* Move Left 16bit */ writel(tmp_reg, SVB_VER_REG); get_temperature(&temperature); start_hpm(&hpm_core); adjust_hpm(&hpm_core, temperature); hpm_check(&hpm_core); pwm_id = readl(SVB_PWM_SEL) & 0xf; set_hpm_core_volt(hpm_core, pwm_id); delay(160); /* delay 160ms */ } /* [CUSTOM] DDR PHY0-PHY1 base register */ #define DDR_REG_BASE_PHY0 0x120dc000 /* [CUSTOM] DDR DMC0-DMC3 base register */ #define DDR_REG_BASE_DMC0 0x120d8000 #define DDR_REG_BASE_DMC1 0x120d8000 #ifdef DDR_REG_BASE_PHY1 #define DDR_REG_BASE_DMC2 0x120d9000 #define DDR_REG_BASE_DMC3 0x120d9000 #endif #define CRG_REG_BASE 0x12010000 #define PERI_CRG_DDRT 0x198 #define DDR_REG_BASE_SYSCTRL 0x12020000 /* [SYSCTRL]RAM Retention control register 0 */ #define SYSCTRL_MISC_CTRL4 0x8010 #define DDR_PHY_DRAMCFG 0x2c /* DRAM config register */ #define PHY_DRAMCFG_TYPE_MASK 0xf /* [3:0] */ #define PHY_DRAMCFG_TYPE_LPDDR4 0x6 /* [2:0] 110 LPDDR4 */ #define BYTE_NUM 2 /** * ddr_boot_prepare * @void * * Do some prepare before ddr training. * Keep empty when nothing to do. */ static void ddr_boot_prepare(struct tr_relate_reg *reg) { /* select ddrt bus path */ reg->custom.ive_ddrt_mst_sel = readl(DDR_REG_BASE_SYSCTRL + SYSCTRL_MISC_CTRL4); writel(reg->custom.ive_ddrt_mst_sel & 0xffffffdf, DDR_REG_BASE_SYSCTRL + SYSCTRL_MISC_CTRL4); /* turn on ddrt clock */ reg->custom.ddrt_clk_reg = readl(CRG_REG_BASE + PERI_CRG_DDRT); /* enable ddrt0 clock */ writel(reg->custom.ddrt_clk_reg | (0x1 << 1), CRG_REG_BASE + PERI_CRG_DDRT); __asm__ __volatile__("nop"); /* disable ddrt0 soft reset */ writel(readl(CRG_REG_BASE + PERI_CRG_DDRT) & (~(0x1 << 0)), CRG_REG_BASE + PERI_CRG_DDRT); /* disable rdqs anti-aging */ reg->custom.phy0_age_compst_en = readl(DDR_REG_BASE_PHY0 + DDR_PHY_PHYRSCTRL); writel((reg->custom.phy0_age_compst_en & 0x7fffffff), DDR_REG_BASE_PHY0 + DDR_PHY_PHYRSCTRL); #ifdef DDR_REG_BASE_PHY1 reg->custom.phy1_age_compst_en = readl(DDR_REG_BASE_PHY1 + DDR_PHY_PHYRSCTRL); writel((reg->custom.phy1_age_compst_en & 0x7fffffff), DDR_REG_BASE_PHY1 + DDR_PHY_PHYRSCTRL); #endif } /** * ddr_boot_restore * @void * * Restore register config after ddr training. * Keep empty when nothing to do. */ static void ddr_boot_restore(struct tr_relate_reg *reg) { /* restore ddrt bus path */ writel(reg->custom.ive_ddrt_mst_sel, DDR_REG_BASE_SYSCTRL + SYSCTRL_MISC_CTRL4); /* restore ddrt clock */ writel(reg->custom.ddrt_clk_reg, CRG_REG_BASE + PERI_CRG_DDRT); /* restore rdqs anti-aging */ writel(reg->custom.phy0_age_compst_en, DDR_REG_BASE_PHY0 + DDR_PHY_PHYRSCTRL); #ifdef DDR_REG_BASE_PHY1 writel(reg->custom.phy1_age_compst_en, DDR_REG_BASE_PHY1 + DDR_PHY_PHYRSCTRL); #endif } /** * ddr_rdqs_bdl_adj * @void * * Adjust rdqs/rdq/rdm bdl to avoid problem cause by ddr anti-aging. */ static void ddr_rdqs_bdl_adj(void) { int i; unsigned int rdqs; unsigned int rdq03; unsigned int rdq47; unsigned int rdm; unsigned int tmp; for (i = 0; i < BYTE_NUM; i++) { rdqs = readl(DDR_REG_BASE_PHY0 + 0x22c + i * 0x80); rdq03 = readl(DDR_REG_BASE_PHY0 + 0x21c + i * 0x80); rdq47 = readl(DDR_REG_BASE_PHY0 + 0x220 + i * 0x80); rdm = readl(DDR_REG_BASE_PHY0 + 0x224 + i * 0x80); /* rdqs bdl lower two bit shoud be 0x11 */ while ((rdqs & 0x3) < 0x3) { /* rdqs/rdq/rdm bdl + 1 */ rdqs = rdqs + 0x1; rdq03 = rdq03 + 0x01010101; rdq47 = rdq47 + 0x01010101; rdm = rdm + 0x1; writel(rdqs, DDR_REG_BASE_PHY0 + 0x22c + i * 0x80); writel(rdq03, DDR_REG_BASE_PHY0 + 0x21c + i * 0x80); writel(rdq47, DDR_REG_BASE_PHY0 + 0x220 + i * 0x80); writel(rdm, DDR_REG_BASE_PHY0 + 0x224 + i * 0x80); } } tmp = readl(DDR_REG_BASE_PHY0 + DDR_PHY_MISC); tmp |= (1 << PHY_MISC_UPDATE_BIT); /* update new config to PHY */ writel(tmp, DDR_REG_BASE_PHY0 + DDR_PHY_MISC); tmp &= ~(1 << PHY_MISC_UPDATE_BIT); writel(tmp, DDR_REG_BASE_PHY0 + DDR_PHY_MISC); tmp = readl(DDR_REG_BASE_PHY0 + DDR_PHY_PHYINITCTRL); /* set 1 to issue PHY counter reset signal */ tmp |= (1 << PHY_PHYCONN_RST_BIT); writel(tmp, DDR_REG_BASE_PHY0 + DDR_PHY_PHYINITCTRL); /* set 0 to end the reset signal */ tmp &= ~(1 << PHY_PHYCONN_RST_BIT); writel(tmp, DDR_REG_BASE_PHY0 + DDR_PHY_PHYINITCTRL); } void start_ddr_training(unsigned int base) { struct tr_relate_reg relate_reg; struct tr_relate_reg *reg = &relate_reg; start_svb(); ddr_boot_prepare(reg); /* ddr pcode training */ ddr_pcode_training_if(); /* ddr hw training */ ddr_hw_training_if(); /* ddr sw training */ ddr_sw_training_if(); ddr_rdqs_bdl_adj(); ddr_boot_restore(reg); /* 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); } #ifdef DDR_REG_BASE_PHY1 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); } #endif /* enable ddr scramb */ }