openwrt/target/linux/bcm53xx/patches-4.3/130-ARM-BCM-Add-SMP-support-for-Broadcom-NSP.patch
Hauke Mehrtens fa4395db04 bcm53xx: add support basic for kernel 4.3
The files directory is now split up into the files which are needed for
every kernel version and the files only needed by kernel 4.1. The files
in files-4.1 are already merged into mainline kernel 4.3. This patch
only removed patches which were merged into mainline kernel 4.3.

Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>

SVN-Revision: 47251
2015-10-25 16:43:14 +00:00

636 lines
18 KiB
Diff

From a0ad1511d5805b95ac4c454d7904c670a1696055 Mon Sep 17 00:00:00 2001
From: Kapil Hali <kapilh@broadcom.com>
Date: Wed, 14 Oct 2015 13:47:00 -0400
Subject: [PATCH] ARM: BCM: Add SMP support for Broadcom NSP
Add SMP support for Broadcom's Northstar Plus SoC,
cpu enable method and pen_release procedures. This
changes also consolidates iProc family's - BCM NSP
and BCM Kona, SMP handling in a common file.
Northstar Plus SoC is based on ARM Cortex-A9
revision r3p0 which requires configuration for ARM
Errata 764369 for SMP. This change adds the needed
configuration option.
Signed-off-by: Kapil Hali <kapilh@broadcom.com>
---
arch/arm/mach-bcm/Makefile | 2 +-
arch/arm/mach-bcm/bcm_nsp.h | 19 +++
arch/arm/mach-bcm/headsmp.S | 37 +++++
arch/arm/mach-bcm/kona_smp.c | 202 ---------------------------
arch/arm/mach-bcm/platsmp.c | 326 +++++++++++++++++++++++++++++++++++++++++++
5 files changed, 383 insertions(+), 203 deletions(-)
create mode 100644 arch/arm/mach-bcm/bcm_nsp.h
create mode 100644 arch/arm/mach-bcm/headsmp.S
delete mode 100644 arch/arm/mach-bcm/kona_smp.c
create mode 100644 arch/arm/mach-bcm/platsmp.c
--- a/arch/arm/mach-bcm/Makefile
+++ b/arch/arm/mach-bcm/Makefile
@@ -20,7 +20,7 @@ obj-$(CONFIG_ARCH_BCM_281XX) += board_bc
obj-$(CONFIG_ARCH_BCM_21664) += board_bcm21664.o
# BCM281XX and BCM21664 SMP support
-obj-$(CONFIG_ARCH_BCM_MOBILE_SMP) += kona_smp.o
+obj-$(CONFIG_ARCH_BCM_MOBILE_SMP) += platsmp.o
# BCM281XX and BCM21664 L2 cache control
obj-$(CONFIG_ARCH_BCM_MOBILE_L2_CACHE) += kona_l2_cache.o
--- /dev/null
+++ b/arch/arm/mach-bcm/bcm_nsp.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2015 Broadcom Corporation
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __BCM_NSP_H
+#define __BCM_NSP_H
+
+extern void nsp_secondary_startup(void);
+
+#endif /* __BCM_NSP_H */
--- /dev/null
+++ b/arch/arm/mach-bcm/headsmp.S
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 Broadcom Corporation
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/linkage.h>
+
+/*
+ * iProc specific entry point for secondary CPUs. This provides
+ * a "holding pen" into which all secondary cores are held until
+ * we are ready for them to initialise.
+ */
+ENTRY(nsp_secondary_startup)
+ mrc p15, 0, r0, c0, c0, 5
+ and r0, r0, #15
+ adr r4, 1f
+ ldmia r4, {r5, r6}
+ sub r4, r4, r5
+ add r6, r6, r4
+pen: ldr r7, [r6]
+ cmp r7, r0
+ bne pen
+
+ b secondary_startup
+
+1: .long .
+ .long pen_release
+
+ENDPROC(nsp_secondary_startup)
--- a/arch/arm/mach-bcm/kona_smp.c
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (C) 2014 Broadcom Corporation
- * Copyright 2014 Linaro Limited
- *
- * 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 version 2.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/io.h>
-#include <linux/of.h>
-#include <linux/sched.h>
-
-#include <asm/smp.h>
-#include <asm/smp_plat.h>
-#include <asm/smp_scu.h>
-
-/* Size of mapped Cortex A9 SCU address space */
-#define CORTEX_A9_SCU_SIZE 0x58
-
-#define SECONDARY_TIMEOUT_NS NSEC_PER_MSEC /* 1 msec (in nanoseconds) */
-#define BOOT_ADDR_CPUID_MASK 0x3
-
-/* Name of device node property defining secondary boot register location */
-#define OF_SECONDARY_BOOT "secondary-boot-reg"
-
-/* I/O address of register used to coordinate secondary core startup */
-static u32 secondary_boot;
-
-/*
- * Enable the Cortex A9 Snoop Control Unit
- *
- * By the time this is called we already know there are multiple
- * cores present. We assume we're running on a Cortex A9 processor,
- * so any trouble getting the base address register or getting the
- * SCU base is a problem.
- *
- * Return 0 if successful or an error code otherwise.
- */
-static int __init scu_a9_enable(void)
-{
- unsigned long config_base;
- void __iomem *scu_base;
-
- if (!scu_a9_has_base()) {
- pr_err("no configuration base address register!\n");
- return -ENXIO;
- }
-
- /* Config base address register value is zero for uniprocessor */
- config_base = scu_a9_get_base();
- if (!config_base) {
- pr_err("hardware reports only one core\n");
- return -ENOENT;
- }
-
- scu_base = ioremap((phys_addr_t)config_base, CORTEX_A9_SCU_SIZE);
- if (!scu_base) {
- pr_err("failed to remap config base (%lu/%u) for SCU\n",
- config_base, CORTEX_A9_SCU_SIZE);
- return -ENOMEM;
- }
-
- scu_enable(scu_base);
-
- iounmap(scu_base); /* That's the last we'll need of this */
-
- return 0;
-}
-
-static void __init bcm_smp_prepare_cpus(unsigned int max_cpus)
-{
- static cpumask_t only_cpu_0 = { CPU_BITS_CPU0 };
- struct device_node *node;
- int ret;
-
- BUG_ON(secondary_boot); /* We're called only once */
-
- /*
- * This function is only called via smp_ops->smp_prepare_cpu().
- * That only happens if a "/cpus" device tree node exists
- * and has an "enable-method" property that selects the SMP
- * operations defined herein.
- */
- node = of_find_node_by_path("/cpus");
- BUG_ON(!node);
-
- /*
- * Our secondary enable method requires a "secondary-boot-reg"
- * property to specify a register address used to request the
- * ROM code boot a secondary code. If we have any trouble
- * getting this we fall back to uniprocessor mode.
- */
- if (of_property_read_u32(node, OF_SECONDARY_BOOT, &secondary_boot)) {
- pr_err("%s: missing/invalid " OF_SECONDARY_BOOT " property\n",
- node->name);
- ret = -ENOENT; /* Arrange to disable SMP */
- goto out;
- }
-
- /*
- * Enable the SCU on Cortex A9 based SoCs. If -ENOENT is
- * returned, the SoC reported a uniprocessor configuration.
- * We bail on any other error.
- */
- ret = scu_a9_enable();
-out:
- of_node_put(node);
- if (ret) {
- /* Update the CPU present map to reflect uniprocessor mode */
- BUG_ON(ret != -ENOENT);
- pr_warn("disabling SMP\n");
- init_cpu_present(&only_cpu_0);
- }
-}
-
-/*
- * The ROM code has the secondary cores looping, waiting for an event.
- * When an event occurs each core examines the bottom two bits of the
- * secondary boot register. When a core finds those bits contain its
- * own core id, it performs initialization, including computing its boot
- * address by clearing the boot register value's bottom two bits. The
- * core signals that it is beginning its execution by writing its boot
- * address back to the secondary boot register, and finally jumps to
- * that address.
- *
- * So to start a core executing we need to:
- * - Encode the (hardware) CPU id with the bottom bits of the secondary
- * start address.
- * - Write that value into the secondary boot register.
- * - Generate an event to wake up the secondary CPU(s).
- * - Wait for the secondary boot register to be re-written, which
- * indicates the secondary core has started.
- */
-static int bcm_boot_secondary(unsigned int cpu, struct task_struct *idle)
-{
- void __iomem *boot_reg;
- phys_addr_t boot_func;
- u64 start_clock;
- u32 cpu_id;
- u32 boot_val;
- bool timeout = false;
-
- cpu_id = cpu_logical_map(cpu);
- if (cpu_id & ~BOOT_ADDR_CPUID_MASK) {
- pr_err("bad cpu id (%u > %u)\n", cpu_id, BOOT_ADDR_CPUID_MASK);
- return -EINVAL;
- }
-
- if (!secondary_boot) {
- pr_err("required secondary boot register not specified\n");
- return -EINVAL;
- }
-
- boot_reg = ioremap_nocache((phys_addr_t)secondary_boot, sizeof(u32));
- if (!boot_reg) {
- pr_err("unable to map boot register for cpu %u\n", cpu_id);
- return -ENOSYS;
- }
-
- /*
- * Secondary cores will start in secondary_startup(),
- * defined in "arch/arm/kernel/head.S"
- */
- boot_func = virt_to_phys(secondary_startup);
- BUG_ON(boot_func & BOOT_ADDR_CPUID_MASK);
- BUG_ON(boot_func > (phys_addr_t)U32_MAX);
-
- /* The core to start is encoded in the low bits */
- boot_val = (u32)boot_func | cpu_id;
- writel_relaxed(boot_val, boot_reg);
-
- sev();
-
- /* The low bits will be cleared once the core has started */
- start_clock = local_clock();
- while (!timeout && readl_relaxed(boot_reg) == boot_val)
- timeout = local_clock() - start_clock > SECONDARY_TIMEOUT_NS;
-
- iounmap(boot_reg);
-
- if (!timeout)
- return 0;
-
- pr_err("timeout waiting for cpu %u to start\n", cpu_id);
-
- return -ENOSYS;
-}
-
-static struct smp_operations bcm_smp_ops __initdata = {
- .smp_prepare_cpus = bcm_smp_prepare_cpus,
- .smp_boot_secondary = bcm_boot_secondary,
-};
-CPU_METHOD_OF_DECLARE(bcm_smp_bcm281xx, "brcm,bcm11351-cpu-method",
- &bcm_smp_ops);
--- /dev/null
+++ b/arch/arm/mach-bcm/platsmp.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2014-2015 Broadcom Corporation
+ * Copyright 2014 Linaro Limited
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/cpumask.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/of.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+
+#include <asm/cacheflush.h>
+#include <asm/smp.h>
+#include <asm/smp_plat.h>
+#include <asm/smp_scu.h>
+
+#include "bcm_nsp.h"
+
+/* Size of mapped Cortex A9 SCU address space */
+#define CORTEX_A9_SCU_SIZE 0x58
+
+#define SECONDARY_TIMEOUT_NS NSEC_PER_MSEC /* 1 msec (in nanoseconds) */
+#define BOOT_ADDR_CPUID_MASK 0x3
+
+/* Name of device node property defining secondary boot register location */
+#define OF_SECONDARY_BOOT "secondary-boot-reg"
+
+/* I/O address of register used to coordinate secondary core startup */
+static u32 secondary_boot;
+
+static DEFINE_SPINLOCK(boot_lock);
+
+/*
+ * Write pen_release in a way that is guaranteed to be visible to all
+ * observers, irrespective of whether they're taking part in coherency
+ * or not. This is necessary for the hotplug code to work reliably.
+ */
+static void write_pen_release(int val)
+{
+ pen_release = val;
+ /*
+ * Ensure write to pen_release is visible to the other cores,
+ * here - primary core
+ */
+ smp_wmb();
+ sync_cache_w(&pen_release);
+}
+
+/*
+ * Enable the Cortex A9 Snoop Control Unit
+ *
+ * By the time this is called we already know there are multiple
+ * cores present. We assume we're running on a Cortex A9 processor,
+ * so any trouble getting the base address register or getting the
+ * SCU base is a problem.
+ *
+ * Return 0 if successful or an error code otherwise.
+ */
+static int __init scu_a9_enable(void)
+{
+ unsigned long config_base;
+ void __iomem *scu_base;
+
+ if (!scu_a9_has_base()) {
+ pr_err("no configuration base address register!\n");
+ return -ENXIO;
+ }
+
+ /* Config base address register value is zero for uniprocessor */
+ config_base = scu_a9_get_base();
+ if (!config_base) {
+ pr_err("hardware reports only one core\n");
+ return -ENOENT;
+ }
+
+ scu_base = ioremap((phys_addr_t)config_base, CORTEX_A9_SCU_SIZE);
+ if (!scu_base) {
+ pr_err("failed to remap config base (%lu/%u) for SCU\n",
+ config_base, CORTEX_A9_SCU_SIZE);
+ return -ENOMEM;
+ }
+
+ scu_enable(scu_base);
+
+ iounmap(scu_base); /* That's the last we'll need of this */
+
+ return 0;
+}
+
+static int nsp_write_lut(void (*secondary_startup) (void))
+{
+ void __iomem *sku_rom_lut;
+ phys_addr_t secondary_startup_phy;
+
+ if (!secondary_boot) {
+ pr_warn("required secondary boot register not specified\n");
+ return -EINVAL;
+ }
+
+ sku_rom_lut = ioremap_nocache((phys_addr_t)secondary_boot,
+ sizeof(secondary_boot));
+ if (!sku_rom_lut) {
+ pr_warn("unable to ioremap SKU-ROM LUT register\n");
+ return -ENOMEM;
+ }
+
+ secondary_startup_phy = virt_to_phys(secondary_startup);
+ BUG_ON(secondary_startup_phy > (phys_addr_t)U32_MAX);
+
+ writel_relaxed(secondary_startup_phy, sku_rom_lut);
+ /*
+ * Ensure the write is visible to the secondary core.
+ */
+ smp_wmb();
+
+ iounmap(sku_rom_lut);
+
+ return 0;
+}
+
+static void nsp_secondary_init(unsigned int cpu)
+{
+ /*
+ * Let the primary cpu know we are out of holding pen.
+ */
+ write_pen_release(-1);
+
+ /*
+ * Synchronise with the boot thread.
+ */
+ spin_lock(&boot_lock);
+ spin_unlock(&boot_lock);
+}
+
+static void __init bcm_smp_prepare_cpus(unsigned int max_cpus)
+{
+ static cpumask_t only_cpu_0 = { CPU_BITS_CPU0 };
+ struct device_node *node;
+ int ret;
+
+ BUG_ON(secondary_boot); /* We're called only once */
+
+ /*
+ * This function is only called via smp_ops->smp_prepare_cpu().
+ * That only happens if a "/cpus" device tree node exists
+ * and has an "enable-method" property that selects the SMP
+ * operations defined herein.
+ */
+ node = of_find_node_by_path("/cpus");
+ BUG_ON(!node);
+
+ /*
+ * Our secondary enable method requires a "secondary-boot-reg"
+ * property to specify a register address used to request the
+ * ROM code boot a secondary core. If we have any trouble
+ * getting this we fall back to uniprocessor mode.
+ */
+ if (of_property_read_u32(node, OF_SECONDARY_BOOT, &secondary_boot)) {
+ pr_warn("%s: missing/invalid " OF_SECONDARY_BOOT " property\n",
+ node->name);
+ ret = -ENOENT; /* Arrange to disable SMP */
+ goto out;
+ }
+
+ /*
+ * Enable the SCU on Cortex A9 based SoCs. If -ENOENT is
+ * returned, the SoC reported a uniprocessor configuration.
+ * We bail on any other error.
+ */
+ ret = scu_a9_enable();
+out:
+ of_node_put(node);
+ if (ret) {
+ /* Update the CPU present map to reflect uniprocessor mode */
+ pr_warn("disabling SMP\n");
+ init_cpu_present(&only_cpu_0);
+ }
+}
+
+/*
+ * The ROM code has the secondary cores looping, waiting for an event.
+ * When an event occurs each core examines the bottom two bits of the
+ * secondary boot register. When a core finds those bits contain its
+ * own core id, it performs initialization, including computing its boot
+ * address by clearing the boot register value's bottom two bits. The
+ * core signals that it is beginning its execution by writing its boot
+ * address back to the secondary boot register, and finally jumps to
+ * that address.
+ *
+ * So to start a core executing we need to:
+ * - Encode the (hardware) CPU id with the bottom bits of the secondary
+ * start address.
+ * - Write that value into the secondary boot register.
+ * - Generate an event to wake up the secondary CPU(s).
+ * - Wait for the secondary boot register to be re-written, which
+ * indicates the secondary core has started.
+ */
+static int kona_boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+ void __iomem *boot_reg;
+ phys_addr_t boot_func;
+ u64 start_clock;
+ u32 cpu_id;
+ u32 boot_val;
+ bool timeout = false;
+
+ cpu_id = cpu_logical_map(cpu);
+ if (cpu_id & ~BOOT_ADDR_CPUID_MASK) {
+ pr_err("bad cpu id (%u > %u)\n", cpu_id, BOOT_ADDR_CPUID_MASK);
+ return -EINVAL;
+ }
+
+ if (!secondary_boot) {
+ pr_err("required secondary boot register not specified\n");
+ return -EINVAL;
+ }
+
+ boot_reg = ioremap_nocache((phys_addr_t)secondary_boot, sizeof(u32));
+ if (!boot_reg) {
+ pr_err("unable to map boot register for cpu %u\n", cpu_id);
+ return -ENOMEM;
+ }
+
+ /*
+ * Secondary cores will start in secondary_startup(),
+ * defined in "arch/arm/kernel/head.S"
+ */
+ boot_func = virt_to_phys(secondary_startup);
+ BUG_ON(boot_func & BOOT_ADDR_CPUID_MASK);
+ BUG_ON(boot_func > (phys_addr_t)U32_MAX);
+
+ /* The core to start is encoded in the low bits */
+ boot_val = (u32)boot_func | cpu_id;
+ writel_relaxed(boot_val, boot_reg);
+
+ sev();
+
+ /* The low bits will be cleared once the core has started */
+ start_clock = local_clock();
+ while (!timeout && readl_relaxed(boot_reg) == boot_val)
+ timeout = local_clock() - start_clock > SECONDARY_TIMEOUT_NS;
+
+ iounmap(boot_reg);
+
+ if (!timeout)
+ return 0;
+
+ pr_err("timeout waiting for cpu %u to start\n", cpu_id);
+
+ return -ENXIO;
+}
+
+static int nsp_boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+ unsigned long timeout;
+ int ret;
+
+ /*
+ * After wake up, secondary core branches to the startup
+ * address programmed at SKU ROM LUT location.
+ */
+ ret = nsp_write_lut(nsp_secondary_startup);
+ if (ret) {
+ pr_err("unable to write startup addr to SKU ROM LUT\n");
+ goto out;
+ }
+
+ /*
+ * The secondary processor is waiting to be released from
+ * the holding pen - release it, then wait for it to flag
+ * that it has been released by resetting pen_release.
+ */
+ spin_lock(&boot_lock);
+
+ write_pen_release(cpu_logical_map(cpu));
+ /*
+ * Send an Event to wake up the secondary core which is in
+ * WFE state. Updated pen_release should also be visible to
+ * the secondary core.
+ */
+ dsb_sev();
+
+ timeout = jiffies + (1 * HZ);
+ while (time_before(jiffies, timeout)) {
+ /* Make sure loads on other CPU is visible */
+ smp_rmb();
+ if (pen_release == -1)
+ break;
+
+ udelay(10);
+ }
+
+ spin_unlock(&boot_lock);
+
+ ret = pen_release != -1 ? -ENXIO : 0;
+
+out:
+ return ret;
+}
+
+static struct smp_operations bcm_smp_ops __initdata = {
+ .smp_prepare_cpus = bcm_smp_prepare_cpus,
+ .smp_boot_secondary = kona_boot_secondary,
+};
+CPU_METHOD_OF_DECLARE(bcm_smp_bcm281xx, "brcm,bcm11351-cpu-method",
+ &bcm_smp_ops);
+
+struct smp_operations nsp_smp_ops __initdata = {
+ .smp_prepare_cpus = bcm_smp_prepare_cpus,
+ .smp_secondary_init = nsp_secondary_init,
+ .smp_boot_secondary = nsp_boot_secondary,
+};
+CPU_METHOD_OF_DECLARE(bcm_smp_nsp, "brcm,bcm-nsp-smp", &nsp_smp_ops);