ARM: mach-bcm: soc updates for 3.17
- BCM Mobile SMP support - BRCM STB platform support -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJT1mChAAoJEOfTILNwq7R4DDYQAJudlSqjwerAjQZwcdoe/xmP 74LjnWeCU3l1PhvtTynjPz3YWp+6VsHPDiHwJ00/ZTotCaa3Nl+zs5EbKs4aDeIb R6YvvIJ4bmAaStmiWV32gM9UzC+4VGV8RsI8zbsbLFLhh6ChMYs47xTyLFeXd9Ks 999CW2T+TIQmvJHhD9hmpVFf6s+TWeDpS1VKagPeZAz1tTB7XTqKItQNalrobi+b j4ak0Qsg/cYKjjG48320I2LuuXvUBZuW1nGLJ6sR90MKj1fWvUnwlJGv0Fyze1eF B1mTBD5OznZyCOJqOUA8zXxesDFoCcPSjq4rrBgCBYu8KLTCeC7ZXUPiuYLihs1B I64y7JzQThTqJghORgOb9oRq+DRHmO2hN6Ssfh41uJIJk9T6tssG3/qcwW6sdJPR r64VknaQR3yyWTOWsCpWQwjY0VgTUvtH5WjVAnnx/MZzp/P8JDDYftYh4TPXnIu+ fhHPl6P1NoFm4hV6cYUNd1dT+tDACJDVOBytxRqgJ6BpcCOyPbe8aODDHES1ZOT7 XVTO0NSrO2ywbDb7JA/OteEHy1Ql7BL85Dx0+AK3WCsgOrZ7YjoODo2/5qr+I8p0 0hyYP+NZNljCrnXy0j4F1DNjJq1saVp1LS03ye9RqclsAdjqsrze9cmupZLNAj5o qzDzm8AMGqRYBrM2gVfJ =lUq9 -----END PGP SIGNATURE----- Merge tag 'for-3.17/bcm-soc' of git://github.com/broadcom/mach-bcm into next/soc Merge "ARM: mach-bcm: soc updates for 3.17" from Matt Porter: - BCM Mobile SMP support - BRCM STB platform support * tag 'for-3.17/bcm-soc' of git://github.com/broadcom/mach-bcm: 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 Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
commit
21b9554e7b
|
@ -1978,6 +1978,14 @@ F: arch/arm/mach-bcm/bcm_5301x.c
|
|||
F: arch/arm/boot/dts/bcm5301x.dtsi
|
||||
F: arch/arm/boot/dts/bcm470*
|
||||
|
||||
BROADCOM BCM7XXX ARM ARCHITECTURE
|
||||
M: Marc Carino <marc.ceeeee@gmail.com>
|
||||
M: Brian Norris <computersforpeace@gmail.com>
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
F: arch/arm/mach-bcm/*brcmstb*
|
||||
F: arch/arm/boot/dts/bcm7*.dts*
|
||||
|
||||
BROADCOM TG3 GIGABIT ETHERNET DRIVER
|
||||
M: Nithin Nayak Sujir <nsujir@broadcom.com>
|
||||
M: Michael Chan <mchan@broadcom.com>
|
||||
|
|
|
@ -27,6 +27,7 @@ CONFIG_PARTITION_ADVANCED=y
|
|||
CONFIG_ARCH_BCM=y
|
||||
CONFIG_ARCH_BCM_MOBILE=y
|
||||
CONFIG_ARM_THUMBEE=y
|
||||
CONFIG_SMP=y
|
||||
CONFIG_PREEMPT=y
|
||||
CONFIG_AEABI=y
|
||||
# CONFIG_COMPACTION is not set
|
||||
|
|
|
@ -19,6 +19,7 @@ CONFIG_MACH_DOVE=y
|
|||
CONFIG_ARCH_BCM=y
|
||||
CONFIG_ARCH_BCM_MOBILE=y
|
||||
CONFIG_ARCH_BCM_5301X=y
|
||||
CONFIG_ARCH_BRCMSTB=y
|
||||
CONFIG_ARCH_BERLIN=y
|
||||
CONFIG_MACH_BERLIN_BG2=y
|
||||
CONFIG_MACH_BERLIN_BG2CD=y
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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__ */
|
|
@ -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)
|
|
@ -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);
|
|
@ -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);
|
Loading…
Reference in New Issue