Merge branch 'multiplatform/platform-data' into next/multiplatform
[pandora-kernel.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  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2 as
12  * published by the Free Software Foundation.
13  */
14 #include <linux/init.h>
15 #include <linux/errno.h>
16 #include <linux/delay.h>
17 #include <linux/device.h>
18 #include <linux/jiffies.h>
19 #include <linux/smp.h>
20 #include <linux/io.h>
21
22 #include <asm/cacheflush.h>
23 #include <asm/hardware/gic.h>
24 #include <asm/mach-types.h>
25 #include <asm/smp_scu.h>
26
27 #include <mach/clk.h>
28 #include <mach/iomap.h>
29 #include <mach/powergate.h>
30
31 #include "fuse.h"
32 #include "flowctrl.h"
33 #include "reset.h"
34 #include "tegra_cpu_car.h"
35
36 #include "common.h"
37
38 extern void tegra_secondary_startup(void);
39
40 static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE);
41
42 #define EVP_CPU_RESET_VECTOR \
43         (IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100)
44
45 static void __cpuinit tegra_secondary_init(unsigned int cpu)
46 {
47         /*
48          * if any interrupts are already enabled for the primary
49          * core (e.g. timer irq), then they will not have been enabled
50          * for us: do so
51          */
52         gic_secondary_init(0);
53
54 }
55
56 static int tegra20_power_up_cpu(unsigned int cpu)
57 {
58         /* Enable the CPU clock. */
59         tegra_enable_cpu_clock(cpu);
60
61         /* Clear flow controller CSR. */
62         flowctrl_write_cpu_csr(cpu, 0);
63
64         return 0;
65 }
66
67 static int tegra30_power_up_cpu(unsigned int cpu)
68 {
69         int ret, pwrgateid;
70         unsigned long timeout;
71
72         pwrgateid = tegra_cpu_powergate_id(cpu);
73         if (pwrgateid < 0)
74                 return pwrgateid;
75
76         /* If this is the first boot, toggle powergates directly. */
77         if (!tegra_powergate_is_powered(pwrgateid)) {
78                 ret = tegra_powergate_power_on(pwrgateid);
79                 if (ret)
80                         return ret;
81
82                 /* Wait for the power to come up. */
83                 timeout = jiffies + 10*HZ;
84                 while (tegra_powergate_is_powered(pwrgateid)) {
85                         if (time_after(jiffies, timeout))
86                                 return -ETIMEDOUT;
87                         udelay(10);
88                 }
89         }
90
91         /* CPU partition is powered. Enable the CPU clock. */
92         tegra_enable_cpu_clock(cpu);
93         udelay(10);
94
95         /* Remove I/O clamps. */
96         ret = tegra_powergate_remove_clamping(pwrgateid);
97         udelay(10);
98
99         /* Clear flow controller CSR. */
100         flowctrl_write_cpu_csr(cpu, 0);
101
102         return 0;
103 }
104
105 static int __cpuinit tegra_boot_secondary(unsigned int cpu, struct task_struct *idle)
106 {
107         int status;
108
109         /*
110          * Force the CPU into reset. The CPU must remain in reset when the
111          * flow controller state is cleared (which will cause the flow
112          * controller to stop driving reset if the CPU has been power-gated
113          * via the flow controller). This will have no effect on first boot
114          * of the CPU since it should already be in reset.
115          */
116         tegra_put_cpu_in_reset(cpu);
117
118         /*
119          * Unhalt the CPU. If the flow controller was used to power-gate the
120          * CPU this will cause the flow controller to stop driving reset.
121          * The CPU will remain in reset because the clock and reset block
122          * is now driving reset.
123          */
124         flowctrl_write_cpu_halt(cpu, 0);
125
126         switch (tegra_chip_id) {
127         case TEGRA20:
128                 status = tegra20_power_up_cpu(cpu);
129                 break;
130         case TEGRA30:
131                 status = tegra30_power_up_cpu(cpu);
132                 break;
133         default:
134                 status = -EINVAL;
135                 break;
136         }
137
138         if (status)
139                 goto done;
140
141         /* Take the CPU out of reset. */
142         tegra_cpu_out_of_reset(cpu);
143 done:
144         return status;
145 }
146
147 /*
148  * Initialise the CPU possible map early - this describes the CPUs
149  * which may be present or become present in the system.
150  */
151 static void __init tegra_smp_init_cpus(void)
152 {
153         unsigned int i, ncores = scu_get_core_count(scu_base);
154
155         if (ncores > nr_cpu_ids) {
156                 pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
157                         ncores, nr_cpu_ids);
158                 ncores = nr_cpu_ids;
159         }
160
161         for (i = 0; i < ncores; i++)
162                 set_cpu_possible(i, true);
163
164         set_smp_cross_call(gic_raise_softirq);
165 }
166
167 static void __init tegra_smp_prepare_cpus(unsigned int max_cpus)
168 {
169         tegra_cpu_reset_handler_init();
170         scu_enable(scu_base);
171 }
172
173 struct smp_operations tegra_smp_ops __initdata = {
174         .smp_init_cpus          = tegra_smp_init_cpus,
175         .smp_prepare_cpus       = tegra_smp_prepare_cpus,
176         .smp_secondary_init     = tegra_secondary_init,
177         .smp_boot_secondary     = tegra_boot_secondary,
178 #ifdef CONFIG_HOTPLUG_CPU
179         .cpu_die                = tegra_cpu_die,
180         .cpu_disable            = tegra_cpu_disable,
181 #endif
182 };