[ARM/tegra] Add Tegra3 support
[linux-2.6.git] / arch / arm / mach-tegra / platsmp.c
1 /*
2  *  linux/arch/arm/mach-tegra/platsmp.c
3  *
4  *  Copyright (C) 2002 ARM Ltd.
5  *  All Rights Reserved
6  *
7  *  Copyright (C) 2009 Palm
8  *  All Rights Reserved
9  *
10  *  Copyright (C) 2010 NVIDIA Corporation
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License version 2 as
14  * published by the Free Software Foundation.
15  */
16
17 #include <linux/kernel.h>
18 #include <linux/init.h>
19 #include <linux/io.h>
20 #include <linux/smp.h>
21 #include <linux/delay.h>
22
23 #include <asm/hardware/gic.h>
24 #include <asm/smp_scu.h>
25
26 #include <mach/iomap.h>
27 #include <mach/powergate.h>
28
29 #include "pm.h"
30
31 #define EVP_CPU_RESET_VECTOR \
32         (IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100)
33 #define CLK_RST_CONTROLLER_CLK_CPU_CMPLX \
34         (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x4c)
35 #define CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET \
36         (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x340)
37 #define CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR \
38         (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x344)
39 #define FLOW_CTRL_HALT_CPUx_EVENTS(cpu) \
40         (IO_ADDRESS(TEGRA_FLOW_CTRL_BASE + ((cpu)?(((cpu)-1)*8 + 0x14) : 0x0)))
41
42 #define CPU_CLOCK(cpu)  (0x1<<(8+cpu))
43 #define CPU_RESET(cpu)  (0x1111ul<<(cpu))
44
45 static unsigned int available_cpus(void);
46 #if defined(CONFIG_ARCH_TEGRA_2x_SOC)
47 static inline int is_g_cluster_available(unsigned int cpu)
48 { return -EPERM; }
49 static inline bool is_cpu_powered(unsigned int cpu)
50 { return true; }
51 static inline int power_up_cpu(unsigned int cpu)
52 { return 0; }
53
54 /* For Tegra2 use the software-written value of the reset regsiter for status.*/
55 #define CLK_RST_CONTROLLER_CPU_CMPLX_STATUS CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET
56
57 #else
58 static int is_g_cluster_available(unsigned int cpu);
59 static bool is_cpu_powered(unsigned int cpu);
60 static int power_up_cpu(unsigned int cpu);
61
62 #define CAR_BOND_OUT_V \
63         (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x390)
64 #define CAR_BOND_OUT_V_CPU_G    (1<<0)
65 #define CLK_RST_CONTROLLER_CPU_CMPLX_STATUS \
66         (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x470)
67
68 #define FUSE_SKU_DIRECT_CONFIG \
69         (IO_ADDRESS(TEGRA_FUSE_BASE) + 0x1F4)
70 #define FUSE_SKU_DISABLE_ALL_CPUS       (1<<5)
71 #define FUSE_SKU_NUM_DISABLED_CPUS(x)   (((x) >> 3) & 3)
72 #endif
73
74 extern void tegra_secondary_startup(void);
75
76 static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE);
77
78 void __cpuinit platform_secondary_init(unsigned int cpu)
79 {
80         gic_secondary_init(0);
81 }
82
83 int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
84 {
85         u32 reg;
86         int status;
87
88         if (is_lp_cluster()) {
89                 /* The G CPU may not be available for a
90                    variety of reasons. */
91                 status = is_g_cluster_available(cpu);
92                 if (status)
93                         return status;
94
95                 /* Switch to the G CPU before continuing. */
96                 status = tegra_cluster_control(0,
97                                                TEGRA_POWER_CLUSTER_G |
98                                                TEGRA_POWER_CLUSTER_IMMEDIATE);
99                 if (status)
100                         return status;
101         }
102
103         smp_wmb();
104
105         /* set the reset vector to point to the secondary_startup routine */
106         writel(virt_to_phys(tegra_secondary_startup), EVP_CPU_RESET_VECTOR);
107
108         /* Force the CPU into reset. The CPU must remain in reset when the
109            flow controller state is cleared (which will cause the flow
110            controller to stop driving reset if the CPU has been power-gated
111            via the flow controller). This will have no effect on first boot
112            of the CPU since it should already be in reset. */
113         writel(CPU_RESET(cpu), CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
114         dmb();
115
116         /* Unhalt the CPU. If the flow controller was used to power-gate the
117            CPU this will cause the flow controller to stop driving reset.
118            The CPU will remain in reset because the clock and reset block
119            is now driving reset. */
120         writel(0, FLOW_CTRL_HALT_CPUx_EVENTS(cpu));
121         dmb();
122
123         /* enable cpu clock on cpu */
124         reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
125         writel(reg & ~CPU_CLOCK(cpu), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
126         dmb();
127
128         status = power_up_cpu(cpu);
129         if (status)
130                 goto done;
131
132         dmb();
133         writel(CPU_RESET(cpu), CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR);
134
135 done:
136         return status;
137 }
138
139 /*
140  * Initialise the CPU possible map early - this describes the CPUs
141  * which may be present or become present in the system.
142  */
143 void __init smp_init_cpus(void)
144 {
145         unsigned int i, ncores = available_cpus();
146
147         if (ncores > nr_cpu_ids) {
148                 pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
149                         ncores, nr_cpu_ids);
150                 ncores = nr_cpu_ids;
151         }
152
153         for (i = 0; i < ncores; i++)
154                 set_cpu_possible(i, true);
155
156         set_smp_cross_call(gic_raise_softirq);
157 }
158
159 void __init platform_smp_prepare_cpus(unsigned int max_cpus)
160 {
161
162         scu_enable(scu_base);
163 }
164
165 #if defined(CONFIG_ARCH_TEGRA_3x_SOC)
166
167 static bool is_cpu_powered(unsigned int cpu)
168 {
169         if (is_lp_cluster())
170                 return true;
171         else
172                 return tegra_powergate_is_powered(TEGRA_CPU_POWERGATE_ID(cpu));
173 }
174
175 static int power_up_cpu(unsigned int cpu)
176 {
177         int ret;
178         unsigned long timeout;
179
180         BUG_ON(cpu == smp_processor_id());
181         BUG_ON(is_lp_cluster());
182
183         if (!is_cpu_powered(cpu))
184         {
185                 ret = tegra_powergate_power_on(TEGRA_CPU_POWERGATE_ID(cpu));
186                 if (ret)
187                         goto fail;
188
189                 /* Wait for the power to come up. */
190                 timeout = jiffies + 10*HZ;
191
192                 do {
193                         if (is_cpu_powered(cpu))
194                                 goto remove_clamps;
195                         udelay(10);
196                 } while (time_before(jiffies, timeout));
197                 ret = -ETIMEDOUT;
198                 goto fail;
199         }
200
201 remove_clamps:
202         ret = tegra_powergate_remove_clamping(TEGRA_CPU_POWERGATE_ID(cpu));
203 fail:
204         return ret;
205 }
206
207 static int is_g_cluster_available(unsigned int cpu)
208 {
209         u32 fuse_sku = readl(FUSE_SKU_DIRECT_CONFIG);
210         u32 bond_out = readl(CAR_BOND_OUT_V);
211
212         /* Does the G CPU complex exist at all? */
213         if ((fuse_sku & FUSE_SKU_DISABLE_ALL_CPUS) ||
214             (bond_out & CAR_BOND_OUT_V_CPU_G))
215                 return -EPERM;
216
217         if (cpu >= available_cpus())
218                 return -EPERM;
219
220         /* FIXME: The G CPU can be unavailable for a number of reasons
221          *        (e.g., low battery, over temperature, etc.). Add checks for
222          *        these conditions. */
223
224         return 0;
225 }
226 #endif
227
228 static unsigned int available_cpus(void)
229 {
230         static unsigned int ncores = 0;
231
232         if (ncores == 0) {
233                 ncores = scu_get_core_count(scu_base);
234 #ifdef CONFIG_ARCH_TEGRA_3x_SOC
235                 if (ncores > 1) {
236                         u32 fuse_sku = readl(FUSE_SKU_DIRECT_CONFIG);
237                         ncores -= FUSE_SKU_NUM_DISABLED_CPUS(fuse_sku);
238                         BUG_ON((int)ncores <= 0);
239                 }
240 #endif
241         }
242         return ncores;
243 }