diff options
Diffstat (limited to 'arch/arm/mach-qcom/platsmp.c')
-rw-r--r-- | arch/arm/mach-qcom/platsmp.c | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c new file mode 100644 index 000000000000..9c53ea70550d --- /dev/null +++ b/arch/arm/mach-qcom/platsmp.c | |||
@@ -0,0 +1,137 @@ | |||
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 | }; | ||