diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-20 17:30:33 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-20 17:30:33 -0500 |
commit | 79a69d342d71b2b4eafdf51e2451606cfe380a44 (patch) | |
tree | 7246dbcb872f416b3e27a8020106cf5098cb30f9 /arch/arm64/kernel | |
parent | 6db167dfc013b0e114c81077ac091ba26a69f4ed (diff) | |
parent | ec45d1cfd3cb65121fc52f39efc17d832f4f7b91 (diff) |
Merge tag 'arm64-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cmarinas/linux-aarch64
Pull arm64 patches from Catalin Marinas:
- SMP support for the PSCI booting protocol (power state coordination
interface).
- Simple earlyprintk support.
- Platform devices populated by default from the DT (SoC-agnostic).
- CONTEXTIDR support (used by external trace tools).
* tag 'arm64-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cmarinas/linux-aarch64:
arm64: mm: update CONTEXTIDR register to contain PID of current process
arm64: atomics: fix grossly inconsistent asm constraints for exclusives
arm64: compat: use compat_uptr_t type for compat_ucontext.uc_link
arm64: Select ARCH_WANT_FRAME_POINTERS
arm64: Add kvm_para.h and xor.h generic headers
arm64: SMP: enable PSCI boot method
arm64: psci: add support for PSCI invocations from the kernel
arm64: SMP: rework the SMP code to be enabling method agnostic
arm64: perf: add guest vs host discrimination
arm64: add COMPAT_PSR_*_BIT flags
arm64: Add simple earlyprintk support
arm64: Populate the platform devices
Diffstat (limited to 'arch/arm64/kernel')
-rw-r--r-- | arch/arm64/kernel/Makefile | 5 | ||||
-rw-r--r-- | arch/arm64/kernel/early_printk.c | 118 | ||||
-rw-r--r-- | arch/arm64/kernel/head.S | 12 | ||||
-rw-r--r-- | arch/arm64/kernel/perf_event.c | 37 | ||||
-rw-r--r-- | arch/arm64/kernel/process.c | 4 | ||||
-rw-r--r-- | arch/arm64/kernel/psci.c | 211 | ||||
-rw-r--r-- | arch/arm64/kernel/setup.c | 11 | ||||
-rw-r--r-- | arch/arm64/kernel/signal32.c | 4 | ||||
-rw-r--r-- | arch/arm64/kernel/smp.c | 69 | ||||
-rw-r--r-- | arch/arm64/kernel/smp_psci.c | 52 | ||||
-rw-r--r-- | arch/arm64/kernel/smp_spin_table.c | 66 |
11 files changed, 560 insertions, 29 deletions
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 74239c31e25a..7b4b564961d4 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile | |||
@@ -9,14 +9,15 @@ AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET) | |||
9 | arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ | 9 | arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ |
10 | entry-fpsimd.o process.o ptrace.o setup.o signal.o \ | 10 | entry-fpsimd.o process.o ptrace.o setup.o signal.o \ |
11 | sys.o stacktrace.o time.o traps.o io.o vdso.o \ | 11 | sys.o stacktrace.o time.o traps.o io.o vdso.o \ |
12 | hyp-stub.o | 12 | hyp-stub.o psci.o |
13 | 13 | ||
14 | arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ | 14 | arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ |
15 | sys_compat.o | 15 | sys_compat.o |
16 | arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o | 16 | arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o |
17 | arm64-obj-$(CONFIG_SMP) += smp.o | 17 | arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o smp_psci.o |
18 | arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o | 18 | arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o |
19 | arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o | 19 | arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o |
20 | arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o | ||
20 | 21 | ||
21 | obj-y += $(arm64-obj-y) vdso/ | 22 | obj-y += $(arm64-obj-y) vdso/ |
22 | obj-m += $(arm64-obj-m) | 23 | obj-m += $(arm64-obj-m) |
diff --git a/arch/arm64/kernel/early_printk.c b/arch/arm64/kernel/early_printk.c new file mode 100644 index 000000000000..7e320a2edb9b --- /dev/null +++ b/arch/arm64/kernel/early_printk.c | |||
@@ -0,0 +1,118 @@ | |||
1 | /* | ||
2 | * Earlyprintk support. | ||
3 | * | ||
4 | * Copyright (C) 2012 ARM Ltd. | ||
5 | * Author: Catalin Marinas <catalin.marinas@arm.com> | ||
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 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
18 | */ | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/console.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/string.h> | ||
23 | #include <linux/mm.h> | ||
24 | #include <linux/io.h> | ||
25 | |||
26 | #include <linux/amba/serial.h> | ||
27 | |||
28 | static void __iomem *early_base; | ||
29 | static void (*printch)(char ch); | ||
30 | |||
31 | /* | ||
32 | * PL011 single character TX. | ||
33 | */ | ||
34 | static void pl011_printch(char ch) | ||
35 | { | ||
36 | while (readl_relaxed(early_base + UART01x_FR) & UART01x_FR_TXFF) | ||
37 | ; | ||
38 | writeb_relaxed(ch, early_base + UART01x_DR); | ||
39 | while (readl_relaxed(early_base + UART01x_FR) & UART01x_FR_BUSY) | ||
40 | ; | ||
41 | } | ||
42 | |||
43 | struct earlycon_match { | ||
44 | const char *name; | ||
45 | void (*printch)(char ch); | ||
46 | }; | ||
47 | |||
48 | static const struct earlycon_match earlycon_match[] __initconst = { | ||
49 | { .name = "pl011", .printch = pl011_printch, }, | ||
50 | {} | ||
51 | }; | ||
52 | |||
53 | static void early_write(struct console *con, const char *s, unsigned n) | ||
54 | { | ||
55 | while (n-- > 0) { | ||
56 | if (*s == '\n') | ||
57 | printch('\r'); | ||
58 | printch(*s); | ||
59 | s++; | ||
60 | } | ||
61 | } | ||
62 | |||
63 | static struct console early_console = { | ||
64 | .name = "earlycon", | ||
65 | .write = early_write, | ||
66 | .flags = CON_PRINTBUFFER | CON_BOOT, | ||
67 | .index = -1, | ||
68 | }; | ||
69 | |||
70 | /* | ||
71 | * Parse earlyprintk=... parameter in the format: | ||
72 | * | ||
73 | * <name>[,<addr>][,<options>] | ||
74 | * | ||
75 | * and register the early console. It is assumed that the UART has been | ||
76 | * initialised by the bootloader already. | ||
77 | */ | ||
78 | static int __init setup_early_printk(char *buf) | ||
79 | { | ||
80 | const struct earlycon_match *match = earlycon_match; | ||
81 | phys_addr_t paddr = 0; | ||
82 | |||
83 | if (!buf) { | ||
84 | pr_warning("No earlyprintk arguments passed.\n"); | ||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | while (match->name) { | ||
89 | size_t len = strlen(match->name); | ||
90 | if (!strncmp(buf, match->name, len)) { | ||
91 | buf += len; | ||
92 | break; | ||
93 | } | ||
94 | match++; | ||
95 | } | ||
96 | if (!match->name) { | ||
97 | pr_warning("Unknown earlyprintk arguments: %s\n", buf); | ||
98 | return 0; | ||
99 | } | ||
100 | |||
101 | /* I/O address */ | ||
102 | if (!strncmp(buf, ",0x", 3)) { | ||
103 | char *e; | ||
104 | paddr = simple_strtoul(buf + 1, &e, 16); | ||
105 | buf = e; | ||
106 | } | ||
107 | /* no options parsing yet */ | ||
108 | |||
109 | if (paddr) | ||
110 | early_base = early_io_map(paddr, EARLYCON_IOBASE); | ||
111 | |||
112 | printch = match->printch; | ||
113 | register_console(&early_console); | ||
114 | |||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | early_param("earlyprintk", setup_early_printk); | ||
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S index 368ad1f7c36c..0a0a49756826 100644 --- a/arch/arm64/kernel/head.S +++ b/arch/arm64/kernel/head.S | |||
@@ -82,10 +82,8 @@ | |||
82 | 82 | ||
83 | #ifdef CONFIG_ARM64_64K_PAGES | 83 | #ifdef CONFIG_ARM64_64K_PAGES |
84 | #define MM_MMUFLAGS PTE_ATTRINDX(MT_NORMAL) | PTE_FLAGS | 84 | #define MM_MMUFLAGS PTE_ATTRINDX(MT_NORMAL) | PTE_FLAGS |
85 | #define IO_MMUFLAGS PTE_ATTRINDX(MT_DEVICE_nGnRE) | PTE_XN | PTE_FLAGS | ||
86 | #else | 85 | #else |
87 | #define MM_MMUFLAGS PMD_ATTRINDX(MT_NORMAL) | PMD_FLAGS | 86 | #define MM_MMUFLAGS PMD_ATTRINDX(MT_NORMAL) | PMD_FLAGS |
88 | #define IO_MMUFLAGS PMD_ATTRINDX(MT_DEVICE_nGnRE) | PMD_SECT_XN | PMD_FLAGS | ||
89 | #endif | 87 | #endif |
90 | 88 | ||
91 | /* | 89 | /* |
@@ -368,6 +366,7 @@ ENDPROC(__calc_phys_offset) | |||
368 | * - identity mapping to enable the MMU (low address, TTBR0) | 366 | * - identity mapping to enable the MMU (low address, TTBR0) |
369 | * - first few MB of the kernel linear mapping to jump to once the MMU has | 367 | * - first few MB of the kernel linear mapping to jump to once the MMU has |
370 | * been enabled, including the FDT blob (TTBR1) | 368 | * been enabled, including the FDT blob (TTBR1) |
369 | * - UART mapping if CONFIG_EARLY_PRINTK is enabled (TTBR1) | ||
371 | */ | 370 | */ |
372 | __create_page_tables: | 371 | __create_page_tables: |
373 | pgtbl x25, x26, x24 // idmap_pg_dir and swapper_pg_dir addresses | 372 | pgtbl x25, x26, x24 // idmap_pg_dir and swapper_pg_dir addresses |
@@ -420,6 +419,15 @@ __create_page_tables: | |||
420 | sub x6, x6, #1 // inclusive range | 419 | sub x6, x6, #1 // inclusive range |
421 | create_block_map x0, x7, x3, x5, x6 | 420 | create_block_map x0, x7, x3, x5, x6 |
422 | 1: | 421 | 1: |
422 | #ifdef CONFIG_EARLY_PRINTK | ||
423 | /* | ||
424 | * Create the pgd entry for the UART mapping. The full mapping is done | ||
425 | * later based earlyprintk kernel parameter. | ||
426 | */ | ||
427 | ldr x5, =EARLYCON_IOBASE // UART virtual address | ||
428 | add x0, x26, #2 * PAGE_SIZE // section table address | ||
429 | create_pgd_entry x26, x0, x5, x6, x7 | ||
430 | #endif | ||
423 | ret | 431 | ret |
424 | ENDPROC(__create_page_tables) | 432 | ENDPROC(__create_page_tables) |
425 | .ltorg | 433 | .ltorg |
diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c index f7073c7b1ca9..1e49e5eb81e9 100644 --- a/arch/arm64/kernel/perf_event.c +++ b/arch/arm64/kernel/perf_event.c | |||
@@ -1331,6 +1331,11 @@ void perf_callchain_user(struct perf_callchain_entry *entry, | |||
1331 | { | 1331 | { |
1332 | struct frame_tail __user *tail; | 1332 | struct frame_tail __user *tail; |
1333 | 1333 | ||
1334 | if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { | ||
1335 | /* We don't support guest os callchain now */ | ||
1336 | return; | ||
1337 | } | ||
1338 | |||
1334 | tail = (struct frame_tail __user *)regs->regs[29]; | 1339 | tail = (struct frame_tail __user *)regs->regs[29]; |
1335 | 1340 | ||
1336 | while (entry->nr < PERF_MAX_STACK_DEPTH && | 1341 | while (entry->nr < PERF_MAX_STACK_DEPTH && |
@@ -1355,8 +1360,40 @@ void perf_callchain_kernel(struct perf_callchain_entry *entry, | |||
1355 | { | 1360 | { |
1356 | struct stackframe frame; | 1361 | struct stackframe frame; |
1357 | 1362 | ||
1363 | if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { | ||
1364 | /* We don't support guest os callchain now */ | ||
1365 | return; | ||
1366 | } | ||
1367 | |||
1358 | frame.fp = regs->regs[29]; | 1368 | frame.fp = regs->regs[29]; |
1359 | frame.sp = regs->sp; | 1369 | frame.sp = regs->sp; |
1360 | frame.pc = regs->pc; | 1370 | frame.pc = regs->pc; |
1361 | walk_stackframe(&frame, callchain_trace, entry); | 1371 | walk_stackframe(&frame, callchain_trace, entry); |
1362 | } | 1372 | } |
1373 | |||
1374 | unsigned long perf_instruction_pointer(struct pt_regs *regs) | ||
1375 | { | ||
1376 | if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) | ||
1377 | return perf_guest_cbs->get_guest_ip(); | ||
1378 | |||
1379 | return instruction_pointer(regs); | ||
1380 | } | ||
1381 | |||
1382 | unsigned long perf_misc_flags(struct pt_regs *regs) | ||
1383 | { | ||
1384 | int misc = 0; | ||
1385 | |||
1386 | if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { | ||
1387 | if (perf_guest_cbs->is_user_mode()) | ||
1388 | misc |= PERF_RECORD_MISC_GUEST_USER; | ||
1389 | else | ||
1390 | misc |= PERF_RECORD_MISC_GUEST_KERNEL; | ||
1391 | } else { | ||
1392 | if (user_mode(regs)) | ||
1393 | misc |= PERF_RECORD_MISC_USER; | ||
1394 | else | ||
1395 | misc |= PERF_RECORD_MISC_KERNEL; | ||
1396 | } | ||
1397 | |||
1398 | return misc; | ||
1399 | } | ||
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index c7002d40a9b0..0337cdb0667b 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c | |||
@@ -45,9 +45,10 @@ | |||
45 | 45 | ||
46 | #include <asm/compat.h> | 46 | #include <asm/compat.h> |
47 | #include <asm/cacheflush.h> | 47 | #include <asm/cacheflush.h> |
48 | #include <asm/fpsimd.h> | ||
49 | #include <asm/mmu_context.h> | ||
48 | #include <asm/processor.h> | 50 | #include <asm/processor.h> |
49 | #include <asm/stacktrace.h> | 51 | #include <asm/stacktrace.h> |
50 | #include <asm/fpsimd.h> | ||
51 | 52 | ||
52 | static void setup_restart(void) | 53 | static void setup_restart(void) |
53 | { | 54 | { |
@@ -314,6 +315,7 @@ struct task_struct *__switch_to(struct task_struct *prev, | |||
314 | /* the actual thread switch */ | 315 | /* the actual thread switch */ |
315 | last = cpu_switch_to(prev, next); | 316 | last = cpu_switch_to(prev, next); |
316 | 317 | ||
318 | contextidr_thread_switch(next); | ||
317 | return last; | 319 | return last; |
318 | } | 320 | } |
319 | 321 | ||
diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c new file mode 100644 index 000000000000..14f73c445ff5 --- /dev/null +++ b/arch/arm64/kernel/psci.c | |||
@@ -0,0 +1,211 @@ | |||
1 | /* | ||
2 | * This program is free software; you can redistribute it and/or modify | ||
3 | * it under the terms of the GNU General Public License version 2 as | ||
4 | * published by the Free Software Foundation. | ||
5 | * | ||
6 | * This program is distributed in the hope that it will be useful, | ||
7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
9 | * GNU General Public License for more details. | ||
10 | * | ||
11 | * Copyright (C) 2013 ARM Limited | ||
12 | * | ||
13 | * Author: Will Deacon <will.deacon@arm.com> | ||
14 | */ | ||
15 | |||
16 | #define pr_fmt(fmt) "psci: " fmt | ||
17 | |||
18 | #include <linux/init.h> | ||
19 | #include <linux/of.h> | ||
20 | |||
21 | #include <asm/compiler.h> | ||
22 | #include <asm/errno.h> | ||
23 | #include <asm/psci.h> | ||
24 | |||
25 | struct psci_operations psci_ops; | ||
26 | |||
27 | static int (*invoke_psci_fn)(u64, u64, u64, u64); | ||
28 | |||
29 | enum psci_function { | ||
30 | PSCI_FN_CPU_SUSPEND, | ||
31 | PSCI_FN_CPU_ON, | ||
32 | PSCI_FN_CPU_OFF, | ||
33 | PSCI_FN_MIGRATE, | ||
34 | PSCI_FN_MAX, | ||
35 | }; | ||
36 | |||
37 | static u32 psci_function_id[PSCI_FN_MAX]; | ||
38 | |||
39 | #define PSCI_RET_SUCCESS 0 | ||
40 | #define PSCI_RET_EOPNOTSUPP -1 | ||
41 | #define PSCI_RET_EINVAL -2 | ||
42 | #define PSCI_RET_EPERM -3 | ||
43 | |||
44 | static int psci_to_linux_errno(int errno) | ||
45 | { | ||
46 | switch (errno) { | ||
47 | case PSCI_RET_SUCCESS: | ||
48 | return 0; | ||
49 | case PSCI_RET_EOPNOTSUPP: | ||
50 | return -EOPNOTSUPP; | ||
51 | case PSCI_RET_EINVAL: | ||
52 | return -EINVAL; | ||
53 | case PSCI_RET_EPERM: | ||
54 | return -EPERM; | ||
55 | }; | ||
56 | |||
57 | return -EINVAL; | ||
58 | } | ||
59 | |||
60 | #define PSCI_POWER_STATE_ID_MASK 0xffff | ||
61 | #define PSCI_POWER_STATE_ID_SHIFT 0 | ||
62 | #define PSCI_POWER_STATE_TYPE_MASK 0x1 | ||
63 | #define PSCI_POWER_STATE_TYPE_SHIFT 16 | ||
64 | #define PSCI_POWER_STATE_AFFL_MASK 0x3 | ||
65 | #define PSCI_POWER_STATE_AFFL_SHIFT 24 | ||
66 | |||
67 | static u32 psci_power_state_pack(struct psci_power_state state) | ||
68 | { | ||
69 | return ((state.id & PSCI_POWER_STATE_ID_MASK) | ||
70 | << PSCI_POWER_STATE_ID_SHIFT) | | ||
71 | ((state.type & PSCI_POWER_STATE_TYPE_MASK) | ||
72 | << PSCI_POWER_STATE_TYPE_SHIFT) | | ||
73 | ((state.affinity_level & PSCI_POWER_STATE_AFFL_MASK) | ||
74 | << PSCI_POWER_STATE_AFFL_SHIFT); | ||
75 | } | ||
76 | |||
77 | /* | ||
78 | * The following two functions are invoked via the invoke_psci_fn pointer | ||
79 | * and will not be inlined, allowing us to piggyback on the AAPCS. | ||
80 | */ | ||
81 | static noinline int __invoke_psci_fn_hvc(u64 function_id, u64 arg0, u64 arg1, | ||
82 | u64 arg2) | ||
83 | { | ||
84 | asm volatile( | ||
85 | __asmeq("%0", "x0") | ||
86 | __asmeq("%1", "x1") | ||
87 | __asmeq("%2", "x2") | ||
88 | __asmeq("%3", "x3") | ||
89 | "hvc #0\n" | ||
90 | : "+r" (function_id) | ||
91 | : "r" (arg0), "r" (arg1), "r" (arg2)); | ||
92 | |||
93 | return function_id; | ||
94 | } | ||
95 | |||
96 | static noinline int __invoke_psci_fn_smc(u64 function_id, u64 arg0, u64 arg1, | ||
97 | u64 arg2) | ||
98 | { | ||
99 | asm volatile( | ||
100 | __asmeq("%0", "x0") | ||
101 | __asmeq("%1", "x1") | ||
102 | __asmeq("%2", "x2") | ||
103 | __asmeq("%3", "x3") | ||
104 | "smc #0\n" | ||
105 | : "+r" (function_id) | ||
106 | : "r" (arg0), "r" (arg1), "r" (arg2)); | ||
107 | |||
108 | return function_id; | ||
109 | } | ||
110 | |||
111 | static int psci_cpu_suspend(struct psci_power_state state, | ||
112 | unsigned long entry_point) | ||
113 | { | ||
114 | int err; | ||
115 | u32 fn, power_state; | ||
116 | |||
117 | fn = psci_function_id[PSCI_FN_CPU_SUSPEND]; | ||
118 | power_state = psci_power_state_pack(state); | ||
119 | err = invoke_psci_fn(fn, power_state, entry_point, 0); | ||
120 | return psci_to_linux_errno(err); | ||
121 | } | ||
122 | |||
123 | static int psci_cpu_off(struct psci_power_state state) | ||
124 | { | ||
125 | int err; | ||
126 | u32 fn, power_state; | ||
127 | |||
128 | fn = psci_function_id[PSCI_FN_CPU_OFF]; | ||
129 | power_state = psci_power_state_pack(state); | ||
130 | err = invoke_psci_fn(fn, power_state, 0, 0); | ||
131 | return psci_to_linux_errno(err); | ||
132 | } | ||
133 | |||
134 | static int psci_cpu_on(unsigned long cpuid, unsigned long entry_point) | ||
135 | { | ||
136 | int err; | ||
137 | u32 fn; | ||
138 | |||
139 | fn = psci_function_id[PSCI_FN_CPU_ON]; | ||
140 | err = invoke_psci_fn(fn, cpuid, entry_point, 0); | ||
141 | return psci_to_linux_errno(err); | ||
142 | } | ||
143 | |||
144 | static int psci_migrate(unsigned long cpuid) | ||
145 | { | ||
146 | int err; | ||
147 | u32 fn; | ||
148 | |||
149 | fn = psci_function_id[PSCI_FN_MIGRATE]; | ||
150 | err = invoke_psci_fn(fn, cpuid, 0, 0); | ||
151 | return psci_to_linux_errno(err); | ||
152 | } | ||
153 | |||
154 | static const struct of_device_id psci_of_match[] __initconst = { | ||
155 | { .compatible = "arm,psci", }, | ||
156 | {}, | ||
157 | }; | ||
158 | |||
159 | int __init psci_init(void) | ||
160 | { | ||
161 | struct device_node *np; | ||
162 | const char *method; | ||
163 | u32 id; | ||
164 | int err = 0; | ||
165 | |||
166 | np = of_find_matching_node(NULL, psci_of_match); | ||
167 | if (!np) | ||
168 | return -ENODEV; | ||
169 | |||
170 | pr_info("probing function IDs from device-tree\n"); | ||
171 | |||
172 | if (of_property_read_string(np, "method", &method)) { | ||
173 | pr_warning("missing \"method\" property\n"); | ||
174 | err = -ENXIO; | ||
175 | goto out_put_node; | ||
176 | } | ||
177 | |||
178 | if (!strcmp("hvc", method)) { | ||
179 | invoke_psci_fn = __invoke_psci_fn_hvc; | ||
180 | } else if (!strcmp("smc", method)) { | ||
181 | invoke_psci_fn = __invoke_psci_fn_smc; | ||
182 | } else { | ||
183 | pr_warning("invalid \"method\" property: %s\n", method); | ||
184 | err = -EINVAL; | ||
185 | goto out_put_node; | ||
186 | } | ||
187 | |||
188 | if (!of_property_read_u32(np, "cpu_suspend", &id)) { | ||
189 | psci_function_id[PSCI_FN_CPU_SUSPEND] = id; | ||
190 | psci_ops.cpu_suspend = psci_cpu_suspend; | ||
191 | } | ||
192 | |||
193 | if (!of_property_read_u32(np, "cpu_off", &id)) { | ||
194 | psci_function_id[PSCI_FN_CPU_OFF] = id; | ||
195 | psci_ops.cpu_off = psci_cpu_off; | ||
196 | } | ||
197 | |||
198 | if (!of_property_read_u32(np, "cpu_on", &id)) { | ||
199 | psci_function_id[PSCI_FN_CPU_ON] = id; | ||
200 | psci_ops.cpu_on = psci_cpu_on; | ||
201 | } | ||
202 | |||
203 | if (!of_property_read_u32(np, "migrate", &id)) { | ||
204 | psci_function_id[PSCI_FN_MIGRATE] = id; | ||
205 | psci_ops.migrate = psci_migrate; | ||
206 | } | ||
207 | |||
208 | out_put_node: | ||
209 | of_node_put(np); | ||
210 | return err; | ||
211 | } | ||
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index 7665a9bfdb1e..113db863f832 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c | |||
@@ -39,6 +39,7 @@ | |||
39 | #include <linux/proc_fs.h> | 39 | #include <linux/proc_fs.h> |
40 | #include <linux/memblock.h> | 40 | #include <linux/memblock.h> |
41 | #include <linux/of_fdt.h> | 41 | #include <linux/of_fdt.h> |
42 | #include <linux/of_platform.h> | ||
42 | 43 | ||
43 | #include <asm/cputype.h> | 44 | #include <asm/cputype.h> |
44 | #include <asm/elf.h> | 45 | #include <asm/elf.h> |
@@ -49,6 +50,7 @@ | |||
49 | #include <asm/tlbflush.h> | 50 | #include <asm/tlbflush.h> |
50 | #include <asm/traps.h> | 51 | #include <asm/traps.h> |
51 | #include <asm/memblock.h> | 52 | #include <asm/memblock.h> |
53 | #include <asm/psci.h> | ||
52 | 54 | ||
53 | unsigned int processor_id; | 55 | unsigned int processor_id; |
54 | EXPORT_SYMBOL(processor_id); | 56 | EXPORT_SYMBOL(processor_id); |
@@ -260,6 +262,8 @@ void __init setup_arch(char **cmdline_p) | |||
260 | 262 | ||
261 | unflatten_device_tree(); | 263 | unflatten_device_tree(); |
262 | 264 | ||
265 | psci_init(); | ||
266 | |||
263 | #ifdef CONFIG_SMP | 267 | #ifdef CONFIG_SMP |
264 | smp_init_cpus(); | 268 | smp_init_cpus(); |
265 | #endif | 269 | #endif |
@@ -289,6 +293,13 @@ static int __init topology_init(void) | |||
289 | } | 293 | } |
290 | subsys_initcall(topology_init); | 294 | subsys_initcall(topology_init); |
291 | 295 | ||
296 | static int __init arm64_device_probe(void) | ||
297 | { | ||
298 | of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); | ||
299 | return 0; | ||
300 | } | ||
301 | device_initcall(arm64_device_probe); | ||
302 | |||
292 | static const char *hwcap_str[] = { | 303 | static const char *hwcap_str[] = { |
293 | "fp", | 304 | "fp", |
294 | "asimd", | 305 | "asimd", |
diff --git a/arch/arm64/kernel/signal32.c b/arch/arm64/kernel/signal32.c index a4db3d22aac4..41db148a7eb9 100644 --- a/arch/arm64/kernel/signal32.c +++ b/arch/arm64/kernel/signal32.c | |||
@@ -76,7 +76,7 @@ struct compat_sigcontext { | |||
76 | 76 | ||
77 | struct compat_ucontext { | 77 | struct compat_ucontext { |
78 | compat_ulong_t uc_flags; | 78 | compat_ulong_t uc_flags; |
79 | struct compat_ucontext *uc_link; | 79 | compat_uptr_t uc_link; |
80 | compat_stack_t uc_stack; | 80 | compat_stack_t uc_stack; |
81 | struct compat_sigcontext uc_mcontext; | 81 | struct compat_sigcontext uc_mcontext; |
82 | compat_sigset_t uc_sigmask; | 82 | compat_sigset_t uc_sigmask; |
@@ -703,7 +703,7 @@ int compat_setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info, | |||
703 | err |= copy_siginfo_to_user32(&frame->info, info); | 703 | err |= copy_siginfo_to_user32(&frame->info, info); |
704 | 704 | ||
705 | __put_user_error(0, &frame->sig.uc.uc_flags, err); | 705 | __put_user_error(0, &frame->sig.uc.uc_flags, err); |
706 | __put_user_error(NULL, &frame->sig.uc.uc_link, err); | 706 | __put_user_error(0, &frame->sig.uc.uc_link, err); |
707 | 707 | ||
708 | memset(&stack, 0, sizeof(stack)); | 708 | memset(&stack, 0, sizeof(stack)); |
709 | stack.ss_sp = (compat_uptr_t)current->sas_ss_sp; | 709 | stack.ss_sp = (compat_uptr_t)current->sas_ss_sp; |
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index 538300f2273d..bdd34597254b 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c | |||
@@ -233,7 +233,28 @@ void __init smp_prepare_boot_cpu(void) | |||
233 | } | 233 | } |
234 | 234 | ||
235 | static void (*smp_cross_call)(const struct cpumask *, unsigned int); | 235 | static void (*smp_cross_call)(const struct cpumask *, unsigned int); |
236 | static phys_addr_t cpu_release_addr[NR_CPUS]; | 236 | |
237 | static const struct smp_enable_ops *enable_ops[] __initconst = { | ||
238 | &smp_spin_table_ops, | ||
239 | &smp_psci_ops, | ||
240 | NULL, | ||
241 | }; | ||
242 | |||
243 | static const struct smp_enable_ops *smp_enable_ops[NR_CPUS]; | ||
244 | |||
245 | static const struct smp_enable_ops * __init smp_get_enable_ops(const char *name) | ||
246 | { | ||
247 | const struct smp_enable_ops *ops = enable_ops[0]; | ||
248 | |||
249 | while (ops) { | ||
250 | if (!strcmp(name, ops->name)) | ||
251 | return ops; | ||
252 | |||
253 | ops++; | ||
254 | } | ||
255 | |||
256 | return NULL; | ||
257 | } | ||
237 | 258 | ||
238 | /* | 259 | /* |
239 | * Enumerate the possible CPU set from the device tree. | 260 | * Enumerate the possible CPU set from the device tree. |
@@ -252,22 +273,22 @@ void __init smp_init_cpus(void) | |||
252 | * We currently support only the "spin-table" enable-method. | 273 | * We currently support only the "spin-table" enable-method. |
253 | */ | 274 | */ |
254 | enable_method = of_get_property(dn, "enable-method", NULL); | 275 | enable_method = of_get_property(dn, "enable-method", NULL); |
255 | if (!enable_method || strcmp(enable_method, "spin-table")) { | 276 | if (!enable_method) { |
256 | pr_err("CPU %d: missing or invalid enable-method property: %s\n", | 277 | pr_err("CPU %d: missing enable-method property\n", cpu); |
257 | cpu, enable_method); | ||
258 | goto next; | 278 | goto next; |
259 | } | 279 | } |
260 | 280 | ||
261 | /* | 281 | smp_enable_ops[cpu] = smp_get_enable_ops(enable_method); |
262 | * Determine the address from which the CPU is polling. | 282 | |
263 | */ | 283 | if (!smp_enable_ops[cpu]) { |
264 | if (of_property_read_u64(dn, "cpu-release-addr", | 284 | pr_err("CPU %d: invalid enable-method property: %s\n", |
265 | &cpu_release_addr[cpu])) { | 285 | cpu, enable_method); |
266 | pr_err("CPU %d: missing or invalid cpu-release-addr property\n", | ||
267 | cpu); | ||
268 | goto next; | 286 | goto next; |
269 | } | 287 | } |
270 | 288 | ||
289 | if (smp_enable_ops[cpu]->init_cpu(dn, cpu)) | ||
290 | goto next; | ||
291 | |||
271 | set_cpu_possible(cpu, true); | 292 | set_cpu_possible(cpu, true); |
272 | next: | 293 | next: |
273 | cpu++; | 294 | cpu++; |
@@ -281,8 +302,7 @@ next: | |||
281 | 302 | ||
282 | void __init smp_prepare_cpus(unsigned int max_cpus) | 303 | void __init smp_prepare_cpus(unsigned int max_cpus) |
283 | { | 304 | { |
284 | int cpu; | 305 | int cpu, err; |
285 | void **release_addr; | ||
286 | unsigned int ncores = num_possible_cpus(); | 306 | unsigned int ncores = num_possible_cpus(); |
287 | 307 | ||
288 | /* | 308 | /* |
@@ -291,30 +311,35 @@ void __init smp_prepare_cpus(unsigned int max_cpus) | |||
291 | if (max_cpus > ncores) | 311 | if (max_cpus > ncores) |
292 | max_cpus = ncores; | 312 | max_cpus = ncores; |
293 | 313 | ||
314 | /* Don't bother if we're effectively UP */ | ||
315 | if (max_cpus <= 1) | ||
316 | return; | ||
317 | |||
294 | /* | 318 | /* |
295 | * Initialise the present map (which describes the set of CPUs | 319 | * Initialise the present map (which describes the set of CPUs |
296 | * actually populated at the present time) and release the | 320 | * actually populated at the present time) and release the |
297 | * secondaries from the bootloader. | 321 | * secondaries from the bootloader. |
322 | * | ||
323 | * Make sure we online at most (max_cpus - 1) additional CPUs. | ||
298 | */ | 324 | */ |
325 | max_cpus--; | ||
299 | for_each_possible_cpu(cpu) { | 326 | for_each_possible_cpu(cpu) { |
300 | if (max_cpus == 0) | 327 | if (max_cpus == 0) |
301 | break; | 328 | break; |
302 | 329 | ||
303 | if (!cpu_release_addr[cpu]) | 330 | if (cpu == smp_processor_id()) |
331 | continue; | ||
332 | |||
333 | if (!smp_enable_ops[cpu]) | ||
304 | continue; | 334 | continue; |
305 | 335 | ||
306 | release_addr = __va(cpu_release_addr[cpu]); | 336 | err = smp_enable_ops[cpu]->prepare_cpu(cpu); |
307 | release_addr[0] = (void *)__pa(secondary_holding_pen); | 337 | if (err) |
308 | __flush_dcache_area(release_addr, sizeof(release_addr[0])); | 338 | continue; |
309 | 339 | ||
310 | set_cpu_present(cpu, true); | 340 | set_cpu_present(cpu, true); |
311 | max_cpus--; | 341 | max_cpus--; |
312 | } | 342 | } |
313 | |||
314 | /* | ||
315 | * Send an event to wake up the secondaries. | ||
316 | */ | ||
317 | sev(); | ||
318 | } | 343 | } |
319 | 344 | ||
320 | 345 | ||
diff --git a/arch/arm64/kernel/smp_psci.c b/arch/arm64/kernel/smp_psci.c new file mode 100644 index 000000000000..112091684c22 --- /dev/null +++ b/arch/arm64/kernel/smp_psci.c | |||
@@ -0,0 +1,52 @@ | |||
1 | /* | ||
2 | * PSCI SMP initialisation | ||
3 | * | ||
4 | * Copyright (C) 2013 ARM Ltd. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | */ | ||
18 | |||
19 | #include <linux/init.h> | ||
20 | #include <linux/of.h> | ||
21 | #include <linux/smp.h> | ||
22 | |||
23 | #include <asm/psci.h> | ||
24 | |||
25 | static int __init smp_psci_init_cpu(struct device_node *dn, int cpu) | ||
26 | { | ||
27 | return 0; | ||
28 | } | ||
29 | |||
30 | static int __init smp_psci_prepare_cpu(int cpu) | ||
31 | { | ||
32 | int err; | ||
33 | |||
34 | if (!psci_ops.cpu_on) { | ||
35 | pr_err("psci: no cpu_on method, not booting CPU%d\n", cpu); | ||
36 | return -ENODEV; | ||
37 | } | ||
38 | |||
39 | err = psci_ops.cpu_on(cpu, __pa(secondary_holding_pen)); | ||
40 | if (err) { | ||
41 | pr_err("psci: failed to boot CPU%d (%d)\n", cpu, err); | ||
42 | return err; | ||
43 | } | ||
44 | |||
45 | return 0; | ||
46 | } | ||
47 | |||
48 | const struct smp_enable_ops smp_psci_ops __initconst = { | ||
49 | .name = "psci", | ||
50 | .init_cpu = smp_psci_init_cpu, | ||
51 | .prepare_cpu = smp_psci_prepare_cpu, | ||
52 | }; | ||
diff --git a/arch/arm64/kernel/smp_spin_table.c b/arch/arm64/kernel/smp_spin_table.c new file mode 100644 index 000000000000..7c35fa682f76 --- /dev/null +++ b/arch/arm64/kernel/smp_spin_table.c | |||
@@ -0,0 +1,66 @@ | |||
1 | /* | ||
2 | * Spin Table SMP initialisation | ||
3 | * | ||
4 | * Copyright (C) 2013 ARM Ltd. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | */ | ||
18 | |||
19 | #include <linux/init.h> | ||
20 | #include <linux/of.h> | ||
21 | #include <linux/smp.h> | ||
22 | |||
23 | #include <asm/cacheflush.h> | ||
24 | |||
25 | static phys_addr_t cpu_release_addr[NR_CPUS]; | ||
26 | |||
27 | static int __init smp_spin_table_init_cpu(struct device_node *dn, int cpu) | ||
28 | { | ||
29 | /* | ||
30 | * Determine the address from which the CPU is polling. | ||
31 | */ | ||
32 | if (of_property_read_u64(dn, "cpu-release-addr", | ||
33 | &cpu_release_addr[cpu])) { | ||
34 | pr_err("CPU %d: missing or invalid cpu-release-addr property\n", | ||
35 | cpu); | ||
36 | |||
37 | return -1; | ||
38 | } | ||
39 | |||
40 | return 0; | ||
41 | } | ||
42 | |||
43 | static int __init smp_spin_table_prepare_cpu(int cpu) | ||
44 | { | ||
45 | void **release_addr; | ||
46 | |||
47 | if (!cpu_release_addr[cpu]) | ||
48 | return -ENODEV; | ||
49 | |||
50 | release_addr = __va(cpu_release_addr[cpu]); | ||
51 | release_addr[0] = (void *)__pa(secondary_holding_pen); | ||
52 | __flush_dcache_area(release_addr, sizeof(release_addr[0])); | ||
53 | |||
54 | /* | ||
55 | * Send an event to wake up the secondary CPU. | ||
56 | */ | ||
57 | sev(); | ||
58 | |||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | const struct smp_enable_ops smp_spin_table_ops __initconst = { | ||
63 | .name = "spin-table", | ||
64 | .init_cpu = smp_spin_table_init_cpu, | ||
65 | .prepare_cpu = smp_spin_table_prepare_cpu, | ||
66 | }; | ||