/* * Lowlevel setup for EXYNOS5 based board * * Copyright (C) 2013 Samsung Electronics * Rajeshwari Shinde * * See file CREDITS for list of people who contributed to this * project. * * 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include "common_setup.h" #include "exynos5_setup.h" /* These are the things we can do during low-level init */ enum { DO_WAKEUP = 1 << 0, DO_CLOCKS = 1 << 1, DO_MEM_RESET = 1 << 2, DO_UART = 1 << 3, DO_POWER = 1 << 4, }; #ifdef CONFIG_EXYNOS5420 /* Address for relocating helper code (Last 4 KB of IRAM) */ #define EXYNOS_RELOCATE_CODE_BASE (CFG_IRAM_TOP - 0x1000) /* * Power up secondary CPUs. */ static void secondary_cpu_start(void) { v7_enable_smp(EXYNOS5420_INFORM_BASE); svc32_mode_en(); branch_bx(EXYNOS_RELOCATE_CODE_BASE); } /* * This is the entry point of hotplug-in and * cluster switching. */ static void low_power_start(void) { uint32_t val, reg_val; reg_val = readl(EXYNOS5420_SPARE_BASE); if (reg_val != CPU_RST_FLAG_VAL) { writel(0x0, CFG_LOWPOWER_FLAG); branch_bx(0x0); } reg_val = readl(CFG_PHY_IRAM_BASE + 0x4); if (reg_val != (uint32_t)&low_power_start) { /* Store jump address as low_power_start if not present */ writel((uint32_t)&low_power_start, CFG_PHY_IRAM_BASE + 0x4); dsb(); sev(); } /* Set the CPU to SVC32 mode */ svc32_mode_en(); #ifndef CONFIG_SYS_L2CACHE_OFF /* Read MIDR for Primary Part Number */ mrc_midr(val); val = (val >> 4); val &= 0xf; if (val == 0xf) { configure_l2_ctlr(); configure_l2_actlr(); v7_enable_l2_hazard_detect(); } #endif /* Invalidate L1 & TLB */ val = 0x0; mcr_tlb(val); mcr_icache(val); /* Disable MMU stuff and caches */ mrc_sctlr(val); val &= ~((0x2 << 12) | 0x7); val |= ((0x1 << 12) | (0x8 << 8) | 0x2); mcr_sctlr(val); /* CPU state is hotplug or reset */ secondary_cpu_start(); /* Core should not enter into WFI here */ wfi(); } /* * Pointer to this function is stored in iRam which is used * for jump and power down of a specific core. */ static void power_down_core(void) { uint32_t tmp, core_id, core_config; /* Get the unique core id */ /* * Multiprocessor Affinity Register * [11:8] Cluster ID * [1:0] CPU ID */ mrc_mpafr(core_id); tmp = core_id & 0x3; core_id = (core_id >> 6) & ~3; core_id |= tmp; core_id &= 0x3f; /* Set the status of the core to low */ core_config = (core_id * CPU_CONFIG_STATUS_OFFSET); core_config += EXYNOS5420_CPU_CONFIG_BASE; writel(0x0, core_config); /* Core enter WFI */ wfi(); } /* * Configurations for secondary cores are inapt at this stage. * Reconfigure secondary cores. Shutdown and change the status * of all cores except the primary core. */ static void secondary_cores_configure(void) { /* Clear secondary boot iRAM base */ writel(0x0, (EXYNOS_RELOCATE_CODE_BASE + 0x1C)); /* set lowpower flag and address */ writel(CPU_RST_FLAG_VAL, CFG_LOWPOWER_FLAG); writel((uint32_t)&low_power_start, CFG_LOWPOWER_ADDR); writel(CPU_RST_FLAG_VAL, EXYNOS5420_SPARE_BASE); /* Store jump address for power down */ writel((uint32_t)&power_down_core, CFG_PHY_IRAM_BASE + 0x4); /* Need all core power down check */ dsb(); sev(); } extern void relocate_wait_code(void); #endif int do_lowlevel_init(void) { uint32_t reset_status; int actions = 0; arch_cpu_init(); #if !defined(CONFIG_SYS_L2CACHE_OFF) && defined(CONFIG_EXYNOS5420) /* * Init L2 cache parameters here for use by boot and resume * * These are here instead of in v7_outer_cache_enable() so that the * L2 cache settings get properly set even at resume time or if we're * running U-Boot with the cache off. The kernel still needs us to * set these for it. */ configure_l2_ctlr(); configure_l2_actlr(); dsb(); isb(); relocate_wait_code(); /* Reconfigure secondary cores */ secondary_cores_configure(); #endif reset_status = get_reset_status(); switch (reset_status) { case S5P_CHECK_SLEEP: actions = DO_CLOCKS | DO_WAKEUP; break; case S5P_CHECK_DIDLE: case S5P_CHECK_LPA: actions = DO_WAKEUP; break; default: /* This is a normal boot (not a wake from sleep) */ actions = DO_CLOCKS | DO_MEM_RESET | DO_POWER; } if (actions & DO_POWER) set_ps_hold_ctrl(); if (actions & DO_CLOCKS) { system_clock_init(); #ifdef CONFIG_DEBUG_UART #if (defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_SERIAL)) || \ !defined(CONFIG_SPL_BUILD) exynos_pinmux_config(PERIPH_ID_UART3, PINMUX_FLAG_NONE); debug_uart_init(); #endif #endif mem_ctrl_init(actions & DO_MEM_RESET); tzpc_init(); } return actions & DO_WAKEUP; }