blob: de15c2a1f791cf2eed470f65c5213eee8b3114eb [file] [log] [blame]
Zeng Zhaominga9ce0be2011-06-28 09:15:47 +08001/*
2 * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19#include <linux/kernel.h>
20#include <linux/clk.h>
21#include <linux/platform_device.h>
22#include <linux/regulator/consumer.h>
23#include <linux/pmic_external.h>
24#include <asm/io.h>
25#include <mach/hardware.h>
26#include <mach/clock.h>
27#include <asm/proc-fns.h>
28#include <asm/system.h>
Anson Huangcda08922011-07-18 12:54:13 +080029#include "crm_regs.h"
30
Anson Huangb1e93f52011-10-21 10:48:56 +080031#define SCU_CTRL 0x00
32#define SCU_CONFIG 0x04
33#define SCU_CPU_STATUS 0x08
34#define SCU_INVALIDATE 0x0c
35#define SCU_FPGA_REVISION 0x10
36#define GPC_CNTR_OFFSET 0x0
37#define GPC_PGC_GPU_PGCR_OFFSET 0x260
38#define GPC_PGC_CPU_PDN_OFFSET 0x2a0
Anson Huang6724c142011-08-02 17:24:15 +080039#define GPC_PGC_CPU_PUPSCR_OFFSET 0x2a4
40#define GPC_PGC_CPU_PDNSCR_OFFSET 0x2a8
Anson Huangb1e93f52011-10-21 10:48:56 +080041#define ANATOP_REG_2P5_OFFSET 0x130
42#define ANATOP_REG_CORE_OFFSET 0x140
Anson Huangcda08922011-07-18 12:54:13 +080043
Huang Shijie9106af62011-07-21 17:24:29 +080044#define MODULE_CLKGATE (1 << 30)
45#define MODULE_SFTRST (1 << 31)
Anson Huangb1e93f52011-10-21 10:48:56 +080046/* static DEFINE_SPINLOCK(wfi_lock); */
Huang Shijie9106af62011-07-21 17:24:29 +080047
Anson Huang81b59382011-07-20 14:45:21 +080048extern unsigned int gpc_wake_irq[4];
49
Anson Huangb1e93f52011-10-21 10:48:56 +080050/* static unsigned int cpu_idle_mask; */
Ranjani Vaidyanathan70b540a2011-09-01 10:04:06 -050051
52static void __iomem *gpc_base = IO_ADDRESS(GPC_BASE_ADDR);
53
54extern void (*mx6_wait_in_iram)(void *ccm_base);
55extern void mx6_wait(void);
56extern void *mx6_wait_in_iram_base;
Ranjani Vaidyanathan2ad978b2011-11-04 16:19:19 -050057extern bool enable_wait_mode;
Ranjani Vaidyanathan70b540a2011-09-01 10:04:06 -050058
Anson Huang81b59382011-07-20 14:45:21 +080059void gpc_set_wakeup(unsigned int irq[4])
Anson Huangcda08922011-07-18 12:54:13 +080060{
Anson Huang81b59382011-07-20 14:45:21 +080061 /* Mask all wake up source */
62 __raw_writel(~irq[0], gpc_base + 0x8);
63 __raw_writel(~irq[1], gpc_base + 0xc);
64 __raw_writel(~irq[2], gpc_base + 0x10);
65 __raw_writel(~irq[3], gpc_base + 0x14);
Ranjani Vaidyanathan70b540a2011-09-01 10:04:06 -050066
Anson Huang81b59382011-07-20 14:45:21 +080067 return;
Anson Huangcda08922011-07-18 12:54:13 +080068}
69/* set cpu low power mode before WFI instruction */
70void mxc_cpu_lp_set(enum mxc_cpu_pwr_mode mode)
71{
Anson Huangb1e93f52011-10-21 10:48:56 +080072
Anson Huangcda08922011-07-18 12:54:13 +080073 int stop_mode = 0;
Anson Huangb1e93f52011-10-21 10:48:56 +080074 void __iomem *anatop_base = IO_ADDRESS(ANATOP_BASE_ADDR);
75 u32 ccm_clpcr, anatop_val;
Anson Huangcda08922011-07-18 12:54:13 +080076
Anson Huangcda08922011-07-18 12:54:13 +080077 ccm_clpcr = __raw_readl(MXC_CCM_CLPCR) & ~(MXC_CCM_CLPCR_LPM_MASK);
78
79 switch (mode) {
80 case WAIT_CLOCKED:
81 break;
82 case WAIT_UNCLOCKED:
83 ccm_clpcr |= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET;
84 break;
85 case WAIT_UNCLOCKED_POWER_OFF:
86 case STOP_POWER_OFF:
Anson Huang6724c142011-08-02 17:24:15 +080087 case ARM_POWER_OFF:
Anson Huangcda08922011-07-18 12:54:13 +080088 if (mode == WAIT_UNCLOCKED_POWER_OFF) {
Anson Huangcda08922011-07-18 12:54:13 +080089 ccm_clpcr &= ~MXC_CCM_CLPCR_VSTBY;
90 ccm_clpcr &= ~MXC_CCM_CLPCR_SBYOS;
Ranjani Vaidyanathan70b540a2011-09-01 10:04:06 -050091 ccm_clpcr |= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET;
92 ccm_clpcr |= MXC_CCM_CLPCR_BYP_MMDC_CH1_LPM_HS;
Anson Huangcda08922011-07-18 12:54:13 +080093 stop_mode = 0;
Anson Huang6724c142011-08-02 17:24:15 +080094 } else if (mode == STOP_POWER_OFF) {
Anson Huangcda08922011-07-18 12:54:13 +080095 ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET;
96 ccm_clpcr |= 0x3 << MXC_CCM_CLPCR_STBY_COUNT_OFFSET;
97 ccm_clpcr |= MXC_CCM_CLPCR_VSTBY;
98 ccm_clpcr |= MXC_CCM_CLPCR_SBYOS;
99 ccm_clpcr |= MXC_CCM_CLPCR_BYP_MMDC_CH1_LPM_HS;
100 stop_mode = 1;
Anson Huang6724c142011-08-02 17:24:15 +0800101 } else {
102 ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET;
103 ccm_clpcr |= 0x3 << MXC_CCM_CLPCR_STBY_COUNT_OFFSET;
104 ccm_clpcr |= MXC_CCM_CLPCR_VSTBY;
105 ccm_clpcr |= MXC_CCM_CLPCR_SBYOS;
106 ccm_clpcr |= MXC_CCM_CLPCR_BYP_MMDC_CH1_LPM_HS;
107 stop_mode = 2;
Anson Huangcda08922011-07-18 12:54:13 +0800108 }
Anson Huangcda08922011-07-18 12:54:13 +0800109 break;
110 case STOP_POWER_ON:
111 ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET;
Ranjani Vaidyanathan70b540a2011-09-01 10:04:06 -0500112
Anson Huangcda08922011-07-18 12:54:13 +0800113 break;
114 default:
115 printk(KERN_WARNING "UNKNOWN cpu power mode: %d\n", mode);
116 return;
117 }
118
Anson Huang6724c142011-08-02 17:24:15 +0800119 if (stop_mode > 0) {
Anson Huang81b59382011-07-20 14:45:21 +0800120 gpc_set_wakeup(gpc_wake_irq);
Anson Huangcda08922011-07-18 12:54:13 +0800121 /* Power down and power up sequence */
Anson Huang6724c142011-08-02 17:24:15 +0800122 __raw_writel(0xFFFFFFFF, gpc_base + GPC_PGC_CPU_PUPSCR_OFFSET);
123 __raw_writel(0xFFFFFFFF, gpc_base + GPC_PGC_CPU_PDNSCR_OFFSET);
124
125 /* dormant mode, need to power off the arm core */
Anson Huangb1e93f52011-10-21 10:48:56 +0800126 if (stop_mode == 2) {
Anson Huangd5d3b692011-11-19 10:24:21 +0800127 __raw_writel(0x1, gpc_base + GPC_PGC_CPU_PDN_OFFSET);
Anson Huangb1e93f52011-10-21 10:48:56 +0800128 __raw_writel(0x1, gpc_base + GPC_PGC_GPU_PGCR_OFFSET);
129 __raw_writel(0x1, gpc_base + GPC_CNTR_OFFSET);
130 /* Enable weak 2P5 linear regulator */
131 anatop_val = __raw_readl(anatop_base + ANATOP_REG_2P5_OFFSET);
132 anatop_val |= 1 << 18;
133 __raw_writel(anatop_val, anatop_base + ANATOP_REG_2P5_OFFSET);
Anson Huangd5d3b692011-11-19 10:24:21 +0800134 /* Make sure ARM and SOC domain has same voltage and PU domain off */
Anson Huangb1e93f52011-10-21 10:48:56 +0800135 anatop_val = __raw_readl(anatop_base + ANATOP_REG_CORE_OFFSET);
Anson Huangd5d3b692011-11-19 10:24:21 +0800136 anatop_val &= 0xff83001f;
137 anatop_val |= (anatop_val & 0x1f) << 18;
Anson Huangb1e93f52011-10-21 10:48:56 +0800138 __raw_writel(anatop_val, anatop_base + ANATOP_REG_CORE_OFFSET);
139 __raw_writel(__raw_readl(MXC_CCM_CCR) | MXC_CCM_CCR_RBC_EN, MXC_CCM_CCR);
140 ccm_clpcr |= MXC_CCM_CLPCR_WB_PER_AT_LPM;
141 }
Anson Huangcda08922011-07-18 12:54:13 +0800142 }
Anson Huangcda08922011-07-18 12:54:13 +0800143 __raw_writel(ccm_clpcr, MXC_CCM_CLPCR);
144}
Zeng Zhaominga9ce0be2011-06-28 09:15:47 +0800145
146void arch_idle(void)
147{
Ranjani Vaidyanathan2ad978b2011-11-04 16:19:19 -0500148 if (enable_wait_mode) {
149 if ((num_online_cpus() == num_present_cpus())
150 && mx6_wait_in_iram != NULL) {
151 mxc_cpu_lp_set(WAIT_UNCLOCKED_POWER_OFF);
152 mx6_wait_in_iram(MXC_CCM_BASE);
153 }
Ranjani Vaidyanathan70b540a2011-09-01 10:04:06 -0500154 } else
155 cpu_do_idle();
Zeng Zhaominga9ce0be2011-06-28 09:15:47 +0800156}
Huang Shijie9106af62011-07-21 17:24:29 +0800157
158static int __mxs_reset_block(void __iomem *hwreg, int just_enable)
159{
160 u32 c;
161 int timeout;
162
163 /* the process of software reset of IP block is done
164 in several steps:
165
166 - clear SFTRST and wait for block is enabled;
167 - clear clock gating (CLKGATE bit);
168 - set the SFTRST again and wait for block is in reset;
169 - clear SFTRST and wait for reset completion.
170 */
171 c = __raw_readl(hwreg);
172 c &= ~MODULE_SFTRST; /* clear SFTRST */
173 __raw_writel(c, hwreg);
174 for (timeout = 1000000; timeout > 0; timeout--)
175 /* still in SFTRST state ? */
176 if ((__raw_readl(hwreg) & MODULE_SFTRST) == 0)
177 break;
178 if (timeout <= 0) {
179 printk(KERN_ERR "%s(%p): timeout when enabling\n",
180 __func__, hwreg);
181 return -ETIME;
182 }
183
184 c = __raw_readl(hwreg);
185 c &= ~MODULE_CLKGATE; /* clear CLKGATE */
186 __raw_writel(c, hwreg);
187
188 if (!just_enable) {
189 c = __raw_readl(hwreg);
190 c |= MODULE_SFTRST; /* now again set SFTRST */
191 __raw_writel(c, hwreg);
192 for (timeout = 1000000; timeout > 0; timeout--)
193 /* poll until CLKGATE set */
194 if (__raw_readl(hwreg) & MODULE_CLKGATE)
195 break;
196 if (timeout <= 0) {
197 printk(KERN_ERR "%s(%p): timeout when resetting\n",
198 __func__, hwreg);
199 return -ETIME;
200 }
201
202 c = __raw_readl(hwreg);
203 c &= ~MODULE_SFTRST; /* clear SFTRST */
204 __raw_writel(c, hwreg);
205 for (timeout = 1000000; timeout > 0; timeout--)
206 /* still in SFTRST state ? */
207 if ((__raw_readl(hwreg) & MODULE_SFTRST) == 0)
208 break;
209 if (timeout <= 0) {
210 printk(KERN_ERR "%s(%p): timeout when enabling "
211 "after reset\n", __func__, hwreg);
212 return -ETIME;
213 }
214
215 c = __raw_readl(hwreg);
216 c &= ~MODULE_CLKGATE; /* clear CLKGATE */
217 __raw_writel(c, hwreg);
218 }
219 for (timeout = 1000000; timeout > 0; timeout--)
220 /* still in SFTRST state ? */
221 if ((__raw_readl(hwreg) & MODULE_CLKGATE) == 0)
222 break;
223
224 if (timeout <= 0) {
225 printk(KERN_ERR "%s(%p): timeout when unclockgating\n",
226 __func__, hwreg);
227 return -ETIME;
228 }
229
230 return 0;
231}
232
233int mxs_reset_block(void __iomem *hwreg, int just_enable)
234{
235 int try = 10;
236 int r;
237
238 while (try--) {
239 r = __mxs_reset_block(hwreg, just_enable);
240 if (!r)
241 break;
242 pr_debug("%s: try %d failed\n", __func__, 10 - try);
243 }
244 return r;
245}