@@ -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
@@ -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;
@@ -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");
@@ -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;
@@ -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);
@@ -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
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@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(-)