1 /* 2 * Copyright (C) 2002 ARM Ltd. 3 * All Rights Reserved 4 * Copyright (c) 2010, Code Aurora Forum. All rights reserved. 5 * Copyright (c) 2014 The Linux Foundation. All rights reserved. 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 */ 11 12 #include <linux/init.h> 13 #include <linux/errno.h> 14 #include <linux/delay.h> 15 #include <linux/device.h> 16 #include <linux/smp.h> 17 #include <linux/io.h> 18 19 #include <asm/cputype.h> 20 #include <asm/smp_plat.h> 21 22 #include "scm-boot.h" 23 24 #define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x15A0 25 #define SCSS_CPU1CORE_RESET 0xD80 26 #define SCSS_DBG_STATUS_CORE_PWRDUP 0xE64 27 28 extern void secondary_startup(void); 29 30 static DEFINE_SPINLOCK(boot_lock); 31 32 #ifdef CONFIG_HOTPLUG_CPU 33 static void __ref qcom_cpu_die(unsigned int cpu) 34 { 35 wfi(); 36 } 37 #endif 38 39 static inline int get_core_count(void) 40 { 41 /* 1 + the PART[1:0] field of MIDR */ 42 return ((read_cpuid_id() >> 4) & 3) + 1; 43 } 44 45 static void qcom_secondary_init(unsigned int cpu) 46 { 47 /* 48 * Synchronise with the boot thread. 49 */ 50 spin_lock(&boot_lock); 51 spin_unlock(&boot_lock); 52 } 53 54 static void prepare_cold_cpu(unsigned int cpu) 55 { 56 int ret; 57 ret = scm_set_boot_addr(virt_to_phys(secondary_startup), 58 SCM_FLAG_COLDBOOT_CPU1); 59 if (ret == 0) { 60 void __iomem *sc1_base_ptr; 61 sc1_base_ptr = ioremap_nocache(0x00902000, SZ_4K*2); 62 if (sc1_base_ptr) { 63 writel(0, sc1_base_ptr + VDD_SC1_ARRAY_CLAMP_GFS_CTL); 64 writel(0, sc1_base_ptr + SCSS_CPU1CORE_RESET); 65 writel(3, sc1_base_ptr + SCSS_DBG_STATUS_CORE_PWRDUP); 66 iounmap(sc1_base_ptr); 67 } 68 } else 69 printk(KERN_DEBUG "Failed to set secondary core boot " 70 "address\n"); 71 } 72 73 static int qcom_boot_secondary(unsigned int cpu, struct task_struct *idle) 74 { 75 static int cold_boot_done; 76 77 /* Only need to bring cpu out of reset this way once */ 78 if (cold_boot_done == false) { 79 prepare_cold_cpu(cpu); 80 cold_boot_done = true; 81 } 82 83 /* 84 * set synchronisation state between this boot processor 85 * and the secondary one 86 */ 87 spin_lock(&boot_lock); 88 89 /* 90 * Send the secondary CPU a soft interrupt, thereby causing 91 * the boot monitor to read the system wide flags register, 92 * and branch to the address found there. 93 */ 94 arch_send_wakeup_ipi_mask(cpumask_of(cpu)); 95 96 /* 97 * now the secondary core is starting up let it run its 98 * calibrations, then wait for it to finish 99 */ 100 spin_unlock(&boot_lock); 101 102 return 0; 103 } 104 105 /* 106 * Initialise the CPU possible map early - this describes the CPUs 107 * which may be present or become present in the system. The msm8x60 108 * does not support the ARM SCU, so just set the possible cpu mask to 109 * NR_CPUS. 110 */ 111 static void __init qcom_smp_init_cpus(void) 112 { 113 unsigned int i, ncores = get_core_count(); 114 115 if (ncores > nr_cpu_ids) { 116 pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", 117 ncores, nr_cpu_ids); 118 ncores = nr_cpu_ids; 119 } 120 121 for (i = 0; i < ncores; i++) 122 set_cpu_possible(i, true); 123 } 124 125 static void __init qcom_smp_prepare_cpus(unsigned int max_cpus) 126 { 127 } 128 129 struct smp_operations qcom_smp_ops __initdata = { 130 .smp_init_cpus = qcom_smp_init_cpus, 131 .smp_prepare_cpus = qcom_smp_prepare_cpus, 132 .smp_secondary_init = qcom_secondary_init, 133 .smp_boot_secondary = qcom_boot_secondary, 134 #ifdef CONFIG_HOTPLUG_CPU 135 .cpu_die = qcom_cpu_die, 136 #endif 137 }; 138