AT91 soc for 5.15:
- add new SoC based on a Cortex-A7 core: the SAMA7G5 family - mach-at91 entry, Kconfig and header files - Power Management Controller (PMC) code and associated power management changes. Support for suspend/resume, Ultra Low Power modes and Backup with Memory in Self-Refresh mode. - Power management association with DDR controller and shutdown controller for addressing this variety of modes. -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQQ5TRCVIBiyi/S+BG4fOrpwrNPNDAUCYQpPnQAKCRAfOrpwrNPN DJyeAQDvS538E7jFPdsmUn3iZhMhV4zCZVsPk9fox9ptV8ewEAD/QuWFZg78YkZE vGe/AXlxjA4V/TMiGElf0/nbZ8xLiw4= =YucO -----END PGP SIGNATURE----- gpgsig -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEo6/YBQwIrVS28WGKmmx57+YAGNkFAmEKnQEACgkQmmx57+YA GNnU8g/+OZx39NCFnyUcUUmdFHEl/tn6Vkcirs9abdkpjSSylXLJnIVuimHZy0d5 oxDc3H83dIhCxLO5my27y8QH5e41dhCD7gVIxf+MF/RCYzGt+BjXiImt6YTOvrK9 hEFzO49XkiBSTtXpNER6hk5BWuIVAg4b1bFnzrhH9ajGxMCboubsxuRMlRP7t4ad Vg8Rgf4TOY1AgMm6P5zVjjMAij4C3uKcOO8WLZA9zVGFlKdftdDCvYBfojitctho O97Hg4tSYb4N+l8UPcD+dIie0L7Pp8QtgNjV2MiT/Nyuhewdf1DyH14cFSNxIrmA laVeRU5WPXJwxNdnEYukb1yI6TEKFQDHTksGWkHr0cQkxalHhuegNUczVwHgZaEZ j6pk1yskhRtqmwMyt/810lrEB/o01aPX2TldGda3h3XUxe4q/mw4QR7j9fIFDzjY 06S5LH/kL36kJ7QAV6ppQLFjBJ5+UJFHymHSXnsCNnaeD6mSZxncaLDoNt+O90kE IdALvvwq1ggbKDTQVgpIyU1XfYaNdXcbX1hACXP2LO5/MQejgQ2KJNtS1Qk1aLCC T161nfyi/YW5o7h70JGpNK9go5lj/60MgGtocQ5d+zxzyUKNCARaiYRjy0xP7/KR 1eftv0qgk+J+3+NYOGroLaixVbfLWlIjD9hyaMDnGKzYB857nSY= =+KcA -----END PGP SIGNATURE----- Merge tag 'at91-soc-5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/at91/linux into arm/soc AT91 soc for 5.15: - add new SoC based on a Cortex-A7 core: the SAMA7G5 family - mach-at91 entry, Kconfig and header files - Power Management Controller (PMC) code and associated power management changes. Support for suspend/resume, Ultra Low Power modes and Backup with Memory in Self-Refresh mode. - Power management association with DDR controller and shutdown controller for addressing this variety of modes. * tag 'at91-soc-5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/at91/linux: (26 commits) ARM: at91: pm: add sama7g5 shdwc ARM: at91: pm: add pm support for SAMA7G5 ARM: at91: sama7: introduce sama7 SoC family ARM: at91: pm: add sama7g5's pmc ARM: at91: pm: add backup mode support for SAMA7G5 ARM: at91: pm: save ddr phy calibration data to securam ARM: at91: pm: add sama7g5 ddr phy controller ARM: at91: pm: add sama7g5 ddr controller ARM: at91: pm: wait for ddr power mode off ARM: at91: pm: add support for 2.5V LDO regulator control ARM: at91: pm: add support for MCK1..4 save/restore for ulp modes ARM: at91: pm: add self-refresh support for sama7g5 ARM: at91: ddr: add registers definitions for sama7g5's ddr ARM: at91: sfrbu: add sfrbu registers definitions for sama7g5 ARM: at91: pm: add support for waiting MCK1..4 ARM: at91: pm: s/CONFIG_SOC_SAM9X60/CONFIG_HAVE_AT91_SAM9X60_PLL/g ARM: at91: pm: avoid push and pop on stack while memory is in self-refersh ARM: at91: pm: use r7 instead of tmp1 ARM: at91: pm: do not initialize pdev ARM: at91: pm: check for different controllers in at91_pm_modes_init() ... Link: https://lore.kernel.org/r/20210804084316.12641-1-nicolas.ferre@microchip.com Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
Коммит
318845985f
|
@ -193,6 +193,14 @@ choice
|
|||
their output to the USART1 port on SAMV7 based
|
||||
machines.
|
||||
|
||||
config DEBUG_AT91_SAMA7G5_FLEXCOM3
|
||||
bool "Kernel low-level debugging on SAMA7G5 FLEXCOM3"
|
||||
select DEBUG_AT91_UART
|
||||
depends on SOC_SAMA7G5
|
||||
help
|
||||
Say Y here if you want kernel low-level debugging support
|
||||
on the FLEXCOM3 port of SAMA7G5.
|
||||
|
||||
config DEBUG_BCM2835
|
||||
bool "Kernel low-level debugging on BCM2835 PL011 UART"
|
||||
depends on ARCH_BCM2835 && ARCH_MULTI_V6
|
||||
|
@ -1668,6 +1676,7 @@ config DEBUG_UART_PHYS
|
|||
default 0xd4017000 if DEBUG_MMP_UART2
|
||||
default 0xd4018000 if DEBUG_MMP_UART3
|
||||
default 0xe0000000 if DEBUG_SPEAR13XX
|
||||
default 0xe1824200 if DEBUG_AT91_SAMA7G5_FLEXCOM3
|
||||
default 0xe4007000 if DEBUG_HIP04_UART
|
||||
default 0xe6c40000 if DEBUG_RMOBILE_SCIFA0
|
||||
default 0xe6c50000 if DEBUG_RMOBILE_SCIFA1
|
||||
|
@ -1729,6 +1738,7 @@ config DEBUG_UART_VIRT
|
|||
default 0xc8821000 if DEBUG_RV1108_UART1
|
||||
default 0xc8912000 if DEBUG_RV1108_UART0
|
||||
default 0xe0010fe0 if ARCH_RPC
|
||||
default 0xe0824200 if DEBUG_AT91_SAMA7G5_FLEXCOM3
|
||||
default 0xf0010000 if DEBUG_ASM9260_UART
|
||||
default 0xf0100000 if DEBUG_DIGICOLOR_UA0
|
||||
default 0xf01fb000 if DEBUG_NOMADIK_UART
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
||||
if (!at91_is_pm_mode_active(AT91_PM_BACKUP) &&
|
||||
!at91_is_pm_mode_active(AT91_PM_ULP1))
|
||||
return;
|
||||
|
||||
np = of_find_matching_node(NULL, atmel_shdwc_ids);
|
||||
if (!np) {
|
||||
pr_warn("%s: failed to find shdwc!\n", __func__);
|
||||
goto ulp1_default;
|
||||
}
|
||||
|
||||
soc_pm.data.shdwc = of_iomap(np, 0);
|
||||
of_node_put(np);
|
||||
int ret, mode;
|
||||
|
||||
ret = at91_pm_backup_init();
|
||||
if (ret) {
|
||||
if (!at91_is_pm_mode_active(AT91_PM_ULP1))
|
||||
goto unmap;
|
||||
else
|
||||
goto backup_default;
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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]
|
||||
|
||||
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]
|
||||
/* De-assert SDRAM initialization. */
|
||||
ldr tmp1, [r2, #UDDRC_DFIMISC]
|
||||
bic tmp1, tmp1, #UDDRC_DFIMISC_DFI_INIT_COMPLETE_EN
|
||||
str tmp1, [r2, #UDDRC_DFIMISC]
|
||||
|
||||
/* Active the self-refresh mode */
|
||||
mov r0, #SRAMC_SELF_FRESH_ACTIVE
|
||||
bl at91_sramc_self_refresh
|
||||
/* Quasi-dynamic programming done. */
|
||||
mov tmp1, #UDDRC_SWCTRL_SW_DONE
|
||||
str tmp1, [r2, #UDDRC_SWCTRL]
|
||||
|
||||
ldr r0, .pm_mode
|
||||
cmp r0, #AT91_PM_STANDBY
|
||||
beq standby
|
||||
cmp r0, #AT91_PM_BACKUP
|
||||
beq backup_mode
|
||||
sr_dis_1:
|
||||
ldr tmp1, [r2, #UDDRC_SWSTAT]
|
||||
tst tmp1, #UDDRC_SWSTAT_SW_DONE_ACK
|
||||
beq sr_dis_1
|
||||
|
||||
bl at91_ulp_mode
|
||||
b exit_suspend
|
||||
/* 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]
|
||||
|
||||
standby:
|
||||
/* Wait for interrupt */
|
||||
ldr pmc, .pmc_base
|
||||
at91_cpu_idle
|
||||
b exit_suspend
|
||||
sr_dis_4:
|
||||
/* Wait for it. */
|
||||
ldr tmp1, [r3, #DDR3PHY_PGSR]
|
||||
tst tmp1, #DDR3PHY_PGSR_IDONE
|
||||
beq sr_dis_4
|
||||
|
||||
backup_mode:
|
||||
bl at91_backup_mode
|
||||
b exit_suspend
|
||||
/* Enable quasi-dynamic programming. */
|
||||
mov tmp1, #0
|
||||
str tmp1, [r2, #UDDRC_SWCTRL]
|
||||
|
||||
exit_suspend:
|
||||
/* Exit the self-refresh mode */
|
||||
mov r0, #SRAMC_SELF_FRESH_EXIT
|
||||
bl at91_sramc_self_refresh
|
||||
/* 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]
|
||||
|
||||
/* Restore registers, and return */
|
||||
ldmfd sp!, {r4 - r12, pc}
|
||||
ENDPROC(at91_pm_suspend_in_sram)
|
||||
/* Programming is done. Set sw_done. */
|
||||
mov tmp1, #UDDRC_SWCTRL_SW_DONE
|
||||
str tmp1, [r2, #UDDRC_SWCTRL]
|
||||
|
||||
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]
|
||||
sr_dis_5:
|
||||
/* Wait for it. */
|
||||
ldr tmp1, [r2, #UDDRC_SWSTAT]
|
||||
tst tmp1, #UDDRC_SWSTAT_SW_DONE_ACK
|
||||
beq sr_dis_5
|
||||
|
||||
wait_mckrdy
|
||||
/* Trigger self-refresh exit. */
|
||||
ldr tmp1, [r2, #UDDRC_PWRCTL]
|
||||
bic tmp1, tmp1, #UDDRC_PWRCTRL_SELFREF_SW
|
||||
str tmp1, [r2, #UDDRC_PWRCTL]
|
||||
|
||||
/*BUMEN*/
|
||||
ldr r0, .sfrbu
|
||||
mov tmp1, #0x1
|
||||
str tmp1, [r0, #0x10]
|
||||
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
|
||||
|
||||
/* Shutdown */
|
||||
ldr r0, .shdwc
|
||||
mov tmp1, #0xA5000000
|
||||
add tmp1, tmp1, #0x1
|
||||
str tmp1, [r0, #0]
|
||||
ENDPROC(at91_backup_mode)
|
||||
/* 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]
|
||||
|
||||
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
|
||||
|
||||
cmp r1, #AT91_MEMCTRL_MC
|
||||
bne sr_ena_ddrc_sf
|
||||
|
||||
/* Active SDRAM self-refresh mode */
|
||||
mov r3, #1
|
||||
str r3, [r2, #AT91_MC_SDRAMC_SRR]
|
||||
b sr_ena_exit
|
||||
|
||||
sr_ena_ddrc_sf:
|
||||
cmp r1, #AT91_MEMCTRL_DDRSDR
|
||||
bne sr_ena_sdramc_sf
|
||||
|
||||
/*
|
||||
* DDR Memory controller
|
||||
*/
|
||||
|
||||
/* 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 sr_ena_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]
|
||||
|
||||
sr_ena_no_2nd_ddrc:
|
||||
b sr_ena_exit
|
||||
|
||||
/*
|
||||
* 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 tmp3, #0
|
||||
wait_mckrdy tmp3
|
||||
|
||||
mov pc, lr
|
||||
ENDPROC(at91_ulp_mode)
|
||||
at91_mckx_ps_restore
|
||||
.endm
|
||||
|
||||
.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]
|
||||
|
||||
mov tmp3, #0
|
||||
wait_mckrdy tmp3
|
||||
|
||||
/*BUMEN*/
|
||||
ldr r0, .sfrbu
|
||||
mov tmp1, #0x1
|
||||
str tmp1, [r0, #0x10]
|
||||
|
||||
/* Wait for it. */
|
||||
1: ldr tmp1, [r0, #0x10]
|
||||
tst tmp1, #0x1
|
||||
beq 1b
|
||||
|
||||
/* Shutdown */
|
||||
ldr r0, .shdwc
|
||||
mov tmp1, #0xA5000000
|
||||
add tmp1, tmp1, #0x1
|
||||
at91_backup_set_lpm tmp1
|
||||
str tmp1, [r0, #0]
|
||||
.endm
|
||||
|
||||
/*
|
||||
* void at91_sramc_self_refresh(unsigned int is_active)
|
||||
*
|
||||
* void at91_suspend_sram_fn(struct at91_pm_data*)
|
||||
* @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
|
||||
* @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}
|
||||
|
||||
ENTRY(at91_sramc_self_refresh)
|
||||
ldr r1, .memtype
|
||||
ldr r2, .sramc_base
|
||||
/* Drain write buffer */
|
||||
mov tmp1, #0
|
||||
mcr p15, 0, tmp1, c7, c10, 4
|
||||
|
||||
cmp r1, #AT91_MEMCTRL_MC
|
||||
bne ddrc_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]
|
||||
|
||||
/*
|
||||
* at91rm9200 Memory controller
|
||||
*/
|
||||
/* Active the self-refresh mode */
|
||||
at91_sramc_self_refresh_ena
|
||||
|
||||
/*
|
||||
* For exiting the self-refresh mode, do nothing,
|
||||
* automatically exit the self-refresh mode.
|
||||
*/
|
||||
tst r0, #SRAMC_SELF_FRESH_ACTIVE
|
||||
beq exit_sramc_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 exit_sramc_sf
|
||||
at91_ulp_mode
|
||||
b exit_suspend
|
||||
|
||||
ddrc_sf:
|
||||
cmp r1, #AT91_MEMCTRL_DDRSDR
|
||||
bne sdramc_sf
|
||||
standby:
|
||||
/* Wait for interrupt */
|
||||
ldr pmc, .pmc_base
|
||||
at91_cpu_idle
|
||||
b exit_suspend
|
||||
|
||||
/*
|
||||
* DDR Memory controller
|
||||
*/
|
||||
tst r0, #SRAMC_SELF_FRESH_ACTIVE
|
||||
beq ddrc_exit_sf
|
||||
backup_mode:
|
||||
at91_backup_mode
|
||||
|
||||
/* 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 */
|
||||
at91_sramc_self_refresh_dis
|
||||
|
||||
/* 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)
|
||||
/* 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
|
||||
|
|
|
@ -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
|
||||
|
|
@ -137,6 +137,32 @@
|
|||
#define AT91_PMC_PLLADIV2_ON (1 << 12)
|
||||
#define AT91_PMC_H32MXDIV BIT(24)
|
||||
|
||||
#define AT91_PMC_MCR_V2 0x30 /* Master Clock Register [SAMA7G5 only] */
|
||||
#define AT91_PMC_MCR_V2_ID_MSK (0xF)
|
||||
#define AT91_PMC_MCR_V2_ID(_id) ((_id) & AT91_PMC_MCR_V2_ID_MSK)
|
||||
#define AT91_PMC_MCR_V2_CMD (1 << 7)
|
||||
#define AT91_PMC_MCR_V2_DIV (7 << 8)
|
||||
#define AT91_PMC_MCR_V2_DIV1 (0 << 8)
|
||||
#define AT91_PMC_MCR_V2_DIV2 (1 << 8)
|
||||
#define AT91_PMC_MCR_V2_DIV4 (2 << 8)
|
||||
#define AT91_PMC_MCR_V2_DIV8 (3 << 8)
|
||||
#define AT91_PMC_MCR_V2_DIV16 (4 << 8)
|
||||
#define AT91_PMC_MCR_V2_DIV32 (5 << 8)
|
||||
#define AT91_PMC_MCR_V2_DIV64 (6 << 8)
|
||||
#define AT91_PMC_MCR_V2_DIV3 (7 << 8)
|
||||
#define AT91_PMC_MCR_V2_CSS (0x1F << 16)
|
||||
#define AT91_PMC_MCR_V2_CSS_MD_SLCK (0 << 16)
|
||||
#define AT91_PMC_MCR_V2_CSS_TD_SLCK (1 << 16)
|
||||
#define AT91_PMC_MCR_V2_CSS_MAINCK (2 << 16)
|
||||
#define AT91_PMC_MCR_V2_CSS_MCK0 (3 << 16)
|
||||
#define AT91_PMC_MCR_V2_CSS_SYSPLL (5 << 16)
|
||||
#define AT91_PMC_MCR_V2_CSS_DDRPLL (6 << 16)
|
||||
#define AT91_PMC_MCR_V2_CSS_IMGPLL (7 << 16)
|
||||
#define AT91_PMC_MCR_V2_CSS_BAUDPLL (8 << 16)
|
||||
#define AT91_PMC_MCR_V2_CSS_AUDIOPLL (9 << 16)
|
||||
#define AT91_PMC_MCR_V2_CSS_ETHPLL (10 << 16)
|
||||
#define AT91_PMC_MCR_V2_EN (1 << 28)
|
||||
|
||||
#define AT91_PMC_XTALF 0x34 /* Main XTAL Frequency Register [SAMA7G5 only] */
|
||||
|
||||
#define AT91_PMC_USB 0x38 /* USB Clock Register [some SAM9 only] */
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Microchip SAMA7 UDDR Controller and DDR3 PHY Controller registers offsets
|
||||
* and bit definitions.
|
||||
*
|
||||
* Copyright (C) [2020] Microchip Technology Inc. and its subsidiaries
|
||||
*
|
||||
* Author: Claudu Beznea <claudiu.beznea@microchip.com>
|
||||
*/
|
||||
|
||||
#ifndef __SAMA7_DDR_H__
|
||||
#define __SAMA7_DDR_H__
|
||||
|
||||
#ifdef CONFIG_SOC_SAMA7
|
||||
|
||||
/* DDR3PHY */
|
||||
#define DDR3PHY_PIR (0x04) /* DDR3PHY PHY Initialization Register */
|
||||
#define DDR3PHY_PIR_DLLBYP (1 << 17) /* DLL Bypass */
|
||||
#define DDR3PHY_PIR_ITMSRST (1 << 4) /* Interface Timing Module Soft Reset */
|
||||
#define DDR3PHY_PIR_DLLLOCK (1 << 2) /* DLL Lock */
|
||||
#define DDR3PHY_PIR_DLLSRST (1 << 1) /* DLL Soft Rest */
|
||||
#define DDR3PHY_PIR_INIT (1 << 0) /* Initialization Trigger */
|
||||
|
||||
#define DDR3PHY_PGCR (0x08) /* DDR3PHY PHY General Configuration Register */
|
||||
#define DDR3PHY_PGCR_CKDV1 (1 << 13) /* CK# Disable Value */
|
||||
#define DDR3PHY_PGCR_CKDV0 (1 << 12) /* CK Disable Value */
|
||||
|
||||
#define DDR3PHY_PGSR (0x0C) /* DDR3PHY PHY General Status Register */
|
||||
#define DDR3PHY_PGSR_IDONE (1 << 0) /* Initialization Done */
|
||||
|
||||
#define DDR3PHY_ACIOCR (0x24) /* DDR3PHY AC I/O Configuration Register */
|
||||
#define DDR3PHY_ACIOCR_CSPDD_CS0 (1 << 18) /* CS#[0] Power Down Driver */
|
||||
#define DDR3PHY_ACIOCR_CKPDD_CK0 (1 << 8) /* CK[0] Power Down Driver */
|
||||
#define DDR3PHY_ACIORC_ACPDD (1 << 3) /* AC Power Down Driver */
|
||||
|
||||
#define DDR3PHY_DXCCR (0x28) /* DDR3PHY DATX8 Common Configuration Register */
|
||||
#define DDR3PHY_DXCCR_DXPDR (1 << 3) /* Data Power Down Receiver */
|
||||
|
||||
#define DDR3PHY_DSGCR (0x2C) /* DDR3PHY DDR System General Configuration Register */
|
||||
#define DDR3PHY_DSGCR_ODTPDD_ODT0 (1 << 20) /* ODT[0] Power Down Driver */
|
||||
|
||||
#define DDR3PHY_ZQ0SR0 (0x188) /* ZQ status register 0 */
|
||||
|
||||
/* UDDRC */
|
||||
#define UDDRC_STAT (0x04) /* UDDRC Operating Mode Status Register */
|
||||
#define UDDRC_STAT_SELFREF_TYPE_DIS (0x0 << 4) /* SDRAM is not in Self-refresh */
|
||||
#define UDDRC_STAT_SELFREF_TYPE_PHY (0x1 << 4) /* SDRAM is in Self-refresh, which was caused by PHY Master Request */
|
||||
#define UDDRC_STAT_SELFREF_TYPE_SW (0x2 << 4) /* SDRAM is in Self-refresh, which was not caused solely under Automatic Self-refresh control */
|
||||
#define UDDRC_STAT_SELFREF_TYPE_AUTO (0x3 << 4) /* SDRAM is in Self-refresh, which was caused by Automatic Self-refresh only */
|
||||
#define UDDRC_STAT_SELFREF_TYPE_MSK (0x3 << 4) /* Self-refresh type mask */
|
||||
#define UDDRC_STAT_OPMODE_INIT (0x0 << 0) /* Init */
|
||||
#define UDDRC_STAT_OPMODE_NORMAL (0x1 << 0) /* Normal */
|
||||
#define UDDRC_STAT_OPMODE_PWRDOWN (0x2 << 0) /* Power-down */
|
||||
#define UDDRC_STAT_OPMODE_SELF_REFRESH (0x3 << 0) /* Self-refresh */
|
||||
#define UDDRC_STAT_OPMODE_MSK (0x7 << 0) /* Operating mode mask */
|
||||
|
||||
#define UDDRC_PWRCTL (0x30) /* UDDRC Low Power Control Register */
|
||||
#define UDDRC_PWRCTRL_SELFREF_SW (1 << 5) /* Software self-refresh */
|
||||
|
||||
#define UDDRC_DFIMISC (0x1B0) /* UDDRC DFI Miscellaneous Control Register */
|
||||
#define UDDRC_DFIMISC_DFI_INIT_COMPLETE_EN (1 << 0) /* PHY initialization complete enable signal */
|
||||
|
||||
#define UDDRC_SWCTRL (0x320) /* UDDRC Software Register Programming Control Enable */
|
||||
#define UDDRC_SWCTRL_SW_DONE (1 << 0) /* Enable quasi-dynamic register programming outside reset */
|
||||
|
||||
#define UDDRC_SWSTAT (0x324) /* UDDRC Software Register Programming Control Status */
|
||||
#define UDDRC_SWSTAT_SW_DONE_ACK (1 << 0) /* Register programming done */
|
||||
|
||||
#define UDDRC_PSTAT (0x3FC) /* UDDRC Port Status Register */
|
||||
#define UDDRC_PSTAT_ALL_PORTS (0x1F001F) /* Read + writes outstanding transactions on all ports */
|
||||
|
||||
#define UDDRC_PCTRL_0 (0x490) /* UDDRC Port 0 Control Register */
|
||||
#define UDDRC_PCTRL_1 (0x540) /* UDDRC Port 1 Control Register */
|
||||
#define UDDRC_PCTRL_2 (0x5F0) /* UDDRC Port 2 Control Register */
|
||||
#define UDDRC_PCTRL_3 (0x6A0) /* UDDRC Port 3 Control Register */
|
||||
#define UDDRC_PCTRL_4 (0x750) /* UDDRC Port 4 Control Register */
|
||||
|
||||
#endif /* CONFIG_SOC_SAMA7 */
|
||||
|
||||
#endif /* __SAMA7_DDR_H__ */
|
|
@ -0,0 +1,34 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Microchip SAMA7 SFRBU registers offsets and bit definitions.
|
||||
*
|
||||
* Copyright (C) [2020] Microchip Technology Inc. and its subsidiaries
|
||||
*
|
||||
* Author: Claudu Beznea <claudiu.beznea@microchip.com>
|
||||
*/
|
||||
|
||||
#ifndef __SAMA7_SFRBU_H__
|
||||
#define __SAMA7_SFRBU_H__
|
||||
|
||||
#ifdef CONFIG_SOC_SAMA7
|
||||
|
||||
#define AT91_SFRBU_PSWBU (0x00) /* SFRBU Power Switch BU Control Register */
|
||||
#define AT91_SFRBU_PSWBU_PSWKEY (0x4BD20C << 8) /* Specific value mandatory to allow writing of other register bits */
|
||||
#define AT91_SFRBU_PSWBU_STATE (1 << 2) /* Power switch BU state */
|
||||
#define AT91_SFRBU_PSWBU_SOFTSWITCH (1 << 1) /* Power switch BU source selection */
|
||||
#define AT91_SFRBU_PSWBU_CTRL (1 << 0) /* Power switch BU control */
|
||||
|
||||
#define AT91_SFRBU_25LDOCR (0x0C) /* SFRBU 2.5V LDO Control Register */
|
||||
#define AT91_SFRBU_25LDOCR_LDOANAKEY (0x3B6E18 << 8) /* Specific value mandatory to allow writing of other register bits. */
|
||||
#define AT91_SFRBU_25LDOCR_STATE (1 << 3) /* LDOANA Switch On/Off Control */
|
||||
#define AT91_SFRBU_25LDOCR_LP (1 << 2) /* LDOANA Low-Power Mode Control */
|
||||
#define AT91_SFRBU_PD_VALUE_MSK (0x3)
|
||||
#define AT91_SFRBU_25LDOCR_PD_VALUE(v) ((v) & AT91_SFRBU_PD_VALUE_MSK) /* LDOANA Pull-down value */
|
||||
|
||||
#define AT91_FRBU_DDRPWR (0x10) /* SFRBU DDR Power Control Register */
|
||||
#define AT91_FRBU_DDRPWR_STATE (1 << 0) /* DDR Power Mode State */
|
||||
|
||||
#endif /* CONFIG_SOC_SAMA7 */
|
||||
|
||||
#endif /* __SAMA7_SFRBU_H__ */
|
||||
|
Загрузка…
Ссылка в новой задаче