aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2010-11-29 01:23:55 -0500
committerColin Cross <ccross@android.com>2011-02-10 01:18:21 -0500
commit3524b70ef3336a4f1351a489e83894b88106ab7c (patch)
tree3777ad90274259834c83d93c01ccd60f6da7f131 /arch/arm
parent093617851c5fa0d1fdf5ce378f20691b7adb35e4 (diff)
ARM: tegra: irq: Add support for suspend wake sources
Signed-off-by: Colin Cross <ccross@android.com>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/mach-tegra/include/mach/legacy_irq.h4
-rw-r--r--arch/arm/mach-tegra/irq.c176
-rw-r--r--arch/arm/mach-tegra/legacy_irq.c109
3 files changed, 184 insertions, 105 deletions
diff --git a/arch/arm/mach-tegra/include/mach/legacy_irq.h b/arch/arm/mach-tegra/include/mach/legacy_irq.h
index db1eb3dd04c8..d898c0e3d905 100644
--- a/arch/arm/mach-tegra/include/mach/legacy_irq.h
+++ b/arch/arm/mach-tegra/include/mach/legacy_irq.h
@@ -27,5 +27,9 @@ int tegra_legacy_force_irq_status(unsigned int irq);
27void tegra_legacy_select_fiq(unsigned int irq, bool fiq); 27void tegra_legacy_select_fiq(unsigned int irq, bool fiq);
28unsigned long tegra_legacy_vfiq(int nr); 28unsigned long tegra_legacy_vfiq(int nr);
29unsigned long tegra_legacy_class(int nr); 29unsigned long tegra_legacy_class(int nr);
30int tegra_legacy_irq_set_wake(int irq, int enable);
31void tegra_legacy_irq_set_lp1_wake_mask(void);
32void tegra_legacy_irq_restore_mask(void);
33void tegra_init_legacy_irq(void);
30 34
31#endif 35#endif
diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c
index 5f065f9fdf53..7fb73490eb55 100644
--- a/arch/arm/mach-tegra/irq.c
+++ b/arch/arm/mach-tegra/irq.c
@@ -18,6 +18,7 @@
18 */ 18 */
19 19
20#include <linux/kernel.h> 20#include <linux/kernel.h>
21#include <linux/delay.h>
21#include <linux/init.h> 22#include <linux/init.h>
22#include <linux/interrupt.h> 23#include <linux/interrupt.h>
23#include <linux/irq.h> 24#include <linux/irq.h>
@@ -26,74 +27,104 @@
26#include <asm/hardware/gic.h> 27#include <asm/hardware/gic.h>
27 28
28#include <mach/iomap.h> 29#include <mach/iomap.h>
30#include <mach/legacy_irq.h>
29#include <mach/suspend.h> 31#include <mach/suspend.h>
30 32
31#include "board.h" 33#include "board.h"
32 34
33#define INT_SYS_NR (INT_GPIO_BASE - INT_PRI_BASE) 35#define PMC_CTRL 0x0
34#define INT_SYS_SZ (INT_SEC_BASE - INT_PRI_BASE) 36#define PMC_CTRL_LATCH_WAKEUPS (1 << 5)
35#define PPI_NR ((INT_SYS_NR+INT_SYS_SZ-1)/INT_SYS_SZ) 37#define PMC_WAKE_MASK 0xc
38#define PMC_WAKE_LEVEL 0x10
39#define PMC_WAKE_STATUS 0x14
40#define PMC_SW_WAKE_STATUS 0x18
41#define PMC_DPD_SAMPLE 0x20
36 42
37#define APBDMA_IRQ_STA_CPU 0x14 43static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
38#define APBDMA_IRQ_MASK_SET 0x20
39#define APBDMA_IRQ_MASK_CLR 0x24
40 44
41#define ICTLR_CPU_IER 0x20 45static u32 tegra_lp0_wake_enb;
42#define ICTLR_CPU_IER_SET 0x24 46static u32 tegra_lp0_wake_level;
43#define ICTLR_CPU_IER_CLR 0x28 47static u32 tegra_lp0_wake_level_any;
44#define ICTLR_CPU_IEP_CLASS 0x2c
45#define ICTLR_COP_IER 0x30
46#define ICTLR_COP_IER_SET 0x34
47#define ICTLR_COP_IER_CLR 0x38
48#define ICTLR_COP_IEP_CLASS 0x3c
49 48
50static void (*tegra_gic_mask_irq)(struct irq_data *d); 49static void (*tegra_gic_mask_irq)(struct irq_data *d);
51static void (*tegra_gic_unmask_irq)(struct irq_data *d); 50static void (*tegra_gic_unmask_irq)(struct irq_data *d);
52 51
53#define irq_to_ictlr(irq) (((irq) - 32) >> 5) 52/* ensures that sufficient time is passed for a register write to
54static void __iomem *tegra_ictlr_base = IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE); 53 * serialize into the 32KHz domain */
55#define ictlr_to_virt(ictlr) (tegra_ictlr_base + (ictlr) * 0x100) 54static void pmc_32kwritel(u32 val, unsigned long offs)
55{
56 writel(val, pmc + offs);
57 udelay(130);
58}
56 59
57static void tegra_mask(struct irq_data *d) 60int tegra_set_lp1_wake(int irq, int enable)
58{ 61{
59 void __iomem *addr = ictlr_to_virt(irq_to_ictlr(d->irq)); 62 return tegra_legacy_irq_set_wake(irq, enable);
60 tegra_gic_mask_irq(d);
61 writel(1 << (d->irq & 31), addr+ICTLR_CPU_IER_CLR);
62} 63}
63 64
64static void tegra_unmask(struct irq_data *d) 65void tegra_set_lp0_wake_pads(u32 wake_enb, u32 wake_level, u32 wake_any)
65{ 66{
66 void __iomem *addr = ictlr_to_virt(irq_to_ictlr(d->irq)); 67 u32 temp;
67 tegra_gic_unmask_irq(d); 68 u32 status;
68 writel(1<<(d->irq&31), addr+ICTLR_CPU_IER_SET); 69 u32 lvl;
70
71 wake_level &= wake_enb;
72 wake_any &= wake_enb;
73
74 wake_level |= (tegra_lp0_wake_level & tegra_lp0_wake_enb);
75 wake_any |= (tegra_lp0_wake_level_any & tegra_lp0_wake_enb);
76
77 wake_enb |= tegra_lp0_wake_enb;
78
79 pmc_32kwritel(0, PMC_SW_WAKE_STATUS);
80 temp = readl(pmc + PMC_CTRL);
81 temp |= PMC_CTRL_LATCH_WAKEUPS;
82 pmc_32kwritel(temp, PMC_CTRL);
83 temp &= ~PMC_CTRL_LATCH_WAKEUPS;
84 pmc_32kwritel(temp, PMC_CTRL);
85 status = readl(pmc + PMC_SW_WAKE_STATUS);
86 lvl = readl(pmc + PMC_WAKE_LEVEL);
87
88 /* flip the wakeup trigger for any-edge triggered pads
89 * which are currently asserting as wakeups */
90 lvl ^= status;
91 lvl &= wake_any;
92
93 wake_level |= lvl;
94
95 writel(wake_level, pmc + PMC_WAKE_LEVEL);
96 /* Enable DPD sample to trigger sampling pads data and direction
97 * in which pad will be driven during lp0 mode*/
98 writel(0x1, pmc + PMC_DPD_SAMPLE);
99
100 writel(wake_enb, pmc + PMC_WAKE_MASK);
69} 101}
70 102
71#ifdef CONFIG_PM 103static void tegra_mask(struct irq_data *d)
104{
105 tegra_gic_mask_irq(d);
106 tegra_legacy_mask_irq(d->irq);
107}
72 108
73static int tegra_set_wake(struct irq_data *d, unsigned int on) 109static void tegra_unmask(struct irq_data *d)
74{ 110{
75 return 0; 111 tegra_gic_unmask_irq(d);
112 tegra_legacy_unmask_irq(d->irq);
76} 113}
77#endif
78 114
79static struct irq_chip tegra_irq = { 115static struct irq_chip tegra_irq = {
80 .name = "PPI", 116 .name = "PPI",
81 .irq_mask = tegra_mask, 117 .irq_mask = tegra_mask,
82 .irq_unmask = tegra_unmask, 118 .irq_unmask = tegra_unmask,
83#ifdef CONFIG_PM
84 .irq_set_wake = tegra_set_wake,
85#endif
86}; 119};
87 120
88void __init tegra_init_irq(void) 121void __init tegra_init_irq(void)
89{ 122{
90 struct irq_chip *gic; 123 struct irq_chip *gic;
91 unsigned int i; 124 unsigned int i;
125 int irq;
92 126
93 for (i = 0; i < PPI_NR; i++) { 127 tegra_init_legacy_irq();
94 writel(~0, ictlr_to_virt(i) + ICTLR_CPU_IER_CLR);
95 writel(0, ictlr_to_virt(i) + ICTLR_CPU_IEP_CLASS);
96 }
97 128
98 gic_init(0, 29, IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE), 129 gic_init(0, 29, IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE),
99 IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100)); 130 IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100));
@@ -106,67 +137,10 @@ void __init tegra_init_irq(void)
106 tegra_irq.irq_set_affinity = gic->irq_set_affinity; 137 tegra_irq.irq_set_affinity = gic->irq_set_affinity;
107#endif 138#endif
108 139
109 for (i = INT_PRI_BASE; i < INT_GPIO_BASE; i++) { 140 for (i = 0; i < INT_MAIN_NR; i++) {
110 set_irq_chip(i, &tegra_irq); 141 irq = INT_PRI_BASE + i;
111 set_irq_handler(i, handle_level_irq); 142 set_irq_chip(irq, &tegra_irq);
112 set_irq_flags(i, IRQF_VALID); 143 set_irq_handler(irq, handle_level_irq);
113 } 144 set_irq_flags(irq, IRQF_VALID);
114}
115
116#ifdef CONFIG_PM
117static u32 cop_ier[PPI_NR];
118static u32 cpu_ier[PPI_NR];
119static u32 cpu_iep[PPI_NR];
120
121void tegra_irq_suspend(void)
122{
123 unsigned long flags;
124 int i;
125
126 for (i = INT_PRI_BASE; i < INT_GPIO_BASE; i++) {
127 struct irq_desc *desc = irq_to_desc(i);
128 if (!desc)
129 continue;
130 if (desc->status & IRQ_WAKEUP) {
131 pr_debug("irq %d is wakeup\n", i);
132 continue;
133 }
134 disable_irq(i);
135 }
136
137 local_irq_save(flags);
138 for (i = 0; i < PPI_NR; i++) {
139 void __iomem *ictlr = ictlr_to_virt(i);
140 cpu_ier[i] = readl(ictlr + ICTLR_CPU_IER);
141 cpu_iep[i] = readl(ictlr + ICTLR_CPU_IEP_CLASS);
142 cop_ier[i] = readl(ictlr + ICTLR_COP_IER);
143 writel(~0, ictlr + ICTLR_COP_IER_CLR);
144 } 145 }
145 local_irq_restore(flags);
146} 146}
147
148void tegra_irq_resume(void)
149{
150 unsigned long flags;
151 int i;
152
153 local_irq_save(flags);
154 for (i = 0; i < PPI_NR; i++) {
155 void __iomem *ictlr = ictlr_to_virt(i);
156 writel(cpu_iep[i], ictlr + ICTLR_CPU_IEP_CLASS);
157 writel(~0ul, ictlr + ICTLR_CPU_IER_CLR);
158 writel(cpu_ier[i], ictlr + ICTLR_CPU_IER_SET);
159 writel(0, ictlr + ICTLR_COP_IEP_CLASS);
160 writel(~0ul, ictlr + ICTLR_COP_IER_CLR);
161 writel(cop_ier[i], ictlr + ICTLR_COP_IER_SET);
162 }
163 local_irq_restore(flags);
164
165 for (i = INT_PRI_BASE; i < INT_GPIO_BASE; i++) {
166 struct irq_desc *desc = irq_to_desc(i);
167 if (!desc || (desc->status & IRQ_WAKEUP))
168 continue;
169 enable_irq(i);
170 }
171}
172#endif
diff --git a/arch/arm/mach-tegra/legacy_irq.c b/arch/arm/mach-tegra/legacy_irq.c
index 7cc8601c19ff..38eb719a4f53 100644
--- a/arch/arm/mach-tegra/legacy_irq.c
+++ b/arch/arm/mach-tegra/legacy_irq.c
@@ -18,17 +18,30 @@
18#include <linux/io.h> 18#include <linux/io.h>
19#include <linux/kernel.h> 19#include <linux/kernel.h>
20#include <mach/iomap.h> 20#include <mach/iomap.h>
21#include <mach/irqs.h>
21#include <mach/legacy_irq.h> 22#include <mach/legacy_irq.h>
22 23
23#define ICTLR_CPU_IER 0x20 24#define INT_SYS_NR (INT_GPIO_BASE - INT_PRI_BASE)
24#define ICTLR_CPU_IER_SET 0x24 25#define INT_SYS_SZ (INT_SEC_BASE - INT_PRI_BASE)
25#define ICTLR_CPU_IER_CLR 0x28 26#define PPI_NR ((INT_SYS_NR+INT_SYS_SZ-1)/INT_SYS_SZ)
26#define ICTLR_CPU_IEP_CLASS 0x2C 27
27#define ICTLR_CPU_IEP_VFIQ 0x08 28#define ICTLR_CPU_IEP_VFIQ 0x08
28#define ICTLR_CPU_IEP_FIR 0x14 29#define ICTLR_CPU_IEP_FIR 0x14
29#define ICTLR_CPU_IEP_FIR_SET 0x18 30#define ICTLR_CPU_IEP_FIR_SET 0x18
30#define ICTLR_CPU_IEP_FIR_CLR 0x1c 31#define ICTLR_CPU_IEP_FIR_CLR 0x1c
31 32
33#define ICTLR_CPU_IER 0x20
34#define ICTLR_CPU_IER_SET 0x24
35#define ICTLR_CPU_IER_CLR 0x28
36#define ICTLR_CPU_IEP_CLASS 0x2C
37
38#define ICTLR_COP_IER 0x30
39#define ICTLR_COP_IER_SET 0x34
40#define ICTLR_COP_IER_CLR 0x38
41#define ICTLR_COP_IEP_CLASS 0x3c
42
43#define NUM_ICTLRS 4
44
32static void __iomem *ictlr_reg_base[] = { 45static void __iomem *ictlr_reg_base[] = {
33 IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE), 46 IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE),
34 IO_ADDRESS(TEGRA_SECONDARY_ICTLR_BASE), 47 IO_ADDRESS(TEGRA_SECONDARY_ICTLR_BASE),
@@ -36,6 +49,9 @@ static void __iomem *ictlr_reg_base[] = {
36 IO_ADDRESS(TEGRA_QUATERNARY_ICTLR_BASE), 49 IO_ADDRESS(TEGRA_QUATERNARY_ICTLR_BASE),
37}; 50};
38 51
52static u32 tegra_legacy_wake_mask[4];
53static u32 tegra_legacy_saved_mask[4];
54
39/* When going into deep sleep, the CPU is powered down, taking the GIC with it 55/* When going into deep sleep, the CPU is powered down, taking the GIC with it
40 In order to wake, the wake interrupts need to be enabled in the legacy 56 In order to wake, the wake interrupts need to be enabled in the legacy
41 interrupt controller. */ 57 interrupt controller. */
@@ -112,3 +128,88 @@ unsigned long tegra_legacy_class(int nr)
112 base = ictlr_reg_base[nr]; 128 base = ictlr_reg_base[nr];
113 return readl(base + ICTLR_CPU_IEP_CLASS); 129 return readl(base + ICTLR_CPU_IEP_CLASS);
114} 130}
131
132int tegra_legacy_irq_set_wake(int irq, int enable)
133{
134 irq -= 32;
135 if (enable)
136 tegra_legacy_wake_mask[irq >> 5] |= 1 << (irq & 31);
137 else
138 tegra_legacy_wake_mask[irq >> 5] &= ~(1 << (irq & 31));
139
140 return 0;
141}
142
143void tegra_legacy_irq_set_lp1_wake_mask(void)
144{
145 void __iomem *base;
146 int i;
147
148 for (i = 0; i < NUM_ICTLRS; i++) {
149 base = ictlr_reg_base[i];
150 tegra_legacy_saved_mask[i] = readl(base + ICTLR_CPU_IER);
151 writel(tegra_legacy_wake_mask[i], base + ICTLR_CPU_IER);
152 }
153}
154
155void tegra_legacy_irq_restore_mask(void)
156{
157 void __iomem *base;
158 int i;
159
160 for (i = 0; i < NUM_ICTLRS; i++) {
161 base = ictlr_reg_base[i];
162 writel(tegra_legacy_saved_mask[i], base + ICTLR_CPU_IER);
163 }
164}
165
166void tegra_init_legacy_irq(void)
167{
168 int i;
169
170 for (i = 0; i < NUM_ICTLRS; i++) {
171 void __iomem *ictlr = ictlr_reg_base[i];
172 writel(~0, ictlr + ICTLR_CPU_IER_CLR);
173 writel(0, ictlr + ICTLR_CPU_IEP_CLASS);
174 }
175}
176
177#ifdef CONFIG_PM
178static u32 cop_ier[NUM_ICTLRS];
179static u32 cpu_ier[NUM_ICTLRS];
180static u32 cpu_iep[NUM_ICTLRS];
181
182void tegra_irq_suspend(void)
183{
184 unsigned long flags;
185 int i;
186
187 local_irq_save(flags);
188 for (i = 0; i < NUM_ICTLRS; i++) {
189 void __iomem *ictlr = ictlr_reg_base[i];
190 cpu_ier[i] = readl(ictlr + ICTLR_CPU_IER);
191 cpu_iep[i] = readl(ictlr + ICTLR_CPU_IEP_CLASS);
192 cop_ier[i] = readl(ictlr + ICTLR_COP_IER);
193 writel(~0, ictlr + ICTLR_COP_IER_CLR);
194 }
195 local_irq_restore(flags);
196}
197
198void tegra_irq_resume(void)
199{
200 unsigned long flags;
201 int i;
202
203 local_irq_save(flags);
204 for (i = 0; i < NUM_ICTLRS; i++) {
205 void __iomem *ictlr = ictlr_reg_base[i];
206 writel(cpu_iep[i], ictlr + ICTLR_CPU_IEP_CLASS);
207 writel(~0ul, ictlr + ICTLR_CPU_IER_CLR);
208 writel(cpu_ier[i], ictlr + ICTLR_CPU_IER_SET);
209 writel(0, ictlr + ICTLR_COP_IEP_CLASS);
210 writel(~0ul, ictlr + ICTLR_COP_IER_CLR);
211 writel(cop_ier[i], ictlr + ICTLR_COP_IER_SET);
212 }
213 local_irq_restore(flags);
214}
215#endif