blob: c9a77fa62744b832a3f1340edd3196303a27c383 [file] [log] [blame]
Kumar Galad5b26db2008-11-19 09:35:56 -06001/*
2 * Author: Andy Fleming <afleming@freescale.com>
3 * Kumar Gala <galak@kernel.crashing.org>
4 *
5 * Copyright 2006-2008 Freescale Semiconductor Inc.
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version.
11 */
12
13#include <linux/stddef.h>
14#include <linux/kernel.h>
15#include <linux/init.h>
16#include <linux/delay.h>
17#include <linux/of.h>
Matthew McClintockf933a412010-07-21 16:14:53 -050018#include <linux/kexec.h>
Kumar Galad5b26db2008-11-19 09:35:56 -060019
20#include <asm/machdep.h>
21#include <asm/pgtable.h>
22#include <asm/page.h>
23#include <asm/mpic.h>
24#include <asm/cacheflush.h>
Kumar Gala563fdd42009-02-11 22:50:42 -060025#include <asm/dbell.h>
Kumar Galad5b26db2008-11-19 09:35:56 -060026
27#include <sysdev/fsl_soc.h>
Matthew McClintockf933a412010-07-21 16:14:53 -050028#include <sysdev/mpic.h>
Kumar Galad5b26db2008-11-19 09:35:56 -060029
Kumar Galad5b26db2008-11-19 09:35:56 -060030extern void __early_start(void);
31
32#define BOOT_ENTRY_ADDR_UPPER 0
33#define BOOT_ENTRY_ADDR_LOWER 1
34#define BOOT_ENTRY_R3_UPPER 2
35#define BOOT_ENTRY_R3_LOWER 3
36#define BOOT_ENTRY_RESV 4
37#define BOOT_ENTRY_PIR 5
38#define BOOT_ENTRY_R6_UPPER 6
39#define BOOT_ENTRY_R6_LOWER 7
40#define NUM_BOOT_ENTRY 8
41#define SIZE_BOOT_ENTRY (NUM_BOOT_ENTRY * sizeof(u32))
42
43static void __init
44smp_85xx_kick_cpu(int nr)
45{
46 unsigned long flags;
47 const u64 *cpu_rel_addr;
48 __iomem u32 *bptr_vaddr;
49 struct device_node *np;
50 int n = 0;
Peter Tyserd1d47ec2009-12-18 16:50:37 -060051 int ioremappable;
Kumar Galad5b26db2008-11-19 09:35:56 -060052
53 WARN_ON (nr < 0 || nr >= NR_CPUS);
54
55 pr_debug("smp_85xx_kick_cpu: kick CPU #%d\n", nr);
56
Kumar Galad5b26db2008-11-19 09:35:56 -060057 np = of_get_cpu_node(nr, NULL);
58 cpu_rel_addr = of_get_property(np, "cpu-release-addr", NULL);
59
60 if (cpu_rel_addr == NULL) {
61 printk(KERN_ERR "No cpu-release-addr for cpu %d\n", nr);
Kumar Galad5b26db2008-11-19 09:35:56 -060062 return;
63 }
64
Peter Tyserd1d47ec2009-12-18 16:50:37 -060065 /*
66 * A secondary core could be in a spinloop in the bootpage
67 * (0xfffff000), somewhere in highmem, or somewhere in lowmem.
68 * The bootpage and highmem can be accessed via ioremap(), but
69 * we need to directly access the spinloop if its in lowmem.
70 */
71 ioremappable = *cpu_rel_addr > virt_to_phys(high_memory);
72
Kumar Galad5b26db2008-11-19 09:35:56 -060073 /* Map the spin table */
Peter Tyserd1d47ec2009-12-18 16:50:37 -060074 if (ioremappable)
75 bptr_vaddr = ioremap(*cpu_rel_addr, SIZE_BOOT_ENTRY);
76 else
77 bptr_vaddr = phys_to_virt(*cpu_rel_addr);
Kumar Galad5b26db2008-11-19 09:35:56 -060078
Kumar Galacb1ffb6202009-06-19 03:30:42 -050079 local_irq_save(flags);
80
Kumar Galad5b26db2008-11-19 09:35:56 -060081 out_be32(bptr_vaddr + BOOT_ENTRY_PIR, nr);
Kumar Gala5b8544c2010-10-08 10:37:31 -050082#ifdef CONFIG_PPC32
Kumar Galad5b26db2008-11-19 09:35:56 -060083 out_be32(bptr_vaddr + BOOT_ENTRY_ADDR_LOWER, __pa(__early_start));
84
Peter Tyserd1d47ec2009-12-18 16:50:37 -060085 if (!ioremappable)
86 flush_dcache_range((ulong)bptr_vaddr,
87 (ulong)(bptr_vaddr + SIZE_BOOT_ENTRY));
88
Kumar Galad5b26db2008-11-19 09:35:56 -060089 /* Wait a bit for the CPU to ack. */
90 while ((__secondary_hold_acknowledge != nr) && (++n < 1000))
91 mdelay(1);
Kumar Gala5b8544c2010-10-08 10:37:31 -050092#else
93 out_be64((u64 *)(bptr_vaddr + BOOT_ENTRY_ADDR_UPPER),
94 __pa((u64)*((unsigned long long *) generic_secondary_smp_init)));
95
96 smp_generic_kick_cpu(nr);
97#endif
Kumar Galad5b26db2008-11-19 09:35:56 -060098
Kumar Galad5b26db2008-11-19 09:35:56 -060099 local_irq_restore(flags);
100
Peter Tyserd1d47ec2009-12-18 16:50:37 -0600101 if (ioremappable)
102 iounmap(bptr_vaddr);
Kumar Galacb1ffb6202009-06-19 03:30:42 -0500103
Kumar Galad5b26db2008-11-19 09:35:56 -0600104 pr_debug("waited %d msecs for CPU #%d.\n", n, nr);
105}
106
107static void __init
Kumar Gala563fdd42009-02-11 22:50:42 -0600108smp_85xx_setup_cpu(int cpu_nr)
109{
110 mpic_setup_this_cpu();
Benjamin Herrenschmidtb9f1cd72010-07-09 15:29:53 +1000111 if (cpu_has_feature(CPU_FTR_DBELL))
112 doorbell_setup_this_cpu();
Kumar Gala563fdd42009-02-11 22:50:42 -0600113}
114
Kumar Galad5b26db2008-11-19 09:35:56 -0600115struct smp_ops_t smp_85xx_ops = {
Kumar Galad5b26db2008-11-19 09:35:56 -0600116 .kick_cpu = smp_85xx_kick_cpu,
Matthew McClintockf933a412010-07-21 16:14:53 -0500117#ifdef CONFIG_KEXEC
118 .give_timebase = smp_generic_give_timebase,
119 .take_timebase = smp_generic_take_timebase,
120#endif
Kumar Galad5b26db2008-11-19 09:35:56 -0600121};
122
Matthew McClintockf933a412010-07-21 16:14:53 -0500123#ifdef CONFIG_KEXEC
Matthew McClintock5d692962010-09-16 17:58:25 -0500124atomic_t kexec_down_cpus = ATOMIC_INIT(0);
Matthew McClintockf933a412010-07-21 16:14:53 -0500125
126void mpc85xx_smp_kexec_cpu_down(int crash_shutdown, int secondary)
127{
Matthew McClintock5d692962010-09-16 17:58:25 -0500128 local_irq_disable();
Matthew McClintockf933a412010-07-21 16:14:53 -0500129
Matthew McClintock5d692962010-09-16 17:58:25 -0500130 if (secondary) {
131 atomic_inc(&kexec_down_cpus);
132 /* loop forever */
Matthew McClintockf933a412010-07-21 16:14:53 -0500133 while (1);
134 }
135}
136
137static void mpc85xx_smp_kexec_down(void *arg)
138{
139 if (ppc_md.kexec_cpu_down)
140 ppc_md.kexec_cpu_down(0,1);
141}
142
143static void mpc85xx_smp_machine_kexec(struct kimage *image)
144{
Matthew McClintock5d692962010-09-16 17:58:25 -0500145 int timeout = INT_MAX;
146 int i, num_cpus = num_present_cpus();
Matthew McClintockf933a412010-07-21 16:14:53 -0500147
Matthew McClintockf933a412010-07-21 16:14:53 -0500148
Matthew McClintock5d692962010-09-16 17:58:25 -0500149 if (image->type == KEXEC_TYPE_DEFAULT)
150 smp_call_function(mpc85xx_smp_kexec_down, NULL, 0);
Matthew McClintockf933a412010-07-21 16:14:53 -0500151
Matthew McClintock5d692962010-09-16 17:58:25 -0500152 while ( (atomic_read(&kexec_down_cpus) != (num_cpus - 1)) &&
Matthew McClintockf933a412010-07-21 16:14:53 -0500153 ( timeout > 0 ) )
154 {
155 timeout--;
156 }
157
158 if ( !timeout )
159 printk(KERN_ERR "Unable to bring down secondary cpu(s)");
160
Matthew McClintock5d692962010-09-16 17:58:25 -0500161 for (i = 0; i < num_cpus; i++)
Matthew McClintockf933a412010-07-21 16:14:53 -0500162 {
163 if ( i == smp_processor_id() ) continue;
164 mpic_reset_core(i);
165 }
166
167 default_machine_kexec(image);
168}
169#endif /* CONFIG_KEXEC */
170
Kumar Gala563fdd42009-02-11 22:50:42 -0600171void __init mpc85xx_smp_init(void)
172{
173 struct device_node *np;
174
Kumar Gala563fdd42009-02-11 22:50:42 -0600175 np = of_find_node_by_type(NULL, "open-pic");
176 if (np) {
177 smp_85xx_ops.probe = smp_mpic_probe;
178 smp_85xx_ops.setup_cpu = smp_85xx_setup_cpu;
179 smp_85xx_ops.message_pass = smp_mpic_message_pass;
Kumar Gala563fdd42009-02-11 22:50:42 -0600180 }
181
182 if (cpu_has_feature(CPU_FTR_DBELL))
Benjamin Herrenschmidtb9f1cd72010-07-09 15:29:53 +1000183 smp_85xx_ops.message_pass = doorbell_message_pass;
Kumar Gala563fdd42009-02-11 22:50:42 -0600184
185 BUG_ON(!smp_85xx_ops.message_pass);
186
Kumar Galad5b26db2008-11-19 09:35:56 -0600187 smp_ops = &smp_85xx_ops;
Matthew McClintockf933a412010-07-21 16:14:53 -0500188
189#ifdef CONFIG_KEXEC
190 ppc_md.kexec_cpu_down = mpc85xx_smp_kexec_cpu_down;
191 ppc_md.machine_kexec = mpc85xx_smp_machine_kexec;
192#endif
Kumar Galad5b26db2008-11-19 09:35:56 -0600193}