| Jaecheol Lee | 3d73998 | 2011-03-16 07:28:23 +0900 | [diff] [blame] | 1 | /* linux/arch/arm/mach-exynos4/cpuidle.c | 
 | 2 |  * | 
 | 3 |  * Copyright (c) 2011 Samsung Electronics Co., Ltd. | 
 | 4 |  *		http://www.samsung.com | 
 | 5 |  * | 
 | 6 |  * This program is free software; you can redistribute it and/or modify | 
 | 7 |  * it under the terms of the GNU General Public License version 2 as | 
 | 8 |  * published by the Free Software Foundation. | 
 | 9 | */ | 
 | 10 |  | 
 | 11 | #include <linux/kernel.h> | 
 | 12 | #include <linux/init.h> | 
 | 13 | #include <linux/cpuidle.h> | 
| Amit Daniel Kachhap | 67173ca4 | 2012-03-08 02:07:27 -0800 | [diff] [blame] | 14 | #include <linux/cpu_pm.h> | 
| Jaecheol Lee | 3d73998 | 2011-03-16 07:28:23 +0900 | [diff] [blame] | 15 | #include <linux/io.h> | 
| Kyungmin Park | 76ee455 | 2011-11-08 19:57:59 +0900 | [diff] [blame] | 16 | #include <linux/export.h> | 
 | 17 | #include <linux/time.h> | 
| Jaecheol Lee | 3d73998 | 2011-03-16 07:28:23 +0900 | [diff] [blame] | 18 |  | 
 | 19 | #include <asm/proc-fns.h> | 
| Amit Daniel Kachhap | 67173ca4 | 2012-03-08 02:07:27 -0800 | [diff] [blame] | 20 | #include <asm/smp_scu.h> | 
 | 21 | #include <asm/suspend.h> | 
 | 22 | #include <asm/unified.h> | 
| Amit Daniel Kachhap | 06c77b3 | 2012-05-12 16:29:21 +0900 | [diff] [blame] | 23 | #include <asm/cpuidle.h> | 
| Abhilash Kesavan | 0f9e035 | 2012-11-20 20:34:58 +0900 | [diff] [blame] | 24 | #include <mach/regs-clock.h> | 
| Amit Daniel Kachhap | 67173ca4 | 2012-03-08 02:07:27 -0800 | [diff] [blame] | 25 | #include <mach/regs-pmu.h> | 
 | 26 | #include <mach/pmu.h> | 
 | 27 |  | 
 | 28 | #include <plat/cpu.h> | 
 | 29 |  | 
 | 30 | #define REG_DIRECTGO_ADDR	(samsung_rev() == EXYNOS4210_REV_1_1 ? \ | 
 | 31 | 			S5P_INFORM7 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \ | 
 | 32 | 			(S5P_VA_SYSRAM + 0x24) : S5P_INFORM0)) | 
 | 33 | #define REG_DIRECTGO_FLAG	(samsung_rev() == EXYNOS4210_REV_1_1 ? \ | 
 | 34 | 			S5P_INFORM6 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \ | 
 | 35 | 			(S5P_VA_SYSRAM + 0x20) : S5P_INFORM1)) | 
 | 36 |  | 
 | 37 | #define S5P_CHECK_AFTR		0xFCBA0D10 | 
| Jaecheol Lee | 3d73998 | 2011-03-16 07:28:23 +0900 | [diff] [blame] | 38 |  | 
| Amit Daniel Kachhap | 67173ca4 | 2012-03-08 02:07:27 -0800 | [diff] [blame] | 39 | static int exynos4_enter_lowpower(struct cpuidle_device *dev, | 
 | 40 | 				struct cpuidle_driver *drv, | 
 | 41 | 				int index); | 
| Jaecheol Lee | 3d73998 | 2011-03-16 07:28:23 +0900 | [diff] [blame] | 42 |  | 
| Amit Daniel Kachhap | 67173ca4 | 2012-03-08 02:07:27 -0800 | [diff] [blame] | 43 | static struct cpuidle_state exynos4_cpuidle_set[] __initdata = { | 
| Amit Daniel Kachhap | 06c77b3 | 2012-05-12 16:29:21 +0900 | [diff] [blame] | 44 | 	[0] = ARM_CPUIDLE_WFI_STATE, | 
| Amit Daniel Kachhap | 67173ca4 | 2012-03-08 02:07:27 -0800 | [diff] [blame] | 45 | 	[1] = { | 
 | 46 | 		.enter			= exynos4_enter_lowpower, | 
 | 47 | 		.exit_latency		= 300, | 
 | 48 | 		.target_residency	= 100000, | 
 | 49 | 		.flags			= CPUIDLE_FLAG_TIME_VALID, | 
 | 50 | 		.name			= "C1", | 
 | 51 | 		.desc			= "ARM power down", | 
 | 52 | 	}, | 
| Jaecheol Lee | 3d73998 | 2011-03-16 07:28:23 +0900 | [diff] [blame] | 53 | }; | 
 | 54 |  | 
 | 55 | static DEFINE_PER_CPU(struct cpuidle_device, exynos4_cpuidle_device); | 
 | 56 |  | 
 | 57 | static struct cpuidle_driver exynos4_idle_driver = { | 
| Amit Daniel Kachhap | 06c77b3 | 2012-05-12 16:29:21 +0900 | [diff] [blame] | 58 | 	.name			= "exynos4_idle", | 
 | 59 | 	.owner			= THIS_MODULE, | 
 | 60 | 	.en_core_tk_irqen	= 1, | 
| Jaecheol Lee | 3d73998 | 2011-03-16 07:28:23 +0900 | [diff] [blame] | 61 | }; | 
 | 62 |  | 
| Amit Daniel Kachhap | 67173ca4 | 2012-03-08 02:07:27 -0800 | [diff] [blame] | 63 | /* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */ | 
 | 64 | static void exynos4_set_wakeupmask(void) | 
 | 65 | { | 
 | 66 | 	__raw_writel(0x0000ff3e, S5P_WAKEUP_MASK); | 
 | 67 | } | 
 | 68 |  | 
 | 69 | static unsigned int g_pwr_ctrl, g_diag_reg; | 
 | 70 |  | 
 | 71 | static void save_cpu_arch_register(void) | 
 | 72 | { | 
 | 73 | 	/*read power control register*/ | 
 | 74 | 	asm("mrc p15, 0, %0, c15, c0, 0" : "=r"(g_pwr_ctrl) : : "cc"); | 
 | 75 | 	/*read diagnostic register*/ | 
 | 76 | 	asm("mrc p15, 0, %0, c15, c0, 1" : "=r"(g_diag_reg) : : "cc"); | 
 | 77 | 	return; | 
 | 78 | } | 
 | 79 |  | 
 | 80 | static void restore_cpu_arch_register(void) | 
 | 81 | { | 
 | 82 | 	/*write power control register*/ | 
 | 83 | 	asm("mcr p15, 0, %0, c15, c0, 0" : : "r"(g_pwr_ctrl) : "cc"); | 
 | 84 | 	/*write diagnostic register*/ | 
 | 85 | 	asm("mcr p15, 0, %0, c15, c0, 1" : : "r"(g_diag_reg) : "cc"); | 
 | 86 | 	return; | 
 | 87 | } | 
 | 88 |  | 
 | 89 | static int idle_finisher(unsigned long flags) | 
 | 90 | { | 
 | 91 | 	cpu_do_idle(); | 
 | 92 | 	return 1; | 
 | 93 | } | 
 | 94 |  | 
 | 95 | static int exynos4_enter_core0_aftr(struct cpuidle_device *dev, | 
 | 96 | 				struct cpuidle_driver *drv, | 
 | 97 | 				int index) | 
 | 98 | { | 
| Amit Daniel Kachhap | 67173ca4 | 2012-03-08 02:07:27 -0800 | [diff] [blame] | 99 | 	unsigned long tmp; | 
 | 100 |  | 
| Amit Daniel Kachhap | 67173ca4 | 2012-03-08 02:07:27 -0800 | [diff] [blame] | 101 | 	exynos4_set_wakeupmask(); | 
 | 102 |  | 
 | 103 | 	/* Set value of power down register for aftr mode */ | 
| Jongpill Lee | 7d44d2b | 2012-02-17 09:51:31 +0900 | [diff] [blame] | 104 | 	exynos_sys_powerdown_conf(SYS_AFTR); | 
| Amit Daniel Kachhap | 67173ca4 | 2012-03-08 02:07:27 -0800 | [diff] [blame] | 105 |  | 
 | 106 | 	__raw_writel(virt_to_phys(s3c_cpu_resume), REG_DIRECTGO_ADDR); | 
 | 107 | 	__raw_writel(S5P_CHECK_AFTR, REG_DIRECTGO_FLAG); | 
 | 108 |  | 
 | 109 | 	save_cpu_arch_register(); | 
 | 110 |  | 
 | 111 | 	/* Setting Central Sequence Register for power down mode */ | 
 | 112 | 	tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); | 
 | 113 | 	tmp &= ~S5P_CENTRAL_LOWPWR_CFG; | 
 | 114 | 	__raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); | 
 | 115 |  | 
 | 116 | 	cpu_pm_enter(); | 
 | 117 | 	cpu_suspend(0, idle_finisher); | 
 | 118 |  | 
 | 119 | #ifdef CONFIG_SMP | 
| Abhilash Kesavan | a633208 | 2012-11-22 14:46:34 +0900 | [diff] [blame] | 120 | 	if (!soc_is_exynos5250()) | 
 | 121 | 		scu_enable(S5P_VA_SCU); | 
| Amit Daniel Kachhap | 67173ca4 | 2012-03-08 02:07:27 -0800 | [diff] [blame] | 122 | #endif | 
 | 123 | 	cpu_pm_exit(); | 
 | 124 |  | 
 | 125 | 	restore_cpu_arch_register(); | 
 | 126 |  | 
 | 127 | 	/* | 
 | 128 | 	 * If PMU failed while entering sleep mode, WFI will be | 
 | 129 | 	 * ignored by PMU and then exiting cpu_do_idle(). | 
 | 130 | 	 * S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically | 
 | 131 | 	 * in this situation. | 
 | 132 | 	 */ | 
 | 133 | 	tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION); | 
 | 134 | 	if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) { | 
 | 135 | 		tmp |= S5P_CENTRAL_LOWPWR_CFG; | 
 | 136 | 		__raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION); | 
 | 137 | 	} | 
 | 138 |  | 
 | 139 | 	/* Clear wakeup state register */ | 
 | 140 | 	__raw_writel(0x0, S5P_WAKEUP_STAT); | 
 | 141 |  | 
| Deepthi Dharwar | e978aa7 | 2011-10-28 16:20:09 +0530 | [diff] [blame] | 142 | 	return index; | 
| Jaecheol Lee | 3d73998 | 2011-03-16 07:28:23 +0900 | [diff] [blame] | 143 | } | 
 | 144 |  | 
| Amit Daniel Kachhap | 67173ca4 | 2012-03-08 02:07:27 -0800 | [diff] [blame] | 145 | static int exynos4_enter_lowpower(struct cpuidle_device *dev, | 
 | 146 | 				struct cpuidle_driver *drv, | 
 | 147 | 				int index) | 
 | 148 | { | 
 | 149 | 	int new_index = index; | 
 | 150 |  | 
 | 151 | 	/* This mode only can be entered when other core's are offline */ | 
 | 152 | 	if (num_online_cpus() > 1) | 
 | 153 | 		new_index = drv->safe_state_index; | 
 | 154 |  | 
 | 155 | 	if (new_index == 0) | 
| Amit Daniel Kachhap | 06c77b3 | 2012-05-12 16:29:21 +0900 | [diff] [blame] | 156 | 		return arm_cpuidle_simple_enter(dev, drv, new_index); | 
| Amit Daniel Kachhap | 67173ca4 | 2012-03-08 02:07:27 -0800 | [diff] [blame] | 157 | 	else | 
 | 158 | 		return exynos4_enter_core0_aftr(dev, drv, new_index); | 
 | 159 | } | 
 | 160 |  | 
| Abhilash Kesavan | 0f9e035 | 2012-11-20 20:34:58 +0900 | [diff] [blame] | 161 | static void __init exynos5_core_down_clk(void) | 
 | 162 | { | 
 | 163 | 	unsigned int tmp; | 
 | 164 |  | 
 | 165 | 	/* | 
 | 166 | 	 * Enable arm clock down (in idle) and set arm divider | 
 | 167 | 	 * ratios in WFI/WFE state. | 
 | 168 | 	 */ | 
 | 169 | 	tmp = PWR_CTRL1_CORE2_DOWN_RATIO | \ | 
 | 170 | 	      PWR_CTRL1_CORE1_DOWN_RATIO | \ | 
 | 171 | 	      PWR_CTRL1_DIV2_DOWN_EN	 | \ | 
 | 172 | 	      PWR_CTRL1_DIV1_DOWN_EN	 | \ | 
 | 173 | 	      PWR_CTRL1_USE_CORE1_WFE	 | \ | 
 | 174 | 	      PWR_CTRL1_USE_CORE0_WFE	 | \ | 
 | 175 | 	      PWR_CTRL1_USE_CORE1_WFI	 | \ | 
 | 176 | 	      PWR_CTRL1_USE_CORE0_WFI; | 
 | 177 | 	__raw_writel(tmp, EXYNOS5_PWR_CTRL1); | 
 | 178 |  | 
 | 179 | 	/* | 
 | 180 | 	 * Enable arm clock up (on exiting idle). Set arm divider | 
 | 181 | 	 * ratios when not in idle along with the standby duration | 
 | 182 | 	 * ratios. | 
 | 183 | 	 */ | 
 | 184 | 	tmp = PWR_CTRL2_DIV2_UP_EN	 | \ | 
 | 185 | 	      PWR_CTRL2_DIV1_UP_EN	 | \ | 
 | 186 | 	      PWR_CTRL2_DUR_STANDBY2_VAL | \ | 
 | 187 | 	      PWR_CTRL2_DUR_STANDBY1_VAL | \ | 
 | 188 | 	      PWR_CTRL2_CORE2_UP_RATIO	 | \ | 
 | 189 | 	      PWR_CTRL2_CORE1_UP_RATIO; | 
 | 190 | 	__raw_writel(tmp, EXYNOS5_PWR_CTRL2); | 
 | 191 | } | 
 | 192 |  | 
| Jaecheol Lee | 3d73998 | 2011-03-16 07:28:23 +0900 | [diff] [blame] | 193 | static int __init exynos4_init_cpuidle(void) | 
 | 194 | { | 
 | 195 | 	int i, max_cpuidle_state, cpu_id; | 
 | 196 | 	struct cpuidle_device *device; | 
| Deepthi Dharwar | 46bcfad | 2011-10-28 16:20:42 +0530 | [diff] [blame] | 197 | 	struct cpuidle_driver *drv = &exynos4_idle_driver; | 
| Jaecheol Lee | 3d73998 | 2011-03-16 07:28:23 +0900 | [diff] [blame] | 198 |  | 
| Abhilash Kesavan | 0f9e035 | 2012-11-20 20:34:58 +0900 | [diff] [blame] | 199 | 	if (soc_is_exynos5250()) | 
 | 200 | 		exynos5_core_down_clk(); | 
 | 201 |  | 
| Deepthi Dharwar | 46bcfad | 2011-10-28 16:20:42 +0530 | [diff] [blame] | 202 | 	/* Setup cpuidle driver */ | 
 | 203 | 	drv->state_count = (sizeof(exynos4_cpuidle_set) / | 
 | 204 | 				       sizeof(struct cpuidle_state)); | 
 | 205 | 	max_cpuidle_state = drv->state_count; | 
 | 206 | 	for (i = 0; i < max_cpuidle_state; i++) { | 
 | 207 | 		memcpy(&drv->states[i], &exynos4_cpuidle_set[i], | 
 | 208 | 				sizeof(struct cpuidle_state)); | 
 | 209 | 	} | 
| Amit Daniel Kachhap | 67173ca4 | 2012-03-08 02:07:27 -0800 | [diff] [blame] | 210 | 	drv->safe_state_index = 0; | 
| Jaecheol Lee | 3d73998 | 2011-03-16 07:28:23 +0900 | [diff] [blame] | 211 | 	cpuidle_register_driver(&exynos4_idle_driver); | 
 | 212 |  | 
 | 213 | 	for_each_cpu(cpu_id, cpu_online_mask) { | 
 | 214 | 		device = &per_cpu(exynos4_cpuidle_device, cpu_id); | 
 | 215 | 		device->cpu = cpu_id; | 
 | 216 |  | 
| Amit Daniel Kachhap | 67173ca4 | 2012-03-08 02:07:27 -0800 | [diff] [blame] | 217 | 		if (cpu_id == 0) | 
 | 218 | 			device->state_count = (sizeof(exynos4_cpuidle_set) / | 
 | 219 | 					       sizeof(struct cpuidle_state)); | 
 | 220 | 		else | 
 | 221 | 			device->state_count = 1;	/* Support IDLE only */ | 
| Jaecheol Lee | 3d73998 | 2011-03-16 07:28:23 +0900 | [diff] [blame] | 222 |  | 
 | 223 | 		if (cpuidle_register_device(device)) { | 
 | 224 | 			printk(KERN_ERR "CPUidle register device failed\n,"); | 
 | 225 | 			return -EIO; | 
 | 226 | 		} | 
 | 227 | 	} | 
| Amit Daniel Kachhap | 67173ca4 | 2012-03-08 02:07:27 -0800 | [diff] [blame] | 228 |  | 
| Jaecheol Lee | 3d73998 | 2011-03-16 07:28:23 +0900 | [diff] [blame] | 229 | 	return 0; | 
 | 230 | } | 
 | 231 | device_initcall(exynos4_init_cpuidle); |