123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585 |
- From 55be958cd27439a58c4d9369d6fe2a1f83efdaa6 Mon Sep 17 00:00:00 2001
- From: Kapil Hali <kapilh@broadcom.com>
- Date: Sat, 5 Dec 2015 06:53:43 -0500
- Subject: [PATCH] ARM: BCM: Add SMP support for Broadcom NSP
- Add SMP support for Broadcom's Northstar Plus SoC
- cpu enable method. This changes also consolidates
- iProc family's - BCM NSP and BCM Kona, platform
- 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>
- Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
- ---
- arch/arm/mach-bcm/Kconfig | 2 +
- arch/arm/mach-bcm/Makefile | 8 +-
- arch/arm/mach-bcm/kona_smp.c | 228 ----------------------------------
- arch/arm/mach-bcm/platsmp.c | 290 +++++++++++++++++++++++++++++++++++++++++++
- 4 files changed, 298 insertions(+), 230 deletions(-)
- delete mode 100644 arch/arm/mach-bcm/kona_smp.c
- create mode 100644 arch/arm/mach-bcm/platsmp.c
- --- a/arch/arm/mach-bcm/Kconfig
- +++ b/arch/arm/mach-bcm/Kconfig
- @@ -40,6 +40,8 @@ config ARCH_BCM_NSP
- select ARCH_BCM_IPROC
- select ARM_ERRATA_754322
- select ARM_ERRATA_775420
- + select ARM_ERRATA_764369 if SMP
- + select HAVE_SMP
- help
- Support for Broadcom Northstar Plus SoC.
- Broadcom Northstar Plus family of SoCs are used for switching control
- --- a/arch/arm/mach-bcm/Makefile
- +++ b/arch/arm/mach-bcm/Makefile
- @@ -14,7 +14,11 @@
- obj-$(CONFIG_ARCH_BCM_CYGNUS) += bcm_cygnus.o
-
- # Northstar Plus
- -obj-$(CONFIG_ARCH_BCM_NSP) += bcm_nsp.o
- +obj-$(CONFIG_ARCH_BCM_NSP) += bcm_nsp.o
- +
- +ifeq ($(CONFIG_ARCH_BCM_NSP),y)
- +obj-$(CONFIG_SMP) += platsmp.o
- +endif
-
- # BCM281XX
- obj-$(CONFIG_ARCH_BCM_281XX) += board_bcm281xx.o
- @@ -23,7 +27,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
- --- a/arch/arm/mach-bcm/kona_smp.c
- +++ /dev/null
- @@ -1,228 +0,0 @@
- -/*
- - * 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/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"
- -#define MPIDR_CPUID_BITMASK 0x3
- -
- -/* I/O address of register used to coordinate secondary core startup */
- -static u32 secondary_boot_addr;
- -
- -/*
- - * 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 *cpus_node = NULL;
- - struct device_node *cpu_node = NULL;
- - int ret;
- -
- - /*
- - * 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.
- - */
- - cpus_node = of_find_node_by_path("/cpus");
- - if (!cpus_node)
- - return;
- -
- - for_each_child_of_node(cpus_node, cpu_node) {
- - u32 cpuid;
- -
- - if (of_node_cmp(cpu_node->type, "cpu"))
- - continue;
- -
- - if (of_property_read_u32(cpu_node, "reg", &cpuid)) {
- - pr_debug("%s: missing reg property\n",
- - cpu_node->full_name);
- - ret = -ENOENT;
- - goto out;
- - }
- -
- - /*
- - * "secondary-boot-reg" property should be defined only
- - * for secondary cpu
- - */
- - if ((cpuid & MPIDR_CPUID_BITMASK) == 1) {
- - /*
- - * 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(cpu_node,
- - OF_SECONDARY_BOOT,
- - &secondary_boot_addr)) {
- - pr_warn("%s: no" OF_SECONDARY_BOOT "property\n",
- - cpu_node->name);
- - ret = -ENOENT;
- - 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(cpu_node);
- - of_node_put(cpus_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_addr) {
- - pr_err("required secondary boot register not specified\n");
- - return -EINVAL;
- - }
- -
- - boot_reg = ioremap_nocache(
- - (phys_addr_t)secondary_boot_addr, 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 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);
- --- /dev/null
- +++ b/arch/arm/mach-bcm/platsmp.c
- @@ -0,0 +1,290 @@
- +/*
- + * 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>
- +
- +/* 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"
- +#define MPIDR_CPUID_BITMASK 0x3
- +
- +/* I/O address of register used to coordinate secondary core startup */
- +static u32 secondary_boot_addr;
- +
- +/*
- + * 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)
- +{
- + void __iomem *sku_rom_lut;
- + phys_addr_t secondary_startup_phy;
- +
- + if (!secondary_boot_addr) {
- + pr_warn("required secondary boot register not specified\n");
- + return -EINVAL;
- + }
- +
- + sku_rom_lut = ioremap_nocache((phys_addr_t)secondary_boot_addr,
- + sizeof(secondary_boot_addr));
- + 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 __init bcm_smp_prepare_cpus(unsigned int max_cpus)
- +{
- + static cpumask_t only_cpu_0 = { CPU_BITS_CPU0 };
- + struct device_node *cpus_node = NULL;
- + struct device_node *cpu_node = NULL;
- + int ret;
- +
- + /*
- + * 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.
- + */
- + cpus_node = of_find_node_by_path("/cpus");
- + if (!cpus_node)
- + return;
- +
- + for_each_child_of_node(cpus_node, cpu_node) {
- + u32 cpuid;
- +
- + if (of_node_cmp(cpu_node->type, "cpu"))
- + continue;
- +
- + if (of_property_read_u32(cpu_node, "reg", &cpuid)) {
- + pr_debug("%s: missing reg property\n",
- + cpu_node->full_name);
- + ret = -ENOENT;
- + goto out;
- + }
- +
- + /*
- + * "secondary-boot-reg" property should be defined only
- + * for secondary cpu
- + */
- + if ((cpuid & MPIDR_CPUID_BITMASK) == 1) {
- + /*
- + * 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(cpu_node,
- + OF_SECONDARY_BOOT,
- + &secondary_boot_addr)) {
- + pr_warn("%s: no" OF_SECONDARY_BOOT "property\n",
- + cpu_node->name);
- + ret = -ENOENT;
- + 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(cpu_node);
- + of_node_put(cpus_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_addr) {
- + pr_err("required secondary boot register not specified\n");
- + return -EINVAL;
- + }
- +
- + boot_reg = ioremap_nocache(
- + (phys_addr_t)secondary_boot_addr, 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)
- +{
- + int ret;
- +
- + /*
- + * After wake up, secondary core branches to the startup
- + * address programmed at SKU ROM LUT location.
- + */
- + ret = nsp_write_lut();
- + if (ret) {
- + pr_err("unable to write startup addr to SKU ROM LUT\n");
- + goto out;
- + }
- +
- + /* Send a CPU wakeup interrupt to the secondary core */
- + arch_send_wakeup_ipi_mask(cpumask_of(cpu));
- +
- +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_boot_secondary = nsp_boot_secondary,
- +};
- +CPU_METHOD_OF_DECLARE(bcm_smp_nsp, "brcm,bcm-nsp-smp", &nsp_smp_ops);
|