On Thu, Nov 06, 2014 at 03:32:30AM +0000, al.stone@linaro.org wrote:
From: Mark Salter msalter@redhat.com
This is a first-cut effort at parking protocol support. It is very much a work in progress (as is the spec it is based on). This code deviates from the current spec in a number of ways to work around current firmware issues and issues with kernels using 64K page sizes.
caveat utilitor
It would be very nice for the patch to have comments as to what is a workaround. Some things I've commented on below might simply be bugs, but it's not always obvious.
Signed-off-by: Mark Salter msalter@redhat.com
arch/arm64/Kconfig | 3 + arch/arm64/include/asm/acpi.h | 3 + arch/arm64/include/asm/smp.h | 5 ++ arch/arm64/kernel/Makefile | 3 +- arch/arm64/kernel/acpi.c | 34 ++++++++-- arch/arm64/kernel/cpu_ops.c | 4 ++ arch/arm64/kernel/smp_parking_protocol.c | 107 +++++++++++++++++++++++++++++++ drivers/irqchip/irq-gic-v3.c | 10 +++ drivers/irqchip/irq-gic.c | 10 +++ include/linux/irqchip/arm-gic.h | 2 + 10 files changed, 174 insertions(+), 7 deletions(-) create mode 100644 arch/arm64/kernel/smp_parking_protocol.c
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index aee6a60..7789db7 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -269,6 +269,9 @@ config SMP
If you don't know what to do here, say N.
+config ARM_PARKING_PROTOCOL
def_bool y if SMP
config SCHED_MC bool "Multi-core scheduler support" depends on SMP diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h index 483ff45..6e692f4 100644 --- a/arch/arm64/include/asm/acpi.h +++ b/arch/arm64/include/asm/acpi.h @@ -89,11 +89,14 @@ static inline bool acpi_has_cpu_in_madt(void) static inline void arch_fix_phys_package_id(int num, u32 slot) { } void __init acpi_smp_init_cpus(void);
+extern int acpi_get_cpu_parked_address(int cpu, u64 *addr);
#else static inline void disable_acpi(void) { } static inline bool acpi_psci_present(void) { return false; } static inline bool acpi_psci_use_hvc(void) { return false; } static inline void acpi_smp_init_cpus(void) { } +static inline int acpi_get_cpu_parked_address(int cpu, u64 *addr) { return -EOPNOTSUPP; } #endif /* CONFIG_ACPI */
#endif /*_ASM_ACPI_H*/ diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h index bf22650..3411561 100644 --- a/arch/arm64/include/asm/smp.h +++ b/arch/arm64/include/asm/smp.h @@ -52,6 +52,11 @@ extern void set_smp_cross_call(void (*)(const struct cpumask *, unsigned int)); extern void (*__smp_cross_call)(const struct cpumask *, unsigned int);
/*
- Provide a function to signal a parked secondary CPU.
- */
+extern void set_smp_boot_wakeup_call(void (*)(int cpu));
Can't you just call smp_cross_call directly?
Then you don't need anything in the interrupt controller driver (be it GICv2 or GICv3). I was under the impression that the parking protocol just required an IPI, not any particular one.
+/*
- Called from the secondary holding pen, this is the secondary CPU entry point.
*/ asmlinkage void secondary_start_kernel(void); diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index f48e3f7..f4ba4fe 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -21,7 +21,8 @@ arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ sys_compat.o arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o -arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o topology.o +arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o topology.o \
smp_parking_protocol.o
arm64-obj-$(CONFIG_PERF_EVENTS) += perf_regs.o arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c index 6411600..7b3318b 100644 --- a/arch/arm64/kernel/acpi.c +++ b/arch/arm64/kernel/acpi.c @@ -37,6 +37,9 @@ EXPORT_SYMBOL(acpi_pci_disabled);
static int enabled_cpus; /* Processors (GICC) with enabled flag in MADT */
+static char *boot_method; +static u64 parked_address[NR_CPUS];
/*
- Since we're on ARM, the default interrupt routing model
- clearly has to be GIC.
@@ -71,7 +74,7 @@ void __init __acpi_unmap_table(char *map, unsigned long size)
- Returns the logical cpu number which maps to MPIDR
*/ -static int acpi_map_gic_cpu_interface(u64 mpidr, u8 enabled) +static int acpi_map_gic_cpu_interface(u64 mpidr, u64 parked_addr, u8 enabled) { int cpu;
@@ -125,9 +128,11 @@ static int acpi_map_gic_cpu_interface(u64 mpidr, u8 enabled) cpu = 0; }
parked_address[cpu] = parked_addr;
/* CPU 0 was already initialized */ if (cpu) {
cpu_ops[cpu] = cpu_get_ops(acpi_psci_present() ? "psci" : NULL);
cpu_ops[cpu] = cpu_get_ops(boot_method); if (!cpu_ops[cpu]) return -EINVAL;
@@ -140,7 +145,7 @@ static int acpi_map_gic_cpu_interface(u64 mpidr, u8 enabled) set_cpu_possible(cpu, true); } else { /* get cpu0's ops, no need to return if ops is null */
cpu_ops[0] = cpu_get_ops(acpi_psci_present() ? "psci" : NULL);
cpu_ops[0] = cpu_get_ops(boot_method); } enabled_cpus++;
@@ -161,7 +166,7 @@ acpi_parse_gic_cpu_interface(struct acpi_subtable_header *header, acpi_table_print_madt_entry(header);
acpi_map_gic_cpu_interface(processor->arm_mpidr & MPIDR_HWID_BITMASK,
processor->flags & ACPI_MADT_ENABLED);
processor->parked_address, processor->flags & ACPI_MADT_ENABLED); return 0;
} @@ -278,9 +283,12 @@ static int __init acpi_parse_fadt(struct acpi_table_header *table) * the ACPI spec or the Parking protocol spec. */ if (acpi_psci_present())
return 0;
boot_method = "psci";
else if (IS_ENABLED(CONFIG_ARM_PARKING_PROTOCOL))
boot_method = "parking-protocol";
pr_warn("No PSCI support, will not bring up secondary CPUs\n");
if (!boot_method)
pr_warn("No boot method, will not bring up secondary CPUs\n"); return -EOPNOTSUPP; }
@@ -341,6 +349,20 @@ void __init acpi_gic_init(void) early_acpi_os_unmap_memory((char *)table, tbl_size); }
+/*
- Parked Address in ACPI GIC structure will be used as the CPU
- release address
- */
+int acpi_get_cpu_parked_address(int cpu, u64 *addr) +{
if (!addr || !parked_address[cpu])
return -EINVAL;
I don't think addr can ever be NULL given that the only caller (smp_parking_protocol_cpu_init) generates addr by taking the address of an array element.
*addr = parked_address[cpu];
return 0;
+}
static int __init parse_acpi(char *arg) { if (!arg) diff --git a/arch/arm64/kernel/cpu_ops.c b/arch/arm64/kernel/cpu_ops.c index 1a04deb..1d90f31 100644 --- a/arch/arm64/kernel/cpu_ops.c +++ b/arch/arm64/kernel/cpu_ops.c @@ -23,6 +23,7 @@ #include <linux/string.h>
extern const struct cpu_operations smp_spin_table_ops; +extern const struct cpu_operations smp_parking_protocol_ops; extern const struct cpu_operations cpu_psci_ops;
const struct cpu_operations *cpu_ops[NR_CPUS]; @@ -30,6 +31,9 @@ const struct cpu_operations *cpu_ops[NR_CPUS]; static const struct cpu_operations *supported_cpu_ops[] = { #ifdef CONFIG_SMP &smp_spin_table_ops, +#ifdef CONFIG_ARM_PARKING_PROTOCOL
&smp_parking_protocol_ops,
+#endif #endif
Won't this mean "parking-protocol" will be accepted in a dtb? That shouldn't be the case.
&cpu_psci_ops, NULL,
diff --git a/arch/arm64/kernel/smp_parking_protocol.c b/arch/arm64/kernel/smp_parking_protocol.c new file mode 100644 index 0000000..e9c0c68 --- /dev/null +++ b/arch/arm64/kernel/smp_parking_protocol.c @@ -0,0 +1,107 @@ +/*
- Parking Protocol SMP initialisation
- Based largely on spin-table method.
- Copyright (C) 2013 ARM Ltd.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License version 2 as
- published by the Free Software Foundation.
- 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, see http://www.gnu.org/licenses/.
- */
+#include <linux/delay.h> +#include <linux/init.h> +#include <linux/of.h> +#include <linux/smp.h> +#include <linux/types.h> +#include <linux/acpi.h>
+#include <asm/cacheflush.h> +#include <asm/cpu_ops.h> +#include <asm/cputype.h> +#include <asm/smp_plat.h>
+static phys_addr_t cpu_mailbox_addr[NR_CPUS];
+static void (*__smp_boot_wakeup)(int cpu);
+void set_smp_boot_wakeup_call(void (*fn)(int cpu)) +{
__smp_boot_wakeup = fn;
+}
+static int smp_parking_protocol_cpu_init(struct device_node *dn,
unsigned int cpu)
+{
/*
* Determine the mailbox address.
*/
if (!acpi_get_cpu_parked_address(cpu, &cpu_mailbox_addr[cpu])) {
pr_info("%s: ACPI parked addr=%llx\n",
__func__, cpu_mailbox_addr[cpu]);
return 0;
}
pr_err("CPU %d: missing or invalid parking protocol mailbox\n", cpu);
return -1;
+}
+static int smp_parking_protocol_cpu_prepare(unsigned int cpu) +{
return 0;
+}
+struct parking_protocol_mailbox {
__le32 cpu_id;
__le32 reserved;
__le64 entry_point;
+};
+static int smp_parking_protocol_cpu_boot(unsigned int cpu) +{
struct parking_protocol_mailbox __iomem *mailbox;
if (!cpu_mailbox_addr[cpu] || !__smp_boot_wakeup)
return -ENODEV;
We should probably complain about the lack of a mailbox in the prepare callback, so we don't set the CPU as present.
/*
* The mailbox may or may not be inside the linear mapping.
* As ioremap_cache will either give us a new mapping or reuse the
* existing linear mapping, we can use it to cover both cases. In
* either case the memory will be MT_NORMAL.
*/
mailbox = ioremap_cache(cpu_mailbox_addr[cpu], sizeof(*mailbox));
if (!mailbox)
return -ENOMEM;
The mailbox should not ever be in the linear mapping, and the memory map is broken if that is the case. The spec doesn't allow for cacheable mappings of the mailbox.
/*
* We write the entry point and cpu id as LE regardless of the
* native endianess of the kernel. Therefore, any boot-loaders
* that read this address need to convert this address to the
* Boot-Loader's endianess before jumping.
*/
writeq(__pa(secondary_entry), &mailbox->entry_point);
writel(cpu, &mailbox->cpu_id);
__flush_dcache_area(mailbox, sizeof(*mailbox));
Without a cacheable mapping, this flush shouldn't be necessary.
__smp_boot_wakeup(cpu);
This should be able to be an smp_cross_call.
iounmap(mailbox);
return 0;
+}
+const struct cpu_operations smp_parking_protocol_ops = {
.name = "parking-protocol",
.cpu_init = smp_parking_protocol_cpu_init,
.cpu_prepare = smp_parking_protocol_cpu_prepare,
.cpu_boot = smp_parking_protocol_cpu_boot,
+};
I believe all of the below GIC changes can go.
Thanks, Mark.
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index aa17ae8..d330dab 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -506,9 +506,19 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) isb(); }
+#ifdef CONFIG_ARM_PARKING_PROTOCOL +static void gic_wakeup_parked_cpu(int cpu) +{
gic_raise_softirq(cpumask_of(cpu), 0);
+} +#endif
static void gic_smp_init(void) { set_smp_cross_call(gic_raise_softirq); +#ifdef CONFIG_ARM_PARKING_PROTOCOL
set_smp_boot_wakeup_call(gic_wakeup_parked_cpu);
+#endif register_cpu_notifier(&gic_cpu_notifier); }
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index afdeaa1..26e6773 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -643,6 +643,13 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
raw_spin_unlock_irqrestore(&irq_controller_lock, flags);
}
+#ifdef CONFIG_ARM_PARKING_PROTOCOL +static void gic_wakeup_parked_cpu(int cpu) +{
gic_raise_softirq(cpumask_of(cpu), GIC_DIST_SOFTINT_NSATT);
+} +#endif #endif
#ifdef CONFIG_BL_SWITCHER @@ -998,6 +1005,9 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, #ifdef CONFIG_SMP set_smp_cross_call(gic_raise_softirq); register_cpu_notifier(&gic_cpu_notifier); +#ifdef CONFIG_ARM_PARKING_PROTOCOL
set_smp_boot_wakeup_call(gic_wakeup_parked_cpu);
+#endif #endif set_handle_irq(gic_handle_irq); } diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h index 13eed92..dc9cb5f 100644 --- a/include/linux/irqchip/arm-gic.h +++ b/include/linux/irqchip/arm-gic.h @@ -55,6 +55,8 @@ (GICD_INT_DEF_PRI << 8) |\ GICD_INT_DEF_PRI)
+#define GIC_DIST_SOFTINT_NSATT 0x8000
#define GICH_HCR 0x0 #define GICH_VTR 0x4
#define GICH_VMCR 0x8
1.9.3
Linaro-acpi mailing list Linaro-acpi@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-acpi