Merge branch 'timers-for-linus-hpet' of git://git.kernel.org/pub/scm/linux/kernel...
[sfrench/cifs-2.6.git] / arch / arm / mach-omap2 / omap-smp.c
1 /*
2  * OMAP4 SMP source file. It contains platform specific fucntions
3  * needed for the linux smp kernel.
4  *
5  * Copyright (C) 2009 Texas Instruments, Inc.
6  *
7  * Author:
8  *      Santosh Shilimkar <santosh.shilimkar@ti.com>
9  *
10  * Platform file needed for the OMAP4 SMP. This file is based on arm
11  * realview smp platform.
12  * * Copyright (c) 2002 ARM Limited.
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License version 2 as
16  * published by the Free Software Foundation.
17  */
18 #include <linux/init.h>
19 #include <linux/device.h>
20 #include <linux/jiffies.h>
21 #include <linux/smp.h>
22 #include <linux/io.h>
23
24 #include <asm/localtimer.h>
25 #include <asm/smp_scu.h>
26 #include <mach/hardware.h>
27 #include <plat/common.h>
28
29 /* Registers used for communicating startup information */
30 static void __iomem *omap4_auxcoreboot_reg0;
31 static void __iomem *omap4_auxcoreboot_reg1;
32
33 /* SCU base address */
34 static void __iomem *scu_base;
35
36 /*
37  * Use SCU config register to count number of cores
38  */
39 static inline unsigned int get_core_count(void)
40 {
41         if (scu_base)
42                 return scu_get_core_count(scu_base);
43         return 1;
44 }
45
46 static DEFINE_SPINLOCK(boot_lock);
47
48 void __cpuinit platform_secondary_init(unsigned int cpu)
49 {
50         trace_hardirqs_off();
51
52         /*
53          * If any interrupts are already enabled for the primary
54          * core (e.g. timer irq), then they will not have been enabled
55          * for us: do so
56          */
57         gic_cpu_init(0, gic_cpu_base_addr);
58
59         /*
60          * Synchronise with the boot thread.
61          */
62         spin_lock(&boot_lock);
63         spin_unlock(&boot_lock);
64 }
65
66 int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
67 {
68         unsigned long timeout;
69
70         /*
71          * Set synchronisation state between this boot processor
72          * and the secondary one
73          */
74         spin_lock(&boot_lock);
75
76         /*
77          * Update the AuxCoreBoot1 with boot state for secondary core.
78          * omap_secondary_startup() routine will hold the secondary core till
79          * the AuxCoreBoot1 register is updated with cpu state
80          * A barrier is added to ensure that write buffer is drained
81          */
82         __raw_writel(cpu, omap4_auxcoreboot_reg1);
83         smp_wmb();
84
85         timeout = jiffies + (1 * HZ);
86         while (time_before(jiffies, timeout))
87                 ;
88
89         /*
90          * Now the secondary core is starting up let it run its
91          * calibrations, then wait for it to finish
92          */
93         spin_unlock(&boot_lock);
94
95         return 0;
96 }
97
98 static void __init wakeup_secondary(void)
99 {
100         /*
101          * Write the address of secondary startup routine into the
102          * AuxCoreBoot0 where ROM code will jump and start executing
103          * on secondary core once out of WFE
104          * A barrier is added to ensure that write buffer is drained
105          */
106         __raw_writel(virt_to_phys(omap_secondary_startup),         \
107                                         omap4_auxcoreboot_reg0);
108         smp_wmb();
109
110         /*
111          * Send a 'sev' to wake the secondary core from WFE.
112          */
113         set_event();
114         mb();
115 }
116
117 /*
118  * Initialise the CPU possible map early - this describes the CPUs
119  * which may be present or become present in the system.
120  */
121 void __init smp_init_cpus(void)
122 {
123         unsigned int i, ncores;
124
125         /* Never released */
126         scu_base = ioremap(OMAP44XX_SCU_BASE, SZ_256);
127         BUG_ON(!scu_base);
128
129         ncores = get_core_count();
130
131         for (i = 0; i < ncores; i++)
132                 set_cpu_possible(i, true);
133 }
134
135 void __init smp_prepare_cpus(unsigned int max_cpus)
136 {
137         unsigned int ncores = get_core_count();
138         unsigned int cpu = smp_processor_id();
139         void __iomem *omap4_wkupgen_base;
140         int i;
141
142         /* sanity check */
143         if (ncores == 0) {
144                 printk(KERN_ERR
145                        "OMAP4: strange core count of 0? Default to 1\n");
146                 ncores = 1;
147         }
148
149         if (ncores > NR_CPUS) {
150                 printk(KERN_WARNING
151                        "OMAP4: no. of cores (%d) greater than configured "
152                        "maximum of %d - clipping\n",
153                        ncores, NR_CPUS);
154                 ncores = NR_CPUS;
155         }
156         smp_store_cpu_info(cpu);
157
158         /*
159          * are we trying to boot more cores than exist?
160          */
161         if (max_cpus > ncores)
162                 max_cpus = ncores;
163
164         /*
165          * Initialise the present map, which describes the set of CPUs
166          * actually populated at the present time.
167          */
168         for (i = 0; i < max_cpus; i++)
169                 set_cpu_present(i, true);
170
171         /* Never released */
172         omap4_wkupgen_base = ioremap(OMAP44XX_WKUPGEN_BASE, SZ_4K);
173         BUG_ON(!omap4_wkupgen_base);
174         omap4_auxcoreboot_reg0 = omap4_wkupgen_base + 0x800;
175         omap4_auxcoreboot_reg1 = omap4_wkupgen_base + 0x804;
176
177         if (max_cpus > 1) {
178                 /*
179                  * Enable the local timer or broadcast device for the
180                  * boot CPU, but only if we have more than one CPU.
181                  */
182                 percpu_timer_setup();
183
184                 /*
185                  * Initialise the SCU and wake up the secondary core using
186                  * wakeup_secondary().
187                  */
188                 scu_enable(scu_base);
189                 wakeup_secondary();
190         }
191 }