1 /* 2 * Copyright (C) 2002 ARM Ltd. 3 * Copyright (C) 2008 STMicroelctronics. 4 * Copyright (C) 2009 ST-Ericsson. 5 * Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com> 6 * 7 * This file is based on arm realview platform 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 as 11 * published by the Free Software Foundation. 12 */ 13 #include <linux/init.h> 14 #include <linux/errno.h> 15 #include <linux/delay.h> 16 #include <linux/device.h> 17 #include <linux/smp.h> 18 #include <linux/io.h> 19 20 #include <asm/cacheflush.h> 21 #include <asm/localtimer.h> 22 #include <asm/smp_scu.h> 23 #include <mach/hardware.h> 24 25 /* 26 * control for which core is the next to come out of the secondary 27 * boot "holding pen" 28 */ 29 volatile int __cpuinitdata pen_release = -1; 30 31 static unsigned int __init get_core_count(void) 32 { 33 return scu_get_core_count(__io_address(UX500_SCU_BASE)); 34 } 35 36 static DEFINE_SPINLOCK(boot_lock); 37 38 void __cpuinit platform_secondary_init(unsigned int cpu) 39 { 40 trace_hardirqs_off(); 41 42 /* 43 * if any interrupts are already enabled for the primary 44 * core (e.g. timer irq), then they will not have been enabled 45 * for us: do so 46 */ 47 gic_cpu_init(0, __io_address(UX500_GIC_CPU_BASE)); 48 49 /* 50 * let the primary processor know we're out of the 51 * pen, then head off into the C entry point 52 */ 53 pen_release = -1; 54 55 /* 56 * Synchronise with the boot thread. 57 */ 58 spin_lock(&boot_lock); 59 spin_unlock(&boot_lock); 60 } 61 62 int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) 63 { 64 unsigned long timeout; 65 66 /* 67 * set synchronisation state between this boot processor 68 * and the secondary one 69 */ 70 spin_lock(&boot_lock); 71 72 /* 73 * The secondary processor is waiting to be released from 74 * the holding pen - release it, then wait for it to flag 75 * that it has been released by resetting pen_release. 76 */ 77 pen_release = cpu; 78 __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release)); 79 outer_clean_range(__pa(&pen_release), __pa(&pen_release) + 1); 80 81 timeout = jiffies + (1 * HZ); 82 while (time_before(jiffies, timeout)) { 83 if (pen_release == -1) 84 break; 85 } 86 87 /* 88 * now the secondary core is starting up let it run its 89 * calibrations, then wait for it to finish 90 */ 91 spin_unlock(&boot_lock); 92 93 return pen_release != -1 ? -ENOSYS : 0; 94 } 95 96 static void __init wakeup_secondary(void) 97 { 98 /* nobody is to be released from the pen yet */ 99 pen_release = -1; 100 101 /* 102 * write the address of secondary startup into the backup ram register 103 * at offset 0x1FF4, then write the magic number 0xA1FEED01 to the 104 * backup ram register at offset 0x1FF0, which is what boot rom code 105 * is waiting for. This would wake up the secondary core from WFE 106 */ 107 #define U8500_CPU1_JUMPADDR_OFFSET 0x1FF4 108 __raw_writel(virt_to_phys(u8500_secondary_startup), 109 __io_address(UX500_BACKUPRAM0_BASE) + 110 U8500_CPU1_JUMPADDR_OFFSET); 111 112 #define U8500_CPU1_WAKEMAGIC_OFFSET 0x1FF0 113 __raw_writel(0xA1FEED01, 114 __io_address(UX500_BACKUPRAM0_BASE) + 115 U8500_CPU1_WAKEMAGIC_OFFSET); 116 117 /* make sure write buffer is drained */ 118 mb(); 119 } 120 121 /* 122 * Initialise the CPU possible map early - this describes the CPUs 123 * which may be present or become present in the system. 124 */ 125 void __init smp_init_cpus(void) 126 { 127 unsigned int i, ncores = get_core_count(); 128 129 for (i = 0; i < ncores; i++) 130 set_cpu_possible(i, true); 131 } 132 133 void __init smp_prepare_cpus(unsigned int max_cpus) 134 { 135 unsigned int ncores = get_core_count(); 136 unsigned int cpu = smp_processor_id(); 137 int i; 138 139 /* sanity check */ 140 if (ncores == 0) { 141 printk(KERN_ERR 142 "U8500: strange CM count of 0? Default to 1\n"); 143 ncores = 1; 144 } 145 146 if (ncores > num_possible_cpus()) { 147 printk(KERN_WARNING 148 "U8500: no. of cores (%d) greater than configured " 149 "maximum of %d - clipping\n", 150 ncores, num_possible_cpus()); 151 ncores = num_possible_cpus(); 152 } 153 154 smp_store_cpu_info(cpu); 155 156 /* 157 * are we trying to boot more cores than exist? 158 */ 159 if (max_cpus > ncores) 160 max_cpus = ncores; 161 162 /* 163 * Initialise the present map, which describes the set of CPUs 164 * actually populated at the present time. 165 */ 166 for (i = 0; i < max_cpus; i++) 167 set_cpu_present(i, true); 168 169 if (max_cpus > 1) { 170 /* 171 * Enable the local timer or broadcast device for the 172 * boot CPU, but only if we have more than one CPU. 173 */ 174 percpu_timer_setup(); 175 scu_enable(__io_address(UX500_SCU_BASE)); 176 wakeup_secondary(); 177 } 178 } 179