18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * OMAP3 Power Management Routines 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2006-2008 Nokia Corporation 68c2ecf20Sopenharmony_ci * Tony Lindgren <tony@atomide.com> 78c2ecf20Sopenharmony_ci * Jouni Hogander 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Copyright (C) 2007 Texas Instruments, Inc. 108c2ecf20Sopenharmony_ci * Rajendra Nayak <rnayak@ti.com> 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Copyright (C) 2005 Texas Instruments, Inc. 138c2ecf20Sopenharmony_ci * Richard Woodruff <r-woodruff2@ti.com> 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Based on pm.c for omap1 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/cpu_pm.h> 198c2ecf20Sopenharmony_ci#include <linux/pm.h> 208c2ecf20Sopenharmony_ci#include <linux/suspend.h> 218c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 228c2ecf20Sopenharmony_ci#include <linux/module.h> 238c2ecf20Sopenharmony_ci#include <linux/list.h> 248c2ecf20Sopenharmony_ci#include <linux/err.h> 258c2ecf20Sopenharmony_ci#include <linux/clk.h> 268c2ecf20Sopenharmony_ci#include <linux/delay.h> 278c2ecf20Sopenharmony_ci#include <linux/slab.h> 288c2ecf20Sopenharmony_ci#include <linux/of.h> 298c2ecf20Sopenharmony_ci#include <linux/omap-gpmc.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <trace/events/power.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include <asm/fncpy.h> 348c2ecf20Sopenharmony_ci#include <asm/suspend.h> 358c2ecf20Sopenharmony_ci#include <asm/system_misc.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include "clockdomain.h" 388c2ecf20Sopenharmony_ci#include "powerdomain.h" 398c2ecf20Sopenharmony_ci#include "soc.h" 408c2ecf20Sopenharmony_ci#include "common.h" 418c2ecf20Sopenharmony_ci#include "cm3xxx.h" 428c2ecf20Sopenharmony_ci#include "cm-regbits-34xx.h" 438c2ecf20Sopenharmony_ci#include "prm-regbits-34xx.h" 448c2ecf20Sopenharmony_ci#include "prm3xxx.h" 458c2ecf20Sopenharmony_ci#include "pm.h" 468c2ecf20Sopenharmony_ci#include "sdrc.h" 478c2ecf20Sopenharmony_ci#include "omap-secure.h" 488c2ecf20Sopenharmony_ci#include "sram.h" 498c2ecf20Sopenharmony_ci#include "control.h" 508c2ecf20Sopenharmony_ci#include "vc.h" 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* pm34xx errata defined in pm.h */ 538c2ecf20Sopenharmony_ciu16 pm34xx_errata; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistruct power_state { 568c2ecf20Sopenharmony_ci struct powerdomain *pwrdm; 578c2ecf20Sopenharmony_ci u32 next_state; 588c2ecf20Sopenharmony_ci#ifdef CONFIG_SUSPEND 598c2ecf20Sopenharmony_ci u32 saved_state; 608c2ecf20Sopenharmony_ci#endif 618c2ecf20Sopenharmony_ci struct list_head node; 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic LIST_HEAD(pwrst_list); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_civoid (*omap3_do_wfi_sram)(void); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic struct powerdomain *mpu_pwrdm, *neon_pwrdm; 698c2ecf20Sopenharmony_cistatic struct powerdomain *core_pwrdm, *per_pwrdm; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic void omap3_core_save_context(void) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci omap3_ctrl_save_padconf(); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* 768c2ecf20Sopenharmony_ci * Force write last pad into memory, as this can fail in some 778c2ecf20Sopenharmony_ci * cases according to errata 1.157, 1.185 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_ci omap_ctrl_writel(omap_ctrl_readl(OMAP343X_PADCONF_ETK_D14), 808c2ecf20Sopenharmony_ci OMAP343X_CONTROL_MEM_WKUP + 0x2a0); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* Save the Interrupt controller context */ 838c2ecf20Sopenharmony_ci omap_intc_save_context(); 848c2ecf20Sopenharmony_ci /* Save the GPMC context */ 858c2ecf20Sopenharmony_ci omap3_gpmc_save_context(); 868c2ecf20Sopenharmony_ci /* Save the system control module context, padconf already save above*/ 878c2ecf20Sopenharmony_ci omap3_control_save_context(); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic void omap3_core_restore_context(void) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci /* Restore the control module context, padconf restored by h/w */ 938c2ecf20Sopenharmony_ci omap3_control_restore_context(); 948c2ecf20Sopenharmony_ci /* Restore the GPMC context */ 958c2ecf20Sopenharmony_ci omap3_gpmc_restore_context(); 968c2ecf20Sopenharmony_ci /* Restore the interrupt controller context */ 978c2ecf20Sopenharmony_ci omap_intc_restore_context(); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* 1018c2ecf20Sopenharmony_ci * FIXME: This function should be called before entering off-mode after 1028c2ecf20Sopenharmony_ci * OMAP3 secure services have been accessed. Currently it is only called 1038c2ecf20Sopenharmony_ci * once during boot sequence, but this works as we are not using secure 1048c2ecf20Sopenharmony_ci * services. 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_cistatic void omap3_save_secure_ram_context(void) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci u32 ret; 1098c2ecf20Sopenharmony_ci int mpu_next_state = pwrdm_read_next_pwrst(mpu_pwrdm); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (omap_type() != OMAP2_DEVICE_TYPE_GP) { 1128c2ecf20Sopenharmony_ci /* 1138c2ecf20Sopenharmony_ci * MPU next state must be set to POWER_ON temporarily, 1148c2ecf20Sopenharmony_ci * otherwise the WFI executed inside the ROM code 1158c2ecf20Sopenharmony_ci * will hang the system. 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_ci pwrdm_set_next_pwrst(mpu_pwrdm, PWRDM_POWER_ON); 1188c2ecf20Sopenharmony_ci ret = omap3_save_secure_ram(omap3_secure_ram_storage, 1198c2ecf20Sopenharmony_ci OMAP3_SAVE_SECURE_RAM_SZ); 1208c2ecf20Sopenharmony_ci pwrdm_set_next_pwrst(mpu_pwrdm, mpu_next_state); 1218c2ecf20Sopenharmony_ci /* Following is for error tracking, it should not happen */ 1228c2ecf20Sopenharmony_ci if (ret) { 1238c2ecf20Sopenharmony_ci pr_err("save_secure_sram() returns %08x\n", ret); 1248c2ecf20Sopenharmony_ci while (1) 1258c2ecf20Sopenharmony_ci ; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic irqreturn_t _prcm_int_handle_io(int irq, void *unused) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci int c; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci c = omap_prm_clear_mod_irqs(WKUP_MOD, 1, OMAP3430_ST_IO_MASK | 1358c2ecf20Sopenharmony_ci OMAP3430_ST_IO_CHAIN_MASK); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return c ? IRQ_HANDLED : IRQ_NONE; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic irqreturn_t _prcm_int_handle_wakeup(int irq, void *unused) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci int c; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* 1458c2ecf20Sopenharmony_ci * Clear all except ST_IO and ST_IO_CHAIN for wkup module, 1468c2ecf20Sopenharmony_ci * these are handled in a separate handler to avoid acking 1478c2ecf20Sopenharmony_ci * IO events before parsing in mux code 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_ci c = omap_prm_clear_mod_irqs(WKUP_MOD, 1, ~(OMAP3430_ST_IO_MASK | 1508c2ecf20Sopenharmony_ci OMAP3430_ST_IO_CHAIN_MASK)); 1518c2ecf20Sopenharmony_ci c += omap_prm_clear_mod_irqs(CORE_MOD, 1, ~0); 1528c2ecf20Sopenharmony_ci c += omap_prm_clear_mod_irqs(OMAP3430_PER_MOD, 1, ~0); 1538c2ecf20Sopenharmony_ci if (omap_rev() > OMAP3430_REV_ES1_0) { 1548c2ecf20Sopenharmony_ci c += omap_prm_clear_mod_irqs(CORE_MOD, 3, ~0); 1558c2ecf20Sopenharmony_ci c += omap_prm_clear_mod_irqs(OMAP3430ES2_USBHOST_MOD, 1, ~0); 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci return c ? IRQ_HANDLED : IRQ_NONE; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic void omap34xx_save_context(u32 *save) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci u32 val; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* Read Auxiliary Control Register */ 1668c2ecf20Sopenharmony_ci asm("mrc p15, 0, %0, c1, c0, 1" : "=r" (val)); 1678c2ecf20Sopenharmony_ci *save++ = 1; 1688c2ecf20Sopenharmony_ci *save++ = val; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* Read L2 AUX ctrl register */ 1718c2ecf20Sopenharmony_ci asm("mrc p15, 1, %0, c9, c0, 2" : "=r" (val)); 1728c2ecf20Sopenharmony_ci *save++ = 1; 1738c2ecf20Sopenharmony_ci *save++ = val; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic int omap34xx_do_sram_idle(unsigned long save_state) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci omap34xx_cpu_suspend(save_state); 1798c2ecf20Sopenharmony_ci return 0; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_civoid omap_sram_idle(void) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci /* Variable to tell what needs to be saved and restored 1858c2ecf20Sopenharmony_ci * in omap_sram_idle*/ 1868c2ecf20Sopenharmony_ci /* save_state = 0 => Nothing to save and restored */ 1878c2ecf20Sopenharmony_ci /* save_state = 1 => Only L1 and logic lost */ 1888c2ecf20Sopenharmony_ci /* save_state = 2 => Only L2 lost */ 1898c2ecf20Sopenharmony_ci /* save_state = 3 => L1, L2 and logic lost */ 1908c2ecf20Sopenharmony_ci int save_state = 0; 1918c2ecf20Sopenharmony_ci int mpu_next_state = PWRDM_POWER_ON; 1928c2ecf20Sopenharmony_ci int per_next_state = PWRDM_POWER_ON; 1938c2ecf20Sopenharmony_ci int core_next_state = PWRDM_POWER_ON; 1948c2ecf20Sopenharmony_ci u32 sdrc_pwr = 0; 1958c2ecf20Sopenharmony_ci int error; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci mpu_next_state = pwrdm_read_next_pwrst(mpu_pwrdm); 1988c2ecf20Sopenharmony_ci switch (mpu_next_state) { 1998c2ecf20Sopenharmony_ci case PWRDM_POWER_ON: 2008c2ecf20Sopenharmony_ci case PWRDM_POWER_RET: 2018c2ecf20Sopenharmony_ci /* No need to save context */ 2028c2ecf20Sopenharmony_ci save_state = 0; 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci case PWRDM_POWER_OFF: 2058c2ecf20Sopenharmony_ci save_state = 3; 2068c2ecf20Sopenharmony_ci break; 2078c2ecf20Sopenharmony_ci default: 2088c2ecf20Sopenharmony_ci /* Invalid state */ 2098c2ecf20Sopenharmony_ci pr_err("Invalid mpu state in sram_idle\n"); 2108c2ecf20Sopenharmony_ci return; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* NEON control */ 2148c2ecf20Sopenharmony_ci if (pwrdm_read_pwrst(neon_pwrdm) == PWRDM_POWER_ON) 2158c2ecf20Sopenharmony_ci pwrdm_set_next_pwrst(neon_pwrdm, mpu_next_state); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* Enable IO-PAD and IO-CHAIN wakeups */ 2188c2ecf20Sopenharmony_ci per_next_state = pwrdm_read_next_pwrst(per_pwrdm); 2198c2ecf20Sopenharmony_ci core_next_state = pwrdm_read_next_pwrst(core_pwrdm); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci pwrdm_pre_transition(NULL); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci /* PER */ 2248c2ecf20Sopenharmony_ci if (per_next_state == PWRDM_POWER_OFF) { 2258c2ecf20Sopenharmony_ci error = cpu_cluster_pm_enter(); 2268c2ecf20Sopenharmony_ci if (error) 2278c2ecf20Sopenharmony_ci return; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* CORE */ 2318c2ecf20Sopenharmony_ci if (core_next_state < PWRDM_POWER_ON) { 2328c2ecf20Sopenharmony_ci if (core_next_state == PWRDM_POWER_OFF) { 2338c2ecf20Sopenharmony_ci omap3_core_save_context(); 2348c2ecf20Sopenharmony_ci omap3_cm_save_context(); 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci /* Configure PMIC signaling for I2C4 or sys_off_mode */ 2398c2ecf20Sopenharmony_ci omap3_vc_set_pmic_signaling(core_next_state); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci omap3_intc_prepare_idle(); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* 2448c2ecf20Sopenharmony_ci * On EMU/HS devices ROM code restores a SRDC value 2458c2ecf20Sopenharmony_ci * from scratchpad which has automatic self refresh on timeout 2468c2ecf20Sopenharmony_ci * of AUTO_CNT = 1 enabled. This takes care of erratum ID i443. 2478c2ecf20Sopenharmony_ci * Hence store/restore the SDRC_POWER register here. 2488c2ecf20Sopenharmony_ci */ 2498c2ecf20Sopenharmony_ci if (cpu_is_omap3430() && omap_rev() >= OMAP3430_REV_ES3_0 && 2508c2ecf20Sopenharmony_ci (omap_type() == OMAP2_DEVICE_TYPE_EMU || 2518c2ecf20Sopenharmony_ci omap_type() == OMAP2_DEVICE_TYPE_SEC) && 2528c2ecf20Sopenharmony_ci core_next_state == PWRDM_POWER_OFF) 2538c2ecf20Sopenharmony_ci sdrc_pwr = sdrc_read_reg(SDRC_POWER); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* 2568c2ecf20Sopenharmony_ci * omap3_arm_context is the location where some ARM context 2578c2ecf20Sopenharmony_ci * get saved. The rest is placed on the stack, and restored 2588c2ecf20Sopenharmony_ci * from there before resuming. 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_ci if (save_state) 2618c2ecf20Sopenharmony_ci omap34xx_save_context(omap3_arm_context); 2628c2ecf20Sopenharmony_ci if (save_state == 1 || save_state == 3) 2638c2ecf20Sopenharmony_ci cpu_suspend(save_state, omap34xx_do_sram_idle); 2648c2ecf20Sopenharmony_ci else 2658c2ecf20Sopenharmony_ci omap34xx_do_sram_idle(save_state); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* Restore normal SDRC POWER settings */ 2688c2ecf20Sopenharmony_ci if (cpu_is_omap3430() && omap_rev() >= OMAP3430_REV_ES3_0 && 2698c2ecf20Sopenharmony_ci (omap_type() == OMAP2_DEVICE_TYPE_EMU || 2708c2ecf20Sopenharmony_ci omap_type() == OMAP2_DEVICE_TYPE_SEC) && 2718c2ecf20Sopenharmony_ci core_next_state == PWRDM_POWER_OFF) 2728c2ecf20Sopenharmony_ci sdrc_write_reg(sdrc_pwr, SDRC_POWER); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* CORE */ 2758c2ecf20Sopenharmony_ci if (core_next_state < PWRDM_POWER_ON && 2768c2ecf20Sopenharmony_ci pwrdm_read_prev_pwrst(core_pwrdm) == PWRDM_POWER_OFF) { 2778c2ecf20Sopenharmony_ci omap3_core_restore_context(); 2788c2ecf20Sopenharmony_ci omap3_cm_restore_context(); 2798c2ecf20Sopenharmony_ci omap3_sram_restore_context(); 2808c2ecf20Sopenharmony_ci omap2_sms_restore_context(); 2818c2ecf20Sopenharmony_ci } else { 2828c2ecf20Sopenharmony_ci /* 2838c2ecf20Sopenharmony_ci * In off-mode resume path above, omap3_core_restore_context 2848c2ecf20Sopenharmony_ci * also handles the INTC autoidle restore done here so limit 2858c2ecf20Sopenharmony_ci * this to non-off mode resume paths so we don't do it twice. 2868c2ecf20Sopenharmony_ci */ 2878c2ecf20Sopenharmony_ci omap3_intc_resume_idle(); 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci pwrdm_post_transition(NULL); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* PER */ 2938c2ecf20Sopenharmony_ci if (per_next_state == PWRDM_POWER_OFF) 2948c2ecf20Sopenharmony_ci cpu_cluster_pm_exit(); 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic void omap3_pm_idle(void) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci if (omap_irq_pending()) 3008c2ecf20Sopenharmony_ci return; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci omap_sram_idle(); 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci#ifdef CONFIG_SUSPEND 3068c2ecf20Sopenharmony_cistatic int omap3_pm_suspend(void) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct power_state *pwrst; 3098c2ecf20Sopenharmony_ci int state, ret = 0; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci /* Read current next_pwrsts */ 3128c2ecf20Sopenharmony_ci list_for_each_entry(pwrst, &pwrst_list, node) 3138c2ecf20Sopenharmony_ci pwrst->saved_state = pwrdm_read_next_pwrst(pwrst->pwrdm); 3148c2ecf20Sopenharmony_ci /* Set ones wanted by suspend */ 3158c2ecf20Sopenharmony_ci list_for_each_entry(pwrst, &pwrst_list, node) { 3168c2ecf20Sopenharmony_ci if (omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state)) 3178c2ecf20Sopenharmony_ci goto restore; 3188c2ecf20Sopenharmony_ci if (pwrdm_clear_all_prev_pwrst(pwrst->pwrdm)) 3198c2ecf20Sopenharmony_ci goto restore; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci omap3_intc_suspend(); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci omap_sram_idle(); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cirestore: 3278c2ecf20Sopenharmony_ci /* Restore next_pwrsts */ 3288c2ecf20Sopenharmony_ci list_for_each_entry(pwrst, &pwrst_list, node) { 3298c2ecf20Sopenharmony_ci state = pwrdm_read_prev_pwrst(pwrst->pwrdm); 3308c2ecf20Sopenharmony_ci if (state > pwrst->next_state) { 3318c2ecf20Sopenharmony_ci pr_info("Powerdomain (%s) didn't enter target state %d\n", 3328c2ecf20Sopenharmony_ci pwrst->pwrdm->name, pwrst->next_state); 3338c2ecf20Sopenharmony_ci ret = -1; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci omap_set_pwrdm_state(pwrst->pwrdm, pwrst->saved_state); 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci if (ret) 3388c2ecf20Sopenharmony_ci pr_err("Could not enter target state in pm_suspend\n"); 3398c2ecf20Sopenharmony_ci else 3408c2ecf20Sopenharmony_ci pr_info("Successfully put all powerdomains to target state\n"); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci return ret; 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci#else 3458c2ecf20Sopenharmony_ci#define omap3_pm_suspend NULL 3468c2ecf20Sopenharmony_ci#endif /* CONFIG_SUSPEND */ 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic void __init prcm_setup_regs(void) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci omap3_ctrl_init(); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci omap3_prm_init_pm(cpu_is_omap3630(), omap3_has_iva()); 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_civoid omap3_pm_off_mode_enable(int enable) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct power_state *pwrst; 3588c2ecf20Sopenharmony_ci u32 state; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (enable) 3618c2ecf20Sopenharmony_ci state = PWRDM_POWER_OFF; 3628c2ecf20Sopenharmony_ci else 3638c2ecf20Sopenharmony_ci state = PWRDM_POWER_RET; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci list_for_each_entry(pwrst, &pwrst_list, node) { 3668c2ecf20Sopenharmony_ci if (IS_PM34XX_ERRATUM(PM_SDRC_WAKEUP_ERRATUM_i583) && 3678c2ecf20Sopenharmony_ci pwrst->pwrdm == core_pwrdm && 3688c2ecf20Sopenharmony_ci state == PWRDM_POWER_OFF) { 3698c2ecf20Sopenharmony_ci pwrst->next_state = PWRDM_POWER_RET; 3708c2ecf20Sopenharmony_ci pr_warn("%s: Core OFF disabled due to errata i583\n", 3718c2ecf20Sopenharmony_ci __func__); 3728c2ecf20Sopenharmony_ci } else { 3738c2ecf20Sopenharmony_ci pwrst->next_state = state; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state); 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ciint omap3_pm_get_suspend_state(struct powerdomain *pwrdm) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci struct power_state *pwrst; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci list_for_each_entry(pwrst, &pwrst_list, node) { 3848c2ecf20Sopenharmony_ci if (pwrst->pwrdm == pwrdm) 3858c2ecf20Sopenharmony_ci return pwrst->next_state; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci return -EINVAL; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ciint omap3_pm_set_suspend_state(struct powerdomain *pwrdm, int state) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci struct power_state *pwrst; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci list_for_each_entry(pwrst, &pwrst_list, node) { 3958c2ecf20Sopenharmony_ci if (pwrst->pwrdm == pwrdm) { 3968c2ecf20Sopenharmony_ci pwrst->next_state = state; 3978c2ecf20Sopenharmony_ci return 0; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci return -EINVAL; 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct power_state *pwrst; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci if (!pwrdm->pwrsts) 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci pwrst = kmalloc(sizeof(struct power_state), GFP_ATOMIC); 4118c2ecf20Sopenharmony_ci if (!pwrst) 4128c2ecf20Sopenharmony_ci return -ENOMEM; 4138c2ecf20Sopenharmony_ci pwrst->pwrdm = pwrdm; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (enable_off_mode) 4168c2ecf20Sopenharmony_ci pwrst->next_state = PWRDM_POWER_OFF; 4178c2ecf20Sopenharmony_ci else 4188c2ecf20Sopenharmony_ci pwrst->next_state = PWRDM_POWER_RET; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci list_add(&pwrst->node, &pwrst_list); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci if (pwrdm_has_hdwr_sar(pwrdm)) 4238c2ecf20Sopenharmony_ci pwrdm_enable_hdwr_sar(pwrdm); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci return omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state); 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci/* 4298c2ecf20Sopenharmony_ci * Push functions to SRAM 4308c2ecf20Sopenharmony_ci * 4318c2ecf20Sopenharmony_ci * The minimum set of functions is pushed to SRAM for execution: 4328c2ecf20Sopenharmony_ci * - omap3_do_wfi for erratum i581 WA, 4338c2ecf20Sopenharmony_ci */ 4348c2ecf20Sopenharmony_civoid omap_push_sram_idle(void) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci omap3_do_wfi_sram = omap_sram_push(omap3_do_wfi, omap3_do_wfi_sz); 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic void __init pm_errata_configure(void) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci if (cpu_is_omap3630()) { 4428c2ecf20Sopenharmony_ci pm34xx_errata |= PM_RTA_ERRATUM_i608; 4438c2ecf20Sopenharmony_ci /* Enable the l2 cache toggling in sleep logic */ 4448c2ecf20Sopenharmony_ci enable_omap3630_toggle_l2_on_restore(); 4458c2ecf20Sopenharmony_ci if (omap_rev() < OMAP3630_REV_ES1_2) 4468c2ecf20Sopenharmony_ci pm34xx_errata |= (PM_SDRC_WAKEUP_ERRATUM_i583 | 4478c2ecf20Sopenharmony_ci PM_PER_MEMORIES_ERRATUM_i582); 4488c2ecf20Sopenharmony_ci } else if (cpu_is_omap34xx()) { 4498c2ecf20Sopenharmony_ci pm34xx_errata |= PM_PER_MEMORIES_ERRATUM_i582; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic void __init omap3_pm_check_pmic(void) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci struct device_node *np; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "ti,twl4030-power-idle"); 4588c2ecf20Sopenharmony_ci if (!np) 4598c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "ti,twl4030-power-idle-osc-off"); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (np) { 4628c2ecf20Sopenharmony_ci of_node_put(np); 4638c2ecf20Sopenharmony_ci enable_off_mode = 1; 4648c2ecf20Sopenharmony_ci } else { 4658c2ecf20Sopenharmony_ci enable_off_mode = 0; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ciint __init omap3_pm_init(void) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci struct power_state *pwrst, *tmp; 4728c2ecf20Sopenharmony_ci struct clockdomain *neon_clkdm, *mpu_clkdm, *per_clkdm, *wkup_clkdm; 4738c2ecf20Sopenharmony_ci int ret; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (!omap3_has_io_chain_ctrl()) 4768c2ecf20Sopenharmony_ci pr_warn("PM: no software I/O chain control; some wakeups may be lost\n"); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci pm_errata_configure(); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci /* XXX prcm_setup_regs needs to be before enabling hw 4818c2ecf20Sopenharmony_ci * supervised mode for powerdomains */ 4828c2ecf20Sopenharmony_ci prcm_setup_regs(); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci ret = request_irq(omap_prcm_event_to_irq("wkup"), 4858c2ecf20Sopenharmony_ci _prcm_int_handle_wakeup, IRQF_NO_SUSPEND, "pm_wkup", NULL); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci if (ret) { 4888c2ecf20Sopenharmony_ci pr_err("pm: Failed to request pm_wkup irq\n"); 4898c2ecf20Sopenharmony_ci goto err1; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci /* IO interrupt is shared with mux code */ 4938c2ecf20Sopenharmony_ci ret = request_irq(omap_prcm_event_to_irq("io"), 4948c2ecf20Sopenharmony_ci _prcm_int_handle_io, IRQF_SHARED | IRQF_NO_SUSPEND, "pm_io", 4958c2ecf20Sopenharmony_ci omap3_pm_init); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (ret) { 4988c2ecf20Sopenharmony_ci pr_err("pm: Failed to request pm_io irq\n"); 4998c2ecf20Sopenharmony_ci goto err2; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci omap3_pm_check_pmic(); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci ret = pwrdm_for_each(pwrdms_setup, NULL); 5058c2ecf20Sopenharmony_ci if (ret) { 5068c2ecf20Sopenharmony_ci pr_err("Failed to setup powerdomains\n"); 5078c2ecf20Sopenharmony_ci goto err3; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci (void) clkdm_for_each(omap_pm_clkdms_setup, NULL); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci mpu_pwrdm = pwrdm_lookup("mpu_pwrdm"); 5138c2ecf20Sopenharmony_ci if (mpu_pwrdm == NULL) { 5148c2ecf20Sopenharmony_ci pr_err("Failed to get mpu_pwrdm\n"); 5158c2ecf20Sopenharmony_ci ret = -EINVAL; 5168c2ecf20Sopenharmony_ci goto err3; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci neon_pwrdm = pwrdm_lookup("neon_pwrdm"); 5208c2ecf20Sopenharmony_ci per_pwrdm = pwrdm_lookup("per_pwrdm"); 5218c2ecf20Sopenharmony_ci core_pwrdm = pwrdm_lookup("core_pwrdm"); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci neon_clkdm = clkdm_lookup("neon_clkdm"); 5248c2ecf20Sopenharmony_ci mpu_clkdm = clkdm_lookup("mpu_clkdm"); 5258c2ecf20Sopenharmony_ci per_clkdm = clkdm_lookup("per_clkdm"); 5268c2ecf20Sopenharmony_ci wkup_clkdm = clkdm_lookup("wkup_clkdm"); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci omap_common_suspend_init(omap3_pm_suspend); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci arm_pm_idle = omap3_pm_idle; 5318c2ecf20Sopenharmony_ci omap3_idle_init(); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci /* 5348c2ecf20Sopenharmony_ci * RTA is disabled during initialization as per erratum i608 5358c2ecf20Sopenharmony_ci * it is safer to disable RTA by the bootloader, but we would like 5368c2ecf20Sopenharmony_ci * to be doubly sure here and prevent any mishaps. 5378c2ecf20Sopenharmony_ci */ 5388c2ecf20Sopenharmony_ci if (IS_PM34XX_ERRATUM(PM_RTA_ERRATUM_i608)) 5398c2ecf20Sopenharmony_ci omap3630_ctrl_disable_rta(); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci /* 5428c2ecf20Sopenharmony_ci * The UART3/4 FIFO and the sidetone memory in McBSP2/3 are 5438c2ecf20Sopenharmony_ci * not correctly reset when the PER powerdomain comes back 5448c2ecf20Sopenharmony_ci * from OFF or OSWR when the CORE powerdomain is kept active. 5458c2ecf20Sopenharmony_ci * See OMAP36xx Erratum i582 "PER Domain reset issue after 5468c2ecf20Sopenharmony_ci * Domain-OFF/OSWR Wakeup". This wakeup dependency is not a 5478c2ecf20Sopenharmony_ci * complete workaround. The kernel must also prevent the PER 5488c2ecf20Sopenharmony_ci * powerdomain from going to OSWR/OFF while the CORE 5498c2ecf20Sopenharmony_ci * powerdomain is not going to OSWR/OFF. And if PER last 5508c2ecf20Sopenharmony_ci * power state was off while CORE last power state was ON, the 5518c2ecf20Sopenharmony_ci * UART3/4 and McBSP2/3 SIDETONE devices need to run a 5528c2ecf20Sopenharmony_ci * self-test using their loopback tests; if that fails, those 5538c2ecf20Sopenharmony_ci * devices are unusable until the PER/CORE can complete a transition 5548c2ecf20Sopenharmony_ci * from ON to OSWR/OFF and then back to ON. 5558c2ecf20Sopenharmony_ci * 5568c2ecf20Sopenharmony_ci * XXX Technically this workaround is only needed if off-mode 5578c2ecf20Sopenharmony_ci * or OSWR is enabled. 5588c2ecf20Sopenharmony_ci */ 5598c2ecf20Sopenharmony_ci if (IS_PM34XX_ERRATUM(PM_PER_MEMORIES_ERRATUM_i582)) 5608c2ecf20Sopenharmony_ci clkdm_add_wkdep(per_clkdm, wkup_clkdm); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci clkdm_add_wkdep(neon_clkdm, mpu_clkdm); 5638c2ecf20Sopenharmony_ci if (omap_type() != OMAP2_DEVICE_TYPE_GP) { 5648c2ecf20Sopenharmony_ci omap3_secure_ram_storage = 5658c2ecf20Sopenharmony_ci kmalloc(OMAP3_SAVE_SECURE_RAM_SZ, GFP_KERNEL); 5668c2ecf20Sopenharmony_ci if (!omap3_secure_ram_storage) 5678c2ecf20Sopenharmony_ci pr_err("Memory allocation failed when allocating for secure sram context\n"); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci local_irq_disable(); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci omap3_save_secure_ram_context(); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci local_irq_enable(); 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci omap3_save_scratchpad_contents(); 5778c2ecf20Sopenharmony_ci return ret; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cierr3: 5808c2ecf20Sopenharmony_ci list_for_each_entry_safe(pwrst, tmp, &pwrst_list, node) { 5818c2ecf20Sopenharmony_ci list_del(&pwrst->node); 5828c2ecf20Sopenharmony_ci kfree(pwrst); 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci free_irq(omap_prcm_event_to_irq("io"), omap3_pm_init); 5858c2ecf20Sopenharmony_cierr2: 5868c2ecf20Sopenharmony_ci free_irq(omap_prcm_event_to_irq("wkup"), NULL); 5878c2ecf20Sopenharmony_cierr1: 5888c2ecf20Sopenharmony_ci return ret; 5898c2ecf20Sopenharmony_ci} 590