diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-08 14:14:29 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-08 14:14:29 -0400 |
commit | b3345d7c57d70e6cb6749af25cdbe80515582e99 (patch) | |
tree | 04cce706bc7e944ad1fb257108a8ae735948f97f /arch/arm/mach-mvebu | |
parent | 44c916d58b9ef1f2c4aec2def57fa8289c716a60 (diff) | |
parent | c2fff85e21818952aa0ee5778926beee6c03e579 (diff) |
Merge tag 'soc-for-3.17' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC platform changes from Olof Johansson:
"This is the bulk of new SoC enablement and other platform changes for
3.17:
- Samsung S5PV210 has been converted to DT and multiplatform
- Clock drivers and bindings for some of the lower-end i.MX 1/2
platforms
- Kirkwood, one of the popular Marvell platforms, is folded into the
mvebu platform code, removing mach-kirkwood
- Hwmod data for TI AM43xx and DRA7 platforms
- More additions of Renesas shmobile platform support
- Removal of plat-samsung contents that can be removed with S5PV210
being multiplatform/DT-enabled and the other two old platforms
being removed
New platforms (most with only basic support right now):
- Hisilicon X5HD2 settop box chipset is introduced
- Mediatek MT6589 (mobile chipset) is introduced
- Broadcom BCM7xxx settop box chipset is introduced
+ as usual a lot other pieces all over the platform code"
* tag 'soc-for-3.17' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (240 commits)
ARM: hisi: remove smp from machine descriptor
power: reset: move hisilicon reboot code
ARM: dts: Add hix5hd2-dkb dts file.
ARM: debug: Rename Hi3716 to HIX5HD2
ARM: hisi: enable hix5hd2 SoC
ARM: hisi: add ARCH_HISI
MAINTAINERS: add entry for Broadcom ARM STB architecture
ARM: brcmstb: select GISB arbiter and interrupt drivers
ARM: brcmstb: add infrastructure for ARM-based Broadcom STB SoCs
ARM: configs: enable SMP in bcm_defconfig
ARM: add SMP support for Broadcom mobile SoCs
Documentation: arm: misc updates to Marvell EBU SoC status
Documentation: arm: add URLs to public datasheets for the Marvell Armada XP SoC
ARM: mvebu: fix build without platforms selected
ARM: mvebu: add cpuidle support for Armada 38x
ARM: mvebu: add cpuidle support for Armada 370
cpuidle: mvebu: add Armada 38x support
cpuidle: mvebu: add Armada 370 support
cpuidle: mvebu: rename the driver from armada-370-xp to mvebu-v7
ARM: mvebu: export the SCU address
...
Diffstat (limited to 'arch/arm/mach-mvebu')
-rw-r--r-- | arch/arm/mach-mvebu/Kconfig | 13 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/Makefile | 4 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/armada-370-xp.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/board-v7.c | 22 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/board.h | 5 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/common.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/cpu-reset.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/headsmp-a9.S | 15 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/hotplug.c | 31 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/kirkwood.c | 3 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/mvebu-soc-id.c | 21 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/netxbig.c | 191 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/platsmp-a9.c | 45 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/platsmp.c | 49 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/pmsu.c | 438 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/pmsu.h | 5 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/pmsu_ll.S | 36 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/system-controller.c | 50 |
18 files changed, 797 insertions, 138 deletions
diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig index b9bc599a5fd0..c1e4567a5ab3 100644 --- a/arch/arm/mach-mvebu/Kconfig +++ b/arch/arm/mach-mvebu/Kconfig | |||
@@ -14,11 +14,15 @@ menuconfig ARCH_MVEBU | |||
14 | 14 | ||
15 | if ARCH_MVEBU | 15 | if ARCH_MVEBU |
16 | 16 | ||
17 | config MACH_MVEBU_ANY | ||
18 | bool | ||
19 | |||
17 | config MACH_MVEBU_V7 | 20 | config MACH_MVEBU_V7 |
18 | bool | 21 | bool |
19 | select ARMADA_370_XP_TIMER | 22 | select ARMADA_370_XP_TIMER |
20 | select CACHE_L2X0 | 23 | select CACHE_L2X0 |
21 | select ARM_CPU_SUSPEND | 24 | select ARM_CPU_SUSPEND |
25 | select MACH_MVEBU_ANY | ||
22 | 26 | ||
23 | config MACH_ARMADA_370 | 27 | config MACH_ARMADA_370 |
24 | bool "Marvell Armada 370 boards" if ARCH_MULTI_V7 | 28 | bool "Marvell Armada 370 boards" if ARCH_MULTI_V7 |
@@ -75,6 +79,7 @@ config MACH_DOVE | |||
75 | select CACHE_L2X0 | 79 | select CACHE_L2X0 |
76 | select CPU_PJ4 | 80 | select CPU_PJ4 |
77 | select DOVE_CLK | 81 | select DOVE_CLK |
82 | select MACH_MVEBU_ANY | ||
78 | select ORION_IRQCHIP | 83 | select ORION_IRQCHIP |
79 | select ORION_TIMER | 84 | select ORION_TIMER |
80 | select PINCTRL_DOVE | 85 | select PINCTRL_DOVE |
@@ -87,6 +92,7 @@ config MACH_KIRKWOOD | |||
87 | select ARCH_REQUIRE_GPIOLIB | 92 | select ARCH_REQUIRE_GPIOLIB |
88 | select CPU_FEROCEON | 93 | select CPU_FEROCEON |
89 | select KIRKWOOD_CLK | 94 | select KIRKWOOD_CLK |
95 | select MACH_MVEBU_ANY | ||
90 | select ORION_IRQCHIP | 96 | select ORION_IRQCHIP |
91 | select ORION_TIMER | 97 | select ORION_TIMER |
92 | select PCI | 98 | select PCI |
@@ -96,4 +102,11 @@ config MACH_KIRKWOOD | |||
96 | Say 'Y' here if you want your kernel to support boards based | 102 | Say 'Y' here if you want your kernel to support boards based |
97 | on the Marvell Kirkwood device tree. | 103 | on the Marvell Kirkwood device tree. |
98 | 104 | ||
105 | config MACH_NETXBIG | ||
106 | bool "LaCie 2Big and 5Big Network v2" | ||
107 | depends on MACH_KIRKWOOD | ||
108 | help | ||
109 | Say 'Y' here if you want your kernel to support the | ||
110 | LaCie 2Big and 5Big Network v2 | ||
111 | |||
99 | endif | 112 | endif |
diff --git a/arch/arm/mach-mvebu/Makefile b/arch/arm/mach-mvebu/Makefile index 1636cdbef01a..e24136b42765 100644 --- a/arch/arm/mach-mvebu/Makefile +++ b/arch/arm/mach-mvebu/Makefile | |||
@@ -4,13 +4,13 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \ | |||
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 | CFLAGS_pmsu.o := -march=armv7-a |
6 | 6 | ||
7 | obj-y += system-controller.o mvebu-soc-id.o | 7 | obj-$(CONFIG_MACH_MVEBU_ANY) += system-controller.o mvebu-soc-id.o |
8 | 8 | ||
9 | ifeq ($(CONFIG_MACH_MVEBU_V7),y) | 9 | ifeq ($(CONFIG_MACH_MVEBU_V7),y) |
10 | obj-y += cpu-reset.o board-v7.o coherency.o coherency_ll.o pmsu.o pmsu_ll.o | 10 | obj-y += cpu-reset.o board-v7.o coherency.o coherency_ll.o pmsu.o pmsu_ll.o |
11 | obj-$(CONFIG_SMP) += platsmp.o headsmp.o platsmp-a9.o headsmp-a9.o | 11 | obj-$(CONFIG_SMP) += platsmp.o headsmp.o platsmp-a9.o headsmp-a9.o |
12 | obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o | ||
13 | endif | 12 | endif |
14 | 13 | ||
15 | obj-$(CONFIG_MACH_DOVE) += dove.o | 14 | obj-$(CONFIG_MACH_DOVE) += dove.o |
16 | obj-$(CONFIG_MACH_KIRKWOOD) += kirkwood.o kirkwood-pm.o | 15 | obj-$(CONFIG_MACH_KIRKWOOD) += kirkwood.o kirkwood-pm.o |
16 | obj-$(CONFIG_MACH_NETXBIG) += netxbig.o | ||
diff --git a/arch/arm/mach-mvebu/armada-370-xp.h b/arch/arm/mach-mvebu/armada-370-xp.h index c3465f5b1250..84cd90d9b860 100644 --- a/arch/arm/mach-mvebu/armada-370-xp.h +++ b/arch/arm/mach-mvebu/armada-370-xp.h | |||
@@ -24,4 +24,6 @@ void armada_xp_secondary_startup(void); | |||
24 | extern struct smp_operations armada_xp_smp_ops; | 24 | extern struct smp_operations armada_xp_smp_ops; |
25 | #endif | 25 | #endif |
26 | 26 | ||
27 | int armada_370_xp_pmsu_idle_enter(unsigned long deepidle); | ||
28 | |||
27 | #endif /* __MACH_ARMADA_370_XP_H */ | 29 | #endif /* __MACH_ARMADA_370_XP_H */ |
diff --git a/arch/arm/mach-mvebu/board-v7.c b/arch/arm/mach-mvebu/board-v7.c index b2524d689f21..6478626e3ff6 100644 --- a/arch/arm/mach-mvebu/board-v7.c +++ b/arch/arm/mach-mvebu/board-v7.c | |||
@@ -34,14 +34,14 @@ | |||
34 | #include "coherency.h" | 34 | #include "coherency.h" |
35 | #include "mvebu-soc-id.h" | 35 | #include "mvebu-soc-id.h" |
36 | 36 | ||
37 | static void __iomem *scu_base; | ||
38 | |||
37 | /* | 39 | /* |
38 | * Enables the SCU when available. Obviously, this is only useful on | 40 | * Enables the SCU when available. Obviously, this is only useful on |
39 | * Cortex-A based SOCs, not on PJ4B based ones. | 41 | * Cortex-A based SOCs, not on PJ4B based ones. |
40 | */ | 42 | */ |
41 | static void __init mvebu_scu_enable(void) | 43 | static void __init mvebu_scu_enable(void) |
42 | { | 44 | { |
43 | void __iomem *scu_base; | ||
44 | |||
45 | struct device_node *np = | 45 | struct device_node *np = |
46 | of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu"); | 46 | of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu"); |
47 | if (np) { | 47 | if (np) { |
@@ -51,6 +51,11 @@ static void __init mvebu_scu_enable(void) | |||
51 | } | 51 | } |
52 | } | 52 | } |
53 | 53 | ||
54 | void __iomem *mvebu_get_scu_base(void) | ||
55 | { | ||
56 | return scu_base; | ||
57 | } | ||
58 | |||
54 | /* | 59 | /* |
55 | * Early versions of Armada 375 SoC have a bug where the BootROM | 60 | * Early versions of Armada 375 SoC have a bug where the BootROM |
56 | * leaves an external data abort pending. The kernel is hit by this | 61 | * leaves an external data abort pending. The kernel is hit by this |
@@ -125,8 +130,16 @@ static void __init thermal_quirk(void) | |||
125 | { | 130 | { |
126 | struct device_node *np; | 131 | struct device_node *np; |
127 | u32 dev, rev; | 132 | u32 dev, rev; |
133 | int res; | ||
128 | 134 | ||
129 | if (mvebu_get_soc_id(&dev, &rev) == 0 && rev > ARMADA_375_Z1_REV) | 135 | /* |
136 | * The early SoC Z1 revision needs a quirk to be applied in order | ||
137 | * for the thermal controller to work properly. This quirk breaks | ||
138 | * the thermal support if applied on a SoC that doesn't need it, | ||
139 | * so we enforce the SoC revision to be known. | ||
140 | */ | ||
141 | res = mvebu_get_soc_id(&dev, &rev); | ||
142 | if (res < 0 || (res == 0 && rev > ARMADA_375_Z1_REV)) | ||
130 | return; | 143 | return; |
131 | 144 | ||
132 | for_each_compatible_node(np, NULL, "marvell,armada375-thermal") { | 145 | for_each_compatible_node(np, NULL, "marvell,armada375-thermal") { |
@@ -160,7 +173,8 @@ static void __init thermal_quirk(void) | |||
160 | 173 | ||
161 | /* | 174 | /* |
162 | * The thermal controller needs some quirk too, so let's change | 175 | * The thermal controller needs some quirk too, so let's change |
163 | * the compatible string to reflect this. | 176 | * the compatible string to reflect this and allow the driver |
177 | * the take the necessary action. | ||
164 | */ | 178 | */ |
165 | prop = kzalloc(sizeof(*prop), GFP_KERNEL); | 179 | prop = kzalloc(sizeof(*prop), GFP_KERNEL); |
166 | prop->name = kstrdup("compatible", GFP_KERNEL); | 180 | prop->name = kstrdup("compatible", GFP_KERNEL); |
diff --git a/arch/arm/mach-mvebu/board.h b/arch/arm/mach-mvebu/board.h index 9c7bb4386f8b..98e32cc2ef3d 100644 --- a/arch/arm/mach-mvebu/board.h +++ b/arch/arm/mach-mvebu/board.h | |||
@@ -13,4 +13,9 @@ | |||
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_NETXBIG | ||
17 | void netxbig_init(void); | ||
18 | #else | ||
19 | static inline void netxbig_init(void) {}; | ||
20 | #endif | ||
16 | #endif | 21 | #endif |
diff --git a/arch/arm/mach-mvebu/common.h b/arch/arm/mach-mvebu/common.h index b67fb7a10d8b..3ccb40c3bf94 100644 --- a/arch/arm/mach-mvebu/common.h +++ b/arch/arm/mach-mvebu/common.h | |||
@@ -21,7 +21,8 @@ void mvebu_restart(enum reboot_mode mode, const char *cmd); | |||
21 | int mvebu_cpu_reset_deassert(int cpu); | 21 | int mvebu_cpu_reset_deassert(int cpu); |
22 | void mvebu_pmsu_set_cpu_boot_addr(int hw_cpu, void *boot_addr); | 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); | 23 | void mvebu_system_controller_set_cpu_boot_addr(void *boot_addr); |
24 | int mvebu_system_controller_get_soc_id(u32 *dev, u32 *rev); | ||
24 | 25 | ||
25 | void armada_xp_cpu_die(unsigned int cpu); | 26 | void __iomem *mvebu_get_scu_base(void); |
26 | 27 | ||
27 | #endif | 28 | #endif |
diff --git a/arch/arm/mach-mvebu/cpu-reset.c b/arch/arm/mach-mvebu/cpu-reset.c index 4a8f9eebebea..60fb53787004 100644 --- a/arch/arm/mach-mvebu/cpu-reset.c +++ b/arch/arm/mach-mvebu/cpu-reset.c | |||
@@ -67,7 +67,7 @@ static int mvebu_cpu_reset_map(struct device_node *np, int res_idx) | |||
67 | return 0; | 67 | return 0; |
68 | } | 68 | } |
69 | 69 | ||
70 | int __init mvebu_cpu_reset_init(void) | 70 | static int __init mvebu_cpu_reset_init(void) |
71 | { | 71 | { |
72 | struct device_node *np; | 72 | struct device_node *np; |
73 | int res_idx; | 73 | int res_idx; |
diff --git a/arch/arm/mach-mvebu/headsmp-a9.S b/arch/arm/mach-mvebu/headsmp-a9.S index 2c3c7fc65e28..be51c998c0cd 100644 --- a/arch/arm/mach-mvebu/headsmp-a9.S +++ b/arch/arm/mach-mvebu/headsmp-a9.S | |||
@@ -18,21 +18,6 @@ | |||
18 | #include <asm/assembler.h> | 18 | #include <asm/assembler.h> |
19 | 19 | ||
20 | __CPUINIT | 20 | __CPUINIT |
21 | #define CPU_RESUME_ADDR_REG 0xf10182d4 | ||
22 | |||
23 | .global armada_375_smp_cpu1_enable_code_start | ||
24 | .global armada_375_smp_cpu1_enable_code_end | ||
25 | |||
26 | armada_375_smp_cpu1_enable_code_start: | ||
27 | ARM_BE8(setend be) | ||
28 | adr r0, 1f | ||
29 | ldr r0, [r0] | ||
30 | ldr r1, [r0] | ||
31 | ARM_BE8(rev r1, r1) | ||
32 | ret r1 | ||
33 | 1: | ||
34 | .word CPU_RESUME_ADDR_REG | ||
35 | armada_375_smp_cpu1_enable_code_end: | ||
36 | 21 | ||
37 | ENTRY(mvebu_cortex_a9_secondary_startup) | 22 | ENTRY(mvebu_cortex_a9_secondary_startup) |
38 | ARM_BE8(setend be) | 23 | ARM_BE8(setend be) |
diff --git a/arch/arm/mach-mvebu/hotplug.c b/arch/arm/mach-mvebu/hotplug.c deleted file mode 100644 index d95e91047168..000000000000 --- a/arch/arm/mach-mvebu/hotplug.c +++ /dev/null | |||
@@ -1,31 +0,0 @@ | |||
1 | /* | ||
2 | * Symmetric Multi Processing (SMP) support for Armada XP | ||
3 | * | ||
4 | * Copyright (C) 2012 Marvell | ||
5 | * | ||
6 | * Lior Amsalem <alior@marvell.com> | ||
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 | #include <linux/kernel.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/smp.h> | ||
17 | #include <asm/proc-fns.h> | ||
18 | #include "common.h" | ||
19 | |||
20 | /* | ||
21 | * platform-specific code to shutdown a CPU | ||
22 | * | ||
23 | * Called with IRQs disabled | ||
24 | */ | ||
25 | void __ref armada_xp_cpu_die(unsigned int cpu) | ||
26 | { | ||
27 | cpu_do_idle(); | ||
28 | |||
29 | /* We should never return from idle */ | ||
30 | panic("mvebu: cpu %d unexpectedly exit from shutdown\n", cpu); | ||
31 | } | ||
diff --git a/arch/arm/mach-mvebu/kirkwood.c b/arch/arm/mach-mvebu/kirkwood.c index 46f105913c84..6b5310828eb2 100644 --- a/arch/arm/mach-mvebu/kirkwood.c +++ b/arch/arm/mach-mvebu/kirkwood.c | |||
@@ -180,6 +180,9 @@ 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("lacie,netxbig")) | ||
184 | netxbig_init(); | ||
185 | |||
183 | of_platform_populate(NULL, of_default_bus_match_table, auxdata, NULL); | 186 | of_platform_populate(NULL, of_default_bus_match_table, auxdata, NULL); |
184 | } | 187 | } |
185 | 188 | ||
diff --git a/arch/arm/mach-mvebu/mvebu-soc-id.c b/arch/arm/mach-mvebu/mvebu-soc-id.c index d0f35b4d4a23..a99434bcee84 100644 --- a/arch/arm/mach-mvebu/mvebu-soc-id.c +++ b/arch/arm/mach-mvebu/mvebu-soc-id.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/of_address.h> | 25 | #include <linux/of_address.h> |
26 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
27 | #include <linux/sys_soc.h> | 27 | #include <linux/sys_soc.h> |
28 | #include "common.h" | ||
28 | #include "mvebu-soc-id.h" | 29 | #include "mvebu-soc-id.h" |
29 | 30 | ||
30 | #define PCIE_DEV_ID_OFF 0x0 | 31 | #define PCIE_DEV_ID_OFF 0x0 |
@@ -51,10 +52,10 @@ int mvebu_get_soc_id(u32 *dev, u32 *rev) | |||
51 | *rev = soc_rev; | 52 | *rev = soc_rev; |
52 | return 0; | 53 | return 0; |
53 | } else | 54 | } else |
54 | return -1; | 55 | return -ENODEV; |
55 | } | 56 | } |
56 | 57 | ||
57 | static int __init mvebu_soc_id_init(void) | 58 | static int __init get_soc_id_by_pci(void) |
58 | { | 59 | { |
59 | struct device_node *np; | 60 | struct device_node *np; |
60 | int ret = 0; | 61 | int ret = 0; |
@@ -129,6 +130,22 @@ clk_err: | |||
129 | 130 | ||
130 | return ret; | 131 | return ret; |
131 | } | 132 | } |
133 | |||
134 | static int __init mvebu_soc_id_init(void) | ||
135 | { | ||
136 | |||
137 | /* | ||
138 | * First try to get the ID and the revision by the system | ||
139 | * register and use PCI registers only if it is not possible | ||
140 | */ | ||
141 | if (!mvebu_system_controller_get_soc_id(&soc_dev_id, &soc_rev)) { | ||
142 | is_id_valid = true; | ||
143 | pr_info("MVEBU SoC ID=0x%X, Rev=0x%X\n", soc_dev_id, soc_rev); | ||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | return get_soc_id_by_pci(); | ||
148 | } | ||
132 | early_initcall(mvebu_soc_id_init); | 149 | early_initcall(mvebu_soc_id_init); |
133 | 150 | ||
134 | static int __init mvebu_soc_device(void) | 151 | static int __init mvebu_soc_device(void) |
diff --git a/arch/arm/mach-mvebu/netxbig.c b/arch/arm/mach-mvebu/netxbig.c new file mode 100644 index 000000000000..94b11b6585a4 --- /dev/null +++ b/arch/arm/mach-mvebu/netxbig.c | |||
@@ -0,0 +1,191 @@ | |||
1 | /* | ||
2 | * arch/arm/mach-mvbu/board-netxbig.c | ||
3 | * | ||
4 | * LaCie 2Big and 5Big Network v2 board setup | ||
5 | * | ||
6 | * Copyright (C) 2010 Simon Guinot <sguinot@lacie.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | */ | ||
18 | |||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/of.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/platform_data/leds-kirkwood-netxbig.h> | ||
23 | #include "common.h" | ||
24 | |||
25 | /***************************************************************************** | ||
26 | * GPIO extension LEDs | ||
27 | ****************************************************************************/ | ||
28 | |||
29 | /* | ||
30 | * The LEDs are controlled by a CPLD and can be configured through a GPIO | ||
31 | * extension bus: | ||
32 | * | ||
33 | * - address register : bit [0-2] -> GPIO [47-49] | ||
34 | * - data register : bit [0-2] -> GPIO [44-46] | ||
35 | * - enable register : GPIO 29 | ||
36 | */ | ||
37 | |||
38 | static int netxbig_v2_gpio_ext_addr[] = { 47, 48, 49 }; | ||
39 | static int netxbig_v2_gpio_ext_data[] = { 44, 45, 46 }; | ||
40 | |||
41 | static struct netxbig_gpio_ext netxbig_v2_gpio_ext = { | ||
42 | .addr = netxbig_v2_gpio_ext_addr, | ||
43 | .num_addr = ARRAY_SIZE(netxbig_v2_gpio_ext_addr), | ||
44 | .data = netxbig_v2_gpio_ext_data, | ||
45 | .num_data = ARRAY_SIZE(netxbig_v2_gpio_ext_data), | ||
46 | .enable = 29, | ||
47 | }; | ||
48 | |||
49 | /* | ||
50 | * Address register selection: | ||
51 | * | ||
52 | * addr | register | ||
53 | * ---------------------------- | ||
54 | * 0 | front LED | ||
55 | * 1 | front LED brightness | ||
56 | * 2 | SATA LED brightness | ||
57 | * 3 | SATA0 LED | ||
58 | * 4 | SATA1 LED | ||
59 | * 5 | SATA2 LED | ||
60 | * 6 | SATA3 LED | ||
61 | * 7 | SATA4 LED | ||
62 | * | ||
63 | * Data register configuration: | ||
64 | * | ||
65 | * data | LED brightness | ||
66 | * ------------------------------------------------- | ||
67 | * 0 | min (off) | ||
68 | * - | - | ||
69 | * 7 | max | ||
70 | * | ||
71 | * data | front LED mode | ||
72 | * ------------------------------------------------- | ||
73 | * 0 | fix off | ||
74 | * 1 | fix blue on | ||
75 | * 2 | fix red on | ||
76 | * 3 | blink blue on=1 sec and blue off=1 sec | ||
77 | * 4 | blink red on=1 sec and red off=1 sec | ||
78 | * 5 | blink blue on=2.5 sec and red on=0.5 sec | ||
79 | * 6 | blink blue on=1 sec and red on=1 sec | ||
80 | * 7 | blink blue on=0.5 sec and blue off=2.5 sec | ||
81 | * | ||
82 | * data | SATA LED mode | ||
83 | * ------------------------------------------------- | ||
84 | * 0 | fix off | ||
85 | * 1 | SATA activity blink | ||
86 | * 2 | fix red on | ||
87 | * 3 | blink blue on=1 sec and blue off=1 sec | ||
88 | * 4 | blink red on=1 sec and red off=1 sec | ||
89 | * 5 | blink blue on=2.5 sec and red on=0.5 sec | ||
90 | * 6 | blink blue on=1 sec and red on=1 sec | ||
91 | * 7 | fix blue on | ||
92 | */ | ||
93 | |||
94 | static int netxbig_v2_red_mled[NETXBIG_LED_MODE_NUM] = { | ||
95 | [NETXBIG_LED_OFF] = 0, | ||
96 | [NETXBIG_LED_ON] = 2, | ||
97 | [NETXBIG_LED_SATA] = NETXBIG_LED_INVALID_MODE, | ||
98 | [NETXBIG_LED_TIMER1] = 4, | ||
99 | [NETXBIG_LED_TIMER2] = NETXBIG_LED_INVALID_MODE, | ||
100 | }; | ||
101 | |||
102 | static int netxbig_v2_blue_pwr_mled[NETXBIG_LED_MODE_NUM] = { | ||
103 | [NETXBIG_LED_OFF] = 0, | ||
104 | [NETXBIG_LED_ON] = 1, | ||
105 | [NETXBIG_LED_SATA] = NETXBIG_LED_INVALID_MODE, | ||
106 | [NETXBIG_LED_TIMER1] = 3, | ||
107 | [NETXBIG_LED_TIMER2] = 7, | ||
108 | }; | ||
109 | |||
110 | static int netxbig_v2_blue_sata_mled[NETXBIG_LED_MODE_NUM] = { | ||
111 | [NETXBIG_LED_OFF] = 0, | ||
112 | [NETXBIG_LED_ON] = 7, | ||
113 | [NETXBIG_LED_SATA] = 1, | ||
114 | [NETXBIG_LED_TIMER1] = 3, | ||
115 | [NETXBIG_LED_TIMER2] = NETXBIG_LED_INVALID_MODE, | ||
116 | }; | ||
117 | |||
118 | static struct netxbig_led_timer netxbig_v2_led_timer[] = { | ||
119 | [0] = { | ||
120 | .delay_on = 500, | ||
121 | .delay_off = 500, | ||
122 | .mode = NETXBIG_LED_TIMER1, | ||
123 | }, | ||
124 | [1] = { | ||
125 | .delay_on = 500, | ||
126 | .delay_off = 1000, | ||
127 | .mode = NETXBIG_LED_TIMER2, | ||
128 | }, | ||
129 | }; | ||
130 | |||
131 | #define NETXBIG_LED(_name, maddr, mval, baddr) \ | ||
132 | { .name = _name, \ | ||
133 | .mode_addr = maddr, \ | ||
134 | .mode_val = mval, \ | ||
135 | .bright_addr = baddr } | ||
136 | |||
137 | static struct netxbig_led net2big_v2_leds_ctrl[] = { | ||
138 | NETXBIG_LED("net2big-v2:blue:power", 0, netxbig_v2_blue_pwr_mled, 1), | ||
139 | NETXBIG_LED("net2big-v2:red:power", 0, netxbig_v2_red_mled, 1), | ||
140 | NETXBIG_LED("net2big-v2:blue:sata0", 3, netxbig_v2_blue_sata_mled, 2), | ||
141 | NETXBIG_LED("net2big-v2:red:sata0", 3, netxbig_v2_red_mled, 2), | ||
142 | NETXBIG_LED("net2big-v2:blue:sata1", 4, netxbig_v2_blue_sata_mled, 2), | ||
143 | NETXBIG_LED("net2big-v2:red:sata1", 4, netxbig_v2_red_mled, 2), | ||
144 | }; | ||
145 | |||
146 | static struct netxbig_led_platform_data net2big_v2_leds_data = { | ||
147 | .gpio_ext = &netxbig_v2_gpio_ext, | ||
148 | .timer = netxbig_v2_led_timer, | ||
149 | .num_timer = ARRAY_SIZE(netxbig_v2_led_timer), | ||
150 | .leds = net2big_v2_leds_ctrl, | ||
151 | .num_leds = ARRAY_SIZE(net2big_v2_leds_ctrl), | ||
152 | }; | ||
153 | |||
154 | static struct netxbig_led net5big_v2_leds_ctrl[] = { | ||
155 | NETXBIG_LED("net5big-v2:blue:power", 0, netxbig_v2_blue_pwr_mled, 1), | ||
156 | NETXBIG_LED("net5big-v2:red:power", 0, netxbig_v2_red_mled, 1), | ||
157 | NETXBIG_LED("net5big-v2:blue:sata0", 3, netxbig_v2_blue_sata_mled, 2), | ||
158 | NETXBIG_LED("net5big-v2:red:sata0", 3, netxbig_v2_red_mled, 2), | ||
159 | NETXBIG_LED("net5big-v2:blue:sata1", 4, netxbig_v2_blue_sata_mled, 2), | ||
160 | NETXBIG_LED("net5big-v2:red:sata1", 4, netxbig_v2_red_mled, 2), | ||
161 | NETXBIG_LED("net5big-v2:blue:sata2", 5, netxbig_v2_blue_sata_mled, 2), | ||
162 | NETXBIG_LED("net5big-v2:red:sata2", 5, netxbig_v2_red_mled, 2), | ||
163 | NETXBIG_LED("net5big-v2:blue:sata3", 6, netxbig_v2_blue_sata_mled, 2), | ||
164 | NETXBIG_LED("net5big-v2:red:sata3", 6, netxbig_v2_red_mled, 2), | ||
165 | NETXBIG_LED("net5big-v2:blue:sata4", 7, netxbig_v2_blue_sata_mled, 2), | ||
166 | NETXBIG_LED("net5big-v2:red:sata4", 7, netxbig_v2_red_mled, 2), | ||
167 | }; | ||
168 | |||
169 | static struct netxbig_led_platform_data net5big_v2_leds_data = { | ||
170 | .gpio_ext = &netxbig_v2_gpio_ext, | ||
171 | .timer = netxbig_v2_led_timer, | ||
172 | .num_timer = ARRAY_SIZE(netxbig_v2_led_timer), | ||
173 | .leds = net5big_v2_leds_ctrl, | ||
174 | .num_leds = ARRAY_SIZE(net5big_v2_leds_ctrl), | ||
175 | }; | ||
176 | |||
177 | static struct platform_device netxbig_v2_leds = { | ||
178 | .name = "leds-netxbig", | ||
179 | .id = -1, | ||
180 | .dev = { | ||
181 | .platform_data = &net2big_v2_leds_data, | ||
182 | }, | ||
183 | }; | ||
184 | |||
185 | void __init netxbig_init(void) | ||
186 | { | ||
187 | |||
188 | if (of_machine_is_compatible("lacie,net5big_v2")) | ||
189 | netxbig_v2_leds.dev.platform_data = &net5big_v2_leds_data; | ||
190 | platform_device_register(&netxbig_v2_leds); | ||
191 | } | ||
diff --git a/arch/arm/mach-mvebu/platsmp-a9.c b/arch/arm/mach-mvebu/platsmp-a9.c index 96c2c59e34b6..47a71a924b96 100644 --- a/arch/arm/mach-mvebu/platsmp-a9.c +++ b/arch/arm/mach-mvebu/platsmp-a9.c | |||
@@ -20,33 +20,8 @@ | |||
20 | #include <asm/smp_scu.h> | 20 | #include <asm/smp_scu.h> |
21 | #include <asm/smp_plat.h> | 21 | #include <asm/smp_plat.h> |
22 | #include "common.h" | 22 | #include "common.h" |
23 | #include "mvebu-soc-id.h" | ||
24 | #include "pmsu.h" | 23 | #include "pmsu.h" |
25 | 24 | ||
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); | 25 | extern void mvebu_cortex_a9_secondary_startup(void); |
51 | 26 | ||
52 | static int __cpuinit mvebu_cortex_a9_boot_secondary(unsigned int cpu, | 27 | static int __cpuinit mvebu_cortex_a9_boot_secondary(unsigned int cpu, |
@@ -63,21 +38,10 @@ static int __cpuinit mvebu_cortex_a9_boot_secondary(unsigned int cpu, | |||
63 | * address. | 38 | * address. |
64 | */ | 39 | */ |
65 | hw_cpu = cpu_logical_map(cpu); | 40 | hw_cpu = cpu_logical_map(cpu); |
66 | 41 | if (of_machine_is_compatible("marvell,armada375")) | |
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); | 42 | mvebu_system_controller_set_cpu_boot_addr(mvebu_cortex_a9_secondary_startup); |
75 | } | 43 | else |
76 | else { | 44 | mvebu_pmsu_set_cpu_boot_addr(hw_cpu, mvebu_cortex_a9_secondary_startup); |
77 | mvebu_pmsu_set_cpu_boot_addr(hw_cpu, | ||
78 | mvebu_cortex_a9_secondary_startup); | ||
79 | } | ||
80 | |||
81 | smp_wmb(); | 45 | smp_wmb(); |
82 | ret = mvebu_cpu_reset_deassert(hw_cpu); | 46 | ret = mvebu_cpu_reset_deassert(hw_cpu); |
83 | if (ret) { | 47 | if (ret) { |
@@ -91,9 +55,6 @@ static int __cpuinit mvebu_cortex_a9_boot_secondary(unsigned int cpu, | |||
91 | 55 | ||
92 | static struct smp_operations mvebu_cortex_a9_smp_ops __initdata = { | 56 | static struct smp_operations mvebu_cortex_a9_smp_ops __initdata = { |
93 | .smp_boot_secondary = mvebu_cortex_a9_boot_secondary, | 57 | .smp_boot_secondary = mvebu_cortex_a9_boot_secondary, |
94 | #ifdef CONFIG_HOTPLUG_CPU | ||
95 | .cpu_die = armada_xp_cpu_die, | ||
96 | #endif | ||
97 | }; | 58 | }; |
98 | 59 | ||
99 | CPU_METHOD_OF_DECLARE(mvebu_armada_375_smp, "marvell,armada-375-smp", | 60 | CPU_METHOD_OF_DECLARE(mvebu_armada_375_smp, "marvell,armada-375-smp", |
diff --git a/arch/arm/mach-mvebu/platsmp.c b/arch/arm/mach-mvebu/platsmp.c index 88b976b31719..895dc373c8a1 100644 --- a/arch/arm/mach-mvebu/platsmp.c +++ b/arch/arm/mach-mvebu/platsmp.c | |||
@@ -67,6 +67,7 @@ static void __init set_secondary_cpus_clock(void) | |||
67 | if (!cpu_clk) | 67 | if (!cpu_clk) |
68 | return; | 68 | return; |
69 | clk_set_rate(cpu_clk, rate); | 69 | clk_set_rate(cpu_clk, rate); |
70 | clk_prepare_enable(cpu_clk); | ||
70 | } | 71 | } |
71 | } | 72 | } |
72 | 73 | ||
@@ -78,6 +79,17 @@ static int armada_xp_boot_secondary(unsigned int cpu, struct task_struct *idle) | |||
78 | 79 | ||
79 | hw_cpu = cpu_logical_map(cpu); | 80 | hw_cpu = cpu_logical_map(cpu); |
80 | mvebu_pmsu_set_cpu_boot_addr(hw_cpu, armada_xp_secondary_startup); | 81 | mvebu_pmsu_set_cpu_boot_addr(hw_cpu, armada_xp_secondary_startup); |
82 | |||
83 | /* | ||
84 | * This is needed to wake up CPUs in the offline state after | ||
85 | * using CPU hotplug. | ||
86 | */ | ||
87 | arch_send_wakeup_ipi_mask(cpumask_of(cpu)); | ||
88 | |||
89 | /* | ||
90 | * This is needed to take secondary CPUs out of reset on the | ||
91 | * initial boot. | ||
92 | */ | ||
81 | ret = mvebu_cpu_reset_deassert(hw_cpu); | 93 | ret = mvebu_cpu_reset_deassert(hw_cpu); |
82 | if (ret) { | 94 | if (ret) { |
83 | pr_warn("unable to boot CPU: %d\n", ret); | 95 | pr_warn("unable to boot CPU: %d\n", ret); |
@@ -87,6 +99,19 @@ static int armada_xp_boot_secondary(unsigned int cpu, struct task_struct *idle) | |||
87 | return 0; | 99 | return 0; |
88 | } | 100 | } |
89 | 101 | ||
102 | /* | ||
103 | * When a CPU is brought back online, either through CPU hotplug, or | ||
104 | * because of the boot of a kexec'ed kernel, the PMSU configuration | ||
105 | * for this CPU might be in the deep idle state, preventing this CPU | ||
106 | * from receiving interrupts. Here, we therefore take out the current | ||
107 | * CPU from this state, which was entered by armada_xp_cpu_die() | ||
108 | * below. | ||
109 | */ | ||
110 | static void armada_xp_secondary_init(unsigned int cpu) | ||
111 | { | ||
112 | mvebu_v7_pmsu_idle_exit(); | ||
113 | } | ||
114 | |||
90 | static void __init armada_xp_smp_init_cpus(void) | 115 | static void __init armada_xp_smp_init_cpus(void) |
91 | { | 116 | { |
92 | unsigned int ncores = num_possible_cpus(); | 117 | unsigned int ncores = num_possible_cpus(); |
@@ -122,12 +147,36 @@ static void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus) | |||
122 | panic("The address for the BootROM is incorrect"); | 147 | panic("The address for the BootROM is incorrect"); |
123 | } | 148 | } |
124 | 149 | ||
150 | #ifdef CONFIG_HOTPLUG_CPU | ||
151 | static void armada_xp_cpu_die(unsigned int cpu) | ||
152 | { | ||
153 | /* | ||
154 | * CPU hotplug is implemented by putting offline CPUs into the | ||
155 | * deep idle sleep state. | ||
156 | */ | ||
157 | armada_370_xp_pmsu_idle_enter(true); | ||
158 | } | ||
159 | |||
160 | /* | ||
161 | * We need a dummy function, so that platform_can_cpu_hotplug() knows | ||
162 | * we support CPU hotplug. However, the function does not need to do | ||
163 | * anything, because CPUs going offline can enter the deep idle state | ||
164 | * by themselves, without any help from a still alive CPU. | ||
165 | */ | ||
166 | static int armada_xp_cpu_kill(unsigned int cpu) | ||
167 | { | ||
168 | return 1; | ||
169 | } | ||
170 | #endif | ||
171 | |||
125 | struct smp_operations armada_xp_smp_ops __initdata = { | 172 | struct smp_operations armada_xp_smp_ops __initdata = { |
126 | .smp_init_cpus = armada_xp_smp_init_cpus, | 173 | .smp_init_cpus = armada_xp_smp_init_cpus, |
127 | .smp_prepare_cpus = armada_xp_smp_prepare_cpus, | 174 | .smp_prepare_cpus = armada_xp_smp_prepare_cpus, |
128 | .smp_boot_secondary = armada_xp_boot_secondary, | 175 | .smp_boot_secondary = armada_xp_boot_secondary, |
176 | .smp_secondary_init = armada_xp_secondary_init, | ||
129 | #ifdef CONFIG_HOTPLUG_CPU | 177 | #ifdef CONFIG_HOTPLUG_CPU |
130 | .cpu_die = armada_xp_cpu_die, | 178 | .cpu_die = armada_xp_cpu_die, |
179 | .cpu_kill = armada_xp_cpu_kill, | ||
131 | #endif | 180 | #endif |
132 | }; | 181 | }; |
133 | 182 | ||
diff --git a/arch/arm/mach-mvebu/pmsu.c b/arch/arm/mach-mvebu/pmsu.c index 25aa8237d668..8a70a51533fd 100644 --- a/arch/arm/mach-mvebu/pmsu.c +++ b/arch/arm/mach-mvebu/pmsu.c | |||
@@ -18,22 +18,29 @@ | |||
18 | 18 | ||
19 | #define pr_fmt(fmt) "mvebu-pmsu: " fmt | 19 | #define pr_fmt(fmt) "mvebu-pmsu: " fmt |
20 | 20 | ||
21 | #include <linux/clk.h> | ||
21 | #include <linux/cpu_pm.h> | 22 | #include <linux/cpu_pm.h> |
22 | #include <linux/kernel.h> | 23 | #include <linux/delay.h> |
23 | #include <linux/init.h> | 24 | #include <linux/init.h> |
24 | #include <linux/of_address.h> | ||
25 | #include <linux/io.h> | 25 | #include <linux/io.h> |
26 | #include <linux/kernel.h> | ||
27 | #include <linux/mbus.h> | ||
28 | #include <linux/of_address.h> | ||
29 | #include <linux/of_device.h> | ||
26 | #include <linux/platform_device.h> | 30 | #include <linux/platform_device.h> |
27 | #include <linux/smp.h> | 31 | #include <linux/pm_opp.h> |
28 | #include <linux/resource.h> | 32 | #include <linux/resource.h> |
33 | #include <linux/slab.h> | ||
34 | #include <linux/smp.h> | ||
29 | #include <asm/cacheflush.h> | 35 | #include <asm/cacheflush.h> |
30 | #include <asm/cp15.h> | 36 | #include <asm/cp15.h> |
37 | #include <asm/smp_scu.h> | ||
31 | #include <asm/smp_plat.h> | 38 | #include <asm/smp_plat.h> |
32 | #include <asm/suspend.h> | 39 | #include <asm/suspend.h> |
33 | #include <asm/tlbflush.h> | 40 | #include <asm/tlbflush.h> |
34 | #include "common.h" | 41 | #include "common.h" |
42 | #include "armada-370-xp.h" | ||
35 | 43 | ||
36 | static void __iomem *pmsu_mp_base; | ||
37 | 44 | ||
38 | #define PMSU_BASE_OFFSET 0x100 | 45 | #define PMSU_BASE_OFFSET 0x100 |
39 | #define PMSU_REG_SIZE 0x1000 | 46 | #define PMSU_REG_SIZE 0x1000 |
@@ -57,20 +64,45 @@ static void __iomem *pmsu_mp_base; | |||
57 | #define PMSU_STATUS_AND_MASK_IRQ_MASK BIT(24) | 64 | #define PMSU_STATUS_AND_MASK_IRQ_MASK BIT(24) |
58 | #define PMSU_STATUS_AND_MASK_FIQ_MASK BIT(25) | 65 | #define PMSU_STATUS_AND_MASK_FIQ_MASK BIT(25) |
59 | 66 | ||
67 | #define PMSU_EVENT_STATUS_AND_MASK(cpu) ((cpu * 0x100) + 0x120) | ||
68 | #define PMSU_EVENT_STATUS_AND_MASK_DFS_DONE BIT(1) | ||
69 | #define PMSU_EVENT_STATUS_AND_MASK_DFS_DONE_MASK BIT(17) | ||
70 | |||
60 | #define PMSU_BOOT_ADDR_REDIRECT_OFFSET(cpu) ((cpu * 0x100) + 0x124) | 71 | #define PMSU_BOOT_ADDR_REDIRECT_OFFSET(cpu) ((cpu * 0x100) + 0x124) |
61 | 72 | ||
62 | /* PMSU fabric registers */ | 73 | /* PMSU fabric registers */ |
63 | #define L2C_NFABRIC_PM_CTL 0x4 | 74 | #define L2C_NFABRIC_PM_CTL 0x4 |
64 | #define L2C_NFABRIC_PM_CTL_PWR_DOWN BIT(20) | 75 | #define L2C_NFABRIC_PM_CTL_PWR_DOWN BIT(20) |
65 | 76 | ||
77 | /* PMSU delay registers */ | ||
78 | #define PMSU_POWERDOWN_DELAY 0xF04 | ||
79 | #define PMSU_POWERDOWN_DELAY_PMU BIT(1) | ||
80 | #define PMSU_POWERDOWN_DELAY_MASK 0xFFFE | ||
81 | #define PMSU_DFLT_ARMADA38X_DELAY 0x64 | ||
82 | |||
83 | /* CA9 MPcore SoC Control registers */ | ||
84 | |||
85 | #define MPCORE_RESET_CTL 0x64 | ||
86 | #define MPCORE_RESET_CTL_L2 BIT(0) | ||
87 | #define MPCORE_RESET_CTL_DEBUG BIT(16) | ||
88 | |||
89 | #define SRAM_PHYS_BASE 0xFFFF0000 | ||
90 | #define BOOTROM_BASE 0xFFF00000 | ||
91 | #define BOOTROM_SIZE 0x100000 | ||
92 | |||
93 | #define ARMADA_370_CRYPT0_ENG_TARGET 0x9 | ||
94 | #define ARMADA_370_CRYPT0_ENG_ATTR 0x1 | ||
95 | |||
66 | extern void ll_disable_coherency(void); | 96 | extern void ll_disable_coherency(void); |
67 | extern void ll_enable_coherency(void); | 97 | extern void ll_enable_coherency(void); |
68 | 98 | ||
69 | extern void armada_370_xp_cpu_resume(void); | 99 | extern void armada_370_xp_cpu_resume(void); |
100 | extern void armada_38x_cpu_resume(void); | ||
70 | 101 | ||
71 | static struct platform_device armada_xp_cpuidle_device = { | 102 | static phys_addr_t pmsu_mp_phys_base; |
72 | .name = "cpuidle-armada-370-xp", | 103 | static void __iomem *pmsu_mp_base; |
73 | }; | 104 | |
105 | static void *mvebu_cpu_resume; | ||
74 | 106 | ||
75 | static struct of_device_id of_pmsu_table[] = { | 107 | static struct of_device_id of_pmsu_table[] = { |
76 | { .compatible = "marvell,armada-370-pmsu", }, | 108 | { .compatible = "marvell,armada-370-pmsu", }, |
@@ -85,7 +117,49 @@ void mvebu_pmsu_set_cpu_boot_addr(int hw_cpu, void *boot_addr) | |||
85 | PMSU_BOOT_ADDR_REDIRECT_OFFSET(hw_cpu)); | 117 | PMSU_BOOT_ADDR_REDIRECT_OFFSET(hw_cpu)); |
86 | } | 118 | } |
87 | 119 | ||
88 | static int __init armada_370_xp_pmsu_init(void) | 120 | extern unsigned char mvebu_boot_wa_start; |
121 | extern unsigned char mvebu_boot_wa_end; | ||
122 | |||
123 | /* | ||
124 | * This function sets up the boot address workaround needed for SMP | ||
125 | * boot on Armada 375 Z1 and cpuidle on Armada 370. It unmaps the | ||
126 | * BootROM Mbus window, and instead remaps a crypto SRAM into which a | ||
127 | * custom piece of code is copied to replace the problematic BootROM. | ||
128 | */ | ||
129 | int mvebu_setup_boot_addr_wa(unsigned int crypto_eng_target, | ||
130 | unsigned int crypto_eng_attribute, | ||
131 | phys_addr_t resume_addr_reg) | ||
132 | { | ||
133 | void __iomem *sram_virt_base; | ||
134 | u32 code_len = &mvebu_boot_wa_end - &mvebu_boot_wa_start; | ||
135 | |||
136 | mvebu_mbus_del_window(BOOTROM_BASE, BOOTROM_SIZE); | ||
137 | mvebu_mbus_add_window_by_id(crypto_eng_target, crypto_eng_attribute, | ||
138 | SRAM_PHYS_BASE, SZ_64K); | ||
139 | |||
140 | sram_virt_base = ioremap(SRAM_PHYS_BASE, SZ_64K); | ||
141 | if (!sram_virt_base) { | ||
142 | pr_err("Unable to map SRAM to setup the boot address WA\n"); | ||
143 | return -ENOMEM; | ||
144 | } | ||
145 | |||
146 | memcpy(sram_virt_base, &mvebu_boot_wa_start, code_len); | ||
147 | |||
148 | /* | ||
149 | * The last word of the code copied in SRAM must contain the | ||
150 | * physical base address of the PMSU register. We | ||
151 | * intentionally store this address in the native endianness | ||
152 | * of the system. | ||
153 | */ | ||
154 | __raw_writel((unsigned long)resume_addr_reg, | ||
155 | sram_virt_base + code_len - 4); | ||
156 | |||
157 | iounmap(sram_virt_base); | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static int __init mvebu_v7_pmsu_init(void) | ||
89 | { | 163 | { |
90 | struct device_node *np; | 164 | struct device_node *np; |
91 | struct resource res; | 165 | struct resource res; |
@@ -116,6 +190,8 @@ static int __init armada_370_xp_pmsu_init(void) | |||
116 | goto out; | 190 | goto out; |
117 | } | 191 | } |
118 | 192 | ||
193 | pmsu_mp_phys_base = res.start; | ||
194 | |||
119 | pmsu_mp_base = ioremap(res.start, resource_size(&res)); | 195 | pmsu_mp_base = ioremap(res.start, resource_size(&res)); |
120 | if (!pmsu_mp_base) { | 196 | if (!pmsu_mp_base) { |
121 | pr_err("unable to map registers\n"); | 197 | pr_err("unable to map registers\n"); |
@@ -129,7 +205,7 @@ static int __init armada_370_xp_pmsu_init(void) | |||
129 | return ret; | 205 | return ret; |
130 | } | 206 | } |
131 | 207 | ||
132 | static void armada_370_xp_pmsu_enable_l2_powerdown_onidle(void) | 208 | static void mvebu_v7_pmsu_enable_l2_powerdown_onidle(void) |
133 | { | 209 | { |
134 | u32 reg; | 210 | u32 reg; |
135 | 211 | ||
@@ -142,14 +218,20 @@ static void armada_370_xp_pmsu_enable_l2_powerdown_onidle(void) | |||
142 | writel(reg, pmsu_mp_base + L2C_NFABRIC_PM_CTL); | 218 | writel(reg, pmsu_mp_base + L2C_NFABRIC_PM_CTL); |
143 | } | 219 | } |
144 | 220 | ||
221 | enum pmsu_idle_prepare_flags { | ||
222 | PMSU_PREPARE_NORMAL = 0, | ||
223 | PMSU_PREPARE_DEEP_IDLE = BIT(0), | ||
224 | PMSU_PREPARE_SNOOP_DISABLE = BIT(1), | ||
225 | }; | ||
226 | |||
145 | /* No locking is needed because we only access per-CPU registers */ | 227 | /* No locking is needed because we only access per-CPU registers */ |
146 | void armada_370_xp_pmsu_idle_prepare(bool deepidle) | 228 | static int mvebu_v7_pmsu_idle_prepare(unsigned long flags) |
147 | { | 229 | { |
148 | unsigned int hw_cpu = cpu_logical_map(smp_processor_id()); | 230 | unsigned int hw_cpu = cpu_logical_map(smp_processor_id()); |
149 | u32 reg; | 231 | u32 reg; |
150 | 232 | ||
151 | if (pmsu_mp_base == NULL) | 233 | if (pmsu_mp_base == NULL) |
152 | return; | 234 | return -EINVAL; |
153 | 235 | ||
154 | /* | 236 | /* |
155 | * Adjust the PMSU configuration to wait for WFI signal, enable | 237 | * Adjust the PMSU configuration to wait for WFI signal, enable |
@@ -167,22 +249,34 @@ void armada_370_xp_pmsu_idle_prepare(bool deepidle) | |||
167 | 249 | ||
168 | reg = readl(pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu)); | 250 | reg = readl(pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu)); |
169 | /* ask HW to power down the L2 Cache if needed */ | 251 | /* ask HW to power down the L2 Cache if needed */ |
170 | if (deepidle) | 252 | if (flags & PMSU_PREPARE_DEEP_IDLE) |
171 | reg |= PMSU_CONTROL_AND_CONFIG_L2_PWDDN; | 253 | reg |= PMSU_CONTROL_AND_CONFIG_L2_PWDDN; |
172 | 254 | ||
173 | /* request power down */ | 255 | /* request power down */ |
174 | reg |= PMSU_CONTROL_AND_CONFIG_PWDDN_REQ; | 256 | reg |= PMSU_CONTROL_AND_CONFIG_PWDDN_REQ; |
175 | writel(reg, pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu)); | 257 | writel(reg, pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu)); |
176 | 258 | ||
177 | /* Disable snoop disable by HW - SW is taking care of it */ | 259 | if (flags & PMSU_PREPARE_SNOOP_DISABLE) { |
178 | reg = readl(pmsu_mp_base + PMSU_CPU_POWER_DOWN_CONTROL(hw_cpu)); | 260 | /* Disable snoop disable by HW - SW is taking care of it */ |
179 | reg |= PMSU_CPU_POWER_DOWN_DIS_SNP_Q_SKIP; | 261 | reg = readl(pmsu_mp_base + PMSU_CPU_POWER_DOWN_CONTROL(hw_cpu)); |
180 | writel(reg, pmsu_mp_base + PMSU_CPU_POWER_DOWN_CONTROL(hw_cpu)); | 262 | reg |= PMSU_CPU_POWER_DOWN_DIS_SNP_Q_SKIP; |
263 | writel(reg, pmsu_mp_base + PMSU_CPU_POWER_DOWN_CONTROL(hw_cpu)); | ||
264 | } | ||
265 | |||
266 | return 0; | ||
181 | } | 267 | } |
182 | 268 | ||
183 | static noinline int do_armada_370_xp_cpu_suspend(unsigned long deepidle) | 269 | int armada_370_xp_pmsu_idle_enter(unsigned long deepidle) |
184 | { | 270 | { |
185 | armada_370_xp_pmsu_idle_prepare(deepidle); | 271 | unsigned long flags = PMSU_PREPARE_SNOOP_DISABLE; |
272 | int ret; | ||
273 | |||
274 | if (deepidle) | ||
275 | flags |= PMSU_PREPARE_DEEP_IDLE; | ||
276 | |||
277 | ret = mvebu_v7_pmsu_idle_prepare(flags); | ||
278 | if (ret) | ||
279 | return ret; | ||
186 | 280 | ||
187 | v7_exit_coherency_flush(all); | 281 | v7_exit_coherency_flush(all); |
188 | 282 | ||
@@ -208,25 +302,50 @@ static noinline int do_armada_370_xp_cpu_suspend(unsigned long deepidle) | |||
208 | "isb " | 302 | "isb " |
209 | : : : "r0"); | 303 | : : : "r0"); |
210 | 304 | ||
211 | pr_warn("Failed to suspend the system\n"); | 305 | pr_debug("Failed to suspend the system\n"); |
212 | 306 | ||
213 | return 0; | 307 | return 0; |
214 | } | 308 | } |
215 | 309 | ||
216 | static int armada_370_xp_cpu_suspend(unsigned long deepidle) | 310 | static int armada_370_xp_cpu_suspend(unsigned long deepidle) |
217 | { | 311 | { |
218 | return cpu_suspend(deepidle, do_armada_370_xp_cpu_suspend); | 312 | return cpu_suspend(deepidle, armada_370_xp_pmsu_idle_enter); |
313 | } | ||
314 | |||
315 | static int armada_38x_do_cpu_suspend(unsigned long deepidle) | ||
316 | { | ||
317 | unsigned long flags = 0; | ||
318 | |||
319 | if (deepidle) | ||
320 | flags |= PMSU_PREPARE_DEEP_IDLE; | ||
321 | |||
322 | mvebu_v7_pmsu_idle_prepare(flags); | ||
323 | /* | ||
324 | * Already flushed cache, but do it again as the outer cache | ||
325 | * functions dirty the cache with spinlocks | ||
326 | */ | ||
327 | v7_exit_coherency_flush(louis); | ||
328 | |||
329 | scu_power_mode(mvebu_get_scu_base(), SCU_PM_POWEROFF); | ||
330 | |||
331 | cpu_do_idle(); | ||
332 | |||
333 | return 1; | ||
334 | } | ||
335 | |||
336 | static int armada_38x_cpu_suspend(unsigned long deepidle) | ||
337 | { | ||
338 | return cpu_suspend(false, armada_38x_do_cpu_suspend); | ||
219 | } | 339 | } |
220 | 340 | ||
221 | /* No locking is needed because we only access per-CPU registers */ | 341 | /* No locking is needed because we only access per-CPU registers */ |
222 | static noinline void armada_370_xp_pmsu_idle_restore(void) | 342 | void mvebu_v7_pmsu_idle_exit(void) |
223 | { | 343 | { |
224 | unsigned int hw_cpu = cpu_logical_map(smp_processor_id()); | 344 | unsigned int hw_cpu = cpu_logical_map(smp_processor_id()); |
225 | u32 reg; | 345 | u32 reg; |
226 | 346 | ||
227 | if (pmsu_mp_base == NULL) | 347 | if (pmsu_mp_base == NULL) |
228 | return; | 348 | return; |
229 | |||
230 | /* cancel ask HW to power down the L2 Cache if possible */ | 349 | /* cancel ask HW to power down the L2 Cache if possible */ |
231 | reg = readl(pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu)); | 350 | reg = readl(pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu)); |
232 | reg &= ~PMSU_CONTROL_AND_CONFIG_L2_PWDDN; | 351 | reg &= ~PMSU_CONTROL_AND_CONFIG_L2_PWDDN; |
@@ -241,53 +360,292 @@ static noinline void armada_370_xp_pmsu_idle_restore(void) | |||
241 | writel(reg, pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu)); | 360 | writel(reg, pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu)); |
242 | } | 361 | } |
243 | 362 | ||
244 | static int armada_370_xp_cpu_pm_notify(struct notifier_block *self, | 363 | static int mvebu_v7_cpu_pm_notify(struct notifier_block *self, |
245 | unsigned long action, void *hcpu) | 364 | unsigned long action, void *hcpu) |
246 | { | 365 | { |
247 | if (action == CPU_PM_ENTER) { | 366 | if (action == CPU_PM_ENTER) { |
248 | unsigned int hw_cpu = cpu_logical_map(smp_processor_id()); | 367 | unsigned int hw_cpu = cpu_logical_map(smp_processor_id()); |
249 | mvebu_pmsu_set_cpu_boot_addr(hw_cpu, armada_370_xp_cpu_resume); | 368 | mvebu_pmsu_set_cpu_boot_addr(hw_cpu, mvebu_cpu_resume); |
250 | } else if (action == CPU_PM_EXIT) { | 369 | } else if (action == CPU_PM_EXIT) { |
251 | armada_370_xp_pmsu_idle_restore(); | 370 | mvebu_v7_pmsu_idle_exit(); |
252 | } | 371 | } |
253 | 372 | ||
254 | return NOTIFY_OK; | 373 | return NOTIFY_OK; |
255 | } | 374 | } |
256 | 375 | ||
257 | static struct notifier_block armada_370_xp_cpu_pm_notifier = { | 376 | static struct notifier_block mvebu_v7_cpu_pm_notifier = { |
258 | .notifier_call = armada_370_xp_cpu_pm_notify, | 377 | .notifier_call = mvebu_v7_cpu_pm_notify, |
259 | }; | 378 | }; |
260 | 379 | ||
261 | int __init armada_370_xp_cpu_pm_init(void) | 380 | static struct platform_device mvebu_v7_cpuidle_device; |
381 | |||
382 | static __init int armada_370_cpuidle_init(void) | ||
262 | { | 383 | { |
263 | struct device_node *np; | 384 | struct device_node *np; |
385 | phys_addr_t redirect_reg; | ||
386 | |||
387 | np = of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric"); | ||
388 | if (!np) | ||
389 | return -ENODEV; | ||
390 | of_node_put(np); | ||
264 | 391 | ||
265 | /* | 392 | /* |
266 | * Check that all the requirements are available to enable | 393 | * On Armada 370, there is "a slow exit process from the deep |
267 | * cpuidle. So far, it is only supported on Armada XP, cpuidle | 394 | * idle state due to heavy L1/L2 cache cleanup operations |
268 | * needs the coherency fabric and the PMSU enabled | 395 | * performed by the BootROM software". To avoid this, we |
396 | * replace the restart code of the bootrom by a a simple jump | ||
397 | * to the boot address. Then the code located at this boot | ||
398 | * address will take care of the initialization. | ||
269 | */ | 399 | */ |
400 | redirect_reg = pmsu_mp_phys_base + PMSU_BOOT_ADDR_REDIRECT_OFFSET(0); | ||
401 | mvebu_setup_boot_addr_wa(ARMADA_370_CRYPT0_ENG_TARGET, | ||
402 | ARMADA_370_CRYPT0_ENG_ATTR, | ||
403 | redirect_reg); | ||
270 | 404 | ||
271 | if (!of_machine_is_compatible("marvell,armadaxp")) | 405 | mvebu_cpu_resume = armada_370_xp_cpu_resume; |
272 | return 0; | 406 | mvebu_v7_cpuidle_device.dev.platform_data = armada_370_xp_cpu_suspend; |
407 | mvebu_v7_cpuidle_device.name = "cpuidle-armada-370"; | ||
408 | |||
409 | return 0; | ||
410 | } | ||
411 | |||
412 | static __init int armada_38x_cpuidle_init(void) | ||
413 | { | ||
414 | struct device_node *np; | ||
415 | void __iomem *mpsoc_base; | ||
416 | u32 reg; | ||
417 | |||
418 | np = of_find_compatible_node(NULL, NULL, | ||
419 | "marvell,armada-380-coherency-fabric"); | ||
420 | if (!np) | ||
421 | return -ENODEV; | ||
422 | of_node_put(np); | ||
423 | |||
424 | np = of_find_compatible_node(NULL, NULL, | ||
425 | "marvell,armada-380-mpcore-soc-ctrl"); | ||
426 | if (!np) | ||
427 | return -ENODEV; | ||
428 | mpsoc_base = of_iomap(np, 0); | ||
429 | BUG_ON(!mpsoc_base); | ||
430 | of_node_put(np); | ||
431 | |||
432 | /* Set up reset mask when powering down the cpus */ | ||
433 | reg = readl(mpsoc_base + MPCORE_RESET_CTL); | ||
434 | reg |= MPCORE_RESET_CTL_L2; | ||
435 | reg |= MPCORE_RESET_CTL_DEBUG; | ||
436 | writel(reg, mpsoc_base + MPCORE_RESET_CTL); | ||
437 | iounmap(mpsoc_base); | ||
438 | |||
439 | /* Set up delay */ | ||
440 | reg = readl(pmsu_mp_base + PMSU_POWERDOWN_DELAY); | ||
441 | reg &= ~PMSU_POWERDOWN_DELAY_MASK; | ||
442 | reg |= PMSU_DFLT_ARMADA38X_DELAY; | ||
443 | reg |= PMSU_POWERDOWN_DELAY_PMU; | ||
444 | writel(reg, pmsu_mp_base + PMSU_POWERDOWN_DELAY); | ||
445 | |||
446 | mvebu_cpu_resume = armada_38x_cpu_resume; | ||
447 | mvebu_v7_cpuidle_device.dev.platform_data = armada_38x_cpu_suspend; | ||
448 | mvebu_v7_cpuidle_device.name = "cpuidle-armada-38x"; | ||
449 | |||
450 | return 0; | ||
451 | } | ||
452 | |||
453 | static __init int armada_xp_cpuidle_init(void) | ||
454 | { | ||
455 | struct device_node *np; | ||
273 | 456 | ||
274 | np = of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric"); | 457 | np = of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric"); |
275 | if (!np) | 458 | if (!np) |
276 | return 0; | 459 | return -ENODEV; |
277 | of_node_put(np); | 460 | of_node_put(np); |
278 | 461 | ||
462 | mvebu_cpu_resume = armada_370_xp_cpu_resume; | ||
463 | mvebu_v7_cpuidle_device.dev.platform_data = armada_370_xp_cpu_suspend; | ||
464 | mvebu_v7_cpuidle_device.name = "cpuidle-armada-xp"; | ||
465 | |||
466 | return 0; | ||
467 | } | ||
468 | |||
469 | static int __init mvebu_v7_cpu_pm_init(void) | ||
470 | { | ||
471 | struct device_node *np; | ||
472 | int ret; | ||
473 | |||
279 | np = of_find_matching_node(NULL, of_pmsu_table); | 474 | np = of_find_matching_node(NULL, of_pmsu_table); |
280 | if (!np) | 475 | if (!np) |
281 | return 0; | 476 | return 0; |
282 | of_node_put(np); | 477 | of_node_put(np); |
283 | 478 | ||
284 | armada_370_xp_pmsu_enable_l2_powerdown_onidle(); | 479 | if (of_machine_is_compatible("marvell,armadaxp")) |
285 | armada_xp_cpuidle_device.dev.platform_data = armada_370_xp_cpu_suspend; | 480 | ret = armada_xp_cpuidle_init(); |
286 | platform_device_register(&armada_xp_cpuidle_device); | 481 | else if (of_machine_is_compatible("marvell,armada370")) |
287 | cpu_pm_register_notifier(&armada_370_xp_cpu_pm_notifier); | 482 | ret = armada_370_cpuidle_init(); |
483 | else if (of_machine_is_compatible("marvell,armada380")) | ||
484 | ret = armada_38x_cpuidle_init(); | ||
485 | else | ||
486 | return 0; | ||
487 | |||
488 | if (ret) | ||
489 | return ret; | ||
490 | |||
491 | mvebu_v7_pmsu_enable_l2_powerdown_onidle(); | ||
492 | platform_device_register(&mvebu_v7_cpuidle_device); | ||
493 | cpu_pm_register_notifier(&mvebu_v7_cpu_pm_notifier); | ||
494 | |||
495 | return 0; | ||
496 | } | ||
497 | |||
498 | arch_initcall(mvebu_v7_cpu_pm_init); | ||
499 | early_initcall(mvebu_v7_pmsu_init); | ||
500 | |||
501 | static void mvebu_pmsu_dfs_request_local(void *data) | ||
502 | { | ||
503 | u32 reg; | ||
504 | u32 cpu = smp_processor_id(); | ||
505 | unsigned long flags; | ||
506 | |||
507 | local_irq_save(flags); | ||
508 | |||
509 | /* Prepare to enter idle */ | ||
510 | reg = readl(pmsu_mp_base + PMSU_STATUS_AND_MASK(cpu)); | ||
511 | reg |= PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT | | ||
512 | PMSU_STATUS_AND_MASK_IRQ_MASK | | ||
513 | PMSU_STATUS_AND_MASK_FIQ_MASK; | ||
514 | writel(reg, pmsu_mp_base + PMSU_STATUS_AND_MASK(cpu)); | ||
515 | |||
516 | /* Request the DFS transition */ | ||
517 | reg = readl(pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(cpu)); | ||
518 | reg |= PMSU_CONTROL_AND_CONFIG_DFS_REQ; | ||
519 | writel(reg, pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(cpu)); | ||
520 | |||
521 | /* The fact of entering idle will trigger the DFS transition */ | ||
522 | wfi(); | ||
523 | |||
524 | /* | ||
525 | * We're back from idle, the DFS transition has completed, | ||
526 | * clear the idle wait indication. | ||
527 | */ | ||
528 | reg = readl(pmsu_mp_base + PMSU_STATUS_AND_MASK(cpu)); | ||
529 | reg &= ~PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT; | ||
530 | writel(reg, pmsu_mp_base + PMSU_STATUS_AND_MASK(cpu)); | ||
531 | |||
532 | local_irq_restore(flags); | ||
533 | } | ||
534 | |||
535 | int mvebu_pmsu_dfs_request(int cpu) | ||
536 | { | ||
537 | unsigned long timeout; | ||
538 | int hwcpu = cpu_logical_map(cpu); | ||
539 | u32 reg; | ||
540 | |||
541 | /* Clear any previous DFS DONE event */ | ||
542 | reg = readl(pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(hwcpu)); | ||
543 | reg &= ~PMSU_EVENT_STATUS_AND_MASK_DFS_DONE; | ||
544 | writel(reg, pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(hwcpu)); | ||
545 | |||
546 | /* Mask the DFS done interrupt, since we are going to poll */ | ||
547 | reg = readl(pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(hwcpu)); | ||
548 | reg |= PMSU_EVENT_STATUS_AND_MASK_DFS_DONE_MASK; | ||
549 | writel(reg, pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(hwcpu)); | ||
550 | |||
551 | /* Trigger the DFS on the appropriate CPU */ | ||
552 | smp_call_function_single(cpu, mvebu_pmsu_dfs_request_local, | ||
553 | NULL, false); | ||
554 | |||
555 | /* Poll until the DFS done event is generated */ | ||
556 | timeout = jiffies + HZ; | ||
557 | while (time_before(jiffies, timeout)) { | ||
558 | reg = readl(pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(hwcpu)); | ||
559 | if (reg & PMSU_EVENT_STATUS_AND_MASK_DFS_DONE) | ||
560 | break; | ||
561 | udelay(10); | ||
562 | } | ||
563 | |||
564 | if (time_after(jiffies, timeout)) | ||
565 | return -ETIME; | ||
566 | |||
567 | /* Restore the DFS mask to its original state */ | ||
568 | reg = readl(pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(hwcpu)); | ||
569 | reg &= ~PMSU_EVENT_STATUS_AND_MASK_DFS_DONE_MASK; | ||
570 | writel(reg, pmsu_mp_base + PMSU_EVENT_STATUS_AND_MASK(hwcpu)); | ||
571 | |||
572 | return 0; | ||
573 | } | ||
574 | |||
575 | static int __init armada_xp_pmsu_cpufreq_init(void) | ||
576 | { | ||
577 | struct device_node *np; | ||
578 | struct resource res; | ||
579 | int ret, cpu; | ||
580 | |||
581 | if (!of_machine_is_compatible("marvell,armadaxp")) | ||
582 | return 0; | ||
583 | |||
584 | /* | ||
585 | * In order to have proper cpufreq handling, we need to ensure | ||
586 | * that the Device Tree description of the CPU clock includes | ||
587 | * the definition of the PMU DFS registers. If not, we do not | ||
588 | * register the clock notifier and the cpufreq driver. This | ||
589 | * piece of code is only for compatibility with old Device | ||
590 | * Trees. | ||
591 | */ | ||
592 | np = of_find_compatible_node(NULL, NULL, "marvell,armada-xp-cpu-clock"); | ||
593 | if (!np) | ||
594 | return 0; | ||
595 | |||
596 | ret = of_address_to_resource(np, 1, &res); | ||
597 | if (ret) { | ||
598 | pr_warn(FW_WARN "not enabling cpufreq, deprecated armada-xp-cpu-clock binding\n"); | ||
599 | of_node_put(np); | ||
600 | return 0; | ||
601 | } | ||
602 | |||
603 | of_node_put(np); | ||
604 | |||
605 | /* | ||
606 | * For each CPU, this loop registers the operating points | ||
607 | * supported (which are the nominal CPU frequency and half of | ||
608 | * it), and registers the clock notifier that will take care | ||
609 | * of doing the PMSU part of a frequency transition. | ||
610 | */ | ||
611 | for_each_possible_cpu(cpu) { | ||
612 | struct device *cpu_dev; | ||
613 | struct clk *clk; | ||
614 | int ret; | ||
615 | |||
616 | cpu_dev = get_cpu_device(cpu); | ||
617 | if (!cpu_dev) { | ||
618 | pr_err("Cannot get CPU %d\n", cpu); | ||
619 | continue; | ||
620 | } | ||
621 | |||
622 | clk = clk_get(cpu_dev, 0); | ||
623 | if (IS_ERR(clk)) { | ||
624 | pr_err("Cannot get clock for CPU %d\n", cpu); | ||
625 | return PTR_ERR(clk); | ||
626 | } | ||
627 | |||
628 | /* | ||
629 | * In case of a failure of dev_pm_opp_add(), we don't | ||
630 | * bother with cleaning up the registered OPP (there's | ||
631 | * no function to do so), and simply cancel the | ||
632 | * registration of the cpufreq device. | ||
633 | */ | ||
634 | ret = dev_pm_opp_add(cpu_dev, clk_get_rate(clk), 0); | ||
635 | if (ret) { | ||
636 | clk_put(clk); | ||
637 | return ret; | ||
638 | } | ||
639 | |||
640 | ret = dev_pm_opp_add(cpu_dev, clk_get_rate(clk) / 2, 0); | ||
641 | if (ret) { | ||
642 | clk_put(clk); | ||
643 | return ret; | ||
644 | } | ||
645 | } | ||
288 | 646 | ||
647 | platform_device_register_simple("cpufreq-generic", -1, NULL, 0); | ||
289 | return 0; | 648 | return 0; |
290 | } | 649 | } |
291 | 650 | ||
292 | arch_initcall(armada_370_xp_cpu_pm_init); | 651 | device_initcall(armada_xp_pmsu_cpufreq_init); |
293 | early_initcall(armada_370_xp_pmsu_init); | ||
diff --git a/arch/arm/mach-mvebu/pmsu.h b/arch/arm/mach-mvebu/pmsu.h index 07a737c6b95d..6b58c1fe2b0d 100644 --- a/arch/arm/mach-mvebu/pmsu.h +++ b/arch/arm/mach-mvebu/pmsu.h | |||
@@ -12,5 +12,10 @@ | |||
12 | #define __MACH_MVEBU_PMSU_H | 12 | #define __MACH_MVEBU_PMSU_H |
13 | 13 | ||
14 | int armada_xp_boot_cpu(unsigned int cpu_id, void *phys_addr); | 14 | int armada_xp_boot_cpu(unsigned int cpu_id, void *phys_addr); |
15 | int mvebu_setup_boot_addr_wa(unsigned int crypto_eng_target, | ||
16 | unsigned int crypto_eng_attribute, | ||
17 | phys_addr_t resume_addr_reg); | ||
18 | |||
19 | void mvebu_v7_pmsu_idle_exit(void); | ||
15 | 20 | ||
16 | #endif /* __MACH_370_XP_PMSU_H */ | 21 | #endif /* __MACH_370_XP_PMSU_H */ |
diff --git a/arch/arm/mach-mvebu/pmsu_ll.S b/arch/arm/mach-mvebu/pmsu_ll.S index fc3de68d8c54..a945756cfb45 100644 --- a/arch/arm/mach-mvebu/pmsu_ll.S +++ b/arch/arm/mach-mvebu/pmsu_ll.S | |||
@@ -23,3 +23,39 @@ ARM_BE8(setend be ) @ go BE8 if entered LE | |||
23 | b cpu_resume | 23 | b cpu_resume |
24 | ENDPROC(armada_370_xp_cpu_resume) | 24 | ENDPROC(armada_370_xp_cpu_resume) |
25 | 25 | ||
26 | ENTRY(armada_38x_cpu_resume) | ||
27 | /* do we need it for Armada 38x*/ | ||
28 | ARM_BE8(setend be ) @ go BE8 if entered LE | ||
29 | bl v7_invalidate_l1 | ||
30 | mrc p15, 4, r1, c15, c0 @ get SCU base address | ||
31 | orr r1, r1, #0x8 @ SCU CPU Power Status Register | ||
32 | mrc 15, 0, r0, cr0, cr0, 5 @ get the CPU ID | ||
33 | and r0, r0, #15 | ||
34 | add r1, r1, r0 | ||
35 | mov r0, #0x0 | ||
36 | strb r0, [r1] @ switch SCU power state to Normal mode | ||
37 | b cpu_resume | ||
38 | ENDPROC(armada_38x_cpu_resume) | ||
39 | |||
40 | .global mvebu_boot_wa_start | ||
41 | .global mvebu_boot_wa_end | ||
42 | |||
43 | /* The following code will be executed from SRAM */ | ||
44 | ENTRY(mvebu_boot_wa_start) | ||
45 | mvebu_boot_wa_start: | ||
46 | ARM_BE8(setend be) | ||
47 | adr r0, 1f | ||
48 | ldr r0, [r0] @ load the address of the | ||
49 | @ resume register | ||
50 | ldr r0, [r0] @ load the value in the | ||
51 | @ resume register | ||
52 | ARM_BE8(rev r0, r0) @ the value is stored LE | ||
53 | mov pc, r0 @ jump to this value | ||
54 | /* | ||
55 | * the last word of this piece of code will be filled by the physical | ||
56 | * address of the boot address register just after being copied in SRAM | ||
57 | */ | ||
58 | 1: | ||
59 | .long . | ||
60 | mvebu_boot_wa_end: | ||
61 | ENDPROC(mvebu_boot_wa_end) | ||
diff --git a/arch/arm/mach-mvebu/system-controller.c b/arch/arm/mach-mvebu/system-controller.c index 0c5524ac75b7..a068cb5c2ce8 100644 --- a/arch/arm/mach-mvebu/system-controller.c +++ b/arch/arm/mach-mvebu/system-controller.c | |||
@@ -28,8 +28,14 @@ | |||
28 | #include <linux/io.h> | 28 | #include <linux/io.h> |
29 | #include <linux/reboot.h> | 29 | #include <linux/reboot.h> |
30 | #include "common.h" | 30 | #include "common.h" |
31 | #include "mvebu-soc-id.h" | ||
32 | #include "pmsu.h" | ||
33 | |||
34 | #define ARMADA_375_CRYPT0_ENG_TARGET 41 | ||
35 | #define ARMADA_375_CRYPT0_ENG_ATTR 1 | ||
31 | 36 | ||
32 | static void __iomem *system_controller_base; | 37 | static void __iomem *system_controller_base; |
38 | static phys_addr_t system_controller_phys_base; | ||
33 | 39 | ||
34 | struct mvebu_system_controller { | 40 | struct mvebu_system_controller { |
35 | u32 rstoutn_mask_offset; | 41 | u32 rstoutn_mask_offset; |
@@ -39,6 +45,9 @@ struct mvebu_system_controller { | |||
39 | u32 system_soft_reset; | 45 | u32 system_soft_reset; |
40 | 46 | ||
41 | u32 resume_boot_addr; | 47 | u32 resume_boot_addr; |
48 | |||
49 | u32 dev_id; | ||
50 | u32 rev_id; | ||
42 | }; | 51 | }; |
43 | static struct mvebu_system_controller *mvebu_sc; | 52 | static struct mvebu_system_controller *mvebu_sc; |
44 | 53 | ||
@@ -47,6 +56,8 @@ static const struct mvebu_system_controller armada_370_xp_system_controller = { | |||
47 | .system_soft_reset_offset = 0x64, | 56 | .system_soft_reset_offset = 0x64, |
48 | .rstoutn_mask_reset_out_en = 0x1, | 57 | .rstoutn_mask_reset_out_en = 0x1, |
49 | .system_soft_reset = 0x1, | 58 | .system_soft_reset = 0x1, |
59 | .dev_id = 0x38, | ||
60 | .rev_id = 0x3c, | ||
50 | }; | 61 | }; |
51 | 62 | ||
52 | static const struct mvebu_system_controller armada_375_system_controller = { | 63 | static const struct mvebu_system_controller armada_375_system_controller = { |
@@ -55,6 +66,8 @@ static const struct mvebu_system_controller armada_375_system_controller = { | |||
55 | .rstoutn_mask_reset_out_en = 0x1, | 66 | .rstoutn_mask_reset_out_en = 0x1, |
56 | .system_soft_reset = 0x1, | 67 | .system_soft_reset = 0x1, |
57 | .resume_boot_addr = 0xd4, | 68 | .resume_boot_addr = 0xd4, |
69 | .dev_id = 0x38, | ||
70 | .rev_id = 0x3c, | ||
58 | }; | 71 | }; |
59 | 72 | ||
60 | static const struct mvebu_system_controller orion_system_controller = { | 73 | static const struct mvebu_system_controller orion_system_controller = { |
@@ -101,11 +114,45 @@ void mvebu_restart(enum reboot_mode mode, const char *cmd) | |||
101 | ; | 114 | ; |
102 | } | 115 | } |
103 | 116 | ||
117 | int mvebu_system_controller_get_soc_id(u32 *dev, u32 *rev) | ||
118 | { | ||
119 | if (of_machine_is_compatible("marvell,armada380") && | ||
120 | system_controller_base) { | ||
121 | *dev = readl(system_controller_base + mvebu_sc->dev_id) >> 16; | ||
122 | *rev = (readl(system_controller_base + mvebu_sc->rev_id) >> 8) | ||
123 | & 0xF; | ||
124 | return 0; | ||
125 | } else | ||
126 | return -ENODEV; | ||
127 | } | ||
128 | |||
104 | #ifdef CONFIG_SMP | 129 | #ifdef CONFIG_SMP |
130 | void mvebu_armada375_smp_wa_init(void) | ||
131 | { | ||
132 | u32 dev, rev; | ||
133 | phys_addr_t resume_addr_reg; | ||
134 | |||
135 | if (mvebu_get_soc_id(&dev, &rev) != 0) | ||
136 | return; | ||
137 | |||
138 | if (rev != ARMADA_375_Z1_REV) | ||
139 | return; | ||
140 | |||
141 | resume_addr_reg = system_controller_phys_base + | ||
142 | mvebu_sc->resume_boot_addr; | ||
143 | mvebu_setup_boot_addr_wa(ARMADA_375_CRYPT0_ENG_TARGET, | ||
144 | ARMADA_375_CRYPT0_ENG_ATTR, | ||
145 | resume_addr_reg); | ||
146 | } | ||
147 | |||
105 | void mvebu_system_controller_set_cpu_boot_addr(void *boot_addr) | 148 | void mvebu_system_controller_set_cpu_boot_addr(void *boot_addr) |
106 | { | 149 | { |
107 | BUG_ON(system_controller_base == NULL); | 150 | BUG_ON(system_controller_base == NULL); |
108 | BUG_ON(mvebu_sc->resume_boot_addr == 0); | 151 | BUG_ON(mvebu_sc->resume_boot_addr == 0); |
152 | |||
153 | if (of_machine_is_compatible("marvell,armada375")) | ||
154 | mvebu_armada375_smp_wa_init(); | ||
155 | |||
109 | writel(virt_to_phys(boot_addr), system_controller_base + | 156 | writel(virt_to_phys(boot_addr), system_controller_base + |
110 | mvebu_sc->resume_boot_addr); | 157 | mvebu_sc->resume_boot_addr); |
111 | } | 158 | } |
@@ -119,7 +166,10 @@ static int __init mvebu_system_controller_init(void) | |||
119 | np = of_find_matching_node_and_match(NULL, of_system_controller_table, | 166 | np = of_find_matching_node_and_match(NULL, of_system_controller_table, |
120 | &match); | 167 | &match); |
121 | if (np) { | 168 | if (np) { |
169 | struct resource res; | ||
122 | system_controller_base = of_iomap(np, 0); | 170 | system_controller_base = of_iomap(np, 0); |
171 | of_address_to_resource(np, 0, &res); | ||
172 | system_controller_phys_base = res.start; | ||
123 | mvebu_sc = (struct mvebu_system_controller *)match->data; | 173 | mvebu_sc = (struct mvebu_system_controller *)match->data; |
124 | of_node_put(np); | 174 | of_node_put(np); |
125 | } | 175 | } |