summaryrefslogtreecommitdiff
path: root/arch/arm/mach-omap2/omap4-wakeupgen.c
blob: 91f596e5bc3b86cc985bab73ba38bd21f33a1b42 (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
/*
 * OMAP4 Wakeupgen Source file
 *
 * The WakeUpGen unit is responsible for generating wakeup event
 * from the incoming interrupts and enable bits. The WakeUpGen
 * is implemented in MPU Always-On power domain. Only SPI
 * interrupts are wakeup capabale
 *
 * Copyright (C) 2010 Texas Instruments, Inc.
 * Written by Santosh Shilimkar <santosh.shilimkar@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/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/platform_device.h>

#include <mach/omap4-wakeupgen.h>
#include <mach/omap4-common.h>

/*
 * WakeUpGen save restore offset from SAR_BANK3
 */
#define WAKEUPGENENB_OFFSET_CPU0		0x684
#define WAKEUPGENENB_SECURE_OFFSET_CPU0		0x694
#define WAKEUPGENENB_OFFSET_CPU1		0x6A4
#define WAKEUPGENENB_SECURE_OFFSET_CPU1		0x6B4
#define AUXCOREBOOT0_OFFSET			0x6C4
#define AUXCOREBOOT1_OFFSET			0x6C8
#define PTMSYNCREQ_MASK_OFFSET			0x6CC
#define PTMSYNCREQ_EN_OFFSET			0x6D0
#define SAR_BACKUP_STATUS_OFFSET		0x500
#define SAR_BACKUP_STATUS_WAKEUPGEN		0x10

/* Wakeupgen Base addres */
void __iomem *wakeupgen_base;

/*
 * Static helper functions
 */
static int __wakeupgen_irq(unsigned int cpu, unsigned int irq, unsigned int set)
{
	unsigned int reg_index, reg_value, spi_irq;

	/*
	 * Subtract the GIC offset
	 */
	spi_irq = irq - 32;

	/*
	 * Not supported on ES1.0 silicon
	 */
	if ((cpu > NR_CPUS) || (omap_rev() == OMAP4430_REV_ES1_0))
		return -EPERM;

	/*
	 * Each wakeup gen register controls 32
	 * interrupts. i.e 1 bit per SPI IRQ
	 */
	switch (spi_irq >> 5) {
	case 0:
		/* IRQ32 to IRQ64 */
		reg_index = 0;
		break;
	case 1:
		/* IRQ64 to IRQ96 */
		reg_index = 1;
		spi_irq -= 32;
		break;
	case 2:
		/* IRQ96 to IRQ128 */
		reg_index = 2;
		spi_irq -= 64;
		break;
	case 3:
		/* IRQ128 to IRQ160 */
		reg_index = 3;
		spi_irq -= 96;
		break;
	default:
		/* Invalid IRQ */
		return -EPERM;
	}

	if (cpu) {
		reg_value = readl(wakeupgen_base + OMAP4_WKG_ENB_A_1
							+ (4 * reg_index));
		if (set)
			reg_value |= (1 << spi_irq);
		else
			reg_value &= ~(1 << spi_irq);
		writel(reg_value, wakeupgen_base + OMAP4_WKG_ENB_A_1
							+ (4 * reg_index));
	} else {
		reg_value = readl(wakeupgen_base + OMAP4_WKG_ENB_A_0
							+ (4 * reg_index));
		if (set)
			reg_value |= (1 << spi_irq);
		else
			reg_value &= ~(1 << spi_irq);
		writel(reg_value, wakeupgen_base + OMAP4_WKG_ENB_A_0
							+ (4 * reg_index));
	}

	return 0;
}

static int __wakeupgen_irq_all(unsigned int cpu, unsigned int reg)
{
	unsigned int reg_index;

	/*
	 * Not supported on ES1.0 silicon
	 */
	if ((cpu > NR_CPUS) || (omap_rev() == OMAP4430_REV_ES1_0))
		return -EPERM;

	for (reg_index = 0; reg_index < 4; reg_index++) {
		if (cpu)
			writel(reg, wakeupgen_base + OMAP4_WKG_ENB_A_1
							+ (4 * reg_index));
		else
			writel(reg, wakeupgen_base + OMAP4_WKG_ENB_A_0
							+ (4 * reg_index));
	}

	return 0;
}

/*
 * Initialse the wakeupgen module
 */
static int __init omap4_wakeupgen_init(void)
{
	/*
	 * To avoid code running on other OMAPs in
	 * multi-omap builds
	 */
	if (!cpu_is_omap44xx())
		return -ENODEV;

	/* Static mapping, never released */
	wakeupgen_base = ioremap(OMAP44XX_WKUPGEN_BASE, SZ_4K);
	BUG_ON(!wakeupgen_base);

	return 0;
}
early_initcall(omap4_wakeupgen_init);


/*
 * Disable the wakeup on 'cpu' from a specific 'irq'
 */
void omap4_wakeupgen_clear_interrupt(unsigned int cpu, unsigned int irq)
{
	if (__wakeupgen_irq(cpu, irq, 0))
		pr_warning("OMAP4: WakeUpGen not supported..\n");
}

/*
 * Enable the wakeup on 'cpu' from a specific 'irq'
 */
void omap4_wakeupgen_set_interrupt(unsigned int cpu, unsigned int irq)
{
	if (__wakeupgen_irq(cpu, irq, 1))
		pr_warning("OMAP4: WakeUpGen not supported..\n");
}

/*
 * Disable the wakeup on 'cpu' from all interrupts
 */
void omap4_wakeupgen_clear_all(unsigned int cpu)
{
	if (__wakeupgen_irq_all(cpu, 0x00000000))
		pr_warning("OMAP4: WakeUpGen not supported..\n");
}

/*
 * Enable the wakeup on 'cpu' from all interrupts
 */
void omap4_wakeupgen_set_all(unsigned int cpu)
{

	if (__wakeupgen_irq_all(cpu, 0xffffffff))
		pr_warning("OMAP4: WakeUpGen not supported..\n");
}

/*
 * Save WakewupGen context in SAR RAM3. Restore is done by ROM code.
 * WakeupGen is lost only when DEVICE hits OFF. Though the register
 * context is retained in MPU OFF state, hw recommondation is to
 * save/restore WakeupGen along with GIC to have consistent interrupt
 * state at both the blocks.
 * During normal operation, WakeUpGen delivers external interrupts
 * directly to the GIC. When the CPU asserts StandbyWFI, indicating
 * it wants to enter lowpower state, the Standby Controller checks
 * with the WakeUpGen unit using the idlereq/idleack handshake to make
 * sure there is no incoming interrupts.
 */

void omap4_wakeupgen_save(void)
{
	u32 reg_value, reg_index;
	void __iomem *sar_bank3_base;

	/*
	 * WakewupGen needs to be saved in SAR_BANK3
	 */
	sar_bank3_base = sar_ram_base + SAR_BANK3_OFFSET;

	for (reg_index = 0; reg_index < 0x4 ; reg_index++) {
		/*
		 * Save the CPU0 Wakeup Enable for Interrupts 0 to 127
		 */
		reg_value = readl(wakeupgen_base + OMAP4_WKG_ENB_A_0
							+ (4 * reg_index));
		writel(reg_value, sar_bank3_base +
				WAKEUPGENENB_OFFSET_CPU0 + (4 * reg_index));
		/*
		 * Force to 0x0 CPU0 isecure Wakeup Enable Interrupts
		 */
		writel(0x0, sar_bank3_base +
			WAKEUPGENENB_SECURE_OFFSET_CPU0 + (4 * reg_index));
		/*
		 * Save the CPU1 Wakeup Enable for Interrupts 0 to 127
		 */
		reg_value = readl(wakeupgen_base + OMAP4_WKG_ENB_A_1
							+ (4 * reg_index));
		writel(reg_value, sar_bank3_base +
				WAKEUPGENENB_OFFSET_CPU1 + (4 * reg_index));
		/*
		 * Force to 0x0 CPU1 secure Wakeup Enable Interrupts
		 */
		writel(0x0, sar_bank3_base +
			WAKEUPGENENB_SECURE_OFFSET_CPU1 + (4 * reg_index));
	}

	/*
	 * Save AuxBoot registers
	 */
	reg_value = readl(wakeupgen_base + OMAP4_AUX_CORE_BOOT_0);
	writel(reg_value, sar_bank3_base + AUXCOREBOOT0_OFFSET);
	reg_value = readl(wakeupgen_base + OMAP4_AUX_CORE_BOOT_0);
	writel(reg_value, sar_bank3_base + AUXCOREBOOT1_OFFSET);

	/*
	 * SyncReq generation logic
	 */
	reg_value = readl(wakeupgen_base + OMAP4_PTMSYNCREQ_MASK);
	writel(reg_value, sar_bank3_base + PTMSYNCREQ_MASK_OFFSET);
	reg_value = readl(wakeupgen_base + OMAP4_PTMSYNCREQ_EN);
	writel(reg_value, sar_bank3_base + PTMSYNCREQ_EN_OFFSET);

	/*
	 * Set the Backup Bit Mask status
	 */
	reg_value = readl(sar_bank3_base + SAR_BACKUP_STATUS_OFFSET);
	reg_value |= SAR_BACKUP_STATUS_WAKEUPGEN;
	writel(reg_value, sar_bank3_base + SAR_BACKUP_STATUS_OFFSET);
}