From 81b43a6e2d072126df5eb016524819f6921262f3 Mon Sep 17 00:00:00 2001 From: Marc Carino Date: Wed, 13 Aug 2014 15:50:02 -0700 Subject: ARM: brcmstb: add debug UART for earlyprintk support Add the UART definitions needed to support earlyprintk on brcmstb machines. Signed-off-by: Marc Carino Acked-by: Florian Fainelli Acked-by: Arnd Bergmann Signed-off-by: Brian Norris Signed-off-by: Florian Fainelli --- arch/arm/Kconfig.debug | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug index 03dc4c1a8736..6a4ee008b092 100644 --- a/arch/arm/Kconfig.debug +++ b/arch/arm/Kconfig.debug @@ -139,6 +139,17 @@ choice Say Y here if you want kernel low-level debugging support on Marvell Berlin SoC based platforms. + config DEBUG_BRCMSTB_UART + bool "Use BRCMSTB UART for low-level debug" + depends on ARCH_BRCMSTB + select DEBUG_UART_8250 + help + Say Y here if you want the debug print routines to direct + their output to the first serial port on these devices. + + If you have a Broadcom STB chip and would like early print + messages to appear over the UART, select this option. + config DEBUG_CLPS711X_UART1 bool "Kernel low-level debugging messages via UART1" depends on ARCH_CLPS711X @@ -1153,6 +1164,7 @@ config DEBUG_UART_PHYS default 0xe0000000 if ARCH_SPEAR13XX default 0xe4007000 if DEBUG_HIP04_UART default 0xf0000be0 if ARCH_EBSA110 + default 0xf040ab00 if DEBUG_BRCMSTB_UART default 0xf1012000 if DEBUG_MVEBU_UART_ALTERNATE default 0xf1012000 if ARCH_DOVE || ARCH_MV78XX0 || \ ARCH_ORION5X @@ -1204,6 +1216,7 @@ config DEBUG_UART_VIRT default 0xfb002000 if DEBUG_CNS3XXX default 0xfb009000 if DEBUG_REALVIEW_STD_PORT default 0xfb10c000 if DEBUG_REALVIEW_PB1176_PORT + default 0xfc40ab00 if DEBUG_BRCMSTB_UART default 0xfcfe8600 if DEBUG_UART_BCM63XX default 0xfd000000 if ARCH_SPEAR3XX || ARCH_SPEAR6XX default 0xfd000000 if ARCH_SPEAR13XX @@ -1260,7 +1273,8 @@ config DEBUG_UART_8250_WORD ARCH_KEYSTONE || \ DEBUG_DAVINCI_DMx_UART0 || DEBUG_DAVINCI_DA8XX_UART1 || \ DEBUG_DAVINCI_DA8XX_UART2 || \ - DEBUG_BCM_KONA_UART || DEBUG_RK32_UART2 + DEBUG_BCM_KONA_UART || DEBUG_RK32_UART2 || \ + DEBUG_BRCMSTB_UART config DEBUG_UART_8250_FLOW_CONTROL bool "Enable flow control for 8250 UART" -- cgit v1.2.3 From 62639c2f5332a0f25b11806ddcfe1d95d3d635fb Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 5 Sep 2014 16:31:04 -0700 Subject: ARM: brcmstb: reintroduce SMP support Support for SMP bringup of the B15 CPUs on Broadcom STB chips was added in commit 4fbe66d9903425156c193ae44c81c0f7557755c4 but was reverted in commit fc3e825fa91636a5d1b992e769b2d8279877bfad to address some late review comments. This reintroduces SMP support. Signed-off-by: Marc Carino Signed-off-by: Brian Norris Signed-off-by: Florian Fainelli --- arch/arm/mach-bcm/Makefile | 2 + arch/arm/mach-bcm/brcmstb.h | 19 +++ arch/arm/mach-bcm/headsmp-brcmstb.S | 33 ++++ arch/arm/mach-bcm/platsmp-brcmstb.c | 329 ++++++++++++++++++++++++++++++++++++ 4 files changed, 383 insertions(+) create mode 100644 arch/arm/mach-bcm/brcmstb.h create mode 100644 arch/arm/mach-bcm/headsmp-brcmstb.S create mode 100644 arch/arm/mach-bcm/platsmp-brcmstb.c diff --git a/arch/arm/mach-bcm/Makefile b/arch/arm/mach-bcm/Makefile index 300ae4b79ae6..6710ea321220 100644 --- a/arch/arm/mach-bcm/Makefile +++ b/arch/arm/mach-bcm/Makefile @@ -38,5 +38,7 @@ obj-$(CONFIG_ARCH_BCM_5301X) += bcm_5301x.o obj-$(CONFIG_ARCH_BCM_63XX) := bcm63xx.o ifeq ($(CONFIG_ARCH_BRCMSTB),y) +CFLAGS_platsmp-brcmstb.o += -march=armv7-a obj-y += brcmstb.o +obj-$(CONFIG_SMP) += headsmp-brcmstb.o platsmp-brcmstb.o endif 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 +#include +#include + + .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/platsmp-brcmstb.c b/arch/arm/mach-bcm/platsmp-brcmstb.c new file mode 100644 index 000000000000..31c87a284a34 --- /dev/null +++ b/arch/arm/mach-bcm/platsmp-brcmstb.c @@ -0,0 +1,329 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 +/* + * We must quiesce a dying CPU before it can be killed by the boot CPU. Because + * one or more cache may be disabled, we must flush to ensure coherency. We + * cannot use traditionl completion structures or spinlocks as they rely on + * coherency. + */ +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) +{ + dmb(); + per_cpu(per_cpu_sw_state, cpu) = val; + sync_cache_w(SHIFT_PERCPU_PTR(&per_cpu_sw_state, per_cpu_offset(cpu))); +} +#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) +{ + /* Mark this CPU as "up" */ + per_cpu_sw_state_wr(cpu, 1); + + /* + * 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; + + /* 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)); +} + +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); + + per_cpu_sw_state_wr(cpu, 0); + + /* Sit and wait to die */ + wfi(); + + /* We should never get here... */ + while (1) + ; +} + +static int brcmstb_cpu_kill(u32 cpu) +{ + u32 tmp; + + 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)); + + /* Flush pipeline before resetting CPU */ + mb(); + + /* 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: + 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: + 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 int brcmstb_boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + /* Missing the brcm,brcmstb-smpboot DT node? */ + if (!cpubiuctrl_block || !hif_cont_block) + return -ENODEV; + + /* 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); + + return 0; +} + +static struct smp_operations brcmstb_smp_ops __initdata = { + .smp_prepare_cpus = brcmstb_cpu_ctrl_setup, + .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); -- cgit v1.2.3 From 1aaaad3ffaec839929cc69992f419d656e18dc55 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 5 Sep 2014 16:25:37 -0700 Subject: ARM: brcmstb: Kconfig: drop unneeded symbol selections These are either implied or not necessary. Signed-off-by: Brian Norris Signed-off-by: Florian Fainelli --- arch/arm/mach-bcm/Kconfig | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig index 2abad742516d..9625dddb5cd3 100644 --- a/arch/arm/mach-bcm/Kconfig +++ b/arch/arm/mach-bcm/Kconfig @@ -118,10 +118,7 @@ config ARCH_BCM_63XX 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 -- cgit v1.2.3 From f1bee783dd37d088a8a7924205476ba1cf675378 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 18 Sep 2014 12:32:10 -0700 Subject: bus: brcmstb_gisb: register the fault code hook Commit 44127b771d9c31 ("bus: add Broadcom GISB bus arbiter timeout/error handler") added everything that is required to register an ARM fault handler for imprecise external aborts, except that there is nothing calling this currently. We do not need to export that specific function and have to update arch/arm/mach-bcm/brcmstb.c to call it, simply, register the fault handler during the probe() function of the driver. Signed-off-by: Florian Fainelli --- drivers/bus/brcmstb_gisb.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/bus/brcmstb_gisb.c b/drivers/bus/brcmstb_gisb.c index f2cd6a2d40b4..41b09418f515 100644 --- a/drivers/bus/brcmstb_gisb.c +++ b/drivers/bus/brcmstb_gisb.c @@ -160,12 +160,6 @@ static int brcmstb_bus_error_handler(unsigned long addr, unsigned int fsr, return ret; } -void __init brcmstb_hook_fault_code(void) -{ - hook_fault_code(22, brcmstb_bus_error_handler, SIGBUS, 0, - "imprecise external abort"); -} - static irqreturn_t brcmstb_gisb_timeout_handler(int irq, void *dev_id) { brcmstb_gisb_arb_decode_addr(dev_id, "timeout"); @@ -261,6 +255,9 @@ static int brcmstb_gisb_arb_probe(struct platform_device *pdev) list_add_tail(&gdev->next, &brcmstb_gisb_arb_device_list); + hook_fault_code(22, brcmstb_bus_error_handler, SIGBUS, 0, + "imprecise external abort"); + dev_info(&pdev->dev, "registered mem: %p, irqs: %d, %d\n", gdev->base, timeout_irq, tea_irq); -- cgit v1.2.3 From 203bb85ed605e43eadca62afb3a8cd128a8ec10a Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 18 Sep 2014 12:37:14 -0700 Subject: bus: brcmstb_gisb: save and restore GISB timeout When the system enters S3, we will lose the GISB timeout value we have configured, make sure that we do save this timeout value, and restore this timeout value prior to re-enabling interrupts such that the GISB timeout interrupt will fire with the expected timeout. Signed-off-by: Florian Fainelli --- drivers/bus/brcmstb_gisb.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/bus/brcmstb_gisb.c b/drivers/bus/brcmstb_gisb.c index 41b09418f515..e7ccd21a45c9 100644 --- a/drivers/bus/brcmstb_gisb.c +++ b/drivers/bus/brcmstb_gisb.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -48,6 +49,7 @@ struct brcmstb_gisb_arb_device { struct list_head next; u32 valid_mask; const char *master_names[sizeof(u32) * BITS_PER_BYTE]; + u32 saved_timeout; }; static LIST_HEAD(brcmstb_gisb_arb_device_list); @@ -264,6 +266,39 @@ static int brcmstb_gisb_arb_probe(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int brcmstb_gisb_arb_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev); + + gdev->saved_timeout = ioread32(gdev->base + ARB_TIMER); + + return 0; +} + +/* Make sure we provide the same timeout value that was configured before, and + * do this before the GISB timeout interrupt handler has any chance to run. + */ +static int brcmstb_gisb_arb_resume_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev); + + iowrite32(gdev->saved_timeout, gdev->base + ARB_TIMER); + + return 0; +} +#else +#define brcmstb_gisb_arb_suspend NULL +#define brcmstb_gisb_arb_resume_noirq NULL +#endif + +static const struct dev_pm_ops brcmstb_gisb_arb_pm_ops = { + .suspend = brcmstb_gisb_arb_suspend, + .resume_noirq = brcmstb_gisb_arb_resume_noirq, +}; + static const struct of_device_id brcmstb_gisb_arb_of_match[] = { { .compatible = "brcm,gisb-arb" }, { }, @@ -275,6 +310,7 @@ static struct platform_driver brcmstb_gisb_arb_driver = { .name = "brcm-gisb-arb", .owner = THIS_MODULE, .of_match_table = brcmstb_gisb_arb_of_match, + .pm = &brcmstb_gisb_arb_pm_ops, }, }; -- cgit v1.2.3