1c9347101SJongpill Lee /* 20d713cf1SBartlomiej Zolnierkiewicz * Copyright (c) 2011-2014 Samsung Electronics Co., Ltd. 383014579SKukjin Kim * http://www.samsung.com 483014579SKukjin Kim * 5c9347101SJongpill Lee * EXYNOS - Power Management support 683014579SKukjin Kim * 783014579SKukjin Kim * Based on arch/arm/mach-s3c2410/pm.c 883014579SKukjin Kim * Copyright (c) 2006 Simtec Electronics 983014579SKukjin Kim * Ben Dooks <ben@simtec.co.uk> 1083014579SKukjin Kim * 1183014579SKukjin Kim * This program is free software; you can redistribute it and/or modify 1283014579SKukjin Kim * it under the terms of the GNU General Public License version 2 as 1383014579SKukjin Kim * published by the Free Software Foundation. 1483014579SKukjin Kim */ 1583014579SKukjin Kim 1683014579SKukjin Kim #include <linux/init.h> 1783014579SKukjin Kim #include <linux/suspend.h> 1885f9f908SDaniel Lezcano #include <linux/cpu_pm.h> 1983014579SKukjin Kim #include <linux/io.h> 2083014579SKukjin Kim #include <linux/err.h> 2183014579SKukjin Kim 222b9d9c32STomasz Figa #include <asm/firmware.h> 2363b870f1SShawn Guo #include <asm/smp_scu.h> 24d710aa31STomasz Figa #include <asm/suspend.h> 2583014579SKukjin Kim 26d710aa31STomasz Figa #include <plat/pm-common.h> 27ccd458c1SKukjin Kim 28ccd458c1SKukjin Kim #include "common.h" 2965c9a853SKukjin Kim #include "regs-pmu.h" 30318fd20bSPankaj Dubey #include "regs-sys.h" 3183014579SKukjin Kim 32134abc29SBartlomiej Zolnierkiewicz static inline void __iomem *exynos_boot_vector_addr(void) 33134abc29SBartlomiej Zolnierkiewicz { 34134abc29SBartlomiej Zolnierkiewicz if (samsung_rev() == EXYNOS4210_REV_1_1) 35134abc29SBartlomiej Zolnierkiewicz return pmu_base_addr + S5P_INFORM7; 36134abc29SBartlomiej Zolnierkiewicz else if (samsung_rev() == EXYNOS4210_REV_1_0) 37134abc29SBartlomiej Zolnierkiewicz return sysram_base_addr + 0x24; 38134abc29SBartlomiej Zolnierkiewicz return pmu_base_addr + S5P_INFORM0; 39134abc29SBartlomiej Zolnierkiewicz } 40134abc29SBartlomiej Zolnierkiewicz 41134abc29SBartlomiej Zolnierkiewicz static inline void __iomem *exynos_boot_vector_flag(void) 42134abc29SBartlomiej Zolnierkiewicz { 43134abc29SBartlomiej Zolnierkiewicz if (samsung_rev() == EXYNOS4210_REV_1_1) 44134abc29SBartlomiej Zolnierkiewicz return pmu_base_addr + S5P_INFORM6; 45134abc29SBartlomiej Zolnierkiewicz else if (samsung_rev() == EXYNOS4210_REV_1_0) 46134abc29SBartlomiej Zolnierkiewicz return sysram_base_addr + 0x20; 47134abc29SBartlomiej Zolnierkiewicz return pmu_base_addr + S5P_INFORM1; 48134abc29SBartlomiej Zolnierkiewicz } 493681bafeSDaniel Lezcano 503681bafeSDaniel Lezcano #define S5P_CHECK_AFTR 0xFCBA0D10 513681bafeSDaniel Lezcano 5283014579SKukjin Kim /* For Cortex-A9 Diagnostic and Power control register */ 5383014579SKukjin Kim static unsigned int save_arm_register[2]; 5483014579SKukjin Kim 550d713cf1SBartlomiej Zolnierkiewicz void exynos_cpu_save_register(void) 56309e08c4SDaniel Lezcano { 57309e08c4SDaniel Lezcano unsigned long tmp; 58309e08c4SDaniel Lezcano 59309e08c4SDaniel Lezcano /* Save Power control register */ 60309e08c4SDaniel Lezcano asm ("mrc p15, 0, %0, c15, c0, 0" 61309e08c4SDaniel Lezcano : "=r" (tmp) : : "cc"); 62309e08c4SDaniel Lezcano 63309e08c4SDaniel Lezcano save_arm_register[0] = tmp; 64309e08c4SDaniel Lezcano 65309e08c4SDaniel Lezcano /* Save Diagnostic register */ 66309e08c4SDaniel Lezcano asm ("mrc p15, 0, %0, c15, c0, 1" 67309e08c4SDaniel Lezcano : "=r" (tmp) : : "cc"); 68309e08c4SDaniel Lezcano 69309e08c4SDaniel Lezcano save_arm_register[1] = tmp; 70309e08c4SDaniel Lezcano } 71309e08c4SDaniel Lezcano 720d713cf1SBartlomiej Zolnierkiewicz void exynos_cpu_restore_register(void) 73309e08c4SDaniel Lezcano { 74309e08c4SDaniel Lezcano unsigned long tmp; 75309e08c4SDaniel Lezcano 76309e08c4SDaniel Lezcano /* Restore Power control register */ 77309e08c4SDaniel Lezcano tmp = save_arm_register[0]; 78309e08c4SDaniel Lezcano 79309e08c4SDaniel Lezcano asm volatile ("mcr p15, 0, %0, c15, c0, 0" 80309e08c4SDaniel Lezcano : : "r" (tmp) 81309e08c4SDaniel Lezcano : "cc"); 82309e08c4SDaniel Lezcano 83309e08c4SDaniel Lezcano /* Restore Diagnostic register */ 84309e08c4SDaniel Lezcano tmp = save_arm_register[1]; 85309e08c4SDaniel Lezcano 86309e08c4SDaniel Lezcano asm volatile ("mcr p15, 0, %0, c15, c0, 1" 87309e08c4SDaniel Lezcano : : "r" (tmp) 88309e08c4SDaniel Lezcano : "cc"); 89309e08c4SDaniel Lezcano } 90309e08c4SDaniel Lezcano 910d713cf1SBartlomiej Zolnierkiewicz void exynos_pm_central_suspend(void) 9201601b34STomasz Figa { 9301601b34STomasz Figa unsigned long tmp; 9401601b34STomasz Figa 9501601b34STomasz Figa /* Setting Central Sequence Register for power down mode */ 9601601b34STomasz Figa tmp = pmu_raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); 9701601b34STomasz Figa tmp &= ~S5P_CENTRAL_LOWPWR_CFG; 9801601b34STomasz Figa pmu_raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); 99c2dd114dSBartlomiej Zolnierkiewicz 100c2dd114dSBartlomiej Zolnierkiewicz /* Setting SEQ_OPTION register */ 101c2dd114dSBartlomiej Zolnierkiewicz pmu_raw_writel(S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0, 102c2dd114dSBartlomiej Zolnierkiewicz S5P_CENTRAL_SEQ_OPTION); 10301601b34STomasz Figa } 10401601b34STomasz Figa 1050d713cf1SBartlomiej Zolnierkiewicz int exynos_pm_central_resume(void) 10601601b34STomasz Figa { 10701601b34STomasz Figa unsigned long tmp; 10801601b34STomasz Figa 10901601b34STomasz Figa /* 11001601b34STomasz Figa * If PMU failed while entering sleep mode, WFI will be 11101601b34STomasz Figa * ignored by PMU and then exiting cpu_do_idle(). 11201601b34STomasz Figa * S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically 11301601b34STomasz Figa * in this situation. 11401601b34STomasz Figa */ 11501601b34STomasz Figa tmp = pmu_raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); 11601601b34STomasz Figa if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) { 11701601b34STomasz Figa tmp |= S5P_CENTRAL_LOWPWR_CFG; 11801601b34STomasz Figa pmu_raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); 11901601b34STomasz Figa /* clear the wakeup state register */ 12001601b34STomasz Figa pmu_raw_writel(0x0, S5P_WAKEUP_STAT); 12101601b34STomasz Figa /* No need to perform below restore code */ 12201601b34STomasz Figa return -1; 12301601b34STomasz Figa } 12401601b34STomasz Figa 12501601b34STomasz Figa return 0; 12601601b34STomasz Figa } 12701601b34STomasz Figa 12801601b34STomasz Figa /* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */ 12901601b34STomasz Figa static void exynos_set_wakeupmask(long mask) 13001601b34STomasz Figa { 13101601b34STomasz Figa pmu_raw_writel(mask, S5P_WAKEUP_MASK); 13201601b34STomasz Figa } 13301601b34STomasz Figa 13401601b34STomasz Figa static void exynos_cpu_set_boot_vector(long flags) 13501601b34STomasz Figa { 136134abc29SBartlomiej Zolnierkiewicz __raw_writel(virt_to_phys(exynos_cpu_resume), 137134abc29SBartlomiej Zolnierkiewicz exynos_boot_vector_addr()); 138134abc29SBartlomiej Zolnierkiewicz __raw_writel(flags, exynos_boot_vector_flag()); 13901601b34STomasz Figa } 14001601b34STomasz Figa 14101601b34STomasz Figa static int exynos_aftr_finisher(unsigned long flags) 14201601b34STomasz Figa { 143a135e201SBartlomiej Zolnierkiewicz int ret; 144a135e201SBartlomiej Zolnierkiewicz 14501601b34STomasz Figa exynos_set_wakeupmask(0x0000ff3e); 14601601b34STomasz Figa /* Set value of power down register for aftr mode */ 14701601b34STomasz Figa exynos_sys_powerdown_conf(SYS_AFTR); 148a135e201SBartlomiej Zolnierkiewicz 149a135e201SBartlomiej Zolnierkiewicz ret = call_firmware_op(do_idle, FW_DO_IDLE_AFTR); 150a135e201SBartlomiej Zolnierkiewicz if (ret == -ENOSYS) { 151a135e201SBartlomiej Zolnierkiewicz if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) 152a135e201SBartlomiej Zolnierkiewicz exynos_cpu_save_register(); 153a135e201SBartlomiej Zolnierkiewicz exynos_cpu_set_boot_vector(S5P_CHECK_AFTR); 15401601b34STomasz Figa cpu_do_idle(); 155a135e201SBartlomiej Zolnierkiewicz } 15601601b34STomasz Figa 15701601b34STomasz Figa return 1; 15801601b34STomasz Figa } 15901601b34STomasz Figa 16001601b34STomasz Figa void exynos_enter_aftr(void) 16101601b34STomasz Figa { 16201601b34STomasz Figa cpu_pm_enter(); 16301601b34STomasz Figa 16401601b34STomasz Figa exynos_pm_central_suspend(); 16501601b34STomasz Figa 16601601b34STomasz Figa cpu_suspend(0, exynos_aftr_finisher); 16701601b34STomasz Figa 16801601b34STomasz Figa if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) { 16901601b34STomasz Figa scu_enable(S5P_VA_SCU); 170a135e201SBartlomiej Zolnierkiewicz if (call_firmware_op(resume) == -ENOSYS) 17101601b34STomasz Figa exynos_cpu_restore_register(); 17201601b34STomasz Figa } 17301601b34STomasz Figa 17401601b34STomasz Figa exynos_pm_central_resume(); 17501601b34STomasz Figa 17601601b34STomasz Figa cpu_pm_exit(); 17701601b34STomasz Figa } 178