summary refs log tree commit diff
path: root/arch/arm/mach-bcm
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-08-08 11:14:29 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2014-08-08 11:14:29 -0700
commitb3345d7c57d70e6cb6749af25cdbe80515582e99 (patch)
tree04cce706bc7e944ad1fb257108a8ae735948f97f /arch/arm/mach-bcm
parent44c916d58b9ef1f2c4aec2def57fa8289c716a60 (diff)
parentc2fff85e21818952aa0ee5778926beee6c03e579 (diff)
downloadlinux-b3345d7c57d70e6cb6749af25cdbe80515582e99.tar.gz
Merge tag 'soc-for-3.17' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC platform changes from Olof Johansson:
 "This is the bulk of new SoC enablement and other platform changes for
  3.17:

   - Samsung S5PV210 has been converted to DT and multiplatform
   - Clock drivers and bindings for some of the lower-end i.MX 1/2
     platforms
   - Kirkwood, one of the popular Marvell platforms, is folded into the
     mvebu platform code, removing mach-kirkwood
   - Hwmod data for TI AM43xx and DRA7 platforms
   - More additions of Renesas shmobile platform support
   - Removal of plat-samsung contents that can be removed with S5PV210
     being multiplatform/DT-enabled and the other two old platforms
     being removed

  New platforms (most with only basic support right now):

   - Hisilicon X5HD2 settop box chipset is introduced
   - Mediatek MT6589 (mobile chipset) is introduced
   - Broadcom BCM7xxx settop box chipset is introduced

  + as usual a lot other pieces all over the platform code"

* tag 'soc-for-3.17' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (240 commits)
  ARM: hisi: remove smp from machine descriptor
  power: reset: move hisilicon reboot code
  ARM: dts: Add hix5hd2-dkb dts file.
  ARM: debug: Rename Hi3716 to HIX5HD2
  ARM: hisi: enable hix5hd2 SoC
  ARM: hisi: add ARCH_HISI
  MAINTAINERS: add entry for Broadcom ARM STB architecture
  ARM: brcmstb: select GISB arbiter and interrupt drivers
  ARM: brcmstb: add infrastructure for ARM-based Broadcom STB SoCs
  ARM: configs: enable SMP in bcm_defconfig
  ARM: add SMP support for Broadcom mobile SoCs
  Documentation: arm: misc updates to Marvell EBU SoC status
  Documentation: arm: add URLs to public datasheets for the Marvell Armada XP SoC
  ARM: mvebu: fix build without platforms selected
  ARM: mvebu: add cpuidle support for Armada 38x
  ARM: mvebu: add cpuidle support for Armada 370
  cpuidle: mvebu: add Armada 38x support
  cpuidle: mvebu: add Armada 370 support
  cpuidle: mvebu: rename the driver from armada-370-xp to mvebu-v7
  ARM: mvebu: export the SCU address
  ...
Diffstat (limited to 'arch/arm/mach-bcm')
-rw-r--r--arch/arm/mach-bcm/Kconfig34
-rw-r--r--arch/arm/mach-bcm/Makefile8
-rw-r--r--arch/arm/mach-bcm/brcmstb.c28
-rw-r--r--arch/arm/mach-bcm/brcmstb.h19
-rw-r--r--arch/arm/mach-bcm/headsmp-brcmstb.S33
-rw-r--r--arch/arm/mach-bcm/kona_smp.c202
-rw-r--r--arch/arm/mach-bcm/platsmp-brcmstb.c363
7 files changed, 684 insertions, 3 deletions
diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig
index 41c839167e87..fc938005ad39 100644
--- a/arch/arm/mach-bcm/Kconfig
+++ b/arch/arm/mach-bcm/Kconfig
@@ -9,7 +9,6 @@ config ARCH_BCM_MOBILE
 	bool "Broadcom Mobile SoC Support" if ARCH_MULTI_V7
 	select ARCH_REQUIRE_GPIOLIB
 	select ARM_ERRATA_754322
-	select ARM_ERRATA_764369 if SMP
 	select ARM_ERRATA_775420
 	select ARM_GIC
 	select GPIO_BCM_KONA
@@ -26,16 +25,18 @@ menu "Broadcom Mobile SoC Selection"
 config ARCH_BCM_281XX
 	bool "Broadcom BCM281XX SoC family"
 	default y
+	select HAVE_SMP
 	help
-	  Enable support for the the BCM281XX family, which includes
+	  Enable support for the BCM281XX family, which includes
 	  BCM11130, BCM11140, BCM11351, BCM28145 and BCM28155
 	  variants.
 
 config ARCH_BCM_21664
 	bool "Broadcom BCM21664 SoC family"
 	default y
+	select HAVE_SMP
 	help
-	  Enable support for the the BCM21664 family, which includes
+	  Enable support for the BCM21664 family, which includes
 	  BCM21663 and BCM21664 variants.
 
 config ARCH_BCM_MOBILE_L2_CACHE
@@ -49,6 +50,17 @@ config ARCH_BCM_MOBILE_SMC
 	bool
 	depends on ARCH_BCM_281XX || ARCH_BCM_21664
 
+config ARCH_BCM_MOBILE_SMP
+	bool "Broadcom mobile SoC SMP support"
+	depends on (ARCH_BCM_281XX || ARCH_BCM_21664) && SMP
+	default y
+	select HAVE_ARM_SCU
+	select ARM_ERRATA_764369
+	help
+	  SMP support for the BCM281XX and BCM21664 SoC families.
+	  Provided as an option so SMP support for SoCs of this type
+	  can be disabled for an SMP-enabled kernel.
+
 endmenu
 
 endif
@@ -87,4 +99,20 @@ config ARCH_BCM_5301X
 	  different SoC or with the older BCM47XX and BCM53XX based
 	  network SoC using a MIPS CPU, they are supported by arch/mips/bcm47xx
 
+config ARCH_BRCMSTB
+	bool "Broadcom BCM7XXX based boards" if ARCH_MULTI_V7
+	depends on MMU
+	select ARM_GIC
+	select MIGHT_HAVE_PCI
+	select HAVE_SMP
+	select HAVE_ARM_ARCH_TIMER
+	select BRCMSTB_GISB_ARB
+	select BRCMSTB_L2_IRQ
+	help
+	  Say Y if you intend to run the kernel on a Broadcom ARM-based STB
+	  chipset.
+
+	  This enables support for Broadcom ARM-based set-top box chipsets,
+	  including the 7445 family of chips.
+
 endif
diff --git a/arch/arm/mach-bcm/Makefile b/arch/arm/mach-bcm/Makefile
index 731292114975..67c492aabf4d 100644
--- a/arch/arm/mach-bcm/Makefile
+++ b/arch/arm/mach-bcm/Makefile
@@ -16,6 +16,9 @@ obj-$(CONFIG_ARCH_BCM_281XX)	+= board_bcm281xx.o
 # BCM21664
 obj-$(CONFIG_ARCH_BCM_21664)	+= board_bcm21664.o
 
+# BCM281XX and BCM21664 SMP support
+obj-$(CONFIG_ARCH_BCM_MOBILE_SMP) += kona_smp.o
+
 # BCM281XX and BCM21664 L2 cache control
 obj-$(CONFIG_ARCH_BCM_MOBILE_L2_CACHE) += kona_l2_cache.o
 
@@ -30,3 +33,8 @@ obj-$(CONFIG_ARCH_BCM2835)	+= board_bcm2835.o
 
 # BCM5301X
 obj-$(CONFIG_ARCH_BCM_5301X)	+= bcm_5301x.o
+
+ifeq ($(CONFIG_ARCH_BRCMSTB),y)
+obj-y				+= brcmstb.o
+obj-$(CONFIG_SMP)		+= headsmp-brcmstb.o platsmp-brcmstb.o
+endif
diff --git a/arch/arm/mach-bcm/brcmstb.c b/arch/arm/mach-bcm/brcmstb.c
new file mode 100644
index 000000000000..60a5afa06ed7
--- /dev/null
+++ b/arch/arm/mach-bcm/brcmstb.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2013-2014 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/init.h>
+#include <linux/of_platform.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
+static const char *brcmstb_match[] __initconst = {
+	"brcm,bcm7445",
+	"brcm,brcmstb",
+	NULL
+};
+
+DT_MACHINE_START(BRCMSTB, "Broadcom STB (Flattened Device Tree)")
+	.dt_compat	= brcmstb_match,
+MACHINE_END
diff --git a/arch/arm/mach-bcm/brcmstb.h b/arch/arm/mach-bcm/brcmstb.h
new file mode 100644
index 000000000000..ec0c3d112b36
--- /dev/null
+++ b/arch/arm/mach-bcm/brcmstb.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2013-2014 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 __BRCMSTB_H__
+#define __BRCMSTB_H__
+
+void brcmstb_secondary_startup(void);
+
+#endif /* __BRCMSTB_H__ */
diff --git a/arch/arm/mach-bcm/headsmp-brcmstb.S b/arch/arm/mach-bcm/headsmp-brcmstb.S
new file mode 100644
index 000000000000..199c1ea58248
--- /dev/null
+++ b/arch/arm/mach-bcm/headsmp-brcmstb.S
@@ -0,0 +1,33 @@
+/*
+ * SMP boot code for secondary CPUs
+ * Based on arch/arm/mach-tegra/headsmp.S
+ *
+ * Copyright (C) 2010 NVIDIA, Inc.
+ * Copyright (C) 2013-2014 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 <asm/assembler.h>
+#include <linux/linkage.h>
+#include <linux/init.h>
+
+        .section ".text.head", "ax"
+
+ENTRY(brcmstb_secondary_startup)
+        /*
+         * Ensure CPU is in a sane state by disabling all IRQs and switching
+         * into SVC mode.
+         */
+        setmode	PSR_I_BIT | PSR_F_BIT | SVC_MODE, r0
+
+        bl      v7_invalidate_l1
+        b       secondary_startup
+ENDPROC(brcmstb_secondary_startup)
diff --git a/arch/arm/mach-bcm/kona_smp.c b/arch/arm/mach-bcm/kona_smp.c
new file mode 100644
index 000000000000..66a0465528a5
--- /dev/null
+++ b/arch/arm/mach-bcm/kona_smp.c
@@ -0,0 +1,202 @@
+/*
+ * 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);
diff --git a/arch/arm/mach-bcm/platsmp-brcmstb.c b/arch/arm/mach-bcm/platsmp-brcmstb.c
new file mode 100644
index 000000000000..af780e9c23a6
--- /dev/null
+++ b/arch/arm/mach-bcm/platsmp-brcmstb.c
@@ -0,0 +1,363 @@
+/*
+ * Broadcom STB CPU SMP and hotplug support for ARM
+ *
+ * Copyright (C) 2013-2014 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/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/printk.h>
+#include <linux/regmap.h>
+#include <linux/smp.h>
+#include <linux/mfd/syscon.h>
+#include <linux/spinlock.h>
+
+#include <asm/cacheflush.h>
+#include <asm/cp15.h>
+#include <asm/mach-types.h>
+#include <asm/smp_plat.h>
+
+#include "brcmstb.h"
+
+enum {
+	ZONE_MAN_CLKEN_MASK		= BIT(0),
+	ZONE_MAN_RESET_CNTL_MASK	= BIT(1),
+	ZONE_MAN_MEM_PWR_MASK		= BIT(4),
+	ZONE_RESERVED_1_MASK		= BIT(5),
+	ZONE_MAN_ISO_CNTL_MASK		= BIT(6),
+	ZONE_MANUAL_CONTROL_MASK	= BIT(7),
+	ZONE_PWR_DN_REQ_MASK		= BIT(9),
+	ZONE_PWR_UP_REQ_MASK		= BIT(10),
+	ZONE_BLK_RST_ASSERT_MASK	= BIT(12),
+	ZONE_PWR_OFF_STATE_MASK		= BIT(25),
+	ZONE_PWR_ON_STATE_MASK		= BIT(26),
+	ZONE_DPG_PWR_STATE_MASK		= BIT(28),
+	ZONE_MEM_PWR_STATE_MASK		= BIT(29),
+	ZONE_RESET_STATE_MASK		= BIT(31),
+	CPU0_PWR_ZONE_CTRL_REG		= 1,
+	CPU_RESET_CONFIG_REG		= 2,
+};
+
+static void __iomem *cpubiuctrl_block;
+static void __iomem *hif_cont_block;
+static u32 cpu0_pwr_zone_ctrl_reg;
+static u32 cpu_rst_cfg_reg;
+static u32 hif_cont_reg;
+
+#ifdef CONFIG_HOTPLUG_CPU
+static DEFINE_PER_CPU_ALIGNED(int, per_cpu_sw_state);
+
+static int per_cpu_sw_state_rd(u32 cpu)
+{
+	sync_cache_r(SHIFT_PERCPU_PTR(&per_cpu_sw_state, per_cpu_offset(cpu)));
+	return per_cpu(per_cpu_sw_state, cpu);
+}
+
+static void per_cpu_sw_state_wr(u32 cpu, int val)
+{
+	per_cpu(per_cpu_sw_state, cpu) = val;
+	dmb();
+	sync_cache_w(SHIFT_PERCPU_PTR(&per_cpu_sw_state, per_cpu_offset(cpu)));
+	dsb_sev();
+}
+#else
+static inline void per_cpu_sw_state_wr(u32 cpu, int val) { }
+#endif
+
+static void __iomem *pwr_ctrl_get_base(u32 cpu)
+{
+	void __iomem *base = cpubiuctrl_block + cpu0_pwr_zone_ctrl_reg;
+	base += (cpu_logical_map(cpu) * 4);
+	return base;
+}
+
+static u32 pwr_ctrl_rd(u32 cpu)
+{
+	void __iomem *base = pwr_ctrl_get_base(cpu);
+	return readl_relaxed(base);
+}
+
+static void pwr_ctrl_wr(u32 cpu, u32 val)
+{
+	void __iomem *base = pwr_ctrl_get_base(cpu);
+	writel(val, base);
+}
+
+static void cpu_rst_cfg_set(u32 cpu, int set)
+{
+	u32 val;
+	val = readl_relaxed(cpubiuctrl_block + cpu_rst_cfg_reg);
+	if (set)
+		val |= BIT(cpu_logical_map(cpu));
+	else
+		val &= ~BIT(cpu_logical_map(cpu));
+	writel_relaxed(val, cpubiuctrl_block + cpu_rst_cfg_reg);
+}
+
+static void cpu_set_boot_addr(u32 cpu, unsigned long boot_addr)
+{
+	const int reg_ofs = cpu_logical_map(cpu) * 8;
+	writel_relaxed(0, hif_cont_block + hif_cont_reg + reg_ofs);
+	writel_relaxed(boot_addr, hif_cont_block + hif_cont_reg + 4 + reg_ofs);
+}
+
+static void brcmstb_cpu_boot(u32 cpu)
+{
+	pr_info("SMP: Booting CPU%d...\n", cpu);
+
+	/*
+	 * set the reset vector to point to the secondary_startup
+	 * routine
+	 */
+	cpu_set_boot_addr(cpu, virt_to_phys(brcmstb_secondary_startup));
+
+	/* unhalt the cpu */
+	cpu_rst_cfg_set(cpu, 0);
+}
+
+static void brcmstb_cpu_power_on(u32 cpu)
+{
+	/*
+	 * The secondary cores power was cut, so we must go through
+	 * power-on initialization.
+	 */
+	u32 tmp;
+
+	pr_info("SMP: Powering up CPU%d...\n", cpu);
+
+	/* Request zone power up */
+	pwr_ctrl_wr(cpu, ZONE_PWR_UP_REQ_MASK);
+
+	/* Wait for the power up FSM to complete */
+	do {
+		tmp = pwr_ctrl_rd(cpu);
+	} while (!(tmp & ZONE_PWR_ON_STATE_MASK));
+
+	per_cpu_sw_state_wr(cpu, 1);
+}
+
+static int brcmstb_cpu_get_power_state(u32 cpu)
+{
+	int tmp = pwr_ctrl_rd(cpu);
+	return (tmp & ZONE_RESET_STATE_MASK) ? 0 : 1;
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+
+static void brcmstb_cpu_die(u32 cpu)
+{
+	v7_exit_coherency_flush(all);
+
+	/* Prevent all interrupts from reaching this CPU. */
+	arch_local_irq_disable();
+
+	/*
+	 * Final full barrier to ensure everything before this instruction has
+	 * quiesced.
+	 */
+	isb();
+	dsb();
+
+	per_cpu_sw_state_wr(cpu, 0);
+
+	/* Sit and wait to die */
+	wfi();
+
+	/* We should never get here... */
+	panic("Spurious interrupt on CPU %d received!\n", cpu);
+}
+
+static int brcmstb_cpu_kill(u32 cpu)
+{
+	u32 tmp;
+
+	pr_info("SMP: Powering down CPU%d...\n", cpu);
+
+	while (per_cpu_sw_state_rd(cpu))
+		;
+
+	/* Program zone reset */
+	pwr_ctrl_wr(cpu, ZONE_RESET_STATE_MASK | ZONE_BLK_RST_ASSERT_MASK |
+			      ZONE_PWR_DN_REQ_MASK);
+
+	/* Verify zone reset */
+	tmp = pwr_ctrl_rd(cpu);
+	if (!(tmp & ZONE_RESET_STATE_MASK))
+		pr_err("%s: Zone reset bit for CPU %d not asserted!\n",
+			__func__, cpu);
+
+	/* Wait for power down */
+	do {
+		tmp = pwr_ctrl_rd(cpu);
+	} while (!(tmp & ZONE_PWR_OFF_STATE_MASK));
+
+	/* Settle-time from Broadcom-internal DVT reference code */
+	udelay(7);
+
+	/* Assert reset on the CPU */
+	cpu_rst_cfg_set(cpu, 1);
+
+	return 1;
+}
+
+#endif /* CONFIG_HOTPLUG_CPU */
+
+static int __init setup_hifcpubiuctrl_regs(struct device_node *np)
+{
+	int rc = 0;
+	char *name;
+	struct device_node *syscon_np = NULL;
+
+	name = "syscon-cpu";
+
+	syscon_np = of_parse_phandle(np, name, 0);
+	if (!syscon_np) {
+		pr_err("can't find phandle %s\n", name);
+		rc = -EINVAL;
+		goto cleanup;
+	}
+
+	cpubiuctrl_block = of_iomap(syscon_np, 0);
+	if (!cpubiuctrl_block) {
+		pr_err("iomap failed for cpubiuctrl_block\n");
+		rc = -EINVAL;
+		goto cleanup;
+	}
+
+	rc = of_property_read_u32_index(np, name, CPU0_PWR_ZONE_CTRL_REG,
+					&cpu0_pwr_zone_ctrl_reg);
+	if (rc) {
+		pr_err("failed to read 1st entry from %s property (%d)\n", name,
+			rc);
+		rc = -EINVAL;
+		goto cleanup;
+	}
+
+	rc = of_property_read_u32_index(np, name, CPU_RESET_CONFIG_REG,
+					&cpu_rst_cfg_reg);
+	if (rc) {
+		pr_err("failed to read 2nd entry from %s property (%d)\n", name,
+			rc);
+		rc = -EINVAL;
+		goto cleanup;
+	}
+
+cleanup:
+	if (syscon_np)
+		of_node_put(syscon_np);
+
+	return rc;
+}
+
+static int __init setup_hifcont_regs(struct device_node *np)
+{
+	int rc = 0;
+	char *name;
+	struct device_node *syscon_np = NULL;
+
+	name = "syscon-cont";
+
+	syscon_np = of_parse_phandle(np, name, 0);
+	if (!syscon_np) {
+		pr_err("can't find phandle %s\n", name);
+		rc = -EINVAL;
+		goto cleanup;
+	}
+
+	hif_cont_block = of_iomap(syscon_np, 0);
+	if (!hif_cont_block) {
+		pr_err("iomap failed for hif_cont_block\n");
+		rc = -EINVAL;
+		goto cleanup;
+	}
+
+	/* offset is at top of hif_cont_block */
+	hif_cont_reg = 0;
+
+cleanup:
+	if (syscon_np)
+		of_node_put(syscon_np);
+
+	return rc;
+}
+
+static void __init brcmstb_cpu_ctrl_setup(unsigned int max_cpus)
+{
+	int rc;
+	struct device_node *np;
+	char *name;
+
+	name = "brcm,brcmstb-smpboot";
+	np = of_find_compatible_node(NULL, NULL, name);
+	if (!np) {
+		pr_err("can't find compatible node %s\n", name);
+		return;
+	}
+
+	rc = setup_hifcpubiuctrl_regs(np);
+	if (rc)
+		return;
+
+	rc = setup_hifcont_regs(np);
+	if (rc)
+		return;
+}
+
+static DEFINE_SPINLOCK(boot_lock);
+
+static void brcmstb_secondary_init(unsigned int cpu)
+{
+	/*
+	 * Synchronise with the boot thread.
+	 */
+	spin_lock(&boot_lock);
+	spin_unlock(&boot_lock);
+}
+
+static int brcmstb_boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+	/*
+	 * set synchronisation state between this boot processor
+	 * and the secondary one
+	 */
+	spin_lock(&boot_lock);
+
+	/* Bring up power to the core if necessary */
+	if (brcmstb_cpu_get_power_state(cpu) == 0)
+		brcmstb_cpu_power_on(cpu);
+
+	brcmstb_cpu_boot(cpu);
+
+	/*
+	 * now the secondary core is starting up let it run its
+	 * calibrations, then wait for it to finish
+	 */
+	spin_unlock(&boot_lock);
+
+	return 0;
+}
+
+static struct smp_operations brcmstb_smp_ops __initdata = {
+	.smp_prepare_cpus	= brcmstb_cpu_ctrl_setup,
+	.smp_secondary_init	= brcmstb_secondary_init,
+	.smp_boot_secondary	= brcmstb_boot_secondary,
+#ifdef CONFIG_HOTPLUG_CPU
+	.cpu_kill		= brcmstb_cpu_kill,
+	.cpu_die		= brcmstb_cpu_die,
+#endif
+};
+
+CPU_METHOD_OF_DECLARE(brcmstb_smp, "brcm,brahma-b15", &brcmstb_smp_ops);