diff options
author | Colin Cross <ccross@android.com> | 2010-11-29 01:23:55 -0500 |
---|---|---|
committer | Colin Cross <ccross@android.com> | 2011-02-10 01:18:21 -0500 |
commit | 3524b70ef3336a4f1351a489e83894b88106ab7c (patch) | |
tree | 3777ad90274259834c83d93c01ccd60f6da7f131 /arch/arm | |
parent | 093617851c5fa0d1fdf5ce378f20691b7adb35e4 (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.h | 4 | ||||
-rw-r--r-- | arch/arm/mach-tegra/irq.c | 176 | ||||
-rw-r--r-- | arch/arm/mach-tegra/legacy_irq.c | 109 |
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); | |||
27 | void tegra_legacy_select_fiq(unsigned int irq, bool fiq); | 27 | void tegra_legacy_select_fiq(unsigned int irq, bool fiq); |
28 | unsigned long tegra_legacy_vfiq(int nr); | 28 | unsigned long tegra_legacy_vfiq(int nr); |
29 | unsigned long tegra_legacy_class(int nr); | 29 | unsigned long tegra_legacy_class(int nr); |
30 | int tegra_legacy_irq_set_wake(int irq, int enable); | ||
31 | void tegra_legacy_irq_set_lp1_wake_mask(void); | ||
32 | void tegra_legacy_irq_restore_mask(void); | ||
33 | void 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 | 43 | static 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 | 45 | static u32 tegra_lp0_wake_enb; |
42 | #define ICTLR_CPU_IER_SET 0x24 | 46 | static u32 tegra_lp0_wake_level; |
43 | #define ICTLR_CPU_IER_CLR 0x28 | 47 | static 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 | ||
50 | static void (*tegra_gic_mask_irq)(struct irq_data *d); | 49 | static void (*tegra_gic_mask_irq)(struct irq_data *d); |
51 | static void (*tegra_gic_unmask_irq)(struct irq_data *d); | 50 | static 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 |
54 | static 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) | 54 | static void pmc_32kwritel(u32 val, unsigned long offs) |
55 | { | ||
56 | writel(val, pmc + offs); | ||
57 | udelay(130); | ||
58 | } | ||
56 | 59 | ||
57 | static void tegra_mask(struct irq_data *d) | 60 | int 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 | ||
64 | static void tegra_unmask(struct irq_data *d) | 65 | void 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 | 103 | static void tegra_mask(struct irq_data *d) |
104 | { | ||
105 | tegra_gic_mask_irq(d); | ||
106 | tegra_legacy_mask_irq(d->irq); | ||
107 | } | ||
72 | 108 | ||
73 | static int tegra_set_wake(struct irq_data *d, unsigned int on) | 109 | static 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 | ||
79 | static struct irq_chip tegra_irq = { | 115 | static 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 | ||
88 | void __init tegra_init_irq(void) | 121 | void __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 | ||
117 | static u32 cop_ier[PPI_NR]; | ||
118 | static u32 cpu_ier[PPI_NR]; | ||
119 | static u32 cpu_iep[PPI_NR]; | ||
120 | |||
121 | void 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 | |||
148 | void 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 | |||
32 | static void __iomem *ictlr_reg_base[] = { | 45 | static 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 | ||
52 | static u32 tegra_legacy_wake_mask[4]; | ||
53 | static 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 | |||
132 | int 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 | |||
143 | void 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 | |||
155 | void 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 | |||
166 | void 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 | ||
178 | static u32 cop_ier[NUM_ICTLRS]; | ||
179 | static u32 cpu_ier[NUM_ICTLRS]; | ||
180 | static u32 cpu_iep[NUM_ICTLRS]; | ||
181 | |||
182 | void 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 | |||
198 | void 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 | ||