X-Git-Url: https://git.kernelconcepts.de/?p=karo-tx-uboot.git;a=blobdiff_plain;f=arch%2Farm%2Fcpu%2Farmv7%2Fmx6%2Fsoc.c;h=08f2cdbe7fcd6ef31e23556696deaff1d3d5ff4e;hp=5f5f49720107f56717e676eafbfd94983ffee9b4;hb=a83c6285388b5aa22980d96a70fff7d111d971fd;hpb=878cd63e02f63f245182a101807186b44e20f116 diff --git a/arch/arm/cpu/armv7/mx6/soc.c b/arch/arm/cpu/armv7/mx6/soc.c index 5f5f497201..08f2cdbe7f 100644 --- a/arch/arm/cpu/armv7/mx6/soc.c +++ b/arch/arm/cpu/armv7/mx6/soc.c @@ -8,6 +8,10 @@ */ #include +#include +#include +#include +#include #include #include #include @@ -15,16 +19,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 +89,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 +107,93 @@ 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) +{ + uint32_t val; + + if (fuse_read(0, 3, &val)) { + printf("Failed to read speed_grade fuse\n"); + return 0; + } + 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) +{ + uint32_t val; + + if (fuse_read(1, 0, &val)) { + printf("Failed to read temp_grade fuse\n"); + val = 0; + } + 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,75 @@ static int set_ldo_voltage(enum ldo_reg ldo, u32 mv) return 0; } +int check_cpu_temperature(int boot) +{ + int ret; + static int __data max_temp; + int boot_limit = getenv_ulong("max_boot_temp", 10, TEMPERATURE_HOT); + int tmp; + struct udevice *dev; + bool first = true; + + if (uclass_get_device_by_name(UCLASS_THERMAL, "imx_thermal", &dev)) { + if (first) { + printf("No thermal device found; cannot read CPU temperature\n"); + first = false; + } + return 0; + } + + ret = thermal_get_temp(dev, &tmp); + if (ret) { + printf("Failed to read temperature: %d\n", ret); + return TEMPERATURE_MAX; + } + 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 { + 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); + ret = thermal_get_temp(dev, &tmp); + if (ret < 0) { + printf("Failed to read temperature: %d\n", ret); + return TEMPERATURE_MAX; + } + 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 +427,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); +} + +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_MX6SL +#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 +463,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 +486,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 +502,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; } @@ -332,21 +557,31 @@ void enable_caches(void) #if defined(CONFIG_FEC_MXC) void imx_get_mac_from_fuse(int dev_id, unsigned char *mac) { - struct ocotp_regs *ocotp = (struct ocotp_regs *)OCOTP_BASE_ADDR; - struct fuse_bank *bank = &ocotp->bank[4]; - struct fuse_bank4_regs *fuse = - (struct fuse_bank4_regs *)bank->fuse_regs; - - u32 value = readl(&fuse->mac_addr_high); - mac[0] = (value >> 8); - mac[1] = value ; + unsigned int mac0, mac1; - value = readl(&fuse->mac_addr_low); - mac[2] = value >> 24 ; - mac[3] = value >> 16 ; - mac[4] = value >> 8 ; - mac[5] = value ; + memset(mac, 0, 6); + if (dev_id < 0 || dev_id > 2) + return; + if (fuse_read(4, 2, &mac0)) { + printf("Failed to read MAC0 fuse\n"); + return; + } + if (fuse_read(4, 3, &mac1)) { + printf("Failed to read MAC1 fuse\n"); + return; + } + mac[0] = mac1 >> 8; + mac[1] = mac1; + mac[2] = mac0 >> 24; + mac[3] = mac0 >> 16; + if (dev_id == 0) { + mac[4] = mac0 >> 8; + mac[5] = mac0; + } else { + mac[4] = mac1 >> 24; + mac[5] = mac1 >> 16; + } } #endif @@ -393,7 +628,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 +712,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 +751,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);