162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/suspend.h> 662306a36Sopenharmony_ci#include <linux/clk.h> 762306a36Sopenharmony_ci#include <linux/io.h> 862306a36Sopenharmony_ci#include <linux/err.h> 962306a36Sopenharmony_ci#include <linux/export.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/genalloc.h> 1262306a36Sopenharmony_ci#include <linux/of.h> 1362306a36Sopenharmony_ci#include <linux/of_address.h> 1462306a36Sopenharmony_ci#include <linux/of_platform.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <asm/cacheflush.h> 1862306a36Sopenharmony_ci#include <asm/fncpy.h> 1962306a36Sopenharmony_ci#include <asm/system_misc.h> 2062306a36Sopenharmony_ci#include <asm/tlbflush.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "common.h" 2362306a36Sopenharmony_ci#include "cpuidle.h" 2462306a36Sopenharmony_ci#include "hardware.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define MXC_CCM_CLPCR 0x54 2762306a36Sopenharmony_ci#define MXC_CCM_CLPCR_LPM_OFFSET 0 2862306a36Sopenharmony_ci#define MXC_CCM_CLPCR_LPM_MASK 0x3 2962306a36Sopenharmony_ci#define MXC_CCM_CLPCR_STBY_COUNT_OFFSET 9 3062306a36Sopenharmony_ci#define MXC_CCM_CLPCR_VSTBY (0x1 << 8) 3162306a36Sopenharmony_ci#define MXC_CCM_CLPCR_SBYOS (0x1 << 6) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define MXC_CORTEXA8_PLAT_LPC 0xc 3462306a36Sopenharmony_ci#define MXC_CORTEXA8_PLAT_LPC_DSM (1 << 0) 3562306a36Sopenharmony_ci#define MXC_CORTEXA8_PLAT_LPC_DBG_DSM (1 << 1) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define MXC_SRPG_NEON_SRPGCR 0x280 3862306a36Sopenharmony_ci#define MXC_SRPG_ARM_SRPGCR 0x2a0 3962306a36Sopenharmony_ci#define MXC_SRPG_EMPGC0_SRPGCR 0x2c0 4062306a36Sopenharmony_ci#define MXC_SRPG_EMPGC1_SRPGCR 0x2d0 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define MXC_SRPGCR_PCR 1 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* 4562306a36Sopenharmony_ci * The WAIT_UNCLOCKED_POWER_OFF state only requires <= 500ns to exit. 4662306a36Sopenharmony_ci * This is also the lowest power state possible without affecting 4762306a36Sopenharmony_ci * non-cpu parts of the system. For these reasons, imx5 should default 4862306a36Sopenharmony_ci * to always using this state for cpu idling. The PM_SUSPEND_STANDBY also 4962306a36Sopenharmony_ci * uses this state and needs to take no action when registers remain configured 5062306a36Sopenharmony_ci * for this state. 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_ci#define IMX5_DEFAULT_CPU_IDLE_STATE WAIT_UNCLOCKED_POWER_OFF 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistruct imx5_suspend_io_state { 5562306a36Sopenharmony_ci u32 offset; 5662306a36Sopenharmony_ci u32 clear; 5762306a36Sopenharmony_ci u32 set; 5862306a36Sopenharmony_ci u32 saved_value; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistruct imx5_pm_data { 6262306a36Sopenharmony_ci phys_addr_t ccm_addr; 6362306a36Sopenharmony_ci phys_addr_t cortex_addr; 6462306a36Sopenharmony_ci phys_addr_t gpc_addr; 6562306a36Sopenharmony_ci phys_addr_t m4if_addr; 6662306a36Sopenharmony_ci phys_addr_t iomuxc_addr; 6762306a36Sopenharmony_ci void (*suspend_asm)(void __iomem *ocram_vbase); 6862306a36Sopenharmony_ci const u32 *suspend_asm_sz; 6962306a36Sopenharmony_ci const struct imx5_suspend_io_state *suspend_io_config; 7062306a36Sopenharmony_ci int suspend_io_count; 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic const struct imx5_suspend_io_state imx53_suspend_io_config[] = { 7462306a36Sopenharmony_ci#define MX53_DSE_HIGHZ_MASK (0x7 << 19) 7562306a36Sopenharmony_ci {.offset = 0x584, .clear = MX53_DSE_HIGHZ_MASK}, /* DQM0 */ 7662306a36Sopenharmony_ci {.offset = 0x594, .clear = MX53_DSE_HIGHZ_MASK}, /* DQM1 */ 7762306a36Sopenharmony_ci {.offset = 0x560, .clear = MX53_DSE_HIGHZ_MASK}, /* DQM2 */ 7862306a36Sopenharmony_ci {.offset = 0x554, .clear = MX53_DSE_HIGHZ_MASK}, /* DQM3 */ 7962306a36Sopenharmony_ci {.offset = 0x574, .clear = MX53_DSE_HIGHZ_MASK}, /* CAS */ 8062306a36Sopenharmony_ci {.offset = 0x588, .clear = MX53_DSE_HIGHZ_MASK}, /* RAS */ 8162306a36Sopenharmony_ci {.offset = 0x578, .clear = MX53_DSE_HIGHZ_MASK}, /* SDCLK_0 */ 8262306a36Sopenharmony_ci {.offset = 0x570, .clear = MX53_DSE_HIGHZ_MASK}, /* SDCLK_1 */ 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci {.offset = 0x580, .clear = MX53_DSE_HIGHZ_MASK}, /* SDODT0 */ 8562306a36Sopenharmony_ci {.offset = 0x564, .clear = MX53_DSE_HIGHZ_MASK}, /* SDODT1 */ 8662306a36Sopenharmony_ci {.offset = 0x57c, .clear = MX53_DSE_HIGHZ_MASK}, /* SDQS0 */ 8762306a36Sopenharmony_ci {.offset = 0x590, .clear = MX53_DSE_HIGHZ_MASK}, /* SDQS1 */ 8862306a36Sopenharmony_ci {.offset = 0x568, .clear = MX53_DSE_HIGHZ_MASK}, /* SDQS2 */ 8962306a36Sopenharmony_ci {.offset = 0x558, .clear = MX53_DSE_HIGHZ_MASK}, /* SDSQ3 */ 9062306a36Sopenharmony_ci {.offset = 0x6f0, .clear = MX53_DSE_HIGHZ_MASK}, /* GRP_ADDS */ 9162306a36Sopenharmony_ci {.offset = 0x718, .clear = MX53_DSE_HIGHZ_MASK}, /* GRP_BODS */ 9262306a36Sopenharmony_ci {.offset = 0x71c, .clear = MX53_DSE_HIGHZ_MASK}, /* GRP_B1DS */ 9362306a36Sopenharmony_ci {.offset = 0x728, .clear = MX53_DSE_HIGHZ_MASK}, /* GRP_B2DS */ 9462306a36Sopenharmony_ci {.offset = 0x72c, .clear = MX53_DSE_HIGHZ_MASK}, /* GRP_B3DS */ 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* Controls the CKE signal which is required to leave self refresh */ 9762306a36Sopenharmony_ci {.offset = 0x720, .clear = MX53_DSE_HIGHZ_MASK, .set = 1 << 19}, /* CTLDS */ 9862306a36Sopenharmony_ci}; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic const struct imx5_pm_data imx51_pm_data __initconst = { 10162306a36Sopenharmony_ci .ccm_addr = 0x73fd4000, 10262306a36Sopenharmony_ci .cortex_addr = 0x83fa0000, 10362306a36Sopenharmony_ci .gpc_addr = 0x73fd8000, 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic const struct imx5_pm_data imx53_pm_data __initconst = { 10762306a36Sopenharmony_ci .ccm_addr = 0x53fd4000, 10862306a36Sopenharmony_ci .cortex_addr = 0x63fa0000, 10962306a36Sopenharmony_ci .gpc_addr = 0x53fd8000, 11062306a36Sopenharmony_ci .m4if_addr = 0x63fd8000, 11162306a36Sopenharmony_ci .iomuxc_addr = 0x53fa8000, 11262306a36Sopenharmony_ci .suspend_asm = &imx53_suspend, 11362306a36Sopenharmony_ci .suspend_asm_sz = &imx53_suspend_sz, 11462306a36Sopenharmony_ci .suspend_io_config = imx53_suspend_io_config, 11562306a36Sopenharmony_ci .suspend_io_count = ARRAY_SIZE(imx53_suspend_io_config), 11662306a36Sopenharmony_ci}; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci#define MX5_MAX_SUSPEND_IOSTATE ARRAY_SIZE(imx53_suspend_io_config) 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/* 12162306a36Sopenharmony_ci * This structure is for passing necessary data for low level ocram 12262306a36Sopenharmony_ci * suspend code(arch/arm/mach-imx/suspend-imx53.S), if this struct 12362306a36Sopenharmony_ci * definition is changed, the offset definition in that file 12462306a36Sopenharmony_ci * must be also changed accordingly otherwise, the suspend to ocram 12562306a36Sopenharmony_ci * function will be broken! 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_cistruct imx5_cpu_suspend_info { 12862306a36Sopenharmony_ci void __iomem *m4if_base; 12962306a36Sopenharmony_ci void __iomem *iomuxc_base; 13062306a36Sopenharmony_ci u32 io_count; 13162306a36Sopenharmony_ci struct imx5_suspend_io_state io_state[MX5_MAX_SUSPEND_IOSTATE]; 13262306a36Sopenharmony_ci} __aligned(8); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic void __iomem *ccm_base; 13562306a36Sopenharmony_cistatic void __iomem *cortex_base; 13662306a36Sopenharmony_cistatic void __iomem *gpc_base; 13762306a36Sopenharmony_cistatic void __iomem *suspend_ocram_base; 13862306a36Sopenharmony_cistatic void (*imx5_suspend_in_ocram_fn)(void __iomem *ocram_vbase); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/* 14162306a36Sopenharmony_ci * set cpu low power mode before WFI instruction. This function is called 14262306a36Sopenharmony_ci * mx5 because it can be used for mx51, and mx53. 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_cistatic void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci u32 plat_lpc, arm_srpgcr, ccm_clpcr; 14762306a36Sopenharmony_ci u32 empgc0, empgc1; 14862306a36Sopenharmony_ci int stop_mode = 0; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* always allow platform to issue a deep sleep mode request */ 15162306a36Sopenharmony_ci plat_lpc = imx_readl(cortex_base + MXC_CORTEXA8_PLAT_LPC) & 15262306a36Sopenharmony_ci ~(MXC_CORTEXA8_PLAT_LPC_DSM); 15362306a36Sopenharmony_ci ccm_clpcr = imx_readl(ccm_base + MXC_CCM_CLPCR) & 15462306a36Sopenharmony_ci ~(MXC_CCM_CLPCR_LPM_MASK); 15562306a36Sopenharmony_ci arm_srpgcr = imx_readl(gpc_base + MXC_SRPG_ARM_SRPGCR) & 15662306a36Sopenharmony_ci ~(MXC_SRPGCR_PCR); 15762306a36Sopenharmony_ci empgc0 = imx_readl(gpc_base + MXC_SRPG_EMPGC0_SRPGCR) & 15862306a36Sopenharmony_ci ~(MXC_SRPGCR_PCR); 15962306a36Sopenharmony_ci empgc1 = imx_readl(gpc_base + MXC_SRPG_EMPGC1_SRPGCR) & 16062306a36Sopenharmony_ci ~(MXC_SRPGCR_PCR); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci switch (mode) { 16362306a36Sopenharmony_ci case WAIT_CLOCKED: 16462306a36Sopenharmony_ci break; 16562306a36Sopenharmony_ci case WAIT_UNCLOCKED: 16662306a36Sopenharmony_ci ccm_clpcr |= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET; 16762306a36Sopenharmony_ci break; 16862306a36Sopenharmony_ci case WAIT_UNCLOCKED_POWER_OFF: 16962306a36Sopenharmony_ci case STOP_POWER_OFF: 17062306a36Sopenharmony_ci plat_lpc |= MXC_CORTEXA8_PLAT_LPC_DSM 17162306a36Sopenharmony_ci | MXC_CORTEXA8_PLAT_LPC_DBG_DSM; 17262306a36Sopenharmony_ci if (mode == WAIT_UNCLOCKED_POWER_OFF) { 17362306a36Sopenharmony_ci ccm_clpcr |= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET; 17462306a36Sopenharmony_ci ccm_clpcr &= ~MXC_CCM_CLPCR_VSTBY; 17562306a36Sopenharmony_ci ccm_clpcr &= ~MXC_CCM_CLPCR_SBYOS; 17662306a36Sopenharmony_ci stop_mode = 0; 17762306a36Sopenharmony_ci } else { 17862306a36Sopenharmony_ci ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET; 17962306a36Sopenharmony_ci ccm_clpcr |= 0x3 << MXC_CCM_CLPCR_STBY_COUNT_OFFSET; 18062306a36Sopenharmony_ci ccm_clpcr |= MXC_CCM_CLPCR_VSTBY; 18162306a36Sopenharmony_ci ccm_clpcr |= MXC_CCM_CLPCR_SBYOS; 18262306a36Sopenharmony_ci stop_mode = 1; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci arm_srpgcr |= MXC_SRPGCR_PCR; 18562306a36Sopenharmony_ci break; 18662306a36Sopenharmony_ci case STOP_POWER_ON: 18762306a36Sopenharmony_ci ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET; 18862306a36Sopenharmony_ci break; 18962306a36Sopenharmony_ci default: 19062306a36Sopenharmony_ci printk(KERN_WARNING "UNKNOWN cpu power mode: %d\n", mode); 19162306a36Sopenharmony_ci return; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci imx_writel(plat_lpc, cortex_base + MXC_CORTEXA8_PLAT_LPC); 19562306a36Sopenharmony_ci imx_writel(ccm_clpcr, ccm_base + MXC_CCM_CLPCR); 19662306a36Sopenharmony_ci imx_writel(arm_srpgcr, gpc_base + MXC_SRPG_ARM_SRPGCR); 19762306a36Sopenharmony_ci imx_writel(arm_srpgcr, gpc_base + MXC_SRPG_NEON_SRPGCR); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (stop_mode) { 20062306a36Sopenharmony_ci empgc0 |= MXC_SRPGCR_PCR; 20162306a36Sopenharmony_ci empgc1 |= MXC_SRPGCR_PCR; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci imx_writel(empgc0, gpc_base + MXC_SRPG_EMPGC0_SRPGCR); 20462306a36Sopenharmony_ci imx_writel(empgc1, gpc_base + MXC_SRPG_EMPGC1_SRPGCR); 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic int mx5_suspend_enter(suspend_state_t state) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci switch (state) { 21162306a36Sopenharmony_ci case PM_SUSPEND_MEM: 21262306a36Sopenharmony_ci mx5_cpu_lp_set(STOP_POWER_OFF); 21362306a36Sopenharmony_ci break; 21462306a36Sopenharmony_ci case PM_SUSPEND_STANDBY: 21562306a36Sopenharmony_ci /* DEFAULT_IDLE_STATE already configured */ 21662306a36Sopenharmony_ci break; 21762306a36Sopenharmony_ci default: 21862306a36Sopenharmony_ci return -EINVAL; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (state == PM_SUSPEND_MEM) { 22262306a36Sopenharmony_ci local_flush_tlb_all(); 22362306a36Sopenharmony_ci flush_cache_all(); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /*clear the EMPGC0/1 bits */ 22662306a36Sopenharmony_ci imx_writel(0, gpc_base + MXC_SRPG_EMPGC0_SRPGCR); 22762306a36Sopenharmony_ci imx_writel(0, gpc_base + MXC_SRPG_EMPGC1_SRPGCR); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (imx5_suspend_in_ocram_fn) 23062306a36Sopenharmony_ci imx5_suspend_in_ocram_fn(suspend_ocram_base); 23162306a36Sopenharmony_ci else 23262306a36Sopenharmony_ci cpu_do_idle(); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci } else { 23562306a36Sopenharmony_ci cpu_do_idle(); 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* return registers to default idle state */ 23962306a36Sopenharmony_ci mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE); 24062306a36Sopenharmony_ci return 0; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic int mx5_pm_valid(suspend_state_t state) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci return (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic const struct platform_suspend_ops mx5_suspend_ops = { 24962306a36Sopenharmony_ci .valid = mx5_pm_valid, 25062306a36Sopenharmony_ci .enter = mx5_suspend_enter, 25162306a36Sopenharmony_ci}; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic inline int imx5_cpu_do_idle(void) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci int ret = tzic_enable_wake(); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (likely(!ret)) 25862306a36Sopenharmony_ci cpu_do_idle(); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci return ret; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic void imx5_pm_idle(void) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci imx5_cpu_do_idle(); 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic int __init imx_suspend_alloc_ocram( 26962306a36Sopenharmony_ci size_t size, 27062306a36Sopenharmony_ci void __iomem **virt_out, 27162306a36Sopenharmony_ci phys_addr_t *phys_out) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct device_node *node; 27462306a36Sopenharmony_ci struct platform_device *pdev; 27562306a36Sopenharmony_ci struct gen_pool *ocram_pool; 27662306a36Sopenharmony_ci unsigned long ocram_base; 27762306a36Sopenharmony_ci void __iomem *virt; 27862306a36Sopenharmony_ci phys_addr_t phys; 27962306a36Sopenharmony_ci int ret = 0; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* Copied from imx6: TODO factorize */ 28262306a36Sopenharmony_ci node = of_find_compatible_node(NULL, NULL, "mmio-sram"); 28362306a36Sopenharmony_ci if (!node) { 28462306a36Sopenharmony_ci pr_warn("%s: failed to find ocram node!\n", __func__); 28562306a36Sopenharmony_ci return -ENODEV; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci pdev = of_find_device_by_node(node); 28962306a36Sopenharmony_ci if (!pdev) { 29062306a36Sopenharmony_ci pr_warn("%s: failed to find ocram device!\n", __func__); 29162306a36Sopenharmony_ci ret = -ENODEV; 29262306a36Sopenharmony_ci goto put_node; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci ocram_pool = gen_pool_get(&pdev->dev, NULL); 29662306a36Sopenharmony_ci if (!ocram_pool) { 29762306a36Sopenharmony_ci pr_warn("%s: ocram pool unavailable!\n", __func__); 29862306a36Sopenharmony_ci ret = -ENODEV; 29962306a36Sopenharmony_ci goto put_device; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci ocram_base = gen_pool_alloc(ocram_pool, size); 30362306a36Sopenharmony_ci if (!ocram_base) { 30462306a36Sopenharmony_ci pr_warn("%s: unable to alloc ocram!\n", __func__); 30562306a36Sopenharmony_ci ret = -ENOMEM; 30662306a36Sopenharmony_ci goto put_device; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci phys = gen_pool_virt_to_phys(ocram_pool, ocram_base); 31062306a36Sopenharmony_ci virt = __arm_ioremap_exec(phys, size, false); 31162306a36Sopenharmony_ci if (phys_out) 31262306a36Sopenharmony_ci *phys_out = phys; 31362306a36Sopenharmony_ci if (virt_out) 31462306a36Sopenharmony_ci *virt_out = virt; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ciput_device: 31762306a36Sopenharmony_ci put_device(&pdev->dev); 31862306a36Sopenharmony_ciput_node: 31962306a36Sopenharmony_ci of_node_put(node); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci return ret; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic int __init imx5_suspend_init(const struct imx5_pm_data *soc_data) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci struct imx5_cpu_suspend_info *suspend_info; 32762306a36Sopenharmony_ci int ret; 32862306a36Sopenharmony_ci /* Need this to avoid compile error due to const typeof in fncpy.h */ 32962306a36Sopenharmony_ci void (*suspend_asm)(void __iomem *) = soc_data->suspend_asm; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (!suspend_asm) 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (!soc_data->suspend_asm_sz || !*soc_data->suspend_asm_sz) 33562306a36Sopenharmony_ci return -EINVAL; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci ret = imx_suspend_alloc_ocram( 33862306a36Sopenharmony_ci *soc_data->suspend_asm_sz + sizeof(*suspend_info), 33962306a36Sopenharmony_ci &suspend_ocram_base, NULL); 34062306a36Sopenharmony_ci if (ret) 34162306a36Sopenharmony_ci return ret; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci suspend_info = suspend_ocram_base; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci suspend_info->io_count = soc_data->suspend_io_count; 34662306a36Sopenharmony_ci memcpy(suspend_info->io_state, soc_data->suspend_io_config, 34762306a36Sopenharmony_ci sizeof(*suspend_info->io_state) * soc_data->suspend_io_count); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci suspend_info->m4if_base = ioremap(soc_data->m4if_addr, SZ_16K); 35062306a36Sopenharmony_ci if (!suspend_info->m4if_base) { 35162306a36Sopenharmony_ci ret = -ENOMEM; 35262306a36Sopenharmony_ci goto failed_map_m4if; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci suspend_info->iomuxc_base = ioremap(soc_data->iomuxc_addr, SZ_16K); 35662306a36Sopenharmony_ci if (!suspend_info->iomuxc_base) { 35762306a36Sopenharmony_ci ret = -ENOMEM; 35862306a36Sopenharmony_ci goto failed_map_iomuxc; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci imx5_suspend_in_ocram_fn = fncpy( 36262306a36Sopenharmony_ci suspend_ocram_base + sizeof(*suspend_info), 36362306a36Sopenharmony_ci suspend_asm, 36462306a36Sopenharmony_ci *soc_data->suspend_asm_sz); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci return 0; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cifailed_map_iomuxc: 36962306a36Sopenharmony_ci iounmap(suspend_info->m4if_base); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cifailed_map_m4if: 37262306a36Sopenharmony_ci return ret; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic int __init imx5_pm_common_init(const struct imx5_pm_data *data) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci int ret; 37862306a36Sopenharmony_ci struct clk *gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs"); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (IS_ERR(gpc_dvfs_clk)) 38162306a36Sopenharmony_ci return PTR_ERR(gpc_dvfs_clk); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci ret = clk_prepare_enable(gpc_dvfs_clk); 38462306a36Sopenharmony_ci if (ret) 38562306a36Sopenharmony_ci return ret; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci arm_pm_idle = imx5_pm_idle; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci ccm_base = ioremap(data->ccm_addr, SZ_16K); 39062306a36Sopenharmony_ci cortex_base = ioremap(data->cortex_addr, SZ_16K); 39162306a36Sopenharmony_ci gpc_base = ioremap(data->gpc_addr, SZ_16K); 39262306a36Sopenharmony_ci WARN_ON(!ccm_base || !cortex_base || !gpc_base); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* Set the registers to the default cpu idle state. */ 39562306a36Sopenharmony_ci mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci ret = imx5_cpuidle_init(); 39862306a36Sopenharmony_ci if (ret) 39962306a36Sopenharmony_ci pr_warn("%s: cpuidle init failed %d\n", __func__, ret); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci ret = imx5_suspend_init(data); 40262306a36Sopenharmony_ci if (ret) 40362306a36Sopenharmony_ci pr_warn("%s: No DDR LPM support with suspend %d!\n", 40462306a36Sopenharmony_ci __func__, ret); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci suspend_set_ops(&mx5_suspend_ops); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci return 0; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_civoid __init imx51_pm_init(void) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SOC_IMX51)) 41462306a36Sopenharmony_ci imx5_pm_common_init(&imx51_pm_data); 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_civoid __init imx53_pm_init(void) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SOC_IMX53)) 42062306a36Sopenharmony_ci imx5_pm_common_init(&imx53_pm_data); 42162306a36Sopenharmony_ci} 422