18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PXA910 Power Management Routines 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (C) Copyright 2009 Marvell International Ltd. 68c2ecf20Sopenharmony_ci * All Rights Reserved 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/errno.h> 118c2ecf20Sopenharmony_ci#include <linux/err.h> 128c2ecf20Sopenharmony_ci#include <linux/time.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/suspend.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci#include <linux/irq.h> 188c2ecf20Sopenharmony_ci#include <asm/mach-types.h> 198c2ecf20Sopenharmony_ci#include <asm/outercache.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/soc/mmp/cputype.h> 228c2ecf20Sopenharmony_ci#include "addr-map.h" 238c2ecf20Sopenharmony_ci#include "pm-pxa910.h" 248c2ecf20Sopenharmony_ci#include "regs-icu.h" 258c2ecf20Sopenharmony_ci#include "irqs.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ciint pxa910_set_wake(struct irq_data *data, unsigned int on) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci uint32_t awucrm = 0, apcr = 0; 308c2ecf20Sopenharmony_ci int irq = data->irq; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci /* setting wakeup sources */ 338c2ecf20Sopenharmony_ci switch (irq) { 348c2ecf20Sopenharmony_ci /* wakeup line 2 */ 358c2ecf20Sopenharmony_ci case IRQ_PXA910_AP_GPIO: 368c2ecf20Sopenharmony_ci awucrm = MPMU_AWUCRM_WAKEUP(2); 378c2ecf20Sopenharmony_ci apcr |= MPMU_APCR_SLPWP2; 388c2ecf20Sopenharmony_ci break; 398c2ecf20Sopenharmony_ci /* wakeup line 3 */ 408c2ecf20Sopenharmony_ci case IRQ_PXA910_KEYPAD: 418c2ecf20Sopenharmony_ci awucrm = MPMU_AWUCRM_WAKEUP(3) | MPMU_AWUCRM_KEYPRESS; 428c2ecf20Sopenharmony_ci apcr |= MPMU_APCR_SLPWP3; 438c2ecf20Sopenharmony_ci break; 448c2ecf20Sopenharmony_ci case IRQ_PXA910_ROTARY: 458c2ecf20Sopenharmony_ci awucrm = MPMU_AWUCRM_WAKEUP(3) | MPMU_AWUCRM_NEWROTARY; 468c2ecf20Sopenharmony_ci apcr |= MPMU_APCR_SLPWP3; 478c2ecf20Sopenharmony_ci break; 488c2ecf20Sopenharmony_ci case IRQ_PXA910_TRACKBALL: 498c2ecf20Sopenharmony_ci awucrm = MPMU_AWUCRM_WAKEUP(3) | MPMU_AWUCRM_TRACKBALL; 508c2ecf20Sopenharmony_ci apcr |= MPMU_APCR_SLPWP3; 518c2ecf20Sopenharmony_ci break; 528c2ecf20Sopenharmony_ci /* wakeup line 4 */ 538c2ecf20Sopenharmony_ci case IRQ_PXA910_AP1_TIMER1: 548c2ecf20Sopenharmony_ci awucrm = MPMU_AWUCRM_WAKEUP(4) | MPMU_AWUCRM_AP1_TIMER_1; 558c2ecf20Sopenharmony_ci apcr |= MPMU_APCR_SLPWP4; 568c2ecf20Sopenharmony_ci break; 578c2ecf20Sopenharmony_ci case IRQ_PXA910_AP1_TIMER2: 588c2ecf20Sopenharmony_ci awucrm = MPMU_AWUCRM_WAKEUP(4) | MPMU_AWUCRM_AP1_TIMER_2; 598c2ecf20Sopenharmony_ci apcr |= MPMU_APCR_SLPWP4; 608c2ecf20Sopenharmony_ci break; 618c2ecf20Sopenharmony_ci case IRQ_PXA910_AP1_TIMER3: 628c2ecf20Sopenharmony_ci awucrm = MPMU_AWUCRM_WAKEUP(4) | MPMU_AWUCRM_AP1_TIMER_3; 638c2ecf20Sopenharmony_ci apcr |= MPMU_APCR_SLPWP4; 648c2ecf20Sopenharmony_ci break; 658c2ecf20Sopenharmony_ci case IRQ_PXA910_AP2_TIMER1: 668c2ecf20Sopenharmony_ci awucrm = MPMU_AWUCRM_WAKEUP(4) | MPMU_AWUCRM_AP2_TIMER_1; 678c2ecf20Sopenharmony_ci apcr |= MPMU_APCR_SLPWP4; 688c2ecf20Sopenharmony_ci break; 698c2ecf20Sopenharmony_ci case IRQ_PXA910_AP2_TIMER2: 708c2ecf20Sopenharmony_ci awucrm = MPMU_AWUCRM_WAKEUP(4) | MPMU_AWUCRM_AP2_TIMER_2; 718c2ecf20Sopenharmony_ci apcr |= MPMU_APCR_SLPWP4; 728c2ecf20Sopenharmony_ci break; 738c2ecf20Sopenharmony_ci case IRQ_PXA910_AP2_TIMER3: 748c2ecf20Sopenharmony_ci awucrm = MPMU_AWUCRM_WAKEUP(4) | MPMU_AWUCRM_AP2_TIMER_3; 758c2ecf20Sopenharmony_ci apcr |= MPMU_APCR_SLPWP4; 768c2ecf20Sopenharmony_ci break; 778c2ecf20Sopenharmony_ci case IRQ_PXA910_RTC_ALARM: 788c2ecf20Sopenharmony_ci awucrm = MPMU_AWUCRM_WAKEUP(4) | MPMU_AWUCRM_RTC_ALARM; 798c2ecf20Sopenharmony_ci apcr |= MPMU_APCR_SLPWP4; 808c2ecf20Sopenharmony_ci break; 818c2ecf20Sopenharmony_ci /* wakeup line 5 */ 828c2ecf20Sopenharmony_ci case IRQ_PXA910_USB1: 838c2ecf20Sopenharmony_ci case IRQ_PXA910_USB2: 848c2ecf20Sopenharmony_ci awucrm = MPMU_AWUCRM_WAKEUP(5); 858c2ecf20Sopenharmony_ci apcr |= MPMU_APCR_SLPWP5; 868c2ecf20Sopenharmony_ci break; 878c2ecf20Sopenharmony_ci /* wakeup line 6 */ 888c2ecf20Sopenharmony_ci case IRQ_PXA910_MMC: 898c2ecf20Sopenharmony_ci awucrm = MPMU_AWUCRM_WAKEUP(6) 908c2ecf20Sopenharmony_ci | MPMU_AWUCRM_SDH1 918c2ecf20Sopenharmony_ci | MPMU_AWUCRM_SDH2; 928c2ecf20Sopenharmony_ci apcr |= MPMU_APCR_SLPWP6; 938c2ecf20Sopenharmony_ci break; 948c2ecf20Sopenharmony_ci /* wakeup line 7 */ 958c2ecf20Sopenharmony_ci case IRQ_PXA910_PMIC_INT: 968c2ecf20Sopenharmony_ci awucrm = MPMU_AWUCRM_WAKEUP(7); 978c2ecf20Sopenharmony_ci apcr |= MPMU_APCR_SLPWP7; 988c2ecf20Sopenharmony_ci break; 998c2ecf20Sopenharmony_ci default: 1008c2ecf20Sopenharmony_ci if (irq >= IRQ_GPIO_START && irq < IRQ_BOARD_START) { 1018c2ecf20Sopenharmony_ci awucrm = MPMU_AWUCRM_WAKEUP(2); 1028c2ecf20Sopenharmony_ci apcr |= MPMU_APCR_SLPWP2; 1038c2ecf20Sopenharmony_ci } else { 1048c2ecf20Sopenharmony_ci /* FIXME: This should return a proper error code ! */ 1058c2ecf20Sopenharmony_ci printk(KERN_ERR "Error: no defined wake up source irq: %d\n", 1068c2ecf20Sopenharmony_ci irq); 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (on) { 1118c2ecf20Sopenharmony_ci if (awucrm) { 1128c2ecf20Sopenharmony_ci awucrm |= __raw_readl(MPMU_AWUCRM); 1138c2ecf20Sopenharmony_ci __raw_writel(awucrm, MPMU_AWUCRM); 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci if (apcr) { 1168c2ecf20Sopenharmony_ci apcr = ~apcr & __raw_readl(MPMU_APCR); 1178c2ecf20Sopenharmony_ci __raw_writel(apcr, MPMU_APCR); 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci } else { 1208c2ecf20Sopenharmony_ci if (awucrm) { 1218c2ecf20Sopenharmony_ci awucrm = ~awucrm & __raw_readl(MPMU_AWUCRM); 1228c2ecf20Sopenharmony_ci __raw_writel(awucrm, MPMU_AWUCRM); 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci if (apcr) { 1258c2ecf20Sopenharmony_ci apcr |= __raw_readl(MPMU_APCR); 1268c2ecf20Sopenharmony_ci __raw_writel(apcr, MPMU_APCR); 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci return 0; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_civoid pxa910_pm_enter_lowpower_mode(int state) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci uint32_t idle_cfg, apcr; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci idle_cfg = __raw_readl(APMU_MOH_IDLE_CFG); 1378c2ecf20Sopenharmony_ci apcr = __raw_readl(MPMU_APCR); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci apcr &= ~(MPMU_APCR_DDRCORSD | MPMU_APCR_APBSD | MPMU_APCR_AXISD 1408c2ecf20Sopenharmony_ci | MPMU_APCR_VCTCXOSD | MPMU_APCR_STBYEN); 1418c2ecf20Sopenharmony_ci idle_cfg &= ~(APMU_MOH_IDLE_CFG_MOH_IDLE 1428c2ecf20Sopenharmony_ci | APMU_MOH_IDLE_CFG_MOH_PWRDWN); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci switch (state) { 1458c2ecf20Sopenharmony_ci case POWER_MODE_UDR: 1468c2ecf20Sopenharmony_ci /* only shutdown APB in UDR */ 1478c2ecf20Sopenharmony_ci apcr |= MPMU_APCR_STBYEN | MPMU_APCR_APBSD; 1488c2ecf20Sopenharmony_ci fallthrough; 1498c2ecf20Sopenharmony_ci case POWER_MODE_SYS_SLEEP: 1508c2ecf20Sopenharmony_ci apcr |= MPMU_APCR_SLPEN; /* set the SLPEN bit */ 1518c2ecf20Sopenharmony_ci apcr |= MPMU_APCR_VCTCXOSD; /* set VCTCXOSD */ 1528c2ecf20Sopenharmony_ci fallthrough; 1538c2ecf20Sopenharmony_ci case POWER_MODE_APPS_SLEEP: 1548c2ecf20Sopenharmony_ci apcr |= MPMU_APCR_DDRCORSD; /* set DDRCORSD */ 1558c2ecf20Sopenharmony_ci fallthrough; 1568c2ecf20Sopenharmony_ci case POWER_MODE_APPS_IDLE: 1578c2ecf20Sopenharmony_ci apcr |= MPMU_APCR_AXISD; /* set AXISDD bit */ 1588c2ecf20Sopenharmony_ci fallthrough; 1598c2ecf20Sopenharmony_ci case POWER_MODE_CORE_EXTIDLE: 1608c2ecf20Sopenharmony_ci idle_cfg |= APMU_MOH_IDLE_CFG_MOH_IDLE; 1618c2ecf20Sopenharmony_ci idle_cfg |= APMU_MOH_IDLE_CFG_MOH_PWRDWN; 1628c2ecf20Sopenharmony_ci idle_cfg |= APMU_MOH_IDLE_CFG_MOH_PWR_SW(3) 1638c2ecf20Sopenharmony_ci | APMU_MOH_IDLE_CFG_MOH_L2_PWR_SW(3); 1648c2ecf20Sopenharmony_ci fallthrough; 1658c2ecf20Sopenharmony_ci case POWER_MODE_CORE_INTIDLE: 1668c2ecf20Sopenharmony_ci break; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* program the memory controller hardware sleep type and auto wakeup */ 1708c2ecf20Sopenharmony_ci idle_cfg |= APMU_MOH_IDLE_CFG_MOH_DIS_MC_SW_REQ; 1718c2ecf20Sopenharmony_ci idle_cfg |= APMU_MOH_IDLE_CFG_MOH_MC_WAKE_EN; 1728c2ecf20Sopenharmony_ci __raw_writel(0x0, APMU_MC_HW_SLP_TYPE); /* auto refresh */ 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* set DSPSD, DTCMSD, BBSD, MSASLPEN */ 1758c2ecf20Sopenharmony_ci apcr |= MPMU_APCR_DSPSD | MPMU_APCR_DTCMSD | MPMU_APCR_BBSD 1768c2ecf20Sopenharmony_ci | MPMU_APCR_MSASLPEN; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /*always set SLEPEN bit mainly for MSA*/ 1798c2ecf20Sopenharmony_ci apcr |= MPMU_APCR_SLPEN; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* finally write the registers back */ 1828c2ecf20Sopenharmony_ci __raw_writel(idle_cfg, APMU_MOH_IDLE_CFG); 1838c2ecf20Sopenharmony_ci __raw_writel(apcr, MPMU_APCR); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic int pxa910_pm_enter(suspend_state_t state) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci unsigned int idle_cfg, reg = 0; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /*pmic thread not completed,exit;otherwise system can't be waked up*/ 1928c2ecf20Sopenharmony_ci reg = __raw_readl(ICU_INT_CONF(IRQ_PXA910_PMIC_INT)); 1938c2ecf20Sopenharmony_ci if ((reg & 0x3) == 0) 1948c2ecf20Sopenharmony_ci return -EAGAIN; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci idle_cfg = __raw_readl(APMU_MOH_IDLE_CFG); 1978c2ecf20Sopenharmony_ci idle_cfg |= APMU_MOH_IDLE_CFG_MOH_PWRDWN 1988c2ecf20Sopenharmony_ci | APMU_MOH_IDLE_CFG_MOH_SRAM_PWRDWN; 1998c2ecf20Sopenharmony_ci __raw_writel(idle_cfg, APMU_MOH_IDLE_CFG); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* disable L2 */ 2028c2ecf20Sopenharmony_ci outer_disable(); 2038c2ecf20Sopenharmony_ci /* wait for l2 idle */ 2048c2ecf20Sopenharmony_ci while (!(readl(CIU_REG(0x8)) & (1 << 16))) 2058c2ecf20Sopenharmony_ci udelay(1); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci cpu_do_idle(); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* enable L2 */ 2108c2ecf20Sopenharmony_ci outer_resume(); 2118c2ecf20Sopenharmony_ci /* wait for l2 idle */ 2128c2ecf20Sopenharmony_ci while (!(readl(CIU_REG(0x8)) & (1 << 16))) 2138c2ecf20Sopenharmony_ci udelay(1); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci idle_cfg = __raw_readl(APMU_MOH_IDLE_CFG); 2168c2ecf20Sopenharmony_ci idle_cfg &= ~(APMU_MOH_IDLE_CFG_MOH_PWRDWN 2178c2ecf20Sopenharmony_ci | APMU_MOH_IDLE_CFG_MOH_SRAM_PWRDWN); 2188c2ecf20Sopenharmony_ci __raw_writel(idle_cfg, APMU_MOH_IDLE_CFG); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci return 0; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci/* 2248c2ecf20Sopenharmony_ci * Called after processes are frozen, but before we shut down devices. 2258c2ecf20Sopenharmony_ci */ 2268c2ecf20Sopenharmony_cistatic int pxa910_pm_prepare(void) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci pxa910_pm_enter_lowpower_mode(POWER_MODE_UDR); 2298c2ecf20Sopenharmony_ci return 0; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci/* 2338c2ecf20Sopenharmony_ci * Called after devices are re-setup, but before processes are thawed. 2348c2ecf20Sopenharmony_ci */ 2358c2ecf20Sopenharmony_cistatic void pxa910_pm_finish(void) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci pxa910_pm_enter_lowpower_mode(POWER_MODE_CORE_INTIDLE); 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic int pxa910_pm_valid(suspend_state_t state) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci return ((state == PM_SUSPEND_STANDBY) || (state == PM_SUSPEND_MEM)); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic const struct platform_suspend_ops pxa910_pm_ops = { 2468c2ecf20Sopenharmony_ci .valid = pxa910_pm_valid, 2478c2ecf20Sopenharmony_ci .prepare = pxa910_pm_prepare, 2488c2ecf20Sopenharmony_ci .enter = pxa910_pm_enter, 2498c2ecf20Sopenharmony_ci .finish = pxa910_pm_finish, 2508c2ecf20Sopenharmony_ci}; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic int __init pxa910_pm_init(void) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci uint32_t awucrm = 0; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (!cpu_is_pxa910()) 2578c2ecf20Sopenharmony_ci return -EIO; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci suspend_set_ops(&pxa910_pm_ops); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* Set the following bits for MMP3 playback with VCTXO on */ 2628c2ecf20Sopenharmony_ci __raw_writel(__raw_readl(APMU_SQU_CLK_GATE_CTRL) | (1 << 30), 2638c2ecf20Sopenharmony_ci APMU_SQU_CLK_GATE_CTRL); 2648c2ecf20Sopenharmony_ci __raw_writel(__raw_readl(MPMU_FCCR) | (1 << 28), MPMU_FCCR); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci awucrm |= MPMU_AWUCRM_AP_ASYNC_INT | MPMU_AWUCRM_AP_FULL_IDLE; 2678c2ecf20Sopenharmony_ci __raw_writel(awucrm, MPMU_AWUCRM); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci return 0; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cilate_initcall(pxa910_pm_init); 273