diff options
-rw-r--r-- | MAINTAINERS | 8 | ||||
-rw-r--r-- | arch/arm/configs/bcm_defconfig | 1 | ||||
-rw-r--r-- | arch/arm/configs/multi_v7_defconfig | 1 | ||||
-rw-r--r-- | arch/arm/mach-bcm/Kconfig | 34 | ||||
-rw-r--r-- | arch/arm/mach-bcm/Makefile | 8 | ||||
-rw-r--r-- | arch/arm/mach-bcm/brcmstb.c | 28 | ||||
-rw-r--r-- | arch/arm/mach-bcm/brcmstb.h | 19 | ||||
-rw-r--r-- | arch/arm/mach-bcm/headsmp-brcmstb.S | 33 | ||||
-rw-r--r-- | arch/arm/mach-bcm/kona_smp.c | 202 | ||||
-rw-r--r-- | arch/arm/mach-bcm/platsmp-brcmstb.c | 363 |
10 files changed, 694 insertions, 3 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index fb784e03db0d..5d3b0ccaa90a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -1978,6 +1978,14 @@ F: arch/arm/mach-bcm/bcm_5301x.c | |||
1978 | F: arch/arm/boot/dts/bcm5301x.dtsi | 1978 | F: arch/arm/boot/dts/bcm5301x.dtsi |
1979 | F: arch/arm/boot/dts/bcm470* | 1979 | F: arch/arm/boot/dts/bcm470* |
1980 | 1980 | ||
1981 | BROADCOM BCM7XXX ARM ARCHITECTURE | ||
1982 | M: Marc Carino <marc.ceeeee@gmail.com> | ||
1983 | M: Brian Norris <computersforpeace@gmail.com> | ||
1984 | L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) | ||
1985 | S: Maintained | ||
1986 | F: arch/arm/mach-bcm/*brcmstb* | ||
1987 | F: arch/arm/boot/dts/bcm7*.dts* | ||
1988 | |||
1981 | BROADCOM TG3 GIGABIT ETHERNET DRIVER | 1989 | BROADCOM TG3 GIGABIT ETHERNET DRIVER |
1982 | M: Nithin Nayak Sujir <nsujir@broadcom.com> | 1990 | M: Nithin Nayak Sujir <nsujir@broadcom.com> |
1983 | M: Michael Chan <mchan@broadcom.com> | 1991 | M: Michael Chan <mchan@broadcom.com> |
diff --git a/arch/arm/configs/bcm_defconfig b/arch/arm/configs/bcm_defconfig index 4bf72264b175..fbebcbce1e8c 100644 --- a/arch/arm/configs/bcm_defconfig +++ b/arch/arm/configs/bcm_defconfig | |||
@@ -27,6 +27,7 @@ CONFIG_PARTITION_ADVANCED=y | |||
27 | CONFIG_ARCH_BCM=y | 27 | CONFIG_ARCH_BCM=y |
28 | CONFIG_ARCH_BCM_MOBILE=y | 28 | CONFIG_ARCH_BCM_MOBILE=y |
29 | CONFIG_ARM_THUMBEE=y | 29 | CONFIG_ARM_THUMBEE=y |
30 | CONFIG_SMP=y | ||
30 | CONFIG_PREEMPT=y | 31 | CONFIG_PREEMPT=y |
31 | CONFIG_AEABI=y | 32 | CONFIG_AEABI=y |
32 | # CONFIG_COMPACTION is not set | 33 | # CONFIG_COMPACTION is not set |
diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index f70ea2116fec..3332a4231684 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig | |||
@@ -19,6 +19,7 @@ CONFIG_MACH_DOVE=y | |||
19 | CONFIG_ARCH_BCM=y | 19 | CONFIG_ARCH_BCM=y |
20 | CONFIG_ARCH_BCM_MOBILE=y | 20 | CONFIG_ARCH_BCM_MOBILE=y |
21 | CONFIG_ARCH_BCM_5301X=y | 21 | CONFIG_ARCH_BCM_5301X=y |
22 | CONFIG_ARCH_BRCMSTB=y | ||
22 | CONFIG_ARCH_BERLIN=y | 23 | CONFIG_ARCH_BERLIN=y |
23 | CONFIG_MACH_BERLIN_BG2=y | 24 | CONFIG_MACH_BERLIN_BG2=y |
24 | CONFIG_MACH_BERLIN_BG2CD=y | 25 | CONFIG_MACH_BERLIN_BG2CD=y |
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 | |||
9 | bool "Broadcom Mobile SoC Support" if ARCH_MULTI_V7 | 9 | bool "Broadcom Mobile SoC Support" if ARCH_MULTI_V7 |
10 | select ARCH_REQUIRE_GPIOLIB | 10 | select ARCH_REQUIRE_GPIOLIB |
11 | select ARM_ERRATA_754322 | 11 | select ARM_ERRATA_754322 |
12 | select ARM_ERRATA_764369 if SMP | ||
13 | select ARM_ERRATA_775420 | 12 | select ARM_ERRATA_775420 |
14 | select ARM_GIC | 13 | select ARM_GIC |
15 | select GPIO_BCM_KONA | 14 | select GPIO_BCM_KONA |
@@ -26,16 +25,18 @@ menu "Broadcom Mobile SoC Selection" | |||
26 | config ARCH_BCM_281XX | 25 | config ARCH_BCM_281XX |
27 | bool "Broadcom BCM281XX SoC family" | 26 | bool "Broadcom BCM281XX SoC family" |
28 | default y | 27 | default y |
28 | select HAVE_SMP | ||
29 | help | 29 | help |
30 | Enable support for the the BCM281XX family, which includes | 30 | Enable support for the BCM281XX family, which includes |
31 | BCM11130, BCM11140, BCM11351, BCM28145 and BCM28155 | 31 | BCM11130, BCM11140, BCM11351, BCM28145 and BCM28155 |
32 | variants. | 32 | variants. |
33 | 33 | ||
34 | config ARCH_BCM_21664 | 34 | config ARCH_BCM_21664 |
35 | bool "Broadcom BCM21664 SoC family" | 35 | bool "Broadcom BCM21664 SoC family" |
36 | default y | 36 | default y |
37 | select HAVE_SMP | ||
37 | help | 38 | help |
38 | Enable support for the the BCM21664 family, which includes | 39 | Enable support for the BCM21664 family, which includes |
39 | BCM21663 and BCM21664 variants. | 40 | BCM21663 and BCM21664 variants. |
40 | 41 | ||
41 | config ARCH_BCM_MOBILE_L2_CACHE | 42 | config ARCH_BCM_MOBILE_L2_CACHE |
@@ -49,6 +50,17 @@ config ARCH_BCM_MOBILE_SMC | |||
49 | bool | 50 | bool |
50 | depends on ARCH_BCM_281XX || ARCH_BCM_21664 | 51 | depends on ARCH_BCM_281XX || ARCH_BCM_21664 |
51 | 52 | ||
53 | config ARCH_BCM_MOBILE_SMP | ||
54 | bool "Broadcom mobile SoC SMP support" | ||
55 | depends on (ARCH_BCM_281XX || ARCH_BCM_21664) && SMP | ||
56 | default y | ||
57 | select HAVE_ARM_SCU | ||
58 | select ARM_ERRATA_764369 | ||
59 | help | ||
60 | SMP support for the BCM281XX and BCM21664 SoC families. | ||
61 | Provided as an option so SMP support for SoCs of this type | ||
62 | can be disabled for an SMP-enabled kernel. | ||
63 | |||
52 | endmenu | 64 | endmenu |
53 | 65 | ||
54 | endif | 66 | endif |
@@ -87,4 +99,20 @@ config ARCH_BCM_5301X | |||
87 | different SoC or with the older BCM47XX and BCM53XX based | 99 | different SoC or with the older BCM47XX and BCM53XX based |
88 | network SoC using a MIPS CPU, they are supported by arch/mips/bcm47xx | 100 | network SoC using a MIPS CPU, they are supported by arch/mips/bcm47xx |
89 | 101 | ||
102 | config ARCH_BRCMSTB | ||
103 | bool "Broadcom BCM7XXX based boards" if ARCH_MULTI_V7 | ||
104 | depends on MMU | ||
105 | select ARM_GIC | ||
106 | select MIGHT_HAVE_PCI | ||
107 | select HAVE_SMP | ||
108 | select HAVE_ARM_ARCH_TIMER | ||
109 | select BRCMSTB_GISB_ARB | ||
110 | select BRCMSTB_L2_IRQ | ||
111 | help | ||
112 | Say Y if you intend to run the kernel on a Broadcom ARM-based STB | ||
113 | chipset. | ||
114 | |||
115 | This enables support for Broadcom ARM-based set-top box chipsets, | ||
116 | including the 7445 family of chips. | ||
117 | |||
90 | endif | 118 | 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 | |||
16 | # BCM21664 | 16 | # BCM21664 |
17 | obj-$(CONFIG_ARCH_BCM_21664) += board_bcm21664.o | 17 | obj-$(CONFIG_ARCH_BCM_21664) += board_bcm21664.o |
18 | 18 | ||
19 | # BCM281XX and BCM21664 SMP support | ||
20 | obj-$(CONFIG_ARCH_BCM_MOBILE_SMP) += kona_smp.o | ||
21 | |||
19 | # BCM281XX and BCM21664 L2 cache control | 22 | # BCM281XX and BCM21664 L2 cache control |
20 | obj-$(CONFIG_ARCH_BCM_MOBILE_L2_CACHE) += kona_l2_cache.o | 23 | obj-$(CONFIG_ARCH_BCM_MOBILE_L2_CACHE) += kona_l2_cache.o |
21 | 24 | ||
@@ -30,3 +33,8 @@ obj-$(CONFIG_ARCH_BCM2835) += board_bcm2835.o | |||
30 | 33 | ||
31 | # BCM5301X | 34 | # BCM5301X |
32 | obj-$(CONFIG_ARCH_BCM_5301X) += bcm_5301x.o | 35 | obj-$(CONFIG_ARCH_BCM_5301X) += bcm_5301x.o |
36 | |||
37 | ifeq ($(CONFIG_ARCH_BRCMSTB),y) | ||
38 | obj-y += brcmstb.o | ||
39 | obj-$(CONFIG_SMP) += headsmp-brcmstb.o platsmp-brcmstb.o | ||
40 | 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 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013-2014 Broadcom Corporation | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License as | ||
6 | * published by the Free Software Foundation version 2. | ||
7 | * | ||
8 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | ||
9 | * kind, whether express or implied; without even the implied warranty | ||
10 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #include <linux/init.h> | ||
15 | #include <linux/of_platform.h> | ||
16 | |||
17 | #include <asm/mach-types.h> | ||
18 | #include <asm/mach/arch.h> | ||
19 | |||
20 | static const char *brcmstb_match[] __initconst = { | ||
21 | "brcm,bcm7445", | ||
22 | "brcm,brcmstb", | ||
23 | NULL | ||
24 | }; | ||
25 | |||
26 | DT_MACHINE_START(BRCMSTB, "Broadcom STB (Flattened Device Tree)") | ||
27 | .dt_compat = brcmstb_match, | ||
28 | 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 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013-2014 Broadcom Corporation | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License as | ||
6 | * published by the Free Software Foundation version 2. | ||
7 | * | ||
8 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | ||
9 | * kind, whether express or implied; without even the implied warranty | ||
10 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #ifndef __BRCMSTB_H__ | ||
15 | #define __BRCMSTB_H__ | ||
16 | |||
17 | void brcmstb_secondary_startup(void); | ||
18 | |||
19 | #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 @@ | |||
1 | /* | ||
2 | * SMP boot code for secondary CPUs | ||
3 | * Based on arch/arm/mach-tegra/headsmp.S | ||
4 | * | ||
5 | * Copyright (C) 2010 NVIDIA, Inc. | ||
6 | * Copyright (C) 2013-2014 Broadcom Corporation | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License as | ||
10 | * published by the Free Software Foundation version 2. | ||
11 | * | ||
12 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | ||
13 | * kind, whether express or implied; without even the implied warranty | ||
14 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | */ | ||
17 | |||
18 | #include <asm/assembler.h> | ||
19 | #include <linux/linkage.h> | ||
20 | #include <linux/init.h> | ||
21 | |||
22 | .section ".text.head", "ax" | ||
23 | |||
24 | ENTRY(brcmstb_secondary_startup) | ||
25 | /* | ||
26 | * Ensure CPU is in a sane state by disabling all IRQs and switching | ||
27 | * into SVC mode. | ||
28 | */ | ||
29 | setmode PSR_I_BIT | PSR_F_BIT | SVC_MODE, r0 | ||
30 | |||
31 | bl v7_invalidate_l1 | ||
32 | b secondary_startup | ||
33 | 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 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014 Broadcom Corporation | ||
3 | * Copyright 2014 Linaro Limited | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License as | ||
7 | * published by the Free Software Foundation version 2. | ||
8 | * | ||
9 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | ||
10 | * kind, whether express or implied; without even the implied warranty | ||
11 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | */ | ||
14 | |||
15 | #include <linux/init.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/io.h> | ||
18 | #include <linux/of.h> | ||
19 | #include <linux/sched.h> | ||
20 | |||
21 | #include <asm/smp.h> | ||
22 | #include <asm/smp_plat.h> | ||
23 | #include <asm/smp_scu.h> | ||
24 | |||
25 | /* Size of mapped Cortex A9 SCU address space */ | ||
26 | #define CORTEX_A9_SCU_SIZE 0x58 | ||
27 | |||
28 | #define SECONDARY_TIMEOUT_NS NSEC_PER_MSEC /* 1 msec (in nanoseconds) */ | ||
29 | #define BOOT_ADDR_CPUID_MASK 0x3 | ||
30 | |||
31 | /* Name of device node property defining secondary boot register location */ | ||
32 | #define OF_SECONDARY_BOOT "secondary-boot-reg" | ||
33 | |||
34 | /* I/O address of register used to coordinate secondary core startup */ | ||
35 | static u32 secondary_boot; | ||
36 | |||
37 | /* | ||
38 | * Enable the Cortex A9 Snoop Control Unit | ||
39 | * | ||
40 | * By the time this is called we already know there are multiple | ||
41 | * cores present. We assume we're running on a Cortex A9 processor, | ||
42 | * so any trouble getting the base address register or getting the | ||
43 | * SCU base is a problem. | ||
44 | * | ||
45 | * Return 0 if successful or an error code otherwise. | ||
46 | */ | ||
47 | static int __init scu_a9_enable(void) | ||
48 | { | ||
49 | unsigned long config_base; | ||
50 | void __iomem *scu_base; | ||
51 | |||
52 | if (!scu_a9_has_base()) { | ||
53 | pr_err("no configuration base address register!\n"); | ||
54 | return -ENXIO; | ||
55 | } | ||
56 | |||
57 | /* Config base address register value is zero for uniprocessor */ | ||
58 | config_base = scu_a9_get_base(); | ||
59 | if (!config_base) { | ||
60 | pr_err("hardware reports only one core\n"); | ||
61 | return -ENOENT; | ||
62 | } | ||
63 | |||
64 | scu_base = ioremap((phys_addr_t)config_base, CORTEX_A9_SCU_SIZE); | ||
65 | if (!scu_base) { | ||
66 | pr_err("failed to remap config base (%lu/%u) for SCU\n", | ||
67 | config_base, CORTEX_A9_SCU_SIZE); | ||
68 | return -ENOMEM; | ||
69 | } | ||
70 | |||
71 | scu_enable(scu_base); | ||
72 | |||
73 | iounmap(scu_base); /* That's the last we'll need of this */ | ||
74 | |||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | static void __init bcm_smp_prepare_cpus(unsigned int max_cpus) | ||
79 | { | ||
80 | static cpumask_t only_cpu_0 = { CPU_BITS_CPU0 }; | ||
81 | struct device_node *node; | ||
82 | int ret; | ||
83 | |||
84 | BUG_ON(secondary_boot); /* We're called only once */ | ||
85 | |||
86 | /* | ||
87 | * This function is only called via smp_ops->smp_prepare_cpu(). | ||
88 | * That only happens if a "/cpus" device tree node exists | ||
89 | * and has an "enable-method" property that selects the SMP | ||
90 | * operations defined herein. | ||
91 | */ | ||
92 | node = of_find_node_by_path("/cpus"); | ||
93 | BUG_ON(!node); | ||
94 | |||
95 | /* | ||
96 | * Our secondary enable method requires a "secondary-boot-reg" | ||
97 | * property to specify a register address used to request the | ||
98 | * ROM code boot a secondary code. If we have any trouble | ||
99 | * getting this we fall back to uniprocessor mode. | ||
100 | */ | ||
101 | if (of_property_read_u32(node, OF_SECONDARY_BOOT, &secondary_boot)) { | ||
102 | pr_err("%s: missing/invalid " OF_SECONDARY_BOOT " property\n", | ||
103 | node->name); | ||
104 | ret = -ENOENT; /* Arrange to disable SMP */ | ||
105 | goto out; | ||
106 | } | ||
107 | |||
108 | /* | ||
109 | * Enable the SCU on Cortex A9 based SoCs. If -ENOENT is | ||
110 | * returned, the SoC reported a uniprocessor configuration. | ||
111 | * We bail on any other error. | ||
112 | */ | ||
113 | ret = scu_a9_enable(); | ||
114 | out: | ||
115 | of_node_put(node); | ||
116 | if (ret) { | ||
117 | /* Update the CPU present map to reflect uniprocessor mode */ | ||
118 | BUG_ON(ret != -ENOENT); | ||
119 | pr_warn("disabling SMP\n"); | ||
120 | init_cpu_present(&only_cpu_0); | ||
121 | } | ||
122 | } | ||
123 | |||
124 | /* | ||
125 | * The ROM code has the secondary cores looping, waiting for an event. | ||
126 | * When an event occurs each core examines the bottom two bits of the | ||
127 | * secondary boot register. When a core finds those bits contain its | ||
128 | * own core id, it performs initialization, including computing its boot | ||
129 | * address by clearing the boot register value's bottom two bits. The | ||
130 | * core signals that it is beginning its execution by writing its boot | ||
131 | * address back to the secondary boot register, and finally jumps to | ||
132 | * that address. | ||
133 | * | ||
134 | * So to start a core executing we need to: | ||
135 | * - Encode the (hardware) CPU id with the bottom bits of the secondary | ||
136 | * start address. | ||
137 | * - Write that value into the secondary boot register. | ||
138 | * - Generate an event to wake up the secondary CPU(s). | ||
139 | * - Wait for the secondary boot register to be re-written, which | ||
140 | * indicates the secondary core has started. | ||
141 | */ | ||
142 | static int bcm_boot_secondary(unsigned int cpu, struct task_struct *idle) | ||
143 | { | ||
144 | void __iomem *boot_reg; | ||
145 | phys_addr_t boot_func; | ||
146 | u64 start_clock; | ||
147 | u32 cpu_id; | ||
148 | u32 boot_val; | ||
149 | bool timeout = false; | ||
150 | |||
151 | cpu_id = cpu_logical_map(cpu); | ||
152 | if (cpu_id & ~BOOT_ADDR_CPUID_MASK) { | ||
153 | pr_err("bad cpu id (%u > %u)\n", cpu_id, BOOT_ADDR_CPUID_MASK); | ||
154 | return -EINVAL; | ||
155 | } | ||
156 | |||
157 | if (!secondary_boot) { | ||
158 | pr_err("required secondary boot register not specified\n"); | ||
159 | return -EINVAL; | ||
160 | } | ||
161 | |||
162 | boot_reg = ioremap_nocache((phys_addr_t)secondary_boot, sizeof(u32)); | ||
163 | if (!boot_reg) { | ||
164 | pr_err("unable to map boot register for cpu %u\n", cpu_id); | ||
165 | return -ENOSYS; | ||
166 | } | ||
167 | |||
168 | /* | ||
169 | * Secondary cores will start in secondary_startup(), | ||
170 | * defined in "arch/arm/kernel/head.S" | ||
171 | */ | ||
172 | boot_func = virt_to_phys(secondary_startup); | ||
173 | BUG_ON(boot_func & BOOT_ADDR_CPUID_MASK); | ||
174 | BUG_ON(boot_func > (phys_addr_t)U32_MAX); | ||
175 | |||
176 | /* The core to start is encoded in the low bits */ | ||
177 | boot_val = (u32)boot_func | cpu_id; | ||
178 | writel_relaxed(boot_val, boot_reg); | ||
179 | |||
180 | sev(); | ||
181 | |||
182 | /* The low bits will be cleared once the core has started */ | ||
183 | start_clock = local_clock(); | ||
184 | while (!timeout && readl_relaxed(boot_reg) == boot_val) | ||
185 | timeout = local_clock() - start_clock > SECONDARY_TIMEOUT_NS; | ||
186 | |||
187 | iounmap(boot_reg); | ||
188 | |||
189 | if (!timeout) | ||
190 | return 0; | ||
191 | |||
192 | pr_err("timeout waiting for cpu %u to start\n", cpu_id); | ||
193 | |||
194 | return -ENOSYS; | ||
195 | } | ||
196 | |||
197 | static struct smp_operations bcm_smp_ops __initdata = { | ||
198 | .smp_prepare_cpus = bcm_smp_prepare_cpus, | ||
199 | .smp_boot_secondary = bcm_boot_secondary, | ||
200 | }; | ||
201 | CPU_METHOD_OF_DECLARE(bcm_smp_bcm281xx, "brcm,bcm11351-cpu-method", | ||
202 | &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 @@ | |||
1 | /* | ||
2 | * Broadcom STB CPU SMP and hotplug support for ARM | ||
3 | * | ||
4 | * Copyright (C) 2013-2014 Broadcom Corporation | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License as | ||
8 | * published by the Free Software Foundation version 2. | ||
9 | * | ||
10 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | ||
11 | * kind, whether express or implied; without even the implied warranty | ||
12 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | */ | ||
15 | |||
16 | #include <linux/delay.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/io.h> | ||
20 | #include <linux/of_address.h> | ||
21 | #include <linux/of_platform.h> | ||
22 | #include <linux/printk.h> | ||
23 | #include <linux/regmap.h> | ||
24 | #include <linux/smp.h> | ||
25 | #include <linux/mfd/syscon.h> | ||
26 | #include <linux/spinlock.h> | ||
27 | |||
28 | #include <asm/cacheflush.h> | ||
29 | #include <asm/cp15.h> | ||
30 | #include <asm/mach-types.h> | ||
31 | #include <asm/smp_plat.h> | ||
32 | |||
33 | #include "brcmstb.h" | ||
34 | |||
35 | enum { | ||
36 | ZONE_MAN_CLKEN_MASK = BIT(0), | ||
37 | ZONE_MAN_RESET_CNTL_MASK = BIT(1), | ||
38 | ZONE_MAN_MEM_PWR_MASK = BIT(4), | ||
39 | ZONE_RESERVED_1_MASK = BIT(5), | ||
40 | ZONE_MAN_ISO_CNTL_MASK = BIT(6), | ||
41 | ZONE_MANUAL_CONTROL_MASK = BIT(7), | ||
42 | ZONE_PWR_DN_REQ_MASK = BIT(9), | ||
43 | ZONE_PWR_UP_REQ_MASK = BIT(10), | ||
44 | ZONE_BLK_RST_ASSERT_MASK = BIT(12), | ||
45 | ZONE_PWR_OFF_STATE_MASK = BIT(25), | ||
46 | ZONE_PWR_ON_STATE_MASK = BIT(26), | ||
47 | ZONE_DPG_PWR_STATE_MASK = BIT(28), | ||
48 | ZONE_MEM_PWR_STATE_MASK = BIT(29), | ||
49 | ZONE_RESET_STATE_MASK = BIT(31), | ||
50 | CPU0_PWR_ZONE_CTRL_REG = 1, | ||
51 | CPU_RESET_CONFIG_REG = 2, | ||
52 | }; | ||
53 | |||
54 | static void __iomem *cpubiuctrl_block; | ||
55 | static void __iomem *hif_cont_block; | ||
56 | static u32 cpu0_pwr_zone_ctrl_reg; | ||
57 | static u32 cpu_rst_cfg_reg; | ||
58 | static u32 hif_cont_reg; | ||
59 | |||
60 | #ifdef CONFIG_HOTPLUG_CPU | ||
61 | static DEFINE_PER_CPU_ALIGNED(int, per_cpu_sw_state); | ||
62 | |||
63 | static int per_cpu_sw_state_rd(u32 cpu) | ||
64 | { | ||
65 | sync_cache_r(SHIFT_PERCPU_PTR(&per_cpu_sw_state, per_cpu_offset(cpu))); | ||
66 | return per_cpu(per_cpu_sw_state, cpu); | ||
67 | } | ||
68 | |||
69 | static void per_cpu_sw_state_wr(u32 cpu, int val) | ||
70 | { | ||
71 | per_cpu(per_cpu_sw_state, cpu) = val; | ||
72 | dmb(); | ||
73 | sync_cache_w(SHIFT_PERCPU_PTR(&per_cpu_sw_state, per_cpu_offset(cpu))); | ||
74 | dsb_sev(); | ||
75 | } | ||
76 | #else | ||
77 | static inline void per_cpu_sw_state_wr(u32 cpu, int val) { } | ||
78 | #endif | ||
79 | |||
80 | static void __iomem *pwr_ctrl_get_base(u32 cpu) | ||
81 | { | ||
82 | void __iomem *base = cpubiuctrl_block + cpu0_pwr_zone_ctrl_reg; | ||
83 | base += (cpu_logical_map(cpu) * 4); | ||
84 | return base; | ||
85 | } | ||
86 | |||
87 | static u32 pwr_ctrl_rd(u32 cpu) | ||
88 | { | ||
89 | void __iomem *base = pwr_ctrl_get_base(cpu); | ||
90 | return readl_relaxed(base); | ||
91 | } | ||
92 | |||
93 | static void pwr_ctrl_wr(u32 cpu, u32 val) | ||
94 | { | ||
95 | void __iomem *base = pwr_ctrl_get_base(cpu); | ||
96 | writel(val, base); | ||
97 | } | ||
98 | |||
99 | static void cpu_rst_cfg_set(u32 cpu, int set) | ||
100 | { | ||
101 | u32 val; | ||
102 | val = readl_relaxed(cpubiuctrl_block + cpu_rst_cfg_reg); | ||
103 | if (set) | ||
104 | val |= BIT(cpu_logical_map(cpu)); | ||
105 | else | ||
106 | val &= ~BIT(cpu_logical_map(cpu)); | ||
107 | writel_relaxed(val, cpubiuctrl_block + cpu_rst_cfg_reg); | ||
108 | } | ||
109 | |||
110 | static void cpu_set_boot_addr(u32 cpu, unsigned long boot_addr) | ||
111 | { | ||
112 | const int reg_ofs = cpu_logical_map(cpu) * 8; | ||
113 | writel_relaxed(0, hif_cont_block + hif_cont_reg + reg_ofs); | ||
114 | writel_relaxed(boot_addr, hif_cont_block + hif_cont_reg + 4 + reg_ofs); | ||
115 | } | ||
116 | |||
117 | static void brcmstb_cpu_boot(u32 cpu) | ||
118 | { | ||
119 | pr_info("SMP: Booting CPU%d...\n", cpu); | ||
120 | |||
121 | /* | ||
122 | * set the reset vector to point to the secondary_startup | ||
123 | * routine | ||
124 | */ | ||
125 | cpu_set_boot_addr(cpu, virt_to_phys(brcmstb_secondary_startup)); | ||
126 | |||
127 | /* unhalt the cpu */ | ||
128 | cpu_rst_cfg_set(cpu, 0); | ||
129 | } | ||
130 | |||
131 | static void brcmstb_cpu_power_on(u32 cpu) | ||
132 | { | ||
133 | /* | ||
134 | * The secondary cores power was cut, so we must go through | ||
135 | * power-on initialization. | ||
136 | */ | ||
137 | u32 tmp; | ||
138 | |||
139 | pr_info("SMP: Powering up CPU%d...\n", cpu); | ||
140 | |||
141 | /* Request zone power up */ | ||
142 | pwr_ctrl_wr(cpu, ZONE_PWR_UP_REQ_MASK); | ||
143 | |||
144 | /* Wait for the power up FSM to complete */ | ||
145 | do { | ||
146 | tmp = pwr_ctrl_rd(cpu); | ||
147 | } while (!(tmp & ZONE_PWR_ON_STATE_MASK)); | ||
148 | |||
149 | per_cpu_sw_state_wr(cpu, 1); | ||
150 | } | ||
151 | |||
152 | static int brcmstb_cpu_get_power_state(u32 cpu) | ||
153 | { | ||
154 | int tmp = pwr_ctrl_rd(cpu); | ||
155 | return (tmp & ZONE_RESET_STATE_MASK) ? 0 : 1; | ||
156 | } | ||
157 | |||
158 | #ifdef CONFIG_HOTPLUG_CPU | ||
159 | |||
160 | static void brcmstb_cpu_die(u32 cpu) | ||
161 | { | ||
162 | v7_exit_coherency_flush(all); | ||
163 | |||
164 | /* Prevent all interrupts from reaching this CPU. */ | ||
165 | arch_local_irq_disable(); | ||
166 | |||
167 | /* | ||
168 | * Final full barrier to ensure everything before this instruction has | ||
169 | * quiesced. | ||
170 | */ | ||
171 | isb(); | ||
172 | dsb(); | ||
173 | |||
174 | per_cpu_sw_state_wr(cpu, 0); | ||
175 | |||
176 | /* Sit and wait to die */ | ||
177 | wfi(); | ||
178 | |||
179 | /* We should never get here... */ | ||
180 | panic("Spurious interrupt on CPU %d received!\n", cpu); | ||
181 | } | ||
182 | |||
183 | static int brcmstb_cpu_kill(u32 cpu) | ||
184 | { | ||
185 | u32 tmp; | ||
186 | |||
187 | pr_info("SMP: Powering down CPU%d...\n", cpu); | ||
188 | |||
189 | while (per_cpu_sw_state_rd(cpu)) | ||
190 | ; | ||
191 | |||
192 | /* Program zone reset */ | ||
193 | pwr_ctrl_wr(cpu, ZONE_RESET_STATE_MASK | ZONE_BLK_RST_ASSERT_MASK | | ||
194 | ZONE_PWR_DN_REQ_MASK); | ||
195 | |||
196 | /* Verify zone reset */ | ||
197 | tmp = pwr_ctrl_rd(cpu); | ||
198 | if (!(tmp & ZONE_RESET_STATE_MASK)) | ||
199 | pr_err("%s: Zone reset bit for CPU %d not asserted!\n", | ||
200 | __func__, cpu); | ||
201 | |||
202 | /* Wait for power down */ | ||
203 | do { | ||
204 | tmp = pwr_ctrl_rd(cpu); | ||
205 | } while (!(tmp & ZONE_PWR_OFF_STATE_MASK)); | ||
206 | |||
207 | /* Settle-time from Broadcom-internal DVT reference code */ | ||
208 | udelay(7); | ||
209 | |||
210 | /* Assert reset on the CPU */ | ||
211 | cpu_rst_cfg_set(cpu, 1); | ||
212 | |||
213 | return 1; | ||
214 | } | ||
215 | |||
216 | #endif /* CONFIG_HOTPLUG_CPU */ | ||
217 | |||
218 | static int __init setup_hifcpubiuctrl_regs(struct device_node *np) | ||
219 | { | ||
220 | int rc = 0; | ||
221 | char *name; | ||
222 | struct device_node *syscon_np = NULL; | ||
223 | |||
224 | name = "syscon-cpu"; | ||
225 | |||
226 | syscon_np = of_parse_phandle(np, name, 0); | ||
227 | if (!syscon_np) { | ||
228 | pr_err("can't find phandle %s\n", name); | ||
229 | rc = -EINVAL; | ||
230 | goto cleanup; | ||
231 | } | ||
232 | |||
233 | cpubiuctrl_block = of_iomap(syscon_np, 0); | ||
234 | if (!cpubiuctrl_block) { | ||
235 | pr_err("iomap failed for cpubiuctrl_block\n"); | ||
236 | rc = -EINVAL; | ||
237 | goto cleanup; | ||
238 | } | ||
239 | |||
240 | rc = of_property_read_u32_index(np, name, CPU0_PWR_ZONE_CTRL_REG, | ||
241 | &cpu0_pwr_zone_ctrl_reg); | ||
242 | if (rc) { | ||
243 | pr_err("failed to read 1st entry from %s property (%d)\n", name, | ||
244 | rc); | ||
245 | rc = -EINVAL; | ||
246 | goto cleanup; | ||
247 | } | ||
248 | |||
249 | rc = of_property_read_u32_index(np, name, CPU_RESET_CONFIG_REG, | ||
250 | &cpu_rst_cfg_reg); | ||
251 | if (rc) { | ||
252 | pr_err("failed to read 2nd entry from %s property (%d)\n", name, | ||
253 | rc); | ||
254 | rc = -EINVAL; | ||
255 | goto cleanup; | ||
256 | } | ||
257 | |||
258 | cleanup: | ||
259 | if (syscon_np) | ||
260 | of_node_put(syscon_np); | ||
261 | |||
262 | return rc; | ||
263 | } | ||
264 | |||
265 | static int __init setup_hifcont_regs(struct device_node *np) | ||
266 | { | ||
267 | int rc = 0; | ||
268 | char *name; | ||
269 | struct device_node *syscon_np = NULL; | ||
270 | |||
271 | name = "syscon-cont"; | ||
272 | |||
273 | syscon_np = of_parse_phandle(np, name, 0); | ||
274 | if (!syscon_np) { | ||
275 | pr_err("can't find phandle %s\n", name); | ||
276 | rc = -EINVAL; | ||
277 | goto cleanup; | ||
278 | } | ||
279 | |||
280 | hif_cont_block = of_iomap(syscon_np, 0); | ||
281 | if (!hif_cont_block) { | ||
282 | pr_err("iomap failed for hif_cont_block\n"); | ||
283 | rc = -EINVAL; | ||
284 | goto cleanup; | ||
285 | } | ||
286 | |||
287 | /* offset is at top of hif_cont_block */ | ||
288 | hif_cont_reg = 0; | ||
289 | |||
290 | cleanup: | ||
291 | if (syscon_np) | ||
292 | of_node_put(syscon_np); | ||
293 | |||
294 | return rc; | ||
295 | } | ||
296 | |||
297 | static void __init brcmstb_cpu_ctrl_setup(unsigned int max_cpus) | ||
298 | { | ||
299 | int rc; | ||
300 | struct device_node *np; | ||
301 | char *name; | ||
302 | |||
303 | name = "brcm,brcmstb-smpboot"; | ||
304 | np = of_find_compatible_node(NULL, NULL, name); | ||
305 | if (!np) { | ||
306 | pr_err("can't find compatible node %s\n", name); | ||
307 | return; | ||
308 | } | ||
309 | |||
310 | rc = setup_hifcpubiuctrl_regs(np); | ||
311 | if (rc) | ||
312 | return; | ||
313 | |||
314 | rc = setup_hifcont_regs(np); | ||
315 | if (rc) | ||
316 | return; | ||
317 | } | ||
318 | |||
319 | static DEFINE_SPINLOCK(boot_lock); | ||
320 | |||
321 | static void brcmstb_secondary_init(unsigned int cpu) | ||
322 | { | ||
323 | /* | ||
324 | * Synchronise with the boot thread. | ||
325 | */ | ||
326 | spin_lock(&boot_lock); | ||
327 | spin_unlock(&boot_lock); | ||
328 | } | ||
329 | |||
330 | static int brcmstb_boot_secondary(unsigned int cpu, struct task_struct *idle) | ||
331 | { | ||
332 | /* | ||
333 | * set synchronisation state between this boot processor | ||
334 | * and the secondary one | ||
335 | */ | ||
336 | spin_lock(&boot_lock); | ||
337 | |||
338 | /* Bring up power to the core if necessary */ | ||
339 | if (brcmstb_cpu_get_power_state(cpu) == 0) | ||
340 | brcmstb_cpu_power_on(cpu); | ||
341 | |||
342 | brcmstb_cpu_boot(cpu); | ||
343 | |||
344 | /* | ||
345 | * now the secondary core is starting up let it run its | ||
346 | * calibrations, then wait for it to finish | ||
347 | */ | ||
348 | spin_unlock(&boot_lock); | ||
349 | |||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | static struct smp_operations brcmstb_smp_ops __initdata = { | ||
354 | .smp_prepare_cpus = brcmstb_cpu_ctrl_setup, | ||
355 | .smp_secondary_init = brcmstb_secondary_init, | ||
356 | .smp_boot_secondary = brcmstb_boot_secondary, | ||
357 | #ifdef CONFIG_HOTPLUG_CPU | ||
358 | .cpu_kill = brcmstb_cpu_kill, | ||
359 | .cpu_die = brcmstb_cpu_die, | ||
360 | #endif | ||
361 | }; | ||
362 | |||
363 | CPU_METHOD_OF_DECLARE(brcmstb_smp, "brcm,brahma-b15", &brcmstb_smp_ops); | ||