0001
0002
0003
0004
0005 #include <linux/suspend.h>
0006 #include <linux/clk.h>
0007 #include <linux/io.h>
0008 #include <linux/err.h>
0009 #include <linux/export.h>
0010
0011 #include <linux/genalloc.h>
0012 #include <linux/of.h>
0013 #include <linux/of_address.h>
0014 #include <linux/of_platform.h>
0015
0016 #include <asm/cacheflush.h>
0017 #include <asm/fncpy.h>
0018 #include <asm/system_misc.h>
0019 #include <asm/tlbflush.h>
0020
0021 #include "common.h"
0022 #include "cpuidle.h"
0023 #include "hardware.h"
0024
0025 #define MXC_CCM_CLPCR 0x54
0026 #define MXC_CCM_CLPCR_LPM_OFFSET 0
0027 #define MXC_CCM_CLPCR_LPM_MASK 0x3
0028 #define MXC_CCM_CLPCR_STBY_COUNT_OFFSET 9
0029 #define MXC_CCM_CLPCR_VSTBY (0x1 << 8)
0030 #define MXC_CCM_CLPCR_SBYOS (0x1 << 6)
0031
0032 #define MXC_CORTEXA8_PLAT_LPC 0xc
0033 #define MXC_CORTEXA8_PLAT_LPC_DSM (1 << 0)
0034 #define MXC_CORTEXA8_PLAT_LPC_DBG_DSM (1 << 1)
0035
0036 #define MXC_SRPG_NEON_SRPGCR 0x280
0037 #define MXC_SRPG_ARM_SRPGCR 0x2a0
0038 #define MXC_SRPG_EMPGC0_SRPGCR 0x2c0
0039 #define MXC_SRPG_EMPGC1_SRPGCR 0x2d0
0040
0041 #define MXC_SRPGCR_PCR 1
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051 #define IMX5_DEFAULT_CPU_IDLE_STATE WAIT_UNCLOCKED_POWER_OFF
0052
0053 struct imx5_suspend_io_state {
0054 u32 offset;
0055 u32 clear;
0056 u32 set;
0057 u32 saved_value;
0058 };
0059
0060 struct imx5_pm_data {
0061 phys_addr_t ccm_addr;
0062 phys_addr_t cortex_addr;
0063 phys_addr_t gpc_addr;
0064 phys_addr_t m4if_addr;
0065 phys_addr_t iomuxc_addr;
0066 void (*suspend_asm)(void __iomem *ocram_vbase);
0067 const u32 *suspend_asm_sz;
0068 const struct imx5_suspend_io_state *suspend_io_config;
0069 int suspend_io_count;
0070 };
0071
0072 static const struct imx5_suspend_io_state imx53_suspend_io_config[] = {
0073 #define MX53_DSE_HIGHZ_MASK (0x7 << 19)
0074 {.offset = 0x584, .clear = MX53_DSE_HIGHZ_MASK},
0075 {.offset = 0x594, .clear = MX53_DSE_HIGHZ_MASK},
0076 {.offset = 0x560, .clear = MX53_DSE_HIGHZ_MASK},
0077 {.offset = 0x554, .clear = MX53_DSE_HIGHZ_MASK},
0078 {.offset = 0x574, .clear = MX53_DSE_HIGHZ_MASK},
0079 {.offset = 0x588, .clear = MX53_DSE_HIGHZ_MASK},
0080 {.offset = 0x578, .clear = MX53_DSE_HIGHZ_MASK},
0081 {.offset = 0x570, .clear = MX53_DSE_HIGHZ_MASK},
0082
0083 {.offset = 0x580, .clear = MX53_DSE_HIGHZ_MASK},
0084 {.offset = 0x564, .clear = MX53_DSE_HIGHZ_MASK},
0085 {.offset = 0x57c, .clear = MX53_DSE_HIGHZ_MASK},
0086 {.offset = 0x590, .clear = MX53_DSE_HIGHZ_MASK},
0087 {.offset = 0x568, .clear = MX53_DSE_HIGHZ_MASK},
0088 {.offset = 0x558, .clear = MX53_DSE_HIGHZ_MASK},
0089 {.offset = 0x6f0, .clear = MX53_DSE_HIGHZ_MASK},
0090 {.offset = 0x718, .clear = MX53_DSE_HIGHZ_MASK},
0091 {.offset = 0x71c, .clear = MX53_DSE_HIGHZ_MASK},
0092 {.offset = 0x728, .clear = MX53_DSE_HIGHZ_MASK},
0093 {.offset = 0x72c, .clear = MX53_DSE_HIGHZ_MASK},
0094
0095
0096 {.offset = 0x720, .clear = MX53_DSE_HIGHZ_MASK, .set = 1 << 19},
0097 };
0098
0099 static const struct imx5_pm_data imx51_pm_data __initconst = {
0100 .ccm_addr = 0x73fd4000,
0101 .cortex_addr = 0x83fa0000,
0102 .gpc_addr = 0x73fd8000,
0103 };
0104
0105 static const struct imx5_pm_data imx53_pm_data __initconst = {
0106 .ccm_addr = 0x53fd4000,
0107 .cortex_addr = 0x63fa0000,
0108 .gpc_addr = 0x53fd8000,
0109 .m4if_addr = 0x63fd8000,
0110 .iomuxc_addr = 0x53fa8000,
0111 .suspend_asm = &imx53_suspend,
0112 .suspend_asm_sz = &imx53_suspend_sz,
0113 .suspend_io_config = imx53_suspend_io_config,
0114 .suspend_io_count = ARRAY_SIZE(imx53_suspend_io_config),
0115 };
0116
0117 #define MX5_MAX_SUSPEND_IOSTATE ARRAY_SIZE(imx53_suspend_io_config)
0118
0119
0120
0121
0122
0123
0124
0125
0126 struct imx5_cpu_suspend_info {
0127 void __iomem *m4if_base;
0128 void __iomem *iomuxc_base;
0129 u32 io_count;
0130 struct imx5_suspend_io_state io_state[MX5_MAX_SUSPEND_IOSTATE];
0131 } __aligned(8);
0132
0133 static void __iomem *ccm_base;
0134 static void __iomem *cortex_base;
0135 static void __iomem *gpc_base;
0136 static void __iomem *suspend_ocram_base;
0137 static void (*imx5_suspend_in_ocram_fn)(void __iomem *ocram_vbase);
0138
0139
0140
0141
0142
0143 static void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode)
0144 {
0145 u32 plat_lpc, arm_srpgcr, ccm_clpcr;
0146 u32 empgc0, empgc1;
0147 int stop_mode = 0;
0148
0149
0150 plat_lpc = imx_readl(cortex_base + MXC_CORTEXA8_PLAT_LPC) &
0151 ~(MXC_CORTEXA8_PLAT_LPC_DSM);
0152 ccm_clpcr = imx_readl(ccm_base + MXC_CCM_CLPCR) &
0153 ~(MXC_CCM_CLPCR_LPM_MASK);
0154 arm_srpgcr = imx_readl(gpc_base + MXC_SRPG_ARM_SRPGCR) &
0155 ~(MXC_SRPGCR_PCR);
0156 empgc0 = imx_readl(gpc_base + MXC_SRPG_EMPGC0_SRPGCR) &
0157 ~(MXC_SRPGCR_PCR);
0158 empgc1 = imx_readl(gpc_base + MXC_SRPG_EMPGC1_SRPGCR) &
0159 ~(MXC_SRPGCR_PCR);
0160
0161 switch (mode) {
0162 case WAIT_CLOCKED:
0163 break;
0164 case WAIT_UNCLOCKED:
0165 ccm_clpcr |= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET;
0166 break;
0167 case WAIT_UNCLOCKED_POWER_OFF:
0168 case STOP_POWER_OFF:
0169 plat_lpc |= MXC_CORTEXA8_PLAT_LPC_DSM
0170 | MXC_CORTEXA8_PLAT_LPC_DBG_DSM;
0171 if (mode == WAIT_UNCLOCKED_POWER_OFF) {
0172 ccm_clpcr |= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET;
0173 ccm_clpcr &= ~MXC_CCM_CLPCR_VSTBY;
0174 ccm_clpcr &= ~MXC_CCM_CLPCR_SBYOS;
0175 stop_mode = 0;
0176 } else {
0177 ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET;
0178 ccm_clpcr |= 0x3 << MXC_CCM_CLPCR_STBY_COUNT_OFFSET;
0179 ccm_clpcr |= MXC_CCM_CLPCR_VSTBY;
0180 ccm_clpcr |= MXC_CCM_CLPCR_SBYOS;
0181 stop_mode = 1;
0182 }
0183 arm_srpgcr |= MXC_SRPGCR_PCR;
0184 break;
0185 case STOP_POWER_ON:
0186 ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET;
0187 break;
0188 default:
0189 printk(KERN_WARNING "UNKNOWN cpu power mode: %d\n", mode);
0190 return;
0191 }
0192
0193 imx_writel(plat_lpc, cortex_base + MXC_CORTEXA8_PLAT_LPC);
0194 imx_writel(ccm_clpcr, ccm_base + MXC_CCM_CLPCR);
0195 imx_writel(arm_srpgcr, gpc_base + MXC_SRPG_ARM_SRPGCR);
0196 imx_writel(arm_srpgcr, gpc_base + MXC_SRPG_NEON_SRPGCR);
0197
0198 if (stop_mode) {
0199 empgc0 |= MXC_SRPGCR_PCR;
0200 empgc1 |= MXC_SRPGCR_PCR;
0201
0202 imx_writel(empgc0, gpc_base + MXC_SRPG_EMPGC0_SRPGCR);
0203 imx_writel(empgc1, gpc_base + MXC_SRPG_EMPGC1_SRPGCR);
0204 }
0205 }
0206
0207 static int mx5_suspend_enter(suspend_state_t state)
0208 {
0209 switch (state) {
0210 case PM_SUSPEND_MEM:
0211 mx5_cpu_lp_set(STOP_POWER_OFF);
0212 break;
0213 case PM_SUSPEND_STANDBY:
0214
0215 break;
0216 default:
0217 return -EINVAL;
0218 }
0219
0220 if (state == PM_SUSPEND_MEM) {
0221 local_flush_tlb_all();
0222 flush_cache_all();
0223
0224
0225 imx_writel(0, gpc_base + MXC_SRPG_EMPGC0_SRPGCR);
0226 imx_writel(0, gpc_base + MXC_SRPG_EMPGC1_SRPGCR);
0227
0228 if (imx5_suspend_in_ocram_fn)
0229 imx5_suspend_in_ocram_fn(suspend_ocram_base);
0230 else
0231 cpu_do_idle();
0232
0233 } else {
0234 cpu_do_idle();
0235 }
0236
0237
0238 mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE);
0239 return 0;
0240 }
0241
0242 static int mx5_pm_valid(suspend_state_t state)
0243 {
0244 return (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX);
0245 }
0246
0247 static const struct platform_suspend_ops mx5_suspend_ops = {
0248 .valid = mx5_pm_valid,
0249 .enter = mx5_suspend_enter,
0250 };
0251
0252 static inline int imx5_cpu_do_idle(void)
0253 {
0254 int ret = tzic_enable_wake();
0255
0256 if (likely(!ret))
0257 cpu_do_idle();
0258
0259 return ret;
0260 }
0261
0262 static void imx5_pm_idle(void)
0263 {
0264 imx5_cpu_do_idle();
0265 }
0266
0267 static int __init imx_suspend_alloc_ocram(
0268 size_t size,
0269 void __iomem **virt_out,
0270 phys_addr_t *phys_out)
0271 {
0272 struct device_node *node;
0273 struct platform_device *pdev;
0274 struct gen_pool *ocram_pool;
0275 unsigned long ocram_base;
0276 void __iomem *virt;
0277 phys_addr_t phys;
0278 int ret = 0;
0279
0280
0281 node = of_find_compatible_node(NULL, NULL, "mmio-sram");
0282 if (!node) {
0283 pr_warn("%s: failed to find ocram node!\n", __func__);
0284 return -ENODEV;
0285 }
0286
0287 pdev = of_find_device_by_node(node);
0288 if (!pdev) {
0289 pr_warn("%s: failed to find ocram device!\n", __func__);
0290 ret = -ENODEV;
0291 goto put_node;
0292 }
0293
0294 ocram_pool = gen_pool_get(&pdev->dev, NULL);
0295 if (!ocram_pool) {
0296 pr_warn("%s: ocram pool unavailable!\n", __func__);
0297 ret = -ENODEV;
0298 goto put_device;
0299 }
0300
0301 ocram_base = gen_pool_alloc(ocram_pool, size);
0302 if (!ocram_base) {
0303 pr_warn("%s: unable to alloc ocram!\n", __func__);
0304 ret = -ENOMEM;
0305 goto put_device;
0306 }
0307
0308 phys = gen_pool_virt_to_phys(ocram_pool, ocram_base);
0309 virt = __arm_ioremap_exec(phys, size, false);
0310 if (phys_out)
0311 *phys_out = phys;
0312 if (virt_out)
0313 *virt_out = virt;
0314
0315 put_device:
0316 put_device(&pdev->dev);
0317 put_node:
0318 of_node_put(node);
0319
0320 return ret;
0321 }
0322
0323 static int __init imx5_suspend_init(const struct imx5_pm_data *soc_data)
0324 {
0325 struct imx5_cpu_suspend_info *suspend_info;
0326 int ret;
0327
0328 void (*suspend_asm)(void __iomem *) = soc_data->suspend_asm;
0329
0330 if (!suspend_asm)
0331 return 0;
0332
0333 if (!soc_data->suspend_asm_sz || !*soc_data->suspend_asm_sz)
0334 return -EINVAL;
0335
0336 ret = imx_suspend_alloc_ocram(
0337 *soc_data->suspend_asm_sz + sizeof(*suspend_info),
0338 &suspend_ocram_base, NULL);
0339 if (ret)
0340 return ret;
0341
0342 suspend_info = suspend_ocram_base;
0343
0344 suspend_info->io_count = soc_data->suspend_io_count;
0345 memcpy(suspend_info->io_state, soc_data->suspend_io_config,
0346 sizeof(*suspend_info->io_state) * soc_data->suspend_io_count);
0347
0348 suspend_info->m4if_base = ioremap(soc_data->m4if_addr, SZ_16K);
0349 if (!suspend_info->m4if_base) {
0350 ret = -ENOMEM;
0351 goto failed_map_m4if;
0352 }
0353
0354 suspend_info->iomuxc_base = ioremap(soc_data->iomuxc_addr, SZ_16K);
0355 if (!suspend_info->iomuxc_base) {
0356 ret = -ENOMEM;
0357 goto failed_map_iomuxc;
0358 }
0359
0360 imx5_suspend_in_ocram_fn = fncpy(
0361 suspend_ocram_base + sizeof(*suspend_info),
0362 suspend_asm,
0363 *soc_data->suspend_asm_sz);
0364
0365 return 0;
0366
0367 failed_map_iomuxc:
0368 iounmap(suspend_info->m4if_base);
0369
0370 failed_map_m4if:
0371 return ret;
0372 }
0373
0374 static int __init imx5_pm_common_init(const struct imx5_pm_data *data)
0375 {
0376 int ret;
0377 struct clk *gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs");
0378
0379 if (IS_ERR(gpc_dvfs_clk))
0380 return PTR_ERR(gpc_dvfs_clk);
0381
0382 ret = clk_prepare_enable(gpc_dvfs_clk);
0383 if (ret)
0384 return ret;
0385
0386 arm_pm_idle = imx5_pm_idle;
0387
0388 ccm_base = ioremap(data->ccm_addr, SZ_16K);
0389 cortex_base = ioremap(data->cortex_addr, SZ_16K);
0390 gpc_base = ioremap(data->gpc_addr, SZ_16K);
0391 WARN_ON(!ccm_base || !cortex_base || !gpc_base);
0392
0393
0394 mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE);
0395
0396 ret = imx5_cpuidle_init();
0397 if (ret)
0398 pr_warn("%s: cpuidle init failed %d\n", __func__, ret);
0399
0400 ret = imx5_suspend_init(data);
0401 if (ret)
0402 pr_warn("%s: No DDR LPM support with suspend %d!\n",
0403 __func__, ret);
0404
0405 suspend_set_ops(&mx5_suspend_ops);
0406
0407 return 0;
0408 }
0409
0410 void __init imx51_pm_init(void)
0411 {
0412 if (IS_ENABLED(CONFIG_SOC_IMX51))
0413 imx5_pm_common_init(&imx51_pm_data);
0414 }
0415
0416 void __init imx53_pm_init(void)
0417 {
0418 if (IS_ENABLED(CONFIG_SOC_IMX53))
0419 imx5_pm_common_init(&imx53_pm_data);
0420 }