diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-28 23:24:40 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-28 23:24:40 +0400 |
commit | 4bb2d1009f671815870e8f78e826e4f9071392a7 (patch) | |
tree | 4b9f4e3b349a67d47b11eff7fcebbf57a3c40a1e /arch/arm/mach-tegra | |
parent | ff877c498eb2f9c4ea386270642e383bc867f63c (diff) | |
parent | 83fe628e16d84efc8df2731bc403eae4e4f53801 (diff) | |
download | linux-4bb2d1009f671815870e8f78e826e4f9071392a7.tar.xz |
Merge tag 'soc2' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull "ARM: More SoC support updates" from Olof Johansson:
"This branch contains a handful of updates of SoC base code that had
dependencies on other external trees that have now been merged:
* Support for the new EXYNOS5250 SoC from Samsung
* SMP and power domain support for Tegra3 from NVIDIA
* ux500 updates for exporting SoC information through sysfs"
Fix up trivial merge conflicts as per Olof.
* tag 'soc2' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (30 commits)
ARM: mach-shmobile: ap4evb: Reserve DMA memory for the frame buffer
ARM: EXYNOS: Fix compilation error with mach-exynos4-dt board
ARM: dts: add initial dts file for EXYNOS5250, SMDK5250
ARM: EXYNOS: add support device tree enabled board file for EXYNOS5
ARM: EXYNOS: add support ARCH_EXYNOS5 for EXYNOS5 SoCs
ARM: EXYNOS: add support get_core_count() for EXYNOS5250
ARM: EXYNOS: support EINT for EXYNOS4 and EXYNOS5
ARM: EXYNOS: add interrupt definitions for EXYNOS5250
ARM: EXYNOS: add support for EXYNOS5250 SoC
ARM: EXYNOS: add support uart for EXYNOS4 and EXYNOS5
ARM: EXYNOS: add initial setup-i2c0 for EXYNOS5
ARM: EXYNOS: add clock part for EXYNOS5250 SoC
ARM: EXYNOS: use exynos_init_uarts() instead of exynos4_init_uarts()
ARM: EXYNOS: to declare static for mach-exynos/common.c
ARM: EXYNOS: Add clkdev lookup entry for lcd clock
ARM: dt: Explicitly configure all serial ports on Tegra Cardhu
ARM: tegra: support for secondary cores on Tegra30
ARM: tegra: support for Tegra30 CPU powerdomains
ARM: tegra: add support for Tegra30 powerdomains
ARM: tegra: export tegra_powergate_is_powered()
...
Diffstat (limited to 'arch/arm/mach-tegra')
-rw-r--r-- | arch/arm/mach-tegra/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-tegra/board-dt-tegra30.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-tegra/common.c | 4 | ||||
-rw-r--r-- | arch/arm/mach-tegra/flowctrl.c | 62 | ||||
-rw-r--r-- | arch/arm/mach-tegra/flowctrl.h | 5 | ||||
-rw-r--r-- | arch/arm/mach-tegra/fuse.c | 18 | ||||
-rw-r--r-- | arch/arm/mach-tegra/fuse.h | 4 | ||||
-rw-r--r-- | arch/arm/mach-tegra/headsmp.S | 167 | ||||
-rw-r--r-- | arch/arm/mach-tegra/include/mach/iomap.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-tegra/include/mach/powergate.h | 15 | ||||
-rw-r--r-- | arch/arm/mach-tegra/platsmp.c | 137 | ||||
-rw-r--r-- | arch/arm/mach-tegra/powergate.c | 53 | ||||
-rw-r--r-- | arch/arm/mach-tegra/reset.c | 84 | ||||
-rw-r--r-- | arch/arm/mach-tegra/reset.h | 50 |
14 files changed, 542 insertions, 64 deletions
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index 1dd2726986cf..d87d968115ec 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -8,6 +8,7 @@ obj-y += timer.o obj-y += pinmux.o obj-y += fuse.o obj-y += pmc.o +obj-y += flowctrl.o obj-$(CONFIG_CPU_IDLE) += cpuidle.o obj-$(CONFIG_CPU_IDLE) += sleep.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += powergate.o @@ -18,6 +19,7 @@ obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += pinmux-tegra30-tables.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += board-dt-tegra30.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_clocks.o obj-$(CONFIG_SMP) += platsmp.o headsmp.o +obj-$(CONFIG_SMP) += reset.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o obj-$(CONFIG_TEGRA_SYSTEM_DMA) += dma.o apbio.o obj-$(CONFIG_CPU_FREQ) += cpu-tegra.o diff --git a/arch/arm/mach-tegra/board-dt-tegra30.c b/arch/arm/mach-tegra/board-dt-tegra30.c index 96f6c0d030bd..5f7c03e972f3 100644 --- a/arch/arm/mach-tegra/board-dt-tegra30.c +++ b/arch/arm/mach-tegra/board-dt-tegra30.c @@ -56,7 +56,7 @@ struct of_dev_auxdata tegra30_auxdata_lookup[] __initdata = { static __initdata struct tegra_clk_init_table tegra_dt_clk_init_table[] = { /* name parent rate enabled */ - { "uartd", "pll_p", 408000000, true }, + { "uarta", "pll_p", 408000000, true }, { NULL, NULL, 0, 0}, }; diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c index 2f86fcca64a6..22df10fb9972 100644 --- a/arch/arm/mach-tegra/common.c +++ b/arch/arm/mach-tegra/common.c @@ -27,6 +27,7 @@ #include <asm/hardware/gic.h> #include <mach/iomap.h> +#include <mach/powergate.h> #include "board.h" #include "clock.h" @@ -118,13 +119,16 @@ void __init tegra20_init_early(void) tegra_clk_init_from_table(tegra20_clk_init_table); tegra_init_cache(0x331, 0x441); tegra_pmc_init(); + tegra_powergate_init(); } #endif #ifdef CONFIG_ARCH_TEGRA_3x_SOC void __init tegra30_init_early(void) { + tegra_init_fuse(); tegra30_init_clocks(); tegra_init_cache(0x441, 0x551); tegra_pmc_init(); + tegra_powergate_init(); } #endif diff --git a/arch/arm/mach-tegra/flowctrl.c b/arch/arm/mach-tegra/flowctrl.c new file mode 100644 index 000000000000..fef66a7486ed --- /dev/null +++ b/arch/arm/mach-tegra/flowctrl.c @@ -0,0 +1,62 @@ +/* + * arch/arm/mach-tegra/flowctrl.c + * + * functions and macros to control the flowcontroller + * + * Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/io.h> + +#include <mach/iomap.h> + +#include "flowctrl.h" + +u8 flowctrl_offset_halt_cpu[] = { + FLOW_CTRL_HALT_CPU0_EVENTS, + FLOW_CTRL_HALT_CPU1_EVENTS, + FLOW_CTRL_HALT_CPU1_EVENTS + 8, + FLOW_CTRL_HALT_CPU1_EVENTS + 16, +}; + +u8 flowctrl_offset_cpu_csr[] = { + FLOW_CTRL_CPU0_CSR, + FLOW_CTRL_CPU1_CSR, + FLOW_CTRL_CPU1_CSR + 8, + FLOW_CTRL_CPU1_CSR + 16, +}; + +static void flowctrl_update(u8 offset, u32 value) +{ + void __iomem *addr = IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + offset; + + writel(value, addr); + + /* ensure the update has reached the flow controller */ + wmb(); + readl_relaxed(addr); +} + +void flowctrl_write_cpu_csr(unsigned int cpuid, u32 value) +{ + return flowctrl_update(flowctrl_offset_halt_cpu[cpuid], value); +} + +void flowctrl_write_cpu_halt(unsigned int cpuid, u32 value) +{ + return flowctrl_update(flowctrl_offset_cpu_csr[cpuid], value); +} diff --git a/arch/arm/mach-tegra/flowctrl.h b/arch/arm/mach-tegra/flowctrl.h index 74c6efbe52fa..19428173855e 100644 --- a/arch/arm/mach-tegra/flowctrl.h +++ b/arch/arm/mach-tegra/flowctrl.h @@ -34,4 +34,9 @@ #define FLOW_CTRL_HALT_CPU1_EVENTS 0x14 #define FLOW_CTRL_CPU1_CSR 0x18 +#ifndef __ASSEMBLY__ +void flowctrl_write_cpu_csr(unsigned int cpuid, u32 value); +void flowctrl_write_cpu_halt(unsigned int cpuid, u32 value); +#endif + #endif diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c index c1afb2738769..f946d129423c 100644 --- a/arch/arm/mach-tegra/fuse.c +++ b/arch/arm/mach-tegra/fuse.c @@ -34,6 +34,7 @@ int tegra_sku_id; int tegra_cpu_process_id; int tegra_core_process_id; +int tegra_chip_id; enum tegra_revision tegra_revision; /* The BCT to use at boot is specified by board straps that can be read @@ -66,12 +67,9 @@ static inline bool get_spare_fuse(int bit) return tegra_fuse_readl(FUSE_SPARE_BIT + bit * 4); } -static enum tegra_revision tegra_get_revision(void) +static enum tegra_revision tegra_get_revision(u32 id) { - void __iomem *chip_id = IO_ADDRESS(TEGRA_APB_MISC_BASE) + 0x804; - u32 id = readl(chip_id); u32 minor_rev = (id >> 16) & 0xf; - u32 chipid = (id >> 8) & 0xff; switch (minor_rev) { case 1: @@ -79,7 +77,8 @@ static enum tegra_revision tegra_get_revision(void) case 2: return TEGRA_REVISION_A02; case 3: - if (chipid == 0x20 && (get_spare_fuse(18) || get_spare_fuse(19))) + if (tegra_chip_id == TEGRA20 && + (get_spare_fuse(18) || get_spare_fuse(19))) return TEGRA_REVISION_A03p; else return TEGRA_REVISION_A03; @@ -92,6 +91,8 @@ static enum tegra_revision tegra_get_revision(void) void tegra_init_fuse(void) { + u32 id; + u32 reg = readl(IO_TO_VIRT(TEGRA_CLK_RESET_BASE + 0x48)); reg |= 1 << 28; writel(reg, IO_TO_VIRT(TEGRA_CLK_RESET_BASE + 0x48)); @@ -108,10 +109,13 @@ void tegra_init_fuse(void) reg = tegra_apb_readl(TEGRA_APB_MISC_BASE + STRAP_OPT); tegra_bct_strapping = (reg & RAM_ID_MASK) >> RAM_CODE_SHIFT; - tegra_revision = tegra_get_revision(); + id = readl_relaxed(IO_ADDRESS(TEGRA_APB_MISC_BASE) + 0x804); + tegra_chip_id = (id >> 8) & 0xff; + + tegra_revision = tegra_get_revision(id); pr_info("Tegra Revision: %s SKU: %d CPU Process: %d Core Process: %d\n", - tegra_revision_name[tegra_get_revision()], + tegra_revision_name[tegra_revision], tegra_sku_id, tegra_cpu_process_id, tegra_core_process_id); } diff --git a/arch/arm/mach-tegra/fuse.h b/arch/arm/mach-tegra/fuse.h index d65d2abf803b..d2107b2cb85a 100644 --- a/arch/arm/mach-tegra/fuse.h +++ b/arch/arm/mach-tegra/fuse.h @@ -35,9 +35,13 @@ enum tegra_revision { #define SKU_ID_AP25E 27 #define SKU_ID_T25E 28 +#define TEGRA20 0x20 +#define TEGRA30 0x30 + extern int tegra_sku_id; extern int tegra_cpu_process_id; extern int tegra_core_process_id; +extern int tegra_chip_id; extern enum tegra_revision tegra_revision; extern int tegra_bct_strapping; diff --git a/arch/arm/mach-tegra/headsmp.S b/arch/arm/mach-tegra/headsmp.S index b5349b2f13d2..fef9c2c51370 100644 --- a/arch/arm/mach-tegra/headsmp.S +++ b/arch/arm/mach-tegra/headsmp.S @@ -1,6 +1,23 @@ #include <linux/linkage.h> #include <linux/init.h> +#include <asm/cache.h> + +#include <mach/iomap.h> + +#include "flowctrl.h" +#include "reset.h" + +#define APB_MISC_GP_HIDREV 0x804 +#define PMC_SCRATCH41 0x140 + +#define RESET_DATA(x) ((TEGRA_RESET_##x)*4) + + .macro mov32, reg, val + movw \reg, #:lower16:\val + movt \reg, #:upper16:\val + .endm + .section ".text.head", "ax" __CPUINIT @@ -47,15 +64,149 @@ ENTRY(v7_invalidate_l1) mov pc, lr ENDPROC(v7_invalidate_l1) + ENTRY(tegra_secondary_startup) - msr cpsr_fsxc, #0xd3 bl v7_invalidate_l1 - mrc p15, 0, r0, c0, c0, 5 - and r0, r0, #15 - ldr r1, =0x6000f100 - str r0, [r1] -1: ldr r2, [r1] - cmp r0, r2 - beq 1b + /* Enable coresight */ + mov32 r0, 0xC5ACCE55 + mcr p14, 0, r0, c7, c12, 6 b secondary_startup ENDPROC(tegra_secondary_startup) + + .align L1_CACHE_SHIFT +ENTRY(__tegra_cpu_reset_handler_start) + +/* + * __tegra_cpu_reset_handler: + * + * Common handler for all CPU reset events. + * + * Register usage within the reset handler: + * + * R7 = CPU present (to the OS) mask + * R8 = CPU in LP1 state mask + * R9 = CPU in LP2 state mask + * R10 = CPU number + * R11 = CPU mask + * R12 = pointer to reset handler data + * + * NOTE: This code is copied to IRAM. All code and data accesses + * must be position-independent. + */ + + .align L1_CACHE_SHIFT +ENTRY(__tegra_cpu_reset_handler) + + cpsid aif, 0x13 @ SVC mode, interrupts disabled + mrc p15, 0, r10, c0, c0, 5 @ MPIDR + and r10, r10, #0x3 @ R10 = CPU number + mov r11, #1 + mov r11, r11, lsl r10 @ R11 = CPU mask + adr r12, __tegra_cpu_reset_handler_data + +#ifdef CONFIG_SMP + /* Does the OS know about this CPU? */ + ldr r7, [r12, #RESET_DATA(MASK_PRESENT)] + tst r7, r11 @ if !present + bleq __die @ CPU not present (to OS) +#endif + +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + /* Are we on Tegra20? */ + mov32 r6, TEGRA_APB_MISC_BASE + ldr r0, [r6, #APB_MISC_GP_HIDREV] + and r0, r0, #0xff00 + cmp r0, #(0x20 << 8) + bne 1f + /* If not CPU0, don't let CPU0 reset CPU1 now that CPU1 is coming up. */ + mov32 r6, TEGRA_PMC_BASE + mov r0, #0 + cmp r10, #0 + strne r0, [r6, #PMC_SCRATCH41] +1: +#endif + +#ifdef CONFIG_SMP + /* + * Can only be secondary boot (initial or hotplug) but CPU 0 + * cannot be here. + */ + cmp r10, #0 + bleq __die @ CPU0 cannot be here + ldr lr, [r12, #RESET_DATA(STARTUP_SECONDARY)] + cmp lr, #0 + bleq __die @ no secondary startup handler + bx lr +#endif + +/* + * We don't know why the CPU reset. Just kill it. + * The LR register will contain the address we died at + 4. + */ + +__die: + sub lr, lr, #4 + mov32 r7, TEGRA_PMC_BASE + str lr, [r7, #PMC_SCRATCH41] + + mov32 r7, TEGRA_CLK_RESET_BASE + + /* Are we on Tegra20? */ + mov32 r6, TEGRA_APB_MISC_BASE + ldr r0, [r6, #APB_MISC_GP_HIDREV] + and r0, r0, #0xff00 + cmp r0, #(0x20 << 8) + bne 1f + +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + mov32 r0, 0x1111 + mov r1, r0, lsl r10 + str r1, [r7, #0x340] @ CLK_RST_CPU_CMPLX_SET +#endif +1: +#ifdef CONFIG_ARCH_TEGRA_3x_SOC + mov32 r6, TEGRA_FLOW_CTRL_BASE + + cmp r10, #0 + moveq r1, #FLOW_CTRL_HALT_CPU0_EVENTS + moveq r2, #FLOW_CTRL_CPU0_CSR + movne r1, r10, lsl #3 + addne r2, r1, #(FLOW_CTRL_CPU1_CSR-8) + addne r1, r1, #(FLOW_CTRL_HALT_CPU1_EVENTS-8) + + /* Clear CPU "event" and "interrupt" flags and power gate + it when halting but not before it is in the "WFI" state. */ + ldr r0, [r6, +r2] + orr r0, r0, #FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG + orr r0, r0, #FLOW_CTRL_CSR_ENABLE + str r0, [r6, +r2] + + /* Unconditionally halt this CPU */ + mov r0, #FLOW_CTRL_WAITEVENT + str r0, [r6, +r1] + ldr r0, [r6, +r1] @ memory barrier + + dsb + isb + wfi @ CPU should be power gated here + + /* If the CPU didn't power gate above just kill it's clock. */ + + mov r0, r11, lsl #8 + str r0, [r7, #348] @ CLK_CPU_CMPLX_SET +#endif + + /* If the CPU still isn't dead, just spin here. */ + b . +ENDPROC(__tegra_cpu_reset_handler) + + .align L1_CACHE_SHIFT + .type __tegra_cpu_reset_handler_data, %object + .globl __tegra_cpu_reset_handler_data +__tegra_cpu_reset_handler_data: + .rept TEGRA_RESET_DATA_SIZE + .long 0 + .endr + .align L1_CACHE_SHIFT + +ENTRY(__tegra_cpu_reset_handler_end) diff --git a/arch/arm/mach-tegra/include/mach/iomap.h b/arch/arm/mach-tegra/include/mach/iomap.h index 67644c905d8e..cff672a344f4 100644 --- a/arch/arm/mach-tegra/include/mach/iomap.h +++ b/arch/arm/mach-tegra/include/mach/iomap.h @@ -113,6 +113,9 @@ #define TEGRA_AHB_GIZMO_BASE 0x6000C004 #define TEGRA_AHB_GIZMO_SIZE 0x10C +#define TEGRA_SB_BASE 0x6000C200 +#define TEGRA_SB_SIZE 256 + #define TEGRA_STATMON_BASE 0x6000C400 #define TEGRA_STATMON_SIZE SZ_1K diff --git a/arch/arm/mach-tegra/include/mach/powergate.h b/arch/arm/mach-tegra/include/mach/powergate.h index 39c396d2ddb0..4752b1a68f35 100644 --- a/arch/arm/mach-tegra/include/mach/powergate.h +++ b/arch/arm/mach-tegra/include/mach/powergate.h @@ -27,8 +27,21 @@ #define TEGRA_POWERGATE_VDEC 4 #define TEGRA_POWERGATE_L2 5 #define TEGRA_POWERGATE_MPE 6 -#define TEGRA_NUM_POWERGATE 7 +#define TEGRA_POWERGATE_HEG 7 +#define TEGRA_POWERGATE_SATA 8 +#define TEGRA_POWERGATE_CPU1 9 +#define TEGRA_POWERGATE_CPU2 10 +#define TEGRA_POWERGATE_CPU3 11 +#define TEGRA_POWERGATE_CELP 12 +#define TEGRA_POWERGATE_3D1 13 +#define TEGRA_POWERGATE_CPU0 TEGRA_POWERGATE_CPU +#define TEGRA_POWERGATE_3D0 TEGRA_POWERGATE_3D + +int __init tegra_powergate_init(void); + +int tegra_cpu_powergate_id(int cpuid); +int tegra_powergate_is_powered(int id); int tegra_powergate_power_on(int id); int tegra_powergate_power_off(int id); int tegra_powergate_remove_clamping(int id); diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c index 7d2b5d03c1df..1a208dbf682f 100644 --- a/arch/arm/mach-tegra/platsmp.c +++ b/arch/arm/mach-tegra/platsmp.c @@ -24,19 +24,31 @@ #include <asm/mach-types.h> #include <asm/smp_scu.h> +#include <mach/clk.h> #include <mach/iomap.h> +#include <mach/powergate.h> + +#include "fuse.h" +#include "flowctrl.h" +#include "reset.h" extern void tegra_secondary_startup(void); -static DEFINE_SPINLOCK(boot_lock); static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE); #define EVP_CPU_RESET_VECTOR \ (IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100) #define CLK_RST_CONTROLLER_CLK_CPU_CMPLX \ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x4c) +#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET \ + (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x340) #define CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR \ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x344) +#define CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR \ + (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x34c) + +#define CPU_CLOCK(cpu) (0x1<<(8+cpu)) +#define CPU_RESET(cpu) (0x1111ul<<(cpu)) void __cpuinit platform_secondary_init(unsigned int cpu) { @@ -47,63 +59,106 @@ void __cpuinit platform_secondary_init(unsigned int cpu) */ gic_secondary_init(0); - /* - * Synchronise with the boot thread. - */ - spin_lock(&boot_lock); - spin_unlock(&boot_lock); } -int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) +static int tegra20_power_up_cpu(unsigned int cpu) { - unsigned long old_boot_vector; - unsigned long boot_vector; - unsigned long timeout; u32 reg; - /* - * set synchronisation state between this boot processor - * and the secondary one - */ - spin_lock(&boot_lock); + /* Enable the CPU clock. */ + reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX); + writel(reg & ~CPU_CLOCK(cpu), CLK_RST_CONTROLLER_CLK_CPU_CMPLX); + barrier(); + reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX); + /* Clear flow controller CSR. */ + flowctrl_write_cpu_csr(cpu, 0); - /* set the reset vector to point to the secondary_startup routine */ + return 0; +} - boot_vector = virt_to_phys(tegra_secondary_startup); - old_boot_vector = readl(EVP_CPU_RESET_VECTOR); - writel(boot_vector, EVP_CPU_RESET_VECTOR); +static int tegra30_power_up_cpu(unsigned int cpu) +{ + u32 reg; + int ret, pwrgateid; + unsigned long timeout; - /* enable cpu clock on cpu1 */ - reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX); - writel(reg & ~(1<<9), CLK_RST_CONTROLLER_CLK_CPU_CMPLX); + pwrgateid = tegra_cpu_powergate_id(cpu); + if (pwrgateid < 0) + return pwrgateid; + + /* If this is the first boot, toggle powergates directly. */ + if (!tegra_powergate_is_powered(pwrgateid)) { + ret = tegra_powergate_power_on(pwrgateid); + if (ret) + return ret; + + /* Wait for the power to come up. */ + timeout = jiffies + 10*HZ; + while (tegra_powergate_is_powered(pwrgateid)) { + if (time_after(jiffies, timeout)) + return -ETIMEDOUT; + udelay(10); + } + } - reg = (1<<13) | (1<<9) | (1<<5) | (1<<1); - writel(reg, CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR); + /* CPU partition is powered. Enable the CPU clock. */ + writel(CPU_CLOCK(cpu), CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR); + reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR); + udelay(10); - smp_wmb(); - flush_cache_all(); + /* Remove I/O clamps. */ + ret = tegra_powergate_remove_clamping(pwrgateid); + udelay(10); - /* unhalt the cpu */ - writel(0, IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + 0x14); + /* Clear flow controller CSR. */ + flowctrl_write_cpu_csr(cpu, 0); - timeout = jiffies + (1 * HZ); - while (time_before(jiffies, timeout)) { - if (readl(EVP_CPU_RESET_VECTOR) != boot_vector) - break; - udelay(10); - } + return 0; +} + +int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + int status; - /* put the old boot vector back */ - writel(old_boot_vector, EVP_CPU_RESET_VECTOR); + /* + * Force the CPU into reset. The CPU must remain in reset when the + * flow controller state is cleared (which will cause the flow + * controller to stop driving reset if the CPU has been power-gated + * via the flow controller). This will have no effect on first boot + * of the CPU since it should already be in reset. + */ + writel(CPU_RESET(cpu), CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET); + dmb(); /* - * now the secondary core is starting up let it run its - * calibrations, then wait for it to finish + * Unhalt the CPU. If the flow controller was used to power-gate the + * CPU this will cause the flow controller to stop driving reset. + * The CPU will remain in reset because the clock and reset block + * is now driving reset. */ - spin_unlock(&boot_lock); + flowctrl_write_cpu_halt(cpu, 0); + + switch (tegra_chip_id) { + case TEGRA20: + status = tegra20_power_up_cpu(cpu); + break; + case TEGRA30: + status = tegra30_power_up_cpu(cpu); + break; + default: + status = -EINVAL; + break; + } - return 0; + if (status) + goto done; + + /* Take the CPU out of reset. */ + writel(CPU_RESET(cpu), CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR); + wmb(); +done: + return status; } /* @@ -128,6 +183,6 @@ void __init smp_init_cpus(void) void __init platform_smp_prepare_cpus(unsigned int max_cpus) { - + tegra_cpu_reset_handler_init(); scu_enable(scu_base); } diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c index 948306491a59..c238699ae86f 100644 --- a/arch/arm/mach-tegra/powergate.c +++ b/arch/arm/mach-tegra/powergate.c @@ -31,6 +31,8 @@ #include <mach/iomap.h> #include <mach/powergate.h> +#include "fuse.h" + #define PWRGATE_TOGGLE 0x30 #define PWRGATE_TOGGLE_START (1 << 8) @@ -38,6 +40,16 @@ #define PWRGATE_STATUS 0x38 +static int tegra_num_powerdomains; +static int tegra_num_cpu_domains; +static u8 *tegra_cpu_domains; +static u8 tegra30_cpu_domains[] = { + TEGRA_POWERGATE_CPU0, + TEGRA_POWERGATE_CPU1, + TEGRA_POWERGATE_CPU2, + TEGRA_POWERGATE_CPU3, +}; + static DEFINE_SPINLOCK(tegra_powergate_lock); static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); @@ -75,7 +87,7 @@ static int tegra_powergate_set(int id, bool new_state) int tegra_powergate_power_on(int id) { - if (id < 0 || id >= TEGRA_NUM_POWERGATE) + if (id < 0 || id >= tegra_num_powerdomains) return -EINVAL; return tegra_powergate_set(id, true); @@ -83,17 +95,18 @@ int tegra_powergate_power_on(int id) int tegra_powergate_power_off(int id) { - if (id < 0 || id >= TEGRA_NUM_POWERGATE) + if (id < 0 || id >= tegra_num_powerdomains) return -EINVAL; return tegra_powergate_set(id, false); } -static bool tegra_powergate_is_powered(int id) +int tegra_powergate_is_powered(int id) { u32 status; - WARN_ON(id < 0 || id >= TEGRA_NUM_POWERGATE); + if (id < 0 || id >= tegra_num_powerdomains) + return -EINVAL; status = pmc_read(PWRGATE_STATUS) & (1 << id); return !!status; @@ -103,7 +116,7 @@ int tegra_powergate_remove_clamping(int id) { u32 mask; - if (id < 0 || id >= TEGRA_NUM_POWERGATE) + if (id < 0 || id >= tegra_num_powerdomains) return -EINVAL; /* @@ -156,6 +169,34 @@ err_power: return ret; } +int tegra_cpu_powergate_id(int cpuid) +{ + if (cpuid > 0 && cpuid < tegra_num_cpu_domains) + return tegra_cpu_domains[cpuid]; + + return -EINVAL; +} + +int __init tegra_powergate_init(void) +{ + switch (tegra_chip_id) { + case TEGRA20: + tegra_num_powerdomains = 7; + break; + case TEGRA30: + tegra_num_powerdomains = 14; + tegra_num_cpu_domains = 4; + tegra_cpu_domains = tegra30_cpu_domains; + break; + default: + /* Unknown Tegra variant. Disable powergating */ + tegra_num_powerdomains = 0; + break; + } + + return 0; +} + #ifdef CONFIG_DEBUG_FS static const char * const powergate_name[] = { @@ -175,7 +216,7 @@ static int powergate_show(struct seq_file *s, void *data) seq_printf(s, " powergate powered\n"); seq_printf(s, "------------------\n"); - for (i = 0; i < TEGRA_NUM_POWERGATE; i++) + for (i = 0; i < tegra_num_powerdomains; i++) seq_printf(s, " %9s %7s\n", powergate_name[i], tegra_powergate_is_powered(i) ? "yes" : "no"); return 0; diff --git a/arch/arm/mach-tegra/reset.c b/arch/arm/mach-tegra/reset.c new file mode 100644 index 000000000000..4d6a2ee99c3b --- /dev/null +++ b/arch/arm/mach-tegra/reset.c @@ -0,0 +1,84 @@ +/* + * arch/arm/mach-tegra/reset.c + * + * Copyright (C) 2011,2012 NVIDIA Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include <linux/init.h> +#include <linux/io.h> +#include <linux/cpumask.h> +#include <linux/bitops.h> + +#include <asm/cacheflush.h> +#include <asm/hardware/cache-l2x0.h> + +#include <mach/iomap.h> +#include <mach/irammap.h> + +#include "reset.h" +#include "fuse.h" + +#define TEGRA_IRAM_RESET_BASE (TEGRA_IRAM_BASE + \ + TEGRA_IRAM_RESET_HANDLER_OFFSET) + +static bool is_enabled; + +static void tegra_cpu_reset_handler_enable(void) +{ + void __iomem *iram_base = IO_ADDRESS(TEGRA_IRAM_RESET_BASE); + void __iomem *evp_cpu_reset = + IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE + 0x100); + void __iomem *sb_ctrl = IO_ADDRESS(TEGRA_SB_BASE); + u32 reg; + + BUG_ON(is_enabled); + BUG_ON(tegra_cpu_reset_handler_size > TEGRA_IRAM_RESET_HANDLER_SIZE); + + memcpy(iram_base, (void *)__tegra_cpu_reset_handler_start, + tegra_cpu_reset_handler_size); + + /* + * NOTE: This must be the one and only write to the EVP CPU reset + * vector in the entire system. + */ + writel(TEGRA_IRAM_RESET_BASE + tegra_cpu_reset_handler_offset, + evp_cpu_reset); + wmb(); + reg = readl(evp_cpu_reset); + + /* + * Prevent further modifications to the physical reset vector. + * NOTE: Has no effect on chips prior to Tegra30. + */ + if (tegra_chip_id != TEGRA20) { + reg = readl(sb_ctrl); + reg |= 2; + writel(reg, sb_ctrl); + wmb(); + } + + is_enabled = true; +} + +void __init tegra_cpu_reset_handler_init(void) +{ + +#ifdef CONFIG_SMP + __tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_PRESENT] = + *((u32 *)cpu_present_mask); + __tegra_cpu_reset_handler_data[TEGRA_RESET_STARTUP_SECONDARY] = + virt_to_phys((void *)tegra_secondary_startup); +#endif + + tegra_cpu_reset_handler_enable(); +} diff --git a/arch/arm/mach-tegra/reset.h b/arch/arm/mach-tegra/reset.h new file mode 100644 index 000000000000..de88bf851dd3 --- /dev/null +++ b/arch/arm/mach-tegra/reset.h @@ -0,0 +1,50 @@ +/* + * arch/arm/mach-tegra/reset.h + * + * CPU reset dispatcher. + * + * Copyright (c) 2011, NVIDIA Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef __MACH_TEGRA_RESET_H +#define __MACH_TEGRA_RESET_H + +#define TEGRA_RESET_MASK_PRESENT 0 +#define TEGRA_RESET_MASK_LP1 1 +#define TEGRA_RESET_MASK_LP2 2 +#define TEGRA_RESET_STARTUP_SECONDARY 3 +#define TEGRA_RESET_STARTUP_LP2 4 +#define TEGRA_RESET_STARTUP_LP1 5 +#define TEGRA_RESET_DATA_SIZE 6 + +#ifndef __ASSEMBLY__ + +extern unsigned long __tegra_cpu_reset_handler_data[TEGRA_RESET_DATA_SIZE]; + +void __tegra_cpu_reset_handler_start(void); +void __tegra_cpu_reset_handler(void); +void __tegra_cpu_reset_handler_end(void); +void tegra_secondary_startup(void); + +#define tegra_cpu_reset_handler_offset \ + ((u32)__tegra_cpu_reset_handler - \ + (u32)__tegra_cpu_reset_handler_start) + +#define tegra_cpu_reset_handler_size \ + (__tegra_cpu_reset_handler_end - \ + __tegra_cpu_reset_handler_start) + +void __init tegra_cpu_reset_handler_init(void); + +#endif +#endif |