summaryrefslogtreecommitdiff
path: root/arch/arm/mach-ux500/pm/pm.c
blob: 691642e4200d20d3ff97fa0aefe87302e0bbf59d (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
/*
 * Copyright (C) ST-Ericsson SA 2010
 * Author: Rickard Andersson <rickard.andersson@stericsson.com> for
 *         ST-Ericsson.
 * License terms: GNU General Public License (GPL) version 2
 *
 */

#include <linux/io.h>
#include <linux/percpu.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/gpio/nomadik.h>
#include <linux/mfd/dbx500-prcmu.h>

#include <asm/hardware/gic.h>
#include <asm/processor.h>

#include <mach/hardware.h>
#include <mach/pm.h>

#define STABILIZATION_TIME 30 /* us */
#define GIC_FREEZE_DELAY 1 /* us */

#define PRCM_ARM_WFI_STANDBY_CPU0_WFI 0x8
#define PRCM_ARM_WFI_STANDBY_CPU1_WFI 0x10

/* Dual A9 core interrupt management unit registers */
#define PRCM_A9_MASK_REQ	0x328
#define PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ	0x1
#define PRCM_A9_MASK_ACK	0x32c

#define PRCM_ARMITMSK31TO0	0x11c
#define PRCM_ARMITMSK63TO32	0x120
#define PRCM_ARMITMSK95TO64	0x124
#define PRCM_ARMITMSK127TO96	0x128
#define PRCM_POWER_STATE_VAL	0x25C
#define PRCM_ARMITVAL31TO0	0x260
#define PRCM_ARMITVAL63TO32	0x264
#define PRCM_ARMITVAL95TO64	0x268
#define PRCM_ARMITVAL127TO96	0x26C

/* ARM WFI Standby signal register */
#define PRCM_ARM_WFI_STANDBY    0x130

/* IO force */
#define PRCM_IOCR		0x310
#define PRCM_IOCR_IOFORCE			0x1

#ifdef CONFIG_UX500_SUSPEND
int ux500_console_uart_gpio_pin = CONFIG_UX500_CONSOLE_UART_GPIO_PIN;
#endif

static u32 u8500_gpio_banks[] = {U8500_GPIOBANK0_BASE,
				 U8500_GPIOBANK1_BASE,
				 U8500_GPIOBANK2_BASE,
				 U8500_GPIOBANK3_BASE,
				 U8500_GPIOBANK4_BASE,
				 U8500_GPIOBANK5_BASE,
				 U8500_GPIOBANK6_BASE,
				 U8500_GPIOBANK7_BASE,
				 U8500_GPIOBANK8_BASE};

static u32 u5500_gpio_banks[] = {U5500_GPIOBANK0_BASE,
				 U5500_GPIOBANK1_BASE,
				 U5500_GPIOBANK2_BASE,
				 U5500_GPIOBANK3_BASE,
				 U5500_GPIOBANK4_BASE,
				 U5500_GPIOBANK5_BASE,
				 U5500_GPIOBANK6_BASE,
				 U5500_GPIOBANK7_BASE};

static u32 ux500_gpio_wks[ARRAY_SIZE(u8500_gpio_banks)];

inline int ux500_pm_arm_on_ext_clk(bool leave_arm_pll_on)
{
	return 0;
}

/* Decouple GIC from the interrupt bus */
void ux500_pm_gic_decouple(void)
{
	prcmu_write_masked(PRCM_A9_MASK_REQ,
			   PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ,
			   PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ);

	while (!prcmu_read(PRCM_A9_MASK_REQ))
		cpu_relax();

	/* TODO: Use the ack bit when possible */
	udelay(GIC_FREEZE_DELAY); /* Wait for the GIC to freeze */
}

/* Recouple GIC with the interrupt bus */
void ux500_pm_gic_recouple(void)
{
	prcmu_write_masked(PRCM_A9_MASK_REQ,
			   PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ,
			   0);

	/* TODO: Use the ack bit when possible */
}

#define GIC_NUMBER_REGS 5
bool ux500_pm_gic_pending_interrupt(void)
{
	u32 pr; /* Pending register */
	u32 er; /* Enable register */
	int i;

	/* 5 registers. STI & PPI not skipped */
	for (i = 0; i < GIC_NUMBER_REGS; i++) {

		pr = readl_relaxed(__io_address(U8500_GIC_DIST_BASE) +
				   GIC_DIST_PENDING_SET + i * 4);
		er = readl_relaxed(__io_address(U8500_GIC_DIST_BASE) +
				   GIC_DIST_ENABLE_SET + i * 4);

		if (pr & er)
			return true; /* There is a pending interrupt */
	}
	return false;
}

#define GIC_NUMBER_SPI_REGS 4
bool ux500_pm_prcmu_pending_interrupt(void)
{
	u32 it;
	u32 im;
	int i;

	for (i = 0; i < GIC_NUMBER_SPI_REGS; i++) { /* There are 4 registers */

		it = prcmu_read(PRCM_ARMITVAL31TO0 + i * 4);
		im = prcmu_read(PRCM_ARMITMSK31TO0 + i * 4);

		if (it & im)
			return true; /* There is a pending interrupt */
	}

	return false;
}

void ux500_pm_prcmu_set_ioforce(bool enable)
{
	if (enable)
		prcmu_write_masked(PRCM_IOCR,
				   PRCM_IOCR_IOFORCE,
				   PRCM_IOCR_IOFORCE);
	else
		prcmu_write_masked(PRCM_IOCR,
				   PRCM_IOCR_IOFORCE,
				   0);
}

void ux500_pm_prcmu_copy_gic_settings(void)
{
	u32 er; /* Enable register */
	int i;

	for (i = 0; i < GIC_NUMBER_SPI_REGS; i++) { /* 4*32 SPI interrupts */
		/* +1 due to skip STI and PPI */
		er = readl_relaxed(__io_address(U8500_GIC_DIST_BASE) +
			   GIC_DIST_ENABLE_SET + (i + 1) * 4);
		prcmu_write(PRCM_ARMITMSK31TO0 + i * 4, er);
	}
}

void ux500_pm_gpio_save_wake_up_status(void)
{
	int num_banks;
	u32 *banks;
	int i;

	if (cpu_is_u5500()) {
		num_banks = ARRAY_SIZE(u5500_gpio_banks);
		banks = u5500_gpio_banks;
	} else {
		num_banks = ARRAY_SIZE(u8500_gpio_banks);
		banks = u8500_gpio_banks;
	}

	nmk_gpio_clocks_enable();

	for (i = 0; i < num_banks; i++)
		ux500_gpio_wks[i] = readl(__io_address(banks[i]) + NMK_GPIO_WKS);

	nmk_gpio_clocks_disable();
}

u32 ux500_pm_gpio_read_wake_up_status(unsigned int bank_num)
{
	if (WARN_ON(cpu_is_u5500() && bank_num >=
		    ARRAY_SIZE(u5500_gpio_banks)))
		return 0;

	if (WARN_ON(cpu_is_u8500() && bank_num >=
		    ARRAY_SIZE(u8500_gpio_banks)))
		return 0;

	return ux500_gpio_wks[bank_num];
}

/* Check if the other CPU is in WFI */
bool ux500_pm_other_cpu_wfi(void)
{
	if (smp_processor_id()) {
		/* We are CPU 1 => check if CPU0 is in WFI */
		if (prcmu_read(PRCM_ARM_WFI_STANDBY) &
		    PRCM_ARM_WFI_STANDBY_CPU0_WFI)
			return true;
	} else {
		/* We are CPU 0 => check if CPU1 is in WFI */
		if (prcmu_read(PRCM_ARM_WFI_STANDBY) &
		    PRCM_ARM_WFI_STANDBY_CPU1_WFI)
			return true;
	}

	return false;
}