Commit | Line | Data |
---|---|---|
5ad36c5f EG |
1 | /* |
2 | * Copyright (C) 2010 Google, Inc. | |
3 | * | |
4 | * Author: | |
5 | * Colin Cross <ccross@google.com> | |
6 | * | |
460907bc GK |
7 | * Copyright (C) 2010, NVIDIA Corporation |
8 | * | |
5ad36c5f EG |
9 | * This software is licensed under the terms of the GNU General Public |
10 | * License version 2, as published by the Free Software Foundation, and | |
11 | * may be copied, distributed, and modified under those terms. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | */ | |
19 | ||
20 | #include <linux/kernel.h> | |
21 | #include <linux/init.h> | |
22 | #include <linux/interrupt.h> | |
23 | #include <linux/irq.h> | |
24 | #include <linux/io.h> | |
25 | ||
26 | #include <asm/hardware/gic.h> | |
27 | ||
28 | #include <mach/iomap.h> | |
29 | ||
30 | #include "board.h" | |
31 | ||
460907bc GK |
32 | #define INT_SYS_NR (INT_GPIO_BASE - INT_PRI_BASE) |
33 | #define INT_SYS_SZ (INT_SEC_BASE - INT_PRI_BASE) | |
34 | #define PPI_NR ((INT_SYS_NR+INT_SYS_SZ-1)/INT_SYS_SZ) | |
35 | ||
36 | #define APBDMA_IRQ_STA_CPU 0x14 | |
37 | #define APBDMA_IRQ_MASK_SET 0x20 | |
38 | #define APBDMA_IRQ_MASK_CLR 0x24 | |
39 | ||
40 | #define ICTLR_CPU_IER 0x20 | |
41 | #define ICTLR_CPU_IER_SET 0x24 | |
42 | #define ICTLR_CPU_IER_CLR 0x28 | |
43 | #define ICTLR_CPU_IEP_CLASS 0x2c | |
44 | #define ICTLR_COP_IER 0x30 | |
45 | #define ICTLR_COP_IER_SET 0x34 | |
46 | #define ICTLR_COP_IER_CLR 0x38 | |
47 | #define ICTLR_COP_IEP_CLASS 0x3c | |
48 | ||
49 | static void (*gic_mask_irq)(unsigned int irq); | |
50 | static void (*gic_unmask_irq)(unsigned int irq); | |
51 | ||
52 | #define irq_to_ictlr(irq) (((irq)-32) >> 5) | |
53 | static void __iomem *tegra_ictlr_base = IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE); | |
54 | #define ictlr_to_virt(ictlr) (tegra_ictlr_base + (ictlr)*0x100) | |
55 | ||
56 | static void tegra_mask(unsigned int irq) | |
57 | { | |
58 | void __iomem *addr = ictlr_to_virt(irq_to_ictlr(irq)); | |
59 | gic_mask_irq(irq); | |
60 | writel(1<<(irq&31), addr+ICTLR_CPU_IER_CLR); | |
61 | } | |
62 | ||
63 | static void tegra_unmask(unsigned int irq) | |
64 | { | |
65 | void __iomem *addr = ictlr_to_virt(irq_to_ictlr(irq)); | |
66 | gic_unmask_irq(irq); | |
67 | writel(1<<(irq&31), addr+ICTLR_CPU_IER_SET); | |
68 | } | |
69 | ||
70 | #ifdef CONFIG_PM | |
71 | ||
72 | static int tegra_set_wake(unsigned int irq, unsigned int on) | |
73 | { | |
74 | return 0; | |
75 | } | |
76 | #endif | |
77 | ||
78 | static struct irq_chip tegra_irq = { | |
79 | .name = "PPI", | |
80 | .mask = tegra_mask, | |
81 | .unmask = tegra_unmask, | |
82 | #ifdef CONFIG_PM | |
83 | .set_wake = tegra_set_wake, | |
84 | #endif | |
85 | }; | |
86 | ||
5ad36c5f EG |
87 | void __init tegra_init_irq(void) |
88 | { | |
460907bc GK |
89 | struct irq_chip *gic; |
90 | unsigned int i; | |
91 | ||
92 | for (i = 0; i < PPI_NR; i++) { | |
93 | writel(~0, ictlr_to_virt(i) + ICTLR_CPU_IER_CLR); | |
94 | writel(0, ictlr_to_virt(i) + ICTLR_CPU_IEP_CLASS); | |
95 | } | |
96 | ||
5ad36c5f EG |
97 | gic_dist_init(0, IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE), 29); |
98 | gic_cpu_init(0, IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100)); | |
460907bc GK |
99 | |
100 | gic = get_irq_chip(29); | |
101 | gic_unmask_irq = gic->unmask; | |
102 | gic_mask_irq = gic->mask; | |
103 | tegra_irq.ack = gic->ack; | |
104 | #ifdef CONFIG_SMP | |
105 | tegra_irq.set_affinity = gic->set_affinity; | |
106 | #endif | |
107 | ||
108 | for (i = INT_PRI_BASE; i < INT_GPIO_BASE; i++) { | |
109 | set_irq_chip(i, &tegra_irq); | |
110 | set_irq_handler(i, handle_level_irq); | |
111 | set_irq_flags(i, IRQF_VALID); | |
112 | } | |
113 | } | |
114 | ||
115 | #ifdef CONFIG_PM | |
116 | static u32 cop_ier[PPI_NR]; | |
117 | static u32 cpu_ier[PPI_NR]; | |
118 | static u32 cpu_iep[PPI_NR]; | |
119 | ||
120 | void tegra_irq_suspend(void) | |
121 | { | |
122 | unsigned long flags; | |
123 | int i; | |
124 | ||
125 | for (i = INT_PRI_BASE; i < INT_GPIO_BASE; i++) { | |
126 | struct irq_desc *desc = irq_to_desc(i); | |
127 | if (!desc) | |
128 | continue; | |
129 | if (desc->status & IRQ_WAKEUP) { | |
130 | pr_debug("irq %d is wakeup\n", i); | |
131 | continue; | |
132 | } | |
133 | disable_irq(i); | |
134 | } | |
135 | ||
136 | local_irq_save(flags); | |
137 | for (i = 0; i < PPI_NR; i++) { | |
138 | void __iomem *ictlr = ictlr_to_virt(i); | |
139 | cpu_ier[i] = readl(ictlr + ICTLR_CPU_IER); | |
140 | cpu_iep[i] = readl(ictlr + ICTLR_CPU_IEP_CLASS); | |
141 | cop_ier[i] = readl(ictlr + ICTLR_COP_IER); | |
142 | writel(~0, ictlr + ICTLR_COP_IER_CLR); | |
143 | } | |
144 | local_irq_restore(flags); | |
145 | } | |
146 | ||
147 | void tegra_irq_resume(void) | |
148 | { | |
149 | unsigned long flags; | |
150 | int i; | |
151 | ||
152 | local_irq_save(flags); | |
153 | for (i = 0; i < PPI_NR; i++) { | |
154 | void __iomem *ictlr = ictlr_to_virt(i); | |
155 | writel(cpu_iep[i], ictlr + ICTLR_CPU_IEP_CLASS); | |
156 | writel(~0ul, ictlr + ICTLR_CPU_IER_CLR); | |
157 | writel(cpu_ier[i], ictlr + ICTLR_CPU_IER_SET); | |
158 | writel(0, ictlr + ICTLR_COP_IEP_CLASS); | |
159 | writel(~0ul, ictlr + ICTLR_COP_IER_CLR); | |
160 | writel(cop_ier[i], ictlr + ICTLR_COP_IER_SET); | |
161 | } | |
162 | local_irq_restore(flags); | |
163 | ||
164 | for (i = INT_PRI_BASE; i < INT_GPIO_BASE; i++) { | |
165 | struct irq_desc *desc = irq_to_desc(i); | |
166 | if (!desc || (desc->status & IRQ_WAKEUP)) | |
167 | continue; | |
168 | enable_irq(i); | |
169 | } | |
5ad36c5f | 170 | } |
460907bc | 171 | #endif |