summaryrefslogtreecommitdiff
path: root/arch/arm/mach-mx6/plat_hotplug.c
blob: 20a33329f472e2862912c95c8668ebcde0362371 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/*
 * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/smp.h>
#include <linux/delay.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
#include <linux/io.h>
#include "src-reg.h"
#include <linux/sched.h>
#include <asm/cacheflush.h>

extern unsigned int num_cpu_idle_lock;
void __iomem *src_base = IO_ADDRESS(SRC_BASE_ADDR);

int platform_cpu_kill(unsigned int cpu)
{
	unsigned int val;

	val = jiffies;
	/* wait secondary cpu to die, timeout is 50ms */
	while (__raw_readl(src_base + SRC_GPR1_OFFSET + (8 * cpu) + 4) == 0) {
		if (time_after(jiffies, (unsigned long)(val + HZ / 20))) {
			printk(KERN_WARNING "cpu %d: cpu could not die\n", cpu);
			break;
		}
	}

	/*
	 * we're ready for shutdown now, so do it
	 */
	val = __raw_readl(src_base + SRC_SCR_OFFSET);
	val &= ~(1 << (BP_SRC_SCR_CORES_DBG_RST + cpu));
	val |= (1 << (BP_SRC_SCR_CORE0_RST + cpu));
	__raw_writel(val, src_base + SRC_SCR_OFFSET);

	val = jiffies;
	/* wait secondary cpu reset done, timeout is 10ms */
	while ((__raw_readl(src_base + SRC_SCR_OFFSET) &
		(1 << (BP_SRC_SCR_CORE0_RST + cpu))) != 0) {
		if (time_after(jiffies, (unsigned long)(val + HZ / 100))) {
			printk(KERN_WARNING "cpu %d: cpu reset fail\n", cpu);
			break;
		}
	}

	__raw_writel(0x0, src_base + SRC_GPR1_OFFSET + (8 * cpu) + 4);
	return 1;
}

/*
 * platform-specific code to shutdown a CPU
 * Called with IRQs disabled
 */
void platform_cpu_die(unsigned int cpu)
{
	unsigned int v;
	if (cpu == 0) {
		printk(KERN_ERR "CPU0 can't be disabled!\n");
		return;
	}
	flush_cache_all();
	asm volatile(
	"	mcr p15, 0, %1, c7, c5, 0\n" /* Invalidate I cache */
	"	mcr p15, 0, %1, c7, c10, 4\n" /* DSB */
	/*
	* Turn off coherency
	*/
	"	mrc p15, 0, %0, c1, c0, 1\n" /* Disable SMP in ACTLR */
	"	bic %0, %0, %3\n"
	"	mcr p15, 0, %0, c1, c0, 1\n"
	"	mrc p15, 0, %0, c1, c0, 0\n" /* Disable D cache in SCTLR */
	"	bic %0, %0, %2\n"
	"	mcr p15, 0, %0, c1, c0, 0\n"
	:	"=&r" (v)
	:	"r" (0), "Ir" (CR_C), "Ir" (0x40)
	:	"cc");
	/* Tell cpu0 to kill this core, as this core's cache is
	already disabled, and we want to set a flag to tell cpu0
	to kill this core, so I write the flag to this core's SRC
	parameter register, after cpu0 kill this core, it will
	clear this register. */

	__raw_writel(0x1, src_base + SRC_GPR1_OFFSET + (8 * cpu) + 4);

	for (;;) {
		/*
		 * Execute WFI
		 */
		asm(".word	0xe320f003\n"
		    :
		    :
		    : "memory", "cc");
		printk(KERN_ERR "cpu %d wake up from wfi !!!\n", cpu);
	}
	asm volatile(
	"	mrc	p15, 0, %0, c1, c0, 0\n" /* Enable D cache in SCTLR */
	"	orr	%0, %0, %1\n"
	"	mcr	p15, 0, %0, c1, c0, 0\n"
	"	mrc	p15, 0, %0, c1, c0, 1\n" /* Enable SMP in ACTLR */
	"	orr	%0, %0, %2\n"
	"	mcr	p15, 0, %0, c1, c0, 1\n"
	:	"=&r" (v)
	:	"Ir" (CR_C), "Ir" (0x40)
	:	"cc");
	__raw_writel(0x0, src_base + SRC_GPR1_OFFSET + (8 * cpu) + 4);
}

int platform_cpu_disable(unsigned int cpu)
{
	/*
	 * we don't allow CPU 0 to be shutdown (it is still too special
	 * e.g. clock tick interrupts)
	 */
	*((char *)(&num_cpu_idle_lock) + (u8)cpu) = 0xff;
	return cpu == 0 ? -EPERM : 0;
}