blob: fb7d90d36672dea2ec6435e7231c947ec291a340 [file] [log] [blame]
Shawn Guoa1f1c7e2011-09-06 15:08:40 +08001/*
Anson Huange95dddb2013-03-20 19:39:42 -04002 * Copyright 2011-2013 Freescale Semiconductor, Inc.
Shawn Guoa1f1c7e2011-09-06 15:08:40 +08003 * Copyright 2011 Linaro Ltd.
4 *
5 * The code contained herein is licensed under the GNU General Public
6 * License. You may obtain a copy of the GNU General Public License
7 * Version 2 or later at the following locations:
8 *
9 * http://www.opensource.org/licenses/gpl-license.html
10 * http://www.gnu.org/copyleft/gpl.html
11 */
12
Shawn Guo9e8147b2013-09-25 23:09:36 +080013#include <linux/delay.h>
Shawn Guoa1f1c7e2011-09-06 15:08:40 +080014#include <linux/init.h>
15#include <linux/io.h>
16#include <linux/of.h>
Shawn Guo9e8147b2013-09-25 23:09:36 +080017#include <linux/of_address.h>
Shawn Guoa1f1c7e2011-09-06 15:08:40 +080018#include <linux/suspend.h>
19#include <asm/cacheflush.h>
20#include <asm/proc-fns.h>
21#include <asm/suspend.h>
22#include <asm/hardware/cache-l2x0.h>
Shawn Guoa1f1c7e2011-09-06 15:08:40 +080023
Shawn Guoe3372472012-09-13 21:01:00 +080024#include "common.h"
Shawn Guo50f2de62012-09-14 14:14:45 +080025#include "hardware.h"
Shawn Guoe3372472012-09-13 21:01:00 +080026
Shawn Guo9e8147b2013-09-25 23:09:36 +080027#define CCR 0x0
28#define BM_CCR_WB_COUNT (0x7 << 16)
29#define BM_CCR_RBC_BYPASS_COUNT (0x3f << 21)
30#define BM_CCR_RBC_EN (0x1 << 27)
31
32#define CLPCR 0x54
33#define BP_CLPCR_LPM 0
34#define BM_CLPCR_LPM (0x3 << 0)
35#define BM_CLPCR_BYPASS_PMIC_READY (0x1 << 2)
36#define BM_CLPCR_ARM_CLK_DIS_ON_LPM (0x1 << 5)
37#define BM_CLPCR_SBYOS (0x1 << 6)
38#define BM_CLPCR_DIS_REF_OSC (0x1 << 7)
39#define BM_CLPCR_VSTBY (0x1 << 8)
40#define BP_CLPCR_STBY_COUNT 9
41#define BM_CLPCR_STBY_COUNT (0x3 << 9)
42#define BM_CLPCR_COSC_PWRDOWN (0x1 << 11)
43#define BM_CLPCR_WB_PER_AT_LPM (0x1 << 16)
44#define BM_CLPCR_WB_CORE_AT_LPM (0x1 << 17)
45#define BM_CLPCR_BYP_MMDC_CH0_LPM_HS (0x1 << 19)
46#define BM_CLPCR_BYP_MMDC_CH1_LPM_HS (0x1 << 21)
47#define BM_CLPCR_MASK_CORE0_WFI (0x1 << 22)
48#define BM_CLPCR_MASK_CORE1_WFI (0x1 << 23)
49#define BM_CLPCR_MASK_CORE2_WFI (0x1 << 24)
50#define BM_CLPCR_MASK_CORE3_WFI (0x1 << 25)
51#define BM_CLPCR_MASK_SCU_IDLE (0x1 << 26)
52#define BM_CLPCR_MASK_L2CC_IDLE (0x1 << 27)
53
54#define CGPR 0x64
55#define BM_CGPR_CHICKEN_BIT (0x1 << 17)
56
57static void __iomem *ccm_base;
58
59void imx6q_set_chicken_bit(void)
60{
61 u32 val = readl_relaxed(ccm_base + CGPR);
62
63 val |= BM_CGPR_CHICKEN_BIT;
64 writel_relaxed(val, ccm_base + CGPR);
65}
66
67static void imx6q_enable_rbc(bool enable)
68{
69 u32 val;
Shawn Guo9e8147b2013-09-25 23:09:36 +080070
Shawn Guo9e8147b2013-09-25 23:09:36 +080071 /*
72 * need to mask all interrupts in GPC before
73 * operating RBC configurations
74 */
75 imx_gpc_mask_all();
76
77 /* configure RBC enable bit */
78 val = readl_relaxed(ccm_base + CCR);
79 val &= ~BM_CCR_RBC_EN;
80 val |= enable ? BM_CCR_RBC_EN : 0;
81 writel_relaxed(val, ccm_base + CCR);
82
83 /* configure RBC count */
84 val = readl_relaxed(ccm_base + CCR);
85 val &= ~BM_CCR_RBC_BYPASS_COUNT;
86 val |= enable ? BM_CCR_RBC_BYPASS_COUNT : 0;
87 writel(val, ccm_base + CCR);
88
89 /*
90 * need to delay at least 2 cycles of CKIL(32K)
91 * due to hardware design requirement, which is
92 * ~61us, here we use 65us for safe
93 */
94 udelay(65);
95
96 /* restore GPC interrupt mask settings */
97 imx_gpc_restore_all();
Shawn Guo9e8147b2013-09-25 23:09:36 +080098}
99
100static void imx6q_enable_wb(bool enable)
101{
102 u32 val;
Shawn Guo9e8147b2013-09-25 23:09:36 +0800103
104 /* configure well bias enable bit */
105 val = readl_relaxed(ccm_base + CLPCR);
106 val &= ~BM_CLPCR_WB_PER_AT_LPM;
107 val |= enable ? BM_CLPCR_WB_PER_AT_LPM : 0;
108 writel_relaxed(val, ccm_base + CLPCR);
109
110 /* configure well bias count */
111 val = readl_relaxed(ccm_base + CCR);
112 val &= ~BM_CCR_WB_COUNT;
113 val |= enable ? BM_CCR_WB_COUNT : 0;
114 writel_relaxed(val, ccm_base + CCR);
Shawn Guo9e8147b2013-09-25 23:09:36 +0800115}
116
117int imx6q_set_lpm(enum mxc_cpu_pwr_mode mode)
118{
119 u32 val = readl_relaxed(ccm_base + CLPCR);
120
121 val &= ~BM_CLPCR_LPM;
122 switch (mode) {
123 case WAIT_CLOCKED:
Shawn Guo9e8147b2013-09-25 23:09:36 +0800124 break;
125 case WAIT_UNCLOCKED:
126 val |= 0x1 << BP_CLPCR_LPM;
127 val |= BM_CLPCR_ARM_CLK_DIS_ON_LPM;
128 break;
129 case STOP_POWER_ON:
130 val |= 0x2 << BP_CLPCR_LPM;
131 break;
132 case WAIT_UNCLOCKED_POWER_OFF:
133 val |= 0x1 << BP_CLPCR_LPM;
134 val &= ~BM_CLPCR_VSTBY;
135 val &= ~BM_CLPCR_SBYOS;
136 break;
137 case STOP_POWER_OFF:
138 val |= 0x2 << BP_CLPCR_LPM;
139 val |= 0x3 << BP_CLPCR_STBY_COUNT;
140 val |= BM_CLPCR_VSTBY;
141 val |= BM_CLPCR_SBYOS;
Shawn Guo9e8147b2013-09-25 23:09:36 +0800142 break;
143 default:
144 return -EINVAL;
145 }
146
147 writel_relaxed(val, ccm_base + CLPCR);
148
149 return 0;
150}
151
Shawn Guoa1f1c7e2011-09-06 15:08:40 +0800152static int imx6q_suspend_finish(unsigned long val)
153{
154 cpu_do_idle();
155 return 0;
156}
157
158static int imx6q_pm_enter(suspend_state_t state)
159{
160 switch (state) {
161 case PM_SUSPEND_MEM:
162 imx6q_set_lpm(STOP_POWER_OFF);
Shawn Guo1d674a72013-10-09 20:31:28 +0800163 imx6q_enable_wb(true);
164 imx6q_enable_rbc(true);
Shawn Guoa1f1c7e2011-09-06 15:08:40 +0800165 imx_gpc_pre_suspend();
Anson Huange95dddb2013-03-20 19:39:42 -0400166 imx_anatop_pre_suspend();
Shawn Guoa1f1c7e2011-09-06 15:08:40 +0800167 imx_set_cpu_jump(0, v7_cpu_resume);
168 /* Zzz ... */
169 cpu_suspend(0, imx6q_suspend_finish);
170 imx_smp_prepare();
Anson Huange95dddb2013-03-20 19:39:42 -0400171 imx_anatop_post_resume();
Shawn Guoa1f1c7e2011-09-06 15:08:40 +0800172 imx_gpc_post_resume();
Shawn Guo1d674a72013-10-09 20:31:28 +0800173 imx6q_enable_rbc(false);
174 imx6q_enable_wb(false);
Shawn Guo83ae20982013-01-14 21:11:10 +0800175 imx6q_set_lpm(WAIT_CLOCKED);
Shawn Guoa1f1c7e2011-09-06 15:08:40 +0800176 break;
177 default:
178 return -EINVAL;
179 }
180
181 return 0;
182}
183
184static const struct platform_suspend_ops imx6q_pm_ops = {
185 .enter = imx6q_pm_enter,
186 .valid = suspend_valid_only_mem,
187};
188
Shawn Guo9e8147b2013-09-25 23:09:36 +0800189void __init imx6q_pm_set_ccm_base(void __iomem *base)
190{
191 ccm_base = base;
192}
193
Shawn Guoa1f1c7e2011-09-06 15:08:40 +0800194void __init imx6q_pm_init(void)
195{
Shawn Guo9e8147b2013-09-25 23:09:36 +0800196 WARN_ON(!ccm_base);
197
198 /* Set initial power mode */
199 imx6q_set_lpm(WAIT_CLOCKED);
200
Shawn Guoa1f1c7e2011-09-06 15:08:40 +0800201 suspend_set_ops(&imx6q_pm_ops);
202}