[PATCH 6/9] ARM: mvebu: add suspend/resume support for Armada 38x

Thomas Petazzoni thomas.petazzoni at free-electrons.com
Tue Jun 16 05:13:02 PDT 2015


This commit adds support for suspend/resume on Armada 38x, and
specifically for the Armada 388 GP board (since on Marvell EBU
systems, suspend/resume requires board-level specific details). In
details, the needed changes are:

 - Register mvebu_memblock_reserve() as the ->reserve() callback in
   DT_MACHINE_START. This is needed to make sure that the small
   portions of RAM used by the bootloader to do the DDR3 training are
   not used by the kernel, since this training is done again when
   existing from suspend to RAM.

 - Add support for Armada 38x in set_cpu_coherent() by enabling the
   SCU. This will make sure the SCU gets re-enabled after existing
   from suspend to RAM.

 - Add marvell,a388-gp to the list of supported boards in the
   board-specific code pm-board.c. No other changes are needed since
   the Armada 388 GP uses a 3 GPIOs protocol with the PIC
   micro-controller, like the one used on Armada XP GP.

 - Add mvebu_pm_store_armada38x_bootinfo() in pm.c to prepare the
   entry to suspend to RAM by creating the boot information structure
   expected by the bootloader.

 - Add the assembly code in pmsu_ll.S used when returning from suspend
   to RAM.

Signed-off-by: Thomas Petazzoni <thomas.petazzoni at free-electrons.com>
---
 arch/arm/mach-mvebu/board-v7.c  |  1 +
 arch/arm/mach-mvebu/coherency.c |  4 ++++
 arch/arm/mach-mvebu/pm-board.c  |  3 ++-
 arch/arm/mach-mvebu/pm.c        | 41 +++++++++++++++++++++++++++++++++++++++
 arch/arm/mach-mvebu/pmsu.h      |  1 +
 arch/arm/mach-mvebu/pmsu_ll.S   | 43 +++++++++++++++++++++++++++++++++++++++++
 6 files changed, 92 insertions(+), 1 deletion(-)

diff --git a/arch/arm/mach-mvebu/board-v7.c b/arch/arm/mach-mvebu/board-v7.c
index afee908..e5911de 100644
--- a/arch/arm/mach-mvebu/board-v7.c
+++ b/arch/arm/mach-mvebu/board-v7.c
@@ -230,6 +230,7 @@ DT_MACHINE_START(ARMADA_38X_DT, "Marvell Armada 380/385 (Device Tree)")
 	.l2c_aux_mask	= ~0,
 	.init_irq       = mvebu_init_irq,
 	.restart	= mvebu_restart,
+	.reserve        = mvebu_memblock_reserve,
 	.dt_compat	= armada_38x_dt_compat,
 MACHINE_END
 
diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c
index 44eedf3..caf4769 100644
--- a/arch/arm/mach-mvebu/coherency.c
+++ b/arch/arm/mach-mvebu/coherency.c
@@ -31,9 +31,11 @@
 #include <linux/mbus.h>
 #include <linux/pci.h>
 #include <asm/smp_plat.h>
+#include <asm/smp_scu.h>
 #include <asm/cacheflush.h>
 #include <asm/mach/map.h>
 #include <asm/dma-mapping.h>
+#include "common.h"
 #include "coherency.h"
 #include "mvebu-soc-id.h"
 
@@ -206,6 +208,8 @@ int set_cpu_coherent(void)
 		}
 		ll_add_cpu_to_smp_group();
 		return ll_enable_coherency();
+	} else if (type == COHERENCY_FABRIC_TYPE_ARMADA_380) {
+		scu_enable(mvebu_get_scu_base());
 	}
 
 	return 0;
diff --git a/arch/arm/mach-mvebu/pm-board.c b/arch/arm/mach-mvebu/pm-board.c
index b8c26cb..0f471fa 100644
--- a/arch/arm/mach-mvebu/pm-board.c
+++ b/arch/arm/mach-mvebu/pm-board.c
@@ -82,7 +82,8 @@ static int mvebu_armada_pm_init(void)
 	struct device_node *gpio_ctrl_np;
 	int ret = 0, i;
 
-	if (!of_machine_is_compatible("marvell,axp-gp"))
+	if (!of_machine_is_compatible("marvell,axp-gp") &&
+	    !of_machine_is_compatible("marvell,a388-gp"))
 		return -ENODEV;
 
 	np = of_find_node_by_name(NULL, "pm_pic");
diff --git a/arch/arm/mach-mvebu/pm.c b/arch/arm/mach-mvebu/pm.c
index 02fdf67..90d85ef 100644
--- a/arch/arm/mach-mvebu/pm.c
+++ b/arch/arm/mach-mvebu/pm.c
@@ -149,6 +149,45 @@ static void mvebu_pm_store_armadaxp_bootinfo(u32 *store_addr)
 	writel(BOOT_MAGIC_LIST_END, store_addr);
 }
 
+static void mvebu_pm_store_armada38x_bootinfo(u32 *store_addr)
+{
+	phys_addr_t resume_pc;
+	extern unsigned char armada_38x_mem_resume_data;
+	void *armada_38x_mem_resume_datap =
+		&armada_38x_mem_resume_data;
+
+	/*
+	 * Provide the internal register address to the resume code in
+	 * assembly. The value must be given in the native endianness
+	 * of the system, hence the usage of the raw variant.
+	 */
+	__raw_writel(mvebu_internal_reg_base(),
+		     armada_38x_mem_resume_datap);
+
+	resume_pc = virt_to_phys(armada_38x_mem_resume);
+
+	/*
+	 * The bootloader expects the first two words to be a magic
+	 * value (BOOT_MAGIC_WORD), followed by the address of the
+	 * resume code to jump to. Then, it expects a sequence of
+	 * (address, value) pairs, which can be used to restore the
+	 * value of certain registers. This sequence must end with the
+	 * BOOT_MAGIC_LIST_END magic value.
+	 */
+
+	writel(BOOT_MAGIC_WORD, store_addr++);
+	writel(resume_pc, store_addr++);
+
+	/*
+	 * We don't restore much registers here compared to Armada XP,
+	 * because we're getting out of the bootloader with MMU
+	 * enabled, so we have to disable it first in
+	 * armada_38x_mem_resume before being able to restore things.
+	 */
+
+	writel(BOOT_MAGIC_LIST_END, store_addr);
+}
+
 static int mvebu_pm_store_bootinfo(void)
 {
 	u32 *store_addr;
@@ -157,6 +196,8 @@ static int mvebu_pm_store_bootinfo(void)
 
 	if (of_machine_is_compatible("marvell,armadaxp"))
 		mvebu_pm_store_armadaxp_bootinfo(store_addr);
+	else if (of_machine_is_compatible("marvell,armada380"))
+		mvebu_pm_store_armada38x_bootinfo(store_addr);
 	else
 		return -ENODEV;
 
diff --git a/arch/arm/mach-mvebu/pmsu.h b/arch/arm/mach-mvebu/pmsu.h
index ea79269..9166e94 100644
--- a/arch/arm/mach-mvebu/pmsu.h
+++ b/arch/arm/mach-mvebu/pmsu.h
@@ -18,6 +18,7 @@ int mvebu_setup_boot_addr_wa(unsigned int crypto_eng_target,
 
 void mvebu_v7_pmsu_idle_exit(void);
 void armada_370_xp_cpu_resume(void);
+void armada_38x_mem_resume(void);
 
 int armada_370_xp_pmsu_idle_enter(unsigned long deepidle);
 int armada_38x_do_cpu_suspend(unsigned long deepidle);
diff --git a/arch/arm/mach-mvebu/pmsu_ll.S b/arch/arm/mach-mvebu/pmsu_ll.S
index 8865122..6305d80 100644
--- a/arch/arm/mach-mvebu/pmsu_ll.S
+++ b/arch/arm/mach-mvebu/pmsu_ll.S
@@ -51,6 +51,49 @@ ARM_BE8(setend	be )			@ go BE8 if entered LE
 	b	cpu_resume
 ENDPROC(armada_38x_cpu_resume)
 
+.global armada_38x_mem_resume_data
+
+#define MBUS_INTERNAL_REG_ADDRESS 0xd0020080
+
+ENTRY(armada_38x_mem_resume)
+ARM_BE8(setend	be )			@ go BE8 if entered LE
+	/* MMU disable, left enabled by the bootloader */
+	mrc	p15, 0, r1, c1, c0, 0
+	bic	r1, #1
+	mcr	p15, 0, r1, c1, c0, 0
+
+	bl      v7_invalidate_l1
+
+	/*
+	 * Load the internal register base address, we keep the value
+	 * unmodified in r1 throughout this function.
+	 */
+	adr	r1, armada_38x_mem_resume_data
+	ldr	r1, [r1]
+
+	/* Restore internal register address */
+	mov	r2, r1
+ARM_BE8(rev	r2, r2)
+	ldr	r3, =MBUS_INTERNAL_REG_ADDRESS
+	str	r2, [r3]
+
+	/* Update SCU offset CP15 register */
+	add	r2, r1, #0xC000
+	mcr  	p15, 4, r2, c15, c0, 0
+
+	/*
+	 * Disable L2 cache, left enabled by the bootloader,
+	 * it will be re-enabled later by the resume logic
+	 */
+	add	r2, r1, #0x8100
+	ldr  	r3, =0x0
+	str  	r3, [r2]
+
+	b 	cpu_resume
+armada_38x_mem_resume_data:
+	.long	.
+ENDPROC(armada_38x_mem_resume)
+
 .global mvebu_boot_wa_start
 .global mvebu_boot_wa_end
 
-- 
2.1.0




More information about the linux-arm-kernel mailing list