diff options
author | Olof Johansson <olof@lixom.net> | 2014-05-20 00:59:55 -0400 |
---|---|---|
committer | Olof Johansson <olof@lixom.net> | 2014-05-20 00:59:55 -0400 |
commit | 5df22a6148d7f073513a61eb306a98654c5a6fe8 (patch) | |
tree | d80a90222f36b303eea4f6334a3cbe6e35baeecd | |
parent | c15c1199b0031f2dcc3437e9998e9abe99e2609c (diff) | |
parent | 9f0affcf3e21fc56d8bce625bb3d5800b7a7d284 (diff) |
Merge tag 'mvebu-soc-3.16' of git://git.infradead.org/linux-mvebu into next/soc
Merge "ARM: mvebu: SoC changes for v3.16" from Jason Cooper:
mvebu SoC changes for v3.16
- Armada 375/38x coherency support
- Armada 375/38x SMP support
- mvebu PMSU and CPU reset support
- Armada 370/XP cpuidle support
- kirkwood remove platform init of audio device
- small fixes and cleanup for new SoC (375/38x)
Note:
- due to complex deps, cpuidle changes Acked by appropriate maintainer for
going though arm-soc tree.
* tag 'mvebu-soc-3.16' of git://git.infradead.org/linux-mvebu: (46 commits)
ARM: mvebu: Fix pmsu compilation when ARMv6 is selected
ARM: mvebu: conditionalize Armada 375 coherency workaround
ARM: mvebu: conditionalize Armada 375 SMP workaround
ARM: mvebu: add Armada 375 A0 revision definition
ARM: mvebu: initialize mvebu-soc-id earlier
ARM: mvebu: fix thermal quirk SoC revision check
ARM: Kirkwood: t5325: Remove platform device to instantiate audio
ARM: Kirkwood: Remove platform driver for codec
ARM: mvebu: Add thermal quirk for the Armada 375 DB board
ARM: mvebu: Select HAVE_ARM_TWD only if SMP is enabled
ARM: mvebu: fix the name of the parameter used in mvebu_get_soc_id
ARM: mvebu: remove unnecessary ifdef around l2x0_of_init
ARM: mvebu: register the cpuidle driver for the Armada XP SoCs
cpuidle: mvebu: Add initial CPU idle support for Armada 370/XP SoC
ARM: mvebu: Register notifier callback for the cpuidle transition
ARM: mvebu: refine which files are build in mach-mvebu
ARM: mvebu: Add the PMSU related part of the cpu idle functions
ARM: mvebu: Allow to power down L2 cache controller in idle mode
ARM: mvebu: Low level function to disable HW coherency support
ARM: mvebu: Split low level functions to manipulate HW coherency
...
Signed-off-by: Olof Johansson <olof@lixom.net>
33 files changed, 1178 insertions, 206 deletions
diff --git a/Documentation/devicetree/bindings/arm/armada-370-xp-pmsu.txt b/Documentation/devicetree/bindings/arm/armada-370-xp-pmsu.txt index 926b4d6aae7e..26799ef562df 100644 --- a/Documentation/devicetree/bindings/arm/armada-370-xp-pmsu.txt +++ b/Documentation/devicetree/bindings/arm/armada-370-xp-pmsu.txt | |||
@@ -1,20 +1,21 @@ | |||
1 | Power Management Service Unit(PMSU) | 1 | Power Management Service Unit(PMSU) |
2 | ----------------------------------- | 2 | ----------------------------------- |
3 | Available on Marvell SOCs: Armada 370 and Armada XP | 3 | Available on Marvell SOCs: Armada 370, Armada 38x and Armada XP |
4 | 4 | ||
5 | Required properties: | 5 | Required properties: |
6 | 6 | ||
7 | - compatible: "marvell,armada-370-xp-pmsu" | 7 | - compatible: should be one of: |
8 | - "marvell,armada-370-pmsu" for Armada 370 or Armada XP | ||
9 | - "marvell,armada-380-pmsu" for Armada 38x | ||
10 | - "marvell,armada-370-xp-pmsu" was used for Armada 370/XP but is now | ||
11 | deprecated and will be removed | ||
8 | 12 | ||
9 | - reg: Should contain PMSU registers location and length. First pair | 13 | - reg: Should contain PMSU registers location and length. |
10 | for the per-CPU SW Reset Control registers, second pair for the | ||
11 | Power Management Service Unit. | ||
12 | 14 | ||
13 | Example: | 15 | Example: |
14 | 16 | ||
15 | armada-370-xp-pmsu@d0022000 { | 17 | armada-370-xp-pmsu@22000 { |
16 | compatible = "marvell,armada-370-xp-pmsu"; | 18 | compatible = "marvell,armada-370-pmsu"; |
17 | reg = <0xd0022100 0x430>, | 19 | reg = <0x22000 0x1000>; |
18 | <0xd0020800 0x20>; | ||
19 | }; | 20 | }; |
20 | 21 | ||
diff --git a/Documentation/devicetree/bindings/arm/armada-cpu-reset.txt b/Documentation/devicetree/bindings/arm/armada-cpu-reset.txt new file mode 100644 index 000000000000..b63a7b6ab998 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/armada-cpu-reset.txt | |||
@@ -0,0 +1,14 @@ | |||
1 | Marvell Armada CPU reset controller | ||
2 | =================================== | ||
3 | |||
4 | Required properties: | ||
5 | |||
6 | - compatible: Should be "marvell,armada-370-cpu-reset". | ||
7 | |||
8 | - reg: should be register base and length as documented in the | ||
9 | datasheet for the CPU reset registers | ||
10 | |||
11 | cpurst: cpurst@20800 { | ||
12 | compatible = "marvell,armada-370-cpu-reset"; | ||
13 | reg = <0x20800 0x20>; | ||
14 | }; | ||
diff --git a/Documentation/devicetree/bindings/arm/coherency-fabric.txt b/Documentation/devicetree/bindings/arm/coherency-fabric.txt index 17d8cd107559..8dd46617c889 100644 --- a/Documentation/devicetree/bindings/arm/coherency-fabric.txt +++ b/Documentation/devicetree/bindings/arm/coherency-fabric.txt | |||
@@ -1,16 +1,33 @@ | |||
1 | Coherency fabric | 1 | Coherency fabric |
2 | ---------------- | 2 | ---------------- |
3 | Available on Marvell SOCs: Armada 370 and Armada XP | 3 | Available on Marvell SOCs: Armada 370, Armada 375, Armada 38x and Armada XP |
4 | 4 | ||
5 | Required properties: | 5 | Required properties: |
6 | 6 | ||
7 | - compatible: "marvell,coherency-fabric" | 7 | - compatible: the possible values are: |
8 | |||
9 | * "marvell,coherency-fabric", to be used for the coherency fabric of | ||
10 | the Armada 370 and Armada XP. | ||
11 | |||
12 | * "marvell,armada-375-coherency-fabric", for the Armada 375 coherency | ||
13 | fabric. | ||
14 | |||
15 | * "marvell,armada-380-coherency-fabric", for the Armada 38x coherency | ||
16 | fabric. | ||
8 | 17 | ||
9 | - reg: Should contain coherency fabric registers location and | 18 | - reg: Should contain coherency fabric registers location and |
10 | length. First pair for the coherency fabric registers, second pair | 19 | length. |
11 | for the per-CPU fabric registers registers. | 20 | |
21 | * For "marvell,coherency-fabric", the first pair for the coherency | ||
22 | fabric registers, second pair for the per-CPU fabric registers. | ||
12 | 23 | ||
13 | Example: | 24 | * For "marvell,armada-375-coherency-fabric", only one pair is needed |
25 | for the per-CPU fabric registers. | ||
26 | |||
27 | * For "marvell,armada-380-coherency-fabric", only one pair is needed | ||
28 | for the per-CPU fabric registers. | ||
29 | |||
30 | Examples: | ||
14 | 31 | ||
15 | coherency-fabric@d0020200 { | 32 | coherency-fabric@d0020200 { |
16 | compatible = "marvell,coherency-fabric"; | 33 | compatible = "marvell,coherency-fabric"; |
@@ -19,3 +36,8 @@ coherency-fabric@d0020200 { | |||
19 | 36 | ||
20 | }; | 37 | }; |
21 | 38 | ||
39 | coherency-fabric@21810 { | ||
40 | compatible = "marvell,armada-375-coherency-fabric"; | ||
41 | reg = <0x21810 0x1c>; | ||
42 | }; | ||
43 | |||
diff --git a/Documentation/devicetree/bindings/arm/cpus.txt b/Documentation/devicetree/bindings/arm/cpus.txt index 333f4aea3029..4bbcf4fb7583 100644 --- a/Documentation/devicetree/bindings/arm/cpus.txt +++ b/Documentation/devicetree/bindings/arm/cpus.txt | |||
@@ -185,6 +185,9 @@ nodes to be present and contain the properties described below. | |||
185 | "qcom,gcc-msm8660" | 185 | "qcom,gcc-msm8660" |
186 | "qcom,kpss-acc-v1" | 186 | "qcom,kpss-acc-v1" |
187 | "qcom,kpss-acc-v2" | 187 | "qcom,kpss-acc-v2" |
188 | "marvell,armada-375-smp" | ||
189 | "marvell,armada-380-smp" | ||
190 | "marvell,armada-xp-smp" | ||
188 | 191 | ||
189 | - cpu-release-addr | 192 | - cpu-release-addr |
190 | Usage: required for systems that have an "enable-method" | 193 | Usage: required for systems that have an "enable-method" |
diff --git a/arch/arm/mach-kirkwood/board-dt.c b/arch/arm/mach-kirkwood/board-dt.c index 2801da49e2a3..ff18ff20f71f 100644 --- a/arch/arm/mach-kirkwood/board-dt.c +++ b/arch/arm/mach-kirkwood/board-dt.c | |||
@@ -195,7 +195,7 @@ static void __init kirkwood_dt_init(void) | |||
195 | { | 195 | { |
196 | kirkwood_disable_mbus_error_propagation(); | 196 | kirkwood_disable_mbus_error_propagation(); |
197 | 197 | ||
198 | BUG_ON(mvebu_mbus_dt_init()); | 198 | BUG_ON(mvebu_mbus_dt_init(false)); |
199 | 199 | ||
200 | #ifdef CONFIG_CACHE_FEROCEON_L2 | 200 | #ifdef CONFIG_CACHE_FEROCEON_L2 |
201 | feroceon_of_init(); | 201 | feroceon_of_init(); |
diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig index 3f73eecbcfb0..d6b0a772a6dd 100644 --- a/arch/arm/mach-mvebu/Kconfig +++ b/arch/arm/mach-mvebu/Kconfig | |||
@@ -6,6 +6,7 @@ config ARCH_MVEBU | |||
6 | select IRQ_DOMAIN | 6 | select IRQ_DOMAIN |
7 | select PINCTRL | 7 | select PINCTRL |
8 | select PLAT_ORION | 8 | select PLAT_ORION |
9 | select SOC_BUS | ||
9 | select MVEBU_MBUS | 10 | select MVEBU_MBUS |
10 | select ZONE_DMA if ARM_LPAE | 11 | select ZONE_DMA if ARM_LPAE |
11 | select ARCH_REQUIRE_GPIOLIB | 12 | select ARCH_REQUIRE_GPIOLIB |
@@ -39,6 +40,9 @@ config MACH_ARMADA_375 | |||
39 | select ARM_GIC | 40 | select ARM_GIC |
40 | select ARMADA_375_CLK | 41 | select ARMADA_375_CLK |
41 | select CPU_V7 | 42 | select CPU_V7 |
43 | select HAVE_ARM_SCU | ||
44 | select HAVE_ARM_TWD if SMP | ||
45 | select HAVE_SMP | ||
42 | select MACH_MVEBU_V7 | 46 | select MACH_MVEBU_V7 |
43 | select PINCTRL_ARMADA_375 | 47 | select PINCTRL_ARMADA_375 |
44 | help | 48 | help |
@@ -52,6 +56,9 @@ config MACH_ARMADA_38X | |||
52 | select ARM_GIC | 56 | select ARM_GIC |
53 | select ARMADA_38X_CLK | 57 | select ARMADA_38X_CLK |
54 | select CPU_V7 | 58 | select CPU_V7 |
59 | select HAVE_ARM_SCU | ||
60 | select HAVE_ARM_TWD if SMP | ||
61 | select HAVE_SMP | ||
55 | select MACH_MVEBU_V7 | 62 | select MACH_MVEBU_V7 |
56 | select PINCTRL_ARMADA_38X | 63 | select PINCTRL_ARMADA_38X |
57 | help | 64 | help |
@@ -97,13 +104,6 @@ config MACH_KIRKWOOD | |||
97 | Say 'Y' here if you want your kernel to support boards based | 104 | Say 'Y' here if you want your kernel to support boards based |
98 | on the Marvell Kirkwood device tree. | 105 | on the Marvell Kirkwood device tree. |
99 | 106 | ||
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 | 107 | endmenu |
108 | 108 | ||
109 | endif | 109 | 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..01cfce6ac20b 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 | ||
@@ -123,6 +196,7 @@ static const char * const armada_375_dt_compat[] = { | |||
123 | 196 | ||
124 | DT_MACHINE_START(ARMADA_375_DT, "Marvell Armada 375 (Device Tree)") | 197 | DT_MACHINE_START(ARMADA_375_DT, "Marvell Armada 375 (Device Tree)") |
125 | .init_time = mvebu_timer_and_clk_init, | 198 | .init_time = mvebu_timer_and_clk_init, |
199 | .init_machine = mvebu_dt_init, | ||
126 | .restart = mvebu_restart, | 200 | .restart = mvebu_restart, |
127 | .dt_compat = armada_375_dt_compat, | 201 | .dt_compat = armada_375_dt_compat, |
128 | MACHINE_END | 202 | MACHINE_END |
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..d5a975b6a590 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,17 @@ | |||
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> | ||
27 | #include <asm/smp_plat.h> | 32 | #include <asm/smp_plat.h> |
28 | #include <asm/cacheflush.h> | 33 | #include <asm/cacheflush.h> |
29 | #include "armada-370-xp.h" | 34 | #include "armada-370-xp.h" |
30 | #include "coherency.h" | 35 | #include "coherency.h" |
36 | #include "mvebu-soc-id.h" | ||
31 | 37 | ||
32 | unsigned long coherency_phys_base; | 38 | unsigned long coherency_phys_base; |
33 | static void __iomem *coherency_base; | 39 | void __iomem *coherency_base; |
34 | static void __iomem *coherency_cpu_base; | 40 | static void __iomem *coherency_cpu_base; |
35 | 41 | ||
36 | /* Coherency fabric registers */ | 42 | /* Coherency fabric registers */ |
@@ -38,27 +44,190 @@ static void __iomem *coherency_cpu_base; | |||
38 | 44 | ||
39 | #define IO_SYNC_BARRIER_CTL_OFFSET 0x0 | 45 | #define IO_SYNC_BARRIER_CTL_OFFSET 0x0 |
40 | 46 | ||
47 | enum { | ||
48 | COHERENCY_FABRIC_TYPE_NONE, | ||
49 | COHERENCY_FABRIC_TYPE_ARMADA_370_XP, | ||
50 | COHERENCY_FABRIC_TYPE_ARMADA_375, | ||
51 | COHERENCY_FABRIC_TYPE_ARMADA_380, | ||
52 | }; | ||
53 | |||
41 | static struct of_device_id of_coherency_table[] = { | 54 | static struct of_device_id of_coherency_table[] = { |
42 | {.compatible = "marvell,coherency-fabric"}, | 55 | {.compatible = "marvell,coherency-fabric", |
56 | .data = (void *) COHERENCY_FABRIC_TYPE_ARMADA_370_XP }, | ||
57 | {.compatible = "marvell,armada-375-coherency-fabric", | ||
58 | .data = (void *) COHERENCY_FABRIC_TYPE_ARMADA_375 }, | ||
59 | {.compatible = "marvell,armada-380-coherency-fabric", | ||
60 | .data = (void *) COHERENCY_FABRIC_TYPE_ARMADA_380 }, | ||
43 | { /* end of list */ }, | 61 | { /* end of list */ }, |
44 | }; | 62 | }; |
45 | 63 | ||
46 | /* Function defined in coherency_ll.S */ | 64 | /* Functions defined in coherency_ll.S */ |
47 | int ll_set_cpu_coherent(void __iomem *base_addr, unsigned int hw_cpu_id); | 65 | int ll_enable_coherency(void); |
66 | void ll_add_cpu_to_smp_group(void); | ||
48 | 67 | ||
49 | int set_cpu_coherent(unsigned int hw_cpu_id, int smp_group_id) | 68 | int set_cpu_coherent(void) |
50 | { | 69 | { |
51 | if (!coherency_base) { | 70 | if (!coherency_base) { |
52 | pr_warn("Can't make CPU %d cache coherent.\n", hw_cpu_id); | 71 | pr_warn("Can't make current CPU cache coherent.\n"); |
53 | pr_warn("Coherency fabric is not initialized\n"); | 72 | pr_warn("Coherency fabric is not initialized\n"); |
54 | return 1; | 73 | return 1; |
55 | } | 74 | } |
56 | 75 | ||
57 | return ll_set_cpu_coherent(coherency_base, hw_cpu_id); | 76 | ll_add_cpu_to_smp_group(); |
77 | return ll_enable_coherency(); | ||
78 | } | ||
79 | |||
80 | /* | ||
81 | * The below code implements the I/O coherency workaround on Armada | ||
82 | * 375. This workaround consists in using the two channels of the | ||
83 | * first XOR engine to trigger a XOR transaction that serves as the | ||
84 | * I/O coherency barrier. | ||
85 | */ | ||
86 | |||
87 | static void __iomem *xor_base, *xor_high_base; | ||
88 | static dma_addr_t coherency_wa_buf_phys[CONFIG_NR_CPUS]; | ||
89 | static void *coherency_wa_buf[CONFIG_NR_CPUS]; | ||
90 | static bool coherency_wa_enabled; | ||
91 | |||
92 | #define XOR_CONFIG(chan) (0x10 + (chan * 4)) | ||
93 | #define XOR_ACTIVATION(chan) (0x20 + (chan * 4)) | ||
94 | #define WINDOW_BAR_ENABLE(chan) (0x240 + ((chan) << 2)) | ||
95 | #define WINDOW_BASE(w) (0x250 + ((w) << 2)) | ||
96 | #define WINDOW_SIZE(w) (0x270 + ((w) << 2)) | ||
97 | #define WINDOW_REMAP_HIGH(w) (0x290 + ((w) << 2)) | ||
98 | #define WINDOW_OVERRIDE_CTRL(chan) (0x2A0 + ((chan) << 2)) | ||
99 | #define XOR_DEST_POINTER(chan) (0x2B0 + (chan * 4)) | ||
100 | #define XOR_BLOCK_SIZE(chan) (0x2C0 + (chan * 4)) | ||
101 | #define XOR_INIT_VALUE_LOW 0x2E0 | ||
102 | #define XOR_INIT_VALUE_HIGH 0x2E4 | ||
103 | |||
104 | static inline void mvebu_hwcc_armada375_sync_io_barrier_wa(void) | ||
105 | { | ||
106 | int idx = smp_processor_id(); | ||
107 | |||
108 | /* Write '1' to the first word of the buffer */ | ||
109 | writel(0x1, coherency_wa_buf[idx]); | ||
110 | |||
111 | /* Wait until the engine is idle */ | ||
112 | while ((readl(xor_base + XOR_ACTIVATION(idx)) >> 4) & 0x3) | ||
113 | ; | ||
114 | |||
115 | dmb(); | ||
116 | |||
117 | /* Trigger channel */ | ||
118 | writel(0x1, xor_base + XOR_ACTIVATION(idx)); | ||
119 | |||
120 | /* Poll the data until it is cleared by the XOR transaction */ | ||
121 | while (readl(coherency_wa_buf[idx])) | ||
122 | ; | ||
123 | } | ||
124 | |||
125 | static void __init armada_375_coherency_init_wa(void) | ||
126 | { | ||
127 | const struct mbus_dram_target_info *dram; | ||
128 | struct device_node *xor_node; | ||
129 | struct property *xor_status; | ||
130 | struct clk *xor_clk; | ||
131 | u32 win_enable = 0; | ||
132 | int i; | ||
133 | |||
134 | pr_warn("enabling coherency workaround for Armada 375 Z1, one XOR engine disabled\n"); | ||
135 | |||
136 | /* | ||
137 | * Since the workaround uses one XOR engine, we grab a | ||
138 | * reference to its Device Tree node first. | ||
139 | */ | ||
140 | xor_node = of_find_compatible_node(NULL, NULL, "marvell,orion-xor"); | ||
141 | BUG_ON(!xor_node); | ||
142 | |||
143 | /* | ||
144 | * Then we mark it as disabled so that the real XOR driver | ||
145 | * will not use it. | ||
146 | */ | ||
147 | xor_status = kzalloc(sizeof(struct property), GFP_KERNEL); | ||
148 | BUG_ON(!xor_status); | ||
149 | |||
150 | xor_status->value = kstrdup("disabled", GFP_KERNEL); | ||
151 | BUG_ON(!xor_status->value); | ||
152 | |||
153 | xor_status->length = 8; | ||
154 | xor_status->name = kstrdup("status", GFP_KERNEL); | ||
155 | BUG_ON(!xor_status->name); | ||
156 | |||
157 | of_update_property(xor_node, xor_status); | ||
158 | |||
159 | /* | ||
160 | * And we remap the registers, get the clock, and do the | ||
161 | * initial configuration of the XOR engine. | ||
162 | */ | ||
163 | xor_base = of_iomap(xor_node, 0); | ||
164 | xor_high_base = of_iomap(xor_node, 1); | ||
165 | |||
166 | xor_clk = of_clk_get_by_name(xor_node, NULL); | ||
167 | BUG_ON(!xor_clk); | ||
168 | |||
169 | clk_prepare_enable(xor_clk); | ||
170 | |||
171 | dram = mv_mbus_dram_info(); | ||
172 | |||
173 | for (i = 0; i < 8; i++) { | ||
174 | writel(0, xor_base + WINDOW_BASE(i)); | ||
175 | writel(0, xor_base + WINDOW_SIZE(i)); | ||
176 | if (i < 4) | ||
177 | writel(0, xor_base + WINDOW_REMAP_HIGH(i)); | ||
178 | } | ||
179 | |||
180 | for (i = 0; i < dram->num_cs; i++) { | ||
181 | const struct mbus_dram_window *cs = dram->cs + i; | ||
182 | writel((cs->base & 0xffff0000) | | ||
183 | (cs->mbus_attr << 8) | | ||
184 | dram->mbus_dram_target_id, xor_base + WINDOW_BASE(i)); | ||
185 | writel((cs->size - 1) & 0xffff0000, xor_base + WINDOW_SIZE(i)); | ||
186 | |||
187 | win_enable |= (1 << i); | ||
188 | win_enable |= 3 << (16 + (2 * i)); | ||
189 | } | ||
190 | |||
191 | writel(win_enable, xor_base + WINDOW_BAR_ENABLE(0)); | ||
192 | writel(win_enable, xor_base + WINDOW_BAR_ENABLE(1)); | ||
193 | writel(0, xor_base + WINDOW_OVERRIDE_CTRL(0)); | ||
194 | writel(0, xor_base + WINDOW_OVERRIDE_CTRL(1)); | ||
195 | |||
196 | for (i = 0; i < CONFIG_NR_CPUS; i++) { | ||
197 | coherency_wa_buf[i] = kzalloc(PAGE_SIZE, GFP_KERNEL); | ||
198 | BUG_ON(!coherency_wa_buf[i]); | ||
199 | |||
200 | /* | ||
201 | * We can't use the DMA mapping API, since we don't | ||
202 | * have a valid 'struct device' pointer | ||
203 | */ | ||
204 | coherency_wa_buf_phys[i] = | ||
205 | virt_to_phys(coherency_wa_buf[i]); | ||
206 | BUG_ON(!coherency_wa_buf_phys[i]); | ||
207 | |||
208 | /* | ||
209 | * Configure the XOR engine for memset operation, with | ||
210 | * a 128 bytes block size | ||
211 | */ | ||
212 | writel(0x444, xor_base + XOR_CONFIG(i)); | ||
213 | writel(128, xor_base + XOR_BLOCK_SIZE(i)); | ||
214 | writel(coherency_wa_buf_phys[i], | ||
215 | xor_base + XOR_DEST_POINTER(i)); | ||
216 | } | ||
217 | |||
218 | writel(0x0, xor_base + XOR_INIT_VALUE_LOW); | ||
219 | writel(0x0, xor_base + XOR_INIT_VALUE_HIGH); | ||
220 | |||
221 | coherency_wa_enabled = true; | ||
58 | } | 222 | } |
59 | 223 | ||
60 | static inline void mvebu_hwcc_sync_io_barrier(void) | 224 | static inline void mvebu_hwcc_sync_io_barrier(void) |
61 | { | 225 | { |
226 | if (coherency_wa_enabled) { | ||
227 | mvebu_hwcc_armada375_sync_io_barrier_wa(); | ||
228 | return; | ||
229 | } | ||
230 | |||
62 | writel(0x1, coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET); | 231 | writel(0x1, coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET); |
63 | while (readl(coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET) & 0x1); | 232 | while (readl(coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET) & 0x1); |
64 | } | 233 | } |
@@ -121,42 +290,93 @@ static struct notifier_block mvebu_hwcc_platform_nb = { | |||
121 | .notifier_call = mvebu_hwcc_platform_notifier, | 290 | .notifier_call = mvebu_hwcc_platform_notifier, |
122 | }; | 291 | }; |
123 | 292 | ||
124 | int __init coherency_init(void) | 293 | static void __init armada_370_coherency_init(struct device_node *np) |
294 | { | ||
295 | struct resource res; | ||
296 | |||
297 | of_address_to_resource(np, 0, &res); | ||
298 | coherency_phys_base = res.start; | ||
299 | /* | ||
300 | * Ensure secondary CPUs will see the updated value, | ||
301 | * which they read before they join the coherency | ||
302 | * fabric, and therefore before they are coherent with | ||
303 | * the boot CPU cache. | ||
304 | */ | ||
305 | sync_cache_w(&coherency_phys_base); | ||
306 | coherency_base = of_iomap(np, 0); | ||
307 | coherency_cpu_base = of_iomap(np, 1); | ||
308 | set_cpu_coherent(); | ||
309 | } | ||
310 | |||
311 | static void __init armada_375_380_coherency_init(struct device_node *np) | ||
312 | { | ||
313 | coherency_cpu_base = of_iomap(np, 0); | ||
314 | } | ||
315 | |||
316 | static int coherency_type(void) | ||
125 | { | 317 | { |
126 | struct device_node *np; | 318 | struct device_node *np; |
319 | const struct of_device_id *match; | ||
127 | 320 | ||
128 | np = of_find_matching_node(NULL, of_coherency_table); | 321 | np = of_find_matching_node_and_match(NULL, of_coherency_table, &match); |
129 | if (np) { | 322 | if (np) { |
130 | struct resource res; | 323 | int type = (int) match->data; |
131 | pr_info("Initializing Coherency fabric\n"); | 324 | |
132 | of_address_to_resource(np, 0, &res); | 325 | /* Armada 370/XP coherency works in both UP and SMP */ |
133 | coherency_phys_base = res.start; | 326 | if (type == COHERENCY_FABRIC_TYPE_ARMADA_370_XP) |
134 | /* | 327 | return type; |
135 | * Ensure secondary CPUs will see the updated value, | 328 | |
136 | * which they read before they join the coherency | 329 | /* Armada 375 coherency works only on SMP */ |
137 | * fabric, and therefore before they are coherent with | 330 | else if (type == COHERENCY_FABRIC_TYPE_ARMADA_375 && is_smp()) |
138 | * the boot CPU cache. | 331 | return type; |
139 | */ | 332 | |
140 | sync_cache_w(&coherency_phys_base); | 333 | /* Armada 380 coherency works only on SMP */ |
141 | coherency_base = of_iomap(np, 0); | 334 | else if (type == COHERENCY_FABRIC_TYPE_ARMADA_380 && is_smp()) |
142 | coherency_cpu_base = of_iomap(np, 1); | 335 | return type; |
143 | set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0); | ||
144 | of_node_put(np); | ||
145 | } | 336 | } |
146 | 337 | ||
147 | return 0; | 338 | return COHERENCY_FABRIC_TYPE_NONE; |
148 | } | 339 | } |
149 | 340 | ||
150 | static int __init coherency_late_init(void) | 341 | int coherency_available(void) |
342 | { | ||
343 | return coherency_type() != COHERENCY_FABRIC_TYPE_NONE; | ||
344 | } | ||
345 | |||
346 | int __init coherency_init(void) | ||
151 | { | 347 | { |
348 | int type = coherency_type(); | ||
152 | struct device_node *np; | 349 | struct device_node *np; |
153 | 350 | ||
154 | np = of_find_matching_node(NULL, of_coherency_table); | 351 | np = of_find_matching_node(NULL, of_coherency_table); |
155 | if (np) { | 352 | |
156 | bus_register_notifier(&platform_bus_type, | 353 | if (type == COHERENCY_FABRIC_TYPE_ARMADA_370_XP) |
157 | &mvebu_hwcc_platform_nb); | 354 | armada_370_coherency_init(np); |
158 | of_node_put(np); | 355 | else if (type == COHERENCY_FABRIC_TYPE_ARMADA_375 || |
356 | type == COHERENCY_FABRIC_TYPE_ARMADA_380) | ||
357 | armada_375_380_coherency_init(np); | ||
358 | |||
359 | return 0; | ||
360 | } | ||
361 | |||
362 | static int __init coherency_late_init(void) | ||
363 | { | ||
364 | int type = coherency_type(); | ||
365 | |||
366 | if (type == COHERENCY_FABRIC_TYPE_NONE) | ||
367 | return 0; | ||
368 | |||
369 | if (type == COHERENCY_FABRIC_TYPE_ARMADA_375) { | ||
370 | u32 dev, rev; | ||
371 | |||
372 | if (mvebu_get_soc_id(&dev, &rev) == 0 && | ||
373 | rev == ARMADA_375_Z1_REV) | ||
374 | armada_375_coherency_init_wa(); | ||
159 | } | 375 | } |
376 | |||
377 | bus_register_notifier(&platform_bus_type, | ||
378 | &mvebu_hwcc_platform_nb); | ||
379 | |||
160 | return 0; | 380 | return 0; |
161 | } | 381 | } |
162 | 382 | ||
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..6828f9f157b0 100644 --- a/arch/arm/mach-mvebu/coherency_ll.S +++ b/arch/arm/mach-mvebu/coherency_ll.S | |||
@@ -21,38 +21,108 @@ | |||
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 |
26 | /* | 27 | /* Returns with the coherency address in r1 (r0 is untouched)*/ |
27 | * r0: Coherency fabric base register address | 28 | ENTRY(ll_get_coherency_base) |
28 | * r1: HW CPU id | 29 | mrc p15, 0, r1, c1, c0, 0 |
29 | */ | 30 | tst r1, #CR_M @ Check MMU bit enabled |
30 | ENTRY(ll_set_cpu_coherent) | 31 | bne 1f |
31 | /* Create bit by cpu index */ | ||
32 | mov r3, #(1 << 24) | ||
33 | lsl r1, r3, r1 | ||
34 | ARM_BE8(rev r1, r1) | ||
35 | 32 | ||
36 | /* Add CPU to SMP group - Atomic */ | 33 | /* use physical address of the coherency register */ |
37 | add r3, r0, #ARMADA_XP_CFB_CTL_REG_OFFSET | 34 | adr r1, 3f |
35 | ldr r3, [r1] | ||
36 | ldr r1, [r1, r3] | ||
37 | b 2f | ||
38 | 1: | 38 | 1: |
39 | ldrex r2, [r3] | 39 | /* use virtual address of the coherency register */ |
40 | orr r2, r2, r1 | 40 | ldr r1, =coherency_base |
41 | strex r0, r2, [r3] | 41 | ldr r1, [r1] |
42 | cmp r0, #0 | 42 | 2: |
43 | bne 1b | 43 | mov pc, lr |
44 | 44 | ENDPROC(ll_get_coherency_base) | |
45 | /* Enable coherency on CPU - Atomic */ | 45 | |
46 | add r3, r3, #ARMADA_XP_CFB_CFG_REG_OFFSET | 46 | /* Returns with the CPU ID in r3 (r0 is untouched)*/ |
47 | ENTRY(ll_get_cpuid) | ||
48 | mrc 15, 0, r3, cr0, cr0, 5 | ||
49 | and r3, r3, #15 | ||
50 | mov r2, #(1 << 24) | ||
51 | lsl r3, r2, r3 | ||
52 | ARM_BE8(rev r1, r1) | ||
53 | mov pc, lr | ||
54 | ENDPROC(ll_get_cpuid) | ||
55 | |||
56 | /* ll_add_cpu_to_smp_group, ll_enable_coherency and | ||
57 | * ll_disable_coherency use strex/ldrex whereas MMU can be off. The | ||
58 | * Armada XP SoC has an exclusive monitor that can track transactions | ||
59 | * to Device and/or SO and as such also when MMU is disabled the | ||
60 | * exclusive transactions will be functional | ||
61 | */ | ||
62 | |||
63 | ENTRY(ll_add_cpu_to_smp_group) | ||
64 | /* | ||
65 | * r0 being untouched in ll_get_coherency_base and | ||
66 | * ll_get_cpuid, we can use it to save lr modifing it with the | ||
67 | * following bl | ||
68 | */ | ||
69 | mov r0, lr | ||
70 | bl ll_get_coherency_base | ||
71 | bl ll_get_cpuid | ||
72 | mov lr, r0 | ||
73 | add r0, r1, #ARMADA_XP_CFB_CFG_REG_OFFSET | ||
47 | 1: | 74 | 1: |
48 | ldrex r2, [r3] | 75 | ldrex r2, [r0] |
49 | orr r2, r2, r1 | 76 | orr r2, r2, r3 |
50 | strex r0, r2, [r3] | 77 | strex r1, r2, [r0] |
51 | cmp r0, #0 | 78 | cmp r1, #0 |
52 | bne 1b | 79 | bne 1b |
80 | mov pc, lr | ||
81 | ENDPROC(ll_add_cpu_to_smp_group) | ||
53 | 82 | ||
83 | ENTRY(ll_enable_coherency) | ||
84 | /* | ||
85 | * r0 being untouched in ll_get_coherency_base and | ||
86 | * ll_get_cpuid, we can use it to save lr modifing it with the | ||
87 | * following bl | ||
88 | */ | ||
89 | mov r0, lr | ||
90 | bl ll_get_coherency_base | ||
91 | bl ll_get_cpuid | ||
92 | mov lr, r0 | ||
93 | add r0, r1, #ARMADA_XP_CFB_CTL_REG_OFFSET | ||
94 | 1: | ||
95 | ldrex r2, [r0] | ||
96 | orr r2, r2, r3 | ||
97 | strex r1, r2, [r0] | ||
98 | cmp r1, #0 | ||
99 | bne 1b | ||
54 | dsb | 100 | dsb |
55 | |||
56 | mov r0, #0 | 101 | mov r0, #0 |
57 | mov pc, lr | 102 | mov pc, lr |
58 | ENDPROC(ll_set_cpu_coherent) | 103 | ENDPROC(ll_enable_coherency) |
104 | |||
105 | ENTRY(ll_disable_coherency) | ||
106 | /* | ||
107 | * r0 being untouched in ll_get_coherency_base and | ||
108 | * ll_get_cpuid, we can use it to save lr modifing it with the | ||
109 | * following bl | ||
110 | */ | ||
111 | mov r0, lr | ||
112 | bl ll_get_coherency_base | ||
113 | bl ll_get_cpuid | ||
114 | mov lr, r0 | ||
115 | add r0, r1, #ARMADA_XP_CFB_CTL_REG_OFFSET | ||
116 | 1: | ||
117 | ldrex r2, [r0] | ||
118 | bic r2, r2, r3 | ||
119 | strex r1, r2, [r0] | ||
120 | cmp r1, #0 | ||
121 | bne 1b | ||
122 | dsb | ||
123 | mov pc, lr | ||
124 | ENDPROC(ll_disable_coherency) | ||
125 | |||
126 | .align 2 | ||
127 | 3: | ||
128 | .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 f3d4cf53f746..e9119a99a5f3 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 |
@@ -116,5 +118,33 @@ clk_err: | |||
116 | 118 | ||
117 | return ret; | 119 | return ret; |
118 | } | 120 | } |
119 | core_initcall(mvebu_soc_id_init); | 121 | early_initcall(mvebu_soc_id_init); |
120 | 122 | ||
123 | static int __init mvebu_soc_device(void) | ||
124 | { | ||
125 | struct soc_device_attribute *soc_dev_attr; | ||
126 | struct soc_device *soc_dev; | ||
127 | |||
128 | /* Also protects against running on non-mvebu systems */ | ||
129 | if (!is_id_valid) | ||
130 | return 0; | ||
131 | |||
132 | soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); | ||
133 | if (!soc_dev_attr) | ||
134 | return -ENOMEM; | ||
135 | |||
136 | soc_dev_attr->family = kasprintf(GFP_KERNEL, "Marvell"); | ||
137 | soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%X", soc_rev); | ||
138 | soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%X", soc_dev_id); | ||
139 | |||
140 | soc_dev = soc_device_register(soc_dev_attr); | ||
141 | if (IS_ERR(soc_dev)) { | ||
142 | kfree(soc_dev_attr->family); | ||
143 | kfree(soc_dev_attr->revision); | ||
144 | kfree(soc_dev_attr->soc_id); | ||
145 | kfree(soc_dev_attr); | ||
146 | } | ||
147 | |||
148 | return 0; | ||
149 | } | ||
150 | 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); |
diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c index 293e2e0a0a87..ff02fc90fc21 100644 --- a/drivers/bus/mvebu-mbus.c +++ b/drivers/bus/mvebu-mbus.c | |||
@@ -694,7 +694,6 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus, | |||
694 | phys_addr_t sdramwins_phys_base, | 694 | phys_addr_t sdramwins_phys_base, |
695 | size_t sdramwins_size) | 695 | size_t sdramwins_size) |
696 | { | 696 | { |
697 | struct device_node *np; | ||
698 | int win; | 697 | int win; |
699 | 698 | ||
700 | mbus->mbuswins_base = ioremap(mbuswins_phys_base, mbuswins_size); | 699 | mbus->mbuswins_base = ioremap(mbuswins_phys_base, mbuswins_size); |
@@ -707,12 +706,6 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus, | |||
707 | return -ENOMEM; | 706 | return -ENOMEM; |
708 | } | 707 | } |
709 | 708 | ||
710 | np = of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric"); | ||
711 | if (np) { | ||
712 | mbus->hw_io_coherency = 1; | ||
713 | of_node_put(np); | ||
714 | } | ||
715 | |||
716 | for (win = 0; win < mbus->soc->num_wins; win++) | 709 | for (win = 0; win < mbus->soc->num_wins; win++) |
717 | mvebu_mbus_disable_window(mbus, win); | 710 | mvebu_mbus_disable_window(mbus, win); |
718 | 711 | ||
@@ -882,7 +875,7 @@ static void __init mvebu_mbus_get_pcie_resources(struct device_node *np, | |||
882 | } | 875 | } |
883 | } | 876 | } |
884 | 877 | ||
885 | int __init mvebu_mbus_dt_init(void) | 878 | int __init mvebu_mbus_dt_init(bool is_coherent) |
886 | { | 879 | { |
887 | struct resource mbuswins_res, sdramwins_res; | 880 | struct resource mbuswins_res, sdramwins_res; |
888 | struct device_node *np, *controller; | 881 | struct device_node *np, *controller; |
@@ -920,6 +913,8 @@ int __init mvebu_mbus_dt_init(void) | |||
920 | return -EINVAL; | 913 | return -EINVAL; |
921 | } | 914 | } |
922 | 915 | ||
916 | mbus_state.hw_io_coherency = is_coherent; | ||
917 | |||
923 | /* Get optional pcie-{mem,io}-aperture properties */ | 918 | /* Get optional pcie-{mem,io}-aperture properties */ |
924 | mvebu_mbus_get_pcie_resources(np, &mbus_state.pcie_mem_aperture, | 919 | mvebu_mbus_get_pcie_resources(np, &mbus_state.pcie_mem_aperture, |
925 | &mbus_state.pcie_io_aperture); | 920 | &mbus_state.pcie_io_aperture); |
diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm index 97ccc31dbdd8..5bb94780d377 100644 --- a/drivers/cpuidle/Kconfig.arm +++ b/drivers/cpuidle/Kconfig.arm | |||
@@ -1,6 +1,11 @@ | |||
1 | # | 1 | # |
2 | # ARM CPU Idle drivers | 2 | # ARM CPU Idle drivers |
3 | # | 3 | # |
4 | config ARM_ARMADA_370_XP_CPUIDLE | ||
5 | bool "CPU Idle Driver for Armada 370/XP family processors" | ||
6 | depends on ARCH_MVEBU | ||
7 | help | ||
8 | Select this to enable cpuidle on Armada 370/XP processors. | ||
4 | 9 | ||
5 | config ARM_BIG_LITTLE_CPUIDLE | 10 | config ARM_BIG_LITTLE_CPUIDLE |
6 | bool "Support for ARM big.LITTLE processors" | 11 | bool "Support for ARM big.LITTLE processors" |
diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index f71ae1b373c5..9902d052bd87 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile | |||
@@ -7,6 +7,7 @@ obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o | |||
7 | 7 | ||
8 | ################################################################################## | 8 | ################################################################################## |
9 | # ARM SoC drivers | 9 | # ARM SoC drivers |
10 | obj-$(CONFIG_ARM_ARMADA_370_XP_CPUIDLE) += cpuidle-armada-370-xp.o | ||
10 | obj-$(CONFIG_ARM_BIG_LITTLE_CPUIDLE) += cpuidle-big_little.o | 11 | obj-$(CONFIG_ARM_BIG_LITTLE_CPUIDLE) += cpuidle-big_little.o |
11 | obj-$(CONFIG_ARM_HIGHBANK_CPUIDLE) += cpuidle-calxeda.o | 12 | obj-$(CONFIG_ARM_HIGHBANK_CPUIDLE) += cpuidle-calxeda.o |
12 | obj-$(CONFIG_ARM_KIRKWOOD_CPUIDLE) += cpuidle-kirkwood.o | 13 | obj-$(CONFIG_ARM_KIRKWOOD_CPUIDLE) += cpuidle-kirkwood.o |
diff --git a/drivers/cpuidle/cpuidle-armada-370-xp.c b/drivers/cpuidle/cpuidle-armada-370-xp.c new file mode 100644 index 000000000000..28587d0f3947 --- /dev/null +++ b/drivers/cpuidle/cpuidle-armada-370-xp.c | |||
@@ -0,0 +1,93 @@ | |||
1 | /* | ||
2 | * Marvell Armada 370 and Armada XP SoC cpuidle driver | ||
3 | * | ||
4 | * Copyright (C) 2014 Marvell | ||
5 | * | ||
6 | * Nadav Haklai <nadavh@marvell.com> | ||
7 | * Gregory CLEMENT <gregory.clement@free-electrons.com> | ||
8 | * | ||
9 | * This file is licensed under the terms of the GNU General Public | ||
10 | * License version 2. This program is licensed "as is" without any | ||
11 | * warranty of any kind, whether express or implied. | ||
12 | * | ||
13 | * Maintainer: Gregory CLEMENT <gregory.clement@free-electrons.com> | ||
14 | */ | ||
15 | |||
16 | #include <linux/cpu_pm.h> | ||
17 | #include <linux/cpuidle.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/of.h> | ||
20 | #include <linux/suspend.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <asm/cpuidle.h> | ||
23 | |||
24 | #define ARMADA_370_XP_MAX_STATES 3 | ||
25 | #define ARMADA_370_XP_FLAG_DEEP_IDLE 0x10000 | ||
26 | |||
27 | static int (*armada_370_xp_cpu_suspend)(int); | ||
28 | |||
29 | static int armada_370_xp_enter_idle(struct cpuidle_device *dev, | ||
30 | struct cpuidle_driver *drv, | ||
31 | int index) | ||
32 | { | ||
33 | int ret; | ||
34 | bool deepidle = false; | ||
35 | cpu_pm_enter(); | ||
36 | |||
37 | if (drv->states[index].flags & ARMADA_370_XP_FLAG_DEEP_IDLE) | ||
38 | deepidle = true; | ||
39 | |||
40 | ret = armada_370_xp_cpu_suspend(deepidle); | ||
41 | if (ret) | ||
42 | return ret; | ||
43 | |||
44 | cpu_pm_exit(); | ||
45 | |||
46 | return index; | ||
47 | } | ||
48 | |||
49 | static struct cpuidle_driver armada_370_xp_idle_driver = { | ||
50 | .name = "armada_370_xp_idle", | ||
51 | .states[0] = ARM_CPUIDLE_WFI_STATE, | ||
52 | .states[1] = { | ||
53 | .enter = armada_370_xp_enter_idle, | ||
54 | .exit_latency = 10, | ||
55 | .power_usage = 50, | ||
56 | .target_residency = 100, | ||
57 | .flags = CPUIDLE_FLAG_TIME_VALID, | ||
58 | .name = "MV CPU IDLE", | ||
59 | .desc = "CPU power down", | ||
60 | }, | ||
61 | .states[2] = { | ||
62 | .enter = armada_370_xp_enter_idle, | ||
63 | .exit_latency = 100, | ||
64 | .power_usage = 5, | ||
65 | .target_residency = 1000, | ||
66 | .flags = CPUIDLE_FLAG_TIME_VALID | | ||
67 | ARMADA_370_XP_FLAG_DEEP_IDLE, | ||
68 | .name = "MV CPU DEEP IDLE", | ||
69 | .desc = "CPU and L2 Fabric power down", | ||
70 | }, | ||
71 | .state_count = ARMADA_370_XP_MAX_STATES, | ||
72 | }; | ||
73 | |||
74 | static int armada_370_xp_cpuidle_probe(struct platform_device *pdev) | ||
75 | { | ||
76 | |||
77 | armada_370_xp_cpu_suspend = (void *)(pdev->dev.platform_data); | ||
78 | return cpuidle_register(&armada_370_xp_idle_driver, NULL); | ||
79 | } | ||
80 | |||
81 | static struct platform_driver armada_370_xp_cpuidle_plat_driver = { | ||
82 | .driver = { | ||
83 | .name = "cpuidle-armada-370-xp", | ||
84 | .owner = THIS_MODULE, | ||
85 | }, | ||
86 | .probe = armada_370_xp_cpuidle_probe, | ||
87 | }; | ||
88 | |||
89 | module_platform_driver(armada_370_xp_cpuidle_plat_driver); | ||
90 | |||
91 | MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>"); | ||
92 | MODULE_DESCRIPTION("Armada 370/XP cpu idle driver"); | ||
93 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index 3899ba7821c5..c887e6eebc41 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/irq.h> | 19 | #include <linux/irq.h> |
20 | #include <linux/interrupt.h> | 20 | #include <linux/interrupt.h> |
21 | #include <linux/irqchip/chained_irq.h> | 21 | #include <linux/irqchip/chained_irq.h> |
22 | #include <linux/cpu.h> | ||
22 | #include <linux/io.h> | 23 | #include <linux/io.h> |
23 | #include <linux/of_address.h> | 24 | #include <linux/of_address.h> |
24 | #include <linux/of_irq.h> | 25 | #include <linux/of_irq.h> |
@@ -310,7 +311,8 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h, | |||
310 | } | 311 | } |
311 | 312 | ||
312 | #ifdef CONFIG_SMP | 313 | #ifdef CONFIG_SMP |
313 | void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq) | 314 | static void armada_mpic_send_doorbell(const struct cpumask *mask, |
315 | unsigned int irq) | ||
314 | { | 316 | { |
315 | int cpu; | 317 | int cpu; |
316 | unsigned long map = 0; | 318 | unsigned long map = 0; |
@@ -330,7 +332,7 @@ void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq) | |||
330 | ARMADA_370_XP_SW_TRIG_INT_OFFS); | 332 | ARMADA_370_XP_SW_TRIG_INT_OFFS); |
331 | } | 333 | } |
332 | 334 | ||
333 | void armada_xp_mpic_smp_cpu_init(void) | 335 | static void armada_xp_mpic_smp_cpu_init(void) |
334 | { | 336 | { |
335 | /* Clear pending IPIs */ | 337 | /* Clear pending IPIs */ |
336 | writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); | 338 | writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); |
@@ -342,6 +344,20 @@ void armada_xp_mpic_smp_cpu_init(void) | |||
342 | /* Unmask IPI interrupt */ | 344 | /* Unmask IPI interrupt */ |
343 | writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); | 345 | writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); |
344 | } | 346 | } |
347 | |||
348 | static int armada_xp_mpic_secondary_init(struct notifier_block *nfb, | ||
349 | unsigned long action, void *hcpu) | ||
350 | { | ||
351 | if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) | ||
352 | armada_xp_mpic_smp_cpu_init(); | ||
353 | return NOTIFY_OK; | ||
354 | } | ||
355 | |||
356 | static struct notifier_block armada_370_xp_mpic_cpu_notifier = { | ||
357 | .notifier_call = armada_xp_mpic_secondary_init, | ||
358 | .priority = 100, | ||
359 | }; | ||
360 | |||
345 | #endif /* CONFIG_SMP */ | 361 | #endif /* CONFIG_SMP */ |
346 | 362 | ||
347 | static struct irq_domain_ops armada_370_xp_mpic_irq_ops = { | 363 | static struct irq_domain_ops armada_370_xp_mpic_irq_ops = { |
@@ -497,6 +513,10 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, | |||
497 | if (parent_irq <= 0) { | 513 | if (parent_irq <= 0) { |
498 | irq_set_default_host(armada_370_xp_mpic_domain); | 514 | irq_set_default_host(armada_370_xp_mpic_domain); |
499 | set_handle_irq(armada_370_xp_handle_irq); | 515 | set_handle_irq(armada_370_xp_handle_irq); |
516 | #ifdef CONFIG_SMP | ||
517 | set_smp_cross_call(armada_mpic_send_doorbell); | ||
518 | register_cpu_notifier(&armada_370_xp_mpic_cpu_notifier); | ||
519 | #endif | ||
500 | } else { | 520 | } else { |
501 | irq_set_chained_handler(parent_irq, | 521 | irq_set_chained_handler(parent_irq, |
502 | armada_370_xp_mpic_handle_cascade_irq); | 522 | armada_370_xp_mpic_handle_cascade_irq); |
diff --git a/drivers/irqchip/irq-orion.c b/drivers/irqchip/irq-orion.c index e25f246cd2fb..34d18b48bb78 100644 --- a/drivers/irqchip/irq-orion.c +++ b/drivers/irqchip/irq-orion.c | |||
@@ -42,7 +42,7 @@ __exception_irq_entry orion_handle_irq(struct pt_regs *regs) | |||
42 | u32 stat = readl_relaxed(gc->reg_base + ORION_IRQ_CAUSE) & | 42 | u32 stat = readl_relaxed(gc->reg_base + ORION_IRQ_CAUSE) & |
43 | gc->mask_cache; | 43 | gc->mask_cache; |
44 | while (stat) { | 44 | while (stat) { |
45 | u32 hwirq = ffs(stat) - 1; | 45 | u32 hwirq = __fls(stat); |
46 | u32 irq = irq_find_mapping(orion_irq_domain, | 46 | u32 irq = irq_find_mapping(orion_irq_domain, |
47 | gc->irq_base + hwirq); | 47 | gc->irq_base + hwirq); |
48 | handle_IRQ(irq, regs); | 48 | handle_IRQ(irq, regs); |
@@ -117,7 +117,7 @@ static void orion_bridge_irq_handler(unsigned int irq, struct irq_desc *desc) | |||
117 | gc->mask_cache; | 117 | gc->mask_cache; |
118 | 118 | ||
119 | while (stat) { | 119 | while (stat) { |
120 | u32 hwirq = ffs(stat) - 1; | 120 | u32 hwirq = __fls(stat); |
121 | 121 | ||
122 | generic_handle_irq(irq_find_mapping(d, gc->irq_base + hwirq)); | 122 | generic_handle_irq(irq_find_mapping(d, gc->irq_base + hwirq)); |
123 | stat &= ~(1 << hwirq); | 123 | stat &= ~(1 << hwirq); |
diff --git a/include/linux/mbus.h b/include/linux/mbus.h index 345b8c53b897..550c88fb0267 100644 --- a/include/linux/mbus.h +++ b/include/linux/mbus.h | |||
@@ -73,6 +73,6 @@ int mvebu_mbus_del_window(phys_addr_t base, size_t size); | |||
73 | int mvebu_mbus_init(const char *soc, phys_addr_t mbus_phys_base, | 73 | int mvebu_mbus_init(const char *soc, phys_addr_t mbus_phys_base, |
74 | size_t mbus_size, phys_addr_t sdram_phys_base, | 74 | size_t mbus_size, phys_addr_t sdram_phys_base, |
75 | size_t sdram_size); | 75 | size_t sdram_size); |
76 | int mvebu_mbus_dt_init(void); | 76 | int mvebu_mbus_dt_init(bool is_coherent); |
77 | 77 | ||
78 | #endif /* __LINUX_MBUS_H */ | 78 | #endif /* __LINUX_MBUS_H */ |