diff options
author | Nicolas Ferre <nicolas.ferre@atmel.com> | 2014-08-25 15:35:26 +0200 |
---|---|---|
committer | Nicolas Ferre <nicolas.ferre@atmel.com> | 2014-08-25 15:35:26 +0200 |
commit | e657ce689a85ff1c887b8020282e5b2ab1411ded (patch) | |
tree | b9e9195aeaeb77fc7fa730eac0ce98bcbe267dea | |
parent | 39333610ba3b1e076ac1ceccca34de037a413a03 (diff) | |
parent | ae499f0fadaf28bf3138676fa2d3f6cf7d57556a (diff) |
Merge tag 'at91-drivers-for-3.17' of git://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux
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
-rw-r--r-- | Documentation/devicetree/bindings/arm/atmel-at91.txt | 1 | ||||
-rw-r--r-- | arch/arm/mach-at91/Kconfig | 4 | ||||
-rw-r--r-- | arch/arm/mach-at91/setup.c | 1 | ||||
-rw-r--r-- | drivers/clk/at91/clk-system.c | 8 | ||||
-rw-r--r-- | drivers/memory/Kconfig | 10 | ||||
-rw-r--r-- | drivers/memory/Makefile | 1 | ||||
-rw-r--r-- | drivers/memory/atmel-sdramc.c | 98 | ||||
-rw-r--r-- | drivers/power/reset/Kconfig | 33 | ||||
-rw-r--r-- | drivers/power/reset/Makefile | 2 | ||||
-rw-r--r-- | drivers/power/reset/at91-poweroff.c | 156 | ||||
-rw-r--r-- | drivers/power/reset/at91-reset.c | 252 |
11 files changed, 551 insertions, 15 deletions
diff --git a/Documentation/devicetree/bindings/arm/atmel-at91.txt b/Documentation/devicetree/bindings/arm/atmel-at91.txt index 16f60b41c14..33bcf8b5779 100644 --- a/Documentation/devicetree/bindings/arm/atmel-at91.txt +++ b/Documentation/devicetree/bindings/arm/atmel-at91.txt @@ -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. diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index dd28e1fedbd..6aa7ab47205 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -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" diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c index 0bf893a574f..1174b5fe74d 100644 --- a/arch/arm/mach-at91/setup.c +++ b/arch/arm/mach-at91/setup.c @@ -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*/ } }; diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c index 8c96307d736..a76d03fd577 100644 --- a/drivers/clk/at91/clk-system.c +++ b/drivers/clk/at91/clk-system.c @@ -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; diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig index fab81a143bd..6d91c27fd4c 100644 --- a/drivers/memory/Kconfig +++ b/drivers/memory/Kconfig @@ -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 diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile index 4055c47f45a..c32d31981be 100644 --- a/drivers/memory/Makefile +++ b/drivers/memory/Makefile @@ -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 index 00000000000..fed04e8efe7 --- /dev/null +++ b/drivers/memory/atmel-sdramc.c @@ -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"); diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index ca41523bbeb..c1c046d0464 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -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 diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index a42e70edd03..4433a753fd9 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -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 index 00000000000..40bf42d146f --- /dev/null +++ b/drivers/power/reset/at91-poweroff.c @@ -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 index 00000000000..3611806c9cf --- /dev/null +++ b/drivers/power/reset/at91-reset.c @@ -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); |