diff options
Diffstat (limited to 'arch/arm64/kernel')
-rw-r--r-- | arch/arm64/kernel/early_printk.c | 35 | ||||
-rw-r--r-- | arch/arm64/kernel/head.S | 4 | ||||
-rw-r--r-- | arch/arm64/kernel/irq.c | 19 | ||||
-rw-r--r-- | arch/arm64/kernel/setup.c | 12 | ||||
-rw-r--r-- | arch/arm64/kernel/smp.c | 102 | ||||
-rw-r--r-- | arch/arm64/kernel/smp_psci.c | 5 |
6 files changed, 152 insertions, 25 deletions
diff --git a/arch/arm64/kernel/early_printk.c b/arch/arm64/kernel/early_printk.c index 7e320a2edb9b..ac974f48a7a2 100644 --- a/arch/arm64/kernel/early_printk.c +++ b/arch/arm64/kernel/early_printk.c @@ -24,6 +24,7 @@ #include <linux/io.h> #include <linux/amba/serial.h> +#include <linux/serial_reg.h> static void __iomem *early_base; static void (*printch)(char ch); @@ -40,6 +41,37 @@ static void pl011_printch(char ch) ; } +/* + * Semihosting-based debug console + */ +static void smh_printch(char ch) +{ + asm volatile("mov x1, %0\n" + "mov x0, #3\n" + "hlt 0xf000\n" + : : "r" (&ch) : "x0", "x1", "memory"); +} + +/* + * 8250/16550 (8-bit aligned registers) single character TX. + */ +static void uart8250_8bit_printch(char ch) +{ + while (!(readb_relaxed(early_base + UART_LSR) & UART_LSR_THRE)) + ; + writeb_relaxed(ch, early_base + UART_TX); +} + +/* + * 8250/16550 (32-bit aligned registers) single character TX. + */ +static void uart8250_32bit_printch(char ch) +{ + while (!(readl_relaxed(early_base + (UART_LSR << 2)) & UART_LSR_THRE)) + ; + writel_relaxed(ch, early_base + (UART_TX << 2)); +} + struct earlycon_match { const char *name; void (*printch)(char ch); @@ -47,6 +79,9 @@ struct earlycon_match { static const struct earlycon_match earlycon_match[] __initconst = { { .name = "pl011", .printch = pl011_printch, }, + { .name = "smh", .printch = smh_printch, }, + { .name = "uart8250-8bit", .printch = uart8250_8bit_printch, }, + { .name = "uart8250-32bit", .printch = uart8250_32bit_printch, }, {} }; diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S index 0a0a49756826..53dcae49e729 100644 --- a/arch/arm64/kernel/head.S +++ b/arch/arm64/kernel/head.S @@ -26,6 +26,7 @@ #include <asm/assembler.h> #include <asm/ptrace.h> #include <asm/asm-offsets.h> +#include <asm/cputype.h> #include <asm/memory.h> #include <asm/thread_info.h> #include <asm/pgtable-hwdef.h> @@ -229,7 +230,8 @@ ENTRY(secondary_holding_pen) bl __calc_phys_offset // x24=phys offset bl el2_setup // Drop to EL1 mrs x0, mpidr_el1 - and x0, x0, #15 // CPU number + ldr x1, =MPIDR_HWID_BITMASK + and x0, x0, x1 adr x1, 1b ldp x2, x3, [x1] sub x1, x1, x2 diff --git a/arch/arm64/kernel/irq.c b/arch/arm64/kernel/irq.c index 0373c6609eaf..ecb3354292ed 100644 --- a/arch/arm64/kernel/irq.c +++ b/arch/arm64/kernel/irq.c @@ -25,7 +25,7 @@ #include <linux/irq.h> #include <linux/smp.h> #include <linux/init.h> -#include <linux/of_irq.h> +#include <linux/irqchip.h> #include <linux/seq_file.h> #include <linux/ratelimit.h> @@ -67,18 +67,17 @@ void handle_IRQ(unsigned int irq, struct pt_regs *regs) set_irq_regs(old_regs); } -/* - * Interrupt controllers supported by the kernel. - */ -static const struct of_device_id intctrl_of_match[] __initconst = { - /* IRQ controllers { .compatible, .data } info to go here */ - {} -}; +void __init set_handle_irq(void (*handle_irq)(struct pt_regs *)) +{ + if (handle_arch_irq) + return; + + handle_arch_irq = handle_irq; +} void __init init_IRQ(void) { - of_irq_init(intctrl_of_match); - + irqchip_init(); if (!handle_arch_irq) panic("No interrupt controller found."); } diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index 113db863f832..6a9a53292590 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -32,6 +32,7 @@ #include <linux/kexec.h> #include <linux/crash_dump.h> #include <linux/root_dev.h> +#include <linux/clk-provider.h> #include <linux/cpu.h> #include <linux/interrupt.h> #include <linux/smp.h> @@ -46,6 +47,7 @@ #include <asm/cputable.h> #include <asm/sections.h> #include <asm/setup.h> +#include <asm/smp_plat.h> #include <asm/cacheflush.h> #include <asm/tlbflush.h> #include <asm/traps.h> @@ -240,6 +242,8 @@ static void __init request_standard_resources(void) } } +u64 __cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = INVALID_HWID }; + void __init setup_arch(char **cmdline_p) { setup_processor(); @@ -264,6 +268,7 @@ void __init setup_arch(char **cmdline_p) psci_init(); + cpu_logical_map(0) = read_cpuid_mpidr() & MPIDR_HWID_BITMASK; #ifdef CONFIG_SMP smp_init_cpus(); #endif @@ -277,6 +282,13 @@ void __init setup_arch(char **cmdline_p) #endif } +static int __init arm64_of_clk_init(void) +{ + of_clk_init(NULL); + return 0; +} +arch_initcall(arm64_of_clk_init); + static DEFINE_PER_CPU(struct cpu, cpu_data); static int __init topology_init(void) diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index bdd34597254b..d4dcc6515253 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -43,6 +43,7 @@ #include <asm/pgtable.h> #include <asm/pgalloc.h> #include <asm/processor.h> +#include <asm/smp_plat.h> #include <asm/sections.h> #include <asm/tlbflush.h> #include <asm/ptrace.h> @@ -53,7 +54,7 @@ * where to place its SVC stack */ struct secondary_data secondary_data; -volatile unsigned long secondary_holding_pen_release = -1; +volatile unsigned long secondary_holding_pen_release = INVALID_HWID; enum ipi_msg_type { IPI_RESCHEDULE, @@ -70,7 +71,7 @@ static DEFINE_RAW_SPINLOCK(boot_lock); * in coherency or not. This is necessary for the hotplug code to work * reliably. */ -static void __cpuinit write_pen_release(int val) +static void __cpuinit write_pen_release(u64 val) { void *start = (void *)&secondary_holding_pen_release; unsigned long size = sizeof(secondary_holding_pen_release); @@ -96,7 +97,7 @@ static int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) /* * Update the pen release flag. */ - write_pen_release(cpu); + write_pen_release(cpu_logical_map(cpu)); /* * Send an event, causing the secondaries to read pen_release. @@ -105,7 +106,7 @@ static int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) timeout = jiffies + (1 * HZ); while (time_before(jiffies, timeout)) { - if (secondary_holding_pen_release == -1UL) + if (secondary_holding_pen_release == INVALID_HWID) break; udelay(10); } @@ -116,7 +117,7 @@ static int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) */ raw_spin_unlock(&boot_lock); - return secondary_holding_pen_release != -1 ? -ENOSYS : 0; + return secondary_holding_pen_release != INVALID_HWID ? -ENOSYS : 0; } static DECLARE_COMPLETION(cpu_running); @@ -190,7 +191,7 @@ asmlinkage void __cpuinit secondary_start_kernel(void) * Let the primary processor know we're out of the * pen, then head off into the C entry point */ - write_pen_release(-1); + write_pen_release(INVALID_HWID); /* * Synchronise with the boot thread. @@ -257,15 +258,77 @@ static const struct smp_enable_ops * __init smp_get_enable_ops(const char *name) } /* - * Enumerate the possible CPU set from the device tree. + * Enumerate the possible CPU set from the device tree and build the + * cpu logical map array containing MPIDR values related to logical + * cpus. Assumes that cpu_logical_map(0) has already been initialized. */ void __init smp_init_cpus(void) { const char *enable_method; struct device_node *dn = NULL; - int cpu = 0; + int i, cpu = 1; + bool bootcpu_valid = false; while ((dn = of_find_node_by_type(dn, "cpu"))) { + u64 hwid; + + /* + * A cpu node with missing "reg" property is + * considered invalid to build a cpu_logical_map + * entry. + */ + if (of_property_read_u64(dn, "reg", &hwid)) { + pr_err("%s: missing reg property\n", dn->full_name); + goto next; + } + + /* + * Non affinity bits must be set to 0 in the DT + */ + if (hwid & ~MPIDR_HWID_BITMASK) { + pr_err("%s: invalid reg property\n", dn->full_name); + goto next; + } + + /* + * Duplicate MPIDRs are a recipe for disaster. Scan + * all initialized entries and check for + * duplicates. If any is found just ignore the cpu. + * cpu_logical_map was initialized to INVALID_HWID to + * avoid matching valid MPIDR values. + */ + for (i = 1; (i < cpu) && (i < NR_CPUS); i++) { + if (cpu_logical_map(i) == hwid) { + pr_err("%s: duplicate cpu reg properties in the DT\n", + dn->full_name); + goto next; + } + } + + /* + * The numbering scheme requires that the boot CPU + * must be assigned logical id 0. Record it so that + * the logical map built from DT is validated and can + * be used. + */ + if (hwid == cpu_logical_map(0)) { + if (bootcpu_valid) { + pr_err("%s: duplicate boot cpu reg property in DT\n", + dn->full_name); + goto next; + } + + bootcpu_valid = true; + + /* + * cpu_logical_map has already been + * initialized and the boot cpu doesn't need + * the enable-method so continue without + * incrementing cpu. + */ + continue; + } + if (cpu >= NR_CPUS) goto next; @@ -274,22 +337,24 @@ void __init smp_init_cpus(void) */ enable_method = of_get_property(dn, "enable-method", NULL); if (!enable_method) { - pr_err("CPU %d: missing enable-method property\n", cpu); + pr_err("%s: missing enable-method property\n", + dn->full_name); goto next; } smp_enable_ops[cpu] = smp_get_enable_ops(enable_method); if (!smp_enable_ops[cpu]) { - pr_err("CPU %d: invalid enable-method property: %s\n", - cpu, enable_method); + pr_err("%s: invalid enable-method property: %s\n", + dn->full_name, enable_method); goto next; } if (smp_enable_ops[cpu]->init_cpu(dn, cpu)) goto next; - set_cpu_possible(cpu, true); + pr_debug("cpu logical map 0x%llx\n", hwid); + cpu_logical_map(cpu) = hwid; next: cpu++; } @@ -298,6 +363,19 @@ next: if (cpu > NR_CPUS) pr_warning("no. of cores (%d) greater than configured maximum of %d - clipping\n", cpu, NR_CPUS); + + if (!bootcpu_valid) { + pr_err("DT missing boot CPU MPIDR, not enabling secondaries\n"); + return; + } + + /* + * All the cpus that made it to the cpu_logical_map have been + * validated so set them as possible cpus. + */ + for (i = 0; i < NR_CPUS; i++) + if (cpu_logical_map(i) != INVALID_HWID) + set_cpu_possible(i, true); } void __init smp_prepare_cpus(unsigned int max_cpus) diff --git a/arch/arm64/kernel/smp_psci.c b/arch/arm64/kernel/smp_psci.c index 112091684c22..0c533301be77 100644 --- a/arch/arm64/kernel/smp_psci.c +++ b/arch/arm64/kernel/smp_psci.c @@ -21,6 +21,7 @@ #include <linux/smp.h> #include <asm/psci.h> +#include <asm/smp_plat.h> static int __init smp_psci_init_cpu(struct device_node *dn, int cpu) { @@ -36,7 +37,7 @@ static int __init smp_psci_prepare_cpu(int cpu) return -ENODEV; } - err = psci_ops.cpu_on(cpu, __pa(secondary_holding_pen)); + err = psci_ops.cpu_on(cpu_logical_map(cpu), __pa(secondary_holding_pen)); if (err) { pr_err("psci: failed to boot CPU%d (%d)\n", cpu, err); return err; @@ -47,6 +48,6 @@ static int __init smp_psci_prepare_cpu(int cpu) const struct smp_enable_ops smp_psci_ops __initconst = { .name = "psci", - .init_cpu = smp_psci_init_cpu, + .init_cpu = smp_psci_init_cpu, .prepare_cpu = smp_psci_prepare_cpu, }; |