943 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			943 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* arch/arm/mach-msm/pm.c
 | |
|  *
 | |
|  * MSM Power Management Routines
 | |
|  *
 | |
|  * Copyright (C) 2007 Google, Inc.
 | |
|  *
 | |
|  * This software is licensed under the terms of the GNU General Public
 | |
|  * License version 2, as published by the Free Software Foundation, and
 | |
|  * may be copied, distributed, and modified under those terms.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|  * GNU General Public License for more details.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #include <linux/module.h>
 | |
| #include <linux/kernel.h>
 | |
| #include <linux/clk.h>
 | |
| #include <linux/console.h>
 | |
| #include <linux/delay.h>
 | |
| #include <linux/init.h>
 | |
| #include <linux/pm.h>
 | |
| #include <linux/proc_fs.h>
 | |
| #include <linux/suspend.h>
 | |
| #include <linux/reboot.h>
 | |
| #include <linux/earlysuspend.h>
 | |
| #include <mach/msm_iomap.h>
 | |
| #include <mach/system.h>
 | |
| #include <asm/io.h>
 | |
| #ifdef CONFIG_VFP
 | |
| #include <asm/vfp.h>
 | |
| #endif
 | |
| 
 | |
| #ifdef CONFIG_CACHE_L2X0
 | |
| #include <asm/hardware/cache-l2x0.h>
 | |
| #endif
 | |
| 
 | |
| #include "smd_private.h"
 | |
| #include "smd_rpcrouter.h"
 | |
| #include "acpuclock.h"
 | |
| #include "proc_comm.h"
 | |
| #include "pmic_global.h"
 | |
| #include "clock.h"
 | |
| #include "spm.h"
 | |
| #ifdef CONFIG_HAS_WAKELOCK
 | |
| #include <linux/wakelock.h>
 | |
| #endif
 | |
| #include "board-htcleo.h"
 | |
| 
 | |
| enum {
 | |
| 	MSM_PM_DEBUG_SUSPEND = 1U << 0,
 | |
| 	MSM_PM_DEBUG_POWER_COLLAPSE = 1U << 1,
 | |
| 	MSM_PM_DEBUG_STATE = 1U << 2,
 | |
| 	MSM_PM_DEBUG_CLOCK = 1U << 3,
 | |
| 	MSM_PM_DEBUG_RESET_VECTOR = 1U << 4,
 | |
| 	MSM_PM_DEBUG_SMSM_STATE = 1U << 5,
 | |
| 	MSM_PM_DEBUG_IDLE = 1U << 6,
 | |
| 	MSM_PM_DEBUG_SLEEP_LIMIT = 1U << 7,
 | |
| 	MSM_PM_DEBUG_WAKEUP_REASON = 1U << 8,
 | |
| };
 | |
| static int msm_pm_debug_mask = MSM_PM_DEBUG_SLEEP_LIMIT | MSM_PM_DEBUG_WAKEUP_REASON;
 | |
| module_param_named(debug_mask, msm_pm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP);
 | |
| 
 | |
| enum {
 | |
| 	MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND,
 | |
| 	MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
 | |
| 	MSM_PM_SLEEP_MODE_APPS_SLEEP,
 | |
| 	MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT,
 | |
| 	MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT,
 | |
| };
 | |
| static int msm_pm_sleep_mode = CONFIG_MSM7X00A_SLEEP_MODE;
 | |
| module_param_named(sleep_mode, msm_pm_sleep_mode, int, S_IRUGO | S_IWUSR | S_IWGRP);
 | |
| static int msm_pm_idle_sleep_mode = CONFIG_MSM7X00A_IDLE_SLEEP_MODE;
 | |
| module_param_named(idle_sleep_mode, msm_pm_idle_sleep_mode, int, S_IRUGO | S_IWUSR | S_IWGRP);
 | |
| static int msm_pm_idle_sleep_min_time = CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME;
 | |
| module_param_named(idle_sleep_min_time, msm_pm_idle_sleep_min_time, int, S_IRUGO | S_IWUSR | S_IWGRP);
 | |
| static int msm_pm_idle_spin_time = CONFIG_MSM7X00A_IDLE_SPIN_TIME;
 | |
| module_param_named(idle_spin_time, msm_pm_idle_spin_time, int, S_IRUGO | S_IWUSR | S_IWGRP);
 | |
| 
 | |
| #if defined (CONFIG_ARCH_MSM7X30)
 | |
| #define A11S_CLK_SLEEP_EN (MSM_GCC_BASE + 0x020)
 | |
| #define A11S_PWRDOWN (MSM_ACC_BASE + 0x01C)
 | |
| #define A11S_SECOP (MSM_TCSR_BASE + 0x38)
 | |
| #else
 | |
| #define A11S_CLK_SLEEP_EN (MSM_CSR_BASE + 0x11c)
 | |
| #define A11S_PWRDOWN (MSM_CSR_BASE + 0x440)
 | |
| #define A11S_STANDBY_CTL (MSM_CSR_BASE + 0x108)
 | |
| #endif
 | |
| #define A11RAMBACKBIAS (MSM_CSR_BASE + 0x508)
 | |
| 
 | |
| #if defined(CONFIG_MSM_N_WAY_SMD)
 | |
| #define DEM_MASTER_BITS_PER_CPU             6
 | |
| 
 | |
| /* Power Master State Bits - Per CPU */
 | |
| #define DEM_MASTER_SMSM_RUN \
 | |
|         (0x01UL << (DEM_MASTER_BITS_PER_CPU * SMSM_STATE_APPS))
 | |
| #define DEM_MASTER_SMSM_RSA \
 | |
|         (0x02UL << (DEM_MASTER_BITS_PER_CPU * SMSM_STATE_APPS))
 | |
| #define DEM_MASTER_SMSM_PWRC_EARLY_EXIT \
 | |
|         (0x04UL << (DEM_MASTER_BITS_PER_CPU * SMSM_STATE_APPS))
 | |
| #define DEM_MASTER_SMSM_SLEEP_EXIT \
 | |
|         (0x08UL << (DEM_MASTER_BITS_PER_CPU * SMSM_STATE_APPS))
 | |
| #define DEM_MASTER_SMSM_READY \
 | |
|         (0x10UL << (DEM_MASTER_BITS_PER_CPU * SMSM_STATE_APPS))
 | |
| #define DEM_MASTER_SMSM_SLEEP \
 | |
|         (0x20UL << (DEM_MASTER_BITS_PER_CPU * SMSM_STATE_APPS))
 | |
| 
 | |
| /* Power Slave State Bits */
 | |
| #define DEM_SLAVE_SMSM_RUN                  (0x0001)
 | |
| #define DEM_SLAVE_SMSM_PWRC                 (0x0002)
 | |
| #define DEM_SLAVE_SMSM_PWRC_DELAY           (0x0004)
 | |
| #define DEM_SLAVE_SMSM_PWRC_EARLY_EXIT      (0x0008)
 | |
| #define DEM_SLAVE_SMSM_WFPI                 (0x0010)
 | |
| #define DEM_SLAVE_SMSM_SLEEP                (0x0020)
 | |
| #define DEM_SLAVE_SMSM_SLEEP_EXIT           (0x0040)
 | |
| #define DEM_SLAVE_SMSM_MSGS_REDUCED         (0x0080)
 | |
| #define DEM_SLAVE_SMSM_RESET                (0x0100)
 | |
| #define DEM_SLAVE_SMSM_PWRC_SUSPEND         (0x0200)
 | |
| 
 | |
| #define PM_SMSM_WRITE_STATE	SMSM_STATE_APPS_DEM
 | |
| #define PM_SMSM_READ_STATE	SMSM_STATE_POWER_MASTER_DEM
 | |
| 
 | |
| #define PM_SMSM_WRITE_RUN	DEM_SLAVE_SMSM_RUN
 | |
| #define PM_SMSM_READ_RUN	DEM_MASTER_SMSM_RUN
 | |
| #else
 | |
| #define PM_SMSM_WRITE_STATE     SMSM_STATE_APPS
 | |
| #define PM_SMSM_READ_STATE      SMSM_STATE_MODEM
 | |
| 
 | |
| #define PM_SMSM_WRITE_RUN       SMSM_RUN
 | |
| #define PM_SMSM_READ_RUN        SMSM_RUN
 | |
| #endif
 | |
| 
 | |
| int msm_pm_collapse(void);
 | |
| int msm_arch_idle(void);
 | |
| void msm_pm_collapse_exit(void);
 | |
| 
 | |
| int64_t msm_timer_enter_idle(void);
 | |
| void msm_timer_exit_idle(int low_power);
 | |
| int msm_irq_idle_sleep_allowed(void);
 | |
| int msm_irq_pending(void);
 | |
| int clks_allow_tcxo_locked_debug(void);
 | |
| extern int board_mfg_mode(void);
 | |
| extern char * board_get_mfg_sleep_gpio_table(void);
 | |
| extern void gpio_set_diag_gpio_table(unsigned long * dwMFG_gpio_table);
 | |
| extern void wait_rmt_final_call_back(int timeout);
 | |
| 
 | |
| static int axi_rate;
 | |
| static int sleep_axi_rate;
 | |
| static struct clk *axi_clk;
 | |
| static uint32_t *msm_pm_reset_vector;
 | |
| 
 | |
| static uint32_t msm_pm_max_sleep_time;
 | |
| 
 | |
| #ifdef CONFIG_MSM_IDLE_STATS
 | |
| enum msm_pm_time_stats_id {
 | |
| 	MSM_PM_STAT_REQUESTED_IDLE,
 | |
| 	MSM_PM_STAT_IDLE_SPIN,
 | |
| 	MSM_PM_STAT_IDLE_WFI,
 | |
| 	MSM_PM_STAT_IDLE_SLEEP,
 | |
| 	MSM_PM_STAT_IDLE_FAILED_SLEEP,
 | |
| 	MSM_PM_STAT_NOT_IDLE,
 | |
| 	MSM_PM_STAT_COUNT
 | |
| };
 | |
| 
 | |
| static struct msm_pm_time_stats {
 | |
| 	const char *name;
 | |
| 	int bucket[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
 | |
| 	int64_t min_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
 | |
| 	int64_t max_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
 | |
| 	int count;
 | |
| 	int64_t total_time;
 | |
| } msm_pm_stats[MSM_PM_STAT_COUNT] = {
 | |
| 	[MSM_PM_STAT_REQUESTED_IDLE].name = "idle-request",
 | |
| 	[MSM_PM_STAT_IDLE_SPIN].name = "idle-spin",
 | |
| 	[MSM_PM_STAT_IDLE_WFI].name = "idle-wfi",
 | |
| 	[MSM_PM_STAT_IDLE_SLEEP].name = "idle-sleep",
 | |
| 	[MSM_PM_STAT_IDLE_FAILED_SLEEP].name = "idle-failed-sleep",
 | |
| 	[MSM_PM_STAT_NOT_IDLE].name = "not-idle",
 | |
| };
 | |
| 
 | |
| static void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t)
 | |
| {
 | |
| 	int i;
 | |
| 	int64_t bt;
 | |
| 	msm_pm_stats[id].total_time += t;
 | |
| 	msm_pm_stats[id].count++;
 | |
| 	bt = t;
 | |
| 	do_div(bt, CONFIG_MSM_IDLE_STATS_FIRST_BUCKET);
 | |
| 	if (bt < 1ULL << (CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT *
 | |
| 				(CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1)))
 | |
| 		i = DIV_ROUND_UP(fls((uint32_t)bt),
 | |
| 					CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT);
 | |
| 	else
 | |
| 		i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1;
 | |
| 	msm_pm_stats[id].bucket[i]++;
 | |
| 	if (t < msm_pm_stats[id].min_time[i] || !msm_pm_stats[id].max_time[i])
 | |
| 		msm_pm_stats[id].min_time[i] = t;
 | |
| 	if (t > msm_pm_stats[id].max_time[i])
 | |
| 		msm_pm_stats[id].max_time[i] = t;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static int
 | |
| msm_pm_wait_state(uint32_t wait_all_set, uint32_t wait_all_clear,
 | |
|                   uint32_t wait_any_set, uint32_t wait_any_clear)
 | |
| {
 | |
| 	int i;
 | |
| 	uint32_t state;
 | |
| 
 | |
| 	for (i = 0; i < 100000; i++) {
 | |
| 		state = smsm_get_state(PM_SMSM_READ_STATE);
 | |
| 		if (((wait_all_set || wait_all_clear) && 
 | |
| 		     !(~state & wait_all_set) && !(state & wait_all_clear)) ||
 | |
| 		    (state & wait_any_set) || (~state & wait_any_clear))
 | |
| 			return 0;
 | |
| 		udelay(1);
 | |
| 	}
 | |
| 	pr_err("msm_pm_wait_state(%x, %x, %x, %x) failed %x\n",	wait_all_set,
 | |
| 		wait_all_clear, wait_any_set, wait_any_clear, state);
 | |
| 	return -ETIMEDOUT;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * For speeding up boot time:
 | |
|  * During booting up, disable entering arch_idle() by disable_hlt()
 | |
|  * Enable it after booting up BOOT_LOCK_TIMEOUT sec.
 | |
|  */
 | |
| #define BOOT_LOCK_TIMEOUT      (60 * HZ)
 | |
| static void do_expire_boot_lock(struct work_struct *work)
 | |
| {
 | |
| 	enable_hlt();
 | |
| 	pr_info("Release 'boot-time' halt_lock\n");
 | |
| }
 | |
| static DECLARE_DELAYED_WORK(work_expire_boot_lock, do_expire_boot_lock);
 | |
| 
 | |
| #ifdef CONFIG_MSM_FIQ_SUPPORT
 | |
| void msm_fiq_exit_sleep(void);
 | |
| #else
 | |
| static inline void msm_fiq_exit_sleep(void) { }
 | |
| #endif
 | |
| 
 | |
| #ifdef CONFIG_HTC_POWER_COLLAPSE_MAGIC
 | |
| /* Set magic number in SMEM for power collapse state */
 | |
| #define HTC_POWER_COLLAPSE_ADD	(MSM_SHARED_RAM_BASE + 0x000F8000 + 0x000007F8)
 | |
| #define HTC_POWER_COLLAPSE_MAGIC_NUM	(HTC_POWER_COLLAPSE_ADD - 0x04)
 | |
| unsigned int magic_num;
 | |
| #endif
 | |
| 
 | |
| static int msm_sleep(int sleep_mode, uint32_t sleep_delay, int from_idle)
 | |
| {
 | |
| 	uint32_t saved_vector[2];
 | |
| 	int collapsed;
 | |
| 	void msm_irq_enter_sleep1(bool arm9_wake, int from_idle);
 | |
| 	int msm_irq_enter_sleep2(bool arm9_wake, int from_idle);
 | |
| 	void msm_irq_exit_sleep1(void);
 | |
| 	void msm_irq_exit_sleep2(void);
 | |
| 	void msm_irq_exit_sleep3(void);
 | |
| 	void msm_gpio_enter_sleep(int from_idle);
 | |
| 	void msm_gpio_exit_sleep(void);
 | |
| 	void smd_sleep_exit(void);
 | |
| 	uint32_t enter_state;
 | |
| 	uint32_t enter_wait_set = 0;
 | |
| 	uint32_t enter_wait_clear = 0;
 | |
| 	uint32_t exit_state;
 | |
| 	uint32_t exit_wait_clear = 0;
 | |
| 	uint32_t exit_wait_any_set = 0;
 | |
| 	unsigned long pm_saved_acpu_clk_rate = 0;
 | |
| 	int ret;
 | |
| 	int rv = -EINTR;
 | |
| 	bool invalid_inital_state = false;
 | |
| #if defined(CONFIG_MACH_HTCLEO) 
 | |
| 	if(!htcleo_is_nand_boot() && sleep_mode<2)
 | |
| 		sleep_mode=2;
 | |
| #endif
 | |
| 	if (board_mfg_mode() == 4) /*power test mode*/
 | |
| 		gpio_set_diag_gpio_table(board_get_mfg_sleep_gpio_table());
 | |
| 
 | |
| 	if (msm_pm_debug_mask & MSM_PM_DEBUG_SUSPEND)
 | |
| 		printk(KERN_INFO "msm_sleep(): mode %d delay %u idle %d\n",
 | |
| 		       sleep_mode, sleep_delay, from_idle);
 | |
| #if defined(CONFIG_MSM_N_WAY_SMD)
 | |
| 	switch (sleep_mode) {
 | |
| 	case MSM_PM_SLEEP_MODE_POWER_COLLAPSE:
 | |
| 		enter_state = DEM_SLAVE_SMSM_PWRC;
 | |
| 		enter_wait_set = DEM_MASTER_SMSM_RSA;
 | |
| 		exit_state = DEM_SLAVE_SMSM_WFPI;
 | |
| 		exit_wait_any_set =
 | |
| 			DEM_MASTER_SMSM_RUN | DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
 | |
| 		break;
 | |
| 	case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND:
 | |
| 		enter_state = DEM_SLAVE_SMSM_PWRC_SUSPEND;
 | |
| 		enter_wait_set = DEM_MASTER_SMSM_RSA;
 | |
| 		exit_state = DEM_SLAVE_SMSM_WFPI;
 | |
| 		exit_wait_any_set =
 | |
| 			DEM_MASTER_SMSM_RUN | DEM_MASTER_SMSM_PWRC_EARLY_EXIT;
 | |
| 		break;
 | |
| 	case MSM_PM_SLEEP_MODE_APPS_SLEEP:
 | |
| 		enter_state = DEM_SLAVE_SMSM_SLEEP;
 | |
| 		enter_wait_set = DEM_MASTER_SMSM_SLEEP;
 | |
| 		exit_state = DEM_SLAVE_SMSM_SLEEP_EXIT;
 | |
| 		exit_wait_any_set = DEM_MASTER_SMSM_SLEEP_EXIT;
 | |
| 		break;
 | |
| 	default:
 | |
| 		enter_state = 0;
 | |
| 		exit_state = 0;
 | |
| 	}
 | |
| #else
 | |
| 	switch (sleep_mode) {
 | |
| 	case MSM_PM_SLEEP_MODE_POWER_COLLAPSE:
 | |
| 		enter_state = SMSM_PWRC;
 | |
| 		enter_wait_set = SMSM_RSA;
 | |
| 		exit_state = SMSM_WFPI;
 | |
| 		exit_wait_clear = SMSM_RSA;
 | |
| 		break;
 | |
| 	case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND:
 | |
| 		enter_state = SMSM_PWRC_SUSPEND;
 | |
| 		enter_wait_set = SMSM_RSA;
 | |
| 		exit_state = SMSM_WFPI;
 | |
| 		exit_wait_clear = SMSM_RSA;
 | |
| 		break;
 | |
| 	case MSM_PM_SLEEP_MODE_APPS_SLEEP:
 | |
| 		enter_state = SMSM_SLEEP;
 | |
| 		exit_state = SMSM_SLEEPEXIT;
 | |
| 		exit_wait_any_set = SMSM_SLEEPEXIT;
 | |
| 		break;
 | |
| 	default:
 | |
| 		enter_state = 0;
 | |
| 		exit_state = 0;
 | |
| 	}
 | |
| #endif
 | |
| 	clk_enter_sleep(from_idle);
 | |
| 	msm_irq_enter_sleep1(!!enter_state, from_idle);
 | |
| 	msm_gpio_enter_sleep(from_idle);
 | |
| 
 | |
| 	if (enter_state) {
 | |
| 		/* Make sure last sleep request did not end with a timeout */
 | |
| 		ret = msm_pm_wait_state(PM_SMSM_READ_RUN, 0, 0, 0);
 | |
| 		if (ret) {
 | |
| 			printk(KERN_ERR "msm_sleep(): invalid inital state\n");
 | |
| 			invalid_inital_state = true;
 | |
| 		}
 | |
| 
 | |
| 		if (sleep_delay == 0 && sleep_mode >= MSM_PM_SLEEP_MODE_APPS_SLEEP)
 | |
| 			sleep_delay = 192000*5; /* APPS_SLEEP does not allow infinite timeout */
 | |
| 		ret = smsm_set_sleep_duration(sleep_delay);
 | |
| 		if (ret) {
 | |
| 			printk(KERN_ERR "msm_sleep(): smsm_set_sleep_duration %x failed\n", enter_state);
 | |
| 			enter_state = 0;
 | |
| 			exit_state = 0;
 | |
| 		}
 | |
| 		if ((!from_idle && msm_pm_debug_mask & MSM_PM_DEBUG_SLEEP_LIMIT) ||
 | |
| 			(from_idle && msm_pm_debug_mask & MSM_PM_DEBUG_IDLE))
 | |
| 			clks_allow_tcxo_locked_debug();
 | |
| 
 | |
| 		ret = smsm_change_state(PM_SMSM_WRITE_STATE, PM_SMSM_WRITE_RUN, enter_state);
 | |
| 		if (ret) {
 | |
| 			printk(KERN_ERR "msm_sleep(): smsm_change_state %x failed\n", enter_state);
 | |
| 			enter_state = 0;
 | |
| 			exit_state = 0;
 | |
| 		}
 | |
| 		ret = msm_pm_wait_state(enter_wait_set, enter_wait_clear, 0, 0);
 | |
| 		if (ret || invalid_inital_state) {
 | |
| 			printk(KERN_INFO "msm_sleep(): msm_pm_wait_state failed, %x\n", smsm_get_state(PM_SMSM_READ_STATE));
 | |
| 			goto enter_failed;
 | |
| 		}
 | |
| 	}
 | |
| 	if (msm_irq_enter_sleep2(!!enter_state, from_idle))
 | |
| 		goto enter_failed;
 | |
| 
 | |
| 	if (enter_state) {
 | |
| #if defined (CONFIG_ARCH_MSM7X30)
 | |
| 		writel(1, A11S_PWRDOWN);
 | |
| 		writel(4, A11S_SECOP);
 | |
| #else
 | |
| 		writel(0x1f, A11S_CLK_SLEEP_EN);
 | |
| 		writel(1, A11S_PWRDOWN);
 | |
| 
 | |
| 		writel(0, A11S_STANDBY_CTL);
 | |
| #endif
 | |
| #if !defined(CONFIG_MSM_N_WAY_SMD)
 | |
| 		writel(0, A11RAMBACKBIAS);
 | |
| #endif
 | |
| 
 | |
| 		if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE)
 | |
| 			printk(KERN_INFO "msm_sleep(): enter "
 | |
| 			       "A11S_CLK_SLEEP_EN %x, A11S_PWRDOWN %x, "
 | |
| 			       "smsm_get_state %x\n", readl(A11S_CLK_SLEEP_EN),
 | |
| 			       readl(A11S_PWRDOWN), smsm_get_state(PM_SMSM_READ_STATE));
 | |
| 	}
 | |
| 
 | |
| 	if (sleep_mode <= MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT) {
 | |
| 		pm_saved_acpu_clk_rate = acpuclk_power_collapse(from_idle);
 | |
| 		if (msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK)
 | |
| 			printk(KERN_INFO "msm_sleep(): %ld enter power collapse"
 | |
| 			       "\n", pm_saved_acpu_clk_rate);
 | |
| 		if (pm_saved_acpu_clk_rate == 0)
 | |
| 			goto ramp_down_failed;
 | |
| 
 | |
| #ifdef CONFIG_AXI_SCREEN_POLICY
 | |
| 		/* Drop AXI request when the screen is on */
 | |
| 		if (axi_rate)
 | |
| 			clk_set_rate(axi_clk, sleep_axi_rate);
 | |
| #endif
 | |
| 	}
 | |
| #ifdef CONFIG_HTC_POWER_COLLAPSE_MAGIC
 | |
| 	magic_num = 0xAAAA1111;
 | |
| 	writel(magic_num, HTC_POWER_COLLAPSE_MAGIC_NUM);
 | |
| #endif
 | |
| 	if (sleep_mode < MSM_PM_SLEEP_MODE_APPS_SLEEP) {
 | |
| 		if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE)
 | |
| 			smsm_print_sleep_info(0);
 | |
| 		saved_vector[0] = msm_pm_reset_vector[0];
 | |
| 		saved_vector[1] = msm_pm_reset_vector[1];
 | |
| 		msm_pm_reset_vector[0] = 0xE51FF004; /* ldr pc, 4 */
 | |
| 		msm_pm_reset_vector[1] = virt_to_phys(msm_pm_collapse_exit);
 | |
| 		if (msm_pm_debug_mask & MSM_PM_DEBUG_RESET_VECTOR)
 | |
| 			printk(KERN_INFO "msm_sleep(): vector %x %x -> "
 | |
| 			       "%x %x\n", saved_vector[0], saved_vector[1],
 | |
| 			       msm_pm_reset_vector[0], msm_pm_reset_vector[1]);
 | |
| #ifdef CONFIG_VFP
 | |
| 		if (from_idle)
 | |
| 			vfp_flush_context();
 | |
| #endif
 | |
| 
 | |
| #ifdef CONFIG_CACHE_L2X0
 | |
| 		l2x0_suspend();
 | |
| #endif
 | |
| 		if (!from_idle) printk(KERN_INFO "[R] suspend end\n");
 | |
| 		/* reset idle sleep mode when suspend. */
 | |
| 		if (!from_idle) msm_pm_idle_sleep_mode = CONFIG_MSM7X00A_IDLE_SLEEP_MODE;
 | |
| 		collapsed = msm_pm_collapse();
 | |
| 		if (!from_idle) printk(KERN_INFO "[R] resume start\n");
 | |
| #ifdef CONFIG_CACHE_L2X0
 | |
| 		l2x0_resume(collapsed);
 | |
| #endif
 | |
| 		msm_pm_reset_vector[0] = saved_vector[0];
 | |
| 		msm_pm_reset_vector[1] = saved_vector[1];
 | |
| 		if (collapsed) {
 | |
| #ifdef CONFIG_VFP
 | |
| 			if (from_idle)
 | |
| 				vfp_reinit();
 | |
| #endif
 | |
| 			cpu_init();
 | |
| 			__asm__("cpsie   a");
 | |
| 			msm_fiq_exit_sleep();
 | |
| 			local_fiq_enable();
 | |
| 			rv = 0;
 | |
| 		}
 | |
| 		if (msm_pm_debug_mask & MSM_PM_DEBUG_POWER_COLLAPSE)
 | |
| 			printk(KERN_INFO "msm_pm_collapse(): returned %d\n",
 | |
| 			       collapsed);
 | |
| 		if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE)
 | |
| 			smsm_print_sleep_info(0);
 | |
| 	} else {
 | |
| 		msm_arch_idle();
 | |
| 		rv = 0;
 | |
| 	}
 | |
| #ifdef CONFIG_HTC_POWER_COLLAPSE_MAGIC
 | |
| 	magic_num = 0xBBBB9999;
 | |
| 	writel(magic_num, HTC_POWER_COLLAPSE_MAGIC_NUM);
 | |
| #endif
 | |
| 	if (sleep_mode <= MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT) {
 | |
| 		if (msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK)
 | |
| 			printk(KERN_INFO "msm_sleep(): exit power collapse %ld"
 | |
| 			       "\n", pm_saved_acpu_clk_rate);
 | |
| #if defined(CONFIG_ARCH_QSD8X50)
 | |
| 		if (acpuclk_set_rate(pm_saved_acpu_clk_rate, 1) < 0)
 | |
| #else
 | |
| 		if (acpuclk_set_rate(pm_saved_acpu_clk_rate,
 | |
| 			from_idle ? SETRATE_PC_IDLE : SETRATE_PC) < 0)
 | |
| #endif
 | |
| 			printk(KERN_ERR "msm_sleep(): clk_set_rate %ld "
 | |
| 			       "failed\n", pm_saved_acpu_clk_rate);
 | |
| 
 | |
| #ifdef CONFIG_AXI_SCREEN_POLICY
 | |
| 		/* Restore axi rate if needed */
 | |
| 		if (axi_rate)
 | |
| 			clk_set_rate(axi_clk, axi_rate);
 | |
| #endif
 | |
| 	}
 | |
| 	if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE)
 | |
| 		printk(KERN_INFO "msm_sleep(): exit A11S_CLK_SLEEP_EN %x, "
 | |
| 		       "A11S_PWRDOWN %x, smsm_get_state %x\n",
 | |
| 		       readl(A11S_CLK_SLEEP_EN), readl(A11S_PWRDOWN),
 | |
| 		       smsm_get_state(PM_SMSM_READ_STATE));
 | |
| ramp_down_failed:
 | |
| 	msm_irq_exit_sleep1();
 | |
| enter_failed:
 | |
| 	if (enter_state) {
 | |
| #if defined (CONFIG_ARCH_MSM7X30)
 | |
| 		writel(0, A11S_SECOP);
 | |
| 		writel(0, A11S_PWRDOWN);
 | |
| 		msm_spm_reinit();
 | |
| #else
 | |
| 		writel(0x00, A11S_CLK_SLEEP_EN);
 | |
| 		writel(0, A11S_PWRDOWN);
 | |
| #endif
 | |
| 		smsm_change_state(PM_SMSM_WRITE_STATE, enter_state, exit_state);
 | |
| 		msm_pm_wait_state(0, exit_wait_clear, exit_wait_any_set, 0);
 | |
| 		if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE)
 | |
| 			printk(KERN_INFO "msm_sleep(): sleep exit "
 | |
| 			       "A11S_CLK_SLEEP_EN %x, A11S_PWRDOWN %x, "
 | |
| 			       "smsm_get_state %x\n", readl(A11S_CLK_SLEEP_EN),
 | |
| 			       readl(A11S_PWRDOWN), smsm_get_state(PM_SMSM_READ_STATE));
 | |
| 		if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE)
 | |
| 			smsm_print_sleep_info(0);
 | |
| 		if (msm_pm_debug_mask & MSM_PM_DEBUG_WAKEUP_REASON && !from_idle)
 | |
| 			smsm_print_sleep_info(1);
 | |
| 	}
 | |
| 	msm_irq_exit_sleep2();
 | |
| 	if (enter_state) {
 | |
| 		smsm_change_state(PM_SMSM_WRITE_STATE, exit_state, PM_SMSM_WRITE_RUN);
 | |
| 		msm_pm_wait_state(PM_SMSM_READ_RUN, 0, 0, 0);
 | |
| 		if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE)
 | |
| 			printk(KERN_INFO "msm_sleep(): sleep exit "
 | |
| 			       "A11S_CLK_SLEEP_EN %x, A11S_PWRDOWN %x, "
 | |
| 			       "smsm_get_state %x\n", readl(A11S_CLK_SLEEP_EN),
 | |
| 			       readl(A11S_PWRDOWN), smsm_get_state(PM_SMSM_READ_STATE));
 | |
| 	}
 | |
| 	msm_irq_exit_sleep3();
 | |
| 	msm_gpio_exit_sleep();
 | |
| 	smd_sleep_exit();
 | |
| 	clk_exit_sleep();
 | |
| 	return rv;
 | |
| }
 | |
| 
 | |
| static int msm_pm_idle_spin(void)
 | |
| {
 | |
| 	int spin;
 | |
| 	spin = msm_pm_idle_spin_time >> 10;
 | |
| 	while (spin-- > 0) {
 | |
| 		if (msm_irq_pending()) {
 | |
| 			return -1;
 | |
| 		}
 | |
| 		udelay(1);
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void arch_idle(void)
 | |
| {
 | |
| 	int ret;
 | |
| 	int64_t sleep_time;
 | |
| 	int low_power = 0;
 | |
| #ifdef CONFIG_MSM_IDLE_STATS
 | |
| 	int64_t t1;
 | |
| 	static int64_t t2;
 | |
| 	int exit_stat;
 | |
| #endif
 | |
| 	int allow_sleep =
 | |
| 		msm_pm_idle_sleep_mode < MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT &&
 | |
| #ifdef CONFIG_HAS_WAKELOCK
 | |
| 		!has_wake_lock(WAKE_LOCK_IDLE) &&
 | |
| #endif
 | |
| 		msm_irq_idle_sleep_allowed();
 | |
| 	if (msm_pm_reset_vector == NULL)
 | |
| 		return;
 | |
| 
 | |
| 	sleep_time = msm_timer_enter_idle();
 | |
| #ifdef CONFIG_MSM_IDLE_STATS
 | |
| 	t1 = ktime_to_ns(ktime_get());
 | |
| 	msm_pm_add_stat(MSM_PM_STAT_NOT_IDLE, t1 - t2);
 | |
| 	msm_pm_add_stat(MSM_PM_STAT_REQUESTED_IDLE, sleep_time);
 | |
| #endif
 | |
| 	if (msm_pm_debug_mask & MSM_PM_DEBUG_IDLE)
 | |
| 		printk(KERN_INFO "arch_idle: sleep time %llu, allow_sleep %d\n",
 | |
| 		       sleep_time, allow_sleep);
 | |
| 	if (sleep_time < msm_pm_idle_sleep_min_time || !allow_sleep) {
 | |
| 		unsigned long saved_rate;
 | |
| 		/* only spin while trying wfi ramp down */
 | |
| 		if (acpuclk_get_wfi_rate() && msm_pm_idle_spin() < 0) {
 | |
| #ifdef CONFIG_MSM_IDLE_STATS
 | |
| 			exit_stat = MSM_PM_STAT_IDLE_SPIN;
 | |
| #endif
 | |
| 			goto abort_idle;
 | |
| 		}
 | |
| 		saved_rate = acpuclk_wait_for_irq();
 | |
| 
 | |
| 
 | |
| 		if (saved_rate && msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK)
 | |
| 			printk(KERN_DEBUG "arch_idle: clk %ld -> swfi\n",
 | |
| 				saved_rate);
 | |
| 
 | |
| 		/*
 | |
| 		 * If there is a wfi speed specified and we failed to ramp, do not
 | |
| 		 * go into wfi.
 | |
| 		 */
 | |
| 		if (acpuclk_get_wfi_rate() && !saved_rate)
 | |
| 			while (!msm_irq_pending())
 | |
| 				udelay(1);
 | |
| 		else
 | |
| 			msm_arch_idle();
 | |
| 
 | |
| 		if (msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK)
 | |
| 			printk(KERN_DEBUG "msm_sleep: clk swfi -> %ld\n",
 | |
| 				saved_rate);
 | |
| #if defined(CONFIG_ARCH_QSD8X50)
 | |
| 		if (saved_rate && acpuclk_set_rate(saved_rate, 1) < 0)
 | |
| #else
 | |
| 		if (saved_rate
 | |
| 		    && acpuclk_set_rate(saved_rate, SETRATE_SWFI) < 0)
 | |
| #endif
 | |
| 			printk(KERN_ERR "msm_sleep(): clk_set_rate %ld "
 | |
| 			       "failed\n", saved_rate);
 | |
| #ifdef CONFIG_MSM_IDLE_STATS
 | |
| 		exit_stat = MSM_PM_STAT_IDLE_WFI;
 | |
| #endif
 | |
|   	} else {
 | |
| 		if (msm_pm_idle_spin() < 0) {
 | |
| #ifdef CONFIG_MSM_IDLE_STATS
 | |
| 			exit_stat = MSM_PM_STAT_IDLE_SPIN;
 | |
| #endif
 | |
| 			goto abort_idle;
 | |
| 		}
 | |
| 
 | |
| 		low_power = 1;
 | |
| 		do_div(sleep_time, NSEC_PER_SEC / 32768);
 | |
| 		if (sleep_time > 0x6DDD000) {
 | |
| 			printk("sleep_time too big %lld\n", sleep_time);
 | |
| 			sleep_time = 0x6DDD000;
 | |
| 		}
 | |
| 		ret = msm_sleep(msm_pm_idle_sleep_mode, sleep_time, 1);
 | |
| #ifdef CONFIG_MSM_IDLE_STATS
 | |
| 		if (ret)
 | |
| 			exit_stat = MSM_PM_STAT_IDLE_FAILED_SLEEP;
 | |
| 		else
 | |
| 			exit_stat = MSM_PM_STAT_IDLE_SLEEP;
 | |
| #endif
 | |
| 	}
 | |
| abort_idle:
 | |
| 	msm_timer_exit_idle(low_power);
 | |
| #ifdef CONFIG_MSM_IDLE_STATS
 | |
| 	t2 = ktime_to_ns(ktime_get());
 | |
| 	msm_pm_add_stat(exit_stat, t2 - t1);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| static int msm_pm_enter(suspend_state_t state)
 | |
| {
 | |
| 	msm_sleep(msm_pm_sleep_mode, msm_pm_max_sleep_time, 0);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static struct platform_suspend_ops msm_pm_ops = {
 | |
| 	.enter		= msm_pm_enter,
 | |
| 	.valid		= suspend_valid_only_mem,
 | |
| };
 | |
| 
 | |
| static uint32_t restart_reason = 0x776655AA;
 | |
| 
 | |
| static int msm_wakeup_after;	/* default, no wakeup by alarm */
 | |
| static int msm_power_wakeup_after(const char *val, struct kernel_param *kp)
 | |
| {
 | |
| 	int ret;
 | |
| 	struct uart_port *port;
 | |
| 	struct msm_port *msm_port;
 | |
| 
 | |
| 	ret = param_set_int(val, kp);
 | |
| 	printk(KERN_INFO "+msm_power_wakeup_after, ret=%d\r\n", ret);
 | |
| 
 | |
| 	if (!ret) {
 | |
| 		printk(KERN_INFO "msm_wakeup_after=%d\r\n", msm_wakeup_after);
 | |
| 		if (msm_wakeup_after < 0)
 | |
| 			msm_wakeup_after = 0; /* invalid, no wakeup */
 | |
| 	}
 | |
| 	printk(KERN_INFO "-msm_power_wakeup_after, msm_wakeup_after=%d\r\n", msm_wakeup_after);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| module_param_call(wakeup_after, msm_power_wakeup_after, param_get_int,
 | |
| 		  &msm_wakeup_after, S_IWUSR | S_IRUGO);
 | |
| 
 | |
| static void msm_pm_power_off(void)
 | |
| {
 | |
| 	printk(KERN_INFO "msm_pm_power_off:wakeup after %d\r\n", msm_wakeup_after);
 | |
| 
 | |
| 	if (msm_wakeup_after)
 | |
| 		msm_proc_comm(PCOM_SET_RTC_ALARM, &msm_wakeup_after, 0);
 | |
| 
 | |
| 	pmic_glb_power_down();
 | |
| 
 | |
| #if CONFIG_MSM_RMT_STORAGE_SERVER
 | |
| 		printk(KERN_INFO "from %s\r\n", __func__);
 | |
| 		wait_rmt_final_call_back(10);
 | |
| 		printk(KERN_INFO "back %s\r\n", __func__);
 | |
| #endif
 | |
| 	for (;;) ;
 | |
| }
 | |
| 
 | |
| static bool console_flushed;
 | |
| 
 | |
| void msm_pm_flush_console(void)
 | |
| {
 | |
| 	if (console_flushed)
 | |
| 		return;
 | |
| 	console_flushed = true;
 | |
| 
 | |
| 	printk("\n");
 | |
| 	printk(KERN_EMERG "Restarting %s\n", linux_banner);
 | |
| 	if (!try_acquire_console_sem()) {
 | |
| 		release_console_sem();
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	mdelay(50);
 | |
| 
 | |
| 	local_irq_disable();
 | |
| 	if (try_acquire_console_sem())
 | |
| 		printk(KERN_EMERG "msm_restart: Console was locked! Busting\n");
 | |
| 	else
 | |
| 		printk(KERN_EMERG "msm_restart: Console was locked!\n");
 | |
| 	release_console_sem();
 | |
| }
 | |
| 
 | |
| #if defined(CONFIG_MACH_HTCLEO)
 | |
| static void htcleo_save_reset_reason()
 | |
| {
 | |
| 	/* save restart_reason to be accesible in bootloader @ ramconsole - 0x1000*/
 | |
| 	uint32_t *bootloader_reset_reason = ioremap(0x2FFB0000, PAGE_SIZE);
 | |
| 	if(bootloader_reset_reason!=NULL)
 | |
| 	{
 | |
| 		printk(KERN_INFO "msm_restart saving reason %x @ 0x2FFB0000 \n", restart_reason);
 | |
| 		bootloader_reset_reason[0]=restart_reason;
 | |
| 		bootloader_reset_reason[1]=restart_reason^0x004b4c63; //XOR with cLK signature so we know is not trash
 | |
| 	}
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static void msm_pm_restart(char str)
 | |
| {
 | |
| 	msm_pm_flush_console();
 | |
| 
 | |
| #if defined(CONFIG_MACH_HTCLEO)
 | |
| 	htcleo_save_reset_reason();
 | |
| #endif
 | |
| 
 | |
| 	/*  always reboot device through proc comm */
 | |
| 	if (restart_reason == 0x6f656d99)
 | |
| 		msm_proc_comm(PCOM_RESET_CHIP_IMM, &restart_reason, 0);
 | |
| 	else
 | |
| 		msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0);
 | |
| 
 | |
| #if CONFIG_MSM_RMT_STORAGE_SERVER
 | |
| 	printk(KERN_INFO "from %s\r\n", __func__);
 | |
| 	wait_rmt_final_call_back(10);
 | |
| 	printk(KERN_INFO "back %s\r\n", __func__);
 | |
| 	/* wait 2 seconds to let radio reset device after the final EFS sync*/
 | |
| 	mdelay(2000);
 | |
| #else
 | |
| 	/* In case Radio is dead, reset device after notify Radio 5 seconds */
 | |
| 	mdelay(5000);
 | |
| #endif
 | |
| 
 | |
| 	/* hard reboot if possible */
 | |
| 	if (msm_hw_reset_hook) {
 | |
| 		printk(KERN_INFO "%s : Do HW_RESET by APP not by RADIO\r\n", __func__);
 | |
| 		msm_hw_reset_hook();
 | |
| 	}
 | |
| 
 | |
| 	for (;;) ;
 | |
| }
 | |
| 
 | |
| static int msm_reboot_call(struct notifier_block *this, unsigned long code, void *_cmd)
 | |
| {
 | |
| 	if((code == SYS_RESTART) && _cmd) {
 | |
| 		char *cmd = _cmd;
 | |
| 		if (!strcmp(cmd, "bootloader")) {
 | |
| 			restart_reason = 0x77665500;
 | |
| 		} else if (!strcmp(cmd, "recovery")) {
 | |
| 			restart_reason = 0x77665502;
 | |
| 		} else if (!strcmp(cmd, "eraseflash")) {
 | |
| 			restart_reason = 0x776655EF;
 | |
| 		} else if (!strncmp(cmd, "oem-", 4)) {
 | |
| 			unsigned code = simple_strtoul(cmd + 4, 0, 16) & 0xff;
 | |
| 			restart_reason = 0x6f656d00 | code;
 | |
| 		} else if (!strcmp(cmd, "force-hard")) {
 | |
| 			restart_reason = 0x776655AA;
 | |
| 		} else {
 | |
| 			restart_reason = 0x77665501;
 | |
| 		}
 | |
| 	}
 | |
| 	return NOTIFY_DONE;
 | |
| }
 | |
| 
 | |
| static struct notifier_block msm_reboot_notifier =
 | |
| {
 | |
| 	.notifier_call = msm_reboot_call,
 | |
| };
 | |
| 
 | |
| #ifdef CONFIG_MSM_IDLE_STATS
 | |
| static int msm_pm_read_proc(char *page, char **start, off_t off,
 | |
|                                int count, int *eof, void *data)
 | |
| {
 | |
| 	int len = 0;
 | |
| 	int i, j;
 | |
| 	char *p = page;
 | |
| 
 | |
| 	for (i = 0; i < ARRAY_SIZE(msm_pm_stats); i++) {
 | |
| 		int64_t bucket_time;
 | |
| 		int64_t s;
 | |
| 		uint32_t ns;
 | |
| 		s = msm_pm_stats[i].total_time;
 | |
| 		ns = do_div(s, NSEC_PER_SEC);
 | |
| 		p += sprintf(p,
 | |
| 			"%s:\n"
 | |
| 			"  count: %7d\n"
 | |
| 			"  total_time: %lld.%09u\n",
 | |
| 			msm_pm_stats[i].name,
 | |
| 			msm_pm_stats[i].count,
 | |
| 			s, ns);
 | |
| 		bucket_time = CONFIG_MSM_IDLE_STATS_FIRST_BUCKET;
 | |
| 		for (j = 0; j < CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1; j++) {
 | |
| 			s = bucket_time;
 | |
| 			ns = do_div(s, NSEC_PER_SEC);
 | |
| 			p += sprintf(p, "   <%2lld.%09u: %7d (%lld-%lld)\n",
 | |
| 				s, ns, msm_pm_stats[i].bucket[j],
 | |
| 				msm_pm_stats[i].min_time[j],
 | |
| 				msm_pm_stats[i].max_time[j]);
 | |
| 			bucket_time <<= CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT;
 | |
| 		}
 | |
| 		p += sprintf(p, "  >=%2lld.%09u: %7d (%lld-%lld)\n",
 | |
| 			s, ns, msm_pm_stats[i].bucket[j],
 | |
| 			msm_pm_stats[i].min_time[j],
 | |
| 			msm_pm_stats[i].max_time[j]);
 | |
| 	}
 | |
| 	*start = page + off;
 | |
| 
 | |
| 	len = p - page;
 | |
| 	if (len > off)
 | |
| 		len -= off;
 | |
| 	else
 | |
| 		len = 0;
 | |
| 
 | |
| 	return len < count ? len  : count;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns)
 | |
| {
 | |
| 	int64_t max_sleep_time_bs = max_sleep_time_ns;
 | |
| 
 | |
| 	/* Convert from ns -> BS units */
 | |
| 	do_div(max_sleep_time_bs, NSEC_PER_SEC / 32768);
 | |
| 
 | |
| 	if (max_sleep_time_bs > 0x6DDD000)
 | |
| 		msm_pm_max_sleep_time = (uint32_t) 0x6DDD000;
 | |
| 	else
 | |
| 		msm_pm_max_sleep_time = (uint32_t) max_sleep_time_bs;
 | |
| 
 | |
| 	if (msm_pm_debug_mask & MSM_PM_DEBUG_SUSPEND)
 | |
| 		printk("%s: Requested %lldns (%lldbs), Giving %ubs\n",
 | |
| 		       __func__, max_sleep_time_ns, 
 | |
| 		       max_sleep_time_bs, 
 | |
| 		       msm_pm_max_sleep_time);
 | |
| }
 | |
| EXPORT_SYMBOL(msm_pm_set_max_sleep_time);
 | |
| 
 | |
| #ifdef CONFIG_EARLYSUSPEND
 | |
| /* axi 128 screen on, 61mhz screen off */
 | |
| static void axi_early_suspend(struct early_suspend *handler)
 | |
| {
 | |
| 	axi_rate = 0;
 | |
| 	clk_set_rate(axi_clk, axi_rate);
 | |
| }
 | |
| 
 | |
| static void axi_late_resume(struct early_suspend *handler)
 | |
| {
 | |
| 	axi_rate = 128000000;
 | |
| 	sleep_axi_rate = 117000000;
 | |
| 	clk_set_rate(axi_clk, axi_rate);
 | |
| }
 | |
| 
 | |
| static struct early_suspend axi_screen_suspend = {
 | |
| 	.suspend = axi_early_suspend,
 | |
| 	.resume = axi_late_resume,
 | |
| };
 | |
| #endif
 | |
| 
 | |
| static void __init msm_pm_axi_init(void)
 | |
| {
 | |
| #ifdef CONFIG_EARLYSUSPEND
 | |
| 	axi_clk = clk_get(NULL, "ebi1_clk");
 | |
| 	if (IS_ERR(axi_clk)) {
 | |
| 		int result = PTR_ERR(axi_clk);
 | |
| 		pr_err("clk_get(ebi1_clk) returned %d\n", result);
 | |
| 		return;
 | |
| 	}
 | |
| 	axi_rate = 128000000;
 | |
| 	sleep_axi_rate = 117000000;
 | |
| 	clk_set_rate(axi_clk, axi_rate);
 | |
| 	register_early_suspend(&axi_screen_suspend);
 | |
| #else
 | |
| 	axi_rate = 0;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| static int __init msm_pm_init(void)
 | |
| {
 | |
| 	pm_power_off = msm_pm_power_off;
 | |
| 	arm_pm_restart = msm_pm_restart;
 | |
| 	msm_pm_max_sleep_time = 0;
 | |
| #if defined(CONFIG_ARCH_MSM_SCORPION)
 | |
| #ifdef CONFIG_AXI_SCREEN_POLICY
 | |
| 	msm_pm_axi_init();
 | |
| #endif
 | |
| #endif
 | |
| 
 | |
| 	register_reboot_notifier(&msm_reboot_notifier);
 | |
| 	msm_pm_reset_vector = ioremap(0x0, PAGE_SIZE);
 | |
| 
 | |
| #if defined(CONFIG_MACH_HTCLEO)
 | |
| 	// if cLK is bootloader 0x0 is protected and not writtable but cLK changed reset vecotr to jump at address stored at 0x11800004
 | |
| 	if(htcleo_is_nand_boot()==2){
 | |
| 		pr_info("msm_pm: 0x00000000: %x\n", msm_pm_reset_vector[0]);
 | |
| 		pr_info("msm_pm: 0x00000004: %x\n", msm_pm_reset_vector[1]);
 | |
| 		msm_pm_reset_vector = ioremap(0x11800000, PAGE_SIZE);
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	if (msm_pm_reset_vector == NULL) {
 | |
| 		printk(KERN_ERR "msm_pm_init: failed to map reset vector\n");
 | |
| 		return -ENODEV;
 | |
| 	}
 | |
| 
 | |
| 	suspend_set_ops(&msm_pm_ops);
 | |
| 
 | |
| #ifdef CONFIG_MSM_IDLE_STATS
 | |
| 	create_proc_read_entry("msm_pm_stats", S_IRUGO,
 | |
| 				NULL, msm_pm_read_proc, NULL);
 | |
| #endif
 | |
| 
 | |
| 	if ((board_mfg_mode() == 0) || (board_mfg_mode() == 1)) {
 | |
| 		disable_hlt();
 | |
| 		schedule_delayed_work(&work_expire_boot_lock, BOOT_LOCK_TIMEOUT);
 | |
| 		pr_info("Acquire 'boot-time' halt_lock\n");
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| __initcall(msm_pm_init);
 |