X-Git-Url: https://git.kernelconcepts.de/?p=karo-tx-uboot.git;a=blobdiff_plain;f=arch%2Farm%2Fcpu%2Farmv7%2Fmx6%2Fsoc.c;h=c45c2f9d03f09896f8a367c95c4611507eab65c8;hp=5f5f49720107f56717e676eafbfd94983ffee9b4;hb=2e62c0bab7fe68ba2ab3baf6777537267a26ac02;hpb=85bafb6da4dddfffa78479aa49a72ae48578a4ce diff --git a/arch/arm/cpu/armv7/mx6/soc.c b/arch/arm/cpu/armv7/mx6/soc.c index 5f5f497201..c45c2f9d03 100644 --- a/arch/arm/cpu/armv7/mx6/soc.c +++ b/arch/arm/cpu/armv7/mx6/soc.c @@ -8,6 +8,8 @@ */ #include +#include +#include #include #include #include @@ -15,16 +17,38 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include +DECLARE_GLOBAL_DATA_PTR; + +#define __data __attribute__((section(".data"))) + +#ifdef CONFIG_MX6_TEMPERATURE_MIN +#define TEMPERATURE_MIN CONFIG_MX6_TEMPERATURE_MIN +#else +#define TEMPERATURE_MIN (-40) +#endif +#ifdef CONFIG_MX6_TEMPERATURE_HOT +#define TEMPERATURE_HOT CONFIG_MX6_TEMPERATURE_HOT +#else +#define TEMPERATURE_HOT 80 +#endif +#ifdef CONFIG_MX6_TEMPERATURE_MAX +#define TEMPERATURE_MAX CONFIG_MX6_TEMPERATURE_MAX +#else +#define TEMPERATURE_MAX 125 +#endif +#define TEMP_AVG_COUNT 5 +#define TEMP_WARN_THRESHOLD 5 + enum ldo_reg { LDO_ARM, LDO_SOC, @@ -63,11 +87,12 @@ u32 get_cpu_rev(void) struct anatop_regs *anatop = (struct anatop_regs *)ANATOP_BASE_ADDR; u32 reg = readl(&anatop->digprog_sololite); u32 type = ((reg >> 16) & 0xff); + u32 major, cfg = 0; if (type != MXC_CPU_MX6SL) { reg = readl(&anatop->digprog); struct scu_regs *scu = (struct scu_regs *)SCU_BASE_ADDR; - u32 cfg = readl(&scu->config) & 3; + cfg = readl(&scu->config) & 3; type = ((reg >> 16) & 0xff); if (type == MXC_CPU_MX6DL) { if (!cfg) @@ -80,8 +105,95 @@ u32 get_cpu_rev(void) } } + major = ((reg >> 8) & 0xff); + if ((major >= 1) && + ((type == MXC_CPU_MX6Q) || (type == MXC_CPU_MX6D))) { + major--; + type = MXC_CPU_MX6QP; + if (cfg == 1) + type = MXC_CPU_MX6DP; + } reg &= 0xff; /* mx6 silicon revision */ - return (type << 12) | (reg + 0x10); + return (type << 12) | (reg + (0x10 * (major + 1))); +} + +/* + * OCOTP_CFG3[17:16] (see Fusemap Description Table offset 0x440) + * defines a 2-bit SPEED_GRADING + */ +#define OCOTP_CFG3_SPEED_SHIFT 16 +#define OCOTP_CFG3_SPEED_800MHZ 0 +#define OCOTP_CFG3_SPEED_850MHZ 1 +#define OCOTP_CFG3_SPEED_1GHZ 2 +#define OCOTP_CFG3_SPEED_1P2GHZ 3 + +u32 get_cpu_speed_grade_hz(void) +{ + struct ocotp_regs *ocotp = (struct ocotp_regs *)OCOTP_BASE_ADDR; + struct fuse_bank *bank = &ocotp->bank[0]; + struct fuse_bank0_regs *fuse = + (struct fuse_bank0_regs *)bank->fuse_regs; + uint32_t val; + + val = readl(&fuse->cfg3); + val >>= OCOTP_CFG3_SPEED_SHIFT; + val &= 0x3; + + switch (val) { + /* Valid for IMX6DQ */ + case OCOTP_CFG3_SPEED_1P2GHZ: + if (is_cpu_type(MXC_CPU_MX6Q) || is_cpu_type(MXC_CPU_MX6D)) + return 1200000000; + /* Valid for IMX6SX/IMX6SDL/IMX6DQ */ + case OCOTP_CFG3_SPEED_1GHZ: + return 996000000; + /* Valid for IMX6DQ */ + case OCOTP_CFG3_SPEED_850MHZ: + if (is_cpu_type(MXC_CPU_MX6Q) || is_cpu_type(MXC_CPU_MX6D)) + return 852000000; + /* Valid for IMX6SX/IMX6SDL/IMX6DQ */ + case OCOTP_CFG3_SPEED_800MHZ: + return 792000000; + } + return 0; +} + +/* + * OCOTP_MEM0[7:6] (see Fusemap Description Table offset 0x480) + * defines a 2-bit Temperature Grade + * + * return temperature grade and min/max temperature in celcius + */ +#define OCOTP_MEM0_TEMP_SHIFT 6 + +u32 get_cpu_temp_grade(int *minc, int *maxc) +{ + struct ocotp_regs *ocotp = (struct ocotp_regs *)OCOTP_BASE_ADDR; + struct fuse_bank *bank = &ocotp->bank[1]; + struct fuse_bank1_regs *fuse = + (struct fuse_bank1_regs *)bank->fuse_regs; + uint32_t val; + + val = readl(&fuse->mem0); + val >>= OCOTP_MEM0_TEMP_SHIFT; + val &= 0x3; + + if (minc && maxc) { + if (val == TEMP_AUTOMOTIVE) { + *minc = -40; + *maxc = 125; + } else if (val == TEMP_INDUSTRIAL) { + *minc = -40; + *maxc = 105; + } else if (val == TEMP_EXTCOMMERCIAL) { + *minc = -20; + *maxc = 105; + } else { + *minc = 0; + *maxc = 95; + } + } + return val; } #ifdef CONFIG_REVISION_TAG @@ -102,14 +214,14 @@ u32 __weak get_board_rev(void) void init_aips(void) { struct aipstz_regs *aips1, *aips2; -#ifdef CONFIG_MX6SX +#ifdef CONFIG_SOC_MX6SX struct aipstz_regs *aips3; #endif - aips1 = (struct aipstz_regs *)AIPS1_BASE_ADDR; - aips2 = (struct aipstz_regs *)AIPS2_BASE_ADDR; -#ifdef CONFIG_MX6SX - aips3 = (struct aipstz_regs *)AIPS3_BASE_ADDR; + aips1 = (struct aipstz_regs *)AIPS1_ARB_BASE_ADDR; + aips2 = (struct aipstz_regs *)AIPS2_ARB_BASE_ADDR; +#ifdef CONFIG_SOC_MX6SX + aips3 = (struct aipstz_regs *)AIPS3_ARB_BASE_ADDR; #endif /* @@ -137,7 +249,7 @@ void init_aips(void) writel(0x00000000, &aips2->opacr3); writel(0x00000000, &aips2->opacr4); -#ifdef CONFIG_MX6SX +#ifdef CONFIG_SOC_MX6SX /* * Set all MPROTx to be non-bufferable, trusted for R/W, * not forced to user-mode. @@ -225,10 +337,185 @@ static int set_ldo_voltage(enum ldo_reg ldo, u32 mv) return 0; } +static u32 __data thermal_calib; + +#define FACTOR0 10000000 +#define FACTOR1 15976 +#define FACTOR2 4297157 + +int raw_to_celsius(unsigned int raw, unsigned int raw_25c, unsigned int raw_hot, + unsigned int hot_temp) +{ + int temperature; + + if (raw_hot != 0 && hot_temp != 0) { + unsigned int raw_n40c, ratio; + + ratio = ((raw_25c - raw_hot) * 100) / (hot_temp - 25); + raw_n40c = raw_25c + (13 * ratio) / 20; + if (raw <= raw_n40c) + temperature = (raw_n40c - raw) * 100 / ratio - 40; + else + temperature = TEMPERATURE_MIN; + } else { + u64 temp64 = FACTOR0; + unsigned int c1, c2; + /* + * Derived from linear interpolation: + * slope = 0.4297157 - (0.0015976 * 25C fuse) + * slope = (FACTOR2 - FACTOR1 * n1) / FACTOR0 + * (Nmeas - n1) / (Tmeas - t1) = slope + * We want to reduce this down to the minimum computation necessary + * for each temperature read. Also, we want Tmeas in millicelsius + * and we don't want to lose precision from integer division. So... + * Tmeas = (Nmeas - n1) / slope + t1 + * milli_Tmeas = 1000 * (Nmeas - n1) / slope + 1000 * t1 + * milli_Tmeas = -1000 * (n1 - Nmeas) / slope + 1000 * t1 + * Let constant c1 = (-1000 / slope) + * milli_Tmeas = (n1 - Nmeas) * c1 + 1000 * t1 + * Let constant c2 = n1 *c1 + 1000 * t1 + * milli_Tmeas = c2 - Nmeas * c1 + */ + temp64 *= 1000; + do_div(temp64, FACTOR1 * raw_25c - FACTOR2); + c1 = temp64; + c2 = raw_25c * c1 + 1000 * 25; + temperature = (c2 - raw * c1) / 1000; + } + return temperature; +} + +int read_cpu_temperature(void) +{ + unsigned int reg, tmp, i; + unsigned int raw_25c, raw_hot, hot_temp; + int temperature; + struct anatop_regs *const anatop = (void *)ANATOP_BASE_ADDR; + struct mx6_ocotp_regs *const ocotp_regs = (void *)OCOTP_BASE_ADDR; + + if (!thermal_calib) { + enable_ocotp_clk(1); + writel(1, &ocotp_regs->hw_ocotp_read_ctrl); + thermal_calib = readl(&ocotp_regs->hw_ocotp_ana1); + writel(0, &ocotp_regs->hw_ocotp_read_ctrl); + enable_ocotp_clk(0); + } + + if (thermal_calib == 0 || thermal_calib == 0xffffffff) + return TEMPERATURE_MIN; + + /* Fuse data layout: + * [31:20] sensor value @ 25C + * [19:8] sensor value of hot + * [7:0] hot temperature value */ + raw_25c = thermal_calib >> 20; + raw_hot = (thermal_calib & 0xfff00) >> 8; + hot_temp = thermal_calib & 0xff; + + /* now we only using single measure, every time we measure + * the temperature, we will power on/off the anadig module + */ + writel(BM_ANADIG_TEMPSENSE0_POWER_DOWN, &anatop->tempsense0_clr); + writel(BM_ANADIG_ANA_MISC0_REFTOP_SELBIASOFF, &anatop->ana_misc0_set); + + /* write measure freq */ + writel(327, &anatop->tempsense1); + writel(BM_ANADIG_TEMPSENSE0_MEASURE_TEMP, &anatop->tempsense0_clr); + writel(BM_ANADIG_TEMPSENSE0_FINISHED, &anatop->tempsense0_clr); + writel(BM_ANADIG_TEMPSENSE0_MEASURE_TEMP, &anatop->tempsense0_set); + + /* average the temperature value over multiple readings */ + for (i = 0; i < TEMP_AVG_COUNT; i++) { + static int failed; + int limit = 100; + + while ((readl(&anatop->tempsense0) & + BM_ANADIG_TEMPSENSE0_FINISHED) == 0) { + udelay(10000); + if (--limit < 0) + break; + } + if ((readl(&anatop->tempsense0) & + BM_ANADIG_TEMPSENSE0_FINISHED) == 0) { + if (!failed) { + printf("Failed to read temp sensor\n"); + failed = 1; + } + return 0; + } + failed = 0; + reg = (readl(&anatop->tempsense0) & + BM_ANADIG_TEMPSENSE0_TEMP_VALUE) >> + BP_ANADIG_TEMPSENSE0_TEMP_VALUE; + if (i == 0) + tmp = reg; + else + tmp = (tmp * i + reg) / (i + 1); + writel(BM_ANADIG_TEMPSENSE0_FINISHED, + &anatop->tempsense0_clr); + } + + temperature = raw_to_celsius(tmp, raw_25c, raw_hot, hot_temp); + + /* power down anatop thermal sensor */ + writel(BM_ANADIG_TEMPSENSE0_POWER_DOWN, &anatop->tempsense0_set); + writel(BM_ANADIG_ANA_MISC0_REFTOP_SELBIASOFF, &anatop->ana_misc0_clr); + + return temperature; +} + +int check_cpu_temperature(int boot) +{ + static int __data max_temp; + int boot_limit = getenv_ulong("max_boot_temp", 10, TEMPERATURE_HOT); + int tmp = read_cpu_temperature(); + bool first = true; + + if (tmp < TEMPERATURE_MIN || tmp > TEMPERATURE_MAX) { + printf("Temperature: can't get valid data!\n"); + return tmp; + } + + if (!boot) { + if (tmp > boot_limit) { + printf("CPU is %d C, too hot, resetting...\n", tmp); + udelay(100000); + reset_cpu(0); + } + if (tmp > max_temp) { + if (tmp > boot_limit - TEMP_WARN_THRESHOLD) + printf("WARNING: CPU temperature %d C\n", tmp); + max_temp = tmp; + } + } else { + printf("Temperature: %d C, calibration data 0x%x\n", + tmp, thermal_calib); + while (tmp >= boot_limit) { + if (first) { + printf("CPU is %d C, too hot to boot, waiting...\n", + tmp); + first = false; + } + if (ctrlc()) + break; + udelay(50000); + tmp = read_cpu_temperature(); + if (tmp > boot_limit - TEMP_WARN_THRESHOLD && tmp != max_temp) + printf("WARNING: CPU temperature %d C\n", tmp); + max_temp = tmp; + } + } + return tmp; +} + static void imx_set_wdog_powerdown(bool enable) { struct wdog_regs *wdog1 = (struct wdog_regs *)WDOG1_BASE_ADDR; struct wdog_regs *wdog2 = (struct wdog_regs *)WDOG2_BASE_ADDR; + struct wdog_regs *wdog3 = (struct wdog_regs *)WDOG3_BASE_ADDR; + + if (is_cpu_type(MXC_CPU_MX6SX) || is_cpu_type(MXC_CPU_MX6UL)) + writew(enable, &wdog3->wmcr); /* Write to the PDE (Power Down Enable) bit */ writew(enable, &wdog1->wmcr); @@ -250,12 +537,31 @@ static void set_ahb_rate(u32 val) static void clear_mmdc_ch_mask(void) { struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; + u32 reg; + reg = readl(&mxc_ccm->ccdr); /* Clear MMDC channel mask */ - writel(0, &mxc_ccm->ccdr); + reg &= ~(MXC_CCM_CCDR_MMDC_CH1_HS_MASK | MXC_CCM_CCDR_MMDC_CH0_HS_MASK); + writel(reg, &mxc_ccm->ccdr); } -#ifdef CONFIG_MX6SL +static void init_bandgap(void) +{ + struct anatop_regs *anatop = (struct anatop_regs *)ANATOP_BASE_ADDR; + /* + * Ensure the bandgap has stabilized. + */ + while (!(readl(&anatop->ana_misc0) & 0x80)) + ; + /* + * For best noise performance of the analog blocks using the + * outputs of the bandgap, the reftop_selfbiasoff bit should + * be set. + */ + writel(BM_ANADIG_ANA_MISC0_REFTOP_SELBIASOFF, &anatop->ana_misc0_set); +} + +#ifdef CONFIG_SOC_MX6SL static void set_preclk_from_osc(void) { struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; @@ -267,6 +573,22 @@ static void set_preclk_from_osc(void) } #endif +#define SRC_SCR_WARM_RESET_ENABLE 0 + +static void init_src(void) +{ + struct src *src_regs = (struct src *)SRC_BASE_ADDR; + u32 val; + + /* + * force warm reset sources to generate cold reset + * for a more reliable restart + */ + val = readl(&src_regs->scr); + val &= ~(1 << SRC_SCR_WARM_RESET_ENABLE); + writel(val, &src_regs->scr); +} + int arch_cpu_init(void) { init_aips(); @@ -274,6 +596,13 @@ int arch_cpu_init(void) /* Need to clear MMDC_CHx_MASK to make warm reset work. */ clear_mmdc_ch_mask(); + /* + * Disable self-bias circuit in the analog bandap. + * The self-bias circuit is used by the bandgap during startup. + * This bit should be set after the bandgap has initialized. + */ + init_bandgap(); + /* * When low freq boot is enabled, ROM will not set AHB * freq, so we need to ensure AHB freq is 132MHz in such @@ -283,17 +612,23 @@ int arch_cpu_init(void) set_ahb_rate(132000000); /* Set perclk to source from OSC 24MHz */ -#if defined(CONFIG_MX6SL) +#if defined(CONFIG_SOC_MX6SL) set_preclk_from_osc(); #endif imx_set_wdog_powerdown(false); /* Disable PDE bit of WMCR register */ -#ifdef CONFIG_APBH_DMA - /* Start APBH DMA */ +#ifdef CONFIG_VIDEO_IPUV3 + gd->arch.ipu_hw_rev = IPUV3_HW_REV_IPUV3H; +#endif +#ifdef CONFIG_APBH_DMA + /* Timer is required for Initializing APBH DMA */ + timer_init(); mxs_dma_init(); #endif + init_src(); + return 0; } @@ -339,14 +674,13 @@ void imx_get_mac_from_fuse(int dev_id, unsigned char *mac) u32 value = readl(&fuse->mac_addr_high); mac[0] = (value >> 8); - mac[1] = value ; + mac[1] = value; value = readl(&fuse->mac_addr_low); - mac[2] = value >> 24 ; - mac[3] = value >> 16 ; - mac[4] = value >> 8 ; - mac[5] = value ; - + mac[2] = value >> 24; + mac[3] = value >> 16; + mac[4] = value >> 8; + mac[5] = value; } #endif @@ -393,7 +727,7 @@ void s_init(void) u32 mask528; u32 reg, periph1, periph2; - if (is_cpu_type(MXC_CPU_MX6SX)) + if (is_cpu_type(MXC_CPU_MX6SX) || is_cpu_type(MXC_CPU_MX6UL)) return; /* Due to hardware limitation, on MX6Q we need to gate/ungate all PFDs @@ -477,7 +811,15 @@ void v7_outer_cache_enable(void) struct pl310_regs *const pl310 = (struct pl310_regs *)L2_PL310_BASE; unsigned int val; -#if defined CONFIG_MX6SL + + /* + * Set bit 22 in the auxiliary control register. If this bit + * is cleared, PL310 treats Normal Shared Non-cacheable + * accesses as Cacheable no-allocate. + */ + setbits_le32(&pl310->pl310_aux_ctrl, L310_SHARED_ATT_OVERRIDE_ENABLE); + +#if defined CONFIG_SOC_MX6SL struct iomuxc *iomux = (struct iomuxc *)IOMUXC_BASE_ADDR; val = readl(&iomux->gpr[11]); if (val & IOMUXC_GPR11_L2CACHE_AS_OCRAM) { @@ -508,7 +850,7 @@ void v7_outer_cache_enable(void) * double linefill feature. This is the default behavior. */ -#ifndef CONFIG_MX6Q +#ifndef CONFIG_SOC_MX6Q val |= 0x40800000; #endif writel(val, &pl310->pl310_prefetch_ctrl);