]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/commitdiff
Merge tag 'at91-drivers-for-3.17' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorNicolas Ferre <nicolas.ferre@atmel.com>
Mon, 25 Aug 2014 13:35:26 +0000 (15:35 +0200)
committerNicolas Ferre <nicolas.ferre@atmel.com>
Mon, 25 Aug 2014 13:35:26 +0000 (15:35 +0200)
Pull AT91 reset, poweroff and ram drivers from Maxime Ripard:
 "This tag holds the various new drivers introduced to move code that used to be
  in mach-at91 over to the proper frameworks.

  These files are the reboot and poweroff code for all AT91 SoCs but the RM9200,
  and the ram controller driver is not doing much at the time, except for grabing
  the RAM clock in order to leave it always enabled."

Conflicts:
arch/arm/mach-at91/Kconfig

Documentation/devicetree/bindings/arm/atmel-at91.txt
arch/arm/mach-at91/Kconfig
arch/arm/mach-at91/setup.c
drivers/clk/at91/clk-system.c
drivers/memory/Kconfig
drivers/memory/Makefile
drivers/memory/atmel-sdramc.c [new file with mode: 0644]
drivers/power/reset/Kconfig
drivers/power/reset/Makefile
drivers/power/reset/at91-poweroff.c [new file with mode: 0644]
drivers/power/reset/at91-reset.c [new file with mode: 0644]

index 16f60b41c14722893584faf3e60148e2edb56f39..33bcf8b57798b8687d9ccaf1c004f4e88cb59bd3 100644 (file)
@@ -61,6 +61,7 @@ RAMC SDRAM/DDR Controller required properties:
 - compatible: Should be "atmel,at91rm9200-sdramc",
                        "atmel,at91sam9260-sdramc",
                        "atmel,at91sam9g45-ddramc",
+                       "atmel,sama5d3-ddramc",
 - reg: Should contain registers location and length
   For at91sam9263 and at91sam9g45 you must specify 2 entries.
 
index dd28e1fedbdce67ac24269cedbb6e6558b67f6a2..6aa7ab47205e767a3926088b48dc84035c5e2304 100644 (file)
@@ -53,6 +53,8 @@ config SOC_AT91SAM9
        select ATMEL_AIC_IRQ if !OLD_IRQ_AT91
        select CPU_ARM926T
        select GENERIC_CLOCKEVENTS
+       select MEMORY if USE_OF
+       select ATMEL_SDRAMC if USE_OF
 
 config SOC_SAMA5
        bool
@@ -61,6 +63,8 @@ config SOC_SAMA5
        select CPU_V7
        select GENERIC_CLOCKEVENTS
        select USE_OF
+       select MEMORY
+       select ATMEL_SDRAMC
 
 menu "Atmel AT91 System-on-Chip"
 
index 0bf893a574f984ec7844eb0626e9ed90dee389ce..1174b5fe74d88bfeecd95bbf44c5a084ccdf8e77 100644 (file)
@@ -385,6 +385,7 @@ static struct of_device_id ramc_ids[] = {
        { .compatible = "atmel,at91rm9200-sdramc", .data = at91rm9200_standby },
        { .compatible = "atmel,at91sam9260-sdramc", .data = at91sam9_sdram_standby },
        { .compatible = "atmel,at91sam9g45-ddramc", .data = at91_ddr_standby },
+       { .compatible = "atmel,sama5d3-ddramc", .data = at91_ddr_standby },
        { /*sentinel*/ }
 };
 
index 8c96307d736343e3cbc6924c09580408f32c2da7..a76d03fd577be05fe86a95d51092380ab01e4eb1 100644 (file)
@@ -119,13 +119,7 @@ at91_clk_register_system(struct at91_pmc *pmc, const char *name,
        init.ops = &system_ops;
        init.parent_names = &parent_name;
        init.num_parents = 1;
-       /*
-        * CLK_IGNORE_UNUSED is used to avoid ddrck switch off.
-        * TODO : we should implement a driver supporting at91 ddr controller
-        * (see drivers/memory) which would request and enable the ddrck clock.
-        * When this is done we will be able to remove CLK_IGNORE_UNUSED flag.
-        */
-       init.flags = CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED;
+       init.flags = CLK_SET_RATE_PARENT;
 
        sys->id = id;
        sys->hw.init = &init;
index fab81a143bd7ea80e257cf21637e99872ed751c0..6d91c27fd4c8f325f29b7d21f5e93a89cff59691 100644 (file)
@@ -7,6 +7,16 @@ menuconfig MEMORY
 
 if MEMORY
 
+config ATMEL_SDRAMC
+       bool "Atmel (Multi-port DDR-)SDRAM Controller"
+       default y
+       depends on ARCH_AT91 && OF
+       help
+         This driver is for Atmel SDRAM Controller or Atmel Multi-port
+         DDR-SDRAM Controller available on Atmel AT91SAM9 and SAMA5 SoCs.
+         Starting with the at91sam9g45, this controller supports SDR, DDR and
+         LP-DDR memories.
+
 config TI_AEMIF
        tristate "Texas Instruments AEMIF driver"
        depends on (ARCH_DAVINCI || ARCH_KEYSTONE) && OF
index 4055c47f45ab337362524804bfeff4d20ca8699c..c32d31981be314518d16c8933177937e97841545 100644 (file)
@@ -5,6 +5,7 @@
 ifeq ($(CONFIG_DDR),y)
 obj-$(CONFIG_OF)               += of_memory.o
 endif
+obj-$(CONFIG_ATMEL_SDRAMC)     += atmel-sdramc.o
 obj-$(CONFIG_TI_AEMIF)         += ti-aemif.o
 obj-$(CONFIG_TI_EMIF)          += emif.o
 obj-$(CONFIG_FSL_CORENET_CF)   += fsl-corenet-cf.o
diff --git a/drivers/memory/atmel-sdramc.c b/drivers/memory/atmel-sdramc.c
new file mode 100644 (file)
index 0000000..fed04e8
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Atmel (Multi-port DDR-)SDRAM Controller driver
+ *
+ * Copyright (C) 2014 Atmel
+ *
+ * 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 version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+struct at91_ramc_caps {
+       bool has_ddrck;
+       bool has_mpddr_clk;
+};
+
+static const struct at91_ramc_caps at91rm9200_caps = { };
+
+static const struct at91_ramc_caps at91sam9g45_caps = {
+       .has_ddrck = 1,
+       .has_mpddr_clk = 0,
+};
+
+static const struct at91_ramc_caps sama5d3_caps = {
+       .has_ddrck = 1,
+       .has_mpddr_clk = 1,
+};
+
+static const struct of_device_id atmel_ramc_of_match[] = {
+       { .compatible = "atmel,at91rm9200-sdramc", .data = &at91rm9200_caps, },
+       { .compatible = "atmel,at91sam9260-sdramc", .data = &at91rm9200_caps, },
+       { .compatible = "atmel,at91sam9g45-ddramc", .data = &at91sam9g45_caps, },
+       { .compatible = "atmel,sama5d3-ddramc", .data = &sama5d3_caps, },
+       {},
+};
+MODULE_DEVICE_TABLE(of, atmel_ramc_of_match);
+
+static int atmel_ramc_probe(struct platform_device *pdev)
+{
+       const struct of_device_id *match;
+       const struct at91_ramc_caps *caps;
+       struct clk *clk;
+
+       match = of_match_device(atmel_ramc_of_match, &pdev->dev);
+       caps = match->data;
+
+       if (caps->has_ddrck) {
+               clk = devm_clk_get(&pdev->dev, "ddrck");
+               if (IS_ERR(clk))
+                       return PTR_ERR(clk);
+               clk_prepare_enable(clk);
+       }
+
+       if (caps->has_mpddr_clk) {
+               clk = devm_clk_get(&pdev->dev, "mpddr");
+               if (IS_ERR(clk)) {
+                       pr_err("AT91 RAMC: couldn't get mpddr clock\n");
+                       return PTR_ERR(clk);
+               }
+               clk_prepare_enable(clk);
+       }
+
+       return 0;
+}
+
+static struct platform_driver atmel_ramc_driver = {
+       .probe          = atmel_ramc_probe,
+       .driver         = {
+               .name   = "atmel-ramc",
+               .owner  = THIS_MODULE,
+               .of_match_table = atmel_ramc_of_match,
+       },
+};
+
+static int __init atmel_ramc_init(void)
+{
+       return platform_driver_register(&atmel_ramc_driver);
+}
+module_init(atmel_ramc_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");
+MODULE_DESCRIPTION("Atmel (Multi-port DDR-)SDRAM Controller");
index ca41523bbebf767ad1af56594cd1ba57d297b06e..c1c046d0464e398d9d732b19969c791dfb6298c1 100644 (file)
@@ -6,15 +6,33 @@ menuconfig POWER_RESET
 
          Say Y here to enable board reset and power off
 
+if POWER_RESET
+
 config POWER_RESET_AS3722
        bool "ams AS3722 power-off driver"
-       depends on MFD_AS3722 && POWER_RESET
+       depends on MFD_AS3722
        help
          This driver supports turning off board via a ams AS3722 power-off.
 
+config POWER_RESET_AT91_POWEROFF
+       bool "Atmel AT91 poweroff driver"
+       depends on MACH_AT91
+       default SOC_AT91SAM9 || SOC_SAMA5
+       help
+         This driver supports poweroff for Atmel AT91SAM9 and SAMA5
+         SoCs
+
+config POWER_RESET_AT91_RESET
+       bool "Atmel AT91 reset driver"
+       depends on MACH_AT91
+       default SOC_AT91SAM9 || SOC_SAMA5
+       help
+         This driver supports restart for Atmel AT91SAM9 and SAMA5
+         SoCs
+
 config POWER_RESET_AXXIA
        bool "LSI Axxia reset driver"
-       depends on POWER_RESET && ARCH_AXXIA
+       depends on ARCH_AXXIA
        help
          This driver supports restart for Axxia SoC.
 
@@ -33,7 +51,7 @@ config POWER_RESET_BRCMSTB
 
 config POWER_RESET_GPIO
        bool "GPIO power-off driver"
-       depends on OF_GPIO && POWER_RESET
+       depends on OF_GPIO
        help
          This driver supports turning off your board via a GPIO line.
          If your board needs a GPIO high/low to power down, say Y and
@@ -47,13 +65,13 @@ config POWER_RESET_HISI
 
 config POWER_RESET_MSM
        bool "Qualcomm MSM power-off driver"
-       depends on POWER_RESET && ARCH_QCOM
+       depends on ARCH_QCOM
        help
          Power off and restart support for Qualcomm boards.
 
 config POWER_RESET_QNAP
        bool "QNAP power-off driver"
-       depends on OF_GPIO && POWER_RESET && PLAT_ORION
+       depends on OF_GPIO && PLAT_ORION
        help
          This driver supports turning off QNAP NAS devices by sending
          commands to the microcontroller which controls the main power.
@@ -71,14 +89,13 @@ config POWER_RESET_RESTART
 config POWER_RESET_SUN6I
        bool "Allwinner A31 SoC reset driver"
        depends on ARCH_SUNXI
-       depends on POWER_RESET
        help
          Reboot support for the Allwinner A31 SoCs.
 
 config POWER_RESET_VEXPRESS
        bool "ARM Versatile Express power-off and reset driver"
        depends on ARM || ARM64
-       depends on POWER_RESET && VEXPRESS_CONFIG
+       depends on VEXPRESS_CONFIG
        help
          Power off and reset support for the ARM Ltd. Versatile
          Express boards.
@@ -86,7 +103,6 @@ config POWER_RESET_VEXPRESS
 config POWER_RESET_XGENE
        bool "APM SoC X-Gene reset driver"
        depends on ARM64
-       depends on POWER_RESET
        help
          Reboot support for the APM SoC X-Gene Eval boards.
 
@@ -97,3 +113,4 @@ config POWER_RESET_KEYSTONE
        help
          Reboot support for the KEYSTONE SoCs.
 
+endif
index a42e70edd0376912b88b4f4a4a3f3fc01ef17496..4433a753fd937eb4b2e1553bd86f1c61deea763a 100644 (file)
@@ -1,4 +1,6 @@
 obj-$(CONFIG_POWER_RESET_AS3722) += as3722-poweroff.o
+obj-$(CONFIG_POWER_RESET_AT91_POWEROFF) += at91-poweroff.o
+obj-$(CONFIG_POWER_RESET_AT91_RESET) += at91-reset.o
 obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o
 obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o
 obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
diff --git a/drivers/power/reset/at91-poweroff.c b/drivers/power/reset/at91-poweroff.c
new file mode 100644 (file)
index 0000000..40bf42d
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Atmel AT91 SAM9 SoCs reset code
+ *
+ * Copyright (C) 2007 Atmel Corporation.
+ * Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
+ * Copyright (C) 2014 Free Electrons
+ *
+ * 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 <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+
+#define AT91_SHDW_CR   0x00            /* Shut Down Control Register */
+#define AT91_SHDW_SHDW         BIT(0)                  /* Shut Down command */
+#define AT91_SHDW_KEY          (0xa5 << 24)            /* KEY Password */
+
+#define AT91_SHDW_MR   0x04            /* Shut Down Mode Register */
+#define AT91_SHDW_WKMODE0      GENMASK(2, 0)           /* Wake-up 0 Mode Selection */
+#define AT91_SHDW_CPTWK0_MAX   0xf                     /* Maximum Counter On Wake Up 0 */
+#define AT91_SHDW_CPTWK0       (AT91_SHDW_CPTWK0_MAX << 4) /* Counter On Wake Up 0 */
+#define AT91_SHDW_CPTWK0_(x)   ((x) << 4)
+#define AT91_SHDW_RTTWKEN      BIT(16)                 /* Real Time Timer Wake-up Enable */
+#define AT91_SHDW_RTCWKEN      BIT(17)                 /* Real Time Clock Wake-up Enable */
+
+#define AT91_SHDW_SR   0x08            /* Shut Down Status Register */
+#define AT91_SHDW_WAKEUP0      BIT(0)                  /* Wake-up 0 Status */
+#define AT91_SHDW_RTTWK                BIT(16)                 /* Real-time Timer Wake-up */
+#define AT91_SHDW_RTCWK                BIT(17)                 /* Real-time Clock Wake-up [SAM9RL] */
+
+enum wakeup_type {
+       AT91_SHDW_WKMODE0_NONE          = 0,
+       AT91_SHDW_WKMODE0_HIGH          = 1,
+       AT91_SHDW_WKMODE0_LOW           = 2,
+       AT91_SHDW_WKMODE0_ANYLEVEL      = 3,
+};
+
+static const char *shdwc_wakeup_modes[] = {
+       [AT91_SHDW_WKMODE0_NONE]        = "none",
+       [AT91_SHDW_WKMODE0_HIGH]        = "high",
+       [AT91_SHDW_WKMODE0_LOW]         = "low",
+       [AT91_SHDW_WKMODE0_ANYLEVEL]    = "any",
+};
+
+static void __iomem *at91_shdwc_base;
+
+static void __init at91_wakeup_status(void)
+{
+       u32 reg = readl(at91_shdwc_base);
+       char *reason = "unknown";
+
+       /* Simple power-on, just bail out */
+       if (!reg)
+               return;
+
+       if (reg & AT91_SHDW_RTTWK)
+               reason = "RTT";
+       else if (reg & AT91_SHDW_RTCWK)
+               reason = "RTC";
+
+       pr_info("AT91: Wake-Up source: %s\n", reason);
+}
+
+static void at91_poweroff(void)
+{
+       writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, at91_shdwc_base + AT91_SHDW_CR);
+}
+
+const enum wakeup_type at91_poweroff_get_wakeup_mode(struct device_node *np)
+{
+       const char *pm;
+       int err, i;
+
+       err = of_property_read_string(np, "atmel,wakeup-mode", &pm);
+       if (err < 0)
+               return AT91_SHDW_WKMODE0_ANYLEVEL;
+
+       for (i = 0; i < ARRAY_SIZE(shdwc_wakeup_modes); i++)
+               if (!strcasecmp(pm, shdwc_wakeup_modes[i]))
+                       return i;
+
+       return -ENODEV;
+}
+
+static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       enum wakeup_type wakeup_mode;
+       u32 mode = 0, tmp;
+
+       wakeup_mode = at91_poweroff_get_wakeup_mode(np);
+       if (wakeup_mode < 0) {
+               dev_warn(&pdev->dev, "shdwc unknown wakeup mode\n");
+               return;
+       }
+
+       if (!of_property_read_u32(np, "atmel,wakeup-counter", &tmp)) {
+               if (tmp > AT91_SHDW_CPTWK0_MAX) {
+                       dev_warn(&pdev->dev,
+                                "shdwc wakeup counter 0x%x > 0x%x reduce it to 0x%x\n",
+                                tmp, AT91_SHDW_CPTWK0_MAX, AT91_SHDW_CPTWK0_MAX);
+                       tmp = AT91_SHDW_CPTWK0_MAX;
+               }
+               mode |= AT91_SHDW_CPTWK0_(tmp);
+       }
+
+       if (of_property_read_bool(np, "atmel,wakeup-rtc-timer"))
+                       mode |= AT91_SHDW_RTCWKEN;
+
+       if (of_property_read_bool(np, "atmel,wakeup-rtt-timer"))
+                       mode |= AT91_SHDW_RTTWKEN;
+
+       writel(wakeup_mode | mode, at91_shdwc_base + AT91_SHDW_MR);
+}
+
+static int at91_poweroff_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       at91_shdwc_base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(at91_shdwc_base)) {
+               dev_err(&pdev->dev, "Could not map reset controller address\n");
+               return PTR_ERR(at91_shdwc_base);
+       }
+
+       at91_wakeup_status();
+
+       if (pdev->dev.of_node)
+               at91_poweroff_dt_set_wakeup_mode(pdev);
+
+       pm_power_off = at91_poweroff;
+
+       return 0;
+}
+
+static struct of_device_id at91_poweroff_of_match[] = {
+       { .compatible = "atmel,at91sam9260-shdwc", },
+       { .compatible = "atmel,at91sam9rl-shdwc", },
+       { .compatible = "atmel,at91sam9x5-shdwc", },
+       { /*sentinel*/ }
+};
+
+static struct platform_driver at91_poweroff_driver = {
+       .probe = at91_poweroff_probe,
+       .driver = {
+               .name = "at91-poweroff",
+               .of_match_table = at91_poweroff_of_match,
+       },
+};
+module_platform_driver(at91_poweroff_driver);
diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c
new file mode 100644 (file)
index 0000000..3611806
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * Atmel AT91 SAM9 SoCs reset code
+ *
+ * Copyright (C) 2007 Atmel Corporation.
+ * Copyright (C) BitBox Ltd 2010
+ * Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcosoft.com>
+ * Copyright (C) 2014 Free Electrons
+ *
+ * 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 <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+
+#include <asm/system_misc.h>
+
+#include <mach/at91sam9_ddrsdr.h>
+#include <mach/at91sam9_sdramc.h>
+
+#define AT91_RSTC_CR   0x00            /* Reset Controller Control Register */
+#define AT91_RSTC_PROCRST      BIT(0)          /* Processor Reset */
+#define AT91_RSTC_PERRST       BIT(2)          /* Peripheral Reset */
+#define AT91_RSTC_EXTRST       BIT(3)          /* External Reset */
+#define AT91_RSTC_KEY          (0xa5 << 24)    /* KEY Password */
+
+#define AT91_RSTC_SR   0x04            /* Reset Controller Status Register */
+#define AT91_RSTC_URSTS                BIT(0)          /* User Reset Status */
+#define AT91_RSTC_RSTTYP       GENMASK(10, 8)  /* Reset Type */
+#define AT91_RSTC_NRSTL                BIT(16)         /* NRST Pin Level */
+#define AT91_RSTC_SRCMP                BIT(17)         /* Software Reset Command in Progress */
+
+#define AT91_RSTC_MR   0x08            /* Reset Controller Mode Register */
+#define AT91_RSTC_URSTEN       BIT(0)          /* User Reset Enable */
+#define AT91_RSTC_URSTIEN      BIT(4)          /* User Reset Interrupt Enable */
+#define AT91_RSTC_ERSTL                GENMASK(11, 8)  /* External Reset Length */
+
+enum reset_type {
+       RESET_TYPE_GENERAL      = 0,
+       RESET_TYPE_WAKEUP       = 1,
+       RESET_TYPE_WATCHDOG     = 2,
+       RESET_TYPE_SOFTWARE     = 3,
+       RESET_TYPE_USER         = 4,
+};
+
+static void __iomem *at91_ramc_base[2], *at91_rstc_base;
+
+/*
+* unless the SDRAM is cleanly shutdown before we hit the
+* reset register it can be left driving the data bus and
+* killing the chance of a subsequent boot from NAND
+*/
+static void at91sam9260_restart(enum reboot_mode mode, const char *cmd)
+{
+       asm volatile(
+               /* Align to cache lines */
+               ".balign 32\n\t"
+
+               /* Disable SDRAM accesses */
+               "str    %2, [%0, #" __stringify(AT91_SDRAMC_TR) "]\n\t"
+
+               /* Power down SDRAM */
+               "str    %3, [%0, #" __stringify(AT91_SDRAMC_LPR) "]\n\t"
+
+               /* Reset CPU */
+               "str    %4, [%1, #" __stringify(AT91_RSTC_CR) "]\n\t"
+
+               "b      .\n\t"
+               :
+               : "r" (at91_ramc_base[0]),
+                 "r" (at91_rstc_base),
+                 "r" (1),
+                 "r" (AT91_SDRAMC_LPCB_POWER_DOWN),
+                 "r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST));
+}
+
+static void at91sam9g45_restart(enum reboot_mode mode, const char *cmd)
+{
+       asm volatile(
+               /*
+                * Test wether we have a second RAM controller to care
+                * about.
+                *
+                * First, test that we can dereference the virtual address.
+                */
+               "cmp    %1, #0\n\t"
+               "beq    1f\n\t"
+
+               /* Then, test that the RAM controller is enabled */
+               "ldr    r0, [%1]\n\t"
+               "cmp    r0, #0\n\t"
+
+               /* Align to cache lines */
+               ".balign 32\n\t"
+
+               /* Disable SDRAM0 accesses */
+               "1:     str     %3, [%0, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t"
+               /* Power down SDRAM0 */
+               "       str     %4, [%0, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t"
+               /* Disable SDRAM1 accesses */
+               "       strne   %3, [%1, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t"
+               /* Power down SDRAM1 */
+               "       strne   %4, [%1, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t"
+               /* Reset CPU */
+               "       str     %5, [%2, #" __stringify(AT91_RSTC_CR) "]\n\t"
+
+               "       b       .\n\t"
+               :
+               : "r" (at91_ramc_base[0]),
+                 "r" (at91_ramc_base[1]),
+                 "r" (at91_rstc_base),
+                 "r" (1),
+                 "r" (AT91_DDRSDRC_LPCB_POWER_DOWN),
+                 "r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST)
+               : "r0");
+}
+
+static void __init at91_reset_status(struct platform_device *pdev)
+{
+       u32 reg = readl(at91_rstc_base + AT91_RSTC_SR);
+       char *reason;
+
+       switch ((reg & AT91_RSTC_RSTTYP) >> 8) {
+       case RESET_TYPE_GENERAL:
+               reason = "general reset";
+               break;
+       case RESET_TYPE_WAKEUP:
+               reason = "wakeup";
+               break;
+       case RESET_TYPE_WATCHDOG:
+               reason = "watchdog reset";
+               break;
+       case RESET_TYPE_SOFTWARE:
+               reason = "software reset";
+               break;
+       case RESET_TYPE_USER:
+               reason = "user reset";
+               break;
+       default:
+               reason = "unknown reset";
+               break;
+       }
+
+       pr_info("AT91: Starting after %s\n", reason);
+}
+
+static struct of_device_id at91_ramc_of_match[] = {
+       { .compatible = "atmel,at91sam9260-sdramc", },
+       { .compatible = "atmel,at91sam9g45-ddramc", },
+       { .compatible = "atmel,sama5d3-ddramc", },
+       { /* sentinel */ }
+};
+
+static struct of_device_id at91_reset_of_match[] = {
+       { .compatible = "atmel,at91sam9260-rstc", .data = at91sam9260_restart },
+       { .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart },
+       { /* sentinel */ }
+};
+
+static int at91_reset_of_probe(struct platform_device *pdev)
+{
+       const struct of_device_id *match;
+       struct device_node *np;
+       int idx = 0;
+
+       at91_rstc_base = of_iomap(pdev->dev.of_node, 0);
+       if (!at91_rstc_base) {
+               dev_err(&pdev->dev, "Could not map reset controller address\n");
+               return -ENODEV;
+       }
+
+       for_each_matching_node(np, at91_ramc_of_match) {
+               at91_ramc_base[idx] = of_iomap(np, 0);
+               if (!at91_ramc_base[idx]) {
+                       dev_err(&pdev->dev, "Could not map ram controller address\n");
+                       return -ENODEV;
+               }
+               idx++;
+       }
+
+       match = of_match_node(at91_reset_of_match, pdev->dev.of_node);
+       arm_pm_restart = match->data;
+
+       return 0;
+}
+
+static int at91_reset_platform_probe(struct platform_device *pdev)
+{
+       const struct platform_device_id *match;
+       struct resource *res;
+       int idx = 0;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       at91_rstc_base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(at91_rstc_base)) {
+               dev_err(&pdev->dev, "Could not map reset controller address\n");
+               return PTR_ERR(at91_rstc_base);
+       }
+
+       for (idx = 0; idx < 2; idx++) {
+               res = platform_get_resource(pdev, IORESOURCE_MEM, idx + 1 );
+               at91_ramc_base[idx] = devm_ioremap(&pdev->dev, res->start,
+                                                  resource_size(res));
+               if (IS_ERR(at91_ramc_base[idx])) {
+                       dev_err(&pdev->dev, "Could not map ram controller address\n");
+                       return PTR_ERR(at91_ramc_base[idx]);
+               }
+       }
+
+       match = platform_get_device_id(pdev);
+       arm_pm_restart = (void (*)(enum reboot_mode, const char*))
+               match->driver_data;
+
+       return 0;
+}
+
+static int at91_reset_probe(struct platform_device *pdev)
+{
+       int ret;
+
+       if (pdev->dev.of_node)
+               ret = at91_reset_of_probe(pdev);
+       else
+               ret = at91_reset_platform_probe(pdev);
+
+       if (ret)
+               return ret;
+
+       at91_reset_status(pdev);
+
+       return 0;
+}
+
+static struct platform_device_id at91_reset_plat_match[] = {
+       { "at91-sam9260-reset", (unsigned long)at91sam9260_restart },
+       { "at91-sam9g45-reset", (unsigned long)at91sam9g45_restart },
+       { /* sentinel */ }
+};
+
+static struct platform_driver at91_reset_driver = {
+       .probe = at91_reset_probe,
+       .driver = {
+               .name = "at91-reset",
+               .of_match_table = at91_reset_of_match,
+       },
+       .id_table = at91_reset_plat_match,
+};
+module_platform_driver(at91_reset_driver);