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__ */ | ||
