diff options
Diffstat (limited to 'arch/arm/mach-tegra/platsmp.c')
-rw-r--r-- | arch/arm/mach-tegra/platsmp.c | 137 |
1 files changed, 96 insertions, 41 deletions
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 @@ | |||
24 | #include <asm/mach-types.h> | 24 | #include <asm/mach-types.h> |
25 | #include <asm/smp_scu.h> | 25 | #include <asm/smp_scu.h> |
26 | 26 | ||
27 | #include <mach/clk.h> | ||
27 | #include <mach/iomap.h> | 28 | #include <mach/iomap.h> |
29 | #include <mach/powergate.h> | ||
30 | |||
31 | #include "fuse.h" | ||
32 | #include "flowctrl.h" | ||
33 | #include "reset.h" | ||
28 | 34 | ||
29 | extern void tegra_secondary_startup(void); | 35 | extern void tegra_secondary_startup(void); |
30 | 36 | ||
31 | static DEFINE_SPINLOCK(boot_lock); | ||
32 | static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE); | 37 | static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE); |
33 | 38 | ||
34 | #define EVP_CPU_RESET_VECTOR \ | 39 | #define EVP_CPU_RESET_VECTOR \ |
35 | (IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100) | 40 | (IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100) |
36 | #define CLK_RST_CONTROLLER_CLK_CPU_CMPLX \ | 41 | #define CLK_RST_CONTROLLER_CLK_CPU_CMPLX \ |
37 | (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x4c) | 42 | (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x4c) |
43 | #define CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET \ | ||
44 | (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x340) | ||
38 | #define CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR \ | 45 | #define CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR \ |
39 | (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x344) | 46 | (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x344) |
47 | #define CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR \ | ||
48 | (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x34c) | ||
49 | |||
50 | #define CPU_CLOCK(cpu) (0x1<<(8+cpu)) | ||
51 | #define CPU_RESET(cpu) (0x1111ul<<(cpu)) | ||
40 | 52 | ||
41 | void __cpuinit platform_secondary_init(unsigned int cpu) | 53 | void __cpuinit platform_secondary_init(unsigned int cpu) |
42 | { | 54 | { |
@@ -47,63 +59,106 @@ void __cpuinit platform_secondary_init(unsigned int cpu) | |||
47 | */ | 59 | */ |
48 | gic_secondary_init(0); | 60 | gic_secondary_init(0); |
49 | 61 | ||
50 | /* | ||
51 | * Synchronise with the boot thread. | ||
52 | */ | ||
53 | spin_lock(&boot_lock); | ||
54 | spin_unlock(&boot_lock); | ||
55 | } | 62 | } |
56 | 63 | ||
57 | int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) | 64 | static int tegra20_power_up_cpu(unsigned int cpu) |
58 | { | 65 | { |
59 | unsigned long old_boot_vector; | ||
60 | unsigned long boot_vector; | ||
61 | unsigned long timeout; | ||
62 | u32 reg; | 66 | u32 reg; |
63 | 67 | ||
64 | /* | 68 | /* Enable the CPU clock. */ |
65 | * set synchronisation state between this boot processor | 69 | reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX); |
66 | * and the secondary one | 70 | writel(reg & ~CPU_CLOCK(cpu), CLK_RST_CONTROLLER_CLK_CPU_CMPLX); |
67 | */ | 71 | barrier(); |
68 | spin_lock(&boot_lock); | 72 | reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX); |
69 | 73 | ||
74 | /* Clear flow controller CSR. */ | ||
75 | flowctrl_write_cpu_csr(cpu, 0); | ||
70 | 76 | ||
71 | /* set the reset vector to point to the secondary_startup routine */ | 77 | return 0; |
78 | } | ||
72 | 79 | ||
73 | boot_vector = virt_to_phys(tegra_secondary_startup); | 80 | static int tegra30_power_up_cpu(unsigned int cpu) |
74 | old_boot_vector = readl(EVP_CPU_RESET_VECTOR); | 81 | { |
75 | writel(boot_vector, EVP_CPU_RESET_VECTOR); | 82 | u32 reg; |
83 | int ret, pwrgateid; | ||
84 | unsigned long timeout; | ||
76 | 85 | ||
77 | /* enable cpu clock on cpu1 */ | 86 | pwrgateid = tegra_cpu_powergate_id(cpu); |
78 | reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX); | 87 | if (pwrgateid < 0) |
79 | writel(reg & ~(1<<9), CLK_RST_CONTROLLER_CLK_CPU_CMPLX); | 88 | return pwrgateid; |
89 | |||
90 | /* If this is the first boot, toggle powergates directly. */ | ||
91 | if (!tegra_powergate_is_powered(pwrgateid)) { | ||
92 | ret = tegra_powergate_power_on(pwrgateid); | ||
93 | if (ret) | ||
94 | return ret; | ||
95 | |||
96 | /* Wait for the power to come up. */ | ||
97 | timeout = jiffies + 10*HZ; | ||
98 | while (tegra_powergate_is_powered(pwrgateid)) { | ||
99 | if (time_after(jiffies, timeout)) | ||
100 | return -ETIMEDOUT; | ||
101 | udelay(10); | ||
102 | } | ||
103 | } | ||
80 | 104 | ||
81 | reg = (1<<13) | (1<<9) | (1<<5) | (1<<1); | 105 | /* CPU partition is powered. Enable the CPU clock. */ |
82 | writel(reg, CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR); | 106 | writel(CPU_CLOCK(cpu), CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR); |
107 | reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR); | ||
108 | udelay(10); | ||
83 | 109 | ||
84 | smp_wmb(); | 110 | /* Remove I/O clamps. */ |
85 | flush_cache_all(); | 111 | ret = tegra_powergate_remove_clamping(pwrgateid); |
112 | udelay(10); | ||
86 | 113 | ||
87 | /* unhalt the cpu */ | 114 | /* Clear flow controller CSR. */ |
88 | writel(0, IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + 0x14); | 115 | flowctrl_write_cpu_csr(cpu, 0); |
89 | 116 | ||
90 | timeout = jiffies + (1 * HZ); | 117 | return 0; |
91 | while (time_before(jiffies, timeout)) { | 118 | } |
92 | if (readl(EVP_CPU_RESET_VECTOR) != boot_vector) | 119 | |
93 | break; | 120 | int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) |
94 | udelay(10); | 121 | { |
95 | } | 122 | int status; |
96 | 123 | ||
97 | /* put the old boot vector back */ | 124 | /* |
98 | writel(old_boot_vector, EVP_CPU_RESET_VECTOR); | 125 | * Force the CPU into reset. The CPU must remain in reset when the |
126 | * flow controller state is cleared (which will cause the flow | ||
127 | * controller to stop driving reset if the CPU has been power-gated | ||
128 | * via the flow controller). This will have no effect on first boot | ||
129 | * of the CPU since it should already be in reset. | ||
130 | */ | ||
131 | writel(CPU_RESET(cpu), CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET); | ||
132 | dmb(); | ||
99 | 133 | ||
100 | /* | 134 | /* |
101 | * now the secondary core is starting up let it run its | 135 | * Unhalt the CPU. If the flow controller was used to power-gate the |
102 | * calibrations, then wait for it to finish | 136 | * CPU this will cause the flow controller to stop driving reset. |
137 | * The CPU will remain in reset because the clock and reset block | ||
138 | * is now driving reset. | ||
103 | */ | 139 | */ |
104 | spin_unlock(&boot_lock); | 140 | flowctrl_write_cpu_halt(cpu, 0); |
141 | |||
142 | switch (tegra_chip_id) { | ||
143 | case TEGRA20: | ||
144 | status = tegra20_power_up_cpu(cpu); | ||
145 | break; | ||
146 | case TEGRA30: | ||
147 | status = tegra30_power_up_cpu(cpu); | ||
148 | break; | ||
149 | default: | ||
150 | status = -EINVAL; | ||
151 | break; | ||
152 | } | ||
105 | 153 | ||
106 | return 0; | 154 | if (status) |
155 | goto done; | ||
156 | |||
157 | /* Take the CPU out of reset. */ | ||
158 | writel(CPU_RESET(cpu), CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR); | ||
159 | wmb(); | ||
160 | done: | ||
161 | return status; | ||
107 | } | 162 | } |
108 | 163 | ||
109 | /* | 164 | /* |
@@ -128,6 +183,6 @@ void __init smp_init_cpus(void) | |||
128 | 183 | ||
129 | void __init platform_smp_prepare_cpus(unsigned int max_cpus) | 184 | void __init platform_smp_prepare_cpus(unsigned int max_cpus) |
130 | { | 185 | { |
131 | 186 | tegra_cpu_reset_handler_init(); | |
132 | scu_enable(scu_base); | 187 | scu_enable(scu_base); |
133 | } | 188 | } |