~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/arm/mach-exynos/platsmp.c

Version: ~ [ linux-5.8-rc5 ] ~ [ linux-5.7.8 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.51 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.132 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.188 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.230 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.230 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.85 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /* linux/arch/arm/mach-exynos4/platsmp.c
  2  *
  3  * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
  4  *              http://www.samsung.com
  5  *
  6  * Cloned from linux/arch/arm/mach-vexpress/platsmp.c
  7  *
  8  *  Copyright (C) 2002 ARM Ltd.
  9  *  All Rights Reserved
 10  *
 11  * This program is free software; you can redistribute it and/or modify
 12  * it under the terms of the GNU General Public License version 2 as
 13  * published by the Free Software Foundation.
 14 */
 15 
 16 #include <linux/init.h>
 17 #include <linux/errno.h>
 18 #include <linux/delay.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/cacheflush.h>
 25 #include <asm/smp_plat.h>
 26 #include <asm/smp_scu.h>
 27 #include <asm/firmware.h>
 28 
 29 #include <mach/hardware.h>
 30 #include <mach/regs-clock.h>
 31 #include <mach/regs-pmu.h>
 32 
 33 #include <plat/cpu.h>
 34 
 35 #include "common.h"
 36 
 37 extern void exynos4_secondary_startup(void);
 38 
 39 static inline void __iomem *cpu_boot_reg_base(void)
 40 {
 41         if (soc_is_exynos4210() && samsung_rev() == EXYNOS4210_REV_1_1)
 42                 return S5P_INFORM5;
 43         return S5P_VA_SYSRAM;
 44 }
 45 
 46 static inline void __iomem *cpu_boot_reg(int cpu)
 47 {
 48         void __iomem *boot_reg;
 49 
 50         boot_reg = cpu_boot_reg_base();
 51         if (soc_is_exynos4412())
 52                 boot_reg += 4*cpu;
 53         else if (soc_is_exynos5420())
 54                 boot_reg += 4;
 55         return boot_reg;
 56 }
 57 
 58 /*
 59  * Write pen_release in a way that is guaranteed to be visible to all
 60  * observers, irrespective of whether they're taking part in coherency
 61  * or not.  This is necessary for the hotplug code to work reliably.
 62  */
 63 static void write_pen_release(int val)
 64 {
 65         pen_release = val;
 66         smp_wmb();
 67         __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
 68         outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
 69 }
 70 
 71 static void __iomem *scu_base_addr(void)
 72 {
 73         return (void __iomem *)(S5P_VA_SCU);
 74 }
 75 
 76 static DEFINE_SPINLOCK(boot_lock);
 77 
 78 static void exynos_secondary_init(unsigned int cpu)
 79 {
 80         /*
 81          * let the primary processor know we're out of the
 82          * pen, then head off into the C entry point
 83          */
 84         write_pen_release(-1);
 85 
 86         /*
 87          * Synchronise with the boot thread.
 88          */
 89         spin_lock(&boot_lock);
 90         spin_unlock(&boot_lock);
 91 }
 92 
 93 static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle)
 94 {
 95         unsigned long timeout;
 96         unsigned long phys_cpu = cpu_logical_map(cpu);
 97 
 98         /*
 99          * Set synchronisation state between this boot processor
100          * and the secondary one
101          */
102         spin_lock(&boot_lock);
103 
104         /*
105          * The secondary processor is waiting to be released from
106          * the holding pen - release it, then wait for it to flag
107          * that it has been released by resetting pen_release.
108          *
109          * Note that "pen_release" is the hardware CPU ID, whereas
110          * "cpu" is Linux's internal ID.
111          */
112         write_pen_release(phys_cpu);
113 
114         if (!(__raw_readl(S5P_ARM_CORE1_STATUS) & S5P_CORE_LOCAL_PWR_EN)) {
115                 __raw_writel(S5P_CORE_LOCAL_PWR_EN,
116                              S5P_ARM_CORE1_CONFIGURATION);
117 
118                 timeout = 10;
119 
120                 /* wait max 10 ms until cpu1 is on */
121                 while ((__raw_readl(S5P_ARM_CORE1_STATUS)
122                         & S5P_CORE_LOCAL_PWR_EN) != S5P_CORE_LOCAL_PWR_EN) {
123                         if (timeout-- == 0)
124                                 break;
125 
126                         mdelay(1);
127                 }
128 
129                 if (timeout == 0) {
130                         printk(KERN_ERR "cpu1 power enable failed");
131                         spin_unlock(&boot_lock);
132                         return -ETIMEDOUT;
133                 }
134         }
135         /*
136          * Send the secondary CPU a soft interrupt, thereby causing
137          * the boot monitor to read the system wide flags register,
138          * and branch to the address found there.
139          */
140 
141         timeout = jiffies + (1 * HZ);
142         while (time_before(jiffies, timeout)) {
143                 unsigned long boot_addr;
144 
145                 smp_rmb();
146 
147                 boot_addr = virt_to_phys(exynos4_secondary_startup);
148 
149                 /*
150                  * Try to set boot address using firmware first
151                  * and fall back to boot register if it fails.
152                  */
153                 if (call_firmware_op(set_cpu_boot_addr, phys_cpu, boot_addr))
154                         __raw_writel(boot_addr, cpu_boot_reg(phys_cpu));
155 
156                 call_firmware_op(cpu_boot, phys_cpu);
157 
158                 arch_send_wakeup_ipi_mask(cpumask_of(cpu));
159 
160                 if (pen_release == -1)
161                         break;
162 
163                 udelay(10);
164         }
165 
166         /*
167          * now the secondary core is starting up let it run its
168          * calibrations, then wait for it to finish
169          */
170         spin_unlock(&boot_lock);
171 
172         return pen_release != -1 ? -ENOSYS : 0;
173 }
174 
175 /*
176  * Initialise the CPU possible map early - this describes the CPUs
177  * which may be present or become present in the system.
178  */
179 
180 static void __init exynos_smp_init_cpus(void)
181 {
182         void __iomem *scu_base = scu_base_addr();
183         unsigned int i, ncores;
184 
185         if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9)
186                 ncores = scu_base ? scu_get_core_count(scu_base) : 1;
187         else
188                 /*
189                  * CPU Nodes are passed thru DT and set_cpu_possible
190                  * is set by "arm_dt_init_cpu_maps".
191                  */
192                 return;
193 
194         /* sanity check */
195         if (ncores > nr_cpu_ids) {
196                 pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
197                         ncores, nr_cpu_ids);
198                 ncores = nr_cpu_ids;
199         }
200 
201         for (i = 0; i < ncores; i++)
202                 set_cpu_possible(i, true);
203 }
204 
205 static void __init exynos_smp_prepare_cpus(unsigned int max_cpus)
206 {
207         int i;
208 
209         if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9)
210                 scu_enable(scu_base_addr());
211 
212         /*
213          * Write the address of secondary startup into the
214          * system-wide flags register. The boot monitor waits
215          * until it receives a soft interrupt, and then the
216          * secondary CPU branches to this address.
217          *
218          * Try using firmware operation first and fall back to
219          * boot register if it fails.
220          */
221         for (i = 1; i < max_cpus; ++i) {
222                 unsigned long phys_cpu;
223                 unsigned long boot_addr;
224 
225                 phys_cpu = cpu_logical_map(i);
226                 boot_addr = virt_to_phys(exynos4_secondary_startup);
227 
228                 if (call_firmware_op(set_cpu_boot_addr, phys_cpu, boot_addr))
229                         __raw_writel(boot_addr, cpu_boot_reg(phys_cpu));
230         }
231 }
232 
233 struct smp_operations exynos_smp_ops __initdata = {
234         .smp_init_cpus          = exynos_smp_init_cpus,
235         .smp_prepare_cpus       = exynos_smp_prepare_cpus,
236         .smp_secondary_init     = exynos_secondary_init,
237         .smp_boot_secondary     = exynos_boot_secondary,
238 #ifdef CONFIG_HOTPLUG_CPU
239         .cpu_die                = exynos_cpu_die,
240 #endif
241 };
242 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp