diff options
author | Arnd Bergmann <arnd@arndb.de> | 2014-07-26 12:17:08 -0400 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2014-07-26 12:17:08 -0400 |
commit | 39fbf984089e27ec102e246a03c247b7bbd063bd (patch) | |
tree | e52897f6b148121c89d933f9a7c7efe84672cf82 | |
parent | dffd7e35a50d1f8fd79805dd10d412a041f67cb3 (diff) | |
parent | b6e9f521902970732eed7038a1a76354c89daf06 (diff) |
Merge tag 'mvebu-soc-3.17-4' of git://git.infradead.org/linux-mvebu into next/soc
Merge "mvebu SoC changes for v3.17 (round 4)" from Jason Cooper:
- Armada XP
- Fix return value check in pmsu code
- Document URLs for new public datasheets (Thanks, Marvell & free-electrons!)
- Armada 370/38x
- Add cpuidle support
- mvebu
- Fix build when no platforms are selected
- Update EBU SoC status in docs
* tag 'mvebu-soc-3.17-4' of git://git.infradead.org/linux-mvebu: (21 commits)
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
ARM: mvebu: make the snoop disabling optional in mvebu_v7_pmsu_idle_prepare()
ARM: mvebu: use a local variable to store the resume address
ARM: mvebu: make the cpuidle initialization more generic
ARM: mvebu: rename the armada_370_xp symbols to mvebu_v7 in pmsu.c
ARM: mvebu: use the common function for Armada 375 SMP workaround
ARM: mvebu: add a common function for the boot address work around
ARM: mvebu: sort the #include of pmsu.c in alphabetic order
ARM: mvebu: split again armada_370_xp_pmsu_idle_enter() in PMSU code
ARM: mvebu: fix return value check in armada_xp_pmsu_cpufreq_init()
clk: mvebu: extend clk-cpu for dynamic frequency scaling
ARM: mvebu: extend PMSU code to support dynamic frequency scaling
...
Conflicts:
arch/arm/mach-mvebu/Kconfig
drivers/cpuidle/cpuidle-armada-370-xp.c
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
-rw-r--r-- | Documentation/arm/Marvell/README | 19 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt | 5 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/Kconfig | 6 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/armada-370-xp.h | 1 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/board-v7.c | 9 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/common.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/headsmp-a9.S | 15 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/platsmp-a9.c | 42 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/platsmp.c | 3 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/pmsu.c | 435 | ||||
-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 | 31 | ||||
-rw-r--r-- | drivers/clk/mvebu/clk-cpu.c | 80 | ||||
-rw-r--r-- | drivers/cpuidle/Kconfig.arm | 12 | ||||
-rw-r--r-- | drivers/cpuidle/Makefile | 2 | ||||
-rw-r--r-- | drivers/cpuidle/cpuidle-armada-370-xp.c | 93 | ||||
-rw-r--r-- | drivers/cpuidle/cpuidle-mvebu-v7.c | 150 | ||||
-rw-r--r-- | include/linux/mvebu-pmsu.h | 20 |
20 files changed, 757 insertions, 211 deletions
diff --git a/Documentation/arm/Marvell/README b/Documentation/arm/Marvell/README index 1af3a5d5621d..4dc66c173e10 100644 --- a/Documentation/arm/Marvell/README +++ b/Documentation/arm/Marvell/README | |||
@@ -53,8 +53,8 @@ Kirkwood family | |||
53 | Functional Spec: http://www.marvell.com/embedded-processors/kirkwood/assets/FS_88F6180_9x_6281_OpenSource.pdf | 53 | Functional Spec: http://www.marvell.com/embedded-processors/kirkwood/assets/FS_88F6180_9x_6281_OpenSource.pdf |
54 | Homepage: http://www.marvell.com/embedded-processors/kirkwood/ | 54 | Homepage: http://www.marvell.com/embedded-processors/kirkwood/ |
55 | Core: Feroceon ARMv5 compatible | 55 | Core: Feroceon ARMv5 compatible |
56 | Linux kernel mach directory: arch/arm/mach-kirkwood | 56 | Linux kernel mach directory: arch/arm/mach-mvebu |
57 | Linux kernel plat directory: arch/arm/plat-orion | 57 | Linux kernel plat directory: none |
58 | 58 | ||
59 | Discovery family | 59 | Discovery family |
60 | ---------------- | 60 | ---------------- |
@@ -102,8 +102,7 @@ EBU Armada family | |||
102 | MV78460 | 102 | MV78460 |
103 | NOTE: not to be confused with the non-SMP 78xx0 SoCs | 103 | NOTE: not to be confused with the non-SMP 78xx0 SoCs |
104 | Product Brief: http://www.marvell.com/embedded-processors/armada-xp/assets/Marvell-ArmadaXP-SoC-product%20brief.pdf | 104 | Product Brief: http://www.marvell.com/embedded-processors/armada-xp/assets/Marvell-ArmadaXP-SoC-product%20brief.pdf |
105 | 105 | Functional Spec: http://www.marvell.com/embedded-processors/armada-xp/assets/ARMADA-XP-Functional-SpecDatasheet.pdf | |
106 | No public datasheet available. | ||
107 | 106 | ||
108 | Core: Sheeva ARMv7 compatible | 107 | Core: Sheeva ARMv7 compatible |
109 | 108 | ||
@@ -137,7 +136,9 @@ Dove family (application processor) | |||
137 | Functional Spec : http://www.marvell.com/application-processors/armada-500/assets/Armada-510-Functional-Spec.pdf | 136 | Functional Spec : http://www.marvell.com/application-processors/armada-500/assets/Armada-510-Functional-Spec.pdf |
138 | Homepage: http://www.marvell.com/application-processors/armada-500/ | 137 | Homepage: http://www.marvell.com/application-processors/armada-500/ |
139 | Core: ARMv7 compatible | 138 | Core: ARMv7 compatible |
140 | Directory: arch/arm/mach-dove | 139 | |
140 | Directory: arch/arm/mach-mvebu (DT enabled platforms) | ||
141 | arch/arm/mach-dove (non-DT enabled platforms) | ||
141 | 142 | ||
142 | PXA 2xx/3xx/93x/95x family | 143 | PXA 2xx/3xx/93x/95x family |
143 | -------------------------- | 144 | -------------------------- |
@@ -255,10 +256,10 @@ Berlin family (Digital Entertainment) | |||
255 | Long-term plans | 256 | Long-term plans |
256 | --------------- | 257 | --------------- |
257 | 258 | ||
258 | * Unify the mach-dove/, mach-mv78xx0/, mach-orion5x/ and | 259 | * Unify the mach-dove/, mach-mv78xx0/, mach-orion5x/ into the |
259 | mach-kirkwood/ into the mach-mvebu/ to support all SoCs from the | 260 | mach-mvebu/ to support all SoCs from the Marvell EBU (Engineering |
260 | Marvell EBU (Engineering Business Unit) in a single mach-<foo> | 261 | Business Unit) in a single mach-<foo> directory. The plat-orion/ |
261 | directory. The plat-orion/ would therefore disappear. | 262 | would therefore disappear. |
262 | 263 | ||
263 | * Unify the mach-mmp/ and mach-pxa/ into the same mach-pxa | 264 | * Unify the mach-mmp/ and mach-pxa/ into the same mach-pxa |
264 | directory. The plat-pxa/ would therefore disappear. | 265 | directory. The plat-pxa/ would therefore disappear. |
diff --git a/Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt index feb830130714..99c214660bdc 100644 --- a/Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt +++ b/Documentation/devicetree/bindings/clock/mvebu-cpu-clock.txt | |||
@@ -3,14 +3,15 @@ Device Tree Clock bindings for cpu clock of Marvell EBU platforms | |||
3 | Required properties: | 3 | Required properties: |
4 | - compatible : shall be one of the following: | 4 | - compatible : shall be one of the following: |
5 | "marvell,armada-xp-cpu-clock" - cpu clocks for Armada XP | 5 | "marvell,armada-xp-cpu-clock" - cpu clocks for Armada XP |
6 | - reg : Address and length of the clock complex register set | 6 | - reg : Address and length of the clock complex register set, followed |
7 | by address and length of the PMU DFS registers | ||
7 | - #clock-cells : should be set to 1. | 8 | - #clock-cells : should be set to 1. |
8 | - clocks : shall be the input parent clock phandle for the clock. | 9 | - clocks : shall be the input parent clock phandle for the clock. |
9 | 10 | ||
10 | cpuclk: clock-complex@d0018700 { | 11 | cpuclk: clock-complex@d0018700 { |
11 | #clock-cells = <1>; | 12 | #clock-cells = <1>; |
12 | compatible = "marvell,armada-xp-cpu-clock"; | 13 | compatible = "marvell,armada-xp-cpu-clock"; |
13 | reg = <0xd0018700 0xA0>; | 14 | reg = <0xd0018700 0xA0>, <0x1c054 0x10>; |
14 | clocks = <&coreclk 1>; | 15 | clocks = <&coreclk 1>; |
15 | } | 16 | } |
16 | 17 | ||
diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig index 955d4a3afabd..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 |
diff --git a/arch/arm/mach-mvebu/Makefile b/arch/arm/mach-mvebu/Makefile index bc7689e530a4..e24136b42765 100644 --- a/arch/arm/mach-mvebu/Makefile +++ b/arch/arm/mach-mvebu/Makefile | |||
@@ -4,7 +4,7 @@ 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 |
diff --git a/arch/arm/mach-mvebu/armada-370-xp.h b/arch/arm/mach-mvebu/armada-370-xp.h index 52c1603a4f92..84cd90d9b860 100644 --- a/arch/arm/mach-mvebu/armada-370-xp.h +++ b/arch/arm/mach-mvebu/armada-370-xp.h | |||
@@ -25,6 +25,5 @@ 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); | 27 | int armada_370_xp_pmsu_idle_enter(unsigned long deepidle); |
28 | void armada_370_xp_pmsu_idle_exit(void); | ||
29 | 28 | ||
30 | #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 f244622ffc00..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 |
diff --git a/arch/arm/mach-mvebu/common.h b/arch/arm/mach-mvebu/common.h index a97778e28bf6..3ccb40c3bf94 100644 --- a/arch/arm/mach-mvebu/common.h +++ b/arch/arm/mach-mvebu/common.h | |||
@@ -23,4 +23,6 @@ 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 | int mvebu_system_controller_get_soc_id(u32 *dev, u32 *rev); |
25 | 25 | ||
26 | void __iomem *mvebu_get_scu_base(void); | ||
27 | |||
26 | #endif | 28 | #endif |
diff --git a/arch/arm/mach-mvebu/headsmp-a9.S b/arch/arm/mach-mvebu/headsmp-a9.S index da5bb292b91c..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 | mov pc, 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/platsmp-a9.c b/arch/arm/mach-mvebu/platsmp-a9.c index 43aaf3fa75ee..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 | static 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) { |
diff --git a/arch/arm/mach-mvebu/platsmp.c b/arch/arm/mach-mvebu/platsmp.c index b6fa9f0c98b8..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 | ||
@@ -108,7 +109,7 @@ static int armada_xp_boot_secondary(unsigned int cpu, struct task_struct *idle) | |||
108 | */ | 109 | */ |
109 | static void armada_xp_secondary_init(unsigned int cpu) | 110 | static void armada_xp_secondary_init(unsigned int cpu) |
110 | { | 111 | { |
111 | armada_370_xp_pmsu_idle_exit(); | 112 | mvebu_v7_pmsu_idle_exit(); |
112 | } | 113 | } |
113 | 114 | ||
114 | static void __init armada_xp_smp_init_cpus(void) | 115 | static void __init armada_xp_smp_init_cpus(void) |
diff --git a/arch/arm/mach-mvebu/pmsu.c b/arch/arm/mach-mvebu/pmsu.c index b31a8293a347..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,8 +218,14 @@ 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 | int armada_370_xp_pmsu_idle_enter(unsigned long 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; |
@@ -167,17 +249,34 @@ int armada_370_xp_pmsu_idle_enter(unsigned long 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; | ||
267 | } | ||
268 | |||
269 | int armada_370_xp_pmsu_idle_enter(unsigned long deepidle) | ||
270 | { | ||
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; | ||
181 | 280 | ||
182 | v7_exit_coherency_flush(all); | 281 | v7_exit_coherency_flush(all); |
183 | 282 | ||
@@ -203,7 +302,7 @@ int armada_370_xp_pmsu_idle_enter(unsigned long deepidle) | |||
203 | "isb " | 302 | "isb " |
204 | : : : "r0"); | 303 | : : : "r0"); |
205 | 304 | ||
206 | pr_warn("Failed to suspend the system\n"); | 305 | pr_debug("Failed to suspend the system\n"); |
207 | 306 | ||
208 | return 0; | 307 | return 0; |
209 | } | 308 | } |
@@ -213,15 +312,40 @@ static int armada_370_xp_cpu_suspend(unsigned long deepidle) | |||
213 | return cpu_suspend(deepidle, armada_370_xp_pmsu_idle_enter); | 312 | return cpu_suspend(deepidle, armada_370_xp_pmsu_idle_enter); |
214 | } | 313 | } |
215 | 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); | ||
339 | } | ||
340 | |||
216 | /* No locking is needed because we only access per-CPU registers */ | 341 | /* No locking is needed because we only access per-CPU registers */ |
217 | void armada_370_xp_pmsu_idle_exit(void) | 342 | void mvebu_v7_pmsu_idle_exit(void) |
218 | { | 343 | { |
219 | unsigned int hw_cpu = cpu_logical_map(smp_processor_id()); | 344 | unsigned int hw_cpu = cpu_logical_map(smp_processor_id()); |
220 | u32 reg; | 345 | u32 reg; |
221 | 346 | ||
222 | if (pmsu_mp_base == NULL) | 347 | if (pmsu_mp_base == NULL) |
223 | return; | 348 | return; |
224 | |||
225 | /* cancel ask HW to power down the L2 Cache if possible */ | 349 | /* cancel ask HW to power down the L2 Cache if possible */ |
226 | reg = readl(pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu)); | 350 | reg = readl(pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu)); |
227 | reg &= ~PMSU_CONTROL_AND_CONFIG_L2_PWDDN; | 351 | reg &= ~PMSU_CONTROL_AND_CONFIG_L2_PWDDN; |
@@ -236,53 +360,292 @@ void armada_370_xp_pmsu_idle_exit(void) | |||
236 | writel(reg, pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu)); | 360 | writel(reg, pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu)); |
237 | } | 361 | } |
238 | 362 | ||
239 | static int armada_370_xp_cpu_pm_notify(struct notifier_block *self, | 363 | static int mvebu_v7_cpu_pm_notify(struct notifier_block *self, |
240 | unsigned long action, void *hcpu) | 364 | unsigned long action, void *hcpu) |
241 | { | 365 | { |
242 | if (action == CPU_PM_ENTER) { | 366 | if (action == CPU_PM_ENTER) { |
243 | unsigned int hw_cpu = cpu_logical_map(smp_processor_id()); | 367 | unsigned int hw_cpu = cpu_logical_map(smp_processor_id()); |
244 | 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); |
245 | } else if (action == CPU_PM_EXIT) { | 369 | } else if (action == CPU_PM_EXIT) { |
246 | armada_370_xp_pmsu_idle_exit(); | 370 | mvebu_v7_pmsu_idle_exit(); |
247 | } | 371 | } |
248 | 372 | ||
249 | return NOTIFY_OK; | 373 | return NOTIFY_OK; |
250 | } | 374 | } |
251 | 375 | ||
252 | static struct notifier_block armada_370_xp_cpu_pm_notifier = { | 376 | static struct notifier_block mvebu_v7_cpu_pm_notifier = { |
253 | .notifier_call = armada_370_xp_cpu_pm_notify, | 377 | .notifier_call = mvebu_v7_cpu_pm_notify, |
254 | }; | 378 | }; |
255 | 379 | ||
256 | static 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) | ||
257 | { | 383 | { |
258 | 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); | ||
259 | 391 | ||
260 | /* | 392 | /* |
261 | * Check that all the requirements are available to enable | 393 | * On Armada 370, there is "a slow exit process from the deep |
262 | * cpuidle. So far, it is only supported on Armada XP, cpuidle | 394 | * idle state due to heavy L1/L2 cache cleanup operations |
263 | * 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. | ||
264 | */ | 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); | ||
265 | 404 | ||
266 | if (!of_machine_is_compatible("marvell,armadaxp")) | 405 | mvebu_cpu_resume = armada_370_xp_cpu_resume; |
267 | 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; | ||
268 | 456 | ||
269 | np = of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric"); | 457 | np = of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric"); |
270 | if (!np) | 458 | if (!np) |
271 | return 0; | 459 | return -ENODEV; |
272 | of_node_put(np); | 460 | of_node_put(np); |
273 | 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 | |||
274 | np = of_find_matching_node(NULL, of_pmsu_table); | 474 | np = of_find_matching_node(NULL, of_pmsu_table); |
275 | if (!np) | 475 | if (!np) |
276 | return 0; | 476 | return 0; |
277 | of_node_put(np); | 477 | of_node_put(np); |
278 | 478 | ||
279 | armada_370_xp_pmsu_enable_l2_powerdown_onidle(); | 479 | if (of_machine_is_compatible("marvell,armadaxp")) |
280 | armada_xp_cpuidle_device.dev.platform_data = armada_370_xp_cpu_suspend; | 480 | ret = armada_xp_cpuidle_init(); |
281 | platform_device_register(&armada_xp_cpuidle_device); | 481 | else if (of_machine_is_compatible("marvell,armada370")) |
282 | 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 | } | ||
283 | 646 | ||
647 | platform_device_register_simple("cpufreq-generic", -1, NULL, 0); | ||
284 | return 0; | 648 | return 0; |
285 | } | 649 | } |
286 | 650 | ||
287 | arch_initcall(armada_370_xp_cpu_pm_init); | 651 | device_initcall(armada_xp_pmsu_cpufreq_init); |
288 | 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 b2b4e3d6558c..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; |
@@ -121,10 +127,32 @@ int mvebu_system_controller_get_soc_id(u32 *dev, u32 *rev) | |||
121 | } | 127 | } |
122 | 128 | ||
123 | #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 | |||
124 | void mvebu_system_controller_set_cpu_boot_addr(void *boot_addr) | 148 | void mvebu_system_controller_set_cpu_boot_addr(void *boot_addr) |
125 | { | 149 | { |
126 | BUG_ON(system_controller_base == NULL); | 150 | BUG_ON(system_controller_base == NULL); |
127 | 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 | |||
128 | writel(virt_to_phys(boot_addr), system_controller_base + | 156 | writel(virt_to_phys(boot_addr), system_controller_base + |
129 | mvebu_sc->resume_boot_addr); | 157 | mvebu_sc->resume_boot_addr); |
130 | } | 158 | } |
@@ -138,7 +166,10 @@ static int __init mvebu_system_controller_init(void) | |||
138 | 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, |
139 | &match); | 167 | &match); |
140 | if (np) { | 168 | if (np) { |
169 | struct resource res; | ||
141 | 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; | ||
142 | mvebu_sc = (struct mvebu_system_controller *)match->data; | 173 | mvebu_sc = (struct mvebu_system_controller *)match->data; |
143 | of_node_put(np); | 174 | of_node_put(np); |
144 | } | 175 | } |
diff --git a/drivers/clk/mvebu/clk-cpu.c b/drivers/clk/mvebu/clk-cpu.c index 8ebf757d29e2..3821a88077ea 100644 --- a/drivers/clk/mvebu/clk-cpu.c +++ b/drivers/clk/mvebu/clk-cpu.c | |||
@@ -16,10 +16,19 @@ | |||
16 | #include <linux/io.h> | 16 | #include <linux/io.h> |
17 | #include <linux/of.h> | 17 | #include <linux/of.h> |
18 | #include <linux/delay.h> | 18 | #include <linux/delay.h> |
19 | #include <linux/mvebu-pmsu.h> | ||
20 | #include <asm/smp_plat.h> | ||
19 | 21 | ||
20 | #define SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET 0x0 | 22 | #define SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET 0x0 |
21 | #define SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET 0xC | 23 | #define SYS_CTRL_CLK_DIVIDER_CTRL_RESET_ALL 0xff |
22 | #define SYS_CTRL_CLK_DIVIDER_MASK 0x3F | 24 | #define SYS_CTRL_CLK_DIVIDER_CTRL_RESET_SHIFT 8 |
25 | #define SYS_CTRL_CLK_DIVIDER_CTRL2_OFFSET 0x8 | ||
26 | #define SYS_CTRL_CLK_DIVIDER_CTRL2_NBCLK_RATIO_SHIFT 16 | ||
27 | #define SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET 0xC | ||
28 | #define SYS_CTRL_CLK_DIVIDER_MASK 0x3F | ||
29 | |||
30 | #define PMU_DFS_RATIO_SHIFT 16 | ||
31 | #define PMU_DFS_RATIO_MASK 0x3F | ||
23 | 32 | ||
24 | #define MAX_CPU 4 | 33 | #define MAX_CPU 4 |
25 | struct cpu_clk { | 34 | struct cpu_clk { |
@@ -28,6 +37,7 @@ struct cpu_clk { | |||
28 | const char *clk_name; | 37 | const char *clk_name; |
29 | const char *parent_name; | 38 | const char *parent_name; |
30 | void __iomem *reg_base; | 39 | void __iomem *reg_base; |
40 | void __iomem *pmu_dfs; | ||
31 | }; | 41 | }; |
32 | 42 | ||
33 | static struct clk **clks; | 43 | static struct clk **clks; |
@@ -62,8 +72,9 @@ static long clk_cpu_round_rate(struct clk_hw *hwclk, unsigned long rate, | |||
62 | return *parent_rate / div; | 72 | return *parent_rate / div; |
63 | } | 73 | } |
64 | 74 | ||
65 | static int clk_cpu_set_rate(struct clk_hw *hwclk, unsigned long rate, | 75 | static int clk_cpu_off_set_rate(struct clk_hw *hwclk, unsigned long rate, |
66 | unsigned long parent_rate) | 76 | unsigned long parent_rate) |
77 | |||
67 | { | 78 | { |
68 | struct cpu_clk *cpuclk = to_cpu_clk(hwclk); | 79 | struct cpu_clk *cpuclk = to_cpu_clk(hwclk); |
69 | u32 reg, div; | 80 | u32 reg, div; |
@@ -95,6 +106,58 @@ static int clk_cpu_set_rate(struct clk_hw *hwclk, unsigned long rate, | |||
95 | return 0; | 106 | return 0; |
96 | } | 107 | } |
97 | 108 | ||
109 | static int clk_cpu_on_set_rate(struct clk_hw *hwclk, unsigned long rate, | ||
110 | unsigned long parent_rate) | ||
111 | { | ||
112 | u32 reg; | ||
113 | unsigned long fabric_div, target_div, cur_rate; | ||
114 | struct cpu_clk *cpuclk = to_cpu_clk(hwclk); | ||
115 | |||
116 | /* | ||
117 | * PMU DFS registers are not mapped, Device Tree does not | ||
118 | * describes them. We cannot change the frequency dynamically. | ||
119 | */ | ||
120 | if (!cpuclk->pmu_dfs) | ||
121 | return -ENODEV; | ||
122 | |||
123 | cur_rate = __clk_get_rate(hwclk->clk); | ||
124 | |||
125 | reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL2_OFFSET); | ||
126 | fabric_div = (reg >> SYS_CTRL_CLK_DIVIDER_CTRL2_NBCLK_RATIO_SHIFT) & | ||
127 | SYS_CTRL_CLK_DIVIDER_MASK; | ||
128 | |||
129 | /* Frequency is going up */ | ||
130 | if (rate == 2 * cur_rate) | ||
131 | target_div = fabric_div / 2; | ||
132 | /* Frequency is going down */ | ||
133 | else | ||
134 | target_div = fabric_div; | ||
135 | |||
136 | if (target_div == 0) | ||
137 | target_div = 1; | ||
138 | |||
139 | reg = readl(cpuclk->pmu_dfs); | ||
140 | reg &= ~(PMU_DFS_RATIO_MASK << PMU_DFS_RATIO_SHIFT); | ||
141 | reg |= (target_div << PMU_DFS_RATIO_SHIFT); | ||
142 | writel(reg, cpuclk->pmu_dfs); | ||
143 | |||
144 | reg = readl(cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET); | ||
145 | reg |= (SYS_CTRL_CLK_DIVIDER_CTRL_RESET_ALL << | ||
146 | SYS_CTRL_CLK_DIVIDER_CTRL_RESET_SHIFT); | ||
147 | writel(reg, cpuclk->reg_base + SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET); | ||
148 | |||
149 | return mvebu_pmsu_dfs_request(cpuclk->cpu); | ||
150 | } | ||
151 | |||
152 | static int clk_cpu_set_rate(struct clk_hw *hwclk, unsigned long rate, | ||
153 | unsigned long parent_rate) | ||
154 | { | ||
155 | if (__clk_is_enabled(hwclk->clk)) | ||
156 | return clk_cpu_on_set_rate(hwclk, rate, parent_rate); | ||
157 | else | ||
158 | return clk_cpu_off_set_rate(hwclk, rate, parent_rate); | ||
159 | } | ||
160 | |||
98 | static const struct clk_ops cpu_ops = { | 161 | static const struct clk_ops cpu_ops = { |
99 | .recalc_rate = clk_cpu_recalc_rate, | 162 | .recalc_rate = clk_cpu_recalc_rate, |
100 | .round_rate = clk_cpu_round_rate, | 163 | .round_rate = clk_cpu_round_rate, |
@@ -105,6 +168,7 @@ static void __init of_cpu_clk_setup(struct device_node *node) | |||
105 | { | 168 | { |
106 | struct cpu_clk *cpuclk; | 169 | struct cpu_clk *cpuclk; |
107 | void __iomem *clock_complex_base = of_iomap(node, 0); | 170 | void __iomem *clock_complex_base = of_iomap(node, 0); |
171 | void __iomem *pmu_dfs_base = of_iomap(node, 1); | ||
108 | int ncpus = 0; | 172 | int ncpus = 0; |
109 | struct device_node *dn; | 173 | struct device_node *dn; |
110 | 174 | ||
@@ -114,6 +178,10 @@ static void __init of_cpu_clk_setup(struct device_node *node) | |||
114 | return; | 178 | return; |
115 | } | 179 | } |
116 | 180 | ||
181 | if (pmu_dfs_base == NULL) | ||
182 | pr_warn("%s: pmu-dfs base register not set, dynamic frequency scaling not available\n", | ||
183 | __func__); | ||
184 | |||
117 | for_each_node_by_type(dn, "cpu") | 185 | for_each_node_by_type(dn, "cpu") |
118 | ncpus++; | 186 | ncpus++; |
119 | 187 | ||
@@ -146,6 +214,8 @@ static void __init of_cpu_clk_setup(struct device_node *node) | |||
146 | cpuclk[cpu].clk_name = clk_name; | 214 | cpuclk[cpu].clk_name = clk_name; |
147 | cpuclk[cpu].cpu = cpu; | 215 | cpuclk[cpu].cpu = cpu; |
148 | cpuclk[cpu].reg_base = clock_complex_base; | 216 | cpuclk[cpu].reg_base = clock_complex_base; |
217 | if (pmu_dfs_base) | ||
218 | cpuclk[cpu].pmu_dfs = pmu_dfs_base + 4 * cpu; | ||
149 | cpuclk[cpu].hw.init = &init; | 219 | cpuclk[cpu].hw.init = &init; |
150 | 220 | ||
151 | init.name = cpuclk[cpu].clk_name; | 221 | init.name = cpuclk[cpu].clk_name; |
diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm index 2f6b33ea6e08..33fc0ff0af1c 100644 --- a/drivers/cpuidle/Kconfig.arm +++ b/drivers/cpuidle/Kconfig.arm | |||
@@ -1,12 +1,6 @@ | |||
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. | ||
9 | |||
10 | config ARM_BIG_LITTLE_CPUIDLE | 4 | config ARM_BIG_LITTLE_CPUIDLE |
11 | bool "Support for ARM big.LITTLE processors" | 5 | bool "Support for ARM big.LITTLE processors" |
12 | depends on ARCH_VEXPRESS_TC2_PM || ARCH_EXYNOS | 6 | depends on ARCH_VEXPRESS_TC2_PM || ARCH_EXYNOS |
@@ -61,3 +55,9 @@ config ARM_EXYNOS_CPUIDLE | |||
61 | depends on ARCH_EXYNOS | 55 | depends on ARCH_EXYNOS |
62 | help | 56 | help |
63 | Select this to enable cpuidle for Exynos processors | 57 | Select this to enable cpuidle for Exynos processors |
58 | |||
59 | config ARM_MVEBU_V7_CPUIDLE | ||
60 | bool "CPU Idle Driver for mvebu v7 family processors" | ||
61 | depends on ARCH_MVEBU | ||
62 | help | ||
63 | Select this to enable cpuidle on Armada 370, 38x and XP processors. | ||
diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index d8bb1ff72561..11edb31c55e9 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile | |||
@@ -7,7 +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_MVEBU_V7_CPUIDLE) += cpuidle-mvebu-v7.o |
11 | obj-$(CONFIG_ARM_BIG_LITTLE_CPUIDLE) += cpuidle-big_little.o | 11 | obj-$(CONFIG_ARM_BIG_LITTLE_CPUIDLE) += cpuidle-big_little.o |
12 | obj-$(CONFIG_ARM_CLPS711X_CPUIDLE) += cpuidle-clps711x.o | 12 | obj-$(CONFIG_ARM_CLPS711X_CPUIDLE) += cpuidle-clps711x.o |
13 | obj-$(CONFIG_ARM_HIGHBANK_CPUIDLE) += cpuidle-calxeda.o | 13 | obj-$(CONFIG_ARM_HIGHBANK_CPUIDLE) += cpuidle-calxeda.o |
diff --git a/drivers/cpuidle/cpuidle-armada-370-xp.c b/drivers/cpuidle/cpuidle-armada-370-xp.c deleted file mode 100644 index a5fba0287bfb..000000000000 --- a/drivers/cpuidle/cpuidle-armada-370-xp.c +++ /dev/null | |||
@@ -1,93 +0,0 @@ | |||
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 = "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 = "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/cpuidle/cpuidle-mvebu-v7.c b/drivers/cpuidle/cpuidle-mvebu-v7.c new file mode 100644 index 000000000000..45371bb16214 --- /dev/null +++ b/drivers/cpuidle/cpuidle-mvebu-v7.c | |||
@@ -0,0 +1,150 @@ | |||
1 | /* | ||
2 | * Marvell Armada 370, 38x and 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 MVEBU_V7_FLAG_DEEP_IDLE 0x10000 | ||
25 | |||
26 | static int (*mvebu_v7_cpu_suspend)(int); | ||
27 | |||
28 | static int mvebu_v7_enter_idle(struct cpuidle_device *dev, | ||
29 | struct cpuidle_driver *drv, | ||
30 | int index) | ||
31 | { | ||
32 | int ret; | ||
33 | bool deepidle = false; | ||
34 | cpu_pm_enter(); | ||
35 | |||
36 | if (drv->states[index].flags & MVEBU_V7_FLAG_DEEP_IDLE) | ||
37 | deepidle = true; | ||
38 | |||
39 | ret = mvebu_v7_cpu_suspend(deepidle); | ||
40 | if (ret) | ||
41 | return ret; | ||
42 | |||
43 | cpu_pm_exit(); | ||
44 | |||
45 | return index; | ||
46 | } | ||
47 | |||
48 | static struct cpuidle_driver armadaxp_idle_driver = { | ||
49 | .name = "armada_xp_idle", | ||
50 | .states[0] = ARM_CPUIDLE_WFI_STATE, | ||
51 | .states[1] = { | ||
52 | .enter = mvebu_v7_enter_idle, | ||
53 | .exit_latency = 10, | ||
54 | .power_usage = 50, | ||
55 | .target_residency = 100, | ||
56 | .flags = CPUIDLE_FLAG_TIME_VALID, | ||
57 | .name = "MV CPU IDLE", | ||
58 | .desc = "CPU power down", | ||
59 | }, | ||
60 | .states[2] = { | ||
61 | .enter = mvebu_v7_enter_idle, | ||
62 | .exit_latency = 100, | ||
63 | .power_usage = 5, | ||
64 | .target_residency = 1000, | ||
65 | .flags = CPUIDLE_FLAG_TIME_VALID | | ||
66 | MVEBU_V7_FLAG_DEEP_IDLE, | ||
67 | .name = "MV CPU DEEP IDLE", | ||
68 | .desc = "CPU and L2 Fabric power down", | ||
69 | }, | ||
70 | .state_count = 3, | ||
71 | }; | ||
72 | |||
73 | static struct cpuidle_driver armada370_idle_driver = { | ||
74 | .name = "armada_370_idle", | ||
75 | .states[0] = ARM_CPUIDLE_WFI_STATE, | ||
76 | .states[1] = { | ||
77 | .enter = mvebu_v7_enter_idle, | ||
78 | .exit_latency = 100, | ||
79 | .power_usage = 5, | ||
80 | .target_residency = 1000, | ||
81 | .flags = (CPUIDLE_FLAG_TIME_VALID | | ||
82 | MVEBU_V7_FLAG_DEEP_IDLE), | ||
83 | .name = "Deep Idle", | ||
84 | .desc = "CPU and L2 Fabric power down", | ||
85 | }, | ||
86 | .state_count = 2, | ||
87 | }; | ||
88 | |||
89 | static struct cpuidle_driver armada38x_idle_driver = { | ||
90 | .name = "armada_38x_idle", | ||
91 | .states[0] = ARM_CPUIDLE_WFI_STATE, | ||
92 | .states[1] = { | ||
93 | .enter = mvebu_v7_enter_idle, | ||
94 | .exit_latency = 10, | ||
95 | .power_usage = 5, | ||
96 | .target_residency = 100, | ||
97 | .flags = CPUIDLE_FLAG_TIME_VALID, | ||
98 | .name = "Idle", | ||
99 | .desc = "CPU and SCU power down", | ||
100 | }, | ||
101 | .state_count = 2, | ||
102 | }; | ||
103 | |||
104 | static int mvebu_v7_cpuidle_probe(struct platform_device *pdev) | ||
105 | { | ||
106 | mvebu_v7_cpu_suspend = pdev->dev.platform_data; | ||
107 | |||
108 | if (!strcmp(pdev->dev.driver->name, "cpuidle-armada-xp")) | ||
109 | return cpuidle_register(&armadaxp_idle_driver, NULL); | ||
110 | else if (!strcmp(pdev->dev.driver->name, "cpuidle-armada-370")) | ||
111 | return cpuidle_register(&armada370_idle_driver, NULL); | ||
112 | else if (!strcmp(pdev->dev.driver->name, "cpuidle-armada-38x")) | ||
113 | return cpuidle_register(&armada38x_idle_driver, NULL); | ||
114 | else | ||
115 | return -EINVAL; | ||
116 | } | ||
117 | |||
118 | static struct platform_driver armadaxp_cpuidle_plat_driver = { | ||
119 | .driver = { | ||
120 | .name = "cpuidle-armada-xp", | ||
121 | .owner = THIS_MODULE, | ||
122 | }, | ||
123 | .probe = mvebu_v7_cpuidle_probe, | ||
124 | }; | ||
125 | |||
126 | module_platform_driver(armadaxp_cpuidle_plat_driver); | ||
127 | |||
128 | static struct platform_driver armada370_cpuidle_plat_driver = { | ||
129 | .driver = { | ||
130 | .name = "cpuidle-armada-370", | ||
131 | .owner = THIS_MODULE, | ||
132 | }, | ||
133 | .probe = mvebu_v7_cpuidle_probe, | ||
134 | }; | ||
135 | |||
136 | module_platform_driver(armada370_cpuidle_plat_driver); | ||
137 | |||
138 | static struct platform_driver armada38x_cpuidle_plat_driver = { | ||
139 | .driver = { | ||
140 | .name = "cpuidle-armada-38x", | ||
141 | .owner = THIS_MODULE, | ||
142 | }, | ||
143 | .probe = mvebu_v7_cpuidle_probe, | ||
144 | }; | ||
145 | |||
146 | module_platform_driver(armada38x_cpuidle_plat_driver); | ||
147 | |||
148 | MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>"); | ||
149 | MODULE_DESCRIPTION("Marvell EBU v7 cpuidle driver"); | ||
150 | MODULE_LICENSE("GPL"); | ||
diff --git a/include/linux/mvebu-pmsu.h b/include/linux/mvebu-pmsu.h new file mode 100644 index 000000000000..b918d07efe23 --- /dev/null +++ b/include/linux/mvebu-pmsu.h | |||
@@ -0,0 +1,20 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 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 | #ifndef __MVEBU_PMSU_H__ | ||
12 | #define __MVEBU_PMSU_H__ | ||
13 | |||
14 | #ifdef CONFIG_MACH_MVEBU_V7 | ||
15 | int mvebu_pmsu_dfs_request(int cpu); | ||
16 | #else | ||
17 | static inline int mvebu_pmsu_dfs_request(int cpu) { return -ENODEV; } | ||
18 | #endif | ||
19 | |||
20 | #endif /* __MVEBU_PMSU_H__ */ | ||