aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-mvebu
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-mvebu')
-rw-r--r--arch/arm/mach-mvebu/Kconfig21
-rw-r--r--arch/arm/mach-mvebu/Makefile13
-rw-r--r--arch/arm/mach-mvebu/armada-370-xp.h2
-rw-r--r--arch/arm/mach-mvebu/board-t5325.c41
-rw-r--r--arch/arm/mach-mvebu/board-v7.c88
-rw-r--r--arch/arm/mach-mvebu/board.h6
-rw-r--r--arch/arm/mach-mvebu/coherency.c340
-rw-r--r--arch/arm/mach-mvebu/coherency.h3
-rw-r--r--arch/arm/mach-mvebu/coherency_ll.S143
-rw-r--r--arch/arm/mach-mvebu/common.h3
-rw-r--r--arch/arm/mach-mvebu/cpu-reset.c103
-rw-r--r--arch/arm/mach-mvebu/dove.c2
-rw-r--r--arch/arm/mach-mvebu/headsmp-a9.S34
-rw-r--r--arch/arm/mach-mvebu/headsmp.S15
-rw-r--r--arch/arm/mach-mvebu/kirkwood.c5
-rw-r--r--arch/arm/mach-mvebu/mvebu-soc-id.c32
-rw-r--r--arch/arm/mach-mvebu/mvebu-soc-id.h4
-rw-r--r--arch/arm/mach-mvebu/platsmp-a9.c102
-rw-r--r--arch/arm/mach-mvebu/platsmp.c23
-rw-r--r--arch/arm/mach-mvebu/pmsu.c273
-rw-r--r--arch/arm/mach-mvebu/system-controller.c15
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
16if ARCH_MVEBU 14if 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
100config 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
107endmenu 100endmenu
108 101
109endif 102endif
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
4AFLAGS_coherency_ll.o := -Wa,-march=armv7-a 4AFLAGS_coherency_ll.o := -Wa,-march=armv7-a
5CFLAGS_pmsu.o := -march=armv7-a
5 6
6obj-y += system-controller.o mvebu-soc-id.o 7obj-y += system-controller.o mvebu-soc-id.o
7obj-$(CONFIG_MACH_MVEBU_V7) += board-v7.o 8
9ifeq ($(CONFIG_MACH_MVEBU_V7),y)
10obj-y += cpu-reset.o board-v7.o coherency.o coherency_ll.o pmsu.o
11obj-$(CONFIG_SMP) += platsmp.o headsmp.o platsmp-a9.o headsmp-a9.o
12obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
13endif
14
8obj-$(CONFIG_MACH_DOVE) += dove.o 15obj-$(CONFIG_MACH_DOVE) += dove.o
9obj-$(CONFIG_ARCH_MVEBU) += coherency.o coherency_ll.o pmsu.o
10obj-$(CONFIG_SMP) += platsmp.o headsmp.o
11obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
12obj-$(CONFIG_MACH_KIRKWOOD) += kirkwood.o kirkwood-pm.o 16obj-$(CONFIG_MACH_KIRKWOOD) += kirkwood.o kirkwood-pm.o
13obj-$(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
23void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq);
24void armada_xp_mpic_smp_cpu_init(void);
25void armada_xp_secondary_startup(void); 23void armada_xp_secondary_startup(void);
26extern struct smp_operations armada_xp_smp_ops; 24extern 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
20static struct platform_device hp_t5325_audio_device = {
21 .name = "t5325-audio",
22 .id = -1,
23};
24
25static struct alc5623_platform_data alc5621_data = {
26 .add_ctrl = 0x3700,
27 .jack_det_ctrl = 0x4810,
28};
29
30static struct i2c_board_info i2c_board_info[] __initdata = {
31 {
32 I2C_BOARD_INFO("alc5621", 0x1a),
33 .platform_data = &alc5621_data,
34 },
35};
36
37void __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 */
40static 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
118static 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
99static void __init mvebu_dt_init(void) 169static 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
111DT_MACHINE_START(ARMADA_370_XP_DT, "Marvell Armada 370/XP (Device Tree)") 184DT_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
124DT_MACHINE_START(ARMADA_375_DT, "Marvell Armada 375 (Device Tree)") 199DT_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,
128MACHINE_END 206MACHINE_END
@@ -134,6 +212,8 @@ static const char * const armada_38x_dt_compat[] = {
134}; 212};
135 213
136DT_MACHINE_START(ARMADA_38X_DT, "Marvell Armada 380/385 (Device Tree)") 214DT_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
17void t5325_init(void);
18#else
19static 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
32unsigned long coherency_phys_base; 40unsigned long coherency_phys_base;
33static void __iomem *coherency_base; 41void __iomem *coherency_base;
34static void __iomem *coherency_cpu_base; 42static 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
49enum {
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
41static struct of_device_id of_coherency_table[] = { 56static 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 */
47int ll_set_cpu_coherent(void __iomem *base_addr, unsigned int hw_cpu_id); 67int ll_enable_coherency(void);
68void ll_add_cpu_to_smp_group(void);
48 69
49int set_cpu_coherent(unsigned int hw_cpu_id, int smp_group_id) 70int 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
89static void __iomem *xor_base, *xor_high_base;
90static dma_addr_t coherency_wa_buf_phys[CONFIG_NR_CPUS];
91static void *coherency_wa_buf[CONFIG_NR_CPUS];
92static 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
106static 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
127static 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
60static inline void mvebu_hwcc_sync_io_barrier(void) 226static 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
108static int mvebu_hwcc_platform_notifier(struct notifier_block *nb, 279static 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
120static struct notifier_block mvebu_hwcc_platform_nb = { 291static struct notifier_block mvebu_hwcc_nb = {
121 .notifier_call = mvebu_hwcc_platform_notifier, 292 .notifier_call = mvebu_hwcc_notifier,
122}; 293};
123 294
124int __init coherency_init(void) 295static 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 */
319static void __iomem *
320armada_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
333static 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
356static 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
150static int __init coherency_late_init(void) 381int coherency_available(void)
382{
383 return coherency_type() != COHERENCY_FABRIC_TYPE_NONE;
384}
385
386int __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
402static 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
163postcore_initcall(coherency_late_init); 423postcore_initcall(coherency_late_init);
424
425#if IS_ENABLED(CONFIG_PCI)
426static 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
434arch_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
17extern unsigned long coherency_phys_base; 17extern unsigned long coherency_phys_base;
18int set_cpu_coherent(void);
18 19
19int set_cpu_coherent(unsigned int cpu_id, int smp_group_id);
20int coherency_init(void); 20int coherency_init(void);
21int 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) */
28ENTRY(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
411:
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]
482:
49 mov pc, lr
50ENDPROC(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 */
30ENTRY(ll_set_cpu_coherent) 60ENTRY(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)
34ARM_BE8(rev r1, r1) 64 lsl r3, r2, r3
35 65ARM_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 67ENDPROC(ll_get_coherency_cpumask)
381: 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
78ENTRY(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
471: 911:
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
98ENDPROC(ll_add_cpu_to_smp_group)
53 99
100ENTRY(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
1131:
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
58ENDPROC(ll_set_cpu_coherent) 122ENDPROC(ll_enable_coherency)
123
124ENTRY(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
1371:
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
145ENDPROC(ll_disable_coherency)
146
147 .align 2
1483:
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
20void mvebu_restart(enum reboot_mode mode, const char *cmd); 20void mvebu_restart(enum reboot_mode mode, const char *cmd);
21int mvebu_cpu_reset_deassert(int cpu);
22void mvebu_pmsu_set_cpu_boot_addr(int hw_cpu, void *boot_addr);
23void mvebu_system_controller_set_cpu_boot_addr(void *boot_addr);
21 24
22void armada_xp_cpu_die(unsigned int cpu); 25void 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
20static void __iomem *cpu_reset_base;
21static size_t cpu_reset_size;
22
23#define CPU_RESET_OFFSET(cpu) (cpu * 0x8)
24#define CPU_RESET_ASSERT BIT(0)
25
26int 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
43static 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
70int __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
103early_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
24armada_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
29armada_375_smp_cpu1_enable_code_end:
30
31ENTRY(mvebu_cortex_a9_secondary_startup)
32 bl v7_invalidate_l1
33 b secondary_startup
34ENDPROC(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 @@
31ENTRY(armada_xp_secondary_startup) 31ENTRY(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
47ENDPROC(armada_xp_secondary_startup) 40ENDPROC(armada_xp_secondary_startup)
48
49 .align 2
501:
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}
130core_initcall(mvebu_soc_id_init); 132early_initcall(mvebu_soc_id_init);
131 133
134static 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}
161postcore_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
24int mvebu_get_soc_id(u32 *dev, u32 *rev); 28int 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
33extern unsigned char armada_375_smp_cpu1_enable_code_end;
34extern unsigned char armada_375_smp_cpu1_enable_code_start;
35
36void 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
50extern void mvebu_cortex_a9_secondary_startup(void);
51
52static 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
92static 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
99CPU_METHOD_OF_DECLARE(mvebu_armada_375_smp, "marvell,armada-375-smp",
100 &mvebu_cortex_a9_smp_ops);
101CPU_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
73static void armada_xp_secondary_init(unsigned int cpu)
74{
75 armada_xp_mpic_smp_cpu_init();
76}
77
78static int armada_xp_boot_secondary(unsigned int cpu, struct task_struct *idle) 73static 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
97static void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus) 98static 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)
124struct smp_operations armada_xp_smp_ops __initdata = { 125struct 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
134CPU_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
27static void __iomem *pmsu_mp_base; 36static void __iomem *pmsu_mp_base;
28static 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
66extern void ll_disable_coherency(void);
67extern void ll_enable_coherency(void);
68
69static struct platform_device armada_xp_cpuidle_device = {
70 .name = "cpuidle-armada-370-xp",
71};
32 72
33static struct of_device_id of_pmsu_table[] = { 73static 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 80void mvebu_pmsu_set_cpu_boot_addr(int hw_cpu, void *boot_addr)
39int 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
86static 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
130static 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
143static 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 */
151void 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
188static 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
62static int __init armada_370_xp_pmsu_init(void) 221static 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 */
227static 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
249static 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
262static struct notifier_block armada_370_xp_cpu_pm_notifier = {
263 .notifier_call = armada_370_xp_cpu_pm_notify,
264};
265
266int __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
297arch_initcall(armada_370_xp_cpu_pm_init);
77early_initcall(armada_370_xp_pmsu_init); 298early_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};
41static struct mvebu_system_controller *mvebu_sc; 43static 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
57static const struct mvebu_system_controller orion_system_controller = { 60static 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
105void 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
101static int __init mvebu_system_controller_init(void) 114static 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
117arch_initcall(mvebu_system_controller_init); 130early_initcall(mvebu_system_controller_init);