| Kevin Hilman | 8bd2294 | 2009-05-28 10:56:16 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * OMAP3 Power Management Routines | 
 | 3 |  * | 
 | 4 |  * Copyright (C) 2006-2008 Nokia Corporation | 
 | 5 |  * Tony Lindgren <tony@atomide.com> | 
 | 6 |  * Jouni Hogander | 
 | 7 |  * | 
 | 8 |  * Copyright (C) 2005 Texas Instruments, Inc. | 
 | 9 |  * Richard Woodruff <r-woodruff2@ti.com> | 
 | 10 |  * | 
 | 11 |  * Based on pm.c for omap1 | 
 | 12 |  * | 
 | 13 |  * This program is free software; you can redistribute it and/or modify | 
 | 14 |  * it under the terms of the GNU General Public License version 2 as | 
 | 15 |  * published by the Free Software Foundation. | 
 | 16 |  */ | 
 | 17 |  | 
 | 18 | #include <linux/pm.h> | 
 | 19 | #include <linux/suspend.h> | 
 | 20 | #include <linux/interrupt.h> | 
 | 21 | #include <linux/module.h> | 
 | 22 | #include <linux/list.h> | 
 | 23 | #include <linux/err.h> | 
 | 24 | #include <linux/gpio.h> | 
 | 25 |  | 
 | 26 | #include <mach/sram.h> | 
 | 27 | #include <mach/clockdomain.h> | 
 | 28 | #include <mach/powerdomain.h> | 
 | 29 | #include <mach/control.h> | 
| Kevin Hilman | 4af4016 | 2009-02-04 10:51:40 -0800 | [diff] [blame] | 30 | #include <mach/serial.h> | 
| Kevin Hilman | 8bd2294 | 2009-05-28 10:56:16 -0700 | [diff] [blame] | 31 |  | 
 | 32 | #include "cm.h" | 
 | 33 | #include "cm-regbits-34xx.h" | 
 | 34 | #include "prm-regbits-34xx.h" | 
 | 35 |  | 
 | 36 | #include "prm.h" | 
 | 37 | #include "pm.h" | 
 | 38 |  | 
 | 39 | struct power_state { | 
 | 40 | 	struct powerdomain *pwrdm; | 
 | 41 | 	u32 next_state; | 
 | 42 | 	u32 saved_state; | 
 | 43 | 	struct list_head node; | 
 | 44 | }; | 
 | 45 |  | 
 | 46 | static LIST_HEAD(pwrst_list); | 
 | 47 |  | 
 | 48 | static void (*_omap_sram_idle)(u32 *addr, int save_state); | 
 | 49 |  | 
 | 50 | static struct powerdomain *mpu_pwrdm; | 
 | 51 |  | 
 | 52 | /* PRCM Interrupt Handler for wakeups */ | 
 | 53 | static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id) | 
 | 54 | { | 
 | 55 | 	u32 wkst, irqstatus_mpu; | 
 | 56 | 	u32 fclk, iclk; | 
 | 57 |  | 
 | 58 | 	/* WKUP */ | 
 | 59 | 	wkst = prm_read_mod_reg(WKUP_MOD, PM_WKST); | 
 | 60 | 	if (wkst) { | 
 | 61 | 		iclk = cm_read_mod_reg(WKUP_MOD, CM_ICLKEN); | 
 | 62 | 		fclk = cm_read_mod_reg(WKUP_MOD, CM_FCLKEN); | 
 | 63 | 		cm_set_mod_reg_bits(wkst, WKUP_MOD, CM_ICLKEN); | 
 | 64 | 		cm_set_mod_reg_bits(wkst, WKUP_MOD, CM_FCLKEN); | 
 | 65 | 		prm_write_mod_reg(wkst, WKUP_MOD, PM_WKST); | 
 | 66 | 		while (prm_read_mod_reg(WKUP_MOD, PM_WKST)) | 
 | 67 | 			cpu_relax(); | 
 | 68 | 		cm_write_mod_reg(iclk, WKUP_MOD, CM_ICLKEN); | 
 | 69 | 		cm_write_mod_reg(fclk, WKUP_MOD, CM_FCLKEN); | 
 | 70 | 	} | 
 | 71 |  | 
 | 72 | 	/* CORE */ | 
 | 73 | 	wkst = prm_read_mod_reg(CORE_MOD, PM_WKST1); | 
 | 74 | 	if (wkst) { | 
 | 75 | 		iclk = cm_read_mod_reg(CORE_MOD, CM_ICLKEN1); | 
 | 76 | 		fclk = cm_read_mod_reg(CORE_MOD, CM_FCLKEN1); | 
 | 77 | 		cm_set_mod_reg_bits(wkst, CORE_MOD, CM_ICLKEN1); | 
 | 78 | 		cm_set_mod_reg_bits(wkst, CORE_MOD, CM_FCLKEN1); | 
 | 79 | 		prm_write_mod_reg(wkst, CORE_MOD, PM_WKST1); | 
 | 80 | 		while (prm_read_mod_reg(CORE_MOD, PM_WKST1)) | 
 | 81 | 			cpu_relax(); | 
 | 82 | 		cm_write_mod_reg(iclk, CORE_MOD, CM_ICLKEN1); | 
 | 83 | 		cm_write_mod_reg(fclk, CORE_MOD, CM_FCLKEN1); | 
 | 84 | 	} | 
 | 85 | 	wkst = prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_WKST3); | 
 | 86 | 	if (wkst) { | 
 | 87 | 		iclk = cm_read_mod_reg(CORE_MOD, CM_ICLKEN3); | 
 | 88 | 		fclk = cm_read_mod_reg(CORE_MOD, OMAP3430ES2_CM_FCLKEN3); | 
 | 89 | 		cm_set_mod_reg_bits(wkst, CORE_MOD, CM_ICLKEN3); | 
 | 90 | 		cm_set_mod_reg_bits(wkst, CORE_MOD, OMAP3430ES2_CM_FCLKEN3); | 
 | 91 | 		prm_write_mod_reg(wkst, CORE_MOD, OMAP3430ES2_PM_WKST3); | 
 | 92 | 		while (prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_WKST3)) | 
 | 93 | 			cpu_relax(); | 
 | 94 | 		cm_write_mod_reg(iclk, CORE_MOD, CM_ICLKEN3); | 
 | 95 | 		cm_write_mod_reg(fclk, CORE_MOD, OMAP3430ES2_CM_FCLKEN3); | 
 | 96 | 	} | 
 | 97 |  | 
 | 98 | 	/* PER */ | 
 | 99 | 	wkst = prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKST); | 
 | 100 | 	if (wkst) { | 
 | 101 | 		iclk = cm_read_mod_reg(OMAP3430_PER_MOD, CM_ICLKEN); | 
 | 102 | 		fclk = cm_read_mod_reg(OMAP3430_PER_MOD, CM_FCLKEN); | 
 | 103 | 		cm_set_mod_reg_bits(wkst, OMAP3430_PER_MOD, CM_ICLKEN); | 
 | 104 | 		cm_set_mod_reg_bits(wkst, OMAP3430_PER_MOD, CM_FCLKEN); | 
 | 105 | 		prm_write_mod_reg(wkst, OMAP3430_PER_MOD, PM_WKST); | 
 | 106 | 		while (prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKST)) | 
 | 107 | 			cpu_relax(); | 
 | 108 | 		cm_write_mod_reg(iclk, OMAP3430_PER_MOD, CM_ICLKEN); | 
 | 109 | 		cm_write_mod_reg(fclk, OMAP3430_PER_MOD, CM_FCLKEN); | 
 | 110 | 	} | 
 | 111 |  | 
 | 112 | 	if (omap_rev() > OMAP3430_REV_ES1_0) { | 
 | 113 | 		/* USBHOST */ | 
 | 114 | 		wkst = prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, PM_WKST); | 
 | 115 | 		if (wkst) { | 
 | 116 | 			iclk = cm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, | 
 | 117 | 					       CM_ICLKEN); | 
 | 118 | 			fclk = cm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, | 
 | 119 | 					       CM_FCLKEN); | 
 | 120 | 			cm_set_mod_reg_bits(wkst, OMAP3430ES2_USBHOST_MOD, | 
 | 121 | 					    CM_ICLKEN); | 
 | 122 | 			cm_set_mod_reg_bits(wkst, OMAP3430ES2_USBHOST_MOD, | 
 | 123 | 					    CM_FCLKEN); | 
 | 124 | 			prm_write_mod_reg(wkst, OMAP3430ES2_USBHOST_MOD, | 
 | 125 | 					  PM_WKST); | 
 | 126 | 			while (prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, | 
 | 127 | 						PM_WKST)) | 
 | 128 | 				cpu_relax(); | 
 | 129 | 			cm_write_mod_reg(iclk, OMAP3430ES2_USBHOST_MOD, | 
 | 130 | 					 CM_ICLKEN); | 
 | 131 | 			cm_write_mod_reg(fclk, OMAP3430ES2_USBHOST_MOD, | 
 | 132 | 					 CM_FCLKEN); | 
 | 133 | 		} | 
 | 134 | 	} | 
 | 135 |  | 
 | 136 | 	irqstatus_mpu = prm_read_mod_reg(OCP_MOD, | 
 | 137 | 					 OMAP3_PRM_IRQSTATUS_MPU_OFFSET); | 
 | 138 | 	prm_write_mod_reg(irqstatus_mpu, OCP_MOD, | 
 | 139 | 			  OMAP3_PRM_IRQSTATUS_MPU_OFFSET); | 
 | 140 |  | 
 | 141 | 	while (prm_read_mod_reg(OCP_MOD, OMAP3_PRM_IRQSTATUS_MPU_OFFSET)) | 
 | 142 | 		cpu_relax(); | 
 | 143 |  | 
 | 144 | 	return IRQ_HANDLED; | 
 | 145 | } | 
 | 146 |  | 
 | 147 | static void omap_sram_idle(void) | 
 | 148 | { | 
 | 149 | 	/* Variable to tell what needs to be saved and restored | 
 | 150 | 	 * in omap_sram_idle*/ | 
 | 151 | 	/* save_state = 0 => Nothing to save and restored */ | 
 | 152 | 	/* save_state = 1 => Only L1 and logic lost */ | 
 | 153 | 	/* save_state = 2 => Only L2 lost */ | 
 | 154 | 	/* save_state = 3 => L1, L2 and logic lost */ | 
 | 155 | 	int save_state = 0, mpu_next_state; | 
 | 156 |  | 
 | 157 | 	if (!_omap_sram_idle) | 
 | 158 | 		return; | 
 | 159 |  | 
 | 160 | 	mpu_next_state = pwrdm_read_next_pwrst(mpu_pwrdm); | 
 | 161 | 	switch (mpu_next_state) { | 
 | 162 | 	case PWRDM_POWER_RET: | 
 | 163 | 		/* No need to save context */ | 
 | 164 | 		save_state = 0; | 
 | 165 | 		break; | 
 | 166 | 	default: | 
 | 167 | 		/* Invalid state */ | 
 | 168 | 		printk(KERN_ERR "Invalid mpu state in sram_idle\n"); | 
 | 169 | 		return; | 
 | 170 | 	} | 
 | 171 | 	omap2_gpio_prepare_for_retention(); | 
| Kevin Hilman | 4af4016 | 2009-02-04 10:51:40 -0800 | [diff] [blame] | 172 | 	omap_uart_prepare_idle(0); | 
 | 173 | 	omap_uart_prepare_idle(1); | 
 | 174 | 	omap_uart_prepare_idle(2); | 
| Kevin Hilman | 8bd2294 | 2009-05-28 10:56:16 -0700 | [diff] [blame] | 175 |  | 
 | 176 | 	_omap_sram_idle(NULL, save_state); | 
 | 177 | 	cpu_init(); | 
 | 178 |  | 
| Kevin Hilman | 4af4016 | 2009-02-04 10:51:40 -0800 | [diff] [blame] | 179 | 	omap_uart_resume_idle(2); | 
 | 180 | 	omap_uart_resume_idle(1); | 
 | 181 | 	omap_uart_resume_idle(0); | 
| Kevin Hilman | 8bd2294 | 2009-05-28 10:56:16 -0700 | [diff] [blame] | 182 | 	omap2_gpio_resume_after_retention(); | 
 | 183 | } | 
 | 184 |  | 
 | 185 | /* | 
 | 186 |  * Check if functional clocks are enabled before entering | 
 | 187 |  * sleep. This function could be behind CONFIG_PM_DEBUG | 
 | 188 |  * when all drivers are configuring their sysconfig registers | 
 | 189 |  * properly and using their clocks properly. | 
 | 190 |  */ | 
 | 191 | static int omap3_fclks_active(void) | 
 | 192 | { | 
 | 193 | 	u32 fck_core1 = 0, fck_core3 = 0, fck_sgx = 0, fck_dss = 0, | 
 | 194 | 		fck_cam = 0, fck_per = 0, fck_usbhost = 0; | 
 | 195 |  | 
 | 196 | 	fck_core1 = cm_read_mod_reg(CORE_MOD, | 
 | 197 | 				    CM_FCLKEN1); | 
 | 198 | 	if (omap_rev() > OMAP3430_REV_ES1_0) { | 
 | 199 | 		fck_core3 = cm_read_mod_reg(CORE_MOD, | 
 | 200 | 					    OMAP3430ES2_CM_FCLKEN3); | 
 | 201 | 		fck_sgx = cm_read_mod_reg(OMAP3430ES2_SGX_MOD, | 
 | 202 | 					  CM_FCLKEN); | 
 | 203 | 		fck_usbhost = cm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, | 
 | 204 | 					      CM_FCLKEN); | 
 | 205 | 	} else | 
 | 206 | 		fck_sgx = cm_read_mod_reg(GFX_MOD, | 
 | 207 | 					  OMAP3430ES2_CM_FCLKEN3); | 
 | 208 | 	fck_dss = cm_read_mod_reg(OMAP3430_DSS_MOD, | 
 | 209 | 				  CM_FCLKEN); | 
 | 210 | 	fck_cam = cm_read_mod_reg(OMAP3430_CAM_MOD, | 
 | 211 | 				  CM_FCLKEN); | 
 | 212 | 	fck_per = cm_read_mod_reg(OMAP3430_PER_MOD, | 
 | 213 | 				  CM_FCLKEN); | 
| Kevin Hilman | 4af4016 | 2009-02-04 10:51:40 -0800 | [diff] [blame] | 214 |  | 
 | 215 | 	/* Ignore UART clocks.  These are handled by UART core (serial.c) */ | 
 | 216 | 	fck_core1 &= ~(OMAP3430_EN_UART1 | OMAP3430_EN_UART2); | 
 | 217 | 	fck_per &= ~OMAP3430_EN_UART3; | 
 | 218 |  | 
| Kevin Hilman | 8bd2294 | 2009-05-28 10:56:16 -0700 | [diff] [blame] | 219 | 	if (fck_core1 | fck_core3 | fck_sgx | fck_dss | | 
 | 220 | 	    fck_cam | fck_per | fck_usbhost) | 
 | 221 | 		return 1; | 
 | 222 | 	return 0; | 
 | 223 | } | 
 | 224 |  | 
 | 225 | static int omap3_can_sleep(void) | 
 | 226 | { | 
| Kevin Hilman | 4af4016 | 2009-02-04 10:51:40 -0800 | [diff] [blame] | 227 | 	if (!omap_uart_can_sleep()) | 
 | 228 | 		return 0; | 
| Kevin Hilman | 8bd2294 | 2009-05-28 10:56:16 -0700 | [diff] [blame] | 229 | 	if (omap3_fclks_active()) | 
 | 230 | 		return 0; | 
 | 231 | 	return 1; | 
 | 232 | } | 
 | 233 |  | 
 | 234 | /* This sets pwrdm state (other than mpu & core. Currently only ON & | 
 | 235 |  * RET are supported. Function is assuming that clkdm doesn't have | 
 | 236 |  * hw_sup mode enabled. */ | 
 | 237 | static int set_pwrdm_state(struct powerdomain *pwrdm, u32 state) | 
 | 238 | { | 
 | 239 | 	u32 cur_state; | 
 | 240 | 	int sleep_switch = 0; | 
 | 241 | 	int ret = 0; | 
 | 242 |  | 
 | 243 | 	if (pwrdm == NULL || IS_ERR(pwrdm)) | 
 | 244 | 		return -EINVAL; | 
 | 245 |  | 
 | 246 | 	while (!(pwrdm->pwrsts & (1 << state))) { | 
 | 247 | 		if (state == PWRDM_POWER_OFF) | 
 | 248 | 			return ret; | 
 | 249 | 		state--; | 
 | 250 | 	} | 
 | 251 |  | 
 | 252 | 	cur_state = pwrdm_read_next_pwrst(pwrdm); | 
 | 253 | 	if (cur_state == state) | 
 | 254 | 		return ret; | 
 | 255 |  | 
 | 256 | 	if (pwrdm_read_pwrst(pwrdm) < PWRDM_POWER_ON) { | 
 | 257 | 		omap2_clkdm_wakeup(pwrdm->pwrdm_clkdms[0]); | 
 | 258 | 		sleep_switch = 1; | 
 | 259 | 		pwrdm_wait_transition(pwrdm); | 
 | 260 | 	} | 
 | 261 |  | 
 | 262 | 	ret = pwrdm_set_next_pwrst(pwrdm, state); | 
 | 263 | 	if (ret) { | 
 | 264 | 		printk(KERN_ERR "Unable to set state of powerdomain: %s\n", | 
 | 265 | 		       pwrdm->name); | 
 | 266 | 		goto err; | 
 | 267 | 	} | 
 | 268 |  | 
 | 269 | 	if (sleep_switch) { | 
 | 270 | 		omap2_clkdm_allow_idle(pwrdm->pwrdm_clkdms[0]); | 
 | 271 | 		pwrdm_wait_transition(pwrdm); | 
 | 272 | 	} | 
 | 273 |  | 
 | 274 | err: | 
 | 275 | 	return ret; | 
 | 276 | } | 
 | 277 |  | 
 | 278 | static void omap3_pm_idle(void) | 
 | 279 | { | 
 | 280 | 	local_irq_disable(); | 
 | 281 | 	local_fiq_disable(); | 
 | 282 |  | 
 | 283 | 	if (!omap3_can_sleep()) | 
 | 284 | 		goto out; | 
 | 285 |  | 
 | 286 | 	if (omap_irq_pending()) | 
 | 287 | 		goto out; | 
 | 288 |  | 
 | 289 | 	omap_sram_idle(); | 
 | 290 |  | 
 | 291 | out: | 
 | 292 | 	local_fiq_enable(); | 
 | 293 | 	local_irq_enable(); | 
 | 294 | } | 
 | 295 |  | 
 | 296 | static int omap3_pm_prepare(void) | 
 | 297 | { | 
 | 298 | 	disable_hlt(); | 
 | 299 | 	return 0; | 
 | 300 | } | 
 | 301 |  | 
 | 302 | static int omap3_pm_suspend(void) | 
 | 303 | { | 
 | 304 | 	struct power_state *pwrst; | 
 | 305 | 	int state, ret = 0; | 
 | 306 |  | 
 | 307 | 	/* Read current next_pwrsts */ | 
 | 308 | 	list_for_each_entry(pwrst, &pwrst_list, node) | 
 | 309 | 		pwrst->saved_state = pwrdm_read_next_pwrst(pwrst->pwrdm); | 
 | 310 | 	/* Set ones wanted by suspend */ | 
 | 311 | 	list_for_each_entry(pwrst, &pwrst_list, node) { | 
 | 312 | 		if (set_pwrdm_state(pwrst->pwrdm, pwrst->next_state)) | 
 | 313 | 			goto restore; | 
 | 314 | 		if (pwrdm_clear_all_prev_pwrst(pwrst->pwrdm)) | 
 | 315 | 			goto restore; | 
 | 316 | 	} | 
 | 317 |  | 
| Kevin Hilman | 4af4016 | 2009-02-04 10:51:40 -0800 | [diff] [blame] | 318 | 	omap_uart_prepare_suspend(); | 
| Kevin Hilman | 8bd2294 | 2009-05-28 10:56:16 -0700 | [diff] [blame] | 319 | 	omap_sram_idle(); | 
 | 320 |  | 
 | 321 | restore: | 
 | 322 | 	/* Restore next_pwrsts */ | 
 | 323 | 	list_for_each_entry(pwrst, &pwrst_list, node) { | 
 | 324 | 		set_pwrdm_state(pwrst->pwrdm, pwrst->saved_state); | 
 | 325 | 		state = pwrdm_read_prev_pwrst(pwrst->pwrdm); | 
 | 326 | 		if (state > pwrst->next_state) { | 
 | 327 | 			printk(KERN_INFO "Powerdomain (%s) didn't enter " | 
 | 328 | 			       "target state %d\n", | 
 | 329 | 			       pwrst->pwrdm->name, pwrst->next_state); | 
 | 330 | 			ret = -1; | 
 | 331 | 		} | 
 | 332 | 	} | 
 | 333 | 	if (ret) | 
 | 334 | 		printk(KERN_ERR "Could not enter target state in pm_suspend\n"); | 
 | 335 | 	else | 
 | 336 | 		printk(KERN_INFO "Successfully put all powerdomains " | 
 | 337 | 		       "to target state\n"); | 
 | 338 |  | 
 | 339 | 	return ret; | 
 | 340 | } | 
 | 341 |  | 
 | 342 | static int omap3_pm_enter(suspend_state_t state) | 
 | 343 | { | 
 | 344 | 	int ret = 0; | 
 | 345 |  | 
 | 346 | 	switch (state) { | 
 | 347 | 	case PM_SUSPEND_STANDBY: | 
 | 348 | 	case PM_SUSPEND_MEM: | 
 | 349 | 		ret = omap3_pm_suspend(); | 
 | 350 | 		break; | 
 | 351 | 	default: | 
 | 352 | 		ret = -EINVAL; | 
 | 353 | 	} | 
 | 354 |  | 
 | 355 | 	return ret; | 
 | 356 | } | 
 | 357 |  | 
 | 358 | static void omap3_pm_finish(void) | 
 | 359 | { | 
 | 360 | 	enable_hlt(); | 
 | 361 | } | 
 | 362 |  | 
 | 363 | static struct platform_suspend_ops omap_pm_ops = { | 
 | 364 | 	.prepare	= omap3_pm_prepare, | 
 | 365 | 	.enter		= omap3_pm_enter, | 
 | 366 | 	.finish		= omap3_pm_finish, | 
 | 367 | 	.valid		= suspend_valid_only_mem, | 
 | 368 | }; | 
 | 369 |  | 
| Kevin Hilman | 1155e42 | 2008-11-25 11:48:24 -0800 | [diff] [blame] | 370 |  | 
 | 371 | /** | 
 | 372 |  * omap3_iva_idle(): ensure IVA is in idle so it can be put into | 
 | 373 |  *                   retention | 
 | 374 |  * | 
 | 375 |  * In cases where IVA2 is activated by bootcode, it may prevent | 
 | 376 |  * full-chip retention or off-mode because it is not idle.  This | 
 | 377 |  * function forces the IVA2 into idle state so it can go | 
 | 378 |  * into retention/off and thus allow full-chip retention/off. | 
 | 379 |  * | 
 | 380 |  **/ | 
 | 381 | static void __init omap3_iva_idle(void) | 
 | 382 | { | 
 | 383 | 	/* ensure IVA2 clock is disabled */ | 
 | 384 | 	cm_write_mod_reg(0, OMAP3430_IVA2_MOD, CM_FCLKEN); | 
 | 385 |  | 
 | 386 | 	/* if no clock activity, nothing else to do */ | 
 | 387 | 	if (!(cm_read_mod_reg(OMAP3430_IVA2_MOD, OMAP3430_CM_CLKSTST) & | 
 | 388 | 	      OMAP3430_CLKACTIVITY_IVA2_MASK)) | 
 | 389 | 		return; | 
 | 390 |  | 
 | 391 | 	/* Reset IVA2 */ | 
 | 392 | 	prm_write_mod_reg(OMAP3430_RST1_IVA2 | | 
 | 393 | 			  OMAP3430_RST2_IVA2 | | 
 | 394 | 			  OMAP3430_RST3_IVA2, | 
 | 395 | 			  OMAP3430_IVA2_MOD, RM_RSTCTRL); | 
 | 396 |  | 
 | 397 | 	/* Enable IVA2 clock */ | 
 | 398 | 	cm_write_mod_reg(OMAP3430_CM_FCLKEN_IVA2_EN_IVA2, | 
 | 399 | 			 OMAP3430_IVA2_MOD, CM_FCLKEN); | 
 | 400 |  | 
 | 401 | 	/* Set IVA2 boot mode to 'idle' */ | 
 | 402 | 	omap_ctrl_writel(OMAP3_IVA2_BOOTMOD_IDLE, | 
 | 403 | 			 OMAP343X_CONTROL_IVA2_BOOTMOD); | 
 | 404 |  | 
 | 405 | 	/* Un-reset IVA2 */ | 
 | 406 | 	prm_write_mod_reg(0, OMAP3430_IVA2_MOD, RM_RSTCTRL); | 
 | 407 |  | 
 | 408 | 	/* Disable IVA2 clock */ | 
 | 409 | 	cm_write_mod_reg(0, OMAP3430_IVA2_MOD, CM_FCLKEN); | 
 | 410 |  | 
 | 411 | 	/* Reset IVA2 */ | 
 | 412 | 	prm_write_mod_reg(OMAP3430_RST1_IVA2 | | 
 | 413 | 			  OMAP3430_RST2_IVA2 | | 
 | 414 | 			  OMAP3430_RST3_IVA2, | 
 | 415 | 			  OMAP3430_IVA2_MOD, RM_RSTCTRL); | 
 | 416 | } | 
 | 417 |  | 
| Kevin Hilman | 8111b22 | 2009-04-28 15:27:44 -0700 | [diff] [blame] | 418 | static void __init omap3_d2d_idle(void) | 
| Kevin Hilman | 8bd2294 | 2009-05-28 10:56:16 -0700 | [diff] [blame] | 419 | { | 
| Kevin Hilman | 8111b22 | 2009-04-28 15:27:44 -0700 | [diff] [blame] | 420 | 	u16 mask, padconf; | 
 | 421 |  | 
 | 422 | 	/* In a stand alone OMAP3430 where there is not a stacked | 
 | 423 | 	 * modem for the D2D Idle Ack and D2D MStandby must be pulled | 
 | 424 | 	 * high. S CONTROL_PADCONF_SAD2D_IDLEACK and | 
 | 425 | 	 * CONTROL_PADCONF_SAD2D_MSTDBY to have a pull up. */ | 
 | 426 | 	mask = (1 << 4) | (1 << 3); /* pull-up, enabled */ | 
 | 427 | 	padconf = omap_ctrl_readw(OMAP3_PADCONF_SAD2D_MSTANDBY); | 
 | 428 | 	padconf |= mask; | 
 | 429 | 	omap_ctrl_writew(padconf, OMAP3_PADCONF_SAD2D_MSTANDBY); | 
 | 430 |  | 
 | 431 | 	padconf = omap_ctrl_readw(OMAP3_PADCONF_SAD2D_IDLEACK); | 
 | 432 | 	padconf |= mask; | 
 | 433 | 	omap_ctrl_writew(padconf, OMAP3_PADCONF_SAD2D_IDLEACK); | 
 | 434 |  | 
| Kevin Hilman | 8bd2294 | 2009-05-28 10:56:16 -0700 | [diff] [blame] | 435 | 	/* reset modem */ | 
 | 436 | 	prm_write_mod_reg(OMAP3430_RM_RSTCTRL_CORE_MODEM_SW_RSTPWRON | | 
 | 437 | 			  OMAP3430_RM_RSTCTRL_CORE_MODEM_SW_RST, | 
 | 438 | 			  CORE_MOD, RM_RSTCTRL); | 
 | 439 | 	prm_write_mod_reg(0, CORE_MOD, RM_RSTCTRL); | 
| Kevin Hilman | 8111b22 | 2009-04-28 15:27:44 -0700 | [diff] [blame] | 440 | } | 
| Kevin Hilman | 8bd2294 | 2009-05-28 10:56:16 -0700 | [diff] [blame] | 441 |  | 
| Kevin Hilman | 8111b22 | 2009-04-28 15:27:44 -0700 | [diff] [blame] | 442 | static void __init prcm_setup_regs(void) | 
 | 443 | { | 
| Kevin Hilman | 8bd2294 | 2009-05-28 10:56:16 -0700 | [diff] [blame] | 444 | 	/* XXX Reset all wkdeps. This should be done when initializing | 
 | 445 | 	 * powerdomains */ | 
 | 446 | 	prm_write_mod_reg(0, OMAP3430_IVA2_MOD, PM_WKDEP); | 
 | 447 | 	prm_write_mod_reg(0, MPU_MOD, PM_WKDEP); | 
 | 448 | 	prm_write_mod_reg(0, OMAP3430_DSS_MOD, PM_WKDEP); | 
 | 449 | 	prm_write_mod_reg(0, OMAP3430_NEON_MOD, PM_WKDEP); | 
 | 450 | 	prm_write_mod_reg(0, OMAP3430_CAM_MOD, PM_WKDEP); | 
 | 451 | 	prm_write_mod_reg(0, OMAP3430_PER_MOD, PM_WKDEP); | 
 | 452 | 	if (omap_rev() > OMAP3430_REV_ES1_0) { | 
 | 453 | 		prm_write_mod_reg(0, OMAP3430ES2_SGX_MOD, PM_WKDEP); | 
 | 454 | 		prm_write_mod_reg(0, OMAP3430ES2_USBHOST_MOD, PM_WKDEP); | 
 | 455 | 	} else | 
 | 456 | 		prm_write_mod_reg(0, GFX_MOD, PM_WKDEP); | 
 | 457 |  | 
 | 458 | 	/* | 
 | 459 | 	 * Enable interface clock autoidle for all modules. | 
 | 460 | 	 * Note that in the long run this should be done by clockfw | 
 | 461 | 	 */ | 
 | 462 | 	cm_write_mod_reg( | 
| Kevin Hilman | 8111b22 | 2009-04-28 15:27:44 -0700 | [diff] [blame] | 463 | 		OMAP3430_AUTO_MODEM | | 
| Kevin Hilman | 8bd2294 | 2009-05-28 10:56:16 -0700 | [diff] [blame] | 464 | 		OMAP3430ES2_AUTO_MMC3 | | 
 | 465 | 		OMAP3430ES2_AUTO_ICR | | 
 | 466 | 		OMAP3430_AUTO_AES2 | | 
 | 467 | 		OMAP3430_AUTO_SHA12 | | 
 | 468 | 		OMAP3430_AUTO_DES2 | | 
 | 469 | 		OMAP3430_AUTO_MMC2 | | 
 | 470 | 		OMAP3430_AUTO_MMC1 | | 
 | 471 | 		OMAP3430_AUTO_MSPRO | | 
 | 472 | 		OMAP3430_AUTO_HDQ | | 
 | 473 | 		OMAP3430_AUTO_MCSPI4 | | 
 | 474 | 		OMAP3430_AUTO_MCSPI3 | | 
 | 475 | 		OMAP3430_AUTO_MCSPI2 | | 
 | 476 | 		OMAP3430_AUTO_MCSPI1 | | 
 | 477 | 		OMAP3430_AUTO_I2C3 | | 
 | 478 | 		OMAP3430_AUTO_I2C2 | | 
 | 479 | 		OMAP3430_AUTO_I2C1 | | 
 | 480 | 		OMAP3430_AUTO_UART2 | | 
 | 481 | 		OMAP3430_AUTO_UART1 | | 
 | 482 | 		OMAP3430_AUTO_GPT11 | | 
 | 483 | 		OMAP3430_AUTO_GPT10 | | 
 | 484 | 		OMAP3430_AUTO_MCBSP5 | | 
 | 485 | 		OMAP3430_AUTO_MCBSP1 | | 
 | 486 | 		OMAP3430ES1_AUTO_FAC | /* This is es1 only */ | 
 | 487 | 		OMAP3430_AUTO_MAILBOXES | | 
 | 488 | 		OMAP3430_AUTO_OMAPCTRL | | 
 | 489 | 		OMAP3430ES1_AUTO_FSHOSTUSB | | 
 | 490 | 		OMAP3430_AUTO_HSOTGUSB | | 
| Kevin Hilman | 8111b22 | 2009-04-28 15:27:44 -0700 | [diff] [blame] | 491 | 		OMAP3430_AUTO_SAD2D | | 
| Kevin Hilman | 8bd2294 | 2009-05-28 10:56:16 -0700 | [diff] [blame] | 492 | 		OMAP3430_AUTO_SSI, | 
 | 493 | 		CORE_MOD, CM_AUTOIDLE1); | 
 | 494 |  | 
 | 495 | 	cm_write_mod_reg( | 
 | 496 | 		OMAP3430_AUTO_PKA | | 
 | 497 | 		OMAP3430_AUTO_AES1 | | 
 | 498 | 		OMAP3430_AUTO_RNG | | 
 | 499 | 		OMAP3430_AUTO_SHA11 | | 
 | 500 | 		OMAP3430_AUTO_DES1, | 
 | 501 | 		CORE_MOD, CM_AUTOIDLE2); | 
 | 502 |  | 
 | 503 | 	if (omap_rev() > OMAP3430_REV_ES1_0) { | 
 | 504 | 		cm_write_mod_reg( | 
| Kevin Hilman | 8111b22 | 2009-04-28 15:27:44 -0700 | [diff] [blame] | 505 | 			OMAP3430_AUTO_MAD2D | | 
| Kevin Hilman | 8bd2294 | 2009-05-28 10:56:16 -0700 | [diff] [blame] | 506 | 			OMAP3430ES2_AUTO_USBTLL, | 
 | 507 | 			CORE_MOD, CM_AUTOIDLE3); | 
 | 508 | 	} | 
 | 509 |  | 
 | 510 | 	cm_write_mod_reg( | 
 | 511 | 		OMAP3430_AUTO_WDT2 | | 
 | 512 | 		OMAP3430_AUTO_WDT1 | | 
 | 513 | 		OMAP3430_AUTO_GPIO1 | | 
 | 514 | 		OMAP3430_AUTO_32KSYNC | | 
 | 515 | 		OMAP3430_AUTO_GPT12 | | 
 | 516 | 		OMAP3430_AUTO_GPT1 , | 
 | 517 | 		WKUP_MOD, CM_AUTOIDLE); | 
 | 518 |  | 
 | 519 | 	cm_write_mod_reg( | 
 | 520 | 		OMAP3430_AUTO_DSS, | 
 | 521 | 		OMAP3430_DSS_MOD, | 
 | 522 | 		CM_AUTOIDLE); | 
 | 523 |  | 
 | 524 | 	cm_write_mod_reg( | 
 | 525 | 		OMAP3430_AUTO_CAM, | 
 | 526 | 		OMAP3430_CAM_MOD, | 
 | 527 | 		CM_AUTOIDLE); | 
 | 528 |  | 
 | 529 | 	cm_write_mod_reg( | 
 | 530 | 		OMAP3430_AUTO_GPIO6 | | 
 | 531 | 		OMAP3430_AUTO_GPIO5 | | 
 | 532 | 		OMAP3430_AUTO_GPIO4 | | 
 | 533 | 		OMAP3430_AUTO_GPIO3 | | 
 | 534 | 		OMAP3430_AUTO_GPIO2 | | 
 | 535 | 		OMAP3430_AUTO_WDT3 | | 
 | 536 | 		OMAP3430_AUTO_UART3 | | 
 | 537 | 		OMAP3430_AUTO_GPT9 | | 
 | 538 | 		OMAP3430_AUTO_GPT8 | | 
 | 539 | 		OMAP3430_AUTO_GPT7 | | 
 | 540 | 		OMAP3430_AUTO_GPT6 | | 
 | 541 | 		OMAP3430_AUTO_GPT5 | | 
 | 542 | 		OMAP3430_AUTO_GPT4 | | 
 | 543 | 		OMAP3430_AUTO_GPT3 | | 
 | 544 | 		OMAP3430_AUTO_GPT2 | | 
 | 545 | 		OMAP3430_AUTO_MCBSP4 | | 
 | 546 | 		OMAP3430_AUTO_MCBSP3 | | 
 | 547 | 		OMAP3430_AUTO_MCBSP2, | 
 | 548 | 		OMAP3430_PER_MOD, | 
 | 549 | 		CM_AUTOIDLE); | 
 | 550 |  | 
 | 551 | 	if (omap_rev() > OMAP3430_REV_ES1_0) { | 
 | 552 | 		cm_write_mod_reg( | 
 | 553 | 			OMAP3430ES2_AUTO_USBHOST, | 
 | 554 | 			OMAP3430ES2_USBHOST_MOD, | 
 | 555 | 			CM_AUTOIDLE); | 
 | 556 | 	} | 
 | 557 |  | 
 | 558 | 	/* | 
 | 559 | 	 * Set all plls to autoidle. This is needed until autoidle is | 
 | 560 | 	 * enabled by clockfw | 
 | 561 | 	 */ | 
 | 562 | 	cm_write_mod_reg(1 << OMAP3430_AUTO_IVA2_DPLL_SHIFT, | 
 | 563 | 			 OMAP3430_IVA2_MOD, CM_AUTOIDLE2); | 
 | 564 | 	cm_write_mod_reg(1 << OMAP3430_AUTO_MPU_DPLL_SHIFT, | 
 | 565 | 			 MPU_MOD, | 
 | 566 | 			 CM_AUTOIDLE2); | 
 | 567 | 	cm_write_mod_reg((1 << OMAP3430_AUTO_PERIPH_DPLL_SHIFT) | | 
 | 568 | 			 (1 << OMAP3430_AUTO_CORE_DPLL_SHIFT), | 
 | 569 | 			 PLL_MOD, | 
 | 570 | 			 CM_AUTOIDLE); | 
 | 571 | 	cm_write_mod_reg(1 << OMAP3430ES2_AUTO_PERIPH2_DPLL_SHIFT, | 
 | 572 | 			 PLL_MOD, | 
 | 573 | 			 CM_AUTOIDLE2); | 
 | 574 |  | 
 | 575 | 	/* | 
 | 576 | 	 * Enable control of expternal oscillator through | 
 | 577 | 	 * sys_clkreq. In the long run clock framework should | 
 | 578 | 	 * take care of this. | 
 | 579 | 	 */ | 
 | 580 | 	prm_rmw_mod_reg_bits(OMAP_AUTOEXTCLKMODE_MASK, | 
 | 581 | 			     1 << OMAP_AUTOEXTCLKMODE_SHIFT, | 
 | 582 | 			     OMAP3430_GR_MOD, | 
 | 583 | 			     OMAP3_PRM_CLKSRC_CTRL_OFFSET); | 
 | 584 |  | 
 | 585 | 	/* setup wakup source */ | 
 | 586 | 	prm_write_mod_reg(OMAP3430_EN_IO | OMAP3430_EN_GPIO1 | | 
 | 587 | 			  OMAP3430_EN_GPT1 | OMAP3430_EN_GPT12, | 
 | 588 | 			  WKUP_MOD, PM_WKEN); | 
 | 589 | 	/* No need to write EN_IO, that is always enabled */ | 
 | 590 | 	prm_write_mod_reg(OMAP3430_EN_GPIO1 | OMAP3430_EN_GPT1 | | 
 | 591 | 			  OMAP3430_EN_GPT12, | 
 | 592 | 			  WKUP_MOD, OMAP3430_PM_MPUGRPSEL); | 
 | 593 | 	/* For some reason IO doesn't generate wakeup event even if | 
 | 594 | 	 * it is selected to mpu wakeup goup */ | 
 | 595 | 	prm_write_mod_reg(OMAP3430_IO_EN | OMAP3430_WKUP_EN, | 
 | 596 | 			  OCP_MOD, OMAP3_PRM_IRQENABLE_MPU_OFFSET); | 
| Kevin Hilman | 1155e42 | 2008-11-25 11:48:24 -0800 | [diff] [blame] | 597 |  | 
| Kevin Hilman | d3fd329 | 2009-05-05 16:34:25 -0700 | [diff] [blame] | 598 | 	/* Don't attach IVA interrupts */ | 
 | 599 | 	prm_write_mod_reg(0, WKUP_MOD, OMAP3430_PM_IVAGRPSEL); | 
 | 600 | 	prm_write_mod_reg(0, CORE_MOD, OMAP3430_PM_IVAGRPSEL1); | 
 | 601 | 	prm_write_mod_reg(0, CORE_MOD, OMAP3430ES2_PM_IVAGRPSEL3); | 
 | 602 | 	prm_write_mod_reg(0, OMAP3430_PER_MOD, OMAP3430_PM_IVAGRPSEL); | 
 | 603 |  | 
| Kevin Hilman | b1340d1 | 2009-04-27 16:14:54 -0700 | [diff] [blame] | 604 | 	/* Clear any pending 'reset' flags */ | 
 | 605 | 	prm_write_mod_reg(0xffffffff, MPU_MOD, RM_RSTST); | 
 | 606 | 	prm_write_mod_reg(0xffffffff, CORE_MOD, RM_RSTST); | 
 | 607 | 	prm_write_mod_reg(0xffffffff, OMAP3430_PER_MOD, RM_RSTST); | 
 | 608 | 	prm_write_mod_reg(0xffffffff, OMAP3430_EMU_MOD, RM_RSTST); | 
 | 609 | 	prm_write_mod_reg(0xffffffff, OMAP3430_NEON_MOD, RM_RSTST); | 
 | 610 | 	prm_write_mod_reg(0xffffffff, OMAP3430_DSS_MOD, RM_RSTST); | 
 | 611 | 	prm_write_mod_reg(0xffffffff, OMAP3430ES2_USBHOST_MOD, RM_RSTST); | 
 | 612 |  | 
| Kevin Hilman | 014c46d | 2009-04-27 07:50:23 -0700 | [diff] [blame] | 613 | 	/* Clear any pending PRCM interrupts */ | 
 | 614 | 	prm_write_mod_reg(0, OCP_MOD, OMAP3_PRM_IRQSTATUS_MPU_OFFSET); | 
 | 615 |  | 
| Kevin Hilman | 1155e42 | 2008-11-25 11:48:24 -0800 | [diff] [blame] | 616 | 	omap3_iva_idle(); | 
| Kevin Hilman | 8111b22 | 2009-04-28 15:27:44 -0700 | [diff] [blame] | 617 | 	omap3_d2d_idle(); | 
| Kevin Hilman | 8bd2294 | 2009-05-28 10:56:16 -0700 | [diff] [blame] | 618 | } | 
 | 619 |  | 
 | 620 | static int __init pwrdms_setup(struct powerdomain *pwrdm) | 
 | 621 | { | 
 | 622 | 	struct power_state *pwrst; | 
 | 623 |  | 
 | 624 | 	if (!pwrdm->pwrsts) | 
 | 625 | 		return 0; | 
 | 626 |  | 
 | 627 | 	pwrst = kmalloc(sizeof(struct power_state), GFP_KERNEL); | 
 | 628 | 	if (!pwrst) | 
 | 629 | 		return -ENOMEM; | 
 | 630 | 	pwrst->pwrdm = pwrdm; | 
 | 631 | 	pwrst->next_state = PWRDM_POWER_RET; | 
 | 632 | 	list_add(&pwrst->node, &pwrst_list); | 
 | 633 |  | 
 | 634 | 	if (pwrdm_has_hdwr_sar(pwrdm)) | 
 | 635 | 		pwrdm_enable_hdwr_sar(pwrdm); | 
 | 636 |  | 
 | 637 | 	return set_pwrdm_state(pwrst->pwrdm, pwrst->next_state); | 
 | 638 | } | 
 | 639 |  | 
 | 640 | /* | 
 | 641 |  * Enable hw supervised mode for all clockdomains if it's | 
 | 642 |  * supported. Initiate sleep transition for other clockdomains, if | 
 | 643 |  * they are not used | 
 | 644 |  */ | 
 | 645 | static int __init clkdms_setup(struct clockdomain *clkdm) | 
 | 646 | { | 
 | 647 | 	if (clkdm->flags & CLKDM_CAN_ENABLE_AUTO) | 
 | 648 | 		omap2_clkdm_allow_idle(clkdm); | 
 | 649 | 	else if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP && | 
 | 650 | 		 atomic_read(&clkdm->usecount) == 0) | 
 | 651 | 		omap2_clkdm_sleep(clkdm); | 
 | 652 | 	return 0; | 
 | 653 | } | 
 | 654 |  | 
 | 655 | int __init omap3_pm_init(void) | 
 | 656 | { | 
 | 657 | 	struct power_state *pwrst, *tmp; | 
 | 658 | 	int ret; | 
 | 659 |  | 
 | 660 | 	if (!cpu_is_omap34xx()) | 
 | 661 | 		return -ENODEV; | 
 | 662 |  | 
 | 663 | 	printk(KERN_ERR "Power Management for TI OMAP3.\n"); | 
 | 664 |  | 
 | 665 | 	/* XXX prcm_setup_regs needs to be before enabling hw | 
 | 666 | 	 * supervised mode for powerdomains */ | 
 | 667 | 	prcm_setup_regs(); | 
 | 668 |  | 
 | 669 | 	ret = request_irq(INT_34XX_PRCM_MPU_IRQ, | 
 | 670 | 			  (irq_handler_t)prcm_interrupt_handler, | 
 | 671 | 			  IRQF_DISABLED, "prcm", NULL); | 
 | 672 | 	if (ret) { | 
 | 673 | 		printk(KERN_ERR "request_irq failed to register for 0x%x\n", | 
 | 674 | 		       INT_34XX_PRCM_MPU_IRQ); | 
 | 675 | 		goto err1; | 
 | 676 | 	} | 
 | 677 |  | 
 | 678 | 	ret = pwrdm_for_each(pwrdms_setup); | 
 | 679 | 	if (ret) { | 
 | 680 | 		printk(KERN_ERR "Failed to setup powerdomains\n"); | 
 | 681 | 		goto err2; | 
 | 682 | 	} | 
 | 683 |  | 
 | 684 | 	(void) clkdm_for_each(clkdms_setup); | 
 | 685 |  | 
 | 686 | 	mpu_pwrdm = pwrdm_lookup("mpu_pwrdm"); | 
 | 687 | 	if (mpu_pwrdm == NULL) { | 
 | 688 | 		printk(KERN_ERR "Failed to get mpu_pwrdm\n"); | 
 | 689 | 		goto err2; | 
 | 690 | 	} | 
 | 691 |  | 
 | 692 | 	_omap_sram_idle = omap_sram_push(omap34xx_cpu_suspend, | 
 | 693 | 					 omap34xx_cpu_suspend_sz); | 
 | 694 |  | 
 | 695 | 	suspend_set_ops(&omap_pm_ops); | 
 | 696 |  | 
 | 697 | 	pm_idle = omap3_pm_idle; | 
 | 698 |  | 
 | 699 | err1: | 
 | 700 | 	return ret; | 
 | 701 | err2: | 
 | 702 | 	free_irq(INT_34XX_PRCM_MPU_IRQ, NULL); | 
 | 703 | 	list_for_each_entry_safe(pwrst, tmp, &pwrst_list, node) { | 
 | 704 | 		list_del(&pwrst->node); | 
 | 705 | 		kfree(pwrst); | 
 | 706 | 	} | 
 | 707 | 	return ret; | 
 | 708 | } | 
 | 709 |  | 
 | 710 | late_initcall(omap3_pm_init); |