Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
omap-mpuss-lowpower.c
Go to the documentation of this file.
1 /*
2  * OMAP MPUSS low power code
3  *
4  * Copyright (C) 2011 Texas Instruments, Inc.
5  * Santosh Shilimkar <[email protected]>
6  *
7  * OMAP4430 MPUSS mainly consists of dual Cortex-A9 with per-CPU
8  * Local timer and Watchdog, GIC, SCU, PL310 L2 cache controller,
9  * CPU0 and CPU1 LPRM modules.
10  * CPU0, CPU1 and MPUSS each have there own power domain and
11  * hence multiple low power combinations of MPUSS are possible.
12  *
13  * The CPU0 and CPU1 can't support Closed switch Retention (CSWR)
14  * because the mode is not supported by hw constraints of dormant
15  * mode. While waking up from the dormant mode, a reset signal
16  * to the Cortex-A9 processor must be asserted by the external
17  * power controller.
18  *
19  * With architectural inputs and hardware recommendations, only
20  * below modes are supported from power gain vs latency point of view.
21  *
22  * CPU0 CPU1 MPUSS
23  * ----------------------------------------------
24  * ON ON ON
25  * ON(Inactive) OFF ON(Inactive)
26  * OFF OFF CSWR
27  * OFF OFF OSWR
28  * OFF OFF OFF(Device OFF *TBD)
29  * ----------------------------------------------
30  *
31  * Note: CPU0 is the master core and it is the last CPU to go down
32  * and first to wake-up when MPUSS low power states are excercised
33  *
34  *
35  * This program is free software; you can redistribute it and/or modify
36  * it under the terms of the GNU General Public License version 2 as
37  * published by the Free Software Foundation.
38  */
39 
40 #include <linux/kernel.h>
41 #include <linux/io.h>
42 #include <linux/errno.h>
43 #include <linux/linkage.h>
44 #include <linux/smp.h>
45 
46 #include <asm/cacheflush.h>
47 #include <asm/tlbflush.h>
48 #include <asm/smp_scu.h>
49 #include <asm/pgalloc.h>
50 #include <asm/suspend.h>
52 
53 #include "common.h"
54 #include "omap44xx.h"
55 #include "omap4-sar-layout.h"
56 #include "pm.h"
57 #include "prcm_mpu44xx.h"
58 #include "prminst44xx.h"
59 #include "prcm44xx.h"
60 #include "prm44xx.h"
61 #include "prm-regbits-44xx.h"
62 
63 #ifdef CONFIG_SMP
64 
65 struct omap4_cpu_pm_info {
66  struct powerdomain *pwrdm;
67  void __iomem *scu_sar_addr;
68  void __iomem *wkup_sar_addr;
69  void __iomem *l2x0_sar_addr;
70 };
71 
72 static DEFINE_PER_CPU(struct omap4_cpu_pm_info, omap4_pm_info);
73 static struct powerdomain *mpuss_pd;
74 static void __iomem *sar_base;
75 
76 /*
77  * Program the wakeup routine address for the CPU0 and CPU1
78  * used for OFF or DORMANT wakeup.
79  */
80 static inline void set_cpu_wakeup_addr(unsigned int cpu_id, u32 addr)
81 {
82  struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
83 
84  __raw_writel(addr, pm_info->wkup_sar_addr);
85 }
86 
87 /*
88  * Set the CPUx powerdomain's previous power state
89  */
90 static inline void set_cpu_next_pwrst(unsigned int cpu_id,
91  unsigned int power_state)
92 {
93  struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
94 
95  pwrdm_set_next_pwrst(pm_info->pwrdm, power_state);
96 }
97 
98 /*
99  * Read CPU's previous power state
100  */
101 static inline unsigned int read_cpu_prev_pwrst(unsigned int cpu_id)
102 {
103  struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
104 
105  return pwrdm_read_prev_pwrst(pm_info->pwrdm);
106 }
107 
108 /*
109  * Clear the CPUx powerdomain's previous power state
110  */
111 static inline void clear_cpu_prev_pwrst(unsigned int cpu_id)
112 {
113  struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
114 
115  pwrdm_clear_all_prev_pwrst(pm_info->pwrdm);
116 }
117 
118 /*
119  * Store the SCU power status value to scratchpad memory
120  */
121 static void scu_pwrst_prepare(unsigned int cpu_id, unsigned int cpu_state)
122 {
123  struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
124  u32 scu_pwr_st;
125 
126  switch (cpu_state) {
127  case PWRDM_POWER_RET:
128  scu_pwr_st = SCU_PM_DORMANT;
129  break;
130  case PWRDM_POWER_OFF:
131  scu_pwr_st = SCU_PM_POWEROFF;
132  break;
133  case PWRDM_POWER_ON:
135  default:
136  scu_pwr_st = SCU_PM_NORMAL;
137  break;
138  }
139 
140  __raw_writel(scu_pwr_st, pm_info->scu_sar_addr);
141 }
142 
143 /* Helper functions for MPUSS OSWR */
144 static inline void mpuss_clear_prev_logic_pwrst(void)
145 {
146  u32 reg;
147 
152 }
153 
154 static inline void cpu_clear_prev_logic_pwrst(unsigned int cpu_id)
155 {
156  u32 reg;
157 
158  if (cpu_id) {
163  } else {
168  }
169 }
170 
175 u32 omap4_mpuss_read_prev_context_state(void)
176 {
177  u32 reg;
178 
182  return reg;
183 }
184 
185 /*
186  * Store the CPU cluster state for L2X0 low power operations.
187  */
188 static void l2x0_pwrst_prepare(unsigned int cpu_id, unsigned int save_state)
189 {
190  struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
191 
192  __raw_writel(save_state, pm_info->l2x0_sar_addr);
193 }
194 
195 /*
196  * Save the L2X0 AUXCTRL and POR value to SAR memory. Its used to
197  * in every restore MPUSS OFF path.
198  */
199 #ifdef CONFIG_CACHE_L2X0
200 static void save_l2x0_context(void)
201 {
202  u32 val;
203  void __iomem *l2x0_base = omap4_get_l2cache_base();
204 
205  val = __raw_readl(l2x0_base + L2X0_AUX_CTRL);
206  __raw_writel(val, sar_base + L2X0_AUXCTRL_OFFSET);
207  val = __raw_readl(l2x0_base + L2X0_PREFETCH_CTRL);
208  __raw_writel(val, sar_base + L2X0_PREFETCH_CTRL_OFFSET);
209 }
210 #else
211 static void save_l2x0_context(void)
212 {}
213 #endif
214 
229 int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
230 {
231  unsigned int save_state = 0;
232  unsigned int wakeup_cpu;
233 
234  if (omap_rev() == OMAP4430_REV_ES1_0)
235  return -ENXIO;
236 
237  switch (power_state) {
238  case PWRDM_POWER_ON:
240  save_state = 0;
241  break;
242  case PWRDM_POWER_OFF:
243  save_state = 1;
244  break;
245  case PWRDM_POWER_RET:
246  default:
247  /*
248  * CPUx CSWR is invalid hardware state. Also CPUx OSWR
249  * doesn't make much scense, since logic is lost and $L1
250  * needs to be cleaned because of coherency. This makes
251  * CPUx OSWR equivalent to CPUX OFF and hence not supported
252  */
253  WARN_ON(1);
254  return -ENXIO;
255  }
256 
258 
259  /*
260  * Check MPUSS next state and save interrupt controller if needed.
261  * In MPUSS OSWR or device OFF, interrupt controller contest is lost.
262  */
263  mpuss_clear_prev_logic_pwrst();
264  if ((pwrdm_read_next_pwrst(mpuss_pd) == PWRDM_POWER_RET) &&
266  save_state = 2;
267 
268  cpu_clear_prev_logic_pwrst(cpu);
269  set_cpu_next_pwrst(cpu, power_state);
270  set_cpu_wakeup_addr(cpu, virt_to_phys(omap4_cpu_resume));
271  scu_pwrst_prepare(cpu, power_state);
272  l2x0_pwrst_prepare(cpu, save_state);
273 
274  /*
275  * Call low level function with targeted low power state.
276  */
277  cpu_suspend(save_state, omap4_finish_suspend);
278 
279  /*
280  * Restore the CPUx power state to ON otherwise CPUx
281  * power domain can transitions to programmed low power
282  * state while doing WFI outside the low powe code. On
283  * secure devices, CPUx does WFI which can result in
284  * domain transition
285  */
286  wakeup_cpu = smp_processor_id();
287  set_cpu_next_pwrst(wakeup_cpu, PWRDM_POWER_ON);
288 
290 
291  return 0;
292 }
293 
299 int __cpuinit omap4_hotplug_cpu(unsigned int cpu, unsigned int power_state)
300 {
301  unsigned int cpu_state = 0;
302 
303  if (omap_rev() == OMAP4430_REV_ES1_0)
304  return -ENXIO;
305 
306  if (power_state == PWRDM_POWER_OFF)
307  cpu_state = 1;
308 
309  clear_cpu_prev_pwrst(cpu);
310  set_cpu_next_pwrst(cpu, power_state);
311  set_cpu_wakeup_addr(cpu, virt_to_phys(omap_secondary_startup));
312  scu_pwrst_prepare(cpu, power_state);
313 
314  /*
315  * CPU never retuns back if targeted power state is OFF mode.
316  * CPU ONLINE follows normal CPU ONLINE ptah via
317  * omap_secondary_startup().
318  */
319  omap4_finish_suspend(cpu_state);
320 
321  set_cpu_next_pwrst(cpu, PWRDM_POWER_ON);
322  return 0;
323 }
324 
325 
326 /*
327  * Initialise OMAP4 MPUSS
328  */
329 int __init omap4_mpuss_init(void)
330 {
331  struct omap4_cpu_pm_info *pm_info;
332 
333  if (omap_rev() == OMAP4430_REV_ES1_0) {
334  WARN(1, "Power Management not supported on OMAP4430 ES1.0\n");
335  return -ENODEV;
336  }
337 
338  sar_base = omap4_get_sar_ram_base();
339 
340  /* Initilaise per CPU PM information */
341  pm_info = &per_cpu(omap4_pm_info, 0x0);
342  pm_info->scu_sar_addr = sar_base + SCU_OFFSET0;
343  pm_info->wkup_sar_addr = sar_base + CPU0_WAKEUP_NS_PA_ADDR_OFFSET;
344  pm_info->l2x0_sar_addr = sar_base + L2X0_SAVE_OFFSET0;
345  pm_info->pwrdm = pwrdm_lookup("cpu0_pwrdm");
346  if (!pm_info->pwrdm) {
347  pr_err("Lookup failed for CPU0 pwrdm\n");
348  return -ENODEV;
349  }
350 
351  /* Clear CPU previous power domain state */
352  pwrdm_clear_all_prev_pwrst(pm_info->pwrdm);
353  cpu_clear_prev_logic_pwrst(0);
354 
355  /* Initialise CPU0 power domain state to ON */
356  pwrdm_set_next_pwrst(pm_info->pwrdm, PWRDM_POWER_ON);
357 
358  pm_info = &per_cpu(omap4_pm_info, 0x1);
359  pm_info->scu_sar_addr = sar_base + SCU_OFFSET1;
360  pm_info->wkup_sar_addr = sar_base + CPU1_WAKEUP_NS_PA_ADDR_OFFSET;
361  pm_info->l2x0_sar_addr = sar_base + L2X0_SAVE_OFFSET1;
362  pm_info->pwrdm = pwrdm_lookup("cpu1_pwrdm");
363  if (!pm_info->pwrdm) {
364  pr_err("Lookup failed for CPU1 pwrdm\n");
365  return -ENODEV;
366  }
367 
368  /* Clear CPU previous power domain state */
369  pwrdm_clear_all_prev_pwrst(pm_info->pwrdm);
370  cpu_clear_prev_logic_pwrst(1);
371 
372  /* Initialise CPU1 power domain state to ON */
373  pwrdm_set_next_pwrst(pm_info->pwrdm, PWRDM_POWER_ON);
374 
375  mpuss_pd = pwrdm_lookup("mpu_pwrdm");
376  if (!mpuss_pd) {
377  pr_err("Failed to lookup MPUSS power domain\n");
378  return -ENODEV;
379  }
380  pwrdm_clear_all_prev_pwrst(mpuss_pd);
381  mpuss_clear_prev_logic_pwrst();
382 
383  /* Save device type on scratchpad for low level code to use */
385  __raw_writel(1, sar_base + OMAP_TYPE_OFFSET);
386  else
387  __raw_writel(0, sar_base + OMAP_TYPE_OFFSET);
388 
389  save_l2x0_context();
390 
391  return 0;
392 }
393 
394 #endif