162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * arch/arm/mach-at91/pm.c 462306a36Sopenharmony_ci * AT91 Power Management 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2005 David Brownell 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/genalloc.h> 1062306a36Sopenharmony_ci#include <linux/io.h> 1162306a36Sopenharmony_ci#include <linux/of_address.h> 1262306a36Sopenharmony_ci#include <linux/of.h> 1362306a36Sopenharmony_ci#include <linux/of_fdt.h> 1462306a36Sopenharmony_ci#include <linux/of_platform.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/parser.h> 1762306a36Sopenharmony_ci#include <linux/suspend.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/clk.h> 2062306a36Sopenharmony_ci#include <linux/clk/at91_pmc.h> 2162306a36Sopenharmony_ci#include <linux/platform_data/atmel.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <asm/cacheflush.h> 2462306a36Sopenharmony_ci#include <asm/fncpy.h> 2562306a36Sopenharmony_ci#include <asm/system_misc.h> 2662306a36Sopenharmony_ci#include <asm/suspend.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "generic.h" 2962306a36Sopenharmony_ci#include "pm.h" 3062306a36Sopenharmony_ci#include "sam_secure.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define BACKUP_DDR_PHY_CALIBRATION (9) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/** 3562306a36Sopenharmony_ci * struct at91_pm_bu - AT91 power management backup unit data structure 3662306a36Sopenharmony_ci * @suspended: true if suspended to backup mode 3762306a36Sopenharmony_ci * @reserved: reserved 3862306a36Sopenharmony_ci * @canary: canary data for memory checking after exit from backup mode 3962306a36Sopenharmony_ci * @resume: resume API 4062306a36Sopenharmony_ci * @ddr_phy_calibration: DDR PHY calibration data: ZQ0CR0, first 8 words 4162306a36Sopenharmony_ci * of the memory 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_cistruct at91_pm_bu { 4462306a36Sopenharmony_ci int suspended; 4562306a36Sopenharmony_ci unsigned long reserved; 4662306a36Sopenharmony_ci phys_addr_t canary; 4762306a36Sopenharmony_ci phys_addr_t resume; 4862306a36Sopenharmony_ci unsigned long ddr_phy_calibration[BACKUP_DDR_PHY_CALIBRATION]; 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/** 5262306a36Sopenharmony_ci * struct at91_pm_sfrbu_regs - registers mapping for SFRBU 5362306a36Sopenharmony_ci * @pswbu: power switch BU control registers 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_cistruct at91_pm_sfrbu_regs { 5662306a36Sopenharmony_ci struct { 5762306a36Sopenharmony_ci u32 key; 5862306a36Sopenharmony_ci u32 ctrl; 5962306a36Sopenharmony_ci u32 state; 6062306a36Sopenharmony_ci u32 softsw; 6162306a36Sopenharmony_ci } pswbu; 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/** 6562306a36Sopenharmony_ci * enum at91_pm_eth_clk - Ethernet clock indexes 6662306a36Sopenharmony_ci * @AT91_PM_ETH_PCLK: pclk index 6762306a36Sopenharmony_ci * @AT91_PM_ETH_HCLK: hclk index 6862306a36Sopenharmony_ci * @AT91_PM_ETH_MAX_CLK: max index 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_cienum at91_pm_eth_clk { 7162306a36Sopenharmony_ci AT91_PM_ETH_PCLK, 7262306a36Sopenharmony_ci AT91_PM_ETH_HCLK, 7362306a36Sopenharmony_ci AT91_PM_ETH_MAX_CLK, 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/** 7762306a36Sopenharmony_ci * enum at91_pm_eth - Ethernet controller indexes 7862306a36Sopenharmony_ci * @AT91_PM_G_ETH: gigabit Ethernet controller index 7962306a36Sopenharmony_ci * @AT91_PM_E_ETH: megabit Ethernet controller index 8062306a36Sopenharmony_ci * @AT91_PM_MAX_ETH: max index 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_cienum at91_pm_eth { 8362306a36Sopenharmony_ci AT91_PM_G_ETH, 8462306a36Sopenharmony_ci AT91_PM_E_ETH, 8562306a36Sopenharmony_ci AT91_PM_MAX_ETH, 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/** 8962306a36Sopenharmony_ci * struct at91_pm_quirk_eth - AT91 PM Ethernet quirks 9062306a36Sopenharmony_ci * @dev: Ethernet device 9162306a36Sopenharmony_ci * @np: Ethernet device node 9262306a36Sopenharmony_ci * @clks: Ethernet clocks 9362306a36Sopenharmony_ci * @modes: power management mode that this quirk applies to 9462306a36Sopenharmony_ci * @dns_modes: do not suspend modes: stop suspending if Ethernet is configured 9562306a36Sopenharmony_ci * as wakeup source but buggy and no other wakeup source is 9662306a36Sopenharmony_ci * available 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_cistruct at91_pm_quirk_eth { 9962306a36Sopenharmony_ci struct device *dev; 10062306a36Sopenharmony_ci struct device_node *np; 10162306a36Sopenharmony_ci struct clk_bulk_data clks[AT91_PM_ETH_MAX_CLK]; 10262306a36Sopenharmony_ci u32 modes; 10362306a36Sopenharmony_ci u32 dns_modes; 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/** 10762306a36Sopenharmony_ci * struct at91_pm_quirks - AT91 PM quirks 10862306a36Sopenharmony_ci * @eth: Ethernet quirks 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_cistruct at91_pm_quirks { 11162306a36Sopenharmony_ci struct at91_pm_quirk_eth eth[AT91_PM_MAX_ETH]; 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/** 11562306a36Sopenharmony_ci * struct at91_soc_pm - AT91 SoC power management data structure 11662306a36Sopenharmony_ci * @config_shdwc_ws: wakeup sources configuration function for SHDWC 11762306a36Sopenharmony_ci * @config_pmc_ws: wakeup srouces configuration function for PMC 11862306a36Sopenharmony_ci * @ws_ids: wakup sources of_device_id array 11962306a36Sopenharmony_ci * @bu: backup unit mapped data (for backup mode) 12062306a36Sopenharmony_ci * @quirks: PM quirks 12162306a36Sopenharmony_ci * @data: PM data to be used on last phase of suspend 12262306a36Sopenharmony_ci * @sfrbu_regs: SFRBU registers mapping 12362306a36Sopenharmony_ci * @memcs: memory chip select 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_cistruct at91_soc_pm { 12662306a36Sopenharmony_ci int (*config_shdwc_ws)(void __iomem *shdwc, u32 *mode, u32 *polarity); 12762306a36Sopenharmony_ci int (*config_pmc_ws)(void __iomem *pmc, u32 mode, u32 polarity); 12862306a36Sopenharmony_ci const struct of_device_id *ws_ids; 12962306a36Sopenharmony_ci struct at91_pm_bu *bu; 13062306a36Sopenharmony_ci struct at91_pm_quirks quirks; 13162306a36Sopenharmony_ci struct at91_pm_data data; 13262306a36Sopenharmony_ci struct at91_pm_sfrbu_regs sfrbu_regs; 13362306a36Sopenharmony_ci void *memcs; 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/** 13762306a36Sopenharmony_ci * enum at91_pm_iomaps - IOs that needs to be mapped for different PM modes 13862306a36Sopenharmony_ci * @AT91_PM_IOMAP_SHDWC: SHDWC controller 13962306a36Sopenharmony_ci * @AT91_PM_IOMAP_SFRBU: SFRBU controller 14062306a36Sopenharmony_ci * @AT91_PM_IOMAP_ETHC: Ethernet controller 14162306a36Sopenharmony_ci */ 14262306a36Sopenharmony_cienum at91_pm_iomaps { 14362306a36Sopenharmony_ci AT91_PM_IOMAP_SHDWC, 14462306a36Sopenharmony_ci AT91_PM_IOMAP_SFRBU, 14562306a36Sopenharmony_ci AT91_PM_IOMAP_ETHC, 14662306a36Sopenharmony_ci}; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci#define AT91_PM_IOMAP(name) BIT(AT91_PM_IOMAP_##name) 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic struct at91_soc_pm soc_pm = { 15162306a36Sopenharmony_ci .data = { 15262306a36Sopenharmony_ci .standby_mode = AT91_PM_STANDBY, 15362306a36Sopenharmony_ci .suspend_mode = AT91_PM_ULP0, 15462306a36Sopenharmony_ci }, 15562306a36Sopenharmony_ci}; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic const match_table_t pm_modes __initconst = { 15862306a36Sopenharmony_ci { AT91_PM_STANDBY, "standby" }, 15962306a36Sopenharmony_ci { AT91_PM_ULP0, "ulp0" }, 16062306a36Sopenharmony_ci { AT91_PM_ULP0_FAST, "ulp0-fast" }, 16162306a36Sopenharmony_ci { AT91_PM_ULP1, "ulp1" }, 16262306a36Sopenharmony_ci { AT91_PM_BACKUP, "backup" }, 16362306a36Sopenharmony_ci { -1, NULL }, 16462306a36Sopenharmony_ci}; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci#define at91_ramc_read(id, field) \ 16762306a36Sopenharmony_ci __raw_readl(soc_pm.data.ramc[id] + field) 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci#define at91_ramc_write(id, field, value) \ 17062306a36Sopenharmony_ci __raw_writel(value, soc_pm.data.ramc[id] + field) 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic int at91_pm_valid_state(suspend_state_t state) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci switch (state) { 17562306a36Sopenharmony_ci case PM_SUSPEND_ON: 17662306a36Sopenharmony_ci case PM_SUSPEND_STANDBY: 17762306a36Sopenharmony_ci case PM_SUSPEND_MEM: 17862306a36Sopenharmony_ci return 1; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci default: 18162306a36Sopenharmony_ci return 0; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic int canary = 0xA5A5A5A5; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistruct wakeup_source_info { 18862306a36Sopenharmony_ci unsigned int pmc_fsmr_bit; 18962306a36Sopenharmony_ci unsigned int shdwc_mr_bit; 19062306a36Sopenharmony_ci bool set_polarity; 19162306a36Sopenharmony_ci}; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic const struct wakeup_source_info ws_info[] = { 19462306a36Sopenharmony_ci { .pmc_fsmr_bit = AT91_PMC_FSTT(10), .set_polarity = true }, 19562306a36Sopenharmony_ci { .pmc_fsmr_bit = AT91_PMC_RTCAL, .shdwc_mr_bit = BIT(17) }, 19662306a36Sopenharmony_ci { .pmc_fsmr_bit = AT91_PMC_USBAL }, 19762306a36Sopenharmony_ci { .pmc_fsmr_bit = AT91_PMC_SDMMC_CD }, 19862306a36Sopenharmony_ci { .pmc_fsmr_bit = AT91_PMC_RTTAL }, 19962306a36Sopenharmony_ci { .pmc_fsmr_bit = AT91_PMC_RXLP_MCE }, 20062306a36Sopenharmony_ci}; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic const struct of_device_id sama5d2_ws_ids[] = { 20362306a36Sopenharmony_ci { .compatible = "atmel,sama5d2-gem", .data = &ws_info[0] }, 20462306a36Sopenharmony_ci { .compatible = "atmel,sama5d2-rtc", .data = &ws_info[1] }, 20562306a36Sopenharmony_ci { .compatible = "atmel,sama5d3-udc", .data = &ws_info[2] }, 20662306a36Sopenharmony_ci { .compatible = "atmel,at91rm9200-ohci", .data = &ws_info[2] }, 20762306a36Sopenharmony_ci { .compatible = "usb-ohci", .data = &ws_info[2] }, 20862306a36Sopenharmony_ci { .compatible = "atmel,at91sam9g45-ehci", .data = &ws_info[2] }, 20962306a36Sopenharmony_ci { .compatible = "usb-ehci", .data = &ws_info[2] }, 21062306a36Sopenharmony_ci { .compatible = "atmel,sama5d2-sdhci", .data = &ws_info[3] }, 21162306a36Sopenharmony_ci { /* sentinel */ } 21262306a36Sopenharmony_ci}; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic const struct of_device_id sam9x60_ws_ids[] = { 21562306a36Sopenharmony_ci { .compatible = "microchip,sam9x60-rtc", .data = &ws_info[1] }, 21662306a36Sopenharmony_ci { .compatible = "atmel,at91rm9200-ohci", .data = &ws_info[2] }, 21762306a36Sopenharmony_ci { .compatible = "usb-ohci", .data = &ws_info[2] }, 21862306a36Sopenharmony_ci { .compatible = "atmel,at91sam9g45-ehci", .data = &ws_info[2] }, 21962306a36Sopenharmony_ci { .compatible = "usb-ehci", .data = &ws_info[2] }, 22062306a36Sopenharmony_ci { .compatible = "microchip,sam9x60-rtt", .data = &ws_info[4] }, 22162306a36Sopenharmony_ci { .compatible = "cdns,sam9x60-macb", .data = &ws_info[5] }, 22262306a36Sopenharmony_ci { /* sentinel */ } 22362306a36Sopenharmony_ci}; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic const struct of_device_id sama7g5_ws_ids[] = { 22662306a36Sopenharmony_ci { .compatible = "microchip,sama7g5-rtc", .data = &ws_info[1] }, 22762306a36Sopenharmony_ci { .compatible = "microchip,sama7g5-ohci", .data = &ws_info[2] }, 22862306a36Sopenharmony_ci { .compatible = "usb-ohci", .data = &ws_info[2] }, 22962306a36Sopenharmony_ci { .compatible = "atmel,at91sam9g45-ehci", .data = &ws_info[2] }, 23062306a36Sopenharmony_ci { .compatible = "usb-ehci", .data = &ws_info[2] }, 23162306a36Sopenharmony_ci { .compatible = "microchip,sama7g5-sdhci", .data = &ws_info[3] }, 23262306a36Sopenharmony_ci { .compatible = "microchip,sama7g5-rtt", .data = &ws_info[4] }, 23362306a36Sopenharmony_ci { /* sentinel */ } 23462306a36Sopenharmony_ci}; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic int at91_pm_config_ws(unsigned int pm_mode, bool set) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci const struct wakeup_source_info *wsi; 23962306a36Sopenharmony_ci const struct of_device_id *match; 24062306a36Sopenharmony_ci struct platform_device *pdev; 24162306a36Sopenharmony_ci struct device_node *np; 24262306a36Sopenharmony_ci unsigned int mode = 0, polarity = 0, val = 0; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (pm_mode != AT91_PM_ULP1) 24562306a36Sopenharmony_ci return 0; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (!soc_pm.data.pmc || !soc_pm.data.shdwc || !soc_pm.ws_ids) 24862306a36Sopenharmony_ci return -EPERM; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (!set) { 25162306a36Sopenharmony_ci writel(mode, soc_pm.data.pmc + AT91_PMC_FSMR); 25262306a36Sopenharmony_ci return 0; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (soc_pm.config_shdwc_ws) 25662306a36Sopenharmony_ci soc_pm.config_shdwc_ws(soc_pm.data.shdwc, &mode, &polarity); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* SHDWC.MR */ 25962306a36Sopenharmony_ci val = readl(soc_pm.data.shdwc + 0x04); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* Loop through defined wakeup sources. */ 26262306a36Sopenharmony_ci for_each_matching_node_and_match(np, soc_pm.ws_ids, &match) { 26362306a36Sopenharmony_ci pdev = of_find_device_by_node(np); 26462306a36Sopenharmony_ci if (!pdev) 26562306a36Sopenharmony_ci continue; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (device_may_wakeup(&pdev->dev)) { 26862306a36Sopenharmony_ci wsi = match->data; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* Check if enabled on SHDWC. */ 27162306a36Sopenharmony_ci if (wsi->shdwc_mr_bit && !(val & wsi->shdwc_mr_bit)) 27262306a36Sopenharmony_ci goto put_device; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci mode |= wsi->pmc_fsmr_bit; 27562306a36Sopenharmony_ci if (wsi->set_polarity) 27662306a36Sopenharmony_ci polarity |= wsi->pmc_fsmr_bit; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ciput_device: 28062306a36Sopenharmony_ci put_device(&pdev->dev); 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (mode) { 28462306a36Sopenharmony_ci if (soc_pm.config_pmc_ws) 28562306a36Sopenharmony_ci soc_pm.config_pmc_ws(soc_pm.data.pmc, mode, polarity); 28662306a36Sopenharmony_ci } else { 28762306a36Sopenharmony_ci pr_err("AT91: PM: no ULP1 wakeup sources found!"); 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return mode ? 0 : -EPERM; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic int at91_sama5d2_config_shdwc_ws(void __iomem *shdwc, u32 *mode, 29462306a36Sopenharmony_ci u32 *polarity) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci u32 val; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* SHDWC.WUIR */ 29962306a36Sopenharmony_ci val = readl(shdwc + 0x0c); 30062306a36Sopenharmony_ci *mode |= (val & 0x3ff); 30162306a36Sopenharmony_ci *polarity |= ((val >> 16) & 0x3ff); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return 0; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic int at91_sama5d2_config_pmc_ws(void __iomem *pmc, u32 mode, u32 polarity) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci writel(mode, pmc + AT91_PMC_FSMR); 30962306a36Sopenharmony_ci writel(polarity, pmc + AT91_PMC_FSPR); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci return 0; 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic int at91_sam9x60_config_pmc_ws(void __iomem *pmc, u32 mode, u32 polarity) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci writel(mode, pmc + AT91_PMC_FSMR); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci return 0; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic bool at91_pm_eth_quirk_is_valid(struct at91_pm_quirk_eth *eth) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci struct platform_device *pdev; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* Interface NA in DT. */ 32662306a36Sopenharmony_ci if (!eth->np) 32762306a36Sopenharmony_ci return false; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* No quirks for this interface and current suspend mode. */ 33062306a36Sopenharmony_ci if (!(eth->modes & BIT(soc_pm.data.mode))) 33162306a36Sopenharmony_ci return false; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (!eth->dev) { 33462306a36Sopenharmony_ci /* Driver not probed. */ 33562306a36Sopenharmony_ci pdev = of_find_device_by_node(eth->np); 33662306a36Sopenharmony_ci if (!pdev) 33762306a36Sopenharmony_ci return false; 33862306a36Sopenharmony_ci /* put_device(eth->dev) is called at the end of suspend. */ 33962306a36Sopenharmony_ci eth->dev = &pdev->dev; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* No quirks if device isn't a wakeup source. */ 34362306a36Sopenharmony_ci if (!device_may_wakeup(eth->dev)) 34462306a36Sopenharmony_ci return false; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci return true; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic int at91_pm_config_quirks(bool suspend) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci struct at91_pm_quirk_eth *eth; 35262306a36Sopenharmony_ci int i, j, ret, tmp; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* 35562306a36Sopenharmony_ci * Ethernet IPs who's device_node pointers are stored into 35662306a36Sopenharmony_ci * soc_pm.quirks.eth[].np cannot handle WoL packets while in ULP0, ULP1 35762306a36Sopenharmony_ci * or both due to a hardware bug. If they receive WoL packets while in 35862306a36Sopenharmony_ci * ULP0 or ULP1 IPs could stop working or the whole system could stop 35962306a36Sopenharmony_ci * working. We cannot handle this scenario in the ethernet driver itself 36062306a36Sopenharmony_ci * as the driver is common to multiple vendors and also we only know 36162306a36Sopenharmony_ci * here, in this file, if we suspend to ULP0 or ULP1 mode. Thus handle 36262306a36Sopenharmony_ci * these scenarios here, as quirks. 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_ci for (i = 0; i < AT91_PM_MAX_ETH; i++) { 36562306a36Sopenharmony_ci eth = &soc_pm.quirks.eth[i]; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (!at91_pm_eth_quirk_is_valid(eth)) 36862306a36Sopenharmony_ci continue; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* 37162306a36Sopenharmony_ci * For modes in dns_modes mask the system blocks if quirk is not 37262306a36Sopenharmony_ci * applied but if applied the interface doesn't act at WoL 37362306a36Sopenharmony_ci * events. Thus take care to avoid suspending if this interface 37462306a36Sopenharmony_ci * is the only configured wakeup source. 37562306a36Sopenharmony_ci */ 37662306a36Sopenharmony_ci if (suspend && eth->dns_modes & BIT(soc_pm.data.mode)) { 37762306a36Sopenharmony_ci int ws_count = 0; 37862306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 37962306a36Sopenharmony_ci struct wakeup_source *ws; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci for_each_wakeup_source(ws) { 38262306a36Sopenharmony_ci if (ws->dev == eth->dev) 38362306a36Sopenharmony_ci continue; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci ws_count++; 38662306a36Sopenharmony_ci break; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci#endif 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* 39162306a36Sopenharmony_ci * Checking !ws is good for all platforms with issues 39262306a36Sopenharmony_ci * even when both G_ETH and E_ETH are available as dns_modes 39362306a36Sopenharmony_ci * is populated only on G_ETH interface. 39462306a36Sopenharmony_ci */ 39562306a36Sopenharmony_ci if (!ws_count) { 39662306a36Sopenharmony_ci pr_err("AT91: PM: Ethernet cannot resume from WoL!"); 39762306a36Sopenharmony_ci ret = -EPERM; 39862306a36Sopenharmony_ci put_device(eth->dev); 39962306a36Sopenharmony_ci eth->dev = NULL; 40062306a36Sopenharmony_ci /* No need to revert clock settings for this eth. */ 40162306a36Sopenharmony_ci i--; 40262306a36Sopenharmony_ci goto clk_unconfigure; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (suspend) { 40762306a36Sopenharmony_ci clk_bulk_disable_unprepare(AT91_PM_ETH_MAX_CLK, eth->clks); 40862306a36Sopenharmony_ci } else { 40962306a36Sopenharmony_ci ret = clk_bulk_prepare_enable(AT91_PM_ETH_MAX_CLK, 41062306a36Sopenharmony_ci eth->clks); 41162306a36Sopenharmony_ci if (ret) 41262306a36Sopenharmony_ci goto clk_unconfigure; 41362306a36Sopenharmony_ci /* 41462306a36Sopenharmony_ci * Release the reference to eth->dev taken in 41562306a36Sopenharmony_ci * at91_pm_eth_quirk_is_valid(). 41662306a36Sopenharmony_ci */ 41762306a36Sopenharmony_ci put_device(eth->dev); 41862306a36Sopenharmony_ci eth->dev = NULL; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci return 0; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ciclk_unconfigure: 42562306a36Sopenharmony_ci /* 42662306a36Sopenharmony_ci * In case of resume we reach this point if clk_prepare_enable() failed. 42762306a36Sopenharmony_ci * we don't want to revert the previous clk_prepare_enable() for the 42862306a36Sopenharmony_ci * other IP. 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_ci for (j = i; j >= 0; j--) { 43162306a36Sopenharmony_ci eth = &soc_pm.quirks.eth[j]; 43262306a36Sopenharmony_ci if (suspend) { 43362306a36Sopenharmony_ci if (!at91_pm_eth_quirk_is_valid(eth)) 43462306a36Sopenharmony_ci continue; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci tmp = clk_bulk_prepare_enable(AT91_PM_ETH_MAX_CLK, eth->clks); 43762306a36Sopenharmony_ci if (tmp) { 43862306a36Sopenharmony_ci pr_err("AT91: PM: failed to enable %s clocks\n", 43962306a36Sopenharmony_ci j == AT91_PM_G_ETH ? "geth" : "eth"); 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* 44462306a36Sopenharmony_ci * Release the reference to eth->dev taken in 44562306a36Sopenharmony_ci * at91_pm_eth_quirk_is_valid(). 44662306a36Sopenharmony_ci */ 44762306a36Sopenharmony_ci put_device(eth->dev); 44862306a36Sopenharmony_ci eth->dev = NULL; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci return ret; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci/* 45562306a36Sopenharmony_ci * Called after processes are frozen, but before we shutdown devices. 45662306a36Sopenharmony_ci */ 45762306a36Sopenharmony_cistatic int at91_pm_begin(suspend_state_t state) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci int ret; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci switch (state) { 46262306a36Sopenharmony_ci case PM_SUSPEND_MEM: 46362306a36Sopenharmony_ci soc_pm.data.mode = soc_pm.data.suspend_mode; 46462306a36Sopenharmony_ci break; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci case PM_SUSPEND_STANDBY: 46762306a36Sopenharmony_ci soc_pm.data.mode = soc_pm.data.standby_mode; 46862306a36Sopenharmony_ci break; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci default: 47162306a36Sopenharmony_ci soc_pm.data.mode = -1; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci ret = at91_pm_config_ws(soc_pm.data.mode, true); 47562306a36Sopenharmony_ci if (ret) 47662306a36Sopenharmony_ci return ret; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (soc_pm.data.mode == AT91_PM_BACKUP) 47962306a36Sopenharmony_ci soc_pm.bu->suspended = 1; 48062306a36Sopenharmony_ci else if (soc_pm.bu) 48162306a36Sopenharmony_ci soc_pm.bu->suspended = 0; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci return 0; 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci/* 48762306a36Sopenharmony_ci * Verify that all the clocks are correct before entering 48862306a36Sopenharmony_ci * slow-clock mode. 48962306a36Sopenharmony_ci */ 49062306a36Sopenharmony_cistatic int at91_pm_verify_clocks(void) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci unsigned long scsr; 49362306a36Sopenharmony_ci int i; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci scsr = readl(soc_pm.data.pmc + AT91_PMC_SCSR); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci /* USB must not be using PLLB */ 49862306a36Sopenharmony_ci if ((scsr & soc_pm.data.uhp_udp_mask) != 0) { 49962306a36Sopenharmony_ci pr_err("AT91: PM - Suspend-to-RAM with USB still active\n"); 50062306a36Sopenharmony_ci return 0; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci /* PCK0..PCK3 must be disabled, or configured to use clk32k */ 50462306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 50562306a36Sopenharmony_ci u32 css; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if ((scsr & (AT91_PMC_PCK0 << i)) == 0) 50862306a36Sopenharmony_ci continue; 50962306a36Sopenharmony_ci css = readl(soc_pm.data.pmc + AT91_PMC_PCKR(i)) & AT91_PMC_CSS; 51062306a36Sopenharmony_ci if (css != AT91_PMC_CSS_SLOW) { 51162306a36Sopenharmony_ci pr_err("AT91: PM - Suspend-to-RAM with PCK%d src %d\n", i, css); 51262306a36Sopenharmony_ci return 0; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci return 1; 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci/* 52062306a36Sopenharmony_ci * Call this from platform driver suspend() to see how deeply to suspend. 52162306a36Sopenharmony_ci * For example, some controllers (like OHCI) need one of the PLL clocks 52262306a36Sopenharmony_ci * in order to act as a wakeup source, and those are not available when 52362306a36Sopenharmony_ci * going into slow clock mode. 52462306a36Sopenharmony_ci * 52562306a36Sopenharmony_ci * REVISIT: generalize as clk_will_be_available(clk)? Other platforms have 52662306a36Sopenharmony_ci * the very same problem (but not using at91 main_clk), and it'd be better 52762306a36Sopenharmony_ci * to add one generic API rather than lots of platform-specific ones. 52862306a36Sopenharmony_ci */ 52962306a36Sopenharmony_ciint at91_suspend_entering_slow_clock(void) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci return (soc_pm.data.mode >= AT91_PM_ULP0); 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ciEXPORT_SYMBOL(at91_suspend_entering_slow_clock); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic void (*at91_suspend_sram_fn)(struct at91_pm_data *); 53662306a36Sopenharmony_ciextern void at91_pm_suspend_in_sram(struct at91_pm_data *pm_data); 53762306a36Sopenharmony_ciextern u32 at91_pm_suspend_in_sram_sz; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic int at91_suspend_finish(unsigned long val) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci unsigned char modified_gray_code[] = { 54262306a36Sopenharmony_ci 0x00, 0x01, 0x02, 0x03, 0x06, 0x07, 0x04, 0x05, 0x0c, 0x0d, 54362306a36Sopenharmony_ci 0x0e, 0x0f, 0x0a, 0x0b, 0x08, 0x09, 0x18, 0x19, 0x1a, 0x1b, 54462306a36Sopenharmony_ci 0x1e, 0x1f, 0x1c, 0x1d, 0x14, 0x15, 0x16, 0x17, 0x12, 0x13, 54562306a36Sopenharmony_ci 0x10, 0x11, 54662306a36Sopenharmony_ci }; 54762306a36Sopenharmony_ci unsigned int tmp, index; 54862306a36Sopenharmony_ci int i; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (soc_pm.data.mode == AT91_PM_BACKUP && soc_pm.data.ramc_phy) { 55162306a36Sopenharmony_ci /* 55262306a36Sopenharmony_ci * Bootloader will perform DDR recalibration and will try to 55362306a36Sopenharmony_ci * restore the ZQ0SR0 with the value saved here. But the 55462306a36Sopenharmony_ci * calibration is buggy and restoring some values from ZQ0SR0 55562306a36Sopenharmony_ci * is forbidden and risky thus we need to provide processed 55662306a36Sopenharmony_ci * values for these (modified gray code values). 55762306a36Sopenharmony_ci */ 55862306a36Sopenharmony_ci tmp = readl(soc_pm.data.ramc_phy + DDR3PHY_ZQ0SR0); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* Store pull-down output impedance select. */ 56162306a36Sopenharmony_ci index = (tmp >> DDR3PHY_ZQ0SR0_PDO_OFF) & 0x1f; 56262306a36Sopenharmony_ci soc_pm.bu->ddr_phy_calibration[0] = modified_gray_code[index]; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* Store pull-up output impedance select. */ 56562306a36Sopenharmony_ci index = (tmp >> DDR3PHY_ZQ0SR0_PUO_OFF) & 0x1f; 56662306a36Sopenharmony_ci soc_pm.bu->ddr_phy_calibration[0] |= modified_gray_code[index]; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci /* Store pull-down on-die termination impedance select. */ 56962306a36Sopenharmony_ci index = (tmp >> DDR3PHY_ZQ0SR0_PDODT_OFF) & 0x1f; 57062306a36Sopenharmony_ci soc_pm.bu->ddr_phy_calibration[0] |= modified_gray_code[index]; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* Store pull-up on-die termination impedance select. */ 57362306a36Sopenharmony_ci index = (tmp >> DDR3PHY_ZQ0SRO_PUODT_OFF) & 0x1f; 57462306a36Sopenharmony_ci soc_pm.bu->ddr_phy_calibration[0] |= modified_gray_code[index]; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci /* 57762306a36Sopenharmony_ci * The 1st 8 words of memory might get corrupted in the process 57862306a36Sopenharmony_ci * of DDR PHY recalibration; it is saved here in securam and it 57962306a36Sopenharmony_ci * will be restored later, after recalibration, by bootloader 58062306a36Sopenharmony_ci */ 58162306a36Sopenharmony_ci for (i = 1; i < BACKUP_DDR_PHY_CALIBRATION; i++) 58262306a36Sopenharmony_ci soc_pm.bu->ddr_phy_calibration[i] = 58362306a36Sopenharmony_ci *((unsigned int *)soc_pm.memcs + (i - 1)); 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci flush_cache_all(); 58762306a36Sopenharmony_ci outer_disable(); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci at91_suspend_sram_fn(&soc_pm.data); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci return 0; 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_cistatic void at91_pm_switch_ba_to_vbat(void) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci unsigned int offset = offsetof(struct at91_pm_sfrbu_regs, pswbu); 59762306a36Sopenharmony_ci unsigned int val; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci /* Just for safety. */ 60062306a36Sopenharmony_ci if (!soc_pm.data.sfrbu) 60162306a36Sopenharmony_ci return; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci val = readl(soc_pm.data.sfrbu + offset); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci /* Already on VBAT. */ 60662306a36Sopenharmony_ci if (!(val & soc_pm.sfrbu_regs.pswbu.state)) 60762306a36Sopenharmony_ci return; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci val &= ~soc_pm.sfrbu_regs.pswbu.softsw; 61062306a36Sopenharmony_ci val |= soc_pm.sfrbu_regs.pswbu.key | soc_pm.sfrbu_regs.pswbu.ctrl; 61162306a36Sopenharmony_ci writel(val, soc_pm.data.sfrbu + offset); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci /* Wait for update. */ 61462306a36Sopenharmony_ci val = readl(soc_pm.data.sfrbu + offset); 61562306a36Sopenharmony_ci while (val & soc_pm.sfrbu_regs.pswbu.state) 61662306a36Sopenharmony_ci val = readl(soc_pm.data.sfrbu + offset); 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic void at91_pm_suspend(suspend_state_t state) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci if (soc_pm.data.mode == AT91_PM_BACKUP) { 62262306a36Sopenharmony_ci at91_pm_switch_ba_to_vbat(); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci cpu_suspend(0, at91_suspend_finish); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci /* The SRAM is lost between suspend cycles */ 62762306a36Sopenharmony_ci at91_suspend_sram_fn = fncpy(at91_suspend_sram_fn, 62862306a36Sopenharmony_ci &at91_pm_suspend_in_sram, 62962306a36Sopenharmony_ci at91_pm_suspend_in_sram_sz); 63062306a36Sopenharmony_ci } else { 63162306a36Sopenharmony_ci at91_suspend_finish(0); 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci outer_resume(); 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci/* 63862306a36Sopenharmony_ci * STANDBY mode has *all* drivers suspended; ignores irqs not marked as 'wakeup' 63962306a36Sopenharmony_ci * event sources; and reduces DRAM power. But otherwise it's identical to 64062306a36Sopenharmony_ci * PM_SUSPEND_ON: cpu idle, and nothing fancy done with main or cpu clocks. 64162306a36Sopenharmony_ci * 64262306a36Sopenharmony_ci * AT91_PM_ULP0 is like STANDBY plus slow clock mode, so drivers must 64362306a36Sopenharmony_ci * suspend more deeply, the master clock switches to the clk32k and turns off 64462306a36Sopenharmony_ci * the main oscillator 64562306a36Sopenharmony_ci * 64662306a36Sopenharmony_ci * AT91_PM_BACKUP turns off the whole SoC after placing the DDR in self refresh 64762306a36Sopenharmony_ci */ 64862306a36Sopenharmony_cistatic int at91_pm_enter(suspend_state_t state) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci int ret; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci ret = at91_pm_config_quirks(true); 65362306a36Sopenharmony_ci if (ret) 65462306a36Sopenharmony_ci return ret; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci switch (state) { 65762306a36Sopenharmony_ci case PM_SUSPEND_MEM: 65862306a36Sopenharmony_ci case PM_SUSPEND_STANDBY: 65962306a36Sopenharmony_ci /* 66062306a36Sopenharmony_ci * Ensure that clocks are in a valid state. 66162306a36Sopenharmony_ci */ 66262306a36Sopenharmony_ci if (soc_pm.data.mode >= AT91_PM_ULP0 && 66362306a36Sopenharmony_ci !at91_pm_verify_clocks()) 66462306a36Sopenharmony_ci goto error; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci at91_pm_suspend(state); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci break; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci case PM_SUSPEND_ON: 67162306a36Sopenharmony_ci cpu_do_idle(); 67262306a36Sopenharmony_ci break; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci default: 67562306a36Sopenharmony_ci pr_debug("AT91: PM - bogus suspend state %d\n", state); 67662306a36Sopenharmony_ci goto error; 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_cierror: 68062306a36Sopenharmony_ci at91_pm_config_quirks(false); 68162306a36Sopenharmony_ci return 0; 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci/* 68562306a36Sopenharmony_ci * Called right prior to thawing processes. 68662306a36Sopenharmony_ci */ 68762306a36Sopenharmony_cistatic void at91_pm_end(void) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci at91_pm_config_ws(soc_pm.data.mode, false); 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_cistatic const struct platform_suspend_ops at91_pm_ops = { 69462306a36Sopenharmony_ci .valid = at91_pm_valid_state, 69562306a36Sopenharmony_ci .begin = at91_pm_begin, 69662306a36Sopenharmony_ci .enter = at91_pm_enter, 69762306a36Sopenharmony_ci .end = at91_pm_end, 69862306a36Sopenharmony_ci}; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_cistatic struct platform_device at91_cpuidle_device = { 70162306a36Sopenharmony_ci .name = "cpuidle-at91", 70262306a36Sopenharmony_ci}; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci/* 70562306a36Sopenharmony_ci * The AT91RM9200 goes into self-refresh mode with this command, and will 70662306a36Sopenharmony_ci * terminate self-refresh automatically on the next SDRAM access. 70762306a36Sopenharmony_ci * 70862306a36Sopenharmony_ci * Self-refresh mode is exited as soon as a memory access is made, but we don't 70962306a36Sopenharmony_ci * know for sure when that happens. However, we need to restore the low-power 71062306a36Sopenharmony_ci * mode if it was enabled before going idle. Restoring low-power mode while 71162306a36Sopenharmony_ci * still in self-refresh is "not recommended", but seems to work. 71262306a36Sopenharmony_ci */ 71362306a36Sopenharmony_cistatic void at91rm9200_standby(void) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci asm volatile( 71662306a36Sopenharmony_ci "b 1f\n\t" 71762306a36Sopenharmony_ci ".align 5\n\t" 71862306a36Sopenharmony_ci "1: mcr p15, 0, %0, c7, c10, 4\n\t" 71962306a36Sopenharmony_ci " str %2, [%1, %3]\n\t" 72062306a36Sopenharmony_ci " mcr p15, 0, %0, c7, c0, 4\n\t" 72162306a36Sopenharmony_ci : 72262306a36Sopenharmony_ci : "r" (0), "r" (soc_pm.data.ramc[0]), 72362306a36Sopenharmony_ci "r" (1), "r" (AT91_MC_SDRAMC_SRR)); 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci/* We manage both DDRAM/SDRAM controllers, we need more than one value to 72762306a36Sopenharmony_ci * remember. 72862306a36Sopenharmony_ci */ 72962306a36Sopenharmony_cistatic void at91_ddr_standby(void) 73062306a36Sopenharmony_ci{ 73162306a36Sopenharmony_ci /* Those two values allow us to delay self-refresh activation 73262306a36Sopenharmony_ci * to the maximum. */ 73362306a36Sopenharmony_ci u32 lpr0, lpr1 = 0; 73462306a36Sopenharmony_ci u32 mdr, saved_mdr0, saved_mdr1 = 0; 73562306a36Sopenharmony_ci u32 saved_lpr0, saved_lpr1 = 0; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci /* LPDDR1 --> force DDR2 mode during self-refresh */ 73862306a36Sopenharmony_ci saved_mdr0 = at91_ramc_read(0, AT91_DDRSDRC_MDR); 73962306a36Sopenharmony_ci if ((saved_mdr0 & AT91_DDRSDRC_MD) == AT91_DDRSDRC_MD_LOW_POWER_DDR) { 74062306a36Sopenharmony_ci mdr = saved_mdr0 & ~AT91_DDRSDRC_MD; 74162306a36Sopenharmony_ci mdr |= AT91_DDRSDRC_MD_DDR2; 74262306a36Sopenharmony_ci at91_ramc_write(0, AT91_DDRSDRC_MDR, mdr); 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci if (soc_pm.data.ramc[1]) { 74662306a36Sopenharmony_ci saved_lpr1 = at91_ramc_read(1, AT91_DDRSDRC_LPR); 74762306a36Sopenharmony_ci lpr1 = saved_lpr1 & ~AT91_DDRSDRC_LPCB; 74862306a36Sopenharmony_ci lpr1 |= AT91_DDRSDRC_LPCB_SELF_REFRESH; 74962306a36Sopenharmony_ci saved_mdr1 = at91_ramc_read(1, AT91_DDRSDRC_MDR); 75062306a36Sopenharmony_ci if ((saved_mdr1 & AT91_DDRSDRC_MD) == AT91_DDRSDRC_MD_LOW_POWER_DDR) { 75162306a36Sopenharmony_ci mdr = saved_mdr1 & ~AT91_DDRSDRC_MD; 75262306a36Sopenharmony_ci mdr |= AT91_DDRSDRC_MD_DDR2; 75362306a36Sopenharmony_ci at91_ramc_write(1, AT91_DDRSDRC_MDR, mdr); 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci saved_lpr0 = at91_ramc_read(0, AT91_DDRSDRC_LPR); 75862306a36Sopenharmony_ci lpr0 = saved_lpr0 & ~AT91_DDRSDRC_LPCB; 75962306a36Sopenharmony_ci lpr0 |= AT91_DDRSDRC_LPCB_SELF_REFRESH; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci /* self-refresh mode now */ 76262306a36Sopenharmony_ci at91_ramc_write(0, AT91_DDRSDRC_LPR, lpr0); 76362306a36Sopenharmony_ci if (soc_pm.data.ramc[1]) 76462306a36Sopenharmony_ci at91_ramc_write(1, AT91_DDRSDRC_LPR, lpr1); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci cpu_do_idle(); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci at91_ramc_write(0, AT91_DDRSDRC_MDR, saved_mdr0); 76962306a36Sopenharmony_ci at91_ramc_write(0, AT91_DDRSDRC_LPR, saved_lpr0); 77062306a36Sopenharmony_ci if (soc_pm.data.ramc[1]) { 77162306a36Sopenharmony_ci at91_ramc_write(0, AT91_DDRSDRC_MDR, saved_mdr1); 77262306a36Sopenharmony_ci at91_ramc_write(1, AT91_DDRSDRC_LPR, saved_lpr1); 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cistatic void sama5d3_ddr_standby(void) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci u32 lpr0; 77962306a36Sopenharmony_ci u32 saved_lpr0; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci saved_lpr0 = at91_ramc_read(0, AT91_DDRSDRC_LPR); 78262306a36Sopenharmony_ci lpr0 = saved_lpr0 & ~AT91_DDRSDRC_LPCB; 78362306a36Sopenharmony_ci lpr0 |= AT91_DDRSDRC_LPCB_POWER_DOWN; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci at91_ramc_write(0, AT91_DDRSDRC_LPR, lpr0); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci cpu_do_idle(); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci at91_ramc_write(0, AT91_DDRSDRC_LPR, saved_lpr0); 79062306a36Sopenharmony_ci} 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci/* We manage both DDRAM/SDRAM controllers, we need more than one value to 79362306a36Sopenharmony_ci * remember. 79462306a36Sopenharmony_ci */ 79562306a36Sopenharmony_cistatic void at91sam9_sdram_standby(void) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci u32 lpr0, lpr1 = 0; 79862306a36Sopenharmony_ci u32 saved_lpr0, saved_lpr1 = 0; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci if (soc_pm.data.ramc[1]) { 80162306a36Sopenharmony_ci saved_lpr1 = at91_ramc_read(1, AT91_SDRAMC_LPR); 80262306a36Sopenharmony_ci lpr1 = saved_lpr1 & ~AT91_SDRAMC_LPCB; 80362306a36Sopenharmony_ci lpr1 |= AT91_SDRAMC_LPCB_SELF_REFRESH; 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci saved_lpr0 = at91_ramc_read(0, AT91_SDRAMC_LPR); 80762306a36Sopenharmony_ci lpr0 = saved_lpr0 & ~AT91_SDRAMC_LPCB; 80862306a36Sopenharmony_ci lpr0 |= AT91_SDRAMC_LPCB_SELF_REFRESH; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci /* self-refresh mode now */ 81162306a36Sopenharmony_ci at91_ramc_write(0, AT91_SDRAMC_LPR, lpr0); 81262306a36Sopenharmony_ci if (soc_pm.data.ramc[1]) 81362306a36Sopenharmony_ci at91_ramc_write(1, AT91_SDRAMC_LPR, lpr1); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci cpu_do_idle(); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci at91_ramc_write(0, AT91_SDRAMC_LPR, saved_lpr0); 81862306a36Sopenharmony_ci if (soc_pm.data.ramc[1]) 81962306a36Sopenharmony_ci at91_ramc_write(1, AT91_SDRAMC_LPR, saved_lpr1); 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_cistatic void sama7g5_standby(void) 82362306a36Sopenharmony_ci{ 82462306a36Sopenharmony_ci int pwrtmg, ratio; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci pwrtmg = readl(soc_pm.data.ramc[0] + UDDRC_PWRCTL); 82762306a36Sopenharmony_ci ratio = readl(soc_pm.data.pmc + AT91_PMC_RATIO); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci /* 83062306a36Sopenharmony_ci * Place RAM into self-refresh after a maximum idle clocks. The maximum 83162306a36Sopenharmony_ci * idle clocks is configured by bootloader in 83262306a36Sopenharmony_ci * UDDRC_PWRMGT.SELFREF_TO_X32. 83362306a36Sopenharmony_ci */ 83462306a36Sopenharmony_ci writel(pwrtmg | UDDRC_PWRCTL_SELFREF_EN, 83562306a36Sopenharmony_ci soc_pm.data.ramc[0] + UDDRC_PWRCTL); 83662306a36Sopenharmony_ci /* Divide CPU clock by 16. */ 83762306a36Sopenharmony_ci writel(ratio & ~AT91_PMC_RATIO_RATIO, soc_pm.data.pmc + AT91_PMC_RATIO); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci cpu_do_idle(); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci /* Restore previous configuration. */ 84262306a36Sopenharmony_ci writel(ratio, soc_pm.data.pmc + AT91_PMC_RATIO); 84362306a36Sopenharmony_ci writel(pwrtmg, soc_pm.data.ramc[0] + UDDRC_PWRCTL); 84462306a36Sopenharmony_ci} 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_cistruct ramc_info { 84762306a36Sopenharmony_ci void (*idle)(void); 84862306a36Sopenharmony_ci unsigned int memctrl; 84962306a36Sopenharmony_ci}; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_cistatic const struct ramc_info ramc_infos[] __initconst = { 85262306a36Sopenharmony_ci { .idle = at91rm9200_standby, .memctrl = AT91_MEMCTRL_MC}, 85362306a36Sopenharmony_ci { .idle = at91sam9_sdram_standby, .memctrl = AT91_MEMCTRL_SDRAMC}, 85462306a36Sopenharmony_ci { .idle = at91_ddr_standby, .memctrl = AT91_MEMCTRL_DDRSDR}, 85562306a36Sopenharmony_ci { .idle = sama5d3_ddr_standby, .memctrl = AT91_MEMCTRL_DDRSDR}, 85662306a36Sopenharmony_ci { .idle = sama7g5_standby, }, 85762306a36Sopenharmony_ci}; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_cistatic const struct of_device_id ramc_ids[] __initconst = { 86062306a36Sopenharmony_ci { .compatible = "atmel,at91rm9200-sdramc", .data = &ramc_infos[0] }, 86162306a36Sopenharmony_ci { .compatible = "atmel,at91sam9260-sdramc", .data = &ramc_infos[1] }, 86262306a36Sopenharmony_ci { .compatible = "atmel,at91sam9g45-ddramc", .data = &ramc_infos[2] }, 86362306a36Sopenharmony_ci { .compatible = "atmel,sama5d3-ddramc", .data = &ramc_infos[3] }, 86462306a36Sopenharmony_ci { .compatible = "microchip,sama7g5-uddrc", .data = &ramc_infos[4], }, 86562306a36Sopenharmony_ci { /*sentinel*/ } 86662306a36Sopenharmony_ci}; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_cistatic const struct of_device_id ramc_phy_ids[] __initconst = { 86962306a36Sopenharmony_ci { .compatible = "microchip,sama7g5-ddr3phy", }, 87062306a36Sopenharmony_ci { /* Sentinel. */ }, 87162306a36Sopenharmony_ci}; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_cistatic __init int at91_dt_ramc(bool phy_mandatory) 87462306a36Sopenharmony_ci{ 87562306a36Sopenharmony_ci struct device_node *np; 87662306a36Sopenharmony_ci const struct of_device_id *of_id; 87762306a36Sopenharmony_ci int idx = 0; 87862306a36Sopenharmony_ci void *standby = NULL; 87962306a36Sopenharmony_ci const struct ramc_info *ramc; 88062306a36Sopenharmony_ci int ret; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci for_each_matching_node_and_match(np, ramc_ids, &of_id) { 88362306a36Sopenharmony_ci soc_pm.data.ramc[idx] = of_iomap(np, 0); 88462306a36Sopenharmony_ci if (!soc_pm.data.ramc[idx]) { 88562306a36Sopenharmony_ci pr_err("unable to map ramc[%d] cpu registers\n", idx); 88662306a36Sopenharmony_ci ret = -ENOMEM; 88762306a36Sopenharmony_ci of_node_put(np); 88862306a36Sopenharmony_ci goto unmap_ramc; 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci ramc = of_id->data; 89262306a36Sopenharmony_ci if (ramc) { 89362306a36Sopenharmony_ci if (!standby) 89462306a36Sopenharmony_ci standby = ramc->idle; 89562306a36Sopenharmony_ci soc_pm.data.memctrl = ramc->memctrl; 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci idx++; 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci if (!idx) { 90262306a36Sopenharmony_ci pr_err("unable to find compatible ram controller node in dtb\n"); 90362306a36Sopenharmony_ci ret = -ENODEV; 90462306a36Sopenharmony_ci goto unmap_ramc; 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci /* Lookup for DDR PHY node, if any. */ 90862306a36Sopenharmony_ci for_each_matching_node_and_match(np, ramc_phy_ids, &of_id) { 90962306a36Sopenharmony_ci soc_pm.data.ramc_phy = of_iomap(np, 0); 91062306a36Sopenharmony_ci if (!soc_pm.data.ramc_phy) { 91162306a36Sopenharmony_ci pr_err("unable to map ramc phy cpu registers\n"); 91262306a36Sopenharmony_ci ret = -ENOMEM; 91362306a36Sopenharmony_ci of_node_put(np); 91462306a36Sopenharmony_ci goto unmap_ramc; 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if (phy_mandatory && !soc_pm.data.ramc_phy) { 91962306a36Sopenharmony_ci pr_err("DDR PHY is mandatory!\n"); 92062306a36Sopenharmony_ci ret = -ENODEV; 92162306a36Sopenharmony_ci goto unmap_ramc; 92262306a36Sopenharmony_ci } 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci if (!standby) { 92562306a36Sopenharmony_ci pr_warn("ramc no standby function available\n"); 92662306a36Sopenharmony_ci return 0; 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci at91_cpuidle_device.dev.platform_data = standby; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci return 0; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ciunmap_ramc: 93462306a36Sopenharmony_ci while (idx) 93562306a36Sopenharmony_ci iounmap(soc_pm.data.ramc[--idx]); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci return ret; 93862306a36Sopenharmony_ci} 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_cistatic void at91rm9200_idle(void) 94162306a36Sopenharmony_ci{ 94262306a36Sopenharmony_ci /* 94362306a36Sopenharmony_ci * Disable the processor clock. The processor will be automatically 94462306a36Sopenharmony_ci * re-enabled by an interrupt or by a reset. 94562306a36Sopenharmony_ci */ 94662306a36Sopenharmony_ci writel(AT91_PMC_PCK, soc_pm.data.pmc + AT91_PMC_SCDR); 94762306a36Sopenharmony_ci} 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_cistatic void at91sam9_idle(void) 95062306a36Sopenharmony_ci{ 95162306a36Sopenharmony_ci writel(AT91_PMC_PCK, soc_pm.data.pmc + AT91_PMC_SCDR); 95262306a36Sopenharmony_ci cpu_do_idle(); 95362306a36Sopenharmony_ci} 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_cistatic void __init at91_pm_sram_init(void) 95662306a36Sopenharmony_ci{ 95762306a36Sopenharmony_ci struct gen_pool *sram_pool; 95862306a36Sopenharmony_ci phys_addr_t sram_pbase; 95962306a36Sopenharmony_ci unsigned long sram_base; 96062306a36Sopenharmony_ci struct device_node *node; 96162306a36Sopenharmony_ci struct platform_device *pdev = NULL; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci for_each_compatible_node(node, NULL, "mmio-sram") { 96462306a36Sopenharmony_ci pdev = of_find_device_by_node(node); 96562306a36Sopenharmony_ci if (pdev) { 96662306a36Sopenharmony_ci of_node_put(node); 96762306a36Sopenharmony_ci break; 96862306a36Sopenharmony_ci } 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci if (!pdev) { 97262306a36Sopenharmony_ci pr_warn("%s: failed to find sram device!\n", __func__); 97362306a36Sopenharmony_ci return; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci sram_pool = gen_pool_get(&pdev->dev, NULL); 97762306a36Sopenharmony_ci if (!sram_pool) { 97862306a36Sopenharmony_ci pr_warn("%s: sram pool unavailable!\n", __func__); 97962306a36Sopenharmony_ci goto out_put_device; 98062306a36Sopenharmony_ci } 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci sram_base = gen_pool_alloc(sram_pool, at91_pm_suspend_in_sram_sz); 98362306a36Sopenharmony_ci if (!sram_base) { 98462306a36Sopenharmony_ci pr_warn("%s: unable to alloc sram!\n", __func__); 98562306a36Sopenharmony_ci goto out_put_device; 98662306a36Sopenharmony_ci } 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci sram_pbase = gen_pool_virt_to_phys(sram_pool, sram_base); 98962306a36Sopenharmony_ci at91_suspend_sram_fn = __arm_ioremap_exec(sram_pbase, 99062306a36Sopenharmony_ci at91_pm_suspend_in_sram_sz, false); 99162306a36Sopenharmony_ci if (!at91_suspend_sram_fn) { 99262306a36Sopenharmony_ci pr_warn("SRAM: Could not map\n"); 99362306a36Sopenharmony_ci goto out_put_device; 99462306a36Sopenharmony_ci } 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci /* Copy the pm suspend handler to SRAM */ 99762306a36Sopenharmony_ci at91_suspend_sram_fn = fncpy(at91_suspend_sram_fn, 99862306a36Sopenharmony_ci &at91_pm_suspend_in_sram, at91_pm_suspend_in_sram_sz); 99962306a36Sopenharmony_ci return; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ciout_put_device: 100262306a36Sopenharmony_ci put_device(&pdev->dev); 100362306a36Sopenharmony_ci return; 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cistatic bool __init at91_is_pm_mode_active(int pm_mode) 100762306a36Sopenharmony_ci{ 100862306a36Sopenharmony_ci return (soc_pm.data.standby_mode == pm_mode || 100962306a36Sopenharmony_ci soc_pm.data.suspend_mode == pm_mode); 101062306a36Sopenharmony_ci} 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_cistatic int __init at91_pm_backup_scan_memcs(unsigned long node, 101362306a36Sopenharmony_ci const char *uname, int depth, 101462306a36Sopenharmony_ci void *data) 101562306a36Sopenharmony_ci{ 101662306a36Sopenharmony_ci const char *type; 101762306a36Sopenharmony_ci const __be32 *reg; 101862306a36Sopenharmony_ci int *located = data; 101962306a36Sopenharmony_ci int size; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci /* Memory node already located. */ 102262306a36Sopenharmony_ci if (*located) 102362306a36Sopenharmony_ci return 0; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci type = of_get_flat_dt_prop(node, "device_type", NULL); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci /* We are scanning "memory" nodes only. */ 102862306a36Sopenharmony_ci if (!type || strcmp(type, "memory")) 102962306a36Sopenharmony_ci return 0; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci reg = of_get_flat_dt_prop(node, "reg", &size); 103262306a36Sopenharmony_ci if (reg) { 103362306a36Sopenharmony_ci soc_pm.memcs = __va((phys_addr_t)be32_to_cpu(*reg)); 103462306a36Sopenharmony_ci *located = 1; 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci return 0; 103862306a36Sopenharmony_ci} 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_cistatic int __init at91_pm_backup_init(void) 104162306a36Sopenharmony_ci{ 104262306a36Sopenharmony_ci struct gen_pool *sram_pool; 104362306a36Sopenharmony_ci struct device_node *np; 104462306a36Sopenharmony_ci struct platform_device *pdev; 104562306a36Sopenharmony_ci int ret = -ENODEV, located = 0; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_SOC_SAMA5D2) && 104862306a36Sopenharmony_ci !IS_ENABLED(CONFIG_SOC_SAMA7G5)) 104962306a36Sopenharmony_ci return -EPERM; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci if (!at91_is_pm_mode_active(AT91_PM_BACKUP)) 105262306a36Sopenharmony_ci return 0; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-securam"); 105562306a36Sopenharmony_ci if (!np) 105662306a36Sopenharmony_ci return ret; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci pdev = of_find_device_by_node(np); 105962306a36Sopenharmony_ci of_node_put(np); 106062306a36Sopenharmony_ci if (!pdev) { 106162306a36Sopenharmony_ci pr_warn("%s: failed to find securam device!\n", __func__); 106262306a36Sopenharmony_ci return ret; 106362306a36Sopenharmony_ci } 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci sram_pool = gen_pool_get(&pdev->dev, NULL); 106662306a36Sopenharmony_ci if (!sram_pool) { 106762306a36Sopenharmony_ci pr_warn("%s: securam pool unavailable!\n", __func__); 106862306a36Sopenharmony_ci goto securam_fail; 106962306a36Sopenharmony_ci } 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci soc_pm.bu = (void *)gen_pool_alloc(sram_pool, sizeof(struct at91_pm_bu)); 107262306a36Sopenharmony_ci if (!soc_pm.bu) { 107362306a36Sopenharmony_ci pr_warn("%s: unable to alloc securam!\n", __func__); 107462306a36Sopenharmony_ci ret = -ENOMEM; 107562306a36Sopenharmony_ci goto securam_fail; 107662306a36Sopenharmony_ci } 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci soc_pm.bu->suspended = 0; 107962306a36Sopenharmony_ci soc_pm.bu->canary = __pa_symbol(&canary); 108062306a36Sopenharmony_ci soc_pm.bu->resume = __pa_symbol(cpu_resume); 108162306a36Sopenharmony_ci if (soc_pm.data.ramc_phy) { 108262306a36Sopenharmony_ci of_scan_flat_dt(at91_pm_backup_scan_memcs, &located); 108362306a36Sopenharmony_ci if (!located) 108462306a36Sopenharmony_ci goto securam_fail; 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci return 0; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_cisecuram_fail: 109062306a36Sopenharmony_ci put_device(&pdev->dev); 109162306a36Sopenharmony_ci return ret; 109262306a36Sopenharmony_ci} 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_cistatic void __init at91_pm_secure_init(void) 109562306a36Sopenharmony_ci{ 109662306a36Sopenharmony_ci int suspend_mode; 109762306a36Sopenharmony_ci struct arm_smccc_res res; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci suspend_mode = soc_pm.data.suspend_mode; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci res = sam_smccc_call(SAMA5_SMC_SIP_SET_SUSPEND_MODE, 110262306a36Sopenharmony_ci suspend_mode, 0); 110362306a36Sopenharmony_ci if (res.a0 == 0) { 110462306a36Sopenharmony_ci pr_info("AT91: Secure PM: suspend mode set to %s\n", 110562306a36Sopenharmony_ci pm_modes[suspend_mode].pattern); 110662306a36Sopenharmony_ci return; 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci pr_warn("AT91: Secure PM: %s mode not supported !\n", 111062306a36Sopenharmony_ci pm_modes[suspend_mode].pattern); 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci res = sam_smccc_call(SAMA5_SMC_SIP_GET_SUSPEND_MODE, 0, 0); 111362306a36Sopenharmony_ci if (res.a0 == 0) { 111462306a36Sopenharmony_ci pr_warn("AT91: Secure PM: failed to get default mode\n"); 111562306a36Sopenharmony_ci return; 111662306a36Sopenharmony_ci } 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci pr_info("AT91: Secure PM: using default suspend mode %s\n", 111962306a36Sopenharmony_ci pm_modes[suspend_mode].pattern); 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci soc_pm.data.suspend_mode = res.a1; 112262306a36Sopenharmony_ci} 112362306a36Sopenharmony_cistatic const struct of_device_id atmel_shdwc_ids[] = { 112462306a36Sopenharmony_ci { .compatible = "atmel,sama5d2-shdwc" }, 112562306a36Sopenharmony_ci { .compatible = "microchip,sam9x60-shdwc" }, 112662306a36Sopenharmony_ci { .compatible = "microchip,sama7g5-shdwc" }, 112762306a36Sopenharmony_ci { /* sentinel. */ } 112862306a36Sopenharmony_ci}; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_cistatic const struct of_device_id gmac_ids[] __initconst = { 113162306a36Sopenharmony_ci { .compatible = "atmel,sama5d3-gem" }, 113262306a36Sopenharmony_ci { .compatible = "atmel,sama5d2-gem" }, 113362306a36Sopenharmony_ci { .compatible = "atmel,sama5d29-gem" }, 113462306a36Sopenharmony_ci { .compatible = "microchip,sama7g5-gem" }, 113562306a36Sopenharmony_ci { }, 113662306a36Sopenharmony_ci}; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_cistatic const struct of_device_id emac_ids[] __initconst = { 113962306a36Sopenharmony_ci { .compatible = "atmel,sama5d3-macb" }, 114062306a36Sopenharmony_ci { .compatible = "microchip,sama7g5-emac" }, 114162306a36Sopenharmony_ci { }, 114262306a36Sopenharmony_ci}; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci/* 114562306a36Sopenharmony_ci * Replaces _mode_to_replace with a supported mode that doesn't depend 114662306a36Sopenharmony_ci * on controller pointed by _map_bitmask 114762306a36Sopenharmony_ci * @_maps: u32 array containing AT91_PM_IOMAP() flags and indexed by AT91 114862306a36Sopenharmony_ci * PM mode 114962306a36Sopenharmony_ci * @_map_bitmask: AT91_PM_IOMAP() bitmask; if _mode_to_replace depends on 115062306a36Sopenharmony_ci * controller represented by _map_bitmask, _mode_to_replace needs to be 115162306a36Sopenharmony_ci * updated 115262306a36Sopenharmony_ci * @_mode_to_replace: standby_mode or suspend_mode that need to be 115362306a36Sopenharmony_ci * updated 115462306a36Sopenharmony_ci * @_mode_to_check: standby_mode or suspend_mode; this is needed here 115562306a36Sopenharmony_ci * to avoid having standby_mode and suspend_mode set with the same AT91 115662306a36Sopenharmony_ci * PM mode 115762306a36Sopenharmony_ci */ 115862306a36Sopenharmony_ci#define AT91_PM_REPLACE_MODE(_maps, _map_bitmask, _mode_to_replace, \ 115962306a36Sopenharmony_ci _mode_to_check) \ 116062306a36Sopenharmony_ci do { \ 116162306a36Sopenharmony_ci if (((_maps)[(_mode_to_replace)]) & (_map_bitmask)) { \ 116262306a36Sopenharmony_ci int _mode_to_use, _mode_complementary; \ 116362306a36Sopenharmony_ci /* Use ULP0 if it doesn't need _map_bitmask. */ \ 116462306a36Sopenharmony_ci if (!((_maps)[AT91_PM_ULP0] & (_map_bitmask))) {\ 116562306a36Sopenharmony_ci _mode_to_use = AT91_PM_ULP0; \ 116662306a36Sopenharmony_ci _mode_complementary = AT91_PM_STANDBY; \ 116762306a36Sopenharmony_ci } else { \ 116862306a36Sopenharmony_ci _mode_to_use = AT91_PM_STANDBY; \ 116962306a36Sopenharmony_ci _mode_complementary = AT91_PM_STANDBY; \ 117062306a36Sopenharmony_ci } \ 117162306a36Sopenharmony_ci \ 117262306a36Sopenharmony_ci if ((_mode_to_check) != _mode_to_use) \ 117362306a36Sopenharmony_ci (_mode_to_replace) = _mode_to_use; \ 117462306a36Sopenharmony_ci else \ 117562306a36Sopenharmony_ci (_mode_to_replace) = _mode_complementary;\ 117662306a36Sopenharmony_ci } \ 117762306a36Sopenharmony_ci } while (0) 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci/* 118062306a36Sopenharmony_ci * Replaces standby and suspend modes with default supported modes: 118162306a36Sopenharmony_ci * ULP0 and STANDBY. 118262306a36Sopenharmony_ci * @_maps: u32 array indexed by AT91 PM mode containing AT91_PM_IOMAP() 118362306a36Sopenharmony_ci * flags 118462306a36Sopenharmony_ci * @_map: controller specific name; standby and suspend mode need to be 118562306a36Sopenharmony_ci * replaced in order to not depend on this controller 118662306a36Sopenharmony_ci */ 118762306a36Sopenharmony_ci#define AT91_PM_REPLACE_MODES(_maps, _map) \ 118862306a36Sopenharmony_ci do { \ 118962306a36Sopenharmony_ci AT91_PM_REPLACE_MODE((_maps), BIT(AT91_PM_IOMAP_##_map),\ 119062306a36Sopenharmony_ci (soc_pm.data.standby_mode), \ 119162306a36Sopenharmony_ci (soc_pm.data.suspend_mode)); \ 119262306a36Sopenharmony_ci AT91_PM_REPLACE_MODE((_maps), BIT(AT91_PM_IOMAP_##_map),\ 119362306a36Sopenharmony_ci (soc_pm.data.suspend_mode), \ 119462306a36Sopenharmony_ci (soc_pm.data.standby_mode)); \ 119562306a36Sopenharmony_ci } while (0) 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_cistatic int __init at91_pm_get_eth_clks(struct device_node *np, 119862306a36Sopenharmony_ci struct clk_bulk_data *clks) 119962306a36Sopenharmony_ci{ 120062306a36Sopenharmony_ci clks[AT91_PM_ETH_PCLK].clk = of_clk_get_by_name(np, "pclk"); 120162306a36Sopenharmony_ci if (IS_ERR(clks[AT91_PM_ETH_PCLK].clk)) 120262306a36Sopenharmony_ci return PTR_ERR(clks[AT91_PM_ETH_PCLK].clk); 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci clks[AT91_PM_ETH_HCLK].clk = of_clk_get_by_name(np, "hclk"); 120562306a36Sopenharmony_ci if (IS_ERR(clks[AT91_PM_ETH_HCLK].clk)) 120662306a36Sopenharmony_ci return PTR_ERR(clks[AT91_PM_ETH_HCLK].clk); 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci return 0; 120962306a36Sopenharmony_ci} 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_cistatic int __init at91_pm_eth_clks_empty(struct clk_bulk_data *clks) 121262306a36Sopenharmony_ci{ 121362306a36Sopenharmony_ci return IS_ERR(clks[AT91_PM_ETH_PCLK].clk) || 121462306a36Sopenharmony_ci IS_ERR(clks[AT91_PM_ETH_HCLK].clk); 121562306a36Sopenharmony_ci} 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_cistatic void __init at91_pm_modes_init(const u32 *maps, int len) 121862306a36Sopenharmony_ci{ 121962306a36Sopenharmony_ci struct at91_pm_quirk_eth *gmac = &soc_pm.quirks.eth[AT91_PM_G_ETH]; 122062306a36Sopenharmony_ci struct at91_pm_quirk_eth *emac = &soc_pm.quirks.eth[AT91_PM_E_ETH]; 122162306a36Sopenharmony_ci struct device_node *np; 122262306a36Sopenharmony_ci int ret; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci ret = at91_pm_backup_init(); 122562306a36Sopenharmony_ci if (ret) { 122662306a36Sopenharmony_ci if (soc_pm.data.standby_mode == AT91_PM_BACKUP) 122762306a36Sopenharmony_ci soc_pm.data.standby_mode = AT91_PM_ULP0; 122862306a36Sopenharmony_ci if (soc_pm.data.suspend_mode == AT91_PM_BACKUP) 122962306a36Sopenharmony_ci soc_pm.data.suspend_mode = AT91_PM_ULP0; 123062306a36Sopenharmony_ci } 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci if (maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(SHDWC) || 123362306a36Sopenharmony_ci maps[soc_pm.data.suspend_mode] & AT91_PM_IOMAP(SHDWC)) { 123462306a36Sopenharmony_ci np = of_find_matching_node(NULL, atmel_shdwc_ids); 123562306a36Sopenharmony_ci if (!np) { 123662306a36Sopenharmony_ci pr_warn("%s: failed to find shdwc!\n", __func__); 123762306a36Sopenharmony_ci AT91_PM_REPLACE_MODES(maps, SHDWC); 123862306a36Sopenharmony_ci } else { 123962306a36Sopenharmony_ci soc_pm.data.shdwc = of_iomap(np, 0); 124062306a36Sopenharmony_ci of_node_put(np); 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci } 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci if (maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(SFRBU) || 124562306a36Sopenharmony_ci maps[soc_pm.data.suspend_mode] & AT91_PM_IOMAP(SFRBU)) { 124662306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-sfrbu"); 124762306a36Sopenharmony_ci if (!np) { 124862306a36Sopenharmony_ci pr_warn("%s: failed to find sfrbu!\n", __func__); 124962306a36Sopenharmony_ci AT91_PM_REPLACE_MODES(maps, SFRBU); 125062306a36Sopenharmony_ci } else { 125162306a36Sopenharmony_ci soc_pm.data.sfrbu = of_iomap(np, 0); 125262306a36Sopenharmony_ci of_node_put(np); 125362306a36Sopenharmony_ci } 125462306a36Sopenharmony_ci } 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci if ((at91_is_pm_mode_active(AT91_PM_ULP1) || 125762306a36Sopenharmony_ci at91_is_pm_mode_active(AT91_PM_ULP0) || 125862306a36Sopenharmony_ci at91_is_pm_mode_active(AT91_PM_ULP0_FAST)) && 125962306a36Sopenharmony_ci (maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(ETHC) || 126062306a36Sopenharmony_ci maps[soc_pm.data.suspend_mode] & AT91_PM_IOMAP(ETHC))) { 126162306a36Sopenharmony_ci np = of_find_matching_node(NULL, gmac_ids); 126262306a36Sopenharmony_ci if (!np) { 126362306a36Sopenharmony_ci np = of_find_matching_node(NULL, emac_ids); 126462306a36Sopenharmony_ci if (np) 126562306a36Sopenharmony_ci goto get_emac_clks; 126662306a36Sopenharmony_ci AT91_PM_REPLACE_MODES(maps, ETHC); 126762306a36Sopenharmony_ci goto unmap_unused_nodes; 126862306a36Sopenharmony_ci } else { 126962306a36Sopenharmony_ci gmac->np = np; 127062306a36Sopenharmony_ci at91_pm_get_eth_clks(np, gmac->clks); 127162306a36Sopenharmony_ci } 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci np = of_find_matching_node(NULL, emac_ids); 127462306a36Sopenharmony_ci if (!np) { 127562306a36Sopenharmony_ci if (at91_pm_eth_clks_empty(gmac->clks)) 127662306a36Sopenharmony_ci AT91_PM_REPLACE_MODES(maps, ETHC); 127762306a36Sopenharmony_ci } else { 127862306a36Sopenharmony_ciget_emac_clks: 127962306a36Sopenharmony_ci emac->np = np; 128062306a36Sopenharmony_ci ret = at91_pm_get_eth_clks(np, emac->clks); 128162306a36Sopenharmony_ci if (ret && at91_pm_eth_clks_empty(gmac->clks)) { 128262306a36Sopenharmony_ci of_node_put(gmac->np); 128362306a36Sopenharmony_ci of_node_put(emac->np); 128462306a36Sopenharmony_ci gmac->np = NULL; 128562306a36Sopenharmony_ci emac->np = NULL; 128662306a36Sopenharmony_ci } 128762306a36Sopenharmony_ci } 128862306a36Sopenharmony_ci } 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ciunmap_unused_nodes: 129162306a36Sopenharmony_ci /* Unmap all unnecessary. */ 129262306a36Sopenharmony_ci if (soc_pm.data.shdwc && 129362306a36Sopenharmony_ci !(maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(SHDWC) || 129462306a36Sopenharmony_ci maps[soc_pm.data.suspend_mode] & AT91_PM_IOMAP(SHDWC))) { 129562306a36Sopenharmony_ci iounmap(soc_pm.data.shdwc); 129662306a36Sopenharmony_ci soc_pm.data.shdwc = NULL; 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci if (soc_pm.data.sfrbu && 130062306a36Sopenharmony_ci !(maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(SFRBU) || 130162306a36Sopenharmony_ci maps[soc_pm.data.suspend_mode] & AT91_PM_IOMAP(SFRBU))) { 130262306a36Sopenharmony_ci iounmap(soc_pm.data.sfrbu); 130362306a36Sopenharmony_ci soc_pm.data.sfrbu = NULL; 130462306a36Sopenharmony_ci } 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci return; 130762306a36Sopenharmony_ci} 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_cistruct pmc_info { 131062306a36Sopenharmony_ci unsigned long uhp_udp_mask; 131162306a36Sopenharmony_ci unsigned long mckr; 131262306a36Sopenharmony_ci unsigned long version; 131362306a36Sopenharmony_ci}; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_cistatic const struct pmc_info pmc_infos[] __initconst = { 131662306a36Sopenharmony_ci { 131762306a36Sopenharmony_ci .uhp_udp_mask = AT91RM9200_PMC_UHP | AT91RM9200_PMC_UDP, 131862306a36Sopenharmony_ci .mckr = 0x30, 131962306a36Sopenharmony_ci .version = AT91_PMC_V1, 132062306a36Sopenharmony_ci }, 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci { 132362306a36Sopenharmony_ci .uhp_udp_mask = AT91SAM926x_PMC_UHP | AT91SAM926x_PMC_UDP, 132462306a36Sopenharmony_ci .mckr = 0x30, 132562306a36Sopenharmony_ci .version = AT91_PMC_V1, 132662306a36Sopenharmony_ci }, 132762306a36Sopenharmony_ci { 132862306a36Sopenharmony_ci .uhp_udp_mask = AT91SAM926x_PMC_UHP, 132962306a36Sopenharmony_ci .mckr = 0x30, 133062306a36Sopenharmony_ci .version = AT91_PMC_V1, 133162306a36Sopenharmony_ci }, 133262306a36Sopenharmony_ci { .uhp_udp_mask = 0, 133362306a36Sopenharmony_ci .mckr = 0x30, 133462306a36Sopenharmony_ci .version = AT91_PMC_V1, 133562306a36Sopenharmony_ci }, 133662306a36Sopenharmony_ci { 133762306a36Sopenharmony_ci .uhp_udp_mask = AT91SAM926x_PMC_UHP | AT91SAM926x_PMC_UDP, 133862306a36Sopenharmony_ci .mckr = 0x28, 133962306a36Sopenharmony_ci .version = AT91_PMC_V2, 134062306a36Sopenharmony_ci }, 134162306a36Sopenharmony_ci { 134262306a36Sopenharmony_ci .mckr = 0x28, 134362306a36Sopenharmony_ci .version = AT91_PMC_V2, 134462306a36Sopenharmony_ci }, 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci}; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_cistatic const struct of_device_id atmel_pmc_ids[] __initconst = { 134962306a36Sopenharmony_ci { .compatible = "atmel,at91rm9200-pmc", .data = &pmc_infos[0] }, 135062306a36Sopenharmony_ci { .compatible = "atmel,at91sam9260-pmc", .data = &pmc_infos[1] }, 135162306a36Sopenharmony_ci { .compatible = "atmel,at91sam9261-pmc", .data = &pmc_infos[1] }, 135262306a36Sopenharmony_ci { .compatible = "atmel,at91sam9263-pmc", .data = &pmc_infos[1] }, 135362306a36Sopenharmony_ci { .compatible = "atmel,at91sam9g45-pmc", .data = &pmc_infos[2] }, 135462306a36Sopenharmony_ci { .compatible = "atmel,at91sam9n12-pmc", .data = &pmc_infos[1] }, 135562306a36Sopenharmony_ci { .compatible = "atmel,at91sam9rl-pmc", .data = &pmc_infos[3] }, 135662306a36Sopenharmony_ci { .compatible = "atmel,at91sam9x5-pmc", .data = &pmc_infos[1] }, 135762306a36Sopenharmony_ci { .compatible = "atmel,sama5d3-pmc", .data = &pmc_infos[1] }, 135862306a36Sopenharmony_ci { .compatible = "atmel,sama5d4-pmc", .data = &pmc_infos[1] }, 135962306a36Sopenharmony_ci { .compatible = "atmel,sama5d2-pmc", .data = &pmc_infos[1] }, 136062306a36Sopenharmony_ci { .compatible = "microchip,sam9x60-pmc", .data = &pmc_infos[4] }, 136162306a36Sopenharmony_ci { .compatible = "microchip,sama7g5-pmc", .data = &pmc_infos[5] }, 136262306a36Sopenharmony_ci { /* sentinel */ }, 136362306a36Sopenharmony_ci}; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_cistatic void __init at91_pm_modes_validate(const int *modes, int len) 136662306a36Sopenharmony_ci{ 136762306a36Sopenharmony_ci u8 i, standby = 0, suspend = 0; 136862306a36Sopenharmony_ci int mode; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci for (i = 0; i < len; i++) { 137162306a36Sopenharmony_ci if (standby && suspend) 137262306a36Sopenharmony_ci break; 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci if (modes[i] == soc_pm.data.standby_mode && !standby) { 137562306a36Sopenharmony_ci standby = 1; 137662306a36Sopenharmony_ci continue; 137762306a36Sopenharmony_ci } 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci if (modes[i] == soc_pm.data.suspend_mode && !suspend) { 138062306a36Sopenharmony_ci suspend = 1; 138162306a36Sopenharmony_ci continue; 138262306a36Sopenharmony_ci } 138362306a36Sopenharmony_ci } 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci if (!standby) { 138662306a36Sopenharmony_ci if (soc_pm.data.suspend_mode == AT91_PM_STANDBY) 138762306a36Sopenharmony_ci mode = AT91_PM_ULP0; 138862306a36Sopenharmony_ci else 138962306a36Sopenharmony_ci mode = AT91_PM_STANDBY; 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci pr_warn("AT91: PM: %s mode not supported! Using %s.\n", 139262306a36Sopenharmony_ci pm_modes[soc_pm.data.standby_mode].pattern, 139362306a36Sopenharmony_ci pm_modes[mode].pattern); 139462306a36Sopenharmony_ci soc_pm.data.standby_mode = mode; 139562306a36Sopenharmony_ci } 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci if (!suspend) { 139862306a36Sopenharmony_ci if (soc_pm.data.standby_mode == AT91_PM_ULP0) 139962306a36Sopenharmony_ci mode = AT91_PM_STANDBY; 140062306a36Sopenharmony_ci else 140162306a36Sopenharmony_ci mode = AT91_PM_ULP0; 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci pr_warn("AT91: PM: %s mode not supported! Using %s.\n", 140462306a36Sopenharmony_ci pm_modes[soc_pm.data.suspend_mode].pattern, 140562306a36Sopenharmony_ci pm_modes[mode].pattern); 140662306a36Sopenharmony_ci soc_pm.data.suspend_mode = mode; 140762306a36Sopenharmony_ci } 140862306a36Sopenharmony_ci} 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_cistatic void __init at91_pm_init(void (*pm_idle)(void)) 141162306a36Sopenharmony_ci{ 141262306a36Sopenharmony_ci struct device_node *pmc_np; 141362306a36Sopenharmony_ci const struct of_device_id *of_id; 141462306a36Sopenharmony_ci const struct pmc_info *pmc; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci if (at91_cpuidle_device.dev.platform_data) 141762306a36Sopenharmony_ci platform_device_register(&at91_cpuidle_device); 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci pmc_np = of_find_matching_node_and_match(NULL, atmel_pmc_ids, &of_id); 142062306a36Sopenharmony_ci soc_pm.data.pmc = of_iomap(pmc_np, 0); 142162306a36Sopenharmony_ci of_node_put(pmc_np); 142262306a36Sopenharmony_ci if (!soc_pm.data.pmc) { 142362306a36Sopenharmony_ci pr_err("AT91: PM not supported, PMC not found\n"); 142462306a36Sopenharmony_ci return; 142562306a36Sopenharmony_ci } 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci pmc = of_id->data; 142862306a36Sopenharmony_ci soc_pm.data.uhp_udp_mask = pmc->uhp_udp_mask; 142962306a36Sopenharmony_ci soc_pm.data.pmc_mckr_offset = pmc->mckr; 143062306a36Sopenharmony_ci soc_pm.data.pmc_version = pmc->version; 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci if (pm_idle) 143362306a36Sopenharmony_ci arm_pm_idle = pm_idle; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci at91_pm_sram_init(); 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci if (at91_suspend_sram_fn) { 143862306a36Sopenharmony_ci suspend_set_ops(&at91_pm_ops); 143962306a36Sopenharmony_ci pr_info("AT91: PM: standby: %s, suspend: %s\n", 144062306a36Sopenharmony_ci pm_modes[soc_pm.data.standby_mode].pattern, 144162306a36Sopenharmony_ci pm_modes[soc_pm.data.suspend_mode].pattern); 144262306a36Sopenharmony_ci } else { 144362306a36Sopenharmony_ci pr_info("AT91: PM not supported, due to no SRAM allocated\n"); 144462306a36Sopenharmony_ci } 144562306a36Sopenharmony_ci} 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_civoid __init at91rm9200_pm_init(void) 144862306a36Sopenharmony_ci{ 144962306a36Sopenharmony_ci int ret; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_SOC_AT91RM9200)) 145262306a36Sopenharmony_ci return; 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci /* 145562306a36Sopenharmony_ci * Force STANDBY and ULP0 mode to avoid calling 145662306a36Sopenharmony_ci * at91_pm_modes_validate() which may increase booting time. 145762306a36Sopenharmony_ci * Platform supports anyway only STANDBY and ULP0 modes. 145862306a36Sopenharmony_ci */ 145962306a36Sopenharmony_ci soc_pm.data.standby_mode = AT91_PM_STANDBY; 146062306a36Sopenharmony_ci soc_pm.data.suspend_mode = AT91_PM_ULP0; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci ret = at91_dt_ramc(false); 146362306a36Sopenharmony_ci if (ret) 146462306a36Sopenharmony_ci return; 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci /* 146762306a36Sopenharmony_ci * AT91RM9200 SDRAM low-power mode cannot be used with self-refresh. 146862306a36Sopenharmony_ci */ 146962306a36Sopenharmony_ci at91_ramc_write(0, AT91_MC_SDRAMC_LPR, 0); 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci at91_pm_init(at91rm9200_idle); 147262306a36Sopenharmony_ci} 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_civoid __init sam9x60_pm_init(void) 147562306a36Sopenharmony_ci{ 147662306a36Sopenharmony_ci static const int modes[] __initconst = { 147762306a36Sopenharmony_ci AT91_PM_STANDBY, AT91_PM_ULP0, AT91_PM_ULP0_FAST, AT91_PM_ULP1, 147862306a36Sopenharmony_ci }; 147962306a36Sopenharmony_ci static const int iomaps[] __initconst = { 148062306a36Sopenharmony_ci [AT91_PM_ULP1] = AT91_PM_IOMAP(SHDWC), 148162306a36Sopenharmony_ci }; 148262306a36Sopenharmony_ci int ret; 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_SOC_SAM9X60)) 148562306a36Sopenharmony_ci return; 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci at91_pm_modes_validate(modes, ARRAY_SIZE(modes)); 148862306a36Sopenharmony_ci at91_pm_modes_init(iomaps, ARRAY_SIZE(iomaps)); 148962306a36Sopenharmony_ci ret = at91_dt_ramc(false); 149062306a36Sopenharmony_ci if (ret) 149162306a36Sopenharmony_ci return; 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci at91_pm_init(NULL); 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci soc_pm.ws_ids = sam9x60_ws_ids; 149662306a36Sopenharmony_ci soc_pm.config_pmc_ws = at91_sam9x60_config_pmc_ws; 149762306a36Sopenharmony_ci} 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_civoid __init at91sam9_pm_init(void) 150062306a36Sopenharmony_ci{ 150162306a36Sopenharmony_ci int ret; 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_SOC_AT91SAM9)) 150462306a36Sopenharmony_ci return; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci /* 150762306a36Sopenharmony_ci * Force STANDBY and ULP0 mode to avoid calling 150862306a36Sopenharmony_ci * at91_pm_modes_validate() which may increase booting time. 150962306a36Sopenharmony_ci * Platform supports anyway only STANDBY and ULP0 modes. 151062306a36Sopenharmony_ci */ 151162306a36Sopenharmony_ci soc_pm.data.standby_mode = AT91_PM_STANDBY; 151262306a36Sopenharmony_ci soc_pm.data.suspend_mode = AT91_PM_ULP0; 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci ret = at91_dt_ramc(false); 151562306a36Sopenharmony_ci if (ret) 151662306a36Sopenharmony_ci return; 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci at91_pm_init(at91sam9_idle); 151962306a36Sopenharmony_ci} 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_civoid __init sama5_pm_init(void) 152262306a36Sopenharmony_ci{ 152362306a36Sopenharmony_ci static const int modes[] __initconst = { 152462306a36Sopenharmony_ci AT91_PM_STANDBY, AT91_PM_ULP0, AT91_PM_ULP0_FAST, 152562306a36Sopenharmony_ci }; 152662306a36Sopenharmony_ci static const u32 iomaps[] __initconst = { 152762306a36Sopenharmony_ci [AT91_PM_ULP0] = AT91_PM_IOMAP(ETHC), 152862306a36Sopenharmony_ci [AT91_PM_ULP0_FAST] = AT91_PM_IOMAP(ETHC), 152962306a36Sopenharmony_ci }; 153062306a36Sopenharmony_ci int ret; 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_SOC_SAMA5)) 153362306a36Sopenharmony_ci return; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci at91_pm_modes_validate(modes, ARRAY_SIZE(modes)); 153662306a36Sopenharmony_ci at91_pm_modes_init(iomaps, ARRAY_SIZE(iomaps)); 153762306a36Sopenharmony_ci ret = at91_dt_ramc(false); 153862306a36Sopenharmony_ci if (ret) 153962306a36Sopenharmony_ci return; 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci at91_pm_init(NULL); 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci /* Quirks applies to ULP0, ULP0 fast and ULP1 modes. */ 154462306a36Sopenharmony_ci soc_pm.quirks.eth[AT91_PM_G_ETH].modes = BIT(AT91_PM_ULP0) | 154562306a36Sopenharmony_ci BIT(AT91_PM_ULP0_FAST) | 154662306a36Sopenharmony_ci BIT(AT91_PM_ULP1); 154762306a36Sopenharmony_ci /* Do not suspend in ULP0, ULP0 fast if GETH is the only wakeup source. */ 154862306a36Sopenharmony_ci soc_pm.quirks.eth[AT91_PM_G_ETH].dns_modes = BIT(AT91_PM_ULP0) | 154962306a36Sopenharmony_ci BIT(AT91_PM_ULP0_FAST); 155062306a36Sopenharmony_ci} 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_civoid __init sama5d2_pm_init(void) 155362306a36Sopenharmony_ci{ 155462306a36Sopenharmony_ci static const int modes[] __initconst = { 155562306a36Sopenharmony_ci AT91_PM_STANDBY, AT91_PM_ULP0, AT91_PM_ULP0_FAST, AT91_PM_ULP1, 155662306a36Sopenharmony_ci AT91_PM_BACKUP, 155762306a36Sopenharmony_ci }; 155862306a36Sopenharmony_ci static const u32 iomaps[] __initconst = { 155962306a36Sopenharmony_ci [AT91_PM_ULP0] = AT91_PM_IOMAP(ETHC), 156062306a36Sopenharmony_ci [AT91_PM_ULP0_FAST] = AT91_PM_IOMAP(ETHC), 156162306a36Sopenharmony_ci [AT91_PM_ULP1] = AT91_PM_IOMAP(SHDWC) | 156262306a36Sopenharmony_ci AT91_PM_IOMAP(ETHC), 156362306a36Sopenharmony_ci [AT91_PM_BACKUP] = AT91_PM_IOMAP(SHDWC) | 156462306a36Sopenharmony_ci AT91_PM_IOMAP(SFRBU), 156562306a36Sopenharmony_ci }; 156662306a36Sopenharmony_ci int ret; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_SOC_SAMA5D2)) 156962306a36Sopenharmony_ci return; 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_ATMEL_SECURE_PM)) { 157262306a36Sopenharmony_ci pr_warn("AT91: Secure PM: ignoring standby mode\n"); 157362306a36Sopenharmony_ci at91_pm_secure_init(); 157462306a36Sopenharmony_ci return; 157562306a36Sopenharmony_ci } 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci at91_pm_modes_validate(modes, ARRAY_SIZE(modes)); 157862306a36Sopenharmony_ci at91_pm_modes_init(iomaps, ARRAY_SIZE(iomaps)); 157962306a36Sopenharmony_ci ret = at91_dt_ramc(false); 158062306a36Sopenharmony_ci if (ret) 158162306a36Sopenharmony_ci return; 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci at91_pm_init(NULL); 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci soc_pm.ws_ids = sama5d2_ws_ids; 158662306a36Sopenharmony_ci soc_pm.config_shdwc_ws = at91_sama5d2_config_shdwc_ws; 158762306a36Sopenharmony_ci soc_pm.config_pmc_ws = at91_sama5d2_config_pmc_ws; 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci soc_pm.sfrbu_regs.pswbu.key = (0x4BD20C << 8); 159062306a36Sopenharmony_ci soc_pm.sfrbu_regs.pswbu.ctrl = BIT(0); 159162306a36Sopenharmony_ci soc_pm.sfrbu_regs.pswbu.softsw = BIT(1); 159262306a36Sopenharmony_ci soc_pm.sfrbu_regs.pswbu.state = BIT(3); 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci /* Quirk applies to ULP0, ULP0 fast and ULP1 modes. */ 159562306a36Sopenharmony_ci soc_pm.quirks.eth[AT91_PM_G_ETH].modes = BIT(AT91_PM_ULP0) | 159662306a36Sopenharmony_ci BIT(AT91_PM_ULP0_FAST) | 159762306a36Sopenharmony_ci BIT(AT91_PM_ULP1); 159862306a36Sopenharmony_ci /* 159962306a36Sopenharmony_ci * Do not suspend in ULP0, ULP0 fast if GETH is the only wakeup 160062306a36Sopenharmony_ci * source. 160162306a36Sopenharmony_ci */ 160262306a36Sopenharmony_ci soc_pm.quirks.eth[AT91_PM_G_ETH].dns_modes = BIT(AT91_PM_ULP0) | 160362306a36Sopenharmony_ci BIT(AT91_PM_ULP0_FAST); 160462306a36Sopenharmony_ci} 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_civoid __init sama7_pm_init(void) 160762306a36Sopenharmony_ci{ 160862306a36Sopenharmony_ci static const int modes[] __initconst = { 160962306a36Sopenharmony_ci AT91_PM_STANDBY, AT91_PM_ULP0, AT91_PM_ULP1, AT91_PM_BACKUP, 161062306a36Sopenharmony_ci }; 161162306a36Sopenharmony_ci static const u32 iomaps[] __initconst = { 161262306a36Sopenharmony_ci [AT91_PM_ULP0] = AT91_PM_IOMAP(SFRBU), 161362306a36Sopenharmony_ci [AT91_PM_ULP1] = AT91_PM_IOMAP(SFRBU) | 161462306a36Sopenharmony_ci AT91_PM_IOMAP(SHDWC) | 161562306a36Sopenharmony_ci AT91_PM_IOMAP(ETHC), 161662306a36Sopenharmony_ci [AT91_PM_BACKUP] = AT91_PM_IOMAP(SFRBU) | 161762306a36Sopenharmony_ci AT91_PM_IOMAP(SHDWC), 161862306a36Sopenharmony_ci }; 161962306a36Sopenharmony_ci int ret; 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_SOC_SAMA7)) 162262306a36Sopenharmony_ci return; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci at91_pm_modes_validate(modes, ARRAY_SIZE(modes)); 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci ret = at91_dt_ramc(true); 162762306a36Sopenharmony_ci if (ret) 162862306a36Sopenharmony_ci return; 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci at91_pm_modes_init(iomaps, ARRAY_SIZE(iomaps)); 163162306a36Sopenharmony_ci at91_pm_init(NULL); 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci soc_pm.ws_ids = sama7g5_ws_ids; 163462306a36Sopenharmony_ci soc_pm.config_pmc_ws = at91_sam9x60_config_pmc_ws; 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci soc_pm.sfrbu_regs.pswbu.key = (0x4BD20C << 8); 163762306a36Sopenharmony_ci soc_pm.sfrbu_regs.pswbu.ctrl = BIT(0); 163862306a36Sopenharmony_ci soc_pm.sfrbu_regs.pswbu.softsw = BIT(1); 163962306a36Sopenharmony_ci soc_pm.sfrbu_regs.pswbu.state = BIT(2); 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci /* Quirks applies to ULP1 for both Ethernet interfaces. */ 164262306a36Sopenharmony_ci soc_pm.quirks.eth[AT91_PM_E_ETH].modes = BIT(AT91_PM_ULP1); 164362306a36Sopenharmony_ci soc_pm.quirks.eth[AT91_PM_G_ETH].modes = BIT(AT91_PM_ULP1); 164462306a36Sopenharmony_ci} 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_cistatic int __init at91_pm_modes_select(char *str) 164762306a36Sopenharmony_ci{ 164862306a36Sopenharmony_ci char *s; 164962306a36Sopenharmony_ci substring_t args[MAX_OPT_ARGS]; 165062306a36Sopenharmony_ci int standby, suspend; 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci if (!str) 165362306a36Sopenharmony_ci return 0; 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci s = strsep(&str, ","); 165662306a36Sopenharmony_ci standby = match_token(s, pm_modes, args); 165762306a36Sopenharmony_ci if (standby < 0) 165862306a36Sopenharmony_ci return 0; 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci suspend = match_token(str, pm_modes, args); 166162306a36Sopenharmony_ci if (suspend < 0) 166262306a36Sopenharmony_ci return 0; 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci soc_pm.data.standby_mode = standby; 166562306a36Sopenharmony_ci soc_pm.data.suspend_mode = suspend; 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci return 0; 166862306a36Sopenharmony_ci} 166962306a36Sopenharmony_ciearly_param("atmel.pm_modes", at91_pm_modes_select); 1670