--- zzzz-none-000/linux-3.10.107/arch/arm/mach-imx/pm-imx5.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/arch/arm/mach-imx/pm-imx5.c 2021-02-04 17:41:59.000000000 +0000 @@ -13,15 +13,39 @@ #include #include #include + +#include +#include +#include +#include + #include +#include #include #include #include "common.h" #include "cpuidle.h" -#include "crm-regs-imx5.h" #include "hardware.h" +#define MXC_CCM_CLPCR 0x54 +#define MXC_CCM_CLPCR_LPM_OFFSET 0 +#define MXC_CCM_CLPCR_LPM_MASK 0x3 +#define MXC_CCM_CLPCR_STBY_COUNT_OFFSET 9 +#define MXC_CCM_CLPCR_VSTBY (0x1 << 8) +#define MXC_CCM_CLPCR_SBYOS (0x1 << 6) + +#define MXC_CORTEXA8_PLAT_LPC 0xc +#define MXC_CORTEXA8_PLAT_LPC_DSM (1 << 0) +#define MXC_CORTEXA8_PLAT_LPC_DBG_DSM (1 << 1) + +#define MXC_SRPG_NEON_SRPGCR 0x280 +#define MXC_SRPG_ARM_SRPGCR 0x2a0 +#define MXC_SRPG_EMPGC0_SRPGCR 0x2c0 +#define MXC_SRPG_EMPGC1_SRPGCR 0x2d0 + +#define MXC_SRPGCR_PCR 1 + /* * The WAIT_UNCLOCKED_POWER_OFF state only requires <= 500ns to exit. * This is also the lowest power state possible without affecting @@ -32,6 +56,92 @@ */ #define IMX5_DEFAULT_CPU_IDLE_STATE WAIT_UNCLOCKED_POWER_OFF +struct imx5_suspend_io_state { + u32 offset; + u32 clear; + u32 set; + u32 saved_value; +}; + +struct imx5_pm_data { + phys_addr_t ccm_addr; + phys_addr_t cortex_addr; + phys_addr_t gpc_addr; + phys_addr_t m4if_addr; + phys_addr_t iomuxc_addr; + void (*suspend_asm)(void __iomem *ocram_vbase); + const u32 *suspend_asm_sz; + const struct imx5_suspend_io_state *suspend_io_config; + int suspend_io_count; +}; + +static const struct imx5_suspend_io_state imx53_suspend_io_config[] = { +#define MX53_DSE_HIGHZ_MASK (0x7 << 19) + {.offset = 0x584, .clear = MX53_DSE_HIGHZ_MASK}, /* DQM0 */ + {.offset = 0x594, .clear = MX53_DSE_HIGHZ_MASK}, /* DQM1 */ + {.offset = 0x560, .clear = MX53_DSE_HIGHZ_MASK}, /* DQM2 */ + {.offset = 0x554, .clear = MX53_DSE_HIGHZ_MASK}, /* DQM3 */ + {.offset = 0x574, .clear = MX53_DSE_HIGHZ_MASK}, /* CAS */ + {.offset = 0x588, .clear = MX53_DSE_HIGHZ_MASK}, /* RAS */ + {.offset = 0x578, .clear = MX53_DSE_HIGHZ_MASK}, /* SDCLK_0 */ + {.offset = 0x570, .clear = MX53_DSE_HIGHZ_MASK}, /* SDCLK_1 */ + + {.offset = 0x580, .clear = MX53_DSE_HIGHZ_MASK}, /* SDODT0 */ + {.offset = 0x564, .clear = MX53_DSE_HIGHZ_MASK}, /* SDODT1 */ + {.offset = 0x57c, .clear = MX53_DSE_HIGHZ_MASK}, /* SDQS0 */ + {.offset = 0x590, .clear = MX53_DSE_HIGHZ_MASK}, /* SDQS1 */ + {.offset = 0x568, .clear = MX53_DSE_HIGHZ_MASK}, /* SDQS2 */ + {.offset = 0x558, .clear = MX53_DSE_HIGHZ_MASK}, /* SDSQ3 */ + {.offset = 0x6f0, .clear = MX53_DSE_HIGHZ_MASK}, /* GRP_ADDS */ + {.offset = 0x718, .clear = MX53_DSE_HIGHZ_MASK}, /* GRP_BODS */ + {.offset = 0x71c, .clear = MX53_DSE_HIGHZ_MASK}, /* GRP_B1DS */ + {.offset = 0x728, .clear = MX53_DSE_HIGHZ_MASK}, /* GRP_B2DS */ + {.offset = 0x72c, .clear = MX53_DSE_HIGHZ_MASK}, /* GRP_B3DS */ + + /* Controls the CKE signal which is required to leave self refresh */ + {.offset = 0x720, .clear = MX53_DSE_HIGHZ_MASK, .set = 1 << 19}, /* CTLDS */ +}; + +static const struct imx5_pm_data imx51_pm_data __initconst = { + .ccm_addr = 0x73fd4000, + .cortex_addr = 0x83fa0000, + .gpc_addr = 0x73fd8000, +}; + +static const struct imx5_pm_data imx53_pm_data __initconst = { + .ccm_addr = 0x53fd4000, + .cortex_addr = 0x63fa0000, + .gpc_addr = 0x53fd8000, + .m4if_addr = 0x63fd8000, + .iomuxc_addr = 0x53fa8000, + .suspend_asm = &imx53_suspend, + .suspend_asm_sz = &imx53_suspend_sz, + .suspend_io_config = imx53_suspend_io_config, + .suspend_io_count = ARRAY_SIZE(imx53_suspend_io_config), +}; + +#define MX5_MAX_SUSPEND_IOSTATE ARRAY_SIZE(imx53_suspend_io_config) + +/* + * This structure is for passing necessary data for low level ocram + * suspend code(arch/arm/mach-imx/suspend-imx53.S), if this struct + * definition is changed, the offset definition in that file + * must be also changed accordingly otherwise, the suspend to ocram + * function will be broken! + */ +struct imx5_cpu_suspend_info { + void __iomem *m4if_base; + void __iomem *iomuxc_base; + u32 io_count; + struct imx5_suspend_io_state io_state[MX5_MAX_SUSPEND_IOSTATE]; +} __aligned(8); + +static void __iomem *ccm_base; +static void __iomem *cortex_base; +static void __iomem *gpc_base; +static void __iomem *suspend_ocram_base; +static void (*imx5_suspend_in_ocram_fn)(void __iomem *ocram_vbase); + /* * set cpu low power mode before WFI instruction. This function is called * mx5 because it can be used for mx51, and mx53. @@ -43,12 +153,16 @@ int stop_mode = 0; /* always allow platform to issue a deep sleep mode request */ - plat_lpc = __raw_readl(MXC_CORTEXA8_PLAT_LPC) & + plat_lpc = __raw_readl(cortex_base + MXC_CORTEXA8_PLAT_LPC) & ~(MXC_CORTEXA8_PLAT_LPC_DSM); - ccm_clpcr = __raw_readl(MXC_CCM_CLPCR) & ~(MXC_CCM_CLPCR_LPM_MASK); - arm_srpgcr = __raw_readl(MXC_SRPG_ARM_SRPGCR) & ~(MXC_SRPGCR_PCR); - empgc0 = __raw_readl(MXC_SRPG_EMPGC0_SRPGCR) & ~(MXC_SRPGCR_PCR); - empgc1 = __raw_readl(MXC_SRPG_EMPGC1_SRPGCR) & ~(MXC_SRPGCR_PCR); + ccm_clpcr = __raw_readl(ccm_base + MXC_CCM_CLPCR) & + ~(MXC_CCM_CLPCR_LPM_MASK); + arm_srpgcr = __raw_readl(gpc_base + MXC_SRPG_ARM_SRPGCR) & + ~(MXC_SRPGCR_PCR); + empgc0 = __raw_readl(gpc_base + MXC_SRPG_EMPGC0_SRPGCR) & + ~(MXC_SRPGCR_PCR); + empgc1 = __raw_readl(gpc_base + MXC_SRPG_EMPGC1_SRPGCR) & + ~(MXC_SRPGCR_PCR); switch (mode) { case WAIT_CLOCKED: @@ -82,17 +196,17 @@ return; } - __raw_writel(plat_lpc, MXC_CORTEXA8_PLAT_LPC); - __raw_writel(ccm_clpcr, MXC_CCM_CLPCR); - __raw_writel(arm_srpgcr, MXC_SRPG_ARM_SRPGCR); - __raw_writel(arm_srpgcr, MXC_SRPG_NEON_SRPGCR); + __raw_writel(plat_lpc, cortex_base + MXC_CORTEXA8_PLAT_LPC); + __raw_writel(ccm_clpcr, ccm_base + MXC_CCM_CLPCR); + __raw_writel(arm_srpgcr, gpc_base + MXC_SRPG_ARM_SRPGCR); + __raw_writel(arm_srpgcr, gpc_base + MXC_SRPG_NEON_SRPGCR); if (stop_mode) { empgc0 |= MXC_SRPGCR_PCR; empgc1 |= MXC_SRPGCR_PCR; - __raw_writel(empgc0, MXC_SRPG_EMPGC0_SRPGCR); - __raw_writel(empgc1, MXC_SRPG_EMPGC1_SRPGCR); + __raw_writel(empgc0, gpc_base + MXC_SRPG_EMPGC0_SRPGCR); + __raw_writel(empgc1, gpc_base + MXC_SRPG_EMPGC1_SRPGCR); } } @@ -114,10 +228,17 @@ flush_cache_all(); /*clear the EMPGC0/1 bits */ - __raw_writel(0, MXC_SRPG_EMPGC0_SRPGCR); - __raw_writel(0, MXC_SRPG_EMPGC1_SRPGCR); + __raw_writel(0, gpc_base + MXC_SRPG_EMPGC0_SRPGCR); + __raw_writel(0, gpc_base + MXC_SRPG_EMPGC1_SRPGCR); + + if (imx5_suspend_in_ocram_fn) + imx5_suspend_in_ocram_fn(suspend_ocram_base); + else + cpu_do_idle(); + + } else { + cpu_do_idle(); } - cpu_do_idle(); /* return registers to default idle state */ mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE); @@ -149,7 +270,112 @@ imx5_cpu_do_idle(); } -static int __init imx5_pm_common_init(void) +static int __init imx_suspend_alloc_ocram( + size_t size, + void __iomem **virt_out, + phys_addr_t *phys_out) +{ + struct device_node *node; + struct platform_device *pdev; + struct gen_pool *ocram_pool; + unsigned long ocram_base; + void __iomem *virt; + phys_addr_t phys; + int ret = 0; + + /* Copied from imx6: TODO factorize */ + node = of_find_compatible_node(NULL, NULL, "mmio-sram"); + if (!node) { + pr_warn("%s: failed to find ocram node!\n", __func__); + return -ENODEV; + } + + pdev = of_find_device_by_node(node); + if (!pdev) { + pr_warn("%s: failed to find ocram device!\n", __func__); + ret = -ENODEV; + goto put_node; + } + + ocram_pool = gen_pool_get(&pdev->dev, NULL); + if (!ocram_pool) { + pr_warn("%s: ocram pool unavailable!\n", __func__); + ret = -ENODEV; + goto put_node; + } + + ocram_base = gen_pool_alloc(ocram_pool, size); + if (!ocram_base) { + pr_warn("%s: unable to alloc ocram!\n", __func__); + ret = -ENOMEM; + goto put_node; + } + + phys = gen_pool_virt_to_phys(ocram_pool, ocram_base); + virt = __arm_ioremap_exec(phys, size, false); + if (phys_out) + *phys_out = phys; + if (virt_out) + *virt_out = virt; + +put_node: + of_node_put(node); + + return ret; +} + +static int __init imx5_suspend_init(const struct imx5_pm_data *soc_data) +{ + struct imx5_cpu_suspend_info *suspend_info; + int ret; + /* Need this to avoid compile error due to const typeof in fncpy.h */ + void (*suspend_asm)(void __iomem *) = soc_data->suspend_asm; + + if (!suspend_asm) + return 0; + + if (!soc_data->suspend_asm_sz || !*soc_data->suspend_asm_sz) + return -EINVAL; + + ret = imx_suspend_alloc_ocram( + *soc_data->suspend_asm_sz + sizeof(*suspend_info), + &suspend_ocram_base, NULL); + if (ret) + return ret; + + suspend_info = suspend_ocram_base; + + suspend_info->io_count = soc_data->suspend_io_count; + memcpy(suspend_info->io_state, soc_data->suspend_io_config, + sizeof(*suspend_info->io_state) * soc_data->suspend_io_count); + + suspend_info->m4if_base = ioremap(soc_data->m4if_addr, SZ_16K); + if (!suspend_info->m4if_base) { + ret = -ENOMEM; + goto failed_map_m4if; + } + + suspend_info->iomuxc_base = ioremap(soc_data->iomuxc_addr, SZ_16K); + if (!suspend_info->iomuxc_base) { + ret = -ENOMEM; + goto failed_map_iomuxc; + } + + imx5_suspend_in_ocram_fn = fncpy( + suspend_ocram_base + sizeof(*suspend_info), + suspend_asm, + *soc_data->suspend_asm_sz); + + return 0; + +failed_map_iomuxc: + iounmap(suspend_info->m4if_base); + +failed_map_m4if: + return ret; +} + +static int __init imx5_pm_common_init(const struct imx5_pm_data *data) { int ret; struct clk *gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs"); @@ -163,20 +389,36 @@ arm_pm_idle = imx5_pm_idle; + ccm_base = ioremap(data->ccm_addr, SZ_16K); + cortex_base = ioremap(data->cortex_addr, SZ_16K); + gpc_base = ioremap(data->gpc_addr, SZ_16K); + WARN_ON(!ccm_base || !cortex_base || !gpc_base); + /* Set the registers to the default cpu idle state. */ mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE); - return imx5_cpuidle_init(); + ret = imx5_cpuidle_init(); + if (ret) + pr_warn("%s: cpuidle init failed %d\n", __func__, ret); + + ret = imx5_suspend_init(data); + if (ret) + pr_warn("%s: No DDR LPM support with suspend %d!\n", + __func__, ret); + + suspend_set_ops(&mx5_suspend_ops); + + return 0; } void __init imx51_pm_init(void) { - int ret = imx5_pm_common_init(); - if (!ret) - suspend_set_ops(&mx5_suspend_ops); + if (IS_ENABLED(CONFIG_SOC_IMX51)) + imx5_pm_common_init(&imx51_pm_data); } void __init imx53_pm_init(void) { - imx5_pm_common_init(); + if (IS_ENABLED(CONFIG_SOC_IMX53)) + imx5_pm_common_init(&imx53_pm_data); }