diff options
Diffstat (limited to 'arch/arm/mach-mvebu')
-rw-r--r-- | arch/arm/mach-mvebu/Kconfig | 21 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/Makefile | 13 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/armada-370-xp.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/board-t5325.c | 41 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/board-v7.c | 88 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/board.h | 6 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/coherency.c | 340 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/coherency.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/coherency_ll.S | 143 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/common.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/cpu-reset.c | 103 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/dove.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/headsmp-a9.S | 34 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/headsmp.S | 15 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/kirkwood.c | 5 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/mvebu-soc-id.c | 32 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/mvebu-soc-id.h | 4 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/platsmp-a9.c | 102 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/platsmp.c | 23 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/pmsu.c | 273 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/system-controller.c | 15 |
21 files changed, 1079 insertions, 189 deletions
diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig index 3f73eecbcfb0..6090b9eb00c8 100644 --- a/arch/arm/mach-mvebu/Kconfig +++ b/arch/arm/mach-mvebu/Kconfig | |||
@@ -3,15 +3,13 @@ config ARCH_MVEBU | |||
3 | select ARCH_SUPPORTS_BIG_ENDIAN | 3 | select ARCH_SUPPORTS_BIG_ENDIAN |
4 | select CLKSRC_MMIO | 4 | select CLKSRC_MMIO |
5 | select GENERIC_IRQ_CHIP | 5 | select GENERIC_IRQ_CHIP |
6 | select IRQ_DOMAIN | ||
7 | select PINCTRL | 6 | select PINCTRL |
8 | select PLAT_ORION | 7 | select PLAT_ORION |
8 | select SOC_BUS | ||
9 | select MVEBU_MBUS | 9 | select MVEBU_MBUS |
10 | select ZONE_DMA if ARM_LPAE | 10 | select ZONE_DMA if ARM_LPAE |
11 | select ARCH_REQUIRE_GPIOLIB | 11 | select ARCH_REQUIRE_GPIOLIB |
12 | select MIGHT_HAVE_PCI | ||
13 | select PCI_QUIRKS if PCI | 12 | select PCI_QUIRKS if PCI |
14 | select OF_ADDRESS_PCI | ||
15 | 13 | ||
16 | if ARCH_MVEBU | 14 | if ARCH_MVEBU |
17 | 15 | ||
@@ -38,7 +36,9 @@ config MACH_ARMADA_375 | |||
38 | select ARM_ERRATA_753970 | 36 | select ARM_ERRATA_753970 |
39 | select ARM_GIC | 37 | select ARM_GIC |
40 | select ARMADA_375_CLK | 38 | select ARMADA_375_CLK |
41 | select CPU_V7 | 39 | select HAVE_ARM_SCU |
40 | select HAVE_ARM_TWD if SMP | ||
41 | select HAVE_SMP | ||
42 | select MACH_MVEBU_V7 | 42 | select MACH_MVEBU_V7 |
43 | select PINCTRL_ARMADA_375 | 43 | select PINCTRL_ARMADA_375 |
44 | help | 44 | help |
@@ -51,7 +51,9 @@ config MACH_ARMADA_38X | |||
51 | select ARM_ERRATA_753970 | 51 | select ARM_ERRATA_753970 |
52 | select ARM_GIC | 52 | select ARM_GIC |
53 | select ARMADA_38X_CLK | 53 | select ARMADA_38X_CLK |
54 | select CPU_V7 | 54 | select HAVE_ARM_SCU |
55 | select HAVE_ARM_TWD if SMP | ||
56 | select HAVE_SMP | ||
55 | select MACH_MVEBU_V7 | 57 | select MACH_MVEBU_V7 |
56 | select PINCTRL_ARMADA_38X | 58 | select PINCTRL_ARMADA_38X |
57 | help | 59 | help |
@@ -86,24 +88,15 @@ config MACH_KIRKWOOD | |||
86 | select ARCH_REQUIRE_GPIOLIB | 88 | select ARCH_REQUIRE_GPIOLIB |
87 | select CPU_FEROCEON | 89 | select CPU_FEROCEON |
88 | select KIRKWOOD_CLK | 90 | select KIRKWOOD_CLK |
89 | select OF_IRQ | ||
90 | select ORION_IRQCHIP | 91 | select ORION_IRQCHIP |
91 | select ORION_TIMER | 92 | select ORION_TIMER |
92 | select PCI | 93 | select PCI |
93 | select PCI_QUIRKS | 94 | select PCI_QUIRKS |
94 | select PINCTRL_KIRKWOOD | 95 | select PINCTRL_KIRKWOOD |
95 | select USE_OF | ||
96 | help | 96 | help |
97 | Say 'Y' here if you want your kernel to support boards based | 97 | Say 'Y' here if you want your kernel to support boards based |
98 | on the Marvell Kirkwood device tree. | 98 | on the Marvell Kirkwood device tree. |
99 | 99 | ||
100 | config MACH_T5325 | ||
101 | bool "HP T5325 thin client" | ||
102 | depends on MACH_KIRKWOOD | ||
103 | help | ||
104 | Say 'Y' here if you want your kernel to support the | ||
105 | HP T5325 Thin client | ||
106 | |||
107 | endmenu | 100 | endmenu |
108 | 101 | ||
109 | endif | 102 | endif |
diff --git a/arch/arm/mach-mvebu/Makefile b/arch/arm/mach-mvebu/Makefile index a63e43b6b451..2ecb828e4a8b 100644 --- a/arch/arm/mach-mvebu/Makefile +++ b/arch/arm/mach-mvebu/Makefile | |||
@@ -2,12 +2,15 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \ | |||
2 | -I$(srctree)/arch/arm/plat-orion/include | 2 | -I$(srctree)/arch/arm/plat-orion/include |
3 | 3 | ||
4 | AFLAGS_coherency_ll.o := -Wa,-march=armv7-a | 4 | AFLAGS_coherency_ll.o := -Wa,-march=armv7-a |
5 | CFLAGS_pmsu.o := -march=armv7-a | ||
5 | 6 | ||
6 | obj-y += system-controller.o mvebu-soc-id.o | 7 | obj-y += system-controller.o mvebu-soc-id.o |
7 | obj-$(CONFIG_MACH_MVEBU_V7) += board-v7.o | 8 | |
9 | ifeq ($(CONFIG_MACH_MVEBU_V7),y) | ||
10 | obj-y += cpu-reset.o board-v7.o coherency.o coherency_ll.o pmsu.o | ||
11 | obj-$(CONFIG_SMP) += platsmp.o headsmp.o platsmp-a9.o headsmp-a9.o | ||
12 | obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o | ||
13 | endif | ||
14 | |||
8 | obj-$(CONFIG_MACH_DOVE) += dove.o | 15 | obj-$(CONFIG_MACH_DOVE) += dove.o |
9 | obj-$(CONFIG_ARCH_MVEBU) += coherency.o coherency_ll.o pmsu.o | ||
10 | obj-$(CONFIG_SMP) += platsmp.o headsmp.o | ||
11 | obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o | ||
12 | obj-$(CONFIG_MACH_KIRKWOOD) += kirkwood.o kirkwood-pm.o | 16 | obj-$(CONFIG_MACH_KIRKWOOD) += kirkwood.o kirkwood-pm.o |
13 | obj-$(CONFIG_MACH_T5325) += board-t5325.o | ||
diff --git a/arch/arm/mach-mvebu/armada-370-xp.h b/arch/arm/mach-mvebu/armada-370-xp.h index 237c86b83390..c3465f5b1250 100644 --- a/arch/arm/mach-mvebu/armada-370-xp.h +++ b/arch/arm/mach-mvebu/armada-370-xp.h | |||
@@ -20,8 +20,6 @@ | |||
20 | 20 | ||
21 | #define ARMADA_XP_MAX_CPUS 4 | 21 | #define ARMADA_XP_MAX_CPUS 4 |
22 | 22 | ||
23 | void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq); | ||
24 | void armada_xp_mpic_smp_cpu_init(void); | ||
25 | void armada_xp_secondary_startup(void); | 23 | void armada_xp_secondary_startup(void); |
26 | extern struct smp_operations armada_xp_smp_ops; | 24 | extern struct smp_operations armada_xp_smp_ops; |
27 | #endif | 25 | #endif |
diff --git a/arch/arm/mach-mvebu/board-t5325.c b/arch/arm/mach-mvebu/board-t5325.c deleted file mode 100644 index 65ace6db9f28..000000000000 --- a/arch/arm/mach-mvebu/board-t5325.c +++ /dev/null | |||
@@ -1,41 +0,0 @@ | |||
1 | /* | ||
2 | * HP T5325 Board Setup | ||
3 | * | ||
4 | * Copyright (C) 2014 | ||
5 | * | ||
6 | * Andrew Lunn <andrew@lunn.ch> | ||
7 | * | ||
8 | * This file is licensed under the terms of the GNU General Public | ||
9 | * License version 2. This program is licensed "as is" without any | ||
10 | * warranty of any kind, whether express or implied. | ||
11 | */ | ||
12 | |||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/i2c.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <sound/alc5623.h> | ||
18 | #include "board.h" | ||
19 | |||
20 | static struct platform_device hp_t5325_audio_device = { | ||
21 | .name = "t5325-audio", | ||
22 | .id = -1, | ||
23 | }; | ||
24 | |||
25 | static struct alc5623_platform_data alc5621_data = { | ||
26 | .add_ctrl = 0x3700, | ||
27 | .jack_det_ctrl = 0x4810, | ||
28 | }; | ||
29 | |||
30 | static struct i2c_board_info i2c_board_info[] __initdata = { | ||
31 | { | ||
32 | I2C_BOARD_INFO("alc5621", 0x1a), | ||
33 | .platform_data = &alc5621_data, | ||
34 | }, | ||
35 | }; | ||
36 | |||
37 | void __init t5325_init(void) | ||
38 | { | ||
39 | i2c_register_board_info(0, i2c_board_info, ARRAY_SIZE(i2c_board_info)); | ||
40 | platform_device_register(&hp_t5325_audio_device); | ||
41 | } | ||
diff --git a/arch/arm/mach-mvebu/board-v7.c b/arch/arm/mach-mvebu/board-v7.c index 333fca8fdc41..594262b27f56 100644 --- a/arch/arm/mach-mvebu/board-v7.c +++ b/arch/arm/mach-mvebu/board-v7.c | |||
@@ -27,12 +27,30 @@ | |||
27 | #include <asm/mach/arch.h> | 27 | #include <asm/mach/arch.h> |
28 | #include <asm/mach/map.h> | 28 | #include <asm/mach/map.h> |
29 | #include <asm/mach/time.h> | 29 | #include <asm/mach/time.h> |
30 | #include <asm/smp_scu.h> | ||
30 | #include "armada-370-xp.h" | 31 | #include "armada-370-xp.h" |
31 | #include "common.h" | 32 | #include "common.h" |
32 | #include "coherency.h" | 33 | #include "coherency.h" |
33 | #include "mvebu-soc-id.h" | 34 | #include "mvebu-soc-id.h" |
34 | 35 | ||
35 | /* | 36 | /* |
37 | * Enables the SCU when available. Obviously, this is only useful on | ||
38 | * Cortex-A based SOCs, not on PJ4B based ones. | ||
39 | */ | ||
40 | static void __init mvebu_scu_enable(void) | ||
41 | { | ||
42 | void __iomem *scu_base; | ||
43 | |||
44 | struct device_node *np = | ||
45 | of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu"); | ||
46 | if (np) { | ||
47 | scu_base = of_iomap(np, 0); | ||
48 | scu_enable(scu_base); | ||
49 | of_node_put(np); | ||
50 | } | ||
51 | } | ||
52 | |||
53 | /* | ||
36 | * Early versions of Armada 375 SoC have a bug where the BootROM | 54 | * Early versions of Armada 375 SoC have a bug where the BootROM |
37 | * leaves an external data abort pending. The kernel is hit by this | 55 | * leaves an external data abort pending. The kernel is hit by this |
38 | * data abort as soon as it enters userspace, because it unmasks the | 56 | * data abort as soon as it enters userspace, because it unmasks the |
@@ -57,11 +75,10 @@ static void __init mvebu_timer_and_clk_init(void) | |||
57 | { | 75 | { |
58 | of_clk_init(NULL); | 76 | of_clk_init(NULL); |
59 | clocksource_of_init(); | 77 | clocksource_of_init(); |
78 | mvebu_scu_enable(); | ||
60 | coherency_init(); | 79 | coherency_init(); |
61 | BUG_ON(mvebu_mbus_dt_init()); | 80 | BUG_ON(mvebu_mbus_dt_init(coherency_available())); |
62 | #ifdef CONFIG_CACHE_L2X0 | ||
63 | l2x0_of_init(0, ~0UL); | 81 | l2x0_of_init(0, ~0UL); |
64 | #endif | ||
65 | 82 | ||
66 | if (of_machine_is_compatible("marvell,armada375")) | 83 | if (of_machine_is_compatible("marvell,armada375")) |
67 | hook_fault_code(16 + 6, armada_375_external_abort_wa, SIGBUS, 0, | 84 | hook_fault_code(16 + 6, armada_375_external_abort_wa, SIGBUS, 0, |
@@ -78,7 +95,7 @@ static void __init i2c_quirk(void) | |||
78 | * mechanism. We can exit only if we are sure that we can | 95 | * mechanism. We can exit only if we are sure that we can |
79 | * get the SoC revision and it is more recent than A0. | 96 | * get the SoC revision and it is more recent than A0. |
80 | */ | 97 | */ |
81 | if (mvebu_get_soc_id(&rev, &dev) == 0 && dev > MV78XX0_A0_REV) | 98 | if (mvebu_get_soc_id(&dev, &rev) == 0 && rev > MV78XX0_A0_REV) |
82 | return; | 99 | return; |
83 | 100 | ||
84 | for_each_compatible_node(np, NULL, "marvell,mv78230-i2c") { | 101 | for_each_compatible_node(np, NULL, "marvell,mv78230-i2c") { |
@@ -96,10 +113,66 @@ static void __init i2c_quirk(void) | |||
96 | return; | 113 | return; |
97 | } | 114 | } |
98 | 115 | ||
116 | #define A375_Z1_THERMAL_FIXUP_OFFSET 0xc | ||
117 | |||
118 | static void __init thermal_quirk(void) | ||
119 | { | ||
120 | struct device_node *np; | ||
121 | u32 dev, rev; | ||
122 | |||
123 | if (mvebu_get_soc_id(&dev, &rev) == 0 && rev > ARMADA_375_Z1_REV) | ||
124 | return; | ||
125 | |||
126 | for_each_compatible_node(np, NULL, "marvell,armada375-thermal") { | ||
127 | struct property *prop; | ||
128 | __be32 newval, *newprop, *oldprop; | ||
129 | int len; | ||
130 | |||
131 | /* | ||
132 | * The register offset is at a wrong location. This quirk | ||
133 | * creates a new reg property as a clone of the previous | ||
134 | * one and corrects the offset. | ||
135 | */ | ||
136 | oldprop = (__be32 *)of_get_property(np, "reg", &len); | ||
137 | if (!oldprop) | ||
138 | continue; | ||
139 | |||
140 | /* Create a duplicate of the 'reg' property */ | ||
141 | prop = kzalloc(sizeof(*prop), GFP_KERNEL); | ||
142 | prop->length = len; | ||
143 | prop->name = kstrdup("reg", GFP_KERNEL); | ||
144 | prop->value = kzalloc(len, GFP_KERNEL); | ||
145 | memcpy(prop->value, oldprop, len); | ||
146 | |||
147 | /* Fixup the register offset of the second entry */ | ||
148 | oldprop += 2; | ||
149 | newprop = (__be32 *)prop->value + 2; | ||
150 | newval = cpu_to_be32(be32_to_cpu(*oldprop) - | ||
151 | A375_Z1_THERMAL_FIXUP_OFFSET); | ||
152 | *newprop = newval; | ||
153 | of_update_property(np, prop); | ||
154 | |||
155 | /* | ||
156 | * The thermal controller needs some quirk too, so let's change | ||
157 | * the compatible string to reflect this. | ||
158 | */ | ||
159 | prop = kzalloc(sizeof(*prop), GFP_KERNEL); | ||
160 | prop->name = kstrdup("compatible", GFP_KERNEL); | ||
161 | prop->length = sizeof("marvell,armada375-z1-thermal"); | ||
162 | prop->value = kstrdup("marvell,armada375-z1-thermal", | ||
163 | GFP_KERNEL); | ||
164 | of_update_property(np, prop); | ||
165 | } | ||
166 | return; | ||
167 | } | ||
168 | |||
99 | static void __init mvebu_dt_init(void) | 169 | static void __init mvebu_dt_init(void) |
100 | { | 170 | { |
101 | if (of_machine_is_compatible("plathome,openblocks-ax3-4")) | 171 | if (of_machine_is_compatible("plathome,openblocks-ax3-4")) |
102 | i2c_quirk(); | 172 | i2c_quirk(); |
173 | if (of_machine_is_compatible("marvell,a375-db")) | ||
174 | thermal_quirk(); | ||
175 | |||
103 | of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); | 176 | of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); |
104 | } | 177 | } |
105 | 178 | ||
@@ -109,6 +182,8 @@ static const char * const armada_370_xp_dt_compat[] = { | |||
109 | }; | 182 | }; |
110 | 183 | ||
111 | DT_MACHINE_START(ARMADA_370_XP_DT, "Marvell Armada 370/XP (Device Tree)") | 184 | DT_MACHINE_START(ARMADA_370_XP_DT, "Marvell Armada 370/XP (Device Tree)") |
185 | .l2c_aux_val = 0, | ||
186 | .l2c_aux_mask = ~0, | ||
112 | .smp = smp_ops(armada_xp_smp_ops), | 187 | .smp = smp_ops(armada_xp_smp_ops), |
113 | .init_machine = mvebu_dt_init, | 188 | .init_machine = mvebu_dt_init, |
114 | .init_time = mvebu_timer_and_clk_init, | 189 | .init_time = mvebu_timer_and_clk_init, |
@@ -122,7 +197,10 @@ static const char * const armada_375_dt_compat[] = { | |||
122 | }; | 197 | }; |
123 | 198 | ||
124 | DT_MACHINE_START(ARMADA_375_DT, "Marvell Armada 375 (Device Tree)") | 199 | DT_MACHINE_START(ARMADA_375_DT, "Marvell Armada 375 (Device Tree)") |
200 | .l2c_aux_val = 0, | ||
201 | .l2c_aux_mask = ~0, | ||
125 | .init_time = mvebu_timer_and_clk_init, | 202 | .init_time = mvebu_timer_and_clk_init, |
203 | .init_machine = mvebu_dt_init, | ||
126 | .restart = mvebu_restart, | 204 | .restart = mvebu_restart, |
127 | .dt_compat = armada_375_dt_compat, | 205 | .dt_compat = armada_375_dt_compat, |
128 | MACHINE_END | 206 | MACHINE_END |
@@ -134,6 +212,8 @@ static const char * const armada_38x_dt_compat[] = { | |||
134 | }; | 212 | }; |
135 | 213 | ||
136 | DT_MACHINE_START(ARMADA_38X_DT, "Marvell Armada 380/385 (Device Tree)") | 214 | DT_MACHINE_START(ARMADA_38X_DT, "Marvell Armada 380/385 (Device Tree)") |
215 | .l2c_aux_val = 0, | ||
216 | .l2c_aux_mask = ~0, | ||
137 | .init_time = mvebu_timer_and_clk_init, | 217 | .init_time = mvebu_timer_and_clk_init, |
138 | .restart = mvebu_restart, | 218 | .restart = mvebu_restart, |
139 | .dt_compat = armada_38x_dt_compat, | 219 | .dt_compat = armada_38x_dt_compat, |
diff --git a/arch/arm/mach-mvebu/board.h b/arch/arm/mach-mvebu/board.h index de7f0a191394..9c7bb4386f8b 100644 --- a/arch/arm/mach-mvebu/board.h +++ b/arch/arm/mach-mvebu/board.h | |||
@@ -13,10 +13,4 @@ | |||
13 | #ifndef __ARCH_MVEBU_BOARD_H | 13 | #ifndef __ARCH_MVEBU_BOARD_H |
14 | #define __ARCH_MVEBU_BOARD_H | 14 | #define __ARCH_MVEBU_BOARD_H |
15 | 15 | ||
16 | #ifdef CONFIG_MACH_T5325 | ||
17 | void t5325_init(void); | ||
18 | #else | ||
19 | static inline void t5325_init(void) {}; | ||
20 | #endif | ||
21 | |||
22 | #endif | 16 | #endif |
diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c index 4e9d58148ca7..477202fd39cc 100644 --- a/arch/arm/mach-mvebu/coherency.c +++ b/arch/arm/mach-mvebu/coherency.c | |||
@@ -17,6 +17,8 @@ | |||
17 | * supplies basic routines for configuring and controlling hardware coherency | 17 | * supplies basic routines for configuring and controlling hardware coherency |
18 | */ | 18 | */ |
19 | 19 | ||
20 | #define pr_fmt(fmt) "mvebu-coherency: " fmt | ||
21 | |||
20 | #include <linux/kernel.h> | 22 | #include <linux/kernel.h> |
21 | #include <linux/init.h> | 23 | #include <linux/init.h> |
22 | #include <linux/of_address.h> | 24 | #include <linux/of_address.h> |
@@ -24,13 +26,19 @@ | |||
24 | #include <linux/smp.h> | 26 | #include <linux/smp.h> |
25 | #include <linux/dma-mapping.h> | 27 | #include <linux/dma-mapping.h> |
26 | #include <linux/platform_device.h> | 28 | #include <linux/platform_device.h> |
29 | #include <linux/slab.h> | ||
30 | #include <linux/mbus.h> | ||
31 | #include <linux/clk.h> | ||
32 | #include <linux/pci.h> | ||
27 | #include <asm/smp_plat.h> | 33 | #include <asm/smp_plat.h> |
28 | #include <asm/cacheflush.h> | 34 | #include <asm/cacheflush.h> |
35 | #include <asm/mach/map.h> | ||
29 | #include "armada-370-xp.h" | 36 | #include "armada-370-xp.h" |
30 | #include "coherency.h" | 37 | #include "coherency.h" |
38 | #include "mvebu-soc-id.h" | ||
31 | 39 | ||
32 | unsigned long coherency_phys_base; | 40 | unsigned long coherency_phys_base; |
33 | static void __iomem *coherency_base; | 41 | void __iomem *coherency_base; |
34 | static void __iomem *coherency_cpu_base; | 42 | static void __iomem *coherency_cpu_base; |
35 | 43 | ||
36 | /* Coherency fabric registers */ | 44 | /* Coherency fabric registers */ |
@@ -38,27 +46,190 @@ static void __iomem *coherency_cpu_base; | |||
38 | 46 | ||
39 | #define IO_SYNC_BARRIER_CTL_OFFSET 0x0 | 47 | #define IO_SYNC_BARRIER_CTL_OFFSET 0x0 |
40 | 48 | ||
49 | enum { | ||
50 | COHERENCY_FABRIC_TYPE_NONE, | ||
51 | COHERENCY_FABRIC_TYPE_ARMADA_370_XP, | ||
52 | COHERENCY_FABRIC_TYPE_ARMADA_375, | ||
53 | COHERENCY_FABRIC_TYPE_ARMADA_380, | ||
54 | }; | ||
55 | |||
41 | static struct of_device_id of_coherency_table[] = { | 56 | static struct of_device_id of_coherency_table[] = { |
42 | {.compatible = "marvell,coherency-fabric"}, | 57 | {.compatible = "marvell,coherency-fabric", |
58 | .data = (void *) COHERENCY_FABRIC_TYPE_ARMADA_370_XP }, | ||
59 | {.compatible = "marvell,armada-375-coherency-fabric", | ||
60 | .data = (void *) COHERENCY_FABRIC_TYPE_ARMADA_375 }, | ||
61 | {.compatible = "marvell,armada-380-coherency-fabric", | ||
62 | .data = (void *) COHERENCY_FABRIC_TYPE_ARMADA_380 }, | ||
43 | { /* end of list */ }, | 63 | { /* end of list */ }, |
44 | }; | 64 | }; |
45 | 65 | ||
46 | /* Function defined in coherency_ll.S */ | 66 | /* Functions defined in coherency_ll.S */ |
47 | int ll_set_cpu_coherent(void __iomem *base_addr, unsigned int hw_cpu_id); | 67 | int ll_enable_coherency(void); |
68 | void ll_add_cpu_to_smp_group(void); | ||
48 | 69 | ||
49 | int set_cpu_coherent(unsigned int hw_cpu_id, int smp_group_id) | 70 | int set_cpu_coherent(void) |
50 | { | 71 | { |
51 | if (!coherency_base) { | 72 | if (!coherency_base) { |
52 | pr_warn("Can't make CPU %d cache coherent.\n", hw_cpu_id); | 73 | pr_warn("Can't make current CPU cache coherent.\n"); |
53 | pr_warn("Coherency fabric is not initialized\n"); | 74 | pr_warn("Coherency fabric is not initialized\n"); |
54 | return 1; | 75 | return 1; |
55 | } | 76 | } |
56 | 77 | ||
57 | return ll_set_cpu_coherent(coherency_base, hw_cpu_id); | 78 | ll_add_cpu_to_smp_group(); |
79 | return ll_enable_coherency(); | ||
80 | } | ||
81 | |||
82 | /* | ||
83 | * The below code implements the I/O coherency workaround on Armada | ||
84 | * 375. This workaround consists in using the two channels of the | ||
85 | * first XOR engine to trigger a XOR transaction that serves as the | ||
86 | * I/O coherency barrier. | ||
87 | */ | ||
88 | |||
89 | static void __iomem *xor_base, *xor_high_base; | ||
90 | static dma_addr_t coherency_wa_buf_phys[CONFIG_NR_CPUS]; | ||
91 | static void *coherency_wa_buf[CONFIG_NR_CPUS]; | ||
92 | static bool coherency_wa_enabled; | ||
93 | |||
94 | #define XOR_CONFIG(chan) (0x10 + (chan * 4)) | ||
95 | #define XOR_ACTIVATION(chan) (0x20 + (chan * 4)) | ||
96 | #define WINDOW_BAR_ENABLE(chan) (0x240 + ((chan) << 2)) | ||
97 | #define WINDOW_BASE(w) (0x250 + ((w) << 2)) | ||
98 | #define WINDOW_SIZE(w) (0x270 + ((w) << 2)) | ||
99 | #define WINDOW_REMAP_HIGH(w) (0x290 + ((w) << 2)) | ||
100 | #define WINDOW_OVERRIDE_CTRL(chan) (0x2A0 + ((chan) << 2)) | ||
101 | #define XOR_DEST_POINTER(chan) (0x2B0 + (chan * 4)) | ||
102 | #define XOR_BLOCK_SIZE(chan) (0x2C0 + (chan * 4)) | ||
103 | #define XOR_INIT_VALUE_LOW 0x2E0 | ||
104 | #define XOR_INIT_VALUE_HIGH 0x2E4 | ||
105 | |||
106 | static inline void mvebu_hwcc_armada375_sync_io_barrier_wa(void) | ||
107 | { | ||
108 | int idx = smp_processor_id(); | ||
109 | |||
110 | /* Write '1' to the first word of the buffer */ | ||
111 | writel(0x1, coherency_wa_buf[idx]); | ||
112 | |||
113 | /* Wait until the engine is idle */ | ||
114 | while ((readl(xor_base + XOR_ACTIVATION(idx)) >> 4) & 0x3) | ||
115 | ; | ||
116 | |||
117 | dmb(); | ||
118 | |||
119 | /* Trigger channel */ | ||
120 | writel(0x1, xor_base + XOR_ACTIVATION(idx)); | ||
121 | |||
122 | /* Poll the data until it is cleared by the XOR transaction */ | ||
123 | while (readl(coherency_wa_buf[idx])) | ||
124 | ; | ||
125 | } | ||
126 | |||
127 | static void __init armada_375_coherency_init_wa(void) | ||
128 | { | ||
129 | const struct mbus_dram_target_info *dram; | ||
130 | struct device_node *xor_node; | ||
131 | struct property *xor_status; | ||
132 | struct clk *xor_clk; | ||
133 | u32 win_enable = 0; | ||
134 | int i; | ||
135 | |||
136 | pr_warn("enabling coherency workaround for Armada 375 Z1, one XOR engine disabled\n"); | ||
137 | |||
138 | /* | ||
139 | * Since the workaround uses one XOR engine, we grab a | ||
140 | * reference to its Device Tree node first. | ||
141 | */ | ||
142 | xor_node = of_find_compatible_node(NULL, NULL, "marvell,orion-xor"); | ||
143 | BUG_ON(!xor_node); | ||
144 | |||
145 | /* | ||
146 | * Then we mark it as disabled so that the real XOR driver | ||
147 | * will not use it. | ||
148 | */ | ||
149 | xor_status = kzalloc(sizeof(struct property), GFP_KERNEL); | ||
150 | BUG_ON(!xor_status); | ||
151 | |||
152 | xor_status->value = kstrdup("disabled", GFP_KERNEL); | ||
153 | BUG_ON(!xor_status->value); | ||
154 | |||
155 | xor_status->length = 8; | ||
156 | xor_status->name = kstrdup("status", GFP_KERNEL); | ||
157 | BUG_ON(!xor_status->name); | ||
158 | |||
159 | of_update_property(xor_node, xor_status); | ||
160 | |||
161 | /* | ||
162 | * And we remap the registers, get the clock, and do the | ||
163 | * initial configuration of the XOR engine. | ||
164 | */ | ||
165 | xor_base = of_iomap(xor_node, 0); | ||
166 | xor_high_base = of_iomap(xor_node, 1); | ||
167 | |||
168 | xor_clk = of_clk_get_by_name(xor_node, NULL); | ||
169 | BUG_ON(!xor_clk); | ||
170 | |||
171 | clk_prepare_enable(xor_clk); | ||
172 | |||
173 | dram = mv_mbus_dram_info(); | ||
174 | |||
175 | for (i = 0; i < 8; i++) { | ||
176 | writel(0, xor_base + WINDOW_BASE(i)); | ||
177 | writel(0, xor_base + WINDOW_SIZE(i)); | ||
178 | if (i < 4) | ||
179 | writel(0, xor_base + WINDOW_REMAP_HIGH(i)); | ||
180 | } | ||
181 | |||
182 | for (i = 0; i < dram->num_cs; i++) { | ||
183 | const struct mbus_dram_window *cs = dram->cs + i; | ||
184 | writel((cs->base & 0xffff0000) | | ||
185 | (cs->mbus_attr << 8) | | ||
186 | dram->mbus_dram_target_id, xor_base + WINDOW_BASE(i)); | ||
187 | writel((cs->size - 1) & 0xffff0000, xor_base + WINDOW_SIZE(i)); | ||
188 | |||
189 | win_enable |= (1 << i); | ||
190 | win_enable |= 3 << (16 + (2 * i)); | ||
191 | } | ||
192 | |||
193 | writel(win_enable, xor_base + WINDOW_BAR_ENABLE(0)); | ||
194 | writel(win_enable, xor_base + WINDOW_BAR_ENABLE(1)); | ||
195 | writel(0, xor_base + WINDOW_OVERRIDE_CTRL(0)); | ||
196 | writel(0, xor_base + WINDOW_OVERRIDE_CTRL(1)); | ||
197 | |||
198 | for (i = 0; i < CONFIG_NR_CPUS; i++) { | ||
199 | coherency_wa_buf[i] = kzalloc(PAGE_SIZE, GFP_KERNEL); | ||
200 | BUG_ON(!coherency_wa_buf[i]); | ||
201 | |||
202 | /* | ||
203 | * We can't use the DMA mapping API, since we don't | ||
204 | * have a valid 'struct device' pointer | ||
205 | */ | ||
206 | coherency_wa_buf_phys[i] = | ||
207 | virt_to_phys(coherency_wa_buf[i]); | ||
208 | BUG_ON(!coherency_wa_buf_phys[i]); | ||
209 | |||
210 | /* | ||
211 | * Configure the XOR engine for memset operation, with | ||
212 | * a 128 bytes block size | ||
213 | */ | ||
214 | writel(0x444, xor_base + XOR_CONFIG(i)); | ||
215 | writel(128, xor_base + XOR_BLOCK_SIZE(i)); | ||
216 | writel(coherency_wa_buf_phys[i], | ||
217 | xor_base + XOR_DEST_POINTER(i)); | ||
218 | } | ||
219 | |||
220 | writel(0x0, xor_base + XOR_INIT_VALUE_LOW); | ||
221 | writel(0x0, xor_base + XOR_INIT_VALUE_HIGH); | ||
222 | |||
223 | coherency_wa_enabled = true; | ||
58 | } | 224 | } |
59 | 225 | ||
60 | static inline void mvebu_hwcc_sync_io_barrier(void) | 226 | static inline void mvebu_hwcc_sync_io_barrier(void) |
61 | { | 227 | { |
228 | if (coherency_wa_enabled) { | ||
229 | mvebu_hwcc_armada375_sync_io_barrier_wa(); | ||
230 | return; | ||
231 | } | ||
232 | |||
62 | writel(0x1, coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET); | 233 | writel(0x1, coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET); |
63 | while (readl(coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET) & 0x1); | 234 | while (readl(coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET) & 0x1); |
64 | } | 235 | } |
@@ -105,8 +276,8 @@ static struct dma_map_ops mvebu_hwcc_dma_ops = { | |||
105 | .set_dma_mask = arm_dma_set_mask, | 276 | .set_dma_mask = arm_dma_set_mask, |
106 | }; | 277 | }; |
107 | 278 | ||
108 | static int mvebu_hwcc_platform_notifier(struct notifier_block *nb, | 279 | static int mvebu_hwcc_notifier(struct notifier_block *nb, |
109 | unsigned long event, void *__dev) | 280 | unsigned long event, void *__dev) |
110 | { | 281 | { |
111 | struct device *dev = __dev; | 282 | struct device *dev = __dev; |
112 | 283 | ||
@@ -117,47 +288,148 @@ static int mvebu_hwcc_platform_notifier(struct notifier_block *nb, | |||
117 | return NOTIFY_OK; | 288 | return NOTIFY_OK; |
118 | } | 289 | } |
119 | 290 | ||
120 | static struct notifier_block mvebu_hwcc_platform_nb = { | 291 | static struct notifier_block mvebu_hwcc_nb = { |
121 | .notifier_call = mvebu_hwcc_platform_notifier, | 292 | .notifier_call = mvebu_hwcc_notifier, |
122 | }; | 293 | }; |
123 | 294 | ||
124 | int __init coherency_init(void) | 295 | static void __init armada_370_coherency_init(struct device_node *np) |
296 | { | ||
297 | struct resource res; | ||
298 | |||
299 | of_address_to_resource(np, 0, &res); | ||
300 | coherency_phys_base = res.start; | ||
301 | /* | ||
302 | * Ensure secondary CPUs will see the updated value, | ||
303 | * which they read before they join the coherency | ||
304 | * fabric, and therefore before they are coherent with | ||
305 | * the boot CPU cache. | ||
306 | */ | ||
307 | sync_cache_w(&coherency_phys_base); | ||
308 | coherency_base = of_iomap(np, 0); | ||
309 | coherency_cpu_base = of_iomap(np, 1); | ||
310 | set_cpu_coherent(); | ||
311 | } | ||
312 | |||
313 | /* | ||
314 | * This ioremap hook is used on Armada 375/38x to ensure that PCIe | ||
315 | * memory areas are mapped as MT_UNCACHED instead of MT_DEVICE. This | ||
316 | * is needed as a workaround for a deadlock issue between the PCIe | ||
317 | * interface and the cache controller. | ||
318 | */ | ||
319 | static void __iomem * | ||
320 | armada_pcie_wa_ioremap_caller(phys_addr_t phys_addr, size_t size, | ||
321 | unsigned int mtype, void *caller) | ||
322 | { | ||
323 | struct resource pcie_mem; | ||
324 | |||
325 | mvebu_mbus_get_pcie_mem_aperture(&pcie_mem); | ||
326 | |||
327 | if (pcie_mem.start <= phys_addr && (phys_addr + size) <= pcie_mem.end) | ||
328 | mtype = MT_UNCACHED; | ||
329 | |||
330 | return __arm_ioremap_caller(phys_addr, size, mtype, caller); | ||
331 | } | ||
332 | |||
333 | static void __init armada_375_380_coherency_init(struct device_node *np) | ||
334 | { | ||
335 | struct device_node *cache_dn; | ||
336 | |||
337 | coherency_cpu_base = of_iomap(np, 0); | ||
338 | arch_ioremap_caller = armada_pcie_wa_ioremap_caller; | ||
339 | |||
340 | /* | ||
341 | * Add the PL310 property "arm,io-coherent". This makes sure the | ||
342 | * outer sync operation is not used, which allows to | ||
343 | * workaround the system erratum that causes deadlocks when | ||
344 | * doing PCIe in an SMP situation on Armada 375 and Armada | ||
345 | * 38x. | ||
346 | */ | ||
347 | for_each_compatible_node(cache_dn, NULL, "arm,pl310-cache") { | ||
348 | struct property *p; | ||
349 | |||
350 | p = kzalloc(sizeof(*p), GFP_KERNEL); | ||
351 | p->name = kstrdup("arm,io-coherent", GFP_KERNEL); | ||
352 | of_add_property(cache_dn, p); | ||
353 | } | ||
354 | } | ||
355 | |||
356 | static int coherency_type(void) | ||
125 | { | 357 | { |
126 | struct device_node *np; | 358 | struct device_node *np; |
359 | const struct of_device_id *match; | ||
127 | 360 | ||
128 | np = of_find_matching_node(NULL, of_coherency_table); | 361 | np = of_find_matching_node_and_match(NULL, of_coherency_table, &match); |
129 | if (np) { | 362 | if (np) { |
130 | struct resource res; | 363 | int type = (int) match->data; |
131 | pr_info("Initializing Coherency fabric\n"); | 364 | |
132 | of_address_to_resource(np, 0, &res); | 365 | /* Armada 370/XP coherency works in both UP and SMP */ |
133 | coherency_phys_base = res.start; | 366 | if (type == COHERENCY_FABRIC_TYPE_ARMADA_370_XP) |
134 | /* | 367 | return type; |
135 | * Ensure secondary CPUs will see the updated value, | 368 | |
136 | * which they read before they join the coherency | 369 | /* Armada 375 coherency works only on SMP */ |
137 | * fabric, and therefore before they are coherent with | 370 | else if (type == COHERENCY_FABRIC_TYPE_ARMADA_375 && is_smp()) |
138 | * the boot CPU cache. | 371 | return type; |
139 | */ | 372 | |
140 | sync_cache_w(&coherency_phys_base); | 373 | /* Armada 380 coherency works only on SMP */ |
141 | coherency_base = of_iomap(np, 0); | 374 | else if (type == COHERENCY_FABRIC_TYPE_ARMADA_380 && is_smp()) |
142 | coherency_cpu_base = of_iomap(np, 1); | 375 | return type; |
143 | set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0); | ||
144 | of_node_put(np); | ||
145 | } | 376 | } |
146 | 377 | ||
147 | return 0; | 378 | return COHERENCY_FABRIC_TYPE_NONE; |
148 | } | 379 | } |
149 | 380 | ||
150 | static int __init coherency_late_init(void) | 381 | int coherency_available(void) |
382 | { | ||
383 | return coherency_type() != COHERENCY_FABRIC_TYPE_NONE; | ||
384 | } | ||
385 | |||
386 | int __init coherency_init(void) | ||
151 | { | 387 | { |
388 | int type = coherency_type(); | ||
152 | struct device_node *np; | 389 | struct device_node *np; |
153 | 390 | ||
154 | np = of_find_matching_node(NULL, of_coherency_table); | 391 | np = of_find_matching_node(NULL, of_coherency_table); |
155 | if (np) { | 392 | |
156 | bus_register_notifier(&platform_bus_type, | 393 | if (type == COHERENCY_FABRIC_TYPE_ARMADA_370_XP) |
157 | &mvebu_hwcc_platform_nb); | 394 | armada_370_coherency_init(np); |
158 | of_node_put(np); | 395 | else if (type == COHERENCY_FABRIC_TYPE_ARMADA_375 || |
396 | type == COHERENCY_FABRIC_TYPE_ARMADA_380) | ||
397 | armada_375_380_coherency_init(np); | ||
398 | |||
399 | return 0; | ||
400 | } | ||
401 | |||
402 | static int __init coherency_late_init(void) | ||
403 | { | ||
404 | int type = coherency_type(); | ||
405 | |||
406 | if (type == COHERENCY_FABRIC_TYPE_NONE) | ||
407 | return 0; | ||
408 | |||
409 | if (type == COHERENCY_FABRIC_TYPE_ARMADA_375) { | ||
410 | u32 dev, rev; | ||
411 | |||
412 | if (mvebu_get_soc_id(&dev, &rev) == 0 && | ||
413 | rev == ARMADA_375_Z1_REV) | ||
414 | armada_375_coherency_init_wa(); | ||
159 | } | 415 | } |
416 | |||
417 | bus_register_notifier(&platform_bus_type, | ||
418 | &mvebu_hwcc_nb); | ||
419 | |||
160 | return 0; | 420 | return 0; |
161 | } | 421 | } |
162 | 422 | ||
163 | postcore_initcall(coherency_late_init); | 423 | postcore_initcall(coherency_late_init); |
424 | |||
425 | #if IS_ENABLED(CONFIG_PCI) | ||
426 | static int __init coherency_pci_init(void) | ||
427 | { | ||
428 | if (coherency_available()) | ||
429 | bus_register_notifier(&pci_bus_type, | ||
430 | &mvebu_hwcc_nb); | ||
431 | return 0; | ||
432 | } | ||
433 | |||
434 | arch_initcall(coherency_pci_init); | ||
435 | #endif | ||
diff --git a/arch/arm/mach-mvebu/coherency.h b/arch/arm/mach-mvebu/coherency.h index 760226c41353..54cb7607b526 100644 --- a/arch/arm/mach-mvebu/coherency.h +++ b/arch/arm/mach-mvebu/coherency.h | |||
@@ -15,8 +15,9 @@ | |||
15 | #define __MACH_370_XP_COHERENCY_H | 15 | #define __MACH_370_XP_COHERENCY_H |
16 | 16 | ||
17 | extern unsigned long coherency_phys_base; | 17 | extern unsigned long coherency_phys_base; |
18 | int set_cpu_coherent(void); | ||
18 | 19 | ||
19 | int set_cpu_coherent(unsigned int cpu_id, int smp_group_id); | ||
20 | int coherency_init(void); | 20 | int coherency_init(void); |
21 | int coherency_available(void); | ||
21 | 22 | ||
22 | #endif /* __MACH_370_XP_COHERENCY_H */ | 23 | #endif /* __MACH_370_XP_COHERENCY_H */ |
diff --git a/arch/arm/mach-mvebu/coherency_ll.S b/arch/arm/mach-mvebu/coherency_ll.S index ee7598fe75db..510c29e079ca 100644 --- a/arch/arm/mach-mvebu/coherency_ll.S +++ b/arch/arm/mach-mvebu/coherency_ll.S | |||
@@ -21,38 +21,129 @@ | |||
21 | #define ARMADA_XP_CFB_CFG_REG_OFFSET 0x4 | 21 | #define ARMADA_XP_CFB_CFG_REG_OFFSET 0x4 |
22 | 22 | ||
23 | #include <asm/assembler.h> | 23 | #include <asm/assembler.h> |
24 | #include <asm/cp15.h> | ||
24 | 25 | ||
25 | .text | 26 | .text |
27 | /* Returns the coherency base address in r1 (r0 is untouched) */ | ||
28 | ENTRY(ll_get_coherency_base) | ||
29 | mrc p15, 0, r1, c1, c0, 0 | ||
30 | tst r1, #CR_M @ Check MMU bit enabled | ||
31 | bne 1f | ||
32 | |||
33 | /* | ||
34 | * MMU is disabled, use the physical address of the coherency | ||
35 | * base address. | ||
36 | */ | ||
37 | adr r1, 3f | ||
38 | ldr r3, [r1] | ||
39 | ldr r1, [r1, r3] | ||
40 | b 2f | ||
41 | 1: | ||
42 | /* | ||
43 | * MMU is enabled, use the virtual address of the coherency | ||
44 | * base address. | ||
45 | */ | ||
46 | ldr r1, =coherency_base | ||
47 | ldr r1, [r1] | ||
48 | 2: | ||
49 | mov pc, lr | ||
50 | ENDPROC(ll_get_coherency_base) | ||
51 | |||
26 | /* | 52 | /* |
27 | * r0: Coherency fabric base register address | 53 | * Returns the coherency CPU mask in r3 (r0 is untouched). This |
28 | * r1: HW CPU id | 54 | * coherency CPU mask can be used with the coherency fabric |
55 | * configuration and control registers. Note that the mask is already | ||
56 | * endian-swapped as appropriate so that the calling functions do not | ||
57 | * have to care about endianness issues while accessing the coherency | ||
58 | * fabric registers | ||
29 | */ | 59 | */ |
30 | ENTRY(ll_set_cpu_coherent) | 60 | ENTRY(ll_get_coherency_cpumask) |
31 | /* Create bit by cpu index */ | 61 | mrc 15, 0, r3, cr0, cr0, 5 |
32 | mov r3, #(1 << 24) | 62 | and r3, r3, #15 |
33 | lsl r1, r3, r1 | 63 | mov r2, #(1 << 24) |
34 | ARM_BE8(rev r1, r1) | 64 | lsl r3, r2, r3 |
35 | 65 | ARM_BE8(rev r3, r3) | |
36 | /* Add CPU to SMP group - Atomic */ | 66 | mov pc, lr |
37 | add r3, r0, #ARMADA_XP_CFB_CTL_REG_OFFSET | 67 | ENDPROC(ll_get_coherency_cpumask) |
38 | 1: | 68 | |
39 | ldrex r2, [r3] | 69 | /* |
40 | orr r2, r2, r1 | 70 | * ll_add_cpu_to_smp_group(), ll_enable_coherency() and |
41 | strex r0, r2, [r3] | 71 | * ll_disable_coherency() use the strex/ldrex instructions while the |
42 | cmp r0, #0 | 72 | * MMU can be disabled. The Armada XP SoC has an exclusive monitor |
43 | bne 1b | 73 | * that tracks transactions to Device and/or SO memory and thanks to |
44 | 74 | * that, exclusive transactions are functional even when the MMU is | |
45 | /* Enable coherency on CPU - Atomic */ | 75 | * disabled. |
46 | add r3, r3, #ARMADA_XP_CFB_CFG_REG_OFFSET | 76 | */ |
77 | |||
78 | ENTRY(ll_add_cpu_to_smp_group) | ||
79 | /* | ||
80 | * As r0 is not modified by ll_get_coherency_base() and | ||
81 | * ll_get_coherency_cpumask(), we use it to temporarly save lr | ||
82 | * and avoid it being modified by the branch and link | ||
83 | * calls. This function is used very early in the secondary | ||
84 | * CPU boot, and no stack is available at this point. | ||
85 | */ | ||
86 | mov r0, lr | ||
87 | bl ll_get_coherency_base | ||
88 | bl ll_get_coherency_cpumask | ||
89 | mov lr, r0 | ||
90 | add r0, r1, #ARMADA_XP_CFB_CFG_REG_OFFSET | ||
47 | 1: | 91 | 1: |
48 | ldrex r2, [r3] | 92 | ldrex r2, [r0] |
49 | orr r2, r2, r1 | 93 | orr r2, r2, r3 |
50 | strex r0, r2, [r3] | 94 | strex r1, r2, [r0] |
51 | cmp r0, #0 | 95 | cmp r1, #0 |
52 | bne 1b | 96 | bne 1b |
97 | mov pc, lr | ||
98 | ENDPROC(ll_add_cpu_to_smp_group) | ||
53 | 99 | ||
100 | ENTRY(ll_enable_coherency) | ||
101 | /* | ||
102 | * As r0 is not modified by ll_get_coherency_base() and | ||
103 | * ll_get_coherency_cpumask(), we use it to temporarly save lr | ||
104 | * and avoid it being modified by the branch and link | ||
105 | * calls. This function is used very early in the secondary | ||
106 | * CPU boot, and no stack is available at this point. | ||
107 | */ | ||
108 | mov r0, lr | ||
109 | bl ll_get_coherency_base | ||
110 | bl ll_get_coherency_cpumask | ||
111 | mov lr, r0 | ||
112 | add r0, r1, #ARMADA_XP_CFB_CTL_REG_OFFSET | ||
113 | 1: | ||
114 | ldrex r2, [r0] | ||
115 | orr r2, r2, r3 | ||
116 | strex r1, r2, [r0] | ||
117 | cmp r1, #0 | ||
118 | bne 1b | ||
54 | dsb | 119 | dsb |
55 | |||
56 | mov r0, #0 | 120 | mov r0, #0 |
57 | mov pc, lr | 121 | mov pc, lr |
58 | ENDPROC(ll_set_cpu_coherent) | 122 | ENDPROC(ll_enable_coherency) |
123 | |||
124 | ENTRY(ll_disable_coherency) | ||
125 | /* | ||
126 | * As r0 is not modified by ll_get_coherency_base() and | ||
127 | * ll_get_coherency_cpumask(), we use it to temporarly save lr | ||
128 | * and avoid it being modified by the branch and link | ||
129 | * calls. This function is used very early in the secondary | ||
130 | * CPU boot, and no stack is available at this point. | ||
131 | */ | ||
132 | mov r0, lr | ||
133 | bl ll_get_coherency_base | ||
134 | bl ll_get_coherency_cpumask | ||
135 | mov lr, r0 | ||
136 | add r0, r1, #ARMADA_XP_CFB_CTL_REG_OFFSET | ||
137 | 1: | ||
138 | ldrex r2, [r0] | ||
139 | bic r2, r2, r3 | ||
140 | strex r1, r2, [r0] | ||
141 | cmp r1, #0 | ||
142 | bne 1b | ||
143 | dsb | ||
144 | mov pc, lr | ||
145 | ENDPROC(ll_disable_coherency) | ||
146 | |||
147 | .align 2 | ||
148 | 3: | ||
149 | .long coherency_phys_base - . | ||
diff --git a/arch/arm/mach-mvebu/common.h b/arch/arm/mach-mvebu/common.h index 55449c487c9e..b67fb7a10d8b 100644 --- a/arch/arm/mach-mvebu/common.h +++ b/arch/arm/mach-mvebu/common.h | |||
@@ -18,6 +18,9 @@ | |||
18 | #include <linux/reboot.h> | 18 | #include <linux/reboot.h> |
19 | 19 | ||
20 | void mvebu_restart(enum reboot_mode mode, const char *cmd); | 20 | void mvebu_restart(enum reboot_mode mode, const char *cmd); |
21 | int mvebu_cpu_reset_deassert(int cpu); | ||
22 | void mvebu_pmsu_set_cpu_boot_addr(int hw_cpu, void *boot_addr); | ||
23 | void mvebu_system_controller_set_cpu_boot_addr(void *boot_addr); | ||
21 | 24 | ||
22 | void armada_xp_cpu_die(unsigned int cpu); | 25 | void armada_xp_cpu_die(unsigned int cpu); |
23 | 26 | ||
diff --git a/arch/arm/mach-mvebu/cpu-reset.c b/arch/arm/mach-mvebu/cpu-reset.c new file mode 100644 index 000000000000..4a8f9eebebea --- /dev/null +++ b/arch/arm/mach-mvebu/cpu-reset.c | |||
@@ -0,0 +1,103 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014 Marvell | ||
3 | * | ||
4 | * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> | ||
5 | * | ||
6 | * This file is licensed under the terms of the GNU General Public | ||
7 | * License version 2. This program is licensed "as is" without any | ||
8 | * warranty of any kind, whether express or implied. | ||
9 | */ | ||
10 | |||
11 | #define pr_fmt(fmt) "mvebu-cpureset: " fmt | ||
12 | |||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/of_address.h> | ||
16 | #include <linux/io.h> | ||
17 | #include <linux/resource.h> | ||
18 | #include "armada-370-xp.h" | ||
19 | |||
20 | static void __iomem *cpu_reset_base; | ||
21 | static size_t cpu_reset_size; | ||
22 | |||
23 | #define CPU_RESET_OFFSET(cpu) (cpu * 0x8) | ||
24 | #define CPU_RESET_ASSERT BIT(0) | ||
25 | |||
26 | int mvebu_cpu_reset_deassert(int cpu) | ||
27 | { | ||
28 | u32 reg; | ||
29 | |||
30 | if (!cpu_reset_base) | ||
31 | return -ENODEV; | ||
32 | |||
33 | if (CPU_RESET_OFFSET(cpu) >= cpu_reset_size) | ||
34 | return -EINVAL; | ||
35 | |||
36 | reg = readl(cpu_reset_base + CPU_RESET_OFFSET(cpu)); | ||
37 | reg &= ~CPU_RESET_ASSERT; | ||
38 | writel(reg, cpu_reset_base + CPU_RESET_OFFSET(cpu)); | ||
39 | |||
40 | return 0; | ||
41 | } | ||
42 | |||
43 | static int mvebu_cpu_reset_map(struct device_node *np, int res_idx) | ||
44 | { | ||
45 | struct resource res; | ||
46 | |||
47 | if (of_address_to_resource(np, res_idx, &res)) { | ||
48 | pr_err("unable to get resource\n"); | ||
49 | return -ENOENT; | ||
50 | } | ||
51 | |||
52 | if (!request_mem_region(res.start, resource_size(&res), | ||
53 | np->full_name)) { | ||
54 | pr_err("unable to request region\n"); | ||
55 | return -EBUSY; | ||
56 | } | ||
57 | |||
58 | cpu_reset_base = ioremap(res.start, resource_size(&res)); | ||
59 | if (!cpu_reset_base) { | ||
60 | pr_err("unable to map registers\n"); | ||
61 | release_mem_region(res.start, resource_size(&res)); | ||
62 | return -ENOMEM; | ||
63 | } | ||
64 | |||
65 | cpu_reset_size = resource_size(&res); | ||
66 | |||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | int __init mvebu_cpu_reset_init(void) | ||
71 | { | ||
72 | struct device_node *np; | ||
73 | int res_idx; | ||
74 | int ret; | ||
75 | |||
76 | np = of_find_compatible_node(NULL, NULL, | ||
77 | "marvell,armada-370-cpu-reset"); | ||
78 | if (np) { | ||
79 | res_idx = 0; | ||
80 | } else { | ||
81 | /* | ||
82 | * This code is kept for backward compatibility with | ||
83 | * old Device Trees. | ||
84 | */ | ||
85 | np = of_find_compatible_node(NULL, NULL, | ||
86 | "marvell,armada-370-xp-pmsu"); | ||
87 | if (np) { | ||
88 | pr_warn(FW_WARN "deprecated pmsu binding\n"); | ||
89 | res_idx = 1; | ||
90 | } | ||
91 | } | ||
92 | |||
93 | /* No reset node found */ | ||
94 | if (!np) | ||
95 | return -ENODEV; | ||
96 | |||
97 | ret = mvebu_cpu_reset_map(np, res_idx); | ||
98 | of_node_put(np); | ||
99 | |||
100 | return ret; | ||
101 | } | ||
102 | |||
103 | early_initcall(mvebu_cpu_reset_init); | ||
diff --git a/arch/arm/mach-mvebu/dove.c b/arch/arm/mach-mvebu/dove.c index 5e5a43624237..b50464ec1130 100644 --- a/arch/arm/mach-mvebu/dove.c +++ b/arch/arm/mach-mvebu/dove.c | |||
@@ -23,7 +23,7 @@ static void __init dove_init(void) | |||
23 | #ifdef CONFIG_CACHE_TAUROS2 | 23 | #ifdef CONFIG_CACHE_TAUROS2 |
24 | tauros2_init(0); | 24 | tauros2_init(0); |
25 | #endif | 25 | #endif |
26 | BUG_ON(mvebu_mbus_dt_init()); | 26 | BUG_ON(mvebu_mbus_dt_init(false)); |
27 | of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); | 27 | of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); |
28 | } | 28 | } |
29 | 29 | ||
diff --git a/arch/arm/mach-mvebu/headsmp-a9.S b/arch/arm/mach-mvebu/headsmp-a9.S new file mode 100644 index 000000000000..5925366bc03c --- /dev/null +++ b/arch/arm/mach-mvebu/headsmp-a9.S | |||
@@ -0,0 +1,34 @@ | |||
1 | /* | ||
2 | * SMP support: Entry point for secondary CPUs of Marvell EBU | ||
3 | * Cortex-A9 based SOCs (Armada 375 and Armada 38x). | ||
4 | * | ||
5 | * Copyright (C) 2014 Marvell | ||
6 | * | ||
7 | * Gregory CLEMENT <gregory.clement@free-electrons.com> | ||
8 | * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> | ||
9 | * | ||
10 | * This file is licensed under the terms of the GNU General Public | ||
11 | * License version 2. This program is licensed "as is" without any | ||
12 | * warranty of any kind, whether express or implied. | ||
13 | */ | ||
14 | |||
15 | #include <linux/linkage.h> | ||
16 | #include <linux/init.h> | ||
17 | |||
18 | __CPUINIT | ||
19 | #define CPU_RESUME_ADDR_REG 0xf10182d4 | ||
20 | |||
21 | .global armada_375_smp_cpu1_enable_code_start | ||
22 | .global armada_375_smp_cpu1_enable_code_end | ||
23 | |||
24 | armada_375_smp_cpu1_enable_code_start: | ||
25 | ldr r0, [pc, #4] | ||
26 | ldr r1, [r0] | ||
27 | mov pc, r1 | ||
28 | .word CPU_RESUME_ADDR_REG | ||
29 | armada_375_smp_cpu1_enable_code_end: | ||
30 | |||
31 | ENTRY(mvebu_cortex_a9_secondary_startup) | ||
32 | bl v7_invalidate_l1 | ||
33 | b secondary_startup | ||
34 | ENDPROC(mvebu_cortex_a9_secondary_startup) | ||
diff --git a/arch/arm/mach-mvebu/headsmp.S b/arch/arm/mach-mvebu/headsmp.S index 3dd80df428f7..2c4032e368ba 100644 --- a/arch/arm/mach-mvebu/headsmp.S +++ b/arch/arm/mach-mvebu/headsmp.S | |||
@@ -31,21 +31,10 @@ | |||
31 | ENTRY(armada_xp_secondary_startup) | 31 | ENTRY(armada_xp_secondary_startup) |
32 | ARM_BE8(setend be ) @ go BE8 if entered LE | 32 | ARM_BE8(setend be ) @ go BE8 if entered LE |
33 | 33 | ||
34 | /* Get coherency fabric base physical address */ | 34 | bl ll_add_cpu_to_smp_group |
35 | adr r0, 1f | ||
36 | ldr r1, [r0] | ||
37 | ldr r0, [r0, r1] | ||
38 | 35 | ||
39 | /* Read CPU id */ | 36 | bl ll_enable_coherency |
40 | mrc p15, 0, r1, c0, c0, 5 | ||
41 | and r1, r1, #0xF | ||
42 | 37 | ||
43 | /* Add CPU to coherency fabric */ | ||
44 | bl ll_set_cpu_coherent | ||
45 | b secondary_startup | 38 | b secondary_startup |
46 | 39 | ||
47 | ENDPROC(armada_xp_secondary_startup) | 40 | ENDPROC(armada_xp_secondary_startup) |
48 | |||
49 | .align 2 | ||
50 | 1: | ||
51 | .long coherency_phys_base - . | ||
diff --git a/arch/arm/mach-mvebu/kirkwood.c b/arch/arm/mach-mvebu/kirkwood.c index 120207fc36f1..46f105913c84 100644 --- a/arch/arm/mach-mvebu/kirkwood.c +++ b/arch/arm/mach-mvebu/kirkwood.c | |||
@@ -169,7 +169,7 @@ static void __init kirkwood_dt_init(void) | |||
169 | { | 169 | { |
170 | kirkwood_disable_mbus_error_propagation(); | 170 | kirkwood_disable_mbus_error_propagation(); |
171 | 171 | ||
172 | BUG_ON(mvebu_mbus_dt_init()); | 172 | BUG_ON(mvebu_mbus_dt_init(false)); |
173 | 173 | ||
174 | #ifdef CONFIG_CACHE_FEROCEON_L2 | 174 | #ifdef CONFIG_CACHE_FEROCEON_L2 |
175 | feroceon_of_init(); | 175 | feroceon_of_init(); |
@@ -180,9 +180,6 @@ static void __init kirkwood_dt_init(void) | |||
180 | kirkwood_pm_init(); | 180 | kirkwood_pm_init(); |
181 | kirkwood_dt_eth_fixup(); | 181 | kirkwood_dt_eth_fixup(); |
182 | 182 | ||
183 | if (of_machine_is_compatible("hp,t5325")) | ||
184 | t5325_init(); | ||
185 | |||
186 | of_platform_populate(NULL, of_default_bus_match_table, auxdata, NULL); | 183 | of_platform_populate(NULL, of_default_bus_match_table, auxdata, NULL); |
187 | } | 184 | } |
188 | 185 | ||
diff --git a/arch/arm/mach-mvebu/mvebu-soc-id.c b/arch/arm/mach-mvebu/mvebu-soc-id.c index 09520e19b78e..d0f35b4d4a23 100644 --- a/arch/arm/mach-mvebu/mvebu-soc-id.c +++ b/arch/arm/mach-mvebu/mvebu-soc-id.c | |||
@@ -23,6 +23,8 @@ | |||
23 | #include <linux/kernel.h> | 23 | #include <linux/kernel.h> |
24 | #include <linux/of.h> | 24 | #include <linux/of.h> |
25 | #include <linux/of_address.h> | 25 | #include <linux/of_address.h> |
26 | #include <linux/slab.h> | ||
27 | #include <linux/sys_soc.h> | ||
26 | #include "mvebu-soc-id.h" | 28 | #include "mvebu-soc-id.h" |
27 | 29 | ||
28 | #define PCIE_DEV_ID_OFF 0x0 | 30 | #define PCIE_DEV_ID_OFF 0x0 |
@@ -127,5 +129,33 @@ clk_err: | |||
127 | 129 | ||
128 | return ret; | 130 | return ret; |
129 | } | 131 | } |
130 | core_initcall(mvebu_soc_id_init); | 132 | early_initcall(mvebu_soc_id_init); |
131 | 133 | ||
134 | static int __init mvebu_soc_device(void) | ||
135 | { | ||
136 | struct soc_device_attribute *soc_dev_attr; | ||
137 | struct soc_device *soc_dev; | ||
138 | |||
139 | /* Also protects against running on non-mvebu systems */ | ||
140 | if (!is_id_valid) | ||
141 | return 0; | ||
142 | |||
143 | soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); | ||
144 | if (!soc_dev_attr) | ||
145 | return -ENOMEM; | ||
146 | |||
147 | soc_dev_attr->family = kasprintf(GFP_KERNEL, "Marvell"); | ||
148 | soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%X", soc_rev); | ||
149 | soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%X", soc_dev_id); | ||
150 | |||
151 | soc_dev = soc_device_register(soc_dev_attr); | ||
152 | if (IS_ERR(soc_dev)) { | ||
153 | kfree(soc_dev_attr->family); | ||
154 | kfree(soc_dev_attr->revision); | ||
155 | kfree(soc_dev_attr->soc_id); | ||
156 | kfree(soc_dev_attr); | ||
157 | } | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | postcore_initcall(mvebu_soc_device); | ||
diff --git a/arch/arm/mach-mvebu/mvebu-soc-id.h b/arch/arm/mach-mvebu/mvebu-soc-id.h index 31654252fe35..c16bb68ca81f 100644 --- a/arch/arm/mach-mvebu/mvebu-soc-id.h +++ b/arch/arm/mach-mvebu/mvebu-soc-id.h | |||
@@ -20,6 +20,10 @@ | |||
20 | #define MV78XX0_A0_REV 0x1 | 20 | #define MV78XX0_A0_REV 0x1 |
21 | #define MV78XX0_B0_REV 0x2 | 21 | #define MV78XX0_B0_REV 0x2 |
22 | 22 | ||
23 | /* Armada 375 */ | ||
24 | #define ARMADA_375_Z1_REV 0x0 | ||
25 | #define ARMADA_375_A0_REV 0x3 | ||
26 | |||
23 | #ifdef CONFIG_ARCH_MVEBU | 27 | #ifdef CONFIG_ARCH_MVEBU |
24 | int mvebu_get_soc_id(u32 *dev, u32 *rev); | 28 | int mvebu_get_soc_id(u32 *dev, u32 *rev); |
25 | #else | 29 | #else |
diff --git a/arch/arm/mach-mvebu/platsmp-a9.c b/arch/arm/mach-mvebu/platsmp-a9.c new file mode 100644 index 000000000000..96c2c59e34b6 --- /dev/null +++ b/arch/arm/mach-mvebu/platsmp-a9.c | |||
@@ -0,0 +1,102 @@ | |||
1 | /* | ||
2 | * Symmetric Multi Processing (SMP) support for Marvell EBU Cortex-A9 | ||
3 | * based SOCs (Armada 375/38x). | ||
4 | * | ||
5 | * Copyright (C) 2014 Marvell | ||
6 | * | ||
7 | * Gregory CLEMENT <gregory.clement@free-electrons.com> | ||
8 | * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> | ||
9 | * | ||
10 | * This file is licensed under the terms of the GNU General Public | ||
11 | * License version 2. This program is licensed "as is" without any | ||
12 | * warranty of any kind, whether express or implied. | ||
13 | */ | ||
14 | |||
15 | #include <linux/init.h> | ||
16 | #include <linux/io.h> | ||
17 | #include <linux/of.h> | ||
18 | #include <linux/smp.h> | ||
19 | #include <linux/mbus.h> | ||
20 | #include <asm/smp_scu.h> | ||
21 | #include <asm/smp_plat.h> | ||
22 | #include "common.h" | ||
23 | #include "mvebu-soc-id.h" | ||
24 | #include "pmsu.h" | ||
25 | |||
26 | #define CRYPT0_ENG_ID 41 | ||
27 | #define CRYPT0_ENG_ATTR 0x1 | ||
28 | #define SRAM_PHYS_BASE 0xFFFF0000 | ||
29 | |||
30 | #define BOOTROM_BASE 0xFFF00000 | ||
31 | #define BOOTROM_SIZE 0x100000 | ||
32 | |||
33 | extern unsigned char armada_375_smp_cpu1_enable_code_end; | ||
34 | extern unsigned char armada_375_smp_cpu1_enable_code_start; | ||
35 | |||
36 | void armada_375_smp_cpu1_enable_wa(void) | ||
37 | { | ||
38 | void __iomem *sram_virt_base; | ||
39 | |||
40 | mvebu_mbus_del_window(BOOTROM_BASE, BOOTROM_SIZE); | ||
41 | mvebu_mbus_add_window_by_id(CRYPT0_ENG_ID, CRYPT0_ENG_ATTR, | ||
42 | SRAM_PHYS_BASE, SZ_64K); | ||
43 | sram_virt_base = ioremap(SRAM_PHYS_BASE, SZ_64K); | ||
44 | |||
45 | memcpy(sram_virt_base, &armada_375_smp_cpu1_enable_code_start, | ||
46 | &armada_375_smp_cpu1_enable_code_end | ||
47 | - &armada_375_smp_cpu1_enable_code_start); | ||
48 | } | ||
49 | |||
50 | extern void mvebu_cortex_a9_secondary_startup(void); | ||
51 | |||
52 | static int __cpuinit mvebu_cortex_a9_boot_secondary(unsigned int cpu, | ||
53 | struct task_struct *idle) | ||
54 | { | ||
55 | int ret, hw_cpu; | ||
56 | |||
57 | pr_info("Booting CPU %d\n", cpu); | ||
58 | |||
59 | /* | ||
60 | * Write the address of secondary startup into the system-wide | ||
61 | * flags register. The boot monitor waits until it receives a | ||
62 | * soft interrupt, and then the secondary CPU branches to this | ||
63 | * address. | ||
64 | */ | ||
65 | hw_cpu = cpu_logical_map(cpu); | ||
66 | |||
67 | if (of_machine_is_compatible("marvell,armada375")) { | ||
68 | u32 dev, rev; | ||
69 | |||
70 | if (mvebu_get_soc_id(&dev, &rev) == 0 && | ||
71 | rev == ARMADA_375_Z1_REV) | ||
72 | armada_375_smp_cpu1_enable_wa(); | ||
73 | |||
74 | mvebu_system_controller_set_cpu_boot_addr(mvebu_cortex_a9_secondary_startup); | ||
75 | } | ||
76 | else { | ||
77 | mvebu_pmsu_set_cpu_boot_addr(hw_cpu, | ||
78 | mvebu_cortex_a9_secondary_startup); | ||
79 | } | ||
80 | |||
81 | smp_wmb(); | ||
82 | ret = mvebu_cpu_reset_deassert(hw_cpu); | ||
83 | if (ret) { | ||
84 | pr_err("Could not start the secondary CPU: %d\n", ret); | ||
85 | return ret; | ||
86 | } | ||
87 | arch_send_wakeup_ipi_mask(cpumask_of(cpu)); | ||
88 | |||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | static struct smp_operations mvebu_cortex_a9_smp_ops __initdata = { | ||
93 | .smp_boot_secondary = mvebu_cortex_a9_boot_secondary, | ||
94 | #ifdef CONFIG_HOTPLUG_CPU | ||
95 | .cpu_die = armada_xp_cpu_die, | ||
96 | #endif | ||
97 | }; | ||
98 | |||
99 | CPU_METHOD_OF_DECLARE(mvebu_armada_375_smp, "marvell,armada-375-smp", | ||
100 | &mvebu_cortex_a9_smp_ops); | ||
101 | CPU_METHOD_OF_DECLARE(mvebu_armada_380_smp, "marvell,armada-380-smp", | ||
102 | &mvebu_cortex_a9_smp_ops); | ||
diff --git a/arch/arm/mach-mvebu/platsmp.c b/arch/arm/mach-mvebu/platsmp.c index a6da03f5b24e..88b976b31719 100644 --- a/arch/arm/mach-mvebu/platsmp.c +++ b/arch/arm/mach-mvebu/platsmp.c | |||
@@ -70,16 +70,19 @@ static void __init set_secondary_cpus_clock(void) | |||
70 | } | 70 | } |
71 | } | 71 | } |
72 | 72 | ||
73 | static void armada_xp_secondary_init(unsigned int cpu) | ||
74 | { | ||
75 | armada_xp_mpic_smp_cpu_init(); | ||
76 | } | ||
77 | |||
78 | static int armada_xp_boot_secondary(unsigned int cpu, struct task_struct *idle) | 73 | static int armada_xp_boot_secondary(unsigned int cpu, struct task_struct *idle) |
79 | { | 74 | { |
75 | int ret, hw_cpu; | ||
76 | |||
80 | pr_info("Booting CPU %d\n", cpu); | 77 | pr_info("Booting CPU %d\n", cpu); |
81 | 78 | ||
82 | armada_xp_boot_cpu(cpu, armada_xp_secondary_startup); | 79 | hw_cpu = cpu_logical_map(cpu); |
80 | mvebu_pmsu_set_cpu_boot_addr(hw_cpu, armada_xp_secondary_startup); | ||
81 | ret = mvebu_cpu_reset_deassert(hw_cpu); | ||
82 | if (ret) { | ||
83 | pr_warn("unable to boot CPU: %d\n", ret); | ||
84 | return ret; | ||
85 | } | ||
83 | 86 | ||
84 | return 0; | 87 | return 0; |
85 | } | 88 | } |
@@ -90,8 +93,6 @@ static void __init armada_xp_smp_init_cpus(void) | |||
90 | 93 | ||
91 | if (ncores == 0 || ncores > ARMADA_XP_MAX_CPUS) | 94 | if (ncores == 0 || ncores > ARMADA_XP_MAX_CPUS) |
92 | panic("Invalid number of CPUs in DT\n"); | 95 | panic("Invalid number of CPUs in DT\n"); |
93 | |||
94 | set_smp_cross_call(armada_mpic_send_doorbell); | ||
95 | } | 96 | } |
96 | 97 | ||
97 | static void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus) | 98 | static void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus) |
@@ -102,7 +103,7 @@ static void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus) | |||
102 | 103 | ||
103 | set_secondary_cpus_clock(); | 104 | set_secondary_cpus_clock(); |
104 | flush_cache_all(); | 105 | flush_cache_all(); |
105 | set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0); | 106 | set_cpu_coherent(); |
106 | 107 | ||
107 | /* | 108 | /* |
108 | * In order to boot the secondary CPUs we need to ensure | 109 | * In order to boot the secondary CPUs we need to ensure |
@@ -124,9 +125,11 @@ static void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus) | |||
124 | struct smp_operations armada_xp_smp_ops __initdata = { | 125 | struct smp_operations armada_xp_smp_ops __initdata = { |
125 | .smp_init_cpus = armada_xp_smp_init_cpus, | 126 | .smp_init_cpus = armada_xp_smp_init_cpus, |
126 | .smp_prepare_cpus = armada_xp_smp_prepare_cpus, | 127 | .smp_prepare_cpus = armada_xp_smp_prepare_cpus, |
127 | .smp_secondary_init = armada_xp_secondary_init, | ||
128 | .smp_boot_secondary = armada_xp_boot_secondary, | 128 | .smp_boot_secondary = armada_xp_boot_secondary, |
129 | #ifdef CONFIG_HOTPLUG_CPU | 129 | #ifdef CONFIG_HOTPLUG_CPU |
130 | .cpu_die = armada_xp_cpu_die, | 130 | .cpu_die = armada_xp_cpu_die, |
131 | #endif | 131 | #endif |
132 | }; | 132 | }; |
133 | |||
134 | CPU_METHOD_OF_DECLARE(armada_xp_smp, "marvell,armada-xp-smp", | ||
135 | &armada_xp_smp_ops); | ||
diff --git a/arch/arm/mach-mvebu/pmsu.c b/arch/arm/mach-mvebu/pmsu.c index d71ef53107c4..53a55c8520bf 100644 --- a/arch/arm/mach-mvebu/pmsu.c +++ b/arch/arm/mach-mvebu/pmsu.c | |||
@@ -16,62 +16,283 @@ | |||
16 | * other SOC units | 16 | * other SOC units |
17 | */ | 17 | */ |
18 | 18 | ||
19 | #define pr_fmt(fmt) "mvebu-pmsu: " fmt | ||
20 | |||
21 | #include <linux/cpu_pm.h> | ||
19 | #include <linux/kernel.h> | 22 | #include <linux/kernel.h> |
20 | #include <linux/init.h> | 23 | #include <linux/init.h> |
21 | #include <linux/of_address.h> | 24 | #include <linux/of_address.h> |
22 | #include <linux/io.h> | 25 | #include <linux/io.h> |
26 | #include <linux/platform_device.h> | ||
23 | #include <linux/smp.h> | 27 | #include <linux/smp.h> |
28 | #include <linux/resource.h> | ||
29 | #include <asm/cacheflush.h> | ||
30 | #include <asm/cp15.h> | ||
24 | #include <asm/smp_plat.h> | 31 | #include <asm/smp_plat.h> |
25 | #include "pmsu.h" | 32 | #include <asm/suspend.h> |
33 | #include <asm/tlbflush.h> | ||
34 | #include "common.h" | ||
26 | 35 | ||
27 | static void __iomem *pmsu_mp_base; | 36 | static void __iomem *pmsu_mp_base; |
28 | static void __iomem *pmsu_reset_base; | ||
29 | 37 | ||
30 | #define PMSU_BOOT_ADDR_REDIRECT_OFFSET(cpu) ((cpu * 0x100) + 0x24) | 38 | #define PMSU_BASE_OFFSET 0x100 |
31 | #define PMSU_RESET_CTL_OFFSET(cpu) (cpu * 0x8) | 39 | #define PMSU_REG_SIZE 0x1000 |
40 | |||
41 | /* PMSU MP registers */ | ||
42 | #define PMSU_CONTROL_AND_CONFIG(cpu) ((cpu * 0x100) + 0x104) | ||
43 | #define PMSU_CONTROL_AND_CONFIG_DFS_REQ BIT(18) | ||
44 | #define PMSU_CONTROL_AND_CONFIG_PWDDN_REQ BIT(16) | ||
45 | #define PMSU_CONTROL_AND_CONFIG_L2_PWDDN BIT(20) | ||
46 | |||
47 | #define PMSU_CPU_POWER_DOWN_CONTROL(cpu) ((cpu * 0x100) + 0x108) | ||
48 | |||
49 | #define PMSU_CPU_POWER_DOWN_DIS_SNP_Q_SKIP BIT(0) | ||
50 | |||
51 | #define PMSU_STATUS_AND_MASK(cpu) ((cpu * 0x100) + 0x10c) | ||
52 | #define PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT BIT(16) | ||
53 | #define PMSU_STATUS_AND_MASK_SNP_Q_EMPTY_WAIT BIT(17) | ||
54 | #define PMSU_STATUS_AND_MASK_IRQ_WAKEUP BIT(20) | ||
55 | #define PMSU_STATUS_AND_MASK_FIQ_WAKEUP BIT(21) | ||
56 | #define PMSU_STATUS_AND_MASK_DBG_WAKEUP BIT(22) | ||
57 | #define PMSU_STATUS_AND_MASK_IRQ_MASK BIT(24) | ||
58 | #define PMSU_STATUS_AND_MASK_FIQ_MASK BIT(25) | ||
59 | |||
60 | #define PMSU_BOOT_ADDR_REDIRECT_OFFSET(cpu) ((cpu * 0x100) + 0x124) | ||
61 | |||
62 | /* PMSU fabric registers */ | ||
63 | #define L2C_NFABRIC_PM_CTL 0x4 | ||
64 | #define L2C_NFABRIC_PM_CTL_PWR_DOWN BIT(20) | ||
65 | |||
66 | extern void ll_disable_coherency(void); | ||
67 | extern void ll_enable_coherency(void); | ||
68 | |||
69 | static struct platform_device armada_xp_cpuidle_device = { | ||
70 | .name = "cpuidle-armada-370-xp", | ||
71 | }; | ||
32 | 72 | ||
33 | static struct of_device_id of_pmsu_table[] = { | 73 | static struct of_device_id of_pmsu_table[] = { |
34 | {.compatible = "marvell,armada-370-xp-pmsu"}, | 74 | { .compatible = "marvell,armada-370-pmsu", }, |
75 | { .compatible = "marvell,armada-370-xp-pmsu", }, | ||
76 | { .compatible = "marvell,armada-380-pmsu", }, | ||
35 | { /* end of list */ }, | 77 | { /* end of list */ }, |
36 | }; | 78 | }; |
37 | 79 | ||
38 | #ifdef CONFIG_SMP | 80 | void mvebu_pmsu_set_cpu_boot_addr(int hw_cpu, void *boot_addr) |
39 | int armada_xp_boot_cpu(unsigned int cpu_id, void *boot_addr) | ||
40 | { | 81 | { |
41 | int reg, hw_cpu; | 82 | writel(virt_to_phys(boot_addr), pmsu_mp_base + |
83 | PMSU_BOOT_ADDR_REDIRECT_OFFSET(hw_cpu)); | ||
84 | } | ||
85 | |||
86 | static int __init armada_370_xp_pmsu_init(void) | ||
87 | { | ||
88 | struct device_node *np; | ||
89 | struct resource res; | ||
90 | int ret = 0; | ||
91 | |||
92 | np = of_find_matching_node(NULL, of_pmsu_table); | ||
93 | if (!np) | ||
94 | return 0; | ||
95 | |||
96 | pr_info("Initializing Power Management Service Unit\n"); | ||
42 | 97 | ||
43 | if (!pmsu_mp_base || !pmsu_reset_base) { | 98 | if (of_address_to_resource(np, 0, &res)) { |
44 | pr_warn("Can't boot CPU. PMSU is uninitialized\n"); | 99 | pr_err("unable to get resource\n"); |
45 | return 1; | 100 | ret = -ENOENT; |
101 | goto out; | ||
46 | } | 102 | } |
47 | 103 | ||
48 | hw_cpu = cpu_logical_map(cpu_id); | 104 | if (of_device_is_compatible(np, "marvell,armada-370-xp-pmsu")) { |
105 | pr_warn(FW_WARN "deprecated pmsu binding\n"); | ||
106 | res.start = res.start - PMSU_BASE_OFFSET; | ||
107 | res.end = res.start + PMSU_REG_SIZE - 1; | ||
108 | } | ||
49 | 109 | ||
50 | writel(virt_to_phys(boot_addr), pmsu_mp_base + | 110 | if (!request_mem_region(res.start, resource_size(&res), |
51 | PMSU_BOOT_ADDR_REDIRECT_OFFSET(hw_cpu)); | 111 | np->full_name)) { |
112 | pr_err("unable to request region\n"); | ||
113 | ret = -EBUSY; | ||
114 | goto out; | ||
115 | } | ||
116 | |||
117 | pmsu_mp_base = ioremap(res.start, resource_size(&res)); | ||
118 | if (!pmsu_mp_base) { | ||
119 | pr_err("unable to map registers\n"); | ||
120 | release_mem_region(res.start, resource_size(&res)); | ||
121 | ret = -ENOMEM; | ||
122 | goto out; | ||
123 | } | ||
124 | |||
125 | out: | ||
126 | of_node_put(np); | ||
127 | return ret; | ||
128 | } | ||
129 | |||
130 | static void armada_370_xp_pmsu_enable_l2_powerdown_onidle(void) | ||
131 | { | ||
132 | u32 reg; | ||
133 | |||
134 | if (pmsu_mp_base == NULL) | ||
135 | return; | ||
136 | |||
137 | /* Enable L2 & Fabric powerdown in Deep-Idle mode - Fabric */ | ||
138 | reg = readl(pmsu_mp_base + L2C_NFABRIC_PM_CTL); | ||
139 | reg |= L2C_NFABRIC_PM_CTL_PWR_DOWN; | ||
140 | writel(reg, pmsu_mp_base + L2C_NFABRIC_PM_CTL); | ||
141 | } | ||
142 | |||
143 | static void armada_370_xp_cpu_resume(void) | ||
144 | { | ||
145 | asm volatile("bl ll_add_cpu_to_smp_group\n\t" | ||
146 | "bl ll_enable_coherency\n\t" | ||
147 | "b cpu_resume\n\t"); | ||
148 | } | ||
149 | |||
150 | /* No locking is needed because we only access per-CPU registers */ | ||
151 | void armada_370_xp_pmsu_idle_prepare(bool deepidle) | ||
152 | { | ||
153 | unsigned int hw_cpu = cpu_logical_map(smp_processor_id()); | ||
154 | u32 reg; | ||
155 | |||
156 | if (pmsu_mp_base == NULL) | ||
157 | return; | ||
52 | 158 | ||
53 | /* Release CPU from reset by clearing reset bit*/ | 159 | /* |
54 | reg = readl(pmsu_reset_base + PMSU_RESET_CTL_OFFSET(hw_cpu)); | 160 | * Adjust the PMSU configuration to wait for WFI signal, enable |
55 | reg &= (~0x1); | 161 | * IRQ and FIQ as wakeup events, set wait for snoop queue empty |
56 | writel(reg, pmsu_reset_base + PMSU_RESET_CTL_OFFSET(hw_cpu)); | 162 | * indication and mask IRQ and FIQ from CPU |
163 | */ | ||
164 | reg = readl(pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu)); | ||
165 | reg |= PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT | | ||
166 | PMSU_STATUS_AND_MASK_IRQ_WAKEUP | | ||
167 | PMSU_STATUS_AND_MASK_FIQ_WAKEUP | | ||
168 | PMSU_STATUS_AND_MASK_SNP_Q_EMPTY_WAIT | | ||
169 | PMSU_STATUS_AND_MASK_IRQ_MASK | | ||
170 | PMSU_STATUS_AND_MASK_FIQ_MASK; | ||
171 | writel(reg, pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu)); | ||
172 | |||
173 | reg = readl(pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu)); | ||
174 | /* ask HW to power down the L2 Cache if needed */ | ||
175 | if (deepidle) | ||
176 | reg |= PMSU_CONTROL_AND_CONFIG_L2_PWDDN; | ||
177 | |||
178 | /* request power down */ | ||
179 | reg |= PMSU_CONTROL_AND_CONFIG_PWDDN_REQ; | ||
180 | writel(reg, pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu)); | ||
181 | |||
182 | /* Disable snoop disable by HW - SW is taking care of it */ | ||
183 | reg = readl(pmsu_mp_base + PMSU_CPU_POWER_DOWN_CONTROL(hw_cpu)); | ||
184 | reg |= PMSU_CPU_POWER_DOWN_DIS_SNP_Q_SKIP; | ||
185 | writel(reg, pmsu_mp_base + PMSU_CPU_POWER_DOWN_CONTROL(hw_cpu)); | ||
186 | } | ||
187 | |||
188 | static noinline int do_armada_370_xp_cpu_suspend(unsigned long deepidle) | ||
189 | { | ||
190 | armada_370_xp_pmsu_idle_prepare(deepidle); | ||
191 | |||
192 | v7_exit_coherency_flush(all); | ||
193 | |||
194 | ll_disable_coherency(); | ||
195 | |||
196 | dsb(); | ||
197 | |||
198 | wfi(); | ||
199 | |||
200 | /* If we are here, wfi failed. As processors run out of | ||
201 | * coherency for some time, tlbs might be stale, so flush them | ||
202 | */ | ||
203 | local_flush_tlb_all(); | ||
204 | |||
205 | ll_enable_coherency(); | ||
206 | |||
207 | /* Test the CR_C bit and set it if it was cleared */ | ||
208 | asm volatile( | ||
209 | "mrc p15, 0, %0, c1, c0, 0 \n\t" | ||
210 | "tst %0, #(1 << 2) \n\t" | ||
211 | "orreq %0, %0, #(1 << 2) \n\t" | ||
212 | "mcreq p15, 0, %0, c1, c0, 0 \n\t" | ||
213 | "isb " | ||
214 | : : "r" (0)); | ||
215 | |||
216 | pr_warn("Failed to suspend the system\n"); | ||
57 | 217 | ||
58 | return 0; | 218 | return 0; |
59 | } | 219 | } |
60 | #endif | ||
61 | 220 | ||
62 | static int __init armada_370_xp_pmsu_init(void) | 221 | static int armada_370_xp_cpu_suspend(unsigned long deepidle) |
222 | { | ||
223 | return cpu_suspend(deepidle, do_armada_370_xp_cpu_suspend); | ||
224 | } | ||
225 | |||
226 | /* No locking is needed because we only access per-CPU registers */ | ||
227 | static noinline void armada_370_xp_pmsu_idle_restore(void) | ||
228 | { | ||
229 | unsigned int hw_cpu = cpu_logical_map(smp_processor_id()); | ||
230 | u32 reg; | ||
231 | |||
232 | if (pmsu_mp_base == NULL) | ||
233 | return; | ||
234 | |||
235 | /* cancel ask HW to power down the L2 Cache if possible */ | ||
236 | reg = readl(pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu)); | ||
237 | reg &= ~PMSU_CONTROL_AND_CONFIG_L2_PWDDN; | ||
238 | writel(reg, pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu)); | ||
239 | |||
240 | /* cancel Enable wakeup events and mask interrupts */ | ||
241 | reg = readl(pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu)); | ||
242 | reg &= ~(PMSU_STATUS_AND_MASK_IRQ_WAKEUP | PMSU_STATUS_AND_MASK_FIQ_WAKEUP); | ||
243 | reg &= ~PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT; | ||
244 | reg &= ~PMSU_STATUS_AND_MASK_SNP_Q_EMPTY_WAIT; | ||
245 | reg &= ~(PMSU_STATUS_AND_MASK_IRQ_MASK | PMSU_STATUS_AND_MASK_FIQ_MASK); | ||
246 | writel(reg, pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu)); | ||
247 | } | ||
248 | |||
249 | static int armada_370_xp_cpu_pm_notify(struct notifier_block *self, | ||
250 | unsigned long action, void *hcpu) | ||
251 | { | ||
252 | if (action == CPU_PM_ENTER) { | ||
253 | unsigned int hw_cpu = cpu_logical_map(smp_processor_id()); | ||
254 | mvebu_pmsu_set_cpu_boot_addr(hw_cpu, armada_370_xp_cpu_resume); | ||
255 | } else if (action == CPU_PM_EXIT) { | ||
256 | armada_370_xp_pmsu_idle_restore(); | ||
257 | } | ||
258 | |||
259 | return NOTIFY_OK; | ||
260 | } | ||
261 | |||
262 | static struct notifier_block armada_370_xp_cpu_pm_notifier = { | ||
263 | .notifier_call = armada_370_xp_cpu_pm_notify, | ||
264 | }; | ||
265 | |||
266 | int __init armada_370_xp_cpu_pm_init(void) | ||
63 | { | 267 | { |
64 | struct device_node *np; | 268 | struct device_node *np; |
65 | 269 | ||
270 | /* | ||
271 | * Check that all the requirements are available to enable | ||
272 | * cpuidle. So far, it is only supported on Armada XP, cpuidle | ||
273 | * needs the coherency fabric and the PMSU enabled | ||
274 | */ | ||
275 | |||
276 | if (!of_machine_is_compatible("marvell,armadaxp")) | ||
277 | return 0; | ||
278 | |||
279 | np = of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric"); | ||
280 | if (!np) | ||
281 | return 0; | ||
282 | of_node_put(np); | ||
283 | |||
66 | np = of_find_matching_node(NULL, of_pmsu_table); | 284 | np = of_find_matching_node(NULL, of_pmsu_table); |
67 | if (np) { | 285 | if (!np) |
68 | pr_info("Initializing Power Management Service Unit\n"); | 286 | return 0; |
69 | pmsu_mp_base = of_iomap(np, 0); | 287 | of_node_put(np); |
70 | pmsu_reset_base = of_iomap(np, 1); | 288 | |
71 | of_node_put(np); | 289 | armada_370_xp_pmsu_enable_l2_powerdown_onidle(); |
72 | } | 290 | armada_xp_cpuidle_device.dev.platform_data = armada_370_xp_cpu_suspend; |
291 | platform_device_register(&armada_xp_cpuidle_device); | ||
292 | cpu_pm_register_notifier(&armada_370_xp_cpu_pm_notifier); | ||
73 | 293 | ||
74 | return 0; | 294 | return 0; |
75 | } | 295 | } |
76 | 296 | ||
297 | arch_initcall(armada_370_xp_cpu_pm_init); | ||
77 | early_initcall(armada_370_xp_pmsu_init); | 298 | early_initcall(armada_370_xp_pmsu_init); |
diff --git a/arch/arm/mach-mvebu/system-controller.c b/arch/arm/mach-mvebu/system-controller.c index 614ba6832ff3..0c5524ac75b7 100644 --- a/arch/arm/mach-mvebu/system-controller.c +++ b/arch/arm/mach-mvebu/system-controller.c | |||
@@ -37,6 +37,8 @@ struct mvebu_system_controller { | |||
37 | 37 | ||
38 | u32 rstoutn_mask_reset_out_en; | 38 | u32 rstoutn_mask_reset_out_en; |
39 | u32 system_soft_reset; | 39 | u32 system_soft_reset; |
40 | |||
41 | u32 resume_boot_addr; | ||
40 | }; | 42 | }; |
41 | static struct mvebu_system_controller *mvebu_sc; | 43 | static struct mvebu_system_controller *mvebu_sc; |
42 | 44 | ||
@@ -52,6 +54,7 @@ static const struct mvebu_system_controller armada_375_system_controller = { | |||
52 | .system_soft_reset_offset = 0x58, | 54 | .system_soft_reset_offset = 0x58, |
53 | .rstoutn_mask_reset_out_en = 0x1, | 55 | .rstoutn_mask_reset_out_en = 0x1, |
54 | .system_soft_reset = 0x1, | 56 | .system_soft_reset = 0x1, |
57 | .resume_boot_addr = 0xd4, | ||
55 | }; | 58 | }; |
56 | 59 | ||
57 | static const struct mvebu_system_controller orion_system_controller = { | 60 | static const struct mvebu_system_controller orion_system_controller = { |
@@ -98,6 +101,16 @@ void mvebu_restart(enum reboot_mode mode, const char *cmd) | |||
98 | ; | 101 | ; |
99 | } | 102 | } |
100 | 103 | ||
104 | #ifdef CONFIG_SMP | ||
105 | void mvebu_system_controller_set_cpu_boot_addr(void *boot_addr) | ||
106 | { | ||
107 | BUG_ON(system_controller_base == NULL); | ||
108 | BUG_ON(mvebu_sc->resume_boot_addr == 0); | ||
109 | writel(virt_to_phys(boot_addr), system_controller_base + | ||
110 | mvebu_sc->resume_boot_addr); | ||
111 | } | ||
112 | #endif | ||
113 | |||
101 | static int __init mvebu_system_controller_init(void) | 114 | static int __init mvebu_system_controller_init(void) |
102 | { | 115 | { |
103 | const struct of_device_id *match; | 116 | const struct of_device_id *match; |
@@ -114,4 +127,4 @@ static int __init mvebu_system_controller_init(void) | |||
114 | return 0; | 127 | return 0; |
115 | } | 128 | } |
116 | 129 | ||
117 | arch_initcall(mvebu_system_controller_init); | 130 | early_initcall(mvebu_system_controller_init); |