summaryrefslogtreecommitdiff
path: root/arch/arm/mach-omap2/pm-domain.c
blob: 2494091a078b1b332f2d51628532016ee3b8b41e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
/*
 * linux/arch/arm/mach-omap2/pm-domain.c
 *
 * Power domain functions for OMAP2
 *
 * Copyright (C) 2006 Nokia Corporation
 * Tony Lindgren <tony@atomide.com>
 *
 * Some code based on earlier OMAP2 sample PM code
 * Copyright (C) 2005 Texas Instruments, Inc.
 * Richard Woodruff <r-woodruff2@ti.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/clk.h>

#include <asm/io.h>

#include "prcm-regs.h"

/* Power domain offsets */
#define PM_MPU_OFFSET			0x100
#define PM_CORE_OFFSET			0x200
#define PM_GFX_OFFSET			0x300
#define PM_WKUP_OFFSET			0x400		/* Autoidle only */
#define PM_PLL_OFFSET			0x500		/* Autoidle only */
#define PM_DSP_OFFSET			0x800
#define PM_MDM_OFFSET			0xc00

/* Power domain wake-up dependency control register */
#define PM_WKDEP_OFFSET			0xc8
#define		EN_MDM			(1 << 5)
#define		EN_WKUP			(1 << 4)
#define		EN_GFX			(1 << 3)
#define		EN_DSP			(1 << 2)
#define		EN_MPU			(1 << 1)
#define		EN_CORE			(1 << 0)

/* Core power domain state transition control register */
#define PM_PWSTCTRL_OFFSET		0xe0
#define		FORCESTATE		(1 << 18)	/* Only for DSP & GFX */
#define		MEM4RETSTATE		(1 << 6)
#define		MEM3RETSTATE		(1 << 5)
#define		MEM2RETSTATE		(1 << 4)
#define		MEM1RETSTATE		(1 << 3)
#define		LOGICRETSTATE		(1 << 2)	/* Logic is retained */
#define		POWERSTATE_OFF		0x3
#define		POWERSTATE_RETENTION	0x1
#define		POWERSTATE_ON		0x0

/* Power domain state register */
#define PM_PWSTST_OFFSET		0xe4

/* Hardware supervised state transition control register */
#define CM_CLKSTCTRL_OFFSET		0x48
#define		AUTOSTAT_MPU		(1 << 0)	/* MPU */
#define		AUTOSTAT_DSS		(1 << 2)	/* Core */
#define		AUTOSTAT_L4		(1 << 1)	/* Core */
#define		AUTOSTAT_L3		(1 << 0)	/* Core */
#define		AUTOSTAT_GFX		(1 << 0)	/* GFX */
#define		AUTOSTAT_IVA		(1 << 8)	/* 2420 IVA in DSP domain */
#define		AUTOSTAT_DSP		(1 << 0)	/* DSP */
#define		AUTOSTAT_MDM		(1 << 0)	/* MDM */

/* Automatic control of interface clock idling */
#define CM_AUTOIDLE1_OFFSET		0x30
#define CM_AUTOIDLE2_OFFSET		0x34		/* Core only */
#define CM_AUTOIDLE3_OFFSET		0x38		/* Core only */
#define CM_AUTOIDLE4_OFFSET		0x3c		/* Core only */
#define		AUTO_54M(x)		(((x) & 0x3) << 6)
#define		AUTO_96M(x)		(((x) & 0x3) << 2)
#define		AUTO_DPLL(x)		(((x) & 0x3) << 0)
#define		AUTO_STOPPED		0x3
#define		AUTO_BYPASS_FAST	0x2		/* DPLL only */
#define		AUTO_BYPASS_LOW_POWER	0x1		/* DPLL only */
#define		AUTO_DISABLED		0x0

/* Voltage control PRCM_VOLTCTRL bits */
#define		AUTO_EXTVOLT		(1 << 15)
#define		FORCE_EXTVOLT		(1 << 14)
#define		SETOFF_LEVEL(x)		(((x) & 0x3) << 12)
#define		MEMRETCTRL		(1 << 8)
#define		SETRET_LEVEL(x)		(((x) & 0x3) << 6)
#define		VOLT_LEVEL(x)		(((x) & 0x3) << 0)

#define OMAP24XX_PRCM_VBASE	IO_ADDRESS(OMAP24XX_PRCM_BASE)
#define prcm_readl(r)		__raw_readl(OMAP24XX_PRCM_VBASE + (r))
#define prcm_writel(v, r)	__raw_writel((v), OMAP24XX_PRCM_VBASE + (r))

static u32 pmdomain_get_wakeup_dependencies(int domain_offset)
{
	return prcm_readl(domain_offset + PM_WKDEP_OFFSET);
}

static void pmdomain_set_wakeup_dependencies(u32 state, int domain_offset)
{
	prcm_writel(state, domain_offset + PM_WKDEP_OFFSET);
}

static u32 pmdomain_get_powerstate(int domain_offset)
{
	return prcm_readl(domain_offset + PM_PWSTCTRL_OFFSET);
}

static void pmdomain_set_powerstate(u32 state, int domain_offset)
{
	prcm_writel(state, domain_offset + PM_PWSTCTRL_OFFSET);
}

static u32 pmdomain_get_clock_autocontrol(int domain_offset)
{
	return prcm_readl(domain_offset + CM_CLKSTCTRL_OFFSET);
}

static void pmdomain_set_clock_autocontrol(u32 state, int domain_offset)
{
	prcm_writel(state, domain_offset + CM_CLKSTCTRL_OFFSET);
}

static u32 pmdomain_get_clock_autoidle1(int domain_offset)
{
	return prcm_readl(domain_offset + CM_AUTOIDLE1_OFFSET);
}

/* Core domain only */
static u32 pmdomain_get_clock_autoidle2(int domain_offset)
{
	return prcm_readl(domain_offset + CM_AUTOIDLE2_OFFSET);
}

/* Core domain only */
static u32 pmdomain_get_clock_autoidle3(int domain_offset)
{
	return prcm_readl(domain_offset + CM_AUTOIDLE3_OFFSET);
}

/* Core domain only */
static u32 pmdomain_get_clock_autoidle4(int domain_offset)
{
	return prcm_readl(domain_offset + CM_AUTOIDLE4_OFFSET);
}

static void pmdomain_set_clock_autoidle1(u32 state, int domain_offset)
{
	prcm_writel(state, CM_AUTOIDLE1_OFFSET + domain_offset);
}

/* Core domain only */
static void pmdomain_set_clock_autoidle2(u32 state, int domain_offset)
{
	prcm_writel(state, CM_AUTOIDLE2_OFFSET + domain_offset);
}

/* Core domain only */
static void pmdomain_set_clock_autoidle3(u32 state, int domain_offset)
{
	prcm_writel(state, CM_AUTOIDLE3_OFFSET + domain_offset);
}

/* Core domain only */
static void pmdomain_set_clock_autoidle4(u32 state, int domain_offset)
{
	prcm_writel(state, CM_AUTOIDLE4_OFFSET + domain_offset);
}

/*
 * Configures power management domains to idle clocks automatically.
 */
void pmdomain_set_autoidle(void)
{
	u32 val;

	/* Set PLL auto stop for 54M, 96M & DPLL */
	pmdomain_set_clock_autoidle1(AUTO_54M(AUTO_STOPPED) |
				     AUTO_96M(AUTO_STOPPED) |
				     AUTO_DPLL(AUTO_STOPPED), PM_PLL_OFFSET);

	/* External clock input control
	 * REVISIT: Should this be in clock framework?
	 */
	PRCM_CLKSRC_CTRL |= (0x3 << 3);

	/* Configure number of 32KHz clock cycles for sys_clk */
	PRCM_CLKSSETUP = 0x00ff;

	/* Configure automatic voltage transition */
	PRCM_VOLTSETUP = 0;
	val = PRCM_VOLTCTRL;
	val &= ~(SETOFF_LEVEL(0x3) | VOLT_LEVEL(0x3));
	val |= SETOFF_LEVEL(1) | VOLT_LEVEL(1) | AUTO_EXTVOLT;
	PRCM_VOLTCTRL = val;

	/* Disable emulation tools functional clock */
	PRCM_CLKEMUL_CTRL = 0x0;

	/* Set core memory retention state */
	val = pmdomain_get_powerstate(PM_CORE_OFFSET);
	if (cpu_is_omap2420()) {
		val &= ~(0x7 << 3);
		val |= (MEM3RETSTATE | MEM2RETSTATE | MEM1RETSTATE);
	} else {
		val &= ~(0xf << 3);
		val |= (MEM4RETSTATE | MEM3RETSTATE | MEM2RETSTATE |
			MEM1RETSTATE);
	}
	pmdomain_set_powerstate(val, PM_CORE_OFFSET);

	/* OCP interface smart idle. REVISIT: Enable autoidle bit0 ? */
	val = SMS_SYSCONFIG;
	val &= ~(0x3 << 3);
	val |= (0x2 << 3) | (1 << 0);
	SMS_SYSCONFIG |= val;

	val = SDRC_SYSCONFIG;
	val &= ~(0x3 << 3);
	val |= (0x2 << 3);
	SDRC_SYSCONFIG = val;

	/* Configure L3 interface for smart idle.
	 * REVISIT: Enable autoidle bit0 ?
	 */
	val = GPMC_SYSCONFIG;
	val &= ~(0x3 << 3);
	val |= (0x2 << 3) | (1 << 0);
	GPMC_SYSCONFIG = val;

	pmdomain_set_powerstate(LOGICRETSTATE | POWERSTATE_RETENTION,
				PM_MPU_OFFSET);
	pmdomain_set_powerstate(POWERSTATE_RETENTION, PM_CORE_OFFSET);
	if (!cpu_is_omap2420())
		pmdomain_set_powerstate(POWERSTATE_RETENTION, PM_MDM_OFFSET);

	/* Assume suspend function has saved the state for DSP and GFX */
	pmdomain_set_powerstate(FORCESTATE | POWERSTATE_OFF, PM_DSP_OFFSET);
	pmdomain_set_powerstate(FORCESTATE | POWERSTATE_OFF, PM_GFX_OFFSET);

#if 0
	/* REVISIT: Internal USB needs special handling */
	force_standby_usb();
	if (cpu_is_omap2430())
		force_hsmmc();
	sdram_self_refresh_on_idle_req(1);
#endif

	/* Enable clock auto control for all domains.
	 * Note that CORE domain includes also DSS, L4 & L3.
	 */
	pmdomain_set_clock_autocontrol(AUTOSTAT_MPU, PM_MPU_OFFSET);
	pmdomain_set_clock_autocontrol(AUTOSTAT_GFX, PM_GFX_OFFSET);
	pmdomain_set_clock_autocontrol(AUTOSTAT_DSS | AUTOSTAT_L4 | AUTOSTAT_L3,
				       PM_CORE_OFFSET);
	if (cpu_is_omap2420())
		pmdomain_set_clock_autocontrol(AUTOSTAT_IVA | AUTOSTAT_DSP,
					       PM_DSP_OFFSET);
	else {
		pmdomain_set_clock_autocontrol(AUTOSTAT_DSP, PM_DSP_OFFSET);
		pmdomain_set_clock_autocontrol(AUTOSTAT_MDM, PM_MDM_OFFSET);
	}

	/* Enable clock autoidle for all domains */
	pmdomain_set_clock_autoidle1(0x2, PM_DSP_OFFSET);
	if (cpu_is_omap2420()) {
		pmdomain_set_clock_autoidle1(0xfffffff9, PM_CORE_OFFSET);
		pmdomain_set_clock_autoidle2(0x7, PM_CORE_OFFSET);
		pmdomain_set_clock_autoidle1(0x3f, PM_WKUP_OFFSET);
	} else {
		pmdomain_set_clock_autoidle1(0xeafffff1, PM_CORE_OFFSET);
		pmdomain_set_clock_autoidle2(0xfff, PM_CORE_OFFSET);
		pmdomain_set_clock_autoidle1(0x7f, PM_WKUP_OFFSET);
		pmdomain_set_clock_autoidle1(0x3, PM_MDM_OFFSET);
	}
	pmdomain_set_clock_autoidle3(0x7, PM_CORE_OFFSET);
	pmdomain_set_clock_autoidle4(0x1f, PM_CORE_OFFSET);
}

/*
 * Initializes power domains by removing wake-up dependencies and powering
 * down DSP and GFX. Gets called from PM init. Note that DSP and IVA code
 * must re-enable DSP and GFX when used.
 */
void __init pmdomain_init(void)
{
	/* Remove all domain wakeup dependencies */
	pmdomain_set_wakeup_dependencies(EN_WKUP | EN_CORE, PM_MPU_OFFSET);
	pmdomain_set_wakeup_dependencies(0, PM_DSP_OFFSET);
	pmdomain_set_wakeup_dependencies(0, PM_GFX_OFFSET);
	pmdomain_set_wakeup_dependencies(EN_WKUP | EN_MPU, PM_CORE_OFFSET);
	if (cpu_is_omap2430())
		pmdomain_set_wakeup_dependencies(0, PM_MDM_OFFSET);

	/* Power down DSP and GFX */
	pmdomain_set_powerstate(POWERSTATE_OFF | FORCESTATE, PM_DSP_OFFSET);
	pmdomain_set_powerstate(POWERSTATE_OFF | FORCESTATE, PM_GFX_OFFSET);
}