diff options
Diffstat (limited to 'arch/arm/mach-at91')
-rw-r--r-- | arch/arm/mach-at91/Kconfig | 18 | ||||
-rw-r--r-- | arch/arm/mach-at91/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-at91/generic.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-at91/pm.c | 343 | ||||
-rw-r--r-- | arch/arm/mach-at91/pm.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-at91/pm_data-offsets.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-at91/pm_suspend.S | 827 | ||||
-rw-r--r-- | arch/arm/mach-at91/sama7.c | 33 |
8 files changed, 940 insertions, 289 deletions
diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index ccd7e80ce943..b09bb2279f7f 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -57,6 +57,16 @@ config SOC_SAMA5D4 help Select this if you are using one of Microchip's SAMA5D4 family SoC. +config SOC_SAMA7G5 + bool "SAMA7G5 family" + depends on ARCH_MULTI_V7 + select HAVE_AT91_GENERATED_CLK + select HAVE_AT91_SAM9X60_PLL + select HAVE_AT91_UTMI + select SOC_SAMA7 + help + Select this if you are using one of Microchip's SAMA7G5 family SoC. + config SOC_AT91RM9200 bool "AT91RM9200" depends on ARCH_MULTI_V4T @@ -191,4 +201,12 @@ config SOC_SAMA5 config ATMEL_PM bool +config SOC_SAMA7 + bool + select ARM_GIC + select ATMEL_PM if PM + select ATMEL_SDRAMC + select MEMORY + select SOC_SAM_V7 + select SRAM if PM endif diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile index f565490f1b70..522b680b6446 100644 --- a/arch/arm/mach-at91/Makefile +++ b/arch/arm/mach-at91/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_SOC_AT91RM9200) += at91rm9200.o obj-$(CONFIG_SOC_AT91SAM9) += at91sam9.o obj-$(CONFIG_SOC_SAM9X60) += sam9x60.o obj-$(CONFIG_SOC_SAMA5) += sama5.o +obj-$(CONFIG_SOC_SAMA7) += sama7.o obj-$(CONFIG_SOC_SAMV7) += samv7.o # Power Management diff --git a/arch/arm/mach-at91/generic.h b/arch/arm/mach-at91/generic.h index 0a4cdcb4985b..0c3960a8b3eb 100644 --- a/arch/arm/mach-at91/generic.h +++ b/arch/arm/mach-at91/generic.h @@ -14,12 +14,14 @@ extern void __init at91sam9_pm_init(void); extern void __init sam9x60_pm_init(void); extern void __init sama5_pm_init(void); extern void __init sama5d2_pm_init(void); +extern void __init sama7_pm_init(void); #else static inline void __init at91rm9200_pm_init(void) { } static inline void __init at91sam9_pm_init(void) { } static inline void __init sam9x60_pm_init(void) { } static inline void __init sama5_pm_init(void) { } static inline void __init sama5d2_pm_init(void) { } +static inline void __init sama7_pm_init(void) { } #endif #endif /* _AT91_GENERIC_H */ diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c index 90dcdfe3b3d0..d6cfe7c4bb00 100644 --- a/arch/arm/mach-at91/pm.c +++ b/arch/arm/mach-at91/pm.c @@ -10,6 +10,7 @@ #include <linux/io.h> #include <linux/of_address.h> #include <linux/of.h> +#include <linux/of_fdt.h> #include <linux/of_platform.h> #include <linux/parser.h> #include <linux/suspend.h> @@ -27,13 +28,55 @@ #include "generic.h" #include "pm.h" +#define BACKUP_DDR_PHY_CALIBRATION (9) + +/** + * struct at91_pm_bu - AT91 power management backup unit data structure + * @suspended: true if suspended to backup mode + * @reserved: reserved + * @canary: canary data for memory checking after exit from backup mode + * @resume: resume API + * @ddr_phy_calibration: DDR PHY calibration data: ZQ0CR0, first 8 words + * of the memory + */ +struct at91_pm_bu { + int suspended; + unsigned long reserved; + phys_addr_t canary; + phys_addr_t resume; + unsigned long ddr_phy_calibration[BACKUP_DDR_PHY_CALIBRATION]; +}; + +/** + * struct at91_soc_pm - AT91 SoC power management data structure + * @config_shdwc_ws: wakeup sources configuration function for SHDWC + * @config_pmc_ws: wakeup srouces configuration function for PMC + * @ws_ids: wakup sources of_device_id array + * @data: PM data to be used on last phase of suspend + * @bu: backup unit mapped data (for backup mode) + * @memcs: memory chip select + */ struct at91_soc_pm { int (*config_shdwc_ws)(void __iomem *shdwc, u32 *mode, u32 *polarity); int (*config_pmc_ws)(void __iomem *pmc, u32 mode, u32 polarity); const struct of_device_id *ws_ids; + struct at91_pm_bu *bu; struct at91_pm_data data; + void *memcs; }; +/** + * enum at91_pm_iomaps: IOs that needs to be mapped for different PM modes + * @AT91_PM_IOMAP_SHDWC: SHDWC controller + * @AT91_PM_IOMAP_SFRBU: SFRBU controller + */ +enum at91_pm_iomaps { + AT91_PM_IOMAP_SHDWC, + AT91_PM_IOMAP_SFRBU, +}; + +#define AT91_PM_IOMAP(name) BIT(AT91_PM_IOMAP_##name) + static struct at91_soc_pm soc_pm = { .data = { .standby_mode = AT91_PM_STANDBY, @@ -71,13 +114,6 @@ static int at91_pm_valid_state(suspend_state_t state) 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; @@ -116,6 +152,17 @@ static const struct of_device_id sam9x60_ws_ids[] = { { /* sentinel */ } }; +static const struct of_device_id sama7g5_ws_ids[] = { + { .compatible = "atmel,at91sam9x5-rtc", .data = &ws_info[1] }, + { .compatible = "microchip,sama7g5-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 = "microchip,sama7g5-sdhci", .data = &ws_info[3] }, + { .compatible = "atmel,at91sam9260-rtt", .data = &ws_info[4] }, + { /* sentinel */ } +}; + static int at91_pm_config_ws(unsigned int pm_mode, bool set) { const struct wakeup_source_info *wsi; @@ -206,6 +253,8 @@ static int at91_sam9x60_config_pmc_ws(void __iomem *pmc, u32 mode, u32 polarity) */ static int at91_pm_begin(suspend_state_t state) { + int ret; + switch (state) { case PM_SUSPEND_MEM: soc_pm.data.mode = soc_pm.data.suspend_mode; @@ -219,7 +268,16 @@ static int at91_pm_begin(suspend_state_t state) soc_pm.data.mode = -1; } - return at91_pm_config_ws(soc_pm.data.mode, true); + ret = at91_pm_config_ws(soc_pm.data.mode, true); + if (ret) + return ret; + + if (soc_pm.data.mode == AT91_PM_BACKUP) + soc_pm.bu->suspended = 1; + else if (soc_pm.bu) + soc_pm.bu->suspended = 0; + + return 0; } /* @@ -277,6 +335,19 @@ extern u32 at91_pm_suspend_in_sram_sz; static int at91_suspend_finish(unsigned long val) { + int i; + + if (soc_pm.data.mode == AT91_PM_BACKUP && soc_pm.data.ramc_phy) { + /* + * The 1st 8 words of memory might get corrupted in the process + * of DDR PHY recalibration; it is saved here in securam and it + * will be restored later, after recalibration, by bootloader + */ + for (i = 1; i < BACKUP_DDR_PHY_CALIBRATION; i++) + soc_pm.bu->ddr_phy_calibration[i] = + *((unsigned int *)soc_pm.memcs + (i - 1)); + } + flush_cache_all(); outer_disable(); @@ -288,8 +359,6 @@ static int at91_suspend_finish(unsigned long val) static void at91_pm_suspend(suspend_state_t state) { if (soc_pm.data.mode == AT91_PM_BACKUP) { - pm_bu->suspended = 1; - cpu_suspend(0, at91_suspend_finish); /* The SRAM is lost between suspend cycles */ @@ -511,10 +580,16 @@ static const struct of_device_id ramc_ids[] __initconst = { { .compatible = "atmel,at91sam9260-sdramc", .data = &ramc_infos[1] }, { .compatible = "atmel,at91sam9g45-ddramc", .data = &ramc_infos[2] }, { .compatible = "atmel,sama5d3-ddramc", .data = &ramc_infos[3] }, + { .compatible = "microchip,sama7g5-uddrc", }, { /*sentinel*/ } }; -static __init void at91_dt_ramc(void) +static const struct of_device_id ramc_phy_ids[] __initconst = { + { .compatible = "microchip,sama7g5-ddr3phy", }, + { /* Sentinel. */ }, +}; + +static __init void at91_dt_ramc(bool phy_mandatory) { struct device_node *np; const struct of_device_id *of_id; @@ -528,9 +603,11 @@ static __init void at91_dt_ramc(void) panic(pr_fmt("unable to map ramc[%d] cpu registers\n"), idx); ramc = of_id->data; - if (!standby) - standby = ramc->idle; - soc_pm.data.memctrl = ramc->memctrl; + if (ramc) { + if (!standby) + standby = ramc->idle; + soc_pm.data.memctrl = ramc->memctrl; + } idx++; } @@ -538,6 +615,16 @@ static __init void at91_dt_ramc(void) if (!idx) panic(pr_fmt("unable to find compatible ram controller node in dtb\n")); + /* Lookup for DDR PHY node, if any. */ + for_each_matching_node_and_match(np, ramc_phy_ids, &of_id) { + soc_pm.data.ramc_phy = of_iomap(np, 0); + if (!soc_pm.data.ramc_phy) + panic(pr_fmt("unable to map ramc phy cpu registers\n")); + } + + if (phy_mandatory && !soc_pm.data.ramc_phy) + panic(pr_fmt("DDR PHY is mandatory!\n")); + if (!standby) { pr_warn("ramc no standby function available\n"); return; @@ -618,37 +705,57 @@ static bool __init at91_is_pm_mode_active(int pm_mode) soc_pm.data.suspend_mode == pm_mode); } +static int __init at91_pm_backup_scan_memcs(unsigned long node, + const char *uname, int depth, + void *data) +{ + const char *type; + const __be32 *reg; + int *located = data; + int size; + + /* Memory node already located. */ + if (*located) + return 0; + + type = of_get_flat_dt_prop(node, "device_type", NULL); + + /* We are scanning "memory" nodes only. */ + if (!type || strcmp(type, "memory")) + return 0; + + reg = of_get_flat_dt_prop(node, "reg", &size); + if (reg) { + soc_pm.memcs = __va((phys_addr_t)be32_to_cpu(*reg)); + *located = 1; + } + + return 0; +} + 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; + struct platform_device *pdev; + int ret = -ENODEV, located = 0; - if (!IS_ENABLED(CONFIG_SOC_SAMA5D2)) + if (!IS_ENABLED(CONFIG_SOC_SAMA5D2) && + !IS_ENABLED(CONFIG_SOC_SAMA7G5)) return -EPERM; if (!at91_is_pm_mode_active(AT91_PM_BACKUP)) return 0; - np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-sfrbu"); - if (!np) { - pr_warn("%s: failed to find sfrbu!\n", __func__); - return ret; - } - - soc_pm.data.sfrbu = of_iomap(np, 0); - of_node_put(np); - np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-securam"); if (!np) - goto securam_fail_no_ref_dev; + return ret; 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_no_ref_dev; + return ret; } sram_pool = gen_pool_get(&pdev->dev, NULL); @@ -657,79 +764,117 @@ static int __init at91_pm_backup_init(void) goto securam_fail; } - pm_bu = (void *)gen_pool_alloc(sram_pool, sizeof(struct at91_pm_bu)); - if (!pm_bu) { + soc_pm.bu = (void *)gen_pool_alloc(sram_pool, sizeof(struct at91_pm_bu)); + if (!soc_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); + soc_pm.bu->suspended = 0; + soc_pm.bu->canary = __pa_symbol(&canary); + soc_pm.bu->resume = __pa_symbol(cpu_resume); + if (soc_pm.data.ramc_phy) { + of_scan_flat_dt(at91_pm_backup_scan_memcs, &located); + if (!located) + goto securam_fail; + + /* DDR3PHY_ZQ0SR0 */ + soc_pm.bu->ddr_phy_calibration[0] = readl(soc_pm.data.ramc_phy + + 0x188); + } return 0; securam_fail: put_device(&pdev->dev); -securam_fail_no_ref_dev: - iounmap(soc_pm.data.sfrbu); - soc_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 (soc_pm.data.standby_mode == pm_mode) - soc_pm.data.standby_mode = AT91_PM_ULP0; - if (soc_pm.data.suspend_mode == pm_mode) - soc_pm.data.suspend_mode = AT91_PM_ULP0; -} - static const struct of_device_id atmel_shdwc_ids[] = { { .compatible = "atmel,sama5d2-shdwc" }, { .compatible = "microchip,sam9x60-shdwc" }, + { .compatible = "microchip,sama7g5-shdwc" }, { /* sentinel. */ } }; -static void __init at91_pm_modes_init(void) +static void __init at91_pm_modes_init(const u32 *maps, int len) { struct device_node *np; - int ret; + int ret, mode; - if (!at91_is_pm_mode_active(AT91_PM_BACKUP) && - !at91_is_pm_mode_active(AT91_PM_ULP1)) - return; + ret = at91_pm_backup_init(); + if (ret) { + if (soc_pm.data.standby_mode == AT91_PM_BACKUP) + soc_pm.data.standby_mode = AT91_PM_ULP0; + if (soc_pm.data.suspend_mode == AT91_PM_BACKUP) + soc_pm.data.suspend_mode = AT91_PM_ULP0; + } - np = of_find_matching_node(NULL, atmel_shdwc_ids); - if (!np) { - pr_warn("%s: failed to find shdwc!\n", __func__); - goto ulp1_default; + if (maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(SHDWC) || + maps[soc_pm.data.suspend_mode] & AT91_PM_IOMAP(SHDWC)) { + np = of_find_matching_node(NULL, atmel_shdwc_ids); + if (!np) { + pr_warn("%s: failed to find shdwc!\n", __func__); + + /* Use ULP0 if it doesn't needs SHDWC.*/ + if (!(maps[AT91_PM_ULP0] & AT91_PM_IOMAP(SHDWC))) + mode = AT91_PM_ULP0; + else + mode = AT91_PM_STANDBY; + + if (maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(SHDWC)) + soc_pm.data.standby_mode = mode; + if (maps[soc_pm.data.suspend_mode] & AT91_PM_IOMAP(SHDWC)) + soc_pm.data.suspend_mode = mode; + } else { + soc_pm.data.shdwc = of_iomap(np, 0); + of_node_put(np); + } } - soc_pm.data.shdwc = of_iomap(np, 0); - of_node_put(np); + if (maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(SFRBU) || + maps[soc_pm.data.suspend_mode] & AT91_PM_IOMAP(SFRBU)) { + np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-sfrbu"); + if (!np) { + pr_warn("%s: failed to find sfrbu!\n", __func__); + + /* + * Use ULP0 if it doesn't need SHDWC or if SHDWC + * was already located. + */ + if (!(maps[AT91_PM_ULP0] & AT91_PM_IOMAP(SHDWC)) || + soc_pm.data.shdwc) + mode = AT91_PM_ULP0; + else + mode = AT91_PM_STANDBY; + + if (maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(SFRBU)) + soc_pm.data.standby_mode = mode; + if (maps[soc_pm.data.suspend_mode] & AT91_PM_IOMAP(SFRBU)) + soc_pm.data.suspend_mode = mode; + } else { + soc_pm.data.sfrbu = 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; + /* Unmap all unnecessary. */ + if (soc_pm.data.shdwc && + !(maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(SHDWC) || + maps[soc_pm.data.suspend_mode] & AT91_PM_IOMAP(SHDWC))) { + iounmap(soc_pm.data.shdwc); + soc_pm.data.shdwc = NULL; } - return; + if (soc_pm.data.sfrbu && + !(maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(SFRBU) || + maps[soc_pm.data.suspend_mode] & AT91_PM_IOMAP(SFRBU))) { + iounmap(soc_pm.data.sfrbu); + soc_pm.data.sfrbu = NULL; + } -unmap: - iounmap(soc_pm.data.shdwc); - soc_pm.data.shdwc = NULL; -ulp1_default: - at91_pm_use_default_mode(AT91_PM_ULP1); -backup_default: - at91_pm_use_default_mode(AT91_PM_BACKUP); + return; } struct pmc_info { @@ -764,6 +909,11 @@ static const struct pmc_info pmc_infos[] __initconst = { .mckr = 0x28, .version = AT91_PMC_V2, }, + { + .mckr = 0x28, + .version = AT91_PMC_V2, + }, + }; static const struct of_device_id atmel_pmc_ids[] __initconst = { @@ -779,6 +929,7 @@ static const struct of_device_id atmel_pmc_ids[] __initconst = { { .compatible = "atmel,sama5d4-pmc", .data = &pmc_infos[1] }, { .compatible = "atmel,sama5d2-pmc", .data = &pmc_infos[1] }, { .compatible = "microchip,sam9x60-pmc", .data = &pmc_infos[4] }, + { .compatible = "microchip,sama7g5-pmc", .data = &pmc_infos[5] }, { /* sentinel */ }, }; @@ -877,7 +1028,7 @@ void __init at91rm9200_pm_init(void) soc_pm.data.standby_mode = AT91_PM_STANDBY; soc_pm.data.suspend_mode = AT91_PM_ULP0; - at91_dt_ramc(); + at91_dt_ramc(false); /* * AT91RM9200 SDRAM low-power mode cannot be used with self-refresh. @@ -892,13 +1043,16 @@ void __init sam9x60_pm_init(void) static const int modes[] __initconst = { AT91_PM_STANDBY, AT91_PM_ULP0, AT91_PM_ULP0_FAST, AT91_PM_ULP1, }; + static const int iomaps[] __initconst = { + [AT91_PM_ULP1] = AT91_PM_IOMAP(SHDWC), + }; if (!IS_ENABLED(CONFIG_SOC_SAM9X60)) return; at91_pm_modes_validate(modes, ARRAY_SIZE(modes)); - at91_pm_modes_init(); - at91_dt_ramc(); + at91_pm_modes_init(iomaps, ARRAY_SIZE(iomaps)); + at91_dt_ramc(false); at91_pm_init(NULL); soc_pm.ws_ids = sam9x60_ws_ids; @@ -918,7 +1072,7 @@ void __init at91sam9_pm_init(void) soc_pm.data.standby_mode = AT91_PM_STANDBY; soc_pm.data.suspend_mode = AT91_PM_ULP0; - at91_dt_ramc(); + at91_dt_ramc(false); at91_pm_init(at91sam9_idle); } @@ -932,7 +1086,7 @@ void __init sama5_pm_init(void) return; at91_pm_modes_validate(modes, ARRAY_SIZE(modes)); - at91_dt_ramc(); + at91_dt_ramc(false); at91_pm_init(NULL); } @@ -942,13 +1096,18 @@ void __init sama5d2_pm_init(void) AT91_PM_STANDBY, AT91_PM_ULP0, AT91_PM_ULP0_FAST, AT91_PM_ULP1, AT91_PM_BACKUP, }; + static const u32 iomaps[] __initconst = { + [AT91_PM_ULP1] = AT91_PM_IOMAP(SHDWC), + [AT91_PM_BACKUP] = AT91_PM_IOMAP(SHDWC) | + AT91_PM_IOMAP(SFRBU), + }; if (!IS_ENABLED(CONFIG_SOC_SAMA5D2)) return; at91_pm_modes_validate(modes, ARRAY_SIZE(modes)); - at91_pm_modes_init(); - at91_dt_ramc(); + at91_pm_modes_init(iomaps, ARRAY_SIZE(iomaps)); + at91_dt_ramc(false); at91_pm_init(NULL); soc_pm.ws_ids = sama5d2_ws_ids; @@ -956,6 +1115,32 @@ void __init sama5d2_pm_init(void) soc_pm.config_pmc_ws = at91_sama5d2_config_pmc_ws; } +void __init sama7_pm_init(void) +{ + static const int modes[] __initconst = { + AT91_PM_STANDBY, AT91_PM_ULP0, AT91_PM_ULP1, AT91_PM_BACKUP, + }; + static const u32 iomaps[] __initconst = { + [AT91_PM_ULP0] = AT91_PM_IOMAP(SFRBU), + [AT91_PM_ULP1] = AT91_PM_IOMAP(SFRBU) | + AT91_PM_IOMAP(SHDWC), + [AT91_PM_BACKUP] = AT91_PM_IOMAP(SFRBU) | + AT91_PM_IOMAP(SHDWC), + }; + + if (!IS_ENABLED(CONFIG_SOC_SAMA7)) + return; + + at91_pm_modes_validate(modes, ARRAY_SIZE(modes)); + + at91_dt_ramc(true); + at91_pm_modes_init(iomaps, ARRAY_SIZE(iomaps)); + at91_pm_init(NULL); + + soc_pm.ws_ids = sama7g5_ws_ids; + soc_pm.config_pmc_ws = at91_sam9x60_config_pmc_ws; +} + static int __init at91_pm_modes_select(char *str) { char *s; diff --git a/arch/arm/mach-at91/pm.h b/arch/arm/mach-at91/pm.h index bfb260be371e..53bdc9000e44 100644 --- a/arch/arm/mach-at91/pm.h +++ b/arch/arm/mach-at91/pm.h @@ -12,6 +12,8 @@ #include <linux/mfd/syscon/atmel-mc.h> #include <soc/at91/at91sam9_ddrsdr.h> #include <soc/at91/at91sam9_sdramc.h> +#include <soc/at91/sama7-ddr.h> +#include <soc/at91/sama7-sfrbu.h> #define AT91_MEMCTRL_MC 0 #define AT91_MEMCTRL_SDRAMC 1 @@ -27,6 +29,7 @@ struct at91_pm_data { void __iomem *pmc; void __iomem *ramc[2]; + void __iomem *ramc_phy; unsigned long uhp_udp_mask; unsigned int memctrl; unsigned int mode; diff --git a/arch/arm/mach-at91/pm_data-offsets.c b/arch/arm/mach-at91/pm_data-offsets.c index 82089ff258c0..40bd4e8fe40a 100644 --- a/arch/arm/mach-at91/pm_data-offsets.c +++ b/arch/arm/mach-at91/pm_data-offsets.c @@ -8,6 +8,8 @@ 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_RAMC_PHY, offsetof(struct at91_pm_data, + ramc_phy)); 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)); diff --git a/arch/arm/mach-at91/pm_suspend.S b/arch/arm/mach-at91/pm_suspend.S index b683c2caa40b..cbd61a3bcab1 100644 --- a/arch/arm/mach-at91/pm_suspend.S +++ b/arch/arm/mach-at91/pm_suspend.S @@ -22,39 +22,57 @@ tmp3 .req r6 /* * Wait until master clock is ready (after switching master clock source) + * + * @r_mckid: register holding master clock identifier + * + * Side effects: overwrites r7, r8 */ - .macro wait_mckrdy -1: ldr tmp1, [pmc, #AT91_PMC_SR] - tst tmp1, #AT91_PMC_MCKRDY - beq 1b + .macro wait_mckrdy r_mckid +#ifdef CONFIG_SOC_SAMA7 + cmp \r_mckid, #0 + beq 1f + mov r7, #AT91_PMC_MCKXRDY + b 2f +#endif +1: mov r7, #AT91_PMC_MCKRDY +2: ldr r8, [pmc, #AT91_PMC_SR] + and r8, r7 + cmp r8, r7 + bne 2b .endm /* * Wait until master oscillator has stabilized. + * + * Side effects: overwrites r7 */ .macro wait_moscrdy -1: ldr tmp1, [pmc, #AT91_PMC_SR] - tst tmp1, #AT91_PMC_MOSCS +1: ldr r7, [pmc, #AT91_PMC_SR] + tst r7, #AT91_PMC_MOSCS beq 1b .endm /* * Wait for main oscillator selection is done + * + * Side effects: overwrites r7 */ .macro wait_moscsels -1: ldr tmp1, [pmc, #AT91_PMC_SR] - tst tmp1, #AT91_PMC_MOSCSELS +1: ldr r7, [pmc, #AT91_PMC_SR] + tst r7, #AT91_PMC_MOSCSELS beq 1b .endm /* * Put the processor to enter the idle state + * + * Side effects: overwrites r7 */ .macro at91_cpu_idle #if defined(CONFIG_CPU_V7) - mov tmp1, #AT91_PMC_PCK - str tmp1, [pmc, #AT91_PMC_SCDR] + mov r7, #AT91_PMC_PCK + str r7, [pmc, #AT91_PMC_SCDR] dsb @@ -65,102 +83,375 @@ tmp3 .req r6 .endm +/** + * Set state for 2.5V low power regulator + * @ena: 0 - disable regulator + * 1 - enable regulator + * + * Side effects: overwrites r7, r8, r9, r10 + */ + .macro at91_2_5V_reg_set_low_power ena +#ifdef CONFIG_SOC_SAMA7 + ldr r7, .sfrbu + mov r8, #\ena + ldr r9, [r7, #AT91_SFRBU_25LDOCR] + orr r9, r9, #AT91_SFRBU_25LDOCR_LP + cmp r8, #1 + beq lp_done_\ena + bic r9, r9, #AT91_SFRBU_25LDOCR_LP +lp_done_\ena: + ldr r10, =AT91_SFRBU_25LDOCR_LDOANAKEY + orr r9, r9, r10 + str r9, [r7, #AT91_SFRBU_25LDOCR] +#endif + .endm + + .macro at91_backup_set_lpm reg +#ifdef CONFIG_SOC_SAMA7 + orr \reg, \reg, #0x200000 +#endif + .endm + .text .arm -/* - * void at91_suspend_sram_fn(struct at91_pm_data*) - * @input param: - * @r0: base address of struct at91_pm_data +#ifdef CONFIG_SOC_SAMA7 +/** + * Enable self-refresh + * + * Side effects: overwrites r2, r3, tmp1, tmp2, tmp3, r7 */ -/* 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} +.macro at91_sramc_self_refresh_ena + ldr r2, .sramc_base + ldr r3, .sramc_phy_base + ldr r7, .pm_mode - /* Drain write buffer */ + dsb + + /* Disable all AXI ports. */ + ldr tmp1, [r2, #UDDRC_PCTRL_0] + bic tmp1, tmp1, #0x1 + str tmp1, [r2, #UDDRC_PCTRL_0] + + ldr tmp1, [r2, #UDDRC_PCTRL_1] + bic tmp1, tmp1, #0x1 + str tmp1, [r2, #UDDRC_PCTRL_1] + + ldr tmp1, [r2, #UDDRC_PCTRL_2] + bic tmp1, tmp1, #0x1 + str tmp1, [r2, #UDDRC_PCTRL_2] + + ldr tmp1, [r2, #UDDRC_PCTRL_3] + bic tmp1, tmp1, #0x1 + str tmp1, [r2, #UDDRC_PCTRL_3] + + ldr tmp1, [r2, #UDDRC_PCTRL_4] + bic tmp1, tmp1, #0x1 + str tmp1, [r2, #UDDRC_PCTRL_4] + +sr_ena_1: + /* Wait for all ports to disable. */ + ldr tmp1, [r2, #UDDRC_PSTAT] + ldr tmp2, =UDDRC_PSTAT_ALL_PORTS + tst tmp1, tmp2 + bne sr_ena_1 + + /* Switch to self-refresh. */ + ldr tmp1, [r2, #UDDRC_PWRCTL] + orr tmp1, tmp1, #UDDRC_PWRCTRL_SELFREF_SW + str tmp1, [r2, #UDDRC_PWRCTL] + +sr_ena_2: + /* Wait for self-refresh enter. */ + ldr tmp1, [r2, #UDDRC_STAT] + bic tmp1, tmp1, #~UDDRC_STAT_SELFREF_TYPE_MSK + cmp tmp1, #UDDRC_STAT_SELFREF_TYPE_SW + bne sr_ena_2 + + /* Put DDR PHY's DLL in bypass mode for non-backup modes. */ + cmp r7, #AT91_PM_BACKUP + beq sr_ena_3 + ldr tmp1, [r3, #DDR3PHY_PIR] + orr tmp1, tmp1, #DDR3PHY_PIR_DLLBYP + str tmp1, [r3, #DDR3PHY_PIR] + +sr_ena_3: + /* Power down DDR PHY data receivers. */ + ldr tmp1, [r3, #DDR3PHY_DXCCR] + orr tmp1, tmp1, #DDR3PHY_DXCCR_DXPDR + str tmp1, [r3, #DDR3PHY_DXCCR] + + /* Power down ADDR/CMD IO. */ + ldr tmp1, [r3, #DDR3PHY_ACIOCR] + orr tmp1, tmp1, #DDR3PHY_ACIORC_ACPDD + orr tmp1, tmp1, #DDR3PHY_ACIOCR_CKPDD_CK0 + orr tmp1, tmp1, #DDR3PHY_ACIOCR_CSPDD_CS0 + str tmp1, [r3, #DDR3PHY_ACIOCR] + + /* Power down ODT. */ + ldr tmp1, [r3, #DDR3PHY_DSGCR] + orr tmp1, tmp1, #DDR3PHY_DSGCR_ODTPDD_ODT0 + str tmp1, [r3, #DDR3PHY_DSGCR] +.endm + +/** + * Disable self-refresh + * + * Side effects: overwrites r2, r3, tmp1, tmp2, tmp3 + */ +.macro at91_sramc_self_refresh_dis + ldr r2, .sramc_base + ldr r3, .sramc_phy_base + + /* Power up DDR PHY data receivers. */ + ldr tmp1, [r3, #DDR3PHY_DXCCR] + bic tmp1, tmp1, #DDR3PHY_DXCCR_DXPDR + str tmp1, [r3, #DDR3PHY_DXCCR] + + /* Power up the output of CK and CS pins. */ + ldr tmp1, [r3, #DDR3PHY_ACIOCR] + bic tmp1, tmp1, #DDR3PHY_ACIORC_ACPDD + bic tmp1, tmp1, #DDR3PHY_ACIOCR_CKPDD_CK0 + bic tmp1, tmp1, #DDR3PHY_ACIOCR_CSPDD_CS0 + str tmp1, [r3, #DDR3PHY_ACIOCR] + + /* Power up ODT. */ + ldr tmp1, [r3, #DDR3PHY_DSGCR] + bic tmp1, tmp1, #DDR3PHY_DSGCR_ODTPDD_ODT0 + str tmp1, [r3, #DDR3PHY_DSGCR] + + /* Take DDR PHY's DLL out of bypass mode. */ + ldr tmp1, [r3, #DDR3PHY_PIR] + bic tmp1, tmp1, #DDR3PHY_PIR_DLLBYP + str tmp1, [r3, #DDR3PHY_PIR] + + /* Enable quasi-dynamic programming. */ mov tmp1, #0 - mcr p15, 0, tmp1, c7, c10, 4 + str tmp1, [r2, #UDDRC_SWCTRL] + + /* De-assert SDRAM initialization. */ + ldr tmp1, [r2, #UDDRC_DFIMISC] + bic tmp1, tmp1, #UDDRC_DFIMISC_DFI_INIT_COMPLETE_EN + str tmp1, [r2, #UDDRC_DFIMISC] + + /* Quasi-dynamic programming done. */ + mov tmp1, #UDDRC_SWCTRL_SW_DONE + str tmp1, [r2, #UDDRC_SWCTRL] + +sr_dis_1: + ldr tmp1, [r2, #UDDRC_SWSTAT] + tst tmp1, #UDDRC_SWSTAT_SW_DONE_ACK + beq sr_dis_1 + + /* DLL soft-reset + DLL lock wait + ITM reset */ + mov tmp1, #(DDR3PHY_PIR_INIT | DDR3PHY_PIR_DLLSRST | \ + DDR3PHY_PIR_DLLLOCK | DDR3PHY_PIR_ITMSRST) + str tmp1, [r3, #DDR3PHY_PIR] + +sr_dis_4: + /* Wait for it. */ + ldr tmp1, [r3, #DDR3PHY_PGSR] + tst tmp1, #DDR3PHY_PGSR_IDONE + beq sr_dis_4 + + /* Enable quasi-dynamic programming. */ + mov tmp1, #0 + str tmp1, [r2, #UDDRC_SWCTRL] + + /* Assert PHY init complete enable signal. */ + ldr tmp1, [r2, #UDDRC_DFIMISC] + orr tmp1, tmp1, #UDDRC_DFIMISC_DFI_INIT_COMPLETE_EN + str tmp1, [r2, #UDDRC_DFIMISC] + + /* Programming is done. Set sw_done. */ + mov tmp1, #UDDRC_SWCTRL_SW_DONE + str tmp1, [r2, #UDDRC_SWCTRL] + +sr_dis_5: + /* Wait for it. */ + ldr tmp1, [r2, #UDDRC_SWSTAT] + tst tmp1, #UDDRC_SWSTAT_SW_DONE_ACK + beq sr_dis_5 + + /* Trigger self-refresh exit. */ + ldr tmp1, [r2, #UDDRC_PWRCTL] + bic tmp1, tmp1, #UDDRC_PWRCTRL_SELFREF_SW + str tmp1, [r2, #UDDRC_PWRCTL] + +sr_dis_6: + /* Wait for self-refresh exit done. */ + ldr tmp1, [r2, #UDDRC_STAT] + bic tmp1, tmp1, #~UDDRC_STAT_OPMODE_MSK + cmp tmp1, #UDDRC_STAT_OPMODE_NORMAL + bne sr_dis_6 + + /* Enable all AXI ports. */ + ldr tmp1, [r2, #UDDRC_PCTRL_0] + orr tmp1, tmp1, #0x1 + str tmp1, [r2, #UDDRC_PCTRL_0] + + ldr tmp1, [r2, #UDDRC_PCTRL_1] + orr tmp1, tmp1, #0x1 + str tmp1, [r2, #UDDRC_PCTRL_1] + + ldr tmp1, [r2, #UDDRC_PCTRL_2] + orr tmp1, tmp1, #0x1 + str tmp1, [r2, #UDDRC_PCTRL_2] + + ldr tmp1, [r2, #UDDRC_PCTRL_3] + orr tmp1, tmp1, #0x1 + str tmp1, [r2, #UDDRC_PCTRL_3] + + ldr tmp1, [r2, #UDDRC_PCTRL_4] + orr tmp1, tmp1, #0x1 + str tmp1, [r2, #UDDRC_PCTRL_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 - ldr tmp1, [r0, #PM_DATA_PMC_MCKR_OFFSET] - str tmp1, .mckr_offset - ldr tmp1, [r0, #PM_DATA_PMC_VERSION] - str tmp1, .pmc_version - /* 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, .sfrbu - cmp tmp1, #0 - ldrne tmp2, [tmp1, #0x10] + dsb +.endm +#else +/** + * Enable self-refresh + * + * register usage: + * @r1: memory type + * @r2: base address of the sram controller + * @r3: temporary + */ +.macro at91_sramc_self_refresh_ena + ldr r1, .memtype + ldr r2, .sramc_base - /* Active the self-refresh mode */ - mov r0, #SRAMC_SELF_FRESH_ACTIVE - bl at91_sramc_self_refresh + cmp r1, #AT91_MEMCTRL_MC + bne sr_ena_ddrc_sf - ldr r0, .pm_mode - cmp r0, #AT91_PM_STANDBY - beq standby - cmp r0, #AT91_PM_BACKUP - beq backup_mode + /* Active SDRAM self-refresh mode */ + mov r3, #1 + str r3, [r2, #AT91_MC_SDRAMC_SRR] + b sr_ena_exit - bl at91_ulp_mode - b exit_suspend +sr_ena_ddrc_sf: + cmp r1, #AT91_MEMCTRL_DDRSDR + bne sr_ena_sdramc_sf -standby: - /* Wait for interrupt */ - ldr pmc, .pmc_base - at91_cpu_idle - b exit_suspend + /* + * DDR Memory controller + */ -backup_mode: - bl at91_backup_mode - b exit_suspend + /* 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] -exit_suspend: - /* Exit the self-refresh mode */ - mov r0, #SRAMC_SELF_FRESH_EXIT - bl at91_sramc_self_refresh + /* 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] - /* Restore registers, and return */ - ldmfd sp!, {r4 - r12, pc} -ENDPROC(at91_pm_suspend_in_sram) + /* If using the 2nd ddr controller */ + ldr r2, .sramc1_base + cmp r2, #0 + beq sr_ena_no_2nd_ddrc -ENTRY(at91_backup_mode) - /* Switch the master clock source to slow clock. */ - ldr pmc, .pmc_base - ldr tmp2, .mckr_offset - ldr tmp1, [pmc, tmp2] - bic tmp1, tmp1, #AT91_PMC_CSS - str tmp1, [pmc, tmp2] + 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] - wait_mckrdy + /* 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] - /*BUMEN*/ - ldr r0, .sfrbu - mov tmp1, #0x1 - str tmp1, [r0, #0x10] +sr_ena_no_2nd_ddrc: + b sr_ena_exit - /* Shutdown */ - ldr r0, .shdwc - mov tmp1, #0xA5000000 - add tmp1, tmp1, #0x1 - str tmp1, [r0, #0] -ENDPROC(at91_backup_mode) + /* + * SDRAMC Memory controller + */ +sr_ena_sdramc_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] + + ldr r3, .saved_sam9_lpr + str r3, [r2, #AT91_SDRAMC_LPR] + +sr_ena_exit: +.endm + +/** + * Disable self-refresh + * + * register usage: + * @r1: memory type + * @r2: base address of the sram controller + * @r3: temporary + */ +.macro at91_sramc_self_refresh_dis + ldr r1, .memtype + ldr r2, .sramc_base + + cmp r1, #AT91_MEMCTRL_MC + bne sr_dis_ddrc_exit_sf + + /* + * at91rm9200 Memory controller + */ + + /* + * For exiting the self-refresh mode, do nothing, + * automatically exit the self-refresh mode. + */ + b sr_dis_exit + +sr_dis_ddrc_exit_sf: + cmp r1, #AT91_MEMCTRL_DDRSDR + bne sdramc_exit_sf + + /* DDR Memory controller */ + + /* 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 sr_dis_exit + +sdramc_exit_sf: + /* SDRAMC Memory controller */ + ldr r3, .saved_sam9_lpr + str r3, [r2, #AT91_SDRAMC_LPR] + +sr_dis_exit: +.endm +#endif .macro at91_pm_ulp0_mode ldr pmc, .pmc_base @@ -176,7 +467,9 @@ ENDPROC(at91_backup_mode) bic tmp1, tmp1, #AT91_PMC_PRES orr tmp1, tmp1, #AT91_PMC_PRES_64 str tmp1, [pmc, tmp3] - wait_mckrdy + + mov tmp3, #0 + wait_mckrdy tmp3 b 1f 0: @@ -212,10 +505,13 @@ ENDPROC(at91_backup_mode) bne 5f /* Set lowest prescaler for fast resume. */ + ldr tmp3, .mckr_offset ldr tmp1, [pmc, tmp3] bic tmp1, tmp1, #AT91_PMC_PRES str tmp1, [pmc, tmp3] - wait_mckrdy + + mov tmp3, #0 + wait_mckrdy tmp3 b 6f 5: /* Restore RC oscillator state */ @@ -252,6 +548,7 @@ ENDPROC(at91_backup_mode) .macro at91_pm_ulp1_mode ldr pmc, .pmc_base ldr tmp2, .mckr_offset + mov tmp3, #0 /* Save RC oscillator state and check if it is enabled. */ ldr tmp1, [pmc, #AT91_PMC_SR] @@ -293,7 +590,7 @@ ENDPROC(at91_backup_mode) orr tmp1, tmp1, #AT91_PMC_CSS_MAIN str tmp1, [pmc, tmp2] - wait_mckrdy + wait_mckrdy tmp3 /* Enter the ULP1 mode by set WAITMODE bit in CKGR_MOR */ ldr tmp1, [pmc, #AT91_CKGR_MOR] @@ -306,7 +603,7 @@ ENDPROC(at91_backup_mode) nop nop - wait_mckrdy + wait_mckrdy tmp3 /* Enable the crystal oscillator */ ldr tmp1, [pmc, #AT91_CKGR_MOR] @@ -322,7 +619,7 @@ ENDPROC(at91_backup_mode) bic tmp1, tmp1, #AT91_PMC_CSS str tmp1, [pmc, tmp2] - wait_mckrdy + wait_mckrdy tmp3 /* Switch main clock source to crystal oscillator */ ldr tmp1, [pmc, #AT91_CKGR_MOR] @@ -339,7 +636,7 @@ ENDPROC(at91_backup_mode) orr tmp1, tmp1, #AT91_PMC_CSS_MAIN str tmp1, [pmc, tmp2] - wait_mckrdy + wait_mckrdy tmp3 /* Restore RC oscillator state */ ldr tmp1, .saved_osc_status @@ -367,7 +664,7 @@ ENDPROC(at91_backup_mode) cmp tmp1, #AT91_PMC_V1 beq 1f -#ifdef CONFIG_SOC_SAM9X60 +#ifdef CONFIG_HAVE_AT91_SAM9X60_PLL /* Save PLLA settings. */ ldr tmp2, [pmc, #AT91_PMC_PLL_UPDT] bic tmp2, tmp2, #AT91_PMC_PLL_UPDT_ID @@ -434,7 +731,7 @@ ENDPROC(at91_backup_mode) cmp tmp3, #AT91_PMC_V1 beq 4f -#ifdef CONFIG_SOC_SAM9X60 +#ifdef CONFIG_HAVE_AT91_SAM9X60_PLL /* step 1. */ ldr tmp1, [pmc, #AT91_PMC_PLL_UPDT] bic tmp1, tmp1, #AT91_PMC_PLL_UPDT_ID @@ -497,7 +794,122 @@ ENDPROC(at91_backup_mode) 2: .endm -ENTRY(at91_ulp_mode) +/** + * at91_mckx_ps_enable: save MCK1..4 settings and switch it to main clock + * + * Side effects: overwrites tmp1, tmp2 + */ +.macro at91_mckx_ps_enable +#ifdef CONFIG_SOC_SAMA7 + ldr pmc, .pmc_base + + /* There are 4 MCKs we need to handle: MCK1..4 */ + mov tmp1, #1 +e_loop: cmp tmp1, #5 + beq e_done + + /* Write MCK ID to retrieve the settings. */ + str tmp1, [pmc, #AT91_PMC_MCR_V2] + ldr tmp2, [pmc, #AT91_PMC_MCR_V2] + +e_save_mck1: + cmp tmp1, #1 + bne e_save_mck2 + str tmp2, .saved_mck1 + b e_ps + +e_save_mck2: + cmp tmp1, #2 + bne e_save_mck3 + str tmp2, .saved_mck2 + b e_ps + +e_save_mck3: + cmp tmp1, #3 + bne e_save_mck4 + str tmp2, .saved_mck3 + b e_ps + +e_save_mck4: + str tmp2, .saved_mck4 + +e_ps: + /* Use CSS=MAINCK and DIV=1. */ + bic tmp2, tmp2, #AT91_PMC_MCR_V2_CSS + bic tmp2, tmp2, #AT91_PMC_MCR_V2_DIV + orr tmp2, tmp2, #AT91_PMC_MCR_V2_CSS_MAINCK + orr tmp2, tmp2, #AT91_PMC_MCR_V2_DIV1 + str tmp2, [pmc, #AT91_PMC_MCR_V2] + + wait_mckrdy tmp1 + + add tmp1, tmp1, #1 + b e_loop + +e_done: +#endif +.endm + +/** + * at91_mckx_ps_restore: restore MCK1..4 settings + * + * Side effects: overwrites tmp1, tmp2 + */ +.macro at91_mckx_ps_restore +#ifdef CONFIG_SOC_SAMA7 + ldr pmc, .pmc_base + + /* There are 4 MCKs we need to handle: MCK1..4 */ + mov tmp1, #1 +r_loop: cmp tmp1, #5 + beq r_done + +r_save_mck1: + cmp tmp1, #1 + bne r_save_mck2 + ldr tmp2, .saved_mck1 + b r_ps + +r_save_mck2: + cmp tmp1, #2 + bne r_save_mck3 + ldr tmp2, .saved_mck2 + b r_ps + +r_save_mck3: + cmp tmp1, #3 + bne r_save_mck4 + ldr tmp2, .saved_mck3 + b r_ps + +r_save_mck4: + ldr tmp2, .saved_mck4 + +r_ps: + /* Write MCK ID to retrieve the settings. */ + str tmp1, [pmc, #AT91_PMC_MCR_V2] + ldr tmp3, [pmc, #AT91_PMC_MCR_V2] + + /* We need to restore CSS and DIV. */ + bic tmp3, tmp3, #AT91_PMC_MCR_V2_CSS + bic tmp3, tmp3, #AT91_PMC_MCR_V2_DIV + orr tmp3, tmp3, tmp2 + bic tmp3, tmp3, #AT91_PMC_MCR_V2_ID_MSK + orr tmp3, tmp3, tmp1 + orr tmp3, tmp3, #AT91_PMC_MCR_V2_CMD + str tmp2, [pmc, #AT91_PMC_MCR_V2] + + wait_mckrdy tmp1 + + add tmp1, tmp1, #1 + b r_loop +r_done: +#endif +.endm + +.macro at91_ulp_mode + at91_mckx_ps_enable + ldr pmc, .pmc_base ldr tmp2, .mckr_offset ldr tmp3, .pm_mode @@ -518,10 +930,15 @@ ENTRY(at91_ulp_mode) save_mck: str tmp1, [pmc, tmp2] - wait_mckrdy + mov tmp3, #0 + wait_mckrdy tmp3 at91_plla_disable + /* Enable low power mode for 2.5V regulator. */ + at91_2_5V_reg_set_low_power 1 + + ldr tmp3, .pm_mode cmp tmp3, #AT91_PM_ULP1 beq ulp1_mode @@ -533,6 +950,9 @@ ulp1_mode: b ulp_exit ulp_exit: + /* Disable low power mode for 2.5V regulator. */ + at91_2_5V_reg_set_low_power 0 + ldr pmc, .pmc_base at91_plla_enable @@ -544,135 +964,110 @@ ulp_exit: ldr tmp2, .saved_mckr str tmp2, [pmc, tmp1] - wait_mckrdy - - mov pc, lr -ENDPROC(at91_ulp_mode) + mov tmp3, #0 + wait_mckrdy tmp3 -/* - * 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 + at91_mckx_ps_restore +.endm -ddrc_sf: - cmp r1, #AT91_MEMCTRL_DDRSDR - bne sdramc_sf +.macro at91_backup_mode + /* Switch the master clock source to slow clock. */ + ldr pmc, .pmc_base + ldr tmp2, .mckr_offset + ldr tmp1, [pmc, tmp2] + bic tmp1, tmp1, #AT91_PMC_CSS + str tmp1, [pmc, tmp2] - /* - * DDR Memory controller - */ - tst r0, #SRAMC_SELF_FRESH_ACTIVE - beq ddrc_exit_sf + mov tmp3, #0 + wait_mckrdy tmp3 - /* 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] + /*BUMEN*/ + ldr r0, .sfrbu + mov tmp1, #0x1 + str tmp1, [r0, #0x10] - /* 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] + /* Wait for it. */ +1: ldr tmp1, [r0, #0x10] + tst tmp1, #0x1 + beq 1b - /* If using the 2nd ddr controller */ - ldr r2, .sramc1_base - cmp r2, #0 - beq no_2nd_ddrc + /* Shutdown */ + ldr r0, .shdwc + mov tmp1, #0xA5000000 + add tmp1, tmp1, #0x1 + at91_backup_set_lpm tmp1 + str tmp1, [r0, #0] +.endm - 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] +/* + * 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} - /* 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] + /* Drain write buffer */ + mov tmp1, #0 + mcr p15, 0, tmp1, c7, c10, 4 -no_2nd_ddrc: - b exit_sramc_sf + 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_RAMC_PHY] + str tmp1, .sramc_phy_base + ldr tmp1, [r0, #PM_DATA_MEMCTRL] + str tmp1, .memtype + ldr tmp1, [r0, #PM_DATA_MODE] + str tmp1, .pm_mode + ldr tmp1, [r0, #PM_DATA_PMC_MCKR_OFFSET] + str tmp1, .mckr_offset + ldr tmp1, [r0, #PM_DATA_PMC_VERSION] + str tmp1, .pmc_version + /* 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, .sfrbu + cmp tmp1, #0 + ldrne tmp2, [tmp1, #0x10] -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] + /* Active the self-refresh mode */ + at91_sramc_self_refresh_ena - /* 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] + ldr r0, .pm_mode + cmp r0, #AT91_PM_STANDBY + beq standby + cmp r0, #AT91_PM_BACKUP + beq backup_mode - b exit_sramc_sf + at91_ulp_mode + b exit_suspend - /* - * SDRAMC Memory controller - */ -sdramc_sf: - tst r0, #SRAMC_SELF_FRESH_ACTIVE - beq sdramc_exit_sf +standby: + /* Wait for interrupt */ + ldr pmc, .pmc_base + at91_cpu_idle + b exit_suspend - /* 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] +backup_mode: + at91_backup_mode -sdramc_exit_sf: - ldr r3, .saved_sam9_lpr - str r3, [r2, #AT91_SDRAMC_LPR] +exit_suspend: + /* Exit the self-refresh mode */ + at91_sramc_self_refresh_dis -exit_sramc_sf: - mov pc, lr -ENDPROC(at91_sramc_self_refresh) + /* Restore registers, and return */ + ldmfd sp!, {r4 - r12, pc} +ENDPROC(at91_pm_suspend_in_sram) .pmc_base: .word 0 @@ -680,6 +1075,8 @@ ENDPROC(at91_sramc_self_refresh) .word 0 .sramc1_base: .word 0 +.sramc_phy_base: + .word 0 .shdwc: .word 0 .sfrbu: @@ -706,6 +1103,16 @@ ENDPROC(at91_sramc_self_refresh) .word 0 .saved_osc_status: .word 0 +#ifdef CONFIG_SOC_SAMA7 +.saved_mck1: + .word 0 +.saved_mck2: + .word 0 +.saved_mck3: + .word 0 +.saved_mck4: + .word 0 +#endif ENTRY(at91_pm_suspend_in_sram_sz) .word .-at91_pm_suspend_in_sram diff --git a/arch/arm/mach-at91/sama7.c b/arch/arm/mach-at91/sama7.c new file mode 100644 index 000000000000..bd43733ede18 --- /dev/null +++ b/arch/arm/mach-at91/sama7.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Setup code for SAMA7 + * + * Copyright (C) 2021 Microchip Technology, Inc. and its subsidiaries + * + */ + +#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 sama7_dt_device_init(void) +{ + of_platform_default_populate(NULL, NULL, NULL); + sama7_pm_init(); +} + +static const char *const sama7_dt_board_compat[] __initconst = { + "microchip,sama7", + NULL +}; + +DT_MACHINE_START(sama7_dt, "Microchip SAMA7") + /* Maintainer: Microchip */ + .init_machine = sama7_dt_device_init, + .dt_compat = sama7_dt_board_compat, +MACHINE_END + |