Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pm-imx5.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
3  *
4  * The code contained herein is licensed under the GNU General Public
5  * License. You may obtain a copy of the GNU General Public License
6  * Version 2 or later at the following locations:
7  *
8  * http://www.opensource.org/licenses/gpl-license.html
9  * http://www.gnu.org/copyleft/gpl.html
10  */
11 #include <linux/suspend.h>
12 #include <linux/clk.h>
13 #include <linux/io.h>
14 #include <linux/err.h>
15 #include <linux/export.h>
16 #include <asm/cacheflush.h>
17 #include <asm/system_misc.h>
18 #include <asm/tlbflush.h>
19 #include <mach/common.h>
20 #include <mach/cpuidle.h>
21 #include <mach/hardware.h>
22 #include "crm-regs-imx5.h"
23 
24 /*
25  * The WAIT_UNCLOCKED_POWER_OFF state only requires <= 500ns to exit.
26  * This is also the lowest power state possible without affecting
27  * non-cpu parts of the system. For these reasons, imx5 should default
28  * to always using this state for cpu idling. The PM_SUSPEND_STANDBY also
29  * uses this state and needs to take no action when registers remain confgiured
30  * for this state.
31  */
32 #define IMX5_DEFAULT_CPU_IDLE_STATE WAIT_UNCLOCKED_POWER_OFF
33 
34 /*
35  * set cpu low power mode before WFI instruction. This function is called
36  * mx5 because it can be used for mx50, mx51, and mx53.
37  */
38 static void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode)
39 {
40  u32 plat_lpc, arm_srpgcr, ccm_clpcr;
41  u32 empgc0, empgc1;
42  int stop_mode = 0;
43 
44  /* always allow platform to issue a deep sleep mode request */
51 
52  switch (mode) {
53  case WAIT_CLOCKED:
54  break;
55  case WAIT_UNCLOCKED:
56  ccm_clpcr |= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET;
57  break;
59  case STOP_POWER_OFF:
60  plat_lpc |= MXC_CORTEXA8_PLAT_LPC_DSM
62  if (mode == WAIT_UNCLOCKED_POWER_OFF) {
63  ccm_clpcr |= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET;
64  ccm_clpcr &= ~MXC_CCM_CLPCR_VSTBY;
65  ccm_clpcr &= ~MXC_CCM_CLPCR_SBYOS;
66  stop_mode = 0;
67  } else {
68  ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET;
69  ccm_clpcr |= 0x3 << MXC_CCM_CLPCR_STBY_COUNT_OFFSET;
70  ccm_clpcr |= MXC_CCM_CLPCR_VSTBY;
71  ccm_clpcr |= MXC_CCM_CLPCR_SBYOS;
72  stop_mode = 1;
73  }
74  arm_srpgcr |= MXC_SRPGCR_PCR;
75  break;
76  case STOP_POWER_ON:
77  ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET;
78  break;
79  default:
80  printk(KERN_WARNING "UNKNOWN cpu power mode: %d\n", mode);
81  return;
82  }
83 
85  __raw_writel(ccm_clpcr, MXC_CCM_CLPCR);
86  __raw_writel(arm_srpgcr, MXC_SRPG_ARM_SRPGCR);
87 
88  /* Enable NEON SRPG for all but MX50TO1.0. */
91 
92  if (stop_mode) {
93  empgc0 |= MXC_SRPGCR_PCR;
94  empgc1 |= MXC_SRPGCR_PCR;
95 
98  }
99 }
100 
101 static int mx5_suspend_enter(suspend_state_t state)
102 {
103  switch (state) {
104  case PM_SUSPEND_MEM:
105  mx5_cpu_lp_set(STOP_POWER_OFF);
106  break;
107  case PM_SUSPEND_STANDBY:
108  /* DEFAULT_IDLE_STATE already configured */
109  break;
110  default:
111  return -EINVAL;
112  }
113 
114  if (state == PM_SUSPEND_MEM) {
116  flush_cache_all();
117 
118  /*clear the EMPGC0/1 bits */
121  }
122  cpu_do_idle();
123 
124  /* return registers to default idle state */
125  mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE);
126  return 0;
127 }
128 
129 static int mx5_pm_valid(suspend_state_t state)
130 {
131  return (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX);
132 }
133 
134 static const struct platform_suspend_ops mx5_suspend_ops = {
135  .valid = mx5_pm_valid,
136  .enter = mx5_suspend_enter,
137 };
138 
139 static inline int imx5_cpu_do_idle(void)
140 {
141  int ret = tzic_enable_wake();
142 
143  if (likely(!ret))
144  cpu_do_idle();
145 
146  return ret;
147 }
148 
149 static void imx5_pm_idle(void)
150 {
151  imx5_cpu_do_idle();
152 }
153 
154 static int imx5_cpuidle_enter(struct cpuidle_device *dev,
155  struct cpuidle_driver *drv, int idx)
156 {
157  int ret;
158 
159  ret = imx5_cpu_do_idle();
160  if (ret < 0)
161  return ret;
162 
163  return idx;
164 }
165 
166 static struct cpuidle_driver imx5_cpuidle_driver = {
167  .name = "imx5_cpuidle",
168  .owner = THIS_MODULE,
169  .en_core_tk_irqen = 1,
170  .states[0] = {
171  .enter = imx5_cpuidle_enter,
172  .exit_latency = 2,
173  .target_residency = 1,
174  .flags = CPUIDLE_FLAG_TIME_VALID,
175  .name = "IMX5 SRPG",
176  .desc = "CPU state retained,powered off",
177  },
178  .state_count = 1,
179 };
180 
181 static int __init imx5_pm_common_init(void)
182 {
183  int ret;
184  struct clk *gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs");
185 
186  if (IS_ERR(gpc_dvfs_clk))
187  return PTR_ERR(gpc_dvfs_clk);
188 
189  ret = clk_prepare_enable(gpc_dvfs_clk);
190  if (ret)
191  return ret;
192 
193  arm_pm_idle = imx5_pm_idle;
194 
195  /* Set the registers to the default cpu idle state. */
196  mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE);
197 
198  imx_cpuidle_init(&imx5_cpuidle_driver);
199  return 0;
200 }
201 
203 {
204  int ret = imx5_pm_common_init();
205  if (!ret)
206  suspend_set_ops(&mx5_suspend_ops);
207 }
208 
210 {
211  imx5_pm_common_init();
212 }