1/*
2 *  linux/arch/arm/mach-realview/hotplug.c
3 *
4 *  Copyright (C) 2002 ARM Ltd.
5 *  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#include <linux/kernel.h>
12#include <linux/errno.h>
13#include <linux/smp.h>
14#include <linux/completion.h>
15
16extern volatile int pen_release;
17
18static DECLARE_COMPLETION(cpu_killed);
19
20static inline void cpu_enter_lowpower(void)
21{
22	unsigned int v;
23
24	asm volatile(	"mcr	p15, 0, %1, c7, c14, 0\n"
25	"	mcr	p15, 0, %1, c7, c5, 0\n"
26	"	mcr	p15, 0, %1, c7, c10, 4\n"
27	/*
28	 * Turn off coherency
29	 */
30	"	mrc	p15, 0, %0, c1, c0, 1\n"
31	"	bic	%0, %0, #0x20\n"
32	"	mcr	p15, 0, %0, c1, c0, 1\n"
33	"	mrc	p15, 0, %0, c1, c0, 0\n"
34	"	bic	%0, %0, #0x04\n"
35	"	mcr	p15, 0, %0, c1, c0, 0\n"
36	  : "=&r" (v)
37	  : "r" (0)
38	  : "cc");
39}
40
41static inline void cpu_leave_lowpower(void)
42{
43	unsigned int v;
44
45	asm volatile(	"mrc	p15, 0, %0, c1, c0, 0\n"
46	"	orr	%0, %0, #0x04\n"
47	"	mcr	p15, 0, %0, c1, c0, 0\n"
48	"	mrc	p15, 0, %0, c1, c0, 1\n"
49	"	orr	%0, %0, #0x20\n"
50	"	mcr	p15, 0, %0, c1, c0, 1\n"
51	  : "=&r" (v)
52	  :
53	  : "cc");
54}
55
56static inline void platform_do_lowpower(unsigned int cpu)
57{
58	/*
59	 * there is no power-control hardware on this platform, so all
60	 * we can do is put the core into WFI; this is safe as the calling
61	 * code will have already disabled interrupts
62	 */
63	for (;;) {
64		/*
65		 * here's the WFI
66		 */
67		asm(".word	0xe320f003\n"
68		    :
69		    :
70		    : "memory", "cc");
71
72		if (pen_release == cpu) {
73			/*
74			 * OK, proper wakeup, we're done
75			 */
76			break;
77		}
78
79		/*
80		 * getting here, means that we have come out of WFI without
81		 * having been woken up - this shouldn't happen
82		 *
83		 * The trouble is, letting people know about this is not really
84		 * possible, since we are currently running incoherently, and
85		 * therefore cannot safely call printk() or anything else
86		 */
87#ifdef DEBUG
88		printk("CPU%u: spurious wakeup call\n", cpu);
89#endif
90	}
91}
92
93int platform_cpu_kill(unsigned int cpu)
94{
95	return wait_for_completion_timeout(&cpu_killed, 5000);
96}
97
98/*
99 * platform-specific code to shutdown a CPU
100 *
101 * Called with IRQs disabled
102 */
103void platform_cpu_die(unsigned int cpu)
104{
105#ifdef DEBUG
106	unsigned int this_cpu = hard_smp_processor_id();
107
108	if (cpu != this_cpu) {
109		printk(KERN_CRIT "Eek! platform_cpu_die running on %u, should be %u\n",
110			   this_cpu, cpu);
111		BUG();
112	}
113#endif
114
115	printk(KERN_NOTICE "CPU%u: shutdown\n", cpu);
116	complete(&cpu_killed);
117
118	/*
119	 * we're ready for shutdown now, so do it
120	 */
121	cpu_enter_lowpower();
122	platform_do_lowpower(cpu);
123
124	/*
125	 * bring this CPU back into the world of cache
126	 * coherency, and then restore interrupts
127	 */
128	cpu_leave_lowpower();
129}
130
131int mach_cpu_disable(unsigned int cpu)
132{
133	/*
134	 * we don't allow CPU 0 to be shutdown (it is still too special
135	 * e.g. clock tick interrupts)
136	 */
137	return cpu == 0 ? -EPERM : 0;
138}
139