From 56a705a48eae9c474ba7f82af5c4ff5cf306f654 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Tue, 4 Mar 2014 18:51:47 +0100 Subject: [PATCH 01/46] ARM: mvebu: Add a SOC bus device entry Add the SoC Family, device ID and revision to /sys/bus/soc. Signed-off-by: Andrew Lunn Link: https://lkml.kernel.org/r/1393955507-26436-1-git-send-email-andrew@lunn.ch Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/Kconfig | 1 + arch/arm/mach-mvebu/mvebu-soc-id.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig index 3f73eecbcfb0..475cf8a6d165 100644 --- a/arch/arm/mach-mvebu/Kconfig +++ b/arch/arm/mach-mvebu/Kconfig @@ -6,6 +6,7 @@ config ARCH_MVEBU select IRQ_DOMAIN select PINCTRL select PLAT_ORION + select SOC_BUS select MVEBU_MBUS select ZONE_DMA if ARM_LPAE select ARCH_REQUIRE_GPIOLIB diff --git a/arch/arm/mach-mvebu/mvebu-soc-id.c b/arch/arm/mach-mvebu/mvebu-soc-id.c index f3d4cf53f746..874a7504818e 100644 --- a/arch/arm/mach-mvebu/mvebu-soc-id.c +++ b/arch/arm/mach-mvebu/mvebu-soc-id.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include "mvebu-soc-id.h" #define PCIE_DEV_ID_OFF 0x0 @@ -118,3 +120,31 @@ clk_err: } core_initcall(mvebu_soc_id_init); +static int __init mvebu_soc_device(void) +{ + struct soc_device_attribute *soc_dev_attr; + struct soc_device *soc_dev; + + /* Also protects against running on non-mvebu systems */ + if (!is_id_valid) + return 0; + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + return -ENOMEM; + + soc_dev_attr->family = kasprintf(GFP_KERNEL, "Marvell"); + soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%X", soc_rev); + soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%X", soc_dev_id); + + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR(soc_dev)) { + kfree(soc_dev_attr->family); + kfree(soc_dev_attr->revision); + kfree(soc_dev_attr->soc_id); + kfree(soc_dev_attr); + } + + return 0; +} +postcore_initcall(mvebu_soc_device); From 924d38f404936495b59ef7fa3ff232d978d64b18 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 14 Apr 2014 15:46:59 +0200 Subject: [PATCH 02/46] ARM: mvebu: prepare coherency code to support more SOCs The code that handles the coherency fabric of Armada 370 and Armada XP in arch/arm/mach-mvebu/coherency.c made the assumption that there was only one type of coherency fabric. Unfortunately, it turns out that upcoming SoCs have a slightly different coherency unit. In preparation to the introduction of the coherency support for more SoCs, this commit: * Introduces a data associated to the compatible string in the compatible string match table, so that the code can differantiate the variant of coherency unit being used. * Separates the coherency unit initialization code into its own function. Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1397483228-25625-2-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- .../bindings/arm/coherency-fabric.txt | 5 +- arch/arm/mach-mvebu/coherency.c | 47 +++++++++++++------ 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/coherency-fabric.txt b/Documentation/devicetree/bindings/arm/coherency-fabric.txt index 17d8cd107559..f0bfa37edfc6 100644 --- a/Documentation/devicetree/bindings/arm/coherency-fabric.txt +++ b/Documentation/devicetree/bindings/arm/coherency-fabric.txt @@ -4,7 +4,10 @@ Available on Marvell SOCs: Armada 370 and Armada XP Required properties: -- compatible: "marvell,coherency-fabric" +- compatible: the possible values are: + + * "marvell,coherency-fabric", to be used for the coherency fabric of + the Armada 370 and Armada XP. - reg: Should contain coherency fabric registers location and length. First pair for the coherency fabric registers, second pair diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c index 4e9d58148ca7..434cf5f90a80 100644 --- a/arch/arm/mach-mvebu/coherency.c +++ b/arch/arm/mach-mvebu/coherency.c @@ -38,8 +38,13 @@ static void __iomem *coherency_cpu_base; #define IO_SYNC_BARRIER_CTL_OFFSET 0x0 +enum { + COHERENCY_FABRIC_TYPE_ARMADA_370_XP, +}; + static struct of_device_id of_coherency_table[] = { - {.compatible = "marvell,coherency-fabric"}, + {.compatible = "marvell,coherency-fabric", + .data = (void *) COHERENCY_FABRIC_TYPE_ARMADA_370_XP }, { /* end of list */ }, }; @@ -121,26 +126,40 @@ static struct notifier_block mvebu_hwcc_platform_nb = { .notifier_call = mvebu_hwcc_platform_notifier, }; +static void __init armada_370_coherency_init(struct device_node *np) +{ + struct resource res; + + of_address_to_resource(np, 0, &res); + coherency_phys_base = res.start; + /* + * Ensure secondary CPUs will see the updated value, + * which they read before they join the coherency + * fabric, and therefore before they are coherent with + * the boot CPU cache. + */ + sync_cache_w(&coherency_phys_base); + coherency_base = of_iomap(np, 0); + coherency_cpu_base = of_iomap(np, 1); + set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0); +} + int __init coherency_init(void) { struct device_node *np; np = of_find_matching_node(NULL, of_coherency_table); if (np) { - struct resource res; + const struct of_device_id *match = + of_match_node(of_coherency_table, np); + int type; + + type = (int) match->data; pr_info("Initializing Coherency fabric\n"); - of_address_to_resource(np, 0, &res); - coherency_phys_base = res.start; - /* - * Ensure secondary CPUs will see the updated value, - * which they read before they join the coherency - * fabric, and therefore before they are coherent with - * the boot CPU cache. - */ - sync_cache_w(&coherency_phys_base); - coherency_base = of_iomap(np, 0); - coherency_cpu_base = of_iomap(np, 1); - set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0); + + if (type == COHERENCY_FABRIC_TYPE_ARMADA_370_XP) + armada_370_coherency_init(np); + of_node_put(np); } From 501f928e009760f4987d4630265bb9fff28a6ecc Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 14 Apr 2014 15:47:00 +0200 Subject: [PATCH 03/46] ARM: mvebu: add a coherency_available() call This commit extends the coherency fabric code to provide a coherency_available()function that the SoC code can call to be told whether coherency support is available or not. On Armada 370/XP, coherency support is available as soon as the relevant DT node is present. On some upcoming SoCs, the DT node needs to be present *and* the system running with CONFIG_SMP enabled. Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1397483228-25625-3-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/coherency.c | 32 +++++++++++++++++++++++--------- arch/arm/mach-mvebu/coherency.h | 1 + 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c index 434cf5f90a80..296cf14f08a4 100644 --- a/arch/arm/mach-mvebu/coherency.c +++ b/arch/arm/mach-mvebu/coherency.c @@ -39,6 +39,7 @@ static void __iomem *coherency_cpu_base; #define IO_SYNC_BARRIER_CTL_OFFSET 0x0 enum { + COHERENCY_FABRIC_TYPE_NONE, COHERENCY_FABRIC_TYPE_ARMADA_370_XP, }; @@ -144,7 +145,7 @@ static void __init armada_370_coherency_init(struct device_node *np) set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0); } -int __init coherency_init(void) +static int coherency_type(void) { struct device_node *np; @@ -155,27 +156,40 @@ int __init coherency_init(void) int type; type = (int) match->data; - pr_info("Initializing Coherency fabric\n"); + /* Armada 370/XP coherency works in both UP and SMP */ if (type == COHERENCY_FABRIC_TYPE_ARMADA_370_XP) - armada_370_coherency_init(np); + return type; of_node_put(np); } + return COHERENCY_FABRIC_TYPE_NONE; +} + +int coherency_available(void) +{ + return coherency_type() != COHERENCY_FABRIC_TYPE_NONE; +} + +int __init coherency_init(void) +{ + int type = coherency_type(); + struct device_node *np; + + np = of_find_matching_node(NULL, of_coherency_table); + + if (type == COHERENCY_FABRIC_TYPE_ARMADA_370_XP) + armada_370_coherency_init(np); + return 0; } static int __init coherency_late_init(void) { - struct device_node *np; - - np = of_find_matching_node(NULL, of_coherency_table); - if (np) { + if (coherency_available()) bus_register_notifier(&platform_bus_type, &mvebu_hwcc_platform_nb); - of_node_put(np); - } return 0; } diff --git a/arch/arm/mach-mvebu/coherency.h b/arch/arm/mach-mvebu/coherency.h index 760226c41353..02f7ec34ced2 100644 --- a/arch/arm/mach-mvebu/coherency.h +++ b/arch/arm/mach-mvebu/coherency.h @@ -18,5 +18,6 @@ extern unsigned long coherency_phys_base; int set_cpu_coherent(unsigned int cpu_id, int smp_group_id); int coherency_init(void); +int coherency_available(void); #endif /* __MACH_370_XP_COHERENCY_H */ From 5686a1e5aa436c49187a60052d5885fb1f541ce6 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 14 Apr 2014 15:47:01 +0200 Subject: [PATCH 04/46] bus: mvebu: pass the coherency availability information at init time Until now, the mvebu-mbus was guessing by itself whether hardware I/O coherency was available or not by poking into the Device Tree to see if the coherency fabric Device Tree node was present or not. However, on some upcoming SoCs, the presence or absence of the coherency fabric DT node isn't sufficient: in CONFIG_SMP, the coherency can be enabled, but not in !CONFIG_SMP. In order to clean this up, the mvebu_mbus_dt_init() function is extended to get a boolean argument telling whether coherency is enabled or not. Therefore, the logic to decide whether coherency is available or not now belongs to the core SoC code instead of the mvebu-mbus driver itself, which is much better. Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1397483228-25625-4-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-kirkwood/board-dt.c | 2 +- arch/arm/mach-mvebu/board-v7.c | 2 +- arch/arm/mach-mvebu/dove.c | 2 +- arch/arm/mach-mvebu/kirkwood.c | 2 +- drivers/bus/mvebu-mbus.c | 11 +++-------- include/linux/mbus.h | 2 +- 6 files changed, 8 insertions(+), 13 deletions(-) diff --git a/arch/arm/mach-kirkwood/board-dt.c b/arch/arm/mach-kirkwood/board-dt.c index 2801da49e2a3..ff18ff20f71f 100644 --- a/arch/arm/mach-kirkwood/board-dt.c +++ b/arch/arm/mach-kirkwood/board-dt.c @@ -195,7 +195,7 @@ static void __init kirkwood_dt_init(void) { kirkwood_disable_mbus_error_propagation(); - BUG_ON(mvebu_mbus_dt_init()); + BUG_ON(mvebu_mbus_dt_init(false)); #ifdef CONFIG_CACHE_FEROCEON_L2 feroceon_of_init(); diff --git a/arch/arm/mach-mvebu/board-v7.c b/arch/arm/mach-mvebu/board-v7.c index 333fca8fdc41..1730e0cdb6f6 100644 --- a/arch/arm/mach-mvebu/board-v7.c +++ b/arch/arm/mach-mvebu/board-v7.c @@ -58,7 +58,7 @@ static void __init mvebu_timer_and_clk_init(void) of_clk_init(NULL); clocksource_of_init(); coherency_init(); - BUG_ON(mvebu_mbus_dt_init()); + BUG_ON(mvebu_mbus_dt_init(coherency_available())); #ifdef CONFIG_CACHE_L2X0 l2x0_of_init(0, ~0UL); #endif diff --git a/arch/arm/mach-mvebu/dove.c b/arch/arm/mach-mvebu/dove.c index 5e5a43624237..b50464ec1130 100644 --- a/arch/arm/mach-mvebu/dove.c +++ b/arch/arm/mach-mvebu/dove.c @@ -23,7 +23,7 @@ static void __init dove_init(void) #ifdef CONFIG_CACHE_TAUROS2 tauros2_init(0); #endif - BUG_ON(mvebu_mbus_dt_init()); + BUG_ON(mvebu_mbus_dt_init(false)); of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); } diff --git a/arch/arm/mach-mvebu/kirkwood.c b/arch/arm/mach-mvebu/kirkwood.c index 120207fc36f1..a77e0bae9c55 100644 --- a/arch/arm/mach-mvebu/kirkwood.c +++ b/arch/arm/mach-mvebu/kirkwood.c @@ -169,7 +169,7 @@ static void __init kirkwood_dt_init(void) { kirkwood_disable_mbus_error_propagation(); - BUG_ON(mvebu_mbus_dt_init()); + BUG_ON(mvebu_mbus_dt_init(false)); #ifdef CONFIG_CACHE_FEROCEON_L2 feroceon_of_init(); diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c index 293e2e0a0a87..ff02fc90fc21 100644 --- a/drivers/bus/mvebu-mbus.c +++ b/drivers/bus/mvebu-mbus.c @@ -694,7 +694,6 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus, phys_addr_t sdramwins_phys_base, size_t sdramwins_size) { - struct device_node *np; int win; mbus->mbuswins_base = ioremap(mbuswins_phys_base, mbuswins_size); @@ -707,12 +706,6 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus, return -ENOMEM; } - np = of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric"); - if (np) { - mbus->hw_io_coherency = 1; - of_node_put(np); - } - for (win = 0; win < mbus->soc->num_wins; win++) mvebu_mbus_disable_window(mbus, win); @@ -882,7 +875,7 @@ static void __init mvebu_mbus_get_pcie_resources(struct device_node *np, } } -int __init mvebu_mbus_dt_init(void) +int __init mvebu_mbus_dt_init(bool is_coherent) { struct resource mbuswins_res, sdramwins_res; struct device_node *np, *controller; @@ -920,6 +913,8 @@ int __init mvebu_mbus_dt_init(void) return -EINVAL; } + mbus_state.hw_io_coherency = is_coherent; + /* Get optional pcie-{mem,io}-aperture properties */ mvebu_mbus_get_pcie_resources(np, &mbus_state.pcie_mem_aperture, &mbus_state.pcie_io_aperture); diff --git a/include/linux/mbus.h b/include/linux/mbus.h index 345b8c53b897..550c88fb0267 100644 --- a/include/linux/mbus.h +++ b/include/linux/mbus.h @@ -73,6 +73,6 @@ int mvebu_mbus_del_window(phys_addr_t base, size_t size); int mvebu_mbus_init(const char *soc, phys_addr_t mbus_phys_base, size_t mbus_size, phys_addr_t sdram_phys_base, size_t sdram_size); -int mvebu_mbus_dt_init(void); +int mvebu_mbus_dt_init(bool is_coherent); #endif /* __LINUX_MBUS_H */ From 5fbba0805165a4883c60cf159f272dd0d7205c44 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 14 Apr 2014 15:47:02 +0200 Subject: [PATCH 05/46] ARM: mvebu: ARM: mvebu: use of_find_matching_node_and_match() in coherency.c In the mach-mvebu coherency code, instead of using of_find_matching_node() and then of_match_node(), directly use the of_find_matching_node_and_match() which does both at once. We take this opportunity to also simplify the initialization of the "type" variable. Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1397483228-25625-5-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/coherency.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c index 296cf14f08a4..862100f7c836 100644 --- a/arch/arm/mach-mvebu/coherency.c +++ b/arch/arm/mach-mvebu/coherency.c @@ -148,14 +148,11 @@ static void __init armada_370_coherency_init(struct device_node *np) static int coherency_type(void) { struct device_node *np; + const struct of_device_id *match; - np = of_find_matching_node(NULL, of_coherency_table); + np = of_find_matching_node_and_match(NULL, of_coherency_table, &match); if (np) { - const struct of_device_id *match = - of_match_node(of_coherency_table, np); - int type; - - type = (int) match->data; + int type = (int) match->data; /* Armada 370/XP coherency works in both UP and SMP */ if (type == COHERENCY_FABRIC_TYPE_ARMADA_370_XP) From 8e6ac20338228dc77c8f3538efb5a3aba712e0e5 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 14 Apr 2014 15:47:03 +0200 Subject: [PATCH 06/46] ARM: mvebu: enable the ARM SCU on Armada 375 and Armada 38x Contrary to the Armada 370 and XP that used the PJ4B Marvell cores, the Armada 375 and Armada 38x use the ARM Cortex-A9. A consequence of this is that the unit responsible for the coherency between CPUs is now the ARM SCU, and not the Marvell coherency unit (which is still present to do coherency with I/O devices). Therefore this commit: * Ensures that the selection of the Armada 375 or Armada 38x SoC support enables the ARM SCU support in the kernel. * Make sure to initialize the SCU at boot time. Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1397483228-25625-6-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/Kconfig | 2 ++ arch/arm/mach-mvebu/board-v7.c | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig index 475cf8a6d165..736bd2cbc1f0 100644 --- a/arch/arm/mach-mvebu/Kconfig +++ b/arch/arm/mach-mvebu/Kconfig @@ -40,6 +40,7 @@ config MACH_ARMADA_375 select ARM_GIC select ARMADA_375_CLK select CPU_V7 + select HAVE_ARM_SCU select MACH_MVEBU_V7 select PINCTRL_ARMADA_375 help @@ -53,6 +54,7 @@ config MACH_ARMADA_38X select ARM_GIC select ARMADA_38X_CLK select CPU_V7 + select HAVE_ARM_SCU select MACH_MVEBU_V7 select PINCTRL_ARMADA_38X help diff --git a/arch/arm/mach-mvebu/board-v7.c b/arch/arm/mach-mvebu/board-v7.c index 1730e0cdb6f6..85f964e55bf3 100644 --- a/arch/arm/mach-mvebu/board-v7.c +++ b/arch/arm/mach-mvebu/board-v7.c @@ -27,11 +27,29 @@ #include #include #include +#include #include "armada-370-xp.h" #include "common.h" #include "coherency.h" #include "mvebu-soc-id.h" +/* + * Enables the SCU when available. Obviously, this is only useful on + * Cortex-A based SOCs, not on PJ4B based ones. + */ +static void __init mvebu_scu_enable(void) +{ + void __iomem *scu_base; + + struct device_node *np = + of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu"); + if (np) { + scu_base = of_iomap(np, 0); + scu_enable(scu_base); + of_node_put(np); + } +} + /* * Early versions of Armada 375 SoC have a bug where the BootROM * leaves an external data abort pending. The kernel is hit by this @@ -57,6 +75,7 @@ static void __init mvebu_timer_and_clk_init(void) { of_clk_init(NULL); clocksource_of_init(); + mvebu_scu_enable(); coherency_init(); BUG_ON(mvebu_mbus_dt_init(coherency_available())); #ifdef CONFIG_CACHE_L2X0 From 77fa4b9ab0aafc0a06976814ad62aeff990dfd2c Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 14 Apr 2014 15:47:04 +0200 Subject: [PATCH 07/46] ARM: mvebu: add Armada 375 support to the coherency code The Armada 375, like the Armada 370 and Armada XP, has a coherency unit. However, unlike the coherency unit of 370/XP which does both CPU and I/O coherency, the one on Armada 735 only does I/O coherency. Therefore, instead of having two sets of registers (the first one being used mainly to register each CPU in the coherency fabric, the second one being used for the I/O coherency barrier), it has only one set of register (for the I/O coherency barrier). This commit adds a new "marvell,armada-375-coherency-fabric" compatible string for this variant of the coherency fabric. The custom DMA operations, and the way of triggering an I/O barrier is the same as Armada 370/XP, so the code changes are minimal. However, the set_cpu_coherent() function is not needed on Armada 375 and will not work. Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1397483228-25625-7-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- .../bindings/arm/coherency-fabric.txt | 24 +++++++++++++++---- arch/arm/mach-mvebu/coherency.c | 14 +++++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/coherency-fabric.txt b/Documentation/devicetree/bindings/arm/coherency-fabric.txt index f0bfa37edfc6..dcd80d6e0471 100644 --- a/Documentation/devicetree/bindings/arm/coherency-fabric.txt +++ b/Documentation/devicetree/bindings/arm/coherency-fabric.txt @@ -1,6 +1,6 @@ Coherency fabric ---------------- -Available on Marvell SOCs: Armada 370 and Armada XP +Available on Marvell SOCs: Armada 370, Armada 375 and Armada XP Required properties: @@ -9,11 +9,20 @@ Required properties: * "marvell,coherency-fabric", to be used for the coherency fabric of the Armada 370 and Armada XP. -- reg: Should contain coherency fabric registers location and - length. First pair for the coherency fabric registers, second pair - for the per-CPU fabric registers registers. + * "marvell,armada-375-coherency-fabric", for the Armada 375 coherency + fabric. -Example: +- reg: Should contain coherency fabric registers location and + length. + + * For "marvell,coherency-fabric", the first pair for the coherency + fabric registers, second pair for the per-CPU fabric registers. + + * For "marvell,armada-375-coherency-fabric", only one pair is needed + for the per-CPU fabric registers. + + +Examples: coherency-fabric@d0020200 { compatible = "marvell,coherency-fabric"; @@ -22,3 +31,8 @@ coherency-fabric@d0020200 { }; +coherency-fabric@21810 { + compatible = "marvell,armada-375-coherency-fabric"; + reg = <0x21810 0x1c>; +}; + diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c index 862100f7c836..7ccaf87fd772 100644 --- a/arch/arm/mach-mvebu/coherency.c +++ b/arch/arm/mach-mvebu/coherency.c @@ -41,11 +41,14 @@ static void __iomem *coherency_cpu_base; enum { COHERENCY_FABRIC_TYPE_NONE, COHERENCY_FABRIC_TYPE_ARMADA_370_XP, + COHERENCY_FABRIC_TYPE_ARMADA_375, }; static struct of_device_id of_coherency_table[] = { {.compatible = "marvell,coherency-fabric", .data = (void *) COHERENCY_FABRIC_TYPE_ARMADA_370_XP }, + {.compatible = "marvell,armada-375-coherency-fabric", + .data = (void *) COHERENCY_FABRIC_TYPE_ARMADA_375 }, { /* end of list */ }, }; @@ -145,6 +148,11 @@ static void __init armada_370_coherency_init(struct device_node *np) set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0); } +static void __init armada_375_coherency_init(struct device_node *np) +{ + coherency_cpu_base = of_iomap(np, 0); +} + static int coherency_type(void) { struct device_node *np; @@ -158,6 +166,10 @@ static int coherency_type(void) if (type == COHERENCY_FABRIC_TYPE_ARMADA_370_XP) return type; + /* Armada 375 coherency works only on SMP */ + else if (type == COHERENCY_FABRIC_TYPE_ARMADA_375 && is_smp()) + return type; + of_node_put(np); } @@ -178,6 +190,8 @@ int __init coherency_init(void) if (type == COHERENCY_FABRIC_TYPE_ARMADA_370_XP) armada_370_coherency_init(np); + else if (type == COHERENCY_FABRIC_TYPE_ARMADA_375) + armada_375_coherency_init(np); return 0; } From 5ab5afd8ba837560f76f8ee527271d2e819bcaef Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 14 Apr 2014 15:47:05 +0200 Subject: [PATCH 08/46] ARM: mvebu: implement Armada 375 coherency workaround The early revisions of Armada 375 SOCs (Z1 stepping) have a bug in the I/O coherency unit that prevents using the normal method for the I/O coherency barrier. The recommended workaround is to use a XOR memset transfer to act as the I/O coherency barrier. This involves "borrowing" a XOR engine, which gets disabled in the Device Tree so the normal XOR driver doesn't use it. Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1397483228-25625-8-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/coherency.c | 168 +++++++++++++++++++++++++++++++- 1 file changed, 165 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c index 7ccaf87fd772..75db33ef93ee 100644 --- a/arch/arm/mach-mvebu/coherency.c +++ b/arch/arm/mach-mvebu/coherency.c @@ -17,6 +17,8 @@ * supplies basic routines for configuring and controlling hardware coherency */ +#define pr_fmt(fmt) "mvebu-coherency: " fmt + #include #include #include @@ -24,6 +26,9 @@ #include #include #include +#include +#include +#include #include #include #include "armada-370-xp.h" @@ -66,8 +71,157 @@ int set_cpu_coherent(unsigned int hw_cpu_id, int smp_group_id) return ll_set_cpu_coherent(coherency_base, hw_cpu_id); } +/* + * The below code implements the I/O coherency workaround on Armada + * 375. This workaround consists in using the two channels of the + * first XOR engine to trigger a XOR transaction that serves as the + * I/O coherency barrier. + */ + +static void __iomem *xor_base, *xor_high_base; +static dma_addr_t coherency_wa_buf_phys[CONFIG_NR_CPUS]; +static void *coherency_wa_buf[CONFIG_NR_CPUS]; +static bool coherency_wa_enabled; + +#define XOR_CONFIG(chan) (0x10 + (chan * 4)) +#define XOR_ACTIVATION(chan) (0x20 + (chan * 4)) +#define WINDOW_BAR_ENABLE(chan) (0x240 + ((chan) << 2)) +#define WINDOW_BASE(w) (0x250 + ((w) << 2)) +#define WINDOW_SIZE(w) (0x270 + ((w) << 2)) +#define WINDOW_REMAP_HIGH(w) (0x290 + ((w) << 2)) +#define WINDOW_OVERRIDE_CTRL(chan) (0x2A0 + ((chan) << 2)) +#define XOR_DEST_POINTER(chan) (0x2B0 + (chan * 4)) +#define XOR_BLOCK_SIZE(chan) (0x2C0 + (chan * 4)) +#define XOR_INIT_VALUE_LOW 0x2E0 +#define XOR_INIT_VALUE_HIGH 0x2E4 + +static inline void mvebu_hwcc_armada375_sync_io_barrier_wa(void) +{ + int idx = smp_processor_id(); + + /* Write '1' to the first word of the buffer */ + writel(0x1, coherency_wa_buf[idx]); + + /* Wait until the engine is idle */ + while ((readl(xor_base + XOR_ACTIVATION(idx)) >> 4) & 0x3) + ; + + dmb(); + + /* Trigger channel */ + writel(0x1, xor_base + XOR_ACTIVATION(idx)); + + /* Poll the data until it is cleared by the XOR transaction */ + while (readl(coherency_wa_buf[idx])) + ; +} + +static void __init armada_375_coherency_init_wa(void) +{ + const struct mbus_dram_target_info *dram; + struct device_node *xor_node; + struct property *xor_status; + struct clk *xor_clk; + u32 win_enable = 0; + int i; + + pr_warn("enabling coherency workaround for Armada 375 Z1, one XOR engine disabled\n"); + + /* + * Since the workaround uses one XOR engine, we grab a + * reference to its Device Tree node first. + */ + xor_node = of_find_compatible_node(NULL, NULL, "marvell,orion-xor"); + BUG_ON(!xor_node); + + /* + * Then we mark it as disabled so that the real XOR driver + * will not use it. + */ + xor_status = kzalloc(sizeof(struct property), GFP_KERNEL); + BUG_ON(!xor_status); + + xor_status->value = kstrdup("disabled", GFP_KERNEL); + BUG_ON(!xor_status->value); + + xor_status->length = 8; + xor_status->name = kstrdup("status", GFP_KERNEL); + BUG_ON(!xor_status->name); + + of_update_property(xor_node, xor_status); + + /* + * And we remap the registers, get the clock, and do the + * initial configuration of the XOR engine. + */ + xor_base = of_iomap(xor_node, 0); + xor_high_base = of_iomap(xor_node, 1); + + xor_clk = of_clk_get_by_name(xor_node, NULL); + BUG_ON(!xor_clk); + + clk_prepare_enable(xor_clk); + + dram = mv_mbus_dram_info(); + + for (i = 0; i < 8; i++) { + writel(0, xor_base + WINDOW_BASE(i)); + writel(0, xor_base + WINDOW_SIZE(i)); + if (i < 4) + writel(0, xor_base + WINDOW_REMAP_HIGH(i)); + } + + for (i = 0; i < dram->num_cs; i++) { + const struct mbus_dram_window *cs = dram->cs + i; + writel((cs->base & 0xffff0000) | + (cs->mbus_attr << 8) | + dram->mbus_dram_target_id, xor_base + WINDOW_BASE(i)); + writel((cs->size - 1) & 0xffff0000, xor_base + WINDOW_SIZE(i)); + + win_enable |= (1 << i); + win_enable |= 3 << (16 + (2 * i)); + } + + writel(win_enable, xor_base + WINDOW_BAR_ENABLE(0)); + writel(win_enable, xor_base + WINDOW_BAR_ENABLE(1)); + writel(0, xor_base + WINDOW_OVERRIDE_CTRL(0)); + writel(0, xor_base + WINDOW_OVERRIDE_CTRL(1)); + + for (i = 0; i < CONFIG_NR_CPUS; i++) { + coherency_wa_buf[i] = kzalloc(PAGE_SIZE, GFP_KERNEL); + BUG_ON(!coherency_wa_buf[i]); + + /* + * We can't use the DMA mapping API, since we don't + * have a valid 'struct device' pointer + */ + coherency_wa_buf_phys[i] = + virt_to_phys(coherency_wa_buf[i]); + BUG_ON(!coherency_wa_buf_phys[i]); + + /* + * Configure the XOR engine for memset operation, with + * a 128 bytes block size + */ + writel(0x444, xor_base + XOR_CONFIG(i)); + writel(128, xor_base + XOR_BLOCK_SIZE(i)); + writel(coherency_wa_buf_phys[i], + xor_base + XOR_DEST_POINTER(i)); + } + + writel(0x0, xor_base + XOR_INIT_VALUE_LOW); + writel(0x0, xor_base + XOR_INIT_VALUE_HIGH); + + coherency_wa_enabled = true; +} + static inline void mvebu_hwcc_sync_io_barrier(void) { + if (coherency_wa_enabled) { + mvebu_hwcc_armada375_sync_io_barrier_wa(); + return; + } + writel(0x1, coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET); while (readl(coherency_cpu_base + IO_SYNC_BARRIER_CTL_OFFSET) & 0x1); } @@ -198,9 +352,17 @@ int __init coherency_init(void) static int __init coherency_late_init(void) { - if (coherency_available()) - bus_register_notifier(&platform_bus_type, - &mvebu_hwcc_platform_nb); + int type = coherency_type(); + + if (type == COHERENCY_FABRIC_TYPE_NONE) + return 0; + + if (type == COHERENCY_FABRIC_TYPE_ARMADA_375) + armada_375_coherency_init_wa(); + + bus_register_notifier(&platform_bus_type, + &mvebu_hwcc_platform_nb); + return 0; } From d0de9323822fada543f2f244eb67f520aa21ed77 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 14 Apr 2014 15:47:06 +0200 Subject: [PATCH 09/46] ARM: mvebu: add Armada 38x support to the coherency code The Armada 38x has a coherency unit that is similar to the one of the Armada 375 SoC, except that it does not have the bug of the Armada 375 coherency unit that requires the XOR based workaround. This commit therefore extends the Marvell EBU coherency code with a new compatible string to support the Armada 38x coherency unit. Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1397483228-25625-9-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- .../devicetree/bindings/arm/coherency-fabric.txt | 7 ++++++- arch/arm/mach-mvebu/coherency.c | 14 +++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/coherency-fabric.txt b/Documentation/devicetree/bindings/arm/coherency-fabric.txt index dcd80d6e0471..8dd46617c889 100644 --- a/Documentation/devicetree/bindings/arm/coherency-fabric.txt +++ b/Documentation/devicetree/bindings/arm/coherency-fabric.txt @@ -1,6 +1,6 @@ Coherency fabric ---------------- -Available on Marvell SOCs: Armada 370, Armada 375 and Armada XP +Available on Marvell SOCs: Armada 370, Armada 375, Armada 38x and Armada XP Required properties: @@ -12,6 +12,9 @@ Required properties: * "marvell,armada-375-coherency-fabric", for the Armada 375 coherency fabric. + * "marvell,armada-380-coherency-fabric", for the Armada 38x coherency + fabric. + - reg: Should contain coherency fabric registers location and length. @@ -21,6 +24,8 @@ Required properties: * For "marvell,armada-375-coherency-fabric", only one pair is needed for the per-CPU fabric registers. + * For "marvell,armada-380-coherency-fabric", only one pair is needed + for the per-CPU fabric registers. Examples: diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c index 75db33ef93ee..489edd157cfb 100644 --- a/arch/arm/mach-mvebu/coherency.c +++ b/arch/arm/mach-mvebu/coherency.c @@ -47,6 +47,7 @@ enum { COHERENCY_FABRIC_TYPE_NONE, COHERENCY_FABRIC_TYPE_ARMADA_370_XP, COHERENCY_FABRIC_TYPE_ARMADA_375, + COHERENCY_FABRIC_TYPE_ARMADA_380, }; static struct of_device_id of_coherency_table[] = { @@ -54,6 +55,8 @@ static struct of_device_id of_coherency_table[] = { .data = (void *) COHERENCY_FABRIC_TYPE_ARMADA_370_XP }, {.compatible = "marvell,armada-375-coherency-fabric", .data = (void *) COHERENCY_FABRIC_TYPE_ARMADA_375 }, + {.compatible = "marvell,armada-380-coherency-fabric", + .data = (void *) COHERENCY_FABRIC_TYPE_ARMADA_380 }, { /* end of list */ }, }; @@ -302,7 +305,7 @@ static void __init armada_370_coherency_init(struct device_node *np) set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0); } -static void __init armada_375_coherency_init(struct device_node *np) +static void __init armada_375_380_coherency_init(struct device_node *np) { coherency_cpu_base = of_iomap(np, 0); } @@ -324,6 +327,10 @@ static int coherency_type(void) else if (type == COHERENCY_FABRIC_TYPE_ARMADA_375 && is_smp()) return type; + /* Armada 380 coherency works only on SMP */ + else if (type == COHERENCY_FABRIC_TYPE_ARMADA_380 && is_smp()) + return type; + of_node_put(np); } @@ -344,8 +351,9 @@ int __init coherency_init(void) if (type == COHERENCY_FABRIC_TYPE_ARMADA_370_XP) armada_370_coherency_init(np); - else if (type == COHERENCY_FABRIC_TYPE_ARMADA_375) - armada_375_coherency_init(np); + else if (type == COHERENCY_FABRIC_TYPE_ARMADA_375 || + type == COHERENCY_FABRIC_TYPE_ARMADA_380) + armada_375_380_coherency_init(np); return 0; } From 3f20fb1153b374737acd40d42cb3cab2ae5dae35 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 14 Apr 2014 15:50:28 +0200 Subject: [PATCH 10/46] ARM: mvebu: introduce CPU reset code The Armada 370 and Armada XP have registers that allow to reset the CPUs, which is particularly useful to take the secondary CPUs out of reset in the context of the SMP support. Unfortunately, an implementation mistake was originally made and the support for these registers was integrated into the PMSU driver, which is in fact completely unrelated. And it turns out that the Armada 375 has the same CPU reset registers, but does not have the PMSU registers. Therefore, this commit creates a small CPU reset driver. All it does is provide a simple mvebu_cpu_reset_deassert() function that the SMP support code can call to take secondary CPUs out of reset. As of this commit, the driver isn't being used, it will be used through changes in the following commits. Note that we initially planned to use the 'reset controller' framework, but it requires the addition of "resets" properties in the Device Tree, which are causing too many problems if we want to keep the Device Tree backward compatibility. Moreover, the 'reset controller' framework is mainly useful when a device driver needs to request a reset of its device from a separate reset controller. In our case, the CPU reset handling and the SMP core code are both located in arch/arm/mach-mvebu/ and are tightly linked together, so there's no real benefit in going through a separate framework. Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1397483433-25836-2-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- .../bindings/arm/armada-cpu-reset.txt | 14 ++++ arch/arm/mach-mvebu/Makefile | 2 +- arch/arm/mach-mvebu/common.h | 1 + arch/arm/mach-mvebu/cpu-reset.c | 82 +++++++++++++++++++ 4 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/arm/armada-cpu-reset.txt create mode 100644 arch/arm/mach-mvebu/cpu-reset.c diff --git a/Documentation/devicetree/bindings/arm/armada-cpu-reset.txt b/Documentation/devicetree/bindings/arm/armada-cpu-reset.txt new file mode 100644 index 000000000000..b63a7b6ab998 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/armada-cpu-reset.txt @@ -0,0 +1,14 @@ +Marvell Armada CPU reset controller +=================================== + +Required properties: + +- compatible: Should be "marvell,armada-370-cpu-reset". + +- reg: should be register base and length as documented in the + datasheet for the CPU reset registers + +cpurst: cpurst@20800 { + compatible = "marvell,armada-370-cpu-reset"; + reg = <0x20800 0x20>; +}; diff --git a/arch/arm/mach-mvebu/Makefile b/arch/arm/mach-mvebu/Makefile index a63e43b6b451..f9cfab05c5f9 100644 --- a/arch/arm/mach-mvebu/Makefile +++ b/arch/arm/mach-mvebu/Makefile @@ -3,7 +3,7 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \ AFLAGS_coherency_ll.o := -Wa,-march=armv7-a -obj-y += system-controller.o mvebu-soc-id.o +obj-y += system-controller.o mvebu-soc-id.o cpu-reset.o obj-$(CONFIG_MACH_MVEBU_V7) += board-v7.o obj-$(CONFIG_MACH_DOVE) += dove.o obj-$(CONFIG_ARCH_MVEBU) += coherency.o coherency_ll.o pmsu.o diff --git a/arch/arm/mach-mvebu/common.h b/arch/arm/mach-mvebu/common.h index 55449c487c9e..cfb129b144c0 100644 --- a/arch/arm/mach-mvebu/common.h +++ b/arch/arm/mach-mvebu/common.h @@ -18,6 +18,7 @@ #include void mvebu_restart(enum reboot_mode mode, const char *cmd); +int mvebu_cpu_reset_deassert(int cpu); void armada_xp_cpu_die(unsigned int cpu); diff --git a/arch/arm/mach-mvebu/cpu-reset.c b/arch/arm/mach-mvebu/cpu-reset.c new file mode 100644 index 000000000000..4ff4ce77f8b6 --- /dev/null +++ b/arch/arm/mach-mvebu/cpu-reset.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2014 Marvell + * + * Thomas Petazzoni + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#define pr_fmt(fmt) "mvebu-cpureset: " fmt + +#include +#include +#include +#include +#include +#include "armada-370-xp.h" + +static void __iomem *cpu_reset_base; +static size_t cpu_reset_size; + +#define CPU_RESET_OFFSET(cpu) (cpu * 0x8) +#define CPU_RESET_ASSERT BIT(0) + +int mvebu_cpu_reset_deassert(int cpu) +{ + u32 reg; + + if (!cpu_reset_base) + return -ENODEV; + + if (CPU_RESET_OFFSET(cpu) >= cpu_reset_size) + return -EINVAL; + + reg = readl(cpu_reset_base + CPU_RESET_OFFSET(cpu)); + reg &= ~CPU_RESET_ASSERT; + writel(reg, cpu_reset_base + CPU_RESET_OFFSET(cpu)); + + return 0; +} + +static int __init mvebu_cpu_reset_init(void) +{ + struct device_node *np; + struct resource res; + int ret = 0; + + np = of_find_compatible_node(NULL, NULL, + "marvell,armada-370-cpu-reset"); + if (!np) + return 0; + + if (of_address_to_resource(np, 0, &res)) { + pr_err("unable to get resource\n"); + ret = -ENOENT; + goto out; + } + + if (!request_mem_region(res.start, resource_size(&res), + np->full_name)) { + pr_err("unable to request region\n"); + ret = -EBUSY; + goto out; + } + + cpu_reset_base = ioremap(res.start, resource_size(&res)); + if (!cpu_reset_base) { + pr_err("unable to map registers\n"); + release_mem_region(res.start, resource_size(&res)); + ret = -ENOMEM; + goto out; + } + + cpu_reset_size = resource_size(&res); + +out: + of_node_put(np); + return ret; +} + +early_initcall(mvebu_cpu_reset_init); From 49754ffef5dca1d212e5fea5957a2a164585e92c Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 14 Apr 2014 15:50:29 +0200 Subject: [PATCH 11/46] ARM: mvebu: start using the CPU reset driver This commit changes the PMSU driver to no longer map itself the CPU reset registers, and instead call into the CPU reset driver to deassert the secondary CPUs for SMP booting. In order to provide Device Tree backward compatibility, the CPU reset driver is extended to not only support its official compatible string "marvell,armada-370-cpu-reset", but to also look at the PMSU compatible string "marvell,armada-370-xp-pmsu" to find the CPU reset registers address. This allows old Device Tree to work correctly with newer kernel versions. Therefore, the CPU reset driver implements the following logic: * If one of the normal compatible strings "marvell,armada-370-cpu-reset" is found, then we map its first memory resource as the CPU reset registers. * Otherwise, if none of the normal compatible strings have been found, we look for the "marvell,armada-370-xp-pmsu" compatible string, and we map the second memory as the CPU reset registers. Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1397483433-25836-3-git-send-email-thomas.petazzoni@free-electrons.com Acked-by: Gregory CLEMENT Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/cpu-reset.c | 53 +++++++++++++++++++++++---------- arch/arm/mach-mvebu/pmsu.c | 20 ++++++------- 2 files changed, 47 insertions(+), 26 deletions(-) diff --git a/arch/arm/mach-mvebu/cpu-reset.c b/arch/arm/mach-mvebu/cpu-reset.c index 4ff4ce77f8b6..4a8f9eebebea 100644 --- a/arch/arm/mach-mvebu/cpu-reset.c +++ b/arch/arm/mach-mvebu/cpu-reset.c @@ -40,42 +40,63 @@ int mvebu_cpu_reset_deassert(int cpu) return 0; } -static int __init mvebu_cpu_reset_init(void) +static int mvebu_cpu_reset_map(struct device_node *np, int res_idx) { - struct device_node *np; struct resource res; - int ret = 0; - np = of_find_compatible_node(NULL, NULL, - "marvell,armada-370-cpu-reset"); - if (!np) - return 0; - - if (of_address_to_resource(np, 0, &res)) { + if (of_address_to_resource(np, res_idx, &res)) { pr_err("unable to get resource\n"); - ret = -ENOENT; - goto out; + return -ENOENT; } if (!request_mem_region(res.start, resource_size(&res), np->full_name)) { pr_err("unable to request region\n"); - ret = -EBUSY; - goto out; + return -EBUSY; } cpu_reset_base = ioremap(res.start, resource_size(&res)); if (!cpu_reset_base) { pr_err("unable to map registers\n"); release_mem_region(res.start, resource_size(&res)); - ret = -ENOMEM; - goto out; + return -ENOMEM; } cpu_reset_size = resource_size(&res); -out: + return 0; +} + +int __init mvebu_cpu_reset_init(void) +{ + struct device_node *np; + int res_idx; + int ret; + + np = of_find_compatible_node(NULL, NULL, + "marvell,armada-370-cpu-reset"); + if (np) { + res_idx = 0; + } else { + /* + * This code is kept for backward compatibility with + * old Device Trees. + */ + np = of_find_compatible_node(NULL, NULL, + "marvell,armada-370-xp-pmsu"); + if (np) { + pr_warn(FW_WARN "deprecated pmsu binding\n"); + res_idx = 1; + } + } + + /* No reset node found */ + if (!np) + return -ENODEV; + + ret = mvebu_cpu_reset_map(np, res_idx); of_node_put(np); + return ret; } diff --git a/arch/arm/mach-mvebu/pmsu.c b/arch/arm/mach-mvebu/pmsu.c index d71ef53107c4..1807639173b1 100644 --- a/arch/arm/mach-mvebu/pmsu.c +++ b/arch/arm/mach-mvebu/pmsu.c @@ -21,14 +21,14 @@ #include #include #include +#include #include +#include "common.h" #include "pmsu.h" static void __iomem *pmsu_mp_base; -static void __iomem *pmsu_reset_base; #define PMSU_BOOT_ADDR_REDIRECT_OFFSET(cpu) ((cpu * 0x100) + 0x24) -#define PMSU_RESET_CTL_OFFSET(cpu) (cpu * 0x8) static struct of_device_id of_pmsu_table[] = { {.compatible = "marvell,armada-370-xp-pmsu"}, @@ -38,11 +38,11 @@ static struct of_device_id of_pmsu_table[] = { #ifdef CONFIG_SMP int armada_xp_boot_cpu(unsigned int cpu_id, void *boot_addr) { - int reg, hw_cpu; + int hw_cpu, ret; - if (!pmsu_mp_base || !pmsu_reset_base) { + if (!pmsu_mp_base) { pr_warn("Can't boot CPU. PMSU is uninitialized\n"); - return 1; + return -ENODEV; } hw_cpu = cpu_logical_map(cpu_id); @@ -50,10 +50,11 @@ int armada_xp_boot_cpu(unsigned int cpu_id, void *boot_addr) writel(virt_to_phys(boot_addr), pmsu_mp_base + PMSU_BOOT_ADDR_REDIRECT_OFFSET(hw_cpu)); - /* Release CPU from reset by clearing reset bit*/ - reg = readl(pmsu_reset_base + PMSU_RESET_CTL_OFFSET(hw_cpu)); - reg &= (~0x1); - writel(reg, pmsu_reset_base + PMSU_RESET_CTL_OFFSET(hw_cpu)); + ret = mvebu_cpu_reset_deassert(hw_cpu); + if (ret) { + pr_warn("unable to boot CPU: %d\n", ret); + return ret; + } return 0; } @@ -67,7 +68,6 @@ static int __init armada_370_xp_pmsu_init(void) if (np) { pr_info("Initializing Power Management Service Unit\n"); pmsu_mp_base = of_iomap(np, 0); - pmsu_reset_base = of_iomap(np, 1); of_node_put(np); } From bd045a1ebb48e5901508574188404d9bd3bdd72f Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 14 Apr 2014 15:50:30 +0200 Subject: [PATCH 12/46] ARM: mvebu: improve PMSU driver to request its resource Until now, the PMSU driver was using of_iomap() to map its registers, but of_iomap() doesn't call request_mem_region(). This commit fixes the memory mapping code of the PMSU to do so, which will also be useful for a later commit since we will need to adjust the resource base address and size for Device Tree backward compatibility. Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1397483433-25836-4-git-send-email-thomas.petazzoni@free-electrons.com Acked-by: Gregory CLEMENT Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/pmsu.c | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-mvebu/pmsu.c b/arch/arm/mach-mvebu/pmsu.c index 1807639173b1..b337fe56bae5 100644 --- a/arch/arm/mach-mvebu/pmsu.c +++ b/arch/arm/mach-mvebu/pmsu.c @@ -16,6 +16,8 @@ * other SOC units */ +#define pr_fmt(fmt) "mvebu-pmsu: " fmt + #include #include #include @@ -63,15 +65,39 @@ int armada_xp_boot_cpu(unsigned int cpu_id, void *boot_addr) static int __init armada_370_xp_pmsu_init(void) { struct device_node *np; + struct resource res; + int ret = 0; np = of_find_matching_node(NULL, of_pmsu_table); - if (np) { - pr_info("Initializing Power Management Service Unit\n"); - pmsu_mp_base = of_iomap(np, 0); - of_node_put(np); + if (!np) + return 0; + + pr_info("Initializing Power Management Service Unit\n"); + + if (of_address_to_resource(np, 0, &res)) { + pr_err("unable to get resource\n"); + ret = -ENOENT; + goto out; } - return 0; + if (!request_mem_region(res.start, resource_size(&res), + np->full_name)) { + pr_err("unable to request region\n"); + ret = -EBUSY; + goto out; + } + + pmsu_mp_base = ioremap(res.start, resource_size(&res)); + if (!pmsu_mp_base) { + pr_err("unable to map registers\n"); + release_mem_region(res.start, resource_size(&res)); + ret = -ENOMEM; + goto out; + } + + out: + of_node_put(np); + return ret; } early_initcall(armada_370_xp_pmsu_init); From 0c3acc746d2cd84c3654b6cc07eda2411584f4af Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Mon, 14 Apr 2014 15:50:31 +0200 Subject: [PATCH 13/46] ARM: mvebu: extend the PMSU registers The initial binding for PMSU was wrong, as it didn't take into account all the registers from the PMSU and moreover it referred to the CPU reset registers which are not part of PMSU. The Power Management Unit Service block also controls the Coherency Fabric subsystem. These registers are needed for the CPU idle implementation for the Armada 370/XP, it allows to enter a deep CPU idle state where the Coherency Fabric and the L2 cache are powered down. This commit adds support for a new compatible for the PMSU node which includes the registers related to the coherency fabric. It also keeps compatibility with the old compatible string. Signed-off-by: Gregory CLEMENT Link: https://lkml.kernel.org/r/1397483433-25836-5-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1397483433-25836-5-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- .../devicetree/bindings/arm/armada-370-xp-pmsu.txt | 14 ++++++-------- arch/arm/mach-mvebu/pmsu.c | 14 ++++++++++++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/armada-370-xp-pmsu.txt b/Documentation/devicetree/bindings/arm/armada-370-xp-pmsu.txt index 926b4d6aae7e..976188770079 100644 --- a/Documentation/devicetree/bindings/arm/armada-370-xp-pmsu.txt +++ b/Documentation/devicetree/bindings/arm/armada-370-xp-pmsu.txt @@ -4,17 +4,15 @@ Available on Marvell SOCs: Armada 370 and Armada XP Required properties: -- compatible: "marvell,armada-370-xp-pmsu" +- compatible: should be "marvell,armada-370-pmsu", whereas + "marvell,armada-370-xp-pmsu" is deprecated and will be removed -- reg: Should contain PMSU registers location and length. First pair - for the per-CPU SW Reset Control registers, second pair for the - Power Management Service Unit. +- reg: Should contain PMSU registers location and length. Example: -armada-370-xp-pmsu@d0022000 { - compatible = "marvell,armada-370-xp-pmsu"; - reg = <0xd0022100 0x430>, - <0xd0020800 0x20>; +armada-370-xp-pmsu@22000 { + compatible = "marvell,armada-370-pmsu"; + reg = <0x22000 0x1000>; }; diff --git a/arch/arm/mach-mvebu/pmsu.c b/arch/arm/mach-mvebu/pmsu.c index b337fe56bae5..4ae3ea1af7c3 100644 --- a/arch/arm/mach-mvebu/pmsu.c +++ b/arch/arm/mach-mvebu/pmsu.c @@ -30,10 +30,14 @@ static void __iomem *pmsu_mp_base; -#define PMSU_BOOT_ADDR_REDIRECT_OFFSET(cpu) ((cpu * 0x100) + 0x24) +#define PMSU_BASE_OFFSET 0x100 +#define PMSU_REG_SIZE 0x1000 + +#define PMSU_BOOT_ADDR_REDIRECT_OFFSET(cpu) ((cpu * 0x100) + 0x124) static struct of_device_id of_pmsu_table[] = { - {.compatible = "marvell,armada-370-xp-pmsu"}, + { .compatible = "marvell,armada-370-pmsu", }, + { .compatible = "marvell,armada-370-xp-pmsu", }, { /* end of list */ }, }; @@ -80,6 +84,12 @@ static int __init armada_370_xp_pmsu_init(void) goto out; } + if (of_device_is_compatible(np, "marvell,armada-370-xp-pmsu")) { + pr_warn(FW_WARN "deprecated pmsu binding\n"); + res.start = res.start - PMSU_BASE_OFFSET; + res.end = res.start + PMSU_REG_SIZE - 1; + } + if (!request_mem_region(res.start, resource_size(&res), np->full_name)) { pr_err("unable to request region\n"); From 02e7b06795fc129e45ed39983673efbb05d69506 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Mon, 14 Apr 2014 15:50:33 +0200 Subject: [PATCH 14/46] ARM: mvebu: use a separate function to set the boot address of CPUs Setting the start (or boot) address of a CPU is no more used only during SMP bring up on Armada 370/XP, but it will also be used by the CPU idle function of Armada XP, and by the Armada 38x SMP support. Therefore this commit creates a separate PMSU function to set the boot address of a CPU with the PMSU. Signed-off-by: Gregory CLEMENT Link: https://lkml.kernel.org/r/1397483433-25836-7-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1397483433-25836-7-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/pmsu.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-mvebu/pmsu.c b/arch/arm/mach-mvebu/pmsu.c index 4ae3ea1af7c3..8361281f9180 100644 --- a/arch/arm/mach-mvebu/pmsu.c +++ b/arch/arm/mach-mvebu/pmsu.c @@ -41,6 +41,12 @@ static struct of_device_id of_pmsu_table[] = { { /* end of list */ }, }; +static void mvebu_pmsu_set_cpu_boot_addr(int hw_cpu, void *boot_addr) +{ + writel(virt_to_phys(boot_addr), pmsu_mp_base + + PMSU_BOOT_ADDR_REDIRECT_OFFSET(hw_cpu)); +} + #ifdef CONFIG_SMP int armada_xp_boot_cpu(unsigned int cpu_id, void *boot_addr) { @@ -53,8 +59,7 @@ int armada_xp_boot_cpu(unsigned int cpu_id, void *boot_addr) hw_cpu = cpu_logical_map(cpu_id); - writel(virt_to_phys(boot_addr), pmsu_mp_base + - PMSU_BOOT_ADDR_REDIRECT_OFFSET(hw_cpu)); + mvebu_pmsu_set_cpu_boot_addr(hw_cpu, boot_addr); ret = mvebu_cpu_reset_deassert(hw_cpu); if (ret) { From ef37d337e1d37bd84ccaa5811a8d1f00f8b3677c Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 14 Apr 2014 15:54:01 +0200 Subject: [PATCH 15/46] irqchip: irq-armada-370-xp: Do the set_smp_cross_call() in the driver Instead of having the SoC code in arch/arm/mach-mvebu/platsmp.c do the set_smp_cross_call() to register the IPI-triggering function, it makes more sense to do exactly what the GIC driver is doing: let the irqchip driver do it. This way, it avoids having to expose the armada_mpic_send_doorbell() function between the irqchip driver and the SoC code. Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1397483648-26611-5-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/armada-370-xp.h | 1 - arch/arm/mach-mvebu/platsmp.c | 2 -- drivers/irqchip/irq-armada-370-xp.c | 6 +++++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-mvebu/armada-370-xp.h b/arch/arm/mach-mvebu/armada-370-xp.h index 237c86b83390..cd57c78af271 100644 --- a/arch/arm/mach-mvebu/armada-370-xp.h +++ b/arch/arm/mach-mvebu/armada-370-xp.h @@ -20,7 +20,6 @@ #define ARMADA_XP_MAX_CPUS 4 -void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq); void armada_xp_mpic_smp_cpu_init(void); void armada_xp_secondary_startup(void); extern struct smp_operations armada_xp_smp_ops; diff --git a/arch/arm/mach-mvebu/platsmp.c b/arch/arm/mach-mvebu/platsmp.c index a6da03f5b24e..6f06f6ddb51e 100644 --- a/arch/arm/mach-mvebu/platsmp.c +++ b/arch/arm/mach-mvebu/platsmp.c @@ -90,8 +90,6 @@ static void __init armada_xp_smp_init_cpus(void) if (ncores == 0 || ncores > ARMADA_XP_MAX_CPUS) panic("Invalid number of CPUs in DT\n"); - - set_smp_cross_call(armada_mpic_send_doorbell); } static void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus) diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index 41be897df8d5..727566216e24 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -315,7 +315,8 @@ static int armada_370_xp_mpic_irq_map(struct irq_domain *h, } #ifdef CONFIG_SMP -void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq) +static void armada_mpic_send_doorbell(const struct cpumask *mask, + unsigned int irq) { int cpu; unsigned long map = 0; @@ -511,6 +512,9 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, if (parent_irq <= 0) { irq_set_default_host(armada_370_xp_mpic_domain); set_handle_irq(armada_370_xp_handle_irq); +#ifdef CONFIG_SMP + set_smp_cross_call(armada_mpic_send_doorbell); +#endif } else { irq_set_chained_handler(parent_irq, armada_370_xp_mpic_handle_cascade_irq); From d7df84b3cecad4c768e4065d1d61b2f8fd02b7fa Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 14 Apr 2014 15:54:02 +0200 Subject: [PATCH 16/46] irqchip: irq-armada-370-xp: Use cpu notifier to initialize secondary CPUs Some irqchip initialization must be done on secondary CPUs. On mvebu platforms, this is currently achieved by having the arch/arm/mach-mvebu/platsmp.c code directly call into a function exported by the irqchip driver, which isn't really nice. This commit changes this by using the same solution as the one used in the GIC driver: the irqchip driver registers a CPU notifier, which is used to do the secondary CPU IRQ initialization. This way, the irqchip driver is completely autonomous, and the function no longer needs to be exposed from the irqchip driver to the SoC code. Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1397483648-26611-6-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/armada-370-xp.h | 1 - arch/arm/mach-mvebu/platsmp.c | 6 ------ drivers/irqchip/irq-armada-370-xp.c | 18 +++++++++++++++++- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-mvebu/armada-370-xp.h b/arch/arm/mach-mvebu/armada-370-xp.h index cd57c78af271..c3465f5b1250 100644 --- a/arch/arm/mach-mvebu/armada-370-xp.h +++ b/arch/arm/mach-mvebu/armada-370-xp.h @@ -20,7 +20,6 @@ #define ARMADA_XP_MAX_CPUS 4 -void armada_xp_mpic_smp_cpu_init(void); void armada_xp_secondary_startup(void); extern struct smp_operations armada_xp_smp_ops; #endif diff --git a/arch/arm/mach-mvebu/platsmp.c b/arch/arm/mach-mvebu/platsmp.c index 6f06f6ddb51e..e43727f391f7 100644 --- a/arch/arm/mach-mvebu/platsmp.c +++ b/arch/arm/mach-mvebu/platsmp.c @@ -70,11 +70,6 @@ static void __init set_secondary_cpus_clock(void) } } -static void armada_xp_secondary_init(unsigned int cpu) -{ - armada_xp_mpic_smp_cpu_init(); -} - static int armada_xp_boot_secondary(unsigned int cpu, struct task_struct *idle) { pr_info("Booting CPU %d\n", cpu); @@ -122,7 +117,6 @@ static void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus) struct smp_operations armada_xp_smp_ops __initdata = { .smp_init_cpus = armada_xp_smp_init_cpus, .smp_prepare_cpus = armada_xp_smp_prepare_cpus, - .smp_secondary_init = armada_xp_secondary_init, .smp_boot_secondary = armada_xp_boot_secondary, #ifdef CONFIG_HOTPLUG_CPU .cpu_die = armada_xp_cpu_die, diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c index 727566216e24..71f77848bc23 100644 --- a/drivers/irqchip/irq-armada-370-xp.c +++ b/drivers/irqchip/irq-armada-370-xp.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -336,7 +337,7 @@ static void armada_mpic_send_doorbell(const struct cpumask *mask, ARMADA_370_XP_SW_TRIG_INT_OFFS); } -void armada_xp_mpic_smp_cpu_init(void) +static void armada_xp_mpic_smp_cpu_init(void) { /* Clear pending IPIs */ writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS); @@ -348,6 +349,20 @@ void armada_xp_mpic_smp_cpu_init(void) /* Unmask IPI interrupt */ writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); } + +static int armada_xp_mpic_secondary_init(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) + armada_xp_mpic_smp_cpu_init(); + return NOTIFY_OK; +} + +static struct notifier_block armada_370_xp_mpic_cpu_notifier = { + .notifier_call = armada_xp_mpic_secondary_init, + .priority = 100, +}; + #endif /* CONFIG_SMP */ static struct irq_domain_ops armada_370_xp_mpic_irq_ops = { @@ -514,6 +529,7 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, set_handle_irq(armada_370_xp_handle_irq); #ifdef CONFIG_SMP set_smp_cross_call(armada_mpic_send_doorbell); + register_cpu_notifier(&armada_370_xp_mpic_cpu_notifier); #endif } else { irq_set_chained_handler(parent_irq, From bffbc6eabd0e48bba5415c4aec34cb75d459c73a Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Mon, 28 Apr 2014 23:12:08 +0200 Subject: [PATCH 17/46] irqchip: orion: Reverse irq handling priority Non-DT irq handlers were working through irq causes from most-significant to least-significant bit, while DT irqchip driver does it the other way round. This revealed some more HW issues on Kirkwood peripheral IP, where spurious sdio irqs can happen although irqs are masked. Also, the generated binaries show that original non-DT order compared to DT order save two instructions for each bit count check: irqchip DT order with ffs(): 60: e3a06001 mov r6, #1 64: e2643000 rsb r3, r4, #0 68: e0033004 and r3, r3, r4 6c: e16f3f13 clz r3, r3 70: e263301f rsb r3, r3, #31 74: e1c44316 bic r4, r4, r6, lsl r3 78: e5971004 ldr r1, [r7, #4] Original non-DT order with fls(): 60: e3a07001 mov r7, #1 64: e16f3f14 clz r3, r4 68: e263301f rsb r3, r3, #31 6c: e1c44317 bic r4, r4, r7, lsl r3 70: e5951004 ldr r1, [r5, #4] Therefore, reverse irq bit handling back to original order by replacing ffs() with fls(). Signed-off-by: Sebastian Hesselbarth Link: https://lkml.kernel.org/r/1398719528-23607-1-git-send-email-sebastian.hesselbarth@gmail.com Acked-by: Jason Cooper Signed-off-by: Jason Cooper --- drivers/irqchip/irq-orion.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-orion.c b/drivers/irqchip/irq-orion.c index e25f246cd2fb..34d18b48bb78 100644 --- a/drivers/irqchip/irq-orion.c +++ b/drivers/irqchip/irq-orion.c @@ -42,7 +42,7 @@ __exception_irq_entry orion_handle_irq(struct pt_regs *regs) u32 stat = readl_relaxed(gc->reg_base + ORION_IRQ_CAUSE) & gc->mask_cache; while (stat) { - u32 hwirq = ffs(stat) - 1; + u32 hwirq = __fls(stat); u32 irq = irq_find_mapping(orion_irq_domain, gc->irq_base + hwirq); handle_IRQ(irq, regs); @@ -117,7 +117,7 @@ static void orion_bridge_irq_handler(unsigned int irq, struct irq_desc *desc) gc->mask_cache; while (stat) { - u32 hwirq = ffs(stat) - 1; + u32 hwirq = __fls(stat); generic_handle_irq(irq_find_mapping(d, gc->irq_base + hwirq)); stat &= ~(1 << hwirq); From 05ad690608098b469d6052c700d96c35e697a955 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 14 Apr 2014 15:53:58 +0200 Subject: [PATCH 18/46] ARM: mvebu: move Armada XP specific SMP initialization to platsmp.c The pmsu.c driver contained an armada_xp_boot_cpu() function that sets the boot address of a secondary CPUs and deasserts the reset. However, the Armada 375 needs a slightly different logic, so it makes more sense to move this code into the Armada XP specific platsmp.c. In order to achieve this, the mvebu_pmsu_set_cpu_boot_addr() function is exported. It will be needed for both the Armada XP and Armada 38x SMP implementations. Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1397483648-26611-2-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/common.h | 1 + arch/arm/mach-mvebu/platsmp.c | 10 +++++++++- arch/arm/mach-mvebu/pmsu.c | 26 +------------------------- 3 files changed, 11 insertions(+), 26 deletions(-) diff --git a/arch/arm/mach-mvebu/common.h b/arch/arm/mach-mvebu/common.h index cfb129b144c0..2b88eb00de89 100644 --- a/arch/arm/mach-mvebu/common.h +++ b/arch/arm/mach-mvebu/common.h @@ -19,6 +19,7 @@ void mvebu_restart(enum reboot_mode mode, const char *cmd); int mvebu_cpu_reset_deassert(int cpu); +void mvebu_pmsu_set_cpu_boot_addr(int hw_cpu, void *boot_addr); void armada_xp_cpu_die(unsigned int cpu); diff --git a/arch/arm/mach-mvebu/platsmp.c b/arch/arm/mach-mvebu/platsmp.c index a6da03f5b24e..e98075f7175a 100644 --- a/arch/arm/mach-mvebu/platsmp.c +++ b/arch/arm/mach-mvebu/platsmp.c @@ -77,9 +77,17 @@ static void armada_xp_secondary_init(unsigned int cpu) static int armada_xp_boot_secondary(unsigned int cpu, struct task_struct *idle) { + int ret, hw_cpu; + pr_info("Booting CPU %d\n", cpu); - armada_xp_boot_cpu(cpu, armada_xp_secondary_startup); + hw_cpu = cpu_logical_map(cpu); + mvebu_pmsu_set_cpu_boot_addr(hw_cpu, armada_xp_secondary_startup); + ret = mvebu_cpu_reset_deassert(hw_cpu); + if (ret) { + pr_warn("unable to boot CPU: %d\n", ret); + return ret; + } return 0; } diff --git a/arch/arm/mach-mvebu/pmsu.c b/arch/arm/mach-mvebu/pmsu.c index 8361281f9180..a1508deecd26 100644 --- a/arch/arm/mach-mvebu/pmsu.c +++ b/arch/arm/mach-mvebu/pmsu.c @@ -41,36 +41,12 @@ static struct of_device_id of_pmsu_table[] = { { /* end of list */ }, }; -static void mvebu_pmsu_set_cpu_boot_addr(int hw_cpu, void *boot_addr) +void mvebu_pmsu_set_cpu_boot_addr(int hw_cpu, void *boot_addr) { writel(virt_to_phys(boot_addr), pmsu_mp_base + PMSU_BOOT_ADDR_REDIRECT_OFFSET(hw_cpu)); } -#ifdef CONFIG_SMP -int armada_xp_boot_cpu(unsigned int cpu_id, void *boot_addr) -{ - int hw_cpu, ret; - - if (!pmsu_mp_base) { - pr_warn("Can't boot CPU. PMSU is uninitialized\n"); - return -ENODEV; - } - - hw_cpu = cpu_logical_map(cpu_id); - - mvebu_pmsu_set_cpu_boot_addr(hw_cpu, boot_addr); - - ret = mvebu_cpu_reset_deassert(hw_cpu); - if (ret) { - pr_warn("unable to boot CPU: %d\n", ret); - return ret; - } - - return 0; -} -#endif - static int __init armada_370_xp_pmsu_init(void) { struct device_node *np; From 2c9b2240bee340711048589023eb057f23fda356 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 14 Apr 2014 15:53:59 +0200 Subject: [PATCH 19/46] ARM: mvebu: use CPU_METHOD_OF_DECLARE for SMP on Armada XP This commit adds the CPU_METHOD_OF_DECLARE declaration for the Armada XP SMP operations. Note that the .smp_ops field of Armada XP DT_MACHINE structure is kept, in order to ensure we remain compatible with older Device Trees that do not include the "enable-method" property for the CPUs. Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1397483648-26611-3-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- Documentation/devicetree/bindings/arm/cpus.txt | 1 + arch/arm/mach-mvebu/platsmp.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/cpus.txt b/Documentation/devicetree/bindings/arm/cpus.txt index 333f4aea3029..0dc59c36ad63 100644 --- a/Documentation/devicetree/bindings/arm/cpus.txt +++ b/Documentation/devicetree/bindings/arm/cpus.txt @@ -185,6 +185,7 @@ nodes to be present and contain the properties described below. "qcom,gcc-msm8660" "qcom,kpss-acc-v1" "qcom,kpss-acc-v2" + "marvell,armada-xp-smp" - cpu-release-addr Usage: required for systems that have an "enable-method" diff --git a/arch/arm/mach-mvebu/platsmp.c b/arch/arm/mach-mvebu/platsmp.c index e98075f7175a..e01ae51bee56 100644 --- a/arch/arm/mach-mvebu/platsmp.c +++ b/arch/arm/mach-mvebu/platsmp.c @@ -138,3 +138,6 @@ struct smp_operations armada_xp_smp_ops __initdata = { .cpu_die = armada_xp_cpu_die, #endif }; + +CPU_METHOD_OF_DECLARE(armada_xp_smp, "marvell,armada-xp-smp", + &armada_xp_smp_ops); From 00504be42add413614bdaa943612787d6fd297f5 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Mon, 14 Apr 2014 15:54:03 +0200 Subject: [PATCH 20/46] ARM: mvebu: add function to set the resume boot address for Armada 375 In order to boot the secondary CPUs on Armada 375, we need to set the boot address of these CPUs, through a register part of the System Controller (this deviates from the Armada XP design, where the boot address was defined using a register part of the PMSU unit). Therefore, this commit adds a new helper function in the System Controller driver to set the secondary CPU boot address. Moreover, it moves the System Controller initialization as an early_initcall(), since arch_initcall() is too late for an SMP-related initialization. Signed-off-by: Gregory CLEMENT Link: https://lkml.kernel.org/r/1397483648-26611-7-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1397483648-26611-7-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/common.h | 1 + arch/arm/mach-mvebu/system-controller.c | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-mvebu/common.h b/arch/arm/mach-mvebu/common.h index 2b88eb00de89..b67fb7a10d8b 100644 --- a/arch/arm/mach-mvebu/common.h +++ b/arch/arm/mach-mvebu/common.h @@ -20,6 +20,7 @@ void mvebu_restart(enum reboot_mode mode, const char *cmd); int mvebu_cpu_reset_deassert(int cpu); void mvebu_pmsu_set_cpu_boot_addr(int hw_cpu, void *boot_addr); +void mvebu_system_controller_set_cpu_boot_addr(void *boot_addr); void armada_xp_cpu_die(unsigned int cpu); diff --git a/arch/arm/mach-mvebu/system-controller.c b/arch/arm/mach-mvebu/system-controller.c index 614ba6832ff3..0c5524ac75b7 100644 --- a/arch/arm/mach-mvebu/system-controller.c +++ b/arch/arm/mach-mvebu/system-controller.c @@ -37,6 +37,8 @@ struct mvebu_system_controller { u32 rstoutn_mask_reset_out_en; u32 system_soft_reset; + + u32 resume_boot_addr; }; static struct mvebu_system_controller *mvebu_sc; @@ -52,6 +54,7 @@ static const struct mvebu_system_controller armada_375_system_controller = { .system_soft_reset_offset = 0x58, .rstoutn_mask_reset_out_en = 0x1, .system_soft_reset = 0x1, + .resume_boot_addr = 0xd4, }; static const struct mvebu_system_controller orion_system_controller = { @@ -98,6 +101,16 @@ void mvebu_restart(enum reboot_mode mode, const char *cmd) ; } +#ifdef CONFIG_SMP +void mvebu_system_controller_set_cpu_boot_addr(void *boot_addr) +{ + BUG_ON(system_controller_base == NULL); + BUG_ON(mvebu_sc->resume_boot_addr == 0); + writel(virt_to_phys(boot_addr), system_controller_base + + mvebu_sc->resume_boot_addr); +} +#endif + static int __init mvebu_system_controller_init(void) { const struct of_device_id *match; @@ -114,4 +127,4 @@ static int __init mvebu_system_controller_init(void) return 0; } -arch_initcall(mvebu_system_controller_init); +early_initcall(mvebu_system_controller_init); From 1ee89e2231a1b04dc34765edd195725ddf4ba998 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Mon, 14 Apr 2014 15:54:05 +0200 Subject: [PATCH 21/46] ARM: mvebu: add SMP support for Armada 375 and Armada 38x This commit adds the SMP support for Armada 375 and Armada 38x. It turns out that the SMP logic for both of these SOCs are fairly similar, the only differences being: * A different method to set the secondary CPU boot address * An Armada 375 specific workaround needed for the early Z1 stepping, added by the following patch. Other than that, the patch is fairly straightforward and adds the usual platsmp and headsmp code, defining the smp_operations structure that is referenced from the DT_MACHINE structures. Signed-off-by: Gregory CLEMENT Link: https://lkml.kernel.org/r/1397483648-26611-9-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1397483648-26611-9-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- .../devicetree/bindings/arm/cpus.txt | 2 + arch/arm/mach-mvebu/Kconfig | 4 ++ arch/arm/mach-mvebu/Makefile | 2 +- arch/arm/mach-mvebu/headsmp-a9.S | 22 ++++++ arch/arm/mach-mvebu/platsmp-a9.c | 68 +++++++++++++++++++ 5 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 arch/arm/mach-mvebu/headsmp-a9.S create mode 100644 arch/arm/mach-mvebu/platsmp-a9.c diff --git a/Documentation/devicetree/bindings/arm/cpus.txt b/Documentation/devicetree/bindings/arm/cpus.txt index 0dc59c36ad63..4bbcf4fb7583 100644 --- a/Documentation/devicetree/bindings/arm/cpus.txt +++ b/Documentation/devicetree/bindings/arm/cpus.txt @@ -185,6 +185,8 @@ nodes to be present and contain the properties described below. "qcom,gcc-msm8660" "qcom,kpss-acc-v1" "qcom,kpss-acc-v2" + "marvell,armada-375-smp" + "marvell,armada-380-smp" "marvell,armada-xp-smp" - cpu-release-addr diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig index 736bd2cbc1f0..7bb92b1a63fc 100644 --- a/arch/arm/mach-mvebu/Kconfig +++ b/arch/arm/mach-mvebu/Kconfig @@ -41,6 +41,8 @@ config MACH_ARMADA_375 select ARMADA_375_CLK select CPU_V7 select HAVE_ARM_SCU + select HAVE_ARM_TWD + select HAVE_SMP select MACH_MVEBU_V7 select PINCTRL_ARMADA_375 help @@ -55,6 +57,8 @@ config MACH_ARMADA_38X select ARMADA_38X_CLK select CPU_V7 select HAVE_ARM_SCU + select HAVE_ARM_TWD + select HAVE_SMP select MACH_MVEBU_V7 select PINCTRL_ARMADA_38X help diff --git a/arch/arm/mach-mvebu/Makefile b/arch/arm/mach-mvebu/Makefile index f9cfab05c5f9..02b5389f05fd 100644 --- a/arch/arm/mach-mvebu/Makefile +++ b/arch/arm/mach-mvebu/Makefile @@ -7,7 +7,7 @@ obj-y += system-controller.o mvebu-soc-id.o cpu-reset.o obj-$(CONFIG_MACH_MVEBU_V7) += board-v7.o obj-$(CONFIG_MACH_DOVE) += dove.o obj-$(CONFIG_ARCH_MVEBU) += coherency.o coherency_ll.o pmsu.o -obj-$(CONFIG_SMP) += platsmp.o headsmp.o +obj-$(CONFIG_SMP) += platsmp.o headsmp.o platsmp-a9.o headsmp-a9.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o obj-$(CONFIG_MACH_KIRKWOOD) += kirkwood.o kirkwood-pm.o obj-$(CONFIG_MACH_T5325) += board-t5325.o diff --git a/arch/arm/mach-mvebu/headsmp-a9.S b/arch/arm/mach-mvebu/headsmp-a9.S new file mode 100644 index 000000000000..78e66c96e718 --- /dev/null +++ b/arch/arm/mach-mvebu/headsmp-a9.S @@ -0,0 +1,22 @@ +/* + * SMP support: Entry point for secondary CPUs of Marvell EBU + * Cortex-A9 based SOCs (Armada 375 and Armada 38x). + * + * Copyright (C) 2014 Marvell + * + * Gregory CLEMENT + * Thomas Petazzoni + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include + + __CPUINIT +ENTRY(mvebu_cortex_a9_secondary_startup) + bl v7_invalidate_l1 + b secondary_startup +ENDPROC(mvebu_cortex_a9_secondary_startup) diff --git a/arch/arm/mach-mvebu/platsmp-a9.c b/arch/arm/mach-mvebu/platsmp-a9.c new file mode 100644 index 000000000000..27dacfc7fe98 --- /dev/null +++ b/arch/arm/mach-mvebu/platsmp-a9.c @@ -0,0 +1,68 @@ +/* + * Symmetric Multi Processing (SMP) support for Marvell EBU Cortex-A9 + * based SOCs (Armada 375/38x). + * + * Copyright (C) 2014 Marvell + * + * Gregory CLEMENT + * Thomas Petazzoni + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include "common.h" +#include "pmsu.h" + +extern void mvebu_cortex_a9_secondary_startup(void); + +static int __cpuinit mvebu_cortex_a9_boot_secondary(unsigned int cpu, + struct task_struct *idle) +{ + int ret, hw_cpu; + + pr_info("Booting CPU %d\n", cpu); + + /* + * Write the address of secondary startup into the system-wide + * flags register. The boot monitor waits until it receives a + * soft interrupt, and then the secondary CPU branches to this + * address. + */ + hw_cpu = cpu_logical_map(cpu); + + if (of_machine_is_compatible("marvell,armada375")) + mvebu_system_controller_set_cpu_boot_addr(mvebu_cortex_a9_secondary_startup); + else + mvebu_pmsu_set_cpu_boot_addr(hw_cpu, + mvebu_cortex_a9_secondary_startup); + + smp_wmb(); + ret = mvebu_cpu_reset_deassert(hw_cpu); + if (ret) { + pr_err("Could not start the secondary CPU: %d\n", ret); + return ret; + } + arch_send_wakeup_ipi_mask(cpumask_of(cpu)); + + return 0; +} + +static struct smp_operations mvebu_cortex_a9_smp_ops __initdata = { + .smp_boot_secondary = mvebu_cortex_a9_boot_secondary, +#ifdef CONFIG_HOTPLUG_CPU + .cpu_die = armada_xp_cpu_die, +#endif +}; + +CPU_METHOD_OF_DECLARE(mvebu_armada_375_smp, "marvell,armada-375-smp", + &mvebu_cortex_a9_smp_ops); +CPU_METHOD_OF_DECLARE(mvebu_armada_380_smp, "marvell,armada-380-smp", + &mvebu_cortex_a9_smp_ops); From 87384cc0b4bffbe26d9172d49d8b287332e9d397 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Mon, 14 Apr 2014 15:54:06 +0200 Subject: [PATCH 22/46] ARM: mvebu: add workaround for SMP support for Armada 375 stepping Z1 Due to internal bootrom issue, CPU[1] initial jump code (four instructions) should be placed in SRAM memory of the SoC. In order to achieve this, we have to unmap the BootROM and at some specific location where the BootROM was place, create a specific MBus window for the SRAM. This SRAM is initialized with a few instructions of code that allows to jump into the real secondary CPU boot address. This workaround will most likely be disabled when newer steppings of the Armada 375 will be made available, in which case a dynamic test based on mvebu-soc-id will be added. Signed-off-by: Gregory CLEMENT Link: https://lkml.kernel.org/r/1397483648-26611-10-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1397483648-26611-10-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/headsmp-a9.S | 12 ++++++++++++ arch/arm/mach-mvebu/platsmp-a9.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/arch/arm/mach-mvebu/headsmp-a9.S b/arch/arm/mach-mvebu/headsmp-a9.S index 78e66c96e718..5925366bc03c 100644 --- a/arch/arm/mach-mvebu/headsmp-a9.S +++ b/arch/arm/mach-mvebu/headsmp-a9.S @@ -16,6 +16,18 @@ #include __CPUINIT +#define CPU_RESUME_ADDR_REG 0xf10182d4 + +.global armada_375_smp_cpu1_enable_code_start +.global armada_375_smp_cpu1_enable_code_end + +armada_375_smp_cpu1_enable_code_start: + ldr r0, [pc, #4] + ldr r1, [r0] + mov pc, r1 + .word CPU_RESUME_ADDR_REG +armada_375_smp_cpu1_enable_code_end: + ENTRY(mvebu_cortex_a9_secondary_startup) bl v7_invalidate_l1 b secondary_startup diff --git a/arch/arm/mach-mvebu/platsmp-a9.c b/arch/arm/mach-mvebu/platsmp-a9.c index 27dacfc7fe98..04d0b1847c39 100644 --- a/arch/arm/mach-mvebu/platsmp-a9.c +++ b/arch/arm/mach-mvebu/platsmp-a9.c @@ -16,11 +16,36 @@ #include #include #include +#include #include #include #include "common.h" #include "pmsu.h" +#define CRYPT0_ENG_ID 41 +#define CRYPT0_ENG_ATTR 0x1 +#define SRAM_PHYS_BASE 0xFFFF0000 + +#define BOOTROM_BASE 0xFFF00000 +#define BOOTROM_SIZE 0x100000 + +extern unsigned char armada_375_smp_cpu1_enable_code_end; +extern unsigned char armada_375_smp_cpu1_enable_code_start; + +void armada_375_smp_cpu1_enable_wa(void) +{ + void __iomem *sram_virt_base; + + mvebu_mbus_del_window(BOOTROM_BASE, BOOTROM_SIZE); + mvebu_mbus_add_window_by_id(CRYPT0_ENG_ID, CRYPT0_ENG_ATTR, + SRAM_PHYS_BASE, SZ_64K); + sram_virt_base = ioremap(SRAM_PHYS_BASE, SZ_64K); + + memcpy(sram_virt_base, &armada_375_smp_cpu1_enable_code_start, + &armada_375_smp_cpu1_enable_code_end + - &armada_375_smp_cpu1_enable_code_start); +} + extern void mvebu_cortex_a9_secondary_startup(void); static int __cpuinit mvebu_cortex_a9_boot_secondary(unsigned int cpu, @@ -55,7 +80,14 @@ static int __cpuinit mvebu_cortex_a9_boot_secondary(unsigned int cpu, return 0; } +static void __init mvebu_cortex_a9_smp_prepare_cpus(unsigned int max_cpus) +{ + if (of_machine_is_compatible("marvell,armada375")) + armada_375_smp_cpu1_enable_wa(); +} + static struct smp_operations mvebu_cortex_a9_smp_ops __initdata = { + .smp_prepare_cpus = mvebu_cortex_a9_smp_prepare_cpus, .smp_boot_secondary = mvebu_cortex_a9_boot_secondary, #ifdef CONFIG_HOTPLUG_CPU .cpu_die = armada_xp_cpu_die, From b4bca24957c990a09dbddde75858b24150faec89 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 14 Apr 2014 15:54:04 +0200 Subject: [PATCH 23/46] ARM: mvebu: add Armada 38x compatible string to pmsu Since the Armada 38x PMSU registers are slightly different than the Armada 370/XP PMSU ones, we introduce a new compatible string "armada-380-pmsu" in the PMSU driver. These differences are not visible for the current usage of the PMSU, but they might become visible in the future. Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1397483648-26611-8-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- .../devicetree/bindings/arm/armada-370-xp-pmsu.txt | 9 ++++++--- arch/arm/mach-mvebu/pmsu.c | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/armada-370-xp-pmsu.txt b/Documentation/devicetree/bindings/arm/armada-370-xp-pmsu.txt index 976188770079..26799ef562df 100644 --- a/Documentation/devicetree/bindings/arm/armada-370-xp-pmsu.txt +++ b/Documentation/devicetree/bindings/arm/armada-370-xp-pmsu.txt @@ -1,11 +1,14 @@ Power Management Service Unit(PMSU) ----------------------------------- -Available on Marvell SOCs: Armada 370 and Armada XP +Available on Marvell SOCs: Armada 370, Armada 38x and Armada XP Required properties: -- compatible: should be "marvell,armada-370-pmsu", whereas - "marvell,armada-370-xp-pmsu" is deprecated and will be removed +- compatible: should be one of: + - "marvell,armada-370-pmsu" for Armada 370 or Armada XP + - "marvell,armada-380-pmsu" for Armada 38x + - "marvell,armada-370-xp-pmsu" was used for Armada 370/XP but is now + deprecated and will be removed - reg: Should contain PMSU registers location and length. diff --git a/arch/arm/mach-mvebu/pmsu.c b/arch/arm/mach-mvebu/pmsu.c index a1508deecd26..a8796b832e2e 100644 --- a/arch/arm/mach-mvebu/pmsu.c +++ b/arch/arm/mach-mvebu/pmsu.c @@ -38,6 +38,7 @@ static void __iomem *pmsu_mp_base; static struct of_device_id of_pmsu_table[] = { { .compatible = "marvell,armada-370-pmsu", }, { .compatible = "marvell,armada-370-xp-pmsu", }, + { .compatible = "marvell,armada-380-pmsu", }, { /* end of list */ }, }; From ccd6a13180193700067bfdac5f7d7b436d757382 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Mon, 14 Apr 2014 17:10:05 +0200 Subject: [PATCH 24/46] ARM: mvebu: remove the address parameter for ll_set_cpu_coherent In order to be able to deal with the MMU enabled and the MMU disabled cases, the base address of the coherency registers was passed to the function. The address by itself was not interesting as it can't change for a given SoC, the only thing we need is to have a distinction between the physical or the virtual address. This patch add a check of the MMU bit to choose the accurate address, then the calling function doesn't have to pass this information. Signed-off-by: Gregory CLEMENT Link: https://lkml.kernel.org/r/1397488214-20685-3-git-send-email-gregory.clement@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/coherency.c | 6 +++--- arch/arm/mach-mvebu/coherency_ll.S | 22 ++++++++++++++++++++-- arch/arm/mach-mvebu/headsmp.S | 9 --------- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c index 489edd157cfb..615dc0b6093e 100644 --- a/arch/arm/mach-mvebu/coherency.c +++ b/arch/arm/mach-mvebu/coherency.c @@ -35,7 +35,7 @@ #include "coherency.h" unsigned long coherency_phys_base; -static void __iomem *coherency_base; +void __iomem *coherency_base; static void __iomem *coherency_cpu_base; /* Coherency fabric registers */ @@ -61,7 +61,7 @@ static struct of_device_id of_coherency_table[] = { }; /* Function defined in coherency_ll.S */ -int ll_set_cpu_coherent(void __iomem *base_addr, unsigned int hw_cpu_id); +int ll_set_cpu_coherent(unsigned int hw_cpu_id); int set_cpu_coherent(unsigned int hw_cpu_id, int smp_group_id) { @@ -71,7 +71,7 @@ int set_cpu_coherent(unsigned int hw_cpu_id, int smp_group_id) return 1; } - return ll_set_cpu_coherent(coherency_base, hw_cpu_id); + return ll_set_cpu_coherent(hw_cpu_id); } /* diff --git a/arch/arm/mach-mvebu/coherency_ll.S b/arch/arm/mach-mvebu/coherency_ll.S index ee7598fe75db..1f2bcd4b5424 100644 --- a/arch/arm/mach-mvebu/coherency_ll.S +++ b/arch/arm/mach-mvebu/coherency_ll.S @@ -21,13 +21,27 @@ #define ARMADA_XP_CFB_CFG_REG_OFFSET 0x4 #include +#include .text /* - * r0: Coherency fabric base register address - * r1: HW CPU id + * r0: HW CPU id */ ENTRY(ll_set_cpu_coherent) + mrc p15, 0, r1, c1, c0, 0 + tst r1, #CR_M @ Check MMU bit enabled + bne 1f + + /* use physical address of the coherency register*/ + adr r0, 3f + ldr r3, [r0] + ldr r0, [r0, r3] + b 2f +1: + /* use virtual address of the coherency register*/ + ldr r0, =coherency_base + ldr r0, [r0] +2: /* Create bit by cpu index */ mov r3, #(1 << 24) lsl r1, r3, r1 @@ -56,3 +70,7 @@ ARM_BE8(rev r1, r1) mov r0, #0 mov pc, lr ENDPROC(ll_set_cpu_coherent) + + .align 2 +3: + .long coherency_phys_base - . diff --git a/arch/arm/mach-mvebu/headsmp.S b/arch/arm/mach-mvebu/headsmp.S index 3dd80df428f7..f30bc8d78871 100644 --- a/arch/arm/mach-mvebu/headsmp.S +++ b/arch/arm/mach-mvebu/headsmp.S @@ -31,11 +31,6 @@ ENTRY(armada_xp_secondary_startup) ARM_BE8(setend be ) @ go BE8 if entered LE - /* Get coherency fabric base physical address */ - adr r0, 1f - ldr r1, [r0] - ldr r0, [r0, r1] - /* Read CPU id */ mrc p15, 0, r1, c0, c0, 5 and r1, r1, #0xF @@ -45,7 +40,3 @@ ENTRY(armada_xp_secondary_startup) b secondary_startup ENDPROC(armada_xp_secondary_startup) - - .align 2 -1: - .long coherency_phys_base - . From b41375f71aceadb7d74a18aafba4da5024fc104b Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Mon, 14 Apr 2014 17:10:06 +0200 Subject: [PATCH 25/46] ARM: mvebu: ll_set_cpu_coherent always uses the current CPU ll_set_cpu_coherent is always used on the current CPU, so instead of passing the CPU id as argument, ll_set_cpu_coherent() can find it by itself. Signed-off-by: Gregory CLEMENT Link: https://lkml.kernel.org/r/1397488214-20685-4-git-send-email-gregory.clement@free-electrons.com Acked-by: Thomas Petazzoni Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/coherency.c | 12 +++++------- arch/arm/mach-mvebu/coherency.h | 2 +- arch/arm/mach-mvebu/coherency_ll.S | 10 +++++----- arch/arm/mach-mvebu/headsmp.S | 4 ---- arch/arm/mach-mvebu/platsmp.c | 2 +- 5 files changed, 12 insertions(+), 18 deletions(-) diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c index 615dc0b6093e..2df90c998820 100644 --- a/arch/arm/mach-mvebu/coherency.c +++ b/arch/arm/mach-mvebu/coherency.c @@ -61,17 +61,17 @@ static struct of_device_id of_coherency_table[] = { }; /* Function defined in coherency_ll.S */ -int ll_set_cpu_coherent(unsigned int hw_cpu_id); +int ll_set_cpu_coherent(void); -int set_cpu_coherent(unsigned int hw_cpu_id, int smp_group_id) +int set_cpu_coherent(int smp_group_id) { if (!coherency_base) { - pr_warn("Can't make CPU %d cache coherent.\n", hw_cpu_id); + pr_warn("Can't make current CPU cache coherent.\n"); pr_warn("Coherency fabric is not initialized\n"); return 1; } - return ll_set_cpu_coherent(hw_cpu_id); + return ll_set_cpu_coherent(); } /* @@ -302,7 +302,7 @@ static void __init armada_370_coherency_init(struct device_node *np) sync_cache_w(&coherency_phys_base); coherency_base = of_iomap(np, 0); coherency_cpu_base = of_iomap(np, 1); - set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0); + set_cpu_coherent(0); } static void __init armada_375_380_coherency_init(struct device_node *np) @@ -330,8 +330,6 @@ static int coherency_type(void) /* Armada 380 coherency works only on SMP */ else if (type == COHERENCY_FABRIC_TYPE_ARMADA_380 && is_smp()) return type; - - of_node_put(np); } return COHERENCY_FABRIC_TYPE_NONE; diff --git a/arch/arm/mach-mvebu/coherency.h b/arch/arm/mach-mvebu/coherency.h index 02f7ec34ced2..ab594a75fef3 100644 --- a/arch/arm/mach-mvebu/coherency.h +++ b/arch/arm/mach-mvebu/coherency.h @@ -16,7 +16,7 @@ extern unsigned long coherency_phys_base; -int set_cpu_coherent(unsigned int cpu_id, int smp_group_id); +int set_cpu_coherent(int smp_group_id); int coherency_init(void); int coherency_available(void); diff --git a/arch/arm/mach-mvebu/coherency_ll.S b/arch/arm/mach-mvebu/coherency_ll.S index 1f2bcd4b5424..98a0b73f909b 100644 --- a/arch/arm/mach-mvebu/coherency_ll.S +++ b/arch/arm/mach-mvebu/coherency_ll.S @@ -24,9 +24,7 @@ #include .text -/* - * r0: HW CPU id - */ + ENTRY(ll_set_cpu_coherent) mrc p15, 0, r1, c1, c0, 0 tst r1, #CR_M @ Check MMU bit enabled @@ -43,8 +41,10 @@ ENTRY(ll_set_cpu_coherent) ldr r0, [r0] 2: /* Create bit by cpu index */ - mov r3, #(1 << 24) - lsl r1, r3, r1 + mrc 15, 0, r1, cr0, cr0, 5 + and r1, r1, #15 + mov r2, #(1 << 24) + lsl r1, r2, r1 ARM_BE8(rev r1, r1) /* Add CPU to SMP group - Atomic */ diff --git a/arch/arm/mach-mvebu/headsmp.S b/arch/arm/mach-mvebu/headsmp.S index f30bc8d78871..cf7abe6554f7 100644 --- a/arch/arm/mach-mvebu/headsmp.S +++ b/arch/arm/mach-mvebu/headsmp.S @@ -31,10 +31,6 @@ ENTRY(armada_xp_secondary_startup) ARM_BE8(setend be ) @ go BE8 if entered LE - /* Read CPU id */ - mrc p15, 0, r1, c0, c0, 5 - and r1, r1, #0xF - /* Add CPU to coherency fabric */ bl ll_set_cpu_coherent b secondary_startup diff --git a/arch/arm/mach-mvebu/platsmp.c b/arch/arm/mach-mvebu/platsmp.c index 32bf78e80c48..75436c0023a8 100644 --- a/arch/arm/mach-mvebu/platsmp.c +++ b/arch/arm/mach-mvebu/platsmp.c @@ -103,7 +103,7 @@ static void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus) set_secondary_cpus_clock(); flush_cache_all(); - set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0); + set_cpu_coherent(0); /* * In order to boot the secondary CPUs we need to ensure From 952f4ca79b4ec7114291aa711add1b36c6ba7515 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Mon, 14 Apr 2014 17:10:07 +0200 Subject: [PATCH 26/46] ARM: mvebu: Remove the unused argument of set_cpu_coherent() set_cpu_coherent() took the SMP group ID as parameter. But this parameter was never used, and the CPU always uses the SMP group 0. So we can remove this parameter. Signed-off-by: Gregory CLEMENT Link: https://lkml.kernel.org/r/1397488214-20685-5-git-send-email-gregory.clement@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/coherency.c | 4 ++-- arch/arm/mach-mvebu/coherency.h | 2 +- arch/arm/mach-mvebu/platsmp.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c index 2df90c998820..944372a18ed6 100644 --- a/arch/arm/mach-mvebu/coherency.c +++ b/arch/arm/mach-mvebu/coherency.c @@ -63,7 +63,7 @@ static struct of_device_id of_coherency_table[] = { /* Function defined in coherency_ll.S */ int ll_set_cpu_coherent(void); -int set_cpu_coherent(int smp_group_id) +int set_cpu_coherent(void) { if (!coherency_base) { pr_warn("Can't make current CPU cache coherent.\n"); @@ -302,7 +302,7 @@ static void __init armada_370_coherency_init(struct device_node *np) sync_cache_w(&coherency_phys_base); coherency_base = of_iomap(np, 0); coherency_cpu_base = of_iomap(np, 1); - set_cpu_coherent(0); + set_cpu_coherent(); } static void __init armada_375_380_coherency_init(struct device_node *np) diff --git a/arch/arm/mach-mvebu/coherency.h b/arch/arm/mach-mvebu/coherency.h index ab594a75fef3..54cb7607b526 100644 --- a/arch/arm/mach-mvebu/coherency.h +++ b/arch/arm/mach-mvebu/coherency.h @@ -15,8 +15,8 @@ #define __MACH_370_XP_COHERENCY_H extern unsigned long coherency_phys_base; +int set_cpu_coherent(void); -int set_cpu_coherent(int smp_group_id); int coherency_init(void); int coherency_available(void); diff --git a/arch/arm/mach-mvebu/platsmp.c b/arch/arm/mach-mvebu/platsmp.c index 75436c0023a8..88b976b31719 100644 --- a/arch/arm/mach-mvebu/platsmp.c +++ b/arch/arm/mach-mvebu/platsmp.c @@ -103,7 +103,7 @@ static void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus) set_secondary_cpus_clock(); flush_cache_all(); - set_cpu_coherent(0); + set_cpu_coherent(); /* * In order to boot the secondary CPUs we need to ensure From 2e8a5942f8751c03fdd50228a02909654d13f01d Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Mon, 14 Apr 2014 17:10:08 +0200 Subject: [PATCH 27/46] ARM: mvebu: Split low level functions to manipulate HW coherency Actually enabling coherency and adding a CPU on a SMP group are two different operations which can be done separately. This patch splits this in two functions. Moreover as they use common pattern, this patch also creates local low level functions (ll_get_coherency_base and ll_get_cpuid) to be used by the exposed functions (ll_add_cpu_to_smp_group and ll_enable_coherency) Signed-off-by: Gregory CLEMENT Link: https://lkml.kernel.org/r/1397488214-20685-6-git-send-email-gregory.clement@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/coherency.c | 8 ++- arch/arm/mach-mvebu/coherency_ll.S | 92 ++++++++++++++++++++---------- arch/arm/mach-mvebu/headsmp.S | 6 +- 3 files changed, 71 insertions(+), 35 deletions(-) diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c index 944372a18ed6..9d5ccd372712 100644 --- a/arch/arm/mach-mvebu/coherency.c +++ b/arch/arm/mach-mvebu/coherency.c @@ -60,8 +60,9 @@ static struct of_device_id of_coherency_table[] = { { /* end of list */ }, }; -/* Function defined in coherency_ll.S */ -int ll_set_cpu_coherent(void); +/* Functions defined in coherency_ll.S */ +int ll_enable_coherency(void); +void ll_add_cpu_to_smp_group(void); int set_cpu_coherent(void) { @@ -71,7 +72,8 @@ int set_cpu_coherent(void) return 1; } - return ll_set_cpu_coherent(); + ll_add_cpu_to_smp_group(); + return ll_enable_coherency(); } /* diff --git a/arch/arm/mach-mvebu/coherency_ll.S b/arch/arm/mach-mvebu/coherency_ll.S index 98a0b73f909b..f2e2e8677c4b 100644 --- a/arch/arm/mach-mvebu/coherency_ll.S +++ b/arch/arm/mach-mvebu/coherency_ll.S @@ -24,52 +24,84 @@ #include .text - -ENTRY(ll_set_cpu_coherent) +/* Returns with the coherency address in r1 (r0 is untouched)*/ +ENTRY(ll_get_coherency_base) mrc p15, 0, r1, c1, c0, 0 tst r1, #CR_M @ Check MMU bit enabled bne 1f - /* use physical address of the coherency register*/ - adr r0, 3f - ldr r3, [r0] - ldr r0, [r0, r3] + /* use physical address of the coherency register */ + adr r1, 3f + ldr r3, [r1] + ldr r1, [r1, r3] b 2f 1: - /* use virtual address of the coherency register*/ - ldr r0, =coherency_base - ldr r0, [r0] + /* use virtual address of the coherency register */ + ldr r1, =coherency_base + ldr r1, [r1] 2: - /* Create bit by cpu index */ - mrc 15, 0, r1, cr0, cr0, 5 - and r1, r1, #15 + mov pc, lr +ENDPROC(ll_get_coherency_base) + +/* Returns with the CPU ID in r3 (r0 is untouched)*/ +ENTRY(ll_get_cpuid) + mrc 15, 0, r3, cr0, cr0, 5 + and r3, r3, #15 mov r2, #(1 << 24) - lsl r1, r2, r1 + lsl r3, r2, r3 ARM_BE8(rev r1, r1) + mov pc, lr +ENDPROC(ll_get_cpuid) - /* Add CPU to SMP group - Atomic */ - add r3, r0, #ARMADA_XP_CFB_CTL_REG_OFFSET +/* ll_add_cpu_to_smp_group, ll_enable_coherency and + * ll_disable_coherency use strex/ldrex whereas MMU can be off. The + * Armada XP SoC has an exclusive monitor that can track transactions + * to Device and/or SO and as such also when MMU is disabled the + * exclusive transactions will be functional + */ + +ENTRY(ll_add_cpu_to_smp_group) + /* + * r0 being untouched in ll_get_coherency_base and + * ll_get_cpuid, we can use it to save lr modifing it with the + * following bl + */ + mov r0, lr + bl ll_get_coherency_base + bl ll_get_cpuid + mov lr, r0 + add r0, r1, #ARMADA_XP_CFB_CFG_REG_OFFSET 1: - ldrex r2, [r3] - orr r2, r2, r1 - strex r0, r2, [r3] - cmp r0, #0 - bne 1b + ldrex r2, [r0] + orr r2, r2, r3 + strex r1, r2, [r0] + cmp r1, #0 + bne 1b + mov pc, lr +ENDPROC(ll_add_cpu_to_smp_group) - /* Enable coherency on CPU - Atomic */ - add r3, r3, #ARMADA_XP_CFB_CFG_REG_OFFSET +ENTRY(ll_enable_coherency) + /* + * r0 being untouched in ll_get_coherency_base and + * ll_get_cpuid, we can use it to save lr modifing it with the + * following bl + */ + mov r0, lr + bl ll_get_coherency_base + bl ll_get_cpuid + mov lr, r0 + add r0, r1, #ARMADA_XP_CFB_CTL_REG_OFFSET 1: - ldrex r2, [r3] - orr r2, r2, r1 - strex r0, r2, [r3] - cmp r0, #0 - bne 1b - + ldrex r2, [r0] + orr r2, r2, r3 + strex r1, r2, [r0] + cmp r1, #0 + bne 1b dsb - mov r0, #0 mov pc, lr -ENDPROC(ll_set_cpu_coherent) +ENDPROC(ll_enable_coherency) + .align 2 3: diff --git a/arch/arm/mach-mvebu/headsmp.S b/arch/arm/mach-mvebu/headsmp.S index cf7abe6554f7..2c4032e368ba 100644 --- a/arch/arm/mach-mvebu/headsmp.S +++ b/arch/arm/mach-mvebu/headsmp.S @@ -31,8 +31,10 @@ ENTRY(armada_xp_secondary_startup) ARM_BE8(setend be ) @ go BE8 if entered LE - /* Add CPU to coherency fabric */ - bl ll_set_cpu_coherent + bl ll_add_cpu_to_smp_group + + bl ll_enable_coherency + b secondary_startup ENDPROC(armada_xp_secondary_startup) From 1a6bfbc339b6a2b59a8f88fa494fe70073cdb85a Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Mon, 14 Apr 2014 17:10:09 +0200 Subject: [PATCH 28/46] ARM: mvebu: Low level function to disable HW coherency support When going to deep idle we need to disable the SoC snooping (aka hardware coherency support). Playing with the coherency fabric requires to use assembly code to be sure that the compiler doesn't reorder the instructions nor do wrong optimization. Signed-off-by: Gregory CLEMENT Link: https://lkml.kernel.org/r/1397488214-20685-7-git-send-email-gregory.clement@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/coherency_ll.S | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm/mach-mvebu/coherency_ll.S b/arch/arm/mach-mvebu/coherency_ll.S index f2e2e8677c4b..6828f9f157b0 100644 --- a/arch/arm/mach-mvebu/coherency_ll.S +++ b/arch/arm/mach-mvebu/coherency_ll.S @@ -102,6 +102,26 @@ ENTRY(ll_enable_coherency) mov pc, lr ENDPROC(ll_enable_coherency) +ENTRY(ll_disable_coherency) + /* + * r0 being untouched in ll_get_coherency_base and + * ll_get_cpuid, we can use it to save lr modifing it with the + * following bl + */ + mov r0, lr + bl ll_get_coherency_base + bl ll_get_cpuid + mov lr, r0 + add r0, r1, #ARMADA_XP_CFB_CTL_REG_OFFSET +1: + ldrex r2, [r0] + bic r2, r2, r3 + strex r1, r2, [r0] + cmp r1, #0 + bne 1b + dsb + mov pc, lr +ENDPROC(ll_disable_coherency) .align 2 3: From f713c7e7421d6945c977c8d8813e8089f925de41 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Mon, 14 Apr 2014 17:10:10 +0200 Subject: [PATCH 29/46] ARM: mvebu: Allow to power down L2 cache controller in idle mode This commit adds a function which adjusts the PMSU configuration to automatically power down the L2 and coherency fabric when we enter a certain idle state. This feature is part of the Power Management Service Unit of the Armada 370 and Armada XP SoCs. Signed-off-by: Gregory CLEMENT Link: https://lkml.kernel.org/r/1397488214-20685-8-git-send-email-gregory.clement@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/pmsu.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-mvebu/pmsu.c b/arch/arm/mach-mvebu/pmsu.c index a8796b832e2e..7ccf14a065b1 100644 --- a/arch/arm/mach-mvebu/pmsu.c +++ b/arch/arm/mach-mvebu/pmsu.c @@ -33,7 +33,12 @@ static void __iomem *pmsu_mp_base; #define PMSU_BASE_OFFSET 0x100 #define PMSU_REG_SIZE 0x1000 -#define PMSU_BOOT_ADDR_REDIRECT_OFFSET(cpu) ((cpu * 0x100) + 0x124) +/* PMSU MP registers */ +#define PMSU_BOOT_ADDR_REDIRECT_OFFSET(cpu) ((cpu * 0x100) + 0x124) + +/* PMSU fabric registers */ +#define L2C_NFABRIC_PM_CTL 0x4 +#define L2C_NFABRIC_PM_CTL_PWR_DOWN BIT(20) static struct of_device_id of_pmsu_table[] = { { .compatible = "marvell,armada-370-pmsu", }, @@ -92,4 +97,17 @@ static int __init armada_370_xp_pmsu_init(void) return ret; } +static void armada_370_xp_pmsu_enable_l2_powerdown_onidle(void) +{ + u32 reg; + + if (pmsu_mp_base == NULL) + return; + + /* Enable L2 & Fabric powerdown in Deep-Idle mode - Fabric */ + reg = readl(pmsu_mp_base + L2C_NFABRIC_PM_CTL); + reg |= L2C_NFABRIC_PM_CTL_PWR_DOWN; + writel(reg, pmsu_mp_base + L2C_NFABRIC_PM_CTL); +} + early_initcall(armada_370_xp_pmsu_init); From c3e04cabb135625df8ff4b71ef4130f0ccbcc669 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Mon, 14 Apr 2014 17:10:11 +0200 Subject: [PATCH 30/46] ARM: mvebu: Add the PMSU related part of the cpu idle functions The cpu idle support will need to access to Power Management Service Unit. This commit adds the architecture related functions that will be used in the idle path of the cpuidle driver. Signed-off-by: Gregory CLEMENT Link: https://lkml.kernel.org/r/1397488214-20685-9-git-send-email-gregory.clement@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/pmsu.c | 132 ++++++++++++++++++++++++++++++++++++- 1 file changed, 131 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-mvebu/pmsu.c b/arch/arm/mach-mvebu/pmsu.c index 7ccf14a065b1..78cf0279e1af 100644 --- a/arch/arm/mach-mvebu/pmsu.c +++ b/arch/arm/mach-mvebu/pmsu.c @@ -24,9 +24,12 @@ #include #include #include +#include +#include #include +#include +#include #include "common.h" -#include "pmsu.h" static void __iomem *pmsu_mp_base; @@ -34,12 +37,33 @@ static void __iomem *pmsu_mp_base; #define PMSU_REG_SIZE 0x1000 /* PMSU MP registers */ +#define PMSU_CONTROL_AND_CONFIG(cpu) ((cpu * 0x100) + 0x104) +#define PMSU_CONTROL_AND_CONFIG_DFS_REQ BIT(18) +#define PMSU_CONTROL_AND_CONFIG_PWDDN_REQ BIT(16) +#define PMSU_CONTROL_AND_CONFIG_L2_PWDDN BIT(20) + +#define PMSU_CPU_POWER_DOWN_CONTROL(cpu) ((cpu * 0x100) + 0x108) + +#define PMSU_CPU_POWER_DOWN_DIS_SNP_Q_SKIP BIT(0) + +#define PMSU_STATUS_AND_MASK(cpu) ((cpu * 0x100) + 0x10c) +#define PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT BIT(16) +#define PMSU_STATUS_AND_MASK_SNP_Q_EMPTY_WAIT BIT(17) +#define PMSU_STATUS_AND_MASK_IRQ_WAKEUP BIT(20) +#define PMSU_STATUS_AND_MASK_FIQ_WAKEUP BIT(21) +#define PMSU_STATUS_AND_MASK_DBG_WAKEUP BIT(22) +#define PMSU_STATUS_AND_MASK_IRQ_MASK BIT(24) +#define PMSU_STATUS_AND_MASK_FIQ_MASK BIT(25) + #define PMSU_BOOT_ADDR_REDIRECT_OFFSET(cpu) ((cpu * 0x100) + 0x124) /* PMSU fabric registers */ #define L2C_NFABRIC_PM_CTL 0x4 #define L2C_NFABRIC_PM_CTL_PWR_DOWN BIT(20) +extern void ll_disable_coherency(void); +extern void ll_enable_coherency(void); + static struct of_device_id of_pmsu_table[] = { { .compatible = "marvell,armada-370-pmsu", }, { .compatible = "marvell,armada-370-xp-pmsu", }, @@ -110,4 +134,110 @@ static void armada_370_xp_pmsu_enable_l2_powerdown_onidle(void) writel(reg, pmsu_mp_base + L2C_NFABRIC_PM_CTL); } +static void armada_370_xp_cpu_resume(void) +{ + asm volatile("bl ll_add_cpu_to_smp_group\n\t" + "bl ll_enable_coherency\n\t" + "b cpu_resume\n\t"); +} + +/* No locking is needed because we only access per-CPU registers */ +void armada_370_xp_pmsu_idle_prepare(bool deepidle) +{ + unsigned int hw_cpu = cpu_logical_map(smp_processor_id()); + u32 reg; + + if (pmsu_mp_base == NULL) + return; + + /* + * Adjust the PMSU configuration to wait for WFI signal, enable + * IRQ and FIQ as wakeup events, set wait for snoop queue empty + * indication and mask IRQ and FIQ from CPU + */ + reg = readl(pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu)); + reg |= PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT | + PMSU_STATUS_AND_MASK_IRQ_WAKEUP | + PMSU_STATUS_AND_MASK_FIQ_WAKEUP | + PMSU_STATUS_AND_MASK_SNP_Q_EMPTY_WAIT | + PMSU_STATUS_AND_MASK_IRQ_MASK | + PMSU_STATUS_AND_MASK_FIQ_MASK; + writel(reg, pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu)); + + reg = readl(pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu)); + /* ask HW to power down the L2 Cache if needed */ + if (deepidle) + reg |= PMSU_CONTROL_AND_CONFIG_L2_PWDDN; + + /* request power down */ + reg |= PMSU_CONTROL_AND_CONFIG_PWDDN_REQ; + writel(reg, pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu)); + + /* Disable snoop disable by HW - SW is taking care of it */ + reg = readl(pmsu_mp_base + PMSU_CPU_POWER_DOWN_CONTROL(hw_cpu)); + reg |= PMSU_CPU_POWER_DOWN_DIS_SNP_Q_SKIP; + writel(reg, pmsu_mp_base + PMSU_CPU_POWER_DOWN_CONTROL(hw_cpu)); +} + +static noinline int do_armada_370_xp_cpu_suspend(unsigned long deepidle) +{ + armada_370_xp_pmsu_idle_prepare(deepidle); + + v7_exit_coherency_flush(all); + + ll_disable_coherency(); + + dsb(); + + wfi(); + + /* If we are here, wfi failed. As processors run out of + * coherency for some time, tlbs might be stale, so flush them + */ + local_flush_tlb_all(); + + ll_enable_coherency(); + + /* Test the CR_C bit and set it if it was cleared */ + asm volatile( + "mrc p15, 0, %0, c1, c0, 0 \n\t" + "tst %0, #(1 << 2) \n\t" + "orreq %0, %0, #(1 << 2) \n\t" + "mcreq p15, 0, %0, c1, c0, 0 \n\t" + "isb " + : : "r" (0)); + + pr_warn("Failed to suspend the system\n"); + + return 0; +} + +static int armada_370_xp_cpu_suspend(unsigned long deepidle) +{ + return cpu_suspend(deepidle, do_armada_370_xp_cpu_suspend); +} + +/* No locking is needed because we only access per-CPU registers */ +static noinline void armada_370_xp_pmsu_idle_restore(void) +{ + unsigned int hw_cpu = cpu_logical_map(smp_processor_id()); + u32 reg; + + if (pmsu_mp_base == NULL) + return; + + /* cancel ask HW to power down the L2 Cache if possible */ + reg = readl(pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu)); + reg &= ~PMSU_CONTROL_AND_CONFIG_L2_PWDDN; + writel(reg, pmsu_mp_base + PMSU_CONTROL_AND_CONFIG(hw_cpu)); + + /* cancel Enable wakeup events and mask interrupts */ + reg = readl(pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu)); + reg &= ~(PMSU_STATUS_AND_MASK_IRQ_WAKEUP | PMSU_STATUS_AND_MASK_FIQ_WAKEUP); + reg &= ~PMSU_STATUS_AND_MASK_CPU_IDLE_WAIT; + reg &= ~PMSU_STATUS_AND_MASK_SNP_Q_EMPTY_WAIT; + reg &= ~(PMSU_STATUS_AND_MASK_IRQ_MASK | PMSU_STATUS_AND_MASK_FIQ_MASK); + writel(reg, pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu)); +} + early_initcall(armada_370_xp_pmsu_init); From 0041464ceeccd4718de228141438335e2d92f91b Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 28 Apr 2014 20:20:39 +0200 Subject: [PATCH 31/46] ARM: mvebu: refine which files are build in mach-mvebu Following the integration into mach-mvebu of the Kirkwood ARMv5 support, we need to be more careful about which files get built. For example, the pmsu.c file now calls wfi(), which only exists on ARMv7 platforms. Therefore, this commit changes mach-mvebu/Makefile to build the Armada 370/XP/375/38x specific files only when CONFIG_MACH_MVEBU_V7 is enabled. Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1398709239-6126-1-git-send-email-thomas.petazzoni@free-electrons.com Acked-by: Gregory CLEMENT Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/Makefile | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-mvebu/Makefile b/arch/arm/mach-mvebu/Makefile index 02b5389f05fd..ec0b2177d991 100644 --- a/arch/arm/mach-mvebu/Makefile +++ b/arch/arm/mach-mvebu/Makefile @@ -3,11 +3,14 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \ AFLAGS_coherency_ll.o := -Wa,-march=armv7-a -obj-y += system-controller.o mvebu-soc-id.o cpu-reset.o -obj-$(CONFIG_MACH_MVEBU_V7) += board-v7.o +obj-y += system-controller.o mvebu-soc-id.o + +ifeq ($(CONFIG_MACH_MVEBU_V7),y) +obj-y += cpu-reset.o board-v7.o coherency.o coherency_ll.o pmsu.o +obj-$(CONFIG_SMP) += platsmp.o headsmp.o platsmp-a9.o headsmp-a9.o +obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o +endif + obj-$(CONFIG_MACH_DOVE) += dove.o -obj-$(CONFIG_ARCH_MVEBU) += coherency.o coherency_ll.o pmsu.o -obj-$(CONFIG_SMP) += platsmp.o headsmp.o platsmp-a9.o headsmp-a9.o -obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o obj-$(CONFIG_MACH_KIRKWOOD) += kirkwood.o kirkwood-pm.o obj-$(CONFIG_MACH_T5325) += board-t5325.o From d163ee165bd49a51f77bae632ebf37eda4899d0e Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Mon, 14 Apr 2014 17:10:12 +0200 Subject: [PATCH 32/46] ARM: mvebu: Register notifier callback for the cpuidle transition In order to have well encapsulated code, we use notifier callbacks for CPU_PM_ENTER and CPU_PM_EXIT inside the mvebu power management code. Signed-off-by: Gregory CLEMENT Link: https://lkml.kernel.org/r/1397488214-20685-10-git-send-email-gregory.clement@free-electrons.com Acked-by: Daniel Lezcano Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/pmsu.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/arch/arm/mach-mvebu/pmsu.c b/arch/arm/mach-mvebu/pmsu.c index 78cf0279e1af..7902d21d7d68 100644 --- a/arch/arm/mach-mvebu/pmsu.c +++ b/arch/arm/mach-mvebu/pmsu.c @@ -18,6 +18,7 @@ #define pr_fmt(fmt) "mvebu-pmsu: " fmt +#include #include #include #include @@ -240,4 +241,21 @@ static noinline void armada_370_xp_pmsu_idle_restore(void) writel(reg, pmsu_mp_base + PMSU_STATUS_AND_MASK(hw_cpu)); } +static int armada_370_xp_cpu_pm_notify(struct notifier_block *self, + unsigned long action, void *hcpu) +{ + if (action == CPU_PM_ENTER) { + unsigned int hw_cpu = cpu_logical_map(smp_processor_id()); + mvebu_pmsu_set_cpu_boot_addr(hw_cpu, armada_370_xp_cpu_resume); + } else if (action == CPU_PM_EXIT) { + armada_370_xp_pmsu_idle_restore(); + } + + return NOTIFY_OK; +} + +static struct notifier_block armada_370_xp_cpu_pm_notifier = { + .notifier_call = armada_370_xp_cpu_pm_notify, +}; + early_initcall(armada_370_xp_pmsu_init); From b858fbc1919720f7f54360098ece03b383e961fa Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Mon, 14 Apr 2014 17:10:13 +0200 Subject: [PATCH 33/46] cpuidle: mvebu: Add initial CPU idle support for Armada 370/XP SoC Add the wfi, cpu idle and cpu deep idle power states support for the Armada XP SoCs. All the latencies and the power consumption values used at the "armada_370_xp_idle_driver" structure are preliminary and will be modified in the future after running some measurements and analysis. Based on the work of Nadav Haklai. Signed-off-by: Nadav Haklai Link: https://lkml.kernel.org/r/1397488214-20685-11-git-send-email-gregory.clement@free-electrons.com Signed-off-by: Gregory CLEMENT Link: https://lkml.kernel.org/r/1397488214-20685-11-git-send-email-gregory.clement@free-electrons.com Acked-by: Daniel Lezcano Signed-off-by: Jason Cooper --- drivers/cpuidle/Kconfig.arm | 5 ++ drivers/cpuidle/Makefile | 1 + drivers/cpuidle/cpuidle-armada-370-xp.c | 93 +++++++++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 drivers/cpuidle/cpuidle-armada-370-xp.c diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm index 97ccc31dbdd8..5bb94780d377 100644 --- a/drivers/cpuidle/Kconfig.arm +++ b/drivers/cpuidle/Kconfig.arm @@ -1,6 +1,11 @@ # # ARM CPU Idle drivers # +config ARM_ARMADA_370_XP_CPUIDLE + bool "CPU Idle Driver for Armada 370/XP family processors" + depends on ARCH_MVEBU + help + Select this to enable cpuidle on Armada 370/XP processors. config ARM_BIG_LITTLE_CPUIDLE bool "Support for ARM big.LITTLE processors" diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index f71ae1b373c5..9902d052bd87 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o ################################################################################## # ARM SoC drivers +obj-$(CONFIG_ARM_ARMADA_370_XP_CPUIDLE) += cpuidle-armada-370-xp.o obj-$(CONFIG_ARM_BIG_LITTLE_CPUIDLE) += cpuidle-big_little.o obj-$(CONFIG_ARM_HIGHBANK_CPUIDLE) += cpuidle-calxeda.o obj-$(CONFIG_ARM_KIRKWOOD_CPUIDLE) += cpuidle-kirkwood.o diff --git a/drivers/cpuidle/cpuidle-armada-370-xp.c b/drivers/cpuidle/cpuidle-armada-370-xp.c new file mode 100644 index 000000000000..28587d0f3947 --- /dev/null +++ b/drivers/cpuidle/cpuidle-armada-370-xp.c @@ -0,0 +1,93 @@ +/* + * Marvell Armada 370 and Armada XP SoC cpuidle driver + * + * Copyright (C) 2014 Marvell + * + * Nadav Haklai + * Gregory CLEMENT + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * Maintainer: Gregory CLEMENT + */ + +#include +#include +#include +#include +#include +#include +#include + +#define ARMADA_370_XP_MAX_STATES 3 +#define ARMADA_370_XP_FLAG_DEEP_IDLE 0x10000 + +static int (*armada_370_xp_cpu_suspend)(int); + +static int armada_370_xp_enter_idle(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) +{ + int ret; + bool deepidle = false; + cpu_pm_enter(); + + if (drv->states[index].flags & ARMADA_370_XP_FLAG_DEEP_IDLE) + deepidle = true; + + ret = armada_370_xp_cpu_suspend(deepidle); + if (ret) + return ret; + + cpu_pm_exit(); + + return index; +} + +static struct cpuidle_driver armada_370_xp_idle_driver = { + .name = "armada_370_xp_idle", + .states[0] = ARM_CPUIDLE_WFI_STATE, + .states[1] = { + .enter = armada_370_xp_enter_idle, + .exit_latency = 10, + .power_usage = 50, + .target_residency = 100, + .flags = CPUIDLE_FLAG_TIME_VALID, + .name = "MV CPU IDLE", + .desc = "CPU power down", + }, + .states[2] = { + .enter = armada_370_xp_enter_idle, + .exit_latency = 100, + .power_usage = 5, + .target_residency = 1000, + .flags = CPUIDLE_FLAG_TIME_VALID | + ARMADA_370_XP_FLAG_DEEP_IDLE, + .name = "MV CPU DEEP IDLE", + .desc = "CPU and L2 Fabric power down", + }, + .state_count = ARMADA_370_XP_MAX_STATES, +}; + +static int armada_370_xp_cpuidle_probe(struct platform_device *pdev) +{ + + armada_370_xp_cpu_suspend = (void *)(pdev->dev.platform_data); + return cpuidle_register(&armada_370_xp_idle_driver, NULL); +} + +static struct platform_driver armada_370_xp_cpuidle_plat_driver = { + .driver = { + .name = "cpuidle-armada-370-xp", + .owner = THIS_MODULE, + }, + .probe = armada_370_xp_cpuidle_probe, +}; + +module_platform_driver(armada_370_xp_cpuidle_plat_driver); + +MODULE_AUTHOR("Gregory CLEMENT "); +MODULE_DESCRIPTION("Armada 370/XP cpu idle driver"); +MODULE_LICENSE("GPL"); From 8c16babc6476111efabafbb262b47f8309942403 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Mon, 14 Apr 2014 17:10:14 +0200 Subject: [PATCH 34/46] ARM: mvebu: register the cpuidle driver for the Armada XP SoCs The cpuidle is a platform driver so we register the device just after the initialization of the board in an arch_initcall. Signed-off-by: Gregory CLEMENT Link: https://lkml.kernel.org/r/1397488214-20685-12-git-send-email-gregory.clement@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/pmsu.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/arch/arm/mach-mvebu/pmsu.c b/arch/arm/mach-mvebu/pmsu.c index 7902d21d7d68..53a55c8520bf 100644 --- a/arch/arm/mach-mvebu/pmsu.c +++ b/arch/arm/mach-mvebu/pmsu.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,10 @@ static void __iomem *pmsu_mp_base; extern void ll_disable_coherency(void); extern void ll_enable_coherency(void); +static struct platform_device armada_xp_cpuidle_device = { + .name = "cpuidle-armada-370-xp", +}; + static struct of_device_id of_pmsu_table[] = { { .compatible = "marvell,armada-370-pmsu", }, { .compatible = "marvell,armada-370-xp-pmsu", }, @@ -258,4 +263,36 @@ static struct notifier_block armada_370_xp_cpu_pm_notifier = { .notifier_call = armada_370_xp_cpu_pm_notify, }; +int __init armada_370_xp_cpu_pm_init(void) +{ + struct device_node *np; + + /* + * Check that all the requirements are available to enable + * cpuidle. So far, it is only supported on Armada XP, cpuidle + * needs the coherency fabric and the PMSU enabled + */ + + if (!of_machine_is_compatible("marvell,armadaxp")) + return 0; + + np = of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric"); + if (!np) + return 0; + of_node_put(np); + + np = of_find_matching_node(NULL, of_pmsu_table); + if (!np) + return 0; + of_node_put(np); + + armada_370_xp_pmsu_enable_l2_powerdown_onidle(); + armada_xp_cpuidle_device.dev.platform_data = armada_370_xp_cpu_suspend; + platform_device_register(&armada_xp_cpuidle_device); + cpu_pm_register_notifier(&armada_370_xp_cpu_pm_notifier); + + return 0; +} + +arch_initcall(armada_370_xp_cpu_pm_init); early_initcall(armada_370_xp_pmsu_init); From c42e1ffa269f098133629adf54cabe242596b647 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Sat, 19 Apr 2014 18:32:49 +0200 Subject: [PATCH 35/46] ARM: mvebu: remove unnecessary ifdef around l2x0_of_init l2x0_of_init function is always defined arch/arm/include/asm/hardware/cache-l2x0.h: in case of CONFIG_CACHE_L2X0 is not selected then a placeholder is defined. Then there is no need to have ifdef around l2x0_of_init. Signed-off-by: Gregory CLEMENT Reviewed-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1397925170-8202-2-git-send-email-gregory.clement@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/board-v7.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm/mach-mvebu/board-v7.c b/arch/arm/mach-mvebu/board-v7.c index 85f964e55bf3..b674f3717cfc 100644 --- a/arch/arm/mach-mvebu/board-v7.c +++ b/arch/arm/mach-mvebu/board-v7.c @@ -78,9 +78,7 @@ static void __init mvebu_timer_and_clk_init(void) mvebu_scu_enable(); coherency_init(); BUG_ON(mvebu_mbus_dt_init(coherency_available())); -#ifdef CONFIG_CACHE_L2X0 l2x0_of_init(0, ~0UL); -#endif if (of_machine_is_compatible("marvell,armada375")) hook_fault_code(16 + 6, armada_375_external_abort_wa, SIGBUS, 0, From 8eee0f81cdaafb2fc78dcd5087a15c7f428d7751 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Sat, 19 Apr 2014 18:32:50 +0200 Subject: [PATCH 36/46] ARM: mvebu: fix the name of the parameter used in mvebu_get_soc_id The name of the two parameters of mvebu_get_soc_id were inverted. This patch fix it in order to have a more readable code. Reported-by: Ezequiel Garcia Signed-off-by: Gregory CLEMENT Link: https://lkml.kernel.org/r/1397925170-8202-3-git-send-email-gregory.clement@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/board-v7.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-mvebu/board-v7.c b/arch/arm/mach-mvebu/board-v7.c index b674f3717cfc..c8a90d992be2 100644 --- a/arch/arm/mach-mvebu/board-v7.c +++ b/arch/arm/mach-mvebu/board-v7.c @@ -95,7 +95,7 @@ static void __init i2c_quirk(void) * mechanism. We can exit only if we are sure that we can * get the SoC revision and it is more recent than A0. */ - if (mvebu_get_soc_id(&rev, &dev) == 0 && dev > MV78XX0_A0_REV) + if (mvebu_get_soc_id(&dev, &rev) == 0 && rev > MV78XX0_A0_REV) return; for_each_compatible_node(np, NULL, "marvell,mv78230-i2c") { From e9d3c849a8dc92e1019a6c7ced98f6ac231a2703 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Thu, 24 Apr 2014 08:34:36 -0300 Subject: [PATCH 37/46] ARM: mvebu: Select HAVE_ARM_TWD only if SMP is enabled HAVE_ARM_TWD depends on SMP, so we should only select it if SMP is enabled, as the others platforms do. Signed-off-by: Ezequiel Garcia Link: https://lkml.kernel.org/r/1398339276-5754-1-git-send-email-ezequiel.garcia@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig index 7bb92b1a63fc..8c9fd60de40d 100644 --- a/arch/arm/mach-mvebu/Kconfig +++ b/arch/arm/mach-mvebu/Kconfig @@ -41,7 +41,7 @@ config MACH_ARMADA_375 select ARMADA_375_CLK select CPU_V7 select HAVE_ARM_SCU - select HAVE_ARM_TWD + select HAVE_ARM_TWD if SMP select HAVE_SMP select MACH_MVEBU_V7 select PINCTRL_ARMADA_375 @@ -57,7 +57,7 @@ config MACH_ARMADA_38X select ARMADA_38X_CLK select CPU_V7 select HAVE_ARM_SCU - select HAVE_ARM_TWD + select HAVE_ARM_TWD if SMP select HAVE_SMP select MACH_MVEBU_V7 select PINCTRL_ARMADA_38X From 5fd62066d2900b25a4fb3295ad13e3ee31474a51 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Thu, 24 Apr 2014 17:23:22 -0300 Subject: [PATCH 38/46] ARM: mvebu: Add thermal quirk for the Armada 375 DB board The initial release of the Armada 375 DB board has an Armada 375 Z1 stepping silicon. This commit introduces a quirk that allows to workaround a series of issues with the thermal sensor in this stepping, but updating the devicetree: * Updates the compatible string for the thermal, so the driver can perform a specific initialization of the sensor. * Moves the offset of the thermal control register. This quirk allows to specifiy the correct (A0 stepping) offset in the devicetree. Signed-off-by: Ezequiel Garcia Link: https://lkml.kernel.org/r/1398371004-15807-9-git-send-email-ezequiel.garcia@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/board-v7.c | 57 ++++++++++++++++++++++++++++++ arch/arm/mach-mvebu/mvebu-soc-id.h | 3 ++ 2 files changed, 60 insertions(+) diff --git a/arch/arm/mach-mvebu/board-v7.c b/arch/arm/mach-mvebu/board-v7.c index c8a90d992be2..bc0283f0dd88 100644 --- a/arch/arm/mach-mvebu/board-v7.c +++ b/arch/arm/mach-mvebu/board-v7.c @@ -113,10 +113,66 @@ static void __init i2c_quirk(void) return; } +#define A375_Z1_THERMAL_FIXUP_OFFSET 0xc + +static void __init thermal_quirk(void) +{ + struct device_node *np; + u32 dev, rev; + + if (mvebu_get_soc_id(&dev, &rev) && rev > ARMADA_375_Z1_REV) + return; + + for_each_compatible_node(np, NULL, "marvell,armada375-thermal") { + struct property *prop; + __be32 newval, *newprop, *oldprop; + int len; + + /* + * The register offset is at a wrong location. This quirk + * creates a new reg property as a clone of the previous + * one and corrects the offset. + */ + oldprop = (__be32 *)of_get_property(np, "reg", &len); + if (!oldprop) + continue; + + /* Create a duplicate of the 'reg' property */ + prop = kzalloc(sizeof(*prop), GFP_KERNEL); + prop->length = len; + prop->name = kstrdup("reg", GFP_KERNEL); + prop->value = kzalloc(len, GFP_KERNEL); + memcpy(prop->value, oldprop, len); + + /* Fixup the register offset of the second entry */ + oldprop += 2; + newprop = (__be32 *)prop->value + 2; + newval = cpu_to_be32(be32_to_cpu(*oldprop) - + A375_Z1_THERMAL_FIXUP_OFFSET); + *newprop = newval; + of_update_property(np, prop); + + /* + * The thermal controller needs some quirk too, so let's change + * the compatible string to reflect this. + */ + prop = kzalloc(sizeof(*prop), GFP_KERNEL); + prop->name = kstrdup("compatible", GFP_KERNEL); + prop->length = sizeof("marvell,armada375-z1-thermal"); + prop->value = kstrdup("marvell,armada375-z1-thermal", + GFP_KERNEL); + of_update_property(np, prop); + } + return; +} + static void __init mvebu_dt_init(void) { if (of_machine_is_compatible("plathome,openblocks-ax3-4")) i2c_quirk(); + if (of_machine_is_compatible("marvell,a375-db")) + thermal_quirk(); + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); } @@ -140,6 +196,7 @@ static const char * const armada_375_dt_compat[] = { DT_MACHINE_START(ARMADA_375_DT, "Marvell Armada 375 (Device Tree)") .init_time = mvebu_timer_and_clk_init, + .init_machine = mvebu_dt_init, .restart = mvebu_restart, .dt_compat = armada_375_dt_compat, MACHINE_END diff --git a/arch/arm/mach-mvebu/mvebu-soc-id.h b/arch/arm/mach-mvebu/mvebu-soc-id.h index 31654252fe35..294a443f56fc 100644 --- a/arch/arm/mach-mvebu/mvebu-soc-id.h +++ b/arch/arm/mach-mvebu/mvebu-soc-id.h @@ -20,6 +20,9 @@ #define MV78XX0_A0_REV 0x1 #define MV78XX0_B0_REV 0x2 +/* Armada 375 */ +#define ARMADA_375_Z1_REV 0x0 + #ifdef CONFIG_ARCH_MVEBU int mvebu_get_soc_id(u32 *dev, u32 *rev); #else From 7745b2512898e23507753513f7b5262ea1458135 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sat, 3 May 2014 20:30:12 +0200 Subject: [PATCH 39/46] ARM: Kirkwood: Remove platform driver for codec Remove the platform driver and platform data for the audio codec. A DT node will replace it. Signed-off-by: Andrew Lunn Link: https://lkml.kernel.org/r/1399141819-23924-3-git-send-email-andrew@lunn.ch Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/board-t5325.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/arch/arm/mach-mvebu/board-t5325.c b/arch/arm/mach-mvebu/board-t5325.c index 65ace6db9f28..f2401b298218 100644 --- a/arch/arm/mach-mvebu/board-t5325.c +++ b/arch/arm/mach-mvebu/board-t5325.c @@ -11,10 +11,8 @@ */ #include -#include #include #include -#include #include "board.h" static struct platform_device hp_t5325_audio_device = { @@ -22,20 +20,7 @@ static struct platform_device hp_t5325_audio_device = { .id = -1, }; -static struct alc5623_platform_data alc5621_data = { - .add_ctrl = 0x3700, - .jack_det_ctrl = 0x4810, -}; - -static struct i2c_board_info i2c_board_info[] __initdata = { - { - I2C_BOARD_INFO("alc5621", 0x1a), - .platform_data = &alc5621_data, - }, -}; - void __init t5325_init(void) { - i2c_register_board_info(0, i2c_board_info, ARRAY_SIZE(i2c_board_info)); platform_device_register(&hp_t5325_audio_device); } From efdf811d82b8001781087fd9174bb90a9530e578 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Sat, 3 May 2014 20:30:16 +0200 Subject: [PATCH 40/46] ARM: Kirkwood: t5325: Remove platform device to instantiate audio Remove platform device instantiating of the audio, which results in board-t5325.c being removed. A DT node will be added to take its place. Signed-off-by: Andrew Lunn Link: https://lkml.kernel.org/r/1399141819-23924-7-git-send-email-andrew@lunn.ch Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/Kconfig | 7 ------- arch/arm/mach-mvebu/Makefile | 1 - arch/arm/mach-mvebu/board-t5325.c | 26 -------------------------- arch/arm/mach-mvebu/board.h | 6 ------ arch/arm/mach-mvebu/kirkwood.c | 3 --- 5 files changed, 43 deletions(-) delete mode 100644 arch/arm/mach-mvebu/board-t5325.c diff --git a/arch/arm/mach-mvebu/Kconfig b/arch/arm/mach-mvebu/Kconfig index 8c9fd60de40d..d6b0a772a6dd 100644 --- a/arch/arm/mach-mvebu/Kconfig +++ b/arch/arm/mach-mvebu/Kconfig @@ -104,13 +104,6 @@ config MACH_KIRKWOOD Say 'Y' here if you want your kernel to support boards based on the Marvell Kirkwood device tree. -config MACH_T5325 - bool "HP T5325 thin client" - depends on MACH_KIRKWOOD - help - Say 'Y' here if you want your kernel to support the - HP T5325 Thin client - endmenu endif diff --git a/arch/arm/mach-mvebu/Makefile b/arch/arm/mach-mvebu/Makefile index ec0b2177d991..854b72af9faa 100644 --- a/arch/arm/mach-mvebu/Makefile +++ b/arch/arm/mach-mvebu/Makefile @@ -13,4 +13,3 @@ endif obj-$(CONFIG_MACH_DOVE) += dove.o obj-$(CONFIG_MACH_KIRKWOOD) += kirkwood.o kirkwood-pm.o -obj-$(CONFIG_MACH_T5325) += board-t5325.o diff --git a/arch/arm/mach-mvebu/board-t5325.c b/arch/arm/mach-mvebu/board-t5325.c deleted file mode 100644 index f2401b298218..000000000000 --- a/arch/arm/mach-mvebu/board-t5325.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * HP T5325 Board Setup - * - * Copyright (C) 2014 - * - * Andrew Lunn - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include -#include -#include -#include "board.h" - -static struct platform_device hp_t5325_audio_device = { - .name = "t5325-audio", - .id = -1, -}; - -void __init t5325_init(void) -{ - platform_device_register(&hp_t5325_audio_device); -} diff --git a/arch/arm/mach-mvebu/board.h b/arch/arm/mach-mvebu/board.h index de7f0a191394..9c7bb4386f8b 100644 --- a/arch/arm/mach-mvebu/board.h +++ b/arch/arm/mach-mvebu/board.h @@ -13,10 +13,4 @@ #ifndef __ARCH_MVEBU_BOARD_H #define __ARCH_MVEBU_BOARD_H -#ifdef CONFIG_MACH_T5325 -void t5325_init(void); -#else -static inline void t5325_init(void) {}; -#endif - #endif diff --git a/arch/arm/mach-mvebu/kirkwood.c b/arch/arm/mach-mvebu/kirkwood.c index a77e0bae9c55..46f105913c84 100644 --- a/arch/arm/mach-mvebu/kirkwood.c +++ b/arch/arm/mach-mvebu/kirkwood.c @@ -180,9 +180,6 @@ static void __init kirkwood_dt_init(void) kirkwood_pm_init(); kirkwood_dt_eth_fixup(); - if (of_machine_is_compatible("hp,t5325")) - t5325_init(); - of_platform_populate(NULL, of_default_bus_match_table, auxdata, NULL); } From c1a01a0360f6744c9c1735e5db7b208df819156e Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 5 May 2014 17:05:22 +0200 Subject: [PATCH 41/46] ARM: mvebu: fix thermal quirk SoC revision check In commit 54fe26a900bc528f3df1e4235cb6b9ca5c6d4dc2 ('ARM: mvebu: Add thermal quirk for the Armada 375 DB board'), a check on the Armada SoC revision was added to decide whether a quirk for the thermal device should be applied or not. However, the quirk implementation has a bug: it assumes mvebu_get_soc_id() returns true on success, but it returns 0. Therefore, the condition: if (mvebu_get_soc_id(&dev, &rev) && rev > ARMADA_375_Z1_REV) is always false (as long as mvebu-soc-id is properly initialized). As a consequence, the quirk is always applied, even on A0 steppings, for which the quirk should not be applied. This was spotted by testing the thermal driver on Armada 375 A0, which Ezequiel could not do since he does not have access to the A0 revision of the SoC for the moment. Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1399302326-6917-2-git-send-email-thomas.petazzoni@free-electrons.com Fixes: 54fe26a900bc528f3df1e4235cb6b9ca5c6d4dc2 ('ARM: mvebu: Add thermal quirk for the Armada 375 DB board') Acked-by: Ezequiel Garcia Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/board-v7.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-mvebu/board-v7.c b/arch/arm/mach-mvebu/board-v7.c index bc0283f0dd88..01cfce6ac20b 100644 --- a/arch/arm/mach-mvebu/board-v7.c +++ b/arch/arm/mach-mvebu/board-v7.c @@ -120,7 +120,7 @@ static void __init thermal_quirk(void) struct device_node *np; u32 dev, rev; - if (mvebu_get_soc_id(&dev, &rev) && rev > ARMADA_375_Z1_REV) + if (mvebu_get_soc_id(&dev, &rev) == 0 && rev > ARMADA_375_Z1_REV) return; for_each_compatible_node(np, NULL, "marvell,armada375-thermal") { From 73c3c79137f05de2ffcfec3469e4110e40dd1522 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 5 May 2014 17:05:23 +0200 Subject: [PATCH 42/46] ARM: mvebu: initialize mvebu-soc-id earlier Currently, the mvebu-soc-id logic is initialized through a core_initcall(). However, we will soon need to know the SoC revision before booting secondary CPUs, because a workaround affects Armada 375 Z1 steppings, but should not be applied on Armada 375 A0 steppings. Unfortunately, core_initcall() are called way too late compared to the SMP initialization. Therefore, the mvebu-soc-id initialization is move to an early_initcall(), which is called before the SMP initialization. Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1399302326-6917-3-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/mvebu-soc-id.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-mvebu/mvebu-soc-id.c b/arch/arm/mach-mvebu/mvebu-soc-id.c index 874a7504818e..e9119a99a5f3 100644 --- a/arch/arm/mach-mvebu/mvebu-soc-id.c +++ b/arch/arm/mach-mvebu/mvebu-soc-id.c @@ -118,7 +118,7 @@ clk_err: return ret; } -core_initcall(mvebu_soc_id_init); +early_initcall(mvebu_soc_id_init); static int __init mvebu_soc_device(void) { From 5093dcfb422d212ccdd22450bd986a2fb03cfb9f Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 5 May 2014 17:05:24 +0200 Subject: [PATCH 43/46] ARM: mvebu: add Armada 375 A0 revision definition Now that we have access to Armada 375 A0 platforms, we can add the corresponding revision definition in mvebu-soc-id.h. Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1399302326-6917-4-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/mvebu-soc-id.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-mvebu/mvebu-soc-id.h b/arch/arm/mach-mvebu/mvebu-soc-id.h index 294a443f56fc..c16bb68ca81f 100644 --- a/arch/arm/mach-mvebu/mvebu-soc-id.h +++ b/arch/arm/mach-mvebu/mvebu-soc-id.h @@ -22,6 +22,7 @@ /* Armada 375 */ #define ARMADA_375_Z1_REV 0x0 +#define ARMADA_375_A0_REV 0x3 #ifdef CONFIG_ARCH_MVEBU int mvebu_get_soc_id(u32 *dev, u32 *rev); From a58d5af7d992a5e6dd8e55b3e618bd77f0368b57 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 5 May 2014 17:05:25 +0200 Subject: [PATCH 44/46] ARM: mvebu: conditionalize Armada 375 SMP workaround The Armada 375 SMP workaround only needs to be applied to the Z1 revision of the SoC. The A0 and later revisions have been fixed, and no longer need this workaround. Note that the initialization of the SMP workaround is delayed from ->smp_prepare_cpus() to ->smp_boot_secondary() because when ->smp_prepare_cpus() is called, the early initcalls have not be called, so the mvebu-soc-id mechanism is not operational. Since the workaround is anyway not needed before the secondary CPU is started, we can delay its implementation until the ->smp_boot_secondary() call. Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1399302326-6917-5-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/platsmp-a9.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/arch/arm/mach-mvebu/platsmp-a9.c b/arch/arm/mach-mvebu/platsmp-a9.c index 04d0b1847c39..96c2c59e34b6 100644 --- a/arch/arm/mach-mvebu/platsmp-a9.c +++ b/arch/arm/mach-mvebu/platsmp-a9.c @@ -20,6 +20,7 @@ #include #include #include "common.h" +#include "mvebu-soc-id.h" #include "pmsu.h" #define CRYPT0_ENG_ID 41 @@ -63,11 +64,19 @@ static int __cpuinit mvebu_cortex_a9_boot_secondary(unsigned int cpu, */ hw_cpu = cpu_logical_map(cpu); - if (of_machine_is_compatible("marvell,armada375")) + if (of_machine_is_compatible("marvell,armada375")) { + u32 dev, rev; + + if (mvebu_get_soc_id(&dev, &rev) == 0 && + rev == ARMADA_375_Z1_REV) + armada_375_smp_cpu1_enable_wa(); + mvebu_system_controller_set_cpu_boot_addr(mvebu_cortex_a9_secondary_startup); - else + } + else { mvebu_pmsu_set_cpu_boot_addr(hw_cpu, mvebu_cortex_a9_secondary_startup); + } smp_wmb(); ret = mvebu_cpu_reset_deassert(hw_cpu); @@ -80,14 +89,7 @@ static int __cpuinit mvebu_cortex_a9_boot_secondary(unsigned int cpu, return 0; } -static void __init mvebu_cortex_a9_smp_prepare_cpus(unsigned int max_cpus) -{ - if (of_machine_is_compatible("marvell,armada375")) - armada_375_smp_cpu1_enable_wa(); -} - static struct smp_operations mvebu_cortex_a9_smp_ops __initdata = { - .smp_prepare_cpus = mvebu_cortex_a9_smp_prepare_cpus, .smp_boot_secondary = mvebu_cortex_a9_boot_secondary, #ifdef CONFIG_HOTPLUG_CPU .cpu_die = armada_xp_cpu_die, From 39438567179536c9f32e85d19586a11aebe1f860 Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Mon, 5 May 2014 17:05:26 +0200 Subject: [PATCH 45/46] ARM: mvebu: conditionalize Armada 375 coherency workaround The Armada 375 coherency workaround only needs to be applied to the Z1 revision of the SoC. The A0 and later revisions have been fixed, and no longer need this workaround. Signed-off-by: Thomas Petazzoni Link: https://lkml.kernel.org/r/1399302326-6917-6-git-send-email-thomas.petazzoni@free-electrons.com Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/coherency.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c index 9d5ccd372712..d5a975b6a590 100644 --- a/arch/arm/mach-mvebu/coherency.c +++ b/arch/arm/mach-mvebu/coherency.c @@ -33,6 +33,7 @@ #include #include "armada-370-xp.h" #include "coherency.h" +#include "mvebu-soc-id.h" unsigned long coherency_phys_base; void __iomem *coherency_base; @@ -365,8 +366,13 @@ static int __init coherency_late_init(void) if (type == COHERENCY_FABRIC_TYPE_NONE) return 0; - if (type == COHERENCY_FABRIC_TYPE_ARMADA_375) - armada_375_coherency_init_wa(); + if (type == COHERENCY_FABRIC_TYPE_ARMADA_375) { + u32 dev, rev; + + if (mvebu_get_soc_id(&dev, &rev) == 0 && + rev == ARMADA_375_Z1_REV) + armada_375_coherency_init_wa(); + } bus_register_notifier(&platform_bus_type, &mvebu_hwcc_platform_nb); From 9f0affcf3e21fc56d8bce625bb3d5800b7a7d284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Stehl=C3=A9?= Date: Tue, 6 May 2014 22:23:02 +0200 Subject: [PATCH 46/46] ARM: mvebu: Fix pmsu compilation when ARMv6 is selected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When compiling for multiplatform for both ARMv6 and ARMv7, the default compiler flags are for ARMv6, and we will get: /tmp/ccwDEzd0.s: Assembler messages: /tmp/ccwDEzd0.s:639: Error: selected processor does not support ARM mode `isb ' /tmp/ccwDEzd0.s:645: Error: selected processor does not support ARM mode `isb ' /tmp/ccwDEzd0.s:646: Error: selected processor does not support ARM mode `dsb ' /tmp/ccwDEzd0.s:695: Error: selected processor does not support ARM mode `isb ' make[1]: *** [arch/arm/mach-mvebu/pmsu.o] Error 1 Fix this in a similar manner than done previously in commit 72533b77d30c2be02672e26b5dde1263d7b4c2be, by specifying ARMv7 flags for pmsu.o. Signed-off-by: Vincent Stehlé Link: https://lkml.kernel.org/r/1399407782-29091-1-git-send-email-vincent.stehle@laposte.net Cc: Jason Cooper Cc: Andrew Lunn Cc: Gregory Clement Cc: Sebastian Hesselbarth Cc: Russell King Signed-off-by: Jason Cooper --- arch/arm/mach-mvebu/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-mvebu/Makefile b/arch/arm/mach-mvebu/Makefile index 854b72af9faa..2ecb828e4a8b 100644 --- a/arch/arm/mach-mvebu/Makefile +++ b/arch/arm/mach-mvebu/Makefile @@ -2,6 +2,7 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \ -I$(srctree)/arch/arm/plat-orion/include AFLAGS_coherency_ll.o := -Wa,-march=armv7-a +CFLAGS_pmsu.o := -march=armv7-a obj-y += system-controller.o mvebu-soc-id.o