v4.19.13 snapshot.
diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
new file mode 100644
index 0000000..903f23c
--- /dev/null
+++ b/arch/arm/mach-at91/Kconfig
@@ -0,0 +1,154 @@
+menuconfig ARCH_AT91
+	bool "AT91/Microchip SoCs"
+	depends on ARCH_MULTI_V4T || ARCH_MULTI_V5 || ARCH_MULTI_V7 || ARM_SINGLE_ARMV7M
+	select ARM_CPU_SUSPEND if PM && ARCH_MULTI_V7
+	select COMMON_CLK_AT91
+	select GPIOLIB
+	select PINCTRL
+	select SOC_BUS
+
+if ARCH_AT91
+config SOC_SAMV7
+	bool "SAM Cortex-M7 family" if ARM_SINGLE_ARMV7M
+	select COMMON_CLK_AT91
+	select PINCTRL_AT91
+	help
+	  Select this if you are using an SoC from Microchip's SAME7, SAMS7 or SAMV7
+	  families.
+
+config SOC_SAMA5D2
+	bool "SAMA5D2 family"
+	depends on ARCH_MULTI_V7
+	select SOC_SAMA5
+	select CACHE_L2X0
+	select HAVE_FB_ATMEL
+	select HAVE_AT91_UTMI
+	select HAVE_AT91_USB_CLK
+	select HAVE_AT91_H32MX
+	select HAVE_AT91_GENERATED_CLK
+	select HAVE_AT91_AUDIO_PLL
+	select HAVE_AT91_I2S_MUX_CLK
+	select PINCTRL_AT91PIO4
+	help
+	  Select this if ou are using one of Microchip's SAMA5D2 family SoC.
+
+config SOC_SAMA5D3
+	bool "SAMA5D3 family"
+	depends on ARCH_MULTI_V7
+	select SOC_SAMA5
+	select HAVE_FB_ATMEL
+	select HAVE_AT91_UTMI
+	select HAVE_AT91_SMD
+	select HAVE_AT91_USB_CLK
+	select PINCTRL_AT91
+	help
+	  Select this if you are using one of Microchip's SAMA5D3 family SoC.
+	  This support covers SAMA5D31, SAMA5D33, SAMA5D34, SAMA5D35, SAMA5D36.
+
+config SOC_SAMA5D4
+	bool "SAMA5D4 family"
+	depends on ARCH_MULTI_V7
+	select SOC_SAMA5
+	select CACHE_L2X0
+	select HAVE_FB_ATMEL
+	select HAVE_AT91_UTMI
+	select HAVE_AT91_SMD
+	select HAVE_AT91_USB_CLK
+	select HAVE_AT91_H32MX
+	select PINCTRL_AT91
+	help
+	  Select this if you are using one of Microchip's SAMA5D4 family SoC.
+
+config SOC_AT91RM9200
+	bool "AT91RM9200"
+	depends on ARCH_MULTI_V4T
+	select ATMEL_AIC_IRQ
+	select ATMEL_PM if PM
+	select ATMEL_ST
+	select CPU_ARM920T
+	select HAVE_AT91_USB_CLK
+	select PINCTRL_AT91
+	select SOC_SAM_V4_V5
+	select SRAM if PM
+	help
+	  Select this if you are using Microchip's AT91RM9200 SoC.
+
+config SOC_AT91SAM9
+	bool "AT91SAM9"
+	depends on ARCH_MULTI_V5
+	select ATMEL_AIC_IRQ
+	select ATMEL_PM if PM
+	select ATMEL_SDRAMC
+	select CPU_ARM926T
+	select HAVE_AT91_SMD
+	select HAVE_AT91_USB_CLK
+	select HAVE_AT91_UTMI
+	select HAVE_FB_ATMEL
+	select MEMORY
+	select PINCTRL_AT91
+	select SOC_SAM_V4_V5
+	select SRAM if PM
+	help
+	  Select this if you are using one of those Microchip SoC:
+	    AT91SAM9260
+	    AT91SAM9261
+	    AT91SAM9263
+	    AT91SAM9G15
+	    AT91SAM9G20
+	    AT91SAM9G25
+	    AT91SAM9G35
+	    AT91SAM9G45
+	    AT91SAM9G46
+	    AT91SAM9M10
+	    AT91SAM9M11
+	    AT91SAM9N12
+	    AT91SAM9RL
+	    AT91SAM9X25
+	    AT91SAM9X35
+	    AT91SAM9XE
+
+config HAVE_AT91_UTMI
+	bool
+
+config HAVE_AT91_USB_CLK
+	bool
+
+config COMMON_CLK_AT91
+	bool
+	select COMMON_CLK
+	select MFD_SYSCON
+
+config HAVE_AT91_SMD
+	bool
+
+config HAVE_AT91_H32MX
+	bool
+
+config HAVE_AT91_GENERATED_CLK
+	bool
+
+config HAVE_AT91_AUDIO_PLL
+	bool
+
+config HAVE_AT91_I2S_MUX_CLK
+	bool
+
+config SOC_SAM_V4_V5
+	bool
+
+config SOC_SAM_V7
+	bool
+
+config SOC_SAMA5
+	bool
+	select ATMEL_AIC5_IRQ
+	select ATMEL_PM if PM
+	select ATMEL_SDRAMC
+	select MEMORY
+	select SOC_SAM_V7
+	select SRAM if PM
+
+config ATMEL_PM
+	bool
+
+endif
diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile
new file mode 100644
index 0000000..7415f18
--- /dev/null
+++ b/arch/arm/mach-at91/Makefile
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the linux kernel.
+#
+
+# CPU-specific support
+obj-$(CONFIG_SOC_AT91RM9200)	+= at91rm9200.o
+obj-$(CONFIG_SOC_AT91SAM9)	+= at91sam9.o
+obj-$(CONFIG_SOC_SAMA5)		+= sama5.o
+obj-$(CONFIG_SOC_SAMV7)		+= samv7.o
+
+# Power Management
+obj-$(CONFIG_ATMEL_PM)		+= pm.o pm_suspend.o
+
+ifeq ($(CONFIG_CPU_V7),y)
+AFLAGS_pm_suspend.o := -march=armv7-a
+endif
+ifeq ($(CONFIG_PM_DEBUG),y)
+CFLAGS_pm.o += -DDEBUG
+endif
+
+arch/arm/mach-at91/pm_data-offsets.s: arch/arm/mach-at91/pm_data-offsets.c
+	$(call if_changed_dep,cc_s_c)
+
+include/generated/at91_pm_data-offsets.h: arch/arm/mach-at91/pm_data-offsets.s FORCE
+	$(call filechk,offsets,__PM_DATA_OFFSETS_H__)
+
+arch/arm/mach-at91/pm_suspend.o: include/generated/at91_pm_data-offsets.h
diff --git a/arch/arm/mach-at91/Makefile.boot b/arch/arm/mach-at91/Makefile.boot
new file mode 100644
index 0000000..eacfc3f
--- /dev/null
+++ b/arch/arm/mach-at91/Makefile.boot
@@ -0,0 +1,3 @@
+# Empty file waiting for deletion once Makefile.boot isn't needed any more.
+# Patch waits for application at
+# http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=7889/1 .
diff --git a/arch/arm/mach-at91/at91rm9200.c b/arch/arm/mach-at91/at91rm9200.c
new file mode 100644
index 0000000..656ad40
--- /dev/null
+++ b/arch/arm/mach-at91/at91rm9200.c
@@ -0,0 +1,33 @@
+/*
+ *  Setup code for AT91RM9200
+ *
+ *  Copyright (C) 2011 Atmel,
+ *                2011 Nicolas Ferre <nicolas.ferre@atmel.com>
+ *                2012 Joachim Eastwood <manabian@gmail.com>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+#include <asm/mach/arch.h>
+
+#include "generic.h"
+
+static void __init at91rm9200_dt_device_init(void)
+{
+	of_platform_default_populate(NULL, NULL, NULL);
+
+	at91rm9200_pm_init();
+}
+
+static const char *const at91rm9200_dt_board_compat[] __initconst = {
+	"atmel,at91rm9200",
+	NULL
+};
+
+DT_MACHINE_START(at91rm9200_dt, "Atmel AT91RM9200")
+	.init_machine	= at91rm9200_dt_device_init,
+	.dt_compat	= at91rm9200_dt_board_compat,
+MACHINE_END
diff --git a/arch/arm/mach-at91/at91sam9.c b/arch/arm/mach-at91/at91sam9.c
new file mode 100644
index 0000000..3dbdef4
--- /dev/null
+++ b/arch/arm/mach-at91/at91sam9.c
@@ -0,0 +1,34 @@
+/*
+ *  Setup code for AT91SAM9
+ *
+ *  Copyright (C) 2011 Atmel,
+ *                2011 Nicolas Ferre <nicolas.ferre@atmel.com>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+#include <asm/mach/arch.h>
+#include <asm/system_misc.h>
+
+#include "generic.h"
+
+static void __init at91sam9_init(void)
+{
+	of_platform_default_populate(NULL, NULL, NULL);
+
+	at91sam9_pm_init();
+}
+
+static const char *const at91_dt_board_compat[] __initconst = {
+	"atmel,at91sam9",
+	NULL
+};
+
+DT_MACHINE_START(at91sam_dt, "Atmel AT91SAM9")
+	/* Maintainer: Atmel */
+	.init_machine	= at91sam9_init,
+	.dt_compat	= at91_dt_board_compat,
+MACHINE_END
diff --git a/arch/arm/mach-at91/generic.h b/arch/arm/mach-at91/generic.h
new file mode 100644
index 0000000..e2bd172
--- /dev/null
+++ b/arch/arm/mach-at91/generic.h
@@ -0,0 +1,26 @@
+/*
+ * linux/arch/arm/mach-at91/generic.h
+ *
+ *  Copyright (C) 2005 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _AT91_GENERIC_H
+#define _AT91_GENERIC_H
+
+#ifdef CONFIG_PM
+extern void __init at91rm9200_pm_init(void);
+extern void __init at91sam9_pm_init(void);
+extern void __init sama5_pm_init(void);
+extern void __init sama5d2_pm_init(void);
+#else
+static inline void __init at91rm9200_pm_init(void) { }
+static inline void __init at91sam9_pm_init(void) { }
+static inline void __init sama5_pm_init(void) { }
+static inline void __init sama5d2_pm_init(void) { }
+#endif
+
+#endif /* _AT91_GENERIC_H */
diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
new file mode 100644
index 0000000..32fae4d
--- /dev/null
+++ b/arch/arm/mach-at91/pm.c
@@ -0,0 +1,804 @@
+/*
+ * arch/arm/mach-at91/pm.c
+ * AT91 Power Management
+ *
+ * Copyright (C) 2005 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/genalloc.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/parser.h>
+#include <linux/suspend.h>
+
+#include <linux/clk/at91_pmc.h>
+
+#include <asm/cacheflush.h>
+#include <asm/fncpy.h>
+#include <asm/system_misc.h>
+#include <asm/suspend.h>
+
+#include "generic.h"
+#include "pm.h"
+
+/*
+ * FIXME: this is needed to communicate between the pinctrl driver and
+ * the PM implementation in the machine. Possibly part of the PM
+ * implementation should be moved down into the pinctrl driver and get
+ * called as part of the generic suspend/resume path.
+ */
+#ifdef CONFIG_PINCTRL_AT91
+extern void at91_pinctrl_gpio_suspend(void);
+extern void at91_pinctrl_gpio_resume(void);
+#endif
+
+static const match_table_t pm_modes __initconst = {
+	{ AT91_PM_STANDBY, "standby" },
+	{ AT91_PM_ULP0, "ulp0" },
+	{ AT91_PM_ULP1, "ulp1" },
+	{ AT91_PM_BACKUP, "backup" },
+	{ -1, NULL },
+};
+
+static struct at91_pm_data pm_data = {
+	.standby_mode = AT91_PM_STANDBY,
+	.suspend_mode = AT91_PM_ULP0,
+};
+
+#define at91_ramc_read(id, field) \
+	__raw_readl(pm_data.ramc[id] + field)
+
+#define at91_ramc_write(id, field, value) \
+	__raw_writel(value, pm_data.ramc[id] + field)
+
+static int at91_pm_valid_state(suspend_state_t state)
+{
+	switch (state) {
+		case PM_SUSPEND_ON:
+		case PM_SUSPEND_STANDBY:
+		case PM_SUSPEND_MEM:
+			return 1;
+
+		default:
+			return 0;
+	}
+}
+
+static int canary = 0xA5A5A5A5;
+
+static struct at91_pm_bu {
+	int suspended;
+	unsigned long reserved;
+	phys_addr_t canary;
+	phys_addr_t resume;
+} *pm_bu;
+
+struct wakeup_source_info {
+	unsigned int pmc_fsmr_bit;
+	unsigned int shdwc_mr_bit;
+	bool set_polarity;
+};
+
+static const struct wakeup_source_info ws_info[] = {
+	{ .pmc_fsmr_bit = AT91_PMC_FSTT(10),	.set_polarity = true },
+	{ .pmc_fsmr_bit = AT91_PMC_RTCAL,	.shdwc_mr_bit = BIT(17) },
+	{ .pmc_fsmr_bit = AT91_PMC_USBAL },
+	{ .pmc_fsmr_bit = AT91_PMC_SDMMC_CD },
+};
+
+static const struct of_device_id sama5d2_ws_ids[] = {
+	{ .compatible = "atmel,sama5d2-gem",		.data = &ws_info[0] },
+	{ .compatible = "atmel,at91rm9200-rtc",		.data = &ws_info[1] },
+	{ .compatible = "atmel,sama5d3-udc",		.data = &ws_info[2] },
+	{ .compatible = "atmel,at91rm9200-ohci",	.data = &ws_info[2] },
+	{ .compatible = "usb-ohci",			.data = &ws_info[2] },
+	{ .compatible = "atmel,at91sam9g45-ehci",	.data = &ws_info[2] },
+	{ .compatible = "usb-ehci",			.data = &ws_info[2] },
+	{ .compatible = "atmel,sama5d2-sdhci",		.data = &ws_info[3] },
+	{ /* sentinel */ }
+};
+
+static int at91_pm_config_ws(unsigned int pm_mode, bool set)
+{
+	const struct wakeup_source_info *wsi;
+	const struct of_device_id *match;
+	struct platform_device *pdev;
+	struct device_node *np;
+	unsigned int mode = 0, polarity = 0, val = 0;
+
+	if (pm_mode != AT91_PM_ULP1)
+		return 0;
+
+	if (!pm_data.pmc || !pm_data.shdwc)
+		return -EPERM;
+
+	if (!set) {
+		writel(mode, pm_data.pmc + AT91_PMC_FSMR);
+		return 0;
+	}
+
+	/* SHDWC.WUIR */
+	val = readl(pm_data.shdwc + 0x0c);
+	mode |= (val & 0x3ff);
+	polarity |= ((val >> 16) & 0x3ff);
+
+	/* SHDWC.MR */
+	val = readl(pm_data.shdwc + 0x04);
+
+	/* Loop through defined wakeup sources. */
+	for_each_matching_node_and_match(np, sama5d2_ws_ids, &match) {
+		pdev = of_find_device_by_node(np);
+		if (!pdev)
+			continue;
+
+		if (device_may_wakeup(&pdev->dev)) {
+			wsi = match->data;
+
+			/* Check if enabled on SHDWC. */
+			if (wsi->shdwc_mr_bit && !(val & wsi->shdwc_mr_bit))
+				goto put_node;
+
+			mode |= wsi->pmc_fsmr_bit;
+			if (wsi->set_polarity)
+				polarity |= wsi->pmc_fsmr_bit;
+		}
+
+put_node:
+		of_node_put(np);
+	}
+
+	if (mode) {
+		writel(mode, pm_data.pmc + AT91_PMC_FSMR);
+		writel(polarity, pm_data.pmc + AT91_PMC_FSPR);
+	} else {
+		pr_err("AT91: PM: no ULP1 wakeup sources found!");
+	}
+
+	return mode ? 0 : -EPERM;
+}
+
+/*
+ * Called after processes are frozen, but before we shutdown devices.
+ */
+static int at91_pm_begin(suspend_state_t state)
+{
+	switch (state) {
+	case PM_SUSPEND_MEM:
+		pm_data.mode = pm_data.suspend_mode;
+		break;
+
+	case PM_SUSPEND_STANDBY:
+		pm_data.mode = pm_data.standby_mode;
+		break;
+
+	default:
+		pm_data.mode = -1;
+	}
+
+	return at91_pm_config_ws(pm_data.mode, true);
+}
+
+/*
+ * Verify that all the clocks are correct before entering
+ * slow-clock mode.
+ */
+static int at91_pm_verify_clocks(void)
+{
+	unsigned long scsr;
+	int i;
+
+	scsr = readl(pm_data.pmc + AT91_PMC_SCSR);
+
+	/* USB must not be using PLLB */
+	if ((scsr & pm_data.uhp_udp_mask) != 0) {
+		pr_err("AT91: PM - Suspend-to-RAM with USB still active\n");
+		return 0;
+	}
+
+	/* PCK0..PCK3 must be disabled, or configured to use clk32k */
+	for (i = 0; i < 4; i++) {
+		u32 css;
+
+		if ((scsr & (AT91_PMC_PCK0 << i)) == 0)
+			continue;
+		css = readl(pm_data.pmc + AT91_PMC_PCKR(i)) & AT91_PMC_CSS;
+		if (css != AT91_PMC_CSS_SLOW) {
+			pr_err("AT91: PM - Suspend-to-RAM with PCK%d src %d\n", i, css);
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+/*
+ * Call this from platform driver suspend() to see how deeply to suspend.
+ * For example, some controllers (like OHCI) need one of the PLL clocks
+ * in order to act as a wakeup source, and those are not available when
+ * going into slow clock mode.
+ *
+ * REVISIT: generalize as clk_will_be_available(clk)?  Other platforms have
+ * the very same problem (but not using at91 main_clk), and it'd be better
+ * to add one generic API rather than lots of platform-specific ones.
+ */
+int at91_suspend_entering_slow_clock(void)
+{
+	return (pm_data.mode >= AT91_PM_ULP0);
+}
+EXPORT_SYMBOL(at91_suspend_entering_slow_clock);
+
+static void (*at91_suspend_sram_fn)(struct at91_pm_data *);
+extern void at91_pm_suspend_in_sram(struct at91_pm_data *pm_data);
+extern u32 at91_pm_suspend_in_sram_sz;
+
+static int at91_suspend_finish(unsigned long val)
+{
+	flush_cache_all();
+	outer_disable();
+
+	at91_suspend_sram_fn(&pm_data);
+
+	return 0;
+}
+
+static void at91_pm_suspend(suspend_state_t state)
+{
+	if (pm_data.mode == AT91_PM_BACKUP) {
+		pm_bu->suspended = 1;
+
+		cpu_suspend(0, at91_suspend_finish);
+
+		/* The SRAM is lost between suspend cycles */
+		at91_suspend_sram_fn = fncpy(at91_suspend_sram_fn,
+					     &at91_pm_suspend_in_sram,
+					     at91_pm_suspend_in_sram_sz);
+	} else {
+		at91_suspend_finish(0);
+	}
+
+	outer_resume();
+}
+
+/*
+ * STANDBY mode has *all* drivers suspended; ignores irqs not marked as 'wakeup'
+ * event sources; and reduces DRAM power.  But otherwise it's identical to
+ * PM_SUSPEND_ON: cpu idle, and nothing fancy done with main or cpu clocks.
+ *
+ * AT91_PM_ULP0 is like STANDBY plus slow clock mode, so drivers must
+ * suspend more deeply, the master clock switches to the clk32k and turns off
+ * the main oscillator
+ *
+ * AT91_PM_BACKUP turns off the whole SoC after placing the DDR in self refresh
+ */
+static int at91_pm_enter(suspend_state_t state)
+{
+#ifdef CONFIG_PINCTRL_AT91
+	at91_pinctrl_gpio_suspend();
+#endif
+
+	switch (state) {
+	case PM_SUSPEND_MEM:
+	case PM_SUSPEND_STANDBY:
+		/*
+		 * Ensure that clocks are in a valid state.
+		 */
+		if (pm_data.mode >= AT91_PM_ULP0 &&
+		    !at91_pm_verify_clocks())
+			goto error;
+
+		at91_pm_suspend(state);
+
+		break;
+
+	case PM_SUSPEND_ON:
+		cpu_do_idle();
+		break;
+
+	default:
+		pr_debug("AT91: PM - bogus suspend state %d\n", state);
+		goto error;
+	}
+
+error:
+#ifdef CONFIG_PINCTRL_AT91
+	at91_pinctrl_gpio_resume();
+#endif
+	return 0;
+}
+
+/*
+ * Called right prior to thawing processes.
+ */
+static void at91_pm_end(void)
+{
+	at91_pm_config_ws(pm_data.mode, false);
+}
+
+
+static const struct platform_suspend_ops at91_pm_ops = {
+	.valid	= at91_pm_valid_state,
+	.begin	= at91_pm_begin,
+	.enter	= at91_pm_enter,
+	.end	= at91_pm_end,
+};
+
+static struct platform_device at91_cpuidle_device = {
+	.name = "cpuidle-at91",
+};
+
+/*
+ * The AT91RM9200 goes into self-refresh mode with this command, and will
+ * terminate self-refresh automatically on the next SDRAM access.
+ *
+ * Self-refresh mode is exited as soon as a memory access is made, but we don't
+ * know for sure when that happens. However, we need to restore the low-power
+ * mode if it was enabled before going idle. Restoring low-power mode while
+ * still in self-refresh is "not recommended", but seems to work.
+ */
+static void at91rm9200_standby(void)
+{
+	asm volatile(
+		"b    1f\n\t"
+		".align    5\n\t"
+		"1:  mcr    p15, 0, %0, c7, c10, 4\n\t"
+		"    str    %2, [%1, %3]\n\t"
+		"    mcr    p15, 0, %0, c7, c0, 4\n\t"
+		:
+		: "r" (0), "r" (pm_data.ramc[0]),
+		  "r" (1), "r" (AT91_MC_SDRAMC_SRR));
+}
+
+/* We manage both DDRAM/SDRAM controllers, we need more than one value to
+ * remember.
+ */
+static void at91_ddr_standby(void)
+{
+	/* Those two values allow us to delay self-refresh activation
+	 * to the maximum. */
+	u32 lpr0, lpr1 = 0;
+	u32 mdr, saved_mdr0, saved_mdr1 = 0;
+	u32 saved_lpr0, saved_lpr1 = 0;
+
+	/* LPDDR1 --> force DDR2 mode during self-refresh */
+	saved_mdr0 = at91_ramc_read(0, AT91_DDRSDRC_MDR);
+	if ((saved_mdr0 & AT91_DDRSDRC_MD) == AT91_DDRSDRC_MD_LOW_POWER_DDR) {
+		mdr = saved_mdr0 & ~AT91_DDRSDRC_MD;
+		mdr |= AT91_DDRSDRC_MD_DDR2;
+		at91_ramc_write(0, AT91_DDRSDRC_MDR, mdr);
+	}
+
+	if (pm_data.ramc[1]) {
+		saved_lpr1 = at91_ramc_read(1, AT91_DDRSDRC_LPR);
+		lpr1 = saved_lpr1 & ~AT91_DDRSDRC_LPCB;
+		lpr1 |= AT91_DDRSDRC_LPCB_SELF_REFRESH;
+		saved_mdr1 = at91_ramc_read(1, AT91_DDRSDRC_MDR);
+		if ((saved_mdr1 & AT91_DDRSDRC_MD) == AT91_DDRSDRC_MD_LOW_POWER_DDR) {
+			mdr = saved_mdr1 & ~AT91_DDRSDRC_MD;
+			mdr |= AT91_DDRSDRC_MD_DDR2;
+			at91_ramc_write(1, AT91_DDRSDRC_MDR, mdr);
+		}
+	}
+
+	saved_lpr0 = at91_ramc_read(0, AT91_DDRSDRC_LPR);
+	lpr0 = saved_lpr0 & ~AT91_DDRSDRC_LPCB;
+	lpr0 |= AT91_DDRSDRC_LPCB_SELF_REFRESH;
+
+	/* self-refresh mode now */
+	at91_ramc_write(0, AT91_DDRSDRC_LPR, lpr0);
+	if (pm_data.ramc[1])
+		at91_ramc_write(1, AT91_DDRSDRC_LPR, lpr1);
+
+	cpu_do_idle();
+
+	at91_ramc_write(0, AT91_DDRSDRC_MDR, saved_mdr0);
+	at91_ramc_write(0, AT91_DDRSDRC_LPR, saved_lpr0);
+	if (pm_data.ramc[1]) {
+		at91_ramc_write(0, AT91_DDRSDRC_MDR, saved_mdr1);
+		at91_ramc_write(1, AT91_DDRSDRC_LPR, saved_lpr1);
+	}
+}
+
+static void sama5d3_ddr_standby(void)
+{
+	u32 lpr0;
+	u32 saved_lpr0;
+
+	saved_lpr0 = at91_ramc_read(0, AT91_DDRSDRC_LPR);
+	lpr0 = saved_lpr0 & ~AT91_DDRSDRC_LPCB;
+	lpr0 |= AT91_DDRSDRC_LPCB_POWER_DOWN;
+
+	at91_ramc_write(0, AT91_DDRSDRC_LPR, lpr0);
+
+	cpu_do_idle();
+
+	at91_ramc_write(0, AT91_DDRSDRC_LPR, saved_lpr0);
+}
+
+/* We manage both DDRAM/SDRAM controllers, we need more than one value to
+ * remember.
+ */
+static void at91sam9_sdram_standby(void)
+{
+	u32 lpr0, lpr1 = 0;
+	u32 saved_lpr0, saved_lpr1 = 0;
+
+	if (pm_data.ramc[1]) {
+		saved_lpr1 = at91_ramc_read(1, AT91_SDRAMC_LPR);
+		lpr1 = saved_lpr1 & ~AT91_SDRAMC_LPCB;
+		lpr1 |= AT91_SDRAMC_LPCB_SELF_REFRESH;
+	}
+
+	saved_lpr0 = at91_ramc_read(0, AT91_SDRAMC_LPR);
+	lpr0 = saved_lpr0 & ~AT91_SDRAMC_LPCB;
+	lpr0 |= AT91_SDRAMC_LPCB_SELF_REFRESH;
+
+	/* self-refresh mode now */
+	at91_ramc_write(0, AT91_SDRAMC_LPR, lpr0);
+	if (pm_data.ramc[1])
+		at91_ramc_write(1, AT91_SDRAMC_LPR, lpr1);
+
+	cpu_do_idle();
+
+	at91_ramc_write(0, AT91_SDRAMC_LPR, saved_lpr0);
+	if (pm_data.ramc[1])
+		at91_ramc_write(1, AT91_SDRAMC_LPR, saved_lpr1);
+}
+
+struct ramc_info {
+	void (*idle)(void);
+	unsigned int memctrl;
+};
+
+static const struct ramc_info ramc_infos[] __initconst = {
+	{ .idle = at91rm9200_standby, .memctrl = AT91_MEMCTRL_MC},
+	{ .idle = at91sam9_sdram_standby, .memctrl = AT91_MEMCTRL_SDRAMC},
+	{ .idle = at91_ddr_standby, .memctrl = AT91_MEMCTRL_DDRSDR},
+	{ .idle = sama5d3_ddr_standby, .memctrl = AT91_MEMCTRL_DDRSDR},
+};
+
+static const struct of_device_id ramc_ids[] __initconst = {
+	{ .compatible = "atmel,at91rm9200-sdramc", .data = &ramc_infos[0] },
+	{ .compatible = "atmel,at91sam9260-sdramc", .data = &ramc_infos[1] },
+	{ .compatible = "atmel,at91sam9g45-ddramc", .data = &ramc_infos[2] },
+	{ .compatible = "atmel,sama5d3-ddramc", .data = &ramc_infos[3] },
+	{ /*sentinel*/ }
+};
+
+static __init void at91_dt_ramc(void)
+{
+	struct device_node *np;
+	const struct of_device_id *of_id;
+	int idx = 0;
+	void *standby = NULL;
+	const struct ramc_info *ramc;
+
+	for_each_matching_node_and_match(np, ramc_ids, &of_id) {
+		pm_data.ramc[idx] = of_iomap(np, 0);
+		if (!pm_data.ramc[idx])
+			panic(pr_fmt("unable to map ramc[%d] cpu registers\n"), idx);
+
+		ramc = of_id->data;
+		if (!standby)
+			standby = ramc->idle;
+		pm_data.memctrl = ramc->memctrl;
+
+		idx++;
+	}
+
+	if (!idx)
+		panic(pr_fmt("unable to find compatible ram controller node in dtb\n"));
+
+	if (!standby) {
+		pr_warn("ramc no standby function available\n");
+		return;
+	}
+
+	at91_cpuidle_device.dev.platform_data = standby;
+}
+
+static void at91rm9200_idle(void)
+{
+	/*
+	 * Disable the processor clock.  The processor will be automatically
+	 * re-enabled by an interrupt or by a reset.
+	 */
+	writel(AT91_PMC_PCK, pm_data.pmc + AT91_PMC_SCDR);
+}
+
+static void at91sam9_idle(void)
+{
+	writel(AT91_PMC_PCK, pm_data.pmc + AT91_PMC_SCDR);
+	cpu_do_idle();
+}
+
+static void __init at91_pm_sram_init(void)
+{
+	struct gen_pool *sram_pool;
+	phys_addr_t sram_pbase;
+	unsigned long sram_base;
+	struct device_node *node;
+	struct platform_device *pdev = NULL;
+
+	for_each_compatible_node(node, NULL, "mmio-sram") {
+		pdev = of_find_device_by_node(node);
+		if (pdev) {
+			of_node_put(node);
+			break;
+		}
+	}
+
+	if (!pdev) {
+		pr_warn("%s: failed to find sram device!\n", __func__);
+		return;
+	}
+
+	sram_pool = gen_pool_get(&pdev->dev, NULL);
+	if (!sram_pool) {
+		pr_warn("%s: sram pool unavailable!\n", __func__);
+		return;
+	}
+
+	sram_base = gen_pool_alloc(sram_pool, at91_pm_suspend_in_sram_sz);
+	if (!sram_base) {
+		pr_warn("%s: unable to alloc sram!\n", __func__);
+		return;
+	}
+
+	sram_pbase = gen_pool_virt_to_phys(sram_pool, sram_base);
+	at91_suspend_sram_fn = __arm_ioremap_exec(sram_pbase,
+					at91_pm_suspend_in_sram_sz, false);
+	if (!at91_suspend_sram_fn) {
+		pr_warn("SRAM: Could not map\n");
+		return;
+	}
+
+	/* Copy the pm suspend handler to SRAM */
+	at91_suspend_sram_fn = fncpy(at91_suspend_sram_fn,
+			&at91_pm_suspend_in_sram, at91_pm_suspend_in_sram_sz);
+}
+
+static bool __init at91_is_pm_mode_active(int pm_mode)
+{
+	return (pm_data.standby_mode == pm_mode ||
+		pm_data.suspend_mode == pm_mode);
+}
+
+static int __init at91_pm_backup_init(void)
+{
+	struct gen_pool *sram_pool;
+	struct device_node *np;
+	struct platform_device *pdev = NULL;
+	int ret = -ENODEV;
+
+	if (!at91_is_pm_mode_active(AT91_PM_BACKUP))
+		return 0;
+
+	pm_bu = NULL;
+
+	np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-sfrbu");
+	if (!np) {
+		pr_warn("%s: failed to find sfrbu!\n", __func__);
+		return ret;
+	}
+
+	pm_data.sfrbu = of_iomap(np, 0);
+	of_node_put(np);
+	pm_bu = NULL;
+
+	np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-securam");
+	if (!np)
+		goto securam_fail;
+
+	pdev = of_find_device_by_node(np);
+	of_node_put(np);
+	if (!pdev) {
+		pr_warn("%s: failed to find securam device!\n", __func__);
+		goto securam_fail;
+	}
+
+	sram_pool = gen_pool_get(&pdev->dev, NULL);
+	if (!sram_pool) {
+		pr_warn("%s: securam pool unavailable!\n", __func__);
+		goto securam_fail;
+	}
+
+	pm_bu = (void *)gen_pool_alloc(sram_pool, sizeof(struct at91_pm_bu));
+	if (!pm_bu) {
+		pr_warn("%s: unable to alloc securam!\n", __func__);
+		ret = -ENOMEM;
+		goto securam_fail;
+	}
+
+	pm_bu->suspended = 0;
+	pm_bu->canary = __pa_symbol(&canary);
+	pm_bu->resume = __pa_symbol(cpu_resume);
+
+	return 0;
+
+securam_fail:
+	iounmap(pm_data.sfrbu);
+	pm_data.sfrbu = NULL;
+	return ret;
+}
+
+static void __init at91_pm_use_default_mode(int pm_mode)
+{
+	if (pm_mode != AT91_PM_ULP1 && pm_mode != AT91_PM_BACKUP)
+		return;
+
+	if (pm_data.standby_mode == pm_mode)
+		pm_data.standby_mode = AT91_PM_ULP0;
+	if (pm_data.suspend_mode == pm_mode)
+		pm_data.suspend_mode = AT91_PM_ULP0;
+}
+
+static void __init at91_pm_modes_init(void)
+{
+	struct device_node *np;
+	int ret;
+
+	if (!at91_is_pm_mode_active(AT91_PM_BACKUP) &&
+	    !at91_is_pm_mode_active(AT91_PM_ULP1))
+		return;
+
+	np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-shdwc");
+	if (!np) {
+		pr_warn("%s: failed to find shdwc!\n", __func__);
+		goto ulp1_default;
+	}
+
+	pm_data.shdwc = of_iomap(np, 0);
+	of_node_put(np);
+
+	ret = at91_pm_backup_init();
+	if (ret) {
+		if (!at91_is_pm_mode_active(AT91_PM_ULP1))
+			goto unmap;
+		else
+			goto backup_default;
+	}
+
+	return;
+
+unmap:
+	iounmap(pm_data.shdwc);
+	pm_data.shdwc = NULL;
+ulp1_default:
+	at91_pm_use_default_mode(AT91_PM_ULP1);
+backup_default:
+	at91_pm_use_default_mode(AT91_PM_BACKUP);
+}
+
+struct pmc_info {
+	unsigned long uhp_udp_mask;
+};
+
+static const struct pmc_info pmc_infos[] __initconst = {
+	{ .uhp_udp_mask = AT91RM9200_PMC_UHP | AT91RM9200_PMC_UDP },
+	{ .uhp_udp_mask = AT91SAM926x_PMC_UHP | AT91SAM926x_PMC_UDP },
+	{ .uhp_udp_mask = AT91SAM926x_PMC_UHP },
+	{ .uhp_udp_mask = 0 },
+};
+
+static const struct of_device_id atmel_pmc_ids[] __initconst = {
+	{ .compatible = "atmel,at91rm9200-pmc", .data = &pmc_infos[0] },
+	{ .compatible = "atmel,at91sam9260-pmc", .data = &pmc_infos[1] },
+	{ .compatible = "atmel,at91sam9261-pmc", .data = &pmc_infos[1] },
+	{ .compatible = "atmel,at91sam9263-pmc", .data = &pmc_infos[1] },
+	{ .compatible = "atmel,at91sam9g45-pmc", .data = &pmc_infos[2] },
+	{ .compatible = "atmel,at91sam9n12-pmc", .data = &pmc_infos[1] },
+	{ .compatible = "atmel,at91sam9rl-pmc", .data = &pmc_infos[3] },
+	{ .compatible = "atmel,at91sam9x5-pmc", .data = &pmc_infos[1] },
+	{ .compatible = "atmel,sama5d3-pmc", .data = &pmc_infos[1] },
+	{ .compatible = "atmel,sama5d4-pmc", .data = &pmc_infos[1] },
+	{ .compatible = "atmel,sama5d2-pmc", .data = &pmc_infos[1] },
+	{ /* sentinel */ },
+};
+
+static void __init at91_pm_init(void (*pm_idle)(void))
+{
+	struct device_node *pmc_np;
+	const struct of_device_id *of_id;
+	const struct pmc_info *pmc;
+
+	if (at91_cpuidle_device.dev.platform_data)
+		platform_device_register(&at91_cpuidle_device);
+
+	pmc_np = of_find_matching_node_and_match(NULL, atmel_pmc_ids, &of_id);
+	pm_data.pmc = of_iomap(pmc_np, 0);
+	if (!pm_data.pmc) {
+		pr_err("AT91: PM not supported, PMC not found\n");
+		return;
+	}
+
+	pmc = of_id->data;
+	pm_data.uhp_udp_mask = pmc->uhp_udp_mask;
+
+	if (pm_idle)
+		arm_pm_idle = pm_idle;
+
+	at91_pm_sram_init();
+
+	if (at91_suspend_sram_fn) {
+		suspend_set_ops(&at91_pm_ops);
+		pr_info("AT91: PM: standby: %s, suspend: %s\n",
+			pm_modes[pm_data.standby_mode].pattern,
+			pm_modes[pm_data.suspend_mode].pattern);
+	} else {
+		pr_info("AT91: PM not supported, due to no SRAM allocated\n");
+	}
+}
+
+void __init at91rm9200_pm_init(void)
+{
+	if (!IS_ENABLED(CONFIG_SOC_AT91RM9200))
+		return;
+
+	at91_dt_ramc();
+
+	/*
+	 * AT91RM9200 SDRAM low-power mode cannot be used with self-refresh.
+	 */
+	at91_ramc_write(0, AT91_MC_SDRAMC_LPR, 0);
+
+	at91_pm_init(at91rm9200_idle);
+}
+
+void __init at91sam9_pm_init(void)
+{
+	if (!IS_ENABLED(CONFIG_SOC_AT91SAM9))
+		return;
+
+	at91_dt_ramc();
+	at91_pm_init(at91sam9_idle);
+}
+
+void __init sama5_pm_init(void)
+{
+	if (!IS_ENABLED(CONFIG_SOC_SAMA5))
+		return;
+
+	at91_dt_ramc();
+	at91_pm_init(NULL);
+}
+
+void __init sama5d2_pm_init(void)
+{
+	if (!IS_ENABLED(CONFIG_SOC_SAMA5D2))
+		return;
+
+	at91_pm_modes_init();
+	sama5_pm_init();
+}
+
+static int __init at91_pm_modes_select(char *str)
+{
+	char *s;
+	substring_t args[MAX_OPT_ARGS];
+	int standby, suspend;
+
+	if (!str)
+		return 0;
+
+	s = strsep(&str, ",");
+	standby = match_token(s, pm_modes, args);
+	if (standby < 0)
+		return 0;
+
+	suspend = match_token(str, pm_modes, args);
+	if (suspend < 0)
+		return 0;
+
+	pm_data.standby_mode = standby;
+	pm_data.suspend_mode = suspend;
+
+	return 0;
+}
+early_param("atmel.pm_modes", at91_pm_modes_select);
diff --git a/arch/arm/mach-at91/pm.h b/arch/arm/mach-at91/pm.h
new file mode 100644
index 0000000..9bd4e6c
--- /dev/null
+++ b/arch/arm/mach-at91/pm.h
@@ -0,0 +1,43 @@
+/*
+ * AT91 Power Management
+ *
+ * Copyright (C) 2005 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __ARCH_ARM_MACH_AT91_PM
+#define __ARCH_ARM_MACH_AT91_PM
+
+#include <asm/proc-fns.h>
+
+#include <linux/mfd/syscon/atmel-mc.h>
+#include <soc/at91/at91sam9_ddrsdr.h>
+#include <soc/at91/at91sam9_sdramc.h>
+
+#define AT91_MEMCTRL_MC		0
+#define AT91_MEMCTRL_SDRAMC	1
+#define AT91_MEMCTRL_DDRSDR	2
+
+#define	AT91_PM_STANDBY		0x00
+#define AT91_PM_ULP0		0x01
+#define AT91_PM_ULP1		0x02
+#define	AT91_PM_BACKUP		0x03
+
+#ifndef __ASSEMBLY__
+struct at91_pm_data {
+	void __iomem *pmc;
+	void __iomem *ramc[2];
+	unsigned long uhp_udp_mask;
+	unsigned int memctrl;
+	unsigned int mode;
+	void __iomem *shdwc;
+	void __iomem *sfrbu;
+	unsigned int standby_mode;
+	unsigned int suspend_mode;
+};
+#endif
+
+#endif
diff --git a/arch/arm/mach-at91/pm_data-offsets.c b/arch/arm/mach-at91/pm_data-offsets.c
new file mode 100644
index 0000000..f2d893c
--- /dev/null
+++ b/arch/arm/mach-at91/pm_data-offsets.c
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/stddef.h>
+#include <linux/kbuild.h>
+#include "pm.h"
+
+int main(void)
+{
+	DEFINE(PM_DATA_PMC,		offsetof(struct at91_pm_data, pmc));
+	DEFINE(PM_DATA_RAMC0,		offsetof(struct at91_pm_data, ramc[0]));
+	DEFINE(PM_DATA_RAMC1,		offsetof(struct at91_pm_data, ramc[1]));
+	DEFINE(PM_DATA_MEMCTRL,	offsetof(struct at91_pm_data, memctrl));
+	DEFINE(PM_DATA_MODE,		offsetof(struct at91_pm_data, mode));
+	DEFINE(PM_DATA_SHDWC,		offsetof(struct at91_pm_data, shdwc));
+	DEFINE(PM_DATA_SFRBU,		offsetof(struct at91_pm_data, sfrbu));
+
+	return 0;
+}
diff --git a/arch/arm/mach-at91/pm_suspend.S b/arch/arm/mach-at91/pm_suspend.S
new file mode 100644
index 0000000..a7c6ae1
--- /dev/null
+++ b/arch/arm/mach-at91/pm_suspend.S
@@ -0,0 +1,472 @@
+/*
+ * arch/arm/mach-at91/pm_slow_clock.S
+ *
+ *  Copyright (C) 2006 Savin Zlobec
+ *
+ * AT91SAM9 support:
+ *  Copyright (C) 2007 Anti Sullin <anti.sullin@artecdesign.ee>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/linkage.h>
+#include <linux/clk/at91_pmc.h>
+#include "pm.h"
+#include "generated/at91_pm_data-offsets.h"
+
+#define	SRAMC_SELF_FRESH_ACTIVE		0x01
+#define	SRAMC_SELF_FRESH_EXIT		0x00
+
+pmc	.req	r0
+tmp1	.req	r4
+tmp2	.req	r5
+
+/*
+ * Wait until master clock is ready (after switching master clock source)
+ */
+	.macro wait_mckrdy
+1:	ldr	tmp1, [pmc, #AT91_PMC_SR]
+	tst	tmp1, #AT91_PMC_MCKRDY
+	beq	1b
+	.endm
+
+/*
+ * Wait until master oscillator has stabilized.
+ */
+	.macro wait_moscrdy
+1:	ldr	tmp1, [pmc, #AT91_PMC_SR]
+	tst	tmp1, #AT91_PMC_MOSCS
+	beq	1b
+	.endm
+
+/*
+ * Wait for main oscillator selection is done
+ */
+	.macro wait_moscsels
+1:	ldr	tmp1, [pmc, #AT91_PMC_SR]
+	tst	tmp1, #AT91_PMC_MOSCSELS
+	beq	1b
+	.endm
+
+/*
+ * Wait until PLLA has locked.
+ */
+	.macro wait_pllalock
+1:	ldr	tmp1, [pmc, #AT91_PMC_SR]
+	tst	tmp1, #AT91_PMC_LOCKA
+	beq	1b
+	.endm
+
+/*
+ * Put the processor to enter the idle state
+ */
+	.macro at91_cpu_idle
+
+#if defined(CONFIG_CPU_V7)
+	mov	tmp1, #AT91_PMC_PCK
+	str	tmp1, [pmc, #AT91_PMC_SCDR]
+
+	dsb
+
+	wfi		@ Wait For Interrupt
+#else
+	mcr	p15, 0, tmp1, c7, c0, 4
+#endif
+
+	.endm
+
+	.text
+
+	.arm
+
+/*
+ * void at91_suspend_sram_fn(struct at91_pm_data*)
+ * @input param:
+ * 	@r0: base address of struct at91_pm_data
+ */
+/* at91_pm_suspend_in_sram must be 8-byte aligned per the requirements of fncpy() */
+	.align 3
+ENTRY(at91_pm_suspend_in_sram)
+	/* Save registers on stack */
+	stmfd	sp!, {r4 - r12, lr}
+
+	/* Drain write buffer */
+	mov	tmp1, #0
+	mcr	p15, 0, tmp1, c7, c10, 4
+
+	ldr	tmp1, [r0, #PM_DATA_PMC]
+	str	tmp1, .pmc_base
+	ldr	tmp1, [r0, #PM_DATA_RAMC0]
+	str	tmp1, .sramc_base
+	ldr	tmp1, [r0, #PM_DATA_RAMC1]
+	str	tmp1, .sramc1_base
+	ldr	tmp1, [r0, #PM_DATA_MEMCTRL]
+	str	tmp1, .memtype
+	ldr	tmp1, [r0, #PM_DATA_MODE]
+	str	tmp1, .pm_mode
+	/* Both ldrne below are here to preload their address in the TLB */
+	ldr	tmp1, [r0, #PM_DATA_SHDWC]
+	str	tmp1, .shdwc
+	cmp	tmp1, #0
+	ldrne	tmp2, [tmp1, #0]
+	ldr	tmp1, [r0, #PM_DATA_SFRBU]
+	str	tmp1, .sfr
+	cmp	tmp1, #0
+	ldrne	tmp2, [tmp1, #0x10]
+
+	/* Active the self-refresh mode */
+	mov	r0, #SRAMC_SELF_FRESH_ACTIVE
+	bl	at91_sramc_self_refresh
+
+	ldr	r0, .pm_mode
+	cmp	r0, #AT91_PM_STANDBY
+	beq	standby
+	cmp	r0, #AT91_PM_BACKUP
+	beq	backup_mode
+
+	bl	at91_ulp_mode
+	b	exit_suspend
+
+standby:
+	/* Wait for interrupt */
+	ldr	pmc, .pmc_base
+	at91_cpu_idle
+	b	exit_suspend
+
+backup_mode:
+	bl	at91_backup_mode
+	b	exit_suspend
+
+exit_suspend:
+	/* Exit the self-refresh mode */
+	mov	r0, #SRAMC_SELF_FRESH_EXIT
+	bl	at91_sramc_self_refresh
+
+	/* Restore registers, and return */
+	ldmfd	sp!, {r4 - r12, pc}
+ENDPROC(at91_pm_suspend_in_sram)
+
+ENTRY(at91_backup_mode)
+	/*BUMEN*/
+	ldr	r0, .sfr
+	mov	tmp1, #0x1
+	str	tmp1, [r0, #0x10]
+
+	/* Shutdown */
+	ldr	r0, .shdwc
+	mov	tmp1, #0xA5000000
+	add	tmp1, tmp1, #0x1
+	str	tmp1, [r0, #0]
+ENDPROC(at91_backup_mode)
+
+.macro at91_pm_ulp0_mode
+	ldr	pmc, .pmc_base
+
+	/* Turn off the crystal oscillator */
+	ldr	tmp1, [pmc, #AT91_CKGR_MOR]
+	bic	tmp1, tmp1, #AT91_PMC_MOSCEN
+	orr	tmp1, tmp1, #AT91_PMC_KEY
+	str	tmp1, [pmc, #AT91_CKGR_MOR]
+
+	/* Wait for interrupt */
+	at91_cpu_idle
+
+	/* Turn on the crystal oscillator */
+	ldr	tmp1, [pmc, #AT91_CKGR_MOR]
+	orr	tmp1, tmp1, #AT91_PMC_MOSCEN
+	orr	tmp1, tmp1, #AT91_PMC_KEY
+	str	tmp1, [pmc, #AT91_CKGR_MOR]
+
+	wait_moscrdy
+.endm
+
+/**
+ * Note: This procedure only applies on the platform which uses
+ * the external crystal oscillator as a main clock source.
+ */
+.macro at91_pm_ulp1_mode
+	ldr	pmc, .pmc_base
+
+	/* Switch the main clock source to 12-MHz RC oscillator */
+	ldr	tmp1, [pmc, #AT91_CKGR_MOR]
+	bic	tmp1, tmp1, #AT91_PMC_MOSCSEL
+	bic	tmp1, tmp1, #AT91_PMC_KEY_MASK
+	orr	tmp1, tmp1, #AT91_PMC_KEY
+	str	tmp1, [pmc, #AT91_CKGR_MOR]
+
+	wait_moscsels
+
+	/* Disable the crystal oscillator */
+	ldr	tmp1, [pmc, #AT91_CKGR_MOR]
+	bic	tmp1, tmp1, #AT91_PMC_MOSCEN
+	bic	tmp1, tmp1, #AT91_PMC_KEY_MASK
+	orr	tmp1, tmp1, #AT91_PMC_KEY
+	str	tmp1, [pmc, #AT91_CKGR_MOR]
+
+	/* Switch the master clock source to main clock */
+	ldr	tmp1, [pmc, #AT91_PMC_MCKR]
+	bic	tmp1, tmp1, #AT91_PMC_CSS
+	orr	tmp1, tmp1, #AT91_PMC_CSS_MAIN
+	str	tmp1, [pmc, #AT91_PMC_MCKR]
+
+	wait_mckrdy
+
+	/* Enter the ULP1 mode by set WAITMODE bit in CKGR_MOR */
+	ldr	tmp1, [pmc, #AT91_CKGR_MOR]
+	orr	tmp1, tmp1, #AT91_PMC_WAITMODE
+	bic	tmp1, tmp1, #AT91_PMC_KEY_MASK
+	orr	tmp1, tmp1, #AT91_PMC_KEY
+	str	tmp1, [pmc, #AT91_CKGR_MOR]
+
+	wait_mckrdy
+
+	/* Enable the crystal oscillator */
+	ldr	tmp1, [pmc, #AT91_CKGR_MOR]
+	orr	tmp1, tmp1, #AT91_PMC_MOSCEN
+	bic	tmp1, tmp1, #AT91_PMC_KEY_MASK
+	orr	tmp1, tmp1, #AT91_PMC_KEY
+	str	tmp1, [pmc, #AT91_CKGR_MOR]
+
+	wait_moscrdy
+
+	/* Switch the master clock source to slow clock */
+	ldr	tmp1, [pmc, #AT91_PMC_MCKR]
+	bic	tmp1, tmp1, #AT91_PMC_CSS
+	str	tmp1, [pmc, #AT91_PMC_MCKR]
+
+	wait_mckrdy
+
+	/* Switch main clock source to crystal oscillator */
+	ldr	tmp1, [pmc, #AT91_CKGR_MOR]
+	orr	tmp1, tmp1, #AT91_PMC_MOSCSEL
+	bic	tmp1, tmp1, #AT91_PMC_KEY_MASK
+	orr	tmp1, tmp1, #AT91_PMC_KEY
+	str	tmp1, [pmc, #AT91_CKGR_MOR]
+
+	wait_moscsels
+
+	/* Switch the master clock source to main clock */
+	ldr	tmp1, [pmc, #AT91_PMC_MCKR]
+	bic	tmp1, tmp1, #AT91_PMC_CSS
+	orr	tmp1, tmp1, #AT91_PMC_CSS_MAIN
+	str	tmp1, [pmc, #AT91_PMC_MCKR]
+
+	wait_mckrdy
+.endm
+
+ENTRY(at91_ulp_mode)
+	ldr	pmc, .pmc_base
+
+	/* Save Master clock setting */
+	ldr	tmp1, [pmc, #AT91_PMC_MCKR]
+	str	tmp1, .saved_mckr
+
+	/*
+	 * Set the Master clock source to slow clock
+	 */
+	bic	tmp1, tmp1, #AT91_PMC_CSS
+	str	tmp1, [pmc, #AT91_PMC_MCKR]
+
+	wait_mckrdy
+
+	/* Save PLLA setting and disable it */
+	ldr	tmp1, [pmc, #AT91_CKGR_PLLAR]
+	str	tmp1, .saved_pllar
+
+	mov	tmp1, #AT91_PMC_PLLCOUNT
+	orr	tmp1, tmp1, #(1 << 29)		/* bit 29 always set */
+	str	tmp1, [pmc, #AT91_CKGR_PLLAR]
+
+	ldr	r0, .pm_mode
+	cmp	r0, #AT91_PM_ULP1
+	beq	ulp1_mode
+
+	at91_pm_ulp0_mode
+	b	ulp_exit
+
+ulp1_mode:
+	at91_pm_ulp1_mode
+	b	ulp_exit
+
+ulp_exit:
+	ldr	pmc, .pmc_base
+
+	/* Restore PLLA setting */
+	ldr	tmp1, .saved_pllar
+	str	tmp1, [pmc, #AT91_CKGR_PLLAR]
+
+	tst	tmp1, #(AT91_PMC_MUL &  0xff0000)
+	bne	3f
+	tst	tmp1, #(AT91_PMC_MUL & ~0xff0000)
+	beq	4f
+3:
+	wait_pllalock
+4:
+
+	/*
+	 * Restore master clock setting
+	 */
+	ldr	tmp1, .saved_mckr
+	str	tmp1, [pmc, #AT91_PMC_MCKR]
+
+	wait_mckrdy
+
+	mov	pc, lr
+ENDPROC(at91_ulp_mode)
+
+/*
+ * void at91_sramc_self_refresh(unsigned int is_active)
+ *
+ * @input param:
+ *	@r0: 1 - active self-refresh mode
+ *	     0 - exit self-refresh mode
+ * register usage:
+ * 	@r1: memory type
+ *	@r2: base address of the sram controller
+ */
+
+ENTRY(at91_sramc_self_refresh)
+	ldr	r1, .memtype
+	ldr	r2, .sramc_base
+
+	cmp	r1, #AT91_MEMCTRL_MC
+	bne	ddrc_sf
+
+	/*
+	 * at91rm9200 Memory controller
+	 */
+
+	 /*
+	  * For exiting the self-refresh mode, do nothing,
+	  * automatically exit the self-refresh mode.
+	  */
+	tst	r0, #SRAMC_SELF_FRESH_ACTIVE
+	beq	exit_sramc_sf
+
+	/* Active SDRAM self-refresh mode */
+	mov	r3, #1
+	str	r3, [r2, #AT91_MC_SDRAMC_SRR]
+	b	exit_sramc_sf
+
+ddrc_sf:
+	cmp	r1, #AT91_MEMCTRL_DDRSDR
+	bne	sdramc_sf
+
+	/*
+	 * DDR Memory controller
+	 */
+	tst	r0, #SRAMC_SELF_FRESH_ACTIVE
+	beq	ddrc_exit_sf
+
+	/* LPDDR1 --> force DDR2 mode during self-refresh */
+	ldr	r3, [r2, #AT91_DDRSDRC_MDR]
+	str	r3, .saved_sam9_mdr
+	bic	r3, r3, #~AT91_DDRSDRC_MD
+	cmp	r3, #AT91_DDRSDRC_MD_LOW_POWER_DDR
+	ldreq	r3, [r2, #AT91_DDRSDRC_MDR]
+	biceq	r3, r3, #AT91_DDRSDRC_MD
+	orreq	r3, r3, #AT91_DDRSDRC_MD_DDR2
+	streq	r3, [r2, #AT91_DDRSDRC_MDR]
+
+	/* Active DDRC self-refresh mode */
+	ldr	r3, [r2, #AT91_DDRSDRC_LPR]
+	str	r3, .saved_sam9_lpr
+	bic	r3, r3, #AT91_DDRSDRC_LPCB
+	orr	r3, r3, #AT91_DDRSDRC_LPCB_SELF_REFRESH
+	str	r3, [r2, #AT91_DDRSDRC_LPR]
+
+	/* If using the 2nd ddr controller */
+	ldr	r2, .sramc1_base
+	cmp	r2, #0
+	beq	no_2nd_ddrc
+
+	ldr	r3, [r2, #AT91_DDRSDRC_MDR]
+	str	r3, .saved_sam9_mdr1
+	bic	r3, r3, #~AT91_DDRSDRC_MD
+	cmp	r3, #AT91_DDRSDRC_MD_LOW_POWER_DDR
+	ldreq	r3, [r2, #AT91_DDRSDRC_MDR]
+	biceq	r3, r3, #AT91_DDRSDRC_MD
+	orreq	r3, r3, #AT91_DDRSDRC_MD_DDR2
+	streq	r3, [r2, #AT91_DDRSDRC_MDR]
+
+	/* Active DDRC self-refresh mode */
+	ldr	r3, [r2, #AT91_DDRSDRC_LPR]
+	str	r3, .saved_sam9_lpr1
+	bic	r3, r3, #AT91_DDRSDRC_LPCB
+	orr	r3, r3, #AT91_DDRSDRC_LPCB_SELF_REFRESH
+	str	r3, [r2, #AT91_DDRSDRC_LPR]
+
+no_2nd_ddrc:
+	b	exit_sramc_sf
+
+ddrc_exit_sf:
+	/* Restore MDR in case of LPDDR1 */
+	ldr	r3, .saved_sam9_mdr
+	str	r3, [r2, #AT91_DDRSDRC_MDR]
+	/* Restore LPR on AT91 with DDRAM */
+	ldr	r3, .saved_sam9_lpr
+	str	r3, [r2, #AT91_DDRSDRC_LPR]
+
+	/* If using the 2nd ddr controller */
+	ldr	r2, .sramc1_base
+	cmp	r2, #0
+	ldrne	r3, .saved_sam9_mdr1
+	strne	r3, [r2, #AT91_DDRSDRC_MDR]
+	ldrne	r3, .saved_sam9_lpr1
+	strne	r3, [r2, #AT91_DDRSDRC_LPR]
+
+	b	exit_sramc_sf
+
+	/*
+	 * SDRAMC Memory controller
+	 */
+sdramc_sf:
+	tst	r0, #SRAMC_SELF_FRESH_ACTIVE
+	beq	sdramc_exit_sf
+
+	/* Active SDRAMC self-refresh mode */
+	ldr	r3, [r2, #AT91_SDRAMC_LPR]
+	str	r3, .saved_sam9_lpr
+	bic	r3, r3, #AT91_SDRAMC_LPCB
+	orr	r3, r3, #AT91_SDRAMC_LPCB_SELF_REFRESH
+	str	r3, [r2, #AT91_SDRAMC_LPR]
+
+sdramc_exit_sf:
+	ldr	r3, .saved_sam9_lpr
+	str	r3, [r2, #AT91_SDRAMC_LPR]
+
+exit_sramc_sf:
+	mov	pc, lr
+ENDPROC(at91_sramc_self_refresh)
+
+.pmc_base:
+	.word 0
+.sramc_base:
+	.word 0
+.sramc1_base:
+	.word 0
+.shdwc:
+	.word 0
+.sfr:
+	.word 0
+.memtype:
+	.word 0
+.pm_mode:
+	.word 0
+.saved_mckr:
+	.word 0
+.saved_pllar:
+	.word 0
+.saved_sam9_lpr:
+	.word 0
+.saved_sam9_lpr1:
+	.word 0
+.saved_sam9_mdr:
+	.word 0
+.saved_sam9_mdr1:
+	.word 0
+
+ENTRY(at91_pm_suspend_in_sram_sz)
+	.word .-at91_pm_suspend_in_sram
diff --git a/arch/arm/mach-at91/sama5.c b/arch/arm/mach-at91/sama5.c
new file mode 100644
index 0000000..3d0bf95
--- /dev/null
+++ b/arch/arm/mach-at91/sama5.c
@@ -0,0 +1,64 @@
+/*
+ *  Setup code for SAMA5
+ *
+ *  Copyright (C) 2013 Atmel,
+ *                2013 Ludovic Desroches <ludovic.desroches@atmel.com>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/system_misc.h>
+
+#include "generic.h"
+
+static void __init sama5_dt_device_init(void)
+{
+	of_platform_default_populate(NULL, NULL, NULL);
+	sama5_pm_init();
+}
+
+static const char *const sama5_dt_board_compat[] __initconst = {
+	"atmel,sama5",
+	NULL
+};
+
+DT_MACHINE_START(sama5_dt, "Atmel SAMA5")
+	/* Maintainer: Atmel */
+	.init_machine	= sama5_dt_device_init,
+	.dt_compat	= sama5_dt_board_compat,
+MACHINE_END
+
+static const char *const sama5_alt_dt_board_compat[] __initconst = {
+	"atmel,sama5d4",
+	NULL
+};
+
+DT_MACHINE_START(sama5_alt_dt, "Atmel SAMA5")
+	/* Maintainer: Atmel */
+	.init_machine	= sama5_dt_device_init,
+	.dt_compat	= sama5_alt_dt_board_compat,
+	.l2c_aux_mask	= ~0UL,
+MACHINE_END
+
+static void __init sama5d2_init(void)
+{
+	of_platform_default_populate(NULL, NULL, NULL);
+	sama5d2_pm_init();
+}
+
+static const char *const sama5d2_compat[] __initconst = {
+	"atmel,sama5d2",
+	NULL
+};
+
+DT_MACHINE_START(sama5d2, "Atmel SAMA5")
+	/* Maintainer: Atmel */
+	.init_machine	= sama5d2_init,
+	.dt_compat	= sama5d2_compat,
+	.l2c_aux_mask	= ~0UL,
+MACHINE_END
diff --git a/arch/arm/mach-at91/samv7.c b/arch/arm/mach-at91/samv7.c
new file mode 100644
index 0000000..11386f1
--- /dev/null
+++ b/arch/arm/mach-at91/samv7.c
@@ -0,0 +1,25 @@
+/*
+ *  Setup code for SAMv7x
+ *
+ *  Copyright (C) 2013 Atmel,
+ *                2016 Andras Szemzo <szemzo.andras@gmail.com>
+ *
+ * Licensed under GPLv2 or later.
+ */
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/system_misc.h>
+#include "generic.h"
+
+static const char *const samv7_dt_board_compat[] __initconst = {
+	"atmel,samv7",
+	NULL
+};
+
+DT_MACHINE_START(samv7_dt, "Atmel SAMV7")
+	.dt_compat	= samv7_dt_board_compat,
+MACHINE_END